Commit 37ee16ae authored by Russell King's avatar Russell King Committed by Russell King

[ARM SMP] Add core ARM support for local timers

Add infrastructure for supporting per-cpu local timers to update
the profiling information and update system time accounting.
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 3b6353fa
...@@ -356,6 +356,16 @@ config HOTPLUG_CPU ...@@ -356,6 +356,16 @@ config HOTPLUG_CPU
Say Y here to experiment with turning CPUs off and on. CPUs Say Y here to experiment with turning CPUs off and on. CPUs
can be controlled through /sys/devices/system/cpu. can be controlled through /sys/devices/system/cpu.
config LOCAL_TIMERS
bool "Use local timer interrupts"
depends on SMP && n
default y
help
Enable support for local timers on SMP platforms, rather then the
legacy IPI broadcast method. Local timers allows the system
accounting to be spread across the timer interval, preventing a
"thundering herd" at every timer tick.
config PREEMPT config PREEMPT
bool "Preemptible Kernel (EXPERIMENTAL)" bool "Preemptible Kernel (EXPERIMENTAL)"
depends on EXPERIMENTAL depends on EXPERIMENTAL
......
...@@ -47,6 +47,13 @@ ...@@ -47,6 +47,13 @@
movne r0, sp movne r0, sp
adrne lr, 1b adrne lr, 1b
bne do_IPI bne do_IPI
#ifdef CONFIG_LOCAL_TIMERS
test_for_ltirq r0, r6, r5, lr
movne r0, sp
adrne lr, 1b
bne do_local_timer
#endif
#endif #endif
.endm .endm
......
...@@ -264,6 +264,7 @@ int show_interrupts(struct seq_file *p, void *v) ...@@ -264,6 +264,7 @@ int show_interrupts(struct seq_file *p, void *v)
#endif #endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
show_ipi_list(p); show_ipi_list(p);
show_local_irqs(p);
#endif #endif
seq_printf(p, "Err: %10lu\n", irq_err_count); seq_printf(p, "Err: %10lu\n", irq_err_count);
} }
......
...@@ -184,6 +184,11 @@ int __cpuexit __cpu_disable(void) ...@@ -184,6 +184,11 @@ int __cpuexit __cpu_disable(void)
*/ */
migrate_irqs(); migrate_irqs();
/*
* Stop the local timer for this CPU.
*/
local_timer_stop(cpu);
/* /*
* Flush user cache and TLB mappings, and then remove this CPU * Flush user cache and TLB mappings, and then remove this CPU
* from the vm mask set of all processes. * from the vm mask set of all processes.
...@@ -289,6 +294,11 @@ asmlinkage void __cpuinit secondary_start_kernel(void) ...@@ -289,6 +294,11 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
*/ */
cpu_set(cpu, cpu_online_map); cpu_set(cpu, cpu_online_map);
/*
* Setup local timer for this CPU.
*/
local_timer_setup(cpu);
/* /*
* OK, it's off to the idle thread for us * OK, it's off to the idle thread for us
*/ */
...@@ -454,6 +464,18 @@ void show_ipi_list(struct seq_file *p) ...@@ -454,6 +464,18 @@ void show_ipi_list(struct seq_file *p)
seq_putc(p, '\n'); seq_putc(p, '\n');
} }
void show_local_irqs(struct seq_file *p)
{
unsigned int cpu;
seq_printf(p, "LOC: ");
for_each_present_cpu(cpu)
seq_printf(p, "%10u ", irq_stat[cpu].local_timer_irqs);
seq_putc(p, '\n');
}
static void ipi_timer(struct pt_regs *regs) static void ipi_timer(struct pt_regs *regs)
{ {
int user = user_mode(regs); int user = user_mode(regs);
...@@ -464,6 +486,18 @@ static void ipi_timer(struct pt_regs *regs) ...@@ -464,6 +486,18 @@ static void ipi_timer(struct pt_regs *regs)
irq_exit(); irq_exit();
} }
#ifdef CONFIG_LOCAL_TIMERS
asmlinkage void do_local_timer(struct pt_regs *regs)
{
int cpu = smp_processor_id();
if (local_timer_ack()) {
irq_stat[cpu].local_timer_irqs++;
ipi_timer(regs);
}
}
#endif
/* /*
* ipi_call_function - handle IPI from smp_call_function() * ipi_call_function - handle IPI from smp_call_function()
* *
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
typedef struct { typedef struct {
unsigned int __softirq_pending; unsigned int __softirq_pending;
unsigned int local_timer_irqs;
} ____cacheline_aligned irq_cpustat_t; } ____cacheline_aligned irq_cpustat_t;
#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */ #include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
......
...@@ -92,4 +92,42 @@ extern void platform_cpu_die(unsigned int cpu); ...@@ -92,4 +92,42 @@ extern void platform_cpu_die(unsigned int cpu);
extern int platform_cpu_kill(unsigned int cpu); extern int platform_cpu_kill(unsigned int cpu);
extern void platform_cpu_enable(unsigned int cpu); extern void platform_cpu_enable(unsigned int cpu);
#ifdef CONFIG_LOCAL_TIMERS
/*
* Setup a local timer interrupt for a CPU.
*/
extern void local_timer_setup(unsigned int cpu);
/*
* Stop a local timer interrupt.
*/
extern void local_timer_stop(unsigned int cpu);
/*
* Platform provides this to acknowledge a local timer IRQ
*/
extern int local_timer_ack(void);
#else
static inline void local_timer_setup(unsigned int cpu)
{
}
static inline void local_timer_stop(unsigned int cpu)
{
}
#endif
/*
* show local interrupt info
*/
extern void show_local_irqs(struct seq_file *);
/*
* Called from assembly, this is the local timer IRQ handler
*/
asmlinkage void do_local_timer(struct pt_regs *);
#endif /* ifndef __ASM_ARM_SMP_H */ #endif /* ifndef __ASM_ARM_SMP_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment