* [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib
@ 2026-05-30 0:21 Josh Hilke
2026-05-30 0:21 ` [PATCH v4 01/19] KVM: selftests: Build and link selftests/vfio/lib into KVM selftests Josh Hilke
` (14 more replies)
0 siblings, 15 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
This is v4 of a series which introduces tools/testing/selftests/kvm/irq_test.c
in KVM selftests. This test exercises the delivery of interrupts (both
emulated via eventfd and real from a VFIO device) to guest vCPUs. Beyond basic
injection, the series adds coverage for several complex scenarios, including:
- Dynamic updates to KVM's GSI routing table while interrupts are active.
- Waking up halted vCPUs via interrupts.
- Stressing interrupt delivery during random host IRQ affinity changes.
- Stressing interrupt delivery during random vCPU thread migration across
physical CPUs.
- Testing non-postable interrupt remapping (using NMIs to force transitions).
- Supporting both xAPIC and x2APIC modes in the guest.
The series also links the VFIO selftests library into KVM selftests to enable
testing of VFIO-KVM interactions.
The test can optionally use a PCI device bound to vfio-pci to test physical
device interrupts. If using a device, it can be invoked by passing the BDF to
the VFIO selftests setup script, and then running the test with the device BDF
passed via the -d option:
$ ./tools/testing/selftests/vfio/scripts/setup.sh 0000:6a:01.0
$ tools/testing/selftests/kvm/irq_test -d 0000:6a:01.0
This test only supports x86. Testing physical device interrupts (-d argument)
requires a device with a supported VFIO selftest driver. Currently supported
devices include:
- Intel DSA (Data Streaming Accelerator), 8086:0b25
- Intel IOAT (I/O Acceleration Technology), 8086:2021
The test can be run with following command-line arguments allow for broad
coverage of the interrupt delivery path:
-a: Random IRQ Affinity. Randomly affinitizes the device IRQ to different host
CPUs to verify stable delivery during interrupt steering changes.
-b: Block vCPUs. Causes vCPUs to HLT instead of spinning, verifying that
Posted-interrupt wakeup (PIW) correctly kicks blocked vCPUs.
-c: Clear GSI Routes. Periodically destroys/recreates KVM's GSI routing table to
verify handling of dynamic IRQ updates.
-d: Device MSI Triggers. Uses the physical device to trigger MSIs instead of
eventfd emulation (requires a supported device driver).
-i: IRQ Count. Sets the number of interrupts to generate (default 1000).
-m: vCPU Migration. Migrates vCPUs to random physical CPUs to verify that
posted interrupts follow the vCPU across host cores.
-n: NMI Delivery. Routes interrupts as NMIs into the guest to verify the
VFIO-NMI delivery path.
-v: vCPU Count. Distributes interrupts across multiple vCPUs via round-robin
routing.
-x: xAPIC Mode. Forces legacy xAPIC mode to verify compatibility.
---
Changelog
v3 -> v4
- Add context to short logs specifying if the change relates
specifically to the IRQ test (Sean)
- Format code correctly (Sean)
- Fix compilation errors for gettid() wrapper (Sean)
- Add macros for GUEST_RECEIVED_NMI and GUEST_RECEIVED_IRQ (Sean)
- Split helper functions into separate patches (Sean)
David Matlack (12):
KVM: selftests: Build and link selftests/vfio/lib into KVM selftests
KVM: selftests: Add /proc/interrupts parsing helpers for IRQ test
KVM: selftests: Add guest read/write macros
KVM: selftests: Add IRQ injection test
KVM: selftests: Verify IRQ bypass works in IRQ test
KVM: selftests: Verify interrupts are received when IRQ affinity
changes in IRQ test
KVM: selftests: Verify IRQs wake up halted vCPUs in IRQ test
KVM: selftests: Verify interrupts are received after modifying IRQ
routes in IRQ test
KVM: selftests: Make number of IRQs configurable in IRQ test
KVM: selftests: Verify non-postable IRQ remapping in IRQ test
KVM: selftests: Make number of vCPUs configurable in IRQ test
KVM: selftests: Add xAPIC support in IRQ test
Josh Hilke (7):
KVM: selftests: Rename guest_rng to kvm_rng
KVM: selftests: Add helper to generate random u64 in range [min,max]
KVM: selftests: Add kvm_gettid() wrapper and convert users
KVM: selftests: Add kvm_sched_getaffinity() wrapper and convert users
KVM: selftests: Add pin_task_to_random_cpu() helper function
KVM: selftests: Verify vCPU migration during IRQ delivery in IRQ test
KVM: selftests: Print vCPU affinity on timeout during IRQ test
tools/testing/selftests/kvm/Makefile.kvm | 8 +-
tools/testing/selftests/kvm/arch_timer.c | 2 +-
.../kvm/arm64/arch_timer_edge_cases.c | 2 +-
.../selftests/kvm/demand_paging_test.c | 2 +-
.../selftests/kvm/dirty_log_perf_test.c | 4 +-
tools/testing/selftests/kvm/dirty_log_test.c | 8 +-
.../selftests/kvm/include/kvm_syscalls.h | 6 +
.../testing/selftests/kvm/include/kvm_util.h | 13 +
.../testing/selftests/kvm/include/proc_util.h | 28 ++
.../testing/selftests/kvm/include/test_util.h | 25 +-
.../selftests/kvm/include/x86/kvm_util_arch.h | 4 +-
tools/testing/selftests/kvm/irq_test.c | 370 ++++++++++++++++++
tools/testing/selftests/kvm/lib/assert.c | 8 +-
tools/testing/selftests/kvm/lib/kvm_util.c | 95 ++++-
tools/testing/selftests/kvm/lib/memstress.c | 8 +-
tools/testing/selftests/kvm/lib/proc_util.c | 62 +++
tools/testing/selftests/kvm/lib/test_util.c | 27 +-
tools/testing/selftests/kvm/mmu_stress_test.c | 15 +-
tools/testing/selftests/kvm/rseq_test.c | 6 +-
tools/testing/selftests/kvm/steal_time.c | 22 +-
20 files changed, 635 insertions(+), 80 deletions(-)
create mode 100644 tools/testing/selftests/kvm/include/proc_util.h
create mode 100644 tools/testing/selftests/kvm/irq_test.c
create mode 100644 tools/testing/selftests/kvm/lib/proc_util.c
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v4 01/19] KVM: selftests: Build and link selftests/vfio/lib into KVM selftests
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-05-30 0:21 ` [PATCH v4 02/19] KVM: selftests: Add /proc/interrupts parsing helpers for IRQ test Josh Hilke
` (13 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
From: David Matlack <dmatlack@google.com>
Include libvfio.mk into the KVM selftests Makefile and link it into all
KVM selftests by adding it to LIBKVM_OBJS.
This lays the groundwork for future changes to utilize VFIO devices to
verify IRQ bypass in KVM selftests.
Note that KVM selftests build their own copy of sefltests/vfio/lib and
the resulting object files are placed in $(OUTPUT)/lib. This allows the
KVM and VFIO selftests to apply different CFLAGS when building without
conflicting with each other.
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
---
tools/testing/selftests/kvm/Makefile.kvm | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 6471fa214a9f..b3e223f45575 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -252,6 +252,7 @@ OVERRIDE_TARGETS = 1
# which causes the environment variable to override the makefile).
include ../lib.mk
include ../cgroup/lib/libcgroup.mk
+include ../vfio/lib/libvfio.mk
INSTALL_HDR_PATH = $(top_srcdir)/usr
LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/
@@ -306,7 +307,9 @@ LIBKVM_S := $(filter %.S,$(LIBKVM))
LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C))
LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S))
LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING))
-LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) $(LIBCGROUP_O)
+LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ)
+LIBKVM_OBJS += $(LIBCGROUP_O)
+LIBKVM_OBJS += $(LIBVFIO_O)
SPLIT_TEST_GEN_PROGS := $(patsubst %, $(OUTPUT)/%, $(SPLIT_TESTS))
SPLIT_TEST_GEN_OBJ := $(patsubst %, $(OUTPUT)/$(ARCH)/%.o, $(SPLIT_TESTS))
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v4 02/19] KVM: selftests: Add /proc/interrupts parsing helpers for IRQ test
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
2026-05-30 0:21 ` [PATCH v4 01/19] KVM: selftests: Build and link selftests/vfio/lib into KVM selftests Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-06-01 19:19 ` Sean Christopherson
2026-05-30 0:21 ` [PATCH v4 03/19] KVM: selftests: Add guest read/write macros Josh Hilke
` (12 subsequent siblings)
14 siblings, 1 reply; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
From: David Matlack <dmatlack@google.com>
Introduce utility functions to parse /proc/interrupts and extract the
host IRQ number for a given VFIO device and MSI vector.
These helpers lay the groundwork for subsequent patches that will creat
a KVM IRQ test and use real VFIO devices to trigger hardware interrupts.
Specifically, they are needed to allow the test to log the exact IRQ
number being used for better debugging.
Suggested-by: Sean Christopherson <seanjc@google.com>
Co-developed-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../testing/selftests/kvm/include/proc_util.h | 9 ++++
tools/testing/selftests/kvm/lib/proc_util.c | 42 +++++++++++++++++++
3 files changed, 52 insertions(+)
create mode 100644 tools/testing/selftests/kvm/include/proc_util.h
create mode 100644 tools/testing/selftests/kvm/lib/proc_util.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index b3e223f45575..d944b81cad7d 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -11,6 +11,7 @@ LIBKVM += lib/kvm_util.c
LIBKVM += lib/lru_gen_util.c
LIBKVM += lib/memstress.c
LIBKVM += lib/guest_sprintf.c
+LIBKVM += lib/proc_util.c
LIBKVM += lib/rbtree.c
LIBKVM += lib/sparsebit.c
LIBKVM += lib/test_util.c
diff --git a/tools/testing/selftests/kvm/include/proc_util.h b/tools/testing/selftests/kvm/include/proc_util.h
new file mode 100644
index 000000000000..7c465e8584e2
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/proc_util.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef SELFTEST_KVM_PROC_UTIL_H
+#define SELFTEST_KVM_PROC_UTIL_H
+
+#include <stdint.h>
+
+int get_proc_vfio_irq_number(const char *vfio_device_bdf, int msi);
+
+#endif /* SELFTEST_KVM_PROC_UTIL_H */
diff --git a/tools/testing/selftests/kvm/lib/proc_util.c b/tools/testing/selftests/kvm/lib/proc_util.c
new file mode 100644
index 000000000000..ad1c54a81869
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/proc_util.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "kvm_util.h"
+#include "test_util.h"
+#include "proc_util.h"
+
+#include <ctype.h>
+
+static FILE *open_proc_interrupts(void)
+{
+ FILE *fp;
+
+ fp = fopen("/proc/interrupts", "r");
+ TEST_ASSERT(fp, "fopen(/proc/interrupts) failed");
+
+ return fp;
+}
+
+int get_proc_vfio_irq_number(const char *device_bdf, int msi)
+{
+ char search_string[64];
+ char line[4096];
+ int irq = -1;
+ FILE *fp;
+
+ fp = open_proc_interrupts();
+
+ snprintf(search_string, sizeof(search_string), "vfio-msix[%d]", msi);
+
+ while (fgets(line, sizeof(line), fp)) {
+ if (strstr(line, device_bdf) && strstr(line, search_string)) {
+ TEST_ASSERT_EQ(1, sscanf(line, "%d:", &irq));
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ TEST_ASSERT(irq != -1, "Failed to locate IRQ for %s %s", device_bdf,
+ search_string);
+ return irq;
+}
+
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v4 03/19] KVM: selftests: Add guest read/write macros
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
2026-05-30 0:21 ` [PATCH v4 01/19] KVM: selftests: Build and link selftests/vfio/lib into KVM selftests Josh Hilke
2026-05-30 0:21 ` [PATCH v4 02/19] KVM: selftests: Add /proc/interrupts parsing helpers for IRQ test Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-05-30 0:21 ` [PATCH v4 04/19] KVM: selftests: Rename guest_rng to kvm_rng Josh Hilke
` (11 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
From: David Matlack <dmatlack@google.com>
Add SYNC_FROM_GUEST_AND_READ(vm, variable), to read a variable value
from the guest. Add WRITE_AND_SYNC_TO_GUEST(vm, variable) to write a
value to a guest variable. These macros improve the readability of code
which reads and writes data between host and guest in tests.
Also update existing guest reads/writes to use these macros in the
following tests:
- tools/testing/selftests/kvm/dirty_log_test.c
- tools/testing/selftests/kvm/mmu_stress_test.c
- tools/testing/selftests/kvm/steal_time.c
No functional changes are intended.
Suggested-by: Sean Christopherson <seanjc@google.com>
Co-developed-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
---
tools/testing/selftests/kvm/dirty_log_test.c | 6 ++---
.../testing/selftests/kvm/include/kvm_util.h | 10 +++++++++
tools/testing/selftests/kvm/mmu_stress_test.c | 9 +++-----
tools/testing/selftests/kvm/steal_time.c | 22 +++++++------------
4 files changed, 23 insertions(+), 24 deletions(-)
diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c
index d58a641b0e6a..66c3d9554379 100644
--- a/tools/testing/selftests/kvm/dirty_log_test.c
+++ b/tools/testing/selftests/kvm/dirty_log_test.c
@@ -764,16 +764,14 @@ static void run_test(enum vm_guest_mode mode, void *arg)
* writing memory during verification, pages that this thread
* sees as clean may be written with this iteration's value.
*/
- WRITE_ONCE(vcpu_stop, true);
- sync_global_to_guest(vm, vcpu_stop);
+ WRITE_AND_SYNC_TO_GUEST(vm, vcpu_stop, true);
sem_wait(&sem_vcpu_stop);
/*
* Clear vcpu_stop after the vCPU thread has acknowledge the
* stop request and is waiting, i.e. is definitely not running!
*/
- WRITE_ONCE(vcpu_stop, false);
- sync_global_to_guest(vm, vcpu_stop);
+ WRITE_AND_SYNC_TO_GUEST(vm, vcpu_stop, false);
/*
* Sync the number of writes performed before verification, the
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 8b39cb919f4f..499e695f1fc4 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -1144,6 +1144,16 @@ vm_adjust_num_guest_pages(enum vm_guest_mode mode, unsigned int num_guest_pages)
memcpy(&(g), _p, sizeof(g)); \
})
+#define SYNC_FROM_GUEST_AND_READ(_vm, _variable) ({ \
+ sync_global_from_guest(_vm, _variable); \
+ READ_ONCE(_variable); \
+})
+
+#define WRITE_AND_SYNC_TO_GUEST(_vm, _variable, _value) do { \
+ WRITE_ONCE(_variable, _value); \
+ sync_global_to_guest(_vm, _variable); \
+} while (0)
+
/*
* Write a global value, but only in the VM's (guest's) domain. Primarily used
* for "globals" that hold per-VM values (VMs always duplicate code and global
diff --git a/tools/testing/selftests/kvm/mmu_stress_test.c b/tools/testing/selftests/kvm/mmu_stress_test.c
index 51c070556f3e..a9fac4bc02c2 100644
--- a/tools/testing/selftests/kvm/mmu_stress_test.c
+++ b/tools/testing/selftests/kvm/mmu_stress_test.c
@@ -155,10 +155,8 @@ static void *vcpu_worker(void *data)
"Expected EFAULT on write to RO memory, got r = %d, errno = %d", r, errno);
atomic_inc(&nr_ro_faults);
- if (atomic_read(&nr_ro_faults) == nr_vcpus) {
- WRITE_ONCE(all_vcpus_hit_ro_fault, true);
- sync_global_to_guest(vm, all_vcpus_hit_ro_fault);
- }
+ if (atomic_read(&nr_ro_faults) == nr_vcpus)
+ WRITE_AND_SYNC_TO_GUEST(vm, all_vcpus_hit_ro_fault, true);
#if defined(__x86_64__) || defined(__aarch64__)
/*
@@ -383,8 +381,7 @@ int main(int argc, char *argv[])
rendezvous_with_vcpus(&time_run2, "run 2");
mprotect(mem, slot_size, PROT_READ);
- mprotect_ro_done = true;
- sync_global_to_guest(vm, mprotect_ro_done);
+ WRITE_AND_SYNC_TO_GUEST(vm, mprotect_ro_done, true);
rendezvous_with_vcpus(&time_ro, "mprotect RO");
mprotect(mem, slot_size, PROT_READ | PROT_WRITE);
diff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c
index 7be8adfe5dd3..a866927581fa 100644
--- a/tools/testing/selftests/kvm/steal_time.c
+++ b/tools/testing/selftests/kvm/steal_time.c
@@ -72,8 +72,8 @@ static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i)
int ret;
/* ST_GPA_BASE is identity mapped */
- st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE);
- sync_global_to_guest(vcpu->vm, st_gva[i]);
+ WRITE_AND_SYNC_TO_GUEST(vcpu->vm, st_gva[i],
+ (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE));
ret = _vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME,
(ulong)st_gva[i] | KVM_STEAL_RESERVED_MASK);
@@ -181,8 +181,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i)
vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &dev);
/* ST_GPA_BASE is identity mapped */
- st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE);
- sync_global_to_guest(vm, st_gva[i]);
+ WRITE_AND_SYNC_TO_GUEST(vm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE));
st_ipa = (ulong)st_gva[i] | 1;
ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev);
@@ -279,10 +278,8 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu)
static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i)
{
/* ST_GPA_BASE is identity mapped */
- st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE);
- st_gpa[i] = addr_gva2gpa(vcpu->vm, (vm_vaddr_t)st_gva[i]);
- sync_global_to_guest(vcpu->vm, st_gva[i]);
- sync_global_to_guest(vcpu->vm, st_gpa[i]);
+ WRITE_AND_SYNC_TO_GUEST(vcpu->vm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE));
+ WRITE_AND_SYNC_TO_GUEST(vcpu->vm, st_gpa[i], addr_gva2gpa(vcpu->vm, (vm_vaddr_t)st_gva[i]));
}
static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx)
@@ -376,8 +373,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i)
};
/* ST_GPA_BASE is identity mapped */
- st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE);
- sync_global_to_guest(vm, st_gva[i]);
+ WRITE_AND_SYNC_TO_GUEST(vm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE));
err = __vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &attr);
TEST_ASSERT(err == 0, "No PV stealtime Feature");
@@ -476,8 +472,7 @@ int main(int ac, char **av)
/* Second VCPU run, expect guest stolen time to be <= run_delay */
run_vcpu(vcpus[i]);
- sync_global_from_guest(vm, guest_stolen_time[i]);
- stolen_time = guest_stolen_time[i];
+ stolen_time = SYNC_FROM_GUEST_AND_READ(vm, guest_stolen_time[i]);
run_delay = get_run_delay();
TEST_ASSERT(stolen_time <= run_delay,
"Expected stolen time <= %ld, got %ld",
@@ -497,8 +492,7 @@ int main(int ac, char **av)
/* Run VCPU again to confirm stolen time is consistent with run_delay */
run_vcpu(vcpus[i]);
- sync_global_from_guest(vm, guest_stolen_time[i]);
- stolen_time = guest_stolen_time[i] - stolen_time;
+ stolen_time = SYNC_FROM_GUEST_AND_READ(vm, guest_stolen_time[i]) - stolen_time;
TEST_ASSERT(stolen_time >= run_delay,
"Expected stolen time >= %ld, got %ld",
run_delay, stolen_time);
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v4 04/19] KVM: selftests: Rename guest_rng to kvm_rng
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (2 preceding siblings ...)
2026-05-30 0:21 ` [PATCH v4 03/19] KVM: selftests: Add guest read/write macros Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-05-30 0:21 ` [PATCH v4 05/19] KVM: selftests: Add helper to generate random u64 in range [min,max] Josh Hilke
` (10 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
Rename functions prefixed with 'guest_random_' to 'kvm_random_' and the
global random state variable 'guest_rng' to 'kvm_rng'. This expands
their usage and reflects that the random number generator can be used by
both the host and the guest.
No functional changes are intended.
Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
---
.../selftests/kvm/dirty_log_perf_test.c | 4 ++--
tools/testing/selftests/kvm/dirty_log_test.c | 2 +-
.../testing/selftests/kvm/include/test_util.h | 22 +++++++++----------
.../selftests/kvm/include/x86/kvm_util_arch.h | 4 ++--
tools/testing/selftests/kvm/lib/kvm_util.c | 20 ++++++++---------
tools/testing/selftests/kvm/lib/memstress.c | 8 +++----
tools/testing/selftests/kvm/lib/test_util.c | 6 ++---
7 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/tools/testing/selftests/kvm/dirty_log_perf_test.c b/tools/testing/selftests/kvm/dirty_log_perf_test.c
index 0a1ea1d1e2d8..606abd858a12 100644
--- a/tools/testing/selftests/kvm/dirty_log_perf_test.c
+++ b/tools/testing/selftests/kvm/dirty_log_perf_test.c
@@ -311,7 +311,7 @@ int main(int argc, char *argv[])
int opt;
/* Override the seed to be deterministic by default. */
- guest_random_seed = 1;
+ kvm_random_seed = 1;
dirty_log_manual_caps =
kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
@@ -357,7 +357,7 @@ int main(int argc, char *argv[])
p.phys_offset = strtoull(optarg, NULL, 0);
break;
case 'r':
- guest_random_seed = atoi_positive("Random seed", optarg);
+ kvm_random_seed = atoi_positive("Random seed", optarg);
break;
case 's':
p.backing_src = parse_backing_src_type(optarg);
diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c
index 66c3d9554379..cddf56400e6c 100644
--- a/tools/testing/selftests/kvm/dirty_log_test.c
+++ b/tools/testing/selftests/kvm/dirty_log_test.c
@@ -121,7 +121,7 @@ static void guest_code(void)
while (true) {
while (!READ_ONCE(vcpu_stop)) {
addr = guest_test_virt_mem;
- addr += (guest_random_u64(&guest_rng) % guest_num_pages)
+ addr += (kvm_random_u64(&kvm_rng) % guest_num_pages)
* guest_page_size;
addr = align_down(addr, host_page_size);
diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
index b4872ba8ed12..ae39c4293b9a 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -106,30 +106,30 @@ struct timespec timespec_sub(struct timespec ts1, struct timespec ts2);
struct timespec timespec_elapsed(struct timespec start);
struct timespec timespec_div(struct timespec ts, int divisor);
-struct guest_random_state {
+struct kvm_random_state {
uint32_t seed;
};
-extern uint32_t guest_random_seed;
-extern struct guest_random_state guest_rng;
+extern uint32_t kvm_random_seed;
+extern struct kvm_random_state kvm_rng;
-struct guest_random_state new_guest_random_state(uint32_t seed);
-uint32_t guest_random_u32(struct guest_random_state *state);
+struct kvm_random_state new_kvm_random_state(uint32_t seed);
+uint32_t kvm_random_u32(struct kvm_random_state *state);
-static inline bool __guest_random_bool(struct guest_random_state *state,
+static inline bool __kvm_random_bool(struct kvm_random_state *state,
uint8_t percent)
{
- return (guest_random_u32(state) % 100) < percent;
+ return (kvm_random_u32(state) % 100) < percent;
}
-static inline bool guest_random_bool(struct guest_random_state *state)
+static inline bool kvm_random_bool(struct kvm_random_state *state)
{
- return __guest_random_bool(state, 50);
+ return __kvm_random_bool(state, 50);
}
-static inline uint64_t guest_random_u64(struct guest_random_state *state)
+static inline uint64_t kvm_random_u64(struct kvm_random_state *state)
{
- return ((uint64_t)guest_random_u32(state) << 32) | guest_random_u32(state);
+ return ((uint64_t)kvm_random_u32(state) << 32) | kvm_random_u32(state);
}
enum vm_mem_backing_src_type {
diff --git a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h b/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h
index be35d26bb320..63a12ef9008e 100644
--- a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h
+++ b/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h
@@ -55,9 +55,9 @@ static inline bool __vm_arch_has_protected_memory(struct kvm_vm_arch *arch)
do { \
const typeof(mem) val = (__val); \
\
- if (!is_forced_emulation_enabled || guest_random_bool(&guest_rng)) { \
+ if (!is_forced_emulation_enabled || kvm_random_bool(&kvm_rng)) { \
(mem) = val; \
- } else if (guest_random_bool(&guest_rng)) { \
+ } else if (kvm_random_bool(&kvm_rng)) { \
__asm__ __volatile__(KVM_FEP "mov %1, %0" \
: "+m" (mem) \
: "r" (val) : "memory"); \
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 1959bf556e88..b247b2015b2a 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -20,9 +20,9 @@
#define KVM_UTIL_MIN_PFN 2
-uint32_t guest_random_seed;
-struct guest_random_state guest_rng;
-static uint32_t last_guest_seed;
+uint32_t kvm_random_seed;
+struct kvm_random_state kvm_rng;
+static uint32_t last_kvm_seed;
static size_t vcpu_mmap_sz(void);
@@ -515,12 +515,12 @@ struct kvm_vm *__vm_create(struct vm_shape shape, uint32_t nr_runnable_vcpus,
slot0 = memslot2region(vm, 0);
ucall_init(vm, slot0->region.guest_phys_addr + slot0->region.memory_size);
- if (guest_random_seed != last_guest_seed) {
- pr_info("Random seed: 0x%x\n", guest_random_seed);
- last_guest_seed = guest_random_seed;
+ if (kvm_random_seed != last_kvm_seed) {
+ pr_info("Random seed: 0x%x\n", kvm_random_seed);
+ last_kvm_seed = kvm_random_seed;
}
- guest_rng = new_guest_random_state(guest_random_seed);
- sync_global_to_guest(vm, guest_rng);
+ kvm_rng = new_kvm_random_state(kvm_random_seed);
+ sync_global_to_guest(vm, kvm_rng);
kvm_arch_vm_post_create(vm, nr_runnable_vcpus);
@@ -2358,8 +2358,8 @@ void __attribute((constructor)) kvm_selftest_init(void)
sigaction(SIGILL, &sig_sa, NULL);
sigaction(SIGFPE, &sig_sa, NULL);
- guest_random_seed = last_guest_seed = random();
- pr_info("Random seed: 0x%x\n", guest_random_seed);
+ kvm_random_seed = last_kvm_seed = random();
+ pr_info("Random seed: 0x%x\n", kvm_random_seed);
kvm_selftest_arch_init();
}
diff --git a/tools/testing/selftests/kvm/lib/memstress.c b/tools/testing/selftests/kvm/lib/memstress.c
index 557c0a0a5658..c5df9c99102a 100644
--- a/tools/testing/selftests/kvm/lib/memstress.c
+++ b/tools/testing/selftests/kvm/lib/memstress.c
@@ -48,14 +48,14 @@ void memstress_guest_code(uint32_t vcpu_idx)
{
struct memstress_args *args = &memstress_args;
struct memstress_vcpu_args *vcpu_args = &args->vcpu_args[vcpu_idx];
- struct guest_random_state rand_state;
+ struct kvm_random_state rand_state;
uint64_t gva;
uint64_t pages;
uint64_t addr;
uint64_t page;
int i;
- rand_state = new_guest_random_state(guest_random_seed + vcpu_idx);
+ rand_state = new_kvm_random_state(kvm_random_seed + vcpu_idx);
gva = vcpu_args->gva;
pages = vcpu_args->pages;
@@ -69,13 +69,13 @@ void memstress_guest_code(uint32_t vcpu_idx)
for (i = 0; i < pages; i++) {
if (args->random_access)
- page = guest_random_u32(&rand_state) % pages;
+ page = kvm_random_u32(&rand_state) % pages;
else
page = i;
addr = gva + (page * args->guest_page_size);
- if (__guest_random_bool(&rand_state, args->write_percent))
+ if (__kvm_random_bool(&rand_state, args->write_percent))
*(uint64_t *)addr = 0x0123456789ABCDEF;
else
READ_ONCE(*(uint64_t *)addr);
diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c
index 8a1848586a85..e342d9fb4771 100644
--- a/tools/testing/selftests/kvm/lib/test_util.c
+++ b/tools/testing/selftests/kvm/lib/test_util.c
@@ -30,13 +30,13 @@ void __attribute__((used)) expect_sigbus_handler(int signum)
* Park-Miller LCG using standard constants.
*/
-struct guest_random_state new_guest_random_state(uint32_t seed)
+struct kvm_random_state new_kvm_random_state(uint32_t seed)
{
- struct guest_random_state s = {.seed = seed};
+ struct kvm_random_state s = {.seed = seed};
return s;
}
-uint32_t guest_random_u32(struct guest_random_state *state)
+uint32_t kvm_random_u32(struct kvm_random_state *state)
{
state->seed = (uint64_t)state->seed * 48271 % ((uint32_t)(1 << 31) - 1);
return state->seed;
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v4 05/19] KVM: selftests: Add helper to generate random u64 in range [min,max]
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (3 preceding siblings ...)
2026-05-30 0:21 ` [PATCH v4 04/19] KVM: selftests: Rename guest_rng to kvm_rng Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-05-30 0:21 ` [PATCH v4 06/19] KVM: selftests: Add IRQ injection test Josh Hilke
` (9 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
Introduce kvm_random_u64_in_range(state, min, max). This function
returns a random u64 in the inclusive range of [min, max] using a struct
kvm_random_state.
Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
---
.../testing/selftests/kvm/include/test_util.h | 3 +++
tools/testing/selftests/kvm/lib/test_util.c | 18 ++++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
index ae39c4293b9a..d601227dbdba 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -132,6 +132,9 @@ static inline uint64_t kvm_random_u64(struct kvm_random_state *state)
return ((uint64_t)kvm_random_u32(state) << 32) | kvm_random_u32(state);
}
+uint64_t kvm_random_u64_in_range(struct kvm_random_state *state, uint64_t min,
+ uint64_t max);
+
enum vm_mem_backing_src_type {
VM_MEM_SRC_ANONYMOUS,
VM_MEM_SRC_ANONYMOUS_THP,
diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c
index e342d9fb4771..dc47ed9736e9 100644
--- a/tools/testing/selftests/kvm/lib/test_util.c
+++ b/tools/testing/selftests/kvm/lib/test_util.c
@@ -42,6 +42,24 @@ uint32_t kvm_random_u32(struct kvm_random_state *state)
return state->seed;
}
+/* Returns a random u64 in the inclusive range [min, max] */
+uint64_t kvm_random_u64_in_range(struct kvm_random_state *state, uint64_t min,
+ uint64_t max)
+{
+ uint64_t value;
+ uint64_t range;
+
+ TEST_ASSERT(min <= max, "PEBKAC, min = 0x%lx, max = 0x%lx", min, max);
+
+ value = kvm_random_u64(state);
+
+ range = max - min;
+ if (range == ULLONG_MAX)
+ return value;
+
+ return min + (value % (range + 1));
+}
+
/*
* Parses "[0-9]+[kmgt]?".
*/
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v4 06/19] KVM: selftests: Add IRQ injection test
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (4 preceding siblings ...)
2026-05-30 0:21 ` [PATCH v4 05/19] KVM: selftests: Add helper to generate random u64 in range [min,max] Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-05-30 0:21 ` [PATCH v4 15/19] KVM: selftests: Add pin_task_to_random_cpu() helper function Josh Hilke
` (8 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
From: David Matlack <dmatlack@google.com>
Add a new test, irq_test.c, which verifies that KVM correctly injects
interrupts into a running guest when triggered via an eventfd bound to a
GSI using the irqfd mechanism.
This test is intentionally simple. Support for IRQ bypass, receiving
interrupts from real devices, and other features will be added to this
test in future changes.
Suggested-by: Sean Christopherson <seanjc@google.com>
Link: https://lore.kernel.org/kvm/20250404193923.1413163-68-seanjc@google.com/
Co-developed-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
---
tools/testing/selftests/kvm/Makefile.kvm | 2 +
tools/testing/selftests/kvm/irq_test.c | 174 +++++++++++++++++++++++
2 files changed, 176 insertions(+)
create mode 100644 tools/testing/selftests/kvm/irq_test.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index d944b81cad7d..9e5a00699dd0 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -149,6 +149,7 @@ TEST_GEN_PROGS_x86 += coalesced_io_test
TEST_GEN_PROGS_x86 += dirty_log_perf_test
TEST_GEN_PROGS_x86 += guest_memfd_test
TEST_GEN_PROGS_x86 += hardware_disable_test
+TEST_GEN_PROGS_x86 += irq_test
TEST_GEN_PROGS_x86 += memslot_modification_stress_test
TEST_GEN_PROGS_x86 += memslot_perf_test
TEST_GEN_PROGS_x86 += mmu_stress_test
@@ -157,6 +158,7 @@ TEST_GEN_PROGS_x86 += steal_time
TEST_GEN_PROGS_x86 += system_counter_offset_test
TEST_GEN_PROGS_x86 += pre_fault_memory_test
+
# Compiled outputs used by test targets
TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
new file mode 100644
index 000000000000..d3fee51cf451
--- /dev/null
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "kvm_util.h"
+#include "test_util.h"
+#include "apic.h"
+#include "processor.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/eventfd.h>
+
+static u64 timeout_ns = 2ULL * 1000 * 1000 * 1000;
+static bool guest_ready_for_irqs[KVM_MAX_VCPUS];
+static bool guest_received_irq[KVM_MAX_VCPUS];
+static bool done;
+
+#define GUEST_RECEIVED_IRQ(__vcpu) \
+ SYNC_FROM_GUEST_AND_READ((__vcpu)->vm, guest_received_irq[(__vcpu)->id])
+
+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)
+{
+ struct {
+ struct kvm_irq_routing head;
+ struct kvm_irq_routing_entry entry;
+ } routing_data = {};
+
+ struct kvm_irq_routing *routes = &routing_data.head;
+
+ 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 void help(const char *name)
+{
+ printf("Usage: %s [-h]\n", name);
+ printf("\n");
+ printf("Tests KVM IRQ injection via irqfd using an emulated eventfd.\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 = kvm_random_u64_in_range(&kvm_rng, 24, KVM_MAX_IRQ_ROUTES - 1);
+ u8 vector = kvm_random_u64_in_range(&kvm_rng, 32, UINT8_MAX);
+
+ struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
+ pthread_t vcpu_threads[KVM_MAX_VCPUS];
+ int nr_irqs = 1000, nr_vcpus = 1;
+ int i, j, c, eventfd;
+ struct kvm_vm *vm;
+
+ while ((c = getopt(argc, argv, "h")) != -1) {
+ switch (c) {
+ case 'h':
+ default:
+ help(argv[0]);
+ }
+ }
+
+ TEST_REQUIRE(kvm_arch_has_default_irqchip());
+
+ vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
+ vm_install_exception_handler(vm, vector, guest_irq_handler);
+
+ eventfd = kvm_new_eventfd();
+
+ printf("Injecting interrupts for GSI %d (Vector 0x%x) %d times\n",
+ gsi, vector, nr_irqs);
+
+ kvm_assign_irqfd(vm, gsi, eventfd);
+
+ 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 (!SYNC_FROM_GUEST_AND_READ(vm, guest_ready_for_irqs[vcpu->id]))
+ continue;
+ }
+
+ 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(!GUEST_RECEIVED_IRQ(vcpus[j]),
+ "IRQ flag for vCPU %d not clear prior to test",
+ vcpus[j]->id);
+
+ /* Trigger interrupt */
+ eventfd_write(eventfd, 1);
+
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ for (;;) {
+ if (GUEST_RECEIVED_IRQ(vcpu))
+ break;
+
+ if (timespec_to_ns(timespec_elapsed(start)) > timeout_ns)
+ TEST_FAIL("vCPU %d timed out waiting for IRQ from GSI %d (Vector 0x%x) !\n",
+ vcpu->id, gsi, vector);
+ }
+
+ WRITE_AND_SYNC_TO_GUEST(vm, guest_received_irq[vcpu->id], false);
+ }
+
+ WRITE_AND_SYNC_TO_GUEST(vm, done, true);
+
+ for (i = 0; i < nr_vcpus; i++)
+ pthread_join(vcpu_threads[i], NULL);
+
+ printf("Test passed!\n");
+
+ return 0;
+}
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v4 15/19] KVM: selftests: Add pin_task_to_random_cpu() helper function
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (5 preceding siblings ...)
2026-05-30 0:21 ` [PATCH v4 06/19] KVM: selftests: Add IRQ injection test Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-05-30 0:21 ` [PATCH v4 16/19] KVM: selftests: Verify vCPU migration during IRQ delivery in IRQ test Josh Hilke
` (7 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
Add a helper function pin_task_to_random_cpu() to randomly select a CPU
from a given cpu_set_t and pin the specified task to it.
This helper will be used in a future change in the KVM IRQ test to
migrate vCPU threads.
Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
---
.../testing/selftests/kvm/include/kvm_util.h | 2 ++
tools/testing/selftests/kvm/lib/kvm_util.c | 21 +++++++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 499e695f1fc4..f725ab0ed4f9 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -1096,6 +1096,8 @@ static inline void pin_task_to_cpu(pthread_t task, int cpu)
TEST_ASSERT(!r, "Failed to set thread affinity to pCPU '%u'", cpu);
}
+void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus);
+
static inline int pin_task_to_any_cpu(pthread_t task)
{
int cpu = sched_getcpu();
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index ebd04cf1847b..29cd649a54d5 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -662,6 +662,27 @@ void kvm_print_vcpu_pinning_help(void)
" (default: no pinning)\n", name, name);
}
+void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus)
+{
+ int target_idx;
+ int nr_cpus;
+ int cpu;
+
+ nr_cpus = CPU_COUNT(possible_cpus);
+ TEST_ASSERT(nr_cpus > 0, "No CPUs available in possible_cpus");
+
+ target_idx = kvm_random_u64(&kvm_rng) % nr_cpus;
+
+ for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
+ if (CPU_ISSET(cpu, possible_cpus) && target_idx-- == 0) {
+ pin_task_to_cpu(task, cpu);
+ return;
+ }
+ }
+
+ TEST_FAIL("Failed to find random CPU in possible_cpus");
+}
+
void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[],
int nr_vcpus)
{
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v4 16/19] KVM: selftests: Verify vCPU migration during IRQ delivery in IRQ test
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (6 preceding siblings ...)
2026-05-30 0:21 ` [PATCH v4 15/19] KVM: selftests: Add pin_task_to_random_cpu() helper function Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-05-30 0:21 ` [PATCH v4 17/19] KVM: selftests: Print vCPU affinity on timeout during " Josh Hilke
` (6 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
Add the '-m' flag to tools/testing/selftests/kvm/irq_test.c to migrate
vCPU threads across random physical CPUs during the test. This validates
KVM's ability to handle vCPUs changing physical CPUs while interrupts
are actively being injected.
Co-developed-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
---
tools/testing/selftests/kvm/irq_test.c | 34 ++++++++++++++++++++++++--
1 file changed, 32 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index 79b0691c9889..f653317022e3 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -18,6 +18,8 @@ static u64 timeout_ns = 2ULL * 1000 * 1000 * 1000;
static bool guest_ready_for_irqs[KVM_MAX_VCPUS];
static bool guest_received_irq[KVM_MAX_VCPUS];
static bool guest_received_nmi[KVM_MAX_VCPUS];
+static pid_t vcpu_tids[KVM_MAX_VCPUS];
+static bool migrate_vcpus;
static bool irq_affinity;
static bool block_vcpus;
static bool done;
@@ -67,12 +69,23 @@ static void *vcpu_thread_main(void *arg)
struct kvm_vcpu *vcpu = arg;
struct ucall uc;
+ WRITE_ONCE(vcpu_tids[vcpu->id], kvm_gettid());
+
vcpu_run(vcpu);
TEST_ASSERT_EQ(UCALL_DONE, get_ucall(vcpu, &uc));
return NULL;
}
+static void migrate_vcpu_threads(int nr_vcpus, pthread_t *vcpu_threads,
+ cpu_set_t *available_cpus)
+{
+ int i;
+
+ for (i = 0; i < nr_vcpus; i++)
+ pin_task_to_random_cpu(vcpu_threads[i], available_cpus);
+}
+
static int vfio_setup_msi(struct vfio_pci_device *device)
{
const int flags = MAP_SHARED | MAP_ANONYMOUS;
@@ -131,7 +144,7 @@ static void kvm_clear_gsi_routes(struct kvm_vm *vm)
static void help(const char *name)
{
- printf("Usage: %s [-a] [-b] [-c] [-d <segment:bus:device.function>] [-h] [-i nr_irqs] [-n]\n", name);
+ printf("Usage: %s [-a] [-b] [-c] [-d <segment:bus:device.function>] [-h] [-i nr_irqs] [-m] [-n]\n", name);
printf("\n");
printf("Tests KVM IRQ injection via irqfd using an emulated eventfd.\n");
printf("-a Randomly affinitize the device's host IRQ to different physical CPUs throughout the test\n");
@@ -139,6 +152,7 @@ static void help(const char *name)
printf("-c Destroy and recreate KVM's GSI routing table in between some interrupts\n");
printf("-d Use a VFIO device to send MSI-X interrupts instead of using an emulated eventfd\n");
printf("-i The number of IRQs to generate during the test\n");
+ printf("-m Pin vCPU threads to random physical CPUs throughout the test\n");
printf("-n Deliver 50 percent of IRQs as non-maskable interrupts\n");
printf("\n");
exit(KSFT_FAIL);
@@ -171,8 +185,9 @@ int main(int argc, char **argv)
FILE *irq_affinity_fp = NULL;
struct iommu *iommu;
struct kvm_vm *vm;
+ cpu_set_t available_cpus;
- while ((c = getopt(argc, argv, "abcd:hi:n")) != -1) {
+ while ((c = getopt(argc, argv, "abcd:hi:mn")) != -1) {
switch (c) {
case 'a':
irq_affinity = true;
@@ -189,6 +204,9 @@ int main(int argc, char **argv)
case 'i':
nr_irqs = atoi_positive("Number of IRQs", optarg);
break;
+ case 'm':
+ migrate_vcpus = true;
+ break;
case 'n':
use_nmi = true;
break;
@@ -238,6 +256,15 @@ int main(int argc, char **argv)
continue;
}
+ if (migrate_vcpus) {
+ kvm_sched_getaffinity(vcpu_tids[0], sizeof(available_cpus), &available_cpus);
+
+ if (nr_vcpus > CPU_COUNT(&available_cpus)) {
+ printf("There are more vCPUs than pCPUs; refusing to migrate.\n");
+ migrate_vcpus = false;
+ }
+ }
+
for (i = 0; i < nr_irqs; i++) {
const bool do_clear_routes = clear_routes && (i & BIT(3));
const bool do_use_nmi = use_nmi && (i & BIT(2));
@@ -254,6 +281,9 @@ int main(int argc, char **argv)
write_proc_irq_affinity(irq_affinity_fp, irq, irq_cpu);
}
+ if (migrate_vcpus && vcpu->id == 0)
+ migrate_vcpu_threads(nr_vcpus, vcpu_threads, &available_cpus);
+
for (j = 0; j < nr_vcpus; j++) {
TEST_ASSERT(!GUEST_RECEIVED_IRQ(vcpus[j]),
"IRQ flag for vCPU %d not clear prior to test",
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v4 17/19] KVM: selftests: Print vCPU affinity on timeout during IRQ test
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (7 preceding siblings ...)
2026-05-30 0:21 ` [PATCH v4 16/19] KVM: selftests: Verify vCPU migration during IRQ delivery in IRQ test Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-05-30 0:21 ` [PATCH v4 18/19] KVM: selftests: Make number of vCPUs configurable in " Josh Hilke
` (5 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
Add kvm_print_vcpu_affinity() to lib/kvm_util.c to print the physical
CPU affinity of a vCPU thread in a human-readable format.
Use this new helper in tools/testing/selftests/kvm/irq_test.c to print
the vCPU's affinity if it times out waiting for an interrupt while vCPU
migration (-m) is enabled. This provides valuable context for debugging
IRQ delivery timeouts when vCPUs are being actively migrated across
physical CPUs.
Signed-off-by: Josh Hilke <jrhilke@google.com>
---
.../testing/selftests/kvm/include/kvm_util.h | 1 +
tools/testing/selftests/kvm/irq_test.c | 2 +
tools/testing/selftests/kvm/lib/kvm_util.c | 49 +++++++++++++++++++
3 files changed, 52 insertions(+)
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index f725ab0ed4f9..0ec82088bc43 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -1119,6 +1119,7 @@ static inline int pin_self_to_any_cpu(void)
void kvm_print_vcpu_pinning_help(void);
void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[],
int nr_vcpus);
+void kvm_print_vcpu_affinity(struct kvm_vcpu *vcpu, pid_t tid);
unsigned long vm_compute_max_gfn(struct kvm_vm *vm);
unsigned int vm_calc_num_guest_pages(enum vm_guest_mode mode, size_t size);
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index f653317022e3..0db4ea4eb1ef 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -309,6 +309,8 @@ int main(int argc, char **argv)
printf(" is interrupt NMI: %s\n", do_use_nmi ? "true" : "false");
if (irq_affinity)
printf(" irq_cpu: %d\n", irq_cpu);
+ if (migrate_vcpus)
+ kvm_print_vcpu_affinity(vcpu, vcpu_tids[vcpu->id]);
TEST_FAIL("vCPU %d timed out waiting for IRQ from GSI %d (Vector 0x%x) !\n",
vcpu->id, gsi, vector);
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 29cd649a54d5..b2e534f331e2 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -15,6 +15,7 @@
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/sysinfo.h>
#include <unistd.h>
#include <linux/kernel.h>
@@ -662,6 +663,54 @@ void kvm_print_vcpu_pinning_help(void)
" (default: no pinning)\n", name, name);
}
+void kvm_print_vcpu_affinity(struct kvm_vcpu *vcpu, pid_t tid)
+{
+ int nprocs = get_nprocs();
+ bool first_range = true;
+ int i, start = -1;
+ cpu_set_t cpus;
+
+ kvm_sched_getaffinity(tid, sizeof(cpus), &cpus);
+
+ /*
+ * Output format examples:
+ * - Single CPU: "vCPU 0 (TID 1234) affined to pCPU(s): 2"
+ * - List: "vCPU 0 (TID 1234) affined to pCPU(s): 0,2,4"
+ * - Range: "vCPU 0 (TID 1234) affined to pCPU(s): 0-7"
+ * - Mixed: "vCPU 0 (TID 1234) affined to pCPU(s): 0-3,8,10-12"
+ */
+ printf("vCPU %u (TID %d) affined to pCPU(s): ", vcpu->id, tid);
+
+ for (i = 0; i <= nprocs; i++) {
+ /*
+ * Iterate to nprocs (inclusive) to ensure that if the last pCPU
+ * is part of a range, the 'else' block triggers one final time
+ * to flush that range to stdout.
+ */
+ if (i < nprocs && CPU_ISSET(i, &cpus)) {
+ if (start == -1)
+ start = i;
+ continue;
+ }
+
+ if (start != -1) {
+ int end = i - 1;
+
+ if (!first_range)
+ printf(",");
+
+ if (start == end)
+ printf("%d", start);
+ else
+ printf("%d-%d", start, end);
+
+ start = -1;
+ first_range = false;
+ }
+ }
+ printf("\n");
+}
+
void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus)
{
int target_idx;
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v4 18/19] KVM: selftests: Make number of vCPUs configurable in IRQ test
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (8 preceding siblings ...)
2026-05-30 0:21 ` [PATCH v4 17/19] KVM: selftests: Print vCPU affinity on timeout during " Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-05-30 0:21 ` [PATCH v4 19/19] KVM: selftests: Add xAPIC support " Josh Hilke
` (4 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
From: David Matlack <dmatlack@google.com>
Add a '-v' flag to tools/testing/selftests/kvm/irq_test.c to allow users
to configure the number of vCPUs to run in the test.
Co-developed-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
---
tools/testing/selftests/kvm/irq_test.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index 0db4ea4eb1ef..967083b8e2ff 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -144,7 +144,7 @@ static void kvm_clear_gsi_routes(struct kvm_vm *vm)
static void help(const char *name)
{
- printf("Usage: %s [-a] [-b] [-c] [-d <segment:bus:device.function>] [-h] [-i nr_irqs] [-m] [-n]\n", name);
+ printf("Usage: %s [-a] [-b] [-c] [-d <segment:bus:device.function>] [-h] [-i nr_irqs] [-m] [-n] [-v nr_vcpus]\n", name);
printf("\n");
printf("Tests KVM IRQ injection via irqfd using an emulated eventfd.\n");
printf("-a Randomly affinitize the device's host IRQ to different physical CPUs throughout the test\n");
@@ -154,6 +154,7 @@ static void help(const char *name)
printf("-i The number of IRQs to generate during the test\n");
printf("-m Pin vCPU threads to random physical CPUs throughout the test\n");
printf("-n Deliver 50 percent of IRQs as non-maskable interrupts\n");
+ printf("-v Number of vCPUS to run\n");
printf("\n");
exit(KSFT_FAIL);
}
@@ -187,7 +188,7 @@ int main(int argc, char **argv)
struct kvm_vm *vm;
cpu_set_t available_cpus;
- while ((c = getopt(argc, argv, "abcd:hi:mn")) != -1) {
+ while ((c = getopt(argc, argv, "abcd:hi:mnv:")) != -1) {
switch (c) {
case 'a':
irq_affinity = true;
@@ -210,6 +211,9 @@ int main(int argc, char **argv)
case 'n':
use_nmi = true;
break;
+ case 'v':
+ nr_vcpus = atoi_positive("Number of vCPUS", optarg);
+ break;
case 'h':
default:
help(argv[0]);
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v4 19/19] KVM: selftests: Add xAPIC support in IRQ test
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (9 preceding siblings ...)
2026-05-30 0:21 ` [PATCH v4 18/19] KVM: selftests: Make number of vCPUs configurable in " Josh Hilke
@ 2026-05-30 0:21 ` Josh Hilke
2026-05-30 0:26 ` [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (3 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:21 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
From: David Matlack <dmatlack@google.com>
Add the '-x' flag to tools/testing/selftests/kvm/irq_test.c to configure
the guest to use xAPIC mode instead of the default x2APIC mode. This
expands test coverage to ensure that IRQ injection and routing work
correctly when the guest uses the older xAPIC interface.
Co-developed-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
---
tools/testing/selftests/kvm/irq_test.c | 28 +++++++++++++++++++++-----
1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index 967083b8e2ff..29f54a435c8e 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -20,6 +20,7 @@ static bool guest_received_irq[KVM_MAX_VCPUS];
static bool guest_received_nmi[KVM_MAX_VCPUS];
static pid_t vcpu_tids[KVM_MAX_VCPUS];
static bool migrate_vcpus;
+static bool x2apic = true;
static bool irq_affinity;
static bool block_vcpus;
static bool done;
@@ -31,14 +32,20 @@ static bool done;
static u32 guest_get_vcpu_id(void)
{
- return x2apic_read_reg(APIC_ID);
+ if (x2apic)
+ return x2apic_read_reg(APIC_ID);
+ else
+ return xapic_read_reg(APIC_ID) >> 24;
}
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);
+ if (x2apic)
+ x2apic_write_reg(APIC_EOI, 0);
+ else
+ xapic_write_reg(APIC_EOI, 0);
}
static void guest_nmi_handler(struct ex_regs *regs)
@@ -48,7 +55,10 @@ static void guest_nmi_handler(struct ex_regs *regs)
static void guest_code(void)
{
- x2apic_enable();
+ if (x2apic)
+ x2apic_enable();
+ else
+ xapic_enable();
sti_nop();
@@ -144,7 +154,7 @@ static void kvm_clear_gsi_routes(struct kvm_vm *vm)
static void help(const char *name)
{
- printf("Usage: %s [-a] [-b] [-c] [-d <segment:bus:device.function>] [-h] [-i nr_irqs] [-m] [-n] [-v nr_vcpus]\n", name);
+ printf("Usage: %s [-a] [-b] [-c] [-d <segment:bus:device.function>] [-h] [-i nr_irqs] [-m] [-n] [-v nr_vcpus] [-x]\n", name);
printf("\n");
printf("Tests KVM IRQ injection via irqfd using an emulated eventfd.\n");
printf("-a Randomly affinitize the device's host IRQ to different physical CPUs throughout the test\n");
@@ -155,6 +165,7 @@ static void help(const char *name)
printf("-m Pin vCPU threads to random physical CPUs throughout the test\n");
printf("-n Deliver 50 percent of IRQs as non-maskable interrupts\n");
printf("-v Number of vCPUS to run\n");
+ printf("-x Use xAPIC mode instead of x2APIC mode in the guest\n");
printf("\n");
exit(KSFT_FAIL);
}
@@ -188,7 +199,7 @@ int main(int argc, char **argv)
struct kvm_vm *vm;
cpu_set_t available_cpus;
- while ((c = getopt(argc, argv, "abcd:hi:mnv:")) != -1) {
+ while ((c = getopt(argc, argv, "abcd:hi:mnv:x")) != -1) {
switch (c) {
case 'a':
irq_affinity = true;
@@ -214,6 +225,9 @@ int main(int argc, char **argv)
case 'v':
nr_vcpus = atoi_positive("Number of vCPUS", optarg);
break;
+ case 'x':
+ x2apic = false;
+ break;
case 'h':
default:
help(argv[0]);
@@ -226,6 +240,9 @@ int main(int argc, char **argv)
vm_install_exception_handler(vm, vector, guest_irq_handler);
vm_install_exception_handler(vm, NMI_VECTOR, guest_nmi_handler);
+ if (!x2apic)
+ virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
+
if (device_bdf) {
iommu = iommu_init(default_iommu_mode);
device = vfio_pci_device_init(device_bdf, iommu);
@@ -248,6 +265,7 @@ int main(int argc, char **argv)
kvm_assign_irqfd(vm, gsi, eventfd);
+ sync_global_to_guest(vm, x2apic);
sync_global_to_guest(vm, block_vcpus);
for (i = 0; i < nr_vcpus; i++)
--
2.54.0.929.g9b7fa37559-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (10 preceding siblings ...)
2026-05-30 0:21 ` [PATCH v4 19/19] KVM: selftests: Add xAPIC support " Josh Hilke
@ 2026-05-30 0:26 ` Josh Hilke
2026-06-01 19:07 ` Sean Christopherson
` (2 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Josh Hilke @ 2026-05-30 0:26 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson
On Fri, May 29, 2026 at 5:22 PM Josh Hilke <jrhilke@google.com> wrote:
> ---
> Changelog
>
> v3 -> v4
> - Add context to short logs specifying if the change relates
> specifically to the IRQ test (Sean)
> - Format code correctly (Sean)
> - Fix compilation errors for gettid() wrapper (Sean)
> - Add macros for GUEST_RECEIVED_NMI and GUEST_RECEIVED_IRQ (Sean)
> - Split helper functions into separate patches (Sean)
link to v3: https://lore.kernel.org/kvm/20260421231557.1254270-1-jrhilke@google.com/
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
` (11 preceding siblings ...)
2026-05-30 0:26 ` [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
@ 2026-06-01 19:07 ` Sean Christopherson
[not found] ` <20260530002134.558837-15-jrhilke@google.com>
[not found] ` <20260530002134.558837-9-jrhilke@google.com>
14 siblings, 0 replies; 17+ messages in thread
From: Sean Christopherson @ 2026-06-01 19:07 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Sat, May 30, 2026, Josh Hilke wrote:
> David Matlack (12):
> KVM: selftests: Build and link selftests/vfio/lib into KVM selftests
> KVM: selftests: Add /proc/interrupts parsing helpers for IRQ test
> KVM: selftests: Add guest read/write macros
> KVM: selftests: Add IRQ injection test
> KVM: selftests: Verify IRQ bypass works in IRQ test
> KVM: selftests: Verify interrupts are received when IRQ affinity
> changes in IRQ test
> KVM: selftests: Verify IRQs wake up halted vCPUs in IRQ test
> KVM: selftests: Verify interrupts are received after modifying IRQ
> routes in IRQ test
> KVM: selftests: Make number of IRQs configurable in IRQ test
> KVM: selftests: Verify non-postable IRQ remapping in IRQ test
> KVM: selftests: Make number of vCPUs configurable in IRQ test
> KVM: selftests: Add xAPIC support in IRQ test
>
> Josh Hilke (7):
> KVM: selftests: Rename guest_rng to kvm_rng
> KVM: selftests: Add helper to generate random u64 in range [min,max]
> KVM: selftests: Add kvm_gettid() wrapper and convert users
> KVM: selftests: Add kvm_sched_getaffinity() wrapper and convert users
> KVM: selftests: Add pin_task_to_random_cpu() helper function
> KVM: selftests: Verify vCPU migration during IRQ delivery in IRQ test
> KVM: selftests: Print vCPU affinity on timeout during IRQ test
>
> tools/testing/selftests/kvm/Makefile.kvm | 8 +-
> tools/testing/selftests/kvm/arch_timer.c | 2 +-
> .../kvm/arm64/arch_timer_edge_cases.c | 2 +-
> .../selftests/kvm/demand_paging_test.c | 2 +-
> .../selftests/kvm/dirty_log_perf_test.c | 4 +-
> tools/testing/selftests/kvm/dirty_log_test.c | 8 +-
> .../selftests/kvm/include/kvm_syscalls.h | 6 +
> .../testing/selftests/kvm/include/kvm_util.h | 13 +
> .../testing/selftests/kvm/include/proc_util.h | 28 ++
> .../testing/selftests/kvm/include/test_util.h | 25 +-
> .../selftests/kvm/include/x86/kvm_util_arch.h | 4 +-
> tools/testing/selftests/kvm/irq_test.c | 370 ++++++++++++++++++
> tools/testing/selftests/kvm/lib/assert.c | 8 +-
> tools/testing/selftests/kvm/lib/kvm_util.c | 95 ++++-
> tools/testing/selftests/kvm/lib/memstress.c | 8 +-
> tools/testing/selftests/kvm/lib/proc_util.c | 62 +++
> tools/testing/selftests/kvm/lib/test_util.c | 27 +-
> tools/testing/selftests/kvm/mmu_stress_test.c | 15 +-
> tools/testing/selftests/kvm/rseq_test.c | 6 +-
> tools/testing/selftests/kvm/steal_time.c | 22 +-
> 20 files changed, 635 insertions(+), 80 deletions(-)
> create mode 100644 tools/testing/selftests/kvm/include/proc_util.h
> create mode 100644 tools/testing/selftests/kvm/irq_test.c
> create mode 100644 tools/testing/selftests/kvm/lib/proc_util.c
>
> --
Please read through Documentation/process/maintainer-kvm-x86.rst. This isn't
strictly scoped to KVM x86, but at the same time it's pretty obvious who is going
to be applying this series.
Specifically:
Git Base
~~~~~~~~
If you are using git version 2.9.0 or later (Googlers, this is all of you!),
use ``git format-patch`` with the ``--base`` flag to automatically include the
base tree information in the generated patches.
Note, ``--base=auto`` works as expected if and only if a branch's upstream is
set to the base topic branch, e.g. it will do the wrong thing if your upstream
is set to your personal repository for backup purposes. An alternative "auto"
solution is to derive the names of your development branches based on their
KVM x86 topic, and feed that into ``--base``. E.g. ``x86/pmu/my_branch_name``,
and then write a small wrapper to extract ``pmu`` from the current branch name
to yield ``--base=x/pmu``, where ``x`` is whatever name your repository uses to
track the KVM x86 remote.
and to a lesser extent:
Base Tree/Branch
~~~~~~~~~~~~~~~~
Fixes that target the current release, a.k.a. mainline, should be based on
``git://git.kernel.org/pub/scm/virt/kvm/kvm.git master``. Note, fixes do not
automatically warrant inclusion in the current release. There is no singular
rule, but typically only fixes for bugs that are urgent, critical, and/or were
introduced in the current release should target the current release.
Everything else should be based on ``kvm-x86/next``, i.e. there is no need to
select a specific topic branch as the base. If there are conflicts and/or
dependencies across topic branches, it is the maintainer's job to sort them
out.
The only exception to using ``kvm-x86/next`` as the base is if a patch/series
is a multi-arch series, i.e. has non-trivial modifications to common KVM code
and/or has more than superficial changes to other architectures' code. Multi-
arch patch/series should instead be based on a common, stable point in KVM's
history, e.g. the release candidate upon which ``kvm-x86 next`` is based. If
you're unsure whether a patch/series is truly multi-arch, err on the side of
caution and treat it as multi-arch, i.e. use a common base.
Through random guessing, I discovered that this appears to be based on 7.0.
(a) I shouldn't have to guess. (b) that's simply too old of a base, e.g. this
doesn't even apply clean only 7.1-rc1.
While there's room for interpretation in the "Base Tree/Branch" section, IMO
using any base older than 7.1-rc2 is blatantly wrong. Any of 7.1-rc2+, kvm/next,
or kvm-x86/next would be acceptable (though fairly literal reading of the docs
would say "don't use kvm-x86/next").
> 2.54.0.929.g9b7fa37559-goog
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v4 02/19] KVM: selftests: Add /proc/interrupts parsing helpers for IRQ test
2026-05-30 0:21 ` [PATCH v4 02/19] KVM: selftests: Add /proc/interrupts parsing helpers for IRQ test Josh Hilke
@ 2026-06-01 19:19 ` Sean Christopherson
0 siblings, 0 replies; 17+ messages in thread
From: Sean Christopherson @ 2026-06-01 19:19 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Sat, May 30, 2026, Josh Hilke wrote:
> index 000000000000..7c465e8584e2
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/include/proc_util.h
> @@ -0,0 +1,9 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef SELFTEST_KVM_PROC_UTIL_H
> +#define SELFTEST_KVM_PROC_UTIL_H
> +
> +#include <stdint.h>
> +
> +int get_proc_vfio_irq_number(const char *vfio_device_bdf, int msi);
I feel like the "proc" is confusing and unintesting. How the utility gets the
information is an implementation detail that I don't think the caller particular
cares about.
"irq_number" is also vague. There are myriad IRQ "numbers" in play. "virtual
IRQ", as they're sometimes described by the IRQ subsystem, is unfortunately
terrible terminology for KVM+VFIO. host_irq and linux_irq are the two names I
can think of that are fairly specific without too much potential for confusion.
I'd probably vote for host_irq?
I also think the function needs to have msix in the name, because within VFIO,
there are multiple flavors of interrupts that can be wired up, and this _only_
works for MSI-X. And The param should be @msix.
I also think it might make sense to return an "unsigned int", to communicate that
it guarantees a valid IRQ was found, but I don't care terribly either way.
Maybe something like this?
unsigned int vfio_msix_to_host_irq(const char *vfio_device_bdf, int msix)
or
unsigned int vfio_get_msix_host_irq(const char *vfio_device_bdf, int msix)
or
unsigned int vfio_get_host_irq_for_msix(const char *vfio_device_bdf, int msix)
I'd probably vote for the first one, vfio_msix_to_host_irq(), as it's more
obviously "just" a translation helper, e.g. isn't allocating an IRQ or anything.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v4 14/19] KVM: selftests: Add kvm_sched_getaffinity() wrapper and convert users
[not found] ` <20260530002134.558837-15-jrhilke@google.com>
@ 2026-06-01 19:27 ` Sean Christopherson
0 siblings, 0 replies; 17+ messages in thread
From: Sean Christopherson @ 2026-06-01 19:27 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Sat, May 30, 2026, Josh Hilke wrote:
> diff --git a/tools/testing/selftests/kvm/include/kvm_syscalls.h b/tools/testing/selftests/kvm/include/kvm_syscalls.h
> index 522ffff0462c..2e362fda50af 100644
> --- a/tools/testing/selftests/kvm/include/kvm_syscalls.h
> +++ b/tools/testing/selftests/kvm/include/kvm_syscalls.h
> @@ -82,5 +82,6 @@ __KVM_SYSCALL_DEFINE(munmap, 2, void *, mem, size_t, size);
> __KVM_SYSCALL_DEFINE(close, 1, int, fd);
> __KVM_SYSCALL_DEFINE(fallocate, 4, int, fd, int, mode, loff_t, offset, loff_t, len);
> __KVM_SYSCALL_DEFINE(ftruncate, 2, unsigned int, fd, off_t, length);
> +__KVM_SYSCALL_DEFINE(sched_getaffinity, 3, pid_t, pid, size_t, cpusetsize, cpu_set_t *, mask);
This doesn't compile, needs:
diff --git tools/testing/selftests/kvm/include/kvm_syscalls.h tools/testing/selftests/kvm/include/kvm_syscalls.h
index 3e4cc91d5f87..d796a7228d9d 100644
--- tools/testing/selftests/kvm/include/kvm_syscalls.h
+++ tools/testing/selftests/kvm/include/kvm_syscalls.h
@@ -2,6 +2,8 @@
#ifndef SELFTEST_KVM_SYSCALLS_H
#define SELFTEST_KVM_SYSCALLS_H
+#include <sched.h>
+
#include <sys/syscall.h>
#define MAP_ARGS0(m,...)
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v4 08/19] KVM: selftests: Verify interrupts are received when IRQ affinity changes in IRQ test
[not found] ` <20260530002134.558837-9-jrhilke@google.com>
@ 2026-06-01 20:02 ` Sean Christopherson
0 siblings, 0 replies; 17+ messages in thread
From: Sean Christopherson @ 2026-06-01 20:02 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Sat, May 30, 2026, Josh Hilke wrote:
> @@ -134,17 +137,21 @@ int main(int argc, char **argv)
> u32 gsi = kvm_random_u64_in_range(&kvm_rng, 24, KVM_MAX_IRQ_ROUTES - 1);
> u8 vector = kvm_random_u64_in_range(&kvm_rng, 32, UINT8_MAX);
>
> + int i, j, c, msi, irq, eventfd, irq_cpu;
This fails to build with -Werror:
irq_test.c: In function ‘main’:
irq_test.c:183:35: error: ‘irq’ may be used uninitialized [-Werror=maybe-uninitialized]
183 | irq_affinity_fp = open_proc_irq_affinity(irq);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
irq_test.c:140:27: note: ‘irq’ was declared here
140 | int i, j, c, msi, irq, eventfd, irq_cpu;
| ^~~
irq_test.c:228:41: error: ‘irq_cpu’ may be used uninitialized [-Werror=maybe-uninitialized]
228 | printf(" irq_cpu: %d\n", irq_cpu);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
irq_test.c:140:41: note: ‘irq_cpu’ was declared here
140 | int i, j, c, msi, irq, eventfd, irq_cpu;
| ^~~~~~~
The first one (of three distinct uses) is easy enough to solve:
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index 29f54a435c8e..23c82ddee6b9 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -251,15 +251,16 @@ int main(int argc, char **argv)
eventfd = device->msi_eventfds[msi];
printf("Using device %s MSI-X[%d] (IRQ-%d)\n", device_bdf, msi,
irq);
+
+ if (irq_affinity)
+ irq_affinity_fp = open_proc_irq_affinity(irq);
} else {
+ TEST_ASSERT(!irq_affinity,
+ "Setting IRQ affinity (-a) requires a backing device (-d)");
+
eventfd = kvm_new_eventfd();
}
- if (irq_affinity) {
- TEST_ASSERT(device_bdf, "-a requires -d");
- irq_affinity_fp = open_proc_irq_affinity(irq);
- }
-
printf("Injecting interrupts for GSI %d (Vector 0x%x) %d times\n",
gsi, vector, nr_irqs);
And the second can be addressed by checking irq_affinity_fp instead of irq_affinity:
@@ -298,7 +299,7 @@ int main(int argc, char **argv)
kvm_route_msi(vm, gsi, vcpu, vector, do_use_nmi);
- if (irq_affinity && vcpu->id == 0) {
+ if (irq_affinity_fp && vcpu->id == 0) {
irq_cpu = kvm_random_u64(&kvm_rng) % get_nprocs();
write_proc_irq_affinity(irq_affinity_fp, irq, irq_cpu);
}
but I don't think that's quite correct. Keying off vCPU0 for task migration
makes sense, because the test round-robins IRQs to each vCPU, so it's logical to
redo task/vCPU affinity after every round.
But IRQs are being spread over _all_ host CPUs, and there is no round-robin
behavior, it's completely random. So rather than rate-limit based on number of
vCPUs, I think it makes more sense to hardcode the changes in affinity, e.g.
if (irq_affinity_fp && !(i % 10)) {
irq_cpu = kvm_random_u64(&kvm_rng) % get_nprocs();
write_proc_irq_affinity(irq_affinity_fp, irq, irq_cpu);
}
Sadly, even that doesn't address the third "used uninitialized" issue:
irq_test.c:334:41: error: ‘irq_cpu’ may be used uninitialized [-Werror=maybe-uninitialized]
334 | printf(" irq_cpu: %d\n", irq_cpu);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
because apparently gcc isn't smart enough to understand that "!(i % 10)" (or
even "!i" is guaranteed to run on the first iteration. However, that's false
positive probably a blessing in disguise, because it highlights that printing
only what userspace has _requested_ could be very misleading, given that the
whole point of the offending printf() is to spit out information when the IRQ
got lost.
Writing smp_affinity_list (or smp_affinity) does NOT guarantee the new affinity
is realized in hardware. E.g. the kernel typically "lazily" migrates IRQs, and
only actually changes the affinity when an IRQ actually arrives. To see what
CPU an IRQ is actually wired up to, you need to read /proc/<irq>/effective_affinity{,list}.
As it pertains to the "used uninitialized" flaw, I think the test should spit
out three things:
1. irq_cpu, i.e. what the test thought it set the affinity to
2. /proc/<irq>/smp_affinity, i.e. what the kernel has for the allowed list
3. /proc/<irq>/effective_affinity, i.e. what the actual, current affinity is
And then _if_ you want to ratelimit changes in affinity, fix the "used uninitialized"
issue by explicitly initializing irq_cpu, with a comment calling out that the
compiler is being stupid.
@@ -287,6 +288,12 @@ int main(int argc, char **argv)
}
}
+ /*
+ * Suppress a false positive maybe-uninitialized compiler warning due
+ * to ratelimiting changes in IRQ affinity.
+ */
+ irq_cpu = -1;
+
for (i = 0; i < nr_irqs; i++) {
const bool do_clear_routes = clear_routes && (i & BIT(3));
const bool do_use_nmi = use_nmi && (i & BIT(2));
@@ -298,7 +305,7 @@ int main(int argc, char **argv)
kvm_route_msi(vm, gsi, vcpu, vector, do_use_nmi);
- if (irq_affinity && vcpu->id == 0) {
+ if (irq_affinity_fp && !(i % 10)) {
irq_cpu = kvm_random_u64(&kvm_rng) % get_nprocs();
write_proc_irq_affinity(irq_affinity_fp, irq, irq_cpu);
}
@@ -329,7 +336,7 @@ int main(int argc, char **argv)
printf("Timeout waiting for interrupt!\n");
printf(" vCPU: %d\n", vcpu->id);
printf(" is interrupt NMI: %s\n", do_use_nmi ? "true" : "false");
- if (irq_affinity)
+ if (irq_affinity_fp)
printf(" irq_cpu: %d\n", irq_cpu);
if (migrate_vcpus)
kvm_print_vcpu_affinity(vcpu, vcpu_tids[vcpu->id]);
But honestly, unless it impacts the runtime, I don't see much value in ratelimiting
changes in IRQ affinity. Putting that much stress on the IRQ subsystem is actually
probably interesting.
And no matter what, /proc/<irq>/effective_affinity needs to be printed on failure.
^ permalink raw reply related [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-06-01 20:02 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-30 0:21 [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
2026-05-30 0:21 ` [PATCH v4 01/19] KVM: selftests: Build and link selftests/vfio/lib into KVM selftests Josh Hilke
2026-05-30 0:21 ` [PATCH v4 02/19] KVM: selftests: Add /proc/interrupts parsing helpers for IRQ test Josh Hilke
2026-06-01 19:19 ` Sean Christopherson
2026-05-30 0:21 ` [PATCH v4 03/19] KVM: selftests: Add guest read/write macros Josh Hilke
2026-05-30 0:21 ` [PATCH v4 04/19] KVM: selftests: Rename guest_rng to kvm_rng Josh Hilke
2026-05-30 0:21 ` [PATCH v4 05/19] KVM: selftests: Add helper to generate random u64 in range [min,max] Josh Hilke
2026-05-30 0:21 ` [PATCH v4 06/19] KVM: selftests: Add IRQ injection test Josh Hilke
2026-05-30 0:21 ` [PATCH v4 15/19] KVM: selftests: Add pin_task_to_random_cpu() helper function Josh Hilke
2026-05-30 0:21 ` [PATCH v4 16/19] KVM: selftests: Verify vCPU migration during IRQ delivery in IRQ test Josh Hilke
2026-05-30 0:21 ` [PATCH v4 17/19] KVM: selftests: Print vCPU affinity on timeout during " Josh Hilke
2026-05-30 0:21 ` [PATCH v4 18/19] KVM: selftests: Make number of vCPUs configurable in " Josh Hilke
2026-05-30 0:21 ` [PATCH v4 19/19] KVM: selftests: Add xAPIC support " Josh Hilke
2026-05-30 0:26 ` [PATCH v4 00/19] KVM: selftests: Link with VFIO selftests lib Josh Hilke
2026-06-01 19:07 ` Sean Christopherson
[not found] ` <20260530002134.558837-15-jrhilke@google.com>
2026-06-01 19:27 ` [PATCH v4 14/19] KVM: selftests: Add kvm_sched_getaffinity() wrapper and convert users Sean Christopherson
[not found] ` <20260530002134.558837-9-jrhilke@google.com>
2026-06-01 20:02 ` [PATCH v4 08/19] KVM: selftests: Verify interrupts are received when IRQ affinity changes in IRQ test Sean Christopherson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox