From 2f0b192633f1fbf253b21c90938733491549edae Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 25 Oct 2009 10:40:02 +0000 Subject: ARM: Avoid duplicated implementation for VIVT cache flushing We had two copies of the wrapper code for VIVT cache flushing - one in asm/cacheflush.h and one in arch/arm/mm/flush.c. Reduce this down to one common copy. Signed-off-by: Russell King --- arch/arm/mm/flush.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'arch/arm/mm/flush.c') diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 7f294f307c83..a480f161a4bb 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -41,8 +41,7 @@ static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr) void flush_cache_mm(struct mm_struct *mm) { if (cache_is_vivt()) { - if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(mm))) - __cpuc_flush_user_all(); + vivt_flush_cache_mm(mm); return; } @@ -59,9 +58,7 @@ void flush_cache_mm(struct mm_struct *mm) void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { if (cache_is_vivt()) { - if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) - __cpuc_flush_user_range(start & PAGE_MASK, PAGE_ALIGN(end), - vma->vm_flags); + vivt_flush_cache_range(vma, start, end); return; } @@ -78,10 +75,7 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn) { if (cache_is_vivt()) { - if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) { - unsigned long addr = user_addr & PAGE_MASK; - __cpuc_flush_user_range(addr, addr + PAGE_SIZE, vma->vm_flags); - } + vivt_flush_cache_page(vma, user_addr, pfn); return; } @@ -94,10 +88,7 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, unsigned long len, int write) { if (cache_is_vivt()) { - if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) { - unsigned long addr = (unsigned long)kaddr; - __cpuc_coherent_kern_range(addr, addr + len); - } + vivt_flush_ptrace_access(vma, page, uaddr, kaddr, len, write); return; } -- cgit v1.2.3 From b7dc0b2cfc6e9bc7270915c642a8a8e999b6095e Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 25 Oct 2009 11:25:50 +0000 Subject: ARM: Avoid evaluating page_address() multiple times page_address() is a function call rather than a macro, and so: if (page_address(page)) do_something(page_address(page)); results in two calls to this function. This is unnecessary; remove the duplication. Signed-off-by: Russell King --- arch/arm/mm/flush.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'arch/arm/mm/flush.c') diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index a480f161a4bb..43474d8752a6 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -111,6 +111,8 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, void __flush_dcache_page(struct address_space *mapping, struct page *page) { + void *addr = page_address(page); + /* * Writeback any data associated with the kernel mapping of this * page. This ensures that data in the physical page is mutually @@ -121,9 +123,9 @@ void __flush_dcache_page(struct address_space *mapping, struct page *page) * kmap_atomic() doesn't set the page virtual address, and * kunmap_atomic() takes care of cache flushing already. */ - if (page_address(page)) + if (addr) #endif - __cpuc_flush_dcache_page(page_address(page)); + __cpuc_flush_dcache_page(addr); /* * If this is a page cache page, and we have an aliasing VIPT cache, -- cgit v1.2.3 From 421fe93cc4b06b2f5e875cbe0f692800d4862ee5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 25 Oct 2009 10:23:04 +0000 Subject: ARM: ZERO_PAGE: Avoid flush_dcache_page() for zero page The zero page is read-only, and has its cache state cleared during boot. No further maintanence for this page is required. Signed-off-by: Russell King --- arch/arm/mm/flush.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'arch/arm/mm/flush.c') diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 43474d8752a6..82f4b06bf6b4 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -189,7 +189,16 @@ static void __flush_dcache_aliases(struct address_space *mapping, struct page *p */ void flush_dcache_page(struct page *page) { - struct address_space *mapping = page_mapping(page); + struct address_space *mapping; + + /* + * The zero page is never written to, so never has any dirty + * cache lines, and therefore never needs to be flushed. + */ + if (page == ZERO_PAGE(0)) + return; + + mapping = page_mapping(page); #ifndef CONFIG_SMP if (!PageHighMem(page) && mapping && !mapping_mapped(mapping)) -- cgit v1.2.3 From 2df341edf6b8a2db7f414d00faeadccbdd9844ab Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 24 Oct 2009 22:58:40 +0100 Subject: ARM: Move __flush_icache_all() out of flush_pfn_alias() Signed-off-by: Russell King --- arch/arm/mm/flush.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'arch/arm/mm/flush.c') diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 82f4b06bf6b4..302d66517488 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -35,7 +35,6 @@ static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr) : : "r" (to), "r" (to + PAGE_SIZE - L1_CACHE_BYTES), "r" (zero) : "cc"); - __flush_icache_all(); } void flush_cache_mm(struct mm_struct *mm) @@ -79,8 +78,10 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig return; } - if (cache_is_vipt_aliasing()) + if (cache_is_vipt_aliasing()) { flush_pfn_alias(pfn, user_addr); + __flush_icache_all(); + } } void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, @@ -94,6 +95,7 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, if (cache_is_vipt_aliasing()) { flush_pfn_alias(page_to_pfn(page), uaddr); + __flush_icache_all(); return; } @@ -132,9 +134,11 @@ void __flush_dcache_page(struct address_space *mapping, struct page *page) * we only need to do one flush - which would be at the relevant * userspace colour, which is congruent with page->index. */ - if (mapping && cache_is_vipt_aliasing()) + if (mapping && cache_is_vipt_aliasing()) { flush_pfn_alias(page_to_pfn(page), page->index << PAGE_CACHE_SHIFT); + __flush_icache_all(); + } } static void __flush_dcache_aliases(struct address_space *mapping, struct page *page) @@ -244,6 +248,7 @@ void __flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned l * userspace address only. */ flush_pfn_alias(pfn, vmaddr); + __flush_icache_all(); } /* -- cgit v1.2.3 From f91fb05d826a43063fa0aa2ec30c23d3993a208d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 24 Oct 2009 23:05:34 +0100 Subject: ARM: Remove __flush_icache_all() from __flush_dcache_page() Both call sites for __flush_dcache_page() end up calling __flush_icache_all() themselves, so having __flush_dcache_page() do this as well is wasteful. Remove the duplicated icache flushing. Signed-off-by: Russell King --- arch/arm/mm/flush.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'arch/arm/mm/flush.c') diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 302d66517488..dc66f867bec4 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -134,11 +134,9 @@ void __flush_dcache_page(struct address_space *mapping, struct page *page) * we only need to do one flush - which would be at the relevant * userspace colour, which is congruent with page->index. */ - if (mapping && cache_is_vipt_aliasing()) { + if (mapping && cache_is_vipt_aliasing()) flush_pfn_alias(page_to_pfn(page), page->index << PAGE_CACHE_SHIFT); - __flush_icache_all(); - } } static void __flush_dcache_aliases(struct address_space *mapping, struct page *page) -- cgit v1.2.3 From 9e95922b1016ac941db7edcf6b6088b3c2e916c8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 25 Oct 2009 13:35:13 +0000 Subject: ARM: I-cache: Add invalidation for VIVT ASID tagged caches Signed-off-by: Russell King --- arch/arm/mm/flush.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'arch/arm/mm/flush.c') diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index dc66f867bec4..9770e27dd581 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -69,6 +69,9 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned : "cc"); __flush_icache_all(); } + + if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged()) + __flush_icache_all(); } void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn) @@ -82,6 +85,9 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig flush_pfn_alias(pfn, user_addr); __flush_icache_all(); } + + if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged()) + __flush_icache_all(); } void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, -- cgit v1.2.3 From ea201dbb78651c71c56e440b8b3132906bc7456d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 25 Oct 2009 14:31:40 +0000 Subject: ARM: I-cache: avoid flushing in flush_cache_mm() flush_cache_mm() is called in two cases: 1. when a process exits, just before the page tables are torn down. We can allow the stale lines to evict themselves over time without causing any harm. 2. when a process forks, and we've allocated a new ASID. The instruction cache issues are dealt with as pages are brought into the new process address space. Flushing the I-cache here is therefore unnecessary. However, we must keep the VIPT aliasing D-cache flush to ensure that any dirty cache lines are not written back after the pages have been reallocated for some other use - which would result in corruption. Signed-off-by: Russell King --- arch/arm/mm/flush.c | 1 - 1 file changed, 1 deletion(-) (limited to 'arch/arm/mm/flush.c') diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 9770e27dd581..f8feb5d919fe 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -50,7 +50,6 @@ void flush_cache_mm(struct mm_struct *mm) : : "r" (0) : "cc"); - __flush_icache_all(); } } -- cgit v1.2.3 From 6060e8df517847bf445ebc61de7d4d9c7faae990 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 25 Oct 2009 14:12:27 +0000 Subject: ARM: I-cache: flush executable mappings in flush_cache_range() Dirk Behme reported instability on ARM11 SMP (VIPT non-aliasing cache) caused by the dynamic linker changing protection on text pages to write GOT entries. The problem is due to an interaction between the write faulting code providing new anonymous pages which are incoherent with the I-cache due to write buffering, and the I-cache not having been invalidated. a4db94d plugs the hole with the data cache coherency. This patch provides the other half of the fix by flushing the I-cache in flush_cache_range() for VM_EXEC VMAs (which is what we have when the region is being made executable again.) This ensures that the I-cache will be up to date with the newly COW'd pages. Note: if users are writing instructions, then they still need to use the ARM sys_cacheflush API to ensure that the caches are correctly synchronized. Signed-off-by: Russell King --- arch/arm/mm/flush.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'arch/arm/mm/flush.c') diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index f8feb5d919fe..329594e760cd 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -66,10 +66,9 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned : : "r" (0) : "cc"); - __flush_icache_all(); } - if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged()) + if (vma->vm_flags & VM_EXEC) __flush_icache_all(); } -- cgit v1.2.3