1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Basic resctrl file system operations 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 static int find_resctrl_mount(char *buffer) 14 { 15 FILE *mounts; 16 char line[256], *fs, *mntpoint; 17 18 mounts = fopen("/proc/mounts", "r"); 19 if (!mounts) { 20 perror("/proc/mounts"); 21 return -ENXIO; 22 } 23 while (!feof(mounts)) { 24 if (!fgets(line, 256, mounts)) 25 break; 26 fs = strtok(line, " \t"); 27 if (!fs) 28 continue; 29 mntpoint = strtok(NULL, " \t"); 30 if (!mntpoint) 31 continue; 32 fs = strtok(NULL, " \t"); 33 if (!fs) 34 continue; 35 if (strcmp(fs, "resctrl")) 36 continue; 37 38 fclose(mounts); 39 if (buffer) 40 strncpy(buffer, mntpoint, 256); 41 42 return 0; 43 } 44 45 fclose(mounts); 46 47 return -ENOENT; 48 } 49 50 /* 51 * mount_resctrlfs - Mount resctrl FS at /sys/fs/resctrl 52 * 53 * Mounts resctrl FS. Fails if resctrl FS is already mounted to avoid 54 * pre-existing settings interfering with the test results. 55 * 56 * Return: 0 on success, non-zero on failure 57 */ 58 int mount_resctrlfs(void) 59 { 60 int ret; 61 62 ret = find_resctrl_mount(NULL); 63 if (ret != -ENOENT) 64 return -1; 65 66 ksft_print_msg("Mounting resctrl to \"%s\"\n", RESCTRL_PATH); 67 ret = mount("resctrl", RESCTRL_PATH, "resctrl", 0, NULL); 68 if (ret) 69 perror("# mount"); 70 71 return ret; 72 } 73 74 int umount_resctrlfs(void) 75 { 76 char mountpoint[256]; 77 int ret; 78 79 ret = find_resctrl_mount(mountpoint); 80 if (ret == -ENOENT) 81 return 0; 82 if (ret) 83 return ret; 84 85 if (umount(mountpoint)) { 86 perror("# Unable to umount resctrl"); 87 88 return errno; 89 } 90 91 return 0; 92 } 93 94 /* 95 * get_resource_id - Get socket number/l3 id for a specified CPU 96 * @cpu_no: CPU number 97 * @resource_id: Socket number or l3_id 98 * 99 * Return: >= 0 on success, < 0 on failure. 100 */ 101 int get_resource_id(int cpu_no, int *resource_id) 102 { 103 char phys_pkg_path[1024]; 104 FILE *fp; 105 106 if (get_vendor() == ARCH_AMD) 107 sprintf(phys_pkg_path, "%s%d/cache/index3/id", 108 PHYS_ID_PATH, cpu_no); 109 else 110 sprintf(phys_pkg_path, "%s%d/topology/physical_package_id", 111 PHYS_ID_PATH, cpu_no); 112 113 fp = fopen(phys_pkg_path, "r"); 114 if (!fp) { 115 perror("Failed to open physical_package_id"); 116 117 return -1; 118 } 119 if (fscanf(fp, "%d", resource_id) <= 0) { 120 perror("Could not get socket number or l3 id"); 121 fclose(fp); 122 123 return -1; 124 } 125 fclose(fp); 126 127 return 0; 128 } 129 130 /* 131 * get_cache_size - Get cache size for a specified CPU 132 * @cpu_no: CPU number 133 * @cache_type: Cache level L2/L3 134 * @cache_size: pointer to cache_size 135 * 136 * Return: = 0 on success, < 0 on failure. 137 */ 138 int get_cache_size(int cpu_no, char *cache_type, unsigned long *cache_size) 139 { 140 char cache_path[1024], cache_str[64]; 141 int length, i, cache_num; 142 FILE *fp; 143 144 if (!strcmp(cache_type, "L3")) { 145 cache_num = 3; 146 } else if (!strcmp(cache_type, "L2")) { 147 cache_num = 2; 148 } else { 149 perror("Invalid cache level"); 150 return -1; 151 } 152 153 sprintf(cache_path, "/sys/bus/cpu/devices/cpu%d/cache/index%d/size", 154 cpu_no, cache_num); 155 fp = fopen(cache_path, "r"); 156 if (!fp) { 157 perror("Failed to open cache size"); 158 159 return -1; 160 } 161 if (fscanf(fp, "%s", cache_str) <= 0) { 162 perror("Could not get cache_size"); 163 fclose(fp); 164 165 return -1; 166 } 167 fclose(fp); 168 169 length = (int)strlen(cache_str); 170 171 *cache_size = 0; 172 173 for (i = 0; i < length; i++) { 174 if ((cache_str[i] >= '0') && (cache_str[i] <= '9')) 175 176 *cache_size = *cache_size * 10 + (cache_str[i] - '0'); 177 178 else if (cache_str[i] == 'K') 179 180 *cache_size = *cache_size * 1024; 181 182 else if (cache_str[i] == 'M') 183 184 *cache_size = *cache_size * 1024 * 1024; 185 186 else 187 break; 188 } 189 190 return 0; 191 } 192 193 #define CORE_SIBLINGS_PATH "/sys/bus/cpu/devices/cpu" 194 195 /* 196 * get_cbm_mask - Get cbm mask for given cache 197 * @cache_type: Cache level L2/L3 198 * @cbm_mask: cbm_mask returned as a string 199 * 200 * Return: = 0 on success, < 0 on failure. 201 */ 202 int get_cbm_mask(char *cache_type, char *cbm_mask) 203 { 204 char cbm_mask_path[1024]; 205 FILE *fp; 206 207 if (!cbm_mask) 208 return -1; 209 210 sprintf(cbm_mask_path, "%s/%s/cbm_mask", INFO_PATH, cache_type); 211 212 fp = fopen(cbm_mask_path, "r"); 213 if (!fp) { 214 perror("Failed to open cache level"); 215 216 return -1; 217 } 218 if (fscanf(fp, "%s", cbm_mask) <= 0) { 219 perror("Could not get max cbm_mask"); 220 fclose(fp); 221 222 return -1; 223 } 224 fclose(fp); 225 226 return 0; 227 } 228 229 /* 230 * get_core_sibling - Get sibling core id from the same socket for given CPU 231 * @cpu_no: CPU number 232 * 233 * Return: > 0 on success, < 0 on failure. 234 */ 235 int get_core_sibling(int cpu_no) 236 { 237 char core_siblings_path[1024], cpu_list_str[64]; 238 int sibling_cpu_no = -1; 239 FILE *fp; 240 241 sprintf(core_siblings_path, "%s%d/topology/core_siblings_list", 242 CORE_SIBLINGS_PATH, cpu_no); 243 244 fp = fopen(core_siblings_path, "r"); 245 if (!fp) { 246 perror("Failed to open core siblings path"); 247 248 return -1; 249 } 250 if (fscanf(fp, "%s", cpu_list_str) <= 0) { 251 perror("Could not get core_siblings list"); 252 fclose(fp); 253 254 return -1; 255 } 256 fclose(fp); 257 258 char *token = strtok(cpu_list_str, "-,"); 259 260 while (token) { 261 sibling_cpu_no = atoi(token); 262 /* Skipping core 0 as we don't want to run test on core 0 */ 263 if (sibling_cpu_no != 0 && sibling_cpu_no != cpu_no) 264 break; 265 token = strtok(NULL, "-,"); 266 } 267 268 return sibling_cpu_no; 269 } 270 271 /* 272 * taskset_benchmark - Taskset PID (i.e. benchmark) to a specified cpu 273 * @bm_pid: PID that should be binded 274 * @cpu_no: CPU number at which the PID would be binded 275 * 276 * Return: 0 on success, non-zero on failure 277 */ 278 int taskset_benchmark(pid_t bm_pid, int cpu_no) 279 { 280 cpu_set_t my_set; 281 282 CPU_ZERO(&my_set); 283 CPU_SET(cpu_no, &my_set); 284 285 if (sched_setaffinity(bm_pid, sizeof(cpu_set_t), &my_set)) { 286 perror("Unable to taskset benchmark"); 287 288 return -1; 289 } 290 291 return 0; 292 } 293 294 /* 295 * run_benchmark - Run a specified benchmark or fill_buf (default benchmark) 296 * in specified signal. Direct benchmark stdio to /dev/null. 297 * @signum: signal number 298 * @info: signal info 299 * @ucontext: user context in signal handling 300 * 301 * Return: void 302 */ 303 void run_benchmark(int signum, siginfo_t *info, void *ucontext) 304 { 305 int operation, ret, memflush; 306 char **benchmark_cmd; 307 size_t span; 308 bool once; 309 FILE *fp; 310 311 benchmark_cmd = info->si_ptr; 312 313 /* 314 * Direct stdio of child to /dev/null, so that only parent writes to 315 * stdio (console) 316 */ 317 fp = freopen("/dev/null", "w", stdout); 318 if (!fp) 319 PARENT_EXIT("Unable to direct benchmark status to /dev/null"); 320 321 if (strcmp(benchmark_cmd[0], "fill_buf") == 0) { 322 /* Execute default fill_buf benchmark */ 323 span = strtoul(benchmark_cmd[1], NULL, 10); 324 memflush = atoi(benchmark_cmd[2]); 325 operation = atoi(benchmark_cmd[3]); 326 if (!strcmp(benchmark_cmd[4], "true")) 327 once = true; 328 else if (!strcmp(benchmark_cmd[4], "false")) 329 once = false; 330 else 331 PARENT_EXIT("Invalid once parameter"); 332 333 if (run_fill_buf(span, memflush, operation, once)) 334 fprintf(stderr, "Error in running fill buffer\n"); 335 } else { 336 /* Execute specified benchmark */ 337 ret = execvp(benchmark_cmd[0], benchmark_cmd); 338 if (ret) 339 perror("wrong\n"); 340 } 341 342 fclose(stdout); 343 PARENT_EXIT("Unable to run specified benchmark"); 344 } 345 346 /* 347 * create_grp - Create a group only if one doesn't exist 348 * @grp_name: Name of the group 349 * @grp: Full path and name of the group 350 * @parent_grp: Full path and name of the parent group 351 * 352 * Return: 0 on success, non-zero on failure 353 */ 354 static int create_grp(const char *grp_name, char *grp, const char *parent_grp) 355 { 356 int found_grp = 0; 357 struct dirent *ep; 358 DIR *dp; 359 360 /* 361 * At this point, we are guaranteed to have resctrl FS mounted and if 362 * length of grp_name == 0, it means, user wants to use root con_mon 363 * grp, so do nothing 364 */ 365 if (strlen(grp_name) == 0) 366 return 0; 367 368 /* Check if requested grp exists or not */ 369 dp = opendir(parent_grp); 370 if (dp) { 371 while ((ep = readdir(dp)) != NULL) { 372 if (strcmp(ep->d_name, grp_name) == 0) 373 found_grp = 1; 374 } 375 closedir(dp); 376 } else { 377 perror("Unable to open resctrl for group"); 378 379 return -1; 380 } 381 382 /* Requested grp doesn't exist, hence create it */ 383 if (found_grp == 0) { 384 if (mkdir(grp, 0) == -1) { 385 perror("Unable to create group"); 386 387 return -1; 388 } 389 } 390 391 return 0; 392 } 393 394 static int write_pid_to_tasks(char *tasks, pid_t pid) 395 { 396 FILE *fp; 397 398 fp = fopen(tasks, "w"); 399 if (!fp) { 400 perror("Failed to open tasks file"); 401 402 return -1; 403 } 404 if (fprintf(fp, "%d\n", pid) < 0) { 405 perror("Failed to wr pid to tasks file"); 406 fclose(fp); 407 408 return -1; 409 } 410 fclose(fp); 411 412 return 0; 413 } 414 415 /* 416 * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS 417 * @bm_pid: PID that should be written 418 * @ctrlgrp: Name of the control monitor group (con_mon grp) 419 * @mongrp: Name of the monitor group (mon grp) 420 * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc) 421 * 422 * If a con_mon grp is requested, create it and write pid to it, otherwise 423 * write pid to root con_mon grp. 424 * If a mon grp is requested, create it and write pid to it, otherwise 425 * pid is not written, this means that pid is in con_mon grp and hence 426 * should consult con_mon grp's mon_data directory for results. 427 * 428 * Return: 0 on success, non-zero on failure 429 */ 430 int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp, 431 char *resctrl_val) 432 { 433 char controlgroup[128], monitorgroup[512], monitorgroup_p[256]; 434 char tasks[1024]; 435 int ret = 0; 436 437 if (strlen(ctrlgrp)) 438 sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp); 439 else 440 sprintf(controlgroup, "%s", RESCTRL_PATH); 441 442 /* Create control and monitoring group and write pid into it */ 443 ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH); 444 if (ret) 445 goto out; 446 sprintf(tasks, "%s/tasks", controlgroup); 447 ret = write_pid_to_tasks(tasks, bm_pid); 448 if (ret) 449 goto out; 450 451 /* Create mon grp and write pid into it for "mbm" and "cmt" test */ 452 if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)) || 453 !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) { 454 if (strlen(mongrp)) { 455 sprintf(monitorgroup_p, "%s/mon_groups", controlgroup); 456 sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp); 457 ret = create_grp(mongrp, monitorgroup, monitorgroup_p); 458 if (ret) 459 goto out; 460 461 sprintf(tasks, "%s/mon_groups/%s/tasks", 462 controlgroup, mongrp); 463 ret = write_pid_to_tasks(tasks, bm_pid); 464 if (ret) 465 goto out; 466 } 467 } 468 469 out: 470 ksft_print_msg("Writing benchmark parameters to resctrl FS\n"); 471 if (ret) 472 perror("# writing to resctrlfs"); 473 474 return ret; 475 } 476 477 /* 478 * write_schemata - Update schemata of a con_mon grp 479 * @ctrlgrp: Name of the con_mon grp 480 * @schemata: Schemata that should be updated to 481 * @cpu_no: CPU number that the benchmark PID is binded to 482 * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc) 483 * 484 * Update schemata of a con_mon grp *only* if requested resctrl feature is 485 * allocation type 486 * 487 * Return: 0 on success, non-zero on failure 488 */ 489 int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, char *resctrl_val) 490 { 491 char controlgroup[1024], schema[1024], reason[64]; 492 int resource_id, ret = 0; 493 FILE *fp; 494 495 if (strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) && 496 strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR)) && 497 strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) && 498 strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) 499 return -ENOENT; 500 501 if (!schemata) { 502 ksft_print_msg("Skipping empty schemata update\n"); 503 504 return -1; 505 } 506 507 if (get_resource_id(cpu_no, &resource_id) < 0) { 508 sprintf(reason, "Failed to get resource id"); 509 ret = -1; 510 511 goto out; 512 } 513 514 if (strlen(ctrlgrp) != 0) 515 sprintf(controlgroup, "%s/%s/schemata", RESCTRL_PATH, ctrlgrp); 516 else 517 sprintf(controlgroup, "%s/schemata", RESCTRL_PATH); 518 519 if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) || 520 !strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) 521 sprintf(schema, "%s%d%c%s", "L3:", resource_id, '=', schemata); 522 if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) || 523 !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) 524 sprintf(schema, "%s%d%c%s", "MB:", resource_id, '=', schemata); 525 526 fp = fopen(controlgroup, "w"); 527 if (!fp) { 528 sprintf(reason, "Failed to open control group"); 529 ret = -1; 530 531 goto out; 532 } 533 534 if (fprintf(fp, "%s\n", schema) < 0) { 535 sprintf(reason, "Failed to write schemata in control group"); 536 fclose(fp); 537 ret = -1; 538 539 goto out; 540 } 541 fclose(fp); 542 543 out: 544 ksft_print_msg("Write schema \"%s\" to resctrl FS%s%s\n", 545 schema, ret ? " # " : "", 546 ret ? reason : ""); 547 548 return ret; 549 } 550 551 bool check_resctrlfs_support(void) 552 { 553 FILE *inf = fopen("/proc/filesystems", "r"); 554 DIR *dp; 555 char *res; 556 bool ret = false; 557 558 if (!inf) 559 return false; 560 561 res = fgrep(inf, "nodev\tresctrl\n"); 562 563 if (res) { 564 ret = true; 565 free(res); 566 } 567 568 fclose(inf); 569 570 ksft_print_msg("%s Check kernel supports resctrl filesystem\n", 571 ret ? "Pass:" : "Fail:"); 572 573 if (!ret) 574 return ret; 575 576 dp = opendir(RESCTRL_PATH); 577 ksft_print_msg("%s Check resctrl mountpoint \"%s\" exists\n", 578 dp ? "Pass:" : "Fail:", RESCTRL_PATH); 579 if (dp) 580 closedir(dp); 581 582 ksft_print_msg("resctrl filesystem %s mounted\n", 583 find_resctrl_mount(NULL) ? "not" : "is"); 584 585 return ret; 586 } 587 588 char *fgrep(FILE *inf, const char *str) 589 { 590 char line[256]; 591 int slen = strlen(str); 592 593 while (!feof(inf)) { 594 if (!fgets(line, 256, inf)) 595 break; 596 if (strncmp(line, str, slen)) 597 continue; 598 599 return strdup(line); 600 } 601 602 return NULL; 603 } 604 605 /* 606 * validate_resctrl_feature_request - Check if requested feature is valid. 607 * @resctrl_val: Requested feature 608 * 609 * Return: True if the feature is supported, else false. False is also 610 * returned if resctrl FS is not mounted. 611 */ 612 bool validate_resctrl_feature_request(const char *resctrl_val) 613 { 614 struct stat statbuf; 615 bool found = false; 616 char *res; 617 FILE *inf; 618 int ret; 619 620 if (!resctrl_val) 621 return false; 622 623 ret = find_resctrl_mount(NULL); 624 if (ret) 625 return false; 626 627 if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) { 628 if (!stat(L3_PATH, &statbuf)) 629 return true; 630 } else if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR))) { 631 if (!stat(MB_PATH, &statbuf)) 632 return true; 633 } else if (!strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR)) || 634 !strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) { 635 if (!stat(L3_MON_PATH, &statbuf)) { 636 inf = fopen(L3_MON_FEATURES_PATH, "r"); 637 if (!inf) 638 return false; 639 640 if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) { 641 res = fgrep(inf, "llc_occupancy"); 642 if (res) { 643 found = true; 644 free(res); 645 } 646 } 647 648 if (!strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) { 649 res = fgrep(inf, "mbm_total_bytes"); 650 if (res) { 651 free(res); 652 res = fgrep(inf, "mbm_local_bytes"); 653 if (res) { 654 found = true; 655 free(res); 656 } 657 } 658 } 659 fclose(inf); 660 } 661 } 662 663 return found; 664 } 665 666 int filter_dmesg(void) 667 { 668 char line[1024]; 669 FILE *fp; 670 int pipefds[2]; 671 pid_t pid; 672 int ret; 673 674 ret = pipe(pipefds); 675 if (ret) { 676 perror("pipe"); 677 return ret; 678 } 679 fflush(stdout); 680 pid = fork(); 681 if (pid == 0) { 682 close(pipefds[0]); 683 dup2(pipefds[1], STDOUT_FILENO); 684 execlp("dmesg", "dmesg", NULL); 685 perror("executing dmesg"); 686 exit(1); 687 } 688 close(pipefds[1]); 689 fp = fdopen(pipefds[0], "r"); 690 if (!fp) { 691 perror("fdopen(pipe)"); 692 kill(pid, SIGTERM); 693 694 return -1; 695 } 696 697 while (fgets(line, 1024, fp)) { 698 if (strstr(line, "intel_rdt:")) 699 ksft_print_msg("dmesg: %s", line); 700 if (strstr(line, "resctrl:")) 701 ksft_print_msg("dmesg: %s", line); 702 } 703 fclose(fp); 704 waitpid(pid, NULL, 0); 705 706 return 0; 707 } 708 709 int validate_bw_report_request(char *bw_report) 710 { 711 if (strcmp(bw_report, "reads") == 0) 712 return 0; 713 if (strcmp(bw_report, "writes") == 0) 714 return 0; 715 if (strcmp(bw_report, "nt-writes") == 0) { 716 strcpy(bw_report, "writes"); 717 return 0; 718 } 719 if (strcmp(bw_report, "total") == 0) 720 return 0; 721 722 fprintf(stderr, "Requested iMC B/W report type unavailable\n"); 723 724 return -1; 725 } 726 727 int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, 728 int group_fd, unsigned long flags) 729 { 730 int ret; 731 732 ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, 733 group_fd, flags); 734 return ret; 735 } 736 737 unsigned int count_bits(unsigned long n) 738 { 739 unsigned int count = 0; 740 741 while (n) { 742 count += n & 1; 743 n >>= 1; 744 } 745 746 return count; 747 } 748