[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