parallel load of modules on an ARM multicore
Subsystems (3)
Hi,
I'm getting unexpected results from loading several modules - some of them in parallel - on an ARM11 MPcore system.
The system does not use "single sequential" modprobe commands - instead it starts several shells doing insmod sequences like this:
shell A:
insmod a
insmod ab
insmod abc
shell B:
insmod b
insmod bc
insmod bcd
shell C:
insmod c
insmod cd
insmod cde
This is done to crash^H^H^H^H^Hboot faster ;)
While one insmod operation is protected via the module_mutex - I'm wondering what happens with the instruction cache invalidation.
AFAICT the flush_icache_range only invalidates the ICache on the running cpu. The module_mutex is unlocked after _loading_ the module, do_mod_ctors() and do_one_initcall() are called without that lock - can they run on a different cpu? It's an preemptible system (SMP PREEMPT armv6l).
Wouldn't it be required to flush the icache on _all_ cpus?
It might be highly dependable from the cache organization, but isn't it possible that the cpu that didn't load the module, is scheduled to run a function from it with a "stale" instruction cache line???
I sometimes see oopses where I wonder how such a state could be reached. The code and register state seem not to fit - looks like an interrupt does not restore all registers properly :)
Also an Oops happens when loading module abc, and still the module list is reported empty in the Oops despite the fact that "abc" depends on "a". Hmh.
Best regards
Peter
<snip kernel/module.c:SYSCALL_DEFINE3(init_module> [..]
/* Only one module load at a time, please */
if (mutex_lock_interruptible(&module_mutex) != 0)
return -EINTR;
/* Do all the hard work */
mod = load_module(umod, len, uargs);
if (IS_ERR(mod)) {
mutex_unlock(&module_mutex);
return PTR_ERR(mod);
}
/* Drop lock so they can recurse */
mutex_unlock(&module_mutex);
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_COMING, mod);
do_mod_ctors(mod);
/* Start the module */
if (mod->init != NULL)
ret = do_one_initcall(mod->init);</snip>
<snip kernel/module.c:load_module>
[..]
old_fs = get_fs();
set_fs(KERNEL_DS);
/*
* Flush the instruction cache, since we've played with text.
* Do it before processing of module parameters, so the module
* can provide parameter accessor functions of its own.
*/
if (mod->module_init)
flush_icache_range((unsigned long)mod->module_init,
(unsigned long)mod->module_init
+ mod->init_size);
flush_icache_range((unsigned long)mod->module_core,
(unsigned long)mod->module_core + mod->core_size);
set_fs(old_fs);</snip>
arch/arm/mm/cache-v6.S:
ENTRY(v6_flush_kern_cache_all)
mov r0, #0
#ifdef HARVARD_CACHE
mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate
#ifndef CONFIG_ARM_ERRATA_411920
mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate
#else
b v6_icache_inval_all
#endif
#else
mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate
#endif
mov pc, lr
Quoted 23 lines · On Mon, Jun 20, 2011 at 03:43:27PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
I'm getting unexpected results from loading several modules - some of them in parallel - on an ARM11 MPcore system.
The system does not use "single sequential" modprobe commands - instead it starts several shells doing insmod sequences like this:
shell A:
insmod a
insmod ab
insmod abcshell B:
insmod b
insmod bc
insmod bcdshell C:
insmod c
insmod cd
insmod cdeThis is done to crash^H^H^H^H^Hboot faster ;)
Quoted 8 lines
While one insmod operation is protected via the
module_mutex- I'm wondering what happens with the instruction cache invalidation.
AFAICT theflush_icache_rangeonly invalidates the ICache on the running cpu. Themodule_mutexis unlocked after _loading_ the module,do_mod_ctors()anddo_one_initcall()are called without that lock - can they run on a different cpu? It's an preemptible system (SMP PREEMPT armv6l).Wouldn't it be required to flush the icache on _all_ cpus?
I theory, you would need to flush both the I and D cache on all the CPUs but that's not easily possible (well, I don't think there are many users of flush_icache_range(), so we could make this do an smp_call_function().
I think what happens (I'm looking at a 3.0-rc3 kernel) is that the module code is copied into RAM and the process could migrate to another CPU before flush_icache_range() gets called (this function is called outside the mutex_lock regions). We therefore don't flush the data out of the D-cache that was allocated during copy_from_user().
You can try the patch below (I haven't tested it for some time), it may fix the issue.
8<-----------------------------------------------
ARM: Allow lazy cache flushing on ARM11MPCore
From: Catalin Marinas <[email protected]>
The ARM11MPCore doesn't broadcast the cache maintenance operations in hardware, therefore the flush_dcache_page() currently performs the cache flushing non-lazily. But since not all drivers call this function after writing to a page cache page, the kernel needs a different approach like using read-for-ownership on the CPU flushing the cache to force the dirty cache lines migration from other CPUs. This way the cache flushing operation can be done lazily via __sync_icache_dcache().
Since we cannot force read-for-ownership on the I-cache, the patch invalidates the whole I-cache when a thread migrates to another CPU.
--- arch/arm/include/asm/mmu_context.h | 3 ++- arch/arm/mm/cache-v6.S | 8 ++++++++ arch/arm/mm/flush.c | 3 +-- 3 files changed, 11 insertions(+), 3 deletions(-)
arch/arm/include/asm/mmu_context.h +2 -1
@@ -114,7 +114,8 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,#ifdef CONFIG_SMP /* check for possible thread migration */ if (!cpumask_empty (mm_cpumask (next)) && - !cpumask_test_cpu (cpu,mm_cpumask (next))) + (cache_ops_need_broadcast () || + !cpumask_test_cpu (cpu,mm_cpumask (next))))__flush_icache_all ();#endif if (!cpumask_test_and_set_cpu (cpu,mm_cpumask (next)) || prev != next) {
arch/arm/mm/cache-v6.S +8 -0
@@ -133,6 +133,10 @@ ENTRY(v6_coherent_user_range) #ifdef HARVARD_CACHE bic r0, r0, #CACHE_LINE_SIZE - 1 1: +#ifdef CONFIG_SMP + /* no cache maintenance broadcasting on ARM11MPCore */ + USER( ldr r2, [r0] ) @ read for ownership +#endif USER( mcr p15, 0, r0, c7, c10, 1 ) @ clean D line add r0, r0, #CACHE_LINE_SIZE 2:
@@ -178,6 +182,10 @@ ENTRY(v6_flush_kern_dcache_area) add r1, r0, r1 bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: +#ifdef CONFIG_SMP + /* no cache maintenance broadcasting on ARM11MPCore */ + ldr r2, [r0] @ read for ownership +#endif #ifdef HARVARD_CACHE mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line #else
arch/arm/mm/flush.c +1 -2
@@ -291,8 +291,7 @@ void flush_dcache_page(struct page *page) mapping =page_mapping (page); -if (!cache_ops_need_broadcast () && - mapping && !mapping_mapped (mapping)) +if (mapping && !mapping_mapped (mapping))clear_bit (PG_dcache_clean, &page->flags);else {__flush_dcache_page (mapping, page);
Signature · 1 line
Catalin
Catalin,
it's interesting that you almost agree with me.
But isn't it really expensive to flush the icache on switch_mm? Is that meant as a test to see if the problem goes away?
Wouldn't it suffice to get_cpu/put_cpu to disable preemption while load_module() works? A running task in kernel mode will not be migrated, wouldn't it?
I think the other way will cause trouble: the module is loaded on cpu0 for example, preempted, woken up on cpu1 with a stale icache line not holding the "newly loaded code" and running mod->init peng! Nobody told cpu1 to revalidate it's icache.
Don't know if this is possible though.
The data of the module won't get through the icache anyway.
AFAIK we are not able to reproduce quickly and it will take some time before I can try...
Mit freundlichen Gr??en / Best regards
Peter W?chtler
Quoted 156 lines
-----Urspr?ngliche Nachricht-----
Von: Catalin Marinas [mailto:catalin.marinas at gmail.com] Im Auftrag von Catalin Marinas
Gesendet: Dienstag, 21. Juni 2011 17:51
An: EXTERNAL Waechtler Peter (Fa. TCP, CM-AI/PJ-CF31)
Cc: linux-arm-kernel at lists.infradead.org
Betreff: Re: parallel load of modules on an ARM multicoreQuoted 4 lines · On Mon, Jun 20, 2011 at 03:43:27PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
I'm getting unexpected results from loading several modules - some of them in parallel - on an ARM11 MPcore system.
The system does not use "single sequential" modprobe
commands - instead it
Quoted 12 lines
starts several shells doing insmod sequences like this:
shell A:
insmod a
insmod ab
insmod abcshell B:
insmod b
insmod bc
insmod bcdQuoted 8 lines
shell C:
insmod c
insmod cd
insmod cdeThis is done to crash^H^H^H^H^Hboot faster ;)
While one insmod operation is protected via the
module_mutex- I'm wonderingQuoted 2 lines
what happens with the instruction cache invalidation. AFAICT the
flush_icache_rangeonly invalidates the ICacheon the running cpu.
Quoted 1 line
The
module_mutexis unlocked after _loading_ the module,
do_mod_ctors()andQuoted 1 line
do_one_initcall()are called without that lock - can theyrun on a different cpu?
Quoted 3 lines
It's an preemptible system (SMP PREEMPT armv6l).
Wouldn't it be required to flush the icache on _all_ cpus?
I theory, you would need to flush both the I and D cache on all the CPUs
but that's not easily possible (well, I don't think there are many users
offlush_icache_range(), so we could make this do ansmp_call_function().I think what happens (I'm looking at a 3.0-rc3 kernel) is that the module code is copied into RAM and the process could migrate to another
CPU beforeflush_icache_range()gets called (this function is called outside themutex_lockregions). We therefore don't flush the data out of the D-cache that was allocated duringcopy_from_user().You can try the patch below (I haven't tested it for some time), it may
fix the issue.8<-----------------------------------------------
ARM: Allow lazy cache flushing on ARM11MPCore
From: Catalin Marinas <[email protected]>
The ARM11MPCore doesn't broadcast the cache maintenance operations in hardware, therefore the
flush_dcache_page()currently performs the cache
flushing non-lazily. But since not all drivers call this function after
writing to a page cache page, the kernel needs a different approach like
using read-for-ownership on the CPU flushing the cache to force the dirty cache lines migration from other CPUs. This way the cache flushing
operation can be done lazily via__sync_icache_dcache().Since we cannot force read-for-ownership on the I-cache, the patch invalidates the whole I-cache when a thread migrates to another CPU.
Signed-off-by Catalin Marinas <[email protected]>--- arch/arm/include/asm/mmu_context.h | 3 ++- arch/arm/mm/cache-v6.S | 8 ++++++++ arch/arm/mm/flush.c | 3 +-- 3 files changed, 11 insertions(+), 3 deletions(-)arch/arm/include/asm/mmu_context.h +2
-1@@ -114,7 +114,8 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,#ifdef CONFIG_SMP /* check for possible thread migration */ if (!cpumask_empty (mm_cpumask (next)) && - !cpumask_test_cpu (cpu,mm_cpumask (next))) + (cache_ops_need_broadcast () || + !cpumask_test_cpu (cpu,mm_cpumask (next))))__flush_icache_all ();#endif if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) ||prev != next) {
arch/arm/mm/cache-v6.S +3
-0@@ -133,6 +133,10 @@ ENTRY(v6_coherent_user_range) #ifdef HARVARD_CACHE bic r0, r0, #CACHE_LINE_SIZE - 1 1: +#ifdef CONFIG_SMP + /* no cache maintenance broadcasting on ARM11MPCore */ + USER( ldr r2, [r0] ) @ readfor ownership
arch/arm/mm/cache-v6.S +5
-0+#endif USER( mcr p15, 0, r0, c7, c10, 1 ) @ clean D line add r0, r0, #CACHE_LINE_SIZE 2:@@ -178,6 +182,10 @@ ENTRY(v6_flush_kern_dcache_area) add r1, r0, r1 bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: +#ifdef CONFIG_SMP + /* no cache maintenance broadcasting on ARM11MPCore */ + ldr r2, [r0] @ read for ownership +#endif #ifdef HARVARD_CACHE mcr p15, 0, r0, c7, c14, 1 @ clean &invalidate D line
#elsearch/arm/mm/flush.c +1
-3@@ -291,8 +291,7 @@ void flush_dcache_page(struct page *page) mapping =page_mapping (page); -if (!cache_ops_need_broadcast () && - mapping && !mapping_mapped (mapping)) +if (mapping && !mapping_mapped (mapping))clear_bit (PG_dcache_clean, &page->flags);else {__flush_dcache_page (mapping, page); --Catalin
Peter,
Quoted 4 lines · On Thu, Jun 23, 2011 at 04:39:01PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
it's interesting that you almost agree with me.
But isn't it really expensive to flush the icache on
switch_mm? Is that meant as a test to see if the problem goes away?
Who said anything about flushing the icache on switch_mm()? My patch doesn't do this, it actually reduces the amount of cache flushing on ARM11MPCore.
Quoted 2 lines
Wouldn't it suffice to get_cpu/put_cpu to disable preemption while
load_module()works?
It may work, just give it a try.
Quoted 5 lines
I think the other way will cause trouble: the module is loaded on cpu0 for example, preempted, woken up on cpu1 with a stale icache line not holding the "newly loaded code" and running
mod->initpeng! Nobody told cpu1 to revalidate it's icache.
Don't know if this is possible though.
That's possible as well if the pages allocated for the module code have been previously used for other code.
To resolve the stale I-cache lines, you would have to broadcast the cache maintenance to all the CPUs. For the D-cache we could trick the CPU via some reading to force the dirty cache lines migration but that's not possible for the I-cache.
Quoted 1 line
The data of the module won't get through the icache anyway.
No but the module is copied into the new allocated space via the D-cache. This needs to be flushed so that the I-cache would get the right instructions.
Quoted 2 lines
AFAIK we are not able to reproduce quickly and it will take some time before I can try...
OK, just let us know how it goes.
Signature · 1 line
Catalin
Quoted 11 lines · Catalin Marinas
Peter,
Quoted 4 lines · On Thu, Jun 23, 2011 at 04:39:01PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
it's interesting that you almost agree with me.
But isn't it really expensive to flush the icache on
switch_mm? Is that meant as a test to see if the problem goes away?Who said anything about flushing the icache on
switch_mm()? My patch doesn't do this, it actually reduces the amount of cache flushing on ARM11MPCore.
Ahem, your patch does:
arch/arm/include/asm/mmu_context.h +2 -1
@@ -114,7 +114,8 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,#ifdef CONFIG_SMP /* check for possible thread migration */ if (!cpumask_empty (mm_cpumask (next)) && - !cpumask_test_cpu (cpu,mm_cpumask (next))) + (cache_ops_need_broadcast () || + !cpumask_test_cpu (cpu,mm_cpumask (next))))__flush_icache_all ();#endif if (!cpumask_test_and_set_cpu (cpu,mm_cpumask (next)) || prev != next) {
This means that if cache_ops_need_broadcast() is true, we will flush the I-cache at every MM context switch as very few MMs will have an empty mm_cpumask().
Far from reducing the number of I-cache flushes, this will significantly increase the flushing.
Quoted 28 lines · Russell King - ARM Linux
Quoted 11 lines · Catalin Marinas
Peter,
Quoted 4 lines · On Thu, Jun 23, 2011 at 04:39:01PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
it's interesting that you almost agree with me.
But isn't it really expensive to flush the icache on
switch_mm? Is that meant as a test to see if the problem goes away?Who said anything about flushing the icache on
switch_mm()? My patch doesn't do this, it actually reduces the amount of cache flushing on ARM11MPCore.Ahem, your patch does:
arch/arm/include/asm/mmu_context.h +2
-1@@ -114,7 +114,8 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,#ifdef CONFIG_SMP /* check for possible thread migration */ if (!cpumask_empty (mm_cpumask (next)) && - !cpumask_test_cpu (cpu,mm_cpumask (next))) + (cache_ops_need_broadcast () || + !cpumask_test_cpu (cpu,mm_cpumask (next))))__flush_icache_all ();#endif if (!cpumask_test_and_set_cpu (cpu,mm_cpumask (next)) || prev != next) {
Quoted 3 lines
This means that if
cache_ops_need_broadcast()is true, we will flush the I-cache at every MM context switch as very few MMs will have an emptymm_cpumask().
Ah, I forgot about this. That's left over from some earlier version of the patch where cache_ops_need_broadcast() was set to 0 for ARMv6 SMP but then I realised that it is still needed for some ptrace stuff and re-instated it.
So this hunk is definitely not needed.
BTW, the patch (with the fix above) would be useful for the cases where we have block drivers not calling flush_dcache_page().
Signature · 1 line
Catalin
Quoted 38 lines · Catalin Marinas
Quoted 28 lines · Russell King - ARM Linux
Quoted 11 lines · Catalin Marinas
Peter,
Quoted 4 lines · On Thu, Jun 23, 2011 at 04:39:01PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
it's interesting that you almost agree with me.
But isn't it really expensive to flush the icache on
switch_mm? Is that meant as a test to see if the problem goes away?Who said anything about flushing the icache on
switch_mm()? My patch doesn't do this, it actually reduces the amount of cache flushing on ARM11MPCore.Ahem, your patch does:
arch/arm/include/asm/mmu_context.h +2
-1@@ -114,7 +114,8 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,#ifdef CONFIG_SMP /* check for possible thread migration */ if (!cpumask_empty (mm_cpumask (next)) && - !cpumask_test_cpu (cpu,mm_cpumask (next))) + (cache_ops_need_broadcast () || + !cpumask_test_cpu (cpu,mm_cpumask (next))))__flush_icache_all ();#endif if (!cpumask_test_and_set_cpu (cpu,mm_cpumask (next)) || prev != next) {Quoted 3 lines
This means that if
cache_ops_need_broadcast()is true, we will flush the I-cache at every MM context switch as very few MMs will have an emptymm_cpumask().Ah, I forgot about this. That's left over from some earlier version of the patch where
cache_ops_need_broadcast()was set to 0 for ARMv6 SMP but then I realised that it is still needed for some ptrace stuff and re-instated it.
Quoted 1 line
So this hunk is definitely not needed.
BTW, do we actually need the I-cache invalidation during thread migration with cache ops broadcasting in hardware?
Signature · 1 line
Catalin
No, you didn't say that, but the patch ;)
arch/arm/include/asm/mmu_context.h +2 -1
@@ -114,7 +114,8 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,#ifdef CONFIG_SMP /* check for possible thread migration */ if (!cpumask_empty (mm_cpumask (next)) && - !cpumask_test_cpu (cpu,mm_cpumask (next))) + (cache_ops_need_broadcast () || + !cpumask_test_cpu (cpu,mm_cpumask (next))))__flush_icache_all ();
On ARM11MPcore cache_ops_need_broadcast() returns true - with that, anytime the thread migrates the icache is flushed?
Mit freundlichen Gr??en / Best regards
Peter W?chtler
Quoted 56 lines
-----Urspr?ngliche Nachricht-----
Von: Catalin Marinas [mailto:catalin.marinas at gmail.com] Im Auftrag von Catalin Marinas
Gesendet: Donnerstag, 23. Juni 2011 16:52
An: EXTERNAL Waechtler Peter (Fa. TCP, CM-AI/PJ-CF31)
Cc: linux-arm-kernel at lists.infradead.org
Betreff: Re: parallel load of modules on an ARM multicorePeter,
Quoted 4 lines · On Thu, Jun 23, 2011 at 04:39:01PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
it's interesting that you almost agree with me.
But isn't it really expensive to flush the icache on
switch_mm? Is that meant as a test to see if the problem goes away?Who said anything about flushing the icache on
switch_mm()? My patch doesn't do this, it actually reduces the amount of cache flushing on ARM11MPCore.Quoted 2 lines
Wouldn't it suffice to get_cpu/put_cpu to disable preemption while
load_module()works?It may work, just give it a try.
Quoted 5 lines
I think the other way will cause trouble: the module is loaded on cpu0 for example, preempted, woken up on cpu1 with a stale icache line not holding the "newly loaded code" and running
mod->initpeng! Nobody told cpu1 to revalidate it's icache.
Don't know if this is possible though.That's possible as well if the pages allocated for the module code have
been previously used for other code.To resolve the stale I-cache lines, you would have to broadcast the cache maintenance to all the CPUs. For the D-cache we could trick the CPU via some reading to force the dirty cache lines migration but that's
not possible for the I-cache.Quoted 1 line
The data of the module won't get through the icache anyway.
No but the module is copied into the new allocated space via the D-cache. This needs to be flushed so that the I-cache would get the right instructions.
Quoted 2 lines
AFAIK we are not able to reproduce quickly and it will take some time before I can try...
OK, just let us know how it goes.
--
Catalin
Hi,
On Tue, Jun 21, 2011 at 04:50:30PM +0100, Catalin Marinas wrote:
// CUT
Quoted 41 lines
ARM: Allow lazy cache flushing on ARM11MPCore
From: Catalin Marinas <[email protected]>
The ARM11MPCore doesn't broadcast the cache maintenance operations in hardware, therefore the
flush_dcache_page()currently performs the cache flushing non-lazily. But since not all drivers call this function after writing to a page cache page, the kernel needs a different approach like using read-for-ownership on the CPU flushing the cache to force the dirty cache lines migration from other CPUs. This way the cache flushing operation can be done lazily via__sync_icache_dcache().Since we cannot force read-for-ownership on the I-cache, the patch invalidates the whole I-cache when a thread migrates to another CPU.
Signed-off-by Catalin Marinas <[email protected]>--- arch/arm/include/asm/mmu_context.h | 3 ++- arch/arm/mm/cache-v6.S | 8 ++++++++ arch/arm/mm/flush.c | 3 +-- 3 files changed, 11 insertions(+), 3 deletions(-)arch/arm/include/asm/mmu_context.h +2
-1@@ -114,7 +114,8 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,#ifdef CONFIG_SMP /* check for possible thread migration */ if (!cpumask_empty (mm_cpumask (next)) && - !cpumask_test_cpu (cpu,mm_cpumask (next))) + (cache_ops_need_broadcast () || + !cpumask_test_cpu (cpu,mm_cpumask (next))))__flush_icache_all ();#endif if (!cpumask_test_and_set_cpu (cpu,mm_cpumask (next)) || prev != next) {arch/arm/mm/cache-v6.S +0
-0
arch/arm/mm/cache-v6.S ENTRY(v6_coherent_user_range) · line 136
Quoted 4 lines
arch/arm/mm/cache-v6.S +1
-0@@ -133,6 +133,10 @@ ENTRY(v6_coherent_user_range) #ifdef HARVARD_CACHE bic r0, r0, #CACHE_LINE_SIZE - 1 1: +#ifdef CONFIG_SMP
s/CONFIG_SMP/CONFIG_RWFO/
arch/arm/mm/cache-v6.S ENTRY(v6_flush_kern_dcache_area) · line 185
Quoted 11 lines
arch/arm/mm/cache-v6.S +4
-0+ /* no cache maintenance broadcasting on ARM11MPCore */ + USER( ldr r2, [r0] ) @ read for ownership +#endif USER( mcr p15, 0, r0, c7, c10, 1 ) @ clean D line add r0, r0, #CACHE_LINE_SIZE 2:@@ -178,6 +182,10 @@ ENTRY(v6_flush_kern_dcache_area) add r1, r0, r1 bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: +#ifdef CONFIG_SMP
s/CONFIG_SMP/CONFIG_RWFO/
--
Regards,
George
Quoted 28 lines
arch/arm/mm/cache-v6.S +3
-0+ /* no cache maintenance broadcasting on ARM11MPCore */ + ldr r2, [r0] @ read for ownership +#endif #ifdef HARVARD_CACHE mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line #elsearch/arm/mm/flush.c +1
-2@@ -291,8 +291,7 @@ void flush_dcache_page(struct page *page) mapping =page_mapping (page); -if (!cache_ops_need_broadcast () && - mapping && !mapping_mapped (mapping)) +if (mapping && !mapping_mapped (mapping))clear_bit (PG_dcache_clean, &page->flags);else {__flush_dcache_page (mapping, page);Signature · 7 lines
-- Catalin _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Hello,
I'd like to resurrect this thread in order to get the "ARM: Allow lazy cache flushing on ARM11MPCore" or some variant pushed upstream.
Quoted 61 lines · Catalin Marinas
Quoted 23 lines · On Mon, Jun 20, 2011 at 03:43:27PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
I'm getting unexpected results from loading several modules - some of them in parallel - on an ARM11 MPcore system.
The system does not use "single sequential" modprobe commands - instead it starts several shells doing insmod sequences like this:
shell A:
insmod a
insmod ab
insmod abcshell B:
insmod b
insmod bc
insmod bcdshell C:
insmod c
insmod cd
insmod cdeThis is done to crash^H^H^H^H^Hboot faster ;)
Quoted 8 lines
While one insmod operation is protected via the
module_mutex- I'm wondering what happens with the instruction cache invalidation.
AFAICT theflush_icache_rangeonly invalidates the ICache on the running cpu. Themodule_mutexis unlocked after _loading_ the module,do_mod_ctors()anddo_one_initcall()are called without that lock - can they run on a different cpu? It's an preemptible system (SMP PREEMPT armv6l).Wouldn't it be required to flush the icache on _all_ cpus?
I theory, you would need to flush both the I and D cache on all the CPUs but that's not easily possible (well, I don't think there are many users of
flush_icache_range(), so we could make this do ansmp_call_function().I think what happens (I'm looking at a 3.0-rc3 kernel) is that the module code is copied into RAM and the process could migrate to another CPU before
flush_icache_range()gets called (this function is called outside themutex_lockregions). We therefore don't flush the data out of the D-cache that was allocated duringcopy_from_user().You can try the patch below (I haven't tested it for some time), it may fix the issue.
8<-----------------------------------------------
ARM: Allow lazy cache flushing on ARM11MPCore
From: Catalin Marinas <[email protected]>
The ARM11MPCore doesn't broadcast the cache maintenance operations in hardware, therefore the
flush_dcache_page()currently performs the cache flushing non-lazily. But since not all drivers call this function after writing to a page cache page, the kernel needs a different approach like using read-for-ownership on the CPU flushing the cache to force the dirty cache lines migration from other CPUs. This way the cache flushing operation can be done lazily via__sync_icache_dcache().
Quoted 4 lines
Since we cannot force read-for-ownership on the I-cache, the patch invalidates the whole I-cache when a thread migrates to another CPU.
Signed-off-by Catalin Marinas <[email protected]>
On an ARM11 MPCore w/3-cores w/o this change, parallel module loading stress testing with CONFIG_PREEMPT enabled falls over due to cache incoherency. Applying this change resolves parallel module load oopses due to cache coherency issues.
Quoted 11 lines
--- arch/arm/include/asm/mmu_context.h | 3 ++- arch/arm/mm/cache-v6.S | 8 ++++++++ arch/arm/mm/flush.c | 3 +-- 3 files changed, 11 insertions(+), 3 deletions(-)arch/arm/include/asm/mmu_context.h +0
-0
arch/arm/include/asm/mmu_context.h switch_mm(struct mm_struct *prev, struct mm_struct *next, · line 119
Quoted 7 lines
arch/arm/include/asm/mmu_context.h +2
-1@@ -114,7 +114,8 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,#ifdef CONFIG_SMP /* check for possible thread migration */ if (!cpumask_empty (mm_cpumask (next)) && - !cpumask_test_cpu (cpu,mm_cpumask (next))) + (cache_ops_need_broadcast () || + !cpumask_test_cpu (cpu,mm_cpumask (next))))__flush_icache_all ();
Dropped this hunk based on feedback in this thread that it is not required and merely results in __flush_icache_all() on every context switch.
Quoted 18 lines
arch/arm/include/asm/mmu_context.h +0
-0#endif if (!cpumask_test_and_set_cpu (cpu,mm_cpumask (next)) || prev != next) {arch/arm/mm/cache-v6.S +4
-0@@ -133,6 +133,10 @@ ENTRY(v6_coherent_user_range) #ifdef HARVARD_CACHE bic r0, r0, #CACHE_LINE_SIZE - 1 1: +#ifdef CONFIG_SMP + /* no cache maintenance broadcasting on ARM11MPCore */ + USER( ldr r2, [r0] ) @ read for ownership +#endif USER( mcr p15, 0, r0, c7, c10, 1 ) @ clean D line add r0, r0, #CACHE_LINE_SIZE 2:
arch/arm/mm/cache-v6.S ENTRY(v6_flush_kern_dcache_area) · line 191
Quoted 10 lines
arch/arm/mm/cache-v6.S +4
-0@@ -178,6 +182,10 @@ ENTRY(v6_flush_kern_dcache_area) add r1, r0, r1 bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: +#ifdef CONFIG_SMP + /* no cache maintenance broadcasting on ARM11MPCore */ + ldr r2, [r0] @ read for ownership +#endif #ifdef HARVARD_CACHE mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line #else
I also added the following around the above RFOs to disable preemption:
From: George G. Davis <[email protected]>
Date: Tue, 4 Oct 2011 13:44:37 -0400
Subject: [PATCH] ARM: ARM11 MPCore: RFO in v6_coherent_{kern,user}_range and v6_flush_kern_dcache_area are not preempt safe
The RFO operations added to v6_coherent_{kern,user}_range and v6_flush_kern_dcache_area via the "ARM: Allow lazy cache flushing on ARM11MPCore" commit are not preempt safe. If preemption occurs immediately following a RFO operation on a cache line of CPU A, it is possible for task migration to occur on resume at which point the subsequent cache maintenance operation on the same cache line of CPU B will have no affect on the CPU A cache line leading to inconsistent memory and/or cache state. To prevent this, disable preemption during RFO operations in these functions.
This change depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
arch/arm/mm/cache-v6.S +30 -0
@@ -123,6 +123,12 @@ ENTRY(v6_coherent_kern_range) */ ENTRY(v6_coherent_user_range) UNWIND(.fnstart ) +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif #ifdef HARVARD_CACHE bic r0, r0, #CACHE_LINE_SIZE - 1 1:
@@ -147,6 +153,15 @@ ENTRY(v6_coherent_user_range) #else mcr p15, 0, r0, c7, c5, 6 @ invalidate BTB #endif +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + teq r3, #0 @ preempt count == 0? + str r3, [ip, #TI_PREEMPT] @ restore preempt count + bne 99f @ done if non-zero + ldr r3, [ip, #TI_FLAGS] @ else check flags + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ yes, do preempt_schedule +99: +#endif mov pc, lr /*
@@ -172,6 +187,12 @@ ENDPROC(v6_coherent_kern_range) * - size - region size */ ENTRY(v6_flush_kern_dcache_area) +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif add r1, r0, r1 bic r0, r0, #D_CACHE_LINE_SIZE - 1 1:
@@ -191,6 +212,15 @@ ENTRY(v6_flush_kern_dcache_area) mov r0, #0 mcr p15, 0, r0, c7, c10, 4 #endif +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + teq r3, #0 @ preempt count == 0? + str r3, [ip, #TI_PREEMPT] @ restore preempt count + bne 99f @ done if non-zero + ldr r3, [ip, #TI_FLAGS] @ else check flags + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ yes, do preempt_schedule +99: +#endif mov pc, lr
Thanks!
--
Regards,
George
Quoted 22 lines
arch/arm/mm/flush.c +1
-2@@ -291,8 +291,7 @@ void flush_dcache_page(struct page *page) mapping =page_mapping (page); -if (!cache_ops_need_broadcast () && - mapping && !mapping_mapped (mapping)) +if (mapping && !mapping_mapped (mapping))clear_bit (PG_dcache_clean, &page->flags);else {__flush_dcache_page (mapping, page);Signature · 7 lines
-- Catalin _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Hello,
Quoted 23 lines · On Mon, Jun 20, 2011 at 03:43:27PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
Hi,
I'm getting unexpected results from loading several modules - some of them in parallel - on an ARM11 MPcore system.
The system does not use "single sequential" modprobe commands - instead it starts several shells doing insmod sequences like this:
shell A:
insmod a
insmod ab
insmod abcshell B:
insmod b
insmod bc
insmod bcdshell C:
insmod c
insmod cd
insmod cde
Quoted 8 lines
This is done to crash^H^H^H^H^Hboot faster ;)
While one insmod operation is protected via the
module_mutex- I'm wondering what happens with the instruction cache invalidation.
AFAICT theflush_icache_rangeonly invalidates the ICache on the running cpu. Themodule_mutexis unlocked after _loading_ the module,do_mod_ctors()anddo_one_initcall()are called without that lock - can they run on a different cpu? It's an preemptible system (SMP PREEMPT armv6l).
In case anyone missed the subtlety, this report was for an ARM11 MPCore system with CONFIG_PREEMPT enabled. I've also been looking into this and various other memory corruption issues on ARM11 MPCore with CONFIG_PREEMPT enabled and have come to the conclusion that CONFIG_PREEMPT is broken on ARM11 MPCore.
I added the following instrumentation in 3.1.0-rc4ish to watch for process migration in a few places of interest:
arch/arm/include/asm/pgalloc.h +18 -0
@@ -62,11 +62,18 @@ static inline pte_t * pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr) {pte_t *pte; +int cpu =raw_smp_processor_id (); pte = (pte_t *)__get_free_page (PGALLOC_GFP );if (pte)clean_pte_table (pte); +// WARN_ON(cpu != raw_smp_processor_id()); +if (cpu !=raw_smp_processor_id ()) +printk (KERN_EMERG "%s:%d: cpu was %d but is now %d, memory corruption is possible\n" , + __func__, __LINE__, cpu,raw_smp_processor_id ()); + +return pte; }
@@ -74,6 +81,7 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long addr) {struct page *pte; +int cpu =raw_smp_processor_id ();#ifdef CONFIG_HIGHPTE pte =alloc_pages (PGALLOC_GFP | __GFP_HIGHMEM,0 );
@@ -86,6 +94,11 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)pgtable_page_ctor (pte); } +// WARN_ON(cpu != raw_smp_processor_id()); +if (cpu !=raw_smp_processor_id ()) +printk (KERN_EMERG "%s:%d: cpu was %d but is now %d, memory corruption is possible\n" , + __func__, __LINE__, cpu,raw_smp_processor_id ()); +return pte; }
@@ -108,9 +121,14 @@ static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, unsigned long prot) {unsigned long pmdval = (pte +PTE_HWTABLE_OFF ) | prot; +int cpu =raw_smp_processor_id (); pmdp[0 ] =__pmd (pmdval); pmdp[1 ] =__pmd (pmdval +256 *sizeof (pte_t));flush_pmd_entry (pmdp); +// WARN_ON(cpu != raw_smp_processor_id()); +if (cpu !=raw_smp_processor_id ()) +printk (KERN_EMERG "%s:%d: cpu was %d but is now %d, memory corruption is possible\n" , + __func__, __LINE__, cpu,raw_smp_processor_id ()); }/*
kernel/module.c +4 -0
@@ -2807,6 +2807,7 @@ static struct module *load_module(void __user *umod,struct load_info info = {NULL , };struct module *mod;long err; +int cpu =raw_smp_processor_id ();DEBUGP ("load_module: umod=%p, len=%lu, uargs=%p\n" , umod, len, uargs);
@@ -2852,6 +2853,9 @@ static struct module *load_module(void __user *umod,if (err <0 ) goto free_modinfo; +if (cpu !=raw_smp_processor_id ()) +printk (KERN_EMERG "%s:%d: cpu was %d but is now %d, memory corruption is possible\n" , + __func__, __LINE__, cpu,raw_smp_processor_id ());flush_module_icache (mod);/* Now copy in args */
Now with sufficient system stress, I get the following recurring problems (it's a 3-core system : ):
load_module:2858: cpu was 0 but is now 1, memory corruption is possible load_module:2858: cpu was 0 but is now 2, memory corruption is possible load_module:2858: cpu was 1 but is now 0, memory corruption is possible load_module:2858: cpu was 1 but is now 2, memory corruption is possible load_module:2858: cpu was 2 but is now 0, memory corruption is possible load_module:2858: cpu was 2 but is now 1, memory corruption is possible pte_alloc_one:100: cpu was 0 but is now 1, memory corruption is possible pte_alloc_one:100: cpu was 0 but is now 2, memory corruption is possible pte_alloc_one:100: cpu was 1 but is now 0, memory corruption is possible pte_alloc_one:100: cpu was 1 but is now 2, memory corruption is possible pte_alloc_one:100: cpu was 2 but is now 0, memory corruption is possible pte_alloc_one:100: cpu was 2 but is now 1, memory corruption is possible pte_alloc_one_kernel:74: cpu was 2 but is now 1, memory corruption is possible
With sufficient stress and extended run time, the system will eventually hang or oops with non-sensical oops traces - machine state does not make sense relative to the code excuting@the time of the oops.
The interesting point here is that each of the above contain critical sections in which ARM11 MPCore memory is inconsistent, i.e. cache on CPU A contains modified entries but then migration occurs and the cache is flushed on CPU B yet those cache ops called in the above cases do not implement ARM11 MPCore RWFO workarounds. Furthermore, the current ARM11 MPCore RWFO workarounds for DMA et al are unsafe as well for the CONFIG_PREEMPT case because, again, process migration can occur during DMA cache maintance operations in between RWFO and cache op instructions resulting in memory inconsistencies for the DMA case - a very narrow but real window.
So what's the recommendation, don't use CONFIG_PREEMPT on ARM11 MPCore?
Are there any known fixes for CONFIG_PREEMPT on ARM11 MPCore if it is indeed broken as it appears?
Thanks in advance for comments.
--
Regards,
George
Quoted 92 lines
Wouldn't it be required to flush the icache on _all_ cpus?
It might be highly dependable from the cache organization, but isn't it possible that the cpu that didn't load the module, is scheduled to run a function from it with a "stale" instruction cache line???I sometimes see oopses where I wonder how such a state could be reached. The code and register state seem not to fit - looks like an interrupt does not restore all registers properly :)
Also an Oops happens when loading module abc, and still the module list is reported empty in the Oops despite the fact that "abc" depends on "a". Hmh.
Best regards
Peter
<snip kernel/module.c:SYSCALL_DEFINE3(init_module> [..]
/* Only one module load at a time, please */ if (mutex_lock_interruptible(&module_mutex) != 0) return -EINTR; /* Do all the hard work */ mod = load_module(umod, len, uargs); if (IS_ERR(mod)) { mutex_unlock(&module_mutex); return PTR_ERR(mod); } /* Drop lock so they can recurse */ mutex_unlock(&module_mutex); blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod); do_mod_ctors(mod); /* Start the module */ if (mod->init != NULL) ret = do_one_initcall(mod->init);</snip>
<snip kernel/module.c:load_module>
[..]old_fs = get_fs(); set_fs(KERNEL_DS); /* * Flush the instruction cache, since we've played with text. * Do it before processing of module parameters, so the module * can provide parameter accessor functions of its own. */ if (mod->module_init) flush_icache_range((unsigned long)mod->module_init, (unsigned long)mod->module_init + mod->init_size); flush_icache_range((unsigned long)mod->module_core, (unsigned long)mod->module_core + mod->core_size); set_fs(old_fs);</snip>
arch/arm/mm/cache-v6.S:
ENTRY(v6_flush_kern_cache_all)mov r0, #0#ifdef
HARVARD_CACHE
mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate
#ifndefCONFIG_ARM_ERRATA_411920
mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate
#else
bv6_icache_inval_all
#endif
#else
mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate
#endif
mov pc, lr_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Hi George,
Quoted 3 lines · George G. Davis
Quoted 2 lines · On Mon, Jun 20, 2011 at 03:43:27PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
I'm getting unexpected results from loading several modules - some of them in parallel - on an ARM11 MPcore system.
...
Quoted 7 lines
In case anyone missed the subtlety, this report was for an ARM11 MPCore system with
CONFIG_PREEMPTenabled. ?I've also been looking into this and various other memory corruption issues on ARM11 MPCore withCONFIG_PREEMPTenabled and have come to the conclusion thatCONFIG_PREEMPTis broken on ARM11 MPCore.I added the following instrumentation in 3.1.0-rc4ish to watch for process migration in a few places of interest:
...
Quoted 17 lines
Now with sufficient system stress, I get the following recurring problems (it's a 3-core system : ):
load_module:2858: cpu was 0 but is now 1, memory corruption is possible load_module:2858: cpu was 0 but is now 2, memory corruption is possible load_module:2858: cpu was 1 but is now 0, memory corruption is possible load_module:2858: cpu was 1 but is now 2, memory corruption is possible load_module:2858: cpu was 2 but is now 0, memory corruption is possible load_module:2858: cpu was 2 but is now 1, memory corruption is possible pte_alloc_one:100: cpu was 0 but is now 1, memory corruption is possible pte_alloc_one:100: cpu was 0 but is now 2, memory corruption is possible pte_alloc_one:100: cpu was 1 but is now 0, memory corruption is possible pte_alloc_one:100: cpu was 1 but is now 2, memory corruption is possible pte_alloc_one:100: cpu was 2 but is now 0, memory corruption is possible pte_alloc_one:100: cpu was 2 but is now 1, memory corruption is possible pte_alloc_one_kernel:74: cpu was 2 but is now 1, memory corruption is possible
Quoted 3 lines
With sufficient stress and extended run time, the system will eventually hang or oops with non-sensical oops traces - machine state does not make sense relative to the code excuting at the time of the oops.
I think your analysis is valid and these places are not safe with CONFIG_PREEMPT enabled.
Quoted 5 lines
The interesting point here is that each of the above contain critical sections in which ARM11 MPCore memory is inconsistent, i.e. cache on CPU A contains modified entries but then migration occurs and the cache is flushed on CPU B yet those cache ops called in the above cases do not implement ARM11 MPCore RWFO workarounds.
I agree, my follow-up patch to implement lazy cache flushing on ARM11MPCore was meant for other uses (like drivers not calling flush_dcache_page), I never had PREEMPT in mind.
Quoted 6 lines
Furthermore,
the current ARM11 MPCore RWFO workarounds for DMA et al are unsafe as well for theCONFIG_PREEMPTcase because, again, process migration can occur during DMA cache maintance operations in between RWFO and cache op instructions resulting in memory inconsistencies for the DMA case - a very narrow but real window.
Yes, that's correct.
Quoted 4 lines
So what's the recommendation, don't use
CONFIG_PREEMPTon ARM11 MPCore?Are there any known fixes for
CONFIG_PREEMPTon ARM11 MPCore if it is indeed broken as it appears?
The scenarios you have described look valid to me. I think for now we can say that ARM11MPCore and PREEMPT don't go well together. This can be fixed though by making sure that cache maintenance places with the RWFO trick have the preemption disabled. But the RWFO has some performance impact as well, so I would only use it where absolutely necessary. In this case, I would just disable PREEMPT.
Signature · 1 line
Catalin
Hello Catalin,
On Sep 22, 2011, at 4:52 AM, Catalin Marinas wrote:
Ugh, sorry, I've been having problems with fetchmail/POP and did not see your reply until just now, logged into IMAP finally. : /
Quoted 32 lines
Hi George,
Quoted 3 lines · George G. Davis
Quoted 2 lines · On Mon, Jun 20, 2011 at 03:43:27PM +0200, EXTERNAL Waechtler Peter (Fa. TCP…
I'm getting unexpected results from loading several modules - some of them in parallel - on an ARM11 MPcore system.
...
Quoted 7 lines
In case anyone missed the subtlety, this report was for an ARM11 MPCore system with
CONFIG_PREEMPTenabled. I've also been looking into this and various other memory corruption issues on ARM11 MPCore withCONFIG_PREEMPTenabled and have come to the conclusion thatCONFIG_PREEMPTis broken on ARM11 MPCore.I added the following instrumentation in 3.1.0-rc4ish to watch for process migration in a few places of interest:
...
Quoted 17 lines
Now with sufficient system stress, I get the following recurring problems (it's a 3-core system : ):
load_module:2858: cpu was 0 but is now 1, memory corruption is possible load_module:2858: cpu was 0 but is now 2, memory corruption is possible load_module:2858: cpu was 1 but is now 0, memory corruption is possible load_module:2858: cpu was 1 but is now 2, memory corruption is possible load_module:2858: cpu was 2 but is now 0, memory corruption is possible load_module:2858: cpu was 2 but is now 1, memory corruption is possible pte_alloc_one:100: cpu was 0 but is now 1, memory corruption is possible pte_alloc_one:100: cpu was 0 but is now 2, memory corruption is possible pte_alloc_one:100: cpu was 1 but is now 0, memory corruption is possible pte_alloc_one:100: cpu was 1 but is now 2, memory corruption is possible pte_alloc_one:100: cpu was 2 but is now 0, memory corruption is possible pte_alloc_one:100: cpu was 2 but is now 1, memory corruption is possible pte_alloc_one_kernel:74: cpu was 2 but is now 1, memory corruption is possible
Quoted 6 lines
Quoted 3 lines
With sufficient stress and extended run time, the system will eventually hang or oops with non-sensical oops traces - machine state does not make sense relative to the code excuting at the time of the oops.
I think your analysis is valid and these places are not safe with
CONFIG_PREEMPTenabled.
Alas, the stress test stability problems persist even with CONFIG_PREEMPT off. Perhaps the windows are smaller, but they still exist.
Quoted 19 lines
Quoted 5 lines
The interesting point here is that each of the above contain critical sections in which ARM11 MPCore memory is inconsistent, i.e. cache on CPU A contains modified entries but then migration occurs and the cache is flushed on CPU B yet those cache ops called in the above cases do not implement ARM11 MPCore RWFO workarounds.
I agree, my follow-up patch to implement lazy cache flushing on ARM11MPCore was meant for other uses (like drivers not calling
flush_dcache_page), I never had PREEMPT in mind.Quoted 6 lines
Furthermore,
the current ARM11 MPCore RWFO workarounds for DMA et al are unsafe as well for theCONFIG_PREEMPTcase because, again, process migration can occur during DMA cache maintance operations in between RWFO and cache op instructions resulting in memory inconsistencies for the DMA case - a very narrow but real window.Yes, that's correct.
Quoted 7 lines
Quoted 4 lines
So what's the recommendation, don't use
CONFIG_PREEMPTon ARM11 MPCore?Are there any known fixes for
CONFIG_PREEMPTon ARM11 MPCore if it is indeed broken as it appears?The scenarios you have described look valid to me. I think for now we can say that ARM11MPCore and PREEMPT don't go well together.
But it is unreliable even for the !PREEMPT case based on my stress testing, even with your lazy cache flushing workaound applied. : /
Quoted 5 lines
This can
be fixed though by making sure that cache maintenance places with the RWFO trick have the preemption disabled. But the RWFO has some performance impact as well, so I would only use it where absolutely necessary. In this case, I would just disable PREEMPT.
I'll post a series of RFC patches which adress the "low hanging fruit". I'm still working on the harder nuts which of course have performance trade offs between RWFO v. broadcast cache ops to consider...
Thanks and apologies again for lack of follow up reply on my part. I blame my fetchmail/POP as I'm still getting at least some LAKML messages, just not all. : /
--
Regards,
George
Quoted 3 lines
Signature · 2 lines
-- Catalin
cover ARM: ARM11 MPCore preemption/task migration cache coherency fixups
Greetings,
On ARM11 MPCore, the "SCU does not handle coherency consequences of CP15 cache operations" [1]. So cache maintenance functions have to insure that memory is globally consistent. Although the current Linux kernel works reasonably well on ARM11 MPCore machines, PREEMPT stress testing, e.g. parallel module loading and hackbench, results in crashes which exhibit non-sense oops traces where machine state does not make sense relative to the code executing at the time of the oops.
Review and analysis of the various ARM11 MPCore cache maintenance functions reveal that there are a number critical sections in which ARM11 MPCore caches and/or memory may become inconsistent, i.e. a cache line on CPU A contains a modified entry but preemption and task migration occurs after which the same cache line is cleaned/flushed on CPU B. This can obviously lead to inconsistent memory and/or cache state as cache ops on ARM11 MPCore are non-coherent.
The following is a partial series of ARM11 MPCore preemption/task migration fixes to resolve cache coherency problems on these machines:
George G. Davis (6):
ARM: ARM11 MPCore: pte_alloc_one{,_kernel} are not preempt safe
ARM: ARM11 MPCore: {clean,flush}_pmd_entry are not preempt safe
ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
ARM: Move get_thread_info macro definition to <asm/assembler.h>
ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
Konstantin Baidarov (1):
ARM: ARM11 MPCore: pgd_alloc is not preempt safe
arch/arm/include/asm/assembler.h | 13 +++++++++++++ arch/arm/include/asm/pgalloc.h | 23 ++++++++++++++++++++--- arch/arm/include/asm/pgtable.h | 9 +++++++++ arch/arm/include/asm/smp_plat.h | 2 ++ arch/arm/kernel/entry-header.S | 11 ----------- arch/arm/mm/cache-v6.S | 33 +++++++++++++++++++++++++++++++++ arch/arm/mm/idmap.c | 5 +++++ arch/arm/mm/ioremap.c | 9 +++++++++ arch/arm/mm/mmu.c | 12 ++++++++++++ arch/arm/mm/pgd.c | 7 +++++++ arch/arm/mm/proc-v6.S | 11 +++++++++++ arch/arm/plat-omap/iommu.c | 10 ++++++++++ arch/arm/vfp/entry.S | 5 ++++- arch/arm/vfp/vfphw.S | 5 ++++- 14 files changed, 139 insertions(+), 16 deletions(-)
The above changes are among the "low hanging fruit" where the workarounds are relatively low impact from a performance and/or implementation effort perspective. I'm still working on fixes for the harder problems. Also, I believe that Catalin's "ARM: Allow lazy cache flushing on ARM11MPCore" [2][3] is required for ARM11 MPCore machines and would like to see that or similar/alternative solution applied.
Comments/feedback greatly appreciated.
TIA!
--
Regards,
George
[1] http://infocenter.arm.com/help/topic/com.arm.doc.dai0228a/index.html#arm_toc9
[2] http://www.spinics.net/lists/arm-kernel/msg129403.html
[3] http://lists.infradead.org/pipermail/linux-arm-kernel/2010-May/014990.html
PATCH 1/7 ARM: ARM11 MPCore: pgd_alloc is not preempt safe
From: Konstantin Baidarov <[email protected]>
If preemption and subsequent task migration occurs during a call to pgd_alloc on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption around initialization and flushing of new PGDs.
--- arch/arm/mm/pgd.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
arch/arm/mm/pgd.c +7 -0
@@ -14,6 +14,7 @@#include <asm/pgalloc.h> #include <asm/page.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #include "mm.h"
@@ -31,6 +32,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)if (!new_pgd) goto no_pgd; +if (cache_ops_need_broadcast ()) +get_cpu (); +memset (new_pgd,0 ,USER_PTRS_PER_PGD *sizeof (pgd_t));/*
@@ -42,6 +46,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)clean_dcache_area (new_pgd,PTRS_PER_PGD *sizeof (pgd_t)); +if (cache_ops_need_broadcast ()) +put_cpu (); +if (!vectors_high ()) {/* * On ARM, first page must always be allocated since it
Signature · 1 line
1.7.4.4
PATCH 1/7 ARM: ARM11 MPCore: pgd_alloc is not preempt safe
arch/arm/mm/pgd.c pgd_t *pgd_alloc(struct mm_struct *mm) · line 36
Quoted 6 lines · gdavis at mvista.com
arch/arm/mm/pgd.c +2
-0@@ -31,6 +32,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)if (!new_pgd) goto no_pgd; +if (cache_ops_need_broadcast ()) +get_cpu ();
Is there something wrong with preempt_disable() here and preempt_enable() below? If it's preempt that we're concerned about, these are the correct interfaces to be used.
Quoted 11 lines
arch/arm/mm/pgd.c +4
-0+memset (new_pgd,0 ,USER_PTRS_PER_PGD *sizeof (pgd_t));/* @@ -42,6 +46,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)clean_dcache_area (new_pgd,PTRS_PER_PGD *sizeof (pgd_t)); +if (cache_ops_need_broadcast ()) +put_cpu (); +
PATCH 1/7 ARM: ARM11 MPCore: pgd_alloc is not preempt safe
Quoted 10 lines · Russell King - ARM Linux
arch/arm/mm/pgd.c pgd_t *pgd_alloc(struct mm_struct *mm) · line 36
Quoted 6 lines · gdavis at mvista.com
arch/arm/mm/pgd.c +2
-0@@ -31,6 +32,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)if (!new_pgd) goto no_pgd; +if (cache_ops_need_broadcast ()) +get_cpu ();Is there something wrong with
preempt_disable()here andpreempt_enable()below?
I used the {get,put}_cpu() variants merely to (try to) make it clear that we want this region to be non-preemptible for the current CPU.
Quoted 2 lines
If it's preempt that we're concerned about, these are the correct
interfaces to be used.
Since {get,put}_cpu() are wrappers for preempt_{enable,disable}, it works either way and I'm happy to just use preempt_{enable,disable} instead. It achieves the same goal with slightly less overhead. Also, while we're on the subject of preempt_enable, an earlier version of this change used preempt_enable_no_resched() but I chose to avoid the no_reched flavor since I felt that it was worth minimizing scheduler latencies.
I'll change all of the {get,put}_cpu() variants to preempt_{enable,disable} and submit an updated series.
Thanks!
--
Regards,
George
Quoted 11 lines
Quoted 11 lines
arch/arm/mm/pgd.c +4
-0+memset (new_pgd,0 ,USER_PTRS_PER_PGD *sizeof (pgd_t));/* @@ -42,6 +46,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)clean_dcache_area (new_pgd,PTRS_PER_PGD *sizeof (pgd_t)); +if (cache_ops_need_broadcast ()) +put_cpu (); +
PATCH 2/7 ARM: ARM11 MPCore: pte_alloc_one{, _kernel} are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during a call to pte_alloc_one{,_kernel} on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption around initialization and flushing of new PTEs.
--- arch/arm/include/asm/pgalloc.h | 19 ++++++++++++++++--- 1 files changed, 16 insertions(+), 3 deletions(-)
arch/arm/include/asm/pgalloc.h +16 -3
@@ -17,6 +17,7 @@#include <asm/processor.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #define check_pgt_cache () do { } while (0)
@@ -35,7 +36,7 @@extern pgd_t *pgd_alloc (struct mm_struct *mm);extern void pgd_free (struct mm_struct *mm,pgd_t *pgd); -#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO) +#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT)static inline void clean_pte_table (pte_t *pte) {
@@ -64,8 +65,14 @@ pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)pte_t *pte; pte = (pte_t *)__get_free_page (PGALLOC_GFP ); -if (pte) +if (pte) { +if (cache_ops_need_broadcast ()) +get_cpu (); +memset (pte,0 ,PAGE_SIZE );clean_pte_table (pte); +if (cache_ops_need_broadcast ()) +put_cpu (); + }return pte; }
@@ -81,8 +88,14 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr) pte =alloc_pages (PGALLOC_GFP ,0 );#endif if (pte) { +void *p =page_address (pte); +if (cache_ops_need_broadcast ()) +get_cpu (); +memset (p,0 ,PAGE_SIZE );if (!PageHighMem (pte)) -clean_pte_table (page_address (pte)); +clean_pte_table (p); +if (cache_ops_need_broadcast ()) +put_cpu ();pgtable_page_ctor (pte); }
Signature · 1 line
1.7.4.4
PATCH 3/7 ARM: ARM11 MPCore: {clean, flush}_pmd_entry are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to {clean,flush}_pmd_entry on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around PMD modifications and subsequent {clean,flush}_pmd_entry calls.
--- arch/arm/include/asm/pgalloc.h | 4 ++++ arch/arm/include/asm/pgtable.h | 9 +++++++++ arch/arm/include/asm/smp_plat.h | 2 ++ arch/arm/mm/idmap.c | 5 +++++ arch/arm/mm/ioremap.c | 9 +++++++++ arch/arm/mm/mmu.c | 12 ++++++++++++ 6 files changed, 41 insertions(+), 0 deletions(-)
arch/arm/include/asm/pgalloc.h +4 -0
@@ -121,9 +121,13 @@ static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, unsigned long prot) {unsigned long pmdval = (pte +PTE_HWTABLE_OFF ) | prot; +if (cache_ops_need_broadcast ()) +get_cpu (); pmdp[0 ] =__pmd (pmdval); pmdp[1 ] =__pmd (pmdval +256 *sizeof (pte_t));flush_pmd_entry (pmdp); +if (cache_ops_need_broadcast ()) +put_cpu (); }/*
arch/arm/include/asm/pgtable.h +9 -0
@@ -13,6 +13,7 @@#include <linux/const.h> #include <asm-generic/4level-fixup.h> #include <asm/proc-fns.h> +#include <asm/smp_plat.h> #ifndef CONFIG_MMU
@@ -313,16 +314,24 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];#define copy_pmd (pmdpd,pmdps) \ do { \ + if (cache_ops_need_broadcast()) \ + get_cpu(); \ pmdpd[0] = pmdps[0]; \ pmdpd[1] = pmdps[1]; \ flush_pmd_entry(pmdpd); \ + if (cache_ops_need_broadcast()) \ + put_cpu(); \ } while (0)#define pmd_clear (pmdp) \ do { \ + if (cache_ops_need_broadcast()) \ + get_cpu(); \ pmdp[0] = __pmd(0); \ pmdp[1] = __pmd(0); \ clean_pmd_entry(pmdp); \ + if (cache_ops_need_broadcast()) \ + put_cpu(); \ } while (0)static inline pte_t *pmd_page_vaddr (pmd_t pmd)
arch/arm/include/asm/smp_plat.h +2 -0
@@ -5,6 +5,7 @@#ifndef __ASMARM_SMP_PLAT_H#define __ASMARM_SMP_PLAT_H +#ifndef __ASSEMBLY__#include <asm/cputype.h> /*
@@ -42,5 +43,6 @@ static inline int cache_ops_need_broadcast(void)return ((read_cpuid_ext (CPUID_EXT_MMFR3 ) >>12 ) &0xf ) <1 ; }#endif +#endif #endif
arch/arm/mm/idmap.c +5 -0
@@ -3,17 +3,22 @@#include <asm/cputype.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> +#include <asm/smp_plat.h> static void idmap_add_pmd (pud_t *pud,unsigned long addr,unsigned long end,unsigned long prot) {pmd_t *pmd =pmd_offset (pud, addr); +if (cache_ops_need_broadcast ()) +get_cpu (); addr = (addr &PMD_MASK ) | prot; pmd[0 ] =__pmd (addr); addr +=SECTION_SIZE ; pmd[1 ] =__pmd (addr);flush_pmd_entry (pmd); +if (cache_ops_need_broadcast ()) +put_cpu (); } static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
arch/arm/mm/ioremap.c +9 -0
@@ -32,6 +32,7 @@#include <asm/pgalloc.h> #include <asm/tlbflush.h> #include <asm/sizes.h> +#include <asm/smp_plat.h> #include <asm/mach/map.h> #include "mm.h"
@@ -135,11 +136,15 @@ remap_area_sections(unsigned long virt, unsigned long pfn, do { pmd_t *pmd = pmd_offset(pgd, addr); + if (cache_ops_need_broadcast()) + get_cpu(); pmd[0] = __pmd(__pfn_to_phys(pfn) | type->prot_sect); pfn += SZ_1M >> PAGE_SHIFT; pmd[1] = __pmd(__pfn_to_phys(pfn) | type->prot_sect); pfn += SZ_1M >> PAGE_SHIFT; flush_pmd_entry(pmd); + if (cache_ops_need_broadcast()) + put_cpu(); addr += PGDIR_SIZE; pgd++;
@@ -172,9 +177,13 @@ remap_area_supersections(unsigned long virt, unsigned long pfn,for (i =0 ; i <8 ; i++) {pmd_t *pmd =pmd_offset (pgd, addr); +if (cache_ops_need_broadcast ()) +get_cpu (); pmd[0 ] =__pmd (super_pmd_val); pmd[1 ] =__pmd (super_pmd_val);flush_pmd_entry (pmd); +if (cache_ops_need_broadcast ()) +put_cpu (); addr +=PGDIR_SIZE ; pgd++;
arch/arm/mm/mmu.c +12 -0
@@ -567,12 +567,24 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,if (addr &SECTION_SIZE ) pmd++; +if (cache_ops_need_broadcast ()) +get_cpu ();do { *pmd =__pmd (phys | type->prot_sect); phys +=SECTION_SIZE ; }while (pmd++, addr +=SECTION_SIZE , addr != end); +/* FIXME: Multiple PMD entries may be written above +* but only one cache line, up to 8 PMDs depending +* on the alignment of this mapping, is flushed below. +* IFF this mapping spans >8MiB, then only the first +* 8MiB worth of entries will be flushed. Entries +* above the 8MiB limit will not be flushed if I +* read this correctly. +*/ flush_pmd_entry (p); +if (cache_ops_need_broadcast ()) +put_cpu (); }else {/* * No need to loop; pte's aren't interested in the
Signature · 1 line
1.7.4.4
PATCH 4/7 ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to clean_dcache_area on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around memory modifications and subsequent clean_dcache_area calls.
--- arch/arm/plat-omap/iommu.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
arch/arm/plat-omap/iommu.c +10 -0
@@ -1014,8 +1014,12 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) err = -ENOMEM ; goto err_pgd; } +if (cache_ops_need_broadcast ()) +get_cpu ();memset (p,0 ,IOPGD_TABLE_SIZE );clean_dcache_area (p,IOPGD_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +put_cpu (); obj->iopgd = p;BUG_ON (!IS_ALIGNED ((unsigned long )obj->iopgd,IOPGD_TABLE_SIZE ));
@@ -1069,7 +1073,13 @@ static struct platform_driver omap_iommu_driver = {static void iopte_cachep_ctor (void *iopte) { +if (cache_ops_need_broadcast ()) +get_cpu (); +/* FIXME: This will not work on ARM11 MPCore. +*/ clean_dcache_area (iopte,IOPTE_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +put_cpu (); }static int __init omap_iommu_init (void )
Signature · 1 line
1.7.4.4
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
The get_thread_info assembler macro is currently defined in arch/arm/kernel/entry-header.S and used in the following files:
arch/arm/kernel/entry-armv.S
arch/arm/kernel/entry-common.S
arch/arm/vfp/vfphw.S
arch/arm/vfp/entry.S
The first two cases above use #include "entry-header.S" while the later two cases use #include "../kernel/entry-header.S" to obtain the get_thread_info assembler macro definition.
Move the get_thread_info assembler macro definition into arch/arm/include/asm/assember.h and clean up inclusion of this macro definition to use #include <asm/assember.h>.
This change facilitates use of the get_thread_info assembler macro in other assembler functions without more of the same path relative include directives.
--- arch/arm/include/asm/assembler.h | 13 +++++++++++++ arch/arm/kernel/entry-header.S | 11 ----------- arch/arm/vfp/entry.S | 5 ++++- arch/arm/vfp/vfphw.S | 5 ++++- 4 files changed, 21 insertions(+), 13 deletions(-)
arch/arm/include/asm/assembler.h +13 -0
@@ -23,6 +23,19 @@#include <asm/ptrace.h> #include <asm/domain.h> +#ifndef CONFIG_THUMB2_KERNEL + .macro get_thread_info, rd + mov \rd, sp, lsr #13 + mov \rd, \rd, lsl #13 + .endm +#else /* CONFIG_THUMB2_KERNEL */ + .macro get_thread_info, rd + mov \rd, sp + lsr \rd, \rd, #13 + mov \rd, \rd, lsl #13 + .endm +#endif /* !CONFIG_THUMB2_KERNEL */ +/* * Endian independent macros for shifting bytes within registers. */
arch/arm/kernel/entry-header.S +0 -11
@@ -108,11 +108,6 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm - .macro get_thread_info, rd - mov \rd, sp, lsr #13 - mov \rd, \rd, lsl #13 - .endm - @ @ 32-bit wide "mov pc, reg" @
@@ -148,12 +143,6 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm - .macro get_thread_info, rd - mov \rd, sp - lsr \rd, \rd, #13 - mov \rd, \rd, lsl #13 - .endm - @ @ 32-bit wide "mov pc, reg" @
arch/arm/vfp/entry.S +4 -1
@@ -15,9 +15,12 @@ * r10 = thread_info structure * lr = failure return */ +#include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" +#include <asm/assembler.h> +#include <asm/asm-offsets.h> ENTRY(do_vfp) #ifdef CONFIG_PREEMPT
arch/arm/vfp/vfphw.S +4 -1
@@ -14,9 +14,12 @@ * r10 points at the start of the private FP workspace in the thread structure * sp points to a struct pt_regs (as defined in include/asm/proc/ptrace.h) */ +#include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" +#include <asm/assembler.h> +#include <asm/asm-offsets.h> .macro DBGSTR, str #ifdef DEBUG
Signature · 1 line
1.7.4.4
PATCH 6/7 ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
The DMA_CACHE_RWFO operations are not preempt safe. If preemption occurs immediately following a RWFO operation on a cache line of CPU A, it is possible for task migration to occur on resume at which point the subsequent cache maintenance operation on the same cache line of CPU B will have no affect on the CPU A cache line leading to inconsistent memory and/or cache state. To prevent this, disable preemption during RWFO operations.
This changes depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
--- arch/arm/mm/cache-v6.S | 33 +++++++++++++++++++++++++++++++++ 1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm/mm/cache-v6.S +33 -0
@@ -205,6 +205,13 @@ ENTRY(v6_flush_kern_dcache_area) */ v6_dma_inv_range: #ifdef CONFIG_DMA_CACHE_RWFO +#ifdef CONFIG_PREEMPT + stmdb sp!, {r4, r10, r11} + get_thread_info r10 + ldr r4, [r10, #TI_PREEMPT] @ get preempt count + add r11, r4, #1 @ increment it + str r11, [r10, #TI_PREEMPT] @ disable preempt +#endif ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership #endif
@@ -241,6 +248,10 @@ v6_dma_inv_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r4, [r10, #TI_PREEMPT] @ restore preempt count + ldmia sp!, {r4, r10, r11} +#endif mov pc, lr /*
@@ -249,6 +260,13 @@ v6_dma_inv_range: * - end - virtual end address of region */ v6_dma_clean_range: +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + stmdb sp!, {r4, r10, r11} + get_thread_info r10 + ldr r4, [r10, #TI_PREEMPT] @ get preempt count + add r11, r4, #1 @ increment it + str r11, [r10, #TI_PREEMPT] @ disable preempt +#endif bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: #ifdef CONFIG_DMA_CACHE_RWFO
@@ -264,6 +282,10 @@ v6_dma_clean_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r4, [r10, #TI_PREEMPT] @ restore preempt count + ldmia sp!, {r4, r10, r11} +#endif mov pc, lr /*
@@ -273,6 +295,13 @@ v6_dma_clean_range: */ ENTRY(v6_dma_flush_range) #ifdef CONFIG_DMA_CACHE_RWFO +#ifdef CONFIG_PREEMPT + stmdb sp!, {r4, r10, r11} + get_thread_info r10 + ldr r4, [r10, #TI_PREEMPT] @ get preempt count + add r11, r4, #1 @ increment it + str r11, [r10, #TI_PREEMPT] @ disable preempt +#endif ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership #endif
@@ -292,6 +321,10 @@ ENTRY(v6_dma_flush_range) blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r4, [r10, #TI_PREEMPT] @ restore preempt count + ldmia sp!, {r4, r10, r11} +#endif mov pc, lr /*
Signature · 1 line
1.7.4.4
PATCH 6/7 ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
arch/arm/mm/cache-v6.S ENTRY(v6_flush_kern_dcache_area) · line 214
Quoted 9 lines · gdavis at mvista.com
v6_dma_inv_range:
#ifdefCONFIG_DMA_CACHE_RWFOarch/arm/mm/cache-v6.S +7
-0+#ifdef CONFIG_PREEMPT + stmdb sp!, {r4, r10, r11} + get_thread_info r10 + ldr r4, [r10, #TI_PREEMPT] @ get preempt count + add r11, r4, #1 @ increment it + str r11, [r10, #TI_PREEMPT] @ disable preempt +#endif
r11 is the frame pointer. On kernels built with the frame pointer enabled, this register must either be a valid frame pointer or zero.
There's no reason to use r4, r10 and r11 here - you could use r4, r5 and ip (r12) - and then there's no need to save ip as that's allowed to be corrupted by called functions.
PATCH 6/7 ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
Quoted 13 lines · Russell King - ARM Linux
arch/arm/mm/cache-v6.S ENTRY(v6_flush_kern_dcache_area) · line 214
Quoted 9 lines · gdavis at mvista.com
v6_dma_inv_range:
#ifdefCONFIG_DMA_CACHE_RWFOarch/arm/mm/cache-v6.S +7
-0+#ifdef CONFIG_PREEMPT + stmdb sp!, {r4, r10, r11} + get_thread_info r10 + ldr r4, [r10, #TI_PREEMPT] @ get preempt count + add r11, r4, #1 @ increment it + str r11, [r10, #TI_PREEMPT] @ disable preempt +#endifr11 is the frame pointer. On kernels built with the frame pointer enabled, this register must either be a valid frame pointer or zero.
Doh!
Quoted 3 lines
There's no reason to use r4, r10 and r11 here - you could use r4, r5 and ip (r12) - and then there's no need to save ip as that's allowed to be corrupted by called functions.
OK, I'll make these changes in the next round.
Thanks again!
--
Regards,
George
PATCH 7/7 ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to cpu_v6_set_pte_ext on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in cpu_v6_set_pte_ext.
This changes depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
--- arch/arm/mm/proc-v6.S | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-)
arch/arm/mm/proc-v6.S +11 -0
@@ -122,7 +122,18 @@ ENTRY(cpu_v6_switch_mm) ENTRY(cpu_v6_set_pte_ext) #ifdef CONFIG_MMU +#ifdef CONFIG_PREEMPT + ALT_SMP(stmdb sp!, {r4, r10, r11}) + ALT_SMP(get_thread_info r10) + ALT_SMP(ldr r4, [r10, #TI_PREEMPT]) @ get preempt count + ALT_SMP(add r11, r4, #1) @ increment it + ALT_SMP(str r11, [r10, #TI_PREEMPT]) @ disable preempt +#endif armv6_set_pte_ext cpu_v6 +#ifdef CONFIG_PREEMPT + ALT_SMP(str r4, [r10, #TI_PREEMPT]) @ restore preempt count + ALT_SMP(ldmia sp!, {r4, r10, r11}) +#endif #endif mov pc, lr
Signature · 1 line
1.7.4.4
PATCH 7/7 ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
Quoted 11 lines · gdavis at mvista.com
arch/arm/mm/proc-v6.S +3
-0@@ -122,7 +122,18 @@ ENTRY(cpu_v6_switch_mm) ENTRY(cpu_v6_set_pte_ext) #ifdef CONFIG_MMU +#ifdef CONFIG_PREEMPT + ALT_SMP(stmdb sp!, {r4, r10, r11}) + ALT_SMP(get_thread_info r10)
arch/arm/mm/proc-v6.S ENTRY(cpu_v6_switch_mm) · line 137
Quoted 10 lines
arch/arm/mm/proc-v6.S +8
-0+ ALT_SMP(ldr r4, [r10, #TI_PREEMPT]) @ get preempt count + ALT_SMP(add r11, r4, #1) @ increment it + ALT_SMP(str r11, [r10, #TI_PREEMPT]) @ disable preempt +#endif armv6_set_pte_ext cpu_v6 +#ifdef CONFIG_PREEMPT + ALT_SMP(str r4, [r10, #TI_PREEMPT]) @ restore preempt count + ALT_SMP(ldmia sp!, {r4, r10, r11}) +#endif #endif
And what effect do you expect the above to have? Do you understand the purpose of the ALT_SMP macro? Do you understand that it should be paired with ALT_UP ?
PATCH 7/7 ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
Quoted 16 lines · Russell King - ARM Linux
Quoted 15 lines · gdavis at mvista.com
arch/arm/mm/proc-v6.S +7
-0@@ -122,7 +122,18 @@ ENTRY(cpu_v6_switch_mm) ENTRY(cpu_v6_set_pte_ext) #ifdef CONFIG_MMU +#ifdef CONFIG_PREEMPT + ALT_SMP(stmdb sp!, {r4, r10, r11}) + ALT_SMP(get_thread_info r10) + ALT_SMP(ldr r4, [r10, #TI_PREEMPT]) @ get preempt count + ALT_SMP(add r11, r4, #1) @ increment it + ALT_SMP(str r11, [r10, #TI_PREEMPT]) @ disable preempt +#endif
Quoted 8 lines
arch/arm/mm/proc-v6.S ENTRY(cpu_v6_switch_mm) · line 137
Quoted 6 lines
arch/arm/mm/proc-v6.S +4
-0armv6_set_pte_ext cpu_v6 +#ifdef CONFIG_PREEMPT + ALT_SMP(str r4, [r10, #TI_PREEMPT]) @ restore preempt count + ALT_SMP(ldmia sp!, {r4, r10, r11}) +#endif #endifAnd what effect do you expect the above to have?
My goal was to be proactive, heading off a request to use this macro for the SMP case since preempt disable is only required on ARM11 MPCore. I thought this would achieve that goal, only using those instructions on SMP, not UP machines.
Quoted 1 line
Do you understand the purpose of the
ALT_SMPmacro?
Clearly I don't understand the use case.
Quoted 1 line
Do you understand that it should be paired with
ALT_UP?
No, I did not know that. I'll remove use of ALT_SMP and add CONFIG_SMP as a conditional compile dependency then. Is that OK?
Thanks!
--
Regards,
George
v2 cover ARM11 MPCore: preemption/task migration cache coherency fixups
In reply to gdavis at mvista.comGreetings,
On ARM11 MPCore, the "SCU does not handle coherency consequences of CP15 cache operations" [1]. ?So cache maintenance functions have to insure that memory is globally consistent. ?Although the current Linux kernel works reasonably well on ARM11 MPCore machines, PREEMPT stress testing, e.g. parallel module loading and hackbench, results in crashes which exhibit non-sense oops traces where machine state does not make sense relative to the code executing at the time of the oops.
Review and analysis of the various ARM11 MPCore cache maintenance functions reveal that there are a number critical sections in which ARM11 MPCore caches and/or memory may become inconsistent, i.e. a cache line on CPU A contains a modified entry but preemption and task migration occurs after which the same cache line is cleaned/flushed on CPU B. ?This can obviously lead to inconsistent memory and/or cache state as cache ops on ARM11 MPCore are non-coherent.
The following is a partial series of ARM11 MPCore preemption/task migration fixes to resolve cache coherency problems on these machines:
George G. Davis (6):
ARM: ARM11 MPCore: pte_alloc_one{,_kernel} are not preempt safe
ARM: ARM11 MPCore: {clean,flush}_pmd_entry are not preempt safe
ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
ARM: Move get_thread_info macro definition to <asm/assembler.h>
ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
Konstantin Baidarov (1):
ARM: ARM11 MPCore: pgd_alloc is not preempt safe
arch/arm/include/asm/assembler.h | 13 +++++++++++++ arch/arm/include/asm/pgalloc.h | 23 ++++++++++++++++++++--- arch/arm/include/asm/pgtable.h | 9 +++++++++ arch/arm/include/asm/smp_plat.h | 2 ++ arch/arm/kernel/entry-header.S | 11 ----------- arch/arm/mm/cache-v6.S | 27 +++++++++++++++++++++++++++ arch/arm/mm/idmap.c | 5 +++++ arch/arm/mm/ioremap.c | 9 +++++++++ arch/arm/mm/mmu.c | 12 ++++++++++++ arch/arm/mm/pgd.c | 7 +++++++ arch/arm/mm/proc-v6.S | 11 +++++++++++ arch/arm/plat-omap/iommu.c | 10 ++++++++++ arch/arm/vfp/entry.S | 5 ++++- arch/arm/vfp/vfphw.S | 5 ++++- 14 files changed, 133 insertions(+), 16 deletions(-)
ChangeLog:
V2:
- Substitute {get,put}_cpu() with preempt_{disable,enable}().
- Fixed preempt {dis,en}able assembler code sequences to not use r11 since it is reserved for frame pointer use. Also optimised these sequences to use r2, r3; ip scratch registers in most cases to eliminate stack push/pop. In one case, cpu_v6_set_pte_ext, there are only two scratch registers available, r3 and ip. However, both of these are used within the armv6_set_pte_ext macro. So for this case, r3 is used as a temporary scratch when disabling preemption and r4 and r5 are pushed/popped as needed for other uses to avoid conflict with scratch register usage in armv6_set_pte_ext.
- Remove incorrect use of ALT_SMP macros in cpu_v6_set_pte_ext, making the preempt {dis,en}able assembler code sequences compile time dependent upon CONFIG_SMP instead. This code is safe on UP machines anyway.
The above changes are among the "low hanging fruit" where the workarounds are relatively low impact from a performance and/or implementation effort perspective. ?I'm still working on fixes for the harder problems. ?Also, I believe that Catalin's "ARM: Allow lazy cache flushing on ARM11MPCore" [2][3] is required for ARM11 MPCore machines and would like to see that or similar/alternative solution applied.
Comments/feedback greatly appreciated.
TIA!
--
Regards,
George
[1] http://infocenter.arm.com/help/topic/com.arm.doc.dai0228a/index.html#arm_toc9
[2] http://www.spinics.net/lists/arm-kernel/msg129403.html
[3] http://lists.infradead.org/pipermail/linux-arm-kernel/2010-May/014990.html
PATCH 1/7 ARM: ARM11 MPCore: pgd_alloc is not preempt safe
From: Konstantin Baidarov <[email protected]>
If preemption and subsequent task migration occurs during a call to pgd_alloc on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption around initialization and flushing of new PGDs.
--- arch/arm/mm/pgd.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
arch/arm/mm/pgd.c +7 -0
@@ -14,6 +14,7 @@#include <asm/pgalloc.h> #include <asm/page.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #include "mm.h"
@@ -31,6 +32,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)if (!new_pgd) goto no_pgd; +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (new_pgd,0 ,USER_PTRS_PER_PGD *sizeof (pgd_t));/*
@@ -42,6 +46,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)clean_dcache_area (new_pgd,PTRS_PER_PGD *sizeof (pgd_t)); +if (cache_ops_need_broadcast ()) +preempt_enable (); +if (!vectors_high ()) {/* * On ARM, first page must always be allocated since it
Signature · 1 line
1.7.4.4
PATCH 2/7 ARM: ARM11 MPCore: pte_alloc_one{, _kernel} are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during a call to pte_alloc_one{,_kernel} on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption around initialization and flushing of new PTEs.
--- arch/arm/include/asm/pgalloc.h | 19 ++++++++++++++++--- 1 files changed, 16 insertions(+), 3 deletions(-)
arch/arm/include/asm/pgalloc.h +16 -3
@@ -17,6 +17,7 @@#include <asm/processor.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #define check_pgt_cache () do { } while (0)
@@ -35,7 +36,7 @@extern pgd_t *pgd_alloc (struct mm_struct *mm);extern void pgd_free (struct mm_struct *mm,pgd_t *pgd); -#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO) +#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT)static inline void clean_pte_table (pte_t *pte) {
@@ -64,8 +65,14 @@ pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)pte_t *pte; pte = (pte_t *)__get_free_page (PGALLOC_GFP ); -if (pte) +if (pte) { +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (pte,0 ,PAGE_SIZE );clean_pte_table (pte); +if (cache_ops_need_broadcast ()) +preempt_enable (); + }return pte; }
@@ -81,8 +88,14 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr) pte =alloc_pages (PGALLOC_GFP ,0 );#endif if (pte) { +void *p =page_address (pte); +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (p,0 ,PAGE_SIZE );if (!PageHighMem (pte)) -clean_pte_table (page_address (pte)); +clean_pte_table (p); +if (cache_ops_need_broadcast ()) +preempt_enable ();pgtable_page_ctor (pte); }
Signature · 1 line
1.7.4.4
PATCH 2/7 ARM: ARM11 MPCore: pte_alloc_one{,_kernel} are not preempt safe
Quoted 5 lines · gdavis at mvista.com
arch/arm/include/asm/pgalloc.h +1
-1-#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO) +#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT)@@ -81,8 +88,14 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr) pte =alloc_pages (PGALLOC_GFP ,0 );#endif
arch/arm/include/asm/pgalloc.h pte_alloc_one(struct mm_struct *mm, unsigned long addr) · line 98
Quoted 10 lines
arch/arm/include/asm/pgalloc.h +7
-1if (pte) { +void *p =page_address (pte); +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (p,0 ,PAGE_SIZE );if (!PageHighMem (pte)) -clean_pte_table (page_address (pte)); +clean_pte_table (p); +if (cache_ops_need_broadcast ()) +preempt_enable ();
This won't work - page_address(pte) will be NULL for highmem pages, and so will cause an oops, and removing the __GFP_ZERO will mean that highmem pages will not be zeroed, meaning that such page tables will not have been initialized.
PATCH 2/7 ARM: ARM11 MPCore: pte_alloc_one{, _kernel} are not preempt safe
Quoted 13 lines · Russell King - ARM Linux
Quoted 12 lines · gdavis at mvista.com
arch/arm/include/asm/pgalloc.h +5
-2-#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO) +#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT)@@ -81,8 +88,14 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr) pte =alloc_pages (PGALLOC_GFP ,0 );#endif if (pte) { +void *p =page_address (pte); +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (p,0 ,PAGE_SIZE );if (!PageHighMem (pte)) -clean_pte_table (page_address (pte));
Quoted 8 lines
arch/arm/include/asm/pgalloc.h pte_alloc_one(struct mm_struct *mm, unsigned long addr) · line 98
Quoted 3 lines
arch/arm/include/asm/pgalloc.h +3
-0+clean_pte_table (p); +if (cache_ops_need_broadcast ()) +preempt_enable ();This won't work -
page_address(pte)will be NULL for highmem pages, and so will cause an oops, and removing the__GFP_ZEROwill mean that highmem pages will not be zeroed, meaning that such page tables will not have been initialized.
Yep, I had a feeling that it was not going to work correctly for the HIGHMEM case. Alas, I'm not using HIGHMEM so I did not see the issue in my testing. I'll respin it to fix HIGHMEM breakage.
Thanks!
--
Regards,
George
PATCH 3/7 ARM: ARM11 MPCore: {clean, flush}_pmd_entry are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to {clean,flush}_pmd_entry on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around PMD modifications and subsequent {clean,flush}_pmd_entry calls.
--- arch/arm/include/asm/pgalloc.h | 4 ++++ arch/arm/include/asm/pgtable.h | 9 +++++++++ arch/arm/include/asm/smp_plat.h | 2 ++ arch/arm/mm/idmap.c | 5 +++++ arch/arm/mm/ioremap.c | 9 +++++++++ arch/arm/mm/mmu.c | 12 ++++++++++++ 6 files changed, 41 insertions(+), 0 deletions(-)
arch/arm/include/asm/pgalloc.h +4 -0
@@ -121,9 +121,13 @@ static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, unsigned long prot) {unsigned long pmdval = (pte +PTE_HWTABLE_OFF ) | prot; +if (cache_ops_need_broadcast ()) +preempt_disable (); pmdp[0 ] =__pmd (pmdval); pmdp[1 ] =__pmd (pmdval +256 *sizeof (pte_t));flush_pmd_entry (pmdp); +if (cache_ops_need_broadcast ()) +preempt_enable (); }/*
arch/arm/include/asm/pgtable.h +9 -0
@@ -13,6 +13,7 @@#include <linux/const.h> #include <asm-generic/4level-fixup.h> #include <asm/proc-fns.h> +#include <asm/smp_plat.h> #ifndef CONFIG_MMU
@@ -313,16 +314,24 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];#define copy_pmd (pmdpd,pmdps) \ do { \ + if (cache_ops_need_broadcast()) \ + preempt_disable(); \ pmdpd[0] = pmdps[0]; \ pmdpd[1] = pmdps[1]; \ flush_pmd_entry(pmdpd); \ + if (cache_ops_need_broadcast()) \ + preempt_enable(); \ } while (0)#define pmd_clear (pmdp) \ do { \ + if (cache_ops_need_broadcast()) \ + preempt_disable(); \ pmdp[0] = __pmd(0); \ pmdp[1] = __pmd(0); \ clean_pmd_entry(pmdp); \ + if (cache_ops_need_broadcast()) \ + preempt_enable(); \ } while (0)static inline pte_t *pmd_page_vaddr (pmd_t pmd)
arch/arm/include/asm/smp_plat.h +2 -0
@@ -5,6 +5,7 @@#ifndef __ASMARM_SMP_PLAT_H#define __ASMARM_SMP_PLAT_H +#ifndef __ASSEMBLY__#include <asm/cputype.h> /*
@@ -42,5 +43,6 @@ static inline int cache_ops_need_broadcast(void)return ((read_cpuid_ext (CPUID_EXT_MMFR3 ) >>12 ) &0xf ) <1 ; }#endif +#endif #endif
arch/arm/mm/idmap.c +5 -0
@@ -3,17 +3,22 @@#include <asm/cputype.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> +#include <asm/smp_plat.h> static void idmap_add_pmd (pud_t *pud,unsigned long addr,unsigned long end,unsigned long prot) {pmd_t *pmd =pmd_offset (pud, addr); +if (cache_ops_need_broadcast ()) +preempt_disable (); addr = (addr &PMD_MASK ) | prot; pmd[0 ] =__pmd (addr); addr +=SECTION_SIZE ; pmd[1 ] =__pmd (addr);flush_pmd_entry (pmd); +if (cache_ops_need_broadcast ()) +preempt_enable (); } static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
arch/arm/mm/ioremap.c +9 -0
@@ -32,6 +32,7 @@#include <asm/pgalloc.h> #include <asm/tlbflush.h> #include <asm/sizes.h> +#include <asm/smp_plat.h> #include <asm/mach/map.h> #include "mm.h"
@@ -135,11 +136,15 @@ remap_area_sections(unsigned long virt, unsigned long pfn, do { pmd_t *pmd = pmd_offset(pgd, addr); + if (cache_ops_need_broadcast()) + preempt_disable(); pmd[0] = __pmd(__pfn_to_phys(pfn) | type->prot_sect); pfn += SZ_1M >> PAGE_SHIFT; pmd[1] = __pmd(__pfn_to_phys(pfn) | type->prot_sect); pfn += SZ_1M >> PAGE_SHIFT; flush_pmd_entry(pmd); + if (cache_ops_need_broadcast()) + preempt_enable(); addr += PGDIR_SIZE; pgd++;
@@ -172,9 +177,13 @@ remap_area_supersections(unsigned long virt, unsigned long pfn,for (i =0 ; i <8 ; i++) {pmd_t *pmd =pmd_offset (pgd, addr); +if (cache_ops_need_broadcast ()) +preempt_disable (); pmd[0 ] =__pmd (super_pmd_val); pmd[1 ] =__pmd (super_pmd_val);flush_pmd_entry (pmd); +if (cache_ops_need_broadcast ()) +preempt_enable (); addr +=PGDIR_SIZE ; pgd++;
arch/arm/mm/mmu.c +12 -0
@@ -567,12 +567,24 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,if (addr &SECTION_SIZE ) pmd++; +if (cache_ops_need_broadcast ()) +preempt_disable ();do { *pmd =__pmd (phys | type->prot_sect); phys +=SECTION_SIZE ; }while (pmd++, addr +=SECTION_SIZE , addr != end); +/* FIXME: Multiple PMD entries may be written above +* but only one cache line, up to 8 PMDs depending +* on the alignment of this mapping, is flushed below. +* IFF this mapping spans >8MiB, then only the first +* 8MiB worth of entries will be flushed. Entries +* above the 8MiB limit will not be flushed if I +* read this correctly. +*/ flush_pmd_entry (p); +if (cache_ops_need_broadcast ()) +preempt_enable (); }else {/* * No need to loop; pte's aren't interested in the
Signature · 1 line
1.7.4.4
PATCH 3/7 ARM: ARM11 MPCore: {clean,flush}_pmd_entry are not preempt safe
Quoted 16 lines · gdavis at mvista.com
arch/arm/mm/mmu.c +3
-0@@ -567,12 +567,24 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,if (addr &SECTION_SIZE ) pmd++; +if (cache_ops_need_broadcast ()) +preempt_disable ();do { *pmd =__pmd (phys | type->prot_sect); phys +=SECTION_SIZE ; }while (pmd++, addr +=SECTION_SIZE , addr != end); +/* FIXME: Multiple PMD entries may be written above
arch/arm/mm/mmu.c static void __init alloc_init_section(pud_t *pud, unsigned long addr, · line 587
Quoted 10 lines
arch/arm/mm/mmu.c +9
-0+* but only one cache line, up to 8 PMDs depending +* on the alignment of this mapping, is flushed below. +* IFF this mapping spans >8MiB, then only the first +* 8MiB worth of entries will be flushed. Entries +* above the 8MiB limit will not be flushed if I +* read this correctly. +*/ flush_pmd_entry (p); +if (cache_ops_need_broadcast ()) +preempt_enable ();
My reading of the create_mapping() code is that alloc_init_pud() and alloc_init_section() are called with a 2MB range only (that's 2 entries) given by pgd_addr_end().
Signature · 1 line
Catalin
PATCH 3/7 ARM: ARM11 MPCore: {clean, flush}_pmd_entry are not preempt safe
Quoted 16 lines · Catalin Marinas
Quoted 15 lines · gdavis at mvista.com
arch/arm/mm/mmu.c +2
-0@@ -567,12 +567,24 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,if (addr &SECTION_SIZE ) pmd++; +if (cache_ops_need_broadcast ()) +preempt_disable ();do { *pmd =__pmd (phys | type->prot_sect); phys +=SECTION_SIZE ; }while (pmd++, addr +=SECTION_SIZE , addr != end);
Quoted 15 lines
arch/arm/mm/mmu.c static void __init alloc_init_section(pud_t *pud, unsigned long addr, · line 587
Quoted 11 lines
arch/arm/mm/mmu.c +10
-0+/* FIXME: Multiple PMD entries may be written above +* but only one cache line, up to 8 PMDs depending +* on the alignment of this mapping, is flushed below. +* IFF this mapping spans >8MiB, then only the first +* 8MiB worth of entries will be flushed. Entries +* above the 8MiB limit will not be flushed if I +* read this correctly. +*/ flush_pmd_entry (p); +if (cache_ops_need_broadcast ()) +preempt_enable ();My reading of the
create_mapping()code is thatalloc_init_pud()andalloc_init_section()are called with a 2MB range only (that's 2 entries) given bypgd_addr_end().
You're correct of course. I initially stuck that FIXME note in there more as a reminder to review it more carefully. Alas, in my haste, I submitted as-is w/o checking parameter bounds passed from callers which do as you say, so it's never more than 2MiB in size. Thanks for the feedback. I'll remove that note.
--
Regards,
George
Quoted 3 lines
Signature · 2 lines
-- Catalin
PATCH 3/7 ARM: ARM11 MPCore: {clean,flush}_pmd_entry are not preempt safe
Quoted 35 lines · George G. Davis
Quoted 16 lines · Catalin Marinas
Quoted 15 lines · gdavis at mvista.com
arch/arm/mm/mmu.c +2
-0@@ -567,12 +567,24 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,if (addr &SECTION_SIZE ) pmd++; +if (cache_ops_need_broadcast ()) +preempt_disable ();do { *pmd =__pmd (phys | type->prot_sect); phys +=SECTION_SIZE ; }while (pmd++, addr +=SECTION_SIZE , addr != end);Quoted 15 lines
arch/arm/mm/mmu.c static void __init alloc_init_section(pud_t *pud, unsigned long addr, · line 587
Quoted 11 lines
arch/arm/mm/mmu.c +10
-0+/* FIXME: Multiple PMD entries may be written above +* but only one cache line, up to 8 PMDs depending +* on the alignment of this mapping, is flushed below. +* IFF this mapping spans >8MiB, then only the first +* 8MiB worth of entries will be flushed. Entries +* above the 8MiB limit will not be flushed if I +* read this correctly. +*/ flush_pmd_entry (p); +if (cache_ops_need_broadcast ()) +preempt_enable ();My reading of the
create_mapping()code is thatalloc_init_pud()andalloc_init_section()are called with a 2MB range only (that's 2 entries) given bypgd_addr_end().
Quoted 5 lines
You're correct of course. I initially stuck that FIXME note in there more as a reminder to review it more carefully. Alas, in my haste, I submitted as-is w/o checking parameter bounds passed from callers which do as you say, so it's never more than 2MiB in size. Thanks for the feedback. I'll remove that note.
Note also that the early bringup of MMU mappings (alloc_init_section etc) are running before SMP bringup, so they're already bound to a single CPU. So these shouldn't need 'fixing'.
PATCH 3/7 ARM: ARM11 MPCore: {clean, flush}_pmd_entry are not preempt safe
Quoted 42 lines · Russell King - ARM Linux
Quoted 35 lines · George G. Davis
Quoted 16 lines · Catalin Marinas
Quoted 15 lines · gdavis at mvista.com
arch/arm/mm/mmu.c +2
-0@@ -567,12 +567,24 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,if (addr &SECTION_SIZE ) pmd++; +if (cache_ops_need_broadcast ()) +preempt_disable ();do { *pmd =__pmd (phys | type->prot_sect); phys +=SECTION_SIZE ; }while (pmd++, addr +=SECTION_SIZE , addr != end);Quoted 15 lines
arch/arm/mm/mmu.c static void __init alloc_init_section(pud_t *pud, unsigned long addr, · line 587
Quoted 11 lines
arch/arm/mm/mmu.c +10
-0+/* FIXME: Multiple PMD entries may be written above +* but only one cache line, up to 8 PMDs depending +* on the alignment of this mapping, is flushed below. +* IFF this mapping spans >8MiB, then only the first +* 8MiB worth of entries will be flushed. Entries +* above the 8MiB limit will not be flushed if I +* read this correctly. +*/ flush_pmd_entry (p); +if (cache_ops_need_broadcast ()) +preempt_enable ();My reading of the
create_mapping()code is thatalloc_init_pud()andalloc_init_section()are called with a 2MB range only (that's 2 entries) given bypgd_addr_end().Quoted 5 lines
You're correct of course. I initially stuck that FIXME note in there more as a reminder to review it more carefully. Alas, in my haste, I submitted as-is w/o checking parameter bounds passed from callers which do as you say, so it's never more than 2MiB in size. Thanks for the feedback. I'll remove that note.
Quoted 3 lines
Note also that the early bringup of MMU mappings (
alloc_init_sectionetc) are running before SMP bringup, so they're already bound to a single CPU. So these shouldn't need 'fixing'.
Yep, I knew that and just went ahead and used a shot gun anyway. I'll drop cases which are already safe.
Thanks!
--
Regards,
George
PATCH 4/7 ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to clean_dcache_area on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around memory modifications and subsequent clean_dcache_area calls.
--- arch/arm/plat-omap/iommu.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
arch/arm/plat-omap/iommu.c +10 -0
@@ -1014,8 +1014,12 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) err = -ENOMEM ; goto err_pgd; } +if (cache_ops_need_broadcast ()) +preempt_disable ();memset (p,0 ,IOPGD_TABLE_SIZE );clean_dcache_area (p,IOPGD_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +preempt_enable (); obj->iopgd = p;BUG_ON (!IS_ALIGNED ((unsigned long )obj->iopgd,IOPGD_TABLE_SIZE ));
@@ -1069,7 +1073,13 @@ static struct platform_driver omap_iommu_driver = {static void iopte_cachep_ctor (void *iopte) { +if (cache_ops_need_broadcast ()) +preempt_disable (); +/* FIXME: This will not work on ARM11 MPCore. +*/ clean_dcache_area (iopte,IOPTE_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +preempt_enable (); }static int __init omap_iommu_init (void )
Signature · 1 line
1.7.4.4
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
The get_thread_info assembler macro is currently defined in arch/arm/kernel/entry-header.S and used in the following files:
arch/arm/kernel/entry-armv.S
arch/arm/kernel/entry-common.S
arch/arm/vfp/vfphw.S
arch/arm/vfp/entry.S
The first two cases above use #include "entry-header.S" while the later two cases use #include "../kernel/entry-header.S" to obtain the get_thread_info assembler macro definition.
Move the get_thread_info assembler macro definition into arch/arm/include/asm/assember.h and clean up inclusion of this macro definition to use #include <asm/assember.h>.
This change facilitates use of the get_thread_info assembler macro in other assembler functions without more of the same path relative include directives.
--- arch/arm/include/asm/assembler.h | 13 +++++++++++++ arch/arm/kernel/entry-header.S | 11 ----------- arch/arm/vfp/entry.S | 5 ++++- arch/arm/vfp/vfphw.S | 5 ++++- 4 files changed, 21 insertions(+), 13 deletions(-)
arch/arm/include/asm/assembler.h +13 -0
@@ -23,6 +23,19 @@#include <asm/ptrace.h> #include <asm/domain.h> +#ifndef CONFIG_THUMB2_KERNEL + .macro get_thread_info, rd + mov \rd, sp, lsr #13 + mov \rd, \rd, lsl #13 + .endm +#else /* CONFIG_THUMB2_KERNEL */ + .macro get_thread_info, rd + mov \rd, sp + lsr \rd, \rd, #13 + mov \rd, \rd, lsl #13 + .endm +#endif /* !CONFIG_THUMB2_KERNEL */ +/* * Endian independent macros for shifting bytes within registers. */
arch/arm/kernel/entry-header.S +0 -11
@@ -108,11 +108,6 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm - .macro get_thread_info, rd - mov \rd, sp, lsr #13 - mov \rd, \rd, lsl #13 - .endm - @ @ 32-bit wide "mov pc, reg" @
@@ -148,12 +143,6 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm - .macro get_thread_info, rd - mov \rd, sp - lsr \rd, \rd, #13 - mov \rd, \rd, lsl #13 - .endm - @ @ 32-bit wide "mov pc, reg" @
arch/arm/vfp/entry.S +4 -1
@@ -15,9 +15,12 @@ * r10 = thread_info structure * lr = failure return */ +#include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" +#include <asm/assembler.h> +#include <asm/asm-offsets.h> ENTRY(do_vfp) #ifdef CONFIG_PREEMPT
arch/arm/vfp/vfphw.S +4 -1
@@ -14,9 +14,12 @@ * r10 points at the start of the private FP workspace in the thread structure * sp points to a struct pt_regs (as defined in include/asm/proc/ptrace.h) */ +#include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" +#include <asm/assembler.h> +#include <asm/asm-offsets.h> .macro DBGSTR, str #ifdef DEBUG
Signature · 1 line
1.7.4.4
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
Quoted 10 lines · gdavis at mvista.com
arch/arm/include/asm/assembler.h +2
-0@@ -23,6 +23,19 @@#include <asm/ptrace.h> #include <asm/domain.h> +#ifndef CONFIG_THUMB2_KERNEL + .macro get_thread_info, rd
arch/arm/include/asm/assembler.h line 37
Quoted 10 lines
arch/arm/include/asm/assembler.h +10
-0+ mov \rd, sp, lsr #13 + mov \rd, \rd, lsl #13 + .endm +#else /* CONFIG_THUMB2_KERNEL */ + .macro get_thread_info, rd + mov \rd, sp + lsr \rd, \rd, #13 + mov \rd, \rd, lsl #13 + .endm +#endif /* !CONFIG_THUMB2_KERNEL */
We could even write some preempt_enable and preempt_disable asm macros, I think it would simplify the code in the other files.
Signature · 1 line
Catalin
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
Quoted 9 lines · Catalin Marinas
Quoted 8 lines · gdavis at mvista.com
arch/arm/include/asm/assembler.h +0
-0@@ -23,6 +23,19 @@#include <asm/ptrace.h> #include <asm/domain.h>
Quoted 15 lines
arch/arm/include/asm/assembler.h line 37
Quoted 12 lines
arch/arm/include/asm/assembler.h +12
-0+#ifndef CONFIG_THUMB2_KERNEL + .macro get_thread_info, rd + mov \rd, sp, lsr #13 + mov \rd, \rd, lsl #13 + .endm +#else /* CONFIG_THUMB2_KERNEL */ + .macro get_thread_info, rd + mov \rd, sp + lsr \rd, \rd, #13 + mov \rd, \rd, lsl #13 + .endm +#endif /* !CONFIG_THUMB2_KERNEL */We could even write some
preempt_enableandpreempt_disableasm macros, I think it would simplify the code in the other files.
Like so (based on above patch and not even compile tested):
arch/arm/include/asm/assembler.h +14 -0
@@ -36,6 +36,20 @@ .endm#endif /* !CONFIG_THUMB2_KERNEL */ + .macro preempt_disable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + add \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm + + .macro preempt_enable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + sub \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm +/* * Endian independent macros for shifting bytes within registers. */
Not as efficient as it could be but I imagine the macros could be written to support optional load of \tsk and/or optional \tmp parameters to cover other common cases.
--
Regards,
George
Quoted 3 lines
Signature · 2 lines
-- Catalin
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
Quoted 27 lines · gdavis at mvista.com
arch/arm/include/asm/assembler.h +14
-0@@ -36,6 +36,20 @@ .endm#endif /* !CONFIG_THUMB2_KERNEL */ + .macro preempt_disable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + add \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm + + .macro preempt_enable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + sub \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm +/* * Endian independent macros for shifting bytes within registers. */
Quoted 3 lines
Not as efficient as it could be but I imagine the macros could be written to support optional load of \tsk and/or optional \tmp parameters to cover other common cases.
It's actually not that simple either: if you disable preemption, then you need to check for a preempt event after re-enabling preemption. The C level code does this:
#define preempt_enable_no_resched() \
do { \
barrier(); \
dec_preempt_count(); \
} while (0)#define preempt_check_resched() \
do { \
if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \
preempt_schedule(); \
} while (0)#define preempt_enable() \
do { \
preempt_enable_no_resched(); \
barrier(); \
preempt_check_resched(); \
} while (0)Note that preempt_schedule() will check the preempt count itself and return immediately if non-zero or irqs are disabled.
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
Quoted 28 lines · Russell King - ARM Linux
Quoted 27 lines · gdavis at mvista.com
arch/arm/include/asm/assembler.h +14
-0@@ -36,6 +36,20 @@ .endm#endif /* !CONFIG_THUMB2_KERNEL */ + .macro preempt_disable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + add \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm + + .macro preempt_enable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + sub \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm +/* * Endian independent macros for shifting bytes within registers. */
Quoted 6 lines
Quoted 3 lines
Not as efficient as it could be but I imagine the macros could be written to support optional load of \tsk and/or optional \tmp parameters to cover other common cases.
It's actually not that simple either: if you disable preemption, then you need to check for a preempt event after re-enabling preemption.
That's not easily possible in assembly as calling a function would corrupt some registers. Is there any disadvantage with just doing the equivalent of preempt_enable_no_resched() for a few asm cases where this is needed?
Signature · 1 line
Catalin
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
Quoted 36 lines · Catalin Marinas
Quoted 28 lines · Russell King - ARM Linux
Quoted 27 lines · gdavis at mvista.com
arch/arm/include/asm/assembler.h +14
-0@@ -36,6 +36,20 @@ .endm#endif /* !CONFIG_THUMB2_KERNEL */ + .macro preempt_disable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + add \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm + + .macro preempt_enable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + sub \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm +/* * Endian independent macros for shifting bytes within registers. */ Quoted 6 lines
Quoted 3 lines
Not as efficient as it could be but I imagine the macros could be written to support optional load of \tsk and/or optional \tmp parameters to cover other common cases.
It's actually not that simple either: if you disable preemption, then you need to check for a preempt event after re-enabling preemption.
Quoted 4 lines
That's not easily possible in assembly as calling a function would corrupt some registers. Is there any disadvantage with just doing the equivalent of
preempt_enable_no_resched()for a few asm cases where this is needed?
It means you drop preemption reschedule events, which means that you can no longer do hard-RT in any shape or form (because you're dropping the reschedule for an indefinite amount of time.)
preempt_enable_no_resched() is fine if you're going to also put an explicit check a bit later in the code path when it is convenient to do so.
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
Quoted 37 lines · Russell King - ARM Linux
Quoted 36 lines · Catalin Marinas
Quoted 34 lines · Russell King - ARM Linux
Quoted 27 lines · gdavis at mvista.com
arch/arm/include/asm/assembler.h +14
-0@@ -36,6 +36,20 @@ .endm#endif /* !CONFIG_THUMB2_KERNEL */ + .macro preempt_disable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + add \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm + + .macro preempt_enable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + sub \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm +/* * Endian independent macros for shifting bytes within registers. */ Quoted 3 lines
Not as efficient as it could be but I imagine the macros could be written to support optional load of \tsk and/or optional \tmp parameters to cover other common cases.
It's actually not that simple either: if you disable preemption, then you need to check for a preempt event after re-enabling preemption.
Quoted 8 lines
Quoted 4 lines
That's not easily possible in assembly as calling a function would corrupt some registers. Is there any disadvantage with just doing the equivalent of
preempt_enable_no_resched()for a few asm cases where this is needed?It means you drop preemption reschedule events, which means that you can no longer do hard-RT in any shape or form (because you're dropping the reschedule for an indefinite amount of time.)
I ignored that due to laziness in this series. I figured it was a small window and another interrupt will come along soon enough but yes, it does impact preempt latency.
Quoted 3 lines
preempt_enable_no_resched()is fine if you're going to also put an explicit check a bit later in the code path when it is convenient to do so.
I implemented it for all cases here and will submit an update.
Thanks!
--
Regards,
George
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
In reply to Catalin MarinasQuoted 36 lines · Catalin Marinas
Quoted 28 lines · Russell King - ARM Linux
Quoted 27 lines · gdavis at mvista.com
arch/arm/include/asm/assembler.h +14
-0@@ -36,6 +36,20 @@ .endm#endif /* !CONFIG_THUMB2_KERNEL */ + .macro preempt_disable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + add \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm + + .macro preempt_enable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + sub \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm +/* * Endian independent macros for shifting bytes within registers. */ Quoted 6 lines
Quoted 3 lines
Not as efficient as it could be but I imagine the macros could be written to support optional load of \tsk and/or optional \tmp parameters to cover other common cases.
It's actually not that simple either: if you disable preemption, then you need to check for a preempt event after re-enabling preemption.
Quoted 2 lines
That's not easily possible in assembly as calling a function would corrupt some registers.
Since the preempt_enable is done at the end of all current v6 funcs in this series, it was easy to implement. I'll include those changes in an update.
Thanks!
--
Regards,
George
Quoted 6 lines
Is there any disadvantage with just doing the
equivalent ofpreempt_enable_no_resched()for a few asm cases where this is needed?Signature · 1 line
Catalin
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
In reply to Russell King - ARM LinuxQuoted 49 lines · Russell King - ARM Linux
Quoted 27 lines · gdavis at mvista.com
arch/arm/include/asm/assembler.h +14
-0@@ -36,6 +36,20 @@ .endm#endif /* !CONFIG_THUMB2_KERNEL */ + .macro preempt_disable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + add \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm + + .macro preempt_enable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + sub \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm +/* * Endian independent macros for shifting bytes within registers. */ Quoted 3 lines
Not as efficient as it could be but I imagine the macros could be written to support optional load of \tsk and/or optional \tmp parameters to cover other common cases.
It's actually not that simple either: if you disable preemption, then you need to check for a preempt event after re-enabling preemption. The C level code does this:
#define
preempt_enable_no_resched()\do { \ barrier(); \ dec_preempt_count(); \ } while (0)#define
preempt_check_resched()\do { \ if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ preempt_schedule(); \ } while (0)#define
preempt_enable()\
Quoted 8 lines
do { \ preempt_enable_no_resched(); \ barrier(); \ preempt_check_resched(); \ } while (0)Note that
preempt_schedule()will check the preempt count itself and return immediately if non-zero or irqs are disabled.
It would be easier to just insert preempt_disable/preempt_enable in the cache and proc function call macros. I did that in an earlier test patch but moved it into the cache-v6.S and proc-v6.S files instead since it only affects ARM11 MPCore as far as I'm aware.
--
Regards,
George
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
Quoted 58 lines · George G. Davis
Quoted 55 lines · Russell King - ARM Linux
Quoted 27 lines · gdavis at mvista.com
arch/arm/include/asm/assembler.h +14
-0@@ -36,6 +36,20 @@ .endm#endif /* !CONFIG_THUMB2_KERNEL */ + .macro preempt_disable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + add \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm + + .macro preempt_enable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + sub \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm +/* * Endian independent macros for shifting bytes within registers. */ Quoted 3 lines
Not as efficient as it could be but I imagine the macros could be written to support optional load of \tsk and/or optional \tmp parameters to cover other common cases.
It's actually not that simple either: if you disable preemption, then you need to check for a preempt event after re-enabling preemption. The C level code does this:
#define
preempt_enable_no_resched()\do { \ barrier(); \ dec_preempt_count(); \ } while (0)#define
preempt_check_resched()\do { \ if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ preempt_schedule(); \ } while (0)#define
preempt_enable()\do { \ preempt_enable_no_resched(); \ barrier(); \ preempt_check_resched(); \ } while (0)
Quoted 7 lines
Quoted 2 lines
Note that
preempt_schedule()will check the preempt count itself and return immediately if non-zero or irqs are disabled.It would be easier to just insert preempt_disable/preempt_enable in the cache and proc function call macros. I did that in an earlier test patch but moved it into the
cache-v6.Sandproc-v6.Sfiles instead since it only affects ARM11 MPCore as far as I'm aware.
Alternately, why don't you simply disable IRQs locally? No preemption can happen until enabled again, just like preempt_disable() would do, and the IRQ off period should be short enough not to visibly affect IRQ latency. And that is easily done in assembly too, without preventing rescheduling if needed afterwards.
Nicolas
PATCH 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
Quoted 68 lines · Nicolas Pitre
Quoted 58 lines · George G. Davis
Quoted 55 lines · Russell King - ARM Linux
Quoted 27 lines · gdavis at mvista.com
arch/arm/include/asm/assembler.h +14
-0@@ -36,6 +36,20 @@ .endm#endif /* !CONFIG_THUMB2_KERNEL */ + .macro preempt_disable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + add \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm + + .macro preempt_enable, tsk, cnt + get_thread_info \tsk + ldr \cnt, [\tsk, #TI_PREEMPT] + sub \cnt, \cnt, #1 + str \cnt, [\tsk, #TI_PREEMPT] + .endm +/* * Endian independent macros for shifting bytes within registers. */ Quoted 3 lines
Not as efficient as it could be but I imagine the macros could be written to support optional load of \tsk and/or optional \tmp parameters to cover other common cases.
It's actually not that simple either: if you disable preemption, then you need to check for a preempt event after re-enabling preemption. The C level code does this:
#define
preempt_enable_no_resched()\do { \ barrier(); \ dec_preempt_count(); \ } while (0)#define
preempt_check_resched()\do { \ if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ preempt_schedule(); \ } while (0)#define
preempt_enable()\do { \ preempt_enable_no_resched(); \ barrier(); \ preempt_check_resched(); \ } while (0)Quoted 7 lines
Quoted 2 lines
Note that
preempt_schedule()will check the preempt count itself and return immediately if non-zero or irqs are disabled.It would be easier to just insert preempt_disable/preempt_enable in the cache and proc function call macros. I did that in an earlier test patch but moved it into the
cache-v6.Sandproc-v6.Sfiles instead since it only affects ARM11 MPCore as far as I'm aware.
Quoted 5 lines
Alternately, why don't you simply disable IRQs locally? No preemption can happen until enabled again, just like
preempt_disable()would do, and the IRQ off period should be short enough not to visibly affect IRQ latency. And that is easily done in assembly too, without preventing rescheduling if needed afterwards.
I didn't want to impact interrupt latency but yes, that would work as well and is easier to implement. : )
--
Regards,
George
Quoted 3 lines
Nicolas
PATCH 6/7 ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
The DMA_CACHE_RWFO operations are not preempt safe. If preemption occurs immediately following a RWFO operation on a cache line of CPU A, it is possible for task migration to occur on resume at which point the subsequent cache maintenance operation on the same cache line of CPU B will have no affect on the CPU A cache line leading to inconsistent memory and/or cache state. To prevent this, disable preemption during RWFO operations.
This change depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
--- arch/arm/mm/cache-v6.S | 27 +++++++++++++++++++++++++++ 1 files changed, 27 insertions(+), 0 deletions(-)
arch/arm/mm/cache-v6.S +27 -0
@@ -205,6 +205,12 @@ ENTRY(v6_flush_kern_dcache_area) */ v6_dma_inv_range: #ifdef CONFIG_DMA_CACHE_RWFO +#ifdef CONFIG_PREEMPT + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership #endif
@@ -241,6 +247,9 @@ v6_dma_inv_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count +#endif mov pc, lr /*
@@ -249,6 +258,12 @@ v6_dma_inv_range: * - end - virtual end address of region */ v6_dma_clean_range: +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: #ifdef CONFIG_DMA_CACHE_RWFO
@@ -264,6 +279,9 @@ v6_dma_clean_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count +#endif mov pc, lr /*
@@ -273,6 +291,12 @@ v6_dma_clean_range: */ ENTRY(v6_dma_flush_range) #ifdef CONFIG_DMA_CACHE_RWFO +#ifdef CONFIG_PREEMPT + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership #endif
@@ -292,6 +316,9 @@ ENTRY(v6_dma_flush_range) blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count +#endif mov pc, lr /*
Signature · 1 line
1.7.4.4
PATCH 7/7 ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to cpu_v6_set_pte_ext on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in cpu_v6_set_pte_ext.
This change depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
--- arch/arm/mm/proc-v6.S | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-)
arch/arm/mm/proc-v6.S +11 -0
@@ -122,7 +122,18 @@ ENTRY(cpu_v6_switch_mm) ENTRY(cpu_v6_set_pte_ext) #ifdef CONFIG_MMU +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + stmdb sp!, {r4, r5} + get_thread_info r5 + ldr r4, [r5, #TI_PREEMPT] @ get preempt count + add r3, r4, #1 @ increment it + str r3, [r5, #TI_PREEMPT] @ disable preempt +#endif armv6_set_pte_ext cpu_v6 +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + str r4, [r5, #TI_PREEMPT] @ restore preempt count + ldmia sp!, {r4, r5} +#endif #endif mov pc, lr
Signature · 1 line
1.7.4.4
v3 cover ARM11 MPCore: preemption/task migration cache coherency fixups
In reply to gdavis at mvista.comGreetings,
On ARM11 MPCore, the "SCU does not handle coherency consequences of CP15 cache operations" [1]. ?So cache maintenance functions have to insure that memory is globally consistent. ?Although the current Linux kernel works reasonably well on ARM11 MPCore machines, PREEMPT stress testing, e.g. parallel module loading and hackbench, results in crashes which exhibit non-sense oops traces where machine state does not make sense relative to the code executing at the time of the oops.
Review and analysis of the various ARM11 MPCore cache maintenance functions reveal that there are a number critical sections in which ARM11 MPCore caches and/or memory may become inconsistent, i.e. a cache line on CPU A contains a modified entry but preemption and task migration occurs after which the same cache line is cleaned/flushed on CPU B. ?This can obviously lead to inconsistent memory and/or cache state as cache ops on ARM11 MPCore are non-coherent.
The following is a partial series of ARM11 MPCore preemption/task migration fixes to resolve cache coherency problems on these machines:
George G. Davis (6):
ARM: ARM11 MPCore: pte_alloc_one{,_kernel} are not preempt safe
ARM: ARM11 MPCore: {clean,flush}_pmd_entry are not preempt safe
ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
ARM: Move get_thread_info macro definition to <asm/assembler.h>
ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
Konstantin Baidarov (1):
ARM: ARM11 MPCore: pgd_alloc is not preempt safe
arch/arm/include/asm/assembler.h | 13 +++++++++++++ arch/arm/include/asm/pgalloc.h | 28 +++++++++++++++++++++++----- arch/arm/include/asm/pgtable.h | 9 +++++++++ arch/arm/include/asm/smp_plat.h | 2 ++ arch/arm/kernel/entry-header.S | 11 ----------- arch/arm/mm/cache-v6.S | 27 +++++++++++++++++++++++++++ arch/arm/mm/idmap.c | 5 +++++ arch/arm/mm/ioremap.c | 9 +++++++++ arch/arm/mm/mmu.c | 12 ++++++++++++ arch/arm/mm/pgd.c | 7 +++++++ arch/arm/mm/proc-v6.S | 11 +++++++++++ arch/arm/plat-omap/iommu.c | 10 ++++++++++ arch/arm/vfp/entry.S | 5 ++++- arch/arm/vfp/vfphw.S | 5 ++++- 14 files changed, 136 insertions(+), 18 deletions(-)
ChangeLog:
V2:
- Substitute {get,put}_cpu() with preempt_{disable,enable}().
- Fixed preempt {dis,en}able assembler code sequences to not use r11 since it is reserved for frame pointer use. Also optimised these sequences to use r2, r3; ip scratch registers in most cases to eliminate stack push/pop. In one case, cpu_v6_set_pte_ext, there are only two scratch registers available, r3 and ip. However, both of these are used within the armv6_set_pte_ext macro. So for this case, r3 is used as a temporary scratch when disabling preemption and r4 and r5 are pushed/popped as needed for other uses to avoid conflict with scratch register usage in armv6_set_pte_ext.
- Remove incorrect use of ALT_SMP macros in cpu_v6_set_pte_ext, making the preempt {dis,en}able assembler code sequences compile time dependent upon CONFIG_SMP instead. This code is safe on UP machines anyway.
V3:
- Fix HIGHMEM breakage.
The above changes are among the "low hanging fruit" where the workarounds are relatively low impact from a performance and/or implementation effort perspective. ?I'm still working on fixes for the harder problems. ?Also, I believe that Catalin's "ARM: Allow lazy cache flushing on ARM11MPCore" [2][3] is required for ARM11 MPCore machines and would like to see that or similar/alternative solution applied.
Comments/feedback greatly appreciated.
TIA!
--
Regards,
George
[1] http://infocenter.arm.com/help/topic/com.arm.doc.dai0228a/index.html#arm_toc9
[2] http://www.spinics.net/lists/arm-kernel/msg129403.html
[3] http://lists.infradead.org/pipermail/linux-arm-kernel/2010-May/014990.html
PATCH v3 1/7 ARM: ARM11 MPCore: pgd_alloc is not preempt safe
From: Konstantin Baidarov <[email protected]>
If preemption and subsequent task migration occurs during a call to pgd_alloc on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption around initialization and flushing of new PGDs.
--- arch/arm/mm/pgd.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
arch/arm/mm/pgd.c +7 -0
@@ -14,6 +14,7 @@#include <asm/pgalloc.h> #include <asm/page.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #include "mm.h"
@@ -31,6 +32,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)if (!new_pgd) goto no_pgd; +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (new_pgd,0 ,USER_PTRS_PER_PGD *sizeof (pgd_t));/*
@@ -42,6 +46,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)clean_dcache_area (new_pgd,PTRS_PER_PGD *sizeof (pgd_t)); +if (cache_ops_need_broadcast ()) +preempt_enable (); +if (!vectors_high ()) {/* * On ARM, first page must always be allocated since it
Signature · 1 line
1.7.4.4
PATCH v3 2/7 ARM: ARM11 MPCore: pte_alloc_one{, _kernel} are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during a call to pte_alloc_one{,_kernel} on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption around initialization and flushing of new PTEs.
--- arch/arm/include/asm/pgalloc.h | 24 +++++++++++++++++++----- 1 files changed, 19 insertions(+), 5 deletions(-)
arch/arm/include/asm/pgalloc.h +19 -5
@@ -17,6 +17,7 @@#include <asm/processor.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #define check_pgt_cache () do { } while (0)
@@ -35,7 +36,7 @@extern pgd_t *pgd_alloc (struct mm_struct *mm);extern void pgd_free (struct mm_struct *mm,pgd_t *pgd); -#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO) +#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT)static inline void clean_pte_table (pte_t *pte) {
@@ -64,8 +65,14 @@ pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)pte_t *pte; pte = (pte_t *)__get_free_page (PGALLOC_GFP ); -if (pte) +if (pte) { +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (pte,0 ,PAGE_SIZE );clean_pte_table (pte); +if (cache_ops_need_broadcast ()) +preempt_enable (); + }return pte; }
@@ -76,13 +83,20 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)struct page *pte;#ifdef CONFIG_HIGHPTE - pte =alloc_pages (PGALLOC_GFP | __GFP_HIGHMEM,0 ); + pte =alloc_pages (PGALLOC_GFP | __GFP_ZERO | __GFP_HIGHMEM,0 );#else pte =alloc_pages (PGALLOC_GFP ,0 );#endif if (pte) { -if (!PageHighMem (pte)) -clean_pte_table (page_address (pte)); +if (!PageHighMem (pte)) { +void *page =page_address (pte); +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (page,0 ,PAGE_SIZE ); +clean_pte_table (page); +if (cache_ops_need_broadcast ()) +preempt_enable (); + }pgtable_page_ctor (pte); }
Signature · 1 line
1.7.4.4
PATCH v3 3/7 ARM: ARM11 MPCore: {clean, flush}_pmd_entry are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to {clean,flush}_pmd_entry on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around PMD modifications and subsequent {clean,flush}_pmd_entry calls.
--- arch/arm/include/asm/pgalloc.h | 4 ++++ arch/arm/include/asm/pgtable.h | 9 +++++++++ arch/arm/include/asm/smp_plat.h | 2 ++ arch/arm/mm/idmap.c | 5 +++++ arch/arm/mm/ioremap.c | 9 +++++++++ arch/arm/mm/mmu.c | 12 ++++++++++++ 6 files changed, 41 insertions(+), 0 deletions(-)
arch/arm/include/asm/pgalloc.h +4 -0
@@ -122,9 +122,13 @@ static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, unsigned long prot) {unsigned long pmdval = (pte +PTE_HWTABLE_OFF ) | prot; +if (cache_ops_need_broadcast ()) +preempt_disable (); pmdp[0 ] =__pmd (pmdval); pmdp[1 ] =__pmd (pmdval +256 *sizeof (pte_t));flush_pmd_entry (pmdp); +if (cache_ops_need_broadcast ()) +preempt_enable (); }/*
arch/arm/include/asm/pgtable.h +9 -0
@@ -13,6 +13,7 @@#include <linux/const.h> #include <asm-generic/4level-fixup.h> #include <asm/proc-fns.h> +#include <asm/smp_plat.h> #ifndef CONFIG_MMU
@@ -313,16 +314,24 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];#define copy_pmd (pmdpd,pmdps) \ do { \ + if (cache_ops_need_broadcast()) \ + preempt_disable(); \ pmdpd[0] = pmdps[0]; \ pmdpd[1] = pmdps[1]; \ flush_pmd_entry(pmdpd); \ + if (cache_ops_need_broadcast()) \ + preempt_enable(); \ } while (0)#define pmd_clear (pmdp) \ do { \ + if (cache_ops_need_broadcast()) \ + preempt_disable(); \ pmdp[0] = __pmd(0); \ pmdp[1] = __pmd(0); \ clean_pmd_entry(pmdp); \ + if (cache_ops_need_broadcast()) \ + preempt_enable(); \ } while (0)static inline pte_t *pmd_page_vaddr (pmd_t pmd)
arch/arm/include/asm/smp_plat.h +2 -0
@@ -5,6 +5,7 @@#ifndef __ASMARM_SMP_PLAT_H#define __ASMARM_SMP_PLAT_H +#ifndef __ASSEMBLY__#include <asm/cputype.h> /*
@@ -42,5 +43,6 @@ static inline int cache_ops_need_broadcast(void)return ((read_cpuid_ext (CPUID_EXT_MMFR3 ) >>12 ) &0xf ) <1 ; }#endif +#endif #endif
arch/arm/mm/idmap.c +5 -0
@@ -3,17 +3,22 @@#include <asm/cputype.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> +#include <asm/smp_plat.h> static void idmap_add_pmd (pud_t *pud,unsigned long addr,unsigned long end,unsigned long prot) {pmd_t *pmd =pmd_offset (pud, addr); +if (cache_ops_need_broadcast ()) +preempt_disable (); addr = (addr &PMD_MASK ) | prot; pmd[0 ] =__pmd (addr); addr +=SECTION_SIZE ; pmd[1 ] =__pmd (addr);flush_pmd_entry (pmd); +if (cache_ops_need_broadcast ()) +preempt_enable (); } static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
arch/arm/mm/ioremap.c +9 -0
@@ -32,6 +32,7 @@#include <asm/pgalloc.h> #include <asm/tlbflush.h> #include <asm/sizes.h> +#include <asm/smp_plat.h> #include <asm/mach/map.h> #include "mm.h"
@@ -135,11 +136,15 @@ remap_area_sections(unsigned long virt, unsigned long pfn, do { pmd_t *pmd = pmd_offset(pgd, addr); + if (cache_ops_need_broadcast()) + preempt_disable(); pmd[0] = __pmd(__pfn_to_phys(pfn) | type->prot_sect); pfn += SZ_1M >> PAGE_SHIFT; pmd[1] = __pmd(__pfn_to_phys(pfn) | type->prot_sect); pfn += SZ_1M >> PAGE_SHIFT; flush_pmd_entry(pmd); + if (cache_ops_need_broadcast()) + preempt_enable(); addr += PGDIR_SIZE; pgd++;
@@ -172,9 +177,13 @@ remap_area_supersections(unsigned long virt, unsigned long pfn,for (i =0 ; i <8 ; i++) {pmd_t *pmd =pmd_offset (pgd, addr); +if (cache_ops_need_broadcast ()) +preempt_disable (); pmd[0 ] =__pmd (super_pmd_val); pmd[1 ] =__pmd (super_pmd_val);flush_pmd_entry (pmd); +if (cache_ops_need_broadcast ()) +preempt_enable (); addr +=PGDIR_SIZE ; pgd++;
arch/arm/mm/mmu.c +12 -0
@@ -567,12 +567,24 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,if (addr &SECTION_SIZE ) pmd++; +if (cache_ops_need_broadcast ()) +preempt_disable ();do { *pmd =__pmd (phys | type->prot_sect); phys +=SECTION_SIZE ; }while (pmd++, addr +=SECTION_SIZE , addr != end); +/* FIXME: Multiple PMD entries may be written above +* but only one cache line, up to 8 PMDs depending +* on the alignment of this mapping, is flushed below. +* IFF this mapping spans >8MiB, then only the first +* 8MiB worth of entries will be flushed. Entries +* above the 8MiB limit will not be flushed if I +* read this correctly. +*/ flush_pmd_entry (p); +if (cache_ops_need_broadcast ()) +preempt_enable (); }else {/* * No need to loop; pte's aren't interested in the
Signature · 1 line
1.7.4.4
PATCH v3 4/7 ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to clean_dcache_area on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around memory modifications and subsequent clean_dcache_area calls.
--- arch/arm/plat-omap/iommu.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
arch/arm/plat-omap/iommu.c +10 -0
@@ -1014,8 +1014,12 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) err = -ENOMEM ; goto err_pgd; } +if (cache_ops_need_broadcast ()) +preempt_disable ();memset (p,0 ,IOPGD_TABLE_SIZE );clean_dcache_area (p,IOPGD_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +preempt_enable (); obj->iopgd = p;BUG_ON (!IS_ALIGNED ((unsigned long )obj->iopgd,IOPGD_TABLE_SIZE ));
@@ -1069,7 +1073,13 @@ static struct platform_driver omap_iommu_driver = {static void iopte_cachep_ctor (void *iopte) { +if (cache_ops_need_broadcast ()) +preempt_disable (); +/* FIXME: This will not work on ARM11 MPCore. +*/ clean_dcache_area (iopte,IOPTE_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +preempt_enable (); }static int __init omap_iommu_init (void )
Signature · 1 line
1.7.4.4
PATCH v3 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
The get_thread_info assembler macro is currently defined in arch/arm/kernel/entry-header.S and used in the following files:
arch/arm/kernel/entry-armv.S
arch/arm/kernel/entry-common.S
arch/arm/vfp/vfphw.S
arch/arm/vfp/entry.S
The first two cases above use #include "entry-header.S" while the later two cases use #include "../kernel/entry-header.S" to obtain the get_thread_info assembler macro definition.
Move the get_thread_info assembler macro definition into arch/arm/include/asm/assember.h and clean up inclusion of this macro definition to use #include <asm/assember.h>.
This change facilitates use of the get_thread_info assembler macro in other assembler functions without more of the same path relative include directives.
--- arch/arm/include/asm/assembler.h | 13 +++++++++++++ arch/arm/kernel/entry-header.S | 11 ----------- arch/arm/vfp/entry.S | 5 ++++- arch/arm/vfp/vfphw.S | 5 ++++- 4 files changed, 21 insertions(+), 13 deletions(-)
arch/arm/include/asm/assembler.h +13 -0
@@ -23,6 +23,19 @@#include <asm/ptrace.h> #include <asm/domain.h> +#ifndef CONFIG_THUMB2_KERNEL + .macro get_thread_info, rd + mov \rd, sp, lsr #13 + mov \rd, \rd, lsl #13 + .endm +#else /* CONFIG_THUMB2_KERNEL */ + .macro get_thread_info, rd + mov \rd, sp + lsr \rd, \rd, #13 + mov \rd, \rd, lsl #13 + .endm +#endif /* !CONFIG_THUMB2_KERNEL */ +/* * Endian independent macros for shifting bytes within registers. */
arch/arm/kernel/entry-header.S +0 -11
@@ -108,11 +108,6 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm - .macro get_thread_info, rd - mov \rd, sp, lsr #13 - mov \rd, \rd, lsl #13 - .endm - @ @ 32-bit wide "mov pc, reg" @
@@ -148,12 +143,6 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm - .macro get_thread_info, rd - mov \rd, sp - lsr \rd, \rd, #13 - mov \rd, \rd, lsl #13 - .endm - @ @ 32-bit wide "mov pc, reg" @
arch/arm/vfp/entry.S +4 -1
@@ -15,9 +15,12 @@ * r10 = thread_info structure * lr = failure return */ +#include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" +#include <asm/assembler.h> +#include <asm/asm-offsets.h> ENTRY(do_vfp) #ifdef CONFIG_PREEMPT
arch/arm/vfp/vfphw.S +4 -1
@@ -14,9 +14,12 @@ * r10 points at the start of the private FP workspace in the thread structure * sp points to a struct pt_regs (as defined in include/asm/proc/ptrace.h) */ +#include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" +#include <asm/assembler.h> +#include <asm/asm-offsets.h> .macro DBGSTR, str #ifdef DEBUG
Signature · 1 line
1.7.4.4
PATCH v3 6/7 ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
The DMA_CACHE_RWFO operations are not preempt safe. If preemption occurs immediately following a RWFO operation on a cache line of CPU A, it is possible for task migration to occur on resume at which point the subsequent cache maintenance operation on the same cache line of CPU B will have no affect on the CPU A cache line leading to inconsistent memory and/or cache state. To prevent this, disable preemption during RWFO operations.
This change depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
--- arch/arm/mm/cache-v6.S | 27 +++++++++++++++++++++++++++ 1 files changed, 27 insertions(+), 0 deletions(-)
arch/arm/mm/cache-v6.S +27 -0
@@ -205,6 +205,12 @@ ENTRY(v6_flush_kern_dcache_area) */ v6_dma_inv_range: #ifdef CONFIG_DMA_CACHE_RWFO +#ifdef CONFIG_PREEMPT + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership #endif
@@ -241,6 +247,9 @@ v6_dma_inv_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count +#endif mov pc, lr /*
@@ -249,6 +258,12 @@ v6_dma_inv_range: * - end - virtual end address of region */ v6_dma_clean_range: +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: #ifdef CONFIG_DMA_CACHE_RWFO
@@ -264,6 +279,9 @@ v6_dma_clean_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count +#endif mov pc, lr /*
@@ -273,6 +291,12 @@ v6_dma_clean_range: */ ENTRY(v6_dma_flush_range) #ifdef CONFIG_DMA_CACHE_RWFO +#ifdef CONFIG_PREEMPT + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership #endif
@@ -292,6 +316,9 @@ ENTRY(v6_dma_flush_range) blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count +#endif mov pc, lr /*
Signature · 1 line
1.7.4.4
PATCH v3 7/7 ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to cpu_v6_set_pte_ext on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in cpu_v6_set_pte_ext.
This change depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
--- arch/arm/mm/proc-v6.S | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-)
arch/arm/mm/proc-v6.S +11 -0
@@ -122,7 +122,18 @@ ENTRY(cpu_v6_switch_mm) ENTRY(cpu_v6_set_pte_ext) #ifdef CONFIG_MMU +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + stmdb sp!, {r4, r5} + get_thread_info r5 + ldr r4, [r5, #TI_PREEMPT] @ get preempt count + add r3, r4, #1 @ increment it + str r3, [r5, #TI_PREEMPT] @ disable preempt +#endif armv6_set_pte_ext cpu_v6 +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + str r4, [r5, #TI_PREEMPT] @ restore preempt count + ldmia sp!, {r4, r5} +#endif #endif mov pc, lr
Signature · 1 line
1.7.4.4
v4 cover ARM11 MPCore: preemption/task migration cache coherency fixups
In reply to gdavis at mvista.comGreetings,
On ARM11 MPCore, the "SCU does not handle coherency consequences of CP15 cache operations" [1]. ?So cache maintenance functions have to insure that memory is globally consistent. ?Although the current Linux kernel works reasonably well on ARM11 MPCore machines, PREEMPT stress testing, e.g. parallel module loading and hackbench, results in crashes which exhibit non-sense oops traces where machine state does not make sense relative to the code executing at the time of the oops.
Review and analysis of the various ARM11 MPCore cache maintenance functions reveal that there are a number critical sections in which ARM11 MPCore caches and/or memory may become inconsistent, i.e. a cache line on CPU A contains a modified entry but preemption and task migration occurs after which the same cache line is cleaned/flushed on CPU B. ?This can obviously lead to inconsistent memory and/or cache state as cache ops on ARM11 MPCore are non-coherent.
The following is a partial series of ARM11 MPCore preemption/task migration fixes to resolve cache coherency problems on these machines:
George G. Davis (6):
ARM: ARM11 MPCore: pte_alloc_one{,_kernel} are not preempt safe
ARM: ARM11 MPCore: {clean,flush}_pmd_entry are not preempt safe
ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
ARM: Move get_thread_info macro definition to <asm/assembler.h>
ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
Konstantin Baidarov (1):
ARM: ARM11 MPCore: pgd_alloc is not preempt safe
arch/arm/include/asm/assembler.h | 13 ++++++++++++ arch/arm/include/asm/pgalloc.h | 28 ++++++++++++++++++++++---- arch/arm/include/asm/pgtable.h | 9 ++++++++ arch/arm/include/asm/smp_plat.h | 2 + arch/arm/kernel/entry-header.S | 11 ---------- arch/arm/mm/cache-v6.S | 39 ++++++++++++++++++++++++++++++++++++++ arch/arm/mm/idmap.c | 5 ++++ arch/arm/mm/pgd.c | 7 ++++++ arch/arm/mm/proc-v6.S | 15 ++++++++++++++ arch/arm/plat-omap/iommu.c | 10 +++++++++ arch/arm/vfp/entry.S | 5 +++- arch/arm/vfp/vfphw.S | 5 +++- 12 files changed, 131 insertions(+), 18 deletions(-)
ChangeLog:
V2:
- Substitute {get,put}_cpu() with preempt_{disable,enable}().
- Fixed preempt {dis,en}able assembler code sequences to not use r11 since it is reserved for frame pointer use. Also optimised these sequences to use r2, r3; ip scratch registers in most cases to eliminate stack push/pop. In one case, cpu_v6_set_pte_ext, there are only two scratch registers available, r3 and ip. However, both of these are used within the armv6_set_pte_ext macro. So for this case, r3 is used as a temporary scratch when disabling preemption and r4 and r5 are pushed/popped as needed for other uses to avoid conflict with scratch register usage in armv6_set_pte_ext.
- Remove incorrect use of ALT_SMP macros in cpu_v6_set_pte_ext, making the preempt {dis,en}able assembler code sequences compile time dependent upon CONFIG_SMP instead. This code is safe on UP machines anyway.
V3:
- Fix HIGHMEM breakage.
V4:
- Drop preempt_{disable,enable}() around calls to flush_pmd_entry() in remap_area_sections() and remap_area_supersections() in file arch/arm/mm/ioremap.c since these functions are not used on SMP machines (they're enclosed within #ifndef SMP/#endif)
- Drop preempt_{disable,enable}() around call to flush_pmd_entry() in alloc_init_section() in file arch/arm/mm/mmu.c since this function is called during early kernel initialization during which time preemption and task migration is not possible. Also removed bogus FIXME comment as part of this change.
- Added calls to preempt_schedule when re-enabling preemption in various arch/arm/mm/{cache,proc}-v6.S functions which need preemption disabled due task migration issues.
The above changes are among the "low hanging fruit" where the workarounds are relatively low impact from a performance and/or implementation effort perspective. ?I'm still working on fixes for the harder problems. ?Also, I believe that Catalin's "ARM: Allow lazy cache flushing on ARM11MPCore" [2][3] is required for ARM11 MPCore machines and would like to see that or similar/alternative solution applied.
Comments/feedback greatly appreciated.
TIA!
--
Regards,
George
[1] http://infocenter.arm.com/help/topic/com.arm.doc.dai0228a/index.html#arm_toc9
[2] http://www.spinics.net/lists/arm-kernel/msg129403.html
[3] http://lists.infradead.org/pipermail/linux-arm-kernel/2010-May/014990.html
PATCH v4 1/7 ARM: ARM11 MPCore: pgd_alloc is not preempt safe
From: Konstantin Baidarov <[email protected]>
If preemption and subsequent task migration occurs during a call to pgd_alloc on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption around initialization and flushing of new PGDs.
--- arch/arm/mm/pgd.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
arch/arm/mm/pgd.c +7 -0
@@ -14,6 +14,7 @@#include <asm/pgalloc.h> #include <asm/page.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #include "mm.h"
@@ -31,6 +32,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)if (!new_pgd) goto no_pgd; +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (new_pgd,0 ,USER_PTRS_PER_PGD *sizeof (pgd_t));/*
@@ -42,6 +46,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)clean_dcache_area (new_pgd,PTRS_PER_PGD *sizeof (pgd_t)); +if (cache_ops_need_broadcast ()) +preempt_enable (); +if (!vectors_high ()) {/* * On ARM, first page must always be allocated since it
Signature · 1 line
1.7.4.4
PATCH v4 2/7 ARM: ARM11 MPCore: pte_alloc_one{, _kernel} are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during a call to pte_alloc_one{,_kernel} on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption around initialization and flushing of new PTEs.
--- arch/arm/include/asm/pgalloc.h | 24 +++++++++++++++++++----- 1 files changed, 19 insertions(+), 5 deletions(-)
arch/arm/include/asm/pgalloc.h +19 -5
@@ -17,6 +17,7 @@#include <asm/processor.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #define check_pgt_cache () do { } while (0)
@@ -35,7 +36,7 @@extern pgd_t *pgd_alloc (struct mm_struct *mm);extern void pgd_free (struct mm_struct *mm,pgd_t *pgd); -#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO) +#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT)static inline void clean_pte_table (pte_t *pte) {
@@ -64,8 +65,14 @@ pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)pte_t *pte; pte = (pte_t *)__get_free_page (PGALLOC_GFP ); -if (pte) +if (pte) { +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (pte,0 ,PAGE_SIZE );clean_pte_table (pte); +if (cache_ops_need_broadcast ()) +preempt_enable (); + }return pte; }
@@ -76,13 +83,20 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)struct page *pte;#ifdef CONFIG_HIGHPTE - pte =alloc_pages (PGALLOC_GFP | __GFP_HIGHMEM,0 ); + pte =alloc_pages (PGALLOC_GFP | __GFP_ZERO | __GFP_HIGHMEM,0 );#else pte =alloc_pages (PGALLOC_GFP ,0 );#endif if (pte) { -if (!PageHighMem (pte)) -clean_pte_table (page_address (pte)); +if (!PageHighMem (pte)) { +void *page =page_address (pte); +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (page,0 ,PAGE_SIZE ); +clean_pte_table (page); +if (cache_ops_need_broadcast ()) +preempt_enable (); + }pgtable_page_ctor (pte); }
Signature · 1 line
1.7.4.4
PATCH v4 3/7 ARM: ARM11 MPCore: {clean, flush}_pmd_entry are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to {clean,flush}_pmd_entry on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around PMD modifications and subsequent {clean,flush}_pmd_entry calls.
--- arch/arm/include/asm/pgalloc.h | 4 ++++ arch/arm/include/asm/pgtable.h | 9 +++++++++ arch/arm/include/asm/smp_plat.h | 2 ++ arch/arm/mm/idmap.c | 5 +++++ 4 files changed, 20 insertions(+), 0 deletions(-)
arch/arm/include/asm/pgalloc.h +4 -0
@@ -122,9 +122,13 @@ static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, unsigned long prot) {unsigned long pmdval = (pte +PTE_HWTABLE_OFF ) | prot; +if (cache_ops_need_broadcast ()) +preempt_disable (); pmdp[0 ] =__pmd (pmdval); pmdp[1 ] =__pmd (pmdval +256 *sizeof (pte_t));flush_pmd_entry (pmdp); +if (cache_ops_need_broadcast ()) +preempt_enable (); }/*
arch/arm/include/asm/pgtable.h +9 -0
@@ -13,6 +13,7 @@#include <linux/const.h> #include <asm-generic/4level-fixup.h> #include <asm/proc-fns.h> +#include <asm/smp_plat.h> #ifndef CONFIG_MMU
@@ -313,16 +314,24 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];#define copy_pmd (pmdpd,pmdps) \ do { \ + if (cache_ops_need_broadcast()) \ + preempt_disable(); \ pmdpd[0] = pmdps[0]; \ pmdpd[1] = pmdps[1]; \ flush_pmd_entry(pmdpd); \ + if (cache_ops_need_broadcast()) \ + preempt_enable(); \ } while (0)#define pmd_clear (pmdp) \ do { \ + if (cache_ops_need_broadcast()) \ + preempt_disable(); \ pmdp[0] = __pmd(0); \ pmdp[1] = __pmd(0); \ clean_pmd_entry(pmdp); \ + if (cache_ops_need_broadcast()) \ + preempt_enable(); \ } while (0)static inline pte_t *pmd_page_vaddr (pmd_t pmd)
arch/arm/include/asm/smp_plat.h +2 -0
@@ -5,6 +5,7 @@#ifndef __ASMARM_SMP_PLAT_H#define __ASMARM_SMP_PLAT_H +#ifndef __ASSEMBLY__#include <asm/cputype.h> /*
@@ -42,5 +43,6 @@ static inline int cache_ops_need_broadcast(void)return ((read_cpuid_ext (CPUID_EXT_MMFR3 ) >>12 ) &0xf ) <1 ; }#endif +#endif #endif
arch/arm/mm/idmap.c +5 -0
@@ -3,17 +3,22 @@#include <asm/cputype.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> +#include <asm/smp_plat.h> static void idmap_add_pmd (pud_t *pud,unsigned long addr,unsigned long end,unsigned long prot) {pmd_t *pmd =pmd_offset (pud, addr); +if (cache_ops_need_broadcast ()) +preempt_disable (); addr = (addr &PMD_MASK ) | prot; pmd[0 ] =__pmd (addr); addr +=SECTION_SIZE ; pmd[1 ] =__pmd (addr);flush_pmd_entry (pmd); +if (cache_ops_need_broadcast ()) +preempt_enable (); } static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
Signature · 1 line
1.7.4.4
PATCH v4 4/7 ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to clean_dcache_area on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around memory modifications and subsequent clean_dcache_area calls.
--- arch/arm/plat-omap/iommu.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
arch/arm/plat-omap/iommu.c +10 -0
@@ -1014,8 +1014,12 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) err = -ENOMEM ; goto err_pgd; } +if (cache_ops_need_broadcast ()) +preempt_disable ();memset (p,0 ,IOPGD_TABLE_SIZE );clean_dcache_area (p,IOPGD_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +preempt_enable (); obj->iopgd = p;BUG_ON (!IS_ALIGNED ((unsigned long )obj->iopgd,IOPGD_TABLE_SIZE ));
@@ -1069,7 +1073,13 @@ static struct platform_driver omap_iommu_driver = {static void iopte_cachep_ctor (void *iopte) { +if (cache_ops_need_broadcast ()) +preempt_disable (); +/* FIXME: This will not work on ARM11 MPCore. +*/ clean_dcache_area (iopte,IOPTE_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +preempt_enable (); }static int __init omap_iommu_init (void )
Signature · 1 line
1.7.4.4
PATCH v4 4/7 ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
Hi,
Quoted 7 lines · gdavis at mvista.com
From: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to
clean_dcache_areaon ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around memory modifications and subsequentclean_dcache_areacalls.
This one needs to be refreshed against what's queued in for-next as Ohad has moved most of the iommu code into drivers. I've added Ohad to Cc as well.
Regards,
Tony
Quoted 41 lines
Cc Tony Lindgren <[email protected]>Signed-off-by George G. Davis <[email protected]>--- arch/arm/plat-omap/iommu.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)arch/arm/plat-omap/iommu.c +10
-0@@ -1014,8 +1014,12 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) err = -ENOMEM ; goto err_pgd; } +if (cache_ops_need_broadcast ()) +preempt_disable ();memset (p,0 ,IOPGD_TABLE_SIZE );clean_dcache_area (p,IOPGD_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +preempt_enable (); obj->iopgd = p;BUG_ON (!IS_ALIGNED ((unsigned long )obj->iopgd,IOPGD_TABLE_SIZE ));@@ -1069,7 +1073,13 @@ static struct platform_driver omap_iommu_driver = {static void iopte_cachep_ctor (void *iopte) { +if (cache_ops_need_broadcast ()) +preempt_disable (); +/* FIXME: This will not work on ARM11 MPCore. +*/ clean_dcache_area (iopte,IOPTE_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +preempt_enable (); }static int __init omap_iommu_init (void )Signature · 1 line
1.7.4.4
PATCH v4 4/7 ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
Hello Tony/Ohad,
Quoted 11 lines · Tony Lindgren
Hi,
Quoted 7 lines · gdavis at mvista.com
From: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to
clean_dcache_areaon ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around memory modifications and subsequentclean_dcache_areacalls.
Quoted 3 lines
This one needs to be refreshed against what's queued in for-next as Ohad has moved most of the iommu code into drivers. I've added Ohad to Cc as well.
If you do not use ARM11 MPCore with those drivers, then we can drop this altogether.
Thanks!
--
Regards,
George
Quoted 46 lines
Regards,
Tony
Quoted 41 lines
Cc Tony Lindgren <[email protected]>Signed-off-by George G. Davis <[email protected]>---
arch/arm/plat-omap/iommu.c| 10 ++++++++++1 files changed, 10 insertions(+), 0 deletions(-)arch/arm/plat-omap/iommu.c +10
-0@@ -1014,8 +1014,12 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) err = -ENOMEM ; goto err_pgd; } +if (cache_ops_need_broadcast ()) +preempt_disable ();memset (p,0 ,IOPGD_TABLE_SIZE );clean_dcache_area (p,IOPGD_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +preempt_enable (); obj->iopgd = p;BUG_ON (!IS_ALIGNED ((unsigned long )obj->iopgd,IOPGD_TABLE_SIZE ));@@ -1069,7 +1073,13 @@ static struct platform_driver omap_iommu_driver = {static void iopte_cachep_ctor (void *iopte) { +if (cache_ops_need_broadcast ()) +preempt_disable (); +/* FIXME: This will not work on ARM11 MPCore. +*/ clean_dcache_area (iopte,IOPTE_TABLE_SIZE ); +if (cache_ops_need_broadcast ()) +preempt_enable (); }static int __init omap_iommu_init (void )Signature · 1 line
1.7.4.4
PATCH v4 4/7 ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
Quoted 15 lines · George G. Davis
Hello Tony/Ohad,
Quoted 11 lines · Tony Lindgren
Hi,
Quoted 7 lines · gdavis at mvista.com
From: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to
clean_dcache_areaon ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around memory modifications and subsequentclean_dcache_areacalls.
Quoted 5 lines
Quoted 3 lines
This one needs to be refreshed against what's queued in for-next as Ohad has moved most of the iommu code into drivers. I've added Ohad to Cc as well.
If you do not use ARM11 MPCore with those drivers, then we can drop this altogether.
No MPCore omaps available AFAIK, so that sounds OK to me.
Tony
PATCH v4 4/7 ARM: ARM11 MPCore: clean_dcache_area is not preempt safe
Quoted 16 lines · Tony Lindgren
Quoted 15 lines · George G. Davis
Hello Tony/Ohad,
Quoted 11 lines · Tony Lindgren
Hi,
Quoted 7 lines · gdavis at mvista.com
From: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to
clean_dcache_areaon ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around memory modifications and subsequentclean_dcache_areacalls.
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
This one needs to be refreshed against what's queued in for-next as Ohad has moved most of the iommu code into drivers. I've added Ohad to Cc as well.
If you do not use ARM11 MPCore with those drivers, then we can drop this altogether.
No MPCore omaps available AFAIK, so that sounds OK to me.
Good, I'll drop it then.
Thanks!
--
Regards,
George
Quoted 2 lines
Tony
PATCH v4 5/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
The get_thread_info assembler macro is currently defined in arch/arm/kernel/entry-header.S and used in the following files:
arch/arm/kernel/entry-armv.S
arch/arm/kernel/entry-common.S
arch/arm/vfp/vfphw.S
arch/arm/vfp/entry.S
The first two cases above use #include "entry-header.S" while the later two cases use #include "../kernel/entry-header.S" to obtain the get_thread_info assembler macro definition.
Move the get_thread_info assembler macro definition into arch/arm/include/asm/assember.h and clean up inclusion of this macro definition to use #include <asm/assember.h>.
This change facilitates use of the get_thread_info assembler macro in other assembler functions without more of the same path relative include directives.
--- arch/arm/include/asm/assembler.h | 13 +++++++++++++ arch/arm/kernel/entry-header.S | 11 ----------- arch/arm/vfp/entry.S | 5 ++++- arch/arm/vfp/vfphw.S | 5 ++++- 4 files changed, 21 insertions(+), 13 deletions(-)
arch/arm/include/asm/assembler.h +13 -0
@@ -23,6 +23,19 @@#include <asm/ptrace.h> #include <asm/domain.h> +#ifndef CONFIG_THUMB2_KERNEL + .macro get_thread_info, rd + mov \rd, sp, lsr #13 + mov \rd, \rd, lsl #13 + .endm +#else /* CONFIG_THUMB2_KERNEL */ + .macro get_thread_info, rd + mov \rd, sp + lsr \rd, \rd, #13 + mov \rd, \rd, lsl #13 + .endm +#endif /* !CONFIG_THUMB2_KERNEL */ +/* * Endian independent macros for shifting bytes within registers. */
arch/arm/kernel/entry-header.S +0 -11
@@ -108,11 +108,6 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm - .macro get_thread_info, rd - mov \rd, sp, lsr #13 - mov \rd, \rd, lsl #13 - .endm - @ @ 32-bit wide "mov pc, reg" @
@@ -148,12 +143,6 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm - .macro get_thread_info, rd - mov \rd, sp - lsr \rd, \rd, #13 - mov \rd, \rd, lsl #13 - .endm - @ @ 32-bit wide "mov pc, reg" @
arch/arm/vfp/entry.S +4 -1
@@ -15,9 +15,12 @@ * r10 = thread_info structure * lr = failure return */ +#include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" +#include <asm/assembler.h> +#include <asm/asm-offsets.h> ENTRY(do_vfp) #ifdef CONFIG_PREEMPT
arch/arm/vfp/vfphw.S +4 -1
@@ -14,9 +14,12 @@ * r10 points at the start of the private FP workspace in the thread structure * sp points to a struct pt_regs (as defined in include/asm/proc/ptrace.h) */ +#include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" +#include <asm/assembler.h> +#include <asm/asm-offsets.h> .macro DBGSTR, str #ifdef DEBUG
Signature · 1 line
1.7.4.4
PATCH v4 6/7 ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
The DMA_CACHE_RWFO operations are not preempt safe. If preemption occurs immediately following a RWFO operation on a cache line of CPU A, it is possible for task migration to occur on resume at which point the subsequent cache maintenance operation on the same cache line of CPU B will have no affect on the CPU A cache line leading to inconsistent memory and/or cache state. To prevent this, disable preemption during RWFO operations.
This change depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
--- arch/arm/mm/cache-v6.S | 39 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 39 insertions(+), 0 deletions(-)
arch/arm/mm/cache-v6.S +39 -0
@@ -205,6 +205,12 @@ ENTRY(v6_flush_kern_dcache_area) */ v6_dma_inv_range: #ifdef CONFIG_DMA_CACHE_RWFO +#ifdef CONFIG_PREEMPT + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership #endif
@@ -241,6 +247,13 @@ v6_dma_inv_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count + teq r3, #0 @ preempt count == 0? + ldreq r3, [ip, #TI_FLAGS] @ load flags if yes + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ ret via preempt_schedule +#endif mov pc, lr /*
@@ -249,6 +262,12 @@ v6_dma_inv_range: * - end - virtual end address of region */ v6_dma_clean_range: +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: #ifdef CONFIG_DMA_CACHE_RWFO
@@ -264,6 +283,13 @@ v6_dma_clean_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count + teq r3, #0 @ preempt count == 0? + ldreq r3, [ip, #TI_FLAGS] @ load flags if yes + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ ret via preempt_schedule +#endif mov pc, lr /*
@@ -273,6 +299,12 @@ v6_dma_clean_range: */ ENTRY(v6_dma_flush_range) #ifdef CONFIG_DMA_CACHE_RWFO +#ifdef CONFIG_PREEMPT + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership #endif
@@ -292,6 +324,13 @@ ENTRY(v6_dma_flush_range) blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count + teq r3, #0 @ preempt count == 0? + ldreq r3, [ip, #TI_FLAGS] @ load flags if yes + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ ret via preempt_schedule +#endif mov pc, lr /*
Signature · 1 line
1.7.4.4
PATCH v4 6/7 ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
arch/arm/mm/cache-v6.S v6_dma_inv_range: · line 255
Quoted 10 lines · gdavis at mvista.com
arch/arm/mm/cache-v6.S +6
-0@@ -241,6 +247,13 @@ v6_dma_inv_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count + teq r3, #0 @ preempt count == 0? + ldreq r3, [ip, #TI_FLAGS] @ load flags if yes + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ ret via preempt_schedule
This is buggy. If the preempt count is _not_ zero, you end up not loading the TI_FLAGS bits and testing _TIF_NEED_RESCHED against that non-zero preempt count.
Nicolas
PATCH v4 6/7 ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
Hello,
Quoted 16 lines · Nicolas Pitre
Review v6_dma_inv_range: · line 255
Quoted 10 lines · gdavis at mvista.com
(quoted diff) +6
-0@@ -241,6 +247,13 @@ v6_dma_inv_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + str r3, [ip, #TI_PREEMPT] @ restore preempt count + teq r3, #0 @ preempt count == 0? + ldreq r3, [ip, #TI_FLAGS] @ load flags if yes + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ ret via preempt_scheduleThis is buggy. If the preempt count is _not_ zero, you end up not loading the
TI_FLAGSbits and testing_TIF_NEED_RESCHEDagainst that non-zero preempt count.
Sigh, yep. FWIW, I had the following before submitting this but dropped it due to brain failure rereading the code - convinced myself that the movne was redundant:
arch/arm/mm/cache-v6.S +3 -0
@@ -285,6 +287,7 @@ v6_dma_inv_range: str r3, [ip, #TI_PREEMPT] @ restore preempt count teq r3, #0 @ preempt count == 0? ldreq r3, [ip, #TI_FLAGS] @ load flags if yes + movne r3, #0 @ force flags to 0 tst r3, #_TIF_NEED_RESCHED @ need resched? bne preempt_schedule @ ret via preempt_schedule #endif
@@ -321,6 +324,7 @@ v6_dma_clean_range: str r3, [ip, #TI_PREEMPT] @ restore preempt count teq r3, #0 @ preempt count == 0? ldreq r3, [ip, #TI_FLAGS] @ load flags if yes + movne r3, #0 @ force flags to 0 tst r3, #_TIF_NEED_RESCHED @ need resched? bne preempt_schedule @ ret via preempt_schedule #endif
@@ -362,6 +366,7 @@ ENTRY(v6_dma_flush_range) str r3, [ip, #TI_PREEMPT] @ restore preempt count teq r3, #0 @ preempt count == 0? ldreq r3, [ip, #TI_FLAGS] @ load flags if yes + movne r3, #0 @ force flags to 0 tst r3, #_TIF_NEED_RESCHED @ need resched? bne preempt_schedule @ ret via preempt_schedule #endif
In this case, I think preempt_{disable,enable} is the better option (over interrupt disable/enable) due to variable DMA buffer sizes.
Thanks!
--
Regards,
George
Quoted 3 lines
Nicolas
PATCH v4 6/7 ARM: ARM11 MPCore: DMA_CACHE_RWFO operations are not preempt safe
Quoted 15 lines · George G. Davis
Quoted 3 lines · Nicolas Pitre
This is buggy. If the preempt count is _not_ zero, you end up not loading the
TI_FLAGSbits and testing_TIF_NEED_RESCHEDagainst that non-zero preempt count.Sigh, yep. FWIW, I had the following before submitting this but dropped it due to brain failure rereading the code - convinced myself that the movne was redundant:
arch/arm/mm/cache-v6.S +0
-0
arch/arm/mm/cache-v6.S v6_dma_inv_range: · line 293
Quoted 7 lines
arch/arm/mm/cache-v6.S +1
-0@@ -285,6 +287,7 @@ v6_dma_inv_range: str r3, [ip, #TI_PREEMPT] @ restore preempt count teq r3, #0 @ preempt count == 0? ldreq r3, [ip, #TI_FLAGS] @ load flags if yes + movne r3, #0 @ force flags to 0 tst r3, #_TIF_NEED_RESCHED @ need resched? bne preempt_schedule @ ret via preempt_schedule #endif
At this point, especially on ARMv6+, I think it is more efficient to simply skip over the code:
teq r3, #0 str r3, [ip, #TI_PREEMPT] bne 99f ldr r3, [ip, #TI_FLAGS] tst r3, #_TIF_NEED_RESCHED bne preempt_schedule
99:
Quoted 2 lines
In this case, I think preempt_{disable,enable} is the better option (over interrupt disable/enable) due to variable DMA buffer sizes.
Agreed.
Nicolas
PATCH v4 7/7 ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to cpu_v6_set_pte_ext on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in cpu_v6_set_pte_ext.
This change depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
--- arch/arm/mm/proc-v6.S | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-)
arch/arm/mm/proc-v6.S +15 -0
@@ -122,7 +122,22 @@ ENTRY(cpu_v6_switch_mm) ENTRY(cpu_v6_set_pte_ext) #ifdef CONFIG_MMU +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + stmdb sp!, {r4, r5} + get_thread_info r5 + ldr r4, [r5, #TI_PREEMPT] @ get preempt count + add r3, r4, #1 @ increment it + str r3, [r5, #TI_PREEMPT] @ disable preempt +#endif armv6_set_pte_ext cpu_v6 +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + str r4, [r5, #TI_PREEMPT] @ restore preempt count + teq r4, #0 @ preempt count == 0? + ldreq r4, [r5, #TI_FLAGS] @ load flags if yes + tst r4, #_TIF_NEED_RESCHED @ need resched? + ldmia sp!, {r4, r5} + bne preempt_schedule @ ret via preempt_schedule +#endif #endif mov pc, lr
Signature · 1 line
1.7.4.4
PATCH v4 7/7 ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
Quoted 10 lines · gdavis at mvista.com
(quoted diff) +6
-0@@ -122,7 +122,22 @@ ENTRY(cpu_v6_switch_mm) ENTRY(cpu_v6_set_pte_ext) #ifdef CONFIG_MMU +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + stmdb sp!, {r4, r5} + get_thread_info r5 + ldr r4, [r5, #TI_PREEMPT] @ get preempt count + add r3, r4, #1 @ increment it + str r3, [r5, #TI_PREEMPT] @ disable preempt
Review ENTRY(cpu_v6_switch_mm) · line 140
Quoted 10 lines
(quoted diff) +9
-0+#endif armv6_set_pte_ext cpu_v6 +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + str r4, [r5, #TI_PREEMPT] @ restore preempt count + teq r4, #0 @ preempt count == 0? + ldreq r4, [r5, #TI_FLAGS] @ load flags if yes + tst r4, #_TIF_NEED_RESCHED @ need resched? + ldmia sp!, {r4, r5} + bne preempt_schedule @ ret via preempt_schedule +#endif
Same issue as previous patch.
Wouldn't it be better if you disabled interrupts around the small critical region within armv6_set_pte_ext instead? That would certainly be a lighter solution. Something like:
arch/arm/mm/proc-macros.S +3 -0
@@ -167,8 +167,11 @@ tstne r1, #L_PTE_PRESENT moveq r3, #0 + mrs r2, cpsr + cpsid i str r3, [r0] mcr p15, 0, r0, c7, c10, 1 @ flush_pte + msr cpsr_c, r2 .endm
Nicolas
PATCH v4 7/7 ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe
Quoted 16 lines · Nicolas Pitre
Quoted 14 lines · gdavis at mvista.com
(quoted diff) +9
-0@@ -122,7 +122,22 @@ ENTRY(cpu_v6_switch_mm) ENTRY(cpu_v6_set_pte_ext) #ifdef CONFIG_MMU +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + stmdb sp!, {r4, r5} + get_thread_info r5 + ldr r4, [r5, #TI_PREEMPT] @ get preempt count + add r3, r4, #1 @ increment it + str r3, [r5, #TI_PREEMPT] @ disable preempt +#endif armv6_set_pte_ext cpu_v6 +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + str r4, [r5, #TI_PREEMPT] @ restore preempt count
Quoted 8 lines
Review ENTRY(cpu_v6_switch_mm) · line 140
Quoted 6 lines
(quoted diff) +6
-0+ teq r4, #0 @ preempt count == 0? + ldreq r4, [r5, #TI_FLAGS] @ load flags if yes + tst r4, #_TIF_NEED_RESCHED @ need resched? + ldmia sp!, {r4, r5} + bne preempt_schedule @ ret via preempt_schedule +#endifSame issue as previous patch.
Same mistake, I second guessed myself and removed the movne r4, #0 before submitting. : /
Quoted 9 lines
Wouldn't it be better if you disabled interrupts around the small critical region within
armv6_set_pte_extinstead? That would certainly be a lighter solution. Something like:arch/arm/mm/proc-macros.S +0
-0
arch/arm/mm/proc-macros.S line 175
Quoted 9 lines
arch/arm/mm/proc-macros.S +3
-0@@ -167,8 +167,11 @@ tstne r1, #L_PTE_PRESENT moveq r3, #0 + mrs r2, cpsr + cpsid i str r3, [r0] mcr p15, 0, r0, c7, c10, 1 @ flush_pte + msr cpsr_c, r2 .endm
I do like this better, less overhead than the preempt_{disable,enable} case.
Thanks!
--
Regards,
George
Quoted 3 lines
Nicolas
v5 cover ARM11 MPCore: preemption/task migration cache coherency fixups
In reply to gdavis at mvista.comGreetings,
Apologies for the long delay following up on this...
On ARM11 MPCore, the "SCU does not handle coherency consequences of CP15 cache operations" [1]. So cache maintenance functions have to insure that memory is globally consistent. Although the current Linux kernel works reasonably well on ARM11 MPCore machines, PREEMPT stress testing, e.g. parallel module loading, hackbench and LTP stress, results in crashes which exhibit non-sense oops traces where machine state does not make sense relative to the code executing at the time of the oops.
Review and analysis of the various ARM11 MPCore cache maintenance functions reveal that there are a number critical sections in which ARM11 MPCore caches and/or memory may become inconsistent, i.e. a cache line on CPU A contains a modified entry but preemption and task migration occurs after which the same cache line is cleaned/flushed on CPU B. This can obviously lead to inconsistent memory and/or cache state as cache ops on ARM11 MPCore are non-coherent.
The following is a series of ARM11 MPCore preemption/task migration fixes to resolve cache coherency problems on these machines:
George G. Davis (6):
ARM: ARM11 MPCore: Make pte_alloc_one{,_kernel} preempt safe
ARM: ARM11 MPCore: Make {clean,flush}_pmd_entry preempt safe
ARM: Move get_thread_info macro definition to <asm/assembler.h>
ARM: ARM11 MPCore: cpu_v6_dcache_clean_area needs RFO
ARM: ARM11 MPCore: Make DMA_CACHE_RWFO operations preempt safe
ARM: ARM11 MPCore: Make cpu_v6_set_pte_ext preempt safe
Konstantin Baidarov (1):
ARM: ARM11 MPCore: Make pgd_alloc preempt safe
arch/arm/include/asm/assembler.h | 13 +++++++++ arch/arm/include/asm/pgalloc.h | 28 +++++++++++++++++--- arch/arm/include/asm/pgtable-2level.h | 8 ++++++ arch/arm/include/asm/pgtable.h | 1 + arch/arm/include/asm/smp_plat.h | 2 + arch/arm/kernel/entry-header.S | 11 -------- arch/arm/mm/cache-v6.S | 45 +++++++++++++++++++++++++++++++++ arch/arm/mm/idmap.c | 4 +++ arch/arm/mm/pgd.c | 7 +++++ arch/arm/mm/proc-macros.S | 3 ++ arch/arm/mm/proc-v6.S | 22 +++++++++++++++- arch/arm/vfp/entry.S | 5 +++- arch/arm/vfp/vfphw.S | 5 +++- 13 files changed, 135 insertions(+), 19 deletions(-)
The above changes were tested on:
1. ARM Ltd RealView ARM11 MPCore
2. NEC NE1 TB
3. NEC NEmid TB
Test cases used:
1. Continuous parallel modprobe stress test
2. 24 hour LTP stress test
When these fixes are not applied, both test cases fail often and early where the failure symptoms are random hard/soft lockups and/or non-sense oopses.
When these fixes are applied, no test case failures are observed.
Note that the following change is an alternative implementation to fix all callers of clean_dcache_area:
ARM: ARM11 MPCore: cpu_v6_dcache_clean_area needs RFO
If the above change is not an acceptable fix, then preemption/ task migration issues in callers of clean_dcache_area still need to be fixed in some way. On the other hand, if the above change is acceptable, then the following changes can be dropped since they call clean_dcache_area which is fixed by the above change:
ARM: ARM11 MPCore: Make pgd_alloc preempt safe
ARM: ARM11 MPCore: Make pte_alloc_one{,_kernel} preempt safe
I further believe that Catalin's "ARM: Allow lazy cache flushing on ARM11MPCore" [2][3] is required for ARM11 MPCore machines and would like to see that or similar/alternative solution applied. I have an additional RWFO preempt fix which must be applied on top of Catalin's patch [2][3] to resolve preeption/task migration issues when his patch is applied. I'll submit that patch as a follow up to that thread.
Comments/feedback greatly appreciated.
TIA!
ChangeLog:
V2:
- Substitute {get,put}_cpu() with preempt_{disable,enable}().
- Fixed preempt {dis,en}able assembler code sequences to not use r11 since it is reserved for frame pointer use. Also optimised these sequences to use r2, r3; ip scratch registers in most cases to eliminate stack push/pop. In one case, cpu_v6_set_pte_ext, there are only two scratch registers available, r3 and ip. However, both of these are used within the armv6_set_pte_ext macro. So for this case, r3 is used as a temporary scratch when disabling preemption and r4 and r5 are pushed/popped as needed for other uses to avoid conflict with scratch register usage in armv6_set_pte_ext.
- Remove incorrect use of ALT_SMP macros in cpu_v6_set_pte_ext, making the preempt {dis,en}able assembler code sequences compile time dependent upon CONFIG_SMP instead. This code is safe on UP machines anyway.
V3:
- Fix HIGHMEM breakage.
V4:
- Drop preempt_{disable,enable}() around calls to flush_pmd_entry() in remap_area_sections() and remap_area_supersections() in file arch/arm/mm/ioremap.c since these functions are not used on SMP machines (they're enclosed within #ifndef SMP/#endif)
- Drop preempt_{disable,enable}() around call to flush_pmd_entry() in alloc_init_section() in file arch/arm/mm/mmu.c since this function is called during early kernel initialization during which time preemption and task migration is not possible. Also removed bogus FIXME comment as part of this change.
- Added calls to preempt_schedule when re-enabling preemption in various arch/arm/mm/{cache,proc}-v6.S functions which need preemption disabled due task migration issues.
V5:
- Drop OMAP specific changes in "ARM: ARM11 MPCore: clean_dcache_area is not preempt safe" based on feed back from Tony Lindgren [4] indicating that the affected code is not used on ARM11 MPCore machines.
- Fix bug in implementation of conditional preempt_schedule calls pointed out by Nicolas Pitre [5].
- Reimplement "ARM: ARM11 MPCore: cpu_v6_set_pte_ext is not preempt safe" to simply disable/enable interrupts around critical section based on feedback from Nicolas Pitre [6].
- Improve (ARMv6+ instruction cycle) efficiency of preempt_enable assembler code sequences as suggested by Nicolas Pitre [7].
- Implement alternative implementation for fixup of clean_dcache_area callers by implementing RFO and preempt disable in cpu_v6_dcache_clean_area. If this change is acceptable, the "ARM: ARM11 MPCore: Make pgd_alloc preempt safe" and "ARM: ARM11
MPCore: Make pte_alloc_one{,_kernel} preempt safe" commits can be dropped. Else, if unacceptable, some callers of clean_dcache_area may be missing preempt/migration fixups which are not otherwise fixed in this series.
--
Regards,
George
References:
[1] http://infocenter.arm.com/help/topic/com.arm.doc.dai0228a/index.html#arm_toc9
[2] http://www.spinics.net/lists/arm-kernel/msg129403.html
[3] http://lists.infradead.org/pipermail/linux-arm-kernel/2010-May/014990.html
[4] http://lists.infradead.org/pipermail/linux-arm-kernel/2011-October/069912.html
[5] http://lists.infradead.org/pipermail/linux-arm-kernel/2011-October/069942.html
[6] http://lists.infradead.org/pipermail/linux-arm-kernel/2011-October/069943.html
[7] http://lists.infradead.org/pipermail/linux-arm-kernel/2011-October/069953.html
PATCH v5 1/7 ARM: ARM11 MPCore: Make pgd_alloc preempt safe
From: Konstantin Baidarov <[email protected]>
If preemption and subsequent task migration occurs during a call to pgd_alloc on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption around initialization and flushing of new PGDs.
--- arch/arm/mm/pgd.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
arch/arm/mm/pgd.c +7 -0
@@ -16,6 +16,7 @@#include <asm/pgalloc.h> #include <asm/page.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #include "mm.h"
@@ -41,6 +42,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)if (!new_pgd) goto no_pgd; +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (new_pgd,0 ,USER_PTRS_PER_PGD *sizeof (pgd_t));/*
@@ -52,6 +56,9 @@ pgd_t *pgd_alloc(struct mm_struct *mm)clean_dcache_area (new_pgd,PTRS_PER_PGD *sizeof (pgd_t)); +if (cache_ops_need_broadcast ()) +preempt_enable (); +#ifdef CONFIG_ARM_LPAE /* * Allocate PMD table for modules and pkmap mappings.
Signature · 1 line
1.7.4.4
PATCH v5 2/7 ARM: ARM11 MPCore: Make pte_alloc_one{, _kernel} preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during a call to pte_alloc_one{,_kernel} on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption around initialization and flushing of new PTEs.
--- arch/arm/include/asm/pgalloc.h | 24 +++++++++++++++++++----- 1 files changed, 19 insertions(+), 5 deletions(-)
arch/arm/include/asm/pgalloc.h +19 -5
@@ -17,6 +17,7 @@#include <asm/processor.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #define check_pgt_cache () do { } while (0)
@@ -57,7 +58,7 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)extern pgd_t *pgd_alloc (struct mm_struct *mm);extern void pgd_free (struct mm_struct *mm,pgd_t *pgd); -#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO) +#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT)static inline void clean_pte_table (pte_t *pte) {
@@ -86,8 +87,14 @@ pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)pte_t *pte; pte = (pte_t *)__get_free_page (PGALLOC_GFP ); -if (pte) +if (pte) { +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (pte,0 ,PAGE_SIZE );clean_pte_table (pte); +if (cache_ops_need_broadcast ()) +preempt_enable (); + }return pte; }
@@ -98,13 +105,20 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)struct page *pte;#ifdef CONFIG_HIGHPTE - pte =alloc_pages (PGALLOC_GFP | __GFP_HIGHMEM,0 ); + pte =alloc_pages (PGALLOC_GFP | __GFP_ZERO | __GFP_HIGHMEM,0 );#else pte =alloc_pages (PGALLOC_GFP ,0 );#endif if (pte) { -if (!PageHighMem (pte)) -clean_pte_table (page_address (pte)); +if (!PageHighMem (pte)) { +void *page =page_address (pte); +if (cache_ops_need_broadcast ()) +preempt_disable (); +memset (page,0 ,PAGE_SIZE ); +clean_pte_table (page); +if (cache_ops_need_broadcast ()) +preempt_enable (); + }pgtable_page_ctor (pte); }
Signature · 1 line
1.7.4.4
PATCH v5 3/7 ARM: ARM11 MPCore: Make {clean, flush}_pmd_entry preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to {clean,flush}_pmd_entry on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable preemption in callers of these functions around PMD modifications and subsequent {clean,flush}_pmd_entry calls.
--- arch/arm/include/asm/pgalloc.h | 4 ++++ arch/arm/include/asm/pgtable-2level.h | 8 ++++++++ arch/arm/include/asm/pgtable.h | 1 + arch/arm/include/asm/smp_plat.h | 2 ++ arch/arm/mm/idmap.c | 4 ++++ 5 files changed, 19 insertions(+), 0 deletions(-)
arch/arm/include/asm/pgalloc.h +4 -0
@@ -144,11 +144,15 @@ static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, pmdval_t prot) {pmdval_t pmdval = (pte +PTE_HWTABLE_OFF ) | prot; +if (cache_ops_need_broadcast ()) +preempt_disable (); pmdp[0 ] =__pmd (pmdval);#ifndef CONFIG_ARM_LPAE pmdp[1 ] =__pmd (pmdval +256 *sizeof (pte_t));#endif flush_pmd_entry (pmdp); +if (cache_ops_need_broadcast ()) +preempt_enable (); }/*
arch/arm/include/asm/pgtable-2level.h +8 -0
@@ -162,16 +162,24 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)#define copy_pmd (pmdpd,pmdps) \ do { \ + if (cache_ops_need_broadcast()) \ + preempt_disable(); \ pmdpd[0] = pmdps[0]; \ pmdpd[1] = pmdps[1]; \ flush_pmd_entry(pmdpd); \ + if (cache_ops_need_broadcast()) \ + preempt_enable(); \ } while (0)#define pmd_clear (pmdp) \ do { \ + if (cache_ops_need_broadcast()) \ + preempt_disable(); \ pmdp[0] = __pmd(0); \ pmdp[1] = __pmd(0); \ clean_pmd_entry(pmdp); \ + if (cache_ops_need_broadcast()) \ + preempt_enable(); \ } while (0)/* we don't need complex calculations here as the pmd is folded into the pgd */
arch/arm/include/asm/pgtable.h +1 -0
@@ -12,6 +12,7 @@#include <linux/const.h> #include <asm/proc-fns.h> +#include <asm/smp_plat.h> #ifndef CONFIG_MMU
arch/arm/include/asm/smp_plat.h +2 -0
@@ -5,6 +5,7 @@#ifndef __ASMARM_SMP_PLAT_H#define __ASMARM_SMP_PLAT_H +#ifndef __ASSEMBLY__#include <asm/cputype.h> /*
@@ -50,3 +51,4 @@ extern int __cpu_logical_map[];#define cpu_logical_map (cpu) __cpu_logical_map[cpu]#endif +#endif
arch/arm/mm/idmap.c +4 -0
@@ -39,11 +39,15 @@ static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end, {pmd_t *pmd =pmd_offset (pud, addr); +if (cache_ops_need_broadcast ()) +preempt_disable (); addr = (addr &PMD_MASK ) | prot; pmd[0 ] =__pmd (addr); addr +=SECTION_SIZE ; pmd[1 ] =__pmd (addr);flush_pmd_entry (pmd); +if (cache_ops_need_broadcast ()) +preempt_enable (); }#endif /* CONFIG_ARM_LPAE */
Signature · 1 line
1.7.4.4
PATCH v5 4/7 ARM: Move get_thread_info macro definition to <asm/assembler.h>
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
The get_thread_info assembler macro is currently defined in arch/arm/kernel/entry-header.S and used in the following files:
arch/arm/kernel/entry-armv.S
arch/arm/kernel/entry-common.S
arch/arm/vfp/vfphw.S
arch/arm/vfp/entry.S
The first two cases above use #include "entry-header.S" while the later two cases use #include "../kernel/entry-header.S" to obtain the get_thread_info assembler macro definition.
Move the get_thread_info assembler macro definition into arch/arm/include/asm/assember.h and clean up inclusion of this macro definition to use #include <asm/assember.h>.
This change facilitates use of the get_thread_info assembler macro in other assembler functions without more of the same path relative include directives.
--- arch/arm/include/asm/assembler.h | 13 +++++++++++++ arch/arm/kernel/entry-header.S | 11 ----------- arch/arm/vfp/entry.S | 5 ++++- arch/arm/vfp/vfphw.S | 5 ++++- 4 files changed, 21 insertions(+), 13 deletions(-)
arch/arm/include/asm/assembler.h +13 -0
@@ -23,6 +23,19 @@#include <asm/ptrace.h> #include <asm/domain.h> +#ifndef CONFIG_THUMB2_KERNEL + .macro get_thread_info, rd + mov \rd, sp, lsr #13 + mov \rd, \rd, lsl #13 + .endm +#else /* CONFIG_THUMB2_KERNEL */ + .macro get_thread_info, rd + mov \rd, sp + lsr \rd, \rd, #13 + mov \rd, \rd, lsl #13 + .endm +#endif /* !CONFIG_THUMB2_KERNEL */ + #define IOMEM(x) (x)/*
arch/arm/kernel/entry-header.S +0 -11
@@ -108,11 +108,6 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm - .macro get_thread_info, rd - mov \rd, sp, lsr #13 - mov \rd, \rd, lsl #13 - .endm - @ @ 32-bit wide "mov pc, reg" @
@@ -148,12 +143,6 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm - .macro get_thread_info, rd - mov \rd, sp - lsr \rd, \rd, #13 - mov \rd, \rd, lsl #13 - .endm - @ @ 32-bit wide "mov pc, reg" @
arch/arm/vfp/entry.S +4 -1
@@ -15,9 +15,12 @@ * r10 = thread_info structure * lr = failure return */ +#include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" +#include <asm/assembler.h> +#include <asm/asm-offsets.h> ENTRY(do_vfp) #ifdef CONFIG_PREEMPT
arch/arm/vfp/vfphw.S +4 -1
@@ -14,9 +14,12 @@ * r10 points at the start of the private FP workspace in the thread structure * sp points to a struct pt_regs (as defined in include/asm/proc/ptrace.h) */ +#include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/vfpmacros.h> -#include "../kernel/entry-header.S" +#include <asm/assembler.h> +#include <asm/asm-offsets.h> .macro DBGSTR, str #ifdef DEBUG
Signature · 1 line
1.7.4.4
PATCH v5 5/7 ARM: ARM11 MPCore: cpu_v6_dcache_clean_area needs RFO
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
Implement Request-For-Ownership in cpu_v6_dcache_clean_area and disable preemption in same to insure that memory is consistent in cases of preemption and subsequent task migration just before and during calls to this function. This is an alternative implementation to other workarounds which disable preemption in callers of this function while updating PTEs.
This change depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
--- arch/arm/mm/proc-v6.S | 22 +++++++++++++++++++++- 1 files changed, 21 insertions(+), 1 deletions(-)
arch/arm/mm/proc-v6.S +21 -1
@@ -81,10 +81,30 @@ ENTRY(cpu_v6_do_idle) ENTRY(cpu_v6_dcache_clean_area) #ifndef TLB_CAN_READ_FROM_L1_CACHE -1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preemption +#endif +1: +#ifdef CONFIG_SMP + /* no cache maintenance broadcasting on ARM11MPCore */ + ldr r2, [r0] @ read for ownership +#endif + mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #D_CACHE_LINE_SIZE subs r1, r1, #D_CACHE_LINE_SIZE bhi 1b +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + teq r3, #0 @ preempt count == 0? + str r3, [ip, #TI_PREEMPT] @ restore preempt count + bne 99f @ done if non-zero + ldr r3, [ip, #TI_FLAGS] @ else check flags + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ yes, do preempt_schedule +99: +#endif #endif mov pc, lr
Signature · 1 line
1.7.4.4
PATCH v5 5/7 ARM: ARM11 MPCore: cpu_v6_dcache_clean_area needs RFO
Quoted 38 lines · gdavis at mvista.com
From: George G. Davis <[email protected]>
Implement Request-For-Ownership in
cpu_v6_dcache_clean_areaand disable preemption in same to insure that memory is consistent in cases of preemption and subsequent task migration just before and during calls to this function. This is an alternative implementation to other workarounds which disable preemption in callers of this function while updating PTEs.This change depends on "ARM: Move
get_thread_infomacro definition to <asm/assembler.h>".Signed-off-by George G. Davis <[email protected]>--- arch/arm/mm/proc-v6.S | 22 +++++++++++++++++++++- 1 files changed, 21 insertions(+), 1 deletions(-)arch/arm/mm/proc-v6.S +12
-1@@ -81,10 +81,30 @@ ENTRY(cpu_v6_do_idle) ENTRY(cpu_v6_dcache_clean_area) #ifndef TLB_CAN_READ_FROM_L1_CACHE -1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preemption +#endif +1: +#ifdef CONFIG_SMP + /* no cache maintenance broadcasting on ARM11MPCore */ + ldr r2, [r0] @ read for ownership +#endif + mcr p15, 0, r0, c7, c10, 1 @ clean D entry
arch/arm/mm/proc-v6.S ENTRY(cpu_v6_do_idle) · line 105
Quoted 10 lines
arch/arm/mm/proc-v6.S +7
-0add r0, r0, #D_CACHE_LINE_SIZE subs r1, r1, #D_CACHE_LINE_SIZE bhi 1b +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + teq r3, #0 @ preempt count == 0? + str r3, [ip, #TI_PREEMPT] @ restore preempt count + bne 99f @ done if non-zero + ldr r3, [ip, #TI_FLAGS] @ else check flags + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ yes, do preempt_schedule
Maybe we should get some asm macros for preempt disable/enable as they are used in more than one place.
Alternatively, we could disable the interrupts around RFO and D-cache cleaning (inside the loop), though I think the preempt disabling is faster.
Signature · 1 line
Catalin
PATCH v5 5/7 ARM: ARM11 MPCore: cpu_v6_dcache_clean_area needs RFO
Quoted 3 lines · Catalin Marinas
Alternatively, we could disable the interrupts around RFO and D-cache cleaning (inside the loop), though I think the preempt disabling is faster.
It also has the *important* effect that it doesn't cause problems with interrupt latency, which is a real concern if you have peripherals requiring quick servicing.
PATCH v5 5/7 ARM: ARM11 MPCore: cpu_v6_dcache_clean_area needs RFO
Quoted 8 lines · Russell King - ARM Linux
Quoted 3 lines · Catalin Marinas
Alternatively, we could disable the interrupts around RFO and D-cache cleaning (inside the loop), though I think the preempt disabling is faster.
It also has the *important* effect that it doesn't cause problems with interrupt latency, which is a real concern if you have peripherals requiring quick servicing.
I was suggesting just disabling the interrupts inside the loop around RFO and D-cache cleaning, so only for a single cache line (and not the whole range) which would not introduce noticeable interrupt latency.
Signature · 1 line
Catalin
PATCH v5 5/7 ARM: ARM11 MPCore: cpu_v6_dcache_clean_area needs RFO
Quoted 6 lines · Catalin Marinas
Quoted 5 lines · Russell King - ARM Linux
Quoted 3 lines · Catalin Marinas
Alternatively, we could disable the interrupts around RFO and D-cache cleaning (inside the loop), though I think the preempt disabling is faster.
Quoted 7 lines
Quoted 3 lines
It also has the *important* effect that it doesn't cause problems with interrupt latency, which is a real concern if you have peripherals requiring quick servicing.
I was suggesting just disabling the interrupts inside the loop around RFO and D-cache cleaning, so only for a single cache line (and not the whole range) which would not introduce noticeable interrupt latency.
But would slow down the loop, probably significantly.
PATCH v5 5/7 ARM: ARM11 MPCore: cpu_v6_dcache_clean_area needs RFO
Quoted 11 lines · Russell King - ARM Linux
Quoted 10 lines · Catalin Marinas
Quoted 8 lines · Russell King - ARM Linux
Quoted 3 lines · Catalin Marinas
Alternatively, we could disable the interrupts around RFO and D-cache cleaning (inside the loop), though I think the preempt disabling is faster.
It also has the *important* effect that it doesn't cause problems with interrupt latency, which is a real concern if you have peripherals requiring quick servicing.
Quoted 5 lines
Quoted 3 lines
I was suggesting just disabling the interrupts inside the loop around RFO and D-cache cleaning, so only for a single cache line (and not the whole range) which would not introduce noticeable interrupt latency.
But would slow down the loop, probably significantly.
Yes, indeed.
Signature · 1 line
Catalin
PATCH v5 5/7 ARM: ARM11 MPCore: cpu_v6_dcache_clean_area needs RFO
In reply to Russell King - ARM LinuxQuoted 8 lines · Russell King - ARM Linux
Quoted 3 lines · Catalin Marinas
Alternatively, we could disable the interrupts around RFO and D-cache cleaning (inside the loop), though I think the preempt disabling is faster.
It also has the *important* effect that it doesn't cause problems with interrupt latency, which is a real concern if you have peripherals requiring quick servicing.
Yep, even disabling preemption is not acceptable for some work loads but this is the least painful option here.
Thanks!
--
Regards,
George
PATCH v5 5/7 ARM: ARM11 MPCore: cpu_v6_dcache_clean_area needs RFO
In reply to Catalin MarinasHello,
Quoted 44 lines · Catalin Marinas
Quoted 43 lines · gdavis at mvista.com
From: George G. Davis <[email protected]>
Implement Request-For-Ownership in
cpu_v6_dcache_clean_areaand disable preemption in same to insure that memory is consistent in cases of preemption and subsequent task migration just before and during calls to this function. This is an alternative implementation to other workarounds which disable preemption in callers of this function while updating PTEs.This change depends on "ARM: Move
get_thread_infomacro definition to <asm/assembler.h>".Signed-off-by George G. Davis <[email protected]>---
arch/arm/mm/proc-v6.S| 22 +++++++++++++++++++++-1 files changed, 21 insertions(+), 1 deletions(-)arch/arm/mm/proc-v6.S +14
-1@@ -81,10 +81,30 @@ ENTRY(cpu_v6_do_idle) ENTRY(cpu_v6_dcache_clean_area) #ifndef TLB_CAN_READ_FROM_L1_CACHE -1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preemption +#endif +1: +#ifdef CONFIG_SMP + /* no cache maintenance broadcasting on ARM11MPCore */ + ldr r2, [r0] @ read for ownership +#endif + mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #D_CACHE_LINE_SIZE subs r1, r1, #D_CACHE_LINE_SIZE bhi 1b +#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) + teq r3, #0 @ preempt count == 0?
Quoted 8 lines
arch/arm/mm/proc-v6.S ENTRY(cpu_v6_do_idle) · line 105
Quoted 5 lines
arch/arm/mm/proc-v6.S +5
-0+ str r3, [ip, #TI_PREEMPT] @ restore preempt count + bne 99f @ done if non-zero + ldr r3, [ip, #TI_FLAGS] @ else check flags + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ yes, do preempt_scheduleMaybe we should get some asm macros for preempt disable/enable as they are used in more than one place.
I've pondered that a bit, it's not easy, the conditional call to preempt_schedule in particular is non-trivial, you have to save/restore lr somewhere. In the open coded cases in these patches, it was easy to simply branch to preempt_schedule and return from there but I suspect it won't be so trivial in all cases.
Quoted 3 lines
Alternatively, we could disable the interrupts around RFO and D-cache cleaning (inside the loop), though I think the preempt disabling is faster.
I've had reports that disabling preemption is impacting performance in some of these cases (requiring nasty hacks in upper level macros to break up blocks into smaller pieces). So I don't even want to consider disabling interrupts for these cases. : )
Thanks!
--
Regards,
George
Quoted 3 lines
Signature · 2 lines
-- Catalin
PATCH v5 6/7 ARM: ARM11 MPCore: Make DMA_CACHE_RWFO operations preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
The DMA_CACHE_RWFO operations are not preempt safe. If preemption occurs immediately following a RWFO operation on a cache line of CPU A, it is possible for task migration to occur on resume at which point the subsequent cache maintenance operation on the same cache line of CPU B will have no affect on the CPU A cache line leading to inconsistent memory and/or cache state. To prevent this, disable preemption during RWFO operations.
This change depends on "ARM: Move get_thread_info macro definition to <asm/assembler.h>".
--- arch/arm/mm/cache-v6.S | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 45 insertions(+), 0 deletions(-)
arch/arm/mm/cache-v6.S +45 -0
@@ -203,6 +203,12 @@ ENTRY(v6_flush_kern_dcache_area) */ v6_dma_inv_range: #ifdef CONFIG_DMA_CACHE_RWFO +#ifdef CONFIG_PREEMPT + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership #endif
@@ -239,6 +245,15 @@ v6_dma_inv_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + teq r3, #0 @ preempt count == 0? + str r3, [ip, #TI_PREEMPT] @ restore preempt count + bne 99f @ done if non-zero + ldr r3, [ip, #TI_FLAGS] @ else check flags + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ yes, do preempt_schedule +99: +#endif mov pc, lr /*
@@ -247,6 +262,12 @@ v6_dma_inv_range: * - end - virtual end address of region */ v6_dma_clean_range: +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: #ifdef CONFIG_DMA_CACHE_RWFO
@@ -262,6 +283,15 @@ v6_dma_clean_range: blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + teq r3, #0 @ preempt count == 0? + str r3, [ip, #TI_PREEMPT] @ restore preempt count + bne 99f @ done if non-zero + ldr r3, [ip, #TI_FLAGS] @ else check flags + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ yes, do preempt_schedule +99: +#endif mov pc, lr /*
@@ -271,6 +301,12 @@ v6_dma_clean_range: */ ENTRY(v6_dma_flush_range) #ifdef CONFIG_DMA_CACHE_RWFO +#ifdef CONFIG_PREEMPT + get_thread_info ip + ldr r3, [ip, #TI_PREEMPT] @ get preempt count + add r2, r3, #1 @ increment it + str r2, [ip, #TI_PREEMPT] @ disable preempt +#endif ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership #endif
@@ -290,6 +326,15 @@ ENTRY(v6_dma_flush_range) blo 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#if defined(CONFIG_DMA_CACHE_RWFO) && defined(CONFIG_PREEMPT) + teq r3, #0 @ preempt count == 0? + str r3, [ip, #TI_PREEMPT] @ restore preempt count + bne 99f @ done if non-zero + ldr r3, [ip, #TI_FLAGS] @ else check flags + tst r3, #_TIF_NEED_RESCHED @ need resched? + bne preempt_schedule @ yes, do preempt_schedule +99: +#endif mov pc, lr /*
Signature · 1 line
1.7.4.4
PATCH v5 7/7 ARM: ARM11 MPCore: Make cpu_v6_set_pte_ext preempt safe
In reply to gdavis at mvista.comFrom: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to cpu_v6_set_pte_ext on ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable interrupts in cpu_v6_set_pte_ext.
--- arch/arm/mm/proc-macros.S | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
arch/arm/mm/proc-macros.S +3 -0
@@ -168,8 +168,11 @@ tstne r1, #L_PTE_PRESENT moveq r3, #0 + mrs r2, cpsr + cpsid i str r3, [r0] mcr p15, 0, r0, c7, c10, 1 @ flush_pte + msr cpsr_c, r2 .endm
Signature · 1 line
1.7.4.4
PATCH v5 7/7 ARM: ARM11 MPCore: Make cpu_v6_set_pte_ext preempt safe
Quoted 17 lines · gdavis at mvista.com
From: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to
cpu_v6_set_pte_exton ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable interrupts incpu_v6_set_pte_ext.Signed-off-by George G. Davis <[email protected]>--- arch/arm/mm/proc-macros.S | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)arch/arm/mm/proc-macros.S +0
-0
arch/arm/mm/proc-macros.S line 176
Quoted 9 lines
arch/arm/mm/proc-macros.S +3
-0@@ -168,8 +168,11 @@ tstne r1, #L_PTE_PRESENT moveq r3, #0 + mrs r2, cpsr + cpsid i str r3, [r0] mcr p15, 0, r0, c7, c10, 1 @ flush_pte + msr cpsr_c, r2 .endm
We have save_and_disable_irqs and restore_irqs (and _notrace variants) macros in asm/assembler.h.
Signature · 1 line
Catalin
PATCH v5 7/7 ARM: ARM11 MPCore: Make cpu_v6_set_pte_ext preempt safe
Quoted 21 lines · Catalin Marinas
Quoted 20 lines · gdavis at mvista.com
From: George G. Davis <[email protected]>
If preemption and subsequent task migration occurs during calls to
cpu_v6_set_pte_exton ARM11 MPCore machines, global memory state can become inconsistent. To prevent inconsistent memory state on these machines, disable interrupts incpu_v6_set_pte_ext.Signed-off-by George G. Davis <[email protected]>---
arch/arm/mm/proc-macros.S| 3 +++1 files changed, 3 insertions(+), 0 deletions(-)arch/arm/mm/proc-macros.S +0
-0@@ -168,8 +168,11 @@ tstne r1, #L_PTE_PRESENT moveq r3, #0
Quoted 9 lines
arch/arm/mm/proc-macros.S line 176
Quoted 6 lines
arch/arm/mm/proc-macros.S +3
-0+ mrs r2, cpsr + cpsid i str r3, [r0] mcr p15, 0, r0, c7, c10, 1 @ flush_pte + msr cpsr_c, r2 .endmWe have
save_and_disable_irqsandrestore_irqs(and_notracevariants) macros inasm/assembler.h.
I updated this patch to use save_and_disable_irqs and restore_irqs.
Thanks!
--
Regards,
George
Quoted 3 lines
Signature · 2 lines
-- Catalin