Re: [PATCH v2 11/11] nfsd: wire up GET_DIR_DELEGATION handling

Jeff Layton <[email protected]>
Newsgroups org.kernel.vger.ecryptfs,dev.linux.lists.netfs,org.kernel.vger.linux-cifs,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-nfs,org.kernel.vger.linux-unionfs,org.kernel.vger.linux-xfs,org.kernel.vger.netdev
Message-ID <[email protected]>
On Sat, 2025-10-18 at 15:46 -0400, Chuck Lever wrote:
> On 10/17/25 7:32 AM, Jeff Layton wrote:
> > Add a new routine for acquiring a read delegation on a directory. These
> > are recallable-only delegations with no support for CB_NOTIFY. That will
> > be added in a later phase.
> > 
> > Since the same CB_RECALL/DELEGRETURN infrastrure is used for regular and
> > directory delegations, a normal nfs4_delegation is used to represent a
> > directory delegation.
> 
> s/infrastrure/infrastructure/
> 
> 
> > Signed-off-by: Jeff Layton <[email protected]>
> > ---
> >  fs/nfsd/nfs4proc.c  |  21 ++++++++++-
> >  fs/nfsd/nfs4state.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  fs/nfsd/state.h     |   5 +++
> >  3 files changed, 125 insertions(+), 1 deletion(-)
> > 
> > diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> > index 7f7e6bb23a90d9a1cafd154c0f09e236df75b083..527f8dc52159803770964700170473509ec328ed 100644
> > --- a/fs/nfsd/nfs4proc.c
> > +++ b/fs/nfsd/nfs4proc.c
> > @@ -2342,6 +2342,13 @@ nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
> >  			 union nfsd4_op_u *u)
> >  {
> >  	struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
> > +	struct nfs4_delegation *dd;
> > +	struct nfsd_file *nf;
> > +	__be32 status;
> > +
> > +	status = nfsd_file_acquire_dir(rqstp, &cstate->current_fh, &nf);
> > +	if (status != nfs_ok)
> > +		return status;
> >  
> >  	/*
> >  	 * RFC 8881, section 18.39.3 says:
> > @@ -2355,7 +2362,19 @@ nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
> >  	 * return NFS4_OK with a non-fatal status of GDD4_UNAVAIL in this
> >  	 * situation.
> >  	 */
> > -	gdd->gddrnf_status = GDD4_UNAVAIL;
> > +	dd = nfsd_get_dir_deleg(cstate, gdd, nf);
> > +	if (IS_ERR(dd)) {
> > +		int err = PTR_ERR(dd);
> > +
> > +		if (err != -EAGAIN)
> > +			return nfserrno(err);
> > +		gdd->gddrnf_status = GDD4_UNAVAIL;
> > +		return nfs_ok;
> > +	}
> 
> These error flows might leak the nf acquired just above.
> 

Good catch. I'll fix that.

> 
> > +
> > +	gdd->gddrnf_status = GDD4_OK;
> > +	memcpy(&gdd->gddr_stateid, &dd->dl_stid.sc_stateid, sizeof(gdd->gddr_stateid));
> > +	nfs4_put_stid(&dd->dl_stid);
> >  	return nfs_ok;
> >  }
> >  
> > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> > index b06591f154aa372db710e071c69260f4639956d7..a63e8c885291fc377163f3255f26f5f693704437 100644
> > --- a/fs/nfsd/nfs4state.c
> > +++ b/fs/nfsd/nfs4state.c
> > @@ -9359,3 +9359,103 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
> >  	nfs4_put_stid(&dp->dl_stid);
> >  	return status;
> >  }
> > +
> > +/**
> > + * nfsd_get_dir_deleg - attempt to get a directory delegation
> > + * @cstate: compound state
> > + * @gdd: GET_DIR_DELEGATION arg/resp structure
> > + * @nf: nfsd_file opened on the directory
> > + *
> > + * Given a GET_DIR_DELEGATION request @gdd, attempt to acquire a delegation
> > + * on the directory to which @nf refers. Note that this does not set up any
> > + * sort of async notifications for the delegation.
> > + */
> > +struct nfs4_delegation *
> > +nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
> > +		   struct nfsd4_get_dir_delegation *gdd,
> > +		   struct nfsd_file *nf)
> > +{
> > +	struct nfs4_client *clp = cstate->clp;
> > +	struct nfs4_delegation *dp;
> > +	struct file_lease *fl;
> > +	struct nfs4_file *fp, *rfp;
> > +	int status = 0;
> > +
> > +	fp = nfsd4_alloc_file();
> > +	if (!fp)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	nfsd4_file_init(&cstate->current_fh, fp);
> > +
> > +	rfp = nfsd4_file_hash_insert(fp, &cstate->current_fh);
> > +	if (unlikely(!rfp)) {
> > +		put_nfs4_file(fp);
> > +		return ERR_PTR(-ENOMEM);
> > +	}
> > +
> > +	if (rfp != fp) {
> > +		put_nfs4_file(fp);
> > +		fp = rfp;
> > +	}
> > +
> > +	/* if this client already has one, return that it's unavailable */
> > +	spin_lock(&state_lock);
> > +	spin_lock(&fp->fi_lock);
> > +	/* existing delegation? */
> > +	if (nfs4_delegation_exists(clp, fp)) {
> > +		status = -EAGAIN;
> > +	} else if (!fp->fi_deleg_file) {
> > +		fp->fi_deleg_file = nf;
> > +		fp->fi_delegees = 1;
> > +	} else {
> > +		++fp->fi_delegees;
> 
> The new nf is unused in this arm. Does it need to be released?
> 

Yes. Good catch!

> 
> > +	}
> > +	spin_unlock(&fp->fi_lock);
> > +	spin_unlock(&state_lock);
> > +
> > +	if (status) {
> > +		put_nfs4_file(fp);
> > +		return ERR_PTR(status);
> > +	}
> > +
> > +	/* Try to set up the lease */
> > +	status = -ENOMEM;
> > +	dp = alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ);
> > +	if (!dp)
> > +		goto out_delegees;
> > +
> > +	fl = nfs4_alloc_init_lease(dp);
> > +	if (!fl)
> > +		goto out_put_stid;
> > +
> > +	status = kernel_setlease(nf->nf_file,
> > +				 fl->c.flc_type, &fl, NULL);
> > +	if (fl)
> > +		locks_free_lease(fl);
> > +	if (status)
> > +		goto out_put_stid;
> > +
> > +	/*
> > +	 * Now, try to hash it. This can fail if we race another nfsd task
> > +	 * trying to set a delegation on the same file. If that happens,
> > +	 * then just say UNAVAIL.
> > +	 */
> > +	spin_lock(&state_lock);
> > +	spin_lock(&clp->cl_lock);
> > +	spin_lock(&fp->fi_lock);
> > +	status = hash_delegation_locked(dp, fp);
> > +	spin_unlock(&fp->fi_lock);
> > +	spin_unlock(&clp->cl_lock);
> > +	spin_unlock(&state_lock);
> > +
> > +	if (!status)
> > +		return dp;
> > +
> > +	/* Something failed. Drop the lease and clean up the stid */
> > +	kernel_setlease(fp->fi_deleg_file->nf_file, F_UNLCK, NULL, (void **)&dp);
> > +out_put_stid:
> > +	nfs4_put_stid(&dp->dl_stid);
> > +out_delegees:
> > +	put_deleg_file(fp);
> > +	return ERR_PTR(status);
> > +}
> > diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
> > index 1e736f4024263ffa9c93bcc9ec48f44566a8cc77..b052c1effdc5356487c610db9728df8ecfe851d4 100644
> > --- a/fs/nfsd/state.h
> > +++ b/fs/nfsd/state.h
> > @@ -867,4 +867,9 @@ static inline bool try_to_expire_client(struct nfs4_client *clp)
> >  
> >  extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp,
> >  		struct dentry *dentry, struct nfs4_delegation **pdp);
> > +
> > +struct nfsd4_get_dir_delegation;
> > +struct nfs4_delegation *nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
> > +						struct nfsd4_get_dir_delegation *gdd,
> > +						struct nfsd_file *nf);
> >  #endif   /* NFSD4_STATE_H */
> > 
> 

-- 
Jeff Layton <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.