[libvirt] [PATCH 0/5] Support Ephemeral passthrough hostdevs

The ephemeral flag helps support migration with PCI-passthrough. An ephemeral hostdev is automatically unplugged before migration and replugged (if one is available on the destination) after migration. Shradha Shah (5): Added ephemeral flag for hostdev in domain conf. Adding ephemeral flag for hostdev in network conf. Ephemeral flag mofication within the network driver. Ephemeral flag modification within the qemu driver. Migration support for ephemeral hostdevs. docs/schemas/domaincommon.rng | 16 ++++ docs/schemas/network.rng | 8 ++ src/conf/domain_conf.c | 23 +++++- src/conf/domain_conf.h | 1 + src/conf/network_conf.c | 11 +++ src/conf/network_conf.h | 1 + src/network/bridge_driver.c | 1 + src/qemu/qemu_command.c | 63 +++++++++----- src/qemu/qemu_migration.c | 94 +++++++++++++++++++- tests/networkxml2xmlin/hostdev-pf.xml | 2 +- tests/networkxml2xmlin/hostdev.xml | 2 +- tests/networkxml2xmlout/hostdev-pf.xml | 2 +- tests/networkxml2xmlout/hostdev.xml | 2 +- .../qemuxml2argv-hostdev-pci-address.xml | 2 +- .../qemuxml2argv-hostdev-usb-address.xml | 2 +- .../qemuxml2argvdata/qemuxml2argv-net-hostdev.xml | 2 +- tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml | 4 +- 17 files changed, 200 insertions(+), 36 deletions(-) -- 1.7.4.4

