[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCHv2 2/3] qemu: hostdev: Add checks if PCI passthrough is availabe in the host



Add code to check availability of PCI passhthrough using VFIO and the
legacy KVM passthrough and use it when starting VMs and hotplugging
devices to live machine.
---

Notes:
    Version 2:
    - Merge of multiple patches from v1 posting
    - changed +qemuHostdevHostSupportsPassthrough*() to static
    - added function splitting out common parts of the code between machine start and hotplug

 src/qemu/qemu_hostdev.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_hostdev.h |   4 ++
 src/qemu/qemu_hotplug.c |   4 ++
 src/qemu/qemu_process.c |   4 ++
 4 files changed, 138 insertions(+)

diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index 21fe47f..dbbc2b4 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -23,6 +23,11 @@

 #include <config.h>

+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
 #include "qemu_hostdev.h"
 #include "virlog.h"
 #include "virerror.h"
@@ -31,6 +36,7 @@
 #include "virusb.h"
 #include "virscsi.h"
 #include "virnetdev.h"
+#include "virfile.h"

 #define VIR_FROM_THIS VIR_FROM_QEMU

@@ -1287,3 +1293,123 @@ void qemuDomainReAttachHostDevices(virQEMUDriverPtr driver,
     qemuDomainReAttachHostScsiDevices(driver, def->name, def->hostdevs,
                                       def->nhostdevs);
 }
+
+
+static bool
+qemuHostdevHostSupportsPassthroughVFIO(void)
+{
+    DIR *iommuDir = NULL;
+    struct dirent *iommuGroup = NULL;
+    bool ret = false;
+
+    /* condition 1 - /sys/kernel/iommu_groups/ contains entries */
+    if (!(iommuDir = opendir("/sys/kernel/iommu_groups/")))
+        goto cleanup;
+
+    while ((iommuGroup = readdir(iommuDir))) {
+        /* skip ./ ../ */
+        if (STRPREFIX(iommuGroup->d_name, "."))
+            continue;
+
+        /* assume we found a group */
+        break;
+    }
+
+    if (!iommuGroup)
+        goto cleanup;
+    /* okay, iommu is on and recognizes groups */
+
+    /* condition 2 - /dev/vfio/vfio exists */
+    if (!virFileExists("/dev/vfio/vfio"))
+        goto cleanup;
+
+    ret = true;
+
+cleanup:
+    if (iommuDir)
+        closedir(iommuDir);
+
+    return ret;
+}
+
+
+#if HAVE_LINUX_KVM_H
+# include <linux/kvm.h>
+static bool
+qemuHostdevHostSupportsPassthroughLegacy(void)
+{
+    int kvmfd = -1;
+    bool ret = false;
+
+    if ((kvmfd = open("/dev/kvm", O_RDONLY)) < 0)
+        goto cleanup;
+
+# ifdef KVM_CAP_IOMMU
+    if ((ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_IOMMU)) <= 0)
+        goto cleanup;
+
+    ret = true;
+# endif
+
+cleanup:
+    VIR_FORCE_CLOSE(kvmfd);
+
+    return ret;
+}
+#else
+static bool
+qemuHostdevHostSupportsPassthroughLegacy(void)
+{
+    return false;
+}
+#endif
+
+bool
+qemuHostdevHostVerifySupport(virDomainHostdevDefPtr *hostdevs,
+                             size_t nhostdevs)
+{
+    int supportsPassthroughKVM = -1;
+    int supportsPassthroughVFIO = -1;
+    size_t i;
+
+    /* assign defaults for hostdev passthrough */
+    for (i = 0; i < nhostdevs; i++) {
+        virDomainHostdevDefPtr hostdev = hostdevs[i];
+
+        if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+            hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
+            int *backend = &hostdev->source.subsys.u.pci.backend;
+
+            /* cache host state of passthrough support */
+            if (supportsPassthroughKVM == -1 || supportsPassthroughVFIO == -1) {
+                supportsPassthroughKVM = qemuHostdevHostSupportsPassthroughLegacy();
+                supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO();
+            }
+
+            switch ((virDomainHostdevSubsysPciBackendType) *backend) {
+            case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO:
+                if (!supportsPassthroughVFIO) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                                   _("host doesn't support VFIO PCI passthrough"));
+                    return false;
+                }
+                break;
+
+            case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
+            case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM:
+                if (!supportsPassthroughKVM) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                                   _("host doesn't support legacy PCI passthrough"));
+                    return false;
+                }
+
+                break;
+
+            case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST:
+                break;
+            }
+        }
+    }
+
+    return true;
+}
diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h
index 327d4d5..6d88830 100644
--- a/src/qemu/qemu_hostdev.h
+++ b/src/qemu/qemu_hostdev.h
@@ -69,4 +69,8 @@ int qemuDomainHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev,
 int qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev,
                                       char *stateDir);

+bool qemuHostdevHostVerifySupport(virDomainHostdevDefPtr *hostdevs,
+                                  size_t nhostdevs);
+
+
 #endif /* __QEMU_HOSTDEV_H__ */
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 728c734..604a0ed 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -1143,6 +1143,10 @@ int qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver,
                                      &hostdev, 1) < 0)
         return -1;

+    /* verify the availability of passthrough support */
+    if (!qemuHostdevHostVerifySupport(&hostdev, 1))
+        goto error;
+
     switch ((virDomainHostdevSubsysPciBackendType) *backend) {
     case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO:
         if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) {
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index dd16f6c..baf021f 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3713,6 +3713,10 @@ int qemuProcessStart(virConnectPtr conn,
         goto cleanup;
     }

+    /* check and assign device assignment settings */
+    if (!qemuHostdevHostVerifySupport(vm->def->hostdevs, vm->def->nhostdevs))
+        goto cleanup;
+
     /*
      * Normally PCI addresses are assigned in the virDomainCreate
      * or virDomainDefine methods. We might still need to assign
-- 
1.8.3.2


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]