linux/tools/perf/arch/x86/util/iostat.c
<<
>>
Prefs
   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}
 471