1 // SPDX-License-Identifier: GPL-2.0
3 * Basic resctrl file system operations
5 * Copyright (C) 2018 Intel Corporation
8 * Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>,
9 * Fenghua Yu <fenghua.yu@intel.com>
15 static int find_resctrl_mount(char *buffer
)
18 char line
[256], *fs
, *mntpoint
;
20 mounts
= fopen("/proc/mounts", "r");
22 perror("/proc/mounts");
25 while (!feof(mounts
)) {
26 if (!fgets(line
, 256, mounts
))
28 fs
= strtok(line
, " \t");
31 mntpoint
= strtok(NULL
, " \t");
34 fs
= strtok(NULL
, " \t");
37 if (strcmp(fs
, "resctrl"))
42 strncpy(buffer
, mntpoint
, 256);
55 * remount_resctrlfs - Remount resctrl FS at /sys/fs/resctrl
56 * @mum_resctrlfs: Should the resctrl FS be remounted?
58 * If not mounted, mount it.
59 * If mounted and mum_resctrlfs then remount resctrl FS.
60 * If mounted and !mum_resctrlfs then noop
62 * Return: 0 on success, non-zero on failure
64 int remount_resctrlfs(bool mum_resctrlfs
)
69 ret
= find_resctrl_mount(mountpoint
);
71 strcpy(mountpoint
, RESCTRL_PATH
);
73 if (!ret
&& mum_resctrlfs
&& umount(mountpoint
)) {
74 printf("not ok unmounting \"%s\"\n", mountpoint
);
79 if (!ret
&& !mum_resctrlfs
)
82 ret
= mount("resctrl", RESCTRL_PATH
, "resctrl", 0, NULL
);
83 printf("%sok mounting resctrl to \"%s\"\n", ret
? "not " : "",
93 int umount_resctrlfs(void)
95 if (umount(RESCTRL_PATH
)) {
96 perror("# Unable to umount resctrl");
105 * get_resource_id - Get socket number/l3 id for a specified CPU
106 * @cpu_no: CPU number
107 * @resource_id: Socket number or l3_id
109 * Return: >= 0 on success, < 0 on failure.
111 int get_resource_id(int cpu_no
, int *resource_id
)
113 char phys_pkg_path
[1024];
117 sprintf(phys_pkg_path
, "%s%d/cache/index3/id",
118 PHYS_ID_PATH
, cpu_no
);
120 sprintf(phys_pkg_path
, "%s%d/topology/physical_package_id",
121 PHYS_ID_PATH
, cpu_no
);
123 fp
= fopen(phys_pkg_path
, "r");
125 perror("Failed to open physical_package_id");
129 if (fscanf(fp
, "%d", resource_id
) <= 0) {
130 perror("Could not get socket number or l3 id");
141 * get_cache_size - Get cache size for a specified CPU
142 * @cpu_no: CPU number
143 * @cache_type: Cache level L2/L3
144 * @cache_size: pointer to cache_size
146 * Return: = 0 on success, < 0 on failure.
148 int get_cache_size(int cpu_no
, char *cache_type
, unsigned long *cache_size
)
150 char cache_path
[1024], cache_str
[64];
151 int length
, i
, cache_num
;
154 if (!strcmp(cache_type
, "L3")) {
156 } else if (!strcmp(cache_type
, "L2")) {
159 perror("Invalid cache level");
163 sprintf(cache_path
, "/sys/bus/cpu/devices/cpu%d/cache/index%d/size",
165 fp
= fopen(cache_path
, "r");
167 perror("Failed to open cache size");
171 if (fscanf(fp
, "%s", cache_str
) <= 0) {
172 perror("Could not get cache_size");
179 length
= (int)strlen(cache_str
);
183 for (i
= 0; i
< length
; i
++) {
184 if ((cache_str
[i
] >= '0') && (cache_str
[i
] <= '9'))
186 *cache_size
= *cache_size
* 10 + (cache_str
[i
] - '0');
188 else if (cache_str
[i
] == 'K')
190 *cache_size
= *cache_size
* 1024;
192 else if (cache_str
[i
] == 'M')
194 *cache_size
= *cache_size
* 1024 * 1024;
203 #define CORE_SIBLINGS_PATH "/sys/bus/cpu/devices/cpu"
206 * get_cbm_mask - Get cbm mask for given cache
207 * @cache_type: Cache level L2/L3
209 * Mask is stored in cbm_mask which is global variable.
211 * Return: = 0 on success, < 0 on failure.
213 int get_cbm_mask(char *cache_type
)
215 char cbm_mask_path
[1024];
218 sprintf(cbm_mask_path
, "%s/%s/cbm_mask", CBM_MASK_PATH
, cache_type
);
220 fp
= fopen(cbm_mask_path
, "r");
222 perror("Failed to open cache level");
226 if (fscanf(fp
, "%s", cbm_mask
) <= 0) {
227 perror("Could not get max cbm_mask");
238 * get_core_sibling - Get sibling core id from the same socket for given CPU
239 * @cpu_no: CPU number
241 * Return: > 0 on success, < 0 on failure.
243 int get_core_sibling(int cpu_no
)
245 char core_siblings_path
[1024], cpu_list_str
[64];
246 int sibling_cpu_no
= -1;
249 sprintf(core_siblings_path
, "%s%d/topology/core_siblings_list",
250 CORE_SIBLINGS_PATH
, cpu_no
);
252 fp
= fopen(core_siblings_path
, "r");
254 perror("Failed to open core siblings path");
258 if (fscanf(fp
, "%s", cpu_list_str
) <= 0) {
259 perror("Could not get core_siblings list");
266 char *token
= strtok(cpu_list_str
, "-,");
269 sibling_cpu_no
= atoi(token
);
270 /* Skipping core 0 as we don't want to run test on core 0 */
271 if (sibling_cpu_no
!= 0)
273 token
= strtok(NULL
, "-,");
276 return sibling_cpu_no
;
280 * taskset_benchmark - Taskset PID (i.e. benchmark) to a specified cpu
281 * @bm_pid: PID that should be binded
282 * @cpu_no: CPU number at which the PID would be binded
284 * Return: 0 on success, non-zero on failure
286 int taskset_benchmark(pid_t bm_pid
, int cpu_no
)
291 CPU_SET(cpu_no
, &my_set
);
293 if (sched_setaffinity(bm_pid
, sizeof(cpu_set_t
), &my_set
)) {
294 perror("Unable to taskset benchmark");
303 * run_benchmark - Run a specified benchmark or fill_buf (default benchmark)
304 * in specified signal. Direct benchmark stdio to /dev/null.
305 * @signum: signal number
307 * @ucontext: user context in signal handling
311 void run_benchmark(int signum
, siginfo_t
*info
, void *ucontext
)
313 int operation
, ret
, malloc_and_init_memory
, memflush
;
314 unsigned long span
, buffer_span
;
315 char **benchmark_cmd
;
316 char resctrl_val
[64];
319 benchmark_cmd
= info
->si_ptr
;
322 * Direct stdio of child to /dev/null, so that only parent writes to
325 fp
= freopen("/dev/null", "w", stdout
);
327 PARENT_EXIT("Unable to direct benchmark status to /dev/null");
329 if (strcmp(benchmark_cmd
[0], "fill_buf") == 0) {
330 /* Execute default fill_buf benchmark */
331 span
= strtoul(benchmark_cmd
[1], NULL
, 10);
332 malloc_and_init_memory
= atoi(benchmark_cmd
[2]);
333 memflush
= atoi(benchmark_cmd
[3]);
334 operation
= atoi(benchmark_cmd
[4]);
335 sprintf(resctrl_val
, "%s", benchmark_cmd
[5]);
337 if (strcmp(resctrl_val
, "cqm") != 0)
338 buffer_span
= span
* MB
;
342 if (run_fill_buf(buffer_span
, malloc_and_init_memory
, memflush
,
343 operation
, resctrl_val
))
344 fprintf(stderr
, "Error in running fill buffer\n");
346 /* Execute specified benchmark */
347 ret
= execvp(benchmark_cmd
[0], benchmark_cmd
);
353 PARENT_EXIT("Unable to run specified benchmark");
357 * create_grp - Create a group only if one doesn't exist
358 * @grp_name: Name of the group
359 * @grp: Full path and name of the group
360 * @parent_grp: Full path and name of the parent group
362 * Return: 0 on success, non-zero on failure
364 static int create_grp(const char *grp_name
, char *grp
, const char *parent_grp
)
371 * At this point, we are guaranteed to have resctrl FS mounted and if
372 * length of grp_name == 0, it means, user wants to use root con_mon
375 if (strlen(grp_name
) == 0)
378 /* Check if requested grp exists or not */
379 dp
= opendir(parent_grp
);
381 while ((ep
= readdir(dp
)) != NULL
) {
382 if (strcmp(ep
->d_name
, grp_name
) == 0)
387 perror("Unable to open resctrl for group");
392 /* Requested grp doesn't exist, hence create it */
393 if (found_grp
== 0) {
394 if (mkdir(grp
, 0) == -1) {
395 perror("Unable to create group");
404 static int write_pid_to_tasks(char *tasks
, pid_t pid
)
408 fp
= fopen(tasks
, "w");
410 perror("Failed to open tasks file");
414 if (fprintf(fp
, "%d\n", pid
) < 0) {
415 perror("Failed to wr pid to tasks file");
426 * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS
427 * @bm_pid: PID that should be written
428 * @ctrlgrp: Name of the control monitor group (con_mon grp)
429 * @mongrp: Name of the monitor group (mon grp)
430 * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc)
432 * If a con_mon grp is requested, create it and write pid to it, otherwise
433 * write pid to root con_mon grp.
434 * If a mon grp is requested, create it and write pid to it, otherwise
435 * pid is not written, this means that pid is in con_mon grp and hence
436 * should consult con_mon grp's mon_data directory for results.
438 * Return: 0 on success, non-zero on failure
440 int write_bm_pid_to_resctrl(pid_t bm_pid
, char *ctrlgrp
, char *mongrp
,
443 char controlgroup
[128], monitorgroup
[512], monitorgroup_p
[256];
448 sprintf(controlgroup
, "%s/%s", RESCTRL_PATH
, ctrlgrp
);
450 sprintf(controlgroup
, "%s", RESCTRL_PATH
);
452 /* Create control and monitoring group and write pid into it */
453 ret
= create_grp(ctrlgrp
, controlgroup
, RESCTRL_PATH
);
456 sprintf(tasks
, "%s/tasks", controlgroup
);
457 ret
= write_pid_to_tasks(tasks
, bm_pid
);
461 /* Create mon grp and write pid into it for "mbm" and "cqm" test */
462 if ((strcmp(resctrl_val
, "cqm") == 0) ||
463 (strcmp(resctrl_val
, "mbm") == 0)) {
464 if (strlen(mongrp
)) {
465 sprintf(monitorgroup_p
, "%s/mon_groups", controlgroup
);
466 sprintf(monitorgroup
, "%s/%s", monitorgroup_p
, mongrp
);
467 ret
= create_grp(mongrp
, monitorgroup
, monitorgroup_p
);
471 sprintf(tasks
, "%s/mon_groups/%s/tasks",
472 controlgroup
, mongrp
);
473 ret
= write_pid_to_tasks(tasks
, bm_pid
);
480 printf("%sok writing benchmark parameters to resctrl FS\n",
483 perror("# writing to resctrlfs");
491 * write_schemata - Update schemata of a con_mon grp
492 * @ctrlgrp: Name of the con_mon grp
493 * @schemata: Schemata that should be updated to
494 * @cpu_no: CPU number that the benchmark PID is binded to
495 * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc)
497 * Update schemata of a con_mon grp *only* if requested resctrl feature is
500 * Return: 0 on success, non-zero on failure
502 int write_schemata(char *ctrlgrp
, char *schemata
, int cpu_no
, char *resctrl_val
)
504 char controlgroup
[1024], schema
[1024], reason
[64];
505 int resource_id
, ret
= 0;
508 if ((strcmp(resctrl_val
, "mba") != 0) &&
509 (strcmp(resctrl_val
, "cat") != 0) &&
510 (strcmp(resctrl_val
, "cqm") != 0))
514 printf("# Skipping empty schemata update\n");
519 if (get_resource_id(cpu_no
, &resource_id
) < 0) {
520 sprintf(reason
, "Failed to get resource id");
526 if (strlen(ctrlgrp
) != 0)
527 sprintf(controlgroup
, "%s/%s/schemata", RESCTRL_PATH
, ctrlgrp
);
529 sprintf(controlgroup
, "%s/schemata", RESCTRL_PATH
);
531 if (!strcmp(resctrl_val
, "cat") || !strcmp(resctrl_val
, "cqm"))
532 sprintf(schema
, "%s%d%c%s", "L3:", resource_id
, '=', schemata
);
533 if (strcmp(resctrl_val
, "mba") == 0)
534 sprintf(schema
, "%s%d%c%s", "MB:", resource_id
, '=', schemata
);
536 fp
= fopen(controlgroup
, "w");
538 sprintf(reason
, "Failed to open control group");
544 if (fprintf(fp
, "%s\n", schema
) < 0) {
545 sprintf(reason
, "Failed to write schemata in control group");
554 printf("%sok Write schema \"%s\" to resctrl FS%s%s\n",
555 ret
? "not " : "", schema
, ret
? " # " : "",
562 bool check_resctrlfs_support(void)
564 FILE *inf
= fopen("/proc/filesystems", "r");
572 res
= fgrep(inf
, "nodev\tresctrl\n");
581 printf("%sok kernel supports resctrl filesystem\n", ret
? "" : "not ");
584 dp
= opendir(RESCTRL_PATH
);
585 printf("%sok resctrl mountpoint \"%s\" exists\n",
586 dp
? "" : "not ", RESCTRL_PATH
);
591 printf("# resctrl filesystem %s mounted\n",
592 find_resctrl_mount(NULL
) ? "not" : "is");
597 char *fgrep(FILE *inf
, const char *str
)
600 int slen
= strlen(str
);
603 if (!fgets(line
, 256, inf
))
605 if (strncmp(line
, str
, slen
))
615 * validate_resctrl_feature_request - Check if requested feature is valid.
616 * @resctrl_val: Requested feature
618 * Return: 0 on success, non-zero on failure
620 bool validate_resctrl_feature_request(char *resctrl_val
)
622 FILE *inf
= fopen("/proc/cpuinfo", "r");
629 res
= fgrep(inf
, "flags");
632 char *s
= strchr(res
, ':');
634 found
= s
&& !strstr(s
, resctrl_val
);
642 int filter_dmesg(void)
658 dup2(pipefds
[1], STDOUT_FILENO
);
659 execlp("dmesg", "dmesg", NULL
);
660 perror("executing dmesg");
664 fp
= fdopen(pipefds
[0], "r");
666 perror("fdopen(pipe)");
672 while (fgets(line
, 1024, fp
)) {
673 if (strstr(line
, "intel_rdt:"))
674 printf("# dmesg: %s", line
);
675 if (strstr(line
, "resctrl:"))
676 printf("# dmesg: %s", line
);
679 waitpid(pid
, NULL
, 0);
684 int validate_bw_report_request(char *bw_report
)
686 if (strcmp(bw_report
, "reads") == 0)
688 if (strcmp(bw_report
, "writes") == 0)
690 if (strcmp(bw_report
, "nt-writes") == 0) {
691 strcpy(bw_report
, "writes");
694 if (strcmp(bw_report
, "total") == 0)
697 fprintf(stderr
, "Requested iMC B/W report type unavailable\n");
702 int perf_event_open(struct perf_event_attr
*hw_event
, pid_t pid
, int cpu
,
703 int group_fd
, unsigned long flags
)
707 ret
= syscall(__NR_perf_event_open
, hw_event
, pid
, cpu
,
712 unsigned int count_bits(unsigned long n
)
714 unsigned int count
= 0;