[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: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
3b6353fae0
commit
37ee16ae93
|
@ -356,6 +356,16 @@ config HOTPLUG_CPU
|
|||
Say Y here to experiment with turning CPUs off and on. CPUs
|
||||
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
|
||||
bool "Preemptible Kernel (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
|
|
|
@ -47,6 +47,13 @@
|
|||
movne r0, sp
|
||||
adrne lr, 1b
|
||||
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
|
||||
|
||||
.endm
|
||||
|
|
|
@ -264,6 +264,7 @@ unlock:
|
|||
#endif
|
||||
#ifdef CONFIG_SMP
|
||||
show_ipi_list(p);
|
||||
show_local_irqs(p);
|
||||
#endif
|
||||
seq_printf(p, "Err: %10lu\n", irq_err_count);
|
||||
}
|
||||
|
|
|
@ -184,6 +184,11 @@ int __cpuexit __cpu_disable(void)
|
|||
*/
|
||||
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.
|
||||
|
@ -289,6 +294,11 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
|
|||
*/
|
||||
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
|
||||
*/
|
||||
|
@ -454,6 +464,18 @@ void show_ipi_list(struct seq_file *p)
|
|||
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)
|
||||
{
|
||||
int user = user_mode(regs);
|
||||
|
@ -464,6 +486,18 @@ static void ipi_timer(struct pt_regs *regs)
|
|||
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()
|
||||
*
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
typedef struct {
|
||||
unsigned int __softirq_pending;
|
||||
unsigned int local_timer_irqs;
|
||||
} ____cacheline_aligned irq_cpustat_t;
|
||||
|
||||
#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
|
||||
|
|
|
@ -92,4 +92,42 @@ extern void platform_cpu_die(unsigned int cpu);
|
|||
extern int platform_cpu_kill(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 */
|
||||
|
|
Loading…
Reference in a new issue