i386,x86-64 Handle missing local APIC timer interrupts on C3 state
RFC PATCH 3/3 i386,x86-64 Handle missing local APIC timer interrupts on C3 state
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 <[email protected]>
arch/x86_64/kernel/apic.c +42 -1
@@ -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 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 ;
@@ -781,7 +788,7 @@local_irq_enable (); } -void __cpuinit disable_APIC_timer (void ) +void disable_APIC_timer (void ) {if (using_apic_timer) {unsigned long v;
@@ -801,6 +808,40 @@ } } +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 ) +{ +if (!cpus_empty (timer_interrupt_broadcast_ipi_mask)) { +send_IPI_mask (timer_interrupt_broadcast_ipi_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 ;
include/asm-x86_64/apic.h +6 -0
@@ -113,6 +113,12 @@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;
arch/x86_64/kernel/time.c +5 -0
@@ -476,6 +476,11 @@write_sequnlock (&xtime_lock); +#ifdef CONFIG_X86_LOCAL_APIC +if (using_apic_timer) +smp_send_timer_broadcast_ipi (); +#endif +return IRQ_HANDLED ; }