All of lore.kernel.org
 help / color / mirror / Atom feed
* [LTP] [PATCH v7] syscalls/statx13: Add basic test for STATX_WRITE_ATOMIC on regular file
@ 2026-10-16  7:13 Gaurav Pathak
  2026-10-16 10:52 ` [LTP] " linuxtestproject.agent
  0 siblings, 1 reply; 8+ messages in thread
From: Gaurav Pathak @ 2026-10-16  7:13 UTC (permalink / raw)
  To: Linux Test Project; +Cc: Gaurav Pathak

From: Gaurav Pathak <gaurav.pathak@suse.com>

This patch adds a new test to validate the atomic write limit fields.
The test ensures the filesystem correctly advertises the
STATX_ATTR_WRITE_ATOMIC attribute when queried on a file opened with
O_DIRECT. It also verifies that the reported optimized maximum is
logically consistent by falling within the absolute minimum and maximum
boundaries. Furthermore, it checks that all reported atomic write unit
sizes are valid powers of two, adhering to the strict requirements of
the kernel block layer.

If the underlying storage hardware or filesystem lacks atomic write
support, the test gracefully skips with TCONF.

Signed-off-by: Gaurav Pathak <gaurav.pathak@suse.com>
---
 include/lapi/stat.h                        |  17 +++-
 runtest/syscalls                           |   1 +
 testcases/kernel/syscalls/statx/.gitignore |   1 +
 testcases/kernel/syscalls/statx/statx13.c  | 138 +++++++++++++++++++++++++++++
 4 files changed, 156 insertions(+), 1 deletion(-)

diff --git a/include/lapi/stat.h b/include/lapi/stat.h
index 17b62ea98d6776fafc8713e710093f3c8cf41fc4..859e410ab5cd5560cda49594c77ca87522ae1143 100644
--- a/include/lapi/stat.h
+++ b/include/lapi/stat.h
@@ -93,7 +93,14 @@ struct statx_timestamp {
 	uint64_t	stx_mnt_id; \
 	uint32_t	stx_dio_mem_align; \
 	uint32_t	stx_dio_offset_align; \
-	uint64_t	__spare3[12]; \
+	uint64_t	stx_subvol; \
+	uint32_t	stx_atomic_write_unit_min; \
+	uint32_t	stx_atomic_write_unit_max; \
+	uint32_t	stx_atomic_write_segments_max; \
+	uint32_t	stx_dio_read_offset_align; \
+	uint32_t	stx_atomic_write_unit_max_opt; \
+	uint32_t	__spare2[1]; \
+	uint64_t	__spare3[8]; \
 };
 
 LTP_DEFINE_STATX_STRUCT(statx_fallback);
@@ -197,6 +204,10 @@ static inline int statx(int dirfd, const char *pathname, unsigned int flags,
 # define STATX_DIOALIGN		0x00002000U
 #endif
 
+#ifndef STATX_WRITE_ATOMIC
+#define STATX_WRITE_ATOMIC  0x00010000U
+#endif
+
 #ifndef STATX__RESERVED
 # define STATX__RESERVED	0x80000000U
 #endif
@@ -248,6 +259,10 @@ static inline int statx(int dirfd, const char *pathname, unsigned int flags,
 # define STATX_MNT_ID_UNIQUE  0x00004000U
 #endif
 
+#ifndef STATX_ATTR_WRITE_ATOMIC
+#define STATX_ATTR_WRITE_ATOMIC	0x00400000
+#endif
+
 #define SAFE_FCHMODAT2(dfd, filename, mode, flags) \
 	safe_fchmodat2(__FILE__, __LINE__, (dfd), (filename), (mode), (flags))
 
diff --git a/runtest/syscalls b/runtest/syscalls
index accdf2df9b36a0b839a4611b2c231be8d79f4db7..c0d8eb31ec53805244c629611ce26b3cf414508f 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1947,6 +1947,7 @@ statx09 statx09
 statx10 statx10
 statx11 statx11
 statx12 statx12
+statx13 statx13
 
 membarrier01 membarrier01
 
diff --git a/testcases/kernel/syscalls/statx/.gitignore b/testcases/kernel/syscalls/statx/.gitignore
index f6a423eed84dd67152e6f75809037c92ceb1e060..e601a46a352562fb67836a5f78e19e49323f9bf4 100644
--- a/testcases/kernel/syscalls/statx/.gitignore
+++ b/testcases/kernel/syscalls/statx/.gitignore
@@ -10,3 +10,4 @@
 /statx10
 /statx11
 /statx12
+/statx13
diff --git a/testcases/kernel/syscalls/statx/statx13.c b/testcases/kernel/syscalls/statx/statx13.c
new file mode 100644
index 0000000000000000000000000000000000000000..cf94568e601d9f8cef67d7e2da51027538d6c2a7
--- /dev/null
+++ b/testcases/kernel/syscalls/statx/statx13.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 SUSE LLC <gaurav.pathak@suse.com>
+ */
+
+/*\
+ * This test validates the STATX_WRITE_ATOMIC feature (introduced in Linux 6.13).
+ * It ensures that supported filesystems (xfs and ext4) correctly report their
+ * atomic write limits to user space when queried via statx().
+ *
+ * Minimum Linux version required is v6.12.
+ */
+
+#define _GNU_SOURCE
+#include "tst_test.h"
+#include "lapi/stat.h"
+
+#define MNTPOINT "mnt_point"
+#define TESTFILE MNTPOINT "/testfile"
+#define MODE 0644
+
+static int file_fd = -1;
+static struct ltp_statx *buff;
+
+static void verify_statx(void)
+{
+	TST_EXP_PASS(statx(AT_FDCWD, TESTFILE, 0, STATX_BASIC_STATS | STATX_WRITE_ATOMIC, &buff->buff),
+			"statx(AT_FDCWD, %s, 0, STATX_BASIC_STATS | STATX_WRITE_ATOMIC, &buf)", TESTFILE);
+
+	if (!TST_PASS)
+		return;
+
+	/* Check if the kernel populated the atomic limit struct members */
+	if (!(buff->data.stx_mask & STATX_WRITE_ATOMIC))
+		tst_brk(TCONF, "Kernel may be too old to report atomic limits: STATX_WRITE_ATOMIC missing from stx_mask");
+
+	/* Check if the filesystem supports reporting the atomic write attribute */
+	if (!(buff->data.stx_attributes_mask & STATX_ATTR_WRITE_ATOMIC)) {
+		tst_res(TFAIL, "Inconsistent: stx_mask has STATX_WRITE_ATOMIC but stx_attributes_mask lacks STATX_ATTR_WRITE_ATOMIC");
+		return;
+	}
+
+	/* Check if the file/device actively supports atomic writes */
+	if (!(buff->data.stx_attributes & STATX_ATTR_WRITE_ATOMIC)) {
+		if (buff->data.stx_atomic_write_unit_min || buff->data.stx_atomic_write_unit_max ||
+				buff->data.stx_atomic_write_unit_max_opt || buff->data.stx_atomic_write_segments_max) {
+			tst_res(TFAIL,
+				"Inconsistent: STATX_ATTR_WRITE_ATOMIC unset but limits nonzero (min=%u max=%u opt=%u segs=%u)",
+				buff->data.stx_atomic_write_unit_min, buff->data.stx_atomic_write_unit_max,
+				buff->data.stx_atomic_write_unit_max_opt, buff->data.stx_atomic_write_segments_max);
+			return;
+		}
+
+		tst_brk(TCONF, "STATX_WRITE_ATOMIC unsupported by block dev or filesystem: STATX_ATTR_WRITE_ATOMIC missing from stx_attributes");
+	}
+
+	TST_EXP_EXPR(buff->data.stx_atomic_write_unit_min <= buff->data.stx_atomic_write_unit_max,
+			"stx_atomic_write_unit_min(%u) <= stx_atomic_write_unit_max(%u)",
+			buff->data.stx_atomic_write_unit_min, buff->data.stx_atomic_write_unit_max);
+
+	TST_EXP_EXPR(buff->data.stx_atomic_write_segments_max > 0,
+			"stx_atomic_write_segments_max(%u) > 0", buff->data.stx_atomic_write_segments_max);
+
+	TST_EXP_EXPR(buff->data.stx_atomic_write_unit_min > 0 &&
+			__builtin_popcount(buff->data.stx_atomic_write_unit_min) == 1,
+			"stx_atomic_write_unit_min(%u) is a power of 2",
+			buff->data.stx_atomic_write_unit_min);
+
+	TST_EXP_EXPR(buff->data.stx_atomic_write_unit_max > 0 &&
+			__builtin_popcount(buff->data.stx_atomic_write_unit_max) == 1,
+			"stx_atomic_write_unit_max(%u) is a power of 2",
+			buff->data.stx_atomic_write_unit_max);
+
+	if (buff->data.stx_atomic_write_unit_max_opt == 0) {
+		tst_res(TPASS, "stx_atomic_write_unit_max_opt is 0 (no optimized max reported)");
+	} else {
+		if (buff->data.stx_atomic_write_unit_max_opt > buff->data.stx_atomic_write_unit_max)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) exceeds max (%u)",
+					buff->data.stx_atomic_write_unit_max_opt,
+					buff->data.stx_atomic_write_unit_max);
+
+		else if (buff->data.stx_atomic_write_unit_max_opt < buff->data.stx_atomic_write_unit_min)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is less than min (%u)",
+					buff->data.stx_atomic_write_unit_max_opt,
+					buff->data.stx_atomic_write_unit_min);
+		else
+			tst_res(TPASS, "stx_atomic_write_unit_max_opt (%u) is within valid range [%u, %u]",
+					buff->data.stx_atomic_write_unit_max_opt,
+					buff->data.stx_atomic_write_unit_min,
+					buff->data.stx_atomic_write_unit_max);
+
+		TST_EXP_EXPR(__builtin_popcount(buff->data.stx_atomic_write_unit_max_opt) == 1,
+				"stx_atomic_write_unit_max_opt(%u) is a power of 2",
+				buff->data.stx_atomic_write_unit_max_opt);
+	}
+}
+
+static void setup(void)
+{
+	char *data_buff = NULL;
+	struct stat sb;
+
+	SAFE_STAT(MNTPOINT, &sb);
+
+	data_buff = SAFE_MEMALIGN(sb.st_blksize, sb.st_blksize);
+	memset(data_buff, '@', sb.st_blksize);
+
+	file_fd = SAFE_OPEN(TESTFILE, O_RDWR | O_CREAT | O_DIRECT, MODE);
+	SAFE_WRITE(SAFE_WRITE_ALL, file_fd, data_buff, sb.st_blksize);
+
+	SAFE_CLOSE(file_fd);
+	free(data_buff);
+}
+
+static struct tst_test test = {
+	.test_all = verify_statx,
+	.setup = setup,
+	.min_kver = "6.11",
+	.needs_root = 1,
+	.mntpoint = MNTPOINT,
+	.mount_device = 1,
+	.bufs = (struct tst_buffers []) {
+		{&buff, .size = sizeof(*buff)},
+		{}
+	},
+	.filesystems = (struct tst_fs[]) {
+		{
+			.type = "xfs",
+			.min_kver = "6.12",
+			.mkfs_opts = (const char *const []){"-f", "-bsize=16K", NULL},
+		},
+		{
+			.type = "ext4",
+			.mkfs_opts = (const char *const []){"-O", "bigalloc", "-b", "4096", "-C", "65536", NULL},
+		},
+		{}
+	},
+};

---
base-commit: 07da37cf3dbda876b33ad820168a33b9deacbddb
change-id: 20261016-statx-atomic-write-cf0c15e273f4

Best regards,
-- 
Gaurav Pathak <gaurav.pathak@suse.com>


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 8+ messages in thread
* [LTP] [PATCH v6] syscalls/statx13: Add basic test for STATX_WRITE_ATOMIC on regular file
  2026-10-13 11:27 ` [LTP] " linuxtestproject.agent
