x86, mce: check early in exception handler if panic is needed
[pandora-kernel.git] / arch / x86 / kernel / cpu / mcheck / mce.c
index 1d0aa9c..5031814 100644 (file)
@@ -36,6 +36,7 @@
 #include <asm/mce.h>
 #include <asm/msr.h>
 
+#include "mce-internal.h"
 #include "mce.h"
 
 /* Handle unconfigured int18 (should never happen) */
@@ -57,6 +58,8 @@ int                           mce_disabled;
 
 atomic_t mce_entry;
 
+DEFINE_PER_CPU(unsigned, mce_exception_count);
+
 /*
  * Tolerant levels:
  *   0: always panic on uncorrected errors, log corrected errors
@@ -92,8 +95,17 @@ static inline int skip_bank_init(int i)
 void mce_setup(struct mce *m)
 {
        memset(m, 0, sizeof(struct mce));
-       m->cpu = smp_processor_id();
+       m->cpu = m->extcpu = smp_processor_id();
        rdtscll(m->tsc);
+       /* We hope get_seconds stays lockless */
+       m->time = get_seconds();
+       m->cpuvendor = boot_cpu_data.x86_vendor;
+       m->cpuid = cpuid_eax(1);
+#ifdef CONFIG_SMP
+       m->socketid = cpu_data(m->extcpu).phys_proc_id;
+#endif
+       m->apicid = cpu_data(m->extcpu).initial_apicid;
+       rdmsrl(MSR_IA32_MCG_CAP, m->mcgcap);
 }
 
 DEFINE_PER_CPU(struct mce, injectm);
@@ -106,8 +118,9 @@ EXPORT_PER_CPU_SYMBOL_GPL(injectm);
  */
 
 static struct mce_log mcelog = {
-       MCE_LOG_SIGNATURE,
-       MCE_LOG_LEN,
+       .signature      = MCE_LOG_SIGNATURE,
+       .len            = MCE_LOG_LEN,
+       .recordlen      = sizeof(struct mce),
 };
 
 void mce_log(struct mce *mce)
@@ -146,6 +159,7 @@ void mce_log(struct mce *mce)
        mcelog.entry[entry].finished = 1;
        wmb();
 
+       mce->finished = 1;
        set_bit(0, &notify_user);
 }
 
@@ -155,7 +169,7 @@ static void print_mce(struct mce *m)
               KERN_EMERG "HARDWARE ERROR\n"
               KERN_EMERG
               "CPU %d: Machine Check Exception: %16Lx Bank %d: %016Lx\n",
-              m->cpu, m->mcgstatus, m->bank, m->status);
+              m->extcpu, m->mcgstatus, m->bank, m->status);
        if (m->ip) {
                printk(KERN_EMERG "RIP%s %02x:<%016Lx> ",
                       !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "",
@@ -170,28 +184,39 @@ static void print_mce(struct mce *m)
        if (m->misc)
                printk("MISC %llx ", m->misc);
        printk("\n");
+       printk(KERN_EMERG "PROCESSOR %u:%x TIME %llu SOCKET %u APIC %x\n",
+                       m->cpuvendor, m->cpuid, m->time, m->socketid,
+                       m->apicid);
        printk(KERN_EMERG "This is not a software problem!\n");
        printk(KERN_EMERG "Run through mcelog --ascii to decode "
               "and contact your hardware vendor\n");
 }
 
-static void mce_panic(char *msg, struct mce *backup, u64 start)
+static void mce_panic(char *msg, struct mce *final, char *exp)
 {
        int i;
 
        bust_spinlocks(1);
        console_verbose();
+       /* First print corrected ones that are still unlogged */
        for (i = 0; i < MCE_LOG_LEN; i++) {
-               u64 tsc = mcelog.entry[i].tsc;
-
-               if ((s64)(tsc - start) < 0)
+               struct mce *m = &mcelog.entry[i];
+               if ((m->status & MCI_STATUS_VAL) &&
+                       !(m->status & MCI_STATUS_UC))
+                       print_mce(m);
+       }
+       /* Now print uncorrected but with the final one last */
+       for (i = 0; i < MCE_LOG_LEN; i++) {
+               struct mce *m = &mcelog.entry[i];
+               if (!(m->status & MCI_STATUS_VAL))
                        continue;
-               print_mce(&mcelog.entry[i]);
-               if (backup && mcelog.entry[i].tsc == backup->tsc)
-                       backup = NULL;
+               if (!final || memcmp(m, final, sizeof(struct mce)))
+                       print_mce(m);
        }
-       if (backup)
-               print_mce(backup);
+       if (final)
+               print_mce(final);
+       if (exp)
+               printk(KERN_EMERG "Machine check: %s\n", exp);
        panic(msg);
 }
 
@@ -262,6 +287,8 @@ static inline void mce_get_rip(struct mce *m, struct pt_regs *regs)
        }
 }
 
