[PATCH v4 17/19] KVM: selftests: Print vCPU affinity on timeout during IRQ test

From: Josh Hilke

Date: Fri May 29 2026 - 20:34:05 EST


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@xxxxxxxxxx>
---
.../testing/selftests/kvm/include/kvm_util.h | 1 +
tools/testing/selftests/kvm/irq_test.c | 2 +
tools/testing/selftests/kvm/lib/kvm_util.c | 49 +++++++++++++++++++
3 files changed, 52 insertions(+)

diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index f725ab0ed4f9..0ec82088bc43 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -1119,6 +1119,7 @@ static inline int pin_self_to_any_cpu(void)
void kvm_print_vcpu_pinning_help(void);
void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[],
int nr_vcpus);
+void kvm_print_vcpu_affinity(struct kvm_vcpu *vcpu, pid_t tid);

unsigned long vm_compute_max_gfn(struct kvm_vm *vm);
unsigned int vm_calc_num_guest_pages(enum vm_guest_mode mode, size_t size);
diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c
index f653317022e3..0db4ea4eb1ef 100644
--- a/tools/testing/selftests/kvm/irq_test.c
+++ b/tools/testing/selftests/kvm/irq_test.c
@@ -309,6 +309,8 @@ int main(int argc, char **argv)
printf(" is interrupt NMI: %s\n", do_use_nmi ? "true" : "false");
if (irq_affinity)
printf(" irq_cpu: %d\n", irq_cpu);
+ if (migrate_vcpus)
+ kvm_print_vcpu_affinity(vcpu, vcpu_tids[vcpu->id]);

TEST_FAIL("vCPU %d timed out waiting for IRQ from GSI %d (Vector 0x%x) !\n",
vcpu->id, gsi, vector);
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 29cd649a54d5..b2e534f331e2 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -15,6 +15,7 @@
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/sysinfo.h>
#include <unistd.h>
#include <linux/kernel.h>

@@ -662,6 +663,54 @@ void kvm_print_vcpu_pinning_help(void)
" (default: no pinning)\n", name, name);
}

+void kvm_print_vcpu_affinity(struct kvm_vcpu *vcpu, pid_t tid)
+{
+ int nprocs = get_nprocs();
+ bool first_range = true;
+ int i, start = -1;
+ cpu_set_t cpus;
+
+ kvm_sched_getaffinity(tid, sizeof(cpus), &cpus);
+
+ /*
+ * Output format examples:
+ * - Single CPU: "vCPU 0 (TID 1234) affined to pCPU(s): 2"
+ * - List: "vCPU 0 (TID 1234) affined to pCPU(s): 0,2,4"
+ * - Range: "vCPU 0 (TID 1234) affined to pCPU(s): 0-7"
+ * - Mixed: "vCPU 0 (TID 1234) affined to pCPU(s): 0-3,8,10-12"
+ */
+ printf("vCPU %u (TID %d) affined to pCPU(s): ", vcpu->id, tid);
+
+ for (i = 0; i <= nprocs; i++) {
+ /*
+ * Iterate to nprocs (inclusive) to ensure that if the last pCPU
+ * is part of a range, the 'else' block triggers one final time
+ * to flush that range to stdout.
+ */
+ if (i < nprocs && CPU_ISSET(i, &cpus)) {
+ if (start == -1)
+ start = i;
+ continue;
+ }
+
+ if (start != -1) {
+ int end = i - 1;
+
+ if (!first_range)
+ printf(",");
+
+ if (start == end)
+ printf("%d", start);
+ else
+ printf("%d-%d", start, end);
+
+ start = -1;
+ first_range = false;
+ }
+ }
+ printf("\n");
+}
+
void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus)
{
int target_idx;
--
2.54.0.929.g9b7fa37559-goog