xref: /linux/tools/testing/selftests/resctrl/resctrlfs.c (revision c4bbe83d27c2446a033cc0381c3fb6be5e8c41c7)
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