Re: [PATCH 04/13] NFSv4.2: define limits and sizes for user xattr handling

Schumaker, Anna
2020-03-12T20:35:49Z

Hi Frank,

On Wed, 2020-03-11 at 19:56 +0000, Frank van der Linden wrote:
> Set limits for extended attributes (attribute value size and listxattr
> buffer size), based on the fs-independent limits (XATTR_*_MAX).
>
> Define the maximum XDR sizes for the RFC 8276 XATTR operations.
> In the case of operations that carry a larger payload (SETXATTR,
> GETXATTR, LISTXATTR), these exclude that payload, which is added
> as separate pages, like other operations do.
>
> Define, much like for read and write operations, the maximum overhead
> sizes for get/set/listxattr, and use them to limit the maximum payload
> size for those operations, in combination with the channel attributes.
>
> Signed-off-by: Frank van der Linden <fllinden@amazon.com>
> ---
> fs/nfs/client.c | 19 ++++++++++--
> fs/nfs/internal.h | 5 ++++
> fs/nfs/nfs42.h | 16 ++++++++++
> fs/nfs/nfs42xdr.c | 74
> +++++++++++++++++++++++++++++++++++++++++++++++
> fs/nfs/nfs4client.c | 31 ++++++++++++++++++++
> include/linux/nfs_fs_sb.h | 5 ++++
> 6 files changed, 148 insertions(+), 2 deletions(-)
>
> diff --git a/fs/nfs/client.c b/fs/nfs/client.c
> index 989c30c98511..eef39a4ec114 100644
> --- a/fs/nfs/client.c
> +++ b/fs/nfs/client.c
> @@ -50,6 +50,7 @@
> #include "nfs.h"
> #include "netns.h"
> #include "sysfs.h"
> +#include "nfs42.h"
>
> #define NFSDBG_FACILITY NFSDBG_CLIENT
>
> @@ -748,7 +749,7 @@ static int nfs_init_server(struct nfs_server *server,
> static void nfs_server_set_fsinfo(struct nfs_server *server,
> struct nfs_fsinfo *fsinfo)
> {
> - unsigned long max_rpc_payload;
> + unsigned long max_rpc_payload, raw_max_rpc_payload;
>
> /* Work out a lot of parameters */
> if (server->rsize == 0)
> @@ -761,7 +762,9 @@ static void nfs_server_set_fsinfo(struct nfs_server
> *server,
> if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax)
> server->wsize = nfs_block_size(fsinfo->wtmax, NULL);
>
> - max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL);
> + raw_max_rpc_payload = rpc_max_payload(server->client);
> + max_rpc_payload = nfs_block_size(raw_max_rpc_payload, NULL);
> +
> if (server->rsize > max_rpc_payload)
> server->rsize = max_rpc_payload;
> if (server->rsize > NFS_MAX_FILE_IO_SIZE)
> @@ -794,6 +797,18 @@ static void nfs_server_set_fsinfo(struct nfs_server
> *server,
> server->clone_blksize = fsinfo->clone_blksize;
> /* We're airborne Set socket buffersize */
> rpc_setbufsize(server->client, server->wsize + 100, server->rsize +
> 100);
> +
> +#ifdef CONFIG_NFS_V4_2
> + /*
> + * Defaults until limited by the session parameters.
> + */
> + server->gxasize = min_t(unsigned int, raw_max_rpc_payload,
> + XATTR_SIZE_MAX);
> + server->sxasize = min_t(unsigned int, raw_max_rpc_payload,
> + XATTR_SIZE_MAX);
> + server->lxasize = min_t(unsigned int, raw_max_rpc_payload,
> + nfs42_listxattr_xdrsize(XATTR_LIST_MAX));
> +#endif
> }
>
> /*
> diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
> index f80c47d5ff27..68f235a571e1 100644
> --- a/fs/nfs/internal.h
> +++ b/fs/nfs/internal.h
> @@ -306,6 +306,11 @@ extern const u32 nfs41_maxread_overhead;
> extern const u32 nfs41_maxwrite_overhead;
> extern const u32 nfs41_maxgetdevinfo_overhead;
> #endif
> +#ifdef CONFIG_NFS_V4_2
> +extern const u32 nfs42_maxsetxattr_overhead;
> +extern const u32 nfs42_maxgetxattr_overhead;
> +extern const u32 nfs42_maxlistxattrs_overhead;
> +#endif

As far as I can tell, these are only used by the NFS v4 code. Can you put these
definitions in nfs4_fs.h instead of internal.h, since the generic client doesn't
really need to know about it?

Thanks,
Anna
>
> /* nfs4proc.c */
> #if IS_ENABLED(CONFIG_NFS_V4)
> diff --git a/fs/nfs/nfs42.h b/fs/nfs/nfs42.h
> index c891af949886..51de8ddc7d88 100644
> --- a/fs/nfs/nfs42.h
> +++ b/fs/nfs/nfs42.h
> @@ -6,6 +6,8 @@
> #ifndef __LINUX_FS_NFS_NFS4_2_H
> #define __LINUX_FS_NFS_NFS4_2_H
>
> +#include <linux/xattr.h>
> +
> /*
> * FIXME: four LAYOUTSTATS calls per compound at most! Do we need to support
> * more? Need to consider not to pre-alloc too much for a compound.
> @@ -36,5 +38,19 @@ static inline bool nfs42_files_from_same_server(struct file
> *in,
> return nfs4_check_serverowner_major_id(c_in->cl_serverowner,
> c_out->cl_serverowner);
> }
> +
> +/*
> + * Maximum XDR buffer size needed for a listxattr buffer of buflen size.
> + *
> + * The upper boundary is a buffer with all 1-byte sized attribute names.
> + * They would be 7 bytes long in the eventual buffer ("user.x\0"), and
> + * 8 bytes long XDR-encoded.
> + *
> + * Include the trailing eof word as well.
> + */
> +static inline u32 nfs42_listxattr_xdrsize(u32 buflen)
> +{
> + return ((buflen / (XATTR_USER_PREFIX_LEN + 2)) * 8) + 4;
> +}
> #endif /* CONFIG_NFS_V4_2 */
> #endif /* __LINUX_FS_NFS_NFS4_2_H */
> diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c
> index c03f3246d6c5..6712daa9d85b 100644
> --- a/fs/nfs/nfs42xdr.c
> +++ b/fs/nfs/nfs42xdr.c
> @@ -169,6 +169,80 @@
> decode_clone_maxsz + \
> decode_getattr_maxsz)
>
> +#ifdef CONFIG_NFS_V4_2
> +/* Not limited by NFS itself, limited by the generic xattr code */
> +#define nfs4_xattr_name_maxsz XDR_QUADLEN(XATTR_NAME_MAX)
> +
> +#define encode_getxattr_maxsz (op_encode_hdr_maxsz + 1 + \
> + nfs4_xattr_name_maxsz)
> +#define decode_getxattr_maxsz (op_decode_hdr_maxsz + 1 + 1)
> +#define encode_setxattr_maxsz (op_encode_hdr_maxsz + \
> + 1 + nfs4_xattr_name_maxsz + 1)
> +#define decode_setxattr_maxsz (op_decode_hdr_maxsz +
> decode_change_info_maxsz)
> +#define encode_listxattrs_maxsz (op_encode_hdr_maxsz + 2 + 1)
> +#define decode_listxattrs_maxsz (op_decode_hdr_maxsz + 2 + 1 + 1)
> +#define encode_removexattr_maxsz (op_encode_hdr_maxsz + 1 + \
> + nfs4_xattr_name_maxsz)
> +#define decode_removexattr_maxsz (op_decode_hdr_maxsz + \
> + decode_change_info_maxsz)
> +
> +#define NFS4_enc_getxattr_sz (compound_encode_hdr_maxsz + \
> + encode_sequence_maxsz + \
> + encode_putfh_maxsz + \
> + encode_getxattr_maxsz)
> +#define NFS4_dec_getxattr_sz (compound_decode_hdr_maxsz + \
> + decode_sequence_maxsz + \
> + decode_putfh_maxsz + \
> + decode_getxattr_maxsz)
> +#define NFS4_enc_setxattr_sz (compound_encode_hdr_maxsz + \
> + encode_sequence_maxsz + \
> + encode_putfh_maxsz + \
> + encode_setxattr_maxsz)
> +#define NFS4_dec_setxattr_sz (compound_decode_hdr_maxsz + \
> + decode_sequence_maxsz + \
> + decode_putfh_maxsz + \
> + decode_setxattr_maxsz)
> +#define NFS4_enc_listxattrs_sz (compound_encode_hdr_maxsz + \
> + encode_sequence_maxsz + \
> + encode_putfh_maxsz + \
> + encode_listxattrs_maxsz)
> +#define NFS4_dec_listxattrs_sz (compound_decode_hdr_maxsz + \
> + decode_sequence_maxsz + \
> + decode_putfh_maxsz + \
> + decode_listxattrs_maxsz)
> +#define NFS4_enc_removexattr_sz (compound_encode_hdr_maxsz + \
> + encode_sequence_maxsz + \
> + encode_putfh_maxsz + \
> + encode_removexattr_maxsz)
> +#define NFS4_dec_removexattr_sz (compound_decode_hdr_maxsz + \
> + decode_sequence_maxsz + \
> + decode_putfh_maxsz + \
> + decode_removexattr_maxsz)
> +
> +/*
> + * These values specify the maximum amount of data that is not
> + * associated with the extended attribute name or extended
> + * attribute list in the SETXATTR, GETXATTR and LISTXATTR
> + * respectively.
> + */
> +const u32 nfs42_maxsetxattr_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
> + compound_encode_hdr_maxsz +
> + encode_sequence_maxsz +
> + encode_putfh_maxsz + 1 +
> + nfs4_xattr_name_maxsz)
> + * XDR_UNIT);
> +
> +const u32 nfs42_maxgetxattr_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
> + compound_decode_hdr_maxsz +
> + decode_sequence_maxsz +
> + decode_putfh_maxsz + 1) * XDR_UNIT);
> +
> +const u32 nfs42_maxlistxattrs_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
> + compound_decode_hdr_maxsz +
> + decode_sequence_maxsz +
> + decode_putfh_maxsz + 3) * XDR_UNIT);
> +#endif
> +
> static void encode_fallocate(struct xdr_stream *xdr,
> const struct nfs42_falloc_args *args)
> {
> diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
> index 0cd767e5c977..8b320ef0e8a3 100644
> --- a/fs/nfs/nfs4client.c
> +++ b/fs/nfs/nfs4client.c
> @@ -993,6 +993,36 @@ static void nfs4_session_limit_rwsize(struct nfs_server
> *server)
> #endif /* CONFIG_NFS_V4_1 */
> }
>
> +/*
> + * Limit xattr sizes using the channel attributes.
> + */
> +static void nfs4_session_limit_xasize(struct nfs_server *server)
> +{
> +#ifdef CONFIG_NFS_V4_2
> + struct nfs4_session *sess;
> + u32 server_gxa_sz;
> + u32 server_sxa_sz;
> + u32 server_lxa_sz;
> +
> + if (!nfs4_has_session(server->nfs_client))
> + return;
> +
> + sess = server->nfs_client->cl_session;
> +
> + server_gxa_sz = sess->fc_attrs.max_resp_sz - nfs42_maxgetxattr_overhead;
> + server_sxa_sz = sess->fc_attrs.max_rqst_sz - nfs42_maxsetxattr_overhead;
> + server_lxa_sz = sess->fc_attrs.max_resp_sz -
> + nfs42_maxlistxattrs_overhead;
> +
> + if (server->gxasize > server_gxa_sz)
> + server->gxasize = server_gxa_sz;
> + if (server->sxasize > server_sxa_sz)
> + server->sxasize = server_sxa_sz;
> + if (server->lxasize > server_lxa_sz)
> + server->lxasize = server_lxa_sz;
> +#endif
> +}
> +
> static int nfs4_server_common_setup(struct nfs_server *server,
> struct nfs_fh *mntfh, bool auth_probe)
> {
> @@ -1040,6 +1070,7 @@ static int nfs4_server_common_setup(struct nfs_server
> *server,
> goto out;
>
> nfs4_session_limit_rwsize(server);
> + nfs4_session_limit_xasize(server);
>
> if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
> server->namelen = NFS4_MAXNAMLEN;
> diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
> index d881f7a38bc9..7eae72a8762e 100644
> --- a/include/linux/nfs_fs_sb.h
> +++ b/include/linux/nfs_fs_sb.h
> @@ -163,6 +163,11 @@ struct nfs_server {
> unsigned int dtsize; /* readdir size */
> unsigned short port; /* "port=" setting */
> unsigned int bsize; /* server block size */
> +#ifdef CONFIG_NFS_V4_2
> + unsigned int gxasize; /* getxattr size */
> + unsigned int sxasize; /* setxattr size */
> + unsigned int lxasize; /* listxattr size */
> +#endif
> unsigned int acregmin; /* attr cache timeouts */
> unsigned int acregmax;
> unsigned int acdirmin;