* [PATCH blktests] tests/md: add test 005 for raid1 deadlock under I/O, limit changes and recovery
@ 2026-09-10 10:09 Jack Wang
0 siblings, 0 replies; only message in thread
From: Jack Wang @ 2026-09-10 10:09 UTC (permalink / raw)
To: linux-block, Shin'ichiro Kawasaki; +Cc: Jack Wang
From: Jack Wang <jinpu.wang@cloud.ionos.com>
Add a regression test for a deadlock reported against raid1 with an
internal bitmap, where concurrent I/O, queue limit updates
(max_sectors_kb) and device fail/remove/add (recovery) cycles can
deadlock.
The test creates a raid1 array with an internal bitmap on two brd
devices, drives it with fio while repeatedly toggling
max_sectors_kb and cycling one member through fail/remove/add, and
flags a likely deadlock if any of these steps fail to make progress
within a bounded timeout.
Link: https://lore.kernel.org/linux-raid/CAMGffE=heGA3y8FjQ0Sm1jj-kd-=H9Y54WozKASSEZhc9UNKjA@mail.gmail.com/T/#t
Signed-off-by: Jack Wang <jinpu.wang@cloud.ionos.com>
---
tests/md/005 | 142 +++++++++++++++++++++++++++++++++++++++++++++++
tests/md/005.out | 2 +
2 files changed, 144 insertions(+)
create mode 100755 tests/md/005
create mode 100644 tests/md/005.out
diff --git a/tests/md/005 b/tests/md/005
new file mode 100755
index 000000000000..5493d0edc933
--- /dev/null
+++ b/tests/md/005
@@ -0,0 +1,142 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-3.0+
+# Copyright (C) 2026 Jack Wang
+#
+# Regression test for a deadlock reported against raid1 with an internal
+# bitmap, where concurrent I/O, queue limit updates (max_sectors_kb) and
+# device fail/remove/add cycles (recovery) can deadlock.
+#
+# See: https://lore.kernel.org/linux-raid/CAMGffE=heGA3y8FjQ0Sm1jj-kd-=H9Y54WozKASSEZhc9UNKjA@mail.gmail.com/T/#t
+
+. tests/md/rc
+. common/brd
+. common/fio
+
+DESCRIPTION="test for raid1 deadlock with internal bitmap under I/O, queue limit changes and fail/remove/add"
+
+requires() {
+ _have_fio
+ _have_driver raid1
+ _have_brd
+}
+
+# Resolve the real md device node (e.g. "md126") backing /dev/ram0 via
+# sysfs. This is more reliable than the mdadm-requested /dev/md/<name>
+# symlink, whose creation depends on udev rules/timing that can vary
+# across systems.
+_resolve_md_dev() {
+ local holder
+
+ for holder in /sys/class/block/ram0/holders/*; do
+ [[ -e $holder ]] || continue
+ basename "$(readlink -f "$holder")"
+ return 0
+ done
+
+ return 1
+}
+
+_toggle_max_sectors_kb() {
+ local sysfs_attr="/sys/block/${1}/queue/max_sectors_kb"
+
+ while [[ -e "$sysfs_attr" ]]; do
+ echo 128 > "$sysfs_attr" 2>/dev/null
+ done
+}
+
+# Wait up to $2 seconds for background pid $1 to exit. Returns 1 only if
+# it is still running once the deadline is hit (a real hang); the pid's
+# own exit status is irrelevant here and is not propagated.
+_wait_pid_timeout() {
+ local pid=$1
+ local secs=$2
+ local waited=0
+
+ while kill -0 "$pid" 2>/dev/null; do
+ if ((waited >= secs)); then
+ return 1
+ fi
+ sleep 1
+ ((waited++))
+ done
+
+ wait "$pid" 2>/dev/null
+ return 0
+}
+
+# Repeatedly fail/remove/add a member while I/O is ongoing, racing
+# recovery against I/O submission and queue limit updates. mdadm --wait
+# returns failure when there was nothing to wait for (e.g. a fail/remove/
+# add cycle that didn't actually trigger recovery), which is expected and
+# not itself a sign of trouble here; a real hang is instead caught by the
+# overall `timeout` this is invoked under.
+_fail_remove_add_loop() {
+ local md_dev=$1
+ local i
+
+ for ((i = 0; i < 10; i++)); do
+ mdadm "/dev/${md_dev}" --fail /dev/ram0
+ mdadm "/dev/${md_dev}" --remove /dev/ram0
+ mdadm "/dev/${md_dev}" --add /dev/ram0
+ mdadm --wait "/dev/${md_dev}"
+ done
+}
+export -f _fail_remove_add_loop
+
+test() {
+ echo "Running ${TEST_NAME}"
+
+ local md_dev
+ local toggle_pid
+ local fio_pid
+
+ if ! _init_brd rd_size=262144 rd_nr=2; then
+ return 1
+ fi
+
+ mdadm --quiet --create /dev/md/blktests_md --force -e 1.2 \
+ --assume-clean --level=1 --bitmap=internal --raid-devices=2 \
+ /dev/ram0 /dev/ram1 >> "$FULL" 2>&1
+
+ if ! md_dev=$(_resolve_md_dev); then
+ echo "failed to resolve md device for /dev/ram0"
+ _cleanup_brd
+ return 1
+ fi
+
+ # Keep writing to max_sectors_kb while I/O and recovery are ongoing,
+ # this races queue limit updates against I/O submission and md
+ # reconfiguration.
+ _toggle_max_sectors_kb "$md_dev" &
+ toggle_pid=$!
+
+ fio --direct=1 --rw=randrw --ioengine=libaio --iodepth=32 --numjobs=4 \
+ --time_based=1 --runtime=60 --filename="/dev/${md_dev}" \
+ --name=repro >> "$FULL" 2>&1 &
+ fio_pid=$!
+
+ # Give fio a moment to start driving I/O so the array is in
+ # read-write mode before failing/adding a member below.
+ sleep 2
+
+ # shellcheck disable=SC2016
+ if ! timeout 120 bash -c '_fail_remove_add_loop "$1"' _ "$md_dev" \
+ >> "$FULL" 2>&1; then
+ echo "fail/remove/add loop did not complete in time, possible deadlock"
+ fi
+
+ if ! _wait_pid_timeout "$fio_pid" 90; then
+ echo "fio did not complete in time, possible deadlock"
+ kill -9 "$fio_pid" 2>/dev/null
+ fi
+
+ kill "$toggle_pid" 2>/dev/null
+ wait "$toggle_pid" 2>/dev/null
+
+ if ! timeout 30 mdadm --quiet --stop "/dev/${md_dev}" >> "$FULL" 2>&1; then
+ echo "mdadm --stop timed out, possible deadlock"
+ fi
+ _cleanup_brd
+
+ echo "Test complete"
+}
diff --git a/tests/md/005.out b/tests/md/005.out
new file mode 100644
index 000000000000..d5ca24713e54
--- /dev/null
+++ b/tests/md/005.out
@@ -0,0 +1,2 @@
+Running md/005
+Test complete
--
2.43.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-10 10:09 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-10 10:09 [PATCH blktests] tests/md: add test 005 for raid1 deadlock under I/O, limit changes and recovery Jack Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox