sh: Provide __flush_anon_page().
authorPaul Mundt <lethal@linux-sh.org>
Tue, 4 Aug 2009 07:02:43 +0000 (16:02 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Tue, 4 Aug 2009 07:02:43 +0000 (16:02 +0900)
This provides a __flush_anon_page() that handles both the aliasing and
non-aliasing cases. This fixes up some crashes with heavy
get_user_pages() users.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
arch/sh/include/asm/cacheflush.h
arch/sh/mm/pg-mmu.c

index 4c85d55847cce12704a003d662149b51a326a239..5dffbd126e469b69eca0654627de7ea7da8c9b44 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __ASM_SH_CACHEFLUSH_H
 #define __ASM_SH_CACHEFLUSH_H
 
+#include <linux/mm.h>
+
 #ifdef __KERNEL__
 
 #ifdef CONFIG_CACHE_OFF
@@ -43,6 +45,18 @@ extern void __flush_purge_region(void *start, int size);
 extern void __flush_invalidate_region(void *start, int size);
 #endif
 
+#ifdef CONFIG_MMU
+#define ARCH_HAS_FLUSH_ANON_PAGE
+extern void __flush_anon_page(struct page *page, unsigned long);
+
+static inline void flush_anon_page(struct vm_area_struct *vma,
+                                  struct page *page, unsigned long vmaddr)
+{
+       if (boot_cpu_data.dcache.n_aliases && PageAnon(page))
+               __flush_anon_page(page, vmaddr);
+}
+#endif
+
 #define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE
 static inline void flush_kernel_dcache_page(struct page *page)
 {
index a9ede7bae5203cfe6afb875907d17badc2732d6c..027c4d83fb8e6787e77ce51ddd1fdc0bdde7b3a7 100644 (file)
@@ -157,3 +157,20 @@ void __update_cache(struct vm_area_struct *vma,
                }
        }
 }
+
+void __flush_anon_page(struct page *page, unsigned long vmaddr)
+{
+       unsigned long addr = (unsigned long) page_address(page);
+
+       if (pages_do_alias(addr, vmaddr)) {
+               if (boot_cpu_data.dcache.n_aliases && page_mapped(page) &&
+                   !test_bit(PG_dcache_dirty, &page->flags)) {
+                       void *kaddr;
+
+                       kaddr = kmap_coherent(page, vmaddr);
+                       __flush_wback_region((void *)kaddr, PAGE_SIZE);
+                       kunmap_coherent();
+               } else
+                       __flush_wback_region((void *)addr, PAGE_SIZE);
+       }
+}