[libvirt] [PATCH 10/23] Add support for USB host device passthrough with LXC

Daniel P. Berrange berrange at redhat.com
Fri Nov 30 20:26:24 UTC 2012


From: "Daniel P. Berrange" <berrange at redhat.com>

This adds support for host device passthrough with the
LXC driver. Since there is only a single kernel image,
it doesn't make sense to pass through PCI devices, but
USB devices are fine. For the latter we merely need to
make the /dev/bus/usb/NNN/MMM character device exist
in the container's /dev

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 src/Makefile.am         |   1 +
 src/lxc/lxc_cgroup.c    |  64 ++++++++
 src/lxc/lxc_cgroup.h    |  12 ++
 src/lxc/lxc_conf.h      |   3 +
 src/lxc/lxc_container.c | 124 ++++++++++++++-
 src/lxc/lxc_driver.c    |   6 +
 src/lxc/lxc_hostdev.c   | 390 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/lxc/lxc_hostdev.h   |  43 ++++++
 src/lxc/lxc_process.c   |  11 ++
 9 files changed, 653 insertions(+), 1 deletion(-)
 create mode 100644 src/lxc/lxc_hostdev.c
 create mode 100644 src/lxc/lxc_hostdev.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 627dbb5..c3459a5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -411,6 +411,7 @@ LXC_DRIVER_SOURCES =						\
 		lxc/lxc_container.c lxc/lxc_container.h		\
 		lxc/lxc_cgroup.c lxc/lxc_cgroup.h		\
 		lxc/lxc_domain.c lxc/lxc_domain.h		\
+		lxc/lxc_hostdev.c lxc/lxc_hostdev.h		\
 		lxc/lxc_monitor.c lxc/lxc_monitor.h		\
 		lxc/lxc_process.c lxc/lxc_process.h		\
 		lxc/lxc_fuse.c lxc/lxc_fuse.h			\
diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c
index 0636869..14c840a 100644
--- a/src/lxc/lxc_cgroup.c
+++ b/src/lxc/lxc_cgroup.c
@@ -291,6 +291,49 @@ struct _virLXCCgroupDevicePolicy {
 };
 
 
+int
+virLXCSetupHostUsbDeviceCgroup(usbDevice *dev ATTRIBUTE_UNUSED,
+                               const char *path,
+                               void *opaque)
+{
+    virCgroupPtr cgroup = opaque;
+    int rc;
+
+    VIR_DEBUG("Process path '%s' for USB device", path);
+    rc = virCgroupAllowDevicePath(cgroup, path,
+                                  VIR_CGROUP_DEVICE_RW);
+    if (rc < 0) {
+        virReportSystemError(-rc,
+                             _("Unable to allow device %s"),
+                             path);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+int
+virLXCTeardownHostUsbDeviceCgroup(usbDevice *dev ATTRIBUTE_UNUSED,
+                                  const char *path,
+                                  void *opaque)
+{
+    virCgroupPtr cgroup = opaque;
+    int rc;
+
+    VIR_DEBUG("Process path '%s' for USB device", path);
+    rc = virCgroupDenyDevicePath(cgroup, path,
+                                 VIR_CGROUP_DEVICE_RW);
+    if (rc < 0) {
+        virReportSystemError(-rc,
+                             _("Unable to deny device %s"),
+                             path);
+        return -1;
+    }
+
+    return 0;
+}
+
 
 static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def,
                                       virCgroupPtr cgroup)
@@ -367,6 +410,27 @@ static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def,
         }
     }
 
