Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  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 "resctrl.h"
 12
 13static int find_resctrl_mount(char *buffer)
 14{
 15	FILE *mounts;
 16	char line[256], *fs, *mntpoint;
 17
 18	mounts = fopen("/proc/mounts", "r");
 19	if (!mounts) {
 20		perror("/proc/mounts");
 21		return -ENXIO;
 22	}
 23	while (!feof(mounts)) {
 24		if (!fgets(line, 256, mounts))
 25			break;
 26		fs = strtok(line, " \t");
 27		if (!fs)
 28			continue;
 29		mntpoint = strtok(NULL, " \t");
 30		if (!mntpoint)
 31			continue;
 32		fs = strtok(NULL, " \t");
 33		if (!fs)
 34			continue;
 35		if (strcmp(fs, "resctrl"))
 36			continue;
 37
 38		fclose(mounts);
 39		if (buffer)
 40			strncpy(buffer, mntpoint, 256);
 41
 42		return 0;
 43	}
 44
 45	fclose(mounts);
 46
 47	return -ENOENT;
 48}
 49
 50/*
 51 * remount_resctrlfs - Remount resctrl FS at /sys/fs/resctrl
 52 * @mum_resctrlfs:	Should the resctrl FS be remounted?
 53 *
 54 * If not mounted, mount it.
 55 * If mounted and mum_resctrlfs then remount resctrl FS.
 56 * If mounted and !mum_resctrlfs then noop
 57 *
 58 * Return: 0 on success, non-zero on failure
 59 */
 60int remount_resctrlfs(bool mum_resctrlfs)
 61{
 62	char mountpoint[256];
 63	int ret;
 64
 65	ret = find_resctrl_mount(mountpoint);
 66	if (ret)
 67		strcpy(mountpoint, RESCTRL_PATH);
 68
 69	if (!ret && mum_resctrlfs && umount(mountpoint))
 70		ksft_print_msg("Fail: unmounting \"%s\"\n", mountpoint);
 71
 72	if (!ret && !mum_resctrlfs)
 73		return 0;
 74
 75	ksft_print_msg("Mounting resctrl to \"%s\"\n", RESCTRL_PATH);
 76	ret = mount("resctrl", RESCTRL_PATH, "resctrl", 0, NULL);
 77	if (ret)
 78		perror("# mount");
 79
 80	return ret;
 81}
 82
 83int umount_resctrlfs(void)
 84{
 85	if (find_resctrl_mount(NULL))
 86		return 0;
 87
 88	if (umount(RESCTRL_PATH)) {
 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 */
104int get_resource_id(int cpu_no, int *resource_id)
105{
106	char phys_pkg_path[1024];
107	FILE *fp;
108
109	if (is_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 */
141int 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 */
205int 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", CBM_MASK_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 */
238int 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 */
281int 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 * run_benchmark - Run a specified benchmark or fill_buf (default benchmark)
299 *		   in specified signal. Direct benchmark stdio to /dev/null.
300 * @signum:	signal number
301 * @info:	signal info
302 * @ucontext:	user context in signal handling
303 *
304 * Return: void
305 */
306void run_benchmark(int signum, siginfo_t *info, void *ucontext)
307{
308	int operation, ret, malloc_and_init_memory, memflush;
309	unsigned long span, buffer_span;
310	char **benchmark_cmd;
311	char resctrl_val[64];
312	FILE *fp;
313
314	benchmark_cmd = info->si_ptr;
315
316	/*
317	 * Direct stdio of child to /dev/null, so that only parent writes to
318	 * stdio (console)
319	 */
320	fp = freopen("/dev/null", "w", stdout);
321	if (!fp)
322		PARENT_EXIT("Unable to direct benchmark status to /dev/null");
323
324	if (strcmp(benchmark_cmd[0], "fill_buf") == 0) {
325		/* Execute default fill_buf benchmark */
326		span = strtoul(benchmark_cmd[1], NULL, 10);
327		malloc_and_init_memory = atoi(benchmark_cmd[2]);
328		memflush =  atoi(benchmark_cmd[3]);
329		operation = atoi(benchmark_cmd[4]);
330		sprintf(resctrl_val, "%s", benchmark_cmd[5]);
331
332		if (strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)))
333			buffer_span = span * MB;
334		else
335			buffer_span = span;
336
337		if (run_fill_buf(buffer_span, malloc_and_init_memory, memflush,
338				 operation, resctrl_val))
339			fprintf(stderr, "Error in running fill buffer\n");
340	} else {
341		/* Execute specified benchmark */
342		ret = execvp(benchmark_cmd[0], benchmark_cmd);
343		if (ret)
344			perror("wrong\n");
345	}
346
347	fclose(stdout);
348	PARENT_EXIT("Unable to run specified benchmark");
349}
350
351/*
352 * create_grp - Create a group only if one doesn't exist
353 * @grp_name:	Name of the group
354 * @grp:	Full path and name of the group
355 * @parent_grp:	Full path and name of the parent group
356 *
357 * Return: 0 on success, non-zero on failure
358 */
359static int create_grp(const char *grp_name, char *grp, const char *parent_grp)
360{
361	int found_grp = 0;
362	struct dirent *ep;
363	DIR *dp;
364
365	/*
366	 * At this point, we are guaranteed to have resctrl FS mounted and if
367	 * length of grp_name == 0, it means, user wants to use root con_mon
368	 * grp, so do nothing
369	 */
370	if (strlen(grp_name) == 0)
371		return 0;
372
373	/* Check if requested grp exists or not */
374	dp = opendir(parent_grp);
375	if (dp) {
376		while ((ep = readdir(dp)) != NULL) {
377			if (strcmp(ep->d_name, grp_name) == 0)
378				found_grp = 1;
379		}
380		closedir(dp);
381	} else {
382		perror("Unable to open resctrl for group");
383
384		return -1;
385	}
386
387	/* Requested grp doesn't exist, hence create it */
388	if (found_grp == 0) {
389		if (mkdir(grp, 0) == -1) {
390			perror("Unable to create group");
391
392			return -1;
393		}
394	}
395
396	return 0;
397}
398
399static int write_pid_to_tasks(char *tasks, pid_t pid)
400{
401	FILE *fp;
402
403	fp = fopen(tasks, "w");
404	if (!fp) {
405		perror("Failed to open tasks file");
406
407		return -1;
408	}
409	if (fprintf(fp, "%d\n", pid) < 0) {
410		perror("Failed to wr pid to tasks file");
411		fclose(fp);
412
413		return -1;
414	}
415	fclose(fp);
416
417	return 0;
418}
419
420/*
421 * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS
422 * @bm_pid:		PID that should be written
423 * @ctrlgrp:		Name of the control monitor group (con_mon grp)
424 * @mongrp:		Name of the monitor group (mon grp)
425 * @resctrl_val:	Resctrl feature (Eg: mbm, mba.. etc)
426 *
427 * If a con_mon grp is requested, create it and write pid to it, otherwise
428 * write pid to root con_mon grp.
429 * If a mon grp is requested, create it and write pid to it, otherwise
430 * pid is not written, this means that pid is in con_mon grp and hence
431 * should consult con_mon grp's mon_data directory for results.
432 *
433 * Return: 0 on success, non-zero on failure
434 */
435int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp,
436			    char *resctrl_val)
437{
438	char controlgroup[128], monitorgroup[512], monitorgroup_p[256];
439	char tasks[1024];
440	int ret = 0;
441
442	if (strlen(ctrlgrp))
443		sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp);
444	else
445		sprintf(controlgroup, "%s", RESCTRL_PATH);
446
447	/* Create control and monitoring group and write pid into it */
448	ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH);
449	if (ret)
450		goto out;
451	sprintf(tasks, "%s/tasks", controlgroup);
452	ret = write_pid_to_tasks(tasks, bm_pid);
453	if (ret)
454		goto out;
455
456	/* Create mon grp and write pid into it for "mbm" and "cmt" test */
457	if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)) ||
458	    !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) {
459		if (strlen(mongrp)) {
460			sprintf(monitorgroup_p, "%s/mon_groups", controlgroup);
461			sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp);
462			ret = create_grp(mongrp, monitorgroup, monitorgroup_p);
463			if (ret)
464				goto out;
465
466			sprintf(tasks, "%s/mon_groups/%s/tasks",
467				controlgroup, mongrp);
468			ret = write_pid_to_tasks(tasks, bm_pid);
469			if (ret)
470				goto out;
471		}
472	}
473
474out:
475	ksft_print_msg("Writing benchmark parameters to resctrl FS\n");
476	if (ret)
477		perror("# writing to resctrlfs");
478
479	return ret;
480}
481
482/*
483 * write_schemata - Update schemata of a con_mon grp
484 * @ctrlgrp:		Name of the con_mon grp
485 * @schemata:		Schemata that should be updated to
486 * @cpu_no:		CPU number that the benchmark PID is binded to
487 * @resctrl_val:	Resctrl feature (Eg: mbm, mba.. etc)
488 *
489 * Update schemata of a con_mon grp *only* if requested resctrl feature is
490 * allocation type
491 *
492 * Return: 0 on success, non-zero on failure
493 */
494int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, char *resctrl_val)
495{
496	char controlgroup[1024], schema[1024], reason[64];
497	int resource_id, ret = 0;
498	FILE *fp;
499
500	if (strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) &&
501	    strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) &&
502	    strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)))
503		return -ENOENT;
504
505	if (!schemata) {
506		ksft_print_msg("Skipping empty schemata update\n");
507
508		return -1;
509	}
510
511	if (get_resource_id(cpu_no, &resource_id) < 0) {
512		sprintf(reason, "Failed to get resource id");
513		ret = -1;
514
515		goto out;
516	}
517
518	if (strlen(ctrlgrp) != 0)
519		sprintf(controlgroup, "%s/%s/schemata", RESCTRL_PATH, ctrlgrp);
520	else
521		sprintf(controlgroup, "%s/schemata", RESCTRL_PATH);
522
523	if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) ||
524	    !strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)))
525		sprintf(schema, "%s%d%c%s", "L3:", resource_id, '=', schemata);
526	if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)))
527		sprintf(schema, "%s%d%c%s", "MB:", resource_id, '=', schemata);
528
529	fp = fopen(controlgroup, "w");
530	if (!fp) {
531		sprintf(reason, "Failed to open control group");
532		ret = -1;
533
534		goto out;
535	}
536
537	if (fprintf(fp, "%s\n", schema) < 0) {
538		sprintf(reason, "Failed to write schemata in control group");
539		fclose(fp);
540		ret = -1;
541
542		goto out;
543	}
544	fclose(fp);
545
546out:
547	ksft_print_msg("Write schema \"%s\" to resctrl FS%s%s\n",
548		       schema, ret ? " # " : "",
549		       ret ? reason : "");
550
551	return ret;
552}
553
554bool check_resctrlfs_support(void)
555{
556	FILE *inf = fopen("/proc/filesystems", "r");
557	DIR *dp;
558	char *res;
559	bool ret = false;
560
561	if (!inf)
562		return false;
563
564	res = fgrep(inf, "nodev\tresctrl\n");
565
566	if (res) {
567		ret = true;
568		free(res);
569	}
570
571	fclose(inf);
572
573	ksft_print_msg("%s Check kernel supports resctrl filesystem\n",
574		       ret ? "Pass:" : "Fail:");
575
576	if (!ret)
577		return ret;
578
579	dp = opendir(RESCTRL_PATH);
580	ksft_print_msg("%s Check resctrl mountpoint \"%s\" exists\n",
581		       dp ? "Pass:" : "Fail:", RESCTRL_PATH);
582	if (dp)
583		closedir(dp);
584
585	ksft_print_msg("resctrl filesystem %s mounted\n",
586		       find_resctrl_mount(NULL) ? "not" : "is");
587
588	return ret;
589}
590
591char *fgrep(FILE *inf, const char *str)
592{
593	char line[256];
594	int slen = strlen(str);
595
596	while (!feof(inf)) {
597		if (!fgets(line, 256, inf))
598			break;
599		if (strncmp(line, str, slen))
600			continue;
601
602		return strdup(line);
603	}
604
605	return NULL;
606}
607
608/*
609 * validate_resctrl_feature_request - Check if requested feature is valid.
610 * @resctrl_val:	Requested feature
611 *
612 * Return: True if the feature is supported, else false
613 */
614bool validate_resctrl_feature_request(const char *resctrl_val)
615{
616	struct stat statbuf;
617	bool found = false;
618	char *res;
619	FILE *inf;
620
621	if (!resctrl_val)
622		return false;
623
624	if (remount_resctrlfs(false))
625		return false;
626
627	if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) {
628		if (!stat(L3_PATH, &statbuf))
629			return true;
630	} else if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR))) {
631		if (!stat(MB_PATH, &statbuf))
632			return true;
633	} else if (!strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR)) ||
634		   !strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) {
635		if (!stat(L3_MON_PATH, &statbuf)) {
636			inf = fopen(L3_MON_FEATURES_PATH, "r");
637			if (!inf)
638				return false;
639
640			if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) {
641				res = fgrep(inf, "llc_occupancy");
642				if (res) {
643					found = true;
644					free(res);
645				}
646			}
647
648			if (!strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) {
649				res = fgrep(inf, "mbm_total_bytes");
650				if (res) {
651					free(res);
652					res = fgrep(inf, "mbm_local_bytes");
653					if (res) {
654						found = true;
655						free(res);
656					}
657				}
658			}
659			fclose(inf);
660		}
661	}
662
663	return found;
664}
665
666int filter_dmesg(void)
667{
668	char line[1024];
669	FILE *fp;
670	int pipefds[2];
671	pid_t pid;
672	int ret;
673
674	ret = pipe(pipefds);
675	if (ret) {
676		perror("pipe");
677		return ret;
678	}
679	pid = fork();
680	if (pid == 0) {
681		close(pipefds[0]);
682		dup2(pipefds[1], STDOUT_FILENO);
683		execlp("dmesg", "dmesg", NULL);
684		perror("executing dmesg");
685		exit(1);
686	}
687	close(pipefds[1]);
688	fp = fdopen(pipefds[0], "r");
689	if (!fp) {
690		perror("fdopen(pipe)");
691		kill(pid, SIGTERM);
692
693		return -1;
694	}
695
696	while (fgets(line, 1024, fp)) {
697		if (strstr(line, "intel_rdt:"))
698			ksft_print_msg("dmesg: %s", line);
699		if (strstr(line, "resctrl:"))
700			ksft_print_msg("dmesg: %s", line);
701	}
702	fclose(fp);
703	waitpid(pid, NULL, 0);
704
705	return 0;
706}
707
708int validate_bw_report_request(char *bw_report)
709{
710	if (strcmp(bw_report, "reads") == 0)
711		return 0;
712	if (strcmp(bw_report, "writes") == 0)
713		return 0;
714	if (strcmp(bw_report, "nt-writes") == 0) {
715		strcpy(bw_report, "writes");
716		return 0;
717	}
718	if (strcmp(bw_report, "total") == 0)
719		return 0;
720
721	fprintf(stderr, "Requested iMC B/W report type unavailable\n");
722
723	return -1;
724}
725
726int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu,
727		    int group_fd, unsigned long flags)
728{
729	int ret;
730
731	ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
732		      group_fd, flags);
733	return ret;
734}
735
736unsigned int count_bits(unsigned long n)
737{
738	unsigned int count = 0;
739
740	while (n) {
741		count += n & 1;
742		n >>= 1;
743	}
744
745	return count;
746}