]> git.hungrycats.org Git - linux/log
linux
6 days agohistoric: 2026-09-13 ENOSPC chain v1 — the 34-commit stack before the cleanup 6.18/historic/2026-09-13-enospc-chain-v1
Zygo Blaxell [Mon, 14 Sep 2026 02:07:19 +0000 (22:07 -0400)]
historic: 2026-09-13 ENOSPC chain v1 — the 34-commit stack before the cleanup

Alias of refs/wip/enospc/6.18-stripe-alloc as it stood on 2026-09-13
before the rebase onto the post-quiesce topic: 34 commits on the
pre-retag 6.18 topics/stripe-alloc tip.  Superseded by
refs/wip/enospc/6.18-stripe-alloc-v2 (26 commits), which drops the two
topics/delalloc-fixes duplicates and the wip nocow_ro_drains counter and
folds the commits whose lines were fully rewritten later in the stack
(the five reclaim-trigger commits into one; A2 into the relocation
margin charge).  Kept for the original per-commit messages and for the
blame survey ($CLAUDE_JOB_DIR chain-survive.txt) that drove the fold.
Not for upstream or test builds.

Assisted-by: Claude:claude-fable-5-1
8 days agobtrfs: stripe_alloc: hold the probe margin until the metadata reservation charges it
Zygo Blaxell [Sat, 12 Sep 2026 05:41:46 +0000 (01:41 -0400)]
btrfs: stripe_alloc: hold the probe margin until the metadata reservation charges it

btrfs_check_data_free_space() admits a write for its bytes plus a
whole-stripe margin per extent, then releases the margin at once, on the
understanding that btrfs_delalloc_reserve_metadata() re-charges it into
bytes_stripe_margin moments later.  Between the two nothing holds it:
at the fill edge, eight fsstress writers were admitted into each other's
released margins, and the stripes their claims then needed had already
gone to their neighbours.  The failure-time dump showed exactly that
shape -- claimable 0, bytes_may_use holding only the failing write, one
stripe of margin per outstanding extent, nothing stranded -- and it
survived charging preallocation by whole stripes and holding a stripe
across it, because it was never preallocation's stripe.

Let the callers that go on to reserve metadata keep the probe's margin
in bytes_may_use and release it only after that reservation has charged
bytes_stripe_margin: the buffered write, page_mkwrite, the block
truncation, the direct IO path (carried in btrfs_dio_data across the
two functions), the log-tail carry and btrfs_delalloc_reserve_space().
The one caller that reserves no metadata, the v1 space cache write-out,
keeps the immediate release.

Assisted-by: Claude:claude-fable-5
8 days agobtrfs: stripe_alloc: hold a stripe of margin across a preallocation
Zygo Blaxell [Sat, 12 Sep 2026 00:27:55 +0000 (20:27 -0400)]
btrfs: stripe_alloc: hold a stripe of margin across a preallocation

Admission now charges a preallocation the roundup of its bytes to whole
stripes, but that charge lives only for the check: between the
reservation and the allocation only the raw bytes sit in bytes_may_use.
fallocate() is quick, but eight fsstress processes preallocating at the
fill edge were quick together, and each in-flight preallocation short of
a stripe still took a stripe the gate had counted for someone else: the
failure-time dump kept showing admitted writes with the supply gone.

Do for preallocation what the delalloc margin does per outstanding
extent: hold one stripe in bytes_stripe_margin from the successful data
reservation until the caller's allocations are done, at the four users
of btrfs_alloc_data_chunk_ondemand() -- fallocate's range loop, the
zero-range preallocation, relocation's cluster preallocation and the
encoded write's extent reservation.  Released as soon as the claims have
landed, so the hold costs nothing beyond that window.

Assisted-by: Claude:claude-fable-5
8 days agobtrfs: stripe_alloc: admit data reservations by whole stripes
Zygo Blaxell [Fri, 11 Sep 2026 22:25:09 +0000 (18:25 -0400)]
btrfs: stripe_alloc: admit data reservations by whole stripes

An allocation of N bytes that has to open a stripe run claims
round_up(N, full stripe) whole stripes.  A delalloc write is admitted
with a full-stripe margin per outstanding extent, which covers that
roundup for itself, and relocation is admitted for round_up(N) + 1
stripe.  A preallocation was admitted for N alone: fallocate() has no
outstanding extent and holds no margin, so each one short of a stripe
took up to a stripe more from the whole-stripe supply than the gate had
charged.  The supply behind writes admitted earlier shrank by that much
and at the fill edge their claims found nothing.

With the stranded-stripe accounting and the relocation margin in place,
the failure-time dump on a degraded fsstress fill showed exactly this:
every failed write had bytes_stripe_claimable 0, bytes_may_use equal to
its own size, one stripe of margin per outstanding extent, nothing
stranded, no group read-only and no relocation group dedicated -- the
gate's arithmetic was consistent and still the stripes were gone, with
a preallocation's reservation in flight in the dump.

Charge every non-relocation admission the roundup to whole stripes.  The
excess is not held past admission (a preallocation allocates in the same
call), so a concurrent admission in that window can still overrun by a
stripe; holding it would need the reservation to carry the roundup until
the allocation lands, a later refinement.

Assisted-by: Claude:claude-fable-5
8 days agowip: btrfs: count read-only NOCOW drains in sysfs stripe_run_stats
Zygo Blaxell [Fri, 11 Sep 2026 17:01:54 +0000 (13:01 -0400)]
wip: btrfs: count read-only NOCOW drains in sysfs stripe_run_stats

Test-visibility counter for the previous patch; a tracepoint would be the
upstream form.

Assisted-by: Claude:claude-fable-5
8 days agobtrfs: drain pending NOCOW writes before making a block group read-only
Zygo Blaxell [Fri, 11 Sep 2026 17:00:28 +0000 (13:00 -0400)]
btrfs: drain pending NOCOW writes before making a block group read-only

A buffered write to a nodatacow or preallocated range decides at write()
time, in btrfs_check_nocow_lock(), that it will be written in place, and
records that with EXTENT_NORESERVE: no data space is reserved for it.
The block group's nocow_writers count, which relocation and scrub wait
for, is only taken at writeback, in run_delalloc_nocow().  Between the
two nothing stops the group from going read-only -- scrub, balance and
zoned reclaim all call btrfs_inc_block_group_ro() without flushing
anything -- and when the writeback then finds ->ro set it falls back to
COW: fallback_to_cow() charges the data space without any admission
check, and on a full filesystem cow_file_range() fails and the pages are
dropped, the error surfacing only at fsync or close.  Snapshots and
reflinks flush the range before changing its sharing, so this window is
the read-only transition's alone.

Drain the group first.  btrfs_extent_readonly() now refuses NOCOW into a
group that is being made read-only and notes, per group, that a write()
has decided to NOCOW into it; btrfs_check_nocow_lock() holds an fs-wide
in-flight count until btrfs_check_nocow_unlock(), i.e. until the pages
are dirtied.  btrfs_inc_block_group_ro() blocks new decisions, waits for
the in-flight ones, and -- only if a NOCOW decision has landed on the
group since it was last drained -- flushes delalloc and waits for the
group's ordered extents, so every pending range is written in place
while the group is still writable, before the transaction is joined and
the group flipped.  The block is lifted if the transition fails and when
the group returns to read-write.

Assisted-by: Claude:claude-fable-5
8 days agobtrfs: stripe_alloc: charge the held stripe margin against relocation's admission too
Zygo Blaxell [Fri, 11 Sep 2026 16:36:25 +0000 (12:36 -0400)]
btrfs: stripe_alloc: charge the held stripe margin against relocation's admission too

The data admission gate charges bytes_stripe_margin -- the whole-stripe
collateral every outstanding data extent holds against the commit that
traps its run's tail -- when admitting ordinary writes, but not when
admitting relocation.  Relocation was therefore admitted against supply
the margin was reserving, and at the fill edge of a degraded acceptance
run it consumed the whole stripes that small writes' claims then needed:
about fifty 4-32 KiB writes per run failed at writeback in bursts that
tracked the block group relocations to the minute, with nothing stranded
and no private runs (the other two admission defects already fixed).

The margin is owed to writers already admitted, so every later admission
must respect it.  Charge it on the relocation path as well; relocation
that cannot be covered waits on its ticket like anyone else, and the
margin returns as the writes complete.

Assisted-by: Claude:claude-fable-5
8 days agobtrfs: stripe_alloc: count nocow run lifecycles and stranded stripes in sysfs
Zygo Blaxell [Fri, 11 Sep 2026 13:27:32 +0000 (09:27 -0400)]
btrfs: stripe_alloc: count nocow run lifecycles and stranded stripes in sysfs

Add a stripe_run_stats file beside stripe_park_stats with lifetime
counters for the paths the previous two patches touched, so a test can
show that it exercised them rather than merely passed: private NOCOW
runs opened, claim candidates refused for overlapping a live run, bytes
of whole stripes the scans found stranded behind live runs, and rescans
requested by a freed run to credit its stripes back.

Assisted-by: Claude:claude-fable-5
8 days agobtrfs: stripe_alloc: give nocow extents a private run only while they may be written...
Zygo Blaxell [Fri, 11 Sep 2026 13:23:47 +0000 (09:23 -0400)]
btrfs: stripe_alloc: give nocow extents a private run only while they may be written in place

Preallocated extents and a nodatacow inode's extents are steered into a
per-inode NOCOW-class stripe run, so that their stripes never hold any
other file's data and an in-place write can be confined to the file that
waived COW protection.  Such a run survives transaction commits, so a
slowly appended nocow file does not burn a stripe per commit.

Both the steering and the survival were unconditional, but whether a
nocow extent can be written in place at all is decided by the
stripe_alloc_allow_rmw policy, and with nothing waived (the default)
btrfs_stripe_nocow_writable() forces COW for every such write.  The
private runs then hold only ordinary COWed extents and unwritten
preallocation, never in-place data, and their survival protects nothing:
it only keeps the run object alive until the inode is evicted, and every
whole stripe freed inside the run's range meanwhile -- a prealloc extent
overwritten by a COWed one, or unlinked -- is stranded, counted free but
refused to every claim.  An fsstress run on a filesystem filled to
ENOSPC left 387 and 976 such runs in single block groups, holding 99% of
the group's free whole stripes.

Steer into a NOCOW run only when the policy lets that kind of extent
(prealloc or nodatacow) be written in place, and let a NOCOW run outlive
commits only when the policy allowed in-place writes when the run was
opened.  The verdict is recorded in the run at open time so the commit's
close and settle predicates cannot disagree with each other when the
policy changes underneath them: a run opened under one policy keeps its
lifecycle until it is closed by filling up, by the inode's eviction or
by a forced quiesce.

Assisted-by: Claude:claude-fable-5
8 days agobtrfs: stripe_alloc: count whole stripes stranded behind live runs as trapped
Zygo Blaxell [Fri, 11 Sep 2026 12:59:09 +0000 (08:59 -0400)]
btrfs: stripe_alloc: count whole stripes stranded behind live runs as trapped

The data admission gate admits a reservation while the whole-stripe
supply, bytes_stripe_claimable, covers it, and the per-commit scan
measures that supply as the block group's wholly free full stripes.
But a wholly free stripe inside a live stripe run's range cannot be
claimed: btrfs_stripe_run_range_usable() rejects any claim overlapping
a run object, because a second run over the same range would make the
range-to-run lookups ambiguous.  The run was claimed over the stripe,
the extents allocated there were freed again, and the run has not been
freed yet.  For a COW run that window closes at the next commit; a
nodatacow inode's private run deliberately survives commits and lives
until the inode is evicted.

