Merge tag 'mce-for-tip' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras...
authorIngo Molnar <mingo@elte.hu>
Wed, 14 Mar 2012 06:44:48 +0000 (07:44 +0100)
committerIngo Molnar <mingo@elte.hu>
Wed, 14 Mar 2012 06:44:48 +0000 (07:44 +0100)
Apply two miscellaneous MCE fixes.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
1  2 
arch/x86/kernel/cpu/mcheck/mce.c
arch/x86/kernel/cpu/mcheck/mce_amd.c

index ad573d8baf10c4820d5b8c4a32b3a18ae0c835ce,87c56ba8080c913e4b13987150b8bd20f4fb42e2..c614bd4de0f36b4dad96a3a60418e2751f77f0f2
@@@ -540,27 -540,6 +540,27 @@@ static void mce_report_event(struct pt_
        irq_work_queue(&__get_cpu_var(mce_irq_work));
  }
  
 +/*
 + * Read ADDR and MISC registers.
 + */
 +static void mce_read_aux(struct mce *m, int i)
 +{
 +      if (m->status & MCI_STATUS_MISCV)
 +              m->misc = mce_rdmsrl(MSR_IA32_MCx_MISC(i));
 +      if (m->status & MCI_STATUS_ADDRV) {
 +              m->addr = mce_rdmsrl(MSR_IA32_MCx_ADDR(i));
 +
 +              /*
 +               * Mask the reported address by the reported granularity.
 +               */
 +              if (mce_ser && (m->status & MCI_STATUS_MISCV)) {
 +                      u8 shift = MCI_MISC_ADDR_LSB(m->misc);
 +                      m->addr >>= shift;
 +                      m->addr <<= shift;
 +              }
 +      }
 +}
 +
  DEFINE_PER_CPU(unsigned, mce_poll_count);
  
  /*
@@@ -611,7 -590,10 +611,7 @@@ void machine_check_poll(enum mcp_flags 
                    (m.status & (mce_ser ? MCI_STATUS_S : MCI_STATUS_UC)))
                        continue;
  
 -              if (m.status & MCI_STATUS_MISCV)
 -                      m.misc = mce_rdmsrl(MSR_IA32_MCx_MISC(i));
 -              if (m.status & MCI_STATUS_ADDRV)
 -                      m.addr = mce_rdmsrl(MSR_IA32_MCx_ADDR(i));
 +              mce_read_aux(&m, i);
  
                if (!(flags & MCP_TIMESTAMP))
                        m.tsc = 0;
@@@ -934,49 -916,6 +934,49 @@@ 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;
 +} mce_info[MCE_INFO_MAX];
 +
 +static void mce_save_info(__u64 addr)
 +{
 +      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;
 +                      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.
@@@ -1030,9 -969,7 +1030,9 @@@ void do_machine_check(struct pt_regs *r
        barrier();
  
        /*
 -       * When no restart IP must always kill or panic.
 +       * When no restart IP might need to kill or panic.
 +       * Assume the worst for now, but if we find the
 +       * severity is MCE_AR_SEVERITY we have other options.
         */
        if (!(m.mcgstatus & MCG_STATUS_RIPV))
                kill_it = 1;
                        continue;
                }
  
 -              /*
 -               * Kill on action required.
 -               */
 -              if (severity == MCE_AR_SEVERITY)
 -                      kill_it = 1;
 -
 -              if (m.status & MCI_STATUS_MISCV)
 -                      m.misc = mce_rdmsrl(MSR_IA32_MCx_MISC(i));
 -              if (m.status & MCI_STATUS_ADDRV)
 -                      m.addr = mce_rdmsrl(MSR_IA32_MCx_ADDR(i));
 +              mce_read_aux(&m, i);
  
                /*
                 * Action optional error. Queue address for later processing.
                }
        }
  
 +      /* mce_clear_state will clear *final, save locally for use later */
 +      m = *final;
 +
        if (!no_way_out)
                mce_clear_state(toclear);
  
                no_way_out = worst >= MCE_PANIC_SEVERITY;
  
        /*
 -       * If we have decided that we just CAN'T continue, and the user
 -       * has not set tolerant to an insane level, give up and die.
 -       *
 -       * This is mainly used in the case when the system doesn't
 -       * support MCE broadcasting or it has been disabled.
 +       * At insane "tolerant" levels we take no action. Otherwise
 +       * we only die if we have no other choice. For less serious
 +       * issues we try to recover, or limit damage to the current
 +       * process.
         */
 -      if (no_way_out && tolerant < 3)
 -              mce_panic("Fatal machine check on current CPU", final, msg);
 -
 -      /*
 -       * If the error seems to be unrecoverable, something should be
 -       * done.  Try to kill as little as possible.  If we can kill just
 -       * one task, do that.  If the user has set the tolerance very
 -       * high, don't try to do anything at all.
 -       */
 -
 -      if (kill_it && tolerant < 3)
 -              force_sig(SIGBUS, current);
 -
 -      /* notify userspace ASAP */
 -      set_thread_flag(TIF_MCE_NOTIFY);
 +      if (tolerant < 3) {
 +              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);
 +                      set_thread_flag(TIF_MCE_NOTIFY);
 +              } else if (kill_it) {
 +                      force_sig(SIGBUS, current);
 +              }
 +      }
  
        if (worst > 0)
                mce_report_event(regs);
  }
  EXPORT_SYMBOL_GPL(do_machine_check);
  
 -/* dummy to break dependency. actual code is in mm/memory-failure.c */
 -void __attribute__((weak)) memory_failure(unsigned long pfn, int vector)
 +#ifndef CONFIG_MEMORY_FAILURE
 +int memory_failure(unsigned long pfn, int vector, int flags)
  {
 -      printk(KERN_ERR "Action optional memory failure at %lx ignored\n", pfn);
 +      /* mce_severity() should not hand us an ACTION_REQUIRED error */
 +      BUG_ON(flags & MF_ACTION_REQUIRED);
 +      printk(KERN_ERR "Uncorrected memory error in page 0x%lx ignored\n"
 +              "Rebuild kernel with CONFIG_MEMORY_FAILURE=y for smarter handling\n", pfn);
 +
 +      return 0;
  }
 +#endif
  
  /*
 - * Called after mce notification in process context. This code
 - * is allowed to sleep. Call the high level VM handler to process
 - * any corrupted pages.
 - * Assume that the work queue code only calls this one at a time
 - * per CPU.
 - * Note we don't disable preemption, so this code might run on the wrong
 - * CPU. In this case the event is picked up by the scheduled work queue.
 - * This is merely a fast path to expedite processing in some common
 - * cases.
 + * 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;
 -      mce_notify_irq();
 -      while (mce_ring_get(&pfn))
 -              memory_failure(pfn, MCE_VECTOR);
 +      struct mce_info *mi = mce_find_info();
 +
 +      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);
 +      if (memory_failure(pfn, MCE_VECTOR, MF_ACTION_REQUIRED) < 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()
 + * placed into the "ring").
 + */
  static void mce_process_work(struct work_struct *dummy)
  {
 -      mce_notify_process();
 +      unsigned long pfn;
 +
 +      while (mce_ring_get(&pfn))
 +              memory_failure(pfn, MCE_VECTOR, 0);
  }
  
  #ifdef CONFIG_X86_MCE_INTEL
