From a252710fc5b63b24934905ca47ecf661702d7f00 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 27 Sep 2006 11:29:55 +0900 Subject: sh: flush_cache_range() cleanup and optimizations. flush_cache_range() wasn't page aligning the end of the range, we can't assume that it will always be page aligned, and we ended up getting unaligned faults in some rare call paths. Additionally, we add a small optimization to just purge the dcache entirely if the range is large enough that the page table walking will take longer. We use an arbitrary value of 64 pages for the large range size, as per sh64. Signed-off-by: Paul Mundt --- arch/sh/mm/cache-sh4.c | 72 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 26 deletions(-) (limited to 'arch') diff --git a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c index 524cea5b47f..94c05d09c3f 100644 --- a/arch/sh/mm/cache-sh4.c +++ b/arch/sh/mm/cache-sh4.c @@ -2,7 +2,7 @@ * arch/sh/mm/cache-sh4.c * * Copyright (C) 1999, 2000, 2002 Niibe Yutaka - * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Paul Mundt * Copyright (C) 2003 Richard Curnow * * This file is subject to the terms and conditions of the GNU General Public @@ -25,6 +25,8 @@ #include #include +extern void __flush_cache_4096(unsigned long addr, unsigned long phys, + unsigned long exec_offset); extern void __flush_cache_4096_all(unsigned long start); static void __flush_cache_4096_all_ex(unsigned long start); extern void __flush_dcache_all(void); @@ -112,9 +114,14 @@ static void __flush_dcache_all_ex(void) { unsigned long addr, end_addr, entry_offset; - end_addr = CACHE_OC_ADDRESS_ARRAY + (cpu_data->dcache.sets << cpu_data->dcache.entry_shift) * cpu_data->dcache.ways; + end_addr = CACHE_OC_ADDRESS_ARRAY + + (cpu_data->dcache.sets << cpu_data->dcache.entry_shift) * + cpu_data->dcache.ways; + entry_offset = 1 << cpu_data->dcache.entry_shift; - for (addr = CACHE_OC_ADDRESS_ARRAY; addr < end_addr; addr += entry_offset) { + for (addr = CACHE_OC_ADDRESS_ARRAY; + addr < end_addr; + addr += entry_offset) { ctrl_outl(0, addr); } } @@ -125,7 +132,8 @@ static void __flush_cache_4096_all_ex(unsigned long start) int i; entry_offset = 1 << cpu_data->dcache.entry_shift; - for (i = 0; i < cpu_data->dcache.ways; i++, start += cpu_data->dcache.way_incr) { + for (i = 0; i < cpu_data->dcache.ways; + i++, start += cpu_data->dcache.way_incr) { for (addr = CACHE_OC_ADDRESS_ARRAY + start; addr < CACHE_OC_ADDRESS_ARRAY + 4096 + start; addr += entry_offset) { @@ -153,14 +161,14 @@ void flush_icache_range(unsigned long start, unsigned long end) } /* - * Write back the D-cache and purge the I-cache for signal trampoline. + * Write back the D-cache and purge the I-cache for signal trampoline. * .. which happens to be the same behavior as flush_icache_range(). * So, we simply flush out a line. */ void flush_cache_sigtramp(unsigned long addr) { unsigned long v, index; - unsigned long flags; + unsigned long flags; int i; v = addr & ~(L1_CACHE_BYTES-1); @@ -172,7 +180,8 @@ void flush_cache_sigtramp(unsigned long addr) local_irq_save(flags); jump_to_P2(); - for(i = 0; i < cpu_data->icache.ways; i++, index += cpu_data->icache.way_incr) + for (i = 0; i < cpu_data->icache.ways; + i++, index += cpu_data->icache.way_incr) ctrl_outl(0, index); /* Clear out Valid-bit */ back_to_P1(); local_irq_restore(flags); @@ -181,8 +190,7 @@ void flush_cache_sigtramp(unsigned long addr) static inline void flush_cache_4096(unsigned long start, unsigned long phys) { - unsigned long flags; - extern void __flush_cache_4096(unsigned long addr, unsigned long phys, unsigned long exec_offset); + unsigned long flags; /* * SH7751, SH7751R, and ST40 have no restriction to handle cache. @@ -191,10 +199,12 @@ static inline void flush_cache_4096(unsigned long start, if ((cpu_data->flags & CPU_HAS_P2_FLUSH_BUG) || start < CACHE_OC_ADDRESS_ARRAY) { local_irq_save(flags); - __flush_cache_4096(start | SH_CACHE_ASSOC, P1SEGADDR(phys), 0x20000000); + __flush_cache_4096(start | SH_CACHE_ASSOC, + P1SEGADDR(phys), 0x20000000); local_irq_restore(flags); } else { - __flush_cache_4096(start | SH_CACHE_ASSOC, P1SEGADDR(phys), 0); + __flush_cache_4096(start | SH_CACHE_ASSOC, + P1SEGADDR(phys), 0); } } @@ -231,29 +241,22 @@ static inline void flush_icache_all(void) local_irq_restore(flags); } -void flush_cache_all(void) +void flush_dcache_all(void) { if (cpu_data->dcache.ways == 1) __flush_dcache_all(); else __flush_dcache_all_ex(); +} + +void flush_cache_all(void) +{ + flush_dcache_all(); flush_icache_all(); } void flush_cache_mm(struct mm_struct *mm) { - /* Is there any good way? */ - /* XXX: possibly call flush_cache_range for each vm area */ - /* - * FIXME: Really, the optimal solution here would be able to flush out - * individual lines created by the specified context, but this isn't - * feasible for a number of architectures (such as MIPS, and some - * SPARC) .. is this possible for SuperH? - * - * In the meantime, we'll just flush all of the caches.. this - * seems to be the simplest way to avoid at least a few wasted - * cache flushes. -Lethal - */ flush_cache_all(); } @@ -301,13 +304,30 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long p = start & PAGE_MASK; pgd_t *dir; pmd_t *pmd; + pud_t *pud; pte_t *pte; pte_t entry; unsigned long phys; unsigned long d = 0; + /* + * Don't bother with the lookup and alias check if we have a + * wide range to cover, just blow away the dcache in its + * entirety instead. -- PFM. + */ + if (((end - start) >> PAGE_SHIFT) >= 64) { + flush_dcache_all(); + + if (vma->vm_flags & VM_EXEC) + flush_icache_all(); + + return; + } + dir = pgd_offset(vma->vm_mm, p); - pmd = pmd_offset(dir, p); + pud = pud_offset(dir, p); + pmd = pmd_offset(pud, p); + end = PAGE_ALIGN(end); do { if (pmd_none(*pmd) || pmd_bad(*pmd)) { @@ -322,7 +342,7 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, if ((pte_val(entry) & _PAGE_PRESENT)) { phys = pte_val(entry)&PTE_PHYS_MASK; if ((p^phys) & CACHE_ALIAS) { - d |= 1 << ((p & CACHE_ALIAS)>>12); + d |= 1 << ((p & CACHE_ALIAS)>>12); d |= 1 << ((phys & CACHE_ALIAS)>>12); if (d == 0x0f) goto loop_exit; -- cgit v1.2.3