Use real device MSIs to deliver interrupts into the guest in vfio_pci_device_irq_test (when possible). If the device has a driver, we can use the driver to make the device send a physical MSI, rather than synthesizing an eventfd notification in software. This allows this test to exercise IRQ Bypass (e.g. VT-d device-posted interrupts in Intel).
Signed-off-by: David Matlack dmatlack@google.com --- .../selftests/kvm/vfio_pci_device_irq_test.c | 66 +++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-)
diff --git a/tools/testing/selftests/kvm/vfio_pci_device_irq_test.c b/tools/testing/selftests/kvm/vfio_pci_device_irq_test.c index c792fc169028..8cbaf38357c4 100644 --- a/tools/testing/selftests/kvm/vfio_pci_device_irq_test.c +++ b/tools/testing/selftests/kvm/vfio_pci_device_irq_test.c @@ -7,6 +7,8 @@ #include <pthread.h> #include <time.h> #include <linux/vfio.h> +#include <linux/sizes.h> + #include <vfio_util.h>
static bool guest_ready_for_irq; @@ -60,11 +62,54 @@ void *vcpu_thread_main(void *arg)
static void help(const char *name) { - printf("Usage: %s [-i iommu_mode] segment:bus:device.function\n", name); + printf("Usage: %s [-i iommu_mode] [-d] segment:bus:device.function\n", name); + printf(" -d: Send a real MSI from the device, rather than synthesizing\n" + " an eventfd signal from VFIO. Note that this option requires\n" + " a VFIO selftests driver that supports the device.\n"); iommu_mode_help("-i"); exit(1); }
+static int setup_msi(struct vfio_pci_device *device, bool use_device_msi) +{ + const int flags = MAP_SHARED | MAP_ANONYMOUS; + const int prot = PROT_READ | PROT_WRITE; + struct vfio_dma_region *region; + + if (use_device_msi) { + /* A driver is required to generate an MSI. */ + TEST_REQUIRE(device->driver.ops); + + /* Set up a DMA-able region for the driver to use. */ + region = &device->driver.region; + region->iova = 0; + region->size = SZ_2M; + region->vaddr = mmap(NULL, region->size, prot, flags, -1, 0); + TEST_ASSERT(region->vaddr != MAP_FAILED, "mmap() failed\n"); + vfio_pci_dma_map(device, region); + + vfio_pci_driver_init(device); + + return device->driver.msi; + } + + TEST_REQUIRE(device->msix_info.count > 0); + vfio_pci_msix_enable(device, 0, 1); + return 0; +} + +static void send_msi(struct vfio_pci_device *device, bool use_device_msi, int msi) +{ + if (use_device_msi) { + printf("Sending MSI %d from the device\n", msi); + TEST_ASSERT_EQ(msi, device->driver.msi); + vfio_pci_driver_send_msi(device); + } else { + printf("Notifying the eventfd for MSI %d from VFIO\n", msi); + vfio_pci_irq_trigger(device, VFIO_PCI_MSIX_IRQ_INDEX, msi); + } +} + int main(int argc, char **argv) { /* Random non-reserved vector and GSI to use for the device IRQ */ @@ -74,16 +119,21 @@ int main(int argc, char **argv) struct timespec start, elapsed; struct vfio_pci_device *device; const char *iommu_mode = NULL; + bool use_device_msi = false; struct kvm_vcpu *vcpu; pthread_t vcpu_thread; struct kvm_vm *vm; + int msi; int c;
- while ((c = getopt(argc, argv, "i:")) != -1) { + while ((c = getopt(argc, argv, "i:d")) != -1) { switch (c) { case 'i': iommu_mode = optarg; break; + case 'd': + use_device_msi = true; + break; default: help(argv[0]); } @@ -96,10 +146,9 @@ int main(int argc, char **argv) vm_install_exception_handler(vm, vector, guest_irq_handler);
device = vfio_pci_device_init(argv[optind], iommu_mode); - TEST_REQUIRE(device->msix_info.count > 0); + msi = setup_msi(device, use_device_msi);
- vfio_pci_msix_enable(device, 0, 1); - kvm_add_irqfd(vm, gsi, device->msi_eventfds[0]); + kvm_add_irqfd(vm, gsi, device->msi_eventfds[msi]); kvm_route_msi(vm, gsi, vcpu, vector);
pthread_create(&vcpu_thread, NULL, vcpu_thread_main, vcpu); @@ -107,12 +156,7 @@ int main(int argc, char **argv) while (!READ_ONCE(guest_ready_for_irq)) sync_global_from_guest(vm, guest_ready_for_irq);
- /* - * TODO: Get the device to send a physical MSI to exercise IRQ Bypass - * (e.g. VT-d on Intel), rather than manually synthesizing a - * notification from VFIO. - */ - vfio_pci_irq_trigger(device, VFIO_PCI_MSIX_IRQ_INDEX, 0); + send_msi(device, use_device_msi, msi);
clock_gettime(CLOCK_MONOTONIC, &start);