With master clock, a pvclock clock read calculates:

ret = system_timestamp + [ (rdtsc + tsc_offset) - tsc_timestamp ]

Where 'rdtsc' is the host TSC.

system_timestamp and tsc_timestamp are unique, one tuple 
per VM: the "master clock".

Given a host with synchronized TSCs, its obvious that
guest TSC must be matched for the above to guarantee monotonicity.

Allow master clock usage only if guest TSCs are synchronized.

Signed-off-by: Marcelo Tosatti <mtosa...@redhat.com>

Index: vsyscall/arch/x86/include/asm/kvm_host.h
===================================================================
--- vsyscall.orig/arch/x86/include/asm/kvm_host.h
+++ vsyscall/arch/x86/include/asm/kvm_host.h
@@ -563,6 +563,7 @@ struct kvm_arch {
        u64 cur_tsc_write;
        u64 cur_tsc_offset;
        u8  cur_tsc_generation;
+       int nr_vcpus_matched_tsc;
 
        spinlock_t pvclock_gtod_sync_lock;
        bool use_master_clock;
Index: vsyscall/arch/x86/kvm/x86.c
===================================================================
--- vsyscall.orig/arch/x86/kvm/x86.c
+++ vsyscall/arch/x86/kvm/x86.c
@@ -1047,12 +1047,38 @@ static u64 compute_guest_tsc(struct kvm_
        return tsc;
 }
 
+void kvm_track_tsc_matching(struct kvm_vcpu *vcpu)
+{
+       bool vcpus_matched;
+       bool do_request = false;
+       struct kvm_arch *ka = &vcpu->kvm->arch;
+       struct pvclock_gtod_data *gtod = &pvclock_gtod_data;
+
+       vcpus_matched = (ka->nr_vcpus_matched_tsc + 1 ==
+                        atomic_read(&vcpu->kvm->online_vcpus));
+
+       if (vcpus_matched && gtod->clock.vclock_mode == VCLOCK_TSC)
+               if (!ka->use_master_clock)
+                       do_request = 1;
+
+       if (!vcpus_matched && ka->use_master_clock)
+                       do_request = 1;
+
+       if (do_request)
+               kvm_make_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu);
+
+       trace_kvm_track_tsc(vcpu->vcpu_id, ka->nr_vcpus_matched_tsc,
+                           atomic_read(&vcpu->kvm->online_vcpus),
+                           ka->use_master_clock, gtod->clock.vclock_mode);
+}
+
 void kvm_write_tsc(struct kvm_vcpu *vcpu, u64 data)
 {
        struct kvm *kvm = vcpu->kvm;
        u64 offset, ns, elapsed;
        unsigned long flags;
        s64 usdiff;
+       bool matched;
 
        raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags);
        offset = kvm_x86_ops->compute_tsc_offset(vcpu, data);
@@ -1095,6 +1121,7 @@ void kvm_write_tsc(struct kvm_vcpu *vcpu
                        offset = kvm_x86_ops->compute_tsc_offset(vcpu, data);
                        pr_debug("kvm: adjusted tsc offset by %llu\n", delta);
                }
