Linux filesystem development
 help / color / mirror / Atom feed
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>


             reply	other threads:[~2026-10-27 13:59 UTC|newest]

Thread overview: 3+ 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

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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox