| #include "libcflat.h" |
| #include "smp.h" |
| #include "atomic.h" |
| #include "processor.h" |
| #include "kvmclock.h" |
| |
| #define DEFAULT_TEST_LOOPS 100000000L |
| #define DEFAULT_THRESHOLD 5L |
| |
| long loops = DEFAULT_TEST_LOOPS; |
| long sec = 0; |
| long threshold = DEFAULT_THRESHOLD; |
| |
| struct test_info { |
| struct spinlock lock; |
| u64 warps; /* warp count */ |
| u64 stalls; /* stall count */ |
| long long worst; /* worst warp */ |
| volatile cycle_t last; /* last cycle seen by test */ |
| int check; /* check cycle ? */ |
| }; |
| |
| struct test_info ti[4]; |
| |
| static void wallclock_test(void *data) |
| { |
| int *p_err = data; |
| long ksec, offset; |
| struct timespec ts; |
| |
| kvm_get_wallclock(&ts); |
| ksec = ts.tv_sec; |
| |
| 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); |
| |
| if (offset > threshold || offset < -threshold) { |
| printf("offset too large!\n"); |
| (*p_err)++; |
| } |
| } |
| |
| static void kvm_clock_test(void *data) |
| { |
| struct test_info *hv_test_info = (struct test_info *)data; |
| long i, check = hv_test_info->check; |
| |
| for (i = 0; i < loops; i++){ |
| cycle_t t0, t1; |
| long long delta; |
| |
| if (check == 0) { |
| kvm_clock_read(); |
| continue; |
| } |
| |
| spin_lock(&hv_test_info->lock); |
| t1 = kvm_clock_read(); |
| t0 = hv_test_info->last; |
| hv_test_info->last = kvm_clock_read(); |
| spin_unlock(&hv_test_info->lock); |
| |
| delta = t1 - t0; |
| if (delta < 0) { |
| spin_lock(&hv_test_info->lock); |
| ++hv_test_info->warps; |
| if (delta < hv_test_info->worst){ |
| hv_test_info->worst = delta; |
| printf("Worst warp %lld\n", hv_test_info->worst); |
| } |
| spin_unlock(&hv_test_info->lock); |
| } |
| if (delta == 0) |
| ++hv_test_info->stalls; |
| |
| if (!((unsigned long)i & 31)) |
| asm volatile("rep; nop"); |
| } |
| } |
| |
| static int cycle_test(int check, struct test_info *ti) |
| { |
| unsigned long long begin, end; |
| |
| begin = rdtsc(); |
| |
| ti->check = check; |
| on_cpus(kvm_clock_test, ti); |
| |
| end = rdtsc(); |
| |
| printf("Total vcpus: %d\n", cpu_count()); |
| printf("Test loops: %ld\n", loops); |
| if (check == 1) { |
| printf("Total warps: %" PRId64 "\n", ti->warps); |
| printf("Total stalls: %" PRId64 "\n", ti->stalls); |
| printf("Worst warp: %lld\n", ti->worst); |
| } else |
| printf("TSC cycles: %lld\n", end - begin); |
| |
| return ti->warps ? 1 : 0; |
| } |
| |
| int main(int ac, char **av) |
| { |
| int nerr = 0; |
| int ncpus; |
| int i; |
| |
| if (ac > 1) |
| loops = atol(av[1]); |
| if (ac > 2) |
| sec = atol(av[2]); |
| if (ac > 3) |
| threshold = atol(av[3]); |
| |
| smp_init(); |
| |
| ncpus = cpu_count(); |
| if (ncpus > MAX_CPU) |
| report_abort("number cpus exceeds %d", MAX_CPU); |
| |
| on_cpus(kvm_clock_init, NULL); |
| |
| if (ac > 2) { |
| printf("Wallclock test, threshold %ld\n", threshold); |
| printf("Seconds get from host: %ld\n", sec); |
| for (i = 0; i < ncpus; ++i) |
| on_cpu(i, wallclock_test, &nerr); |
| } |
| |
| printf("Check the stability of raw cycle ...\n"); |
| pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT |
| | PVCLOCK_RAW_CYCLE_BIT); |
| if (cycle_test(1, &ti[0])) |
| printf("Raw cycle is not stable\n"); |
| else |
| printf("Raw cycle is stable\n"); |
| |
| pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT); |
| printf("Monotonic cycle test:\n"); |
| nerr += cycle_test(1, &ti[1]); |
| |
| printf("Measure the performance of raw cycle ...\n"); |
| pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT |
| | PVCLOCK_RAW_CYCLE_BIT); |
| cycle_test(0, &ti[2]); |
| |
| printf("Measure the performance of adjusted cycle ...\n"); |
| pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT); |
| cycle_test(0, &ti[3]); |
| |
| on_cpus(kvm_clock_clear, NULL); |
| |
| return nerr > 0 ? 1 : 0; |
| } |