]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/libvirt.git/commitdiff
qemu: Make save/restore with USB devices usable
authorJiri Denemark <jdenemar@redhat.com>
Tue, 9 Oct 2012 11:15:46 +0000 (13:15 +0200)
committerJiri Denemark <jdenemar@redhat.com>
Thu, 11 Oct 2012 13:11:42 +0000 (15:11 +0200)
Save/restore with passed through USB devices currently only works if the
USB device can be found at the same USB address where it used to be
before saving a domain. This makes sense in case a user explicitly
configure the USB address in domain XML. However, if the device was
found automatically by vendor/product identification, we should try to
search for that device when restoring the domain and use any device we
find as long as there is only one available. In other words, the USB
device can now be removed and plugged again or the host can be rebooted
between saving and restoring the domain.

src/conf/domain_conf.c
src/conf/domain_conf.h
src/qemu/qemu_driver.c
src/qemu/qemu_hostdev.c
src/qemu/qemu_migration.c

index 2b6ac66f00a16f97c7e1aa317c66c4dfe193475f..1eda5510fda0654f5315d2b0d73adb56ef823b90 100644 (file)
@@ -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);
index 6f4dc5c72c623322f1b1fd01b7cb756f476816d9..cc63da1d009549641f160607d94d2d50d6281a53 100644 (file)
@@ -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;
 
index 555e56d45d3236231e4c26f58612b8518b1ae0f7..078703939be4b18949616ffee985016e594f7d17 100644 (file)
@@ -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,
index 90dfd28f9f3a8435ea894c8f49696fcc4eb08c62..e24c022f4a6fe7afd14b46b3be6ae9260c505c7a 100644 (file)
@@ -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 <address> 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 <address> 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;
index 7cc1f9848ce976969044a9938e4bdda5516d819c..68d614d0e9e9204e2a3a75055888fbc2f6e6c487 100644 (file)
@@ -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,