return false;
}
+/*
+ * Report a sub-stripe write that is going out as a read-modify-write.
+ *
+ * With stripe_alloc on, data never gets here: it is either a full stripe or
+ * padded up to one. Metadata, system chunks and anything in a mixed block
+ * group are not covered by the allocator, so those still update stripes in
+ * place -- and every such write is a write hole window, where parity and
+ * data reach the disk separately and a crash between them leaves the stripe
+ * unreconstructible. The mount-time warning says the exposure exists; this
+ * says it is actually happening, and how often.
+ *
+ * Rate limited: a raid56-metadata filesystem does this continuously, and
+ * the point is to make the exposure visible, not to fill the log. Silent
+ * when stripe_alloc is off, where the entire filesystem works this way and
+ * the user has asked for nothing else.
+ */
+static void report_uncovered_rmw(struct btrfs_raid_bio *rbio)
+{
+ struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
+
+ if (!btrfs_test_opt(fs_info, STRIPE_ALLOC))
+ return;
+ /*
+ * The exact complement of btrfs_is_stripe_alloc_bg(): everything
+ * that is not data-without-metadata. Note this includes system
+ * chunks, which carry no METADATA bit but are the chunk tree.
+ */
+ if ((rbio->bioc->map_type & BTRFS_BLOCK_GROUP_DATA) &&
+ !(rbio->bioc->map_type & BTRFS_BLOCK_GROUP_METADATA))
+ return;
+
+ atomic64_inc(&fs_info->stripe_park_stats.meta_rmw);
+ btrfs_warn_rl(fs_info,
+"read-modify-write of full stripe %llu: this block group is not covered by stripe_alloc, the raid56 write hole applies to it",
+ rbio->bioc->full_stripe_logical);
+}
+
static void rmw_rbio(struct btrfs_raid_bio *rbio)
{
struct bio_list bio_list;
goto out;
}
+ /* Still a sub-stripe write: this stripe is being modified in place. */
+ if (!rbio_is_full(rbio) && !test_bit(RBIO_PADDED_BIT, &rbio->flags))
+ report_uncovered_rmw(rbio);
+
/*
* At this stage we're not allowed to add any new bios to the
* bio list any more, anyone else that wants to change this stripe
"kicked %lld\n"
"expired %lld\n"
"padded %lld\n"
- "rmw_reads %lld\n",
+ "rmw_reads %lld\n"
+ "meta_rmw %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));
+ atomic64_read(&fs_info->stripe_park_stats.rmw_reads),
+ atomic64_read(&fs_info->stripe_park_stats.meta_rmw));
}
BTRFS_ATTR(, stripe_park_stats, btrfs_stripe_park_stats_show);