Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * perf iostat
0004  *
0005  * Copyright (C) 2020, Intel Corporation
0006  *
0007  * Authors: Alexander Antonov <alexander.antonov@linux.intel.com>
0008  */
0009 
0010 #include <api/fs/fs.h>
0011 #include <linux/kernel.h>
0012 #include <linux/err.h>
0013 #include <limits.h>
0014 #include <stdio.h>
0015 #include <string.h>
0016 #include <errno.h>
0017 #include <sys/types.h>
0018 #include <sys/stat.h>
0019 #include <fcntl.h>
0020 #include <dirent.h>
0021 #include <unistd.h>
0022 #include <stdlib.h>
0023 #include <regex.h>
0024 #include "util/cpumap.h"
0025 #include "util/debug.h"
0026 #include "util/iostat.h"
0027 #include "util/counts.h"
0028 #include "path.h"
0029 
0030 #ifndef MAX_PATH
0031 #define MAX_PATH 1024
0032 #endif
0033 
0034 #define UNCORE_IIO_PMU_PATH "devices/uncore_iio_%d"
0035 #define SYSFS_UNCORE_PMU_PATH   "%s/"UNCORE_IIO_PMU_PATH
0036 #define PLATFORM_MAPPING_PATH   UNCORE_IIO_PMU_PATH"/die%d"
0037 
0038 /*
0039  * Each metric requiries one IIO event which increments at every 4B transfer
0040  * in corresponding direction. The formulas to compute metrics are generic:
0041  *     #EventCount * 4B / (1024 * 1024)
0042  */
0043 static const char * const iostat_metrics[] = {
0044     "Inbound Read(MB)",
0045     "Inbound Write(MB)",
0046     "Outbound Read(MB)",
0047     "Outbound Write(MB)",
0048 };
0049 
0050 static inline int iostat_metrics_count(void)
0051 {
0052     return sizeof(iostat_metrics) / sizeof(char *);
0053 }
0054 
0055 static const char *iostat_metric_by_idx(int idx)
0056 {
0057     return *(iostat_metrics + idx % iostat_metrics_count());
0058 }
0059 
0060 struct iio_root_port {
0061     u32 domain;
0062     u8 bus;
0063     u8 die;
0064     u8 pmu_idx;
0065     int idx;
0066 };
0067 
0068 struct iio_root_ports_list {
0069     struct iio_root_port **rps;
0070     int nr_entries;
0071 };
0072 
0073 static struct iio_root_ports_list *root_ports;
0074 
0075 static void iio_root_port_show(FILE *output,
0076                    const struct iio_root_port * const rp)
0077 {
0078     if (output && rp)
0079         fprintf(output, "S%d-uncore_iio_%d<%04x:%02x>\n",
0080             rp->die, rp->pmu_idx, rp->domain, rp->bus);
0081 }
0082 
0083 static struct iio_root_port *iio_root_port_new(u32 domain, u8 bus,
0084                            u8 die, u8 pmu_idx)
0085 {
0086     struct iio_root_port *p = calloc(1, sizeof(*p));
0087 
0088     if (p) {
0089         p->domain = domain;
0090         p->bus = bus;
0091         p->die = die;
0092         p->pmu_idx = pmu_idx;
0093     }
0094     return p;
0095 }
0096 
0097 static void iio_root_ports_list_free(struct iio_root_ports_list *list)
0098 {
0099     int idx;
0100 
0101     if (list) {
0102         for (idx = 0; idx < list->nr_entries; idx++)
0103             free(list->rps[idx]);
0104         free(list->rps);
0105         free(list);
0106     }
0107 }
0108 
0109 static struct iio_root_port *iio_root_port_find_by_notation(
0110     const struct iio_root_ports_list * const list, u32 domain, u8 bus)
0111 {
0112     int idx;
0113     struct iio_root_port *rp;
0114 
0115     if (list) {
0116         for (idx = 0; idx < list->nr_entries; idx++) {
0117             rp = list->rps[idx];
0118             if (rp && rp->domain == domain && rp->bus == bus)
0119                 return rp;
0120         }
0121     }
0122     return NULL;
0123 }
0124 
0125 static int iio_root_ports_list_insert(struct iio_root_ports_list *list,
0126                       struct iio_root_port * const rp)
0127 {
0128     struct iio_root_port **tmp_buf;
0129 
0130     if (list && rp) {
0131         rp->idx = list->nr_entries++;
0132         tmp_buf = realloc(list->rps,
0133                   list->nr_entries * sizeof(*list->rps));
0134         if (!tmp_buf) {
0135             pr_err("Failed to realloc memory\n");
0136             return -ENOMEM;
0137         }
0138         tmp_buf[rp->idx] = rp;
0139         list->rps = tmp_buf;
0140     }
0141     return 0;
0142 }
0143 
0144 static int iio_mapping(u8 pmu_idx, struct iio_root_ports_list * const list)
0145 {
0146     char *buf;
0147     char path[MAX_PATH];
0148     u32 domain;
0149     u8 bus;
0150     struct iio_root_port *rp;
0151     size_t size;
0152     int ret;
0153 
0154     for (int die = 0; die < cpu__max_node(); die++) {
0155         scnprintf(path, MAX_PATH, PLATFORM_MAPPING_PATH, pmu_idx, die);
0156         if (sysfs__read_str(path, &buf, &size) < 0) {
0157             if (pmu_idx)
0158                 goto out;
0159             pr_err("Mode iostat is not supported\n");
0160             return -1;
0161         }
0162         ret = sscanf(buf, "%04x:%02hhx", &domain, &bus);
0163         free(buf);
0164         if (ret != 2) {
0165             pr_err("Invalid mapping data: iio_%d; die%d\n",
0166                    pmu_idx, die);
0167             return -1;
0168         }
0169         rp = iio_root_port_new(domain, bus, die, pmu_idx);
0170         if (!rp || iio_root_ports_list_insert(list, rp)) {
0171             free(rp);
0172             return -ENOMEM;
0173         }
0174     }
0175 out:
0176     return 0;
0177 }
0178 
0179 static u8 iio_pmu_count(void)
0180 {
0181     u8 pmu_idx = 0;
0182     char path[MAX_PATH];
0183     const char *sysfs = sysfs__mountpoint();
0184 
0185     if (sysfs) {
0186         for (;; pmu_idx++) {
0187             snprintf(path, sizeof(path), SYSFS_UNCORE_PMU_PATH,
0188                  sysfs, pmu_idx);
0189             if (access(path, F_OK) != 0)
0190                 break;
0191         }
0192     }
0193     return pmu_idx;
0194 }
0195 
0196 static int iio_root_ports_scan(struct iio_root_ports_list **list)
0197 {
0198     int ret = -ENOMEM;
0199     struct iio_root_ports_list *tmp_list;
0200     u8 pmu_count = iio_pmu_count();
0201 
0202     if (!pmu_count) {
0203         pr_err("Unsupported uncore pmu configuration\n");
0204         return -1;
0205     }
0206 
0207     tmp_list = calloc(1, sizeof(*tmp_list));
0208     if (!tmp_list)
0209         goto err;
0210 
0211     for (u8 pmu_idx = 0; pmu_idx < pmu_count; pmu_idx++) {
0212         ret = iio_mapping(pmu_idx, tmp_list);
0213         if (ret)
0214             break;
0215     }
0216 err:
0217     if (!ret)
0218         *list = tmp_list;
0219     else
0220         iio_root_ports_list_free(tmp_list);
0221 
0222     return ret;
0223 }
0224 
0225 static int iio_root_port_parse_str(u32 *domain, u8 *bus, char *str)
0226 {
0227     int ret;
0228     regex_t regex;
0229     /*
0230      * Expected format domain:bus:
0231      * Valid domain range [0:ffff]
0232      * Valid bus range [0:ff]
0233      * Example: 0000:af, 0:3d, 01:7
0234      */
0235     regcomp(&regex, "^([a-f0-9A-F]{1,}):([a-f0-9A-F]{1,2})", REG_EXTENDED);
0236     ret = regexec(&regex, str, 0, NULL, 0);
0237     if (ret || sscanf(str, "%08x:%02hhx", domain, bus) != 2)
0238         pr_warning("Unrecognized root port format: %s\n"
0239                "Please use the following format:\n"
0240                "\t [domain]:[bus]\n"
0241                "\t for example: 0000:3d\n", str);
0242 
0243     regfree(&regex);
0244     return ret;
0245 }
0246 
0247 static int iio_root_ports_list_filter(struct iio_root_ports_list **list,
0248                       const char *filter)
0249 {
0250     char *tok, *tmp, *filter_copy = NULL;
0251     struct iio_root_port *rp;
0252     u32 domain;
0253     u8 bus;
0254     int ret = -ENOMEM;
0255     struct iio_root_ports_list *tmp_list = calloc(1, sizeof(*tmp_list));
0256 
0257     if (!tmp_list)
0258         goto err;
0259 
0260     filter_copy = strdup(filter);
0261     if (!filter_copy)
0262         goto err;
0263 
0264     for (tok = strtok_r(filter_copy, ",", &tmp); tok;
0265          tok = strtok_r(NULL, ",", &tmp)) {
0266         if (!iio_root_port_parse_str(&domain, &bus, tok)) {
0267             rp = iio_root_port_find_by_notation(*list, domain, bus);
0268             if (rp) {
0269                 (*list)->rps[rp->idx] = NULL;
0270                 ret = iio_root_ports_list_insert(tmp_list, rp);
0271                 if (ret) {
0272                     free(rp);
0273                     goto err;
0274                 }
0275             } else if (!iio_root_port_find_by_notation(tmp_list,
0276                                    domain, bus))
0277                 pr_warning("Root port %04x:%02x were not found\n",
0278                        domain, bus);
0279         }
0280     }
0281 
0282     if (tmp_list->nr_entries == 0) {
0283         pr_err("Requested root ports were not found\n");
0284         ret = -EINVAL;
0285     }
0286 err:
0287     iio_root_ports_list_free(*list);
0288     if (ret)
0289         iio_root_ports_list_free(tmp_list);
0290     else
0291         *list = tmp_list;
0292 
0293     free(filter_copy);
0294     return ret;
0295 }
0296 
0297 static int iostat_event_group(struct evlist *evl,
0298                   struct iio_root_ports_list *list)
0299 {
0300     int ret;
0301     int idx;
0302     const char *iostat_cmd_template =
0303     "{uncore_iio_%x/event=0x83,umask=0x04,ch_mask=0xF,fc_mask=0x07/,\
0304       uncore_iio_%x/event=0x83,umask=0x01,ch_mask=0xF,fc_mask=0x07/,\
0305       uncore_iio_%x/event=0xc0,umask=0x04,ch_mask=0xF,fc_mask=0x07/,\
0306       uncore_iio_%x/event=0xc0,umask=0x01,ch_mask=0xF,fc_mask=0x07/}";
0307     const int len_template = strlen(iostat_cmd_template) + 1;
0308     struct evsel *evsel = NULL;
0309     int metrics_count = iostat_metrics_count();
0310     char *iostat_cmd = calloc(len_template, 1);
0311 
0312     if (!iostat_cmd)
0313         return -ENOMEM;
0314 
0315     for (idx = 0; idx < list->nr_entries; idx++) {
0316         sprintf(iostat_cmd, iostat_cmd_template,
0317             list->rps[idx]->pmu_idx, list->rps[idx]->pmu_idx,
0318             list->rps[idx]->pmu_idx, list->rps[idx]->pmu_idx);
0319         ret = parse_event(evl, iostat_cmd);
0320         if (ret)
0321             goto err;
0322     }
0323 
0324     evlist__for_each_entry(evl, evsel) {
0325         evsel->priv = list->rps[evsel->core.idx / metrics_count];
0326     }
0327     list->nr_entries = 0;
0328 err:
0329     iio_root_ports_list_free(list);
0330     free(iostat_cmd);
0331     return ret;
0332 }
0333 
0334 int iostat_prepare(struct evlist *evlist, struct perf_stat_config *config)
0335 {
0336     if (evlist->core.nr_entries > 0) {
0337         pr_warning("The -e and -M options are not supported."
0338                "All chosen events/metrics will be dropped\n");
0339         evlist__delete(evlist);
0340         evlist = evlist__new();
0341         if (!evlist)
0342             return -ENOMEM;
0343     }
0344 
0345     config->metric_only = true;
0346     config->aggr_mode = AGGR_GLOBAL;
0347 
0348     return iostat_event_group(evlist, root_ports);
0349 }
0350 
0351 int iostat_parse(const struct option *opt, const char *str,
0352          int unset __maybe_unused)
0353 {
0354     int ret;
0355     struct perf_stat_config *config = (struct perf_stat_config *)opt->data;
0356 
0357     ret = iio_root_ports_scan(&root_ports);
0358     if (!ret) {
0359         config->iostat_run = true;
0360         if (!str)
0361             iostat_mode = IOSTAT_RUN;
0362         else if (!strcmp(str, "list"))
0363             iostat_mode = IOSTAT_LIST;
0364         else {
0365             iostat_mode = IOSTAT_RUN;
0366             ret = iio_root_ports_list_filter(&root_ports, str);
0367         }
0368     }
0369     return ret;
0370 }
0371 
0372 void iostat_list(struct evlist *evlist, struct perf_stat_config *config)
0373 {
0374     struct evsel *evsel;
0375     struct iio_root_port *rp = NULL;
0376 
0377     evlist__for_each_entry(evlist, evsel) {
0378         if (rp != evsel->priv) {
0379             rp = evsel->priv;
0380             iio_root_port_show(config->output, rp);
0381         }
0382     }
0383 }
0384 
0385 void iostat_release(struct evlist *evlist)
0386 {
0387     struct evsel *evsel;
0388     struct iio_root_port *rp = NULL;
0389 
0390     evlist__for_each_entry(evlist, evsel) {
0391         if (rp != evsel->priv) {
0392             rp = evsel->priv;
0393             free(evsel->priv);
0394         }
0395     }
0396 }
0397 
0398 void iostat_prefix(struct evlist *evlist,
0399            struct perf_stat_config *config,
0400            char *prefix, struct timespec *ts)
0401 {
0402     struct iio_root_port *rp = evlist->selected->priv;
0403 
0404     if (rp) {
0405         if (ts)
0406             sprintf(prefix, "%6lu.%09lu%s%04x:%02x%s",
0407                 ts->tv_sec, ts->tv_nsec,
0408                 config->csv_sep, rp->domain, rp->bus,
0409                 config->csv_sep);
0410         else
0411             sprintf(prefix, "%04x:%02x%s", rp->domain, rp->bus,
0412                 config->csv_sep);
0413     }
0414 }
0415 
0416 void iostat_print_header_prefix(struct perf_stat_config *config)
0417 {
0418     if (config->csv_output)
0419         fputs("port,", config->output);
0420     else if (config->interval)
0421         fprintf(config->output, "#          time    port         ");
0422     else
0423         fprintf(config->output, "   port         ");
0424 }
0425 
0426 void iostat_print_metric(struct perf_stat_config *config, struct evsel *evsel,
0427              struct perf_stat_output_ctx *out)
0428 {
0429     double iostat_value = 0;
0430     u64 prev_count_val = 0;
0431     const char *iostat_metric = iostat_metric_by_idx(evsel->core.idx);
0432     u8 die = ((struct iio_root_port *)evsel->priv)->die;
0433     struct perf_counts_values *count = perf_counts(evsel->counts, die, 0);
0434 
0435     if (count && count->run && count->ena) {
0436         if (evsel->prev_raw_counts && !out->force_header) {
0437             struct perf_counts_values *prev_count =
0438                 perf_counts(evsel->prev_raw_counts, die, 0);
0439 
0440             prev_count_val = prev_count->val;
0441             prev_count->val = count->val;
0442         }
0443         iostat_value = (count->val - prev_count_val) /
0444                    ((double) count->run / count->ena);
0445     }
0446     out->print_metric(config, out->ctx, NULL, "%8.0f", iostat_metric,
0447               iostat_value / (256 * 1024));
0448 }
0449 
0450 void iostat_print_counters(struct evlist *evlist,
0451                struct perf_stat_config *config, struct timespec *ts,
0452                char *prefix, iostat_print_counter_t print_cnt_cb)
0453 {
0454     void *perf_device = NULL;
0455     struct evsel *counter = evlist__first(evlist);
0456 
0457     evlist__set_selected(evlist, counter);
0458     iostat_prefix(evlist, config, prefix, ts);
0459     fprintf(config->output, "%s", prefix);
0460     evlist__for_each_entry(evlist, counter) {
0461         perf_device = evlist->selected->priv;
0462         if (perf_device && perf_device != counter->priv) {
0463             evlist__set_selected(evlist, counter);
0464             iostat_prefix(evlist, config, prefix, ts);
0465             fprintf(config->output, "\n%s", prefix);
0466         }
0467         print_cnt_cb(config, counter, prefix);
0468     }
0469     fputc('\n', config->output);
0470 }