[PATCH] x86_64 merge: arch + asm
[opensuse:kernel.git] / arch / x86_64 / kernel / nmi.c
1 /*
2  *  linux/arch/x86_64/nmi.c
3  *
4  *  NMI watchdog support on APIC systems
5  *
6  *  Started by Ingo Molnar <mingo@redhat.com>
7  *
8  *  Fixes:
9  *  Mikael Pettersson   : AMD K7 support for local APIC NMI watchdog.
10  *  Mikael Pettersson   : Power Management for local APIC NMI watchdog.
11  */
12
13 #include <linux/config.h>
14 #include <linux/mm.h>
15 #include <linux/irq.h>
16 #include <linux/delay.h>
17 #include <linux/bootmem.h>
18 #include <linux/smp_lock.h>
19 #include <linux/interrupt.h>
20 #include <linux/mc146818rtc.h>
21 #include <linux/kernel_stat.h>
22
23 #include <asm/smp.h>
24 #include <asm/mtrr.h>
25 #include <asm/mpspec.h>
26
27 unsigned int nmi_watchdog = NMI_NONE;
28 static unsigned int nmi_hz = HZ;
29 unsigned int nmi_perfctr_msr;   /* the MSR to reset in NMI handler */
30 extern void show_registers(struct pt_regs *regs);
31
32 #define K7_EVNTSEL_ENABLE       (1 << 22)
33 #define K7_EVNTSEL_INT          (1 << 20)
34 #define K7_EVNTSEL_OS           (1 << 17)
35 #define K7_EVNTSEL_USR          (1 << 16)
36 #define K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING    0x76
37 #define K7_NMI_EVENT            K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING
38
39 #define P6_EVNTSEL0_ENABLE      (1 << 22)
40 #define P6_EVNTSEL_INT          (1 << 20)
41 #define P6_EVNTSEL_OS           (1 << 17)
42 #define P6_EVNTSEL_USR          (1 << 16)
43 #define P6_EVENT_CPU_CLOCKS_NOT_HALTED  0x79
44 #define P6_NMI_EVENT            P6_EVENT_CPU_CLOCKS_NOT_HALTED
45
46 int __init check_nmi_watchdog (void)
47 {
48         int counts[NR_CPUS];
49         int j, cpu;
50
51         printk(KERN_INFO "testing NMI watchdog ... ");
52
53         for (j = 0; j < NR_CPUS; ++j) 
54                 counts[j] = cpu_pda[cpu_logical_map(j)].__nmi_count; 
55         sti();
56         mdelay((10*1000)/nmi_hz); // wait 10 ticks
57
58         for (j = 0; j < smp_num_cpus; j++) {
59                 cpu = cpu_logical_map(j);
60                 if (nmi_count(cpu) - counts[j] <= 5) {
61                         printk("CPU#%d: NMI appears to be stuck!\n", cpu);
62                         return -1;
63                 }
64         }
65         printk("OK.\n");
66
67         /* now that we know it works we can reduce NMI frequency to
68            something more reasonable; makes a difference in some configs */
69         if (nmi_watchdog == NMI_LOCAL_APIC)
70                 nmi_hz = 1;
71
72         return 0;
73 }
74
75 static int __init setup_nmi_watchdog(char *str)
76 {
77         int nmi;
78
79         get_option(&str, &nmi);
80
81         if (nmi >= NMI_INVALID)
82                 return 0;
83         if (nmi == NMI_NONE)
84                 nmi_watchdog = nmi;
85         /*
86          * If any other x86 CPU has a local APIC, then
87          * please test the NMI stuff there and send me the
88          * missing bits. Right now Intel P6 and AMD K7 only.
89          */
90         if ((nmi == NMI_LOCAL_APIC) &&
91                         (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) &&
92                         (boot_cpu_data.x86 == 6))
93                 nmi_watchdog = nmi;
94         if ((nmi == NMI_LOCAL_APIC) &&
95                         (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) &&
96                         (boot_cpu_data.x86 == 6))
97                 nmi_watchdog = nmi;
98         /*
99          * We can enable the IO-APIC watchdog
100          * unconditionally.
101          */
102         if (nmi == NMI_IO_APIC)
103                 nmi_watchdog = nmi;
104         return 1;
105 }
106
107 __setup("nmi_watchdog=", setup_nmi_watchdog);
108
109 #ifdef CONFIG_PM
110
111 #include <linux/pm.h>
112
113 struct pm_dev *nmi_pmdev;
114
115 static void disable_apic_nmi_watchdog(void)
116 {
117         switch (boot_cpu_data.x86_vendor) {
118         case X86_VENDOR_AMD:
119                 wrmsr(MSR_K7_EVNTSEL0, 0, 0);
120                 break;
121         case X86_VENDOR_INTEL:
122                 wrmsr(MSR_IA32_EVNTSEL0, 0, 0);
123                 break;
124         }
125 }
126
127 static int nmi_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
128 {
129         switch (rqst) {
130         case PM_SUSPEND:
131                 disable_apic_nmi_watchdog();
132                 break;
133         case PM_RESUME:
134                 setup_apic_nmi_watchdog();
135                 break;
136         }
137         return 0;
138 }
139
140 static void nmi_pm_init(void)
141 {
142         if (!nmi_pmdev)
143                 nmi_pmdev = apic_pm_register(PM_SYS_DEV, 0, nmi_pm_callback);
144 }
145
146 #define __pminit        /*empty*/
147
148 #else   /* CONFIG_PM */
149
150 static inline void nmi_pm_init(void) { }
151
152 #define __pminit        __init
153
154 #endif  /* CONFIG_PM */
155
156 /*
157  * Activate the NMI watchdog via the local APIC.
158  * Original code written by Keith Owens.
159  */
160
161 static void __pminit setup_k7_watchdog(void)
162 {
163         int i;
164         unsigned int evntsel;
165
166         nmi_perfctr_msr = MSR_K7_PERFCTR0;
167
168         for(i = 0; i < 4; ++i) {
169                 wrmsr(MSR_K7_EVNTSEL0+i, 0, 0);
170                 wrmsr(MSR_K7_PERFCTR0+i, 0, 0);
171         }
172
173         evntsel = K7_EVNTSEL_INT
174                 | K7_EVNTSEL_OS
175                 | K7_EVNTSEL_USR
176                 | K7_NMI_EVENT;
177
178         wrmsr(MSR_K7_EVNTSEL0, evntsel, 0);
179         Dprintk("setting K7_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000));
180         wrmsr(MSR_K7_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1);
181         apic_write(APIC_LVTPC, APIC_DM_NMI);
182         evntsel |= K7_EVNTSEL_ENABLE;
183         wrmsr(MSR_K7_EVNTSEL0, evntsel, 0);
184 }
185
186 void __pminit setup_apic_nmi_watchdog (void)
187 {
188         switch (boot_cpu_data.x86_vendor) {
189         case X86_VENDOR_AMD:
190                 if (boot_cpu_data.x86 != 6)
191                         return;
192                 setup_k7_watchdog();
193                 break;
194         default:
195                 return;
196         }
197         nmi_pm_init();
198 }
199
200 static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED;
201
202 /*
203  * the best way to detect whether a CPU has a 'hard lockup' problem
204  * is to check it's local APIC timer IRQ counts. If they are not
205  * changing then that CPU has some problem.
206  *
207  * as these watchdog NMI IRQs are generated on every CPU, we only
208  * have to check the current processor.
209  *
210  * since NMIs dont listen to _any_ locks, we have to be extremely
211  * careful not to rely on unsafe variables. The printk might lock
212  * up though, so we have to break up any console locks first ...
213  * [when there will be more tty-related locks, break them up
214  *  here too!]
215  */
216
217 static unsigned int
218         last_irq_sums [NR_CPUS],
219         alert_counter [NR_CPUS];
220
221 void touch_nmi_watchdog (void)
222 {
223         int i;
224
225         /*
226          * Just reset the alert counters, (other CPUs might be
227          * spinning on locks we hold):
228          */
229         for (i = 0; i < smp_num_cpus; i++)
230                 alert_counter[i] = 0;
231 }
232
233 void nmi_watchdog_tick (struct pt_regs * regs)
234 {
235
236         /*
237          * Since current_thread_info()-> is always on the stack, and we
238          * always switch the stack NMI-atomically, it's safe to use
239          * smp_processor_id().
240          */
241         int sum, cpu = smp_processor_id();
242
243         sum = apic_timer_irqs[cpu];
244
245         if (last_irq_sums[cpu] == sum) {
246                 /*
247                  * Ayiee, looks like this CPU is stuck ...
248                  * wait a few IRQs (5 seconds) before doing the oops ...
249                  */
250                 alert_counter[cpu]++;
251                 if (alert_counter[cpu] == 5*nmi_hz) {
252                         spin_lock(&nmi_print_lock);
253                         /*
254                          * We are in trouble anyway, lets at least try
255                          * to get a message out.
256                          */
257                         bust_spinlocks(1);
258                         printk("NMI Watchdog detected LOCKUP on CPU%d, registers:\n", cpu);
259                         show_registers(regs);
260                         printk("console shuts up ...\n");
261                         console_silent();
262                         spin_unlock(&nmi_print_lock);
263                         bust_spinlocks(0);
264                         do_exit(SIGSEGV);
265                 }
266         } else {
267                 last_irq_sums[cpu] = sum;
268                 alert_counter[cpu] = 0;
269         }
270         if (nmi_perfctr_msr)
271                 wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1);
272 }