]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu: call usb search function for hostdev initialization and hotplug
authorGuannan Ren <gren@redhat.com>
Sun, 6 May 2012 14:45:05 +0000 (22:45 +0800)
committerGuannan Ren <gren@redhat.com>
Mon, 7 May 2012 15:36:25 +0000 (23:36 +0800)
src/qemu/qemu_hostdev.c:
refactor qemuPrepareHostdevUSBDevices function, make it focus on
adding usb device to activeUsbHostdevs after check. After that,
the usb hotplug function qemuDomainAttachHostDevice also could use
it.
expand qemuPrepareHostUSBDevices to perform the usb search,
rollback on failure.

src/qemu/qemu_hotplug.c:
If there are multiple usb devices available with same vendorID and productID,
but with different value of "bus, device", we give an error to let user
use <address> to specify the desired one.

src/qemu/qemu_hostdev.c
src/qemu/qemu_hostdev.h
src/qemu/qemu_hotplug.c

index b98fd8f5df8ae4b87fd7ef75b6e26b9caa134d71..b649ae0872359223ce9d306a398ae9c64ce93497 100644 (file)
@@ -565,13 +565,53 @@ qemuPrepareHostPCIDevices(struct qemud_driver *driver,
 int
 qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
                              const char *name,
-                             virDomainHostdevDefPtr *hostdevs,
-                             int nhostdevs)
+                             usbDeviceList *list)
 {
-    int ret = -1;
     int i;
+    unsigned int count;
+    usbDevice *tmp;
+
+    count = usbDeviceListCount(list);
+
+    for (i = 0; i < count; i++) {
+        usbDevice *usb = usbDeviceListGet(list, i);
+        if ((tmp = usbDeviceListFind(driver->activeUsbHostdevs, usb))) {
+            const char *other_name = usbDeviceGetUsedBy(tmp);
+
+            if (other_name)
+                qemuReportError(VIR_ERR_OPERATION_INVALID,
+                                _("USB device %s is in use by domain %s"),
+                                usbDeviceGetName(tmp), other_name);
+            else
+                qemuReportError(VIR_ERR_OPERATION_INVALID,
+                                _("USB device %s is already in use"),
+                                usbDeviceGetName(tmp));
+            return -1;
+        }
+
+        usbDeviceSetUsedBy(usb, name);
+        VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs",
+                  usbDeviceGetBus(usb), usbDeviceGetDevno(usb), name);
+        /*
+         * The caller is responsible to steal these usb devices
+         * from the usbDeviceList that passed in on success,
+         * perform rollback on failure.
+         */
+        if (usbDeviceListAdd(driver->activeUsbHostdevs, usb) < 0)
+            return -1;
+    }
+    return 0;
+}
+
+static int
+qemuPrepareHostUSBDevices(struct qemud_driver *driver,
+                          virDomainDefPtr def)
+{
+    int i, ret = -1;
     usbDeviceList *list;
     usbDevice *tmp;
+    virDomainHostdevDefPtr *hostdevs = def->hostdevs;
+    int nhostdevs = def->nhostdevs;
 
     /* To prevent situation where USB device is assigned to two domains
      * we need to keep a list of currently assigned USB devices.
@@ -586,70 +626,61 @@ qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
      */
     for (i = 0 ; i < nhostdevs ; i++) {
         virDomainHostdevDefPtr hostdev = hostdevs[i];
+        usbDevice *usb = NULL;
 
         if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
             continue;
         if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
             continue;
 
-        /* Resolve a vendor/product to bus/device */
-        if (hostdev->source.subsys.u.usb.vendor) {
-            usbDevice *usb;
-            usbDeviceList *devs;
+        unsigned vendor = hostdev->source.subsys.u.usb.vendor;
+        unsigned product = hostdev->source.subsys.u.usb.product;
+        unsigned bus = hostdev->source.subsys.u.usb.bus;
+        unsigned device = hostdev->source.subsys.u.usb.device;
 
-            devs = usbFindDeviceByVendor(hostdev->source.subsys.u.usb.vendor,
-                                         hostdev->source.subsys.u.usb.product);
+        if (vendor && bus) {
+            usb = usbFindDevice(vendor, product, bus, device);
 
+        } else if (vendor && !bus) {
+            usbDeviceList *devs = usbFindDeviceByVendor(vendor, product);
             if (!devs)
                 goto cleanup;
 
+            if (usbDeviceListCount(devs) > 1) {
+                qemuReportError(VIR_ERR_OPERATION_FAILED,
+                                _("multiple USB devices for %x:%x, "
+                                  "use <address> to specify one"), vendor, product);
+                usbDeviceListFree(devs);
+                goto cleanup;
+            }
             usb = usbDeviceListGet(devs, 0);
             usbDeviceListSteal(devs, usb);
             usbDeviceListFree(devs);
 
-            if ((tmp = usbDeviceListFind(driver->activeUsbHostdevs, usb))) {
-                const char *other_name = usbDeviceGetUsedBy(tmp);
-
-                if (other_name)
-                    qemuReportError(VIR_ERR_OPERATION_INVALID,
-                                    _("USB device %s is in use by domain %s"),
-                                    usbDeviceGetName(tmp), other_name);
-                else
-                    qemuReportError(VIR_ERR_OPERATION_INVALID,
-                                    _("USB device %s is already in use"),
-                                    usbDeviceGetName(tmp));
-                usbFreeDevice(usb);
-                goto cleanup;
-            }
-
             hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
             hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
 
-            if (usbDeviceListAdd(list, usb) < 0) {
-                usbFreeDevice(usb);
-                goto cleanup;
-            }
+        } else if (!vendor && bus) {
+            usb = usbFindDeviceByBus(bus, device);
+        }
+
+        if (!usb)
+            goto cleanup;
 
+        if (usbDeviceListAdd(list, usb) < 0) {
+            usbFreeDevice(usb);
+            goto cleanup;
         }
     }
 
-    /* Loop 2: Mark devices in temporary list as used by @name
+    /* Mark devices in temporary list as used by @name
      * and add them do driver list. However, if something goes
      * wrong, perform rollback.
      */
-    for (i = 0; i < usbDeviceListCount(list); i++) {
-        tmp = usbDeviceListGet(list, i);
-        usbDeviceSetUsedBy(tmp, name);
-
-        VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs",
-                  usbDeviceGetBus(tmp), usbDeviceGetDevno(tmp), name);
-        if (usbDeviceListAdd(driver->activeUsbHostdevs, tmp) < 0) {
-            usbFreeDevice(tmp);
-            goto inactivedevs;
-        }
-    }
+    if (qemuPrepareHostdevUSBDevices(driver, def->name, list) < 0)
+        goto inactivedevs;
 
-    /* Loop 3: Temporary list was successfully merged with
+    /* Loop 2: Temporary list was successfully merged with
      * driver list, so steal all items to avoid freeing them
      * in cleanup label.
      */
@@ -675,13 +706,6 @@ cleanup:
     return ret;
 }
 