+DEFINE_PER_CPU(unsigned, mce_poll_count);
+
 /*
  * Poll for corrected events or events that happened before reset.
  * Those are just logged through /dev/mcelog.
@@ -273,6 +300,8 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
        struct mce m;
        int i;
 
+       __get_cpu_var(mce_poll_count)++;
+
        mce_setup(&m);
 
        m.mcgstatus = mce_rdmsrl(MSR_IA32_MCG_STATUS);
@@ -331,6 +360,22 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
 }
 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
  * exceptions when something got corrupted coming in through int 18.
@@ -343,7 +388,6 @@ void do_machine_check(struct pt_regs *regs, long error_code)
 {
        struct mce m, panicm;
        int panicm_found = 0;
-       u64 mcestart = 0;
        int i;
        /*
         * If no_way_out gets set, there is no safe way to recover from this
@@ -356,9 +400,12 @@ void do_machine_check(struct pt_regs *regs, long error_code)
         */
        int kill_it = 0;
        DECLARE_BITMAP(toclear, MAX_NR_BANKS);
+       char *msg = "Unknown";
 
        atomic_inc(&mce_entry);
 
+       __get_cpu_var(mce_exception_count)++;
+
        if (notify_die(DIE_NMI, "machine check", regs, error_code,
                           18, SIGKILL) == NOTIFY_STOP)
                goto out;
@@ -368,12 +415,8 @@ void do_machine_check(struct pt_regs *regs, long error_code)
        mce_setup(&m);
 
        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;
-
-       rdtscll(mcestart);
        barrier();
 
        for (i = 0; i < banks; i++) {
@@ -391,9 +434,9 @@ void do_machine_check(struct pt_regs *regs, long error_code)
 
                /*
                 * Non uncorrected errors are handled by machine_check_poll
-                * Leave them alone.
+                * Leave them alone, unless this panics.
                 */
-               if ((m.status & MCI_STATUS_UC) == 0)
+               if ((m.status & MCI_STATUS_UC) == 0 && !no_way_out)
                        continue;
 
                /*
@@ -404,18 +447,13 @@ void do_machine_check(struct pt_regs *regs, long error_code)
                __set_bit(i, toclear);
 
                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
                         * an overflow, we're in trouble.  If no overflow,
                         * we might get away with just killing a task.
                         */
-                       if (m.status & MCI_STATUS_UC) {
-                               if (tolerant < 1 || m.status & MCI_STATUS_OVER)
-                                       no_way_out = 1;
+                       if (m.status & MCI_STATUS_UC)
                                kill_it = 1;
-                       }
                } else {
                        /*
                         * Machine check event was not enabled. Clear, but
@@ -457,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.
         */
        if (no_way_out && tolerant < 3)
-               mce_panic("Machine check", &panicm, mcestart);
+               mce_panic("Machine check", &panicm, msg);
 
        /*
         * If the error seems to be unrecoverable, something should be
@@ -485,8 +523,7 @@ void do_machine_check(struct pt_regs *regs, long error_code)
                if (user_space) {
                        force_sig(SIGBUS, current);
                } else if (panic_on_oops || tolerant < 2) {
-                       mce_panic("Uncorrected machine check",
-                               &panicm, mcestart);
+                       mce_panic("Uncorrected machine check", &panicm, msg);
                }
        }