diff options
Diffstat (limited to 'module')
-rw-r--r-- | module/zcommon/zfs_prop.c | 2 | ||||
-rw-r--r-- | module/zfs/zfs_vfsops.c | 13 | ||||
-rw-r--r-- | module/zfs/zfs_znode.c | 82 |
3 files changed, 88 insertions, 9 deletions
diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 121b1eb57..dd456b59a 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -263,6 +263,8 @@ 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); + zprop_register_index(ZFS_PROP_RELATIME, "relatime", 0, PROP_INHERIT, + ZFS_TYPE_FILESYSTEM, "on | off", "RELATIME", boolean_table); zprop_register_index(ZFS_PROP_DEVICES, "devices", 1, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "DEVICES", boolean_table); diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c index f331a36ea..ec59bfbd8 100644 --- a/module/zfs/zfs_vfsops.c +++ b/module/zfs/zfs_vfsops.c @@ -138,6 +138,12 @@ atime_changed_cb(void *arg, uint64_t newval) } static void +relatime_changed_cb(void *arg, uint64_t newval) +{ + ((zfs_sb_t *)arg)->z_relatime = newval; +} + +static void xattr_changed_cb(void *arg, uint64_t newval) { zfs_sb_t *zsb = arg; @@ -275,6 +281,8 @@ zfs_register_callbacks(zfs_sb_t *zsb) dsl_pool_config_enter(dmu_objset_pool(os), FTAG); error = dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_ATIME), atime_changed_cb, zsb); + error = dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_RELATIME), relatime_changed_cb, zsb); error = error ? error : dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_XATTR), xattr_changed_cb, zsb); error = error ? error : dsl_prop_register(ds, @@ -314,6 +322,8 @@ unregister: */ (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_ATIME), atime_changed_cb, zsb); + (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_RELATIME), + relatime_changed_cb, zsb); (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_XATTR), xattr_changed_cb, zsb); (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_RECORDSIZE), @@ -914,6 +924,9 @@ zfs_unregister_callbacks(zfs_sb_t *zsb) VERIFY(dsl_prop_unregister(ds, "atime", atime_changed_cb, zsb) == 0); + VERIFY(dsl_prop_unregister(ds, "relatime", relatime_changed_cb, + zsb) == 0); + VERIFY(dsl_prop_unregister(ds, "xattr", xattr_changed_cb, zsb) == 0); diff --git a/module/zfs/zfs_znode.c b/module/zfs/zfs_znode.c index 2a4b1c648..5e9941034 100644 --- a/module/zfs/zfs_znode.c +++ b/module/zfs/zfs_znode.c @@ -1109,25 +1109,89 @@ zfs_zinactive(znode_t *zp) ZFS_OBJ_HOLD_EXIT(zsb, z_id); } +static inline int +zfs_compare_timespec(struct timespec *t1, struct timespec *t2) +{ + if (t1->tv_sec < t2->tv_sec) + return (-1); + + if (t1->tv_sec > t2->tv_sec) + return (1); + + return (t1->tv_nsec - t2->tv_nsec); +} + +/* + * 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. + */ +static inline boolean_t +zfs_atime_need_update(znode_t *zp, timestruc_t *now) +{ + if (!ZTOZSB(zp)->z_relatime) + return (B_TRUE); + + /* + * 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. + */ + if (zfs_compare_timespec(&ZTOI(zp)->i_mtime, &ZTOI(zp)->i_atime) >= 0) + return (B_TRUE); + + if (zfs_compare_timespec(&ZTOI(zp)->i_ctime, &ZTOI(zp)->i_atime) >= 0) + return (B_TRUE); + + if ((long)now->tv_sec - ZTOI(zp)->i_atime.tv_sec >= 24*60*60) + return (B_TRUE); + + return (B_FALSE); +} + +/* + * Prepare to update znode time stamps. + * + * IN: zp - znode requiring timestamp update + * flag - ATTR_MTIME, ATTR_CTIME, ATTR_ATIME flags + * have_tx - true of caller is creating a new txg + * + * OUT: zp - new atime (via underlying inode's i_atime) + * mtime - new mtime + * ctime - new ctime + * + * NOTE: The arguments are somewhat redundant. The following condition + * is always true: + * + * have_tx == !(flag & ATTR_ATIME) + */ void zfs_tstamp_update_setup(znode_t *zp, uint_t flag, uint64_t mtime[2], uint64_t ctime[2], boolean_t have_tx) { timestruc_t now; + ASSERT(have_tx == !(flag & ATTR_ATIME)); gethrestime(&now); - if (have_tx) { /* will sa_bulk_update happen really soon? */ + /* + * NOTE: The following test intentionally does not update z_atime_dirty + * in the case where an ATIME update has been requested but for which + * the update is omitted due to relatime logic. The rationale being + * that if the flag was set somewhere else, we should leave it alone + * here. + */ + if (flag & ATTR_ATIME) { + if (zfs_atime_need_update(zp, &now)) { + ZFS_TIME_ENCODE(&now, zp->z_atime); + ZTOI(zp)->i_atime.tv_sec = zp->z_atime[0]; + ZTOI(zp)->i_atime.tv_nsec = zp->z_atime[1]; + zp->z_atime_dirty = 1; + } + } else { zp->z_atime_dirty = 0; zp->z_seq++; - } else { - zp->z_atime_dirty = 1; - } - - if (flag & ATTR_ATIME) { - ZFS_TIME_ENCODE(&now, zp->z_atime); - ZTOI(zp)->i_atime.tv_sec = zp->z_atime[0]; - ZTOI(zp)->i_atime.tv_nsec = zp->z_atime[1]; } if (flag & ATTR_MTIME) { |