mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Chuck Lever" <cel@kernel.org>
To: "Jeff Layton" <jlayton@kernel.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>,
	"Jonathan Corbet" <corbet@lwn.net>,
	"Shuah Khan" <skhan@linuxfoundation.org>
Cc: "Steven Rostedt" <rostedt@goodmis.org>,
	"Alexander Aring" <alex.aring@gmail.com>,
	"Amir Goldstein" <amir73il@gmail.com>, "Jan Kara" <jack@suse.cz>,
	"Alexander Viro" <viro@zeniv.linux.org.uk>,
	"Christian Brauner" <brauner@kernel.org>,
	"Calum Mackay" <calum.mackay@oracle.com>,
	linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-nfs@vger.kernel.org
Subject: Re: [PATCH v6 20/20] nfsd: add support to CB_NOTIFY for dir attribute changes
Date: Fri, 12 Jun 2026 14:21:58 -0400	[thread overview]
Message-ID: <0a0bfce8-c35e-4c96-8f7b-65267fd96a61@app.fastmail.com> (raw)
In-Reply-To: <20260611-dir-deleg-v6-20-4c45080e5f3f@kernel.org>



On Thu, Jun 11, 2026, at 1:50 PM, Jeff Layton wrote:
> If the client requested dir attribute change notifications, send those
> alongside any set of add/remove/rename events. Note that the server will
> still recall the delegation on a SETATTR, so these are only sent for
> changes to child dirents.
>
> The child filehandle returned in these notifications is composed by
> setup_notify_fhandle() without going through fh_compose(), so it does
> not get a MAC appended. On exports configured with NFSEXP_SIGN_FH the
> client would then get back an unsigned filehandle that fh_verify()
> rejects as stale. Pass the delegation's export down to
> setup_notify_fhandle() and append the MAC with fh_append_mac() when the
> export requires signed filehandles; if signing fails, drop the
> filehandle attribute rather than handing out an unusable one.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>  fs/nfsd/nfs4state.c | 25 ++++++++++++++++--
>  fs/nfsd/nfs4xdr.c   | 73 +++++++++++++++++++++++++++++++++++++++++++++--------
>  fs/nfsd/xdr4.h      |  2 ++
>  3 files changed, 88 insertions(+), 12 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 12627afb604f..e394278fb92e 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -3503,10 +3503,15 @@ nfsd4_cb_notify_prepare(struct nfsd4_callback *cb)
>  	struct nfsd_notify_event *events[NOTIFY4_EVENT_QUEUE_SIZE];
>  	struct xdr_buf xdr = { .buflen = PAGE_SIZE * NOTIFY4_PAGE_ARRAY_SIZE,
>  			       .pages  = ncn->ncn_pages };
> +	int limit = NOTIFY4_EVENT_QUEUE_SIZE;

When a client requests NOTIFY4_CHANGE_DIR_ATTRS, the CB_NOTIFY event
queue can fill to NOTIFY4_EVENT_QUEUE_SIZE (3) events while the consumer
only accepts 2 (it reserves a slot for the dir-attr-change entry). The
resulting overflow path in nfsd4_cb_notify_prepare() recalls the
delegation without draining the queue, and nfsd4_cb_notify_release()
then requeues the same callback indefinitely.


