diff options
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce_64.c | 68 |
1 files changed, 45 insertions, 23 deletions
diff --git a/arch/x86/kernel/cpu/mcheck/mce_64.c b/arch/x86/kernel/cpu/mcheck/mce_64.c index 18b379cf0610..3f0550d16f3c 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_64.c +++ b/arch/x86/kernel/cpu/mcheck/mce_64.c | |||
@@ -353,18 +353,17 @@ void mce_log_therm_throt_event(unsigned int cpu, __u64 status) | |||
353 | 353 | ||
354 | static int check_interval = 5 * 60; /* 5 minutes */ | 354 | static int check_interval = 5 * 60; /* 5 minutes */ |
355 | static int next_interval; /* in jiffies */ | 355 | static int next_interval; /* in jiffies */ |
356 | static void mcheck_timer(struct work_struct *work); | 356 | static void mcheck_timer(unsigned long); |
357 | static DECLARE_DELAYED_WORK(mcheck_work, mcheck_timer); | 357 | static DEFINE_PER_CPU(struct timer_list, mce_timer); |
358 | 358 | ||
359 | static void mcheck_check_cpu(void *info) | 359 | static void mcheck_timer(unsigned long data) |
360 | { | 360 | { |
361 | struct timer_list *t = &per_cpu(mce_timer, data); | ||
362 | |||
363 | WARN_ON(smp_processor_id() != data); | ||
364 | |||
361 | if (mce_available(¤t_cpu_data)) | 365 | if (mce_available(¤t_cpu_data)) |
362 | do_machine_check(NULL, 0); | 366 | do_machine_check(NULL, 0); |
363 | } | ||
364 | |||
365 | static void mcheck_timer(struct work_struct *work) | ||
366 | { | ||
367 | on_each_cpu(mcheck_check_cpu, NULL, 1); | ||
368 | 367 | ||
369 | /* | 368 | /* |
370 | * Alert userspace if needed. If we logged an MCE, reduce the | 369 | * Alert userspace if needed. If we logged an MCE, reduce the |
@@ -377,7 +376,8 @@ static void mcheck_timer(struct work_struct *work) | |||
377 | (int)round_jiffies_relative(check_interval*HZ)); | 376 | (int)round_jiffies_relative(check_interval*HZ)); |
378 | } | 377 | } |
379 | 378 | ||
380 | schedule_delayed_work(&mcheck_work, next_interval); | 379 | t->expires = jiffies + next_interval; |
380 | add_timer(t); | ||
381 | } | 381 | } |
382 | 382 | ||
383 | static void mce_do_trigger(struct work_struct *work) | 383 | static void mce_do_trigger(struct work_struct *work) |
@@ -436,16 +436,11 @@ static struct notifier_block mce_idle_notifier = { | |||
436 | 436 | ||
437 | static __init int periodic_mcheck_init(void) | 437 | static __init int periodic_mcheck_init(void) |
438 | { | 438 | { |
439 | next_interval = check_interval * HZ; | 439 | idle_notifier_register(&mce_idle_notifier); |
440 | if (next_interval) | 440 | return 0; |
441 | schedule_delayed_work(&mcheck_work, | ||
442 | round_jiffies_relative(next_interval)); | ||
443 | idle_notifier_register(&mce_idle_notifier); | ||
444 | return 0; | ||
445 | } | 441 | } |
446 | __initcall(periodic_mcheck_init); | 442 | __initcall(periodic_mcheck_init); |
447 | 443 | ||
448 | |||
449 | /* | 444 | /* |
450 | * Initialize Machine Checks for a CPU. | 445 | * Initialize Machine Checks for a CPU. |
451 | */ | 446 | */ |
@@ -515,6 +510,20 @@ static void __cpuinit mce_cpu_features(struct cpuinfo_x86 *c) | |||
515 | } | 510 | } |
516 | } | 511 | } |
517 | 512 | ||
513 | static void mce_init_timer(void) | ||
514 | { | ||
515 | struct timer_list *t = &__get_cpu_var(mce_timer); | ||
516 | |||
517 | /* data race harmless because everyone sets to the same value */ | ||
518 | if (!next_interval) | ||
519 | next_interval = check_interval * HZ; | ||
520 | if (!next_interval) | ||
521 | return; | ||
522 | setup_timer(t, mcheck_timer, smp_processor_id()); | ||
523 | t->expires = round_jiffies_relative(jiffies + next_interval); | ||
524 | add_timer(t); | ||
525 | } | ||
526 | |||
518 | /* | 527 | /* |
519 | * Called for each booted CPU to set up machine checks. | 528 | * Called for each booted CPU to set up machine checks. |
520 | * Must be called with preempt off. | 529 | * Must be called with preempt off. |
@@ -529,6 +538,7 @@ void __cpuinit mcheck_init(struct cpuinfo_x86 *c) | |||
529 | 538 | ||
530 | mce_init(NULL); | 539 | mce_init(NULL); |
531 | mce_cpu_features(c); | 540 | mce_cpu_features(c); |
541 | mce_init_timer(); | ||
532 | } | 542 | } |
533 | 543 | ||
534 | /* | 544 | /* |
@@ -758,17 +768,19 @@ static int mce_resume(struct sys_device *dev) | |||
758 | return 0; | 768 | return 0; |
759 | } | 769 | } |
760 | 770 | ||
771 | static void mce_cpu_restart(void *data) | ||
772 | { | ||
773 | del_timer_sync(&__get_cpu_var(mce_timer)); | ||
774 | if (mce_available(¤t_cpu_data)) | ||
775 | mce_init(NULL); | ||
776 | mce_init_timer(); | ||
777 | } | ||
778 | |||
761 | /* Reinit MCEs after user configuration changes */ | 779 | /* Reinit MCEs after user configuration changes */ |
762 | static void mce_restart(void) | 780 | static void mce_restart(void) |
763 | { | 781 | { |
764 | if (next_interval) | ||
765 | cancel_delayed_work(&mcheck_work); | ||
766 | /* Timer race is harmless here */ | ||
767 | on_each_cpu(mce_init, NULL, 1); | ||
768 | next_interval = check_interval * HZ; | 782 | next_interval = check_interval * HZ; |
769 | if (next_interval) | 783 | on_each_cpu(mce_cpu_restart, NULL, 1); |
770 | schedule_delayed_work(&mcheck_work, | ||
771 | round_jiffies_relative(next_interval)); | ||
772 | } | 784 | } |
773 | 785 | ||
774 | static struct sysdev_class mce_sysclass = { | 786 | static struct sysdev_class mce_sysclass = { |
@@ -899,6 +911,7 @@ static int __cpuinit mce_cpu_callback(struct notifier_block *nfb, | |||
899 | unsigned long action, void *hcpu) | 911 | unsigned long action, void *hcpu) |
900 | { | 912 | { |
901 | unsigned int cpu = (unsigned long)hcpu; | 913 | unsigned int cpu = (unsigned long)hcpu; |
914 | struct timer_list *t = &per_cpu(mce_timer, cpu); | ||
902 | 915 | ||
903 | switch (action) { | 916 | switch (action) { |
904 | case CPU_ONLINE: | 917 | case CPU_ONLINE: |
@@ -913,6 +926,15 @@ static int __cpuinit mce_cpu_callback(struct notifier_block *nfb, | |||
913 | threshold_cpu_callback(action, cpu); | 926 | threshold_cpu_callback(action, cpu); |
914 | mce_remove_device(cpu); | 927 | mce_remove_device(cpu); |
915 | break; | 928 | break; |
929 | case CPU_DOWN_PREPARE: | ||
930 | case CPU_DOWN_PREPARE_FROZEN: | ||
931 | del_timer_sync(t); | ||
932 | break; | ||
933 | case CPU_DOWN_FAILED: | ||
934 | case CPU_DOWN_FAILED_FROZEN: | ||
935 | t->expires = round_jiffies_relative(jiffies + next_interval); | ||
936 | add_timer_on(t, cpu); | ||
937 | break; | ||
916 | } | 938 | } |
917 | return NOTIFY_OK; | 939 | return NOTIFY_OK; |
918 | } | 940 | } |