| From 221203ce6406273cf00e5c6397257d986c003ee6 Mon Sep 17 00:00:00 2001 |
| From: Trond Myklebust <trondmy@gmail.com> |
| Date: Mon, 6 Jan 2020 15:25:04 -0500 |
| Subject: NFS/pnfs: Fix pnfs_generic_prepare_to_resend_writes() |
| |
| From: Trond Myklebust <trondmy@gmail.com> |
| |
| commit 221203ce6406273cf00e5c6397257d986c003ee6 upstream. |
| |
| 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@hammerspace.com> |
| Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| 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(-) |
| |
| --- a/fs/nfs/direct.c |
| +++ b/fs/nfs/direct.c |
| @@ -245,10 +245,10 @@ static int nfs_direct_cmp_commit_data_ve |
| 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); |
| } |
| |
| /** |
| --- a/fs/nfs/nfs3xdr.c |
| +++ b/fs/nfs/nfs3xdr.c |
| @@ -2338,6 +2338,7 @@ static int nfs3_xdr_dec_commit3res(struc |
| void *data) |
| { |
| struct nfs_commitres *result = data; |
| + struct nfs_writeverf *verf = result->verf; |
| enum nfs_stat status; |
| int error; |
| |
| @@ -2350,7 +2351,9 @@ static int nfs3_xdr_dec_commit3res(struc |
| 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: |
| --- a/fs/nfs/nfs4xdr.c |
| +++ b/fs/nfs/nfs4xdr.c |
| @@ -4316,11 +4316,14 @@ static int decode_write_verifier(struct |
| |
| 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; |
| } |
| |
| --- a/fs/nfs/pnfs_nfs.c |
| +++ b/fs/nfs/pnfs_nfs.c |
| @@ -31,12 +31,11 @@ EXPORT_SYMBOL_GPL(pnfs_generic_rw_releas |
| /* 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); |
| |
| --- a/fs/nfs/write.c |
| +++ b/fs/nfs/write.c |
| @@ -1837,6 +1837,7 @@ static void nfs_commit_done(struct rpc_t |
| |
| 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; |
| @@ -1864,7 +1865,8 @@ static void nfs_commit_release_pages(str |
| |
| /* 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); |