nfsd: Fix stable writes
authorTrond Myklebust <trondmy@gmail.com>
Mon, 6 Jan 2020 18:40:30 +0000 (13:40 -0500)
committerJ. Bruce Fields <bfields@redhat.com>
Wed, 22 Jan 2020 21:25:40 +0000 (16:25 -0500)
Strictly speaking, a stable write error needs to reflect the
write + the commit of that write (and only that write). To
ensure that we don't pick up the write errors from other
writebacks, add a rw_semaphore to provide exclusion.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/filecache.c
fs/nfsd/filecache.h
fs/nfsd/vfs.c

index f275c11c4e284dcaad70058b88d4a4e8bbd543f7..2fadf080ac42f6f30eca7b46264919f817f4b51a 100644 (file)
@@ -195,6 +195,7 @@ nfsd_file_alloc(struct inode *inode, unsigned int may, unsigned int hashval,
                                __set_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags);
                }
                nf->nf_mark = NULL;
+               init_rwsem(&nf->nf_rwsem);
                trace_nfsd_file_alloc(nf);
        }
        return nf;
index 79a7d6808d979970d33a41509d3587b8496ccb22..986c325a54bd393de65f3894d43cf2f841b02a88 100644 (file)
@@ -46,6 +46,7 @@ struct nfsd_file {
        atomic_t                nf_ref;
        unsigned char           nf_may;
        struct nfsd_file_mark   *nf_mark;
+       struct rw_semaphore     nf_rwsem;
 };
 
 int nfsd_file_cache_init(void);
index e1ffefab2552ff417789eae705b0f5e3f953efd3..4652854f3dd575bb628f080a86c459bd71ad209a 100644 (file)
@@ -982,7 +982,18 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
                flags |= RWF_SYNC;
 
        iov_iter_kvec(&iter, WRITE, vec, vlen, *cnt);
-       host_err = vfs_iter_write(file, &iter, &pos, flags);
+       if (flags & RWF_SYNC) {
+               down_write(&nf->nf_rwsem);
+               host_err = vfs_iter_write(file, &iter, &pos, flags);
+               if (host_err < 0)
+                       nfsd_reset_boot_verifier(net_generic(SVC_NET(rqstp),
+                                                nfsd_net_id));
+               up_write(&nf->nf_rwsem);
+       } else {
+               down_read(&nf->nf_rwsem);
+               host_err = vfs_iter_write(file, &iter, &pos, flags);
+               up_read(&nf->nf_rwsem);
+       }
        if (host_err < 0)
                goto out_nfserr;
        *cnt = host_err;
@@ -1097,8 +1108,10 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
        if (err)
                goto out;
        if (EX_ISSYNC(fhp->fh_export)) {
-               int err2 = vfs_fsync_range(nf->nf_file, offset, end, 0);
+               int err2;
 
+               down_write(&nf->nf_rwsem);
+               err2 = vfs_fsync_range(nf->nf_file, offset, end, 0);
                switch (err2) {
                case 0:
                        break;
@@ -1110,6 +1123,7 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
                        nfsd_reset_boot_verifier(net_generic(nf->nf_net,
                                                 nfsd_net_id));
                }
+               up_write(&nf->nf_rwsem);
        }
 
        nfsd_file_put(nf);