Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq
[pandora-kernel.git] / arch / arm / mm / flush.c
index c6844cb..391ffae 100644 (file)
@@ -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"
 
@@ -39,6 +40,18 @@ static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr)
            : "cc");
 }
 
+static void flush_icache_alias(unsigned long pfn, unsigned long vaddr, unsigned long len)
+{
+       unsigned long colour = CACHE_COLOUR(vaddr);
+       unsigned long offset = vaddr & (PAGE_SIZE - 1);
+       unsigned long to;
+
+       set_pte_ext(TOP_PTE(ALIAS_FLUSH_START) + colour, pfn_pte(pfn, PAGE_KERNEL), 0);
+       to = ALIAS_FLUSH_START + (colour << PAGE_SHIFT) + offset;
+       flush_tlb_kernel_page(to);
+       flush_icache_range(to, to + len);
+}
+
 void flush_cache_mm(struct mm_struct *mm)
 {
        if (cache_is_vivt()) {
@@ -89,16 +102,16 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig
        if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged())
                __flush_icache_all();
 }
+
 #else
-#define flush_pfn_alias(pfn,vaddr)     do { } while (0)
+#define flush_pfn_alias(pfn,vaddr)             do { } while (0)
+#define flush_icache_alias(pfn,vaddr,len)      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,
@@ -118,15 +131,16 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
                return;
        }
 
-       /* VIPT non-aliasing cache */
+       /* VIPT non-aliasing D-cache */
        if (vma->vm_flags & VM_EXEC) {
                unsigned long addr = (unsigned long)kaddr;
-               __cpuc_coherent_kern_range(addr, addr + len);
-#ifdef CONFIG_SMP
+               if (icache_is_vipt_aliasing())
+                       flush_icache_alias(page_to_pfn(page), uaddr, len);
+               else
+                       __cpuc_coherent_kern_range(addr, addr + len);
                if (cache_ops_need_broadcast())
                        smp_call_function(flush_ptrace_access_other,
                                          NULL, 1);
-#endif
        }
 }
 
@@ -215,6 +229,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.
@@ -246,17 +290,16 @@ void flush_dcache_page(struct page *page)
 
        mapping = page_mapping(page);
 
-#ifndef CONFIG_SMP
-       if (!PageHighMem(page) && mapping && !mapping_mapped(mapping))
-               set_bit(PG_dcache_dirty, &page->flags);
-       else
-#endif
-       {
+       if (!cache_ops_need_broadcast() &&
+           mapping && !mapping_mapped(mapping))
+               clear_bit(PG_dcache_clean, &page->flags);
+       else {
                __flush_dcache_page(mapping, page);
                if (mapping && cache_is_vivt())
                        __flush_dcache_aliases(mapping, page);
                else if (mapping)
                        __flush_icache_all();
+               set_bit(PG_dcache_clean, &page->flags);
        }
 }
 EXPORT_SYMBOL(flush_dcache_page);