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 16/23] KVM: x86: Add KVM_VCPU_TSC_SCALE and fix the documentation on TSC migration
Date: Thu, 05 Nov 2026 20:06:37 +0000	[thread overview]
Message-ID: <sashiko-outbox-200074@kernel.org> (raw)
In-Reply-To: <20261105154955.2525419-17-dwmw2@infradead.org>

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Medium] KVM: selftests: missing TEST_REQUIRE() checks in pvclock_migration_test
- [Low] KVM: x86: doc per-vCPU KVM_SET_TSC_KHZ order breaks KVM_SET_CLOCK_GUEST
- [Low] KVM: x86: vcpu.rst drops source KVM_GET_TSC_KHZ step still used on dest
- [Low] KVM: selftests: VM memory leak in main() from using kvm_vm_release()
- [Low] KVM: selftests: duplicated pvclock and TSC scaling helpers in test
--

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

KVM: x86: Add KVM_VCPU_TSC_SCALE and fix the documentation on TSC migration

This commit adds the KVM_VCPU_TSC_SCALE device attribute to expose a vCPU's
TSC scaling ratio and fraction bits to userspace when hardware TSC scaling is
supported. It also updates the vCPU TSC migration documentation and adds a
selftest to verify kvmclock precision across simulated live migration.

> diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst
> index deb5c51bc00c8..3daa77c5d2150 100644
> --- a/Documentation/virt/kvm/devices/vcpu.rst
> +++ b/Documentation/virt/kvm/devices/vcpu.rst
> @@ -239,6 +239,7 @@ including the layout of the stolen time structure.
>  :Architectures: x86
>  
>  4.1 ATTRIBUTE: KVM_VCPU_TSC_OFFSET
> +----------------------------------

[ ... ]

> @@ -261,44 +265,110 @@ following describes a possible algorithm to use for this purpose.
>  
>  From the source VMM process:
>  
> -1. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (tsc_src),
> +1. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (host_tsc_src),
>     kvmclock nanoseconds (guest_src), and host CLOCK_REALTIME nanoseconds
> -   (host_src).
> +   (time_src) at a given moment (Tsrc). The host_tsc field is only valid
> +   when the returned flags include KVM_CLOCK_HOST_TSC.
> +
> +2. For each vCPU[i]:
> +
> +   a. Read the KVM_VCPU_TSC_OFFSET attribute to record the guest TSC offset
> +      (ofs_src[i]).
> +
> +   b. Read the KVM_VCPU_TSC_SCALE attribute to record the guest TSC scaling
> +      ratio (ratio_src[i], frac_bits_src[i]). If the attribute is not
> +      supported (-ENXIO), there is no hardware TSC scaling and identity
> +      scaling applies: use ratio 1, frac_bits 0.
>  
> -2. Read the KVM_VCPU_TSC_OFFSET attribute for every vCPU to record the
> -   guest TSC offset (ofs_src[i]).
> +   c. Use host_tsc_src and the scaling/offset factors to calculate this
> +      vCPU's TSC at time Tsrc:
>  
> -3. Invoke the KVM_GET_TSC_KHZ ioctl to record the frequency of the
> -   guest's TSC (freq).
> +      tsc_src[i] = ((host_tsc_src * ratio_src[i]) >> frac_bits_src[i]) + ofs_src[i]
> +
> +      Note that frac_bits may be as high as 48, so this multiplication
> +      needs a 128-bit intermediate to avoid overflow.
> +
> +3. Invoke the KVM_GET_CLOCK_GUEST ioctl on the boot vCPU to return the KVM
> +   clock as a function of the guest TSC (pvti_src). (This ioctl may not
> +   succeed if the host and guest TSCs are not consistent and well-behaved.)

[Severity: Low]
Previously, step 3 on the source side instructed the VMM to invoke
KVM_GET_TSC_KHZ to record the guest TSC frequency (freq), which was replaced
here by KVM_GET_CLOCK_GUEST.

However, destination step 4 and step 7b below still reference freq and
freq[i]:

    tsc_dst[i] = tsc_src[i] + (ΔT * freq[i] / 10^6)

Should step 2 on the source side also include invoking KVM_GET_TSC_KHZ to
record freq[i] (or freq) for each vCPU?

