I think it's about time to enable invariant TSC timecounter on SMP by 
default.  Please see the attached patch.  It is also available from 
here:

http://people.freebsd.org/~jkim/tsc_smp_test4.diff

avg convinced me enough that it should be an opt-out feature going 
forward. :-)

Comments?

Cheers,

Jung-uk Kim
Index: sys/x86/x86/tsc.c
===================================================================
--- sys/x86/x86/tsc.c   (revision 222262)
+++ sys/x86/x86/tsc.c   (working copy)
@@ -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 = {
@@ -385,7 +386,7 @@ test_smp_tsc(void)
        if (bootverbose)
                printf("SMP: %sed TSC synchronization test\n",
                    smp_tsc ? "pass" : "fail");
-       return (smp_tsc ? 800 : -100);
+       return (smp_tsc ? (tsc_is_invariant ? 1000 : 800) : -100);
 }
 
 #undef N
@@ -395,11 +396,19 @@ test_smp_tsc(void)
 static void
 init_TSC_tc(void)
 {
+       uint64_t max_freq;
+       int shift;
 
        if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled)
                return;
 
        /*
+        * Limit timecounter frequency to fit in an int and prevent it from
+        * overflowing too fast.
+        */
+       max_freq = UINT_MAX;
+
+       /*
         * We can not use the TSC if we support APM.  Precise timekeeping
         * on an APM'ed machine is at best a fools pursuit, since 
         * any and all of the time spent in various SMM code can't 
@@ -421,13 +430,30 @@ init_TSC_tc(void)
         * We can not use the TSC in SMP mode unless the TSCs on all CPUs are
         * synchronized.  If the user is sure that the system has synchronized
         * TSCs, set kern.timecounter.smp_tsc tunable to a non-zero value.
+        * We also limit the frequency even lower to avoid "temporal anomalies"
+        * as much as possible.
         */
-       if (smp_cpus > 1)
+       if (smp_cpus > 1) {
                tsc_timecounter.tc_quality = test_smp_tsc();
+               max_freq >>= 8;
+       } else
 #endif
+       if (tsc_is_invariant)
+               tsc_timecounter.tc_quality = 1000;
+
 init:
+       for (shift = 0; shift < 32 && (tsc_freq >> shift) > max_freq; shift++)
+               ;
+       if (shift > 0) {
+               tsc_timecounter.tc_get_timecount = tsc_get_timecount_lowres;
+               tsc_timecounter.tc_name = "TSC-low";
+               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 +525,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 +541,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 +551,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);
+}
_______________________________________________
freebsd-hackers@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
To unsubscribe, send any mail to "freebsd-hackers-unsubscr...@freebsd.org"

Reply via email to