mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Xiang Zheng <xiang.zheng@linux.alibaba.com>
To: Paolo Bonzini <pbonzini@redhat.com>,
	Sean Christopherson <seanjc@google.com>,
	Thomas Gleixner <tglx@kernel.org>, Ingo Molnar <mingo@redhat.com>,
	Borislav Petkov <bp@alien8.de>,
	Dave Hansen <dave.hansen@linux.intel.com>,
	"H . Peter Anvin" <hpa@zytor.com>,
	Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>,
	Shuah Khan <shuah@kernel.org>
Cc: Shannon Zhao <shannon.zhao@linux.alibaba.com>,
	kvm@vger.kernel.org, x86@kernel.org,
	linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org
Subject: [PATCH 2/2] KVM: selftests: x86: Add RTC EOI retargeting regression test
Date: Tue, 20 Oct 2026 14:00:53 +0800	[thread overview]
Message-ID: <20261020060053.25011-3-xiang.zheng@linux.alibaba.com> (raw)
In-Reply-To: <20261020060053.25011-1-xiang.zheng@linux.alibaba.com>

Inject the RTC interrupt (GSI 8) from userspace, let the target vCPU
handle it and write the EOI, then have the guest retarget the RTC to
the other vCPU before the next injection.  Every injection must be
reported as delivered by KVM_IRQ_LINE_STATUS, and each target vCPU must
see its own interrupt count advance, for 64 rounds.

Without the ioapic fix, and with AVIC enabled, the second injection is
reported as coalesced and the test fails; it passes with AVIC disabled
or with the fix applied.

Signed-off-by: Xiang Zheng <xiang.zheng@linux.alibaba.com>
---
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../selftests/kvm/x86/rtc_eoi_retarget_test.c | 155 ++++++++++++++++++
 2 files changed, 156 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/x86/rtc_eoi_retarget_test.c

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index f43549a56f0b..7bc2db4c57be 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -140,6 +140,7 @@ TEST_GEN_PROGS_x86 += x86/apic_bus_clock_test
 TEST_GEN_PROGS_x86 += x86/xapic_ipi_test
 TEST_GEN_PROGS_x86 += x86/xapic_state_test
 TEST_GEN_PROGS_x86 += x86/xapic_tpr_test
+TEST_GEN_PROGS_x86 += x86/rtc_eoi_retarget_test
 TEST_GEN_PROGS_x86 += x86/xcr0_cpuid_test
 TEST_GEN_PROGS_x86 += x86/xss_msr_test
 TEST_GEN_PROGS_x86 += x86/debug_regs
