* [PATCH v2 2/2] libmount: support explicit loop direct I/O requests
2026-10-04 2:41 [PATCH v2 0/2] libmount: preserve reused loop devices and request direct I/O Tianyi Chen
@ 2026-09-21 7:34 ` Tianyi Chen
2026-10-02 12:17 ` [PATCH v2 1/2] libmount: keep reused loop devices attached on mount failure Tianyi Chen
1 sibling, 0 replies; 3+ messages in thread
From: Tianyi Chen @ 2026-09-21 7:34 UTC (permalink / raw)
To: kzak; +Cc: util-linux
Allow X-loopdev.direct-io=on|off to select the direct I/O mode when
mounting a file through a loop device. Pass the flag to the existing
loop setup path and require a matching mode when reusing a device.
Omitting the option preserves the current behavior.
Add an explicit diagnostic for direct I/O mode conflicts and document
that the option is a request whose result should be checked with
losetup. Parse the boolean with ul_strtobool and cover the option with
the standard loop-device test cleanup helpers.
Fixes: https://github.com/util-linux/util-linux/issues/3464
Signed-off-by: Tianyi Chen <hi@tychen.cc>
---
libmount/src/hook_loopdev.c | 34 +++++++++++-
sys-utils/mount.8.adoc | 13 ++++-
tests/expected/mount/loop-direct-io | 6 +++
tests/ts/mount/loop-direct-io | 84 +++++++++++++++++++++++++++++
4 files changed, 134 insertions(+), 3 deletions(-)
create mode 100644 tests/expected/mount/loop-direct-io
create mode 100755 tests/ts/mount/loop-direct-io
diff --git a/libmount/src/hook_loopdev.c b/libmount/src/hook_loopdev.c
index 809d64d8..981ad6c1 100644
--- a/libmount/src/hook_loopdev.c
+++ b/libmount/src/hook_loopdev.c
@@ -139,7 +139,7 @@ static int setup_loopdev(struct libmnt_context *cxt,
{
const char *backing_file, *loopdev = NULL;
struct loopdev_cxt lc;
- int rc = 0, lo_flags = 0;
+ int rc = 0, lo_flags = 0, direct_io = -1;
uint64_t offset = 0, sizelimit = 0;
bool reuse = FALSE;
struct libmnt_opt *opt, *loopopt = NULL;
@@ -155,6 +155,23 @@ static int setup_loopdev(struct libmnt_context *cxt,
lo_flags |= LO_FLAGS_READ_ONLY;
}
+ /*
+ * X-loopdev.direct-io=
+ */
+ opt = mnt_optlist_get_named(ol, "X-loopdev.direct-io", cxt->map_userspace);
+ if (opt) {
+ const char *value = mnt_opt_get_value(opt);
+ bool enabled;
+
+ if (ul_strtobool(value, &enabled) != 0) {
+ rc = -MNT_ERR_MOUNTOPT;
+ goto done_no_deinit;
+ }
+ direct_io = enabled;
+ if (enabled)
+ lo_flags |= LO_FLAGS_DIRECT_IO;
+ }
+
/*
* loop=
*/
@@ -258,6 +275,18 @@ static int setup_loopdev(struct libmnt_context *cxt,
goto done;
}
+ if (direct_io != -1 && !!loopcxt_is_dio(&lc) != direct_io) {
+ const char *dev = loopcxt_get_device(&lc);
+
+ DBG_OBJ(LOOP, cxt, ul_debug("%s has incompatible direct I/O mode",
+ dev));
+ /* TRANSLATORS: Do not translate "e ". It is a message classifier. */
+ mnt_context_sprintf_mesg(cxt,
+ _("e loop device %s has incompatible direct I/O mode"), dev);
+ rc = -MNT_ERR_LOOPOVERLAP;
+ goto done;
+ }
+
/* This is no more supported, but check to be safe. */
if (loopcxt_get_encrypt_type(&lc, &lc_encrypt_type) == 0
&& lc_encrypt_type != LO_CRYPT_NONE) {
@@ -454,7 +483,8 @@ static int is_loopdev_required(struct libmnt_context *cxt, struct libmnt_optlist
return 0;
/* loop= (sizelimit= or offset=) explicitly specified */
- if (flags & (MNT_MS_LOOP | MNT_MS_OFFSET | MNT_MS_SIZELIMIT)) {
+ if (flags & (MNT_MS_LOOP | MNT_MS_OFFSET | MNT_MS_SIZELIMIT)
+ || mnt_optlist_get_named(ol, "X-loopdev.direct-io", cxt->map_userspace)) {
DBG_OBJ(LOOP, cxt, ul_debug("loopdev specific options detected"));
return 1;
}
diff --git a/sys-utils/mount.8.adoc b/sys-utils/mount.8.adoc
index 922ecda7..6261bac3 100644
--- a/sys-utils/mount.8.adoc
+++ b/sys-utils/mount.8.adoc
@@ -796,6 +796,17 @@ ____
mount -t auto -o'X-mount.auto-fstypes="noext2,ext3"' /dev/sdc1 /mnt/test
____
+*X-loopdev.direct-io*=*on*|*off*::
+Request enabling or disabling direct I/O when setting up a loop device. The
+kernel may fall back to buffered I/O, for example when the backing file
+offset is unaligned or the block size is too small. Direct I/O is a request,
+not a guarantee; check the effective mode with *losetup --list -O NAME,DIO*. If a loop device already exists for
+the same file, offset and size limit, its direct I/O mode must match; otherwise
+the mount fails without changing the existing device. Without this option, new
+loop devices use buffered I/O and existing devices can be reused in either
+mode. This option does not change an existing mount or apply when no loop
+device is needed. See also *losetup*(8) *--direct-io*.
+
*X-mount.mkdir*[**=**_mode_]::
Allow to make a target directory (mountpoint) if it does not exist yet. The optional argument _mode_ specifies the filesystem access mode used for *mkdir*(2) in octal notation. The default mode is 0755. This functionality is supported only for root users or when *mount* is executed without suid permissions. The option is also supported as *x-mount.mkdir*, but this notation is deprecated since v2.30. See also *--mkdir* command line option.
@@ -1708,7 +1719,7 @@ ____
*mount -t ext4 /tmp/disk.img /mnt*
____
-This type of mount knows about three options, namely *loop*, *offset* and *sizelimit*, that are really options to *losetup*(8). (These options can be used in addition to those specific to the filesystem type.)
+Loop device setup accepts *loop*, *offset*, *sizelimit* and *X-loopdev.direct-io*, corresponding to options of *losetup*(8). (These options can be used in addition to those specific to the filesystem type.)
Since Linux 2.6.25 auto-destruction of loop devices is supported, meaning that any loop device allocated by *mount* will be freed by *umount* independently of _/etc/mtab_.
diff --git a/tests/expected/mount/loop-direct-io b/tests/expected/mount/loop-direct-io
new file mode 100644
index 00000000..990eda84
--- /dev/null
+++ b/tests/expected/mount/loop-direct-io
@@ -0,0 +1,6 @@
+invalid options: OK
+new device (on): OK
+new device (off): OK
+new device (default): OK
+reuse and failed mount (on): OK
+reuse and failed mount (off): OK
diff --git a/tests/ts/mount/loop-direct-io b/tests/ts/mount/loop-direct-io
new file mode 100755
index 00000000..c5cf9723
--- /dev/null
+++ b/tests/ts/mount/loop-direct-io
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="loop direct I/O"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_MOUNT"
+ts_check_test_command "$TS_CMD_UMOUNT"
+ts_skip_nonroot
+ts_check_losetup
+ts_check_prog "mkfs.ext2"
+
+IMAGE=$(ts_image_init)
+
+mkfs.ext2 -F "$IMAGE" &> /dev/null || ts_die "Cannot make filesystem"
+mkdir -p "$TS_MOUNTPOINT"
+
+for option in X-loopdev.direct-io X-loopdev.direct-io= X-loopdev.direct-io=invalid; do
+ $TS_CMD_MOUNT -o "$option" "$IMAGE" "$TS_MOUNTPOINT" &> /dev/null &&
+ ts_die "Accepted invalid option: $option"
+done
+[ -z "$($TS_CMD_LOSETUP -j "$IMAGE")" ] || ts_die "Invalid option left a loop device"
+ts_log "invalid options: OK"
+
+DEVICE=$($TS_CMD_LOSETUP --direct-io=on --find --show "$IMAGE" 2>> "$TS_ERRLOG") ||
+ ts_skip "backing filesystem does not support direct I/O"
+ts_register_loop_device "$DEVICE"
+[ "$(cat "/sys/class/block/${DEVICE##*/}/loop/dio")" = 1 ] ||
+ ts_skip "loop direct I/O not supported"
+ts_cleanup_on_exit
+
+for mode in on off default; do
+ options=loop
+ expected=0
+ [ "$mode" = default ] || options+=",X-loopdev.direct-io=$mode"
+ [ "$mode" != on ] || expected=1
+ $TS_CMD_MOUNT -o "$options" "$IMAGE" "$TS_MOUNTPOINT" >> "$TS_ERRLOG" 2>&1 ||
+ ts_die "Cannot mount with mode $mode"
+ DEVICE=$($TS_CMD_LOSETUP -j "$IMAGE" | cut -d: -f1)
+ ts_register_loop_device "$DEVICE"
+ [ "$(cat "/sys/class/block/${DEVICE##*/}/loop/dio")" = "$expected" ] ||
+ ts_die "Wrong direct I/O mode: $mode"
+ $TS_CMD_UMOUNT "$TS_MOUNTPOINT" || ts_die "Cannot unmount"
+ ts_cleanup_on_exit
+ $TS_CMD_MOUNT -t ext2 -o "$options,invalid-fs-option" "$IMAGE" "$TS_MOUNTPOINT" &> /dev/null &&
+ ts_die "Accepted invalid filesystem option on new device"
+ [ -z "$($TS_CMD_LOSETUP -j "$IMAGE")" ] || ts_die "Failed mount left a new loop device"
+ ts_log "new device ($mode): OK"
+done
+
+for mode in on off; do
+ DEVICE=$($TS_CMD_LOSETUP --direct-io="$mode" --find --show "$IMAGE") ||
+ ts_die "Cannot set up device"
+ ts_register_loop_device "$DEVICE"
+ alias=yes
+ [ "$mode" != off ] || alias=no
+ for options in "loop,X-loopdev.direct-io=$mode" "X-loopdev.direct-io=$alias" loop; do
+ $TS_CMD_MOUNT -o "$options" "$IMAGE" "$TS_MOUNTPOINT" >> "$TS_ERRLOG" 2>&1 ||
+ ts_die "Cannot reuse device: $options"
+ $TS_CMD_UMOUNT "$TS_MOUNTPOINT" || ts_die "Cannot unmount reused device"
+ done
+ other=on
+ [ "$mode" != on ] || other=off
+ $TS_CMD_MOUNT -o "X-loopdev.direct-io=$other" "$IMAGE" "$TS_MOUNTPOINT" > "$TS_OUTDIR/mode-error" 2>&1 &&
+ ts_die "Reused device with incompatible direct I/O mode"
+ grep -q "incompatible direct I/O mode" "$TS_OUTDIR/mode-error" ||
+ ts_die "Missing direct I/O diagnostic"
+ $TS_CMD_MOUNT -t ext2 -o "X-loopdev.direct-io=$mode,invalid-fs-option" \
+ "$IMAGE" "$TS_MOUNTPOINT" &> /dev/null &&
+ ts_die "Accepted invalid filesystem option"
+ [ "$($TS_CMD_LOSETUP -j "$IMAGE" | cut -d: -f1)" = "$DEVICE" ] ||
+ ts_die "Failed mount detached or duplicated reused device"
+ expected=0
+ [ "$mode" != on ] || expected=1
+ [ "$(cat "/sys/class/block/${DEVICE##*/}/loop/dio")" = "$expected" ] ||
+ ts_die "Reuse changed direct I/O mode"
+ ts_cleanup_on_exit
+ ts_log "reuse and failed mount ($mode): OK"
+done
+
+ts_finalize
--
2.55.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH v2 1/2] libmount: keep reused loop devices attached on mount failure
2026-10-04 2:41 [PATCH v2 0/2] libmount: preserve reused loop devices and request direct I/O Tianyi Chen
2026-09-21 7:34 ` [PATCH v2 2/2] libmount: support explicit loop direct I/O requests Tianyi Chen
@ 2026-10-02 12:17 ` Tianyi Chen
1 sibling, 0 replies; 3+ messages in thread
From: Tianyi Chen @ 2026-10-02 12:17 UTC (permalink / raw)
To: kzak; +Cc: util-linux
A loop device found by overlap detection may have been set up by the
caller. Do not detach it when a later mount step fails; only close the
reference acquired by this mount.
Add a regression test using a preconfigured device and an invalid
filesystem option.
Signed-off-by: Tianyi Chen <hi@tychen.cc>
---
libmount/src/hook_loopdev.c | 7 +++++++
tests/expected/mount/loop-reuse | 1 +
tests/ts/mount/loop-reuse | 23 +++++++++++++++++++++++
3 files changed, 31 insertions(+)
create mode 100644 tests/expected/mount/loop-reuse
create mode 100755 tests/ts/mount/loop-reuse
diff --git a/libmount/src/hook_loopdev.c b/libmount/src/hook_loopdev.c
index b2291340..809d64d8 100644
--- a/libmount/src/hook_loopdev.c
+++ b/libmount/src/hook_loopdev.c
@@ -21,6 +21,7 @@
struct hook_data {
int loopdev_fd;
+ bool reused;
};
static int delete_loopdev(struct libmnt_context *cxt, struct hook_data *hd);
@@ -350,6 +351,7 @@ static int setup_loopdev(struct libmnt_context *cxt,
} while (1);
success:
+ hd->reused = reuse;
if (!rc)
rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc));
@@ -408,6 +410,11 @@ static int delete_loopdev(struct libmnt_context *cxt, struct hook_data *hd)
hd->loopdev_fd = -1;
}
+ if (hd && hd->reused) {
+ DBG_OBJ(LOOP, cxt, ul_debug("keeping reused device"));
+ return 0;
+ }
+
rc = loopdev_detach(src); /* see lib/loopdev.c */
DBG_OBJ(LOOP, cxt, ul_debug("deleted [rc=%d]", rc));
diff --git a/tests/expected/mount/loop-reuse b/tests/expected/mount/loop-reuse
new file mode 100644
index 00000000..9b132bb6
--- /dev/null
+++ b/tests/expected/mount/loop-reuse
@@ -0,0 +1 @@
+reused device preserved: OK
diff --git a/tests/ts/mount/loop-reuse b/tests/ts/mount/loop-reuse
new file mode 100755
index 00000000..704b3378
--- /dev/null
+++ b/tests/ts/mount/loop-reuse
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="keep reused loop device on failure"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+ts_check_test_command "$TS_CMD_MOUNT"
+ts_check_test_command "$TS_CMD_UMOUNT"
+ts_skip_nonroot
+ts_check_losetup
+ts_check_prog "mkfs.ext2"
+
+ts_device_init
+mkfs.ext2 -F "$TS_LODEV" &> /dev/null || ts_die "Cannot make filesystem"
+mkdir -p "$TS_MOUNTPOINT"
+$TS_CMD_MOUNT -t ext2 -o loop,invalid-fs-option "$TS_OUTDIR/$TS_TESTNAME.img" \
+ "$TS_MOUNTPOINT" &> /dev/null && ts_die "Accepted invalid filesystem option"
+[ "$($TS_CMD_LOSETUP -j "$TS_OUTDIR/$TS_TESTNAME.img" | cut -d: -f1)" = "$TS_LODEV" ] ||
+ ts_die "Failed mount detached reused device"
+ts_log "reused device preserved: OK"
+ts_finalize
--
2.55.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH v2 0/2] libmount: preserve reused loop devices and request direct I/O
@ 2026-10-04 2:41 Tianyi Chen
2026-09-21 7:34 ` [PATCH v2 2/2] libmount: support explicit loop direct I/O requests Tianyi Chen
2026-10-02 12:17 ` [PATCH v2 1/2] libmount: keep reused loop devices attached on mount failure Tianyi Chen
0 siblings, 2 replies; 3+ messages in thread
From: Tianyi Chen @ 2026-10-04 2:41 UTC (permalink / raw)
To: kzak; +Cc: util-linux
Hi Karel,
This revision addresses your review of PR #4646. The reused-device fix
is now separate from the direct-I/O option.
Changes in v2:
- Add a specific diagnostic for direct-I/O mode conflicts.
- Use ul_strtobool() and the test-suite cleanup helpers.
- Document buffered-I/O fallback and how to check the effective mode.
- Add the option comment header and reused-device debug message.
- Remove the AI attribution trailer as requested.
Built mount, umount, losetup and findmnt with Meson. As root in a private
mount namespace, loop-reuse, loop-direct-io, regfile and fstab-loop all
passed. The direct-I/O test also checks boolean aliases and the conflict
diagnostic.
Thanks,
Tianyi
Tianyi Chen (2):
libmount: keep reused loop devices attached on mount failure
libmount: support explicit loop direct I/O requests
libmount/src/hook_loopdev.c | 41 +++++++++++++-
sys-utils/mount.8.adoc | 13 ++++-
tests/expected/mount/loop-direct-io | 6 +++
tests/expected/mount/loop-reuse | 1 +
tests/ts/mount/loop-direct-io | 84 +++++++++++++++++++++++++++++
tests/ts/mount/loop-reuse | 23 ++++++++
6 files changed, 165 insertions(+), 3 deletions(-)
create mode 100644 tests/expected/mount/loop-direct-io
create mode 100644 tests/expected/mount/loop-reuse
create mode 100755 tests/ts/mount/loop-direct-io
create mode 100755 tests/ts/mount/loop-reuse
base-commit: 55e3c4dba6101a47ecaae71c91edbe10c59b8d56
--
2.55.0
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-04 2:42 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-04 2:41 [PATCH v2 0/2] libmount: preserve reused loop devices and request direct I/O Tianyi Chen
2026-09-21 7:34 ` [PATCH v2 2/2] libmount: support explicit loop direct I/O requests Tianyi Chen
2026-10-02 12:17 ` [PATCH v2 1/2] libmount: keep reused loop devices attached on mount failure Tianyi Chen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox