diff options
-rw-r--r-- | arch/x86/include/asm/mce.h | 4 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/dev-mcelog.c | 47 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce-inject.c | 66 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce-internal.h | 6 |
4 files changed, 61 insertions, 62 deletions
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index 3f9a3d2a5209..181264989db5 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h | |||
@@ -285,10 +285,6 @@ int mce_notify_irq(void); | |||
285 | 285 | ||
286 | DECLARE_PER_CPU(struct mce, injectm); | 286 | DECLARE_PER_CPU(struct mce, injectm); |
287 | 287 | ||
288 | extern void register_mce_write_callback(ssize_t (*)(struct file *filp, | ||
289 | const char __user *ubuf, | ||
290 | size_t usize, loff_t *off)); | ||
291 | |||
292 | /* Disable CMCI/polling for MCA bank claimed by firmware */ | 288 | /* Disable CMCI/polling for MCA bank claimed by firmware */ |
293 | extern void mce_disable_bank(int bank); | 289 | extern void mce_disable_bank(int bank); |
294 | 290 | ||
diff --git a/arch/x86/kernel/cpu/mcheck/dev-mcelog.c b/arch/x86/kernel/cpu/mcheck/dev-mcelog.c index 9c632cb88546..a80427c30c93 100644 --- a/arch/x86/kernel/cpu/mcheck/dev-mcelog.c +++ b/arch/x86/kernel/cpu/mcheck/dev-mcelog.c | |||
@@ -17,6 +17,8 @@ | |||
17 | 17 | ||
18 | #include "mce-internal.h" | 18 | #include "mce-internal.h" |
19 | 19 | ||
20 | static BLOCKING_NOTIFIER_HEAD(mce_injector_chain); | ||
21 | |||
20 | static DEFINE_MUTEX(mce_chrdev_read_mutex); | 22 | static DEFINE_MUTEX(mce_chrdev_read_mutex); |
21 | 23 | ||
22 | static char mce_helper[128]; | 24 | static char mce_helper[128]; |
@@ -345,24 +347,49 @@ static long mce_chrdev_ioctl(struct file *f, unsigned int cmd, | |||
345 | } | 347 | } |
346 | } | 348 | } |
347 | 349 | ||
348 | static ssize_t (*mce_write)(struct file *filp, const char __user *ubuf, | 350 | void mce_register_injector_chain(struct notifier_block *nb) |
349 | size_t usize, loff_t *off); | 351 | { |
352 | blocking_notifier_chain_register(&mce_injector_chain, nb); | ||
353 | } | ||
354 | EXPORT_SYMBOL_GPL(mce_register_injector_chain); | ||
350 | 355 | ||
351 | void register_mce_write_callback(ssize_t (*fn)(struct file *filp, | 356 | void mce_unregister_injector_chain(struct notifier_block *nb) |
352 | const char __user *ubuf, | ||
353 | size_t usize, loff_t *off)) | ||
354 | { | 357 | { |
355 | mce_write = fn; | 358 | blocking_notifier_chain_unregister(&mce_injector_chain, nb); |
356 | } | 359 | } |
357 | EXPORT_SYMBOL_GPL(register_mce_write_callback); | 360 | EXPORT_SYMBOL_GPL(mce_unregister_injector_chain); |
358 | 361 | ||
359 | static ssize_t mce_chrdev_write(struct file *filp, const char __user *ubuf, | 362 | static ssize_t mce_chrdev_write(struct file *filp, const char __user *ubuf, |
360 | size_t usize, loff_t *off) | 363 | size_t usize, loff_t *off) |
361 | { | 364 | { |
362 | if (mce_write) | 365 | struct mce m; |
363 | return mce_write(filp, ubuf, usize, off); | 366 | |
364 | else | 367 | if (!capable(CAP_SYS_ADMIN)) |
368 | return -EPERM; | ||
369 | /* | ||
370 | * There are some cases where real MSR reads could slip | ||
371 | * through. | ||
372 | */ | ||
373 | if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA)) | ||
374 | return -EIO; | ||
375 | |||
376 | if ((unsigned long)usize > sizeof(struct mce)) | ||
377 | usize = sizeof(struct mce); | ||
378 | if (copy_from_user(&m, ubuf, usize)) | ||
379 | return -EFAULT; | ||
380 | |||
381 | if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu)) | ||
365 | return -EINVAL; | 382 | return -EINVAL; |
383 | |||
384 | /* | ||
385 | * Need to give user space some time to set everything up, | ||
386 | * so do it a jiffie or two later everywhere. | ||
387 | */ | ||
388 | schedule_timeout(2); | ||
389 | |||
390 | blocking_notifier_call_chain(&mce_injector_chain, 0, &m); | ||
391 | |||
392 | return usize; | ||
366 | } | 393 | } |
367 | 394 | ||
368 | static const struct file_operations mce_chrdev_ops = { | 395 | static const struct file_operations mce_chrdev_ops = { |
diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c index 7170186938e5..c21c1a73712a 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-inject.c +++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c | |||
@@ -283,42 +283,24 @@ static void __maybe_unused raise_mce(struct mce *m) | |||
283 | } | 283 | } |
284 | } | 284 | } |
285 | 285 | ||
286 | #ifdef CONFIG_X86_MCELOG_LEGACY | 286 | static int mce_inject_raise(struct notifier_block *nb, unsigned long val, |
287 | /* Error injection interface */ | 287 | void *data) |
288 | static ssize_t mce_write(struct file *filp, const char __user *ubuf, | ||
289 | size_t usize, loff_t *off) | ||
290 | { | 288 | { |
291 | struct mce m; | 289 | struct mce *m = (struct mce *)data; |
292 | |||
293 | if (!capable(CAP_SYS_ADMIN)) | ||
294 | return -EPERM; | ||
295 | /* | ||
296 | * There are some cases where real MSR reads could slip | ||
297 | * through. | ||
298 | */ | ||
299 | if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA)) | ||
300 | return -EIO; | ||
301 | |||
302 | if ((unsigned long)usize > sizeof(struct mce)) | ||
303 | usize = sizeof(struct mce); | ||
304 | if (copy_from_user(&m, ubuf, usize)) | ||
305 | return -EFAULT; | ||
306 | |||
307 | if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu)) | ||
308 | return -EINVAL; | ||
309 | 290 | ||
310 | /* | 291 | if (!m) |
311 | * Need to give user space some time to set everything up, | 292 | return NOTIFY_DONE; |
312 | * so do it a jiffie or two later everywhere. | ||
313 | */ | ||
314 | schedule_timeout(2); | ||
315 | 293 | ||
316 | mutex_lock(&mce_inject_mutex); | 294 | mutex_lock(&mce_inject_mutex); |
317 | raise_mce(&m); | 295 | raise_mce(m); |
318 | mutex_unlock(&mce_inject_mutex); | 296 | mutex_unlock(&mce_inject_mutex); |
319 | return usize; | 297 | |
298 | return NOTIFY_DONE; | ||
320 | } | 299 | } |
321 | #endif /* CONFIG_X86_MCELOG_LEGACY */ | 300 | |
301 | static struct notifier_block inject_nb = { | ||
302 | .notifier_call = mce_inject_raise, | ||
303 | }; | ||
322 | 304 | ||
323 | /* | 305 | /* |
324 | * Caller needs to be make sure this cpu doesn't disappear | 306 | * Caller needs to be make sure this cpu doesn't disappear |
@@ -719,44 +701,34 @@ static int __init inject_init(void) | |||
719 | if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL)) | 701 | if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL)) |
720 | return -ENOMEM; | 702 | return -ENOMEM; |
721 | 703 | ||
722 | #ifdef CONFIG_X86_MCELOG_LEGACY | ||
723 | register_mce_write_callback(mce_write); | ||
724 | #endif | ||
725 | |||
726 | register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify"); | ||
727 | |||
728 | err = debugfs_init(); | 704 | err = debugfs_init(); |
729 | if (err) { | 705 | if (err) { |
730 | free_cpumask_var(mce_inject_cpumask); | 706 | free_cpumask_var(mce_inject_cpumask); |
731 | return err; | 707 | return err; |
732 | } | 708 | } |
733 | 709 | ||
710 | register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify"); | ||
711 | mce_register_injector_chain(&inject_nb); | ||
712 | |||
734 | pr_info("Machine check injector initialized\n"); | 713 | pr_info("Machine check injector initialized\n"); |
735 | 714 | ||
736 | return 0; | 715 | return 0; |
737 | } | 716 | } |
738 | 717 | ||
739 | module_init(inject_init); | ||
740 | |||
741 | /* | ||
742 | * Cannot tolerate unloading currently because we cannot | ||
743 | * guarantee all openers of mce_chrdev will get a reference to us. | ||
744 | */ | ||
745 | #ifndef CONFIG_X86_MCELOG_LEGACY | ||
746 | static void __exit inject_exit(void) | 718 | static void __exit inject_exit(void) |
747 | { | 719 | { |
748 | 720 | ||
721 | mce_unregister_injector_chain(&inject_nb); | ||
722 | unregister_nmi_handler(NMI_LOCAL, "mce_notify"); | ||
723 | |||
749 | debugfs_remove_recursive(dfs_inj); | 724 | debugfs_remove_recursive(dfs_inj); |
750 | dfs_inj = NULL; | 725 | dfs_inj = NULL; |
751 | 726 | ||
752 | memset(&dfs_fls, 0, sizeof(dfs_fls)); | 727 | memset(&dfs_fls, 0, sizeof(dfs_fls)); |
753 | 728 | ||
754 | unregister_nmi_handler(NMI_LOCAL, "mce_notify"); | ||
755 | |||
756 | free_cpumask_var(mce_inject_cpumask); | 729 | free_cpumask_var(mce_inject_cpumask); |
757 | } | 730 | } |
758 | 731 | ||
732 | module_init(inject_init); | ||
759 | module_exit(inject_exit); | 733 | module_exit(inject_exit); |
760 | #endif | ||
761 | |||
762 | MODULE_LICENSE("GPL"); | 734 | MODULE_LICENSE("GPL"); |
diff --git a/arch/x86/kernel/cpu/mcheck/mce-internal.h b/arch/x86/kernel/cpu/mcheck/mce-internal.h index 654ad0668d72..098530a93bb7 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-internal.h +++ b/arch/x86/kernel/cpu/mcheck/mce-internal.h | |||
@@ -100,7 +100,11 @@ static inline bool mce_cmp(struct mce *m1, struct mce *m2) | |||
100 | extern struct device_attribute dev_attr_trigger; | 100 | extern struct device_attribute dev_attr_trigger; |
101 | 101 | ||
102 | #ifdef CONFIG_X86_MCELOG_LEGACY | 102 | #ifdef CONFIG_X86_MCELOG_LEGACY |
103 | extern void mce_work_trigger(void); | 103 | void mce_work_trigger(void); |
104 | void mce_register_injector_chain(struct notifier_block *nb); | ||
105 | void mce_unregister_injector_chain(struct notifier_block *nb); | ||
104 | #else | 106 | #else |
105 | static inline void mce_work_trigger(void) { } | 107 | static inline void mce_work_trigger(void) { } |
108 | static inline void mce_register_injector_chain(struct notifier_block *nb) { } | ||
109 | static inline void mce_unregister_injector_chain(struct notifier_block *nb) { } | ||
106 | #endif | 110 | #endif |