]> git.hungrycats.org Git - linux/commit
btrfs: drain pending NOCOW writes before making a block group read-only 6.18/topics/delalloc-fixes
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sat, 12 Sep 2026 06:13:30 +0000 (02:13 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Wed, 16 Sep 2026 21:39:58 +0000 (17:39 -0400)
commit848365d18aa022a161033d5d470772c2216edc2e
treec967c6107c141511a76a16d47a9f102a0a0b349c
parente51c0307725b40da8a0b5e6b30ab0bdc97c9a867
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-opus-4-8
fs/btrfs/block-group.c
fs/btrfs/block-group.h
fs/btrfs/file.c
fs/btrfs/fs.h
fs/btrfs/inode.c