From: Jeff Layton <jlayton@kernel.org>
To: Zorro Lang <zlang@kernel.org>
Cc: fstests@vger.kernel.org, linux-fsdevel@vger.kernel.org,
Jeff Layton <jlayton@kernel.org>
Subject: [PATCH RFC fstests] generic: add a test for multigrain timestamps
Date: Tue, 27 Oct 2026 09:58:50 -0400 [thread overview]
Message-ID: <20261027-mgtime-v1-1-ec68914a116e@kernel.org> (raw)
Ensure that the mtime and ctime apparently change, even when there are
multiple writes in quick succession. Since this is opt-in for in-kernel
filesystems and there is no way to test for this feature in userland,
limit it to just ext4, f2fs, btrfs, tmpfs and xfs for now.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
I'd like to start testing this to make sure it doesn't break, but it's
not universally supported on all filesystems. Is an explicit allowlist
like this the right approach?
---
src/Makefile | 2 +-
src/mgtime.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++
tests/generic/999 | 35 ++++++++++++++++
tests/generic/999.out | 1 +
4 files changed, 145 insertions(+), 1 deletion(-)
diff --git a/src/Makefile b/src/Makefile
index 0ff607ae1593..07bf749a1b8c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -36,7 +36,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
fscrypt-crypt-util bulkstat_null_ocount splice-test chprojid_fail \
detached_mounts_propagation ext4_resize t_readdir_3 splice2pipe \
uuid_ioctl t_snapshot_deleted_subvolume fiemap-fault min_dio_alignment \
- rw_hint btrfs_ioctl
+ rw_hint btrfs_ioctl mgtime
EXTRA_EXECS = dmerror fill2attr fill2fs fill2fs_check scaleread.sh \
btrfs_crc32c_forged_name.py popdir.pl popattr.py \
diff --git a/src/mgtime.c b/src/mgtime.c
new file mode 100644
index 000000000000..4ff7c206d31f
--- /dev/null
+++ b/src/mgtime.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Older Linux kernels always use coarse-grained timestamps, with a
+ * resolution of around 1 jiffy. Writes that are done in quick succession
+ * on those kernels apparent change to the ctime or mtime.
+ *
+ * Newer kernels attempt to ensure that fine-grained timestamps are used
+ * when the mtime or ctime are queried from userland.
+ *
+ * Open a file and do a 1 byte write to it and then statx the mtime and ctime.
+ * Do that in a loop 1000 times and ensure that the value is different from
+ * the last.
+ *
+ * Copyright (c) 2023: Jeff Layton <jlayton@kernel.org>
+ */
+#define _GNU_SOURCE 1
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdbool.h>
+
+#define NUM_WRITES 1000
+
+static int64_t statx_ts_cmp(struct statx_timestamp *ts1, struct statx_timestamp *ts2)
+{
+ int64_t ret = ts2->tv_sec - ts1->tv_sec;
+
+ if (ret)
+ return ret;
+
+ ret = ts2->tv_nsec;
+ ret -= ts1->tv_nsec;
+
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ int ret, fd, i;
+ struct statx stx = { };
+ struct statx_timestamp ctime, mtime;
+ bool verbose = false;
+
+ while ((i = getopt(argc, argv, "v")) != -1) {
+ switch (i) {
+ case 'v':
+ verbose = true;
+ break;
+ }
+ }
+
+ if (argc < 2) {
+ errno = -EINVAL;
+ perror("usage");
+ }
+
+ fd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ ret = statx(fd, "", AT_EMPTY_PATH, STATX_MTIME|STATX_CTIME, &stx);
+ if (ret < 0) {
+ perror("stat");
+ return 1;
+ }
+
+ ctime = stx.stx_ctime;
+ mtime = stx.stx_mtime;
+
+ for (i = 0; i < NUM_WRITES; ++i) {
+ ssize_t written;
+
+ written = write(fd, "a", 1);
+ if (written < 0) {
+ perror("write");
+ return 1;
+ }
+
+ ret = statx(fd, "", AT_EMPTY_PATH, STATX_MTIME|STATX_CTIME, &stx);
+ if (ret < 0) {
+ perror("stat");
+ return 1;
+ }
+
+ if (verbose)
+ printf("%d: %llu.%u\n", i, stx.stx_ctime.tv_sec, stx.stx_ctime.tv_nsec);
+
+ if (!statx_ts_cmp(&ctime, &stx.stx_ctime)) {
+ printf("Duplicate ctime after write!\n");
+ return 1;
+ }
+
+ if (!statx_ts_cmp(&mtime, &stx.stx_mtime)) {
+ printf("Duplicate mtime after write!\n");
+ return 1;
+ }
+
+ ctime = stx.stx_ctime;
+ mtime = stx.stx_mtime;
+ }
+ return 0;
+}
diff --git a/tests/generic/999 b/tests/generic/999
new file mode 100755
index 000000000000..878247515e71
--- /dev/null
+++ b/tests/generic/999
@@ -0,0 +1,35 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026, Jeff Layton <jlayton@kernel.org>
+#
+# FS QA Test No. 999
+#
+# Multigrain time test
+#
+# Open a file, and do 1 byte writes to it, and statx the file for
+# the ctime and mtime after each write. Ensure that they have changed
+# since the previous write.
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# Override the default cleanup function.
+_require_test_program mgtime
+
+# Multigrain timestamps are opt-in per filesystem; only allowlist the ones
+# known to support them.
+case "$FSTYP" in
+ext4|btrfs|tmpfs|xfs|f2fs)
+ ;;
+*)
+ _notrun "multigrain timestamps not supported on $FSTYP"
+ ;;
+esac
+
+testfile="$TEST_DIR/test_mgtime_file.$$"
+
+$here/src/mgtime $testfile
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/999.out b/tests/generic/999.out
new file mode 100644
index 000000000000..7fbc67688211
--- /dev/null
+++ b/tests/generic/999.out
@@ -0,0 +1 @@
+QA output created by 999
---
base-commit: 333c067ccf2f389f5558860336a7fa64157cd46e
change-id: 20261027-mgtime-64194b4e22b4
Best regards,
--
Jeff Layton <jlayton@kernel.org>
next reply other threads:[~2026-10-27 13:59 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 13:58 Jeff Layton [this message]
2026-10-28 15:54 ` [PATCH RFC fstests] generic: add a test for multigrain timestamps Christoph Hellwig
2026-10-28 17:20 ` Jeff Layton
2026-10-29 12:02 ` Christoph Hellwig
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261027-mgtime-v1-1-ec68914a116e@kernel.org \
--to=jlayton@kernel.org \
--cc=fstests@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=zlang@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.