*/
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;
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;
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;
* 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;
}
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);
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
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 +
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)
"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"
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),
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,