Blame view

arch/i386/kernel/nmi.c 11 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   *  linux/arch/i386/nmi.c
   *
   *  NMI watchdog support on APIC systems
   *
   *  Started by Ingo Molnar <mingo@redhat.com>
   *
   *  Fixes:
   *  Mikael Pettersson	: AMD K7 support for local APIC NMI watchdog.
   *  Mikael Pettersson	: Power Management for local APIC NMI watchdog.
   *  Mikael Pettersson	: Pentium 4 support for local APIC NMI watchdog.
   *  Pavel Machek and
   *  Mikael Pettersson	: PM converted to driver model. Disable/enable API.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include <linux/delay.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include <linux/interrupt.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
  #include <linux/module.h>
  #include <linux/nmi.h>
  #include <linux/sysdev.h>
  #include <linux/sysctl.h>
3e4ff1157   Don Zickus   [PATCH] x86_64: n...
21
  #include <linux/percpu.h>
06039754d   Fernando Luis Vázquez Cao   [PATCH] i386: Dis...
22
  #include <linux/kprobes.h>
bb81a09e5   Andrew Morton   [PATCH] x86: all ...
23
  #include <linux/cpumask.h>
f8b5035b9   Thomas Gleixner   [PATCH] i386 prep...
24
  #include <linux/kernel_stat.h>
1eeb66a1b   Christoph Hellwig   move die notifier...
25
  #include <linux/kdebug.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
  
  #include <asm/smp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
  #include <asm/nmi.h>
  
  #include "mach_traps.h"
29cbc78b9   Andi Kleen   [PATCH] x86: Clea...
31
32
  int unknown_nmi_panic;
  int nmi_watchdog_enabled;
1714f9bfc   Andi Kleen   [PATCH] x86: Fix ...
33
  static cpumask_t backtrace_mask = CPU_MASK_NONE;
09198e685   Andi Kleen   [PATCH] i386: Cle...
34

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
  /* nmi_active:
b7471c6da   Don Zickus   [PATCH] i386: Add...
36
37
   * >0: the lapic NMI watchdog is active, but can be disabled
   * <0: the lapic NMI watchdog has not been set up, and cannot
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
   *     be enabled
b7471c6da   Don Zickus   [PATCH] i386: Add...
39
   *  0: the lapic NMI watchdog is disabled, but can be enabled
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
   */
b7471c6da   Don Zickus   [PATCH] i386: Add...
41
  atomic_t nmi_active = ATOMIC_INIT(0);		/* oprofile uses this */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42

b7471c6da   Don Zickus   [PATCH] i386: Add...
43
44
  unsigned int nmi_watchdog = NMI_DEFAULT;
  static unsigned int nmi_hz = HZ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45

09198e685   Andi Kleen   [PATCH] i386: Cle...
46
  static DEFINE_PER_CPU(short, wd_enabled);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47

b7471c6da   Don Zickus   [PATCH] i386: Add...
48
  /* local prototypes */
b7471c6da   Don Zickus   [PATCH] i386: Add...
49
  static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu);
92715e282   Ravikiran G Thirumalai   [PATCH] x86: Fix ...
50
  static int endflag __initdata = 0;
29b70081f   Eric W. Biederman   [PATCH] i386 nmi_...
51
52
53
54
55
56
57
  #ifdef CONFIG_SMP
  /* The performance counters used by NMI_LOCAL_APIC don't trigger when
   * the CPU is idle. To make sure the NMI watchdog really ticks on all
   * CPUs during the test make them busy.
   */
  static __init void nmi_cpu_busy(void *data)
  {
366c7f554   Ingo Molnar   [PATCH] lockdep: ...
58
  	local_irq_enable_in_hardirq();
29b70081f   Eric W. Biederman   [PATCH] i386 nmi_...
59
60
61
62
63
64
  	/* Intentionally don't use cpu_relax here. This is
  	   to make sure that the performance counter really ticks,
  	   even if there is a simulator or similar that catches the
  	   pause instruction. On a real HT machine this is fine because
  	   all other CPUs are busy with "useless" delay loops and don't
  	   care if they get somewhat less cycles. */
92715e282   Ravikiran G Thirumalai   [PATCH] x86: Fix ...
65
66
  	while (endflag == 0)
  		mb();
29b70081f   Eric W. Biederman   [PATCH] i386 nmi_...
67
68
  }
  #endif