The ephemeral flag helps support migration with PCI-passthrough. An ephemeral hostdev is automatically unplugged before migration and replugged (if one is available on the destination) after migration. --- docs/schemas/domaincommon.rng | 16 +++++++++++++ src/conf/domain_conf.c | 23 ++++++++++++++++++- src/conf/domain_conf.h | 1 + src/qemu/qemu_command.c | 1 + .../qemuxml2argv-hostdev-pci-address.xml | 2 +- .../qemuxml2argv-hostdev-usb-address.xml | 2 +- .../qemuxml2argvdata/qemuxml2argv-net-hostdev.xml | 2 +- tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml | 4 +- 8 files changed, 44 insertions(+), 7 deletions(-) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 0e85739..29fc382 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -1694,6 +1694,14 @@ </choice> </attribute> </optional> + <optional> + <attribute name="ephemeral"> + <choice> + <value>yes</value> + <value>no</value> + </choice> + </attribute> + </optional> <interleave> <element name="source"> <optional> @@ -2836,6 +2844,14 @@ </choice> </attribute> </optional> + <optional> + <attribute name="ephemeral"> + <choice> + <value>yes</value> + <value>no</value> + </choice> + </attribute> + </optional> <group> <element name="source"> <optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2ca608f..d0142f7 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2958,6 +2958,7 @@ virDomainHostdevPartsParse(xmlNodePtr node, { xmlNodePtr sourcenode; char *managed = NULL; + char *ephemeral = NULL; int ret = -1; /* @mode is passed in separately from the caller, since an @@ -2984,6 +2985,16 @@ virDomainHostdevPartsParse(xmlNodePtr node, def->managed = 1; } + /* @ephemeral can be read from the xml document - it is always an + * attribute of the toplevel element, no matter what type of + * element that might be (pure hostdev, or higher level device + * (e.g. <interface>) with type='hostdev') + */ + if ((ephemeral = virXMLPropString(node, "ephemeral"))!= NULL) { + if (STREQ(ephemeral,"yes")) + def->ephemeral = 1; + } + /* @type is passed in from the caller rather than read from the * xml document, because it is specified in different places for * different kinds of defs - it is an attribute of @@ -12428,6 +12439,10 @@ virDomainActualNetDefFormat(virBufferPtr buf, def->data.hostdev.def.managed) { virBufferAddLit(buf, " managed='yes'"); } + if (def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV && + def->data.hostdev.def.ephemeral) { + virBufferAddLit(buf, " ephemeral='yes'"); + } virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); @@ -12498,6 +12513,10 @@ virDomainNetDefFormat(virBufferPtr buf, def->data.hostdev.def.managed) { virBufferAddLit(buf, " managed='yes'"); } + if (def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV && + def->data.hostdev.def.ephemeral) { + virBufferAddLit(buf, " ephemeral='yes'"); + } virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 6); @@ -13473,8 +13492,8 @@ virDomainHostdevDefFormat(virBufferPtr buf, return -1; } - virBufferAsprintf(buf, " <hostdev mode='%s' type='%s' managed='%s'>\n", - mode, type, def->managed ? "yes" : "no"); + virBufferAsprintf(buf, " <hostdev mode='%s' type='%s' managed='%s' ephemeral='%s'>\n", + mode, type, def->managed ? "yes" : "no", def->ephemeral ? "yes" : "no"); virBufferAdjustIndent(buf, 6); if (virDomainHostdevSourceFormat(buf, def, flags, false) < 0) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 4ab15e9..597480f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -390,6 +390,7 @@ struct _virDomainHostdevDef { int startupPolicy; /* enum virDomainStartupPolicy */ unsigned int managed : 1; unsigned int missing : 1; + unsigned int ephemeral : 1; union { virDomainHostdevSubsys subsys; struct { diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 7736575..1b20d23 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -7729,6 +7729,7 @@ qemuParseCommandLinePCI(const char *val) def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; def->managed = 1; + def->ephemeral = 1; def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; def->source.subsys.u.pci.bus = bus; def->source.subsys.u.pci.slot = slot; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml index 3c69f83..fc7ea5c 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml @@ -21,7 +21,7 @@ </disk> <controller type='usb' index='0'/> <controller type='ide' index='0'/> - <hostdev mode='subsystem' type='pci' managed='yes'> + <hostdev mode='subsystem' type='pci' managed='yes' ephemeral='yes'> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address.xml index 811e987..d07dbe3 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address.xml @@ -21,7 +21,7 @@ </disk> <controller type='usb' index='0'/> <controller type='ide' index='0'/> - <hostdev mode='subsystem' type='usb' managed='no'> + <hostdev mode='subsystem' type='usb' managed='no' ephemeral='no'> <source> <address bus='14' device='6'/> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml index 81f70d0..54aa5e6 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml @@ -21,7 +21,7 @@ </disk> <controller type='usb' index='0'/> <controller type='ide' index='0'/> - <interface type='hostdev' managed='yes'> + <interface type='hostdev' managed='yes' ephemeral='yes'> <mac address='00:11:22:33:44:55'/> <source> <address type='pci' domain='0x0002' bus='0x03' slot='0x07' function='0x1'/> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml b/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml index 371835d..cce0d7f 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml @@ -31,13 +31,13 @@ <model type='virtio'/> <rom file='/etc/fake/bootrom.bin'/> </interface> - <hostdev mode='subsystem' type='pci' managed='yes'> + <hostdev mode='subsystem' type='pci' managed='yes' ephemeral='no'> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/> </source> <rom bar='off'/> </hostdev> - <hostdev mode='subsystem' type='pci' managed='yes'> + <hostdev mode='subsystem' type='pci' managed='yes' ephemeral='no'> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x6'/> </source> -- 1.7.4.4

The ephemeral flag helps support migration with PCI-passthrough. An ephemeral hostdev is automatically unplugged before migration and replugged (if one is available on the destination) after migration. --- docs/schemas/network.rng | 8 ++++++++ src/conf/network_conf.c | 11 +++++++++++ src/conf/network_conf.h | 1 + tests/networkxml2xmlin/hostdev-pf.xml | 2 +- tests/networkxml2xmlin/hostdev.xml | 2 +- tests/networkxml2xmlout/hostdev-pf.xml | 2 +- tests/networkxml2xmlout/hostdev.xml | 2 +- 7 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index 4abfd91..c86ade8 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -100,6 +100,14 @@ </choice> </attribute> </optional> + <optional> + <attribute name="ephemeral"> + <choice> + <value>yes</value> + <value>no</value> + </choice> + </attribute> + </optional> <interleave> <choice> <group> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 228951d..4f48f64 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1222,6 +1222,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) int nIps, nPortGroups, nForwardIfs, nForwardPfs, nForwardAddrs; char *forwardDev = NULL; char *forwardManaged = NULL; + char *forwardEphemeral = NULL; char *type = NULL; xmlNodePtr save = ctxt->node; xmlNodePtr bandwidthNode = NULL; @@ -1380,6 +1381,11 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) if (STRCASEEQ(forwardManaged, "yes")) def->managed = 1; } + forwardEphemeral = virXPathString("string(./@ephemeral)", ctxt); + if (forwardEphemeral != NULL) { + if (STRCASEEQ(forwardEphemeral, "yes")) + def->ephemeral = 1; + } /* all of these modes can use a pool of physical interfaces */ nForwardIfs = virXPathNodeSet("./interface", ctxt, &forwardIfNodes); @@ -1527,6 +1533,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) VIR_FREE(type); VIR_FREE(forwardDev); VIR_FREE(forwardManaged); + VIR_FREE(forwardEphemeral); VIR_FREE(forwardPfNodes); VIR_FREE(forwardIfNodes); VIR_FREE(forwardAddrNodes); @@ -1861,6 +1868,10 @@ char *virNetworkDefFormat(const virNetworkDefPtr def, unsigned int flags) virBufferAddLit(&buf, " managed='yes'"); else virBufferAddLit(&buf, " managed='no'"); + if (def->ephemeral == 1) + virBufferAddLit(&buf, " ephemeral='yes'"); + else + virBufferAddLit(&buf, " ephemeral='no'"); } virBufferAsprintf(&buf, "%s>\n", (def->nForwardIfs || def->nForwardPfs) ? "" : "/"); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 3e46304..37a5d42 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -186,6 +186,7 @@ struct _virNetworkDef { int forwardType; /* One of virNetworkForwardType constants */ int managed; /* managed attribute for hostdev mode */ + int ephemeral; /* ephemeral attribute for hostdev mode */ /* If there are multiple forward devices (i.e. a pool of * interfaces), they will be listed here. diff --git a/tests/networkxml2xmlin/hostdev-pf.xml b/tests/networkxml2xmlin/hostdev-pf.xml index 7bf857d..6b928a6 100644 --- a/tests/networkxml2xmlin/hostdev-pf.xml +++ b/tests/networkxml2xmlin/hostdev-pf.xml @@ -1,7 +1,7 @@ <network> <name>hostdev</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> - <forward mode='hostdev' managed='yes'> + <forward mode='hostdev' managed='yes' ephemeral='yes'> <pf dev='eth2'/> </forward> </network> diff --git a/tests/networkxml2xmlin/hostdev.xml b/tests/networkxml2xmlin/hostdev.xml index 03f1411..406c2df 100644 --- a/tests/networkxml2xmlin/hostdev.xml +++ b/tests/networkxml2xmlin/hostdev.xml @@ -1,7 +1,7 @@ <network> <name>hostdev</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> - <forward mode='hostdev' managed='yes'> + <forward mode='hostdev' managed='yes' ephemeral='yes'> <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x1'/> <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x2'/> <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x3'/> diff --git a/tests/networkxml2xmlout/hostdev-pf.xml b/tests/networkxml2xmlout/hostdev-pf.xml index 7bf857d..6b928a6 100644 --- a/tests/networkxml2xmlout/hostdev-pf.xml +++ b/tests/networkxml2xmlout/hostdev-pf.xml @@ -1,7 +1,7 @@ <network> <name>hostdev</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> - <forward mode='hostdev' managed='yes'> + <forward mode='hostdev' managed='yes' ephemeral='yes'> <pf dev='eth2'/> </forward> </network> diff --git a/tests/networkxml2xmlout/hostdev.xml b/tests/networkxml2xmlout/hostdev.xml index 03f1411..406c2df 100644 --- a/tests/networkxml2xmlout/hostdev.xml +++ b/tests/networkxml2xmlout/hostdev.xml @@ -1,7 +1,7 @@ <network> <name>hostdev</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> - <forward mode='hostdev' managed='yes'> + <forward mode='hostdev' managed='yes' ephemeral='yes'> <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x1'/> <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x2'/> <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x3'/> -- 1.7.4.4

--- src/network/bridge_driver.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index fb167dc..a72f3b4 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -3553,6 +3553,7 @@ networkAllocateActualDevice(virDomainNetDefPtr iface) iface->data.network.actual->data.hostdev.def.info = &iface->info; iface->data.network.actual->data.hostdev.def.mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; iface->data.network.actual->data.hostdev.def.managed = netdef->managed; + iface->data.network.actual->data.hostdev.def.ephemeral = netdef->ephemeral; iface->data.network.actual->data.hostdev.def.source.subsys.type = dev->type; iface->data.network.actual->data.hostdev.def.source.subsys.u.pci = dev->device.pci; -- 1.7.4.4

When a guest with ephemeral device is migrated the PCI- passthrough of the ephemeral device should take place after migration and hence we check for the vmop in qemuBuildCommandLine. We also dicard the PCI slot assigned by qemuCollectPCIAddress as a PCI address will be assigned later during the hotplug. --- src/qemu/qemu_command.c | 62 ++++++++++++++++++++++++++++++---------------- 1 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 1b20d23..6e1851c 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4954,6 +4954,9 @@ qemuBuildCommandLine(virConnectPtr conn, VIR_DOMAIN_CONTROLLER_TYPE_CCID, }; + virDomainObjPtr vm = NULL; + virDomainObjListPtr doms = &driver->domains; + VIR_DEBUG("conn=%p driver=%p def=%p mon=%p json=%d " "caps=%p migrateFrom=%s migrateFD=%d " "snapshot=%p vmop=%d", @@ -4962,6 +4965,8 @@ qemuBuildCommandLine(virConnectPtr conn, virUUIDFormat(def->uuid, uuid); + vm = virHashLookup(doms->objs, uuid); + emulator = def->emulator; /* @@ -5931,36 +5936,49 @@ qemuBuildCommandLine(virConnectPtr conn, if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { virDomainHostdevDefPtr hostdev = virDomainNetGetActualHostdev(net); virDomainHostdevDefPtr found; + qemuDomainObjPrivatePtr priv = vm->privateData; /* For a network with <forward mode='hostdev'>, there is a need to * add the newly minted hostdev to the hostdevs array. */ - if (qemuAssignDeviceHostdevAlias(def, hostdev, - (def->nhostdevs-1)) < 0) { - goto error; - } - - if (virDomainHostdevFind(def, hostdev, &found) < 0) { - if (virDomainHostdevInsert(def, hostdev) < 0) { - virReportOOMError(); + if (vmop == VIR_NETDEV_VPORT_PROFILE_OP_CREATE) { + if (qemuAssignDeviceHostdevAlias(def, hostdev, + (def->nhostdevs-1)) < 0) { goto error; } - if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, - &hostdev, 1) < 0) { + + if (virDomainHostdevFind(def, hostdev, &found) < 0) { + if (virDomainHostdevInsert(def, hostdev) < 0) { + virReportOOMError(); + goto error; + } + if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, + &hostdev, 1) < 0) { + goto error; + } + } + else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("PCI device %04x:%02x:%02x.%x " + "allocated from network %s is already " + "in use by domain %s"), + hostdev->source.subsys.u.pci.domain, + hostdev->source.subsys.u.pci.bus, + hostdev->source.subsys.u.pci.slot, + hostdev->source.subsys.u.pci.function, + net->data.network.name, + def->name); goto error; } } - else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("PCI device %04x:%02x:%02x.%x " - "allocated from network %s is already " - "in use by domain %s"), - hostdev->source.subsys.u.pci.domain, - hostdev->source.subsys.u.pci.bus, - hostdev->source.subsys.u.pci.slot, - hostdev->source.subsys.u.pci.function, - net->data.network.name, - def->name); - goto error; + else if (vmop == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START) { + /* During migration the hostdev device is hotplugged at + * a later stage hence remove the PCI address collected by + * qemuCollectPCIAddress */ + if (qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, + hostdev->info->addr.pci.slot) < 0) + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not release PCI slot during migration " + "for hotplug at a later stage")); } } continue; -- 1.7.4.4

