From: Josh Hilke <jrhilke@google.com>
To: Paolo Bonzini <pbonzini@redhat.com>,
Sean Christopherson <seanjc@google.com>
Cc: kvm@vger.kernel.org, David Matlack <dmatlack@google.com>,
Alex Williamson <alex@shazbot.org>,
Josh Hilke <jrhilke@google.com>
Subject: [PATCH v2 03/14] KVM: selftests: Add vfio_pci_irq_test
Date: Tue, 31 Mar 2026 19:40:22 +0000 [thread overview]
Message-ID: <20260331194033.3890309-4-jrhilke@google.com> (raw)
In-Reply-To: <20260331194033.3890309-1-jrhilke@google.com>
From: David Matlack <dmatlack@google.com>
Add the initial implementation of vfio_pci_irq_test, which routes and
delivers an MSI from a vfio-pci device into a guest. This establishes
the core test infrastructure, including the vCPU thread loop and the
basic IRQ triggering mechanism using irqfd.
Suggested-by: Sean Christopherson <seanjc@google.com>
Link: https://lore.kernel.org/kvm/20250404193923.1413163-68-seanjc@google.com/
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
Co-developed-by: Josh Hilke <jrhilke@google.com>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../testing/selftests/kvm/vfio_pci_irq_test.c | 217 ++++++++++++++++++
2 files changed, 218 insertions(+)
create mode 100644 tools/testing/selftests/kvm/vfio_pci_irq_test.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index d8f77e181e8e..4220edaa466a 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -156,6 +156,7 @@ TEST_GEN_PROGS_x86 += rseq_test
TEST_GEN_PROGS_x86 += steal_time
TEST_GEN_PROGS_x86 += system_counter_offset_test
TEST_GEN_PROGS_x86 += pre_fault_memory_test
+TEST_GEN_PROGS_x86 += vfio_pci_irq_test
# Compiled outputs used by test targets
TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test
diff --git a/tools/testing/selftests/kvm/vfio_pci_irq_test.c b/tools/testing/selftests/kvm/vfio_pci_irq_test.c
new file mode 100644
index 000000000000..6f40b3c2b985
--- /dev/null
+++ b/tools/testing/selftests/kvm/vfio_pci_irq_test.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "kvm_util.h"
+#include "test_util.h"
+#include "irq_util.h"
+#include "apic.h"
+#include "processor.h"
+
+#include <stdint.h>
+#include <pthread.h>
+#include <ctype.h>
+#include <time.h>
+#include <linux/vfio.h>
+#include <linux/sizes.h>
+#include <sys/sysinfo.h>
+
+#include <libvfio.h>
+
+static bool done;
+
+static bool guest_ready_for_irqs[KVM_MAX_VCPUS];
+static bool guest_received_irq[KVM_MAX_VCPUS];
+#define TIMEOUT_NS (2ULL * 1000 * 1000 * 1000)
+
+static u32 guest_get_vcpu_id(void)
+{
+ return x2apic_read_reg(APIC_ID);
+}
+
+static void guest_irq_handler(struct ex_regs *regs)
+{
+ WRITE_ONCE(guest_received_irq[guest_get_vcpu_id()], true);
+
+ x2apic_write_reg(APIC_EOI, 0);
+}
+
+static void guest_code(void)
+{
+ x2apic_enable();
+
+ sti_nop();
+
+ WRITE_ONCE(guest_ready_for_irqs[guest_get_vcpu_id()], true);
+
+ while (!READ_ONCE(done)) {
+ cpu_relax();
+ }
+
+ GUEST_DONE();
+}
+
+static void *vcpu_thread_main(void *arg)
+{
+ struct kvm_vcpu *vcpu = arg;
+ struct ucall uc;
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_EQ(UCALL_DONE, get_ucall(vcpu, &uc));
+
+ return NULL;
+}
+
+static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu,
+ u8 vector)
+{
+ u8 buf[sizeof(struct kvm_irq_routing) + sizeof(struct kvm_irq_routing_entry)] = {};
+ struct kvm_irq_routing *routes = (void *)&buf;
+
+ routes->nr = 1;
+ routes->entries[0].gsi = gsi;
+ routes->entries[0].type = KVM_IRQ_ROUTING_MSI;
+ routes->entries[0].u.msi.address_lo = 0xFEE00000 | (vcpu->id << 12);
+ routes->entries[0].u.msi.data = vector;
+
+ vm_ioctl(vm, KVM_SET_GSI_ROUTING, routes);
+}
+
+static int setup_msi(struct vfio_pci_device *device)
+{
+ 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, int msi)
+{
+ vfio_pci_irq_trigger(device, VFIO_PCI_MSIX_IRQ_INDEX, msi);
+}
+
+static void help(const char *name)
+{
+ printf("Usage: %s [-h] segment:bus:device.function\n",
+ name);
+ printf("\n");
+ printf("\n");
+ exit(KSFT_FAIL);
+}
+
+int main(int argc, char **argv)
+{
+ /*
+ * Pick a random vector and a random GSI to use for device IRQ.
+ *
+ * Pick an IRQ vector in range [32, UINT8_MAX]. Min value is 32 because
+ * Linux/x86 reserves vectors 0-31 for exceptions and architecture
+ * defined NMIs and interrupts.
+ *
+ * Pick a GSI in range [24, KVM_MAX_IRQ_ROUTES - 1]. The min value is 24
+ * because KVM reserves GSIs 0-15 for legacy ISA IRQs and 16-23 only go
+ * to the IOAPIC. The max is KVM_MAX_IRQ_ROUTES - 1, because
+ * KVM_MAX_IRQ_ROUTES is exclusive.
+ */
+ u32 gsi = 24 + rand() % (KVM_MAX_IRQ_ROUTES - 1 - 24);
+ u8 vector = 32 + rand() % (UINT8_MAX - 32);
+
+ /* Test configuration (overridable by command line flags). */
+ int nr_irqs = 1000;
+ int nr_vcpus = 1;
+
+ struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
+ pthread_t vcpu_threads[KVM_MAX_VCPUS];
+ u64 irq_count, pin_count, piw_count;
+ struct vfio_pci_device *device;
+ struct iommu *iommu;
+ const char *device_bdf;
+ int i, j, c, msi, irq;
+ struct kvm_vm *vm;
+
+ device_bdf = vfio_selftests_get_bdf(&argc, argv);
+
+ while ((c = getopt(argc, argv, "h")) != -1) {
+ switch (c) {
+ case 'h':
+ default:
+ help(argv[0]);
+ }
+ }
+
+ vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
+ vm_install_exception_handler(vm, vector, guest_irq_handler);
+
+ iommu = iommu_init(default_iommu_mode);
+ device = vfio_pci_device_init(device_bdf, iommu);
+ msi = setup_msi(device);
+ irq = get_irq_number(device_bdf, msi);
+
+ irq_count = get_irq_count(irq);
+ pin_count = get_irq_count_by_name("PIN:");
+ piw_count = get_irq_count_by_name("PIW:");
+
+ printf("%s %s MSI-X[%d] (IRQ-%d) %d times\n",
+ "Notifying the eventfd for",
+ device_bdf, msi, irq, nr_irqs);
+
+ kvm_assign_irqfd(vm, gsi, device->msi_eventfds[msi]);
+
+ for (i = 0; i < nr_vcpus; i++)
+ pthread_create(&vcpu_threads[i], NULL, vcpu_thread_main, vcpus[i]);
+
+ for (i = 0; i < nr_vcpus; i++) {
+ struct kvm_vcpu *vcpu = vcpus[i];
+
+ while (!READ_FROM_GUEST(vm, guest_ready_for_irqs[vcpu->id]))
+ continue;
+ }
+
+ /* Set a consistent seed so that test are repeatable. */
+ srand(0);
+
+ for (i = 0; i < nr_irqs; i++) {
+ struct kvm_vcpu *vcpu = vcpus[i % nr_vcpus];
+ struct timespec start;
+
+ kvm_route_msi(vm, gsi, vcpu, vector);
+
+ for (j = 0; j < nr_vcpus; j++) {
+ TEST_ASSERT(
+ !READ_FROM_GUEST(vm, guest_received_irq[vcpu->id]),
+ "IRQ flag for vCPU %d not clear prior to test",
+ vcpu->id);
+ }
+
+ send_msi(device, msi);
+
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ for (;;) {
+ if (READ_FROM_GUEST(vm, guest_received_irq[vcpu->id]))
+ break;
+
+ if (timespec_to_ns(timespec_elapsed(start)) > TIMEOUT_NS) {
+ printf("Timeout waiting for interrupt!\n");
+ printf(" vCPU: %d\n", vcpu->id);
+
+ TEST_FAIL("vCPU never received IRQ!\n");
+ }
+ }
+
+ WRITE_TO_GUEST(vm, guest_received_irq[vcpu->id], false);
+ }
+
+ WRITE_TO_GUEST(vm, done, true);
+
+ for (i = 0; i < nr_vcpus; i++) {
+ pthread_join(vcpu_threads[i], NULL);
+ }
+
+ printf("Host interrupts handled:\n");
+ printf(" IRQ-%d: %lu\n", irq, get_irq_count(irq) - irq_count);
+ printf(" Posted-interrupt notification events: %lu\n",
+ get_irq_count_by_name("PIN:") - pin_count);
+ printf(" Posted-interrupt wakeup events: %lu\n",
+ get_irq_count_by_name("PIW:") - piw_count);
+
+ vfio_pci_device_cleanup(device);
+ iommu_cleanup(iommu);
+
+ return 0;
+}
--
2.53.0.1118.gaef5881109-goog
next prev parent reply other threads:[~2026-03-31 19:41 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-31 19:40 [PATCH v2 00/14] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
2026-03-31 19:40 ` [PATCH v2 01/14] KVM: selftests: Build and link sefltests/vfio/lib into KVM selftests Josh Hilke
2026-04-01 18:17 ` Sean Christopherson
2026-04-01 23:49 ` Josh Hilke
2026-03-31 19:40 ` [PATCH v2 02/14] KVM: selftests: Add helper functions for IRQ testing Josh Hilke
2026-04-01 18:26 ` Sean Christopherson
2026-04-01 23:54 ` Josh Hilke
2026-03-31 19:40 ` Josh Hilke [this message]
2026-04-01 19:58 ` [PATCH v2 03/14] KVM: selftests: Add vfio_pci_irq_test Sean Christopherson
2026-04-02 0:13 ` Josh Hilke
2026-04-02 17:52 ` Sean Christopherson
2026-03-31 19:40 ` [PATCH v2 04/14] KVM: selftests: Reproduce tests that rely on randomization Josh Hilke
2026-03-31 19:40 ` [PATCH v2 05/14] KVM: selftests: Add support for random host IRQ affinity Josh Hilke
2026-04-01 20:01 ` Sean Christopherson
2026-04-02 1:16 ` Josh Hilke
2026-03-31 19:40 ` [PATCH v2 06/14] KVM: selftests: Allow blocking vCPUs via HLT Josh Hilke
2026-04-01 20:03 ` Sean Christopherson
2026-03-31 19:40 ` [PATCH v2 07/14] KVM: selftests: Add support for physical device MSI triggers Josh Hilke
2026-04-01 20:24 ` Sean Christopherson
2026-04-02 3:23 ` Josh Hilke
2026-03-31 19:40 ` [PATCH v2 08/14] KVM: selftests: Add option to clear GSI routes Josh Hilke
2026-03-31 19:40 ` [PATCH v2 09/14] KVM: selftests: Make test IRQ count configurable Josh Hilke
2026-03-31 19:40 ` [PATCH v2 10/14] KVM: selftests: Add support for NMI delivery Josh Hilke
2026-04-01 20:29 ` Sean Christopherson
2026-04-02 5:27 ` Josh Hilke
2026-03-31 19:40 ` [PATCH v2 11/14] KVM: selftests: Add support for vCPU pinning Josh Hilke
2026-04-01 20:55 ` Sean Christopherson
2026-03-31 19:40 ` [PATCH v2 12/14] KVM: selftests: Support testing with multiple vCPUs Josh Hilke
2026-03-31 19:40 ` [PATCH v2 13/14] KVM: selftests: Add xAPIC mode support Josh Hilke
2026-03-31 19:40 ` [PATCH v2 14/14] KVM: selftests: Make vfio_pci_irq_test timeout configurable Josh Hilke
2026-04-01 21:00 ` Sean Christopherson
2026-04-01 18:17 ` [PATCH v2 00/14] KVM: selftests: Link with VFIO selftests lib and test device interrupts Sean Christopherson
2026-04-01 18:52 ` David Matlack
2026-04-01 19:07 ` Sean Christopherson
2026-04-01 20:12 ` David Matlack
2026-04-01 23:41 ` Josh Hilke
2026-04-01 23:58 ` David Matlack
2026-04-02 0:38 ` Josh Hilke
2026-04-02 1:49 ` Josh Hilke
2026-04-02 17:35 ` Sean Christopherson
2026-04-02 17:56 ` David Matlack
2026-04-02 18:07 ` Josh Hilke
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260331194033.3890309-4-jrhilke@google.com \
--to=jrhilke@google.com \
--cc=alex@shazbot.org \
--cc=dmatlack@google.com \
--cc=kvm@vger.kernel.org \
--cc=pbonzini@redhat.com \
--cc=seanjc@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox