From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> Wire up the attach/detach device drivers in LXC to support the hotplug/unplug of USB host devices. Signed-off-by: Daniel P. Berrange <berrange@xxxxxxxxxx> --- src/lxc/lxc_driver.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 08ac70d..50050cf 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -39,6 +39,7 @@ #include "virterror_internal.h" #include "logging.h" #include "datatypes.h" +#include "lxc_cgroup.h" #include "lxc_conf.h" #include "lxc_container.h" #include "lxc_domain.h" @@ -3205,6 +3206,195 @@ cleanup: static int +lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = dev->data.hostdev; + int ret = -1; + char *vroot = NULL; + char *src = NULL; + char *dstdir = NULL; + char *dstfile = NULL; + struct stat sb; + mode_t mode; + bool created = false; + usbDeviceList *list = NULL; + usbDevice *usb = NULL; + virCgroupPtr group = NULL; + + if (virAsprintf(&vroot, "/proc/%llu/root", + (unsigned long long)priv->initpid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dstdir, "%s/dev/bus/usb/%03d", + vroot, + 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, "/dev/bus/usb/%03d/%03d", + def->source.subsys.u.usb.bus, + def->source.subsys.u.usb.device) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!(usb = usbGetDevice(def->source.subsys.u.usb.bus, + def->source.subsys.u.usb.device, vroot))) + goto cleanup; + + if (!(list = usbDeviceListNew())) + goto cleanup; + + if (usbDeviceListAdd(list, usb) < 0) { + usbFreeDevice(usb); + usb = NULL; + goto cleanup; + } + + if (virLXCPrepareHostdevUSBDevices(driver, vm->def->name, list) < 0) { + usb = NULL; + goto cleanup; + } + + usbDeviceListSteal(list, usb); + + 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; + } + created = true; + + if (virSecurityManagerSetHostdevLabel(driver->securityManager, + vm->def, def, vroot) < 0) + goto cleanup; + + if (usbDeviceFileIterate(usb, + virLXCSetupHostUsbDeviceCgroup, + group) < 0) + goto cleanup; + + vm->def->hostdevs[vm->def->nhostdevs++] = def; + + ret = 0; + +cleanup: + virDomainAuditHostdev(vm, def, "attach", ret == 0); + if (ret < 0 && created) + unlink(dstfile); + + usbDeviceListFree(list); + if (ret < 0 && usb) + usbDeviceListSteal(driver->activeUsbHostdevs, usb); + + virCgroupFree(&group); + VIR_FREE(src); + VIR_FREE(dstfile); + VIR_FREE(dstdir); + VIR_FREE(vroot); + return ret; +} + + +static int +lxcDomainAttachDeviceHostdevSubsysLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + switch (dev->data.hostdev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + return lxcDomainAttachDeviceHostdevSubsysUSBLive(driver, vm, dev); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device type %s"), + virDomainHostdevSubsysTypeToString(dev->data.hostdev->source.subsys.type)); + return -1; + } +} + + +static int +lxcDomainAttachDeviceHostdevLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach hostdev until init PID is known")); + return -1; + } + + switch (dev->data.hostdev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return lxcDomainAttachDeviceHostdevSubsysLive(driver, vm, dev); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device mode %s"), + virDomainHostdevModeTypeToString(dev->data.hostdev->mode)); + return -1; + } +} + + +static int lxcDomainAttachDeviceLive(virConnectPtr conn, virLXCDriverPtr driver, virDomainObjPtr vm, @@ -3226,6 +3416,12 @@ lxcDomainAttachDeviceLive(virConnectPtr conn, dev->data.net = NULL; break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = lxcDomainAttachDeviceHostdevLive(driver, vm, dev); + if (!ret) + dev->data.disk = NULL; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be attached"), @@ -3375,6 +3571,138 @@ cleanup: static int +lxcDomainDetachDeviceHostdevUSBLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + virDomainHostdevDefPtr def = NULL; + virCgroupPtr group = NULL; + int idx, ret = -1; + char *dst = NULL; + char *vroot = NULL; + usbDevice *usb = NULL; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach hostdev until init PID is known")); + goto cleanup; + } + + + if ((idx = virDomainHostdevFind(vm->def, + dev->data.hostdev, + &def)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("usb device not found")); + goto cleanup; + } + + if (virAsprintf(&vroot, "/proc/%llu/root", + (unsigned long long)priv->initpid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&dst, "%s/dev/bus/usb/%03d/%03d", + vroot, + def->source.subsys.u.usb.bus, + def->source.subsys.u.usb.device) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (!(usb = usbGetDevice(def->source.subsys.u.usb.bus, + def->source.subsys.u.usb.device, vroot))) + goto cleanup; + + VIR_DEBUG("Unlinking %s", dst); + if (unlink(dst) < 0 && errno != ENOENT) { + virDomainAuditHostdev(vm, def, "detach", false); + virReportSystemError(errno, + _("Unable to remove device %s"), dst); + goto cleanup; + } + virDomainAuditHostdev(vm, def, "detach", true); + + if (usbDeviceFileIterate(usb, + virLXCTeardownHostUsbDeviceCgroup, + group) < 0) + VIR_WARN("cannot deny device %s for domain %s", + dst, vm->def->name); + + usbDeviceListDel(driver->activeUsbHostdevs, usb); + + virDomainHostdevRemove(vm->def, idx); + virDomainHostdevDefFree(def); + + ret = 0; + +cleanup: + usbFreeDevice(usb); + VIR_FREE(dst); + VIR_FREE(vroot); + virCgroupFree(&group); + return ret; +} + +static int +lxcDomainDetachDeviceHostdevSubsysLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + switch (dev->data.hostdev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + return lxcDomainDetachDeviceHostdevUSBLive(driver, vm, dev); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device type %s"), + virDomainHostdevSubsysTypeToString(dev->data.hostdev->source.subsys.type)); + return -1; + } +} + + +static int +lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virLXCDomainObjPrivatePtr priv = vm->privateData; + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Cannot attach hostdev until init PID is known")); + return -1; + } + + switch (dev->data.hostdev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return lxcDomainDetachDeviceHostdevSubsysLive(driver, vm, dev); + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported host device mode %s"), + virDomainHostdevModeTypeToString(dev->data.hostdev->mode)); + return -1; + } +} + + +static int lxcDomainDetachDeviceLive(virLXCDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) @@ -3390,6 +3718,10 @@ lxcDomainDetachDeviceLive(virLXCDriverPtr driver, ret = lxcDomainDetachDeviceNetLive(vm, dev); break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = lxcDomainDetachDeviceHostdevLive(driver, vm, dev); + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be detached"), -- 1.8.0.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list