67701ae97   Jack F Vogel   [PATCH] check nmi...
69
  static int __init check_nmi_watchdog(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
  {
29b70081f   Eric W. Biederman   [PATCH] i386 nmi_...
71
  	unsigned int *prev_nmi_count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  	int cpu;
b7471c6da   Don Zickus   [PATCH] i386: Add...
73
74
75
76
  	if ((nmi_watchdog == NMI_NONE) || (nmi_watchdog == NMI_DEFAULT))
  		return 0;
  
  	if (!atomic_read(&nmi_active))
67701ae97   Jack F Vogel   [PATCH] check nmi...
77
  		return 0;
29b70081f   Eric W. Biederman   [PATCH] i386 nmi_...
78
79
80
  	prev_nmi_count = kmalloc(NR_CPUS * sizeof(int), GFP_KERNEL);
  	if (!prev_nmi_count)
  		return -1;
67701ae97   Jack F Vogel   [PATCH] check nmi...
81
  	printk(KERN_INFO "Testing NMI watchdog ... ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82

29b70081f   Eric W. Biederman   [PATCH] i386 nmi_...
83
84
  	if (nmi_watchdog == NMI_LOCAL_APIC)
  		smp_call_function(nmi_cpu_busy, (void *)&endflag, 0, 0);
c8912599c   KAMEZAWA Hiroyuki   [PATCH] for_each_...
85
  	for_each_possible_cpu(cpu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
  		prev_nmi_count[cpu] = per_cpu(irq_stat, cpu).__nmi_count;
  	local_irq_enable();
0fb2ebfcb   Andi Kleen   [PATCH] x86-64: I...
88
  	mdelay((20*1000)/nmi_hz); // wait 20 ticks
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89

c8912599c   KAMEZAWA Hiroyuki   [PATCH] for_each_...
90
  	for_each_possible_cpu(cpu) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
95
96
  #ifdef CONFIG_SMP
  		/* Check cpu_callin_map here because that is set
  		   after the timer is started. */
  		if (!cpu_isset(cpu, cpu_callin_map))
  			continue;
  #endif
09198e685   Andi Kleen   [PATCH] i386: Cle...
97
  		if (!per_cpu(wd_enabled, cpu))
b7471c6da   Don Zickus   [PATCH] i386: Add...
98
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
  		if (nmi_count(cpu) - prev_nmi_count[cpu] <= 5) {
29b70081f   Eric W. Biederman   [PATCH] i386 nmi_...
100
101
102
103
104
  			printk("CPU#%d: NMI appears to be stuck (%d->%d)!
  ",
  				cpu,
  				prev_nmi_count[cpu],
  				nmi_count(cpu));
09198e685   Andi Kleen   [PATCH] i386: Cle...
105
  			per_cpu(wd_enabled, cpu) = 0;
b7471c6da   Don Zickus   [PATCH] i386: Add...
106
  			atomic_dec(&nmi_active);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
  		}
  	}
b7471c6da   Don Zickus   [PATCH] i386: Add...
109
110
111
112
113
  	if (!atomic_read(&nmi_active)) {
  		kfree(prev_nmi_count);
  		atomic_set(&nmi_active, -1);
  		return -1;
  	}
29b70081f   Eric W. Biederman   [PATCH] i386 nmi_...
114
  	endflag = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
  	printk("OK.
  ");
  
  	/* now that we know it works we can reduce NMI frequency to
  	   something more reasonable; makes a difference in some configs */
09198e685   Andi Kleen   [PATCH] i386: Cle...
120
121
  	if (nmi_watchdog == NMI_LOCAL_APIC)
  		nmi_hz = lapic_adjust_nmi_hz(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122

29b70081f   Eric W. Biederman   [PATCH] i386 nmi_...
123
  	kfree(prev_nmi_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
  	return 0;
  }
67701ae97   Jack F Vogel   [PATCH] check nmi...
126
127
  /* This needs to happen later in boot so counters are working */
  late_initcall(check_nmi_watchdog);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
133
  
  static int __init setup_nmi_watchdog(char *str)
  {
  	int nmi;
  
  	get_option(&str, &nmi);
b7471c6da   Don Zickus   [PATCH] i386: Add...
134
  	if ((nmi >= NMI_INVALID) || (nmi < NMI_NONE))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
  		return 0;
58d9ce7d7   Venkatesh Pallipadi   [PATCH] Revert nm...
136

b7471c6da   Don Zickus   [PATCH] i386: Add...
137
  	nmi_watchdog = nmi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
  	return 1;
  }
  
  __setup("nmi_watchdog=", setup_nmi_watchdog);
5d0e600d9   Ingo Molnar   [PATCH] x86: fix ...
142

09198e685   Andi Kleen   [PATCH] i386: Cle...
143
  /* Suspend/resume support */
5d0e600d9   Ingo Molnar   [PATCH] x86: fix ...
144

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
  #ifdef CONFIG_PM
  
  static int nmi_pm_active; /* nmi_active before suspend */
438510f6f   Pavel Machek   [PATCH] pm_messag...
148
  static int lapic_nmi_suspend(struct sys_device *dev, pm_message_t state)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  {
4038f901c   Shaohua Li   [PATCH] i386/x86-...
150
  	/* only CPU0 goes here, other CPUs should be offline */
b7471c6da   Don Zickus   [PATCH] i386: Add...
151
  	nmi_pm_active = atomic_read(&nmi_active);
4038f901c   Shaohua Li   [PATCH] i386/x86-...
152
153
  	stop_apic_nmi_watchdog(NULL);
  	BUG_ON(atomic_read(&nmi_active) != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
155
156
157
158
  	return 0;
  }
  
  static int lapic_nmi_resume(struct sys_device *dev)
  {
4038f901c   Shaohua Li   [PATCH] i386/x86-...
159
160
161
162
163
  	/* only CPU0 goes here, other CPUs should be offline */
  	if (nmi_pm_active > 0) {
  		setup_apic_nmi_watchdog(NULL);
  		touch_nmi_watchdog();
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  	return 0;
  }
  
  
  static struct sysdev_class nmi_sysclass = {
  	set_kset_name("lapic_nmi"),
  	.resume		= lapic_nmi_resume,
  	.suspend	= lapic_nmi_suspend,
  };
  
  static struct sys_device device_lapic_nmi = {
  	.id	= 0,
  	.cls	= &nmi_sysclass,
  };
  
  static int __init init_lapic_nmi_sysfs(void)
  {
  	int error;
b7471c6da   Don Zickus   [PATCH] i386: Add...
182
183
184
185
186
  	/* should really be a BUG_ON but b/c this is an
  	 * init call, it just doesn't work.  -dcz
  	 */
  	if (nmi_watchdog != NMI_LOCAL_APIC)
  		return 0;
09198e685   Andi Kleen   [PATCH] i386: Cle...
187
  	if (atomic_read(&nmi_active) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
192
193
194
195
196
197
198
  		return 0;
  
  	error = sysdev_class_register(&nmi_sysclass);
  	if (!error)
  		error = sysdev_register(&device_lapic_nmi);
  	return error;
  }
  /* must come after the local APIC's device_initcall() */
  late_initcall(init_lapic_nmi_sysfs);
  
  #endif	/* CONFIG_PM */
09198e685   Andi Kleen   [PATCH] i386: Cle...
199
  static void __acpi_nmi_enable(void *__unused)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  {
09198e685   Andi Kleen   [PATCH] i386: Cle...
201
  	apic_write_around(APIC_LVT0, APIC_DM_NMI);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  }
09198e685   Andi Kleen   [PATCH] i386: Cle...
203
204
205
206
  /*
   * Enable timer based NMIs on all CPUs:
   */
  void acpi_nmi_enable(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  {
09198e685   Andi Kleen   [PATCH] i386: Cle...
208
209
  	if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC)
  		on_each_cpu(__acpi_nmi_enable, NULL, 0, 1);
b7471c6da   Don Zickus   [PATCH] i386: Add...
210
  }
09198e685   Andi Kleen   [PATCH] i386: Cle...
211
  static void __acpi_nmi_disable(void *__unused)
248dcb2ff   Venkatesh Pallipadi   [PATCH] x86: i386...
212
  {
09198e685   Andi Kleen   [PATCH] i386: Cle...
213
  	apic_write(APIC_LVT0, APIC_DM_NMI | APIC_LVT_MASKED);
248dcb2ff   Venkatesh Pallipadi   [PATCH] x86: i386...
214
  }
09198e685   Andi Kleen   [PATCH] i386: Cle...
215
216
217
218
  /*
   * Disable timer based NMIs on all CPUs:
   */
  void acpi_nmi_disable(void)
248dcb2ff   Venkatesh Pallipadi   [PATCH] x86: i386...
219
  {
09198e685   Andi Kleen   [PATCH] i386: Cle...
220
221
  	if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC)
  		on_each_cpu(__acpi_nmi_disable, NULL, 0, 1);
248dcb2ff   Venkatesh Pallipadi   [PATCH] x86: i386...
222
  }
b7471c6da   Don Zickus   [PATCH] i386: Add...
223
224
  void setup_apic_nmi_watchdog (void *unused)
  {
09198e685   Andi Kleen   [PATCH] i386: Cle...
225
226
  	if (__get_cpu_var(wd_enabled))
   		return;
4038f901c   Shaohua Li   [PATCH] i386/x86-...
227
228
229
230
231
  
  	/* cheap hack to support suspend/resume */
  	/* if cpu0 is not active neither should the other cpus */
  	if ((smp_processor_id() != 0) && (atomic_read(&nmi_active) <= 0))
  		return;
09198e685   Andi Kleen   [PATCH] i386: Cle...
232
233
234
235
236
  	switch (nmi_watchdog) {
  	case NMI_LOCAL_APIC:
  		__get_cpu_var(wd_enabled) = 1; /* enable it before to avoid race with handler */
  		if (lapic_watchdog_init(nmi_hz) < 0) {
  			__get_cpu_var(wd_enabled) = 0;
b7471c6da   Don Zickus   [PATCH] i386: Add...
237
238
  			return;
  		}
09198e685   Andi Kleen   [PATCH] i386: Cle...
239
240
241
242
  		/* FALL THROUGH */
  	case NMI_IO_APIC:
  		__get_cpu_var(wd_enabled) = 1;
  		atomic_inc(&nmi_active);
b7471c6da   Don Zickus   [PATCH] i386: Add...
243
  	}
b7471c6da   Don Zickus   [PATCH] i386: Add...
244
  }
4038f901c   Shaohua Li   [PATCH] i386/x86-...
245
  void stop_apic_nmi_watchdog(void *unused)
b7471c6da   Don Zickus   [PATCH] i386: Add...
246
247
248
249
250
  {
  	/* only support LOCAL and IO APICs for now */
  	if ((nmi_watchdog != NMI_LOCAL_APIC) &&
  	    (nmi_watchdog != NMI_IO_APIC))
  	    	return;
09198e685   Andi Kleen   [PATCH] i386: Cle...
251
  	if (__get_cpu_var(wd_enabled) == 0)
4038f901c   Shaohua Li   [PATCH] i386/x86-...
252
  		return;
09198e685   Andi Kleen   [PATCH] i386: Cle...
253
254
255
  	if (nmi_watchdog == NMI_LOCAL_APIC)
  		lapic_watchdog_stop();
  	__get_cpu_var(wd_enabled) = 0;
b7471c6da   Don Zickus   [PATCH] i386: Add...
256
  	atomic_dec(&nmi_active);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  }
  
  /*
   * the best way to detect whether a CPU has a 'hard lockup' problem
   * is to check it's local APIC timer IRQ counts. If they are not
   * changing then that CPU has some problem.
   *
   * as these watchdog NMI IRQs are generated on every CPU, we only
   * have to check the current processor.
   *
   * since NMIs don't listen to _any_ locks, we have to be extremely
   * careful not to rely on unsafe variables. The printk might lock
   * up though, so we have to break up any console locks first ...
   * [when there will be more tty-related locks, break them up
   *  here too!]
   */
  
  static unsigned int
  	last_irq_sums [NR_CPUS],
  	alert_counter [NR_CPUS];
f2890255b   Andrew Morton   i386: speedup tou...
277
  void touch_nmi_watchdog(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  {
c6ea396de   Jan Beulich   [PATCH] i386: Don...
279
280
  	if (nmi_watchdog > 0) {
  		unsigned cpu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281

c6ea396de   Jan Beulich   [PATCH] i386: Don...
282
283
284
285
  		/*
  		 * Just reset the alert counters, (other CPUs might be
  		 * spinning on locks we hold):
  		 */
f2890255b   Andrew Morton   i386: speedup tou...
286
287
288
289
  		for_each_present_cpu(cpu) {
  			if (alert_counter[cpu])
  				alert_counter[cpu] = 0;
  		}
c6ea396de   Jan Beulich   [PATCH] i386: Don...
290
  	}
8446f1d39   Ingo Molnar   [PATCH] detect so...
291
292
293
294
295
  
  	/*
  	 * Tickle the softlockup detector too:
  	 */
  	touch_softlockup_watchdog();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
  }
1e86240f3   Michal Schmidt   [PATCH] IDE: Touc...
297
  EXPORT_SYMBOL(touch_nmi_watchdog);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
  
  extern void die_nmi(struct pt_regs *, const char *msg);
06039754d   Fernando Luis Vázquez Cao   [PATCH] i386: Dis...
300
  __kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
303
304
305
306
307
  {
  
  	/*
  	 * Since current_thread_info()-> is always on the stack, and we
  	 * always switch the stack NMI-atomically, it's safe to use
  	 * smp_processor_id().
  	 */
b791ccef2   Jesper Juhl   [PATCH] fix signe...
308
  	unsigned int sum;
b7471c6da   Don Zickus   [PATCH] i386: Add...
309
  	int touched = 0;
b791ccef2   Jesper Juhl   [PATCH] fix signe...
310
  	int cpu = smp_processor_id();
3adbbcce9   Don Zickus   [PATCH] x86: Clea...
311
  	int rc=0;
b7471c6da   Don Zickus   [PATCH] i386: Add...
312
313
314
315
  
  	/* check for other users first */
  	if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
  			== NOTIFY_STOP) {
3adbbcce9   Don Zickus   [PATCH] x86: Clea...
316
  		rc = 1;
b7471c6da   Don Zickus   [PATCH] i386: Add...
317
318
  		touched = 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319

bb81a09e5   Andrew Morton   [PATCH] x86: all ...
320
321
322
323
324
325
326
327
328
329
  	if (cpu_isset(cpu, backtrace_mask)) {
  		static DEFINE_SPINLOCK(lock);	/* Serialise the printks */
  
  		spin_lock(&lock);
  		printk("NMI backtrace for cpu %d
  ", cpu);
  		dump_stack();
  		spin_unlock(&lock);
  		cpu_clear(cpu, backtrace_mask);
  	}
f8b5035b9   Thomas Gleixner   [PATCH] i386 prep...
330
331
332
333
334
  	/*
  	 * Take the local apic timer and PIT/HPET into account. We don't
  	 * know which one is active, when we have highres/dyntick on
  	 */
  	sum = per_cpu(irq_stat, cpu).apic_timer_irqs + kstat_irqs(0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335

f8b5035b9   Thomas Gleixner   [PATCH] i386 prep...
336
  	/* if the none of the timers isn't firing, this cpu isn't doing much */
b7471c6da   Don Zickus   [PATCH] i386: Add...
337
  	if (!touched && last_irq_sums[cpu] == sum) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
340
341
342
343
  		/*
  		 * Ayiee, looks like this CPU is stuck ...
  		 * wait a few IRQs (5 seconds) before doing the oops ...
  		 */
  		alert_counter[cpu]++;
  		if (alert_counter[cpu] == 5*nmi_hz)
748f2edb5   George Anzinger   [PATCH] x86 NMI: ...
344
345
346
  			/*
  			 * die_nmi will return ONLY if NOTIFY_STOP happens..
  			 */
91368d73e   Ingo Molnar   [PATCH] make bug ...
347
  			die_nmi(regs, "BUG: NMI Watchdog detected LOCKUP");
b884e2578   GOTO Masanori   [PATCH] x86: Fix ...
348
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
350
351
  		last_irq_sums[cpu] = sum;
  		alert_counter[cpu] = 0;
  	}
b7471c6da   Don Zickus   [PATCH] i386: Add...
352
  	/* see if the nmi watchdog went off */
09198e685   Andi Kleen   [PATCH] i386: Cle...
353
354
355
356
357
358
359
360
361
362
363
364
365
  	if (!__get_cpu_var(wd_enabled))
  		return rc;
  	switch (nmi_watchdog) {
  	case NMI_LOCAL_APIC:
  		rc |= lapic_wd_event(nmi_hz);
  		break;
  	case NMI_IO_APIC:
  		/* don't know how to accurately check for this.
  		 * just assume it was a watchdog timer interrupt
  		 * This matches the old behaviour.
  		 */
  		rc = 1;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  	}
3adbbcce9   Don Zickus   [PATCH] x86: Clea...
367
  	return rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  }
2fbe7b25c   Don Zickus   [PATCH] i386/x86-...
369
370
371
372
373
374
375
376
  int do_nmi_callback(struct pt_regs * regs, int cpu)
  {
  #ifdef CONFIG_SYSCTL
  	if (unknown_nmi_panic)
  		return unknown_nmi_panic_callback(regs, cpu);
  #endif
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
382
  #ifdef CONFIG_SYSCTL
  
  static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
  {
  	unsigned char reason = get_nmi_reason();
  	char buf[64];
2fbe7b25c   Don Zickus   [PATCH] i386/x86-...
383
384
385
  	sprintf(buf, "NMI received for unknown reason %02x
  ", reason);
  	die_nmi(regs, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
  	return 0;
  }
407984f1a   Don Zickus   [PATCH] x86: Add ...
388
  /*
e33e89ab1   Don Zickus   [PATCH] x86: Add ...
389
   * proc handler for /proc/sys/kernel/nmi
407984f1a   Don Zickus   [PATCH] x86: Add ...
390
391
392
393
394
395
396
397
398
399
400
401
402
   */
  int proc_nmi_enabled(struct ctl_table *table, int write, struct file *file,
  			void __user *buffer, size_t *length, loff_t *ppos)
  {
  	int old_state;
  
  	nmi_watchdog_enabled = (atomic_read(&nmi_active) > 0) ? 1 : 0;
  	old_state = nmi_watchdog_enabled;
  	proc_dointvec(table, write, file, buffer, length, ppos);
  	if (!!old_state == !!nmi_watchdog_enabled)
  		return 0;
  
  	if (atomic_read(&nmi_active) < 0) {
e33e89ab1   Don Zickus   [PATCH] x86: Add ...
403
404
405
  		printk( KERN_WARNING "NMI watchdog is permanently disabled
  ");
  		return -EIO;
407984f1a   Don Zickus   [PATCH] x86: Add ...
406
407
408
  	}
  
  	if (nmi_watchdog == NMI_DEFAULT) {
09198e685   Andi Kleen   [PATCH] i386: Cle...
409
  		if (lapic_watchdog_ok())
407984f1a   Don Zickus   [PATCH] x86: Add ...
410
411
412
413
  			nmi_watchdog = NMI_LOCAL_APIC;
  		else
  			nmi_watchdog = NMI_IO_APIC;
  	}
e33e89ab1   Don Zickus   [PATCH] x86: Add ...
414
  	if (nmi_watchdog == NMI_LOCAL_APIC) {
407984f1a   Don Zickus   [PATCH] x86: Add ...
415
416
417
418
  		if (nmi_watchdog_enabled)
  			enable_lapic_nmi_watchdog();
  		else
  			disable_lapic_nmi_watchdog();
407984f1a   Don Zickus   [PATCH] x86: Add ...
419
420
421
422
423
424
425
426
  	} else {
  		printk( KERN_WARNING
  			"NMI watchdog doesn't know what hardware to touch
  ");
  		return -EIO;
  	}
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
  #endif
bb81a09e5   Andrew Morton   [PATCH] x86: all ...
428
429
430
431
432
433
434
435
436
437
438
439
  void __trigger_all_cpu_backtrace(void)
  {
  	int i;
  
  	backtrace_mask = cpu_online_map;
  	/* Wait for up to 10 seconds for all CPUs to do the backtrace */
  	for (i = 0; i < 10 * 1000; i++) {
  		if (cpus_empty(backtrace_mask))
  			break;
  		mdelay(1);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
  EXPORT_SYMBOL(nmi_active);
  EXPORT_SYMBOL(nmi_watchdog);