1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Resctrl tests 4 * 5 * Copyright (C) 2018 Intel Corporation 6 * 7 * Authors: 8 * Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>, 9 * Fenghua Yu <fenghua.yu@intel.com> 10 */ 11 #include "resctrl.h" 12 13 /* Volatile memory sink to prevent compiler optimizations */ 14 static volatile int sink_target; 15 volatile int *value_sink = &sink_target; 16 17 static struct resctrl_test *resctrl_tests[] = { 18 &mbm_test, 19 &mba_test, 20 &cmt_test, 21 &l3_cat_test, 22 &l3_noncont_cat_test, 23 &l2_noncont_cat_test, 24 }; 25 26 static unsigned int detect_vendor(void) 27 { 28 static unsigned int vendor_id; 29 static bool initialized; 30 char *s = NULL; 31 FILE *inf; 32 char *res; 33 34 if (initialized) 35 return vendor_id; 36 37 inf = fopen("/proc/cpuinfo", "r"); 38 if (!inf) { 39 vendor_id = 0; 40 initialized = true; 41 return vendor_id; 42 } 43 44 res = fgrep(inf, "vendor_id"); 45 46 if (res) 47 s = strchr(res, ':'); 48 49 if (s && !strcmp(s, ": GenuineIntel\n")) 50 vendor_id = ARCH_INTEL; 51 else if (s && !strcmp(s, ": AuthenticAMD\n")) 52 vendor_id = ARCH_AMD; 53 else if (s && !strcmp(s, ": HygonGenuine\n")) 54 vendor_id = ARCH_HYGON; 55 56 fclose(inf); 57 free(res); 58 59 initialized = true; 60 return vendor_id; 61 } 62 63 unsigned int get_vendor(void) 64 { 65 unsigned int vendor; 66 67 vendor = detect_vendor(); 68 69 if (vendor == 0) 70 ksft_print_msg("Can not get vendor info...\n"); 71 72 return vendor; 73 } 74 75 static void cmd_help(void) 76 { 77 int i; 78 79 printf("usage: resctrl_tests [-h] [-t test list] [-n no_of_bits] [-b benchmark_cmd [option]...]\n"); 80 printf("\t-b benchmark_cmd [option]...: run specified benchmark for MBM, MBA and CMT\n"); 81 printf("\t default benchmark is builtin fill_buf\n"); 82 printf("\t-t test list: run tests/groups specified by the list, "); 83 printf("e.g. -t mbm,mba,cmt,cat\n"); 84 printf("\t\tSupported tests (group):\n"); 85 for (i = 0; i < ARRAY_SIZE(resctrl_tests); i++) { 86 if (resctrl_tests[i]->group) 87 printf("\t\t\t%s (%s)\n", resctrl_tests[i]->name, resctrl_tests[i]->group); 88 else 89 printf("\t\t\t%s\n", resctrl_tests[i]->name); 90 } 91 printf("\t-n no_of_bits: run cache tests using specified no of bits in cache bit mask\n"); 92 printf("\t-p cpu_no: specify CPU number to run the test. 1 is default\n"); 93 printf("\t-h: help\n"); 94 } 95 96 static int test_prepare(const struct resctrl_test *test) 97 { 98 int res; 99 100 res = signal_handler_register(test); 101 if (res) { 102 ksft_print_msg("Failed to register signal handler\n"); 103 return res; 104 } 105 106 res = mount_resctrlfs(); 107 if (res) { 108 signal_handler_unregister(); 109 ksft_print_msg("Failed to mount resctrl FS\n"); 110 return res; 111 } 112 return 0; 113 } 114 115 static void test_cleanup(const struct resctrl_test *test) 116 { 117 if (test->cleanup) 118 test->cleanup(); 119 umount_resctrlfs(); 120 signal_handler_unregister(); 121 } 122 123 static bool test_vendor_specific_check(const struct resctrl_test *test) 124 { 125 if (!test->vendor_specific) 126 return true; 127 128 return get_vendor() & test->vendor_specific; 129 } 130 131 static void run_single_test(const struct resctrl_test *test, const struct user_params *uparams) 132 { 133 int ret, snc_mode; 134 135 if (test->disabled) 136 return; 137 138 if (!test_vendor_specific_check(test)) { 139 ksft_test_result_skip("Hardware does not support %s\n", test->name); 140 return; 141 } 142 143 snc_mode = snc_nodes_per_l3_cache(); 144 145 ksft_print_msg("Starting %s test ...\n", test->name); 146 147 if (snc_mode == 1 && snc_unreliable && get_vendor() == ARCH_INTEL) { 148 ksft_test_result_skip("SNC detection unreliable due to offline CPUs. Test results may not be accurate if SNC enabled.\n"); 149 return; 150 } 151 152 if (test_prepare(test)) { 153 ksft_exit_fail_msg("Abnormal failure when preparing for the test\n"); 154 return; 155 } 156 157 if (!test->feature_check(test)) { 158 ksft_test_result_skip("Hardware does not support %s or %s is disabled\n", 159 test->name, test->name); 160 goto cleanup; 161 } 162 163 ret = test->run_test(test, uparams); 164 ksft_test_result(!ret, "%s: test\n", test->name); 165 166 cleanup: 167 test_cleanup(test); 168 } 169 170 /* 171 * Allocate and initialize a struct fill_buf_param with user provided 172 * (via "-b fill_buf <fill_buf parameters>") parameters. 173 * 174 * Use defaults (that may not be appropriate for all tests) for any 175 * fill_buf parameters omitted by the user. 176 * 177 * Historically it may have been possible for user space to provide 178 * additional parameters, "operation" ("read" vs "write") in 179 * benchmark_cmd[3] and "once" (run "once" or until terminated) in 180 * benchmark_cmd[4]. Changing these parameters have never been 181 * supported with the default of "read" operation and running until 182 * terminated built into the tests. Any unsupported values for 183 * (original) "fill_buf" parameters are treated as failure. 184 * 185 * Return: On failure, forcibly exits the test on any parsing failure, 186 * returns NULL if no parsing needed (user did not actually provide 187 * "-b fill_buf"). 188 * On success, returns pointer to newly allocated and fully 189 * initialized struct fill_buf_param that caller must free. 190 */ 191 static struct fill_buf_param *alloc_fill_buf_param(struct user_params *uparams) 192 { 193 struct fill_buf_param *fill_param = NULL; 194 char *endptr = NULL; 195 196 if (!uparams->benchmark_cmd[0] || strcmp(uparams->benchmark_cmd[0], "fill_buf")) 197 return NULL; 198 199 fill_param = malloc(sizeof(*fill_param)); 200 if (!fill_param) 201 ksft_exit_skip("Unable to allocate memory for fill_buf parameters.\n"); 202 203 if (uparams->benchmark_cmd[1] && *uparams->benchmark_cmd[1] != '\0') { 204 errno = 0; 205 fill_param->buf_size = strtoul(uparams->benchmark_cmd[1], &endptr, 10); 206 if (errno || *endptr != '\0') { 207 free(fill_param); 208 ksft_exit_skip("Unable to parse benchmark buffer size.\n"); 209 } 210 } else { 211 fill_param->buf_size = MINIMUM_SPAN; 212 } 213 214 if (uparams->benchmark_cmd[2] && *uparams->benchmark_cmd[2] != '\0') { 215 errno = 0; 216 fill_param->memflush = strtol(uparams->benchmark_cmd[2], &endptr, 10) != 0; 217 if (errno || *endptr != '\0') { 218 free(fill_param); 219 ksft_exit_skip("Unable to parse benchmark memflush parameter.\n"); 220 } 221 } else { 222 fill_param->memflush = true; 223 } 224 225 if (uparams->benchmark_cmd[3] && *uparams->benchmark_cmd[3] != '\0') { 226 if (strcmp(uparams->benchmark_cmd[3], "0")) { 227 free(fill_param); 228 ksft_exit_skip("Only read operations supported.\n"); 229 } 230 } 231 232 if (uparams->benchmark_cmd[4] && *uparams->benchmark_cmd[4] != '\0') { 233 if (strcmp(uparams->benchmark_cmd[4], "false")) { 234 free(fill_param); 235 ksft_exit_skip("fill_buf is required to run until termination.\n"); 236 } 237 } 238 239 return fill_param; 240 } 241 242 static void init_user_params(struct user_params *uparams) 243 { 244 memset(uparams, 0, sizeof(*uparams)); 245 246 uparams->cpu = 1; 247 uparams->bits = 0; 248 } 249 250 int main(int argc, char **argv) 251 { 252 struct fill_buf_param *fill_param = NULL; 253 int tests = ARRAY_SIZE(resctrl_tests); 254 bool test_param_seen = false; 255 struct user_params uparams; 256 int c, i; 257 258 init_user_params(&uparams); 259 260 while ((c = getopt(argc, argv, "ht:b:n:p:")) != -1) { 261 char *token; 262 263 switch (c) { 264 case 'b': 265 /* 266 * First move optind back to the (first) optarg and 267 * then build the benchmark command using the 268 * remaining arguments. 269 */ 270 optind--; 271 if (argc - optind >= BENCHMARK_ARGS) 272 ksft_exit_fail_msg("Too long benchmark command"); 273 274 /* Extract benchmark command from command line. */ 275 for (i = 0; i < argc - optind; i++) 276 uparams.benchmark_cmd[i] = argv[i + optind]; 277 uparams.benchmark_cmd[i] = NULL; 278 279 goto last_arg; 280 case 't': 281 token = strtok(optarg, ","); 282 283 if (!test_param_seen) { 284 for (i = 0; i < ARRAY_SIZE(resctrl_tests); i++) 285 resctrl_tests[i]->disabled = true; 286 tests = 0; 287 test_param_seen = true; 288 } 289 while (token) { 290 bool found = false; 291 292 for (i = 0; i < ARRAY_SIZE(resctrl_tests); i++) { 293 if (!strcasecmp(token, resctrl_tests[i]->name) || 294 (resctrl_tests[i]->group && 295 !strcasecmp(token, resctrl_tests[i]->group))) { 296 if (resctrl_tests[i]->disabled) 297 tests++; 298 resctrl_tests[i]->disabled = false; 299 found = true; 300 } 301 } 302 303 if (!found) { 304 printf("invalid test: %s\n", token); 305 306 return -1; 307 } 308 token = strtok(NULL, ","); 309 } 310 break; 311 case 'p': 312 uparams.cpu = atoi(optarg); 313 break; 314 case 'n': 315 uparams.bits = atoi(optarg); 316 if (uparams.bits <= 0) { 317 printf("Bail out! invalid argument for no_of_bits\n"); 318 return -1; 319 } 320 break; 321 case 'h': 322 cmd_help(); 323 324 return 0; 325 default: 326 printf("invalid argument\n"); 327 328 return -1; 329 } 330 } 331 last_arg: 332 333 fill_param = alloc_fill_buf_param(&uparams); 334 if (fill_param) 335 uparams.fill_buf = fill_param; 336 337 ksft_print_header(); 338 339 /* 340 * Typically we need root privileges, because: 341 * 1. We write to resctrl FS 342 * 2. We execute perf commands 343 */ 344 if (geteuid() != 0) 345 ksft_exit_skip("Not running as root. Skipping...\n"); 346 347 if (!check_resctrlfs_support()) 348 ksft_exit_skip("resctrl FS does not exist. Enable X86_CPU_RESCTRL config option.\n"); 349 350 if (umount_resctrlfs()) 351 ksft_exit_skip("resctrl FS unmount failed.\n"); 352 353 filter_dmesg(); 354 355 ksft_set_plan(tests); 356 357 for (i = 0; i < ARRAY_SIZE(resctrl_tests); i++) 358 run_single_test(resctrl_tests[i], &uparams); 359 360 free(fill_param); 361 ksft_finished(); 362 } 363