* [PATCH 0/9] Various patches for 2.6.37-rc1
@ 2010-08-31 13:57 Catalin Marinas
2010-08-31 13:58 ` [PATCH 1/9] ARM: Allow lazy cache flushing via PG_arch_1 for highmem pages Catalin Marinas
` (8 more replies)
0 siblings, 9 replies; 19+ messages in thread
From: Catalin Marinas @ 2010-08-31 13:57 UTC (permalink / raw)
To: linux-arm-kernel
Hi Russell,
These are the patches that I plan to submit for 2.6.37-rc1. They have
been posted in the past on this list and are currently in linux-next.
Please let me know if you have any objections. Thanks.
Catalin Marinas (8):
ARM: Allow lazy cache flushing via PG_arch_1 for highmem pages
ARM: Assume new page cache pages have dirty D-cache
ARM: Introduce __sync_icache_dcache() for VIPT caches
ARM: Use lazy cache flushing on ARMv7 SMP systems
ARM: Remove superfluous flush_kernel_dcache_page()
ARM: Implement phys_mem_access_prot() to avoid attributes aliasing
ARM: Improve the L2 cache performance when PL310 is used
ARM: Remove the domain switching on ARMv6k/v7 CPUs
Leif Lindholm (1):
ARM: Add SWP/SWPB emulation for ARMv7 processors (v5)
Documentation/arm/00-INDEX | 2
Documentation/arm/swp_emulation | 27 ++++
arch/arm/include/asm/assembler.h | 13 +-
arch/arm/include/asm/cacheflush.h | 9 -
arch/arm/include/asm/domain.h | 31 ++++
arch/arm/include/asm/futex.h | 9 +
arch/arm/include/asm/pgtable.h | 29 ++++
arch/arm/include/asm/smp_plat.h | 4 +
arch/arm/include/asm/tlbflush.h | 12 +-
arch/arm/include/asm/traps.h | 2
arch/arm/include/asm/uaccess.h | 16 +-
arch/arm/kernel/Makefile | 1
arch/arm/kernel/entry-armv.S | 4 -
arch/arm/kernel/fiq.c | 5 +
arch/arm/kernel/swp_emulate.c | 260 +++++++++++++++++++++++++++++++++++++
arch/arm/kernel/traps.c | 14 +-
arch/arm/lib/getuser.S | 13 +-
arch/arm/lib/putuser.S | 29 ++--
arch/arm/lib/uaccess.S | 83 ++++++------
arch/arm/mm/Kconfig | 43 ++++++
arch/arm/mm/cache-l2x0.c | 15 ++
arch/arm/mm/copypage-v4mc.c | 2
arch/arm/mm/copypage-v6.c | 2
arch/arm/mm/copypage-xscale.c | 2
arch/arm/mm/dma-mapping.c | 6 +
arch/arm/mm/fault-armv.c | 8 +
arch/arm/mm/flush.c | 46 +++++--
arch/arm/mm/mmu.c | 20 ++-
arch/arm/mm/proc-macros.S | 7 +
arch/arm/mm/proc-v7.S | 9 +
30 files changed, 600 insertions(+), 123 deletions(-)
create mode 100644 Documentation/arm/swp_emulation
create mode 100644 arch/arm/kernel/swp_emulate.c
--
Catalin
^ permalink raw reply [flat|nested] 19+ messages in thread* [PATCH 1/9] ARM: Allow lazy cache flushing via PG_arch_1 for highmem pages 2010-08-31 13:57 [PATCH 0/9] Various patches for 2.6.37-rc1 Catalin Marinas @ 2010-08-31 13:58 ` Catalin Marinas 2010-08-31 13:58 ` [PATCH 2/9] ARM: Assume new page cache pages have dirty D-cache Catalin Marinas ` (7 subsequent siblings) 8 siblings, 0 replies; 19+ messages in thread From: Catalin Marinas @ 2010-08-31 13:58 UTC (permalink / raw) To: linux-arm-kernel Commit d73cd42 forced non-lazy cache flushing of highmem pages in flush_dcache_page(). This isn't needed since __flush_dcache_page() (called lazily from update_mmu_cache) can handle highmem pages (fixed by commit 7e5a69e). Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Nicolas Pitre <nicolas.pitre@linaro.org> --- arch/arm/mm/flush.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index c6844cb..87dd5ff 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -247,7 +247,7 @@ void flush_dcache_page(struct page *page) mapping = page_mapping(page); #ifndef CONFIG_SMP - if (!PageHighMem(page) && mapping && !mapping_mapped(mapping)) + if (mapping && !mapping_mapped(mapping)) set_bit(PG_dcache_dirty, &page->flags); else #endif ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 2/9] ARM: Assume new page cache pages have dirty D-cache 2010-08-31 13:57 [PATCH 0/9] Various patches for 2.6.37-rc1 Catalin Marinas 2010-08-31 13:58 ` [PATCH 1/9] ARM: Allow lazy cache flushing via PG_arch_1 for highmem pages Catalin Marinas @ 2010-08-31 13:58 ` Catalin Marinas 2010-08-31 13:58 ` [PATCH 3/9] ARM: Introduce __sync_icache_dcache() for VIPT caches Catalin Marinas ` (6 subsequent siblings) 8 siblings, 0 replies; 19+ messages in thread From: Catalin Marinas @ 2010-08-31 13:58 UTC (permalink / raw) To: linux-arm-kernel There are places in Linux where writes to newly allocated page cache pages happen without a subsequent call to flush_dcache_page() (several PIO drivers including USB HCD). This patch changes the meaning of PG_arch_1 to be PG_dcache_clean and always flush the D-cache for a newly mapped page in update_mmu_cache(). The patch also sets the PG_arch_1 bit in the DMA cache maintenance function to avoid additional cache flushing in update_mmu_cache(). Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Tested-by: Rabin Vincent <rabin.vincent@stericsson.com> Cc: Nicolas Pitre <nicolas.pitre@linaro.org> --- arch/arm/include/asm/cacheflush.h | 6 +++--- arch/arm/include/asm/tlbflush.h | 2 +- arch/arm/mm/copypage-v4mc.c | 2 +- arch/arm/mm/copypage-v6.c | 2 +- arch/arm/mm/copypage-xscale.c | 2 +- arch/arm/mm/dma-mapping.c | 6 ++++++ arch/arm/mm/fault-armv.c | 4 ++-- arch/arm/mm/flush.c | 3 ++- 8 files changed, 17 insertions(+), 10 deletions(-) diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index 4656a24..d3730f0 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -137,10 +137,10 @@ #endif /* - * This flag is used to indicate that the page pointed to by a pte - * is dirty and requires cleaning before returning it to the user. + * This flag is used to indicate that the page pointed to by a pte is clean + * and does not require cleaning before returning it to the user. */ -#define PG_dcache_dirty PG_arch_1 +#define PG_dcache_clean PG_arch_1 /* * MM Cache Management diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h index 33b546a..9ad329a 100644 --- a/arch/arm/include/asm/tlbflush.h +++ b/arch/arm/include/asm/tlbflush.h @@ -560,7 +560,7 @@ extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); #endif /* - * if PG_dcache_dirty is set for the page, we need to ensure that any + * If PG_dcache_clean is not set for the page, we need to ensure that any * cache entries for the kernels virtual memory range are written * back to the page. */ diff --git a/arch/arm/mm/copypage-v4mc.c b/arch/arm/mm/copypage-v4mc.c index 598c51a..b806151 100644 --- a/arch/arm/mm/copypage-v4mc.c +++ b/arch/arm/mm/copypage-v4mc.c @@ -73,7 +73,7 @@ void v4_mc_copy_user_highpage(struct page *to, struct page *from, { void *kto = kmap_atomic(to, KM_USER1); - if (test_and_clear_bit(PG_dcache_dirty, &from->flags)) + if (!test_and_set_bit(PG_dcache_clean, &from->flags)) __flush_dcache_page(page_mapping(from), from); spin_lock(&minicache_lock); diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c index f55fa10..bdba6c6 100644 --- a/arch/arm/mm/copypage-v6.c +++ b/arch/arm/mm/copypage-v6.c @@ -79,7 +79,7 @@ static void v6_copy_user_highpage_aliasing(struct page *to, unsigned int offset = CACHE_COLOUR(vaddr); unsigned long kfrom, kto; - if (test_and_clear_bit(PG_dcache_dirty, &from->flags)) + if (!test_and_set_bit(PG_dcache_clean, &from->flags)) __flush_dcache_page(page_mapping(from), from); /* FIXME: not highmem safe */ diff --git a/arch/arm/mm/copypage-xscale.c b/arch/arm/mm/copypage-xscale.c index 9920c0a..649bbcd 100644 --- a/arch/arm/mm/copypage-xscale.c +++ b/arch/arm/mm/copypage-xscale.c @@ -95,7 +95,7 @@ void xscale_mc_copy_user_highpage(struct page *to, struct page *from, { void *kto = kmap_atomic(to, KM_USER1); - if (test_and_clear_bit(PG_dcache_dirty, &from->flags)) + if (!test_and_set_bit(PG_dcache_clean, &from->flags)) __flush_dcache_page(page_mapping(from), from); spin_lock(&minicache_lock); diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index c704eed..ba93c49 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -521,6 +521,12 @@ void ___dma_page_dev_to_cpu(struct page *page, unsigned long off, outer_inv_range(paddr, paddr + size); dma_cache_maint_page(page, off, size, dir, dmac_unmap_area); + + /* + * Mark the D-cache clean for this page to avoid extra flushing. + */ + if (dir != DMA_TO_DEVICE && off == 0 && size >= PAGE_SIZE) + set_bit(PG_dcache_clean, &page->flags); } EXPORT_SYMBOL(___dma_page_dev_to_cpu); diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index 9b906de..58846cb 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -141,7 +141,7 @@ make_coherent(struct address_space *mapping, struct vm_area_struct *vma, * a page table, or changing an existing PTE. Basically, there are two * things that we need to take care of: * - * 1. If PG_dcache_dirty is set for the page, we need to ensure + * 1. If PG_dcache_clean is not set for the page, we need to ensure * that any cache entries for the kernels virtual memory * range are written back to the page. * 2. If we have multiple shared mappings of the same space in @@ -169,7 +169,7 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, mapping = page_mapping(page); #ifndef CONFIG_SMP - if (test_and_clear_bit(PG_dcache_dirty, &page->flags)) + if (!test_and_set_bit(PG_dcache_clean, &page->flags)) __flush_dcache_page(mapping, page); #endif if (mapping) { diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 87dd5ff..b4efce9 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -248,7 +248,7 @@ void flush_dcache_page(struct page *page) #ifndef CONFIG_SMP if (mapping && !mapping_mapped(mapping)) - set_bit(PG_dcache_dirty, &page->flags); + clear_bit(PG_dcache_clean, &page->flags); else #endif { @@ -257,6 +257,7 @@ void flush_dcache_page(struct page *page) __flush_dcache_aliases(mapping, page); else if (mapping) __flush_icache_all(); + set_bit(PG_dcache_clean, &page->flags); } } EXPORT_SYMBOL(flush_dcache_page); ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 3/9] ARM: Introduce __sync_icache_dcache() for VIPT caches 2010-08-31 13:57 [PATCH 0/9] Various patches for 2.6.37-rc1 Catalin Marinas 2010-08-31 13:58 ` [PATCH 1/9] ARM: Allow lazy cache flushing via PG_arch_1 for highmem pages Catalin Marinas 2010-08-31 13:58 ` [PATCH 2/9] ARM: Assume new page cache pages have dirty D-cache Catalin Marinas @ 2010-08-31 13:58 ` Catalin Marinas 2010-08-31 13:58 ` [PATCH 4/9] ARM: Use lazy cache flushing on ARMv7 SMP systems Catalin Marinas ` (5 subsequent siblings) 8 siblings, 0 replies; 19+ messages in thread From: Catalin Marinas @ 2010-08-31 13:58 UTC (permalink / raw) To: linux-arm-kernel On SMP systems, there is a small chance of a PTE becoming visible to a different CPU before the current cache maintenance operations in update_mmu_cache(). To avoid this, cache maintenance must be handled in set_pte_at() (similar to IA-64 and PowerPC). This patch provides a unified VIPT cache handling mechanism and implements the __sync_icache_dcache() function for ARMv6 onwards architectures. It is called from set_pte_at() and replaces the update_mmu_cache(). The latter is still used on VIVT hardware where a vm_area_struct is required. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Tested-by: Rabin Vincent <rabin.vincent@stericsson.com> Cc: Nicolas Pitre <nicolas.pitre@linaro.org> --- arch/arm/include/asm/pgtable.h | 26 +++++++++++++++++++++++--- arch/arm/include/asm/tlbflush.h | 10 +++++++++- arch/arm/mm/fault-armv.c | 4 ++-- arch/arm/mm/flush.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h index ab68cf1..42e694f 100644 --- a/arch/arm/include/asm/pgtable.h +++ b/arch/arm/include/asm/pgtable.h @@ -278,9 +278,24 @@ extern struct page *empty_zero_page; #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext) -#define set_pte_at(mm,addr,ptep,pteval) do { \ - set_pte_ext(ptep, pteval, (addr) >= TASK_SIZE ? 0 : PTE_EXT_NG); \ - } while (0) +#if __LINUX_ARM_ARCH__ < 6 +static inline void __sync_icache_dcache(pte_t pteval) +{ +} +#else +extern void __sync_icache_dcache(pte_t pteval); +#endif + +static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval) +{ + if (addr >= TASK_SIZE) + set_pte_ext(ptep, pteval, 0); + else { + __sync_icache_dcache(pteval); + set_pte_ext(ptep, pteval, PTE_EXT_NG); + } +} /* * The following only work if pte_present() is true. @@ -290,8 +305,13 @@ extern struct page *empty_zero_page; #define pte_write(pte) (pte_val(pte) & L_PTE_WRITE) #define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY) #define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG) +#define pte_exec(pte) (pte_val(pte) & L_PTE_EXEC) #define pte_special(pte) (0) +#define pte_present_user(pte) \ + ((pte_val(pte) & (L_PTE_PRESENT | L_PTE_USER)) == \ + (L_PTE_PRESENT | L_PTE_USER)) + #define PTE_BIT_FUNC(fn,op) \ static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; } diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h index 9ad329a..989c9e5 100644 --- a/arch/arm/include/asm/tlbflush.h +++ b/arch/arm/include/asm/tlbflush.h @@ -562,10 +562,18 @@ extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); /* * If PG_dcache_clean is not set for the page, we need to ensure that any * cache entries for the kernels virtual memory range are written - * back to the page. + * back to the page. On ARMv6 and later, the cache coherency is handled via + * the set_pte_at() function. */ +#if __LINUX_ARM_ARCH__ < 6 extern void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep); +#else +static inline void update_mmu_cache(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep) +{ +} +#endif #endif diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index 58846cb..8440d95 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -28,6 +28,7 @@ static unsigned long shared_pte_mask = L_PTE_MT_BUFFERABLE; +#if __LINUX_ARM_ARCH__ < 6 /* * We take the easy way out of this problem - we make the * PTE uncacheable. However, we leave the write buffer on. @@ -168,10 +169,8 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, return; mapping = page_mapping(page); -#ifndef CONFIG_SMP if (!test_and_set_bit(PG_dcache_clean, &page->flags)) __flush_dcache_page(mapping, page); -#endif if (mapping) { if (cache_is_vivt()) make_coherent(mapping, vma, addr, ptep, pfn); @@ -179,6 +178,7 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, __flush_icache_all(); } } +#endif /* __LINUX_ARM_ARCH__ < 6 */ /* * Check whether the write buffer has physical address aliasing diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index b4efce9..dd5b012 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -215,6 +215,36 @@ static void __flush_dcache_aliases(struct address_space *mapping, struct page *p flush_dcache_mmap_unlock(mapping); } +#if __LINUX_ARM_ARCH__ >= 6 +void __sync_icache_dcache(pte_t pteval) +{ + unsigned long pfn; + struct page *page; + struct address_space *mapping; + + if (!pte_present_user(pteval)) + return; + if (cache_is_vipt_nonaliasing() && !pte_exec(pteval)) + /* only flush non-aliasing VIPT caches for exec mappings */ + return; + pfn = pte_pfn(pteval); + if (!pfn_valid(pfn)) + return; + + page = pfn_to_page(pfn); + if (cache_is_vipt_aliasing()) + mapping = page_mapping(page); + else + mapping = NULL; + + if (!test_and_set_bit(PG_dcache_clean, &page->flags)) + __flush_dcache_page(mapping, page); + /* pte_exec() already checked above for non-aliasing VIPT cache */ + if (cache_is_vipt_nonaliasing() || pte_exec(pteval)) + __flush_icache_all(); +} +#endif + /* * Ensure cache coherency between kernel mapping and userspace mapping * of this page. ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 4/9] ARM: Use lazy cache flushing on ARMv7 SMP systems 2010-08-31 13:57 [PATCH 0/9] Various patches for 2.6.37-rc1 Catalin Marinas ` (2 preceding siblings ...) 2010-08-31 13:58 ` [PATCH 3/9] ARM: Introduce __sync_icache_dcache() for VIPT caches Catalin Marinas @ 2010-08-31 13:58 ` Catalin Marinas 2010-08-31 13:58 ` [PATCH 5/9] ARM: Remove superfluous flush_kernel_dcache_page() Catalin Marinas ` (4 subsequent siblings) 8 siblings, 0 replies; 19+ messages in thread From: Catalin Marinas @ 2010-08-31 13:58 UTC (permalink / raw) To: linux-arm-kernel ARMv7 processors like Cortex-A9 broadcast the cache maintenance operations in hardware. This patch allows the flush_dcache_page/update_mmu_cache pair to work in lazy flushing mode similar to the UP case. Note that cache flushing on SMP systems now takes place via the set_pte_at() call (__sync_icache_dcache) and there is no race with other CPUs executing code from the new PTE before the cache flushing took place. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Tested-by: Rabin Vincent <rabin.vincent@stericsson.com> Cc: Nicolas Pitre <nicolas.pitre@linaro.org> --- arch/arm/include/asm/smp_plat.h | 4 ++++ arch/arm/mm/flush.c | 13 ++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/arch/arm/include/asm/smp_plat.h b/arch/arm/include/asm/smp_plat.h index e621530..963a338 100644 --- a/arch/arm/include/asm/smp_plat.h +++ b/arch/arm/include/asm/smp_plat.h @@ -13,9 +13,13 @@ static inline int tlb_ops_need_broadcast(void) return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; } +#if !defined(CONFIG_SMP) || __LINUX_ARM_ARCH__ >= 7 +#define cache_ops_need_broadcast() 0 +#else static inline int cache_ops_need_broadcast(void) { return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 1; } +#endif #endif diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index dd5b012..2332b77 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -17,6 +17,7 @@ #include <asm/smp_plat.h> #include <asm/system.h> #include <asm/tlbflush.h> +#include <asm/smp_plat.h> #include "mm.h" @@ -93,12 +94,10 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig #define flush_pfn_alias(pfn,vaddr) do { } while (0) #endif -#ifdef CONFIG_SMP static void flush_ptrace_access_other(void *args) { __flush_icache_all(); } -#endif static void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, @@ -122,11 +121,9 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, if (vma->vm_flags & VM_EXEC) { unsigned long addr = (unsigned long)kaddr; __cpuc_coherent_kern_range(addr, addr + len); -#ifdef CONFIG_SMP if (cache_ops_need_broadcast()) smp_call_function(flush_ptrace_access_other, NULL, 1); -#endif } } @@ -276,12 +273,10 @@ void flush_dcache_page(struct page *page) mapping = page_mapping(page); -#ifndef CONFIG_SMP - if (mapping && !mapping_mapped(mapping)) + if (!cache_ops_need_broadcast() && + mapping && !mapping_mapped(mapping)) clear_bit(PG_dcache_clean, &page->flags); - else -#endif - { + else { __flush_dcache_page(mapping, page); if (mapping && cache_is_vivt()) __flush_dcache_aliases(mapping, page); ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 5/9] ARM: Remove superfluous flush_kernel_dcache_page() 2010-08-31 13:57 [PATCH 0/9] Various patches for 2.6.37-rc1 Catalin Marinas ` (3 preceding siblings ...) 2010-08-31 13:58 ` [PATCH 4/9] ARM: Use lazy cache flushing on ARMv7 SMP systems Catalin Marinas @ 2010-08-31 13:58 ` Catalin Marinas 2010-08-31 13:58 ` [PATCH 6/9] ARM: Implement phys_mem_access_prot() to avoid attributes aliasing Catalin Marinas ` (3 subsequent siblings) 8 siblings, 0 replies; 19+ messages in thread From: Catalin Marinas @ 2010-08-31 13:58 UTC (permalink / raw) To: linux-arm-kernel Since page cache pages are now considered 'dirty' by default, the cache flushing is handled via __flush_dcache_page() when a page gets mapped to user space. Highmem pages on VIVT systems are flushed during kunmap() and flush_kernel_dcache_page() was already a no-op in this case. ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE is still defined since ARM needs specific implementations for flush_kernel_vmap_range() and invalidate_kernel_vmap_range(). Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Cc: Nicolas Pitre <nicolas.pitre@linaro.org> --- arch/arm/include/asm/cacheflush.h | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index d3730f0..042e139 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -405,9 +405,6 @@ static inline void flush_anon_page(struct vm_area_struct *vma, #define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE static inline void flush_kernel_dcache_page(struct page *page) { - /* highmem pages are always flushed upon kunmap already */ - if ((cache_is_vivt() || cache_is_vipt_aliasing()) && !PageHighMem(page)) - __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE); } #define flush_dcache_mmap_lock(mapping) \ ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 6/9] ARM: Implement phys_mem_access_prot() to avoid attributes aliasing 2010-08-31 13:57 [PATCH 0/9] Various patches for 2.6.37-rc1 Catalin Marinas ` (4 preceding siblings ...) 2010-08-31 13:58 ` [PATCH 5/9] ARM: Remove superfluous flush_kernel_dcache_page() Catalin Marinas @ 2010-08-31 13:58 ` Catalin Marinas 2010-08-31 13:58 ` [PATCH 7/9] ARM: Improve the L2 cache performance when PL310 is used Catalin Marinas ` (2 subsequent siblings) 8 siblings, 0 replies; 19+ messages in thread From: Catalin Marinas @ 2010-08-31 13:58 UTC (permalink / raw) To: linux-arm-kernel ARMv7 onwards requires that there are no aliases to the same physical location using different memory types (i.e. Normal vs Strongly Ordered). Access to SO mappings when the unaligned accesses are handled in hardware is also Unpredictable (pgprot_noncached() mappings in user space). The /dev/mem driver requires uncached mappings with O_SYNC. The patch implements the phys_mem_access_prot() function which generates Strongly Ordered memory attributes if !pfn_valid() (independent of O_SYNC) and Normal Noncacheable (writecombine) if O_SYNC. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> --- arch/arm/include/asm/pgtable.h | 3 +++ arch/arm/mm/mmu.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 0 deletions(-) diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h index 42e694f..ea19775 100644 --- a/arch/arm/include/asm/pgtable.h +++ b/arch/arm/include/asm/pgtable.h @@ -337,6 +337,9 @@ static inline pte_t pte_mkspecial(pte_t pte) { return pte; } #ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE #define pgprot_dmacoherent(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK|L_PTE_EXEC, L_PTE_MT_BUFFERABLE) +#define __HAVE_PHYS_MEM_ACCESS_PROT +extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, + unsigned long size, pgprot_t vma_prot); #else #define pgprot_dmacoherent(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK|L_PTE_EXEC, L_PTE_MT_UNCACHED) diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 6e1c4f6..a486bd0 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -15,6 +15,7 @@ #include <linux/nodemask.h> #include <linux/memblock.h> #include <linux/sort.h> +#include <linux/fs.h> #include <asm/cputype.h> #include <asm/sections.h> @@ -498,6 +499,19 @@ static void __init build_mem_type_table(void) } } +#ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE +pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, + unsigned long size, pgprot_t vma_prot) +{ + if (!pfn_valid(pfn)) + return pgprot_noncached(vma_prot); + else if (file->f_flags & O_SYNC) + return pgprot_writecombine(vma_prot); + return vma_prot; +} +EXPORT_SYMBOL(phys_mem_access_prot); +#endif + #define vectors_base() (vectors_high() ? 0xffff0000 : 0) static void __init *early_alloc(unsigned long sz) ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 7/9] ARM: Improve the L2 cache performance when PL310 is used 2010-08-31 13:57 [PATCH 0/9] Various patches for 2.6.37-rc1 Catalin Marinas ` (5 preceding siblings ...) 2010-08-31 13:58 ` [PATCH 6/9] ARM: Implement phys_mem_access_prot() to avoid attributes aliasing Catalin Marinas @ 2010-08-31 13:58 ` Catalin Marinas 2010-09-13 11:36 ` Russell King - ARM Linux 2010-08-31 13:58 ` [PATCH 8/9] ARM: Remove the domain switching on ARMv6k/v7 CPUs Catalin Marinas 2010-08-31 13:58 ` [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) Catalin Marinas 8 siblings, 1 reply; 19+ messages in thread From: Catalin Marinas @ 2010-08-31 13:58 UTC (permalink / raw) To: linux-arm-kernel With this L2 cache controller, the cache maintenance by PA and sync operations are atomic and do not require a "wait" loop. This patch conditionally defines the cache_wait() function. Since L2x0 cache controllers do not work with ARMv7 CPUs, the patch automatically enables CACHE_PL310 when only CPU_V7 is defined. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> --- arch/arm/mm/Kconfig | 8 ++++++++ arch/arm/mm/cache-l2x0.c | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index a0a2928..4414a01 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -779,6 +779,14 @@ config CACHE_L2X0 help This option enables the L2x0 PrimeCell. +config CACHE_PL310 + bool + depends on CACHE_L2X0 + default y if CPU_V7 && !CPU_V6 + help + This option enables optimisations for the PL310 cache + controller. + config CACHE_TAUROS2 bool "Enable the Tauros2 L2 cache controller" depends on (ARCH_DOVE || ARCH_MMP) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 9982eb3..edb43ff 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -29,13 +29,22 @@ static void __iomem *l2x0_base; static DEFINE_SPINLOCK(l2x0_lock); static uint32_t l2x0_way_mask; /* Bitmask of active ways */ -static inline void cache_wait(void __iomem *reg, unsigned long mask) +static inline void cache_wait_way(void __iomem *reg, unsigned long mask) { - /* wait for the operation to complete */ + /* wait for cache operation by line or way to complete */ while (readl_relaxed(reg) & mask) ; } +#ifdef CONFIG_CACHE_PL310 +static inline void cache_wait(void __iomem *reg, unsigned long mask) +{ + /* cache operations by line are atomic on PL310 */ +} +#else +#define cache_wait cache_wait_way +#endif + static inline void cache_sync(void) { void __iomem *base = l2x0_base; @@ -110,7 +119,7 @@ static inline void l2x0_inv_all(void) /* invalidate all ways */ spin_lock_irqsave(&l2x0_lock, flags); writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_INV_WAY); - cache_wait(l2x0_base + L2X0_INV_WAY, l2x0_way_mask); + cache_wait_way(l2x0_base + L2X0_INV_WAY, l2x0_way_mask); cache_sync(); spin_unlock_irqrestore(&l2x0_lock, flags); } ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 7/9] ARM: Improve the L2 cache performance when PL310 is used 2010-08-31 13:58 ` [PATCH 7/9] ARM: Improve the L2 cache performance when PL310 is used Catalin Marinas @ 2010-09-13 11:36 ` Russell King - ARM Linux 2010-09-13 11:57 ` Catalin Marinas 0 siblings, 1 reply; 19+ messages in thread From: Russell King - ARM Linux @ 2010-09-13 11:36 UTC (permalink / raw) To: linux-arm-kernel On Tue, Aug 31, 2010 at 02:58:41PM +0100, Catalin Marinas wrote: > With this L2 cache controller, the cache maintenance by PA and sync > operations are atomic and do not require a "wait" loop. This patch > conditionally defines the cache_wait() function. > > Since L2x0 cache controllers do not work with ARMv7 CPUs, the patch > automatically enables CACHE_PL310 when only CPU_V7 is defined. This should be a run-time test, as we're moving towards integrating ARMv6 and ARMv7 support into a single kernel image. ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 7/9] ARM: Improve the L2 cache performance when PL310 is used 2010-09-13 11:36 ` Russell King - ARM Linux @ 2010-09-13 11:57 ` Catalin Marinas 0 siblings, 0 replies; 19+ messages in thread From: Catalin Marinas @ 2010-09-13 11:57 UTC (permalink / raw) To: linux-arm-kernel On Mon, 2010-09-13 at 12:36 +0100, Russell King - ARM Linux wrote: > On Tue, Aug 31, 2010 at 02:58:41PM +0100, Catalin Marinas wrote: > > With this L2 cache controller, the cache maintenance by PA and sync > > operations are atomic and do not require a "wait" loop. This patch > > conditionally defines the cache_wait() function. > > > > Since L2x0 cache controllers do not work with ARMv7 CPUs, the patch > > automatically enables CACHE_PL310 when only CPU_V7 is defined. > > This should be a run-time test, as we're moving towards integrating > ARMv6 and ARMv7 support into a single kernel image. Do we expect to get the full ARMv7 performance when compiling a kernel for both ARMv6 and ARMv7? If not, we can force PL310 to be off in this configuration. With the compile-time option we get some macro optimisation but if we are to detect it we would need a function pointer for calling cache_wait(). -- Catalin ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 8/9] ARM: Remove the domain switching on ARMv6k/v7 CPUs 2010-08-31 13:57 [PATCH 0/9] Various patches for 2.6.37-rc1 Catalin Marinas ` (6 preceding siblings ...) 2010-08-31 13:58 ` [PATCH 7/9] ARM: Improve the L2 cache performance when PL310 is used Catalin Marinas @ 2010-08-31 13:58 ` Catalin Marinas 2010-08-31 13:58 ` [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) Catalin Marinas 8 siblings, 0 replies; 19+ messages in thread From: Catalin Marinas @ 2010-08-31 13:58 UTC (permalink / raw) To: linux-arm-kernel This patch removes the domain switching functionality via the set_fs and __switch_to functions on cores that have a TLS register. Currently, the ioremap and vmalloc areas share the same level 1 page tables and therefore have the same domain (DOMAIN_KERNEL). When the kernel domain is modified from Client to Manager (via the __set_fs or in the __switch_to function), the XN (eXecute Never) bit is overridden and newer CPUs can speculatively prefetch the ioremap'ed memory. Linux performs the kernel domain switching to allow user-specific functions (copy_to/from_user, get/put_user etc.) to access kernel memory. In order for these functions to work with the kernel domain set to Client, the patch modifies the LDRT/STRT and related instructions to the LDR/STR ones. The user pages access rights are also modified for kernel read-only access rather than read/write so that the copy-on-write mechanism still works. CPU_USE_DOMAINS gets disabled only if the hardware has a TLS register (CPU_32v6K is defined) since writing the TLS value to the high vectors page isn't possible. The user addresses passed to the kernel are checked by the access_ok() function so that they do not point to the kernel space. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Tested-by: Anton Vorontsov <cbouatmailru@gmail.com> Cc: Tony Lindgren <tony@atomide.com> --- arch/arm/include/asm/assembler.h | 13 +++--- arch/arm/include/asm/domain.h | 31 +++++++++++++- arch/arm/include/asm/futex.h | 9 ++-- arch/arm/include/asm/traps.h | 2 + arch/arm/include/asm/uaccess.h | 16 ++++--- arch/arm/kernel/entry-armv.S | 4 +- arch/arm/kernel/fiq.c | 5 ++ arch/arm/kernel/traps.c | 14 +++++- arch/arm/lib/getuser.S | 13 +++--- arch/arm/lib/putuser.S | 29 +++++++------ arch/arm/lib/uaccess.S | 83 +++++++++++++++++++------------------- arch/arm/mm/Kconfig | 8 ++++ arch/arm/mm/mmu.c | 6 +-- arch/arm/mm/proc-macros.S | 7 +++ arch/arm/mm/proc-v7.S | 5 +- 15 files changed, 153 insertions(+), 92 deletions(-) diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index 6e8f05c..66db132 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -18,6 +18,7 @@ #endif #include <asm/ptrace.h> +#include <asm/domain.h> /* * Endian independent macros for shifting bytes within registers. @@ -183,12 +184,12 @@ */ #ifdef CONFIG_THUMB2_KERNEL - .macro usraccoff, instr, reg, ptr, inc, off, cond, abort + .macro usraccoff, instr, reg, ptr, inc, off, cond, abort, t=T() 9999: .if \inc == 1 - \instr\cond\()bt \reg, [\ptr, #\off] + \instr\cond\()b\()\t\().w \reg, [\ptr, #\off] .elseif \inc == 4 - \instr\cond\()t \reg, [\ptr, #\off] + \instr\cond\()\t\().w \reg, [\ptr, #\off] .else .error "Unsupported inc macro argument" .endif @@ -223,13 +224,13 @@ #else /* !CONFIG_THUMB2_KERNEL */ - .macro usracc, instr, reg, ptr, inc, cond, rept, abort + .macro usracc, instr, reg, ptr, inc, cond, rept, abort, t=T() .rept \rept 9999: .if \inc == 1 - \instr\cond\()bt \reg, [\ptr], #\inc + \instr\cond\()b\()\t \reg, [\ptr], #\inc .elseif \inc == 4 - \instr\cond\()t \reg, [\ptr], #\inc + \instr\cond\()\t \reg, [\ptr], #\inc .else .error "Unsupported inc macro argument" .endif diff --git a/arch/arm/include/asm/domain.h b/arch/arm/include/asm/domain.h index cc7ef40..af18cea 100644 --- a/arch/arm/include/asm/domain.h +++ b/arch/arm/include/asm/domain.h @@ -45,13 +45,17 @@ */ #define DOMAIN_NOACCESS 0 #define DOMAIN_CLIENT 1 +#ifdef CONFIG_CPU_USE_DOMAINS #define DOMAIN_MANAGER 3 +#else +#define DOMAIN_MANAGER 1 +#endif #define domain_val(dom,type) ((type) << (2*(dom))) #ifndef __ASSEMBLY__ -#ifdef CONFIG_MMU +#ifdef CONFIG_CPU_USE_DOMAINS #define set_domain(x) \ do { \ __asm__ __volatile__( \ @@ -74,5 +78,28 @@ #define modify_domain(dom,type) do { } while (0) #endif +/* + * Generate the T (user) versions of the LDR/STR and related + * instructions (inline assembly) + */ +#ifdef CONFIG_CPU_USE_DOMAINS +#define T(instr) #instr "t" +#else +#define T(instr) #instr #endif -#endif /* !__ASSEMBLY__ */ + +#else /* __ASSEMBLY__ */ + +/* + * Generate the T (user) versions of the LDR/STR and related + * instructions + */ +#ifdef CONFIG_CPU_USE_DOMAINS +#define T(instr) instr ## t +#else +#define T(instr) instr +#endif + +#endif /* __ASSEMBLY__ */ + +#endif /* !__ASM_PROC_DOMAIN_H */ diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index 540a044..b33fe70 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h @@ -13,12 +13,13 @@ #include <linux/preempt.h> #include <linux/uaccess.h> #include <asm/errno.h> +#include <asm/domain.h> #define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ __asm__ __volatile__( \ - "1: ldrt %1, [%2]\n" \ + "1: " T(ldr) " %1, [%2]\n" \ " " insn "\n" \ - "2: strt %0, [%2]\n" \ + "2: " T(str) " %0, [%2]\n" \ " mov %0, #0\n" \ "3:\n" \ " .pushsection __ex_table,\"a\"\n" \ @@ -97,10 +98,10 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) pagefault_disable(); /* implies preempt_disable() */ __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" - "1: ldrt %0, [%3]\n" + "1: " T(ldr) " %0, [%3]\n" " teq %0, %1\n" " it eq @ explicit IT needed for the 2b label\n" - "2: streqt %2, [%3]\n" + "2: " T(streq) " %2, [%3]\n" "3:\n" " .pushsection __ex_table,\"a\"\n" " .align 3\n" diff --git a/arch/arm/include/asm/traps.h b/arch/arm/include/asm/traps.h index 491960b..af5d5d1 100644 --- a/arch/arm/include/asm/traps.h +++ b/arch/arm/include/asm/traps.h @@ -27,4 +27,6 @@ static inline int in_exception_text(unsigned long ptr) extern void __init early_trap_init(void); extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame); +extern void *vectors_page; + #endif diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index 33e4a48..b293616 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -227,7 +227,7 @@ do { \ #define __get_user_asm_byte(x,addr,err) \ __asm__ __volatile__( \ - "1: ldrbt %1,[%2]\n" \ + "1: " T(ldrb) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -263,7 +263,7 @@ do { \ #define __get_user_asm_word(x,addr,err) \ __asm__ __volatile__( \ - "1: ldrt %1,[%2]\n" \ + "1: " T(ldr) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -308,7 +308,7 @@ do { \ #define __put_user_asm_byte(x,__pu_addr,err) \ __asm__ __volatile__( \ - "1: strbt %1,[%2]\n" \ + "1: " T(strb) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -341,7 +341,7 @@ do { \ #define __put_user_asm_word(x,__pu_addr,err) \ __asm__ __volatile__( \ - "1: strt %1,[%2]\n" \ + "1: " T(str) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -366,10 +366,10 @@ do { \ #define __put_user_asm_dword(x,__pu_addr,err) \ __asm__ __volatile__( \ - ARM( "1: strt " __reg_oper1 ", [%1], #4\n" ) \ - ARM( "2: strt " __reg_oper0 ", [%1]\n" ) \ - THUMB( "1: strt " __reg_oper1 ", [%1]\n" ) \ - THUMB( "2: strt " __reg_oper0 ", [%1, #4]\n" ) \ + ARM( "1: " T(str) " " __reg_oper1 ", [%1], #4\n" ) \ + ARM( "2: " T(str) " " __reg_oper0 ", [%1]\n" ) \ + THUMB( "1: " T(str) " " __reg_oper1 ", [%1]\n" ) \ + THUMB( "2: " T(str) " " __reg_oper0 ", [%1, #4]\n" ) \ "3:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index bb8e93a..4a8559f 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -733,7 +733,7 @@ ENTRY(__switch_to) THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 ) THUMB( str lr, [ip], #4 ) -#ifdef CONFIG_MMU +#ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif set_tls r3, r4, r5 @@ -742,7 +742,7 @@ ENTRY(__switch_to) ldr r8, =__stack_chk_guard ldr r7, [r7, #TSK_STACK_CANARY] #endif -#ifdef CONFIG_MMU +#ifdef CONFIG_CPU_USE_DOMAINS mcr p15, 0, r6, c3, c0, 0 @ Set domain register #endif mov r5, r0 diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c index 6ff7919..d601ef2 100644 --- a/arch/arm/kernel/fiq.c +++ b/arch/arm/kernel/fiq.c @@ -45,6 +45,7 @@ #include <asm/fiq.h> #include <asm/irq.h> #include <asm/system.h> +#include <asm/traps.h> static unsigned long no_fiq_insn; @@ -77,7 +78,11 @@ int show_fiq_list(struct seq_file *p, void *v) void set_fiq_handler(void *start, unsigned int length) { +#if defined(CONFIG_CPU_USE_DOMAINS) memcpy((void *)0xffff001c, start, length); +#else + memcpy(vectors_page + 0x1c, start, length); +#endif flush_icache_range(0xffff001c, 0xffff001c + length); if (!vectors_high()) flush_icache_range(0x1c, 0x1c + length); diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index cda78d5..87abca0 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -37,6 +37,8 @@ static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" }; +void *vectors_page; + #ifdef CONFIG_DEBUG_USER unsigned int user_debug; @@ -759,7 +761,11 @@ static void __init kuser_get_tls_init(unsigned long vectors) void __init early_trap_init(void) { +#if defined(CONFIG_CPU_USE_DOMAINS) unsigned long vectors = CONFIG_VECTORS_BASE; +#else + unsigned long vectors = (unsigned long)vectors_page; +#endif extern char __stubs_start[], __stubs_end[]; extern char __vectors_start[], __vectors_end[]; extern char __kuser_helper_start[], __kuser_helper_end[]; @@ -783,10 +789,10 @@ void __init early_trap_init(void) * Copy signal return handlers into the vector page, and * set sigreturn to be a pointer to these. */ - memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes, - sizeof(sigreturn_codes)); - memcpy((void *)KERN_RESTART_CODE, syscall_restart_code, - sizeof(syscall_restart_code)); + memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE), + sigreturn_codes, sizeof(sigreturn_codes)); + memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE), + syscall_restart_code, sizeof(syscall_restart_code)); flush_icache_range(vectors, vectors + PAGE_SIZE); modify_domain(DOMAIN_USER, DOMAIN_CLIENT); diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S index b1631a7..1b049cd 100644 --- a/arch/arm/lib/getuser.S +++ b/arch/arm/lib/getuser.S @@ -28,20 +28,21 @@ */ #include <linux/linkage.h> #include <asm/errno.h> +#include <asm/domain.h> ENTRY(__get_user_1) -1: ldrbt r2, [r0] +1: T(ldrb) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__get_user_1) ENTRY(__get_user_2) #ifdef CONFIG_THUMB2_KERNEL -2: ldrbt r2, [r0] -3: ldrbt r3, [r0, #1] +2: T(ldrb) r2, [r0] +3: T(ldrb) r3, [r0, #1] #else -2: ldrbt r2, [r0], #1 -3: ldrbt r3, [r0] +2: T(ldrb) r2, [r0], #1 +3: T(ldrb) r3, [r0] #endif #ifndef __ARMEB__ orr r2, r2, r3, lsl #8 @@ -53,7 +54,7 @@ ENTRY(__get_user_2) ENDPROC(__get_user_2) ENTRY(__get_user_4) -4: ldrt r2, [r0] +4: T(ldr) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__get_user_4) diff --git a/arch/arm/lib/putuser.S b/arch/arm/lib/putuser.S index 5a01a23..c023fc1 100644 --- a/arch/arm/lib/putuser.S +++ b/arch/arm/lib/putuser.S @@ -28,9 +28,10 @@ */ #include <linux/linkage.h> #include <asm/errno.h> +#include <asm/domain.h> ENTRY(__put_user_1) -1: strbt r2, [r0] +1: T(strb) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__put_user_1) @@ -39,19 +40,19 @@ ENTRY(__put_user_2) mov ip, r2, lsr #8 #ifdef CONFIG_THUMB2_KERNEL #ifndef __ARMEB__ -2: strbt r2, [r0] -3: strbt ip, [r0, #1] +2: T(strb) r2, [r0] +3: T(strb) ip, [r0, #1] #else -2: strbt ip, [r0] -3: strbt r2, [r0, #1] +2: T(strb) ip, [r0] +3: T(strb) r2, [r0, #1] #endif #else /* !CONFIG_THUMB2_KERNEL */ #ifndef __ARMEB__ -2: strbt r2, [r0], #1 -3: strbt ip, [r0] +2: T(strb) r2, [r0], #1 +3: T(strb) ip, [r0] #else -2: strbt ip, [r0], #1 -3: strbt r2, [r0] +2: T(strb) ip, [r0], #1 +3: T(strb) r2, [r0] #endif #endif /* CONFIG_THUMB2_KERNEL */ mov r0, #0 @@ -59,18 +60,18 @@ ENTRY(__put_user_2) ENDPROC(__put_user_2) ENTRY(__put_user_4) -4: strt r2, [r0] +4: T(str) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__put_user_4) ENTRY(__put_user_8) #ifdef CONFIG_THUMB2_KERNEL -5: strt r2, [r0] -6: strt r3, [r0, #4] +5: T(str) r2, [r0] +6: T(str) r3, [r0, #4] #else -5: strt r2, [r0], #4 -6: strt r3, [r0] +5: T(str) r2, [r0], #4 +6: T(str) r3, [r0] #endif mov r0, #0 mov pc, lr diff --git a/arch/arm/lib/uaccess.S b/arch/arm/lib/uaccess.S index fee9f6f..d0ece2a 100644 --- a/arch/arm/lib/uaccess.S +++ b/arch/arm/lib/uaccess.S @@ -14,6 +14,7 @@ #include <linux/linkage.h> #include <asm/assembler.h> #include <asm/errno.h> +#include <asm/domain.h> .text @@ -31,11 +32,11 @@ rsb ip, ip, #4 cmp ip, #2 ldrb r3, [r1], #1 -USER( strbt r3, [r0], #1) @ May fault +USER( T(strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( strgebt r3, [r0], #1) @ May fault +USER( T(strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #1 -USER( strgtbt r3, [r0], #1) @ May fault +USER( T(strgtb) r3, [r0], #1) @ May fault sub r2, r2, ip b .Lc2u_dest_aligned @@ -58,7 +59,7 @@ ENTRY(__copy_to_user) addmi ip, r2, #4 bmi .Lc2u_0nowords ldr r3, [r1], #4 -USER( strt r3, [r0], #4) @ May fault +USER( T(str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -87,18 +88,18 @@ USER( strt r3, [r0], #4) @ May fault stmneia r0!, {r3 - r4} @ Shouldnt fault tst ip, #4 ldrne r3, [r1], #4 - strnet r3, [r0], #4 @ Shouldnt fault + T(strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_0fupi .Lc2u_0nowords: teq ip, #0 beq .Lc2u_finished .Lc2u_nowords: cmp ip, #2 ldrb r3, [r1], #1 -USER( strbt r3, [r0], #1) @ May fault +USER( T(strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( strgebt r3, [r0], #1) @ May fault +USER( T(strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #1 -USER( strgtbt r3, [r0], #1) @ May fault +USER( T(strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_not_enough: @@ -119,7 +120,7 @@ USER( strgtbt r3, [r0], #1) @ May fault mov r3, r7, pull #8 ldr r7, [r1], #4 orr r3, r3, r7, push #24 -USER( strt r3, [r0], #4) @ May fault +USER( T(str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -154,18 +155,18 @@ USER( strt r3, [r0], #4) @ May fault movne r3, r7, pull #8 ldrne r7, [r1], #4 orrne r3, r3, r7, push #24 - strnet r3, [r0], #4 @ Shouldnt fault + T(strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_1fupi .Lc2u_1nowords: mov r3, r7, get_byte_1 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( strbt r3, [r0], #1) @ May fault +USER( T(strb) r3, [r0], #1) @ May fault movge r3, r7, get_byte_2 -USER( strgebt r3, [r0], #1) @ May fault +USER( T(strgeb) r3, [r0], #1) @ May fault movgt r3, r7, get_byte_3 -USER( strgtbt r3, [r0], #1) @ May fault +USER( T(strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_2fupi: subs r2, r2, #4 @@ -174,7 +175,7 @@ USER( strgtbt r3, [r0], #1) @ May fault mov r3, r7, pull #16 ldr r7, [r1], #4 orr r3, r3, r7, push #16 -USER( strt r3, [r0], #4) @ May fault +USER( T(str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -209,18 +210,18 @@ USER( strt r3, [r0], #4) @ May fault movne r3, r7, pull #16 ldrne r7, [r1], #4 orrne r3, r3, r7, push #16 - strnet r3, [r0], #4 @ Shouldnt fault + T(strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_2fupi .Lc2u_2nowords: mov r3, r7, get_byte_2 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( strbt r3, [r0], #1) @ May fault +USER( T(strb) r3, [r0], #1) @ May fault movge r3, r7, get_byte_3 -USER( strgebt r3, [r0], #1) @ May fault +USER( T(strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #0 -USER( strgtbt r3, [r0], #1) @ May fault +USER( T(strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_3fupi: subs r2, r2, #4 @@ -229,7 +230,7 @@ USER( strgtbt r3, [r0], #1) @ May fault mov r3, r7, pull #24 ldr r7, [r1], #4 orr r3, r3, r7, push #8 -USER( strt r3, [r0], #4) @ May fault +USER( T(str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -264,18 +265,18 @@ USER( strt r3, [r0], #4) @ May fault movne r3, r7, pull #24 ldrne r7, [r1], #4 orrne r3, r3, r7, push #8 - strnet r3, [r0], #4 @ Shouldnt fault + T(strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_3fupi .Lc2u_3nowords: mov r3, r7, get_byte_3 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( strbt r3, [r0], #1) @ May fault +USER( T(strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( strgebt r3, [r0], #1) @ May fault +USER( T(strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #0 -USER( strgtbt r3, [r0], #1) @ May fault +USER( T(strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished ENDPROC(__copy_to_user) @@ -294,11 +295,11 @@ ENDPROC(__copy_to_user) .Lcfu_dest_not_aligned: rsb ip, ip, #4 cmp ip, #2 -USER( ldrbt r3, [r1], #1) @ May fault +USER( T(ldrb) r3, [r1], #1) @ May fault strb r3, [r0], #1 -USER( ldrgebt r3, [r1], #1) @ May fault +USER( T(ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( ldrgtbt r3, [r1], #1) @ May fault +USER( T(ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 sub r2, r2, ip b .Lcfu_dest_aligned @@ -321,7 +322,7 @@ ENTRY(__copy_from_user) .Lcfu_0fupi: subs r2, r2, #4 addmi ip, r2, #4 bmi .Lcfu_0nowords -USER( ldrt r3, [r1], #4) +USER( T(ldr) r3, [r1], #4) str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction rsb ip, ip, #0 @@ -350,18 +351,18 @@ USER( ldrt r3, [r1], #4) ldmneia r1!, {r3 - r4} @ Shouldnt fault stmneia r0!, {r3 - r4} tst ip, #4 - ldrnet r3, [r1], #4 @ Shouldnt fault + T(ldrne) r3, [r1], #4 @ Shouldnt fault strne r3, [r0], #4 ands ip, ip, #3 beq .Lcfu_0fupi .Lcfu_0nowords: teq ip, #0 beq .Lcfu_finished .Lcfu_nowords: cmp ip, #2 -USER( ldrbt r3, [r1], #1) @ May fault +USER( T(ldrb) r3, [r1], #1) @ May fault strb r3, [r0], #1 -USER( ldrgebt r3, [r1], #1) @ May fault +USER( T(ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( ldrgtbt r3, [r1], #1) @ May fault +USER( T(ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished @@ -374,7 +375,7 @@ USER( ldrgtbt r3, [r1], #1) @ May fault .Lcfu_src_not_aligned: bic r1, r1, #3 -USER( ldrt r7, [r1], #4) @ May fault +USER( T(ldr) r7, [r1], #4) @ May fault cmp ip, #2 bgt .Lcfu_3fupi beq .Lcfu_2fupi @@ -382,7 +383,7 @@ USER( ldrt r7, [r1], #4) @ May fault addmi ip, r2, #4 bmi .Lcfu_1nowords mov r3, r7, pull #8 -USER( ldrt r7, [r1], #4) @ May fault +USER( T(ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #24 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -417,7 +418,7 @@ USER( ldrt r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #8 -USER( ldrnet r7, [r1], #4) @ May fault +USER( T(ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #24 strne r3, [r0], #4 ands ip, ip, #3 @@ -437,7 +438,7 @@ USER( ldrnet r7, [r1], #4) @ May fault addmi ip, r2, #4 bmi .Lcfu_2nowords mov r3, r7, pull #16 -USER( ldrt r7, [r1], #4) @ May fault +USER( T(ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #16 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -473,7 +474,7 @@ USER( ldrt r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #16 -USER( ldrnet r7, [r1], #4) @ May fault +USER( T(ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #16 strne r3, [r0], #4 ands ip, ip, #3 @@ -485,7 +486,7 @@ USER( ldrnet r7, [r1], #4) @ May fault strb r3, [r0], #1 movge r3, r7, get_byte_3 strgeb r3, [r0], #1 -USER( ldrgtbt r3, [r1], #0) @ May fault +USER( T(ldrgtb) r3, [r1], #0) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished @@ -493,7 +494,7 @@ USER( ldrgtbt r3, [r1], #0) @ May fault addmi ip, r2, #4 bmi .Lcfu_3nowords mov r3, r7, pull #24 -USER( ldrt r7, [r1], #4) @ May fault +USER( T(ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #8 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -528,7 +529,7 @@ USER( ldrt r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #24 -USER( ldrnet r7, [r1], #4) @ May fault +USER( T(ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #8 strne r3, [r0], #4 ands ip, ip, #3 @@ -538,9 +539,9 @@ USER( ldrnet r7, [r1], #4) @ May fault beq .Lcfu_finished cmp ip, #2 strb r3, [r0], #1 -USER( ldrgebt r3, [r1], #1) @ May fault +USER( T(ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( ldrgtbt r3, [r1], #1) @ May fault +USER( T(ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished ENDPROC(__copy_from_user) diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 4414a01..6d05f79 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -599,6 +599,14 @@ config CPU_CP15_MPU help Processor has the CP15 register, which has MPU related registers. +config CPU_USE_DOMAINS + bool + depends on MMU + default y if !CPU_32v6K + help + This option enables or disables the use of domain switching + via the set_fs() function. + # # CPU supports 36-bit I/O # diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index a486bd0..920fb3b 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -25,6 +25,7 @@ #include <asm/smp_plat.h> #include <asm/tlb.h> #include <asm/highmem.h> +#include <asm/traps.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> @@ -897,12 +898,11 @@ static void __init devicemaps_init(struct machine_desc *mdesc) { struct map_desc map; unsigned long addr; - void *vectors; /* * Allocate the vector page early. */ - vectors = early_alloc(PAGE_SIZE); + vectors_page = early_alloc(PAGE_SIZE); for (addr = VMALLOC_END; addr; addr += PGDIR_SIZE) pmd_clear(pmd_off_k(addr)); @@ -942,7 +942,7 @@ static void __init devicemaps_init(struct machine_desc *mdesc) * location (0xffff0000). If we aren't using high-vectors, also * create a mapping at the low-vectors virtual address. */ - map.pfn = __phys_to_pfn(virt_to_phys(vectors)); + map.pfn = __phys_to_pfn(virt_to_phys(vectors_page)); map.virtual = 0xffff0000; map.length = PAGE_SIZE; map.type = MT_HIGH_VECTORS; diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S index 7d63bea..337f102 100644 --- a/arch/arm/mm/proc-macros.S +++ b/arch/arm/mm/proc-macros.S @@ -99,6 +99,10 @@ * 110x 0 1 0 r/w r/o * 11x0 0 1 0 r/w r/o * 1111 0 1 1 r/w r/w + * + * If !CONFIG_CPU_USE_DOMAINS, the following permissions are changed: + * 110x 1 1 1 r/o r/o + * 11x0 1 1 1 r/o r/o */ .macro armv6_mt_table pfx \pfx\()_mt_table: @@ -138,8 +142,11 @@ tst r1, #L_PTE_USER orrne r3, r3, #PTE_EXT_AP1 +#ifdef CONFIG_CPU_USE_DOMAINS + @ allow kernel read/write access to read-only user pages tstne r3, #PTE_EXT_APX bicne r3, r3, #PTE_EXT_APX | PTE_EXT_AP0 +#endif tst r1, #L_PTE_EXEC orreq r3, r3, #PTE_EXT_XN diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 6a8506d..19638c6 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -149,8 +149,11 @@ ENTRY(cpu_v7_set_pte_ext) tst r1, #L_PTE_USER orrne r3, r3, #PTE_EXT_AP1 +#ifdef CONFIG_CPU_USE_DOMAINS + @ allow kernel read/write access to read-only user pages tstne r3, #PTE_EXT_APX bicne r3, r3, #PTE_EXT_APX | PTE_EXT_AP0 +#endif tst r1, #L_PTE_EXEC orreq r3, r3, #PTE_EXT_XN @@ -237,8 +240,6 @@ __v7_setup: mcr p15, 0, r10, c2, c0, 2 @ TTB control register orr r4, r4, #TTB_FLAGS mcr p15, 0, r4, c2, c0, 1 @ load TTB1 - mov r10, #0x1f @ domains 0, 1 = manager - mcr p15, 0, r10, c3, c0, 0 @ load domain access register /* * Memory region attributes with SCTLR.TRE=1 * ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) 2010-08-31 13:57 [PATCH 0/9] Various patches for 2.6.37-rc1 Catalin Marinas ` (7 preceding siblings ...) 2010-08-31 13:58 ` [PATCH 8/9] ARM: Remove the domain switching on ARMv6k/v7 CPUs Catalin Marinas @ 2010-08-31 13:58 ` Catalin Marinas 2010-08-31 15:05 ` Kirill A. Shutemov 2010-09-01 4:08 ` Olof Johansson 8 siblings, 2 replies; 19+ messages in thread From: Catalin Marinas @ 2010-08-31 13:58 UTC (permalink / raw) To: linux-arm-kernel From: Leif Lindholm <leif.lindholm@arm.com> The SWP instruction was deprecated in the ARMv6 architecture, superseded by the LDREX/STREX family of instructions for load-linked/store-conditional operations. The ARMv7 multiprocessing extensions mandate that SWP/SWPB instructions are treated as undefined from reset, with the ability to enable them through the System Control Register SW bit. This patch adds the alternative solution to emulate the SWP and SWPB instructions using LDREX/STREX sequences, and log statistics to /proc/cpu/swp_emulation. To correctly deal with copy-on-write, it also modifies cpu_v7_set_pte_ext to change the mappings to priviliged RO when user RO. Signed-off-by: Leif Lindholm <leif.lindholm@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> --- Documentation/arm/00-INDEX | 2 Documentation/arm/swp_emulation | 27 ++++ arch/arm/kernel/Makefile | 1 arch/arm/kernel/swp_emulate.c | 260 +++++++++++++++++++++++++++++++++++++++ arch/arm/mm/Kconfig | 27 ++++ arch/arm/mm/proc-v7.S | 4 + 6 files changed, 321 insertions(+), 0 deletions(-) create mode 100644 Documentation/arm/swp_emulation create mode 100644 arch/arm/kernel/swp_emulate.c diff --git a/Documentation/arm/00-INDEX b/Documentation/arm/00-INDEX index 7f5fc3b..2d02564 100644 --- a/Documentation/arm/00-INDEX +++ b/Documentation/arm/00-INDEX @@ -32,3 +32,5 @@ memory.txt - description of the virtual memory layout nwfpe/ - NWFPE floating point emulator documentation +swp_emulation + - SWP/SWPB emulation handler/logging description diff --git a/Documentation/arm/swp_emulation b/Documentation/arm/swp_emulation new file mode 100644 index 0000000..af903d2 --- /dev/null +++ b/Documentation/arm/swp_emulation @@ -0,0 +1,27 @@ +Software emulation of deprecated SWP instruction (CONFIG_SWP_EMULATE) +--------------------------------------------------------------------- + +ARMv6 architecture deprecates use of the SWP/SWPB instructions, and recommeds +moving to the load-locked/store-conditional instructions LDREX and STREX. + +ARMv7 multiprocessing extensions introduce the ability to disable these +instructions, triggering an undefined instruction exception when executed. +Trapped instructions are emulated using an LDREX/STREX or LDREXB/STREXB +sequence. If a memory access fault (an abort) occurs, a segmentation fault is +signalled to the triggering process. + +/proc/cpu/swp_emulation holds some statistics/information, including the PID of +the last process to trigger the emulation to be invocated. For example: +--- +Emulated SWP: 12 +Emulated SWPB: 0 +Aborted SWP{B}: 1 +Last process: 314 +--- + +NOTE: when accessing uncached shared regions, LDREX/STREX rely on an external +transaction monitoring block called a global monitor to maintain update +atomicity. If your system does not implement a global monitor, this option can +cause programs that perform SWP operations to uncached memory to deadlock, as +the STREX operation will always fail. + diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 980b78e..d3430ee 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_ARM_UNWIND) += unwind.o obj-$(CONFIG_HAVE_TCM) += tcm.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o +obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c new file mode 100644 index 0000000..c254d1d --- /dev/null +++ b/arch/arm/kernel/swp_emulate.c @@ -0,0 +1,260 @@ +/* + * linux/arch/arm/kernel/swp_emulate.c + * + * Copyright (C) 2009 ARM Limited + * __user_* functions adapted from include/asm/uaccess.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Implements emulation of the SWP/SWPB instructions using load-exclusive and + * store-exclusive for processors that have them disabled (or future ones that + * might not implement them). + * + * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>] + * Where: Rt = destination + * Rt2 = source + * Rn = address + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/proc_fs.h> +#include <linux/sched.h> +#include <linux/syscalls.h> + +#include <asm/traps.h> +#include <asm/uaccess.h> + +/* + * Error-checking SWP macros implemented using ldrex{b}/strex{b} + */ +#define __user_swpX_asm(data, addr, res, B) \ + __asm__ __volatile__( \ + " mov r3, %1\n" \ + "0: ldrex"B" %1, [%2]\n" \ + "1: strex"B" %0, r3, [%2]\n" \ + " cmp %0, #0\n" \ + " movne %0, %3\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + " .align 2\n" \ + "3: mov %0, %4\n" \ + " b 2b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 3\n" \ + " .long 0b, 3b\n" \ + " .long 1b, 3b\n" \ + " .previous" \ + : "=&r" (res), "+r" (data) \ + : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \ + : "cc", "r3") + +#define __user_swp_asm(data, addr, res) __user_swpX_asm(data, addr, res, "") +#define __user_swpb_asm(data, addr, res) __user_swpX_asm(data, addr, res, "b") + +/* + * Macros/defines for extracting register numbers from instruction. + */ +#define EXTRACT_REG_NUM(instruction, offset) \ + (((instruction) & (0xf << (offset))) >> (offset)) +#define RN_OFFSET 16 +#define RT_OFFSET 12 +#define RT2_OFFSET 0 +/* + * Bit 22 of the instruction encoding distinguishes between + * the SWP and SWPB variants (bit set means SWPB). + */ +#define TYPE_SWPB (1 << 22) + +static unsigned long long swpcounter; +static unsigned long long swpbcounter; +static unsigned long long abtcounter; +static long previous_pid; + +#ifdef CONFIG_PROC_FS +static int proc_read_status(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + char *p = page; + int len; + + p += sprintf(p, "Emulated SWP:\t\t%llu\n", swpcounter); + p += sprintf(p, "Emulated SWPB:\t\t%llu\n", swpbcounter); + p += sprintf(p, "Aborted SWP{B}:\t\t%llu\n", abtcounter); + if (previous_pid != 0) + p += sprintf(p, "Last process:\t\t%ld\n", previous_pid); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} +#endif + +/* + * Set up process info to signal segmentation fault - called on access error. + */ +static void set_segfault(struct pt_regs *regs, unsigned long addr) +{ + siginfo_t info; + + if (find_vma(current->mm, addr) == NULL) + info.si_code = SEGV_MAPERR; + else + info.si_code = SEGV_ACCERR; + + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_addr = (void *) instruction_pointer(regs); + + pr_debug("SWP{B} emulation: access caused memory abort!\n"); + arm_notify_die("Illegal memory access", regs, &info, 0, 0); + + abtcounter++; +} + +static int emulate_swpX(unsigned int address, unsigned int *data, + unsigned int type) +{ + unsigned int res = 0; + + if ((type != TYPE_SWPB) && (address & 0x3)) { + /* SWP to unaligned address not permitted */ + pr_debug("SWP instruction on unaligned pointer!\n"); + return -EFAULT; + } + + while (1) { + /* + * Barrier required between accessing protected resource and + * releasing a lock for it. Legacy code might not have done + * this, and we cannot determine that this is not the case + * being emulated, so insert always. + */ + smp_mb(); + + if (type == TYPE_SWPB) + __user_swpb_asm(*data, address, res); + else + __user_swp_asm(*data, address, res); + + if (likely(res != -EAGAIN) || signal_pending(current)) + break; + + cond_resched(); + } + + if (res == 0) { + /* + * Barrier also required between aquiring a lock for a + * protected resource and accessing the resource. Inserted for + * same reason as above. + */ + smp_mb(); + + if (type == TYPE_SWPB) + swpbcounter++; + else + swpcounter++; + } + + return res; +} + +/* + * swp_handler logs the id of calling process, dissects the instruction, sanity + * checks the memory location, calls emulate_swpX for the actual operation and + * deals with fixup/error handling before returning + */ +static int swp_handler(struct pt_regs *regs, unsigned int instr) +{ + unsigned int address, destreg, data, type; + unsigned int res = 0; + + if (current->pid != previous_pid) { + pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n", + current->comm, (unsigned long)current->pid); + previous_pid = current->pid; + } + + address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)]; + data = regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)]; + destreg = EXTRACT_REG_NUM(instr, RT_OFFSET); + + type = instr & TYPE_SWPB; + + pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n", + EXTRACT_REG_NUM(instr, RN_OFFSET), address, + destreg, EXTRACT_REG_NUM(instr, RT2_OFFSET), data); + + /* Check access in reasonable access range for both SWP and SWPB */ + if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) { + pr_debug("SWP{B} emulation: access to %p not allowed!\n", + (void *)address); + res = -EFAULT; + } else { + res = emulate_swpX(address, &data, type); + } + + if (res == 0) { + /* + * On successful emulation, revert the adjustment to the PC + * made in kernel/traps.c in order to resume execution at the + * instruction following the SWP{B}. + */ + regs->ARM_pc += 4; + regs->uregs[destreg] = data; + } else if (res == -EFAULT) { + /* + * Memory errors do not mean emulation failed. + * Set up signal info to return SEGV, then return OK + */ + set_segfault(regs, address); + } + + return 0; +} + +/* + * Only emulate SWP/SWPB executed in ARM state/User mode. + * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE. + */ +static struct undef_hook swp_hook = { + .instr_mask = 0x0fb00ff0, + .instr_val = 0x01000090, + .cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT, + .cpsr_val = USR_MODE, + .fn = swp_handler +}; + +/* + * Register handler and create status file in /proc/cpu + * Invoked as late_initcall, since not needed before init spawned. + */ +static int __init swp_emulation_init(void) +{ +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *res; + + res = create_proc_entry("cpu/swp_emulation", S_IRUGO, NULL); + + if (!res) + return -ENOMEM; + + res->read_proc = proc_read_status; +#endif /* CONFIG_PROC_FS */ + + printk(KERN_NOTICE "Registering SWP/SWPB emulation handler\n"); + register_undef_hook(&swp_hook); + + return 0; +} + +late_initcall(swp_emulation_init); diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 6d05f79..8493ed0 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -636,6 +636,33 @@ config ARM_THUMBEE Say Y here if you have a CPU with the ThumbEE extension and code to make use of it. Say N for code that can run on CPUs without ThumbEE. +config SWP_EMULATE + bool "Emulate SWP/SWPB instructions" + depends on CPU_V7 + select HAVE_PROC_CPU if PROC_FS + default y if SMP + help + ARMv6 architecture deprecates use of the SWP/SWPB instructions. + ARMv7 multiprocessing extensions introduce the ability to disable + these instructions, triggering an undefined instruction exception + when executed. Say Y here to enable software emulation of these + instructions for userspace (not kernel) using LDREX/STREX. + Also creates /proc/cpu/swp_emulation for statistics. + + In some older versions of glibc [<=2.8] SWP is used during futex + trylock() operations with the assumption that the code will not + be preempted. This invalid assumption may be more likely to fail + with SWP emulation enabled, leading to deadlock of the user + application. + + NOTE: when accessing uncached shared regions, LDREX/STREX rely + on an external transaction monitoring block called a global + monitor to maintain update atomicity. If your system does not + implement a global monitor, this option can cause programs that + perform SWP operations to uncached memory to deadlock. + + If unsure, say Y. + config CPU_BIG_ENDIAN bool "Build big-endian kernel" depends on ARCH_SUPPORTS_BIG_ENDIAN diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 19638c6..d5d9b58 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -278,6 +278,10 @@ __v7_setup: #ifdef CONFIG_CPU_ENDIAN_BE8 orr r6, r6, #1 << 25 @ big-endian page tables #endif +#ifdef CONFIG_SWP_EMULATE + orr r5, r5, #(1 << 10) @ set SW bit in "clear" + bic r6, r6, #(1 << 10) @ clear it in "mmuset" +#endif mrc p15, 0, r0, c1, c0, 0 @ read control register bic r0, r0, r5 @ clear bits them orr r0, r0, r6 @ set them ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) 2010-08-31 13:58 ` [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) Catalin Marinas @ 2010-08-31 15:05 ` Kirill A. Shutemov 2010-09-06 15:42 ` Catalin Marinas 2010-09-01 4:08 ` Olof Johansson 1 sibling, 1 reply; 19+ messages in thread From: Kirill A. Shutemov @ 2010-08-31 15:05 UTC (permalink / raw) To: linux-arm-kernel On Tue, Aug 31, 2010 at 02:58:56PM +0100, Catalin Marinas wrote: > From: Leif Lindholm <leif.lindholm@arm.com> > > The SWP instruction was deprecated in the ARMv6 architecture, superseded > by the LDREX/STREX family of instructions for > load-linked/store-conditional operations. The ARMv7 multiprocessing > extensions mandate that SWP/SWPB instructions are treated as undefined > from reset, with the ability to enable them through the System Control > Register SW bit. > > This patch adds the alternative solution to emulate the SWP and SWPB > instructions using LDREX/STREX sequences, and log statistics to > /proc/cpu/swp_emulation. To correctly deal with copy-on-write, it also > modifies cpu_v7_set_pte_ext to change the mappings to priviliged RO when > user RO. > > Signed-off-by: Leif Lindholm <leif.lindholm@arm.com> > Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Kirill A. Shutemov <kirill@shutemov.name> BTW, there is PERF_COUNT_SW_EMULATION_FAULT event in perf. It's good idea to trigger it from here. > --- > Documentation/arm/00-INDEX | 2 > Documentation/arm/swp_emulation | 27 ++++ > arch/arm/kernel/Makefile | 1 > arch/arm/kernel/swp_emulate.c | 260 +++++++++++++++++++++++++++++++++++++++ > arch/arm/mm/Kconfig | 27 ++++ > arch/arm/mm/proc-v7.S | 4 + > 6 files changed, 321 insertions(+), 0 deletions(-) > create mode 100644 Documentation/arm/swp_emulation > create mode 100644 arch/arm/kernel/swp_emulate.c > > diff --git a/Documentation/arm/00-INDEX b/Documentation/arm/00-INDEX > index 7f5fc3b..2d02564 100644 > --- a/Documentation/arm/00-INDEX > +++ b/Documentation/arm/00-INDEX > @@ -32,3 +32,5 @@ memory.txt > - description of the virtual memory layout > nwfpe/ > - NWFPE floating point emulator documentation > +swp_emulation > + - SWP/SWPB emulation handler/logging description > diff --git a/Documentation/arm/swp_emulation b/Documentation/arm/swp_emulation > new file mode 100644 > index 0000000..af903d2 > --- /dev/null > +++ b/Documentation/arm/swp_emulation > @@ -0,0 +1,27 @@ > +Software emulation of deprecated SWP instruction (CONFIG_SWP_EMULATE) > +--------------------------------------------------------------------- > + > +ARMv6 architecture deprecates use of the SWP/SWPB instructions, and recommeds > +moving to the load-locked/store-conditional instructions LDREX and STREX. > + > +ARMv7 multiprocessing extensions introduce the ability to disable these > +instructions, triggering an undefined instruction exception when executed. > +Trapped instructions are emulated using an LDREX/STREX or LDREXB/STREXB > +sequence. If a memory access fault (an abort) occurs, a segmentation fault is > +signalled to the triggering process. > + > +/proc/cpu/swp_emulation holds some statistics/information, including the PID of > +the last process to trigger the emulation to be invocated. For example: > +--- > +Emulated SWP: 12 > +Emulated SWPB: 0 > +Aborted SWP{B}: 1 > +Last process: 314 > +--- > + > +NOTE: when accessing uncached shared regions, LDREX/STREX rely on an external > +transaction monitoring block called a global monitor to maintain update > +atomicity. If your system does not implement a global monitor, this option can > +cause programs that perform SWP operations to uncached memory to deadlock, as > +the STREX operation will always fail. > + > diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile > index 980b78e..d3430ee 100644 > --- a/arch/arm/kernel/Makefile > +++ b/arch/arm/kernel/Makefile > @@ -42,6 +42,7 @@ obj-$(CONFIG_KGDB) += kgdb.o > obj-$(CONFIG_ARM_UNWIND) += unwind.o > obj-$(CONFIG_HAVE_TCM) += tcm.o > obj-$(CONFIG_CRASH_DUMP) += crash_dump.o > +obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o > > obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o > AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 > diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c > new file mode 100644 > index 0000000..c254d1d > --- /dev/null > +++ b/arch/arm/kernel/swp_emulate.c > @@ -0,0 +1,260 @@ > +/* > + * linux/arch/arm/kernel/swp_emulate.c > + * > + * Copyright (C) 2009 ARM Limited > + * __user_* functions adapted from include/asm/uaccess.h > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * Implements emulation of the SWP/SWPB instructions using load-exclusive and > + * store-exclusive for processors that have them disabled (or future ones that > + * might not implement them). > + * > + * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>] > + * Where: Rt = destination > + * Rt2 = source > + * Rn = address > + */ > + > +#include <linux/init.h> > +#include <linux/kernel.h> > +#include <linux/proc_fs.h> > +#include <linux/sched.h> > +#include <linux/syscalls.h> > + > +#include <asm/traps.h> > +#include <asm/uaccess.h> > + > +/* > + * Error-checking SWP macros implemented using ldrex{b}/strex{b} > + */ > +#define __user_swpX_asm(data, addr, res, B) \ > + __asm__ __volatile__( \ > + " mov r3, %1\n" \ > + "0: ldrex"B" %1, [%2]\n" \ > + "1: strex"B" %0, r3, [%2]\n" \ > + " cmp %0, #0\n" \ > + " movne %0, %3\n" \ > + "2:\n" \ > + " .section .fixup,\"ax\"\n" \ > + " .align 2\n" \ > + "3: mov %0, %4\n" \ > + " b 2b\n" \ > + " .previous\n" \ > + " .section __ex_table,\"a\"\n" \ > + " .align 3\n" \ > + " .long 0b, 3b\n" \ > + " .long 1b, 3b\n" \ > + " .previous" \ > + : "=&r" (res), "+r" (data) \ > + : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \ > + : "cc", "r3") > + > +#define __user_swp_asm(data, addr, res) __user_swpX_asm(data, addr, res, "") > +#define __user_swpb_asm(data, addr, res) __user_swpX_asm(data, addr, res, "b") > + > +/* > + * Macros/defines for extracting register numbers from instruction. > + */ > +#define EXTRACT_REG_NUM(instruction, offset) \ > + (((instruction) & (0xf << (offset))) >> (offset)) > +#define RN_OFFSET 16 > +#define RT_OFFSET 12 > +#define RT2_OFFSET 0 > +/* > + * Bit 22 of the instruction encoding distinguishes between > + * the SWP and SWPB variants (bit set means SWPB). > + */ > +#define TYPE_SWPB (1 << 22) > + > +static unsigned long long swpcounter; > +static unsigned long long swpbcounter; > +static unsigned long long abtcounter; > +static long previous_pid; > + > +#ifdef CONFIG_PROC_FS > +static int proc_read_status(char *page, char **start, off_t off, int count, > + int *eof, void *data) > +{ > + char *p = page; > + int len; > + > + p += sprintf(p, "Emulated SWP:\t\t%llu\n", swpcounter); > + p += sprintf(p, "Emulated SWPB:\t\t%llu\n", swpbcounter); > + p += sprintf(p, "Aborted SWP{B}:\t\t%llu\n", abtcounter); > + if (previous_pid != 0) > + p += sprintf(p, "Last process:\t\t%ld\n", previous_pid); > + > + len = (p - page) - off; > + if (len < 0) > + len = 0; > + > + *eof = (len <= count) ? 1 : 0; > + *start = page + off; > + > + return len; > +} > +#endif > + > +/* > + * Set up process info to signal segmentation fault - called on access error. > + */ > +static void set_segfault(struct pt_regs *regs, unsigned long addr) > +{ > + siginfo_t info; > + > + if (find_vma(current->mm, addr) == NULL) > + info.si_code = SEGV_MAPERR; > + else > + info.si_code = SEGV_ACCERR; > + > + info.si_signo = SIGSEGV; > + info.si_errno = 0; > + info.si_addr = (void *) instruction_pointer(regs); > + > + pr_debug("SWP{B} emulation: access caused memory abort!\n"); > + arm_notify_die("Illegal memory access", regs, &info, 0, 0); > + > + abtcounter++; > +} > + > +static int emulate_swpX(unsigned int address, unsigned int *data, > + unsigned int type) > +{ > + unsigned int res = 0; > + > + if ((type != TYPE_SWPB) && (address & 0x3)) { > + /* SWP to unaligned address not permitted */ > + pr_debug("SWP instruction on unaligned pointer!\n"); > + return -EFAULT; > + } > + > + while (1) { > + /* > + * Barrier required between accessing protected resource and > + * releasing a lock for it. Legacy code might not have done > + * this, and we cannot determine that this is not the case > + * being emulated, so insert always. > + */ > + smp_mb(); > + > + if (type == TYPE_SWPB) > + __user_swpb_asm(*data, address, res); > + else > + __user_swp_asm(*data, address, res); > + > + if (likely(res != -EAGAIN) || signal_pending(current)) > + break; > + > + cond_resched(); > + } > + > + if (res == 0) { > + /* > + * Barrier also required between aquiring a lock for a > + * protected resource and accessing the resource. Inserted for > + * same reason as above. > + */ > + smp_mb(); > + > + if (type == TYPE_SWPB) > + swpbcounter++; > + else > + swpcounter++; > + } > + > + return res; > +} > + > +/* > + * swp_handler logs the id of calling process, dissects the instruction, sanity > + * checks the memory location, calls emulate_swpX for the actual operation and > + * deals with fixup/error handling before returning > + */ > +static int swp_handler(struct pt_regs *regs, unsigned int instr) > +{ > + unsigned int address, destreg, data, type; > + unsigned int res = 0; > + > + if (current->pid != previous_pid) { > + pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n", > + current->comm, (unsigned long)current->pid); > + previous_pid = current->pid; > + } > + > + address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)]; > + data = regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)]; > + destreg = EXTRACT_REG_NUM(instr, RT_OFFSET); > + > + type = instr & TYPE_SWPB; > + > + pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n", > + EXTRACT_REG_NUM(instr, RN_OFFSET), address, > + destreg, EXTRACT_REG_NUM(instr, RT2_OFFSET), data); > + > + /* Check access in reasonable access range for both SWP and SWPB */ > + if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) { > + pr_debug("SWP{B} emulation: access to %p not allowed!\n", > + (void *)address); > + res = -EFAULT; > + } else { > + res = emulate_swpX(address, &data, type); > + } > + > + if (res == 0) { > + /* > + * On successful emulation, revert the adjustment to the PC > + * made in kernel/traps.c in order to resume execution at the > + * instruction following the SWP{B}. > + */ > + regs->ARM_pc += 4; > + regs->uregs[destreg] = data; > + } else if (res == -EFAULT) { > + /* > + * Memory errors do not mean emulation failed. > + * Set up signal info to return SEGV, then return OK > + */ > + set_segfault(regs, address); > + } > + > + return 0; > +} > + > +/* > + * Only emulate SWP/SWPB executed in ARM state/User mode. > + * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE. > + */ > +static struct undef_hook swp_hook = { > + .instr_mask = 0x0fb00ff0, > + .instr_val = 0x01000090, > + .cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT, > + .cpsr_val = USR_MODE, > + .fn = swp_handler > +}; > + > +/* > + * Register handler and create status file in /proc/cpu > + * Invoked as late_initcall, since not needed before init spawned. > + */ > +static int __init swp_emulation_init(void) > +{ > +#ifdef CONFIG_PROC_FS > + struct proc_dir_entry *res; > + > + res = create_proc_entry("cpu/swp_emulation", S_IRUGO, NULL); > + > + if (!res) > + return -ENOMEM; > + > + res->read_proc = proc_read_status; > +#endif /* CONFIG_PROC_FS */ > + > + printk(KERN_NOTICE "Registering SWP/SWPB emulation handler\n"); > + register_undef_hook(&swp_hook); > + > + return 0; > +} > + > +late_initcall(swp_emulation_init); > diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig > index 6d05f79..8493ed0 100644 > --- a/arch/arm/mm/Kconfig > +++ b/arch/arm/mm/Kconfig > @@ -636,6 +636,33 @@ config ARM_THUMBEE > Say Y here if you have a CPU with the ThumbEE extension and code to > make use of it. Say N for code that can run on CPUs without ThumbEE. > > +config SWP_EMULATE > + bool "Emulate SWP/SWPB instructions" > + depends on CPU_V7 > + select HAVE_PROC_CPU if PROC_FS > + default y if SMP > + help > + ARMv6 architecture deprecates use of the SWP/SWPB instructions. > + ARMv7 multiprocessing extensions introduce the ability to disable > + these instructions, triggering an undefined instruction exception > + when executed. Say Y here to enable software emulation of these > + instructions for userspace (not kernel) using LDREX/STREX. > + Also creates /proc/cpu/swp_emulation for statistics. > + > + In some older versions of glibc [<=2.8] SWP is used during futex > + trylock() operations with the assumption that the code will not > + be preempted. This invalid assumption may be more likely to fail > + with SWP emulation enabled, leading to deadlock of the user > + application. > + > + NOTE: when accessing uncached shared regions, LDREX/STREX rely > + on an external transaction monitoring block called a global > + monitor to maintain update atomicity. If your system does not > + implement a global monitor, this option can cause programs that > + perform SWP operations to uncached memory to deadlock. > + > + If unsure, say Y. > + > config CPU_BIG_ENDIAN > bool "Build big-endian kernel" > depends on ARCH_SUPPORTS_BIG_ENDIAN > diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S > index 19638c6..d5d9b58 100644 > --- a/arch/arm/mm/proc-v7.S > +++ b/arch/arm/mm/proc-v7.S > @@ -278,6 +278,10 @@ __v7_setup: > #ifdef CONFIG_CPU_ENDIAN_BE8 > orr r6, r6, #1 << 25 @ big-endian page tables > #endif > +#ifdef CONFIG_SWP_EMULATE > + orr r5, r5, #(1 << 10) @ set SW bit in "clear" > + bic r6, r6, #(1 << 10) @ clear it in "mmuset" > +#endif > mrc p15, 0, r0, c1, c0, 0 @ read control register > bic r0, r0, r5 @ clear bits them > orr r0, r0, r6 @ set them > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel -- Kirill A. Shutemov ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) 2010-08-31 15:05 ` Kirill A. Shutemov @ 2010-09-06 15:42 ` Catalin Marinas 2010-09-13 11:55 ` Russell King - ARM Linux 0 siblings, 1 reply; 19+ messages in thread From: Catalin Marinas @ 2010-09-06 15:42 UTC (permalink / raw) To: linux-arm-kernel On Tue, 2010-08-31 at 16:05 +0100, Kirill A. Shutemov wrote: > BTW, there is PERF_COUNT_SW_EMULATION_FAULT event in perf. It's good idea > to trigger it from here. Here's the patch updated with perf support (contributed by Will Deacon but I merged it into the existing patch for simplicity): ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) From: Leif Lindholm <leif.lindholm@arm.com> The SWP instruction was deprecated in the ARMv6 architecture, superseded by the LDREX/STREX family of instructions for load-linked/store-conditional operations. The ARMv7 multiprocessing extensions mandate that SWP/SWPB instructions are treated as undefined from reset, with the ability to enable them through the System Control Register SW bit. This patch adds the alternative solution to emulate the SWP and SWPB instructions using LDREX/STREX sequences, and log statistics to /proc/cpu/swp_emulation. To correctly deal with copy-on-write, it also modifies cpu_v7_set_pte_ext to change the mappings to priviliged RO when user RO. Signed-off-by: Leif Lindholm <leif.lindholm@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Kirill A. Shutemov <kirill@shutemov.name> --- Documentation/arm/00-INDEX | 2 Documentation/arm/swp_emulation | 27 ++++ arch/arm/kernel/Makefile | 1 arch/arm/kernel/swp_emulate.c | 263 +++++++++++++++++++++++++++++++++++++++ arch/arm/mm/Kconfig | 27 ++++ arch/arm/mm/proc-v7.S | 4 + 6 files changed, 324 insertions(+), 0 deletions(-) create mode 100644 Documentation/arm/swp_emulation create mode 100644 arch/arm/kernel/swp_emulate.c diff --git a/Documentation/arm/00-INDEX b/Documentation/arm/00-INDEX index 7f5fc3b..2d02564 100644 --- a/Documentation/arm/00-INDEX +++ b/Documentation/arm/00-INDEX @@ -32,3 +32,5 @@ memory.txt - description of the virtual memory layout nwfpe/ - NWFPE floating point emulator documentation +swp_emulation + - SWP/SWPB emulation handler/logging description diff --git a/Documentation/arm/swp_emulation b/Documentation/arm/swp_emulation new file mode 100644 index 0000000..af903d2 --- /dev/null +++ b/Documentation/arm/swp_emulation @@ -0,0 +1,27 @@ +Software emulation of deprecated SWP instruction (CONFIG_SWP_EMULATE) +--------------------------------------------------------------------- + +ARMv6 architecture deprecates use of the SWP/SWPB instructions, and recommeds +moving to the load-locked/store-conditional instructions LDREX and STREX. + +ARMv7 multiprocessing extensions introduce the ability to disable these +instructions, triggering an undefined instruction exception when executed. +Trapped instructions are emulated using an LDREX/STREX or LDREXB/STREXB +sequence. If a memory access fault (an abort) occurs, a segmentation fault is +signalled to the triggering process. + +/proc/cpu/swp_emulation holds some statistics/information, including the PID of +the last process to trigger the emulation to be invocated. For example: +--- +Emulated SWP: 12 +Emulated SWPB: 0 +Aborted SWP{B}: 1 +Last process: 314 +--- + +NOTE: when accessing uncached shared regions, LDREX/STREX rely on an external +transaction monitoring block called a global monitor to maintain update +atomicity. If your system does not implement a global monitor, this option can +cause programs that perform SWP operations to uncached memory to deadlock, as +the STREX operation will always fail. + diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 980b78e..d3430ee 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_ARM_UNWIND) += unwind.o obj-$(CONFIG_HAVE_TCM) += tcm.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o +obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c new file mode 100644 index 0000000..64eb231 --- /dev/null +++ b/arch/arm/kernel/swp_emulate.c @@ -0,0 +1,263 @@ +/* + * linux/arch/arm/kernel/swp_emulate.c + * + * Copyright (C) 2009 ARM Limited + * __user_* functions adapted from include/asm/uaccess.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Implements emulation of the SWP/SWPB instructions using load-exclusive and + * store-exclusive for processors that have them disabled (or future ones that + * might not implement them). + * + * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>] + * Where: Rt = destination + * Rt2 = source + * Rn = address + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/proc_fs.h> +#include <linux/sched.h> +#include <linux/syscalls.h> +#include <linux/perf_event.h> + +#include <asm/traps.h> +#include <asm/uaccess.h> + +/* + * Error-checking SWP macros implemented using ldrex{b}/strex{b} + */ +#define __user_swpX_asm(data, addr, res, B) \ + __asm__ __volatile__( \ + " mov r3, %1\n" \ + "0: ldrex"B" %1, [%2]\n" \ + "1: strex"B" %0, r3, [%2]\n" \ + " cmp %0, #0\n" \ + " movne %0, %3\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + " .align 2\n" \ + "3: mov %0, %4\n" \ + " b 2b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 3\n" \ + " .long 0b, 3b\n" \ + " .long 1b, 3b\n" \ + " .previous" \ + : "=&r" (res), "+r" (data) \ + : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \ + : "cc", "r3") + +#define __user_swp_asm(data, addr, res) __user_swpX_asm(data, addr, res, "") +#define __user_swpb_asm(data, addr, res) __user_swpX_asm(data, addr, res, "b") + +/* + * Macros/defines for extracting register numbers from instruction. + */ +#define EXTRACT_REG_NUM(instruction, offset) \ + (((instruction) & (0xf << (offset))) >> (offset)) +#define RN_OFFSET 16 +#define RT_OFFSET 12 +#define RT2_OFFSET 0 +/* + * Bit 22 of the instruction encoding distinguishes between + * the SWP and SWPB variants (bit set means SWPB). + */ +#define TYPE_SWPB (1 << 22) + +static unsigned long long swpcounter; +static unsigned long long swpbcounter; +static unsigned long long abtcounter; +static long previous_pid; + +#ifdef CONFIG_PROC_FS +static int proc_read_status(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + char *p = page; + int len; + + p += sprintf(p, "Emulated SWP:\t\t%llu\n", swpcounter); + p += sprintf(p, "Emulated SWPB:\t\t%llu\n", swpbcounter); + p += sprintf(p, "Aborted SWP{B}:\t\t%llu\n", abtcounter); + if (previous_pid != 0) + p += sprintf(p, "Last process:\t\t%ld\n", previous_pid); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} +#endif + +/* + * Set up process info to signal segmentation fault - called on access error. + */ +static void set_segfault(struct pt_regs *regs, unsigned long addr) +{ + siginfo_t info; + + if (find_vma(current->mm, addr) == NULL) + info.si_code = SEGV_MAPERR; + else + info.si_code = SEGV_ACCERR; + + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_addr = (void *) instruction_pointer(regs); + + pr_debug("SWP{B} emulation: access caused memory abort!\n"); + arm_notify_die("Illegal memory access", regs, &info, 0, 0); + + abtcounter++; +} + +static int emulate_swpX(unsigned int address, unsigned int *data, + unsigned int type) +{ + unsigned int res = 0; + + if ((type != TYPE_SWPB) && (address & 0x3)) { + /* SWP to unaligned address not permitted */ + pr_debug("SWP instruction on unaligned pointer!\n"); + return -EFAULT; + } + + while (1) { + /* + * Barrier required between accessing protected resource and + * releasing a lock for it. Legacy code might not have done + * this, and we cannot determine that this is not the case + * being emulated, so insert always. + */ + smp_mb(); + + if (type == TYPE_SWPB) + __user_swpb_asm(*data, address, res); + else + __user_swp_asm(*data, address, res); + + if (likely(res != -EAGAIN) || signal_pending(current)) + break; + + cond_resched(); + } + + if (res == 0) { + /* + * Barrier also required between aquiring a lock for a + * protected resource and accessing the resource. Inserted for + * same reason as above. + */ + smp_mb(); + + if (type == TYPE_SWPB) + swpbcounter++; + else + swpcounter++; + } + + return res; +} + +/* + * swp_handler logs the id of calling process, dissects the instruction, sanity + * checks the memory location, calls emulate_swpX for the actual operation and + * deals with fixup/error handling before returning + */ +static int swp_handler(struct pt_regs *regs, unsigned int instr) +{ + unsigned int address, destreg, data, type; + unsigned int res = 0; + + perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, regs->ARM_pc); + + if (current->pid != previous_pid) { + pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n", + current->comm, (unsigned long)current->pid); + previous_pid = current->pid; + } + + address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)]; + data = regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)]; + destreg = EXTRACT_REG_NUM(instr, RT_OFFSET); + + type = instr & TYPE_SWPB; + + pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n", + EXTRACT_REG_NUM(instr, RN_OFFSET), address, + destreg, EXTRACT_REG_NUM(instr, RT2_OFFSET), data); + + /* Check access in reasonable access range for both SWP and SWPB */ + if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) { + pr_debug("SWP{B} emulation: access to %p not allowed!\n", + (void *)address); + res = -EFAULT; + } else { + res = emulate_swpX(address, &data, type); + } + + if (res == 0) { + /* + * On successful emulation, revert the adjustment to the PC + * made in kernel/traps.c in order to resume execution at the + * instruction following the SWP{B}. + */ + regs->ARM_pc += 4; + regs->uregs[destreg] = data; + } else if (res == -EFAULT) { + /* + * Memory errors do not mean emulation failed. + * Set up signal info to return SEGV, then return OK + */ + set_segfault(regs, address); + } + + return 0; +} + +/* + * Only emulate SWP/SWPB executed in ARM state/User mode. + * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE. + */ +static struct undef_hook swp_hook = { + .instr_mask = 0x0fb00ff0, + .instr_val = 0x01000090, + .cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT, + .cpsr_val = USR_MODE, + .fn = swp_handler +}; + +/* + * Register handler and create status file in /proc/cpu + * Invoked as late_initcall, since not needed before init spawned. + */ +static int __init swp_emulation_init(void) +{ +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *res; + + res = create_proc_entry("cpu/swp_emulation", S_IRUGO, NULL); + + if (!res) + return -ENOMEM; + + res->read_proc = proc_read_status; +#endif /* CONFIG_PROC_FS */ + + printk(KERN_NOTICE "Registering SWP/SWPB emulation handler\n"); + register_undef_hook(&swp_hook); + + return 0; +} + +late_initcall(swp_emulation_init); diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 6d05f79..8493ed0 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -636,6 +636,33 @@ config ARM_THUMBEE Say Y here if you have a CPU with the ThumbEE extension and code to make use of it. Say N for code that can run on CPUs without ThumbEE. +config SWP_EMULATE + bool "Emulate SWP/SWPB instructions" + depends on CPU_V7 + select HAVE_PROC_CPU if PROC_FS + default y if SMP + help + ARMv6 architecture deprecates use of the SWP/SWPB instructions. + ARMv7 multiprocessing extensions introduce the ability to disable + these instructions, triggering an undefined instruction exception + when executed. Say Y here to enable software emulation of these + instructions for userspace (not kernel) using LDREX/STREX. + Also creates /proc/cpu/swp_emulation for statistics. + + In some older versions of glibc [<=2.8] SWP is used during futex + trylock() operations with the assumption that the code will not + be preempted. This invalid assumption may be more likely to fail + with SWP emulation enabled, leading to deadlock of the user + application. + + NOTE: when accessing uncached shared regions, LDREX/STREX rely + on an external transaction monitoring block called a global + monitor to maintain update atomicity. If your system does not + implement a global monitor, this option can cause programs that + perform SWP operations to uncached memory to deadlock. + + If unsure, say Y. + config CPU_BIG_ENDIAN bool "Build big-endian kernel" depends on ARCH_SUPPORTS_BIG_ENDIAN diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 19638c6..d5d9b58 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -278,6 +278,10 @@ __v7_setup: #ifdef CONFIG_CPU_ENDIAN_BE8 orr r6, r6, #1 << 25 @ big-endian page tables #endif +#ifdef CONFIG_SWP_EMULATE + orr r5, r5, #(1 << 10) @ set SW bit in "clear" + bic r6, r6, #(1 << 10) @ clear it in "mmuset" +#endif mrc p15, 0, r0, c1, c0, 0 @ read control register bic r0, r0, r5 @ clear bits them orr r0, r0, r6 @ set them -- Catalin ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) 2010-09-06 15:42 ` Catalin Marinas @ 2010-09-13 11:55 ` Russell King - ARM Linux 2010-09-14 13:50 ` Leif Lindholm 0 siblings, 1 reply; 19+ messages in thread From: Russell King - ARM Linux @ 2010-09-13 11:55 UTC (permalink / raw) To: linux-arm-kernel On Mon, Sep 06, 2010 at 04:42:55PM +0100, Catalin Marinas wrote: > +/* > + * Error-checking SWP macros implemented using ldrex{b}/strex{b} > + */ > +#define __user_swpX_asm(data, addr, res, B) \ > + __asm__ __volatile__( \ > + " mov r3, %1\n" \ > + "0: ldrex"B" %1, [%2]\n" \ > + "1: strex"B" %0, r3, [%2]\n" \ > + " cmp %0, #0\n" \ > + " movne %0, %3\n" \ > + "2:\n" \ > + " .section .fixup,\"ax\"\n" \ > + " .align 2\n" \ > + "3: mov %0, %4\n" \ > + " b 2b\n" \ > + " .previous\n" \ > + " .section __ex_table,\"a\"\n" \ > + " .align 3\n" \ > + " .long 0b, 3b\n" \ > + " .long 1b, 3b\n" \ > + " .previous" \ > + : "=&r" (res), "+r" (data) \ > + : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \ > + : "cc", "r3") This assembly needs to be cleaned up to avoid using a fixed register. r3 is the first register gcc choses to use when allocating registers, so to clobber it almost guarantees making gcc's job a little harder. Instead, do this: unsigned long temp; and in the asm() output line: : "=&r" (res), "+r" (data), "=&r" (temp) > +static unsigned long long swpcounter; > +static unsigned long long swpbcounter; > +static unsigned long long abtcounter; long long can't be atomically updated - and if you have over 4000000000 faults, do you really care at that point? > +static long previous_pid; There is a typedef for pids. Apart from that, the rest looks fine. ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) 2010-09-13 11:55 ` Russell King - ARM Linux @ 2010-09-14 13:50 ` Leif Lindholm 0 siblings, 0 replies; 19+ messages in thread From: Leif Lindholm @ 2010-09-14 13:50 UTC (permalink / raw) To: linux-arm-kernel Subject: [PATCH] ARM: Add SWP/SWPB emulation for ARMv7 processors (v6) The SWP instruction was deprecated in the ARMv6 architecture, superseded by the LDREX/STREX family of instructions for load-linked/store-conditional operations. The ARMv7 multiprocessing extensions mandate that SWP/SWPB instructions are treated as undefined from reset, with the ability to enable them through the System Control Register SW bit. This patch adds the alternative solution to emulate the SWP and SWPB instructions using LDREX/STREX sequences, and log statistics to /proc/cpu/swp_emulation. To correctly deal with copy-on-write, it also modifies cpu_v7_set_pte_ext to change the mappings to priviliged RO when user RO. Signed-off-by: Leif Lindholm <leif.lindholm@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Kirill A. Shutemov <kirill@shutemov.name> --- This version addresses the following comments from Russell: - Avoid using fixed register in inline assembly. - Use 32-bit counters for emulation statistics. - Used pid_t for pids. Documentation/arm/00-INDEX | 2 Documentation/arm/swp_emulation | 27 ++++ arch/arm/kernel/Makefile | 1 arch/arm/kernel/swp_emulate.c | 267 +++++++++++++++++++++++++++++++++++++++ arch/arm/mm/Kconfig | 27 ++++ arch/arm/mm/proc-v7.S | 4 + 6 files changed, 328 insertions(+), 0 deletions(-) create mode 100644 Documentation/arm/swp_emulation create mode 100644 arch/arm/kernel/swp_emulate.c diff --git a/Documentation/arm/00-INDEX b/Documentation/arm/00-INDEX index 7f5fc3b..2d02564 100644 --- a/Documentation/arm/00-INDEX +++ b/Documentation/arm/00-INDEX @@ -32,3 +32,5 @@ memory.txt - description of the virtual memory layout nwfpe/ - NWFPE floating point emulator documentation +swp_emulation + - SWP/SWPB emulation handler/logging description diff --git a/Documentation/arm/swp_emulation b/Documentation/arm/swp_emulation new file mode 100644 index 0000000..af903d2 --- /dev/null +++ b/Documentation/arm/swp_emulation @@ -0,0 +1,27 @@ +Software emulation of deprecated SWP instruction (CONFIG_SWP_EMULATE) +--------------------------------------------------------------------- + +ARMv6 architecture deprecates use of the SWP/SWPB instructions, and recommeds +moving to the load-locked/store-conditional instructions LDREX and STREX. + +ARMv7 multiprocessing extensions introduce the ability to disable these +instructions, triggering an undefined instruction exception when executed. +Trapped instructions are emulated using an LDREX/STREX or LDREXB/STREXB +sequence. If a memory access fault (an abort) occurs, a segmentation fault is +signalled to the triggering process. + +/proc/cpu/swp_emulation holds some statistics/information, including the PID of +the last process to trigger the emulation to be invocated. For example: +--- +Emulated SWP: 12 +Emulated SWPB: 0 +Aborted SWP{B}: 1 +Last process: 314 +--- + +NOTE: when accessing uncached shared regions, LDREX/STREX rely on an external +transaction monitoring block called a global monitor to maintain update +atomicity. If your system does not implement a global monitor, this option can +cause programs that perform SWP operations to uncached memory to deadlock, as +the STREX operation will always fail. + diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 980b78e..d3430ee 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_ARM_UNWIND) += unwind.o obj-$(CONFIG_HAVE_TCM) += tcm.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o +obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c new file mode 100644 index 0000000..7a57609 --- /dev/null +++ b/arch/arm/kernel/swp_emulate.c @@ -0,0 +1,267 @@ +/* + * linux/arch/arm/kernel/swp_emulate.c + * + * Copyright (C) 2009 ARM Limited + * __user_* functions adapted from include/asm/uaccess.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Implements emulation of the SWP/SWPB instructions using load-exclusive and + * store-exclusive for processors that have them disabled (or future ones that + * might not implement them). + * + * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>] + * Where: Rt = destination + * Rt2 = source + * Rn = address + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/proc_fs.h> +#include <linux/sched.h> +#include <linux/syscalls.h> +#include <linux/perf_event.h> + +#include <asm/traps.h> +#include <asm/uaccess.h> + +/* + * Error-checking SWP macros implemented using ldrex{b}/strex{b} + */ +#define __user_swpX_asm(data, addr, res, temp, B) \ + __asm__ __volatile__( \ + " mov %2, %1\n" \ + "0: ldrex"B" %1, [%3]\n" \ + "1: strex"B" %0, %2, [%3]\n" \ + " cmp %0, #0\n" \ + " movne %0, %4\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + " .align 2\n" \ + "3: mov %0, %5\n" \ + " b 2b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 3\n" \ + " .long 0b, 3b\n" \ + " .long 1b, 3b\n" \ + " .previous" \ + : "=&r" (res), "+r" (data), "=&r" (temp) \ + : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \ + : "cc", "memory") + +#define __user_swp_asm(data, addr, res, temp) \ + __user_swpX_asm(data, addr, res, temp, "") +#define __user_swpb_asm(data, addr, res, temp) \ + __user_swpX_asm(data, addr, res, temp, "b") + +/* + * Macros/defines for extracting register numbers from instruction. + */ +#define EXTRACT_REG_NUM(instruction, offset) \ + (((instruction) & (0xf << (offset))) >> (offset)) +#define RN_OFFSET 16 +#define RT_OFFSET 12 +#define RT2_OFFSET 0 +/* + * Bit 22 of the instruction encoding distinguishes between + * the SWP and SWPB variants (bit set means SWPB). + */ +#define TYPE_SWPB (1 << 22) + +static unsigned long swpcounter; +static unsigned long swpbcounter; +static unsigned long abtcounter; +static pid_t previous_pid; + +#ifdef CONFIG_PROC_FS +static int proc_read_status(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + char *p = page; + int len; + + p += sprintf(p, "Emulated SWP:\t\t%lu\n", swpcounter); + p += sprintf(p, "Emulated SWPB:\t\t%lu\n", swpbcounter); + p += sprintf(p, "Aborted SWP{B}:\t\t%lu\n", abtcounter); + if (previous_pid != 0) + p += sprintf(p, "Last process:\t\t%d\n", previous_pid); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} +#endif + +/* + * Set up process info to signal segmentation fault - called on access error. + */ +static void set_segfault(struct pt_regs *regs, unsigned long addr) +{ + siginfo_t info; + + if (find_vma(current->mm, addr) == NULL) + info.si_code = SEGV_MAPERR; + else + info.si_code = SEGV_ACCERR; + + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_addr = (void *) instruction_pointer(regs); + + pr_debug("SWP{B} emulation: access caused memory abort!\n"); + arm_notify_die("Illegal memory access", regs, &info, 0, 0); + + abtcounter++; +} + +static int emulate_swpX(unsigned int address, unsigned int *data, + unsigned int type) +{ + unsigned int res = 0; + + if ((type != TYPE_SWPB) && (address & 0x3)) { + /* SWP to unaligned address not permitted */ + pr_debug("SWP instruction on unaligned pointer!\n"); + return -EFAULT; + } + + while (1) { + unsigned long temp; + + /* + * Barrier required between accessing protected resource and + * releasing a lock for it. Legacy code might not have done + * this, and we cannot determine that this is not the case + * being emulated, so insert always. + */ + smp_mb(); + + if (type == TYPE_SWPB) + __user_swpb_asm(*data, address, res, temp); + else + __user_swp_asm(*data, address, res, temp); + + if (likely(res != -EAGAIN) || signal_pending(current)) + break; + + cond_resched(); + } + + if (res == 0) { + /* + * Barrier also required between aquiring a lock for a + * protected resource and accessing the resource. Inserted for + * same reason as above. + */ + smp_mb(); + + if (type == TYPE_SWPB) + swpbcounter++; + else + swpcounter++; + } + + return res; +} + +/* + * swp_handler logs the id of calling process, dissects the instruction, sanity + * checks the memory location, calls emulate_swpX for the actual operation and + * deals with fixup/error handling before returning + */ +static int swp_handler(struct pt_regs *regs, unsigned int instr) +{ + unsigned int address, destreg, data, type; + unsigned int res = 0; + + perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, regs->ARM_pc); + + if (current->pid != previous_pid) { + pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n", + current->comm, (unsigned long)current->pid); + previous_pid = current->pid; + } + + address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)]; + data = regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)]; + destreg = EXTRACT_REG_NUM(instr, RT_OFFSET); + + type = instr & TYPE_SWPB; + + pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n", + EXTRACT_REG_NUM(instr, RN_OFFSET), address, + destreg, EXTRACT_REG_NUM(instr, RT2_OFFSET), data); + + /* Check access in reasonable access range for both SWP and SWPB */ + if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) { + pr_debug("SWP{B} emulation: access to %p not allowed!\n", + (void *)address); + res = -EFAULT; + } else { + res = emulate_swpX(address, &data, type); + } + + if (res == 0) { + /* + * On successful emulation, revert the adjustment to the PC + * made in kernel/traps.c in order to resume execution at the + * instruction following the SWP{B}. + */ + regs->ARM_pc += 4; + regs->uregs[destreg] = data; + } else if (res == -EFAULT) { + /* + * Memory errors do not mean emulation failed. + * Set up signal info to return SEGV, then return OK + */ + set_segfault(regs, address); + } + + return 0; +} + +/* + * Only emulate SWP/SWPB executed in ARM state/User mode. + * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE. + */ +static struct undef_hook swp_hook = { + .instr_mask = 0x0fb00ff0, + .instr_val = 0x01000090, + .cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT, + .cpsr_val = USR_MODE, + .fn = swp_handler +}; + +/* + * Register handler and create status file in /proc/cpu + * Invoked as late_initcall, since not needed before init spawned. + */ +static int __init swp_emulation_init(void) +{ +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *res; + + res = create_proc_entry("cpu/swp_emulation", S_IRUGO, NULL); + + if (!res) + return -ENOMEM; + + res->read_proc = proc_read_status; +#endif /* CONFIG_PROC_FS */ + + printk(KERN_NOTICE "Registering SWP/SWPB emulation handler\n"); + register_undef_hook(&swp_hook); + + return 0; +} + +late_initcall(swp_emulation_init); diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 51d8610..517efa6 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -636,6 +636,33 @@ config ARM_THUMBEE Say Y here if you have a CPU with the ThumbEE extension and code to make use of it. Say N for code that can run on CPUs without ThumbEE. +config SWP_EMULATE + bool "Emulate SWP/SWPB instructions" + depends on CPU_V7 + select HAVE_PROC_CPU if PROC_FS + default y if SMP + help + ARMv6 architecture deprecates use of the SWP/SWPB instructions. + ARMv7 multiprocessing extensions introduce the ability to disable + these instructions, triggering an undefined instruction exception + when executed. Say Y here to enable software emulation of these + instructions for userspace (not kernel) using LDREX/STREX. + Also creates /proc/cpu/swp_emulation for statistics. + + In some older versions of glibc [<=2.8] SWP is used during futex + trylock() operations with the assumption that the code will not + be preempted. This invalid assumption may be more likely to fail + with SWP emulation enabled, leading to deadlock of the user + application. + + NOTE: when accessing uncached shared regions, LDREX/STREX rely + on an external transaction monitoring block called a global + monitor to maintain update atomicity. If your system does not + implement a global monitor, this option can cause programs that + perform SWP operations to uncached memory to deadlock. + + If unsure, say Y. + config CPU_BIG_ENDIAN bool "Build big-endian kernel" depends on ARCH_SUPPORTS_BIG_ENDIAN diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 19638c6..d5d9b58 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -278,6 +278,10 @@ __v7_setup: #ifdef CONFIG_CPU_ENDIAN_BE8 orr r6, r6, #1 << 25 @ big-endian page tables #endif +#ifdef CONFIG_SWP_EMULATE + orr r5, r5, #(1 << 10) @ set SW bit in "clear" + bic r6, r6, #(1 << 10) @ clear it in "mmuset" +#endif mrc p15, 0, r0, c1, c0, 0 @ read control register bic r0, r0, r5 @ clear bits them orr r0, r0, r6 @ set them ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) 2010-08-31 13:58 ` [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) Catalin Marinas 2010-08-31 15:05 ` Kirill A. Shutemov @ 2010-09-01 4:08 ` Olof Johansson 2010-09-02 17:04 ` Leif Lindholm 1 sibling, 1 reply; 19+ messages in thread From: Olof Johansson @ 2010-09-01 4:08 UTC (permalink / raw) To: linux-arm-kernel Hi, On Tue, Aug 31, 2010 at 02:58:56PM +0100, Catalin Marinas wrote: > From: Leif Lindholm <leif.lindholm@arm.com> > > The SWP instruction was deprecated in the ARMv6 architecture, superseded > by the LDREX/STREX family of instructions for > load-linked/store-conditional operations. The ARMv7 multiprocessing > extensions mandate that SWP/SWPB instructions are treated as undefined > from reset, with the ability to enable them through the System Control > Register SW bit. > > This patch adds the alternative solution to emulate the SWP and SWPB > instructions using LDREX/STREX sequences, and log statistics to > /proc/cpu/swp_emulation. To correctly deal with copy-on-write, it also Sounds like debugfs would be a better place to export this through? That's what PPC does. -Olof ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) 2010-09-01 4:08 ` Olof Johansson @ 2010-09-02 17:04 ` Leif Lindholm 2010-09-02 17:20 ` Olof Johansson 0 siblings, 1 reply; 19+ messages in thread From: Leif Lindholm @ 2010-09-02 17:04 UTC (permalink / raw) To: linux-arm-kernel > From: Olof Johansson [mailto:olof at lixom.net] > > > > This patch adds the alternative solution to emulate the SWP and SWPB > > instructions using LDREX/STREX sequences, and log statistics to > > /proc/cpu/swp_emulation. To correctly deal with copy-on-write, it > also > > Sounds like debugfs would be a better place to export this through? > That's what PPC does. It made sense to me to place it with /proc/cpu/alignment. Moving both of these into debugfs might well make sense in the future, but I think that should be a separate patch. / Leif ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) 2010-09-02 17:04 ` Leif Lindholm @ 2010-09-02 17:20 ` Olof Johansson 0 siblings, 0 replies; 19+ messages in thread From: Olof Johansson @ 2010-09-02 17:20 UTC (permalink / raw) To: linux-arm-kernel On Thu, Sep 02, 2010 at 06:04:24PM +0100, Leif Lindholm wrote: > > From: Olof Johansson [mailto:olof at lixom.net] > > > > > > This patch adds the alternative solution to emulate the SWP and SWPB > > > instructions using LDREX/STREX sequences, and log statistics to > > > /proc/cpu/swp_emulation. To correctly deal with copy-on-write, it > > also > > > > Sounds like debugfs would be a better place to export this through? > > That's what PPC does. > > It made sense to me to place it with /proc/cpu/alignment. > Moving both of these into debugfs might well make sense in the future, > but I think that should be a separate patch. Yep, works for me. -Olof ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2010-09-14 13:50 UTC | newest] Thread overview: 19+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2010-08-31 13:57 [PATCH 0/9] Various patches for 2.6.37-rc1 Catalin Marinas 2010-08-31 13:58 ` [PATCH 1/9] ARM: Allow lazy cache flushing via PG_arch_1 for highmem pages Catalin Marinas 2010-08-31 13:58 ` [PATCH 2/9] ARM: Assume new page cache pages have dirty D-cache Catalin Marinas 2010-08-31 13:58 ` [PATCH 3/9] ARM: Introduce __sync_icache_dcache() for VIPT caches Catalin Marinas 2010-08-31 13:58 ` [PATCH 4/9] ARM: Use lazy cache flushing on ARMv7 SMP systems Catalin Marinas 2010-08-31 13:58 ` [PATCH 5/9] ARM: Remove superfluous flush_kernel_dcache_page() Catalin Marinas 2010-08-31 13:58 ` [PATCH 6/9] ARM: Implement phys_mem_access_prot() to avoid attributes aliasing Catalin Marinas 2010-08-31 13:58 ` [PATCH 7/9] ARM: Improve the L2 cache performance when PL310 is used Catalin Marinas 2010-09-13 11:36 ` Russell King - ARM Linux 2010-09-13 11:57 ` Catalin Marinas 2010-08-31 13:58 ` [PATCH 8/9] ARM: Remove the domain switching on ARMv6k/v7 CPUs Catalin Marinas 2010-08-31 13:58 ` [PATCH 9/9] ARM: Add SWP/SWPB emulation for ARMv7 processors (v5) Catalin Marinas 2010-08-31 15:05 ` Kirill A. Shutemov 2010-09-06 15:42 ` Catalin Marinas 2010-09-13 11:55 ` Russell King - ARM Linux 2010-09-14 13:50 ` Leif Lindholm 2010-09-01 4:08 ` Olof Johansson 2010-09-02 17:04 ` Leif Lindholm 2010-09-02 17:20 ` Olof Johansson
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).