[PATCH v4 08/19] KVM: selftests: Verify interrupts are received when IRQ affinity changes in IRQ test
From: Josh Hilke
Date: Fri May 29 2026 - 20:30:37 EST
From: David Matlack <dmatlack@xxxxxxxxxx>
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@xxxxxxxxxx>
Signed-off-by: Josh Hilke <jrhilke@xxxxxxxxxx>
Signed-off-by: David Matlack <dmatlack@xxxxxxxxxx>
---
.../testing/selftests/kvm/include/proc_util.h | 19 +++++++++++
tools/testing/selftests/kvm/irq_test.c | 34 ++++++++++++++++---
tools/testing/selftests/kvm/lib/proc_util.c | 20 +++++++++++
3 files changed, 69 insertions(+), 4 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 616eb38c05b8..e76784e0a054 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;
#define GUEST_RECEIVED_IRQ(__vcpu) \
@@ -109,9 +111,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);
@@ -134,17 +137,21 @@ int main(int argc, char **argv)
u32 gsi = kvm_random_u64_in_range(&kvm_rng, 24, KVM_MAX_IRQ_ROUTES - 1);
u8 vector = kvm_random_u64_in_range(&kvm_rng, 32, UINT8_MAX);
+ int i, j, c, msi, irq, eventfd, irq_cpu;
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;
@@ -171,6 +178,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);
@@ -192,6 +204,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(!GUEST_RECEIVED_IRQ(vcpus[j]),
"IRQ flag for vCPU %d not clear prior to test",
@@ -204,9 +221,15 @@ int main(int argc, char **argv)
if (GUEST_RECEIVED_IRQ(vcpu))
break;
- if (timespec_to_ns(timespec_elapsed(start)) > timeout_ns)
+ 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);
@@ -217,6 +240,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.929.g9b7fa37559-goog