]> 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, 4 Sep 2026 17:16:58 +0000 (13:16 -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 8631f752930cf87c4cf394255df76c8eeb015788..dbbfa06d537f69a6051805a9aa3c00f1c79530f4 100644 (file)
@@ -1283,7 +1283,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;
@@ -1291,6 +1292,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;
@@ -1305,6 +1311,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;
@@ -1316,9 +1329,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 0e980a5b276aee193efdf836eec398407d8b4071..940e2a01192d6ace786ed677fbe10357f663314d 100644 (file)
@@ -436,9 +436,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 e32462f6796f8d28f482a54a5706168e48fad1b8..cbb0b6680e98b75fa0e6f3f5b9526474bdbeaea5 100644 (file)
@@ -801,6 +801,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 fe6e03b0cba1079c7b2294eb40facf4143f6a0dd..84e7d1df7a808172f606ec8e12f30941c043e26a 100644 (file)
@@ -2619,20 +2619,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_sectors 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_sectors[i].has_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 +
@@ -2640,8 +2660,19 @@ static bool rmw_try_pad_full(struct btrfs_raid_bio *rbio)
 
                if (rbio->bio_sectors[i].has_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 6df2314a3b6db024078e2dbf9c8e321511be383c..c3f5d366d01dbffdf0deddb68fa18672c698c7c4 100644 (file)
@@ -1240,6 +1240,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"
@@ -1255,6 +1256,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 40d84903816b4219d37f6157e437b5c84292c455..0bf8d882853327c3350c41ab9157d25aaaf9a132 100644 (file)
@@ -2393,6 +2393,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,