+    for (i = 0; i < def->nhostdevs; i++) {
+        virDomainHostdevDefPtr hostdev = def->hostdevs[i];
+        usbDevice *usb;
+
+        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+            continue;
+        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
+            continue;
+        if (hostdev->missing)
+            continue;
+
+        if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus,
+                                hostdev->source.subsys.u.usb.device,
+                                NULL)) == NULL)
+            goto cleanup;
+
+        if (usbDeviceFileIterate(usb, virLXCSetupHostUsbDeviceCgroup,
+                                 cgroup) < 0)
+            goto cleanup;
+    }
+
     rc = virCgroupAllowDeviceMajor(cgroup, 'c', LXC_DEV_MAJ_PTY,
                                    VIR_CGROUP_DEVICE_RWM);
     if (rc != 0) {
diff --git a/src/lxc/lxc_cgroup.h b/src/lxc/lxc_cgroup.h
index 6961943..c1d4551 100644
--- a/src/lxc/lxc_cgroup.h
+++ b/src/lxc/lxc_cgroup.h
@@ -24,7 +24,19 @@
 
 # include "domain_conf.h"
 # include "lxc_fuse.h"
+# include "hostusb.h"
 
 int virLXCCgroupSetup(virDomainDefPtr def);
 int virLXCCgroupGetMeminfo(virLXCMeminfoPtr meminfo);
+
+int
+virLXCSetupHostUsbDeviceCgroup(usbDevice *dev,
+                               const char *path,
+                               void *opaque);
+
+int
+virLXCTeardownHostUsbDeviceCgroup(usbDevice *dev,
+                                  const char *path,
+                                  void *opaque);
+
 #endif /* __VIR_LXC_CGROUP_H__ */
diff --git a/src/lxc/lxc_conf.h b/src/lxc/lxc_conf.h
index 4ae0c5e..a6872ea 100644
--- a/src/lxc/lxc_conf.h
+++ b/src/lxc/lxc_conf.h
@@ -35,6 +35,7 @@
 # include "cgroup.h"
 # include "security/security_manager.h"
 # include "configmake.h"
+# include "hostusb.h"
 
 # define LXC_DRIVER_NAME "LXC"
 
@@ -60,6 +61,8 @@ struct _virLXCDriver {
     int log_libvirtd;
     int have_netns;
 
+    usbDeviceList *activeUsbHostdevs;
+
     virDomainEventStatePtr domainEventState;
 
     char *securityDriverName;
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c
index be26de9..0a407bf 100644
--- a/src/lxc/lxc_container.c
+++ b/src/lxc/lxc_container.c
@@ -1311,6 +1311,124 @@ static int lxcContainerSetupAllDisks(virDomainDefPtr vmDef,
 }
 
 
+static int lxcContainerSetupHostdevSubsysUSB(virDomainDefPtr vmDef ATTRIBUTE_UNUSED,
+                                             virDomainHostdevDefPtr def ATTRIBUTE_UNUSED,
+                                             const char *dstprefix ATTRIBUTE_UNUSED,
+                                             virSecurityManagerPtr securityDriver ATTRIBUTE_UNUSED)
+{
+    int ret = -1;
+    char *src = NULL;
+    char *dstdir = NULL;
+    char *dstfile = NULL;
+    struct stat sb;
+    mode_t mode;
+
+    if (virAsprintf(&dstdir, "/dev/bus/usb/%03d",
+                    def->source.subsys.u.usb.bus) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (virAsprintf(&dstfile, "%s/%03d",
+                    dstdir,
+                    def->source.subsys.u.usb.device) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (virAsprintf(&src, "%s/%s", dstprefix, dstfile) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (stat(src, &sb) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to access %s"), src);
+        goto cleanup;
+    }
+
+    if (!S_ISCHR(sb.st_mode)) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("USB source %s was not a character device"),
+                       src);
+        goto cleanup;
+    }
+
+    mode = 0700 | S_IFCHR;
+
+    if (virFileMakePath(dstdir) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to create %s"), dstdir);
+        goto cleanup;
+    }
+
+    VIR_DEBUG("Creating dev %s (%d,%d)",
+              dstfile, major(sb.st_rdev), minor(sb.st_rdev));
+    if (mknod(dstfile, mode, sb.st_rdev) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to create device %s"),
+                             dstfile);
+        goto cleanup;
+    }
+
+    if (virSecurityManagerSetHostdevLabel(securityDriver, vmDef, def, NULL) < 0)
+        goto cleanup;
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(src);
+    VIR_FREE(dstfile);
+    VIR_FREE(dstdir);
+    return ret;
+}
+
+
+static int lxcContainerSetupHostdevSubsys(virDomainDefPtr vmDef,
+                                          virDomainHostdevDefPtr def,
+                                          const char *dstprefix,
+                                          virSecurityManagerPtr securityDriver)
+{
+    switch (def->source.subsys.type) {
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
+        return lxcContainerSetupHostdevSubsysUSB(vmDef, def, dstprefix, securityDriver);
+
+    default:
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Unsupported host device mode %s"),
+                       virDomainHostdevSubsysTypeToString(def->source.subsys.type));
+        return -1;
+    }
+}
+
+
+static int lxcContainerSetupAllHostdevs(virDomainDefPtr vmDef,
+                                        const char *dstprefix,
+                                        virSecurityManagerPtr securityDriver)
+{
+    size_t i;
+    VIR_DEBUG("Setting up hostdevs %s", dstprefix);
+
+    for (i = 0 ; i < vmDef->nhostdevs ; i++) {
+        virDomainHostdevDefPtr def = vmDef->hostdevs[i];
+        switch (def->mode) {
+        case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
+            if (lxcContainerSetupHostdevSubsys(vmDef, def, dstprefix, securityDriver) < 0)
+                return -1;
+            break;
+        default:
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("Unsupported host device mode %s"),
+                           virDomainHostdevModeTypeToString(def->mode));
+            return -1;
+        }
+    }
+
+    VIR_DEBUG("Setup all hostdevs");
+    return 0;
+}
+
+
 static int lxcContainerGetSubtree(const char *prefix,
                                   char ***mountsret,
                                   size_t *nmountsret)
