[ARM SMP] Add core ARM support for local timers
Russell King [Tue, 8 Nov 2005 19:08:05 +0000 (19:08 +0000)]
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>

arch/arm/Kconfig
arch/arm/kernel/entry-armv.S
arch/arm/kernel/irq.c
arch/arm/kernel/smp.c
include/asm-arm/hardirq.h
include/asm-arm/smp.h

index 91d5ef3..3bfef09 100644 (file)
@@ -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
index a511ec5..d9fb819 100644 (file)
        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
index 6f86d0a..d7099db 100644 (file)
@@ -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);
        }
index f5fc57e..77e2e9c 100644 (file)
@@ -185,6 +185,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.
         */
@@ -290,6 +295,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
         */
        cpu_idle();
@@ -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()
  *
index e5ccb6b..1cbb173 100644 (file)
@@ -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 */
index 52e7c8d..5a72e50 100644 (file)
@@ -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 */