diff options
Diffstat (limited to 'arch/i386/kernel/tsc.c')
-rw-r--r-- | arch/i386/kernel/tsc.c | 195 |
1 files changed, 64 insertions, 131 deletions
diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c index 2cfc7b09b925..3082a418635c 100644 --- a/arch/i386/kernel/tsc.c +++ b/arch/i386/kernel/tsc.c | |||
@@ -23,6 +23,7 @@ | |||
23 | * an extra value to store the TSC freq | 23 | * an extra value to store the TSC freq |
24 | */ | 24 | */ |
25 | unsigned int tsc_khz; | 25 | unsigned int tsc_khz; |
26 | unsigned long long (*custom_sched_clock)(void); | ||
26 | 27 | ||
27 | int tsc_disable; | 28 | int tsc_disable; |
28 | 29 | ||
@@ -59,12 +60,6 @@ static inline int check_tsc_unstable(void) | |||
59 | return tsc_unstable; | 60 | return tsc_unstable; |
60 | } | 61 | } |
61 | 62 | ||
62 | void mark_tsc_unstable(void) | ||
63 | { | ||
64 | tsc_unstable = 1; | ||
65 | } | ||
66 | EXPORT_SYMBOL_GPL(mark_tsc_unstable); | ||
67 | |||
68 | /* Accellerators for sched_clock() | 63 | /* Accellerators for sched_clock() |
69 | * convert from cycles(64bits) => nanoseconds (64bits) | 64 | * convert from cycles(64bits) => nanoseconds (64bits) |
70 | * basic equation: | 65 | * basic equation: |
@@ -107,14 +102,14 @@ unsigned long long sched_clock(void) | |||
107 | { | 102 | { |
108 | unsigned long long this_offset; | 103 | unsigned long long this_offset; |
109 | 104 | ||
105 | if (unlikely(custom_sched_clock)) | ||
106 | return (*custom_sched_clock)(); | ||
107 | |||
110 | /* | 108 | /* |
111 | * in the NUMA case we dont use the TSC as they are not | 109 | * Fall back to jiffies if there's no TSC available: |
112 | * synchronized across all CPUs. | ||
113 | */ | 110 | */ |
114 | #ifndef CONFIG_NUMA | 111 | if (unlikely(tsc_disable)) |
115 | if (!cpu_khz || check_tsc_unstable()) | 112 | /* No locking but a rare wrong value is not a big deal: */ |
116 | #endif | ||
117 | /* no locking but a rare wrong value is not a big deal */ | ||
118 | return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ); | 113 | return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ); |
119 | 114 | ||
120 | /* read the Time Stamp Counter: */ | 115 | /* read the Time Stamp Counter: */ |
@@ -194,13 +189,13 @@ EXPORT_SYMBOL(recalibrate_cpu_khz); | |||
194 | void __init tsc_init(void) | 189 | void __init tsc_init(void) |
195 | { | 190 | { |
196 | if (!cpu_has_tsc || tsc_disable) | 191 | if (!cpu_has_tsc || tsc_disable) |
197 | return; | 192 | goto out_no_tsc; |
198 | 193 | ||
199 | cpu_khz = calculate_cpu_khz(); | 194 | cpu_khz = calculate_cpu_khz(); |
200 | tsc_khz = cpu_khz; | 195 | tsc_khz = cpu_khz; |
201 | 196 | ||
202 | if (!cpu_khz) | 197 | if (!cpu_khz) |
203 | return; | 198 | goto out_no_tsc; |
204 | 199 | ||
205 | printk("Detected %lu.%03lu MHz processor.\n", | 200 | printk("Detected %lu.%03lu MHz processor.\n", |
206 | (unsigned long)cpu_khz / 1000, | 201 | (unsigned long)cpu_khz / 1000, |
@@ -208,37 +203,18 @@ void __init tsc_init(void) | |||
208 | 203 | ||
209 | set_cyc2ns_scale(cpu_khz); | 204 | set_cyc2ns_scale(cpu_khz); |
210 | use_tsc_delay(); | 205 | use_tsc_delay(); |
211 | } | 206 | return; |
212 | 207 | ||
213 | #ifdef CONFIG_CPU_FREQ | 208 | out_no_tsc: |
214 | 209 | /* | |
215 | static unsigned int cpufreq_delayed_issched = 0; | 210 | * Set the tsc_disable flag if there's no TSC support, this |
216 | static unsigned int cpufreq_init = 0; | 211 | * makes it a fast flag for the kernel to see whether it |
217 | static struct work_struct cpufreq_delayed_get_work; | 212 | * should be using the TSC. |
218 | 213 | */ | |
219 | static void handle_cpufreq_delayed_get(struct work_struct *work) | 214 | tsc_disable = 1; |
220 | { | ||
221 | unsigned int cpu; | ||
222 | |||
223 | for_each_online_cpu(cpu) | ||
224 | cpufreq_get(cpu); | ||
225 | |||
226 | cpufreq_delayed_issched = 0; | ||
227 | } | 215 | } |
228 | 216 | ||
229 | /* | 217 | #ifdef CONFIG_CPU_FREQ |
230 | * if we notice cpufreq oddness, schedule a call to cpufreq_get() as it tries | ||
231 | * to verify the CPU frequency the timing core thinks the CPU is running | ||
232 | * at is still correct. | ||
233 | */ | ||
234 | static inline void cpufreq_delayed_get(void) | ||
235 | { | ||
236 | if (cpufreq_init && !cpufreq_delayed_issched) { | ||
237 | cpufreq_delayed_issched = 1; | ||
238 | printk(KERN_DEBUG "Checking if CPU frequency changed.\n"); | ||
239 | schedule_work(&cpufreq_delayed_get_work); | ||
240 | } | ||
241 | } | ||
242 | 218 | ||
243 | /* | 219 | /* |
244 | * if the CPU frequency is scaled, TSC-based delays will need a different | 220 | * if the CPU frequency is scaled, TSC-based delays will need a different |
@@ -303,17 +279,9 @@ static struct notifier_block time_cpufreq_notifier_block = { | |||
303 | 279 | ||
304 | static int __init cpufreq_tsc(void) | 280 | static int __init cpufreq_tsc(void) |
305 | { | 281 | { |
306 | int ret; | 282 | return cpufreq_register_notifier(&time_cpufreq_notifier_block, |
307 | 283 | CPUFREQ_TRANSITION_NOTIFIER); | |
308 | INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get); | ||
309 | ret = cpufreq_register_notifier(&time_cpufreq_notifier_block, | ||
310 | CPUFREQ_TRANSITION_NOTIFIER); | ||
311 | if (!ret) | ||
312 | cpufreq_init = 1; | ||
313 | |||
314 | return ret; | ||
315 | } | 284 | } |
316 | |||
317 | core_initcall(cpufreq_tsc); | 285 | core_initcall(cpufreq_tsc); |
318 | 286 | ||
319 | #endif | 287 | #endif |
@@ -321,7 +289,6 @@ core_initcall(cpufreq_tsc); | |||
321 | /* clock source code */ | 289 | /* clock source code */ |
322 | 290 | ||
323 | static unsigned long current_tsc_khz = 0; | 291 | static unsigned long current_tsc_khz = 0; |
324 | static int tsc_update_callback(void); | ||
325 | 292 | ||
326 | static cycle_t read_tsc(void) | 293 | static cycle_t read_tsc(void) |
327 | { | 294 | { |
@@ -339,37 +306,28 @@ static struct clocksource clocksource_tsc = { | |||
339 | .mask = CLOCKSOURCE_MASK(64), | 306 | .mask = CLOCKSOURCE_MASK(64), |
340 | .mult = 0, /* to be set */ | 307 | .mult = 0, /* to be set */ |
341 | .shift = 22, | 308 | .shift = 22, |
342 | .update_callback = tsc_update_callback, | 309 | .flags = CLOCK_SOURCE_IS_CONTINUOUS | |
343 | .is_continuous = 1, | 310 | CLOCK_SOURCE_MUST_VERIFY, |
344 | }; | 311 | }; |
345 | 312 | ||
346 | static int tsc_update_callback(void) | 313 | void mark_tsc_unstable(void) |
347 | { | 314 | { |
348 | int change = 0; | 315 | if (!tsc_unstable) { |
349 | 316 | tsc_unstable = 1; | |
350 | /* check to see if we should switch to the safe clocksource: */ | 317 | /* Can be called before registration */ |
351 | if (clocksource_tsc.rating != 0 && check_tsc_unstable()) { | 318 | if (clocksource_tsc.mult) |
352 | clocksource_tsc.rating = 0; | 319 | clocksource_change_rating(&clocksource_tsc, 0); |
353 | clocksource_reselect(); | 320 | else |
354 | change = 1; | 321 | clocksource_tsc.rating = 0; |
355 | } | ||
356 | |||
357 | /* only update if tsc_khz has changed: */ | ||
358 | if (current_tsc_khz != tsc_khz) { | ||
359 | current_tsc_khz = tsc_khz; | ||
360 | clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz, | ||
361 | clocksource_tsc.shift); | ||
362 | change = 1; | ||
363 | } | 322 | } |
364 | |||
365 | return change; | ||
366 | } | 323 | } |
324 | EXPORT_SYMBOL_GPL(mark_tsc_unstable); | ||
367 | 325 | ||
368 | static int __init dmi_mark_tsc_unstable(struct dmi_system_id *d) | 326 | static int __init dmi_mark_tsc_unstable(struct dmi_system_id *d) |
369 | { | 327 | { |
370 | printk(KERN_NOTICE "%s detected: marking TSC unstable.\n", | 328 | printk(KERN_NOTICE "%s detected: marking TSC unstable.\n", |
371 | d->ident); | 329 | d->ident); |
372 | mark_tsc_unstable(); | 330 | tsc_unstable = 1; |
373 | return 0; | 331 | return 0; |
374 | } | 332 | } |
375 | 333 | ||
@@ -386,65 +344,44 @@ static struct dmi_system_id __initdata bad_tsc_dmi_table[] = { | |||
386 | {} | 344 | {} |
387 | }; | 345 | }; |
388 | 346 | ||
389 | #define TSC_FREQ_CHECK_INTERVAL (10*MSEC_PER_SEC) /* 10sec in MS */ | ||
390 | static struct timer_list verify_tsc_freq_timer; | ||
391 | |||
392 | /* XXX - Probably should add locking */ | ||
393 | static void verify_tsc_freq(unsigned long unused) | ||
394 | { | ||
395 | static u64 last_tsc; | ||
396 | static unsigned long last_jiffies; | ||
397 | |||
398 | u64 now_tsc, interval_tsc; | ||
399 | unsigned long now_jiffies, interval_jiffies; | ||
400 | |||
401 | |||
402 | if (check_tsc_unstable()) | ||
403 | return; | ||
404 | |||
405 | rdtscll(now_tsc); | ||
406 | now_jiffies = jiffies; | ||
407 | |||
408 | if (!last_jiffies) { | ||
409 | goto out; | ||
410 | } | ||
411 | |||
412 | interval_jiffies = now_jiffies - last_jiffies; | ||
413 | interval_tsc = now_tsc - last_tsc; | ||
414 | interval_tsc *= HZ; | ||
415 | do_div(interval_tsc, cpu_khz*1000); | ||
416 | |||
417 | if (interval_tsc < (interval_jiffies * 3 / 4)) { | ||
418 | printk("TSC appears to be running slowly. " | ||
419 | "Marking it as unstable\n"); | ||
420 | mark_tsc_unstable(); | ||
421 | return; | ||
422 | } | ||
423 | |||
424 | out: | ||
425 | last_tsc = now_tsc; | ||
426 | last_jiffies = now_jiffies; | ||
427 | /* set us up to go off on the next interval: */ | ||
428 | mod_timer(&verify_tsc_freq_timer, | ||
429 | jiffies + msecs_to_jiffies(TSC_FREQ_CHECK_INTERVAL)); | ||
430 | } | ||
431 | |||
432 | /* | 347 | /* |
433 | * Make an educated guess if the TSC is trustworthy and synchronized | 348 | * Make an educated guess if the TSC is trustworthy and synchronized |
434 | * over all CPUs. | 349 | * over all CPUs. |
435 | */ | 350 | */ |
436 | static __init int unsynchronized_tsc(void) | 351 | __cpuinit int unsynchronized_tsc(void) |
437 | { | 352 | { |
353 | if (!cpu_has_tsc || tsc_unstable) | ||
354 | return 1; | ||
438 | /* | 355 | /* |
439 | * Intel systems are normally all synchronized. | 356 | * Intel systems are normally all synchronized. |
440 | * Exceptions must mark TSC as unstable: | 357 | * Exceptions must mark TSC as unstable: |
441 | */ | 358 | */ |
442 | if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) | 359 | if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) { |
443 | return 0; | 360 | /* assume multi socket systems are not synchronized: */ |
361 | if (num_possible_cpus() > 1) | ||
362 | tsc_unstable = 1; | ||
363 | } | ||
364 | return tsc_unstable; | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * Geode_LX - the OLPC CPU has a possibly a very reliable TSC | ||
369 | */ | ||
370 | #ifdef CONFIG_MGEODE_LX | ||
371 | /* RTSC counts during suspend */ | ||
372 | #define RTSC_SUSP 0x100 | ||
373 | |||
374 | static void __init check_geode_tsc_reliable(void) | ||
375 | { | ||
376 | unsigned long val; | ||
444 | 377 | ||
445 | /* assume multi socket systems are not synchronized: */ | 378 | rdmsrl(MSR_GEODE_BUSCONT_CONF0, val); |
446 | return num_possible_cpus() > 1; | 379 | if ((val & RTSC_SUSP)) |
380 | clocksource_tsc.flags &= ~CLOCK_SOURCE_MUST_VERIFY; | ||
447 | } | 381 | } |
382 | #else | ||
383 | static inline void check_geode_tsc_reliable(void) { } | ||
384 | #endif | ||
448 | 385 | ||
449 | static int __init init_tsc_clocksource(void) | 386 | static int __init init_tsc_clocksource(void) |
450 | { | 387 | { |
@@ -453,20 +390,16 @@ static int __init init_tsc_clocksource(void) | |||
453 | /* check blacklist */ | 390 | /* check blacklist */ |
454 | dmi_check_system(bad_tsc_dmi_table); | 391 | dmi_check_system(bad_tsc_dmi_table); |
455 | 392 | ||
456 | if (unsynchronized_tsc()) /* mark unstable if unsynced */ | 393 | unsynchronized_tsc(); |
457 | mark_tsc_unstable(); | 394 | check_geode_tsc_reliable(); |
458 | current_tsc_khz = tsc_khz; | 395 | current_tsc_khz = tsc_khz; |
459 | clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz, | 396 | clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz, |
460 | clocksource_tsc.shift); | 397 | clocksource_tsc.shift); |
461 | /* lower the rating if we already know its unstable: */ | 398 | /* lower the rating if we already know its unstable: */ |
462 | if (check_tsc_unstable()) | 399 | if (check_tsc_unstable()) { |
463 | clocksource_tsc.rating = 0; | 400 | clocksource_tsc.rating = 0; |
464 | 401 | clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS; | |
465 | init_timer(&verify_tsc_freq_timer); | 402 | } |
466 | verify_tsc_freq_timer.function = verify_tsc_freq; | ||
467 | verify_tsc_freq_timer.expires = | ||
468 | jiffies + msecs_to_jiffies(TSC_FREQ_CHECK_INTERVAL); | ||
469 | add_timer(&verify_tsc_freq_timer); | ||
470 | 403 | ||
471 | return clocksource_register(&clocksource_tsc); | 404 | return clocksource_register(&clocksource_tsc); |
472 | } | 405 | } |