]> git.hungrycats.org Git - linux/commitdiff
btrfs: stripe_alloc: let a data write fall back to the relocation group rather than...
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sun, 6 Sep 2026 04:10:38 +0000 (00:10 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Wed, 16 Sep 2026 21:40:05 +0000 (17:40 -0400)
Dedicating a group to relocation takes its claimable stripes away from
every other writer at once.  Admission stops counting them from then
on, but a write admitted a moment earlier against exactly those stripes
still has to be placed, and with the group hard-excluded it was not:
ten milliseconds after "relocating block group 2976120832" a 16K
writeback allocation returned ENOSPC and its data was dropped, the one
drop left in a fill-to-the-edge run under reclaim.  The read-only
transition has a guard for this; the dedication had none.

Keep the exclusion for the allocator's first passes and lift it once
the search reaches the chunk-allocation loop, i.e. only when no other
group can serve the write.  The class tag on the stripe runs already
guarantees the two sides never share a stripe, so the fallback costs
relocation a few stripes, not its correctness.  Zoned's hard exclusion
follows from append-only zones, which do not apply here.

Assisted-by: Claude:claude-fable-5
fs/btrfs/extent-tree.c

index b64863ca066ac129965eeea9b58855a08150f1a3..5c8421b122448c776c22cad48384df995772186c 100644 (file)
@@ -4119,8 +4119,23 @@ static int do_allocation_stripe(struct btrfs_block_group *block_group,
        }
        if (fs_info->data_reloc_bg &&
            (ffe_ctl->for_data_reloc !=
-            (block_group->start == fs_info->data_reloc_bg)))
-               skip = true;
+            (block_group->start == fs_info->data_reloc_bg))) {
+               /*
+                * Relocation stays in its group.  Everyone else stays out of
+                * it only while the other groups can serve them: the
+                * dedication can land after a write was admitted against
+                * this group's stripes (the read-only guard covers that
+                * transition, not this one), and refusing the write here
+                * drops its data at writeback.  The class tag on the runs
+                * keeps the two sides from ever sharing a stripe, so a
+                * last-resort allocation in the relocation group is safe,
+                * merely unhelpful to relocation.  Zoned's hard exclusion
+                * follows from append-only zones, which do not apply here.
+                */
+               if (ffe_ctl->for_data_reloc ||
+                   ffe_ctl->loop < LOOP_ALLOC_CHUNK)
+                       skip = true;
+       }
        spin_unlock(&fs_info->relocation_bg_lock);
        if (dedicated_now && block_group->space_info) {
                /* its claimable stripes now belong to relocation alone */