On a raid56 filesystem filled to ENOSPC by fsstress (thousands of
fallocated files, so thousands of cached inodes each holding a private
NOCOW run) a live walk of the free space cache showed the accounting
exact -- the wholly free stripes summed to bytes_stripe_claimable to the
byte -- and 99% of them inside the ranges of open NOCOW runs with no
inflight IO: 22.8 MiB of 22.8 MiB behind 387 runs in one group, 73.4 MiB
of 73.4 MiB behind 976 runs in another.  Every claim failed in O(1) on
the "no claimable run" verdict, every write() admitted against the 99 MiB
of phantom supply failed at writeback, and the data was dropped ("data
writeback allocation ... returned ENOSPC despite reservation margin"),
about 100,000 delalloc ranges per run of the degraded acceptance suite
where the stock allocator drops none.

Have the scan take such stripes out of the claimable supply: count them
in stripe_unusable, which btrfs_space_info_used() already charges so the
gate cannot admit against them, and record them separately as
stripe_stranded so that btrfs_stripe_bg_wants_reclaim() does not ask
relocation to recover space that comes back by itself.  When a run is
freed, mark its group for rescan so the next commit credits the stripes
back.  The space_info total is exposed as bytes_stripe_stranded in sysfs
and in the space info dump.

This makes the gate honest -- write() gets the ENOSPC instead of
writeback dropping the data -- but does not return the stranded space
while the runs live; that is the run lifecycle's business.

Assisted-by: Claude:claude-fable-5
9 days agobtrfs: stripe_alloc: charge the held stripe margin against the claimable supply
Zygo Blaxell [Fri, 11 Sep 2026 08:02:21 +0000 (04:02 -0400)]
btrfs: stripe_alloc: charge the held stripe margin against the claimable supply

The data admission gate is meant to refuse a write at reservation time
when the whole-stripe supply cannot cover it, so that write() returns
ENOSPC instead of writeback finding no stripe later.  It compared
bytes_may_use + bytes against bytes_stripe_claimable and left out
bytes_stripe_margin, the whole-stripe collateral every outstanding data
extent holds against the commit that closes its open run and traps the
tail.  The margin is charged against total_bytes through
btrfs_space_info_used(), but not against the supply the admitted bytes
actually draw on.

Each commit shrinks that supply by the trapped tails, with nothing
holding it, so at the fill edge a write that passed admission reached
find_free_extent() and failed: "data writeback allocation of 4096 bytes
returned ENOSPC despite reservation margin; buffered data in this range
will be dropped", followed by the cow_file_range() failures 35 seconds
later in the same fill.

Add the held margin to the admitted bytes.  This over-refuses by the
margin, which the commit rescan raising bytes_stripe_claimable and the
FLUSH_DATA ticket retry heal, and stays within phase 1's pessimistic
contract; the relocation path keeps its own whole-stripe charge.

Assisted-by: Claude:claude-fable-5
9 days agobtrfs: release the space of delalloc ranges abandoned by a failed writeback
Zygo Blaxell [Fri, 11 Sep 2026 08:02:14 +0000 (04:02 -0400)]
btrfs: release the space of delalloc ranges abandoned by a failed writeback

When a folio holds more than one delalloc range (large folios, or
sector size smaller than page size) and btrfs_run_delalloc_range()
fails on one of them, writepage_delalloc() only unlocks the ranges
that follow it.  The folio's dirty flag was already cleared by
folio_clear_dirty_for_io() for this writeback, so those ranges are never
written back again: EXTENT_DELALLOC stays set on a clean folio, and when
memory reclaim later releases the folio, try_release_extent_state()
clears the bit without EXTENT_CLEAR_META_RESV or EXTENT_CLEAR_DATA_RESV.

Everything reserved for the abandoned ranges leaks: the inode's
outstanding extents, csum_bytes and block_rsv, and the data space_info's
bytes_may_use.  On eviction btrfs_destroy_inode() warns about the
inode's block_rsv.reserved, block_rsv.size and csum_bytes, and at unmount
check_removing_space_info() warns about the data space_info's
bytes_may_use.

Seen on a raid56 filesystem with the stripe-exclusive allocator filled
to ENOSPC, where cow_file_range() fails with -ENOSPC at writeback time;
the same path is reached whenever run_delalloc_range() fails on a
multi-range folio, e.g. -EIO or -ENOSPC on a NOCOW fallback.  A per-inode
record of delalloc clears made without EXTENT_CLEAR_META_RESV showed
that on each leaked inode the last such clear came from

  kswapd -> btrfs_release_folio -> try_release_extent_mapping
    -> btrfs_clear_extent_bit

over a 4096 byte range, which was exactly the leftover csum_bytes; the
inodes had never been through the cow error cleanup themselves.

Fail the remaining ranges the same way btrfs_run_delalloc_range() failed
the range that hit the error: clear the delalloc bits releasing the
metadata and data reservations, free the qgroup reservation, and mark
the blocks written back so the error, which extent_writepage() already
set on the mapping, reaches the writer.

Assisted-by: Claude:claude-fable-5
9 days agobtrfs: stripe_alloc: do not read an inline backref off a keyed-ref extent item
Zygo Blaxell [Thu, 10 Sep 2026 14:53:38 +0000 (10:53 -0400)]
btrfs: stripe_alloc: do not read an inline backref off a keyed-ref extent item

stripe_extents_owned_by() decides whether every extent in a stripe range
belongs to one inode by reading the single inline backref of each data
extent item.  A data extent's only backref is keyed rather than inline
when the leaf had no room to grow the item: insert_inline_extent_backref()
returns -EAGAIN and insert_extent_backref() adds a separate
EXTENT_DATA_REF item.  Such an extent item ends right after the header,
so the walk read the next item's bytes as an inline ref and
btrfs_get_extent_inline_ref_type() warned:

  BTRFS error (device dm-22): eb 81657856 iref 0x3a5c invalid extent inline ref type 1
  WARNING: CPU: 6 PID: 199813 at fs/btrfs/extent-tree.c:380 btrfs_get_extent_inline_ref_type+0xe0/0x190
   stripe_extents_owned_by+0x270/0x310
   btrfs_alloc_from_inode_stripe_run+0x48e/0x6a0
   find_free_extent+0xf9b/0x1870
   btrfs_reserve_extent+0x214/0x890
   __btrfs_prealloc_file_range+0xf1/0x470
   btrfs_fallocate+0xa99/0x10f0

The answer was already the conservative "not owned" (the garbage type is
not EXTENT_DATA_REF_KEY), so only the warning and the taint were wrong.
Check the item size before reading the inline ref and treat a keyed-ref
extent as not owned.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: tell a dead tree block from a live one in the metadata RMW audit
Zygo Blaxell [Sun, 6 Sep 2026 13:01:40 +0000 (09:01 -0400)]
btrfs: stripe_alloc: tell a dead tree block from a live one in the metadata RMW audit

The audit reports any older-generation header under a read-modify-write
as a write hole, and counted over six hundred per metadata fill test.
Since runs close at every transaction and freed stripes are reclaimed
whole, most such headers belong to blocks that died with their stripe:
the stripe was claimed fully free, the dead blocks kept their headers,
and a torn parity write over them damages nothing a reader can reach.

Ask the committed extent tree, the witness a degraded read after a
crash would consult, whether the block is still referenced.  Count and
report a live one as before (now with its bytenr and reference count),
and count a dead one as meta_rmw_ghost without a warning.

A sector the run has already re-allocated in the current transaction
carries an extent item of the current generation for the new block
while the old header is still on disk (the new write has not landed):
that is not a live block either, so the item generation must match the
header generation.  Without that check every single-stripe run of the
current transaction reported its dead predecessors as holes (90 of 135
warnings in one fill test; the other 45 were the raid5 system chunk).

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: show per-group trapped bytes in the space info dump
Zygo Blaxell [Sat, 5 Sep 2026 09:50:30 +0000 (05:50 -0400)]
btrfs: stripe_alloc: show per-group trapped bytes in the space info dump

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_meta: let a group go read-only for relocation against the reserve
Zygo Blaxell [Sun, 6 Sep 2026 13:54:37 +0000 (09:54 -0400)]
btrfs: stripe_meta: let a group go read-only for relocation against the reserve

The read-only guard keeps admitted bytes placeable by refusing to take a
group's usable free space out of the supply when the space_info could
not absorb it.  With the whole-stripe reserve counted as used, that
refusal comes by the width of the reserve at the fill edge: two
eligible metadata groups sat marked for reclaim through a 300 second
wait and the worker dropped each on inc_block_group_ro() -- the
space_info was 7 MiB short of the check with 376 MiB of whole stripes
claimable, 352 MiB of them the reserve that exists for this move.

Going read-only is the first step of relocation, so let the guard draw
on the reserve the way relocation's own reservations do.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_meta: let the transaction machinery's own reservations draw on the...
Zygo Blaxell [Sun, 6 Sep 2026 11:44:45 +0000 (07:44 -0400)]
btrfs: stripe_meta: let the transaction machinery's own reservations draw on the reserve

With the whole-stripe reserve counted as used for everyone but the
relocation task, a committing transaction whose handle reserve runs dry
mid-COW gets ENOSPC from btrfs_use_block_rsv(): its NO_FLUSH fallback
reservation is refused as soon as the claimable supply is below the
reserve, and the global reserve behind it has been refilled by fiat
against stripes that do not exist.  Seen on the delete phase of the
metadata fill test with relocation running: claimable 163 MiB, reserve
352 MiB, delayed refs freeing the deleted leaves, and the extent tree
COW that freeing needs aborted the transaction with ENOSPC while
163 MiB of whole stripes sat unused (no allocator canary: the allocator
was never asked).

The reserve exists to hold back user operations so relocation and the
commit that follows it have room; it must not hold back the commit.
Let NO_FLUSH, FLUSH_LIMIT (delayed refs, delayed inodes) and EMERGENCY
reservations draw on it like relocation does.  They are bounded and
kernel-internal; user reservations (FLUSH_ALL, ALL_STEAL, EVICT, DATA)
still stop at the reserve.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_meta: do not steal from the global reserve below the relocation reserve
Zygo Blaxell [Sun, 6 Sep 2026 05:14:43 +0000 (01:14 -0400)]
btrfs: stripe_meta: do not steal from the global reserve below the relocation reserve

With the whole-stripe reserve in place the fill stopped cleanly at the
edge: 374 MiB claimable, the reserve plus margin plus the outstanding
reservations, no canary, no abort.  Then the test deleted half of its
tiny files, and every unlink that the gate refused came back through
the global reserve steal, which does not look at stripes.  Deleting an
inline file is pure COW churn under stripe_meta: it frees no stripe,
since the leaf it lived in stays, and each transaction of unlinks takes
fresh stripes.  Metadata used did not move by a megabyte while the
claimable supply went from 374 MiB to zero, and the next commit aborted
with the canary ("claimable 0 open 1490944 trapped 1506082816").

Refuse the steal once the claimable supply is down to the relocation
reserve plus the claim margin.  The unlink fails with ENOSPC, which is
recoverable, instead of the abort, which is not, and the reserve stays
with the background relocation that is the only thing that turns
trapped space back into whole stripes.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_meta: hold back whole stripes so tree blocks always land and trapped...
Zygo Blaxell [Sun, 6 Sep 2026 02:58:50 +0000 (22:58 -0400)]
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
12 days agobtrfs: stripe_meta: back the global reserve with usable stripes, and say when a tree...
Zygo Blaxell [Sat, 5 Sep 2026 09:59:56 +0000 (05:59 -0400)]
btrfs: stripe_meta: back the global reserve with usable stripes, and say when a tree block finds none

With trapped and open-run bytes counted as used in the METADATA
space_info, the overcommit check already admits tree block reservations
only against whole free stripes plus unallocated space, so no separate
metadata gate is needed.  Two loose ends remain.

btrfs_update_global_block_rsv() forces a chunk allocation when the
global reserve is at least the space_info's total, but under stripe_meta
the reserve can only be spent in whole stripes: compare it against the
capacity minus the trapped bytes so a metadata chunk is allocated while
unallocated space still exists, instead of the reserve running dry at
the fill edge.

And when a raid56 stripe_meta tree block allocation does fail with
ENOSPC -- which aborts the transaction -- print the stripe counters,
the metadata twin of the data-side "returned ENOSPC despite reservation
margin" warning, so an accounting hole is identifiable from the log.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_meta: maintain trapped free space for metadata groups like data
Zygo Blaxell [Sat, 5 Sep 2026 09:50:29 +0000 (05:50 -0400)]
btrfs: stripe_meta: maintain trapped free space for metadata groups like data

btrfs_block_group_init_stripe_unusable() arms stripe_meta groups and adds
their trapped bytes to the METADATA space_info, but the disarm, rescan
and commit-time scan only accept btrfs_is_stripe_alloc_bg() (DATA), so
a metadata group's contribution was frozen at its first value: never
updated as tree blocks were freed inside stripes, never dropped when the
group went read-only or was removed.  Tree blocks can only be placed in
fully free stripes, so the trapped bytes are as unusable as for data and
need the same maintenance.

Accept stripe_meta groups on the three maintenance paths, let the commit
scan sum METADATA space_infos, and apply the read-only subtraction to
the metadata branch of inc_block_group_ro() as well.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: relocate a queued stripe group even while it has reserved or...
Zygo Blaxell [Sun, 6 Sep 2026 05:14:31 +0000 (01:14 -0400)]
btrfs: stripe_alloc: relocate a queued stripe group even while it has reserved or pinned bytes

The reclaim worker defers any group that has bytes reserved or pinned,
on the theory that it was queued for falling below the used threshold
and may be about to go empty on its own.  A stripe_alloc or stripe_meta
group is queued for the opposite reason -- free space trapped in
partially used stripes, which no amount of waiting frees -- and under
COW churn a metadata group has blocks reserved or pinned at every
moment, so the worker skipped every one of seven queued metadata
groups for the whole of a fill-to-the-edge test and relocated none.
Relocation already waits for in-flight allocations and writers on its
own; take the group.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: re-arm reclaim from the cleaner, and only stamp a mark that...
Zygo Blaxell [Sun, 6 Sep 2026 18:23:37 +0000 (14:23 -0400)]
btrfs: stripe_alloc: re-arm reclaim from the cleaner, and only stamp a mark that landed

The trapped-space trigger lives in the commit-time rescan of groups whose
free space changed, and stamps the five-minute hysteresis whenever it
decides to mark a group.  Two gaps followed.  A group already on another
list cannot be linked to the reclaim list -- a chunk created in the
running transaction is still on trans->new_bgs -- so the mark was lost
while the stamp held it back for five minutes.  And on an idle
filesystem there is no commit, so nothing rescans: the metadata fill test
watched an eligible 63 MiB group sit unrelocated at the edge for fifteen
minutes with the worker running for others.

Stamp the hysteresis only when the mark actually lands, and re-evaluate
every stripe group from the cleaner, which the transaction thread wakes
every cycle whether or not a transaction exists, with the same predicate
(factored out as btrfs_stripe_bg_wants_reclaim()) and hysteresis.  The
worker re-checks whatever it takes, so a stale read here costs at most
one wasted attempt.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: judge a group's trapped space against its free space, not its...
Zygo Blaxell [Sat, 5 Sep 2026 21:37:40 +0000 (17:37 -0400)]
btrfs: stripe_alloc: judge a group's trapped space against its free space, not its length

Zoned reclaim compares unusable bytes with the zone's capacity, which
works because a full zone is all used or unusable.  A stripe_alloc group
also holds claimable whole stripes, and in practice fragmentation
settles around two thirds of the group trapped with a fifth used and
the rest claimable: nothing left worth writing into, yet below any
sensible fraction of the length, so the trigger never fired (65%
trapped, 19% used, zero relocations in fifteen minutes).

Compare the trapped bytes with the group's free space (length minus
used) instead, keeping the cheap-to-move condition on used bytes.  With
the same 75% threshold the settled state above is 80% of free space
trapped and is reclaimed.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: do not re-queue a trapped group for reclaim every commit
Zygo Blaxell [Sat, 5 Sep 2026 17:55:33 +0000 (13:55 -0400)]
btrfs: stripe_alloc: do not re-queue a trapped group for reclaim every commit

The reclaim trigger runs at every commit-time rescan, so a group that
stayed above the threshold because its relocation could not get space
(or was still in progress) was handed to the worker again and again:
258 "relocating block group" messages in 24 minutes on one filesystem
while the trapped total moved once.  Remember when a group was last
queued and leave it alone for five minutes.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: only queue a trapped group for reclaim when it can be relocated
Zygo Blaxell [Sat, 5 Sep 2026 12:48:33 +0000 (08:48 -0400)]
btrfs: stripe_alloc: only queue a trapped group for reclaim when it can be relocated

The reclaim trigger marked a group at every commit rescan while it
stayed above the threshold, and at the fill edge relocation itself
failed for lack of whole stripes ("allocation failed ... relocation: 1"),
so the same group was retried each commit while writers were starved by
the read-only transitions.  Require room for the group's live data in
the rest of the claimable supply, after the bytes already promised to
admitted writers (bytes_may_use plus the held margin), before queueing
it, which also stops
the retry loop: a group that cannot be relocated is left alone until
space is freed.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: hand groups full of trapped free space to the reclaim worker
Zygo Blaxell [Sat, 5 Sep 2026 09:50:29 +0000 (05:50 -0400)]
btrfs: stripe_alloc: hand groups full of trapped free space to the reclaim worker

Zoned filesystems mark a block group for reclaim as soon as its
unusable bytes reach bg_reclaim_threshold percent of its capacity, so
space that can no longer be written is recycled without an operator
running balance.  stripe_alloc had no equivalent: trapped tails
accumulated until balance (1.86 GiB of 6.5 GiB after a few hours of
churn), while every byte-based check still counted them as free, and
the existing reclaim predicate keys on used bytes only.

At the commit-time rescan, where stripe_unusable is authoritative, mark
a group for the existing reclaim worker when its trapped bytes reach
the threshold and its live data is below it; the worker re-checks with
should_reclaim_block_group().  bg_reclaim_threshold defaults to 0 on
non-zoned filesystems, so nothing changes until the sysfs knob is set;
the default is left for review.  Relocation needs inc_block_group_ro()
to succeed, hence the previous fix.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: let a data write fall back to the relocation group rather than...
Zygo Blaxell [Sun, 6 Sep 2026 04:10:38 +0000 (00:10 -0400)]
btrfs: stripe_alloc: let a data write fall back to the relocation group rather than drop

Dedicating a group to relocation takes its claimable stripes away from
every other writer at once.  Admission stops counting them from then
on, but a write admitted a moment earlier against exactly those stripes
still has to be placed, and with the group hard-excluded it was not:
ten milliseconds after "relocating block group 2976120832" a 16K
writeback allocation returned ENOSPC and its data was dropped, the one
drop left in a fill-to-the-edge run under reclaim.  The read-only
transition has a guard for this; the dedication had none.

Keep the exclusion for the allocator's first passes and lift it once
the search reaches the chunk-allocation loop, i.e. only when no other
group can serve the write.  The class tag on the stripe runs already
guarantees the two sides never share a stripe, so the fallback costs
relocation a few stripes, not its correctness.  Zoned's hard exclusion
follows from append-only zones, which do not apply here.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: admit relocation against the whole claimable supply, not only...
Zygo Blaxell [Sun, 6 Sep 2026 03:40:50 +0000 (23:40 -0400)]
btrfs: stripe_alloc: admit relocation against the whole claimable supply, not only its dedicated group

Keeping the relocation group's stripes out of everyone else's admission
supply also confined relocation's own admission to that group.  The
allocator dedicates the first group it visits, which can hold a couple
of free stripes, and once those are short of a reservation's whole
stripes plus one the gate refused every further cluster while the
allocator, which would have dropped the dedication and moved on had it
been asked, never got to run: the reclaim worker failed each queued
group with ENOSPC within fifteen milliseconds, retried every thirty
seconds, with four hundred megabytes of whole stripes free elsewhere
and no relocation ever completing.

Admit relocation against the whole claimable supply.  The dedicated
group's bytes stay out of reach for everyone else, and bytes_may_use
is one counter for both sides, so the sum admitted still fits in the
supply.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: keep the relocation group's stripes out of the admission supply
Zygo Blaxell [Sun, 6 Sep 2026 00:06:01 +0000 (20:06 -0400)]
btrfs: stripe_alloc: keep the relocation group's stripes out of the admission supply

do_allocation_stripe() dedicates one block group to data relocation
(fs_info->data_reloc_bg, as the zoned allocator does): while a
relocation runs, its allocations go only there and everyone else's skip
it, until the relocation finishes or the group fills.  The admission
gate did not know: it counted that group's claimable stripes in the
supply for ordinary writes.  At the fill edge with background reclaim
running, the dedicated group was the only one with claimable stripes
left, ordinary writes were admitted against them, and their writebacks
were dropped:

  stripe_alloc DROP DUMP: sinfo may_use 3837952 claimable 561053696 ...
    bg 7048265728 ... claimable 561053696 ... runs open   <- data_reloc_bg
    every other group: claimable 0

Track the dedicated group's claimable bytes in the space_info, kept in
step by the claim path, the commit-time rescan, the read-only disarm
and the set/clear of the dedication, and gate ordinary reservations on
the supply without them.  Relocation's own reservations are gated on
the dedicated group's supply while one exists, since that is all it
may use.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: charge relocation a margin per reservation, not per extent
Zygo Blaxell [Sat, 5 Sep 2026 18:50:03 +0000 (14:50 -0400)]
btrfs: stripe_alloc: charge relocation a margin per reservation, not per extent

The stripe margin is one full stripe per outstanding extent, collateral
for the tails a commit traps under delalloc that has not been placed
yet.  The data relocation inode charged it too, for extents that are
already allocated: relocating a group of 16-48K files inflated
bytes_stripe_margin to 150 MB, more than the claimable supply, so the
next cluster reservation failed at once and the reclaim trigger retried
the same six groups 130 times in 15 minutes while the margin swung
between zero and 150 MB with nothing dirty.

Do not charge the per-extent margin for the data relocation root.
Instead, when a relocation reservation is checked against the
whole-stripe supply, round it up to whole stripes and add one stripe of
collateral, which is what relocation's own open runs can lose at the
next commit.

Assisted-by: Claude:claude-fable-5
12 days agobtrfs: stripe_alloc: give a queued stripe group a chunk to relocate into when the...
Zygo Blaxell [Mon, 7 Sep 2026 11:33:59 +0000 (07:33 -0400)]
btrfs: stripe_alloc: give a queued stripe group a chunk to relocate into when the supply is short

A relocation data reservation never takes a ticket, so the flush state
machine never runs for it, and with it goes the chunk allocation every
other data reservation gets when its space_info is full.  That did not
show while other groups still had whole stripes.  Once the reclaim
worker had emptied and removed five of seven data groups, the two left
were the trapped ones it was queued to move next, their claimable
supply was under a megabyte, and every relocation failed with ENOSPC
while 2.6 GiB of the devices sat unallocated.

Have the reclaim worker allocate one chunk before it relocates a stripe
group whose live bytes exceed the claimable supply of the other groups.
An earlier version did this from the reservation path instead, for any
refused relocation reservation; that let a live device shrink -- which
relocates into space it does not have and is meant to fail cleanly at
that reservation -- grow the data space_info chunk by chunk until the
raid1 metadata had nowhere to go and the relocation aborted the
transaction in merge_reloc_roots() (6.18 acceptance suite T6).  The
worker is the one caller that should be making room.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: never let relocation wait on the whole-stripe gate
Zygo Blaxell [Sat, 5 Sep 2026 16:16:00 +0000 (12:16 -0400)]
btrfs: stripe_alloc: never let relocation wait on the whole-stripe gate

The data admission gate refuses reservations that would not fit in the
whole-stripe supply, and a refused reservation waits as a flush ticket
for space to appear.  Relocation reserves its prealloc clusters through
the same path, so at the fill edge the reclaim worker sat for half an
hour in

  btrfs_reclaim_bgs_work -> btrfs_relocate_chunk -> relocate_block_group
    -> relocate_file_extent_cluster -> prealloc_file_extent_cluster
    -> btrfs_alloc_data_chunk_ondemand -> __reserve_bytes

holding the exclusive operation and the group it was emptying, while the
gate waited for exactly the stripes that relocation was supposed to
free.  User writers queued behind that head ticket and unmount hung on
their writeback.

Exempting relocation from the gate altogether is wrong too: it then
takes whole stripes that admitted writers were promised, and their
writebacks are dropped at the edge.  Give relocation its own flush type,
BTRFS_RESERVE_FLUSH_DATA_RELOC, that is subject to the same bound but
never waits: if the reservation does not fit beyond the bytes already
promised it fails at once, the relocation is abandoned cleanly and is
retried when the trigger next finds room.  Zoned relocation already
has a flush type of its own for the same reason.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: keep the admitted bytes placeable when a group goes read-only
Zygo Blaxell [Sat, 5 Sep 2026 12:17:26 +0000 (08:17 -0400)]
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
2 weeks agobtrfs: stripe_alloc: do not count trapped free space twice when making a block group...
Zygo Blaxell [Fri, 4 Sep 2026 23:13:05 +0000 (19:13 -0400)]
btrfs: stripe_alloc: do not count trapped free space twice when making a block group read-only

inc_block_group_ro() asks the rest of the space_info to absorb a data
block group's free space before marking the group read-only.  With
stripe_alloc, the part of that free space that is trapped in partially
used stripes is already counted as used (bytes_stripe_unusable), and
the group's contribution is dropped again once it is read-only.  The
check therefore demanded room for those bytes twice, and a fragmented
raid56 data group could never be made read-only.

