]> err.no Git - linux-2.6/commitdiff
sh: Use a per-cpu ASID cache.
authorPaul Mundt <lethal@linux-sh.org>
Mon, 25 Dec 2006 00:51:47 +0000 (09:51 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Tue, 13 Feb 2007 01:54:45 +0000 (10:54 +0900)
Previously this was implemented using a global cache, cache
this per-CPU instead and bump up the number of context IDs to
match NR_CPUS.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
arch/sh/kernel/cpu/init.c
arch/sh/kernel/process.c
arch/sh/mm/init.c
arch/sh/mm/tlb-flush.c
include/asm-sh/mmu.h
include/asm-sh/mmu_context.h
include/asm-sh/processor.h

index 48121766e8d243e07065e3f3797da7376092cc9b..6c3c7687e81f91c1fa7498dbadeeaf82af03f0fe 100644 (file)
@@ -3,7 +3,7 @@
  *
  * CPU init code
  *
- * Copyright (C) 2002, 2003  Paul Mundt
+ * Copyright (C) 2002 - 2006  Paul Mundt
  * Copyright (C) 2003  Richard Curnow
  *
  * This file is subject to the terms and conditions of the GNU General Public
@@ -12,6 +12,8 @@
  */
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/mm.h>
+#include <asm/mmu_context.h>
 #include <asm/processor.h>
 #include <asm/uaccess.h>
 #include <asm/page.h>
@@ -218,6 +220,12 @@ asmlinkage void __init sh_cpu_init(void)
                clear_used_math();
        }
 
+       /*
+        * Initialize the per-CPU ASID cache very early, since the
+        * TLB flushing routines depend on this being setup.
+        */
+       current_cpu_data.asid_cache = NO_CONTEXT;
+
 #ifdef CONFIG_SH_DSP
        /* Probe for DSP */
        dsp_init();
@@ -240,4 +248,3 @@ asmlinkage void __init sh_cpu_init(void)
        ubc_wakeup();
 #endif
 }
-
index cc8f306fd68275d179596b1a007c801648ee7f58..0298f0faa6e62236110b399e6f29a2419dd711f5 100644 (file)
@@ -1,42 +1,30 @@
-/* $Id: process.c,v 1.28 2004/05/05 16:54:23 lethal Exp $
+/*
+ * arch/sh/kernel/process.c
  *
- *  linux/arch/sh/kernel/process.c
+ * This file handles the architecture-dependent parts of process handling..
  *
  *  Copyright (C) 1995  Linus Torvalds
  *
  *  SuperH version:  Copyright (C) 1999, 2000  Niibe Yutaka & Kaz Kojima
  *                  Copyright (C) 2006 Lineo Solutions Inc. support SH4A UBC
+ *                  Copyright (C) 2002 - 2006  Paul Mundt
  */
-
-/*
- * This file handles the architecture-dependent parts of process handling..
- */
-
 #include <linux/module.h>
-#include <linux/unistd.h>
 #include <linux/mm.h>
 #include <linux/elfcore.h>
-#include <linux/a.out.h>
-#include <linux/slab.h>
 #include <linux/pm.h>
-#include <linux/ptrace.h>
 #include <linux/kallsyms.h>
 #include <linux/kexec.h>
-
-#include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
-#include <asm/elf.h>
 #include <asm/ubc.h>
 
-static int hlt_counter=0;
-
+static int hlt_counter;
 int ubc_usercnt = 0;
 
 #define HARD_IDLE_TIMEOUT (HZ / 3)
 
 void (*pm_idle)(void);
-
 void (*pm_power_off)(void);
 EXPORT_SYMBOL(pm_power_off);
 
@@ -44,14 +32,12 @@ void disable_hlt(void)
 {
        hlt_counter++;
 }
-
 EXPORT_SYMBOL(disable_hlt);
 
 void enable_hlt(void)
 {
        hlt_counter--;
 }
