708 lines · 17.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Memory bandwidth monitoring and allocation library * * Copyright (C) 2018 Intel Corporation * * Authors: *    Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>, *    Fenghua Yu <fenghua.yu@intel.com> */#include "resctrl.h" #define UNCORE_IMC		"uncore_imc"#define READ_FILE_NAME		"cas_count_read"#define DYN_PMU_PATH		"/sys/bus/event_source/devices"#define SCALE			0.00006103515625#define MAX_IMCS		40#define MAX_TOKENS		5 #define CON_MBM_LOCAL_BYTES_PATH		\	"%s/%s/mon_data/mon_L3_%02d/mbm_local_bytes" struct membw_read_format {	__u64 value;         /* The value of the event */	__u64 time_enabled;  /* if PERF_FORMAT_TOTAL_TIME_ENABLED */	__u64 time_running;  /* if PERF_FORMAT_TOTAL_TIME_RUNNING */	__u64 id;            /* if PERF_FORMAT_ID */}; struct imc_counter_config {	__u32 type;	__u64 event;	__u64 umask;	struct perf_event_attr pe;	int fd;}; static char mbm_total_path[1024];static int imcs;static struct imc_counter_config imc_counters_config[MAX_IMCS];static const struct resctrl_test *current_test; static void read_mem_bw_initialize_perf_event_attr(int i){	memset(&imc_counters_config[i].pe, 0,	       sizeof(struct perf_event_attr));	imc_counters_config[i].pe.type = imc_counters_config[i].type;	imc_counters_config[i].pe.size = sizeof(struct perf_event_attr);	imc_counters_config[i].pe.disabled = 1;	imc_counters_config[i].pe.inherit = 1;	imc_counters_config[i].pe.exclude_guest = 0;	imc_counters_config[i].pe.config =		imc_counters_config[i].umask << 8 |		imc_counters_config[i].event;	imc_counters_config[i].pe.sample_type = PERF_SAMPLE_IDENTIFIER;	imc_counters_config[i].pe.read_format =		PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;} static void read_mem_bw_ioctl_perf_event_ioc_reset_enable(int i){	ioctl(imc_counters_config[i].fd, PERF_EVENT_IOC_RESET, 0);	ioctl(imc_counters_config[i].fd, PERF_EVENT_IOC_ENABLE, 0);} static void read_mem_bw_ioctl_perf_event_ioc_disable(int i){	ioctl(imc_counters_config[i].fd, PERF_EVENT_IOC_DISABLE, 0);} /* * get_read_event_and_umask:	Parse config into event and umask * @cas_count_cfg:	Config * @count:		iMC number */static void get_read_event_and_umask(char *cas_count_cfg, unsigned int count){	char *token[MAX_TOKENS];	int i = 0; 	token[0] = strtok(cas_count_cfg, "=,"); 	for (i = 1; i < MAX_TOKENS; i++)		token[i] = strtok(NULL, "=,"); 	for (i = 0; i < MAX_TOKENS - 1; i++) {		if (!token[i])			break;		if (strcmp(token[i], "event") == 0)			imc_counters_config[count].event = strtol(token[i + 1], NULL, 16);		if (strcmp(token[i], "umask") == 0)			imc_counters_config[count].umask = strtol(token[i + 1], NULL, 16);	}} static int open_perf_read_event(int i, int cpu_no){	imc_counters_config[i].fd =		perf_event_open(&imc_counters_config[i].pe, -1, cpu_no, -1,				PERF_FLAG_FD_CLOEXEC); 	if (imc_counters_config[i].fd == -1) {		fprintf(stderr, "Error opening leader %llx\n",			imc_counters_config[i].pe.config); 		return -1;	} 	return 0;} static int parse_imc_read_bw_events(char *imc_dir, unsigned int type,				    unsigned int *count){	char imc_events_dir[PATH_MAX], imc_counter_cfg[PATH_MAX];	unsigned int orig_count = *count;	char cas_count_cfg[1024];	struct dirent *ep;	int path_len;	int ret = -1;	int num_cfg;	FILE *fp;	DIR *dp; 	path_len = snprintf(imc_events_dir, sizeof(imc_events_dir), "%sevents",			    imc_dir);	if (path_len >= sizeof(imc_events_dir)) {		ksft_print_msg("Unable to create path to %sevents\n", imc_dir);		return -1;	} 	dp = opendir(imc_events_dir);	if (!dp) {		ksft_perror("Unable to open PMU events directory");		return -1;	} 	while ((ep = readdir(dp))) {		/*		 * Parse all event files with READ_FILE_NAME prefix that		 * contain the event number and umask. Skip files containing		 * "." that contain unused properties of event.		 */		if (!strstr(ep->d_name, READ_FILE_NAME) ||		    strchr(ep->d_name, '.'))			continue; 		path_len = snprintf(imc_counter_cfg, sizeof(imc_counter_cfg),				    "%s/%s", imc_events_dir, ep->d_name);		if (path_len >= sizeof(imc_counter_cfg)) {			ksft_print_msg("Unable to create path to %s/%s\n",				       imc_events_dir, ep->d_name);			goto out_close;		}		fp = fopen(imc_counter_cfg, "r");		if (!fp) {			ksft_perror("Failed to open iMC config file");			goto out_close;		}		num_cfg = fscanf(fp, "%1023s", cas_count_cfg);		fclose(fp);		if (num_cfg <= 0) {			ksft_perror("Could not get iMC cas count read");			goto out_close;		}		if (*count >= MAX_IMCS) {			ksft_print_msg("Maximum iMC count exceeded\n");			goto out_close;		} 		imc_counters_config[*count].type = type;		get_read_event_and_umask(cas_count_cfg, *count);		/* Do not fail after incrementing *count. */		*count += 1;	}	if (*count == orig_count) {		ksft_print_msg("Unable to find events in %s\n", imc_events_dir);		goto out_close;	}	ret = 0;out_close:	closedir(dp);	return ret;} /* Get type and config of an iMC counter's read event. */static int read_from_imc_dir(char *imc_dir, unsigned int *count){	char imc_counter_type[PATH_MAX];	unsigned int type;	int path_len;	FILE *fp;	int ret; 	/* Get type of iMC counter */	path_len = snprintf(imc_counter_type, sizeof(imc_counter_type),			    "%s%s", imc_dir, "type");	if (path_len >= sizeof(imc_counter_type)) {		ksft_print_msg("Unable to create path to %s%s\n",			       imc_dir, "type");		return -1;	}	fp = fopen(imc_counter_type, "r");	if (!fp) {		ksft_perror("Failed to open iMC counter type file"); 		return -1;	}	ret = fscanf(fp, "%u", &type);	fclose(fp);	if (ret <= 0) {		ksft_perror("Could not get iMC type");		return -1;	}	ret = parse_imc_read_bw_events(imc_dir, type, count);	if (ret) {		ksft_print_msg("Unable to parse bandwidth event and umask\n");		return ret;	} 	return 0;} /* * A system can have 'n' number of iMC (Integrated Memory Controller) * counters, get that 'n'. Discover the properties of the available * counters in support of needed performance measurement via perf. * For each iMC counter get it's type and config. Also obtain each * counter's event and umask for the memory read events that will be * measured. * * Enumerate all these details into an array of structures. * * Return: >= 0 on success. < 0 on failure. */static int num_of_imcs(void){	char imc_dir[512], *temp;	unsigned int count = 0;	struct dirent *ep;	int ret;	DIR *dp; 	dp = opendir(DYN_PMU_PATH);	if (dp) {		while ((ep = readdir(dp))) {			temp = strstr(ep->d_name, UNCORE_IMC);			if (!temp)				continue; 			/*			 * imc counters are named as "uncore_imc_<n>", hence			 * increment the pointer to point to <n>. Note that			 * sizeof(UNCORE_IMC) would count for null character as			 * well and hence the last underscore character in			 * uncore_imc'_' need not be counted.			 */			temp = temp + sizeof(UNCORE_IMC); 			/*			 * Some directories under "DYN_PMU_PATH" could have			 * names like "uncore_imc_free_running", hence, check if			 * first character is a numerical digit or not.			 */			if (temp[0] >= '0' && temp[0] <= '9') {				sprintf(imc_dir, "%s/%s/", DYN_PMU_PATH,					ep->d_name);				ret = read_from_imc_dir(imc_dir, &count);				if (ret) {					closedir(dp); 					return ret;				}			}		}		closedir(dp);		if (count == 0) {			ksft_print_msg("Unable to find iMC counters\n"); 			return -1;		}	} else {		ksft_perror("Unable to open PMU directory"); 		return -1;	} 	return count;} int initialize_read_mem_bw_imc(void){	int imc; 	imcs = num_of_imcs();	if (imcs <= 0)		return imcs; 	/* Initialize perf_event_attr structures for all iMC's */	for (imc = 0; imc < imcs; imc++)		read_mem_bw_initialize_perf_event_attr(imc); 	return 0;} static void perf_close_imc_read_mem_bw(void){	int mc; 	for (mc = 0; mc < imcs; mc++) {		if (imc_counters_config[mc].fd != -1)			close(imc_counters_config[mc].fd);	}} /* * perf_open_imc_read_mem_bw - Open perf fds for IMCs * @cpu_no: CPU number that the benchmark PID is bound to * * Return: = 0 on success. < 0 on failure. */static int perf_open_imc_read_mem_bw(int cpu_no){	int imc, ret; 	for (imc = 0; imc < imcs; imc++)		imc_counters_config[imc].fd = -1; 	for (imc = 0; imc < imcs; imc++) {		ret = open_perf_read_event(imc, cpu_no);		if (ret)			goto close_fds;	} 	return 0; close_fds:	perf_close_imc_read_mem_bw();	return -1;} /* * do_imc_read_mem_bw_test - Perform memory bandwidth test * * Runs memory bandwidth test over one second period. Also, handles starting * and stopping of the IMC perf counters around the test. */static void do_imc_read_mem_bw_test(void){	int imc; 	for (imc = 0; imc < imcs; imc++)		read_mem_bw_ioctl_perf_event_ioc_reset_enable(imc); 	sleep(1); 	/* Stop counters after a second to get results. */	for (imc = 0; imc < imcs; imc++)		read_mem_bw_ioctl_perf_event_ioc_disable(imc);} /* * get_read_mem_bw_imc - Memory read bandwidth as reported by iMC counters * * Memory read bandwidth utilized by a process on a socket can be calculated * using iMC counters' read events. Perf events are used to read these * counters. * * Return: = 0 on success. < 0 on failure. */static int get_read_mem_bw_imc(float *bw_imc){	float reads = 0, of_mul_read = 1;	int imc; 	/*	 * Log read event values from all iMC counters into	 * struct imc_counter_config.	 * Take overflow into consideration before calculating total bandwidth.	 */	for (imc = 0; imc < imcs; imc++) {		struct membw_read_format measurement;		struct imc_counter_config *r =			&imc_counters_config[imc]; 		if (read(r->fd, &measurement, sizeof(measurement)) == -1) {			ksft_perror("Couldn't get read bandwidth through iMC");			return -1;		} 		__u64 r_time_enabled = measurement.time_enabled;		__u64 r_time_running = measurement.time_running; 		if (r_time_enabled != r_time_running)			of_mul_read = (float)r_time_enabled /					(float)r_time_running; 		reads += measurement.value * of_mul_read * SCALE;	} 	*bw_imc = reads;	return 0;} /* * initialize_mem_bw_resctrl:	Appropriately populate "mbm_total_path" * @param:	Parameters passed to resctrl_val() * @domain_id:	Domain ID (cache ID; for MB, L3 cache ID) */void initialize_mem_bw_resctrl(const struct resctrl_val_param *param,			       int domain_id){	sprintf(mbm_total_path, CON_MBM_LOCAL_BYTES_PATH, RESCTRL_PATH,		param->ctrlgrp, domain_id);} /* * Open file to read MBM local bytes from resctrl FS */static FILE *open_mem_bw_resctrl(const char *mbm_bw_file){	FILE *fp; 	fp = fopen(mbm_bw_file, "r");	if (!fp)		ksft_perror("Failed to open total memory bandwidth file"); 	return fp;} /* * Get MBM Local bytes as reported by resctrl FS */static int get_mem_bw_resctrl(FILE *fp, unsigned long *mbm_total){	if (fscanf(fp, "%lu\n", mbm_total) <= 0) {		ksft_perror("Could not get MBM local bytes");		return -1;	}	return 0;} static pid_t bm_pid; void ctrlc_handler(int signum, siginfo_t *info, void *ptr){	/* Only kill child after bm_pid is set after fork() */	if (bm_pid)		kill(bm_pid, SIGKILL);	umount_resctrlfs();	if (current_test && current_test->cleanup)		current_test->cleanup();	ksft_print_msg("Ending\n\n"); 	exit(EXIT_SUCCESS);} /* * Register CTRL-C handler for parent, as it has to kill * child process before exiting. */int signal_handler_register(const struct resctrl_test *test){	struct sigaction sigact = {};	int ret = 0; 	bm_pid = 0; 	current_test = test;	sigact.sa_sigaction = ctrlc_handler;	sigemptyset(&sigact.sa_mask);	sigact.sa_flags = SA_SIGINFO;	if (sigaction(SIGINT, &sigact, NULL) ||	    sigaction(SIGTERM, &sigact, NULL) ||	    sigaction(SIGHUP, &sigact, NULL)) {		ksft_perror("sigaction");		ret = -1;	}	return ret;} /* * Reset signal handler to SIG_DFL. * Non-Value return because the caller should keep * the error code of other path even if sigaction fails. */void signal_handler_unregister(void){	struct sigaction sigact = {}; 	current_test = NULL;	sigact.sa_handler = SIG_DFL;	sigemptyset(&sigact.sa_mask);	if (sigaction(SIGINT, &sigact, NULL) ||	    sigaction(SIGTERM, &sigact, NULL) ||	    sigaction(SIGHUP, &sigact, NULL)) {		ksft_perror("sigaction");	}} 

Lines 1–500 of 708.