Merge tag 'libnvdimm-for-4.19_dax-memory-failure' of gitolite.kernel.org:pub/scm...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 26 Aug 2018 01:43:59 +0000 (18:43 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 26 Aug 2018 01:43:59 +0000 (18:43 -0700)
Pull libnvdimm memory-failure update from Dave Jiang:
 "As it stands, memory_failure() gets thoroughly confused by dev_pagemap
  backed mappings. The recovery code has specific enabling for several
  possible page states and needs new enabling to handle poison in dax
  mappings.

  In order to support reliable reverse mapping of user space addresses:

   1/ Add new locking in the memory_failure() rmap path to prevent races
      that would typically be handled by the page lock.

   2/ Since dev_pagemap pages are hidden from the page allocator and the
      "compound page" accounting machinery, add a mechanism to determine
      the size of the mapping that encompasses a given poisoned pfn.

   3/ Given pmem errors can be repaired, change the speculatively
      accessed poison protection, mce_unmap_kpfn(), to be reversible and
      otherwise allow ongoing access from the kernel.

  A side effect of this enabling is that MADV_HWPOISON becomes usable
  for dax mappings, however the primary motivation is to allow the
  system to survive userspace consumption of hardware-poison via dax.
  Specifically the current behavior is:

     mce: Uncorrected hardware memory error in user-access at af34214200
     {1}[Hardware Error]: It has been corrected by h/w and requires no further action
     mce: [Hardware Error]: Machine check events logged
     {1}[Hardware Error]: event severity: corrected
     Memory failure: 0xaf34214: reserved kernel page still referenced by 1 users
     [..]
     Memory failure: 0xaf34214: recovery action for reserved kernel page: Failed
     mce: Memory error not recovered
     <reboot>

  ...and with these changes:

     Injecting memory failure for pfn 0x20cb00 at process virtual address 0x7f763dd00000
     Memory failure: 0x20cb00: Killing dax-pmd:5421 due to hardware memory corruption
     Memory failure: 0x20cb00: recovery action for dax page: Recovered

  Given all the cross dependencies I propose taking this through
  nvdimm.git with acks from Naoya, x86/core, x86/RAS, and of course dax
  folks"

* tag 'libnvdimm-for-4.19_dax-memory-failure' of gitolite.kernel.org:pub/scm/linux/kernel/git/nvdimm/nvdimm:
  libnvdimm, pmem: Restore page attributes when clearing errors
  x86/memory_failure: Introduce {set, clear}_mce_nospec()
  x86/mm/pat: Prepare {reserve, free}_memtype() for "decoy" addresses
  mm, memory_failure: Teach memory_failure() about dev_pagemap pages
  filesystem-dax: Introduce dax_lock_mapping_entry()
  mm, memory_failure: Collect mapping size in collect_procs()
  mm, madvise_inject_error: Let memory_failure() optionally take a page reference
  mm, dev_pagemap: Do not clear ->mapping on final put
  mm, madvise_inject_error: Disable MADV_SOFT_OFFLINE for ZONE_DEVICE pages
  filesystem-dax: Set page->index
  device-dax: Set page->index
  device-dax: Enable page_mapping()
  device-dax: Convert to vmf_insert_mixed and vm_fault_t

1  2 
arch/x86/include/asm/set_memory.h
arch/x86/kernel/cpu/mcheck/mce.c
drivers/dax/device.c
drivers/nvdimm/pmem.c
fs/dax.c
include/linux/huge_mm.h
include/linux/mm.h
kernel/memremap.c
mm/hmm.c
mm/huge_memory.c
mm/memory-failure.c

Simple merge
@@@ -1076,129 -1072,6 +1072,101 @@@ static int do_memory_failure(struct mc
        return ret;
  }
  
- #ifndef mce_unmap_kpfn
- static void mce_unmap_kpfn(unsigned long pfn)
- {
-       unsigned long decoy_addr;
-       /*
-        * Unmap this page from the kernel 1:1 mappings to make sure
-        * we don't log more errors because of speculative access to
-        * the page.
-        * We would like to just call:
-        *      set_memory_np((unsigned long)pfn_to_kaddr(pfn), 1);
-        * but doing that would radically increase the odds of a
-        * speculative access to the poison page because we'd have
-        * the virtual address of the kernel 1:1 mapping sitting
-        * around in registers.
-        * Instead we get tricky.  We create a non-canonical address
-        * that looks just like the one we want, but has bit 63 flipped.
-        * This relies on set_memory_np() not checking whether we passed
-        * a legal address.
-        */
-       decoy_addr = (pfn << PAGE_SHIFT) + (PAGE_OFFSET ^ BIT(63));
-       if (set_memory_np(decoy_addr, 1))
-               pr_warn("Could not invalidate pfn=0x%lx from 1:1 map\n", pfn);
- }
- #endif
 +
 +/*
 + * Cases where we avoid rendezvous handler timeout:
 + * 1) If this CPU is offline.
 + *
 + * 2) If crashing_cpu was set, e.g. we're entering kdump and we need to
 + *  skip those CPUs which remain looping in the 1st kernel - see
 + *  crash_nmi_callback().
 + *
 + * Note: there still is a small window between kexec-ing and the new,
 + * kdump kernel establishing a new #MC handler where a broadcasted MCE
 + * might not get handled properly.
 + */
 +static bool __mc_check_crashing_cpu(int cpu)
 +{
 +      if (cpu_is_offline(cpu) ||
 +          (crashing_cpu != -1 && crashing_cpu != cpu)) {
 +              u64 mcgstatus;
 +
 +              mcgstatus = mce_rdmsrl(MSR_IA32_MCG_STATUS);
 +              if (mcgstatus & MCG_STATUS_RIPV) {
 +                      mce_wrmsrl(MSR_IA32_MCG_STATUS, 0);
 +                      return true;
 +              }
 +      }
 +      return false;
 +}
 +
 +static void __mc_scan_banks(struct mce *m, struct mce *final,
 +                          unsigned long *toclear, unsigned long *valid_banks,
 +                          int no_way_out, int *worst)
 +{
 +      struct mca_config *cfg = &mca_cfg;
 +      int severity, i;
 +
 +      for (i = 0; i < cfg->banks; i++) {
 +              __clear_bit(i, toclear);
 +              if (!test_bit(i, valid_banks))
 +                      continue;
 +
 +              if (!mce_banks[i].ctl)
 +                      continue;
 +
 +              m->misc = 0;
 +              m->addr = 0;
 +              m->bank = i;
 +
 +              m->status = mce_rdmsrl(msr_ops.status(i));
 +              if (!(m->status & MCI_STATUS_VAL))
 +                      continue;
 +
 +              /*
 +               * Corrected or non-signaled errors are handled by
 +               * machine_check_poll(). Leave them alone, unless this panics.
 +               */
 +              if (!(m->status & (cfg->ser ? MCI_STATUS_S : MCI_STATUS_UC)) &&
 +                      !no_way_out)
 +                      continue;
 +
 +              /* Set taint even when machine check was not enabled. */
 +              add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);
 +
 +              severity = mce_severity(m, cfg->tolerant, NULL, true);
 +
 +              /*
 +               * When machine check was for corrected/deferred handler don't
 +               * touch, unless we're panicking.
 +               */
 +              if ((severity == MCE_KEEP_SEVERITY ||
 +                   severity == MCE_UCNA_SEVERITY) && !no_way_out)
 +                      continue;
 +
 +              __set_bit(i, toclear);
 +
 +              /* Machine check event was not enabled. Clear, but ignore. */
 +              if (severity == MCE_NO_SEVERITY)
 +                      continue;
 +
 +              mce_read_aux(m, i);
 +
 +              /* assuming valid severity level != 0 */
 +              m->severity = severity;
 +
 +              mce_log(m);
 +
 +              if (severity > *worst) {
 +                      *final = *m;
 +                      *worst = severity;
 +              }
 +      }
 +
 +      /* mce_clear_state will clear *final, save locally for use later */
 +      *m = *final;
 +}
 +
  /*
   * The actual machine check handler. This only handles real
   * exceptions when something got corrupted coming in through int 18.
Simple merge
Simple merge
diff --cc fs/dax.c
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc mm/hmm.c
Simple merge
Simple merge
  #include <linux/hugetlb.h>
  #include <linux/memory_hotplug.h>
  #include <linux/mm_inline.h>
+ #include <linux/memremap.h>
  #include <linux/kfifo.h>
  #include <linux/ratelimit.h>
 +#include <linux/page-isolation.h>
  #include "internal.h"
  #include "ras/ras_event.h"