From: Zygo Blaxell Date: Sat, 8 Aug 2026 07:57:22 +0000 (-0400) Subject: btrfs: stripe_alloc: trace why padding declined, at the moment it declined X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4fef2cc0de03b18f9e2b45f7d5ebb0b592b29e7f;p=linux btrfs: stripe_alloc: trace why padding declined, at the moment it declined 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 --- diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 8fd2b2a9cd168..587490d58d37e 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -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; } diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h index 590d62f59f9b9..1d75703ddb0a7 100644 --- a/fs/btrfs/block-group.h +++ b/fs/btrfs/block-group.h @@ -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); diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index 97fa603208655..919851032b046 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -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 diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index c3dda5dda9904..2a439066a2dd5 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -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) diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index ee39265e59c09..06ca412ae5687 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -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), diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index e1af4dd8287ce..db541d70e9640 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -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,