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