* [PATCH v3 01/16] KVM: selftests: Build and link sefltests/vfio/lib into KVM selftests
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-04-21 23:15 ` [PATCH v3 02/16] KVM: selftests: Add /proc/interrupts parsing helpers Josh Hilke
` (15 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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.
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.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v3 02/16] KVM: selftests: Add /proc/interrupts parsing helpers
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
2026-04-21 23:15 ` [PATCH v3 01/16] KVM: selftests: Build and link sefltests/vfio/lib into KVM selftests Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-04-21 23:15 ` [PATCH v3 03/16] KVM: selftests: Add guest read/write macros Josh Hilke
` (14 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 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.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v3 03/16] KVM: selftests: Add guest read/write macros
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
2026-04-21 23:15 ` [PATCH v3 01/16] KVM: selftests: Build and link sefltests/vfio/lib into KVM selftests Josh Hilke
2026-04-21 23:15 ` [PATCH v3 02/16] KVM: selftests: Add /proc/interrupts parsing helpers Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-05-27 1:52 ` Sean Christopherson
2026-04-21 23:15 ` [PATCH v3 04/16] KVM: selftests: Rename guest_rng to kvm_rng Josh Hilke
` (13 subsequent siblings)
16 siblings, 1 reply; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 | 21 +++++++------------
4 files changed, 22 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..23380de79817 100644
--- a/tools/testing/selftests/kvm/steal_time.c
+++ b/tools/testing/selftests/kvm/steal_time.c
@@ -72,8 +72,7 @@ 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 +180,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 +277,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 +372,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 +471,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 +491,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.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v3 03/16] KVM: selftests: Add guest read/write macros
2026-04-21 23:15 ` [PATCH v3 03/16] KVM: selftests: Add guest read/write macros Josh Hilke
@ 2026-05-27 1:52 ` Sean Christopherson
2026-05-28 23:01 ` Josh Hilke
0 siblings, 1 reply; 32+ messages in thread
From: Sean Christopherson @ 2026-05-27 1:52 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, Apr 21, 2026, Josh Hilke wrote:
> diff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c
> index 7be8adfe5dd3..23380de79817 100644
> --- a/tools/testing/selftests/kvm/steal_time.c
> +++ b/tools/testing/selftests/kvm/steal_time.c
> @@ -72,8 +72,7 @@ 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));
Please wrap these. 80 chars is a soft limit (and so is 100 chars), but the purpose
of the rules *and* their exceptions is to make the code easier to read. This is
hard to parse, because there's so much going on in one line, whereas I find this
easier to tease apart:
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);
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v3 03/16] KVM: selftests: Add guest read/write macros
2026-05-27 1:52 ` Sean Christopherson
@ 2026-05-28 23:01 ` Josh Hilke
2026-05-28 23:08 ` Sean Christopherson
0 siblings, 1 reply; 32+ messages in thread
From: Josh Hilke @ 2026-05-28 23:01 UTC (permalink / raw)
To: Sean Christopherson
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, May 26, 2026 at 6:52 PM Sean Christopherson <seanjc@google.com> wrote:
>
> On Tue, Apr 21, 2026, Josh Hilke wrote:
> > diff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c
> > index 7be8adfe5dd3..23380de79817 100644
> > --- a/tools/testing/selftests/kvm/steal_time.c
> > +++ b/tools/testing/selftests/kvm/steal_time.c
> > @@ -72,8 +72,7 @@ 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));
>
> Please wrap these. 80 chars is a soft limit (and so is 100 chars), but the purpose
> of the rules *and* their exceptions is to make the code easier to read. This is
> hard to parse, because there's so much going on in one line, whereas I find this
> easier to tease apart:
>
> 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);
Will do.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v3 03/16] KVM: selftests: Add guest read/write macros
2026-05-28 23:01 ` Josh Hilke
@ 2026-05-28 23:08 ` Sean Christopherson
0 siblings, 0 replies; 32+ messages in thread
From: Sean Christopherson @ 2026-05-28 23:08 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Thu, May 28, 2026, Josh Hilke wrote:
> On Tue, May 26, 2026 at 6:52 PM Sean Christopherson <seanjc@google.com> wrote:
> >
> > On Tue, Apr 21, 2026, Josh Hilke wrote:
> > > diff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c
> > > index 7be8adfe5dd3..23380de79817 100644
> > > --- a/tools/testing/selftests/kvm/steal_time.c
> > > +++ b/tools/testing/selftests/kvm/steal_time.c
> > > @@ -72,8 +72,7 @@ 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));
> >
> > Please wrap these. 80 chars is a soft limit (and so is 100 chars), but the purpose
> > of the rules *and* their exceptions is to make the code easier to read. This is
> > hard to parse, because there's so much going on in one line, whereas I find this
> > easier to tease apart:
> >
> > 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);
>
> Will do.
FYI, upstream expectations are that review feedback will be addressed unless there
is additional discussion that suggests otherwise. I.e. there's no need to acknowledge
everything. For me at least, ack responses do more harm than good because they're
pure noise 99.99999% of the time.
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v3 04/16] KVM: selftests: Rename guest_rng to kvm_rng
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (2 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 03/16] KVM: selftests: Add guest read/write macros Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-04-21 23:15 ` [PATCH v3 05/16] KVM: selftests: Add helper to generate random u64 in range [min,max] Josh Hilke
` (12 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v3 05/16] KVM: selftests: Add helper to generate random u64 in range [min,max]
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (3 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 04/16] KVM: selftests: Rename guest_rng to kvm_rng Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-05-27 1:58 ` Sean Christopherson
2026-04-21 23:15 ` [PATCH v3 06/16] KVM: selftests: Add IRQ injection test Josh Hilke
` (11 subsequent siblings)
16 siblings, 1 reply; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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>
---
tools/testing/selftests/kvm/include/test_util.h | 4 ++++
tools/testing/selftests/kvm/lib/test_util.c | 16 ++++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
index ae39c4293b9a..584a4ab43db5 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -132,6 +132,10 @@ static inline uint64_t kvm_random_u64(struct kvm_random_state *state)
return ((uint64_t)kvm_random_u32(state) << 32) | kvm_random_u32(state);
}
+/* 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);
+
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..23d543a9053e 100644
--- a/tools/testing/selftests/kvm/lib/test_util.c
+++ b/tools/testing/selftests/kvm/lib/test_util.c
@@ -42,6 +42,22 @@ uint32_t kvm_random_u32(struct kvm_random_state *state)
return state->seed;
}
+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, "min (0x%lx) cannot be greater than 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.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v3 05/16] KVM: selftests: Add helper to generate random u64 in range [min,max]
2026-04-21 23:15 ` [PATCH v3 05/16] KVM: selftests: Add helper to generate random u64 in range [min,max] Josh Hilke
@ 2026-05-27 1:58 ` Sean Christopherson
2026-05-28 23:01 ` Josh Hilke
0 siblings, 1 reply; 32+ messages in thread
From: Sean Christopherson @ 2026-05-27 1:58 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, Apr 21, 2026, Josh Hilke wrote:
> 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>
> ---
> tools/testing/selftests/kvm/include/test_util.h | 4 ++++
> tools/testing/selftests/kvm/lib/test_util.c | 16 ++++++++++++++++
> 2 files changed, 20 insertions(+)
>
> diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
> index ae39c4293b9a..584a4ab43db5 100644
> --- a/tools/testing/selftests/kvm/include/test_util.h
> +++ b/tools/testing/selftests/kvm/include/test_util.h
> @@ -132,6 +132,10 @@ static inline uint64_t kvm_random_u64(struct kvm_random_state *state)
> return ((uint64_t)kvm_random_u32(state) << 32) | kvm_random_u32(state);
> }
>
> +/* Returns a random u64 in the inclusive range [min, max] */
Put the comment above the definition (But thank you for writing it! I had typed
up a response asking for exactly this, before I realized it was there, just on
the declaration).
> +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..23d543a9053e 100644
> --- a/tools/testing/selftests/kvm/lib/test_util.c
> +++ b/tools/testing/selftests/kvm/lib/test_util.c
> @@ -42,6 +42,22 @@ uint32_t kvm_random_u32(struct kvm_random_state *state)
> return state->seed;
> }
>
> +uint64_t kvm_random_u64_in_range(struct kvm_random_state *state, uint64_t min, uint64_t max)
Wrap.
> +{
> + uint64_t value;
> + uint64_t range;
> +
> + TEST_ASSERT(min <= max, "min (0x%lx) cannot be greater than max (0x%lx)", min, max);
LOL, that's a rather hilarious error message. Partly because it's so pointless
(gotta love TEST_ASSERT()), partly because @min obviously _can_ be greater than
@max :-)
The "min <= max" that gets printed is pretty darn self-explanatory, maybe take
the opportunity to throw shade at the user/developer?
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.rc2.533.g4f5dca5207-goog
>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v3 05/16] KVM: selftests: Add helper to generate random u64 in range [min,max]
2026-05-27 1:58 ` Sean Christopherson
@ 2026-05-28 23:01 ` Josh Hilke
0 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-05-28 23:01 UTC (permalink / raw)
To: Sean Christopherson
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, May 26, 2026 at 6:58 PM Sean Christopherson <seanjc@google.com> wrote:
>
> On Tue, Apr 21, 2026, Josh Hilke wrote:
> > +/* Returns a random u64 in the inclusive range [min, max] */
>
> Put the comment above the definition (But thank you for writing it! I had typed
> up a response asking for exactly this, before I realized it was there, just on
> the declaration).
Will do
> > +uint64_t kvm_random_u64_in_range(struct kvm_random_state *state, uint64_t min, uint64_t max)
>
> Wrap.
Will do.
> > + TEST_ASSERT(min <= max, "min (0x%lx) cannot be greater than max (0x%lx)", min, max);
>
> LOL, that's a rather hilarious error message. Partly because it's so pointless
> (gotta love TEST_ASSERT()), partly because @min obviously _can_ be greater than
> @max :-)
>
> The "min <= max" that gets printed is pretty darn self-explanatory, maybe take
> the opportunity to throw shade at the user/developer?
>
> TEST_ASSERT(min <= max, "PEBKAC, min = 0x%lx, max = 0x%lx", min, max);
I love a good roast ;)
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v3 06/16] KVM: selftests: Add IRQ injection test
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (4 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 05/16] KVM: selftests: Add helper to generate random u64 in range [min,max] Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-05-27 1:59 ` Sean Christopherson
2026-05-27 2:10 ` Sean Christopherson
2026-04-21 23:15 ` [PATCH v3 07/16] KVM: selftests: Verify device IRQs are routed to vCPUs Josh Hilke
` (10 subsequent siblings)
16 siblings, 2 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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.
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 | 1 +
tools/testing/selftests/kvm/irq_test.c | 173 +++++++++++++++++++++++
2 files changed, 174 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..693c03372a31 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 += 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/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
new file mode 100644
index 000000000000..317e04899df3
--- /dev/null
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -0,0 +1,173 @@
+// 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;
+
+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(
+ !SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpus[j]->id]),
+ "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 (SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpu->id]))
+ 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.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v3 06/16] KVM: selftests: Add IRQ injection test
2026-04-21 23:15 ` [PATCH v3 06/16] KVM: selftests: Add IRQ injection test Josh Hilke
@ 2026-05-27 1:59 ` Sean Christopherson
[not found] ` <CAAdrzjs37a-hEneORNmzOvOkh4TX4Dmn6bWKEm5L4hgmkUO0wA@mail.gmail.com>
2026-05-27 2:10 ` Sean Christopherson
1 sibling, 1 reply; 32+ messages in thread
From: Sean Christopherson @ 2026-05-27 1:59 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, Apr 21, 2026, Josh Hilke wrote:
> 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.
Can you add some color explaining that this will/should work with IRQ bypass?
I assume it does right out of the gate.
> 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 | 1 +
> tools/testing/selftests/kvm/irq_test.c | 173 +++++++++++++++++++++++
> 2 files changed, 174 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..693c03372a31 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 += irq_test
Ignore pre_fault_memory_test, sort alphabetically.
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v3 06/16] KVM: selftests: Add IRQ injection test
2026-04-21 23:15 ` [PATCH v3 06/16] KVM: selftests: Add IRQ injection test Josh Hilke
2026-05-27 1:59 ` Sean Christopherson
@ 2026-05-27 2:10 ` Sean Christopherson
2026-05-28 23:02 ` Josh Hilke
1 sibling, 1 reply; 32+ messages in thread
From: Sean Christopherson @ 2026-05-27 2:10 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, Apr 21, 2026, Josh Hilke wrote:
> + 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(
Thou hast committed the cardinal sin of leaking the demonic influence of Google3
into the kernel.
Never, ever wrap immediately after the opening '('.
> + !SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpus[j]->id]),
Hmm, give how many times this shows up, maybe add a wrapper macro?
#define GUEST_RECEIVED_IRQ(__vcpu) \
SYNC_FROM_GUEST_AND_READ((__vcpu)->vm, guest_received_irq[(__vcpu)->id])
Then this becomes:
TEST_ASSERT(GUEST_RECEIVED_IRQ(vcpus[j]),
"Blah blah blah");
> + "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 (SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpu->id]))
> + 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",
Best get to reading Dante's Inferno.
> + 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.rc2.533.g4f5dca5207-goog
>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v3 06/16] KVM: selftests: Add IRQ injection test
2026-05-27 2:10 ` Sean Christopherson
@ 2026-05-28 23:02 ` Josh Hilke
0 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-05-28 23:02 UTC (permalink / raw)
To: Sean Christopherson
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, May 26, 2026 at 7:10 PM Sean Christopherson <seanjc@google.com> wrote:
>
> On Tue, Apr 21, 2026, Josh Hilke wrote:
> > + 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(
>
> Thou hast committed the cardinal sin of leaking the demonic influence of Google3
> into the kernel.
>
> Never, ever wrap immediately after the opening '('.
I'll grab my rosary beads to pay penance (and fix this in v4).
> > + !SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpus[j]->id]),
>
> Hmm, give how many times this shows up, maybe add a wrapper macro?
>
> #define GUEST_RECEIVED_IRQ(__vcpu) \
> SYNC_FROM_GUEST_AND_READ((__vcpu)->vm, guest_received_irq[(__vcpu)->id])
>
> Then this becomes:
>
> TEST_ASSERT(GUEST_RECEIVED_IRQ(vcpus[j]),
> "Blah blah blah");
>
> > + "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 (SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpu->id]))
> > + 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",
>
> Best get to reading Dante's Inferno.
Which circle am I going to? Need to know whether to bring a parka or
one of those mist fans people use at the beach to stay cool.
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v3 07/16] KVM: selftests: Verify device IRQs are routed to vCPUs
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (5 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 06/16] KVM: selftests: Add IRQ injection test Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-04-21 23:15 ` [PATCH v3 08/16] KVM: selftests: Verify IRQ affinity changes Josh Hilke
` (9 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 UTC (permalink / raw)
To: Paolo Bonzini, Sean Christopherson
Cc: kvm, linux-kernel, David Matlack, Alex Williamson, Josh Hilke
From: David Matlack <dmatlack@google.com>
Trigger real interrupts from a VFIO device instead of emulating
interrupts using KVM eventfds.
Add a '-d' argument to tools/testing/selftests/kvm/irq_test which
takes the segment:bus:device.function number of a PCI device bound to
VFIO which will trigger interrupts. The device must have a VFIO selftest
driver in order to work with the test.
Example:
$ ./tools/testing/selftests/kvm/irq_test -d 0000:06:0a.1
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 | 61 +++++++++++++++++++++++---
1 file changed, 55 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index 317e04899df3..434dce7188cc 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -1,14 +1,17 @@
// SPDX-License-Identifier: GPL-2.0
#include "kvm_util.h"
#include "test_util.h"
+#include <linux/sizes.h>
#include "apic.h"
#include "processor.h"
+#include "proc_util.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/eventfd.h>
+#include <libvfio.h>
static u64 timeout_ns = 2ULL * 1000 * 1000 * 1000;
static bool guest_ready_for_irqs[KVM_MAX_VCPUS];
@@ -52,6 +55,36 @@ static void *vcpu_thread_main(void *arg)
return NULL;
}
+static int vfio_setup_msi(struct vfio_pci_device *device)
+{
+ const int flags = MAP_SHARED | MAP_ANONYMOUS;
+ const int prot = PROT_READ | PROT_WRITE;
+ struct dma_region *region;
+
+ /* 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 = kvm_mmap(region->size, prot, flags, -1);
+ TEST_ASSERT(region->vaddr != MAP_FAILED, "mmap() failed\n");
+ iommu_map(device->iommu, region);
+
+ vfio_pci_driver_init(device);
+ return device->driver.msi;
+}
+
+static void trigger_interrupt(struct vfio_pci_device *device, int eventfd)
+{
+ if (device)
+ vfio_pci_driver_send_msi(device);
+ else
+ eventfd_write(eventfd, 1);
+}
+
+
static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu,
u8 vector)
{
@@ -73,9 +106,10 @@ static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu,
static void help(const char *name)
{
- printf("Usage: %s [-h]\n", name);
+ printf("Usage: %s [-d <segment:bus:device.function>] [-h]\n", name);
printf("\n");
printf("Tests KVM IRQ injection via irqfd using an emulated eventfd.\n");
+ printf("-d Use a VFIO device to send MSI-X interrupts instead of using an emulated eventfd\n");
printf("\n");
exit(KSFT_FAIL);
}
@@ -99,12 +133,18 @@ int main(int argc, char **argv)
struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
pthread_t vcpu_threads[KVM_MAX_VCPUS];
+ struct vfio_pci_device *device = NULL;
int nr_irqs = 1000, nr_vcpus = 1;
- int i, j, c, eventfd;
+ int i, j, c, msi, irq, eventfd;
+ const char *device_bdf = NULL;
+ struct iommu *iommu;
struct kvm_vm *vm;
- while ((c = getopt(argc, argv, "h")) != -1) {
+ while ((c = getopt(argc, argv, "d:h")) != -1) {
switch (c) {
+ case 'd':
+ device_bdf = optarg;
+ break;
case 'h':
default:
help(argv[0]);
@@ -116,7 +156,17 @@ int main(int argc, char **argv)
vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
vm_install_exception_handler(vm, vector, guest_irq_handler);
- eventfd = kvm_new_eventfd();
+ if (device_bdf) {
+ iommu = iommu_init(default_iommu_mode);
+ device = vfio_pci_device_init(device_bdf, iommu);
+ msi = vfio_setup_msi(device);
+ irq = get_proc_vfio_irq_number(device_bdf, msi);
+ eventfd = device->msi_eventfds[msi];
+ printf("Using device %s MSI-X[%d] (IRQ-%d)\n", device_bdf, msi,
+ irq);
+ } else {
+ eventfd = kvm_new_eventfd();
+ }
printf("Injecting interrupts for GSI %d (Vector 0x%x) %d times\n",
gsi, vector, nr_irqs);
@@ -145,8 +195,7 @@ int main(int argc, char **argv)
"IRQ flag for vCPU %d not clear prior to test",
vcpus[j]->id);
- /* Trigger interrupt */
- eventfd_write(eventfd, 1);
+ trigger_interrupt(device, eventfd);
clock_gettime(CLOCK_MONOTONIC, &start);
for (;;) {
--
2.54.0.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v3 08/16] KVM: selftests: Verify IRQ affinity changes
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (6 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 07/16] KVM: selftests: Verify device IRQs are routed to vCPUs Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-04-21 23:15 ` [PATCH v3 09/16] KVM: selftests: Verify IRQs wake up halted vCPUs Josh Hilke
` (8 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 '-a' flag to tools/testing/selftests/kvm/irq_test.c to randomly
affinitize the device's host IRQ to different physical CPUs throughout
the test. This stresses the kernel's ability to maintain correct
interrupt routing and delivery even as the underlying hardware IRQ
affinity is changed dynamically via /proc/irq/.
Co-developed-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
---
.../testing/selftests/kvm/include/proc_util.h | 19 ++++++++++
tools/testing/selftests/kvm/irq_test.c | 37 ++++++++++++++++---
tools/testing/selftests/kvm/lib/proc_util.c | 20 ++++++++++
3 files changed, 70 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/proc_util.h b/tools/testing/selftests/kvm/include/proc_util.h
index 7c465e8584e2..2c8086158f68 100644
--- a/tools/testing/selftests/kvm/include/proc_util.h
+++ b/tools/testing/selftests/kvm/include/proc_util.h
@@ -6,4 +6,23 @@
int get_proc_vfio_irq_number(const char *vfio_device_bdf, int msi);
+/*
+ * open_proc_irq_affinity - Open the smp_affinity_list file for a given IRQ
+ * @irq: The IRQ number
+ *
+ * Opens /proc/irq/<irq>/smp_affinity_list for writing and returns the FILE
+ * pointer.
+ */
+FILE *open_proc_irq_affinity(int irq);
+
+/*
+ * write_proc_irq_affinity - Write a CPU number to the smp_affinity_list file
+ * @fp: The FILE pointer for the smp_affinity_list file
+ * @irq: The IRQ number (for error reporting)
+ * @irq_cpu: The CPU number to write
+ *
+ * Writes the given CPU number to the provided FILE pointer.
+ */
+void write_proc_irq_affinity(FILE *fp, int irq, int irq_cpu);
+
#endif /* SELFTEST_KVM_PROC_UTIL_H */
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index 434dce7188cc..bb124028fb20 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -11,11 +11,13 @@
#include <unistd.h>
#include <pthread.h>
#include <sys/eventfd.h>
+#include <sys/sysinfo.h>
#include <libvfio.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 irq_affinity;
static bool done;
static u32 guest_get_vcpu_id(void)
@@ -106,9 +108,10 @@ static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu,
static void help(const char *name)
{
- printf("Usage: %s [-d <segment:bus:device.function>] [-h]\n", name);
+ printf("Usage: %s [-a] [-d <segment:bus:device.function>] [-h]\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");
printf("-d Use a VFIO device to send MSI-X interrupts instead of using an emulated eventfd\n");
printf("\n");
exit(KSFT_FAIL);
@@ -131,17 +134,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;
struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
pthread_t vcpu_threads[KVM_MAX_VCPUS];
struct vfio_pci_device *device = NULL;
int nr_irqs = 1000, nr_vcpus = 1;
- int i, j, c, msi, irq, eventfd;
const char *device_bdf = NULL;
+ FILE *irq_affinity_fp = NULL;
struct iommu *iommu;
struct kvm_vm *vm;
- while ((c = getopt(argc, argv, "d:h")) != -1) {
+ while ((c = getopt(argc, argv, "ad:h")) != -1) {
switch (c) {
+ case 'a':
+ irq_affinity = true;
+ break;
case 'd':
device_bdf = optarg;
break;
@@ -168,6 +175,11 @@ int main(int argc, char **argv)
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);
@@ -189,6 +201,11 @@ int main(int argc, char **argv)
kvm_route_msi(vm, gsi, vcpu, vector);
+ if (irq_affinity && vcpu->id == 0) {
+ irq_cpu = kvm_random_u64(&kvm_rng) % get_nprocs();
+ write_proc_irq_affinity(irq_affinity_fp, irq, irq_cpu);
+ }
+
for (j = 0; j < nr_vcpus; j++)
TEST_ASSERT(
!SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpus[j]->id]),
@@ -202,10 +219,15 @@ int main(int argc, char **argv)
if (SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpu->id]))
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",
+ if (timespec_to_ns(timespec_elapsed(start)) > timeout_ns) {
+ printf("Timeout waiting for interrupt!\n");
+ printf(" vCPU: %d\n", vcpu->id);
+ if (irq_affinity)
+ printf(" irq_cpu: %d\n", irq_cpu);
+
+ 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);
@@ -216,6 +238,9 @@ int main(int argc, char **argv)
for (i = 0; i < nr_vcpus; i++)
pthread_join(vcpu_threads[i], NULL);
+ if (irq_affinity)
+ fclose(irq_affinity_fp);
+
printf("Test passed!\n");
return 0;
diff --git a/tools/testing/selftests/kvm/lib/proc_util.c b/tools/testing/selftests/kvm/lib/proc_util.c
index ad1c54a81869..e5a4ee262f5e 100644
--- a/tools/testing/selftests/kvm/lib/proc_util.c
+++ b/tools/testing/selftests/kvm/lib/proc_util.c
@@ -40,3 +40,23 @@ int get_proc_vfio_irq_number(const char *device_bdf, int msi)
return irq;
}
+FILE *open_proc_irq_affinity(int irq)
+{
+ char path[PATH_MAX];
+ FILE *fp;
+
+ snprintf(path, sizeof(path), "/proc/irq/%d/smp_affinity_list", irq);
+ fp = fopen(path, "w");
+ TEST_ASSERT(fp, "fopen(%s) failed", path);
+
+ return fp;
+}
+
+void write_proc_irq_affinity(FILE *fp, int irq, int irq_cpu)
+{
+ int ret;
+
+ ret = fprintf(fp, "%d\n", irq_cpu);
+ TEST_ASSERT(ret > 0, "Failed to affinitize IRQ-%d to CPU %d", irq, irq_cpu);
+ fflush(fp);
+}
--
2.54.0.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v3 09/16] KVM: selftests: Verify IRQs wake up halted vCPUs
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (7 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 08/16] KVM: selftests: Verify IRQ affinity changes Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-04-21 23:15 ` [PATCH v3 10/16] KVM: selftests: Verify dynamic IRQ routing updates Josh Hilke
` (7 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 '-b' flag to tools/testing/selftests/kvm/irq_test.c to verify
that interrupts successfully wake up vCPUs that are in a halted state.
By default, the guest code spins using cpu_relax() while waiting for
interrupts. With the '-b' flag, the guest executes the HLT instruction
instead. At the end of the test, the host explicitly routes and triggers
a final interrupt for each vCPU. If the interrupt fails to wake the
halted vCPU, the guest will never exit its loop, and the host's call to
pthread_join() will hang, causing the test to time out and fail.
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 | 32 ++++++++++++++++++++++----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index bb124028fb20..d0a48509b932 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -18,6 +18,7 @@ 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 irq_affinity;
+static bool block_vcpus;
static bool done;
static u32 guest_get_vcpu_id(void)
@@ -40,8 +41,12 @@ static void guest_code(void)
WRITE_ONCE(guest_ready_for_irqs[guest_get_vcpu_id()], true);
- while (!READ_ONCE(done))
- cpu_relax();
+ while (!READ_ONCE(done)) {
+ if (block_vcpus)
+ hlt();
+ else
+ cpu_relax();
+ }
GUEST_DONE();
}
@@ -108,10 +113,11 @@ static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu,
static void help(const char *name)
{
- printf("Usage: %s [-a] [-d <segment:bus:device.function>] [-h]\n", name);
+ printf("Usage: %s [-a] [-b] [-d <segment:bus:device.function>] [-h]\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");
+ printf("-b Block vCPUs (e.g. HLT) instead of spinning in guest-mode\n");
printf("-d Use a VFIO device to send MSI-X interrupts instead of using an emulated eventfd\n");
printf("\n");
exit(KSFT_FAIL);
@@ -144,11 +150,14 @@ int main(int argc, char **argv)
struct iommu *iommu;
struct kvm_vm *vm;
- while ((c = getopt(argc, argv, "ad:h")) != -1) {
+ while ((c = getopt(argc, argv, "abd:h")) != -1) {
switch (c) {
case 'a':
irq_affinity = true;
break;
+ case 'b':
+ block_vcpus = true;
+ break;
case 'd':
device_bdf = optarg;
break;
@@ -185,6 +194,8 @@ int main(int argc, char **argv)
kvm_assign_irqfd(vm, gsi, eventfd);
+ sync_global_to_guest(vm, block_vcpus);
+
for (i = 0; i < nr_vcpus; i++)
pthread_create(&vcpu_threads[i], NULL, vcpu_thread_main, vcpus[i]);
@@ -235,8 +246,19 @@ int main(int argc, char **argv)
WRITE_AND_SYNC_TO_GUEST(vm, done, true);
- for (i = 0; i < nr_vcpus; i++)
+ for (i = 0; i < nr_vcpus; i++) {
+ /*
+ * Verify that sending an interrupt to a halted vCPU wakes it
+ * up. If the vCPU does not wake up, the call to pthread_join(),
+ * below, will hang.
+ */
+ if (block_vcpus) {
+ kvm_route_msi(vm, gsi, vcpus[i], vector);
+ trigger_interrupt(device, eventfd);
+ }
+
pthread_join(vcpu_threads[i], NULL);
+ }
if (irq_affinity)
fclose(irq_affinity_fp);
--
2.54.0.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v3 10/16] KVM: selftests: Verify dynamic IRQ routing updates
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (8 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 09/16] KVM: selftests: Verify IRQs wake up halted vCPUs Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-04-21 23:15 ` [PATCH v3 11/16] KVM: selftests: Configure number of IRQs Josh Hilke
` (6 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 '-c' flag to tools/testing/selftests/kvm/irq_test.c to destroy
and recreate KVM's GSI routing table between interrupts. This ensures
that KVM correctly handles dynamic updates to the interrupt routing
table while interrupts are actively being signaled by assigned devices.
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 | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index d0a48509b932..3671f5ce4cc8 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -111,13 +111,21 @@ static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu,
vm_ioctl(vm, KVM_SET_GSI_ROUTING, routes);
}
+static void kvm_clear_gsi_routes(struct kvm_vm *vm)
+{
+ struct kvm_irq_routing routes = {};
+
+ vm_ioctl(vm, KVM_SET_GSI_ROUTING, &routes);
+}
+
static void help(const char *name)
{
- printf("Usage: %s [-a] [-b] [-d <segment:bus:device.function>] [-h]\n", name);
+ printf("Usage: %s [-a] [-b] [-c] [-d <segment:bus:device.function>] [-h]\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");
printf("-b Block vCPUs (e.g. HLT) instead of spinning in guest-mode\n");
+ 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("\n");
exit(KSFT_FAIL);
@@ -147,10 +155,11 @@ int main(int argc, char **argv)
int nr_irqs = 1000, nr_vcpus = 1;
const char *device_bdf = NULL;
FILE *irq_affinity_fp = NULL;
+ bool clear_routes = false;
struct iommu *iommu;
struct kvm_vm *vm;
- while ((c = getopt(argc, argv, "abd:h")) != -1) {
+ while ((c = getopt(argc, argv, "abcd:h")) != -1) {
switch (c) {
case 'a':
irq_affinity = true;
@@ -161,6 +170,9 @@ int main(int argc, char **argv)
case 'd':
device_bdf = optarg;
break;
+ case 'c':
+ clear_routes = true;
+ break;
case 'h':
default:
help(argv[0]);
@@ -207,9 +219,13 @@ int main(int argc, char **argv)
}
for (i = 0; i < nr_irqs; i++) {
+ const bool do_clear_routes = clear_routes && (i & BIT(3));
struct kvm_vcpu *vcpu = vcpus[i % nr_vcpus];
struct timespec start;
+ if (do_clear_routes)
+ kvm_clear_gsi_routes(vm);
+
kvm_route_msi(vm, gsi, vcpu, vector);
if (irq_affinity && vcpu->id == 0) {
--
2.54.0.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v3 11/16] KVM: selftests: Configure number of IRQs
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (9 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 10/16] KVM: selftests: Verify dynamic IRQ routing updates Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-04-21 23:15 ` [PATCH v3 12/16] KVM: selftests: Verify non-postable IRQ remapping Josh Hilke
` (5 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 '-i' flag to tools/testing/selftests/kvm/irq_test.c to allow users
to specify the number of IRQs generated during a test run.
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 3671f5ce4cc8..9efb7e655623 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -120,13 +120,14 @@ 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]\n", name);
+ printf("Usage: %s [-a] [-b] [-c] [-d <segment:bus:device.function>] [-h] [-i nr_irqs]\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");
printf("-b Block vCPUs (e.g. HLT) instead of spinning in guest-mode\n");
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("\n");
exit(KSFT_FAIL);
}
@@ -159,7 +160,7 @@ int main(int argc, char **argv)
struct iommu *iommu;
struct kvm_vm *vm;
- while ((c = getopt(argc, argv, "abcd:h")) != -1) {
+ while ((c = getopt(argc, argv, "abcd:hi:")) != -1) {
switch (c) {
case 'a':
irq_affinity = true;
@@ -173,6 +174,9 @@ int main(int argc, char **argv)
case 'c':
clear_routes = true;
break;
+ case 'i':
+ nr_irqs = atoi_positive("Number of IRQs", optarg);
+ break;
case 'h':
default:
help(argv[0]);
--
2.54.0.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v3 12/16] KVM: selftests: Verify non-postable IRQ remapping
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (10 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 11/16] KVM: selftests: Configure number of IRQs Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-05-27 2:13 ` Sean Christopherson
2026-04-21 23:15 ` [PATCH v3 13/16] KVM: selftests: Verify vCPU migration during IRQ delivery Josh Hilke
` (4 subsequent siblings)
16 siblings, 1 reply; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 -n flag to tools/testing/selftests/kvm/irq_test.c to route a
portion of device interrupts as NMIs (Non-Maskable Interrupts) into the
guest using an alternating pattern of 4 NMIs followed by 4 regular
interrupts.
While this adds coverage for NMI injection, the primary goal is to
validate KVM's handling of non-postable interrupt delivery—specifically
the transitions between posted and remapped modes. NMIs are used for
this purpose because they are a reliable, architectural way to force
these code paths.
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 | 44 ++++++++++++++++++++------
1 file changed, 34 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index 9efb7e655623..03b88b7585b3 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -17,6 +17,7 @@
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 bool irq_affinity;
static bool block_vcpus;
static bool done;
@@ -33,6 +34,11 @@ static void guest_irq_handler(struct ex_regs *regs)
x2apic_write_reg(APIC_EOI, 0);
}
+static void guest_nmi_handler(struct ex_regs *regs)
+{
+ WRITE_ONCE(guest_received_nmi[guest_get_vcpu_id()], true);
+}
+
static void guest_code(void)
{
x2apic_enable();
@@ -93,7 +99,7 @@ static void trigger_interrupt(struct vfio_pci_device *device, int eventfd)
static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu,
- u8 vector)
+ u8 vector, bool use_nmi)
{
struct {
struct kvm_irq_routing head;
@@ -106,7 +112,7 @@ static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu,
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;
+ routes->entries[0].u.msi.data = use_nmi ? NMI_VECTOR | (4 << 8) : vector;
vm_ioctl(vm, KVM_SET_GSI_ROUTING, routes);
}
@@ -120,7 +126,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", name);
+ printf("Usage: %s [-a] [-b] [-c] [-d <segment:bus:device.function>] [-h] [-i nr_irqs] [-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");
@@ -128,6 +134,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("-n Deliver 50 percent of IRQs as non-maskable interrupts\n");
printf("\n");
exit(KSFT_FAIL);
}
@@ -149,6 +156,7 @@ 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);
+ bool clear_routes = false, use_nmi = false;
int i, j, c, msi, irq, eventfd, irq_cpu;
struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
pthread_t vcpu_threads[KVM_MAX_VCPUS];
@@ -156,11 +164,10 @@ int main(int argc, char **argv)
int nr_irqs = 1000, nr_vcpus = 1;
const char *device_bdf = NULL;
FILE *irq_affinity_fp = NULL;
- bool clear_routes = false;
struct iommu *iommu;
struct kvm_vm *vm;
- while ((c = getopt(argc, argv, "abcd:hi:")) != -1) {
+ while ((c = getopt(argc, argv, "abcd:hi:n")) != -1) {
switch (c) {
case 'a':
irq_affinity = true;
@@ -177,6 +184,9 @@ int main(int argc, char **argv)
case 'i':
nr_irqs = atoi_positive("Number of IRQs", optarg);
break;
+ case 'n':
+ use_nmi = true;
+ break;
case 'h':
default:
help(argv[0]);
@@ -187,6 +197,7 @@ int main(int argc, char **argv)
vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
vm_install_exception_handler(vm, vector, guest_irq_handler);
+ vm_install_exception_handler(vm, NMI_VECTOR, guest_nmi_handler);
if (device_bdf) {
iommu = iommu_init(default_iommu_mode);
@@ -224,35 +235,45 @@ int main(int argc, char **argv)
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));
struct kvm_vcpu *vcpu = vcpus[i % nr_vcpus];
struct timespec start;
if (do_clear_routes)
kvm_clear_gsi_routes(vm);
- kvm_route_msi(vm, gsi, vcpu, vector);
+ kvm_route_msi(vm, gsi, vcpu, vector, do_use_nmi);
if (irq_affinity && vcpu->id == 0) {
irq_cpu = kvm_random_u64(&kvm_rng) % get_nprocs();
write_proc_irq_affinity(irq_affinity_fp, irq, irq_cpu);
}
- for (j = 0; j < nr_vcpus; j++)
+ for (j = 0; j < nr_vcpus; j++) {
TEST_ASSERT(
!SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpus[j]->id]),
"IRQ flag for vCPU %d not clear prior to test",
vcpus[j]->id);
+ TEST_ASSERT(
+ !SYNC_FROM_GUEST_AND_READ(vm, guest_received_nmi[vcpus[j]->id]),
+ "NMI flag for vCPU %d not clear prior to test",
+ vcpus[j]->id);
+ }
trigger_interrupt(device, eventfd);
clock_gettime(CLOCK_MONOTONIC, &start);
for (;;) {
- if (SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpu->id]))
+ if (!do_use_nmi && SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpu->id]))
+ break;
+
+ if (do_use_nmi && SYNC_FROM_GUEST_AND_READ(vm, guest_received_nmi[vcpu->id]))
break;
if (timespec_to_ns(timespec_elapsed(start)) > timeout_ns) {
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)
printf(" irq_cpu: %d\n", irq_cpu);
@@ -261,7 +282,10 @@ int main(int argc, char **argv)
}
}
- WRITE_AND_SYNC_TO_GUEST(vm, guest_received_irq[vcpu->id], false);
+ if (do_use_nmi)
+ WRITE_AND_SYNC_TO_GUEST(vm, guest_received_nmi[vcpu->id], false);
+ else
+ WRITE_AND_SYNC_TO_GUEST(vm, guest_received_irq[vcpu->id], false);
}
WRITE_AND_SYNC_TO_GUEST(vm, done, true);
@@ -273,7 +297,7 @@ int main(int argc, char **argv)
* below, will hang.
*/
if (block_vcpus) {
- kvm_route_msi(vm, gsi, vcpus[i], vector);
+ kvm_route_msi(vm, gsi, vcpus[i], vector, false);
trigger_interrupt(device, eventfd);
}
--
2.54.0.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v3 12/16] KVM: selftests: Verify non-postable IRQ remapping
2026-04-21 23:15 ` [PATCH v3 12/16] KVM: selftests: Verify non-postable IRQ remapping Josh Hilke
@ 2026-05-27 2:13 ` Sean Christopherson
2026-05-28 23:03 ` Josh Hilke
0 siblings, 1 reply; 32+ messages in thread
From: Sean Christopherson @ 2026-05-27 2:13 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, Apr 21, 2026, Josh Hilke wrote:
> - for (j = 0; j < nr_vcpus; j++)
> + for (j = 0; j < nr_vcpus; j++) {
> TEST_ASSERT(
> !SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpus[j]->id]),
> "IRQ flag for vCPU %d not clear prior to test",
> vcpus[j]->id);
> + TEST_ASSERT(
A very special kind of hell.
> + !SYNC_FROM_GUEST_AND_READ(vm, guest_received_nmi[vcpus[j]->id]),
Add a macro for this one too?
> + "NMI flag for vCPU %d not clear prior to test",
> + vcpus[j]->id);
> + }
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v3 12/16] KVM: selftests: Verify non-postable IRQ remapping
2026-05-27 2:13 ` Sean Christopherson
@ 2026-05-28 23:03 ` Josh Hilke
0 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-05-28 23:03 UTC (permalink / raw)
To: Sean Christopherson
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, May 26, 2026 at 7:13 PM Sean Christopherson <seanjc@google.com> wrote:
>
> On Tue, Apr 21, 2026, Josh Hilke wrote:
> > - for (j = 0; j < nr_vcpus; j++)
> > + for (j = 0; j < nr_vcpus; j++) {
> > TEST_ASSERT(
> > !SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpus[j]->id]),
> > "IRQ flag for vCPU %d not clear prior to test",
> > vcpus[j]->id);
> > + TEST_ASSERT(
>
> A very special kind of hell.
I'm out of Dante's Inferno references. Will fix in v4.
>
> > + !SYNC_FROM_GUEST_AND_READ(vm, guest_received_nmi[vcpus[j]->id]),
>
> Add a macro for this one too?
Will do.
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v3 13/16] KVM: selftests: Verify vCPU migration during IRQ delivery
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (11 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 12/16] KVM: selftests: Verify non-postable IRQ remapping Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-05-27 2:23 ` Sean Christopherson
2026-04-21 23:15 ` [PATCH v3 14/16] KVM: selftests: Print vCPU affinity on timeout Josh Hilke
` (3 subsequent siblings)
16 siblings, 1 reply; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 '-m' flag to irq_test 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.
To support this, add wrappers for gettid() and sched_getaffinity(), and
introduce pin_task_to_random_cpu() in lib/kvm_util.c.
Co-developed-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: Josh Hilke <jrhilke@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
---
.../selftests/kvm/include/kvm_syscalls.h | 6 ++++
.../testing/selftests/kvm/include/kvm_util.h | 2 ++
tools/testing/selftests/kvm/irq_test.c | 34 +++++++++++++++++--
tools/testing/selftests/kvm/lib/kvm_util.c | 19 +++++++++++
4 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/kvm_syscalls.h b/tools/testing/selftests/kvm/include/kvm_syscalls.h
index d4e613162bba..d73f45c5df92 100644
--- a/tools/testing/selftests/kvm/include/kvm_syscalls.h
+++ b/tools/testing/selftests/kvm/include/kvm_syscalls.h
@@ -73,9 +73,15 @@ static inline int kvm_dup(int fd)
return new_fd;
}
+static inline int gettid(void)
+{
+ return syscall(__NR_gettid);
+}
+
__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);
#endif /* SELFTEST_KVM_SYSCALLS_H */
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/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index 03b88b7585b3..dd6a08d3bf4f 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;
@@ -62,12 +64,23 @@ static void *vcpu_thread_main(void *arg)
struct kvm_vcpu *vcpu = arg;
struct ucall uc;
+ WRITE_ONCE(vcpu_tids[vcpu->id], 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;
@@ -126,7 +139,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");
@@ -134,6 +147,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);
@@ -164,10 +178,11 @@ int main(int argc, char **argv)
int nr_irqs = 1000, nr_vcpus = 1;
const char *device_bdf = NULL;
FILE *irq_affinity_fp = NULL;
+ cpu_set_t available_cpus;
struct iommu *iommu;
struct kvm_vm *vm;
- 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;
@@ -184,6 +199,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;
@@ -233,6 +251,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));
@@ -249,6 +276,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(
!SYNC_FROM_GUEST_AND_READ(vm, guest_received_irq[vcpus[j]->id]),
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index b247b2015b2a..83732c07dfda 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -662,6 +662,25 @@ 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 i = 0, nr_cpus = CPU_COUNT(possible_cpus);
+ int cpu, target_idx;
+
+ target_idx = kvm_random_u64(&kvm_rng) % nr_cpus;
+
+ for (cpu = 0; i < nr_cpus; cpu++) {
+ if (!CPU_ISSET(cpu, possible_cpus))
+ continue;
+
+ if (i == target_idx) {
+ pin_task_to_cpu(task, cpu);
+ return;
+ }
+ i++;
+ }
+}
+
void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[],
int nr_vcpus)
{
--
2.54.0.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v3 13/16] KVM: selftests: Verify vCPU migration during IRQ delivery
2026-04-21 23:15 ` [PATCH v3 13/16] KVM: selftests: Verify vCPU migration during IRQ delivery Josh Hilke
@ 2026-05-27 2:23 ` Sean Christopherson
2026-05-28 23:05 ` Josh Hilke
0 siblings, 1 reply; 32+ messages in thread
From: Sean Christopherson @ 2026-05-27 2:23 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, Apr 21, 2026, Josh Hilke wrote:
> From: David Matlack <dmatlack@google.com>
>
> Add the '-m' flag to irq_test 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.
>
> To support this, add wrappers for gettid() and sched_getaffinity(), and
> introduce pin_task_to_random_cpu() in lib/kvm_util.c.
>
> Co-developed-by: Josh Hilke <jrhilke@google.com>
> Signed-off-by: Josh Hilke <jrhilke@google.com>
> Signed-off-by: David Matlack <dmatlack@google.com>
> ---
> .../selftests/kvm/include/kvm_syscalls.h | 6 ++++
> .../testing/selftests/kvm/include/kvm_util.h | 2 ++
> tools/testing/selftests/kvm/irq_test.c | 34 +++++++++++++++++--
> tools/testing/selftests/kvm/lib/kvm_util.c | 19 +++++++++++
> 4 files changed, 59 insertions(+), 2 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/include/kvm_syscalls.h b/tools/testing/selftests/kvm/include/kvm_syscalls.h
> index d4e613162bba..d73f45c5df92 100644
> --- a/tools/testing/selftests/kvm/include/kvm_syscalls.h
> +++ b/tools/testing/selftests/kvm/include/kvm_syscalls.h
> @@ -73,9 +73,15 @@ static inline int kvm_dup(int fd)
> return new_fd;
> }
>
> +static inline int gettid(void)
gettid() already exists in at least some of my environments:
include/kvm_syscalls.h:87:19: error: static declaration of ‘gettid’ follows non-static declaration
87 | static inline int gettid(void)
| ^~~~~~
In file included from /usr/include/unistd.h:1221,
from /usr/include/x86_64-linux-gnu/bits/sigstksz.h:24,
from /usr/include/signal.h:328,
from include/test_util.h:12,
from lib/kvm_util.c:7:
/usr/include/x86_64-linux-gnu/bits/unistd_ext.h:34:16: note: previous declaration of ‘gettid’ with type ‘__pid_t(void)’ {aka ‘int(void)’}
34 | extern __pid_t gettid (void) __THROW;
| ^~~~~~
> +void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus)
> +{
> + int i = 0, nr_cpus = CPU_COUNT(possible_cpus);
> + int cpu, target_idx;
> +
> + target_idx = kvm_random_u64(&kvm_rng) % nr_cpus;
> +
> + for (cpu = 0; i < nr_cpus; cpu++) {
Shouldn't this be bounded on "cpu < nr_cpus"? If 'i' gets to nr_cpus, it means
"i == target_idx" was never taken. And at that point, the loop will hang
indefinitely because cpu will likely be waaaay out of bounds of possible_cpus,
i.e. the loop will always continue.
> + if (!CPU_ISSET(cpu, possible_cpus))
> + continue;
> +
> + if (i == target_idx) {
> + pin_task_to_cpu(task, cpu);
> + return;
> + }
> + i++;
> + }
This should TEST_FAIL(). Or maybe "break" on the match, and the assert that
i == target_idx?
> +}
> +
> void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[],
> int nr_vcpus)
> {
> --
> 2.54.0.rc2.533.g4f5dca5207-goog
>
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v3 13/16] KVM: selftests: Verify vCPU migration during IRQ delivery
2026-05-27 2:23 ` Sean Christopherson
@ 2026-05-28 23:05 ` Josh Hilke
2026-05-28 23:24 ` Sean Christopherson
0 siblings, 1 reply; 32+ messages in thread
From: Josh Hilke @ 2026-05-28 23:05 UTC (permalink / raw)
To: Sean Christopherson
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, May 26, 2026 at 7:23 PM Sean Christopherson <seanjc@google.com> wrote:
>
> On Tue, Apr 21, 2026, Josh Hilke wrote:
> > +static inline int gettid(void)
>
> gettid() already exists in at least some of my environments:
Should we just use syscall(__NR_gettid) to avoid any compatibility
issues? It's only used once in the test.
> > +void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus)
> > +{
> > + int i = 0, nr_cpus = CPU_COUNT(possible_cpus);
> > + int cpu, target_idx;
> > +
> > + target_idx = kvm_random_u64(&kvm_rng) % nr_cpus;
> > +
> > + for (cpu = 0; i < nr_cpus; cpu++) {
>
> Shouldn't this be bounded on "cpu < nr_cpus"? If 'i' gets to nr_cpus, it means
> "i == target_idx" was never taken. And at that point, the loop will hang
> indefinitely because cpu will likely be waaaay out of bounds of possible_cpus,
> i.e. the loop will always continue.
>
> > + if (!CPU_ISSET(cpu, possible_cpus))
> > + continue;
> > +
> > + if (i == target_idx) {
> > + pin_task_to_cpu(task, cpu);
> > + return;
> > + }
> > + i++;
> > + }
Ahh, true. I think it actually needs to be bounded on "cpu <
CPU_SETSIZE" so that we iterate through all of the CPUs.
> This should TEST_FAIL(). Or maybe "break" on the match, and the assert that
> i == target_idx?
Will do
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v3 13/16] KVM: selftests: Verify vCPU migration during IRQ delivery
2026-05-28 23:05 ` Josh Hilke
@ 2026-05-28 23:24 ` Sean Christopherson
0 siblings, 0 replies; 32+ messages in thread
From: Sean Christopherson @ 2026-05-28 23:24 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Thu, May 28, 2026, Josh Hilke wrote:
> On Tue, May 26, 2026 at 7:23 PM Sean Christopherson <seanjc@google.com> wrote:
> >
> > On Tue, Apr 21, 2026, Josh Hilke wrote:
> > > +static inline int gettid(void)
> >
> > gettid() already exists in at least some of my environments:
>
> Should we just use syscall(__NR_gettid) to avoid any compatibility
> issues? It's only used once in the test.
Hmm, maybe open code it in kvm_gettid()? And use it straightaway in
tools/testing/selftests/kvm/demand_paging_test.c.
I'm a-ok open coding the syscall() somewhere, but I don't want to end up in a
state where we're open coding it "everywhere".
Oh, nice, "This call is always successful.". All the more reason to open code
it as kvm_gettid(), as the kvm_ prefix communicates that the function guarantees
success (or fails the test), it just so happens that we don't need to do anything
to guarantee success. E.g.
static inline pid_t kvm_gettid(void)
{
/* Per kernel documentation, gettid "is always successful". */
return syscall(__NR_gettid);
}
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v3 14/16] KVM: selftests: Print vCPU affinity on timeout
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (12 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 13/16] KVM: selftests: Verify vCPU migration during IRQ delivery Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-04-21 23:15 ` [PATCH v3 15/16] KVM: selftests: Configure number of vCPUs Josh Hilke
` (2 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 dd6a08d3bf4f..43338eb3ab41 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -306,6 +306,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 83732c07dfda..f85e061f47fb 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 i = 0, nr_cpus = CPU_COUNT(possible_cpus);
--
2.54.0.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v3 15/16] KVM: selftests: Configure number of vCPUs
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (13 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 14/16] KVM: selftests: Print vCPU affinity on timeout Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-04-21 23:15 ` [PATCH v3 16/16] KVM: selftests: Add xAPIC support Josh Hilke
2026-05-27 1:50 ` [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Sean Christopherson
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 43338eb3ab41..6fd082d37f9a 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -139,7 +139,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");
@@ -149,6 +149,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);
}
@@ -182,7 +183,7 @@ int main(int argc, char **argv)
struct iommu *iommu;
struct kvm_vm *vm;
- 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;
@@ -205,6 +206,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.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v3 16/16] KVM: selftests: Add xAPIC support
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (14 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 15/16] KVM: selftests: Configure number of vCPUs Josh Hilke
@ 2026-04-21 23:15 ` Josh Hilke
2026-05-27 1:50 ` [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Sean Christopherson
16 siblings, 0 replies; 32+ messages in thread
From: Josh Hilke @ 2026-04-21 23:15 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 6fd082d37f9a..99698cf143fe 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -20,20 +20,27 @@ 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;
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)
@@ -43,7 +50,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();
@@ -139,7 +149,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");
@@ -150,6 +160,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);
}
@@ -183,7 +194,7 @@ int main(int argc, char **argv)
struct iommu *iommu;
struct kvm_vm *vm;
- 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;
@@ -209,6 +220,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]);
@@ -221,6 +235,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);
@@ -243,6 +260,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.rc2.533.g4f5dca5207-goog
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts
2026-04-21 23:15 [PATCH v3 00/16] KVM: selftests: Link with VFIO selftests lib and test device interrupts Josh Hilke
` (15 preceding siblings ...)
2026-04-21 23:15 ` [PATCH v3 16/16] KVM: selftests: Add xAPIC support Josh Hilke
@ 2026-05-27 1:50 ` Sean Christopherson
16 siblings, 0 replies; 32+ messages in thread
From: Sean Christopherson @ 2026-05-27 1:50 UTC (permalink / raw)
To: Josh Hilke
Cc: Paolo Bonzini, kvm, linux-kernel, David Matlack, Alex Williamson
On Tue, Apr 21, 2026, Josh Hilke wrote:
> v3:
> - Split monolithic test patch into smaller, logical patches (Sean).
Needs to go farther. E.g. "KVM: selftests: Verify vCPU migration during IRQ delivery"
should be ~4 patches:
1. Add gettid() wrapper (because what you proposed doesn't compile for me).
2. Add sched_getaffinity() wrapper *AND* convert existing users.
3. Add pin_task_to_random_cpu()
4. Implement IRQ bypass vCPU migration
Maybe combine #3 and #4? Adding helpers/APIs without users isn't ideal, but
pin_task_to_random_cpu() is interesting enough that I think it's worth isolating
in this case.
> David Matlack (13):
> KVM: selftests: Build and link sefltests/vfio/lib into KVM selftests
> KVM: selftests: Add /proc/interrupts parsing helpers
> KVM: selftests: Add guest read/write macros
> KVM: selftests: Add IRQ injection test
> KVM: selftests: Verify device IRQs are routed to vCPUs
> KVM: selftests: Verify IRQ affinity changes
> KVM: selftests: Verify IRQs wake up halted vCPUs
> KVM: selftests: Verify dynamic IRQ routing updates
> KVM: selftests: Configure number of IRQs
> KVM: selftests: Verify non-postable IRQ remapping
> KVM: selftests: Verify vCPU migration during IRQ delivery
> KVM: selftests: Configure number of vCPUs
> KVM: selftests: Add xAPIC support
All of these shortlogs need more context. E.g. "Add xAPIC support" suggests the
patch adds xAPIC support to _all_ of selftests. We generally discourage trying
to set the scope to a specific test, because it inevitably falls apart, but it's
easy enough to "manually" call out the relevant test.
KVM: selftests: Add xAPIC support to the IRQ bypass test
> Josh Hilke (3):
> KVM: selftests: Rename guest_rng to kvm_rng
> KVM: selftests: Add helper to generate random u64 in range [min,max]
> KVM: selftests: Print vCPU affinity on timeout
>
> tools/testing/selftests/kvm/Makefile.kvm | 7 +-
> .../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 | 26 +-
> .../selftests/kvm/include/x86/kvm_util_arch.h | 4 +-
> tools/testing/selftests/kvm/irq_test.c | 367 ++++++++++++++++++
> tools/testing/selftests/kvm/lib/kvm_util.c | 88 ++++-
> 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 | 22 +-
> tools/testing/selftests/kvm/mmu_stress_test.c | 9 +-
> tools/testing/selftests/kvm/steal_time.c | 21 +-
> 15 files changed, 615 insertions(+), 58 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.rc2.533.g4f5dca5207-goog
>
^ permalink raw reply [flat|nested] 32+ messages in thread