]> git.hungrycats.org Git - linux/commit
btrfs: stripe_meta: hold back whole stripes so tree blocks always land and trapped...
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sun, 6 Sep 2026 02:58:50 +0000 (22:58 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 18 Sep 2026 21:36:27 +0000 (17:36 -0400)
commitfd34780bdf50f615b0ad228b5f3487b1d17d5c5b
tree33e8d1a13de6621b34eb6556a2cd32d203a7f512
parent827c40a8342b95d8abbb68123c3ff28ae77ac9e5
btrfs: stripe_meta: hold back whole stripes so tree blocks always land and trapped groups can be reclaimed

Two failures at the metadata fill edge under stripe_meta, both seen
with the tiny-file fill test on raid5 metadata.

The first is a transaction abort.  A tree block is reserved in bytes
and admitted while claimable whole stripes cover the outstanding
reservations, but it is placed in whole stripes: the open run it would
have joined is closed at the transaction boundary, so every group
claims at least one fresh full stripe per transaction.  The last few
megabytes of claimable supply go to those claims, made for reservations
admitted earlier, and the next admitted tree block finds no stripe at
all ("stripe_meta: tree block allocation of 16384 bytes returned
ENOSPC: claimable 0 open 1622016 trapped 1614266368"), which aborts the
transaction.  Pre-pay two transactions' worth of claims -- twice the
number of writable groups times the widest full stripe -- in the
space_info's stripe margin, recomputed at each commit scan.  Best-fit
run selection opens a fresh run only when a group's runs are full, so
this is a few megabytes, not a fraction of the space_info.

The second is a filesystem that quietly runs out at half capacity.
COW frees trap stripes rather than free them: a stripe is claimable
again only once every tree block in it is dead, which random deaths in
a sixteen-block stripe essentially never achieve, so the METADATA
space_info settles around half used, half trapped and no claimable
stripe left (3.04 GiB metadata, 49% used, fs not writable, deletes
included).  Only relocation packs live tree blocks back into whole
stripes, and it needs the moved group's live bytes claimable elsewhere
before it starts -- by the time the trigger fires there is nothing
left.  Hold back a reserve of whole stripes -- the largest writable
group's length, capped at a quarter of the space_info, nothing with a
single group -- from every metadata reservation except those made by
the relocation task, identified by a task pointer that reloc_ctl
already implies.  Reservations admitted against the reserve reach the
same tickets, so the flag travels with the ticket.  The reserve is an
accounting hold-back, not a partition: a committing transaction still
allocates from those stripes when it must, and the next scan restates
the reserve from what remains.  Both counters are cleared when
stripe_alloc is turned off, since the scan that maintains them stops
with it.

The reserve is reported through sysfs as bytes_stripe_reserve and in
the space_info dump, whose stripe_claimable and stripe_margin values
were printed under each other's label.

Assisted-by: Claude:claude-fable-5
fs/btrfs/block-group.c
fs/btrfs/fs.h
fs/btrfs/relocation.c
fs/btrfs/space-info.c
fs/btrfs/space-info.h
fs/btrfs/sysfs.c