btrfs: raid56: say whether a metadata read-modify-write is a write hole
meta_rmw counts sub-stripe metadata writes, which is a proxy for exposure
rather than a measurement of it. A stripe modified in place is only a write
hole if it holds data some completed transaction is relying on; a stripe
that two writes of the *same* transaction happen to split costs an extra
read but risks nothing, because a tear loses that whole transaction anyway.
The counter cannot tell those apart, so it cannot answer the only question
that matters.
The rbio can. A read-modify-write has already read every column the write
does not cover, so at the point of the report it is holding the stripe's
on-disk contents. Walk the uncovered tree block positions and read their
headers: a block whose bytenr and fsid match belongs there, and its
generation says which transaction put it there. Compare that against the
generation of the blocks this write is carrying, taken from the same rbio,
so a transaction committing concurrently cannot skew the verdict:
meta_rmw_cur the same transaction's own blocks -- a cost
meta_rmw_old an earlier transaction's -- a write hole
meta_rmw_free no tree block there at all
This trusts nothing the allocator says about itself. The stripe runs, the
liveness map and the drain accounting are all bookkeeping that could be
wrong in the same way twice; the header in the sector is what a degraded
read would actually have to reconstruct.
On a 3-device raid5 filesystem with raid5 metadata, 6000 small files with
periodic syncs and then a third rewritten, plain stripe_alloc reports 249
sub-stripe metadata writes of which 174 rewrite parity over committed tree
blocks. Adding stripe_meta leaves 37 sub-stripe writes and none of them.