File Index
Files and directories
Sign in
FILE

amd-ibs-period.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/arch/x86/tests/amd-ibs-period.c C
   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
  21static 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
  31enum {
  32	FD_ERROR,
  33	FD_SUCCESS,
  34};
  35
  36enum {
  37	IBS_FETCH,
  38	IBS_OP,
  39};
  40
  41struct perf_pmu *fetch_pmu;
  42struct perf_pmu *op_pmu;
  43unsigned int perf_event_max_sample_rate;
  44
  45/* Dummy workload to generate IBS samples. */
  46static 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
  96out:
  97	free(p);
  98	return ret;
  99}
 100
 101/* Another dummy workload to generate IBS samples. */
 102static 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
 119static 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
 132static void
 133copy_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
 148static 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
 169static 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. */
 182static 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 */
 191static int period_higher(unsigned long min_period, unsigned long act_period)
 192{
 193	return min_period <= act_period ? 0 : -1;
 194}
 195
 196static 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
 229static 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
 235static 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
 250static 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
 265struct 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 */
 278struct 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
 287struct 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
 305static 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
 355static 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
 412struct ibs_period {
 413	/* Input */
 414	int freq;
 415	unsigned long sample_freq;
 416
 417	/* Output */
 418	int ret;
 419	unsigned long period;
 420};
 421
 422struct 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
 455struct 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
 488static 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
 543static 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
 604struct ibs_ioctl {
 605	/* Input */
 606	int freq;
 607	unsigned long period;
 608
 609	/* Expected output */
 610	int ret;
 611};
 612
 613struct 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
 645struct 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
 677static 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
 706static 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
 738static 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
 765struct 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
 775struct ibs_l3missonly fetch_l3missonly = {
 776	.freq = 1,
 777	.sample_freq = 10000,
 778	.ret = FD_SUCCESS,
 779	.min_period = 0x10,
 780};
 781
 782struct ibs_l3missonly op_l3missonly = {
 783	.freq = 1,
 784	.sample_freq = 10000,
 785	.ret = FD_SUCCESS,
 786	.min_period = 0x90,
 787};
 788
 789static 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
 844static 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
 892static 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
 912out:
 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 */
 921static 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
 939int 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}
~/ Read-only file preview.