All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "David Woodhouse" <dwmw2@infradead.org>, kvm@vger.kernel.org
Subject: Re: [PATCH v11 08/23] KVM: x86: Re-synchronize TSC after KVM_SET_TSC_KHZ
Date: Thu, 05 Nov 2026 18:22:09 +0000	[thread overview]
Message-ID: <sashiko-outbox-199742@kernel.org> (raw)
In-Reply-To: <20261105154955.2525419-9-dwmw2@infradead.org>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] KVM: x86: premature kvm_synchronize_tsc() in kvm_arch_vcpu_create()
- [High] KVM: x86: VM-wide user_set_tsc check in kvm_set_tsc_khz() breaks sync
- [Medium] KVM: x86: stale masterclock scaling state after kvm_set_tsc_khz()
- [Medium] KVM: x86: unmatched TSC generations in kvm_set_tsc_khz() after run
--

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

KVM: x86: Re-synchronize TSC after KVM_SET_TSC_KHZ

This commit updates kvm_set_tsc_khz() to invoke kvm_synchronize_tsc() after
changing a vCPU's TSC frequency so that cur_tsc_scaling_ratio and TSC
generation tracking are updated. It also adds an explicit user_initiated
parameter to kvm_synchronize_tsc() to distinguish host userspace MSR_IA32_TSC
writes from internal frequency resynchronizations.

> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 77bd3e4bfdcd7..f551056d72746 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -1064,6 +1064,7 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)
>  {
>  	u32 thresh_lo, thresh_hi;
>  	int use_scaling = 0;
> +	u64 tsc;
>  
>  	/* tsc_khz can be zero if TSC calibration fails */
>  	if (user_tsc_khz == 0) {
> @@ -1091,7 +1092,34 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)
>  			 user_tsc_khz, thresh_lo, thresh_hi);
>  		use_scaling = 1;
>  	}
> -	return set_tsc_khz(vcpu, user_tsc_khz, use_scaling);
> +	/*
> +	 * Read the guest TSC before changing the ratio, so we can
> +	 * re-synchronize to preserve continuity across the change.
> +	 */
> +	tsc = kvm_read_l1_tsc(vcpu, rdtsc());
> +
> +	if (set_tsc_khz(vcpu, user_tsc_khz, use_scaling))
> +		return -1;
> +
> +	/*
> +	 * Preserving continuity only makes sense for a vCPU which has
> +	 * actually run; for one which has not, synchronize as for a zero
> +	 * write, joining the current generation. This makes vCPU creation
> +	 * (which comes through here with the default frequency) join the
> +	 * generation *explicitly*, rather than by the accident that the
> +	 * scaling ratio is still zero at this point so the TSC read above
> +	 * is zero anyway — and it stops an explicit KVM_SET_TSC_KHZ on a
> +	 * never-run vCPU, whose nonzero TSC value could never match, from
> +	 * opening a new generation and orphaning the matched state of its
> +	 * siblings. If userspace has already written a TSC, preserve the
> +	 * value even for a never-run vCPU — a zero write would discard it —
> +	 * and let the user-write matching window do its usual job.
> +	 */
> +	if (vcpu->arch.last_vmentry_cpu != -1 || vcpu->kvm->arch.user_set_tsc)
> +		kvm_synchronize_tsc(vcpu, &tsc, false);

[Severity: High]
Does checking the VM-wide vcpu->kvm->arch.user_set_tsc flag here break TSC
synchronization when userspace configures or restores multiple never-run
vCPUs sequentially?

In __kvm_synchronize_tsc(), user_set_tsc is a VM-wide flag on struct kvm_arch:

	if (user_set_tsc)
		vcpu->kvm->arch.user_set_tsc = true;

If userspace initializes or restores vCPUs in a per-vCPU loop by calling
KVM_SET_TSC_KHZ followed by KVM_SET_MSRS(MSR_IA32_TSC) on each vCPU in turn:

1. Userspace creates vCPU 0 and vCPU 1 (both have last_vmentry_cpu == -1 and
   creation-time TSC offsets ticking from ~0).
