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 <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_PATH 32 #define MAX_PATH 1024 33 #endif 34 35 #define UNCORE_IIO_PMU_PATH "devices/uncore_iio_%d" 36 #define SYSFS_UNCORE_PMU_PATH "%s/"UNCORE_IIO_PMU_PATH 37 #define PLATFORM_MAPPING_PATH UNCORE_IIO_PMU_PATH"/die%d" 38 39 /* 40 * Each metric requiries one IIO event which increments at every 4B transfer 41 * in corresponding direction. The formulas to compute metrics are generic: 42 * #EventCount * 4B / (1024 * 1024) 43 */ 44 static const char * const iostat_metrics[] = { 45 "Inbound Read(MB)", 46 "Inbound Write(MB)", 47 "Outbound Read(MB)", 48 "Outbound Write(MB)", 49 }; 50 51 static inline int iostat_metrics_count(void) 52 { 53 return sizeof(iostat_metrics) / sizeof(char *); 54 } 55 56 static const char *iostat_metric_by_idx(int idx) 57 { 58 return *(iostat_metrics + idx % iostat_metrics_count()); 59 } 60 61 struct iio_root_port { 62 u32 domain; 63 u8 bus; 64 u8 die; 65 u8 pmu_idx; 66 int idx; 67 }; 68 69 struct iio_root_ports_list { 70 struct iio_root_port **rps; 71 int nr_entries; 72 }; 73 74 static struct iio_root_ports_list *root_ports; 75 76 static 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 84 static 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 98 static 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 110 static 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 126 static 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 145 static 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 } 176 out: 177 return 0; 178 } 179 180 static 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 197 static 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 } 217 err: 218 if (!ret) 219 *list = tmp_list; 220 else 221 iio_root_ports_list_free(tmp_list); 222 223 return ret; 224 } 225 226 static 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:7 235 */ 236 regcomp(®ex, "^([a-f0-9A-F]{1,}):([a-f0-9A-F]{1,2})", REG_EXTENDED); 237 ret = regexec(®ex, 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(®ex); 245 return ret; 246 } 247 248 static 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 } 287 err: 288 iio_root_ports_list_free(*list); 289 if (ret) 290 iio_root_ports_list_free(tmp_list); 291 else 292 *list = tmp_list; 293 294 free(filter_copy); 295 return ret; 296 } 297 298 static 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; 329 err: 330 iio_root_ports_list_free(list); 331 free(iostat_cmd); 332 return ret; 333 } 334 335 int 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 352 int 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 373 void 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 386 void 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 399 void 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 else 412 sprintf(prefix, "%04x:%02x%s", rp->domain, rp->bus, 413 config->csv_sep); 414 } 415 } 416 417 void 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 else 424 fprintf(config->output, " port "); 425 } 426 427 void 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, METRIC_THRESHOLD_UNKNOWN, "%8.0f", iostat_metric, 448 iostat_value / (256 * 1024)); 449 } 450 451 void 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