[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v11 01/23] KVM: x86: Improve synchronization in kvm_synchronize_tsc()



From: David Woodhouse <dwmw@xxxxxxxxxxxx>

When synchronizing to an existing TSC (either by explicitly writing
zero, or the legacy hack where the TSC is written within one second's
worth of the previously written TSC), the last_tsc_write and
last_tsc_nsec values were being misrecorded by __kvm_synchronize_tsc().
The *unsynchronized* value of the TSC (perhaps even zero) was being
recorded, along with the current time at which kvm_synchronize_tsc()
was called. This could cause *subsequent* writes to fail to synchronize
correctly.

Fix that by extrapolating the generation's TSC value to the current
time (data = cur_tsc_write + elapsed cycles) before passing it to
__kvm_synchronize_tsc(), so the recorded {data, ns} pair always lies
on the generation's line and the one-second matching window slides
forward with each synchronized write. When the TSC is stable, reuse
the generation's offset exactly; when it is unstable, compute a new
offset for the extrapolated value.

Furthermore, there were *three* different TSC reads used for calculating
the "current" time, all slightly different from each other. Fix that by
using kvm_get_time_and_clockread() where possible and using the same
host_tsc value in all cases.

Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
Reviewed-by: Paul Durrant <paul@xxxxxxx>
---
 arch/x86/kvm/x86.c | 39 ++++++++++++++++++++++++++++++---------
 1 file changed, 30 insertions(+), 9 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 32563a91fba9..7a2b0b99373f 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -210,6 +210,10 @@ module_param(pi_inject_timer, bint, 0644);
 static bool __read_mostly mitigate_smt_rsb;
 module_param(mitigate_smt_rsb, bool, 0444);
 
+#ifdef CONFIG_X86_64
+static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp);
+#endif
+
 #define KVM_SUPPORTED_XCR0     (XFEATURE_MASK_FP | XFEATURE_MASK_SSE \
                                | XFEATURE_MASK_YMM | XFEATURE_MASK_BNDREGS \
                                | XFEATURE_MASK_BNDCSR | XFEATURE_MASK_AVX512 \
@@ -1374,14 +1378,23 @@ void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 
*user_value)
 {
        u64 data = user_value ? *user_value : 0;
        struct kvm *kvm = vcpu->kvm;
-       u64 offset, ns, elapsed;
+       u64 offset, host_tsc, elapsed;
+       s64 ns;
        unsigned long flags;
        bool matched = false;
        bool synchronizing = false;
 
        raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags);
-       offset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data);
-       ns = get_kvmclock_base_ns();
+
+#ifdef CONFIG_X86_64
+       if (!kvm_get_time_and_clockread(&ns, &host_tsc))
+#endif
+       {
+               host_tsc = rdtsc();
+               ns = get_kvmclock_base_ns();
+       }
+
+       offset = kvm_compute_l1_tsc_offset(vcpu, host_tsc, data);
        elapsed = ns - kvm->arch.last_tsc_nsec;
 
        if (vcpu->arch.virtual_tsc_khz) {
@@ -1424,13 +1437,21 @@ void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 
*user_value)
          */
        if (synchronizing &&
            vcpu->arch.virtual_tsc_khz == kvm->arch.last_tsc_khz) {
-               if (!kvm_check_tsc_unstable()) {
+               /*
+                * If synchronizing, advance the reference point to "now"
+                * so the matching window slides forward with each vCPU.
+                * Extrapolate from the generation's own origin: 'elapsed'
+                * is measured from the most recent write, which lies on
+                * the generation's line only for the first matched write.
+                */
+               u64 delta = nsec_to_cycles(vcpu, ns - kvm->arch.cur_tsc_nsec);
+
+               data = kvm->arch.cur_tsc_write + delta;
+
+               if (!kvm_check_tsc_unstable())
                        offset = kvm->arch.cur_tsc_offset;
-               } else {
-                       u64 delta = nsec_to_cycles(vcpu, elapsed);
-                       data += delta;
-                       offset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data);
-               }
+               else
+                       offset = kvm_compute_l1_tsc_offset(vcpu, host_tsc, 
data);
                matched = true;
        }
 
-- 
2.43.0




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.