Commit 70b87f77 authored by J. Bruce Fields's avatar J. Bruce Fields Committed by Chuck Lever
Browse files

nfsd: only call inode_query_iversion in the I_VERSION case



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: default avatarDaire Byrne <daire@dneg.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 4a9d81ca
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -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));
}

/*
+1 −5
Original line number Diff line number Diff line
@@ -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;
}

+10 −4
Original line number Diff line number Diff line
@@ -261,10 +261,16 @@ static inline u64 nfsd4_change_attribute(struct kstat *stat,
{
	u64 chattr;

	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;
}