diff options
author | Marcelo Tosatti <mtosatti@redhat.com> | 2017-01-24 12:09:39 -0500 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2017-02-07 12:16:45 -0500 |
commit | 55dd00a73a518281bc846dc5de1a718349431eb2 (patch) | |
tree | 13c912f125578744049bf28902e26e5e30b57b66 | |
parent | 6342c50ad12e8ce0736e722184a7dbdea4a3477f (diff) |
KVM: x86: add KVM_HC_CLOCK_PAIRING hypercall
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>
-rw-r--r-- | Documentation/virtual/kvm/hypercalls.txt | 35 | ||||
-rw-r--r-- | arch/x86/include/uapi/asm/kvm_para.h | 9 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 66 | ||||
-rw-r--r-- | include/uapi/linux/kvm_para.h | 2 |
4 files changed, 112 insertions, 0 deletions
diff --git a/Documentation/virtual/kvm/hypercalls.txt b/Documentation/virtual/kvm/hypercalls.txt index c8d040e27046..feaaa634f154 100644 --- a/Documentation/virtual/kvm/hypercalls.txt +++ b/Documentation/virtual/kvm/hypercalls.txt | |||
@@ -81,3 +81,38 @@ the vcpu to sleep until occurrence of an appropriate event. Another vcpu of the | |||
81 | same guest can wakeup the sleeping vcpu by issuing KVM_HC_KICK_CPU hypercall, | 81 | same guest can wakeup the sleeping vcpu by issuing KVM_HC_KICK_CPU hypercall, |
82 | specifying APIC ID (a1) of the vcpu to be woken up. An additional argument (a0) | 82 | specifying APIC ID (a1) of the vcpu to be woken up. An additional argument (a0) |
83 | is used in the hypercall for future use. | 83 | is used in the hypercall for future use. |
84 | |||
85 | |||
86 | 6. KVM_HC_CLOCK_PAIRING | ||
87 | ------------------------ | ||
88 | Architecture: x86 | ||
89 | Status: active | ||
90 | Purpose: Hypercall used to synchronize host and guest clocks. | ||
91 | Usage: | ||
92 | |||
93 | a0: guest physical address where host copies | ||
94 | "struct kvm_clock_offset" structure. | ||
95 | |||
96 | a1: clock_type, ATM only KVM_CLOCK_PAIRING_WALLCLOCK (0) | ||
97 | is supported (corresponding to the host's CLOCK_REALTIME clock). | ||
98 | |||
99 | struct kvm_clock_pairing { | ||
100 | __s64 sec; | ||
101 | __s64 nsec; | ||
102 | __u64 tsc; | ||
103 | __u32 flags; | ||
104 | __u32 pad[9]; | ||
105 | }; | ||
106 | |||
107 | Where: | ||
108 | * sec: seconds from clock_type clock. | ||
109 | * nsec: nanoseconds from clock_type clock. | ||
110 | * tsc: guest TSC value used to calculate sec/nsec pair | ||
111 | * flags: flags, unused (0) at the moment. | ||
112 | |||
113 | The hypercall lets a guest compute a precise timestamp across | ||
114 | host and guest. The guest can use the returned TSC value to | ||
115 | compute the CLOCK_REALTIME for its clock, at the same instant. | ||
116 | |||
117 | Returns KVM_EOPNOTSUPP if the host does not use TSC clocksource, | ||
118 | or if clock type is different than KVM_CLOCK_PAIRING_WALLCLOCK. | ||
diff --git a/arch/x86/include/uapi/asm/kvm_para.h b/arch/x86/include/uapi/asm/kvm_para.h index 1421a6585126..cff0bb6556f8 100644 --- a/arch/x86/include/uapi/asm/kvm_para.h +++ b/arch/x86/include/uapi/asm/kvm_para.h | |||
@@ -50,6 +50,15 @@ struct kvm_steal_time { | |||
50 | __u32 pad[11]; | 50 | __u32 pad[11]; |
51 | }; | 51 | }; |
52 | 52 | ||
53 | #define KVM_CLOCK_PAIRING_WALLCLOCK 0 | ||
54 | struct kvm_clock_pairing { | ||
55 | __s64 sec; | ||
56 | __s64 nsec; | ||
57 | __u64 tsc; | ||
58 | __u32 flags; | ||
59 | __u32 pad[9]; | ||
60 | }; | ||
61 | |||
53 | #define KVM_STEAL_ALIGNMENT_BITS 5 | 62 | #define KVM_STEAL_ALIGNMENT_BITS 5 |
54 | #define KVM_STEAL_VALID_BITS ((-1ULL << (KVM_STEAL_ALIGNMENT_BITS + 1))) | 63 | #define KVM_STEAL_VALID_BITS ((-1ULL << (KVM_STEAL_ALIGNMENT_BITS + 1))) |
55 | #define KVM_STEAL_RESERVED_MASK (((1 << KVM_STEAL_ALIGNMENT_BITS) - 1 ) << 1) | 64 | #define KVM_STEAL_RESERVED_MASK (((1 << KVM_STEAL_ALIGNMENT_BITS) - 1 ) << 1) |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4fd4d4f35caf..09e5d31dac98 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -1142,6 +1142,7 @@ struct pvclock_gtod_data { | |||
1142 | 1142 | ||
1143 | u64 boot_ns; | 1143 | u64 boot_ns; |
1144 | u64 nsec_base; | 1144 | u64 nsec_base; |
1145 | u64 wall_time_sec; | ||
1145 | }; | 1146 | }; |
1146 | 1147 | ||
1147 | static struct pvclock_gtod_data pvclock_gtod_data; | 1148 | static struct pvclock_gtod_data pvclock_gtod_data; |
@@ -1165,6 +1166,8 @@ static void update_pvclock_gtod(struct timekeeper *tk) | |||
1165 | vdata->boot_ns = boot_ns; | 1166 | vdata->boot_ns = boot_ns; |
1166 | vdata->nsec_base = tk->tkr_mono.xtime_nsec; | 1167 | vdata->nsec_base = tk->tkr_mono.xtime_nsec; |
1167 | 1168 | ||
1169 | vdata->wall_time_sec = tk->xtime_sec; | ||
1170 | |||
1168 | write_seqcount_end(&vdata->seq); | 1171 | write_seqcount_end(&vdata->seq); |
1169 | } | 1172 | } |
1170 | #endif | 1173 | #endif |
@@ -1626,6 +1629,28 @@ static int do_monotonic_boot(s64 *t, u64 *cycle_now) | |||
1626 | return mode; | 1629 | return mode; |
1627 | } | 1630 | } |
1628 | 1631 | ||
1632 | static int do_realtime(struct timespec *ts, u64 *cycle_now) | ||
1633 | { | ||
1634 | struct pvclock_gtod_data *gtod = &pvclock_gtod_data; | ||
1635 | unsigned long seq; | ||
1636 | int mode; | ||
1637 | u64 ns; | ||
1638 | |||
1639 | do { | ||
1640 | seq = read_seqcount_begin(>od->seq); | ||
1641 | mode = gtod->clock.vclock_mode; | ||
1642 | ts->tv_sec = gtod->wall_time_sec; | ||
1643 | ns = gtod->nsec_base; | ||
1644 | ns += vgettsc(cycle_now); | ||
1645 | ns >>= gtod->clock.shift; | ||
1646 | } while (unlikely(read_seqcount_retry(>od->seq, seq))); | ||
1647 | |||
1648 | ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns); | ||
1649 | ts->tv_nsec = ns; | ||
1650 | |||
1651 | return mode; | ||
1652 | } | ||
1653 | |||
1629 | /* returns true if host is using tsc clocksource */ | 1654 | /* returns true if host is using tsc clocksource */ |
1630 | static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *cycle_now) | 1655 | static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *cycle_now) |
1631 | { | 1656 | { |
@@ -1635,6 +1660,17 @@ static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *cycle_now) | |||
1635 | 1660 | ||
1636 | return do_monotonic_boot(kernel_ns, cycle_now) == VCLOCK_TSC; | 1661 | return do_monotonic_boot(kernel_ns, cycle_now) == VCLOCK_TSC; |
1637 | } | 1662 | } |
1663 | |||
1664 | /* returns true if host is using tsc clocksource */ | ||
1665 | static bool kvm_get_walltime_and_clockread(struct timespec *ts, | ||
1666 | u64 *cycle_now) | ||
1667 | { | ||
1668 | /* checked again under seqlock below */ | ||
1669 | if (pvclock_gtod_data.clock.vclock_mode != VCLOCK_TSC) | ||
1670 | return false; | ||
1671 | |||
1672 | return do_realtime(ts, cycle_now) == VCLOCK_TSC; | ||
1673 | } | ||
1638 | #endif | 1674 | #endif |
1639 | 1675 | ||
1640 | /* | 1676 | /* |
@@ -6112,6 +6148,33 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu) | |||
6112 | } | 6148 | } |
6113 | EXPORT_SYMBOL_GPL(kvm_emulate_halt); | 6149 | EXPORT_SYMBOL_GPL(kvm_emulate_halt); |
6114 | 6150 | ||
6151 | static int kvm_pv_clock_pairing(struct kvm_vcpu *vcpu, gpa_t paddr, | ||
6152 | unsigned long clock_type) | ||
6153 | { | ||
6154 | struct kvm_clock_pairing clock_pairing; | ||
6155 | struct timespec ts; | ||
6156 | cycle_t cycle; | ||
6157 | int ret; | ||
6158 | |||
6159 | if (clock_type != KVM_CLOCK_PAIRING_WALLCLOCK) | ||
6160 | return -KVM_EOPNOTSUPP; | ||
6161 | |||
6162 | if (kvm_get_walltime_and_clockread(&ts, &cycle) == false) | ||
6163 | return -KVM_EOPNOTSUPP; | ||
6164 | |||
6165 | clock_pairing.sec = ts.tv_sec; | ||
6166 | clock_pairing.nsec = ts.tv_nsec; | ||
6167 | clock_pairing.tsc = kvm_read_l1_tsc(vcpu, cycle); | ||
6168 | clock_pairing.flags = 0; | ||
6169 | |||
6170 | ret = 0; | ||
6171 | if (kvm_write_guest(vcpu->kvm, paddr, &clock_pairing, | ||
6172 | sizeof(struct kvm_clock_pairing))) | ||
6173 | ret = -KVM_EFAULT; | ||
6174 | |||
6175 | return ret; | ||
6176 | } | ||
6177 | |||
6115 | /* | 6178 | /* |
6116 | * kvm_pv_kick_cpu_op: Kick a vcpu. | 6179 | * kvm_pv_kick_cpu_op: Kick a vcpu. |
6117 | * | 6180 | * |
@@ -6176,6 +6239,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) | |||
6176 | kvm_pv_kick_cpu_op(vcpu->kvm, a0, a1); | 6239 | kvm_pv_kick_cpu_op(vcpu->kvm, a0, a1); |
6177 | ret = 0; | 6240 | ret = 0; |
6178 | break; | 6241 | break; |
6242 | case KVM_HC_CLOCK_PAIRING: | ||
6243 | ret = kvm_pv_clock_pairing(vcpu, a0, a1); | ||
6244 | break; | ||
6179 | default: | 6245 | default: |
6180 | ret = -KVM_ENOSYS; | 6246 | ret = -KVM_ENOSYS; |
6181 | break; | 6247 | break; |
diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index bf6cd7d5cac2..fed506aeff62 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h | |||
@@ -14,6 +14,7 @@ | |||
14 | #define KVM_EFAULT EFAULT | 14 | #define KVM_EFAULT EFAULT |
15 | #define KVM_E2BIG E2BIG | 15 | #define KVM_E2BIG E2BIG |
16 | #define KVM_EPERM EPERM | 16 | #define KVM_EPERM EPERM |
17 | #define KVM_EOPNOTSUPP 95 | ||
17 | 18 | ||
18 | #define KVM_HC_VAPIC_POLL_IRQ 1 | 19 | #define KVM_HC_VAPIC_POLL_IRQ 1 |
19 | #define KVM_HC_MMU_OP 2 | 20 | #define KVM_HC_MMU_OP 2 |
@@ -23,6 +24,7 @@ | |||
23 | #define KVM_HC_MIPS_GET_CLOCK_FREQ 6 | 24 | #define KVM_HC_MIPS_GET_CLOCK_FREQ 6 |
24 | #define KVM_HC_MIPS_EXIT_VM 7 | 25 | #define KVM_HC_MIPS_EXIT_VM 7 |
25 | #define KVM_HC_MIPS_CONSOLE_OUTPUT 8 | 26 | #define KVM_HC_MIPS_CONSOLE_OUTPUT 8 |
27 | #define KVM_HC_CLOCK_PAIRING 9 | ||
26 | 28 | ||
27 | /* | 29 | /* |
28 | * hypercalls use architecture specific | 30 | * hypercalls use architecture specific |