Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "David Woodhouse" <dwmw2@infradead.org>, kvm@vger.kernel.org
Subject: Re: [PATCH v11 11/23] KVM: x86: Activate master clock from kvm_arch_init_vm()
Date: Thu, 05 Nov 2026 19:15:48 +0000	[thread overview]
Message-ID: <sashiko-outbox-199830@kernel.org> (raw)
In-Reply-To: <20261105154955.2525419-12-dwmw2@infradead.org>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] KVM: x86: unreachable scaling check in kvm_synchronize_tsc()
- [Medium] KVM: x86: premature TSC sync in kvm_arch_vcpu_create() corrupts freq match
- [Medium] KVM: x86: unserviced masterclock request in kvm_arch_vcpu_postcreate()
--

commit e32eb6058695cc6d79a22be2b503c0c26e852ace
Author: David Woodhouse <dwmw@amazon.co.uk>

KVM: x86: Activate master clock from kvm_arch_init_vm()

This commit initializes the initial TSC generation and master clock state in
kvm_arch_init_vm() from an atomic ktime/TSC snapshot, and updates
kvm_synchronize_tsc() and kvm_arch_vcpu_postcreate() to synchronize vCPUs and
refresh the master clock against that initial generation.

> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 9401685287215..6dbdb60962fe7 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -1500,11 +1500,25 @@ void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *value, bool user_initiated)
>  
>  		data = kvm->arch.cur_tsc_write + delta;
>  
> -		if (!kvm_check_tsc_unstable())
> -			offset = kvm->arch.cur_tsc_offset;
> -		else
> +		/*
> +		 * The generation's offset can only be joined by a vCPU which
> +		 * scales the TSC by the same ratio it was recorded with; the
> +		 * initial generation's offset is recorded at VM creation
> +		 * with the default ratio, before any vCPU exists. With a
> +		 * different ratio, open a new generation (recording this
> +		 * vCPU's own offset) rather than leave the generation's
> +		 * ratio and offset describing different scaling domains.
> +		 */
> +		if (vcpu->arch.l1_tsc_scaling_ratio !=
> +		    kvm->arch.cur_tsc_scaling_ratio) {
>  			offset = kvm_compute_l1_tsc_offset(vcpu, host_tsc, data);
> -		matched = true;
> +		} else {
> +			if (!kvm_check_tsc_unstable())
> +				offset = kvm->arch.cur_tsc_offset;
> +			else
> +				offset = kvm_compute_l1_tsc_offset(vcpu, host_tsc, data);
> +			matched = true;
> +		}
>  	}
>  
>  	__kvm_synchronize_tsc(vcpu, offset, data, ns, matched, user_initiated);

[Severity: Medium]
Can this scaling ratio check in kvm_synchronize_tsc() ever be reached?

