Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/perf/arch/x86/tests/amd-ibs-period.c
26292 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include <sched.h>
3
#include <sys/syscall.h>
4
#include <sys/mman.h>
5
#include <sys/ioctl.h>
6
#include <sys/utsname.h>
7
#include <string.h>
8
9
#include "arch-tests.h"
10
#include "linux/perf_event.h"
11
#include "linux/zalloc.h"
12
#include "tests/tests.h"
13
#include "../perf-sys.h"
14
#include "pmu.h"
15
#include "pmus.h"
16
#include "debug.h"
17
#include "util.h"
18
#include "strbuf.h"
19
#include "../util/env.h"
20
21
static int page_size;
22
23
#define PERF_MMAP_DATA_PAGES 32L
24
#define PERF_MMAP_DATA_SIZE (PERF_MMAP_DATA_PAGES * page_size)
25
#define PERF_MMAP_DATA_MASK (PERF_MMAP_DATA_SIZE - 1)
26
#define PERF_MMAP_TOTAL_PAGES (PERF_MMAP_DATA_PAGES + 1)
27
#define PERF_MMAP_TOTAL_SIZE (PERF_MMAP_TOTAL_PAGES * page_size)
28
29
#define rmb() asm volatile("lfence":::"memory")
30
31
enum {
32
FD_ERROR,
33
FD_SUCCESS,
34
};
35
36
enum {
37
IBS_FETCH,
38
IBS_OP,
39
};
40
41
struct perf_pmu *fetch_pmu;
42
struct perf_pmu *op_pmu;
43
unsigned int perf_event_max_sample_rate;
44
45
/* Dummy workload to generate IBS samples. */
46
static int dummy_workload_1(unsigned long count)
47
{
48
int (*func)(void);
49
int ret = 0;
50
char *p;
51
char insn1[] = {
52
0xb8, 0x01, 0x00, 0x00, 0x00, /* mov 1,%eax */
53
0xc3, /* ret */
54
0xcc, /* int 3 */
55
};
56
57
char insn2[] = {
58
0xb8, 0x02, 0x00, 0x00, 0x00, /* mov 2,%eax */
59
0xc3, /* ret */
60
0xcc, /* int 3 */
61
};
62
63
p = zalloc(2 * page_size);
64
if (!p) {
65
printf("malloc() failed. %m");
66
return 1;
67
}
68
69
func = (void *)((unsigned long)(p + page_size - 1) & ~(page_size - 1));
70
71
ret = mprotect(func, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
72
if (ret) {
73
printf("mprotect() failed. %m");
74
goto out;
75
}
76
77
if (count < 100000)
78
count = 100000;
79
else if (count > 10000000)
80
count = 10000000;
81
while (count--) {
82
memcpy((void *)func, insn1, sizeof(insn1));
83
if (func() != 1) {
84
pr_debug("ERROR insn1\n");
85
ret = -1;
86
goto out;
87
}
88
memcpy((void *)func, insn2, sizeof(insn2));
89
if (func() != 2) {
90
pr_debug("ERROR insn2\n");
91
ret = -1;
92
goto out;
93
}
94
}
95
96
out:
97
free(p);
98
return ret;
99
}
100
101
/* Another dummy workload to generate IBS samples. */
102
static void dummy_workload_2(char *perf)
103
{
104
char bench[] = " bench sched messaging -g 10 -l 5000 > /dev/null 2>&1";
105
char taskset[] = "taskset -c 0 ";
106
int ret __maybe_unused;
107
struct strbuf sb;
108
char *cmd;
109
110
strbuf_init(&sb, 0);
111
strbuf_add(&sb, taskset, strlen(taskset));
112
strbuf_add(&sb, perf, strlen(perf));
113
strbuf_add(&sb, bench, strlen(bench));
114
cmd = strbuf_detach(&sb, NULL);
115
ret = system(cmd);
116
free(cmd);
117
}
118
119
static int sched_affine(int cpu)
120
{
121
cpu_set_t set;
122
123
CPU_ZERO(&set);
124
CPU_SET(cpu, &set);
125
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) {
126
pr_debug("sched_setaffinity() failed. [%m]");
127
return -1;
128
}
129
return 0;
130
}
131
132
static void
133
copy_sample_data(void *src, unsigned long offset, void *dest, size_t size)
134
{
135
size_t chunk1_size, chunk2_size;
136
137
if ((offset + size) < (size_t)PERF_MMAP_DATA_SIZE) {
138
memcpy(dest, src + offset, size);
139
} else {
140
chunk1_size = PERF_MMAP_DATA_SIZE - offset;
141
chunk2_size = size - chunk1_size;
142
143
memcpy(dest, src + offset, chunk1_size);
144
memcpy(dest + chunk1_size, src, chunk2_size);
145
}
146
}
147
148
static int rb_read(struct perf_event_mmap_page *rb, void *dest, size_t size)
149
{
150
void *base;
151
unsigned long data_tail, data_head;
152
153
/* Casting to (void *) is needed. */
154
base = (void *)rb + page_size;
155
156
data_head = rb->data_head;
157
rmb();
158
data_tail = rb->data_tail;
159
160
if ((data_head - data_tail) < size)
161
return -1;
162
163
data_tail &= PERF_MMAP_DATA_MASK;
164
copy_sample_data(base, data_tail, dest, size);
165
rb->data_tail += size;
166
return 0;
167
}
168
169
static void rb_skip(struct perf_event_mmap_page *rb, size_t size)
170
{
171
size_t data_head = rb->data_head;
172
173
rmb();
174
175
if ((rb->data_tail + size) > data_head)
176
rb->data_tail = data_head;
177
else
178
rb->data_tail += size;
179
}
180
181
/* Sample period value taken from perf sample must match with expected value. */
182
static int period_equal(unsigned long exp_period, unsigned long act_period)
183
{
184
return exp_period == act_period ? 0 : -1;
185
}
186
187
/*
188
* Sample period value taken from perf sample must be >= minimum sample period
189
* supported by IBS HW.
190
*/
191
static int period_higher(unsigned long min_period, unsigned long act_period)
192
{
193
return min_period <= act_period ? 0 : -1;
194
}
195
196
static int rb_drain_samples(struct perf_event_mmap_page *rb,
197
unsigned long exp_period,
198
int *nr_samples,
199
int (*callback)(unsigned long, unsigned long))
200
{
201
struct perf_event_header hdr;
202
unsigned long period;
203
int ret = 0;
204
205
/*
206
* PERF_RECORD_SAMPLE:
207
* struct {
208
* struct perf_event_header hdr;
209
* { u64 period; } && PERF_SAMPLE_PERIOD
210
* };
211
*/
212
while (1) {
213
if (rb_read(rb, &hdr, sizeof(hdr)))
214
return ret;
215
216
if (hdr.type == PERF_RECORD_SAMPLE) {
217
(*nr_samples)++;
218
period = 0;
219
if (rb_read(rb, &period, sizeof(period)))
220
pr_debug("rb_read(period) error. [%m]");
221
ret |= callback(exp_period, period);
222
} else {
223
rb_skip(rb, hdr.size - sizeof(hdr));
224
}
225
}
226
return ret;
227
}
228
229
static long perf_event_open(struct perf_event_attr *attr, pid_t pid,
230
int cpu, int group_fd, unsigned long flags)
231
{
232
return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
233
}
234
235
static void fetch_prepare_attr(struct perf_event_attr *attr,
236
unsigned long long config, int freq,
237
unsigned long sample_period)
238
{
239
memset(attr, 0, sizeof(struct perf_event_attr));
240
241
attr->type = fetch_pmu->type;
242
attr->size = sizeof(struct perf_event_attr);
243
attr->config = config;
244
attr->disabled = 1;
245
attr->sample_type = PERF_SAMPLE_PERIOD;
246
attr->freq = freq;
247
attr->sample_period = sample_period; /* = ->sample_freq */
248
}
249
250
static void op_prepare_attr(struct perf_event_attr *attr,
251
unsigned long config, int freq,
252
unsigned long sample_period)
253
{
254
memset(attr, 0, sizeof(struct perf_event_attr));
255
256
attr->type = op_pmu->type;
257
attr->size = sizeof(struct perf_event_attr);
258
attr->config = config;
259
attr->disabled = 1;
260
attr->sample_type = PERF_SAMPLE_PERIOD;
261
attr->freq = freq;
262
attr->sample_period = sample_period; /* = ->sample_freq */
263
}
264
265
struct ibs_configs {
266
/* Input */
267
unsigned long config;
268
269
/* Expected output */
270
unsigned long period;
271
int fd;
272
};
273
274
/*
275
* Somehow first Fetch event with sample period = 0x10 causes 0
276
* samples. So start with large period and decrease it gradually.
277
*/
278
struct ibs_configs fetch_configs[] = {
279
{ .config = 0xffff, .period = 0xffff0, .fd = FD_SUCCESS },
280
{ .config = 0x1000, .period = 0x10000, .fd = FD_SUCCESS },
281
{ .config = 0xff, .period = 0xff0, .fd = FD_SUCCESS },
282
{ .config = 0x1, .period = 0x10, .fd = FD_SUCCESS },
283
{ .config = 0x0, .period = -1, .fd = FD_ERROR },
284
{ .config = 0x10000, .period = -1, .fd = FD_ERROR },
285
};
286
287
struct ibs_configs op_configs[] = {
288
{ .config = 0x0, .period = -1, .fd = FD_ERROR },
289
{ .config = 0x1, .period = -1, .fd = FD_ERROR },
290
{ .config = 0x8, .period = -1, .fd = FD_ERROR },
291
{ .config = 0x9, .period = 0x90, .fd = FD_SUCCESS },
292
{ .config = 0xf, .period = 0xf0, .fd = FD_SUCCESS },
293
{ .config = 0x1000, .period = 0x10000, .fd = FD_SUCCESS },
294
{ .config = 0xffff, .period = 0xffff0, .fd = FD_SUCCESS },
295
{ .config = 0x10000, .period = -1, .fd = FD_ERROR },
296
{ .config = 0x100000, .period = 0x100000, .fd = FD_SUCCESS },
297
{ .config = 0xf00000, .period = 0xf00000, .fd = FD_SUCCESS },
298
{ .config = 0xf0ffff, .period = 0xfffff0, .fd = FD_SUCCESS },
299
{ .config = 0x1f0ffff, .period = 0x1fffff0, .fd = FD_SUCCESS },
300
{ .config = 0x7f0ffff, .period = 0x7fffff0, .fd = FD_SUCCESS },
301
{ .config = 0x8f0ffff, .period = -1, .fd = FD_ERROR },
302
{ .config = 0x17f0ffff, .period = -1, .fd = FD_ERROR },
303
};
304
305
static int __ibs_config_test(int ibs_type, struct ibs_configs *config, int *nr_samples)
306
{
307
struct perf_event_attr attr;
308
int fd, i;
309
void *rb;
310
int ret = 0;
311
312
if (ibs_type == IBS_FETCH)
313
fetch_prepare_attr(&attr, config->config, 0, 0);
314
else
315
op_prepare_attr(&attr, config->config, 0, 0);
316
317
/* CPU0, All processes */
318
fd = perf_event_open(&attr, -1, 0, -1, 0);
319
if (config->fd == FD_ERROR) {
320
if (fd != -1) {
321
close(fd);
322
return -1;
323
}
324
return 0;
325
}
326
if (fd <= -1)
327
return -1;
328
329
rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,
330
MAP_SHARED, fd, 0);
331
if (rb == MAP_FAILED) {
332
pr_debug("mmap() failed. [%m]\n");
333
return -1;
334
}
335
336
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
337
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
338
339
i = 5;
340
while (i--) {
341
dummy_workload_1(1000000);
342
343
ret = rb_drain_samples(rb, config->period, nr_samples,
344
period_equal);
345
if (ret)
346
break;
347
}
348
349
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
350
munmap(rb, PERF_MMAP_TOTAL_SIZE);
351
close(fd);
352
return ret;
353
}
354
355
static int ibs_config_test(void)
356
{
357
int nr_samples = 0;
358
unsigned long i;
359
int ret = 0;
360
int r;
361
362
pr_debug("\nIBS config tests:\n");
363
pr_debug("-----------------\n");
364
365
pr_debug("Fetch PMU tests:\n");
366
for (i = 0; i < ARRAY_SIZE(fetch_configs); i++) {
367
nr_samples = 0;
368
r = __ibs_config_test(IBS_FETCH, &(fetch_configs[i]), &nr_samples);
369
370
if (fetch_configs[i].fd == FD_ERROR) {
371
pr_debug("0x%-16lx: %-4s\n", fetch_configs[i].config,
372
!r ? "Ok" : "Fail");
373
} else {
374
/*
375
* Although nr_samples == 0 is reported as Fail here,
376
* the failure status is not cascaded up because, we
377
* can not decide whether test really failed or not
378
* without actual samples.
379
*/
380
pr_debug("0x%-16lx: %-4s (nr samples: %d)\n", fetch_configs[i].config,
381
(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
382
}
383
384
ret |= r;
385
}
386
387
pr_debug("Op PMU tests:\n");
388
for (i = 0; i < ARRAY_SIZE(op_configs); i++) {
389
nr_samples = 0;
390
r = __ibs_config_test(IBS_OP, &(op_configs[i]), &nr_samples);
391
392
if (op_configs[i].fd == FD_ERROR) {
393
pr_debug("0x%-16lx: %-4s\n", op_configs[i].config,
394
!r ? "Ok" : "Fail");
395
} else {
396
/*
397
* Although nr_samples == 0 is reported as Fail here,
398
* the failure status is not cascaded up because, we
399
* can not decide whether test really failed or not
400
* without actual samples.
401
*/
402
pr_debug("0x%-16lx: %-4s (nr samples: %d)\n", op_configs[i].config,
403
(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
404
}
405
406
ret |= r;
407
}
408
409
return ret;
410
}
411
412
struct ibs_period {
413
/* Input */
414
int freq;
415
unsigned long sample_freq;
416
417
/* Output */
418
int ret;
419
unsigned long period;
420
};
421
422
struct ibs_period fetch_period[] = {
423
{ .freq = 0, .sample_freq = 0, .ret = FD_ERROR, .period = -1 },
424
{ .freq = 0, .sample_freq = 1, .ret = FD_ERROR, .period = -1 },
425
{ .freq = 0, .sample_freq = 0xf, .ret = FD_ERROR, .period = -1 },
426
{ .freq = 0, .sample_freq = 0x10, .ret = FD_SUCCESS, .period = 0x10 },
427
{ .freq = 0, .sample_freq = 0x11, .ret = FD_SUCCESS, .period = 0x10 },
428
{ .freq = 0, .sample_freq = 0x8f, .ret = FD_SUCCESS, .period = 0x80 },
429
{ .freq = 0, .sample_freq = 0x90, .ret = FD_SUCCESS, .period = 0x90 },
430
{ .freq = 0, .sample_freq = 0x91, .ret = FD_SUCCESS, .period = 0x90 },
431
{ .freq = 0, .sample_freq = 0x4d2, .ret = FD_SUCCESS, .period = 0x4d0 },
432
{ .freq = 0, .sample_freq = 0x1007, .ret = FD_SUCCESS, .period = 0x1000 },
433
{ .freq = 0, .sample_freq = 0xfff0, .ret = FD_SUCCESS, .period = 0xfff0 },
434
{ .freq = 0, .sample_freq = 0xffff, .ret = FD_SUCCESS, .period = 0xfff0 },
435
{ .freq = 0, .sample_freq = 0x10010, .ret = FD_SUCCESS, .period = 0x10010 },
436
{ .freq = 0, .sample_freq = 0x7fffff, .ret = FD_SUCCESS, .period = 0x7ffff0 },
437
{ .freq = 0, .sample_freq = 0xfffffff, .ret = FD_SUCCESS, .period = 0xffffff0 },
438
{ .freq = 1, .sample_freq = 0, .ret = FD_ERROR, .period = -1 },
439
{ .freq = 1, .sample_freq = 1, .ret = FD_SUCCESS, .period = 0x10 },
440
{ .freq = 1, .sample_freq = 0xf, .ret = FD_SUCCESS, .period = 0x10 },
441
{ .freq = 1, .sample_freq = 0x10, .ret = FD_SUCCESS, .period = 0x10 },
442
{ .freq = 1, .sample_freq = 0x11, .ret = FD_SUCCESS, .period = 0x10 },
443
{ .freq = 1, .sample_freq = 0x8f, .ret = FD_SUCCESS, .period = 0x10 },
444
{ .freq = 1, .sample_freq = 0x90, .ret = FD_SUCCESS, .period = 0x10 },
445
{ .freq = 1, .sample_freq = 0x91, .ret = FD_SUCCESS, .period = 0x10 },
446
{ .freq = 1, .sample_freq = 0x4d2, .ret = FD_SUCCESS, .period = 0x10 },
447
{ .freq = 1, .sample_freq = 0x1007, .ret = FD_SUCCESS, .period = 0x10 },
448
{ .freq = 1, .sample_freq = 0xfff0, .ret = FD_SUCCESS, .period = 0x10 },
449
{ .freq = 1, .sample_freq = 0xffff, .ret = FD_SUCCESS, .period = 0x10 },
450
{ .freq = 1, .sample_freq = 0x10010, .ret = FD_SUCCESS, .period = 0x10 },
451
/* ret=FD_ERROR because freq > default perf_event_max_sample_rate (100000) */
452
{ .freq = 1, .sample_freq = 0x7fffff, .ret = FD_ERROR, .period = -1 },
453
};
454
455
struct ibs_period op_period[] = {
456
{ .freq = 0, .sample_freq = 0, .ret = FD_ERROR, .period = -1 },
457
{ .freq = 0, .sample_freq = 1, .ret = FD_ERROR, .period = -1 },
458
{ .freq = 0, .sample_freq = 0xf, .ret = FD_ERROR, .period = -1 },
459
{ .freq = 0, .sample_freq = 0x10, .ret = FD_ERROR, .period = -1 },
460
{ .freq = 0, .sample_freq = 0x11, .ret = FD_ERROR, .period = -1 },
461
{ .freq = 0, .sample_freq = 0x8f, .ret = FD_ERROR, .period = -1 },
462
{ .freq = 0, .sample_freq = 0x90, .ret = FD_SUCCESS, .period = 0x90 },
463
{ .freq = 0, .sample_freq = 0x91, .ret = FD_SUCCESS, .period = 0x90 },
464
{ .freq = 0, .sample_freq = 0x4d2, .ret = FD_SUCCESS, .period = 0x4d0 },
465
{ .freq = 0, .sample_freq = 0x1007, .ret = FD_SUCCESS, .period = 0x1000 },
466
{ .freq = 0, .sample_freq = 0xfff0, .ret = FD_SUCCESS, .period = 0xfff0 },
467
{ .freq = 0, .sample_freq = 0xffff, .ret = FD_SUCCESS, .period = 0xfff0 },
468
{ .freq = 0, .sample_freq = 0x10010, .ret = FD_SUCCESS, .period = 0x10010 },
469
{ .freq = 0, .sample_freq = 0x7fffff, .ret = FD_SUCCESS, .period = 0x7ffff0 },
470
{ .freq = 0, .sample_freq = 0xfffffff, .ret = FD_SUCCESS, .period = 0xffffff0 },
471
{ .freq = 1, .sample_freq = 0, .ret = FD_ERROR, .period = -1 },
472
{ .freq = 1, .sample_freq = 1, .ret = FD_SUCCESS, .period = 0x90 },
473
{ .freq = 1, .sample_freq = 0xf, .ret = FD_SUCCESS, .period = 0x90 },
474
{ .freq = 1, .sample_freq = 0x10, .ret = FD_SUCCESS, .period = 0x90 },
475
{ .freq = 1, .sample_freq = 0x11, .ret = FD_SUCCESS, .period = 0x90 },
476
{ .freq = 1, .sample_freq = 0x8f, .ret = FD_SUCCESS, .period = 0x90 },
477
{ .freq = 1, .sample_freq = 0x90, .ret = FD_SUCCESS, .period = 0x90 },
478
{ .freq = 1, .sample_freq = 0x91, .ret = FD_SUCCESS, .period = 0x90 },
479
{ .freq = 1, .sample_freq = 0x4d2, .ret = FD_SUCCESS, .period = 0x90 },
480
{ .freq = 1, .sample_freq = 0x1007, .ret = FD_SUCCESS, .period = 0x90 },
481
{ .freq = 1, .sample_freq = 0xfff0, .ret = FD_SUCCESS, .period = 0x90 },
482
{ .freq = 1, .sample_freq = 0xffff, .ret = FD_SUCCESS, .period = 0x90 },
483
{ .freq = 1, .sample_freq = 0x10010, .ret = FD_SUCCESS, .period = 0x90 },
484
/* ret=FD_ERROR because freq > default perf_event_max_sample_rate (100000) */
485
{ .freq = 1, .sample_freq = 0x7fffff, .ret = FD_ERROR, .period = -1 },
486
};
487
488
static int __ibs_period_constraint_test(int ibs_type, struct ibs_period *period,
489
int *nr_samples)
490
{
491
struct perf_event_attr attr;
492
int ret = 0;
493
void *rb;
494
int fd;
495
496
if (period->freq && period->sample_freq > perf_event_max_sample_rate)
497
period->ret = FD_ERROR;
498
499
if (ibs_type == IBS_FETCH)
500
fetch_prepare_attr(&attr, 0, period->freq, period->sample_freq);
501
else
502
op_prepare_attr(&attr, 0, period->freq, period->sample_freq);
503
504
/* CPU0, All processes */
505
fd = perf_event_open(&attr, -1, 0, -1, 0);
506
if (period->ret == FD_ERROR) {
507
if (fd != -1) {
508
close(fd);
509
return -1;
510
}
511
return 0;
512
}
513
if (fd <= -1)
514
return -1;
515
516
rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,
517
MAP_SHARED, fd, 0);
518
if (rb == MAP_FAILED) {
519
pr_debug("mmap() failed. [%m]\n");
520
close(fd);
521
return -1;
522
}
523
524
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
525
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
526
527
if (period->freq) {
528
dummy_workload_1(100000);
529
ret = rb_drain_samples(rb, period->period, nr_samples,
530
period_higher);
531
} else {
532
dummy_workload_1(period->sample_freq * 10);
533
ret = rb_drain_samples(rb, period->period, nr_samples,
534
period_equal);
535
}
536
537
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
538
munmap(rb, PERF_MMAP_TOTAL_SIZE);
539
close(fd);
540
return ret;
541
}
542
543
static int ibs_period_constraint_test(void)
544
{
545
unsigned long i;
546
int nr_samples;
547
int ret = 0;
548
int r;
549
550
pr_debug("\nIBS sample period constraint tests:\n");
551
pr_debug("-----------------------------------\n");
552
553
pr_debug("Fetch PMU test:\n");
554
for (i = 0; i < ARRAY_SIZE(fetch_period); i++) {
555
nr_samples = 0;
556
r = __ibs_period_constraint_test(IBS_FETCH, &fetch_period[i],
557
&nr_samples);
558
559
if (fetch_period[i].ret == FD_ERROR) {
560
pr_debug("freq %d, sample_freq %9ld: %-4s\n",
561
fetch_period[i].freq, fetch_period[i].sample_freq,
562
!r ? "Ok" : "Fail");
563
} else {
564
/*
565
* Although nr_samples == 0 is reported as Fail here,
566
* the failure status is not cascaded up because, we
567
* can not decide whether test really failed or not
568
* without actual samples.
569
*/
570
pr_debug("freq %d, sample_freq %9ld: %-4s (nr samples: %d)\n",
571
fetch_period[i].freq, fetch_period[i].sample_freq,
572
(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
573
}
574
ret |= r;
575
}
576
577
pr_debug("Op PMU test:\n");
578
for (i = 0; i < ARRAY_SIZE(op_period); i++) {
579
nr_samples = 0;
580
r = __ibs_period_constraint_test(IBS_OP, &op_period[i],
581
&nr_samples);
582
583
if (op_period[i].ret == FD_ERROR) {
584
pr_debug("freq %d, sample_freq %9ld: %-4s\n",
585
op_period[i].freq, op_period[i].sample_freq,
586
!r ? "Ok" : "Fail");
587
} else {
588
/*
589
* Although nr_samples == 0 is reported as Fail here,
590
* the failure status is not cascaded up because, we
591
* can not decide whether test really failed or not
592
* without actual samples.
593
*/
594
pr_debug("freq %d, sample_freq %9ld: %-4s (nr samples: %d)\n",
595
op_period[i].freq, op_period[i].sample_freq,
596
(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
597
}
598
ret |= r;
599
}
600
601
return ret;
602
}
603
604
struct ibs_ioctl {
605
/* Input */
606
int freq;
607
unsigned long period;
608
609
/* Expected output */
610
int ret;
611
};
612
613
struct ibs_ioctl fetch_ioctl[] = {
614
{ .freq = 0, .period = 0x0, .ret = FD_ERROR },
615
{ .freq = 0, .period = 0x1, .ret = FD_ERROR },
616
{ .freq = 0, .period = 0xf, .ret = FD_ERROR },
617
{ .freq = 0, .period = 0x10, .ret = FD_SUCCESS },
618
{ .freq = 0, .period = 0x11, .ret = FD_ERROR },
619
{ .freq = 0, .period = 0x1f, .ret = FD_ERROR },
620
{ .freq = 0, .period = 0x20, .ret = FD_SUCCESS },
621
{ .freq = 0, .period = 0x80, .ret = FD_SUCCESS },
622
{ .freq = 0, .period = 0x8f, .ret = FD_ERROR },
623
{ .freq = 0, .period = 0x90, .ret = FD_SUCCESS },
624
{ .freq = 0, .period = 0x91, .ret = FD_ERROR },
625
{ .freq = 0, .period = 0x100, .ret = FD_SUCCESS },
626
{ .freq = 0, .period = 0xfff0, .ret = FD_SUCCESS },
627
{ .freq = 0, .period = 0xffff, .ret = FD_ERROR },
628
{ .freq = 0, .period = 0x10000, .ret = FD_SUCCESS },
629
{ .freq = 0, .period = 0x1fff0, .ret = FD_SUCCESS },
630
{ .freq = 0, .period = 0x1fff5, .ret = FD_ERROR },
631
{ .freq = 1, .period = 0x0, .ret = FD_ERROR },
632
{ .freq = 1, .period = 0x1, .ret = FD_SUCCESS },
633
{ .freq = 1, .period = 0xf, .ret = FD_SUCCESS },
634
{ .freq = 1, .period = 0x10, .ret = FD_SUCCESS },
635
{ .freq = 1, .period = 0x11, .ret = FD_SUCCESS },
636
{ .freq = 1, .period = 0x1f, .ret = FD_SUCCESS },
637
{ .freq = 1, .period = 0x20, .ret = FD_SUCCESS },
638
{ .freq = 1, .period = 0x80, .ret = FD_SUCCESS },
639
{ .freq = 1, .period = 0x8f, .ret = FD_SUCCESS },
640
{ .freq = 1, .period = 0x90, .ret = FD_SUCCESS },
641
{ .freq = 1, .period = 0x91, .ret = FD_SUCCESS },
642
{ .freq = 1, .period = 0x100, .ret = FD_SUCCESS },
643
};
644
645
struct ibs_ioctl op_ioctl[] = {
646
{ .freq = 0, .period = 0x0, .ret = FD_ERROR },
647
{ .freq = 0, .period = 0x1, .ret = FD_ERROR },
648
{ .freq = 0, .period = 0xf, .ret = FD_ERROR },
649
{ .freq = 0, .period = 0x10, .ret = FD_ERROR },
650
{ .freq = 0, .period = 0x11, .ret = FD_ERROR },
651
{ .freq = 0, .period = 0x1f, .ret = FD_ERROR },
652
{ .freq = 0, .period = 0x20, .ret = FD_ERROR },
653
{ .freq = 0, .period = 0x80, .ret = FD_ERROR },
654
{ .freq = 0, .period = 0x8f, .ret = FD_ERROR },
655
{ .freq = 0, .period = 0x90, .ret = FD_SUCCESS },
656
{ .freq = 0, .period = 0x91, .ret = FD_ERROR },
657
{ .freq = 0, .period = 0x100, .ret = FD_SUCCESS },
658
{ .freq = 0, .period = 0xfff0, .ret = FD_SUCCESS },
659
{ .freq = 0, .period = 0xffff, .ret = FD_ERROR },
660
{ .freq = 0, .period = 0x10000, .ret = FD_SUCCESS },
661
{ .freq = 0, .period = 0x1fff0, .ret = FD_SUCCESS },
662
{ .freq = 0, .period = 0x1fff5, .ret = FD_ERROR },
663
{ .freq = 1, .period = 0x0, .ret = FD_ERROR },
664
{ .freq = 1, .period = 0x1, .ret = FD_SUCCESS },
665
{ .freq = 1, .period = 0xf, .ret = FD_SUCCESS },
666
{ .freq = 1, .period = 0x10, .ret = FD_SUCCESS },
667
{ .freq = 1, .period = 0x11, .ret = FD_SUCCESS },
668
{ .freq = 1, .period = 0x1f, .ret = FD_SUCCESS },
669
{ .freq = 1, .period = 0x20, .ret = FD_SUCCESS },
670
{ .freq = 1, .period = 0x80, .ret = FD_SUCCESS },
671
{ .freq = 1, .period = 0x8f, .ret = FD_SUCCESS },
672
{ .freq = 1, .period = 0x90, .ret = FD_SUCCESS },
673
{ .freq = 1, .period = 0x91, .ret = FD_SUCCESS },
674
{ .freq = 1, .period = 0x100, .ret = FD_SUCCESS },
675
};
676
677
static int __ibs_ioctl_test(int ibs_type, struct ibs_ioctl *ibs_ioctl)
678
{
679
struct perf_event_attr attr;
680
int ret = 0;
681
int fd;
682
int r;
683
684
if (ibs_type == IBS_FETCH)
685
fetch_prepare_attr(&attr, 0, ibs_ioctl->freq, 1000);
686
else
687
op_prepare_attr(&attr, 0, ibs_ioctl->freq, 1000);
688
689
/* CPU0, All processes */
690
fd = perf_event_open(&attr, -1, 0, -1, 0);
691
if (fd <= -1) {
692
pr_debug("event_open() Failed\n");
693
return -1;
694
}
695
696
r = ioctl(fd, PERF_EVENT_IOC_PERIOD, &ibs_ioctl->period);
697
if ((ibs_ioctl->ret == FD_SUCCESS && r <= -1) ||
698
(ibs_ioctl->ret == FD_ERROR && r >= 0)) {
699
ret = -1;
700
}
701
702
close(fd);
703
return ret;
704
}
705
706
static int ibs_ioctl_test(void)
707
{
708
unsigned long i;
709
int ret = 0;
710
int r;
711
712
pr_debug("\nIBS ioctl() tests:\n");
713
pr_debug("------------------\n");
714
715
pr_debug("Fetch PMU tests\n");
716
for (i = 0; i < ARRAY_SIZE(fetch_ioctl); i++) {
717
r = __ibs_ioctl_test(IBS_FETCH, &fetch_ioctl[i]);
718
719
pr_debug("ioctl(%s = 0x%-7lx): %s\n",
720
fetch_ioctl[i].freq ? "freq " : "period",
721
fetch_ioctl[i].period, r ? "Fail" : "Ok");
722
ret |= r;
723
}
724
725
pr_debug("Op PMU tests\n");
726
for (i = 0; i < ARRAY_SIZE(op_ioctl); i++) {
727
r = __ibs_ioctl_test(IBS_OP, &op_ioctl[i]);
728
729
pr_debug("ioctl(%s = 0x%-7lx): %s\n",
730
op_ioctl[i].freq ? "freq " : "period",
731
op_ioctl[i].period, r ? "Fail" : "Ok");
732
ret |= r;
733
}
734
735
return ret;
736
}
737
738
static int ibs_freq_neg_test(void)
739
{
740
struct perf_event_attr attr;
741
int fd;
742
743
pr_debug("\nIBS freq (negative) tests:\n");
744
pr_debug("--------------------------\n");
745
746
/*
747
* Assuming perf_event_max_sample_rate <= 100000,
748
* config: 0x300D40 ==> MaxCnt: 200000
749
*/
750
op_prepare_attr(&attr, 0x300D40, 1, 0);
751
752
/* CPU0, All processes */
753
fd = perf_event_open(&attr, -1, 0, -1, 0);
754
if (fd != -1) {
755
pr_debug("freq 1, sample_freq 200000: Fail\n");
756
close(fd);
757
return -1;
758
}
759
760
pr_debug("freq 1, sample_freq 200000: Ok\n");
761
762
return 0;
763
}
764
765
struct ibs_l3missonly {
766
/* Input */
767
int freq;
768
unsigned long sample_freq;
769
770
/* Expected output */
771
int ret;
772
unsigned long min_period;
773
};
774
775
struct ibs_l3missonly fetch_l3missonly = {
776
.freq = 1,
777
.sample_freq = 10000,
778
.ret = FD_SUCCESS,
779
.min_period = 0x10,
780
};
781
782
struct ibs_l3missonly op_l3missonly = {
783
.freq = 1,
784
.sample_freq = 10000,
785
.ret = FD_SUCCESS,
786
.min_period = 0x90,
787
};
788
789
static int __ibs_l3missonly_test(char *perf, int ibs_type, int *nr_samples,
790
struct ibs_l3missonly *l3missonly)
791
{
792
struct perf_event_attr attr;
793
int ret = 0;
794
void *rb;
795
int fd;
796
797
if (l3missonly->sample_freq > perf_event_max_sample_rate)
798
l3missonly->ret = FD_ERROR;
799
800
if (ibs_type == IBS_FETCH) {
801
fetch_prepare_attr(&attr, 0x800000000000000UL, l3missonly->freq,
802
l3missonly->sample_freq);
803
} else {
804
op_prepare_attr(&attr, 0x10000, l3missonly->freq,
805
l3missonly->sample_freq);
806
}
807
808
/* CPU0, All processes */
809
fd = perf_event_open(&attr, -1, 0, -1, 0);
810
if (l3missonly->ret == FD_ERROR) {
811
if (fd != -1) {
812
close(fd);
813
return -1;
814
}
815
return 0;
816
}
817
if (fd == -1) {
818
pr_debug("perf_event_open() failed. [%m]\n");
819
return -1;
820
}
821
822
rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,
823
MAP_SHARED, fd, 0);
824
if (rb == MAP_FAILED) {
825
pr_debug("mmap() failed. [%m]\n");
826
close(fd);
827
return -1;
828
}
829
830
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
831
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
832
833
dummy_workload_2(perf);
834
835
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
836
837
ret = rb_drain_samples(rb, l3missonly->min_period, nr_samples, period_higher);
838
839
munmap(rb, PERF_MMAP_TOTAL_SIZE);
840
close(fd);
841
return ret;
842
}
843
844
static int ibs_l3missonly_test(char *perf)
845
{
846
int nr_samples = 0;
847
int ret = 0;
848
int r = 0;
849
850
pr_debug("\nIBS L3MissOnly test: (takes a while)\n");
851
pr_debug("--------------------\n");
852
853
if (perf_pmu__has_format(fetch_pmu, "l3missonly")) {
854
nr_samples = 0;
855
r = __ibs_l3missonly_test(perf, IBS_FETCH, &nr_samples, &fetch_l3missonly);
856
if (fetch_l3missonly.ret == FD_ERROR) {
857
pr_debug("Fetch L3MissOnly: %-4s\n", !r ? "Ok" : "Fail");
858
} else {
859
/*
860
* Although nr_samples == 0 is reported as Fail here,
861
* the failure status is not cascaded up because, we
862
* can not decide whether test really failed or not
863
* without actual samples.
864
*/
865
pr_debug("Fetch L3MissOnly: %-4s (nr_samples: %d)\n",
866
(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
867
}
868
ret |= r;
869
}
870
871
if (perf_pmu__has_format(op_pmu, "l3missonly")) {
872
nr_samples = 0;
873
r = __ibs_l3missonly_test(perf, IBS_OP, &nr_samples, &op_l3missonly);
874
if (op_l3missonly.ret == FD_ERROR) {
875
pr_debug("Op L3MissOnly: %-4s\n", !r ? "Ok" : "Fail");
876
} else {
877
/*
878
* Although nr_samples == 0 is reported as Fail here,
879
* the failure status is not cascaded up because, we
880
* can not decide whether test really failed or not
881
* without actual samples.
882
*/
883
pr_debug("Op L3MissOnly: %-4s (nr_samples: %d)\n",
884
(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
885
}
886
ret |= r;
887
}
888
889
return ret;
890
}
891
892
static unsigned int get_perf_event_max_sample_rate(void)
893
{
894
unsigned int max_sample_rate = 100000;
895
FILE *fp;
896
int ret;
897
898
fp = fopen("/proc/sys/kernel/perf_event_max_sample_rate", "r");
899
if (!fp) {
900
pr_debug("Can't open perf_event_max_sample_rate. Assuming %d\n",
901
max_sample_rate);
902
goto out;
903
}
904
905
ret = fscanf(fp, "%d", &max_sample_rate);
906
if (ret == EOF) {
907
pr_debug("Can't read perf_event_max_sample_rate. Assuming 100000\n");
908
max_sample_rate = 100000;
909
}
910
fclose(fp);
911
912
out:
913
return max_sample_rate;
914
}
915
916
/*
917
* Bunch of IBS sample period fixes that this test exercise went in v6.15.
918
* Skip the test on older kernels to distinguish between test failure due
919
* to a new bug vs known failure due to older kernel.
920
*/
921
static bool kernel_v6_15_or_newer(void)
922
{
923
struct utsname utsname;
924
char *endptr = NULL;
925
long major, minor;
926
927
if (uname(&utsname) < 0) {
928
pr_debug("uname() failed. [%m]");
929
return false;
930
}
931
932
major = strtol(utsname.release, &endptr, 10);
933
endptr++;
934
minor = strtol(endptr, NULL, 10);
935
936
return major >= 6 && minor >= 15;
937
}
938
939
int test__amd_ibs_period(struct test_suite *test __maybe_unused,
940
int subtest __maybe_unused)
941
{
942
char perf[PATH_MAX] = {'\0'};
943
int ret = TEST_OK;
944
945
page_size = sysconf(_SC_PAGESIZE);
946
947
/*
948
* Reading perf_event_max_sample_rate only once _might_ cause some
949
* of the test to fail if kernel changes it after reading it here.
950
*/
951
perf_event_max_sample_rate = get_perf_event_max_sample_rate();
952
fetch_pmu = perf_pmus__find("ibs_fetch");
953
op_pmu = perf_pmus__find("ibs_op");
954
955
if (!x86__is_amd_cpu() || !fetch_pmu || !op_pmu)
956
return TEST_SKIP;
957
958
if (!kernel_v6_15_or_newer()) {
959
pr_debug("Need v6.15 or newer kernel. Skipping.\n");
960
return TEST_SKIP;
961
}
962
963
perf_exe(perf, sizeof(perf));
964
965
if (sched_affine(0))
966
return TEST_FAIL;
967
968
/*
969
* Perf event can be opened in two modes:
970
* 1 Freq mode
971
* perf_event_attr->freq = 1, ->sample_freq = <frequency>
972
* 2 Sample period mode
973
* perf_event_attr->freq = 0, ->sample_period = <period>
974
*
975
* Instead of using above interface, IBS event in 'sample period mode'
976
* can also be opened by passing <period> value directly in a MaxCnt
977
* bitfields of perf_event_attr->config. Test this IBS specific special
978
* interface.
979
*/
980
if (ibs_config_test())
981
ret = TEST_FAIL;
982
983
/*
984
* IBS Fetch and Op PMUs have HW constraints on minimum sample period.
985
* Also, sample period value must be in multiple of 0x10. Test that IBS
986
* driver honors HW constraints for various possible values in Freq as
987
* well as Sample Period mode IBS events.
988
*/
989
if (ibs_period_constraint_test())
990
ret = TEST_FAIL;
991
992
/*
993
* Test ioctl() with various sample period values for IBS event.
994
*/
995
if (ibs_ioctl_test())
996
ret = TEST_FAIL;
997
998
/*
999
* Test that opening of freq mode IBS event fails when the freq value
1000
* is passed through ->config, not explicitly in ->sample_freq. Also
1001
* use high freq value (beyond perf_event_max_sample_rate) to test IBS
1002
* driver do not bypass perf_event_max_sample_rate checks.
1003
*/
1004
if (ibs_freq_neg_test())
1005
ret = TEST_FAIL;
1006
1007
/*
1008
* L3MissOnly is a post-processing filter, i.e. IBS HW checks for L3
1009
* Miss at the completion of the tagged uOp. The sample is discarded
1010
* if the tagged uOp did not cause L3Miss. Also, IBS HW internally
1011
* resets CurCnt to a small pseudo-random value and resumes counting.
1012
* A new uOp is tagged once CurCnt reaches to MaxCnt. But the process
1013
* repeats until the tagged uOp causes an L3 Miss.
1014
*
1015
* With the freq mode event, the next sample period is calculated by
1016
* generic kernel on every sample to achieve desired freq of samples.
1017
*
1018
* Since the number of times HW internally reset CurCnt and the pseudo-
1019
* random value of CurCnt for all those occurrences are not known to SW,
1020
* the sample period adjustment by kernel goes for a toes for freq mode
1021
* IBS events. Kernel will set very small period for the next sample if
1022
* the window between current sample and prev sample is too high due to
1023
* multiple samples being discarded internally by IBS HW.
1024
*
1025
* Test that IBS sample period constraints are honored when L3MissOnly
1026
* is ON.
1027
*/
1028
if (ibs_l3missonly_test(perf))
1029
ret = TEST_FAIL;
1030
1031
return ret;
1032
}
1033
1034