Index: sys/x86/x86/tsc.c =================================================================== --- sys/x86/x86/tsc.c (revision 222084) +++ sys/x86/x86/tsc.c (working copy) @@ -58,8 +58,8 @@ SYSCTL_INT(_kern_timecounter, OID_AUTO, invariant_ &tsc_is_invariant, 0, "Indicates whether the TSC is P-state invariant"); TUNABLE_INT("kern.timecounter.invariant_tsc", &tsc_is_invariant); +static int smp_tsc; #ifdef SMP -static int smp_tsc; SYSCTL_INT(_kern_timecounter, OID_AUTO, smp_tsc, CTLFLAG_RDTUN, &smp_tsc, 0, "Indicates whether the TSC is safe to use in SMP mode"); TUNABLE_INT("kern.timecounter.smp_tsc", &smp_tsc); @@ -79,7 +79,8 @@ static void tsc_freq_changed(void *arg, const stru int status); static void tsc_freq_changing(void *arg, const struct cf_level *level, int *status); -static unsigned tsc_get_timecount(struct timecounter *tc); +static unsigned tsc_get_timecount(struct timecounter *tc); +static unsigned tsc_get_timecount_lowres(struct timecounter *tc); static void tsc_levels_changed(void *arg, int unit); static struct timecounter tsc_timecounter = { @@ -166,9 +167,7 @@ tsc_freq_vmware(void) tsc_freq = regs[0] | ((uint64_t)regs[1] << 32); } tsc_is_invariant = 1; -#ifdef SMP smp_tsc = 1; /* XXX */ -#endif return (1); } @@ -395,6 +394,7 @@ test_smp_tsc(void) static void init_TSC_tc(void) { + int shift; if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled) return; @@ -425,9 +425,31 @@ init_TSC_tc(void) if (smp_cpus > 1) tsc_timecounter.tc_quality = test_smp_tsc(); #endif + if (tsc_is_invariant) + tsc_timecounter.tc_quality = 1000; + init: + /* + * Lower timecounter frequency to avoid overflowing too fast. + * Also, it reduces chances of "timecounter going backwards", + * just in case all TSCs are not perfectly synchronized for SMP. + * Find a number of bits to discard and make a new frequency + * between 8388609 (2^23+1) and 16777216 (2^24) Hz, inclusive. + * This frequency should be just good enough for a timecounter. + */ + shift = 0; + if (tsc_freq > UINT_MAX || smp_tsc) + while (shift < 32 && (tsc_freq >> shift) > ((uint64_t)1 << 24)) + shift++; + if (shift > 0) { + tsc_timecounter.tc_get_timecount = tsc_get_timecount_lowres; + if (bootverbose) + printf("TSC timecounter discards lower %d bit(s).\n", + shift); + } if (tsc_freq != 0) { - tsc_timecounter.tc_frequency = tsc_freq; + tsc_timecounter.tc_frequency = tsc_freq >> shift; + tsc_timecounter.tc_priv = (void *)(intptr_t)shift; tc_init(&tsc_timecounter); } } @@ -499,7 +521,8 @@ tsc_freq_changed(void *arg, const struct cf_level /* Total setting for this level gives the new frequency in MHz. */ freq = (uint64_t)level->total_set.freq * 1000000; atomic_store_rel_64(&tsc_freq, freq); - atomic_store_rel_64(&tsc_timecounter.tc_frequency, freq); + tsc_timecounter.tc_frequency = + freq >> (int)(intptr_t)tsc_timecounter.tc_priv; } static int @@ -514,7 +537,8 @@ sysctl_machdep_tsc_freq(SYSCTL_HANDLER_ARGS) error = sysctl_handle_64(oidp, &freq, 0, req); if (error == 0 && req->newptr != NULL) { atomic_store_rel_64(&tsc_freq, freq); - atomic_store_rel_64(&tsc_timecounter.tc_frequency, freq); + tsc_timecounter.tc_frequency = + freq >> (int)(intptr_t)tsc_timecounter.tc_priv; } return (error); } @@ -523,8 +547,15 @@ SYSCTL_PROC(_machdep, OID_AUTO, tsc_freq, CTLTYPE_ 0, 0, sysctl_machdep_tsc_freq, "QU", "Time Stamp Counter frequency"); static u_int -tsc_get_timecount(struct timecounter *tc) +tsc_get_timecount(struct timecounter *tc __unused) { return (rdtsc32()); } + +static u_int +tsc_get_timecount_lowres(struct timecounter *tc) +{ + + return (rdtsc() >> (int)(intptr_t)tc->tc_priv); +}