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 <a...@redhat.com>
---
 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to