The enclosing block in kvm_synchronize_tsc() is gated on matching
virtual_tsc_khz:

	if (synchronizing &&
	    vcpu->arch.virtual_tsc_khz == kvm->arch.cur_tsc_khz) {
		u64 delta = nsec_to_cycles(vcpu, elapsed);

		data = kvm->arch.cur_tsc_write + delta;
		...
		if (vcpu->arch.l1_tsc_scaling_ratio !=
		    kvm->arch.cur_tsc_scaling_ratio) {

In set_tsc_khz(), vcpu->arch.l1_tsc_scaling_ratio is derived deterministically
from vcpu->arch.virtual_tsc_khz, and __kvm_synchronize_tsc() updates
kvm->arch.cur_tsc_khz and kvm->arch.cur_tsc_scaling_ratio together.

Meanwhile, kvm_arch_init_vm() initializes:

	kvm->arch.cur_tsc_scaling_ratio = kvm_caps.default_tsc_scaling_ratio;
	kvm->arch.cur_tsc_khz = kvm->arch.default_tsc_khz;

When userspace configures a non-default guest TSC frequency via the VM-scoped
KVM_SET_TSC_KHZ ioctl in kvm_arch_vm_ioctl():

	mutex_lock(&kvm->lock);
	if (!kvm->created_vcpus) {
		WRITE_ONCE(kvm->arch.default_tsc_khz, user_tsc_khz);
		r = 0;
	}
	mutex_unlock(&kvm->lock);

only kvm->arch.default_tsc_khz is updated, leaving kvm->arch.cur_tsc_khz at
the initial host frequency. Or, if userspace instead invokes the vCPU-scoped
KVM_SET_TSC_KHZ ioctl after creating the first vCPU, virtual_tsc_khz also
differs from kvm->arch.cur_tsc_khz.

In both cases, vcpu->arch.virtual_tsc_khz == kvm->arch.cur_tsc_khz is false in
kvm_synchronize_tsc(), so the outer if block is skipped entirely. That leaves
data at 0 instead of extrapolating data = kvm->arch.cur_tsc_write + delta from
the VM-creation epoch (while kvmclock_offset remains anchored at VM creation
time), and on VMs where vcpu->arch.guest_tsc_protected causes
__kvm_synchronize_tsc() to return early, kvm->arch.cur_tsc_scaling_ratio and
kvm->arch.master_tsc_scaling_ratio remain stuck at
kvm_caps.default_tsc_scaling_ratio.

> @@ -9645,8 +9659,15 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
>  		return;
>  	vcpu_load(vcpu);
>  	kvm_synchronize_tsc(vcpu, NULL, false);
> -	if (kvm_check_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu))
> +
> +	/*
> +	 * Refresh the VM-creation masterclock snapshot (see
> +	 * kvm_arch_init_vm), synchronously so that it is valid even for
> +	 * userspace which queries the clock before ever running a vCPU.
> +	 */
> +	if (vcpu->vcpu_idx == 0)
>  		kvm_update_masterclock(vcpu->kvm, NULL);
> +
>  	vcpu_put(vcpu);
>  
>  	/* poll control enabled by default */

[Severity: Medium]
What happens if creating a secondary vCPU (vcpu->vcpu_idx > 0) queues
KVM_REQ_MASTERCLOCK_UPDATE on that vCPU?

During vCPU creation, kvm_synchronize_tsc() -> __kvm_synchronize_tsc() ->
kvm_track_tsc_matching() checks whether a masterclock update is needed:

	if ((ka->use_master_clock && update_mclock) ||
	    (ka->use_master_clock != use_master_clock))
		kvm_make_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu);

When vcpu->vcpu_idx > 0 and its synchronization opens a new TSC generation,
changes ka->all_vcpus_matched_tsc, or changes masterclock eligibility,
KVM_REQ_MASTERCLOCK_UPDATE is queued only on the newly created vCPU.

With kvm_arch_vcpu_postcreate() only calling kvm_update_masterclock() when
vcpu->vcpu_idx == 0, won't that request remain unserviced until the secondary
vCPU enters vcpu_enter_guest(), leaving ka->use_master_clock and the
masterclock state stale for host-side KVM_GET_CLOCK / KVM_GET_CLOCK_GUEST /
KVM_SET_CLOCK_GUEST calls and delaying KVM_REQ_CLOCK_UPDATE for already
running sibling vCPUs?

