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 */
};
/* * get_read_event_and_umask: Parse config into event and umask * @cas_count_cfg: Config * @count: iMC number
*/ staticvoid get_read_event_and_umask(char *cas_count_cfg, 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);
}
}
staticint 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);
/* Get type and config of an iMC counter's read event. */ staticint read_from_imc_dir(char *imc_dir, int count)
{ char cas_count_cfg[1024], imc_counter_cfg[1024], imc_counter_type[1024];
FILE *fp;
/* Get type of iMC counter */
sprintf(imc_counter_type, "%s%s", imc_dir, "type");
fp = fopen(imc_counter_type, "r"); if (!fp) {
ksft_perror("Failed to open iMC counter type file");
return -1;
} if (fscanf(fp, "%u", &imc_counters_config[count].type) <= 0) {
ksft_perror("Could not get iMC type");
fclose(fp);
return -1;
}
fclose(fp);
/* Get read config */
sprintf(imc_counter_cfg, "%s%s", imc_dir, READ_FILE_NAME);
fp = fopen(imc_counter_cfg, "r"); if (!fp) {
ksft_perror("Failed to open iMC config file");
return -1;
} if (fscanf(fp, "%1023s", cas_count_cfg) <= 0) {
ksft_perror("Could not get iMC cas count read");
fclose(fp);
return -1;
}
fclose(fp);
get_read_event_and_umask(cas_count_cfg, count);
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.
*/ staticint num_of_imcs(void)
{ char imc_dir[512], *temp; unsignedint 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;
}
count++;
}
}
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;
}
staticvoid 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.
*/ staticint perf_open_imc_read_mem_bw(int cpu_no)
{ int imc, ret;
/* * 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.
*/ staticvoid 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.
*/ staticint 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 imc_counter_config *r =
&imc_counters_config[imc];
if (read(r->fd, &r->return_value, sizeof(struct membw_read_format)) == -1) {
ksft_perror("Couldn't get read bandwidth through iMC"); return -1;
}
/* * 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(conststruct 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(constchar *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
*/ staticint get_mem_bw_resctrl(FILE *fp, unsignedlong *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(conststruct resctrl_test *test)
{ struct sigaction sigact = {}; int ret = 0;
/* * 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 = {};
/* * measure_read_mem_bw - Measures read memory bandwidth numbers while benchmark runs * @uparams: User supplied parameters * @param: Parameters passed to resctrl_val() * @bm_pid: PID that runs the benchmark * * Measure memory bandwidth from resctrl and from another source which is * perf imc value or could be something else if perf imc event is not * available. Compare the two values to validate resctrl value. It takes * 1 sec to measure the data. * resctrl does not distinguish between read and write operations so * its data includes all memory operations.
*/ int measure_read_mem_bw(conststruct user_params *uparams, struct resctrl_val_param *param, pid_t bm_pid)
{ unsignedlong bw_resc, bw_resc_start, bw_resc_end;
FILE *mem_bw_fp; float bw_imc; int ret;
mem_bw_fp = open_mem_bw_resctrl(mbm_total_path); if (!mem_bw_fp) return -1;
ret = perf_open_imc_read_mem_bw(uparams->cpu); if (ret < 0) goto close_fp;
ret = get_mem_bw_resctrl(mem_bw_fp, &bw_resc_start); if (ret < 0) goto close_imc;
rewind(mem_bw_fp);
do_imc_read_mem_bw_test();
ret = get_mem_bw_resctrl(mem_bw_fp, &bw_resc_end); if (ret < 0) goto close_imc;
ret = get_read_mem_bw_imc(&bw_imc); if (ret < 0) goto close_imc;
/* * resctrl_val: execute benchmark and measure memory bandwidth on * the benchmark * @test: test information structure * @uparams: user supplied parameters * @param: parameters passed to resctrl_val() * * Return: 0 when the test was run, < 0 on error.
*/ int resctrl_val(conststruct resctrl_test *test, conststruct user_params *uparams, struct resctrl_val_param *param)
{ unsignedchar *buf = NULL;
cpu_set_t old_affinity; int domain_id; int ret = 0;
pid_t ppid;
if (strcmp(param->filename, "") == 0)
sprintf(param->filename, "stdio");
ret = get_domain_id(test->resource, uparams->cpu, &domain_id); if (ret < 0) {
ksft_print_msg("Could not get domain ID\n"); return ret;
}
ppid = getpid();
/* Taskset test to specified CPU. */
ret = taskset_benchmark(ppid, uparams->cpu, &old_affinity); if (ret) return ret;
/* Write test to specified control & monitoring group in resctrl FS. */
ret = write_bm_pid_to_resctrl(ppid, param->ctrlgrp, param->mongrp); if (ret) goto reset_affinity;
if (param->init) {
ret = param->init(param, domain_id); if (ret) goto reset_affinity;
}
/* * If not running user provided benchmark, run the default * "fill_buf". First phase of "fill_buf" is to prepare the * buffer that the benchmark will operate on. No measurements * are needed during this phase and prepared memory will be * passed to next part of benchmark via copy-on-write thus * no impact on the benchmark that relies on reading from * memory only.
*/ if (param->fill_buf) {
buf = alloc_buffer(param->fill_buf->buf_size,
param->fill_buf->memflush); if (!buf) {
ret = -ENOMEM; goto reset_affinity;
}
}
fflush(stdout);
bm_pid = fork(); if (bm_pid == -1) {
ret = -errno;
ksft_perror("Unable to fork"); goto free_buf;
}
/* * What needs to be measured runs in separate process until * terminated.
*/ if (bm_pid == 0) { if (param->fill_buf)
fill_cache_read(buf, param->fill_buf->buf_size, false); elseif (uparams->benchmark_cmd[0])
execvp(uparams->benchmark_cmd[0], (char **)uparams->benchmark_cmd); exit(EXIT_SUCCESS);
}
/* Give benchmark enough time to fully run. */
sleep(1);
/* Test runs until the callback setup() tells the test to stop. */ while (1) {
ret = param->setup(test, uparams, param); if (ret == END_OF_TESTS) {
ret = 0; break;
} if (ret < 0) break;
ret = param->measure(uparams, param, bm_pid); if (ret) break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.