Skip to content

Commit 55dd00a

Browse files
matosattibonzini
authored andcommitted
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>
1 parent 6342c50 commit 55dd00a

File tree

4 files changed

+112
-0
lines changed

4 files changed

+112
-0
lines changed

Documentation/virtual/kvm/hypercalls.txt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,38 @@ the vcpu to sleep until occurrence of an appropriate event. Another vcpu of the
8181
same guest can wakeup the sleeping vcpu by issuing KVM_HC_KICK_CPU hypercall,
8282
specifying APIC ID (a1) of the vcpu to be woken up. An additional argument (a0)
8383
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.

arch/x86/include/uapi/asm/kvm_para.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ struct kvm_steal_time {
5050
__u32 pad[11];
5151
};
5252

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+
5362
#define KVM_STEAL_ALIGNMENT_BITS 5
5463
#define KVM_STEAL_VALID_BITS ((-1ULL << (KVM_STEAL_ALIGNMENT_BITS + 1)))
5564
#define KVM_STEAL_RESERVED_MASK (((1 << KVM_STEAL_ALIGNMENT_BITS) - 1 ) << 1)

arch/x86/kvm/x86.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,7 @@ struct pvclock_gtod_data {
11421142

11431143
u64 boot_ns;
11441144
u64 nsec_base;
1145+
u64 wall_time_sec;
11451146
};
11461147

11471148
static struct pvclock_gtod_data pvclock_gtod_data;
@@ -1165,6 +1166,8 @@ static void update_pvclock_gtod(struct timekeeper *tk)
11651166
vdata->boot_ns = boot_ns;
11661167
vdata->nsec_base = tk->tkr_mono.xtime_nsec;
11671168

1169+
vdata->wall_time_sec = tk->xtime_sec;
1170+
11681171
write_seqcount_end(&vdata->seq);
11691172
}
11701173
#endif
@@ -1626,6 +1629,28 @@ static int do_monotonic_boot(s64 *t, u64 *cycle_now)
16261629
return mode;
16271630
}
16281631

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(&gtod->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(&gtod->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+
16291654
/* returns true if host is using tsc clocksource */
16301655
static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *cycle_now)
16311656
{
@@ -1635,6 +1660,17 @@ static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *cycle_now)
16351660

16361661
return do_monotonic_boot(kernel_ns, cycle_now) == VCLOCK_TSC;
16371662
}
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+
}
16381674
#endif
16391675

16401676
/*
@@ -6112,6 +6148,33 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu)
61126148
}
61136149
EXPORT_SYMBOL_GPL(kvm_emulate_halt);
61146150

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+
61156178
/*
61166179
* kvm_pv_kick_cpu_op: Kick a vcpu.
61176180
*
@@ -6176,6 +6239,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
61766239
kvm_pv_kick_cpu_op(vcpu->kvm, a0, a1);
61776240
ret = 0;
61786241
break;
6242+
case KVM_HC_CLOCK_PAIRING:
6243+
ret = kvm_pv_clock_pairing(vcpu, a0, a1);
6244+
break;
61796245
default:
61806246
ret = -KVM_ENOSYS;
61816247
break;

include/uapi/linux/kvm_para.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define KVM_EFAULT EFAULT
1515
#define KVM_E2BIG E2BIG
1616
#define KVM_EPERM EPERM
17+
#define KVM_EOPNOTSUPP 95
1718

1819
#define KVM_HC_VAPIC_POLL_IRQ 1
1920
#define KVM_HC_MMU_OP 2
@@ -23,6 +24,7 @@
2324
#define KVM_HC_MIPS_GET_CLOCK_FREQ 6
2425
#define KVM_HC_MIPS_EXIT_VM 7
2526
#define KVM_HC_MIPS_CONSOLE_OUTPUT 8
27+
#define KVM_HC_CLOCK_PAIRING 9
2628

2729
/*
2830
* hypercalls use architecture specific

0 commit comments

Comments
 (0)