brintos

brintos / linux-shallow public Read only

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