From: Jeff Layton <jlayton@kernel.org>
To: Alexander Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>, Jan Kara <jack@suse.cz>,
Chuck Lever <chuck.lever@oracle.com>,
Alexander Aring <alex.aring@gmail.com>,
Steven Rostedt <rostedt@goodmis.org>,
Masami Hiramatsu <mhiramat@kernel.org>,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
NeilBrown <neil@brown.name>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>,
Trond Myklebust <trondmy@kernel.org>,
Anna Schumaker <anna@kernel.org>,
Amir Goldstein <amir73il@gmail.com>
Cc: Calum Mackay <calum.mackay@oracle.com>,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
linux-nfs@vger.kernel.org, Jeff Layton <jlayton@kernel.org>
Subject: [PATCH v3 15/28] nfsd: use RCU to protect fi_deleg_file
Date: Tue, 28 Apr 2026 08:09:59 +0100 [thread overview]
Message-ID: <20260428-dir-deleg-v3-15-5a0780ba9def@kernel.org> (raw)
In-Reply-To: <20260428-dir-deleg-v3-0-5a0780ba9def@kernel.org>
fi_deleg_file can be NULLed by put_deleg_file() when fi_delegees drops
to zero during delegation teardown (e.g. DELEGRETURN). Concurrent
accesses from workqueue callbacks -- such as CB_NOTIFY -- can
dereference a NULL pointer if they race with this teardown.
Annotate fi_deleg_file with __rcu and convert all accessors to use
proper RCU primitives:
- rcu_assign_pointer() / RCU_INIT_POINTER() for stores
- rcu_dereference_protected() for reads under fi_lock or where
fi_delegees > 0 guarantees stability
This prepares for a subsequent patch that will use rcu_read_lock +
rcu_dereference + nfsd_file_get to safely acquire a reference from
the CB_NOTIFY callback path without holding fi_lock.
Assisted-by: Claude (Anthropic Claude Code)
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfs4layouts.c | 2 +-
fs/nfsd/nfs4state.c | 40 ++++++++++++++++++++++++----------------
fs/nfsd/state.h | 2 +-
3 files changed, 26 insertions(+), 18 deletions(-)
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index 8974e3d85d75..d32cc6b38c23 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -248,7 +248,7 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
NFSPROC4_CLNT_CB_LAYOUT);
if (parent->sc_type == SC_TYPE_DELEG)
- ls->ls_file = nfsd_file_get(fp->fi_deleg_file);
+ ls->ls_file = nfsd_file_get(rcu_dereference_protected(fp->fi_deleg_file, 1));
else
ls->ls_file = find_any_file(fp);
BUG_ON(!ls->ls_file);
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index c0046fc3c1b1..ef04e26b4f30 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1212,7 +1212,9 @@ static void put_deleg_file(struct nfs4_file *fp)
spin_lock(&fp->fi_lock);
if (--fp->fi_delegees == 0) {
- swap(nf, fp->fi_deleg_file);
+ nf = rcu_dereference_protected(fp->fi_deleg_file,
+ lockdep_is_held(&fp->fi_lock));
+ rcu_assign_pointer(fp->fi_deleg_file, NULL);
swap(rnf, fp->fi_rdeleg_file);
}
spin_unlock(&fp->fi_lock);
@@ -1295,7 +1297,7 @@ static void nfsd_fsnotify_recalc_mask(struct nfsd_file *nf)
static void nfs4_unlock_deleg_lease(struct nfs4_delegation *dp)
{
struct nfs4_file *fp = dp->dl_stid.sc_file;
- struct nfsd_file *nf = fp->fi_deleg_file;
+ struct nfsd_file *nf = rcu_dereference_protected(fp->fi_deleg_file, 1);
WARN_ON_ONCE(!fp->fi_delegees);
@@ -3167,7 +3169,8 @@ static int nfs4_show_deleg(struct seq_file *s, struct nfs4_stid *st)
/* XXX: lease time, whether it's being recalled. */
spin_lock(&nf->fi_lock);
- file = nf->fi_deleg_file;
+ file = rcu_dereference_protected(nf->fi_deleg_file,
+ lockdep_is_held(&nf->fi_lock));
if (file) {
seq_puts(s, ", ");
nfs4_show_superblock(s, file);
@@ -4949,7 +4952,7 @@ static void nfsd4_file_init(const struct svc_fh *fh, struct nfs4_file *fp)
INIT_LIST_HEAD(&fp->fi_delegations);
INIT_LIST_HEAD(&fp->fi_clnt_odstate);
fh_copy_shallow(&fp->fi_fhandle, &fh->fh_handle);
- fp->fi_deleg_file = NULL;
+ RCU_INIT_POINTER(fp->fi_deleg_file, NULL);
fp->fi_rdeleg_file = NULL;
fp->fi_had_conflict = false;
fp->fi_share_deny = 0;
@@ -6101,7 +6104,7 @@ static struct file_lease *nfs4_alloc_init_lease(struct nfs4_delegation *dp, u32
fl->c.flc_type = deleg_is_read(dp->dl_type) ? F_RDLCK : F_WRLCK;
fl->c.flc_owner = (fl_owner_t)dp;
fl->c.flc_pid = current->tgid;
- fl->c.flc_file = dp->dl_stid.sc_file->fi_deleg_file->nf_file;
+ fl->c.flc_file = rcu_dereference_protected(dp->dl_stid.sc_file->fi_deleg_file, 1)->nf_file;
return fl;
}
@@ -6109,7 +6112,7 @@ static int nfsd4_check_conflicting_opens(struct nfs4_client *clp,
struct nfs4_file *fp)
{
struct nfs4_ol_stateid *st;
- struct file *f = fp->fi_deleg_file->nf_file;
+ struct file *f = rcu_dereference_protected(fp->fi_deleg_file, 1)->nf_file;
struct inode *ino = file_inode(f);
int writes;
@@ -6186,7 +6189,7 @@ nfsd4_verify_deleg_dentry(struct nfsd4_open *open, struct nfs4_file *fp,
exp_put(exp);
dput(child);
- if (child != file_dentry(fp->fi_deleg_file->nf_file))
+ if (child != file_dentry(rcu_dereference_protected(fp->fi_deleg_file, 1)->nf_file))
return -EAGAIN;
return 0;
@@ -6292,8 +6295,9 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
status = -EAGAIN;
else if (nfsd4_verify_setuid_write(open, nf))
status = -EAGAIN;
- else if (!fp->fi_deleg_file) {
- fp->fi_deleg_file = nf;
+ else if (!rcu_dereference_protected(fp->fi_deleg_file,
+ lockdep_is_held(&fp->fi_lock))) {
+ rcu_assign_pointer(fp->fi_deleg_file, nf);
/* increment early to prevent fi_deleg_file from being
* cleared */
fp->fi_delegees = 1;
@@ -6318,7 +6322,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
if (!fl)
goto out_clnt_odstate;
- status = kernel_setlease(fp->fi_deleg_file->nf_file,
+ status = kernel_setlease(rcu_dereference_protected(fp->fi_deleg_file, 1)->nf_file,
fl->c.flc_type, &fl, NULL);
if (fl)
locks_free_lease(fl);
@@ -6339,7 +6343,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
* Now that the deleg is set, check again to ensure that nothing
* raced in and changed the mode while we weren't looking.
*/
- status = nfsd4_verify_setuid_write(open, fp->fi_deleg_file);
+ status = nfsd4_verify_setuid_write(open, rcu_dereference_protected(fp->fi_deleg_file, 1));
if (status)
goto out_unlock;
@@ -6360,7 +6364,8 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
return dp;
out_unlock:
- kernel_setlease(fp->fi_deleg_file->nf_file, F_UNLCK, NULL, (void **)&dp);
+ kernel_setlease(rcu_dereference_protected(fp->fi_deleg_file, 1)->nf_file,
+ F_UNLCK, NULL, (void **)&dp);
out_clnt_odstate:
put_clnt_odstate(dp->dl_clnt_odstate);
nfs4_put_stid(&dp->dl_stid);
@@ -6517,8 +6522,9 @@ nfs4_open_delegation(struct svc_rqst *rqstp, struct nfsd4_open *open,
memcpy(&open->op_delegate_stateid, &dp->dl_stid.sc_stateid, sizeof(dp->dl_stid.sc_stateid));
if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) {
- struct file *f = dp->dl_stid.sc_file->fi_deleg_file->nf_file;
+ struct file *f;
+ f = rcu_dereference_protected(dp->dl_stid.sc_file->fi_deleg_file, 1)->nf_file;
if (!nfsd4_add_rdaccess_to_wrdeleg(rqstp, open, fh, stp) ||
!nfs4_delegation_stat(dp, currentfh, &stat)) {
nfs4_put_stid(&dp->dl_stid);
@@ -9660,8 +9666,9 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
/* existing delegation? */
if (nfs4_delegation_exists(clp, fp)) {
status = -EAGAIN;
- } else if (!fp->fi_deleg_file) {
- fp->fi_deleg_file = nfsd_file_get(nf);
+ } else if (!rcu_dereference_protected(fp->fi_deleg_file,
+ lockdep_is_held(&fp->fi_lock))) {
+ rcu_assign_pointer(fp->fi_deleg_file, nfsd_file_get(nf));
fp->fi_delegees = 1;
} else {
++fp->fi_delegees;
@@ -9713,7 +9720,8 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
}
/* Something failed. Drop the lease and clean up the stid */
- kernel_setlease(fp->fi_deleg_file->nf_file, F_UNLCK, NULL, (void **)&dp);
+ kernel_setlease(rcu_dereference_protected(fp->fi_deleg_file, 1)->nf_file,
+ F_UNLCK, NULL, (void **)&dp);
out_put_stid:
nfs4_put_stid(&dp->dl_stid);
out_delegees:
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 22c9a1e7d8fd..eb5946b0999e 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -698,7 +698,7 @@ struct nfs4_file {
*/
atomic_t fi_access[2];
u32 fi_share_deny;
- struct nfsd_file *fi_deleg_file;
+ struct nfsd_file __rcu *fi_deleg_file;
struct nfsd_file *fi_rdeleg_file;
int fi_delegees;
struct knfsd_fh fi_fhandle;
--
2.54.0
next prev parent reply other threads:[~2026-04-28 7:11 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-28 7:09 [PATCH v3 00/28] vfs/nfsd: add support for CB_NOTIFY callbacks in directory delegations Jeff Layton
2026-04-28 7:09 ` [PATCH v3 01/28] filelock: pass current blocking lease to trace_break_lease_block() rather than "new_fl" Jeff Layton
2026-04-28 7:09 ` [PATCH v3 02/28] filelock: add support for ignoring deleg breaks for dir change events Jeff Layton
2026-04-28 7:09 ` [PATCH v3 03/28] filelock: add a tracepoint to start of break_lease() Jeff Layton
2026-04-28 7:09 ` [PATCH v3 04/28] filelock: add an inode_lease_ignore_mask helper Jeff Layton
2026-04-28 7:09 ` [PATCH v3 05/28] fsnotify: new tracepoint in fsnotify() Jeff Layton
2026-04-28 7:09 ` [PATCH v3 06/28] fsnotify: add fsnotify_modify_mark_mask() Jeff Layton
2026-04-28 7:09 ` [PATCH v3 07/28] fsnotify: add FSNOTIFY_EVENT_RENAME data type Jeff Layton
2026-04-28 7:09 ` [PATCH v3 08/28] nfsd: check fl_lmops in nfsd_breaker_owns_lease() Jeff Layton
2026-04-28 7:09 ` [PATCH v3 09/28] nfsd: add protocol support for CB_NOTIFY Jeff Layton
2026-04-28 7:09 ` [PATCH v3 10/28] nfs_common: add new NOTIFY4_* flags proposed in RFC8881bis Jeff Layton
2026-04-28 7:09 ` [PATCH v3 11/28] nfsd: allow nfsd to get a dir lease with an ignore mask Jeff Layton
2026-04-28 7:09 ` [PATCH v3 12/28] nfsd: update the fsnotify mark when setting or removing a dir delegation Jeff Layton
2026-04-28 7:09 ` [PATCH v3 13/28] nfsd: make nfsd4_callback_ops->prepare operation bool return Jeff Layton
2026-04-28 7:09 ` [PATCH v3 14/28] nfsd: add callback encoding and decoding linkages for CB_NOTIFY Jeff Layton
2026-04-28 7:09 ` Jeff Layton [this message]
2026-04-28 7:10 ` [PATCH v3 16/28] nfsd: add data structures for handling CB_NOTIFY Jeff Layton
2026-04-28 7:10 ` [PATCH v3 17/28] nfsd: add notification handlers for dir events Jeff Layton
2026-04-28 7:10 ` [PATCH v3 18/28] nfsd: add tracepoint to dir_event handler Jeff Layton
2026-04-28 7:10 ` [PATCH v3 19/28] nfsd: apply the notify mask to the delegation when requested Jeff Layton
2026-04-28 7:10 ` [PATCH v3 20/28] nfsd: add helper to marshal a fattr4 from completed args Jeff Layton
2026-04-28 7:10 ` [PATCH v3 21/28] nfsd: allow nfsd4_encode_fattr4_change() to work with no export Jeff Layton
2026-04-28 7:10 ` [PATCH v3 22/28] nfsd: send basic file attributes in CB_NOTIFY Jeff Layton
2026-04-28 7:10 ` [PATCH v3 23/28] nfsd: allow encoding a filehandle into fattr4 without a svc_fh Jeff Layton
2026-04-28 7:10 ` [PATCH v3 24/28] nfsd: add a fi_connectable flag to struct nfs4_file Jeff Layton
2026-04-28 7:10 ` [PATCH v3 25/28] nfsd: add the filehandle to returned attributes in CB_NOTIFY Jeff Layton
2026-04-28 7:10 ` [PATCH v3 26/28] nfsd: properly track requested child attributes Jeff Layton
2026-04-28 7:10 ` [PATCH v3 27/28] nfsd: track requested dir attributes Jeff Layton
2026-04-28 7:10 ` [PATCH v3 28/28] nfsd: add support to CB_NOTIFY for dir attribute changes Jeff Layton
2026-04-29 13:41 ` [PATCH v3 00/28] vfs/nfsd: add support for CB_NOTIFY callbacks in directory delegations Chuck Lever
2026-05-15 17:26 ` (subset) " Christian Brauner
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=20260428-dir-deleg-v3-15-5a0780ba9def@kernel.org \
--to=jlayton@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=alex.aring@gmail.com \
--cc=amir73il@gmail.com \
--cc=anna@kernel.org \
--cc=brauner@kernel.org \
--cc=calum.mackay@oracle.com \
--cc=chuck.lever@oracle.com \
--cc=corbet@lwn.net \
--cc=jack@suse.cz \
--cc=linux-doc@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=mhiramat@kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=rostedt@goodmis.org \
--cc=skhan@linuxfoundation.org \
--cc=tom@talpey.com \
--cc=trondmy@kernel.org \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.