>  From the destination VMM process:
>  
> -4. Invoke the KVM_SET_CLOCK ioctl, providing the source nanoseconds from
> -   kvmclock (guest_src) and CLOCK_REALTIME (host_src) in their respective
> -   fields.  Ensure that the KVM_CLOCK_REALTIME flag is set in the provided
> -   structure.
> +4. Invoke the KVM_SET_TSC_KHZ ioctl to set the scaled frequency of the
> +   guest's TSC (freq) — either on the VM before creating the vCPUs, or
> +   on each vCPU after creating it (as the exemplar test does).
> +
> +5. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (host_tsc_dst) and
> +   host CLOCK_REALTIME nanoseconds (time_dst) at a given moment (Tdst),
> +   again checking KVM_CLOCK_HOST_TSC in the returned flags.
> +
> +6. Calculate the number of nanoseconds elapsed between Tsrc and Tdst:
> +
> +   ΔT = time_dst - time_src
> +
> +7. As each vCPU[i] is created:
> +
> +   a. Read the KVM_VCPU_TSC_SCALE attribute to record the guest TSC scaling
> +      ratio (ratio_dst[i], frac_bits_dst[i]).
> +
> +   b. Calculate the intended guest TSC value at time Tdst, taking care
> +      with the units (ΔT is in nanoseconds; freq, as set by
> +      KVM_SET_TSC_KHZ, is in kHz):
> +
> +      tsc_dst[i] = tsc_src[i] + (ΔT * freq[i] / 10^6)
>  
> -   KVM will advance the VM's kvmclock to account for elapsed time since
> -   recording the clock values.  Note that this will cause problems in
> -   the guest (e.g., timeouts) unless CLOCK_REALTIME is synchronized
> -   between the source and destination, and a reasonably short time passes
> -   between the source pausing the VMs and the destination executing
> -   steps 4-7.
> +   c. Use host_tsc_dst and the scaling factors to calculate this vCPU's
> +      raw scaled TSC at time Tdst without offsetting:
> +
> +      raw_dst[i] = ((host_tsc_dst * ratio_dst[i]) >> frac_bits_dst[i])
> +
> +   d. Calculate ofs_dst[i] = tsc_dst[i] - raw_dst[i] and set the resulting
> +      offset using the KVM_VCPU_TSC_OFFSET attribute.
> +
> +8. If pvti_src was provided, invoke the KVM_SET_CLOCK_GUEST ioctl on the boot
> +   vCPU to restore the KVM clock as a precise function of the guest TSC.

[Severity: Low]
Can following steps 4, 7, and 8 as written cause KVM_SET_CLOCK_GUEST in step 8
to fail with -ENODATA when a VMM uses per-vCPU KVM_SET_TSC_KHZ?

Step 4 allows calling KVM_SET_TSC_KHZ on each vCPU after creating it, while
step 7 is described as happening "As each vCPU[i] is created". If a VMM
reads KVM_VCPU_TSC_SCALE in step 7a as each vCPU is created before calling
per-vCPU KVM_SET_TSC_KHZ, KVM_VCPU_TSC_SCALE will return the unscaled default
ratio (1 << tsc_frac_bits) rather than the scaled ratio for freq.

Furthermore, if a VMM creates and configures one vCPU at a time (creating
vCPU[0], calling per-vCPU KVM_SET_TSC_KHZ(vCPU[0], freq), and running step 7,
then creating vCPU[1]), creating vCPU[1] invokes:

  kvm_arch_vcpu_create()
    -> kvm_set_tsc_khz(vcpu, kvm->arch.default_tsc_khz)
      -> __kvm_synchronize_tsc()

Because per-vCPU KVM_SET_TSC_KHZ does not update kvm->arch.default_tsc_khz,
vCPU[1] is initialized with the unscaled default_tsc_khz. In
arch/x86/kvm/x86.c:__kvm_synchronize_tsc():

	if (vcpu->arch.virtual_tsc_khz != kvm->arch.cur_tsc_khz) {
		kvm->arch.cur_tsc_freq_generation++;
		kvm->arch.all_vcpus_matched_freq = false;
		kvm->arch.nr_vcpus_matched_freq = 0;
	}

This bumps cur_tsc_freq_generation to default_tsc_khz, and the subsequent
KVM_SET_TSC_KHZ(vCPU[1], freq) bumps cur_tsc_freq_generation again back to
freq, leaving vCPU[0] in an older generation with all_vcpus_matched_freq set
to false. When step 8 then invokes KVM_SET_CLOCK_GUEST,
pvclock_update_vm_gtod_copy() disables use_master_clock and
KVM_SET_CLOCK_GUEST fails with -ENODATA.

Should the documentation clarify that if per-vCPU KVM_SET_TSC_KHZ is used
instead of VM-scoped KVM_SET_TSC_KHZ, all vCPUs must be created and have
KVM_SET_TSC_KHZ applied before reading KVM_VCPU_TSC_SCALE and setting
KVM_VCPU_TSC_OFFSET in step 7?

[ ... ]

> diff --git a/tools/testing/selftests/kvm/x86/pvclock_migration_test.c b/tools/testing/selftests/kvm/x86/pvclock_migration_test.c
> new file mode 100644
> index 0000000000000..c4a64b6a2e429
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/x86/pvclock_migration_test.c

