Since ARMv6 new atomic instructions have been introduced: ldrex/strex. Several implementation are possible based on (1) global and local exclusive monitors and (2) local exclusive monitor and snoop unit.
In case of the 2nd option exclusive store operation on uncached region may be faulty.
Check for availability of the global monitor to provide some hint about possible issues.
Signed-off-by: Vladimir Murzin murzin.v@gmail.com --- arch/arm/include/asm/bugs.h | 14 ++++++++++++-- arch/arm/mm/fault-armv.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/bugs.h b/arch/arm/include/asm/bugs.h index a97f1ea..230432e 100644 --- a/arch/arm/include/asm/bugs.h +++ b/arch/arm/include/asm/bugs.h @@ -13,9 +13,19 @@ #ifdef CONFIG_MMU extern void check_writebuffer_bugs(void);
-#define check_bugs() check_writebuffer_bugs() +#if __LINUX_ARM_ARCH__ < 6 +static void check_gmonitor_bugs(void) {}; #else -#define check_bugs() do { } while (0) +extern void check_gmonitor_bugs(void); +#endif + +static inline void check_bugs(void) +{ + check_writebuffer_bugs(); + check_gmonitor_bugs(); +} +#else +static inline void check_bugs(void) { } #endif
#endif diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index 7599e26..c12846b 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -206,6 +206,49 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, __flush_icache_all(); } } +#else +void __init check_gmonitor_bugs(void) +{ + struct page *page; + const char *reason; + unsigned long res = 1; + + printk(KERN_INFO "CPU: Testing for global monitor: "); + + page = alloc_page(GFP_KERNEL); + if (page) { + unsigned long *p; + pgprot_t prot = __pgprot_modify(PAGE_KERNEL, + L_PTE_MT_MASK, L_PTE_MT_UNCACHED); + + p = vmap(&page, 1, VM_IOREMAP, prot); + + if (p) { + int temp; + + __asm__ __volatile__( \ + "ldrex %1, [%2]\n" \ + "strex %0, %1, [%2]" \ + : "=&r" (res), "=&r" (temp) \ + : "r" (p) \ + : "cc", "memory"); + + reason = "n\a (atomic ops may be faulty)"; + } else { + reason = "unable to map memory\n"; + } + + vunmap(p); + put_page(page); + } else { + reason = "unable to grab page\n"; + } + + if (res) + printk("failed, %s\n", reason); + else + printk("ok\n"); +} #endif /* __LINUX_ARM_ARCH__ < 6 */
/*
On Mon, Feb 18, 2013 at 08:26:50PM +0400, Vladimir Murzin wrote:
Since ARMv6 new atomic instructions have been introduced: ldrex/strex. Several implementation are possible based on (1) global and local exclusive monitors and (2) local exclusive monitor and snoop unit.
In case of the 2nd option exclusive store operation on uncached region may be faulty.
Check for availability of the global monitor to provide some hint about possible issues.
How does this code actually do that?
+void __init check_gmonitor_bugs(void) +{
- struct page *page;
- const char *reason;
- unsigned long res = 1;
- printk(KERN_INFO "CPU: Testing for global monitor: ");
- page = alloc_page(GFP_KERNEL);
- if (page) {
unsigned long *p;
pgprot_t prot = __pgprot_modify(PAGE_KERNEL,
L_PTE_MT_MASK, L_PTE_MT_UNCACHED);
p = vmap(&page, 1, VM_IOREMAP, prot);
This is bad practise. Remapping a page of already mapped kernel memory using different attributes (in this case, strongly ordered) is _definitely_ a violation of the architecture requirements. The behaviour you will see from this are in no way guaranteed.
If you want to do this, it must either come from highmem, or not already be mapped.
Moreover, this is absolutely silly - the ARM ARM says:
"LDREX and STREX operations *must* be performed only on memory with the Normal memory attribute."
L_PTE_MT_UNCACHED doesn't get you that. As I say above, that gets you strongly ordered memory, not "normal memory" as required by the architecture for use with exclusive types.
if (p) {
int temp;
__asm__ __volatile__( \
"ldrex %1, [%2]\n" \
"strex %0, %1, [%2]" \
: "=&r" (res), "=&r" (temp) \
: "r" (p) \
\ character not required for any of the above. Neither is the __ version of "asm" and "volatile".
: "cc", "memory");
reason = "n\\a (atomic ops may be faulty)";
"n\a" ?
So... at the moment this has me wondering - you're testing atomic operations with a strongly ordered memory region, which ARM already define this to be outside of the architecture spec. The behaviour you see is not defined architecturally.
And if you're trying to use LDREX/STREX to a strongly ordered or device memory region, then you're quite right that it'll be unreliable. It's not defined to even work. That's not because they're faulty, it's because you're abusing them.
linaro-kernel@lists.linaro.org