RAID56 groups must be read-only for scrub, so on a filesystem whose
data groups had been churned (2.98 GiB group, 51% used, 1.47 GiB of
the remainder trapped) every device's scrub failed with

  BTRFS warning: scrub: failed setting block group ro: -28

while the same filesystem without stripe_alloc scrubbed at 100% used.
Device replace takes the same path.  Freeing space made scrub work
again.

Subtract the group's own trapped bytes from the space it needs to
find elsewhere.  The counter may lag the space_info total between
commits; the check is a buffer heuristic, and the bound by num_bytes
keeps it from going negative.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_meta: allocate tree blocks by whole stripes
Zygo Blaxell [Sun, 9 Aug 2026 22:05:48 +0000 (18:05 -0400)]
btrfs: stripe_meta: allocate tree blocks by whole stripes

Metadata gets the placement rule data already has: tree blocks are handed
out from runs of fully free stripes, and a stripe is never revisited once
the transaction that filled it has written it.  No separate mount option: the
chunk profile already decides it.  raid56 metadata has the same write hole
as raid56 data, so it gets the same protection at the same time, and
metadata on any other profile has no write hole to close -- the policy's
RAID56_MASK test simply does not match it, so nothing changes there.

It does cost a stranded stripe tail per commit, and a metadata allocation
that fails aborts the transaction rather than failing one write.  Those
are the price of covering raid56 metadata at all rather than a reason to
make it opt-in separately: a filesystem unwilling to pay them does not
want raid56 metadata, and converting it to raid1c3/raid1c4 removes both
the cost and the write hole.

Only system chunks are left uncovered: btrfs_is_stripe_meta_bg() tests the
METADATA flag and a system chunk does not carry it, so a raid56 system
chunk keeps the legacy read-modify-write.  The mount-time warning is
narrowed to say exactly that instead of claiming metadata is uncovered,
which it no longer is.

The machinery is the data machinery, including the per-sector liveness
map the previous patch gave it.  Metadata needed three things of its own:

 - A completion report.  A run drains when every byte allocated from it
   has been accounted for, and metadata had no equivalent of ordered
   extent completion, so its runs could never drain -- the deadlock that
   mixed block groups hit.  end_bbio_meta_write() now reports, and
   btrfs_open_stripe_write_abandoned() covers a tree block freed before
   it was ever written (COWed twice in one transaction, say).

 - A two-phase closure.  btrfs_retire_open_stripes() runs before the
   commit writes tree blocks, so it skips metadata runs; they are closed
   and drained afterwards by btrfs_retire_meta_stripes(), once every block
   is on disk.  Closing earlier would strand blocks the commit has not
   allocated yet, draining earlier would wait for writes it has not
   issued.  Metadata runs are also freed there rather than when their
   counter reaches zero: a stripe's columns can be written by separate
   rbios, and freeing on the first completion leaves the second with no
   run to pad or merge against.

 - Whole stripes per writeback pass.  The opportunistic writeback passes
   do not know about stripes and will happily write three tree blocks now
   and five later; btrfs_defer_stripe_meta_write() leaves the blocks
   dirty for the commit (or a WB_SYNC_ALL waiter) instead, the same shape
   as the zoned write-pointer deferral.

Two free paths bypass the usual completion reporting and must report the
block abandoned themselves: btrfs_free_tree_block() can send a
same-transaction unwritten block through the delayed-ref machinery when
check_ref_cleanup() finds its ref head already processed, and
clean_log_buffer() discards unwritten log tree blocks via
btrfs_pin_reserved_extent() without ever visiting btrfs_free_tree_block().
Either miss leaves the block counted as inflight forever: the run never
drains, the block group reference never drops, and unmount asserts.  Both
were found by the dm-log-writes replay matrix, whose crash-recovery mounts
exercise both paths routinely.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: cache a failed stripe run scan per block group
Zygo Blaxell [Sun, 16 Aug 2026 07:54:18 +0000 (03:54 -0400)]
btrfs: stripe_alloc: cache a failed stripe run scan per block group

On a large aged filesystem (85 TiB, mostly legacy pre-stripe_alloc data,
heavily fragmented free space), the delalloc workers spin at 100% CPU
while commits crawl at KiB/s.  NMI backtraces put the time in
btrfs_claim_free_stripe_run() called from btrfs_alloc_from_open_stripe()
for every small compressed extent allocation.

The cost is in find_free_stripe_run_slow(): when the single-entry fast
path misses (always, on fragmented block groups), the slow path walks
candidate stripes from the START of the block group, doing an rbtree
lookup per free-space piece, all under ctl->tree_lock.  When the block
group has no claimable run at all - the common case for block groups
filled with legacy data - the scan traverses the entire block group,
returns -ENOSPC, and remembers nothing: the next 4K allocation repeats
the whole scan.  Per-allocation cost is O(block group size) in the
fragmented worst case, multiplied by every block group find_free_extent
visits.

The stock allocator avoids exactly this pathology by caching the largest
extent a failed search saw (max_extent_size); the stripe claim path is a
new search primitive built beside it and never inherited the idea.  The
per-block-group stripe_claimable counter cannot serve this role: it is a
commit-scan-granular byte estimate that can err high (block groups where
the accounting promises claimable bytes but the scan finds no aligned,
usable run are precisely where the scan storm happens).

Add bg->max_claimable_run, an upper bound learned from the scans
themselves: (u64)-1 unknown, 0 after an exhaustive scan proved no
claimable run exists (find_free_stripe_run_slow() accepts any run of at
least one full stripe, so its failure is that proof).  Claims fail in
O(1) while the bound is below one full stripe, or when total free space
in the block group is smaller than a full stripe.  Adding free space
resets the bound to unknown in __btrfs_add_free_space(), which covers
all paths that can create a run: frees, unpins, and stripe run returns.
The bound is only trusted and only set once the block group is fully
cached, because cache loading links entries directly and would bypass
the reset.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: gate data admission on claimable whole-stripe supply
Zygo Blaxell [Fri, 14 Aug 2026 01:12:02 +0000 (21:12 -0400)]
btrfs: stripe_alloc: gate data admission on claimable whole-stripe supply

Wire stripe_claimable into the DATA reservation path so write() returns -ENOSPC
when there is no fully-free whole stripe to place the write crash-safely --
matching statfs f_bavail, which already subtracts the trapped partial-stripe
space.  Add stripe_claimable_admit() and AND it into the two data-admit sites:
__reserve_bytes() and btrfs_try_granting_tickets() (the latter is required, or a
flushed data ticket would be granted by the plain used<=total rule, bypassing the
gate).  Scoped to raid56 stripe_alloc DATA (stripe_margin_unit != 0); metadata,
non-raid56 and zoned space_infos are unaffected.

The gate refuses at reservation time, before the range becomes delalloc and later
parks at writeback with nowhere to land -- so ENOSPC is clean and there is no
fill-edge parking collapse.

Phase 1 is pessimistic: it gates on bytes_stripe_claimable alone, which errs LOW,
so it can refuse a write that would have landed in a partially-open stripe
(over-refuses by the open remainder, healed by the commit rescan raising claimable
and the FLUSH_DATA ticket retry).  A later change adds bytes_stripe_open to the
bound for statfs-exact behavior.

Assisted-by: Claude:claude-opus-4-8
2 weeks agobtrfs: raid56: adaptive park deadline from the parked-rbio backlog
Zygo Blaxell [Tue, 11 Aug 2026 06:53:38 +0000 (02:53 -0400)]
btrfs: raid56: adaptive park deadline from the parked-rbio backlog

Full-stripe batching parks a sub-stripe write's rbio for a deadline so the
writes that fill the rest of its stripe can merge into it, turning a
read-modify-write into one full-stripe write.  The deadline is a fixed
per-filesystem value.  It is long enough to catch those merges, but at a
small-file fill to ENOSPC -- where a run's stripes never fill because each
file's neighbours land in the next file's stripe rather than this one --
every partial write waits the deadline out, and then its 10x stuck cap, on
an arrival that never comes before padding to a full stripe and going down.
Lowering the deadline globally fixes that fill but throws away the merges
the deadline exists to catch on ordinary moderate writes.

Feed the deadline back from the parking machinery's own backlog instead of
guessing at the workload.  Track stripe_parked_now, the number of
currently-parked rbios, incremented as a partial write parks and
decremented as it unparks.  When it runs ahead of the stripe_park_congestion
knob (0 disables it, the default), a new async partial write takes the short
sync deadline through the existing sync-park path rather than the full one.
This is negative feedback: the shorter deadline drains the backlog, so once
it falls back below the knob later writes regain the full deadline and full-
stripe batching.  No space-state estimate is needed -- a small-file flood
builds the backlog directly, while a large sequential fill (already full
stripes, never parked) and a moderate steady stream do not.

Measured on an eight-device raid5.  A small-file balance-reclaim soak that
times out at the default deadline completes at a congestion of 4; on a
throttled moderate stream, where the backlog stays low, a congestion of 32
never trips, keeps the full deadline, and merges three times as much with
38% fewer RMW reads as the same filesystem under a flat 3ms deadline.  The
gauge and a congestion_short counter join the stripe_park_stats sysfs file
so the knob can be set from measurement.  Off by default, and no data-path
change: a shortened park still pads or reads exactly as it would have, only
sooner.

Assisted-by: Claude:claude-opus-4-8
2 weeks agobtrfs: stripe_alloc: count claimable whole-stripe supply directly
Zygo Blaxell [Sun, 9 Aug 2026 22:05:44 +0000 (18:05 -0400)]
btrfs: stripe_alloc: count claimable whole-stripe supply directly

Data reservations are admitted against arithmetic -- free space minus
trapped fragments minus open-run remainders minus a per-extent margin --
and under sustained near-full churn the arithmetic and the claim rule
disagree for long enough that admitted buffered writes reach writeback
with nothing claimable left: measured, ~5.2-5.5k reserved writebacks
dropped per fsstress churn run, silently for anything not waiting on
fsync.  Stock refuses the same write()s up front.  Nothing between
"durable by fsync" and "refused by write()" is acceptable when an
operator accident fills the disk.

First step, accounting only: measure the constraint instead of deriving
it.  bg->stripe_claimable counts bytes of fully free whole stripes --
exactly what btrfs_claim_free_stripe_run() can take, the complement of
stripe_unusable within each stripe.  The commit rescan derives it from
the same pass that computes trapped bytes (free minus trapped) and is
the sole upward correction; incremental maintenance under
ctl->tree_lock only ever DEBITS -- removals round their decrement OUT
to every touched stripe, claims subtract exactly what they took.  The
counter can therefore only under-count between commits, never
over-count.

