2020-01-06 18:43:05

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 1/9] nfsd: Allow nfsd_vfs_write() to take the nfsd_file as an argument

Needed in order to fix stable writes.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/nfs4proc.c | 2 +-
fs/nfsd/vfs.c | 5 +++--
fs/nfsd/vfs.h | 4 +++-
3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 4798667af647..3d4e78118e53 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1012,7 +1012,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
&write->wr_head, write->wr_buflen);
WARN_ON_ONCE(nvecs > ARRAY_SIZE(rqstp->rq_vec));

- status = nfsd_vfs_write(rqstp, &cstate->current_fh, nf->nf_file,
+ status = nfsd_vfs_write(rqstp, &cstate->current_fh, nf,
write->wr_offset, rqstp->rq_vec, nvecs, &cnt,
write->wr_how_written);
nfsd_file_put(nf);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 82cf80dde5c7..69cbdb62b262 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -947,10 +947,11 @@ static int wait_for_concurrent_writes(struct file *file)
}

__be32
-nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
+nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
loff_t offset, struct kvec *vec, int vlen,
unsigned long *cnt, int stable)
{
+ struct file *file = nf->nf_file;
struct svc_export *exp;
struct iov_iter iter;
__be32 nfserr;
@@ -1057,7 +1058,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
if (err)
goto out;

- err = nfsd_vfs_write(rqstp, fhp, nf->nf_file, offset, vec,
+ err = nfsd_vfs_write(rqstp, fhp, nf, offset, vec,
vlen, cnt, stable);
nfsd_file_put(nf);
out:
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index cc110a10bfe8..fd779c3bb35b 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -34,6 +34,8 @@
#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)

+struct nfsd_file;
+
/*
* Callback function for readdir
*/
@@ -93,7 +95,7 @@ __be32 nfsd_read(struct svc_rqst *, struct svc_fh *,
__be32 nfsd_write(struct svc_rqst *, struct svc_fh *, loff_t,
struct kvec *, int, unsigned long *, int);
__be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
- struct file *file, loff_t offset,
+ struct nfsd_file *nf, loff_t offset,
struct kvec *vec, int vlen, unsigned long *cnt,
int stable);
__be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
--
2.24.1


2020-01-06 18:43:05

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 2/9] 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 <[email protected]>
---
fs/nfsd/filecache.c | 1 +
fs/nfsd/filecache.h | 1 +
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 69cbdb62b262..218b8293c633 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);
--
2.24.1

2020-01-06 18:43:06

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 3/9] nfsd: Update the boot verifier on stable writes too.

We don't know if the error returned by the fsync() call is
exclusive to the data written by the stable write, so play it
safe.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfsd/vfs.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 218b8293c633..38db4a083375 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -994,8 +994,11 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
host_err = vfs_iter_write(file, &iter, &pos, flags);
up_read(&nf->nf_rwsem);
}
- if (host_err < 0)
+ if (host_err < 0) {
+ nfsd_reset_boot_verifier(net_generic(SVC_NET(rqstp),
+ nfsd_net_id));
goto out_nfserr;
+ }
*cnt = host_err;
nfsdstats.io_write += *cnt;
fsnotify_modify(file);
--
2.24.1