Test that TMR works right even if the virtual-APIC page is modified by another processor. Either x2apic or xapic accesses are tested, depending on whether x2apic is available. With current KVM, four out of eight tests fail, and there is no difference in behavior between self-injection (done from root mode even with posted interrupts) and actual posted interrupt injection where the receiving CPU is in non-root mode). With the proposed TMR patch all eight tests pass, and again there is no difference in behavior between self-injection and non-root mode posted interrupts. Cc: Yang Zhang <yang.z.zhang@xxxxxxxxx> Cc: Jun Nakajima <jun.nakajima@xxxxxxxxx> Cc: Steve Rutherford <srutherford@xxxxxxxxxx> Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx> --- lib/x86/apic.c | 7 ++++ lib/x86/apic.h | 1 + x86/ioapic.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/lib/x86/apic.c b/lib/x86/apic.c index 80b96d8..2ceba39 100644 --- a/lib/x86/apic.c +++ b/lib/x86/apic.c @@ -95,6 +95,13 @@ void apic_write(unsigned reg, u32 val) apic_ops->reg_write(reg, val); } +bool apic_read_bit(unsigned reg, int n) +{ + reg += (n >> 5) << 4; + n &= 31; + return (apic_read(reg) & (1 << n)) != 0; +} + void apic_icr_write(u32 val, u32 dest) { apic_ops->icr_write(val, dest); diff --git a/lib/x86/apic.h b/lib/x86/apic.h index 216b98d..2d0504c 100644 --- a/lib/x86/apic.h +++ b/lib/x86/apic.h @@ -31,6 +31,7 @@ void set_mask(unsigned line, int mask); void enable_apic(void); uint32_t apic_read(unsigned reg); +bool apic_read_bit(unsigned reg, int n); void apic_write(unsigned reg, uint32_t val); void apic_icr_write(uint32_t val, uint32_t dest); uint32_t apic_id(void); diff --git a/x86/ioapic.c b/x86/ioapic.c index 1fe1ccc..a554e43 100644 --- a/x86/ioapic.c +++ b/x86/ioapic.c @@ -147,6 +147,101 @@ static void test_ioapic_simultaneous(void) g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); } +static volatile int g_tmr_79 = -1; + +static void ioapic_isr_79(isr_regs_t *regs) +{ + g_tmr_79 = apic_read_bit(APIC_TMR, 0x79); + set_irq_line(0x0e, 0); + eoi(); +} + +static void test_ioapic_edge_tmr(bool expected_tmr_before) +{ + int tmr_before; + + handle_irq(0x79, ioapic_isr_79); + set_ioapic_redir(0x0e, 0x79, EDGE_TRIGGERED); + tmr_before = apic_read_bit(APIC_TMR, 0x79); + toggle_irq_line(0x0e); + asm volatile ("nop"); + report("TMR for ioapic edge interrupts (expected %s)", + tmr_before == expected_tmr_before && !g_tmr_79, + expected_tmr_before ? "true" : "false"); +} + +static void test_ioapic_level_tmr(bool expected_tmr_before) +{ + int tmr_before; + + handle_irq(0x79, ioapic_isr_79); + set_ioapic_redir(0x0e, 0x79, LEVEL_TRIGGERED); + tmr_before = apic_read_bit(APIC_TMR, 0x79); + set_irq_line(0x0e, 1); + asm volatile ("nop"); + report("TMR for ioapic level interrupts (expected %s)", + tmr_before == expected_tmr_before && g_tmr_79, + expected_tmr_before ? "true" : "false"); +} + +#define IPI_DELAY 1000000 + +static void delay(int count) +{ + while(count--) asm(""); +} + +static void toggle_irq_line_0x0e(void *data) +{ + irq_disable(); + delay(IPI_DELAY); + toggle_irq_line(0x0e); + irq_enable(); +} + +static void test_ioapic_edge_tmr_smp(bool expected_tmr_before) +{ + int tmr_before; + int i; + + g_tmr_79 = -1; + handle_irq(0x79, ioapic_isr_79); + set_ioapic_redir(0x0e, 0x79, EDGE_TRIGGERED); + tmr_before = apic_read_bit(APIC_TMR, 0x79); + on_cpu_async(1, toggle_irq_line_0x0e, 0); + i = 0; + while(g_tmr_79 == -1) i++; + printf("%d iterations before interrupt received\n", i); + report("TMR for ioapic edge interrupts (expected %s)", + tmr_before == expected_tmr_before && !g_tmr_79, + expected_tmr_before ? "true" : "false"); +} + +static void set_irq_line_0x0e(void *data) +{ + irq_disable(); + delay(IPI_DELAY); + set_irq_line(0x0e, 1); + irq_enable(); +} + +static void test_ioapic_level_tmr_smp(bool expected_tmr_before) +{ + int i, tmr_before; + + g_tmr_79 = -1; + handle_irq(0x79, ioapic_isr_79); + set_ioapic_redir(0x0e, 0x79, LEVEL_TRIGGERED); + tmr_before = apic_read_bit(APIC_TMR, 0x79); + on_cpu_async(1, set_irq_line_0x0e, 0); + i = 0; + while(g_tmr_79 == -1) i++; + printf("%d iterations before interrupt received\n", i); + report("TMR for ioapic level interrupts (expected %s)", + tmr_before == expected_tmr_before && g_tmr_79, + expected_tmr_before ? "true" : "false"); +} + static int g_isr_98; static void ioapic_isr_98(isr_regs_t *regs) @@ -306,7 +401,11 @@ int main(void) setup_idt(); mask_pic_interrupts(); - enable_apic(); + + if (enable_x2apic()) + printf("x2apic enabled\n"); + else + printf("x2apic not detected\n"); irq_enable(); @@ -317,6 +416,7 @@ int main(void) test_ioapic_edge_intr(); test_ioapic_level_intr(); test_ioapic_simultaneous(); + test_ioapic_level_coalesce(); test_ioapic_level_sequential(); test_ioapic_level_retrigger(); @@ -325,5 +425,17 @@ int main(void) test_ioapic_level_mask(); test_ioapic_level_retrigger_mask(); + test_ioapic_edge_tmr(false); + test_ioapic_level_tmr(false); + test_ioapic_level_tmr(true); + test_ioapic_edge_tmr(true); + + if (cpu_count() > 1) { + test_ioapic_edge_tmr_smp(false); + test_ioapic_level_tmr_smp(false); + test_ioapic_level_tmr_smp(true); + test_ioapic_edge_tmr_smp(true); + } + return report_summary(); } -- 1.8.3.1 -- 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