2. On vCPU 0, userspace calls KVM_SET_TSC_KHZ and then writes MSR_IA32_TSC
   with a restored value T_restore (where T_restore > 1 second of guest
   uptime). That sets vcpu->kvm->arch.user_set_tsc = true and sets
   cur_tsc_write = T_restore.
3. On vCPU 1, before MSR_IA32_TSC is written on vCPU 1, userspace calls
   KVM_SET_TSC_KHZ. Because the VM-wide user_set_tsc flag is already true,
   kvm_set_tsc_khz() takes the first branch and passes vCPU 1's un-restored,
   near-zero creation TSC (&tsc) instead of NULL.
4. In kvm_synchronize_tsc(), vCPU 1's near-zero tsc is more than 1 second
   away from cur_tsc_write (T_restore), so it fails to match and opens a new
   TSC generation with cur_tsc_write overwritten to that near-zero value.
5. When userspace then writes MSR_IA32_TSC = T_restore on vCPU 1, T_restore
   is more than 1 second away from the clobbered near-zero cur_tsc_write, so
   it fails to match again and leaves all_vcpus_matched_tsc false.

Should this check whether a TSC value was written by userspace on this
specific vCPU rather than checking the VM-wide vcpu->kvm->arch.user_set_tsc
(which is also read here without holding kvm->arch.tsc_write_lock)?

[Severity: Medium]
What happens in kvm_set_tsc_khz() when KVM_SET_TSC_KHZ is called on a
multi-vCPU guest whose vCPUs have already run (last_vmentry_cpu != -1), if
userspace has never explicitly written MSR_IA32_TSC?

Because kvm_set_tsc_khz() passes &tsc (non-zero) with user_initiated = false,
kvm->arch.user_set_tsc remains false. When kvm_synchronize_tsc() runs for each
vCPU:

	if (vcpu->arch.virtual_tsc_khz) {
		if (data == 0) {
			synchronizing = true;
		} else if (kvm->arch.user_set_tsc) {
			...
		}
	}

Since data != 0 and kvm->arch.user_set_tsc is false, synchronizing is false
for every vCPU. Doesn't each vCPU open a separate TSC generation with
nr_vcpus_matched_tsc = 1, orphaning its siblings and leaving
all_vcpus_matched_tsc permanently false?

> +	else
> +		kvm_synchronize_tsc(vcpu, NULL, false);
> +	return 0;

[Severity: High]
Can calling kvm_synchronize_tsc() unconditionally from kvm_set_tsc_khz()
corrupt VM-wide TSC matching state during KVM_CREATE_VCPU?

During vCPU creation, kvm_vm_ioctl_create_vcpu() calls:

  kvm_vm_ioctl_create_vcpu()
    -> kvm_arch_vcpu_create()
      -> kvm_set_tsc_khz(vcpu, vcpu->kvm->arch.default_tsc_khz)
        -> kvm_synchronize_tsc(vcpu, NULL, false)
          -> __kvm_synchronize_tsc()
            -> kvm_track_tsc_matching()

At the point kvm_arch_vcpu_create() runs, atomic_inc(&kvm->online_vcpus) has
not happened yet:

virt/kvm/kvm_main.c:kvm_vm_ioctl_create_vcpu() {
	...
	r = kvm_arch_vcpu_create(vcpu);
	if (r)
		goto vcpu_free_run_page;
	...
	smp_wmb();
	atomic_inc(&kvm->online_vcpus);
	mutex_unlock(&vcpu->mutex);

	kvm_arch_vcpu_postcreate(vcpu);
}

Running __kvm_synchronize_tsc() and kvm_track_tsc_matching() before
online_vcpus is incremented appears to cause several regressions:

