AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/06/27 01:39 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies user-space selftests in tools/testing/selftests/kvm/ and does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

1/1 2026/06/27 01:39 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 8fe60110dced7a96c793c7cd3c0c4f264949d0ff\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sat Jun 27 01:39:34 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm\nindex 4ace12606e937b..88c6c8046ddec0 100644\n--- a/tools/testing/selftests/kvm/Makefile.kvm\n+++ b/tools/testing/selftests/kvm/Makefile.kvm\n@@ -11,6 +11,7 @@ LIBKVM += lib/kvm_util.c\n LIBKVM += lib/lru_gen_util.c\n LIBKVM += lib/memstress.c\n LIBKVM += lib/guest_sprintf.c\n+LIBKVM += lib/proc_util.c\n LIBKVM += lib/rbtree.c\n LIBKVM += lib/sparsebit.c\n LIBKVM += lib/test_util.c\n@@ -157,6 +158,7 @@ TEST_GEN_PROGS_x86 += coalesced_io_test\n TEST_GEN_PROGS_x86 += dirty_log_perf_test\n TEST_GEN_PROGS_x86 += guest_memfd_test\n TEST_GEN_PROGS_x86 += hardware_disable_test\n+TEST_GEN_PROGS_x86 += irq_test\n TEST_GEN_PROGS_x86 += mmu_stress_test\n TEST_GEN_PROGS_x86 += rseq_test\n TEST_GEN_PROGS_x86 += steal_time\n@@ -259,6 +261,7 @@ OVERRIDE_TARGETS = 1\n # which causes the environment variable to override the makefile).\n include ../lib.mk\n include ../cgroup/lib/libcgroup.mk\n+include ../vfio/lib/libvfio.mk\n \n INSTALL_HDR_PATH = $(top_srcdir)/usr\n LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/\n@@ -313,7 +316,9 @@ LIBKVM_S := $(filter %.S,$(LIBKVM))\n LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C))\n LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S))\n LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING))\n-LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) $(LIBCGROUP_O)\n+LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ)\n+LIBKVM_OBJS += $(LIBCGROUP_O)\n+LIBKVM_OBJS += $(LIBVFIO_O)\n SPLIT_TEST_GEN_PROGS := $(patsubst %, $(OUTPUT)/%, $(SPLIT_TESTS))\n SPLIT_TEST_GEN_OBJ := $(patsubst %, $(OUTPUT)/$(ARCH)/%.o, $(SPLIT_TESTS))\n \ndiff --git a/tools/testing/selftests/kvm/arch_timer.c b/tools/testing/selftests/kvm/arch_timer.c\nindex 90c475a61b22b6..f8b02597897bd8 100644\n--- a/tools/testing/selftests/kvm/arch_timer.c\n+++ b/tools/testing/selftests/kvm/arch_timer.c\n@@ -85,7 +85,7 @@ static u32 test_get_pcpu(void)\n \tcpu_set_t online_cpuset;\n \n \tnproc_conf = get_nprocs_conf();\n-\tsched_getaffinity(0, sizeof(cpu_set_t), \u0026online_cpuset);\n+\tkvm_sched_getaffinity(0, sizeof(cpu_set_t), \u0026online_cpuset);\n \n \t/* Randomly find an available pCPU to place a vCPU on */\n \tdo {\ndiff --git a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c\nindex f7625eb711d6b8..d9c9377a63256d 100644\n--- a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c\n+++ b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c\n@@ -1039,7 +1039,7 @@ int main(int argc, char *argv[])\n \tif (!parse_args(argc, argv))\n \t\texit(KSFT_SKIP);\n \n-\tsched_getaffinity(0, sizeof(default_cpuset), \u0026default_cpuset);\n+\tkvm_sched_getaffinity(0, sizeof(default_cpuset), \u0026default_cpuset);\n \tset_counter_defaults();\n \n \tif (test_args.test_virtual) {\ndiff --git a/tools/testing/selftests/kvm/demand_paging_test.c b/tools/testing/selftests/kvm/demand_paging_test.c\nindex 302c4923d0930f..f8b3d0b68830d7 100644\n--- a/tools/testing/selftests/kvm/demand_paging_test.c\n+++ b/tools/testing/selftests/kvm/demand_paging_test.c\n@@ -57,7 +57,7 @@ static void vcpu_worker(struct memstress_vcpu_args *vcpu_args)\n static int handle_uffd_page_request(int uffd_mode, int uffd,\n \t\tstruct uffd_msg *msg)\n {\n-\tpid_t tid = syscall(__NR_gettid);\n+\tpid_t tid = kvm_gettid();\n \tu64 addr = msg-\u003earg.pagefault.address;\n \tstruct timespec start;\n \tstruct timespec ts_diff;\ndiff --git a/tools/testing/selftests/kvm/dirty_log_perf_test.c b/tools/testing/selftests/kvm/dirty_log_perf_test.c\nindex ef779fa91827c9..7c5abe1ae9e018 100644\n--- a/tools/testing/selftests/kvm/dirty_log_perf_test.c\n+++ b/tools/testing/selftests/kvm/dirty_log_perf_test.c\n@@ -311,7 +311,7 @@ int main(int argc, char *argv[])\n \tint opt;\n \n \t/* Override the seed to be deterministic by default. */\n-\tguest_random_seed = 1;\n+\tkvm_random_seed = 1;\n \n \tdirty_log_manual_caps =\n \t\tkvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);\n@@ -357,7 +357,7 @@ int main(int argc, char *argv[])\n \t\t\tp.phys_offset = strtoull(optarg, NULL, 0);\n \t\t\tbreak;\n \t\tcase 'r':\n-\t\t\tguest_random_seed = atoi_positive(\"Random seed\", optarg);\n+\t\t\tkvm_random_seed = atoi_positive(\"Random seed\", optarg);\n \t\t\tbreak;\n \t\tcase 's':\n \t\t\tp.backing_src = parse_backing_src_type(optarg);\ndiff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c\nindex 74ca096bf976b5..e8419d7da1eab5 100644\n--- a/tools/testing/selftests/kvm/dirty_log_test.c\n+++ b/tools/testing/selftests/kvm/dirty_log_test.c\n@@ -121,7 +121,7 @@ static void guest_code(void)\n \twhile (true) {\n \t\twhile (!READ_ONCE(vcpu_stop)) {\n \t\t\taddr = guest_test_virt_mem;\n-\t\t\taddr += (guest_random_u64(\u0026guest_rng) % guest_num_pages)\n+\t\t\taddr += (kvm_random_u64(\u0026kvm_rng) % guest_num_pages)\n \t\t\t\t* guest_page_size;\n \t\t\taddr = align_down(addr, host_page_size);\n \n@@ -708,8 +708,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)\n \n \t\tsync_global_to_guest(vm, iteration);\n \n-\t\tWRITE_ONCE(nr_writes, 0);\n-\t\tsync_global_to_guest(vm, nr_writes);\n+\t\tWRITE_AND_SYNC_TO_GUEST(vm, nr_writes, 0);\n \n \t\tdirty_ring_prev_iteration_last_page = dirty_ring_last_page;\n \t\tWRITE_ONCE(dirty_ring_vcpu_ring_full, false);\n@@ -775,16 +774,14 @@ static void run_test(enum vm_guest_mode mode, void *arg)\n \t\t * writing memory during verification, pages that this thread\n \t\t * sees as clean may be written with this iteration's value.\n \t\t */\n-\t\tWRITE_ONCE(vcpu_stop, true);\n-\t\tsync_global_to_guest(vm, vcpu_stop);\n+\t\tWRITE_AND_SYNC_TO_GUEST(vm, vcpu_stop, true);\n \t\tsem_wait(\u0026sem_vcpu_stop);\n \n \t\t/*\n \t\t * Clear vcpu_stop after the vCPU thread has acknowledge the\n \t\t * stop request and is waiting, i.e. is definitely not running!\n \t\t */\n-\t\tWRITE_ONCE(vcpu_stop, false);\n-\t\tsync_global_to_guest(vm, vcpu_stop);\n+\t\tWRITE_AND_SYNC_TO_GUEST(vm, vcpu_stop, false);\n \n \t\t/*\n \t\t * Sync the number of writes performed before verification, the\ndiff --git a/tools/testing/selftests/kvm/include/kvm_syscalls.h b/tools/testing/selftests/kvm/include/kvm_syscalls.h\nindex 6cb3bed29b81ab..5dae6143ddb04b 100644\n--- a/tools/testing/selftests/kvm/include/kvm_syscalls.h\n+++ b/tools/testing/selftests/kvm/include/kvm_syscalls.h\n@@ -12,6 +12,7 @@\n #include \u003csys/mman.h\u003e\n #include \u003csys/syscall.h\u003e\n \n+#include \u003csched.h\u003e\n #include \u003ctest_util.h\u003e\n \n #define MAP_ARGS0(m,...)\n@@ -83,11 +84,17 @@ static inline int kvm_dup(int fd)\n \treturn new_fd;\n }\n \n+static inline pid_t kvm_gettid(void)\n+{\n+\treturn syscall(__NR_gettid);\n+}\n+\n __KVM_SYSCALL_DEFINE(munmap, 2, void *, mem, size_t, size);\n __KVM_SYSCALL_DEFINE(close, 1, int, fd);\n __KVM_SYSCALL_DEFINE(fallocate, 4, int, fd, int, mode, loff_t, offset, loff_t, len);\n __KVM_SYSCALL_DEFINE(ftruncate, 2, unsigned int, fd, off_t, length);\n __KVM_SYSCALL_DEFINE(madvise, 3, void *, addr, size_t, length, int, advice);\n+__KVM_SYSCALL_DEFINE(sched_getaffinity, 3, pid_t, pid, size_t, cpusetsize, cpu_set_t *, mask);\n \n #define kvm_free_fd(fd)\t\t\\\n do {\t\t\t\t\\\ndiff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h\nindex 04a910164a296a..b39e713c30a469 100644\n--- a/tools/testing/selftests/kvm/include/kvm_util.h\n+++ b/tools/testing/selftests/kvm/include/kvm_util.h\n@@ -1094,6 +1094,8 @@ static inline void pin_task_to_cpu(pthread_t task, int cpu)\n \tTEST_ASSERT(!r, \"Failed to set thread affinity to pCPU '%u'\", cpu);\n }\n \n+void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus);\n+\n static inline int pin_task_to_any_cpu(pthread_t task)\n {\n \tint cpu = sched_getcpu();\n@@ -1138,6 +1140,16 @@ vm_adjust_num_guest_pages(enum vm_guest_mode mode, unsigned int num_guest_pages)\n \tmemcpy(\u0026(g), _p, sizeof(g));\t\t\t\t\\\n })\n \n+#define SYNC_FROM_GUEST_AND_READ(_vm, _variable) ({\t\t\\\n+\tsync_global_from_guest(_vm, _variable);\t\t\t\\\n+\tREAD_ONCE(_variable);\t\t\t\t\t\\\n+})\n+\n+#define WRITE_AND_SYNC_TO_GUEST(_vm, _variable, _value) do {\t\\\n+\tWRITE_ONCE(_variable, _value);\t\t\t\t\\\n+\tsync_global_to_guest(_vm, _variable);\t\t\t\\\n+} while (0)\n+\n /*\n  * Write a global value, but only in the VM's (guest's) domain.  Primarily used\n  * for \"globals\" that hold per-VM values (VMs always duplicate code and global\ndiff --git a/tools/testing/selftests/kvm/include/proc_util.h b/tools/testing/selftests/kvm/include/proc_util.h\nnew file mode 100644\nindex 00000000000000..d1ddc967d11dc5\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/include/proc_util.h\n@@ -0,0 +1,11 @@\n+/* SPDX-License-Identifier: GPL-2.0-only */\n+#ifndef SELFTEST_KVM_PROC_UTIL_H\n+#define SELFTEST_KVM_PROC_UTIL_H\n+\n+#include \u003cstdint.h\u003e\n+\n+unsigned int vfio_msix_to_host_irq(const char *vfio_device_bdf, int msix);\n+\n+void proc_irq_set_smp_affinity(unsigned int irq, int cpu);\n+\n+#endif /* SELFTEST_KVM_PROC_UTIL_H */\ndiff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h\nindex a56271c237ae9e..d64c8a228207a7 100644\n--- a/tools/testing/selftests/kvm/include/test_util.h\n+++ b/tools/testing/selftests/kvm/include/test_util.h\n@@ -108,32 +108,35 @@ struct timespec timespec_sub(struct timespec ts1, struct timespec ts2);\n struct timespec timespec_elapsed(struct timespec start);\n struct timespec timespec_div(struct timespec ts, int divisor);\n \n-struct guest_random_state {\n+struct kvm_random_state {\n \tu32 seed;\n };\n \n-extern u32 guest_random_seed;\n-extern struct guest_random_state guest_rng;\n+extern u32 kvm_random_seed;\n+extern struct kvm_random_state kvm_rng;\n \n-struct guest_random_state new_guest_random_state(u32 seed);\n-u32 guest_random_u32(struct guest_random_state *state);\n+struct kvm_random_state new_kvm_random_state(u32 seed);\n+u32 kvm_random_u32(struct kvm_random_state *state);\n \n-static inline bool __guest_random_bool(struct guest_random_state *state,\n+static inline bool __kvm_random_bool(struct kvm_random_state *state,\n \t\t\t\t       u8 percent)\n {\n-\treturn (guest_random_u32(state) % 100) \u003c percent;\n+\treturn (kvm_random_u32(state) % 100) \u003c percent;\n }\n \n-static inline bool guest_random_bool(struct guest_random_state *state)\n+static inline bool kvm_random_bool(struct kvm_random_state *state)\n {\n-\treturn __guest_random_bool(state, 50);\n+\treturn __kvm_random_bool(state, 50);\n }\n \n-static inline u64 guest_random_u64(struct guest_random_state *state)\n+static inline u64 kvm_random_u64(struct kvm_random_state *state)\n {\n-\treturn ((u64)guest_random_u32(state) \u003c\u003c 32) | guest_random_u32(state);\n+\treturn ((u64)kvm_random_u32(state) \u003c\u003c 32) | kvm_random_u32(state);\n }\n \n+u64 kvm_random_u64_in_range(struct kvm_random_state *state, u64 min,\n+\t\t\t    u64 max);\n+\n enum vm_mem_backing_src_type {\n \tVM_MEM_SRC_ANONYMOUS,\n \tVM_MEM_SRC_ANONYMOUS_THP,\ndiff --git a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h b/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h\nindex c33ab6e0417179..6904dbda79f920 100644\n--- a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h\n+++ b/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h\n@@ -55,9 +55,9 @@ static inline bool __vm_arch_has_protected_memory(struct kvm_vm_arch *arch)\n do {\t\t\t\t\t\t\t\t\t\t\t\\\n \tconst typeof(mem) val = (__val);\t\t\t\t\t\t\\\n \t\t\t\t\t\t\t\t\t\t\t\\\n-\tif (!is_forced_emulation_enabled || guest_random_bool(\u0026guest_rng)) {\t\t\\\n+\tif (!is_forced_emulation_enabled || kvm_random_bool(\u0026kvm_rng)) {\t\t\\\n \t\t(mem) = val;\t\t\t\t\t\t\t\t\\\n-\t} else if (guest_random_bool(\u0026guest_rng)) {\t\t\t\t\t\\\n+\t} else if (kvm_random_bool(\u0026kvm_rng)) {\t\t\t\t\t\\\n \t\t__asm__ __volatile__(KVM_FEP \"mov %1, %0\"\t\t\t\t\\\n \t\t\t\t     : \"+m\" (mem)\t\t\t\t\t\\\n \t\t\t\t     : \"r\" (val) : \"memory\");\t\t\t\t\\\ndiff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c\nnew file mode 100644\nindex 00000000000000..240f6f0fdbe43e\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/irq_test.c\n@@ -0,0 +1,362 @@\n+// SPDX-License-Identifier: GPL-2.0\n+#include \"kvm_util.h\"\n+#include \"test_util.h\"\n+#include \"apic.h\"\n+#include \"processor.h\"\n+#include \"proc_util.h\"\n+\n+#include \u003clibvfio.h\u003e\n+#include \u003clinux/sizes.h\u003e\n+#include \u003cstdio.h\u003e\n+#include \u003cstdlib.h\u003e\n+#include \u003cunistd.h\u003e\n+#include \u003cpthread.h\u003e\n+#include \u003csys/eventfd.h\u003e\n+#include \u003csys/sysinfo.h\u003e\n+\n+static u64 timeout_ns = 2ULL * 1000 * 1000 * 1000;\n+static bool guest_ready_for_irqs[KVM_MAX_VCPUS];\n+static bool guest_received_irq[KVM_MAX_VCPUS];\n+static bool guest_received_nmi[KVM_MAX_VCPUS];\n+static bool x2apic = true;\n+static bool irq_affinity;\n+static bool done;\n+\n+#define GUEST_RECEIVED_IRQ(__vcpu)\t\\\n+\tSYNC_FROM_GUEST_AND_READ((__vcpu)-\u003evm, guest_received_irq[(__vcpu)-\u003eid])\n+#define GUEST_RECEIVED_NMI(__vcpu)\t\\\n+\tSYNC_FROM_GUEST_AND_READ((__vcpu)-\u003evm, guest_received_nmi[(__vcpu)-\u003eid])\n+\n+#define GUEST_RECEIVED_INTERRUPT(__vcpu, __nmi)\t\\\n+\t((__nmi) ? GUEST_RECEIVED_NMI(__vcpu) : GUEST_RECEIVED_IRQ(__vcpu))\n+\n+static u32 guest_get_vcpu_id(void)\n+{\n+\tif (x2apic)\n+\t\treturn x2apic_read_reg(APIC_ID);\n+\telse\n+\t\treturn xapic_read_reg(APIC_ID) \u003e\u003e 24;\n+}\n+\n+static void guest_irq_handler(struct ex_regs *regs)\n+{\n+\tWRITE_ONCE(guest_received_irq[guest_get_vcpu_id()], true);\n+\n+\tif (x2apic)\n+\t\tx2apic_write_reg(APIC_EOI, 0);\n+\telse\n+\t\txapic_write_reg(APIC_EOI, 0);\n+}\n+\n+static void guest_nmi_handler(struct ex_regs *regs)\n+{\n+\tWRITE_ONCE(guest_received_nmi[guest_get_vcpu_id()], true);\n+}\n+\n+static void guest_code(void)\n+{\n+\tif (x2apic)\n+\t\tx2apic_enable();\n+\telse\n+\t\txapic_enable();\n+\n+\tsti_nop();\n+\n+\tWRITE_ONCE(guest_ready_for_irqs[guest_get_vcpu_id()], true);\n+\n+\twhile (!READ_ONCE(done))\n+\t\tcpu_relax();\n+\n+\tGUEST_DONE();\n+}\n+\n+static void *vcpu_thread_main(void *arg)\n+{\n+\tstruct kvm_vcpu *vcpu = arg;\n+\tstruct ucall uc;\n+\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_EQ(UCALL_DONE, get_ucall(vcpu, \u0026uc));\n+\n+\treturn NULL;\n+}\n+\n+static int vfio_setup_msi(struct vfio_pci_device *device)\n+{\n+\tconst int flags = MAP_SHARED | MAP_ANONYMOUS;\n+\tconst int prot = PROT_READ | PROT_WRITE;\n+\tstruct iova_allocator *allocator;\n+\tstruct dma_region *region;\n+\n+\t/* Sanity check that the device+driver can actually send MSIs. */\n+\tTEST_REQUIRE(device-\u003edriver.ops);\n+\tTEST_REQUIRE(device-\u003edriver.ops-\u003esend_msi);\n+\n+\t/*\n+\t * Set up a DMA-able region for the driver to use.   Very few devices\n+\t * provide a way to arbitrarily send interrupts (MSIs), e.g. by writing\n+\t * an MMIO register.  Instead, most devices send MSIs when an action is\n+\t * completed, and practically all actions involve DMA of some form.\n+\t */\n+\tallocator = iova_allocator_init(device-\u003eiommu);\n+\n+\tregion = \u0026device-\u003edriver.region;\n+\tregion-\u003esize = SZ_2M;\n+\tregion-\u003eiova = iova_allocator_alloc(allocator, region-\u003esize);\n+\tregion-\u003evaddr = kvm_mmap(region-\u003esize, prot, flags, -1);\n+\tTEST_ASSERT(region-\u003evaddr != MAP_FAILED, \"mmap() failed\\n\");\n+\tiommu_map(device-\u003eiommu, region);\n+\n+\tiova_allocator_cleanup(allocator);\n+\n+\tvfio_pci_driver_init(device);\n+\n+\treturn device-\u003edriver.msi;\n+}\n+\n+static void trigger_interrupt(struct vfio_pci_device *device, int eventfd)\n+{\n+\tif (device)\n+\t\tvfio_pci_driver_send_msi(device);\n+\telse\n+\t\teventfd_write(eventfd, 1);\n+}\n+\n+\n+static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu,\n+\t\t\t  u8 vector, bool use_nmi)\n+{\n+\tstruct {\n+\t\tstruct kvm_irq_routing header;\n+\t\tstruct kvm_irq_routing_entry entry;\n+\t} routing = {\n+\t\t.header.nr = 1,\n+\t\t.entry = {\n+\t\t\t.gsi = gsi,\n+\t\t\t.type = KVM_IRQ_ROUTING_MSI,\n+\t\t\t.u.msi.address_lo = 0xFEE00000 | (vcpu-\u003eid \u0026 GENMASK(7, 0)) \u003c\u003c 12,\n+\t\t\t.u.msi.address_hi = vcpu-\u003eid \u0026 GENMASK(31, 8),\n+\t\t\t.u.msi.data = use_nmi ? NMI_VECTOR | (4 \u003c\u003c 8) : vector,\n+\t\t},\n+\t};\n+\n+\tvm_ioctl(vm, KVM_SET_GSI_ROUTING, \u0026routing.header);\n+}\n+\n+static void kvm_set_empty_gsi_routing(struct kvm_vm *vm)\n+{\n+\tstruct kvm_irq_routing routing = {};\n+\n+\tvm_ioctl(vm, KVM_SET_GSI_ROUTING, \u0026routing);\n+}\n+\n+static const char *probe_iommu_type(void)\n+{\n+\tint io_fd;\n+\n+\tio_fd = open(\"/dev/iommu\", O_RDONLY);\n+\tif (io_fd \u003e= 0) {\n+\t\tclose(io_fd);\n+\t\treturn MODE_IOMMUFD;\n+\t}\n+\n+\tio_fd = __open_path_or_exit(\"/dev/vfio/vfio\", O_RDONLY,\n+\t\t\t\t    \"Is VFIO (or IOMMUFD) loaded and enabled?\");\n+\tclose(io_fd);\n+\treturn MODE_VFIO_TYPE1_IOMMU;\n+}\n+\n+static void help(const char *name)\n+{\n+\tprintf(\"Usage: %s [-a] [-d \u003csegment:bus:device.function\u003e] [-e] [-h] [-i nr_irqs] [-m] [-n] [-t iommu_type] [-v nr_vcpus] [-x]\\n\", name);\n+\tprintf(\"\\n\");\n+\tprintf(\"Tests KVM interrupt routing and delivery via irqfd.\\n\");\n+\tprintf(\"-a\tAffine the device's host IRQ to a random physical CPU\\n\");\n+\tprintf(\"-d\tUse a VFIO device to send MSI-X interrupts instead of manually signaling the eventfd\\n\");\n+\tprintf(\"-e\tSet empty GSI routing in-between some interrupts\\n\");\n+\tprintf(\"-i\tThe number of IRQs to generate during the test\\n\");\n+\tprintf(\"-m\tPin target vCPU to random physical CPU before triggering interrupt\\n\");\n+\tprintf(\"-n\tDeliver 50 percent of IRQs as non-maskable interrupts\\n\");\n+\tprintf(\"-t\tOverride the IOMMU type to use (vfio_type1_iommu or iommufd)\\n\");\n+\tprintf(\"-v\tNumber of vCPUS to run\\n\");\n+\tprintf(\"-x\tUse xAPIC mode instead of x2APIC mode in the guest\\n\");\n+\tprintf(\"\\n\");\n+\texit(KSFT_FAIL);\n+}\n+\n+int main(int argc, char **argv)\n+{\n+\t/*\n+\t * Pick a random vector and a random GSI to use for device IRQ.\n+\t *\n+\t * Pick an IRQ vector in range [32, UINT8_MAX]. Min value is 32 because\n+\t * Linux/x86 reserves vectors 0-31 for exceptions and architecture\n+\t * defined NMIs and interrupts.\n+\t *\n+\t * Pick a GSI in range [24, KVM_MAX_IRQ_ROUTES - 1]. The min value is 24\n+\t * because KVM reserves GSIs 0-15 for legacy ISA IRQs and 16-23 only go\n+\t * to the IOAPIC. The max is KVM_MAX_IRQ_ROUTES - 1, because\n+\t * KVM_MAX_IRQ_ROUTES is exclusive.\n+\t */\n+\tu32 gsi = kvm_random_u64_in_range(\u0026kvm_rng, 24, KVM_MAX_IRQ_ROUTES - 1);\n+\tu8 vector = kvm_random_u64_in_range(\u0026kvm_rng, 32, UINT8_MAX);\n+\n+\tpthread_t vcpu_threads[KVM_MAX_VCPUS];\n+\tstruct kvm_vcpu *vcpus[KVM_MAX_VCPUS];\n+\tstruct vfio_pci_device *device = NULL;\n+\tint nr_irqs = 1000, nr_vcpus = 1;\n+\tbool set_empty_routing = false;\n+\tconst char *device_bdf = NULL;\n+\tconst char *iommu_type = NULL;\n+\tint i, j, c, msix, eventfd;\n+\tbool migrate_vcpus = false;\n+\tcpu_set_t available_cpus;\n+\tbool use_nmi = false;\n+\tstruct iommu *iommu;\n+\tstruct kvm_vm *vm;\n+\tint irq, irq_cpu;\n+\n+\twhile ((c = getopt(argc, argv, \"ad:ehi:mnt:v:x\")) != -1) {\n+\t\tswitch (c) {\n+\t\tcase 'a':\n+\t\t\tirq_affinity = true;\n+\t\t\tbreak;\n+\t\tcase 'd':\n+\t\t\tdevice_bdf = optarg;\n+\t\t\tbreak;\n+\t\tcase 'e':\n+\t\t\tset_empty_routing = true;\n+\t\t\tbreak;\n+\t\tcase 'i':\n+\t\t\tnr_irqs = atoi_positive(\"Number of IRQs\", optarg);\n+\t\t\tbreak;\n+\t\tcase 'm':\n+\t\t\tmigrate_vcpus = true;\n+\t\t\tbreak;\n+\t\tcase 'n':\n+\t\t\tuse_nmi = true;\n+\t\t\tbreak;\n+\t\tcase 't':\n+\t\t\tiommu_type = optarg;\n+\t\t\tbreak;\n+\t\tcase 'v':\n+\t\t\tnr_vcpus = atoi_positive(\"Number of vCPUS\", optarg);\n+\t\t\tTEST_ASSERT(nr_vcpus \u003c= KVM_MAX_VCPUS,\n+\t\t\t\t    \"KVM selftests support at most %u vCPUs\", KVM_MAX_VCPUS);\n+\t\t\tbreak;\n+\t\tcase 'x':\n+\t\t\tx2apic = false;\n+\t\t\tbreak;\n+\t\tcase 'h':\n+\t\tdefault:\n+\t\t\thelp(argv[0]);\n+\t\t}\n+\t}\n+\n+\tTEST_REQUIRE(kvm_arch_has_default_irqchip());\n+\n+\tvm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);\n+\tvm_enable_cap(vm, KVM_CAP_X2APIC_API, KVM_X2APIC_API_USE_32BIT_IDS |\n+\t\t\t\t\t      KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK);\n+\n+\tvm_install_exception_handler(vm, vector, guest_irq_handler);\n+\tvm_install_exception_handler(vm, NMI_VECTOR, guest_nmi_handler);\n+\n+\tif (!x2apic) {\n+\t\tTEST_ASSERT(nr_vcpus \u003c 256, \"xAPIC can only target IDs [0-254] (255 vCPUs)\");\n+\t\tvirt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);\n+\t}\n+\n+\tif (device_bdf) {\n+\t\tif (!iommu_type)\n+\t\t\tiommu_type = probe_iommu_type();\n+\t\tiommu = iommu_init(iommu_type);\n+\t\tdevice = vfio_pci_device_init(device_bdf, iommu);\n+\t\tmsix = vfio_setup_msi(device);\n+\t\tirq = vfio_msix_to_host_irq(device_bdf, msix);\n+\t\teventfd = device-\u003emsi_eventfds[msix];\n+\t\tprintf(\"Using device %s MSI-X[%d] (IRQ-%u)\\n\", device_bdf, msix,\n+\t\t       irq);\n+\t} else {\n+\t\tTEST_ASSERT(!irq_affinity,\n+\t\t\t    \"Setting IRQ affinity (-a) requires a backing device (-d)\");\n+\n+\t\teventfd = kvm_new_eventfd();\n+\t\tirq = -1;\n+\t}\n+\n+\tpr_info(\"Injecting interrupts for GSI %d (guest vector 0x%x) %d times\\n\",\n+\t\tgsi, vector, nr_irqs);\n+\n+\tkvm_assign_irqfd(vm, gsi, eventfd);\n+\n+\tsync_global_to_guest(vm, x2apic);\n+\n+\tif (migrate_vcpus)\n+\t\tkvm_sched_getaffinity(0, sizeof(available_cpus), \u0026available_cpus);\n+\n+\tfor (i = 0; i \u003c nr_vcpus; i++)\n+\t\tpthread_create(\u0026vcpu_threads[i], NULL, vcpu_thread_main, vcpus[i]);\n+\n+\tfor (i = 0; i \u003c nr_vcpus; i++) {\n+\t\tstruct kvm_vcpu *vcpu = vcpus[i];\n+\n+\t\twhile (!SYNC_FROM_GUEST_AND_READ(vm, guest_ready_for_irqs[vcpu-\u003eid]))\n+\t\t\tcontinue;\n+\t}\n+\n+\tirq_cpu = -1;\n+\n+\tfor (i = 0; i \u003c nr_irqs; i++) {\n+\t\tconst bool do_set_empty_routing = set_empty_routing \u0026\u0026 (i \u0026 BIT(3));\n+\t\tconst bool do_use_nmi = use_nmi \u0026\u0026 (i \u0026 BIT(2));\n+\t\tstruct kvm_vcpu *vcpu = vcpus[i % nr_vcpus];\n+\t\tstruct timespec start;\n+\n+\t\tif (do_set_empty_routing)\n+\t\t\tkvm_set_empty_gsi_routing(vm);\n+\n+\t\tkvm_route_msi(vm, gsi, vcpu, vector, do_use_nmi);\n+\n+\t\tif (irq_affinity) {\n+\t\t\tirq_cpu = kvm_random_u64(\u0026kvm_rng) % get_nprocs();\n+\t\t\tproc_irq_set_smp_affinity(irq, irq_cpu);\n+\t\t}\n+\n+\t\tif (migrate_vcpus)\n+\t\t\tpin_task_to_random_cpu(vcpu_threads[i % nr_vcpus], \u0026available_cpus);\n+\n+\t\tfor (j = 0; j \u003c nr_vcpus; j++) {\n+\t\t\tTEST_ASSERT(!GUEST_RECEIVED_IRQ(vcpus[j]),\n+\t\t\t\t    \"IRQ flag for vCPU %d not clear prior to test\",\n+\t\t\t\t    vcpus[j]-\u003eid);\n+\t\t\tTEST_ASSERT(!GUEST_RECEIVED_NMI(vcpus[j]),\n+\t\t\t\t    \"NMI flag for vCPU %d not clear prior to test\",\n+\t\t\t\t    vcpus[j]-\u003eid);\n+\t\t}\n+\n+\t\ttrigger_interrupt(device, eventfd);\n+\n+\t\tclock_gettime(CLOCK_MONOTONIC, \u0026start);\n+\t\twhile (!GUEST_RECEIVED_INTERRUPT(vcpu, do_use_nmi) \u0026\u0026\n+\t\t       timespec_to_ns(timespec_elapsed(start)) \u003c= timeout_ns)\n+\t\t\tcpu_relax();\n+\n+\t\tTEST_ASSERT(GUEST_RECEIVED_INTERRUPT(vcpu, do_use_nmi),\n+\t\t\t    \"vCPU %d timed out waiting for %s (vector 0x%x) from GSI %d (via CPU %d)\\n\",\n+\t\t\t    vcpu-\u003eid, do_use_nmi ? \"NMI\" : \"IRQ\",\n+\t\t\t    do_use_nmi ? NMI_VECTOR : vector, gsi, irq_cpu);\n+\n+\t\tif (do_use_nmi)\n+\t\t\tWRITE_AND_SYNC_TO_GUEST(vm, guest_received_nmi[vcpu-\u003eid], false);\n+\t\telse\n+\t\t\tWRITE_AND_SYNC_TO_GUEST(vm, guest_received_irq[vcpu-\u003eid], false);\n+\t}\n+\n+\tWRITE_AND_SYNC_TO_GUEST(vm, done, true);\n+\n+\tfor (i = 0; i \u003c nr_vcpus; i++)\n+\t\tpthread_join(vcpu_threads[i], NULL);\n+\n+\treturn 0;\n+}\ndiff --git a/tools/testing/selftests/kvm/lib/assert.c b/tools/testing/selftests/kvm/lib/assert.c\nindex 8be0d09ecf0f98..1d72dcdfce3b66 100644\n--- a/tools/testing/selftests/kvm/lib/assert.c\n+++ b/tools/testing/selftests/kvm/lib/assert.c\n@@ -10,6 +10,7 @@\n #include \u003csys/syscall.h\u003e\n \n #include \"kselftest.h\"\n+#include \"kvm_syscalls.h\"\n \n #ifdef __GLIBC__\n #include \u003cexecinfo.h\u003e\n@@ -64,11 +65,6 @@ static void test_dump_stack(void)\n static void test_dump_stack(void) {}\n #endif\n \n-static pid_t _gettid(void)\n-{\n-\treturn syscall(SYS_gettid);\n-}\n-\n void __attribute__((noinline))\n test_assert(bool exp, const char *exp_str,\n \tconst char *file, unsigned int line, const char *fmt, ...)\n@@ -81,7 +77,7 @@ test_assert(bool exp, const char *exp_str,\n \t\tfprintf(stderr, \"==== Test Assertion Failure ====\\n\"\n \t\t\t\"  %s:%u: %s\\n\"\n \t\t\t\"  pid=%d tid=%d errno=%d - %s\\n\",\n-\t\t\tfile, line, exp_str, getpid(), _gettid(),\n+\t\t\tfile, line, exp_str, getpid(), kvm_gettid(),\n \t\t\terrno, strerror(errno));\n \t\ttest_dump_stack();\n \t\tif (fmt) {\ndiff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c\nindex 195f3fdae1e396..3794575d2ca0ce 100644\n--- a/tools/testing/selftests/kvm/lib/kvm_util.c\n+++ b/tools/testing/selftests/kvm/lib/kvm_util.c\n@@ -15,14 +15,22 @@\n #include \u003csys/resource.h\u003e\n #include \u003csys/types.h\u003e\n #include \u003csys/stat.h\u003e\n+#include \u003ctime.h\u003e\n #include \u003cunistd.h\u003e\n #include \u003clinux/kernel.h\u003e\n \n #define KVM_UTIL_MIN_PFN\t2\n \n-u32 guest_random_seed;\n-struct guest_random_state guest_rng;\n-static u32 last_guest_seed;\n+u32 kvm_random_seed;\n+struct kvm_random_state kvm_rng;\n+static u32 last_kvm_seed;\n+\n+static void kvm_seed_rng(u32 seed)\n+{\n+\tkvm_random_seed = last_kvm_seed = seed;\n+\tpr_info(\"Random seed: 0x%x\\n\", kvm_random_seed);\n+\tkvm_rng = new_kvm_random_state(kvm_random_seed);\n+}\n \n static size_t vcpu_mmap_sz(void);\n \n@@ -515,12 +523,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus,\n \tslot0 = memslot2region(vm, 0);\n \tucall_init(vm, slot0-\u003eregion.guest_phys_addr + slot0-\u003eregion.memory_size);\n \n-\tif (guest_random_seed != last_guest_seed) {\n-\t\tpr_info(\"Random seed: 0x%x\\n\", guest_random_seed);\n-\t\tlast_guest_seed = guest_random_seed;\n-\t}\n-\tguest_rng = new_guest_random_state(guest_random_seed);\n-\tsync_global_to_guest(vm, guest_rng);\n+\tif (kvm_random_seed != last_kvm_seed)\n+\t\tkvm_seed_rng(kvm_random_seed);\n+\n+\tsync_global_to_guest(vm, kvm_rng);\n \n \tkvm_arch_vm_post_create(vm, nr_runnable_vcpus);\n \n@@ -662,19 +668,39 @@ void kvm_print_vcpu_pinning_help(void)\n \t       \"     (default: no pinning)\\n\", name, name);\n }\n \n+void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus)\n+{\n+\tint target_idx;\n+\tint nr_cpus;\n+\tint cpu;\n+\n+\tnr_cpus = CPU_COUNT(possible_cpus);\n+\tTEST_ASSERT(nr_cpus \u003e 0, \"No CPUs available in possible_cpus\");\n+\n+\ttarget_idx = kvm_random_u64(\u0026kvm_rng) % nr_cpus;\n+\n+\tfor (cpu = 0; cpu \u003c CPU_SETSIZE; cpu++) {\n+\t\tif (CPU_ISSET(cpu, possible_cpus) \u0026\u0026 target_idx-- == 0) {\n+\t\t\tpin_task_to_cpu(task, cpu);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tTEST_FAIL(\"Failed to find random CPU in possible_cpus\");\n+}\n+\n void kvm_parse_vcpu_pinning(const char *pcpus_string, u32 vcpu_to_pcpu[],\n \t\t\t    int nr_vcpus)\n {\n \tcpu_set_t allowed_mask;\n \tchar *cpu, *cpu_list;\n \tchar delim[2] = \",\";\n-\tint i, r;\n+\tint i;\n \n \tcpu_list = strdup(pcpus_string);\n \tTEST_ASSERT(cpu_list, \"strdup() allocation failed.\");\n \n-\tr = sched_getaffinity(0, sizeof(allowed_mask), \u0026allowed_mask);\n-\tTEST_ASSERT(!r, \"sched_getaffinity() failed\");\n+\tkvm_sched_getaffinity(0, sizeof(allowed_mask), \u0026allowed_mask);\n \n \tcpu = strtok(cpu_list, delim);\n \n@@ -2279,8 +2305,8 @@ void __attribute((constructor)) kvm_selftest_init(void)\n \tsigaction(SIGILL, \u0026sig_sa, NULL);\n \tsigaction(SIGFPE, \u0026sig_sa, NULL);\n \n-\tguest_random_seed = last_guest_seed = random();\n-\tpr_info(\"Random seed: 0x%x\\n\", guest_random_seed);\n+\tsrandom(time(0));\n+\tkvm_seed_rng(random());\n \n \tkvm_selftest_arch_init();\n }\ndiff --git a/tools/testing/selftests/kvm/lib/memstress.c b/tools/testing/selftests/kvm/lib/memstress.c\nindex 6dcd15910a0608..3599b75d97c919 100644\n--- a/tools/testing/selftests/kvm/lib/memstress.c\n+++ b/tools/testing/selftests/kvm/lib/memstress.c\n@@ -48,14 +48,14 @@ void memstress_guest_code(u32 vcpu_idx)\n {\n \tstruct memstress_args *args = \u0026memstress_args;\n \tstruct memstress_vcpu_args *vcpu_args = \u0026args-\u003evcpu_args[vcpu_idx];\n-\tstruct guest_random_state rand_state;\n+\tstruct kvm_random_state rand_state;\n \tgva_t gva;\n \tu64 pages;\n \tu64 addr;\n \tu64 page;\n \tint i;\n \n-\trand_state = new_guest_random_state(guest_random_seed + vcpu_idx);\n+\trand_state = new_kvm_random_state(kvm_random_seed + vcpu_idx);\n \n \tgva = vcpu_args-\u003egva;\n \tpages = vcpu_args-\u003epages;\n@@ -69,13 +69,13 @@ void memstress_guest_code(u32 vcpu_idx)\n \n \t\tfor (i = 0; i \u003c pages; i++) {\n \t\t\tif (args-\u003erandom_access)\n-\t\t\t\tpage = guest_random_u32(\u0026rand_state) % pages;\n+\t\t\t\tpage = kvm_random_u32(\u0026rand_state) % pages;\n \t\t\telse\n \t\t\t\tpage = i;\n \n \t\t\taddr = gva + (page * args-\u003eguest_page_size);\n \n-\t\t\tif (__guest_random_bool(\u0026rand_state, args-\u003ewrite_percent))\n+\t\t\tif (__kvm_random_bool(\u0026rand_state, args-\u003ewrite_percent))\n \t\t\t\t*(u64 *)addr = 0x0123456789ABCDEF;\n \t\t\telse\n \t\t\t\tREAD_ONCE(*(u64 *)addr);\ndiff --git a/tools/testing/selftests/kvm/lib/proc_util.c b/tools/testing/selftests/kvm/lib/proc_util.c\nnew file mode 100644\nindex 00000000000000..3960b3841d6370\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/lib/proc_util.c\n@@ -0,0 +1,54 @@\n+// SPDX-License-Identifier: GPL-2.0\n+#include \"kvm_util.h\"\n+#include \"test_util.h\"\n+#include \"proc_util.h\"\n+\n+static FILE *open_proc_interrupts(void)\n+{\n+\tFILE *fp;\n+\n+\tfp = fopen(\"/proc/interrupts\", \"r\");\n+\tTEST_ASSERT(fp, \"fopen(/proc/interrupts) failed\");\n+\n+\treturn fp;\n+}\n+\n+unsigned int vfio_msix_to_host_irq(const char *device_bdf, int msix)\n+{\n+\tchar search_string[64];\n+\tchar line[4096];\n+\tint irq = -1;\n+\tFILE *fp;\n+\n+\tfp = open_proc_interrupts();\n+\n+\tsnprintf(search_string, sizeof(search_string), \"vfio-msix[%d]\", msix);\n+\n+\twhile (fgets(line, sizeof(line), fp)) {\n+\t\tif (strstr(line, device_bdf) \u0026\u0026 strstr(line, search_string)) {\n+\t\t\tTEST_ASSERT_EQ(1, sscanf(line, \"%d:\", \u0026irq));\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tfclose(fp);\n+\n+\tTEST_ASSERT(irq != -1, \"Failed to locate IRQ for %s %s\", device_bdf,\n+\t\t    search_string);\n+\treturn (unsigned int)irq;\n+}\n+\n+void proc_irq_set_smp_affinity(unsigned int irq, int cpu)\n+{\n+\tchar path[PATH_MAX];\n+\tint r, fd;\n+\n+\tsnprintf(path, sizeof(path), \"/proc/irq/%u/smp_affinity_list\", irq);\n+\tfd = open(path, O_RDWR);\n+\tTEST_ASSERT(fd \u003e= 0, \"Failed to open %s\", path);\n+\n+\tr = dprintf(fd, \"%d\\n\", cpu);\n+\tTEST_ASSERT(r \u003e 0, \"Failed to affinitize IRQ-%u to CPU %d\", irq, cpu);\n+\n+\tkvm_close(fd);\n+}\ndiff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c\nindex bab1bd2b775b6d..6b00ab11f3c025 100644\n--- a/tools/testing/selftests/kvm/lib/test_util.c\n+++ b/tools/testing/selftests/kvm/lib/test_util.c\n@@ -17,6 +17,7 @@\n #include \"linux/kernel.h\"\n \n #include \"test_util.h\"\n+#include \"kvm_syscalls.h\"\n \n sigjmp_buf expect_sigbus_jmpbuf;\n \n@@ -30,18 +31,36 @@ void __attribute__((used)) expect_sigbus_handler(int signum)\n  * Park-Miller LCG using standard constants.\n  */\n \n-struct guest_random_state new_guest_random_state(u32 seed)\n+struct kvm_random_state new_kvm_random_state(u32 seed)\n {\n-\tstruct guest_random_state s = {.seed = seed};\n+\tstruct kvm_random_state s = {.seed = seed};\n \treturn s;\n }\n \n-u32 guest_random_u32(struct guest_random_state *state)\n+u32 kvm_random_u32(struct kvm_random_state *state)\n {\n \tstate-\u003eseed = (u64)state-\u003eseed * 48271 % ((u32)(1 \u003c\u003c 31) - 1);\n \treturn state-\u003eseed;\n }\n \n+/* Returns a random u64 in the inclusive range [min, max] */\n+u64 kvm_random_u64_in_range(struct kvm_random_state *state, u64 min,\n+\t\t\t    u64 max)\n+{\n+\tu64 value;\n+\tu64 range;\n+\n+\tTEST_ASSERT(min \u003c= max, \"PEBKAC, min = 0x%lx, max = 0x%lx\", min, max);\n+\n+\tvalue = kvm_random_u64(state);\n+\n+\trange = max - min;\n+\tif (range == ULLONG_MAX)\n+\t\treturn value;\n+\n+\treturn min + (value % (range + 1));\n+}\n+\n /*\n  * Parses \"[0-9]+[kmgt]?\".\n  */\n@@ -377,7 +396,7 @@ long get_run_delay(void)\n \tlong val[2];\n \tFILE *fp;\n \n-\tsprintf(path, \"/proc/%ld/schedstat\", syscall(SYS_gettid));\n+\tsprintf(path, \"/proc/%ld/schedstat\", (long)kvm_gettid());\n \tfp = fopen(path, \"r\");\n \t/* Return MIN_RUN_DELAY_NS upon failure just to be safe */\n \tif (fscanf(fp, \"%ld %ld \", \u0026val[0], \u0026val[1]) \u003c 2)\ndiff --git a/tools/testing/selftests/kvm/mmu_stress_test.c b/tools/testing/selftests/kvm/mmu_stress_test.c\nindex 54d281419d3104..3d5f33a63b2b4a 100644\n--- a/tools/testing/selftests/kvm/mmu_stress_test.c\n+++ b/tools/testing/selftests/kvm/mmu_stress_test.c\n@@ -155,10 +155,8 @@ static void *vcpu_worker(void *data)\n \t\t    \"Expected EFAULT on write to RO memory, got r = %d, errno = %d\", r, errno);\n \n \tatomic_inc(\u0026nr_ro_faults);\n-\tif (atomic_read(\u0026nr_ro_faults) == nr_vcpus) {\n-\t\tWRITE_ONCE(all_vcpus_hit_ro_fault, true);\n-\t\tsync_global_to_guest(vm, all_vcpus_hit_ro_fault);\n-\t}\n+\tif (atomic_read(\u0026nr_ro_faults) == nr_vcpus)\n+\t\tWRITE_AND_SYNC_TO_GUEST(vm, all_vcpus_hit_ro_fault, true);\n \n #if defined(__x86_64__) || defined(__aarch64__)\n \t/*\n@@ -257,11 +255,7 @@ static void rendezvous_with_vcpus(struct timespec *time, const char *name)\n static void calc_default_nr_vcpus(void)\n {\n \tcpu_set_t possible_mask;\n-\tint r;\n-\n-\tr = sched_getaffinity(0, sizeof(possible_mask), \u0026possible_mask);\n-\tTEST_ASSERT(!r, \"sched_getaffinity failed, errno = %d (%s)\",\n-\t\t    errno, strerror(errno));\n+\tkvm_sched_getaffinity(0, sizeof(possible_mask), \u0026possible_mask);\n \n \tnr_vcpus = CPU_COUNT(\u0026possible_mask);\n \tTEST_ASSERT(nr_vcpus \u003e 0, \"Uh, no CPUs?\");\n@@ -383,8 +377,7 @@ int main(int argc, char *argv[])\n \trendezvous_with_vcpus(\u0026time_run2, \"run 2\");\n \n \tmprotect(mem, slot_size, PROT_READ);\n-\tmprotect_ro_done = true;\n-\tsync_global_to_guest(vm, mprotect_ro_done);\n+\tWRITE_AND_SYNC_TO_GUEST(vm, mprotect_ro_done, true);\n \n \trendezvous_with_vcpus(\u0026time_ro, \"mprotect RO\");\n \tmprotect(mem, slot_size, PROT_READ | PROT_WRITE);\ndiff --git a/tools/testing/selftests/kvm/rseq_test.c b/tools/testing/selftests/kvm/rseq_test.c\nindex f80ad6b47d16b0..557e393c223b98 100644\n--- a/tools/testing/selftests/kvm/rseq_test.c\n+++ b/tools/testing/selftests/kvm/rseq_test.c\n@@ -226,9 +226,7 @@ int main(int argc, char *argv[])\n \t\t}\n \t}\n \n-\tr = sched_getaffinity(0, sizeof(possible_mask), \u0026possible_mask);\n-\tTEST_ASSERT(!r, \"sched_getaffinity failed, errno = %d (%s)\", errno,\n-\t\t    strerror(errno));\n+\tkvm_sched_getaffinity(0, sizeof(possible_mask), \u0026possible_mask);\n \n \tcalc_min_max_cpu();\n \n@@ -244,7 +242,7 @@ int main(int argc, char *argv[])\n \tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code);\n \n \tpthread_create(\u0026migration_thread, NULL, migration_worker,\n-\t\t       (void *)(unsigned long)syscall(SYS_gettid));\n+\t\t       (void *)(unsigned long)kvm_gettid());\n \n \tif (latency \u003e= 0) {\n \t\t/*\ndiff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c\nindex 76fcdd1fd3cb48..2de87549fcc038 100644\n--- a/tools/testing/selftests/kvm/steal_time.c\n+++ b/tools/testing/selftests/kvm/steal_time.c\n@@ -70,8 +70,8 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu)\n static void steal_time_init(struct kvm_vcpu *vcpu, u32 i)\n {\n \t/* ST_GPA_BASE is identity mapped */\n-\tst_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE);\n-\tsync_global_to_guest(vcpu-\u003evm, st_gva[i]);\n+\tWRITE_AND_SYNC_TO_GUEST(vcpu-\u003evm, st_gva[i],\n+\t\t\t\t(void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE));\n \n \tvcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, (ulong)st_gva[i] | KVM_MSR_ENABLED);\n }\n@@ -187,8 +187,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, u32 i)\n \t};\n \n \t/* ST_GPA_BASE is identity mapped */\n-\tst_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE);\n-\tsync_global_to_guest(vm, st_gva[i]);\n+\tWRITE_AND_SYNC_TO_GUEST(vm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE));\n \n \tst_ipa = (ulong)st_gva[i];\n \tvcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, \u0026dev);\n@@ -310,10 +309,8 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu)\n static void steal_time_init(struct kvm_vcpu *vcpu, u32 i)\n {\n \t/* ST_GPA_BASE is identity mapped */\n-\tst_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE);\n-\tst_gpa[i] = addr_gva2gpa(vcpu-\u003evm, (gva_t)st_gva[i]);\n-\tsync_global_to_guest(vcpu-\u003evm, st_gva[i]);\n-\tsync_global_to_guest(vcpu-\u003evm, st_gpa[i]);\n+\tWRITE_AND_SYNC_TO_GUEST(vcpu-\u003evm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE));\n+\tWRITE_AND_SYNC_TO_GUEST(vcpu-\u003evm, st_gpa[i], addr_gva2gpa(vcpu-\u003evm, (gva_t)st_gva[i]));\n }\n \n static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx)\n@@ -442,8 +439,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, u32 i)\n \t};\n \n \t/* ST_GPA_BASE is identity mapped */\n-\tst_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE);\n-\tsync_global_to_guest(vm, st_gva[i]);\n+\tWRITE_AND_SYNC_TO_GUEST(vm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE));\n \n \terr = __vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, \u0026attr);\n \tTEST_ASSERT(err == 0, \"No PV stealtime Feature\");\n@@ -549,8 +545,7 @@ int main(int ac, char **av)\n \n \t\t/* Second VCPU run, expect guest stolen time to be \u003c= run_delay */\n \t\trun_vcpu(vcpus[i]);\n-\t\tsync_global_from_guest(vm, guest_stolen_time[i]);\n-\t\tstolen_time = guest_stolen_time[i];\n+\t\tstolen_time = SYNC_FROM_GUEST_AND_READ(vm, guest_stolen_time[i]);\n \t\trun_delay = get_run_delay();\n \t\tTEST_ASSERT(stolen_time \u003c= run_delay,\n \t\t\t    \"Expected stolen time \u003c= %ld, got %ld\",\n@@ -570,8 +565,7 @@ int main(int ac, char **av)\n \n \t\t/* Run VCPU again to confirm stolen time is consistent with run_delay */\n \t\trun_vcpu(vcpus[i]);\n-\t\tsync_global_from_guest(vm, guest_stolen_time[i]);\n-\t\tstolen_time = guest_stolen_time[i] - stolen_time;\n+\t\tstolen_time = SYNC_FROM_GUEST_AND_READ(vm, guest_stolen_time[i]) - stolen_time;\n \t\tTEST_ASSERT(stolen_time \u003e= run_delay,\n \t\t\t    \"Expected stolen time \u003e= %ld, got %ld\",\n \t\t\t    run_delay, stolen_time);\ndiff --git a/tools/testing/selftests/kvm/x86/sev_dbg_test.c b/tools/testing/selftests/kvm/x86/sev_dbg_test.c\nindex a9d8e4c059f969..eaa8201b937d30 100644\n--- a/tools/testing/selftests/kvm/x86/sev_dbg_test.c\n+++ b/tools/testing/selftests/kvm/x86/sev_dbg_test.c\n@@ -34,7 +34,7 @@ static void validate_buffers(void)\n \n static void ____test_sev_dbg(struct kvm_vm *vm, int i, int j, int nr_bytes)\n {\n-\tu8 pattern = guest_random_u32(\u0026guest_rng);\n+\tu8 pattern = kvm_random_u32(\u0026kvm_rng);\n \n \tif (i + nr_bytes \u003e BUFFER_SIZE || j + nr_bytes \u003e BUFFER_SIZE)\n \t\treturn;\n"
}

