add migration support for ephemeral host devices, introduce two 'detach' and 'restore' functions to unplug/plug host devices during migration.
Signed-off-by: Chen Fan <chen.fan.f...@cn.fujitsu.com> --- src/qemu/qemu_migration.c | 171 ++++++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_migration.h | 9 +++ src/qemu/qemu_process.c | 11 +++ 3 files changed, 187 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 56112f9..d5a698f 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -3384,6 +3384,158 @@ qemuMigrationPrepareDef(virQEMUDriverPtr driver, return def; } +int +qemuMigrationDetachEphemeralDevices(virQEMUDriverPtr driver, + virDomainObjPtr vm, + bool live) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr hostdev; + virDomainNetDefPtr net; + virDomainDeviceDef dev; + virDomainDeviceDefPtr dev_copy = NULL; + virCapsPtr caps = NULL; + int actualType; + int ret = -1; + size_t i; + + VIR_DEBUG("Rum domain detach ephemeral devices"); + + if (!(caps = virQEMUDriverGetCapabilities(driver, false))) + return ret; + + for (i = 0; i < vm->def->nnets;) { + net = vm->def->nets[i]; + + actualType = virDomainNetGetActualType(net); + if (actualType != VIR_DOMAIN_NET_TYPE_HOSTDEV) { + i++; + continue; + } + + hostdev = virDomainNetGetActualHostdev(net); + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI || + !hostdev->ephemeral) { + i++; + continue; + } + + dev.type = VIR_DOMAIN_DEVICE_NET; + dev.data.net = net; + + dev_copy = virDomainDeviceDefCopy(&dev, vm->def, + caps, driver->xmlopt); + if (!dev_copy) + goto cleanup; + + if (live) { + /* nnets reduced */ + if (qemuDomainDetachNetDevice(driver, vm, dev_copy) < 0) + goto cleanup; + } else { + virDomainNetDefFree(virDomainNetRemove(vm->def, i)); + } + if (VIR_APPEND_ELEMENT(priv->ephemeralDevices, + priv->nEphemeralDevices, + dev_copy) < 0) { + goto cleanup; + } + dev_copy = NULL; + } + + for (i = 0; i < vm->def->nhostdevs;) { + hostdev = vm->def->hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI || + !hostdev->ephemeral) { + i++; + continue; + } + + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = hostdev; + + VIR_FREE(dev_copy); + dev_copy = virDomainDeviceDefCopy(&dev, vm->def, + caps, driver->xmlopt); + if (!dev_copy) + goto cleanup; + + if (live) { + /* nhostdevs reduced */ + if (qemuDomainDetachHostDevice(driver, vm, dev_copy) < 0) + goto cleanup; + } else { + virDomainHostdevDefFree(virDomainHostdevRemove(vm->def, i)); + } + if (VIR_APPEND_ELEMENT(priv->ephemeralDevices, + priv->nEphemeralDevices, + dev_copy) < 0) { + goto cleanup; + } + dev_copy = NULL; + } + + ret = 0; + cleanup: + virDomainDeviceDefFree(dev_copy); + virObjectUnref(caps); + + return ret; +} + +void +qemuMigrationRestoreEphemeralDevices(virQEMUDriverPtr driver, + virConnectPtr conn, + virDomainObjPtr vm, + bool live) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainDeviceDefPtr dev; + int ret = -1; + size_t i; + + VIR_DEBUG("Rum domain restore ephemeral devices"); + + for (i = 0; i < priv->nEphemeralDevices; i++) { + dev = priv->ephemeralDevices[i]; + + switch ((virDomainDeviceType) dev->type) { + case VIR_DOMAIN_DEVICE_NET: + if (live) { + ret = qemuDomainAttachNetDevice(conn, driver, vm, + dev->data.net); + } else { + ret = virDomainNetInsert(vm->def, dev->data.net); + } + + if (!ret) + dev->data.net = NULL; + break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + if (live) { + ret = qemuDomainAttachHostDevice(conn, driver, vm, + dev->data.hostdev); + } else { + ret =virDomainHostdevInsert(vm->def, dev->data.hostdev); + } + if (!ret) + dev->data.hostdev = NULL; + break; + default: + ret = -1; + } + + if (ret == -1) + VIR_WARN("Unable to restore ephemeral device on domain %s ", + vm->def->name); + virDomainDeviceDefFree(dev); + } + VIR_FREE(priv->ephemeralDevices); + priv->nEphemeralDevices = 0; +} static int qemuMigrationConfirmPhase(virQEMUDriverPtr driver, @@ -3454,6 +3606,7 @@ qemuMigrationConfirmPhase(virQEMUDriverPtr driver, /* cancel any outstanding NBD jobs */ qemuMigrationCancelDriveMirror(mig, driver, vm); + qemuMigrationRestoreEphemeralDevices(driver, conn, vm, true); if (qemuMigrationRestoreDomainState(conn, vm)) { event = virDomainEventLifecycleNewFromObj(vm, @@ -4842,6 +4995,9 @@ qemuMigrationPerformJob(virQEMUDriverPtr driver, qemuMigrationStoreDomainState(vm); + if (qemuMigrationDetachEphemeralDevices(driver, vm, true) < 0) + goto endjob; + if ((flags & (VIR_MIGRATE_TUNNELLED | VIR_MIGRATE_PEER2PEER))) { ret = doPeer2PeerMigrate(driver, conn, vm, xmlin, dconnuri, uri, graphicsuri, listenAddress, @@ -4931,6 +5087,9 @@ qemuMigrationPerformPhase(virQEMUDriverPtr driver, virCloseCallbacksUnset(driver->closeCallbacks, vm, qemuMigrationCleanup); + if (qemuMigrationDetachEphemeralDevices(driver, vm, true) < 0) + goto endjob; + ret = doNativeMigrate(driver, vm, uri, cookiein, cookieinlen, cookieout, cookieoutlen, flags, resource, NULL, graphicsuri); @@ -5272,10 +5431,14 @@ qemuMigrationFinish(virQEMUDriverPtr driver, } } - if (virDomainObjIsActive(vm) && - virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) { - VIR_WARN("Failed to save status on vm %s", vm->def->name); - goto endjob; + if (virDomainObjIsActive(vm)) { + /* Check whether exist ephemeral devices, hotplug them. */ + qemuMigrationRestoreEphemeralDevices(driver, dconn, vm, true); + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) { + VIR_WARN("Failed to save status on vm %s", vm->def->name); + goto endjob; + } } /* Guest is successfully running, so cancel previous auto destroy */ diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index 1726455..e378b30 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -177,4 +177,13 @@ int qemuMigrationToFile(virQEMUDriverPtr driver, virDomainObjPtr vm, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5) ATTRIBUTE_RETURN_CHECK; +int +qemuMigrationDetachEphemeralDevices(virQEMUDriverPtr driver, + virDomainObjPtr vm, + bool live); +void +qemuMigrationRestoreEphemeralDevices(virQEMUDriverPtr driver, + virConnectPtr conn, + virDomainObjPtr vm, + bool live); #endif /* __QEMU_MIGRATION_H__ */ diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index d1f089d..904c447 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4496,6 +4496,15 @@ int qemuProcessStart(virConnectPtr conn, if (qemuNetworkPrepareDevices(vm->def) < 0) goto cleanup; + /* + * Ephemeral device would be hotplugged at a later stage + * during migration. hence we should remove the reserved + * PCI address for ephemeral device. + */ + if (vmop == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START) + if (qemuMigrationDetachEphemeralDevices(driver, vm, false) < 0) + goto cleanup; + /* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); if (!cfg->relaxedACS) @@ -5288,6 +5297,8 @@ void qemuProcessStop(virQEMUDriverPtr driver, priv->ccwaddrs = NULL; } + qemuMigrationRestoreEphemeralDevices(driver, NULL, vm, false); + qemuDomainReAttachHostDevices(driver, vm->def); def = vm->def; -- 1.9.3 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list