872 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Basic resctrl file system operations4 *5 * Copyright (C) 2018 Intel Corporation6 *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 16static 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/resctrl55 *56 * Mounts resctrl FS. Fails if resctrl FS is already mounted to avoid57 * pre-existing settings interfering with the test results.58 *59 * Return: 0 on success, < 0 on error.60 */61int 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 77int 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 integer99 * @cache_type: Cache level as string100 *101 * Return: cache level as integer or -1 if @cache_type is invalid.102 */103static 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 114static 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 CPU124 * @resource: resource name125 * @cpu_no: CPU number126 * @domain_id: domain ID (cache ID; for MB, L3 cache ID)127 *128 * Return: >= 0 on success, < 0 on failure.129 */130int 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 CPU161 * @cpu_no: CPU number162 * @cache_type: Cache level L2/L3163 * @cache_size: pointer to cache_size164 *165 * Return: = 0 on success, < 0 on failure.166 */167int 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 else211 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 file221 * @filename: File containing the mask222 * @mask: The bit mask returned as unsigned long223 *224 * Return: = 0 on success, < 0 on failure.225 */226static 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 from254 * /sys/fs/resctrl/info/@resource/@filename255 * @resource: Resource name that matches directory name in256 * /sys/fs/resctrl/info257 * @filename: File in /sys/fs/resctrl/info/@resource258 * @val: Contains read value on success.259 *260 * Return: = 0 on success, < 0 on failure. On success the read261 * value is saved into @val.262 */263int 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 pair290 * @start: LSB of the mask291 * @len Number of bits in the mask292 */293unsigned 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 mask300 * @val A bit mask301 * @start The location of the least-significant bit of the longest train302 *303 * Return: The length of the contiguous bits in the longest train of bits304 */305unsigned 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 else320 *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 cache330 * available for allocation, returned as unsigned long.331 *332 * Return: = 0 on success, < 0 on failure.333 */334int 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_bits354 * @cache_type: Cache type as "L2" or "L3"355 * @shareable_mask: Shareable mask returned as unsigned long356 *357 * Return: = 0 on success, < 0 on failure.358 */359static 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 bits374 * @cache_type: Cache type as "L2" or "L3"375 * @mask: The largest exclusive portion of the cache out of the376 * full CBM, returned as unsigned long377 *378 * Parts of a cache may be shared with other devices such as GPU. This function379 * calculates the largest exclusive portion of the cache where no other devices380 * besides CPU have access to the cache portion.381 *382 * Return: = 0 on success, < 0 on failure.383 */384int 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 cpu405 * @bm_pid: PID that should be binded406 * @cpu_no: CPU number at which the PID would be binded407 * @old_affinity: When not NULL, set to old CPU affinity408 *409 * Return: 0 on success, < 0 on error.410 */411int 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 affinity438 * @bm_pid: PID that should be reset439 * @old_affinity: The old CPU affinity to restore440 *441 * Return: 0 on success, < 0 on error.442 */443int 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 exist455 * @grp_name: Name of the group456 * @grp: Full path and name of the group457 * @parent_grp: Full path and name of the parent group458 *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 */464static 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 499static 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 FS522 * @bm_pid: PID that should be written523 * @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, otherwise527 * write pid to root con_mon grp.528 * If a mon grp is requested, create it and write pid to it, otherwise529 * pid is not written, this means that pid is in con_mon grp and hence530 * should consult con_mon grp's mon_data directory for results.531 *532 * Return: 0 on success, < 0 on error.533 */534int 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 else543 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 569out: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 grp579 * @ctrlgrp: Name of the con_mon grp580 * @schemata: Schemata that should be updated to581 * @cpu_no: CPU number that the benchmark PID is binded to582 * @resource: Resctrl resource (Eg: MB, L3, L2, etc.)583 *584 * Update schemata of a con_mon grp *only* if requested resctrl resource is585 * allocation type586 *587 * Return: 0 on success, < 0 on error.588 */589int 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 else611 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 640err_schema_not_empty:641 schema[schema_len - 1] = 0;642out: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 650bool 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 687char *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 is709 * also returned if resctrl FS is not mounted.710 */711bool 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_MON735 * is valid.736 * @feature: Required monitor feature (in mon_features file).737 *738 * Return: True if the feature is supported, else false.739 */740bool 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 inside763 * /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 */769bool 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 786bool test_resource_feature_check(const struct resctrl_test *test)787{788 return resctrl_resource_exists(test->resource);789}790 791int 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 834const 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 851int 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 861unsigned 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