aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86_64/kernel/apic.c55
-rw-r--r--arch/x86_64/kernel/time.c5
-rw-r--r--include/asm-x86_64/apic.h6
3 files changed, 64 insertions, 2 deletions
diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c
index fa47ae731576..40cbd60e2d0b 100644
--- a/arch/x86_64/kernel/apic.c
+++ b/arch/x86_64/kernel/apic.c
@@ -25,6 +25,7 @@
25#include <linux/mc146818rtc.h> 25#include <linux/mc146818rtc.h>
26#include <linux/kernel_stat.h> 26#include <linux/kernel_stat.h>
27#include <linux/sysdev.h> 27#include <linux/sysdev.h>
28#include <linux/module.h>
28 29
29#include <asm/atomic.h> 30#include <asm/atomic.h>
30#include <asm/smp.h> 31#include <asm/smp.h>
@@ -38,6 +39,12 @@ int apic_verbosity;
38 39
39int disable_apic_timer __initdata; 40int disable_apic_timer __initdata;
40 41
42/*
43 * cpu_mask that denotes the CPUs that needs timer interrupt coming in as
44 * IPIs in place of local APIC timers
45 */
46static cpumask_t timer_interrupt_broadcast_ipi_mask;
47
41/* Using APIC to generate smp_local_timer_interrupt? */ 48/* Using APIC to generate smp_local_timer_interrupt? */
42int using_apic_timer = 0; 49int using_apic_timer = 0;
43 50
@@ -656,9 +663,14 @@ void __init init_apic_mappings(void)
656static void __setup_APIC_LVTT(unsigned int clocks) 663static void __setup_APIC_LVTT(unsigned int clocks)
657{ 664{
658 unsigned int lvtt_value, tmp_value, ver; 665 unsigned int lvtt_value, tmp_value, ver;
666 int cpu = smp_processor_id();
659 667
660 ver = GET_APIC_VERSION(apic_read(APIC_LVR)); 668 ver = GET_APIC_VERSION(apic_read(APIC_LVR));
661 lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; 669 lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;
670
671 if (cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask))
672 lvtt_value |= APIC_LVT_MASKED;
673
662 apic_write_around(APIC_LVTT, lvtt_value); 674 apic_write_around(APIC_LVTT, lvtt_value);
663 675
664 /* 676 /*
@@ -781,7 +793,7 @@ void __cpuinit setup_secondary_APIC_clock(void)
781 local_irq_enable(); 793 local_irq_enable();
782} 794}
783 795
784void __cpuinit disable_APIC_timer(void) 796void disable_APIC_timer(void)
785{ 797{
786 if (using_apic_timer) { 798 if (using_apic_timer) {
787 unsigned long v; 799 unsigned long v;
@@ -793,7 +805,10 @@ void __cpuinit disable_APIC_timer(void)
793 805
794void enable_APIC_timer(void) 806void enable_APIC_timer(void)
795{ 807{
796 if (using_apic_timer) { 808 int cpu = smp_processor_id();
809
810 if (using_apic_timer &&
811 !cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) {
797 unsigned long v; 812 unsigned long v;
798 813
799 v = apic_read(APIC_LVTT); 814 v = apic_read(APIC_LVTT);
@@ -801,6 +816,42 @@ void enable_APIC_timer(void)
801 } 816 }
802} 817}
803 818
819void switch_APIC_timer_to_ipi(void *cpumask)
820{
821 cpumask_t mask = *(cpumask_t *)cpumask;
822 int cpu = smp_processor_id();
823
824 if (cpu_isset(cpu, mask) &&
825 !cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) {
826 disable_APIC_timer();
827 cpu_set(cpu, timer_interrupt_broadcast_ipi_mask);
828 }
829}
830EXPORT_SYMBOL(switch_APIC_timer_to_ipi);
831
832void smp_send_timer_broadcast_ipi(void)
833{
834 cpumask_t mask;
835
836 cpus_and(mask, cpu_online_map, timer_interrupt_broadcast_ipi_mask);
837 if (!cpus_empty(mask)) {
838 send_IPI_mask(mask, LOCAL_TIMER_VECTOR);
839 }
840}
841
842void switch_ipi_to_APIC_timer(void *cpumask)
843{
844 cpumask_t mask = *(cpumask_t *)cpumask;
845 int cpu = smp_processor_id();
846
847 if (cpu_isset(cpu, mask) &&
848 cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) {
849 cpu_clear(cpu, timer_interrupt_broadcast_ipi_mask);
850 enable_APIC_timer();
851 }
852}
853EXPORT_SYMBOL(switch_ipi_to_APIC_timer);
854
804int setup_profiling_timer(unsigned int multiplier) 855int setup_profiling_timer(unsigned int multiplier)
805{ 856{
806 return -EINVAL; 857 return -EINVAL;
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c
index 43c9fa0f8d5f..211bf0e51d9d 100644
--- a/arch/x86_64/kernel/time.c
+++ b/arch/x86_64/kernel/time.c
@@ -471,6 +471,11 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
471 471
472 write_sequnlock(&xtime_lock); 472 write_sequnlock(&xtime_lock);
473 473
474#ifdef CONFIG_X86_LOCAL_APIC
475 if (using_apic_timer)
476 smp_send_timer_broadcast_ipi();
477#endif
478
474 return IRQ_HANDLED; 479 return IRQ_HANDLED;
475} 480}
476 481
diff --git a/include/asm-x86_64/apic.h b/include/asm-x86_64/apic.h
index 5647b7de1749..1768ce719156 100644
--- a/include/asm-x86_64/apic.h
+++ b/include/asm-x86_64/apic.h
@@ -113,6 +113,12 @@ extern int disable_timer_pin_1;
113 113
114extern void setup_threshold_lvt(unsigned long lvt_off); 114extern void setup_threshold_lvt(unsigned long lvt_off);
115 115
116void smp_send_timer_broadcast_ipi(void);
117void switch_APIC_timer_to_ipi(void *cpumask);
118void switch_ipi_to_APIC_timer(void *cpumask);
119
120#define ARCH_APICTIMER_STOPS_ON_C3 1
121
116#endif /* CONFIG_X86_LOCAL_APIC */ 122#endif /* CONFIG_X86_LOCAL_APIC */
117 123
118extern unsigned boot_cpu_id; 124extern unsigned boot_cpu_id;