summaryrefslogtreecommitdiffstats
path: root/sys/x86
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2013-04-18 17:07:04 +0000
committermav <mav@FreeBSD.org>2013-04-18 17:07:04 +0000
commit5055fe41fa2e36b45e64fd5649e8eb7738c82f5e (patch)
tree21ab0f5e1cad6713574f2c4d53ec61f353e7ce13 /sys/x86
parentef3a1169e5fce4941a31bbd3ceb50e8c930e2720 (diff)
downloadFreeBSD-src-5055fe41fa2e36b45e64fd5649e8eb7738c82f5e.zip
FreeBSD-src-5055fe41fa2e36b45e64fd5649e8eb7738c82f5e.tar.gz
Introduce kern.timecounter.smp_tsc_adjust tunable (disabled by default) and
respective functionality, allowing to synchronize TSC on APs to match BSP's during boot. It may be unsafe in general case due to theoretical chance of later drift if CPUs are using different clock rate or source, but it allows to use TSC in some cases when difference caused by some initialization bug, while TSCs are known to increment synchronously. Reviewed by: jimharris, kib MFC after: 1 month
Diffstat (limited to 'sys/x86')
-rw-r--r--sys/x86/x86/tsc.c63
1 files changed, 60 insertions, 3 deletions
diff --git a/sys/x86/x86/tsc.c b/sys/x86/x86/tsc.c
index 0994e9c..2a6c81d 100644
--- a/sys/x86/x86/tsc.c
+++ b/sys/x86/x86/tsc.c
@@ -65,6 +65,11 @@ 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);
+
+int smp_tsc_adjust = 0;
+SYSCTL_INT(_kern_timecounter, OID_AUTO, smp_tsc_adjust, CTLFLAG_RDTUN,
+ &smp_tsc_adjust, 0, "Try to adjust TSC on APs to match BSP");
+TUNABLE_INT("kern.timecounter.smp_tsc_adjust", &smp_tsc_adjust);
#endif
static int tsc_shift = 1;
@@ -403,25 +408,77 @@ comp_smp_tsc(void *arg)
}
}
+static void
+adj_smp_tsc(void *arg)
+{
+ uint64_t *tsc;
+ int64_t d, min, max;
+ u_int cpu = PCPU_GET(cpuid);
+ u_int first, i, size;
+
+ first = CPU_FIRST();
+ if (cpu == first)
+ return;
+ min = INT64_MIN;
+ max = INT64_MAX;
+ size = (mp_maxid + 1) * 3;
+ for (i = 0, tsc = arg; i < N; i++, tsc += size) {
+ d = tsc[first * 3] - tsc[cpu * 3 + 1];
+ if (d > min)
+ min = d;
+ d = tsc[first * 3 + 1] - tsc[cpu * 3 + 2];
+ if (d > min)
+ min = d;
+ d = tsc[first * 3 + 1] - tsc[cpu * 3];
+ if (d < max)
+ max = d;
+ d = tsc[first * 3 + 2] - tsc[cpu * 3 + 1];
+ if (d < max)
+ max = d;
+ }
+ if (min > max)
+ return;
+ d = min / 2 + max / 2;
+ __asm __volatile (
+ "movl $0x10, %%ecx\n\t"
+ "rdmsr\n\t"
+ "addl %%edi, %%eax\n\t"
+ "adcl %%esi, %%edx\n\t"
+ "wrmsr\n"
+ : /* No output */
+ : "D" ((uint32_t)d), "S" ((uint32_t)(d >> 32))
+ : "ax", "cx", "dx", "cc"
+ );
+}
+
static int
test_tsc(void)
{
uint64_t *data, *tsc;
- u_int i, size;
+ u_int i, size, adj;
if ((!smp_tsc && !tsc_is_invariant) || vm_guest)
return (-100);
size = (mp_maxid + 1) * 3;
data = malloc(sizeof(*data) * size * N, M_TEMP, M_WAITOK);
+ adj = 0;
+retry:
for (i = 0, tsc = data; i < N; i++, tsc += size)
smp_rendezvous(tsc_read_0, tsc_read_1, tsc_read_2, tsc);
smp_tsc = 1; /* XXX */
smp_rendezvous(smp_no_rendevous_barrier, comp_smp_tsc,
smp_no_rendevous_barrier, data);
+ if (!smp_tsc && adj < smp_tsc_adjust) {
+ adj++;
+ smp_rendezvous(smp_no_rendevous_barrier, adj_smp_tsc,
+ smp_no_rendevous_barrier, data);
+ goto retry;
+ }
free(data, M_TEMP);
if (bootverbose)
- printf("SMP: %sed TSC synchronization test\n",
- smp_tsc ? "pass" : "fail");
+ printf("SMP: %sed TSC synchronization test%s\n",
+ smp_tsc ? "pass" : "fail",
+ adj > 0 ? " after adjustment" : "");
if (smp_tsc && tsc_is_invariant) {
switch (cpu_vendor_id) {
case CPU_VENDOR_AMD:
OpenPOWER on IntegriCloud