From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-07-24 04:28:47
Makes switch_mm_irqs_off() a bit more readable
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/mmu_context.h | 30 ++++++++++++++++++++++--------
1 file changed, 22 insertions(+), 8 deletions(-)
@@ -77,6 +77,26 @@ extern void switch_cop(struct mm_struct *next);externintuse_cop(unsignedlongacop,structmm_struct*mm);externvoiddrop_cop(unsignedlongacop,structmm_struct*mm);+#if defined(CONFIG_PPC32)+staticinlinevoidswitch_mm_pgdir(structtask_struct*tsk,+structmm_struct*mm)+{+/* 32-bit keeps track of the current PGDIR in the thread struct */+tsk->thread.pgdir=mm->pgd;+}+#elif defined(CONFIG_PPC_BOOK3E_64)+staticinlinevoidswitch_mm_pgdir(structtask_struct*tsk,+structmm_struct*mm)+{+/* 64-bit Book3E keeps track of current PGD in the PACA */+get_paca()->pgd=mm->pgd;+}+#else+staticinlinevoidswitch_mm_pgdir(structtask_struct*tsk,+structmm_struct*mm){}+#endif++/**switch_mmistheentrypointcalledfromthearchitectureindependent*codeinkernel/sched/core.c
@@ -93,15 +113,9 @@ static inline void switch_mm_irqs_off(struct mm_struct *prev,new_on_cpu=true;}-/* 32-bit keeps track of the current PGDIR in the thread struct */-#ifdef CONFIG_PPC32-tsk->thread.pgdir=next->pgd;-#endif /* CONFIG_PPC32 */+/* Some subarchs need to track the PGD elsewhere */+switch_mm_pgdir(tsk,next);-/* 64-bit Book3E keeps track of current PGD in the PACA */-#ifdef CONFIG_PPC_BOOK3E_64-get_paca()->pgd=next->pgd;-#endif/* Nothing else to do if we aren't actually switching */if(prev==next)return;
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-07-24 04:28:40
It's too big to be inline, there is no reason to keep it
that way.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
# Conflicts:
# arch/powerpc/include/asm/mmu_context.h
---
arch/powerpc/include/asm/mmu_context.h | 73 ++----------------------------
arch/powerpc/mm/Makefile | 2 +-
arch/powerpc/mm/mmu_context.c | 81 ++++++++++++++++++++++++++++++++++
3 files changed, 85 insertions(+), 71 deletions(-)
create mode 100644 arch/powerpc/mm/mmu_context.c
@@ -77,76 +77,9 @@ extern void switch_cop(struct mm_struct *next);externintuse_cop(unsignedlongacop,structmm_struct*mm);externvoiddrop_cop(unsignedlongacop,structmm_struct*mm);-#if defined(CONFIG_PPC32)-staticinlinevoidswitch_mm_pgdir(structtask_struct*tsk,-structmm_struct*mm)-{-/* 32-bit keeps track of the current PGDIR in the thread struct */-tsk->thread.pgdir=mm->pgd;-}-#elif defined(CONFIG_PPC_BOOK3E_64)-staticinlinevoidswitch_mm_pgdir(structtask_struct*tsk,-structmm_struct*mm)-{-/* 64-bit Book3E keeps track of current PGD in the PACA */-get_paca()->pgd=mm->pgd;-}-#else-staticinlinevoidswitch_mm_pgdir(structtask_struct*tsk,-structmm_struct*mm){}-#endif--#ifdef CONFIG_PPC_BOOK3S_64-staticinlinevoidinc_mm_active_cpus(structmm_struct*mm)-{-atomic_inc(&mm->context.active_cpus);-}-#else-staticinlinevoidinc_mm_active_cpus(structmm_struct*mm){}-#endif--/*-*switch_mmistheentrypointcalledfromthearchitectureindependent-*codeinkernel/sched/core.c-*/-staticinlinevoidswitch_mm_irqs_off(structmm_struct*prev,-structmm_struct*next,-structtask_struct*tsk)-{-boolnew_on_cpu=false;--/* Mark this context has been used on the new CPU */-if(!cpumask_test_cpu(smp_processor_id(),mm_cpumask(next))){-cpumask_set_cpu(smp_processor_id(),mm_cpumask(next));-inc_mm_active_cpus(next);-smp_mb();-new_on_cpu=true;-}--/* Some subarchs need to track the PGD elsewhere */-switch_mm_pgdir(tsk,next);--/* Nothing else to do if we aren't actually switching */-if(prev==next)-return;--/* We must stop all altivec streams before changing the HW-*context-*/-#ifdef CONFIG_ALTIVEC-if(cpu_has_feature(CPU_FTR_ALTIVEC))-asmvolatile("dssall");-#endif /* CONFIG_ALTIVEC */--if(new_on_cpu)-radix_kvm_prefetch_workaround(next);--/*-*TheactualHWswitchingmethoddiffersbetweenthevarious-*subarchitectures.Outoflinefornow-*/-switch_mmu_context(prev,next,tsk);-}+externvoidswitch_mm_irqs_off(structmm_struct*prev,+structmm_struct*next,+structtask_struct*tsk);staticinlinevoidswitch_mm(structmm_struct*prev,structmm_struct*next,structtask_struct*tsk)
@@ -0,0 +1,81 @@+/*+*Commonimplementationofswitch_mm_irqs_off+*+*CopyrightIBMCorp.2017+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicense+*aspublishedbytheFreeSoftwareFoundation;eitherversion+*2oftheLicense,or(atyouroption)anylaterversion.+*+*/++#include<linux/mm.h>+#include<linux/cpu.h>++#include<asm/mmu_context.h>++#if defined(CONFIG_PPC32)+staticinlinevoidswitch_mm_pgdir(structtask_struct*tsk,+structmm_struct*mm)+{+/* 32-bit keeps track of the current PGDIR in the thread struct */+tsk->thread.pgdir=mm->pgd;+}+#elif defined(CONFIG_PPC_BOOK3E_64)+staticinlinevoidswitch_mm_pgdir(structtask_struct*tsk,+structmm_struct*mm)+{+/* 64-bit Book3E keeps track of current PGD in the PACA */+get_paca()->pgd=mm->pgd;+}+#else+staticinlinevoidswitch_mm_pgdir(structtask_struct*tsk,+structmm_struct*mm){}+#endif++#ifdef CONFIG_PPC_BOOK3S_64+staticinlinevoidinc_mm_active_cpus(structmm_struct*mm)+{+atomic_inc(&mm->context.active_cpus);+}+#else+staticinlinevoidinc_mm_active_cpus(structmm_struct*mm){}+#endif++voidswitch_mm_irqs_off(structmm_struct*prev,structmm_struct*next,+structtask_struct*tsk)+{+boolnew_on_cpu=false;++/* Mark this context has been used on the new CPU */+if(!cpumask_test_cpu(smp_processor_id(),mm_cpumask(next))){+cpumask_set_cpu(smp_processor_id(),mm_cpumask(next));+inc_mm_active_cpus(next);+smp_mb();+new_on_cpu=true;+}++/* Some subarchs need to track the PGD elsewhere */+switch_mm_pgdir(tsk,next);++/* Nothing else to do if we aren't actually switching */+if(prev==next)+return;++/* We must stop all altivec streams before changing the HW+*context+*/+if(cpu_has_feature(CPU_FTR_ALTIVEC))+asmvolatile("dssall");++if(new_on_cpu)+radix_kvm_prefetch_workaround(next);++/*+*TheactualHWswitchingmethoddiffersbetweenthevarious+*subarchitectures.Outoflinefornow+*/+switch_mmu_context(prev,next,tsk);+}+
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-07-24 04:28:45
We open-code testing for the mm being local to the current CPU
in a few places. Use our existing helper instead.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/mm/hash_utils_64.c | 6 ++----
arch/powerpc/mm/hugetlbpage.c | 3 +--
arch/powerpc/mm/pgtable-hash64.c | 4 +---
arch/powerpc/mm/tlb_hash64.c | 7 ++-----
4 files changed, 6 insertions(+), 14 deletions(-)
@@ -1228,7 +1228,6 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,unsignedlongvsid;pte_t*ptep;unsignedhugeshift;-conststructcpumask*tmp;intrc,user_region=0;intpsize,ssize;
@@ -1280,8 +1279,7 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,}/* Check CPU locality */-tmp=cpumask_of(smp_processor_id());-if(user_region&&cpumask_equal(mm_cpumask(mm),tmp))+if(user_region&&mm_is_thread_local(mm))flags|=HPTE_LOCAL_UPDATE;#ifndef CONFIG_PPC_64K_PAGES
@@ -1543,7 +1541,7 @@ void hash_preload(struct mm_struct *mm, unsigned long ea,#endif /* CONFIG_PPC_64K_PAGES *//* Is that local to this CPU ? */-if(cpumask_equal(mm_cpumask(mm),cpumask_of(smp_processor_id())))+if(mm_is_thread_local(mm))update_flags|=HPTE_LOCAL_UPDATE;/* Hash it in */
@@ -329,7 +329,6 @@ void hpte_do_hugepage_flush(struct mm_struct *mm, unsigned long addr,unsignedintpsize;unsignedlongvsid;unsignedlongflags=0;-conststructcpumask*tmp;/* get the base page size,vsid and segment size */#ifdef CONFIG_DEBUG_VM
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-07-24 04:28:45
It calls switch_mm() which already does the irq save/restore
these days.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/mmu_context.h | 4 ----
1 file changed, 4 deletions(-)
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-07-24 04:28:47
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/book3s/64/mmu.h | 3 +++
arch/powerpc/include/asm/mmu_context.h | 9 +++++++++
arch/powerpc/include/asm/tlb.h | 11 ++++++++++-
arch/powerpc/mm/mmu_context_book3s64.c | 2 ++
4 files changed, 24 insertions(+), 1 deletion(-)
@@ -83,6 +83,9 @@ typedef struct {mm_context_id_tid;u16user_psize;/* page size index */+/* Number of bits in the mm_cpumask */+atomic_tactive_cpus;+/* NPU NMMU context */structnpu_context*npu_context;
@@ -110,6 +118,7 @@ static inline void switch_mm_irqs_off(struct mm_struct *prev,/* Mark this context has been used on the new CPU */if(!cpumask_test_cpu(smp_processor_id(),mm_cpumask(next))){cpumask_set_cpu(smp_processor_id(),mm_cpumask(next));+inc_mm_active_cpus(next);smp_mb();new_on_cpu=true;}
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-07-24 04:30:07
There is no guarantee that the various isync's involved with
the context switch will order the update of the CPU mask with
the first TLB entry for the new context being loaded by the HW.
Be safe here and add a memory barrier to order any subsequent
load/store which may bring entries into the TLB.
The corresponding barrier on the other side already exists as
pte updates use pte_xchg() which uses __cmpxchg_u64 which has
a sync after the atomic operation.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/mmu_context.h | 1 +
1 file changed, 1 insertion(+)
@@ -110,6 +110,7 @@ static inline void switch_mm_irqs_off(struct mm_struct *prev,/* Mark this context has been used on the new CPU */if(!cpumask_test_cpu(smp_processor_id(),mm_cpumask(next))){cpumask_set_cpu(smp_processor_id(),mm_cpumask(next));+smp_mb();new_on_cpu=true;}
From: Nicholas Piggin <npiggin@gmail.com> Date: 2017-07-24 11:20:30
On Mon, 24 Jul 2017 14:28:00 +1000
Benjamin Herrenschmidt [off-list ref] wrote:
quoted hunk
There is no guarantee that the various isync's involved with
the context switch will order the update of the CPU mask with
the first TLB entry for the new context being loaded by the HW.
Be safe here and add a memory barrier to order any subsequent
load/store which may bring entries into the TLB.
The corresponding barrier on the other side already exists as
pte updates use pte_xchg() which uses __cmpxchg_u64 which has
a sync after the atomic operation.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/mmu_context.h | 1 +
1 file changed, 1 insertion(+)
@@ -110,6 +110,7 @@ static inline void switch_mm_irqs_off(struct mm_struct *prev,/* Mark this context has been used on the new CPU */if(!cpumask_test_cpu(smp_processor_id(),mm_cpumask(next))){cpumask_set_cpu(smp_processor_id(),mm_cpumask(next));+smp_mb();new_on_cpu=true;}
I think this is the right thing to do, but it should be commented.
Is hwsync the right barrier? (i.e., it will order the page table walk)
Thanks,
Nick
From: Nicholas Piggin <npiggin@gmail.com> Date: 2017-07-24 11:25:50
On Mon, 24 Jul 2017 14:28:02 +1000
Benjamin Herrenschmidt [off-list ref] wrote:
quoted hunk
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/book3s/64/mmu.h | 3 +++
arch/powerpc/include/asm/mmu_context.h | 9 +++++++++
arch/powerpc/include/asm/tlb.h | 11 ++++++++++-
arch/powerpc/mm/mmu_context_book3s64.c | 2 ++
4 files changed, 24 insertions(+), 1 deletion(-)
@@ -83,6 +83,9 @@ typedef struct {mm_context_id_tid;u16user_psize;/* page size index */+/* Number of bits in the mm_cpumask */+atomic_tactive_cpus;+/* NPU NMMU context */structnpu_context*npu_context;
This is a bit awkward. Can we just move the entire function to test
cpumask and set / increment into helper functions and define them
together with mm_is_thread_local, so it's all in one place?
The extra atomic does not need to be defined when it's not used either.
Also does it make sense to define it based on NR_CPUS > BITS_PER_LONG?
If it's <= then it should be similar load and compare, no?
Looks like a good optimisation though.
Thanks,
Nick
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2017-07-24 13:46:45
Nicholas Piggin [off-list ref] writes:
On Mon, 24 Jul 2017 14:28:02 +1000
Benjamin Herrenschmidt [off-list ref] wrote:
quoted
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
...
Also does it make sense to define it based on NR_CPUS > BITS_PER_LONG?
If it's <= then it should be similar load and compare, no?
Do we make a machine with that few CPUs? ;)
I don't think it's worth special casing, all the distros run with much
much larger NR_CPUs than that.
cheers
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-07-24 20:54:33
On Mon, 2017-07-24 at 21:20 +1000, Nicholas Piggin wrote:
I think this is the right thing to do, but it should be commented.
Is hwsync the right barrier? (i.e., it will order the page table walk)
This is an open question, I've asked the architects and HW guys and
waiting for an answer.
That said, are we really trying to order the page table walk or are
we trying to order any (speculative or not) load/store that may trigger
a page table update ?
Cheers,
Ben.
This is a bit awkward. Can we just move the entire function to test
cpumask and set / increment into helper functions and define them
together with mm_is_thread_local, so it's all in one place?
I thought about it but then we have 2 variants, unless I start moving
the active_cpus into mm_context_t on all the 32-bit subarchs too, etc..
It gets messy either way.
The extra atomic does not need to be defined when it's not used either.
Also does it make sense to define it based on NR_CPUS > BITS_PER_LONG?
If it's <= then it should be similar load and compare, no?
Right, we could.
Looks like a good optimisation though.
Thx. It's a pre-req for further optimizations such as flushing the PID
when a single threaded process moves, so we don't have to constantly
scan the mask.
Cheers,
Ben.
From: Nicholas Piggin <npiggin@gmail.com> Date: 2017-07-25 00:34:54
On Mon, 24 Jul 2017 23:46:44 +1000
Michael Ellerman [off-list ref] wrote:
Nicholas Piggin [off-list ref] writes:
quoted
On Mon, 24 Jul 2017 14:28:02 +1000
Benjamin Herrenschmidt [off-list ref] wrote:
quoted
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
...
quoted
Also does it make sense to define it based on NR_CPUS > BITS_PER_LONG?
If it's <= then it should be similar load and compare, no?
Do we make a machine with that few CPUs? ;)
I don't think it's worth special casing, all the distros run with much
much larger NR_CPUs than that.
Not further special-casing, but just casing it based on NR_CPUS
rather than BOOK3S.
Thanks,
Nick
This is a bit awkward. Can we just move the entire function to test
cpumask and set / increment into helper functions and define them
together with mm_is_thread_local, so it's all in one place?
I thought about it but then we have 2 variants, unless I start moving
the active_cpus into mm_context_t on all the 32-bit subarchs too, etc..
The two variants are just cleaner versions of the two variants you
already introduced.
static inline bool mm_activate_cpu(struct mm_struct *mm)
{
if (!cpumask_test_cpu(smp_processor_id(), mm_cpumask(next))) {
cpumask_set_cpu(smp_processor_id(), mm_cpumask(next));
#if CONFIG_PPC_BOOK3S_64
atomic_inc(&mm->context.active_cpus);
#endif
smp_mb();
return true;
}
return false;
}
I think it would be nicer to put something like that with
mm_is_thread_local etc definitions so you can see how it all works
in one place.
It gets messy either way.
quoted
The extra atomic does not need to be defined when it's not used either.
Also does it make sense to define it based on NR_CPUS > BITS_PER_LONG?
If it's <= then it should be similar load and compare, no?
Right, we could.
quoted
Looks like a good optimisation though.
Thx. It's a pre-req for further optimizations such as flushing the PID
when a single threaded process moves, so we don't have to constantly
scan the mask.
Yep, will be very interesting to see how much global tlbies can be
reduced.
Thanks,
Nick
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-07-25 01:04:13
On Tue, 2017-07-25 at 10:44 +1000, Nicholas Piggin wrote:
The two variants are just cleaner versions of the two variants you
already introduced.
static inline bool mm_activate_cpu(struct mm_struct *mm)
{
if (!cpumask_test_cpu(smp_processor_id(), mm_cpumask(next))) {
cpumask_set_cpu(smp_processor_id(), mm_cpumask(next));
#if CONFIG_PPC_BOOK3S_64
atomic_inc(&mm->context.active_cpus);
#endif
smp_mb();
return true;
}
return false;
}
Well the above is what I originally wrote, which Michael encouraged me
to turn into a helper ;-) I was removing ifdef's from switch_mm in
this series...
I think it would be nicer to put something like that with
mm_is_thread_local etc definitions so you can see how it all works
in one place.
quoted
It gets messy either way.
quoted
The extra atomic does not need to be defined when it's not used either.
Also does it make sense to define it based on NR_CPUS > BITS_PER_LONG?
If it's <= then it should be similar load and compare, no?
Right, we could.
quoted
Looks like a good optimisation though.
Thx. It's a pre-req for further optimizations such as flushing the PID
when a single threaded process moves, so we don't have to constantly
scan the mask.
Yep, will be very interesting to see how much global tlbies can be
reduced.
Thanks,
Nick
From: Nicholas Piggin <npiggin@gmail.com> Date: 2017-07-25 10:55:21
On Tue, 25 Jul 2017 11:03:45 +1000
Benjamin Herrenschmidt [off-list ref] wrote:
On Tue, 2017-07-25 at 10:44 +1000, Nicholas Piggin wrote:
quoted
The two variants are just cleaner versions of the two variants you
already introduced.
static inline bool mm_activate_cpu(struct mm_struct *mm)
{
if (!cpumask_test_cpu(smp_processor_id(), mm_cpumask(next))) {
cpumask_set_cpu(smp_processor_id(), mm_cpumask(next));
#if CONFIG_PPC_BOOK3S_64
atomic_inc(&mm->context.active_cpus);
#endif
smp_mb();
return true;
}
return false;
}
Well the above is what I originally wrote, which Michael encouraged me
to turn into a helper ;-) I was removing ifdef's from switch_mm in
this series...
Well I won't harp on about it if you guys prefer the increment helper.
Just the comment would be good. The rest of the series seems okay to
me.
Thanks,
Nick
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2017-07-25 12:01:00
Nicholas Piggin [off-list ref] writes:
On Mon, 24 Jul 2017 23:46:44 +1000
Michael Ellerman [off-list ref] wrote:
quoted
Nicholas Piggin [off-list ref] writes:
quoted
On Mon, 24 Jul 2017 14:28:02 +1000
Benjamin Herrenschmidt [off-list ref] wrote:
quoted
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
...
quoted
Also does it make sense to define it based on NR_CPUS > BITS_PER_LONG?
If it's <= then it should be similar load and compare, no?
Do we make a machine with that few CPUs? ;)
I don't think it's worth special casing, all the distros run with much
much larger NR_CPUs than that.
Not further special-casing, but just casing it based on NR_CPUS
rather than BOOK3S.
The problem is the mm_context_t is defined based on BookE vs BookS etc.
not based on NR_CPUS.
So we'd have to add the atomic_t to all mm_context_t's, but #ifdef'ed
based on NR_CPUS.
But then some platforms don't support SMP, so it's a waste there. The
existing cpumask check compiles to ~= nothing on UP.
cheers
Le 24/07/2017 à 06:28, Benjamin Herrenschmidt a écrit :
quoted hunk
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/book3s/64/mmu.h | 3 +++
arch/powerpc/include/asm/mmu_context.h | 9 +++++++++
arch/powerpc/include/asm/tlb.h | 11 ++++++++++-
arch/powerpc/mm/mmu_context_book3s64.c | 2 ++
4 files changed, 24 insertions(+), 1 deletion(-)
@@ -83,6 +83,9 @@ typedef struct {mm_context_id_tid;u16user_psize;/* page size index */+/* Number of bits in the mm_cpumask */+atomic_tactive_cpus;+/* NPU NMMU context */structnpu_context*npu_context;
@@ -110,6 +118,7 @@ static inline void switch_mm_irqs_off(struct mm_struct *prev,/* Mark this context has been used on the new CPU */if(!cpumask_test_cpu(smp_processor_id(),mm_cpumask(next))){cpumask_set_cpu(smp_processor_id(),mm_cpumask(next));+inc_mm_active_cpus(next);smp_mb();new_on_cpu=true;}
While working on something related (mark memory context as needing
global TLBI if used behind a NPU or PSL):
http://patchwork.ozlabs.org/patch/796775/
Michael raised the point that the store for the pte update cannot be
reordered with the load which decides the scope of the TLBI, and had
convinced me that a memory barrier was required.
Couldn't we have the same problem here, where the atomic read is
reordered with the store of the invalid PTE?
Thanks,
Fred
While working on something related (mark memory context as needing
global TLBI if used behind a NPU or PSL):
http://patchwork.ozlabs.org/patch/796775/
Michael raised the point that the store for the pte update cannot be
reordered with the load which decides the scope of the TLBI, and had
convinced me that a memory barrier was required.
Couldn't we have the same problem here, where the atomic read is
reordered with the store of the invalid PTE?
The store of the invalid PTE is done with a pte_update which contains a
sync as far as I can tell.
Cheers,
Ben.
From: Nicholas Piggin <npiggin@gmail.com> Date: 2017-08-11 11:07:15
On Mon, 24 Jul 2017 21:20:07 +1000
Nicholas Piggin [off-list ref] wrote:
On Mon, 24 Jul 2017 14:28:00 +1000
Benjamin Herrenschmidt [off-list ref] wrote:
quoted
There is no guarantee that the various isync's involved with
the context switch will order the update of the CPU mask with
the first TLB entry for the new context being loaded by the HW.
Be safe here and add a memory barrier to order any subsequent
load/store which may bring entries into the TLB.
The corresponding barrier on the other side already exists as
pte updates use pte_xchg() which uses __cmpxchg_u64 which has
a sync after the atomic operation.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/mmu_context.h | 1 +
1 file changed, 1 insertion(+)
@@ -110,6 +110,7 @@ static inline void switch_mm_irqs_off(struct mm_struct *prev,/* Mark this context has been used on the new CPU */if(!cpumask_test_cpu(smp_processor_id(),mm_cpumask(next))){cpumask_set_cpu(smp_processor_id(),mm_cpumask(next));+smp_mb();new_on_cpu=true;}
I think this is the right thing to do, but it should be commented.
Is hwsync the right barrier? (i.e., it will order the page table walk)
After some offline discussion, I think we have an agreement that
this is the right barrier, as it orders with the subsequent load
of next->context.id that the mtpid depends on (or slbmte for HPT).
So we should have a comment here to that effect, and including
the pte_xchg comments from your changelog. Some comment (at least
refer back to here) added at pte_xchg too please.
Other than that your series seems good to me if you repost it you
can add
Reviewed-by: Nicholas Piggin <npiggin@gmail.com>
This one out of the series is the bugfix so it should go to stable
as well, right?
Thanks,
Nick
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-08-11 22:41:04
On Fri, 2017-08-11 at 21:06 +1000, Nicholas Piggin wrote:
Other than that your series seems good to me if you repost it you
can add
Reviewed-by: Nicholas Piggin <npiggin@gmail.com>
This one out of the series is the bugfix so it should go to stable
as well, right?
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2017-08-17 12:58:11
Nicholas Piggin [off-list ref] writes:
On Mon, 24 Jul 2017 21:20:07 +1000
Nicholas Piggin [off-list ref] wrote:
quoted
On Mon, 24 Jul 2017 14:28:00 +1000
Benjamin Herrenschmidt [off-list ref] wrote:
quoted
There is no guarantee that the various isync's involved with
the context switch will order the update of the CPU mask with
the first TLB entry for the new context being loaded by the HW.
Be safe here and add a memory barrier to order any subsequent
load/store which may bring entries into the TLB.
The corresponding barrier on the other side already exists as
pte updates use pte_xchg() which uses __cmpxchg_u64 which has
a sync after the atomic operation.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/mmu_context.h | 1 +
1 file changed, 1 insertion(+)
@@ -110,6 +110,7 @@ static inline void switch_mm_irqs_off(struct mm_struct *prev,/* Mark this context has been used on the new CPU */if(!cpumask_test_cpu(smp_processor_id(),mm_cpumask(next))){cpumask_set_cpu(smp_processor_id(),mm_cpumask(next));+smp_mb();new_on_cpu=true;}
I think this is the right thing to do, but it should be commented.
Is hwsync the right barrier? (i.e., it will order the page table walk)
After some offline discussion, I think we have an agreement that
this is the right barrier, as it orders with the subsequent load
of next->context.id that the mtpid depends on (or slbmte for HPT).
So we should have a comment here to that effect, and including
the pte_xchg comments from your changelog. Some comment (at least
refer back to here) added at pte_xchg too please.
Other than that your series seems good to me if you repost it you
can add
Reviewed-by: Nicholas Piggin <npiggin@gmail.com>
This one out of the series is the bugfix so it should go to stable
as well, right?
Hi Ben,
Le 24/07/2017 à 06:28, Benjamin Herrenschmidt a écrit :
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
I'm trying to see if we could merge this patch with what I'm trying to
do to mark a context as requiring global TLBIs.
In http://patchwork.ozlabs.org/patch/796775/
I'm introducing a 'flags' per memory context, using one bit to say if
the context needs global TLBIs.
The 2 could co-exist, just checking... Do you think about using the
actual active_cpus count down the road, or is it just a matter of
knowing if there are more than one active cpus?
Thanks,
Fred
@@ -83,6 +83,9 @@ typedef struct {mm_context_id_tid;u16user_psize;/* page size index */+/* Number of bits in the mm_cpumask */+atomic_tactive_cpus;+/* NPU NMMU context */structnpu_context*npu_context;
@@ -110,6 +118,7 @@ static inline void switch_mm_irqs_off(struct mm_struct *prev,/* Mark this context has been used on the new CPU */if(!cpumask_test_cpu(smp_processor_id(),mm_cpumask(next))){cpumask_set_cpu(smp_processor_id(),mm_cpumask(next));+inc_mm_active_cpus(next);smp_mb();new_on_cpu=true;}
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-08-21 17:35:48
On Mon, 2017-08-21 at 19:27 +0200, Frederic Barrat wrote:
Hi Ben,
Le 24/07/2017 à 06:28, Benjamin Herrenschmidt a écrit :
quoted
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
I'm trying to see if we could merge this patch with what I'm trying to
do to mark a context as requiring global TLBIs.
In http://patchwork.ozlabs.org/patch/796775/
I'm introducing a 'flags' per memory context, using one bit to say if
the context needs global TLBIs.
The 2 could co-exist, just checking... Do you think about using the
actual active_cpus count down the road, or is it just a matter of
knowing if there are more than one active cpus?
Or you could just incrementer my counter. Just make sure you increment
it at most once per CXL context and decrement when the context is gone.
Cheers,
Ben.
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2017-08-22 04:28:39
Frederic Barrat [off-list ref] writes:
Hi Ben,
Le 24/07/2017 =C3=A0 06:28, Benjamin Herrenschmidt a =C3=A9crit :
quoted
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
I'm trying to see if we could merge this patch with what I'm trying to=20
do to mark a context as requiring global TLBIs.
In http://patchwork.ozlabs.org/patch/796775/
I'm introducing a 'flags' per memory context, using one bit to say if=20
the context needs global TLBIs.
The 2 could co-exist, just checking... Do you think about using the=20
actual active_cpus count down the road, or is it just a matter of=20
knowing if there are more than one active cpus?
Currently it's just an optimisation to save comparing the full cpumask
every time to detect if we can go local vs broadcast.
So if you increment it then it will mean we do broadcast, which is what
you need.
It's possible in future we might try to do something more complicated,
like send targeted IPIs etc. But if we ever do that we can adapt CXL
then.
cheers
Le 21/08/2017 à 19:35, Benjamin Herrenschmidt a écrit :
On Mon, 2017-08-21 at 19:27 +0200, Frederic Barrat wrote:
quoted
Hi Ben,
Le 24/07/2017 à 06:28, Benjamin Herrenschmidt a écrit :
quoted
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
I'm trying to see if we could merge this patch with what I'm trying to
do to mark a context as requiring global TLBIs.
In http://patchwork.ozlabs.org/patch/796775/
I'm introducing a 'flags' per memory context, using one bit to say if
the context needs global TLBIs.
The 2 could co-exist, just checking... Do you think about using the
actual active_cpus count down the road, or is it just a matter of
knowing if there are more than one active cpus?
Or you could just incrementer my counter. Just make sure you increment
it at most once per CXL context and decrement when the context is gone.
Ah great, I didn't dare messing with your counter, it makes it easier.
Arguably what happens on those accelerators is pretty close to an active
cpu.
Once it is merged, I'm going to have to backport your patch (and an
update to mine) to the p9-supporting distros. From a quick look, your
patch, i.e."[PATCH 5/6] powerpc/mm: Optimize detection of thread local
mm's" is completely independent from the rest of the series, right?
Fred
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-08-22 16:55:37
On Tue, 2017-08-22 at 15:18 +0200, Frederic Barrat wrote:
quoted
Or you could just incrementer my counter. Just make sure you increment
it at most once per CXL context and decrement when the context is gone.
Ah great, I didn't dare messing with your counter, it makes it easier.
Arguably what happens on those accelerators is pretty close to an active
cpu.
Once it is merged, I'm going to have to backport your patch (and an
update to mine) to the p9-supporting distros. From a quick look, your
patch, i.e."[PATCH 5/6] powerpc/mm: Optimize detection of thread local
mm's" is completely independent from the rest of the series, right?
We also need the memory barrier fix but yes.
Cheers,
Ben.
From: Michael Ellerman <hidden> Date: 2017-08-23 12:01:52
On Mon, 2017-07-24 at 04:28:00 UTC, Benjamin Herrenschmidt wrote:
There is no guarantee that the various isync's involved with
the context switch will order the update of the CPU mask with
the first TLB entry for the new context being loaded by the HW.
Be safe here and add a memory barrier to order any subsequent
load/store which may bring entries into the TLB.
The corresponding barrier on the other side already exists as
pte updates use pte_xchg() which uses __cmpxchg_u64 which has
a sync after the atomic operation.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Reviewed-by: Nicholas Piggin <npiggin@gmail.com>
Le 21/08/2017 à 19:35, Benjamin Herrenschmidt a écrit :
On Mon, 2017-08-21 at 19:27 +0200, Frederic Barrat wrote:
quoted
Hi Ben,
Le 24/07/2017 à 06:28, Benjamin Herrenschmidt a écrit :
quoted
Instead of comparing the whole CPU mask every time, let's
keep a counter of how many bits are set in the mask. Thus
testing for a local mm only requires testing if that counter
is 1 and the current CPU bit is set in the mask.
I'm trying to see if we could merge this patch with what I'm trying to
do to mark a context as requiring global TLBIs.
In http://patchwork.ozlabs.org/patch/796775/
I'm introducing a 'flags' per memory context, using one bit to say if
the context needs global TLBIs.
The 2 could co-exist, just checking... Do you think about using the
actual active_cpus count down the road, or is it just a matter of
knowing if there are more than one active cpus?
Or you could just incrementer my counter. Just make sure you increment
it at most once per CXL context and decrement when the context is gone.
The decrementing part is giving me troubles, and I think it makes sense:
if I decrement the counter when detaching the context from the capi
card, then the next TLBIs for the memory context may be back to local.
So when the process exits, the NPU wouldn't get the associated TLBIs,
which spells trouble the next time the same memory context ID is reused.
I believe this the cause of the problem I'm seeing. As soon as I keep
the TLBIs global, even after I detach from the capi adapter, everything
is fine.
Does it sound right?
So to keep the checks minimal in mm_is_thread_local(), to just checking
the active_cpus count, I'm thinking of introducing a "copro enabled" bit
on the context, so that we can increment active_cpus only once. And
never decrement it.
Fred
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-08-24 18:48:13
On Thu, 2017-08-24 at 18:40 +0200, Frederic Barrat wrote:
The decrementing part is giving me troubles, and I think it makes sense:
if I decrement the counter when detaching the context from the capi
card, then the next TLBIs for the memory context may be back to local.
Yes, you need to flush the CAPI TLB first.
So when the process exits, the NPU wouldn't get the associated TLBIs,
which spells trouble the next time the same memory context ID is reused.
I believe this the cause of the problem I'm seeing. As soon as I keep
the TLBIs global, even after I detach from the capi adapter, everything
is fine.
Does it sound right?
So to keep the checks minimal in mm_is_thread_local(), to just checking
the active_cpus count, I'm thinking of introducing a "copro enabled" bit
on the context, so that we can increment active_cpus only once. And
never decrement it.
You can decrement if you flush. Don't you have MMIOs to do directed
flushes ?
Cheers,
Ben.
Le 24/08/2017 à 20:47, Benjamin Herrenschmidt a écrit :
On Thu, 2017-08-24 at 18:40 +0200, Frederic Barrat wrote:
quoted
The decrementing part is giving me troubles, and I think it makes sense:
if I decrement the counter when detaching the context from the capi
card, then the next TLBIs for the memory context may be back to local.
Yes, you need to flush the CAPI TLB first.
quoted
So when the process exits, the NPU wouldn't get the associated TLBIs,
which spells trouble the next time the same memory context ID is reused.
I believe this the cause of the problem I'm seeing. As soon as I keep
the TLBIs global, even after I detach from the capi adapter, everything
is fine.
Does it sound right?
So to keep the checks minimal in mm_is_thread_local(), to just checking
the active_cpus count, I'm thinking of introducing a "copro enabled" bit
on the context, so that we can increment active_cpus only once. And
never decrement it.
You can decrement if you flush. Don't you have MMIOs to do directed
flushes ?
That's for the nMMU. Last I heard, we don't have MMIOs to flush anything
on the nMMU.
Side note: for the PSL, we do have MMIOs to flush, but they were
perceived as useful only for debug and we don't rely on them, precisely
because the nMMU would fall out of sync, so we have to rely on broadcast.
Fred
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2017-08-25 07:44:48
On Fri, 2017-08-25 at 06:53 +0200, Frederic Barrat wrote:
Le 24/08/2017 à 20:47, Benjamin Herrenschmidt a écrit :
quoted
On Thu, 2017-08-24 at 18:40 +0200, Frederic Barrat wrote:
quoted
The decrementing part is giving me troubles, and I think it makes sense:
if I decrement the counter when detaching the context from the capi
card, then the next TLBIs for the memory context may be back to local.
Yes, you need to flush the CAPI TLB first.
quoted
So when the process exits, the NPU wouldn't get the associated TLBIs,
which spells trouble the next time the same memory context ID is reused.
I believe this the cause of the problem I'm seeing. As soon as I keep
the TLBIs global, even after I detach from the capi adapter, everything
is fine.
Does it sound right?
So to keep the checks minimal in mm_is_thread_local(), to just checking
the active_cpus count, I'm thinking of introducing a "copro enabled" bit
on the context, so that we can increment active_cpus only once. And
never decrement it.
You can decrement if you flush. Don't you have MMIOs to do directed
flushes ?
That's for the nMMU. Last I heard, we don't have MMIOs to flush anything
on the nMMU.
Side note: for the PSL, we do have MMIOs to flush, but they were
perceived as useful only for debug and we don't rely on them, precisely
because the nMMU would fall out of sync, so we have to rely on broadcast.
Well, you can always do a broadcast tlbi to flush the whole PID if you
decrement... that shouldn't be a very frequent operation.
Ben.
Le 25/08/2017 à 09:44, Benjamin Herrenschmidt a écrit :
On Fri, 2017-08-25 at 06:53 +0200, Frederic Barrat wrote:
quoted
Le 24/08/2017 à 20:47, Benjamin Herrenschmidt a écrit :
quoted
On Thu, 2017-08-24 at 18:40 +0200, Frederic Barrat wrote:
quoted
The decrementing part is giving me troubles, and I think it makes sense:
if I decrement the counter when detaching the context from the capi
card, then the next TLBIs for the memory context may be back to local.
Yes, you need to flush the CAPI TLB first.
quoted
So when the process exits, the NPU wouldn't get the associated TLBIs,
which spells trouble the next time the same memory context ID is reused.
I believe this the cause of the problem I'm seeing. As soon as I keep
the TLBIs global, even after I detach from the capi adapter, everything
is fine.
Does it sound right?
So to keep the checks minimal in mm_is_thread_local(), to just checking
the active_cpus count, I'm thinking of introducing a "copro enabled" bit
on the context, so that we can increment active_cpus only once. And
never decrement it.
You can decrement if you flush. Don't you have MMIOs to do directed
flushes ?
That's for the nMMU. Last I heard, we don't have MMIOs to flush anything
on the nMMU.
Side note: for the PSL, we do have MMIOs to flush, but they were
perceived as useful only for debug and we don't rely on them, precisely
because the nMMU would fall out of sync, so we have to rely on broadcast.
Well, you can always do a broadcast tlbi to flush the whole PID if you
decrement... that shouldn't be a very frequent operation.