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 23 static void test_sigp_sense_running(long destcpu) 24 { 25 smp_sigp(destcpu, SIGP_SENSE_RUNNING, 0, NULL); 26 } 27 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 39 static void test_diag9c(long destcpu) 40 { 41 asm volatile( 42 "diag %[destcpu],0,0x9c" 43 : 44 : [destcpu] "d" (destcpu) 45 ); 46 } 47 48 static long setup_get_this_cpuaddr(long ignore) 49 { 50 return stap(); 51 } 52 53 static void test_diag44(long ignore) 54 { 55 asm volatile( 56 "diag 0,0,0x44" 57 ); 58 } 59 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 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 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 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 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 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 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 132 static void test_stfl(long ignore) 133 { 134 asm volatile( 135 "stfl 0" 136 : 137 : 138 : "memory" 139 ); 140 } 141 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 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 161 static long setup_servc(long arg) 162 { 163 memset(pagebuf, 0, PAGE_SIZE); 164 return arg; 165 } 166 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 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 220 static uint64_t tod_to_us(uint64_t tod) 221 { 222 return tod >> STCK_SHIFT_US; 223 } 224 225 static uint64_t tod_to_ns(uint64_t tod) 226 { 227 return tod_to_us(tod * 1000); 228 } 229 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 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 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