brintos

brintos / linux-shallow public Read only

0
0
Text · 18.5 KiB · 250c320 Raw
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