]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
Merge tag 'ras_for_3.21' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras...
authorIngo Molnar <mingo@kernel.org>
Thu, 19 Feb 2015 12:18:02 +0000 (13:18 +0100)
committerIngo Molnar <mingo@kernel.org>
Thu, 19 Feb 2015 12:31:33 +0000 (13:31 +0100)
Pull RAS updates from Borislav Petkov:

 "- Enable AMD thresholding IRQ by default if supported. (Aravind Gopalakrishnan)

  - Unify mce_panic() message pattern. (Derek Che)

  - A bit more involved simplification of the CMCI logic after yet another
    report about race condition with the adaptive logic. (Borislav Petkov)

  - ACPI APEI EINJ fleshing out of the user documentation. (Borislav Petkov)

  - Minor cleanup. (Jan Beulich.)"

Signed-off-by: Ingo Molnar <mingo@kernel.org>
1  2 
arch/x86/include/asm/mce.h
arch/x86/kernel/cpu/mcheck/mce.c

index 9b3de99dc0044a8b6ccdba0e7523f5b1e8425c03,13eeea518233911ed84a05de1819915754525b81..fd38a23e729f3f080bb79e6c97c517f9382bce1e
@@@ -183,13 -183,14 +183,13 @@@ typedef DECLARE_BITMAP(mce_banks_t, MAX
  DECLARE_PER_CPU(mce_banks_t, mce_poll_banks);
  
  enum mcp_flags {
-       MCP_TIMESTAMP = (1 << 0),       /* log time stamp */
-       MCP_UC = (1 << 1),              /* log uncorrected errors */
-       MCP_DONTLOG = (1 << 2),         /* only clear, don't log */
+       MCP_TIMESTAMP   = BIT(0),       /* log time stamp */
+       MCP_UC          = BIT(1),       /* log uncorrected errors */
+       MCP_DONTLOG     = BIT(2),       /* only clear, don't log */
  };
void machine_check_poll(enum mcp_flags flags, mce_banks_t *b);
bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b);
  
  int mce_notify_irq(void);
 -void mce_notify_process(void);
  
  DECLARE_PER_CPU(struct mce, injectm);
  
index cdfed7953963fa31251c24c254f55a267951bf02,aeeb93378cdb0898bf604fb370f2f85b6333b19d..d760931a4546e677e4a9df41f34db9448704e26b
@@@ -43,7 -43,6 +43,7 @@@
  #include <linux/export.h>
  
  #include <asm/processor.h>
 +#include <asm/traps.h>
  #include <asm/mce.h>
  #include <asm/msr.h>
  