@ 2026-10-14  6:20 Gaurav Pathak
  2026-10-14  7:52 ` [LTP] " linuxtestproject.agent
  -1 siblings, 1 reply; 8+ messages in thread
From: Gaurav Pathak @ 2026-10-14  6:20 UTC (permalink / raw)
  To: ltp; +Cc: Gaurav Pathak, linuxtestproject.agent

From: Gaurav Pathak <gaurav.pathak@suse.com>

This patch adds a new test to validate the atomic write limit fields.
The test ensures the filesystem correctly advertises the STATX_ATTR_WRITE_ATOMIC
attribute when queried on a file opened with O_DIRECT. It also verifies that the
reported optimized maximum is logically consistent by falling within the
absolute minimum and maximum boundaries. Furthermore, it checks that all
reported atomic write unit sizes are valid powers of two, adhering to the strict
requirements of the kernel block layer.

If the underlying storage hardware or filesystem lacks atomic write
support, the test gracefully skips with TCONF.

Signed-off-by: Gaurav Pathak <gaurav.pathak@suse.com>
---
 include/lapi/stat.h                        |  17 ++-
 runtest/syscalls                           |   1 +
 testcases/kernel/syscalls/statx/.gitignore |   1 +
 testcases/kernel/syscalls/statx/statx13.c  | 130 +++++++++++++++++++++
 4 files changed, 148 insertions(+), 1 deletion(-)
 create mode 100644 testcases/kernel/syscalls/statx/statx13.c

diff --git a/include/lapi/stat.h b/include/lapi/stat.h
index 17b62ea98..859e410ab 100644
--- a/include/lapi/stat.h
+++ b/include/lapi/stat.h
@@ -93,7 +93,14 @@ struct statx_timestamp {
 	uint64_t	stx_mnt_id; \
 	uint32_t	stx_dio_mem_align; \
 	uint32_t	stx_dio_offset_align; \
-	uint64_t	__spare3[12]; \
+	uint64_t	stx_subvol; \
+	uint32_t	stx_atomic_write_unit_min; \
+	uint32_t	stx_atomic_write_unit_max; \
+	uint32_t	stx_atomic_write_segments_max; \
+	uint32_t	stx_dio_read_offset_align; \
+	uint32_t	stx_atomic_write_unit_max_opt; \
+	uint32_t	__spare2[1]; \
+	uint64_t	__spare3[8]; \
 };
 
 LTP_DEFINE_STATX_STRUCT(statx_fallback);
@@ -197,6 +204,10 @@ static inline int statx(int dirfd, const char *pathname, unsigned int flags,
 # define STATX_DIOALIGN		0x00002000U
 #endif
 
+#ifndef STATX_WRITE_ATOMIC
+#define STATX_WRITE_ATOMIC  0x00010000U
+#endif
+
 #ifndef STATX__RESERVED
 # define STATX__RESERVED	0x80000000U
 #endif
@@ -248,6 +259,10 @@ static inline int statx(int dirfd, const char *pathname, unsigned int flags,
 # define STATX_MNT_ID_UNIQUE  0x00004000U
 #endif
 
+#ifndef STATX_ATTR_WRITE_ATOMIC
+#define STATX_ATTR_WRITE_ATOMIC	0x00400000
+#endif
+
 #define SAFE_FCHMODAT2(dfd, filename, mode, flags) \
 	safe_fchmodat2(__FILE__, __LINE__, (dfd), (filename), (mode), (flags))
 
diff --git a/runtest/syscalls b/runtest/syscalls
index accdf2df9..c0d8eb31e 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1947,6 +1947,7 @@ statx09 statx09
 statx10 statx10
 statx11 statx11
 statx12 statx12
+statx13 statx13
 
 membarrier01 membarrier01
 
diff --git a/testcases/kernel/syscalls/statx/.gitignore b/testcases/kernel/syscalls/statx/.gitignore
index f6a423eed..e601a46a3 100644
--- a/testcases/kernel/syscalls/statx/.gitignore
+++ b/testcases/kernel/syscalls/statx/.gitignore
@@ -10,3 +10,4 @@
 /statx10
 /statx11
 /statx12
+/statx13
diff --git a/testcases/kernel/syscalls/statx/statx13.c b/testcases/kernel/syscalls/statx/statx13.c
new file mode 100644
index 000000000..4b717e27e
--- /dev/null
+++ b/testcases/kernel/syscalls/statx/statx13.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 SUSE LLC <gaurav.pathak@suse.com>
+ */
+
+/*\
+ * This test validates the STATX_WRITE_ATOMIC feature (introduced in Linux 6.13).
+ * It ensures that supported filesystems (xfs and ext4) correctly report their
+ * atomic write limits to user space when queried via statx().
+ *
+ * Minimum Linux version required is v6.11.
+ */
+
+#define _GNU_SOURCE
+#include "tst_test.h"
+#include "lapi/stat.h"
+
+#define MNTPOINT "mnt_point"
+#define TESTFILE MNTPOINT "/testfile"
+#define MODE 0644
+
+static int file_fd = -1;
+static struct ltp_statx *buff;
+
+static void verify_statx(void)
+{
+	TST_EXP_PASS(statx(AT_FDCWD, TESTFILE, 0, STATX_BASIC_STATS | STATX_WRITE_ATOMIC, &buff->buff),
+			"statx(AT_FDCWD, %s, 0, STATX_BASIC_STATS | STATX_WRITE_ATOMIC, &buf)", TESTFILE);
+
+	/* Check if the kernel populated the atomic limit struct members */
+	if (!(buff->data.stx_mask & STATX_WRITE_ATOMIC))
+		tst_brk(TCONF, "Kernel may be too old to report atomic limits: STATX_WRITE_ATOMIC missing from stx_mask");
+
+	/* Check if the filesystem supports reporting the atomic write attribute */
+	if (!(buff->data.stx_attributes_mask & STATX_ATTR_WRITE_ATOMIC)) {
+		tst_res(TFAIL, "Inconsistent: stx_mask has STATX_WRITE_ATOMIC but stx_attributes_mask lacks STATX_ATTR_WRITE_ATOMIC");
+		return;
+	}
+
+	/* Check if the file/device actively supports atomic writes */
+	if (!(buff->data.stx_attributes & STATX_ATTR_WRITE_ATOMIC))
+		tst_brk(TCONF, "STATX_WRITE_ATOMIC unsupported by block dev or filesystem: STATX_ATTR_WRITE_ATOMIC missing from stx_attributes");
+
+	if (buff->data.stx_atomic_write_unit_min <= buff->data.stx_atomic_write_unit_max)
+		tst_res(TPASS, "stx_atomic_write_unit_min(%u) <= stx_atomic_write_unit_max(%u)",
+				buff->data.stx_atomic_write_unit_min, buff->data.stx_atomic_write_unit_max);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_min(%u) > stx_atomic_write_unit_max(%u)",
+				buff->data.stx_atomic_write_unit_min, buff->data.stx_atomic_write_unit_max);
+
+	if (buff->data.stx_atomic_write_unit_min > 0 &&
+			__builtin_popcount(buff->data.stx_atomic_write_unit_min) == 1)
+		tst_res(TPASS, "stx_atomic_write_unit_min(%u) is power of 2",
+				buff->data.stx_atomic_write_unit_min);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_min(%u) is not a power of 2",
+				buff->data.stx_atomic_write_unit_min);
+
+	if (buff->data.stx_atomic_write_unit_max > 0 &&
+			__builtin_popcount(buff->data.stx_atomic_write_unit_max) == 1)
+		tst_res(TPASS, "stx_atomic_write_unit_max(%u) is power of 2",
+				buff->data.stx_atomic_write_unit_max);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_max(%u) is not a power of 2",
+				buff->data.stx_atomic_write_unit_max);
+
+	if (buff->data.stx_atomic_write_unit_max_opt == 0) {
+		tst_res(TPASS, "stx_atomic_write_unit_max_opt is 0 (no optimized max reported)");
+	} else {
+		if (buff->data.stx_atomic_write_unit_max_opt > buff->data.stx_atomic_write_unit_max)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) exceeds max (%u)",
+					buff->data.stx_atomic_write_unit_max_opt,
+					buff->data.stx_atomic_write_unit_max);
+
+		else if (buff->data.stx_atomic_write_unit_max_opt < buff->data.stx_atomic_write_unit_min)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is less than min (%u)",
+					buff->data.stx_atomic_write_unit_max_opt,
+					buff->data.stx_atomic_write_unit_min);
+		else
+			tst_res(TPASS, "stx_atomic_write_unit_max_opt (%u) is within valid range [%u, %u]",
+					buff->data.stx_atomic_write_unit_max_opt,
+					buff->data.stx_atomic_write_unit_min,
+					buff->data.stx_atomic_write_unit_max);
+
+		if (__builtin_popcount(buff->data.stx_atomic_write_unit_max_opt) != 1)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is not a power of 2",
+					buff->data.stx_atomic_write_unit_max_opt);
+	}
+}
+
+static void setup(void)
+{
+	char *data_buff = NULL;
+	struct stat sb;
+
+	SAFE_STAT(MNTPOINT, &sb);
+
+	data_buff = SAFE_MEMALIGN(sb.st_blksize, sb.st_blksize);
+	memset(data_buff, '@', sb.st_blksize);
+
+	file_fd = SAFE_OPEN(TESTFILE, O_RDWR | O_CREAT | O_DIRECT, MODE);
+	SAFE_WRITE(SAFE_WRITE_ALL, file_fd, data_buff, sb.st_blksize);
+
+	SAFE_CLOSE(file_fd);
+	free(data_buff);
+}
+
+static struct tst_test test = {
+	.test_all = verify_statx,
+	.setup = setup,
+	.min_kver = "6.11",
+	.needs_root = 1,
+	.mntpoint = MNTPOINT,
+	.mount_device = 1,
+	.bufs = (struct tst_buffers []) {
+		{&buff, .size = sizeof(*buff)},
+		{}
+	},
+	.filesystems = (struct tst_fs[]) {
+		{
+			.type = "xfs",
+			.mkfs_opts = (const char *const []){"-f", "-bsize=16K", NULL},
+		},
+		{
+			.type = "ext4",
+			.mkfs_opts = (const char *const []){"-O", "bigalloc", "-b", "4096", "-C", "65536", NULL},
+		},
+		{}
+	},
+};
-- 
2.51.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 8+ messages in thread
* [LTP] [PATCH v5] syscalls/statx13: Add basic test for STATX_WRITE_ATOMIC on regular file
@ 2026-10-13  9:07 Gaurav Pathak
  2026-10-13 11:27 ` [LTP] " linuxtestproject.agent
  0 siblings, 1 reply; 8+ messages in thread
