From 8a859a8c528ebf83cdcbf59110726e9f7d847cb7 Mon Sep 17 00:00:00 2001 From: Zygo Blaxell Date: Sat, 5 Sep 2026 05:50:29 -0400 Subject: [PATCH] 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 --- fs/btrfs/block-group.c | 18 ++++++++++++++++-- fs/btrfs/free-space-cache.c | 4 ++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 5e052418b68be..e91fb98c32442 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -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; } diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 58f02314aef85..0c23fa3429dbe 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -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); -- 2.53.0