Message ID | 20240315-dir-deleg-v1-14-a1d6209a3654@kernel.org (mailing list archive) |
---|---|
State | RFC |
Headers | show |
Series | vfs, nfsd, nfs: implement directory delegations | expand |
Context | Check | Description |
---|---|---|
netdev/tree_selection | success | Not a local patch |
On Fri, Mar 15, 2024 at 12:53:05PM -0400, Jeff Layton wrote: > Add a new routine for acquiring a read delegation on a directory. Since > the same CB_RECALL/DELEGRETURN infrastrure is used for regular and > directory delegations, we can just use a normal nfs4_delegation to > represent it. > > Signed-off-by: Jeff Layton <jlayton@kernel.org> > --- > fs/nfsd/nfs4proc.c | 23 ++++++++++++++++-- > fs/nfsd/nfs4state.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++- > fs/nfsd/state.h | 5 ++++ > 3 files changed, 94 insertions(+), 3 deletions(-) > > diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c > index 7973fe17bf3c..1a2c90f4ea53 100644 > --- a/fs/nfsd/nfs4proc.c > +++ b/fs/nfsd/nfs4proc.c > @@ -2179,9 +2179,28 @@ 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; > + > + dd = nfsd_get_dir_deleg(cstate, gdd, nf); > + if (IS_ERR(dd)) { > + int err = PTR_ERR(dd); > + > + if (err != -EAGAIN) > + return nfserrno(err); > + gdd->nf_status = GDD4_UNAVAIL; > + return nfs_ok; > + } > > - /* FIXME: actually return a delegation */ > - gdd->nf_status = GDD4_UNAVAIL; > + gdd->nf_status = GDD4_OK; > + memcpy(&gdd->stateid, &dd->dl_stid.sc_stateid, sizeof(gdd->stateid)); > + memset(&gdd->cookieverf, '\0', sizeof(gdd->cookieverf)); > + nfs4_put_stid(&dd->dl_stid); > return nfs_ok; > } > > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c > index 778c1c6e3b54..36574aedc211 100644 > --- a/fs/nfsd/nfs4state.c > +++ b/fs/nfsd/nfs4state.c > @@ -8874,7 +8874,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode, > } > break_lease: > nfsd_stats_wdeleg_getattr_inc(nn); > - dp = fl->fl_owner; > + dp = fl->c.flc_owner; > ncf = &dp->dl_cb_fattr; > nfs4_cb_getattr(&dp->dl_cb_fattr); > spin_unlock(&ctx->flc_lock); > @@ -8912,3 +8912,70 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode, > spin_unlock(&ctx->flc_lock); > return 0; > } > + I'll admit to not having examined the below function closely, but since it is not a static function, can you add a kdoc comment and a few salient points like, if there is something NFSD doesn't implement (yet), or short cuts that would be good for readers to know about? > +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; > + int status = 0; > + > + fp = nfsd4_alloc_file(); > + if (!fp) > + return ERR_PTR(-ENOMEM); > + > + nfsd4_file_init(&cstate->current_fh, fp); > + fp->fi_deleg_file = nf; > + fp->fi_delegees = 1; > + > + spin_lock(&state_lock); > + spin_lock(&fp->fi_lock); > + if (nfs4_delegation_exists(clp, fp)) > + status = -EAGAIN; > + spin_unlock(&fp->fi_lock); > + spin_unlock(&state_lock); > + > + if (status) > + goto out_delegees; > + > + status = -ENOMEM; > + dp = alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ); > + if (!dp) > + goto out_delegees; > + > + fl = nfs4_alloc_init_lease(dp, NFS4_OPEN_DELEGATE_READ); > + 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; > + > + 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) > + goto out_unlock; > + > + return dp; > +out_unlock: > + 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 01c6f3445646..20551483cc7b 100644 > --- a/fs/nfsd/state.h > +++ b/fs/nfsd/state.h > @@ -782,4 +782,9 @@ static inline bool try_to_expire_client(struct nfs4_client *clp) > > extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, > struct inode *inode, bool *file_modified, u64 *size); > + > +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 */ > > -- > 2.44.0 >
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 7973fe17bf3c..1a2c90f4ea53 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -2179,9 +2179,28 @@ 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; + + dd = nfsd_get_dir_deleg(cstate, gdd, nf); + if (IS_ERR(dd)) { + int err = PTR_ERR(dd); + + if (err != -EAGAIN) + return nfserrno(err); + gdd->nf_status = GDD4_UNAVAIL; + return nfs_ok; + } - /* FIXME: actually return a delegation */ - gdd->nf_status = GDD4_UNAVAIL; + gdd->nf_status = GDD4_OK; + memcpy(&gdd->stateid, &dd->dl_stid.sc_stateid, sizeof(gdd->stateid)); + memset(&gdd->cookieverf, '\0', sizeof(gdd->cookieverf)); + nfs4_put_stid(&dd->dl_stid); return nfs_ok; } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 778c1c6e3b54..36574aedc211 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -8874,7 +8874,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode, } break_lease: nfsd_stats_wdeleg_getattr_inc(nn); - dp = fl->fl_owner; + dp = fl->c.flc_owner; ncf = &dp->dl_cb_fattr; nfs4_cb_getattr(&dp->dl_cb_fattr); spin_unlock(&ctx->flc_lock); @@ -8912,3 +8912,70 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode, spin_unlock(&ctx->flc_lock); return 0; } + +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; + int status = 0; + + fp = nfsd4_alloc_file(); + if (!fp) + return ERR_PTR(-ENOMEM); + + nfsd4_file_init(&cstate->current_fh, fp); + fp->fi_deleg_file = nf; + fp->fi_delegees = 1; + + spin_lock(&state_lock); + spin_lock(&fp->fi_lock); + if (nfs4_delegation_exists(clp, fp)) + status = -EAGAIN; + spin_unlock(&fp->fi_lock); + spin_unlock(&state_lock); + + if (status) + goto out_delegees; + + status = -ENOMEM; + dp = alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ); + if (!dp) + goto out_delegees; + + fl = nfs4_alloc_init_lease(dp, NFS4_OPEN_DELEGATE_READ); + 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; + + 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) + goto out_unlock; + + return dp; +out_unlock: + 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 01c6f3445646..20551483cc7b 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -782,4 +782,9 @@ static inline bool try_to_expire_client(struct nfs4_client *clp) extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode, bool *file_modified, u64 *size); + +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 */
Add a new routine for acquiring a read delegation on a directory. Since the same CB_RECALL/DELEGRETURN infrastrure is used for regular and directory delegations, we can just use a normal nfs4_delegation to represent it. Signed-off-by: Jeff Layton <jlayton@kernel.org> --- fs/nfsd/nfs4proc.c | 23 ++++++++++++++++-- fs/nfsd/nfs4state.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++- fs/nfsd/state.h | 5 ++++ 3 files changed, 94 insertions(+), 3 deletions(-)