Crediting freed whole stripes incrementally on the add side is
deliberately not done, having been tried and dropped.  Several add
paths re-add free space that a low-level remove never de-credited --
the async discard trim (unlink_free_space / bitmap_clear_bits, then
do_trimming's re-add through __btrfs_add_free_space) and
btrfs_remove_free_space's middle-split tail re-add -- so crediting on
add double-counts and drives the counter ABOVE the authoritative scan.
That is the dangerous direction: once admission gates on this counter,
an over-count admits reservations against phantom supply that writeback
then drops, while an under-count is an early, clean write()-time
ENOSPC.  Debit-only makes over-counting structurally impossible,
whatever a future re-add path forgets to de-credit.  (Reproduced with
compress+autodefrag+discard=async on a legacy-converted raid5
filesystem, incremental ~= 2x scanned; credit_return attribution
confirmed the phantom entered through do_trimming's re-add, not the
open-stripe allocator returns.)

A WARN_RATELIMIT at the rescan catches the dangerous direction anyway
(incremental above scanned), which under a debit-only rule means a
missed consumption site, and localizes it; the clamp to the scanned
value keeps every rescan authoritative regardless.  Rate limited rather
than _ONCE because such a gap recurs on every commit, and reporting
only the first hit hides that it is ongoing.

The space_info aggregate bytes_stripe_claimable follows the
stripe_unusable pattern (incremental between commits, re-totaled from
armed groups at the rescan), shows in the ENOSPC dump and in sysfs.
No admission change yet; that comes once the counter proves accurate
under the fill, churn, balance and reclaim suites.  The by-size
fast-fail in the claim keeps its early exit: the point of measuring is
to make such inputs trustworthy, not to search harder around them.

Data block groups only for now; raid56 metadata and mixed block groups
join when the data counter has settled.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: track per-sector liveness and pad from the map
Zygo Blaxell [Sun, 9 Aug 2026 03:14:23 +0000 (23:14 -0400)]
btrfs: stripe_alloc: track per-sector liveness and pad from the map

Padding used to ask where the run's allocation frontier was, which
cannot see an allocation abandoned below it -- a reservation released, an
allocation the finder discarded, an extent freed before its write was
ever issued.  Runs now carry a bit per sector, set at allocation and
cleared by the new btrfs_open_stripe_write_abandoned() report, and
padding fills exactly the sectors that are dead: this replaces
btrfs_stripe_run_pad_start() with btrfs_stripe_run_pad_mask().  The map
is sized once, with headroom for frontier growth, because the growth
path holds the run lock where nothing may sleep; a run that would
outgrow its map stops growing instead.  A closed run's final stripe
overhangs its shrunk end by construction, and those sectors -- the
returned tail, unclaimable while the stripe holds live data -- are dead
and paddable too.

The abandonment report is what closes a writeback ENOSPC race the
fsync-heavy near-full workload hits: an allocation undone without a
report left phantom inflight bytes and phantom-live sectors, and
reservations admitted against that state failed at writeback
(cow_file_range -28) where stock refuses the write() up front.  With
the report in place the same workload shows zero writeback failures at
every reservation margin setting, including margin disabled.

Claiming also learns an O(1) fast fail: the by-size index walk is
extracted into find_free_stripe_run(), stopping at the first entry whose
largest contiguous free run cannot hold a full stripe -- near-full, where
free space degenerates into many sub-stripe holes, this replaces a scan
of the whole tree on every allocation.

Moved out of the stripe_meta patch, which needs all of this for tree
blocks but introduced it tangled with the metadata machinery; data wants
it on its own.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: complete parked writes on allocation coverage, not clocks
Zygo Blaxell [Sat, 8 Aug 2026 15:27:36 +0000 (11:27 -0400)]
btrfs: stripe_alloc: complete parked writes on allocation coverage, not clocks

A parked partial-stripe write goes down its RMW path when its deadline
expires or a retirement flush kicks it, even when every byte it is
missing below the run's frontier belongs to an allocation whose data IO
is already in flight -- allocation happens at writeback submission, and
the commit's retirement drain waits for exactly those arrivals.  Timing
out such a park buys nothing and costs a stripe read plus a second write
of the same stripe: measured on an 8-device raid5 buffered fill, the
100ms deadline turns ~2200 parked stripes per 2GB into read-modify-
writes (pad_decline_live), and stretching the clock 20x recovers only
14% -- the clock is the wrong instrument.

Replace the clock with a coverage test.  A parked rbio is ready when its
gathered bios cover everything the covering run has allocated inside its
stripe (btrfs_stripe_run_alloc_ceiling); the remainder lies at or past
the frontier, so the existing pad turns it into a single full-stripe
write with no read phase.  Merges check readiness as they land, the park
timer holds unready parks instead of expiring them, and run-retirement
flushes leave unready parks parked: the retirement has already closed
the run (freezing the allocated prefix) and its drain waits on the very
arrivals that will complete them.  Waiter-driven flushes (fsync's
pre-writeback kick, ordered-extent waits) and sync parks keep today's
forced behaviour: a blocked waiter's latency beats a saved stripe read.

A stuck cap (10x the park deadline) bounds the wait when an arrival can
never come: a writeback error abandoned the allocation, or the stripe
was already written once by a forced sync park.  Such parks are forced
down the old path and counted.

New sysfs stripe_park_stats counters: unparked_ready (parks completed by
the coverage test) and stuck (parks forced at the cap).

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: trace why padding declined, at the moment it declined
Zygo Blaxell [Sat, 8 Aug 2026 07:57:22 +0000 (03:57 -0400)]
btrfs: stripe_alloc: trace why padding declined, at the moment it declined

The counters say how often padding refused and broadly why, but not what
the run looked like when it happened, and reconstructing that from the
extent tree afterwards cannot distinguish "the stripe was already
allocated when this write arrived" from "it was allocated shortly after".
Those want different fixes, so record the decision where it is made.

Also count the second refusal, which had no counter at all: the oracle can
allow padding and the sector walk still refuse, because a sector below the
frontier is not covered by this write.  That is the case where another
allocation shares the stripe and its data is not in this rbio -- a
different thing from the oracle finding the whole stripe allocated.

The tracepoint carries the stripe, how much of it this write covers, the
refusal reason, and the run's start/end/frontier/inflight, so a trace says
directly whether the frontier had already run past the stripe when the
write showed up.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: tunable park deadlines, and say why padding declined
Zygo Blaxell [Sat, 8 Aug 2026 06:03:40 +0000 (02:03 -0400)]
btrfs: stripe_alloc: tunable park deadlines, and say why padding declined

The deadline constants are meant to be judged "from measurements rather
than taste", but they are compile-time, so every data point costs a kernel
build and a reboot.  Measuring a buffered fill -- dd, no fsync, so nothing
kicks the park and the backstop is the only exit -- found 1861 of 3984
parks expiring at the 100ms deadline, which is precisely the no-waiter case
the constant exists for and the one with no numbers behind it.

Expose both deadlines as writable sysfs files, clamped to 60s because a
parked rbio holds its stripe lock, with 0 disabling parking for that class
(useful as an experiment in itself):

  /sys/fs/btrfs/<uuid>/stripe_park_timeout_ms       (default 100)
  /sys/fs/btrfs/<uuid>/stripe_park_sync_timeout_ms  (default 3)

Add the counters the stats file cannot supply.  rmw_reads counts only
parked writes that still had to read, so it cannot answer "does this
workload read-modify-write at all"; data_rmw counts every data RMW, which
with allow_rmw empty on a covered block group is the number the
stripe-exclusive claim is about, and it should be zero.

And record why padding refused, because the reasons are different defects:
the rest of the stripe is allocated and its data has not arrived yet
(wait longer, or kick when the frontier advances); no open run covers the
stripe, so the run closed before its own write went down; or the frontier
never reached the stripe at all.  Attributing them turns "some RMW
remains" into a specific thing to fix.

No behaviour change at the defaults.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: raid56: say whether a metadata read-modify-write is a write hole
Zygo Blaxell [Wed, 5 Aug 2026 22:15:06 +0000 (18:15 -0400)]
btrfs: raid56: say whether a metadata read-modify-write is a write hole

meta_rmw counts sub-stripe metadata writes, which is a proxy for exposure
rather than a measurement of it.  A stripe modified in place is only a write
hole if it holds data some completed transaction is relying on; a stripe
that two writes of the *same* transaction happen to split costs an extra
read but risks nothing, because a tear loses that whole transaction anyway.
The counter cannot tell those apart, so it cannot answer the only question
that matters.

The rbio can.  A read-modify-write has already read every column the write
does not cover, so at the point of the report it is holding the stripe's
on-disk contents.  Walk the uncovered tree block positions and read their
headers: a block whose bytenr and fsid match belongs there, and its
generation says which transaction put it there.  Compare that against the
generation of the blocks this write is carrying, taken from the same rbio,
so a transaction committing concurrently cannot skew the verdict:

  meta_rmw_cur   the same transaction's own blocks -- a cost
  meta_rmw_old   an earlier transaction's -- a write hole
  meta_rmw_free  no tree block there at all

System block groups are classified too, not just metadata ones.  A system
chunk carries no METADATA bit, so a flags test excludes it, but it holds
the chunk tree: the same tree block header, the same generation field, and
a worse consequence if a degraded crash tears it, since without the chunk
tree no logical address can be mapped at all.  meta_rmw already counted
system chunks; only the classification skipped them, so their read-modify-
writes were visible as a rate and never as a verdict.  Whether a raid56
system chunk ever rewrites parity over a committed tree block is therefore
an open question that this answers by measurement rather than by reasoning
about how the chunk tree is laid out.

This trusts nothing the allocator says about itself.  The stripe runs, the
liveness map and the drain accounting are all bookkeeping that could be
wrong in the same way twice; the header in the sector is what a degraded
read would actually have to reconstruct.

On a 3-device raid5 filesystem with raid5 metadata, 6000 small files with
periodic syncs and then a third rewritten, plain stripe_alloc reports 249
sub-stripe metadata writes of which 174 rewrite parity over committed tree
blocks.  Adding stripe_meta leaves 37 sub-stripe writes and none of them.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: report read-modify-write of uncovered stripes
Zygo Blaxell [Tue, 4 Aug 2026 19:51:54 +0000 (15:51 -0400)]
btrfs: stripe_alloc: report read-modify-write of uncovered stripes

The mount-time warning says which block groups stripe_alloc does not
cover.  It cannot say whether anything is actually landing there, and on
a filesystem with raid56 metadata that is the interesting question: every
sub-stripe write to an uncovered stripe is a write hole window, where
parity and data reach the disk separately and a crash in between leaves
the stripe unreconstructible.

Report it from the one place that knows the write is going out as a
read-modify-write rather than as a full or padded stripe.  Rate limited,
because a raid56-metadata filesystem does this continuously and the point
is to make the exposure visible rather than to fill the log, and paired
with a meta_rmw counter in the existing stripe_park_stats sysfs file so
the rate can be read off without grepping dmesg.

Silent when stripe_alloc is off: there the whole filesystem works this
way and the user has asked for nothing else.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: warn at mount when raid56 metadata is not covered
Zygo Blaxell [Tue, 4 Aug 2026 06:43:20 +0000 (02:43 -0400)]
btrfs: stripe_alloc: warn at mount when raid56 metadata is not covered

stripe_alloc closes the write hole for raid56 data.  It does nothing for
raid56 metadata, which keeps read-modify-write and keeps the hole, and
on a mixed-block-group filesystem it now does nothing at all.  A user
who mounts -o stripe_alloc on -d raid5 -m raid5 has every reason to
believe the filesystem is covered, and finds out otherwise only after a
crash on a degraded array -- with metadata damage, which is worse than
the data damage they were protecting against.

Say it once at mount, after the block groups are read, so the message
reflects what is actually on disk rather than what was asked for.  A
warning, not an error: the data guarantee is real and worth having on
its own, and a filesystem can be converted to raid1c3/raid1c4 metadata
without unmounting.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: claim fully-free stripes that span free space entries
Zygo Blaxell [Fri, 7 Aug 2026 01:35:56 +0000 (21:35 -0400)]
btrfs: stripe_alloc: claim fully-free stripes that span free space entries

The claim fast path searches single entries, so a fully-free full
stripe whose free space spans an entry boundary -- an extent entry
adjoining a bitmap, or two neighbouring bitmap windows -- was
unclaimable.  stripe_unusable accounting is entry-blind and counts
exactly those stripes as claimable, so admission reserves data against
them; at writeback the claim finds nothing, cow_file_range() gets
-ENOSPC, and the already-dirtied pages are dropped.  full_stripe_len
is not a power of two, so stripe boundaries drift through the fixed
128M bitmap windows and a straddling stripe is a certainty near full,
not a corner case.

Observed live at the raid6 fill edge (rolling-failure, then isolated
by fill-edge-debug with enospc_debug): two 384K stripes, each
straddling a bitmap window boundary, held the space_info accounting
786432 bytes above what the claim could reach, and every writeback
allocation against that phantom margin failed -ENOSPC while ~991MB of
genuinely trapped free space sat in the cache.  The reservation margin
cannot absorb this: the gap is per straddling stripe, not per
outstanding extent.

Add an entry-blind slow path: one offset-ordered walk accumulating
contiguous free coverage across entry boundaries, and a piecewise
removal that runs in the same tree_lock critical section as the find,
so a racing claimer cannot see a half-removed run.  The fast path is
unchanged and still serves the common case; the slow path runs only
after it fails, which is the near-full case where a stranded stripe
matters most.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: pessimistic data reservation margin
Zygo Blaxell [Mon, 3 Aug 2026 07:22:52 +0000 (03:22 -0400)]
btrfs: stripe_alloc: pessimistic data reservation margin

A byte-counted data reservation holds no collateral against the stripe
claim rule.  Between admission at write() time and the allocation at
writeback, the trapped-space picture keeps moving: commits close open
stripe runs and trap their sub-stripe tails, and free space returning
mid-transaction (unpinned deletions, same-transaction frees, drained
run tails, freed reservations) lands in the free space cache as
fragments the claim rule can never hand out but that no counter yet
reflects.  The counters were honest at every instant for NEW
admissions while OLD admissions were left holding air; delalloc
writeback gets no second chance, so the pages were dropped.  Measured
at the fill edge: ~19 MiB of orphaned bytes_may_use with accounting
and allocator in perfect agreement, and in a later round the admitted
may_use+margin exactly equaled the phantom mid-transaction fragments.

Be pessimistic at reservation time and optimistic at allocation time,
the same shape metadata reservations already use:

- bytes_stripe_margin: one full stripe width (fs_info->
  stripe_margin_unit, the widest raid56 data full stripe) per
  outstanding delalloc extent, charged and released inside
  btrfs_mod_outstanding_extents() so it stays exact across delalloc
  merges and splits -- including async compression, where the io-tree
  split hook grows the margin to one stripe per compressed piece,
  matching the true worst case.  Counted in btrfs_space_info_used()
  so it holds back admissions.  The margin exists only for in-flight
  dirty data; steady-state capacity and statfs are untouched.

- Admission probes len + margin through the ticketed FLUSH_DATA path
  (then immediately re-releases the probe, which the delalloc hooks
  re-charge), so a writer that cannot be covered waits for the
  flusher -- commits convert pinned deletions into claimable whole
  stripes -- and receives an honest ENOSPC at write(2) if flushing
  cannot produce cover.

- Every btrfs_add_free_space() into an armed group counts the added
  range's sub-stripe head and tail fragments as trapped immediately;
  whole stripes fully inside the range are trivially fully free and
  stay claimable.  A bounded overcount in the safe direction, never
  an undercount; the commit rescan remains the authority and
  reconciles (and now grants tickets when it lowers the counter,
  since admission waiters may be blocked on exactly that headroom).
  This closes the retire-to-rescan window the previous patch left
  open, and closes it in the one place every returning range passes
  through: counting at the individual close/drain call sites instead
  misses paths (the drain returns have two) and gets the arithmetic
  wrong (round_up() on the non-power-of-two 448K stripe width).

- When a margin-backed data allocation must split across free space
  fragments, every non-final piece is kept a whole-stripe multiple
  (rounddown; the width is not a power of two), so at most one piece
  per delalloc extent can strand a tail -- the margin pre-paid
  exactly one.

- A data writeback allocation returning ENOSPC anyway is loudly
  warned: the margin should make it impossible, and the pages are
  dropped.

With this, the fill-to-ENOSPC harness goes fully clean for the first
time: zero writeback drops, zero warnings, zero allocation failures,
margin drains to zero at idle, and fill capacity is unchanged.  A
writeback ENOSPC retry loop that this replaces is dropped entirely.

Known bounds, accepted: compressed writes are probed one unit per
128M range but charged per 128K piece (probe optimistic, charge
honest); direct IO is probed but carries no margin (its reserve-to-
allocate window is one syscall); a remount toggling stripe_alloc
with dirty delalloc drains the counter (charges are gated on the
mount option, releases are not, and the counter clamps at zero).

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: count open stripe run remainders against data reservations
Zygo Blaxell [Sun, 2 Aug 2026 07:35:36 +0000 (03:35 -0400)]
btrfs: stripe_alloc: count open stripe run remainders against data reservations

Close the remaining reservation-vs-allocation windows the commit-time
stripe_unusable rescan cannot see:

- bytes_stripe_open (new): the sum of open runs' unallocated
  remainders, maintained per block group under stripe_run_lock at
  every open/alloc/grow/close, synced into the space_info after each
  mutation, and counted in btrfs_space_info_used().  Claimed bytes are
  invisible to the free space cache and will either be allocated or
  become trapped tails at close, so reservations must not be admitted
  against them.

A closed run's returned tail is still only counted as trapped by the
commit-time rescan, so reservations can race the retire-to-rescan window
inside a commit and be admitted against freshly trapped tails.  The next
patch closes that window from the free space cache side, where every
returning range is seen and no call site can be missed.

Measured on the fill-to-ENOSPC rolling test before this change (with
only the bytes_stripe_unusable accounting): tree 'b' still lost 3.3%
(8532 blocks) and tree 'c' 30% (62371 blocks) to silent writeback
allocation failure as trapping compounded.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: raid56: skip parking for sync rbios finished by their unplug batch
Zygo Blaxell [Fri, 31 Jul 2026 08:41:19 +0000 (04:41 -0400)]
btrfs: raid56: skip parking for sync rbios finished by their unplug batch

Parking exists to widen the merge window, and for plugged submissions
the unplug callback is the natural end of that window from the
submitter's side: once raid_unplug() has sorted and merged the batch,
nothing more is coming from it.  A sync rbio parked after that point --
a sync(2) or WB_SYNC_ALL sweep, whose whole flush shares one plug --
has a waiter behind it and can only sit out the sync deadline.  Mark
rbios leaving an unplug batch and let sync ones skip parking.

Unplugged sync submissions (the fsync flush) still park: their sibling
bios arrive one by one and merge into the parked rbio, and the fsync
path kicks it as soon as they have all been submitted, so parking there
is the merge mechanism and the deadline already never fires.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: raid56: expose parking statistics in sysfs
Zygo Blaxell [Fri, 31 Jul 2026 08:41:19 +0000 (04:41 -0400)]
btrfs: raid56: expose parking statistics in sysfs

/sys/fs/btrfs/<uuid>/stripe_park_stats reports lifetime counters for
the parking machinery: rbios parked, bytes merged into parked rbios,
and how each park ended (filled to a full stripe, kicked by a waiter or
a settle/retire flush, or expired at its deadline), plus how many
flushed writes were padded to full stripes versus still needing the
RMW read phase.  The filled/kicked/expired split shows directly whether
the deadlines are sized right for a given system -- expired parks that
later reappear as rmw_reads are the batching the window failed to
capture -- so any future retuning (or an adaptive deadline) can argue
from measurements instead of taste.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: raid56: do not park sync writes into nocow runs
Zygo Blaxell [Fri, 31 Jul 2026 05:08:44 +0000 (01:08 -0400)]
btrfs: raid56: do not park sync writes into nocow runs

An in-place overwrite of a nodatacow file under stripe_alloc parked
like any partial write, but with a waiter behind it and nothing worth
merging: in-place overwrites arrive one fsync at a time, and unlike
the datacow fsync path nothing kicks the parked rbio before the page
writeback wait, so every fsync ate the full sync park deadline.
Measured against stock nodatacow on the same rig, that deadline was
the bulk of a ~3.7ms per-fsync regression.  Skip parking for sync
writes whose stripe belongs to a NOCOW-class run; async writeback
still parks and merges there.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: persist nocow runs across commits and remounts
Zygo Blaxell [Fri, 31 Jul 2026 03:51:51 +0000 (23:51 -0400)]
btrfs: stripe_alloc: persist nocow runs across commits and remounts

A nodatacow inode's private stripe run used to close at every
transaction commit like all runs, so a slowly appended nocow file burned
a fresh stripe per commit -- and after a remount its partial stripe's
free tail was abandoned outright.  Neither cost buys anything: the run
machinery's commit-time closing exists for invariant I2, and a nocow
stripe holds only the owner's write-hole-waived data, so there is
nothing for I2 to protect.

Keep NOCOW-class runs open across commits: the commit-time retirement
and its drain predicate skip them (their extents insert without the
window-sequence deferral, which is fine -- the data is on disk when the
ordered extent finishes, and later same-stripe writes can tear only the
owner's own data).  They still close on forced quiescing (read-only,
removal, unmount) and now on the owning inode's eviction, so a cached
but idle inode cannot pin a claimed tail forever.

Across remounts, re-adopt instead: when a nocow allocation's hint
points into a partial stripe that the committed extent tree proves is
wholly owned by the allocating inode, claim exactly the stripe's free
tail (a new exact-range claim that verifies every byte is free before
removing; nothing else can consume free space inside a partially used
stripe, so verify-then-remove cannot race) and continue the run at the
old frontier.  Appends to a nocow file then pack sequentially through
commits, evictions and remounts alike.

The re-adoption lookup must not block.  It runs from
btrfs_alloc_from_inode_stripe_run(), i.e. inside find_free_extent(),
which holds space_info->groups_sem for read, and stripe_extents_owned_by()
does a full btrfs_search_slot() on the extent tree.  The reverse order is
longstanding upstream -- delayed ref processing holds extent tree locks
and then calls find_free_extent(), which takes groups_sem -- so waiting
here closes a cycle.  Both sides only take groups_sem for read, which is
not enough: it is an rw_semaphore and block group creation and removal
take it for write, so a writer queued between the two readers blocks the
second one.  The check cannot be hoisted above the lock, since it is a
question about the specific stripe the allocator has just settled on, but
it does not have to block: it is read-only and advisory, and every caller
already treats "not provably ours" as a reason to decline the
optimisation rather than as a fact about the extents.  So set path->nowait
for the groups_sem caller and let -EAGAIN fall into the existing error
paths; the cost on contention is a re-adoption that does not happen.
btrfs_stripe_nocow_writable() keeps the blocking search -- it runs from
the nocow check with no groups_sem held, and a spurious "not ours" there
would force COW on an extent that does not need it.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: allow-rmw policy, and write-in-place for isolated nocow extents
Zygo Blaxell [Thu, 30 Jul 2026 21:40:29 +0000 (17:40 -0400)]
btrfs: stripe_alloc: allow-rmw policy, and write-in-place for isolated nocow extents

Re-enable write-in-place for nodatacow files and preallocated extents
under stripe-exclusive allocation, with the understood caveat that the
write hole cannot be prevented for data that opts out of COW: an
in-place write RMWs its stripe's parity, so a degraded crash can tear
the stripe.  What CAN be guaranteed is the blast radius: in-place is
permitted only for extents whose full stripes are isolated to the
writing inode, so such a crash can tear only the writing file's own
data -- the nodatacow contract, no worse.

The gate is per extent, decided where nocow eligibility is already
checked: fast path, the stripe belongs to one of the inode's own
NOCOW-class runs (which only ever held its extents); slow path, the
committed extent tree proves sole ownership (single plain data ref,
count 1, matching root and objectid; anything shared, foreign or
metadata fails).  The fully-free claim rule keeps uncommitted foreign
extents out of partially used stripes, so the committed tree is
authoritative.  Extents that fail -- anything allocated before stripe
isolation existed, extents shared through reflink or snapshots,
relocated extents -- simply stay force-COWed, and because their rewrite
is steered into the inode's private NOCOW run, the next overwrite of
the same data passes: legacy nocow files migrate themselves to
isolation in one COW generation, with no tool and no flag day.

Log-commit settling skips NOCOW-class runs: nodatacow data gets no
fsync survival guarantee (its own later in-place writes can always
tear it), so closing the run would trap its tail for nothing.  The
write-hole debug checker skips groups that hosted NOCOW-class runs,
like relocation-used groups, since isolated in-place writes land in
stripes whose runs have drained.

The interface is a word-list policy naming the cases in which
stripe_alloc may permit the legacy unsafe RMW, each independently:

  nodatacow  in-place writes for nodatacow files' extents
  prealloc   in-place writes into preallocated extents
  fsync      waive the close-at-log-commit guarantee: no settling, no
             per-inode LOG steering, no carry-forward; logged stripes
             may be extended and RMWed as before those patches

It is "allow_rmw", not "allow_overwrite": the fsync case overwrites
nothing, but all three permit read-modify-write of stripes that a
degraded crash can then tear.  The nodatacow and prealloc cases require
the per-extent stripe isolation above (the blast radius stays confined
to the writing file); fsync restores the 3a-era exposure where a
degraded crash may cost just-fsynced data, detectably, in exchange for
none of the log-commit costs.

The policy is persistent as the btrfs.stripe_alloc_allow_rmw property
on the top-level root directory, following stripe_alloc's precedent
(applied when the root inode loads during mount, before any user IO),
with a mount option of the same name as a non-persistent override; the
effective policy is their union.  Words are separated by comma, space
or colon -- the mount option form must use colon, since mount splits
options at commas.  Everything defaults off: plain stripe_alloc keeps
forcing COW and keeps the full fsync guarantee.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: isolate nodatacow and preallocated extents by stripe
Zygo Blaxell [Thu, 30 Jul 2026 21:27:24 +0000 (17:27 -0400)]
btrfs: stripe_alloc: isolate nodatacow and preallocated extents by stripe

Preallocated extents and nodatacow files' extents are candidates for
write-in-place, which reintroduces the raid56 write hole for every
stripe such a write touches: the RMW recomputes parity that also covers
whatever else shares the stripe.  Before write-in-place can be
re-enabled for them (a later change; stripe_alloc still forces COW
today), their placement must guarantee the blast radius: such an extent
must never share a stripe with any other file's data.

Steer them into private per-inode stripe runs of a new NOCOW class,
reusing the log-active inode machinery: runs owned by one inode, never
in the shared band slots, found by owner-and-class lookup.  Successive
allocations of the same file pack sequentially into the file's own
stripes; different files, and the datacow/relocation/log classes, never
share a stripe with them.  A preallocation signals itself through a new
btrfs_reserve_extent() parameter; nodatacow files are recognized by the
inode flag.  Placement remains best effort: when no fully-free stripes
are left for a private run the allocation falls back to the shared
runs, which is safe -- an extent that lands in a shared stripe simply
stays force-COWed when write-in-place arrives.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: raid56: pad sub-stripe writes to full stripes in open runs
Zygo Blaxell [Thu, 30 Jul 2026 19:49:41 +0000 (15:49 -0400)]
btrfs: raid56: pad sub-stripe writes to full stripes in open runs

A sub-stripe write does a full RMW: read every untouched data sector of
the stripe, recompute parity, write.  Under stripe-exclusive allocation
the read phase is usually pointless: a stripe covered by a live stripe
run has never been written at or past the run's allocation frontier, so
the sectors being read contain nothing.

When every data sector the rbio does not cover lies at or past the
frontier, zero-fill those sectors instead of reading them and write
them out with the stripe -- the zeros must reach the disk, or the
parity (computed over them) would not match what scrub reads back.  The
partial write becomes one full-stripe write: no read phase, one parity
pass.  A frontier that grows during the attempt is safe: the newer
allocation's write serializes behind this rbio's stripe lock and lands
over the zeros.  If any uncovered sector is below the frontier (already
allocated to someone else), fall back to the normal RMW.

Together with kicking parked rbios before the fast fsync's writeback
wait, this removes both stalls the raid56 layer added to fsync under
stripe_alloc: the park deadline and the RMW read round trip.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: kick parked rbios before the fast fsync's writeback wait
Zygo Blaxell [Thu, 30 Jul 2026 18:16:55 +0000 (14:16 -0400)]
btrfs: stripe_alloc: kick parked rbios before the fast fsync's writeback wait

The fast fsync path waits for page writeback, which completes only when
the raid56 layer writes the data -- but a partial-stripe rbio parks to
collect merges until its sync deadline, and on this path nothing unparks
it before the wait: the stripes are settled only later, in the logging
itself.  The full-sync path does not have this problem because the
ordered extent wait already flushes parked rbios before sleeping.

All of the fsync's writes are submitted before the wait, so nothing more
can merge into its stripes; flush the parked rbios covering the attached
ordered extents instead of sleeping out their deadline.  Removes the
sync park timeout from the fast fsync critical path.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: carry an inode's log tail forward at fsync
Zygo Blaxell [Thu, 30 Jul 2026 07:28:14 +0000 (03:28 -0400)]
btrfs: stripe_alloc: carry an inode's log tail forward at fsync

Closing a log-active inode's private stripe run at each log commit
traps the run's final partial stripe tail every time the inode fsyncs:
a steady fsync stream burns one stripe per log commit until the stripes
free or balance runs.

Reclaim the tails by carrying the partial stripe's live data forward.
At each log commit, the settling walk now computes the closed run's
final partial stripe, and the logging paths record the file ranges of
the logged extents that live inside it on a small per-inode table
(bounded; overflow just means an extent is not carried).  At the
inode's next fsync, before its delalloc flush, each recorded range that
still maps to the recorded disk bytenr is re-dirtied, defrag style
(reserve, reset delalloc state, mark the folios dirty; absent folios
are read back, which is a plain read of settled data).  The flush then
COWs the carried ranges together with the new data into the inode's
current private run, and the old partial stripe empties and frees
whole.

Everything downstream is the ordinary COW pipeline: new extent maps,
ordered extents, checksums, file extent items and delayed refs, and
the log's modified-extents snapshot -- taken inside btrfs_log_inode
after the flush -- picks up the new addresses by itself.  Reflinked
ranges need no special handling: foreign referents keep the old extent
alive in its closed (never again written) stripe, and only the space
reclaim degrades.  A range that was rewritten, truncated, punched or
compressed simply fails the mapping check and stays put.

Crash safety does not regress.  A carried extent's old copy is dropped
through the normal paths: if it was committed, the free pins until the
transaction commits; if it was logged but never committed, its ADD and
DROP delayed refs cancel in cleanup_ref_head(), which also pins
must_insert_reserved heads -- either way the old blocks cannot be
reallocated before the log that references them is superseded.

The settling walk also learned to report the partial stripe through a
widened btrfs_log_settle_stripes() signature (inode and file range
instead of fs_info); compressed extents pass a zero file length to opt
out of carrying while still settling.

The re-dirty reserves with NO_FLUSH.  carry_one_range() takes the folio
locks and the extent range lock before reserving, and a flushing
(ticketed) reservation can sleep in wait_reserve_ticket() until the
flusher makes progress -- but every way forward needs the locks we
hold: FLUSH_DELALLOC has to write back the carried range, which blocks
in __folio_lock() on our locked folio, and a transaction commit (with
flushoncommit) waits for the same writeback.  The whole filesystem then
wedges behind the stuck commit.  Carrying is best effort by design and
the carried range's data is already durable in its old stripe, so on
ENOSPC just skip the carry and let the old stripe free the slow way.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: private per-inode stripe runs for log-active inodes
Zygo Blaxell [Thu, 30 Jul 2026 01:43:12 +0000 (21:43 -0400)]
btrfs: stripe_alloc: private per-inode stripe runs for log-active inodes

Closing the open stripe run covering every logged extent at each log
commit made completed fsyncs crash-safe, but at a placement cost: an
fsync-heavy inode shares the open band runs with every other writer, so
each of its log commits closes a shared run, fragmenting concurrent
write streams and trapping the shared run's tail.

Give log-active inodes their own runs instead.  The first fsync of an
inode sets a sticky runtime flag (at btrfs_sync_file entry, before the
fsync flushes its own delalloc, so even that first fsync's allocations
are steered).  Datacow allocations for a flagged inode come from a
private LOG-class run: claimed like any run but never placed in the
shared band slots, owned by the inode, and found by owner lookup under
the block group's stripe_run_lock.  A per-inode hint seeds the
allocator's search with the run's location; the hint is advisory (a
stale hint costs a lookup miss, never a wrong run, since the owner
match is authoritative).  When no fully-free stripes remain for a
private run the allocation falls back to the shared runs, restoring
the previous placement with unchanged safety.

One inode's log commit now settles only its own stripes: other
writers' runs stay open and their streams stay contiguous, and the
only trapped tails are the fsyncing inode's own.  Private runs also
give each log-active inode exclusive stripes, which a later change
uses to copy live tails forward and reclaim them without touching
foreign extents.

The commit-time retirement closed runs by walking the band slots,
which private runs never occupy; walk the block group's run list
instead so every open run, slotted or private, is closed and drained
under invariant I2 (no open run survives a transaction commit).

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: settle a logged extent's stripes at log commit
Zygo Blaxell [Thu, 30 Jul 2026 00:51:24 +0000 (20:51 -0400)]
btrfs: stripe_alloc: settle a logged extent's stripes at log commit

Stripe-exclusive allocation bounds degraded-crash damage to the current
transaction, but within that window two log commits can still share a
stripe: fsync 1 writes the head of a stripe, the open run keeps filling
it, and a later write's read-modify-write rewrites the parity that
protects fsync 1's data.  A degraded crash during the second write tears
the first -- the write hole's shape, confined to the log window, and the
reason the fsync guarantee has so far been "loss is detectable" rather
than "completed fsyncs survive".

Close it with a case analysis.  A logged extent in a full stripe is
already safe: nothing ever writes a full stripe again, since COW never
overwrites and there are no free sectors left to allocate.  A logged
extent in a partial stripe is exposed only to future writes into that
stripe's remaining sectors -- so at log commit, close the stripe's open
run (nothing further allocates into it), kick any parked partial writes
for it, and wait for its in-flight data IO before the log super is
written.  This is the log-window analogue of invariant I2, which the
commit-time retirement provides for full commits.  Both log paths are
hooked: the fast path per extent map in log_one_extent(), and the
full-sync path where copy_items() walks new data extents (old-transaction
extents are skipped there, and are exactly the ones already settled by
their own commit).

The drain is bounded and join-free: run inflight is counted from
allocation, which happens during writeback with submission following in
the same pass, so the wait is bio flight time plus the parked-write
deadline that the flush short-circuits; write_done reporting needs no
transaction join, so waiting under the inode log mutex and a running
transaction handle is safe.

The cost is spatial: every fsync that logs an extent in a partial stripe
retires that stripe early, trapping its unwritten tail like any other
partially filled stripe until it frees or balance reclaims it.
Fsync-heavy workloads therefore burn a stripe tail per touched stripe
per fsync; the follow-up per-inode log runs and copy-forward relocation
exist to reclaim exactly that cost, and are optimizations on top of the
guarantee this patch completes.

Assisted-by: Claude:claude-fable-5
(cherry picked from commit 53c44fdbca58bd1fa267a5648e236ed4238d99ed)

2 weeks agobtrfs: stripe_alloc: control the policy with a filesystem property
Zygo Blaxell [Wed, 29 Jul 2026 16:59:27 +0000 (12:59 -0400)]
btrfs: stripe_alloc: control the policy with a filesystem property

A mount option is an awkward fit for write-hole protection: it occupies
a mount-option bit, and protection silently lapses whenever the option
is forgotten -- an fstab edit, a rescue mount, a recovery boot -- which
is exactly when a degraded raid56 is most likely to be written.

Control the policy with a "stripe_alloc" filesystem property instead,
following the property system's compression precedent: a btrfs.
namespace xattr, user-visible and admin-controlled, on the top-level
subvolume's root directory.  Set it once (setfattr -n btrfs.stripe_alloc
-v 1, or btrfs property once btrfs-progs learns the name) and it is
persistent: the kernel applies it when the root directory inode loads
its properties during mount, before any user IO.  Deleting the xattr
disables the policy; open runs drain at the next commit's retirement,
which runs unconditionally.  Stray copies of the xattr -- a received or
cloned subvolume -- are ignored: only the top-level root carries the
policy, so receiving a stream from a stripe_alloc filesystem cannot
flip the policy on the destination.

