aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2007-12-18 12:05:58 -0500
committerIngo Molnar <mingo@elte.hu>2007-12-18 12:05:58 -0500
commit4aae07025265151e3f7041dfbf0f529e122de1d8 (patch)
tree4ab6b1981c24937bb111385ff469693e539dceaf /arch
parent971e5b35fb02c5088d49e6c024aab73582a35b71 (diff)
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 <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kernel/io_apic_32.c16
-rw-r--r--arch/x86/kernel/io_apic_64.c16
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 6cf27319a91c..c3a565bba106 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);
1882static int __init timer_irq_works(void) 1882static int __init timer_irq_works(void)
1883{ 1883{
1884 unsigned long t1 = jiffies; 1884 unsigned long t1 = jiffies;
1885 unsigned long flags;
1885 1886
1886 if (no_timer_check) 1887 if (no_timer_check)
1887 return 1; 1888 return 1;
1888 1889
1890 local_save_flags(flags);
1889 local_irq_enable(); 1891 local_irq_enable();
1890 /* Let ten ticks pass... */ 1892 /* Let ten ticks pass... */
1891 mdelay((10 * 1000) / HZ); 1893 mdelay((10 * 1000) / HZ);
1894 local_irq_restore(flags);
1892 1895
1893 /* 1896 /*
1894 * Expect a few ticks at least, to be sure some possible 1897 * Expect a few ticks at least, to be sure some possible
@@ -2167,6 +2170,9 @@ static inline void __init check_timer(void)
2167 int apic1, pin1, apic2, pin2; 2170 int apic1, pin1, apic2, pin2;
2168 int vector; 2171 int vector;
2169 unsigned int ver; 2172 unsigned int ver;
2173 unsigned long flags;
2174
2175 local_irq_save(flags);
2170 2176
2171 ver = apic_read(APIC_LVR); 2177 ver = apic_read(APIC_LVR);
2172 ver = GET_APIC_VERSION(ver); 2178 ver = GET_APIC_VERSION(ver);
@@ -2219,7 +2225,7 @@ static inline void __init check_timer(void)
2219 } 2225 }
2220 if (disable_timer_pin_1 > 0) 2226 if (disable_timer_pin_1 > 0)
2221 clear_IO_APIC_pin(0, pin1); 2227 clear_IO_APIC_pin(0, pin1);
2222 return; 2228 goto out;
2223 } 2229 }
2224 clear_IO_APIC_pin(apic1, pin1); 2230 clear_IO_APIC_pin(apic1, pin1);
2225 printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to " 2231 printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to "
@@ -2242,7 +2248,7 @@ static inline void __init check_timer(void)
2242 if (nmi_watchdog == NMI_IO_APIC) { 2248 if (nmi_watchdog == NMI_IO_APIC) {
2243 setup_nmi(); 2249 setup_nmi();
2244 } 2250 }
2245 return; 2251 goto out;
2246 } 2252 }
2247 /* 2253 /*
2248 * Cleanup, just in case ... 2254 * Cleanup, just in case ...
@@ -2266,7 +2272,7 @@ static inline void __init check_timer(void)
2266 2272
2267 if (timer_irq_works()) { 2273 if (timer_irq_works()) {
2268 printk(" works.\n"); 2274 printk(" works.\n");
2269 return; 2275 goto out;
2270 } 2276 }
2271 apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector); 2277 apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector);
2272 printk(" failed.\n"); 2278 printk(" failed.\n");
@@ -2282,11 +2288,13 @@ static inline void __init check_timer(void)
2282 2288
2283 if (timer_irq_works()) { 2289 if (timer_irq_works()) {
2284 printk(" works.\n"); 2290 printk(" works.\n");
2285 return; 2291 goto out;
2286 } 2292 }
2287 printk(" failed :(.\n"); 2293 printk(" failed :(.\n");
2288 panic("IO-APIC + timer doesn't work! Boot with apic=debug and send a " 2294 panic("IO-APIC + timer doesn't work! Boot with apic=debug and send a "
2289 "report. Then try booting with the 'noapic' option"); 2295 "report. Then try booting with the 'noapic' option");
2296out:
2297 local_irq_restore(flags);
2290} 2298}
2291 2299
2292/* 2300/*
diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c
index 435a8c9b55f8..cbac1670c7c3 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)
1281static int __init timer_irq_works(void) 1281static int __init timer_irq_works(void)
1282{ 1282{
1283 unsigned long t1 = jiffies; 1283 unsigned long t1 = jiffies;
1284 unsigned long flags;
1284 1285
1286 local_save_flags(flags);
1285 local_irq_enable(); 1287 local_irq_enable();
1286 /* Let ten ticks pass... */ 1288 /* Let ten ticks pass... */
1287 mdelay((10 * 1000) / HZ); 1289 mdelay((10 * 1000) / HZ);
1290 local_irq_restore(flags);
1288 1291
1289 /* 1292 /*
1290 * Expect a few ticks at least, to be sure some possible 1293 * Expect a few ticks at least, to be sure some possible
@@ -1655,6 +1658,9 @@ static inline void check_timer(void)
1655{ 1658{
1656 struct irq_cfg *cfg = irq_cfg + 0; 1659 struct irq_cfg *cfg = irq_cfg + 0;
1657 int apic1, pin1, apic2, pin2; 1660 int apic1, pin1, apic2, pin2;
1661 unsigned long flags;
1662
1663 local_irq_save(flags);
1658 1664
1659 /* 1665 /*
1660 * get/set the timer IRQ vector: 1666 * get/set the timer IRQ vector:
@@ -1696,7 +1702,7 @@ static inline void check_timer(void)
1696 } 1702 }
1697 if (disable_timer_pin_1 > 0) 1703 if (disable_timer_pin_1 > 0)
1698 clear_IO_APIC_pin(0, pin1); 1704 clear_IO_APIC_pin(0, pin1);
1699 return; 1705 goto out;
1700 } 1706 }
1701 clear_IO_APIC_pin(apic1, pin1); 1707 clear_IO_APIC_pin(apic1, pin1);
1702 apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not " 1708 apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not "
@@ -1718,7 +1724,7 @@ static inline void check_timer(void)
1718 if (nmi_watchdog == NMI_IO_APIC) { 1724 if (nmi_watchdog == NMI_IO_APIC) {
1719 setup_nmi(); 1725 setup_nmi();
1720 } 1726 }
1721 return; 1727 goto out;
1722 } 1728 }
1723 /* 1729 /*
1724 * Cleanup, just in case ... 1730 * Cleanup, just in case ...
@@ -1741,7 +1747,7 @@ static inline void check_timer(void)
1741 1747
1742 if (timer_irq_works()) { 1748 if (timer_irq_works()) {
1743 apic_printk(APIC_VERBOSE," works.\n"); 1749 apic_printk(APIC_VERBOSE," works.\n");
1744 return; 1750 goto out;
1745 } 1751 }
1746 apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector); 1752 apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector);
1747 apic_printk(APIC_VERBOSE," failed.\n"); 1753 apic_printk(APIC_VERBOSE," failed.\n");
@@ -1756,10 +1762,12 @@ static inline void check_timer(void)
1756 1762
1757 if (timer_irq_works()) { 1763 if (timer_irq_works()) {
1758 apic_printk(APIC_VERBOSE," works.\n"); 1764 apic_printk(APIC_VERBOSE," works.\n");
1759 return; 1765 goto out;
1760 } 1766 }
1761 apic_printk(APIC_VERBOSE," failed :(.\n"); 1767 apic_printk(APIC_VERBOSE," failed :(.\n");
1762 panic("IO-APIC + timer doesn't work! Try using the 'noapic' kernel parameter\n"); 1768 panic("IO-APIC + timer doesn't work! Try using the 'noapic' kernel parameter\n");
1769out:
1770 local_irq_restore(flags);
1763} 1771}
1764 1772
1765static int __init notimercheck(char *s) 1773static int __init notimercheck(char *s)