@@@ -59,7 -58,7 +59,7 @@@ static DEFINE_MUTEX(mce_chrdev_read_mut
  #define CREATE_TRACE_POINTS
  #include <trace/events/mce.h>
  
- #define SPINUNIT 100  /* 100ns */
+ #define SPINUNIT              100     /* 100ns */
  
  DEFINE_PER_CPU(unsigned, mce_exception_count);
  
@@@ -88,9 -87,6 +88,6 @@@ static DECLARE_WAIT_QUEUE_HEAD(mce_chrd
  static DEFINE_PER_CPU(struct mce, mces_seen);
  static int                    cpu_missing;
  
- /* CMCI storm detection filter */
- static DEFINE_PER_CPU(unsigned long, mce_polled_error);
  /*
   * MCA banks polled by the period polling timer for corrected events.
   * With Intel CMCI, this only has MCA banks which do not support CMCI (if any).
@@@ -116,7 -112,7 +113,7 @@@ static void (*quirk_no_way_out)(int ban
   * CPU/chipset specific EDAC code can register a notifier call here to print
   * MCE errors in a human-readable form.
   */
 -ATOMIC_NOTIFIER_HEAD(x86_mce_decoder_chain);
 +static ATOMIC_NOTIFIER_HEAD(x86_mce_decoder_chain);
  
  /* Do initial initialization of a struct mce */
  void mce_setup(struct mce *m)
@@@ -312,7 -308,7 +309,7 @@@ static void wait_for_panic(void
        panic("Panicing machine check CPU died");
  }
  
 -static void mce_panic(char *msg, struct mce *final, char *exp)
 +static void mce_panic(const char *msg, struct mce *final, char *exp)
  {
        int i, apei_err = 0;
  
@@@ -530,7 -526,7 +527,7 @@@ static void mce_schedule_work(void
                schedule_work(this_cpu_ptr(&mce_work));
  }
  
 -DEFINE_PER_CPU(struct irq_work, mce_irq_work);
 +static DEFINE_PER_CPU(struct irq_work, mce_irq_work);
  
  static void mce_irq_work_cb(struct irq_work *entry)
  {
@@@ -624,8 -620,9 +621,9 @@@ DEFINE_PER_CPU(unsigned, mce_poll_count
   * is already totally * confused. In this case it's likely it will
   * not fully execute the machine check handler either.
   */
void machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
  {
+       bool error_logged = false;
        struct mce m;
        int severity;
        int i;
                if (!(m.status & MCI_STATUS_VAL))
                        continue;
  
-               this_cpu_write(mce_polled_error, 1);
                /*
                 * Uncorrected or signalled events are handled by the exception
                 * handler when it is enabled, so don't process those here.
                 * Don't get the IP here because it's unlikely to
                 * have anything to do with the actual error location.
                 */
-               if (!(flags & MCP_DONTLOG) && !mca_cfg.dont_log_ce)
+               if (!(flags & MCP_DONTLOG) && !mca_cfg.dont_log_ce) {
+                       error_logged = true;
                        mce_log(&m);
+               }
  
                /*
                 * Clear state for this bank.
         */
  
        sync_core();
+       return error_logged;
  }
  EXPORT_SYMBOL_GPL(machine_check_poll);
  
@@@ -736,7 -737,7 +738,7 @@@ static atomic_t mce_callin
  /*
   * Check if a timeout waiting for other CPUs happened.
   */
 -static int mce_timed_out(u64 *t)
 +static int mce_timed_out(u64 *t, const char *msg)
  {
        /*
         * The others already did panic for some reason.
                goto out;
        if ((s64)*t < SPINUNIT) {
                if (mca_cfg.tolerant <= 1)
 -                      mce_panic("Timeout synchronizing machine check over CPUs",
 -                                NULL, NULL);
 +                      mce_panic(msg, NULL, NULL);
                cpu_missing = 1;
                return 1;
        }
@@@ -815,7 -817,7 +817,7 @@@ static void mce_reign(void
         * other CPUs.
         */
        if (m && global_worst >= MCE_PANIC_SEVERITY && mca_cfg.tolerant < 3)
-               mce_panic("Fatal Machine check", m, msg);
+               mce_panic("Fatal machine check", m, msg);
  
        /*
         * For UC somewhere we let the CPU who detects it handle it.
         * source or one CPU is hung. Panic.
         */
        if (global_worst <= MCE_KEEP_SEVERITY && mca_cfg.tolerant < 3)
-               mce_panic("Machine check from unknown source", NULL, NULL);
+               mce_panic("Fatal machine check from unknown source", NULL, NULL);
  
        /*
         * Now clear all the mces_seen so that they don't reappear on
@@@ -867,8 -869,7 +869,8 @@@ static int mce_start(int *no_way_out
         * Wait for everyone.
         */
        while (atomic_read(&mce_callin) != cpus) {
 -              if (mce_timed_out(&timeout)) {
 +              if (mce_timed_out(&timeout,
 +                                "Timeout: Not all CPUs entered broadcast exception handler")) {
                        atomic_set(&global_nwo, 0);
                        return -1;
                }
                 * only seen by one CPU before cleared, avoiding duplicates.
                 */
                while (atomic_read(&mce_executing) < order) {
 -                      if (mce_timed_out(&timeout)) {
 +                      if (mce_timed_out(&timeout,
 +                                        "Timeout: Subject CPUs unable to finish machine check processing")) {
                                atomic_set(&global_nwo, 0);
                                return -1;
                        }
@@@ -938,8 -938,7 +940,8 @@@ static int mce_end(int order
                 * loops.
                 */
                while (atomic_read(&mce_executing) <= cpus) {
 -                      if (mce_timed_out(&timeout))
 +                      if (mce_timed_out(&timeout,
 +                                        "Timeout: Monarch CPU unable to finish machine check processing"))
                                goto reset;
                        ndelay(SPINUNIT);
                }
                 * Subject: Wait for Monarch to finish.
                 */
                while (atomic_read(&mce_executing) != 0) {
 -                      if (mce_timed_out(&timeout))
 +                      if (mce_timed_out(&timeout,
 +                                        "Timeout: Monarch CPU did not finish machine check processing"))
                                goto reset;
                        ndelay(SPINUNIT);
                }
@@@ -1006,6 -1004,51 +1008,6 @@@ static void mce_clear_state(unsigned lo
        }
  }
  
 -/*
 - * Need to save faulting physical address associated with a process
 - * in the machine check handler some place where we can grab it back
 - * later in mce_notify_process()
 - */
 -#define       MCE_INFO_MAX    16
 -
 -struct mce_info {
 -      atomic_t                inuse;
 -      struct task_struct      *t;
 -      __u64                   paddr;
 -      int                     restartable;
 -} mce_info[MCE_INFO_MAX];
 -
 -static void mce_save_info(__u64 addr, int c)
 -{
 -      struct mce_info *mi;
 -
 -      for (mi = mce_info; mi < &mce_info[MCE_INFO_MAX]; mi++) {
 -              if (atomic_cmpxchg(&mi->inuse, 0, 1) == 0) {
 -                      mi->t = current;
 -                      mi->paddr = addr;
 -                      mi->restartable = c;
 -                      return;
 -              }
 -      }
 -
 -      mce_panic("Too many concurrent recoverable errors", NULL, NULL);
 -}
 -
 -static struct mce_info *mce_find_info(void)
 -{
 -      struct mce_info *mi;
 -
 -      for (mi = mce_info; mi < &mce_info[MCE_INFO_MAX]; mi++)
 -              if (atomic_read(&mi->inuse) && mi->t == current)
 -                      return mi;
 -      return NULL;
 -}
 -
 -static void mce_clear_info(struct mce_info *mi)
 -{
 -      atomic_set(&mi->inuse, 0);
 -}
 -
  /*
   * The actual machine check handler. This only handles real
   * exceptions when something got corrupted coming in through int 18.
@@@ -1022,7 -1065,6 +1024,7 @@@ void do_machine_check(struct pt_regs *r
  {
        struct mca_config *cfg = &mca_cfg;
        struct mce m, *final;
 +      enum ctx_state prev_state;
        int i;
        int worst = 0;
        int severity;
        DECLARE_BITMAP(toclear, MAX_NR_BANKS);
        DECLARE_BITMAP(valid_banks, MAX_NR_BANKS);
        char *msg = "Unknown";
 +      u64 recover_paddr = ~0ull;
 +      int flags = MF_ACTION_REQUIRED;
 +
 +      prev_state = ist_enter(regs);
  
        this_cpu_inc(mce_exception_count);
  
                if (no_way_out)
                        mce_panic("Fatal machine check on current CPU", &m, msg);
                if (worst == MCE_AR_SEVERITY) {
 -                      /* schedule action before return to userland */
 -                      mce_save_info(m.addr, m.mcgstatus & MCG_STATUS_RIPV);
 -                      set_thread_flag(TIF_MCE_NOTIFY);
 +                      recover_paddr = m.addr;
 +                      if (!(m.mcgstatus & MCG_STATUS_RIPV))
 +                              flags |= MF_MUST_KILL;
                } else if (kill_it) {
                        force_sig(SIGBUS, current);
                }
        mce_wrmsrl(MSR_IA32_MCG_STATUS, 0);
  out:
        sync_core();
 +
 +      if (recover_paddr == ~0ull)
 +              goto done;
 +
 +      pr_err("Uncorrected hardware memory error in user-access at %llx",
 +               recover_paddr);
 +      /*
 +       * We must call memory_failure() here even if the current process is
 +       * doomed. We still need to mark the page as poisoned and alert any
 +       * other users of the page.
 +       */
 +      ist_begin_non_atomic(regs);
 +      local_irq_enable();
 +      if (memory_failure(recover_paddr >> PAGE_SHIFT, MCE_VECTOR, flags) < 0) {
 +              pr_err("Memory error not recovered");
 +              force_sig(SIGBUS, current);
 +      }
 +      local_irq_disable();
 +      ist_end_non_atomic();
 +done:
 +      ist_exit(regs, prev_state);
  }
  EXPORT_SYMBOL_GPL(do_machine_check);
  
@@@ -1217,6 -1234,42 +1219,6 @@@ int memory_failure(unsigned long pfn, i
  }
  #endif
  
 -/*
 - * Called in process context that interrupted by MCE and marked with
 - * TIF_MCE_NOTIFY, just before returning to erroneous userland.
 - * This code is allowed to sleep.
 - * Attempt possible recovery such as calling the high level VM handler to
 - * process any corrupted pages, and kill/signal current process if required.
 - * Action required errors are handled here.
 - */
 -void mce_notify_process(void)
 -{
 -      unsigned long pfn;
 -      struct mce_info *mi = mce_find_info();
 -      int flags = MF_ACTION_REQUIRED;
 -
 -      if (!mi)
 -              mce_panic("Lost physical address for unconsumed uncorrectable error", NULL, NULL);
 -      pfn = mi->paddr >> PAGE_SHIFT;
 -
 -      clear_thread_flag(TIF_MCE_NOTIFY);
 -
 -      pr_err("Uncorrected hardware memory error in user-access at %llx",
 -               mi->paddr);
 -      /*
 -       * We must call memory_failure() here even if the current process is
 -       * doomed. We still need to mark the page as poisoned and alert any
 -       * other users of the page.
 -       */
 -      if (!mi->restartable)
 -              flags |= MF_MUST_KILL;
 -      if (memory_failure(pfn, MCE_VECTOR, flags) < 0) {
 -              pr_err("Memory error not recovered");
 -              force_sig(SIGBUS, current);
 -      }
 -      mce_clear_info(mi);
 -}
 -
  /*
   * Action optional processing happens here (picking up
   * from the list of faulting pages that do_machine_check()
@@@ -1260,7 -1313,7 +1262,7 @@@ void mce_log_therm_throt_event(__u64 st
   * poller finds an MCE, poll 2x faster.  When the poller finds no more
   * errors, poll 2x slower (up to check_interval seconds).
   */
- static unsigned long check_interval = 5 * 60; /* 5 minutes */
+ static unsigned long check_interval = INITIAL_CHECK_INTERVAL;
  
  static DEFINE_PER_CPU(unsigned long, mce_next_interval); /* in jiffies */
  static DEFINE_PER_CPU(struct timer_list, mce_timer);
@@@ -1270,49 -1323,57 +1272,57 @@@ static unsigned long mce_adjust_timer_d
        return interval;
  }
  
- static unsigned long (*mce_adjust_timer)(unsigned long interval) =
-       mce_adjust_timer_default;
+ static unsigned long (*mce_adjust_timer)(unsigned long interval) = mce_adjust_timer_default;
  
- static int cmc_error_seen(void)
+ static void __restart_timer(struct timer_list *t, unsigned long interval)
  {
-       unsigned long *v = this_cpu_ptr(&mce_polled_error);
+       unsigned long when = jiffies + interval;
+       unsigned long flags;
  
-       return test_and_clear_bit(0, v);
+       local_irq_save(flags);
+       if (timer_pending(t)) {
+               if (time_before(when, t->expires))
+                       mod_timer_pinned(t, when);
+       } else {
+               t->expires = round_jiffies(when);
+               add_timer_on(t, smp_processor_id());
+       }
+       local_irq_restore(flags);
  }
  
  static void mce_timer_fn(unsigned long data)
  {
        struct timer_list *t = this_cpu_ptr(&mce_timer);
+       int cpu = smp_processor_id();
        unsigned long iv;
-       int notify;
  
-       WARN_ON(smp_processor_id() != data);
+       WARN_ON(cpu != data);
+       iv = __this_cpu_read(mce_next_interval);
  
        if (mce_available(this_cpu_ptr(&cpu_info))) {
-               machine_check_poll(MCP_TIMESTAMP,
-                               this_cpu_ptr(&mce_poll_banks));
-               mce_intel_cmci_poll();
+               machine_check_poll(MCP_TIMESTAMP, this_cpu_ptr(&mce_poll_banks));
+               if (mce_intel_cmci_poll()) {
+                       iv = mce_adjust_timer(iv);
+                       goto done;
+               }
        }
  
        /*
-        * Alert userspace if needed.  If we logged an MCE, reduce the
-        * polling interval, otherwise increase the polling interval.
+        * Alert userspace if needed. If we logged an MCE, reduce the polling
+        * interval, otherwise increase the polling interval.
         */
-       iv = __this_cpu_read(mce_next_interval);
-       notify = mce_notify_irq();
-       notify |= cmc_error_seen();
-       if (notify) {
+       if (mce_notify_irq())
                iv = max(iv / 2, (unsigned long) HZ/100);
-       } else {
+       else
                iv = min(iv * 2, round_jiffies_relative(check_interval * HZ));
-               iv = mce_adjust_timer(iv);
-       }
+ done:
        __this_cpu_write(mce_next_interval, iv);
-       /* Might have become 0 after CMCI storm subsided */
-       if (iv) {
-               t->expires = jiffies + iv;
-               add_timer_on(t, smp_processor_id());
-       }
+       __restart_timer(t, iv);
  }
  
  /*
  void mce_timer_kick(unsigned long interval)
  {
        struct timer_list *t = this_cpu_ptr(&mce_timer);
-       unsigned long when = jiffies + interval;
        unsigned long iv = __this_cpu_read(mce_next_interval);
  
-       if (timer_pending(t)) {
-               if (time_before(when, t->expires))
-                       mod_timer_pinned(t, when);
-       } else {
-               t->expires = round_jiffies(when);
-               add_timer_on(t, smp_processor_id());
-       }
+       __restart_timer(t, interval);
        if (interval < iv)
                __this_cpu_write(mce_next_interval, interval);
  }
@@@ -1631,7 -1686,7 +1635,7 @@@ static void __mcheck_cpu_init_vendor(st
        switch (c->x86_vendor) {
        case X86_VENDOR_INTEL:
                mce_intel_feature_init(c);
-               mce_adjust_timer = mce_intel_adjust_timer;
+               mce_adjust_timer = cmci_intel_adjust_timer;
                break;
        case X86_VENDOR_AMD:
                mce_amd_feature_init(c);