@@@ -1286,6 -1211,8 +1286,6 @@@ int mce_notify_irq(void
        /* Not more than two messages every minute */
        static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2);
  
 -      clear_thread_flag(TIF_MCE_NOTIFY);
 -
        if (test_and_clear_bit(0, &mce_need_notify)) {
                /* wake processes polling /dev/mcelog */
                wake_up_interruptible(&mce_chrdev_wait);
@@@ -1614,6 -1541,12 +1614,12 @@@ static int __mce_read_apei(char __user 
        /* Error or no more MCE record */
        if (rc <= 0) {
                mce_apei_read_done = 1;
+               /*
+                * When ERST is disabled, mce_chrdev_read() should return
+                * "no record" instead of "no device."
+                */
+               if (rc == -ENODEV)
+                       return 0;
                return rc;
        }
        rc = -EFAULT;
@@@ -1932,7 -1865,7 +1938,7 @@@ static struct bus_type mce_subsys = 
        .dev_name       = "machinecheck",
  };
  
struct device *mce_device[CONFIG_NR_CPUS];
DEFINE_PER_CPU(struct device *, mce_device);
  
  __cpuinitdata
  void (*threshold_cpu_callback)(unsigned long action, unsigned int cpu);
@@@ -2111,7 -2044,7 +2117,7 @@@ static __cpuinit int mce_device_create(
                        goto error2;
        }
        cpumask_set_cpu(cpu, mce_device_initialized);
-       mce_device[cpu] = dev;
+       per_cpu(mce_device, cpu) = dev;
  
        return 0;
  error2:
@@@ -2128,7 -2061,7 +2134,7 @@@ error
  
  static __cpuinit void mce_device_remove(unsigned int cpu)
  {
-       struct device *dev = mce_device[cpu];
+       struct device *dev = per_cpu(mce_device, cpu);
        int i;
  
        if (!cpumask_test_cpu(cpu, mce_device_initialized))
  
        device_unregister(dev);
        cpumask_clear_cpu(cpu, mce_device_initialized);
-       mce_device[cpu] = NULL;
+       per_cpu(mce_device, cpu) = NULL;
  }
  
  /* Make sure there are no machine checks on offlined CPUs. */
index e4eeaaf58a470a841d612724b5ef8f523fb3adb6,a4bf9d23cdbaf96d5eeb20621d5d8b616993e600..99b57179f9129b770d0e3f069f09ab06f4f686ec
@@@ -523,12 -523,11 +523,12 @@@ static __cpuinit int threshold_create_b
  {
        int i, err = 0;
        struct threshold_bank *b = NULL;
-       struct device *dev = mce_device[cpu];
+       struct device *dev = per_cpu(mce_device, cpu);
        char name[32];
  
        sprintf(name, "threshold_bank%i", bank);
  
 +#ifdef CONFIG_SMP
        if (cpu_data(cpu).cpu_core_id && shared_bank[bank]) {   /* symlink */
                i = cpumask_first(cpu_llc_shared_mask(cpu));
  
  
                goto out;
        }
 +#endif
  
        b = kzalloc(sizeof(struct threshold_bank), GFP_KERNEL);
        if (!b) {
                if (i == cpu)
                        continue;
  
-               dev = mce_device[i];
+               dev = per_cpu(mce_device, i);
                if (dev)
                        err = sysfs_create_link(&dev->kobj,b->kobj, name);
                if (err)
@@@ -667,7 -665,8 +667,8 @@@ static void threshold_remove_bank(unsig
  #ifdef CONFIG_SMP
        /* sibling symlink */
        if (shared_bank[bank] && b->blocks->cpu != cpu) {
-               sysfs_remove_link(&mce_device[cpu]->kobj, name);
+               dev = per_cpu(mce_device, cpu);
+               sysfs_remove_link(&dev->kobj, name);
                per_cpu(threshold_banks, cpu)[bank] = NULL;
  
                return;
                if (i == cpu)
                        continue;
  
-               dev = mce_device[i];
+               dev = per_cpu(mce_device, i);
                if (dev)
                        sysfs_remove_link(&dev->kobj, name);
                per_cpu(threshold_banks, i)[bank] = NULL;