From: Zygo Blaxell Date: Sat, 5 Sep 2026 12:17:26 +0000 (-0400) Subject: btrfs: stripe_alloc: keep the admitted bytes placeable when a group goes read-only X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0ce8a7297983709a4a84ecead3c92d38c8f09d1f;p=linux btrfs: stripe_alloc: keep the admitted bytes placeable when a group goes read-only inc_block_group_ro() checks that the rest of the space_info can absorb the group's free space in bytes. Under stripe_alloc the data admission gate promised writers whole stripes: bytes_may_use plus the held margin against bytes_stripe_claimable. Making a group read-only removes its claimable stripes from that supply after the promise was made, so with the reclaim worker relocating a group at the fill edge, admitted writebacks found no stripe and were dropped: allocation failed flags 129, wanted 8192 ... space_info DATA has 450560 free, is full ... block group ... [readonly] Refuse the read-only transition while the admitted bytes would no longer fit in the claimable supply without this group. Scrub, balance and reclaim retry later; this is the fail-early direction and only bites transiently at the edge, since bytes_may_use drains as writeback completes. Assisted-by: Claude:claude-fable-5 --- diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index dcc06b5d28183..c6af2d0a0900b 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -3846,6 +3846,19 @@ static int inc_block_group_ro(struct btrfs_block_group *cache, bool force) */ if (sinfo_used - trapped + num_bytes <= sinfo->total_bytes) ret = 0; + + /* + * The stripe_alloc gate admitted writers against whole + * stripes (bytes_may_use plus the held margin must fit in + * bytes_stripe_claimable). Going read-only removes this + * group's claimable stripes from that supply, so refuse while + * the bytes already admitted would no longer be placeable: + * otherwise their writebacks fail and the data is dropped. + */ + if (!ret && READ_ONCE(cache->stripe_unusable_ready) && + sinfo->bytes_may_use + sinfo->bytes_stripe_margin > + sinfo->bytes_stripe_claimable - cache->stripe_claimable) + ret = -ENOSPC; } else { /* * We overcommit metadata, so we need to do the