2/1 2026/06/27 01:39 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies user-space selftests in tools/testing/selftests/kvm/ and does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 8fe60110dced7a96c793c7cd3c0c4f264949d0ff
Author: syz-cluster <triage@syzkaller.com>
Date:   Sat Jun 27 01:39:34 2026 +0000

    syz-cluster: applied patch under review

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 4ace12606e937b..88c6c8046ddec0 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
@@ -157,6 +158,7 @@ TEST_GEN_PROGS_x86 += coalesced_io_test
 TEST_GEN_PROGS_x86 += dirty_log_perf_test
 TEST_GEN_PROGS_x86 += guest_memfd_test
 TEST_GEN_PROGS_x86 += hardware_disable_test
+TEST_GEN_PROGS_x86 += irq_test
 TEST_GEN_PROGS_x86 += mmu_stress_test
 TEST_GEN_PROGS_x86 += rseq_test
 TEST_GEN_PROGS_x86 += steal_time
@@ -259,6 +261,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/
@@ -313,7 +316,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))
 
diff --git a/tools/testing/selftests/kvm/arch_timer.c b/tools/testing/selftests/kvm/arch_timer.c
index 90c475a61b22b6..f8b02597897bd8 100644
--- a/tools/testing/selftests/kvm/arch_timer.c
+++ b/tools/testing/selftests/kvm/arch_timer.c
@@ -85,7 +85,7 @@ static u32 test_get_pcpu(void)
 	cpu_set_t online_cpuset;
 
 	nproc_conf = get_nprocs_conf();
