Commit 5567d11c authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Thomas Gleixner

x86/mce: Send #MC singal from task work

Convert #MC over to using task_work_add(); it will run the same code
slightly later, on the return to user path of the same exception.
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarFrederic Weisbecker <frederic@kernel.org>
Reviewed-by: default avatarAlexandre Chartre <alexandre.chartre@oracle.com>
Link: https://lkml.kernel.org/r/20200505134100.957390899@linutronix.de
parent b052df3d
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/jump_label.h> #include <linux/jump_label.h>
#include <linux/set_memory.h> #include <linux/set_memory.h>
#include <linux/task_work.h>
#include <asm/intel-family.h> #include <asm/intel-family.h>
#include <asm/processor.h> #include <asm/processor.h>
...@@ -1086,23 +1087,6 @@ static void mce_clear_state(unsigned long *toclear) ...@@ -1086,23 +1087,6 @@ static void mce_clear_state(unsigned long *toclear)
} }
} }
static int do_memory_failure(struct mce *m)
{
int flags = MF_ACTION_REQUIRED;
int ret;
pr_err("Uncorrected hardware memory error in user-access at %llx", m->addr);
if (!(m->mcgstatus & MCG_STATUS_RIPV))
flags |= MF_MUST_KILL;
ret = memory_failure(m->addr >> PAGE_SHIFT, flags);
if (ret)
pr_err("Memory error not recovered");
else
set_mce_nospec(m->addr >> PAGE_SHIFT);
return ret;
}
/* /*
* Cases where we avoid rendezvous handler timeout: * Cases where we avoid rendezvous handler timeout:
* 1) If this CPU is offline. * 1) If this CPU is offline.
...@@ -1204,6 +1188,29 @@ static void __mc_scan_banks(struct mce *m, struct mce *final, ...@@ -1204,6 +1188,29 @@ static void __mc_scan_banks(struct mce *m, struct mce *final,
*m = *final; *m = *final;
} }
static void kill_me_now(struct callback_head *ch)
{
force_sig(SIGBUS);
}
static void kill_me_maybe(struct callback_head *cb)
{
struct task_struct *p = container_of(cb, struct task_struct, mce_kill_me);
int flags = MF_ACTION_REQUIRED;
pr_err("Uncorrected hardware memory error in user-access at %llx", p->mce_addr);
if (!(p->mce_status & MCG_STATUS_RIPV))
flags |= MF_MUST_KILL;
if (!memory_failure(p->mce_addr >> PAGE_SHIFT, flags)) {
set_mce_nospec(p->mce_addr >> PAGE_SHIFT);
return;
}
pr_err("Memory error not recovered");
kill_me_now(cb);
}
/* /*
* The actual machine check handler. This only handles real * The actual machine check handler. This only handles real
* exceptions when something got corrupted coming in through int 18. * exceptions when something got corrupted coming in through int 18.
...@@ -1222,7 +1229,7 @@ static void __mc_scan_banks(struct mce *m, struct mce *final, ...@@ -1222,7 +1229,7 @@ static void __mc_scan_banks(struct mce *m, struct mce *final,
* backing the user stack, tracing that reads the user stack will cause * backing the user stack, tracing that reads the user stack will cause
* potentially infinite recursion. * potentially infinite recursion.
*/ */
void notrace do_machine_check(struct pt_regs *regs, long error_code) void noinstr do_machine_check(struct pt_regs *regs, long error_code)
{ {
DECLARE_BITMAP(valid_banks, MAX_NR_BANKS); DECLARE_BITMAP(valid_banks, MAX_NR_BANKS);
DECLARE_BITMAP(toclear, MAX_NR_BANKS); DECLARE_BITMAP(toclear, MAX_NR_BANKS);
...@@ -1354,13 +1361,13 @@ void notrace do_machine_check(struct pt_regs *regs, long error_code) ...@@ -1354,13 +1361,13 @@ void notrace do_machine_check(struct pt_regs *regs, long error_code)
if ((m.cs & 3) == 3) { if ((m.cs & 3) == 3) {
/* If this triggers there is no way to recover. Die hard. */ /* If this triggers there is no way to recover. Die hard. */
BUG_ON(!on_thread_stack() || !user_mode(regs)); BUG_ON(!on_thread_stack() || !user_mode(regs));
local_irq_enable();
preempt_enable();
if (kill_it || do_memory_failure(&m)) current->mce_addr = m.addr;
force_sig(SIGBUS); current->mce_status = m.mcgstatus;
preempt_disable(); current->mce_kill_me.func = kill_me_maybe;
local_irq_disable(); if (kill_it)
current->mce_kill_me.func = kill_me_now;
task_work_add(current, &current->mce_kill_me, true);
} else { } else {
if (!fixup_exception(regs, X86_TRAP_MC, error_code, 0)) if (!fixup_exception(regs, X86_TRAP_MC, error_code, 0))
mce_panic("Failed kernel mode recovery", &m, msg); mce_panic("Failed kernel mode recovery", &m, msg);
...@@ -1370,7 +1377,6 @@ void notrace do_machine_check(struct pt_regs *regs, long error_code) ...@@ -1370,7 +1377,6 @@ void notrace do_machine_check(struct pt_regs *regs, long error_code)
ist_exit(regs); ist_exit(regs);
} }
EXPORT_SYMBOL_GPL(do_machine_check); EXPORT_SYMBOL_GPL(do_machine_check);
NOKPROBE_SYMBOL(do_machine_check);
#ifndef CONFIG_MEMORY_FAILURE #ifndef CONFIG_MEMORY_FAILURE
int memory_failure(unsigned long pfn, int flags) int memory_failure(unsigned long pfn, int flags)
......
...@@ -1297,6 +1297,12 @@ struct task_struct { ...@@ -1297,6 +1297,12 @@ struct task_struct {
unsigned long prev_lowest_stack; unsigned long prev_lowest_stack;
#endif #endif
#ifdef CONFIG_X86_MCE
u64 mce_addr;
u64 mce_status;
struct callback_head mce_kill_me;
#endif
/* /*
* New fields for task_struct should be added above here, so that * New fields for task_struct should be added above here, so that
* they are included in the randomized portion of task_struct. * they are included in the randomized portion of task_struct.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment