diff options
Diffstat (limited to 'arch/i386/kernel/nmi.c')
-rw-r--r-- | arch/i386/kernel/nmi.c | 50 |
1 files changed, 39 insertions, 11 deletions
diff --git a/arch/i386/kernel/nmi.c b/arch/i386/kernel/nmi.c index eaafe233a5da..a5e34d655965 100644 --- a/arch/i386/kernel/nmi.c +++ b/arch/i386/kernel/nmi.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/percpu.h> | 22 | #include <linux/percpu.h> |
23 | #include <linux/dmi.h> | 23 | #include <linux/dmi.h> |
24 | #include <linux/kprobes.h> | 24 | #include <linux/kprobes.h> |
25 | #include <linux/cpumask.h> | ||
25 | 26 | ||
26 | #include <asm/smp.h> | 27 | #include <asm/smp.h> |
27 | #include <asm/nmi.h> | 28 | #include <asm/nmi.h> |
@@ -42,6 +43,8 @@ int nmi_watchdog_enabled; | |||
42 | static DEFINE_PER_CPU(unsigned long, perfctr_nmi_owner); | 43 | static DEFINE_PER_CPU(unsigned long, perfctr_nmi_owner); |
43 | static DEFINE_PER_CPU(unsigned long, evntsel_nmi_owner[3]); | 44 | static DEFINE_PER_CPU(unsigned long, evntsel_nmi_owner[3]); |
44 | 45 | ||
46 | static cpumask_t backtrace_mask = CPU_MASK_NONE; | ||
47 | |||
45 | /* this number is calculated from Intel's MSR_P4_CRU_ESCR5 register and it's | 48 | /* this number is calculated from Intel's MSR_P4_CRU_ESCR5 register and it's |
46 | * offset from MSR_P4_BSU_ESCR0. It will be the max for all platforms (for now) | 49 | * offset from MSR_P4_BSU_ESCR0. It will be the max for all platforms (for now) |
47 | */ | 50 | */ |
@@ -192,6 +195,8 @@ static __cpuinit inline int nmi_known_cpu(void) | |||
192 | return 0; | 195 | return 0; |
193 | } | 196 | } |
194 | 197 | ||
198 | static int endflag __initdata = 0; | ||
199 | |||
195 | #ifdef CONFIG_SMP | 200 | #ifdef CONFIG_SMP |
196 | /* 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 |
197 | * 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 |
@@ -199,7 +204,6 @@ static __cpuinit inline int nmi_known_cpu(void) | |||
199 | */ | 204 | */ |
200 | static __init void nmi_cpu_busy(void *data) | 205 | static __init void nmi_cpu_busy(void *data) |
201 | { | 206 | { |
202 | volatile int *endflag = data; | ||
203 | local_irq_enable_in_hardirq(); | 207 | local_irq_enable_in_hardirq(); |
204 | /* Intentionally don't use cpu_relax here. This is | 208 | /* Intentionally don't use cpu_relax here. This is |
205 | to make sure that the performance counter really ticks, | 209 | to make sure that the performance counter really ticks, |
@@ -207,14 +211,13 @@ static __init void nmi_cpu_busy(void *data) | |||
207 | pause instruction. On a real HT machine this is fine because | 211 | pause instruction. On a real HT machine this is fine because |
208 | 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 |
209 | care if they get somewhat less cycles. */ | 213 | care if they get somewhat less cycles. */ |
210 | while (*endflag == 0) | 214 | while (endflag == 0) |
211 | barrier(); | 215 | mb(); |
212 | } | 216 | } |
213 | #endif | 217 | #endif |
214 | 218 | ||
215 | static int __init check_nmi_watchdog(void) | 219 | static int __init check_nmi_watchdog(void) |
216 | { | 220 | { |
217 | volatile int endflag = 0; | ||
218 | unsigned int *prev_nmi_count; | 221 | unsigned int *prev_nmi_count; |
219 | int cpu; | 222 | int cpu; |
220 | 223 | ||
@@ -867,14 +870,16 @@ static unsigned int | |||
867 | 870 | ||
868 | void touch_nmi_watchdog (void) | 871 | void touch_nmi_watchdog (void) |
869 | { | 872 | { |
870 | int i; | 873 | if (nmi_watchdog > 0) { |
874 | unsigned cpu; | ||
871 | 875 | ||
872 | /* | 876 | /* |
873 | * Just reset the alert counters, (other CPUs might be | 877 | * Just reset the alert counters, (other CPUs might be |
874 | * spinning on locks we hold): | 878 | * spinning on locks we hold): |
875 | */ | 879 | */ |
876 | for_each_possible_cpu(i) | 880 | for_each_present_cpu (cpu) |
877 | alert_counter[i] = 0; | 881 | alert_counter[cpu] = 0; |
882 | } | ||
878 | 883 | ||
879 | /* | 884 | /* |
880 | * Tickle the softlockup detector too: | 885 | * Tickle the softlockup detector too: |
@@ -907,6 +912,16 @@ __kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) | |||
907 | touched = 1; | 912 | touched = 1; |
908 | } | 913 | } |
909 | 914 | ||
915 | if (cpu_isset(cpu, backtrace_mask)) { | ||
916 | static DEFINE_SPINLOCK(lock); /* Serialise the printks */ | ||
917 | |||
918 | spin_lock(&lock); | ||
919 | printk("NMI backtrace for cpu %d\n", cpu); | ||
920 | dump_stack(); | ||
921 | spin_unlock(&lock); | ||
922 | cpu_clear(cpu, backtrace_mask); | ||
923 | } | ||
924 | |||
910 | sum = per_cpu(irq_stat, cpu).apic_timer_irqs; | 925 | sum = per_cpu(irq_stat, cpu).apic_timer_irqs; |
911 | 926 | ||
912 | /* if the apic timer isn't firing, this cpu isn't doing much */ | 927 | /* if the apic timer isn't firing, this cpu isn't doing much */ |
@@ -1033,6 +1048,19 @@ int proc_nmi_enabled(struct ctl_table *table, int write, struct file *file, | |||
1033 | 1048 | ||
1034 | #endif | 1049 | #endif |
1035 | 1050 | ||
1051 | void __trigger_all_cpu_backtrace(void) | ||
1052 | { | ||
1053 | int i; | ||
1054 | |||
1055 | backtrace_mask = cpu_online_map; | ||
1056 | /* Wait for up to 10 seconds for all CPUs to do the backtrace */ | ||
1057 | for (i = 0; i < 10 * 1000; i++) { | ||
1058 | if (cpus_empty(backtrace_mask)) | ||
1059 | break; | ||
1060 | mdelay(1); | ||
1061 | } | ||
1062 | } | ||
1063 | |||
1036 | EXPORT_SYMBOL(nmi_active); | 1064 | EXPORT_SYMBOL(nmi_active); |
1037 | EXPORT_SYMBOL(nmi_watchdog); | 1065 | EXPORT_SYMBOL(nmi_watchdog); |
1038 | EXPORT_SYMBOL(avail_to_resrv_perfctr_nmi); | 1066 | EXPORT_SYMBOL(avail_to_resrv_perfctr_nmi); |