]> git.hungrycats.org Git - linux/commitdiff
btrfs: stripe_meta: back the global reserve with usable stripes, and say when a tree...
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sat, 5 Sep 2026 09:59:56 +0000 (05:59 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sun, 6 Sep 2026 18:23:37 +0000 (14:23 -0400)
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
fs/btrfs/block-rsv.c
fs/btrfs/extent-tree.c

index fc378d2038a2d2852b7c4222971d8dccec35fba1..85b75858e47a25ad9172bd52f3283b62be56211b 100644 (file)
@@ -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);
index f722722a06a1c7efdbfd477d133aedf031b98670..5b6fd1d177f44569af9284ef906c1056cdf6048d 100644 (file)
@@ -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;