-	sched_getaffinity(0, sizeof(cpu_set_t), &online_cpuset);
+	kvm_sched_getaffinity(0, sizeof(cpu_set_t), &online_cpuset);
 
 	/* Randomly find an available pCPU to place a vCPU on */
 	do {
diff --git a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c
index f7625eb711d6b8..d9c9377a63256d 100644
--- a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c
+++ b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c
@@ -1039,7 +1039,7 @@ int main(int argc, char *argv[])
 	if (!parse_args(argc, argv))
 		exit(KSFT_SKIP);
 
-	sched_getaffinity(0, sizeof(default_cpuset), &default_cpuset);
+	kvm_sched_getaffinity(0, sizeof(default_cpuset), &default_cpuset);
 	set_counter_defaults();
 
 	if (test_args.test_virtual) {
diff --git a/tools/testing/selftests/kvm/demand_paging_test.c b/tools/testing/selftests/kvm/demand_paging_test.c
index 302c4923d0930f..f8b3d0b68830d7 100644
--- a/tools/testing/selftests/kvm/demand_paging_test.c
+++ b/tools/testing/selftests/kvm/demand_paging_test.c
@@ -57,7 +57,7 @@ static void vcpu_worker(struct memstress_vcpu_args *vcpu_args)
 static int handle_uffd_page_request(int uffd_mode, int uffd,
 		struct uffd_msg *msg)
 {
-	pid_t tid = syscall(__NR_gettid);
+	pid_t tid = kvm_gettid();
 	u64 addr = msg->arg.pagefault.address;
 	struct timespec start;
 	struct timespec ts_diff;
diff --git a/tools/testing/selftests/kvm/dirty_log_perf_test.c b/tools/testing/selftests/kvm/dirty_log_perf_test.c
index ef779fa91827c9..7c5abe1ae9e018 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 74ca096bf976b5..e8419d7da1eab5 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);
 
@@ -708,8 +708,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
 
 		sync_global_to_guest(vm, iteration);
 
-		WRITE_ONCE(nr_writes, 0);
-		sync_global_to_guest(vm, nr_writes);
+		WRITE_AND_SYNC_TO_GUEST(vm, nr_writes, 0);
 
 		dirty_ring_prev_iteration_last_page = dirty_ring_last_page;
 		WRITE_ONCE(dirty_ring_vcpu_ring_full, false);
@@ -775,16 +774,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_syscalls.h b/tools/testing/selftests/kvm/include/kvm_syscalls.h
index 6cb3bed29b81ab..5dae6143ddb04b 100644
--- a/tools/testing/selftests/kvm/include/kvm_syscalls.h
+++ b/tools/testing/selftests/kvm/include/kvm_syscalls.h
@@ -12,6 +12,7 @@
 #include <sys/mman.h>
 #include <sys/syscall.h>
 
+#include <sched.h>
 #include <test_util.h>
 
 #define MAP_ARGS0(m,...)
@@ -83,11 +84,17 @@ static inline int kvm_dup(int fd)
 	return new_fd;
 }
 
+static inline pid_t kvm_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(madvise, 3, void *, addr, size_t, length, int, advice);
+__KVM_SYSCALL_DEFINE(sched_getaffinity, 3, pid_t, pid, size_t, cpusetsize, cpu_set_t *, mask);
 
 #define kvm_free_fd(fd)		\
 do {				\
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 04a910164a296a..b39e713c30a469 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -1094,6 +1094,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();
@@ -1138,6 +1140,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/include/proc_util.h b/tools/testing/selftests/kvm/include/proc_util.h
new file mode 100644
index 00000000000000..d1ddc967d11dc5
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/proc_util.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef SELFTEST_KVM_PROC_UTIL_H
+#define SELFTEST_KVM_PROC_UTIL_H
+
+#include <stdint.h>
+
+unsigned int vfio_msix_to_host_irq(const char *vfio_device_bdf, int msix);
+
+void proc_irq_set_smp_affinity(unsigned int irq, int cpu);
+
+#endif /* SELFTEST_KVM_PROC_UTIL_H */
diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
index a56271c237ae9e..d64c8a228207a7 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -108,32 +108,35 @@ 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 {
 	u32 seed;
 };
 
-extern u32 guest_random_seed;
-extern struct guest_random_state guest_rng;
+extern u32 kvm_random_seed;
+extern struct kvm_random_state kvm_rng;
 
-struct guest_random_state new_guest_random_state(u32 seed);
-u32 guest_random_u32(struct guest_random_state *state);
+struct kvm_random_state new_kvm_random_state(u32 seed);
+u32 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,
 				       u8 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 u64 guest_random_u64(struct guest_random_state *state)
+static inline u64 kvm_random_u64(struct kvm_random_state *state)
 {
-	return ((u64)guest_random_u32(state) << 32) | guest_random_u32(state);
+	return ((u64)kvm_random_u32(state) << 32) | kvm_random_u32(state);
 }
 
+u64 kvm_random_u64_in_range(struct kvm_random_state *state, u64 min,
+			    u64 max);
+
 enum vm_mem_backing_src_type {
 	VM_MEM_SRC_ANONYMOUS,
 	VM_MEM_SRC_ANONYMOUS_THP,
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 c33ab6e0417179..6904dbda79f920 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/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
new file mode 100644
index 00000000000000..240f6f0fdbe43e
--- /dev/null
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "kvm_util.h"
+#include "test_util.h"
+#include "apic.h"
+#include "processor.h"
+#include "proc_util.h"
+
+#include <libvfio.h>
+#include <linux/sizes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/eventfd.h>
+#include <sys/sysinfo.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 guest_received_nmi[KVM_MAX_VCPUS];
+static bool x2apic = true;
+static bool irq_affinity;
+static bool done;
+
+#define GUEST_RECEIVED_IRQ(__vcpu)	\
+	SYNC_FROM_GUEST_AND_READ((__vcpu)->vm, guest_received_irq[(__vcpu)->id])
+#define GUEST_RECEIVED_NMI(__vcpu)	\
+	SYNC_FROM_GUEST_AND_READ((__vcpu)->vm, guest_received_nmi[(__vcpu)->id])
+
+#define GUEST_RECEIVED_INTERRUPT(__vcpu, __nmi)	\
+	((__nmi) ? GUEST_RECEIVED_NMI(__vcpu) : GUEST_RECEIVED_IRQ(__vcpu))
+
+static u32 guest_get_vcpu_id(void)
+{
+	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);
+
+	if (x2apic)
+		x2apic_write_reg(APIC_EOI, 0);
+	else
+		xapic_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)
+{
+	if (x2apic)
+		x2apic_enable();
+	else
+		xapic_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 int vfio_setup_msi(struct vfio_pci_device *device)
+{
+	const int flags = MAP_SHARED | MAP_ANONYMOUS;
+	const int prot = PROT_READ | PROT_WRITE;
+	struct iova_allocator *allocator;
+	struct dma_region *region;
+
+	/* Sanity check that the device+driver can actually send MSIs. */
+	TEST_REQUIRE(device->driver.ops);
+	TEST_REQUIRE(device->driver.ops->send_msi);
+
+	/*
+	 * Set up a DMA-able region for the driver to use.   Very few devices
+	 * provide a way to arbitrarily send interrupts (MSIs), e.g. by writing
+	 * an MMIO register.  Instead, most devices send MSIs when an action is
+	 * completed, and practically all actions involve DMA of some form.
+	 */
+	allocator = iova_allocator_init(device->iommu);
+
+	region = &device->driver.region;
+	region->size = SZ_2M;
+	region->iova = iova_allocator_alloc(allocator, region->size);
+	region->vaddr = kvm_mmap(region->size, prot, flags, -1);
+	TEST_ASSERT(region->vaddr != MAP_FAILED, "mmap() failed\n");
+	iommu_map(device->iommu, region);
+
+	iova_allocator_cleanup(allocator);
+
+	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, bool use_nmi)
+{
+	struct {
+		struct kvm_irq_routing header;
+		struct kvm_irq_routing_entry entry;
+	} routing = {
+		.header.nr = 1,
+		.entry = {
+			.gsi = gsi,
+			.type = KVM_IRQ_ROUTING_MSI,
+			.u.msi.address_lo = 0xFEE00000 | (vcpu->id & GENMASK(7, 0)) << 12,
+			.u.msi.address_hi = vcpu->id & GENMASK(31, 8),
+			.u.msi.data = use_nmi ? NMI_VECTOR | (4 << 8) : vector,
+		},
+	};
+
+	vm_ioctl(vm, KVM_SET_GSI_ROUTING, &routing.header);
+}
+
+static void kvm_set_empty_gsi_routing(struct kvm_vm *vm)
+{
+	struct kvm_irq_routing routing = {};
+
+	vm_ioctl(vm, KVM_SET_GSI_ROUTING, &routing);
+}
+
+static const char *probe_iommu_type(void)
+{
+	int io_fd;
+
+	io_fd = open("/dev/iommu", O_RDONLY);
+	if (io_fd >= 0) {
+		close(io_fd);
+		return MODE_IOMMUFD;
+	}
+
+	io_fd = __open_path_or_exit("/dev/vfio/vfio", O_RDONLY,
+				    "Is VFIO (or IOMMUFD) loaded and enabled?");
+	close(io_fd);
+	return MODE_VFIO_TYPE1_IOMMU;
+}
+
+static void help(const char *name)
+{
+	printf("Usage: %s [-a] [-d <segment:bus:device.function>] [-e] [-h] [-i nr_irqs] [-m] [-n] [-t iommu_type] [-v nr_vcpus] [-x]\n", name);
+	printf("\n");
+	printf("Tests KVM interrupt routing and delivery via irqfd.\n");
+	printf("-a	Affine the device's host IRQ to a random physical CPU\n");
+	printf("-d	Use a VFIO device to send MSI-X interrupts instead of manually signaling the eventfd\n");
+	printf("-e	Set empty GSI routing in-between some interrupts\n");
+	printf("-i	The number of IRQs to generate during the test\n");
+	printf("-m	Pin target vCPU to random physical CPU before triggering interrupt\n");
+	printf("-n	Deliver 50 percent of IRQs as non-maskable interrupts\n");
+	printf("-t	Override the IOMMU type to use (vfio_type1_iommu or iommufd)\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);
+}
+
+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);
+
+	pthread_t vcpu_threads[KVM_MAX_VCPUS];
+	struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
+	struct vfio_pci_device *device = NULL;
+	int nr_irqs = 1000, nr_vcpus = 1;
+	bool set_empty_routing = false;
+	const char *device_bdf = NULL;
+	const char *iommu_type = NULL;
+	int i, j, c, msix, eventfd;
+	bool migrate_vcpus = false;
+	cpu_set_t available_cpus;
+	bool use_nmi = false;
+	struct iommu *iommu;
+	struct kvm_vm *vm;
+	int irq, irq_cpu;
+
+	while ((c = getopt(argc, argv, "ad:ehi:mnt:v:x")) != -1) {
+		switch (c) {
+		case 'a':
+			irq_affinity = true;
+			break;
+		case 'd':
+			device_bdf = optarg;
+			break;
+		case 'e':
+			set_empty_routing = true;
+			break;
+		case 'i':
+			nr_irqs = atoi_positive("Number of IRQs", optarg);
+			break;
+		case 'm':
+			migrate_vcpus = true;
+			break;
+		case 'n':
+			use_nmi = true;
+			break;
+		case 't':
+			iommu_type = optarg;
+			break;
+		case 'v':
+			nr_vcpus = atoi_positive("Number of vCPUS", optarg);
+			TEST_ASSERT(nr_vcpus <= KVM_MAX_VCPUS,
+				    "KVM selftests support at most %u vCPUs", KVM_MAX_VCPUS);
+			break;
+		case 'x':
+			x2apic = false;
+			break;
+		case 'h':
+		default:
+			help(argv[0]);
+		}
+	}
+
+	TEST_REQUIRE(kvm_arch_has_default_irqchip());
+
+	vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
+	vm_enable_cap(vm, KVM_CAP_X2APIC_API, KVM_X2APIC_API_USE_32BIT_IDS |
+					      KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK);
+
+	vm_install_exception_handler(vm, vector, guest_irq_handler);
+	vm_install_exception_handler(vm, NMI_VECTOR, guest_nmi_handler);
+
+	if (!x2apic) {
+		TEST_ASSERT(nr_vcpus < 256, "xAPIC can only target IDs [0-254] (255 vCPUs)");
+		virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
+	}
+
+	if (device_bdf) {
+		if (!iommu_type)
+			iommu_type = probe_iommu_type();
+		iommu = iommu_init(iommu_type);
+		device = vfio_pci_device_init(device_bdf, iommu);
+		msix = vfio_setup_msi(device);
+		irq = vfio_msix_to_host_irq(device_bdf, msix);
+		eventfd = device->msi_eventfds[msix];
+		printf("Using device %s MSI-X[%d] (IRQ-%u)\n", device_bdf, msix,
+		       irq);
+	} else {
+		TEST_ASSERT(!irq_affinity,
+			    "Setting IRQ affinity (-a) requires a backing device (-d)");
+
+		eventfd = kvm_new_eventfd();
+		irq = -1;
+	}
+
+	pr_info("Injecting interrupts for GSI %d (guest vector 0x%x) %d times\n",
+		gsi, vector, nr_irqs);
+
+	kvm_assign_irqfd(vm, gsi, eventfd);
+
+	sync_global_to_guest(vm, x2apic);
+
+	if (migrate_vcpus)
+		kvm_sched_getaffinity(0, sizeof(available_cpus), &available_cpus);
+
+	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;
+	}
+
+	irq_cpu = -1;
+
+	for (i = 0; i < nr_irqs; i++) {
+		const bool do_set_empty_routing = set_empty_routing && (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_set_empty_routing)
+			kvm_set_empty_gsi_routing(vm);
+
+		kvm_route_msi(vm, gsi, vcpu, vector, do_use_nmi);
+
+		if (irq_affinity) {
+			irq_cpu = kvm_random_u64(&kvm_rng) % get_nprocs();
+			proc_irq_set_smp_affinity(irq, irq_cpu);
+		}
+
+		if (migrate_vcpus)
+			pin_task_to_random_cpu(vcpu_threads[i % nr_vcpus], &available_cpus);
+
+		for (j = 0; j < nr_vcpus; j++) {
+			TEST_ASSERT(!GUEST_RECEIVED_IRQ(vcpus[j]),
+				    "IRQ flag for vCPU %d not clear prior to test",
+				    vcpus[j]->id);
+			TEST_ASSERT(!GUEST_RECEIVED_NMI(vcpus[j]),
+				    "NMI flag for vCPU %d not clear prior to test",
+				    vcpus[j]->id);
+		}
+
+		trigger_interrupt(device, eventfd);
+
+		clock_gettime(CLOCK_MONOTONIC, &start);
+		while (!GUEST_RECEIVED_INTERRUPT(vcpu, do_use_nmi) &&
+		       timespec_to_ns(timespec_elapsed(start)) <= timeout_ns)
+			cpu_relax();
+
+		TEST_ASSERT(GUEST_RECEIVED_INTERRUPT(vcpu, do_use_nmi),
+			    "vCPU %d timed out waiting for %s (vector 0x%x) from GSI %d (via CPU %d)\n",
+			    vcpu->id, do_use_nmi ? "NMI" : "IRQ",
+			    do_use_nmi ? NMI_VECTOR : vector, gsi, irq_cpu);
+
+		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);
+
+	for (i = 0; i < nr_vcpus; i++)
+		pthread_join(vcpu_threads[i], NULL);
+
+	return 0;
+}
diff --git a/tools/testing/selftests/kvm/lib/assert.c b/tools/testing/selftests/kvm/lib/assert.c
index 8be0d09ecf0f98..1d72dcdfce3b66 100644
--- a/tools/testing/selftests/kvm/lib/assert.c
+++ b/tools/testing/selftests/kvm/lib/assert.c
@@ -10,6 +10,7 @@
 #include <sys/syscall.h>
 
 #include "kselftest.h"
+#include "kvm_syscalls.h"
 
 #ifdef __GLIBC__
 #include <execinfo.h>
@@ -64,11 +65,6 @@ static void test_dump_stack(void)
 static void test_dump_stack(void) {}
 #endif
 
-static pid_t _gettid(void)
-{
-	return syscall(SYS_gettid);
-}
-
 void __attribute__((noinline))
 test_assert(bool exp, const char *exp_str,
 	const char *file, unsigned int line, const char *fmt, ...)
@@ -81,7 +77,7 @@ test_assert(bool exp, const char *exp_str,
 		fprintf(stderr, "==== Test Assertion Failure ====\n"
 			"  %s:%u: %s\n"
 			"  pid=%d tid=%d errno=%d - %s\n",
-			file, line, exp_str, getpid(), _gettid(),
+			file, line, exp_str, getpid(), kvm_gettid(),
 			errno, strerror(errno));
 		test_dump_stack();
 		if (fmt) {
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 195f3fdae1e396..3794575d2ca0ce 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -15,14 +15,22 @@
 #include <sys/resource.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <time.h>
 #include <unistd.h>
 #include <linux/kernel.h>
 
 #define KVM_UTIL_MIN_PFN	2
 
-u32 guest_random_seed;
-struct guest_random_state guest_rng;
-static u32 last_guest_seed;
+u32 kvm_random_seed;
+struct kvm_random_state kvm_rng;
+static u32 last_kvm_seed;
+
+static void kvm_seed_rng(u32 seed)
+{
+	kvm_random_seed = last_kvm_seed = seed;
+	pr_info("Random seed: 0x%x\n", kvm_random_seed);
+	kvm_rng = new_kvm_random_state(kvm_random_seed);
+}
 
 static size_t vcpu_mmap_sz(void);
 
@@ -515,12 +523,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 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;
-	}
-	guest_rng = new_guest_random_state(guest_random_seed);
-	sync_global_to_guest(vm, guest_rng);
+	if (kvm_random_seed != last_kvm_seed)
+		kvm_seed_rng(kvm_random_seed);
+
+	sync_global_to_guest(vm, kvm_rng);
 
 	kvm_arch_vm_post_create(vm, nr_runnable_vcpus);
 
@@ -662,19 +668,39 @@ void kvm_print_vcpu_pinning_help(void)
 	       "     (default: no pinning)\n", name, name);
 }
 
