x86: Handle when MPERF/APERF MSRs aren't writable

For performance and/or correct reasons some hypervisors allow
MPERF/APERF MSRs to be read but not written to. This change
modifies the handling of these MSRs to not rely on writes.
This patch is part of Google Cloud Engine (GCE) C4-LSSD turnup.

Sponsored by:		Google
Tested by:		NetApp (previous)
PR:			292808
MFC after:		3 days
Co-authored-by:		Jim Mattson <jmattson@google.com>
Reviewed by:		jrtc27, imp, kib, markj, olce, obiwac
Differential Revision:	https://reviews.freebsd.org/D55996
This commit is contained in:
Matt Delco
2026-03-26 13:30:31 -04:00
committed by Alexander Ziaee
parent c505fc1468
commit 7e7d4e711f
2 changed files with 13 additions and 10 deletions
+7 -7
View File
@@ -423,7 +423,7 @@ int
cpu_est_clockrate(int cpu_id, uint64_t *rate)
{
uint64_t tsc1, tsc2;
uint64_t acnt, mcnt, perf;
uint64_t acnt_start, acnt_end, mcnt_start, mcnt_end, perf;
register_t reg;
int error = 0;
@@ -453,20 +453,20 @@ cpu_est_clockrate(int cpu_id, uint64_t *rate)
/* Calibrate by measuring a short delay. */
reg = intr_disable();
if (tsc_is_invariant) {
wrmsr(MSR_MPERF, 0);
wrmsr(MSR_APERF, 0);
mcnt_start = rdmsr(MSR_MPERF);
acnt_start = rdmsr(MSR_APERF);
tsc1 = rdtsc();
DELAY(1000);
mcnt = rdmsr(MSR_MPERF);
acnt = rdmsr(MSR_APERF);
mcnt_end = rdmsr(MSR_MPERF);
acnt_end = rdmsr(MSR_APERF);
tsc2 = rdtsc();
intr_restore(reg);
if (mcnt == 0) {
if (mcnt_end == mcnt_start) {
tsc_perf_stat = 0;
error = EOPNOTSUPP;
goto err;
}
perf = 1000 * acnt / mcnt;
perf = 1000 * (acnt_end - acnt_start) / (mcnt_end - mcnt_start);
*rate = (tsc2 - tsc1) * perf;
} else {
tsc1 = rdtsc();
+6 -3
View File
@@ -433,6 +433,8 @@ probe_tsc_freq_late(void)
void
start_TSC(void)
{
uint64_t mperf, aperf;
if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled)
return;
@@ -442,11 +444,12 @@ start_TSC(void)
/*
* XXX Some emulators expose host CPUID without actual support
* for these MSRs. We must test whether they really work.
* They may also be read-only, so test for increment.
*/
wrmsr(MSR_MPERF, 0);
wrmsr(MSR_APERF, 0);
mperf = rdmsr(MSR_MPERF);
aperf = rdmsr(MSR_APERF);
DELAY(10);
if (rdmsr(MSR_MPERF) > 0 && rdmsr(MSR_APERF) > 0)
if (rdmsr(MSR_MPERF) != mperf && rdmsr(MSR_APERF) != aperf)
tsc_perf_stat = 1;
}