nfsd4: better estimate of getattr response size
authorJ. Bruce Fields <bfields@redhat.com>
Sun, 23 Mar 2014 16:01:48 +0000 (12:01 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Fri, 30 May 2014 21:32:06 +0000 (17:32 -0400)
We plan to use this estimate to decide whether or not to allow zero-copy
reads.  Currently we're assuming all getattr's are a page, which can be
both too small (ACLs e.g. may be arbitrarily long) and too large (after
an upcoming read patch this will unnecessarily prevent zero copy reads
in any read compound also containing a getattr).

Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/nfs4proc.c

index d95d901..7507eff 100644 (file)
@@ -1465,6 +1465,49 @@ static inline u32 nfsd4_create_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op
                + nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
 }
 
+/*
+ * Note since this is an idempotent operation we won't insist on failing
+ * the op prematurely if the estimate is too large.  We may turn off splice
+ * reads unnecessarily.
+ */
+static inline u32 nfsd4_getattr_rsize(struct svc_rqst *rqstp,
+                                     struct nfsd4_op *op)
+{
+       u32 *bmap = op->u.getattr.ga_bmval;
+       u32 bmap0 = bmap[0], bmap1 = bmap[1], bmap2 = bmap[2];
+       u32 ret = 0;
+
+       if (bmap0 & FATTR4_WORD0_ACL)
+               return svc_max_payload(rqstp);
+       if (bmap0 & FATTR4_WORD0_FS_LOCATIONS)
+               return svc_max_payload(rqstp);
+
+       if (bmap1 & FATTR4_WORD1_OWNER) {
+               ret += IDMAP_NAMESZ + 4;
+               bmap1 &= ~FATTR4_WORD1_OWNER;
+       }
+       if (bmap1 & FATTR4_WORD1_OWNER_GROUP) {
+               ret += IDMAP_NAMESZ + 4;
+               bmap1 &= ~FATTR4_WORD1_OWNER_GROUP;
+       }
+       if (bmap0 & FATTR4_WORD0_FILEHANDLE) {
+               ret += NFS4_FHSIZE + 4;
+               bmap0 &= ~FATTR4_WORD0_FILEHANDLE;
+       }
+       if (bmap2 & FATTR4_WORD2_SECURITY_LABEL) {
+               ret += NFSD4_MAX_SEC_LABEL_LEN + 12;
+               bmap2 &= ~FATTR4_WORD2_SECURITY_LABEL;
+       }
+       /*
+        * Largest of remaining attributes are 16 bytes (e.g.,
+        * supported_attributes)
+        */
+       ret += 16 * (hweight32(bmap0) + hweight32(bmap1) + hweight32(bmap2));
+       /* bitmask, length */
+       ret += 20;
+       return ret;
+}
+
 static inline u32 nfsd4_link_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
 {
        return (op_encode_hdr_size + op_encode_change_info_maxsz)
@@ -1603,6 +1646,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
        [OP_GETATTR] = {
                .op_func = (nfsd4op_func)nfsd4_getattr,
                .op_flags = ALLOWED_ON_ABSENT_FS,
+               .op_rsize_bop = nfsd4_getattr_rsize,
                .op_name = "OP_GETATTR",
        },
        [OP_GETFH] = {