[libvirt] [PATCHv4 0/3] Change preference of default PCI passthrough type to VFIO

Peter Krempa (3): qemu: hostdev: Fix function spacing and header formatting qemu: hostdev: Add checks if PCI passthrough is availabe in the host qemu: Prefer VFIO for PCI device passthrough docs/formatdomain.html.in | 9 +- src/conf/domain_conf.h | 2 +- src/qemu/qemu_command.c | 2 +- src/qemu/qemu_hostdev.c | 207 +++++++++++++++++++++++++++++++++++++++++----- src/qemu/qemu_hostdev.h | 4 +- src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_process.c | 15 ++-- tests/qemuxml2argvtest.c | 11 +++ 8 files changed, 217 insertions(+), 35 deletions(-) -- 1.8.3.2

--- Notes: New in this version. src/qemu/qemu_hostdev.c | 60 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 21fe47f..4127abd 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -85,6 +85,7 @@ qemuGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs) return list; } + /* * qemuGetActivePciHostDeviceList - make a new list with a *copy* of * every virPCIDevice object that is found on the activePciHostdevs @@ -128,8 +129,10 @@ qemuGetActivePciHostDeviceList(virQEMUDriverPtr driver, return list; } -int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, - virDomainDefPtr def) + +int +qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, + virDomainDefPtr def) { virDomainHostdevDefPtr hostdev = NULL; virPCIDevicePtr dev = NULL; @@ -188,6 +191,7 @@ cleanup: return ret; } + int qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, virDomainDefPtr def) @@ -274,8 +278,10 @@ cleanup: return ret; } + static int -qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path) +qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, + char **sysfs_path) { virPCIDeviceAddress config_address; @@ -287,6 +293,7 @@ qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path) return virPCIDeviceAddressGetSysfsFile(&config_address, sysfs_path); } + int qemuDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) { @@ -303,6 +310,7 @@ qemuDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) return ret; } + static int qemuDomainHostdevNetDevice(virDomainHostdevDefPtr hostdev, char **linkdev, int *vf) @@ -331,6 +339,7 @@ cleanup: return ret; } + static int qemuDomainHostdevNetConfigVirtPortProfile(const char *linkdev, int vf, virNetDevVPortProfilePtr virtPort, @@ -370,6 +379,7 @@ qemuDomainHostdevNetConfigVirtPortProfile(const char *linkdev, int vf, return ret; } + int qemuDomainHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev, const unsigned char *uuid, @@ -438,6 +448,7 @@ cleanup: return ret; } + int qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, char *stateDir) @@ -474,11 +485,13 @@ qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, return ret; } -int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, - const char *name, - const unsigned char *uuid, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) + +int +qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, + const char *name, + const unsigned char *uuid, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) { virPCIDeviceListPtr pcidevs; int last_processed_hostdev_vf = -1; @@ -670,6 +683,7 @@ cleanup: return ret; } + int qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, const char *name, @@ -722,6 +736,7 @@ error: return -1; } + int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, @@ -809,6 +824,7 @@ out: return 0; } + static int qemuPrepareHostUSBDevices(virQEMUDriverPtr driver, virDomainDefPtr def, @@ -878,6 +894,7 @@ cleanup: return ret; } + int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, const char *name, @@ -996,9 +1013,11 @@ cleanup: return -1; } -int qemuPrepareHostDevices(virQEMUDriverPtr driver, - virDomainDefPtr def, - bool coldBoot) + +int +qemuPrepareHostDevices(virQEMUDriverPtr driver, + virDomainDefPtr def, + bool coldBoot) { if (!def->nhostdevs) return 0; @@ -1022,7 +1041,8 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver, * Pre-condition: driver->inactivePciHostdevs & driver->activePciHostdevs * are locked */ -void qemuReattachPciDevice(virPCIDevicePtr dev, virQEMUDriverPtr driver) +void +qemuReattachPciDevice(virPCIDevicePtr dev, virQEMUDriverPtr driver) { int retries = 100; @@ -1052,10 +1072,11 @@ void qemuReattachPciDevice(virPCIDevicePtr dev, virQEMUDriverPtr driver) } -void qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) +void +qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, + const char *name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) { virPCIDeviceListPtr pcidevs; size_t i; @@ -1140,6 +1161,7 @@ cleanup: virObjectUnref(cfg); } + static void qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, const char *name, @@ -1203,6 +1225,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, virObjectUnlock(driver->activeUsbHostdevs); } + void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, @@ -1272,8 +1295,9 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, virObjectUnlock(driver->activeScsiHostdevs); } -void qemuDomainReAttachHostDevices(virQEMUDriverPtr driver, - virDomainDefPtr def) +void +qemuDomainReAttachHostDevices(virQEMUDriverPtr driver, + virDomainDefPtr def) { if (!def->nhostdevs) return; -- 1.8.3.2

On 10/08/2013 06:46 PM, Peter Krempa wrote:
---
Notes: New in this version.
src/qemu/qemu_hostdev.c | 60 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 18 deletions(-)
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 21fe47f..4127abd 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -85,6 +85,7 @@ qemuGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs) return list; }
+ /* * qemuGetActivePciHostDeviceList - make a new list with a *copy* of * every virPCIDevice object that is found on the activePciHostdevs @@ -128,8 +129,10 @@ qemuGetActivePciHostDeviceList(virQEMUDriverPtr driver, return list; }
-int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, - virDomainDefPtr def) + +int +qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, + virDomainDefPtr def) { virDomainHostdevDefPtr hostdev = NULL; virPCIDevicePtr dev = NULL; @@ -188,6 +191,7 @@ cleanup: return ret; }
+ int qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, virDomainDefPtr def) @@ -274,8 +278,10 @@ cleanup: return ret; }
+ static int -qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path) +qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, + char **sysfs_path) { virPCIDeviceAddress config_address;
@@ -287,6 +293,7 @@ qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path) return virPCIDeviceAddressGetSysfsFile(&config_address, sysfs_path); }
+ int qemuDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) { @@ -303,6 +310,7 @@ qemuDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) return ret; }
+ static int qemuDomainHostdevNetDevice(virDomainHostdevDefPtr hostdev, char **linkdev, int *vf) @@ -331,6 +339,7 @@ cleanup: return ret; }
+ static int qemuDomainHostdevNetConfigVirtPortProfile(const char *linkdev, int vf, virNetDevVPortProfilePtr virtPort, @@ -370,6 +379,7 @@ qemuDomainHostdevNetConfigVirtPortProfile(const char *linkdev, int vf, return ret; }
+ int qemuDomainHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev, const unsigned char *uuid, @@ -438,6 +448,7 @@ cleanup: return ret; }
+ int qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, char *stateDir) @@ -474,11 +485,13 @@ qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, return ret; }
-int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, - const char *name, - const unsigned char *uuid, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) + +int +qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, + const char *name, + const unsigned char *uuid, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) { virPCIDeviceListPtr pcidevs; int last_processed_hostdev_vf = -1; @@ -670,6 +683,7 @@ cleanup: return ret; }
+ int qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, const char *name, @@ -722,6 +736,7 @@ error: return -1; }
+ int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, @@ -809,6 +824,7 @@ out: return 0; }
+ static int qemuPrepareHostUSBDevices(virQEMUDriverPtr driver, virDomainDefPtr def, @@ -878,6 +894,7 @@ cleanup: return ret; }
+ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, const char *name, @@ -996,9 +1013,11 @@ cleanup: return -1; }
-int qemuPrepareHostDevices(virQEMUDriverPtr driver, - virDomainDefPtr def, - bool coldBoot) + +int +qemuPrepareHostDevices(virQEMUDriverPtr driver, + virDomainDefPtr def, + bool coldBoot) { if (!def->nhostdevs) return 0; @@ -1022,7 +1041,8 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver, * Pre-condition: driver->inactivePciHostdevs & driver->activePciHostdevs * are locked */ -void qemuReattachPciDevice(virPCIDevicePtr dev, virQEMUDriverPtr driver) +void +qemuReattachPciDevice(virPCIDevicePtr dev, virQEMUDriverPtr driver) { int retries = 100;
@@ -1052,10 +1072,11 @@ void qemuReattachPciDevice(virPCIDevicePtr dev, virQEMUDriverPtr driver) }
-void qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) +void +qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, + const char *name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) { virPCIDeviceListPtr pcidevs; size_t i; @@ -1140,6 +1161,7 @@ cleanup: virObjectUnref(cfg); }
+ static void qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, const char *name, @@ -1203,6 +1225,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, virObjectUnlock(driver->activeUsbHostdevs); }
+ void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, @@ -1272,8 +1295,9 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, virObjectUnlock(driver->activeScsiHostdevs); }
-void qemuDomainReAttachHostDevices(virQEMUDriverPtr driver, - virDomainDefPtr def) +void +qemuDomainReAttachHostDevices(virQEMUDriverPtr driver, + virDomainDefPtr def) { if (!def->nhostdevs) return;
ACK

On 10/09/13 14:41, Laine Stump wrote:
On 10/08/2013 06:46 PM, Peter Krempa wrote:
---
Notes: New in this version.
src/qemu/qemu_hostdev.c | 60 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 18 deletions(-)
...
ACK
Pushed; Thanks. Peter

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

On 10/08/2013 06:46 PM, Peter Krempa wrote:
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();
I just double checked, and these two calls won't ever be done unnecessarily, because qemuPrepareHostdevPCIDevices() is never called unless there will be at least one hostdev added.
+ 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);
ACK.

