1 #include "libcflat.h" 2 #include "smp.h" 3 #include "atomic.h" 4 #include "processor.h" 5 #include "hyperv.h" 6 #include "vm.h" 7 #include "alloc_page.h" 8 9 #define MAX_CPU 4 10 #define TICKS_PER_SEC (1000000000 / 100) 11 12 struct hv_reference_tsc_page *hv_clock; 13 14 /* 15 * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, 16 * yielding a 64-bit result. 17 */ 18 static inline u64 scale_delta(u64 delta, u64 mul_frac) 19 { 20 u64 product, unused; 21 22 __asm__ ( 23 "mulq %3" 24 : "=d" (product), "=a" (unused) : "1" (delta), "rm" ((u64)mul_frac) ); 25 26 return product; 27 } 28 29 static u64 hvclock_tsc_to_ticks(struct hv_reference_tsc_page *shadow, uint64_t tsc) 30 { 31 u64 delta = tsc; 32 return scale_delta(delta, shadow->tsc_scale) + shadow->tsc_offset; 33 } 34 35 /* 36 * Reads a consistent set of time-base values from hypervisor, 37 * into a shadow data area. 38 */ 39 static void hvclock_get_time_values(struct hv_reference_tsc_page *shadow, 40 struct hv_reference_tsc_page *page) 41 { 42 int seq; 43 do { 44 seq = page->tsc_sequence; 45 rmb(); /* fetch version before data */ 46 *shadow = *page; 47 rmb(); /* test version after fetching data */ 48 } while (shadow->tsc_sequence != seq); 49 } 50 51 static uint64_t hv_clock_read(void) 52 { 53 struct hv_reference_tsc_page shadow; 54 55 hvclock_get_time_values(&shadow, hv_clock); 56 return hvclock_tsc_to_ticks(&shadow, rdtsc()); 57 } 58 59 bool ok[MAX_CPU]; 60 uint64_t loops[MAX_CPU]; 61 62 #define iabs(x) ((x) < 0 ? -(x) : (x)) 63 64 static void hv_clock_test(void *data) 65 { 66 int i = (long)data; 67 uint64_t t_msr_prev = rdmsr(HV_X64_MSR_TIME_REF_COUNT); 68 uint64_t t_page_prev = hv_clock_read(); 69 uint64_t end = t_page_prev + TICKS_PER_SEC; 70 bool got_drift = false; 71 bool got_warp_msr = false; 72 bool got_warp_page = false; 73 74 ok[i] = true; 75 do { 76 uint64_t t_page_1, t_page_2, t_msr; 77 78 t_page_1 = hv_clock_read(); 79 barrier(); 80 t_msr = rdmsr(HV_X64_MSR_TIME_REF_COUNT); 81 barrier(); 82 t_page_2 = hv_clock_read(); 83 84 if (!got_drift && (t_msr < t_page_1 || t_msr > t_page_2)) { 85 printf("drift on CPU %d, MSR value = %ld, acceptable [%ld, %ld]\n", i, 86 t_msr, t_page_1, t_page_2); 87 ok[i] = false; 88 got_drift = true; 89 } 90 91 if (!got_warp_msr && t_msr < t_msr_prev) { 92 printf("warp on CPU %d, MSR value = %ld prev MSR value = %ld!\n", i, 93 t_msr, t_msr_prev); 94 ok[i] = false; 95 got_warp_msr = true; 96 break; 97 } 98 99 if (!got_warp_page && t_page_1 < t_page_prev) { 100 printf("warp on CPU %d, TSC page value = %ld prev TSC page value = %ld!\n", i, 101 t_page_1, t_page_prev); 102 ok[i] = false; 103 got_warp_page = true; 104 break; 105 } 106 107 t_page_prev = t_page_1; 108 t_msr_prev = t_msr; 109 110 } while(t_page_prev < end); 111 112 barrier(); 113 } 114 115 static void check_test(int ncpus) 116 { 117 int i; 118 bool pass; 119 120 for (i = ncpus - 1; i >= 0; i--) 121 on_cpu_async(i, hv_clock_test, (void *)(long)i); 122 123 while (cpus_active() > 1) 124 pause(); 125 126 pass = true; 127 for (i = ncpus - 1; i >= 0; i--) 128 pass &= ok[i]; 129 130 report(pass, "TSC reference precision test"); 131 } 132 133 static void hv_perf_test(void *data) 134 { 135 int i = (long)data; 136 uint64_t t = hv_clock_read(); 137 uint64_t end = t + 1000000000 / 100; 138 uint64_t local_loops = 0; 139 140 do { 141 t = hv_clock_read(); 142 local_loops++; 143 } while(t < end); 144 145 loops[i] = local_loops; 146 } 147 148 static void perf_test(int ncpus) 149 { 150 int i; 151 uint64_t total_loops; 152 153 for (i = ncpus - 1; i >= 0; i--) 154 on_cpu_async(i, hv_perf_test, (void *)(long)i); 155 156 while (cpus_active() > 1) 157 pause(); 158 159 total_loops = 0; 160 for (i = ncpus - 1; i >= 0; i--) 161 total_loops += loops[i]; 162 printf("iterations/sec: %" PRId64"\n", total_loops / ncpus); 163 } 164 165 int main(int ac, char **av) 166 { 167 int ncpus; 168 struct hv_reference_tsc_page shadow; 169 uint64_t tsc1, t1, tsc2, t2; 170 uint64_t ref1, ref2; 171 172 if (!hv_time_ref_counter_supported()) { 173 report_skip("time reference counter is unsupported"); 174 goto done; 175 } 176 177 setup_vm(); 178 179 ncpus = cpu_count(); 180 if (ncpus > MAX_CPU) 181 report_abort("number cpus exceeds %d", MAX_CPU); 182 183 hv_clock = alloc_page(); 184 wrmsr(HV_X64_MSR_REFERENCE_TSC, (u64)(uintptr_t)hv_clock | 1); 185 report(rdmsr(HV_X64_MSR_REFERENCE_TSC) == ((u64)(uintptr_t)hv_clock | 1), 186 "MSR value after enabling"); 187 188 hvclock_get_time_values(&shadow, hv_clock); 189 if (shadow.tsc_sequence == 0 || shadow.tsc_sequence == 0xFFFFFFFF) 190 report_abort("Reference TSC page not available\n"); 191 192 printf("sequence: %u. scale: %" PRIx64" offset: %" PRId64"\n", 193 shadow.tsc_sequence, shadow.tsc_scale, shadow.tsc_offset); 194 ref1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT); 195 tsc1 = rdtsc(); 196 t1 = hvclock_tsc_to_ticks(&shadow, tsc1); 197 printf("refcnt %" PRId64", TSC %" PRIx64", TSC reference %" PRId64"\n", 198 ref1, tsc1, t1); 199 200 do 201 ref2 = rdmsr(HV_X64_MSR_TIME_REF_COUNT); 202 while (ref2 < ref1 + 2 * TICKS_PER_SEC); 203 204 tsc2 = rdtsc(); 205 t2 = hvclock_tsc_to_ticks(&shadow, tsc2); 206 printf("refcnt %" PRId64" (delta %" PRId64"), TSC %" PRIx64", " 207 "TSC reference %" PRId64" (delta %" PRId64")\n", 208 ref2, ref2 - ref1, tsc2, t2, t2 - t1); 209 210 check_test(ncpus); 211 perf_test(ncpus); 212 213 wrmsr(HV_X64_MSR_REFERENCE_TSC, 0LL); 214 report(rdmsr(HV_X64_MSR_REFERENCE_TSC) == 0, 215 "MSR value after disabling"); 216 217 done: 218 return report_summary(); 219 } 220