From: Zygo Blaxell Date: Sat, 5 Sep 2026 09:59:56 +0000 (-0400) Subject: btrfs: stripe_meta: back the global reserve with usable stripes, and say when a tree... X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4f772f87ac0bf1ec784b96bc0d2ba5ce41f592a1;p=linux btrfs: stripe_meta: back the global reserve with usable stripes, and say when a tree block finds none With trapped and open-run bytes counted as used in the METADATA space_info, the overcommit check already admits tree block reservations only against whole free stripes plus unallocated space, so no separate metadata gate is needed. Two loose ends remain. btrfs_update_global_block_rsv() forces a chunk allocation when the global reserve is at least the space_info's total, but under stripe_meta the reserve can only be spent in whole stripes: compare it against the capacity minus the trapped bytes so a metadata chunk is allocated while unallocated space still exists, instead of the reserve running dry at the fill edge. And when a raid56 stripe_meta tree block allocation does fail with ENOSPC -- which aborts the transaction -- print the stripe counters, the metadata twin of the data-side "returned ENOSPC despite reservation margin" warning, so an accounting hole is identifiable from the log. Assisted-by: Claude:claude-fable-5 --- diff --git a/fs/btrfs/block-rsv.c b/fs/btrfs/block-rsv.c index fc378d2038a2d..85b75858e47a2 100644 --- a/fs/btrfs/block-rsv.c +++ b/fs/btrfs/block-rsv.c @@ -393,7 +393,14 @@ void btrfs_update_global_block_rsv(struct btrfs_fs_info *fs_info) block_rsv->full = (block_rsv->reserved == block_rsv->size); - if (block_rsv->size >= sinfo->total_bytes) + /* + * The global reserve must be backed by space a tree block can + * actually land in. Under stripe_meta that excludes the bytes + * trapped in partially used stripes, so compare against the usable + * capacity, not the raw total, and force a chunk allocation as soon + * as the reserve would not fit in it. + */ + if (block_rsv->size >= sinfo->total_bytes - sinfo->bytes_stripe_unusable) sinfo->force_alloc = CHUNK_ALLOC_FORCE; spin_unlock(&block_rsv->lock); spin_unlock(&sinfo->lock); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index f722722a06a1c..5b6fd1d177f44 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -5009,6 +5009,30 @@ again: btrfs_warn_rl(fs_info, "stripe_alloc: data writeback allocation of %llu bytes returned ENOSPC despite reservation margin; buffered data in this range will be dropped", num_bytes); + /* + * Metadata twin. A tree block reservation was admitted + * against the whole-stripe supply (trapped and open-run bytes + * count as used in the space_info), so a raid56 stripe_meta + * tree block should always find a whole stripe or an open + * run. If it does not, the transaction aborts: report the + * counters so the accounting hole can be identified. + */ + if (!is_data && btrfs_test_opt(fs_info, STRIPE_ALLOC) && + (flags & BTRFS_BLOCK_GROUP_RAID56_MASK)) { + struct btrfs_space_info *sinfo; + + sinfo = btrfs_find_space_info(fs_info, flags); + if (sinfo) + btrfs_warn_rl(fs_info, + "stripe_meta: tree block allocation of %llu bytes returned ENOSPC: claimable %llu open %llu trapped %llu may_use %llu reserved %llu total %llu", + num_bytes, + sinfo->bytes_stripe_claimable, + sinfo->bytes_stripe_open, + sinfo->bytes_stripe_unusable, + sinfo->bytes_may_use, + sinfo->bytes_reserved, + sinfo->total_bytes); + } } return ret;