diff options
| -rw-r--r-- | arch/arm/Kconfig | 10 | ||||
| -rw-r--r-- | arch/arm/kernel/entry-armv.S | 7 | ||||
| -rw-r--r-- | arch/arm/kernel/irq.c | 1 | ||||
| -rw-r--r-- | arch/arm/kernel/smp.c | 34 | ||||
| -rw-r--r-- | include/asm-arm/hardirq.h | 1 | ||||
| -rw-r--r-- | include/asm-arm/smp.h | 38 |
6 files changed, 91 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 91d5ef3397be..3bfef0934c9d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
| @@ -356,6 +356,16 @@ config HOTPLUG_CPU | |||
| 356 | Say Y here to experiment with turning CPUs off and on. CPUs | 356 | Say Y here to experiment with turning CPUs off and on. CPUs |
| 357 | can be controlled through /sys/devices/system/cpu. | 357 | can be controlled through /sys/devices/system/cpu. |
| 358 | 358 | ||
| 359 | config LOCAL_TIMERS | ||
| 360 | bool "Use local timer interrupts" | ||
| 361 | depends on SMP && n | ||
| 362 | default y | ||
| 363 | help | ||
| 364 | Enable support for local timers on SMP platforms, rather then the | ||
| 365 | legacy IPI broadcast method. Local timers allows the system | ||
| 366 | accounting to be spread across the timer interval, preventing a | ||
| 367 | "thundering herd" at every timer tick. | ||
| 368 | |||
| 359 | config PREEMPT | 369 | config PREEMPT |
| 360 | bool "Preemptible Kernel (EXPERIMENTAL)" | 370 | bool "Preemptible Kernel (EXPERIMENTAL)" |
| 361 | depends on EXPERIMENTAL | 371 | depends on EXPERIMENTAL |
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index a511ec5b11a3..d9fb819bf7cc 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S | |||
| @@ -47,6 +47,13 @@ | |||
| 47 | movne r0, sp | 47 | movne r0, sp |
| 48 | adrne lr, 1b | 48 | adrne lr, 1b |
| 49 | bne do_IPI | 49 | bne do_IPI |
| 50 | |||
| 51 | #ifdef CONFIG_LOCAL_TIMERS | ||
| 52 | test_for_ltirq r0, r6, r5, lr | ||
| 53 | movne r0, sp | ||
| 54 | adrne lr, 1b | ||
| 55 | bne do_local_timer | ||
| 56 | #endif | ||
| 50 | #endif | 57 | #endif |
| 51 | 58 | ||
| 52 | .endm | 59 | .endm |
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 6f86d0af7c56..d7099dbbb879 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c | |||
| @@ -264,6 +264,7 @@ unlock: | |||
| 264 | #endif | 264 | #endif |
| 265 | #ifdef CONFIG_SMP | 265 | #ifdef CONFIG_SMP |
| 266 | show_ipi_list(p); | 266 | show_ipi_list(p); |
| 267 | show_local_irqs(p); | ||
| 267 | #endif | 268 | #endif |
| 268 | seq_printf(p, "Err: %10lu\n", irq_err_count); | 269 | seq_printf(p, "Err: %10lu\n", irq_err_count); |
| 269 | } | 270 | } |
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index f5fc57e0fe41..77e2e9ca89fa 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c | |||
| @@ -185,6 +185,11 @@ int __cpuexit __cpu_disable(void) | |||
| 185 | migrate_irqs(); | 185 | migrate_irqs(); |
| 186 | 186 | ||
| 187 | /* | 187 | /* |
| 188 | * Stop the local timer for this CPU. | ||
| 189 | */ | ||
| 190 | local_timer_stop(cpu); | ||
| 191 | |||
| 192 | /* | ||
| 188 | * Flush user cache and TLB mappings, and then remove this CPU | 193 | * Flush user cache and TLB mappings, and then remove this CPU |
| 189 | * from the vm mask set of all processes. | 194 | * from the vm mask set of all processes. |
| 190 | */ | 195 | */ |
| @@ -290,6 +295,11 @@ asmlinkage void __cpuinit secondary_start_kernel(void) | |||
| 290 | cpu_set(cpu, cpu_online_map); | 295 | cpu_set(cpu, cpu_online_map); |
| 291 | 296 | ||
| 292 | /* | 297 | /* |
| 298 | * Setup local timer for this CPU. | ||
| 299 | */ | ||
| 300 | local_timer_setup(cpu); | ||
| 301 | |||
| 302 | /* | ||
| 293 | * OK, it's off to the idle thread for us | 303 | * OK, it's off to the idle thread for us |
| 294 | */ | 304 | */ |
| 295 | cpu_idle(); | 305 | cpu_idle(); |
| @@ -454,6 +464,18 @@ void show_ipi_list(struct seq_file *p) | |||
| 454 | seq_putc(p, '\n'); | 464 | seq_putc(p, '\n'); |
| 455 | } | 465 | } |
| 456 | 466 | ||
| 467 | void show_local_irqs(struct seq_file *p) | ||
| 468 | { | ||
| 469 | unsigned int cpu; | ||
| 470 | |||
| 471 | seq_printf(p, "LOC: "); | ||
| 472 | |||
| 473 | for_each_present_cpu(cpu) | ||
| 474 | seq_printf(p, "%10u ", irq_stat[cpu].local_timer_irqs); | ||
| 475 | |||
| 476 | seq_putc(p, '\n'); | ||
| 477 | } | ||
| 478 | |||
| 457 | static void ipi_timer(struct pt_regs *regs) | 479 | static void ipi_timer(struct pt_regs *regs) |
| 458 | { | 480 | { |
| 459 | int user = user_mode(regs); | 481 | int user = user_mode(regs); |
| @@ -464,6 +486,18 @@ static void ipi_timer(struct pt_regs *regs) | |||
| 464 | irq_exit(); | 486 | irq_exit(); |
| 465 | } | 487 | } |
| 466 | 488 | ||
| 489 | #ifdef CONFIG_LOCAL_TIMERS | ||
| 490 | asmlinkage void do_local_timer(struct pt_regs *regs) | ||
| 491 | { | ||
| 492 | int cpu = smp_processor_id(); | ||
| 493 | |||
| 494 | if (local_timer_ack()) { | ||
| 495 | irq_stat[cpu].local_timer_irqs++; | ||
| 496 | ipi_timer(regs); | ||
| 497 | } | ||
| 498 | } | ||
| 499 | #endif | ||
| 500 | |||
| 467 | /* | 501 | /* |
| 468 | * ipi_call_function - handle IPI from smp_call_function() | 502 | * ipi_call_function - handle IPI from smp_call_function() |
| 469 | * | 503 | * |
diff --git a/include/asm-arm/hardirq.h b/include/asm-arm/hardirq.h index e5ccb6b8ff83..1cbb173bf5b1 100644 --- a/include/asm-arm/hardirq.h +++ b/include/asm-arm/hardirq.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | typedef struct { | 9 | typedef struct { |
| 10 | unsigned int __softirq_pending; | 10 | unsigned int __softirq_pending; |
| 11 | unsigned int local_timer_irqs; | ||
| 11 | } ____cacheline_aligned irq_cpustat_t; | 12 | } ____cacheline_aligned irq_cpustat_t; |
| 12 | 13 | ||
| 13 | #include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */ | 14 | #include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */ |
diff --git a/include/asm-arm/smp.h b/include/asm-arm/smp.h index 52e7c8d830b2..5a72e50ca9fc 100644 --- a/include/asm-arm/smp.h +++ b/include/asm-arm/smp.h | |||
| @@ -92,4 +92,42 @@ extern void platform_cpu_die(unsigned int cpu); | |||
| 92 | extern int platform_cpu_kill(unsigned int cpu); | 92 | extern int platform_cpu_kill(unsigned int cpu); |
| 93 | extern void platform_cpu_enable(unsigned int cpu); | 93 | extern void platform_cpu_enable(unsigned int cpu); |
| 94 | 94 | ||
| 95 | #ifdef CONFIG_LOCAL_TIMERS | ||
| 96 | /* | ||
| 97 | * Setup a local timer interrupt for a CPU. | ||
| 98 | */ | ||
| 99 | extern void local_timer_setup(unsigned int cpu); | ||
| 100 | |||
| 101 | /* | ||
| 102 | * Stop a local timer interrupt. | ||
| 103 | */ | ||
| 104 | extern void local_timer_stop(unsigned int cpu); | ||
| 105 | |||
| 106 | /* | ||
| 107 | * Platform provides this to acknowledge a local timer IRQ | ||
| 108 | */ | ||
| 109 | extern int local_timer_ack(void); | ||
| 110 | |||
| 111 | #else | ||
| 112 | |||
| 113 | static inline void local_timer_setup(unsigned int cpu) | ||
| 114 | { | ||
| 115 | } | ||
| 116 | |||
| 117 | static inline void local_timer_stop(unsigned int cpu) | ||
| 118 | { | ||
| 119 | } | ||
| 120 | |||
| 121 | #endif | ||
| 122 | |||
| 123 | /* | ||
| 124 | * show local interrupt info | ||
| 125 | */ | ||
| 126 | extern void show_local_irqs(struct seq_file *); | ||
| 127 | |||
| 128 | /* | ||
| 129 | * Called from assembly, this is the local timer IRQ handler | ||
| 130 | */ | ||
| 131 | asmlinkage void do_local_timer(struct pt_regs *); | ||
| 132 | |||
| 95 | #endif /* ifndef __ASM_ARM_SMP_H */ | 133 | #endif /* ifndef __ASM_ARM_SMP_H */ |
