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]>