]> 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>
Mon, 7 Sep 2026 06:20:56 +0000 (02:20 -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 71f5129f3f4fdc5e700d690bea77749e5d9f21df..99020867c6f3167c8ed58ddf1f5b584ce34755c9 100644 (file)
@@ -2572,7 +2572,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++) {
@@ -3798,8 +3804,15 @@ 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(cache->fs_info, sinfo, num_bytes,
+               u64 trapped = 0;
+
+               if (READ_ONCE(cache->stripe_unusable_ready))
+                       trapped = min(READ_ONCE(cache->stripe_unusable), num_bytes);
+               if (btrfs_can_overcommit(cache->fs_info, sinfo, num_bytes - trapped,
                                         BTRFS_RESERVE_NO_FLUSH))
                        ret = 0;
        }
index 0dc195311e3be4fdc20beaaa712481d6961207b9..19cb04300550d66903c05eebb9f3c18be11b2994 100644 (file)
@@ -3429,7 +3429,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);
@@ -3509,7 +3509,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);