--- src/qemu/qemu_migration.c | 94 +++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 91 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index d52ec59..dd1a2a7 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -32,6 +32,7 @@ #include "qemu_monitor.h" #include "qemu_domain.h" #include "qemu_process.h" +#include "qemu_hotplug.h" #include "qemu_capabilities.h" #include "qemu_cgroup.h" @@ -50,6 +51,7 @@ #include "storage_file.h" #include "viruri.h" #include "hooks.h" +#include "network/bridge_driver.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -149,6 +151,79 @@ struct _qemuMigrationCookie { qemuMigrationCookieNetworkPtr network; }; +static void +qemuMigrationRemoveEphemeralDevices(struct qemud_driver *driver, + virDomainObjPtr vm) +{ + virDomainHostdevDefPtr dev; + virDomainDeviceDef def; + unsigned int i; + + for (i = 0; i < vm->def->nhostdevs; i++) { + dev = vm->def->hostdevs[i]; + if (dev->ephemeral == 1) { + def.type = VIR_DOMAIN_DEVICE_HOSTDEV; + def.data.hostdev = dev; + + if (qemuDomainDetachHostDevice(driver, vm, &def) >= 0) { + continue; /* nhostdevs reduced */ + } + } + } +} + +static void +qemuMigrationRestoreEphemeralDevices(struct qemud_driver *driver, + virDomainObjPtr vm) +{ + virDomainNetDefPtr net; + unsigned int i; + + /* Do nothing if ephemeral devices are present in which case this + function was called before qemuMigrationRemoveEphemeralDevices */ + + for (i = 0; i < vm->def->nhostdevs; i++) { + if (vm->def->hostdevs[i]->ephemeral == 1) + return; + } + + for (i = 0; i < vm->def->nnets; i++) { + net = vm->def->nets[i]; + + if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + if (qemuDomainAttachHostDevice(driver, vm, + virDomainNetGetActualHostdev(net)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Hostdev cannot be restored")); + networkReleaseActualDevice(net); + } + } + return; + } +} + +static void +qemuMigrationAttachEphemeralDevices(struct qemud_driver *driver, + virDomainObjPtr vm) +{ + virDomainNetDefPtr net; + unsigned int i; + + for (i = 0; i < vm->def->nnets; i++) { + net = vm->def->nets[i]; + + if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + if (qemuDomainAttachHostDevice(driver, vm, + virDomainNetGetActualHostdev(net)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Hostdev cannot be attached after migration")); + networkReleaseActualDevice(net); + } + } + } + return; +} + static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap) { if (!grap) @@ -1041,21 +1116,22 @@ qemuMigrationIsAllowed(struct qemud_driver *driver, virDomainObjPtr vm, def = vm->def; } - /* Migration with USB host devices is allowed, all other devices are + /* Migration with USB and ephemeral PCI host devices is allowed, all other devices are * forbidden. */ forbid = false; for (i = 0; i < def->nhostdevs; i++) { virDomainHostdevDefPtr hostdev = def->hostdevs[i]; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || - hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { + ((hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) && + (hostdev->ephemeral == 0))) { forbid = true; break; } } if (forbid) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("Domain with assigned non-USB host devices " + _("Domain with assigned non-USB and non-ephemeral host devices " "cannot be migrated")); return false; } @@ -2347,6 +2423,7 @@ static int doNativeMigrate(struct qemud_driver *driver, "cookieout=%p, cookieoutlen=%p, flags=%lx, resource=%lu", driver, vm, uri, NULLSTR(cookiein), cookieinlen, cookieout, cookieoutlen, flags, resource); + qemuMigrationRemoveEphemeralDevices(driver, vm); if (STRPREFIX(uri, "tcp:") && !STRPREFIX(uri, "tcp://")) { char *tmp; @@ -2374,6 +2451,9 @@ static int doNativeMigrate(struct qemud_driver *driver, ret = qemuMigrationRun(driver, vm, cookiein, cookieinlen, cookieout, cookieoutlen, flags, resource, &spec, dconn); + if (ret != 0) + qemuMigrationRestoreEphemeralDevices(driver, vm); + if (spec.destType == MIGRATION_DEST_FD) VIR_FORCE_CLOSE(spec.dest.fd.qemu); @@ -2412,6 +2492,8 @@ static int doTunnelMigrate(struct qemud_driver *driver, return -1; } + qemuMigrationRemoveEphemeralDevices(driver, vm); + spec.fwdType = MIGRATION_FWD_STREAM; spec.fwd.stream = st; @@ -2458,6 +2540,8 @@ static int doTunnelMigrate(struct qemud_driver *driver, cookieoutlen, flags, resource, &spec, dconn); cleanup: + if (ret != 0) + qemuMigrationRestoreEphemeralDevices(driver, vm); if (spec.destType == MIGRATION_DEST_FD) { VIR_FORCE_CLOSE(spec.dest.fd.qemu); VIR_FORCE_CLOSE(spec.dest.fd.local); @@ -3366,6 +3450,8 @@ qemuMigrationFinish(struct qemud_driver *driver, goto endjob; } + qemuMigrationAttachEphemeralDevices(driver, vm); + /* Guest is successfully running, so cancel previous auto destroy */ qemuProcessAutoDestroyRemove(driver, vm); } else { @@ -3463,6 +3549,8 @@ int qemuMigrationConfirm(struct qemud_driver *driver, VIR_WARN("Failed to save status on vm %s", vm->def->name); goto cleanup; } + + qemuMigrationRestoreEphemeralDevices(driver, vm); } qemuMigrationCookieFree(mig); -- 1.7.4.4
participants (1)
-
Shradha Shah