@@ -1710,7 +1828,11 @@ static int lxcContainerSetupPivotRoot(virDomainDefPtr vmDef,
     if (lxcContainerSetupAllDisks(vmDef, "/.oldroot", securityDriver) < 0)
         goto cleanup;
 
-    /* Gets rid of all remaining mounts from host OS, including /.oldroot itself */
+    /* Sets up any extra host devices from guest config */
+    if (lxcContainerSetupAllHostdevs(vmDef, "/.oldroot", securityDriver) < 0)
+        goto cleanup;
+
+   /* Gets rid of all remaining mounts from host OS, including /.oldroot itself */
     if (lxcContainerUnmountSubtree("/.oldroot", true) < 0)
         goto cleanup;
 
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index 2e9cfe4..1c6ec8c 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -42,6 +42,7 @@
 #include "lxc_container.h"
 #include "lxc_domain.h"
 #include "lxc_driver.h"
+#include "lxc_hostdev.h"
 #include "lxc_process.h"
 #include "memory.h"
 #include "util.h"
@@ -1463,6 +1464,9 @@ static int lxcStartup(int privileged)
     if ((lxc_driver->caps = lxcCapsInit(lxc_driver)) == NULL)
         goto cleanup;
 
+    if ((lxc_driver->activeUsbHostdevs = usbDeviceListNew()) == NULL)
+        goto cleanup;
+
     virLXCDomainSetPrivateDataHooks(lxc_driver->caps);
 
     if (virLXCProcessAutoDestroyInit(lxc_driver) < 0)
@@ -1548,6 +1552,8 @@ static int lxcShutdown(void)
     virDomainObjListDeinit(&lxc_driver->domains);
     virDomainEventStateFree(lxc_driver->domainEventState);
 
+    usbDeviceListFree(lxc_driver->activeUsbHostdevs);
+
     virLXCProcessAutoDestroyShutdown(lxc_driver);
 
     virCapabilitiesFree(lxc_driver->caps);
diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c
new file mode 100644
index 0000000..c063aa0
--- /dev/null
+++ b/src/lxc/lxc_hostdev.c
@@ -0,0 +1,390 @@
+/*
+ * virLXC_hostdev.c: VIRLXC hostdev management
+ *
+ * Copyright (C) 2006-2007, 2009-2012 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include "lxc_hostdev.h"
+#include "logging.h"
+#include "virterror_internal.h"
+#include "memory.h"
+
+#define VIR_FROM_THIS VIR_FROM_LXC
+
+int
+virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver,
+                              virDomainDefPtr def)
+{
+    virDomainHostdevDefPtr hostdev = NULL;
+    size_t i;
+
+    if (!def->nhostdevs)
+        return 0;
+
+    for (i = 0; i < def->nhostdevs; i++) {
+        usbDevice *usb = NULL;
+        hostdev = def->hostdevs[i];
+
+        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+            continue;
+        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
+            continue;
+
+        usb = usbGetDevice(hostdev->source.subsys.u.usb.bus,
+                           hostdev->source.subsys.u.usb.device,
+                           NULL);
+        if (!usb) {
+            VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s",
+                     hostdev->source.subsys.u.usb.bus,
+                     hostdev->source.subsys.u.usb.device,
+                     def->name);
+            continue;
+        }
+
+        usbDeviceSetUsedBy(usb, def->name);
+
+        if (usbDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) {
+            usbFreeDevice(usb);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+
+int
+virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver,
+                               const char *name,
+                               usbDeviceList *list)
+{
+    size_t i, j;
+    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)
+                virReportError(VIR_ERR_OPERATION_INVALID,
+                               _("USB device %s is in use by domain %s"),
+                               usbDeviceGetName(tmp), other_name);
+            else
+                virReportError(VIR_ERR_OPERATION_INVALID,
+                               _("USB device %s is already in use"),
+                               usbDeviceGetName(tmp));
+            goto error;
+        }
+
+        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)
+            goto error;
+    }
+    return 0;
+
+error:
+    for (j = 0; j < i; j++) {
+        tmp = usbDeviceListGet(list, i);
+        usbDeviceListSteal(driver->activeUsbHostdevs, tmp);
+    }
+    return -1;
+}
+
+int
+virLXCFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
+                           bool mandatory,
+                           usbDevice **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;
+    bool autoAddress = hostdev->source.subsys.u.usb.autoAddress;
+    int rc;
+
+    *usb = NULL;
+
+    if (vendor && bus) {
+        rc = usbFindDevice(vendor, product, bus, device,
+                           NULL,
+                           autoAddress ? false : mandatory,
+                           usb);
+        if (rc < 0) {
+            return -1;
+        } 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,
+                                   NULL,
+                                   mandatory, &devs);
+        if (rc < 0)
+            return -1;
+
+        if (rc == 1) {
+            *usb = usbDeviceListGet(devs, 0);
+            usbDeviceListSteal(devs, *usb);
+        }
+        usbDeviceListFree(devs);
+
+        if (rc == 0) {
+            goto out;
+        } else if (rc > 1) {
+            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,
+                               NULL,
+                               mandatory, usb) < 0)
+            return -1;
+    }
+
+out:
+    if (!*usb)
+        hostdev->missing = 1;
+    return 0;
+}
+
+static int
+virLXCPrepareHostUSBDevices(virLXCDriverPtr driver,
+                            virDomainDefPtr def)
+{
+    size_t i;
+    int 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.
+     * This is done in several loops which cannot be joined into one big
+     * loop. See virLXCPrepareHostdevPCIDevices()
+     */
+    if (!(list = usbDeviceListNew()))
+        goto cleanup;
+
+    /* Loop 1: build temporary list
+     */
+    for (i = 0 ; i < nhostdevs ; i++) {
+        virDomainHostdevDefPtr hostdev = hostdevs[i];
+        bool required = true;
+        usbDevice *usb;
+
+        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+            continue;
+        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
+            continue;
+
+        if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL)
+            required = false;
+
+        if (virLXCFindHostdevUSBDevice(hostdev, required, &usb) < 0)
+            goto cleanup;
+
+        if (usb && usbDeviceListAdd(list, usb) < 0) {
+            usbFreeDevice(usb);
+            goto cleanup;
+        }
+    }
+
+    /* Mark devices in temporary list as used by @name
+     * and add them do driver list. However, if something goes
+     * wrong, perform rollback.
+     */
+    if (virLXCPrepareHostdevUSBDevices(driver, def->name, list) < 0)
+        goto cleanup;
+
+    /* Loop 2: Temporary list was successfully merged with
+     * driver list, so steal all items to avoid freeing them
+     * in cleanup label.
+     */
+    while (usbDeviceListCount(list) > 0) {
+        tmp = usbDeviceListGet(list, 0);
+        usbDeviceListSteal(list, tmp);
+    }
+
+    ret = 0;
+
+cleanup:
+    usbDeviceListFree(list);
+    return ret;
+}
+
+
+int virLXCPrepareHostDevices(virLXCDriverPtr driver,
+                             virDomainDefPtr def)
+{
+    size_t i;
+
+    if (!def->nhostdevs)
+        return 0;
+
+    /* Sanity check for supported configurations only */
+    for (i = 0 ; i < def->nhostdevs ; i++) {
+        virDomainHostdevDefPtr dev = def->hostdevs[i];
+
+        switch (dev->mode) {
+        case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
+            switch (dev->source.subsys.type) {
+            case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
+                break;
+            default:
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("Unsupported hostdev type %s"),
+                               virDomainHostdevSubsysTypeToString(dev->source.subsys.type));
+                break;
+            }
+            break;
+
+        default:
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("Unsupported hostdev mode %s"),
+                           virDomainHostdevModeTypeToString(dev->mode));
+            break;
+        }
+    }
+
+    if (virLXCPrepareHostUSBDevices(driver, def) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static void
+virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver,
+                                   const char *name,
+                                   virDomainHostdevDefPtr *hostdevs,
+                                   int nhostdevs)
+{
+    size_t i;
+
+    for (i = 0; i < nhostdevs; i++) {
+        virDomainHostdevDefPtr hostdev = hostdevs[i];
+        usbDevice *usb, *tmp;
+        const char *used_by = NULL;
+
+        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+            continue;
+        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
+            continue;
+        if (hostdev->missing)
+            continue;
+
+        usb = usbGetDevice(hostdev->source.subsys.u.usb.bus,
+                           hostdev->source.subsys.u.usb.device,
+                           NULL);
+
+        if (!usb) {
+            VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s",
+                     hostdev->source.subsys.u.usb.bus,
+                     hostdev->source.subsys.u.usb.device,
+                     name);
+            continue;
+        }
+
+        /* Delete only those USB devices which belongs
+         * to domain @name because virLXCProcessStart() might
+         * have failed because USB device is already taken.
+         * Therefore we want to steal only those devices from
+         * the list which were taken by @name */
+
+        tmp = usbDeviceListFind(driver->activeUsbHostdevs, usb);
+        usbFreeDevice(usb);
+
+        if (!tmp) {
+            VIR_WARN("Unable to find device %03d.%03d "
+                     "in list of active USB devices",
+                     hostdev->source.subsys.u.usb.bus,
+                     hostdev->source.subsys.u.usb.device);
+            continue;
+        }
+
+        used_by = usbDeviceGetUsedBy(tmp);
+        if (STREQ_NULLABLE(used_by, name)) {
+            VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs",
+                      hostdev->source.subsys.u.usb.bus,
+                      hostdev->source.subsys.u.usb.device,
+                      name);
+
+            usbDeviceListDel(driver->activeUsbHostdevs, tmp);
+        }
+    }
+}
+
+void virLXCDomainReAttachHostDevices(virLXCDriverPtr driver,
+                                     virDomainDefPtr def)
+{
+    if (!def->nhostdevs)
+        return;
+
+    virLXCDomainReAttachHostUsbDevices(driver, def->name, def->hostdevs,
+                                     def->nhostdevs);
+}
diff --git a/src/lxc/lxc_hostdev.h b/src/lxc/lxc_hostdev.h
new file mode 100644
index 0000000..dd98112
--- /dev/null
+++ b/src/lxc/lxc_hostdev.h
@@ -0,0 +1,43 @@
+/*
+ * virLXC_hostdev.h: VIRLXC hostdev management
+ *
+ * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#ifndef __LXC_HOSTDEV_H__
+# define __LXC_HOSTDEV_H__
+
+# include "lxc_conf.h"
+# include "domain_conf.h"
+
+int virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver,
+                                  virDomainDefPtr def);
+int virLXCFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
+                               bool mandatory,
+                               usbDevice **usb);
+int virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver,
+                                   const char *name,
+                                   usbDeviceList *list);
+int virLXCPrepareHostDevices(virLXCDriverPtr driver,
+                             virDomainDefPtr def);
+void virLXCDomainReAttachHostDevices(virLXCDriverPtr driver,
+                                     virDomainDefPtr def);
+
+#endif /* __LXC_HOSTDEV_H__ */
diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 72e1be3..607e826 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -44,6 +44,7 @@
 #include "logging.h"
 #include "command.h"
 #include "hooks.h"
