Blame view
arch/sh/kernel/smp.c
9.92 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 |
/* * arch/sh/kernel/smp.c * * SMP support for the SuperH processors. * |
3366e3585 sh: Move platform... |
6 |
* Copyright (C) 2002 - 2010 Paul Mundt |
aba1030a7 sh: Bring SMP sup... |
7 |
* Copyright (C) 2006 - 2007 Akio Idehara |
1da177e4c Linux-2.6.12-rc2 |
8 |
* |
aba1030a7 sh: Bring SMP sup... |
9 10 11 |
* 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 * for more details. |
1da177e4c Linux-2.6.12-rc2 |
12 |
*/ |
66c5227ec sh: trivial build... |
13 |
#include <linux/err.h> |
1da177e4c Linux-2.6.12-rc2 |
14 15 16 17 |
#include <linux/cache.h> #include <linux/cpumask.h> #include <linux/delay.h> #include <linux/init.h> |
1da177e4c Linux-2.6.12-rc2 |
18 |
#include <linux/spinlock.h> |
aba1030a7 sh: Bring SMP sup... |
19 |
#include <linux/mm.h> |
1da177e4c Linux-2.6.12-rc2 |
20 |
#include <linux/module.h> |
b56050ae5 sh: arch/sh/kerne... |
21 |
#include <linux/cpu.h> |
aba1030a7 sh: Bring SMP sup... |
22 |
#include <linux/interrupt.h> |
184748cc5 sched: Provide sc... |
23 |
#include <linux/sched.h> |
60063497a atomic: use <linu... |
24 |
#include <linux/atomic.h> |
1da177e4c Linux-2.6.12-rc2 |
25 26 27 28 |
#include <asm/processor.h> #include <asm/system.h> #include <asm/mmu_context.h> #include <asm/smp.h> |
aba1030a7 sh: Bring SMP sup... |
29 30 |
#include <asm/cacheflush.h> #include <asm/sections.h> |
1da177e4c Linux-2.6.12-rc2 |
31 |
|
aba1030a7 sh: Bring SMP sup... |
32 33 |
int __cpu_number_map[NR_CPUS]; /* Map physical to logical */ int __cpu_logical_map[NR_CPUS]; /* Map logical to physical */ |
1da177e4c Linux-2.6.12-rc2 |
34 |
|
3366e3585 sh: Move platform... |
35 |
struct plat_smp_ops *mp_ops = NULL; |
9715b8c7d sh: provide percp... |
36 37 |
/* State of each CPU */ DEFINE_PER_CPU(int, cpu_state) = { 0 }; |
3366e3585 sh: Move platform... |
38 39 40 41 42 43 44 45 |
void __cpuinit register_smp_ops(struct plat_smp_ops *ops) { if (mp_ops) printk(KERN_WARNING "Overriding previously set SMP ops "); mp_ops = ops; } |
1cfa1e8f2 sh: flag smp_stor... |
46 |
static inline void __cpuinit smp_store_cpu_info(unsigned int cpu) |
1da177e4c Linux-2.6.12-rc2 |
47 |
{ |
aba1030a7 sh: Bring SMP sup... |
48 |
struct sh_cpuinfo *c = cpu_data + cpu; |
a66c2edea sh: Populate init... |
49 |
memcpy(c, &boot_cpu_data, sizeof(struct sh_cpuinfo)); |
aba1030a7 sh: Bring SMP sup... |
50 |
c->loops_per_jiffy = loops_per_jiffy; |
1da177e4c Linux-2.6.12-rc2 |
51 52 53 54 55 |
} void __init smp_prepare_cpus(unsigned int max_cpus) { unsigned int cpu = smp_processor_id(); |
1da177e4c Linux-2.6.12-rc2 |
56 |
|
aba1030a7 sh: Bring SMP sup... |
57 58 |
init_new_context(current, &init_mm); current_thread_info()->cpu = cpu; |
3366e3585 sh: Move platform... |
59 |
mp_ops->prepare_cpus(max_cpus); |
aba1030a7 sh: Bring SMP sup... |
60 61 |
#ifndef CONFIG_HOTPLUG_CPU |
e09377bae cpumask: Use acce... |
62 |
init_cpu_present(&cpu_possible_map); |
aba1030a7 sh: Bring SMP sup... |
63 |
#endif |
1da177e4c Linux-2.6.12-rc2 |
64 |
} |
1cfa1e8f2 sh: flag smp_stor... |
65 |
void __init smp_prepare_boot_cpu(void) |
1da177e4c Linux-2.6.12-rc2 |
66 67 |
{ unsigned int cpu = smp_processor_id(); |
aba1030a7 sh: Bring SMP sup... |
68 69 |
__cpu_number_map[0] = cpu; __cpu_logical_map[0] = cpu; |
e09377bae cpumask: Use acce... |
70 71 |
set_cpu_online(cpu, true); set_cpu_possible(cpu, true); |
9715b8c7d sh: provide percp... |
72 73 |
per_cpu(cpu_state, cpu) = CPU_ONLINE; |
1da177e4c Linux-2.6.12-rc2 |
74 |
} |
763142d1e sh: CPU hotplug s... |
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
#ifdef CONFIG_HOTPLUG_CPU void native_cpu_die(unsigned int cpu) { unsigned int i; for (i = 0; i < 10; i++) { smp_rmb(); if (per_cpu(cpu_state, cpu) == CPU_DEAD) { if (system_state == SYSTEM_RUNNING) pr_info("CPU %u is now offline ", cpu); return; } msleep(100); } pr_err("CPU %u didn't die... ", cpu); } int native_cpu_disable(unsigned int cpu) { return cpu == 0 ? -EPERM : 0; } void play_dead_common(void) { idle_task_exit(); irq_ctx_exit(raw_smp_processor_id()); mb(); __get_cpu_var(cpu_state) = CPU_DEAD; local_irq_disable(); } void native_play_dead(void) { play_dead_common(); } int __cpu_disable(void) { unsigned int cpu = smp_processor_id(); struct task_struct *p; int ret; ret = mp_ops->cpu_disable(cpu); if (ret) return ret; /* * Take this CPU offline. Once we clear this, we can't return, * and we must not schedule until we're ready to give up the cpu. */ set_cpu_online(cpu, false); /* * OK - migrate IRQs away from this CPU */ migrate_irqs(); /* * Stop the local timer for this CPU. */ local_timer_stop(cpu); /* * Flush user cache and TLB mappings, and then remove this CPU * from the vm mask set of all processes. */ flush_cache_all(); local_flush_tlb_all(); read_lock(&tasklist_lock); for_each_process(p) if (p->mm) cpumask_clear_cpu(cpu, mm_cpumask(p->mm)); read_unlock(&tasklist_lock); return 0; } #else /* ... !CONFIG_HOTPLUG_CPU */ |
1483feac7 sh: native_cpu_di... |
159 |
int native_cpu_disable(unsigned int cpu) |
763142d1e sh: CPU hotplug s... |
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
{ return -ENOSYS; } void native_cpu_die(unsigned int cpu) { /* We said "no" in __cpu_disable */ BUG(); } void native_play_dead(void) { BUG(); } #endif |
aba1030a7 sh: Bring SMP sup... |
175 |
asmlinkage void __cpuinit start_secondary(void) |
1da177e4c Linux-2.6.12-rc2 |
176 |
{ |
9715b8c7d sh: provide percp... |
177 |
unsigned int cpu = smp_processor_id(); |
aba1030a7 sh: Bring SMP sup... |
178 |
struct mm_struct *mm = &init_mm; |
1da177e4c Linux-2.6.12-rc2 |
179 |
|
4bea3418c sh: Enable the mm... |
180 |
enable_mmu(); |
aba1030a7 sh: Bring SMP sup... |
181 182 183 |
atomic_inc(&mm->mm_count); atomic_inc(&mm->mm_users); current->active_mm = mm; |
aba1030a7 sh: Bring SMP sup... |
184 |
enter_lazy_tlb(mm, current); |
763142d1e sh: CPU hotplug s... |
185 |
local_flush_tlb_all(); |
aba1030a7 sh: Bring SMP sup... |
186 187 188 189 |
per_cpu_trap_init(); preempt_disable(); |
9715b8c7d sh: provide percp... |
190 |
notify_cpu_starting(cpu); |
e545a6140 kernel/cpu.c: cre... |
191 |
|
aba1030a7 sh: Bring SMP sup... |
192 |
local_irq_enable(); |
1da177e4c Linux-2.6.12-rc2 |
193 |
|
8c24594de sh: generic clock... |
194 195 |
/* Enable local timers */ local_timer_setup(cpu); |
aba1030a7 sh: Bring SMP sup... |
196 |
calibrate_delay(); |
aba1030a7 sh: Bring SMP sup... |
197 |
smp_store_cpu_info(cpu); |
1da177e4c Linux-2.6.12-rc2 |
198 |
|
f0ccf2770 sh: convert onlin... |
199 |
set_cpu_online(cpu, true); |
9715b8c7d sh: provide percp... |
200 |
per_cpu(cpu_state, cpu) = CPU_ONLINE; |
1da177e4c Linux-2.6.12-rc2 |
201 |
|
aba1030a7 sh: Bring SMP sup... |
202 |
cpu_idle(); |
1da177e4c Linux-2.6.12-rc2 |
203 |
} |
aba1030a7 sh: Bring SMP sup... |
204 205 206 207 208 209 210 211 212 213 |
extern struct { unsigned long sp; unsigned long bss_start; unsigned long bss_end; void *start_kernel_fn; void *cpu_init_fn; void *thread_info; } stack_start; int __cpuinit __cpu_up(unsigned int cpu) |
1da177e4c Linux-2.6.12-rc2 |
214 |
{ |
aba1030a7 sh: Bring SMP sup... |
215 216 |
struct task_struct *tsk; unsigned long timeout; |
5bfb5d690 [PATCH] sched: di... |
217 |
|
8db2bc455 sh: cache seconda... |
218 219 220 221 222 223 224 225 226 227 |
tsk = cpu_data[cpu].idle; if (!tsk) { tsk = fork_idle(cpu); if (IS_ERR(tsk)) { pr_err("Failed forking idle task for cpu %d ", cpu); return PTR_ERR(tsk); } cpu_data[cpu].idle = tsk; |
aba1030a7 sh: Bring SMP sup... |
228 |
} |
1da177e4c Linux-2.6.12-rc2 |
229 |
|
9715b8c7d sh: provide percp... |
230 |
per_cpu(cpu_state, cpu) = CPU_UP_PREPARE; |
aba1030a7 sh: Bring SMP sup... |
231 232 233 234 235 |
/* Fill in data in head.S for secondary cpus */ stack_start.sp = tsk->thread.sp; stack_start.thread_info = tsk->stack; stack_start.bss_start = 0; /* don't clear bss for secondary cpus */ stack_start.start_kernel_fn = start_secondary; |
1da177e4c Linux-2.6.12-rc2 |
236 |
|
d780613ac sh: Only invalida... |
237 238 239 |
flush_icache_range((unsigned long)&stack_start, (unsigned long)&stack_start + sizeof(stack_start)); wmb(); |
1da177e4c Linux-2.6.12-rc2 |
240 |
|
3366e3585 sh: Move platform... |
241 |
mp_ops->start_cpu(cpu, (unsigned long)_stext); |
1da177e4c Linux-2.6.12-rc2 |
242 |
|
aba1030a7 sh: Bring SMP sup... |
243 244 245 246 247 248 |
timeout = jiffies + HZ; while (time_before(jiffies, timeout)) { if (cpu_online(cpu)) break; udelay(10); |
763142d1e sh: CPU hotplug s... |
249 |
barrier(); |
aba1030a7 sh: Bring SMP sup... |
250 251 252 253 254 255 |
} if (cpu_online(cpu)) return 0; return -ENOENT; |
1da177e4c Linux-2.6.12-rc2 |
256 257 258 259 |
} void __init smp_cpus_done(unsigned int max_cpus) { |
aba1030a7 sh: Bring SMP sup... |
260 261 262 263 264 265 266 267 268 269 270 |
unsigned long bogosum = 0; int cpu; for_each_online_cpu(cpu) bogosum += cpu_data[cpu].loops_per_jiffy; printk(KERN_INFO "SMP: Total of %d processors activated " "(%lu.%02lu BogoMIPS). ", num_online_cpus(), bogosum / (500000/HZ), (bogosum / (5000/HZ)) % 100); |
1da177e4c Linux-2.6.12-rc2 |
271 272 273 274 |
} void smp_send_reschedule(int cpu) { |
3366e3585 sh: Move platform... |
275 |
mp_ops->send_ipi(cpu, SMP_MSG_RESCHEDULE); |
1da177e4c Linux-2.6.12-rc2 |
276 |
} |
1da177e4c Linux-2.6.12-rc2 |
277 278 |
void smp_send_stop(void) { |
8691e5a8f smp_call_function... |
279 |
smp_call_function(stop_this_cpu, 0, 0); |
1da177e4c Linux-2.6.12-rc2 |
280 |
} |
819807df6 cpumask: arch_sen... |
281 |
void arch_send_call_function_ipi_mask(const struct cpumask *mask) |
1da177e4c Linux-2.6.12-rc2 |
282 |
{ |
490f5de52 sh: convert to ge... |
283 |
int cpu; |
1da177e4c Linux-2.6.12-rc2 |
284 |
|
819807df6 cpumask: arch_sen... |
285 |
for_each_cpu(cpu, mask) |
3366e3585 sh: Move platform... |
286 |
mp_ops->send_ipi(cpu, SMP_MSG_FUNCTION); |
490f5de52 sh: convert to ge... |
287 |
} |
1da177e4c Linux-2.6.12-rc2 |
288 |
|
490f5de52 sh: convert to ge... |
289 290 |
void arch_send_call_function_single_ipi(int cpu) { |
3366e3585 sh: Move platform... |
291 |
mp_ops->send_ipi(cpu, SMP_MSG_FUNCTION_SINGLE); |
1da177e4c Linux-2.6.12-rc2 |
292 |
} |
320ab2b0b cpumask: convert ... |
293 |
void smp_timer_broadcast(const struct cpumask *mask) |
6f52707e6 sh: smp: Hook up ... |
294 295 |
{ int cpu; |
320ab2b0b cpumask: convert ... |
296 |
for_each_cpu(cpu, mask) |
3366e3585 sh: Move platform... |
297 |
mp_ops->send_ipi(cpu, SMP_MSG_TIMER); |
6f52707e6 sh: smp: Hook up ... |
298 299 300 301 302 |
} static void ipi_timer(void) { irq_enter(); |
8c24594de sh: generic clock... |
303 |
local_timer_interrupt(); |
6f52707e6 sh: smp: Hook up ... |
304 305 |
irq_exit(); } |
173a44dd1 sh: smp: Provide ... |
306 307 308 309 310 311 312 |
void smp_message_recv(unsigned int msg) { switch (msg) { case SMP_MSG_FUNCTION: generic_smp_call_function_interrupt(); break; case SMP_MSG_RESCHEDULE: |
184748cc5 sched: Provide sc... |
313 |
scheduler_ipi(); |
173a44dd1 sh: smp: Provide ... |
314 315 316 317 |
break; case SMP_MSG_FUNCTION_SINGLE: generic_smp_call_function_single_interrupt(); break; |
6f52707e6 sh: smp: Hook up ... |
318 319 320 |
case SMP_MSG_TIMER: ipi_timer(); break; |
173a44dd1 sh: smp: Provide ... |
321 322 323 324 325 326 327 |
default: printk(KERN_WARNING "SMP %d: %s(): unknown IPI %d ", smp_processor_id(), __func__, msg); break; } } |
1da177e4c Linux-2.6.12-rc2 |
328 329 330 331 332 |
/* Not really SMP stuff ... */ int setup_profiling_timer(unsigned int multiplier) { return 0; } |
9964fa8bf sh: Add SMP tlbfl... |
333 334 335 336 337 338 339 |
static void flush_tlb_all_ipi(void *info) { local_flush_tlb_all(); } void flush_tlb_all(void) { |
15c8b6c1a on_each_cpu(): ki... |
340 |
on_each_cpu(flush_tlb_all_ipi, 0, 1); |
9964fa8bf sh: Add SMP tlbfl... |
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
} static void flush_tlb_mm_ipi(void *mm) { local_flush_tlb_mm((struct mm_struct *)mm); } /* * The following tlb flush calls are invoked when old translations are * being torn down, or pte attributes are changing. For single threaded * address spaces, a new context is obtained on the current cpu, and tlb * context on other cpus are invalidated to force a new context allocation * at switch_mm time, should the mm ever be used on other cpus. For * multithreaded address spaces, intercpu interrupts have to be sent. * Another case where intercpu interrupts are required is when the target * mm might be active on another cpu (eg debuggers doing the flushes on * behalf of debugees, kswapd stealing pages from another process etc). * Kanoj 07/00. */ |
9964fa8bf sh: Add SMP tlbfl... |
360 361 362 363 364 |
void flush_tlb_mm(struct mm_struct *mm) { preempt_disable(); if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { |
8691e5a8f smp_call_function... |
365 |
smp_call_function(flush_tlb_mm_ipi, (void *)mm, 1); |
9964fa8bf sh: Add SMP tlbfl... |
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
} else { int i; for (i = 0; i < num_online_cpus(); i++) if (smp_processor_id() != i) cpu_context(i, mm) = 0; } local_flush_tlb_mm(mm); preempt_enable(); } struct flush_tlb_data { struct vm_area_struct *vma; unsigned long addr1; unsigned long addr2; }; static void flush_tlb_range_ipi(void *info) { struct flush_tlb_data *fd = (struct flush_tlb_data *)info; local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2); } void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { struct mm_struct *mm = vma->vm_mm; preempt_disable(); if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { struct flush_tlb_data fd; fd.vma = vma; fd.addr1 = start; fd.addr2 = end; |
8691e5a8f smp_call_function... |
402 |
smp_call_function(flush_tlb_range_ipi, (void *)&fd, 1); |
9964fa8bf sh: Add SMP tlbfl... |
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
} else { int i; for (i = 0; i < num_online_cpus(); i++) if (smp_processor_id() != i) cpu_context(i, mm) = 0; } local_flush_tlb_range(vma, start, end); preempt_enable(); } static void flush_tlb_kernel_range_ipi(void *info) { struct flush_tlb_data *fd = (struct flush_tlb_data *)info; local_flush_tlb_kernel_range(fd->addr1, fd->addr2); } void flush_tlb_kernel_range(unsigned long start, unsigned long end) { struct flush_tlb_data fd; fd.addr1 = start; fd.addr2 = end; |
15c8b6c1a on_each_cpu(): ki... |
426 |
on_each_cpu(flush_tlb_kernel_range_ipi, (void *)&fd, 1); |
9964fa8bf sh: Add SMP tlbfl... |
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
} static void flush_tlb_page_ipi(void *info) { struct flush_tlb_data *fd = (struct flush_tlb_data *)info; local_flush_tlb_page(fd->vma, fd->addr1); } void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { preempt_disable(); if ((atomic_read(&vma->vm_mm->mm_users) != 1) || (current->mm != vma->vm_mm)) { struct flush_tlb_data fd; fd.vma = vma; fd.addr1 = page; |
8691e5a8f smp_call_function... |
445 |
smp_call_function(flush_tlb_page_ipi, (void *)&fd, 1); |
9964fa8bf sh: Add SMP tlbfl... |
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
} else { int i; for (i = 0; i < num_online_cpus(); i++) if (smp_processor_id() != i) cpu_context(i, vma->vm_mm) = 0; } local_flush_tlb_page(vma, page); preempt_enable(); } static void flush_tlb_one_ipi(void *info) { struct flush_tlb_data *fd = (struct flush_tlb_data *)info; local_flush_tlb_one(fd->addr1, fd->addr2); } void flush_tlb_one(unsigned long asid, unsigned long vaddr) { struct flush_tlb_data fd; fd.addr1 = asid; fd.addr2 = vaddr; |
8691e5a8f smp_call_function... |
468 |
smp_call_function(flush_tlb_one_ipi, (void *)&fd, 1); |
9964fa8bf sh: Add SMP tlbfl... |
469 470 |
local_flush_tlb_one(asid, vaddr); } |