The i_version counter for regular files is updated in update_time, and that's usually done before copying the data to the pagecache. It's possible that a reader and writer could race like this: reader writer ------ ------ i_version++ read getattr update page cache If that happens then the reader may associate the i_version value with the wrong inode state. All of the existing filesystems that implement i_version take the i_rwsem in their write_iter operations before incrementing it. Take the inode_lock when issuing a getattr for NFSv4 attributes to prevent the above race. Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> --- fs/nfsd/nfs4xdr.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 4eec2ce05e7e..f7951d8d55ca 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -2872,9 +2872,22 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, goto out; } + /* + * The inode lock is needed here to ensure that there is not a + * write to the inode in progress that might change the size, + * or an in-progress directory morphing operation for directory + * inodes. + * + * READ and GETATTR are not guaranteed to be atomic, even when in + * the same compound, but we do try to present attributes in the + * GETATTR reply as representing a single point in time. + */ + inode_lock(d_inode(dentry)); err = vfs_getattr(&path, &stat, STATX_BASIC_STATS | STATX_BTIME | STATX_INO_VERSION, AT_STATX_SYNC_AS_STAT); + inode_unlock(d_inode(dentry)); + if (err) goto out_nfserr; if (!(stat.result_mask & STATX_BTIME)) -- 2.37.3