x86, mce: check early in exception handler if panic is needed

The exception handler should behave differently if the exception is
fatal versus one that can be returned from.  In the first case it should
never clear any registers because these need to be preserved
for logging after the next boot. Otherwise it should clear them
on each CPU step by step so that other CPUs sharing the same bank don't
see duplicate events. Otherwise we risk reporting events multiple
times on any CPUs which have shared machine check banks, which
is a common problem on Intel Nehalem which has both SMT (two
CPU threads sharing banks) and shared machine check banks in the uncore.

Determine early in a special pass if any event requires a panic.
This uses the mce_severity() function added earlier.

This is needed for the next patch.

Also fixes a problem together with an earlier patch
that corrected events weren't logged on a fatal MCE.

[ Impact: Feature ]

Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
Andi Kleen 2009-05-27 21:56:55 +02:00 committed by H. Peter Anvin
parent 817f32d02a
commit bd19a5e6b7
1 changed files with 25 additions and 13 deletions

View File

@ -36,6 +36,7 @@
#include <asm/mce.h> #include <asm/mce.h>
#include <asm/msr.h> #include <asm/msr.h>
#include "mce-internal.h"
#include "mce.h" #include "mce.h"
/* Handle unconfigured int18 (should never happen) */ /* Handle unconfigured int18 (should never happen) */
@ -191,7 +192,7 @@ static void print_mce(struct mce *m)
"and contact your hardware vendor\n"); "and contact your hardware vendor\n");
} }
static void mce_panic(char *msg, struct mce *final) static void mce_panic(char *msg, struct mce *final, char *exp)
{ {
int i; int i;
@ -214,6 +215,8 @@ static void mce_panic(char *msg, struct mce *final)
} }
if (final) if (final)
print_mce(final); print_mce(final);
if (exp)
printk(KERN_EMERG "Machine check: %s\n", exp);
panic(msg); panic(msg);
} }
@ -357,6 +360,22 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
} }
EXPORT_SYMBOL_GPL(machine_check_poll); EXPORT_SYMBOL_GPL(machine_check_poll);
/*
* Do a quick check if any of the events requires a panic.
* This decides if we keep the events around or clear them.
*/
static int mce_no_way_out(struct mce *m, char **msg)
{
int i;
for (i = 0; i < banks; i++) {
m->status = mce_rdmsrl(MSR_IA32_MC0_STATUS + i*4);
if (mce_severity(m, tolerant, msg) >= MCE_PANIC_SEVERITY)
return 1;
}
return 0;
}
/* /*
* 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.
@ -381,6 +400,7 @@ void do_machine_check(struct pt_regs *regs, long error_code)
*/ */
int kill_it = 0; int kill_it = 0;
DECLARE_BITMAP(toclear, MAX_NR_BANKS); DECLARE_BITMAP(toclear, MAX_NR_BANKS);
char *msg = "Unknown";
atomic_inc(&mce_entry); atomic_inc(&mce_entry);
@ -395,10 +415,7 @@ void do_machine_check(struct pt_regs *regs, long error_code)
mce_setup(&m); mce_setup(&m);
m.mcgstatus = mce_rdmsrl(MSR_IA32_MCG_STATUS); m.mcgstatus = mce_rdmsrl(MSR_IA32_MCG_STATUS);
no_way_out = mce_no_way_out(&m, &msg);
/* if the restart IP is not valid, we're done for */
if (!(m.mcgstatus & MCG_STATUS_RIPV))
no_way_out = 1;
barrier(); barrier();
@ -430,18 +447,13 @@ void do_machine_check(struct pt_regs *regs, long error_code)
__set_bit(i, toclear); __set_bit(i, toclear);
if (m.status & MCI_STATUS_EN) { if (m.status & MCI_STATUS_EN) {
/* if PCC was set, there's no way out */
no_way_out |= !!(m.status & MCI_STATUS_PCC);
/* /*
* If this error was uncorrectable and there was * If this error was uncorrectable and there was
* an overflow, we're in trouble. If no overflow, * an overflow, we're in trouble. If no overflow,
* we might get away with just killing a task. * we might get away with just killing a task.
*/ */
if (m.status & MCI_STATUS_UC) { if (m.status & MCI_STATUS_UC)
if (tolerant < 1 || m.status & MCI_STATUS_OVER)
no_way_out = 1;
kill_it = 1; kill_it = 1;
}
} else { } else {
/* /*
* Machine check event was not enabled. Clear, but * Machine check event was not enabled. Clear, but
@ -483,7 +495,7 @@ void do_machine_check(struct pt_regs *regs, long error_code)
* has not set tolerant to an insane level, give up and die. * has not set tolerant to an insane level, give up and die.
*/ */
if (no_way_out && tolerant < 3) if (no_way_out && tolerant < 3)
mce_panic("Machine check", &panicm); mce_panic("Machine check", &panicm, msg);
/* /*
* If the error seems to be unrecoverable, something should be * If the error seems to be unrecoverable, something should be
@ -511,7 +523,7 @@ void do_machine_check(struct pt_regs *regs, long error_code)
if (user_space) { if (user_space) {
force_sig(SIGBUS, current); force_sig(SIGBUS, current);
} else if (panic_on_oops || tolerant < 2) { } else if (panic_on_oops || tolerant < 2) {
mce_panic("Uncorrected machine check", &panicm); mce_panic("Uncorrected machine check", &panicm, msg);
} }
} }