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);