From: Chirag Jog <hidden> Date: 2008-07-10 03:33:47
Hi,
This patch fixes various paths in the -rt kernel on powerpc64 where per_cpu
variables are accessed in a preempt unsafe way.
When a power box with -rt kernel is booted, multiple BUG messages are
generated "BUG: init:1 task might have lost a preemption check!".
After booting a kernel with these patches applied, these messages
don't appear.
Also I ran the realtime tests from ltp to ensure the stability.
Signed-Off-By: Chirag <redacted>
arch/powerpc/mm/tlb_64.c | 31 ++++++++++++++++---------------
arch/powerpc/platforms/pseries/iommu.c | 14 ++++++++++----
include/asm-powerpc/tlb.h | 5 ++---
3 files changed, 28 insertions(+), 22 deletions(-)
Index: linux-2.6.25.8-rt7/arch/powerpc/mm/tlb_64.c
===================================================================
@@ -253,13 +252,15 @@voidpte_free_finish(void){-/* This is safe since tlb_gather_mmu has disabled preemption */-structpte_freelist_batch**batchp=&__get_cpu_var(pte_freelist_cur);+intcpu;+structpte_freelist_batch**batchp=&get_cpu_var_locked(pte_freelist_cur,&cpu);-if(*batchp==NULL)-return;-pte_free_submit(*batchp);-*batchp=NULL;+if(*batchp){+pte_free_submit(*batchp);+*batchp=NULL;+}++put_cpu_var_locked(pte_freelist_cur,cpu);}/**
@@ -40,18 +40,17 @@staticinlinevoidtlb_flush(structmmu_gather*tlb){-structppc64_tlb_batch*tlbbatch=&__get_cpu_var(ppc64_tlb_batch);+structppc64_tlb_batch*tlbbatch=&get_cpu_var(ppc64_tlb_batch);/* If there's a TLB batch pending, then we must flush it because the*pagesaregoingtobefreedandwereallydon'twanttohaveaCPU*accessafreedpagebecauseithasastaleTLB*/if(tlbbatch->index){-preempt_disable();__flush_tlb_pending(tlbbatch);-preempt_enable();}+put_cpu_var(ppc64_tlb_batch);pte_free_finish();}
@@ -135,12 +135,13 @@u64*tcep;u64rpn;longl,limit;+intcpu;if(npages==1)returntce_build_pSeriesLP(tbl,tcenum,npages,uaddr,direction);-tcep=__get_cpu_var(tce_page);+tcep=get_cpu_var_locked(tce_page,&cpu);/* This is safe to do since interrupts are off when we're called*fromiommu_alloc{,_sg}()
@@ -148,10 +149,13 @@if(!tcep){tcep=(u64*)__get_free_page(GFP_ATOMIC);/* If allocation fails, fall back to the loop implementation */-if(!tcep)+if(!tcep){+put_cpu_var_locked(tce_page,cpu);returntce_build_pSeriesLP(tbl,tcenum,npages,uaddr,direction);-__get_cpu_var(tce_page)=tcep;+}++per_cpu_var_locked(tce_page,cpu)=tcep;}rpn=(virt_to_abs(uaddr))>>TCE_SHIFT;
@@ -188,6 +192,8 @@printk("\ttce[0] val = 0x%lx\n",tcep[0]);show_stack(current,(unsignedlong*)__get_SP());}++put_cpu_var_locked(tce_page,cpu);}staticvoidtce_free_pSeriesLP(structiommu_table*tbl,longtcenum,longnpages)
Hi Chirag,
On Wed, 9 Jul 2008 21:35:43 +0530 Chirag Jog [off-list ref] wrote:
Hi,
This patch fixes various paths in the -rt kernel on powerpc64 where per_cpu
variables are accessed in a preempt unsafe way.
When a power box with -rt kernel is booted, multiple BUG messages are
generated "BUG: init:1 task might have lost a preemption check!".
After booting a kernel with these patches applied, these messages
don't appear.
That does indeed greatly reduce BUGs display. Good. Thanks.
Tested-by: Sebastien Dugue [off-list ref]
Sebastien.
quoted hunk
Also I ran the realtime tests from ltp to ensure the stability.
Signed-Off-By: Chirag <redacted>
arch/powerpc/mm/tlb_64.c | 31 ++++++++++++++++---------------
arch/powerpc/platforms/pseries/iommu.c | 14 ++++++++++----
include/asm-powerpc/tlb.h | 5 ++---
3 files changed, 28 insertions(+), 22 deletions(-)
Index: linux-2.6.25.8-rt7/arch/powerpc/mm/tlb_64.c
===================================================================
@@ -253,13 +252,15 @@voidpte_free_finish(void){-/* This is safe since tlb_gather_mmu has disabled preemption */-structpte_freelist_batch**batchp=&__get_cpu_var(pte_freelist_cur);+intcpu;+structpte_freelist_batch**batchp=&get_cpu_var_locked(pte_freelist_cur,&cpu);-if(*batchp==NULL)-return;-pte_free_submit(*batchp);-*batchp=NULL;+if(*batchp){+pte_free_submit(*batchp);+*batchp=NULL;+}++put_cpu_var_locked(pte_freelist_cur,cpu);}/**
@@ -40,18 +40,17 @@staticinlinevoidtlb_flush(structmmu_gather*tlb){-structppc64_tlb_batch*tlbbatch=&__get_cpu_var(ppc64_tlb_batch);+structppc64_tlb_batch*tlbbatch=&get_cpu_var(ppc64_tlb_batch);/* If there's a TLB batch pending, then we must flush it because the*pagesaregoingtobefreedandwereallydon'twanttohaveaCPU*accessafreedpagebecauseithasastaleTLB*/if(tlbbatch->index){-preempt_disable();__flush_tlb_pending(tlbbatch);-preempt_enable();}+put_cpu_var(ppc64_tlb_batch);pte_free_finish();}
@@ -135,12 +135,13 @@u64*tcep;u64rpn;longl,limit;+intcpu;if(npages==1)returntce_build_pSeriesLP(tbl,tcenum,npages,uaddr,direction);-tcep=__get_cpu_var(tce_page);+tcep=get_cpu_var_locked(tce_page,&cpu);/* This is safe to do since interrupts are off when we're called*fromiommu_alloc{,_sg}()
@@ -148,10 +149,13 @@if(!tcep){tcep=(u64*)__get_free_page(GFP_ATOMIC);/* If allocation fails, fall back to the loop implementation */-if(!tcep)+if(!tcep){+put_cpu_var_locked(tce_page,cpu);returntce_build_pSeriesLP(tbl,tcenum,npages,uaddr,direction);-__get_cpu_var(tce_page)=tcep;+}++per_cpu_var_locked(tce_page,cpu)=tcep;}rpn=(virt_to_abs(uaddr))>>TCE_SHIFT;
@@ -188,6 +192,8 @@printk("\ttce[0] val = 0x%lx\n",tcep[0]);show_stack(current,(unsignedlong*)__get_SP());}++put_cpu_var_locked(tce_page,cpu);}staticvoidtce_free_pSeriesLP(structiommu_table*tbl,longtcenum,longnpages)--
To unsubscribe from this list: send the line "unsubscribe linux-rt-users" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2008-07-15 01:34:18
On Wed, 2008-07-09 at 21:35 +0530, Chirag Jog wrote:
Hi,
This patch fixes various paths in the -rt kernel on powerpc64 where per_cpu
variables are accessed in a preempt unsafe way.
When a power box with -rt kernel is booted, multiple BUG messages are
generated "BUG: init:1 task might have lost a preemption check!".
After booting a kernel with these patches applied, these messages
don't appear.
Also I ran the realtime tests from ltp to ensure the stability.
That sounds bad tho...
IE. You are changing the code to lock/unlock on all those TLB batching
operations, but seem to miss the core reason why it was done that way:
ie, the code assumes that it will not change CPU -between- those calls,
since the whole stuff should be already have been within a per-cpu
locked section at the caller level.
As for the TCE code, well, it lived on the assumption that the upper
level spinlock did the job of preventing preempt, I suppose that's not
the case anymore. So that part of the patch sounds ok.
Ben.
@@ -253,13 +252,15 @@voidpte_free_finish(void){-/* This is safe since tlb_gather_mmu has disabled preemption */-structpte_freelist_batch**batchp=&__get_cpu_var(pte_freelist_cur);+intcpu;+structpte_freelist_batch**batchp=&get_cpu_var_locked(pte_freelist_cur,&cpu);-if(*batchp==NULL)-return;-pte_free_submit(*batchp);-*batchp=NULL;+if(*batchp){+pte_free_submit(*batchp);+*batchp=NULL;+}++put_cpu_var_locked(pte_freelist_cur,cpu);}/**
@@ -40,18 +40,17 @@staticinlinevoidtlb_flush(structmmu_gather*tlb){-structppc64_tlb_batch*tlbbatch=&__get_cpu_var(ppc64_tlb_batch);+structppc64_tlb_batch*tlbbatch=&get_cpu_var(ppc64_tlb_batch);/* If there's a TLB batch pending, then we must flush it because the*pagesaregoingtobefreedandwereallydon'twanttohaveaCPU*accessafreedpagebecauseithasastaleTLB*/if(tlbbatch->index){-preempt_disable();__flush_tlb_pending(tlbbatch);-preempt_enable();}+put_cpu_var(ppc64_tlb_batch);pte_free_finish();}
@@ -135,12 +135,13 @@u64*tcep;u64rpn;longl,limit;+intcpu;if(npages==1)returntce_build_pSeriesLP(tbl,tcenum,npages,uaddr,direction);-tcep=__get_cpu_var(tce_page);+tcep=get_cpu_var_locked(tce_page,&cpu);/* This is safe to do since interrupts are off when we're called*fromiommu_alloc{,_sg}()
@@ -148,10 +149,13 @@if(!tcep){tcep=(u64*)__get_free_page(GFP_ATOMIC);/* If allocation fails, fall back to the loop implementation */-if(!tcep)+if(!tcep){+put_cpu_var_locked(tce_page,cpu);returntce_build_pSeriesLP(tbl,tcenum,npages,uaddr,direction);-__get_cpu_var(tce_page)=tcep;+}++per_cpu_var_locked(tce_page,cpu)=tcep;}rpn=(virt_to_abs(uaddr))>>TCE_SHIFT;
@@ -188,6 +192,8 @@printk("\ttce[0] val = 0x%lx\n",tcep[0]);show_stack(current,(unsignedlong*)__get_SP());}++put_cpu_var_locked(tce_page,cpu);}staticvoidtce_free_pSeriesLP(structiommu_table*tbl,longtcenum,longnpages)
From: Chirag Jog <hidden> Date: 2008-07-17 12:57:11
Hi Benjamin,
Thanks for the review
* Benjamin Herrenschmidt [off-list ref] [2008-07-15 11:32:01]:
On Wed, 2008-07-09 at 21:35 +0530, Chirag Jog wrote:
quoted
Hi,
This patch fixes various paths in the -rt kernel on powerpc64 where per_cpu
variables are accessed in a preempt unsafe way.
When a power box with -rt kernel is booted, multiple BUG messages are
generated "BUG: init:1 task might have lost a preemption check!".
After booting a kernel with these patches applied, these messages
don't appear.
Also I ran the realtime tests from ltp to ensure the stability.
That sounds bad tho...
IE. You are changing the code to lock/unlock on all those TLB batching
operations, but seem to miss the core reason why it was done that way:
ie, the code assumes that it will not change CPU -between- those calls,
since the whole stuff should be already have been within a per-cpu
locked section at the caller level.
All these operations are done assuming that tlb_gather_mmu disables
preemption and tlb_finish_mmu enables preemption again.
This is not true for -rt.
For x86, none of the code paths between tlb_gather_mmu and
tlb_finish_mmu access any per_cpu variables.
But this is not true for powerpc64 as we can see.
One way could be to make tlb_gather_mmu disable preemption as it does
in mainline but only for powerpc.
Although i am not sure, if this is the right step ahead.
I am attaching a patch below for the same.
I have left out the tce bits, as they are fine.
Note: I haven't extensively tested the patch
- Thanks,
Chirag
Index: linux-2.6.25.8-rt7/arch/powerpc/mm/tlb_64.c
===================================================================
@@ -37,7 +37,7 @@/* This is declared as we are using the more or less generic*include/asm-powerpc/tlb.hfile--tgall*/-DEFINE_PER_CPU_LOCKED(structmmu_gather,mmu_gathers);+DEFINE_PER_CPU(structmmu_gather,mmu_gathers);DEFINE_PER_CPU(structpte_freelist_batch*,pte_freelist_cur);unsignedlongpte_freelist_forced_free;
@@ -41,23 +41,32 @@unsignedintnr;/* set to ~0U means fast mode */unsignedintneed_flush;/* Really unmapped some ptes? */unsignedintfullmm;/* non-zero means full mm flush */+#if !defined(__powerpc64__)intcpu;+#endifstructpage*pages[FREE_PTE_NR];};/* Users of the generic TLB shootdown code must declare this storage space. */-DECLARE_PER_CPU_LOCKED(structmmu_gather,mmu_gathers);-+#if !defined(__powerpc64__)+DECLARE_PER_CPU_LOCKED(structmmu_gather,mmu_gathers);+#else+DECLARE_PER_CPU(structmmu_gather,mmu_gathers);+#endif/* tlb_gather_mmu*Returnapointertoaninitializedstructmmu_gather.*/staticinlinestructmmu_gather*tlb_gather_mmu(structmm_struct*mm,unsignedintfull_mm_flush){-intcpu;-structmmu_gather*tlb=&get_cpu_var_locked(mmu_gathers,&cpu);-tlb->cpu=cpu;+#if !defined(__powerpc64__)+intcpu;+structmmu_gather*tlb=&get_cpu_var_locked(mmu_gathers,&cpu);+tlb->cpu=cpu;+#else+structmmu_gather*tlb=&get_cpu_var(mmu_gathers);+#endiftlb->mm=mm;/* Use fast mode if only one CPU is online */
@@ -93,7 +102,11 @@/* keep the page table cache within bounds */check_pgt_cache();-put_cpu_var_locked(mmu_gathers,tlb->cpu);+#if !defined(__powerpc64__)+put_cpu_var_locked(mmu_gathers,tlb->cpu);+#else+put_cpu_var(mmu_gathers);+#endif}/* tlb_remove_page
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2008-07-17 20:23:39
All these operations are done assuming that tlb_gather_mmu disables
preemption and tlb_finish_mmu enables preemption again.
This is not true for -rt.
For x86, none of the code paths between tlb_gather_mmu and
tlb_finish_mmu access any per_cpu variables.
But this is not true for powerpc64 as we can see.
One way could be to make tlb_gather_mmu disable preemption as it does
in mainline but only for powerpc.
Although i am not sure, if this is the right step ahead.
I am attaching a patch below for the same.
I have left out the tce bits, as they are fine.
Note: I haven't extensively tested the patch
A better option is to make sure that a context switch does the right
thing, flushing the pending batch. I think that's already the case,
which means that your original patch may work, but that needs to
be double-checked and commented properly.
Cheers,
Ben.
From: Chirag Jog <hidden> Date: 2008-07-18 10:14:17
* Benjamin Herrenschmidt [off-list ref] [2008-07-18 06:14:31]:
quoted
All these operations are done assuming that tlb_gather_mmu disables
preemption and tlb_finish_mmu enables preemption again.
This is not true for -rt.
For x86, none of the code paths between tlb_gather_mmu and
tlb_finish_mmu access any per_cpu variables.
But this is not true for powerpc64 as we can see.
One way could be to make tlb_gather_mmu disable preemption as it does
in mainline but only for powerpc.
Although i am not sure, if this is the right step ahead.
I am attaching a patch below for the same.
I have left out the tce bits, as they are fine.
Note: I haven't extensively tested the patch
A better option is to make sure that a context switch does the right
thing, flushing the pending batch. I think that's already the case,
which means that your original patch may work, but that needs to
be double-checked and commented properly.
With the original patch, the pending batch does get flushed
in a non-preemptable region.
I am resending the original with just adding the necesary comments.
-Thanks,
Chirag
Signed-Off-By: Chirag <redacted>
Index: linux-2.6.25.8-rt7/arch/powerpc/mm/tlb_64.c
===================================================================
@@ -253,13 +252,15 @@voidpte_free_finish(void){-/* This is safe since tlb_gather_mmu has disabled preemption */-structpte_freelist_batch**batchp=&__get_cpu_var(pte_freelist_cur);+intcpu;+structpte_freelist_batch**batchp=&get_cpu_var_locked(pte_freelist_cur,&cpu);-if(*batchp==NULL)-return;-pte_free_submit(*batchp);-*batchp=NULL;+if(*batchp){+pte_free_submit(*batchp);+*batchp=NULL;+}++put_cpu_var_locked(pte_freelist_cur,cpu);}/**
@@ -40,18 +40,20 @@staticinlinevoidtlb_flush(structmmu_gather*tlb){-structppc64_tlb_batch*tlbbatch=&__get_cpu_var(ppc64_tlb_batch);+/* Disable preemption to ensure the pending TLB batch is flushed+*beforeapotentialcontextswitch+*/+structppc64_tlb_batch*tlbbatch=&get_cpu_var(ppc64_tlb_batch);/* If there's a TLB batch pending, then we must flush it because the*pagesaregoingtobefreedandwereallydon'twanttohaveaCPU*accessafreedpagebecauseithasastaleTLB*/if(tlbbatch->index){-preempt_disable();__flush_tlb_pending(tlbbatch);-preempt_enable();}+put_cpu_var(ppc64_tlb_batch);pte_free_finish();}
@@ -135,12 +135,13 @@u64*tcep;u64rpn;longl,limit;+intcpu;if(npages==1)returntce_build_pSeriesLP(tbl,tcenum,npages,uaddr,direction);-tcep=__get_cpu_var(tce_page);+tcep=get_cpu_var_locked(tce_page,&cpu);/* This is safe to do since interrupts are off when we're called*fromiommu_alloc{,_sg}()
@@ -148,10 +149,13 @@if(!tcep){tcep=(u64*)__get_free_page(GFP_ATOMIC);/* If allocation fails, fall back to the loop implementation */-if(!tcep)+if(!tcep){+put_cpu_var_locked(tce_page,cpu);returntce_build_pSeriesLP(tbl,tcenum,npages,uaddr,direction);-__get_cpu_var(tce_page)=tcep;+}++per_cpu_var_locked(tce_page,cpu)=tcep;}rpn=(virt_to_abs(uaddr))>>TCE_SHIFT;
@@ -188,6 +192,8 @@printk("\ttce[0] val = 0x%lx\n",tcep[0]);show_stack(current,(unsignedlong*)__get_SP());}++put_cpu_var_locked(tce_page,cpu);}staticvoidtce_free_pSeriesLP(structiommu_table*tbl,longtcenum,longnpages)
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2008-07-18 22:06:16
With the original patch, the pending batch does get flushed
in a non-preemptable region.
I am resending the original with just adding the necesary comments.
Your comment isn't what I meant. What I meant is that if the process
is context switched while walking the page tables, the low level powerpc
context switch code should also perform a __flush_tlb_pending.
BTW. Is the pte_lock also not a real spinlock anymore ? That may break
other assumptions the powerpc mm code is doing.
This -rt stuff is just too scary, it changes some fundamental semantics
of the spinlocks. yuck.
Ben.
From: Steven Rostedt <rostedt@goodmis.org> Date: 2008-07-19 03:16:45
On Sat, 19 Jul 2008, Benjamin Herrenschmidt wrote:
quoted
With the original patch, the pending batch does get flushed
in a non-preemptable region.
I am resending the original with just adding the necesary comments.
Your comment isn't what I meant. What I meant is that if the process
is context switched while walking the page tables, the low level powerpc
context switch code should also perform a =EF=BB=BF__flush_tlb_pending.
BTW. Is the pte_lock also not a real spinlock anymore ? That may break
other assumptions the powerpc mm code is doing.
This -rt stuff is just too scary, it changes some fundamental semantics
of the spinlocks. yuck.
There's lots of semantics that are changed with -rt that should make
everything still work ;-) Some spinlocks remain real spinlocks, but we
shouldn't have a problem with most being mutexes.
There's some cases that uses per CPU variables or other per cpu actions
that require a special CPU_LOCK that protects the data in a preemption
mode. The slab.c code in -rt handles this.
-- Steve
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2008-07-19 03:59:26
There's lots of semantics that are changed with -rt that should make
everything still work ;-) Some spinlocks remain real spinlocks, but we
shouldn't have a problem with most being mutexes.
There's some cases that uses per CPU variables or other per cpu actions
that require a special CPU_LOCK that protects the data in a preemption
mode. The slab.c code in -rt handles this.
Well, there is at least in my case a whole class of code that assumes
that because the whole thing happens within a spinlock section at the
toplevel, it could not only access per_cpu variables using the
__variants, that's easy, but it also assumes that it can add things bit
by bit as it gets called at the lower level to that per-cpu cache. It's
not actually prepared for possibly migrating to another CPU right in the
middle.
I need to review that stuff a bit. I think we fixed some of that at one
point, and we made sure that the context switch itself would flush
pending MMU batches, so it -may- be fine in that specific case.
Cheers,
Ben.
From: Chirag Jog <hidden> Date: 2008-07-21 10:26:32
Hi Benjamin
* Benjamin Herrenschmidt [off-list ref] [2008-07-19 08:05:30]:
quoted
With the original patch, the pending batch does get flushed
in a non-preemptable region.
I am resending the original with just adding the necesary comments.
Your comment isn't what I meant. What I meant is that if the process
is context switched while walking the page tables, the low level powerpc
context switch code should also perform a ???__flush_tlb_pending.