If it's not pushed already, there's a typo in the subject: 'availabe' Christophe On Tue, Oct 08, 2013 at 05:46:57PM +0200, Peter Krempa wrote:
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
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 10/09/13 15:02, Christophe Fergeau wrote:
If it's not pushed already, there's a typo in the subject: 'availabe'
Thanks for noticing. Pushed with fixed subject.
Christophe
On Tue, Oct 08, 2013 at 05:46:57PM +0200, Peter Krempa wrote:
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(+)
Peter

Prefer using VFIO (if available) to the legacy KVM device passthrough. With this patch a PCI passthrough device without the driver configured will be started with VFIO if it's available on the host. If not legacy KVM passthrough is checked and error is reported if it's not available. --- Notes: Version 4: - Adapted to call tree change docs/formatdomain.html.in | 9 ++++----- src/conf/domain_conf.h | 2 +- src/qemu/qemu_command.c | 2 +- src/qemu/qemu_hostdev.c | 30 +++++++++++++++++++++++++----- src/qemu/qemu_hostdev.h | 4 +++- src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_process.c | 15 ++++++++------- tests/qemuxml2argvtest.c | 11 +++++++++++ 8 files changed, 54 insertions(+), 21 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 3689399..6f3f7cf 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2755,11 +2755,10 @@ backend, which is compatible with UEFI SecureBoot) or "kvm" (for the legacy device assignment handled directly by the KVM kernel module)<span class="since">Since 1.0.5 (QEMU and KVM - only, requires kernel 3.6 or newer)</span>. Currently, "kvm" - is the default used by libvirt when not explicitly provided, - but since the two are functionally equivalent, this default - could be changed in the future with no impact to domains that - don't specify anything. + only, requires kernel 3.6 or newer)</span>. The default, when + the driver name is not explicitly specified, is to check wether + VFIO is available and use it if it's the case. If VFIO is not + available, the legacy "kvm" assignment is attempted. </dd> <dt><code>readonly</code></dt> <dd>Indicates that the device is readonly, only supported by SCSI host diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index f20a916..6b825d8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -399,7 +399,7 @@ enum virDomainHostdevSubsysType { /* the backend driver used for PCI hostdev devices */ typedef enum { - VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, /* currently kvm, could change */ + VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, /* detect automaticaly, prefer VFIO */ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM, /* force legacy kvm style */ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO, /* force vfio */ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 52dc295..da53c51 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -5487,7 +5487,6 @@ qemuBuildPCIHostdevDevStr(virDomainDefPtr def, switch ((virDomainHostdevSubsysPciBackendType) dev->source.subsys.u.pci.backend) { - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: virBufferAddLit(&buf, "pci-assign"); if (configfd && *configfd) @@ -5498,6 +5497,7 @@ qemuBuildPCIHostdevDevStr(virDomainDefPtr def, virBufferAddLit(&buf, "vfio-pci"); break; + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("PCI passhthrough type needs to be specified")); diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 7f3170d..81e0e88 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -564,7 +564,8 @@ qemuHostdevHostSupportsPassthroughLegacy(void) static bool qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, - size_t nhostdevs) + size_t nhostdevs, + virQEMUCapsPtr qemuCaps) { bool supportsPassthroughKVM = qemuHostdevHostSupportsPassthroughLegacy(); bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); @@ -581,6 +582,23 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, continue; switch ((virDomainHostdevSubsysPciBackendType) *backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + if (supportsPassthroughVFIO && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) { + *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; + } else if (supportsPassthroughKVM && + (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIDEVICE) || + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))) { + *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("host doesn't support passthrough of " + "host PCI devices")); + return false; + } + + break; + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: if (!supportsPassthroughVFIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -589,7 +607,6 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, } break; - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: if (!supportsPassthroughKVM) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -613,7 +630,8 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const char *name, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, - int nhostdevs) + int nhostdevs, + virQEMUCapsPtr qemuCaps) { virPCIDeviceListPtr pcidevs; int last_processed_hostdev_vf = -1; @@ -621,7 +639,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, int ret = -1; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs)) + if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs, qemuCaps)) goto cleanup; virObjectLock(driver->activePciHostdevs); @@ -1142,13 +1160,15 @@ cleanup: int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, bool coldBoot) { if (!def->nhostdevs) return 0; if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, - def->hostdevs, def->nhostdevs) < 0) + def->hostdevs, def->nhostdevs, + qemuCaps) < 0) return -1; if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0) diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index 327d4d5..272086e 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -37,7 +37,8 @@ int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const char *name, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, - int nhostdevs); + int nhostdevs, + virQEMUCapsPtr qemuCaps); int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, virUSBDevicePtr *usb); @@ -50,6 +51,7 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int nhostdevs); int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, bool coldBoot); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 71c7822..b6ae218 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1140,7 +1140,7 @@ int qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, return -1; if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1) < 0) + &hostdev, 1, priv->qemuCaps) < 0) return -1; switch ((virDomainHostdevSubsysPciBackendType) backend) { diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 079f062..354e079 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3567,6 +3567,12 @@ int qemuProcessStart(virConnectPtr conn, goto cleanup; } + VIR_DEBUG("Determining emulator version"); + virObjectUnref(priv->qemuCaps); + if (!(priv->qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache, + vm->def->emulator))) + goto cleanup; + /* network devices must be "prepared" before hostdevs, because * setting up a network device might create a new hostdev that * will need to be setup. @@ -3577,7 +3583,8 @@ int qemuProcessStart(virConnectPtr conn, /* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); - if (qemuPrepareHostDevices(driver, vm->def, !migrateFrom) < 0) + if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, + !migrateFrom) < 0) goto cleanup; VIR_DEBUG("Preparing chr devices"); @@ -3659,12 +3666,6 @@ int qemuProcessStart(virConnectPtr conn, } } - VIR_DEBUG("Determining emulator version"); - virObjectUnref(priv->qemuCaps); - if (!(priv->qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache, - vm->def->emulator))) - goto cleanup; - if (!qemuValidateCpuMax(vm->def, priv->qemuCaps)) goto cleanup; diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 58165fd..92e7461 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -98,6 +98,7 @@ static int testCompareXMLToArgvFiles(const char *xml, virConnectPtr conn; char *log = NULL; virCommandPtr cmd = NULL; + size_t i; if (!(conn = virGetConnect())) goto out; @@ -154,6 +155,16 @@ static int testCompareXMLToArgvFiles(const char *xml, if (qemuAssignDeviceAliases(vmdef, extraFlags) < 0) goto out; + for (i = 0; i < vmdef->nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = vmdef->hostdevs[i]; + + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && + hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) { + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + } + } + if (!(cmd = qemuBuildCommandLine(conn, &driver, vmdef, &monitor_chr, (flags & FLAG_JSON), extraFlags, migrateFrom, migrateFd, NULL, -- 1.8.3.2

On 10/08/2013 06:46 PM, Peter Krempa wrote:
Prefer using VFIO (if available) to the legacy KVM device passthrough.
With this patch a PCI passthrough device without the driver configured will be started with VFIO if it's available on the host. If not legacy KVM passthrough is checked and error is reported if it's not available. ---
Notes: Version 4: - Adapted to call tree change
docs/formatdomain.html.in | 9 ++++----- src/conf/domain_conf.h | 2 +- src/qemu/qemu_command.c | 2 +- src/qemu/qemu_hostdev.c | 30 +++++++++++++++++++++++++----- src/qemu/qemu_hostdev.h | 4 +++- src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_process.c | 15 ++++++++------- tests/qemuxml2argvtest.c | 11 +++++++++++ 8 files changed, 54 insertions(+), 21 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 3689399..6f3f7cf 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2755,11 +2755,10 @@ backend, which is compatible with UEFI SecureBoot) or "kvm" (for the legacy device assignment handled directly by the KVM kernel module)<span class="since">Since 1.0.5 (QEMU and KVM - only, requires kernel 3.6 or newer)</span>. Currently, "kvm" - is the default used by libvirt when not explicitly provided, - but since the two are functionally equivalent, this default - could be changed in the future with no impact to domains that - don't specify anything. + only, requires kernel 3.6 or newer)</span>. The default, when + the driver name is not explicitly specified, is to check wether + VFIO is available and use it if it's the case. If VFIO is not + available, the legacy "kvm" assignment is attempted. </dd> <dt><code>readonly</code></dt> <dd>Indicates that the device is readonly, only supported by SCSI host diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index f20a916..6b825d8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -399,7 +399,7 @@ enum virDomainHostdevSubsysType {
/* the backend driver used for PCI hostdev devices */ typedef enum { - VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, /* currently kvm, could change */ + VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, /* detect automaticaly, prefer VFIO */ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM, /* force legacy kvm style */ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO, /* force vfio */
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 52dc295..da53c51 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -5487,7 +5487,6 @@ qemuBuildPCIHostdevDevStr(virDomainDefPtr def,
switch ((virDomainHostdevSubsysPciBackendType) dev->source.subsys.u.pci.backend) { - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: virBufferAddLit(&buf, "pci-assign"); if (configfd && *configfd) @@ -5498,6 +5497,7 @@ qemuBuildPCIHostdevDevStr(virDomainDefPtr def, virBufferAddLit(&buf, "vfio-pci"); break;
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("PCI passhthrough type needs to be specified"));
I think this error message would be misleading to a user. What they would think is "Oh, I need to specify the 'PCI passthrough type'; I wonder what *that* is..." (since that's not what it's called in the XML). But what it really means is "There is an error in the code - u.pci.backend should have been set to either KVM or VFIO by the functions that called here; please report this as a bug!". As the error is right now, I think it would do as much damage as good - maybe you could change it to "Unable to determine whether to use VFIO or legacy KVM driver for PCI device assignment". (or something else that avoids saying "needs to be specified").
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 7f3170d..81e0e88 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -564,7 +564,8 @@ qemuHostdevHostSupportsPassthroughLegacy(void)
static bool qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, - size_t nhostdevs) + size_t nhostdevs, + virQEMUCapsPtr qemuCaps) { bool supportsPassthroughKVM = qemuHostdevHostSupportsPassthroughLegacy(); bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); @@ -581,6 +582,23 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, continue;
switch ((virDomainHostdevSubsysPciBackendType) *backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + if (supportsPassthroughVFIO && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) { + *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; + } else if (supportsPassthroughKVM && + (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIDEVICE) || + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))) { + *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM;
BTW, thinking about your response to me pointing out that this is changing the output of a --live dumpxml, I think you're right - it's probably a *good* thing that what was chosen is output in dumpxml.
+ } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("host doesn't support passthrough of " + "host PCI devices")); + return false; + } + + break; + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: if (!supportsPassthroughVFIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -589,7 +607,6 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, } break;
- case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: if (!supportsPassthroughKVM) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -613,7 +630,8 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const char *name, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, - int nhostdevs) + int nhostdevs, + virQEMUCapsPtr qemuCaps) { virPCIDeviceListPtr pcidevs; int last_processed_hostdev_vf = -1; @@ -621,7 +639,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, int ret = -1; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
- if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs)) + if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs, qemuCaps)) goto cleanup;
virObjectLock(driver->activePciHostdevs); @@ -1142,13 +1160,15 @@ cleanup: int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, bool coldBoot) { if (!def->nhostdevs) return 0;
if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, - def->hostdevs, def->nhostdevs) < 0) + def->hostdevs, def->nhostdevs, + qemuCaps) < 0) return -1;
if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0) diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index 327d4d5..272086e 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -37,7 +37,8 @@ int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const char *name, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, - int nhostdevs); + int nhostdevs, + virQEMUCapsPtr qemuCaps); int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, virUSBDevicePtr *usb); @@ -50,6 +51,7 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int nhostdevs); int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, bool coldBoot); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 71c7822..b6ae218 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1140,7 +1140,7 @@ int qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, return -1;
if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1) < 0) + &hostdev, 1, priv->qemuCaps) < 0) return -1;
switch ((virDomainHostdevSubsysPciBackendType) backend) { diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 079f062..354e079 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3567,6 +3567,12 @@ int qemuProcessStart(virConnectPtr conn, goto cleanup; }
+ VIR_DEBUG("Determining emulator version"); + virObjectUnref(priv->qemuCaps); + if (!(priv->qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache, + vm->def->emulator))) + goto cleanup; + /* network devices must be "prepared" before hostdevs, because * setting up a network device might create a new hostdev that * will need to be setup. @@ -3577,7 +3583,8 @@ int qemuProcessStart(virConnectPtr conn,
/* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); - if (qemuPrepareHostDevices(driver, vm->def, !migrateFrom) < 0) + if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, + !migrateFrom) < 0) goto cleanup;
VIR_DEBUG("Preparing chr devices"); @@ -3659,12 +3666,6 @@ int qemuProcessStart(virConnectPtr conn, } }
- VIR_DEBUG("Determining emulator version"); - virObjectUnref(priv->qemuCaps); - if (!(priv->qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache, - vm->def->emulator))) - goto cleanup; - if (!qemuValidateCpuMax(vm->def, priv->qemuCaps)) goto cleanup;
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 58165fd..92e7461 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -98,6 +98,7 @@ static int testCompareXMLToArgvFiles(const char *xml, virConnectPtr conn; char *log = NULL; virCommandPtr cmd = NULL; + size_t i;
if (!(conn = virGetConnect())) goto out; @@ -154,6 +155,16 @@ static int testCompareXMLToArgvFiles(const char *xml, if (qemuAssignDeviceAliases(vmdef, extraFlags) < 0) goto out;
+ for (i = 0; i < vmdef->nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = vmdef->hostdevs[i]; + + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && + hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) { + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + } + } + if (!(cmd = qemuBuildCommandLine(conn, &driver, vmdef, &monitor_chr, (flags & FLAG_JSON), extraFlags, migrateFrom, migrateFd, NULL,
ACk, with a less misleading error message.

On 10/09/13 16:01, Laine Stump wrote:
On 10/08/2013 06:46 PM, Peter Krempa wrote:
Prefer using VFIO (if available) to the legacy KVM device passthrough.
With this patch a PCI passthrough device without the driver configured will be started with VFIO if it's available on the host. If not legacy KVM passthrough is checked and error is reported if it's not available. ---
Notes: Version 4: - Adapted to call tree change
docs/formatdomain.html.in | 9 ++++----- src/conf/domain_conf.h | 2 +- src/qemu/qemu_command.c | 2 +- src/qemu/qemu_hostdev.c | 30 +++++++++++++++++++++++++----- src/qemu/qemu_hostdev.h | 4 +++- src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_process.c | 15 ++++++++------- tests/qemuxml2argvtest.c | 11 +++++++++++ 8 files changed, 54 insertions(+), 21 deletions(-)
....
ACk, with a less misleading error message.
I've changed the error message to "_("invalid PCI passthrough type '%s'")" and pushed. Thanks for the review. Peter
participants (3)
-
Christophe Fergeau
-
Laine Stump
-
Peter Krempa