[RFC PATCH 35/55] KVM: x86: Leave TSC matching to the plane 0 vCPUs

From: David Woodhouse

Date: Sat Oct 17 2026 - 08:44:34 EST


From: Mushahid Hussain <hmushi@xxxxxxxxxxxx>

KVM enables the master clock when every vCPU has a matching TSC, judged
by nr_vcpus_matched_tsc + 1 == online_vcpus. online_vcpus counts a vCPU
once, but each of its plane vCPUs synchronizes its TSC at creation and
counts as another matching vCPU. With one higher plane the count can
never equal the number of vCPUs again, the master clock stays off and
kvmclock loses PVCLOCK_TSC_STABLE_BIT. Among other things the Hyper-V
reference TSC page then breaks: KVM writes sequence 0 to it, finds the
clock unstable and leaves it that way.

The planes of a vCPU share its TSC. Give a plane vCPU the current
generation and leave the matching count and generation changes to its
plane 0 sibling.

Assisted-by: LLM
Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
---
arch/x86/kvm/x86.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0f8a149ba0eb..ba468f7c4d4a 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2858,6 +2858,31 @@ static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc,
if (vcpu->arch.guest_tsc_protected)
return;

+ /*
+ * The planes of a vCPU share its TSC and online_vcpus counts the
+ * vCPU once, so a plane vCPU neither counts as a matching vCPU nor
+ * starts a generation; its plane 0 sibling speaks for it. Nor may
+ * it perturb the global matching window (last_tsc_ and
+ * user_set_tsc): KVM_SET_MSRS is accepted on plane fds, and a TSC
+ * write on a plane fd
+ * moving the window would corrupt the next plane-0 write's match
+ * decision. Latch the generation from the plane-0 sibling, which
+ * exists by construction for a plane twin, rather than from the
+ * VM-global cur_tsc_* that may belong to a different vCPU's
+ * generation entirely.
+ */
+ if (vcpu->plane_level) {
+ struct kvm_vcpu *sibling = vcpu->common->vcpus[0];
+
+ vcpu->arch.last_guest_tsc = tsc;
+ kvm_vcpu_write_tsc_offset(vcpu, offset);
+
+ vcpu->arch.this_tsc_generation = sibling->arch.this_tsc_generation;
+ vcpu->arch.this_tsc_nsec = sibling->arch.this_tsc_nsec;
+ vcpu->arch.this_tsc_write = sibling->arch.this_tsc_write;
+ return;
+ }
+
if (user_set_tsc)
vcpu->kvm->arch.user_set_tsc = true;

--
2.50.1