The support checks (free space tree, not zoned, no remap-tree, not
mixed block groups) are enforced both when the property is set and when
it is applied at mount.  The mixed block group test matters most here.
btrfs_check_mountopts() refuses mixed for the mount option, and without
the same test this would be a second way in that skips it -- enabling a
policy the series does not support there, after the block group read
that would have warned about uncovered metadata has already run, so the
user is told the filesystem is protected and it is not.

This stays within the series' no-on-disk-format-change constraint, and
that is the compatibility story: an older kernel mounts the filesystem
read-write and simply uses the legacy allocator, which is fully
compatible because the on-disk layout is unchanged.  New kernels honor
the property during the feature's long-tail testing period; if no use
case surfaces where stripe-exclusive allocation is worse than the write
hole it protects against, it can eventually become the only allocation
mode and the property a no-op.

Known old-kernel interactions with the btrfs. namespace: existing
kernels list and read the xattr (the btrfs. get path is a plain xattr
read) but refuse to set or remove unknown property names, so the flag
can only be managed from a kernel that knows it.  btrfs-progs
interaction (check, property list) with an unrecognized property is a
userspace compatibility item to verify and, if needed, patch.

The mount option is kept for now as a non-persistent override.  Later
protection stages with different risk profiles (the log-tree
full-stripe relocation) should be gated by their own property rather
than widening this one, so their testing exposure can be controlled
independently.

Assisted-by: Claude:claude-fable-5
(cherry picked from commit ecd4d3de8a0493f7c406d919d533f88e8b45390c)

2 weeks agobtrfs: stripe_alloc: never claim stripes covered by a live run
Zygo Blaxell [Wed, 29 Jul 2026 14:35:41 +0000 (10:35 -0400)]
btrfs: stripe_alloc: never claim stripes covered by a live run

The range-to-run lookups -- attaching an ordered extent to its stripe run
and reporting completed data IO by bytenr -- assume that at most one run
on a block group's list covers any given address.  Nothing enforced that.

A reservation that is discarded before anything references it (the
cow_file_range error path under ENOSPC, the find_free_extent backout
paths) returns its bytes to the free space cache immediately, with no
pinning: there is no committed state to protect.  When an ENOSPC failure
storm discards every allocation in a stripe, the stripe is fully free
again and the claim rule -- correctly, by its own lights -- hands it out
as part of a new run while the old run object is still on the list
draining its other stripes' IO.  Full-stripe write batching widened a
run's post-close drain from microseconds to the parked-write timeout, and
the soak test hit the overlap within minutes: new allocations' ordered
extents attached to the old run (first match by range), their completions
drained the old run's inflight accounting into an assertion failure, and
the new run's accounting never drained, wedging the commit's retire wait.
The overlap is harmless to data -- a stripe can only be re-claimed if
every byte of it is free, and discarded reservations never issued bios --
but the accounting corruption is fatal.

Freed committed extents cannot reproduce this: they return to the free
cache only in the unpin phase at the tail of a commit, and the same
commit's retirement already drained -- and freed -- every run opened
before it.  Only the unpinned immediate-free paths race with a draining
run.

Rather than enumerate those paths, enforce the lookups' assumption at the
claim site: btrfs_claim_free_stripe_run() now trims a candidate to end
before the first live run overlapping it, or rejects it if its head
overlaps.  Re-claiming such stripes just waits until the old run drains
off the list, which only comes up inside ENOSPC failure storms.

Reproduced with concurrent fill-to-ENOSPC/delete cycles, balance, and
fsstress on a 4-device raid5: the assertion fired within ~15 minutes
unpatched, and ~10 hours of the same load ran clean with this fix.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: raid56: batch stripe_alloc partial writes into full-stripe writes
Zygo Blaxell [Tue, 28 Jul 2026 15:19:50 +0000 (11:19 -0400)]
btrfs: raid56: batch stripe_alloc partial writes into full-stripe writes

A raid56 sub-stripe write pays a read-modify-write: read the rest of the
stripe, recompute parity, write.  The plug callback merges rbios submitted
within one plug window and the stripe cache saves re-reads for
back-to-back writes, but measurement shows what survives them: on a
4-device raid5, small-file writeback produces 100% sub-stripe rbios (zero
full-stripe writes) and a mixed 4K-512K workload ~55%, so parity is
recomputed and rewritten once per file that lands in a stripe.

Stripe-exclusive allocation gives raid56 something the allocator never
guaranteed before: the rest of an open run's stripe is either written
before the run closes -- within the current transaction -- or not at all.
That makes holding a partial write a sound bet, so park it: a partial
write rbio whose stripe belongs to a stripe run keeps the stripe
lock but does not start its RMW.  Later writes to the stripe merge into
it through the existing lock_stripe_add() path, and the moment its data
bitmap covers the stripe it is submitted as a single full-stripe write:
one parity computation, no reads.

A draining run counts, not just an open one.  A run closes the moment it
is fully allocated -- typically milliseconds before its last writes reach
the raid56 layer -- so gating on "open" would leave exactly those tail
writes unable to park and unable to pad, and they are the bulk of the
read-modify-writes this patch exists to remove.  A draining run is in
fact the safest thing to park against: its frontier is frozen, so
everything below it is inflight IO the commit drain already waits for
(arrival guaranteed) and everything above it is dead space the pad may
fill.

Completion accounting can never trigger the flush of runts -- parked bios
have not been submitted, and preallocated or discarded allocations
produce no bios at all -- so liveness comes from explicit flushes: the
run retirement paths flush before the commit's IO drain (which waits on
the very bios parked here), ordered-extent waiters flush their range on a
short retry period, and a timer bounds everything else.  Writes with a
blocked waiter (REQ_SYNC: fsync- and sync-driven writeback) park with a
~3ms deadline instead of 100ms -- long enough for the rest of one
writeback pass over the stripe to merge in, short enough to stay
invisible in fsync latency -- and a sync bio merging into a parked rbio
pulls the deadline in.  Only the bg->open_stripe slots are consulted for
eligibility, and a parked rbio that a racing retirement closed out from
under is bounded by the timer, never by the drain.

Measured (same 4-device raid5, deterministic workloads, classifying every
write rbio at rmw_rbio):

  workload       full-stripe rbios      total write rbios
  streaming      99% -> 99%             unchanged
  mixed 4K-512K  43% -> 97%             2478 -> 1615
  small files     0% -> 86%             1537 ->  335 (stripe reads 311->23)
  fsync-per-file  0% ->  0%             unchanged (nothing co-pending)

fsync latency is 12ms vs 8ms unpatched (each fsync pays one short park).
Batching also collapses trapped free space in a mixed-size forward fill
from ~87 MiB to ~3 MiB: stripes now fill completely before the commit
closes their run, so the frontier strands almost nothing.

Validated: btrfs selftests; mixed-size + concurrency + churn + balance
workload with trapped-space accounting returning to zero after deleting
everything; scrub and full sha256 read-back over 4005 mixed random files;
and the dm-log-writes write-hole crash matrix (24/24 controls clean, 0/36
armed cases with committed data damage) -- parking does not perturb the
stripe-exclusivity guarantee it rides on.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: grow the frontier run instead of stranding its tail
Zygo Blaxell [Tue, 28 Jul 2026 06:26:50 +0000 (02:26 -0400)]
btrfs: stripe_alloc: grow the frontier run instead of stranding its tail

When an allocation is larger than every open run's remainder, the
allocator claims fresh fully-free stripes and opens a separate run,
leaving the old run's tail behind.  The banded cursors keep that tail
open for a while, but a band collision or the commit eventually closes
it, and the tail -- perfectly usable space that a misfit merely jumped
over -- is stranded in a partially filled stripe.  In a forward fill
this happens at every size upshift, and measurement shows it is where
nearly all of stripe_alloc's trapped space comes from: on a mixed-size
fill it strands ~14% of the data written, a figure that band-granularity
tuning moves by at most a tenth and commit frequency does not move at
all.

Grow the frontier run instead.  If the freshly claimed stripes directly
follow the end of an open run of the same class, extend that run to
absorb them and place the allocation at the run's old tail, spilling
contiguously into the new stripes.  Nothing is stranded, and the extent
is physically contiguous.  In a forward fill the by-size claim naturally
returns the stripes adjacent to the frontier, so the single frontier run
just keeps growing -- the allocator packs the way the stock allocator
does, while keeping stripe exclusivity: the grown stripes were claimed
fully free, the run still closes at the next commit and is never
reopened, so every stripe of the run is filled within one open-run
lifetime.  A grown run keeps its open_seq; it can only still be in a
band slot if the retire walk for its window has not yet run, which the
open_stripe_lock nesting at the claim site excludes from racing.

