[PATCH] x86_64: Handle missing local APIC timer interrupts on C3 state
authorVenkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Wed, 11 Jan 2006 21:44:24 +0000 (22:44 +0100)
committerLinus Torvalds <torvalds@g5.osdl.org>
Thu, 12 Jan 2006 03:04:54 +0000 (19:04 -0800)
Whenever we see that a CPU is capable of C3 (during ACPI cstate init), we
disable local APIC timer and switch to using a broadcast from external timer
interrupt (IRQ 0).

Patch below adds the code for x86_64.

Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/x86_64/kernel/apic.c
arch/x86_64/kernel/time.c
include/asm-x86_64/apic.h

index fa47ae7315761a8a557e256592d9a9336f8f1427..40cbd60e2d0bf47a57cb48f8fe94f17b38d21156 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/mc146818rtc.h>
 #include <linux/kernel_stat.h>
 #include <linux/sysdev.h>
+#include <linux/module.h>
 
 #include <asm/atomic.h>
 #include <asm/smp.h>
@@ -38,6 +39,12 @@ int apic_verbosity;
 
 int disable_apic_timer __initdata;
 
+/*
+ * cpu_mask that denotes the CPUs that needs timer interrupt coming in as
+ * IPIs in place of local APIC timers
+ */
+static cpumask_t timer_interrupt_broadcast_ipi_mask;
+
 /* Using APIC to generate smp_local_timer_interrupt? */
 int using_apic_timer = 0;
 
@@ -656,9 +663,14 @@ void __init init_apic_mappings(void)
 static void __setup_APIC_LVTT(unsigned int clocks)
 {
        unsigned int lvtt_value, tmp_value, ver;
+       int cpu = smp_processor_id();
 
        ver = GET_APIC_VERSION(apic_read(APIC_LVR));
        lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;
+
+       if (cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask))
+               lvtt_value |= APIC_LVT_MASKED;
+
        apic_write_around(APIC_LVTT, lvtt_value);
 
        /*
@@ -781,7 +793,7 @@ void __cpuinit setup_secondary_APIC_clock(void)
        local_irq_enable();
 }
 
-void __cpuinit disable_APIC_timer(void)
+void disable_APIC_timer(void)
 {
        if (using_apic_timer) {
                unsigned long v;
@@ -793,7 +805,10 @@ void __cpuinit disable_APIC_timer(void)
 
 void enable_APIC_timer(void)
 {
-       if (using_apic_timer) {
+       int cpu = smp_processor_id();
+
+       if (using_apic_timer &&
+           !cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) {
                unsigned long v;
 
                v = apic_read(APIC_LVTT);
@@ -801,6 +816,42 @@ void enable_APIC_timer(void)
        }
 }
 
+void switch_APIC_timer_to_ipi(void *cpumask)
+{
+       cpumask_t mask = *(cpumask_t *)cpumask;
+       int cpu = smp_processor_id();
+
+       if (cpu_isset(cpu, mask) &&
+           !cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) {
+               disable_APIC_timer();
+               cpu_set(cpu, timer_interrupt_broadcast_ipi_mask);
+       }
+}
+EXPORT_SYMBOL(switch_APIC_timer_to_ipi);
+
+void smp_send_timer_broadcast_ipi(void)
+{
+       cpumask_t mask;
+
+       cpus_and(mask, cpu_online_map, timer_interrupt_broadcast_ipi_mask);
+       if (!cpus_empty(mask)) {
+               send_IPI_mask(mask, LOCAL_TIMER_VECTOR);
+       }
+}
+
+void switch_ipi_to_APIC_timer(void *cpumask)
+{
+       cpumask_t mask = *(cpumask_t *)cpumask;
+       int cpu = smp_processor_id();
+
+       if (cpu_isset(cpu, mask) &&
+           cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) {
+               cpu_clear(cpu, timer_interrupt_broadcast_ipi_mask);
+               enable_APIC_timer();
+       }
+}
+EXPORT_SYMBOL(switch_ipi_to_APIC_timer);
+
 int setup_profiling_timer(unsigned int multiplier)
 {
        return -EINVAL;
index 43c9fa0f8d5fe4411bc5aa7dd07eaa4eb3bd472e..211bf0e51d9d0f80ee984bdaf6893d6a9f0ad0ec 100644 (file)
@@ -471,6 +471,11 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  
        write_sequnlock(&xtime_lock);
 
+#ifdef CONFIG_X86_LOCAL_APIC
+       if (using_apic_timer)
+               smp_send_timer_broadcast_ipi();
+#endif
+
        return IRQ_HANDLED;
 }
 
index 5647b7de1749a8589f677092e770c015f1d7fa8c..1768ce719156d93a43a9e76d6c890c14adbc7fd7 100644 (file)
@@ -113,6 +113,12 @@ extern int disable_timer_pin_1;
 
 extern void setup_threshold_lvt(unsigned long lvt_off);
 
+void smp_send_timer_broadcast_ipi(void);
+void switch_APIC_timer_to_ipi(void *cpumask);
+void switch_ipi_to_APIC_timer(void *cpumask);
+
+#define ARCH_APICTIMER_STOPS_ON_C3     1
+
 #endif /* CONFIG_X86_LOCAL_APIC */
 
 extern unsigned boot_cpu_id;