+void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus)
+{
+	int target_idx;
+	int nr_cpus;
+	int cpu;
+
+	nr_cpus = CPU_COUNT(possible_cpus);
+	TEST_ASSERT(nr_cpus > 0, "No CPUs available in possible_cpus");
+
+	target_idx = kvm_random_u64(&kvm_rng) % nr_cpus;
+
+	for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
+		if (CPU_ISSET(cpu, possible_cpus) && target_idx-- == 0) {
+			pin_task_to_cpu(task, cpu);
+			return;
+		}
+	}
+
+	TEST_FAIL("Failed to find random CPU in possible_cpus");
+}
+
 void kvm_parse_vcpu_pinning(const char *pcpus_string, u32 vcpu_to_pcpu[],
 			    int nr_vcpus)
 {
 	cpu_set_t allowed_mask;
 	char *cpu, *cpu_list;
 	char delim[2] = ",";
-	int i, r;
+	int i;
 
 	cpu_list = strdup(pcpus_string);
 	TEST_ASSERT(cpu_list, "strdup() allocation failed.");
 
-	r = sched_getaffinity(0, sizeof(allowed_mask), &allowed_mask);
-	TEST_ASSERT(!r, "sched_getaffinity() failed");
+	kvm_sched_getaffinity(0, sizeof(allowed_mask), &allowed_mask);
 
 	cpu = strtok(cpu_list, delim);
 
@@ -2279,8 +2305,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);
+	srandom(time(0));
+	kvm_seed_rng(random());
 
 	kvm_selftest_arch_init();
 }
