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

Peter Krempa pkrempa at redhat.com
Tue Oct 8 15:46:57 UTC 2013


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 4:
    - moved this function so that it can be called from qemuPrepareHostdevPCIDevices()
    - now caching the support for passthrough types right away

 src/qemu/qemu_hostdev.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 125 insertions(+)

diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index 4127abd..7f3170d 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

@@ -486,6 +492,122 @@ qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev,
 }


+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
+
+
+static bool
+qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs,
+                                  size_t nhostdevs)
+{
+    bool supportsPassthroughKVM = qemuHostdevHostSupportsPassthroughLegacy();
+    bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO();
+    size_t i;
+
+    /* assign defaults for hostdev passthrough */
+    for (i = 0; i < nhostdevs; i++) {
+        virDomainHostdevDefPtr hostdev = hostdevs[i];
+        int *backend = &hostdev->source.subsys.u.pci.backend;
+
+        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+            continue;
+        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+            continue;
+
+        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;
+}
+
+
 int
 qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver,
                              const char *name,
@@ -499,6 +621,9 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver,
     int ret = -1;
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);

+    if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs))
+        goto cleanup;
+
     virObjectLock(driver->activePciHostdevs);
     virObjectLock(driver->inactivePciHostdevs);

-- 
1.8.3.2




More information about the libvir-list mailing list