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