xref: /linux/tools/perf/arch/x86/tests/amd-ibs-period.c (revision 0939bd2fcf337243133b0271335a2838857c319f)
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