Measured on a 4-device raid5 with a deterministic interleaved 4K-512K
fill (trapped space via the stripe_unusable counter, byte-identical data
both sides): 111.1 MiB trapped without growing, 7.2 MiB with -- a 94%
reduction, and usable capacity at ENOSPC within 0.5% of the stock
allocator (1561 vs 1569 MiB, vs 1421 MiB without growing).  The benefit
survives churn: with age-correlated deletion the grown runs' contiguous
packing lets whole stripes free together, and steady-state usable
capacity stays at 1486-1561 MiB versus 1284-1421 MiB without growing
across correlated and adversarial small-file deletion patterns.  Data
integrity verified by scrub and full sha256 read-back over mixed
random-content files, and trapped-space accounting still returns to
zero when all files are deleted.

The open-stripe selftest is updated: the misfit allocation now grows the
run and lands at the run's old tail, spanning the stripe boundary, and
the relocation-class case doubles as a check that a claim adjacent to
another class's run never grows it.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: stripe_alloc: keep an open run per size band to trap less
Zygo Blaxell [Tue, 28 Jul 2026 00:55:59 +0000 (20:55 -0400)]
btrfs: stripe_alloc: keep an open run per size band to trap less

A single open stripe run per class forces every allocation that does not
fit the current run's remainder to close that run -- trapping its
unallocated tail in a now partially filled stripe -- and claim a fresh
fully-free stripe.  A workload that interleaves small and large extents
(the common case) therefore strands a tail on every size change, even
though a later small allocation could have filled it.

Keep one open run per power-of-two band of remaining free space instead
(band k holds a run with remaining in [2^k, 2^(k+1)) sectors), per class.
An allocation takes the run in the smallest band that still fits --
segregated best fit -- so a small write lands in an already-open, nearly
full stripe rather than opening a new one, while a large write that no
open run can hold opens a fresh stripe and leaves the smaller runs open
for the small writes that do fit them.  After each allocation the run is
re-placed into the band its new remainder falls in; when two runs collide
in a band the fuller one is kept -- it can serve larger future
allocations, and closing it would trap more -- and the other is closed,
its tail returned to the free space cache.

16 bands cover a full stripe of up to 2^16 sectors, far more than any
raid56 geometry (nr_data_stripes * stripe_len / sectorsize is 158 sectors
at 10 data stripes and a 64K stripe).  Commit-time retirement and the
read-only / removal paths now walk every band of every class; only the
bg->open_stripe[][] slots can ever hold an open run, so one pass still
suffices, and the obsolete single-slot assertions are dropped.

This does not touch the write-hole guarantee: every run is still a
contiguous fill of fully-free stripes, closed at commit and never
reopened.  It only changes which open run an allocation joins, reducing
the free space trapped in partial stripes -- and thus stripe_unusable --
for mixed-size workloads.

The open-stripe selftest is rewritten to the multi-cursor contract: a
misfit allocation now keeps the old run open (its tail is not returned to
the cache) and a later small allocation backfills it, so fewer stripes are
claimed before the free space is exhausted.

Assisted-by: Claude:claude-opus-4-8
2 weeks agobtrfs: account stripe_alloc trapped free space for honest statfs
Zygo Blaxell [Mon, 27 Jul 2026 16:31:14 +0000 (12:31 -0400)]
btrfs: account stripe_alloc trapped free space for honest statfs

Free space in the partially filled stripes of a stripe_alloc (raid56
write-hole-safe) block group is real free space -- it is in the free
space tree and cache -- but the stripe-exclusive allocator cannot hand it
out until the whole stripe frees.  statfs therefore over-reports available
space, promising free space that a later allocation refuses with ENOSPC.

Track this trapped space per block group as stripe_unusable, summed into
space_info->bytes_stripe_unusable.  It is derived, so there is no on-disk
format change and the free space tree and cache are left untouched
(preserving the extent-tree/free-space-tree consistency btrfs check
verifies).

stripe_unusable is defined by a scan that accumulates every free byte of
the block group into a per-stripe array and then sums the stripes that are
partially filled.  A per-stripe accumulator, rather than a streaming sweep
or a per-stripe cache search, is what makes the result correct regardless
of the order the free space cache yields its ranges -- an offset-sorted
bitmap entry can emit runs that lie past a following extent entry, so the
ranges are not globally monotonic -- and regardless of whether free space
is stored as extents or bitmaps.  A debug-build assertion checks that the
scan distributes exactly the cache's free space.  Because trapped space
only settles at commit -- when the retire path returns partially filled
stripes and deleted extents are unpinned -- the scan is recomputed at
commit for groups whose free space changed (flagged cheaply on the
allocation and free paths), which suits the timescale of the reclaim it
feeds far better than a running per-extent tally.  The space_info total is
recomputed as the sum of the armed groups after those rescans, so it
cannot drift.

statfs subtracts bytes_stripe_unusable from the data f_bavail so df
reports what can actually be allocated, and the counter is exposed at
/sys/fs/btrfs/<uuid>/allocation/data/bytes_stripe_unusable.  It is
deliberately not part of btrfs_space_info_used(), so the reservation layer
is unchanged and this carries no ENOSPC-behaviour risk.

The counter is armed once a group's free space cache is loaded, so a group
contributes zero until then and statfs starts optimistic and settles to
honest as groups cache and commit.  It is disarmed when a group turns
read-only (its free space is then accounted as read-only and already
excluded from statfs) and re-armed by a rescan on the way back to
read-write, so a balance both recovers trapped space and updates the
counter.  A CONFIG_BTRFS_DEBUG-only sysfs trigger, stripe_unusable_rescan,
forces a full recompute for auditing the accounting.

Two deliberate imprecisions, matching existing behaviour rather than
bettering it: statfs subtracts bytes_stripe_unusable but not
bytes_zone_unusable, so stripe_alloc df reports availability before reclaim
while zoned df reports it after; and superblock stripes, permanently
unusable in every profile, are left to the existing bytes_super accounting
rather than separately reported here.  Both await a maintainer decision on
a common convention.

Assisted-by: Claude:claude-opus-4-8
2 weeks agobtrfs: stripe_alloc: refuse space_cache=v1
Zygo Blaxell [Wed, 5 Aug 2026 03:33:32 +0000 (23:33 -0400)]
btrfs: stripe_alloc: refuse space_cache=v1

The v1 free space cache inode is nodatacow, preallocated and nodatasum,
so the cache is overwritten in place during commit: a sub-stripe write
into a data block group, landing in whatever committed stripes the cache
occupies, with no csum that could reveal the damage afterwards.  That is
precisely the write this series exists to prevent.

The free space tree is not what stripe_alloc needs -- nothing in it reads
the tree, and the by-size index it allocates from is the in-memory free
space, which exists whatever the on-disk format is.  Having no cache at
all is fine too.  Only v1 has to be kept away, so say that instead of
demanding v2.

Three places, because the cache format can only be converted at mount and
never at remount, so the option alone is not the whole story:

 - btrfs_check_mountopts() rejects space_cache=v1 with stripe_alloc.

 - btrfs_reconfigure() rewrites the cache options after that validation,
   to restore what is on disk.  It has to, given the above.  Re-check
   afterwards rather than assume the options still mean what they did.

 - block group read time refuses to mount when v1 cache inodes are
   present on disk at all.  cache_generation only records whether the
   last mount wrote the cache; the inodes are the durable evidence, so
   btrfs_free_space_cache_v1_present() looks for one under
   BTRFS_FREE_SPACE_OBJECTID in the tree root.  Clearing them is one
   mount away, and the error message says so.

cache_save_setup() also declines to set the cache up while stripe_alloc
is on.  The checks above should make that unreachable, but it is the
point where the in-place write would actually be issued.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: add a write-hole invariant checker to the raid56 write path
Zygo Blaxell [Sat, 25 Jul 2026 15:11:02 +0000 (11:11 -0400)]
btrfs: add a write-hole invariant checker to the raid56 write path

With stripe-exclusive allocation, a raid56 data stripe may only be
written while an open or draining stripe run covers it: after its run
retires and drains at a transaction commit, nothing may ever write to
it again, and a write outside any run means an allocation bypassed the
policy.  Both cases are the write hole about to happen.

Check the invariant (under CONFIG_BTRFS_DEBUG) for every raid56 write
operation, full-stripe and sub-stripe alike, at rmw_rbio() time.  This
turns every upstream violation -- a missed allocation path, a
retirement ordering bug, an accounting leak -- into a deterministic
WARN at the moment of the offending write, instead of silent damage
that needs a crash plus a device failure plus a scrub to observe.  The
bios gathered in an rbio have not reported their IO done yet, so their
runs cannot drain under the check: no false positives from completion
races.

Block groups that ever hosted relocation-class runs are skipped
(sticky, debug-only flag): relocation legitimately overwrites its
preallocated extents in place after their runs drain, and the write
path cannot tell those writes from violations.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: retire stripe runs at commit and gate stripe_alloc
Zygo Blaxell [Sat, 25 Jul 2026 05:35:20 +0000 (01:35 -0400)]
btrfs: retire stripe runs at commit and gate stripe_alloc

Hook stripe run retirement into the transaction commit, right after
TRANS_STATE_COMMIT_DOING stops accepting joins with a single writer
left.  At that point every data extent the transaction references was
inserted by an ordered extent completion that joined earlier, implying
its own data IO is done; the retirement drain waits out in-flight
neighbour writes in the same stripes (a pure data-IO wait, never an
ordered extent wait, which would deadlock on the blocked join).  After
the drain, no stripe this transaction references can ever be written
again, so a crash after the superblock write cannot tear it -- closing
the raid56 write hole for stripe-allocated block groups without
requiring flushoncommit.  A defensive retirement in
btrfs_free_block_groups() covers the transaction abort path.

An ordered extent whose stripe run was opened after the committing
transaction's retire point cannot insert its file extent into that
transaction, so btrfs_finish_one_ordered() ends its handle and waits
for the commit's critical section to end.  That wait must not be taken
under the range's extent lock: buffered writers block on the locked
range in lock_and_cleanup_extent_if_need() while holding their
prepared, locked folios, which stops the writeback flusher and
kcompactd behind those folios, which stops reclaim -- and the
committing transaction is itself draining data bios whose submission
path can need memory (dm-crypt bounce pages on the host where this was
caught).  Drop the extent lock and the FINISHING_ORDERED tag before the
wait and re-take them before re-joining, preserving the
extent-lock-before-join ordering.  The window is safe: the range is
still covered by the pending ordered extent, so a writer that takes the
lock finds it, releases its folios and waits -- which is exactly what
unbinds writeback and reclaim.

Force COW for in-place writes that would land in raid56 data block
groups while stripe_alloc is enabled (nodatacow files and writes into
preallocated extents): a single in-place write could tear a stripe
containing other files' committed extents, making the guarantee
conditional on the whole filesystem's usage.  The check sits in
can_nocow_file_extent(), covering buffered and direct IO with one
choke point, and applies per extent so nocow to non-raid56 profiles
keeps working.  The data relocation inode is exempt: its extents live
in relocation-class stripe runs that never share stripes with other
data, and relocation depends on in-place writes.

Validate the option at mount: it requires the free space tree (the v1
space cache overwrites its data in place during commit), and is
refused with the remap-tree feature (whose relocation writes bypass
the ordered extent accounting) and on zoned filesystems (which have
their own allocator and no write hole).

Mixed data+metadata filesystems are refused too.  The immediate symptom
is a hang: a run drains when it is closed and its inflight_bytes reach
zero, and the only things that subtract are ordered extent completion, a
discarded allocation and a freed reservation.  Metadata has no ordered
extent -- btrfs_alloc_tree_block() allocates with is_data false and
end_bbio_meta_write() reports nothing back to the run -- so a metadata
allocation raises inflight_bytes and nothing ever lowers it, the run
never drains, and btrfs_retire_open_stripes() waits for a completion
that has no code path to arrive from.  The hung task detector stays
quiet, because wait_var_event() sleeps in a state it exempts; the
filesystem just stops.  Reproduced deterministically on
mkfs.btrfs -M -d raid5 -m raid5 mounted -o stripe_alloc: the first sync
after a few hundred small files never returns.

That symptom is not why the refusal is permanent: a later patch
(stripe_meta) gives metadata its own completion report, and the drain
then terminates.  These are why:

 - raid56 deliberately skips csum lookup for mixed block groups, to
   avoid recursing into a metadata read while holding the full stripe
   lock (see the comment above the map_type test in fill_data_csums()).
   That test reads the block group's flags, so in a mixed group it
   cannot tell a data stripe from a metadata stripe and disables
   verification for every one of them.  Stripe-exclusive allocation
   could keep the two apart -- allocation classes already never share a
   run, so never a stripe -- but nothing durable records which class a
   stripe held, and asking the extent tree while holding the full stripe
   lock is the recursion the test exists to avoid.  The recovery paths
   here assume a rebuilt data sector can be checked; in a mixed group it
   cannot.

 - Data and metadata draw on one space_info and one free space pool.
   Data reservations carry a pessimistic whole-stripe margin, metadata
   reservations carry none, and a metadata claim that finds no fully
   free full stripe returns ENOSPC -- which for metadata aborts the
   transaction rather than failing one write.  A data fill can starve
   metadata into an abort, which separate block groups cannot do.

Mixed block groups are a mkfs-time property of small filesystems, where
raid56 is least appropriate and stripe_alloc's trapped space costs
proportionally most, and the option cannot become applicable later.  So
do not carry a half-supported mode.

btrfs_is_stripe_alloc_bg() therefore requires DATA without METADATA as
belt and braces for a block group that somehow reaches the allocator
anyway, and moves out of extent-tree.c's file scope so block-group.c
can share the one copy of the rule.  (Metadata and system block groups
never carried the DATA flag, so they were already excluded.)

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: report stripe run data IO through the ordered extent lifecycle
Zygo Blaxell [Sat, 25 Jul 2026 05:32:11 +0000 (01:32 -0400)]
btrfs: report stripe run data IO through the ordered extent lifecycle

Pair every byte reserved from an open stripe run with exactly one
"write done" report, so commit-time retirement can wait for all data IO
into a window's stripes:

- Ordered extents get a stripe_run pointer, attached at creation by a
  range lookup (cheap: gated on the fs having any stripe runs at all,
  and a run's block group membership is established before its
  allocation returns, so the gate cannot miss).  NOCOW and PREALLOC
  ordered extents write into previously allocated extents, which can
  never lie inside a run claimed from fully-free stripes, and are
  skipped.  The report fires once at IO completion (the IO_DONE moment
  in can_finish_ordered_extent(), before any transaction join, so the
  commit-time drain can never deadlock on a blocked join), with a
  catch-all when an ordered extent is freed without completing IO.

- Reservations freed without an ordered extent ever owning them (error
  paths) report through btrfs_free_reserved_extent(); the one caller
  that frees a range an ordered extent did own (the finish-error path)
  uses btrfs_free_reserved_extent_ordered() to avoid double reporting.

- Preallocated extents never issue data IO and report at insertion;
  this also covers relocation's data inode preallocations.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: add the stripe_alloc allocation policy for raid56 data
Zygo Blaxell [Sat, 25 Jul 2026 05:24:37 +0000 (01:24 -0400)]
btrfs: add the stripe_alloc allocation policy for raid56 data

Wire the open stripe run allocator into find_free_extent() as a per-
block-group policy: with the new stripe_alloc mount option, allocations
from raid56 data block groups go through btrfs_alloc_from_open_stripe()
instead of the clustered allocator, and only ever land in fully-free,
stripe-aligned runs.  Since the loop's terminal LOOP_NO_EMPTY_SIZE
degradation only applies to the clustered path and stripe block groups
ignore empty_size/empty_cluster, the natural terminal behaviour is:
no fully-free stripe in any block group -> allocate a chunk -> ENOSPC.
The availability hint feeds max_extent_size so callers retry with
smaller allocations instead of failing early.

Stripe runs get an allocation class: relocation overwrites its
preallocated extents in place, so its allocations must never share a
stripe with ordinary cow data.  Classes never share a run, and one
block group at a time is softly dedicated to relocation by reusing
fs_info->data_reloc_bg (btrfs_clear_data_reloc_bg() moves from zoned
code to generic code for this; the class tag, not the dedication, is
what carries correctness).  The dedication is dropped when relocation
finishes or the dedicated group runs out of stripes.

Setting a block group read-only now retires its stripe runs (in
btrfs_inc_block_group_ro(), covering scrub, relocation and unused
block group deletion), pairing with the allocator's ->ro check under
the stripe_run_lock so no run can survive into or be created in a
read-only group.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: add open stripe run tracking for stripe-exclusive allocation
Zygo Blaxell [Sat, 25 Jul 2026 04:01:17 +0000 (00:01 -0400)]
btrfs: add open stripe run tracking for stripe-exclusive allocation

Add the in-memory state and lifecycle for "open stripe runs", the
allocation windows of the raid56 stripe-exclusive allocation policy.  A
run is a contiguous stripe-aligned region claimed whole from the free
space cache via btrfs_claim_free_stripe_run() and filled strictly
sequentially by btrfs_alloc_from_open_stripe().  A run closes when it is
exhausted, when an allocation does not fit its remainder, or when the
transaction commit retires it; closed runs are never reopened and their
unallocated tails return to the free space cache, where the fully-free
claim rule makes them unallocatable until the whole stripe frees.  This
is what will guarantee that a full stripe only receives writes within
one commit window, closing the raid56 write hole for these block groups.

Each run counts reserved bytes whose data IO has not completed yet,
maintained under the block group lock and reported back through
btrfs_open_stripe_write_done().  btrfs_retire_open_stripes() implements
commit-time retirement: bump the retire sequence, close every run opened
before it, and wait for their inflight bytes to drain.  It is a pure
data-IO wait, deliberately not an ordered extent wait: it is designed to
run after the committing transaction stops accepting joins
(TRANS_STATE_COMMIT_DOING with a single writer), where waiting for
ordered extent completion would deadlock on the blocked transaction
join, and where every extent the transaction references already has its
own data on disk.  Allocations racing with the commit open runs stamped
with a newer sequence and are neither retired nor waited for; their
extents can only be referenced by the next transaction.  Block groups
with runs are tracked on an fs_info list whose membership is
established before an allocation returns, which is what lets the retire
walk rely on the sequence stamp.

Exercised by a new sanity self-test; the raid56 stripe allocation
policy and the commit hook will be the first non-test users.

Assisted-by: Claude:claude-fable-5
2 weeks agobtrfs: add btrfs_claim_free_stripe_run() for stripe-exclusive allocation
Zygo Blaxell [Sat, 25 Jul 2026 03:58:30 +0000 (23:58 -0400)]
btrfs: add btrfs_claim_free_stripe_run() for stripe-exclusive allocation

