1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2020, Intel Corporation
7 * Authors: Alexander Antonov <alexander.antonov@linux.intel.com>
10 #include <api/fs/fs.h>
11 #include <linux/kernel.h>
12 #include <linux/err.h>
13 #include <linux/zalloc.h>
18 #include <sys/types.h>
25 #include "util/cpumap.h"
26 #include "util/debug.h"
27 #include "util/iostat.h"
28 #include "util/counts.h"
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"
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)
44 static const char * const iostat_metrics
[] = {
51 static inline int iostat_metrics_count(void)
53 return sizeof(iostat_metrics
) / sizeof(char *);
56 static const char *iostat_metric_by_idx(int idx
)
58 return *(iostat_metrics
+ idx
% iostat_metrics_count());
61 struct iio_root_port
{
69 struct iio_root_ports_list
{
70 struct iio_root_port
**rps
;
74 static struct iio_root_ports_list
*root_ports
;
76 static void iio_root_port_show(FILE *output
,
77 const struct iio_root_port
* const rp
)
80 fprintf(output
, "S%d-uncore_iio_%d<%04x:%02x>\n",
81 rp
->die
, rp
->pmu_idx
, rp
->domain
, rp
->bus
);
84 static struct iio_root_port
*iio_root_port_new(u32 domain
, u8 bus
,
87 struct iio_root_port
*p
= calloc(1, sizeof(*p
));
98 static void iio_root_ports_list_free(struct iio_root_ports_list
*list
)
103 for (idx
= 0; idx
< list
->nr_entries
; idx
++)
104 zfree(&list
->rps
[idx
]);
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
)
114 struct iio_root_port
*rp
;
117 for (idx
= 0; idx
< list
->nr_entries
; idx
++) {
119 if (rp
&& rp
->domain
== domain
&& rp
->bus
== bus
)
126 static int iio_root_ports_list_insert(struct iio_root_ports_list
*list
,
127 struct iio_root_port
* const rp
)
129 struct iio_root_port
**tmp_buf
;
132 rp
->idx
= list
->nr_entries
++;
133 tmp_buf
= realloc(list
->rps
,
134 list
->nr_entries
* sizeof(*list
->rps
));
136 pr_err("Failed to realloc memory\n");
139 tmp_buf
[rp
->idx
] = rp
;
145 static int iio_mapping(u8 pmu_idx
, struct iio_root_ports_list
* const list
)
151 struct iio_root_port
*rp
;
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) {
160 pr_err("Mode iostat is not supported\n");
163 ret
= sscanf(buf
, "%04x:%02hhx", &domain
, &bus
);
166 pr_err("Invalid mapping data: iio_%d; die%d\n",
170 rp
= iio_root_port_new(domain
, bus
, die
, pmu_idx
);
171 if (!rp
|| iio_root_ports_list_insert(list
, rp
)) {
180 static u8
iio_pmu_count(void)
184 const char *sysfs
= sysfs__mountpoint();
188 snprintf(path
, sizeof(path
), SYSFS_UNCORE_PMU_PATH
,
190 if (access(path
, F_OK
) != 0)
197 static int iio_root_ports_scan(struct iio_root_ports_list
**list
)
200 struct iio_root_ports_list
*tmp_list
;
201 u8 pmu_count
= iio_pmu_count();
204 pr_err("Unsupported uncore pmu configuration\n");
208 tmp_list
= calloc(1, sizeof(*tmp_list
));
212 for (u8 pmu_idx
= 0; pmu_idx
< pmu_count
; pmu_idx
++) {
213 ret
= iio_mapping(pmu_idx
, tmp_list
);
221 iio_root_ports_list_free(tmp_list
);
226 static int iio_root_port_parse_str(u32
*domain
, u8
*bus
, char *str
)
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
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
);
248 static int iio_root_ports_list_filter(struct iio_root_ports_list
**list
,
251 char *tok
, *tmp
, *filter_copy
= NULL
;
252 struct iio_root_port
*rp
;
256 struct iio_root_ports_list
*tmp_list
= calloc(1, sizeof(*tmp_list
));
261 filter_copy
= strdup(filter
);
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
);
270 (*list
)->rps
[rp
->idx
] = NULL
;
271 ret
= iio_root_ports_list_insert(tmp_list
, rp
);
276 } else if (!iio_root_port_find_by_notation(tmp_list
,
278 pr_warning("Root port %04x:%02x were not found\n",
283 if (tmp_list
->nr_entries
== 0) {
284 pr_err("Requested root ports were not found\n");
288 iio_root_ports_list_free(*list
);
290 iio_root_ports_list_free(tmp_list
);
298 static int iostat_event_group(struct evlist
*evl
,
299 struct iio_root_ports_list
*list
)
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);
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
);
325 evlist__for_each_entry(evl
, evsel
) {
326 evsel
->priv
= list
->rps
[evsel
->core
.idx
/ metrics_count
];
328 list
->nr_entries
= 0;
330 iio_root_ports_list_free(list
);
335 int iostat_prepare(struct evlist
*evlist
, struct perf_stat_config
*config
)
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();
346 config
->metric_only
= true;
347 config
->aggr_mode
= AGGR_GLOBAL
;
349 return iostat_event_group(evlist
, root_ports
);
352 int iostat_parse(const struct option
*opt
, const char *str
,
353 int unset __maybe_unused
)
356 struct perf_stat_config
*config
= (struct perf_stat_config
*)opt
->data
;
358 ret
= iio_root_ports_scan(&root_ports
);
360 config
->iostat_run
= true;
362 iostat_mode
= IOSTAT_RUN
;
363 else if (!strcmp(str
, "list"))
364 iostat_mode
= IOSTAT_LIST
;
366 iostat_mode
= IOSTAT_RUN
;
367 ret
= iio_root_ports_list_filter(&root_ports
, str
);
373 void iostat_list(struct evlist
*evlist
, struct perf_stat_config
*config
)
376 struct iio_root_port
*rp
= NULL
;
378 evlist__for_each_entry(evlist
, evsel
) {
379 if (rp
!= evsel
->priv
) {
381 iio_root_port_show(config
->output
, rp
);
386 void iostat_release(struct evlist
*evlist
)
389 struct iio_root_port
*rp
= NULL
;
391 evlist__for_each_entry(evlist
, evsel
) {
392 if (rp
!= evsel
->priv
) {
399 void iostat_prefix(struct evlist
*evlist
,
400 struct perf_stat_config
*config
,
401 char *prefix
, struct timespec
*ts
)
403 struct iio_root_port
*rp
= evlist
->selected
->priv
;
407 * TODO: This is the incorrect format in JSON mode.
408 * See prepare_timestamp()
411 sprintf(prefix
, "%6lu.%09lu%s%04x:%02x%s",
412 ts
->tv_sec
, ts
->tv_nsec
,
413 config
->csv_sep
, rp
->domain
, rp
->bus
,
416 sprintf(prefix
, "%04x:%02x%s", rp
->domain
, rp
->bus
,
421 void iostat_print_header_prefix(struct perf_stat_config
*config
)
423 if (config
->csv_output
)
424 fputs("port,", config
->output
);
425 else if (config
->interval
)
426 fprintf(config
->output
, "# time port ");
428 fprintf(config
->output
, " port ");
431 void iostat_print_metric(struct perf_stat_config
*config
, struct evsel
*evsel
,
432 struct perf_stat_output_ctx
*out
)
434 double iostat_value
= 0;
435 u64 prev_count_val
= 0;
436 const char *iostat_metric
= iostat_metric_by_idx(evsel
->core
.idx
);
437 u8 die
= ((struct iio_root_port
*)evsel
->priv
)->die
;
438 struct perf_counts_values
*count
= perf_counts(evsel
->counts
, die
, 0);
440 if (count
&& count
->run
&& count
->ena
) {
441 if (evsel
->prev_raw_counts
&& !out
->force_header
) {
442 struct perf_counts_values
*prev_count
=
443 perf_counts(evsel
->prev_raw_counts
, die
, 0);
445 prev_count_val
= prev_count
->val
;
446 prev_count
->val
= count
->val
;
448 iostat_value
= (count
->val
- prev_count_val
) /
449 ((double) count
->run
/ count
->ena
);
451 out
->print_metric(config
, out
->ctx
, METRIC_THRESHOLD_UNKNOWN
, "%8.0f", iostat_metric
,
452 iostat_value
/ (256 * 1024));
455 void iostat_print_counters(struct evlist
*evlist
,
456 struct perf_stat_config
*config
, struct timespec
*ts
,
457 char *prefix
, iostat_print_counter_t print_cnt_cb
, void *arg
)
459 void *perf_device
= NULL
;
460 struct evsel
*counter
= evlist__first(evlist
);
462 evlist__set_selected(evlist
, counter
);
463 iostat_prefix(evlist
, config
, prefix
, ts
);
464 fprintf(config
->output
, "%s", prefix
);
465 evlist__for_each_entry(evlist
, counter
) {
466 perf_device
= evlist
->selected
->priv
;
467 if (perf_device
&& perf_device
!= counter
->priv
) {
468 evlist__set_selected(evlist
, counter
);
469 iostat_prefix(evlist
, config
, prefix
, ts
);
470 fprintf(config
->output
, "\n%s", prefix
);
472 print_cnt_cb(config
, counter
, arg
);
474 fputc('\n', config
->output
);