From: Gaurav Pathak @ 2026-10-13  9:07 UTC (permalink / raw)
  To: ltp; +Cc: Gaurav Pathak

From: Gaurav Pathak <gaurav.pathak@suse.com>

This patch adds a new test to validate the atomic write limit fields.
The test ensures the filesystem correctly advertises the STATX_ATTR_WRITE_ATOMIC
attribute when queried on a file opened with O_DIRECT. It also verifies that the
reported optimized maximum is logically consistent by falling within the
absolute minimum and maximum boundaries. Furthermore, it checks that all
reported atomic write unit sizes are valid powers of two, adhering to the strict
requirements of the kernel block layer.

If the underlying storage hardware or filesystem lacks atomic write
support, the test gracefully skips with TCONF.

Signed-off-by: Gaurav Pathak <gaurav.pathak@suse.com>
---
 configure.ac                               |   4 +-
 include/lapi/stat.h                        |  17 ++-
 runtest/syscalls                           |   1 +
 testcases/kernel/syscalls/statx/.gitignore |   1 +
 testcases/kernel/syscalls/statx/statx13.c  | 125 +++++++++++++++++++++
 5 files changed, 146 insertions(+), 2 deletions(-)
 create mode 100644 testcases/kernel/syscalls/statx/statx13.c

