aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRavikiran G Thirumalai <kiran@scalex86.org>2006-12-09 15:33:35 -0500
committerAndi Kleen <andi@basil.nowhere.org>2006-12-09 15:33:35 -0500
commit92715e282be7c7488f892703c8d39b08976a833b (patch)
tree49acd962775f5a113483b16bbcc9c504728215ca
parent16d279d277aedd640d9dba5ddeb172b5e6bc7d75 (diff)
[PATCH] x86: Fix boot hang due to nmi watchdog init code
2.6.19 stopped booting (or booted based on build/config) on our x86_64 systems due to a bug introduced in 2.6.19. check_nmi_watchdog schedules an IPI on all cpus to busy wait on a flag, but fails to set the busywait flag if NMI functionality is disabled. This causes the secondary cpus to spin in an endless loop, causing the kernel bootup to hang. Depending upon the build, the busywait flag got overwritten (stack variable) and caused the kernel to bootup on certain builds. Following patch fixes the bug by setting the busywait flag before returning from check_nmi_watchdog. I guess using a stack variable is not good here as the calling function could potentially return while the busy wait loop is still spinning on the flag. AK: I redid the patch significantly to be cleaner Signed-off-by: Ravikiran Thirumalai <kiran@scalex86.org> Signed-off-by: Shai Fultheim <shai@scalex86.org> Signed-off-by: Andi Kleen <ak@suse.de>
-rw-r--r--arch/i386/kernel/nmi.c8
-rw-r--r--arch/x86_64/kernel/nmi.c9
2 files changed, 9 insertions, 8 deletions
diff --git a/arch/i386/kernel/nmi.c b/arch/i386/kernel/nmi.c
index f5bc7e1be801..a5e34d655965 100644
--- a/arch/i386/kernel/nmi.c
+++ b/arch/i386/kernel/nmi.c
@@ -195,6 +195,8 @@ static __cpuinit inline int nmi_known_cpu(void)
195 return 0; 195 return 0;
196} 196}
197 197
198static int endflag __initdata = 0;
199
198#ifdef CONFIG_SMP 200#ifdef CONFIG_SMP
199/* The performance counters used by NMI_LOCAL_APIC don't trigger when 201/* The performance counters used by NMI_LOCAL_APIC don't trigger when
200 * the CPU is idle. To make sure the NMI watchdog really ticks on all 202 * the CPU is idle. To make sure the NMI watchdog really ticks on all
@@ -202,7 +204,6 @@ static __cpuinit inline int nmi_known_cpu(void)
202 */ 204 */
203static __init void nmi_cpu_busy(void *data) 205static __init void nmi_cpu_busy(void *data)
204{ 206{
205 volatile int *endflag = data;
206 local_irq_enable_in_hardirq(); 207 local_irq_enable_in_hardirq();
207 /* Intentionally don't use cpu_relax here. This is 208 /* Intentionally don't use cpu_relax here. This is
208 to make sure that the performance counter really ticks, 209 to make sure that the performance counter really ticks,
@@ -210,14 +211,13 @@ static __init void nmi_cpu_busy(void *data)
210 pause instruction. On a real HT machine this is fine because 211 pause instruction. On a real HT machine this is fine because
211 all other CPUs are busy with "useless" delay loops and don't 212 all other CPUs are busy with "useless" delay loops and don't
212 care if they get somewhat less cycles. */ 213 care if they get somewhat less cycles. */
213 while (*endflag == 0) 214 while (endflag == 0)
214 barrier(); 215 mb();
215} 216}
216#endif 217#endif
217 218
218static int __init check_nmi_watchdog(void) 219static int __init check_nmi_watchdog(void)
219{ 220{
220 volatile int endflag = 0;
221 unsigned int *prev_nmi_count; 221 unsigned int *prev_nmi_count;
222 int cpu; 222 int cpu;
223 223
diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c
index 27e95e7922c1..186aebbae32d 100644
--- a/arch/x86_64/kernel/nmi.c
+++ b/arch/x86_64/kernel/nmi.c
@@ -193,6 +193,8 @@ void nmi_watchdog_default(void)
193 nmi_watchdog = NMI_IO_APIC; 193 nmi_watchdog = NMI_IO_APIC;
194} 194}
195 195
196static int endflag __initdata = 0;
197
196#ifdef CONFIG_SMP 198#ifdef CONFIG_SMP
197/* The performance counters used by NMI_LOCAL_APIC don't trigger when 199/* The performance counters used by NMI_LOCAL_APIC don't trigger when
198 * the CPU is idle. To make sure the NMI watchdog really ticks on all 200 * the CPU is idle. To make sure the NMI watchdog really ticks on all
@@ -200,7 +202,6 @@ void nmi_watchdog_default(void)
200 */ 202 */
201static __init void nmi_cpu_busy(void *data) 203static __init void nmi_cpu_busy(void *data)
202{ 204{
203 volatile int *endflag = data;
204 local_irq_enable_in_hardirq(); 205 local_irq_enable_in_hardirq();
205 /* Intentionally don't use cpu_relax here. This is 206 /* Intentionally don't use cpu_relax here. This is
206 to make sure that the performance counter really ticks, 207 to make sure that the performance counter really ticks,
@@ -208,14 +209,13 @@ static __init void nmi_cpu_busy(void *data)
208 pause instruction. On a real HT machine this is fine because 209 pause instruction. On a real HT machine this is fine because
209 all other CPUs are busy with "useless" delay loops and don't 210 all other CPUs are busy with "useless" delay loops and don't
210 care if they get somewhat less cycles. */ 211 care if they get somewhat less cycles. */
211 while (*endflag == 0) 212 while (endflag == 0)
212 barrier(); 213 mb();
213} 214}
214#endif 215#endif
215 216
216int __init check_nmi_watchdog (void) 217int __init check_nmi_watchdog (void)
217{ 218{
218 volatile int endflag = 0;
219 int *counts; 219 int *counts;
220 int cpu; 220 int cpu;
221 221
@@ -256,6 +256,7 @@ int __init check_nmi_watchdog (void)
256 if (!atomic_read(&nmi_active)) { 256 if (!atomic_read(&nmi_active)) {
257 kfree(counts); 257 kfree(counts);
258 atomic_set(&nmi_active, -1); 258 atomic_set(&nmi_active, -1);
259 endflag = 1;
259 return -1; 260 return -1;
260 } 261 }
261 endflag = 1; 262 endflag = 1;