]> git.proxmox.com Git - pve-kernel.git/blobdiff - patches/kernel/0041-x86-mm-Rework-lazy-TLB-mode-and-TLB-freshness-tracki.patch
add tc fixes
[pve-kernel.git] / patches / kernel / 0041-x86-mm-Rework-lazy-TLB-mode-and-TLB-freshness-tracki.patch
diff --git a/patches/kernel/0041-x86-mm-Rework-lazy-TLB-mode-and-TLB-freshness-tracki.patch b/patches/kernel/0041-x86-mm-Rework-lazy-TLB-mode-and-TLB-freshness-tracki.patch
deleted file mode 100644 (file)
index 70f93ef..0000000
+++ /dev/null
@@ -1,453 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Andy Lutomirski <luto@kernel.org>
-Date: Thu, 29 Jun 2017 08:53:17 -0700
-Subject: [PATCH] x86/mm: Rework lazy TLB mode and TLB freshness tracking
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-CVE-2017-5754
-
-x86's lazy TLB mode used to be fairly weak -- it would switch to
-init_mm the first time it tried to flush a lazy TLB.  This meant an
-unnecessary CR3 write and, if the flush was remote, an unnecessary
-IPI.
-
-Rewrite it entirely.  When we enter lazy mode, we simply remove the
-CPU from mm_cpumask.  This means that we need a way to figure out
-whether we've missed a flush when we switch back out of lazy mode.
-I use the tlb_gen machinery to track whether a context is up to
-date.
-
-Note to reviewers: this patch, my itself, looks a bit odd.  I'm
-using an array of length 1 containing (ctx_id, tlb_gen) rather than
-just storing tlb_gen, and making it at array isn't necessary yet.
-I'm doing this because the next few patches add PCID support, and,
-with PCID, we need ctx_id, and the array will end up with a length
-greater than 1.  Making it an array now means that there will be
-less churn and therefore less stress on your eyeballs.
-
-NB: This is dubious but, AFAICT, still correct on Xen and UV.
-xen_exit_mmap() uses mm_cpumask() for nefarious purposes and this
-patch changes the way that mm_cpumask() works.  This should be okay,
-since Xen *also* iterates all online CPUs to find all the CPUs it
-needs to twiddle.
-
-The UV tlbflush code is rather dated and should be changed.
-
-Here are some benchmark results, done on a Skylake laptop at 2.3 GHz
-(turbo off, intel_pstate requesting max performance) under KVM with
-the guest using idle=poll (to avoid artifacts when bouncing between
-CPUs).  I haven't done any real statistics here -- I just ran them
-in a loop and picked the fastest results that didn't look like
-outliers.  Unpatched means commit a4eb8b993554, so all the
-bookkeeping overhead is gone.
-
-MADV_DONTNEED; touch the page; switch CPUs using sched_setaffinity.  In
-an unpatched kernel, MADV_DONTNEED will send an IPI to the previous CPU.
-This is intended to be a nearly worst-case test.
-
-  patched:         13.4µs
-  unpatched:       21.6µs
-
-Vitaly's pthread_mmap microbenchmark with 8 threads (on four cores),
-nrounds = 100, 256M data
-
-  patched:         1.1 seconds or so
-  unpatched:       1.9 seconds or so
-
-The sleepup on Vitaly's test appearss to be because it spends a lot
-of time blocked on mmap_sem, and this patch avoids sending IPIs to
-blocked CPUs.
-
-Signed-off-by: Andy Lutomirski <luto@kernel.org>
-Reviewed-by: Nadav Amit <nadav.amit@gmail.com>
-Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
-Cc: Andrew Banman <abanman@sgi.com>
-Cc: Andrew Morton <akpm@linux-foundation.org>
-Cc: Arjan van de Ven <arjan@linux.intel.com>
-Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
-Cc: Borislav Petkov <bp@alien8.de>
-Cc: Dave Hansen <dave.hansen@intel.com>
-Cc: Dimitri Sivanich <sivanich@sgi.com>
-Cc: Juergen Gross <jgross@suse.com>
-Cc: Linus Torvalds <torvalds@linux-foundation.org>
-Cc: Mel Gorman <mgorman@suse.de>
-Cc: Mike Travis <travis@sgi.com>
-Cc: Peter Zijlstra <peterz@infradead.org>
-Cc: Rik van Riel <riel@redhat.com>
-Cc: linux-mm@kvack.org
-Link: http://lkml.kernel.org/r/ddf2c92962339f4ba39d8fc41b853936ec0b44f1.1498751203.git.luto@kernel.org
-Signed-off-by: Ingo Molnar <mingo@kernel.org>
-(cherry picked from commit 94b1b03b519b81c494900cb112aa00ed205cc2d9)
-Signed-off-by: Andy Whitcroft <apw@canonical.com>
-Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
-(cherry picked from commit b381b7ae452f2bc6384507a897247be7c93a71cc)
-Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
----
- arch/x86/include/asm/mmu_context.h |   6 +-
- arch/x86/include/asm/tlbflush.h    |   4 -
- arch/x86/mm/init.c                 |   1 -
- arch/x86/mm/tlb.c                  | 197 ++++++++++++++++++++++---------------
- arch/x86/xen/mmu_pv.c              |   5 +-
- 5 files changed, 124 insertions(+), 89 deletions(-)
-
-diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
-index 6c05679c715b..d6b055b328f2 100644
---- a/arch/x86/include/asm/mmu_context.h
-+++ b/arch/x86/include/asm/mmu_context.h
-@@ -128,8 +128,10 @@ static inline void switch_ldt(struct mm_struct *prev, struct mm_struct *next)
- static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
- {
--      if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK)
--              this_cpu_write(cpu_tlbstate.state, TLBSTATE_LAZY);
-+      int cpu = smp_processor_id();
-+
-+      if (cpumask_test_cpu(cpu, mm_cpumask(mm)))
-+              cpumask_clear_cpu(cpu, mm_cpumask(mm));
- }
- static inline int init_new_context(struct task_struct *tsk,
-diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
-index 3a167c214560..6397275008db 100644
---- a/arch/x86/include/asm/tlbflush.h
-+++ b/arch/x86/include/asm/tlbflush.h
-@@ -95,7 +95,6 @@ struct tlb_state {
-        * mode even if we've already switched back to swapper_pg_dir.
-        */
-       struct mm_struct *loaded_mm;
--      int state;
-       /*
-        * Access to this CR4 shadow and to H/W CR4 is protected by
-@@ -318,9 +317,6 @@ static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long a)
- void native_flush_tlb_others(const struct cpumask *cpumask,
-                            const struct flush_tlb_info *info);
--#define TLBSTATE_OK   1
--#define TLBSTATE_LAZY 2
--
- static inline void arch_tlbbatch_add_mm(struct arch_tlbflush_unmap_batch *batch,
-                                       struct mm_struct *mm)
- {
-diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
-index df2624b091a7..c86dc071bb10 100644
---- a/arch/x86/mm/init.c
-+++ b/arch/x86/mm/init.c
-@@ -849,7 +849,6 @@ void __init zone_sizes_init(void)
- DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = {
-       .loaded_mm = &init_mm,
--      .state = 0,
-       .cr4 = ~0UL,    /* fail hard if we screw up cr4 shadow initialization */
- };
- EXPORT_SYMBOL_GPL(cpu_tlbstate);
-diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
-index 4e5a5ddb9e4d..0982c997d36f 100644
---- a/arch/x86/mm/tlb.c
-+++ b/arch/x86/mm/tlb.c
-@@ -45,8 +45,8 @@ void leave_mm(int cpu)
-       if (loaded_mm == &init_mm)
-               return;
--      if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK)
--              BUG();
-+      /* Warn if we're not lazy. */
-+      WARN_ON(cpumask_test_cpu(smp_processor_id(), mm_cpumask(loaded_mm)));
-       switch_mm(NULL, &init_mm, NULL);
- }
-@@ -65,94 +65,117 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next,
- void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
-                       struct task_struct *tsk)
- {
--      unsigned cpu = smp_processor_id();
-       struct mm_struct *real_prev = this_cpu_read(cpu_tlbstate.loaded_mm);
-+      unsigned cpu = smp_processor_id();
-+      u64 next_tlb_gen;
-       /*
--       * NB: The scheduler will call us with prev == next when
--       * switching from lazy TLB mode to normal mode if active_mm
--       * isn't changing.  When this happens, there is no guarantee
--       * that CR3 (and hence cpu_tlbstate.loaded_mm) matches next.
-+       * NB: The scheduler will call us with prev == next when switching
-+       * from lazy TLB mode to normal mode if active_mm isn't changing.
-+       * When this happens, we don't assume that CR3 (and hence
-+       * cpu_tlbstate.loaded_mm) matches next.
-        *
-        * NB: leave_mm() calls us with prev == NULL and tsk == NULL.
-        */
--      this_cpu_write(cpu_tlbstate.state, TLBSTATE_OK);
-+      /* We don't want flush_tlb_func_* to run concurrently with us. */
-+      if (IS_ENABLED(CONFIG_PROVE_LOCKING))
-+              WARN_ON_ONCE(!irqs_disabled());
-+
-+      /*
-+       * Verify that CR3 is what we think it is.  This will catch
-+       * hypothetical buggy code that directly switches to swapper_pg_dir
-+       * without going through leave_mm() / switch_mm_irqs_off().
-+       */
-+      VM_BUG_ON(read_cr3_pa() != __pa(real_prev->pgd));
-       if (real_prev == next) {
--              /*
--               * There's nothing to do: we always keep the per-mm control
--               * regs in sync with cpu_tlbstate.loaded_mm.  Just
--               * sanity-check mm_cpumask.
--               */
--              if (WARN_ON_ONCE(!cpumask_test_cpu(cpu, mm_cpumask(next))))
--                      cpumask_set_cpu(cpu, mm_cpumask(next));
--              return;
--      }
-+              VM_BUG_ON(this_cpu_read(cpu_tlbstate.ctxs[0].ctx_id) !=
-+                        next->context.ctx_id);
-+
-+              if (cpumask_test_cpu(cpu, mm_cpumask(next))) {
-+                      /*
-+                       * There's nothing to do: we weren't lazy, and we
-+                       * aren't changing our mm.  We don't need to flush
-+                       * anything, nor do we need to update CR3, CR4, or
-+                       * LDTR.
-+                       */
-+                      return;
-+              }
-+
-+              /* Resume remote flushes and then read tlb_gen. */
-+              cpumask_set_cpu(cpu, mm_cpumask(next));
-+              next_tlb_gen = atomic64_read(&next->context.tlb_gen);
-+
-+              if (this_cpu_read(cpu_tlbstate.ctxs[0].tlb_gen) < next_tlb_gen) {
-+                      /*
-+                       * Ideally, we'd have a flush_tlb() variant that
-+                       * takes the known CR3 value as input.  This would
-+                       * be faster on Xen PV and on hypothetical CPUs
-+                       * on which INVPCID is fast.
-+                       */
-+                      this_cpu_write(cpu_tlbstate.ctxs[0].tlb_gen,
-+                                     next_tlb_gen);
-+                      write_cr3(__pa(next->pgd));
-+
-+                      /*
-+                       * This gets called via leave_mm() in the idle path
-+                       * where RCU functions differently.  Tracing normally
-+                       * uses RCU, so we have to call the tracepoint
-+                       * specially here.
-+                       */
-+                      trace_tlb_flush_rcuidle(TLB_FLUSH_ON_TASK_SWITCH,
-+                                              TLB_FLUSH_ALL);
-+              }
--      if (IS_ENABLED(CONFIG_VMAP_STACK)) {
-               /*
--               * If our current stack is in vmalloc space and isn't
--               * mapped in the new pgd, we'll double-fault.  Forcibly
--               * map it.
-+               * We just exited lazy mode, which means that CR4 and/or LDTR
-+               * may be stale.  (Changes to the required CR4 and LDTR states
-+               * are not reflected in tlb_gen.)
-                */
--              unsigned int stack_pgd_index = pgd_index(current_stack_pointer());
--
--              pgd_t *pgd = next->pgd + stack_pgd_index;
--
--              if (unlikely(pgd_none(*pgd)))
--                      set_pgd(pgd, init_mm.pgd[stack_pgd_index]);
--      }
-+      } else {
-+              VM_BUG_ON(this_cpu_read(cpu_tlbstate.ctxs[0].ctx_id) ==
-+                        next->context.ctx_id);
-+
-+              if (IS_ENABLED(CONFIG_VMAP_STACK)) {
-+                      /*
-+                       * If our current stack is in vmalloc space and isn't
-+                       * mapped in the new pgd, we'll double-fault.  Forcibly
-+                       * map it.
-+                       */
-+                      unsigned int index = pgd_index(current_stack_pointer());
-+                      pgd_t *pgd = next->pgd + index;
-+
-+                      if (unlikely(pgd_none(*pgd)))
-+                              set_pgd(pgd, init_mm.pgd[index]);
-+              }
--      this_cpu_write(cpu_tlbstate.loaded_mm, next);
--      this_cpu_write(cpu_tlbstate.ctxs[0].ctx_id, next->context.ctx_id);
--      this_cpu_write(cpu_tlbstate.ctxs[0].tlb_gen, atomic64_read(&next->context.tlb_gen));
-+              /* Stop remote flushes for the previous mm */
-+              if (cpumask_test_cpu(cpu, mm_cpumask(real_prev)))
-+                      cpumask_clear_cpu(cpu, mm_cpumask(real_prev));
--      WARN_ON_ONCE(cpumask_test_cpu(cpu, mm_cpumask(next)));
--      cpumask_set_cpu(cpu, mm_cpumask(next));
-+              VM_WARN_ON_ONCE(cpumask_test_cpu(cpu, mm_cpumask(next)));
--      /*
--       * Re-load page tables.
--       *
--       * This logic has an ordering constraint:
--       *
--       *  CPU 0: Write to a PTE for 'next'
--       *  CPU 0: load bit 1 in mm_cpumask.  if nonzero, send IPI.
--       *  CPU 1: set bit 1 in next's mm_cpumask
--       *  CPU 1: load from the PTE that CPU 0 writes (implicit)
--       *
--       * We need to prevent an outcome in which CPU 1 observes
--       * the new PTE value and CPU 0 observes bit 1 clear in
--       * mm_cpumask.  (If that occurs, then the IPI will never
--       * be sent, and CPU 0's TLB will contain a stale entry.)
--       *
--       * The bad outcome can occur if either CPU's load is
--       * reordered before that CPU's store, so both CPUs must
--       * execute full barriers to prevent this from happening.
--       *
--       * Thus, switch_mm needs a full barrier between the
--       * store to mm_cpumask and any operation that could load
--       * from next->pgd.  TLB fills are special and can happen
--       * due to instruction fetches or for no reason at all,
--       * and neither LOCK nor MFENCE orders them.
--       * Fortunately, load_cr3() is serializing and gives the
--       * ordering guarantee we need.
--       */
--      load_cr3(next->pgd);
-+              /*
-+               * Start remote flushes and then read tlb_gen.
-+               */
-+              cpumask_set_cpu(cpu, mm_cpumask(next));
-+              next_tlb_gen = atomic64_read(&next->context.tlb_gen);
--      /*
--       * This gets called via leave_mm() in the idle path where RCU
--       * functions differently.  Tracing normally uses RCU, so we have to
--       * call the tracepoint specially here.
--       */
--      trace_tlb_flush_rcuidle(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
-+              this_cpu_write(cpu_tlbstate.ctxs[0].ctx_id, next->context.ctx_id);
-+              this_cpu_write(cpu_tlbstate.ctxs[0].tlb_gen, next_tlb_gen);
-+              this_cpu_write(cpu_tlbstate.loaded_mm, next);
-+              write_cr3(__pa(next->pgd));
--      /* Stop flush ipis for the previous mm */
--      WARN_ON_ONCE(!cpumask_test_cpu(cpu, mm_cpumask(real_prev)) &&
--                   real_prev != &init_mm);
--      cpumask_clear_cpu(cpu, mm_cpumask(real_prev));
-+              /*
-+               * This gets called via leave_mm() in the idle path where RCU
-+               * functions differently.  Tracing normally uses RCU, so we
-+               * have to call the tracepoint specially here.
-+               */
-+              trace_tlb_flush_rcuidle(TLB_FLUSH_ON_TASK_SWITCH,
-+                                      TLB_FLUSH_ALL);
-+      }
--      /* Load per-mm CR4 and LDTR state */
-       load_mm_cr4(next);
-       switch_ldt(real_prev, next);
- }
-@@ -186,13 +209,13 @@ static void flush_tlb_func_common(const struct flush_tlb_info *f,
-       VM_WARN_ON(this_cpu_read(cpu_tlbstate.ctxs[0].ctx_id) !=
-                  loaded_mm->context.ctx_id);
--      if (this_cpu_read(cpu_tlbstate.state) != TLBSTATE_OK) {
-+      if (!cpumask_test_cpu(smp_processor_id(), mm_cpumask(loaded_mm))) {
-               /*
--               * leave_mm() is adequate to handle any type of flush, and
--               * we would prefer not to receive further IPIs.  leave_mm()
--               * clears this CPU's bit in mm_cpumask().
-+               * We're in lazy mode -- don't flush.  We can get here on
-+               * remote flushes due to races and on local flushes if a
-+               * kernel thread coincidentally flushes the mm it's lazily
-+               * still using.
-                */
--              leave_mm(smp_processor_id());
-               return;
-       }
-@@ -203,6 +226,7 @@ static void flush_tlb_func_common(const struct flush_tlb_info *f,
-                * be handled can catch us all the way up, leaving no work for
-                * the second flush.
-                */
-+              trace_tlb_flush(reason, 0);
-               return;
-       }
-@@ -304,6 +328,21 @@ void native_flush_tlb_others(const struct cpumask *cpumask,
-                               (info->end - info->start) >> PAGE_SHIFT);
-       if (is_uv_system()) {
-+              /*
-+               * This whole special case is confused.  UV has a "Broadcast
-+               * Assist Unit", which seems to be a fancy way to send IPIs.
-+               * Back when x86 used an explicit TLB flush IPI, UV was
-+               * optimized to use its own mechanism.  These days, x86 uses
-+               * smp_call_function_many(), but UV still uses a manual IPI,
-+               * and that IPI's action is out of date -- it does a manual
-+               * flush instead of calling flush_tlb_func_remote().  This
-+               * means that the percpu tlb_gen variables won't be updated
-+               * and we'll do pointless flushes on future context switches.
-+               *
-+               * Rather than hooking native_flush_tlb_others() here, I think
-+               * that UV should be updated so that smp_call_function_many(),
-+               * etc, are optimal on UV.
-+               */
-               unsigned int cpu;
-               cpu = smp_processor_id();
-@@ -363,6 +402,7 @@ void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
-       if (cpumask_any_but(mm_cpumask(mm), cpu) < nr_cpu_ids)
-               flush_tlb_others(mm_cpumask(mm), &info);
-+
-       put_cpu();
- }
-@@ -371,8 +411,6 @@ static void do_flush_tlb_all(void *info)
- {
-       count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED);
-       __flush_tlb_all();
--      if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_LAZY)
--              leave_mm(smp_processor_id());
- }
- void flush_tlb_all(void)
-@@ -425,6 +463,7 @@ void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
-       if (cpumask_any_but(&batch->cpumask, cpu) < nr_cpu_ids)
-               flush_tlb_others(&batch->cpumask, &info);
-+
-       cpumask_clear(&batch->cpumask);
-       put_cpu();
-diff --git a/arch/x86/xen/mmu_pv.c b/arch/x86/xen/mmu_pv.c
-index 5f61b7e2e6b2..ba76f3ce997f 100644
---- a/arch/x86/xen/mmu_pv.c
-+++ b/arch/x86/xen/mmu_pv.c
-@@ -1005,14 +1005,12 @@ static void xen_drop_mm_ref(struct mm_struct *mm)
-       /* Get the "official" set of cpus referring to our pagetable. */
-       if (!alloc_cpumask_var(&mask, GFP_ATOMIC)) {
-               for_each_online_cpu(cpu) {
--                      if (!cpumask_test_cpu(cpu, mm_cpumask(mm))
--                          && per_cpu(xen_current_cr3, cpu) != __pa(mm->pgd))
-+                      if (per_cpu(xen_current_cr3, cpu) != __pa(mm->pgd))
-                               continue;
-                       smp_call_function_single(cpu, drop_mm_ref_this_cpu, mm, 1);
-               }
-               return;
-       }
--      cpumask_copy(mask, mm_cpumask(mm));
-       /*
-        * It's possible that a vcpu may have a stale reference to our
-@@ -1021,6 +1019,7 @@ static void xen_drop_mm_ref(struct mm_struct *mm)
-        * look at its actual current cr3 value, and force it to flush
-        * if needed.
-        */
-+      cpumask_clear(mask);
-       for_each_online_cpu(cpu) {
-               if (per_cpu(xen_current_cr3, cpu) == __pa(mm->pgd))
-                       cpumask_set_cpu(cpu, mask);
--- 
-2.14.2
-