* [LTP] [PATCH v2 2/3] swapon04: Add test for discard flags
2026-08-28 9:05 [LTP] [PATCH v2 0/3] Increase coverage for swapon syscall Andrea Cervesato
2026-08-28 9:05 ` [LTP] [PATCH v2 1/3] lapi: fallback definitions " Andrea Cervesato
@ 2026-08-28 9:05 ` Andrea Cervesato
2026-08-28 9:37 ` Li Wang
2026-08-28 9:05 ` [LTP] [PATCH v2 3/3] swapon02: Add test cases for invalid swapflags Andrea Cervesato
2 siblings, 1 reply; 7+ messages in thread
From: Andrea Cervesato @ 2026-08-28 9:05 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Verify that swapon(2) discard flags (SWAP_FLAG_DISCARD,
SWAP_FLAG_DISCARD_ONCE, SWAP_FLAG_DISCARD_PAGES) correctly control
swapon-time area discard on block devices supporting discard operations.
Reviewed-by: Li Wang <li.wang@linux.dev>
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/syscalls | 1 +
testcases/kernel/syscalls/swapon/.gitignore | 1 +
testcases/kernel/syscalls/swapon/swapon04.c | 204 ++++++++++++++++++++++++++++
3 files changed, 206 insertions(+)
diff --git a/runtest/syscalls b/runtest/syscalls
index 737c63e31..ca190dd97 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1676,6 +1676,7 @@ swapoff02 swapoff02
swapon01 swapon01
swapon02 swapon02
swapon03 swapon03
+swapon04 swapon04
#Exclusive syscall() for POWER6 machines only
switch01 endian_switch01
diff --git a/testcases/kernel/syscalls/swapon/.gitignore b/testcases/kernel/syscalls/swapon/.gitignore
index 729226b11..db03b81ea 100644
--- a/testcases/kernel/syscalls/swapon/.gitignore
+++ b/testcases/kernel/syscalls/swapon/.gitignore
@@ -1,3 +1,4 @@
/swapon01
/swapon02
/swapon03
+/swapon04
diff --git a/testcases/kernel/syscalls/swapon/swapon04.c b/testcases/kernel/syscalls/swapon/swapon04.c
new file mode 100644
index 000000000..419edbc05
--- /dev/null
+++ b/testcases/kernel/syscalls/swapon/swapon04.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) Linux Test Project, 2026
+ */
+
+/*\
+ * Check that :manpage:`swapon(2)` discard flags control swapon-time area discard:
+ *
+ * - With 0 (no flags), the swap area is not discarded at swapon.
+ * - With SWAP_FLAG_DISCARD, the swap area is discarded at swapon.
+ * - With SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE, the swap area is discarded at swapon.
+ * - With SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_PAGES, swapon-time area discard
+ * is disabled (page-cluster discard only).
+ * - With SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | SWAP_FLAG_DISCARD_PAGES,
+ * SWAP_FLAG_DISCARD_ONCE takes precedence and discards the swap area at swapon.
+ * - With SWAP_FLAG_DISCARD_ONCE or SWAP_FLAG_DISCARD_PAGES alone (without the
+ * SWAP_FLAG_DISCARD master enable bit), discard is not enabled.
+ *
+ * [Algorithm]
+ *
+ * - Create a backing file on the test filesystem and fill it completely.
+ * - Attach the backing file to a loop device supporting discard.
+ * - For each test case, populate the backing file and format it with mkswap.
+ * - Record the allocated block count before swapon().
+ * - Call swapon() with the test case flags.
+ * - Verify whether the allocated block count dropped (discarded) or remained.
+ * - Call swapoff() to reset the swap device state.
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "tst_test.h"
+#include "tse_swap.h"
+#include "lapi/syscalls.h"
+#include "lapi/fallocate.h"
+
+#define MNTPOINT "mntpoint"
+#define BACKING_FILE MNTPOINT "/swap_backing_file"
+#define SWAP_SIZE_MB 16
+
+static char loop_dev[PATH_MAX];
+static int loop_dev_id = -1;
+static int loop_attached;
+static int swap_active;
+static size_t max_header_blocks;
+
+static struct tcase {
+ int flags;
+ int exp_discard;
+ const char *desc;
+} tcases[] = {
+ {
+ .desc = "0 (no flags)",
+ },
+ {
+ .flags = SWAP_FLAG_DISCARD,
+ .exp_discard = 1,
+ .desc = "SWAP_FLAG_DISCARD",
+ },
+ {
+ .flags = SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE,
+ .exp_discard = 1,
+ .desc = "SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE",
+ },
+ {
+ .flags = SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_PAGES,
+ .desc = "SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_PAGES",
+ },
+ {
+ .flags = SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | SWAP_FLAG_DISCARD_PAGES,
+ .exp_discard = 1,
+ .desc = "SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | SWAP_FLAG_DISCARD_PAGES",
+ },
+ {
+ .flags = SWAP_FLAG_DISCARD_ONCE,
+ .desc = "SWAP_FLAG_DISCARD_ONCE (alone without master flag)",
+ },
+ {
+ .flags = SWAP_FLAG_DISCARD_PAGES,
+ .desc = "SWAP_FLAG_DISCARD_PAGES (alone without master flag)",
+ },
+};
+
+static void setup(void)
+{
+ char discard_path[PATH_MAX];
+ unsigned long discard_max_bytes = 0;
+ size_t page_size, blk_size, alloc_units;
+ struct stat st;
+ int fd;
+
+ if (access("/proc/swaps", F_OK))
+ tst_brk(TCONF, "swap is not supported by kernel");
+
+ fd = SAFE_OPEN(BACKING_FILE, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ TEST(fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 4096));
+
+ if (TST_RET != 0) {
+ SAFE_CLOSE(fd);
+
+ if (TST_ERR == EOPNOTSUPP || TST_ERR == ENOSYS) {
+ tst_brk(TCONF, "Filesystem %s does not support FALLOC_FL_PUNCH_HOLE",
+ tst_device->fs_type);
+ }
+
+ tst_brk(TBROK | TERRNO, "fallocate() error");
+ }
+
+ SAFE_FTRUNCATE(fd, SWAP_SIZE_MB * TST_MB);
+ SAFE_CLOSE(fd);
+
+ loop_dev_id = tst_find_free_loopdev(loop_dev, sizeof(loop_dev));
+ if (loop_dev_id < 0)
+ tst_brk(TBROK, "No free loop device found");
+
+ if (tst_attach_device(loop_dev, BACKING_FILE))
+ tst_brk(TBROK, "Failed to attach %s to %s", loop_dev, BACKING_FILE);
+ loop_attached = 1;
+
+ snprintf(discard_path, sizeof(discard_path),
+ "/sys/block/loop%d/queue/discard_max_bytes", loop_dev_id);
+ if (FILE_SCANF(discard_path, "%lu", &discard_max_bytes) != 0 || discard_max_bytes == 0) {
+ tst_brk(TCONF, "Loop device %s does not support discard on %s",
+ loop_dev, tst_device->fs_type);
+ }
+
+ SAFE_STAT(BACKING_FILE, &st);
+
+ page_size = getpagesize();
+ blk_size = st.st_blksize;
+ alloc_units = page_size > blk_size ? page_size : blk_size;
+
+ /* Minimum 512-byte blocks for 1 page + small tolerance for filesystem metadata */
+ max_header_blocks = (alloc_units / 512) * 2;
+}
+
+static void verify_swapon(unsigned int n)
+{
+ struct tcase *tc = &tcases[n];
+ int fd;
+ struct stat st;
+ blkcnt_t blocks_before, blocks_after;
+ const char *const mkswap_argv[] = {"mkswap", loop_dev, NULL};
+
+ tst_res(TINFO, "Testing swapon(%s, %s)", loop_dev, tc->desc);
+
+ tst_fill_file(BACKING_FILE, 'A', TST_MB, SWAP_SIZE_MB);
+
+ fd = SAFE_OPEN(BACKING_FILE, O_RDONLY);
+ SAFE_FSYNC(fd);
+ SAFE_CLOSE(fd);
+
+ tst_cmd(mkswap_argv, "/dev/null", "/dev/null", TST_CMD_TCONF_ON_MISSING);
+
+ SAFE_STAT(BACKING_FILE, &st);
+ blocks_before = st.st_blocks;
+
+ if (!blocks_before)
+ tst_brk(TBROK, "Backing file has 0 allocated blocks");
+
+ TEST(tst_syscall(__NR_swapon, loop_dev, tc->flags));
+ if (TST_RET != 0) {
+ tst_res(TFAIL | TTERRNO, "swapon(%s, %s)", loop_dev, tc->desc);
+ return;
+ }
+ swap_active = 1;
+
+ SAFE_STAT(BACKING_FILE, &st);
+ blocks_after = st.st_blocks;
+
+ if (tc->exp_discard)
+ TST_EXP_LE_LU(blocks_after, max_header_blocks);
+ else
+ TST_EXP_EQ_LI(blocks_after, blocks_before);
+
+ if (tst_syscall(__NR_swapoff, loop_dev) != 0)
+ tst_brk(TBROK | TTERRNO, "swapoff(%s) failed", loop_dev);
+ swap_active = 0;
+}
+
+static void cleanup(void)
+{
+ if (swap_active && tst_syscall(__NR_swapoff, loop_dev) != 0)
+ tst_res(TWARN | TTERRNO, "swapoff(%s) failed", loop_dev);
+ swap_active = 0;
+
+ if (loop_attached)
+ tst_detach_device(loop_dev);
+ loop_attached = 0;
+}
+
+static struct tst_test test = {
+ .needs_root = 1,
+ .mount_device = 1,
+ .mntpoint = MNTPOINT,
+ .all_filesystems = 1,
+ .setup = setup,
+ .cleanup = cleanup,
+ .test = verify_swapon,
+ .tcnt = ARRAY_SIZE(tcases),
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 7+ messages in thread* [LTP] [PATCH v2 3/3] swapon02: Add test cases for invalid swapflags
2026-08-28 9:05 [LTP] [PATCH v2 0/3] Increase coverage for swapon syscall Andrea Cervesato
2026-08-28 9:05 ` [LTP] [PATCH v2 1/3] lapi: fallback definitions " Andrea Cervesato
2026-08-28 9:05 ` [LTP] [PATCH v2 2/3] swapon04: Add test for discard flags Andrea Cervesato
@ 2026-08-28 9:05 ` Andrea Cervesato
2 siblings, 0 replies; 7+ messages in thread
From: Andrea Cervesato @ 2026-08-28 9:05 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Add test cases verifying that swapon(2) returns EINVAL when swapflags
contains undefined or invalid flag bits.
Reviewed-by: Li Wang <li.wang@linux.dev>
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
testcases/kernel/syscalls/swapon/swapon02.c | 58 +++++++++++++++++++++--------
1 file changed, 42 insertions(+), 16 deletions(-)
diff --git a/testcases/kernel/syscalls/swapon/swapon02.c b/testcases/kernel/syscalls/swapon/swapon02.c
index d3e528c94..9d30f8527 100644
--- a/testcases/kernel/syscalls/swapon/swapon02.c
+++ b/testcases/kernel/syscalls/swapon/swapon02.c
@@ -5,10 +5,11 @@
*/
/*\
- * This test case checks whether swapon(2) system call returns:
+ * This test case checks whether :manpage:`swapon(2)` system call returns:
*
* - ENOENT when the path does not exist
* - EINVAL when the path exists but is invalid
+ * - EINVAL when swapflags contains invalid or undefined flag bits
* - EPERM when user is not a superuser
* - EBUSY when the specified path is already being used as a swap area
*/
@@ -20,10 +21,10 @@
#include "tse_swap.h"
#define MNTPOINT "mntpoint"
-#define TEST_FILE MNTPOINT"/testswap"
-#define NOTSWAP_FILE MNTPOINT"/notswap"
-#define SWAP_FILE MNTPOINT"/swapfile"
-#define USED_FILE MNTPOINT"/alreadyused"
+#define TEST_FILE MNTPOINT "/testswap"
+#define NOTSWAP_FILE MNTPOINT "/notswap"
+#define SWAP_FILE MNTPOINT "/swapfile"
+#define USED_FILE MNTPOINT "/alreadyused"
static uid_t nobody_uid;
static int do_swapoff;
@@ -32,11 +33,40 @@ static struct tcase {
char *err_desc;
int exp_errno;
char *path;
+ int flags;
} tcases[] = {
- {"Path does not exist", ENOENT, "./doesnotexist"},
- {"Invalid path", EINVAL, NOTSWAP_FILE},
- {"Permission denied", EPERM, SWAP_FILE},
- {"File already used", EBUSY, USED_FILE},
+ {
+ .err_desc = "Path does not exist",
+ .exp_errno = ENOENT,
+ .path = "./doesnotexist",
+ },
+ {
+ .err_desc = "Invalid path",
+ .exp_errno = EINVAL,
+ .path = NOTSWAP_FILE,
+ },
+ {
+ .err_desc = "Permission denied",
+ .exp_errno = EPERM,
+ .path = SWAP_FILE,
+ },
+ {
+ .err_desc = "File already used",
+ .exp_errno = EBUSY,
+ .path = USED_FILE,
+ },
+ {
+ .err_desc = "Invalid swapflags (undefined flag bits)",
+ .exp_errno = EINVAL,
+ .path = SWAP_FILE,
+ .flags = 0x80000,
+ },
+ {
+ .err_desc = "Invalid swapflags (high bits set)",
+ .exp_errno = EINVAL,
+ .path = SWAP_FILE,
+ .flags = ~0,
+ },
};
static void setup(void)
@@ -66,20 +96,16 @@ static void cleanup(void)
static void verify_swapon(unsigned int i)
{
- struct tcase *tc = tcases + i;
+ struct tcase *tc = &tcases[i];
+
if (tc->exp_errno == EPERM)
SAFE_SETEUID(nobody_uid);
- TST_EXP_FAIL(tst_syscall(__NR_swapon, tc->path, 0), tc->exp_errno,
+ TST_EXP_FAIL(tst_syscall(__NR_swapon, tc->path, tc->flags), tc->exp_errno,
"swapon(2) fail with %s", tc->err_desc);
if (tc->exp_errno == EPERM)
SAFE_SETEUID(0);
-
- if (TST_RET != -1) {
- tst_res(TFAIL, "swapon(2) failed unexpectedly, expected: %s",
- tst_strerrno(tc->exp_errno));
- }
}
static struct tst_test test = {
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 7+ messages in thread