If multiple NMIs occur simultaneously, the first is handled while the others are collapsed and queued. But the current implementation may collapse all NMIs into the first if timing is bad. Signed-off-by: Avi Kivity <avi@xxxxxxxxxx> --- x86/apic.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 75 insertions(+), 0 deletions(-) diff --git a/x86/apic.c b/x86/apic.c index 1366185..c51e6a5 100644 --- a/x86/apic.c +++ b/x86/apic.c @@ -198,6 +198,80 @@ static void test_sti_nmi(void) report("nmi-after-sti", nmi_hlt_counter == 0); } +static volatile bool nmi_done, nmi_flushed; +static volatile int nmi_received; +static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1; +static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2; + +static void multiple_nmi_handler(isr_regs_t *regs) +{ + ++nmi_received; +} + +static void kick_me_nmi(void *blah) +{ + while (!nmi_done) { + ++cpu1_nmi_ctr1; + while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) { + pause(); + } + if (nmi_done) { + return; + } + apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); + /* make sure the NMI has arrived by sending an IPI after it */ + apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT + | 0x44, 0); + ++cpu1_nmi_ctr2; + while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) { + pause(); + } + } +} + +static void flush_nmi(isr_regs_t *regs) +{ + nmi_flushed = true; + apic_write(APIC_EOI, 0); +} + +static void test_multiple_nmi(void) +{ + int i; + bool ok = true; + + if (cpu_count() < 2) { + return; + } + + sti(); + handle_irq(2, multiple_nmi_handler); + handle_irq(0x44, flush_nmi); + on_cpu_async(1, kick_me_nmi, 0); + for (i = 0; i < 1000000; ++i) { + nmi_flushed = false; + nmi_received = 0; + ++cpu0_nmi_ctr1; + while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) { + pause(); + } + apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); + while (!nmi_flushed) { + pause(); + } + if (nmi_received != 2) { + ok = false; + break; + } + ++cpu0_nmi_ctr2; + while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) { + pause(); + } + } + nmi_done = true; + report("multiple nmi", ok); +} + int main() { setup_vm(); @@ -215,6 +289,7 @@ int main() test_ioapic_intr(); test_ioapic_simultaneous(); test_sti_nmi(); + test_multiple_nmi(); printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); -- 1.7.6.3 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html