]> git.hungrycats.org Git - linux/commitdiff
covers: delalloc-fixes and raid56-rbio-fixes cover letters (upstream candidates,...
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sat, 12 Sep 2026 13:18:32 +0000 (09:18 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 18 Sep 2026 21:36:35 +0000 (17:36 -0400)
Two series cut from topics/delalloc-fixes (writepage_delalloc abandoned-range
release, Cc: stable; NOCOW read-only drain) and the two rbio cache race fixes
at the tip of topics/raid56-fixes.  Patch files for review under
~/share/patches/2026-09-12-misc-next/.

Assisted-by: Claude:claude-opus-4-8
covers/delalloc-fixes.txt [new file with mode: 0644]
covers/raid56-rbio-fixes.txt [new file with mode: 0644]

diff --git a/covers/delalloc-fixes.txt b/covers/delalloc-fixes.txt
new file mode 100644 (file)
index 0000000..94efbc1
--- /dev/null
@@ -0,0 +1,51 @@
+From: Zygo Blaxell <ce3g8jdj@umail.furryterror.org>
+To: linux-btrfs@vger.kernel.org
+Subject: [PATCH 0/2] btrfs: two ways a delalloc range loses its space or its data
+
+Two fixes found while running a raid56 stripe-exclusive allocator to
+ENOSPC under fsstress, neither of which is specific to that allocator.
+
+Patch 1 fixes a leak in writepage_delalloc()'s error handling.  When a
+folio holds more than one delalloc range (large folios, or a sector size
+below the page size) and btrfs_run_delalloc_range() fails on one, the
+ranges after it are only unlocked.  The folio was already cleared dirty
+for this writeback, so they are never written back again: EXTENT_DELALLOC
+stays set on a clean folio until memory reclaim releases it, and
+try_release_extent_state() clears the bit without any release.  The
+inode's outstanding extents, csum_bytes and block reservation and the data
+space_info's bytes_may_use leak, with the WARN_ONs in btrfs_destroy_inode()
+and check_removing_space_info() firing at eviction and unmount.  The fix
+fails those ranges the way the failed range is failed.  This is reachable
+on any filesystem where a run_delalloc_range() call can fail on a
+multi-range folio (-EIO, or -ENOSPC on a NOCOW fallback), so it is marked
+for stable.
+
+Patch 2 closes a race between a nodatacow write and a block group going
+read-only.  A buffered nodatacow write that finds no data space decides
+to write in place at write() time (EXTENT_NORESERVE) and holds nothing
+afterwards; scrub, balance and zoned reclaim make the group read-only
+without flushing, and the writeback then falls back to COW with a data
+reservation that never went through admission.  On a full filesystem
+that COW allocation fails and the pages are dropped, with the error
+surfacing only at fsync or close.  Snapshots and reflinks flush before
+changing sharing, so this window belongs to the read-only transition
+alone.  The fix drains the group before the flip: new NOCOW decisions are
+refused, in-flight write() calls waited for, and -- only when a NOCOW
+decision has landed on the group since it was last drained -- delalloc is
+flushed and the group's ordered extents waited for, before the
+transaction is joined.  A test that fills a filesystem, writes into a
+nodatacow file, scrubs, and syncs while the group is read-only reads the
+file back intact and in place with the fix, and reads its old bytes
+without it.
+
+Zygo Blaxell (2):
+  btrfs: release the space of delalloc ranges abandoned by a failed writeback
+  btrfs: drain pending NOCOW writes before making a block group read-only
+
+ fs/btrfs/block-group.c | 58 ++++++++++++++++++++++++++++++++++++++++++
+ fs/btrfs/block-group.h |  9 +++++++
+ fs/btrfs/extent_io.c   | 34 ++++++++++++++++++++-----
+ fs/btrfs/file.c        | 16 ++++++++++--
+ fs/btrfs/fs.h          |  8 ++++++
+ fs/btrfs/inode.c       | 16 +++++++++---
+ 6 files changed, 129 insertions(+), 12 deletions(-)
diff --git a/covers/raid56-rbio-fixes.txt b/covers/raid56-rbio-fixes.txt
new file mode 100644 (file)
index 0000000..16ecc2a
--- /dev/null
@@ -0,0 +1,36 @@
+From: Zygo Blaxell <ce3g8jdj@umail.furryterror.org>
+To: linux-btrfs@vger.kernel.org
+Subject: [PATCH 0/2] btrfs: raid56: two use-after-free races around the rbio cache
+
+Both crashes were hit on a degraded raid5 array under fsstress while the
+stripe cache was being trimmed by an out-of-tree change that drops cached
+rbios when their stripes' extents are freed; the trimming only makes the
+existing races frequent.  Both are reachable with the in-tree cache
+shrink in cache_rbio() and with any concurrent lock_stripe_add().
+
+Patch 1: lock_stripe_add()'s steal path takes a cached rbio off the hash
+list and drops the hash reference while the bucket and bio_list locks are
+held, then releases them and only afterwards removes the rbio from the
+cache.  In between, the cache reference is the only one left, and a
+concurrent __remove_rbio_from_cache() -- the shrink, or the trimming --
+frees the rbio under the stealing thread, which then dereferences it
+(bioc NULL at __remove_rbio_from_cache()).  Keep the hash reference across
+the steal and drop it after the cache is done with the rbio.
+
+Patch 2: unlock_stripe() caches the finished rbio with cache_rbio() before
+it retakes the locks, with RBIO_RMW_LOCKED_BIT still set.  A
+lock_stripe_add() in that window can neither steal the cached rbio (still
+RMW locked) nor merge with it (cached) and plugs onto it, which is fine:
+unlock_stripe() then hands the stripe lock to the plugged rbio.  But a
+cache removal in the same window finds an empty bio_list, unhashes the
+rbio and hits BUG_ON(!list_empty(&rbio->plug_list)): the transaction
+kthread died in that BUG_ON with the bucket lock held and the rmw workers
+spun until the machine was reset.  Treat an rbio with a plug list as busy,
+like one with bios, and drop only the cache's reference.
+
+Zygo Blaxell (2):
+  btrfs: raid56: keep a reference on a stolen cached rbio until it is dropped
+  btrfs: raid56: do not unhash a cached rbio that has rbios plugged on it
+
+ fs/btrfs/raid56.c | 26 ++++++++++++++++++++++----
+ 1 file changed, 22 insertions(+), 4 deletions(-)