[LTP] [PATCH v4 1/1] syscalls/statx13: Add basic test for STATX_WRITE_ATOMIC on regular file
Andrea Cervesato
andrea.cervesato@suse.com
Mon Oct 12 14:43:28 CEST 2026
Hi Gaurav,
> 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
we don't add Github tags in the git history, please remove it.
>
> 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.
This looks more like [Algorithm] section.
> + */
> +
> +#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
We need to use the right page size when we use O_DIRECT. Please take a look
at the other tests using O_DIRECT, so we avoid defining a default value for it.
> +
> +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));
SAFE_STATX() will fail the syscall, so it shadows TST_EXP_PASS_SILENT().
We need to use raw statx() in these cases, because it's our subject of test,
performing a TST_EXP_PASS() check on it.
We really want to know if statx() fails when STATX_WRITE_ATOMIC is passed.
> +
> + 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
This is not correct. If stx_atomic_write_unit_max is not defined, we should
fallback that into the LTP fallback headers and _before_ even starting the
test, check if statx() supports it. If it doesn't then we can TCONF.
> +
> +#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
Same mistake here.
> +}
> +
> +static void setup(void)
> +{
> + char *data_buff = SAFE_MEMALIGN(ALIGNMENT, WRITE_SIZE);
> +
> + umask(0);
This is not needed.
> + 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);
SAFE_CLOSE() goes here.
> +
> + free(data_buff);
> +}
> +
> +static void cleanup(void)
> +{
> + if (file_fd != -1)
> + SAFE_CLOSE(file_fd);
We can close it right after SAFE_WRITE() because file descriptior is never
in use during the test.
> +}
> +
> +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
--
Andrea Cervesato
SUSE QE Automation Engineer Linux
andrea.cervesato@suse.com
More information about the ltp
mailing list