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.fnst(a)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