When running as a KVM guest with kvmclock support enabled, stuff the APIC timer period/frequency with the local APIC bus frequency reported in CPUID.0x40000010.EBX instead of trying to calibrate/guess the frequency.
See Documentation/virt/kvm/x86/cpuid.rst for details. Signed-off-by: Sean Christopherson <[email protected]> --- arch/x86/include/asm/kvm_para.h | 1 + arch/x86/kernel/kvm.c | 19 ++++++++++++++++--- arch/x86/kernel/kvmclock.c | 13 +++++++++++-- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/kvm_para.h b/arch/x86/include/asm/kvm_para.h index 3f7f558b5b24..381d029b72e7 100644 --- a/arch/x86/include/asm/kvm_para.h +++ b/arch/x86/include/asm/kvm_para.h @@ -130,6 +130,7 @@ void kvmclock_init(void); void kvmclock_cpu_action(enum kvm_guest_cpu_action action); bool kvm_para_available(void); unsigned int kvm_para_tsc_khz(void); +unsigned int kvm_para_apic_bus_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); diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index 5cd92a0b156a..bfe36e361b3c 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -918,12 +918,25 @@ bool kvm_para_available(void) } EXPORT_SYMBOL_GPL(kvm_para_available); -unsigned int kvm_para_tsc_khz(void) +static bool kvm_cpuid_has_timing_info(void) { u32 base = kvm_cpuid_base(); - if (cpuid_eax(base) >= (base | KVM_CPUID_TIMING_INFO)) - return cpuid_eax(base | KVM_CPUID_TIMING_INFO); + return cpuid_eax(base) >= (base | KVM_CPUID_TIMING_INFO); +} + +unsigned int kvm_para_tsc_khz(void) +{ + if (kvm_cpuid_has_timing_info()) + return cpuid_eax(kvm_cpuid_base() | KVM_CPUID_TIMING_INFO); + + return 0; +} + +unsigned int kvm_para_apic_bus_khz(void) +{ + if (kvm_cpuid_has_timing_info()) + return cpuid_ebx(kvm_cpuid_base() | KVM_CPUID_TIMING_INFO); return 0; } diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c index 5ceba4f3836c..abcc5b36ea1d 100644 --- a/arch/x86/kernel/kvmclock.c +++ b/arch/x86/kernel/kvmclock.c @@ -200,10 +200,19 @@ void kvmclock_cpu_action(enum kvm_guest_cpu_action action) */ static unsigned long kvm_get_tsc_khz(void) { +#ifdef CONFIG_X86_LOCAL_APIC + u32 apic_khz = kvm_para_apic_bus_khz(); + /* - * If KVM advertises the frequency directly in CPUID, use that - * instead of reverse-calculating it from the KVM clock data. + * Use the TSC frequency from KVM's (and other hypervisors') PV CPUID + * leaf when available, instead of reverse-calculating it from the KVM + * clock data. As a bonus, the CPUID leaf also includes the local APIC + * bus/timer frequency. */ + if (apic_khz) + lapic_timer_period = apic_khz; +#endif + return kvm_para_tsc_khz() ? : pvclock_tsc_khz(this_cpu_pvti()); } -- 2.54.0.563.g4f69b47b94-goog