>  	struct xdr_stream stream;
>  	struct nfsd_file *nf;
> -	int count, i;
>  	bool error = false;
> +	int count, i;
> +
> +	/* Save a slot for dir attr update if requested */
> +	if (dp->dl_notify_mask & BIT(NOTIFY4_CHANGE_DIR_ATTRS))
> +		--limit;
> 
>  	xdr_init_encode_pages(&stream, &xdr);
> 
> @@ -3520,7 +3525,7 @@ nfsd4_cb_notify_prepare(struct nfsd4_callback *cb)
>  	}
> 
>  	/* we can't keep up! */
> -	if (count > NOTIFY4_EVENT_QUEUE_SIZE) {
> +	if (count > limit) {
>  		spin_unlock(&ncn->ncn_lock);
>  		goto out_recall;
>  	}
> @@ -3567,6 +3572,22 @@ nfsd4_cb_notify_prepare(struct nfsd4_callback 
> *cb)
>  		nfsd_notify_event_put(nne);
>  	}
>  	if (!error) {
> +		if (dp->dl_notify_mask & BIT(NOTIFY4_CHANGE_DIR_ATTRS)) {
> +			u32 *maskp = (u32 *)xdr_reserve_space(&stream, sizeof(*maskp));
> +
> +			if (maskp) {
> +				u8 *p = nfsd4_encode_dir_attr_change(&stream, dp, nf);
> +
> +				if (p) {
> +					*maskp = BIT(NOTIFY4_CHANGE_DIR_ATTRS);
> +					ncn->ncn_nf[count].notify_mask.count = 1;
> +					ncn->ncn_nf[count].notify_mask.element = maskp;
> +					ncn->ncn_nf[count].notify_vals.data = p;
> +					ncn->ncn_nf[count].notify_vals.len = (u8 *)stream.p - p;
> +					++count;
> +				}
> +			}
> +		}

Nit:

When xdr_reserve_space() for maskp succeeds but nfsd4_encode_dir_attr_change()
returns NULL, the 4-byte reservation is never rolled back and *maskp is never
written, yet the function still takes the success path (return true). Unlike
the child-event loop, this branch does not escalate to error = true.

This is probably benign only because nfs4_xdr_enc_cb_notify re-encodes from
the ncn_nf[] array (and count was not incremented), so the garbage hole is
never transmitted.


>  		ncn->ncn_nf_cnt = count;
>  		nfsd_file_put(nf);
>  		return true;
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 1e3c360c06cd..7dd8476028d6 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -4199,7 +4199,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, 
> struct xdr_stream *xdr,
> 
>  static bool
>  setup_notify_fhandle(struct dentry *dentry, struct nfs4_file *fi,
> -		     struct nfsd_file *nf, struct nfsd4_fattr_args *args)
> +		     struct nfsd_file *nf, struct svc_export *exp,
> +		     struct nfsd4_fattr_args *args)
>  {
>  	int fileid_type, fsid_len, maxsize, flags = 0;
>  	struct knfsd_fh *fhp = &args->fhandle;

The function dereferences the new exp parameter unconditionally.

The argument is dp->dl_stid.sc_export, read unlocked at
nfs4xdr.c:4297 and handed down. An in-flight CB_NOTIFY callback holds a
sc_count reference but NOT an export reference. drop_stid_export() can
run concurrently (admin revoke / unexport), NULL sc_export, and drop what
may be the last export reference, freeing the svc_export while the
callback dereferences it.


> @@ -4227,6 +4228,17 @@ setup_notify_fhandle(struct dentry *dentry, 
> struct nfs4_file *fi,
> 
>  	fhp->fh_fileid_type = fileid_type;
>  	fhp->fh_size += maxsize * 4;
> +
> +	/*
> +	 * fh_compose() appends a MAC to filehandles on signed exports; this
> +	 * hand-rolled filehandle must do the same or the client will get back
> +	 * an unsigned filehandle that fh_verify() later rejects as stale.
> +	 * If we can't sign it, don't hand it out at all.
> +	 */
> +	if (exp && (exp->ex_flags & NFSEXP_SIGN_FH))
> +		if (!fh_append_mac(fhp, NFS4_FHSIZE, exp->cd->net))
> +			return false;
> +
>  	return true;
>  }
> 
> @@ -4240,11 +4252,11 @@ nfsd4_setup_notify_entry4(struct notify_entry4 
> *ne, struct xdr_stream *xdr,
>  			  struct nfsd_file *nf, char *name, u32 namelen)
>  {
>  	struct nfs4_file *fi = dp->dl_stid.sc_file;
> -	struct path path =  { .mnt = nf->nf_file->f_path.mnt,
> -			      .dentry = dentry };
> +	struct path path = nf->nf_file->f_path;
>  	struct nfsd4_fattr_args args = { };
>  	uint32_t *attrmask;
>  	__be32 status;
> +	bool parent;
>  	int ret;
> 
>  	/* Reserve space for attrmask */
> @@ -4256,6 +4268,9 @@ nfsd4_setup_notify_entry4(struct notify_entry4 
> *ne, struct xdr_stream *xdr,
>  	ne->ne_file.len = namelen;
>  	ne->ne_attrs.attrmask.element = attrmask;
> 
> +	parent = (dentry == path.dentry);
> +	path.dentry = dentry;
> +
>  	/* FIXME: d_find_alias for inode ? */
>  	if (!path.dentry || !d_inode(path.dentry))
>  		goto noattrs;
> @@ -4271,15 +4286,21 @@ nfsd4_setup_notify_entry4(struct notify_entry4 
> *ne, struct xdr_stream *xdr,
> 
>  	args.change_attr = nfsd4_change_attribute(&args.stat);
> 
> -	attrmask[0] = dp->dl_child_attrs[0];
> -	attrmask[1] = dp->dl_child_attrs[1];
> -	attrmask[2] = 0;
> +	if (parent) {
> +		attrmask[0] = dp->dl_dir_attrs[0];
> +		attrmask[1] = dp->dl_dir_attrs[1];
> +	} else {
> +		attrmask[0] = dp->dl_child_attrs[0];
> +		attrmask[1] = dp->dl_child_attrs[1];
> 
> -	if (!setup_notify_fhandle(dentry, fi, nf, &args))
> -		attrmask[0] &= ~FATTR4_WORD0_FILEHANDLE;
> +		if (!setup_notify_fhandle(dentry, fi, nf,
> +					  dp->dl_stid.sc_export, &args))
> +			attrmask[0] &= ~FATTR4_WORD0_FILEHANDLE;
> 
> -	if (!(args.stat.result_mask & STATX_BTIME))
> -		attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
> +		if (!(args.stat.result_mask & STATX_BTIME))
> +			attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
> +	}
> +	attrmask[2] = 0;
> 
>  	ne->ne_attrs.attrmask.count = 2;
>  	ne->ne_attrs.attr_vals.data = (u8 *)xdr->p;
> @@ -4392,6 +4413,38 @@ u8 *nfsd4_encode_notify_event(struct xdr_stream 
> *xdr, struct nfsd_notify_event *
>  	return NULL;
>  }
> 
> +/**
> + * nfsd4_encode_dir_attr_change
> + * @xdr: stream to which to encode the fattr4
> + * @dp: delegation where the event occurred
> + * @nf: nfsd_file opened on the directory
> + *
> + * Encode a dir attr change event.
> + */
> +u8 *nfsd4_encode_dir_attr_change(struct xdr_stream *xdr, struct 
> nfs4_delegation *dp,
> +				 struct nfsd_file *nf)
> +{
> +	struct dentry *dentry = nf->nf_file->f_path.dentry;
> +	struct notify_attr4 na = { };
> +	bool ret;
> +	u8 *p = NULL;
> +
> +	if (!(dp->dl_notify_mask & BIT(NOTIFY4_CHANGE_DIR_ATTRS)))
> +		return NULL;

It looks like this if() re-checks dl_notify_mask even though its
sole caller already gated on the identical check.

nfsd4_encode_notify_event() does not repeat its caller's check.
The guard is unreachable from current callers.


> +
> +	/* RFC 8881 s10.4.3: ne_file must be a zero-length string for dir 
> attrs */
> +	ret = nfsd4_setup_notify_entry4(&na.na_changed_entry, xdr,
> +					dentry, dp, nf, "", 0);
> +
> +	/* Don't bother with the event if we're not encoding attrs */
> +	if (ret && na.na_changed_entry.ne_attrs.attr_vals.len) {
> +		p = (u8 *)xdr->p;
> +		if (!xdrgen_encode_notify_attr4(xdr, &na))
> +			p = NULL;
> +	}
> +	return p;
> +}
> +
>  static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
>  				struct xdr_buf *buf, __be32 *p, int bytes)
>  {
> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index 62ac790428be..805c7122eb93 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -973,6 +973,8 @@ __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int 
> words,
>  u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct 
> nfsd_notify_event *nne,
>  			      struct nfs4_delegation *dd, struct nfsd_file *nf,
>  			      u32 *notify_mask);
> +u8 *nfsd4_encode_dir_attr_change(struct xdr_stream *xdr, struct 
> nfs4_delegation *dp,
> +				 struct nfsd_file *nf);
>  extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
>  		struct nfsd4_compound_state *, union nfsd4_op_u *u);
>  extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
>
> -- 
> 2.54.0

-- 
Chuck Lever

      reply	other threads:[~2026-06-12 18:22 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-11 17:50 [PATCH v6 00/20] nfsd: add support for CB_NOTIFY callbacks in directory delegations Jeff Layton
2026-06-11 17:50 ` [PATCH v6 01/20] nfsd: check fl_lmops in nfsd_breaker_owns_lease() Jeff Layton
2026-06-11 17:50 ` [PATCH v6 02/20] nfsd: add protocol support for CB_NOTIFY Jeff Layton
2026-06-11 21:33   ` Chuck Lever
2026-06-11 17:50 ` [PATCH v6 03/20] nfs_common: add new NOTIFY4_* flags proposed in RFC8881bis Jeff Layton
2026-06-11 17:50 ` [PATCH v6 04/20] nfsd: allow nfsd to get a dir lease with an ignore mask Jeff Layton
2026-06-11 17:50 ` [PATCH v6 05/20] nfsd: update the fsnotify mark when setting or removing a dir delegation Jeff Layton
2026-06-11 17:50 ` [PATCH v6 06/20] nfsd: make nfsd4_callback_ops->prepare operation bool return Jeff Layton
2026-06-11 17:50 ` [PATCH v6 07/20] nfsd: add callback encoding and decoding linkages for CB_NOTIFY Jeff Layton
2026-06-11 17:50 ` [PATCH v6 08/20] nfsd: use RCU to protect fi_deleg_file Jeff Layton
2026-06-11 17:50 ` [PATCH v6 09/20] nfsd: add data structures for handling CB_NOTIFY Jeff Layton
2026-06-11 17:50 ` [PATCH v6 10/20] nfsd: add notification handlers for dir events Jeff Layton
2026-06-12 17:51   ` Chuck Lever
2026-06-12 18:36     ` Jeff Layton
2026-06-11 17:50 ` [PATCH v6 11/20] nfsd: apply the notify mask to the delegation when requested Jeff Layton
2026-06-12 17:57   ` Chuck Lever
2026-06-11 17:50 ` [PATCH v6 12/20] nfsd: add helper to marshal a fattr4 from completed args Jeff Layton
2026-06-11 17:50 ` [PATCH v6 13/20] nfsd: allow nfsd4_encode_fattr4_change() to work with no export Jeff Layton
2026-06-11 17:50 ` [PATCH v6 14/20] nfsd: send basic file attributes in CB_NOTIFY Jeff Layton
2026-06-11 17:50 ` [PATCH v6 15/20] nfsd: allow encoding a filehandle into fattr4 without a svc_fh Jeff Layton
2026-06-12 18:03   ` Chuck Lever
2026-06-11 17:50 ` [PATCH v6 16/20] nfsd: add a fi_connectable flag to struct nfs4_file Jeff Layton
2026-06-12 18:06   ` Chuck Lever
2026-06-11 17:50 ` [PATCH v6 17/20] nfsd: add the filehandle to returned attributes in CB_NOTIFY Jeff Layton
2026-06-12 18:08   ` Chuck Lever
2026-06-11 17:50 ` [PATCH v6 18/20] nfsd: properly track requested child attributes Jeff Layton
2026-06-12 18:10   ` Chuck Lever
2026-06-11 17:50 ` [PATCH v6 19/20] nfsd: track requested dir attributes Jeff Layton
2026-06-12 18:13   ` Chuck Lever
2026-06-11 17:50 ` [PATCH v6 20/20] nfsd: add support to CB_NOTIFY for dir attribute changes Jeff Layton
2026-06-12 18:21   ` Chuck Lever [this message]

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=0a0bfce8-c35e-4c96-8f7b-65267fd96a61@app.fastmail.com \
    --to=cel@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=corbet@lwn.net \
    --cc=jack@suse.cz \
    --cc=jlayton@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-nfs@vger.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 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®