Add a free space cache primitive that finds and removes a contiguous,
fully-free, stripe-aligned run of full stripes from a block group.  This
is the building block for a raid56 allocation policy that never issues
sub-stripe writes into stripes containing committed data, closing the
raid56 write hole for datacow writes: because a partially-filled stripe
can never satisfy the fully-free requirement, stripes retired at commit
time become unallocatable without any persistent allocator state.

The search walks the by-size free space index (largest max contiguous
free run first) rather than the by-offset tree: an entry whose largest
contiguous free run is smaller than a full stripe cannot contain a
fully-free stripe, and every following entry is no larger, so the search
stops at the first such entry.  Near-full, where the free space
degenerates into many sub-stripe holes, that is an O(1) fast fail
instead of a scan of the whole free space tree on every allocation --
the dominant cost of the known raid56 near-full allocation slowdown.

Full stripe geometry is relative to the block group start and supports
non-power-of-two stripe widths.  Unaligned head and tail remainders are
returned to the free space cache with their trim state preserved.  Runs
are found within a single free space entry; a fully-free stripe split
across an extent entry and a bitmap neighbour is deliberately not found,
which errs toward missing a claimable stripe, never toward claiming a
non-free byte.

The function is exercised by new sanity self-tests covering aligned and
capped claims, partial-stripe exclusion, head/tail carving, block-group-
relative geometry, bitmap entries, and non-power-of-two stripe widths.
The raid56 stripe allocation policy will be its first non-test user.

Assisted-by: Claude:claude-fable-5
2 weeks agoLinux 6.18.49
Greg Kroah-Hartman [Wed, 2 Sep 2026 12:31:51 +0000 (14:31 +0200)]
Linux 6.18.49

Link: https://lore.kernel.org/r/20260831133359.207714926@linuxfoundation.org
Tested-by: Wentao Guan <guanwentao@uniontech.com>
Tested-by: Brett A C Sheffield <bacs@librecast.net>
Tested-by: Peter Schneider <pschneider1968@googlemail.com>
Tested-by: Florian Fainelli <florian.fainelli@broadcom.com>
Tested-by: Pavel Machek (CIP) <pavel@nabladev.com>
Tested-by: Shuah Khan <skhan@linuxfoundation.org>
Tested-by: Miguel Ojeda <ojeda@kernel.org>
Tested-by: Ron Economos <re@w6rz.net>
Tested-by: Barry K. Nathan <barryn@pobox.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agousb: usbfs: fix use-after-free of usb_device in usbdev_release()
Miguel Peñaranda [Mon, 10 Aug 2026 12:12:09 +0000 (14:12 +0200)]
usb: usbfs: fix use-after-free of usb_device in usbdev_release()

commit 0dd68b5d01d022fc9c5e71c82a82b0a94d3d0671 upstream.

usbdev_release() drops its reference to the struct usb_device before
draining the list of completed async URBs, but that drain path reads back
through the same object: free_async() calls dec_usb_memory_use_count()
for any URB whose buffer came from the usbfs mmap() region, and its first
statement is bus_to_hcd(ps->dev->bus).

After a disconnect the usbfs reference can be the last one, in which case
usb_put_dev() frees the device and the subsequent loop reads offset 80 of
freed memory and uses the result as a struct usb_hcd *, which
hcd_buffer_free_pages() then dereferences.

This is reachable by an unprivileged process that has read/write access to
a /dev/bus/usb node: mmap() the fd, submit one URB with a buffer inside the
mapping, wait for the device to be unplugged, then munmap() and close().
It reproduces on every attempt rather than being a race, because a live
MAP_SHARED vma holds a reference on the struct file, so usbdev_release()
cannot run until the last vma is gone and the freeing branch of
dec_usb_memory_use_count() is always taken.

  BUG: KASAN: slab-use-after-free in dec_usb_memory_use_count+0x3ae/0x410
  Read of size 8 at addr ffff8880122ee050 by task poc/769
  CPU: 1 UID: 1000 PID: 769 Comm: poc Tainted: G    B    6.12.94 #3

  Call Trace:
   dec_usb_memory_use_count+0x3ae/0x410
   free_async+0x2aa/0x4f0
   usbdev_release+0x375/0x460
   __fput+0x3ea/0xb50
   __x64_sys_close+0x86/0x100

  Allocated by task 11:
   usb_alloc_dev+0x55/0xd90
   hub_event+0x2524/0x43d0

  Freed by task 769:
   kfree+0x121/0x360
   device_release+0xd2/0x280
   usb_put_dev+0x23/0x30
   usbdev_release+0x2d8/0x460

Release the device reference after the drain loop instead. Nothing between
the two points requires it to have been dropped.

Fixes: f7d34b445abc ("USB: Add support for usbfs zerocopy.")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-5
Signed-off-by: Miguel Peñaranda <mig.penaranda07@gmail.com>
Reviewed-by: Alan Stern <stern@rowland.harvard.edu>
Link: https://patch.msgid.link/20260810121209.795089-1-mig.penaranda07@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agowifi: mt76: mt7925: ensure tx headroom in usb_sdio_tx_prepare_skb
Devin Wittmayer [Wed, 15 Jul 2026 02:33:48 +0000 (19:33 -0700)]
wifi: mt76: mt7925: ensure tx headroom in usb_sdio_tx_prepare_skb

commit ef3e34874d2332d0f63e72c2c35ce5c93568c125 upstream.

mt7925_usb_sdio_tx_prepare_skb() pushes a TX descriptor and a USB
header onto every skb and assumes the headroom for them is already
there. That holds for locally generated traffic, where mac80211
reserves hw->extra_tx_headroom, but forwarded frames are sent through
ieee80211_8023_xmit(), which does not reserve it. Bridge a wired
interface to an mt7925u AP and the first forwarded frame that arrives
short panics the kernel:

 skbuff: skb_under_panic: len:415 put:4 tail:0x19b end:0x640 dev:wlan1
 kernel BUG at net/core/skbuff.c:212!
 Call trace:
  skb_panic+0x58/0x60 (P)
  skb_push+0x58/0x60
  mt7925_usb_sdio_tx_prepare_skb+0xf8/0x1b8 [mt7925_common]
  mt76u_tx_queue_skb+0xa0/0x1f8 [mt76_usb]
  __mt76_tx_queue_skb+0x54/0xe8 [mt76]
  mt76_txq_schedule.part.0+0x204/0x478 [mt76]
  mt76_txq_schedule_all+0x50/0x80 [mt76]
  mt792x_tx_worker+0x68/0x100 [mt792x_lib]
  __mt76_worker_fn+0x84/0x150 [mt76]

Whether a given setup hits it depends on how much headroom the ingress
netdev leaves in its rx skbs. Reproduced on a Raspberry Pi 5 bridging
onboard ethernet to a Netgear A9000; originally reported on an MT7986
router running OpenWrt. Nick Morrow's testing on a Pi 4 (bcmgenet),
which leaves more headroom, helped narrow the trigger to the ingress
path.

