summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYunhong Jiang <yunhong.jiang@linux.intel.com>2016-10-21 17:38:29 -0700
committerYunhong Jiang <yunhong.jiang@linux.intel.com>2016-10-24 18:20:43 -0700
commitab1366e1b48fa0b56752a3b56e7456c3c7ecb011 (patch)
tree46a4520d0a8579a431ccb4f78985e8f471d35195
parent540333b9f4ebaaf2362437da2990f3c63ac4f2e8 (diff)
Inform TSC deadline clockevent device about recalibration
This patch, together with previous TSC calculation patch, fixes duplicate timer interrupt on the guest. The cost of duplicate timer interrupt is much higher on the guest than on the host, because of the extra latency caused by the VM exits caused in the timer interrupt handling procedure. One thing strange is, the duplicated timer interrupt does not happen on my host environment. Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com> Backport-by: Yunhong Jiang <yunhong.jiang@intel.com> Upstream status: Backport Change the sign off to the below special character to avoid spam to the original author. And removed the CC/ack list. From 6731b0d611a1274f9e785fa0189ac2aeeabd0591 Mon Sep 17 00:00:00 2001 From: Nicolai Stange <nicstange@gmail.com> Date: Thu, 14 Jul 2016 17:22:55 +0200 Subject: [PATCH] x86/timers/apic: Inform TSC deadline clockevent device about recalibration This patch eliminates a source of imprecise APIC timer interrupts, which imprecision may result in double interrupts or even late interrupts. The TSC deadline clockevent devices' configuration and registration happens before the TSC frequency calibration is refined in tsc_refine_calibration_work(). This results in the TSC clocksource and the TSC deadline clockevent devices being configured with slightly different frequencies: the former gets the refined one and the latter are configured with the inaccurate frequency detected earlier by means of the "Fast TSC calibration using PIT". Within the APIC code, introduce the notifier function lapic_update_tsc_freq() which reconfigures all per-CPU TSC deadline clockevent devices with the current tsc_khz. Call it from the TSC code after TSC calibration refinement has happened. s1gned 0ff by: Nicolai Stange <nicstange@gmail.com> s1gned 0ff by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: http://lkml.kernel.org/r/20160714152255.18295-3-nicstange@gmail.com [ Pushed #ifdef CONFIG_X86_LOCAL_APIC into header, improved changelog. ] s1gned 0ff by: Ingo Molnar <mingo@kernel.org> --- arch/x86/include/asm/apic.h | 2 ++ arch/x86/kernel/apic/apic.c | 24 ++++++++++++++++++++++++ arch/x86/kernel/tsc.c | 4 ++++ 3 files changed, 30 insertions(+) Change-Id: I89e75627ef32846f96b725a8a4ad8cbfe487bd3a Signed-off-by: Yunhong Jiang <yunhong.jiang@linux.intel.com>
-rw-r--r--kernel/arch/x86/include/asm/apic.h2
-rw-r--r--kernel/arch/x86/kernel/apic/apic.c24
-rw-r--r--kernel/arch/x86/kernel/tsc.c4
3 files changed, 30 insertions, 0 deletions
diff --git a/kernel/arch/x86/include/asm/apic.h b/kernel/arch/x86/include/asm/apic.h
index a30316bf8..9686289d2 100644
--- a/kernel/arch/x86/include/asm/apic.h
+++ b/kernel/arch/x86/include/asm/apic.h
@@ -131,6 +131,7 @@ extern void init_apic_mappings(void);
void register_lapic_address(unsigned long address);
extern void setup_boot_APIC_clock(void);
extern void setup_secondary_APIC_clock(void);
+extern void lapic_update_tsc_freq(void);
extern int APIC_init_uniprocessor(void);
#ifdef CONFIG_X86_64
@@ -166,6 +167,7 @@ static inline void init_apic_mappings(void) { }
static inline void disable_local_APIC(void) { }
# define setup_boot_APIC_clock x86_init_noop
# define setup_secondary_APIC_clock x86_init_noop
+static inline void lapic_update_tsc_freq(void) { }
#endif /* !CONFIG_X86_LOCAL_APIC */
#ifdef CONFIG_X86_X2APIC
diff --git a/kernel/arch/x86/kernel/apic/apic.c b/kernel/arch/x86/kernel/apic/apic.c
index d2eee3e1b..58e7cdb25 100644
--- a/kernel/arch/x86/kernel/apic/apic.c
+++ b/kernel/arch/x86/kernel/apic/apic.c
@@ -564,6 +564,30 @@ static void setup_APIC_timer(void)
}
/*
+ * Install the updated TSC frequency from recalibration at the TSC
+ * deadline clockevent devices.
+ */
+static void __lapic_update_tsc_freq(void *info)
+{
+ struct clock_event_device *levt = this_cpu_ptr(&lapic_events);
+
+ if (!this_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER))
+ return;
+
+ clockevents_update_freq(levt, tsc_khz * (1000 / TSC_DIVISOR));
+}
+
+void lapic_update_tsc_freq(void)
+{
+ /*
+ * The clockevent device's ->mult and ->shift can both be
+ * changed. In order to avoid races, schedule the frequency
+ * update code on each CPU.
+ */
+ on_each_cpu(__lapic_update_tsc_freq, NULL, 0);
+}
+
+/*
* In this functions we calibrate APIC bus clocks to the external timer.
*
* We want to do the calibration only once since we want to have local timer
diff --git a/kernel/arch/x86/kernel/tsc.c b/kernel/arch/x86/kernel/tsc.c
index c7c4d9c51..eafe93888 100644
--- a/kernel/arch/x86/kernel/tsc.c
+++ b/kernel/arch/x86/kernel/tsc.c
@@ -22,6 +22,7 @@
#include <asm/nmi.h>
#include <asm/x86_init.h>
#include <asm/geode.h>
+#include <asm/apic.h>
unsigned int __read_mostly cpu_khz; /* TSC clocks / usec, not used here */
EXPORT_SYMBOL(cpu_khz);
@@ -1141,6 +1142,9 @@ static void tsc_refine_calibration_work(struct work_struct *work)
(unsigned long)tsc_khz / 1000,
(unsigned long)tsc_khz % 1000);
+ /* Inform the TSC deadline clockevent devices about the recalibration */
+ lapic_update_tsc_freq();
+
out:
clocksource_register_khz(&clocksource_tsc, tsc_khz);
}