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
*/
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