1. In kvm_track_tsc_matching():

	int online = atomic_read(&vcpu->kvm->online_vcpus);
	WRITE_ONCE(ka->all_vcpus_matched_tsc,
		   ka->nr_vcpus_matched_tsc >= online);

	update_mclock |= (prev_matched_tsc != ka->all_vcpus_matched_tsc);

	if (ka->all_vcpus_matched_tsc ||
	    ka->nr_vcpus_matched_freq >= online)
		ka->all_vcpus_matched_freq = true;

   If vCPU 0 is already online (online == 1) and configured to a non-default
   frequency F != default_tsc_khz, creating vCPU 1 runs
   __kvm_synchronize_tsc() at default_tsc_khz while online is still 1. That
   resets ka->all_vcpus_matched_freq = false, increments
   ka->nr_vcpus_matched_freq to 1, and sets ka->cur_tsc_khz = default_tsc_khz.
   Then kvm_track_tsc_matching() sees nr_vcpus_matched_freq (1) >= online (1)
   and latches ka->all_vcpus_matched_freq = true. When
   kvm_arch_vcpu_postcreate() later runs with online == 2,
   vcpu->arch.virtual_tsc_khz already equals ka->cur_tsc_khz, so
   all_vcpus_matched_freq is never cleared and kvm_update_masterclock()
   enables masterclock mode across frequency-mismatched vCPUs.

2. If userspace creates and configures vCPUs one by one (create vCPU 0, call
   KVM_SET_TSC_KHZ(vcpu 0, F), then create vCPU 1, call
   KVM_SET_TSC_KHZ(vcpu 1, F)), creating vCPU 1 synchronizes it at
   default_tsc_khz, clobbering cur_tsc_khz, cur_tsc_offset,
   cur_tsc_freq_generation, and cur_tsc_generation so that the subsequent
   KVM_SET_TSC_KHZ(vcpu 1, F) only counts vCPU 1 and leaves
   all_vcpus_matched_freq and all_vcpus_matched_tsc false.

3. If kvm_dirty_ring_alloc(), xa_insert(), or create_vcpu_fd() fails in
   kvm_vm_ioctl_create_vcpu() after kvm_arch_vcpu_create(), the destroyed
   vCPU's increments and generation changes in kvm->arch remain permanently
   recorded.

Should kvm_set_tsc_khz() skip kvm_synchronize_tsc() during
kvm_arch_vcpu_create() and leave initial synchronization to
kvm_arch_vcpu_postcreate() after online_vcpus has been incremented?

[Severity: Medium]
Does the KVM_SET_TSC_KHZ ioctl path also need to process
KVM_REQ_MASTERCLOCK_UPDATE before returning to userspace?

Calling kvm_synchronize_tsc() from kvm_set_tsc_khz() updates
ka->cur_tsc_scaling_ratio and sets KVM_REQ_MASTERCLOCK_UPDATE on the vCPU via
kvm_track_tsc_matching(), but kvm_arch_vcpu_ioctl() returns to userspace
without calling kvm_update_masterclock().

In __get_kvmclock_master_clock(), get_kvmclock() uses
ka->master_tsc_scaling_ratio, ka->master_tsc_shift, and ka->master_tsc_mul,
which are only updated from ka->cur_tsc_scaling_ratio when
pvclock_update_vm_gtod_copy() runs via kvm_update_masterclock():

	hv_clock.tsc_shift = ka->master_tsc_shift;
	hv_clock.tsc_to_system_mul = ka->master_tsc_mul;

	if (kvm_caps.has_tsc_control) {
		u64 tsc_delta = data->host_tsc - ka->master_cycle_now;

		tsc_delta = kvm_scale_tsc(tsc_delta, ka->master_tsc_scaling_ratio);

If userspace calls KVM_SET_TSC_KHZ and then queries KVM_GET_CLOCK before
entering KVM_RUN, won't get_kvmclock() still use the stale masterclock
scaling parameters (or remain in masterclock mode despite a frequency
mismatch)?

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

  reply	other threads:[~2026-11-05 18:22 UTC|newest]

Thread overview: 33+ 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 [this message]
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
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 20:06   ` sashiko-bot
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 20:25   ` sashiko-bot
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 20:50   ` sashiko-bot
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-199742@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.