The same bug was fixed on mt7921 by commit 98c4d0abf5c4 ("mt76:
mt7921: don't assume adequate headroom for SDIO headers"), but mt7925
was copied from mt7921 without the fix. Add the same guard here.

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips")
Cc: stable@vger.kernel.org
Link: https://github.com/morrownr/mt76/issues/52
Signed-off-by: Devin Wittmayer <lucid_duck@justthetip.ca>
Link: https://patch.msgid.link/20260715023348.59506-1-lucid_duck@justthetip.ca
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agoUSB: c67x00: fix use-after-free in c67x00_add_iso_urb()
Shuangpeng Bai [Thu, 6 Aug 2026 01:35:02 +0000 (21:35 -0400)]
USB: c67x00: fix use-after-free in c67x00_add_iso_urb()

commit b1e24de475bf2d66fffc9103f3444b783527d55a upstream.

When TD creation fails for the last packet of an isochronous URB,
c67x00_add_iso_urb() gives the URB back before updating the endpoint
scheduling state.

c67x00_giveback_urb() frees the URB private data, and the completion
callback may release the final URB reference. The following accesses to
urbp->ep_data, urb->interval, and urbp->cnt can therefore use freed
memory.

Update next_frame and cnt before giving back the failed final packet,
making the giveback the last operation that uses the URB and its private
data.

Fixes: e9b29ffc519b ("USB: add Cypress c67x00 OTG controller HCD driver")
Cc: stable@vger.kernel.org
Signed-off-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com>
Link: https://patch.msgid.link/20260806013502.322067-1-shuangpeng.kernel@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agoUSB: serial: spcp8x5: drop broken carrier detect support
Johan Hovold [Thu, 6 Aug 2026 13:52:48 +0000 (15:52 +0200)]
USB: serial: spcp8x5: drop broken carrier detect support

commit d37186bd95a07e334447f47274a38a311dad2172 upstream.

The driver does not support modem status notifications and instead used
to fetch the modem status once at open() and subsequently operate on and
report stale state.

As part of fixing this, a call to fetch the status was added to
carrier_raised(), which does not work as that callback must not sleep
(e.g. unlike tiocmget()).

Drop the broken carrier detect support.

Fixes: e1ed212d8593 ("USB: spcp8x5: add proper modem-status support")
Cc: stable@vger.kernel.org # 3.10
Reported-by: syzbot+3b514b87202742f22c44@syzkaller.appspotmail.com
Link: https://lore.kernel.org/all/6a73cea2.01d0871a.3a0d52.000d.GAE@google.com
Signed-off-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agoUSB: serial: option: fix slab OOB read in interrupt URB callback
Jiale Yao [Sat, 25 Jul 2026 16:27:51 +0000 (00:27 +0800)]
USB: serial: option: fix slab OOB read in interrupt URB callback

commit 885d802f544ca7bfa8f3984d94233cce715bb6b3 upstream.

The interrupt URB buffer is allocated in setup_port_interrupt_in() based
on the endpoint's wMaxPacketSize:

    buffer_size = usb_endpoint_maxp(epd);
    port->interrupt_in_buffer = kmalloc(buffer_size, GFP_KERNEL);

When a USB device declares wMaxPacketSize = 8 on its interrupt IN
endpoint, the buffer is allocated from kmalloc-8 cache (exactly
8 bytes).

If the device sends a short packet (actual_length < wMaxPacketSize),
the URB completes with status == 0 and the callback proceeds to read:

    data[sizeof(struct usb_ctrlrequest)]

which evaluates to data[8], accessing 1 byte beyond the allocated 8-byte
buffer. This results in a slab out-of-bounds read.

Fix this by adding the missing bounds check: first verify that the
actual length is large enough to contain the struct usb_ctrlrequest
header before accessing req_pkt->bRequestType and req_pkt->bRequest,
and then verify that there is an additional byte for the modem signal
state before reading data[sizeof(struct usb_ctrlrequest)] inside the
conditional.  Use sizeof(*req_pkt) instead of sizeof(struct
usb_ctrlrequest) for consistency.

Assisted-by: Claude:deepseek-v4-pro
Signed-off-by: Jiale Yao <yaojiale02@163.com>
Fixes: 58cfe9113e48 ("[PATCH] USB: add Option Card driver")
Cc: stable@vger.kernel.org # v2.6.12
[ johan: use dev_err(); split signals declaration and initialisation ]
Signed-off-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agoALSA: usb-audio: Complete cleanup after system-resume errors
Will Porter [Mon, 24 Aug 2026 22:57:57 +0000 (17:57 -0500)]
ALSA: usb-audio: Complete cleanup after system-resume errors

commit 1739a976312e110c93a8dee66a1cdf893a1b187e upstream.

A failed system resume can leave the card unusable until reboot.
usb_audio_resume() jumps to err_out when snd_usb_pcm_resume() or
snd_usb_mixer_resume() fails. The error path skips the out: block, which
restores D0 and decrements chip->num_suspended_intf.

The card stays in SNDRV_CTL_POWER_D3hot, so later control access blocks in
snd_power_ref_and_wait(). USB core logs an interface resume callback error.
It does not retry that callback, so a later callback cannot complete the
skipped cleanup.

usb_audio_suspend() increments num_suspended_intf before returning success.
A system-resume callback must consume the system-suspend count even if a
component resume fails. Otherwise, the stranded count skews later suspend
and resume cycles.

Do not apply this cleanup to runtime-resume errors. Runtime PM can retry
-EAGAIN or -EBUSY without another suspend callback. The count must continue
to describe that suspended interface. Other runtime-resume errors latch
runtime_error in the PM core and do not cause an immediate callback retry.

Both parts of the system-resume error path are longstanding. Commit
88a8516a2128a ("ALSA: usbaudio: implement USB autosuspend") introduced
err_out past the D0 restore. Commit 862b2509d157c ("ALSA: usb-audio: Fix
inconsistent card PM state after resume") later moved
num_suspended_intf-- into the out: block. The error path now skips both
operations.

No third-party code is needed to reach the error path.
snd_usb_mixer_resume() ends in snd_usb_mixer_activate(), which returns the
result of usb_submit_urb() for devices that have a mixer status URB. Its
mixer->private_resume hook can also fail through scarlett2_init_notify().
snd_usb_pcm_resume() issues a SET_CUR request to a UAC3 power domain. It
can return -EPIPE or -EIO when the device stalls the request.

Route a component error through out: only when system_suspend is nonzero.
Continue to return runtime-resume errors through err_out. Later component
resume stages remain skipped. The original error still reaches USB core.
A later transfer can fail if the device did not recover.

I reproduced the system-resume failure on an Audient iD14 MkI with an
out-of-tree diagnostic mixer resume hook. An injected -EIO on the unpatched
core left control readers in uninterruptible sleep in
snd_power_ref_and_wait() until a reboot. With this patch, the same failure
restored control access. A second system suspend and resume also succeeded
after I disabled fault injection.

Assisted-by: Claude:claude-opus-5
Assisted-by: Antigravity:gemini-3.1-pro-high
Assisted-by: Codex:gpt-5.6-sol
Fixes: 88a8516a2128a ("ALSA: usbaudio: implement USB autosuspend")
Fixes: 862b2509d157c ("ALSA: usb-audio: Fix inconsistent card PM state after resume")
Cc: <stable@vger.kernel.org>
Signed-off-by: Will Porter <mrwillporter@gmail.com>
Link: https://patch.msgid.link/20260824225757.26749-1-mrwillporter@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agoALSA: usb-audio: fix OOB write in snd_usbmidi_novation_output()
Marouane El Moufid [Sun, 23 Aug 2026 13:55:48 +0000 (13:55 +0000)]
ALSA: usb-audio: fix OOB write in snd_usbmidi_novation_output()

commit 1035a8f63bae28e498b0e7b5ac91d749844a7158 upstream.

snd_usbmidi_novation_output() lays out a two-byte header at
transfer_buffer[0..1] and passes &transfer_buffer[2] together with a
length of ep->max_transfer - 2 to snd_rawmidi_transmit():

count = snd_rawmidi_transmit(ep->ports[0].substream,
     &transfer_buffer[2],
     ep->max_transfer - 2);

ep->max_transfer comes from the output endpoint's wMaxPacketSize via
usb_maxpacket(). A malformed or malicious device can advertise a bulk
OUT endpoint with a wMaxPacketSize of 1 - the USB core only clamps this
value downwards - so ep->max_transfer becomes 1 and the count argument
becomes -1.

snd_rawmidi_transmit() passes the negative count on to
__snd_rawmidi_transmit_peek(), where "if (count1 > count) count1 = count"
leaves count1 negative; get_aligned_size() keeps it negative for a
byte-stream substream, so the following memcpy(buffer, ..., count1) runs
with a (size_t)-1 length and writes far past the transfer buffer, which
was allocated with usb_alloc_coherent(ep->max_transfer).

This is the same class of bug that was fixed for snd_usbmidi_akai_output()
in commit 0970274613fb ("ALSA: usb-audio: fix OOB write in
snd_usbmidi_akai_output()"); the novation output routine was left
unguarded. Bail out when the endpoint cannot hold the two-byte header
plus at least one payload byte.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Marouane El Moufid <eun0us@espilon.net>
Link: https://patch.msgid.link/178749334830.543645.13722252148340572274@espilon.net
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agousb: core: Strengthen error handling in hub_hub_status()
Griffin Kroah-Hartman [Wed, 22 Jul 2026 08:17:39 +0000 (10:17 +0200)]
usb: core: Strengthen error handling in hub_hub_status()

commit a29496745aa335d97f617385809583241e118610 upstream.

Add additional error handling after the call to get_hub_status() in
hub_hub_status().

get_hub_status() uses usb_control_msg() which does not verify that the
message is the correct length, substituting it for
usb_control_msg_recv() would also solve this issue but increase memory
allocations.

Instead, error handling is copied from the method used in
hub_ext_port_status(), which shares the same flow of logic as
hub_hub_status().

Assisted-by: gkh_clanker_t1000
Signed-off-by: Griffin Kroah-Hartman <griffin@kroah.com>
Link: https://patch.msgid.link/20260722-usb_core_patches_2-v3-1-87622252bfdd@kroah.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agousb: core: Add lock to usb_wakeup_notification()
Griffin Kroah-Hartman [Mon, 13 Jul 2026 15:43:53 +0000 (17:43 +0200)]
usb: core: Add lock to usb_wakeup_notification()

commit e263e18a9e7b1ff3e7301f0801c6ff87c31adfb6 upstream.

Add a spin lock to usb_wakeup notification to prevent a race condition
with dereferencing freed memory. This could be hit by the xHCI driver as
it calls this function from an IRQ and could race with the
hub_disconnect() function, which properly grabs this lock to protect the
state of the device.

Assisted-by: gkh_clanker_t1000
Signed-off-by: Griffin Kroah-Hartman <griffin@kroah.com>
Link: https://patch.msgid.link/20260713-usb_core_patches_1-v1-3-7721c2b33f53@kroah.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agoKVM: s390: vsie: zero stale crypto bits
Christian Borntraeger [Tue, 11 Aug 2026 15:37:36 +0000 (17:37 +0200)]
KVM: s390: vsie: zero stale crypto bits

commit 34d5b5b646c91cfb9338d7a12c955a70ffb8c66b upstream.

When shadowing crypto access bits from a format0 apcb (crycb 0 or 1),
the bits 64..255 are unchanged from whatever is in the vsie page in the
crycb and thus in the apcb. This gives a nested guest potential access
to a device no longer available. Zero out the remaining bits.

Fixes: 6b79de4b056e ("KVM: s390: vsie: allow guest FORMAT-1 CRYCB on host FORMAT-2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Message-ID: <20260811153738.206885-3-borntraeger@linux.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agocrypto: qce - Remove unsafe/deprecated algorithms
Bartosz Golaszewski [Mon, 22 Jun 2026 13:18:09 +0000 (15:18 +0200)]
crypto: qce - Remove unsafe/deprecated algorithms

commit 7e28b0a5c4b7d075b98ce6d8f5290a9d3deb5b92 upstream.

Remove algorithms that are either unsafe or deprecated and have no
in-kernel users that cannot be served by the ARM CE implementations.

AES-ECB reveals plaintext patterns (identical plaintext blocks produce
identical ciphertext blocks) and should not be exposed as a hardware-
accelerated primitive. DES, Triple DES and HMAC-SHA1 have been
deprecated for years.

Remove sha1, ecb(aes), ecb(des), cbc(des), ecb(des3_ede), cbc(des3_ede),
hmac(sha1) and all AEAD variants built on these primitives as well as
authenc(hmac(sha256),cbc(des)). Also clean up the - now dead - code,
flags and constants.

Cc: stable@vger.kernel.org
Acked-by: Eric Biggers <ebiggers@kernel.org>
Tested-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agocrypto: mxs-dcp - fix source scatterlist length access
Thorsten Blum [Sun, 21 Jun 2026 19:26:16 +0000 (21:26 +0200)]
crypto: mxs-dcp - fix source scatterlist length access

commit c5bcb084a9871e5b62afb5f48b60adfa13b5d9f8 upstream.

mxs_dcp_aes_block_crypt() uses sg_dma_len() without mapping the source
scatterlist with dma_map_sg() first. Therefore, sg_dma_len() is invalid
and could return zero or a stale DMA length, causing encryption and
decryption to process the wrong number of bytes when
CONFIG_NEED_SG_DMA_LENGTH=y.

Use the original scatterlist length instead.

Fixes: 15b59e7c3733 ("crypto: mxs - Add Freescale MXS DCP driver")
Cc: stable@vger.kernel.org
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agocrypto: qce - fix CCM AAD buffer underallocation
Md Sadre Alam [Fri, 7 Aug 2026 06:54:54 +0000 (12:24 +0530)]
crypto: qce - fix CCM AAD buffer underallocation

commit 7f2345f47dd189625f657cd72437179ab4170ee1 upstream.

The AAD buffer allocated in qce_aead_ccm_prepare_buf_assoclen()
can be smaller than the length later programmed into the DMA
scatterlist.

The allocation size is currently calculated as:

  ALIGN(assoclen, 16) + MAX_CCM_ADATA_HEADER_LEN

while the DMA length is set to:

  ALIGN(assoclen + adata_header_len, 16)

Since ALIGN() does not distribute over addition, the allocation
can be smaller than the DMA length. For example, when
assoclen = 32 and adata_header_len = 2:

  allocation = ALIGN(32, 16) + 6 = 38
  DMA length = ALIGN(32 + 2, 16) = 48

As a result, the QCE hardware can read beyond the allocated
buffer while computing the CBC-MAC over the associated data.
The extra bytes are folded into the authentication tag,
resulting in an incorrect tag and causing CCM self-test
failures such as:

  alg: aead: ccm-aes-qce encryption test failed (wrong result)
  on test vector 8

Fix the allocation by adding the maximum possible AAD header
length before alignment:

  ALIGN(assoclen + MAX_CCM_ADATA_HEADER_LEN, 16)

This guarantees that the allocated buffer is large enough
for the fully padded AAD data for all supported header sizes.

Cc: stable@vger.kernel.org
Fixes: 9363efb4181c ("crypto: qce - Add support for AEAD algorithms")
Signed-off-by: Md Sadre Alam <md.alam@oss.qualcomm.com>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agocrypto: krb5 - use kfree_sensitive() for derived key buffers
Jan Sebastian Götte [Mon, 3 Aug 2026 19:26:21 +0000 (21:26 +0200)]
crypto: krb5 - use kfree_sensitive() for derived key buffers

commit f7d53dd3f267e46a784f219a75072f2f400d42b9 upstream.

crypto_krb5_prepare_encryption() and crypto_krb5_prepare_checksum()
free the buffer holding the freshly derived keys with plain kfree(),
leaving the key material behind in the freed slab object.

Fixes: 3936f02bf2d3 ("crypto/krb5: Implement Kerberos crypto core")
Cc: stable@vger.kernel.org
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agocrypto: atmel-tdes - use scatterlist length before DMA mapping
Thorsten Blum [Thu, 11 Jun 2026 10:36:35 +0000 (12:36 +0200)]
crypto: atmel-tdes - use scatterlist length before DMA mapping

commit ba199bdaa80b09a7dd92f28751de7f3dbb06c510 upstream.

Using sg_dma_len() is only valid after mapping the scatterlist with
dma_map_sg(). However, atmel_tdes_crypt_start() uses it before mapping
to compare input/output lengths and to compute the transfer count.

Use the original scatterlist lengths before DMA mapping to avoid reading
stale or uninitialized DMA lengths when CONFIG_NEED_SG_DMA_LENGTH=y.

Drop the output scatterlist length in the fast path since it is equal to
->in_sg->length and does not change the transfer count.

Fixes: 13802005d8f2 ("crypto: atmel - add Atmel DES/TDES driver")
Fixes: 1f858040c2f7 ("crypto: atmel-tdes - add support for latest release of the IP (0x700)")
Cc: stable@vger.kernel.org
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agocrypto: qcom-rng - Allow zero as a random number
Eric Biggers [Mon, 8 Jun 2026 17:58:46 +0000 (17:58 +0000)]
crypto: qcom-rng - Allow zero as a random number

commit 4ef04bdc0c9f98836d1638be516f6bf1bad55f69 upstream.

Zero is a valid random number and needs to be allowed.  Otherwise the
output is distinguishable from random.

Fixes: f29cd5bb64c2 ("crypto: qcom-rng - Add hw_random interface support")
Cc: stable@vger.kernel.org
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agocrypto: qcom-rng - Remove crypto_rng interface
Eric Biggers [Mon, 8 Jun 2026 17:58:47 +0000 (17:58 +0000)]
crypto: qcom-rng - Remove crypto_rng interface

commit 2ecdf5c9910e20f73639bc322f0518a3439d17c0 upstream.

qcom-rng.c exposes the same hardware through two completely separate
interfaces, crypto_rng and hwrng.  However, the implementation of this
is buggy because it permits generation operations from these interfaces
to run concurrently with each other, accessing the same registers.  That
is, qcom_rng_generate() synchronizes with itself but not with
qcom_hwrng_read().  This results in potential repetition of output from
the RNG, output of non-random values, etc.

Fortunately, there's actually no point in hardware RNG drivers
implementing the crypto_rng interface.  It's not actually used by
anything besides the "rng" algorithm type of AF_ALG, which in turn is
not actually used in practice.  Other crypto_rng hardware drivers are
likewise being phased out, leaving just the hwrng support.

Thus, remove it to simplify the code and avoid conflict (and confusion)
with the hwrng interface which is the one that actually matters.

Fixes: f29cd5bb64c2 ("crypto: qcom-rng - Add hw_random interface support")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agocrypto: qcom-rng - Enable clock in hwrng case
Eric Biggers [Mon, 8 Jun 2026 17:58:45 +0000 (17:58 +0000)]
crypto: qcom-rng - Enable clock in hwrng case

commit 0fd97bbda2842d7dcccee599ac2c0e9554bdddbc upstream.

Fix qcom-rng.c to enable the clock before accessing the hardware.

Fixes: f29cd5bb64c2 ("crypto: qcom-rng - Add hw_random interface support")
Cc: stable@vger.kernel.org
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agocrypto: virtio - bound the akcipher result length
Bryam Vargas [Mon, 22 Jun 2026 06:52:15 +0000 (01:52 -0500)]
crypto: virtio - bound the akcipher result length

commit f77a956f6a19f9463ef1527c9d0cda50dded6b92 upstream.

virtio_crypto_dataq_akcipher_callback() sets the result length from the
device-reported response length without bounding it to the destination
buffer, which was allocated for the original request length.
sg_copy_from_buffer() then reads that many bytes from the destination
buffer; a backend reporting a larger length over-reads adjacent kernel
heap into the caller's scatterlist (an out-of-bounds read).

Clamp the reported length to the originally requested destination length.
A conforming device reports no more than that, so valid results are
unaffected.

Fixes: a36bd0ad9fbf ("virtio-crypto: adjust dst_len at ops callback")
Cc: stable@vger.kernel.org
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Message-ID: <20260622-b4-disp-3a2c09a8-v2-1-d1a809281db4@proton.me>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agokunit: irq: Continue increasing hrtimer interval for longer
Eric Biggers [Mon, 3 Aug 2026 18:18:41 +0000 (11:18 -0700)]
kunit: irq: Continue increasing hrtimer interval for longer

commit faa6c4c4e4ac69926564688a926105621295d613 upstream.

Currently, kunit_irq_test_timer_func() stops increasing the hrtimer
interval as soon as some forward progress is made in each of softirq and
task context.  Update it to use a more aggressive strategy: increase the
interval as long as the hrtimer is running significantly faster than
either context.

This resolves an occasional hang in the CRC and crypto library tests
under qemu-system-s390x.  It was exposed by the change in the default
preemption model on s390 from NONE to LAZY.  That seems to have exposed
the issue by allowing some forward progress to be made while the actual
system timer tick is still starved, preventing jiffies from increasing
or the task context from making much progress towards max_iterations.

Fixes: 201ceb94aa1d ("kunit: irq: Ensure timer doesn't fire too frequently")
Cc: stable@vger.kernel.org
Reviewed-by: David Gow <david@davidgow.net>
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://patch.msgid.link/20260803181842.44648-1-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agomm/swap: reject swapon() on filesystem-level encrypted files
Eric Biggers [Mon, 3 Aug 2026 18:04:26 +0000 (11:04 -0700)]
mm/swap: reject swapon() on filesystem-level encrypted files

commit c310a8932a3107c9bc8f01d473e9d085f8aa9c98 upstream.

ext4 and f2fs don't prevent filesystem-level encrypted files from being
set up directly as swap files.  In this case, encryption is bypassed.

No one should be doing this, vs.  the methods of encrypted swap that
actually do work (such as swapping to a dm-crypt device, or swapping to a
loopback device on top of a filesystem-level encrypted file).

Nevertheless, to prevent user error, make swapon() explicitly reject this
case.  Document this behavior in fscrypt.rst as well.

Link: https://lore.kernel.org/20260803180426.3123-1-ebiggers@kernel.org
Fixes: 9bd8212f981e ("ext4 crypto: add encryption policy and password salt support")
Fixes: f424f664f0e8 ("f2fs crypto: add encryption policy and password salt support")
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Reviewed-by: Baoquan He <baoquan.he@linux.dev>
Reviewed-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Cc: Barry Song <baohua@kernel.org>
Cc: Chris Li <chrisl@kernel.org>
Cc: Kairui Song <kasong@tencent.com>
Cc: Kemeng Shi <shikemeng@huaweicloud.com>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agonetfilter: nf_tables: don't queue packet path object notifications
Fourie Zhang [Mon, 10 Aug 2026 11:35:01 +0000 (19:35 +0800)]
netfilter: nf_tables: don't queue packet path object notifications

commit 7904b94768e983bcb2be34a8d6d1f3450f5b838b upstream.

All file:line references below are against v7.2-rc4 (ac5b0e5651b1). The
trace was captured on 7.2.0-rc6-kasan72rc6 (075b74841bd0), where the same
lines apply.

nft_obj_notify() is exported and reached from the packet path. Its only
in-tree caller is nft_quota_obj_eval() (net/netfilter/nft_quota.c:68),
which notifies with GFP_ATOMIC while evaluating a rule for a transiting
packet, holding no mutex.

Since commit 67cc570edaa0 ("netfilter: nf_tables: coalesce multiple
notifications into one skbuff") that notification is no longer sent
immediately. __nft_obj_notify() queues it onto nft_net->notify_list via
nft_notify_enqueue() (net/netfilter/nf_tables_api.c:1211), which is a bare
list_add_tail(). notify_list has no lock of its own
(include/net/netfilter/nf_tables.h:1951), it is serialised by commit_mutex:
the six other enqueue sites all run inside a netlink transaction, and the
drain in nft_commit_notify() (net/netfilter/nf_tables_api.c:10746) does
list_del() + kfree_skb() from nf_tables_commit() with commit_mutex held.

Sending packets through a chain that references a depleted quota object
therefore races an unlocked list_add_tail() against list_del() +
kfree_skb() on another CPU. The WRITE_ONCE(prev->next, new) in __list_add()
then stores through an sk_buff that has already been freed:

  BUG: KASAN: slab-use-after-free in __nft_obj_notify+0x2c5/0x2d0
  Write of size 8 at addr ff110001047183c0 by task poc/76
  CPU: 0 UID: 1000 PID: 76 Comm: poc Tainted: G  W  7.2.0-rc6-kasan72rc6 #4
  Call Trace:
   <IRQ>
   __nft_obj_notify (include/linux/list.h:164 include/linux/list.h:191
                     net/netfilter/nf_tables_api.c:1211
                     net/netfilter/nf_tables_api.c:8743)
   nft_quota_obj_eval (net/netfilter/nft_quota.c:68)
   nft_do_chain_inet
   nf_hook_slow
   __ip_local_out
   ip_push_pending_frames
   udp_send_skb
   udp_sendmsg
   __x64_sys_sendto

  Allocated by task 77:
   __alloc_skb (net/core/skbuff.c:704)
   __nft_obj_notify (include/net/netlink.h:1055
                     net/netfilter/nf_tables_api.c:8731)
   nft_quota_obj_eval (net/netfilter/nft_quota.c:68)
   nft_do_chain

  Freed by task 79:
   nf_tables_commit (include/linux/skbuff.h:1332
                     net/netfilter/nf_tables_api.c:10759
                     net/netfilter/nf_tables_api.c:11185)
   nfnetlink_rcv_batch (net/netfilter/nfnetlink.c:574)
   netlink_unicast
   netlink_sendmsg

  The buggy address belongs to the cache skbuff_head_cache of size 232

Queueing from the packet path is wrong even leaving the race aside:
notify_list is only drained by nft_commit_notify() from nf_tables_commit()
(:11185), so a notification enqueued outside a transaction is not sent
until some later netlink batch commits, if one ever does.

The gfp argument that nft_obj_notify() still takes is a leftover of the
pre-67cc570edaa0 behaviour, where this path called nfnetlink_send()
directly. Restore that: split the message construction out into
nft_obj_notify_alloc() and let each caller decide what to do with the skb.
nft_obj_notify(), the exported one reached from the packet path, sends it
straight away; nf_tables_obj_notify(), which runs under commit_mutex, keeps
queueing it, so transaction notifications are still coalesced.

Fixes: 67cc570edaa0 ("netfilter: nf_tables: coalesce multiple notifications into one skbuff")
Cc: stable@kernel.org
Reported-by: TencentOS Corvus AI <corvus@tencent.com>
Assisted-by: tencentos-corvus-ai:kimi-k3
Signed-off-by: Fourie Zhang <fouriezhang@tencent.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agonetfilter: nft_set_pipapo_avx2: add missing vzeroupper
Eric Biggers [Sat, 15 Aug 2026 20:57:50 +0000 (13:57 -0700)]
netfilter: nft_set_pipapo_avx2: add missing vzeroupper

commit 55dd20f0f4b1be5c9c8a0275d8d763c86563eac2 upstream.

Since pipapo_get_avx2() uses YMM registers, execute vzeroupper before
returning from it.  This is needed to avoid degrading the performance of
any later SSE code that may happen to be executed.

Fixes: 7400b063969b ("nft_set_pipapo: Introduce AVX2-based lookup implementation")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agovxlan: keep the last remote linked during FDB flush
Kyle Zeng [Mon, 10 Aug 2026 14:41:14 +0000 (14:41 +0000)]
vxlan: keep the last remote linked during FDB flush

commit d5d4a7b538b52db63927773a8905fcd9f78a42e2 upstream.

A non-nexthop FDB entry is expected to have at least one remote while it
remains reachable through the FDB hash table. A filtered bulk flush
violates this invariant when every remote matches: It unlinks the last
remote in vxlan_fdb_dst_destroy() and only afterwards tells vxlan_flush()
to destroy the parent FDB entry.

An RCU reader can find the parent during this interval.
first_remote_rcu() then applies list_entry_rcu() to the empty list head,
producing an invalid remote pointer that the receive learning path can
read from and write to.

When a matching remote is the sole remaining remote, leave it linked and
ask the caller to destroy the entire FDB entry. vxlan_fdb_destroy() keeps
the remote attached while sending the deletion notification and removing
the parent from the lookup structures.

Fixes: c499fccb71cb ("vxlan: vxlan_core: Support FDB flushing by destination VNI")
Cc: stable@vger.kernel.org
Signed-off-by: Kyle Zeng <kylebot@openai.com>
Co-developed-by: David Lee <david.lee@trailofbits.com>
Signed-off-by: David Lee <david.lee@trailofbits.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Link: https://patch.msgid.link/20260810144115.821654-1-david.lee@trailofbits.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2 weeks agobatman-adv: reject unrepresentable multicast TVLV offsets
Kyle Zeng [Mon, 17 Aug 2026 08:49:54 +0000 (08:49 +0000)]
batman-adv: reject unrepresentable multicast TVLV offsets

commit f12c2de4f542e3220e17e0606f492110064f04cb upstream.

The network and transport header fields in struct sk_buff are 16-bit
offsets from skb->head, and U16_MAX is reserved as the unset transport
header value. batadv_tvlv_call_handler() sets both fields from a received
multicast TVLV without checking whether the TVLV end is representable.

If the end offset exceeds the field's range, skb_set_transport_header()
truncates it so that the transport header precedes the network header.
The negative difference is then returned by skb_network_header_len() as
a large u32. batadv_mcast_forw_packet() consequently accepts an oversized
multicast tracker and accesses memory beyond the skb data.

Add skb_set_transport_header_careful(), an offset-aware counterpart to
skb_reset_transport_header_careful(), which validates the final
head-relative offset before assigning it. Use the new helper in
batadv_tvlv_call_handler() and reject unrepresentable TVLVs before
setting the network header.

Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding")
Cc: stable@vger.kernel.org
Signed-off-by: Kyle Zeng <kylebot@openai.com>
Co-developed-by: David Lee <david.lee@trailofbits.com>
Signed-off-by: David Lee <david.lee@trailofbits.com>
Acked-by: Sven Eckelmann <sven@narfation.org>
Link: https://patch.msgid.link/20260817084955.944189-1-david.lee@trailofbits.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>