1
2
3
4
5
6
7
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
40
41
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
231
232
233
234
235 regcomp(®ex, "^([a-f0-9A-F]{1,}):([a-f0-9A-F]{1,2})", REG_EXTENDED);
236 ret = regexec(®ex, 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(®ex);
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