+               matched = true;
        } else {
                /*
                 * We split periods of matched TSC writes into generations.
@@ -1109,6 +1136,7 @@ void kvm_write_tsc(struct kvm_vcpu *vcpu
                kvm->arch.cur_tsc_nsec = ns;
                kvm->arch.cur_tsc_write = data;
                kvm->arch.cur_tsc_offset = offset;
+               matched = false;
                pr_debug("kvm: new tsc generation %u, clock %llu\n",
                         kvm->arch.cur_tsc_generation, data);
        }
@@ -1132,6 +1160,15 @@ void kvm_write_tsc(struct kvm_vcpu *vcpu
 
        kvm_x86_ops->write_tsc_offset(vcpu, offset);
        raw_spin_unlock_irqrestore(&kvm->arch.tsc_write_lock, flags);
+
+       spin_lock(&kvm->arch.pvclock_gtod_sync_lock);
+       if (matched)
+               kvm->arch.nr_vcpus_matched_tsc++;
+       else
+               kvm->arch.nr_vcpus_matched_tsc = 0;
+
+       kvm_track_tsc_matching(vcpu);
+       spin_unlock(&kvm->arch.pvclock_gtod_sync_lock);
 }
 
 EXPORT_SYMBOL_GPL(kvm_write_tsc);
@@ -1222,8 +1259,9 @@ static atomic_t kvm_guest_has_master_clo
 
 /*
  *
- * Assuming a stable TSC across physical CPUS, the following condition
- * is possible. Each numbered line represents an event visible to both
+ * Assuming a stable TSC across physical CPUS, and a stable TSC
+ * across virtual CPUs, the following condition is possible.
+ * Each numbered line represents an event visible to both
  * CPUs at the next numbered event.
  *
  * "timespecX" represents host monotonic time. "tscX" represents
@@ -1256,7 +1294,7 @@ static atomic_t kvm_guest_has_master_clo
  * copy of host monotonic time values. Update that master copy
  * in lockstep.
  *
- * Rely on synchronization of host TSCs for monotonicity.
+ * Rely on synchronization of host TSCs and guest TSCs for monotonicity.
  *
  */
 
@@ -1264,19 +1302,26 @@ static void pvclock_update_vm_gtod_copy(
 {
        struct kvm_arch *ka = &kvm->arch;
        int vclock_mode;
+       bool host_tsc_clocksource, vcpus_matched;
 
+       vcpus_matched = (ka->nr_vcpus_matched_tsc + 1 ==
+                               atomic_read(&kvm->online_vcpus));
        /*
         * If the host uses TSC clock, then passthrough TSC as stable
         * to the guest.
         */
-       ka->use_master_clock = kvm_get_time_and_clockread(
+       host_tsc_clocksource = kvm_get_time_and_clockread(
                                        &ka->master_kernel_ns,
                                        &ka->master_cycle_now);
+
+       ka->use_master_clock = host_tsc_clocksource & vcpus_matched;
+
        if (ka->use_master_clock)
                atomic_set(&kvm_guest_has_master_clock, 1);
 
        vclock_mode = pvclock_gtod_data.clock.vclock_mode;
-       trace_kvm_update_master_clock(ka->use_master_clock, vclock_mode);
+       trace_kvm_update_master_clock(ka->use_master_clock, vclock_mode,
+                                     vcpus_matched);
 }
 
 static void kvm_write_pvtime(struct kvm_vcpu *v, struct page *page,
Index: vsyscall/arch/x86/kvm/trace.h
===================================================================
--- vsyscall.orig/arch/x86/kvm/trace.h
+++ vsyscall/arch/x86/kvm/trace.h
@@ -762,21 +762,54 @@ TRACE_EVENT(
        {VCLOCK_HPET, "hpet"}                   \
 
 TRACE_EVENT(kvm_update_master_clock,
-       TP_PROTO(bool use_master_clock, unsigned int host_clock),
-       TP_ARGS(use_master_clock, host_clock),
+       TP_PROTO(bool use_master_clock, unsigned int host_clock, bool 
offset_matched),
+       TP_ARGS(use_master_clock, host_clock, offset_matched),
 
        TP_STRUCT__entry(
                __field(                bool,   use_master_clock        )
                __field(        unsigned int,   host_clock              )
+               __field(                bool,   offset_matched          )
        ),
 
        TP_fast_assign(
                __entry->use_master_clock       = use_master_clock;
                __entry->host_clock             = host_clock;
+               __entry->offset_matched         = offset_matched;
        ),
 
-       TP_printk("masterclock %d hostclock %s",
+       TP_printk("masterclock %d hostclock %s offsetmatched %u",
                  __entry->use_master_clock,
+                 __print_symbolic(__entry->host_clock, host_clocks),
+                 __entry->offset_matched)
+);
+
+TRACE_EVENT(kvm_track_tsc,
+       TP_PROTO(unsigned int vcpu_id, unsigned int nr_matched,
+                unsigned int online_vcpus, bool use_master_clock,
+                unsigned int host_clock),
+       TP_ARGS(vcpu_id, nr_matched, online_vcpus, use_master_clock,
+               host_clock),
+
+       TP_STRUCT__entry(
+               __field(        unsigned int,   vcpu_id                 )
+               __field(        unsigned int,   nr_vcpus_matched_tsc    )
+               __field(        unsigned int,   online_vcpus            )
+               __field(        bool,           use_master_clock        )
+               __field(        unsigned int,   host_clock              )
+       ),
+
+       TP_fast_assign(
+               __entry->vcpu_id                = vcpu_id;
+               __entry->nr_vcpus_matched_tsc   = nr_matched;
+               __entry->online_vcpus           = online_vcpus;
+               __entry->use_master_clock       = use_master_clock;
+               __entry->host_clock             = host_clock;
+       ),
+
+       TP_printk("vcpu_id %u masterclock %u offsetmatched %u nr_online %u"
+                 " hostclock %s",
+                 __entry->vcpu_id, __entry->use_master_clock,
+                 __entry->nr_vcpus_matched_tsc, __entry->online_vcpus,
                  __print_symbolic(__entry->host_clock, host_clocks))
 );
 


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to