NFSv4.2: define limits and sizes for user xattr handling
authorFrank van der Linden <fllinden@amazon.com>
Tue, 23 Jun 2020 22:38:54 +0000 (22:38 +0000)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Mon, 13 Jul 2020 21:52:45 +0000 (17:52 -0400)
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>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/client.c
fs/nfs/nfs42.h
fs/nfs/nfs42xdr.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4client.c
include/linux/nfs_fs_sb.h

index f1ff307..055040b 100644 (file)
@@ -50,6 +50,7 @@
 #include "nfs.h"
 #include "netns.h"
 #include "sysfs.h"
+#include "nfs42.h"
 
 #define NFSDBG_FACILITY                NFSDBG_CLIENT
 
@@ -749,7 +750,7 @@ error:
 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)
@@ -762,7 +763,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)
@@ -795,6 +798,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
 }
 
 /*
index c891af9..51de8dd 100644 (file)
@@ -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 */
index c03f324..6712daa 100644 (file)
                                         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)
 {
index 2b7f6dc..526b3e7 100644 (file)
@@ -557,6 +557,12 @@ static inline void nfs4_unregister_sysctl(void)
 /* nfs4xdr.c */
 extern const struct rpc_procinfo nfs4_procedures[];
 
+#ifdef CONFIG_NFS_V4_2
+extern const u32 nfs42_maxsetxattr_overhead;
+extern const u32 nfs42_maxgetxattr_overhead;
+extern const u32 nfs42_maxlistxattrs_overhead;
+#endif
+
 struct nfs4_mount_data;
 
 /* callback_xdr.c */
index 0bd77cc..c41cbd8 100644 (file)
@@ -992,6 +992,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)
 {
@@ -1039,6 +1069,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;
index 465fa98..128e01a 100644 (file)
@@ -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;