From 5a81b19fbd3150677df0f28d6fe44cfe071533d0 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Wed, 19 Jun 2013 16:37:55 +0800 Subject: [PATCH] clock/tsc: Detect invariant TSC According to Intel's description: "The invariant TSC will run at a constant rate in all ACPI P-, C-. " and T-states. ..." The difference between invariant TSC and constant TSC is that invariant TSC is not affected by frequency changes and deep ACPI C-state. Constant TSC could be detected based on the CPU model (Intel has the model list, while there is no information from AMD's document); constant TSC is not detected yet. --- sys/platform/pc32/include/clock.h | 1 + sys/platform/pc32/isa/clock.c | 27 +++++++++++++++++----- sys/platform/pc64/include/clock.h | 1 + sys/platform/pc64/isa/clock.c | 27 +++++++++++++++++----- sys/platform/vkernel/include/clock.h | 1 + sys/platform/vkernel/platform/init.c | 3 +++ sys/platform/vkernel/platform/systimer.c | 2 ++ sys/platform/vkernel64/include/clock.h | 1 + sys/platform/vkernel64/platform/init.c | 3 +++ sys/platform/vkernel64/platform/systimer.c | 2 ++ 10 files changed, 56 insertions(+), 12 deletions(-) diff --git a/sys/platform/pc32/include/clock.h b/sys/platform/pc32/include/clock.h index e3d45869e3..7144a38cba 100644 --- a/sys/platform/pc32/include/clock.h +++ b/sys/platform/pc32/include/clock.h @@ -34,6 +34,7 @@ extern int disable_rtc_set; extern u_int timer_freq; extern int timer0_max_count; extern int tsc_present; +extern int tsc_invariant; extern int64_t tsc_frequency; extern int tsc_is_broken; extern int wall_cmos_clock; diff --git a/sys/platform/pc32/isa/clock.c b/sys/platform/pc32/isa/clock.c index 066731cc29..1aab043f98 100644 --- a/sys/platform/pc32/isa/clock.c +++ b/sys/platform/pc32/isa/clock.c @@ -105,6 +105,7 @@ static uint16_t i8254_walltimer_cntr; int adjkerntz; /* local offset from GMT in seconds */ int disable_rtc_set; /* disable resettodr() if != 0 */ int tsc_present; +int tsc_invariant; int64_t tsc_frequency; int tsc_is_broken; int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */ @@ -528,7 +529,7 @@ calibrate_clocks(void) int sec, start_sec, timeout; if (bootverbose) - kprintf("Calibrating clock(s) ... "); + kprintf("Calibrating clock(s) ...\n"); if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) goto fail; timeout = 100000000; @@ -595,8 +596,11 @@ calibrate_clocks(void) tsc_frequency = rdtsc() - old_tsc; } - if (tsc_present) - kprintf("TSC clock: %llu Hz, ", tsc_frequency); + if (tsc_present) { + kprintf("TSC%s clock: %llu Hz, ", + tsc_invariant ? " invariant" : "", + (long long)tsc_frequency); + } kprintf("i8254 clock: %u Hz\n", tot_count); return (tot_count); @@ -729,10 +733,20 @@ startrtclock(void) /* * Can we use the TSC? */ - if (cpu_feature & CPUID_TSC) + if (cpu_feature & CPUID_TSC) { tsc_present = 1; - else + if ((cpu_vendor_id == CPU_VENDOR_INTEL || + cpu_vendor_id == CPU_VENDOR_AMD) && + cpu_exthigh >= 0x80000007) { + u_int regs[4]; + + do_cpuid(0x80000007, regs); + if (regs[3] & 0x100) + tsc_invariant = 1; + } + } else { tsc_present = 0; + } /* * Initial RTC state, don't do anything unexpected @@ -1160,6 +1174,7 @@ SYSCTL_PROC(_hw_i8254, OID_AUTO, timestamp, CTLTYPE_STRING|CTLFLAG_RD, SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD, &tsc_present, 0, "TSC Available"); +SYSCTL_INT(_hw, OID_AUTO, tsc_invariant, CTLFLAG_RD, + &tsc_invariant, 0, "Invariant TSC"); SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD, &tsc_frequency, 0, "TSC Frequency"); - diff --git a/sys/platform/pc64/include/clock.h b/sys/platform/pc64/include/clock.h index e3d45869e3..7144a38cba 100644 --- a/sys/platform/pc64/include/clock.h +++ b/sys/platform/pc64/include/clock.h @@ -34,6 +34,7 @@ extern int disable_rtc_set; extern u_int timer_freq; extern int timer0_max_count; extern int tsc_present; +extern int tsc_invariant; extern int64_t tsc_frequency; extern int tsc_is_broken; extern int wall_cmos_clock; diff --git a/sys/platform/pc64/isa/clock.c b/sys/platform/pc64/isa/clock.c index 9d7b750176..933962defd 100644 --- a/sys/platform/pc64/isa/clock.c +++ b/sys/platform/pc64/isa/clock.c @@ -107,6 +107,7 @@ static uint16_t i8254_walltimer_cntr; int adjkerntz; /* local offset from GMT in seconds */ int disable_rtc_set; /* disable resettodr() if != 0 */ int tsc_present; +int tsc_invariant; int64_t tsc_frequency; int tsc_is_broken; int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */ @@ -535,7 +536,7 @@ calibrate_clocks(void) int sec, start_sec, timeout; if (bootverbose) - kprintf("Calibrating clock(s) ... "); + kprintf("Calibrating clock(s) ...\n"); if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) goto fail; timeout = 100000000; @@ -602,8 +603,11 @@ calibrate_clocks(void) tsc_frequency = rdtsc() - old_tsc; } - if (tsc_present) - kprintf("TSC clock: %llu Hz, ", (long long)tsc_frequency); + if (tsc_present) { + kprintf("TSC%s clock: %llu Hz, ", + tsc_invariant ? " invariant" : "", + (long long)tsc_frequency); + } kprintf("i8254 clock: %u Hz\n", tot_count); return (tot_count); @@ -736,10 +740,20 @@ startrtclock(void) /* * Can we use the TSC? */ - if (cpu_feature & CPUID_TSC) + if (cpu_feature & CPUID_TSC) { tsc_present = 1; - else + if ((cpu_vendor_id == CPU_VENDOR_INTEL || + cpu_vendor_id == CPU_VENDOR_AMD) && + cpu_exthigh >= 0x80000007) { + u_int regs[4]; + + do_cpuid(0x80000007, regs); + if (regs[3] & 0x100) + tsc_invariant = 1; + } + } else { tsc_present = 0; + } /* * Initial RTC state, don't do anything unexpected @@ -1167,6 +1181,7 @@ SYSCTL_PROC(_hw_i8254, OID_AUTO, timestamp, CTLTYPE_STRING|CTLFLAG_RD, SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD, &tsc_present, 0, "TSC Available"); +SYSCTL_INT(_hw, OID_AUTO, tsc_invariant, CTLFLAG_RD, + &tsc_invariant, 0, "Invariant TSC"); SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD, &tsc_frequency, 0, "TSC Frequency"); - diff --git a/sys/platform/vkernel/include/clock.h b/sys/platform/vkernel/include/clock.h index 4f461ac309..56f85c9ea8 100644 --- a/sys/platform/vkernel/include/clock.h +++ b/sys/platform/vkernel/include/clock.h @@ -24,6 +24,7 @@ extern int disable_rtc_set; extern u_int timer_freq; extern int timer0_max_count; extern int tsc_present; +extern int tsc_invariant; extern int64_t tsc_frequency; extern int tsc_is_broken; extern int wall_cmos_clock; diff --git a/sys/platform/vkernel/platform/init.c b/sys/platform/vkernel/platform/init.c index dffee02b05..4a59a3d36f 100644 --- a/sys/platform/vkernel/platform/init.c +++ b/sys/platform/vkernel/platform/init.c @@ -106,6 +106,7 @@ vpte_t *KernelPTD; vpte_t *KernelPTA; /* Warning: Offset for direct VA translation */ u_int cpu_feature; /* XXX */ int tsc_present; +int tsc_invariant; int64_t tsc_frequency; int optcpus; /* number of cpus - see mp_start() */ int lwp_cpu_lock; /* if/how to lock virtual CPUs to real CPUs */ @@ -368,6 +369,8 @@ main(int ac, char **av) */ vsize = sizeof(tsc_present); sysctlbyname("hw.tsc_present", &tsc_present, &vsize, NULL, 0); + vsize = sizeof(tsc_invariant); + sysctlbyname("hw.tsc_invariant", &tsc_invariant, &vsize, NULL, 0); vsize = sizeof(tsc_frequency); sysctlbyname("hw.tsc_frequency", &tsc_frequency, &vsize, NULL, 0); if (tsc_present) diff --git a/sys/platform/vkernel/platform/systimer.c b/sys/platform/vkernel/platform/systimer.c index f40fb7c580..3e81fb0120 100644 --- a/sys/platform/vkernel/platform/systimer.c +++ b/sys/platform/vkernel/platform/systimer.c @@ -60,6 +60,8 @@ SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD, &tsc_present, 0, "TSC Available"); +SYSCTL_INT(_hw, OID_AUTO, tsc_invariant, CTLFLAG_RD, + &tsc_invariant, 0, "Invariant TSC"); SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD, &tsc_frequency, 0, "TSC Frequency"); diff --git a/sys/platform/vkernel64/include/clock.h b/sys/platform/vkernel64/include/clock.h index 4f461ac309..56f85c9ea8 100644 --- a/sys/platform/vkernel64/include/clock.h +++ b/sys/platform/vkernel64/include/clock.h @@ -24,6 +24,7 @@ extern int disable_rtc_set; extern u_int timer_freq; extern int timer0_max_count; extern int tsc_present; +extern int tsc_invariant; extern int64_t tsc_frequency; extern int tsc_is_broken; extern int wall_cmos_clock; diff --git a/sys/platform/vkernel64/platform/init.c b/sys/platform/vkernel64/platform/init.c index c4085c1d94..95ecab978e 100644 --- a/sys/platform/vkernel64/platform/init.c +++ b/sys/platform/vkernel64/platform/init.c @@ -108,6 +108,7 @@ vpte_t *KernelPTA; /* Warning: Offset for direct VA translation */ void *dmap_min_address; u_int cpu_feature; /* XXX */ int tsc_present; +int tsc_invariant; int64_t tsc_frequency; int optcpus; /* number of cpus - see mp_start() */ int lwp_cpu_lock; /* if/how to lock virtual CPUs to real CPUs */ @@ -367,6 +368,8 @@ main(int ac, char **av) */ vsize = sizeof(tsc_present); sysctlbyname("hw.tsc_present", &tsc_present, &vsize, NULL, 0); + vsize = sizeof(tsc_invariant); + sysctlbyname("hw.tsc_invariant", &tsc_invariant, &vsize, NULL, 0); vsize = sizeof(tsc_frequency); sysctlbyname("hw.tsc_frequency", &tsc_frequency, &vsize, NULL, 0); if (tsc_present) diff --git a/sys/platform/vkernel64/platform/systimer.c b/sys/platform/vkernel64/platform/systimer.c index e641c62978..49b57b5465 100644 --- a/sys/platform/vkernel64/platform/systimer.c +++ b/sys/platform/vkernel64/platform/systimer.c @@ -61,6 +61,8 @@ SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD, &tsc_present, 0, "TSC Available"); +SYSCTL_INT(_hw, OID_AUTO, tsc_invariant, CTLFLAG_RD, + &tsc_invariant, 0, "Invariant TSC"); SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD, &tsc_frequency, 0, "TSC Frequency"); -- 2.41.0