Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=4aae07025265151e3f7041dfbf0f529e122de1d8
Commit:     4aae07025265151e3f7041dfbf0f529e122de1d8
Parent:     971e5b35fb02c5088d49e6c024aab73582a35b71
Author:     Ingo Molnar <[EMAIL PROTECTED]>
AuthorDate: Tue Dec 18 18:05:58 2007 +0100
Committer:  Ingo Molnar <[EMAIL PROTECTED]>
CommitDate: Tue Dec 18 18:05:58 2007 +0100

    x86: fix "Kernel panic - not syncing: IO-APIC + timer doesn't work!"
    
    this is the tale of a full day spent debugging an ancient but elusive bug.
    
    after booting up thousands of random .config kernels, i finally happened
    to generate a .config that produced the following rare bootup failure
    on 32-bit x86:
    
    | ..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 bug has been reported many times during the years, but it was never
    reproduced nor fixed.
    
    the bug that i hit was extremely sensitive to .config details.
    
    First i did a .config-bisection - suspecting some .config detail.
    That led to CONFIG_X86_MCE: enabling X86_MCE magically made the bug 
disappear
    and the system would boot up just fine.
    
    Debugging my way through the MCE code ended up identifying two unlikely
    candidates: the thing that made a real difference to the hang was that
    X86_MCE did two printks:
    
     Intel machine check architecture supported.
     Intel machine check reporting enabled on CPU#1.
    
    Adding the same printks to a !CONFIG_X86_MCE kernel made the bug go away!
    
    this left timing as the main suspect: i experimented with adding various
    udelay()s to the arch/x86/kernel/io_apic_32.c:check_timer() function, and
    the race window turned out to be narrower than 30 microseconds (!).
    
    That made debugging especially funny, debugging without having printk
    ability before the bug hits is ... interesting ;-)
    
    eventually i started suspecting IRQ activities - those are pretty much the
    only thing that happen this early during bootup and have the timescale of
    a few dozen microseconds. Also, check_timer() changes the IRQ hardware
    in various creative ways, so the main candidate became IRQ0 interaction.
    
    i've added a counter to track timer irqs (on which core they arrived, at
    what exact time, etc.) and found that no timer IRQ would arrive after the
    bug condition hits - even if we re-enable IRQ0 and re-initialize the i8259A,
    but that we'd get a small number of timer irqs right around the time when we
    call the check_timer() function.
    
    Eventually i got the following backtrace triggered from debug code in the
    timer interrupt:
    
    ...trying to set up timer as Virtual Wire IRQ... failed.
    ...trying to set up timer as ExtINT IRQ...
    Pid: 1, comm: swapper Not tainted (2.6.24-rc5 #57)
    EIP: 0060:[<c044d57e>] EFLAGS: 00000246 CPU: 0
    EIP is at _spin_unlock_irqrestore+0x5/0x1c
    EAX: c0634178 EBX: 00000000 ECX: c4947d63 EDX: 00000246
    ESI: 00000002 EDI: 00010031 EBP: c04e0f2e ESP: f7c41df4
     DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
     CR0: 8005003b CR2: ffe04000 CR3: 00630000 CR4: 000006d0
     DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000
     DR6: ffff0ff0 DR7: 00000400
      [<c05f5784>] setup_IO_APIC+0x9c3/0xc5c
    
    the spin_unlock() was called from init_8259A(). Wait ... we have an IRQ0
    entry while we are in the middle of setting up the local APIC, the i8259A
    and the PIT??
    
    That is certainly not how it's supposed to work! check_timer() was supposed
    to be called with irqs turned off - but this eroded away sometime in the
    past. This code would still work most of the time because this code runs
    very quickly, but just the right timing conditions are present and IRQ0
    hits in this small, ~30 usecs window, timer irqs stop and the system does
    not boot up. Also, given how early this is during bootup, the hang is
    very deterministic - but it would only occur on certain machines (and
    certain configs).
    
    The fix was quite simple: disable/restore interrupts properly in this
    function. With that in place the test-system now boots up just fine.
    
    (64-bit x86 io_apic_64.c had the same bug.)
    
    Phew! One down, only 1500 other kernel bugs are left ;-)
    
    Signed-off-by: Ingo Molnar <[EMAIL PROTECTED]>
    Signed-off-by: Thomas Gleixner <[EMAIL PROTECTED]>
---
 arch/x86/kernel/io_apic_32.c |   16 ++++++++++++----
 arch/x86/kernel/io_apic_64.c |   16 ++++++++++++----
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kernel/io_apic_32.c b/arch/x86/kernel/io_apic_32.c
index 6cf2731..c3a565b 100644
--- a/arch/x86/kernel/io_apic_32.c
+++ b/arch/x86/kernel/io_apic_32.c
@@ -1882,13 +1882,16 @@ __setup("no_timer_check", notimercheck);
 static int __init timer_irq_works(void)
 {
        unsigned long t1 = jiffies;
+       unsigned long flags;
 
        if (no_timer_check)
                return 1;
 
+       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
@@ -2167,6 +2170,9 @@ static inline void __init check_timer(void)
        int apic1, pin1, apic2, pin2;
        int vector;
        unsigned int ver;
+       unsigned long flags;
+
+       local_irq_save(flags);
 
        ver = apic_read(APIC_LVR);
        ver = GET_APIC_VERSION(ver);
@@ -2219,7 +2225,7 @@ static inline void __init 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 "
@@ -2242,7 +2248,7 @@ static inline void __init check_timer(void)
                        if (nmi_watchdog == NMI_IO_APIC) {
                                setup_nmi();
                        }
-                       return;
+                       goto out;
                }
                /*
                 * Cleanup, just in case ...
@@ -2266,7 +2272,7 @@ static inline void __init 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");
@@ -2282,11 +2288,13 @@ static inline void __init 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/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c
index 435a8c9..cbac167 100644
--- a/arch/x86/kernel/io_apic_64.c
+++ b/arch/x86/kernel/io_apic_64.c
@@ -1281,10 +1281,13 @@ void disable_IO_APIC(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
@@ -1655,6 +1658,9 @@ static inline void check_timer(void)
 {
        struct irq_cfg *cfg = irq_cfg + 0;
        int apic1, pin1, apic2, pin2;
+       unsigned long flags;
+
+       local_irq_save(flags);
 
        /*
         * get/set the timer IRQ vector:
@@ -1696,7 +1702,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 "
@@ -1718,7 +1724,7 @@ static inline void check_timer(void)
                        if (nmi_watchdog == NMI_IO_APIC) {
                                setup_nmi();
                        }
-                       return;
+                       goto out;
                }
                /*
                 * Cleanup, just in case ...
@@ -1741,7 +1747,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 | cfg->vector);
        apic_printk(APIC_VERBOSE," failed.\n");
@@ -1756,10 +1762,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)
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to