]> git.hungrycats.org Git - linux/commitdiff
btrfs: stripe_alloc: trace why padding declined, at the moment it declined
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sat, 8 Aug 2026 07:57:22 +0000 (03:57 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 18 Sep 2026 21:36:24 +0000 (17:36 -0400)
The counters say how often padding refused and broadly why, but not what
the run looked like when it happened, and reconstructing that from the
extent tree afterwards cannot distinguish "the stripe was already
allocated when this write arrived" from "it was allocated shortly after".
Those want different fixes, so record the decision where it is made.

Also count the second refusal, which had no counter at all: the oracle can
allow padding and the sector walk still refuse, because a sector below the
frontier is not covered by this write.  That is the case where another
allocation shares the stripe and its data is not in this rbio -- a
different thing from the oracle finding the whole stripe allocated.

The tracepoint carries the stripe, how much of it this write covers, the
refusal reason, and the run's start/end/frontier/inflight, so a trace says
directly whether the frontier had already run past the stripe when the
write showed up.

Assisted-by: Claude:claude-fable-5
fs/btrfs/block-group.c
fs/btrfs/block-group.h
fs/btrfs/fs.h
fs/btrfs/raid56.c
fs/btrfs/sysfs.c
include/trace/events/btrfs.h

index 8fd2b2a9cd1681d8d43e208683dff719e3610499..587490d58d37ee46a0df4dc903d0a37daa8172d4 100644 (file)
@@ -1311,7 +1311,8 @@ bool btrfs_stripe_nocow_writable(struct btrfs_inode *inode, u64 start,
  */
 bool btrfs_stripe_run_pad_start(struct btrfs_fs_info *fs_info,
                                u64 stripe_start, u64 stripe_len,
-                               u64 *pad_from)
+                               u64 *pad_from,
+                               struct btrfs_stripe_pad_info *info)
 {
        struct btrfs_block_group *bg;
        struct btrfs_open_stripe_run *run;
@@ -1319,6 +1320,11 @@ bool btrfs_stripe_run_pad_start(struct btrfs_fs_info *fs_info,
        unsigned long flags;
        bool ret = false;
 
+       if (info) {
+               memset(info, 0, sizeof(*info));
+               info->reason = BTRFS_STRIPE_PAD_NO_RUN;
+       }
+
        if (list_empty_careful(&fs_info->open_stripe_bgs)) {
                atomic64_inc(&fs_info->stripe_park_stats.pad_decline_no_run);
                return false;
@@ -1333,6 +1339,13 @@ bool btrfs_stripe_run_pad_start(struct btrfs_fs_info *fs_info,
        list_for_each_entry(run, &bg->open_stripe_runs, list) {
                if (stripe_start < run->start || stripe_start >= run->end)
                        continue;
+               if (info) {
+                       info->run_start = run->start;
+                       info->run_end = run->end;
+                       info->run_offset = run->offset;
+                       info->inflight_bytes = run->inflight_bytes;
+                       info->reason = BTRFS_STRIPE_PAD_OK;
+               }
                if (run->offset > stripe_start &&
                    run->offset < stripe_start + stripe_len) {
                        *pad_from = run->offset;
@@ -1344,9 +1357,13 @@ bool btrfs_stripe_run_pad_start(struct btrfs_fs_info *fs_info,
                         * and the write is waiting on a neighbour's data.
                         */
                        decline = &fs_info->stripe_park_stats.pad_decline_live;
+                       if (info)
+                               info->reason = BTRFS_STRIPE_PAD_LIVE;
                } else {
                        /* The frontier has not reached the stripe at all. */
                        decline = &fs_info->stripe_park_stats.pad_decline_unallocated;
+                       if (info)
+                               info->reason = BTRFS_STRIPE_PAD_UNALLOCATED;
                }
                break;
        }
index 590d62f59f9b998b70112a17f7dbab75fcc075cd..1d75703ddb0a7fb0320b27aa3e3a1084390a73d4 100644 (file)
@@ -446,9 +446,32 @@ bool btrfs_stripe_open_run_class(struct btrfs_fs_info *fs_info, u64 logical,
                                 enum btrfs_stripe_run_class *class);
 bool btrfs_stripe_run_range_usable(struct btrfs_block_group *bg,
                                   u64 run_start, u64 *run_len);
+/*
+ * Why padding could or could not proceed, and the run state it was decided
+ * against.  Filled at the moment of the decision so a trace can answer
+ * "where was the frontier when this write arrived", which reconstructing
+ * the extent tree afterwards cannot.
+ */
+enum btrfs_stripe_pad_reason {
+       BTRFS_STRIPE_PAD_OK = 0,
+       BTRFS_STRIPE_PAD_NO_RUN,        /* no open run covers the stripe */
+       BTRFS_STRIPE_PAD_LIVE,          /* frontier past the stripe: all allocated */
+       BTRFS_STRIPE_PAD_UNALLOCATED,   /* frontier has not reached the stripe */
+       BTRFS_STRIPE_PAD_UNCOVERED,     /* a sector below the frontier is not covered */
+};
+
+struct btrfs_stripe_pad_info {
+       u64 run_start;
+       u64 run_end;
+       u64 run_offset;                 /* the allocation frontier */
+       u64 inflight_bytes;
+       u8 reason;
+};
+
 bool btrfs_stripe_run_pad_start(struct btrfs_fs_info *fs_info,
                                u64 stripe_start, u64 stripe_len,
-                               u64 *pad_from);
+                               u64 *pad_from,
+                               struct btrfs_stripe_pad_info *info);
 bool btrfs_stripe_nocow_writable(struct btrfs_inode *inode, u64 start,
                                 u64 num_bytes, u32 which);
 int btrfs_parse_stripe_rmw(const char *value, size_t len, u32 *mask);
index 97fa60320865589784e754f770a6c2442a65d864..919851032b046a5026c783b5ac0e8901db47eb5d 100644 (file)
@@ -848,6 +848,10 @@ struct btrfs_fs_info {
                atomic64_t pad_decline_live;
                atomic64_t pad_decline_no_run;
                atomic64_t pad_decline_unallocated;
+               /* Oracle allowed padding, but a sector below the
+                * frontier is not covered by this write: another
+                * allocation shares the stripe. */
+               atomic64_t pad_decline_uncovered;
                atomic64_t meta_rmw;
                /*
                 * Breakdown of what a metadata read-modify-write found in the
index c3dda5dda9904c80ca5c86c8933c22128f115755..2a439066a2dd52f2361afa19a915c6943a5c9ae3 100644 (file)
@@ -2732,20 +2732,40 @@ static bool rmw_try_pad_full(struct btrfs_raid_bio *rbio)
        const u64 stripe_start = rbio->bioc->full_stripe_logical;
        const u32 sectorsize = fs_info->sectorsize;
        const u64 stripe_len = (u64)rbio->nr_data * BTRFS_STRIPE_LEN;
-       u64 pad_from;
+       struct btrfs_stripe_pad_info info;
+       const u32 total = rbio->nr_data * rbio->stripe_nsectors;
+       u64 pad_from = 0;
+       u32 covered = 0;
        int i;
 
        if (!btrfs_test_opt(fs_info, STRIPE_ALLOC))
                return false;
        if (!btrfs_stripe_run_pad_start(fs_info, stripe_start, stripe_len,
-                                       &pad_from))
+                                       &pad_from, &info)) {
+               trace_btrfs_stripe_pad_decline(fs_info, stripe_start,
+                                              stripe_len, pad_from, 0, total,
+                                              info.run_start, info.run_end,
+                                              info.run_offset,
+                                              info.inflight_bytes,
+                                              info.reason);
                return false;
+       }
 
        /* Populate bio_paddrs so covered sectors are visible below. */
        index_rbio_pages(rbio);
 
-       /* Every data sector this rbio does not cover must be paddable. */
-       for (i = 0; i < rbio->nr_data * rbio->stripe_nsectors; i++) {
+       /*
+        * Every data sector this rbio does not cover must be paddable.  A
+        * sector below the frontier is allocated to someone else and this
+        * write does not carry its data, so padding would destroy it: that
+        * is a distinct refusal from the oracle's, and it is the one that
+        * says another writer's allocation shares this stripe.
+        */
+       for (i = 0; i < total; i++)
+               if (rbio->bio_paddrs[i * rbio->sector_nsteps] != INVALID_PADDR)
+                       covered++;
+
+       for (i = 0; i < total; i++) {
                const int stripe_nr = i / rbio->stripe_nsectors;
                const int sectornr = i % rbio->stripe_nsectors;
                u64 logical = stripe_start + stripe_nr * BTRFS_STRIPE_LEN +
@@ -2753,8 +2773,19 @@ static bool rmw_try_pad_full(struct btrfs_raid_bio *rbio)
 
                if (rbio->bio_paddrs[i * rbio->sector_nsteps] != INVALID_PADDR)
                        continue;
-               if (logical < pad_from)
+               if (logical < pad_from) {
+                       atomic64_inc(&fs_info->stripe_park_stats.pad_decline_uncovered);
+                       info.reason = BTRFS_STRIPE_PAD_UNCOVERED;
+                       trace_btrfs_stripe_pad_decline(fs_info, stripe_start,
+                                                      stripe_len, pad_from,
+                                                      covered, total,
+                                                      info.run_start,
+                                                      info.run_end,
+                                                      info.run_offset,
+                                                      info.inflight_bytes,
+                                                      info.reason);
                        return false;
+               }
        }
 
        if (alloc_rbio_data_pages(rbio) < 0)
index ee39265e59c09d084aead78ab75ecf21e4c736bf..06ca412ae5687c82518a907e4f0879f64431caf3 100644 (file)
@@ -1130,6 +1130,7 @@ static ssize_t btrfs_stripe_park_stats_show(struct kobject *kobj,
                "pad_decline_live %lld\n"
                "pad_decline_no_run %lld\n"
                "pad_decline_unallocated %lld\n"
+               "pad_decline_uncovered %lld\n"
                "meta_rmw %lld\n"
                "meta_rmw_cur %lld\n"
                "meta_rmw_old %lld\n"
@@ -1145,6 +1146,7 @@ static ssize_t btrfs_stripe_park_stats_show(struct kobject *kobj,
                atomic64_read(&fs_info->stripe_park_stats.pad_decline_live),
                atomic64_read(&fs_info->stripe_park_stats.pad_decline_no_run),
                atomic64_read(&fs_info->stripe_park_stats.pad_decline_unallocated),
+               atomic64_read(&fs_info->stripe_park_stats.pad_decline_uncovered),
                atomic64_read(&fs_info->stripe_park_stats.meta_rmw),
                atomic64_read(&fs_info->stripe_park_stats.meta_rmw_cur),
                atomic64_read(&fs_info->stripe_park_stats.meta_rmw_old),
index e1af4dd8287cec1423da5889e4bb0aca84627400..db541d70e964098279fec6e51b1fe6cbcf186117 100644 (file)
@@ -3299,6 +3299,50 @@ DEFINE_EVENT(btrfs__space_info_update, update_bytes_stripe_unusable,
        TP_ARGS(fs_info, sinfo, old, diff)
 );
 
+TRACE_EVENT(btrfs_stripe_pad_decline,
+
+       TP_PROTO(const struct btrfs_fs_info *fs_info, u64 full_stripe,
+                u64 stripe_len, u64 pad_from, u32 covered, u32 total,
+                u64 run_start, u64 run_end, u64 run_offset, u64 inflight,
+                u8 reason),
+
+       TP_ARGS(fs_info, full_stripe, stripe_len, pad_from, covered, total,
+               run_start, run_end, run_offset, inflight, reason),
+
+       TP_STRUCT__entry_btrfs(
+               __field(        u64,    full_stripe     )
+               __field(        u64,    stripe_len      )
+               __field(        u64,    pad_from        )
+               __field(        u64,    run_start       )
+               __field(        u64,    run_end         )
+               __field(        u64,    run_offset      )
+               __field(        u64,    inflight        )
+               __field(        u32,    covered         )
+               __field(        u32,    total           )
+               __field(        u8,     reason          )
+       ),
+
+       TP_fast_assign_btrfs(fs_info,
+               __entry->full_stripe    = full_stripe;
+               __entry->stripe_len     = stripe_len;
+               __entry->pad_from       = pad_from;
+               __entry->run_start      = run_start;
+               __entry->run_end        = run_end;
+               __entry->run_offset     = run_offset;
+               __entry->inflight       = inflight;
+               __entry->covered        = covered;
+               __entry->total          = total;
+               __entry->reason         = reason;
+       ),
+
+       TP_printk_btrfs(
+"full_stripe=%llu len=%llu covered=%u/%u reason=%u pad_from=%llu run=[%llu,%llu) frontier=%llu inflight=%llu",
+                 __entry->full_stripe, __entry->stripe_len,
+                 __entry->covered, __entry->total, __entry->reason,
+                 __entry->pad_from, __entry->run_start, __entry->run_end,
+                 __entry->run_offset, __entry->inflight)
+);
+
 DECLARE_EVENT_CLASS(btrfs_raid56_bio,
 
        TP_PROTO(const struct btrfs_raid_bio *rbio,