]> git.hungrycats.org Git - linux/commitdiff
btrfs: stripe_alloc: park partial writes against draining runs too
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sat, 8 Aug 2026 17:14:14 +0000 (13:14 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sun, 9 Aug 2026 01:10:28 +0000 (21:10 -0400)
Parking has required the stripe to lie in an OPEN run, but a run closes
the moment it is fully allocated -- typically milliseconds before its
last writes reach the raid56 layer.  Those tail writes arrive to a
draining run, cannot park, and cannot pad (every sector of their stripe
is allocated), so they read-modify-write.  Measured on the buffered-fill
workload, this stranded population -- not expired or kicked parks -- is
nearly all of stripe_alloc's data RMW: 13980 of 14658 RMW reads happen
in the pure async seed phase, and only 73 of 2006 RMW rbios had ever
been parked.  It is also why stretching the park deadline 20x barely
moved the numbers: deadlines govern parks, and these writes never got
one.

With completion-driven parking, a draining run is in fact the safest
thing to park against: its frozen frontier gives an exact allocation
ceiling, everything below the ceiling is inflight IO that the commit
drain already waits for (arrival guaranteed), and everything above it is
dead space the pad may fill -- the pad oracle has always accepted a
closed run's frontier stripe.  Relax the parking gate to accept any run,
open or draining, that covers the stripe.

Assisted-by: Claude:claude-fable-5
fs/btrfs/block-group.c

index c2abe783063a548c737e23bef8192cb59d228232..1bbf976ac3f000f7d9a6193dfbef68b542945074 100644 (file)
@@ -1756,10 +1756,13 @@ void btrfs_disable_stripe_alloc(struct btrfs_fs_info *fs_info)
 }
 
 /*
- * Does @logical lie within an open stripe run?  Used by the raid56 layer to
- * decide whether a partial write to this stripe may be parked to collect
- * into a full-stripe write: an open run guarantees the rest of the stripe
- * is either filled before the run closes or never.
+ * Does @logical lie within a stripe run (open or draining)?  Used by the
+ * raid56 layer to decide whether a partial write to this stripe may be
+ * parked to collect into a full-stripe write: an open run guarantees the
+ * rest of the stripe is either filled before the run closes or never, and
+ * a draining run's frozen frontier makes "never" decidable -- everything
+ * allocated below it is inflight IO the commit drain already waits for,
+ * and everything above it is dead space the pad fills.
  */
 bool btrfs_stripe_in_open_run(struct btrfs_fs_info *fs_info, u64 logical)
 {
@@ -1784,7 +1787,16 @@ bool btrfs_stripe_open_run_class(struct btrfs_fs_info *fs_info, u64 logical,
                return false;
        spin_lock_irqsave(&bg->stripe_run_lock, flags);
        list_for_each_entry(run, &bg->open_stripe_runs, list) {
-               if (run->open && logical >= run->start && logical < run->end) {
+               /*
+                * A closed run's end has been shrunk to its frontier, so a
+                * write into it (necessarily below the frontier) still
+                * matches; its arrival is what the run's drain is waiting
+                * for.  Excluding draining runs here made every run's tail
+                * writes unparkable -- the run closes on exhaustion in the
+                * gap between allocation and write arrival -- and those
+                * writes were the bulk of stripe_alloc's read-modify-writes.
+                */
+               if (logical >= run->start && logical < run->end) {
                        if (class)
                                *class = run->class;
                        ret = true;