]> 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>
Fri, 18 Sep 2026 21:36:26 +0000 (17:36 -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 6a40a25afad47b6836d5c7da78333e6fa6c108ee..335291cc3c9614dfc285bddadc98002f8d027f75 100644 (file)
@@ -2636,6 +2636,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))
@@ -2671,6 +2672,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);
                                }
                        }
                }
@@ -2679,6 +2683,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
@@ -2781,10 +2786,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);
+       }
 }
 
 /*
@@ -2808,6 +2822,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 225a7cdd084aa086f7e1118e7dd6c5c7b8beb371..21b1b4bfad4f6efd13fba5bf8fcef0cf63ab2345 100644 (file)
@@ -4262,6 +4262,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;
@@ -4301,14 +4302,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 075c00ea55b383b1e1be6aaa41dce498fb5f444a..b4526c199ffb53553ed2deb4d9ba7478afadf1c2 100644 (file)
@@ -1533,6 +1533,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);
 }
 
@@ -2172,6 +2178,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 a6c4fd1d598cccf27ab5239f00bec1954ee9824e..bb2b2934670bf893da9099d7c57f72a5fb65009b 100644 (file)
@@ -687,9 +687,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 3d6141665a9d65e7a156fd58676b6702cbe5f1a9..702222bb54ae4e5faca7734599db4c4b741c04d1 100644 (file)
@@ -166,6 +166,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