mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Nikunj A Dadhania <nikunj@amd.com>
To: Sasha Levin <sashal@kernel.org>,
	Sean Christopherson <seanjc@google.com>,
	Paolo Bonzini <pbonzini@redhat.com>
Cc: Vitaly Kuznetsov <vkuznets@redhat.com>,
	Jim Mattson <jmattson@google.com>, <kvm@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH] KVM: x86: Compute the max guest TSC frequency from the current tsc_khz
Date: Tue, 27 Oct 2026 12:10:43 +0530	[thread overview]
Message-ID: <575c1cde-7705-4190-9560-a1d6cfcb4dfa@amd.com> (raw)
In-Reply-To: <20261024174525.1218244-1-sashal@kernel.org>

On 10/24/2026 11:15 PM, Sasha Levin wrote:
> KVM computes the highest frequency that KVM_SET_TSC_KHZ accepts once,
> during vendor init, from the tsc_khz of the time. When KVM is built in,
> that runs before tsc_refine_calibration_work() refines tsc_khz, about a
> second later. If refinement raises tsc_khz, the cap stays below what the
> maximum scaling ratio gives for the refined frequency, and
> KVM_SET_TSC_KHZ rejects frequencies that set_tsc_khz() would accept, as
> set_tsc_khz() computes the ratio from the current tsc_khz.
> 
> nested_tsc_scaling_test runs into this on AMD: its last case asks for
> the frequency KVM_GET_TSC_KHZ reports times SVM's maximum ratio, and gets
> -EINVAL on boots where refinement raised tsc_khz. KernelCI sees it
> intermittently on a DL345 Gen11 (EPYC). On Intel the cap is limited to
> INT_MAX, so the test does not hit it there.
> 
> Like commit 741e511b4208 ("KVM: x86: Don't snapshot "max" TSC if host TSC
> is constant") did for the default frequency of new VMs, compute the cap
> from tsc_khz when it is used instead of taking a snapshot at load time.
> This covers KVM_SET_TSC_KHZ on both the vCPU and the VM file descriptor.
> 
> This also drops setting the cap to tsc_khz in the Hyper-V reenlightenment
> notifier, which left no room for scaling up once the host TSC frequency
> had changed; commit 0092e4346f49 ("x86/kvm: Support Hyper-V
> reenlightenment") gives no reason for that limit. The cap now follows the
> new frequency the same way. The notifier still does not recompute the
> multipliers of vCPUs that already exist.
> 
> Fixes: 92a1f12d2598 ("KVM: X86: Implement userspace interface to set virtual_tsc_khz")
> Link: https://d.kernelci.org/test/maestro:6adc828a55fda507607f79c3
> Signed-off-by: Sasha Levin <sashal@kernel.org>
> ---

Reviewed-by: Nikunj A Dadhania <nikunj@amd.com>
Tested-by: Nikunj A Dadhania <nikunj@amd.com>

Tested on an AMD EPYC 9996 (Venice) with KVM built in (CONFIG_KVM_AMD=y),
booting with tsc_early_khz set to the refined tsc_khz minus 500.

The Hyper-V notifier path is only reachable when KVM runs as a Hyper-V
guest; I haven't tested that path.

> Reproduced on an AMD DL345 Gen11 booted with tsc_early_khz= 500 kHz below
> the refined frequency, so that refinement always raises tsc_khz after KVM
> is initialized. With KernelCI's x86 build of mainline 5b0764a53235,
> nested_tsc_scaling_test failed 3 out of 3 times with KVM_SET_TSC_KHZ
> returning -EINVAL. With the same config plus this patch it passed 3 out
> of 3, and tsc_scaling_sync, tsc_msrs_test, kvm_clock_test and
> nested_tsc_adjust_test still pass. A nested SVM guest with the same
> tsc_early_khz trick gives the same result, and with tsc_early_khz above
> the real frequency (refinement lowers tsc_khz) mainline passes.
> 
> This does not change the existing window between the cap check and
> set_tsc_khz(), which read tsc_khz separately.
> 
>  arch/x86/include/asm/kvm_host.h |  2 --
>  arch/x86/kvm/x86.c              | 30 +++++++++++++++---------------
>  2 files changed, 15 insertions(+), 17 deletions(-)
> 
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index 1ee4b127004df..35a74f0eb45e5 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -260,8 +260,6 @@ struct kvm_x86_pmu_event_filter;
>  struct kvm_caps {
>  	/* control of guest tsc rate supported? */
>  	bool has_tsc_control;
> -	/* maximum supported tsc_khz for guests */
> -	u32  max_guest_tsc_khz;
>  	/* number of bits of the fractional part of the TSC scaling ratio */
>  	u8   tsc_scaling_ratio_frac_bits;
>  	/* maximum allowed value of TSC scaling ratio */
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 32563a91fba99..9e22072f39c18 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -1156,6 +1156,19 @@ static inline u64 __scale_tsc(u64 ratio, u64 tsc)
>  	return mul_u64_u64_shr(tsc, ratio, kvm_caps.tsc_scaling_ratio_frac_bits);
>  }
>  
> +/*
> + * Use the current tsc_khz, not the one at load time, as the TSC frequency can
> + * be refined after KVM is initialized.  Make sure the user can only configure
> + * tsc_khz values that fit into a signed integer, otherwise KVM_GET_TSC_KHZ
> + * would return a negative value and confuse userspace.  A min value is not
> + * calculated because it will always be 1 on all machines.
> + */
> +static u32 kvm_max_guest_tsc_khz(void)
> +{
> +	return min(0x7fffffffULL,
> +		   __scale_tsc(kvm_caps.max_tsc_scaling_ratio, tsc_khz));
> +}
> +
>  u64 kvm_scale_tsc(u64 tsc, u64 ratio)
>  {
>  	u64 _tsc = tsc;
> @@ -3728,7 +3741,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
>  		user_tsc_khz = (u32)arg;
>  
>  		if (kvm_caps.has_tsc_control &&
> -		    user_tsc_khz > kvm_caps.max_guest_tsc_khz)
> +		    user_tsc_khz > kvm_max_guest_tsc_khz())
>  			goto out;
>  
>  		if (user_tsc_khz == 0)
> @@ -4695,7 +4708,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
>  		user_tsc_khz = (u32)arg;
>  
>  		if (kvm_caps.has_tsc_control &&
> -		    user_tsc_khz > kvm_caps.max_guest_tsc_khz)
> +		    user_tsc_khz > kvm_max_guest_tsc_khz())
>  			goto out;
>  
>  		if (user_tsc_khz == 0)
> @@ -6749,7 +6762,6 @@ static void kvm_hyperv_tsc_notifier(void)
>  		for_each_present_cpu(cpu)
>  			per_cpu(cpu_tsc_khz, cpu) = tsc_khz;
>  	}
> -	kvm_caps.max_guest_tsc_khz = tsc_khz;
>  
>  	list_for_each_entry(kvm, &vm_list, vm_list) {
>  		__kvm_start_pvclock_update(kvm);
> @@ -7188,18 +7200,6 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
>  	if (!tdp_enabled)
>  		kvm_caps.supported_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT;
>  
> -	if (kvm_caps.has_tsc_control) {
> -		/*
> -		 * Make sure the user can only configure tsc_khz values that
> -		 * fit into a signed integer, otherwise KVM_GET_TSC_KHZ would
> -		 * return a negative value and confuse userspace.
> -		 * A min value is not calculated because it will always
> -		 * be 1 on all machines.
> -		 */
> -		u64 max = min(0x7fffffffULL,
> -			      __scale_tsc(kvm_caps.max_tsc_scaling_ratio, tsc_khz));
> -		kvm_caps.max_guest_tsc_khz = max;
> -	}
>  	kvm_caps.default_tsc_scaling_ratio = 1ULL << kvm_caps.tsc_scaling_ratio_frac_bits;
>  	kvm_init_msr_lists();
>  	return 0;


      reply	other threads:[~2026-10-27  6:41 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-24 17:45 Sasha Levin
2026-10-27  6:40 ` Nikunj A Dadhania [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=575c1cde-7705-4190-9560-a1d6cfcb4dfa@amd.com \
    --to=nikunj@amd.com \
    --cc=jmattson@google.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=sashal@kernel.org \
    --cc=seanjc@google.com \
    --cc=vkuznets@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®