| 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 |
| |
| |
| @@ -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 |
| |