Revert "x86/mm: Stop calling leave_mm() in idle code"
authorAndy Lutomirski <luto@kernel.org>
Sat, 4 Nov 2017 11:16:12 +0000 (04:16 -0700)
committerIngo Molnar <mingo@kernel.org>
Sat, 4 Nov 2017 14:01:50 +0000 (15:01 +0100)
This reverts commit 43858b4f25cf0adc5c2ca9cf5ce5fdf2532941e5.

The reason I removed the leave_mm() calls in question is because the
heuristic wasn't needed after that patch.  With the original version
of my PCID series, we never flushed a "lazy cpu" (i.e. a CPU running
kernel thread) due a flush on the loaded mm.

Unfortunately, that caused architectural issues, so now I've
reinstated these flushes on non-PCID systems in:

    commit b956575bed91 ("x86/mm: Flush more aggressively in lazy TLB mode").

That, in turn, gives us a power management and occasionally
performance regression as compared to old kernels: a process that
goes into a deep idle state on a given CPU and gets its mm flushed
due to activity on a different CPU will wake the idle CPU.

Reinstate the old ugly heuristic: if a CPU goes into ACPI C3 or an
intel_idle state that is likely to cause a TLB flush gets its mm
switched to init_mm before going idle.

FWIW, this heuristic is lousy.  Whether we should change CR3 before
idle isn't a good hint except insofar as the performance hit is a bit
lower if the TLB is getting flushed by the idle code anyway.  What we
really want to know is whether we anticipate being idle long enough
that the mm is likely to be flushed before we wake up.  This is more a
matter of the expected latency than the idle state that gets chosen.
This heuristic also completely fails on systems that don't know
whether the TLB will be flushed (e.g. AMD systems?).  OTOH it may be a
bit obsolete anyway -- PCID systems don't presently benefit from this
heuristic at all.

We also shouldn't do this callback from innermost bit of the idle code
due to the RCU nastiness it causes.  All the information need is
available before rcu_idle_enter() needs to happen.

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Borislav Petkov <bpetkov@suse.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: 43858b4f25cf "x86/mm: Stop calling leave_mm() in idle code"
Link: http://lkml.kernel.org/r/c513bbd4e653747213e05bc7062de000bf0202a5.1509793738.git.luto@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/ia64/include/asm/acpi.h
arch/x86/include/asm/acpi.h
arch/x86/mm/tlb.c
drivers/acpi/processor_idle.c
drivers/idle/intel_idle.c

index c86a947f5368633b86d1a8e5dc7ddd1803982c64..a3d0211970e95e5152edbaeeecd8ab3a42041ac6 100644 (file)
@@ -112,6 +112,8 @@ static inline void arch_acpi_set_pdc_bits(u32 *buf)
        buf[2] |= ACPI_PDC_EST_CAPABILITY_SMP;
 }
 
+#define acpi_unlazy_tlb(x)
+
 #ifdef CONFIG_ACPI_NUMA
 extern cpumask_t early_cpu_possible_map;
 #define for_each_possible_early_cpu(cpu)  \
index 72d867f6b518e4db5a79a10c924f858a3edb0af8..8d0ec9df1cbeb53894f47454cd1391b297e6086c 100644 (file)
@@ -150,6 +150,8 @@ static inline void disable_acpi(void) { }
 extern int x86_acpi_numa_init(void);
 #endif /* CONFIG_ACPI_NUMA */
 
+#define acpi_unlazy_tlb(x)     leave_mm(x)
+
 #ifdef CONFIG_ACPI_APEI
 static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr)
 {
index 0f3d0cea4d00ca253ee5b46ed78158d460797a56..3118392cdf756bfc913d7a4137d5f7e0d46b046d 100644 (file)
@@ -85,6 +85,7 @@ void leave_mm(int cpu)
 
        switch_mm(NULL, &init_mm, NULL);
 }
+EXPORT_SYMBOL_GPL(leave_mm);
 
 void switch_mm(struct mm_struct *prev, struct mm_struct *next,
               struct task_struct *tsk)
@@ -195,12 +196,22 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
                        this_cpu_write(cpu_tlbstate.ctxs[new_asid].ctx_id, next->context.ctx_id);
                        this_cpu_write(cpu_tlbstate.ctxs[new_asid].tlb_gen, next_tlb_gen);
                        write_cr3(build_cr3(next, new_asid));
-                       trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH,
-                                       TLB_FLUSH_ALL);
+
+                       /*
+                        * NB: This gets called via leave_mm() in the idle path
+                        * where RCU functions differently.  Tracing normally
+                        * uses RCU, so we need to use the _rcuidle variant.
+                        *
+                        * (There is no good reason for this.  The idle code should
+                        *  be rearranged to call this before rcu_idle_enter().)
+                        */
+                       trace_tlb_flush_rcuidle(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
                } else {
                        /* The new ASID is already up to date. */
                        write_cr3(build_cr3_noflush(next, new_asid));
-                       trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, 0);
+
+                       /* See above wrt _rcuidle. */
+                       trace_tlb_flush_rcuidle(TLB_FLUSH_ON_TASK_SWITCH, 0);
                }
 
                this_cpu_write(cpu_tlbstate.loaded_mm, next);
index 2736e25e9dc6458d736383ed0bc60761e7eb4a5f..d50a7b6ccddd99785dc7c6b1e0f368144c597d0e 100644 (file)
@@ -710,6 +710,8 @@ static DEFINE_RAW_SPINLOCK(c3_lock);
 static void acpi_idle_enter_bm(struct acpi_processor *pr,
                               struct acpi_processor_cx *cx, bool timer_bc)
 {
+       acpi_unlazy_tlb(smp_processor_id());
+
        /*
         * Must be done before busmaster disable as we might need to
         * access HPET !
index 5dc7ea4b6bc4cc207cffd8d8efbaed776179c9d9..f0b06b14e782b5b926b5ba7876d827551ce4cfa9 100644 (file)
@@ -913,15 +913,16 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev,
        struct cpuidle_state *state = &drv->states[index];
        unsigned long eax = flg2MWAIT(state->flags);
        unsigned int cstate;
+       int cpu = smp_processor_id();
 
        cstate = (((eax) >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK) + 1;
 
        /*
-        * NB: if CPUIDLE_FLAG_TLB_FLUSHED is set, this idle transition
-        * will probably flush the TLB.  It's not guaranteed to flush
-        * the TLB, though, so it's not clear that we can do anything
-        * useful with this knowledge.
+        * leave_mm() to avoid costly and often unnecessary wakeups
+        * for flushing the user TLB's associated with the active mm.
         */
+       if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED)
+               leave_mm(cpu);
 
        if (!(lapic_timer_reliable_states & (1 << (cstate))))
                tick_broadcast_enter();