> @@ -10106,16 +10133,41 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
>  	 * returns early — is still eligible for PVCLOCK_TSC_STABLE_BIT.
>  	 */
>  	kvm->arch.all_vcpus_matched_tsc = true;
> +	kvm->arch.cur_tsc_scaling_ratio = kvm_caps.default_tsc_scaling_ratio;
> +	kvm->arch.cur_tsc_khz = kvm->arch.default_tsc_khz;
>  
>  	raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags);
> +
> +	/*
> +	 * Establish the initial TSC generation for synchronization logic.
> +	 * The first vCPU will sync to this, starting its guest TSC at the
> +	 * number of cycles elapsed since VM creation rather than at zero.
> +	 * This ensures coherence with the kvmclock epoch (also set here),
> +	 * and the master clock snapshot taken below is immediately valid.
> +	 * It is refreshed again when the first vCPU is created, in case
> +	 * the host clocksource changed (and changed back) in between:
> +	 * pvclock_gtod_update_fn() does not notify a VM with no vCPUs.
> +	 */
> +#ifdef CONFIG_X86_64
> +	if (!kvm_get_time_and_clockread(&kvmclock_ns, &kvmclock_host_tsc))
> +#endif
> +	{
> +		/*
> +		 * If not simultaneous, we want the TSC to be zero slightly
> +		 * *after* the kvmclock is zero.
> +		 */
> +		kvmclock_ns = get_kvmclock_base_ns();
> +		kvmclock_host_tsc = rdtsc();
> +	}
> +	kvm->arch.cur_tsc_write = 0;
> +	kvm->arch.cur_tsc_offset = -(s64)kvmclock_host_tsc;
> +	kvm->arch.cur_tsc_nsec = kvmclock_ns;
> +	kvm->arch.cur_tsc_generation = 1;
> +	kvm->arch.kvmclock_offset = -kvmclock_ns;

[Severity: Medium]
Now that cur_tsc_khz and cur_tsc_generation are initialized from
kvm_arch_init_vm() with all_vcpus_matched_freq = true and
all_vcpus_matched_tsc = true, does the initial kvm_set_tsc_khz() call in
kvm_arch_vcpu_create() corrupt frequency and TSC matching when userspace uses
per-vCPU KVM_SET_TSC_KHZ?

In kvm_vm_ioctl_create_vcpu(), kvm_arch_vcpu_create() runs before
atomic_inc(&kvm->online_vcpus):

	kvm_arch_vcpu_create(vcpu)
		kvm_set_tsc_khz(vcpu, vcpu->kvm->arch.default_tsc_khz)
			set_tsc_khz(vcpu, user_tsc_khz, false)
				kvm_synchronize_tsc(vcpu, NULL, false)

Consider creating vCPU 0 and setting its frequency to F != default_tsc_khz via
the vCPU-scoped KVM_SET_TSC_KHZ ioctl (so cur_tsc_khz = F,
cur_tsc_freq_generation = 1, and nr_vcpus_matched_freq = 1), and then creating
vCPU 1:

1. In kvm_arch_vcpu_create(vcpu 1), kvm_set_tsc_khz() syncs vCPU 1 at
   default_tsc_khz while online_vcpus is still 1. Because default_tsc_khz != F,
   __kvm_synchronize_tsc() increments cur_tsc_freq_generation to 2, clears
   all_vcpus_matched_freq, sets nr_vcpus_matched_freq = 1, and overwrites
   cur_tsc_khz = default_tsc_khz.

2. Immediately afterwards, kvm_track_tsc_matching() reads online = 1, sees
   nr_vcpus_matched_freq (1) >= online (1), and sets
   ka->all_vcpus_matched_freq = true.

3. After online_vcpus is incremented to 2, kvm_arch_vcpu_postcreate(vcpu 1)
   calls kvm_synchronize_tsc(vcpu 1, NULL, false). Because vCPU 1's
   virtual_tsc_khz already matches cur_tsc_khz (default_tsc_khz),
   __kvm_synchronize_tsc() does not clear all_vcpus_matched_freq.

