aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Johnson <djohnson@sw.starentnetworks.com>2007-10-23 16:37:22 -0400
committerThomas Gleixner <tglx@linutronix.de>2007-10-23 16:37:22 -0400
commitedaf420fdc122e7a42326fe39274c8b8c9b19d41 (patch)
tree1bbedab91dfcfe52500a4209a189a2639d933f0a
parent418ccbe37f70f5021c4cd1cdcb0ce7f98d05f2dd (diff)
x86: fix TSC clock source calibration error
I ran into this problem on a system that was unable to obtain NTP sync because the clock was running very slow (over 10000ppm slow). ntpd had declared all of its peers 'reject' with 'peer_dist' reason. On investigation, the tsc_khz variable was significantly incorrect causing xtime to run slow. After a reboot tsc_khz was correct so I did a reboot test to see how often the problem occurred: Test was done on a 2000 Mhz Xeon system. Of 689 reboots, 8 of them had unacceptable tsc_khz values (>500ppm): range of tsc_khz # of boots % of boots ---------------- ---------- ---------- < 1999750 0 0.000% 1999750 - 1999800 21 3.048% 1999800 - 1999850 166 24.128% 1999850 - 1999900 241 35.029% 1999900 - 1999950 211 30.669% 1999950 - 2000000 42 6.105% 2000000 - 2000000 0 0.000% 2000050 - 2000100 0 0.000% [...] 2000100 - 2015000 1 0.145% << BAD 2015000 - 2030000 6 0.872% << BAD 2030000 - 2045000 1 0.145% << BAD 2045000 < 0 0.000% The worst boot was 2032.577 Mhz, over 1.5% off! It appears that on rare occasions, mach_countup() is taking longer to complete than necessary. I suspect that this is caused by the CPU taking a periodic SMI interrupt right at the end of the 30ms calibration loop. This would cause the loop to delay while the SMI BIOS hander runs. The resulting TSC value is beyond what it actually should be resulting in a higher tsc_khz. The below patch makes native_calculate_cpu_khz() take the best (shortest duration, lowest khz) run of it's 3 calibration loops. If a SMI goes off causing a bad result (long duration, higher khz) it will be discarded. With the patch applied, 300 boots of the same system produce good results: range of tsc_khz # of boots % of boots ---------------- ---------- ---------- < 1999750 0 0.000% 1999750 - 1999800 30 10.000% 1999800 - 1999850 166 55.333% 1999850 - 1999900 89 29.667% 1999900 - 1999950 15 5.000% 1999950 < 0 0.000% Problem was found and tested against 2.6.18. Patch is against 2.6.22. Signed-off-by: Dave Johnson <djohnson@sw.starentnetworks.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--arch/x86/kernel/tsc_32.c5
1 files changed, 2 insertions, 3 deletions
diff --git a/arch/x86/kernel/tsc_32.c b/arch/x86/kernel/tsc_32.c
index d78444c788a3..f04d08a7d022 100644
--- a/arch/x86/kernel/tsc_32.c
+++ b/arch/x86/kernel/tsc_32.c
@@ -131,7 +131,7 @@ unsigned long native_calculate_cpu_khz(void)
131{ 131{
132 unsigned long long start, end; 132 unsigned long long start, end;
133 unsigned long count; 133 unsigned long count;
134 u64 delta64; 134 u64 delta64 = (u64)ULLONG_MAX;
135 int i; 135 int i;
136 unsigned long flags; 136 unsigned long flags;
137 137
@@ -143,6 +143,7 @@ unsigned long native_calculate_cpu_khz(void)
143 rdtscll(start); 143 rdtscll(start);
144 mach_countup(&count); 144 mach_countup(&count);
145 rdtscll(end); 145 rdtscll(end);
146 delta64 = min(delta64, (end - start));
146 } 147 }
147 /* 148 /*
148 * Error: ECTCNEVERSET 149 * Error: ECTCNEVERSET
@@ -153,8 +154,6 @@ unsigned long native_calculate_cpu_khz(void)
153 if (count <= 1) 154 if (count <= 1)
154 goto err; 155 goto err;
155 156
156 delta64 = end - start;
157
158 /* cpu freq too fast: */ 157 /* cpu freq too fast: */
159 if (delta64 > (1ULL<<32)) 158 if (delta64 > (1ULL<<32))
160 goto err; 159 goto err;