]> git.hungrycats.org Git - linux/commitdiff
btrfs: stripe_alloc: keep the relocation group's stripes out of the admission supply
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sun, 6 Sep 2026 03:40:50 +0000 (23:40 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Wed, 16 Sep 2026 21:40:05 +0000 (17:40 -0400)
do_allocation_stripe() dedicates one block group to data relocation
(fs_info->data_reloc_bg, as the zoned allocator does): while a
relocation runs, its allocations go only there and everyone else's skip
it, until the relocation finishes or the group fills.  The admission
gate did not know: it counted that group's claimable stripes in the
supply for ordinary writes.  At the fill edge with background reclaim
running, the dedicated group was the only one with claimable stripes
left, ordinary writes were admitted against them, and their writebacks
were dropped:

  stripe_alloc DROP DUMP: sinfo may_use 3837952 claimable 561053696 ...
    bg 7048265728 ... claimable 561053696 ... runs open   <- data_reloc_bg
    every other group: claimable 0

Track the dedicated group's claimable bytes in the space_info
(bytes_stripe_claimable_reloc: set when a group is dedicated, cleared
when the dedication is released or the group is removed, followed by
the per-group claimable deltas and re-summed by the commit rescan) and
admit ordinary reservations against the supply minus that amount.

Relocation itself is admitted against the whole supply, dedicated group
included.  Confining it to the dedicated group's bytes was tried first
and refused relocation as soon as that group -- the first one the
allocator visits, often holding only a couple of free stripes -- ran
short of a reservation's whole stripes plus one, while hundreds of
megabytes of whole stripes sat in other groups: the allocator would
have dropped the dedication and moved on had it been asked, but the
gate failed each queued group with ENOSPC within fifteen milliseconds
and the reclaim worker retried every thirty seconds, with no relocation
ever completing.  bytes_may_use is one counter for both sides, so the
sum admitted still fits in the supply.

Assisted-by: Claude:claude-fable-5
Assisted-by: Claude:claude-fable-5-1
fs/btrfs/block-group.c
fs/btrfs/extent-tree.c
fs/btrfs/free-space-cache.c
fs/btrfs/space-info.c
fs/btrfs/space-info.h

index fe987c3542b378485672a03ff6112af9e4809ca3..cf8a850dad7786336b229749032499a316afeda0 100644 (file)
@@ -2603,6 +2603,7 @@ void btrfs_scan_stripe_unusable(struct btrfs_fs_info *fs_info, bool force)
        list_for_each_entry(sinfo, &fs_info->space_info, list) {
                u64 total = 0;
                u64 total_claimable = 0;
+               u64 total_claimable_reloc = 0;
                int raid;
 
                if (!(sinfo->flags & BTRFS_BLOCK_GROUP_DATA))
@@ -2638,6 +2639,9 @@ void btrfs_scan_stripe_unusable(struct btrfs_fs_info *fs_info, bool force)
                                        total += READ_ONCE(bg->stripe_unusable);
                                        total_claimable +=
                                                READ_ONCE(bg->stripe_claimable);
+                                       if (bg->start == READ_ONCE(fs_info->data_reloc_bg))
+                                               total_claimable_reloc +=
+                                                       READ_ONCE(bg->stripe_claimable);
                                }
                        }
                }
@@ -2646,6 +2650,7 @@ void btrfs_scan_stripe_unusable(struct btrfs_fs_info *fs_info, bool force)
                spin_lock(&sinfo->lock);
                sinfo->bytes_stripe_unusable = total;
                sinfo->bytes_stripe_claimable = total_claimable;
+               sinfo->bytes_stripe_claimable_reloc = total_claimable_reloc;
                /*
                 * The rescan usually lowers the counter (mid-transaction
                 * incremental adds overcount conservatively); admission
@@ -2748,10 +2753,19 @@ void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg)
 {
        struct btrfs_fs_info *fs_info = bg->fs_info;
 
+       bool cleared = false;
+
        spin_lock(&fs_info->relocation_bg_lock);
-       if (fs_info->data_reloc_bg == bg->start)
+       if (fs_info->data_reloc_bg == bg->start) {
                fs_info->data_reloc_bg = 0;
+               cleared = true;
+       }
        spin_unlock(&fs_info->relocation_bg_lock);
+       if (cleared && bg->space_info) {
+               spin_lock(&bg->space_info->lock);
+               bg->space_info->bytes_stripe_claimable_reloc = 0;
+               spin_unlock(&bg->space_info->lock);
+       }
 }
 
 /*
@@ -2775,6 +2789,11 @@ void btrfs_release_data_reloc_bg(struct btrfs_fs_info *fs_info)
        bg = btrfs_lookup_block_group(fs_info, bytenr);
        if (!bg)
                return;
+       if (bg->space_info) {
+               spin_lock(&bg->space_info->lock);
+               bg->space_info->bytes_stripe_claimable_reloc = 0;
+               spin_unlock(&bg->space_info->lock);
+       }
        btrfs_close_bg_open_stripes(bg);
        btrfs_put_block_group(bg);
 }
index a3317b732bd133cf88264102b5ca1359be697746..b64863ca066ac129965eeea9b58855a08150f1a3 100644 (file)
@@ -4072,6 +4072,7 @@ static int do_allocation_stripe(struct btrfs_block_group *block_group,
 {
        struct btrfs_fs_info *fs_info = block_group->fs_info;
        enum btrfs_stripe_run_class class = BTRFS_STRIPE_RUN_COW;
+       bool dedicated_now = false;
        u64 available = 0;
        u64 offset;
        bool skip = false;
@@ -4111,14 +4112,23 @@ static int do_allocation_stripe(struct btrfs_block_group *block_group,
        spin_lock(&fs_info->relocation_bg_lock);
        if (ffe_ctl->for_data_reloc) {
                class = BTRFS_STRIPE_RUN_RELOC;
-               if (!fs_info->data_reloc_bg)
+               if (!fs_info->data_reloc_bg) {
                        fs_info->data_reloc_bg = block_group->start;
+                       dedicated_now = true;
+               }
        }
        if (fs_info->data_reloc_bg &&
            (ffe_ctl->for_data_reloc !=
             (block_group->start == fs_info->data_reloc_bg)))
                skip = true;
        spin_unlock(&fs_info->relocation_bg_lock);
+       if (dedicated_now && block_group->space_info) {
+               /* its claimable stripes now belong to relocation alone */
+               spin_lock(&block_group->space_info->lock);
+               block_group->space_info->bytes_stripe_claimable_reloc =
+                       READ_ONCE(block_group->stripe_claimable);
+               spin_unlock(&block_group->space_info->lock);
+       }
        if (skip)
                return 1;
 
