Skip to contentSkip to conversation

nfsd: reset write verifier when async COPY writeback fails

PATCH In review Chuck Lever 2 participants 2 messages May 22

Subsystems (3)
lore
Share & tools
lore

Chuck Lever

author maintainer
#0 lore raw

PATCH v2 1/1 nfsd: reset write verifier when async COPY writeback fails

From: Chuck Lever <[email protected]>

Async COPY captures nn->writeverf at request time and reports it to the client via CB_OFFLOAD after the worker kthread completes. When the post-copy vfs_fsync_range() or filemap_check_wb_err() in _nfsd_copy_file_range() reports an error, the worker correctly leaves NFSD4_COPY_F_COMMITTED clear so that CB_OFFLOAD encodes wr_stable_how as NFS_UNSTABLE, but the server's write verifier is not rotated.

A client that receives NFS_UNSTABLE in CB_OFFLOAD follows up with COMMIT to make the copied data durable. With the verifier unchanged, COMMIT returns the same value the client just received via CB_OFFLOAD, and the client concludes the copy is durable -- silently dropping the data whose writeback in fact failed. This violates the UNSTABLE+COMMIT durability contract (RFC 7862 section 15.1, RFC 8881 section 18.32) and matches the bug just fixed in nfsd_vfs_write() and nfsd_commit().

Rotate nn->writeverf at the writeback-failure site. The async COPY worker has no svc_rqst, so commit_reset_write_verifier() is not available here; calling nfsd_reset_write_verifier() directly mirrors the trace-less reset already used by nfsd_file_check_write_error() for the same purpose. Filter out -EAGAIN and -ESTALE, matching commit_reset_write_verifier(), since neither indicates a durable-storage failure.

Fixes eac0b17a77fb ("NFSD add vfs_fsync after async copy is done")
Assisted-by kres:claude-opus-4-7
---
 fs/nfsd/nfs4proc.c | 2 ++
 1 file changed, 2 insertions(+)
fs/nfsd/nfs4proc.c +2 -0
index 8561540ab2db..93fcaf90d6ae 100644
@@ -1972,6 +1972,8 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy,
 			status = filemap_check_wb_err(dst->f_mapping, since);
 		if (!status)
 			set_bit(NFSD4_COPY_F_COMMITTED, &copy->cp_flags);
+		else if (status != -EAGAIN && status != -ESTALE)
+			nfsd_reset_write_verifier(copy->cp_nn);
 	}
 	return bytes_copied;
 }
Signature · 1 line
2.54.0
maintainer Reviewed-by on v2 1/1
#1 lore raw
Quoted 40 lines · Chuck Lever

From: Chuck Lever <[email protected]>

Async COPY captures nn->writeverf at request time and reports it to the client via CB_OFFLOAD after the worker kthread completes. When the post-copy vfs_fsync_range() or filemap_check_wb_err() in _nfsd_copy_file_range() reports an error, the worker correctly leaves NFSD4_COPY_F_COMMITTED clear so that CB_OFFLOAD encodes wr_stable_how as NFS_UNSTABLE, but the server's write verifier is not rotated.

A client that receives NFS_UNSTABLE in CB_OFFLOAD follows up with COMMIT to make the copied data durable. With the verifier unchanged, COMMIT returns the same value the client just received via CB_OFFLOAD, and the client concludes the copy is durable -- silently dropping the data whose writeback in fact failed. This violates the UNSTABLE+COMMIT durability contract (RFC 7862 section 15.1, RFC 8881 section 18.32) and matches the bug just fixed in nfsd_vfs_write() and nfsd_commit().

Rotate nn->writeverf at the writeback-failure site. The async COPY worker has no svc_rqst, so commit_reset_write_verifier() is not available here; calling nfsd_reset_write_verifier() directly mirrors the trace-less reset already used by nfsd_file_check_write_error() for the same purpose. Filter out -EAGAIN and -ESTALE, matching commit_reset_write_verifier(), since neither indicates a durable-storage failure.

Fixes eac0b17a77fb ("NFSD add vfs_fsync after async copy is done")
Assisted-by kres:claude-opus-4-7
---
 fs/nfsd/nfs4proc.c | 2 ++
 1 file changed, 2 insertions(+)
fs/nfsd/nfs4proc.c +0 -0
index 8561540ab2db..93fcaf90d6ae 100644

fs/nfsd/nfs4proc.c static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy, · line 1979

Quoted 8 lines
fs/nfsd/nfs4proc.c +2 -0
@@ -1972,6 +1972,8 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy,
 			status = filemap_check_wb_err(dst->f_mapping, since);
 		if (!status)
 			set_bit(NFSD4_COPY_F_COMMITTED, &copy->cp_flags);
+		else if (status != -EAGAIN && status != -ESTALE)
+			nfsd_reset_write_verifier(copy->cp_nn);
 	}
 	return bytes_copied;
 }