diff --git a/tools/testing/selftests/kvm/x86/rtc_eoi_retarget_test.c b/tools/testing/selftests/kvm/x86/rtc_eoi_retarget_test.c
new file mode 100644
index 000000000000..564bc7199c1c
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/rtc_eoi_retarget_test.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stdio.h>
+#include <unistd.h>
+
+#include "apic.h"
+#include "kvm_util.h"
+#include "processor.h"
+#include "test_util.h"
+
+#define IOAPIC_GPA		0xfec00000ULL
+#define IOAPIC_REGSEL		0x00
+#define IOAPIC_WINDOW		0x10
+#define IOAPIC_REDIR_LO(pin)	(0x10 + 2 * (pin))
+#define IOAPIC_REDIR_HI(pin)	(0x11 + 2 * (pin))
+
+#define RTC_GSI			8
+#define RTC_VECTOR		0xd1
+#define NR_VCPUS		2
+#define NR_ROUNDS		64
+
+static u32 irq_count[NR_VCPUS];
+
+static void pio_outb(u16 port, u8 val)
+{
+	asm volatile("outb %0, %1" : : "a"(val), "Nd"(port));
+}
+
+static void ioapic_write(u32 reg, u32 val)
+{
+	WRITE_ONCE(*(u32 *)(IOAPIC_GPA + IOAPIC_REGSEL), reg);
+	WRITE_ONCE(*(u32 *)(IOAPIC_GPA + IOAPIC_WINDOW), val);
+}
+
+static void rtc_handler(struct ex_regs *regs)
+{
+	u32 me = GET_APIC_ID_FIELD(xapic_read_reg(APIC_ID));
+
+	WRITE_ONCE(irq_count[me], READ_ONCE(irq_count[me]) + 1);
+	xapic_write_reg(APIC_EOI, 0);
+}
+
+static void guest_code(void)
+{
+	u32 me, expected = 0;
+
+	xapic_enable();
+	me = GET_APIC_ID_FIELD(xapic_read_reg(APIC_ID));
+
+	if (!me) {
+		/* Mask the PIC so only the IOAPIC contributes to IRQ status. */
+		pio_outb(0x21, 0xff);
+		pio_outb(0xa1, 0xff);
+		ioapic_write(IOAPIC_REDIR_HI(RTC_GSI), 0);
+		ioapic_write(IOAPIC_REDIR_LO(RTC_GSI), RTC_VECTOR);
+	}
+	sti();
+	GUEST_SYNC1(0);
+
+	for (;;) {
+		expected++;
+		while (READ_ONCE(irq_count[me]) < expected)
+			cpu_relax();
+		/* Change the route before the next lazy EOI update. */
+		ioapic_write(IOAPIC_REDIR_HI(RTC_GSI), (me ^ 1) << 24);
+		GUEST_SYNC1(READ_ONCE(irq_count[me]));
+	}
+}
+
+static void run_until_sync(struct kvm_vcpu *vcpu, u64 expected)
+{
+	struct ucall uc;
+
+	vcpu_run(vcpu);
+	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+	switch (get_ucall(vcpu, &uc)) {
+	case UCALL_SYNC:
+		TEST_ASSERT(uc.args[0] == expected,
+			    "vCPU%u: got %lu RTC interrupts, expected %lu",
+			    vcpu->id, (unsigned long)uc.args[0],
+			    (unsigned long)expected);
+		break;
+	case UCALL_ABORT:
+		REPORT_GUEST_ASSERT(uc);
+	default:
+		TEST_FAIL("Unexpected ucall %lu", uc.cmd);
+	}
+}
+
+static int inject_rtc(struct kvm_vm *vm)
+{
+	struct kvm_irq_level irq = { .irq = RTC_GSI, .level = 1 };
+	int status;
+
+	vm_ioctl(vm, KVM_IRQ_LINE_STATUS, &irq);
+	status = irq.status;
+
+	/*
+	 * Deassert (level 0) to clear the IOAPIC IRR bit.  An edge assert
+	 * leaves IRR set, so without this the next round would be reported
+	 * as already pending, not delivered.  .irq and .status share a union.
+	 */
+	irq.irq = RTC_GSI;
+	irq.level = 0;
+	vm_ioctl(vm, KVM_IRQ_LINE_STATUS, &irq);
+
+	return status;
+}
+
+int main(void)
+{
+	struct kvm_vcpu *vcpus[NR_VCPUS];
+	struct kvm_irqchip chip = { .chip_id = KVM_IRQCHIP_IOAPIC };
+	struct kvm_vm *vm;
+	int round, status, r;
+
+	TEST_REQUIRE(kvm_has_cap(KVM_CAP_IRQCHIP));
+	TEST_REQUIRE(kvm_has_cap(KVM_CAP_IRQ_INJECT_STATUS));
+
+	vm = vm_create_with_vcpus(NR_VCPUS, guest_code, vcpus);
+	vm_install_exception_handler(vm, RTC_VECTOR, rtc_handler);
+	virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
+	virt_pg_map(vm, IOAPIC_GPA, IOAPIC_GPA);
+
+	/*
+	 * Require a full in-kernel IOAPIC; vm_create_irqchip() silently falls
+	 * back to a split irqchip when KVM_CREATE_IRQCHIP is unavailable.
+	 */
+	r = __vm_ioctl(vm, KVM_GET_IRQCHIP, &chip);
+	__TEST_REQUIRE(!r || (errno != ENOTTY && errno != ENXIO),
+		       "Full in-kernel IRQCHIP is required");
+	TEST_ASSERT_VM_VCPU_IOCTL(!r, KVM_GET_IRQCHIP, r, vm);
+
+	alarm(30);
+	/* vCPU0 programs the IOAPIC, so it must run first. */
+	run_until_sync(vcpus[0], 0);
+	run_until_sync(vcpus[1], 0);
+
+	for (round = 0; round < NR_ROUNDS; round++) {
+		int target = round & 1;
+
+		status = inject_rtc(vm);
+		TEST_ASSERT(status == 1,
+			    "round %d: RTC to vCPU%d not delivered, status %d%s",
+			    round, target, status,
+			    status == 0 ? " (coalesced)" : "");
+		run_until_sync(vcpus[target], round / 2 + 1);
+	}
+	alarm(0);
+
+	printf("PASS: %d RTC injections delivered across target changes\n",
+	       NR_ROUNDS);
+	kvm_vm_free(vm);
+	return 0;
+}
-- 
2.50.1 (Apple Git-155)


      parent reply	other threads:[~2026-10-20  6:01 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-20  6:00 [PATCH 0/2] KVM: x86: Fix stale RTC pending-EOI tracking under AVIC Xiang Zheng
2026-10-20  6:00 ` [PATCH 1/2] KVM: x86: ioapic: Clear stale RTC pending EOIs using the delivery record Xiang Zheng
2026-10-20  6:00 ` Xiang Zheng [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261020060053.25011-3-xiang.zheng@linux.alibaba.com \
    --to=xiang.zheng@linux.alibaba.com \
    --cc=bp@alien8.de \
    --cc=dave.hansen@linux.intel.com \
    --cc=hpa@zytor.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=seanjc@google.com \
    --cc=shannon.zhao@linux.alibaba.com \
    --cc=shuah@kernel.org \
    --cc=suravee.suthikulpanit@amd.com \
    --cc=tglx@kernel.org \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®