~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/perf/arch/x86/util/iostat.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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(&regex, "^([a-f0-9A-F]{1,}):([a-f0-9A-F]{1,2})", REG_EXTENDED);
237         ret = regexec(&regex, 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(&regex);
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, NULL, "%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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php