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 f1ff3076e4a461d80830103160515576685fee33..055040bf1a8ea57606c0550bea253755024cb416 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 c891af949886f81bbaeab700b7fe473aa00add55..51de8ddc7d8873fa5367ac7a1414a5caf1caad72 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 c03f3246d6c5958eb5f772d3bd5906fc3eee1771..6712daa9d85b6b76378f155e689783a43f0b0f08 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 2b7f6dcd2eb82e0adc9342b03a92dc396d4260b2..526b3e70d57c87307f68be93961e47961b5a9d13 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 0bd77cc1f639fa27eb5ffbe0a59081285adad215..c41cbd86612cae1106158aee81bca1fafcf7d669 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 465fa98258a315c71d25f677405fa15b42c6ef85..128e01acb4cac2cd76c7499e2ad76c93299535d2 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;