diff options
Diffstat (limited to 'arch/powerpc/kernel/smp.c')
-rw-r--r-- | arch/powerpc/kernel/smp.c | 153 |
1 files changed, 97 insertions, 56 deletions
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 981360509172..cbdbb14be4b0 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c | |||
@@ -57,6 +57,25 @@ | |||
57 | #define DBG(fmt...) | 57 | #define DBG(fmt...) |
58 | #endif | 58 | #endif |
59 | 59 | ||
60 | |||
61 | /* Store all idle threads, this can be reused instead of creating | ||
62 | * a new thread. Also avoids complicated thread destroy functionality | ||
63 | * for idle threads. | ||
64 | */ | ||
65 | #ifdef CONFIG_HOTPLUG_CPU | ||
66 | /* | ||
67 | * Needed only for CONFIG_HOTPLUG_CPU because __cpuinitdata is | ||
68 | * removed after init for !CONFIG_HOTPLUG_CPU. | ||
69 | */ | ||
70 | static DEFINE_PER_CPU(struct task_struct *, idle_thread_array); | ||
71 | #define get_idle_for_cpu(x) (per_cpu(idle_thread_array, x)) | ||
72 | #define set_idle_for_cpu(x, p) (per_cpu(idle_thread_array, x) = (p)) | ||
73 | #else | ||
74 | static struct task_struct *idle_thread_array[NR_CPUS] __cpuinitdata ; | ||
75 | #define get_idle_for_cpu(x) (idle_thread_array[(x)]) | ||
76 | #define set_idle_for_cpu(x, p) (idle_thread_array[(x)] = (p)) | ||
77 | #endif | ||
78 | |||
60 | struct thread_info *secondary_ti; | 79 | struct thread_info *secondary_ti; |
61 | 80 | ||
62 | DEFINE_PER_CPU(cpumask_var_t, cpu_sibling_map); | 81 | DEFINE_PER_CPU(cpumask_var_t, cpu_sibling_map); |
@@ -238,23 +257,6 @@ static void __devinit smp_store_cpu_info(int id) | |||
238 | per_cpu(cpu_pvr, id) = mfspr(SPRN_PVR); | 257 | per_cpu(cpu_pvr, id) = mfspr(SPRN_PVR); |
239 | } | 258 | } |
240 | 259 | ||
241 | static void __init smp_create_idle(unsigned int cpu) | ||
242 | { | ||
243 | struct task_struct *p; | ||
244 | |||
245 | /* create a process for the processor */ | ||
246 | p = fork_idle(cpu); | ||
247 | if (IS_ERR(p)) | ||
248 | panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p)); | ||
249 | #ifdef CONFIG_PPC64 | ||
250 | paca[cpu].__current = p; | ||
251 | paca[cpu].kstack = (unsigned long) task_thread_info(p) | ||
252 | + THREAD_SIZE - STACK_FRAME_OVERHEAD; | ||
253 | #endif | ||
254 | current_set[cpu] = task_thread_info(p); | ||
255 | task_thread_info(p)->cpu = cpu; | ||
256 | } | ||
257 | |||
258 | void __init smp_prepare_cpus(unsigned int max_cpus) | 260 | void __init smp_prepare_cpus(unsigned int max_cpus) |
259 | { | 261 | { |
260 | unsigned int cpu; | 262 | unsigned int cpu; |
@@ -288,10 +290,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus) | |||
288 | max_cpus = NR_CPUS; | 290 | max_cpus = NR_CPUS; |
289 | else | 291 | else |
290 | max_cpus = 1; | 292 | max_cpus = 1; |
291 | |||
292 | for_each_possible_cpu(cpu) | ||
293 | if (cpu != boot_cpuid) | ||
294 | smp_create_idle(cpu); | ||
295 | } | 293 | } |
296 | 294 | ||
297 | void __devinit smp_prepare_boot_cpu(void) | 295 | void __devinit smp_prepare_boot_cpu(void) |
@@ -305,7 +303,7 @@ void __devinit smp_prepare_boot_cpu(void) | |||
305 | 303 | ||
306 | #ifdef CONFIG_HOTPLUG_CPU | 304 | #ifdef CONFIG_HOTPLUG_CPU |
307 | /* State of each CPU during hotplug phases */ | 305 | /* State of each CPU during hotplug phases */ |
308 | DEFINE_PER_CPU(int, cpu_state) = { 0 }; | 306 | static DEFINE_PER_CPU(int, cpu_state) = { 0 }; |
309 | 307 | ||
310 | int generic_cpu_disable(void) | 308 | int generic_cpu_disable(void) |
311 | { | 309 | { |
@@ -317,30 +315,8 @@ int generic_cpu_disable(void) | |||
317 | set_cpu_online(cpu, false); | 315 | set_cpu_online(cpu, false); |
318 | #ifdef CONFIG_PPC64 | 316 | #ifdef CONFIG_PPC64 |
319 | vdso_data->processorCount--; | 317 | vdso_data->processorCount--; |
320 | fixup_irqs(cpu_online_mask); | ||
321 | #endif | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | int generic_cpu_enable(unsigned int cpu) | ||
326 | { | ||
327 | /* Do the normal bootup if we haven't | ||
328 | * already bootstrapped. */ | ||
329 | if (system_state != SYSTEM_RUNNING) | ||
330 | return -ENOSYS; | ||
331 | |||
332 | /* get the target out of it's holding state */ | ||
333 | per_cpu(cpu_state, cpu) = CPU_UP_PREPARE; | ||
334 | smp_wmb(); | ||
335 | |||
336 | while (!cpu_online(cpu)) | ||
337 | cpu_relax(); | ||
338 | |||
339 | #ifdef CONFIG_PPC64 | ||
340 | fixup_irqs(cpu_online_mask); | ||
341 | /* counter the irq disable in fixup_irqs */ | ||
342 | local_irq_enable(); | ||
343 | #endif | 318 | #endif |
319 | migrate_irqs(); | ||
344 | return 0; | 320 | return 0; |
345 | } | 321 | } |
346 | 322 | ||
@@ -362,37 +338,89 @@ void generic_mach_cpu_die(void) | |||
362 | unsigned int cpu; | 338 | unsigned int cpu; |
363 | 339 | ||
364 | local_irq_disable(); | 340 | local_irq_disable(); |
341 | idle_task_exit(); | ||
365 | cpu = smp_processor_id(); | 342 | cpu = smp_processor_id(); |
366 | printk(KERN_DEBUG "CPU%d offline\n", cpu); | 343 | printk(KERN_DEBUG "CPU%d offline\n", cpu); |
367 | __get_cpu_var(cpu_state) = CPU_DEAD; | 344 | __get_cpu_var(cpu_state) = CPU_DEAD; |
368 | smp_wmb(); | 345 | smp_wmb(); |
369 | while (__get_cpu_var(cpu_state) != CPU_UP_PREPARE) | 346 | while (__get_cpu_var(cpu_state) != CPU_UP_PREPARE) |
370 | cpu_relax(); | 347 | cpu_relax(); |
371 | set_cpu_online(cpu, true); | 348 | } |
372 | local_irq_enable(); | 349 | |
350 | void generic_set_cpu_dead(unsigned int cpu) | ||
351 | { | ||
352 | per_cpu(cpu_state, cpu) = CPU_DEAD; | ||
373 | } | 353 | } |
374 | #endif | 354 | #endif |
375 | 355 | ||
376 | static int __devinit cpu_enable(unsigned int cpu) | 356 | struct create_idle { |
357 | struct work_struct work; | ||
358 | struct task_struct *idle; | ||
359 | struct completion done; | ||
360 | int cpu; | ||
361 | }; | ||
362 | |||
363 | static void __cpuinit do_fork_idle(struct work_struct *work) | ||
377 | { | 364 | { |
378 | if (smp_ops && smp_ops->cpu_enable) | 365 | struct create_idle *c_idle = |
379 | return smp_ops->cpu_enable(cpu); | 366 | container_of(work, struct create_idle, work); |
367 | |||
368 | c_idle->idle = fork_idle(c_idle->cpu); | ||
369 | complete(&c_idle->done); | ||
370 | } | ||
371 | |||
372 | static int __cpuinit create_idle(unsigned int cpu) | ||
373 | { | ||
374 | struct thread_info *ti; | ||
375 | struct create_idle c_idle = { | ||
376 | .cpu = cpu, | ||
377 | .done = COMPLETION_INITIALIZER_ONSTACK(c_idle.done), | ||
378 | }; | ||
379 | INIT_WORK_ONSTACK(&c_idle.work, do_fork_idle); | ||
380 | |||
381 | c_idle.idle = get_idle_for_cpu(cpu); | ||
382 | |||
383 | /* We can't use kernel_thread since we must avoid to | ||
384 | * reschedule the child. We use a workqueue because | ||
385 | * we want to fork from a kernel thread, not whatever | ||
386 | * userspace process happens to be trying to online us. | ||
387 | */ | ||
388 | if (!c_idle.idle) { | ||
389 | schedule_work(&c_idle.work); | ||
390 | wait_for_completion(&c_idle.done); | ||
391 | } else | ||
392 | init_idle(c_idle.idle, cpu); | ||
393 | if (IS_ERR(c_idle.idle)) { | ||
394 | pr_err("Failed fork for CPU %u: %li", cpu, PTR_ERR(c_idle.idle)); | ||
395 | return PTR_ERR(c_idle.idle); | ||
396 | } | ||
397 | ti = task_thread_info(c_idle.idle); | ||
398 | |||
399 | #ifdef CONFIG_PPC64 | ||
400 | paca[cpu].__current = c_idle.idle; | ||
401 | paca[cpu].kstack = (unsigned long)ti + THREAD_SIZE - STACK_FRAME_OVERHEAD; | ||
402 | #endif | ||
403 | ti->cpu = cpu; | ||
404 | current_set[cpu] = ti; | ||
380 | 405 | ||
381 | return -ENOSYS; | 406 | return 0; |
382 | } | 407 | } |
383 | 408 | ||
384 | int __cpuinit __cpu_up(unsigned int cpu) | 409 | int __cpuinit __cpu_up(unsigned int cpu) |
385 | { | 410 | { |
386 | int c; | 411 | int rc, c; |
387 | 412 | ||
388 | secondary_ti = current_set[cpu]; | 413 | secondary_ti = current_set[cpu]; |
389 | if (!cpu_enable(cpu)) | ||
390 | return 0; | ||
391 | 414 | ||
392 | if (smp_ops == NULL || | 415 | if (smp_ops == NULL || |
393 | (smp_ops->cpu_bootable && !smp_ops->cpu_bootable(cpu))) | 416 | (smp_ops->cpu_bootable && !smp_ops->cpu_bootable(cpu))) |
394 | return -EINVAL; | 417 | return -EINVAL; |
395 | 418 | ||
419 | /* Make sure we have an idle thread */ | ||
420 | rc = create_idle(cpu); | ||
421 | if (rc) | ||
422 | return rc; | ||
423 | |||
396 | /* Make sure callin-map entry is 0 (can be leftover a CPU | 424 | /* Make sure callin-map entry is 0 (can be leftover a CPU |
397 | * hotplug | 425 | * hotplug |
398 | */ | 426 | */ |
@@ -502,7 +530,7 @@ static struct device_node *cpu_to_l2cache(int cpu) | |||
502 | } | 530 | } |
503 | 531 | ||
504 | /* Activate a secondary processor. */ | 532 | /* Activate a secondary processor. */ |
505 | int __devinit start_secondary(void *unused) | 533 | void __devinit start_secondary(void *unused) |
506 | { | 534 | { |
507 | unsigned int cpu = smp_processor_id(); | 535 | unsigned int cpu = smp_processor_id(); |
508 | struct device_node *l2_cache; | 536 | struct device_node *l2_cache; |
@@ -523,6 +551,10 @@ int __devinit start_secondary(void *unused) | |||
523 | 551 | ||
524 | secondary_cpu_time_init(); | 552 | secondary_cpu_time_init(); |
525 | 553 | ||
554 | #ifdef CONFIG_PPC64 | ||
555 | if (system_state == SYSTEM_RUNNING) | ||
556 | vdso_data->processorCount++; | ||
557 | #endif | ||
526 | ipi_call_lock(); | 558 | ipi_call_lock(); |
527 | notify_cpu_starting(cpu); | 559 | notify_cpu_starting(cpu); |
528 | set_cpu_online(cpu, true); | 560 | set_cpu_online(cpu, true); |
@@ -558,7 +590,8 @@ int __devinit start_secondary(void *unused) | |||
558 | local_irq_enable(); | 590 | local_irq_enable(); |
559 | 591 | ||
560 | cpu_idle(); | 592 | cpu_idle(); |
561 | return 0; | 593 | |
594 | BUG(); | ||
562 | } | 595 | } |
563 | 596 | ||
564 | int setup_profiling_timer(unsigned int multiplier) | 597 | int setup_profiling_timer(unsigned int multiplier) |
@@ -585,7 +618,11 @@ void __init smp_cpus_done(unsigned int max_cpus) | |||
585 | 618 | ||
586 | free_cpumask_var(old_mask); | 619 | free_cpumask_var(old_mask); |
587 | 620 | ||
621 | if (smp_ops && smp_ops->bringup_done) | ||
622 | smp_ops->bringup_done(); | ||
623 | |||
588 | dump_numa_cpu_topology(); | 624 | dump_numa_cpu_topology(); |
625 | |||
589 | } | 626 | } |
590 | 627 | ||
591 | int arch_sd_sibling_asym_packing(void) | 628 | int arch_sd_sibling_asym_packing(void) |
@@ -660,5 +697,9 @@ void cpu_die(void) | |||
660 | { | 697 | { |
661 | if (ppc_md.cpu_die) | 698 | if (ppc_md.cpu_die) |
662 | ppc_md.cpu_die(); | 699 | ppc_md.cpu_die(); |
700 | |||
701 | /* If we return, we re-enter start_secondary */ | ||
702 | start_secondary_resume(); | ||
663 | } | 703 | } |
704 | |||
664 | #endif | 705 | #endif |