summaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
Diffstat (limited to 'module')
-rw-r--r--module/zcommon/zfs_prop.c2
-rw-r--r--module/zfs/zfs_vfsops.c13
-rw-r--r--module/zfs/zfs_znode.c82
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) {