-
 EXPORT_SYMBOL(enable_hlt);
 
 void default_idle(void)
@@ -152,19 +138,21 @@ __asm__(".align 5\n"
        ".align 2\n\t"
        "1:.long do_exit");
 
+/* Don't use this in BL=1(cli).  Or else, CPU resets! */
 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
-{      /* Don't use this in BL=1(cli).  Or else, CPU resets! */
+{
        struct pt_regs regs;
 
        memset(&regs, 0, sizeof(regs));
-       regs.regs[4] = (unsigned long) arg;
-       regs.regs[5] = (unsigned long) fn;
+       regs.regs[4] = (unsigned long)arg;
+       regs.regs[5] = (unsigned long)fn;
 
-       regs.pc = (unsigned long) kernel_thread_helper;
+       regs.pc = (unsigned long)kernel_thread_helper;
        regs.sr = (1 << 30);
 
        /* Ok, create the new process.. */
-       return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
+       return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0,
+                      &regs, 0, NULL, NULL);
 }
 
 /*
@@ -211,21 +199,20 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
        return fpvalid;
 }
 
-/* 
+/*
  * Capture the user space registers if the task is not running (in user space)
  */
 int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
 {
        struct pt_regs ptregs;
-       
+
        ptregs = *task_pt_regs(tsk);
        elf_core_copy_regs(regs, &ptregs);
 
        return 1;
 }
 
-int
-dump_task_fpu (struct task_struct *tsk, elf_fpregset_t *fpu)
+int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpu)
 {
        int fpvalid = 0;
 
@@ -263,12 +250,14 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
                childregs->regs[15] = usp;
                ti->addr_limit = USER_DS;
        } else {
-               childregs->regs[15] = (unsigned long)task_stack_page(p) + THREAD_SIZE;
+               childregs->regs[15] = (unsigned long)task_stack_page(p) +
+                                                       THREAD_SIZE;
                ti->addr_limit = KERNEL_DS;
        }
-        if (clone_flags & CLONE_SETTLS) {
+
+        if (clone_flags & CLONE_SETTLS)
                childregs->gbr = childregs->regs[0];
-       }
+
        childregs->regs[0] = 0; /* Set return value for child */
 
        p->thread.sp = (unsigned long) childregs;
@@ -280,8 +269,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
 }
 
 /* Tracing by user break controller.  */
