blob: b8f28ebdce261656e4141d4eec6849200d91cce0 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Machine check handler.
3 * K8 parts Copyright 2002,2003 Andi Kleen, SuSE Labs.
4 * Rest from unknown author(s).
5 * 2004 Andi Kleen. Rewrote most of it.
6 */
7
8#include <linux/init.h>
9#include <linux/types.h>
10#include <linux/kernel.h>
11#include <linux/sched.h>
12#include <linux/string.h>
13#include <linux/rcupdate.h>
14#include <linux/kallsyms.h>
15#include <linux/sysdev.h>
16#include <linux/miscdevice.h>
17#include <linux/fs.h>
Randy Dunlapa9415642006-01-11 12:17:48 -080018#include <linux/capability.h>
Andi Kleen91c6d402005-07-28 21:15:39 -070019#include <linux/cpu.h>
20#include <linux/percpu.h>
Andi Kleen8c566ef2005-09-12 18:49:24 +020021#include <linux/ctype.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <asm/processor.h>
23#include <asm/msr.h>
24#include <asm/mce.h>
25#include <asm/kdebug.h>
26#include <asm/uaccess.h>
27
28#define MISC_MCELOG_MINOR 227
Shaohua Li73ca5352006-01-11 22:43:06 +010029#define NR_BANKS 6
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31static int mce_dont_init;
32
33/* 0: always panic, 1: panic if deadlock possible, 2: try to avoid panic,
34 3: never panic or exit (for testing only) */
35static int tolerant = 1;
36static int banks;
37static unsigned long bank[NR_BANKS] = { [0 ... NR_BANKS-1] = ~0UL };
38static unsigned long console_logged;
39static int notify_user;
Andi Kleen94ad8472005-04-16 15:25:09 -070040static int rip_msr;
Andi Kleene5835382005-11-05 17:25:54 +010041static int mce_bootlog = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
43/*
44 * Lockless MCE logging infrastructure.
45 * This avoids deadlocks on printk locks without having to break locks. Also
46 * separate MCEs from kernel messages to avoid bogus bug reports.
47 */
48
49struct mce_log mcelog = {
50 MCE_LOG_SIGNATURE,
51 MCE_LOG_LEN,
52};
53
54void mce_log(struct mce *mce)
55{
56 unsigned next, entry;
57 mce->finished = 0;
Mike Waychison76441432005-09-30 00:01:27 +020058 wmb();
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 for (;;) {
60 entry = rcu_dereference(mcelog.next);
Mike Waychison76441432005-09-30 00:01:27 +020061 /* The rmb forces the compiler to reload next in each
62 iteration */
63 rmb();
Andi Kleen673242c2005-09-12 18:49:24 +020064 for (;;) {
65 /* When the buffer fills up discard new entries. Assume
66 that the earlier errors are the more interesting. */
67 if (entry >= MCE_LOG_LEN) {
68 set_bit(MCE_OVERFLOW, &mcelog.flags);
69 return;
70 }
71 /* Old left over entry. Skip. */
72 if (mcelog.entry[entry].finished) {
73 entry++;
74 continue;
75 }
Mike Waychison76441432005-09-30 00:01:27 +020076 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 smp_rmb();
79 next = entry + 1;
80 if (cmpxchg(&mcelog.next, entry, next) == entry)
81 break;
82 }
83 memcpy(mcelog.entry + entry, mce, sizeof(struct mce));
Mike Waychison76441432005-09-30 00:01:27 +020084 wmb();
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 mcelog.entry[entry].finished = 1;
Mike Waychison76441432005-09-30 00:01:27 +020086 wmb();
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
88 if (!test_and_set_bit(0, &console_logged))
89 notify_user = 1;
90}
91
92static void print_mce(struct mce *m)
93{
94 printk(KERN_EMERG "\n"
Andi Kleen48551702006-01-11 22:44:48 +010095 KERN_EMERG "HARDWARE ERROR\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 KERN_EMERG
97 "CPU %d: Machine Check Exception: %16Lx Bank %d: %016Lx\n",
98 m->cpu, m->mcgstatus, m->bank, m->status);
99 if (m->rip) {
100 printk(KERN_EMERG
101 "RIP%s %02x:<%016Lx> ",
102 !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "",
103 m->cs, m->rip);
104 if (m->cs == __KERNEL_CS)
105 print_symbol("{%s}", m->rip);
106 printk("\n");
107 }
108 printk(KERN_EMERG "TSC %Lx ", m->tsc);
109 if (m->addr)
110 printk("ADDR %Lx ", m->addr);
111 if (m->misc)
112 printk("MISC %Lx ", m->misc);
113 printk("\n");
Andi Kleen48551702006-01-11 22:44:48 +0100114 printk(KERN_EMERG "This is not a software problem!\n");
115 printk(KERN_EMERG
116 "Run through mcelog --ascii to decode and contact your hardware vendor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117}
118
119static void mce_panic(char *msg, struct mce *backup, unsigned long start)
120{
121 int i;
122 oops_begin();
123 for (i = 0; i < MCE_LOG_LEN; i++) {
124 unsigned long tsc = mcelog.entry[i].tsc;
125 if (time_before(tsc, start))
126 continue;
127 print_mce(&mcelog.entry[i]);
128 if (backup && mcelog.entry[i].tsc == backup->tsc)
129 backup = NULL;
130 }
131 if (backup)
132 print_mce(backup);
133 if (tolerant >= 3)
134 printk("Fake panic: %s\n", msg);
135 else
136 panic(msg);
137}
138
139static int mce_available(struct cpuinfo_x86 *c)
140{
141 return test_bit(X86_FEATURE_MCE, &c->x86_capability) &&
142 test_bit(X86_FEATURE_MCA, &c->x86_capability);
143}
144
Andi Kleen94ad8472005-04-16 15:25:09 -0700145static inline void mce_get_rip(struct mce *m, struct pt_regs *regs)
146{
147 if (regs && (m->mcgstatus & MCG_STATUS_RIPV)) {
148 m->rip = regs->rip;
149 m->cs = regs->cs;
150 } else {
151 m->rip = 0;
152 m->cs = 0;
153 }
154 if (rip_msr) {
155 /* Assume the RIP in the MSR is exact. Is this true? */
156 m->mcgstatus |= MCG_STATUS_EIPV;
157 rdmsrl(rip_msr, m->rip);
158 m->cs = 0;
159 }
160}
161
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162/*
163 * The actual machine check handler
164 */
165
166void do_machine_check(struct pt_regs * regs, long error_code)
167{
168 struct mce m, panicm;
169 int nowayout = (tolerant < 1);
170 int kill_it = 0;
171 u64 mcestart = 0;
172 int i;
173 int panicm_found = 0;
174
175 if (regs)
Jan Beulich6e3f3612006-01-11 22:42:14 +0100176 notify_die(DIE_NMI, "machine check", regs, error_code, 18, SIGKILL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 if (!banks)
178 return;
179
180 memset(&m, 0, sizeof(struct mce));
181 m.cpu = hard_smp_processor_id();
182 rdmsrl(MSR_IA32_MCG_STATUS, m.mcgstatus);
183 if (!(m.mcgstatus & MCG_STATUS_RIPV))
184 kill_it = 1;
185
186 rdtscll(mcestart);
187 barrier();
188
189 for (i = 0; i < banks; i++) {
190 if (!bank[i])
191 continue;
192
193 m.misc = 0;
194 m.addr = 0;
195 m.bank = i;
196 m.tsc = 0;
197
198 rdmsrl(MSR_IA32_MC0_STATUS + i*4, m.status);
199 if ((m.status & MCI_STATUS_VAL) == 0)
200 continue;
201
202 if (m.status & MCI_STATUS_EN) {
203 /* In theory _OVER could be a nowayout too, but
204 assume any overflowed errors were no fatal. */
205 nowayout |= !!(m.status & MCI_STATUS_PCC);
206 kill_it |= !!(m.status & MCI_STATUS_UC);
207 }
208
209 if (m.status & MCI_STATUS_MISCV)
210 rdmsrl(MSR_IA32_MC0_MISC + i*4, m.misc);
211 if (m.status & MCI_STATUS_ADDRV)
212 rdmsrl(MSR_IA32_MC0_ADDR + i*4, m.addr);
213
Andi Kleen94ad8472005-04-16 15:25:09 -0700214 mce_get_rip(&m, regs);
Andi Kleend5172f22005-08-07 09:42:07 -0700215 if (error_code >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 rdtscll(m.tsc);
217 wrmsrl(MSR_IA32_MC0_STATUS + i*4, 0);
Andi Kleend5172f22005-08-07 09:42:07 -0700218 if (error_code != -2)
219 mce_log(&m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
221 /* Did this bank cause the exception? */
222 /* Assume that the bank with uncorrectable errors did it,
223 and that there is only a single one. */
224 if ((m.status & MCI_STATUS_UC) && (m.status & MCI_STATUS_EN)) {
225 panicm = m;
226 panicm_found = 1;
227 }
228
Randy Dunlap9f158332005-09-13 01:25:16 -0700229 add_taint(TAINT_MACHINE_CHECK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 }
231
232 /* Never do anything final in the polling timer */
233 if (!regs)
234 goto out;
235
236 /* If we didn't find an uncorrectable error, pick
237 the last one (shouldn't happen, just being safe). */
238 if (!panicm_found)
239 panicm = m;
240 if (nowayout)
241 mce_panic("Machine check", &panicm, mcestart);
242 if (kill_it) {
243 int user_space = 0;
244
245 if (m.mcgstatus & MCG_STATUS_RIPV)
246 user_space = panicm.rip && (panicm.cs & 3);
247
248 /* When the machine was in user space and the CPU didn't get
249 confused it's normally not necessary to panic, unless you
250 are paranoid (tolerant == 0)
251
252 RED-PEN could be more tolerant for MCEs in idle,
253 but most likely they occur at boot anyways, where
254 it is best to just halt the machine. */
255 if ((!user_space && (panic_on_oops || tolerant < 2)) ||
256 (unsigned)current->pid <= 1)
257 mce_panic("Uncorrected machine check", &panicm, mcestart);
258
259 /* do_exit takes an awful lot of locks and has as
260 slight risk of deadlocking. If you don't want that
261 don't set tolerant >= 2 */
262 if (tolerant < 3)
263 do_exit(SIGBUS);
264 }
265
266 out:
267 /* Last thing done in the machine check exception to clear state. */
268 wrmsrl(MSR_IA32_MCG_STATUS, 0);
269}
270
271/*
272 * Periodic polling timer for "silent" machine check errors.
273 */
274
275static int check_interval = 5 * 60; /* 5 minutes */
276static void mcheck_timer(void *data);
277static DECLARE_WORK(mcheck_work, mcheck_timer, NULL);
278
279static void mcheck_check_cpu(void *info)
280{
281 if (mce_available(&current_cpu_data))
282 do_machine_check(NULL, 0);
283}
284
285static void mcheck_timer(void *data)
286{
287 on_each_cpu(mcheck_check_cpu, NULL, 1, 1);
288 schedule_delayed_work(&mcheck_work, check_interval * HZ);
289
290 /*
291 * It's ok to read stale data here for notify_user and
292 * console_logged as we'll simply get the updated versions
293 * on the next mcheck_timer execution and atomic operations
294 * on console_logged act as synchronization for notify_user
295 * writes.
296 */
297 if (notify_user && console_logged) {
298 notify_user = 0;
299 clear_bit(0, &console_logged);
300 printk(KERN_INFO "Machine check events logged\n");
301 }
302}
303
304
305static __init int periodic_mcheck_init(void)
306{
307 if (check_interval)
308 schedule_delayed_work(&mcheck_work, check_interval*HZ);
309 return 0;
310}
311__initcall(periodic_mcheck_init);
312
313
314/*
315 * Initialize Machine Checks for a CPU.
316 */
317static void mce_init(void *dummy)
318{
319 u64 cap;
320 int i;
321
322 rdmsrl(MSR_IA32_MCG_CAP, cap);
323 banks = cap & 0xff;
324 if (banks > NR_BANKS) {
325 printk(KERN_INFO "MCE: warning: using only %d banks\n", banks);
326 banks = NR_BANKS;
327 }
Andi Kleen94ad8472005-04-16 15:25:09 -0700328 /* Use accurate RIP reporting if available. */
329 if ((cap & (1<<9)) && ((cap >> 16) & 0xff) >= 9)
330 rip_msr = MSR_IA32_MCG_EIP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
332 /* Log the machine checks left over from the previous reset.
333 This also clears all registers */
Andi Kleend5172f22005-08-07 09:42:07 -0700334 do_machine_check(NULL, mce_bootlog ? -1 : -2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336 set_in_cr4(X86_CR4_MCE);
337
338 if (cap & MCG_CTL_P)
339 wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff);
340
341 for (i = 0; i < banks; i++) {
342 wrmsrl(MSR_IA32_MC0_CTL+4*i, bank[i]);
343 wrmsrl(MSR_IA32_MC0_STATUS+4*i, 0);
344 }
345}
346
347/* Add per CPU specific workarounds here */
Ashok Raje6982c62005-06-25 14:54:58 -0700348static void __cpuinit mce_cpu_quirks(struct cpuinfo_x86 *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349{
350 /* This should be disabled by the BIOS, but isn't always */
351 if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 15) {
352 /* disable GART TBL walk error reporting, which trips off
353 incorrectly with the IOMMU & 3ware & Cerberus. */
354 clear_bit(10, &bank[4]);
Andi Kleene5835382005-11-05 17:25:54 +0100355 /* Lots of broken BIOS around that don't clear them
356 by default and leave crap in there. Don't log. */
357 mce_bootlog = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 }
Andi Kleene5835382005-11-05 17:25:54 +0100359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360}
361
Ashok Raje6982c62005-06-25 14:54:58 -0700362static void __cpuinit mce_cpu_features(struct cpuinfo_x86 *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
364 switch (c->x86_vendor) {
365 case X86_VENDOR_INTEL:
366 mce_intel_feature_init(c);
367 break;
Jacob Shin89b831e2005-11-05 17:25:53 +0100368 case X86_VENDOR_AMD:
369 mce_amd_feature_init(c);
370 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 default:
372 break;
373 }
374}
375
376/*
377 * Called for each booted CPU to set up machine checks.
378 * Must be called with preempt off.
379 */
Ashok Raje6982c62005-06-25 14:54:58 -0700380void __cpuinit mcheck_init(struct cpuinfo_x86 *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381{
382 static cpumask_t mce_cpus __initdata = CPU_MASK_NONE;
383
384 mce_cpu_quirks(c);
385
386 if (mce_dont_init ||
387 cpu_test_and_set(smp_processor_id(), mce_cpus) ||
388 !mce_available(c))
389 return;
390
391 mce_init(NULL);
392 mce_cpu_features(c);
393}
394
395/*
396 * Character device to read and clear the MCE log.
397 */
398
399static void collect_tscs(void *data)
400{
401 unsigned long *cpu_tsc = (unsigned long *)data;
402 rdtscll(cpu_tsc[smp_processor_id()]);
403}
404
405static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize, loff_t *off)
406{
Andi Kleenf0de53b2005-04-16 15:25:10 -0700407 unsigned long *cpu_tsc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 static DECLARE_MUTEX(mce_read_sem);
409 unsigned next;
410 char __user *buf = ubuf;
411 int i, err;
412
Andi Kleenf0de53b2005-04-16 15:25:10 -0700413 cpu_tsc = kmalloc(NR_CPUS * sizeof(long), GFP_KERNEL);
414 if (!cpu_tsc)
415 return -ENOMEM;
416
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 down(&mce_read_sem);
418 next = rcu_dereference(mcelog.next);
419
420 /* Only supports full reads right now */
421 if (*off != 0 || usize < MCE_LOG_LEN*sizeof(struct mce)) {
422 up(&mce_read_sem);
Andi Kleenf0de53b2005-04-16 15:25:10 -0700423 kfree(cpu_tsc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 return -EINVAL;
425 }
426
427 err = 0;
Andi Kleen673242c2005-09-12 18:49:24 +0200428 for (i = 0; i < next; i++) {
429 unsigned long start = jiffies;
430 while (!mcelog.entry[i].finished) {
431 if (!time_before(jiffies, start + 2)) {
432 memset(mcelog.entry + i,0, sizeof(struct mce));
433 continue;
434 }
435 cpu_relax();
436 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 smp_rmb();
438 err |= copy_to_user(buf, mcelog.entry + i, sizeof(struct mce));
439 buf += sizeof(struct mce);
440 }
441
442 memset(mcelog.entry, 0, next * sizeof(struct mce));
443 mcelog.next = 0;
444
Paul E. McKenneyb2b18662005-06-25 14:55:38 -0700445 synchronize_sched();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446
447 /* Collect entries that were still getting written before the synchronize. */
448
449 on_each_cpu(collect_tscs, cpu_tsc, 1, 1);
450 for (i = next; i < MCE_LOG_LEN; i++) {
451 if (mcelog.entry[i].finished &&
452 mcelog.entry[i].tsc < cpu_tsc[mcelog.entry[i].cpu]) {
453 err |= copy_to_user(buf, mcelog.entry+i, sizeof(struct mce));
454 smp_rmb();
455 buf += sizeof(struct mce);
456 memset(&mcelog.entry[i], 0, sizeof(struct mce));
457 }
458 }
459 up(&mce_read_sem);
Andi Kleenf0de53b2005-04-16 15:25:10 -0700460 kfree(cpu_tsc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 return err ? -EFAULT : buf - ubuf;
462}
463
464static int mce_ioctl(struct inode *i, struct file *f,unsigned int cmd, unsigned long arg)
465{
466 int __user *p = (int __user *)arg;
467 if (!capable(CAP_SYS_ADMIN))
468 return -EPERM;
469 switch (cmd) {
470 case MCE_GET_RECORD_LEN:
471 return put_user(sizeof(struct mce), p);
472 case MCE_GET_LOG_LEN:
473 return put_user(MCE_LOG_LEN, p);
474 case MCE_GETCLEAR_FLAGS: {
475 unsigned flags;
476 do {
477 flags = mcelog.flags;
478 } while (cmpxchg(&mcelog.flags, flags, 0) != flags);
479 return put_user(flags, p);
480 }
481 default:
482 return -ENOTTY;
483 }
484}
485
486static struct file_operations mce_chrdev_ops = {
487 .read = mce_read,
488 .ioctl = mce_ioctl,
489};
490
491static struct miscdevice mce_log_device = {
492 MISC_MCELOG_MINOR,
493 "mcelog",
494 &mce_chrdev_ops,
495};
496
497/*
498 * Old style boot options parsing. Only for compatibility.
499 */
500
501static int __init mcheck_disable(char *str)
502{
503 mce_dont_init = 1;
504 return 0;
505}
506
507/* mce=off disables machine check. Note you can reenable it later
Andi Kleend5172f22005-08-07 09:42:07 -0700508 using sysfs.
Andi Kleen8c566ef2005-09-12 18:49:24 +0200509 mce=TOLERANCELEVEL (number, see above)
Andi Kleene5835382005-11-05 17:25:54 +0100510 mce=bootlog Log MCEs from before booting. Disabled by default on AMD.
511 mce=nobootlog Don't log MCEs from before booting. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512static int __init mcheck_enable(char *str)
513{
Andi Kleend5172f22005-08-07 09:42:07 -0700514 if (*str == '=')
515 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 if (!strcmp(str, "off"))
517 mce_dont_init = 1;
Andi Kleene5835382005-11-05 17:25:54 +0100518 else if (!strcmp(str, "bootlog") || !strcmp(str,"nobootlog"))
519 mce_bootlog = str[0] == 'b';
Andi Kleen8c566ef2005-09-12 18:49:24 +0200520 else if (isdigit(str[0]))
521 get_option(&str, &tolerant);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 else
523 printk("mce= argument %s ignored. Please use /sys", str);
524 return 0;
525}
526
527__setup("nomce", mcheck_disable);
528__setup("mce", mcheck_enable);
529
530/*
531 * Sysfs support
532 */
533
Andi Kleen413588c2005-09-12 18:49:24 +0200534/* On resume clear all MCE state. Don't want to see leftovers from the BIOS.
535 Only one CPU is active at this time, the others get readded later using
536 CPU hotplug. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537static int mce_resume(struct sys_device *dev)
538{
Andi Kleen413588c2005-09-12 18:49:24 +0200539 mce_init(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 return 0;
541}
542
543/* Reinit MCEs after user configuration changes */
544static void mce_restart(void)
545{
546 if (check_interval)
547 cancel_delayed_work(&mcheck_work);
548 /* Timer race is harmless here */
549 on_each_cpu(mce_init, NULL, 1, 1);
550 if (check_interval)
551 schedule_delayed_work(&mcheck_work, check_interval*HZ);
552}
553
554static struct sysdev_class mce_sysclass = {
555 .resume = mce_resume,
556 set_kset_name("machinecheck"),
557};
558
Andi Kleen91c6d402005-07-28 21:15:39 -0700559static DEFINE_PER_CPU(struct sys_device, device_mce);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
561/* Why are there no generic functions for this? */
562#define ACCESSOR(name, var, start) \
563 static ssize_t show_ ## name(struct sys_device *s, char *buf) { \
564 return sprintf(buf, "%lx\n", (unsigned long)var); \
565 } \
566 static ssize_t set_ ## name(struct sys_device *s,const char *buf,size_t siz) { \
567 char *end; \
568 unsigned long new = simple_strtoul(buf, &end, 0); \
569 if (end == buf) return -EINVAL; \
570 var = new; \
571 start; \
572 return end-buf; \
573 } \
574 static SYSDEV_ATTR(name, 0644, show_ ## name, set_ ## name);
575
576ACCESSOR(bank0ctl,bank[0],mce_restart())
577ACCESSOR(bank1ctl,bank[1],mce_restart())
578ACCESSOR(bank2ctl,bank[2],mce_restart())
579ACCESSOR(bank3ctl,bank[3],mce_restart())
580ACCESSOR(bank4ctl,bank[4],mce_restart())
Shaohua Li73ca5352006-01-11 22:43:06 +0100581ACCESSOR(bank5ctl,bank[5],mce_restart())
582static struct sysdev_attribute * bank_attributes[NR_BANKS] = {
583 &attr_bank0ctl, &attr_bank1ctl, &attr_bank2ctl,
584 &attr_bank3ctl, &attr_bank4ctl, &attr_bank5ctl};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585ACCESSOR(tolerant,tolerant,)
586ACCESSOR(check_interval,check_interval,mce_restart())
587
Andi Kleen91c6d402005-07-28 21:15:39 -0700588/* Per cpu sysdev init. All of the cpus still share the same ctl bank */
589static __cpuinit int mce_create_device(unsigned int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
591 int err;
Shaohua Li73ca5352006-01-11 22:43:06 +0100592 int i;
Andi Kleen91c6d402005-07-28 21:15:39 -0700593 if (!mce_available(&cpu_data[cpu]))
594 return -EIO;
595
596 per_cpu(device_mce,cpu).id = cpu;
597 per_cpu(device_mce,cpu).cls = &mce_sysclass;
598
599 err = sysdev_register(&per_cpu(device_mce,cpu));
600
601 if (!err) {
Shaohua Li73ca5352006-01-11 22:43:06 +0100602 for (i = 0; i < banks; i++)
603 sysdev_create_file(&per_cpu(device_mce,cpu),
604 bank_attributes[i]);
Andi Kleen91c6d402005-07-28 21:15:39 -0700605 sysdev_create_file(&per_cpu(device_mce,cpu), &attr_tolerant);
606 sysdev_create_file(&per_cpu(device_mce,cpu), &attr_check_interval);
607 }
608 return err;
609}
610
611#ifdef CONFIG_HOTPLUG_CPU
612static __cpuinit void mce_remove_device(unsigned int cpu)
613{
Shaohua Li73ca5352006-01-11 22:43:06 +0100614 int i;
615
616 for (i = 0; i < banks; i++)
617 sysdev_remove_file(&per_cpu(device_mce,cpu),
618 bank_attributes[i]);
Andi Kleen91c6d402005-07-28 21:15:39 -0700619 sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_tolerant);
620 sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_check_interval);
621 sysdev_unregister(&per_cpu(device_mce,cpu));
622}
623#endif
624
625/* Get notified when a cpu comes on/off. Be hotplug friendly. */
626static __cpuinit int
627mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
628{
629 unsigned int cpu = (unsigned long)hcpu;
630
631 switch (action) {
632 case CPU_ONLINE:
633 mce_create_device(cpu);
634 break;
635#ifdef CONFIG_HOTPLUG_CPU
636 case CPU_DEAD:
637 mce_remove_device(cpu);
638 break;
639#endif
640 }
641 return NOTIFY_OK;
642}
643
644static struct notifier_block mce_cpu_notifier = {
645 .notifier_call = mce_cpu_callback,
646};
647
648static __init int mce_init_device(void)
649{
650 int err;
651 int i = 0;
652
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 if (!mce_available(&boot_cpu_data))
654 return -EIO;
655 err = sysdev_class_register(&mce_sysclass);
Andi Kleen91c6d402005-07-28 21:15:39 -0700656
657 for_each_online_cpu(i) {
658 mce_create_device(i);
659 }
660
661 register_cpu_notifier(&mce_cpu_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 misc_register(&mce_log_device);
663 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664}
Andi Kleen91c6d402005-07-28 21:15:39 -0700665
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666device_initcall(mce_init_device);