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(®ex, "^([a-f0-9A-F]{1,}):([a-f0-9A-F]{1,2})", REG_EXTENDED); 236 ret = regexec(®ex, 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(®ex); 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}