-static void
-ubc_set_tracing(int asid, unsigned long pc)
+static void ubc_set_tracing(int asid, unsigned long pc)
 {
 #if defined(CONFIG_CPU_SH4A)
        unsigned long val;
@@ -297,7 +285,7 @@ ubc_set_tracing(int asid, unsigned long pc)
        val = (UBC_CRR_RES | UBC_CRR_PCB | UBC_CRR_BIE);
        ctrl_outl(val, UBC_CRR0);
 
-       /* Read UBC register that we writed last. For chekking UBC Register changed */
+       /* Read UBC register that we wrote last, for checking update */
        val = ctrl_inl(UBC_CRR0);
 
 #else  /* CONFIG_CPU_SH4A */
@@ -325,7 +313,8 @@ ubc_set_tracing(int asid, unsigned long pc)
  *     switch_to(x,y) should switch tasks from x to y.
  *
  */
-struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *next)
+struct task_struct *__switch_to(struct task_struct *prev,
+                               struct task_struct *next)
 {
 #if defined(CONFIG_SH_FPU)
        unlazy_fpu(prev, task_pt_regs(prev));
@@ -354,7 +343,7 @@ struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *ne
 #ifdef CONFIG_MMU
        /*
         * Restore the kernel mode register
-        *      k7 (r7_bank1)
+        *      k7 (r7_bank1)
         */
        asm volatile("ldc       %0, r7_bank"
                     : /* no output */
@@ -367,7 +356,7 @@ struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *ne
        else if (next->thread.ubc_pc && next->mm) {
                int asid = 0;
 #ifdef CONFIG_MMU
-               asid |= next->mm->context.id & MMU_CONTEXT_ASID_MASK;
+               asid |= cpu_asid(smp_processor_id(), next->mm);
 #endif
                ubc_set_tracing(asid, next->thread.ubc_pc);
        } else {
@@ -405,7 +394,8 @@ asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
        if (!newsp)
                newsp = regs->regs[15];
        return do_fork(clone_flags, newsp, regs, 0,
-                       (int __user *)parent_tidptr, (int __user *)child_tidptr);
+                       (int __user *)parent_tidptr,
+                       (int __user *)child_tidptr);
 }
 
 /*
index bf0c263cb6fd036ec3f3f9e971799dc41f18fa56..d172065182fb32dc57fc7cdec413b81020e10192 100644 (file)
 DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
 pgd_t swapper_pg_dir[PTRS_PER_PGD];
 
-/*
- * Cache of MMU context last used.
- */
-unsigned long mmu_context_cache = NO_CONTEXT;
-
 #ifdef CONFIG_MMU
 /* It'd be good if these lines were in the standard header file. */
 #define START_PFN      (NODE_DATA(0)->bdata->node_boot_start >> PAGE_SHIFT)
index ef3e4d477864f3d2060f884a3df3e461b0c347c0..b829c17c1d17d14312bc4aaa5c4373f3985439df 100644 (file)
 
 void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 {
-       if (vma->vm_mm && vma->vm_mm->context.id != NO_CONTEXT) {
+       unsigned int cpu = smp_processor_id();
+
+       if (vma->vm_mm && cpu_context(cpu, vma->vm_mm) != NO_CONTEXT) {
                unsigned long flags;
                unsigned long asid;
                unsigned long saved_asid = MMU_NO_ASID;
 
-               asid = vma->vm_mm->context.id & MMU_CONTEXT_ASID_MASK;
+               asid = cpu_asid(cpu, vma->vm_mm);
                page &= PAGE_MASK;
 
                local_irq_save(flags);
@@ -40,22 +42,23 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
                     unsigned long end)
 {
        struct mm_struct *mm = vma->vm_mm;
+       unsigned int cpu = smp_processor_id();
 
-       if (mm->context.id != NO_CONTEXT) {
+       if (cpu_context(cpu, mm) != NO_CONTEXT) {
                unsigned long flags;
                int size;
 
                local_irq_save(flags);
                size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
                if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */
-                       mm->context.id = NO_CONTEXT;
+                       cpu_context(cpu, mm) = NO_CONTEXT;
                        if (mm == current->mm)
-                               activate_context(mm);
+                               activate_context(mm, cpu);
                } else {
                        unsigned long asid;
                        unsigned long saved_asid = MMU_NO_ASID;
 
-                       asid = mm->context.id & MMU_CONTEXT_ASID_MASK;
+                       asid = cpu_asid(cpu, mm);
                        start &= PAGE_MASK;
                        end += (PAGE_SIZE - 1);
                        end &= PAGE_MASK;
@@ -76,6 +79,7 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
 
 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
 {
+       unsigned int cpu = smp_processor_id();
        unsigned long flags;
        int size;
 
@@ -87,7 +91,7 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end)
                unsigned long asid;
                unsigned long saved_asid = get_asid();
 
-               asid = init_mm.context.id & MMU_CONTEXT_ASID_MASK;
+               asid = cpu_asid(cpu, &init_mm);
                start &= PAGE_MASK;
                end += (PAGE_SIZE - 1);
                end &= PAGE_MASK;
@@ -103,15 +107,17 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end)
 
 void flush_tlb_mm(struct mm_struct *mm)
 {
+       unsigned int cpu = smp_processor_id();
+
        /* Invalidate all TLB of this process. */
        /* Instead of invalidating each TLB, we get new MMU context. */
-       if (mm->context.id != NO_CONTEXT) {
+       if (cpu_context(cpu, mm) != NO_CONTEXT) {
                unsigned long flags;
 
                local_irq_save(flags);
-               mm->context.id = NO_CONTEXT;
+               cpu_context(cpu, mm) = NO_CONTEXT;
                if (mm == current->mm)
-                       activate_context(mm);
+                       activate_context(mm, cpu);
                local_irq_restore(flags);
        }
 }
index cf47df79bb9415c43a310c16c3c93737ebc2c1d2..eb0358c097d01a3c236b83400e03aa2ac5b757b7 100644 (file)
@@ -1,25 +1,19 @@
 #ifndef __MMU_H
 #define __MMU_H
 
-#if !defined(CONFIG_MMU)
+/* Default "unsigned long" context */
+typedef unsigned long mm_context_id_t[NR_CPUS];
 
 typedef struct {
+#ifdef CONFIG_MMU
+       mm_context_id_t         id;
+       void                    *vdso;
+#else
        struct vm_list_struct   *vmlist;
        unsigned long           end_brk;
+#endif
 } mm_context_t;
 
-#else
-
-/* Default "unsigned long" context */
-typedef unsigned long mm_context_id_t;
-
-typedef struct {
-       mm_context_id_t id;
-       void *vdso;
-} mm_context_t;
-
-#endif /* CONFIG_MMU */
-
 /*
  * Privileged Space Mapping Buffer (PMB) definitions
  */
index 46f04e23bd45ae9133c90c811ec1808f7832a14c..342024425b7df7a484e32fadb8796f089dd111c9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 1999 Niibe Yutaka
- * Copyright (C) 2003 Paul Mundt
+ * Copyright (C) 2003 - 2006 Paul Mundt
  *
  * ASID handling idea taken from MIPS implementation.
  */
  *    (b) ASID (Address Space IDentifier)
  */
 
-/*
- * Cache of MMU context last used.
- */
-extern unsigned long mmu_context_cache;
-
 #define MMU_CONTEXT_ASID_MASK          0x000000ff
 #define MMU_CONTEXT_VERSION_MASK       0xffffff00
 #define MMU_CONTEXT_FIRST_VERSION      0x00000100
@@ -32,6 +27,11 @@ extern unsigned long mmu_context_cache;
 /* ASID is 8-bit value, so it can't be 0x100 */
 #define MMU_NO_ASID                    0x100
 
+#define cpu_context(cpu, mm)   ((mm)->context.id[cpu])
+#define cpu_asid(cpu, mm)      (cpu_context((cpu), (mm)) & \
+                                MMU_CONTEXT_ASID_MASK)
+#define asid_cache(cpu)                (cpu_data[cpu].asid_cache)
+
 /*
  * Virtual Page Number mask
  */
@@ -41,18 +41,17 @@ extern unsigned long mmu_context_cache;
 /*
  * Get MMU context if needed.
  */
-static inline void get_mmu_context(struct mm_struct *mm)
+static inline void get_mmu_context(struct mm_struct *mm, unsigned int cpu)
 {
-       unsigned long mc = mmu_context_cache;
+       unsigned long asid = asid_cache(cpu);
 
        /* Check if we have old version of context. */
-       if (((mm->context.id ^ mc) & MMU_CONTEXT_VERSION_MASK) == 0)
+       if (((cpu_context(cpu, mm) ^ asid) & MMU_CONTEXT_VERSION_MASK) == 0)
                /* It's up to date, do nothing */
                return;
 
        /* It's old, we need to get new context with new version. */
-       mc = ++mmu_context_cache;
-       if (!(mc & MMU_CONTEXT_ASID_MASK)) {
+       if (!(++asid & MMU_CONTEXT_ASID_MASK)) {
                /*
                 * We exhaust ASID of this version.
                 * Flush all TLB and start new cycle.
@@ -63,10 +62,11 @@ static inline void get_mmu_context(struct mm_struct *mm)
                 * Fix version; Note that we avoid version #0
                 * to distingush NO_CONTEXT.
                 */
-               if (!mc)
-                       mmu_context_cache = mc = MMU_CONTEXT_FIRST_VERSION;
+               if (!asid)
+                       asid = MMU_CONTEXT_FIRST_VERSION;
        }
-       mm->context.id = mc;
+
+       cpu_context(cpu, mm) = asid_cache(cpu) = asid;
 }
 
 /*
@@ -74,9 +74,13 @@ static inline void get_mmu_context(struct mm_struct *mm)
  * instance.
  */
 static inline int init_new_context(struct task_struct *tsk,
-                                      struct mm_struct *mm)
+                                  struct mm_struct *mm)
 {
-       mm->context.id = NO_CONTEXT;
+       int i;
+
+       for (i = 0; i < num_online_cpus(); i++)
+               cpu_context(i, mm) = NO_CONTEXT;
+
        return 0;
 }
 
@@ -117,10 +121,10 @@ static inline unsigned long get_asid(void)
  * After we have set current->mm to a new value, this activates
  * the context for the new mm so we see the new mappings.
  */
-static inline void activate_context(struct mm_struct *mm)
+static inline void activate_context(struct mm_struct *mm, unsigned int cpu)
 {
-       get_mmu_context(mm);
-       set_asid(mm->context.id & MMU_CONTEXT_ASID_MASK);
+       get_mmu_context(mm, cpu);
+       set_asid(cpu_asid(cpu, mm));
 }
 
 /* MMU_TTB is used for optimizing the fault handling. */
@@ -138,10 +142,15 @@ static inline void switch_mm(struct mm_struct *prev,
                             struct mm_struct *next,
                             struct task_struct *tsk)
 {
+       unsigned int cpu = smp_processor_id();
+
        if (likely(prev != next)) {
+               cpu_set(cpu, next->cpu_vm_mask);
                set_TTB(next->pgd);
-               activate_context(next);
-       }
+               activate_context(next, cpu);
+       } else
+               if (!cpu_test_and_set(cpu, next->cpu_vm_mask))
+                       activate_context(next, cpu);
 }
 
 #define deactivate_mm(tsk,mm)  do { } while (0)
@@ -159,7 +168,7 @@ enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
 #define destroy_context(mm)            do { } while (0)
 #define set_asid(asid)                 do { } while (0)
 #define get_asid()                     (0)
-#define activate_context(mm)           do { } while (0)
+#define activate_context(mm,cpu)       do { } while (0)
 #define switch_mm(prev,next,tsk)       do { } while (0)
 #define deactivate_mm(tsk,mm)          do { } while (0)
 #define activate_mm(prev,next)         do { } while (0)
@@ -174,14 +183,16 @@ enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
  */
 static inline void enable_mmu(void)
 {
+       unsigned int cpu = smp_processor_id();
+
        /* Enable MMU */
        ctrl_outl(MMU_CONTROL_INIT, MMUCR);
        ctrl_barrier();
 
-       if (mmu_context_cache == NO_CONTEXT)
-               mmu_context_cache = MMU_CONTEXT_FIRST_VERSION;
+       if (asid_cache(cpu) == NO_CONTEXT)
+               asid_cache(cpu) = MMU_CONTEXT_FIRST_VERSION;
 
-       set_asid(mmu_context_cache & MMU_CONTEXT_ASID_MASK);
+       set_asid(asid_cache(cpu) & MMU_CONTEXT_ASID_MASK);
 }
 
 static inline void disable_mmu(void)
index e29f2abb92defc7f9d24a6d8d4f0b3a9be7d365e..da229aae8e0fe485af3720e999197505aff88c99 100644 (file)
@@ -66,6 +66,7 @@ enum cpu_type {
 struct sh_cpuinfo {
        unsigned int type;
        unsigned long loops_per_jiffy;
+       unsigned long asid_cache;
 
        struct cache_info icache;       /* Primary I-cache */
        struct cache_info dcache;       /* Primary D-cache */