]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
nfsd: only call inode_query_iversion in the I_VERSION case
authorJ. Bruce Fields <bfields@redhat.com>
Mon, 30 Nov 2020 22:46:14 +0000 (17:46 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Jun 2024 12:52:45 +0000 (14:52 +0200)
[ Upstream commit 70b87f77294d16d3e567056ba4c9ee2b091a5b50 ]

inode_query_iversion() can modify i_version.  Depending on the exported
filesystem, that may not be safe.  For example, if you're re-exporting
NFS, NFS stores the server's change attribute in i_version and does not
expect it to be modified locally.  This has been observed causing
unnecessary cache invalidations.

The way a filesystem indicates that it's OK to call
inode_query_iverson() is by setting SB_I_VERSION.

So, move the I_VERSION check out of encode_change(), where it's used
only in GETATTR responses, to nfsd4_change_attribute(), which is
also called for pre- and post- operation attributes.

(Note we could also pull the NFSEXP_V4ROOT case into
nfsd4_change_attribute() as well.  That would actually be a no-op,
since pre/post attrs are only used for metadata-modifying operations,
and V4ROOT exports are read-only.  But we might make the change in
the future just for simplicity.)

Reported-by: Daire Byrne <daire@dneg.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/nfsd/nfs3xdr.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsfh.h

index 0d75d201db1b3e83a8fb03730f71d37f48a55353..5956b0317c55e7ba05bfcc49b2b1fff103b17032 100644 (file)
@@ -291,14 +291,13 @@ void fill_post_wcc(struct svc_fh *fhp)
                printk("nfsd: inode locked twice during operation.\n");
 
        err = fh_getattr(fhp, &fhp->fh_post_attr);
-       fhp->fh_post_change = nfsd4_change_attribute(&fhp->fh_post_attr,
-                                                    d_inode(fhp->fh_dentry));
        if (err) {
                fhp->fh_post_saved = false;
-               /* Grab the ctime anyway - set_change_info might use it */
                fhp->fh_post_attr.ctime = d_inode(fhp->fh_dentry)->i_ctime;
        } else
                fhp->fh_post_saved = true;
+       fhp->fh_post_change = nfsd4_change_attribute(&fhp->fh_post_attr,
+                                                    d_inode(fhp->fh_dentry));
 }
 
 /*
index 315be1c1ab85c68c63527e43aee02220e2b4d307..bdcfb5f7021da5c0bd3698fcf9a74593e56c9542 100644 (file)
@@ -2426,12 +2426,8 @@ static __be32 *encode_change(__be32 *p, struct kstat *stat, struct inode *inode,
        if (exp->ex_flags & NFSEXP_V4ROOT) {
                *p++ = cpu_to_be32(convert_to_wallclock(exp->cd->flush_time));
                *p++ = 0;
-       } else if (IS_I_VERSION(inode)) {
+       } else
                p = xdr_encode_hyper(p, nfsd4_change_attribute(stat, inode));
-       } else {
-               *p++ = cpu_to_be32(stat->ctime.tv_sec);
-               *p++ = cpu_to_be32(stat->ctime.tv_nsec);
-       }
        return p;
 }
 
index 56cfbc36156184657590ec14f58dd050afa431d6..39d764b129fa3c6a3cef2e15362e23a07ac8383e 100644 (file)
@@ -261,10 +261,16 @@ static inline u64 nfsd4_change_attribute(struct kstat *stat,
 {
        u64 chattr;
 
-       chattr =  stat->ctime.tv_sec;
-       chattr <<= 30;
-       chattr += stat->ctime.tv_nsec;
-       chattr += inode_query_iversion(inode);
+       if (IS_I_VERSION(inode)) {
+               chattr =  stat->ctime.tv_sec;
+               chattr <<= 30;
+               chattr += stat->ctime.tv_nsec;
+               chattr += inode_query_iversion(inode);
+       } else {
+               chattr = stat->ctime.tv_sec;
+               chattr <<= 32;
+               chattr += stat->ctime.tv_nsec;
+       }
        return chattr;
 }