xref: /kvm-unit-tests/s390x/exittime.c (revision 5bf99cb38621d44b0a7d2204ecd9326b3209ab73)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Measure run time of various instructions. Can be used to find runtime
4  * regressions of instructions which cause exits.
5  *
6  * Copyright IBM Corp. 2022
7  *
8  * Authors:
9  *  Nico Boehr <nrb@linux.ibm.com>
10  */
11 #include <libcflat.h>
12 #include <smp.h>
13 #include <sclp.h>
14 #include <hardware.h>
15 #include <asm/time.h>
16 #include <asm/sigp.h>
17 #include <asm/interrupt.h>
18 #include <asm/page.h>
19 
20 const uint64_t iters_to_normalize_to = 10000;
21 char pagebuf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
22 
test_sigp_sense_running(long destcpu)23 static void test_sigp_sense_running(long destcpu)
24 {
25 	smp_sigp(destcpu, SIGP_SENSE_RUNNING, 0, NULL);
26 }
27 
test_nop(long ignore)28 static void test_nop(long ignore)
29 {
30 	/* nops don't trap into the hypervisor, so let's test them for reference */
31 	asm volatile(
32 		"nop"
33 		:
34 		:
35 		: "memory"
36 	);
37 }
38 
test_diag9c(long destcpu)39 static void test_diag9c(long destcpu)
40 {
41 	asm volatile(
42 		"diag %[destcpu],0,0x9c"
43 		:
44 		: [destcpu] "d" (destcpu)
45 	);
46 }
47 
setup_get_this_cpuaddr(long ignore)48 static long setup_get_this_cpuaddr(long ignore)
49 {
50 	return stap();
51 }
52 
test_diag44(long ignore)53 static void test_diag44(long ignore)
54 {
55 	asm volatile(
56 		"diag 0,0,0x44"
57 	);
58 }
59 
test_stnsm(long ignore)60 static void test_stnsm(long ignore)
61 {
62 	int out;
63 
64 	asm volatile(
65 		"stnsm %[out],0xff"
66 		: [out] "=Q" (out)
67 	);
68 }
69 
test_stosm(long ignore)70 static void test_stosm(long ignore)
71 {
72 	int out;
73 
74 	asm volatile(
75 		"stosm %[out],0"
76 		: [out] "=Q" (out)
77 	);
78 }
79 
setup_ssm(long ignore)80 static long setup_ssm(long ignore)
81 {
82 	long system_mask = 0;
83 
84 	asm volatile(
85 		"stosm %[system_mask],0"
86 		: [system_mask] "=Q" (system_mask)
87 	);
88 
89 	return system_mask;
90 }
91 
test_ssm(long old_system_mask)92 static void test_ssm(long old_system_mask)
93 {
94 	asm volatile(
95 		"ssm %[old_system_mask]"
96 		:
97 		: [old_system_mask] "Q" (old_system_mask)
98 	);
99 }
100 
setup_lctl4(long ignore)101 static long setup_lctl4(long ignore)
102 {
103 	long ctl4_orig = 0;
104 
105 	asm volatile(
106 		"stctg 4,4,%[ctl4_orig]"
107 		: [ctl4_orig] "=S" (ctl4_orig)
108 	);
109 
110 	return ctl4_orig;
111 }
112 
test_lctl4(long ctl4_orig)113 static void test_lctl4(long ctl4_orig)
114 {
115 	asm volatile(
116 		"lctlg 4,4,%[ctl4_orig]"
117 		:
118 		: [ctl4_orig] "S" (ctl4_orig)
119 	);
120 }
121 
test_stpx(long ignore)122 static void test_stpx(long ignore)
123 {
124 	unsigned int prefix;
125 
126 	asm volatile(
127 		"stpx %[prefix]"
128 		: [prefix] "=Q" (prefix)
129 	);
130 }
131 
test_stfl(long ignore)132 static void test_stfl(long ignore)
133 {
134 	asm volatile(
135 		"stfl 0"
136 		:
137 		:
138 		: "memory"
139 	);
140 }
141 
test_epsw(long ignore)142 static void test_epsw(long ignore)
143 {
144 	long r1, r2;
145 
146 	asm volatile(
147 		"epsw %[r1], %[r2]"
148 		: [r1] "=d" (r1), [r2] "=d" (r2)
149 	);
150 }
151 
test_illegal(long ignore)152 static void test_illegal(long ignore)
153 {
154 	expect_pgm_int();
155 	asm volatile(
156 		".word 0"
157 	);
158 	clear_pgm_int();
159 }
160 
setup_servc(long arg)161 static long setup_servc(long arg)
162 {
163 	memset(pagebuf, 0, PAGE_SIZE);
164 	return arg;
165 }
166 
test_servc(long ignore)167 static void test_servc(long ignore)
168 {
169 	SCCB *sccb = (SCCB *) pagebuf;
170 
171 	sccb->h.length = 8;
172 	servc(0, (unsigned long) sccb);
173 }
174 
test_stsi(long fc)175 static void test_stsi(long fc)
176 {
177 	stsi(pagebuf, fc, 2, 2);
178 }
179 
180 struct test {
181 	const char *name;
182 	bool supports_tcg;
183 	/*
184 	 * When non-null, will be called once before running the test loop.
185 	 * Its return value will be given as argument to testfunc.
186 	 */
187 	long (*setupfunc)(long arg);
188 	void (*testfunc)(long arg);
189 	long arg;
190 	long iters;
191 } const exittime_tests[] = {
192 	{"nop",                   true,  NULL,                   test_nop,                0, 200000 },
193 	{"sigp sense running(0)", true,  NULL,                   test_sigp_sense_running, 0, 20000 },
194 	{"sigp sense running(1)", true,  NULL,                   test_sigp_sense_running, 1, 20000 },
195 	{"diag9c(self)",          false, setup_get_this_cpuaddr, test_diag9c,             0, 2000 },
196 	{"diag9c(0)",             false, NULL,                   test_diag9c,             0, 2000 },
197 	{"diag9c(1)",             false, NULL,                   test_diag9c,             1, 2000 },
198 	{"diag44",                true,  NULL,                   test_diag44,             0, 2000 },
199 	{"stnsm",                 true,  NULL,                   test_stnsm,              0, 200000 },
200 	{"stosm",                 true,  NULL,                   test_stosm,              0, 200000 },
201 	{"ssm",                   true,  setup_ssm,              test_ssm,                0, 200000 },
202 	{"lctl4",                 true,  setup_lctl4,            test_lctl4,              0, 20000 },
203 	{"stpx",                  true,  NULL,                   test_stpx,               0, 2000 },
204 	{"stfl",                  true,  NULL,                   test_stfl,               0, 2000 },
205 	{"epsw",                  true,  NULL,                   test_epsw,               0, 20000 },
206 	{"illegal",               true,  NULL,                   test_illegal,            0, 2000 },
207 	{"servc",                 true,  setup_servc,            test_servc,              0, 2000 },
208 	{"stsi122",               true,  NULL,                   test_stsi,               1, 200 },
209 	{"stsi222",               true,  NULL,                   test_stsi,               2, 200 },
210 	{"stsi322",               true,  NULL,                   test_stsi,               3, 200 },
211 };
212 
213 struct test_result {
214 	uint64_t total;
215 	uint64_t best;
216 	uint64_t average;
217 	uint64_t worst;
218 };
219 
tod_to_us(uint64_t tod)220 static uint64_t tod_to_us(uint64_t tod)
221 {
222 	return tod >> STCK_SHIFT_US;
223 }
224 
tod_to_ns(uint64_t tod)225 static uint64_t tod_to_ns(uint64_t tod)
226 {
227 	return tod_to_us(tod * 1000);
228 }
229 
normalize_iters(uint64_t value_to_normalize,uint64_t iters)230 static uint64_t normalize_iters(uint64_t value_to_normalize, uint64_t iters)
231 {
232 	return value_to_normalize * iters_to_normalize_to / iters;
233 }
234 
report_iteration_result(struct test const * test,struct test_result const * test_result)235 static void report_iteration_result(struct test const* test, struct test_result const* test_result)
236 {
237 	uint64_t total = tod_to_ns(normalize_iters(test_result->total, test->iters)),
238 		 best = tod_to_ns(normalize_iters(test_result->best, test->iters)),
239 		 average = tod_to_ns(normalize_iters(test_result->average, test->iters)),
240 		 worst = tod_to_ns(normalize_iters(test_result->worst, test->iters));
241 
242 	report_pass(
243 		"total/best/avg/worst %lu.%03lu/%lu.%03lu/%lu.%03lu/%lu.%03lu us",
244 		total / 1000, total % 1000,
245 		best / 1000, best % 1000,
246 		average / 1000, average % 1000,
247 		worst / 1000, worst % 1000
248 	);
249 }
250 
main(void)251 int main(void)
252 {
253 	int i, j, k, testfunc_arg;
254 	const int outer_iters = 100;
255 	struct test const *current_test;
256 	struct test_result result;
257 	uint64_t start, end, elapsed;
258 
259 	report_prefix_push("exittime");
260 	report_info("reporting total/best/avg/worst normalized to %lu iterations", iters_to_normalize_to);
261 
262 	for (i = 0; i < ARRAY_SIZE(exittime_tests); i++) {
263 		current_test = &exittime_tests[i];
264 		result.total = 0;
265 		result.worst = 0;
266 		result.best = -1;
267 		report_prefix_pushf("%s", current_test->name);
268 
269 		if (host_is_tcg() && !current_test->supports_tcg) {
270 			report_skip("not supported under TCG");
271 			report_prefix_pop();
272 			continue;
273 		}
274 
275 		testfunc_arg = current_test->arg;
276 		if (current_test->setupfunc)
277 			testfunc_arg = current_test->setupfunc(testfunc_arg);
278 
279 		for (j = 0; j < outer_iters; j++) {
280 			stckf(&start);
281 			for (k = 0; k < current_test->iters; k++)
282 				current_test->testfunc(testfunc_arg);
283 			stckf(&end);
284 			elapsed = end - start;
285 			result.best = MIN(result.best, elapsed);
286 			result.worst = MAX(result.worst, elapsed);
287 			result.total += elapsed;
288 		}
289 		result.average = result.total / outer_iters;
290 		report_iteration_result(current_test, &result);
291 		report_prefix_pop();
292 	}
293 
294 	report_prefix_pop();
295 	return report_summary();
296 }
297