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 ksft_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, < 0 on error. 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 ksft_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 ksft_perror("Unable to umount resctrl"); 90 91 return -1; 92 } 93 94 return 0; 95 } 96 97 /* 98 * get_cache_level - Convert cache level from string to integer 99 * @cache_type: Cache level as string 100 * 101 * Return: cache level as integer or -1 if @cache_type is invalid. 102 */ 103 static int get_cache_level(const char *cache_type) 104 { 105 if (!strcmp(cache_type, "L3")) 106 return 3; 107 if (!strcmp(cache_type, "L2")) 108 return 2; 109 110 ksft_print_msg("Invalid cache level\n"); 111 return -1; 112 } 113 114 static int get_resource_cache_level(const char *resource) 115 { 116 /* "MB" use L3 (LLC) as resource */ 117 if (!strcmp(resource, "MB")) 118 return 3; 119 return get_cache_level(resource); 120 } 121 122 /* 123 * get_domain_id - Get resctrl domain ID for a specified CPU 124 * @resource: resource name 125 * @cpu_no: CPU number 126 * @domain_id: domain ID (cache ID; for MB, L3 cache ID) 127 * 128 * Return: >= 0 on success, < 0 on failure. 129 */ 130 int get_domain_id(const char *resource, int cpu_no, int *domain_id) 131 { 132 char phys_pkg_path[1024]; 133 int cache_num; 134 FILE *fp; 135 136 cache_num = get_resource_cache_level(resource); 137 if (cache_num < 0) 138 return cache_num; 139 140 sprintf(phys_pkg_path, "%s%d/cache/index%d/id", PHYS_ID_PATH, cpu_no, cache_num); 141 142 fp = fopen(phys_pkg_path, "r"); 143 if (!fp) { 144 ksft_perror("Failed to open cache id file"); 145 146 return -1; 147 } 148 if (fscanf(fp, "%d", domain_id) <= 0) { 149 ksft_perror("Could not get domain ID"); 150 fclose(fp); 151 152 return -1; 153 } 154 fclose(fp); 155 156 return 0; 157 } 158 159 /* 160 * get_cache_size - Get cache size for a specified CPU 161 * @cpu_no: CPU number 162 * @cache_type: Cache level L2/L3 163 * @cache_size: pointer to cache_size 164 * 165 * Return: = 0 on success, < 0 on failure. 166 */ 167 int get_cache_size(int cpu_no, const char *cache_type, unsigned long *cache_size) 168 { 169 char cache_path[1024], cache_str[64]; 170 int length, i, cache_num; 171 FILE *fp; 172 173 cache_num = get_cache_level(cache_type); 174 if (cache_num < 0) 175 return cache_num; 176 177 sprintf(cache_path, "/sys/bus/cpu/devices/cpu%d/cache/index%d/size", 178 cpu_no, cache_num); 179 fp = fopen(cache_path, "r"); 180 if (!fp) { 181 ksft_perror("Failed to open cache size"); 182 183 return -1; 184 } 185 if (fscanf(fp, "%s", cache_str) <= 0) { 186 ksft_perror("Could not get cache_size"); 187 fclose(fp); 188 189 return -1; 190 } 191 fclose(fp); 192 193 length = (int)strlen(cache_str); 194 195 *cache_size = 0; 196 197 for (i = 0; i < length; i++) { 198 if ((cache_str[i] >= '0') && (cache_str[i] <= '9')) 199 200 *cache_size = *cache_size * 10 + (cache_str[i] - '0'); 201 202 else if (cache_str[i] == 'K') 203 204 *cache_size = *cache_size * 1024; 205 206 else if (cache_str[i] == 'M') 207 208 *cache_size = *cache_size * 1024 * 1024; 209 210 else 211 break; 212 } 213 214 return 0; 215 } 216 217 #define CORE_SIBLINGS_PATH "/sys/bus/cpu/devices/cpu" 218 219 /* 220 * get_bit_mask - Get bit mask from given file 221 * @filename: File containing the mask 222 * @mask: The bit mask returned as unsigned long 223 * 224 * Return: = 0 on success, < 0 on failure. 225 */ 226 static int get_bit_mask(const char *filename, unsigned long *mask) 227 { 228 FILE *fp; 229 230 if (!filename || !mask) 231 return -1; 232 233 fp = fopen(filename, "r"); 234 if (!fp) { 235 ksft_print_msg("Failed to open bit mask file '%s': %s\n", 236 filename, strerror(errno)); 237 return -1; 238 } 239 240 if (fscanf(fp, "%lx", mask) <= 0) { 241 ksft_print_msg("Could not read bit mask file '%s': %s\n", 242 filename, strerror(errno)); 243 fclose(fp); 244 245 return -1; 246 } 247 fclose(fp); 248 249 return 0; 250 } 251 252 /* 253 * resource_info_unsigned_get - Read an unsigned value from 254 * /sys/fs/resctrl/info/@resource/@filename 255 * @resource: Resource name that matches directory name in 256 * /sys/fs/resctrl/info 257 * @filename: File in /sys/fs/resctrl/info/@resource 258 * @val: Contains read value on success. 259 * 260 * Return: = 0 on success, < 0 on failure. On success the read 261 * value is saved into @val. 262 */ 263 int resource_info_unsigned_get(const char *resource, const char *filename, 264 unsigned int *val) 265 { 266 char file_path[PATH_MAX]; 267 FILE *fp; 268 269 snprintf(file_path, sizeof(file_path), "%s/%s/%s", INFO_PATH, resource, 270 filename); 271 272 fp = fopen(file_path, "r"); 273 if (!fp) { 274 ksft_print_msg("Error opening %s: %m\n", file_path); 275 return -1; 276 } 277 278 if (fscanf(fp, "%u", val) <= 0) { 279 ksft_print_msg("Could not get contents of %s: %m\n", file_path); 280 fclose(fp); 281 return -1; 282 } 283 284 fclose(fp); 285 return 0; 286 } 287 288 /* 289 * create_bit_mask- Create bit mask from start, len pair 290 * @start: LSB of the mask 291 * @len Number of bits in the mask 292 */ 293 unsigned long create_bit_mask(unsigned int start, unsigned int len) 294 { 295 return ((1UL << len) - 1UL) << start; 296 } 297 298 /* 299 * count_contiguous_bits - Returns the longest train of bits in a bit mask 300 * @val A bit mask 301 * @start The location of the least-significant bit of the longest train 302 * 303 * Return: The length of the contiguous bits in the longest train of bits 304 */ 305 unsigned int count_contiguous_bits(unsigned long val, unsigned int *start) 306 { 307 unsigned long last_val; 308 unsigned int count = 0; 309 310 while (val) { 311 last_val = val; 312 val &= (val >> 1); 313 count++; 314 } 315 316 if (start) { 317 if (count) 318 *start = ffsl(last_val) - 1; 319 else 320 *start = 0; 321 } 322 323 return count; 324 } 325 326 /* 327 * get_full_cbm - Get full Cache Bit Mask (CBM) 328 * @cache_type: Cache type as "L2" or "L3" 329 * @mask: Full cache bit mask representing the maximal portion of cache 330 * available for allocation, returned as unsigned long. 331 * 332 * Return: = 0 on success, < 0 on failure. 333 */ 334 int get_full_cbm(const char *cache_type, unsigned long *mask) 335 { 336 char cbm_path[PATH_MAX]; 337 int ret; 338 339 if (!cache_type) 340 return -1; 341 342 snprintf(cbm_path, sizeof(cbm_path), "%s/%s/cbm_mask", 343 INFO_PATH, cache_type); 344 345 ret = get_bit_mask(cbm_path, mask); 346 if (ret || !*mask) 347 return -1; 348 349 return 0; 350 } 351 352 /* 353 * get_shareable_mask - Get shareable mask from shareable_bits 354 * @cache_type: Cache type as "L2" or "L3" 355 * @shareable_mask: Shareable mask returned as unsigned long 356 * 357 * Return: = 0 on success, < 0 on failure. 358 */ 359 static int get_shareable_mask(const char *cache_type, unsigned long *shareable_mask) 360 { 361 char mask_path[PATH_MAX]; 362 363 if (!cache_type) 364 return -1; 365 366 snprintf(mask_path, sizeof(mask_path), "%s/%s/shareable_bits", 367 INFO_PATH, cache_type); 368 369 return get_bit_mask(mask_path, shareable_mask); 370 } 371 372 /* 373 * get_mask_no_shareable - Get Cache Bit Mask (CBM) without shareable bits 374 * @cache_type: Cache type as "L2" or "L3" 375 * @mask: The largest exclusive portion of the cache out of the 376 * full CBM, returned as unsigned long 377 * 378 * Parts of a cache may be shared with other devices such as GPU. This function 379 * calculates the largest exclusive portion of the cache where no other devices 380 * besides CPU have access to the cache portion. 381 * 382 * Return: = 0 on success, < 0 on failure. 383 */ 384 int get_mask_no_shareable(const char *cache_type, unsigned long *mask) 385 { 386 unsigned long full_mask, shareable_mask; 387 unsigned int start, len; 388 389 if (get_full_cbm(cache_type, &full_mask) < 0) 390 return -1; 391 if (get_shareable_mask(cache_type, &shareable_mask) < 0) 392 return -1; 393 394 len = count_contiguous_bits(full_mask & ~shareable_mask, &start); 395 if (!len) 396 return -1; 397 398 *mask = create_bit_mask(start, len); 399 400 return 0; 401 } 402 403 /* 404 * taskset_benchmark - Taskset PID (i.e. benchmark) to a specified cpu 405 * @bm_pid: PID that should be binded 406 * @cpu_no: CPU number at which the PID would be binded 407 * @old_affinity: When not NULL, set to old CPU affinity 408 * 409 * Return: 0 on success, < 0 on error. 410 */ 411 int taskset_benchmark(pid_t bm_pid, int cpu_no, cpu_set_t *old_affinity) 412 { 413 cpu_set_t my_set; 414 415 if (old_affinity) { 416 CPU_ZERO(old_affinity); 417 if (sched_getaffinity(bm_pid, sizeof(*old_affinity), 418 old_affinity)) { 419 ksft_perror("Unable to read CPU affinity"); 420 return -1; 421 } 422 } 423 424 CPU_ZERO(&my_set); 425 CPU_SET(cpu_no, &my_set); 426 427 if (sched_setaffinity(bm_pid, sizeof(cpu_set_t), &my_set)) { 428 ksft_perror("Unable to taskset benchmark"); 429 430 return -1; 431 } 432 433 return 0; 434 } 435 436 /* 437 * taskset_restore - Taskset PID to the earlier CPU affinity 438 * @bm_pid: PID that should be reset 439 * @old_affinity: The old CPU affinity to restore 440 * 441 * Return: 0 on success, < 0 on error. 442 */ 443 int taskset_restore(pid_t bm_pid, cpu_set_t *old_affinity) 444 { 445 if (sched_setaffinity(bm_pid, sizeof(*old_affinity), old_affinity)) { 446 ksft_perror("Unable to restore CPU affinity"); 447 return -1; 448 } 449 450 return 0; 451 } 452 453 /* 454 * create_grp - Create a group only if one doesn't exist 455 * @grp_name: Name of the group 456 * @grp: Full path and name of the group 457 * @parent_grp: Full path and name of the parent group 458 * 459 * Creates a group @grp_name if it does not exist yet. If @grp_name is NULL, 460 * it is interpreted as the root group which always results in success. 461 * 462 * Return: 0 on success, < 0 on error. 463 */ 464 static int create_grp(const char *grp_name, char *grp, const char *parent_grp) 465 { 466 int found_grp = 0; 467 struct dirent *ep; 468 DIR *dp; 469 470 if (!grp_name) 471 return 0; 472 473 /* Check if requested grp exists or not */ 474 dp = opendir(parent_grp); 475 if (dp) { 476 while ((ep = readdir(dp)) != NULL) { 477 if (strcmp(ep->d_name, grp_name) == 0) 478 found_grp = 1; 479 } 480 closedir(dp); 481 } else { 482 ksft_perror("Unable to open resctrl for group"); 483 484 return -1; 485 } 486 487 /* Requested grp doesn't exist, hence create it */ 488 if (found_grp == 0) { 489 if (mkdir(grp, 0) == -1) { 490 ksft_perror("Unable to create group"); 491 492 return -1; 493 } 494 } 495 496 return 0; 497 } 498 499 static int write_pid_to_tasks(char *tasks, pid_t pid) 500 { 501 FILE *fp; 502 503 fp = fopen(tasks, "w"); 504 if (!fp) { 505 ksft_perror("Failed to open tasks file"); 506 507 return -1; 508 } 509 if (fprintf(fp, "%d\n", (int)pid) < 0) { 510 ksft_print_msg("Failed to write pid to tasks file\n"); 511 fclose(fp); 512 513 return -1; 514 } 515 fclose(fp); 516 517 return 0; 518 } 519 520 /* 521 * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS 522 * @bm_pid: PID that should be written 523 * @ctrlgrp: Name of the control monitor group (con_mon grp) 524 * @mongrp: Name of the monitor group (mon grp) 525 * 526 * If a con_mon grp is requested, create it and write pid to it, otherwise 527 * write pid to root con_mon grp. 528 * If a mon grp is requested, create it and write pid to it, otherwise 529 * pid is not written, this means that pid is in con_mon grp and hence 530 * should consult con_mon grp's mon_data directory for results. 531 * 532 * Return: 0 on success, < 0 on error. 533 */ 534 int write_bm_pid_to_resctrl(pid_t bm_pid, const char *ctrlgrp, const char *mongrp) 535 { 536 char controlgroup[128], monitorgroup[512], monitorgroup_p[256]; 537 char tasks[1024]; 538 int ret = 0; 539 540 if (ctrlgrp) 541 sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp); 542 else 543 sprintf(controlgroup, "%s", RESCTRL_PATH); 544 545 /* Create control and monitoring group and write pid into it */ 546 ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH); 547 if (ret) 548 goto out; 549 sprintf(tasks, "%s/tasks", controlgroup); 550 ret = write_pid_to_tasks(tasks, bm_pid); 551 if (ret) 552 goto out; 553 554 /* Create monitor group and write pid into if it is used */ 555 if (mongrp) { 556 sprintf(monitorgroup_p, "%s/mon_groups", controlgroup); 557 sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp); 558 ret = create_grp(mongrp, monitorgroup, monitorgroup_p); 559 if (ret) 560 goto out; 561 562 sprintf(tasks, "%s/mon_groups/%s/tasks", 563 controlgroup, mongrp); 564 ret = write_pid_to_tasks(tasks, bm_pid); 565 if (ret) 566 goto out; 567 } 568 569 out: 570 ksft_print_msg("Writing benchmark parameters to resctrl FS\n"); 571 if (ret) 572 ksft_print_msg("Failed writing to resctrlfs\n"); 573 574 return ret; 575 } 576 577 /* 578 * write_schemata - Update schemata of a con_mon grp 579 * @ctrlgrp: Name of the con_mon grp 580 * @schemata: Schemata that should be updated to 581 * @cpu_no: CPU number that the benchmark PID is binded to 582 * @resource: Resctrl resource (Eg: MB, L3, L2, etc.) 583 * 584 * Update schemata of a con_mon grp *only* if requested resctrl resource is 585 * allocation type 586 * 587 * Return: 0 on success, < 0 on error. 588 */ 589 int write_schemata(const char *ctrlgrp, char *schemata, int cpu_no, 590 const char *resource) 591 { 592 char controlgroup[1024], reason[128], schema[1024] = {}; 593 int domain_id, fd, schema_len, ret = 0; 594 595 if (!schemata) { 596 ksft_print_msg("Skipping empty schemata update\n"); 597 598 return -1; 599 } 600 601 if (get_domain_id(resource, cpu_no, &domain_id) < 0) { 602 sprintf(reason, "Failed to get domain ID"); 603 ret = -1; 604 605 goto out; 606 } 607 608 if (ctrlgrp) 609 sprintf(controlgroup, "%s/%s/schemata", RESCTRL_PATH, ctrlgrp); 610 else 611 sprintf(controlgroup, "%s/schemata", RESCTRL_PATH); 612 613 schema_len = snprintf(schema, sizeof(schema), "%s:%d=%s\n", 614 resource, domain_id, schemata); 615 if (schema_len < 0 || schema_len >= sizeof(schema)) { 616 snprintf(reason, sizeof(reason), 617 "snprintf() failed with return value : %d", schema_len); 618 ret = -1; 619 goto out; 620 } 621 622 fd = open(controlgroup, O_WRONLY); 623 if (fd < 0) { 624 snprintf(reason, sizeof(reason), 625 "open() failed : %s", strerror(errno)); 626 ret = -1; 627 628 goto err_schema_not_empty; 629 } 630 if (write(fd, schema, schema_len) < 0) { 631 snprintf(reason, sizeof(reason), 632 "write() failed : %s", strerror(errno)); 633 close(fd); 634 ret = -1; 635 636 goto err_schema_not_empty; 637 } 638 close(fd); 639 640 err_schema_not_empty: 641 schema[schema_len - 1] = 0; 642 out: 643 ksft_print_msg("Write schema \"%s\" to resctrl FS%s%s\n", 644 schema, ret ? " # " : "", 645 ret ? reason : ""); 646 647 return ret; 648 } 649 650 bool check_resctrlfs_support(void) 651 { 652 FILE *inf = fopen("/proc/filesystems", "r"); 653 DIR *dp; 654 char *res; 655 bool ret = false; 656 657 if (!inf) 658 return false; 659 660 res = fgrep(inf, "nodev\tresctrl\n"); 661 662 if (res) { 663 ret = true; 664 free(res); 665 } 666 667 fclose(inf); 668 669 ksft_print_msg("%s Check kernel supports resctrl filesystem\n", 670 ret ? "Pass:" : "Fail:"); 671 672 if (!ret) 673 return ret; 674 675 dp = opendir(RESCTRL_PATH); 676 ksft_print_msg("%s Check resctrl mountpoint \"%s\" exists\n", 677 dp ? "Pass:" : "Fail:", RESCTRL_PATH); 678 if (dp) 679 closedir(dp); 680 681 ksft_print_msg("resctrl filesystem %s mounted\n", 682 find_resctrl_mount(NULL) ? "not" : "is"); 683 684 return ret; 685 } 686 687 char *fgrep(FILE *inf, const char *str) 688 { 689 char line[256]; 690 int slen = strlen(str); 691 692 while (!feof(inf)) { 693 if (!fgets(line, 256, inf)) 694 break; 695 if (strncmp(line, str, slen)) 696 continue; 697 698 return strdup(line); 699 } 700 701 return NULL; 702 } 703 704 /* 705 * resctrl_resource_exists - Check if a resource is supported. 706 * @resource: Resctrl resource (e.g., MB, L3, L2, L3_MON, etc.) 707 * 708 * Return: True if the resource is supported, else false. False is 709 * also returned if resctrl FS is not mounted. 710 */ 711 bool resctrl_resource_exists(const char *resource) 712 { 713 char res_path[PATH_MAX]; 714 struct stat statbuf; 715 int ret; 716 717 if (!resource) 718 return false; 719 720 ret = find_resctrl_mount(NULL); 721 if (ret) 722 return false; 723 724 snprintf(res_path, sizeof(res_path), "%s/%s", INFO_PATH, resource); 725 726 if (stat(res_path, &statbuf)) 727 return false; 728 729 return true; 730 } 731 732 /* 733 * resctrl_mon_feature_exists - Check if requested monitoring feature is valid. 734 * @resource: Resource that uses the mon_features file. Currently only L3_MON 735 * is valid. 736 * @feature: Required monitor feature (in mon_features file). 737 * 738 * Return: True if the feature is supported, else false. 739 */ 740 bool resctrl_mon_feature_exists(const char *resource, const char *feature) 741 { 742 char res_path[PATH_MAX]; 743 char *res; 744 FILE *inf; 745 746 if (!feature || !resource) 747 return false; 748 749 snprintf(res_path, sizeof(res_path), "%s/%s/mon_features", INFO_PATH, resource); 750 inf = fopen(res_path, "r"); 751 if (!inf) 752 return false; 753 754 res = fgrep(inf, feature); 755 free(res); 756 fclose(inf); 757 758 return !!res; 759 } 760 761 /* 762 * resource_info_file_exists - Check if a file is present inside 763 * /sys/fs/resctrl/info/@resource. 764 * @resource: Required resource (Eg: MB, L3, L2, etc.) 765 * @file: Required file. 766 * 767 * Return: True if the /sys/fs/resctrl/info/@resource/@file exists, else false. 768 */ 769 bool resource_info_file_exists(const char *resource, const char *file) 770 { 771 char res_path[PATH_MAX]; 772 struct stat statbuf; 773 774 if (!file || !resource) 775 return false; 776 777 snprintf(res_path, sizeof(res_path), "%s/%s/%s", INFO_PATH, resource, 778 file); 779 780 if (stat(res_path, &statbuf)) 781 return false; 782 783 return true; 784 } 785 786 bool test_resource_feature_check(const struct resctrl_test *test) 787 { 788 return resctrl_resource_exists(test->resource); 789 } 790 791 int filter_dmesg(void) 792 { 793 char line[1024]; 794 FILE *fp; 795 int pipefds[2]; 796 pid_t pid; 797 int ret; 798 799 ret = pipe(pipefds); 800 if (ret) { 801 ksft_perror("pipe"); 802 return ret; 803 } 804 fflush(stdout); 805 pid = fork(); 806 if (pid == 0) { 807 close(pipefds[0]); 808 dup2(pipefds[1], STDOUT_FILENO); 809 execlp("dmesg", "dmesg", NULL); 810 ksft_perror("Executing dmesg"); 811 exit(1); 812 } 813 close(pipefds[1]); 814 fp = fdopen(pipefds[0], "r"); 815 if (!fp) { 816 ksft_perror("fdopen(pipe)"); 817 kill(pid, SIGTERM); 818 819 return -1; 820 } 821 822 while (fgets(line, 1024, fp)) { 823 if (strstr(line, "intel_rdt:")) 824 ksft_print_msg("dmesg: %s", line); 825 if (strstr(line, "resctrl:")) 826 ksft_print_msg("dmesg: %s", line); 827 } 828 fclose(fp); 829 waitpid(pid, NULL, 0); 830 831 return 0; 832 } 833 834 const char *get_bw_report_type(const char *bw_report) 835 { 836 if (strcmp(bw_report, "reads") == 0) 837 return bw_report; 838 if (strcmp(bw_report, "writes") == 0) 839 return bw_report; 840 if (strcmp(bw_report, "nt-writes") == 0) { 841 return "writes"; 842 } 843 if (strcmp(bw_report, "total") == 0) 844 return bw_report; 845 846 fprintf(stderr, "Requested iMC bandwidth report type unavailable\n"); 847 848 return NULL; 849 } 850 851 int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, 852 int group_fd, unsigned long flags) 853 { 854 int ret; 855 856 ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, 857 group_fd, flags); 858 return ret; 859 } 860 861 unsigned int count_bits(unsigned long n) 862 { 863 unsigned int count = 0; 864 865 while (n) { 866 count += n & 1; 867 n >>= 1; 868 } 869 870 return count; 871 } 872