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