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 --- 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