Commit 27f1fb7af6 for qemu.org
commit 27f1fb7af6073f6c0c5cac619a5d3dd2519e23a0
Author: Denis V. Lunev <den@openvz.org>
Date: Thu Dec 28 11:12:24 2023 +0100
parallels: Handle L1 entries equal to one
If all the bits in a dirty bitmap cluster are ones, the cluster shouldn't
be written. Instead the corresponding L1 entry should be set to 1.
Ask bdrv_dirty_bitmap_next_zero() for a clean bit in the range the entry
covers, and store the marker when there is none. Two things have to
match the region rather than the cluster which serializes it: the search
is bounded by the end of the region, as a count and not as an offset, and
a missing clean bit is the answer we are looking for rather than a reason
to give up. A dirty run is not a substitute for either. Its length says
nothing about the region it lies in, so comparing it to the cluster size
marks a chunk as all ones when only a cluster worth of it is dirty, and
treating "no clean bit at all" as a failure leaves the entry at zero,
which says the whole chunk is clean.
Both directions are silent: a full disk overwrite comes back as a
completely clean bitmap, and a single dirty cluster comes back as
everything the entry covers being dirty. An incremental backup driven by
the first one copies nothing.
The marker goes through cpu_to_le64() like the cluster offsets below it,
as the loading side reads the table with ldq_le_p(). The serialization
moved behind the check, as there is nothing to write when the marker is
stored.
Based on the original work from Alexander Ivanov.
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Denis V. Lunev <den@openvz.org>
diff --git a/block/parallels-ext.c b/block/parallels-ext.c
index e89c489730..6a889d86fa 100644
--- a/block/parallels-ext.c
+++ b/block/parallels-ext.c
@@ -439,12 +439,20 @@ static int GRAPH_RDLOCK parallels_save_bitmap(BlockDriverState *bs,
offset = 0;
while ((offset = bdrv_dirty_bitmap_next_dirty(bitmap, offset,
bm_size)) >= 0) {
- int64_t cluster_off, end, write_size;
+ int64_t cluster_off, end, write_size, first_zero;
idx = offset / limit;
offset = QEMU_ALIGN_DOWN(offset, limit);
end = MIN(bm_size, offset + limit);
+
+ first_zero = bdrv_dirty_bitmap_next_zero(bitmap, offset, end - offset);
+ if (first_zero < 0) {
+ l1_table[idx] = cpu_to_le64(1);
+ offset = end;
+ continue;
+ }
+
write_size = bdrv_dirty_bitmap_serialization_size(bitmap, offset,
end - offset);
assert(write_size <= s->cluster_size);