diff options
Diffstat (limited to 'arch/arm/mm/flush.c')
-rw-r--r-- | arch/arm/mm/flush.c | 78 |
1 files changed, 61 insertions, 17 deletions
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index c6844cb9b508..1a8d4aa821be 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/mm.h> | 11 | #include <linux/mm.h> |
12 | #include <linux/pagemap.h> | 12 | #include <linux/pagemap.h> |
13 | #include <linux/highmem.h> | ||
13 | 14 | ||
14 | #include <asm/cacheflush.h> | 15 | #include <asm/cacheflush.h> |
15 | #include <asm/cachetype.h> | 16 | #include <asm/cachetype.h> |
@@ -39,6 +40,18 @@ static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr) | |||
39 | : "cc"); | 40 | : "cc"); |
40 | } | 41 | } |
41 | 42 | ||
43 | static void flush_icache_alias(unsigned long pfn, unsigned long vaddr, unsigned long len) | ||
44 | { | ||
45 | unsigned long colour = CACHE_COLOUR(vaddr); | ||
46 | unsigned long offset = vaddr & (PAGE_SIZE - 1); | ||
47 | unsigned long to; | ||
48 | |||
49 | set_pte_ext(TOP_PTE(ALIAS_FLUSH_START) + colour, pfn_pte(pfn, PAGE_KERNEL), 0); | ||
50 | to = ALIAS_FLUSH_START + (colour << PAGE_SHIFT) + offset; | ||
51 | flush_tlb_kernel_page(to); | ||
52 | flush_icache_range(to, to + len); | ||
53 | } | ||
54 | |||
42 | void flush_cache_mm(struct mm_struct *mm) | 55 | void flush_cache_mm(struct mm_struct *mm) |
43 | { | 56 | { |
44 | if (cache_is_vivt()) { | 57 | if (cache_is_vivt()) { |
@@ -89,16 +102,16 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig | |||
89 | if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged()) | 102 | if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged()) |
90 | __flush_icache_all(); | 103 | __flush_icache_all(); |
91 | } | 104 | } |
105 | |||
92 | #else | 106 | #else |
93 | #define flush_pfn_alias(pfn,vaddr) do { } while (0) | 107 | #define flush_pfn_alias(pfn,vaddr) do { } while (0) |
108 | #define flush_icache_alias(pfn,vaddr,len) do { } while (0) | ||
94 | #endif | 109 | #endif |
95 | 110 | ||
96 | #ifdef CONFIG_SMP | ||
97 | static void flush_ptrace_access_other(void *args) | 111 | static void flush_ptrace_access_other(void *args) |
98 | { | 112 | { |
99 | __flush_icache_all(); | 113 | __flush_icache_all(); |
100 | } | 114 | } |
101 | #endif | ||
102 | 115 | ||
103 | static | 116 | static |
104 | void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, | 117 | void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, |
@@ -118,15 +131,16 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, | |||
118 | return; | 131 | return; |
119 | } | 132 | } |
120 | 133 | ||
121 | /* VIPT non-aliasing cache */ | 134 | /* VIPT non-aliasing D-cache */ |
122 | if (vma->vm_flags & VM_EXEC) { | 135 | if (vma->vm_flags & VM_EXEC) { |
123 | unsigned long addr = (unsigned long)kaddr; | 136 | unsigned long addr = (unsigned long)kaddr; |
124 | __cpuc_coherent_kern_range(addr, addr + len); | 137 | if (icache_is_vipt_aliasing()) |
125 | #ifdef CONFIG_SMP | 138 | flush_icache_alias(page_to_pfn(page), uaddr, len); |
139 | else | ||
140 | __cpuc_coherent_kern_range(addr, addr + len); | ||
126 | if (cache_ops_need_broadcast()) | 141 | if (cache_ops_need_broadcast()) |
127 | smp_call_function(flush_ptrace_access_other, | 142 | smp_call_function(flush_ptrace_access_other, |
128 | NULL, 1); | 143 | NULL, 1); |
129 | #endif | ||
130 | } | 144 | } |
131 | } | 145 | } |
132 | 146 | ||
@@ -166,10 +180,10 @@ void __flush_dcache_page(struct address_space *mapping, struct page *page) | |||
166 | __cpuc_flush_dcache_area(addr, PAGE_SIZE); | 180 | __cpuc_flush_dcache_area(addr, PAGE_SIZE); |
167 | kunmap_high(page); | 181 | kunmap_high(page); |
168 | } else if (cache_is_vipt()) { | 182 | } else if (cache_is_vipt()) { |
169 | pte_t saved_pte; | 183 | /* unmapped pages might still be cached */ |
170 | addr = kmap_high_l1_vipt(page, &saved_pte); | 184 | addr = kmap_atomic(page); |
171 | __cpuc_flush_dcache_area(addr, PAGE_SIZE); | 185 | __cpuc_flush_dcache_area(addr, PAGE_SIZE); |
172 | kunmap_high_l1_vipt(page, saved_pte); | 186 | kunmap_atomic(addr); |
173 | } | 187 | } |
174 | } | 188 | } |
175 | 189 | ||
@@ -215,6 +229,36 @@ static void __flush_dcache_aliases(struct address_space *mapping, struct page *p | |||
215 | flush_dcache_mmap_unlock(mapping); | 229 | flush_dcache_mmap_unlock(mapping); |
216 | } | 230 | } |
217 | 231 | ||
232 | #if __LINUX_ARM_ARCH__ >= 6 | ||
233 | void __sync_icache_dcache(pte_t pteval) | ||
234 | { | ||
235 | unsigned long pfn; | ||
236 | struct page *page; | ||
237 | struct address_space *mapping; | ||
238 | |||
239 | if (!pte_present_user(pteval)) | ||
240 | return; | ||
241 | if (cache_is_vipt_nonaliasing() && !pte_exec(pteval)) | ||
242 | /* only flush non-aliasing VIPT caches for exec mappings */ | ||
243 | return; | ||
244 | pfn = pte_pfn(pteval); | ||
245 | if (!pfn_valid(pfn)) | ||
246 | return; | ||
247 | |||
248 | page = pfn_to_page(pfn); | ||
249 | if (cache_is_vipt_aliasing()) | ||
250 | mapping = page_mapping(page); | ||
251 | else | ||
252 | mapping = NULL; | ||
253 | |||
254 | if (!test_and_set_bit(PG_dcache_clean, &page->flags)) | ||
255 | __flush_dcache_page(mapping, page); | ||
256 | |||
257 | if (pte_exec(pteval)) | ||
258 | __flush_icache_all(); | ||
259 | } | ||
260 | #endif | ||
261 | |||
218 | /* | 262 | /* |
219 | * Ensure cache coherency between kernel mapping and userspace mapping | 263 | * Ensure cache coherency between kernel mapping and userspace mapping |
220 | * of this page. | 264 | * of this page. |
@@ -231,7 +275,8 @@ static void __flush_dcache_aliases(struct address_space *mapping, struct page *p | |||
231 | * kernel cache lines for later. Otherwise, we assume we have | 275 | * kernel cache lines for later. Otherwise, we assume we have |
232 | * aliasing mappings. | 276 | * aliasing mappings. |
233 | * | 277 | * |
234 | * Note that we disable the lazy flush for SMP. | 278 | * Note that we disable the lazy flush for SMP configurations where |
279 | * the cache maintenance operations are not automatically broadcasted. | ||
235 | */ | 280 | */ |
236 | void flush_dcache_page(struct page *page) | 281 | void flush_dcache_page(struct page *page) |
237 | { | 282 | { |
@@ -246,17 +291,16 @@ void flush_dcache_page(struct page *page) | |||
246 | 291 | ||
247 | mapping = page_mapping(page); | 292 | mapping = page_mapping(page); |
248 | 293 | ||
249 | #ifndef CONFIG_SMP | 294 | if (!cache_ops_need_broadcast() && |
250 | if (!PageHighMem(page) && mapping && !mapping_mapped(mapping)) | 295 | mapping && !mapping_mapped(mapping)) |
251 | set_bit(PG_dcache_dirty, &page->flags); | 296 | clear_bit(PG_dcache_clean, &page->flags); |
252 | else | 297 | else { |
253 | #endif | ||
254 | { | ||
255 | __flush_dcache_page(mapping, page); | 298 | __flush_dcache_page(mapping, page); |
256 | if (mapping && cache_is_vivt()) | 299 | if (mapping && cache_is_vivt()) |
257 | __flush_dcache_aliases(mapping, page); | 300 | __flush_dcache_aliases(mapping, page); |
258 | else if (mapping) | 301 | else if (mapping) |
259 | __flush_icache_all(); | 302 | __flush_icache_all(); |
303 | set_bit(PG_dcache_clean, &page->flags); | ||
260 | } | 304 | } |
261 | } | 305 | } |
262 | EXPORT_SYMBOL(flush_dcache_page); | 306 | EXPORT_SYMBOL(flush_dcache_page); |