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