From: Zygo Blaxell Date: Sat, 5 Sep 2026 09:50:29 +0000 (-0400) Subject: btrfs: stripe_meta: maintain trapped free space for metadata groups like data X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ba85ba7cd84edc45e3f8bebb801ff8cc1eadc32c;p=linux btrfs: stripe_meta: maintain trapped free space for metadata groups like data 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 --- diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 71f5129f3f4fd..99020867c6f31 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -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; } diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 0dc195311e3be..19cb04300550d 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -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);