Found 2026-08-01 by the harness's new degraded-write continuation probe
(WH_DEGRADED_CONTINUE=1) on its first run: on the legacy allocator,
29 of 56 degraded read-write continuations died with fsync EIO -- many
on clean images (consistent state minus one device), where degraded
operation is unambiguously supposed to work.
Writing to a degraded raid5/6 array, any sub-stripe (RMW) write whose target stripe's missing-device column is only partially covered by data csums fails with EIO:
BTRFS error (device loop3): dropping unwritten extent at root 5
ino 1061 offset [0,4095] disk bytenr 638771200 length 4096
No device IO errors, no corruption counted, nothing else logged (the "dropping unwritten extent" line is our own logging patch; stock kernels drop the extent silently). Deterministic per (fs layout, missing device, allocation target): a workload either always dies at the same write or always survives, which produced the confusing per-(cut,drop) failure pattern in the matrix.
verify_one_sector() (fs/btrfs/raid56.c) checks that the rbio has a
csum_buf/csum_bitmap at all, but never tests the bitmap bit for
the sector it verifies. Sectors without csums -- free space, or
sectors whose ordered extent has not yet committed its csums, including
the very sector the sub-stripe write is replacing -- are compared
against an all-zero csum_buf slot and fail.
Mechanism of a failing write, traced with kprobes:
rmw_read_wait_recover) marks every sector of the
missing device as an error, reads the rest.recover_sectors() rebuilds the missing column row by row and
calls verify_one_sector() on each rebuilt sector.The whole-stripe early-outs explain why some degraded RMWs survive:
if the stripe has zero csums anywhere, fill_data_csums() frees
the buffers (no_csum:) and verification is skipped entirely; if the
missing column is fully csummed, every verify passes. Only the
partial case dies -- hence layout-dependent, "flaky-looking" failures.
verify_bio_data_sectors(), the sibling that verifies directly-read
sectors on the same RMW path, has the test_bit skip. The recovery
path never got one.
Upstream since the function's introduction; present in v6.3, v6.6, v6.12, v6.18, and linus/master (2026-08). Not caused by the stripe_alloc series (kernel #76 carries it, but the function is byte-equivalent to upstream).
/* No csum for this sector, nothing to verify against. */
if (!test_bit(stripe_nr * rbio->stripe_nsectors + sector_nr,
rbio->csum_bitmap))
return 0;
after the P/Q early-out in verify_one_sector().
Patch: "btrfs: raid56: only verify recovered sectors that have a checksum", Fixes: 7a3150723061 ("btrfs: raid56: do data csum verification during RMW cycle"), CC stable 6.2+.
/root/manual-repro.sh on bhive: replay a clean 4-device raid5 image
minus devid 2, mount degraded, write files with fsync. Fails at the
first 4 KiB file within seconds on unfixed kernels.
Open question (harmless post-fix, noted for completeness): the clean cut=2 image contained exactly two stripe rows, both csum-less free space, where a single nonzero data column coexists with zero parity. Likely a discard interaction in the original run (async discard is on by default on loop devices and zeroes free data sectors without updating parity). No live data was covered in either row; a future audit could stamp free space and watch discard traffic to confirm.