]> git.hungrycats.org Git - linux/commitdiff
btrfs: stripe_meta: maintain trapped free space for metadata groups like data
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sat, 5 Sep 2026 09:50:29 +0000 (05:50 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 18 Sep 2026 21:36:27 +0000 (17:36 -0400)
btrfs_block_group_init_stripe_unusable() arms stripe_meta groups and adds
their trapped bytes to the METADATA space_info, but the disarm, rescan
and commit-time scan only accept btrfs_is_stripe_alloc_bg() (DATA), so
a metadata group's contribution was frozen at its first value: never
updated as tree blocks were freed inside stripes, never dropped when the
group went read-only or was removed.  Tree blocks can only be placed in
fully free stripes, so the trapped bytes are as unusable as for data and
need the same maintenance.

Accept stripe_meta groups on the three maintenance paths, let the commit
scan sum METADATA space_infos, and apply the read-only subtraction to
the metadata branch of inc_block_group_ro() as well.

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

index 5e052418b68bea79637c03c33065d924f5b5b636..e91fb98c324421d8cfcbd178b901bd9697002abf 100644 (file)
@@ -2639,7 +2639,13 @@ void btrfs_scan_stripe_unusable(struct btrfs_fs_info *fs_info, bool force)
                u64 total_claimable_reloc = 0;
                int raid;
 
-               if (!(sinfo->flags & BTRFS_BLOCK_GROUP_DATA))
+               /*
+                * Data groups under stripe_alloc and metadata groups under
+                * stripe_meta both place extents in whole stripes only, so
+                * both carry trapped free space.  Mixed groups are refused.
+                */
+               if (!(sinfo->flags & (BTRFS_BLOCK_GROUP_DATA |
+                                     BTRFS_BLOCK_GROUP_METADATA)))
                        continue;
                down_read(&sinfo->groups_sem);
                for (raid = 0; raid < BTRFS_NR_RAID_TYPES; raid++) {
@@ -3884,8 +3890,16 @@ static int inc_block_group_ro(struct btrfs_block_group *cache, bool force)
                 * btrfs_can_overcommit check here, and we need to pass in
                 * BTRFS_RESERVE_NO_FLUSH to give ourselves the most amount of
                 * leeway to allow us to mark this block group as read only.
+                *
+                * As for data above: a stripe_meta group's trapped bytes are
+                * already in the space_info total, do not re-provide them.
                 */
-               if (btrfs_can_overcommit(sinfo, num_bytes, BTRFS_RESERVE_NO_FLUSH))
+               u64 trapped = 0;
+
+               if (READ_ONCE(cache->stripe_unusable_ready))
+                       trapped = min(READ_ONCE(cache->stripe_unusable), num_bytes);
+               if (btrfs_can_overcommit(sinfo, num_bytes - trapped,
+                                        BTRFS_RESERVE_NO_FLUSH))
                        ret = 0;
        }
 
index 58f02314aef856fec8dd50d5fb89fe213bcf5f42..0c23fa3429dbe80462fe73a8eb48a1eef160a0a1 100644 (file)
@@ -2158,7 +2158,7 @@ void btrfs_block_group_disarm_stripe_unusable(struct btrfs_block_group *bg)
        u64 trapped;
        u64 claimable;
 
-       if (!btrfs_is_stripe_alloc_bg(bg))
+       if (!btrfs_is_stripe_alloc_bg(bg) && !btrfs_is_stripe_meta_bg(bg))
                return;
 
        spin_lock(&ctl->tree_lock);
@@ -2238,7 +2238,7 @@ void btrfs_block_group_rescan_stripe_unusable(struct btrfs_block_group *bg)
        u64 nstripes;
        u32 *freep;
 
-       if (!btrfs_is_stripe_alloc_bg(bg))
+       if (!btrfs_is_stripe_alloc_bg(bg) && !btrfs_is_stripe_meta_bg(bg))
                return;
 
        nstripes = stripe_unusable_nstripes(bg);