All of lore.kernel.org
 help / color / mirror / Atom feed
From: Cyril Hrubis <chrubis@suse.cz>
To: Andrea Cervesato <andrea.cervesato@suse.de>
Cc: Linux Test Project <ltp@lists.linux.it>
Subject: Re: [LTP] [PATCH v3 2/3] swapon04: Add test for discard flags
Date: Thu, 3 Sep 2026 13:18:16 +0200	[thread overview]
Message-ID: <aplXeHUVD9HrVU_n@yuki.lan> (raw)
In-Reply-To: <20260828-swapon_discard_coverage-v3-2-136924a1a730@suse.com>

Hi!
> +// 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");

It's probably better to have CONFIG_SWAP=y in .needs_kconfigs instead.

> +	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");
> +	}

What is this part for? We puch hole into an empty file?

> +	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);

We need .needs_cmds = {"mkswap", NULL} for this.

> +	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));

Any reason we are using raw syscall instead of libc swapon() here?

> +	if (TST_RET != 0) {
> +		tst_res(TFAIL | TTERRNO, "swapon(%s, %s)", loop_dev, tc->desc);
> +		return;
> +	}

TST_EXP_PASS() ?

> +	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);

Here as well why the raw syscall?

> +	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;

It's not needed to clear the flags here, the process does exit(0) right
after cleanup() is called.

> +}
> +
> +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

-- 
Cyril Hrubis
chrubis@suse.cz

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

  reply	other threads:[~2026-09-03 11:18 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-28 11:10 [LTP] [PATCH v3 0/3] Increase coverage for swapon syscall Andrea Cervesato
2026-08-28 11:10 ` [LTP] [PATCH v3 1/3] tse_swap: Add fallback definitions for swapon Andrea Cervesato
2026-08-28 11:30   ` [LTP] " linuxtestproject.agent
2026-08-28 11:10 ` [LTP] [PATCH v3 2/3] swapon04: Add test for discard flags Andrea Cervesato
2026-09-03 11:18   ` Cyril Hrubis [this message]
2026-08-28 11:10 ` [LTP] [PATCH v3 3/3] swapon02: Add test cases for invalid swapflags Andrea Cervesato
2026-09-03 11:32   ` Cyril Hrubis

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=aplXeHUVD9HrVU_n@yuki.lan \
    --to=chrubis@suse.cz \
    --cc=andrea.cervesato@suse.de \
    --cc=ltp@lists.linux.it \
    /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.