From: Brian Maly <bmaly@redhat.com> Date: Mon, 18 Aug 2008 12:17:37 -0400 Subject: [x86] io_apic: check timer with irq off Message-id: 48A9A0A1.2070308@redhat.com O-Subject: [RHEL5 patch] io_apic: check timer with irq off Bugzilla: 432407 RH-Acked-by: Aristeu Rozanski <arozansk@redhat.com> RH-Acked-by: Prarit Bhargava <prarit@redhat.com> RH-Acked-by: Neil Horman <nhorman@redhat.com> RH-Acked-by: Pete Zaitcev <zaitcev@redhat.com> Resolves BZ 432407 In the RHEL5 kernel code that handles the local APIC setup, init_8259A() is called from check_timer() with interrupts enabled. This leaves a narrow window where a race could occur during this initialization. Interrupts should be disabled in check_timer() as is the case upstream. Without this fix we have an elusive bug thats difficult to reproduce, but with the following error message: | ..TIMER: vector=0x31 apic1=0 pin1=2 apic2=-1 pin2=-1 | ..MP-BIOS bug: 8254 timer not connected to IO-APIC | ...trying to set up timer (IRQ0) through the 8259A ... failed. | ...trying to set up timer as Virtual Wire IRQ... failed. | ...trying to set up timer as ExtINT IRQ... failed :(. | Kernel panic - not syncing: IO-APIC + timer doesn't work! Boot with apic=debug | and send a report. Then try booting with the 'noapic' option This patch resolves the issue by disabling interrupts in the critical code and restoring them once the code has been executed. The patch is a backport from upstream and has been tested successfully on the hardware that reproduces this issue (i.e. a X4100-M2) Brian diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 4fb32c5..9a21455 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -1881,10 +1881,13 @@ static void __init setup_ioapic_ids_from_mpc(void) { } static int __init timer_irq_works(void) { unsigned long t1 = jiffies; + unsigned long flags; + local_save_flags(flags); local_irq_enable(); /* Let ten ticks pass... */ mdelay((10 * 1000) / HZ); + local_irq_restore(flags); /* * Expect a few ticks at least, to be sure some possible @@ -2276,6 +2279,9 @@ static inline void check_timer(void) { int apic1, pin1, apic2, pin2; int vector; + unsigned long flags; + + local_irq_save(flags); /* * get/set the timer IRQ vector: @@ -2321,7 +2327,7 @@ static inline void check_timer(void) } if (disable_timer_pin_1 > 0) clear_IO_APIC_pin(0, pin1); - return; + goto out; } clear_IO_APIC_pin(apic1, pin1); printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to " @@ -2344,7 +2350,7 @@ static inline void check_timer(void) if (nmi_watchdog == NMI_IO_APIC) { setup_nmi(); } - return; + goto out; } /* * Cleanup, just in case ... @@ -2367,7 +2373,7 @@ static inline void check_timer(void) if (timer_irq_works()) { printk(" works.\n"); - return; + goto out; } apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector); printk(" failed.\n"); @@ -2383,11 +2389,13 @@ static inline void check_timer(void) if (timer_irq_works()) { printk(" works.\n"); - return; + goto out; } printk(" failed :(.\n"); panic("IO-APIC + timer doesn't work! Boot with apic=debug and send a " "report. Then try booting with the 'noapic' option"); +out: + local_irq_restore(flags); } /* diff --git a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c index 610d316..76c1f29 100644 --- a/arch/x86_64/kernel/io_apic.c +++ b/arch/x86_64/kernel/io_apic.c @@ -1458,10 +1458,13 @@ static void __init setup_ioapic_ids_from_mpc (void) static int __init timer_irq_works(void) { unsigned long t1 = jiffies; + unsigned long flags; + local_save_flags(flags); local_irq_enable(); /* Let ten ticks pass... */ mdelay((10 * 1000) / HZ); + local_irq_restore(flags); /* * Expect a few ticks at least, to be sure some possible @@ -1823,8 +1826,10 @@ int timer_uses_ioapic_pin_0; static inline void check_timer(void) { int apic1, pin1, apic2, pin2; + unsigned long flags; int vector; + local_irq_save(flags); /* * get/set the timer IRQ vector: */ @@ -1869,7 +1874,7 @@ static inline void check_timer(void) } if (disable_timer_pin_1 > 0) clear_IO_APIC_pin(0, pin1); - return; + goto out; } clear_IO_APIC_pin(apic1, pin1); apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not " @@ -1891,7 +1896,7 @@ static inline void check_timer(void) if (nmi_watchdog == NMI_IO_APIC) { setup_nmi(); } - return; + goto out; } /* * Cleanup, just in case ... @@ -1914,7 +1919,7 @@ static inline void check_timer(void) if (timer_irq_works()) { apic_printk(APIC_VERBOSE," works.\n"); - return; + goto out; } apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector); apic_printk(APIC_VERBOSE," failed.\n"); @@ -1929,10 +1934,12 @@ static inline void check_timer(void) if (timer_irq_works()) { apic_printk(APIC_VERBOSE," works.\n"); - return; + goto out; } apic_printk(APIC_VERBOSE," failed :(.\n"); panic("IO-APIC + timer doesn't work! Try using the 'noapic' kernel parameter\n"); +out: + local_irq_restore(flags); } static int __init notimercheck(char *s)