]> git.hungrycats.org Git - linux/commit
btrfs: stripe_alloc: give nocow extents a private run only while they may be written...
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 11 Sep 2026 13:23:47 +0000 (09:23 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Wed, 16 Sep 2026 21:40:06 +0000 (17:40 -0400)
commit92d1b284951a20406e1537c111e8e86e6875e8e2
tree9312ebc57f7f48189e37189a4ae5d2caa3763bea
parent2864cb69d7ef8c93651b0dc3eb64ca4735e3dad5
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
fs/btrfs/block-group.c
fs/btrfs/extent-tree.c