diff --git a/configure.ac b/configure.ac
index 370c2e74f..8ec6720dd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -180,7 +180,9 @@ AC_CHECK_MEMBERS([struct iocb.aio_rw_flags],,,[#include <linux/aio_abi.h>])
 AC_CHECK_MEMBERS([struct fanotify_event_info_fid.fsid.__val],,,[#include <sys/fanotify.h>])
 AC_CHECK_MEMBERS([struct perf_event_mmap_page.aux_head],,,[#include <linux/perf_event.h>])
 AC_CHECK_MEMBERS([struct sigaction.sa_sigaction],[],[],[#include <signal.h>])
-AC_CHECK_MEMBERS([struct statx.stx_mnt_id, struct statx.stx_dio_mem_align],,,[
+AC_CHECK_MEMBERS([struct statx.stx_mnt_id, struct statx.stx_dio_mem_align, struct statx.stx_atomic_write_unit_max_opt,
+				  struct statx.stx_atomic_write_unit_min, struct statx.stx_atomic_write_unit_max,
+				  struct statx.stx_atomic_write_segments_max],,,[
 #define _GNU_SOURCE
 #include <sys/stat.h>
 ])
diff --git a/include/lapi/stat.h b/include/lapi/stat.h
index 17b62ea98..859e410ab 100644
--- a/include/lapi/stat.h
+++ b/include/lapi/stat.h
@@ -93,7 +93,14 @@ struct statx_timestamp {
 	uint64_t	stx_mnt_id; \
 	uint32_t	stx_dio_mem_align; \
 	uint32_t	stx_dio_offset_align; \
-	uint64_t	__spare3[12]; \
+	uint64_t	stx_subvol; \
+	uint32_t	stx_atomic_write_unit_min; \
+	uint32_t	stx_atomic_write_unit_max; \
+	uint32_t	stx_atomic_write_segments_max; \
+	uint32_t	stx_dio_read_offset_align; \
+	uint32_t	stx_atomic_write_unit_max_opt; \
+	uint32_t	__spare2[1]; \
+	uint64_t	__spare3[8]; \
 };
 
 LTP_DEFINE_STATX_STRUCT(statx_fallback);
@@ -197,6 +204,10 @@ static inline int statx(int dirfd, const char *pathname, unsigned int flags,
 # define STATX_DIOALIGN		0x00002000U
 #endif
 
+#ifndef STATX_WRITE_ATOMIC
+#define STATX_WRITE_ATOMIC  0x00010000U
+#endif
+
 #ifndef STATX__RESERVED
 # define STATX__RESERVED	0x80000000U
 #endif
@@ -248,6 +259,10 @@ static inline int statx(int dirfd, const char *pathname, unsigned int flags,
 # define STATX_MNT_ID_UNIQUE  0x00004000U
 #endif
 
+#ifndef STATX_ATTR_WRITE_ATOMIC
+#define STATX_ATTR_WRITE_ATOMIC	0x00400000
+#endif
+
 #define SAFE_FCHMODAT2(dfd, filename, mode, flags) \
 	safe_fchmodat2(__FILE__, __LINE__, (dfd), (filename), (mode), (flags))
 
diff --git a/runtest/syscalls b/runtest/syscalls
index accdf2df9..c0d8eb31e 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1947,6 +1947,7 @@ statx09 statx09
 statx10 statx10
 statx11 statx11
 statx12 statx12
+statx13 statx13
 
 membarrier01 membarrier01
 
diff --git a/testcases/kernel/syscalls/statx/.gitignore b/testcases/kernel/syscalls/statx/.gitignore
index f6a423eed..e601a46a3 100644
--- a/testcases/kernel/syscalls/statx/.gitignore
+++ b/testcases/kernel/syscalls/statx/.gitignore
@@ -10,3 +10,4 @@
 /statx10
 /statx11
 /statx12
+/statx13
diff --git a/testcases/kernel/syscalls/statx/statx13.c b/testcases/kernel/syscalls/statx/statx13.c
new file mode 100644
index 000000000..01a22a458
--- /dev/null
+++ b/testcases/kernel/syscalls/statx/statx13.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 SUSE LLC <gaurav.pathak@suse.com>
+ */
+
+/*\
+ * This test validates the STATX_WRITE_ATOMIC feature (introduced in Linux 6.13).
+ * It ensures that supported filesystems (xfs and nfs) correctly report their
+ * atomic write limits to user space when queried via statx().
+ *
+ * Minimum Linux version required is v6.11.
+ */
+
+#define _GNU_SOURCE
+#include "tst_test.h"
+#include "lapi/stat.h"
+
+#define MNTPOINT "mnt_point"
+#define TESTFILE MNTPOINT "/testfile"
+#define MODE 0644
+
+static int file_fd = -1;
+
+static void verify_statx(void)
+{
+	struct ltp_statx buff;
+
+	TST_EXP_PASS(statx(AT_FDCWD, TESTFILE, 0, STATX_BASIC_STATS | STATX_WRITE_ATOMIC, &buff.buff),
+			"statx(AT_FDCWD, %s, 0, STATX_BASIC_STATS | STATX_WRITE_ATOMIC, &buf)", TESTFILE);
+
+	/* Check if the kernel populated the atomic limit struct members */
+	if (!(buff.data.stx_mask & STATX_WRITE_ATOMIC))
+		tst_brk(TCONF, "Kernel may be too old to report atomic limits: STATX_WRITE_ATOMIC missing from stx_mask");
+
+	/* Check if the filesystem supports reporting the atomic write attribute */
+	if (!(buff.data.stx_attributes_mask & STATX_ATTR_WRITE_ATOMIC))
+		tst_brk(TCONF, "FS atomic query unsupported: STATX_ATTR_WRITE_ATOMIC missing from stx_attributes_mask.");
+
+	/* Check if the file/device actively supports atomic writes */
+	if (!(buff.data.stx_attributes & STATX_ATTR_WRITE_ATOMIC))
+		tst_brk(TCONF, "STATX_WRITE_ATOMIC unsupported by block dev or filesystem: STATX_ATTR_WRITE_ATOMIC missing from stx_attributes");
+
+	if (buff.data.stx_atomic_write_unit_min <= buff.data.stx_atomic_write_unit_max)
+		tst_res(TPASS, "stx_atomic_write_unit_min(%u) <= stx_atomic_write_unit_max(%u)",
+				buff.data.stx_atomic_write_unit_min, buff.data.stx_atomic_write_unit_max);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_min(%u) > stx_atomic_write_unit_max(%u)",
+				buff.data.stx_atomic_write_unit_min, buff.data.stx_atomic_write_unit_max);
+
+	if (buff.data.stx_atomic_write_unit_min > 0 &&
+			__builtin_popcount(buff.data.stx_atomic_write_unit_min) == 1)
+		tst_res(TPASS, "stx_atomic_write_unit_min(%u) is power of 2",
+				buff.data.stx_atomic_write_unit_min);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_min(%u) is not a power of 2",
+				buff.data.stx_atomic_write_unit_min);
+
+	if (buff.data.stx_atomic_write_unit_max > 0 &&
+			__builtin_popcount(buff.data.stx_atomic_write_unit_max) == 1)
+		tst_res(TPASS, "stx_atomic_write_unit_max(%u) is power of 2",
+				buff.data.stx_atomic_write_unit_max);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_max(%u) is not a power of 2",
+				buff.data.stx_atomic_write_unit_max);
+
+	if (buff.data.stx_atomic_write_unit_max_opt == 0) {
+		tst_res(TPASS, "stx_atomic_write_unit_max_opt is 0 (no optimized max reported)");
+	} else {
+		if (buff.data.stx_atomic_write_unit_max_opt > buff.data.stx_atomic_write_unit_max)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) exceeds max (%u)",
+					buff.data.stx_atomic_write_unit_max_opt,
+					buff.data.stx_atomic_write_unit_max);
+
+		else if (buff.data.stx_atomic_write_unit_max_opt < buff.data.stx_atomic_write_unit_min)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is less than min (%u)",
+					buff.data.stx_atomic_write_unit_max_opt,
+					buff.data.stx_atomic_write_unit_min);
+		else
+			tst_res(TPASS, "stx_atomic_write_unit_max_opt (%u) is within valid range [%u, %u]",
+					buff.data.stx_atomic_write_unit_max_opt,
+					buff.data.stx_atomic_write_unit_min,
+					buff.data.stx_atomic_write_unit_max);
+
+		if (__builtin_popcount(buff.data.stx_atomic_write_unit_max_opt) != 1)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is not a power of 2",
+					buff.data.stx_atomic_write_unit_max_opt);
+	}
+}
+
+static void setup(void)
+{
+	char *data_buff = NULL;
+	struct stat sb;
+
+	SAFE_STAT(MNTPOINT, &sb);
+
+	data_buff = SAFE_MEMALIGN(sb.st_blksize, sb.st_blksize);
+	memset(data_buff, '@', sb.st_blksize);
+
+	file_fd = SAFE_OPEN(TESTFILE, O_RDWR | O_CREAT | O_DIRECT, MODE);
+	SAFE_WRITE(SAFE_WRITE_ALL, file_fd, data_buff, sb.st_blksize);
+
+	SAFE_CLOSE(file_fd);
+	free(data_buff);
+}
+
+static struct tst_test test = {
+	.test_all = verify_statx,
+	.setup = setup,
+	.min_kver = "6.11",
+	.needs_root = 1,
+	.mntpoint = MNTPOINT,
+	.mount_device = 1,
+	.filesystems = (struct tst_fs[]) {
+		{
+			.type = "xfs",
+			.mkfs_opts = (const char *const []){"-f", "-bsize=16K", NULL},
+		},
+		{
+			.type = "ext4",
+			.mkfs_opts = (const char *const []){"-O", "bigalloc", "-b", "4096", "-C", "65536", NULL},
+		},
+		{}
+	},
+};
-- 
2.51.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 8+ messages in thread
* [LTP] [PATCH v3] syscalls/statx13: Add basic test for STATX_WRITE_ATOMIC on regular file
@ 2026-09-18 11:12 Gaurav Pathak
  2026-09-18 12:51 ` [LTP] " linuxtestproject.agent
  0 siblings, 1 reply; 8+ messages in thread
From: Gaurav Pathak @ 2026-09-18 11:12 UTC (permalink / raw)
  To: ltp; +Cc: Gaurav Pathak

From: Gaurav Pathak <gaurav.pathak@suse.com>

This patch adds a new test to validate these atomic write limit fields.
The test ensures the filesystem correctly advertises the STATX_ATTR_WRITE_ATOMIC
attribute when queried on a file opened with O_DIRECT. It also verifies that the
reported optimized maximum is logically consistent by falling within the
absolute minimum and maximum boundaries. Furthermore, it checks that all
reported atomic write unit sizes are valid powers of two, adhering to the strict
requirements of the kernel block layer.

If the underlying storage hardware or filesystem lacks atomic write
support, the test gracefully skips with TCONF.

Closes: #1224

Signed-off-by: Gaurav Pathak <gaurav.pathak@suse.com>
---
 configure.ac                               |   4 +-
 include/lapi/stat.h                        |  17 ++-
 runtest/syscalls                           |   1 +
 testcases/kernel/syscalls/statx/.gitignore |   1 +
 testcases/kernel/syscalls/statx/statx13.c  | 139 +++++++++++++++++++++
 5 files changed, 160 insertions(+), 2 deletions(-)
 create mode 100644 testcases/kernel/syscalls/statx/statx13.c

diff --git a/configure.ac b/configure.ac
index 18bfdb88c..d5a609fa7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,7 +181,9 @@ AC_CHECK_MEMBERS([struct iocb.aio_rw_flags],,,[#include <linux/aio_abi.h>])
 AC_CHECK_MEMBERS([struct fanotify_event_info_fid.fsid.__val],,,[#include <sys/fanotify.h>])
 AC_CHECK_MEMBERS([struct perf_event_mmap_page.aux_head],,,[#include <linux/perf_event.h>])
 AC_CHECK_MEMBERS([struct sigaction.sa_sigaction],[],[],[#include <signal.h>])
-AC_CHECK_MEMBERS([struct statx.stx_mnt_id, struct statx.stx_dio_mem_align],,,[
+AC_CHECK_MEMBERS([struct statx.stx_mnt_id, struct statx.stx_dio_mem_align, struct statx.stx_atomic_write_unit_max_opt,
+				  struct statx.stx_atomic_write_unit_min, struct statx.stx_atomic_write_unit_max,
+				  struct statx.stx_atomic_write_segments_max],,,[
 #define _GNU_SOURCE
 #include <sys/stat.h>
 ])
diff --git a/include/lapi/stat.h b/include/lapi/stat.h
index 17b62ea98..859e410ab 100644
--- a/include/lapi/stat.h
+++ b/include/lapi/stat.h
@@ -93,7 +93,14 @@ struct statx_timestamp {
 	uint64_t	stx_mnt_id; \
 	uint32_t	stx_dio_mem_align; \
 	uint32_t	stx_dio_offset_align; \
-	uint64_t	__spare3[12]; \
+	uint64_t	stx_subvol; \
+	uint32_t	stx_atomic_write_unit_min; \
+	uint32_t	stx_atomic_write_unit_max; \
+	uint32_t	stx_atomic_write_segments_max; \
+	uint32_t	stx_dio_read_offset_align; \
+	uint32_t	stx_atomic_write_unit_max_opt; \
+	uint32_t	__spare2[1]; \
+	uint64_t	__spare3[8]; \
 };
 
 LTP_DEFINE_STATX_STRUCT(statx_fallback);
@@ -197,6 +204,10 @@ static inline int statx(int dirfd, const char *pathname, unsigned int flags,
 # define STATX_DIOALIGN		0x00002000U
 #endif
 
+#ifndef STATX_WRITE_ATOMIC
+#define STATX_WRITE_ATOMIC  0x00010000U
+#endif
+
 #ifndef STATX__RESERVED
 # define STATX__RESERVED	0x80000000U
 #endif
@@ -248,6 +259,10 @@ static inline int statx(int dirfd, const char *pathname, unsigned int flags,
 # define STATX_MNT_ID_UNIQUE  0x00004000U
 #endif
 
+#ifndef STATX_ATTR_WRITE_ATOMIC
+#define STATX_ATTR_WRITE_ATOMIC	0x00400000
+#endif
+
 #define SAFE_FCHMODAT2(dfd, filename, mode, flags) \
 	safe_fchmodat2(__FILE__, __LINE__, (dfd), (filename), (mode), (flags))
 
diff --git a/runtest/syscalls b/runtest/syscalls
index d953f0558..b8c7fbb0f 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1926,6 +1926,7 @@ statx09 statx09
 statx10 statx10
 statx11 statx11
 statx12 statx12
+statx13 statx13
 
 membarrier01 membarrier01
 
diff --git a/testcases/kernel/syscalls/statx/.gitignore b/testcases/kernel/syscalls/statx/.gitignore
index f6a423eed..e601a46a3 100644
--- a/testcases/kernel/syscalls/statx/.gitignore
+++ b/testcases/kernel/syscalls/statx/.gitignore
@@ -10,3 +10,4 @@
 /statx10
 /statx11
 /statx12
+/statx13
diff --git a/testcases/kernel/syscalls/statx/statx13.c b/testcases/kernel/syscalls/statx/statx13.c
new file mode 100644
index 000000000..cc445fa57
--- /dev/null
+++ b/testcases/kernel/syscalls/statx/statx13.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 SUSE LLC <gaurav.pathak@suse.com>
+ */
+
+/*\
+ * This test validates the STATX_WRITE_ATOMIC feature (introduced in Linux 6.13).
+ * It ensures that supported filesystems (xfs and nfs) correctly report their
+ * atomic write limits to user space when queried via statx().
+ *
+ * The test performs the following validations:
+ *
+ * - Creates a test file using O_DIRECT (a prerequisite for atomic writes).
+ * - Calls statx() with the STATX_WRITE_ATOMIC mask to retrieve the limits.
+ * - Verifies that stx_atomic_write_unit_min, stx_atomic_write_unit_max, and
+ *   stx_atomic_write_unit_max_opt are logically consistent (e.g., max_opt is
+ *   within the min and max bounds).
+ * - Ensures all reported atomic write unit sizes are valid powers of two.
+ */
+
+#define _GNU_SOURCE
+#include "tst_test.h"
+#include "lapi/stat.h"
+
+#define MNTPOINT "mnt_point"
+#define TESTFILE MNTPOINT "/testfile"
+#define MODE 0644
+
+#define WRITE_SIZE 4096
+#define ALIGNMENT  4096
+
+static int file_fd = -1;
+
+static void verify_statx(void)
+{
+	struct ltp_statx buff;
+
+	TST_EXP_PASS_SILENT(SAFE_STATX(AT_FDCWD, TESTFILE, 0, STATX_BASIC_STATS | STATX_WRITE_ATOMIC, &buff));
+
+	if (!TST_PASS)
+		return;
+
+	if (!(buff.data.stx_attributes & STATX_ATTR_WRITE_ATOMIC))
+		tst_brk(TCONF, "Filesystem does not support STATX_WRITE_ATOMIC");
+
+#if HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN
+	if (buff.data.stx_atomic_write_unit_min <= buff.data.stx_atomic_write_unit_max)
+		tst_res(TPASS, "stx_atomic_write_unit_min(%u) <= stx_atomic_write_unit_max(%u)",
+				buff.data.stx_atomic_write_unit_min, buff.data.stx_atomic_write_unit_max);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_min(%u) > stx_atomic_write_unit_max(%u)",
+				buff.data.stx_atomic_write_unit_min, buff.data.stx_atomic_write_unit_max);
+
+	if (buff.data.stx_atomic_write_unit_min > 0 &&
+			__builtin_popcount(buff.data.stx_atomic_write_unit_min) == 1)
+		tst_res(TPASS, "stx_atomic_write_unit_min(%u) is power of 2",
+				buff.data.stx_atomic_write_unit_min);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_min(%u) is not a power of 2",
+				buff.data.stx_atomic_write_unit_min);
+
+	if (buff.data.stx_atomic_write_unit_max > 0 &&
+			__builtin_popcount(buff.data.stx_atomic_write_unit_max) == 1)
+		tst_res(TPASS, "stx_atomic_write_unit_max(%u) is power of 2",
+				buff.data.stx_atomic_write_unit_max);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_max(%u) is not a power of 2",
+				buff.data.stx_atomic_write_unit_max);
+#else
+	tst_res(TCONF, "stx_atomic_write_unit_max or stx_atomic_write_unit_min is not defined in struct statx");
+#endif
+
+#if HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT
+	if (buff.data.stx_atomic_write_unit_max_opt == 0) {
+		tst_res(TPASS, "stx_atomic_write_unit_max_opt is 0 (no optimized max reported)");
+	} else {
+		if (buff.data.stx_atomic_write_unit_max_opt > buff.data.stx_atomic_write_unit_max)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) exceeds max (%u)",
+					buff.data.stx_atomic_write_unit_max_opt,
+					buff.data.stx_atomic_write_unit_max);
+
+		else if (buff.data.stx_atomic_write_unit_max_opt < buff.data.stx_atomic_write_unit_min)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is less than min (%u)",
+					buff.data.stx_atomic_write_unit_max_opt,
+					buff.data.stx_atomic_write_unit_min);
+		else
+			tst_res(TPASS, "stx_atomic_write_unit_max_opt (%u) is within valid range [%u, %u]",
+					buff.data.stx_atomic_write_unit_max_opt,
+					buff.data.stx_atomic_write_unit_min,
+					buff.data.stx_atomic_write_unit_max);
+
+		if (__builtin_popcount(buff.data.stx_atomic_write_unit_max_opt) != 1)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is not a power of 2",
+					buff.data.stx_atomic_write_unit_max_opt);
+	}
+#else
+	tst_res(TCONF, "stx_atomic_write_unit_max_opt is not defined in struct statx");
+#endif
+}
+
+static void setup(void)
+{
+	char *data_buff = SAFE_MEMALIGN(ALIGNMENT, WRITE_SIZE);
+
+	umask(0);
+	memset(data_buff, '@', WRITE_SIZE);
+
+	file_fd =  SAFE_OPEN(TESTFILE, O_RDWR | O_CREAT | O_DIRECT, MODE);
+	SAFE_WRITE(SAFE_WRITE_ALL, file_fd, data_buff, WRITE_SIZE);
+
+	free(data_buff);
+}
+
+static void cleanup(void)
+{
+	if (file_fd != -1)
+		SAFE_CLOSE(file_fd);
+}
+
+static struct tst_test test = {
+	.test_all = verify_statx,
+	.setup = setup,
+	.cleanup = cleanup,
+	.min_kver = "6.11",
+	.needs_root = 1,
+	.mntpoint = MNTPOINT,
+	.mount_device = 1,
+	.filesystems = (struct tst_fs[]) {
+		{
+			.type = "xfs",
+			.mkfs_opts = (const char *const []){"-f", "-bsize=16K", NULL},
+		},
+		{
+			.type = "ext4",
+			.mkfs_opts = (const char *const []){"-O", "bigalloc", "-b", "4096", "-C", "65536", NULL},
+		},
+		{}
+	},
+};
-- 
2.51.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 8+ messages in thread
* [LTP] [PATCH] syscalls/statx13: Add basic test for STATX_WRITE_ATOMIC on regular file
  2026-09-17  2:56 [LTP] [PATCH] " Gaurav Pathak
@ 2026-09-17 10:53 Gaurav Pathak
  2026-09-17 11:17 ` [LTP] " linuxtestproject.agent
  0 siblings, 1 reply; 8+ messages in thread
From: Gaurav Pathak @ 2026-09-17 10:53 UTC (permalink / raw)
  To: ltp; +Cc: Gaurav Pathak

From: Gaurav Pathak <gaurav.pathak@suse.com>

This patch adds a new test to validate these atomic write limit fields.
The test ensures the filesystem correctly advertises the STATX_ATTR_WRITE_ATOMIC
attribute when queried on a file opened with O_DIRECT. It also verifies that the
reported optimized maximum is logically consistent by falling within the
absolute minimum and maximum boundaries. Furthermore, it checks that all
reported atomic write unit sizes are valid powers of two, adhering to the strict
requirements of the kernel block layer.

If the underlying storage hardware or filesystem lacks atomic write
support, the test gracefully skips with TCONF.

Fixes: #1224

Signed-off-by: Gaurav Pathak <gaurav.pathak@suse.com>
---
 configure.ac                               |   2 +-
 testcases/kernel/syscalls/statx/.gitignore |   1 +
 testcases/kernel/syscalls/statx/statx13.c  | 130 +++++++++++++++++++++
 3 files changed, 132 insertions(+), 1 deletion(-)
 create mode 100644 testcases/kernel/syscalls/statx/statx13.c

diff --git a/configure.ac b/configure.ac
index 18bfdb88c..72f423c56 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,7 +181,7 @@ AC_CHECK_MEMBERS([struct iocb.aio_rw_flags],,,[#include <linux/aio_abi.h>])
 AC_CHECK_MEMBERS([struct fanotify_event_info_fid.fsid.__val],,,[#include <sys/fanotify.h>])
 AC_CHECK_MEMBERS([struct perf_event_mmap_page.aux_head],,,[#include <linux/perf_event.h>])
 AC_CHECK_MEMBERS([struct sigaction.sa_sigaction],[],[],[#include <signal.h>])
-AC_CHECK_MEMBERS([struct statx.stx_mnt_id, struct statx.stx_dio_mem_align],,,[
+AC_CHECK_MEMBERS([struct statx.stx_mnt_id, struct statx.stx_dio_mem_align, struct statx.stx_atomic_write_unit_max_opt],,,[
 #define _GNU_SOURCE
 #include <sys/stat.h>
 ])
diff --git a/testcases/kernel/syscalls/statx/.gitignore b/testcases/kernel/syscalls/statx/.gitignore
index f6a423eed..e601a46a3 100644
--- a/testcases/kernel/syscalls/statx/.gitignore
+++ b/testcases/kernel/syscalls/statx/.gitignore
@@ -10,3 +10,4 @@
 /statx10
 /statx11
 /statx12
+/statx13
diff --git a/testcases/kernel/syscalls/statx/statx13.c b/testcases/kernel/syscalls/statx/statx13.c
new file mode 100644
index 000000000..ea42a102c
--- /dev/null
+++ b/testcases/kernel/syscalls/statx/statx13.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 SUSE LLC <gaurav.pathak@suse.com>
+ */
+
+/*\
+ * This test validates the STATX_WRITE_ATOMIC feature (introduced in Linux 6.13).
+ * It ensures that supported filesystems (xfs as of now) correctly report their
+ * atomic write limits to user space when queried via statx().
+ *
+ * The test performs the following validations:
+ * - Creates a test file using O_DIRECT (a prerequisite for atomic writes).
+ * - Calls statx() with the STATX_WRITE_ATOMIC mask to retrieve the limits.
+ * - Verifies that stx_atomic_write_unit_min, stx_atomic_write_unit_max, and
+ *    stx_atomic_write_unit_max_opt are logically consistent (e.g., max_opt is
+ *    within the min and max bounds).
+ * - Ensures all reported atomic write unit sizes are valid powers of two.
+ */
+
+#define _GNU_SOURCE
+#include <sys/param.h>
+#include "tst_test.h"
+
+#define MNTPOINT "mnt_point"
+#define TESTFILE MNTPOINT"/testfile"
+#define MODE 0644
+
+#define WRITE_SIZE 4096
+#define ALIGNMENT  4096
+
+static int file_fd = -1;
+
+static void verify_statx(void)
+{
+	struct statx buff;
+
+	TST_EXP_PASS_SILENT(statx(AT_FDCWD, TESTFILE, 0, STATX_BASIC_STATS | STATX_WRITE_ATOMIC, &buff),
+			"statx(AT_FDCWD, %s, 0, STATX_WRITE_ATOMIC, &buf)", TESTFILE);
+
+	if (!(buff.stx_attributes & STATX_ATTR_WRITE_ATOMIC)) {
+		tst_res(TCONF, "Filesystem does not support STATX_WRITE_ATOMIC");
+		return;
+	}
+
+	if (buff.stx_atomic_write_unit_min > 0 &&
+			__builtin_popcount(buff.stx_atomic_write_unit_min) == 1)
+		tst_res(TPASS, "stx_atomic_write_unit_min(%u) is power of 2",
+				buff.stx_atomic_write_unit_min);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_min(%u) is not a power of 2",
+				buff.stx_atomic_write_unit_min);
+
+	if (buff.stx_atomic_write_unit_max > 0 &&
+			__builtin_popcount(buff.stx_atomic_write_unit_max) == 1)
+		tst_res(TPASS, "stx_atomic_write_unit_max(%u) is power of 2",
+				buff.stx_atomic_write_unit_max);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_max(%u) is not a power of 2",
+				buff.stx_atomic_write_unit_max);
+
+#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT
+	if (buff.stx_atomic_write_unit_max_opt == 0) {
+		tst_res(TINFO, "stx_atomic_write_unit_max_opt is 0 (no optimized max reported)");
+	} else {
+		if (buff.stx_atomic_write_unit_max_opt > buff.stx_atomic_write_unit_max)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) exceeds max (%u)",
+					buff.stx_atomic_write_unit_max_opt,
+					buff.stx_atomic_write_unit_max);
+
+		else if (buff.stx_atomic_write_unit_max_opt < buff.stx_atomic_write_unit_min)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is less than min (%u)",
+					buff.stx_atomic_write_unit_max_opt,
+					buff.stx_atomic_write_unit_min);
+		else
+			tst_res(TPASS, "stx_atomic_write_unit_max_opt (%u) is within valid range [%u, %u]",
+					buff.stx_atomic_write_unit_max_opt,
+					buff.stx_atomic_write_unit_min,
+					buff.stx_atomic_write_unit_max);
+
+		if (__builtin_popcount(buff.stx_atomic_write_unit_max_opt) != 1)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is not a power of 2",
+					buff.stx_atomic_write_unit_max_opt);
+	}
+#else
+	tst_res(TCONF, "stx_atomic_write_unit_max_opt is not defined in struct statx");
+#endif
+}
+
+static void setup(void)
+{
+	char *data_buff = SAFE_MEMALIGN(ALIGNMENT, WRITE_SIZE);
+
+	if (strcmp(tst_device->fs_type, "xfs") && strcmp(tst_device->fs_type, "ext4"))
+		tst_brk(TCONF, "This test only supports ext4 and xfs");
+
+	umask(0);
+	memset(data_buff, '@', WRITE_SIZE);
+
+	file_fd =  SAFE_OPEN(TESTFILE, O_RDWR | O_CREAT | O_DIRECT, MODE);
+	SAFE_WRITE(SAFE_WRITE_ALL, file_fd, data_buff, WRITE_SIZE);
+}
+
+static void cleanup(void)
+{
+	if (file_fd > -1)
+		SAFE_CLOSE(file_fd);
+}
+
+static struct tst_test test = {
+	.test_all = verify_statx,
+	.setup = setup,
+	.cleanup = cleanup,
+	.min_kver = "6.13",
+	.needs_root = 1,
+	.needs_device = 1,
+	.needs_tmpdir = 1,
+	.mntpoint = MNTPOINT,
+	.mount_device = 1,
+	.filesystems = (struct tst_fs[]) {
+		{
+			.type = "xfs",
+			.mkfs_opts = (const char *const []){"-f", "-bsize=16K", NULL},
+		},
+		{
+			.type = "ext4",
+			.mkfs_opts = (const char *const []){"-O", "bigalloc", "-b", "4096", "-C", "65536", NULL},
+		},
+		{}
+	},
+};
-- 
2.51.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 8+ messages in thread
* [LTP] [PATCH] syscalls/statx13: Add basic test for STATX_WRITE_ATOMIC on regular file
@ 2026-09-17  2:56 Gaurav Pathak
  2026-09-17 11:15 ` [LTP] " linuxtestproject.agent
  0 siblings, 1 reply; 8+ messages in thread
From: Gaurav Pathak @ 2026-09-17  2:56 UTC (permalink / raw)
  To: ltp; +Cc: Gaurav Pathak

From: Gaurav Pathak <gaurav.pathak@suse.com>

This patch adds a new test to validate these atomic write limit fields.
The test ensures the filesystem correctly advertises the STATX_ATTR_WRITE_ATOMIC
attribute when queried on a file opened with O_DIRECT. It also verifies that the
reported optimized maximum is logically consistent by falling within the
absolute minimum and maximum boundaries. Furthermore, it checks that all
reported atomic write unit sizes are valid powers of two, adhering to the strict
requirements of the kernel block layer.

If the underlying storage hardware or filesystem lacks atomic write
support, the test gracefully skips with TCONF.

Fixes: #1224

Signed-off-by: Gaurav Pathak <gaurav.pathak@suse.com>
---
 configure.ac                               |   2 +-
 testcases/kernel/syscalls/statx/.gitignore |   1 +
 testcases/kernel/syscalls/statx/statx13.c  | 130 +++++++++++++++++++++
 3 files changed, 132 insertions(+), 1 deletion(-)
 create mode 100644 testcases/kernel/syscalls/statx/statx13.c

diff --git a/configure.ac b/configure.ac
index 18bfdb88c..72f423c56 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,7 +181,7 @@ AC_CHECK_MEMBERS([struct iocb.aio_rw_flags],,,[#include <linux/aio_abi.h>])
 AC_CHECK_MEMBERS([struct fanotify_event_info_fid.fsid.__val],,,[#include <sys/fanotify.h>])
 AC_CHECK_MEMBERS([struct perf_event_mmap_page.aux_head],,,[#include <linux/perf_event.h>])
 AC_CHECK_MEMBERS([struct sigaction.sa_sigaction],[],[],[#include <signal.h>])
-AC_CHECK_MEMBERS([struct statx.stx_mnt_id, struct statx.stx_dio_mem_align],,,[
+AC_CHECK_MEMBERS([struct statx.stx_mnt_id, struct statx.stx_dio_mem_align, struct statx.stx_atomic_write_unit_max_opt],,,[
 #define _GNU_SOURCE
 #include <sys/stat.h>
 ])
diff --git a/testcases/kernel/syscalls/statx/.gitignore b/testcases/kernel/syscalls/statx/.gitignore
index f6a423eed..e601a46a3 100644
--- a/testcases/kernel/syscalls/statx/.gitignore
+++ b/testcases/kernel/syscalls/statx/.gitignore
@@ -10,3 +10,4 @@
 /statx10
 /statx11
 /statx12
+/statx13
diff --git a/testcases/kernel/syscalls/statx/statx13.c b/testcases/kernel/syscalls/statx/statx13.c
new file mode 100644
index 000000000..5f73807b4
--- /dev/null
+++ b/testcases/kernel/syscalls/statx/statx13.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 SUSE LLC <gaurav.pathak@suse.com>
+ */
+
+/*\
+ * This test validates the STATX_WRITE_ATOMIC feature (introduced in Linux 6.13).
+ * It ensures that supported filesystems (xfs as of now) correctly report their
+ * atomic write limits to user space when queried via statx().
+ *
+ * The test performs the following validations:
+ * - Creates a test file using O_DIRECT (a prerequisite for atomic writes).
+ * - Calls statx() with the STATX_WRITE_ATOMIC mask to retrieve the limits.
+ * - Verifies that stx_atomic_write_unit_min, stx_atomic_write_unit_max, and
+ *    stx_atomic_write_unit_max_opt are logically consistent (e.g., max_opt is
+ *    within the min and max bounds).
+ * - Ensures all reported atomic write unit sizes are valid powers of two.
+ */
+
+#define _GNU_SOURCE
+#include <sys/param.h>
+#include "tst_test.h"
+
+#define MNTPOINT "mnt_point"
+#define TESTFILE MNTPOINT"/testfile"
+#define MODE 0644
+
+#define WRITE_SIZE 4096
+#define ALIGNMENT  4096
+
+static int file_fd = -1;
+
+static void verify_statx(void)
+{
+	struct statx buff;
+
+	TST_EXP_PASS_SILENT(statx(AT_FDCWD, TESTFILE, 0, STATX_BASIC_STATS | STATX_WRITE_ATOMIC, &buff),
+			"statx(AT_FDCWD, %s, 0, STATX_WRITE_ATOMIC, &buf)", TESTFILE);
+
+	if (!(buff.stx_attributes & STATX_ATTR_WRITE_ATOMIC)) {
+		tst_res(TCONF, "Filesystem does not support STATX_WRITE_ATOMIC");
+		return;
+	}
+
+	if (buff.stx_atomic_write_unit_min > 0 &&
+			__builtin_popcount(buff.stx_atomic_write_unit_min) == 1)
+		tst_res(TPASS, "stx_atomic_write_unit_min(%u) is power of 2",
+				buff.stx_atomic_write_unit_min);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_min(%u) is not a power of 2",
+				buff.stx_atomic_write_unit_min);
+
+	if (buff.stx_atomic_write_unit_max > 0 &&
+			__builtin_popcount(buff.stx_atomic_write_unit_max) == 1)
+		tst_res(TPASS, "stx_atomic_write_unit_max(%u) is power of 2",
+				buff.stx_atomic_write_unit_max);
+	else
+		tst_res(TFAIL, "stx_atomic_write_unit_max(%u) is not a power of 2",
+				buff.stx_atomic_write_unit_max);
+
+#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT
+	if (buff.stx_atomic_write_unit_max_opt == 0) {
+		tst_res(TINFO, "stx_atomic_write_unit_max_opt is 0 (no optimized max reported)");
+	} else {
+		if (buff.stx_atomic_write_unit_max_opt > buff.stx_atomic_write_unit_max)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) exceeds max (%u)",
+					buff.stx_atomic_write_unit_max_opt,
+					buff.stx_atomic_write_unit_max);
+
+		else if (buff.stx_atomic_write_unit_max_opt < buff.stx_atomic_write_unit_min)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is less than min (%u)",
+					buff.stx_atomic_write_unit_max_opt,
+					buff.stx_atomic_write_unit_min);
+		else
+			tst_res(TPASS, "stx_atomic_write_unit_max_opt (%u) is within valid range [%u, %u]",
+					buff.stx_atomic_write_unit_max_opt,
+					buff.stx_atomic_write_unit_min,
+					buff.stx_atomic_write_unit_max);
+
+		if (__builtin_popcount(buff.stx_atomic_write_unit_max_opt) != 1)
+			tst_res(TFAIL, "stx_atomic_write_unit_max_opt (%u) is not a power of 2",
+					buff.stx_atomic_write_unit_max_opt);
+	}
+#else
+    tst_res(TCONF, "stx_atomic_write_unit_max_opt is not defined in struct statx");
+#endif
+}
+
+static void setup(void)
+{
+	char *data_buff = SAFE_MEMALIGN(ALIGNMENT, WRITE_SIZE);
+
+	if (strcmp(tst_device->fs_type, "xfs") && strcmp(tst_device->fs_type, "ext4"))
+		tst_brk(TCONF, "This test only supports ext4 and xfs");
+
+	umask(0);
+	memset(data_buff, '@', WRITE_SIZE);
+
+	file_fd =  SAFE_OPEN(TESTFILE, O_RDWR | O_CREAT | O_DIRECT, MODE);
+	SAFE_WRITE(SAFE_WRITE_ALL, file_fd, data_buff, WRITE_SIZE);
+}
+
+static void cleanup(void)
+{
+	if (file_fd > -1)
+		SAFE_CLOSE(file_fd);
+}
+
+static struct tst_test test = {
+	.test_all = verify_statx,
+	.setup = setup,
+	.cleanup = cleanup,
+	.min_kver = "6.13",
+	.needs_root = 1,
+	.needs_device = 1,
+	.needs_tmpdir = 1,
+	.mntpoint = MNTPOINT,
+	.mount_device = 1,
+	.filesystems = (struct tst_fs[]) {
+		{
+			.type = "xfs",
+			.mkfs_opts = (const char *const []){"-f", "-bsize=16K", NULL},
+		},
+        {
+            .type = "ext4",
+            .mkfs_opts = (const char *const []){"-O", "bigalloc", "-b", "4096", "-C", "65536", NULL},
+        },
+		{}
+	},
+};
-- 
2.51.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-10-16 10:52 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-16  7:13 [LTP] [PATCH v7] syscalls/statx13: Add basic test for STATX_WRITE_ATOMIC on regular file Gaurav Pathak
2026-10-16 10:52 ` [LTP] " linuxtestproject.agent
  -- strict thread matches above, loose matches on Subject: below --
2026-10-14  6:20 [LTP] [PATCH v6] " Gaurav Pathak
2026-10-14  7:52 ` [LTP] " linuxtestproject.agent
2026-10-13  9:07 [LTP] [PATCH v5] " Gaurav Pathak
2026-10-13 11:27 ` [LTP] " linuxtestproject.agent
2026-09-18 11:12 [LTP] [PATCH v3] " Gaurav Pathak
2026-09-18 12:51 ` [LTP] " linuxtestproject.agent
2026-10-05 13:18   ` Andrea Cervesato via ltp
2026-09-17 10:53 [LTP] [PATCH] " Gaurav Pathak
2026-09-17 11:17 ` [LTP] " linuxtestproject.agent
2026-09-17  2:56 [LTP] [PATCH] " Gaurav Pathak
2026-09-17 11:15 ` [LTP] " linuxtestproject.agent

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.