]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu: migration: Automatically fix non-shared-storage migration to bigger block devices
authorPeter Krempa <pkrempa@redhat.com>
Wed, 6 Dec 2023 14:57:16 +0000 (15:57 +0100)
committerPeter Krempa <pkrempa@redhat.com>
Wed, 13 Dec 2023 19:15:50 +0000 (20:15 +0100)
QEMU's blockdev-mirror job doesn't allow copy into a destination which
isn't exactly the same size as source. This is a problem for
non-shared-storage migration when migrating into a raw block device, as
there it's very hard to ensure that the destination size will match the
source size.

Rather than failing the migration, we can add a storage slice in such
case automatically and thus make the migration pass.

To do this we need to probe the size of the block device on the
destination and if it differs form the size detected on the source we'll
install the 'slice'.

An additional handling is required when persisting the VM as we want to
propagate the slice even there to ensure that the device sizes won't
change.

Resolves: https://issues.redhat.com/browse/RHEL-4607
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
src/qemu/qemu_domain.h
src/qemu/qemu_migration.c

index b8331a32d34c3947b10f1e93d74edacb072c602c..fa566dded657c3c3b58adbc70dfd76ae77b5f27c 100644 (file)
@@ -275,6 +275,7 @@ struct _qemuDomainDiskPrivate {
 
     bool migrating; /* the disk is being migrated */
     virStorageSource *migrSource; /* disk source object used for NBD migration */
+    bool migrationslice; /* storage slice was added for migration purposes */
 
     /* information about the device */
     bool tray; /* device has tray */
index 40caf1f7dca763953959fcd4f8e4d0d6573ff750..3ba0aa502b11e48c92f50a0c0b662e4b69b2cf50 100644 (file)
@@ -473,6 +473,25 @@ qemuMigrationDstPrepareStorage(virDomainObj *vm,
         }
 
         if (diskSrcPath) {
+            /* In case the destination of the storage migration is a block
+             * device it might be possible that for various reasons the size
+             * will not be identical. Since qemu refuses to do a blockdev-mirror
+             * into an image which doesn't have the exact same size we need to
+             * install a slice on top of the top image */
+            if (virStorageSourceIsBlockLocal(disk->src) &&
+                disk->src->format == VIR_STORAGE_FILE_RAW &&
+                disk->src->sliceStorage == NULL) {
+                qemuDomainObjPrivate *priv = vm->privateData;
+                g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(priv->driver);
+                qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
+
+                if (qemuDomainStorageUpdatePhysical(cfg, vm, disk->src) == 0 &&
+                    disk->src->physical > nbd->disks[i].capacity) {
+                    disk->src->sliceStorage = g_new0(virStorageSourceSlice, 1);
+                    disk->src->sliceStorage->size = nbd->disks[i].capacity;
+                    diskPriv->migrationslice = true;
+                }
+            }
 
             /* don't pre-create existing disks */
             if (virFileExists(diskSrcPath)) {
@@ -6378,6 +6397,37 @@ qemuMigrationDstPersist(virQEMUDriver *driver,
                                                priv->qemuCaps)))
         goto error;
 
+    if (vm->def) {
+        size_t i;
+
+        for (i = 0; i < vm->def->ndisks; i++) {
+            virDomainDiskDef *disk = vm->def->disks[i];
+            qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
+            virDomainDiskDef *persistDisk;
+
+            /* transfer over any slices added for migration */
+            if (!disk->src->sliceStorage)
+                continue;
+
+            if (!diskPriv->migrationslice)
+                continue;
+
+            diskPriv->migrationslice = false;
+
+            if (!(persistDisk = virDomainDiskByTarget(vm->newDef, disk->dst)))
+                continue;
+
+            if (persistDisk->src->sliceStorage)
+                continue;
+
+            if (!virStorageSourceIsSameLocation(disk->src, persistDisk->src))
+                continue;
+
+            persistDisk->src->sliceStorage = g_new0(virStorageSourceSlice, 1);
+            persistDisk->src->sliceStorage->size = disk->src->sliceStorage->size;
+        }
+    }
+
     if (!oldPersist) {
         eventDetail = VIR_DOMAIN_EVENT_DEFINED_ADDED;