From: "Daniel P. Berrange" <berrange(a)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(a)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(a)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(a)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