Re: [PATCH v3 12/13] nfsd: wire up GET_DIR_DELEGATION handling
Chuck Lever <[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 10/21/25 11:25 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 infrastructure is used for regular > and directory delegations, a normal nfs4_delegation is used to represent > a directory delegation. > > Reviewed-by: NeilBrown <[email protected]> > Signed-off-by: Jeff Layton <[email protected]> > --- > fs/nfsd/nfs4proc.c | 22 +++++++++++- > fs/nfsd/nfs4state.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > fs/nfsd/state.h | 5 +++ > 3 files changed, 126 insertions(+), 1 deletion(-) > > diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c > index 2222bb283baff35703b4035fa0fc593b54d8b937..4f0b1210702ecf4eaa20c74e548aabbee33b7fd1 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,20 @@ 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); > + nfsd_file_put(nf); > + if (IS_ERR(dd)) { > + int err = PTR_ERR(dd); > + > + if (err != -EAGAIN) > + return nfserrno(err); > + gdd->gddrnf_status = GDD4_UNAVAIL; > + return nfs_ok; > + } > + > + 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 8efa37055b21ca2202488e90377d5162613b9343..808c24fb5c9a0b432d3271c051b409fcb75970cd 100644 > --- a/fs/nfsd/nfs4state.c > +++ b/fs/nfsd/nfs4state.c > @@ -9367,3 +9367,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 = nfsd_file_get(nf); > + fp->fi_delegees = 1; > + } else { > + ++fp->fi_delegees; > + } > + 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 */ > Reviewed-by: Chuck Lever <[email protected]> -- Chuck Lever