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. */
dummy_workload_1(unsigned long count)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. */
dummy_workload_2(char * perf)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
sched_affine(int cpu)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
copy_sample_data(void * src,unsigned long offset,void * dest,size_t size)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
rb_read(struct perf_event_mmap_page * rb,void * dest,size_t size)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
rb_skip(struct perf_event_mmap_page * rb,size_t size)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. */
period_equal(unsigned long exp_period,unsigned long act_period)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 */
period_higher(unsigned long min_period,unsigned long act_period)191 static int period_higher(unsigned long min_period, unsigned long act_period)
192 {
193 return min_period <= act_period ? 0 : -1;
194 }
195
rb_drain_samples(struct perf_event_mmap_page * rb,unsigned long exp_period,int * nr_samples,int (* callback)(unsigned long,unsigned long))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
perf_event_open(struct perf_event_attr * attr,pid_t pid,int cpu,int group_fd,unsigned long flags)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
fetch_prepare_attr(struct perf_event_attr * attr,unsigned long long config,int freq,unsigned long sample_period)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
op_prepare_attr(struct perf_event_attr * attr,unsigned long config,int freq,unsigned long sample_period)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
__ibs_config_test(int ibs_type,struct ibs_configs * config,int * nr_samples)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
ibs_config_test(void)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
__ibs_period_constraint_test(int ibs_type,struct ibs_period * period,int * nr_samples)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
ibs_period_constraint_test(void)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
__ibs_ioctl_test(int ibs_type,struct ibs_ioctl * ibs_ioctl)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
ibs_ioctl_test(void)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
ibs_freq_neg_test(void)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
__ibs_l3missonly_test(char * perf,int ibs_type,int * nr_samples,struct ibs_l3missonly * l3missonly)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
ibs_l3missonly_test(char * perf)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
get_perf_event_max_sample_rate(void)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 */
kernel_v6_15_or_newer(void)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
test__amd_ibs_period(struct test_suite * test __maybe_unused,int subtest __maybe_unused)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