Skip to content

Commit b56c91b

Browse files
committed
loop: add regression test for partscan double-scan race
Add a stress test that detects spurious partition removal events when setting up a loop device with partscan enabled. The kernel bug was that disk_force_media_change() set GD_NEED_PART_SCAN, causing udev's device open to trigger a partition scan racing with the explicit scan from loop_reread_partitions(). The second scan would drop and re-add all partitions, making partition devices briefly disappear. The test monitors kernel uevents while repeatedly setting up and tearing down a loop device with partscan. Each cycle should produce exactly one add and one remove uevent for the partition device. Extra events indicate the double-scan race was triggered. Link: https://lore.kernel.org/linux-block/[email protected]/T/#u Signed-off-by: Daan De Meyer <[email protected]>
1 parent dd55eba commit b56c91b

2 files changed

Lines changed: 78 additions & 0 deletions

File tree

tests/loop/012

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-3.0+
3+
# Copyright (C) 2026 Daan De Meyer
4+
#
5+
# Regression test for a race between udev and loop_reread_partitions().
6+
#
7+
# When LOOP_CONFIGURE is called with LO_FLAGS_PARTSCAN,
8+
# disk_force_media_change() used to set GD_NEED_PART_SCAN before the
9+
# uevent was sent. When udev opened the device in response,
10+
# blkdev_get_whole() would trigger a partition scan, and then
11+
# loop_reread_partitions() would scan again. The second scan drops all
12+
# partitions from the first scan before re-adding them, causing
13+
# partition devices to briefly disappear.
14+
#
15+
# Verify that setting up a loop device with partscan does not produce
16+
# spurious partition add/remove events.
17+
18+
. tests/loop/rc
19+
20+
DESCRIPTION="check for spurious partition removal when partscan is enabled"
21+
TIMED=1
22+
23+
requires() {
24+
_have_program sfdisk
25+
}
26+
27+
test() {
28+
echo "Running ${TEST_NAME}"
29+
30+
truncate -s 3MiB "$TMPDIR/img"
31+
sfdisk "$TMPDIR/img" >"$FULL" 2>&1 <<-EOF
32+
label: gpt
33+
size=1MiB
34+
EOF
35+
36+
local dev
37+
dev="$(losetup -f)"
38+
39+
# Monitor kernel uevents for partition block devices.
40+
udevadm monitor --kernel -s block/partition >"$TMPDIR/uevents" 2>&1 &
41+
local mon_pid=$!
42+
# Give the monitor time to set up its netlink socket.
43+
sleep 0.5
44+
45+
local iterations=0
46+
SECONDS=0
47+
while ((SECONDS < "${TIMEOUT:-5}")); do
48+
if ! losetup -P "$dev" "$TMPDIR/img" 2>>"$FULL"; then
49+
continue
50+
fi
51+
losetup -d "$dev" 2>>"$FULL"
52+
((iterations++))
53+
done
54+
55+
sleep 0.5
56+
kill "$mon_pid"
57+
wait "$mon_pid" 2>/dev/null
58+
59+
# Each setup+teardown cycle should produce exactly one add and one
60+
# remove kernel uevent for the partition device. If the race
61+
# triggers, a second partition scan produces an extra remove+add
62+
# pair, inflating the counts beyond the number of iterations.
63+
local name="${dev##*/}"
64+
local adds removes
65+
adds=$(grep -c "^KERNEL\[.*\] add.*${name}p" "$TMPDIR/uevents")
66+
removes=$(grep -c "^KERNEL\[.*\] remove.*${name}p" "$TMPDIR/uevents")
67+
68+
if ((adds > iterations)); then
69+
echo "Fail: $iterations iterations but $adds add events (expected $iterations)"
70+
fi
71+
if ((removes > iterations)); then
72+
echo "Fail: $iterations iterations but $removes remove events (expected $iterations)"
73+
fi
74+
75+
echo "Test complete"
76+
}

tests/loop/012.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Running loop/012
2+
Test complete

0 commit comments

Comments
 (0)