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 (umount(RESCTRL_PATH)) { 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 (is_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", CBM_MASK_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, malloc_and_init_memory, memflush; 306 unsigned long span, buffer_span; 307 char **benchmark_cmd; 308 char resctrl_val[64]; 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 malloc_and_init_memory = atoi(benchmark_cmd[2]); 325 memflush = atoi(benchmark_cmd[3]); 326 operation = atoi(benchmark_cmd[4]); 327 sprintf(resctrl_val, "%s", benchmark_cmd[5]); 328 329 if (strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) 330 buffer_span = span * MB; 331 else 332 buffer_span = span; 333 334 if (run_fill_buf(buffer_span, malloc_and_init_memory, memflush, 335 operation, resctrl_val)) 336 fprintf(stderr, "Error in running fill buffer\n"); 337 } else { 338 /* Execute specified benchmark */ 339 ret = execvp(benchmark_cmd[0], benchmark_cmd); 340 if (ret) 341 perror("wrong\n"); 342 } 343 344 fclose(stdout); 345 PARENT_EXIT("Unable to run specified benchmark"); 346 } 347 348 /* 349 * create_grp - Create a group only if one doesn't exist 350 * @grp_name: Name of the group 351 * @grp: Full path and name of the group 352 * @parent_grp: Full path and name of the parent group 353 * 354 * Return: 0 on success, non-zero on failure 355 */ 356 static int create_grp(const char *grp_name, char *grp, const char *parent_grp) 357 { 358 int found_grp = 0; 359 struct dirent *ep; 360 DIR *dp; 361 362 /* 363 * At this point, we are guaranteed to have resctrl FS mounted and if 364 * length of grp_name == 0, it means, user wants to use root con_mon 365 * grp, so do nothing 366 */ 367 if (strlen(grp_name) == 0) 368 return 0; 369 370 /* Check if requested grp exists or not */ 371 dp = opendir(parent_grp); 372 if (dp) { 373 while ((ep = readdir(dp)) != NULL) { 374 if (strcmp(ep->d_name, grp_name) == 0) 375 found_grp = 1; 376 } 377 closedir(dp); 378 } else { 379 perror("Unable to open resctrl for group"); 380 381 return -1; 382 } 383 384 /* Requested grp doesn't exist, hence create it */ 385 if (found_grp == 0) { 386 if (mkdir(grp, 0) == -1) { 387 perror("Unable to create group"); 388 389 return -1; 390 } 391 } 392 393 return 0; 394 } 395 396 static int write_pid_to_tasks(char *tasks, pid_t pid) 397 { 398 FILE *fp; 399 400 fp = fopen(tasks, "w"); 401 if (!fp) { 402 perror("Failed to open tasks file"); 403 404 return -1; 405 } 406 if (fprintf(fp, "%d\n", pid) < 0) { 407 perror("Failed to wr pid to tasks file"); 408 fclose(fp); 409 410 return -1; 411 } 412 fclose(fp); 413 414 return 0; 415 } 416 417 /* 418 * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS 419 * @bm_pid: PID that should be written 420 * @ctrlgrp: Name of the control monitor group (con_mon grp) 421 * @mongrp: Name of the monitor group (mon grp) 422 * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc) 423 * 424 * If a con_mon grp is requested, create it and write pid to it, otherwise 425 * write pid to root con_mon grp. 426 * If a mon grp is requested, create it and write pid to it, otherwise 427 * pid is not written, this means that pid is in con_mon grp and hence 428 * should consult con_mon grp's mon_data directory for results. 429 * 430 * Return: 0 on success, non-zero on failure 431 */ 432 int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp, 433 char *resctrl_val) 434 { 435 char controlgroup[128], monitorgroup[512], monitorgroup_p[256]; 436 char tasks[1024]; 437 int ret = 0; 438 439 if (strlen(ctrlgrp)) 440 sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp); 441 else 442 sprintf(controlgroup, "%s", RESCTRL_PATH); 443 444 /* Create control and monitoring group and write pid into it */ 445 ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH); 446 if (ret) 447 goto out; 448 sprintf(tasks, "%s/tasks", controlgroup); 449 ret = write_pid_to_tasks(tasks, bm_pid); 450 if (ret) 451 goto out; 452 453 /* Create mon grp and write pid into it for "mbm" and "cmt" test */ 454 if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)) || 455 !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) { 456 if (strlen(mongrp)) { 457 sprintf(monitorgroup_p, "%s/mon_groups", controlgroup); 458 sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp); 459 ret = create_grp(mongrp, monitorgroup, monitorgroup_p); 460 if (ret) 461 goto out; 462 463 sprintf(tasks, "%s/mon_groups/%s/tasks", 464 controlgroup, mongrp); 465 ret = write_pid_to_tasks(tasks, bm_pid); 466 if (ret) 467 goto out; 468 } 469 } 470 471 out: 472 ksft_print_msg("Writing benchmark parameters to resctrl FS\n"); 473 if (ret) 474 perror("# writing to resctrlfs"); 475 476 return ret; 477 } 478 479 /* 480 * write_schemata - Update schemata of a con_mon grp 481 * @ctrlgrp: Name of the con_mon grp 482 * @schemata: Schemata that should be updated to 483 * @cpu_no: CPU number that the benchmark PID is binded to 484 * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc) 485 * 486 * Update schemata of a con_mon grp *only* if requested resctrl feature is 487 * allocation type 488 * 489 * Return: 0 on success, non-zero on failure 490 */ 491 int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, char *resctrl_val) 492 { 493 char controlgroup[1024], schema[1024], reason[64]; 494 int resource_id, ret = 0; 495 FILE *fp; 496 497 if (strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) && 498 strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) && 499 strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) 500 return -ENOENT; 501 502 if (!schemata) { 503 ksft_print_msg("Skipping empty schemata update\n"); 504 505 return -1; 506 } 507 508 if (get_resource_id(cpu_no, &resource_id) < 0) { 509 sprintf(reason, "Failed to get resource id"); 510 ret = -1; 511 512 goto out; 513 } 514 515 if (strlen(ctrlgrp) != 0) 516 sprintf(controlgroup, "%s/%s/schemata", RESCTRL_PATH, ctrlgrp); 517 else 518 sprintf(controlgroup, "%s/schemata", RESCTRL_PATH); 519 520 if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) || 521 !strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) 522 sprintf(schema, "%s%d%c%s", "L3:", resource_id, '=', schemata); 523 if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_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 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 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 610 */ 611 bool validate_resctrl_feature_request(const char *resctrl_val) 612 { 613 struct stat statbuf; 614 bool found = false; 615 char *res; 616 FILE *inf; 617 618 if (!resctrl_val) 619 return false; 620 621 if (remount_resctrlfs(false)) 622 return false; 623 624 if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) { 625 if (!stat(L3_PATH, &statbuf)) 626 return true; 627 } else if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR))) { 628 if (!stat(MB_PATH, &statbuf)) 629 return true; 630 } else if (!strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR)) || 631 !strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) { 632 if (!stat(L3_MON_PATH, &statbuf)) { 633 inf = fopen(L3_MON_FEATURES_PATH, "r"); 634 if (!inf) 635 return false; 636 637 if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) { 638 res = fgrep(inf, "llc_occupancy"); 639 if (res) { 640 found = true; 641 free(res); 642 } 643 } 644 645 if (!strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) { 646 res = fgrep(inf, "mbm_total_bytes"); 647 if (res) { 648 free(res); 649 res = fgrep(inf, "mbm_local_bytes"); 650 if (res) { 651 found = true; 652 free(res); 653 } 654 } 655 } 656 fclose(inf); 657 } 658 } 659 660 return found; 661 } 662 663 int filter_dmesg(void) 664 { 665 char line[1024]; 666 FILE *fp; 667 int pipefds[2]; 668 pid_t pid; 669 int ret; 670 671 ret = pipe(pipefds); 672 if (ret) { 673 perror("pipe"); 674 return ret; 675 } 676 pid = fork(); 677 if (pid == 0) { 678 close(pipefds[0]); 679 dup2(pipefds[1], STDOUT_FILENO); 680 execlp("dmesg", "dmesg", NULL); 681 perror("executing dmesg"); 682 exit(1); 683 } 684 close(pipefds[1]); 685 fp = fdopen(pipefds[0], "r"); 686 if (!fp) { 687 perror("fdopen(pipe)"); 688 kill(pid, SIGTERM); 689 690 return -1; 691 } 692 693 while (fgets(line, 1024, fp)) { 694 if (strstr(line, "intel_rdt:")) 695 ksft_print_msg("dmesg: %s", line); 696 if (strstr(line, "resctrl:")) 697 ksft_print_msg("dmesg: %s", line); 698 } 699 fclose(fp); 700 waitpid(pid, NULL, 0); 701 702 return 0; 703 } 704 705 int validate_bw_report_request(char *bw_report) 706 { 707 if (strcmp(bw_report, "reads") == 0) 708 return 0; 709 if (strcmp(bw_report, "writes") == 0) 710 return 0; 711 if (strcmp(bw_report, "nt-writes") == 0) { 712 strcpy(bw_report, "writes"); 713 return 0; 714 } 715 if (strcmp(bw_report, "total") == 0) 716 return 0; 717 718 fprintf(stderr, "Requested iMC B/W report type unavailable\n"); 719 720 return -1; 721 } 722 723 int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, 724 int group_fd, unsigned long flags) 725 { 726 int ret; 727 728 ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, 729 group_fd, flags); 730 return ret; 731 } 732 733 unsigned int count_bits(unsigned long n) 734 { 735 unsigned int count = 0; 736 737 while (n) { 738 count += n & 1; 739 n >>= 1; 740 } 741 742 return count; 743 } 744