cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

iostat.c (11132B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * perf iostat
      4 *
      5 * Copyright (C) 2020, Intel Corporation
      6 *
      7 * Authors: Alexander Antonov <alexander.antonov@linux.intel.com>
      8 */
      9
     10#include <api/fs/fs.h>
     11#include <linux/kernel.h>
     12#include <linux/err.h>
     13#include <limits.h>
     14#include <stdio.h>
     15#include <string.h>
     16#include <errno.h>
     17#include <sys/types.h>
     18#include <sys/stat.h>
     19#include <fcntl.h>
     20#include <dirent.h>
     21#include <unistd.h>
     22#include <stdlib.h>
     23#include <regex.h>
     24#include "util/cpumap.h"
     25#include "util/debug.h"
     26#include "util/iostat.h"
     27#include "util/counts.h"
     28#include "path.h"
     29
     30#ifndef MAX_PATH
     31#define MAX_PATH 1024
     32#endif
     33
     34#define UNCORE_IIO_PMU_PATH	"devices/uncore_iio_%d"
     35#define SYSFS_UNCORE_PMU_PATH	"%s/"UNCORE_IIO_PMU_PATH
     36#define PLATFORM_MAPPING_PATH	UNCORE_IIO_PMU_PATH"/die%d"
     37
     38/*
     39 * Each metric requiries one IIO event which increments at every 4B transfer
     40 * in corresponding direction. The formulas to compute metrics are generic:
     41 *     #EventCount * 4B / (1024 * 1024)
     42 */
     43static const char * const iostat_metrics[] = {
     44	"Inbound Read(MB)",
     45	"Inbound Write(MB)",
     46	"Outbound Read(MB)",
     47	"Outbound Write(MB)",
     48};
     49
     50static inline int iostat_metrics_count(void)
     51{
     52	return sizeof(iostat_metrics) / sizeof(char *);
     53}
     54
     55static const char *iostat_metric_by_idx(int idx)
     56{
     57	return *(iostat_metrics + idx % iostat_metrics_count());
     58}
     59
     60struct iio_root_port {
     61	u32 domain;
     62	u8 bus;
     63	u8 die;
     64	u8 pmu_idx;
     65	int idx;
     66};
     67
     68struct iio_root_ports_list {
     69	struct iio_root_port **rps;
     70	int nr_entries;
     71};
     72
     73static struct iio_root_ports_list *root_ports;
     74
     75static void iio_root_port_show(FILE *output,
     76			       const struct iio_root_port * const rp)
     77{
     78	if (output && rp)
     79		fprintf(output, "S%d-uncore_iio_%d<%04x:%02x>\n",
     80			rp->die, rp->pmu_idx, rp->domain, rp->bus);
     81}
     82
     83static struct iio_root_port *iio_root_port_new(u32 domain, u8 bus,
     84					       u8 die, u8 pmu_idx)
     85{
     86	struct iio_root_port *p = calloc(1, sizeof(*p));
     87
     88	if (p) {
     89		p->domain = domain;
     90		p->bus = bus;
     91		p->die = die;
     92		p->pmu_idx = pmu_idx;
     93	}
     94	return p;
     95}
     96
     97static void iio_root_ports_list_free(struct iio_root_ports_list *list)
     98{
     99	int idx;
    100
    101	if (list) {
    102		for (idx = 0; idx < list->nr_entries; idx++)
    103			free(list->rps[idx]);
    104		free(list->rps);
    105		free(list);
    106	}
    107}
    108
    109static struct iio_root_port *iio_root_port_find_by_notation(
    110	const struct iio_root_ports_list * const list, u32 domain, u8 bus)
    111{
    112	int idx;
    113	struct iio_root_port *rp;
    114
    115	if (list) {
    116		for (idx = 0; idx < list->nr_entries; idx++) {
    117			rp = list->rps[idx];
    118			if (rp && rp->domain == domain && rp->bus == bus)
    119				return rp;
    120		}
    121	}
    122	return NULL;
    123}
    124
    125static int iio_root_ports_list_insert(struct iio_root_ports_list *list,
    126				      struct iio_root_port * const rp)
    127{
    128	struct iio_root_port **tmp_buf;
    129
    130	if (list && rp) {
    131		rp->idx = list->nr_entries++;
    132		tmp_buf = realloc(list->rps,
    133				  list->nr_entries * sizeof(*list->rps));
    134		if (!tmp_buf) {
    135			pr_err("Failed to realloc memory\n");
    136			return -ENOMEM;
    137		}
    138		tmp_buf[rp->idx] = rp;
    139		list->rps = tmp_buf;
    140	}
    141	return 0;
    142}
    143
    144static int iio_mapping(u8 pmu_idx, struct iio_root_ports_list * const list)
    145{
    146	char *buf;
    147	char path[MAX_PATH];
    148	u32 domain;
    149	u8 bus;
    150	struct iio_root_port *rp;
    151	size_t size;
    152	int ret;
    153
    154	for (int die = 0; die < cpu__max_node(); die++) {
    155		scnprintf(path, MAX_PATH, PLATFORM_MAPPING_PATH, pmu_idx, die);
    156		if (sysfs__read_str(path, &buf, &size) < 0) {
    157			if (pmu_idx)
    158				goto out;
    159			pr_err("Mode iostat is not supported\n");
    160			return -1;
    161		}
    162		ret = sscanf(buf, "%04x:%02hhx", &domain, &bus);
    163		free(buf);
    164		if (ret != 2) {
    165			pr_err("Invalid mapping data: iio_%d; die%d\n",
    166			       pmu_idx, die);
    167			return -1;
    168		}
    169		rp = iio_root_port_new(domain, bus, die, pmu_idx);
    170		if (!rp || iio_root_ports_list_insert(list, rp)) {
    171			free(rp);
    172			return -ENOMEM;
    173		}
    174	}
    175out:
    176	return 0;
    177}
    178
    179static u8 iio_pmu_count(void)
    180{
    181	u8 pmu_idx = 0;
    182	char path[MAX_PATH];
    183	const char *sysfs = sysfs__mountpoint();
    184
    185	if (sysfs) {
    186		for (;; pmu_idx++) {
    187			snprintf(path, sizeof(path), SYSFS_UNCORE_PMU_PATH,
    188				 sysfs, pmu_idx);
    189			if (access(path, F_OK) != 0)
    190				break;
    191		}
    192	}
    193	return pmu_idx;
    194}
    195
    196static int iio_root_ports_scan(struct iio_root_ports_list **list)
    197{
    198	int ret = -ENOMEM;
    199	struct iio_root_ports_list *tmp_list;
    200	u8 pmu_count = iio_pmu_count();
    201
    202	if (!pmu_count) {
    203		pr_err("Unsupported uncore pmu configuration\n");
    204		return -1;
    205	}
    206
    207	tmp_list = calloc(1, sizeof(*tmp_list));
    208	if (!tmp_list)
    209		goto err;
    210
    211	for (u8 pmu_idx = 0; pmu_idx < pmu_count; pmu_idx++) {
    212		ret = iio_mapping(pmu_idx, tmp_list);
    213		if (ret)
    214			break;
    215	}
    216err:
    217	if (!ret)
    218		*list = tmp_list;
    219	else
    220		iio_root_ports_list_free(tmp_list);
    221
    222	return ret;
    223}
    224
    225static int iio_root_port_parse_str(u32 *domain, u8 *bus, char *str)
    226{
    227	int ret;
    228	regex_t regex;
    229	/*
    230	 * Expected format domain:bus:
    231	 * Valid domain range [0:ffff]
    232	 * Valid bus range [0:ff]
    233	 * Example: 0000:af, 0:3d, 01:7
    234	 */
    235	regcomp(&regex, "^([a-f0-9A-F]{1,}):([a-f0-9A-F]{1,2})", REG_EXTENDED);
    236	ret = regexec(&regex, str, 0, NULL, 0);
    237	if (ret || sscanf(str, "%08x:%02hhx", domain, bus) != 2)
    238		pr_warning("Unrecognized root port format: %s\n"
    239			   "Please use the following format:\n"
    240			   "\t [domain]:[bus]\n"
    241			   "\t for example: 0000:3d\n", str);
    242
    243	regfree(&regex);
    244	return ret;
    245}
    246
    247static int iio_root_ports_list_filter(struct iio_root_ports_list **list,
    248				      const char *filter)
    249{
    250	char *tok, *tmp, *filter_copy = NULL;
    251	struct iio_root_port *rp;
    252	u32 domain;
    253	u8 bus;
    254	int ret = -ENOMEM;
    255	struct iio_root_ports_list *tmp_list = calloc(1, sizeof(*tmp_list));
    256
    257	if (!tmp_list)
    258		goto err;
    259
    260	filter_copy = strdup(filter);
    261	if (!filter_copy)
    262		goto err;
    263
    264	for (tok = strtok_r(filter_copy, ",", &tmp); tok;
    265	     tok = strtok_r(NULL, ",", &tmp)) {
    266		if (!iio_root_port_parse_str(&domain, &bus, tok)) {
    267			rp = iio_root_port_find_by_notation(*list, domain, bus);
    268			if (rp) {
    269				(*list)->rps[rp->idx] = NULL;
    270				ret = iio_root_ports_list_insert(tmp_list, rp);
    271				if (ret) {
    272					free(rp);
    273					goto err;
    274				}
    275			} else if (!iio_root_port_find_by_notation(tmp_list,
    276								   domain, bus))
    277				pr_warning("Root port %04x:%02x were not found\n",
    278					   domain, bus);
    279		}
    280	}
    281
    282	if (tmp_list->nr_entries == 0) {
    283		pr_err("Requested root ports were not found\n");
    284		ret = -EINVAL;
    285	}
    286err:
    287	iio_root_ports_list_free(*list);
    288	if (ret)
    289		iio_root_ports_list_free(tmp_list);
    290	else
    291		*list = tmp_list;
    292
    293	free(filter_copy);
    294	return ret;
    295}
    296
    297static int iostat_event_group(struct evlist *evl,
    298			      struct iio_root_ports_list *list)
    299{
    300	int ret;
    301	int idx;
    302	const char *iostat_cmd_template =
    303	"{uncore_iio_%x/event=0x83,umask=0x04,ch_mask=0xF,fc_mask=0x07/,\
    304	  uncore_iio_%x/event=0x83,umask=0x01,ch_mask=0xF,fc_mask=0x07/,\
    305	  uncore_iio_%x/event=0xc0,umask=0x04,ch_mask=0xF,fc_mask=0x07/,\
    306	  uncore_iio_%x/event=0xc0,umask=0x01,ch_mask=0xF,fc_mask=0x07/}";
    307	const int len_template = strlen(iostat_cmd_template) + 1;
    308	struct evsel *evsel = NULL;
    309	int metrics_count = iostat_metrics_count();
    310	char *iostat_cmd = calloc(len_template, 1);
    311
    312	if (!iostat_cmd)
    313		return -ENOMEM;
    314
    315	for (idx = 0; idx < list->nr_entries; idx++) {
    316		sprintf(iostat_cmd, iostat_cmd_template,
    317			list->rps[idx]->pmu_idx, list->rps[idx]->pmu_idx,
    318			list->rps[idx]->pmu_idx, list->rps[idx]->pmu_idx);
    319		ret = parse_events(evl, iostat_cmd, NULL);
    320		if (ret)
    321			goto err;
    322	}
    323
    324	evlist__for_each_entry(evl, evsel) {
    325		evsel->priv = list->rps[evsel->core.idx / metrics_count];
    326	}
    327	list->nr_entries = 0;
    328err:
    329	iio_root_ports_list_free(list);
    330	free(iostat_cmd);
    331	return ret;
    332}
    333
    334int iostat_prepare(struct evlist *evlist, struct perf_stat_config *config)
    335{
    336	if (evlist->core.nr_entries > 0) {
    337		pr_warning("The -e and -M options are not supported."
    338			   "All chosen events/metrics will be dropped\n");
    339		evlist__delete(evlist);
    340		evlist = evlist__new();
    341		if (!evlist)
    342			return -ENOMEM;
    343	}
    344
    345	config->metric_only = true;
    346	config->aggr_mode = AGGR_GLOBAL;
    347
    348	return iostat_event_group(evlist, root_ports);
    349}
    350
    351int iostat_parse(const struct option *opt, const char *str,
    352		 int unset __maybe_unused)
    353{
    354	int ret;
    355	struct perf_stat_config *config = (struct perf_stat_config *)opt->data;
    356
    357	ret = iio_root_ports_scan(&root_ports);
    358	if (!ret) {
    359		config->iostat_run = true;
    360		if (!str)
    361			iostat_mode = IOSTAT_RUN;
    362		else if (!strcmp(str, "list"))
    363			iostat_mode = IOSTAT_LIST;
    364		else {
    365			iostat_mode = IOSTAT_RUN;
    366			ret = iio_root_ports_list_filter(&root_ports, str);
    367		}
    368	}
    369	return ret;
    370}
    371
    372void iostat_list(struct evlist *evlist, struct perf_stat_config *config)
    373{
    374	struct evsel *evsel;
    375	struct iio_root_port *rp = NULL;
    376
    377	evlist__for_each_entry(evlist, evsel) {
    378		if (rp != evsel->priv) {
    379			rp = evsel->priv;
    380			iio_root_port_show(config->output, rp);
    381		}
    382	}
    383}
    384
    385void iostat_release(struct evlist *evlist)
    386{
    387	struct evsel *evsel;
    388	struct iio_root_port *rp = NULL;
    389
    390	evlist__for_each_entry(evlist, evsel) {
    391		if (rp != evsel->priv) {
    392			rp = evsel->priv;
    393			free(evsel->priv);
    394		}
    395	}
    396}
    397
    398void iostat_prefix(struct evlist *evlist,
    399		   struct perf_stat_config *config,
    400		   char *prefix, struct timespec *ts)
    401{
    402	struct iio_root_port *rp = evlist->selected->priv;
    403
    404	if (rp) {
    405		if (ts)
    406			sprintf(prefix, "%6lu.%09lu%s%04x:%02x%s",
    407				ts->tv_sec, ts->tv_nsec,
    408				config->csv_sep, rp->domain, rp->bus,
    409				config->csv_sep);
    410		else
    411			sprintf(prefix, "%04x:%02x%s", rp->domain, rp->bus,
    412				config->csv_sep);
    413	}
    414}
    415
    416void iostat_print_header_prefix(struct perf_stat_config *config)
    417{
    418	if (config->csv_output)
    419		fputs("port,", config->output);
    420	else if (config->interval)
    421		fprintf(config->output, "#          time    port         ");
    422	else
    423		fprintf(config->output, "   port         ");
    424}
    425
    426void iostat_print_metric(struct perf_stat_config *config, struct evsel *evsel,
    427			 struct perf_stat_output_ctx *out)
    428{
    429	double iostat_value = 0;
    430	u64 prev_count_val = 0;
    431	const char *iostat_metric = iostat_metric_by_idx(evsel->core.idx);
    432	u8 die = ((struct iio_root_port *)evsel->priv)->die;
    433	struct perf_counts_values *count = perf_counts(evsel->counts, die, 0);
    434
    435	if (count && count->run && count->ena) {
    436		if (evsel->prev_raw_counts && !out->force_header) {
    437			struct perf_counts_values *prev_count =
    438				perf_counts(evsel->prev_raw_counts, die, 0);
    439
    440			prev_count_val = prev_count->val;
    441			prev_count->val = count->val;
    442		}
    443		iostat_value = (count->val - prev_count_val) /
    444			       ((double) count->run / count->ena);
    445	}
    446	out->print_metric(config, out->ctx, NULL, "%8.0f", iostat_metric,
    447			  iostat_value / (256 * 1024));
    448}
    449
    450void iostat_print_counters(struct evlist *evlist,
    451			   struct perf_stat_config *config, struct timespec *ts,
    452			   char *prefix, iostat_print_counter_t print_cnt_cb)
    453{
    454	void *perf_device = NULL;
    455	struct evsel *counter = evlist__first(evlist);
    456
    457	evlist__set_selected(evlist, counter);
    458	iostat_prefix(evlist, config, prefix, ts);
    459	fprintf(config->output, "%s", prefix);
    460	evlist__for_each_entry(evlist, counter) {
    461		perf_device = evlist->selected->priv;
    462		if (perf_device && perf_device != counter->priv) {
    463			evlist__set_selected(evlist, counter);
    464			iostat_prefix(evlist, config, prefix, ts);
    465			fprintf(config->output, "\n%s", prefix);
    466		}
    467		print_cnt_cb(config, counter, prefix);
    468	}
    469	fputc('\n', config->output);
    470}