Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nfsd4: factor ctime into change attribute

Factoring ctime into the nfsv4 change attribute gives us better
properties than just i_version alone.

Eventually we'll likely also expose this (as opposed to raw i_version)
to userspace, at which point we'll want to move it to a common helper,
called from either userspace or individual filesystems. For now, nfsd
is the only user.

Signed-off-by: J. Bruce Fields <bfields@redhat.com>

+25 -3
+1 -1
fs/nfsd/nfs3xdr.c
··· 260 260 printk("nfsd: inode locked twice during operation.\n"); 261 261 262 262 err = fh_getattr(fhp, &fhp->fh_post_attr); 263 - fhp->fh_post_change = d_inode(fhp->fh_dentry)->i_version; 263 + fhp->fh_post_change = nfsd4_change_attribute(d_inode(fhp->fh_dentry)); 264 264 if (err) { 265 265 fhp->fh_post_saved = false; 266 266 /* Grab the ctime anyway - set_change_info might use it */
+1 -1
fs/nfsd/nfs4xdr.c
··· 1973 1973 *p++ = cpu_to_be32(convert_to_wallclock(exp->cd->flush_time)); 1974 1974 *p++ = 0; 1975 1975 } else if (IS_I_VERSION(inode)) { 1976 - p = xdr_encode_hyper(p, inode->i_version); 1976 + p = xdr_encode_hyper(p, nfsd4_change_attribute(inode)); 1977 1977 } else { 1978 1978 *p++ = cpu_to_be32(stat->ctime.tv_sec); 1979 1979 *p++ = cpu_to_be32(stat->ctime.tv_nsec);
+23 -1
fs/nfsd/nfsfh.h
··· 241 241 } 242 242 243 243 /* 244 + * We could use i_version alone as the change attribute. However, 245 + * i_version can go backwards after a reboot. On its own that doesn't 246 + * necessarily cause a problem, but if i_version goes backwards and then 247 + * is incremented again it could reuse a value that was previously used 248 + * before boot, and a client who queried the two values might 249 + * incorrectly assume nothing changed. 250 + * 251 + * By using both ctime and the i_version counter we guarantee that as 252 + * long as time doesn't go backwards we never reuse an old value. 253 + */ 254 + static inline u64 nfsd4_change_attribute(struct inode *inode) 255 + { 256 + u64 chattr; 257 + 258 + chattr = inode->i_ctime.tv_sec; 259 + chattr <<= 30; 260 + chattr += inode->i_ctime.tv_nsec; 261 + chattr += inode->i_version; 262 + return chattr; 263 + } 264 + 265 + /* 244 266 * Fill in the pre_op attr for the wcc data 245 267 */ 246 268 static inline void ··· 275 253 fhp->fh_pre_mtime = inode->i_mtime; 276 254 fhp->fh_pre_ctime = inode->i_ctime; 277 255 fhp->fh_pre_size = inode->i_size; 278 - fhp->fh_pre_change = inode->i_version; 256 + fhp->fh_pre_change = nfsd4_change_attribute(inode); 279 257 fhp->fh_pre_saved = true; 280 258 } 281 259 }