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

Peter Krempa pkrempa at redhat.com
Tue Sep 24 13:10:06 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 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




More information about the libvir-list mailing list