]> git.hungrycats.org Git - linux/commitdiff
btrfs: raid56: expose parking statistics in sysfs
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 31 Jul 2026 08:41:19 +0000 (04:41 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Wed, 16 Sep 2026 21:40:02 +0000 (17:40 -0400)
/sys/fs/btrfs/<uuid>/stripe_park_stats reports lifetime counters for
the parking machinery: rbios parked, bytes merged into parked rbios,
and how each park ended (filled to a full stripe, kicked by a waiter or
a settle/retire flush, or expired at its deadline), plus how many
flushed writes were padded to full stripes versus still needing the
RMW read phase.  The filled/kicked/expired split shows directly whether
the deadlines are sized right for a given system -- expired parks that
later reappear as rmw_reads are the batching the window failed to
capture -- so any future retuning (or an adaptive deadline) can argue
from measurements instead of taste.

Assisted-by: Claude:claude-fable-5
fs/btrfs/fs.h
fs/btrfs/raid56.c
fs/btrfs/sysfs.c

index 9fd43f6dc9a818738832f265deccbde8b196e44b..59c369d0fb9e8a3b2a34837a3a7f8a773a5a5ffe 100644 (file)
@@ -758,6 +758,20 @@ struct btrfs_fs_info {
 
        u32 data_chunk_allocations;
        u32 metadata_ratio;
+
+       /*
+        * raid56 parking observability (see rbio_try_park()): lifetime
+        * counters exposed through sysfs stripe_park_stats.
+        */
+       struct {
+               atomic64_t parked;
+               atomic64_t merged_bytes;
+               atomic64_t filled;
+               atomic64_t expired;
+               atomic64_t kicked;
+               atomic64_t rmw_reads;
+               atomic64_t padded;
+       } stripe_park_stats;
        /* BTRFS_STRIPE_RMW_* masks; see btrfs_stripe_allow_rmw(). */
        u32 stripe_rmw_opt;
        u32 stripe_rmw_prop;
index 9f01a4d5eccd451787916450d4f6d06049ba5376..ebefa58e926552abdb7018a8c7b271484c1ad606 100644 (file)
@@ -55,6 +55,9 @@
  */
 #define RBIO_PADDED_BIT                5
 
+/* Set at park time and never cleared: this rbio once parked (stats). */
+#define RBIO_WAS_PARKED_BIT    7
+
 #define RBIO_CACHE_SIZE 1024
 
 /*
@@ -465,6 +468,9 @@ static void steal_rbio(struct btrfs_raid_bio *src, struct btrfs_raid_bio *dest)
 static void merge_rbio(struct btrfs_raid_bio *dest,
                       struct btrfs_raid_bio *victim)
 {
+       if (test_bit(RBIO_PARKED_BIT, &dest->flags))
+               atomic64_add(victim->bio_list_bytes,
+                            &dest->bioc->fs_info->stripe_park_stats.merged_bytes);
        bio_list_merge_init(&dest->bio_list, &victim->bio_list);
        dest->bio_list_bytes += victim->bio_list_bytes;
        /* Also inherit the bitmaps from @victim. */
@@ -795,9 +801,11 @@ static bool rbio_try_park(struct btrfs_raid_bio *rbio)
 
        spin_lock(&table->parked_lock);
        set_bit(RBIO_PARKED_BIT, &rbio->flags);
+       set_bit(RBIO_WAS_PARKED_BIT, &rbio->flags);
        rbio->park_deadline = jiffies + msecs_to_jiffies(timeout_ms);
        list_add_tail(&rbio->parked_node, &table->parked);
        spin_unlock(&table->parked_lock);
+       atomic64_inc(&fs_info->stripe_park_stats.parked);
        queue_delayed_work(system_percpu_wq, &table->parked_work,
                           msecs_to_jiffies(BTRFS_RBIO_PARK_SCAN_MS));
 
@@ -835,6 +843,7 @@ static void unpark_full_rbio(struct btrfs_raid_bio *rbio)
        }
        rbio_unpark_locked(rbio);
        spin_unlock(&table->parked_lock);
+       atomic64_inc(&rbio->bioc->fs_info->stripe_park_stats.filled);
        start_async_work(rbio, rmw_rbio_work_locked);
 }
 
