From: "J. Bruce Fields" <[email protected]>

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
unnecssary cache invalidations.

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

(This may look like a no-op--in the encode_change() case it's just
rearranging some code--but note nfsd4_change_attribute() is also called
from fill_pre_wcc() and fill_post_wcc().)

(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 <[email protected]>
Signed-off-by: J. Bruce Fields <[email protected]>
---
 fs/nfsd/nfs4xdr.c |  6 +-----
 fs/nfsd/nfsfh.h   | 14 ++++++++++----
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 833a2c64dfe8..56fd5f6d5c44 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -2298,12 +2298,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;
 }
 
diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h
index 56cfbc361561..3faf5974fa4e 100644
--- a/fs/nfsd/nfsfh.h
+++ b/fs/nfsd/nfsfh.h
@@ -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 = cpu_to_be32(stat->ctime.tv_sec);
+               chattr <<= 32;
+               chattr += cpu_to_be32(stat->ctime.tv_nsec);
+       }
        return chattr;
 }
 
-- 
2.28.0

--
Linux-cachefs mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/linux-cachefs

Reply via email to