Index: sys/pc98/pc98/machdep.c =================================================================== --- sys/pc98/pc98/machdep.c (revision 216359) +++ sys/pc98/pc98/machdep.c (working copy) @@ -1059,7 +1059,6 @@ int cpu_est_clockrate(int cpu_id, uint64_t *rate) { register_t reg; - uint64_t tsc1, tsc2; if (pcpu_find(cpu_id) == NULL || rate == NULL) return (EINVAL); @@ -1081,9 +1080,7 @@ cpu_est_clockrate(int cpu_id, uint64_t *rate) /* Calibrate by measuring a short delay. */ reg = intr_disable(); - tsc1 = rdtsc(); - DELAY(1000); - tsc2 = rdtsc(); + *rate = tsc_cpu_freq(); intr_restore(reg); #ifdef SMP @@ -1092,17 +1089,6 @@ cpu_est_clockrate(int cpu_id, uint64_t *rate) thread_unlock(curthread); #endif - tsc2 -= tsc1; - if (tsc_freq != 0 && !tsc_is_broken) { - *rate = tsc2 * 1000; - return (0); - } - - /* - * Subtract 0.5% of the total. Empirical testing has shown that - * overhead in DELAY() works out to approximately this value. - */ - *rate = tsc2 * 1000 - tsc2 * 5; return (0); } Index: sys/x86/x86/tsc.c =================================================================== --- sys/x86/x86/tsc.c (revision 216359) +++ sys/x86/x86/tsc.c (working copy) @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -102,7 +103,7 @@ init_TSC(void) tsc_freq = tscval[1] - tscval[0]; if (bootverbose) - printf("TSC clock: %ju Hz\n", (intmax_t)tsc_freq); + printf("TSC clock: %ju Hz\n", (uintmax_t)tsc_freq); switch (cpu_vendor_id) { case CPU_VENDOR_AMD: @@ -125,6 +126,8 @@ init_TSC(void) tsc_is_invariant = 1; break; } + if (tsc_is_invariant) + tsc_cpu_freq(); /* * Inform CPU accounting about our boot-time clock rate. This will @@ -187,6 +190,66 @@ init_TSC_tc(void) } } +static uint64_t +tsc_delay_count(void) +{ + static uint64_t ref_freq; + static u_int ref_count; + uint32_t start, tsc; + u_int i, ticks; + + if (ref_freq == 0) + ref_freq = tsc_freq; + + i = tsc = 0; + ticks = MIN(tsc_freq / 1000, INT_MAX); + start = rdtsc(); + + /* + * Benchmark CPU performance, assuming the following loop costs + * exactly the same CPU cycles regardless of its running frequency. + * Avoid compiler optimizations and use registers only. We do: + * + * do i++; while (rdtsc() - start < ticks); + */ + __asm __volatile("1: add $1,%0; rdtsc; sub %4,%1; cmp %1,%5; ja 1b" + : "=r" (i), "=a" (tsc) + : "0" (i), "1" (tsc), "r" (start), "r" (ticks) + : "edx"); + + if (ref_count == 0) + ref_count = i; + + return (ref_freq * i / ref_count); +} + +static uint64_t +tsc_delay_i8254(void) +{ + uint64_t tsc; + + tsc = rdtsc(); + DELAY(1000); + tsc = rdtsc() - tsc; + if (tsc_freq != 0 && !tsc_is_broken) + return (tsc * 1000); + + /* + * Subtract 0.5% of the total. Empirical testing has shown that + * overhead in DELAY() works out to approximately this value. + */ + return (tsc * 995); +} + +uint64_t +tsc_cpu_freq(void) +{ + + if (tsc_is_invariant) + return (tsc_delay_count()); + return (tsc_delay_i8254()); +} + /* * When cpufreq levels change, find out about the (new) max frequency. We * use this to update CPU accounting in case it got a lower estimate at boot. Index: sys/i386/include/clock.h =================================================================== --- sys/i386/include/clock.h (revision 216359) +++ sys/i386/include/clock.h (working copy) @@ -21,21 +21,22 @@ extern uint64_t tsc_freq; extern int tsc_is_broken; extern int tsc_is_invariant; -void i8254_init(void); +void i8254_init(void); /* * Driver to clock driver interface. */ -void startrtclock(void); -void timer_restore(void); -void init_TSC(void); -void init_TSC_tc(void); +void init_TSC(void); +void init_TSC_tc(void); +void startrtclock(void); +void timer_restore(void); +uint64_t tsc_cpu_freq(void); #define HAS_TIMER_SPKR 1 -int timer_spkr_acquire(void); -int timer_spkr_release(void); -void timer_spkr_setfreq(int freq); +int timer_spkr_acquire(void); +int timer_spkr_release(void); +void timer_spkr_setfreq(int); #endif /* _KERNEL */ Index: sys/i386/i386/machdep.c =================================================================== --- sys/i386/i386/machdep.c (revision 216359) +++ sys/i386/i386/machdep.c (working copy) @@ -1124,7 +1124,6 @@ int cpu_est_clockrate(int cpu_id, uint64_t *rate) { register_t reg; - uint64_t tsc1, tsc2; if (pcpu_find(cpu_id) == NULL || rate == NULL) return (EINVAL); @@ -1146,9 +1145,7 @@ cpu_est_clockrate(int cpu_id, uint64_t *rate) /* Calibrate by measuring a short delay. */ reg = intr_disable(); - tsc1 = rdtsc(); - DELAY(1000); - tsc2 = rdtsc(); + *rate = tsc_cpu_freq(); intr_restore(reg); #ifdef SMP @@ -1157,17 +1154,6 @@ cpu_est_clockrate(int cpu_id, uint64_t *rate) thread_unlock(curthread); #endif - tsc2 -= tsc1; - if (tsc_freq != 0 && !tsc_is_broken) { - *rate = tsc2 * 1000; - return (0); - } - - /* - * Subtract 0.5% of the total. Empirical testing has shown that - * overhead in DELAY() works out to approximately this value. - */ - *rate = tsc2 * 1000 - tsc2 * 5; return (0); } Index: sys/amd64/include/clock.h =================================================================== --- sys/amd64/include/clock.h (revision 216359) +++ sys/amd64/include/clock.h (working copy) @@ -21,20 +21,21 @@ extern uint64_t tsc_freq; extern int tsc_is_broken; extern int tsc_is_invariant; -void i8254_init(void); +void i8254_init(void); /* * Driver to clock driver interface. */ -void startrtclock(void); -void init_TSC(void); -void init_TSC_tc(void); +void init_TSC(void); +void init_TSC_tc(void); +void startrtclock(void); +uint64_t tsc_cpu_freq(void); #define HAS_TIMER_SPKR 1 -int timer_spkr_acquire(void); -int timer_spkr_release(void); -void timer_spkr_setfreq(int freq); +int timer_spkr_acquire(void); +int timer_spkr_release(void); +void timer_spkr_setfreq(int); #endif /* _KERNEL */ Index: sys/amd64/amd64/machdep.c =================================================================== --- sys/amd64/amd64/machdep.c (revision 216359) +++ sys/amd64/amd64/machdep.c (working copy) @@ -532,7 +532,6 @@ int cpu_est_clockrate(int cpu_id, uint64_t *rate) { register_t reg; - uint64_t tsc1, tsc2; if (pcpu_find(cpu_id) == NULL || rate == NULL) return (EINVAL); @@ -552,9 +551,7 @@ cpu_est_clockrate(int cpu_id, uint64_t *rate) /* Calibrate by measuring a short delay. */ reg = intr_disable(); - tsc1 = rdtsc(); - DELAY(1000); - tsc2 = rdtsc(); + *rate = tsc_cpu_freq(); intr_restore(reg); #ifdef SMP @@ -563,7 +560,6 @@ cpu_est_clockrate(int cpu_id, uint64_t *rate) thread_unlock(curthread); #endif - *rate = (tsc2 - tsc1) * 1000; return (0); }