Skip to content

Commit 70e5aac

Browse files
committed
Add handling for STATX_CHANGE_COOKIE
This commit adds handling for the STATX_CHANGE_COOKIE so that we can properly surface the ZFS znode sequence to NFS clients via knfsd. If knfsd does not have STATX_CHANGE_COOKIE in statx result then it will synthesize the NFS change_info4 structure and related change4id values algorithmically based on the ctime value of the file. Since internally ZFS is using ktime_get_coarse_real_ts64() for the timestamp calculation here it introduces the possiblity that the change will not increment the change4id of directories / files causing a failure in the client to invalidate its attr cache (among other things). See RFC 8881 Section 10.8 for discussion of how clients may implement name and directory caching. Notable in this commit is that we are not initializing the inode->i_version to the znode->z_seq number. The reason for this is that we're intentionally not setting `SB_I_VERSION`. This indicates that the filesystem manages its own i_version and so it is not populated in the generic_fillattr. The following compares tight loop of setattr over NFSv4 protocol while traching nfsd4_change_attribute. Before change: inode, change_attribute 4723, 7590032215978780890 4723, 7590032215978780890 4723, 7590032215978780890 4723, 7590032215982780865 4723, 7590032215982780865 After change: inode, change_attribute 7602, 7590032992517123951 7602, 7590032992517123952 7602, 7590032992517123953 7602, 7590032992517123954 7602, 7590032992517123955 Signed-off-by: Andrew Walker <andrew.walker@truenas.com>
1 parent b9b8444 commit 70e5aac

File tree

2 files changed

+38
-1
lines changed

2 files changed

+38
-1
lines changed

module/os/linux/zfs/zfs_vnops_os.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2581,8 +2581,19 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zidmap_t *mnt_ns)
25812581
if (fuid_dirtied)
25822582
zfs_fuid_sync(zfsvfs, tx);
25832583

2584-
if (mask != 0)
2584+
if (mask != 0) {
25852585
zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask, fuidp);
2586+
/*
2587+
* Ensure that the z_seq is always incremented on setattr
2588+
* operation. This is required for change accounting for
2589+
* NFS clients.
2590+
*
2591+
* ATTR_MODE already increments via zfs_acl_chmod_setattr.
2592+
* ATTR_SIZE already increments via zfs_freesp.
2593+
*/
2594+
if (!(mask & (ATTR_MODE | ATTR_SIZE)))
2595+
zp->z_seq++;
2596+
}
25862597

25872598
mutex_exit(&zp->z_lock);
25882599
if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE))

module/os/linux/zfs/zpl_inode.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,32 @@ zpl_getattr_impl(const struct path *path, struct kstat *stat, u32 request_mask,
506506
}
507507
#endif
508508

509+
#ifdef STATX_CHANGE_COOKIE
510+
if (request_mask & STATX_CHANGE_COOKIE) {
511+
/*
512+
* knfsd uses the STATX_CHANGE_COOKIE to surface to clients
513+
* change_info4 data, which is used to implement NFS client
514+
* name caching (see RFC 8881 Section 10.8). This number
515+
* should always increase with changes and should not be
516+
* reused. We cannot simply present ctime here because
517+
* ZFS uses a coarse timer to set them, which may cause
518+
* clients to fail to detect changes and invalidate cache.
519+
*
520+
* ZFS always increments znode z_seq number, but this is
521+
* uint_t and so we mask in ctime to upper bits.
522+
*
523+
* STATX_ATTR_CHANGE_MONOTONIC is advertised
524+
* to prevent knfsd from generating the change cookie
525+
* based on ctime. C.f. nfsd4_change_attribute in
526+
* fs/nfsd/nfsfh.c.
527+
*/
528+
stat->change_cookie =
529+
((u64)stat->ctime.tv_sec << 32) | zp->z_seq;
530+
stat->attributes |= STATX_ATTR_CHANGE_MONOTONIC;
531+
stat->result_mask |= STATX_CHANGE_COOKIE;
532+
}
533+
#endif
534+
509535
#ifdef STATX_DIOALIGN
510536
if (request_mask & STATX_DIOALIGN) {
511537
uint64_t align;

0 commit comments

Comments
 (0)