[PATCH AUTOSEL 4.14 130/186] NFS/pnfs: Fix pnfs_generic_prepare_to_resend_writes()

From: Sasha Levin
Date: Fri Feb 14 2020 - 11:51:01 EST


From: Trond Myklebust <trondmy@xxxxxxxxx>

[ Upstream commit 221203ce6406273cf00e5c6397257d986c003ee6 ]

Instead of making assumptions about the commit verifier contents, change
the commit code to ensure we always check that the verifier was set
by the XDR code.

Fixes: f54bcf2ecee9 ("pnfs: Prepare for flexfiles by pulling out common code")
Signed-off-by: Trond Myklebust <trond.myklebust@xxxxxxxxxxxxxxx>
Signed-off-by: Anna Schumaker <Anna.Schumaker@xxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
fs/nfs/direct.c | 4 ++--
fs/nfs/nfs3xdr.c | 5 ++++-
fs/nfs/nfs4xdr.c | 5 ++++-
fs/nfs/pnfs_nfs.c | 7 +++----
fs/nfs/write.c | 4 +++-
5 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 9cdac9945483b..9d07b53e1647b 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -261,10 +261,10 @@ static int nfs_direct_cmp_commit_data_verf(struct nfs_direct_req *dreq,
data->ds_commit_index);

/* verifier not set so always fail */
- if (verfp->committed < 0)
+ if (verfp->committed < 0 || data->res.verf->committed <= NFS_UNSTABLE)
return 1;

- return nfs_direct_cmp_verf(verfp, &data->verf);
+ return nfs_direct_cmp_verf(verfp, data->res.verf);
}

/**
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 6cd33bd5da87c..f1cb0b7eb05f9 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -2373,6 +2373,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
void *data)
{
struct nfs_commitres *result = data;
+ struct nfs_writeverf *verf = result->verf;
enum nfs_stat status;
int error;

@@ -2385,7 +2386,9 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
result->op_status = status;
if (status != NFS3_OK)
goto out_status;
- error = decode_writeverf3(xdr, &result->verf->verifier);
+ error = decode_writeverf3(xdr, &verf->verifier);
+ if (!error)
+ verf->committed = NFS_FILE_SYNC;
out:
return error;
out_status:
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 525684b0056fc..0b2d051990e99 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4409,11 +4409,14 @@ static int decode_write_verifier(struct xdr_stream *xdr, struct nfs_write_verifi

static int decode_commit(struct xdr_stream *xdr, struct nfs_commitres *res)
{
+ struct nfs_writeverf *verf = res->verf;
int status;

status = decode_op_hdr(xdr, OP_COMMIT);
if (!status)
- status = decode_write_verifier(xdr, &res->verf->verifier);
+ status = decode_write_verifier(xdr, &verf->verifier);
+ if (!status)
+ verf->committed = NFS_FILE_SYNC;
return status;
}

diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c
index 4a3dd66175fed..b0ef37f3e2dd7 100644
--- a/fs/nfs/pnfs_nfs.c
+++ b/fs/nfs/pnfs_nfs.c
@@ -30,12 +30,11 @@ EXPORT_SYMBOL_GPL(pnfs_generic_rw_release);
/* Fake up some data that will cause nfs_commit_release to retry the writes. */
void pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data)
{
- struct nfs_page *first = nfs_list_entry(data->pages.next);
+ struct nfs_writeverf *verf = data->res.verf;

data->task.tk_status = 0;
- memcpy(&data->verf.verifier, &first->wb_verf,
- sizeof(data->verf.verifier));
- data->verf.verifier.data[0]++; /* ensure verifier mismatch */
+ memset(&verf->verifier, 0, sizeof(verf->verifier));
+ verf->committed = NFS_UNSTABLE;
}
EXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes);

diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index ed3f5afc4ff7f..89f36040adf62 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1807,6 +1807,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)

static void nfs_commit_release_pages(struct nfs_commit_data *data)
{
+ const struct nfs_writeverf *verf = data->res.verf;
struct nfs_page *req;
int status = data->task.tk_status;
struct nfs_commit_info cinfo;
@@ -1833,7 +1834,8 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)

/* Okay, COMMIT succeeded, apparently. Check the verifier
* returned by the server against all stored verfs. */
- if (!nfs_write_verifier_cmp(&req->wb_verf, &data->verf.verifier)) {
+ if (verf->committed > NFS_UNSTABLE &&
+ !nfs_write_verifier_cmp(&req->wb_verf, &verf->verifier)) {
/* We have a match */
if (req->wb_page)
nfs_inode_remove_request(req);
--
2.20.1