[kvm-unit-tests PATCH] x86/pmu: Add option to raise NMI on overflow

Sandipan Das <[email protected]>
Newsgroups org.kernel.vger.kvm
Message-ID <106bc3a4660653cb7750f3ae07a4dbca42c2762c.1786356229.git.sandipan.das@amd.com>
Interrupts raised on counter overflow are currently delivered through
the PMI_VECTOR as regular maskable interrupts. However, for some
configurations, like SVM with PMC virtualization and VNMI enabled, the
guest LVTPC is ignored and the interrupts are always delivered as NMIs.
This causes the test to crash, as seen below, because it does not
expect NMIs.

  Unhandled exception 2 #NMI(0) at ip 0000000000400326
  error_code=0000      rflags=00000056      cs=00000008
  rax=00000000001300c0 rcx=00000000c0010202 rdx=0000000000000000 rbx=0000000001019f58
  rbp=0000000001019ee0 rsi=00000000000f4240 rdi=0000000001019f58
   r8=ffffffffffffe000  r9=00000000000003f8 r10=000000000000000d r11=0000000000000020
  r12=00000000001300c0 r13=ffffffffff6768f0 r14=ffffffffff6768f0 r15=0000000000000001
  cr0=0000000080010011 cr2=0000000000000000 cr3=00000000010fe000 cr4=0000000000000020
  cr8=0000000000000000
          STACK: @400326 400de1 401715 401e51 4001bd

Add an optional "nmi" argument to improve test coverage for such
configurations. When passed, the LVTPC has the delivery mode changed to
NMI (APIC_DM_NMI) and the overflow handler is installed on the
NMI_VECTOR. Otherwise, it keeps using the maskable PMI_VECTOR.

Unlike maskable interrupts, which stay pending until EFLAGS.IF is set,
an NMI is not gated by it and arrives as soon as the counter overflows.
Clearing the interrupt count after the counter has overflowed therefore
misses the early-arriving NMIs. Hence, reset it before arming the
counter so that overflows are counted regardless of the delivery mode.
The downside is that all future users of check_irq() will have to
maintain this ordering.

Signed-off-by: Sandipan Das <[email protected]>
---
 x86/pmu.c | 39 +++++++++++++++++++++++++++++++--------
 1 file changed, 31 insertions(+), 8 deletions(-)

diff --git a/x86/pmu.c b/x86/pmu.c
index ea31a7a3..236c39b9 100644
--- a/x86/pmu.c
+++ b/x86/pmu.c
@@ -250,23 +250,27 @@ static void adjust_events_range(struct pmu_event *gp_events,
 		gp_events[branch_miss_idx].min = 0;
 }
 
+static bool nmi = false;
+static u32 lvtpc = PMI_VECTOR;
 volatile uint64_t irq_received;
 
 static void cnt_overflow(isr_regs_t *regs)
 {
 	irq_received++;
 	apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED);
-	apic_write(APIC_EOI, 0);
+	if (!nmi)
+		apic_write(APIC_EOI, 0);
 }
 
 static bool check_irq(void)
 {
 	int i;
-	irq_received = 0;
-	sti();
+	if (!nmi)
+		sti();
 	for (i = 0; i < 100000 && !irq_received; i++)
 		asm volatile("pause");
-	cli();
+	if (!nmi)
+		cli();
 	return irq_received;
 }
 
@@ -347,7 +351,7 @@ static void __start_event(pmu_counter_t *evt, uint64_t count)
 	    ctrl = (ctrl & ~(0xf << shift)) | (usrospmi << shift);
 	    wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl);
     }
-    apic_write(APIC_LVTPC, PMI_VECTOR);
+    apic_write(APIC_LVTPC, lvtpc);
 }
 
 static void start_event(pmu_counter_t *evt)
@@ -582,6 +586,7 @@ static void check_counter_overflow(void)
 		else
 			cnt.config &= ~EVNTSEL_INT;
 		idx = event_to_global_idx(&cnt);
+		irq_received = 0;
 		__measure(&cnt, cnt.count);
 		if (pmu.is_intel) {
 			if (pmu.errata.instructions_retired_overcount)
@@ -984,14 +989,32 @@ static void check_invalid_rdpmc_gp(void)
 	       "Expected #GP on RDPMC(64)");
 }
 
-int main(int ac, char **av)
+int main(int argc, char **argv)
 {
 	int instruction_idx;
 	int branch_idx;
 	int branch_miss_idx;
+	int i;
+
+	argv++;
+	argc--;
 
 	setup_vm();
-	handle_irq(PMI_VECTOR, cnt_overflow);
+
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argv[i], "nmi")) {
+			nmi = true;
+			break;
+		}
+	}
+
+	if (nmi) {
+		lvtpc = NMI_VECTOR | APIC_DM_NMI;
+		handle_irq(NMI_VECTOR, cnt_overflow);
+	} else {
+		handle_irq(PMI_VECTOR, cnt_overflow);
+	}
+
 	buf = malloc(N*64);
 
 	if (this_cpu_has_perf_global_ctrl())
@@ -1044,7 +1067,7 @@ int main(int ac, char **av)
 			    "Please update test case.", pmu.nr_fixed_counters,
 			    (unsigned)ARRAY_SIZE(fixed_events));
 
-	apic_write(APIC_LVTPC, PMI_VECTOR);
+	apic_write(APIC_LVTPC, lvtpc);
 
 	check_counters();
 
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.