From: "Daniel P. Berrange" <berrange(a)redhat.com>
Wire up the attach/detach device drivers in LXC to support the
hotplug/unplug of host storage devices.
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
---
src/lxc/lxc_driver.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 237 insertions(+)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index 50050cf..27ee3d7 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -3224,6 +3224,12 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver,
usbDevice *usb = NULL;
virCgroupPtr group = NULL;
+ if (virDomainHostdevFind(vm->def, def, NULL) >= 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("host USB device already exists"));
+ return -1;
+ }
+
if (virAsprintf(&vroot, "/proc/%llu/root",
(unsigned long long)priv->initpid) < 0) {
virReportOOMError();
@@ -3351,6 +3357,123 @@ cleanup:
static int
+lxcDomainAttachDeviceHostdevStorageLive(virLXCDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev)
+{
+ virLXCDomainObjPrivatePtr priv = vm->privateData;
+ virDomainHostdevDefPtr def = dev->data.hostdev;
+ virCgroupPtr group = NULL;
+ int ret = -1;
+ char *dst;
+ char *vroot;
+ struct stat sb;
+ bool created = false;
+ mode_t mode = 0;
+
+ if (!priv->initpid) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("Cannot attach disk until init PID is known"));
+ goto cleanup;
+ }
+
+ if (!def->source.caps.u.storage.block) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Missing storage block path"));
+ goto cleanup;
+ }
+
+ if (virDomainHostdevFind(vm->def, def, NULL) >= 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("host device already exists"));
+ return -1;
+ }
+
+ if (stat(def->source.caps.u.storage.block, &sb) < 0) {
+ virReportSystemError(errno,
+ _("Unable to access %s"),
+ def->source.caps.u.storage.block);
+ goto cleanup;
+ }
+
+ if (!S_ISBLK(sb.st_mode)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Hostdev source %s must be a block device"),
+ def->source.caps.u.storage.block);
+ goto cleanup;
+ }
+
+ if (virAsprintf(&vroot, "/proc/%llu/root",
+ (unsigned long long)priv->initpid) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (virAsprintf(&dst, "%s/%s",
+ vroot,
+ def->source.caps.u.storage.block) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ mode = 0700 | S_IFBLK;
+
+ VIR_DEBUG("Creating dev %s (%d,%d)",
+ def->source.caps.u.storage.block,
+ major(sb.st_rdev), minor(sb.st_rdev));
+ if (mknod(dst, mode, sb.st_rdev) < 0) {
+ virReportSystemError(errno,
+ _("Unable to create device %s"),
+ dst);
+ goto cleanup;
+ }
+ created = true;
+
+ if (virSecurityManagerSetHostdevLabel(driver->securityManager,
+ vm->def, def, vroot) < 0)
+ 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 (virCgroupAllowDevicePath(group, def->source.caps.u.storage.block,
+ VIR_CGROUP_DEVICE_RW |
+ VIR_CGROUP_DEVICE_MKNOD) != 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot allow device %s for domain %s"),
+ def->source.caps.u.storage.block, vm->def->name);
+ goto cleanup;
+ }
+
+ vm->def->hostdevs[vm->def->nhostdevs++] = def;
+
+ ret = 0;
+
+cleanup:
+ virDomainAuditHostdev(vm, def, "attach", ret == 0);
+ if (group)
+ virCgroupFree(&group);
+ if (dst && created && ret < 0)
+ unlink(dst);
+ return ret;
+}
+
+
+static int
lxcDomainAttachDeviceHostdevSubsysLive(virLXCDriverPtr driver,
virDomainObjPtr vm,
virDomainDeviceDefPtr dev)
@@ -3369,6 +3492,24 @@ lxcDomainAttachDeviceHostdevSubsysLive(virLXCDriverPtr driver,
static int
+lxcDomainAttachDeviceHostdevCapsLive(virLXCDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev)
+{
+ switch (dev->data.hostdev->source.caps.type) {
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE:
+ return lxcDomainAttachDeviceHostdevStorageLive(driver, vm, dev);
+
+ default:
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Unsupported host device type %s"),
+
virDomainHostdevCapsTypeToString(dev->data.hostdev->source.caps.type));
+ return -1;
+ }
+}
+
+
+static int
lxcDomainAttachDeviceHostdevLive(virLXCDriverPtr driver,
virDomainObjPtr vm,
virDomainDeviceDefPtr dev)
@@ -3385,6 +3526,9 @@ lxcDomainAttachDeviceHostdevLive(virLXCDriverPtr driver,
case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
return lxcDomainAttachDeviceHostdevSubsysLive(driver, vm, dev);
+ case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
+ return lxcDomainAttachDeviceHostdevCapsLive(driver, vm, dev);
+
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unsupported host device mode %s"),
@@ -3658,6 +3802,78 @@ cleanup:
return ret;
}
+
+static int
+lxcDomainDetachDeviceHostdevStorageLive(virLXCDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev)
+{
+ virLXCDomainObjPrivatePtr priv = vm->privateData;
+ virDomainHostdevDefPtr def = NULL;
+ virCgroupPtr group = NULL;
+ int i, ret = -1;
+ char *dst = NULL;
+
+ if (!priv->initpid) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("Cannot attach disk until init PID is known"));
+ goto cleanup;
+ }
+
+ if ((i = virDomainHostdevFind(vm->def,
+ dev->data.hostdev,
+ &def)) < 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("hostdev %s not found"),
+ dev->data.hostdev->source.caps.u.storage.block);
+ goto cleanup;
+ }
+
+ if (virAsprintf(&dst, "/proc/%llu/root/%s",
+ (unsigned long long)priv->initpid,
+ def->source.caps.u.storage.block) < 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;
+ }
+
+ 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 (virCgroupDenyDevicePath(group, def->source.caps.u.storage.block,
VIR_CGROUP_DEVICE_RWM) != 0)
+ VIR_WARN("cannot deny device %s for domain %s",
+ def->source.caps.u.storage.block, vm->def->name);
+
+ virDomainHostdevRemove(vm->def, i);
+ virDomainHostdevDefFree(def);
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(dst);
+ if (group)
+ virCgroupFree(&group);
+ return ret;
+}
+
+
static int
lxcDomainDetachDeviceHostdevSubsysLive(virLXCDriverPtr driver,
virDomainObjPtr vm,
@@ -3677,6 +3893,24 @@ lxcDomainDetachDeviceHostdevSubsysLive(virLXCDriverPtr driver,
static int
+lxcDomainDetachDeviceHostdevCapsLive(virLXCDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev)
+{
+ switch (dev->data.hostdev->source.caps.type) {
+ case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE:
+ return lxcDomainDetachDeviceHostdevStorageLive(driver, vm, dev);
+
+ default:
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Unsupported host device type %s"),
+
virDomainHostdevCapsTypeToString(dev->data.hostdev->source.caps.type));
+ return -1;
+ }
+}
+
+
+static int
lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver,
virDomainObjPtr vm,
virDomainDeviceDefPtr dev)
@@ -3693,6 +3927,9 @@ lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver,
case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
return lxcDomainDetachDeviceHostdevSubsysLive(driver, vm, dev);
+ case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
+ return lxcDomainDetachDeviceHostdevCapsLive(driver, vm, dev);
+
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unsupported host device mode %s"),
--
1.8.0.1