[PATCH] powerpc/64s/radix: reset mm_cpumask for single thread process when possible
Nicholas Piggin
npiggin at gmail.com
Wed May 9 21:22:26 AEST 2018
On Wed, 9 May 2018 18:23:48 +1000
Balbir Singh <bsingharora at gmail.com> wrote:
> On Wed, May 9, 2018 at 4:56 PM, Nicholas Piggin <npiggin at gmail.com> wrote:
> > When a single-threaded process has a non-local mm_cpumask and requires
> > a full PID tlbie invalidation, use that as an opportunity to reset the
> > cpumask back to the current CPU we're running on.
> >
> > No other thread can concurrently switch to this mm, because it must
> > have had a reference on mm_users before it could use_mm. mm_users can
> > be asynchronously incremented e.g., by mmget_not_zero, but those users
> > must not be doing use_mm.
> >
> > Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
> > ---
> > arch/powerpc/include/asm/mmu_context.h | 19 +++++++++
> > arch/powerpc/include/asm/tlb.h | 8 ++++
> > arch/powerpc/mm/tlb-radix.c | 57 +++++++++++++++++++-------
> > 3 files changed, 70 insertions(+), 14 deletions(-)
> >
> > diff --git a/arch/powerpc/include/asm/mmu_context.h b/arch/powerpc/include/asm/mmu_context.h
> > index 1835ca1505d6..df12a994529f 100644
> > --- a/arch/powerpc/include/asm/mmu_context.h
> > +++ b/arch/powerpc/include/asm/mmu_context.h
> > @@ -6,6 +6,7 @@
> > #include <linux/kernel.h>
> > #include <linux/mm.h>
> > #include <linux/sched.h>
> > +#include <linux/sched/mm.h>
> > #include <linux/spinlock.h>
> > #include <asm/mmu.h>
> > #include <asm/cputable.h>
> > @@ -201,6 +202,24 @@ static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next)
> > static inline void enter_lazy_tlb(struct mm_struct *mm,
> > struct task_struct *tsk)
> > {
> > +#ifdef CONFIG_PPC_BOOK3S_64
> > + /*
> > + * Under radix, we do not want to keep lazy PIDs around because
> > + * even if the CPU does not access userspace, it can still bring
> > + * in translations through speculation and prefetching.
> > + *
> > + * Switching away here allows us to trim back the mm_cpumask in
> > + * cases where we know the process is not running on some CPUs
> > + * (see mm/tlb-radix.c).
> > + */
> > + if (radix_enabled() && mm != &init_mm) {
> > + mmgrab(&init_mm);
>
> This is called when a kernel thread decides to unuse a mm, I agree switching
> to init_mm as active_mm is reasonable thing to do.
We lose lazy PIDR switching. Should probably ifdef CONFIG_SMP it, and
possibly one day we could look into having an arch notification for
scheduler migrating tasks so we could restore the optimisation.
For now I could not measure a cost, but it will be a few cycles.
>
> > + tsk->active_mm = &init_mm;
>
> Are we called with irqs disabled? Don't we need it below?
Hmm, yeah you might be right.
> > + switch_mm_irqs_off(mm, tsk->active_mm, tsk);
> > + mmdrop(mm);
> > + }
> > +#endif
> > +
> > /* 64-bit Book3E keeps track of current PGD in the PACA */
> > #ifdef CONFIG_PPC_BOOK3E_64
> > get_paca()->pgd = NULL;
> > diff --git a/arch/powerpc/include/asm/tlb.h b/arch/powerpc/include/asm/tlb.h
> > index a7eabff27a0f..006fce98c403 100644
> > --- a/arch/powerpc/include/asm/tlb.h
> > +++ b/arch/powerpc/include/asm/tlb.h
> > @@ -76,6 +76,14 @@ static inline int mm_is_thread_local(struct mm_struct *mm)
> > return false;
> > return cpumask_test_cpu(smp_processor_id(), mm_cpumask(mm));
> > }
> > +static inline void mm_reset_thread_local(struct mm_struct *mm)
> reset_thread_local --> reset_to_thread_local?
>
> > +{
> > + WARN_ON(atomic_read(&mm->context.copros) > 0);
>
> Can we put this under DEBUG_VM, VM_WARN_ON?
Yeah we could, I worry nobody tests with them on...
>
> > + WARN_ON(!(atomic_read(&mm->mm_users) == 1 && current->mm == mm));
>
>
>
> > + atomic_set(&mm->context.active_cpus, 1);
> > + cpumask_clear(mm_cpumask(mm));
> > + cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
> > +}
> > #else /* CONFIG_PPC_BOOK3S_64 */
> > static inline int mm_is_thread_local(struct mm_struct *mm)
> > {
> > diff --git a/arch/powerpc/mm/tlb-radix.c b/arch/powerpc/mm/tlb-radix.c
> > index 5ac3206c51cc..d5593a78702a 100644
> > --- a/arch/powerpc/mm/tlb-radix.c
> > +++ b/arch/powerpc/mm/tlb-radix.c
> > @@ -504,6 +504,15 @@ void radix__local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmadd
> > }
> > EXPORT_SYMBOL(radix__local_flush_tlb_page);
> >
> > +static bool mm_is_singlethreaded(struct mm_struct *mm)
> > +{
>
> mm_tlb_context_is_local?
It's not the same thing as _local. _local means only local TLBs.
_singlethreaded means only this thread can access the mappings.
> We should also skip init_mm from these checks
I don't think we should see init_mm here ever -- it has no user
mappings so won't get this user tlb flushing.
>
> > + if (atomic_read(&mm->context.copros) > 0)
> > + return false;
> > + if (atomic_read(&mm->mm_users) == 1 && current->mm == mm)
> > + return true;
> > + return false;
> > +}
> > +
> > static bool mm_needs_flush_escalation(struct mm_struct *mm)
> > {
> > /*
> > @@ -511,7 +520,9 @@ static bool mm_needs_flush_escalation(struct mm_struct *mm)
> > * caching PTEs and not flushing them properly when
> > * RIC = 0 for a PID/LPID invalidate
> > */
> > - return atomic_read(&mm->context.copros) != 0;
> > + if (atomic_read(&mm->context.copros) > 0)
> > + return true;
> > + return false;
> > }
> >
> > #ifdef CONFIG_SMP
> > @@ -525,12 +536,17 @@ void radix__flush_tlb_mm(struct mm_struct *mm)
> >
> > preempt_disable();
> > if (!mm_is_thread_local(mm)) {
> > - if (mm_needs_flush_escalation(mm))
> > + if (mm_is_singlethreaded(mm)) {
> > _tlbie_pid(pid, RIC_FLUSH_ALL);
> > - else
> > + mm_reset_thread_local(mm);
> > + } else if (mm_needs_flush_escalation(mm)) {
> > + _tlbie_pid(pid, RIC_FLUSH_ALL);
> > + } else {
> > _tlbie_pid(pid, RIC_FLUSH_TLB);
> > - } else
> > + }
> > + } else {
> > _tlbiel_pid(pid, RIC_FLUSH_TLB);
> > + }
> > preempt_enable();
> > }
> > EXPORT_SYMBOL(radix__flush_tlb_mm);
> > @@ -544,10 +560,13 @@ void radix__flush_all_mm(struct mm_struct *mm)
> > return;
> >
> > preempt_disable();
> > - if (!mm_is_thread_local(mm))
> > + if (!mm_is_thread_local(mm)) {
> > _tlbie_pid(pid, RIC_FLUSH_ALL);
> > - else
> > + if (mm_is_singlethreaded(mm))
> > + mm_reset_thread_local(mm);
> > + } else {
> > _tlbiel_pid(pid, RIC_FLUSH_ALL);
> > + }
> > preempt_enable();
> > }
> > EXPORT_SYMBOL(radix__flush_all_mm);
> > @@ -644,10 +663,14 @@ void radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
> > if (local) {
> > _tlbiel_pid(pid, RIC_FLUSH_TLB);
> > } else {
> > - if (mm_needs_flush_escalation(mm))
> > + if (mm_is_singlethreaded(mm)) {
> > + _tlbie_pid(pid, RIC_FLUSH_ALL);
> > + mm_reset_thread_local(mm);
> > + } else if (mm_needs_flush_escalation(mm)) {
> > _tlbie_pid(pid, RIC_FLUSH_ALL);
> > - else
> > + } else {
> > _tlbie_pid(pid, RIC_FLUSH_TLB);
> > + }
> > }
> > } else {
> > bool hflush = false;
> > @@ -802,13 +825,19 @@ static inline void __radix__flush_tlb_range_psize(struct mm_struct *mm,
> > }
> >
> > if (full) {
> > - if (!local && mm_needs_flush_escalation(mm))
> > - also_pwc = true;
> > -
> > - if (local)
> > + if (local) {
> > _tlbiel_pid(pid, also_pwc ? RIC_FLUSH_ALL : RIC_FLUSH_TLB);
> > - else
> > - _tlbie_pid(pid, also_pwc ? RIC_FLUSH_ALL: RIC_FLUSH_TLB);
> > + } else {
> > + if (mm_is_singlethreaded(mm)) {
> > + _tlbie_pid(pid, RIC_FLUSH_ALL);
> > + mm_reset_thread_local(mm);
> > + } else {
> > + if (mm_needs_flush_escalation(mm))
> > + also_pwc = true;
> > +
> > + _tlbie_pid(pid, also_pwc ? RIC_FLUSH_ALL : RIC_FLUSH_TLB);
> > + }
> > + }
> > } else {
> > if (local)
> > _tlbiel_va_range(start, end, pid, page_size, psize, also_pwc);
>
>
> Looks good otherwise
Thanks,
Nick
More information about the Linuxppc-dev
mailing list