diff --git a/tools/testing/selftests/kvm/lib/memstress.c b/tools/testing/selftests/kvm/lib/memstress.c
index 6dcd15910a0608..3599b75d97c919 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(u32 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;
 	gva_t gva;
 	u64 pages;
 	u64 addr;
 	u64 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(u32 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))
 				*(u64 *)addr = 0x0123456789ABCDEF;
 			else
 				READ_ONCE(*(u64 *)addr);
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 00000000000000..3960b3841d6370
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/proc_util.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "kvm_util.h"
+#include "test_util.h"
+#include "proc_util.h"
+
+static FILE *open_proc_interrupts(void)
+{
+	FILE *fp;
+
+	fp = fopen("/proc/interrupts", "r");
+	TEST_ASSERT(fp, "fopen(/proc/interrupts) failed");
+
+	return fp;
+}
+
+unsigned int vfio_msix_to_host_irq(const char *device_bdf, int msix)
+{
+	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]", msix);
+
+	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 (unsigned int)irq;
+}
+
+void proc_irq_set_smp_affinity(unsigned int irq, int cpu)
+{
+	char path[PATH_MAX];
+	int r, fd;
+
+	snprintf(path, sizeof(path), "/proc/irq/%u/smp_affinity_list", irq);
+	fd = open(path, O_RDWR);
+	TEST_ASSERT(fd >= 0, "Failed to open %s", path);
+
+	r = dprintf(fd, "%d\n", cpu);
+	TEST_ASSERT(r > 0, "Failed to affinitize IRQ-%u to CPU %d", irq, cpu);
+
+	kvm_close(fd);
+}
diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c
index bab1bd2b775b6d..6b00ab11f3c025 100644
--- a/tools/testing/selftests/kvm/lib/test_util.c
+++ b/tools/testing/selftests/kvm/lib/test_util.c
@@ -17,6 +17,7 @@
 #include "linux/kernel.h"
 
 #include "test_util.h"
