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