From: Cyril Hrubis <chrubis@suse.cz>
To: ltp@lists.linux.it
Subject: [LTP] [PATCH v4 24/31] testcases: sysfs: Add sys_block_loop01
Date: Thu, 27 Aug 2026 13:21:50 +0200 [thread overview]
Message-ID: <20260827112157.1748734-25-chrubis@suse.cz> (raw)
In-Reply-To: <20260827112157.1748734-1-chrubis@suse.cz>
A test for /sys/block/loop*/* files.
The test attaches loop device with different attributes and checks that
the sysfs files are correctly populated.
Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
runtest/sysfs | 1 +
testcases/kernel/sysfs/block/.gitignore | 1 +
testcases/kernel/sysfs/block/Makefile | 7 +
.../kernel/sysfs/block/sys_block_loop01.c | 320 ++++++++++++++++++
4 files changed, 329 insertions(+)
create mode 100644 testcases/kernel/sysfs/block/.gitignore
create mode 100644 testcases/kernel/sysfs/block/Makefile
create mode 100644 testcases/kernel/sysfs/block/sys_block_loop01.c
diff --git a/runtest/sysfs b/runtest/sysfs
index 290866349..26a229f03 100644
--- a/runtest/sysfs
+++ b/runtest/sysfs
@@ -19,3 +19,4 @@ sys_net01 sys_net01
sys_net02 sys_net02
sys_net03 sys_net03
sys_net04 sys_net04
+sys_block_loop01 sys_block_loop01
diff --git a/testcases/kernel/sysfs/block/.gitignore b/testcases/kernel/sysfs/block/.gitignore
new file mode 100644
index 000000000..c64e99087
--- /dev/null
+++ b/testcases/kernel/sysfs/block/.gitignore
@@ -0,0 +1 @@
+/sys_block_loop01
diff --git a/testcases/kernel/sysfs/block/Makefile b/testcases/kernel/sysfs/block/Makefile
new file mode 100644
index 000000000..66e633f40
--- /dev/null
+++ b/testcases/kernel/sysfs/block/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2026 Cyril Hrubis <chrubis@suse.cz>
+
+top_srcdir ?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/sysfs/block/sys_block_loop01.c b/testcases/kernel/sysfs/block/sys_block_loop01.c
new file mode 100644
index 000000000..a99083a99
--- /dev/null
+++ b/testcases/kernel/sysfs/block/sys_block_loop01.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+/*\
+ * Verifies that the loop device sysfs attributes exported under
+ * /sys/block/loopN/loop/ correctly reflect the state of an attached backing
+ * file, and reset once the device is detached again.
+ *
+ * The test creates a plain file, attaches it to a free loop device and
+ * verifies that:
+ *
+ * - loop/backing_file matches the absolute path of the attached file
+ * - loop/offset and loop/sizelimit are 0 (defaults for a plain attach)
+ * - loop/autoclear, loop/partscan and loop/dio (when present) are booleans
+ * and default to 0
+ * - the device's own size (in 512 byte sectors) matches the backing file
+ * size
+ * - ro is 0 (attached read-write by default)
+ *
+ * After detaching the device it verifies that:
+ *
+ * - the device's size is back to 0
+ * - loop/backing_file no longer exists or reads back empty
+ *
+ * The device is then reconfigured via the LOOP_SET_STATUS64 ioctl (offset,
+ * sizelimit and the partscan flag) and it is verified that:
+ *
+ * - loop/offset and loop/sizelimit match the values that were set
+ * - loop/partscan reflects the requested flag
+ * - the device's size matches sizelimit/512, i.e. it is capped by sizelimit
+ * rather than reflecting the whole backing file
+ *
+ * Note that LO_FLAGS_READ_ONLY is not exercised this way: on an already
+ * attached (read-write) device, LOOP_SET_STATUS64 does not actually flip the
+ * device to read-only, ro stays 0 regardless of the flag passed in. Read-only
+ * mode has to be requested at LOOP_SET_FD time instead, by opening the loop
+ * device (or the backing file) O_RDONLY before issuing the ioctl, which is
+ * exercised separately: the device is attached this way and it is verified
+ * that:
+ *
+ * - ro reads back as 1
+ * - the device genuinely rejects being opened or written to O_RDWR, not just
+ * that the attribute says so
+ *
+ * ro turns out not to reset to 0 merely from detaching: it is a property of
+ * the whole gendisk, recalculated only on the next LOOP_SET_FD, so after
+ * detaching the device is reattached read-write and ro is checked to be 0
+ * then instead.
+ *
+ * Finally, autoclear is exercised for real rather than just checking that
+ * the attribute round-trips: the device is attached again, LO_FLAGS_AUTOCLEAR
+ * is set and the last open file descriptor on the device is closed without
+ * calling LOOP_CLR_FD explicitly. The kernel is expected to detach the
+ * device on its own (asynchronously, hence the test polls for it).
+ *
+ * This needs root to attach a loop device.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tst_test.h"
+#include "tst_device.h"
+#include "tst_sysfs_assert.h"
+#include "lapi/loop.h"
+
+#define BACKING_FILE "sys_block_loop01.img"
+#define FILE_SIZE_MB 4
+#define RECONF_OFFSET 4096
+#define RECONF_SIZELIMIT (1024 * 1024)
+
+static char dev_path[64];
+static char loop_dir[64];
+static char abs_backing_file[PATH_MAX + sizeof(BACKING_FILE)];
+static int dev_num;
+static int attached;
+
+static void setup(void)
+{
+ char cwd[PATH_MAX];
+
+ dev_num = tst_find_free_loopdev(dev_path, sizeof(dev_path));
+ if (dev_num < 0)
+ tst_brk(TBROK, "Failed to find free loop device");
+
+ snprintf(loop_dir, sizeof(loop_dir), "/sys/block/loop%d", dev_num);
+
+ tst_prealloc_file(BACKING_FILE, 1024 * 1024, FILE_SIZE_MB);
+
+ SAFE_GETCWD(cwd, sizeof(cwd));
+ snprintf(abs_backing_file, sizeof(abs_backing_file), "%s/%s", cwd,
+ BACKING_FILE);
+}
+
+static void check_backing_file(void)
+{
+ char actual[PATH_MAX] = "";
+
+ TST_SYSFS_READ_STR(actual, sizeof(actual), "%s/loop/backing_file",
+ loop_dir);
+
+ TST_EXP_EQ_STR(actual, abs_backing_file);
+}
+
+static void check_attached(void)
+{
+ check_backing_file();
+
+ TST_SYSFS_EXP_EQ_LI(0, "%s/loop/offset", loop_dir);
+ TST_SYSFS_EXP_EQ_LI(0, "%s/loop/sizelimit", loop_dir);
+
+ TST_SYSFS_ASSERT_BOOL("%s/loop/autoclear", loop_dir);
+ TST_SYSFS_EXP_EQ_LI(0, "%s/loop/autoclear", loop_dir);
+
+ TST_SYSFS_ASSERT_BOOL("%s/loop/partscan", loop_dir);
+ TST_SYSFS_EXP_EQ_LI(0, "%s/loop/partscan", loop_dir);
+
+ if (tst_sysfs_exists("%s/loop/dio", loop_dir)) {
+ TST_SYSFS_ASSERT_BOOL("%s/loop/dio", loop_dir);
+ TST_SYSFS_EXP_EQ_LI(0, "%s/loop/dio", loop_dir);
+ } else {
+ tst_res(TCONF, "%s/loop/dio does not exist", loop_dir);
+ }
+
+ TST_SYSFS_ASSERT_BOOL("/sys/block/loop%d/ro", dev_num);
+ TST_SYSFS_EXP_EQ_LI(0, "/sys/block/loop%d/ro", dev_num);
+
+ TST_SYSFS_EXP_EQ_LI((long)FILE_SIZE_MB * 1024 * 1024 / 512,
+ "/sys/block/loop%d/size", dev_num);
+}
+
+static void check_detached(void)
+{
+ TST_SYSFS_EXP_EQ_LI(0, "/sys/block/loop%d/size", dev_num);
+
+ if (!tst_sysfs_exists("%s/loop/backing_file", loop_dir)) {
+ tst_res(TPASS, "backing_file no longer exists after detach");
+ return;
+ }
+
+ char actual[PATH_MAX] = "";
+
+ TST_SYSFS_READ_STR(actual, sizeof(actual), "%s/loop/backing_file",
+ loop_dir);
+
+ if (actual[0] == '\0')
+ tst_res(TPASS, "backing_file is empty after detach");
+ else
+ tst_res(TFAIL, "backing_file = '%s' after detach, expected empty",
+ actual);
+}
+
+static void check_reconfigured(void)
+{
+ struct loop_info64 info = { 0 };
+ int fd;
+
+ fd = SAFE_OPEN(dev_path, O_RDWR);
+
+ info.lo_offset = RECONF_OFFSET;
+ info.lo_sizelimit = RECONF_SIZELIMIT;
+ info.lo_flags = LO_FLAGS_PARTSCAN;
+
+ SAFE_IOCTL(fd, LOOP_SET_STATUS64, &info);
+ SAFE_CLOSE(fd);
+
+ tst_res(TINFO,
+ "Reconfigured via LOOP_SET_STATUS64: offset=%d sizelimit=%d partscan",
+ RECONF_OFFSET, RECONF_SIZELIMIT);
+
+ TST_SYSFS_EXP_EQ_LI(RECONF_OFFSET, "%s/loop/offset", loop_dir);
+ TST_SYSFS_EXP_EQ_LI(RECONF_SIZELIMIT, "%s/loop/sizelimit", loop_dir);
+ TST_SYSFS_EXP_EQ_LI(1, "%s/loop/partscan", loop_dir);
+ TST_SYSFS_EXP_EQ_LI(RECONF_SIZELIMIT / 512, "/sys/block/loop%d/size",
+ dev_num);
+}
+
+static void check_readonly_attach(void)
+{
+ int dev_fd, file_fd;
+
+ tst_res(TINFO,
+ "Checking a read-only attach (LOOP_SET_FD with an O_RDONLY device fd)");
+
+ dev_fd = SAFE_OPEN(dev_path, O_RDWR);
+ file_fd = SAFE_OPEN(BACKING_FILE, O_RDONLY);
+
+ SAFE_IOCTL(dev_fd, LOOP_SET_FD, file_fd);
+ SAFE_CLOSE(file_fd);
+ SAFE_CLOSE(dev_fd);
+ attached = 1;
+
+ TST_SYSFS_EXP_EQ_LI(1, "/sys/block/loop%d/ro", dev_num);
+
+ dev_fd = open(dev_path, O_RDWR);
+
+ if (dev_fd < 0) {
+ tst_res(TPASS,
+ "open(%s, O_RDWR) failed as expected on a read-only device: %s",
+ dev_path, tst_strerrno(errno));
+ } else {
+ char buf[512] = { 0 };
+ ssize_t written = write(dev_fd, buf, sizeof(buf));
+
+ if (written < 0) {
+ tst_res(TPASS,
+ "write() to a read-only loop device failed as expected: %s",
+ tst_strerrno(errno));
+ } else {
+ tst_res(TFAIL,
+ "write() to a read-only loop device unexpectedly succeeded (%zd bytes)",
+ written);
+ }
+
+ SAFE_CLOSE(dev_fd);
+ }
+
+ if (tst_detach_device(dev_path))
+ tst_brk(TBROK, "Failed to detach %s", dev_path);
+ attached = 0;
+
+ /*
+ * ro does not reset to 0 merely from detaching, it is a property of
+ * the whole gendisk that is only recalculated on the next LOOP_SET_FD,
+ * so the device is reattached read-write here to verify that.
+ */
+ if (tst_attach_device(dev_path, BACKING_FILE))
+ tst_brk(TBROK, "Failed to attach %s to %s", BACKING_FILE,
+ dev_path);
+ attached = 1;
+
+ TST_SYSFS_EXP_EQ_LI(0, "/sys/block/loop%d/ro", dev_num);
+
+ if (tst_detach_device(dev_path))
+ tst_brk(TBROK, "Failed to detach %s", dev_path);
+ attached = 0;
+}
+
+/* Reads back /sys/block/loopN/size, used to poll for autoclear below. */
+static long read_loop_size(void)
+{
+ return TST_SYSFS_READ_LI("/sys/block/loop%d/size", dev_num);
+}
+
+static void check_autoclear_behavior(void)
+{
+ struct loop_info64 info = { 0 };
+ long size;
+ int fd;
+
+ tst_res(TINFO,
+ "Checking that autoclear detaches the device on last close");
+
+ if (tst_attach_device(dev_path, BACKING_FILE)) {
+ tst_res(TFAIL, "Failed to attach %s to %s for autoclear check",
+ BACKING_FILE, dev_path);
+ return;
+ }
+ attached = 1;
+
+ fd = SAFE_OPEN(dev_path, O_RDWR);
+ info.lo_flags = LO_FLAGS_AUTOCLEAR;
+ SAFE_IOCTL(fd, LOOP_SET_STATUS64, &info);
+ SAFE_CLOSE(fd);
+
+ /* autoclear runs asynchronously off a kernel workqueue, poll for it */
+ size = TST_RETRY_FN_EXP_BACKOFF(read_loop_size(), TST_RETVAL_EQ0, 5);
+
+ if (size == 0) {
+ tst_res(TPASS, "Device auto-detached");
+ attached = 0;
+ } else {
+ tst_res(TFAIL, "Device did not auto-detach in time");
+ tst_detach_device(dev_path);
+ attached = 0;
+ }
+}
+
+static void run(void)
+{
+ if (tst_attach_device(dev_path, BACKING_FILE))
+ tst_brk(TBROK, "Failed to attach %s to %s", BACKING_FILE,
+ dev_path);
+ attached = 1;
+
+ check_attached();
+ check_reconfigured();
+
+ if (tst_detach_device(dev_path))
+ tst_brk(TBROK, "Failed to detach %s", dev_path);
+ attached = 0;
+
+ check_detached();
+
+ check_readonly_attach();
+ check_autoclear_behavior();
+}
+
+static void cleanup(void)
+{
+ if (attached)
+ tst_detach_device(dev_path);
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .needs_tmpdir = 1,
+ .needs_kconfigs = (const char *const[]){
+ "CONFIG_BLK_DEV_LOOP",
+ NULL
+ },
+};
--
2.54.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
next prev parent reply other threads:[~2026-08-27 11:30 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-27 11:21 [LTP] [PATCH v3 00/31] Add sysfs sanity tests Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 01/31] lib: Add tst_sysfs_assert Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 02/31] testcases: sysfs: Add sys_power01 Cyril Hrubis
2026-08-27 14:04 ` [LTP] lib: Add tst_sysfs_assert linuxtestproject.agent
2026-08-27 11:21 ` [LTP] [PATCH v4 03/31] testcases: sysfs: Add sys_kernel01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 04/31] testcases: sysfs: Add sys_clocksource01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 05/31] testcases: sysfs: Add sys_node01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 06/31] testcases: sysfs: Add sys_cpu_topology01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 07/31] testcases: sysfs: Add sys_cpu_topology02 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 08/31] testcases: sysfs: Add sys_cpu_vulnerabilities01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 09/31] testcases: sysfs: Add sys_cpu_smt01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 10/31] testcases: sysfs: Add sys_cpu_cache01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 11/31] testcases: sysfs: Add sys_clockevents01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 12/31] testcases: sysfs: Add sys_ata01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 13/31] testcases: sysfs: Add sys_bdi01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 14/31] testcases: sysfs: sys_hwmon01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 15/31] testcases: sysfs: sys_leds01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 16/31] testcases: sysfs: Add sys_wakeup01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 17/31] testcases: sysfs: Add sys_rtc01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 18/31] testcases: sysfs: Add sys_thermal01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 19/31] tst_netdevice: Add two more helper macros Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 20/31] testcases: sysfs: Add sys_net01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 21/31] testcases: sysfs: Add sys_net02 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 22/31] testcases: sysfs: Add sys_net03 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 23/31] testcases: sysfs: Add sys_net04 Cyril Hrubis
2026-08-27 11:21 ` Cyril Hrubis [this message]
2026-08-27 11:21 ` [LTP] [PATCH v4 25/31] testcases: sysfs: Add sys_block_queue01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 26/31] testcases: sysfs: Add sys_block_size01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 27/31] testcases: sysfs: Add sys_hugepages01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 28/31] testcases: sysfs: Add sys_hugepages02 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 29/31] testcases: sysfs: Add sys_ksm01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 30/31] testcases: sysfs: Add sys_mm_swap01 Cyril Hrubis
2026-08-27 11:21 ` [LTP] [PATCH v4 31/31] testcases: sysfs: Add sys_thp01 Cyril Hrubis
2026-08-28 8:32 ` [LTP] [PATCH v3 00/31] Add sysfs sanity tests Li Wang
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=20260827112157.1748734-25-chrubis@suse.cz \
--to=chrubis@suse.cz \
--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.