--- /dev/null
+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(-)
--- /dev/null
+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(-)