KVM: x86: add KVM_HC_CLOCK_PAIRING hypercall
authorMarcelo Tosatti <mtosatti@redhat.com>
Tue, 24 Jan 2017 17:09:39 +0000 (15:09 -0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Tue, 7 Feb 2017 17:16:45 +0000 (18:16 +0100)
Add a hypercall to retrieve the host realtime clock and the TSC value
used to calculate that clock read.

Used to implement clock synchronization between host and guest.

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Documentation/virtual/kvm/hypercalls.txt
arch/x86/include/uapi/asm/kvm_para.h
arch/x86/kvm/x86.c
include/uapi/linux/kvm_para.h

index c8d040e27046d57bdc606ce88d4c2fc63f460848..feaaa634f154bb44d400758f82adcc3f1a6c85da 100644 (file)
@@ -81,3 +81,38 @@ the vcpu to sleep until occurrence of an appropriate event. Another vcpu of the
 same guest can wakeup the sleeping vcpu by issuing KVM_HC_KICK_CPU hypercall,
 specifying APIC ID (a1) of the vcpu to be woken up. An additional argument (a0)
 is used in the hypercall for future use.
+
+
+6. KVM_HC_CLOCK_PAIRING
+------------------------
+Architecture: x86
+Status: active
+Purpose: Hypercall used to synchronize host and guest clocks.
+Usage:
+
+a0: guest physical address where host copies
+"struct kvm_clock_offset" structure.
+
+a1: clock_type, ATM only KVM_CLOCK_PAIRING_WALLCLOCK (0)
+is supported (corresponding to the host's CLOCK_REALTIME clock).
+
+               struct kvm_clock_pairing {
+                       __s64 sec;
+                       __s64 nsec;
+                       __u64 tsc;
+                       __u32 flags;
+                       __u32 pad[9];
+               };
+
+       Where:
+               * sec: seconds from clock_type clock.
+               * nsec: nanoseconds from clock_type clock.
+               * tsc: guest TSC value used to calculate sec/nsec pair
+               * flags: flags, unused (0) at the moment.
+
+The hypercall lets a guest compute a precise timestamp across
+host and guest.  The guest can use the returned TSC value to
+compute the CLOCK_REALTIME for its clock, at the same instant.
+
+Returns KVM_EOPNOTSUPP if the host does not use TSC clocksource,
+or if clock type is different than KVM_CLOCK_PAIRING_WALLCLOCK.
index 1421a6585126bca1da9c46d247addc3104f321e2..cff0bb6556f8809ec39d08c61036a86d7c8adde0 100644 (file)
@@ -50,6 +50,15 @@ struct kvm_steal_time {
        __u32 pad[11];
 };
 
+#define KVM_CLOCK_PAIRING_WALLCLOCK 0
+struct kvm_clock_pairing {
+       __s64 sec;
+       __s64 nsec;
+       __u64 tsc;
+       __u32 flags;
+       __u32 pad[9];
+};
+
 #define KVM_STEAL_ALIGNMENT_BITS 5
 #define KVM_STEAL_VALID_BITS ((-1ULL << (KVM_STEAL_ALIGNMENT_BITS + 1)))
 #define KVM_STEAL_RESERVED_MASK (((1 << KVM_STEAL_ALIGNMENT_BITS) - 1 ) << 1)
index 4fd4d4f35cafc086cac50ac687323ab674f4b8fa..09e5d31dac98bffc02015477a3f9b76dda0c2b02 100644 (file)
@@ -1142,6 +1142,7 @@ struct pvclock_gtod_data {
 
        u64             boot_ns;
        u64             nsec_base;
+       u64             wall_time_sec;
 };
 
 static struct pvclock_gtod_data pvclock_gtod_data;
@@ -1165,6 +1166,8 @@ static void update_pvclock_gtod(struct timekeeper *tk)
        vdata->boot_ns                  = boot_ns;
        vdata->nsec_base                = tk->tkr_mono.xtime_nsec;
 
+       vdata->wall_time_sec            = tk->xtime_sec;
+
        write_seqcount_end(&vdata->seq);
 }
 #endif
@@ -1626,6 +1629,28 @@ static int do_monotonic_boot(s64 *t, u64 *cycle_now)
        return mode;
 }
 
