[libvirt] [PATCH v1 18/21] qemu: Manage /dev entry on hostdev hotplug

Michal Privoznik mprivozn at redhat.com
Thu Nov 24 14:48:07 UTC 2016


When attaching a device to a domain that's using separate mount
namespace we must maintain /dev entries in order for qemu process
to see them.

Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
---
 src/qemu/qemu_domain.c   | 159 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/qemu/qemu_domain.h   |   8 +++
 src/qemu/qemu_hotplug.c  |  48 +++++++++-----
 src/qemu/qemu_security.c |  38 +++++++++++
 src/qemu/qemu_security.h |   8 +++
 5 files changed, 246 insertions(+), 15 deletions(-)

diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index a1edde9..ac09b2c 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -7364,6 +7364,13 @@ qemuDomainAttachDeviceMknodHelper(pid_t pid ATTRIBUTE_UNUSED,
         def->src->path = tmpsrc;
     }   break;
 
+    case VIR_DOMAIN_DEVICE_HOSTDEV: {
+        virDomainHostdevDefPtr def = data->devDef->data.hostdev;
+        if (virSecurityManagerSetHostdevLabel(data->driver->securityManager,
+                                              data->vm->def, def, NULL) < 0)
+            goto cleanup;
+    }   break;
+
     case VIR_DOMAIN_DEVICE_NONE:
     case VIR_DOMAIN_DEVICE_LEASE:
     case VIR_DOMAIN_DEVICE_FS:
