The seconds/nanoseconds pair returned from the hypercall is pretty much the same value that is written into struct pvclock_wall_clock, except better because it does not suffer from the y2038 problem. Test that this is the case, in preparation for using KVM_HC_CLOCK_PAIRING in Linux instead of MSR_KVM_WALL_CLOCK_NEW. Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx> --- x86/kvmclock.c | 16 ++++++++++++++++ x86/kvmclock.h | 12 ++++++++++++ x86/kvmclock_test.c | 29 ++++++++++++++++++++--------- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/x86/kvmclock.c b/x86/kvmclock.c index bad0784..76569ad 100644 --- a/x86/kvmclock.c +++ b/x86/kvmclock.c @@ -282,6 +282,22 @@ void kvm_get_wallclock(struct timespec *ts) pvclock_read_wallclock(&wall_clock, vcpu_time, ts); } +uint64_t kvm_hc_get_wallclock(struct timespec *ts) +{ + u32 ret; + struct kvm_clock_pairing clk = {0}; + + asm volatile("vmcall" + : "=a"(ret) + : "a"(KVM_HC_CLOCK_PAIRING), "b"(&clk), + "c"(KVM_CLOCK_PAIRING_WALLCLOCK) : "memory"); + if (ret != 0) + return -ret; + ts->tv_sec = clk.sec; + ts->tv_nsec = clk.nsec; + return clk.tsc; +} + void pvclock_set_flags(unsigned char flags) { valid_flags = flags; diff --git a/x86/kvmclock.h b/x86/kvmclock.h index dff6802..f74bf8b 100644 --- a/x86/kvmclock.h +++ b/x86/kvmclock.h @@ -9,6 +9,9 @@ #define PVCLOCK_TSC_STABLE_BIT (1 << 0) #define PVCLOCK_RAW_CYCLE_BIT (1 << 7) /* Get raw cycle */ +#define KVM_HC_CLOCK_PAIRING 9 +#define KVM_CLOCK_PAIRING_WALLCLOCK 0 + # define NSEC_PER_SEC 1000000000ULL typedef u64 cycle_t; @@ -35,9 +38,18 @@ struct timespec { long tv_nsec; }; +struct kvm_clock_pairing { + s64 sec; + s64 nsec; + u64 tsc; + u32 flags; + u32 pad[9]; +}; + void pvclock_set_flags(unsigned char flags); cycle_t kvm_clock_read(); void kvm_get_wallclock(struct timespec *ts); +uint64_t kvm_hc_get_wallclock(struct timespec *ts); void kvm_clock_init(void *data); void kvm_clock_clear(void *data); diff --git a/x86/kvmclock_test.c b/x86/kvmclock_test.c index 48a7cdb..985bba1 100644 --- a/x86/kvmclock_test.c +++ b/x86/kvmclock_test.c @@ -5,11 +5,12 @@ #include "kvmclock.h" #define DEFAULT_TEST_LOOPS 100000000L -#define DEFAULT_THRESHOLD 5L + +#define WALLCLOCK_THRESHOLD 5 /* seconds */ +#define KVMCLOCK_THRESHOLD 100000000 /* nanoseconds */ long loops = DEFAULT_TEST_LOOPS; long sec = 0; -long threshold = DEFAULT_THRESHOLD; struct test_info { struct spinlock lock; @@ -25,7 +26,8 @@ struct test_info ti[4]; static void wallclock_test(void *data) { int *p_err = data; - long ksec, offset; + uint64_t ksec, knsec; + long offset; struct timespec ts; kvm_get_wallclock(&ts); @@ -33,12 +35,23 @@ static void wallclock_test(void *data) offset = ksec - sec; printf("Raw nanoseconds value from kvmclock: %" PRIu64 " (cpu %d)\n", kvm_clock_read(), smp_id()); - printf("Seconds get from kvmclock: %ld (cpu %d, offset: %ld)\n", ksec, smp_id(), offset); + printf("Seconds get from kvmclock: %" PRIu64 " (cpu %d, offset: %ld)\n", ksec, smp_id(), offset); - if (offset > threshold || offset < -threshold) { - printf("offset too large!\n"); + if (offset > WALLCLOCK_THRESHOLD || offset < -WALLCLOCK_THRESHOLD) { + printf("offset too large (threshold: %d)!\n", WALLCLOCK_THRESHOLD); (*p_err)++; } + + knsec = ksec * 1000000000ULL + ts.tv_nsec; + if (kvm_hc_get_wallclock(&ts) >= 0) { + offset = knsec - (ts.tv_sec * 1000000000ULL + ts.tv_nsec); + printf("Nanoseconds from hypercall: %" PRIu64 " (cpu %d, offset: %ld)\n", ksec, smp_id(), offset); + if (offset > KVMCLOCK_THRESHOLD || offset < -KVMCLOCK_THRESHOLD) { + printf("offset too large (threshold: %d)!\n", KVMCLOCK_THRESHOLD); + (*p_err)++; + } + } + } static void kvm_clock_test(void *data) @@ -112,8 +125,6 @@ int main(int ac, char **av) loops = atol(av[1]); if (ac > 2) sec = atol(av[2]); - if (ac > 3) - threshold = atol(av[3]); smp_init(); @@ -124,7 +135,7 @@ int main(int ac, char **av) on_cpus(kvm_clock_init, NULL); if (ac > 2) { - printf("Wallclock test, threshold %ld\n", threshold); + printf("Wallclock test\n"); printf("Seconds get from host: %ld\n", sec); for (i = 0; i < ncpus; ++i) on_cpu(i, wallclock_test, &nerr); -- 2.14.3