+static int do_realtime(struct timespec *ts, u64 *cycle_now)
+{
+       struct pvclock_gtod_data *gtod = &pvclock_gtod_data;
+       unsigned long seq;
+       int mode;
+       u64 ns;
+
+       do {
+               seq = read_seqcount_begin(&gtod->seq);
+               mode = gtod->clock.vclock_mode;
+               ts->tv_sec = gtod->wall_time_sec;
+               ns = gtod->nsec_base;
+               ns += vgettsc(cycle_now);
+               ns >>= gtod->clock.shift;
+       } while (unlikely(read_seqcount_retry(&gtod->seq, seq)));
+
+       ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
+       ts->tv_nsec = ns;
+
+       return mode;
+}
+
 /* returns true if host is using tsc clocksource */
 static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *cycle_now)
 {
@@ -1635,6 +1660,17 @@ static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *cycle_now)
 
        return do_monotonic_boot(kernel_ns, cycle_now) == VCLOCK_TSC;
 }
+
+/* returns true if host is using tsc clocksource */
+static bool kvm_get_walltime_and_clockread(struct timespec *ts,
+                                          u64 *cycle_now)
+{
+       /* checked again under seqlock below */
+       if (pvclock_gtod_data.clock.vclock_mode != VCLOCK_TSC)
+               return false;
+
+       return do_realtime(ts, cycle_now) == VCLOCK_TSC;
+}
 #endif
 
 /*
@@ -6112,6 +6148,33 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu)
 }
 EXPORT_SYMBOL_GPL(kvm_emulate_halt);
 
+static int kvm_pv_clock_pairing(struct kvm_vcpu *vcpu, gpa_t paddr,
+                               unsigned long clock_type)
+{
+       struct kvm_clock_pairing clock_pairing;
+       struct timespec ts;
+       cycle_t cycle;
+       int ret;
+
+       if (clock_type != KVM_CLOCK_PAIRING_WALLCLOCK)
+               return -KVM_EOPNOTSUPP;
+
+       if (kvm_get_walltime_and_clockread(&ts, &cycle) == false)
+               return -KVM_EOPNOTSUPP;
+
+       clock_pairing.sec = ts.tv_sec;
+       clock_pairing.nsec = ts.tv_nsec;
+       clock_pairing.tsc = kvm_read_l1_tsc(vcpu, cycle);
+       clock_pairing.flags = 0;
+
+       ret = 0;
+       if (kvm_write_guest(vcpu->kvm, paddr, &clock_pairing,
+                           sizeof(struct kvm_clock_pairing)))
+               ret = -KVM_EFAULT;
+
+       return ret;
+}
+
 /*
  * kvm_pv_kick_cpu_op:  Kick a vcpu.
  *
@@ -6176,6 +6239,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
                kvm_pv_kick_cpu_op(vcpu->kvm, a0, a1);
                ret = 0;
                break;
+       case KVM_HC_CLOCK_PAIRING:
+               ret = kvm_pv_clock_pairing(vcpu, a0, a1);
+               break;
        default:
                ret = -KVM_ENOSYS;
                break;
index bf6cd7d5cac27883c2feffb0bab762522242ff6e..fed506aeff62f52c585280e8f4da2f96c69be0e9 100644 (file)
@@ -14,6 +14,7 @@
 #define KVM_EFAULT             EFAULT
 #define KVM_E2BIG              E2BIG
 #define KVM_EPERM              EPERM
+#define KVM_EOPNOTSUPP         95
 
 #define KVM_HC_VAPIC_POLL_IRQ          1
 #define KVM_HC_MMU_OP                  2
@@ -23,6 +24,7 @@
 #define KVM_HC_MIPS_GET_CLOCK_FREQ     6
 #define KVM_HC_MIPS_EXIT_VM            7
 #define KVM_HC_MIPS_CONSOLE_OUTPUT     8
+#define KVM_HC_CLOCK_PAIRING           9
 
 /*
  * hypercalls use architecture specific