]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
x86/kvm: Obtain TSC frequency from CPUID if present cpuid
authorDavid Woodhouse <dwmw@amazon.co.uk>
Wed, 13 Aug 2025 09:16:59 +0000 (10:16 +0100)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Mon, 18 Aug 2025 09:49:03 +0000 (10:49 +0100)
In https://lkml.org/lkml/2008/10/1/246 a proposal was made for generic
CPUID conventions across hypervisors. It was mostly shot down in flames,
but the leaf at 0x40000010 containing timing information didn't die.

It's used by XNU and FreeBSD guests under all hypervisors¹² to determine
the TSC frequency, and also exposed by the EC2 Nitro hypervisor (as
well as, presumably, VMware). FreeBSD's Bhyve is probably just about
to start exposing it too.

Use it under KVM to obtain the TSC frequency more accurately, instead
of reverse-calculating the frequency from the mul/shift values in the
KVM clock.

Before:
[    0.000020] tsc: Detected 2900.014 MHz processor

After:
[    0.000020] tsc: Detected 2900.015 MHz processor

$ cpuid -1 -l 0x40000010
CPU:
   hypervisor generic timing information (0x40000010):
      TSC frequency (Hz) = 2900015
      bus frequency (Hz) = 1000000

¹ https://github.com/apple/darwin-xnu/blob/main/osfmk/i386/cpuid.c
² https://github.com/freebsd/freebsd-src/commit/4a432614f68

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
arch/x86/include/asm/kvm_para.h
arch/x86/kernel/kvm.c
arch/x86/kernel/kvmclock.c

index 57bc74e112f20936d6ee2601443892ecc083b533..d53927103cab64e514815845940d372d5f009f39 100644 (file)
@@ -121,6 +121,7 @@ static inline long kvm_sev_hypercall3(unsigned int nr, unsigned long p1,
 void kvmclock_init(void);
 void kvmclock_disable(void);
 bool kvm_para_available(void);
+unsigned int kvm_para_tsc_khz(void);
 unsigned int kvm_arch_para_features(void);
 unsigned int kvm_arch_para_hints(void);
 void kvm_async_pf_task_wait_schedule(u32 token);
index 8ae750cde0c6576aac08e89aad60dd9b3fd3a210..44040e37c9a71adfbc4c9a57910e5918c5142f01 100644 (file)
@@ -896,6 +896,16 @@ bool kvm_para_available(void)
 }
 EXPORT_SYMBOL_GPL(kvm_para_available);
 
+unsigned int kvm_para_tsc_khz(void)
+{
+       u32 base = kvm_cpuid_base();
+
+       if (cpuid_eax(base) >= (base | KVM_CPUID_TIMING_INFO))
+               return cpuid_eax(base | KVM_CPUID_TIMING_INFO);
+
+       return 0;
+}
+
 unsigned int kvm_arch_para_features(void)
 {
        return cpuid_eax(kvm_cpuid_base() | KVM_CPUID_FEATURES);
index ca0a49eeac4a2980d5c56b588174a6e30e463b4e..0908450ebac9d2b8176b682b1947342490316771 100644 (file)
@@ -117,7 +117,12 @@ static inline void kvm_sched_clock_init(bool stable)
 static unsigned long kvm_get_tsc_khz(void)
 {
        setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ);
-       return pvclock_tsc_khz(this_cpu_pvti());
+
+       /*
+        * If KVM advertises the frequency directly in CPUID, use that
+        * instead of reverse-calculating it from the KVM clock data.
+        */
+       return kvm_para_tsc_khz() ? : pvclock_tsc_khz(this_cpu_pvti());
 }
 
 static void __init kvm_get_preset_lpj(void)