diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2b6ac66f00..1eda5510fd 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2702,6 +2702,7 @@ virDomainHostdevSubsysUsbDefParseXML(const xmlNodePtr node, int got_product, got_vendor; xmlNodePtr cur; char *startupPolicy = NULL; + char *autoAddress; if ((startupPolicy = virXMLPropString(node, "startupPolicy"))) { def->startupPolicy = @@ -2716,6 +2717,12 @@ virDomainHostdevSubsysUsbDefParseXML(const xmlNodePtr node, VIR_FREE(startupPolicy); } + if ((autoAddress = virXMLPropString(node, "autoAddress"))) { + if (STREQ(autoAddress, "yes")) + def->source.subsys.u.usb.autoAddress = true; + VIR_FREE(autoAddress); + } + /* Product can validly be 0, so we need some extra help to determine * if it is uninitialized*/ got_product = 0; @@ -12092,6 +12099,9 @@ virDomainHostdevSourceFormat(virBufferPtr buf, policy = virDomainStartupPolicyTypeToString(def->startupPolicy); virBufferAsprintf(buf, " startupPolicy='%s'", policy); } + if (def->source.subsys.u.usb.autoAddress && + (flags & VIR_DOMAIN_XML_MIGRATABLE)) + virBufferAddLit(buf, " autoAddress='yes'"); virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6f4dc5c72c..cc63da1d00 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -370,6 +370,8 @@ struct _virDomainHostdevSubsys { int type; /* enum virDomainHostdevSubsysType */ union { struct { + bool autoAddress; /* bus/device were filled automatically based + on vedor/product */ unsigned bus; unsigned device; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 555e56d45d..078703939b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -11184,7 +11184,7 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, } else { /* Easiest way to clone inactive portion of vm->def is via * conversion in and back out of xml. */ - if (!(xml = qemuDomainDefFormatLive(driver, vm->def, true, false)) || + if (!(xml = qemuDomainDefFormatLive(driver, vm->def, true, true)) || !(def->dom = virDomainDefParseString(driver->caps, xml, QEMU_EXPECTED_VIRT_TYPES, VIR_DOMAIN_XML_INACTIVE))) @@ -11735,7 +11735,8 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, if (!(xml = qemuDomainDefFormatXML(driver, snap->def->dom, VIR_DOMAIN_XML_INACTIVE | - VIR_DOMAIN_XML_SECURE))) + VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_MIGRATABLE))) goto cleanup; config = virDomainDefParseString(driver->caps, xml, QEMU_EXPECTED_VIRT_TYPES, diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 90dfd28f9f..e24c022f4a 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -650,15 +650,31 @@ qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, unsigned product = hostdev->source.subsys.u.usb.product; unsigned bus = hostdev->source.subsys.u.usb.bus; unsigned device = hostdev->source.subsys.u.usb.device; + bool autoAddress = hostdev->source.subsys.u.usb.autoAddress; int rc; *usb = NULL; if (vendor && bus) { - rc = usbFindDevice(vendor, product, bus, device, mandatory, usb); - if (rc < 0) + rc = usbFindDevice(vendor, product, bus, device, + autoAddress ? false : mandatory, + usb); + if (rc < 0) { return -1; - } else if (vendor && !bus) { + } else if (!autoAddress) { + goto out; + } else { + VIR_INFO("USB device %x:%x could not be found at previous" + " address (bus:%u device:%u)", + vendor, product, bus, device); + } + } + + /* When vendor is specified, its USB address is either unspecified or the + * device could not be found at the USB device where it had been + * automatically found before. + */ + if (vendor) { usbDeviceList *devs; rc = usbFindDeviceByVendor(vendor, product, mandatory, &devs); @@ -674,15 +690,32 @@ qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, if (rc == 0) { goto out; } else if (rc > 1) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("multiple USB devices for %x:%x, " - "use
to specify one"), - vendor, product); + if (autoAddress) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x were found," + " but none of them is at bus:%u device:%u"), + vendor, product, bus, device); + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x, " + "use
to specify one"), + vendor, product); + } return -1; } hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(*usb); hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(*usb); + hostdev->source.subsys.u.usb.autoAddress = true; + + if (autoAddress) { + VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved" + " from bus:%u device:%u)", + vendor, product, + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + bus, device); + } } else if (!vendor && bus) { if (usbFindDeviceByBus(bus, device, mandatory, usb) < 0) return -1; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 7cc1f9848c..68d614d0e9 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1264,7 +1264,8 @@ qemuMigrationPrepareAny(struct qemud_driver *driver, int hookret; if (!(xml = qemuDomainDefFormatXML(driver, def, - VIR_DOMAIN_XML_SECURE))) + VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_MIGRATABLE))) goto cleanup; hookret = virHookCall(VIR_HOOK_DRIVER_QEMU, def->name,