[ARM SMP] Add core ARM support for local timers
authorRussell King <rmk@dyn-67.arm.linux.org.uk>
Tue, 8 Nov 2005 19:08:05 +0000 (19:08 +0000)
committerRussell King <rmk+kernel@arm.linux.org.uk>
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 91d5ef3397be3d56f643dc5fb21eaa4315303499..3bfef0934c9d39162e346e73764f379aad8946f4 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 a511ec5b11a33344c945b66811b889c0a91bf084..d9fb819bf7cc3960aedb4b9836451371458a4893 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 6f86d0af7c562f8e0211d0e209f3bfdc2814a18b..d7099dbbb879ab8cd9ed6c417bdcc87d461dd747 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 f5fc57e0fe412bc1ea0b5fb28b65705a165b317c..77e2e9ca89fab8477cdb13567d1c992c2e1c7d8b 100644 (file)
@@ -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()
  *
index e5ccb6b8ff83c60700f57db2279de77fac382454..1cbb173bf5b108b2d136a78961ed7ffc873dc276 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 52e7c8d830b2fe91f9418e0099126d93e6a26eca..5a72e50ca9fc0ab5f1640d5ba975ee155851e14e 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 */