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 <fcntl.h> 12 #include <limits.h> 13 14 #include "resctrl.h" 15 16 static int find_resctrl_mount(char *buffer) 17 { 18 FILE *mounts; 19 char line[256], *fs, *mntpoint; 20 21 mounts = fopen("/proc/mounts", "r"); 22 if (!mounts) { 23 perror("/proc/mounts"); 24 return -ENXIO; 25 } 26 while (!feof(mounts)) { 27 if (!fgets(line, 256, mounts)) 28 break; 29 fs = strtok(line, " \t"); 30 if (!fs) 31 continue; 32 mntpoint = strtok(NULL, " \t"); 33 if (!mntpoint) 34 continue; 35 fs = strtok(NULL, " \t"); 36 if (!fs) 37 continue; 38 if (strcmp(fs, "resctrl")) 39 continue; 40 41 fclose(mounts); 42 if (buffer) 43 strncpy(buffer, mntpoint, 256); 44 45 return 0; 46 } 47 48 fclose(mounts); 49 50 return -ENOENT; 51 } 52 53 /* 54 * mount_resctrlfs - Mount resctrl FS at /sys/fs/resctrl 55 * 56 * Mounts resctrl FS. Fails if resctrl FS is already mounted to avoid 57 * pre-existing settings interfering with the test results. 58 * 59 * Return: 0 on success, non-zero on failure 60 */ 61 int mount_resctrlfs(void) 62 { 63 int ret; 64 65 ret = find_resctrl_mount(NULL); 66 if (ret != -ENOENT) 67 return -1; 68 69 ksft_print_msg("Mounting resctrl to \"%s\"\n", RESCTRL_PATH); 70 ret = mount("resctrl", RESCTRL_PATH, "resctrl", 0, NULL); 71 if (ret) 72 perror("# mount"); 73 74 return ret; 75 } 76 77 int umount_resctrlfs(void) 78 { 79 char mountpoint[256]; 80 int ret; 81 82 ret = find_resctrl_mount(mountpoint); 83 if (ret == -ENOENT) 84 return 0; 85 if (ret) 86 return ret; 87 88 if (umount(mountpoint)) { 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", INFO_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 * create_grp - Create a group only if one doesn't exist 299 * @grp_name: Name of the group 300 * @grp: Full path and name of the group 301 * @parent_grp: Full path and name of the parent group 302 * 303 * Return: 0 on success, non-zero on failure 304 */ 305 static int create_grp(const char *grp_name, char *grp, const char *parent_grp) 306 { 307 int found_grp = 0; 308 struct dirent *ep; 309 DIR *dp; 310 311 /* 312 * At this point, we are guaranteed to have resctrl FS mounted and if 313 * length of grp_name == 0, it means, user wants to use root con_mon 314 * grp, so do nothing 315 */ 316 if (strlen(grp_name) == 0) 317 return 0; 318 319 /* Check if requested grp exists or not */ 320 dp = opendir(parent_grp); 321 if (dp) { 322 while ((ep = readdir(dp)) != NULL) { 323 if (strcmp(ep->d_name, grp_name) == 0) 324 found_grp = 1; 325 } 326 closedir(dp); 327 } else { 328 perror("Unable to open resctrl for group"); 329 330 return -1; 331 } 332 333 /* Requested grp doesn't exist, hence create it */ 334 if (found_grp == 0) { 335 if (mkdir(grp, 0) == -1) { 336 perror("Unable to create group"); 337 338 return -1; 339 } 340 } 341 342 return 0; 343 } 344 345 static int write_pid_to_tasks(char *tasks, pid_t pid) 346 { 347 FILE *fp; 348 349 fp = fopen(tasks, "w"); 350 if (!fp) { 351 perror("Failed to open tasks file"); 352 353 return -1; 354 } 355 if (fprintf(fp, "%d\n", pid) < 0) { 356 perror("Failed to wr pid to tasks file"); 357 fclose(fp); 358 359 return -1; 360 } 361 fclose(fp); 362 363 return 0; 364 } 365 366 /* 367 * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS 368 * @bm_pid: PID that should be written 369 * @ctrlgrp: Name of the control monitor group (con_mon grp) 370 * @mongrp: Name of the monitor group (mon grp) 371 * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc) 372 * 373 * If a con_mon grp is requested, create it and write pid to it, otherwise 374 * write pid to root con_mon grp. 375 * If a mon grp is requested, create it and write pid to it, otherwise 376 * pid is not written, this means that pid is in con_mon grp and hence 377 * should consult con_mon grp's mon_data directory for results. 378 * 379 * Return: 0 on success, non-zero on failure 380 */ 381 int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp, 382 char *resctrl_val) 383 { 384 char controlgroup[128], monitorgroup[512], monitorgroup_p[256]; 385 char tasks[1024]; 386 int ret = 0; 387 388 if (strlen(ctrlgrp)) 389 sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp); 390 else 391 sprintf(controlgroup, "%s", RESCTRL_PATH); 392 393 /* Create control and monitoring group and write pid into it */ 394 ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH); 395 if (ret) 396 goto out; 397 sprintf(tasks, "%s/tasks", controlgroup); 398 ret = write_pid_to_tasks(tasks, bm_pid); 399 if (ret) 400 goto out; 401 402 /* Create mon grp and write pid into it for "mbm" and "cmt" test */ 403 if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)) || 404 !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) { 405 if (strlen(mongrp)) { 406 sprintf(monitorgroup_p, "%s/mon_groups", controlgroup); 407 sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp); 408 ret = create_grp(mongrp, monitorgroup, monitorgroup_p); 409 if (ret) 410 goto out; 411 412 sprintf(tasks, "%s/mon_groups/%s/tasks", 413 controlgroup, mongrp); 414 ret = write_pid_to_tasks(tasks, bm_pid); 415 if (ret) 416 goto out; 417 } 418 } 419 420 out: 421 ksft_print_msg("Writing benchmark parameters to resctrl FS\n"); 422 if (ret) 423 perror("# writing to resctrlfs"); 424 425 return ret; 426 } 427 428 /* 429 * write_schemata - Update schemata of a con_mon grp 430 * @ctrlgrp: Name of the con_mon grp 431 * @schemata: Schemata that should be updated to 432 * @cpu_no: CPU number that the benchmark PID is binded to 433 * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc) 434 * 435 * Update schemata of a con_mon grp *only* if requested resctrl feature is 436 * allocation type 437 * 438 * Return: 0 on success, non-zero on failure 439 */ 440 int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, char *resctrl_val) 441 { 442 char controlgroup[1024], reason[128], schema[1024] = {}; 443 int resource_id, fd, schema_len = -1, ret = 0; 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 schema_len = snprintf(schema, sizeof(schema), "%s%d%c%s\n", 472 "L3:", resource_id, '=', schemata); 473 if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) || 474 !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) 475 schema_len = snprintf(schema, sizeof(schema), "%s%d%c%s\n", 476 "MB:", resource_id, '=', schemata); 477 if (schema_len < 0 || schema_len >= sizeof(schema)) { 478 snprintf(reason, sizeof(reason), 479 "snprintf() failed with return value : %d", schema_len); 480 ret = -1; 481 goto out; 482 } 483 484 fd = open(controlgroup, O_WRONLY); 485 if (fd < 0) { 486 snprintf(reason, sizeof(reason), 487 "open() failed : %s", strerror(errno)); 488 ret = -1; 489 490 goto err_schema_not_empty; 491 } 492 if (write(fd, schema, schema_len) < 0) { 493 snprintf(reason, sizeof(reason), 494 "write() failed : %s", strerror(errno)); 495 close(fd); 496 ret = -1; 497 498 goto err_schema_not_empty; 499 } 500 close(fd); 501 502 err_schema_not_empty: 503 schema[schema_len - 1] = 0; 504 out: 505 ksft_print_msg("Write schema \"%s\" to resctrl FS%s%s\n", 506 schema, ret ? " # " : "", 507 ret ? reason : ""); 508 509 return ret; 510 } 511 512 bool check_resctrlfs_support(void) 513 { 514 FILE *inf = fopen("/proc/filesystems", "r"); 515 DIR *dp; 516 char *res; 517 bool ret = false; 518 519 if (!inf) 520 return false; 521 522 res = fgrep(inf, "nodev\tresctrl\n"); 523 524 if (res) { 525 ret = true; 526 free(res); 527 } 528 529 fclose(inf); 530 531 ksft_print_msg("%s Check kernel supports resctrl filesystem\n", 532 ret ? "Pass:" : "Fail:"); 533 534 if (!ret) 535 return ret; 536 537 dp = opendir(RESCTRL_PATH); 538 ksft_print_msg("%s Check resctrl mountpoint \"%s\" exists\n", 539 dp ? "Pass:" : "Fail:", RESCTRL_PATH); 540 if (dp) 541 closedir(dp); 542 543 ksft_print_msg("resctrl filesystem %s mounted\n", 544 find_resctrl_mount(NULL) ? "not" : "is"); 545 546 return ret; 547 } 548 549 char *fgrep(FILE *inf, const char *str) 550 { 551 char line[256]; 552 int slen = strlen(str); 553 554 while (!feof(inf)) { 555 if (!fgets(line, 256, inf)) 556 break; 557 if (strncmp(line, str, slen)) 558 continue; 559 560 return strdup(line); 561 } 562 563 return NULL; 564 } 565 566 /* 567 * validate_resctrl_feature_request - Check if requested feature is valid. 568 * @resource: Required resource (e.g., MB, L3, L2, L3_MON, etc.) 569 * @feature: Required monitor feature (in mon_features file). Can only be 570 * set for L3_MON. Must be NULL for all other resources. 571 * 572 * Return: True if the resource/feature is supported, else false. False is 573 * also returned if resctrl FS is not mounted. 574 */ 575 bool validate_resctrl_feature_request(const char *resource, const char *feature) 576 { 577 char res_path[PATH_MAX]; 578 struct stat statbuf; 579 char *res; 580 FILE *inf; 581 int ret; 582 583 if (!resource) 584 return false; 585 586 ret = find_resctrl_mount(NULL); 587 if (ret) 588 return false; 589 590 snprintf(res_path, sizeof(res_path), "%s/%s", INFO_PATH, resource); 591 592 if (stat(res_path, &statbuf)) 593 return false; 594 595 if (!feature) 596 return true; 597 598 snprintf(res_path, sizeof(res_path), "%s/%s/mon_features", INFO_PATH, resource); 599 inf = fopen(res_path, "r"); 600 if (!inf) 601 return false; 602 603 res = fgrep(inf, feature); 604 free(res); 605 fclose(inf); 606 607 return !!res; 608 } 609 610 int filter_dmesg(void) 611 { 612 char line[1024]; 613 FILE *fp; 614 int pipefds[2]; 615 pid_t pid; 616 int ret; 617 618 ret = pipe(pipefds); 619 if (ret) { 620 perror("pipe"); 621 return ret; 622 } 623 fflush(stdout); 624 pid = fork(); 625 if (pid == 0) { 626 close(pipefds[0]); 627 dup2(pipefds[1], STDOUT_FILENO); 628 execlp("dmesg", "dmesg", NULL); 629 perror("executing dmesg"); 630 exit(1); 631 } 632 close(pipefds[1]); 633 fp = fdopen(pipefds[0], "r"); 634 if (!fp) { 635 perror("fdopen(pipe)"); 636 kill(pid, SIGTERM); 637 638 return -1; 639 } 640 641 while (fgets(line, 1024, fp)) { 642 if (strstr(line, "intel_rdt:")) 643 ksft_print_msg("dmesg: %s", line); 644 if (strstr(line, "resctrl:")) 645 ksft_print_msg("dmesg: %s", line); 646 } 647 fclose(fp); 648 waitpid(pid, NULL, 0); 649 650 return 0; 651 } 652 653 int validate_bw_report_request(char *bw_report) 654 { 655 if (strcmp(bw_report, "reads") == 0) 656 return 0; 657 if (strcmp(bw_report, "writes") == 0) 658 return 0; 659 if (strcmp(bw_report, "nt-writes") == 0) { 660 strcpy(bw_report, "writes"); 661 return 0; 662 } 663 if (strcmp(bw_report, "total") == 0) 664 return 0; 665 666 fprintf(stderr, "Requested iMC B/W report type unavailable\n"); 667 668 return -1; 669 } 670 671 int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, 672 int group_fd, unsigned long flags) 673 { 674 int ret; 675 676 ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, 677 group_fd, flags); 678 return ret; 679 } 680 681 unsigned int count_bits(unsigned long n) 682 { 683 unsigned int count = 0; 684 685 while (n) { 686 count += n & 1; 687 n >>= 1; 688 } 689 690 return count; 691 } 692