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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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