diff options
Diffstat (limited to 'arch/arm/kernel/smp.c')
-rw-r--r-- | arch/arm/kernel/smp.c | 81 |
1 files changed, 71 insertions, 10 deletions
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 511eb0397c1..e895f97ab00 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c | |||
@@ -53,6 +53,7 @@ enum ipi_msg_type { | |||
53 | IPI_CALL_FUNC, | 53 | IPI_CALL_FUNC, |
54 | IPI_CALL_FUNC_SINGLE, | 54 | IPI_CALL_FUNC_SINGLE, |
55 | IPI_CPU_STOP, | 55 | IPI_CPU_STOP, |
56 | IPI_CPU_BACKTRACE, | ||
56 | }; | 57 | }; |
57 | 58 | ||
58 | int __cpuinit __cpu_up(unsigned int cpu) | 59 | int __cpuinit __cpu_up(unsigned int cpu) |
@@ -307,17 +308,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void) | |||
307 | */ | 308 | */ |
308 | platform_secondary_init(cpu); | 309 | platform_secondary_init(cpu); |
309 | 310 | ||
310 | /* | ||
311 | * Enable local interrupts. | ||
312 | */ | ||
313 | notify_cpu_starting(cpu); | 311 | notify_cpu_starting(cpu); |
314 | local_irq_enable(); | ||
315 | local_fiq_enable(); | ||
316 | |||
317 | /* | ||
318 | * Setup the percpu timer for this CPU. | ||
319 | */ | ||
320 | percpu_timer_setup(); | ||
321 | 312 | ||
322 | calibrate_delay(); | 313 | calibrate_delay(); |
323 | 314 | ||
@@ -329,10 +320,23 @@ asmlinkage void __cpuinit secondary_start_kernel(void) | |||
329 | * before we continue. | 320 | * before we continue. |
330 | */ | 321 | */ |
331 | set_cpu_online(cpu, true); | 322 | set_cpu_online(cpu, true); |
323 | |||
324 | /* | ||
325 | * Setup the percpu timer for this CPU. | ||
326 | */ | ||
327 | percpu_timer_setup(); | ||
328 | |||
332 | while (!cpu_active(cpu)) | 329 | while (!cpu_active(cpu)) |
333 | cpu_relax(); | 330 | cpu_relax(); |
334 | 331 | ||
335 | /* | 332 | /* |
333 | * cpu_active bit is set, so it's safe to enable interrupts | ||
334 | * now. | ||
335 | */ | ||
336 | local_irq_enable(); | ||
337 | local_fiq_enable(); | ||
338 | |||
339 | /* | ||
336 | * OK, it's off to the idle thread for us | 340 | * OK, it's off to the idle thread for us |
337 | */ | 341 | */ |
338 | cpu_idle(); | 342 | cpu_idle(); |
@@ -411,6 +415,7 @@ static const char *ipi_types[NR_IPI] = { | |||
411 | S(IPI_CALL_FUNC, "Function call interrupts"), | 415 | S(IPI_CALL_FUNC, "Function call interrupts"), |
412 | S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"), | 416 | S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"), |
413 | S(IPI_CPU_STOP, "CPU stop interrupts"), | 417 | S(IPI_CPU_STOP, "CPU stop interrupts"), |
418 | S(IPI_CPU_BACKTRACE, "CPU backtrace"), | ||
414 | }; | 419 | }; |
415 | 420 | ||
416 | void show_ipi_list(struct seq_file *p, int prec) | 421 | void show_ipi_list(struct seq_file *p, int prec) |
@@ -561,6 +566,58 @@ static void ipi_cpu_stop(unsigned int cpu) | |||
561 | cpu_relax(); | 566 | cpu_relax(); |
562 | } | 567 | } |
563 | 568 | ||
569 | static cpumask_t backtrace_mask; | ||
570 | static DEFINE_RAW_SPINLOCK(backtrace_lock); | ||
571 | |||
572 | /* "in progress" flag of arch_trigger_all_cpu_backtrace */ | ||
573 | static unsigned long backtrace_flag; | ||
574 | |||
575 | void smp_send_all_cpu_backtrace(void) | ||
576 | { | ||
577 | unsigned int this_cpu = smp_processor_id(); | ||
578 | int i; | ||
579 | |||
580 | if (test_and_set_bit(0, &backtrace_flag)) | ||
581 | /* | ||
582 | * If there is already a trigger_all_cpu_backtrace() in progress | ||
583 | * (backtrace_flag == 1), don't output double cpu dump infos. | ||
584 | */ | ||
585 | return; | ||
586 | |||
587 | cpumask_copy(&backtrace_mask, cpu_online_mask); | ||
588 | cpu_clear(this_cpu, backtrace_mask); | ||
589 | |||
590 | pr_info("Backtrace for cpu %d (current):\n", this_cpu); | ||
591 | dump_stack(); | ||
592 | |||
593 | pr_info("\nsending IPI to all other CPUs:\n"); | ||
594 | smp_cross_call(&backtrace_mask, IPI_CPU_BACKTRACE); | ||
595 | |||
596 | /* Wait for up to 10 seconds for all other CPUs to do the backtrace */ | ||
597 | for (i = 0; i < 10 * 1000; i++) { | ||
598 | if (cpumask_empty(&backtrace_mask)) | ||
599 | break; | ||
600 | mdelay(1); | ||
601 | } | ||
602 | |||
603 | clear_bit(0, &backtrace_flag); | ||
604 | smp_mb__after_clear_bit(); | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * ipi_cpu_backtrace - handle IPI from smp_send_all_cpu_backtrace() | ||
609 | */ | ||
610 | static void ipi_cpu_backtrace(unsigned int cpu, struct pt_regs *regs) | ||
611 | { | ||
612 | if (cpu_isset(cpu, backtrace_mask)) { | ||
613 | raw_spin_lock(&backtrace_lock); | ||
614 | pr_warning("IPI backtrace for cpu %d\n", cpu); | ||
615 | show_regs(regs); | ||
616 | raw_spin_unlock(&backtrace_lock); | ||
617 | cpu_clear(cpu, backtrace_mask); | ||
618 | } | ||
619 | } | ||
620 | |||
564 | /* | 621 | /* |
565 | * Main handler for inter-processor interrupts | 622 | * Main handler for inter-processor interrupts |
566 | */ | 623 | */ |
@@ -601,6 +658,10 @@ asmlinkage void __exception_irq_entry do_IPI(int ipinr, struct pt_regs *regs) | |||
601 | irq_exit(); | 658 | irq_exit(); |
602 | break; | 659 | break; |
603 | 660 | ||
661 | case IPI_CPU_BACKTRACE: | ||
662 | ipi_cpu_backtrace(cpu, regs); | ||
663 | break; | ||
664 | |||
604 | default: | 665 | default: |
605 | printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n", | 666 | printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n", |
606 | cpu, ipinr); | 667 | cpu, ipinr); |