diff options
author | Trond Myklebust | 2020-01-06 13:40:30 -0500 |
---|---|---|
committer | J. Bruce Fields | 2020-01-22 16:25:40 -0500 |
commit | 5011af4c698a95b4dc4e374b1ce3acd122a9170f (patch) | |
tree | 963befb8ca298ac85242f9916fc808de07c33420 /fs/nfsd | |
parent | 16f8f894108270fd99667f06bae6ebcdd0f4e4bf (diff) |
nfsd: Fix stable writes
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>
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/filecache.c | 1 | ||||
-rw-r--r-- | fs/nfsd/filecache.h | 1 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 18 |
3 files changed, 18 insertions, 2 deletions
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c index f275c11c4e28..2fadf080ac42 100644 --- a/fs/nfsd/filecache.c +++ b/fs/nfsd/filecache.c @@ -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; diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h index 79a7d6808d97..986c325a54bd 100644 --- a/fs/nfsd/filecache.h +++ b/fs/nfsd/filecache.h @@ -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); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index e1ffefab2552..4652854f3dd5 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -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); |