If vCPU 1 is left at default_tsc_khz, all_vcpus_matched_freq remains true even
though vCPU 0 is at F and vCPU 1 is at default_tsc_khz. Conversely, if
userspace then sets vCPU 1's frequency to F via KVM_SET_TSC_KHZ, the
intermediate sync at default_tsc_khz has already bumped
cur_tsc_freq_generation and cur_tsc_generation, so setting vCPU 1 to F bumps
both generations again with nr_vcpus_matched_freq = 1 and
nr_vcpus_matched_tsc = 1 (for 2 online vCPUs), leaving all_vcpus_matched_freq
and all_vcpus_matched_tsc permanently false even though both vCPUs are
configured to F.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261105154955.2525419-1-dwmw2@infradead.org?part=11

  reply	other threads:[~2026-11-05 19:15 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-05 15:49 [PATCH v11 00/23] KVM: x86: Cleaning up the KVM clock mess, part 2 David Woodhouse
2026-11-05 15:49 ` [PATCH v11 01/23] KVM: x86: Improve synchronization in kvm_synchronize_tsc() David Woodhouse
2026-11-05 15:49 ` [PATCH v11 02/23] KVM: x86: Kill last_tsc_{nsec,write,offset} fields David Woodhouse
2026-11-05 16:47   ` sashiko-bot
2026-11-05 15:49 ` [PATCH v11 03/23] KVM: x86: Replace nr_vcpus_matched_tsc count with all_vcpus_matched_tsc bool David Woodhouse
2026-11-05 15:49 ` [PATCH v11 04/23] KVM: x86: Allow KVM master clock mode when TSCs are offset from each other David Woodhouse
2026-11-05 17:27   ` sashiko-bot
2026-11-05 15:49 ` [PATCH v11 05/23] KVM: x86: Factor out kvm_use_master_clock() David Woodhouse
2026-11-05 15:49 ` [PATCH v11 06/23] KVM: x86: Avoid gratuitous global clock updates David Woodhouse
2026-11-05 15:49 ` [PATCH v11 07/23] KVM: x86: Avoid redundant masterclock updates from multiple vCPUs David Woodhouse
2026-11-05 18:00   ` sashiko-bot
2026-11-05 15:49 ` [PATCH v11 08/23] KVM: x86: Re-synchronize TSC after KVM_SET_TSC_KHZ David Woodhouse
2026-11-05 18:22   ` sashiko-bot
2026-11-05 15:49 ` [PATCH v11 09/23] KVM: x86: Cache host vclock_mode for masterclock eligibility checks David Woodhouse
2026-11-05 15:49 ` [PATCH v11 10/23] KVM: x86: Remove pvclock_gtod_data and private timekeeping code David Woodhouse
2026-11-05 15:49 ` [PATCH v11 11/23] KVM: x86: Activate master clock from kvm_arch_init_vm() David Woodhouse
2026-11-05 19:15   ` sashiko-bot [this message]
2026-11-05 15:49 ` [PATCH v11 12/23] UAPI: x86: Move pvclock-abi to UAPI for x86 platforms David Woodhouse
2026-11-05 15:49 ` [PATCH v11 13/23] KVM: selftests: Use UAPI pvclock-abi.h in xen_shinfo_test David Woodhouse
2026-11-05 15:49 ` [PATCH v11 14/23] KVM: x86: Add KVM_[GS]ET_CLOCK_GUEST for accurate KVM clock migration David Woodhouse
2026-11-05 19:51   ` sashiko-bot
2026-11-05 15:49 ` [PATCH v11 15/23] KVM: selftests: Add library helpers for clock and TSC-offset ioctls David Woodhouse
2026-11-05 15:49 ` [PATCH v11 16/23] KVM: x86: Add KVM_VCPU_TSC_SCALE and fix the documentation on TSC migration David Woodhouse
2026-11-05 15:49 ` [PATCH v11 17/23] KVM: selftests: Add KVM/PV clock selftest to prove timer correction David Woodhouse
2026-11-05 15:49 ` [PATCH v11 18/23] KVM: selftests: Add master clock offset test David Woodhouse
2026-11-05 15:49 ` [PATCH v11 19/23] KVM: selftests: Add Xen/generic CPUID timing leaf test David Woodhouse
2026-11-05 15:49 ` [PATCH v11 20/23] KVM: selftests: Add Xen runstate migration test David Woodhouse
2026-11-05 15:49 ` [PATCH v11 21/23] KVM: selftests: Test that the first user TSC write is honoured exactly David Woodhouse
2026-11-05 15:49 ` [PATCH v11 22/23] KVM: x86/xen: Drop open-coded fallback clock read in kvm_xen_start_timer() David Woodhouse
2026-11-05 15:49 ` [PATCH v11 23/23] KVM: x86: Use a single time snapshot in kvm_vm_ioctl_set_clock() David Woodhouse

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=sashiko-outbox-199830@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dwmw2@infradead.org \
    --cc=kvm@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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