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