xref: /kvm-unit-tests/x86/hyperv_clock.c (revision 45276b5860522921c77fb3ccc1458ff5f223c3c6)
1907ce0f7SPaolo Bonzini #include "libcflat.h"
2907ce0f7SPaolo Bonzini #include "smp.h"
3907ce0f7SPaolo Bonzini #include "atomic.h"
4907ce0f7SPaolo Bonzini #include "processor.h"
5907ce0f7SPaolo Bonzini #include "hyperv.h"
6907ce0f7SPaolo Bonzini #include "vm.h"
7907ce0f7SPaolo Bonzini 
8907ce0f7SPaolo Bonzini #define MAX_CPU 4
9907ce0f7SPaolo Bonzini #define TICKS_PER_SEC (1000000000 / 100)
10907ce0f7SPaolo Bonzini 
11907ce0f7SPaolo Bonzini struct hv_reference_tsc_page *hv_clock;
12907ce0f7SPaolo Bonzini 
13907ce0f7SPaolo Bonzini /*
14907ce0f7SPaolo Bonzini  * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
15907ce0f7SPaolo Bonzini  * yielding a 64-bit result.
16907ce0f7SPaolo Bonzini  */
17907ce0f7SPaolo Bonzini static inline u64 scale_delta(u64 delta, u64 mul_frac)
18907ce0f7SPaolo Bonzini {
19907ce0f7SPaolo Bonzini 	u64 product, unused;
20907ce0f7SPaolo Bonzini 
21907ce0f7SPaolo Bonzini 	__asm__ (
22*45276b58SThomas Petazzoni 		"mulq %3"
23907ce0f7SPaolo Bonzini 		: "=d" (product), "=a" (unused) : "1" (delta), "rm" ((u64)mul_frac) );
24907ce0f7SPaolo Bonzini 
25907ce0f7SPaolo Bonzini 	return product;
26907ce0f7SPaolo Bonzini }
27907ce0f7SPaolo Bonzini 
28907ce0f7SPaolo Bonzini static u64 hvclock_tsc_to_ticks(struct hv_reference_tsc_page *shadow, uint64_t tsc)
29907ce0f7SPaolo Bonzini {
30907ce0f7SPaolo Bonzini 	u64 delta = tsc;
31907ce0f7SPaolo Bonzini 	return scale_delta(delta, shadow->tsc_scale) + shadow->tsc_offset;
32907ce0f7SPaolo Bonzini }
33907ce0f7SPaolo Bonzini 
34907ce0f7SPaolo Bonzini /*
35907ce0f7SPaolo Bonzini  * Reads a consistent set of time-base values from hypervisor,
36907ce0f7SPaolo Bonzini  * into a shadow data area.
37907ce0f7SPaolo Bonzini  */
38907ce0f7SPaolo Bonzini static void hvclock_get_time_values(struct hv_reference_tsc_page *shadow,
39907ce0f7SPaolo Bonzini 				    struct hv_reference_tsc_page *page)
40907ce0f7SPaolo Bonzini {
41907ce0f7SPaolo Bonzini 	int seq;
42907ce0f7SPaolo Bonzini 	do {
43907ce0f7SPaolo Bonzini 		seq = page->tsc_sequence;
44907ce0f7SPaolo Bonzini 		rmb();		/* fetch version before data */
45907ce0f7SPaolo Bonzini 		*shadow = *page;
46907ce0f7SPaolo Bonzini 		rmb();		/* test version after fetching data */
47907ce0f7SPaolo Bonzini 	} while (shadow->tsc_sequence != seq);
48907ce0f7SPaolo Bonzini }
49907ce0f7SPaolo Bonzini 
50907ce0f7SPaolo Bonzini uint64_t hv_clock_read(void)
51907ce0f7SPaolo Bonzini {
52907ce0f7SPaolo Bonzini 	struct hv_reference_tsc_page shadow;
53907ce0f7SPaolo Bonzini 
54907ce0f7SPaolo Bonzini 	hvclock_get_time_values(&shadow, hv_clock);
55907ce0f7SPaolo Bonzini 	return hvclock_tsc_to_ticks(&shadow, rdtsc());
56907ce0f7SPaolo Bonzini }
57907ce0f7SPaolo Bonzini 
58907ce0f7SPaolo Bonzini atomic_t cpus_left;
59907ce0f7SPaolo Bonzini bool ok[MAX_CPU];
60907ce0f7SPaolo Bonzini uint64_t loops[MAX_CPU];
61907ce0f7SPaolo Bonzini 
62907ce0f7SPaolo Bonzini #define iabs(x)   ((x) < 0 ? -(x) : (x))
63907ce0f7SPaolo Bonzini 
64907ce0f7SPaolo Bonzini static void hv_clock_test(void *data)
65907ce0f7SPaolo Bonzini {
66907ce0f7SPaolo Bonzini 	int i = smp_id();
67907ce0f7SPaolo Bonzini 	uint64_t t = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
68907ce0f7SPaolo Bonzini 	uint64_t end = t + 3 * TICKS_PER_SEC;
69907ce0f7SPaolo Bonzini 	uint64_t msr_sample = t + TICKS_PER_SEC;
70907ce0f7SPaolo Bonzini 	int min_delta = 123456, max_delta = -123456;
71907ce0f7SPaolo Bonzini 	bool got_drift = false;
72907ce0f7SPaolo Bonzini 	bool got_warp = false;
73907ce0f7SPaolo Bonzini 
74907ce0f7SPaolo Bonzini 	ok[i] = true;
75907ce0f7SPaolo Bonzini 	do {
76907ce0f7SPaolo Bonzini 		uint64_t now = hv_clock_read();
77907ce0f7SPaolo Bonzini 		int delta = rdmsr(HV_X64_MSR_TIME_REF_COUNT) - now;
78907ce0f7SPaolo Bonzini 
79907ce0f7SPaolo Bonzini 		min_delta = delta < min_delta ? delta : min_delta;
80907ce0f7SPaolo Bonzini 		if (t < msr_sample) {
81907ce0f7SPaolo Bonzini 			max_delta = delta > max_delta ? delta: max_delta;
82907ce0f7SPaolo Bonzini 		} else if (delta < 0 || delta > max_delta * 3 / 2) {
83907ce0f7SPaolo Bonzini 			printf("suspecting drift on CPU %d? delta = %d, acceptable [0, %d)\n", smp_id(),
84907ce0f7SPaolo Bonzini 			       delta, max_delta);
85907ce0f7SPaolo Bonzini 			ok[i] = false;
86907ce0f7SPaolo Bonzini 			got_drift = true;
87907ce0f7SPaolo Bonzini 			max_delta *= 2;
88907ce0f7SPaolo Bonzini 		}
89907ce0f7SPaolo Bonzini 
90907ce0f7SPaolo Bonzini 		if (now < t && !got_warp) {
91907ce0f7SPaolo Bonzini 			printf("warp on CPU %d!\n", smp_id());
92907ce0f7SPaolo Bonzini 			ok[i] = false;
93907ce0f7SPaolo Bonzini 			got_warp = true;
94907ce0f7SPaolo Bonzini 			break;
95907ce0f7SPaolo Bonzini 		}
96907ce0f7SPaolo Bonzini 		t = now;
97907ce0f7SPaolo Bonzini 	} while(t < end);
98907ce0f7SPaolo Bonzini 
99907ce0f7SPaolo Bonzini 	if (!got_drift)
100907ce0f7SPaolo Bonzini 		printf("delta on CPU %d was %d...%d\n", smp_id(), min_delta, max_delta);
101907ce0f7SPaolo Bonzini 	barrier();
102907ce0f7SPaolo Bonzini 	atomic_dec(&cpus_left);
103907ce0f7SPaolo Bonzini }
104907ce0f7SPaolo Bonzini 
105907ce0f7SPaolo Bonzini static void check_test(int ncpus)
106907ce0f7SPaolo Bonzini {
107907ce0f7SPaolo Bonzini 	int i;
108907ce0f7SPaolo Bonzini 	bool pass;
109907ce0f7SPaolo Bonzini 
110907ce0f7SPaolo Bonzini 	atomic_set(&cpus_left, ncpus);
111907ce0f7SPaolo Bonzini 	for (i = ncpus - 1; i >= 0; i--)
112907ce0f7SPaolo Bonzini 		on_cpu_async(i, hv_clock_test, NULL);
113907ce0f7SPaolo Bonzini 
114907ce0f7SPaolo Bonzini 	/* Wait for the end of other vcpu */
115907ce0f7SPaolo Bonzini 	while(atomic_read(&cpus_left))
116907ce0f7SPaolo Bonzini 		;
117907ce0f7SPaolo Bonzini 
118907ce0f7SPaolo Bonzini 	pass = true;
119907ce0f7SPaolo Bonzini 	for (i = ncpus - 1; i >= 0; i--)
120907ce0f7SPaolo Bonzini 		pass &= ok[i];
121907ce0f7SPaolo Bonzini 
122907ce0f7SPaolo Bonzini 	report("TSC reference precision test", pass);
123907ce0f7SPaolo Bonzini }
124907ce0f7SPaolo Bonzini 
125907ce0f7SPaolo Bonzini static void hv_perf_test(void *data)
126907ce0f7SPaolo Bonzini {
127907ce0f7SPaolo Bonzini 	uint64_t t = hv_clock_read();
128907ce0f7SPaolo Bonzini 	uint64_t end = t + 1000000000 / 100;
129907ce0f7SPaolo Bonzini 	uint64_t local_loops = 0;
130907ce0f7SPaolo Bonzini 
131907ce0f7SPaolo Bonzini 	do {
132907ce0f7SPaolo Bonzini 		t = hv_clock_read();
133907ce0f7SPaolo Bonzini 		local_loops++;
134907ce0f7SPaolo Bonzini 	} while(t < end);
135907ce0f7SPaolo Bonzini 
136907ce0f7SPaolo Bonzini 	loops[smp_id()] = local_loops;
137907ce0f7SPaolo Bonzini 	atomic_dec(&cpus_left);
138907ce0f7SPaolo Bonzini }
139907ce0f7SPaolo Bonzini 
140907ce0f7SPaolo Bonzini static void perf_test(int ncpus)
141907ce0f7SPaolo Bonzini {
142907ce0f7SPaolo Bonzini 	int i;
143907ce0f7SPaolo Bonzini 	uint64_t total_loops;
144907ce0f7SPaolo Bonzini 
145907ce0f7SPaolo Bonzini 	atomic_set(&cpus_left, ncpus);
146907ce0f7SPaolo Bonzini 	for (i = ncpus - 1; i >= 0; i--)
147907ce0f7SPaolo Bonzini 		on_cpu_async(i, hv_perf_test, NULL);
148907ce0f7SPaolo Bonzini 
149907ce0f7SPaolo Bonzini 	/* Wait for the end of other vcpu */
150907ce0f7SPaolo Bonzini 	while(atomic_read(&cpus_left))
151907ce0f7SPaolo Bonzini 		;
152907ce0f7SPaolo Bonzini 
153907ce0f7SPaolo Bonzini 	total_loops = 0;
154907ce0f7SPaolo Bonzini 	for (i = ncpus - 1; i >= 0; i--)
155907ce0f7SPaolo Bonzini 		total_loops += loops[i];
156907ce0f7SPaolo Bonzini 	printf("iterations/sec:  %" PRId64"\n", total_loops / ncpus);
157907ce0f7SPaolo Bonzini }
158907ce0f7SPaolo Bonzini 
159907ce0f7SPaolo Bonzini int main(int ac, char **av)
160907ce0f7SPaolo Bonzini {
161907ce0f7SPaolo Bonzini 	int nerr = 0;
162907ce0f7SPaolo Bonzini 	int ncpus;
163907ce0f7SPaolo Bonzini 	struct hv_reference_tsc_page shadow;
164907ce0f7SPaolo Bonzini 	uint64_t tsc1, t1, tsc2, t2;
165907ce0f7SPaolo Bonzini 	uint64_t ref1, ref2;
166907ce0f7SPaolo Bonzini 
167907ce0f7SPaolo Bonzini 	setup_vm();
168907ce0f7SPaolo Bonzini 	smp_init();
169907ce0f7SPaolo Bonzini 
170907ce0f7SPaolo Bonzini 	hv_clock = alloc_page();
171907ce0f7SPaolo Bonzini 	wrmsr(HV_X64_MSR_REFERENCE_TSC, (u64)(uintptr_t)hv_clock | 1);
172907ce0f7SPaolo Bonzini 	report("MSR value after enabling",
173907ce0f7SPaolo Bonzini 	       rdmsr(HV_X64_MSR_REFERENCE_TSC) == ((u64)(uintptr_t)hv_clock | 1));
174907ce0f7SPaolo Bonzini 
175907ce0f7SPaolo Bonzini 	hvclock_get_time_values(&shadow, hv_clock);
176907ce0f7SPaolo Bonzini 	if (shadow.tsc_sequence == 0 || shadow.tsc_sequence == 0xFFFFFFFF) {
177907ce0f7SPaolo Bonzini 		printf("Reference TSC page not available\n");
178907ce0f7SPaolo Bonzini 		exit(1);
179907ce0f7SPaolo Bonzini 	}
180907ce0f7SPaolo Bonzini 
181907ce0f7SPaolo Bonzini 	printf("scale: %" PRIx64" offset: %" PRId64"\n", shadow.tsc_scale, shadow.tsc_offset);
182907ce0f7SPaolo Bonzini 	ref1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
183907ce0f7SPaolo Bonzini 	tsc1 = rdtsc();
184907ce0f7SPaolo Bonzini 	t1 = hvclock_tsc_to_ticks(&shadow, tsc1);
185907ce0f7SPaolo Bonzini 	printf("refcnt %" PRId64", TSC %" PRIx64", TSC reference %" PRId64"\n",
186907ce0f7SPaolo Bonzini 	       ref1, tsc1, t1);
187907ce0f7SPaolo Bonzini 
188907ce0f7SPaolo Bonzini 	do
189907ce0f7SPaolo Bonzini 		ref2 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
190907ce0f7SPaolo Bonzini 	while (ref2 < ref1 + 2 * TICKS_PER_SEC);
191907ce0f7SPaolo Bonzini 
192907ce0f7SPaolo Bonzini 	tsc2 = rdtsc();
193907ce0f7SPaolo Bonzini 	t2 = hvclock_tsc_to_ticks(&shadow, tsc2);
194907ce0f7SPaolo Bonzini 	printf("refcnt %" PRId64" (delta %" PRId64"), TSC %" PRIx64", "
195907ce0f7SPaolo Bonzini 	       "TSC reference %" PRId64" (delta %" PRId64")\n",
196907ce0f7SPaolo Bonzini 	       ref2, ref2 - ref1, tsc2, t2, t2 - t1);
197907ce0f7SPaolo Bonzini 
198907ce0f7SPaolo Bonzini 	ncpus = cpu_count();
199907ce0f7SPaolo Bonzini 	if (ncpus > MAX_CPU)
200907ce0f7SPaolo Bonzini 		ncpus = MAX_CPU;
201907ce0f7SPaolo Bonzini 
202907ce0f7SPaolo Bonzini 	check_test(ncpus);
203907ce0f7SPaolo Bonzini 	perf_test(ncpus);
204907ce0f7SPaolo Bonzini 
205907ce0f7SPaolo Bonzini 	wrmsr(HV_X64_MSR_REFERENCE_TSC, 0LL);
206907ce0f7SPaolo Bonzini 	report("MSR value after disabling", rdmsr(HV_X64_MSR_REFERENCE_TSC) == 0);
207907ce0f7SPaolo Bonzini 
208907ce0f7SPaolo Bonzini 	return nerr > 0 ? 1 : 0;
209907ce0f7SPaolo Bonzini }
210