+#include "kvm_syscalls.h"
 
 sigjmp_buf expect_sigbus_jmpbuf;
 
@@ -30,18 +31,36 @@ void __attribute__((used)) expect_sigbus_handler(int signum)
  * Park-Miller LCG using standard constants.
  */
 
-struct guest_random_state new_guest_random_state(u32 seed)
+struct kvm_random_state new_kvm_random_state(u32 seed)
 {
-	struct guest_random_state s = {.seed = seed};
+	struct kvm_random_state s = {.seed = seed};
 	return s;
 }
 
-u32 guest_random_u32(struct guest_random_state *state)
+u32 kvm_random_u32(struct kvm_random_state *state)
 {
 	state->seed = (u64)state->seed * 48271 % ((u32)(1 << 31) - 1);
 	return state->seed;
 }
 
+/* Returns a random u64 in the inclusive range [min, max] */
+u64 kvm_random_u64_in_range(struct kvm_random_state *state, u64 min,
+			    u64 max)
+{
+	u64 value;
+	u64 range;
+
+	TEST_ASSERT(min <= max, "PEBKAC, min = 0x%lx, max = 0x%lx", min, max);
+
+	value = kvm_random_u64(state);
+
+	range = max - min;
+	if (range == ULLONG_MAX)
+		return value;
+
+	return min + (value % (range + 1));
+}
+
 /*
  * Parses "[0-9]+[kmgt]?".
  */
