From 16858492e6e4793a84858b62f2abe8d1ef435509 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Wed, 1 Apr 2026 16:48:50 -0400 Subject: [PATCH] FreeBSD: Implement relatime property While FreeBSD does not support relatime natively, it seems trivial to implement it just as dataset property for consistency. To not change the status quo, change its default to off on FreeBSD. Now, if explicitly enabled, it should actually work. Reviewed-by: Brian Behlendorf Signed-off-by: Alexander Motin Closes #18385 --- include/os/freebsd/zfs/sys/zfs_vfsops_os.h | 1 + include/os/freebsd/zfs/sys/zfs_znode_impl.h | 4 +- module/os/freebsd/zfs/zfs_vfsops.c | 8 ++++ module/os/freebsd/zfs/zfs_znode_os.c | 43 +++++++++++++++++++++ module/zcommon/zfs_prop.c | 11 ++++-- tests/runfiles/common.run | 3 +- tests/runfiles/linux.run | 4 -- 7 files changed, 65 insertions(+), 9 deletions(-) diff --git a/include/os/freebsd/zfs/sys/zfs_vfsops_os.h b/include/os/freebsd/zfs/sys/zfs_vfsops_os.h index 3ed311d49cc..62e78d82bcb 100644 --- a/include/os/freebsd/zfs/sys/zfs_vfsops_os.h +++ b/include/os/freebsd/zfs/sys/zfs_vfsops_os.h @@ -71,6 +71,7 @@ struct zfsvfs { boolean_t z_utf8; /* utf8-only */ int z_norm; /* normalization flags */ boolean_t z_atime; /* enable atimes mount option */ + boolean_t z_relatime; /* enable relatime mount option */ boolean_t z_unmounted; /* unmounted */ zfs_teardown_lock_t z_teardown_lock; zfs_teardown_inactive_lock_t z_teardown_inactive_lock; diff --git a/include/os/freebsd/zfs/sys/zfs_znode_impl.h b/include/os/freebsd/zfs/sys/zfs_znode_impl.h index b292818750d..6c9e8abad82 100644 --- a/include/os/freebsd/zfs/sys/zfs_znode_impl.h +++ b/include/os/freebsd/zfs/sys/zfs_znode_impl.h @@ -170,11 +170,13 @@ zfs_exit(zfsvfs_t *zfsvfs, const char *tag) (tp)->tv_nsec = (long)(stmp)[1]; \ } #define ZFS_ACCESSTIME_STAMP(zfsvfs, zp) \ - if ((zfsvfs)->z_atime && !((zfsvfs)->z_vfs->vfs_flag & VFS_RDONLY)) \ + if ((zfsvfs)->z_atime && !((zfsvfs)->z_vfs->vfs_flag & VFS_RDONLY) && \ + (!(zfsvfs)->z_relatime || zfs_relatime_need_update(zp))) \ zfs_tstamp_update_setup_ext(zp, ACCESSED, NULL, NULL, B_FALSE); extern void zfs_tstamp_update_setup_ext(struct znode *, uint_t, uint64_t [2], uint64_t [2], boolean_t have_tx); +extern boolean_t zfs_relatime_need_update(const struct znode *); extern void zfs_znode_free(struct znode *); extern zil_replay_func_t *const zfs_replay_vector[TX_MAX_TYPE]; diff --git a/module/os/freebsd/zfs/zfs_vfsops.c b/module/os/freebsd/zfs/zfs_vfsops.c index 79b78428891..7f9c0267822 100644 --- a/module/os/freebsd/zfs/zfs_vfsops.c +++ b/module/os/freebsd/zfs/zfs_vfsops.c @@ -494,6 +494,12 @@ atime_changed_cb(void *arg, uint64_t newval) } } +static void +relatime_changed_cb(void *arg, uint64_t newval) +{ + ((zfsvfs_t *)arg)->z_relatime = (newval != 0); +} + static void xattr_changed_cb(void *arg, uint64_t newval) { @@ -752,6 +758,8 @@ zfs_register_callbacks(vfs_t *vfsp) */ error = dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_ATIME), atime_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_RELATIME), relatime_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_XATTR), xattr_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, diff --git a/module/os/freebsd/zfs/zfs_znode_os.c b/module/os/freebsd/zfs/zfs_znode_os.c index b9f427b3970..6f74c924eb7 100644 --- a/module/os/freebsd/zfs/zfs_znode_os.c +++ b/module/os/freebsd/zfs/zfs_znode_os.c @@ -1325,6 +1325,49 @@ zfs_znode_free(znode_t *zp) zfs_znode_free_kmem(zp); } +/* + * Determine whether the znode's atime must be updated. The logic mostly + * duplicates the Linux kernel's relatime_need_update() functionality. + * This function is only called if the underlying filesystem actually has + * atime updates enabled. + */ +boolean_t +zfs_relatime_need_update(const znode_t *zp) +{ + uint64_t mtime[2], ctime[2]; + sa_bulk_attr_t bulk[2]; + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + struct timespec now, tmp_atime, tmp_ts; + int count = 0; + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, mtime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, ctime, 16); + if (sa_bulk_lookup(zp->z_sa_hdl, bulk, count) != 0) + return (B_TRUE); + + ZFS_TIME_DECODE(&tmp_atime, zp->z_atime); + /* + * In relatime mode, only update the atime if the previous atime + * is earlier than either the ctime or mtime or if at least a day + * has passed since the last update of atime. + */ + ZFS_TIME_DECODE(&tmp_ts, mtime); + /* CSTYLED */ + if (timespeccmp(&tmp_ts, &tmp_atime, >=)) + return (B_TRUE); + + ZFS_TIME_DECODE(&tmp_ts, ctime); + /* CSTYLED */ + if (timespeccmp(&tmp_ts, &tmp_atime, >=)) + return (B_TRUE); + + vfs_timestamp(&now); + if ((hrtime_t)now.tv_sec - (hrtime_t)tmp_atime.tv_sec >= 24*60*60) + return (B_TRUE); + + return (B_FALSE); +} + void zfs_tstamp_update_setup_ext(znode_t *zp, uint_t flag, uint64_t mtime[2], uint64_t ctime[2], boolean_t have_tx) diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 78d4b0a05f7..71482c8d795 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -497,9 +497,14 @@ zfs_prop_init(void) /* inherit index (boolean) properties */ zprop_register_index(ZFS_PROP_ATIME, "atime", 1, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off", "ATIME", boolean_table, sfeatures); - zprop_register_index(ZFS_PROP_RELATIME, "relatime", 1, PROP_INHERIT, - ZFS_TYPE_FILESYSTEM, "on | off", "RELATIME", boolean_table, - sfeatures); + zprop_register_index(ZFS_PROP_RELATIME, "relatime", +#ifdef __FreeBSD__ + 0, /* FreeBSD does not natively support relatime. */ +#else + 1, +#endif + PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off", "RELATIME", + boolean_table, sfeatures); zprop_register_index(ZFS_PROP_DEVICES, "devices", 1, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "DEVICES", boolean_table, sfeatures); diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index 9fa757fc80f..391609332f4 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -50,7 +50,8 @@ tests = ['dbufstats_001_pos', 'dbufstats_002_pos', 'dbufstats_003_pos', tags = ['functional', 'arc'] [tests/functional/atime] -tests = ['atime_001_pos', 'atime_002_neg', 'root_atime_off', 'root_atime_on'] +tests = ['atime_001_pos', 'atime_002_neg', 'atime_003_pos', 'root_atime_off', + 'root_atime_on', 'root_relatime_on'] tags = ['functional', 'atime'] [tests/functional/bclone] diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run index 2717bf53d0b..6c912868d89 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -30,10 +30,6 @@ tags = ['functional', 'acl', 'posix'] tests = ['posix_001_pos', 'posix_002_pos', 'posix_003_pos', 'posix_004_pos'] tags = ['functional', 'acl', 'posix-sa'] -[tests/functional/atime:Linux] -tests = ['atime_003_pos', 'root_relatime_on'] -tags = ['functional', 'atime'] - [tests/functional/block_cloning:Linux] tests = ['block_cloning_ficlone', 'block_cloning_ficlonerange', 'block_cloning_ficlonerange_partial', 'block_cloning_disabled_ficlone',