From: Mushahid Hussain <[email protected]>

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 its plane-0
sibling's TSC offset and generation, and leave the matching count and
generation changes to the sibling.  The offset the caller computed is
ignored: it comes from the VM-global matching window, so it differs
from the sibling's whenever the sibling is not in the current
generation, or on a host with an unstable TSC, and a host TSC write on
a plane fd would otherwise move the twin's TSC away from plane 0's.

Fixes the TSC match count for the plane vCPUs that "KVM: Implement
KVM_CREATE_VCPU ioctl for planes" creates.

Fixes: https://lore.kernel.org/all/[email protected]/
Not-signed-off-by: Mushahid Hussain <[email protected]>
Signed-off-by: David Woodhouse <[email protected]>
---
 arch/x86/kvm/x86.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index cd0e9b6cf8f7..f9adb4e81ebc 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -1366,6 +1366,35 @@ 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.
+        *
+        * Take the offset and the generation from the plane-0 sibling,
+        * which exists by construction for a plane twin, and ignore the
+        * caller's offset: that one comes from the VM-global matching
+        * window, which may belong to a different vCPU's generation, and
+        * is recomputed from elapsed time on an unstable host.  A host
+        * TSC write on a plane fd therefore leaves the TSC alone; plane 0
+        * owns it.
+        */
+       if (vcpu->plane_level) {
+               struct kvm_vcpu *sibling = vcpu->common->vcpus[0];
+
+               kvm_vcpu_write_tsc_offset(vcpu, sibling->arch.l1_tsc_offset);
+               vcpu->arch.last_guest_tsc = kvm_read_l1_tsc(vcpu, rdtsc());
+
+               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


Reply via email to