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