mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
x86/mce: Get rid of register_mce_write_callback()
Make the mcelog call a notifier which lands in the injector module and does the injection. This allows for mce-inject to be a normal kernel module now. Tested-by: Yazen Ghannam <yazen.ghannam@amd.com> Signed-off-by: Borislav Petkov <bp@suse.de> Acked-by: Yazen Ghannam <yazen.ghannam@amd.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Tony Luck <tony.luck@intel.com> Link: http://lkml.kernel.org/r/20170613162835.30750-5-bp@alien8.de Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
bc8e80d56c
commit
fbe9ff9eaf
4 changed files with 62 additions and 63 deletions
|
@ -285,10 +285,6 @@ int mce_notify_irq(void);
|
||||||
|
|
||||||
DECLARE_PER_CPU(struct mce, injectm);
|
DECLARE_PER_CPU(struct mce, injectm);
|
||||||
|
|
||||||
extern void register_mce_write_callback(ssize_t (*)(struct file *filp,
|
|
||||||
const char __user *ubuf,
|
|
||||||
size_t usize, loff_t *off));
|
|
||||||
|
|
||||||
/* Disable CMCI/polling for MCA bank claimed by firmware */
|
/* Disable CMCI/polling for MCA bank claimed by firmware */
|
||||||
extern void mce_disable_bank(int bank);
|
extern void mce_disable_bank(int bank);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include "mce-internal.h"
|
#include "mce-internal.h"
|
||||||
|
|
||||||
|
static BLOCKING_NOTIFIER_HEAD(mce_injector_chain);
|
||||||
|
|
||||||
static DEFINE_MUTEX(mce_chrdev_read_mutex);
|
static DEFINE_MUTEX(mce_chrdev_read_mutex);
|
||||||
|
|
||||||
static char mce_helper[128];
|
static char mce_helper[128];
|
||||||
|
@ -345,24 +347,49 @@ static long mce_chrdev_ioctl(struct file *f, unsigned int cmd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t (*mce_write)(struct file *filp, const char __user *ubuf,
|
void mce_register_injector_chain(struct notifier_block *nb)
|
||||||
size_t usize, loff_t *off);
|
|
||||||
|
|
||||||
void register_mce_write_callback(ssize_t (*fn)(struct file *filp,
|
|
||||||
const char __user *ubuf,
|
|
||||||
size_t usize, loff_t *off))
|
|
||||||
{
|
{
|
||||||
mce_write = fn;
|
blocking_notifier_chain_register(&mce_injector_chain, nb);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(register_mce_write_callback);
|
EXPORT_SYMBOL_GPL(mce_register_injector_chain);
|
||||||
|
|
||||||
|
void mce_unregister_injector_chain(struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
blocking_notifier_chain_unregister(&mce_injector_chain, nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(mce_unregister_injector_chain);
|
||||||
|
|
||||||
static ssize_t mce_chrdev_write(struct file *filp, const char __user *ubuf,
|
static ssize_t mce_chrdev_write(struct file *filp, const char __user *ubuf,
|
||||||
size_t usize, loff_t *off)
|
size_t usize, loff_t *off)
|
||||||
{
|
{
|
||||||
if (mce_write)
|
struct mce m;
|
||||||
return mce_write(filp, ubuf, usize, off);
|
|
||||||
else
|
if (!capable(CAP_SYS_ADMIN))
|
||||||
|
return -EPERM;
|
||||||
|
/*
|
||||||
|
* There are some cases where real MSR reads could slip
|
||||||
|
* through.
|
||||||
|
*/
|
||||||
|
if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if ((unsigned long)usize > sizeof(struct mce))
|
||||||
|
usize = sizeof(struct mce);
|
||||||
|
if (copy_from_user(&m, ubuf, usize))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Need to give user space some time to set everything up,
|
||||||
|
* so do it a jiffie or two later everywhere.
|
||||||
|
*/
|
||||||
|
schedule_timeout(2);
|
||||||
|
|
||||||
|
blocking_notifier_call_chain(&mce_injector_chain, 0, &m);
|
||||||
|
|
||||||
|
return usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct file_operations mce_chrdev_ops = {
|
static const struct file_operations mce_chrdev_ops = {
|
||||||
|
|
|
@ -283,42 +283,24 @@ static void __maybe_unused raise_mce(struct mce *m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_X86_MCELOG_LEGACY
|
static int mce_inject_raise(struct notifier_block *nb, unsigned long val,
|
||||||
/* Error injection interface */
|
void *data)
|
||||||
static ssize_t mce_write(struct file *filp, const char __user *ubuf,
|
|
||||||
size_t usize, loff_t *off)
|
|
||||||
{
|
{
|
||||||
struct mce m;
|
struct mce *m = (struct mce *)data;
|
||||||
|
|
||||||
if (!capable(CAP_SYS_ADMIN))
|
if (!m)
|
||||||
return -EPERM;
|
return NOTIFY_DONE;
|
||||||
/*
|
|
||||||
* There are some cases where real MSR reads could slip
|
|
||||||
* through.
|
|
||||||
*/
|
|
||||||
if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
if ((unsigned long)usize > sizeof(struct mce))
|
|
||||||
usize = sizeof(struct mce);
|
|
||||||
if (copy_from_user(&m, ubuf, usize))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Need to give user space some time to set everything up,
|
|
||||||
* so do it a jiffie or two later everywhere.
|
|
||||||
*/
|
|
||||||
schedule_timeout(2);
|
|
||||||
|
|
||||||
mutex_lock(&mce_inject_mutex);
|
mutex_lock(&mce_inject_mutex);
|
||||||
raise_mce(&m);
|
raise_mce(m);
|
||||||
mutex_unlock(&mce_inject_mutex);
|
mutex_unlock(&mce_inject_mutex);
|
||||||
return usize;
|
|
||||||
|
return NOTIFY_DONE;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_X86_MCELOG_LEGACY */
|
|
||||||
|
static struct notifier_block inject_nb = {
|
||||||
|
.notifier_call = mce_inject_raise,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Caller needs to be make sure this cpu doesn't disappear
|
* Caller needs to be make sure this cpu doesn't disappear
|
||||||
|
@ -719,44 +701,34 @@ static int __init inject_init(void)
|
||||||
if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
|
if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
#ifdef CONFIG_X86_MCELOG_LEGACY
|
|
||||||
register_mce_write_callback(mce_write);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
|
|
||||||
|
|
||||||
err = debugfs_init();
|
err = debugfs_init();
|
||||||
if (err) {
|
if (err) {
|
||||||
free_cpumask_var(mce_inject_cpumask);
|
free_cpumask_var(mce_inject_cpumask);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
|
||||||
|
mce_register_injector_chain(&inject_nb);
|
||||||
|
|
||||||
pr_info("Machine check injector initialized\n");
|
pr_info("Machine check injector initialized\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(inject_init);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Cannot tolerate unloading currently because we cannot
|
|
||||||
* guarantee all openers of mce_chrdev will get a reference to us.
|
|
||||||
*/
|
|
||||||
#ifndef CONFIG_X86_MCELOG_LEGACY
|
|
||||||
static void __exit inject_exit(void)
|
static void __exit inject_exit(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
mce_unregister_injector_chain(&inject_nb);
|
||||||
|
unregister_nmi_handler(NMI_LOCAL, "mce_notify");
|
||||||
|
|
||||||
debugfs_remove_recursive(dfs_inj);
|
debugfs_remove_recursive(dfs_inj);
|
||||||
dfs_inj = NULL;
|
dfs_inj = NULL;
|
||||||
|
|
||||||
memset(&dfs_fls, 0, sizeof(dfs_fls));
|
memset(&dfs_fls, 0, sizeof(dfs_fls));
|
||||||
|
|
||||||
unregister_nmi_handler(NMI_LOCAL, "mce_notify");
|
|
||||||
|
|
||||||
free_cpumask_var(mce_inject_cpumask);
|
free_cpumask_var(mce_inject_cpumask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module_init(inject_init);
|
||||||
module_exit(inject_exit);
|
module_exit(inject_exit);
|
||||||
#endif
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
|
@ -100,7 +100,11 @@ static inline bool mce_cmp(struct mce *m1, struct mce *m2)
|
||||||
extern struct device_attribute dev_attr_trigger;
|
extern struct device_attribute dev_attr_trigger;
|
||||||
|
|
||||||
#ifdef CONFIG_X86_MCELOG_LEGACY
|
#ifdef CONFIG_X86_MCELOG_LEGACY
|
||||||
extern void mce_work_trigger(void);
|
void mce_work_trigger(void);
|
||||||
|
void mce_register_injector_chain(struct notifier_block *nb);
|
||||||
|
void mce_unregister_injector_chain(struct notifier_block *nb);
|
||||||
#else
|
#else
|
||||||
static inline void mce_work_trigger(void) { }
|
static inline void mce_work_trigger(void) { }
|
||||||
|
static inline void mce_register_injector_chain(struct notifier_block *nb) { }
|
||||||
|
static inline void mce_unregister_injector_chain(struct notifier_block *nb) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue