nfsd4: don't try to encode conflicting owner if low on space
authorJ. Bruce Fields <bfields@redhat.com>
Mon, 10 Mar 2014 16:19:10 +0000 (12:19 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Fri, 30 May 2014 21:31:55 +0000 (17:31 -0400)
I ran into this corner case in testing: in theory clients can provide
state owners up to 1024 bytes long.  In the sessions case there might be
a risk of this pushing us over the DRC slot size.

The conflicting owner isn't really that important, so let's humor a
client that provides a small maxresponsize_cached by allowing ourselves
to return without the conflicting owner instead of outright failing the
operation.

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

index 5d8f915..20aad5a 100644 (file)
@@ -1432,7 +1432,8 @@ out:
 #define op_encode_change_info_maxsz    (5)
 #define nfs4_fattr_bitmap_maxsz                (4)
 
-#define op_encode_lockowner_maxsz      (1 + XDR_QUADLEN(IDMAP_NAMESZ))
+/* We'll fall back on returning no lockowner if run out of space: */
+#define op_encode_lockowner_maxsz      (0)
 #define op_encode_lock_denied_maxsz    (8 + op_encode_lockowner_maxsz)
 
 #define nfs4_owner_maxsz               (1 + XDR_QUADLEN(IDMAP_NAMESZ))
index a90a1e8..2f0ea20 100644 (file)
@@ -2878,9 +2878,20 @@ nfsd4_encode_lock_denied(struct xdr_stream *xdr, struct nfsd4_lock_denied *ld)
        struct xdr_netobj *conf = &ld->ld_owner;
        __be32 *p;
 
+again:
        p = xdr_reserve_space(xdr, 32 + XDR_LEN(conf->len));
-       if (!p)
+       if (!p) {
+               /*
+                * Don't fail to return the result just because we can't
+                * return the conflicting open:
+                */
+               if (conf->len) {
+                       conf->len = 0;
+                       conf->data = NULL;
+                       goto again;
+               }
                return nfserr_resource;
+       }
        WRITE64(ld->ld_start);
        WRITE64(ld->ld_length);
        WRITE32(ld->ld_type);
@@ -2888,7 +2899,6 @@ nfsd4_encode_lock_denied(struct xdr_stream *xdr, struct nfsd4_lock_denied *ld)
                WRITEMEM(&ld->ld_clientid, 8);
                WRITE32(conf->len);
                WRITEMEM(conf->data, conf->len);
-               kfree(conf->data);
        }  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
                WRITE64((u64)0); /* clientid */
                WRITE32(0); /* length of owner name */
@@ -2905,7 +2915,7 @@ nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lo
                nfserr = nfsd4_encode_stateid(xdr, &lock->lk_resp_stateid);
        else if (nfserr == nfserr_denied)
                nfserr = nfsd4_encode_lock_denied(xdr, &lock->lk_denied);
-
+       kfree(lock->lk_denied.ld_owner.data);
        return nfserr;
 }