Merge branch 'next-general' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[sfrench/cifs-2.6.git] / arch / x86 / kernel / cpu / mce / core.c
index b7fb541a4873f7803b216b7987fa66517bc5fff6..5112a50e6486fdd4c96083375e6e919591bab90b 100644 (file)
@@ -460,23 +460,6 @@ static void mce_irq_work_cb(struct irq_work *entry)
        mce_schedule_work();
 }
 
-static void mce_report_event(struct pt_regs *regs)
-{
-       if (regs->flags & (X86_VM_MASK|X86_EFLAGS_IF)) {
-               mce_notify_irq();
-               /*
-                * Triggering the work queue here is just an insurance
-                * policy in case the syscall exit notify handler
-                * doesn't run soon enough or ends up running on the
-                * wrong CPU (can happen when audit sleeps)
-                */
-               mce_schedule_work();
-               return;
-       }
-
-       irq_work_queue(&mce_irq_work);
-}
-
 /*
  * Check if the address reported by the CPU is in a format we can parse.
  * It would be possible to add code for most other cases, but all would
@@ -712,19 +695,49 @@ bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
 
                barrier();
                m.status = mce_rdmsrl(msr_ops.status(i));
+
+               /* If this entry is not valid, ignore it */
                if (!(m.status & MCI_STATUS_VAL))
                        continue;
 
                /*
-                * Uncorrected or signalled events are handled by the exception
-                * handler when it is enabled, so don't process those here.
-                *
-                * TBD do the same check for MCI_STATUS_EN here?
+                * If we are logging everything (at CPU online) or this
+                * is a corrected error, then we must log it.
                 */
-               if (!(flags & MCP_UC) &&
-                   (m.status & (mca_cfg.ser ? MCI_STATUS_S : MCI_STATUS_UC)))
-                       continue;
+               if ((flags & MCP_UC) || !(m.status & MCI_STATUS_UC))
+                       goto log_it;
+
+               /*
+                * Newer Intel systems that support software error
+                * recovery need to make additional checks. Other
+                * CPUs should skip over uncorrected errors, but log
+                * everything else.
+                */
+               if (!mca_cfg.ser) {
+                       if (m.status & MCI_STATUS_UC)
+                               continue;
+                       goto log_it;
+               }
 
+               /* Log "not enabled" (speculative) errors */
+               if (!(m.status & MCI_STATUS_EN))
+                       goto log_it;
+
+               /*
+                * Log UCNA (SDM: 15.6.3 "UCR Error Classification")
+                * UC == 1 && PCC == 0 && S == 0
+                */
+               if (!(m.status & MCI_STATUS_PCC) && !(m.status & MCI_STATUS_S))
+                       goto log_it;
+
+               /*
+                * Skip anything else. Presumption is that our read of this
+                * bank is racing with a machine check. Leave the log alone
+                * for do_machine_check() to deal with it.
+                */
+               continue;
+
+log_it:
                error_seen = true;
 
                mce_read_aux(&m, i);
@@ -1301,7 +1314,8 @@ void do_machine_check(struct pt_regs *regs, long error_code)
                mce_panic("Fatal machine check on current CPU", &m, msg);
 
        if (worst > 0)
-               mce_report_event(regs);
+               irq_work_queue(&mce_irq_work);
+
        mce_wrmsrl(MSR_IA32_MCG_STATUS, 0);
 
        sync_core();
@@ -1451,13 +1465,12 @@ EXPORT_SYMBOL_GPL(mce_notify_irq);
 static int __mcheck_cpu_mce_banks_init(void)
 {
        int i;
-       u8 num_banks = mca_cfg.banks;
 
-       mce_banks = kcalloc(num_banks, sizeof(struct mce_bank), GFP_KERNEL);
+       mce_banks = kcalloc(MAX_NR_BANKS, sizeof(struct mce_bank), GFP_KERNEL);
        if (!mce_banks)
                return -ENOMEM;
 
-       for (i = 0; i < num_banks; i++) {
+       for (i = 0; i < MAX_NR_BANKS; i++) {
                struct mce_bank *b = &mce_banks[i];
 
                b->ctl = -1ULL;
@@ -1471,28 +1484,19 @@ static int __mcheck_cpu_mce_banks_init(void)
  */
 static int __mcheck_cpu_cap_init(void)
 {
-       unsigned b;
        u64 cap;
+       u8 b;
 
        rdmsrl(MSR_IA32_MCG_CAP, cap);
 
        b = cap & MCG_BANKCNT_MASK;
-       if (!mca_cfg.banks)
-               pr_info("CPU supports %d MCE banks\n", b);
-
-       if (b > MAX_NR_BANKS) {
-               pr_warn("Using only %u machine check banks out of %u\n",
-                       MAX_NR_BANKS, b);
+       if (WARN_ON_ONCE(b > MAX_NR_BANKS))
                b = MAX_NR_BANKS;
-       }
 
-       /* Don't support asymmetric configurations today */
-       WARN_ON(mca_cfg.banks != 0 && b != mca_cfg.banks);
-       mca_cfg.banks = b;
+       mca_cfg.banks = max(mca_cfg.banks, b);
 
        if (!mce_banks) {
                int err = __mcheck_cpu_mce_banks_init();
-
                if (err)
                        return err;
        }
@@ -1771,6 +1775,14 @@ static void __mcheck_cpu_init_timer(void)
        mce_start_timer(t);
 }
 
+bool filter_mce(struct mce *m)
+{
+       if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+               return amd_filter_mce(m);
+
+       return false;
+}
+
 /* Handle unconfigured int18 (should never happen) */
 static void unexpected_machine_check(struct pt_regs *regs, long error_code)
 {
@@ -2425,8 +2437,8 @@ static int fake_panic_set(void *data, u64 val)
        return 0;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(fake_panic_fops, fake_panic_get,
-                       fake_panic_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fake_panic_fops, fake_panic_get, fake_panic_set,
+                        "%llu\n");
 
 static int __init mcheck_debugfs_init(void)
 {
@@ -2435,8 +2447,8 @@ static int __init mcheck_debugfs_init(void)
        dmce = mce_get_debugfs_dir();
        if (!dmce)
                return -ENOMEM;
-       ffake_panic = debugfs_create_file("fake_panic", 0444, dmce, NULL,
-                                         &fake_panic_fops);
+       ffake_panic = debugfs_create_file_unsafe("fake_panic", 0444, dmce,
+                                                NULL, &fake_panic_fops);
        if (!ffake_panic)
                return -ENOMEM;
 
@@ -2451,6 +2463,8 @@ EXPORT_SYMBOL_GPL(mcsafe_key);
 
 static int __init mcheck_late_init(void)
 {
+       pr_info("Using %d MCE banks\n", mca_cfg.banks);
+
        if (mca_cfg.recovery)
                static_branch_inc(&mcsafe_key);