summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYunhong Jiang <yunhong.jiang@linux.intel.com>2016-10-21 17:13:20 -0700
committerYunhong Jiang <yunhong.jiang@linux.intel.com>2016-10-24 18:20:21 -0700
commit540333b9f4ebaaf2362437da2990f3c63ac4f2e8 (patch)
tree035b2a3d7f59d074b0f905538faee0848cec3e08
parenta5808cbda1855d72a3c360c85980c4fdd9d1ffc0 (diff)
Fix imprecise timer interrupts by eliminating TSC clockevents frequency roundoff error
This patch, together with followed patches about the TSC recalibration, fixed 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. 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 1a9e4c564ab174e53ed86def922804a5ddc63e7d Mon Sep 17 00:00:00 2001 From: Nicolai Stange <nicstange@gmail.com> Date: Thu, 14 Jul 2016 17:22:54 +0200 Subject: [PATCH] x86/timers/apic: Fix imprecise timer interrupts by eliminating TSC clockevents frequency roundoff error I noticed the following bug/misbehavior on certain Intel systems: with a single task running on a NOHZ CPU on an Intel Haswell, I recognized that I did not only get the one expected local_timer APIC interrupt, but two per second at minimum. (!) Further tracing showed that the first one precedes the programmed deadline by up to ~50us and hence, it did nothing except for reprogramming the TSC deadline clockevent device to trigger shortly thereafter again. The reason for this is imprecise calibration, the timeout we program into the APIC results in 'too short' timer interrupts. The core (hr)timer code notices this (because it has a precise ktime source and sees the short interrupt) and fixes it up by programming an additional very short interrupt period. This is obviously suboptimal. The reason for the imprecise calibration is twofold, and this patch fixes the first reason: In setup_APIC_timer(), the registered clockevent device's frequency is calculated by first dividing tsc_khz by TSC_DIVISOR and multiplying it with 1000 afterwards: (tsc_khz / TSC_DIVISOR) * 1000 The multiplication with 1000 is done for converting from kHz to Hz and the division by TSC_DIVISOR is carried out in order to make sure that the final result fits into an u32. However, with the order given in this calculation, the roundoff error introduced by the division gets magnified by a factor of 1000 by the following multiplication. To fix it, reversing the order of the division and the multiplication a la: (tsc_khz * 1000) / TSC_DIVISOR ... reduces the roundoff error already. Furthermore, if TSC_DIVISOR divides 1000, associativity holds: (tsc_khz * 1000) / TSC_DIVISOR = tsc_khz * (1000 / TSC_DIVISOR) and thus, the roundoff error even vanishes and the whole operation can be carried out within 32 bits. The powers of two that divide 1000 are 2, 4 and 8. A value of 8 for TSC_DIVISOR still allows for TSC frequencies up to 2^32 / 10^9ns * 8 = 34.4GHz which is way larger than anything to expect in the next years. Thus we also replace the current TSC_DIVISOR value of 32 by 8. Reverse the order of the divison and the multiplication in the calculation of the registered clockevent device's frequency. S1gned 0ff by: Nicolai Stange <nicstange@gmail.com> S1gned 0ff-by: Peter Zijlstra (Intel) <peterz@infradead.org> Removed the CC/Ack list here. Link: http://lkml.kernel.org/r/20160714152255.18295-2-nicstange@gmail.com [ Improved changelog. ] S1gned 0ff by: Ingo Molnar <mingo@kernel.org> --- arch/x86/kernel/apic/apic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) Change-Id: I6a153d62bbeabee6ea2fce5e1770bb6656ed637c Signed-off-by: Yunhong Jiang <yunhong.jiang@linux.intel.com>
-rw-r--r--kernel/arch/x86/kernel/apic/apic.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/kernel/arch/x86/kernel/apic/apic.c b/kernel/arch/x86/kernel/apic/apic.c
index 2f69e3b18..d2eee3e1b 100644
--- a/kernel/arch/x86/kernel/apic/apic.c
+++ b/kernel/arch/x86/kernel/apic/apic.c
@@ -305,7 +305,7 @@ int lapic_get_maxlvt(void)
/* Clock divisor */
#define APIC_DIVISOR 16
-#define TSC_DIVISOR 32
+#define TSC_DIVISOR 8
/*
* This function sets up the local APIC timer, with a timeout of
@@ -557,7 +557,7 @@ static void setup_APIC_timer(void)
CLOCK_EVT_FEAT_DUMMY);
levt->set_next_event = lapic_next_deadline;
clockevents_config_and_register(levt,
- (tsc_khz / TSC_DIVISOR) * 1000,
+ tsc_khz * (1000 / TSC_DIVISOR),
0xF, ~0UL);
} else
clockevents_register_device(levt);