sh: Optimized flush_icache_range() implementation.
authorChris Smith <chris.smith@st.com>
Wed, 2 Jul 2008 06:17:11 +0000 (15:17 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Mon, 28 Jul 2008 09:10:32 +0000 (18:10 +0900)
Add implementation of flush_icache_range() suitable for signal handler
and kprobes. Remove flush_cache_sigtramp() and change signal.c to use
flush_icache_range().

Signed-off-by: Chris Smith <chris.smith@st.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
arch/sh/kernel/signal_32.c
arch/sh/mm/cache-sh4.c
include/asm-sh/cpu-sh4/cacheflush.h

index 46170a9a7221c0fb049f08c0a12b81930f79e5fe..eee29257a8ae0bdbc4cb26a3a9a1470c88d09876 100644 (file)
@@ -398,10 +398,7 @@ static int setup_frame(int sig, struct k_sigaction *ka,
        pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n",
                 current->comm, task_pid_nr(current), frame, regs->pc, regs->pr);
 
-       flush_cache_sigtramp(regs->pr);
-
-       if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode))
-               flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES);
+       flush_icache_range(regs->pr, regs->pr + sizeof(frame->retcode));
 
        return 0;
 
@@ -486,10 +483,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
        pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n",
                 current->comm, task_pid_nr(current), frame, regs->pc, regs->pr);
 
-       flush_cache_sigtramp(regs->pr);
-
-       if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode))
-               flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES);
+       flush_icache_range(regs->pr, regs->pr + sizeof(frame->retcode));
 
        return 0;
 
index 43d7ff6b6ec7c71cfbaccc244d46bc6bcf0a7ce7..1fdc8d90254a177bbb4a345456712378a17fdb2c 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) 1999, 2000, 2002  Niibe Yutaka
  * Copyright (C) 2001 - 2007  Paul Mundt
  * Copyright (C) 2003  Richard Curnow
+ * Copyright (c) 2007 STMicroelectronics (R&D) Ltd.
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
@@ -22,6 +23,7 @@
  * entirety.
  */
 #define MAX_DCACHE_PAGES       64      /* XXX: Tune for ways */
+#define MAX_ICACHE_PAGES       32
 
 static void __flush_dcache_segment_1way(unsigned long start,
                                        unsigned long extent);
@@ -178,42 +180,45 @@ void __flush_invalidate_region(void *start, int size)
 /*
  * Write back the range of D-cache, and purge the I-cache.
  *
- * Called from kernel/module.c:sys_init_module and routine for a.out format.
+ * Called from kernel/module.c:sys_init_module and routine for a.out format,
+ * signal handler code and kprobes code
  */
 void flush_icache_range(unsigned long start, unsigned long end)
 {
-       flush_cache_all();
-}
-
-/*
- * 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 __uses_jump_to_uncached flush_cache_sigtramp(unsigned long addr)
-{
-       unsigned long v, index;
-       unsigned long flags;
+       int icacheaddr;
+       unsigned long flags, v;
        int i;
 
-       v = addr & ~(L1_CACHE_BYTES-1);
-       asm volatile("ocbwb     %0"
-                    : /* no output */
-                    : "m" (__m(v)));
-
-       index = CACHE_IC_ADDRESS_ARRAY |
-                       (v & boot_cpu_data.icache.entry_mask);
-
-       local_irq_save(flags);
-       jump_to_uncached();
-
-       for (i = 0; i < boot_cpu_data.icache.ways;
-            i++, index += boot_cpu_data.icache.way_incr)
-               ctrl_outl(0, index);    /* Clear out Valid-bit */
-
-       back_to_cached();
-       wmb();
-       local_irq_restore(flags);
+       /* If there are too many pages then just blow the caches */
+        if (((end - start) >> PAGE_SHIFT) >= MAX_ICACHE_PAGES) {
+                flush_cache_all();
+       } else {
+               /* selectively flush d-cache then invalidate the i-cache */
+               /* this is inefficient, so only use for small ranges */
+               start &= ~(L1_CACHE_BYTES-1);
+               end += L1_CACHE_BYTES-1;
+               end &= ~(L1_CACHE_BYTES-1);
+
+               local_irq_save(flags);
+               jump_to_uncached();
+
+               for (v = start; v < end; v+=L1_CACHE_BYTES) {
+                       asm volatile("ocbwb     %0"
+                                    : /* no output */
+                                    : "m" (__m(v)));
+
+                       icacheaddr = CACHE_IC_ADDRESS_ARRAY | (
+                                       v & cpu_data->icache.entry_mask);
+
+                       for (i = 0; i < cpu_data->icache.ways;
+                               i++, icacheaddr += cpu_data->icache.way_incr)
+                                       /* Clear i-cache line valid-bit */
+                                       ctrl_outl(0, icacheaddr);
+               }
+
+               back_to_cached();
+               local_irq_restore(flags);
+       }
 }
 
 static inline void flush_cache_4096(unsigned long start,
index 5fd5c89ef86a8c859429d257f31bc488dfdc47cf..065306d376eb694f59309d4f699ab84478c1b976 100644 (file)
@@ -30,7 +30,6 @@ void flush_dcache_page(struct page *pg);
 #define flush_dcache_mmap_unlock(mapping)      do { } while (0)
 
 void flush_icache_range(unsigned long start, unsigned long end);
-void flush_cache_sigtramp(unsigned long addr);
 void flush_icache_user_range(struct vm_area_struct *vma, struct page *page,
                             unsigned long addr, int len);