NFSD: Fill in WCC data for REMOVE, RMDIR, MKNOD, and MKDIR
authorChuck Lever <chuck.lever@oracle.com>
Tue, 6 Jul 2010 20:53:34 +0000 (16:53 -0400)
committerJ. Bruce Fields <bfields@citi.umich.edu>
Wed, 7 Jul 2010 21:12:32 +0000 (17:12 -0400)
Some well-known NFSv3 clients drop their directory entry caches when
they receive replies with no WCC data.  Without this data, they
employ extra READ, LOOKUP, and GETATTR requests to ensure their
directory entry caches are up to date, causing performance to suffer
needlessly.

In order to return WCC data, our server has to have both the pre-op
and the post-op attribute data on hand when a reply is XDR encoded.
The pre-op data is filled in when the incoming fh is locked, and the
post-op data is filled in when the fh is unlocked.

Unfortunately, for REMOVE, RMDIR, MKNOD, and MKDIR, the directory fh
is not unlocked until well after the reply has been XDR encoded.  This
means that encode_wcc_data() does not have wcc_data for the parent
directory, so none is returned to the client after these operations
complete.

By unlocking the parent directory fh immediately after the internal
operations for each NFS procedure is complete, the post-op data is
filled in before XDR encoding starts, so it can be returned to the
client properly.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
fs/nfsd/nfs3proc.c

index 3d68f45a37b931027ef1e5e729305a4f7645fcf5..9ae93317abe444933f56ae7c688c195519d5c2ca 100644 (file)
@@ -271,7 +271,7 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
        fh_init(&resp->fh, NFS3_FHSIZE);
        nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
                                    &argp->attrs, S_IFDIR, 0, &resp->fh);
-
+       fh_unlock(&resp->dirfh);
        RETURN_STATUS(nfserr);
 }
 
@@ -327,7 +327,7 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp, struct nfsd3_mknodargs *argp,
        type = nfs3_ftypes[argp->ftype];
        nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
                                    &argp->attrs, type, rdev, &resp->fh);
-
+       fh_unlock(&resp->dirfh);
        RETURN_STATUS(nfserr);
 }
 
@@ -348,6 +348,7 @@ nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
        /* Unlink. -S_IFDIR means file must not be a directory */
        fh_copy(&resp->fh, &argp->fh);
        nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len);
+       fh_unlock(&resp->fh);
        RETURN_STATUS(nfserr);
 }
 
@@ -367,6 +368,7 @@ nfsd3_proc_rmdir(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
 
        fh_copy(&resp->fh, &argp->fh);
        nfserr = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, argp->name, argp->len);
+       fh_unlock(&resp->fh);
        RETURN_STATUS(nfserr);
 }