diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86_64/kernel/mce.c | 93 |
1 files changed, 74 insertions, 19 deletions
diff --git a/arch/x86_64/kernel/mce.c b/arch/x86_64/kernel/mce.c index 21e70625a495..3b267c91bb0c 100644 --- a/arch/x86_64/kernel/mce.c +++ b/arch/x86_64/kernel/mce.c | |||
@@ -15,6 +15,8 @@ | |||
15 | #include <linux/sysdev.h> | 15 | #include <linux/sysdev.h> |
16 | #include <linux/miscdevice.h> | 16 | #include <linux/miscdevice.h> |
17 | #include <linux/fs.h> | 17 | #include <linux/fs.h> |
18 | #include <linux/cpu.h> | ||
19 | #include <linux/percpu.h> | ||
18 | #include <asm/processor.h> | 20 | #include <asm/processor.h> |
19 | #include <asm/msr.h> | 21 | #include <asm/msr.h> |
20 | #include <asm/mce.h> | 22 | #include <asm/mce.h> |
@@ -514,10 +516,7 @@ static struct sysdev_class mce_sysclass = { | |||
514 | set_kset_name("machinecheck"), | 516 | set_kset_name("machinecheck"), |
515 | }; | 517 | }; |
516 | 518 | ||
517 | static struct sys_device device_mce = { | 519 | static DEFINE_PER_CPU(struct sys_device, device_mce); |
518 | .id = 0, | ||
519 | .cls = &mce_sysclass, | ||
520 | }; | ||
521 | 520 | ||
522 | /* Why are there no generic functions for this? */ | 521 | /* Why are there no generic functions for this? */ |
523 | #define ACCESSOR(name, var, start) \ | 522 | #define ACCESSOR(name, var, start) \ |
@@ -542,27 +541,83 @@ ACCESSOR(bank4ctl,bank[4],mce_restart()) | |||
542 | ACCESSOR(tolerant,tolerant,) | 541 | ACCESSOR(tolerant,tolerant,) |
543 | ACCESSOR(check_interval,check_interval,mce_restart()) | 542 | ACCESSOR(check_interval,check_interval,mce_restart()) |
544 | 543 | ||
545 | static __cpuinit int mce_init_device(void) | 544 | /* Per cpu sysdev init. All of the cpus still share the same ctl bank */ |
545 | static __cpuinit int mce_create_device(unsigned int cpu) | ||
546 | { | 546 | { |
547 | int err; | 547 | int err; |
548 | if (!mce_available(&cpu_data[cpu])) | ||
549 | return -EIO; | ||
550 | |||
551 | per_cpu(device_mce,cpu).id = cpu; | ||
552 | per_cpu(device_mce,cpu).cls = &mce_sysclass; | ||
553 | |||
554 | err = sysdev_register(&per_cpu(device_mce,cpu)); | ||
555 | |||
556 | if (!err) { | ||
557 | sysdev_create_file(&per_cpu(device_mce,cpu), &attr_bank0ctl); | ||
558 | sysdev_create_file(&per_cpu(device_mce,cpu), &attr_bank1ctl); | ||
559 | sysdev_create_file(&per_cpu(device_mce,cpu), &attr_bank2ctl); | ||
560 | sysdev_create_file(&per_cpu(device_mce,cpu), &attr_bank3ctl); | ||
561 | sysdev_create_file(&per_cpu(device_mce,cpu), &attr_bank4ctl); | ||
562 | sysdev_create_file(&per_cpu(device_mce,cpu), &attr_tolerant); | ||
563 | sysdev_create_file(&per_cpu(device_mce,cpu), &attr_check_interval); | ||
564 | } | ||
565 | return err; | ||
566 | } | ||
567 | |||
568 | #ifdef CONFIG_HOTPLUG_CPU | ||
569 | static __cpuinit void mce_remove_device(unsigned int cpu) | ||
570 | { | ||
571 | sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_bank0ctl); | ||
572 | sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_bank1ctl); | ||
573 | sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_bank2ctl); | ||
574 | sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_bank3ctl); | ||
575 | sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_bank4ctl); | ||
576 | sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_tolerant); | ||
577 | sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_check_interval); | ||
578 | sysdev_unregister(&per_cpu(device_mce,cpu)); | ||
579 | } | ||
580 | #endif | ||
581 | |||
582 | /* Get notified when a cpu comes on/off. Be hotplug friendly. */ | ||
583 | static __cpuinit int | ||
584 | mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) | ||
585 | { | ||
586 | unsigned int cpu = (unsigned long)hcpu; | ||
587 | |||
588 | switch (action) { | ||
589 | case CPU_ONLINE: | ||
590 | mce_create_device(cpu); | ||
591 | break; | ||
592 | #ifdef CONFIG_HOTPLUG_CPU | ||
593 | case CPU_DEAD: | ||
594 | mce_remove_device(cpu); | ||
595 | break; | ||
596 | #endif | ||
597 | } | ||
598 | return NOTIFY_OK; | ||
599 | } | ||
600 | |||
601 | static struct notifier_block mce_cpu_notifier = { | ||
602 | .notifier_call = mce_cpu_callback, | ||
603 | }; | ||
604 | |||
605 | static __init int mce_init_device(void) | ||
606 | { | ||
607 | int err; | ||
608 | int i = 0; | ||
609 | |||
548 | if (!mce_available(&boot_cpu_data)) | 610 | if (!mce_available(&boot_cpu_data)) |
549 | return -EIO; | 611 | return -EIO; |
550 | err = sysdev_class_register(&mce_sysclass); | 612 | err = sysdev_class_register(&mce_sysclass); |
551 | if (!err) | 613 | |
552 | err = sysdev_register(&device_mce); | 614 | for_each_online_cpu(i) { |
553 | if (!err) { | 615 | mce_create_device(i); |
554 | /* could create per CPU objects, but it is not worth it. */ | 616 | } |
555 | sysdev_create_file(&device_mce, &attr_bank0ctl); | 617 | |
556 | sysdev_create_file(&device_mce, &attr_bank1ctl); | 618 | register_cpu_notifier(&mce_cpu_notifier); |
557 | sysdev_create_file(&device_mce, &attr_bank2ctl); | ||
558 | sysdev_create_file(&device_mce, &attr_bank3ctl); | ||
559 | sysdev_create_file(&device_mce, &attr_bank4ctl); | ||
560 | sysdev_create_file(&device_mce, &attr_tolerant); | ||
561 | sysdev_create_file(&device_mce, &attr_check_interval); | ||
562 | } | ||
563 | |||
564 | misc_register(&mce_log_device); | 619 | misc_register(&mce_log_device); |
565 | return err; | 620 | return err; |
566 | |||
567 | } | 621 | } |
622 | |||
568 | device_initcall(mce_init_device); | 623 | device_initcall(mce_init_device); |