[PATCH v5 04/18] nfsd: replace fh_fill_both_attrs() with fh_fill_post_noop()
NeilBrown <[email protected]> Fri, 17 Jul 2026 19:27:52 +1000
| Newsgroups | gmane.linux.nfs |
|---|---|
| Message-ID | <[email protected]> |
From: NeilBrown <neil-+NVA1uvv1dVBDLzU/[email protected]> fh_fill_both_attrs() is only needed for open/create and is used in the case when the target already existed so no creating happens. As part of refactoring this code it is changed to call fh_fill_pre_attrs() once early on (so errors only need to be caught in one place) and then to use a new fh_fill_post_noop() when it is determined that no creation happened. fh_fill_pre_attrs() now stores the attrs (which it had to get all of anyway)_ in ->fh_post_attr. fh_fill_post_noop() simply marks them as valid. fh_fill_post_attrs() replaces them. This change involves moving fh_fill_pre_attrs() out of the inode_lock on the directory. This means that we cannot provide "atomic" wcc data so a new fh_fill_pre_attrs_unlocked() is provided which marks the attrs as non-atomic. This is unfortunate but inevitable if we are ever to allow concurrent updates in a directory (which can significantly improve performance in some cases). To get atomic pre/post attributes we will need to be able to ask the fs to provide them, or to request a lease on the directory for the duration of an operation. Note that we haven't provided pre/post attrs on WRITE requests for a long time for exactly this reason - we cannot lock the file to get them. Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: NeilBrown <neil-+NVA1uvv1dVBDLzU/[email protected]> --- fs/nfsd/nfs4proc.c | 23 +++++++--------- fs/nfsd/nfsfh.c | 69 +++++++++++++++++++++++----------------------- fs/nfsd/nfsfh.h | 14 +++++++++- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index a13dc1756b1b..6e02976484d9 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -285,8 +285,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (status == nfs_ok) status = fh_compose(resfhp, exp, child, fhp); - if (status == nfs_ok) - status = fh_fill_both_attrs(fhp); + fh_fill_post_noop(fhp); open->op_truncate = (iap->ia_valid & ATTR_SIZE) && !iap->ia_size; @@ -355,9 +354,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, /* NFSv4 protocol requires change attributes even though * no change happened. */ - status = fh_fill_both_attrs(fhp); - if (status != nfs_ok) - goto out; + fh_fill_post_noop(fhp); status = fh_compose(resfhp, fhp->fh_export, child, fhp); if (status != nfs_ok) @@ -404,9 +401,6 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (!IS_POSIXACL(inode)) iap->ia_mode &= ~current_umask(); - status = fh_fill_pre_attrs(fhp); - if (status != nfs_ok) - goto out; status = nfsd4_vfs_create(fhp, &child, open); if (status != nfs_ok) goto out; @@ -492,6 +486,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru fh_init(*resfh, NFS4_FHSIZE); open->op_truncate = false; + status = fh_fill_pre_attrs_unlocked(current_fh); + if (status) + goto out; if (open->op_create) { /* FIXME: check session persistence and pnfs flags. * The nfsv4.1 spec requires the following semantics: @@ -523,11 +520,11 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru } else { status = nfsd_lookup(rqstp, current_fh, open->op_fname, open->op_fnamelen, *resfh); - if (status == nfs_ok) - /* NFSv4 protocol requires change attributes even though - * no change happened. - */ - status = fh_fill_both_attrs(current_fh); + /* + * NFSv4 protocol requires change attributes even though + * no change happened. + */ + fh_fill_post_noop(current_fh); } if (status) goto out; diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index c7c60c35bdfc..c0a46784d525 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -782,34 +782,53 @@ __be32 fh_getattr(const struct svc_fh *fhp, struct kstat *stat) AT_STATX_SYNC_AS_STAT)); } -/** - * fh_fill_pre_attrs - Fill in pre-op attributes - * @fhp: file handle to be updated - * - */ -__be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp) +static __be32 __must_check __fh_fill_pre_attrs(struct svc_fh *fhp) { bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE); - struct kstat stat; __be32 err; if (fhp->fh_no_wcc || fhp->fh_pre_saved) return nfs_ok; - err = fh_getattr(fhp, &stat); + err = fh_getattr(fhp, &fhp->fh_post_attr); if (err) return err; if (v4) - fhp->fh_pre_change = nfsd4_change_attribute(&stat); + fhp->fh_pre_change = fhp->fh_post_change = + nfsd4_change_attribute(&fhp->fh_post_attr); - fhp->fh_pre_mtime = stat.mtime; - fhp->fh_pre_ctime = stat.ctime; - fhp->fh_pre_size = stat.size; + fhp->fh_pre_mtime = fhp->fh_post_attr.mtime; + fhp->fh_pre_ctime = fhp->fh_post_attr.ctime; + fhp->fh_pre_size = fhp->fh_post_attr.size; fhp->fh_pre_saved = true; return nfs_ok; } +/** + * fh_fill_pre_attrs - Fill in pre-op attributes + * @fhp: file handle to be updated + * + * Post-op attrs are filled and pre-op attrs are copied + * from there. The post-op attrs can later be replaced by + * fh_fill_post_attrs() or activated by fh_fill_post_noop(). + * + * The inode must be locked. + * + * Returns: error from vfs_getattr() which must be checked. + */ +__be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp) +{ + lockdep_assert_held_write(&fhp->fh_dentry->d_inode->i_rwsem); + return __fh_fill_pre_attrs(fhp); +} + +__be32 __must_check fh_fill_pre_attrs_unlocked(struct svc_fh *fhp) +{ + fhp->fh_no_atomic_attr = true; + return __fh_fill_pre_attrs(fhp); +} + /** * fh_fill_post_attrs - Fill in post-op attributes * @fhp: file handle to be updated @@ -826,6 +845,9 @@ __be32 fh_fill_post_attrs(struct svc_fh *fhp) if (fhp->fh_post_saved) printk("nfsd: inode locked twice during operation.\n"); + if (!fhp->fh_no_atomic_attr) + lockdep_assert_held_write(&fhp->fh_dentry->d_inode->i_rwsem); + err = fh_getattr(fhp, &fhp->fh_post_attr); if (err) return err; @@ -837,29 +859,6 @@ __be32 fh_fill_post_attrs(struct svc_fh *fhp) return nfs_ok; } -/** - * fh_fill_both_attrs - Fill pre-op and post-op attributes - * @fhp: file handle to be updated - * - * This is used when the directory wasn't changed, but wcc attributes - * are needed anyway. - */ -__be32 __must_check fh_fill_both_attrs(struct svc_fh *fhp) -{ - __be32 err; - - err = fh_fill_post_attrs(fhp); - if (err) - return err; - - fhp->fh_pre_change = fhp->fh_post_change; - fhp->fh_pre_mtime = fhp->fh_post_attr.mtime; - fhp->fh_pre_ctime = fhp->fh_post_attr.ctime; - fhp->fh_pre_size = fhp->fh_post_attr.size; - fhp->fh_pre_saved = true; - return nfs_ok; -} - /* * Release a file handle. */ diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h index cdeb5eea65a8..ab15b59ac7b3 100644 --- a/fs/nfsd/nfsfh.h +++ b/fs/nfsd/nfsfh.h @@ -337,6 +337,18 @@ static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp) u64 nfsd4_change_attribute(const struct kstat *stat); __be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp); +__be32 __must_check fh_fill_pre_attrs_unlocked(struct svc_fh *fhp); __be32 fh_fill_post_attrs(struct svc_fh *fhp); -__be32 __must_check fh_fill_both_attrs(struct svc_fh *fhp); + +/** + * fh_fill_post_noop - Copy pre attrs to post attrs + * @fhp: file handle to be updated + * + * This is used when the directory wasn't changed, but wcc attributes + * are needed anyway. + */ +static inline void fh_fill_post_noop(struct svc_fh *fhp) +{ + fhp->fh_post_saved = true; +} #endif /* _LINUX_NFSD_NFSFH_H */ -- 2.50.0.107.gf914562f5916.dirty