Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/perf/arch/x86/util/iostat.c
26289 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* perf iostat
4
*
5
* Copyright (C) 2020, Intel Corporation
6
*
7
* Authors: Alexander Antonov <[email protected]>
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 "bus/event_source/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
/*
407
* TODO: This is the incorrect format in JSON mode.
408
* See prepare_timestamp()
409
*/
410
if (ts)
411
sprintf(prefix, "%6lu.%09lu%s%04x:%02x%s",
412
ts->tv_sec, ts->tv_nsec,
413
config->csv_sep, rp->domain, rp->bus,
414
config->csv_sep);
415
else
416
sprintf(prefix, "%04x:%02x%s", rp->domain, rp->bus,
417
config->csv_sep);
418
}
419
}
420
421
void iostat_print_header_prefix(struct perf_stat_config *config)
422
{
423
if (config->csv_output)
424
fputs("port,", config->output);
425
else if (config->interval)
426
fprintf(config->output, "# time port ");
427
else
428
fprintf(config->output, " port ");
429
}
430
431
void iostat_print_metric(struct perf_stat_config *config, struct evsel *evsel,
432
struct perf_stat_output_ctx *out)
433
{
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);
439
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);
444
445
prev_count_val = prev_count->val;
446
prev_count->val = count->val;
447
}
448
iostat_value = (count->val - prev_count_val) /
449
((double) count->run / count->ena);
450
}
451
out->print_metric(config, out->ctx, METRIC_THRESHOLD_UNKNOWN, "%8.0f", iostat_metric,
452
iostat_value / (256 * 1024));
453
}
454
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)
458
{
459
void *perf_device = NULL;
460
struct evsel *counter = evlist__first(evlist);
461
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);
471
}
472
print_cnt_cb(config, counter, arg);
473
}
474
fputc('\n', config->output);
475
}
476
477