Blob Blame History Raw
From 676f5e9952f7cbc09ad54d77e09d864f61874a6d Mon Sep 17 00:00:00 2001
Message-Id: <676f5e9952f7cbc09ad54d77e09d864f61874a6d.1382534060.git.jdenemar@redhat.com>
From: Peter Krempa <pkrempa@redhat.com>
Date: Thu, 10 Oct 2013 13:56:36 +0200
Subject: [PATCH] qemu: hostdev: Add checks if PCI passthrough is available in
 the host

https://bugzilla.redhat.com/show_bug.cgi?id=1001738

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.

(cherry picked from commit 467b561ac2ca7cb968d7a1d781e715cdd7bf3d14)

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
---
 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.4