-static int
-qemuPrepareHostUSBDevices(struct qemud_driver *driver,
-                          virDomainDefPtr def)
-{
-    return qemuPrepareHostdevUSBDevices(driver, def->name, def->hostdevs, def->nhostdevs);
-}
-
 int qemuPrepareHostDevices(struct qemud_driver *driver,
                            virDomainDefPtr def)
 {
index 371630ab365d1f841c1c06e03f1d1260dc94d81b..a8acccf45b5e5b3db595b43e6ef6e68ebba2eb32 100644 (file)
@@ -38,8 +38,7 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
                                  int nhostdevs);
 int qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
                                  const char *name,
-                                 virDomainHostdevDefPtr *hostdevs,
-                                 int nhostdevs);
+                                 usbDeviceList *list);
 int qemuPrepareHostDevices(struct qemud_driver *driver,
                            virDomainDefPtr def);
 void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver);
index 0f5fed1bcac7b8d7441dd7ab7eac397c11cfca40..ad31eba63093dd63754729be54a064050786b5f4 100644 (file)
@@ -1116,11 +1116,13 @@ error:
     return -1;
 }
 
-
 int qemuDomainAttachHostDevice(struct qemud_driver *driver,
                                virDomainObjPtr vm,
                                virDomainHostdevDefPtr hostdev)
 {
+    usbDeviceList *list;
+    usbDevice *usb = NULL;
+
     if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
         qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                         _("hostdev mode '%s' not supported"),
@@ -1128,35 +1130,58 @@ int qemuDomainAttachHostDevice(struct qemud_driver *driver,
         return -1;
     }
 
-    /* Resolve USB product/vendor to bus/device */
-    if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
-        hostdev->source.subsys.u.usb.vendor) {
-        usbDevice *usb;
-        usbDeviceList *list;
+    if (!(list = usbDeviceListNew()))
+        goto cleanup;
 
-        if (qemuPrepareHostdevUSBDevices(driver, vm->def->name, &hostdev, 1) < 0)
-            goto error;
+    if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
+        unsigned vendor = hostdev->source.subsys.u.usb.vendor;
+        unsigned product = hostdev->source.subsys.u.usb.product;
+        unsigned bus = hostdev->source.subsys.u.usb.bus;
+        unsigned device = hostdev->source.subsys.u.usb.device;
 
-        list = usbFindDeviceByVendor(hostdev->source.subsys.u.usb.vendor,
-                                     hostdev->source.subsys.u.usb.product);
+        if (vendor && bus) {
+            usb = usbFindDevice(vendor, product, bus, device);
 
-        if (!list)
-            return -1;
+        } else if (vendor && !bus) {
+            usbDeviceList *devs = usbFindDeviceByVendor(vendor, product);
+            if (!devs)
+                goto cleanup;
 
-        usb = usbDeviceListGet(list, 0);
-        usbDeviceListSteal(list, usb);
-        usbDeviceListFree(list);
+            if (usbDeviceListCount(devs) > 1) {
+                qemuReportError(VIR_ERR_OPERATION_FAILED,
+                                _("multiple USB devices for %x:%x, "
+                                  "use <address> to specify one"), vendor, product);
+                usbDeviceListFree(devs);
+                goto cleanup;
+            }
+            usb = usbDeviceListGet(devs, 0);
+            usbDeviceListSteal(devs, usb);
+            usbDeviceListFree(devs);
 
-        hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
-        hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
+            hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
+            hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
 
-        usbFreeDevice(usb);
-    }
+        } else if (!vendor && bus) {
+            usb = usbFindDeviceByBus(bus, device);
+        }
 
+        if (!usb)
+            goto cleanup;
+
+        if (usbDeviceListAdd(list, usb) < 0) {
+            usbFreeDevice(usb);
+            goto cleanup;
+        }
+
+        if (qemuPrepareHostdevUSBDevices(driver, vm->def->name, list) < 0)
+            goto cleanup;
+
+        usbDeviceListSteal(list, usb);
+    }
 
     if (virSecurityManagerSetHostdevLabel(driver->securityManager,
                                           vm->def, hostdev) < 0)
-        return -1;
+        goto cleanup;
 
     switch (hostdev->source.subsys.type) {
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
@@ -1178,6 +1203,7 @@ int qemuDomainAttachHostDevice(struct qemud_driver *driver,
         goto error;
     }
 
+    usbDeviceListFree(list);
     return 0;
 
 error:
@@ -1185,6 +1211,9 @@ error:
                                               vm->def, hostdev) < 0)
         VIR_WARN("Unable to restore host device labelling on hotplug fail");
 
+cleanup:
+    usbDeviceListFree(list);
+    usbDeviceListSteal(driver->activeUsbHostdevs, usb);
     return -1;
 }