instrumentation: Avoid CPUID 0x15/0x16 for Hypervisor TSC frequency

This restricts the retrieval of the TSC frequency whilst under a Hypervisor to
either Hypervisor-specific CPUID registers (0x40000010), or TSC
calibration. We previously allowed retrieving from the traditional CPUID
registers for TSC frequency (0x15/0x16) like on bare metal, but it turns out
that they are not trustworthy when virtualized and can report wildly incorrect
frequencies, like 7 kHz when the actual calibrated frequencty is 2.5 GHz.

Per report from buildfarm member drongo.

Author: Lukas Fittl <lukas@fittl.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/jr4hk2sxhqcfpb67ftz5g4vw33nm67cgf7go3wwmqsafu5aclq%405m67ukuhyszz
This commit is contained in:
Andres Freund 2026-04-09 11:50:24 -04:00
parent 60165db6e1
commit 7fc36c5db5

View file

@ -165,19 +165,16 @@ x86_tsc_frequency_khz(void)
{
unsigned int reg[4] = {0};
/*
* If we're inside a virtual machine, try to fetch the TSC frequency from
* the hypervisor, using a hypervisor specific method.
*
* Note it is not safe to utilize the regular 0x15/0x16 CPUID registers
* (i.e. the logic below) in virtual machines, as they have been observed
* to be wildly incorrect when virtualized.
*/
if (x86_feature_available(PG_HYPERVISOR))
{
uint32 freq = x86_hypervisor_tsc_frequency_khz();
/*
* If the hypervisor specific logic didn't figure out the frequency,
* it's possible (although not likely, as often that's hidden from
* guests) that the non-virtualized logic can figure out the
* frequency.
*/
if (freq > 0)
return freq;
}
return x86_hypervisor_tsc_frequency_khz();
/*
* On modern Intel CPUs, the TSC is implemented by invariant timekeeping