Merge tag 'drm-misc-next-2021-10-14' of git://anongit.freedesktop.org/drm/drm-misc...
[linux-2.6-microblaze.git] / tools / perf / arch / x86 / util / iostat.c
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  */
43 static const char * const iostat_metrics[] = {
44         "Inbound Read(MB)",
45         "Inbound Write(MB)",
46         "Outbound Read(MB)",
47         "Outbound Write(MB)",
48 };
49
50 static inline int iostat_metrics_count(void)
51 {
52         return sizeof(iostat_metrics) / sizeof(char *);
53 }
54
55 static const char *iostat_metric_by_idx(int idx)
56 {
57         return *(iostat_metrics + idx % iostat_metrics_count());
58 }
59
60 struct iio_root_port {
61         u32 domain;
62         u8 bus;
63         u8 die;
64         u8 pmu_idx;
65         int idx;
66 };
67
68 struct iio_root_ports_list {
69         struct iio_root_port **rps;
70         int nr_entries;
71 };
72
73 static struct iio_root_ports_list *root_ports;
74
75 static 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
83 static 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
97 static 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
109 static 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
125 static 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
144 static 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         }
175 out:
176         return 0;
177 }
178
179 static 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
196 static 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         }
216 err:
217         if (!ret)
218                 *list = tmp_list;
219         else
220                 iio_root_ports_list_free(tmp_list);
221
222         return ret;
223 }
224
225 static 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
247 static 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         }
286 err:
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
297 static 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;
328 err:
329         iio_root_ports_list_free(list);
330         free(iostat_cmd);
331         return ret;
332 }
333
334 int 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
351 int 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
372 void 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
385 void 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
398 void 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
416 void 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
426 void 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
450 void 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 }