@@ -868,6 +877,7 @@ void btrfs_flush_parked_rbios(struct btrfs_fs_info *fs_info, u64 start,
 
        list_for_each_entry_safe(rbio, tmp, &flush, parked_node) {
                list_del_init(&rbio->parked_node);
+               atomic64_inc(&fs_info->stripe_park_stats.kicked);
                start_async_work(rbio, rmw_rbio_work_locked);
        }
 }
@@ -897,6 +907,7 @@ static void parked_rbios_timeout_work(struct work_struct *work)
 
        list_for_each_entry_safe(rbio, tmp, &flush, parked_node) {
                list_del_init(&rbio->parked_node);
+               atomic64_inc(&rbio->bioc->fs_info->stripe_park_stats.expired);
                start_async_work(rbio, rmw_rbio_work_locked);
        }
        if (rearm)
@@ -2624,6 +2635,7 @@ static bool rmw_try_pad_full(struct btrfs_raid_bio *rbio)
        set_bit(RBIO_PADDED_BIT, &rbio->flags);
        /* The whole stripe goes out: every vertical position is written. */
        bitmap_set(&rbio->dbitmap, 0, rbio->stripe_nsectors);
+       atomic64_inc(&fs_info->stripe_park_stats.padded);
        return true;
 }
 
@@ -2692,6 +2704,10 @@ static void rmw_rbio(struct btrfs_raid_bio *rbio)
         */
        if (!rbio_is_full(rbio) && !rmw_try_pad_full(rbio) &&
            need_read_stripe_sectors(rbio)) {
+               /* A parked-then-flushed write that still had to read. */
+               if (test_bit(RBIO_WAS_PARKED_BIT, &rbio->flags))
+                       atomic64_inc(&rbio->bioc->fs_info->
+                                    stripe_park_stats.rmw_reads);
                /*
                 * Now we're doing sub-stripe write, also need all data stripes
                 * to do the full RMW.
index 53d4667230555ef981d82bb7faf8ec7b9be81575..f2389424ea1e77b618cbdad177faf215325472c0 100644 (file)
@@ -1219,6 +1219,29 @@ static ssize_t btrfs_commit_stats_store(struct kobject *kobj,
 }
 BTRFS_ATTR_RW(, commit_stats, btrfs_commit_stats_show, btrfs_commit_stats_store);
 
+static ssize_t btrfs_stripe_park_stats_show(struct kobject *kobj,
+                                           struct kobj_attribute *a, char *buf)
+{
+       struct btrfs_fs_info *fs_info = to_fs_info(kobj);
+
+       return sysfs_emit(buf,
+               "parked %lld\n"
+               "merged_bytes %lld\n"
+               "filled %lld\n"
+               "kicked %lld\n"
+               "expired %lld\n"
+               "padded %lld\n"
+               "rmw_reads %lld\n",
+               atomic64_read(&fs_info->stripe_park_stats.parked),
+               atomic64_read(&fs_info->stripe_park_stats.merged_bytes),
+               atomic64_read(&fs_info->stripe_park_stats.filled),
+               atomic64_read(&fs_info->stripe_park_stats.kicked),
+               atomic64_read(&fs_info->stripe_park_stats.expired),
+               atomic64_read(&fs_info->stripe_park_stats.padded),
+               atomic64_read(&fs_info->stripe_park_stats.rmw_reads));
+}
+BTRFS_ATTR(, stripe_park_stats, btrfs_stripe_park_stats_show);
+
 static ssize_t btrfs_clone_alignment_show(struct kobject *kobj,
                                struct kobj_attribute *a, char *buf)
 {
@@ -1630,6 +1653,7 @@ static const struct attribute *btrfs_attrs[] = {
        BTRFS_ATTR_PTR(, read_policy),
        BTRFS_ATTR_PTR(, bg_reclaim_threshold),
        BTRFS_ATTR_PTR(, commit_stats),
+       BTRFS_ATTR_PTR(, stripe_park_stats),
        BTRFS_ATTR_PTR(, temp_fsid),
 #ifdef CONFIG_BTRFS_EXPERIMENTAL
        BTRFS_ATTR_PTR(, offload_csum),