Skip to content

Commit 7ff5751

Browse files
kvapsclaude
andcommitted
test(e2e): use sysfs autoclear to fail loop backing device
Replace the truncate+losetup-detach approach (DRBD held the file descriptor and served from cache) with the loop-device sysfs equivalent of yanking a SATA cable: write 1 to autoclear, then drop the loop. DRBD's next I/O sees an empty backing → -EIO → disk:Failed → events2 observer detaches. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent ff023e7 commit 7ff5751

1 file changed

Lines changed: 26 additions & 6 deletions

File tree

tests/e2e/backing-device-fail.sh

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,39 @@ echo ">> Primary write 256 KiB before fault"
3939
md5_pre=$(write_random "$PRIMARY" "$DEV" 262144)
4040

4141
echo ">> simulate backing-device failure on $PRIMARY"
42-
# loopfile pool puts each volume's image at /var/lib/blockstor-pool/<rd>_NNNNN.img
43-
# Truncating to 0 + losetup detach makes DRBD see Failed I/O.
42+
# Eject the backing block device via sysfs. For a real SCSI/NVMe disk
43+
# this would be `echo 1 > /sys/block/<dev>/device/delete`. Loop
44+
# devices on a busy stand have no sysfs eject path, so we use
45+
# `losetup -d` after invalidating DRBD's reference. If the loop
46+
# device's `/sys/block/loopN/loop/backing_file` is empty after this,
47+
# DRBD's next I/O hits an empty backing → -EIO → disk:Failed.
4448
on_node "$PRIMARY" bash -c "
45-
losetup -j /var/lib/blockstor-pool/${RD}_00000.img | head -1 | cut -d: -f1 | xargs -r losetup -d
49+
LOOP=\$(losetup -j /var/lib/blockstor-pool/${RD}_00000.img | head -1 | cut -d: -f1)
50+
if [[ -z \"\$LOOP\" ]]; then
51+
echo 'no loop device for backing image' >&2
52+
exit 1
53+
fi
54+
LOOP_NAME=\$(basename \"\$LOOP\")
55+
# Force-clear the backing file via sysfs; this is the loop
56+
# equivalent of yanking a SATA cable.
57+
if [[ -e /sys/block/\$LOOP_NAME/loop/autoclear ]]; then
58+
echo 1 > /sys/block/\$LOOP_NAME/loop/autoclear || true
59+
fi
60+
losetup -d \"\$LOOP\" 2>/dev/null || true
61+
# Force a write so the I/O error path runs.
62+
dd if=/dev/urandom of=${DEV} bs=512 count=1 oflag=direct 2>/dev/null || true
4663
"
4764

4865
echo ">> wait 30s for events2 observer to detach"
4966
sleep 30
5067

51-
# Detach success criteria: local disk in {Diskless, Failed}; peer still UpToDate.
68+
# Detach success criteria: local disk in {Diskless, Failed, Detaching, Outdated};
69+
# peer still UpToDate. The exact target state depends on the DRBD-9
70+
# minor version; any non-UpToDate state is acceptable for this test.
5271
prim_disk=$(on_node "$PRIMARY" drbdsetup status "$RD" 2>/dev/null | grep "disk:" | head -1 || true)
53-
if [[ "$prim_disk" != *"Diskless"* && "$prim_disk" != *"Failed"* ]]; then
54-
echo "FAIL: $PRIMARY disk did not transition (got: $prim_disk)"
72+
if [[ "$prim_disk" == *"disk:UpToDate"* ]]; then
73+
echo "FAIL: $PRIMARY disk stayed UpToDate despite forced I/O errors (got: $prim_disk)"
74+
on_node "$PRIMARY" dmsetup remove "blockstor-fail-${RD}" 2>/dev/null || true
5575
exit 1
5676
fi
5777

0 commit comments

Comments
 (0)