@@ -7371,7 +7378,6 @@ qemuDomainAttachDeviceMknodHelper(pid_t pid ATTRIBUTE_UNUSED,
     case VIR_DOMAIN_DEVICE_INPUT:
     case VIR_DOMAIN_DEVICE_SOUND:
     case VIR_DOMAIN_DEVICE_VIDEO:
-    case VIR_DOMAIN_DEVICE_HOSTDEV:
     case VIR_DOMAIN_DEVICE_WATCHDOG:
     case VIR_DOMAIN_DEVICE_CONTROLLER:
     case VIR_DOMAIN_DEVICE_GRAPHICS:
@@ -7451,6 +7457,91 @@ qemuDomainAttachDeviceMknod(virQEMUDriverPtr driver,
 }
 
 
+static int
+qemuDomainDetachDeviceUnlinkHelper(pid_t pid ATTRIBUTE_UNUSED,
+                                   void *opaque)
+{
+    const char *path = opaque;
+
+    VIR_DEBUG("Unlinking %s", path);
+    if (unlink(path) < 0 && errno != ENOENT) {
+        virReportSystemError(errno,
+                             _("Unable to remove device %s"), path);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+qemuDomainDetachDeviceUnlink(virQEMUDriverPtr driver,
+                             virDomainObjPtr vm,
+                             virDomainDeviceDefPtr dev,
+                             const char *file)
+{
+    /* Technically, this is not needed. Yet. But in the future
+     * security managers might do some reference counting over
+     * Set/Restore label and thus for every SetLabel() there
+     * should be corresponding RestoreLabel(). */
+    switch ((virDomainDeviceType) dev->type) {
+    case VIR_DOMAIN_DEVICE_DISK: {
+        virDomainDiskDefPtr def = dev->data.disk;
+        char *tmpsrc = def->src->path;
+        def->src->path = (char *) file;
+        if (virSecurityManagerRestoreDiskLabel(driver->securityManager,
+                                               vm->def, def) < 0) {
+            def->src->path = tmpsrc;
+            return -1;
+        }
+        def->src->path = tmpsrc;
+    }   break;
+
+    case VIR_DOMAIN_DEVICE_HOSTDEV: {
+        virDomainHostdevDefPtr def = dev->data.hostdev;
+        if (virSecurityManagerRestoreHostdevLabel(driver->securityManager,
+                                                  vm->def, def, NULL) < 0)
+            return -1;
+    }   break;
+
+    case VIR_DOMAIN_DEVICE_NONE:
+    case VIR_DOMAIN_DEVICE_LEASE:
+    case VIR_DOMAIN_DEVICE_FS:
+    case VIR_DOMAIN_DEVICE_NET:
+    case VIR_DOMAIN_DEVICE_INPUT:
+    case VIR_DOMAIN_DEVICE_SOUND:
+    case VIR_DOMAIN_DEVICE_VIDEO:
+    case VIR_DOMAIN_DEVICE_WATCHDOG:
+    case VIR_DOMAIN_DEVICE_CONTROLLER:
+    case VIR_DOMAIN_DEVICE_GRAPHICS:
+    case VIR_DOMAIN_DEVICE_HUB:
+    case VIR_DOMAIN_DEVICE_REDIRDEV:
+    case VIR_DOMAIN_DEVICE_SMARTCARD:
+    case VIR_DOMAIN_DEVICE_CHR:
+    case VIR_DOMAIN_DEVICE_MEMBALLOON:
+    case VIR_DOMAIN_DEVICE_NVRAM:
+    case VIR_DOMAIN_DEVICE_RNG:
+    case VIR_DOMAIN_DEVICE_SHMEM:
+    case VIR_DOMAIN_DEVICE_TPM:
+    case VIR_DOMAIN_DEVICE_PANIC:
+    case VIR_DOMAIN_DEVICE_MEMORY:
+    case VIR_DOMAIN_DEVICE_IOMMU:
+    case VIR_DOMAIN_DEVICE_LAST:
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unexpected device type %d"),
+                       dev->type);
+        return -1;
+    }
+
+    if (virProcessRunInMountNamespace(vm->pid,
+                                      qemuDomainDetachDeviceUnlinkHelper,
+                                      (void *)file) < 0)
+        return -1;
+
+    return 0;
+}
+
+
 int
 qemuDomainNamespaceSetupDisk(virQEMUDriverPtr driver,
                              virDomainObjPtr vm,
@@ -7512,3 +7603,69 @@ qemuDomainNamespaceTeardownDisk(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
      * I don't, therefore: */
     return 0;
 }
+
+
+int
+qemuDomainNamespaceSetupHostdev(virQEMUDriverPtr driver,
+                                virDomainObjPtr vm,
+                                virDomainHostdevDefPtr hostdev)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    virDomainDeviceDef dev = {.type = VIR_DOMAIN_DEVICE_HOSTDEV, .data.hostdev = hostdev};
+    int ret = -1;
+    char *path = NULL;
+
+    if (!priv->containerized)
+        return 0;
+
+    if (qemuDomainGetHostdevPath(hostdev, &path) < 0)
+        goto cleanup;
+
+    if (!path) {
+        /* There's no /dev device that we need to create. Claim success. */
+        ret = 0;
+        goto cleanup;
+    }
+
+    if (qemuDomainAttachDeviceMknod(driver,
+                                    vm,
+                                    &dev,
+                                    path) < 0)
+        goto cleanup;
+    ret = 0;
+ cleanup:
+    VIR_FREE(path);
+    return ret;
+}
+
+
+int
+qemuDomainNamespaceTeardownHostdev(virQEMUDriverPtr driver,
+                                   virDomainObjPtr vm,
+                                   virDomainHostdevDefPtr hostdev)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    virDomainDeviceDef dev = {.type = VIR_DOMAIN_DEVICE_HOSTDEV, .data.hostdev = hostdev};
+    int ret = -1;
+    char *path = NULL;
+
+    if (!priv->containerized)
+        return 0;
+
+    if (qemuDomainGetHostdevPath(hostdev, &path) < 0)
+        goto cleanup;
+
+    if (!path) {
+        /* There's no /dev device that we need to create. Claim success. */
+        ret = 0;
+        goto cleanup;
+    }
+
+    if (qemuDomainDetachDeviceUnlink(driver, vm, &dev, path) < 0)
+        goto cleanup;
+
+    ret = 0;
+ cleanup:
+    VIR_FREE(path);
+    return ret;
+}
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 045d1ba..ef85ae4 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -803,4 +803,12 @@ int qemuDomainNamespaceSetupDisk(virQEMUDriverPtr driver,
 int qemuDomainNamespaceTeardownDisk(virQEMUDriverPtr driver,
                                     virDomainObjPtr vm,
                                     virDomainDiskDefPtr disk);
+
+int qemuDomainNamespaceSetupHostdev(virQEMUDriverPtr driver,
+                                    virDomainObjPtr vm,
+                                    virDomainHostdevDefPtr hostdev);
+
+int qemuDomainNamespaceTeardownHostdev(virQEMUDriverPtr driver,
+                                       virDomainObjPtr vm,
+                                       virDomainHostdevDefPtr hostdev);
 #endif /* __QEMU_DOMAIN_H__ */
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 9b47b5f..58b2439 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -1384,6 +1384,7 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver,
     bool releaseaddr = false;
     bool teardowncgroup = false;
     bool teardownlabel = false;
+    bool teardowndevice = false;
     int backend;
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
     unsigned int flags = 0;
@@ -1435,12 +1436,15 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver,
     }
     vm->def->hostdevs[--(vm->def->nhostdevs)] = NULL;
 
+    if (qemuDomainNamespaceSetupHostdev(driver, vm, hostdev) < 0)
+        goto error;
+    teardowndevice = true;
+
     if (qemuSetupHostdevCgroup(vm, hostdev) < 0)
         goto error;
     teardowncgroup = true;
 
-    if (virSecurityManagerSetHostdevLabel(driver->securityManager,
-                                          vm->def, hostdev, NULL) < 0)
+    if (qemuSecuritySetHostdevLabel(driver, vm, hostdev) < 0)
         goto error;
     if (backend != VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO)
         teardownlabel = true;
@@ -1493,9 +1497,11 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver,
     if (teardowncgroup && qemuTeardownHostdevCgroup(vm, hostdev) < 0)
         VIR_WARN("Unable to remove host device cgroup ACL on hotplug fail");
     if (teardownlabel &&
-        virSecurityManagerRestoreHostdevLabel(driver->securityManager,
-                                              vm->def, hostdev, NULL) < 0)
+        qemuSecurityRestoreHostdevLabel(driver, vm, hostdev) < 0)
         VIR_WARN("Unable to restore host device labelling on hotplug fail");
