aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathieu Desnoyers <mathieu.desnoyers@polymtl.ca>2005-10-30 17:59:25 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2005-10-30 20:37:11 -0500
commitdacb16b1a034fa7a0b868ee30758119fbfd90bc1 (patch)
treedaaa631c9c6fa2ad011647fb3acd219784faf2e2
parentbfd51626cbf61cb23f787d8ff972ef0d5ddacc0b (diff)
[PATCH] i386 and x86_64 TSC set_cyc2ns_scale imprecision
I just found out that some precision is unnecessarily lost in the arch/i386/kernel/timers/timer_tsc.c:set_cyc2ns_scale function. It uses a cpu_mhz parameter when it could use a cpu_khz. In the specific case of an Intel P4 running at 3001.171 Mhz, the truncation to 3001 Mhz leads to an imprecision of 19 microseconds per second : this is very sad for a timer with nearly nanosecond accuracy. Fix the x86_64 architecture too. Cc: george anzinger <george@mvista.com> Cc: john stultz <johnstul@us.ibm.com> Cc: Andi Kleen <ak@muc.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/i386/kernel/timers/timer_hpet.c17
-rw-r--r--arch/i386/kernel/timers/timer_tsc.c21
-rw-r--r--arch/x86_64/kernel/time.c8
3 files changed, 28 insertions, 18 deletions
diff --git a/arch/i386/kernel/timers/timer_hpet.c b/arch/i386/kernel/timers/timer_hpet.c
index d973a8b681fd..be242723c339 100644
--- a/arch/i386/kernel/timers/timer_hpet.c
+++ b/arch/i386/kernel/timers/timer_hpet.c
@@ -30,23 +30,28 @@ static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;
30 * basic equation: 30 * basic equation:
31 * ns = cycles / (freq / ns_per_sec) 31 * ns = cycles / (freq / ns_per_sec)
32 * ns = cycles * (ns_per_sec / freq) 32 * ns = cycles * (ns_per_sec / freq)
33 * ns = cycles * (10^9 / (cpu_mhz * 10^6)) 33 * ns = cycles * (10^9 / (cpu_khz * 10^3))
34 * ns = cycles * (10^3 / cpu_mhz) 34 * ns = cycles * (10^6 / cpu_khz)
35 * 35 *
36 * Then we use scaling math (suggested by george@mvista.com) to get: 36 * Then we use scaling math (suggested by george@mvista.com) to get:
37 * ns = cycles * (10^3 * SC / cpu_mhz) / SC 37 * ns = cycles * (10^6 * SC / cpu_khz) / SC
38 * ns = cycles * cyc2ns_scale / SC 38 * ns = cycles * cyc2ns_scale / SC
39 * 39 *
40 * And since SC is a constant power of two, we can convert the div 40 * And since SC is a constant power of two, we can convert the div
41 * into a shift. 41 * into a shift.
42 *
43 * We can use khz divisor instead of mhz to keep a better percision, since
44 * cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits.
45 * (mathieu.desnoyers@polymtl.ca)
46 *
42 * -johnstul@us.ibm.com "math is hard, lets go shopping!" 47 * -johnstul@us.ibm.com "math is hard, lets go shopping!"
43 */ 48 */
44static unsigned long cyc2ns_scale; 49static unsigned long cyc2ns_scale;
45#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ 50#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
46 51
47static inline void set_cyc2ns_scale(unsigned long cpu_mhz) 52static inline void set_cyc2ns_scale(unsigned long cpu_khz)
48{ 53{
49 cyc2ns_scale = (1000 << CYC2NS_SCALE_FACTOR)/cpu_mhz; 54 cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz;
50} 55}
51 56
52static inline unsigned long long cycles_2_ns(unsigned long long cyc) 57static inline unsigned long long cycles_2_ns(unsigned long long cyc)
@@ -163,7 +168,7 @@ static int __init init_hpet(char* override)
163 printk("Detected %u.%03u MHz processor.\n", 168 printk("Detected %u.%03u MHz processor.\n",
164 cpu_khz / 1000, cpu_khz % 1000); 169 cpu_khz / 1000, cpu_khz % 1000);
165 } 170 }
166 set_cyc2ns_scale(cpu_khz/1000); 171 set_cyc2ns_scale(cpu_khz);
167 } 172 }
168 /* set this only when cpu_has_tsc */ 173 /* set this only when cpu_has_tsc */
169 timer_hpet.read_timer = read_timer_tsc; 174 timer_hpet.read_timer = read_timer_tsc;
diff --git a/arch/i386/kernel/timers/timer_tsc.c b/arch/i386/kernel/timers/timer_tsc.c
index 6dd470cc9f72..d395e3b42485 100644
--- a/arch/i386/kernel/timers/timer_tsc.c
+++ b/arch/i386/kernel/timers/timer_tsc.c
@@ -49,23 +49,28 @@ static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;
49 * basic equation: 49 * basic equation:
50 * ns = cycles / (freq / ns_per_sec) 50 * ns = cycles / (freq / ns_per_sec)
51 * ns = cycles * (ns_per_sec / freq) 51 * ns = cycles * (ns_per_sec / freq)
52 * ns = cycles * (10^9 / (cpu_mhz * 10^6)) 52 * ns = cycles * (10^9 / (cpu_khz * 10^3))
53 * ns = cycles * (10^3 / cpu_mhz) 53 * ns = cycles * (10^6 / cpu_khz)
54 * 54 *
55 * Then we use scaling math (suggested by george@mvista.com) to get: 55 * Then we use scaling math (suggested by george@mvista.com) to get:
56 * ns = cycles * (10^3 * SC / cpu_mhz) / SC 56 * ns = cycles * (10^6 * SC / cpu_khz) / SC
57 * ns = cycles * cyc2ns_scale / SC 57 * ns = cycles * cyc2ns_scale / SC
58 * 58 *
59 * And since SC is a constant power of two, we can convert the div 59 * And since SC is a constant power of two, we can convert the div
60 * into a shift. 60 * into a shift.
61 *
62 * We can use khz divisor instead of mhz to keep a better percision, since
63 * cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits.
64 * (mathieu.desnoyers@polymtl.ca)
65 *
61 * -johnstul@us.ibm.com "math is hard, lets go shopping!" 66 * -johnstul@us.ibm.com "math is hard, lets go shopping!"
62 */ 67 */
63static unsigned long cyc2ns_scale; 68static unsigned long cyc2ns_scale;
64#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ 69#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
65 70
66static inline void set_cyc2ns_scale(unsigned long cpu_mhz) 71static inline void set_cyc2ns_scale(unsigned long cpu_khz)
67{ 72{
68 cyc2ns_scale = (1000 << CYC2NS_SCALE_FACTOR)/cpu_mhz; 73 cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz;
69} 74}
70 75
71static inline unsigned long long cycles_2_ns(unsigned long long cyc) 76static inline unsigned long long cycles_2_ns(unsigned long long cyc)
@@ -286,7 +291,7 @@ time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
286 if (use_tsc) { 291 if (use_tsc) {
287 if (!(freq->flags & CPUFREQ_CONST_LOOPS)) { 292 if (!(freq->flags & CPUFREQ_CONST_LOOPS)) {
288 fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq); 293 fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq);
289 set_cyc2ns_scale(cpu_khz/1000); 294 set_cyc2ns_scale(cpu_khz);
290 } 295 }
291 } 296 }
292#endif 297#endif
@@ -536,7 +541,7 @@ static int __init init_tsc(char* override)
536 printk("Detected %u.%03u MHz processor.\n", 541 printk("Detected %u.%03u MHz processor.\n",
537 cpu_khz / 1000, cpu_khz % 1000); 542 cpu_khz / 1000, cpu_khz % 1000);
538 } 543 }
539 set_cyc2ns_scale(cpu_khz/1000); 544 set_cyc2ns_scale(cpu_khz);
540 return 0; 545 return 0;
541 } 546 }
542 } 547 }
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c
index 703acde2a1a5..47d25ad08160 100644
--- a/arch/x86_64/kernel/time.c
+++ b/arch/x86_64/kernel/time.c
@@ -481,9 +481,9 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
481static unsigned int cyc2ns_scale; 481static unsigned int cyc2ns_scale;
482#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ 482#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
483 483
484static inline void set_cyc2ns_scale(unsigned long cpu_mhz) 484static inline void set_cyc2ns_scale(unsigned long cpu_khz)
485{ 485{
486 cyc2ns_scale = (1000 << CYC2NS_SCALE_FACTOR)/cpu_mhz; 486 cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz;
487} 487}
488 488
489static inline unsigned long long cycles_2_ns(unsigned long long cyc) 489static inline unsigned long long cycles_2_ns(unsigned long long cyc)
@@ -655,7 +655,7 @@ static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
655 vxtime.tsc_quot = (1000L << 32) / cpu_khz; 655 vxtime.tsc_quot = (1000L << 32) / cpu_khz;
656 } 656 }
657 657
658 set_cyc2ns_scale(cpu_khz_ref / 1000); 658 set_cyc2ns_scale(cpu_khz_ref);
659 659
660 return 0; 660 return 0;
661} 661}
@@ -939,7 +939,7 @@ void __init time_init(void)
939 rdtscll_sync(&vxtime.last_tsc); 939 rdtscll_sync(&vxtime.last_tsc);
940 setup_irq(0, &irq0); 940 setup_irq(0, &irq0);
941 941
942 set_cyc2ns_scale(cpu_khz / 1000); 942 set_cyc2ns_scale(cpu_khz);
943 943
944#ifndef CONFIG_SMP 944#ifndef CONFIG_SMP
945 time_init_gtod(); 945 time_init_gtod();