Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sh: Use a per-cpu ASID cache.

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>

+97 -93
+9 -2
arch/sh/kernel/cpu/init.c
··· 3 3 * 4 4 * CPU init code 5 5 * 6 - * Copyright (C) 2002, 2003 Paul Mundt 6 + * Copyright (C) 2002 - 2006 Paul Mundt 7 7 * Copyright (C) 2003 Richard Curnow 8 8 * 9 9 * This file is subject to the terms and conditions of the GNU General Public ··· 12 12 */ 13 13 #include <linux/init.h> 14 14 #include <linux/kernel.h> 15 + #include <linux/mm.h> 16 + #include <asm/mmu_context.h> 15 17 #include <asm/processor.h> 16 18 #include <asm/uaccess.h> 17 19 #include <asm/page.h> ··· 220 218 clear_used_math(); 221 219 } 222 220 221 + /* 222 + * Initialize the per-CPU ASID cache very early, since the 223 + * TLB flushing routines depend on this being setup. 224 + */ 225 + current_cpu_data.asid_cache = NO_CONTEXT; 226 + 223 227 #ifdef CONFIG_SH_DSP 224 228 /* Probe for DSP */ 225 229 dsp_init(); ··· 248 240 ubc_wakeup(); 249 241 #endif 250 242 } 251 -
+28 -38
arch/sh/kernel/process.c
··· 1 - /* $Id: process.c,v 1.28 2004/05/05 16:54:23 lethal Exp $ 1 + /* 2 + * arch/sh/kernel/process.c 2 3 * 3 - * linux/arch/sh/kernel/process.c 4 + * This file handles the architecture-dependent parts of process handling.. 4 5 * 5 6 * Copyright (C) 1995 Linus Torvalds 6 7 * 7 8 * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima 8 9 * Copyright (C) 2006 Lineo Solutions Inc. support SH4A UBC 10 + * Copyright (C) 2002 - 2006 Paul Mundt 9 11 */ 10 - 11 - /* 12 - * This file handles the architecture-dependent parts of process handling.. 13 - */ 14 - 15 12 #include <linux/module.h> 16 - #include <linux/unistd.h> 17 13 #include <linux/mm.h> 18 14 #include <linux/elfcore.h> 19 - #include <linux/a.out.h> 20 - #include <linux/slab.h> 21 15 #include <linux/pm.h> 22 - #include <linux/ptrace.h> 23 16 #include <linux/kallsyms.h> 24 17 #include <linux/kexec.h> 25 - 26 - #include <asm/io.h> 27 18 #include <asm/uaccess.h> 28 19 #include <asm/mmu_context.h> 29 - #include <asm/elf.h> 30 20 #include <asm/ubc.h> 31 21 32 - static int hlt_counter=0; 33 - 22 + static int hlt_counter; 34 23 int ubc_usercnt = 0; 35 24 36 25 #define HARD_IDLE_TIMEOUT (HZ / 3) 37 26 38 27 void (*pm_idle)(void); 39 - 40 28 void (*pm_power_off)(void); 41 29 EXPORT_SYMBOL(pm_power_off); 42 30 ··· 32 44 { 33 45 hlt_counter++; 34 46 } 35 - 36 47 EXPORT_SYMBOL(disable_hlt); 37 48 38 49 void enable_hlt(void) 39 50 { 40 51 hlt_counter--; 41 52 } 42 - 43 53 EXPORT_SYMBOL(enable_hlt); 44 54 45 55 void default_idle(void) ··· 138 152 ".align 2\n\t" 139 153 "1:.long do_exit"); 140 154 155 + /* Don't use this in BL=1(cli). Or else, CPU resets! */ 141 156 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) 142 - { /* Don't use this in BL=1(cli). Or else, CPU resets! */ 157 + { 143 158 struct pt_regs regs; 144 159 145 160 memset(&regs, 0, sizeof(regs)); 146 - regs.regs[4] = (unsigned long) arg; 147 - regs.regs[5] = (unsigned long) fn; 161 + regs.regs[4] = (unsigned long)arg; 162 + regs.regs[5] = (unsigned long)fn; 148 163 149 - regs.pc = (unsigned long) kernel_thread_helper; 164 + regs.pc = (unsigned long)kernel_thread_helper; 150 165 regs.sr = (1 << 30); 151 166 152 167 /* Ok, create the new process.. */ 153 - return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL); 168 + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, 169 + &regs, 0, NULL, NULL); 154 170 } 155 171 156 172 /* ··· 199 211 return fpvalid; 200 212 } 201 213 202 - /* 214 + /* 203 215 * Capture the user space registers if the task is not running (in user space) 204 216 */ 205 217 int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) 206 218 { 207 219 struct pt_regs ptregs; 208 - 220 + 209 221 ptregs = *task_pt_regs(tsk); 210 222 elf_core_copy_regs(regs, &ptregs); 211 223 212 224 return 1; 213 225 } 214 226 215 - int 216 - dump_task_fpu (struct task_struct *tsk, elf_fpregset_t *fpu) 227 + int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpu) 217 228 { 218 229 int fpvalid = 0; 219 230 ··· 250 263 childregs->regs[15] = usp; 251 264 ti->addr_limit = USER_DS; 252 265 } else { 253 - childregs->regs[15] = (unsigned long)task_stack_page(p) + THREAD_SIZE; 266 + childregs->regs[15] = (unsigned long)task_stack_page(p) + 267 + THREAD_SIZE; 254 268 ti->addr_limit = KERNEL_DS; 255 269 } 256 - if (clone_flags & CLONE_SETTLS) { 270 + 271 + if (clone_flags & CLONE_SETTLS) 257 272 childregs->gbr = childregs->regs[0]; 258 - } 273 + 259 274 childregs->regs[0] = 0; /* Set return value for child */ 260 275 261 276 p->thread.sp = (unsigned long) childregs; ··· 269 280 } 270 281 271 282 /* Tracing by user break controller. */ 272 - static void 273 - ubc_set_tracing(int asid, unsigned long pc) 283 + static void ubc_set_tracing(int asid, unsigned long pc) 274 284 { 275 285 #if defined(CONFIG_CPU_SH4A) 276 286 unsigned long val; ··· 285 297 val = (UBC_CRR_RES | UBC_CRR_PCB | UBC_CRR_BIE); 286 298 ctrl_outl(val, UBC_CRR0); 287 299 288 - /* Read UBC register that we writed last. For chekking UBC Register changed */ 300 + /* Read UBC register that we wrote last, for checking update */ 289 301 val = ctrl_inl(UBC_CRR0); 290 302 291 303 #else /* CONFIG_CPU_SH4A */ ··· 313 325 * switch_to(x,y) should switch tasks from x to y. 314 326 * 315 327 */ 316 - struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *next) 328 + struct task_struct *__switch_to(struct task_struct *prev, 329 + struct task_struct *next) 317 330 { 318 331 #if defined(CONFIG_SH_FPU) 319 332 unlazy_fpu(prev, task_pt_regs(prev)); ··· 343 354 #ifdef CONFIG_MMU 344 355 /* 345 356 * Restore the kernel mode register 346 - * k7 (r7_bank1) 357 + * k7 (r7_bank1) 347 358 */ 348 359 asm volatile("ldc %0, r7_bank" 349 360 : /* no output */ ··· 356 367 else if (next->thread.ubc_pc && next->mm) { 357 368 int asid = 0; 358 369 #ifdef CONFIG_MMU 359 - asid |= next->mm->context.id & MMU_CONTEXT_ASID_MASK; 370 + asid |= cpu_asid(smp_processor_id(), next->mm); 360 371 #endif 361 372 ubc_set_tracing(asid, next->thread.ubc_pc); 362 373 } else { ··· 394 405 if (!newsp) 395 406 newsp = regs->regs[15]; 396 407 return do_fork(clone_flags, newsp, regs, 0, 397 - (int __user *)parent_tidptr, (int __user *)child_tidptr); 408 + (int __user *)parent_tidptr, 409 + (int __user *)child_tidptr); 398 410 } 399 411 400 412 /*
-5
arch/sh/mm/init.c
··· 39 39 DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); 40 40 pgd_t swapper_pg_dir[PTRS_PER_PGD]; 41 41 42 - /* 43 - * Cache of MMU context last used. 44 - */ 45 - unsigned long mmu_context_cache = NO_CONTEXT; 46 - 47 42 #ifdef CONFIG_MMU 48 43 /* It'd be good if these lines were in the standard header file. */ 49 44 #define START_PFN (NODE_DATA(0)->bdata->node_boot_start >> PAGE_SHIFT)
+16 -10
arch/sh/mm/tlb-flush.c
··· 16 16 17 17 void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) 18 18 { 19 - if (vma->vm_mm && vma->vm_mm->context.id != NO_CONTEXT) { 19 + unsigned int cpu = smp_processor_id(); 20 + 21 + if (vma->vm_mm && cpu_context(cpu, vma->vm_mm) != NO_CONTEXT) { 20 22 unsigned long flags; 21 23 unsigned long asid; 22 24 unsigned long saved_asid = MMU_NO_ASID; 23 25 24 - asid = vma->vm_mm->context.id & MMU_CONTEXT_ASID_MASK; 26 + asid = cpu_asid(cpu, vma->vm_mm); 25 27 page &= PAGE_MASK; 26 28 27 29 local_irq_save(flags); ··· 42 40 unsigned long end) 43 41 { 44 42 struct mm_struct *mm = vma->vm_mm; 43 + unsigned int cpu = smp_processor_id(); 45 44 46 - if (mm->context.id != NO_CONTEXT) { 45 + if (cpu_context(cpu, mm) != NO_CONTEXT) { 47 46 unsigned long flags; 48 47 int size; 49 48 50 49 local_irq_save(flags); 51 50 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; 52 51 if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */ 53 - mm->context.id = NO_CONTEXT; 52 + cpu_context(cpu, mm) = NO_CONTEXT; 54 53 if (mm == current->mm) 55 - activate_context(mm); 54 + activate_context(mm, cpu); 56 55 } else { 57 56 unsigned long asid; 58 57 unsigned long saved_asid = MMU_NO_ASID; 59 58 60 - asid = mm->context.id & MMU_CONTEXT_ASID_MASK; 59 + asid = cpu_asid(cpu, mm); 61 60 start &= PAGE_MASK; 62 61 end += (PAGE_SIZE - 1); 63 62 end &= PAGE_MASK; ··· 79 76 80 77 void flush_tlb_kernel_range(unsigned long start, unsigned long end) 81 78 { 79 + unsigned int cpu = smp_processor_id(); 82 80 unsigned long flags; 83 81 int size; 84 82 ··· 91 87 unsigned long asid; 92 88 unsigned long saved_asid = get_asid(); 93 89 94 - asid = init_mm.context.id & MMU_CONTEXT_ASID_MASK; 90 + asid = cpu_asid(cpu, &init_mm); 95 91 start &= PAGE_MASK; 96 92 end += (PAGE_SIZE - 1); 97 93 end &= PAGE_MASK; ··· 107 103 108 104 void flush_tlb_mm(struct mm_struct *mm) 109 105 { 106 + unsigned int cpu = smp_processor_id(); 107 + 110 108 /* Invalidate all TLB of this process. */ 111 109 /* Instead of invalidating each TLB, we get new MMU context. */ 112 - if (mm->context.id != NO_CONTEXT) { 110 + if (cpu_context(cpu, mm) != NO_CONTEXT) { 113 111 unsigned long flags; 114 112 115 113 local_irq_save(flags); 116 - mm->context.id = NO_CONTEXT; 114 + cpu_context(cpu, mm) = NO_CONTEXT; 117 115 if (mm == current->mm) 118 - activate_context(mm); 116 + activate_context(mm, cpu); 119 117 local_irq_restore(flags); 120 118 } 121 119 }
+7 -13
include/asm-sh/mmu.h
··· 1 1 #ifndef __MMU_H 2 2 #define __MMU_H 3 3 4 - #if !defined(CONFIG_MMU) 4 + /* Default "unsigned long" context */ 5 + typedef unsigned long mm_context_id_t[NR_CPUS]; 5 6 6 7 typedef struct { 8 + #ifdef CONFIG_MMU 9 + mm_context_id_t id; 10 + void *vdso; 11 + #else 7 12 struct vm_list_struct *vmlist; 8 13 unsigned long end_brk; 14 + #endif 9 15 } mm_context_t; 10 - 11 - #else 12 - 13 - /* Default "unsigned long" context */ 14 - typedef unsigned long mm_context_id_t; 15 - 16 - typedef struct { 17 - mm_context_id_t id; 18 - void *vdso; 19 - } mm_context_t; 20 - 21 - #endif /* CONFIG_MMU */ 22 16 23 17 /* 24 18 * Privileged Space Mapping Buffer (PMB) definitions
+36 -25
include/asm-sh/mmu_context.h
··· 1 1 /* 2 2 * Copyright (C) 1999 Niibe Yutaka 3 - * Copyright (C) 2003 Paul Mundt 3 + * Copyright (C) 2003 - 2006 Paul Mundt 4 4 * 5 5 * ASID handling idea taken from MIPS implementation. 6 6 */ ··· 19 19 * (b) ASID (Address Space IDentifier) 20 20 */ 21 21 22 - /* 23 - * Cache of MMU context last used. 24 - */ 25 - extern unsigned long mmu_context_cache; 26 - 27 22 #define MMU_CONTEXT_ASID_MASK 0x000000ff 28 23 #define MMU_CONTEXT_VERSION_MASK 0xffffff00 29 24 #define MMU_CONTEXT_FIRST_VERSION 0x00000100 ··· 26 31 27 32 /* ASID is 8-bit value, so it can't be 0x100 */ 28 33 #define MMU_NO_ASID 0x100 34 + 35 + #define cpu_context(cpu, mm) ((mm)->context.id[cpu]) 36 + #define cpu_asid(cpu, mm) (cpu_context((cpu), (mm)) & \ 37 + MMU_CONTEXT_ASID_MASK) 38 + #define asid_cache(cpu) (cpu_data[cpu].asid_cache) 29 39 30 40 /* 31 41 * Virtual Page Number mask ··· 41 41 /* 42 42 * Get MMU context if needed. 43 43 */ 44 - static inline void get_mmu_context(struct mm_struct *mm) 44 + static inline void get_mmu_context(struct mm_struct *mm, unsigned int cpu) 45 45 { 46 - unsigned long mc = mmu_context_cache; 46 + unsigned long asid = asid_cache(cpu); 47 47 48 48 /* Check if we have old version of context. */ 49 - if (((mm->context.id ^ mc) & MMU_CONTEXT_VERSION_MASK) == 0) 49 + if (((cpu_context(cpu, mm) ^ asid) & MMU_CONTEXT_VERSION_MASK) == 0) 50 50 /* It's up to date, do nothing */ 51 51 return; 52 52 53 53 /* It's old, we need to get new context with new version. */ 54 - mc = ++mmu_context_cache; 55 - if (!(mc & MMU_CONTEXT_ASID_MASK)) { 54 + if (!(++asid & MMU_CONTEXT_ASID_MASK)) { 56 55 /* 57 56 * We exhaust ASID of this version. 58 57 * Flush all TLB and start new cycle. ··· 62 63 * Fix version; Note that we avoid version #0 63 64 * to distingush NO_CONTEXT. 64 65 */ 65 - if (!mc) 66 - mmu_context_cache = mc = MMU_CONTEXT_FIRST_VERSION; 66 + if (!asid) 67 + asid = MMU_CONTEXT_FIRST_VERSION; 67 68 } 68 - mm->context.id = mc; 69 + 70 + cpu_context(cpu, mm) = asid_cache(cpu) = asid; 69 71 } 70 72 71 73 /* ··· 74 74 * instance. 75 75 */ 76 76 static inline int init_new_context(struct task_struct *tsk, 77 - struct mm_struct *mm) 77 + struct mm_struct *mm) 78 78 { 79 - mm->context.id = NO_CONTEXT; 79 + int i; 80 + 81 + for (i = 0; i < num_online_cpus(); i++) 82 + cpu_context(i, mm) = NO_CONTEXT; 83 + 80 84 return 0; 81 85 } 82 86 ··· 121 117 * After we have set current->mm to a new value, this activates 122 118 * the context for the new mm so we see the new mappings. 123 119 */ 124 - static inline void activate_context(struct mm_struct *mm) 120 + static inline void activate_context(struct mm_struct *mm, unsigned int cpu) 125 121 { 126 - get_mmu_context(mm); 127 - set_asid(mm->context.id & MMU_CONTEXT_ASID_MASK); 122 + get_mmu_context(mm, cpu); 123 + set_asid(cpu_asid(cpu, mm)); 128 124 } 129 125 130 126 /* MMU_TTB is used for optimizing the fault handling. */ ··· 142 138 struct mm_struct *next, 143 139 struct task_struct *tsk) 144 140 { 141 + unsigned int cpu = smp_processor_id(); 142 + 145 143 if (likely(prev != next)) { 144 + cpu_set(cpu, next->cpu_vm_mask); 146 145 set_TTB(next->pgd); 147 - activate_context(next); 148 - } 146 + activate_context(next, cpu); 147 + } else 148 + if (!cpu_test_and_set(cpu, next->cpu_vm_mask)) 149 + activate_context(next, cpu); 149 150 } 150 151 151 152 #define deactivate_mm(tsk,mm) do { } while (0) ··· 168 159 #define destroy_context(mm) do { } while (0) 169 160 #define set_asid(asid) do { } while (0) 170 161 #define get_asid() (0) 171 - #define activate_context(mm) do { } while (0) 162 + #define activate_context(mm,cpu) do { } while (0) 172 163 #define switch_mm(prev,next,tsk) do { } while (0) 173 164 #define deactivate_mm(tsk,mm) do { } while (0) 174 165 #define activate_mm(prev,next) do { } while (0) ··· 183 174 */ 184 175 static inline void enable_mmu(void) 185 176 { 177 + unsigned int cpu = smp_processor_id(); 178 + 186 179 /* Enable MMU */ 187 180 ctrl_outl(MMU_CONTROL_INIT, MMUCR); 188 181 ctrl_barrier(); 189 182 190 - if (mmu_context_cache == NO_CONTEXT) 191 - mmu_context_cache = MMU_CONTEXT_FIRST_VERSION; 183 + if (asid_cache(cpu) == NO_CONTEXT) 184 + asid_cache(cpu) = MMU_CONTEXT_FIRST_VERSION; 192 185 193 - set_asid(mmu_context_cache & MMU_CONTEXT_ASID_MASK); 186 + set_asid(asid_cache(cpu) & MMU_CONTEXT_ASID_MASK); 194 187 } 195 188 196 189 static inline void disable_mmu(void)
+1
include/asm-sh/processor.h
··· 66 66 struct sh_cpuinfo { 67 67 unsigned int type; 68 68 unsigned long loops_per_jiffy; 69 + unsigned long asid_cache; 69 70 70 71 struct cache_info icache; /* Primary I-cache */ 71 72 struct cache_info dcache; /* Primary D-cache */