On 31/08/2016 16:13, Roman Kagan wrote: > The test checks the relative precision of the reference TSC page > and the time reference counter. > > Reworked from the initial version by Paolo Bonzini. > > Signed-off-by: Roman Kagan <rkagan@xxxxxxxxxxxxx> > Cc: Paolo Bonzini <pbonzini@xxxxxxxxxx> > --- > The test is obviously supposed to fail until Hyper-V reference TSC page > implementation lands in KVM. Hi, this test hung for me (but it might be a bad KVM...) and I had to rework my old drift test now that I actually have tried it on a working implementation. I'm sending the new test I used, can you provide patches on top? Thanks, Paolo > x86/Makefile.common | 2 + > x86/Makefile.x86_64 | 1 + > x86/hyperv.h | 9 ++ > x86/hyperv_clock.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > x86/unittests.cfg | 5 ++ > 5 files changed, 247 insertions(+) > create mode 100644 x86/hyperv_clock.c > > diff --git a/x86/Makefile.common b/x86/Makefile.common > index 356d879..287c0cf 100644 > --- a/x86/Makefile.common > +++ b/x86/Makefile.common > @@ -67,6 +67,8 @@ $(TEST_DIR)/hyperv_synic.elf: $(TEST_DIR)/hyperv.o > > $(TEST_DIR)/hyperv_stimer.elf: $(TEST_DIR)/hyperv.o > > +$(TEST_DIR)/hyperv_clock.elf: $(TEST_DIR)/hyperv.o > + > arch_clean: > $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \ > $(TEST_DIR)/.*.d lib/x86/.*.d > diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64 > index e166911..af99279 100644 > --- a/x86/Makefile.x86_64 > +++ b/x86/Makefile.x86_64 > @@ -14,6 +14,7 @@ tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \ > tests += $(TEST_DIR)/svm.flat > tests += $(TEST_DIR)/vmx.flat > tests += $(TEST_DIR)/tscdeadline_latency.flat > +tests += $(TEST_DIR)/hyperv_clock.flat > > include $(TEST_DIR)/Makefile.common > > diff --git a/x86/hyperv.h b/x86/hyperv.h > index 434a933..bef0317 100644 > --- a/x86/hyperv.h > +++ b/x86/hyperv.h > @@ -11,6 +11,7 @@ > #define HV_X64_MSR_SYNTIMER_AVAILABLE (1 << 3) > > #define HV_X64_MSR_TIME_REF_COUNT 0x40000020 > +#define HV_X64_MSR_REFERENCE_TSC 0x40000021 > > /* Define synthetic interrupt controller model specific registers. */ > #define HV_X64_MSR_SCONTROL 0x40000080 > @@ -179,4 +180,12 @@ void synic_sint_create(int vcpu, int sint, int vec, bool auto_eoi); > void synic_sint_set(int vcpu, int sint); > void synic_sint_destroy(int vcpu, int sint); > > +struct hv_reference_tsc_page { > + uint32_t tsc_sequence; > + uint32_t res1; > + uint64_t tsc_scale; > + int64_t tsc_offset; > +}; > + > + > #endif > diff --git a/x86/hyperv_clock.c b/x86/hyperv_clock.c > new file mode 100644 > index 0000000..3cd6af7 > --- /dev/null > +++ b/x86/hyperv_clock.c > @@ -0,0 +1,230 @@ > +#include "libcflat.h" > +#include "smp.h" > +#include "atomic.h" > +#include "processor.h" > +#include "hyperv.h" > +#include "vm.h" > +#include "asm/barrier.h" > + > +#define MAX_CPU 64 > +#define NSEC_PER_SEC 1000000000ULL > +#define HV_NSEC_PER_TICK 100 > +#define TICKS_PER_SEC (NSEC_PER_SEC / HV_NSEC_PER_TICK) > + > +#define DURATION 2 /* testcase duration (s) */ > +#define CPI_MAX 1000 /* max cycles per iteration */ > + > +struct hv_reference_tsc_page *tsc_ref; > + > +struct warp_test_info { > + unsigned long long warps; > + unsigned long long stalls; > + long long worst; > +}; > +struct warp_test_info wti[MAX_CPU]; > + > +struct perf_test_info { > + unsigned long long cycles; > + unsigned long long loops; > +}; > +struct perf_test_info pti[MAX_CPU]; > + > +atomic_t cpus_left; > + > +/* > + * ret = (a * b) >> 64 > + * where ret, a, b are 64bit > + */ > +static inline u64 mul64_shift64(u64 a, u64 b) > +{ > + u64 product; > + > + __asm__ ( > + "mul %[b]" > + : "+a" (a), "=d" (product) > + : [b] "rm" (b) ); > + > + return product; > +} > + > + > +static inline u64 rdtsc_ordered() > +{ > + /* > + * FIXME: on Intel CPUs rmb() aka lfence is sufficient which brings up > + * to 2x speedup > + */ > + mb(); > + return rdtsc(); > +} > + > +static u64 tsc_ref_read_once(void) > +{ > + return mul64_shift64(rdtsc_ordered(), tsc_ref->tsc_scale) + > + tsc_ref->tsc_offset; > +} > + > +u64 time_msr_read(void) > +{ > + return rdmsr(HV_X64_MSR_TIME_REF_COUNT); > +} > + > +u64 tsc_ref_read(void) > +{ > + u32 seq; > + u64 ret; > + > + do { > + seq = tsc_ref->tsc_sequence; > + if (!seq) > + return time_msr_read(); > + smp_rmb(); /* fetch version before data */ > + ret = tsc_ref_read_once(); > + smp_rmb(); /* test version after fetching data */ > + } while (tsc_ref->tsc_sequence != seq); > + > + return ret; > +} > + > +static void warp_test_cpu(void *data) > +{ > + struct warp_test_info *ti = data; > + u64 t = time_msr_read(); > + u64 end = t + DURATION * TICKS_PER_SEC; > + u16 msr_interval = 1; > + u64 msr_time = t + msr_interval; > + ti->warps = 0; > + ti->stalls = 0; > + ti->worst = 0; > + > + do { > + u64 now; > + s64 delta; > + > + if (t >= msr_time) { > + now = time_msr_read(); > + > + if (msr_interval >= (1U << 15)) > + msr_interval = 1; > + else > + msr_interval <<= 1; > + } else > + now = tsc_ref_read(); > + > + delta = now - t; > + > + if (delta < 0) { > + ti->warps++; > + if (delta < ti->worst) > + ti->worst = delta; > + } > + if (delta == 0) > + ti->stalls++; > + > + t = now; > + } while (t < end); > + > + atomic_dec(&cpus_left); > +} > + > +static void perf_test_cpu(void *data) > +{ > + struct perf_test_info *ti = data; > + u64 end = tsc_ref_read() + DURATION * TICKS_PER_SEC; > + ti->loops = 0; > + ti->cycles = rdtsc(); > + > + do > + ti->loops++; > + while (tsc_ref_read() < end); > + > + ti->cycles = rdtsc() - ti->cycles; > + > + atomic_dec(&cpus_left); > +} > + > +static void presence_test(void) > +{ > + u32 seq; > + u64 end = time_msr_read() + DURATION * TICKS_PER_SEC; > + > + do { > + seq = tsc_ref->tsc_sequence; > + if (seq) > + break; > + } while (time_msr_read() < end); > + > + report("TSC reference page being updated", seq); > +} > + > +static void warp_test(int ncpus) > +{ > + int i; > + unsigned long long warps = 0, stalls = 0; > + long long worst = 0; > + > + atomic_set(&cpus_left, ncpus); > + for (i = ncpus - 1; i >= 0; i--) > + on_cpu_async(i, warp_test_cpu, &wti[i]); > + while (atomic_read(&cpus_left)); > + > + for (i = 0; i < ncpus; i++) { > + warps += wti[i].warps; > + stalls += wti[i].stalls; > + if (wti[i].worst < worst) > + worst = wti[i].worst; > + } > + > + report("warps: %llu (worst %lld), stalls: %llu", > + warps == 0, warps, worst, stalls); > +} > + > +static void perf_test(int ncpus) > +{ > + int i; > + unsigned long long loops = 0, cycles = 0; > + > + atomic_set(&cpus_left, ncpus); > + for (i = ncpus - 1; i >= 0; i--) > + on_cpu_async(i, perf_test_cpu, &pti[i]); > + while (atomic_read(&cpus_left)); > + > + for (i = 0; i < ncpus; i++) { > + loops += pti[i].loops; > + cycles += pti[i].cycles; > + } > + > + cycles /= loops; > + report("iterations/s/cpu: %llu, " > + "cycles/iteration: %llu (expected < %u)", > + cycles < CPI_MAX, > + loops / DURATION / ncpus, cycles, CPI_MAX); > +} > + > +int main(int ac, char **av) > +{ > + int ncpus; > + > + setup_vm(); > + smp_init(); > + > + ncpus = cpu_count(); > + if (ncpus > MAX_CPU) > + ncpus = MAX_CPU; > + > + tsc_ref = alloc_page(); > + wrmsr(HV_X64_MSR_REFERENCE_TSC, (u64)(uintptr_t)tsc_ref | 1); > + report("MSR value after enabling", > + rdmsr(HV_X64_MSR_REFERENCE_TSC) == ((u64)(uintptr_t)tsc_ref | 1)); > + > + presence_test(); > + warp_test(ncpus); > + perf_test(ncpus); > + > + wrmsr(HV_X64_MSR_REFERENCE_TSC, 0LL); > + report("MSR value after disabling", rdmsr(HV_X64_MSR_REFERENCE_TSC) == 0); > + > + free_page(tsc_ref); > + > + return report_summary(); > +} > diff --git a/x86/unittests.cfg b/x86/unittests.cfg > index 4a1f74e..a866613 100644 > --- a/x86/unittests.cfg > +++ b/x86/unittests.cfg > @@ -200,3 +200,8 @@ extra_params = -cpu kvm64,hv_synic -device hyperv-testdev > file = hyperv_stimer.flat > smp = 2 > extra_params = -cpu kvm64,hv_time,hv_synic,hv_stimer -device hyperv-testdev > + > +[hyperv_clock] > +file = hyperv_clock.flat > +smp = 2 > +extra_params = -cpu kvm64,hv_time > -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html