index 36ea5029757d3355363f9930993b83e66b0684f2..775a19a5816e11e7f2b1b9ed4a924fc4019810f8 100644 (file)
@@ -2806,6 +2806,12 @@ static void stripe_claimable_mod(struct btrfs_block_group *bg, s64 delta)
                sinfo->bytes_stripe_claimable = 0;
        else
                btrfs_space_info_update_bytes_stripe_claimable(sinfo, delta);
+       if (bg->start == READ_ONCE(bg->fs_info->data_reloc_bg)) {
+               if (delta < 0 && sinfo->bytes_stripe_claimable_reloc < (u64)-delta)
+                       sinfo->bytes_stripe_claimable_reloc = 0;
+               else
+                       sinfo->bytes_stripe_claimable_reloc += delta;
+       }
        spin_unlock(&sinfo->lock);
 }
 
@@ -3443,6 +3449,8 @@ void btrfs_block_group_disarm_stripe_unusable(struct btrfs_block_group *bg)
                                                              -(s64)trapped);
                btrfs_space_info_update_bytes_stripe_claimable(bg->space_info,
                                                               -(s64)claimable);
+               if (bg->start == READ_ONCE(bg->fs_info->data_reloc_bg))
+                       bg->space_info->bytes_stripe_claimable_reloc = 0;
                spin_unlock(&bg->space_info->lock);
        }
 }
index 6c8e6cf02bdc0e88ee4b6da7686d19bf8f0ce373..97f0e0c4372b076e2990e5f5bf93722ab7161e83 100644 (file)
@@ -628,9 +628,28 @@ static bool stripe_claimable_admit(struct btrfs_space_info *space_info, u64 byte
         * placed): charge it whole stripes plus one, the tail its own open
         * runs can lose at the next commit.
         */
-       if (reloc)
-               bytes = round_up(bytes, unit) + unit;
-       return space_info->bytes_may_use + bytes <= space_info->bytes_stripe_claimable;
+       {
+               u64 supply = space_info->bytes_stripe_claimable;
+               const u64 dedicated = space_info->bytes_stripe_claimable_reloc;
+
+               if (reloc) {
+                       /*
+                        * Against the whole supply, dedicated group included.
+                        * Relocation prefers its dedicated group, but when
+                        * that group runs dry the allocator drops the
+                        * dedication and moves on to another group; confining
+                        * admission to the dedicated group's claimable bytes
+                        * refused relocation (ENOSPC after a handful of 16K
+                        * clusters, retried every 30 seconds) while hundreds
+                        * of megabytes of whole stripes sat in other groups.
+                        */
+                       bytes = round_up(bytes, unit) + unit;
+               } else {
+                       /* ...and that group is out of reach for everyone else */
+                       supply -= min(supply, dedicated);
+               }
+               return space_info->bytes_may_use + bytes <= supply;
+       }
 }
 
 /*
index dbf349a04fe79e215b0ef64707dc3253566c60f0..989b1f6ad5b0f0edf79db4b596e09eca03938bfb 100644 (file)
@@ -141,6 +141,11 @@ struct btrfs_space_info {
                                           cannot admit writes against it, and
                                           subtracted in statfs (from a different
                                           base -- the free space cache walk). */
+       u64 bytes_stripe_claimable_reloc;       /* the part of bytes_stripe_claimable
+                                          in the group dedicated to data
+                                          relocation (fs_info->data_reloc_bg):
+                                          out of reach for other allocations
+                                          while the dedication lasts */
        u64 bytes_stripe_claimable;     /* sum of armed groups' directly
                                           measured claimable whole-stripe
                                           bytes; see stripe_claimable in