+#include "lxc_hostdev.h"
 
 #define VIR_FROM_THIS VIR_FROM_LXC
 
@@ -252,6 +253,8 @@ static void virLXCProcessCleanup(virLXCDriverPtr driver,
     vm->pid = -1;
     vm->def->id = -1;
 
+    virLXCDomainReAttachHostDevices(driver, vm->def);
+
     for (i = 0 ; i < vm->def->nnets ; i++) {
         virDomainNetDefPtr iface = vm->def->nets[i];
         vport = virDomainNetGetActualVirtPortProfile(iface);
@@ -973,6 +976,11 @@ int virLXCProcessStart(virConnectPtr conn,
             goto cleanup;
     }
 
+    /* Must be run before security labelling */
+    VIR_DEBUG("Preparing host devices");
+    if (virLXCPrepareHostDevices(driver, vm->def) < 0)
+        goto cleanup;
+
     /* Here we open all the PTYs we need on the host OS side.
      * The LXC controller will open the guest OS side PTYs
      * and forward I/O between them.
@@ -1298,6 +1306,9 @@ virLXCProcessReconnectDomain(void *payload, const void *name ATTRIBUTE_UNUSED, v
         if (!(priv->monitor = virLXCProcessConnectMonitor(driver, vm)))
             goto error;
 
+        if (virLXCUpdateActiveUsbHostdevs(driver, vm->def) < 0)
+            goto error;
+
         if (virSecurityManagerReserveLabel(driver->securityManager,
                                            vm->def, vm->pid) < 0)
             goto error;
-- 
1.8.0.1




More information about the libvir-list mailing list