Download raw body.
relatime mount option for ffs filesystems
On Mon, Aug 04, 2025 at 05:35:01PM +0200, Jeremie Courreges-Anglas wrote:
>
> By default ffs updates the access time of files each time they are
> accessed, which results in disk writes as soon as you're reading
> files. Even if the files are in your buffer cache and the disk isn't
> accessed for reading, you get disk writes. This is not something I
> want by default on my machines.
>
> A possible workaround is noatime, but noatime drops access time
> updates in an aggressive manner that breaks any application that cares
> about atime - a well-documented example being mutt(1). So another
> scheme was designed and even made the default on Linux: relatime, for
> "relative access time". NetBSD has also implemented it.
> With relatime, access time is updated on reads only if the file has
> been modified (mtime/ctime) since the last read. So merely reading
> files no longer incurs pointless writes.
>
> The diff below implements it for ffs only. It doesn't implement
> possible tweaks like updating atime if previous access happened more
> than 24 hours ago. If this goes in then I can work on making relatime
> available on other filesystems. Making relatime the default isn't on
> my roadmap and is a discussion for another day.
>
>
> ok?
>
> To review the diff I'd split ufs_itimes() call sites in the following
> categories:
> - ffs_read() is my main interest since it sets IN_ACCESS
> - various vop_close callbacks, shouldn't be a concern
> - ufs_*read compound the existing IN_LAZYMOD handling for special
> devices with relatime, shouldn't be a concern
> - ufs_setattr() implications for relatime are a bit contrived: if
> setattr tries to update atime *only*, IN_ACCESS is set but
> ufs_itimes() will skip updating atime and skip setting
> IN_MODIFIED/IN_LAZYMOD. Then atime is forcefully updated in struct
> inode with whatever was passed to setattr, but UFS_UPDATE() may skip
> writing anything to disk. To handle this fringe case, I just set
> IN_MODIFIED if explicitely requested, but it feels a bit paranoid.
> Do my analysis for this special case and the proposal make sense?
>
> Feedback & oks welcome.
I like it. The diff works fine on one of my laptops:
x1e$ mount
/dev/sd0a on / type ffs (local, relatime)
/dev/sd0n on /home type ffs (local, relatime, nodev, nosuid)
/dev/sd0d on /tmp type ffs (local, relatime, nodev, nosuid)
/dev/sd0f on /usr type ffs (local, relatime, nodev)
/dev/sd0g on /usr/X11R6 type ffs (local, relatime, nodev)
/dev/sd0h on /usr/local type ffs (local, relatime, nodev, wxallowed)
/dev/sd0m on /usr/obj type ffs (local, relatime, nodev, nosuid)
/dev/sd0l on /usr/src type ffs (local, relatime, nodev, nosuid)
/dev/sd0e on /var type ffs (local, relatime, nodev, nosuid)
> Index: lib/libc/sys/mount.2
> ===================================================================
> RCS file: /cvs/src/lib/libc/sys/mount.2,v
> diff -u -p -r1.52 mount.2
> --- lib/libc/sys/mount.2 10 Nov 2023 00:25:59 -0000 1.52
> +++ lib/libc/sys/mount.2 3 Aug 2025 16:08:07 -0000
> @@ -78,6 +78,9 @@ suppress default semantics which affect
> .It Dv MNT_RDONLY
> The filesystem should be treated as read-only:
> even the superuser may not write to it.
> +.It Dv MNT_RELATIME
> +Only update the access time on files in the filesystem if the modification
> +or status change times are newer.
> .It Dv MNT_NOATIME
> Do not update the access time on files in the filesystem unless
> the modification or status change times are also being updated.
> Index: sbin/mount/mntopts.h
> ===================================================================
> RCS file: /cvs/src/sbin/mount/mntopts.h,v
> diff -u -p -r1.18 mntopts.h
> --- sbin/mount/mntopts.h 10 Sep 2016 16:53:30 -0000 1.18
> +++ sbin/mount/mntopts.h 3 Aug 2025 16:08:07 -0000
> @@ -61,6 +61,7 @@ union mntval {
> #define MOPT_NOPERM { "perm", MNT_NOPERM, MFLAG_INVERSE | MFLAG_SET }
> #define MOPT_WXALLOWED { "wxallowed", MNT_WXALLOWED, MFLAG_SET }
> #define MOPT_RDONLY { "rdonly", MNT_RDONLY, MFLAG_SET }
> +#define MOPT_RELATIME { "relatime", MNT_RELATIME, MFLAG_SET }
> #define MOPT_SYNC { "sync", MNT_SYNCHRONOUS, MFLAG_SET }
> #define MOPT_USERQUOTA { "userquota", 0, MFLAG_SET | MFLAG_STRVAL \
> | MFLAG_OPT }
> Index: sbin/mount/mount.8
> ===================================================================
> RCS file: /cvs/src/sbin/mount/mount.8,v
> diff -u -p -r1.92 mount.8
> --- sbin/mount/mount.8 10 Nov 2023 00:26:00 -0000 1.92
> +++ sbin/mount/mount.8 3 Aug 2025 16:08:07 -0000
> @@ -178,8 +178,16 @@ The same as
> .Fl f ;
> forces the revocation of write access when trying to downgrade
> a file system mount status from read-write to read-only.
> +.It Cm relatime
> +(FFS only)
> +Only update the access time on files in the filesystem if it is older
> +than the modification or change time.
> +Like
> +.Cm noatime ,
> +it is useful to avoid extra disk activity, but is also more friendly
> +to some applications.
> .It Cm noatime
> -Do not update atime on files in the system unless the mtime or ctime
> +Do not update atime on files in the filesystem unless the mtime or ctime
> is being changed as well.
> This option is useful for laptops and news servers where one does
> not want the extra disk activity associated with updating the atime.
> Index: sbin/mount/mount.c
> ===================================================================
> RCS file: /cvs/src/sbin/mount/mount.c,v
> diff -u -p -r1.78 mount.c
> --- sbin/mount/mount.c 9 May 2024 08:35:40 -0000 1.78
> +++ sbin/mount/mount.c 3 Aug 2025 16:08:07 -0000
> @@ -84,6 +84,7 @@ static struct opt {
> { MNT_EXPORTANON, 1, "anon uid mapping", "" },
> { MNT_EXRDONLY, 1, "exported read-only", "" },
> { MNT_LOCAL, 0, "local", "" },
> + { MNT_RELATIME, 0, "relatime", "relatime" },
> { MNT_NOATIME, 0, "noatime", "noatime" },
> { MNT_NODEV, 0, "nodev", "nodev" },
> { MNT_NOEXEC, 0, "noexec", "noexec" },
> Index: sbin/mount_ffs/mount_ffs.c
> ===================================================================
> RCS file: /cvs/src/sbin/mount_ffs/mount_ffs.c,v
> diff -u -p -r1.26 mount_ffs.c
> --- sbin/mount_ffs/mount_ffs.c 4 Dec 2022 23:50:46 -0000 1.26
> +++ sbin/mount_ffs/mount_ffs.c 3 Aug 2025 16:08:07 -0000
> @@ -52,6 +52,7 @@ static const struct mntopt mopts[] = {
> MOPT_ASYNC,
> MOPT_SYNC,
> MOPT_UPDATE,
> + MOPT_RELATIME,
> MOPT_RELOAD,
> MOPT_FORCE,
> MOPT_SOFTDEP,
> Index: sbin/newfs/newfs.c
> ===================================================================
> RCS file: /cvs/src/sbin/newfs/newfs.c,v
> diff -u -p -r1.118 newfs.c
> --- sbin/newfs/newfs.c 9 Jan 2024 03:16:00 -0000 1.118
> +++ sbin/newfs/newfs.c 3 Aug 2025 16:08:07 -0000
> @@ -82,6 +82,7 @@ struct mntopt mopts[] = {
> MOPT_ASYNC,
> MOPT_UPDATE,
> MOPT_FORCE,
> + MOPT_RELATIME,
> { NULL },
> };
>
> Index: sys/kern/vfs_syscalls.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/vfs_syscalls.c,v
> diff -u -p -r1.376 vfs_syscalls.c
> --- sys/kern/vfs_syscalls.c 5 Jul 2025 04:25:43 -0000 1.376
> +++ sys/kern/vfs_syscalls.c 4 Aug 2025 10:28:10 -0000
> @@ -238,10 +238,11 @@ update:
> else if (mp->mnt_flag & MNT_RDONLY)
> mp->mnt_flag |= MNT_WANTRDWR;
> mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_WXALLOWED | MNT_NODEV |
> - MNT_SYNCHRONOUS | MNT_ASYNC | MNT_NOATIME | MNT_NOPERM | MNT_FORCE);
> + MNT_SYNCHRONOUS | MNT_ASYNC | MNT_RELATIME | MNT_NOATIME |
> + MNT_NOPERM | MNT_FORCE);
> mp->mnt_flag |= flags & (MNT_NOSUID | MNT_NOEXEC | MNT_WXALLOWED |
> - MNT_NODEV | MNT_SYNCHRONOUS | MNT_ASYNC | MNT_NOATIME | MNT_NOPERM |
> - MNT_FORCE);
> + MNT_NODEV | MNT_SYNCHRONOUS | MNT_ASYNC | MNT_RELATIME | MNT_NOATIME |
> + MNT_NOPERM | MNT_FORCE);
> /*
> * Mount the filesystem.
> */
> Index: sys/sys/mount.h
> ===================================================================
> RCS file: /cvs/src/sys/sys/mount.h,v
> diff -u -p -r1.153 mount.h
> --- sys/sys/mount.h 2 Jan 2025 01:19:22 -0000 1.153
> +++ sys/sys/mount.h 4 Aug 2025 09:42:12 -0000
> @@ -383,14 +383,14 @@ struct mount {
> /*
> * Mask of flags that are visible to statfs()
> */
> -#define MNT_VISFLAGMASK 0x0400ffff
> +#define MNT_VISFLAGMASK 0x0440ffff
>
> #define MNT_BITS \
> "\20\001RDONLY\002SYNCHRONOUS\003NOEXEC\004NOSUID\005NODEV\006NOPERM" \
> "\007ASYNC\010EXRDONLY\011EXPORTED\012DEFEXPORTED\013EXPORTANON" \
> "\014WXALLOWED\015LOCAL\016QUOTA\017ROOTFS\020NOATIME\021UPDATE" \
> - "\022DELEXPORT\023RELOAD\024FORCE\025STALLED\026SWAPPABLE\031UNMOUNT" \
> - "\032WANTRDWR\033SOFTDEP\034DOOMED"
> + "\022DELEXPORT\023RELOAD\024FORCE\025STALLED\026SWAPPABLE\027RELATIME" \
> + "\031UNMOUNT\032WANTRDWR\033SOFTDEP\034DOOMED"
>
> /*
> * filesystem control flags.
> @@ -401,6 +401,7 @@ struct mount {
> #define MNT_FORCE 0x00080000 /* force unmount or readonly change */
> #define MNT_STALLED 0x00100000 /* filesystem stalled */
> #define MNT_SWAPPABLE 0x00200000 /* filesystem can be used for swap */
> +#define MNT_RELATIME 0x00400000 /* update atime iff <= to ctime/mtime */
> #define MNT_UNMOUNT 0x01000000 /* unmount in progress */
> #define MNT_WANTRDWR 0x02000000 /* want upgrade to read/write */
> #define MNT_SOFTDEP 0x04000000 /* soft dependencies being done - now ignored */
> Index: sys/ufs/ufs/ufs_vnops.c
> ===================================================================
> RCS file: /cvs/src/sys/ufs/ufs/ufs_vnops.c,v
> diff -u -p -r1.164 ufs_vnops.c
> --- sys/ufs/ufs/ufs_vnops.c 18 Oct 2024 05:52:33 -0000 1.164
> +++ sys/ufs/ufs/ufs_vnops.c 4 Aug 2025 15:03:11 -0000
> @@ -94,6 +94,7 @@ ufs_itimes(struct vnode *vp)
> {
> struct inode *ip;
> struct timespec ts;
> + int modified = 0;
>
> ip = VTOI(vp);
> if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0)
> @@ -109,27 +110,52 @@ ufs_itimes(struct vnode *vp)
> }
> #endif
>
> - if ((vp->v_type == VBLK || vp->v_type == VCHR))
> - ip->i_flag |= IN_LAZYMOD;
> - else
> - ip->i_flag |= IN_MODIFIED;
> -
> getnanotime(&ts);
> - if (ip->i_flag & IN_ACCESS) {
> - DIP_ASSIGN(ip, atime, ts.tv_sec);
> - DIP_ASSIGN(ip, atimensec, ts.tv_nsec);
> - }
> if (ip->i_flag & IN_UPDATE) {
> DIP_ASSIGN(ip, mtime, ts.tv_sec);
> DIP_ASSIGN(ip, mtimensec, ts.tv_nsec);
> + modified = 1;
> }
> if (ip->i_flag & IN_CHANGE) {
> DIP_ASSIGN(ip, ctime, ts.tv_sec);
> DIP_ASSIGN(ip, ctimensec, ts.tv_nsec);
> ip->i_modrev++;
> + modified = 1;
> + }
> + if (ip->i_flag & IN_ACCESS) {
> + if (vp->v_mount->mnt_flag & MNT_RELATIME) {
> + struct timespec atime, ctime, mtime;
> +
> + atime.tv_sec = DIP(ip, atime);
> + atime.tv_nsec = DIP(ip, atimensec);
> + ctime.tv_sec = DIP(ip, ctime);
> + ctime.tv_nsec = DIP(ip, ctimensec);
> + mtime.tv_sec = DIP(ip, mtime);
> + mtime.tv_nsec = DIP(ip, mtimensec);
> +
> + if (timespeccmp(&atime, &ctime, <) ||
> + timespeccmp(&atime, &mtime, <)) {
> + DIP_ASSIGN(ip, atime, ts.tv_sec);
> + DIP_ASSIGN(ip, atimensec, ts.tv_nsec);
> + modified = 1;
> + }
> + } else {
> + DIP_ASSIGN(ip, atime, ts.tv_sec);
> + DIP_ASSIGN(ip, atimensec, ts.tv_nsec);
> + modified = 1;
> + }
> +
> }
>
> - out:
> + if (!modified)
> + goto out;
> +
> + if ((vp->v_type == VBLK || vp->v_type == VCHR))
> + ip->i_flag |= IN_LAZYMOD;
> + else
> + ip->i_flag |= IN_MODIFIED;
> +
> +out:
> ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
> }
>
> @@ -429,10 +455,14 @@ ufs_setattr(void *v)
> if (vap->va_mtime.tv_nsec != VNOVAL) {
> DIP_ASSIGN(ip, mtime, vap->va_mtime.tv_sec);
> DIP_ASSIGN(ip, mtimensec, vap->va_mtime.tv_nsec);
> + /* mtime change explicitely requested */
> + ip->i_flag |= IN_MODIFIED;
> }
> if (vap->va_atime.tv_nsec != VNOVAL) {
> DIP_ASSIGN(ip, atime, vap->va_atime.tv_sec);
> DIP_ASSIGN(ip, atimensec, vap->va_atime.tv_nsec);
> + /* atime change explicitely requested */
> + ip->i_flag |= IN_MODIFIED;
> }
> error = UFS_UPDATE(ip, 0);
> if (error)
> Index: usr.sbin/pstat/pstat.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/pstat/pstat.c,v
> diff -u -p -r1.130 pstat.c
> --- usr.sbin/pstat/pstat.c 10 Jul 2024 13:29:23 -0000 1.130
> +++ usr.sbin/pstat/pstat.c 4 Aug 2025 10:29:10 -0000
> @@ -789,6 +789,11 @@ mount_print(struct mount *mp)
> flags &= ~MNT_ROOTFS;
> comma = ",";
> }
> + if (flags & MNT_RELATIME) {
> + (void)printf("%srelatime", comma);
> + flags &= ~MNT_RELATIME;
> + comma = ",";
> + }
> if (flags & MNT_NOATIME) {
> (void)printf("%snoatime", comma);
> flags &= ~MNT_NOATIME;
>
> --
> jca
>
relatime mount option for ffs filesystems