[PATCH v4 4/4] perf test amd ibs: Add sample period unit test

Ravi Bangoria posted 4 patches 9 months, 2 weeks ago
[PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Ravi Bangoria 9 months, 2 weeks ago
IBS Fetch and IBS Op PMUs has various constraints on supported sample
periods. Add perf unit tests to test those.

Running it in parallel with other tests causes intermittent failures.
Mark it exclusive to force it to run sequentially. Sample output on a
Zen5 machine:

Without kernel fixes:

  $ sudo ./perf test -vv 112
  112: AMD IBS sample period:
  --- start ---
  test child forked, pid 8774
  Using CPUID AuthenticAMD-26-2-1

  IBS config tests:
  -----------------
  Fetch PMU tests:
  0xffff            : Ok   (nr samples: 1078)
  0x1000            : Ok   (nr samples: 17030)
  0xff              : Ok   (nr samples: 41068)
  0x1               : Ok   (nr samples: 40543)
  0x0               : Ok
  0x10000           : Ok
  Op PMU tests:
  0x0               : Ok
  0x1               : Fail
  0x8               : Fail
  0x9               : Ok   (nr samples: 40543)
  0xf               : Ok   (nr samples: 40543)
  0x1000            : Ok   (nr samples: 18736)
  0xffff            : Ok   (nr samples: 1168)
  0x10000           : Ok
  0x100000          : Fail (nr samples: 14)
  0xf00000          : Fail (nr samples: 1)
  0xf0ffff          : Fail (nr samples: 1)
  0x1f0ffff         : Fail (nr samples: 1)
  0x7f0ffff         : Fail (nr samples: 0)
  0x8f0ffff         : Ok
  0x17f0ffff        : Ok

  IBS sample period constraint tests:
  -----------------------------------
  Fetch PMU test:
  freq 0, sample_freq         0: Ok
  freq 0, sample_freq         1: Fail
  freq 0, sample_freq        15: Fail
  freq 0, sample_freq        16: Ok   (nr samples: 1604)
  freq 0, sample_freq        17: Ok   (nr samples: 1604)
  freq 0, sample_freq       143: Ok   (nr samples: 1604)
  freq 0, sample_freq       144: Ok   (nr samples: 1604)
  freq 0, sample_freq       145: Ok   (nr samples: 1604)
  freq 0, sample_freq      1234: Ok   (nr samples: 1566)
  freq 0, sample_freq      4103: Ok   (nr samples: 1119)
  freq 0, sample_freq     65520: Ok   (nr samples: 2264)
  freq 0, sample_freq     65535: Ok   (nr samples: 2263)
  freq 0, sample_freq     65552: Ok   (nr samples: 1166)
  freq 0, sample_freq   8388607: Ok   (nr samples: 268)
  freq 0, sample_freq 268435455: Ok   (nr samples: 8)
  freq 1, sample_freq         0: Ok
  freq 1, sample_freq         1: Ok   (nr samples: 4)
  freq 1, sample_freq        15: Ok   (nr samples: 4)
  freq 1, sample_freq        16: Ok   (nr samples: 4)
  freq 1, sample_freq        17: Ok   (nr samples: 4)
  freq 1, sample_freq       143: Ok   (nr samples: 5)
  freq 1, sample_freq       144: Ok   (nr samples: 5)
  freq 1, sample_freq       145: Ok   (nr samples: 5)
  freq 1, sample_freq      1234: Ok   (nr samples: 7)
  freq 1, sample_freq      4103: Ok   (nr samples: 35)
  freq 1, sample_freq     65520: Ok   (nr samples: 642)
  freq 1, sample_freq     65535: Ok   (nr samples: 636)
  freq 1, sample_freq     65552: Ok   (nr samples: 651)
  freq 1, sample_freq   8388607: Ok
  Op PMU test:
  freq 0, sample_freq         0: Ok
  freq 0, sample_freq         1: Fail
  freq 0, sample_freq        15: Fail
  freq 0, sample_freq        16: Fail
  freq 0, sample_freq        17: Fail
  freq 0, sample_freq       143: Fail
  freq 0, sample_freq       144: Ok   (nr samples: 1604)
  freq 0, sample_freq       145: Ok   (nr samples: 1604)
  freq 0, sample_freq      1234: Ok   (nr samples: 1604)
  freq 0, sample_freq      4103: Ok   (nr samples: 1604)
  freq 0, sample_freq     65520: Ok   (nr samples: 2227)
  freq 0, sample_freq     65535: Ok   (nr samples: 2296)
  freq 0, sample_freq     65552: Ok   (nr samples: 2213)
  freq 0, sample_freq   8388607: Ok   (nr samples: 250)
  freq 0, sample_freq 268435455: Ok   (nr samples: 8)
  freq 1, sample_freq         0: Ok
  freq 1, sample_freq         1: Fail (nr samples: 4)
  freq 1, sample_freq        15: Fail (nr samples: 4)
  freq 1, sample_freq        16: Fail (nr samples: 4)
  freq 1, sample_freq        17: Fail (nr samples: 4)
  freq 1, sample_freq       143: Fail (nr samples: 5)
  freq 1, sample_freq       144: Fail (nr samples: 5)
  freq 1, sample_freq       145: Fail (nr samples: 5)
  freq 1, sample_freq      1234: Fail (nr samples: 8)
  freq 1, sample_freq      4103: Fail (nr samples: 33)
  freq 1, sample_freq     65520: Fail (nr samples: 546)
  freq 1, sample_freq     65535: Fail (nr samples: 544)
  freq 1, sample_freq     65552: Fail (nr samples: 555)
  freq 1, sample_freq   8388607: Ok

  IBS ioctl() tests:
  ------------------
  Fetch PMU tests
  ioctl(period = 0x0      ): Ok
  ioctl(period = 0x1      ): Fail
  ioctl(period = 0xf      ): Fail
  ioctl(period = 0x10     ): Ok
  ioctl(period = 0x11     ): Fail
  ioctl(period = 0x1f     ): Fail
  ioctl(period = 0x20     ): Ok
  ioctl(period = 0x80     ): Ok
  ioctl(period = 0x8f     ): Fail
  ioctl(period = 0x90     ): Ok
  ioctl(period = 0x91     ): Fail
  ioctl(period = 0x100    ): Ok
  ioctl(period = 0xfff0   ): Ok
  ioctl(period = 0xffff   ): Fail
  ioctl(period = 0x10000  ): Ok
  ioctl(period = 0x1fff0  ): Ok
  ioctl(period = 0x1fff5  ): Fail
  ioctl(freq   = 0x0      ): Ok
  ioctl(freq   = 0x1      ): Ok
  ioctl(freq   = 0xf      ): Ok
  ioctl(freq   = 0x10     ): Ok
  ioctl(freq   = 0x11     ): Ok
  ioctl(freq   = 0x1f     ): Ok
  ioctl(freq   = 0x20     ): Ok
  ioctl(freq   = 0x80     ): Ok
  ioctl(freq   = 0x8f     ): Ok
  ioctl(freq   = 0x90     ): Ok
  ioctl(freq   = 0x91     ): Ok
  ioctl(freq   = 0x100    ): Ok
  Op PMU tests
  ioctl(period = 0x0      ): Ok
  ioctl(period = 0x1      ): Fail
  ioctl(period = 0xf      ): Fail
  ioctl(period = 0x10     ): Fail
  ioctl(period = 0x11     ): Fail
  ioctl(period = 0x1f     ): Fail
  ioctl(period = 0x20     ): Fail
  ioctl(period = 0x80     ): Fail
  ioctl(period = 0x8f     ): Fail
  ioctl(period = 0x90     ): Ok
  ioctl(period = 0x91     ): Fail
  ioctl(period = 0x100    ): Ok
  ioctl(period = 0xfff0   ): Ok
  ioctl(period = 0xffff   ): Fail
  ioctl(period = 0x10000  ): Ok
  ioctl(period = 0x1fff0  ): Ok
  ioctl(period = 0x1fff5  ): Fail
  ioctl(freq   = 0x0      ): Ok
  ioctl(freq   = 0x1      ): Ok
  ioctl(freq   = 0xf      ): Ok
  ioctl(freq   = 0x10     ): Ok
  ioctl(freq   = 0x11     ): Ok
  ioctl(freq   = 0x1f     ): Ok
  ioctl(freq   = 0x20     ): Ok
  ioctl(freq   = 0x80     ): Ok
  ioctl(freq   = 0x8f     ): Ok
  ioctl(freq   = 0x90     ): Ok
  ioctl(freq   = 0x91     ): Ok
  ioctl(freq   = 0x100    ): Ok

  IBS freq (negative) tests:
  --------------------------
  freq 1, sample_freq 200000: Fail

  IBS L3MissOnly test: (takes a while)
  --------------------
  Fetch L3MissOnly: Fail (nr_samples: 1213)
  Op L3MissOnly:    Ok   (nr_samples: 1193)
  ---- end(-1) ----
  112: AMD IBS sample period                                           : FAILED!

With kernel fixes:

  $ sudo ./perf test -vv 112
  112: AMD IBS sample period:
  --- start ---
  test child forked, pid 6939
  Using CPUID AuthenticAMD-26-2-1

  IBS config tests:
  -----------------
  Fetch PMU tests:
  0xffff            : Ok   (nr samples: 969)
  0x1000            : Ok   (nr samples: 15540)
  0xff              : Ok   (nr samples: 40555)
  0x1               : Ok   (nr samples: 40543)
  0x0               : Ok
  0x10000           : Ok
  Op PMU tests:
  0x0               : Ok
  0x1               : Ok
  0x8               : Ok
  0x9               : Ok   (nr samples: 40543)
  0xf               : Ok   (nr samples: 40543)
  0x1000            : Ok   (nr samples: 19156)
  0xffff            : Ok   (nr samples: 1169)
  0x10000           : Ok
  0x100000          : Ok   (nr samples: 1151)
  0xf00000          : Ok   (nr samples: 76)
  0xf0ffff          : Ok   (nr samples: 73)
  0x1f0ffff         : Ok   (nr samples: 33)
  0x7f0ffff         : Ok   (nr samples: 10)
  0x8f0ffff         : Ok
  0x17f0ffff        : Ok

  IBS sample period constraint tests:
  -----------------------------------
  Fetch PMU test:
  freq 0, sample_freq         0: Ok
  freq 0, sample_freq         1: Ok
  freq 0, sample_freq        15: Ok
  freq 0, sample_freq        16: Ok   (nr samples: 1203)
  freq 0, sample_freq        17: Ok   (nr samples: 1604)
  freq 0, sample_freq       143: Ok   (nr samples: 1604)
  freq 0, sample_freq       144: Ok   (nr samples: 1604)
  freq 0, sample_freq       145: Ok   (nr samples: 1604)
  freq 0, sample_freq      1234: Ok   (nr samples: 1604)
  freq 0, sample_freq      4103: Ok   (nr samples: 1343)
  freq 0, sample_freq     65520: Ok   (nr samples: 2254)
  freq 0, sample_freq     65535: Ok   (nr samples: 2136)
  freq 0, sample_freq     65552: Ok   (nr samples: 1158)
  freq 0, sample_freq   8388607: Ok   (nr samples: 257)
  freq 0, sample_freq 268435455: Ok   (nr samples: 8)
  freq 1, sample_freq         0: Ok
  freq 1, sample_freq         1: Ok   (nr samples: 4)
  freq 1, sample_freq        15: Ok   (nr samples: 4)
  freq 1, sample_freq        16: Ok   (nr samples: 4)
  freq 1, sample_freq        17: Ok   (nr samples: 4)
  freq 1, sample_freq       143: Ok   (nr samples: 5)
  freq 1, sample_freq       144: Ok   (nr samples: 5)
  freq 1, sample_freq       145: Ok   (nr samples: 5)
  freq 1, sample_freq      1234: Ok   (nr samples: 8)
  freq 1, sample_freq      4103: Ok   (nr samples: 34)
  freq 1, sample_freq     65520: Ok   (nr samples: 458)
  freq 1, sample_freq     65535: Ok   (nr samples: 628)
  freq 1, sample_freq     65552: Ok   (nr samples: 396)
  freq 1, sample_freq   8388607: Ok
  Op PMU test:
  freq 0, sample_freq         0: Ok
  freq 0, sample_freq         1: Ok
  freq 0, sample_freq        15: Ok
  freq 0, sample_freq        16: Ok
  freq 0, sample_freq        17: Ok
  freq 0, sample_freq       143: Ok
  freq 0, sample_freq       144: Ok   (nr samples: 1604)
  freq 0, sample_freq       145: Ok   (nr samples: 1604)
  freq 0, sample_freq      1234: Ok   (nr samples: 1604)
  freq 0, sample_freq      4103: Ok   (nr samples: 1604)
  freq 0, sample_freq     65520: Ok   (nr samples: 2250)
  freq 0, sample_freq     65535: Ok   (nr samples: 2158)
  freq 0, sample_freq     65552: Ok   (nr samples: 2296)
  freq 0, sample_freq   8388607: Ok   (nr samples: 243)
  freq 0, sample_freq 268435455: Ok   (nr samples: 6)
  freq 1, sample_freq         0: Ok
  freq 1, sample_freq         1: Ok   (nr samples: 4)
  freq 1, sample_freq        15: Ok   (nr samples: 4)
  freq 1, sample_freq        16: Ok   (nr samples: 4)
  freq 1, sample_freq        17: Ok   (nr samples: 4)
  freq 1, sample_freq       143: Ok   (nr samples: 4)
  freq 1, sample_freq       144: Ok   (nr samples: 5)
  freq 1, sample_freq       145: Ok   (nr samples: 4)
  freq 1, sample_freq      1234: Ok   (nr samples: 6)
  freq 1, sample_freq      4103: Ok   (nr samples: 27)
  freq 1, sample_freq     65520: Ok   (nr samples: 542)
  freq 1, sample_freq     65535: Ok   (nr samples: 550)
  freq 1, sample_freq     65552: Ok   (nr samples: 552)
  freq 1, sample_freq   8388607: Ok

  IBS ioctl() tests:
  ------------------
  Fetch PMU tests
  ioctl(period = 0x0      ): Ok
  ioctl(period = 0x1      ): Ok
  ioctl(period = 0xf      ): Ok
  ioctl(period = 0x10     ): Ok
  ioctl(period = 0x11     ): Ok
  ioctl(period = 0x1f     ): Ok
  ioctl(period = 0x20     ): Ok
  ioctl(period = 0x80     ): Ok
  ioctl(period = 0x8f     ): Ok
  ioctl(period = 0x90     ): Ok
  ioctl(period = 0x91     ): Ok
  ioctl(period = 0x100    ): Ok
  ioctl(period = 0xfff0   ): Ok
  ioctl(period = 0xffff   ): Ok
  ioctl(period = 0x10000  ): Ok
  ioctl(period = 0x1fff0  ): Ok
  ioctl(period = 0x1fff5  ): Ok
  ioctl(freq   = 0x0      ): Ok
  ioctl(freq   = 0x1      ): Ok
  ioctl(freq   = 0xf      ): Ok
  ioctl(freq   = 0x10     ): Ok
  ioctl(freq   = 0x11     ): Ok
  ioctl(freq   = 0x1f     ): Ok
  ioctl(freq   = 0x20     ): Ok
  ioctl(freq   = 0x80     ): Ok
  ioctl(freq   = 0x8f     ): Ok
  ioctl(freq   = 0x90     ): Ok
  ioctl(freq   = 0x91     ): Ok
  ioctl(freq   = 0x100    ): Ok
  Op PMU tests
  ioctl(period = 0x0      ): Ok
  ioctl(period = 0x1      ): Ok
  ioctl(period = 0xf      ): Ok
  ioctl(period = 0x10     ): Ok
  ioctl(period = 0x11     ): Ok
  ioctl(period = 0x1f     ): Ok
  ioctl(period = 0x20     ): Ok
  ioctl(period = 0x80     ): Ok
  ioctl(period = 0x8f     ): Ok
  ioctl(period = 0x90     ): Ok
  ioctl(period = 0x91     ): Ok
  ioctl(period = 0x100    ): Ok
  ioctl(period = 0xfff0   ): Ok
  ioctl(period = 0xffff   ): Ok
  ioctl(period = 0x10000  ): Ok
  ioctl(period = 0x1fff0  ): Ok
  ioctl(period = 0x1fff5  ): Ok
  ioctl(freq   = 0x0      ): Ok
  ioctl(freq   = 0x1      ): Ok
  ioctl(freq   = 0xf      ): Ok
  ioctl(freq   = 0x10     ): Ok
  ioctl(freq   = 0x11     ): Ok
  ioctl(freq   = 0x1f     ): Ok
  ioctl(freq   = 0x20     ): Ok
  ioctl(freq   = 0x80     ): Ok
  ioctl(freq   = 0x8f     ): Ok
  ioctl(freq   = 0x90     ): Ok
  ioctl(freq   = 0x91     ): Ok
  ioctl(freq   = 0x100    ): Ok

  IBS freq (negative) tests:
  --------------------------
  freq 1, sample_freq 200000: Ok

  IBS L3MissOnly test: (takes a while)
  --------------------
  Fetch L3MissOnly: Ok   (nr_samples: 1301)
  Op L3MissOnly:    Ok   (nr_samples: 1590)
  ---- end(0) ----
  112: AMD IBS sample period                                           : Ok

Signed-off-by: Ravi Bangoria <ravi.bangoria@amd.com>
---
 tools/perf/arch/x86/include/arch-tests.h   |    1 +
 tools/perf/arch/x86/tests/Build            |    1 +
 tools/perf/arch/x86/tests/amd-ibs-period.c | 1001 ++++++++++++++++++++
 tools/perf/arch/x86/tests/arch-tests.c     |    2 +
 4 files changed, 1005 insertions(+)
 create mode 100644 tools/perf/arch/x86/tests/amd-ibs-period.c

diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h
index c0421a26b875..4fd425157d7d 100644
--- a/tools/perf/arch/x86/include/arch-tests.h
+++ b/tools/perf/arch/x86/include/arch-tests.h
@@ -14,6 +14,7 @@ int test__intel_pt_hybrid_compat(struct test_suite *test, int subtest);
 int test__bp_modify(struct test_suite *test, int subtest);
 int test__x86_sample_parsing(struct test_suite *test, int subtest);
 int test__amd_ibs_via_core_pmu(struct test_suite *test, int subtest);
+int test__amd_ibs_period(struct test_suite *test, int subtest);
 int test__hybrid(struct test_suite *test, int subtest);
 
 extern struct test_suite *arch_tests[];
diff --git a/tools/perf/arch/x86/tests/Build b/tools/perf/arch/x86/tests/Build
index 86262c720857..5e00cbfd2d56 100644
--- a/tools/perf/arch/x86/tests/Build
+++ b/tools/perf/arch/x86/tests/Build
@@ -10,6 +10,7 @@ perf-test-$(CONFIG_AUXTRACE) += insn-x86.o
 endif
 perf-test-$(CONFIG_X86_64) += bp-modify.o
 perf-test-y += amd-ibs-via-core-pmu.o
+perf-test-y += amd-ibs-period.o
 
 ifdef SHELLCHECK
   SHELL_TESTS := gen-insn-x86-dat.sh
diff --git a/tools/perf/arch/x86/tests/amd-ibs-period.c b/tools/perf/arch/x86/tests/amd-ibs-period.c
new file mode 100644
index 000000000000..0cf3656e4b9b
--- /dev/null
+++ b/tools/perf/arch/x86/tests/amd-ibs-period.c
@@ -0,0 +1,1001 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <sched.h>
+#include <sys/syscall.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+#include "arch-tests.h"
+#include "linux/perf_event.h"
+#include "linux/zalloc.h"
+#include "tests/tests.h"
+#include "../perf-sys.h"
+#include "pmu.h"
+#include "pmus.h"
+#include "debug.h"
+#include "util.h"
+#include "strbuf.h"
+#include "../util/env.h"
+
+#define PAGE_SIZE               sysconf(_SC_PAGESIZE)
+
+#define PERF_MMAP_DATA_PAGES    32L
+#define PERF_MMAP_DATA_SIZE     (PERF_MMAP_DATA_PAGES * PAGE_SIZE)
+#define PERF_MMAP_DATA_MASK     (PERF_MMAP_DATA_SIZE - 1)
+#define PERF_MMAP_TOTAL_PAGES   (PERF_MMAP_DATA_PAGES + 1)
+#define PERF_MMAP_TOTAL_SIZE    (PERF_MMAP_TOTAL_PAGES * PAGE_SIZE)
+
+#define rmb()                   asm volatile("lfence":::"memory")
+
+enum {
+	FD_ERROR,
+	FD_SUCCESS,
+};
+
+enum {
+	IBS_FETCH,
+	IBS_OP,
+};
+
+struct perf_pmu *fetch_pmu;
+struct perf_pmu *op_pmu;
+unsigned int perf_event_max_sample_rate;
+
+/* Dummy workload to generate IBS samples. */
+static int dummy_workload_1(unsigned long count)
+{
+	int (*func)(void);
+	int ret = 0;
+	char *p;
+	char insn1[] = {
+		0xb8, 0x01, 0x00, 0x00, 0x00, /* mov 1,%eax */
+		0xc3, /* ret */
+		0xcc, /* int 3 */
+	};
+
+	char insn2[] = {
+		0xb8, 0x02, 0x00, 0x00, 0x00, /* mov 2,%eax */
+		0xc3, /* ret */
+		0xcc, /* int 3 */
+	};
+
+	p = zalloc(2 * PAGE_SIZE);
+	if (!p) {
+		printf("malloc() failed. %m");
+		return 1;
+	}
+
+	func = (void *)((unsigned long)(p + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
+
+	ret = mprotect(func, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
+	if (ret) {
+		printf("mprotect() failed. %m");
+		goto out;
+	}
+
+	if (count < 100000)
+		count = 100000;
+	else if (count > 10000000)
+		count = 10000000;
+	while (count--) {
+		memcpy(func, insn1, sizeof(insn1));
+		if (func() != 1) {
+			pr_debug("ERROR insn1\n");
+			ret = -1;
+			goto out;
+		}
+		memcpy(func, insn2, sizeof(insn2));
+		if (func() != 2) {
+			pr_debug("ERROR insn2\n");
+			ret = -1;
+			goto out;
+		}
+	}
+
+out:
+	free(p);
+	return ret;
+}
+
+/* Another dummy workload to generate IBS samples. */
+static void dummy_workload_2(char *perf)
+{
+	char bench[] = " bench sched messaging -g 10 -l 5000 > /dev/null 2>&1";
+	char taskset[] = "taskset -c 0 ";
+	int ret __maybe_unused;
+	struct strbuf sb;
+	char *cmd;
+
+	strbuf_init(&sb, 0);
+	strbuf_add(&sb, taskset, strlen(taskset));
+	strbuf_add(&sb, perf, strlen(perf));
+	strbuf_add(&sb, bench, strlen(bench));
+	cmd = strbuf_detach(&sb, NULL);
+	ret = system(cmd);
+	free(cmd);
+}
+
+static int sched_affine(int cpu)
+{
+	cpu_set_t set;
+
+	CPU_ZERO(&set);
+	CPU_SET(cpu, &set);
+	if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) {
+		pr_debug("sched_setaffinity() failed. [%m]");
+		return -1;
+	}
+	return 0;
+}
+
+static void
+copy_sample_data(void *src, unsigned long offset, void *dest, size_t size)
+{
+	size_t chunk1_size, chunk2_size;
+
+	if ((offset + size) < (size_t)PERF_MMAP_DATA_SIZE) {
+		memcpy(dest, src + offset, size);
+	} else {
+		chunk1_size = PERF_MMAP_DATA_SIZE - offset;
+		chunk2_size = size - chunk1_size;
+
+		memcpy(dest, src + offset, chunk1_size);
+		memcpy(dest + chunk1_size, src, chunk2_size);
+	}
+}
+
+static int rb_read(struct perf_event_mmap_page *rb, void *dest, size_t size)
+{
+	void *base;
+	unsigned long data_tail, data_head;
+
+	/* Casting to (void *) is needed. */
+	base = (void *)rb + PAGE_SIZE;
+
+	data_head = rb->data_head;
+	rmb();
+	data_tail = rb->data_tail;
+
+	if ((data_head - data_tail) < size)
+		return -1;
+
+	data_tail &= PERF_MMAP_DATA_MASK;
+	copy_sample_data(base, data_tail, dest, size);
+	rb->data_tail += size;
+	return 0;
+}
+
+static void rb_skip(struct perf_event_mmap_page *rb, size_t size)
+{
+	size_t data_head = rb->data_head;
+
+	rmb();
+
+	if ((rb->data_tail + size) > data_head)
+		rb->data_tail = data_head;
+	else
+		rb->data_tail += size;
+}
+
+/* Sample period value taken from perf sample must match with expected value. */
+static int period_equal(unsigned long exp_period, unsigned long act_period)
+{
+	return exp_period == act_period ? 0 : -1;
+}
+
+/*
+ * Sample period value taken from perf sample must be >= minimum sample period
+ * supported by IBS HW.
+ */
+static int period_higher(unsigned long min_period, unsigned long act_period)
+{
+	return min_period <= act_period ? 0 : -1;
+}
+
+static int rb_drain_samples(struct perf_event_mmap_page *rb,
+			    unsigned long exp_period,
+			    int *nr_samples,
+			    int (*callback)(unsigned long, unsigned long))
+{
+	struct perf_event_header hdr;
+	unsigned long period;
+	int ret = 0;
+
+	/*
+	 * PERF_RECORD_SAMPLE:
+	 * struct {
+	 *	struct perf_event_header hdr;
+	 *	{ u64			 period;     } && PERF_SAMPLE_PERIOD
+	 * };
+	 */
+	while (1) {
+		if (rb_read(rb, &hdr, sizeof(hdr)))
+			return ret;
+
+		if (hdr.type == PERF_RECORD_SAMPLE) {
+			(*nr_samples)++;
+			period = 0;
+			if (rb_read(rb, &period, sizeof(period)))
+				pr_debug("rb_read(period) error. [%m]");
+			ret |= callback(exp_period, period);
+		} else {
+			rb_skip(rb, hdr.size - sizeof(hdr));
+		}
+	}
+	return ret;
+}
+
+static long perf_event_open(struct perf_event_attr *attr, pid_t pid,
+			    int cpu, int group_fd, unsigned long flags)
+{
+	return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
+}
+
+static void fetch_prepare_attr(struct perf_event_attr *attr,
+			       unsigned long long config, int freq,
+			       unsigned long sample_period)
+{
+	memset(attr, 0, sizeof(struct perf_event_attr));
+
+	attr->type = fetch_pmu->type;
+	attr->size = sizeof(struct perf_event_attr);
+	attr->config = config;
+	attr->disabled = 1;
+	attr->sample_type = PERF_SAMPLE_PERIOD;
+	attr->freq = freq;
+	attr->sample_period = sample_period; /* = ->sample_freq */
+}
+
+static void op_prepare_attr(struct perf_event_attr *attr,
+			    unsigned long config, int freq,
+			    unsigned long sample_period)
+{
+	memset(attr, 0, sizeof(struct perf_event_attr));
+
+	attr->type = op_pmu->type;
+	attr->size = sizeof(struct perf_event_attr);
+	attr->config = config;
+	attr->disabled = 1;
+	attr->sample_type = PERF_SAMPLE_PERIOD;
+	attr->freq = freq;
+	attr->sample_period = sample_period; /* = ->sample_freq */
+}
+
+struct ibs_configs {
+	/* Input */
+	unsigned long config;
+
+	/* Expected output */
+	unsigned long period;
+	int fd;
+};
+
+/*
+ * Somehow first Fetch event with sample period = 0x10 causes 0
+ * samples. So start with large period and decrease it gradually.
+ */
+struct ibs_configs fetch_configs[] = {
+	{ .config =  0xffff, .period = 0xffff0, .fd = FD_SUCCESS },
+	{ .config =  0x1000, .period = 0x10000, .fd = FD_SUCCESS },
+	{ .config =    0xff, .period =   0xff0, .fd = FD_SUCCESS },
+	{ .config =     0x1, .period =    0x10, .fd = FD_SUCCESS },
+	{ .config =     0x0, .period =      -1, .fd = FD_ERROR   },
+	{ .config = 0x10000, .period =      -1, .fd = FD_ERROR   },
+};
+
+struct ibs_configs op_configs[] = {
+	{ .config =        0x0, .period =        -1, .fd = FD_ERROR   },
+	{ .config =        0x1, .period =        -1, .fd = FD_ERROR   },
+	{ .config =        0x8, .period =        -1, .fd = FD_ERROR   },
+	{ .config =        0x9, .period =      0x90, .fd = FD_SUCCESS },
+	{ .config =        0xf, .period =      0xf0, .fd = FD_SUCCESS },
+	{ .config =     0x1000, .period =   0x10000, .fd = FD_SUCCESS },
+	{ .config =     0xffff, .period =   0xffff0, .fd = FD_SUCCESS },
+	{ .config =    0x10000, .period =        -1, .fd = FD_ERROR   },
+	{ .config =   0x100000, .period =  0x100000, .fd = FD_SUCCESS },
+	{ .config =   0xf00000, .period =  0xf00000, .fd = FD_SUCCESS },
+	{ .config =   0xf0ffff, .period =  0xfffff0, .fd = FD_SUCCESS },
+	{ .config =  0x1f0ffff, .period = 0x1fffff0, .fd = FD_SUCCESS },
+	{ .config =  0x7f0ffff, .period = 0x7fffff0, .fd = FD_SUCCESS },
+	{ .config =  0x8f0ffff, .period =        -1, .fd = FD_ERROR   },
+	{ .config = 0x17f0ffff, .period =        -1, .fd = FD_ERROR   },
+};
+
+static int __ibs_config_test(int ibs_type, struct ibs_configs *config, int *nr_samples)
+{
+	struct perf_event_attr attr;
+	int fd, i;
+	void *rb;
+	int ret = 0;
+
+	if (ibs_type == IBS_FETCH)
+		fetch_prepare_attr(&attr, config->config, 0, 0);
+	else
+		op_prepare_attr(&attr, config->config, 0, 0);
+
+	/* CPU0, All processes */
+	fd = perf_event_open(&attr, -1, 0, -1, 0);
+	if (config->fd == FD_ERROR) {
+		if (fd != -1) {
+			close(fd);
+			return -1;
+		}
+		return 0;
+	}
+	if (fd <= -1)
+		return -1;
+
+	rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,
+		  MAP_SHARED, fd, 0);
+	if (rb == MAP_FAILED) {
+		pr_debug("mmap() failed. [%m]\n");
+		return -1;
+	}
+
+	ioctl(fd, PERF_EVENT_IOC_RESET, 0);
+	ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
+
+	i = 5;
+	while (i--) {
+		dummy_workload_1(1000000);
+
+		ret = rb_drain_samples(rb, config->period, nr_samples,
+				       period_equal);
+		if (ret)
+			break;
+	}
+
+	ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
+	munmap(rb, PERF_MMAP_TOTAL_SIZE);
+	close(fd);
+	return ret;
+}
+
+static int ibs_config_test(void)
+{
+	int nr_samples = 0;
+	unsigned long i;
+	int ret = 0;
+	int r;
+
+	pr_debug("\nIBS config tests:\n");
+	pr_debug("-----------------\n");
+
+	pr_debug("Fetch PMU tests:\n");
+	for (i = 0; i < ARRAY_SIZE(fetch_configs); i++) {
+		nr_samples = 0;
+		r = __ibs_config_test(IBS_FETCH, &(fetch_configs[i]), &nr_samples);
+
+		if (fetch_configs[i].fd == FD_ERROR) {
+			pr_debug("0x%-16lx: %-4s\n", fetch_configs[i].config,
+				 !r ? "Ok" : "Fail");
+		} else {
+			/*
+			 * Although nr_samples == 0 is reported as Fail here,
+			 * the failure status is not cascaded up because, we
+			 * can not decide whether test really failed or not
+			 * without actual samples.
+			 */
+			pr_debug("0x%-16lx: %-4s (nr samples: %d)\n", fetch_configs[i].config,
+				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
+		}
+
+		ret |= r;
+	}
+
+	pr_debug("Op PMU tests:\n");
+	for (i = 0; i < ARRAY_SIZE(op_configs); i++) {
+		nr_samples = 0;
+		r = __ibs_config_test(IBS_OP, &(op_configs[i]), &nr_samples);
+
+		if (op_configs[i].fd == FD_ERROR) {
+			pr_debug("0x%-16lx: %-4s\n", op_configs[i].config,
+				 !r ? "Ok" : "Fail");
+		} else {
+			/*
+			 * Although nr_samples == 0 is reported as Fail here,
+			 * the failure status is not cascaded up because, we
+			 * can not decide whether test really failed or not
+			 * without actual samples.
+			 */
+			pr_debug("0x%-16lx: %-4s (nr samples: %d)\n", op_configs[i].config,
+				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
+		}
+
+		ret |= r;
+	}
+
+	return ret;
+}
+
+struct ibs_period {
+	/* Input */
+	int freq;
+	unsigned long sample_freq;
+
+	/* Output */
+	int ret;
+	unsigned long period;
+};
+
+struct ibs_period fetch_period[] = {
+	{ .freq = 0, .sample_freq =         0, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 0, .sample_freq =         1, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 0, .sample_freq =       0xf, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 0, .sample_freq =      0x10, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 0, .sample_freq =      0x11, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 0, .sample_freq =      0x8f, .ret = FD_SUCCESS, .period =      0x80 },
+	{ .freq = 0, .sample_freq =      0x90, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 0, .sample_freq =      0x91, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 0, .sample_freq =     0x4d2, .ret = FD_SUCCESS, .period =     0x4d0 },
+	{ .freq = 0, .sample_freq =    0x1007, .ret = FD_SUCCESS, .period =    0x1000 },
+	{ .freq = 0, .sample_freq =    0xfff0, .ret = FD_SUCCESS, .period =    0xfff0 },
+	{ .freq = 0, .sample_freq =    0xffff, .ret = FD_SUCCESS, .period =    0xfff0 },
+	{ .freq = 0, .sample_freq =   0x10010, .ret = FD_SUCCESS, .period =   0x10010 },
+	{ .freq = 0, .sample_freq =  0x7fffff, .ret = FD_SUCCESS, .period =  0x7ffff0 },
+	{ .freq = 0, .sample_freq = 0xfffffff, .ret = FD_SUCCESS, .period = 0xffffff0 },
+	{ .freq = 1, .sample_freq =         0, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 1, .sample_freq =         1, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =       0xf, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =      0x10, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =      0x11, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =      0x8f, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =      0x90, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =      0x91, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =     0x4d2, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =    0x1007, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =    0xfff0, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =    0xffff, .ret = FD_SUCCESS, .period =      0x10 },
+	{ .freq = 1, .sample_freq =   0x10010, .ret = FD_SUCCESS, .period =      0x10 },
+	/* ret=FD_ERROR because freq > default perf_event_max_sample_rate (100000) */
+	{ .freq = 1, .sample_freq =  0x7fffff, .ret = FD_ERROR,   .period =        -1 },
+};
+
+struct ibs_period op_period[] = {
+	{ .freq = 0, .sample_freq =         0, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 0, .sample_freq =         1, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 0, .sample_freq =       0xf, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 0, .sample_freq =      0x10, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 0, .sample_freq =      0x11, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 0, .sample_freq =      0x8f, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 0, .sample_freq =      0x90, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 0, .sample_freq =      0x91, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 0, .sample_freq =     0x4d2, .ret = FD_SUCCESS, .period =     0x4d0 },
+	{ .freq = 0, .sample_freq =    0x1007, .ret = FD_SUCCESS, .period =    0x1000 },
+	{ .freq = 0, .sample_freq =    0xfff0, .ret = FD_SUCCESS, .period =    0xfff0 },
+	{ .freq = 0, .sample_freq =    0xffff, .ret = FD_SUCCESS, .period =    0xfff0 },
+	{ .freq = 0, .sample_freq =   0x10010, .ret = FD_SUCCESS, .period =   0x10010 },
+	{ .freq = 0, .sample_freq =  0x7fffff, .ret = FD_SUCCESS, .period =  0x7ffff0 },
+	{ .freq = 0, .sample_freq = 0xfffffff, .ret = FD_SUCCESS, .period = 0xffffff0 },
+	{ .freq = 1, .sample_freq =         0, .ret = FD_ERROR,   .period =        -1 },
+	{ .freq = 1, .sample_freq =         1, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =       0xf, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =      0x10, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =      0x11, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =      0x8f, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =      0x90, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =      0x91, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =     0x4d2, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =    0x1007, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =    0xfff0, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =    0xffff, .ret = FD_SUCCESS, .period =      0x90 },
+	{ .freq = 1, .sample_freq =   0x10010, .ret = FD_SUCCESS, .period =      0x90 },
+	/* ret=FD_ERROR because freq > default perf_event_max_sample_rate (100000) */
+	{ .freq = 1, .sample_freq =  0x7fffff, .ret = FD_ERROR,   .period =        -1 },
+};
+
+static int __ibs_period_constraint_test(int ibs_type, struct ibs_period *period,
+					int *nr_samples)
+{
+	struct perf_event_attr attr;
+	int ret = 0;
+	void *rb;
+	int fd;
+
+	if (period->freq && period->sample_freq > perf_event_max_sample_rate)
+		period->ret = FD_ERROR;
+
+	if (ibs_type == IBS_FETCH)
+		fetch_prepare_attr(&attr, 0, period->freq, period->sample_freq);
+	else
+		op_prepare_attr(&attr, 0, period->freq, period->sample_freq);
+
+	/* CPU0, All processes */
+	fd = perf_event_open(&attr, -1, 0, -1, 0);
+	if (period->ret == FD_ERROR) {
+		if (fd != -1) {
+			close(fd);
+			return -1;
+		}
+		return 0;
+	}
+	if (fd <= -1)
+		return -1;
+
+	rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,
+		  MAP_SHARED, fd, 0);
+	if (rb == MAP_FAILED) {
+		pr_debug("mmap() failed. [%m]\n");
+		close(fd);
+		return -1;
+	}
+
+	ioctl(fd, PERF_EVENT_IOC_RESET, 0);
+	ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
+
+	if (period->freq) {
+		dummy_workload_1(100000);
+		ret = rb_drain_samples(rb, period->period, nr_samples,
+				       period_higher);
+	} else {
+		dummy_workload_1(period->sample_freq * 10);
+		ret = rb_drain_samples(rb, period->period, nr_samples,
+				       period_equal);
+	}
+
+	ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
+	munmap(rb, PERF_MMAP_TOTAL_SIZE);
+	close(fd);
+	return ret;
+}
+
+static int ibs_period_constraint_test(void)
+{
+	unsigned long i;
+	int nr_samples;
+	int ret = 0;
+	int r;
+
+	pr_debug("\nIBS sample period constraint tests:\n");
+	pr_debug("-----------------------------------\n");
+
+	pr_debug("Fetch PMU test:\n");
+	for (i = 0; i < ARRAY_SIZE(fetch_period); i++) {
+		nr_samples = 0;
+		r = __ibs_period_constraint_test(IBS_FETCH, &fetch_period[i],
+						 &nr_samples);
+
+		if (fetch_period[i].ret == FD_ERROR) {
+			pr_debug("freq %d, sample_freq %9ld: %-4s\n",
+				 fetch_period[i].freq, fetch_period[i].sample_freq,
+				 !r ? "Ok" : "Fail");
+		} else {
+			/*
+			 * Although nr_samples == 0 is reported as Fail here,
+			 * the failure status is not cascaded up because, we
+			 * can not decide whether test really failed or not
+			 * without actual samples.
+			 */
+			pr_debug("freq %d, sample_freq %9ld: %-4s (nr samples: %d)\n",
+				 fetch_period[i].freq, fetch_period[i].sample_freq,
+				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
+		}
+		ret |= r;
+	}
+
+	pr_debug("Op PMU test:\n");
+	for (i = 0; i < ARRAY_SIZE(op_period); i++) {
+		nr_samples = 0;
+		r = __ibs_period_constraint_test(IBS_OP, &op_period[i],
+						 &nr_samples);
+
+		if (op_period[i].ret == FD_ERROR) {
+			pr_debug("freq %d, sample_freq %9ld: %-4s\n",
+				 op_period[i].freq, op_period[i].sample_freq,
+				 !r ? "Ok" : "Fail");
+		} else {
+			/*
+			 * Although nr_samples == 0 is reported as Fail here,
+			 * the failure status is not cascaded up because, we
+			 * can not decide whether test really failed or not
+			 * without actual samples.
+			 */
+			pr_debug("freq %d, sample_freq %9ld: %-4s (nr samples: %d)\n",
+				 op_period[i].freq, op_period[i].sample_freq,
+				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
+		}
+		ret |= r;
+	}
+
+	return ret;
+}
+
+struct ibs_ioctl {
+	/* Input */
+	int freq;
+	unsigned long period;
+
+	/* Expected output */
+	int ret;
+};
+
+struct ibs_ioctl fetch_ioctl[] = {
+	{ .freq = 0, .period =     0x0, .ret = FD_ERROR   },
+	{ .freq = 0, .period =     0x1, .ret = FD_ERROR   },
+	{ .freq = 0, .period =     0xf, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x10, .ret = FD_SUCCESS },
+	{ .freq = 0, .period =    0x11, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x1f, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x20, .ret = FD_SUCCESS },
+	{ .freq = 0, .period =    0x80, .ret = FD_SUCCESS },
+	{ .freq = 0, .period =    0x8f, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x90, .ret = FD_SUCCESS },
+	{ .freq = 0, .period =    0x91, .ret = FD_ERROR   },
+	{ .freq = 0, .period =   0x100, .ret = FD_SUCCESS },
+	{ .freq = 0, .period =  0xfff0, .ret = FD_SUCCESS },
+	{ .freq = 0, .period =  0xffff, .ret = FD_ERROR   },
+	{ .freq = 0, .period = 0x10000, .ret = FD_SUCCESS },
+	{ .freq = 0, .period = 0x1fff0, .ret = FD_SUCCESS },
+	{ .freq = 0, .period = 0x1fff5, .ret = FD_ERROR   },
+	{ .freq = 1, .period =     0x0, .ret = FD_ERROR   },
+	{ .freq = 1, .period =     0x1, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =     0xf, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x10, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x11, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x1f, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x20, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x80, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x8f, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x90, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x91, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =   0x100, .ret = FD_SUCCESS },
+};
+
+struct ibs_ioctl op_ioctl[] = {
+	{ .freq = 0, .period =     0x0, .ret = FD_ERROR   },
+	{ .freq = 0, .period =     0x1, .ret = FD_ERROR   },
+	{ .freq = 0, .period =     0xf, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x10, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x11, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x1f, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x20, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x80, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x8f, .ret = FD_ERROR   },
+	{ .freq = 0, .period =    0x90, .ret = FD_SUCCESS },
+	{ .freq = 0, .period =    0x91, .ret = FD_ERROR   },
+	{ .freq = 0, .period =   0x100, .ret = FD_SUCCESS },
+	{ .freq = 0, .period =  0xfff0, .ret = FD_SUCCESS },
+	{ .freq = 0, .period =  0xffff, .ret = FD_ERROR   },
+	{ .freq = 0, .period = 0x10000, .ret = FD_SUCCESS },
+	{ .freq = 0, .period = 0x1fff0, .ret = FD_SUCCESS },
+	{ .freq = 0, .period = 0x1fff5, .ret = FD_ERROR   },
+	{ .freq = 1, .period =     0x0, .ret = FD_ERROR   },
+	{ .freq = 1, .period =     0x1, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =     0xf, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x10, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x11, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x1f, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x20, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x80, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x8f, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x90, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =    0x91, .ret = FD_SUCCESS },
+	{ .freq = 1, .period =   0x100, .ret = FD_SUCCESS },
+};
+
+static int __ibs_ioctl_test(int ibs_type, struct ibs_ioctl *ibs_ioctl)
+{
+	struct perf_event_attr attr;
+	int ret = 0;
+	int fd;
+	int r;
+
+	if (ibs_type == IBS_FETCH)
+		fetch_prepare_attr(&attr, 0, ibs_ioctl->freq, 1000);
+	else
+		op_prepare_attr(&attr, 0, ibs_ioctl->freq, 1000);
+
+	/* CPU0, All processes */
+	fd = perf_event_open(&attr, -1, 0, -1, 0);
+	if (fd <= -1) {
+		pr_debug("event_open() Failed\n");
+		return -1;
+	}
+
+	r = ioctl(fd, PERF_EVENT_IOC_PERIOD, &ibs_ioctl->period);
+	if ((ibs_ioctl->ret == FD_SUCCESS && r <= -1) ||
+	    (ibs_ioctl->ret == FD_ERROR && r >= 0)) {
+		ret = -1;
+	}
+
+	close(fd);
+	return ret;
+}
+
+static int ibs_ioctl_test(void)
+{
+	unsigned long i;
+	int ret = 0;
+	int r;
+
+	pr_debug("\nIBS ioctl() tests:\n");
+	pr_debug("------------------\n");
+
+	pr_debug("Fetch PMU tests\n");
+	for (i = 0; i < ARRAY_SIZE(fetch_ioctl); i++) {
+		r = __ibs_ioctl_test(IBS_FETCH, &fetch_ioctl[i]);
+
+		pr_debug("ioctl(%s = 0x%-7lx): %s\n",
+			 fetch_ioctl[i].freq ? "freq  " : "period",
+			 fetch_ioctl[i].period, r ? "Fail" : "Ok");
+		ret |= r;
+	}
+
+	pr_debug("Op PMU tests\n");
+	for (i = 0; i < ARRAY_SIZE(op_ioctl); i++) {
+		r = __ibs_ioctl_test(IBS_OP, &op_ioctl[i]);
+
+		pr_debug("ioctl(%s = 0x%-7lx): %s\n",
+			 op_ioctl[i].freq ? "freq  " : "period",
+			 op_ioctl[i].period, r ? "Fail" : "Ok");
+		ret |= r;
+	}
+
+	return ret;
+}
+
+static int ibs_freq_neg_test(void)
+{
+	struct perf_event_attr attr;
+	int fd;
+
+	pr_debug("\nIBS freq (negative) tests:\n");
+	pr_debug("--------------------------\n");
+
+	/*
+	 * Assuming perf_event_max_sample_rate <= 100000,
+	 * config: 0x300D40 ==> MaxCnt: 200000
+	 */
+	op_prepare_attr(&attr, 0x300D40, 1, 0);
+
+	/* CPU0, All processes */
+	fd = perf_event_open(&attr, -1, 0, -1, 0);
+	if (fd != -1) {
+		pr_debug("freq 1, sample_freq 200000: Fail\n");
+		close(fd);
+		return -1;
+	}
+
+	pr_debug("freq 1, sample_freq 200000: Ok\n");
+
+	return 0;
+}
+
+struct ibs_l3missonly {
+	/* Input */
+	int freq;
+	unsigned long sample_freq;
+
+	/* Expected output */
+	int ret;
+	unsigned long min_period;
+};
+
+struct ibs_l3missonly fetch_l3missonly = {
+	.freq = 1,
+	.sample_freq = 10000,
+	.ret = FD_SUCCESS,
+	.min_period = 0x10,
+};
+
+struct ibs_l3missonly op_l3missonly = {
+	.freq = 1,
+	.sample_freq = 10000,
+	.ret = FD_SUCCESS,
+	.min_period = 0x90,
+};
+
+static int __ibs_l3missonly_test(char *perf, int ibs_type, int *nr_samples,
+				 struct ibs_l3missonly *l3missonly)
+{
+	struct perf_event_attr attr;
+	int ret = 0;
+	void *rb;
+	int fd;
+
+	if (l3missonly->sample_freq > perf_event_max_sample_rate)
+		l3missonly->ret = FD_ERROR;
+
+	if (ibs_type == IBS_FETCH) {
+		fetch_prepare_attr(&attr, 0x800000000000000UL, l3missonly->freq,
+				   l3missonly->sample_freq);
+	} else {
+		op_prepare_attr(&attr, 0x10000, l3missonly->freq,
+				l3missonly->sample_freq);
+	}
+
+	/* CPU0, All processes */
+	fd = perf_event_open(&attr, -1, 0, -1, 0);
+	if (l3missonly->ret == FD_ERROR) {
+		if (fd != -1) {
+			close(fd);
+			return -1;
+		}
+		return 0;
+	}
+	if (fd == -1) {
+		pr_debug("perf_event_open() failed. [%m]\n");
+		return -1;
+	}
+
+	rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,
+		  MAP_SHARED, fd, 0);
+	if (rb == MAP_FAILED) {
+		pr_debug("mmap() failed. [%m]\n");
+		close(fd);
+		return -1;
+	}
+
+	ioctl(fd, PERF_EVENT_IOC_RESET, 0);
+	ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
+
+	dummy_workload_2(perf);
+
+	ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
+
+	ret = rb_drain_samples(rb, l3missonly->min_period, nr_samples, period_higher);
+
+	munmap(rb, PERF_MMAP_TOTAL_SIZE);
+	close(fd);
+	return ret;
+}
+
+static int ibs_l3missonly_test(char *perf)
+{
+	int nr_samples = 0;
+	int ret = 0;
+	int r = 0;
+
+	pr_debug("\nIBS L3MissOnly test: (takes a while)\n");
+	pr_debug("--------------------\n");
+
+	if (perf_pmu__has_format(fetch_pmu, "l3missonly")) {
+		nr_samples = 0;
+		r = __ibs_l3missonly_test(perf, IBS_FETCH, &nr_samples, &fetch_l3missonly);
+		if (fetch_l3missonly.ret == FD_ERROR) {
+			pr_debug("Fetch L3MissOnly: %-4s\n", !r ? "Ok" : "Fail");
+		} else {
+			/*
+			 * Although nr_samples == 0 is reported as Fail here,
+			 * the failure status is not cascaded up because, we
+			 * can not decide whether test really failed or not
+			 * without actual samples.
+			 */
+			pr_debug("Fetch L3MissOnly: %-4s (nr_samples: %d)\n",
+				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
+		}
+		ret |= r;
+	}
+
+	if (perf_pmu__has_format(op_pmu, "l3missonly")) {
+		nr_samples = 0;
+		r = __ibs_l3missonly_test(perf, IBS_OP, &nr_samples, &op_l3missonly);
+		if (op_l3missonly.ret == FD_ERROR) {
+			pr_debug("Op L3MissOnly:    %-4s\n", !r ? "Ok" : "Fail");
+		} else {
+			/*
+			 * Although nr_samples == 0 is reported as Fail here,
+			 * the failure status is not cascaded up because, we
+			 * can not decide whether test really failed or not
+			 * without actual samples.
+			 */
+			pr_debug("Op L3MissOnly:    %-4s (nr_samples: %d)\n",
+				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
+		}
+		ret |= r;
+	}
+
+	return ret;
+}
+
+static unsigned int get_perf_event_max_sample_rate(void)
+{
+	unsigned int max_sample_rate = 100000;
+	FILE *fp;
+	int ret;
+
+	fp = fopen("/proc/sys/kernel/perf_event_max_sample_rate", "r");
+	if (!fp) {
+		pr_debug("Can't open perf_event_max_sample_rate. Asssuming %d\n",
+			 max_sample_rate);
+		goto out;
+	}
+
+	ret = fscanf(fp, "%d", &max_sample_rate);
+	if (ret == EOF) {
+		pr_debug("Can't read perf_event_max_sample_rate. Assuming 100000\n");
+		max_sample_rate = 100000;
+	}
+	fclose(fp);
+
+out:
+	return max_sample_rate;
+}
+
+int test__amd_ibs_period(struct test_suite *test __maybe_unused,
+			 int subtest __maybe_unused)
+{
+	char perf[PATH_MAX] = {'\0'};
+	int ret = TEST_OK;
+
+	/*
+	 * Reading perf_event_max_sample_rate only once _might_ cause some
+	 * of the test to fail if kernel changes it after reading it here.
+	 */
+	perf_event_max_sample_rate = get_perf_event_max_sample_rate();
+	fetch_pmu = perf_pmus__find("ibs_fetch");
+	op_pmu = perf_pmus__find("ibs_op");
+
+	if (!x86__is_amd_cpu() || !fetch_pmu || !op_pmu)
+		return TEST_SKIP;
+
+	perf_exe(perf, sizeof(perf));
+
+	if (sched_affine(0))
+		return TEST_FAIL;
+
+	/*
+	 * Perf event can be opened in two modes:
+	 * 1 Freq mode
+	 *   perf_event_attr->freq = 1, ->sample_freq = <frequency>
+	 * 2 Sample period mode
+	 *   perf_event_attr->freq = 0, ->sample_period = <period>
+	 *
+	 * Instead of using above interface, IBS event in 'sample period mode'
+	 * can also be opened by passing <period> value directly in a MaxCnt
+	 * bitfields of perf_event_attr->config. Test this IBS specific special
+	 * interface.
+	 */
+	if (ibs_config_test())
+		ret = TEST_FAIL;
+
+	/*
+	 * IBS Fetch and Op PMUs have HW constraints on minimum sample period.
+	 * Also, sample period value must be in multiple of 0x10. Test that IBS
+	 * driver honors HW constraints for various possible values in Freq as
+	 * well as Sample Period mode IBS events.
+	 */
+	if (ibs_period_constraint_test())
+		ret = TEST_FAIL;
+
+	/*
+	 * Test ioctl() with various sample period values for IBS event.
+	 */
+	if (ibs_ioctl_test())
+		ret = TEST_FAIL;
+
+	/*
+	 * Test that opening of freq mode IBS event fails when the freq value
+	 * is passed through ->config, not explicitly in ->sample_freq. Also
+	 * use high freq value (beyond perf_event_max_sample_rate) to test IBS
+	 * driver do not bypass perf_event_max_sample_rate checks.
+	 */
+	if (ibs_freq_neg_test())
+		ret = TEST_FAIL;
+
+	/*
+	 * L3MissOnly is a post-processing filter, i.e. IBS HW checks for L3
+	 * Miss at the completion of the tagged uOp. The sample is discarded
+	 * if the tagged uOp did not cause L3Miss. Also, IBS HW internally
+	 * resets CurCnt to a small pseudo-random value and resumes counting.
+	 * A new uOp is tagged once CurCnt reaches to MaxCnt. But the process
+	 * repeats until the tagged uOp causes an L3 Miss.
+	 *
+	 * With the freq mode event, the next sample period is calculated by
+	 * generic kernel on every sample to achieve desired freq of samples.
+	 *
+	 * Since the number of times HW internally reset CurCnt and the pseudo-
+	 * random value of CurCnt for all those occurrences are not known to SW,
+	 * the sample period adjustment by kernel goes for a toes for freq mode
+	 * IBS events. Kernel will set very small period for the next sample if
+	 * the window between current sample and prev sample is too high due to
+	 * multiple samples being discarded internally by IBS HW.
+	 *
+	 * Test that IBS sample period constraints are honored when L3MissOnly
+	 * is ON.
+	 */
+	if (ibs_l3missonly_test(perf))
+		ret = TEST_FAIL;
+
+	return ret;
+}
diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c
index a216a5d172ed..bfee2432515b 100644
--- a/tools/perf/arch/x86/tests/arch-tests.c
+++ b/tools/perf/arch/x86/tests/arch-tests.c
@@ -25,6 +25,7 @@ DEFINE_SUITE("x86 bp modify", bp_modify);
 #endif
 DEFINE_SUITE("x86 Sample parsing", x86_sample_parsing);
 DEFINE_SUITE("AMD IBS via core pmu", amd_ibs_via_core_pmu);
+DEFINE_SUITE_EXCLUSIVE("AMD IBS sample period", amd_ibs_period);
 static struct test_case hybrid_tests[] = {
 	TEST_CASE_REASON("x86 hybrid event parsing", hybrid, "not hybrid"),
 	{ .name = NULL, }
@@ -50,6 +51,7 @@ struct test_suite *arch_tests[] = {
 #endif
 	&suite__x86_sample_parsing,
 	&suite__amd_ibs_via_core_pmu,
+	&suite__amd_ibs_period,
 	&suite__hybrid,
 	NULL,
 };
-- 
2.43.0
Re: [PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Arnaldo Carvalho de Melo 9 months, 2 weeks ago
On Tue, Apr 29, 2025 at 03:59:38AM +0000, Ravi Bangoria wrote:
> IBS Fetch and IBS Op PMUs has various constraints on supported sample
> periods. Add perf unit tests to test those.
> 
> Running it in parallel with other tests causes intermittent failures.
> Mark it exclusive to force it to run sequentially. Sample output on a
> Zen5 machine:

I've applied the series and will test it now, but found some problems
when building in some non-glibc systems, namely the use of PAGE_SIZE,
that is used in libc headers, even in glibc, its just that in glibc we
happen not to include that header where PAGE_SIZE gets redefined:

⬢ [acme@toolbx perf-tools-next]$ grep PAGE_SIZE /usr/include/sys/*.h
/usr/include/sys/user.h:#define PAGE_SIZE		(1UL << PAGE_SHIFT)
/usr/include/sys/user.h:#define PAGE_MASK		(~(PAGE_SIZE-1))
/usr/include/sys/user.h:#define NBPG			PAGE_SIZE
⬢ [acme@toolbx perf-tools-next]$

So I folded the following patch, see if it is acceptable and please ack.

Thanks for respining it!

- Arnaldo

diff --git a/tools/perf/arch/x86/tests/amd-ibs-period.c b/tools/perf/arch/x86/tests/amd-ibs-period.c
index 0cf3656e4b9bdacf..946b0a377554fb81 100644
--- a/tools/perf/arch/x86/tests/amd-ibs-period.c
+++ b/tools/perf/arch/x86/tests/amd-ibs-period.c
@@ -17,13 +17,13 @@
 #include "strbuf.h"
 #include "../util/env.h"
 
-#define PAGE_SIZE               sysconf(_SC_PAGESIZE)
+static int page_size;
 
 #define PERF_MMAP_DATA_PAGES    32L
-#define PERF_MMAP_DATA_SIZE     (PERF_MMAP_DATA_PAGES * PAGE_SIZE)
+#define PERF_MMAP_DATA_SIZE     (PERF_MMAP_DATA_PAGES * page_size)
 #define PERF_MMAP_DATA_MASK     (PERF_MMAP_DATA_SIZE - 1)
 #define PERF_MMAP_TOTAL_PAGES   (PERF_MMAP_DATA_PAGES + 1)
-#define PERF_MMAP_TOTAL_SIZE    (PERF_MMAP_TOTAL_PAGES * PAGE_SIZE)
+#define PERF_MMAP_TOTAL_SIZE    (PERF_MMAP_TOTAL_PAGES * page_size)
 
 #define rmb()                   asm volatile("lfence":::"memory")
 
@@ -59,15 +59,15 @@ static int dummy_workload_1(unsigned long count)
 		0xcc, /* int 3 */
 	};
 
-	p = zalloc(2 * PAGE_SIZE);
+	p = zalloc(2 * page_size);
 	if (!p) {
 		printf("malloc() failed. %m");
 		return 1;
 	}
 
-	func = (void *)((unsigned long)(p + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
+	func = (void *)((unsigned long)(p + page_size - 1) & ~(page_size - 1));
 
-	ret = mprotect(func, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
+	ret = mprotect(func, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
 	if (ret) {
 		printf("mprotect() failed. %m");
 		goto out;
@@ -150,7 +150,7 @@ static int rb_read(struct perf_event_mmap_page *rb, void *dest, size_t size)
 	unsigned long data_tail, data_head;
 
 	/* Casting to (void *) is needed. */
-	base = (void *)rb + PAGE_SIZE;
+	base = (void *)rb + page_size;
 
 	data_head = rb->data_head;
 	rmb();
@@ -918,6 +918,8 @@ int test__amd_ibs_period(struct test_suite *test __maybe_unused,
 	char perf[PATH_MAX] = {'\0'};
 	int ret = TEST_OK;
 
+	page_size = sysconf(_SC_PAGESIZE);
+
 	/*
 	 * Reading perf_event_max_sample_rate only once _might_ cause some
 	 * of the test to fail if kernel changes it after reading it here.
 
> Without kernel fixes:
> 
>   $ sudo ./perf test -vv 112
>   112: AMD IBS sample period:
>   --- start ---
>   test child forked, pid 8774
>   Using CPUID AuthenticAMD-26-2-1
> 
>   IBS config tests:
>   -----------------
>   Fetch PMU tests:
>   0xffff            : Ok   (nr samples: 1078)
>   0x1000            : Ok   (nr samples: 17030)
>   0xff              : Ok   (nr samples: 41068)
>   0x1               : Ok   (nr samples: 40543)
>   0x0               : Ok
>   0x10000           : Ok
>   Op PMU tests:
>   0x0               : Ok
>   0x1               : Fail
>   0x8               : Fail
>   0x9               : Ok   (nr samples: 40543)
>   0xf               : Ok   (nr samples: 40543)
>   0x1000            : Ok   (nr samples: 18736)
>   0xffff            : Ok   (nr samples: 1168)
>   0x10000           : Ok
>   0x100000          : Fail (nr samples: 14)
>   0xf00000          : Fail (nr samples: 1)
>   0xf0ffff          : Fail (nr samples: 1)
>   0x1f0ffff         : Fail (nr samples: 1)
>   0x7f0ffff         : Fail (nr samples: 0)
>   0x8f0ffff         : Ok
>   0x17f0ffff        : Ok
> 
>   IBS sample period constraint tests:
>   -----------------------------------
>   Fetch PMU test:
>   freq 0, sample_freq         0: Ok
>   freq 0, sample_freq         1: Fail
>   freq 0, sample_freq        15: Fail
>   freq 0, sample_freq        16: Ok   (nr samples: 1604)
>   freq 0, sample_freq        17: Ok   (nr samples: 1604)
>   freq 0, sample_freq       143: Ok   (nr samples: 1604)
>   freq 0, sample_freq       144: Ok   (nr samples: 1604)
>   freq 0, sample_freq       145: Ok   (nr samples: 1604)
>   freq 0, sample_freq      1234: Ok   (nr samples: 1566)
>   freq 0, sample_freq      4103: Ok   (nr samples: 1119)
>   freq 0, sample_freq     65520: Ok   (nr samples: 2264)
>   freq 0, sample_freq     65535: Ok   (nr samples: 2263)
>   freq 0, sample_freq     65552: Ok   (nr samples: 1166)
>   freq 0, sample_freq   8388607: Ok   (nr samples: 268)
>   freq 0, sample_freq 268435455: Ok   (nr samples: 8)
>   freq 1, sample_freq         0: Ok
>   freq 1, sample_freq         1: Ok   (nr samples: 4)
>   freq 1, sample_freq        15: Ok   (nr samples: 4)
>   freq 1, sample_freq        16: Ok   (nr samples: 4)
>   freq 1, sample_freq        17: Ok   (nr samples: 4)
>   freq 1, sample_freq       143: Ok   (nr samples: 5)
>   freq 1, sample_freq       144: Ok   (nr samples: 5)
>   freq 1, sample_freq       145: Ok   (nr samples: 5)
>   freq 1, sample_freq      1234: Ok   (nr samples: 7)
>   freq 1, sample_freq      4103: Ok   (nr samples: 35)
>   freq 1, sample_freq     65520: Ok   (nr samples: 642)
>   freq 1, sample_freq     65535: Ok   (nr samples: 636)
>   freq 1, sample_freq     65552: Ok   (nr samples: 651)
>   freq 1, sample_freq   8388607: Ok
>   Op PMU test:
>   freq 0, sample_freq         0: Ok
>   freq 0, sample_freq         1: Fail
>   freq 0, sample_freq        15: Fail
>   freq 0, sample_freq        16: Fail
>   freq 0, sample_freq        17: Fail
>   freq 0, sample_freq       143: Fail
>   freq 0, sample_freq       144: Ok   (nr samples: 1604)
>   freq 0, sample_freq       145: Ok   (nr samples: 1604)
>   freq 0, sample_freq      1234: Ok   (nr samples: 1604)
>   freq 0, sample_freq      4103: Ok   (nr samples: 1604)
>   freq 0, sample_freq     65520: Ok   (nr samples: 2227)
>   freq 0, sample_freq     65535: Ok   (nr samples: 2296)
>   freq 0, sample_freq     65552: Ok   (nr samples: 2213)
>   freq 0, sample_freq   8388607: Ok   (nr samples: 250)
>   freq 0, sample_freq 268435455: Ok   (nr samples: 8)
>   freq 1, sample_freq         0: Ok
>   freq 1, sample_freq         1: Fail (nr samples: 4)
>   freq 1, sample_freq        15: Fail (nr samples: 4)
>   freq 1, sample_freq        16: Fail (nr samples: 4)
>   freq 1, sample_freq        17: Fail (nr samples: 4)
>   freq 1, sample_freq       143: Fail (nr samples: 5)
>   freq 1, sample_freq       144: Fail (nr samples: 5)
>   freq 1, sample_freq       145: Fail (nr samples: 5)
>   freq 1, sample_freq      1234: Fail (nr samples: 8)
>   freq 1, sample_freq      4103: Fail (nr samples: 33)
>   freq 1, sample_freq     65520: Fail (nr samples: 546)
>   freq 1, sample_freq     65535: Fail (nr samples: 544)
>   freq 1, sample_freq     65552: Fail (nr samples: 555)
>   freq 1, sample_freq   8388607: Ok
> 
>   IBS ioctl() tests:
>   ------------------
>   Fetch PMU tests
>   ioctl(period = 0x0      ): Ok
>   ioctl(period = 0x1      ): Fail
>   ioctl(period = 0xf      ): Fail
>   ioctl(period = 0x10     ): Ok
>   ioctl(period = 0x11     ): Fail
>   ioctl(period = 0x1f     ): Fail
>   ioctl(period = 0x20     ): Ok
>   ioctl(period = 0x80     ): Ok
>   ioctl(period = 0x8f     ): Fail
>   ioctl(period = 0x90     ): Ok
>   ioctl(period = 0x91     ): Fail
>   ioctl(period = 0x100    ): Ok
>   ioctl(period = 0xfff0   ): Ok
>   ioctl(period = 0xffff   ): Fail
>   ioctl(period = 0x10000  ): Ok
>   ioctl(period = 0x1fff0  ): Ok
>   ioctl(period = 0x1fff5  ): Fail
>   ioctl(freq   = 0x0      ): Ok
>   ioctl(freq   = 0x1      ): Ok
>   ioctl(freq   = 0xf      ): Ok
>   ioctl(freq   = 0x10     ): Ok
>   ioctl(freq   = 0x11     ): Ok
>   ioctl(freq   = 0x1f     ): Ok
>   ioctl(freq   = 0x20     ): Ok
>   ioctl(freq   = 0x80     ): Ok
>   ioctl(freq   = 0x8f     ): Ok
>   ioctl(freq   = 0x90     ): Ok
>   ioctl(freq   = 0x91     ): Ok
>   ioctl(freq   = 0x100    ): Ok
>   Op PMU tests
>   ioctl(period = 0x0      ): Ok
>   ioctl(period = 0x1      ): Fail
>   ioctl(period = 0xf      ): Fail
>   ioctl(period = 0x10     ): Fail
>   ioctl(period = 0x11     ): Fail
>   ioctl(period = 0x1f     ): Fail
>   ioctl(period = 0x20     ): Fail
>   ioctl(period = 0x80     ): Fail
>   ioctl(period = 0x8f     ): Fail
>   ioctl(period = 0x90     ): Ok
>   ioctl(period = 0x91     ): Fail
>   ioctl(period = 0x100    ): Ok
>   ioctl(period = 0xfff0   ): Ok
>   ioctl(period = 0xffff   ): Fail
>   ioctl(period = 0x10000  ): Ok
>   ioctl(period = 0x1fff0  ): Ok
>   ioctl(period = 0x1fff5  ): Fail
>   ioctl(freq   = 0x0      ): Ok
>   ioctl(freq   = 0x1      ): Ok
>   ioctl(freq   = 0xf      ): Ok
>   ioctl(freq   = 0x10     ): Ok
>   ioctl(freq   = 0x11     ): Ok
>   ioctl(freq   = 0x1f     ): Ok
>   ioctl(freq   = 0x20     ): Ok
>   ioctl(freq   = 0x80     ): Ok
>   ioctl(freq   = 0x8f     ): Ok
>   ioctl(freq   = 0x90     ): Ok
>   ioctl(freq   = 0x91     ): Ok
>   ioctl(freq   = 0x100    ): Ok
> 
>   IBS freq (negative) tests:
>   --------------------------
>   freq 1, sample_freq 200000: Fail
> 
>   IBS L3MissOnly test: (takes a while)
>   --------------------
>   Fetch L3MissOnly: Fail (nr_samples: 1213)
>   Op L3MissOnly:    Ok   (nr_samples: 1193)
>   ---- end(-1) ----
>   112: AMD IBS sample period                                           : FAILED!
> 
> With kernel fixes:
> 
>   $ sudo ./perf test -vv 112
>   112: AMD IBS sample period:
>   --- start ---
>   test child forked, pid 6939
>   Using CPUID AuthenticAMD-26-2-1
> 
>   IBS config tests:
>   -----------------
>   Fetch PMU tests:
>   0xffff            : Ok   (nr samples: 969)
>   0x1000            : Ok   (nr samples: 15540)
>   0xff              : Ok   (nr samples: 40555)
>   0x1               : Ok   (nr samples: 40543)
>   0x0               : Ok
>   0x10000           : Ok
>   Op PMU tests:
>   0x0               : Ok
>   0x1               : Ok
>   0x8               : Ok
>   0x9               : Ok   (nr samples: 40543)
>   0xf               : Ok   (nr samples: 40543)
>   0x1000            : Ok   (nr samples: 19156)
>   0xffff            : Ok   (nr samples: 1169)
>   0x10000           : Ok
>   0x100000          : Ok   (nr samples: 1151)
>   0xf00000          : Ok   (nr samples: 76)
>   0xf0ffff          : Ok   (nr samples: 73)
>   0x1f0ffff         : Ok   (nr samples: 33)
>   0x7f0ffff         : Ok   (nr samples: 10)
>   0x8f0ffff         : Ok
>   0x17f0ffff        : Ok
> 
>   IBS sample period constraint tests:
>   -----------------------------------
>   Fetch PMU test:
>   freq 0, sample_freq         0: Ok
>   freq 0, sample_freq         1: Ok
>   freq 0, sample_freq        15: Ok
>   freq 0, sample_freq        16: Ok   (nr samples: 1203)
>   freq 0, sample_freq        17: Ok   (nr samples: 1604)
>   freq 0, sample_freq       143: Ok   (nr samples: 1604)
>   freq 0, sample_freq       144: Ok   (nr samples: 1604)
>   freq 0, sample_freq       145: Ok   (nr samples: 1604)
>   freq 0, sample_freq      1234: Ok   (nr samples: 1604)
>   freq 0, sample_freq      4103: Ok   (nr samples: 1343)
>   freq 0, sample_freq     65520: Ok   (nr samples: 2254)
>   freq 0, sample_freq     65535: Ok   (nr samples: 2136)
>   freq 0, sample_freq     65552: Ok   (nr samples: 1158)
>   freq 0, sample_freq   8388607: Ok   (nr samples: 257)
>   freq 0, sample_freq 268435455: Ok   (nr samples: 8)
>   freq 1, sample_freq         0: Ok
>   freq 1, sample_freq         1: Ok   (nr samples: 4)
>   freq 1, sample_freq        15: Ok   (nr samples: 4)
>   freq 1, sample_freq        16: Ok   (nr samples: 4)
>   freq 1, sample_freq        17: Ok   (nr samples: 4)
>   freq 1, sample_freq       143: Ok   (nr samples: 5)
>   freq 1, sample_freq       144: Ok   (nr samples: 5)
>   freq 1, sample_freq       145: Ok   (nr samples: 5)
>   freq 1, sample_freq      1234: Ok   (nr samples: 8)
>   freq 1, sample_freq      4103: Ok   (nr samples: 34)
>   freq 1, sample_freq     65520: Ok   (nr samples: 458)
>   freq 1, sample_freq     65535: Ok   (nr samples: 628)
>   freq 1, sample_freq     65552: Ok   (nr samples: 396)
>   freq 1, sample_freq   8388607: Ok
>   Op PMU test:
>   freq 0, sample_freq         0: Ok
>   freq 0, sample_freq         1: Ok
>   freq 0, sample_freq        15: Ok
>   freq 0, sample_freq        16: Ok
>   freq 0, sample_freq        17: Ok
>   freq 0, sample_freq       143: Ok
>   freq 0, sample_freq       144: Ok   (nr samples: 1604)
>   freq 0, sample_freq       145: Ok   (nr samples: 1604)
>   freq 0, sample_freq      1234: Ok   (nr samples: 1604)
>   freq 0, sample_freq      4103: Ok   (nr samples: 1604)
>   freq 0, sample_freq     65520: Ok   (nr samples: 2250)
>   freq 0, sample_freq     65535: Ok   (nr samples: 2158)
>   freq 0, sample_freq     65552: Ok   (nr samples: 2296)
>   freq 0, sample_freq   8388607: Ok   (nr samples: 243)
>   freq 0, sample_freq 268435455: Ok   (nr samples: 6)
>   freq 1, sample_freq         0: Ok
>   freq 1, sample_freq         1: Ok   (nr samples: 4)
>   freq 1, sample_freq        15: Ok   (nr samples: 4)
>   freq 1, sample_freq        16: Ok   (nr samples: 4)
>   freq 1, sample_freq        17: Ok   (nr samples: 4)
>   freq 1, sample_freq       143: Ok   (nr samples: 4)
>   freq 1, sample_freq       144: Ok   (nr samples: 5)
>   freq 1, sample_freq       145: Ok   (nr samples: 4)
>   freq 1, sample_freq      1234: Ok   (nr samples: 6)
>   freq 1, sample_freq      4103: Ok   (nr samples: 27)
>   freq 1, sample_freq     65520: Ok   (nr samples: 542)
>   freq 1, sample_freq     65535: Ok   (nr samples: 550)
>   freq 1, sample_freq     65552: Ok   (nr samples: 552)
>   freq 1, sample_freq   8388607: Ok
> 
>   IBS ioctl() tests:
>   ------------------
>   Fetch PMU tests
>   ioctl(period = 0x0      ): Ok
>   ioctl(period = 0x1      ): Ok
>   ioctl(period = 0xf      ): Ok
>   ioctl(period = 0x10     ): Ok
>   ioctl(period = 0x11     ): Ok
>   ioctl(period = 0x1f     ): Ok
>   ioctl(period = 0x20     ): Ok
>   ioctl(period = 0x80     ): Ok
>   ioctl(period = 0x8f     ): Ok
>   ioctl(period = 0x90     ): Ok
>   ioctl(period = 0x91     ): Ok
>   ioctl(period = 0x100    ): Ok
>   ioctl(period = 0xfff0   ): Ok
>   ioctl(period = 0xffff   ): Ok
>   ioctl(period = 0x10000  ): Ok
>   ioctl(period = 0x1fff0  ): Ok
>   ioctl(period = 0x1fff5  ): Ok
>   ioctl(freq   = 0x0      ): Ok
>   ioctl(freq   = 0x1      ): Ok
>   ioctl(freq   = 0xf      ): Ok
>   ioctl(freq   = 0x10     ): Ok
>   ioctl(freq   = 0x11     ): Ok
>   ioctl(freq   = 0x1f     ): Ok
>   ioctl(freq   = 0x20     ): Ok
>   ioctl(freq   = 0x80     ): Ok
>   ioctl(freq   = 0x8f     ): Ok
>   ioctl(freq   = 0x90     ): Ok
>   ioctl(freq   = 0x91     ): Ok
>   ioctl(freq   = 0x100    ): Ok
>   Op PMU tests
>   ioctl(period = 0x0      ): Ok
>   ioctl(period = 0x1      ): Ok
>   ioctl(period = 0xf      ): Ok
>   ioctl(period = 0x10     ): Ok
>   ioctl(period = 0x11     ): Ok
>   ioctl(period = 0x1f     ): Ok
>   ioctl(period = 0x20     ): Ok
>   ioctl(period = 0x80     ): Ok
>   ioctl(period = 0x8f     ): Ok
>   ioctl(period = 0x90     ): Ok
>   ioctl(period = 0x91     ): Ok
>   ioctl(period = 0x100    ): Ok
>   ioctl(period = 0xfff0   ): Ok
>   ioctl(period = 0xffff   ): Ok
>   ioctl(period = 0x10000  ): Ok
>   ioctl(period = 0x1fff0  ): Ok
>   ioctl(period = 0x1fff5  ): Ok
>   ioctl(freq   = 0x0      ): Ok
>   ioctl(freq   = 0x1      ): Ok
>   ioctl(freq   = 0xf      ): Ok
>   ioctl(freq   = 0x10     ): Ok
>   ioctl(freq   = 0x11     ): Ok
>   ioctl(freq   = 0x1f     ): Ok
>   ioctl(freq   = 0x20     ): Ok
>   ioctl(freq   = 0x80     ): Ok
>   ioctl(freq   = 0x8f     ): Ok
>   ioctl(freq   = 0x90     ): Ok
>   ioctl(freq   = 0x91     ): Ok
>   ioctl(freq   = 0x100    ): Ok
> 
>   IBS freq (negative) tests:
>   --------------------------
>   freq 1, sample_freq 200000: Ok
> 
>   IBS L3MissOnly test: (takes a while)
>   --------------------
>   Fetch L3MissOnly: Ok   (nr_samples: 1301)
>   Op L3MissOnly:    Ok   (nr_samples: 1590)
>   ---- end(0) ----
>   112: AMD IBS sample period                                           : Ok
> 
> Signed-off-by: Ravi Bangoria <ravi.bangoria@amd.com>
> ---
>  tools/perf/arch/x86/include/arch-tests.h   |    1 +
>  tools/perf/arch/x86/tests/Build            |    1 +
>  tools/perf/arch/x86/tests/amd-ibs-period.c | 1001 ++++++++++++++++++++
>  tools/perf/arch/x86/tests/arch-tests.c     |    2 +
>  4 files changed, 1005 insertions(+)
>  create mode 100644 tools/perf/arch/x86/tests/amd-ibs-period.c
> 
> diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h
> index c0421a26b875..4fd425157d7d 100644
> --- a/tools/perf/arch/x86/include/arch-tests.h
> +++ b/tools/perf/arch/x86/include/arch-tests.h
> @@ -14,6 +14,7 @@ int test__intel_pt_hybrid_compat(struct test_suite *test, int subtest);
>  int test__bp_modify(struct test_suite *test, int subtest);
>  int test__x86_sample_parsing(struct test_suite *test, int subtest);
>  int test__amd_ibs_via_core_pmu(struct test_suite *test, int subtest);
> +int test__amd_ibs_period(struct test_suite *test, int subtest);
>  int test__hybrid(struct test_suite *test, int subtest);
>  
>  extern struct test_suite *arch_tests[];
> diff --git a/tools/perf/arch/x86/tests/Build b/tools/perf/arch/x86/tests/Build
> index 86262c720857..5e00cbfd2d56 100644
> --- a/tools/perf/arch/x86/tests/Build
> +++ b/tools/perf/arch/x86/tests/Build
> @@ -10,6 +10,7 @@ perf-test-$(CONFIG_AUXTRACE) += insn-x86.o
>  endif
>  perf-test-$(CONFIG_X86_64) += bp-modify.o
>  perf-test-y += amd-ibs-via-core-pmu.o
> +perf-test-y += amd-ibs-period.o
>  
>  ifdef SHELLCHECK
>    SHELL_TESTS := gen-insn-x86-dat.sh
> diff --git a/tools/perf/arch/x86/tests/amd-ibs-period.c b/tools/perf/arch/x86/tests/amd-ibs-period.c
> new file mode 100644
> index 000000000000..0cf3656e4b9b
> --- /dev/null
> +++ b/tools/perf/arch/x86/tests/amd-ibs-period.c
> @@ -0,0 +1,1001 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <sched.h>
> +#include <sys/syscall.h>
> +#include <sys/mman.h>
> +#include <sys/ioctl.h>
> +#include <string.h>
> +
> +#include "arch-tests.h"
> +#include "linux/perf_event.h"
> +#include "linux/zalloc.h"
> +#include "tests/tests.h"
> +#include "../perf-sys.h"
> +#include "pmu.h"
> +#include "pmus.h"
> +#include "debug.h"
> +#include "util.h"
> +#include "strbuf.h"
> +#include "../util/env.h"
> +
> +#define PAGE_SIZE               sysconf(_SC_PAGESIZE)
> +
> +#define PERF_MMAP_DATA_PAGES    32L
> +#define PERF_MMAP_DATA_SIZE     (PERF_MMAP_DATA_PAGES * PAGE_SIZE)
> +#define PERF_MMAP_DATA_MASK     (PERF_MMAP_DATA_SIZE - 1)
> +#define PERF_MMAP_TOTAL_PAGES   (PERF_MMAP_DATA_PAGES + 1)
> +#define PERF_MMAP_TOTAL_SIZE    (PERF_MMAP_TOTAL_PAGES * PAGE_SIZE)
> +
> +#define rmb()                   asm volatile("lfence":::"memory")
> +
> +enum {
> +	FD_ERROR,
> +	FD_SUCCESS,
> +};
> +
> +enum {
> +	IBS_FETCH,
> +	IBS_OP,
> +};
> +
> +struct perf_pmu *fetch_pmu;
> +struct perf_pmu *op_pmu;
> +unsigned int perf_event_max_sample_rate;
> +
> +/* Dummy workload to generate IBS samples. */
> +static int dummy_workload_1(unsigned long count)
> +{
> +	int (*func)(void);
> +	int ret = 0;
> +	char *p;
> +	char insn1[] = {
> +		0xb8, 0x01, 0x00, 0x00, 0x00, /* mov 1,%eax */
> +		0xc3, /* ret */
> +		0xcc, /* int 3 */
> +	};
> +
> +	char insn2[] = {
> +		0xb8, 0x02, 0x00, 0x00, 0x00, /* mov 2,%eax */
> +		0xc3, /* ret */
> +		0xcc, /* int 3 */
> +	};
> +
> +	p = zalloc(2 * PAGE_SIZE);
> +	if (!p) {
> +		printf("malloc() failed. %m");
> +		return 1;
> +	}
> +
> +	func = (void *)((unsigned long)(p + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
> +
> +	ret = mprotect(func, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
> +	if (ret) {
> +		printf("mprotect() failed. %m");
> +		goto out;
> +	}
> +
> +	if (count < 100000)
> +		count = 100000;
> +	else if (count > 10000000)
> +		count = 10000000;
> +	while (count--) {
> +		memcpy(func, insn1, sizeof(insn1));
> +		if (func() != 1) {
> +			pr_debug("ERROR insn1\n");
> +			ret = -1;
> +			goto out;
> +		}
> +		memcpy(func, insn2, sizeof(insn2));
> +		if (func() != 2) {
> +			pr_debug("ERROR insn2\n");
> +			ret = -1;
> +			goto out;
> +		}
> +	}
> +
> +out:
> +	free(p);
> +	return ret;
> +}
> +
> +/* Another dummy workload to generate IBS samples. */
> +static void dummy_workload_2(char *perf)
> +{
> +	char bench[] = " bench sched messaging -g 10 -l 5000 > /dev/null 2>&1";
> +	char taskset[] = "taskset -c 0 ";
> +	int ret __maybe_unused;
> +	struct strbuf sb;
> +	char *cmd;
> +
> +	strbuf_init(&sb, 0);
> +	strbuf_add(&sb, taskset, strlen(taskset));
> +	strbuf_add(&sb, perf, strlen(perf));
> +	strbuf_add(&sb, bench, strlen(bench));
> +	cmd = strbuf_detach(&sb, NULL);
> +	ret = system(cmd);
> +	free(cmd);
> +}
> +
> +static int sched_affine(int cpu)
> +{
> +	cpu_set_t set;
> +
> +	CPU_ZERO(&set);
> +	CPU_SET(cpu, &set);
> +	if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) {
> +		pr_debug("sched_setaffinity() failed. [%m]");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static void
> +copy_sample_data(void *src, unsigned long offset, void *dest, size_t size)
> +{
> +	size_t chunk1_size, chunk2_size;
> +
> +	if ((offset + size) < (size_t)PERF_MMAP_DATA_SIZE) {
> +		memcpy(dest, src + offset, size);
> +	} else {
> +		chunk1_size = PERF_MMAP_DATA_SIZE - offset;
> +		chunk2_size = size - chunk1_size;
> +
> +		memcpy(dest, src + offset, chunk1_size);
> +		memcpy(dest + chunk1_size, src, chunk2_size);
> +	}
> +}
> +
> +static int rb_read(struct perf_event_mmap_page *rb, void *dest, size_t size)
> +{
> +	void *base;
> +	unsigned long data_tail, data_head;
> +
> +	/* Casting to (void *) is needed. */
> +	base = (void *)rb + PAGE_SIZE;
> +
> +	data_head = rb->data_head;
> +	rmb();
> +	data_tail = rb->data_tail;
> +
> +	if ((data_head - data_tail) < size)
> +		return -1;
> +
> +	data_tail &= PERF_MMAP_DATA_MASK;
> +	copy_sample_data(base, data_tail, dest, size);
> +	rb->data_tail += size;
> +	return 0;
> +}
> +
> +static void rb_skip(struct perf_event_mmap_page *rb, size_t size)
> +{
> +	size_t data_head = rb->data_head;
> +
> +	rmb();
> +
> +	if ((rb->data_tail + size) > data_head)
> +		rb->data_tail = data_head;
> +	else
> +		rb->data_tail += size;
> +}
> +
> +/* Sample period value taken from perf sample must match with expected value. */
> +static int period_equal(unsigned long exp_period, unsigned long act_period)
> +{
> +	return exp_period == act_period ? 0 : -1;
> +}
> +
> +/*
> + * Sample period value taken from perf sample must be >= minimum sample period
> + * supported by IBS HW.
> + */
> +static int period_higher(unsigned long min_period, unsigned long act_period)
> +{
> +	return min_period <= act_period ? 0 : -1;
> +}
> +
> +static int rb_drain_samples(struct perf_event_mmap_page *rb,
> +			    unsigned long exp_period,
> +			    int *nr_samples,
> +			    int (*callback)(unsigned long, unsigned long))
> +{
> +	struct perf_event_header hdr;
> +	unsigned long period;
> +	int ret = 0;
> +
> +	/*
> +	 * PERF_RECORD_SAMPLE:
> +	 * struct {
> +	 *	struct perf_event_header hdr;
> +	 *	{ u64			 period;     } && PERF_SAMPLE_PERIOD
> +	 * };
> +	 */
> +	while (1) {
> +		if (rb_read(rb, &hdr, sizeof(hdr)))
> +			return ret;
> +
> +		if (hdr.type == PERF_RECORD_SAMPLE) {
> +			(*nr_samples)++;
> +			period = 0;
> +			if (rb_read(rb, &period, sizeof(period)))
> +				pr_debug("rb_read(period) error. [%m]");
> +			ret |= callback(exp_period, period);
> +		} else {
> +			rb_skip(rb, hdr.size - sizeof(hdr));
> +		}
> +	}
> +	return ret;
> +}
> +
> +static long perf_event_open(struct perf_event_attr *attr, pid_t pid,
> +			    int cpu, int group_fd, unsigned long flags)
> +{
> +	return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
> +}
> +
> +static void fetch_prepare_attr(struct perf_event_attr *attr,
> +			       unsigned long long config, int freq,
> +			       unsigned long sample_period)
> +{
> +	memset(attr, 0, sizeof(struct perf_event_attr));
> +
> +	attr->type = fetch_pmu->type;
> +	attr->size = sizeof(struct perf_event_attr);
> +	attr->config = config;
> +	attr->disabled = 1;
> +	attr->sample_type = PERF_SAMPLE_PERIOD;
> +	attr->freq = freq;
> +	attr->sample_period = sample_period; /* = ->sample_freq */
> +}
> +
> +static void op_prepare_attr(struct perf_event_attr *attr,
> +			    unsigned long config, int freq,
> +			    unsigned long sample_period)
> +{
> +	memset(attr, 0, sizeof(struct perf_event_attr));
> +
> +	attr->type = op_pmu->type;
> +	attr->size = sizeof(struct perf_event_attr);
> +	attr->config = config;
> +	attr->disabled = 1;
> +	attr->sample_type = PERF_SAMPLE_PERIOD;
> +	attr->freq = freq;
> +	attr->sample_period = sample_period; /* = ->sample_freq */
> +}
> +
> +struct ibs_configs {
> +	/* Input */
> +	unsigned long config;
> +
> +	/* Expected output */
> +	unsigned long period;
> +	int fd;
> +};
> +
> +/*
> + * Somehow first Fetch event with sample period = 0x10 causes 0
> + * samples. So start with large period and decrease it gradually.
> + */
> +struct ibs_configs fetch_configs[] = {
> +	{ .config =  0xffff, .period = 0xffff0, .fd = FD_SUCCESS },
> +	{ .config =  0x1000, .period = 0x10000, .fd = FD_SUCCESS },
> +	{ .config =    0xff, .period =   0xff0, .fd = FD_SUCCESS },
> +	{ .config =     0x1, .period =    0x10, .fd = FD_SUCCESS },
> +	{ .config =     0x0, .period =      -1, .fd = FD_ERROR   },
> +	{ .config = 0x10000, .period =      -1, .fd = FD_ERROR   },
> +};
> +
> +struct ibs_configs op_configs[] = {
> +	{ .config =        0x0, .period =        -1, .fd = FD_ERROR   },
> +	{ .config =        0x1, .period =        -1, .fd = FD_ERROR   },
> +	{ .config =        0x8, .period =        -1, .fd = FD_ERROR   },
> +	{ .config =        0x9, .period =      0x90, .fd = FD_SUCCESS },
> +	{ .config =        0xf, .period =      0xf0, .fd = FD_SUCCESS },
> +	{ .config =     0x1000, .period =   0x10000, .fd = FD_SUCCESS },
> +	{ .config =     0xffff, .period =   0xffff0, .fd = FD_SUCCESS },
> +	{ .config =    0x10000, .period =        -1, .fd = FD_ERROR   },
> +	{ .config =   0x100000, .period =  0x100000, .fd = FD_SUCCESS },
> +	{ .config =   0xf00000, .period =  0xf00000, .fd = FD_SUCCESS },
> +	{ .config =   0xf0ffff, .period =  0xfffff0, .fd = FD_SUCCESS },
> +	{ .config =  0x1f0ffff, .period = 0x1fffff0, .fd = FD_SUCCESS },
> +	{ .config =  0x7f0ffff, .period = 0x7fffff0, .fd = FD_SUCCESS },
> +	{ .config =  0x8f0ffff, .period =        -1, .fd = FD_ERROR   },
> +	{ .config = 0x17f0ffff, .period =        -1, .fd = FD_ERROR   },
> +};
> +
> +static int __ibs_config_test(int ibs_type, struct ibs_configs *config, int *nr_samples)
> +{
> +	struct perf_event_attr attr;
> +	int fd, i;
> +	void *rb;
> +	int ret = 0;
> +
> +	if (ibs_type == IBS_FETCH)
> +		fetch_prepare_attr(&attr, config->config, 0, 0);
> +	else
> +		op_prepare_attr(&attr, config->config, 0, 0);
> +
> +	/* CPU0, All processes */
> +	fd = perf_event_open(&attr, -1, 0, -1, 0);
> +	if (config->fd == FD_ERROR) {
> +		if (fd != -1) {
> +			close(fd);
> +			return -1;
> +		}
> +		return 0;
> +	}
> +	if (fd <= -1)
> +		return -1;
> +
> +	rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,
> +		  MAP_SHARED, fd, 0);
> +	if (rb == MAP_FAILED) {
> +		pr_debug("mmap() failed. [%m]\n");
> +		return -1;
> +	}
> +
> +	ioctl(fd, PERF_EVENT_IOC_RESET, 0);
> +	ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
> +
> +	i = 5;
> +	while (i--) {
> +		dummy_workload_1(1000000);
> +
> +		ret = rb_drain_samples(rb, config->period, nr_samples,
> +				       period_equal);
> +		if (ret)
> +			break;
> +	}
> +
> +	ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
> +	munmap(rb, PERF_MMAP_TOTAL_SIZE);
> +	close(fd);
> +	return ret;
> +}
> +
> +static int ibs_config_test(void)
> +{
> +	int nr_samples = 0;
> +	unsigned long i;
> +	int ret = 0;
> +	int r;
> +
> +	pr_debug("\nIBS config tests:\n");
> +	pr_debug("-----------------\n");
> +
> +	pr_debug("Fetch PMU tests:\n");
> +	for (i = 0; i < ARRAY_SIZE(fetch_configs); i++) {
> +		nr_samples = 0;
> +		r = __ibs_config_test(IBS_FETCH, &(fetch_configs[i]), &nr_samples);
> +
> +		if (fetch_configs[i].fd == FD_ERROR) {
> +			pr_debug("0x%-16lx: %-4s\n", fetch_configs[i].config,
> +				 !r ? "Ok" : "Fail");
> +		} else {
> +			/*
> +			 * Although nr_samples == 0 is reported as Fail here,
> +			 * the failure status is not cascaded up because, we
> +			 * can not decide whether test really failed or not
> +			 * without actual samples.
> +			 */
> +			pr_debug("0x%-16lx: %-4s (nr samples: %d)\n", fetch_configs[i].config,
> +				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
> +		}
> +
> +		ret |= r;
> +	}
> +
> +	pr_debug("Op PMU tests:\n");
> +	for (i = 0; i < ARRAY_SIZE(op_configs); i++) {
> +		nr_samples = 0;
> +		r = __ibs_config_test(IBS_OP, &(op_configs[i]), &nr_samples);
> +
> +		if (op_configs[i].fd == FD_ERROR) {
> +			pr_debug("0x%-16lx: %-4s\n", op_configs[i].config,
> +				 !r ? "Ok" : "Fail");
> +		} else {
> +			/*
> +			 * Although nr_samples == 0 is reported as Fail here,
> +			 * the failure status is not cascaded up because, we
> +			 * can not decide whether test really failed or not
> +			 * without actual samples.
> +			 */
> +			pr_debug("0x%-16lx: %-4s (nr samples: %d)\n", op_configs[i].config,
> +				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
> +		}
> +
> +		ret |= r;
> +	}
> +
> +	return ret;
> +}
> +
> +struct ibs_period {
> +	/* Input */
> +	int freq;
> +	unsigned long sample_freq;
> +
> +	/* Output */
> +	int ret;
> +	unsigned long period;
> +};
> +
> +struct ibs_period fetch_period[] = {
> +	{ .freq = 0, .sample_freq =         0, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 0, .sample_freq =         1, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 0, .sample_freq =       0xf, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 0, .sample_freq =      0x10, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 0, .sample_freq =      0x11, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 0, .sample_freq =      0x8f, .ret = FD_SUCCESS, .period =      0x80 },
> +	{ .freq = 0, .sample_freq =      0x90, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 0, .sample_freq =      0x91, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 0, .sample_freq =     0x4d2, .ret = FD_SUCCESS, .period =     0x4d0 },
> +	{ .freq = 0, .sample_freq =    0x1007, .ret = FD_SUCCESS, .period =    0x1000 },
> +	{ .freq = 0, .sample_freq =    0xfff0, .ret = FD_SUCCESS, .period =    0xfff0 },
> +	{ .freq = 0, .sample_freq =    0xffff, .ret = FD_SUCCESS, .period =    0xfff0 },
> +	{ .freq = 0, .sample_freq =   0x10010, .ret = FD_SUCCESS, .period =   0x10010 },
> +	{ .freq = 0, .sample_freq =  0x7fffff, .ret = FD_SUCCESS, .period =  0x7ffff0 },
> +	{ .freq = 0, .sample_freq = 0xfffffff, .ret = FD_SUCCESS, .period = 0xffffff0 },
> +	{ .freq = 1, .sample_freq =         0, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 1, .sample_freq =         1, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =       0xf, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =      0x10, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =      0x11, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =      0x8f, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =      0x90, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =      0x91, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =     0x4d2, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =    0x1007, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =    0xfff0, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =    0xffff, .ret = FD_SUCCESS, .period =      0x10 },
> +	{ .freq = 1, .sample_freq =   0x10010, .ret = FD_SUCCESS, .period =      0x10 },
> +	/* ret=FD_ERROR because freq > default perf_event_max_sample_rate (100000) */
> +	{ .freq = 1, .sample_freq =  0x7fffff, .ret = FD_ERROR,   .period =        -1 },
> +};
> +
> +struct ibs_period op_period[] = {
> +	{ .freq = 0, .sample_freq =         0, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 0, .sample_freq =         1, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 0, .sample_freq =       0xf, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 0, .sample_freq =      0x10, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 0, .sample_freq =      0x11, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 0, .sample_freq =      0x8f, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 0, .sample_freq =      0x90, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 0, .sample_freq =      0x91, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 0, .sample_freq =     0x4d2, .ret = FD_SUCCESS, .period =     0x4d0 },
> +	{ .freq = 0, .sample_freq =    0x1007, .ret = FD_SUCCESS, .period =    0x1000 },
> +	{ .freq = 0, .sample_freq =    0xfff0, .ret = FD_SUCCESS, .period =    0xfff0 },
> +	{ .freq = 0, .sample_freq =    0xffff, .ret = FD_SUCCESS, .period =    0xfff0 },
> +	{ .freq = 0, .sample_freq =   0x10010, .ret = FD_SUCCESS, .period =   0x10010 },
> +	{ .freq = 0, .sample_freq =  0x7fffff, .ret = FD_SUCCESS, .period =  0x7ffff0 },
> +	{ .freq = 0, .sample_freq = 0xfffffff, .ret = FD_SUCCESS, .period = 0xffffff0 },
> +	{ .freq = 1, .sample_freq =         0, .ret = FD_ERROR,   .period =        -1 },
> +	{ .freq = 1, .sample_freq =         1, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =       0xf, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =      0x10, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =      0x11, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =      0x8f, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =      0x90, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =      0x91, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =     0x4d2, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =    0x1007, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =    0xfff0, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =    0xffff, .ret = FD_SUCCESS, .period =      0x90 },
> +	{ .freq = 1, .sample_freq =   0x10010, .ret = FD_SUCCESS, .period =      0x90 },
> +	/* ret=FD_ERROR because freq > default perf_event_max_sample_rate (100000) */
> +	{ .freq = 1, .sample_freq =  0x7fffff, .ret = FD_ERROR,   .period =        -1 },
> +};
> +
> +static int __ibs_period_constraint_test(int ibs_type, struct ibs_period *period,
> +					int *nr_samples)
> +{
> +	struct perf_event_attr attr;
> +	int ret = 0;
> +	void *rb;
> +	int fd;
> +
> +	if (period->freq && period->sample_freq > perf_event_max_sample_rate)
> +		period->ret = FD_ERROR;
> +
> +	if (ibs_type == IBS_FETCH)
> +		fetch_prepare_attr(&attr, 0, period->freq, period->sample_freq);
> +	else
> +		op_prepare_attr(&attr, 0, period->freq, period->sample_freq);
> +
> +	/* CPU0, All processes */
> +	fd = perf_event_open(&attr, -1, 0, -1, 0);
> +	if (period->ret == FD_ERROR) {
> +		if (fd != -1) {
> +			close(fd);
> +			return -1;
> +		}
> +		return 0;
> +	}
> +	if (fd <= -1)
> +		return -1;
> +
> +	rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,
> +		  MAP_SHARED, fd, 0);
> +	if (rb == MAP_FAILED) {
> +		pr_debug("mmap() failed. [%m]\n");
> +		close(fd);
> +		return -1;
> +	}
> +
> +	ioctl(fd, PERF_EVENT_IOC_RESET, 0);
> +	ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
> +
> +	if (period->freq) {
> +		dummy_workload_1(100000);
> +		ret = rb_drain_samples(rb, period->period, nr_samples,
> +				       period_higher);
> +	} else {
> +		dummy_workload_1(period->sample_freq * 10);
> +		ret = rb_drain_samples(rb, period->period, nr_samples,
> +				       period_equal);
> +	}
> +
> +	ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
> +	munmap(rb, PERF_MMAP_TOTAL_SIZE);
> +	close(fd);
> +	return ret;
> +}
> +
> +static int ibs_period_constraint_test(void)
> +{
> +	unsigned long i;
> +	int nr_samples;
> +	int ret = 0;
> +	int r;
> +
> +	pr_debug("\nIBS sample period constraint tests:\n");
> +	pr_debug("-----------------------------------\n");
> +
> +	pr_debug("Fetch PMU test:\n");
> +	for (i = 0; i < ARRAY_SIZE(fetch_period); i++) {
> +		nr_samples = 0;
> +		r = __ibs_period_constraint_test(IBS_FETCH, &fetch_period[i],
> +						 &nr_samples);
> +
> +		if (fetch_period[i].ret == FD_ERROR) {
> +			pr_debug("freq %d, sample_freq %9ld: %-4s\n",
> +				 fetch_period[i].freq, fetch_period[i].sample_freq,
> +				 !r ? "Ok" : "Fail");
> +		} else {
> +			/*
> +			 * Although nr_samples == 0 is reported as Fail here,
> +			 * the failure status is not cascaded up because, we
> +			 * can not decide whether test really failed or not
> +			 * without actual samples.
> +			 */
> +			pr_debug("freq %d, sample_freq %9ld: %-4s (nr samples: %d)\n",
> +				 fetch_period[i].freq, fetch_period[i].sample_freq,
> +				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
> +		}
> +		ret |= r;
> +	}
> +
> +	pr_debug("Op PMU test:\n");
> +	for (i = 0; i < ARRAY_SIZE(op_period); i++) {
> +		nr_samples = 0;
> +		r = __ibs_period_constraint_test(IBS_OP, &op_period[i],
> +						 &nr_samples);
> +
> +		if (op_period[i].ret == FD_ERROR) {
> +			pr_debug("freq %d, sample_freq %9ld: %-4s\n",
> +				 op_period[i].freq, op_period[i].sample_freq,
> +				 !r ? "Ok" : "Fail");
> +		} else {
> +			/*
> +			 * Although nr_samples == 0 is reported as Fail here,
> +			 * the failure status is not cascaded up because, we
> +			 * can not decide whether test really failed or not
> +			 * without actual samples.
> +			 */
> +			pr_debug("freq %d, sample_freq %9ld: %-4s (nr samples: %d)\n",
> +				 op_period[i].freq, op_period[i].sample_freq,
> +				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
> +		}
> +		ret |= r;
> +	}
> +
> +	return ret;
> +}
> +
> +struct ibs_ioctl {
> +	/* Input */
> +	int freq;
> +	unsigned long period;
> +
> +	/* Expected output */
> +	int ret;
> +};
> +
> +struct ibs_ioctl fetch_ioctl[] = {
> +	{ .freq = 0, .period =     0x0, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =     0x1, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =     0xf, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x10, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period =    0x11, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x1f, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x20, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period =    0x80, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period =    0x8f, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x90, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period =    0x91, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =   0x100, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period =  0xfff0, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period =  0xffff, .ret = FD_ERROR   },
> +	{ .freq = 0, .period = 0x10000, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period = 0x1fff0, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period = 0x1fff5, .ret = FD_ERROR   },
> +	{ .freq = 1, .period =     0x0, .ret = FD_ERROR   },
> +	{ .freq = 1, .period =     0x1, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =     0xf, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x10, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x11, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x1f, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x20, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x80, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x8f, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x90, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x91, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =   0x100, .ret = FD_SUCCESS },
> +};
> +
> +struct ibs_ioctl op_ioctl[] = {
> +	{ .freq = 0, .period =     0x0, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =     0x1, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =     0xf, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x10, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x11, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x1f, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x20, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x80, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x8f, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =    0x90, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period =    0x91, .ret = FD_ERROR   },
> +	{ .freq = 0, .period =   0x100, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period =  0xfff0, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period =  0xffff, .ret = FD_ERROR   },
> +	{ .freq = 0, .period = 0x10000, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period = 0x1fff0, .ret = FD_SUCCESS },
> +	{ .freq = 0, .period = 0x1fff5, .ret = FD_ERROR   },
> +	{ .freq = 1, .period =     0x0, .ret = FD_ERROR   },
> +	{ .freq = 1, .period =     0x1, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =     0xf, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x10, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x11, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x1f, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x20, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x80, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x8f, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x90, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =    0x91, .ret = FD_SUCCESS },
> +	{ .freq = 1, .period =   0x100, .ret = FD_SUCCESS },
> +};
> +
> +static int __ibs_ioctl_test(int ibs_type, struct ibs_ioctl *ibs_ioctl)
> +{
> +	struct perf_event_attr attr;
> +	int ret = 0;
> +	int fd;
> +	int r;
> +
> +	if (ibs_type == IBS_FETCH)
> +		fetch_prepare_attr(&attr, 0, ibs_ioctl->freq, 1000);
> +	else
> +		op_prepare_attr(&attr, 0, ibs_ioctl->freq, 1000);
> +
> +	/* CPU0, All processes */
> +	fd = perf_event_open(&attr, -1, 0, -1, 0);
> +	if (fd <= -1) {
> +		pr_debug("event_open() Failed\n");
> +		return -1;
> +	}
> +
> +	r = ioctl(fd, PERF_EVENT_IOC_PERIOD, &ibs_ioctl->period);
> +	if ((ibs_ioctl->ret == FD_SUCCESS && r <= -1) ||
> +	    (ibs_ioctl->ret == FD_ERROR && r >= 0)) {
> +		ret = -1;
> +	}
> +
> +	close(fd);
> +	return ret;
> +}
> +
> +static int ibs_ioctl_test(void)
> +{
> +	unsigned long i;
> +	int ret = 0;
> +	int r;
> +
> +	pr_debug("\nIBS ioctl() tests:\n");
> +	pr_debug("------------------\n");
> +
> +	pr_debug("Fetch PMU tests\n");
> +	for (i = 0; i < ARRAY_SIZE(fetch_ioctl); i++) {
> +		r = __ibs_ioctl_test(IBS_FETCH, &fetch_ioctl[i]);
> +
> +		pr_debug("ioctl(%s = 0x%-7lx): %s\n",
> +			 fetch_ioctl[i].freq ? "freq  " : "period",
> +			 fetch_ioctl[i].period, r ? "Fail" : "Ok");
> +		ret |= r;
> +	}
> +
> +	pr_debug("Op PMU tests\n");
> +	for (i = 0; i < ARRAY_SIZE(op_ioctl); i++) {
> +		r = __ibs_ioctl_test(IBS_OP, &op_ioctl[i]);
> +
> +		pr_debug("ioctl(%s = 0x%-7lx): %s\n",
> +			 op_ioctl[i].freq ? "freq  " : "period",
> +			 op_ioctl[i].period, r ? "Fail" : "Ok");
> +		ret |= r;
> +	}
> +
> +	return ret;
> +}
> +
> +static int ibs_freq_neg_test(void)
> +{
> +	struct perf_event_attr attr;
> +	int fd;
> +
> +	pr_debug("\nIBS freq (negative) tests:\n");
> +	pr_debug("--------------------------\n");
> +
> +	/*
> +	 * Assuming perf_event_max_sample_rate <= 100000,
> +	 * config: 0x300D40 ==> MaxCnt: 200000
> +	 */
> +	op_prepare_attr(&attr, 0x300D40, 1, 0);
> +
> +	/* CPU0, All processes */
> +	fd = perf_event_open(&attr, -1, 0, -1, 0);
> +	if (fd != -1) {
> +		pr_debug("freq 1, sample_freq 200000: Fail\n");
> +		close(fd);
> +		return -1;
> +	}
> +
> +	pr_debug("freq 1, sample_freq 200000: Ok\n");
> +
> +	return 0;
> +}
> +
> +struct ibs_l3missonly {
> +	/* Input */
> +	int freq;
> +	unsigned long sample_freq;
> +
> +	/* Expected output */
> +	int ret;
> +	unsigned long min_period;
> +};
> +
> +struct ibs_l3missonly fetch_l3missonly = {
> +	.freq = 1,
> +	.sample_freq = 10000,
> +	.ret = FD_SUCCESS,
> +	.min_period = 0x10,
> +};
> +
> +struct ibs_l3missonly op_l3missonly = {
> +	.freq = 1,
> +	.sample_freq = 10000,
> +	.ret = FD_SUCCESS,
> +	.min_period = 0x90,
> +};
> +
> +static int __ibs_l3missonly_test(char *perf, int ibs_type, int *nr_samples,
> +				 struct ibs_l3missonly *l3missonly)
> +{
> +	struct perf_event_attr attr;
> +	int ret = 0;
> +	void *rb;
> +	int fd;
> +
> +	if (l3missonly->sample_freq > perf_event_max_sample_rate)
> +		l3missonly->ret = FD_ERROR;
> +
> +	if (ibs_type == IBS_FETCH) {
> +		fetch_prepare_attr(&attr, 0x800000000000000UL, l3missonly->freq,
> +				   l3missonly->sample_freq);
> +	} else {
> +		op_prepare_attr(&attr, 0x10000, l3missonly->freq,
> +				l3missonly->sample_freq);
> +	}
> +
> +	/* CPU0, All processes */
> +	fd = perf_event_open(&attr, -1, 0, -1, 0);
> +	if (l3missonly->ret == FD_ERROR) {
> +		if (fd != -1) {
> +			close(fd);
> +			return -1;
> +		}
> +		return 0;
> +	}
> +	if (fd == -1) {
> +		pr_debug("perf_event_open() failed. [%m]\n");
> +		return -1;
> +	}
> +
> +	rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,
> +		  MAP_SHARED, fd, 0);
> +	if (rb == MAP_FAILED) {
> +		pr_debug("mmap() failed. [%m]\n");
> +		close(fd);
> +		return -1;
> +	}
> +
> +	ioctl(fd, PERF_EVENT_IOC_RESET, 0);
> +	ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
> +
> +	dummy_workload_2(perf);
> +
> +	ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
> +
> +	ret = rb_drain_samples(rb, l3missonly->min_period, nr_samples, period_higher);
> +
> +	munmap(rb, PERF_MMAP_TOTAL_SIZE);
> +	close(fd);
> +	return ret;
> +}
> +
> +static int ibs_l3missonly_test(char *perf)
> +{
> +	int nr_samples = 0;
> +	int ret = 0;
> +	int r = 0;
> +
> +	pr_debug("\nIBS L3MissOnly test: (takes a while)\n");
> +	pr_debug("--------------------\n");
> +
> +	if (perf_pmu__has_format(fetch_pmu, "l3missonly")) {
> +		nr_samples = 0;
> +		r = __ibs_l3missonly_test(perf, IBS_FETCH, &nr_samples, &fetch_l3missonly);
> +		if (fetch_l3missonly.ret == FD_ERROR) {
> +			pr_debug("Fetch L3MissOnly: %-4s\n", !r ? "Ok" : "Fail");
> +		} else {
> +			/*
> +			 * Although nr_samples == 0 is reported as Fail here,
> +			 * the failure status is not cascaded up because, we
> +			 * can not decide whether test really failed or not
> +			 * without actual samples.
> +			 */
> +			pr_debug("Fetch L3MissOnly: %-4s (nr_samples: %d)\n",
> +				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
> +		}
> +		ret |= r;
> +	}
> +
> +	if (perf_pmu__has_format(op_pmu, "l3missonly")) {
> +		nr_samples = 0;
> +		r = __ibs_l3missonly_test(perf, IBS_OP, &nr_samples, &op_l3missonly);
> +		if (op_l3missonly.ret == FD_ERROR) {
> +			pr_debug("Op L3MissOnly:    %-4s\n", !r ? "Ok" : "Fail");
> +		} else {
> +			/*
> +			 * Although nr_samples == 0 is reported as Fail here,
> +			 * the failure status is not cascaded up because, we
> +			 * can not decide whether test really failed or not
> +			 * without actual samples.
> +			 */
> +			pr_debug("Op L3MissOnly:    %-4s (nr_samples: %d)\n",
> +				 (!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);
> +		}
> +		ret |= r;
> +	}
> +
> +	return ret;
> +}
> +
> +static unsigned int get_perf_event_max_sample_rate(void)
> +{
> +	unsigned int max_sample_rate = 100000;
> +	FILE *fp;
> +	int ret;
> +
> +	fp = fopen("/proc/sys/kernel/perf_event_max_sample_rate", "r");
> +	if (!fp) {
> +		pr_debug("Can't open perf_event_max_sample_rate. Asssuming %d\n",
> +			 max_sample_rate);
> +		goto out;
> +	}
> +
> +	ret = fscanf(fp, "%d", &max_sample_rate);
> +	if (ret == EOF) {
> +		pr_debug("Can't read perf_event_max_sample_rate. Assuming 100000\n");
> +		max_sample_rate = 100000;
> +	}
> +	fclose(fp);
> +
> +out:
> +	return max_sample_rate;
> +}
> +
> +int test__amd_ibs_period(struct test_suite *test __maybe_unused,
> +			 int subtest __maybe_unused)
> +{
> +	char perf[PATH_MAX] = {'\0'};
> +	int ret = TEST_OK;
> +
> +	/*
> +	 * Reading perf_event_max_sample_rate only once _might_ cause some
> +	 * of the test to fail if kernel changes it after reading it here.
> +	 */
> +	perf_event_max_sample_rate = get_perf_event_max_sample_rate();
> +	fetch_pmu = perf_pmus__find("ibs_fetch");
> +	op_pmu = perf_pmus__find("ibs_op");
> +
> +	if (!x86__is_amd_cpu() || !fetch_pmu || !op_pmu)
> +		return TEST_SKIP;
> +
> +	perf_exe(perf, sizeof(perf));
> +
> +	if (sched_affine(0))
> +		return TEST_FAIL;
> +
> +	/*
> +	 * Perf event can be opened in two modes:
> +	 * 1 Freq mode
> +	 *   perf_event_attr->freq = 1, ->sample_freq = <frequency>
> +	 * 2 Sample period mode
> +	 *   perf_event_attr->freq = 0, ->sample_period = <period>
> +	 *
> +	 * Instead of using above interface, IBS event in 'sample period mode'
> +	 * can also be opened by passing <period> value directly in a MaxCnt
> +	 * bitfields of perf_event_attr->config. Test this IBS specific special
> +	 * interface.
> +	 */
> +	if (ibs_config_test())
> +		ret = TEST_FAIL;
> +
> +	/*
> +	 * IBS Fetch and Op PMUs have HW constraints on minimum sample period.
> +	 * Also, sample period value must be in multiple of 0x10. Test that IBS
> +	 * driver honors HW constraints for various possible values in Freq as
> +	 * well as Sample Period mode IBS events.
> +	 */
> +	if (ibs_period_constraint_test())
> +		ret = TEST_FAIL;
> +
> +	/*
> +	 * Test ioctl() with various sample period values for IBS event.
> +	 */
> +	if (ibs_ioctl_test())
> +		ret = TEST_FAIL;
> +
> +	/*
> +	 * Test that opening of freq mode IBS event fails when the freq value
> +	 * is passed through ->config, not explicitly in ->sample_freq. Also
> +	 * use high freq value (beyond perf_event_max_sample_rate) to test IBS
> +	 * driver do not bypass perf_event_max_sample_rate checks.
> +	 */
> +	if (ibs_freq_neg_test())
> +		ret = TEST_FAIL;
> +
> +	/*
> +	 * L3MissOnly is a post-processing filter, i.e. IBS HW checks for L3
> +	 * Miss at the completion of the tagged uOp. The sample is discarded
> +	 * if the tagged uOp did not cause L3Miss. Also, IBS HW internally
> +	 * resets CurCnt to a small pseudo-random value and resumes counting.
> +	 * A new uOp is tagged once CurCnt reaches to MaxCnt. But the process
> +	 * repeats until the tagged uOp causes an L3 Miss.
> +	 *
> +	 * With the freq mode event, the next sample period is calculated by
> +	 * generic kernel on every sample to achieve desired freq of samples.
> +	 *
> +	 * Since the number of times HW internally reset CurCnt and the pseudo-
> +	 * random value of CurCnt for all those occurrences are not known to SW,
> +	 * the sample period adjustment by kernel goes for a toes for freq mode
> +	 * IBS events. Kernel will set very small period for the next sample if
> +	 * the window between current sample and prev sample is too high due to
> +	 * multiple samples being discarded internally by IBS HW.
> +	 *
> +	 * Test that IBS sample period constraints are honored when L3MissOnly
> +	 * is ON.
> +	 */
> +	if (ibs_l3missonly_test(perf))
> +		ret = TEST_FAIL;
> +
> +	return ret;
> +}
> diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c
> index a216a5d172ed..bfee2432515b 100644
> --- a/tools/perf/arch/x86/tests/arch-tests.c
> +++ b/tools/perf/arch/x86/tests/arch-tests.c
> @@ -25,6 +25,7 @@ DEFINE_SUITE("x86 bp modify", bp_modify);
>  #endif
>  DEFINE_SUITE("x86 Sample parsing", x86_sample_parsing);
>  DEFINE_SUITE("AMD IBS via core pmu", amd_ibs_via_core_pmu);
> +DEFINE_SUITE_EXCLUSIVE("AMD IBS sample period", amd_ibs_period);
>  static struct test_case hybrid_tests[] = {
>  	TEST_CASE_REASON("x86 hybrid event parsing", hybrid, "not hybrid"),
>  	{ .name = NULL, }
> @@ -50,6 +51,7 @@ struct test_suite *arch_tests[] = {
>  #endif
>  	&suite__x86_sample_parsing,
>  	&suite__amd_ibs_via_core_pmu,
> +	&suite__amd_ibs_period,
>  	&suite__hybrid,
>  	NULL,
>  };
> -- 
> 2.43.0
Re: [PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Ravi Bangoria 9 months, 2 weeks ago
On 30-Apr-25 2:25 AM, Arnaldo Carvalho de Melo wrote:
> On Tue, Apr 29, 2025 at 03:59:38AM +0000, Ravi Bangoria wrote:
>> IBS Fetch and IBS Op PMUs has various constraints on supported sample
>> periods. Add perf unit tests to test those.
>>
>> Running it in parallel with other tests causes intermittent failures.
>> Mark it exclusive to force it to run sequentially. Sample output on a
>> Zen5 machine:
> 
> I've applied the series and will test it now, but found some problems
> when building in some non-glibc systems, namely the use of PAGE_SIZE,
> that is used in libc headers, even in glibc, its just that in glibc we
> happen not to include that header where PAGE_SIZE gets redefined:
> 
> ⬢ [acme@toolbx perf-tools-next]$ grep PAGE_SIZE /usr/include/sys/*.h
> /usr/include/sys/user.h:#define PAGE_SIZE		(1UL << PAGE_SHIFT)
> /usr/include/sys/user.h:#define PAGE_MASK		(~(PAGE_SIZE-1))
> /usr/include/sys/user.h:#define NBPG			PAGE_SIZE
> ⬢ [acme@toolbx perf-tools-next]$
> 
> So I folded the following patch, see if it is acceptable and please ack.

Thanks for the fix Arnaldo. It LGTM.

Ravi
Re: [PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Arnaldo Carvalho de Melo 9 months, 2 weeks ago
On Tue, Apr 29, 2025 at 05:55:13PM -0300, Arnaldo Carvalho de Melo wrote:
> On Tue, Apr 29, 2025 at 03:59:38AM +0000, Ravi Bangoria wrote:
> > IBS Fetch and IBS Op PMUs has various constraints on supported sample
> > periods. Add perf unit tests to test those.
> > 
> > Running it in parallel with other tests causes intermittent failures.
> > Mark it exclusive to force it to run sequentially. Sample output on a
> > Zen5 machine:
> 
> I've applied the series and will test it now, but found some problems
> when building in some non-glibc systems, namely the use of PAGE_SIZE,
> that is used in libc headers, even in glibc, its just that in glibc we
> happen not to include that header where PAGE_SIZE gets redefined:
> 
> ⬢ [acme@toolbx perf-tools-next]$ grep PAGE_SIZE /usr/include/sys/*.h
> /usr/include/sys/user.h:#define PAGE_SIZE		(1UL << PAGE_SHIFT)
> /usr/include/sys/user.h:#define PAGE_MASK		(~(PAGE_SIZE-1))
> /usr/include/sys/user.h:#define NBPG			PAGE_SIZE
> ⬢ [acme@toolbx perf-tools-next]$
> 
> So I folded the following patch, see if it is acceptable and please ack.
> 
> Thanks for respining it!

Another issue when building with clang on musl:

arch/x86/tests/amd-ibs-period.c:81:3: error: no matching function for call to 'memcpy'
                memcpy(func, insn1, sizeof(insn1));
                ^~~~~~
/usr/include/string.h:27:7: note: candidate function not viable: no known conversion from 'int (*)(void)' to 'void *' for 1st argument
void *memcpy (void *__restrict, const void *__restrict, size_t);
      ^
/usr/include/fortify/string.h:40:27: note: candidate function not viable: no known conversion from 'int (*)(void)' to 'void *const' for 1st argument
_FORTIFY_FN(memcpy) void *memcpy(void * _FORTIFY_POS0 __od,
                          ^
arch/x86/tests/amd-ibs-period.c:87:3: error: no matching function for call to 'memcpy'
                memcpy(func, insn2, sizeof(insn2));
                ^~~~~~
/usr/include/string.h:27:7: note: candidate function not viable: no known conversion from 'int (*)(void)' to 'void *' for 1st argument
void *memcpy (void *__restrict, const void *__restrict, size_t);
      ^
/usr/include/fortify/string.h:40:27: note: candidate function not viable: no known conversion from 'int (*)(void)' to 'void *const' for 1st argument
_FORTIFY_FN(memcpy) void *memcpy(void * _FORTIFY_POS0 __od,
                          ^
2 errors generated.
  CC      /tmp/build/perf/ui/browsers/header.o
  CC      /tmp/build/perf/arch/x86/util/mem-events.o

Adding the patch below cures it, still need to test on a Zen 5 system.

These issues were just in the regression test.

- Arnaldo

diff --git a/tools/perf/arch/x86/tests/amd-ibs-period.c b/tools/perf/arch/x86/tests/amd-ibs-period.c
index 946b0a377554fb81..a198434da9b5c4a1 100644
--- a/tools/perf/arch/x86/tests/amd-ibs-period.c
+++ b/tools/perf/arch/x86/tests/amd-ibs-period.c
@@ -78,13 +78,13 @@ static int dummy_workload_1(unsigned long count)
 	else if (count > 10000000)
 		count = 10000000;
 	while (count--) {
-		memcpy(func, insn1, sizeof(insn1));
+		memcpy((void *)func, insn1, sizeof(insn1));
 		if (func() != 1) {
 			pr_debug("ERROR insn1\n");
 			ret = -1;
 			goto out;
 		}
-		memcpy(func, insn2, sizeof(insn2));
+		memcpy((void *)func, insn2, sizeof(insn2));
 		if (func() != 2) {
 			pr_debug("ERROR insn2\n");
 			ret = -1;
Re: [PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Ravi Bangoria 9 months, 2 weeks ago
> Another issue when building with clang on musl:
> 
> arch/x86/tests/amd-ibs-period.c:81:3: error: no matching function for call to 'memcpy'
>                 memcpy(func, insn1, sizeof(insn1));
>                 ^~~~~~
> /usr/include/string.h:27:7: note: candidate function not viable: no known conversion from 'int (*)(void)' to 'void *' for 1st argument
> void *memcpy (void *__restrict, const void *__restrict, size_t);
>       ^
> /usr/include/fortify/string.h:40:27: note: candidate function not viable: no known conversion from 'int (*)(void)' to 'void *const' for 1st argument
> _FORTIFY_FN(memcpy) void *memcpy(void * _FORTIFY_POS0 __od,
>                           ^
> arch/x86/tests/amd-ibs-period.c:87:3: error: no matching function for call to 'memcpy'
>                 memcpy(func, insn2, sizeof(insn2));
>                 ^~~~~~
> /usr/include/string.h:27:7: note: candidate function not viable: no known conversion from 'int (*)(void)' to 'void *' for 1st argument
> void *memcpy (void *__restrict, const void *__restrict, size_t);
>       ^
> /usr/include/fortify/string.h:40:27: note: candidate function not viable: no known conversion from 'int (*)(void)' to 'void *const' for 1st argument
> _FORTIFY_FN(memcpy) void *memcpy(void * _FORTIFY_POS0 __od,
>                           ^
> 2 errors generated.
>   CC      /tmp/build/perf/ui/browsers/header.o
>   CC      /tmp/build/perf/arch/x86/util/mem-events.o
> 
> Adding the patch below cures it, still need to test on a Zen 5 system.

Thanks for the fix. Looks good.

Ravi
Re: [PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Arnaldo Carvalho de Melo 9 months, 2 weeks ago
On Tue, Apr 29, 2025 at 10:13:53PM -0300, Arnaldo Carvalho de Melo wrote:
> Adding the patch below cures it, still need to test on a Zen 5 system.
> 
> These issues were just in the regression test.

BTW, all is at the tmp.perf-tools-next branch at:

https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git

- Arnaldo
Re: [PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Ravi Bangoria 9 months, 2 weeks ago
On 30-Apr-25 6:52 AM, Arnaldo Carvalho de Melo wrote:
> On Tue, Apr 29, 2025 at 10:13:53PM -0300, Arnaldo Carvalho de Melo wrote:
>> Adding the patch below cures it, still need to test on a Zen 5 system.
>>
>> These issues were just in the regression test.
> 
> BTW, all is at the tmp.perf-tools-next branch at:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git

I tested with few simple perf mem/c2c commands and it seems to be working
fine.

Thanks,
Ravi
Re: [PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Arnaldo Carvalho de Melo 9 months, 2 weeks ago
On Wed, Apr 30, 2025 at 02:32:12PM +0530, Ravi Bangoria wrote:
> On 30-Apr-25 6:52 AM, Arnaldo Carvalho de Melo wrote:
> > On Tue, Apr 29, 2025 at 10:13:53PM -0300, Arnaldo Carvalho de Melo wrote:
> >> Adding the patch below cures it, still need to test on a Zen 5 system.

> >> These issues were just in the regression test.
 
> > BTW, all is at the tmp.perf-tools-next branch at:
 
> > https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git

> I tested with few simple perf mem/c2c commands and it seems to be working
> fine.

Thanks for checking!

While testing it I noticed that locally built kernels using O= to
separate the build files from source are ending up with:

root@number:/home/acme/git/linux# readelf -wi ../build/v6.15.0-rc4+/vmlinux | grep -m1 DW_AT_comp_dir
    <17>   DW_AT_comp_dir    : (indirect line string, offset: 0): /home/acme/git/build/v6.15.0-rc4+
root@number:/home/acme/git/linux# readelf -wi ../build/v6.15.0-rc4+/vmlinux | grep DW_AT_comp_dir | cut -d: -f4 | sort | uniq -c
     49  /home/acme/git/build/v6.15.0-rc2+
   3104  /home/acme/git/build/v6.15.0-rc4+
root@number:/home/acme/git/linux#

I reused a previous build dir, something got reused, but then tools like
annotate, objdump -dS can't find the sources.

A distraction while testing your patches :-\

- Arnaldo
Re: [PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Arnaldo Carvalho de Melo 9 months, 2 weeks ago
On Wed, Apr 30, 2025 at 10:06:33AM -0300, Arnaldo Carvalho de Melo wrote:
> On Wed, Apr 30, 2025 at 02:32:12PM +0530, Ravi Bangoria wrote:
> > On 30-Apr-25 6:52 AM, Arnaldo Carvalho de Melo wrote:
> > > On Tue, Apr 29, 2025 at 10:13:53PM -0300, Arnaldo Carvalho de Melo wrote:
> > >> Adding the patch below cures it, still need to test on a Zen 5 system.
> 
> > >> These issues were just in the regression test.
>  
> > > BTW, all is at the tmp.perf-tools-next branch at:
>  
> > > https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git
> 
> > I tested with few simple perf mem/c2c commands and it seems to be working
> > fine.
> 
> Thanks for checking!
> 

BTW:

root@number:/home/acme/git/linux# perf test -vvv ibs
 73: AMD IBS via core pmu:
--- start ---
test child forked, pid 10047
Using CPUID AuthenticAMD-26-44-0
type: 0x0, config: 0x0, fd: 3  -  Pass
type: 0x0, config: 0x1, fd: -1  -  Pass
type: 0x4, config: 0x76, fd: 3  -  Pass
type: 0x4, config: 0xc1, fd: 3  -  Pass
type: 0x4, config: 0x12, fd: -1  -  Pass
---- end(0) ----
 73: AMD IBS via core pmu                                            : Ok
root@number:/home/acme/git/linux#

- Arnaldo
Re: [PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Ravi Bangoria 9 months, 2 weeks ago
Hi Arnaldo,

> root@number:/home/acme/git/linux# perf test -vvv ibs
>  73: AMD IBS via core pmu:
> --- start ---
> test child forked, pid 10047
> Using CPUID AuthenticAMD-26-44-0
> type: 0x0, config: 0x0, fd: 3  -  Pass
> type: 0x0, config: 0x1, fd: -1  -  Pass
> type: 0x4, config: 0x76, fd: 3  -  Pass
> type: 0x4, config: 0xc1, fd: 3  -  Pass
> type: 0x4, config: 0x12, fd: -1  -  Pass
> ---- end(0) ----
>  73: AMD IBS via core pmu                                            : Ok
> root@number:/home/acme/git/linux#

It picks up both the IBS tests for me. (Is that what you mean?)

  $ sudo ./perf test ibs
   73: AMD IBS via core pmu                                            : Ok
  112: AMD IBS sample period                                           : Ok

  $ ./perf --version
  perf version 6.15.rc2.g35db59fa8ea2

Thanks,
Ravi
Re: [PATCH v4 4/4] perf test amd ibs: Add sample period unit test
Posted by Arnaldo Carvalho de Melo 9 months, 2 weeks ago
On Wed, Apr 30, 2025 at 09:37:22PM +0530, Ravi Bangoria wrote:
> Hi Arnaldo,
> 
> > root@number:/home/acme/git/linux# perf test -vvv ibs
> >  73: AMD IBS via core pmu:
> > --- start ---
> > test child forked, pid 10047
> > Using CPUID AuthenticAMD-26-44-0
> > type: 0x0, config: 0x0, fd: 3  -  Pass
> > type: 0x0, config: 0x1, fd: -1  -  Pass
> > type: 0x4, config: 0x76, fd: 3  -  Pass
> > type: 0x4, config: 0xc1, fd: 3  -  Pass
> > type: 0x4, config: 0x12, fd: -1  -  Pass
> > ---- end(0) ----
> >  73: AMD IBS via core pmu                                            : Ok
> > root@number:/home/acme/git/linux#
> 
> It picks up both the IBS tests for me. (Is that what you mean?)
> 
>   $ sudo ./perf test ibs
>    73: AMD IBS via core pmu                                            : Ok
>   112: AMD IBS sample period                                           : Ok
> 
>   $ ./perf --version
>   perf version 6.15.rc2.g35db59fa8ea2

Are there two? I probably tested it with just the first patch on your
series applied, lemme see...

The second takes quite a while to finish :)

root@number:/# perf test ibs
 73: AMD IBS via core pmu                                            : Ok
112: AMD IBS sample period                                           : Ok
root@number:/#


root@number:/# perf stat --null perf test 73
 73: AMD IBS via core pmu                                            : Ok

 Performance counter stats for 'perf test 73':

       0.018951061 seconds time elapsed

       0.004640000 seconds user
       0.005627000 seconds sys


root@number:/# perf stat --null perf test 112
112: AMD IBS sample period                                           : Ok

 Performance counter stats for 'perf test 112':

      17.888279280 seconds time elapsed

       1.539229000 seconds user
      16.325610000 seconds sys


root@number:/#

But yeah, both are passing on this 9950x3d.

- Arnaldo