block: qcow: Reuse relocated refcount block clusters

Metadata tables use relocate on write. The first modification after a
flush writes the table to a new cluster and frees the old one. The L2
path returns the old cluster to the free list, but the refcount block
path did not. QcowState::set_cluster_refcount set the dropped refcount
block cluster to refcount 0 yet never pushed it onto unref_clusters, so
it stayed committed on the host and was invisible to the allocator.
Every refcount block relocation stranded one cluster, and
get_new_cluster extended the file instead of reusing it.

Under a sync heavy workload each fsync that dirties a table relocates
it, so the physical footprint grew at roughly twice the live data
without bound. qemu-img check stayed clean because the refcounts are
correct, and a reopen, which rebuilds the free list from refcounts,
recovered the space.

Push the dropped refcount block cluster onto unref_clusters so the
running allocator reuses it, matching the L2 table path.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-07-15 18:12:58 +02:00
committed by Rob Bradford
parent 8b61fd0238
commit 57f67a543a

View File

@@ -954,6 +954,7 @@ impl QcowState {
}
Ok(Some(freed_cluster)) => {
let mut freed = self.set_cluster_refcount(freed_cluster, 0)?;
unref_clusters.push(freed_cluster);
unref_clusters.append(&mut freed);
refcount_set = true;
}