From: Jeff Layton <jlayton@kernel.org>
To: Chuck Lever <cel@kernel.org>, NeilBrown <neil@brown.name>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>, Jan Kara <jack@suse.cz>
Cc: Tom Haynes <loghyr@gmail.com>,
linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-fsdevel@vger.kernel.org, Jeff Layton <jlayton@kernel.org>
Subject: [PATCH v3 5/8] nfsd: mark the delegation written on COPY/CLONE instead of stamping c/mtime
Date: Mon, 09 Nov 2026 15:36:46 -0500 [thread overview]
Message-ID: <20261109-deleg-cb-getattr-times-v3-5-710c7244a6ea@kernel.org> (raw)
In-Reply-To: <20261109-deleg-cb-getattr-times-v3-0-710c7244a6ea@kernel.org>
Under a timestamp delegation, nfsd stamps the destination's c/mtime to
the server's "now" after a COPY or CLONE. Since commit 6e7daa3dad29
("NFSv4.2: fix CLONE/COPY attrs in presence of delegated attributes"),
the client also stamps its own c/mtime afterwards and ignores the
server's. If the client's coarse stamp lands even slightly before the
server's, the server can't move ctime back when the holder returns its
times, and ctime changes after DELEGRETURN. COPY returns no attributes,
so the client can't avoid this.
Treat COPY/CLONE like WRITE: mark the delegation written and let the
holder supply the times. If the holder never returns them, DELEGRETURN
stamps them via nfsd4_finalize_deleg_timestamps(); other clients'
GETATTRs go through CB_GETATTR. The async COPY thread has no
filehandle, so look up the nfs4_file at launch, and have the thread
mark the delegation when the copy has written something. Do the lookup
for every async COPY, so a delegation granted while the copy ran is
covered too. nfsd_update_cmtime_attr() is left without callers; remove
it.
Fixes: 2863bac7f49c ("nfsd: update mtime/ctime on CLONE in presense of delegated attributes")
Fixes: 4183cf383b6f ("nfsd: update mtime/ctime on COPY in presence of delegated attributes")
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4proc.c | 48 +++++++++++++++++++++++++++++++++++++-----------
fs/nfsd/nfs4state.c | 30 +-----------------------------
fs/nfsd/state.h | 2 +-
fs/nfsd/xdr4.h | 3 ++-
4 files changed, 41 insertions(+), 42 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 25613c1ffaaf..a65e13f0f77a 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1438,6 +1438,25 @@ static void nfsd4_file_mark_deleg_written(struct nfs4_file *fi)
spin_unlock(&fi->fi_lock);
}
+/*
+ * A COPY or CLONE into a file under a timestamp delegation is the holder's
+ * modification, like a WRITE: the holder supplies the c/mtime, and DELEGRETURN
+ * stamps them if it doesn't.
+ */
+static void nfsd4_copy_mark_deleg_written(struct svc_fh *fhp,
+ struct nfsd_file *dst)
+{
+ struct nfs4_file *fi;
+
+ if (!(READ_ONCE(dst->nf_file->f_mode) & FMODE_NOCMTIME))
+ return;
+ fi = nfsd4_file_hash_lookup(fhp);
+ if (fi) {
+ nfsd4_file_mark_deleg_written(fi);
+ put_nfs4_file(fi);
+ }
+}
+
static __be32
nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
@@ -1576,8 +1595,8 @@ nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
status = nfserrno(host_err);
- if (!status && (READ_ONCE(dst->nf_file->f_mode) & FMODE_NOCMTIME) != 0)
- nfsd_update_cmtime_attr(dst->nf_file, 0);
+ if (!status)
+ nfsd4_copy_mark_deleg_written(&cstate->current_fh, dst);
nfsd_file_put(dst);
nfsd_file_put(src);
@@ -2198,6 +2217,10 @@ static void release_copy_files(struct nfsd4_copy *copy)
nfsd_file_put(copy->nf_dst);
copy->nf_dst = NULL;
}
+ if (copy->cp_dst_fi) {
+ put_nfs4_file(copy->cp_dst_fi);
+ copy->cp_dst_fi = NULL;
+ }
}
/*
@@ -2301,8 +2324,9 @@ static int nfsd4_do_async_copy(void *data)
set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags);
trace_nfsd_copy_async_done(async->cp_stid.sc_client, copy);
atomic_dec(©->cp_nn->pending_async_copies);
- if (copy->cp_res.wr_bytes_written > 0 && copy->attr_update)
- nfsd_update_cmtime_attr(copy->nf_dst->nf_file, 0);
+ /* The holder may DELEGRETURN as soon as it sees CB_OFFLOAD */
+ if (copy->cp_res.wr_bytes_written > 0 && copy->cp_dst_fi)
+ nfsd4_file_mark_deleg_written(copy->cp_dst_fi);
nfsd4_send_cb_offload(async);
/* Drop the kthread's reference (taken in nfsd4_copy()); copy may be freed after this. */
nfs4_put_copy(async);
@@ -2382,9 +2406,12 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
* the callback stateid zeroed.
*/
dup_copy_fields(copy, &async_copy->cp_copy);
- if ((READ_ONCE(copy->nf_dst->nf_file->f_mode) &
- FMODE_NOCMTIME) != 0)
- async_copy->cp_copy.attr_update = true;
+ /*
+ * The copy thread has no filehandle. A timestamp delegation
+ * may be granted while the copy runs.
+ */
+ async_copy->cp_copy.cp_dst_fi =
+ nfsd4_file_hash_lookup(&cstate->current_fh);
memcpy(async_copy->cp_cb_offload.co_referring_sessionid.data,
cstate->session->se_sessionid.data,
NFS4_MAX_SESSIONID_LEN);
@@ -2415,10 +2442,9 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
} else {
status = nfsd4_do_copy(copy, copy->nf_src->nf_file,
copy->nf_dst->nf_file, true);
- if ((READ_ONCE(copy->nf_dst->nf_file->f_mode) &
- FMODE_NOCMTIME) != 0 &&
- copy->cp_res.wr_bytes_written > 0)
- nfsd_update_cmtime_attr(copy->nf_dst->nf_file, 0);
+ if (copy->cp_res.wr_bytes_written > 0)
+ nfsd4_copy_mark_deleg_written(&cstate->current_fh,
+ copy->nf_dst);
}
out:
trace_nfsd_copy_done(copy, status);
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 4566fa86f82f..3bb518270b8b 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -6304,7 +6304,7 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
nfs4_put_stid(&last->st_stid);
}
-static noinline_for_stack struct nfs4_file *
+noinline_for_stack struct nfs4_file *
nfsd4_file_hash_lookup(const struct svc_fh *fhp)
{
struct inode *inode = d_inode(fhp->fh_dentry);
@@ -11177,34 +11177,6 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
return ERR_PTR(status);
}
-/**
- * nfsd_update_cmtime_attr - update file's delegated ctime/mtime,
- * and optionally other attributes (ie ATTR_ATIME).
- * @f: pointer to an opened file
- * @flags: any additional flags that should be updated
- *
- * Given upon opening a file delegated attributes were issues, update
- * @f attributes to current times.
- */
-void nfsd_update_cmtime_attr(struct file *f, unsigned int flags)
-{
- int ret;
- struct inode *inode = file_inode(f);
- struct iattr attr = {
- .ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_DELEG | flags,
- };
-
- inode_lock(inode);
- ret = notify_change(&nop_mnt_idmap, f->f_path.dentry, &attr, NULL);
- inode_unlock(inode);
- if (ret)
- pr_notice_ratelimited("nfsd: Unable to update timestamps on "
- "inode %02x:%02x:%llu: %d\n",
- MAJOR(inode->i_sb->s_dev),
- MINOR(inode->i_sb->s_dev),
- inode->i_ino, ret);
-}
-
static void
nfsd4_run_cb_notify(struct nfsd4_cb_notify *ncn)
{
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 9e8699810e33..8a189f1b0133 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -981,13 +981,13 @@ void nfsd4_get_client(struct nfs4_client *clp);
void nfsd4_put_client(struct nfs4_client *clp);
void nfsd4_async_copy_reaper(struct nfsd_net *nn);
bool nfsd4_has_active_async_copies(struct nfs4_client *clp);
-void nfsd_update_cmtime_attr(struct file *f, unsigned int flags);
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
struct xdr_netobj princhash, struct nfsd_net *nn);
int nfsd_handle_dir_event(u32 mask, const struct inode *dir, const void *data,
int data_type, const struct qstr *name);
void put_nfs4_file(struct nfs4_file *fi);
+struct nfs4_file *nfsd4_file_hash_lookup(const struct svc_fh *fhp);
extern void nfs4_put_cpntf_state(struct nfsd_net *nn,
struct nfs4_cpntf_state *cps);
extern __be32 manage_cpntf_state(struct nfsd_net *nn, stateid_t *st,
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index feda5464f260..8cebeafbfc20 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -646,7 +646,8 @@ struct nfsd4_copy {
struct nfsd_file *nf_src;
struct nfsd_file *nf_dst;
- bool attr_update;
+ /* async: file whose timestamp delegation the copy marks written */
+ struct nfs4_file *cp_dst_fi;
struct nfsd4_ssc_umount_item *ss_nsui;
struct nfs_fh c_fh;
--
2.56.0
next prev parent reply other threads:[~2026-11-09 20:37 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 20:36 [PATCH v3 0/8] nfsd: more delegated timestamp fixes Jeff Layton
2026-11-09 20:36 ` [PATCH v3 1/8] nfsd: Report vetted delegated timestamps in GETATTR replies Jeff Layton
2026-11-09 20:36 ` [PATCH v3 2/8] nfsd: Honor delegated timestamps when change and size match Jeff Layton
2026-11-09 20:36 ` [PATCH v3 3/8] nfsd: don't stamp over an applied delegated mtime at DELEGRETURN Jeff Layton
2026-11-09 20:36 ` [PATCH v3 4/8] nfsd: don't vet delegated times against an exclusive-create verifier Jeff Layton
2026-11-09 20:36 ` Jeff Layton [this message]
2026-11-09 20:36 ` [PATCH v3 6/8] fs: check a fine-grained time before clamping a delegated ctime Jeff Layton
2026-11-09 20:36 ` [PATCH v3 7/8] nfsd: check a fine-grained time before clamping a delegated mtime Jeff Layton
2026-11-09 20:36 ` [PATCH v3 8/8] nfsd: raise delegated time floors after a size-changing SETATTR Jeff Layton
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261109-deleg-cb-getattr-times-v3-5-710c7244a6ea@kernel.org \
--to=jlayton@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=brauner@kernel.org \
--cc=cel@kernel.org \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=loghyr@gmail.com \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®