+    if (teardowndevice &&
+        qemuDomainNamespaceTeardownHostdev(driver, vm, hostdev) < 0)
+        VIR_WARN("Unable to remove host device from /dev");
 
     if (releaseaddr)
         qemuDomainReleaseDeviceAddress(vm, hostdev->info, NULL);
@@ -2275,6 +2281,7 @@ qemuDomainAttachHostUSBDevice(virQEMUDriverPtr driver,
     bool added = false;
     bool teardowncgroup = false;
     bool teardownlabel = false;
+    bool teardowndevice = false;
     int ret = -1;
 
     if (priv->usbaddrs) {
@@ -2288,12 +2295,15 @@ qemuDomainAttachHostUSBDevice(virQEMUDriverPtr driver,
 
     added = true;
 
+    if (qemuDomainNamespaceSetupHostdev(driver, vm, hostdev) < 0)
+        goto cleanup;
+    teardowndevice = true;
+
     if (qemuSetupHostdevCgroup(vm, hostdev) < 0)
         goto cleanup;
     teardowncgroup = true;
 
-    if (virSecurityManagerSetHostdevLabel(driver->securityManager,
-                                          vm->def, hostdev, NULL) < 0)
+    if (qemuSecuritySetHostdevLabel(driver, vm, hostdev) < 0)
         goto cleanup;
     teardownlabel = true;
 
@@ -2323,9 +2333,11 @@ qemuDomainAttachHostUSBDevice(virQEMUDriverPtr driver,
         if (teardowncgroup && qemuTeardownHostdevCgroup(vm, hostdev) < 0)
             VIR_WARN("Unable to remove host device cgroup ACL on hotplug fail");
         if (teardownlabel &&
-            virSecurityManagerRestoreHostdevLabel(driver->securityManager,
-                                                  vm->def, hostdev, NULL) < 0)
+            qemuSecurityRestoreHostdevLabel(driver, vm, hostdev) < 0)
             VIR_WARN("Unable to restore host device labelling on hotplug fail");
+        if (teardowndevice &&
+            qemuDomainNamespaceTeardownHostdev(driver, vm, hostdev) < 0)
+            VIR_WARN("Unable to remove host device from /dev");
         if (added)
             qemuHostdevReAttachUSBDevices(driver, vm->def->name, &hostdev, 1);
         if (releaseaddr)
@@ -2351,6 +2363,7 @@ qemuDomainAttachHostSCSIDevice(virConnectPtr conn,
     char *drivealias = NULL;
     bool teardowncgroup = false;
     bool teardownlabel = false;
+    bool teardowndevice = false;
     bool driveAdded = false;
 
     if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC)) {
@@ -2389,12 +2402,15 @@ qemuDomainAttachHostSCSIDevice(virConnectPtr conn,
         return -1;
     }
 
+    if (qemuDomainNamespaceSetupHostdev(driver, vm, hostdev) < 0)
+        goto cleanup;
+    teardowndevice = true;
+
     if (qemuSetupHostdevCgroup(vm, hostdev) < 0)
         goto cleanup;
     teardowncgroup = true;
 
-    if (virSecurityManagerSetHostdevLabel(driver->securityManager,
-                                          vm->def, hostdev, NULL) < 0)
+    if (qemuSecuritySetHostdevLabel(driver, vm, hostdev) < 0)
         goto cleanup;
     teardownlabel = true;
 
@@ -2441,9 +2457,11 @@ qemuDomainAttachHostSCSIDevice(virConnectPtr conn,
         if (teardowncgroup && qemuTeardownHostdevCgroup(vm, hostdev) < 0)
             VIR_WARN("Unable to remove host device cgroup ACL on hotplug fail");
         if (teardownlabel &&
-            virSecurityManagerRestoreHostdevLabel(driver->securityManager,
-                                                  vm->def, hostdev, NULL) < 0)
+            qemuSecurityRestoreHostdevLabel(driver, vm, hostdev) < 0)
             VIR_WARN("Unable to restore host device labelling on hotplug fail");
+        if (teardowndevice &&
+            qemuDomainNamespaceTeardownHostdev(driver, vm, hostdev) < 0)
+            VIR_WARN("Unable to remove host device from /dev");
     }
     VIR_FREE(drivealias);
     VIR_FREE(drvstr);
@@ -3651,13 +3669,15 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver,
     virDomainAuditHostdev(vm, hostdev, "detach", true);
 
     if (!is_vfio &&
-        virSecurityManagerRestoreHostdevLabel(driver->securityManager,
-                                              vm->def, hostdev, NULL) < 0)
+        qemuSecurityRestoreHostdevLabel(driver, vm, hostdev) < 0)
         VIR_WARN("Failed to restore host device labelling");
 
     if (qemuTeardownHostdevCgroup(vm, hostdev) < 0)
         VIR_WARN("Failed to remove host device cgroup ACL");
 
+    if (qemuDomainNamespaceTeardownHostdev(driver, vm, hostdev) < 0)
+        VIR_WARN("Unable to remove host device from /dev");
+
     switch ((virDomainHostdevSubsysType) hostdev->source.subsys.type) {
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
         qemuDomainRemovePCIHostDevice(driver, vm, hostdev);
diff --git a/src/qemu/qemu_security.c b/src/qemu/qemu_security.c
index f800d9b..de616e8 100644
--- a/src/qemu/qemu_security.c
+++ b/src/qemu/qemu_security.c
@@ -168,3 +168,41 @@ qemuSecurityRestoreDiskLabel(virQEMUDriverPtr driver,
                                               vm->def,
                                               disk);
 }
+
+
+int
+qemuSecuritySetHostdevLabel(virQEMUDriverPtr driver,
+                            virDomainObjPtr vm,
+                            virDomainHostdevDefPtr hostdev)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    if (priv->containerized) {
+        /* Already handled by namespace code. */
+        return 0;
+    }
+
+    return virSecurityManagerSetHostdevLabel(driver->securityManager,
+                                             vm->def,
+                                             hostdev,
+                                             NULL);
+}
+
+
+int
+qemuSecurityRestoreHostdevLabel(virQEMUDriverPtr driver,
+                                virDomainObjPtr vm,
+                                virDomainHostdevDefPtr hostdev)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    if (priv->containerized) {
+        /* Already handled by namespace code. */
+        return 0;
+    }
+
+    return virSecurityManagerRestoreHostdevLabel(driver->securityManager,
+                                                 vm->def,
+                                                 hostdev,
+                                                 NULL);
+}
diff --git a/src/qemu/qemu_security.h b/src/qemu/qemu_security.h
index e3324ca..cc373b3 100644
--- a/src/qemu/qemu_security.h
+++ b/src/qemu/qemu_security.h
@@ -44,4 +44,12 @@ int qemuSecuritySetDiskLabel(virQEMUDriverPtr driver,
 int qemuSecurityRestoreDiskLabel(virQEMUDriverPtr driver,
                                  virDomainObjPtr vm,
                                  virDomainDiskDefPtr disk);
+
+int qemuSecuritySetHostdevLabel(virQEMUDriverPtr driver,
+                                virDomainObjPtr vm,
+                                virDomainHostdevDefPtr hostdev);
+
+int qemuSecurityRestoreHostdevLabel(virQEMUDriverPtr driver,
+                                    virDomainObjPtr vm,
+                                    virDomainHostdevDefPtr hostdev);
 #endif /* __QEMU_SECURITY_H__ */
-- 
2.8.4




More information about the libvir-list mailing list