diff options
-rw-r--r-- | arch/x86/kernel/tsc_32.c | 15 | ||||
-rw-r--r-- | arch/x86/kernel/tsc_64.c | 23 | ||||
-rw-r--r-- | kernel/time/timekeeping.c | 2 |
3 files changed, 36 insertions, 4 deletions
diff --git a/arch/x86/kernel/tsc_32.c b/arch/x86/kernel/tsc_32.c index 06af8cf8251f..e4790728b224 100644 --- a/arch/x86/kernel/tsc_32.c +++ b/arch/x86/kernel/tsc_32.c | |||
@@ -284,14 +284,27 @@ core_initcall(cpufreq_tsc); | |||
284 | /* clock source code */ | 284 | /* clock source code */ |
285 | 285 | ||
286 | static unsigned long current_tsc_khz; | 286 | static unsigned long current_tsc_khz; |
287 | static struct clocksource clocksource_tsc; | ||
287 | 288 | ||
289 | /* | ||
290 | * We compare the TSC to the cycle_last value in the clocksource | ||
291 | * structure to avoid a nasty time-warp issue. This can be observed in | ||
292 | * a very small window right after one CPU updated cycle_last under | ||
293 | * xtime lock and the other CPU reads a TSC value which is smaller | ||
294 | * than the cycle_last reference value due to a TSC which is slighty | ||
295 | * behind. This delta is nowhere else observable, but in that case it | ||
296 | * results in a forward time jump in the range of hours due to the | ||
297 | * unsigned delta calculation of the time keeping core code, which is | ||
298 | * necessary to support wrapping clocksources like pm timer. | ||
299 | */ | ||
288 | static cycle_t read_tsc(void) | 300 | static cycle_t read_tsc(void) |
289 | { | 301 | { |
290 | cycle_t ret; | 302 | cycle_t ret; |
291 | 303 | ||
292 | rdtscll(ret); | 304 | rdtscll(ret); |
293 | 305 | ||
294 | return ret; | 306 | return ret >= clocksource_tsc.cycle_last ? |
307 | ret : clocksource_tsc.cycle_last; | ||
295 | } | 308 | } |
296 | 309 | ||
297 | static struct clocksource clocksource_tsc = { | 310 | static struct clocksource clocksource_tsc = { |
diff --git a/arch/x86/kernel/tsc_64.c b/arch/x86/kernel/tsc_64.c index ceeba01e7f47..fcc16e58609e 100644 --- a/arch/x86/kernel/tsc_64.c +++ b/arch/x86/kernel/tsc_64.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <asm/hpet.h> | 11 | #include <asm/hpet.h> |
12 | #include <asm/timex.h> | 12 | #include <asm/timex.h> |
13 | #include <asm/timer.h> | 13 | #include <asm/timer.h> |
14 | #include <asm/vgtod.h> | ||
14 | 15 | ||
15 | static int notsc __initdata = 0; | 16 | static int notsc __initdata = 0; |
16 | 17 | ||
@@ -287,18 +288,34 @@ int __init notsc_setup(char *s) | |||
287 | 288 | ||
288 | __setup("notsc", notsc_setup); | 289 | __setup("notsc", notsc_setup); |
289 | 290 | ||
291 | static struct clocksource clocksource_tsc; | ||
290 | 292 | ||
291 | /* clock source code: */ | 293 | /* |
294 | * We compare the TSC to the cycle_last value in the clocksource | ||
295 | * structure to avoid a nasty time-warp. This can be observed in a | ||
296 | * very small window right after one CPU updated cycle_last under | ||
297 | * xtime/vsyscall_gtod lock and the other CPU reads a TSC value which | ||
298 | * is smaller than the cycle_last reference value due to a TSC which | ||
299 | * is slighty behind. This delta is nowhere else observable, but in | ||
300 | * that case it results in a forward time jump in the range of hours | ||
301 | * due to the unsigned delta calculation of the time keeping core | ||
302 | * code, which is necessary to support wrapping clocksources like pm | ||
303 | * timer. | ||
304 | */ | ||
292 | static cycle_t read_tsc(void) | 305 | static cycle_t read_tsc(void) |
293 | { | 306 | { |
294 | cycle_t ret = (cycle_t)get_cycles(); | 307 | cycle_t ret = (cycle_t)get_cycles(); |
295 | return ret; | 308 | |
309 | return ret >= clocksource_tsc.cycle_last ? | ||
310 | ret : clocksource_tsc.cycle_last; | ||
296 | } | 311 | } |
297 | 312 | ||
298 | static cycle_t __vsyscall_fn vread_tsc(void) | 313 | static cycle_t __vsyscall_fn vread_tsc(void) |
299 | { | 314 | { |
300 | cycle_t ret = (cycle_t)vget_cycles(); | 315 | cycle_t ret = (cycle_t)vget_cycles(); |
301 | return ret; | 316 | |
317 | return ret >= __vsyscall_gtod_data.clock.cycle_last ? | ||
318 | ret : __vsyscall_gtod_data.clock.cycle_last; | ||
302 | } | 319 | } |
303 | 320 | ||
304 | static struct clocksource clocksource_tsc = { | 321 | static struct clocksource clocksource_tsc = { |
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index a3fa587c350c..2d6087c7cf98 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c | |||
@@ -178,6 +178,7 @@ static void change_clocksource(void) | |||
178 | if (clock == new) | 178 | if (clock == new) |
179 | return; | 179 | return; |
180 | 180 | ||
181 | new->cycle_last = 0; | ||
181 | now = clocksource_read(new); | 182 | now = clocksource_read(new); |
182 | nsec = __get_nsec_offset(); | 183 | nsec = __get_nsec_offset(); |
183 | timespec_add_ns(&xtime, nsec); | 184 | timespec_add_ns(&xtime, nsec); |
@@ -295,6 +296,7 @@ static int timekeeping_resume(struct sys_device *dev) | |||
295 | timespec_add_ns(&xtime, timekeeping_suspend_nsecs); | 296 | timespec_add_ns(&xtime, timekeeping_suspend_nsecs); |
296 | update_xtime_cache(0); | 297 | update_xtime_cache(0); |
297 | /* re-base the last cycle value */ | 298 | /* re-base the last cycle value */ |
299 | clock->cycle_last = 0; | ||
298 | clock->cycle_last = clocksource_read(clock); | 300 | clock->cycle_last = clocksource_read(clock); |
299 | clock->error = 0; | 301 | clock->error = 0; |
300 | timekeeping_suspended = 0; | 302 | timekeeping_suspended = 0; |