diff --git a/libvirt-qemu-migration-Always-offer-block-dirty-bitmaps-during-migration.patch b/libvirt-qemu-migration-Always-offer-block-dirty-bitmaps-during-migration.patch new file mode 100644 index 0000000..ac29a72 --- /dev/null +++ b/libvirt-qemu-migration-Always-offer-block-dirty-bitmaps-during-migration.patch @@ -0,0 +1,147 @@ +From 64ae2c71b95cd25fb1e18fbc68ddbc814f3de8ca Mon Sep 17 00:00:00 2001 +Message-ID: <64ae2c71b95cd25fb1e18fbc68ddbc814f3de8ca.1770203422.git.jdenemar@redhat.com> +From: Peter Krempa +Date: Tue, 27 Jan 2026 17:00:10 +0100 +Subject: [PATCH] qemu: migration: Always offer block dirty bitmaps during + migration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Until now block dirty bitmaps were offered to destination only if +non-shared storage migration was enabled. + +Upcoming patches will want to support it also in cases when storage is +shared but the destination has a qcow2 overlay using the 'data_file' +feature where the qcow2 overlay is not actually shared. + +To support that we'll now always offer bitmaps for migration. The +destination can then decide (using existing logic) to pick only the +ones that are not present in the image on destination, which is how +it was supposed to work even now. + +The patch removes all the flag checks and simply offers bitmaps in any +case. The overhead incurred by this is one 'query-named-block-nodes' +call to qemu. + +Signed-off-by: Peter Krempa +Reviewed-by: Ján Tomko +(cherry picked from commit a4f610ff3fe190058f18baea18b095d0bc69441b) + +https://issues.redhat.com/browse/RHEL-145769 [rhel-10.2] +https://issues.redhat.com/browse/RHEL-145770 [rhel-9.8] +--- + src/qemu/qemu_migration.c | 28 +++++++--------------------- + 1 file changed, 7 insertions(+), 21 deletions(-) + +diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c +index 723e131c98..755b9a5e1a 100644 +--- a/src/qemu/qemu_migration.c ++++ b/src/qemu/qemu_migration.c +@@ -2580,16 +2580,13 @@ qemuMigrationAnyConnectionClosed(virDomainObj *vm, + * qemuMigrationSrcBeginPhaseBlockDirtyBitmaps: + * @mig: migration cookie struct + * @vm: domain object +- * @migrate_disks: disks which are being migrated +- * @nmigrage_disks: number of @migrate_disks + * + * Enumerates block dirty bitmaps on disks which will undergo storage migration + * and fills them into @mig to be offered to the destination. + */ + static int + qemuMigrationSrcBeginPhaseBlockDirtyBitmaps(qemuMigrationCookie *mig, +- virDomainObj *vm, +- const char **migrate_disks) ++ virDomainObj *vm) + + { + GSList *disks = NULL; +@@ -2611,9 +2608,6 @@ qemuMigrationSrcBeginPhaseBlockDirtyBitmaps(qemuMigrationCookie *mig, + if (!nodedata) + continue; + +- if (!qemuMigrationAnyCopyDisk(diskdef, migrate_disks)) +- continue; +- + for (j = 0; j < nodedata->nbitmaps; j++) { + qemuMigrationBlockDirtyBitmapsDiskBitmap *bitmap; + +@@ -2680,7 +2674,6 @@ qemuMigrationSrcBeginXML(virDomainObj *vm, + char **cookieout, + int *cookieoutlen, + unsigned int cookieFlags, +- const char **migrate_disks, + unsigned int flags) + { + qemuDomainObjPrivate *priv = vm->privateData; +@@ -2696,8 +2689,7 @@ qemuMigrationSrcBeginXML(virDomainObj *vm, + if (!(mig = qemuMigrationCookieNew(vm->def, priv->origname))) + return NULL; + +- if (cookieFlags & QEMU_MIGRATION_COOKIE_NBD && +- qemuMigrationSrcBeginPhaseBlockDirtyBitmaps(mig, vm, migrate_disks) < 0) ++ if (qemuMigrationSrcBeginPhaseBlockDirtyBitmaps(mig, vm) < 0) + return NULL; + + if (qemuMigrationCookieFormat(mig, driver, vm, +@@ -2879,8 +2871,7 @@ qemuMigrationSrcBeginPhase(virQEMUDriver *driver, + return NULL; + + return qemuMigrationSrcBeginXML(vm, xmlin, +- cookieout, cookieoutlen, cookieFlags, +- migrate_disks, flags); ++ cookieout, cookieoutlen, cookieFlags, flags); + } + + +@@ -2969,8 +2960,7 @@ qemuMigrationSrcBeginResume(virDomainObj *vm, + return NULL; + } + +- return qemuMigrationSrcBeginXML(vm, xmlin, +- cookieout, cookieoutlen, 0, NULL, flags); ++ return qemuMigrationSrcBeginXML(vm, xmlin, cookieout, cookieoutlen, 0, flags); + } + + +@@ -4752,7 +4742,6 @@ qemuMigrationSrcRunPrepareBlockDirtyBitmaps(virDomainObj *vm, + + /* For VIR_MIGRATE_NON_SHARED_INC we can migrate the bitmaps directly, + * otherwise we must create merged bitmaps from the whole chain */ +- + if (!(flags & VIR_MIGRATE_NON_SHARED_INC) && + qemuMigrationSrcRunPrepareBlockDirtyBitmapsMerge(vm, mig) < 0) + return -1; +@@ -4943,7 +4932,7 @@ qemuMigrationSrcRun(virQEMUDriver *driver, + VIR_AUTOCLOSE fd = -1; + unsigned long restore_max_bandwidth = priv->migMaxBandwidth; + virErrorPtr orig_err = NULL; +- unsigned int cookieFlags = 0; ++ unsigned int cookieFlags = QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS; + bool abort_on_error = !!(flags & VIR_MIGRATE_ABORT_ON_ERROR); + bool storageMigration = flags & (VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC); + bool cancel = false; +@@ -4967,10 +4956,8 @@ qemuMigrationSrcRun(virQEMUDriver *driver, + storageMigration = qemuMigrationHasAnyStorageMigrationDisks(vm->def, + migrate_disks); + +- if (storageMigration) { ++ if (storageMigration) + cookieFlags |= QEMU_MIGRATION_COOKIE_NBD; +- cookieFlags |= QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS; +- } + + if (virLockManagerPluginUsesState(driver->lockManager) && + !cookieout) { +@@ -5004,8 +4991,7 @@ qemuMigrationSrcRun(virQEMUDriver *driver, + cookiein, cookieinlen, + cookieFlags | + QEMU_MIGRATION_COOKIE_GRAPHICS | +- QEMU_MIGRATION_COOKIE_CAPS | +- QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS); ++ QEMU_MIGRATION_COOKIE_CAPS); + if (!mig) + goto error; + +-- +2.52.0 diff --git a/libvirt-qemu-monitor-Detect-list-of-bitmaps-from-qcow2-format-specific-data.patch b/libvirt-qemu-monitor-Detect-list-of-bitmaps-from-qcow2-format-specific-data.patch new file mode 100644 index 0000000..6c5623b --- /dev/null +++ b/libvirt-qemu-monitor-Detect-list-of-bitmaps-from-qcow2-format-specific-data.patch @@ -0,0 +1,173 @@ +From 0722d1d58171ca59ec1852026cf6df0faa0d9312 Mon Sep 17 00:00:00 2001 +Message-ID: <0722d1d58171ca59ec1852026cf6df0faa0d9312.1770203422.git.jdenemar@redhat.com> +From: Peter Krempa +Date: Tue, 27 Jan 2026 20:07:32 +0100 +Subject: [PATCH] qemu: monitor: Detect list of bitmaps from 'qcow2' format + specific data +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We currently probe dirty block tracking bitmaps by looking at the loaded +ones ('dirty-bitmaps'). Unfortunately those may not yet be populated on +incoming migration when the image was not yet activated, but we need to +know which ones are stored in the image so that we don't migrate those +explicitly, which would fail. + +Load the list of bitmaps in a qcow2 image from the format specific data, +which is already loaded at that point. + +Signed-off-by: Peter Krempa +Reviewed-by: Ján Tomko +(cherry picked from commit b2fe3465de1db033e436f38fdd24648c8c884a3d) + +https://issues.redhat.com/browse/RHEL-145769 [rhel-10.2] +https://issues.redhat.com/browse/RHEL-145770 [rhel-9.8] +--- + src/qemu/qemu_monitor.h | 4 ++++ + src/qemu/qemu_monitor_json.c | 17 +++++++++++++++++ + tests/qemublocktest.c | 11 +++++++++++ + tests/qemublocktestdata/bitmap/snapshots.out | 5 +++++ + tests/qemublocktestdata/bitmap/synthetic.out | 4 ++++ + 5 files changed, 41 insertions(+) + +diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h +index d096f474c1..041aa7bc12 100644 +--- a/src/qemu/qemu_monitor.h ++++ b/src/qemu/qemu_monitor.h +@@ -893,8 +893,12 @@ struct _qemuBlockNamedNodeData { + unsigned long long capacity; + unsigned long long physical; + ++ /* Information about change block tracking bitmaps which are active and loaded */ + qemuBlockNamedNodeDataBitmap **bitmaps; + size_t nbitmaps; ++ /* With qcow2 we have also a separate list of bitmaps present in the image ++ * but not yet activated, which happens when starting qemu during migration */ ++ char **qcow2bitmaps; + + /* hash table indexed by snapshot name containing data about snapshots + * (qemuBlockNamedNodeDataSnapshot) */ +diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c +index 494d7ef515..401a28ff9a 100644 +--- a/src/qemu/qemu_monitor_json.c ++++ b/src/qemu/qemu_monitor_json.c +@@ -2730,6 +2730,7 @@ qemuMonitorJSONBlockNamedNodeDataFree(qemuBlockNamedNodeData *data) + qemuMonitorJSONBlockNamedNodeDataBitmapFree(data->bitmaps[i]); + g_clear_pointer(&data->snapshots, g_hash_table_unref); + g_free(data->bitmaps); ++ g_strfreev(data->qcow2bitmaps); + g_free(data); + } + G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuBlockNamedNodeData, qemuMonitorJSONBlockNamedNodeDataFree); +@@ -2854,6 +2855,9 @@ qemuMonitorJSONBlockGetNamedNodeDataWorker(size_t pos G_GNUC_UNUSED, + virJSONValue *qcow2props = virJSONValueObjectGetObject(format_specific, "data"); + + if (qcow2props) { ++ virJSONValue *bmp; ++ size_t nbmp; ++ + if (STREQ_NULLABLE(virJSONValueObjectGetString(qcow2props, "compat"), "0.10")) + ent->qcow2v2 = true; + +@@ -2862,6 +2866,19 @@ qemuMonitorJSONBlockGetNamedNodeDataWorker(size_t pos G_GNUC_UNUSED, + + ignore_value(virJSONValueObjectGetBoolean(qcow2props, "data-file-raw", + &ent->qcow2dataFileRaw)); ++ ++ if ((bmp = virJSONValueObjectGetArray(qcow2props, "bitmaps")) && ++ ((nbmp = virJSONValueArraySize(bmp)) > 0)) { ++ size_t i; ++ ++ ent->qcow2bitmaps = g_new0(char *, nbmp + 1); ++ ++ for (i = 0; i < nbmp; i++) { ++ virJSONValue *b = virJSONValueArrayGet(bmp, i); ++ ++ ent->qcow2bitmaps[i] = g_strdup(virJSONValueObjectGetString(b, "name")); ++ } ++ } + } + } + +diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c +index 51d9268cdd..18ec90edf5 100644 +--- a/tests/qemublocktest.c ++++ b/tests/qemublocktest.c +@@ -600,6 +600,17 @@ testQemuDetectBitmapsWorker(void *payload, + bitmap->granularity, bitmap->dirtybytes); + } + ++ if (data->qcow2bitmaps) { ++ char **b; ++ ++ virBufferAddLit(buf, "qcow2 bitmaps:"); ++ ++ for (b = data->qcow2bitmaps; *b; b++) ++ virBufferAsprintf(buf, " %s", *b); ++ ++ virBufferAddLit(buf, "\n"); ++ } ++ + if (data->snapshots) { + g_autofree virHashKeyValuePair *snaps = virHashGetItems(data->snapshots, NULL, true); + virHashKeyValuePair *n; +diff --git a/tests/qemublocktestdata/bitmap/snapshots.out b/tests/qemublocktestdata/bitmap/snapshots.out +index 29c586be7e..dedd77465c 100644 +--- a/tests/qemublocktestdata/bitmap/snapshots.out ++++ b/tests/qemublocktestdata/bitmap/snapshots.out +@@ -4,21 +4,26 @@ libvirt-1-format: + b: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + c: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + current: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++ qcow2 bitmaps: current c b a d + libvirt-1-storage: + libvirt-2-format: + c: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + b: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + d: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++ qcow2 bitmaps: d a b c + libvirt-2-storage: + libvirt-3-format: + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + b: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + c: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++ qcow2 bitmaps: c b a + libvirt-3-storage: + libvirt-4-format: + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++ qcow2 bitmaps: a + libvirt-4-storage: + libvirt-5-format: + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++ qcow2 bitmaps: a + libvirt-5-storage: +diff --git a/tests/qemublocktestdata/bitmap/synthetic.out b/tests/qemublocktestdata/bitmap/synthetic.out +index 2f4ae2b217..0a47a90107 100644 +--- a/tests/qemublocktestdata/bitmap/synthetic.out ++++ b/tests/qemublocktestdata/bitmap/synthetic.out +@@ -6,17 +6,21 @@ libvirt-1-format: + top-inactive: record:0 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + top-transient: record:1 busy:0 persist:0 inconsist:0 gran:65536 dirty:0 + top-transient-inactive: record:0 busy:0 persist:0 inconsist:0 gran:65536 dirty:0 ++ qcow2 bitmaps: current + libvirt-1-storage: + libvirt-2-format: + d: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++ qcow2 bitmaps: d + libvirt-2-storage: + libvirt-3-format: + b: record:1 busy:0 persist:0 inconsist:0 gran:65536 dirty:0 + c: record:0 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + d: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++ qcow2 bitmaps: c b + libvirt-3-storage: + libvirt-4-format: + libvirt-4-storage: + libvirt-5-format: + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++ qcow2 bitmaps: a + libvirt-5-storage: +-- +2.52.0 diff --git a/libvirt-qemuMigrationDstPrepareAnyBlockDirtyBitmaps-Always-consider-offered-bitmaps.patch b/libvirt-qemuMigrationDstPrepareAnyBlockDirtyBitmaps-Always-consider-offered-bitmaps.patch new file mode 100644 index 0000000..36b02c6 --- /dev/null +++ b/libvirt-qemuMigrationDstPrepareAnyBlockDirtyBitmaps-Always-consider-offered-bitmaps.patch @@ -0,0 +1,98 @@ +From bbd0fde5f840726928cdcd06691da13f1093c02e Mon Sep 17 00:00:00 2001 +Message-ID: +From: Peter Krempa +Date: Tue, 27 Jan 2026 17:28:48 +0100 +Subject: [PATCH] qemuMigrationDstPrepareAnyBlockDirtyBitmaps: Always consider + offered bitmaps +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Consider bitmaps for incoming migration regardless of non-shared storage +flag. + +When bitmaps are offered from the source, consult the local image if the +bitmap is present and if not accept migration. Migration of bitmaps +which exist in the qcow2 metadata is skipped because qemu rejects such +setup (although handles it correctly in case of shared storage setup; +see below). + +This allows bitmap propagation for cases when the qcow2 image is not +actually shared between destinations but the data is (using the +data_file feature). + +At the same time this preserves existing bitmap handling semantics for +other cases. Specifically qemu, in case of shared storage properly +propagates the bitmap which was already recorded in the qcow2 metadata +on disk even if libvirt doesn't instruct migration, yet tolerates +migration instruction if the file is not yet recorded in the on-disk +metadata. In both cases the contents are preserved correctly. + +When storage is not shared (which includes even cases when we migrate +it via NBD) it's expected that the bitmaps don't exist on the +destination and thus all will be picked for migration. We can also +infer that this wasn't ever a problem by the fact that the code skipping +migration of existing bitmaps was broken until recently, and qemu +would refuse such config. + +I've tested all the above scenarios including verifying that the +resulting bitmaps capture dirtied regions before and after migration. +For testing this the following command is useful: + + virsh qemu-monitor-command --domain DOMNAME --hmp 'qemu-io -d /machine/peripheral/virtio-disk0/virtio-backend "write -P 0xcc 4M 1M"' + +Which simulates a write from the guest side without the need to interact +with the guest OS. + +Signed-off-by: Peter Krempa +Reviewed-by: Ján Tomko +(cherry picked from commit bf34b90bb4e39b3a2fd22a5531f5ea5a95681015) + +https://issues.redhat.com/browse/RHEL-145769 [rhel-10.2] +https://issues.redhat.com/browse/RHEL-145770 [rhel-9.8] +--- + src/qemu/qemu_migration.c | 10 +++------- + 1 file changed, 3 insertions(+), 7 deletions(-) + +diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c +index 755b9a5e1a..2a4df1191d 100644 +--- a/src/qemu/qemu_migration.c ++++ b/src/qemu/qemu_migration.c +@@ -3183,7 +3183,6 @@ qemuMigrationDstPrepare(virQEMUDriver *driver, + * @vm: domain object + * @mig: migration cookie + * @migParams: migration parameters +- * @flags: migration flags + * + * Checks whether block dirty bitmaps offered by the migration source are + * to be migrated (e.g. they don't exist, the destination is compatible etc) +@@ -3194,16 +3193,13 @@ qemuMigrationDstPrepare(virQEMUDriver *driver, + static int + qemuMigrationDstPrepareAnyBlockDirtyBitmaps(virDomainObj *vm, + qemuMigrationCookie *mig, +- qemuMigrationParams *migParams, +- unsigned int flags) ++ qemuMigrationParams *migParams) + { + g_autoptr(virJSONValue) mapping = NULL; + g_autoptr(GHashTable) blockNamedNodeData = NULL; + GSList *nextdisk; + +- if (!mig->nbd || +- !mig->blockDirtyBitmaps || +- !(flags & (VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC))) ++ if (!mig->blockDirtyBitmaps) + return 0; + + if (qemuMigrationCookieBlockDirtyBitmapsMatchDisks(vm->def, mig->blockDirtyBitmaps) < 0) +@@ -3350,7 +3346,7 @@ qemuMigrationDstPrepareActive(virQEMUDriver *driver, + goto error; + } + +- if (qemuMigrationDstPrepareAnyBlockDirtyBitmaps(vm, mig, migParams, flags) < 0) ++ if (qemuMigrationDstPrepareAnyBlockDirtyBitmaps(vm, mig, migParams) < 0) + goto error; + + if (qemuMigrationParamsCheck(vm, VIR_ASYNC_JOB_MIGRATION_IN, migParams, +-- +2.52.0 diff --git a/libvirt-qemuMigrationDstPrepareAnyBlockDirtyBitmaps-Fix-check-for-existing-bitmaps.patch b/libvirt-qemuMigrationDstPrepareAnyBlockDirtyBitmaps-Fix-check-for-existing-bitmaps.patch new file mode 100644 index 0000000..ccaef87 --- /dev/null +++ b/libvirt-qemuMigrationDstPrepareAnyBlockDirtyBitmaps-Fix-check-for-existing-bitmaps.patch @@ -0,0 +1,68 @@ +From b99fb51812da73df754151c59c259d15fa654168 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Peter Krempa +Date: Tue, 27 Jan 2026 19:22:08 +0100 +Subject: [PATCH] qemuMigrationDstPrepareAnyBlockDirtyBitmaps: Fix check for + existing bitmaps +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On incoming migration qemu doesn't load bitmaps into memory (which makes +them available under the 'dirty-bitmaps' field which we parse as the +'bitmaps' array in 'qemuBlockNamedNodeData') until after actually +resuming CPUs, thus the check for existing bitmaps never actually +worked. + +We need to check the 'qcow2bitmaps' field instead which is populated +from the qcow2 headers prior to activating the image. + +Signed-off-by: Peter Krempa +Reviewed-by: Ján Tomko +(cherry picked from commit 0c75fc1747f039bf878ceab69cf12482ebb14095) + +https://issues.redhat.com/browse/RHEL-145769 [rhel-10.2] +https://issues.redhat.com/browse/RHEL-145770 [rhel-9.8] +--- + src/qemu/qemu_migration.c | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c +index 9059f9aa3a..723e131c98 100644 +--- a/src/qemu/qemu_migration.c ++++ b/src/qemu/qemu_migration.c +@@ -3227,6 +3227,8 @@ qemuMigrationDstPrepareAnyBlockDirtyBitmaps(virDomainObj *vm, + qemuBlockNamedNodeData *nodedata; + GSList *nextbitmap; + ++ VIR_DEBUG("offer migrate bitmaps for '%s'", disk->target); ++ + if (!(nodedata = virHashLookup(blockNamedNodeData, disk->nodename))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to find data for block node '%1$s'"), +@@ -3243,18 +3245,14 @@ qemuMigrationDstPrepareAnyBlockDirtyBitmaps(virDomainObj *vm, + + for (nextbitmap = disk->bitmaps; nextbitmap; nextbitmap = nextbitmap->next) { + qemuMigrationBlockDirtyBitmapsDiskBitmap *bitmap = nextbitmap->data; +- size_t k; + + /* don't migrate into existing bitmaps */ +- for (k = 0; k < nodedata->nbitmaps; k++) { +- if (STREQ(bitmap->bitmapname, nodedata->bitmaps[k]->name)) { +- bitmap->skip = true; +- break; +- } +- } ++ if (nodedata->qcow2bitmaps) ++ bitmap->skip = g_strv_contains((const char **) nodedata->qcow2bitmaps, bitmap->bitmapname); ++ ++ VIR_DEBUG("offer migrate bitmap '%s' disk '%s' -> skip: '%d'", ++ bitmap->bitmapname, disk->target, bitmap->skip); + +- if (bitmap->skip) +- continue; + } + } + +-- +2.52.0 diff --git a/libvirt-qemublocktest-Iterate-all-nodenames-in-testQemuDetectBitmaps.patch b/libvirt-qemublocktest-Iterate-all-nodenames-in-testQemuDetectBitmaps.patch new file mode 100644 index 0000000..5cabc58 --- /dev/null +++ b/libvirt-qemublocktest-Iterate-all-nodenames-in-testQemuDetectBitmaps.patch @@ -0,0 +1,175 @@ +From 65beee7622ce4f110dbd56c631502444197756e6 Mon Sep 17 00:00:00 2001 +Message-ID: <65beee7622ce4f110dbd56c631502444197756e6.1770203422.git.jdenemar@redhat.com> +From: Peter Krempa +Date: Tue, 27 Jan 2026 22:49:09 +0100 +Subject: [PATCH] qemublocktest: Iterate all nodenames in + 'testQemuDetectBitmaps' +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Rather than looking for 30 specific nodenames (via a loop) iterate +everything in the hash table (in a sorted order). This simplifies the +code and provides more test outputs on previously-ignored nodenames. + +The listing of internal snapshots in the output was also missing a +newline, which would now cause problems with multiple images reproted. + +Signed-off-by: Peter Krempa +Reviewed-by: Ján Tomko +(cherry picked from commit 71643c197e86a3adc792c0b2a378a245ae6c5756) + +https://issues.redhat.com/browse/RHEL-145769 [rhel-10.2] +https://issues.redhat.com/browse/RHEL-145770 [rhel-9.8] +--- + tests/qemublocktest.c | 24 +++++++------------ + tests/qemublocktestdata/bitmap/basic.out | 1 + + tests/qemublocktestdata/bitmap/empty.out | 1 + + .../bitmap/snapshots-internal.out | 7 ++++++ + tests/qemublocktestdata/bitmap/snapshots.out | 5 ++++ + tests/qemublocktestdata/bitmap/synthetic.out | 5 ++++ + 6 files changed, 28 insertions(+), 15 deletions(-) + +diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c +index 47746207cc..51d9268cdd 100644 +--- a/tests/qemublocktest.c ++++ b/tests/qemublocktest.c +@@ -575,17 +575,15 @@ testQemuImageCreate(const void *opaque) + + static const char *bitmapDetectPrefix = "qemublocktestdata/bitmap/"; + +-static void +-testQemuDetectBitmapsWorker(GHashTable *nodedata, ++static int ++testQemuDetectBitmapsWorker(void *payload, + const char *nodename, +- virBuffer *buf) ++ void *opaque) + { +- qemuBlockNamedNodeData *data; ++ qemuBlockNamedNodeData *data = payload; ++ virBuffer *buf = opaque; + size_t i; + +- if (!(data = virHashLookup(nodedata, nodename))) +- return; +- + virBufferAsprintf(buf, "%s:\n", nodename); + if (data->qcow2v2) + virBufferAddLit(buf, " qcow2 v2\n"); +@@ -617,9 +615,12 @@ testQemuDetectBitmapsWorker(GHashTable *nodedata, + + virBufferAsprintf(buf, " '%s'%s", (const char *) n->key, vms); + } ++ ++ virBufferAddLit(buf, "\n"); + } + + virBufferAdjustIndent(buf, -1); ++ return 0; + } + + +@@ -632,7 +633,6 @@ testQemuDetectBitmaps(const void *opaque) + g_autofree char *actual = NULL; + g_autofree char *expectpath = NULL; + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; +- size_t i; + + expectpath = g_strdup_printf("%s/%s%s.out", abs_srcdir, + bitmapDetectPrefix, name); +@@ -646,13 +646,7 @@ testQemuDetectBitmaps(const void *opaque) + return -1; + } + +- /* we detect for the first 30 nodenames for simplicity */ +- for (i = 0; i < 30; i++) { +- g_autofree char *nodename = g_strdup_printf("libvirt-%zu-format", i); +- +- testQemuDetectBitmapsWorker(nodedata, nodename, &buf); +- } +- ++ virHashForEachSorted(nodedata, testQemuDetectBitmapsWorker, &buf); + actual = virBufferContentAndReset(&buf); + + return virTestCompareToFile(actual, expectpath); +diff --git a/tests/qemublocktestdata/bitmap/basic.out b/tests/qemublocktestdata/bitmap/basic.out +index 5c4c35b3f0..b96ffe3d39 100644 +--- a/tests/qemublocktestdata/bitmap/basic.out ++++ b/tests/qemublocktestdata/bitmap/basic.out +@@ -4,3 +4,4 @@ libvirt-1-format: + c: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + b: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++libvirt-1-storage: +diff --git a/tests/qemublocktestdata/bitmap/empty.out b/tests/qemublocktestdata/bitmap/empty.out +index 3787cbd354..c9a5be4f07 100644 +--- a/tests/qemublocktestdata/bitmap/empty.out ++++ b/tests/qemublocktestdata/bitmap/empty.out +@@ -1 +1,2 @@ + libvirt-1-format: ++libvirt-1-storage: +diff --git a/tests/qemublocktestdata/bitmap/snapshots-internal.out b/tests/qemublocktestdata/bitmap/snapshots-internal.out +index dbb3cfded4..cf7bde96a5 100644 +--- a/tests/qemublocktestdata/bitmap/snapshots-internal.out ++++ b/tests/qemublocktestdata/bitmap/snapshots-internal.out +@@ -1,2 +1,9 @@ + libvirt-1-format: + internal snapshots: '1727868651'(*) '1727872064'(*) ++libvirt-1-storage: ++libvirt-2-storage: ++libvirt-pflash0-format: ++libvirt-pflash0-storage: ++libvirt-pflash1-format: ++ internal snapshots: '1727868651' '1727872064' ++libvirt-pflash1-storage: +diff --git a/tests/qemublocktestdata/bitmap/snapshots.out b/tests/qemublocktestdata/bitmap/snapshots.out +index 24ca27e4d8..29c586be7e 100644 +--- a/tests/qemublocktestdata/bitmap/snapshots.out ++++ b/tests/qemublocktestdata/bitmap/snapshots.out +@@ -4,16 +4,21 @@ libvirt-1-format: + b: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + c: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + current: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++libvirt-1-storage: + libvirt-2-format: + c: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + b: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + d: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++libvirt-2-storage: + libvirt-3-format: + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + b: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + c: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++libvirt-3-storage: + libvirt-4-format: + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++libvirt-4-storage: + libvirt-5-format: + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++libvirt-5-storage: +diff --git a/tests/qemublocktestdata/bitmap/synthetic.out b/tests/qemublocktestdata/bitmap/synthetic.out +index 45423903a0..2f4ae2b217 100644 +--- a/tests/qemublocktestdata/bitmap/synthetic.out ++++ b/tests/qemublocktestdata/bitmap/synthetic.out +@@ -6,12 +6,17 @@ libvirt-1-format: + top-inactive: record:0 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + top-transient: record:1 busy:0 persist:0 inconsist:0 gran:65536 dirty:0 + top-transient-inactive: record:0 busy:0 persist:0 inconsist:0 gran:65536 dirty:0 ++libvirt-1-storage: + libvirt-2-format: + d: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++libvirt-2-storage: + libvirt-3-format: + b: record:1 busy:0 persist:0 inconsist:0 gran:65536 dirty:0 + c: record:0 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 + d: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++libvirt-3-storage: + libvirt-4-format: ++libvirt-4-storage: + libvirt-5-format: + a: record:1 busy:0 persist:1 inconsist:0 gran:65536 dirty:0 ++libvirt-5-storage: +-- +2.52.0 diff --git a/libvirt.spec b/libvirt.spec index b41c459..f937200 100644 --- a/libvirt.spec +++ b/libvirt.spec @@ -294,7 +294,7 @@ Summary: Library providing a simple virtualization API Name: libvirt Version: 11.10.0 -Release: 4%{?dist}%{?extra_release} +Release: 5%{?dist}%{?extra_release} License: GPL-2.0-or-later AND LGPL-2.1-only AND LGPL-2.1-or-later AND OFL-1.1 URL: https://libvirt.org/ @@ -327,6 +327,11 @@ Patch22: libvirt-esx-Abstract-all-URL-creation-code-into-one-function.patch Patch23: libvirt-esx-Switch-to-creating-URLs-using-virURIFormat.patch Patch24: libvirt-esx_util-Introduce-esxUtil_EscapeInventoryObject.patch Patch25: libvirt-esx-URI-encode-inventory-objects-twice.patch +Patch26: libvirt-qemublocktest-Iterate-all-nodenames-in-testQemuDetectBitmaps.patch +Patch27: libvirt-qemu-monitor-Detect-list-of-bitmaps-from-qcow2-format-specific-data.patch +Patch28: libvirt-qemuMigrationDstPrepareAnyBlockDirtyBitmaps-Fix-check-for-existing-bitmaps.patch +Patch29: libvirt-qemu-migration-Always-offer-block-dirty-bitmaps-during-migration.patch +Patch30: libvirt-qemuMigrationDstPrepareAnyBlockDirtyBitmaps-Always-consider-offered-bitmaps.patch Requires: libvirt-daemon = %{version}-%{release} @@ -2718,6 +2723,13 @@ exit 0 %endif %changelog +* Wed Feb 4 2026 Jiri Denemark - 11.10.0-5 +- qemublocktest: Iterate all nodenames in 'testQemuDetectBitmaps' (RHEL-145769) +- qemu: monitor: Detect list of bitmaps from 'qcow2' format specific data (RHEL-145769) +- qemuMigrationDstPrepareAnyBlockDirtyBitmaps: Fix check for existing bitmaps (RHEL-145769) +- qemu: migration: Always offer block dirty bitmaps during migration (RHEL-145769) +- qemuMigrationDstPrepareAnyBlockDirtyBitmaps: Always consider offered bitmaps (RHEL-145769) + * Thu Jan 29 2026 Jiri Denemark - 11.10.0-4 - qemuDomainSetThrottleGroup: Enforce non-zero 'groupname' string length (RHEL-141820) - qemuDomainSetBlockIoTuneField: Move setting of 'group_name' out of the loop (RHEL-141820)