746 lines · 15.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Basic resctrl file system operations * * Copyright (C) 2018 Intel Corporation * * Authors: *    Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>, *    Fenghua Yu <fenghua.yu@intel.com> */#include "resctrl.h" static int find_resctrl_mount(char *buffer){	FILE *mounts;	char line[256], *fs, *mntpoint; 	mounts = fopen("/proc/mounts", "r");	if (!mounts) {		perror("/proc/mounts");		return -ENXIO;	}	while (!feof(mounts)) {		if (!fgets(line, 256, mounts))			break;		fs = strtok(line, " \t");		if (!fs)			continue;		mntpoint = strtok(NULL, " \t");		if (!mntpoint)			continue;		fs = strtok(NULL, " \t");		if (!fs)			continue;		if (strcmp(fs, "resctrl"))			continue; 		fclose(mounts);		if (buffer)			strncpy(buffer, mntpoint, 256); 		return 0;	} 	fclose(mounts); 	return -ENOENT;} /* * remount_resctrlfs - Remount resctrl FS at /sys/fs/resctrl * @mum_resctrlfs:	Should the resctrl FS be remounted? * * If not mounted, mount it. * If mounted and mum_resctrlfs then remount resctrl FS. * If mounted and !mum_resctrlfs then noop * * Return: 0 on success, non-zero on failure */int remount_resctrlfs(bool mum_resctrlfs){	char mountpoint[256];	int ret; 	ret = find_resctrl_mount(mountpoint);	if (ret)		strcpy(mountpoint, RESCTRL_PATH); 	if (!ret && mum_resctrlfs && umount(mountpoint))		ksft_print_msg("Fail: unmounting \"%s\"\n", mountpoint); 	if (!ret && !mum_resctrlfs)		return 0; 	ksft_print_msg("Mounting resctrl to \"%s\"\n", RESCTRL_PATH);	ret = mount("resctrl", RESCTRL_PATH, "resctrl", 0, NULL);	if (ret)		perror("# mount"); 	return ret;} int umount_resctrlfs(void){	if (find_resctrl_mount(NULL))		return 0; 	if (umount(RESCTRL_PATH)) {		perror("# Unable to umount resctrl"); 		return errno;	} 	return 0;} /* * get_resource_id - Get socket number/l3 id for a specified CPU * @cpu_no:	CPU number * @resource_id: Socket number or l3_id * * Return: >= 0 on success, < 0 on failure. */int get_resource_id(int cpu_no, int *resource_id){	char phys_pkg_path[1024];	FILE *fp; 	if (is_amd)		sprintf(phys_pkg_path, "%s%d/cache/index3/id",			PHYS_ID_PATH, cpu_no);	else		sprintf(phys_pkg_path, "%s%d/topology/physical_package_id",			PHYS_ID_PATH, cpu_no); 	fp = fopen(phys_pkg_path, "r");	if (!fp) {		perror("Failed to open physical_package_id"); 		return -1;	}	if (fscanf(fp, "%d", resource_id) <= 0) {		perror("Could not get socket number or l3 id");		fclose(fp); 		return -1;	}	fclose(fp); 	return 0;} /* * get_cache_size - Get cache size for a specified CPU * @cpu_no:	CPU number * @cache_type:	Cache level L2/L3 * @cache_size:	pointer to cache_size * * Return: = 0 on success, < 0 on failure. */int get_cache_size(int cpu_no, char *cache_type, unsigned long *cache_size){	char cache_path[1024], cache_str[64];	int length, i, cache_num;	FILE *fp; 	if (!strcmp(cache_type, "L3")) {		cache_num = 3;	} else if (!strcmp(cache_type, "L2")) {		cache_num = 2;	} else {		perror("Invalid cache level");		return -1;	} 	sprintf(cache_path, "/sys/bus/cpu/devices/cpu%d/cache/index%d/size",		cpu_no, cache_num);	fp = fopen(cache_path, "r");	if (!fp) {		perror("Failed to open cache size"); 		return -1;	}	if (fscanf(fp, "%s", cache_str) <= 0) {		perror("Could not get cache_size");		fclose(fp); 		return -1;	}	fclose(fp); 	length = (int)strlen(cache_str); 	*cache_size = 0; 	for (i = 0; i < length; i++) {		if ((cache_str[i] >= '0') && (cache_str[i] <= '9')) 			*cache_size = *cache_size * 10 + (cache_str[i] - '0'); 		else if (cache_str[i] == 'K') 			*cache_size = *cache_size * 1024; 		else if (cache_str[i] == 'M') 			*cache_size = *cache_size * 1024 * 1024; 		else			break;	} 	return 0;} #define CORE_SIBLINGS_PATH	"/sys/bus/cpu/devices/cpu" /* * get_cbm_mask - Get cbm mask for given cache * @cache_type:	Cache level L2/L3 * @cbm_mask:	cbm_mask returned as a string * * Return: = 0 on success, < 0 on failure. */int get_cbm_mask(char *cache_type, char *cbm_mask){	char cbm_mask_path[1024];	FILE *fp; 	if (!cbm_mask)		return -1; 	sprintf(cbm_mask_path, "%s/%s/cbm_mask", CBM_MASK_PATH, cache_type); 	fp = fopen(cbm_mask_path, "r");	if (!fp) {		perror("Failed to open cache level"); 		return -1;	}	if (fscanf(fp, "%s", cbm_mask) <= 0) {		perror("Could not get max cbm_mask");		fclose(fp); 		return -1;	}	fclose(fp); 	return 0;} /* * get_core_sibling - Get sibling core id from the same socket for given CPU * @cpu_no:	CPU number * * Return:	> 0 on success, < 0 on failure. */int get_core_sibling(int cpu_no){	char core_siblings_path[1024], cpu_list_str[64];	int sibling_cpu_no = -1;	FILE *fp; 	sprintf(core_siblings_path, "%s%d/topology/core_siblings_list",		CORE_SIBLINGS_PATH, cpu_no); 	fp = fopen(core_siblings_path, "r");	if (!fp) {		perror("Failed to open core siblings path"); 		return -1;	}	if (fscanf(fp, "%s", cpu_list_str) <= 0) {		perror("Could not get core_siblings list");		fclose(fp); 		return -1;	}	fclose(fp); 	char *token = strtok(cpu_list_str, "-,"); 	while (token) {		sibling_cpu_no = atoi(token);		/* Skipping core 0 as we don't want to run test on core 0 */		if (sibling_cpu_no != 0 && sibling_cpu_no != cpu_no)			break;		token = strtok(NULL, "-,");	} 	return sibling_cpu_no;} /* * taskset_benchmark - Taskset PID (i.e. benchmark) to a specified cpu * @bm_pid:	PID that should be binded * @cpu_no:	CPU number at which the PID would be binded * * Return: 0 on success, non-zero on failure */int taskset_benchmark(pid_t bm_pid, int cpu_no){	cpu_set_t my_set; 	CPU_ZERO(&my_set);	CPU_SET(cpu_no, &my_set); 	if (sched_setaffinity(bm_pid, sizeof(cpu_set_t), &my_set)) {		perror("Unable to taskset benchmark"); 		return -1;	} 	return 0;} /* * run_benchmark - Run a specified benchmark or fill_buf (default benchmark) *		   in specified signal. Direct benchmark stdio to /dev/null. * @signum:	signal number * @info:	signal info * @ucontext:	user context in signal handling * * Return: void */void run_benchmark(int signum, siginfo_t *info, void *ucontext){	int operation, ret, malloc_and_init_memory, memflush;	unsigned long span, buffer_span;	char **benchmark_cmd;	char resctrl_val[64];	FILE *fp; 	benchmark_cmd = info->si_ptr; 	/*	 * Direct stdio of child to /dev/null, so that only parent writes to	 * stdio (console)	 */	fp = freopen("/dev/null", "w", stdout);	if (!fp)		PARENT_EXIT("Unable to direct benchmark status to /dev/null"); 	if (strcmp(benchmark_cmd[0], "fill_buf") == 0) {		/* Execute default fill_buf benchmark */		span = strtoul(benchmark_cmd[1], NULL, 10);		malloc_and_init_memory = atoi(benchmark_cmd[2]);		memflush =  atoi(benchmark_cmd[3]);		operation = atoi(benchmark_cmd[4]);		sprintf(resctrl_val, "%s", benchmark_cmd[5]); 		if (strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)))			buffer_span = span * MB;		else			buffer_span = span; 		if (run_fill_buf(buffer_span, malloc_and_init_memory, memflush,				 operation, resctrl_val))			fprintf(stderr, "Error in running fill buffer\n");	} else {		/* Execute specified benchmark */		ret = execvp(benchmark_cmd[0], benchmark_cmd);		if (ret)			perror("wrong\n");	} 	fclose(stdout);	PARENT_EXIT("Unable to run specified benchmark");} /* * create_grp - Create a group only if one doesn't exist * @grp_name:	Name of the group * @grp:	Full path and name of the group * @parent_grp:	Full path and name of the parent group * * Return: 0 on success, non-zero on failure */static int create_grp(const char *grp_name, char *grp, const char *parent_grp){	int found_grp = 0;	struct dirent *ep;	DIR *dp; 	/*	 * At this point, we are guaranteed to have resctrl FS mounted and if	 * length of grp_name == 0, it means, user wants to use root con_mon	 * grp, so do nothing	 */	if (strlen(grp_name) == 0)		return 0; 	/* Check if requested grp exists or not */	dp = opendir(parent_grp);	if (dp) {		while ((ep = readdir(dp)) != NULL) {			if (strcmp(ep->d_name, grp_name) == 0)				found_grp = 1;		}		closedir(dp);	} else {		perror("Unable to open resctrl for group"); 		return -1;	} 	/* Requested grp doesn't exist, hence create it */	if (found_grp == 0) {		if (mkdir(grp, 0) == -1) {			perror("Unable to create group"); 			return -1;		}	} 	return 0;} static int write_pid_to_tasks(char *tasks, pid_t pid){	FILE *fp; 	fp = fopen(tasks, "w");	if (!fp) {		perror("Failed to open tasks file"); 		return -1;	}	if (fprintf(fp, "%d\n", pid) < 0) {		perror("Failed to wr pid to tasks file");		fclose(fp); 		return -1;	}	fclose(fp); 	return 0;} /* * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS * @bm_pid:		PID that should be written * @ctrlgrp:		Name of the control monitor group (con_mon grp) * @mongrp:		Name of the monitor group (mon grp) * @resctrl_val:	Resctrl feature (Eg: mbm, mba.. etc) * * If a con_mon grp is requested, create it and write pid to it, otherwise * write pid to root con_mon grp. * If a mon grp is requested, create it and write pid to it, otherwise * pid is not written, this means that pid is in con_mon grp and hence * should consult con_mon grp's mon_data directory for results. * * Return: 0 on success, non-zero on failure */int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp,			    char *resctrl_val){	char controlgroup[128], monitorgroup[512], monitorgroup_p[256];	char tasks[1024];	int ret = 0; 	if (strlen(ctrlgrp))		sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp);	else		sprintf(controlgroup, "%s", RESCTRL_PATH); 	/* Create control and monitoring group and write pid into it */	ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH);	if (ret)		goto out;	sprintf(tasks, "%s/tasks", controlgroup);	ret = write_pid_to_tasks(tasks, bm_pid);	if (ret)		goto out; 	/* Create mon grp and write pid into it for "mbm" and "cmt" test */	if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)) ||	    !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) {		if (strlen(mongrp)) {			sprintf(monitorgroup_p, "%s/mon_groups", controlgroup);			sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp);			ret = create_grp(mongrp, monitorgroup, monitorgroup_p);			if (ret)				goto out; 			sprintf(tasks, "%s/mon_groups/%s/tasks",				controlgroup, mongrp);			ret = write_pid_to_tasks(tasks, bm_pid);			if (ret)				goto out;		}	} out:	ksft_print_msg("Writing benchmark parameters to resctrl FS\n");	if (ret)		perror("# writing to resctrlfs"); 	return ret;} /* * write_schemata - Update schemata of a con_mon grp * @ctrlgrp:		Name of the con_mon grp * @schemata:		Schemata that should be updated to * @cpu_no:		CPU number that the benchmark PID is binded to * @resctrl_val:	Resctrl feature (Eg: mbm, mba.. etc) * * Update schemata of a con_mon grp *only* if requested resctrl feature is * allocation type * * Return: 0 on success, non-zero on failure */int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, char *resctrl_val){	char controlgroup[1024], schema[1024], reason[64];	int resource_id, ret = 0;	FILE *fp; 	if (strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) &&

Lines 1–500 of 746.