Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/x86/tsc.c
Show First 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#include <machine/specialreg.h> | #include <machine/specialreg.h> | ||||
#include <x86/vmware.h> | #include <x86/vmware.h> | ||||
#include <dev/acpica/acpi_hpet.h> | #include <dev/acpica/acpi_hpet.h> | ||||
#include <contrib/dev/acpica/include/acpi.h> | #include <contrib/dev/acpica/include/acpi.h> | ||||
#include "cpufreq_if.h" | #include "cpufreq_if.h" | ||||
uint64_t tsc_freq; | uint64_t tsc_freq; | ||||
jhb: For FreeBSD we probably want to leave the default setting as-is by setting this to zero. I… | |||||
int tsc_is_invariant; | int tsc_is_invariant; | ||||
int tsc_perf_stat; | int tsc_perf_stat; | ||||
static eventhandler_tag tsc_levels_tag, tsc_pre_tag, tsc_post_tag; | static eventhandler_tag tsc_levels_tag, tsc_pre_tag, tsc_post_tag; | ||||
SYSCTL_INT(_kern_timecounter, OID_AUTO, invariant_tsc, CTLFLAG_RDTUN, | SYSCTL_INT(_kern_timecounter, OID_AUTO, invariant_tsc, CTLFLAG_RDTUN, | ||||
&tsc_is_invariant, 0, "Indicates whether the TSC is P-state invariant"); | &tsc_is_invariant, 0, "Indicates whether the TSC is P-state invariant"); | ||||
Show All 15 Lines | |||||
SYSCTL_INT(_machdep, OID_AUTO, disable_tsc, CTLFLAG_RDTUN, &tsc_disabled, 0, | SYSCTL_INT(_machdep, OID_AUTO, disable_tsc, CTLFLAG_RDTUN, &tsc_disabled, 0, | ||||
"Disable x86 Time Stamp Counter"); | "Disable x86 Time Stamp Counter"); | ||||
static int tsc_skip_calibration; | static int tsc_skip_calibration; | ||||
SYSCTL_INT(_machdep, OID_AUTO, disable_tsc_calibration, CTLFLAG_RDTUN | | SYSCTL_INT(_machdep, OID_AUTO, disable_tsc_calibration, CTLFLAG_RDTUN | | ||||
CTLFLAG_NOFETCH, &tsc_skip_calibration, 0, | CTLFLAG_NOFETCH, &tsc_skip_calibration, 0, | ||||
"Disable TSC frequency calibration"); | "Disable TSC frequency calibration"); | ||||
static int tsc_vm_enable; | |||||
Done Inline ActionsI would move tsc_vm_enable declaration here and just leave off the ' = 0' (see the tsc_skip_calibration node above for an example of this style). jhb: I would move tsc_vm_enable declaration here and just leave off the ' = 0' (see the… | |||||
SYSCTL_INT(_machdep, OID_AUTO, tsc_vm_enable, CTLFLAG_RDTUN, &tsc_vm_enable, 0, | |||||
"Allow to use tsc in guest-mode"); | |||||
static void tsc_freq_changed(void *arg, const struct cf_level *level, | static void tsc_freq_changed(void *arg, const struct cf_level *level, | ||||
int status); | int status); | ||||
static void tsc_freq_changing(void *arg, const struct cf_level *level, | static void tsc_freq_changing(void *arg, const struct cf_level *level, | ||||
int *status); | int *status); | ||||
static unsigned tsc_get_timecount(struct timecounter *tc); | static unsigned tsc_get_timecount(struct timecounter *tc); | ||||
static inline unsigned tsc_get_timecount_low(struct timecounter *tc); | static inline unsigned tsc_get_timecount_low(struct timecounter *tc); | ||||
static unsigned tsc_get_timecount_lfence(struct timecounter *tc); | static unsigned tsc_get_timecount_lfence(struct timecounter *tc); | ||||
static unsigned tsc_get_timecount_low_lfence(struct timecounter *tc); | static unsigned tsc_get_timecount_low_lfence(struct timecounter *tc); | ||||
Show All 13 Lines | static struct timecounter tsc_timecounter = { | ||||
.tc_name = "TSC", | .tc_name = "TSC", | ||||
.tc_quality = 800, /* adjusted in code */ | .tc_quality = 800, /* adjusted in code */ | ||||
.tc_fill_vdso_timehands = x86_tsc_vdso_timehands, | .tc_fill_vdso_timehands = x86_tsc_vdso_timehands, | ||||
#ifdef COMPAT_FREEBSD32 | #ifdef COMPAT_FREEBSD32 | ||||
.tc_fill_vdso_timehands32 = x86_tsc_vdso_timehands32, | .tc_fill_vdso_timehands32 = x86_tsc_vdso_timehands32, | ||||
#endif | #endif | ||||
}; | }; | ||||
static inline bool | |||||
tsc_vm_guest_allowed(void) | |||||
{ | |||||
Done Inline Actionsstyle(9) would want a single tab indent, a blank line before the indent, and parentheses around the return value. jhb: style(9) would want a single tab indent, a blank line before the indent, and parentheses around… | |||||
return (vm_guest == VM_GUEST_NO || tsc_vm_enable); | |||||
} | |||||
static void | static void | ||||
tsc_freq_vmware(void) | tsc_freq_vmware(void) | ||||
{ | { | ||||
u_int regs[4]; | u_int regs[4]; | ||||
if (hv_high >= 0x40000010) { | if (hv_high >= 0x40000010) { | ||||
do_cpuid(0x40000010, regs); | do_cpuid(0x40000010, regs); | ||||
tsc_freq = regs[0] * 1000; | tsc_freq = regs[0] * 1000; | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | probe_tsc_freq(void) | ||||
if (vm_guest == VM_GUEST_VMWARE) { | if (vm_guest == VM_GUEST_VMWARE) { | ||||
tsc_freq_vmware(); | tsc_freq_vmware(); | ||||
return; | return; | ||||
} | } | ||||
switch (cpu_vendor_id) { | switch (cpu_vendor_id) { | ||||
case CPU_VENDOR_AMD: | case CPU_VENDOR_AMD: | ||||
if ((amd_pminfo & AMDPM_TSC_INVARIANT) != 0 || | if ((amd_pminfo & AMDPM_TSC_INVARIANT) != 0 || | ||||
(vm_guest == VM_GUEST_NO && | (tsc_vm_guest_allowed() && | ||||
CPUID_TO_FAMILY(cpu_id) >= 0x10)) | CPUID_TO_FAMILY(cpu_id) >= 0x10)) | ||||
tsc_is_invariant = 1; | tsc_is_invariant = 1; | ||||
if (cpu_feature & CPUID_SSE2) { | if (cpu_feature & CPUID_SSE2) { | ||||
tsc_timecounter.tc_get_timecount = | tsc_timecounter.tc_get_timecount = | ||||
tsc_get_timecount_mfence; | tsc_get_timecount_mfence; | ||||
} | } | ||||
break; | break; | ||||
case CPU_VENDOR_INTEL: | case CPU_VENDOR_INTEL: | ||||
if ((amd_pminfo & AMDPM_TSC_INVARIANT) != 0 || | if ((amd_pminfo & AMDPM_TSC_INVARIANT) != 0 || | ||||
(vm_guest == VM_GUEST_NO && | (tsc_vm_guest_allowed() && | ||||
((CPUID_TO_FAMILY(cpu_id) == 0x6 && | ((CPUID_TO_FAMILY(cpu_id) == 0x6 && | ||||
CPUID_TO_MODEL(cpu_id) >= 0xe) || | CPUID_TO_MODEL(cpu_id) >= 0xe) || | ||||
(CPUID_TO_FAMILY(cpu_id) == 0xf && | (CPUID_TO_FAMILY(cpu_id) == 0xf && | ||||
CPUID_TO_MODEL(cpu_id) >= 0x3)))) | CPUID_TO_MODEL(cpu_id) >= 0x3)))) | ||||
tsc_is_invariant = 1; | tsc_is_invariant = 1; | ||||
if (cpu_feature & CPUID_SSE2) { | if (cpu_feature & CPUID_SSE2) { | ||||
tsc_timecounter.tc_get_timecount = | tsc_timecounter.tc_get_timecount = | ||||
tsc_get_timecount_lfence; | tsc_get_timecount_lfence; | ||||
} | } | ||||
break; | break; | ||||
case CPU_VENDOR_CENTAUR: | case CPU_VENDOR_CENTAUR: | ||||
if (vm_guest == VM_GUEST_NO && | if (tsc_vm_guest_allowed() && | ||||
CPUID_TO_FAMILY(cpu_id) == 0x6 && | CPUID_TO_FAMILY(cpu_id) == 0x6 && | ||||
CPUID_TO_MODEL(cpu_id) >= 0xf && | CPUID_TO_MODEL(cpu_id) >= 0xf && | ||||
(rdmsr(0x1203) & 0x100000000ULL) == 0) | (rdmsr(0x1203) & 0x100000000ULL) == 0) | ||||
tsc_is_invariant = 1; | tsc_is_invariant = 1; | ||||
if (cpu_feature & CPUID_SSE2) { | if (cpu_feature & CPUID_SSE2) { | ||||
tsc_timecounter.tc_get_timecount = | tsc_timecounter.tc_get_timecount = | ||||
tsc_get_timecount_lfence; | tsc_get_timecount_lfence; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 198 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static int | static int | ||||
test_tsc(int adj_max_count) | test_tsc(int adj_max_count) | ||||
{ | { | ||||
uint64_t *data, *tsc; | uint64_t *data, *tsc; | ||||
u_int i, size, adj; | u_int i, size, adj; | ||||
if ((!smp_tsc && !tsc_is_invariant) || vm_guest) | if ((!smp_tsc && !tsc_is_invariant) || !tsc_vm_guest_allowed()) | ||||
return (-100); | return (-100); | ||||
size = (mp_maxid + 1) * 3; | size = (mp_maxid + 1) * 3; | ||||
data = malloc(sizeof(*data) * size * N, M_TEMP, M_WAITOK); | data = malloc(sizeof(*data) * size * N, M_TEMP, M_WAITOK); | ||||
adj = 0; | adj = 0; | ||||
retry: | retry: | ||||
for (i = 0, tsc = data; i < N; i++, tsc += size) | for (i = 0, tsc = data; i < N; i++, tsc += size) | ||||
smp_rendezvous(tsc_read_0, tsc_read_1, tsc_read_2, tsc); | smp_rendezvous(tsc_read_0, tsc_read_1, tsc_read_2, tsc); | ||||
smp_tsc = 1; /* XXX */ | smp_tsc = 1; /* XXX */ | ||||
▲ Show 20 Lines • Show All 330 Lines • Show Last 20 Lines |
For FreeBSD we probably want to leave the default setting as-is by setting this to zero. I would perhaps rename the knob/variable as well to be something like 'tsc_vm_enable' and 'hw.tsc.vm_enable'. Finally, I would maybe make this a CTLFLAG_RDTUN sysctl and call it 'machdep.tsc_vm_enable'. Then it is documented via sysctl -d and you can inspect the current value on existing systems, etc. The reason for machdep is that the other existing TSC sysctl is 'machdep.tsc_freq'.