From 09ed07293f8e5bffe44a44d4324e4103f03705d0 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 4 Mar 2010 11:48:16 +0000 Subject: [PATCH] Fix USB passthrough based on product/vendor Changeset commit 5073aa994af460e775cb3e548528e28d7660fcc8 Author: Cole Robinson Date: Mon Jan 11 11:40:46 2010 -0500 Added support for product/vendor based passthrough, but it only worked at the security driver layer. The main guest XML config was not updated with the resolved bus/device ID. When the QEMU argv refactoring removed use of product/vendor, this then broke launching guests. THe solution is to move the product/vendor resolution up a layer into the QEMU driver. So the first thing QEMU does is resolve the product/vendor to a bus/device and updates the XML config with this info. The rest of the code, including security drivers and QEMU argv generated can now rely on bus/device always being set. * src/util/hostusb.c, src/util/hostusb.h: Split vendor/product resolution code out of usbGetDevice and into usbFindDevice. Add accessors for bus/device ID * src/security/virt-aa-helper.c, src/security/security_selinux.c, src/qemu/qemu_security_dac.c: Remove vendor/product from the usbGetDevice() calls * src/qemu/qemu_driver.c: Use usbFindDevice to resolve vendor/product into a bus/device ID --- src/libvirt_private.syms | 3 ++ src/qemu/qemu_driver.c | 94 +++++++++++++++++++++++++++++---- src/qemu/qemu_security_dac.c | 8 +-- src/security/security_selinux.c | 8 +-- src/security/virt-aa-helper.c | 4 +- src/util/hostusb.c | 39 ++++++++++---- src/util/hostusb.h | 13 +++-- 7 files changed, 129 insertions(+), 40 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ce9f01373f..c5ee23dc6c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -599,7 +599,10 @@ virArgvToString; # usb.h usbGetDevice; +usbFindDevice; usbFreeDevice; +usbDeviceGetBus; +usbDeviceGetDevno; usbDeviceFileIterate; # uuid.h diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e11e9e88c6..4707f721fe 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2290,17 +2290,15 @@ cleanup: return ret; } + static int -qemuPrepareHostDevices(struct qemud_driver *driver, - virDomainDefPtr def) +qemuPrepareHostPCIDevices(struct qemud_driver *driver, + virDomainDefPtr def) { pciDeviceList *pcidevs; int i; int ret = -1; - if (!def->nhostdevs) - return 0; - if (!(pcidevs = qemuGetPciHostDeviceList(def))) return -1; @@ -2351,6 +2349,56 @@ cleanup: return ret; } + +static int +qemuPrepareHostUSBDevices(struct qemud_driver *driver ATTRIBUTE_UNUSED, + virDomainDefPtr def) +{ + int i; + for (i = 0 ; i < def->nhostdevs ; i++) { + virDomainHostdevDefPtr hostdev = def->hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + /* Resolve a vendor/product to bus/device */ + if (hostdev->source.subsys.u.usb.vendor) { + usbDevice *usb + = usbFindDevice(hostdev->source.subsys.u.usb.vendor, + hostdev->source.subsys.u.usb.product); + + if (!usb) + return -1; + + hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb); + hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb); + + usbFreeDevice(usb); + } + } + + return 0; +} + +static int +qemuPrepareHostDevices(struct qemud_driver *driver, + virDomainDefPtr def) +{ + if (!def->nhostdevs) + return 0; + + if (qemuPrepareHostPCIDevices(driver, def) < 0) + return -1; + + if (qemuPrepareHostUSBDevices(driver, def) < 0) + return -1; + + return 0; +} + + static void qemudReattachManagedDevice(pciDevice *dev) { @@ -6478,6 +6526,23 @@ static int qemudDomainAttachHostDevice(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 + = usbFindDevice(hostdev->source.subsys.u.usb.vendor, + hostdev->source.subsys.u.usb.product); + + if (!usb) + return -1; + + hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb); + hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb); + + usbFreeDevice(usb); + } + + if (driver->securityDriver && driver->securityDriver->domainSetSecurityHostdevLabel && driver->securityDriver->domainSetSecurityHostdevLabel(vm, hostdev) < 0) @@ -7041,11 +7106,22 @@ static int qemudDomainDetachHostUsbDevice(struct qemud_driver *driver, unsigned bus = vm->def->hostdevs[i]->source.subsys.u.usb.bus; unsigned device = vm->def->hostdevs[i]->source.subsys.u.usb.device; + unsigned product = vm->def->hostdevs[i]->source.subsys.u.usb.product; + unsigned vendor = vm->def->hostdevs[i]->source.subsys.u.usb.vendor; - if (dev->data.hostdev->source.subsys.u.usb.bus == bus && - dev->data.hostdev->source.subsys.u.usb.device == device) { - detach = vm->def->hostdevs[i]; - break; + if (dev->data.hostdev->source.subsys.u.usb.bus && + dev->data.hostdev->source.subsys.u.usb.device) { + if (dev->data.hostdev->source.subsys.u.usb.bus == bus && + dev->data.hostdev->source.subsys.u.usb.device == device) { + detach = vm->def->hostdevs[i]; + break; + } + } else { + if (dev->data.hostdev->source.subsys.u.usb.product == product && + dev->data.hostdev->source.subsys.u.usb.vendor == vendor) { + detach = vm->def->hostdevs[i]; + break; + } } } diff --git a/src/qemu/qemu_security_dac.c b/src/qemu/qemu_security_dac.c index f281b96179..6911f4874a 100644 --- a/src/qemu/qemu_security_dac.c +++ b/src/qemu/qemu_security_dac.c @@ -206,9 +206,7 @@ qemuSecurityDACSetSecurityHostdevLabel(virDomainObjPtr vm, switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device, - dev->source.subsys.u.usb.vendor, - dev->source.subsys.u.usb.product); + dev->source.subsys.u.usb.device); if (!usb) goto done; @@ -277,9 +275,7 @@ qemuSecurityDACRestoreSecurityHostdevLabel(virDomainObjPtr vm ATTRIBUTE_UNUSED, switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device, - dev->source.subsys.u.usb.vendor, - dev->source.subsys.u.usb.product); + dev->source.subsys.u.usb.device); if (!usb) goto done; diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 06a2479025..b2c85815c3 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -491,9 +491,7 @@ SELinuxSetSecurityHostdevLabel(virDomainObjPtr vm, switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device, - dev->source.subsys.u.usb.vendor, - dev->source.subsys.u.usb.product); + dev->source.subsys.u.usb.device); if (!usb) goto done; @@ -561,9 +559,7 @@ SELinuxRestoreSecurityHostdevLabel(virDomainObjPtr vm, switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device, - dev->source.subsys.u.usb.vendor, - dev->source.subsys.u.usb.product); + dev->source.subsys.u.usb.device); if (!usb) goto done; diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 066e18b3f3..78bef41ae8 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -836,9 +836,7 @@ get_files(vahControl * ctl) switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device, - dev->source.subsys.u.usb.vendor, - dev->source.subsys.u.usb.product); + dev->source.subsys.u.usb.device); if (usb == NULL) continue; diff --git a/src/util/hostusb.c b/src/util/hostusb.c index bf9653936c..afba325951 100644 --- a/src/util/hostusb.c +++ b/src/util/hostusb.c @@ -159,9 +159,7 @@ cleanup: usbDevice * usbGetDevice(unsigned bus, - unsigned devno, - unsigned vendor, - unsigned product) + unsigned devno) { usbDevice *dev; @@ -170,14 +168,6 @@ usbGetDevice(unsigned bus, return NULL; } - if (vendor) { - /* Look up bus.dev by vendor:product */ - if (usbFindBusByVendor(vendor, product, &bus, &devno) < 0) { - VIR_FREE(dev); - return NULL; - } - } - dev->bus = bus; dev->dev = devno; @@ -194,6 +184,21 @@ usbGetDevice(unsigned bus, return dev; } + +usbDevice * +usbFindDevice(unsigned vendor, + unsigned product) +{ + unsigned bus = 0, devno = 0; + + if (usbFindBusByVendor(vendor, product, &bus, &devno) < 0) { + return NULL; + } + + return usbGetDevice(bus, devno); +} + + void usbFreeDevice(usbDevice *dev) { @@ -202,6 +207,18 @@ usbFreeDevice(usbDevice *dev) } +unsigned usbDeviceGetBus(usbDevice *dev) +{ + return dev->bus; +} + + +unsigned usbDeviceGetDevno(usbDevice *dev) +{ + return dev->dev; +} + + int usbDeviceFileIterate(usbDevice *dev, usbDeviceFileActor actor, void *opaque) diff --git a/src/util/hostusb.h b/src/util/hostusb.h index bc22671783..9a1b1b763c 100644 --- a/src/util/hostusb.h +++ b/src/util/hostusb.h @@ -26,11 +26,14 @@ typedef struct _usbDevice usbDevice; -usbDevice *usbGetDevice (unsigned bus, - unsigned devno, - unsigned vendor, - unsigned product); -void usbFreeDevice (usbDevice *dev); +usbDevice *usbGetDevice(unsigned bus, + unsigned devno); +usbDevice *usbFindDevice(unsigned vendor, + unsigned product); +void usbFreeDevice (usbDevice *dev); + +unsigned usbDeviceGetBus(usbDevice *dev); +unsigned usbDeviceGetDevno(usbDevice *dev); /* * Callback that will be invoked once for each file