@@ -377,7 +396,7 @@ long get_run_delay(void)
 	long val[2];
 	FILE *fp;
 
-	sprintf(path, "/proc/%ld/schedstat", syscall(SYS_gettid));
+	sprintf(path, "/proc/%ld/schedstat", (long)kvm_gettid());
 	fp = fopen(path, "r");
 	/* Return MIN_RUN_DELAY_NS upon failure just to be safe */
 	if (fscanf(fp, "%ld %ld ", &val[0], &val[1]) < 2)
diff --git a/tools/testing/selftests/kvm/mmu_stress_test.c b/tools/testing/selftests/kvm/mmu_stress_test.c
index 54d281419d3104..3d5f33a63b2b4a 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__)
 	/*
@@ -257,11 +255,7 @@ static void rendezvous_with_vcpus(struct timespec *time, const char *name)
 static void calc_default_nr_vcpus(void)
 {
 	cpu_set_t possible_mask;
-	int r;
-
-	r = sched_getaffinity(0, sizeof(possible_mask), &possible_mask);
-	TEST_ASSERT(!r, "sched_getaffinity failed, errno = %d (%s)",
-		    errno, strerror(errno));
+	kvm_sched_getaffinity(0, sizeof(possible_mask), &possible_mask);
 
 	nr_vcpus = CPU_COUNT(&possible_mask);
 	TEST_ASSERT(nr_vcpus > 0, "Uh, no CPUs?");
@@ -383,8 +377,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/rseq_test.c b/tools/testing/selftests/kvm/rseq_test.c
index f80ad6b47d16b0..557e393c223b98 100644
--- a/tools/testing/selftests/kvm/rseq_test.c
+++ b/tools/testing/selftests/kvm/rseq_test.c
@@ -226,9 +226,7 @@ int main(int argc, char *argv[])
 		}
 	}
 
-	r = sched_getaffinity(0, sizeof(possible_mask), &possible_mask);
-	TEST_ASSERT(!r, "sched_getaffinity failed, errno = %d (%s)", errno,
-		    strerror(errno));
+	kvm_sched_getaffinity(0, sizeof(possible_mask), &possible_mask);
 
 	calc_min_max_cpu();
 
@@ -244,7 +242,7 @@ int main(int argc, char *argv[])
 	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
 
 	pthread_create(&migration_thread, NULL, migration_worker,
-		       (void *)(unsigned long)syscall(SYS_gettid));
+		       (void *)(unsigned long)kvm_gettid());
 
 	if (latency >= 0) {
 		/*
diff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c
index 76fcdd1fd3cb48..2de87549fcc038 100644
--- a/tools/testing/selftests/kvm/steal_time.c
+++ b/tools/testing/selftests/kvm/steal_time.c
@@ -70,8 +70,8 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu)
 static void steal_time_init(struct kvm_vcpu *vcpu, u32 i)
 {
 	/* 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));
 
 	vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, (ulong)st_gva[i] | KVM_MSR_ENABLED);
 }
@@ -187,8 +187,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, u32 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));
 
 	st_ipa = (ulong)st_gva[i];
 	vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev);
@@ -310,10 +309,8 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu)
 static void steal_time_init(struct kvm_vcpu *vcpu, u32 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, (gva_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, (gva_t)st_gva[i]));
 }
 
 static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx)
@@ -442,8 +439,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, u32 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");
@@ -549,8 +545,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",
@@ -570,8 +565,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);
diff --git a/tools/testing/selftests/kvm/x86/sev_dbg_test.c b/tools/testing/selftests/kvm/x86/sev_dbg_test.c
index a9d8e4c059f969..eaa8201b937d30 100644
--- a/tools/testing/selftests/kvm/x86/sev_dbg_test.c
+++ b/tools/testing/selftests/kvm/x86/sev_dbg_test.c
@@ -34,7 +34,7 @@ static void validate_buffers(void)
 
 static void ____test_sev_dbg(struct kvm_vm *vm, int i, int j, int nr_bytes)
 {
-	u8 pattern = guest_random_u32(&guest_rng);
+	u8 pattern = kvm_random_u32(&kvm_rng);
 
 	if (i + nr_bytes > BUFFER_SIZE || j + nr_bytes > BUFFER_SIZE)
 		return;

3/2 2026/06/27 01:39 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17818
							output: 66
							thoughts: 884
						

Thoughts:
**Analyzing the Patch for Fuzzing Worthiness**

Okay, so I've got this patch for KVM selftests. It's adding a new test, `irq_test.c`, and making a few changes to existing test files within `tools/testing/selftests/kvm/`.  It looks like there's some renaming of `guest_random_*` to `kvm_random_*` and a bit of file reorganization too.  Now, the prompt specifically asks to return `WorthFuzzing=false` if the patch only contains modifications to things like documentation, Kconfig, comments, decorative changes, numeric constants that don't change execution, or unreachable code.

My initial thought is this looks like it’s *entirely* contained within the selftests directory.  Selftests, by definition, are user-space tests. They're not kernel code.  I need to be very sure about this, so I’m double-checking the changed files. I'm going through the diff, verifying that every single file is within `tools/testing/selftests/kvm/`.  Let's see: `Makefile.kvm`, `arch_timer.c`, `arm64/arch_timer_edge_cases.c`, `demand_paging_test.c`, the dirty log tests, all the include headers, `irq_test.c`, the library files (`assert.c`, `kvm_util.c`, etc.), the memory stress test, RSEQ test, steal time, and the SEV debug test, all within the `kvm` selftests directory.

Yup, it's all in `tools/testing/selftests/kvm/`.  This confirms it's strictly test code, not core kernel code, drivers, or any architecture-specific code that would impact the *kernel's* execution flow.  The prompt directs me to use my code search tools if this were to touch core kernel logic, but since it is self-tests, there's no need. Since I’ve established that it *only* touches the test infrastructure, and the prompt specifies not to fuzz it if it touches *only* test code, I'm confident in setting `WorthFuzzing=false`. I’ll double-check the diff one last time to be extra sure, but I'm pretty certain there are no other files involved.

My final determination is `WorthFuzzing=false` because the patch's scope is limited to KVM selftests, which are user-space tests and don't modify kernel code.



4/2 2026/06/27 01:39 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies user-space selftests in tools/testing/selftests/kvm/ and does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies user-space selftests in tools/testing/selftests/kvm/ and does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)