[ ... ]

> +static u64 pvclock_read_cycles(struct pvclock_vcpu_time_info *src,
> +				    u64 tsc)
> +{
> +	u64 delta = tsc - src->tsc_timestamp;
> +	u64 ns;
> +
> +	if (src->tsc_shift >= 0)
> +		delta <<= src->tsc_shift;
> +	else
> +		delta >>= -(s32)src->tsc_shift;
> +
> +	ns = (unsigned __int128)delta * src->tsc_to_system_mul >> 32;
> +	return src->system_time + ns;
> +}

[Severity: Low]
This isn't a bug, but pvclock_read_cycles() duplicates __pvclock_read_cycles()
from tools/arch/x86/include/asm/pvclock.h (already used by
tools/testing/selftests/kvm/x86/kvm_clock_test.c). In addition, main() later
open-codes the 128-bit TSC scaling calculation for raw_dst:

	if (scale_dst.tsc_frac_bits)
		raw_dst = (unsigned __int128)host_tsc_dst * scale_dst.tsc_ratio
			  >> scale_dst.tsc_frac_bits;
	else
		raw_dst = host_tsc_dst;

Could this test reuse __pvclock_read_cycles() and call
host_to_guest_tsc(&scale_dst, 0, host_tsc_dst) for raw_dst?

[ ... ]

> +int main(void)
> +{
> +	struct pvclock_vcpu_time_info pvti_src[NR_VCPUS];
> +	struct pvclock_vcpu_time_info pvti_dst_ioctl[NR_VCPUS];
> +	struct kvm_clock_data clock_src, clock_dst;
> +	struct kvm_vcpu_tsc_scale scale_src[NR_VCPUS], scale_dst;
> +	struct kvm_vcpu *vcpus[NR_VCPUS];
> +	struct kvm_vm *vm;
> +	u64 mono_before, kvm_before, kvm_after;
> +	s64 delta_before;
> +	u64 ofs_src[NR_VCPUS], tsc_src[NR_VCPUS], tsc_dst, raw_dst;
> +	u64 ofs_dst[NR_VCPUS];
> +	u64 host_tsc_src, host_tsc_dst;
> +	u64 time_src, time_dst;
> +	s64 delta_t;
> +	u32 freq_khz = 1500000; /* 1.5 GHz — forces TSC scaling */
> +	int i, pass;
> +
> +	TEST_REQUIRE(sys_clocksource_is_based_on_tsc());
> +	TEST_REQUIRE(kvm_has_cap(KVM_CAP_TSC_CONTROL));

[Severity: Medium]
What happens if this selftest is run on a kernel that supports
KVM_CAP_TSC_CONTROL, but does not support KVM_CAP_CLOCK_GUEST (or
KVM_VCPU_TSC_SCALE)?

In main(), the subsequent calls to vcpu_get_clock_guest(),
vcpu_set_clock_guest(), and vcpu_device_attr_get(..., KVM_VCPU_TSC_CTRL,
KVM_VCPU_TSC_SCALE, ...) unconditionally assert ioctl success, so the test
will fail with an assertion abort instead of skipping.

Should main() also check TEST_REQUIRE(kvm_has_cap(KVM_CAP_CLOCK_GUEST)) (like
pvclock_test.c, masterclock_offset_test.c, and xen_migration_test.c) and
verify that the vCPU has KVM_VCPU_TSC_SCALE via __vcpu_has_device_attr()?

[ ... ]

> +	/* Step 3: KVM_GET_CLOCK_GUEST (already fetched above as pvti_src[]) */
> +
> +	kvm_vm_release(vm);
> +
> +	/* === PAUSE (simulate migration) === */
> +	pr_info("=== Pausing 1 second ===\n");
> +	sleep(1);
> +
> +	/* === DESTINATION SIDE === */
> +	pr_info("=== Destination VM ===\n");
> +	/* Step 4: create + KVM_SET_TSC_KHZ (in create_vm) */
> +	vm = create_vm(vcpus, freq_khz);

[Severity: Low]
Does calling kvm_vm_release(vm) instead of kvm_vm_free(vm) here (and at the
end of main()) leak the VM's userspace_mem_regions mappings, sparsebit arrays,
and struct kvm_vm when vm is overwritten by create_vm()?

In tools/testing/selftests/kvm/lib/kvm_util.c, kvm_vm_release() only frees the
vCPUs and closes the KVM file descriptors, whereas kvm_vm_free() unmaps the
memory regions, frees vpages_valid and vpages_mapped, calls kvm_vm_release(),
and frees struct kvm_vm.

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

  reply	other threads:[~2026-11-05 20:06 UTC|newest]

Thread overview: 32+ 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
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 [this message]
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 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-200074@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