From: David Woodhouse <[email protected]>

The compute_guest_tsc() function computes the guest TSC at a given
kernel_ns timestamp. When the master clock reference point
(master_kernel_ns) is earlier than vcpu->arch.this_tsc_nsec, the delta
is negative. Since pvclock_scale_delta() takes a u64, the negative
value wraps to a huge positive number, producing a wildly wrong result.

Handle negative deltas explicitly by negating the delta, scaling it,
and subtracting from this_tsc_write.

Signed-off-by: David Woodhouse <[email protected]>
---
 arch/x86/kvm/x86.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index fc9366b83912..8aae22401046 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2588,11 +2588,21 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 
user_tsc_khz)
 
 static u64 compute_guest_tsc(struct kvm_vcpu *vcpu, s64 kernel_ns)
 {
-       u64 tsc = pvclock_scale_delta(kernel_ns-vcpu->arch.this_tsc_nsec,
-                                     vcpu->arch.virtual_tsc_mult,
-                                     vcpu->arch.virtual_tsc_shift);
-       tsc += vcpu->arch.this_tsc_write;
-       return tsc;
+       s64 delta_ns = kernel_ns - vcpu->arch.this_tsc_nsec;
+       u64 tsc;
+
+       /* Handle negative deltas gracefully (master clock ref may be earlier) 
*/
+       if (delta_ns < 0) {
+               tsc = pvclock_scale_delta(-delta_ns,
+                                         vcpu->arch.virtual_tsc_mult,
+                                         vcpu->arch.virtual_tsc_shift);
+               return vcpu->arch.this_tsc_write - tsc;
+       }
+
+       tsc = pvclock_scale_delta(delta_ns,
+                                 vcpu->arch.virtual_tsc_mult,
+                                 vcpu->arch.virtual_tsc_shift);
+       return vcpu->arch.this_tsc_write + tsc;
 }
 
 #ifdef CONFIG_X86_64
-- 
2.54.0


Reply via email to