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