From 57f67a543a07a3bff7b66fa1cc172f4f3b5a48ec Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Wed, 15 Jul 2026 18:12:58 +0200 Subject: [PATCH] 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 --- block/src/formats/qcow/metadata.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/block/src/formats/qcow/metadata.rs b/block/src/formats/qcow/metadata.rs index aa477f1d9..48f67af4b 100644 --- a/block/src/formats/qcow/metadata.rs +++ b/block/src/formats/qcow/metadata.rs @@ -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; }