blob: b8b9529fa89e0db08b90cc6d6bea4da6fa7e0afb [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>
Andi Kleen0a9c3ee2006-01-11 22:46:54 +010027#include <asm/smp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#define MISC_MCELOG_MINOR 227
Shaohua Li73ca5352006-01-11 22:43:06 +010030#define NR_BANKS 6
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32static int mce_dont_init;
33
34/* 0: always panic, 1: panic if deadlock possible, 2: try to avoid panic,
35 3: never panic or exit (for testing only) */
36static int tolerant = 1;
37static int banks;
38static unsigned long bank[NR_BANKS] = { [0 ... NR_BANKS-1] = ~0UL };
39static unsigned long console_logged;
40static int notify_user;
Andi Kleen94ad8472005-04-16 15:25:09 -070041static int rip_msr;
Andi Kleene5835382005-11-05 17:25:54 +010042static int mce_bootlog = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44/*
45 * Lockless MCE logging infrastructure.
46 * This avoids deadlocks on printk locks without having to break locks. Also
47 * separate MCEs from kernel messages to avoid bogus bug reports.
48 */
49
50struct mce_log mcelog = {
51 MCE_LOG_SIGNATURE,
52 MCE_LOG_LEN,
53};
54
55void mce_log(struct mce *mce)
56{
57 unsigned next, entry;
58 mce->finished = 0;
Mike Waychison76441432005-09-30 00:01:27 +020059 wmb();
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 for (;;) {
61 entry = rcu_dereference(mcelog.next);
Mike Waychison76441432005-09-30 00:01:27 +020062 /* The rmb forces the compiler to reload next in each
63 iteration */
64 rmb();
Andi Kleen673242c2005-09-12 18:49:24 +020065 for (;;) {
66 /* When the buffer fills up discard new entries. Assume
67 that the earlier errors are the more interesting. */
68 if (entry >= MCE_LOG_LEN) {
69 set_bit(MCE_OVERFLOW, &mcelog.flags);
70 return;
71 }
72 /* Old left over entry. Skip. */
73 if (mcelog.entry[entry].finished) {
74 entry++;
75 continue;
76 }
Mike Waychison76441432005-09-30 00:01:27 +020077 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 smp_rmb();
80 next = entry + 1;
81 if (cmpxchg(&mcelog.next, entry, next) == entry)
82 break;
83 }
84 memcpy(mcelog.entry + entry, mce, sizeof(struct mce));
Mike Waychison76441432005-09-30 00:01:27 +020085 wmb();
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 mcelog.entry[entry].finished = 1;
Mike Waychison76441432005-09-30 00:01:27 +020087 wmb();
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
89 if (!test_and_set_bit(0, &console_logged))
90 notify_user = 1;
91}
92
93static void print_mce(struct mce *m)
94{
95 printk(KERN_EMERG "\n"
Andi Kleen48551702006-01-11 22:44:48 +010096 KERN_EMERG "HARDWARE ERROR\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 KERN_EMERG
98 "CPU %d: Machine Check Exception: %16Lx Bank %d: %016Lx\n",
99 m->cpu, m->mcgstatus, m->bank, m->status);
100 if (m->rip) {
101 printk(KERN_EMERG
102 "RIP%s %02x:<%016Lx> ",
103 !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "",
104 m->cs, m->rip);
105 if (m->cs == __KERNEL_CS)
106 print_symbol("{%s}", m->rip);
107 printk("\n");
108 }
109 printk(KERN_EMERG "TSC %Lx ", m->tsc);
110 if (m->addr)
111 printk("ADDR %Lx ", m->addr);
112 if (m->misc)
113 printk("MISC %Lx ", m->misc);
114 printk("\n");
Andi Kleen48551702006-01-11 22:44:48 +0100115 printk(KERN_EMERG "This is not a software problem!\n");
116 printk(KERN_EMERG
117 "Run through mcelog --ascii to decode and contact your hardware vendor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118}
119
120static void mce_panic(char *msg, struct mce *backup, unsigned long start)
121{
122 int i;
123 oops_begin();
124 for (i = 0; i < MCE_LOG_LEN; i++) {
125 unsigned long tsc = mcelog.entry[i].tsc;
126 if (time_before(tsc, start))
127 continue;
128 print_mce(&mcelog.entry[i]);
129 if (backup && mcelog.entry[i].tsc == backup->tsc)
130 backup = NULL;
131 }
132 if (backup)
133 print_mce(backup);
134 if (tolerant >= 3)
135 printk("Fake panic: %s\n", msg);
136 else
137 panic(msg);
138}
139
140static int mce_available(struct cpuinfo_x86 *c)
141{
142 return test_bit(X86_FEATURE_MCE, &c->x86_capability) &&
143 test_bit(X86_FEATURE_MCA, &c->x86_capability);
144}
145
Andi Kleen94ad8472005-04-16 15:25:09 -0700146static inline void mce_get_rip(struct mce *m, struct pt_regs *regs)
147{
148 if (regs && (m->mcgstatus & MCG_STATUS_RIPV)) {
149 m->rip = regs->rip;
150 m->cs = regs->cs;
151 } else {
152 m->rip = 0;
153 m->cs = 0;
154 }
155 if (rip_msr) {
156 /* Assume the RIP in the MSR is exact. Is this true? */
157 m->mcgstatus |= MCG_STATUS_EIPV;
158 rdmsrl(rip_msr, m->rip);
159 m->cs = 0;
160 }
161}
162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163/*
164 * The actual machine check handler
165 */
166
167void do_machine_check(struct pt_regs * regs, long error_code)
168{
169 struct mce m, panicm;
170 int nowayout = (tolerant < 1);
171 int kill_it = 0;
172 u64 mcestart = 0;
173 int i;
174 int panicm_found = 0;
175
176 if (regs)
Jan Beulich6e3f3612006-01-11 22:42:14 +0100177 notify_die(DIE_NMI, "machine check", regs, error_code, 18, SIGKILL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 if (!banks)
179 return;
180
181 memset(&m, 0, sizeof(struct mce));
Andi Kleen0a9c3ee2006-01-11 22:46:54 +0100182 m.cpu = safe_smp_processor_id();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 rdmsrl(MSR_IA32_MCG_STATUS, m.mcgstatus);
184 if (!(m.mcgstatus & MCG_STATUS_RIPV))
185 kill_it = 1;
186
187 rdtscll(mcestart);
188 barrier();
189
190 for (i = 0; i < banks; i++) {
191 if (!bank[i])
192 continue;
193
194 m.misc = 0;
195 m.addr = 0;
196 m.bank = i;
197 m.tsc = 0;
198
199 rdmsrl(MSR_IA32_MC0_STATUS + i*4, m.status);
200 if ((m.status & MCI_STATUS_VAL) == 0)
201 continue;
202
203 if (m.status & MCI_STATUS_EN) {
204 /* In theory _OVER could be a nowayout too, but
205 assume any overflowed errors were no fatal. */
206 nowayout |= !!(m.status & MCI_STATUS_PCC);
207 kill_it |= !!(m.status & MCI_STATUS_UC);
208 }
209
210 if (m.status & MCI_STATUS_MISCV)
211 rdmsrl(MSR_IA32_MC0_MISC + i*4, m.misc);
212 if (m.status & MCI_STATUS_ADDRV)
213 rdmsrl(MSR_IA32_MC0_ADDR + i*4, m.addr);
214
Andi Kleen94ad8472005-04-16 15:25:09 -0700215 mce_get_rip(&m, regs);
Andi Kleend5172f22005-08-07 09:42:07 -0700216 if (error_code >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 rdtscll(m.tsc);
218 wrmsrl(MSR_IA32_MC0_STATUS + i*4, 0);
Andi Kleend5172f22005-08-07 09:42:07 -0700219 if (error_code != -2)
220 mce_log(&m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221
222 /* Did this bank cause the exception? */
223 /* Assume that the bank with uncorrectable errors did it,
224 and that there is only a single one. */
225 if ((m.status & MCI_STATUS_UC) && (m.status & MCI_STATUS_EN)) {
226 panicm = m;
227 panicm_found = 1;
228 }
229
Randy Dunlap9f158332005-09-13 01:25:16 -0700230 add_taint(TAINT_MACHINE_CHECK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 }
232
233 /* Never do anything final in the polling timer */
234 if (!regs)
235 goto out;
236
237 /* If we didn't find an uncorrectable error, pick
238 the last one (shouldn't happen, just being safe). */
239 if (!panicm_found)
240 panicm = m;
241 if (nowayout)
242 mce_panic("Machine check", &panicm, mcestart);
243 if (kill_it) {
244 int user_space = 0;
245
246 if (m.mcgstatus & MCG_STATUS_RIPV)
247 user_space = panicm.rip && (panicm.cs & 3);
248
249 /* When the machine was in user space and the CPU didn't get
250 confused it's normally not necessary to panic, unless you
251 are paranoid (tolerant == 0)
252
253 RED-PEN could be more tolerant for MCEs in idle,
254 but most likely they occur at boot anyways, where
255 it is best to just halt the machine. */
256 if ((!user_space && (panic_on_oops || tolerant < 2)) ||
257 (unsigned)current->pid <= 1)
258 mce_panic("Uncorrected machine check", &panicm, mcestart);
259
260 /* do_exit takes an awful lot of locks and has as
261 slight risk of deadlocking. If you don't want that
262 don't set tolerant >= 2 */
263 if (tolerant < 3)
264 do_exit(SIGBUS);
265 }
266
267 out:
268 /* Last thing done in the machine check exception to clear state. */
269 wrmsrl(MSR_IA32_MCG_STATUS, 0);
270}
271
272/*
273 * Periodic polling timer for "silent" machine check errors.
274 */
275
276static int check_interval = 5 * 60; /* 5 minutes */
277static void mcheck_timer(void *data);
278static DECLARE_WORK(mcheck_work, mcheck_timer, NULL);
279
280static void mcheck_check_cpu(void *info)
281{
282 if (mce_available(&current_cpu_data))
283 do_machine_check(NULL, 0);
284}
285
286static void mcheck_timer(void *data)
287{
288 on_each_cpu(mcheck_check_cpu, NULL, 1, 1);
289 schedule_delayed_work(&mcheck_work, check_interval * HZ);
290
291 /*
292 * It's ok to read stale data here for notify_user and
293 * console_logged as we'll simply get the updated versions
294 * on the next mcheck_timer execution and atomic operations
295 * on console_logged act as synchronization for notify_user
296 * writes.
297 */
298 if (notify_user && console_logged) {
299 notify_user = 0;
300 clear_bit(0, &console_logged);
301 printk(KERN_INFO "Machine check events logged\n");
302 }
303}
304
305
306static __init int periodic_mcheck_init(void)
307{
308 if (check_interval)
309 schedule_delayed_work(&mcheck_work, check_interval*HZ);
310 return 0;
311}
312__initcall(periodic_mcheck_init);
313
314
315/*
316 * Initialize Machine Checks for a CPU.
317 */
318static void mce_init(void *dummy)
319{
320 u64 cap;
321 int i;
322
323 rdmsrl(MSR_IA32_MCG_CAP, cap);
324 banks = cap & 0xff;
325 if (banks > NR_BANKS) {
326 printk(KERN_INFO "MCE: warning: using only %d banks\n", banks);
327 banks = NR_BANKS;
328 }
Andi Kleen94ad8472005-04-16 15:25:09 -0700329 /* Use accurate RIP reporting if available. */
330 if ((cap & (1<<9)) && ((cap >> 16) & 0xff) >= 9)
331 rip_msr = MSR_IA32_MCG_EIP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
333 /* Log the machine checks left over from the previous reset.
334 This also clears all registers */
Andi Kleend5172f22005-08-07 09:42:07 -0700335 do_machine_check(NULL, mce_bootlog ? -1 : -2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337 set_in_cr4(X86_CR4_MCE);
338
339 if (cap & MCG_CTL_P)
340 wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff);
341
342 for (i = 0; i < banks; i++) {
343 wrmsrl(MSR_IA32_MC0_CTL+4*i, bank[i]);
344 wrmsrl(MSR_IA32_MC0_STATUS+4*i, 0);
345 }
346}
347
348/* Add per CPU specific workarounds here */
Ashok Raje6982c62005-06-25 14:54:58 -0700349static void __cpuinit mce_cpu_quirks(struct cpuinfo_x86 *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350{
351 /* This should be disabled by the BIOS, but isn't always */
352 if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 15) {
353 /* disable GART TBL walk error reporting, which trips off
354 incorrectly with the IOMMU & 3ware & Cerberus. */
355 clear_bit(10, &bank[4]);
Andi Kleene5835382005-11-05 17:25:54 +0100356 /* Lots of broken BIOS around that don't clear them
357 by default and leave crap in there. Don't log. */
358 mce_bootlog = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 }
Andi Kleene5835382005-11-05 17:25:54 +0100360
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361}
362
Ashok Raje6982c62005-06-25 14:54:58 -0700363static void __cpuinit mce_cpu_features(struct cpuinfo_x86 *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
365 switch (c->x86_vendor) {
366 case X86_VENDOR_INTEL:
367 mce_intel_feature_init(c);
368 break;
Jacob Shin89b831e2005-11-05 17:25:53 +0100369 case X86_VENDOR_AMD:
370 mce_amd_feature_init(c);
371 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 default:
373 break;
374 }
375}
376
377/*
378 * Called for each booted CPU to set up machine checks.
379 * Must be called with preempt off.
380 */
Ashok Raje6982c62005-06-25 14:54:58 -0700381void __cpuinit mcheck_init(struct cpuinfo_x86 *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382{
Ashok Raj7ded5682006-02-03 21:51:23 +0100383 static cpumask_t mce_cpus = CPU_MASK_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
385 mce_cpu_quirks(c);
386
387 if (mce_dont_init ||
388 cpu_test_and_set(smp_processor_id(), mce_cpus) ||
389 !mce_available(c))
390 return;
391
392 mce_init(NULL);
393 mce_cpu_features(c);
394}
395
396/*
397 * Character device to read and clear the MCE log.
398 */
399
400static void collect_tscs(void *data)
401{
402 unsigned long *cpu_tsc = (unsigned long *)data;
403 rdtscll(cpu_tsc[smp_processor_id()]);
404}
405
406static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize, loff_t *off)
407{
Andi Kleenf0de53b2005-04-16 15:25:10 -0700408 unsigned long *cpu_tsc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 static DECLARE_MUTEX(mce_read_sem);
410 unsigned next;
411 char __user *buf = ubuf;
412 int i, err;
413
Andi Kleenf0de53b2005-04-16 15:25:10 -0700414 cpu_tsc = kmalloc(NR_CPUS * sizeof(long), GFP_KERNEL);
415 if (!cpu_tsc)
416 return -ENOMEM;
417
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 down(&mce_read_sem);
419 next = rcu_dereference(mcelog.next);
420
421 /* Only supports full reads right now */
422 if (*off != 0 || usize < MCE_LOG_LEN*sizeof(struct mce)) {
423 up(&mce_read_sem);
Andi Kleenf0de53b2005-04-16 15:25:10 -0700424 kfree(cpu_tsc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 return -EINVAL;
426 }
427
428 err = 0;
Andi Kleen673242c2005-09-12 18:49:24 +0200429 for (i = 0; i < next; i++) {
430 unsigned long start = jiffies;
431 while (!mcelog.entry[i].finished) {
432 if (!time_before(jiffies, start + 2)) {
433 memset(mcelog.entry + i,0, sizeof(struct mce));
434 continue;
435 }
436 cpu_relax();
437 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 smp_rmb();
439 err |= copy_to_user(buf, mcelog.entry + i, sizeof(struct mce));
440 buf += sizeof(struct mce);
441 }
442
443 memset(mcelog.entry, 0, next * sizeof(struct mce));
444 mcelog.next = 0;
445
Paul E. McKenneyb2b18662005-06-25 14:55:38 -0700446 synchronize_sched();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447
448 /* Collect entries that were still getting written before the synchronize. */
449
450 on_each_cpu(collect_tscs, cpu_tsc, 1, 1);
451 for (i = next; i < MCE_LOG_LEN; i++) {
452 if (mcelog.entry[i].finished &&
453 mcelog.entry[i].tsc < cpu_tsc[mcelog.entry[i].cpu]) {
454 err |= copy_to_user(buf, mcelog.entry+i, sizeof(struct mce));
455 smp_rmb();
456 buf += sizeof(struct mce);
457 memset(&mcelog.entry[i], 0, sizeof(struct mce));
458 }
459 }
460 up(&mce_read_sem);
Andi Kleenf0de53b2005-04-16 15:25:10 -0700461 kfree(cpu_tsc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 return err ? -EFAULT : buf - ubuf;
463}
464
465static int mce_ioctl(struct inode *i, struct file *f,unsigned int cmd, unsigned long arg)
466{
467 int __user *p = (int __user *)arg;
468 if (!capable(CAP_SYS_ADMIN))
469 return -EPERM;
470 switch (cmd) {
471 case MCE_GET_RECORD_LEN:
472 return put_user(sizeof(struct mce), p);
473 case MCE_GET_LOG_LEN:
474 return put_user(MCE_LOG_LEN, p);
475 case MCE_GETCLEAR_FLAGS: {
476 unsigned flags;
477 do {
478 flags = mcelog.flags;
479 } while (cmpxchg(&mcelog.flags, flags, 0) != flags);
480 return put_user(flags, p);
481 }
482 default:
483 return -ENOTTY;
484 }
485}
486
487static struct file_operations mce_chrdev_ops = {
488 .read = mce_read,
489 .ioctl = mce_ioctl,
490};
491
492static struct miscdevice mce_log_device = {
493 MISC_MCELOG_MINOR,
494 "mcelog",
495 &mce_chrdev_ops,
496};
497
498/*
499 * Old style boot options parsing. Only for compatibility.
500 */
501
502static int __init mcheck_disable(char *str)
503{
504 mce_dont_init = 1;
505 return 0;
506}
507
508/* mce=off disables machine check. Note you can reenable it later
Andi Kleend5172f22005-08-07 09:42:07 -0700509 using sysfs.
Andi Kleen8c566ef2005-09-12 18:49:24 +0200510 mce=TOLERANCELEVEL (number, see above)
Andi Kleene5835382005-11-05 17:25:54 +0100511 mce=bootlog Log MCEs from before booting. Disabled by default on AMD.
512 mce=nobootlog Don't log MCEs from before booting. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513static int __init mcheck_enable(char *str)
514{
Andi Kleend5172f22005-08-07 09:42:07 -0700515 if (*str == '=')
516 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 if (!strcmp(str, "off"))
518 mce_dont_init = 1;
Andi Kleene5835382005-11-05 17:25:54 +0100519 else if (!strcmp(str, "bootlog") || !strcmp(str,"nobootlog"))
520 mce_bootlog = str[0] == 'b';
Andi Kleen8c566ef2005-09-12 18:49:24 +0200521 else if (isdigit(str[0]))
522 get_option(&str, &tolerant);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 else
524 printk("mce= argument %s ignored. Please use /sys", str);
525 return 0;
526}
527
528__setup("nomce", mcheck_disable);
529__setup("mce", mcheck_enable);
530
531/*
532 * Sysfs support
533 */
534
Andi Kleen413588c2005-09-12 18:49:24 +0200535/* On resume clear all MCE state. Don't want to see leftovers from the BIOS.
536 Only one CPU is active at this time, the others get readded later using
537 CPU hotplug. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538static int mce_resume(struct sys_device *dev)
539{
Andi Kleen413588c2005-09-12 18:49:24 +0200540 mce_init(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 return 0;
542}
543
544/* Reinit MCEs after user configuration changes */
545static void mce_restart(void)
546{
547 if (check_interval)
548 cancel_delayed_work(&mcheck_work);
549 /* Timer race is harmless here */
550 on_each_cpu(mce_init, NULL, 1, 1);
551 if (check_interval)
552 schedule_delayed_work(&mcheck_work, check_interval*HZ);
553}
554
555static struct sysdev_class mce_sysclass = {
556 .resume = mce_resume,
557 set_kset_name("machinecheck"),
558};
559
Andi Kleen91c6d402005-07-28 21:15:39 -0700560static DEFINE_PER_CPU(struct sys_device, device_mce);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
562/* Why are there no generic functions for this? */
563#define ACCESSOR(name, var, start) \
564 static ssize_t show_ ## name(struct sys_device *s, char *buf) { \
565 return sprintf(buf, "%lx\n", (unsigned long)var); \
566 } \
567 static ssize_t set_ ## name(struct sys_device *s,const char *buf,size_t siz) { \
568 char *end; \
569 unsigned long new = simple_strtoul(buf, &end, 0); \
570 if (end == buf) return -EINVAL; \
571 var = new; \
572 start; \
573 return end-buf; \
574 } \
575 static SYSDEV_ATTR(name, 0644, show_ ## name, set_ ## name);
576
577ACCESSOR(bank0ctl,bank[0],mce_restart())
578ACCESSOR(bank1ctl,bank[1],mce_restart())
579ACCESSOR(bank2ctl,bank[2],mce_restart())
580ACCESSOR(bank3ctl,bank[3],mce_restart())
581ACCESSOR(bank4ctl,bank[4],mce_restart())
Shaohua Li73ca5352006-01-11 22:43:06 +0100582ACCESSOR(bank5ctl,bank[5],mce_restart())
583static struct sysdev_attribute * bank_attributes[NR_BANKS] = {
584 &attr_bank0ctl, &attr_bank1ctl, &attr_bank2ctl,
585 &attr_bank3ctl, &attr_bank4ctl, &attr_bank5ctl};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586ACCESSOR(tolerant,tolerant,)
587ACCESSOR(check_interval,check_interval,mce_restart())
588
Andi Kleen91c6d402005-07-28 21:15:39 -0700589/* Per cpu sysdev init. All of the cpus still share the same ctl bank */
590static __cpuinit int mce_create_device(unsigned int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591{
592 int err;
Shaohua Li73ca5352006-01-11 22:43:06 +0100593 int i;
Andi Kleen91c6d402005-07-28 21:15:39 -0700594 if (!mce_available(&cpu_data[cpu]))
595 return -EIO;
596
597 per_cpu(device_mce,cpu).id = cpu;
598 per_cpu(device_mce,cpu).cls = &mce_sysclass;
599
600 err = sysdev_register(&per_cpu(device_mce,cpu));
601
602 if (!err) {
Shaohua Li73ca5352006-01-11 22:43:06 +0100603 for (i = 0; i < banks; i++)
604 sysdev_create_file(&per_cpu(device_mce,cpu),
605 bank_attributes[i]);
Andi Kleen91c6d402005-07-28 21:15:39 -0700606 sysdev_create_file(&per_cpu(device_mce,cpu), &attr_tolerant);
607 sysdev_create_file(&per_cpu(device_mce,cpu), &attr_check_interval);
608 }
609 return err;
610}
611
612#ifdef CONFIG_HOTPLUG_CPU
613static __cpuinit void mce_remove_device(unsigned int cpu)
614{
Shaohua Li73ca5352006-01-11 22:43:06 +0100615 int i;
616
617 for (i = 0; i < banks; i++)
618 sysdev_remove_file(&per_cpu(device_mce,cpu),
619 bank_attributes[i]);
Andi Kleen91c6d402005-07-28 21:15:39 -0700620 sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_tolerant);
621 sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_check_interval);
622 sysdev_unregister(&per_cpu(device_mce,cpu));
623}
624#endif
625
626/* Get notified when a cpu comes on/off. Be hotplug friendly. */
627static __cpuinit int
628mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
629{
630 unsigned int cpu = (unsigned long)hcpu;
631
632 switch (action) {
633 case CPU_ONLINE:
634 mce_create_device(cpu);
635 break;
636#ifdef CONFIG_HOTPLUG_CPU
637 case CPU_DEAD:
638 mce_remove_device(cpu);
639 break;
640#endif
641 }
642 return NOTIFY_OK;
643}
644
645static struct notifier_block mce_cpu_notifier = {
646 .notifier_call = mce_cpu_callback,
647};
648
649static __init int mce_init_device(void)
650{
651 int err;
652 int i = 0;
653
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 if (!mce_available(&boot_cpu_data))
655 return -EIO;
656 err = sysdev_class_register(&mce_sysclass);
Andi Kleen91c6d402005-07-28 21:15:39 -0700657
658 for_each_online_cpu(i) {
659 mce_create_device(i);
660 }
661
662 register_cpu_notifier(&mce_cpu_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 misc_register(&mce_log_device);
664 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665}
Andi Kleen91c6d402005-07-28 21:15:39 -0700666
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667device_initcall(mce_init_device);