From: Jeremie Courreges-Anglas Subject: Re: relatime mount option for ffs filesystems To: tech@openbsd.org Cc: Marcus Glocker Date: Thu, 18 Sep 2025 19:13:51 +0200 On Tue, Aug 05, 2025 at 10:29:53AM +0100, Stuart Henderson wrote: > On 2025/08/05 06:59, Marcus Glocker wrote: > > On Mon, Aug 04, 2025 at 05:35:01PM +0200, Jeremie Courreges-Anglas wrote: > > > > > > > > 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. > > the ">24h" possible tweak would be helpful for this: > > > /dev/sd0d on /tmp type ffs (local, relatime, nodev, nosuid) > > without that, you should probably not use it for /tmp. > > $ grep -A8 tmp /etc/daily Indeed, here's a diff that adds the >24h check. I still think we want something like this, though I won't push for this before the next release cycle. Feedback and input still welcome. Index: lib/libc/sys/mount.2 =================================================================== RCS file: /home/cvs/src/lib/libc/sys/mount.2,v diff -u -p -r1.53 mount.2 --- lib/libc/sys/mount.2 6 Aug 2025 13:23:27 -0000 1.53 +++ lib/libc/sys/mount.2 18 Sep 2025 16:46:57 -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: /home/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 18 Sep 2025 16:46:57 -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: /home/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 18 Sep 2025 16:46:57 -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: /home/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 18 Sep 2025 16:46:57 -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: /home/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 18 Sep 2025 16:46:57 -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: /home/cvs/src/sbin/newfs/newfs.c,v diff -u -p -r1.120 newfs.c --- sbin/newfs/newfs.c 17 Sep 2025 16:07:57 -0000 1.120 +++ sbin/newfs/newfs.c 18 Sep 2025 16:46:57 -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: /home/cvs/src/sys/kern/vfs_syscalls.c,v diff -u -p -r1.377 vfs_syscalls.c --- sys/kern/vfs_syscalls.c 4 Aug 2025 04:59:31 -0000 1.377 +++ sys/kern/vfs_syscalls.c 18 Sep 2025 16:46:57 -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: /home/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 18 Sep 2025 16:46:57 -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: /home/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 18 Sep 2025 17:08:39 -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,58 @@ 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); + + /* + * Update atime in the following cases: + * - atime is older than ctime/mtime + * - atime is older than 24 hours + */ + if (timespeccmp(&atime, &ctime, <) || + timespeccmp(&atime, &mtime, <) || + (ts.tv_sec - atime.tv_sec) >= 86400) { + 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 +461,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: /home/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 18 Sep 2025 16:46:57 -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