From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 4AAA5C43458 for ; Fri, 26 Jun 2026 21:56:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:Reply-To:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Type:Cc:To: From:Subject:Message-ID:References:Mime-Version:In-Reply-To:Date: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=ZSZM6YQ5KHnml7cuEP2t6/hmi1sPUG+74rGvCiTMMxg=; b=W9FKSJZ0rgR+ayuRP5d4l3ghKj j4/Pwmz2PaEg66RHbrM6L6Y6+5OUn0vwa/ckYIDRQqeMh6Zk6vFGagdqPGdSlpjX83D+JmseBZ/93 6q7CMUh2SdtJwh1/Ppitfog5RJ5SPT0ewj/tiExB3JNRlM4JAUq4UkCgc3T5OfDbsdnXNZO1hmhiO 1H7XLrQD7vi/uPNvJdEMyZkfQrFSU3y3bwbyne63HUtl32Q0nHmOOSWgmDv0HvwJ3BO1SxiV2/JUf 2Ny8Hvm1wN5lmI1irLsFfg1o+oObJtGcpNbhJDibL8FWXNSZzhWTtzmn1PbGGvXByhQQTzK/KVyj4 3Ar8xeDQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1wdEDX-0000000BuWa-0mMe; Fri, 26 Jun 2026 21:36:07 +0000 Received: from mail-pj1-x104a.google.com ([2607:f8b0:4864:20::104a]) by bombadil.infradead.org with esmtps (Exim 4.99.1 #2 (Red Hat Linux)) id 1wdEDC-0000000BuHf-2Gg4 for linux-arm-kernel@lists.infradead.org; Fri, 26 Jun 2026 21:35:47 +0000 Received: by mail-pj1-x104a.google.com with SMTP id 98e67ed59e1d1-36d97a4e08fso1085590a91.0 for ; Fri, 26 Jun 2026 14:35:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1782509745; x=1783114545; darn=lists.infradead.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=ZSZM6YQ5KHnml7cuEP2t6/hmi1sPUG+74rGvCiTMMxg=; b=Qn4Dt7wAmdmSPIg3t8/3Qi1h0uuIAy4if5L7gyFierdSzciRBdVp/VBxAhFMlBVaFA i2BG4XytX6fopJr5i+OpOjXpDCSPUkI3tuxqV8y2lPqjm4qMGLBIuVGRYk/R9c7q5ZQr mVscWNpy1LDAFdFLUpplAwbexiI5rJAfN2owRjjN85J4VMkJDq+Z+1xOhwFA3CNxDfg7 NKsq+ahkpb+Lq2ONYxFqEuBGnHmcHoxB4eE5I2MJYEccsphQU1t1KPHoDHuLssZpBUWm fgO1iv16t3Mt9aBSayTyUv0/WjqP9/xsBvrKvicSKygO6dLjXXlAMIiYA/wG+KohP4wX Y0tg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782509745; x=1783114545; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=ZSZM6YQ5KHnml7cuEP2t6/hmi1sPUG+74rGvCiTMMxg=; b=Bw0M74EsQix85MEyGeQhluXjwvdsD6Gu9YMbV91iiEz5UmIZiCV0UD3+PHnFoJNW03 TYPnU8tQF53pIjHgnINPMjxZ56ZxfOP+zXnc6jGC6D8OsdI+HMRH4K+Kj+RRALkEHgVe LIogGWO9/Slt5v1OakIKKf3XnsctL2GxOG+09zLMoRllzjRswjgRAgMy8OPm5OCuVcKy qd/S/kCOqfPvQKFhhm0xbozJ7jZ+qk5YgWw5O1LvkqVvjBRF3CrqgITYRMSt9cgqPKAk v1jlKZOmXuURCtGVlkW7CwiTnBacr+1TzRZ7s63dl5IQLAEWhqQ5NOosw5dJSV6T1Bgo vsiQ== X-Forwarded-Encrypted: i=1; AHgh+Roebr6ZCRHBdb5975v28Jf/+BQok7pLuaPKc/VNAn+2nc6LL3u0twO0gW6gYp59Zp6re7yeY1wSvp2PvM9YB9Mj@lists.infradead.org X-Gm-Message-State: AOJu0Yxs0oA/9b33VVko4o3yhrQMrx+ZqtYQAxXqiJ3Y+h2mQbcVNLic cfJ1NvzIOQNEOrCsnXlWO0lgYryr4V/VO8p1P5WucQCzPjvLMV3wfUrdJw6hWi7c5EBrKcQ8Zf3 G5kL+Hg== X-Received: from pjub20.prod.google.com ([2002:a17:90a:cc14:b0:37d:f152:3b63]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:270b:b0:37c:6973:5a65 with SMTP id 98e67ed59e1d1-37dfa1ead47mr7035730a91.17.1782509745112; Fri, 26 Jun 2026 14:35:45 -0700 (PDT) Date: Fri, 26 Jun 2026 14:35:20 -0700 In-Reply-To: <20260626213534.3866178-1-seanjc@google.com> Mime-Version: 1.0 References: <20260626213534.3866178-1-seanjc@google.com> X-Mailer: git-send-email 2.55.0.rc0.799.gd6f94ed593-goog Message-ID: <20260626213534.3866178-8-seanjc@google.com> Subject: [PATCH v8 07/20] KVM: selftests: Add an irqfd send+receive (and later IRQ bypass) test From: Sean Christopherson To: Paolo Bonzini , Marc Zyngier , Oliver Upton , Sean Christopherson Cc: Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , kvm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org, David Matlack , Josh Hilke Content-Type: text/plain; charset="UTF-8" X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20260626_143546_597698_A903170B X-CRM114-Status: GOOD ( 21.99 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Sean Christopherson Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: David Matlack Add a new test, irq_test to verify that KVM correctly delivers interrupts to a running vCPU, when triggered via an eventfd bound to a KVM GSI using KVM's irqfd mechanism. This test is intentionally simple, for now. Support for sending interrupts via VFIO devices, for IRQ bypass, and for other features will be added in the near future. Add the test in common code, even though it currently will only build and run on x86, as the concept and the bulk of the host-side code isn't specific to x86. Suggested-by: Sean Christopherson Link: https://lore.kernel.org/kvm/20250404193923.1413163-68-seanjc@google.com Signed-off-by: David Matlack Co-developed-by: Josh Hilke Signed-off-by: Josh Hilke [sean: use while() and TEST_ASSERT() instead of if-statement => TEST_FAIL()] Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/irq_test.c | 170 +++++++++++++++++++++++ 2 files changed, 171 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 ae0dfd67dd31..a24875fada03 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -157,6 +157,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 diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c new file mode 100644 index 000000000000..9f8895b89821 --- /dev/null +++ b/tools/testing/selftests/kvm/irq_test.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "kvm_util.h" +#include "test_util.h" +#include "apic.h" +#include "processor.h" + +#include +#include +#include +#include +#include + +static u64 timeout_ns = 2ULL * 1000 * 1000 * 1000; +static bool guest_ready_for_irqs[KVM_MAX_VCPUS]; +static bool guest_received_irq[KVM_MAX_VCPUS]; +static bool done; + +#define GUEST_RECEIVED_IRQ(__vcpu) \ + SYNC_FROM_GUEST_AND_READ((__vcpu)->vm, guest_received_irq[(__vcpu)->id]) + +static u32 guest_get_vcpu_id(void) +{ + return x2apic_read_reg(APIC_ID); +} + +static void guest_irq_handler(struct ex_regs *regs) +{ + WRITE_ONCE(guest_received_irq[guest_get_vcpu_id()], true); + + x2apic_write_reg(APIC_EOI, 0); +} + +static void guest_code(void) +{ + x2apic_enable(); + + sti_nop(); + + WRITE_ONCE(guest_ready_for_irqs[guest_get_vcpu_id()], true); + + while (!READ_ONCE(done)) + cpu_relax(); + + GUEST_DONE(); +} + +static void *vcpu_thread_main(void *arg) +{ + struct kvm_vcpu *vcpu = arg; + struct ucall uc; + + vcpu_run(vcpu); + TEST_ASSERT_EQ(UCALL_DONE, get_ucall(vcpu, &uc)); + + return NULL; +} + +static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu, + u8 vector) +{ + struct { + struct kvm_irq_routing 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 << 12), + .u.msi.data = vector, + }, + }; + + vm_ioctl(vm, KVM_SET_GSI_ROUTING, &routing.header); +} + +static void help(const char *name) +{ + printf("Usage: %s [-h]\n", name); + printf("\n"); + printf("Tests KVM interrupt routing and delivery via irqfd.\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(); + + pr_info("Injecting interrupts for GSI %d (guest vector 0x%x) %d times\n", + gsi, vector, nr_irqs); + + kvm_assign_irqfd(vm, gsi, eventfd); + + for (i = 0; i < nr_vcpus; i++) + pthread_create(&vcpu_threads[i], NULL, vcpu_thread_main, vcpus[i]); + + for (i = 0; i < nr_vcpus; i++) { + struct kvm_vcpu *vcpu = vcpus[i]; + + while (!SYNC_FROM_GUEST_AND_READ(vm, guest_ready_for_irqs[vcpu->id])) + continue; + } + + for (i = 0; i < nr_irqs; i++) { + struct kvm_vcpu *vcpu = vcpus[i % nr_vcpus]; + struct timespec start; + + kvm_route_msi(vm, gsi, vcpu, vector); + + for (j = 0; j < nr_vcpus; j++) + TEST_ASSERT(!GUEST_RECEIVED_IRQ(vcpus[j]), + "IRQ flag for vCPU %d not clear prior to test", + vcpus[j]->id); + + eventfd_write(eventfd, 1); + + clock_gettime(CLOCK_MONOTONIC, &start); + while (!GUEST_RECEIVED_IRQ(vcpu) && + timespec_to_ns(timespec_elapsed(start)) <= timeout_ns) + cpu_relax(); + + TEST_ASSERT(GUEST_RECEIVED_IRQ(vcpu), + "vCPU %d timed out waiting for IRQ (vector 0x%x) from GSI %d\n", + vcpu->id, vector, gsi); + + 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; +} -- 2.55.0.rc0.799.gd6f94ed593-goog