Public interface of block-status is: bdrv_block_status bdrv_block_status_above bdrv_is_allocated bdrv_is_allocated_above = bdrv_block_status = bdrv_make_zero: works on current level of backing-chain, want's to skip zeroes, not interested in @map and @file img convert: convert_iteration_sectors: wants to distinguish ZERO, DATA and go-to-backing. It also tries to not write zeroes, if have short backing file, but does it a bit wrong. Treats unallocated as DATA if no backing. img-map: get_block_status: distinguish ZERO, DATA and go-to-backing. Count depth of the backing. Just reports final ZERO and DATA. So, fs-unallocated thing is reported to user = bdrv_block_status_above = block-copy: block_copy_block_status: wants two things: 1. skip go-to-backing holes in top layer for top mode 2. do write_zero for ZERO areas mirror: call on the whole backing chain - for DATA (and for DATA|ZERO which is bad) do just copy - for ZERO do just ZERO - for 0 (which means that bottom layer doesn't report that unallocated are zero) does DISCARD (which is most-probably zeroing) - absolutely wrong thing qcow2: is_zero: call on the whole backing chain, want's just to check is reads-as-zero or not. qcow2: qcow2_measure: call on the whole backing chain: - skip ZERO - count clusters with both DATA and ALLOCATED set. Hmm. ALLOCATED is always set for DATA. Seems the function actually tries to calculate disk occupation, assuming that BDRV_BLOCK_ALLOCATED helps in it, but it actually doesn't.. I think, correct solution is to support offset and bytes in bdrv_measure, and split it from block_status. Then qcow2_measure will just recursively call bdrv_measure on its children. This would be clean. nbd: nbd_co_send_sparse_read: call on the whole backing chain: - wants to distinguish zeroes nbd: blockstatus_to_extents: call on the whole backing chain: !ALLOCATED -> NBD_HOLE ZERO -> NBD_ZERO So, we report HOLE only if it's not BDRV_BLOCK_ALLOCATED on any layer.. That's wrong. I think, we should report HOLE in a lot more cases. Actually, when not occupy real space on disk. img-compare: call on the whole backing chain: - do not compare zeroes - do not compare if both report unallocated.. it's actually not correct for protocols which reports fs-unallocated-non-zeroes. As reads may differ actually. Still, read from fs-unallocated area is not guaranteed to return same thing each time, yes? At least, null-co doesn't guarantee it :).. So, it may be correct to skip these areas. Or may be better to always report them different?? - consider data-zeroes equal to unallocated.. it's definitely not correct for protocols which reports fs-unallocated-non-zeroes I think, img-compare must only consider zero/non-zero, and don't touch other block-status features. Otherwise it's a mess img-convert: convert_iteration_sectors: call on the whole backing chain: already described in bdrv_block_status section = bdrv_is_allocated = Obvious thing for backing-chain related operation (still wrong that some protocol drivers may return fs-unallocated and it is treated as go-to-backing areas): block-copy, commit, copy-on-read, stream, img-rebase Others: vvfat: o_O it has qcow child.. and operates like self is a backing of this child. But yes, it just uses bdrv_is_allocated to understand is chunk is rewritten in qcow. migration/block: skip unallocated for top mode (shared_base, as it called here) io-alloc: just report number of allocated in top layer io-map: map_is_allocated: same thing as io-alloc, but report chunks test_sync_op_block_status: just check what it returns = bdrv_is_allocated_above = Obvious usage for backing-chain related: commit, mirror, stream, img-rebase. Wrong for fs-unallocated-non-zero reporting drivers Others: qcow2: is_unallocated: call for the whole backing chain. Used to check is-zero.. Wrong for fs-unallocated-non-zero reporting drivers, and may be more efficient if consider also ZERO status.. but in some smart-fast way. replication: allocated or not in backing-chain: common case