diff options
Diffstat (limited to 'module/zfs')
-rw-r--r-- | module/zfs/zfs_vfsops.c | 16 | ||||
-rw-r--r-- | module/zfs/zfs_znode.c | 37 | ||||
-rw-r--r-- | module/zfs/zpl_file.c | 17 |
3 files changed, 61 insertions, 9 deletions
diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c index 781708ba9..18194a5dc 100644 --- a/module/zfs/zfs_vfsops.c +++ b/module/zfs/zfs_vfsops.c @@ -303,7 +303,21 @@ zfs_sync(struct super_block *sb, int wait, cred_t *cr) static void atime_changed_cb(void *arg, uint64_t newval) { - ((zfsvfs_t *)arg)->z_atime = newval; + zfsvfs_t *zfsvfs = arg; + struct super_block *sb = zfsvfs->z_sb; + + if (sb == NULL) + return; + /* + * Update SB_NOATIME bit in VFS super block. Since atime update is + * determined by atime_needs_update(), atime_needs_update() needs to + * return false if atime is turned off, and not unconditionally return + * false if atime is turned on. + */ + if (newval) + sb->s_flags &= ~SB_NOATIME; + else + sb->s_flags |= SB_NOATIME; } static void diff --git a/module/zfs/zfs_znode.c b/module/zfs/zfs_znode.c index d998e42ab..77eb8bb91 100644 --- a/module/zfs/zfs_znode.c +++ b/module/zfs/zfs_znode.c @@ -1345,16 +1345,39 @@ zfs_zinactive(znode_t *zp) zfs_znode_hold_exit(zfsvfs, zh); } -static inline int -zfs_compare_timespec(struct timespec *t1, struct timespec *t2) +#if defined(HAVE_INODE_TIMESPEC64_TIMES) +#define zfs_compare_timespec timespec64_compare +#else +#define zfs_compare_timespec timespec_compare +#endif + +/* + * 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 struct inode *ip) { - if (t1->tv_sec < t2->tv_sec) - return (-1); + inode_timespec_t now; + + gethrestime(&now); + /* + * 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(&ip->i_mtime, &ip->i_atime) >= 0) + return (B_TRUE); + + if (zfs_compare_timespec(&ip->i_ctime, &ip->i_atime) >= 0) + return (B_TRUE); - if (t1->tv_sec > t2->tv_sec) - return (1); + if ((hrtime_t)now.tv_sec - (hrtime_t)ip->i_atime.tv_sec >= 24*60*60) + return (B_TRUE); - return (t1->tv_nsec - t2->tv_nsec); + return (B_FALSE); } /* diff --git a/module/zfs/zpl_file.c b/module/zfs/zpl_file.c index 9c231d950..731836c2c 100644 --- a/module/zfs/zpl_file.c +++ b/module/zfs/zpl_file.c @@ -289,6 +289,8 @@ zpl_iter_read_common(struct kiocb *kiocb, const struct iovec *iovp, { cred_t *cr = CRED(); struct file *filp = kiocb->ki_filp; + struct inode *ip = filp->f_mapping->host; + zfsvfs_t *zfsvfs = ZTOZSB(ITOZ(ip)); ssize_t read; unsigned int f_flags = filp->f_flags; @@ -298,7 +300,20 @@ zpl_iter_read_common(struct kiocb *kiocb, const struct iovec *iovp, nr_segs, &kiocb->ki_pos, seg, f_flags, cr, skip); crfree(cr); - file_accessed(filp); + /* + * If relatime is enabled, call file_accessed() only if + * zfs_relatime_need_update() is true. This is needed since datasets + * with inherited "relatime" property aren't necessarily mounted with + * MNT_RELATIME flag (e.g. after `zfs set relatime=...`), which is what + * relatime test in VFS by relatime_need_update() is based on. + */ + if (!IS_NOATIME(ip) && zfsvfs->z_relatime) { + if (zfs_relatime_need_update(ip)) + file_accessed(filp); + } else { + file_accessed(filp); + } + return (read); } |