[PATCH 2/2] nfsd: Honor delegated timestamps when change and size match
From: Jeff Layton
Date: Mon Nov 02 2026 - 17:12:03 EST
nfsd4_deleg_getattr_conflict() applies time_deleg_access and
time_deleg_modify from a CB_GETATTR reply only when the reply's change
attribute or size differs from the server's. RFC 9754 section 5 makes
the holder of a timestamp delegation the authority for atime and mtime,
and puts no such condition on accepting them.
Because the delegation's file has FMODE_NOCMTIME, the holder's WRITEs
change neither mtime nor the change attribute on the server. After a
flushed WRITE, the CB_GETATTR reply matches on change and size, the
holder's mtime is dropped, and other clients see new data with the
pre-WRITE mtime. Since commit 304d81a2fbf2 ("nfsd: fix file change
detection in CB_GETATTR") compares against the current file size, this
also hits flushed extending WRITEs. Holder reads lose the atime the
same way.
- For timestamp delegations, always vet and apply the CB_GETATTR
times.
- If that advances mtime, mark the file modified so that GETATTR
reports a new change attribute (RFC 9754: the change time is derived
from the modify time). An atime-only update must not advance the
change time, so leave the file unmodified in that case.
Fixes: 6ae30d6eb26b ("nfsd: add support for delegated timestamps")
Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
---
fs/nfsd/nfs4state.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index cd70d86f4bc6..418b3ee37c68 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -10751,6 +10751,13 @@ static int cb_getattr_update_times(struct dentry *dentry, struct nfs4_delegation
inode_lock(inode);
ret = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
inode_unlock(inode);
+
+ /*
+ * FMODE_NOCMTIME kept the holder's WRITEs from bumping the change
+ * attribute; a newer delegated mtime is the only evidence of them.
+ */
+ if (!ret && (attrs.ia_valid & ATTR_MTIME))
+ ncf->ncf_file_modified = true;
return ret;
}
@@ -10836,7 +10843,11 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
else if (i_size_read(inode) != ncf->ncf_cb_fsize)
ncf->ncf_file_modified = true;
}
- if (ncf->ncf_file_modified) {
+ /*
+ * The holder of a timestamp delegation is the authority for atime
+ * and mtime (RFC 9754, section 5), even if change and size match.
+ */
+ if (ncf->ncf_file_modified || deleg_attrs_deleg(dp->dl_type)) {
int err;
/*
@@ -10849,6 +10860,8 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
status = nfserrno(err);
goto out_status;
}
+ }
+ if (ncf->ncf_file_modified) {
ncf->ncf_cur_fsize = ncf->ncf_cb_fsize;
*pdp = dp;
return nfs_ok;
--
2.56.0