Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
count_instructions
l3_bank_test
per_event_excludes
count_stcx_fail
@@ -0,0 +1,70 @@
# SPDX-License-Identifier: GPL-2.0
noarg:
$(MAKE) -C ../
TEST_GEN_PROGS := count_instructions count_stcx_fail l3_bank_test per_event_excludes
EXTRA_SOURCES := ../harness.c event.c lib.c ../utils.c
top_srcdir = ../../../../..
include ../../lib.mk
include ../flags.mk
SUB_DIRS := ebb sampling_tests event_code_tests
all: $(TEST_GEN_PROGS) $(SUB_DIRS)
$(TEST_GEN_PROGS): $(EXTRA_SOURCES)
# loop.S can only be built 64-bit
$(OUTPUT)/count_instructions: CFLAGS += -m64
$(OUTPUT)/count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES)
$(OUTPUT)/count_stcx_fail: CFLAGS += -m64
$(OUTPUT)/count_stcx_fail: loop.S $(EXTRA_SOURCES)
$(OUTPUT)/per_event_excludes: ../utils.c
$(SUB_DIRS):
BUILD_TARGET=$(OUTPUT)/$@; mkdir -p $$BUILD_TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -k -C $@ all
DEFAULT_RUN_TESTS := $(RUN_TESTS)
override define RUN_TESTS
$(DEFAULT_RUN_TESTS)
+@for TARGET in $(SUB_DIRS); do \
BUILD_TARGET=$(OUTPUT)/$$TARGET; \
$(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests; \
done;
endef
emit_tests:
for TEST in $(TEST_GEN_PROGS); do \
BASENAME_TEST=`basename $$TEST`; \
echo "$(COLLECTION):$$BASENAME_TEST"; \
done
+@for TARGET in $(SUB_DIRS); do \
BUILD_TARGET=$(OUTPUT)/$$TARGET; \
$(MAKE) OUTPUT=$$BUILD_TARGET COLLECTION=$(COLLECTION)/$$TARGET -s -C $$TARGET emit_tests; \
done;
DEFAULT_INSTALL_RULE := $(INSTALL_RULE)
override define INSTALL_RULE
$(DEFAULT_INSTALL_RULE)
+@for TARGET in $(SUB_DIRS); do \
BUILD_TARGET=$(OUTPUT)/$$TARGET; \
$(MAKE) OUTPUT=$$BUILD_TARGET INSTALL_PATH=$$INSTALL_PATH/$$TARGET -C $$TARGET install; \
done;
endef
DEFAULT_CLEAN := $(CLEAN)
override define CLEAN
$(DEFAULT_CLEAN)
$(RM) $(TEST_GEN_PROGS) $(OUTPUT)/loop.o
+@for TARGET in $(SUB_DIRS); do \
BUILD_TARGET=$(OUTPUT)/$$TARGET; \
$(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean; \
done;
endef
.PHONY: all run_tests ebb sampling_tests event_code_tests emit_tests
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <ppc-asm.h>
.text
#define ITER_SHIFT 31
FUNC_START(indirect_branch_loop)
li r3, 1
sldi r3, r3, ITER_SHIFT
1: cmpdi r3, 0
beqlr
addi r3, r3, -1
ld r4, 2f@got(%r2)
mtctr r4
bctr
.balign 32
2: b 1b
FUNC_END(indirect_branch_loop)
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/prctl.h>
#include "event.h"
#include "utils.h"
#include "lib.h"
extern void thirty_two_instruction_loop(u64 loops);
static void setup_event(struct event *e, u64 config, char *name)
{
event_init_opts(e, config, PERF_TYPE_HARDWARE, name);
e->attr.disabled = 1;
e->attr.exclude_kernel = 1;
e->attr.exclude_hv = 1;
e->attr.exclude_idle = 1;
}
static int do_count_loop(struct event *events, u64 instructions,
u64 overhead, bool report)
{
s64 difference, expected;
double percentage;
prctl(PR_TASK_PERF_EVENTS_ENABLE);
/* Run for 1M instructions */
thirty_two_instruction_loop(instructions >> 5);
prctl(PR_TASK_PERF_EVENTS_DISABLE);
event_read(&events[0]);
event_read(&events[1]);
expected = instructions + overhead;
difference = events[0].result.value - expected;
percentage = (double)difference / events[0].result.value * 100;
if (report) {
event_report(&events[0]);
event_report(&events[1]);
printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead);
printf("Expected %llu\n", expected);
printf("Actual %llu\n", events[0].result.value);
printf("Delta %lld, %f%%\n", difference, percentage);
}
event_reset(&events[0]);
event_reset(&events[1]);
if (difference < 0)
difference = -difference;
/* Tolerate a difference below 0.0001 % */
difference *= 10000 * 100;
if (difference / events[0].result.value)
return -1;
return 0;
}
/* Count how many instructions it takes to do a null loop */
static u64 determine_overhead(struct event *events)
{
u64 current, overhead;
int i;
do_count_loop(events, 0, 0, false);
overhead = events[0].result.value;
for (i = 0; i < 100; i++) {
do_count_loop(events, 0, 0, false);
current = events[0].result.value;
if (current < overhead) {
printf("Replacing overhead %llu with %llu\n", overhead, current);
overhead = current;
}
}
return overhead;
}
static int test_body(void)
{
struct event events[2];
u64 overhead;
setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, "instructions");
setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, "cycles");
if (event_open(&events[0])) {
perror("perf_event_open");
return -1;
}
if (event_open_with_group(&events[1], events[0].fd)) {
perror("perf_event_open");
return -1;
}
overhead = determine_overhead(events);
printf("Overhead of null loop: %llu instructions\n", overhead);
/* Run for 1Mi instructions */
FAIL_IF(do_count_loop(events, 1000000, overhead, true));
/* Run for 10Mi instructions */
FAIL_IF(do_count_loop(events, 10000000, overhead, true));
/* Run for 100Mi instructions */
FAIL_IF(do_count_loop(events, 100000000, overhead, true));
/* Run for 1Bi instructions */
FAIL_IF(do_count_loop(events, 1000000000, overhead, true));
/* Run for 16Bi instructions */
FAIL_IF(do_count_loop(events, 16000000000, overhead, true));
/* Run for 64Bi instructions */
FAIL_IF(do_count_loop(events, 64000000000, overhead, true));
event_close(&events[0]);
event_close(&events[1]);
return 0;
}
static int count_instructions(void)
{
return eat_cpu(test_body);
}
int main(void)
{
return test_harness(count_instructions, "count_instructions");
}
@@ -0,0 +1,161 @@
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/prctl.h>
#include "event.h"
#include "utils.h"
#include "lib.h"
extern void thirty_two_instruction_loop_with_ll_sc(u64 loops, u64 *ll_sc_target);
static void setup_event(struct event *e, u64 config, int type, char *name)
{
event_init_opts(e, config, type, name);
e->attr.disabled = 1;
e->attr.exclude_kernel = 1;
e->attr.exclude_hv = 1;
e->attr.exclude_idle = 1;
}
static int do_count_loop(struct event *events, u64 instructions,
u64 overhead, bool report)
{
s64 difference, expected;
double percentage;
u64 dummy;
prctl(PR_TASK_PERF_EVENTS_ENABLE);
/* Run for 1M instructions */
thirty_two_instruction_loop_with_ll_sc(instructions >> 5, &dummy);
prctl(PR_TASK_PERF_EVENTS_DISABLE);
event_read(&events[0]);
event_read(&events[1]);
event_read(&events[2]);
expected = instructions + overhead + (events[2].result.value * 10);
difference = events[0].result.value - expected;
percentage = (double)difference / events[0].result.value * 100;
if (report) {
printf("-----\n");
event_report(&events[0]);
event_report(&events[1]);
event_report(&events[2]);
printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead);
printf("Expected %llu\n", expected);
printf("Actual %llu\n", events[0].result.value);
printf("Delta %lld, %f%%\n", difference, percentage);
}
event_reset(&events[0]);
event_reset(&events[1]);
event_reset(&events[2]);
if (difference < 0)
difference = -difference;
/* Tolerate a difference below 0.0001 % */
difference *= 10000 * 100;
if (difference / events[0].result.value)
return -1;
return 0;
}
/* Count how many instructions it takes to do a null loop */
static u64 determine_overhead(struct event *events)
{
u64 current, overhead;
int i;
do_count_loop(events, 0, 0, false);
overhead = events[0].result.value;
for (i = 0; i < 100; i++) {
do_count_loop(events, 0, 0, false);
current = events[0].result.value;
if (current < overhead) {
printf("Replacing overhead %llu with %llu\n", overhead, current);
overhead = current;
}
}
return overhead;
}
#define PM_MRK_STCX_FAIL 0x03e158
#define PM_STCX_FAIL 0x01e058
static int test_body(void)
{
struct event events[3];
u64 overhead;
// The STCX_FAIL event we use works on Power8 or later
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, PERF_TYPE_HARDWARE, "instructions");
setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, PERF_TYPE_HARDWARE, "cycles");
setup_event(&events[2], PM_STCX_FAIL, PERF_TYPE_RAW, "stcx_fail");
if (event_open(&events[0])) {
perror("perf_event_open");
return -1;
}
if (event_open_with_group(&events[1], events[0].fd)) {
perror("perf_event_open");
return -1;
}
if (event_open_with_group(&events[2], events[0].fd)) {
perror("perf_event_open");
return -1;
}
overhead = determine_overhead(events);
printf("Overhead of null loop: %llu instructions\n", overhead);
/* Run for 1Mi instructions */
FAIL_IF(do_count_loop(events, 1000000, overhead, true));
/* Run for 10Mi instructions */
FAIL_IF(do_count_loop(events, 10000000, overhead, true));
/* Run for 100Mi instructions */
FAIL_IF(do_count_loop(events, 100000000, overhead, true));
/* Run for 1Bi instructions */
FAIL_IF(do_count_loop(events, 1000000000, overhead, true));
/* Run for 16Bi instructions */
FAIL_IF(do_count_loop(events, 16000000000, overhead, true));
event_close(&events[0]);
event_close(&events[1]);
return 0;
}
static int count_ll_sc(void)
{
return eat_cpu(test_body);
}
int main(void)
{
return test_harness(count_ll_sc, "count_ll_sc");
}
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0-only
reg_access_test
event_attributes_test
cycles_test
cycles_with_freeze_test
pmc56_overflow_test
ebb_vs_cpu_event_test
cpu_event_vs_ebb_test
cpu_event_pinned_vs_ebb_test
task_event_vs_ebb_test
task_event_pinned_vs_ebb_test
multi_ebb_procs_test
multi_counter_test
pmae_handling_test
close_clears_pmcc_test
instruction_count_test
fork_cleanup_test
ebb_on_child_test
ebb_on_willing_child_test
back_to_back_ebbs_test
lost_exception_test
no_handler_test
cycles_with_mmcr2_test
regs_access_pmccext_test
@@ -0,0 +1,38 @@
# SPDX-License-Identifier: GPL-2.0
include ../../../../../build/Build.include
noarg:
$(MAKE) -C ../../
TEST_GEN_PROGS := reg_access_test event_attributes_test cycles_test \
cycles_with_freeze_test pmc56_overflow_test \
ebb_vs_cpu_event_test cpu_event_vs_ebb_test \
cpu_event_pinned_vs_ebb_test task_event_vs_ebb_test \
task_event_pinned_vs_ebb_test multi_ebb_procs_test \
multi_counter_test pmae_handling_test \
close_clears_pmcc_test instruction_count_test \
fork_cleanup_test ebb_on_child_test \
ebb_on_willing_child_test back_to_back_ebbs_test \
lost_exception_test no_handler_test \
cycles_with_mmcr2_test regs_access_pmccext_test
top_srcdir = ../../../../../..
include ../../../lib.mk
include ../../flags.mk
# The EBB handler is 64-bit code and everything links against it
CFLAGS += -m64
TMPOUT = $(OUTPUT)/TMPDIR/
# Toolchains may build PIE by default which breaks the assembly
no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \
$(CC) -Werror $(KBUILD_CPPFLAGS) $(CC_OPTION_CFLAGS) -no-pie -x c - -o "$$TMP", -no-pie)
LDFLAGS += $(no-pie-option)
$(TEST_GEN_PROGS): ../../harness.c ../../utils.c ../event.c ../lib.c \
ebb.c ebb_handler.S trace.c busy_loop.S
$(OUTPUT)/instruction_count_test: ../loop.S
$(OUTPUT)/lost_exception_test: ../lib.c
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
#define NUMBER_OF_EBBS 50
/*
* Test that if we overflow the counter while in the EBB handler, we take
* another EBB on exiting from the handler.
*
* We do this by counting with a stupidly low sample period, causing us to
* overflow the PMU while we're still in the EBB handler, leading to another
* EBB.
*
* We get out of what would otherwise be an infinite loop by leaving the
* counter frozen once we've taken enough EBBs.
*/
static void ebb_callee(void)
{
uint64_t siar, val;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
/* Resets the PMC */
count_pmc(1, sample_period);
out:
if (ebb_state.stats.ebb_count == NUMBER_OF_EBBS)
/* Reset but leave counters frozen */
reset_ebb_with_clear_mask(MMCR0_PMAO);
else
/* Unfreezes */
reset_ebb();
/* Do some stuff to chew some cycles and pop the counter */
siar = mfspr(SPRN_SIAR);
trace_log_reg(ebb_state.trace, SPRN_SIAR, siar);
val = mfspr(SPRN_PMC1);
trace_log_reg(ebb_state.trace, SPRN_PMC1, val);
val = mfspr(SPRN_MMCR0);
trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
}
int back_to_back_ebbs(void)
{
struct event event;
SKIP_IF(!ebb_is_supported());
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
setup_ebb_handler(ebb_callee);
FAIL_IF(ebb_event_enable(&event));
sample_period = 5;
ebb_freeze_pmcs();
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
ebb_global_enable();
ebb_unfreeze_pmcs();
while (ebb_state.stats.ebb_count < NUMBER_OF_EBBS)
FAIL_IF(core_busy_loop());
ebb_global_disable();
ebb_freeze_pmcs();
dump_ebb_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count != NUMBER_OF_EBBS);
return 0;
}
int main(void)
{
return test_harness(back_to_back_ebbs, "back_to_back_ebbs");
}
@@ -0,0 +1,271 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <ppc-asm.h>
.text
FUNC_START(core_busy_loop)
stdu %r1, -168(%r1)
std r14, 160(%r1)
std r15, 152(%r1)
std r16, 144(%r1)
std r17, 136(%r1)
std r18, 128(%r1)
std r19, 120(%r1)
std r20, 112(%r1)
std r21, 104(%r1)
std r22, 96(%r1)
std r23, 88(%r1)
std r24, 80(%r1)
std r25, 72(%r1)
std r26, 64(%r1)
std r27, 56(%r1)
std r28, 48(%r1)
std r29, 40(%r1)
std r30, 32(%r1)
std r31, 24(%r1)
li r3, 0x3030
std r3, -96(%r1)
li r4, 0x4040
std r4, -104(%r1)
li r5, 0x5050
std r5, -112(%r1)
li r6, 0x6060
std r6, -120(%r1)
li r7, 0x7070
std r7, -128(%r1)
li r8, 0x0808
std r8, -136(%r1)
li r9, 0x0909
std r9, -144(%r1)
li r10, 0x1010
std r10, -152(%r1)
li r11, 0x1111
std r11, -160(%r1)
li r14, 0x1414
std r14, -168(%r1)
li r15, 0x1515
std r15, -176(%r1)
li r16, 0x1616
std r16, -184(%r1)
li r17, 0x1717
std r17, -192(%r1)
li r18, 0x1818
std r18, -200(%r1)
li r19, 0x1919
std r19, -208(%r1)
li r20, 0x2020
std r20, -216(%r1)
li r21, 0x2121
std r21, -224(%r1)
li r22, 0x2222
std r22, -232(%r1)
li r23, 0x2323
std r23, -240(%r1)
li r24, 0x2424
std r24, -248(%r1)
li r25, 0x2525
std r25, -256(%r1)
li r26, 0x2626
std r26, -264(%r1)
li r27, 0x2727
std r27, -272(%r1)
li r28, 0x2828
std r28, -280(%r1)
li r29, 0x2929
std r29, -288(%r1)
li r30, 0x3030
li r31, 0x3131
li r3, 0
0: addi r3, r3, 1
cmpwi r3, 100
blt 0b
/* Return 1 (fail) unless we get through all the checks */
li r3, 1
/* Check none of our registers have been corrupted */
cmpwi r4, 0x4040
bne 1f
cmpwi r5, 0x5050
bne 1f
cmpwi r6, 0x6060
bne 1f
cmpwi r7, 0x7070
bne 1f
cmpwi r8, 0x0808
bne 1f
cmpwi r9, 0x0909
bne 1f
cmpwi r10, 0x1010
bne 1f
cmpwi r11, 0x1111
bne 1f
cmpwi r14, 0x1414
bne 1f
cmpwi r15, 0x1515
bne 1f
cmpwi r16, 0x1616
bne 1f
cmpwi r17, 0x1717
bne 1f
cmpwi r18, 0x1818
bne 1f
cmpwi r19, 0x1919
bne 1f
cmpwi r20, 0x2020
bne 1f
cmpwi r21, 0x2121
bne 1f
cmpwi r22, 0x2222
bne 1f
cmpwi r23, 0x2323
bne 1f
cmpwi r24, 0x2424
bne 1f
cmpwi r25, 0x2525
bne 1f
cmpwi r26, 0x2626
bne 1f
cmpwi r27, 0x2727
bne 1f
cmpwi r28, 0x2828
bne 1f
cmpwi r29, 0x2929
bne 1f
cmpwi r30, 0x3030
bne 1f
cmpwi r31, 0x3131
bne 1f
/* Load junk into all our registers before we reload them from the stack. */
li r3, 0xde
li r4, 0xad
li r5, 0xbe
li r6, 0xef
li r7, 0xde
li r8, 0xad
li r9, 0xbe
li r10, 0xef
li r11, 0xde
li r14, 0xad
li r15, 0xbe
li r16, 0xef
li r17, 0xde
li r18, 0xad
li r19, 0xbe
li r20, 0xef
li r21, 0xde
li r22, 0xad
li r23, 0xbe
li r24, 0xef
li r25, 0xde
li r26, 0xad
li r27, 0xbe
li r28, 0xef
li r29, 0xdd
ld r3, -96(%r1)
cmpwi r3, 0x3030
bne 1f
ld r4, -104(%r1)
cmpwi r4, 0x4040
bne 1f
ld r5, -112(%r1)
cmpwi r5, 0x5050
bne 1f
ld r6, -120(%r1)
cmpwi r6, 0x6060
bne 1f
ld r7, -128(%r1)
cmpwi r7, 0x7070
bne 1f
ld r8, -136(%r1)
cmpwi r8, 0x0808
bne 1f
ld r9, -144(%r1)
cmpwi r9, 0x0909
bne 1f
ld r10, -152(%r1)
cmpwi r10, 0x1010
bne 1f
ld r11, -160(%r1)
cmpwi r11, 0x1111
bne 1f
ld r14, -168(%r1)
cmpwi r14, 0x1414
bne 1f
ld r15, -176(%r1)
cmpwi r15, 0x1515
bne 1f
ld r16, -184(%r1)
cmpwi r16, 0x1616
bne 1f
ld r17, -192(%r1)
cmpwi r17, 0x1717
bne 1f
ld r18, -200(%r1)
cmpwi r18, 0x1818
bne 1f
ld r19, -208(%r1)
cmpwi r19, 0x1919
bne 1f
ld r20, -216(%r1)
cmpwi r20, 0x2020
bne 1f
ld r21, -224(%r1)
cmpwi r21, 0x2121
bne 1f
ld r22, -232(%r1)
cmpwi r22, 0x2222
bne 1f
ld r23, -240(%r1)
cmpwi r23, 0x2323
bne 1f
ld r24, -248(%r1)
cmpwi r24, 0x2424
bne 1f
ld r25, -256(%r1)
cmpwi r25, 0x2525
bne 1f
ld r26, -264(%r1)
cmpwi r26, 0x2626
bne 1f
ld r27, -272(%r1)
cmpwi r27, 0x2727
bne 1f
ld r28, -280(%r1)
cmpwi r28, 0x2828
bne 1f
ld r29, -288(%r1)
cmpwi r29, 0x2929
bne 1f
/* Load 0 (success) to return */
li r3, 0
1: ld r14, 160(%r1)
ld r15, 152(%r1)
ld r16, 144(%r1)
ld r17, 136(%r1)
ld r18, 128(%r1)
ld r19, 120(%r1)
ld r20, 112(%r1)
ld r21, 104(%r1)
ld r22, 96(%r1)
ld r23, 88(%r1)
ld r24, 80(%r1)
ld r25, 72(%r1)
ld r26, 64(%r1)
ld r27, 56(%r1)
ld r28, 48(%r1)
ld r29, 40(%r1)
ld r30, 32(%r1)
ld r31, 24(%r1)
addi %r1, %r1, 168
blr
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>
#include "ebb.h"
/*
* Test that closing the EBB event clears MMCR0_PMCC, preventing further access
* by userspace to the PMU hardware.
*/
int close_clears_pmcc(void)
{
struct event event;
SKIP_IF(!ebb_is_supported());
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 1)
FAIL_IF(core_busy_loop());
ebb_global_disable();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
/* The real test is here, do we take a SIGILL when writing PMU regs now
* that we have closed the event. We expect that we will. */
FAIL_IF(catch_sigill(write_pmc1));
/* We should still be able to read EBB regs though */
mfspr(SPRN_EBBHR);
mfspr(SPRN_EBBRR);
mfspr(SPRN_BESCR);
return 0;
}
int main(void)
{
return test_harness(close_clears_pmcc, "close_clears_pmcc");
}
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests a pinned cpu event vs an EBB - in that order. The pinned cpu event
* should remain and the EBB event should fail to enable.
*/
static int setup_cpu_event(struct event *event, int cpu)
{
event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
event->attr.pinned = 1;
event->attr.exclude_kernel = 1;
event->attr.exclude_hv = 1;
event->attr.exclude_idle = 1;
SKIP_IF(require_paranoia_below(1));
FAIL_IF(event_open_with_cpu(event, cpu));
FAIL_IF(event_enable(event));
return 0;
}
int cpu_event_pinned_vs_ebb(void)
{
union pipe read_pipe, write_pipe;
struct event event;
int cpu, rc;
pid_t pid;
SKIP_IF(!ebb_is_supported());
cpu = bind_to_cpu(BIND_CPU_ANY);
FAIL_IF(cpu < 0);
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(ebb_child(write_pipe, read_pipe));
}
/* We setup the cpu event first */
rc = setup_cpu_event(&event, cpu);
if (rc) {
kill_child_and_wait(pid);
return rc;
}
/* Signal the child to install its EBB event and wait */
if (sync_with_child(read_pipe, write_pipe))
/* If it fails, wait for it to exit */
goto wait;
/* Signal the child to run */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
wait:
/* We expect it to fail to read the event */
FAIL_IF(wait_for_child(pid) != 2);
FAIL_IF(event_disable(&event));
FAIL_IF(event_read(&event));
event_report(&event);
/* The cpu event should have run */
FAIL_IF(event.result.value == 0);
FAIL_IF(event.result.enabled != event.result.running);
return 0;
}
int main(void)
{
return test_harness(cpu_event_pinned_vs_ebb, "cpu_event_pinned_vs_ebb");
}
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests a cpu event vs an EBB - in that order. The EBB should force the cpu
* event off the PMU.
*/
static int setup_cpu_event(struct event *event, int cpu)
{
event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
event->attr.exclude_kernel = 1;
event->attr.exclude_hv = 1;
event->attr.exclude_idle = 1;
SKIP_IF(require_paranoia_below(1));
FAIL_IF(event_open_with_cpu(event, cpu));
FAIL_IF(event_enable(event));
return 0;
}
int cpu_event_vs_ebb(void)
{
union pipe read_pipe, write_pipe;
struct event event;
int cpu, rc;
pid_t pid;
SKIP_IF(!ebb_is_supported());
cpu = bind_to_cpu(BIND_CPU_ANY);
FAIL_IF(cpu < 0);
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(ebb_child(write_pipe, read_pipe));
}
/* We setup the cpu event first */
rc = setup_cpu_event(&event, cpu);
if (rc) {
kill_child_and_wait(pid);
return rc;
}
/* Signal the child to install its EBB event and wait */
if (sync_with_child(read_pipe, write_pipe))
/* If it fails, wait for it to exit */
goto wait;
/* Signal the child to run */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
wait:
/* We expect the child to succeed */
FAIL_IF(wait_for_child(pid));
FAIL_IF(event_disable(&event));
FAIL_IF(event_read(&event));
event_report(&event);
/* The cpu event may have run */
return 0;
}
int main(void)
{
return test_harness(cpu_event_vs_ebb, "cpu_event_vs_ebb");
}
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
/*
* Basic test that counts user cycles and takes EBBs.
*/
int cycles(void)
{
struct event event;
SKIP_IF(!ebb_is_supported());
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 10) {
FAIL_IF(core_busy_loop());
FAIL_IF(ebb_check_mmcr0());
}
ebb_global_disable();
ebb_freeze_pmcs();
dump_ebb_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
FAIL_IF(!ebb_check_count(1, sample_period, 100));
return 0;
}
int main(void)
{
return test_harness(cycles, "cycles");
}
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "ebb.h"
/*
* Test of counting cycles while using MMCR0_FC (freeze counters) to only count
* parts of the code. This is complicated by the fact that FC is set by the
* hardware when the event overflows. We may take the EBB after we have set FC,
* so we have to be careful about whether we clear FC at the end of the EBB
* handler or not.
*/
static bool counters_frozen = false;
static int ebbs_while_frozen = 0;
static void ebb_callee(void)
{
uint64_t mask, val;
mask = MMCR0_PMAO | MMCR0_FC;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
val = mfspr(SPRN_MMCR0);
trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
if (counters_frozen) {
trace_log_string(ebb_state.trace, "frozen");
ebbs_while_frozen++;
mask &= ~MMCR0_FC;
}
count_pmc(1, sample_period);
out:
reset_ebb_with_clear_mask(mask);
}
int cycles_with_freeze(void)
{
struct event event;
uint64_t val;
bool fc_cleared;
SKIP_IF(!ebb_is_supported());
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
setup_ebb_handler(ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
fc_cleared = false;
/* Make sure we loop until we take at least one EBB */
while ((ebb_state.stats.ebb_count < 20 && !fc_cleared) ||
ebb_state.stats.ebb_count < 1)
{
counters_frozen = false;
mb();
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
FAIL_IF(core_busy_loop());
counters_frozen = true;
mb();
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
val = mfspr(SPRN_MMCR0);
if (! (val & MMCR0_FC)) {
printf("Outside of loop, FC NOT set MMCR0 0x%lx\n", val);
fc_cleared = true;
}
}
ebb_global_disable();
ebb_freeze_pmcs();
dump_ebb_state();
printf("EBBs while frozen %d\n", ebbs_while_frozen);
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
FAIL_IF(fc_cleared);
return 0;
}
int main(void)
{
return test_harness(cycles_with_freeze, "cycles_with_freeze");
}
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "ebb.h"
/*
* Test of counting cycles while manipulating the user accessible bits in MMCR2.
*/
/* We use two values because the first freezes PMC1 and so we would get no EBBs */
#define MMCR2_EXPECTED_1 0x4020100804020000UL /* (FC1P|FC2P|FC3P|FC4P|FC5P|FC6P) */
#define MMCR2_EXPECTED_2 0x0020100804020000UL /* ( FC2P|FC3P|FC4P|FC5P|FC6P) */
int cycles_with_mmcr2(void)
{
struct event event;
uint64_t val, expected[2], actual;
int i;
bool bad_mmcr2;
SKIP_IF(!ebb_is_supported());
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
/* XXX Set of MMCR2 must be after enable */
expected[0] = MMCR2_EXPECTED_1;
expected[1] = MMCR2_EXPECTED_2;
i = 0;
bad_mmcr2 = false;
actual = 0;
/* Make sure we loop until we take at least one EBB */
while ((ebb_state.stats.ebb_count < 20 && !bad_mmcr2) ||
ebb_state.stats.ebb_count < 1)
{
mtspr(SPRN_MMCR2, expected[i % 2]);
FAIL_IF(core_busy_loop());
val = mfspr(SPRN_MMCR2);
if (val != expected[i % 2]) {
bad_mmcr2 = true;
actual = val;
}
i++;
}
ebb_global_disable();
ebb_freeze_pmcs();
dump_ebb_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
if (bad_mmcr2)
printf("Bad MMCR2 value seen is 0x%lx\n", actual);
FAIL_IF(bad_mmcr2);
return 0;
}
int main(void)
{
return test_harness(cycles_with_mmcr2, "cycles_with_mmcr2");
}
@@ -0,0 +1,485 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#define _GNU_SOURCE /* For CPU_ZERO etc. */
#include <sched.h>
#include <sys/wait.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include "trace.h"
#include "ebb.h"
void (*ebb_user_func)(void);
void ebb_hook(void)
{
if (ebb_user_func)
ebb_user_func();
}
struct ebb_state ebb_state;
u64 sample_period = 0x40000000ull;
void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask)
{
u64 val;
/* 2) clear MMCR0[PMAO] - docs say BESCR[PMEO] should do this */
/* 3) set MMCR0[PMAE] - docs say BESCR[PME] should do this */
val = mfspr(SPRN_MMCR0);
mtspr(SPRN_MMCR0, (val & ~mmcr0_clear_mask) | MMCR0_PMAE);
/* 4) clear BESCR[PMEO] */
mtspr(SPRN_BESCRR, BESCR_PMEO);
/* 5) set BESCR[PME] */
mtspr(SPRN_BESCRS, BESCR_PME);
/* 6) rfebb 1 - done in our caller */
}
void reset_ebb(void)
{
reset_ebb_with_clear_mask(MMCR0_PMAO | MMCR0_FC);
}
/* Called outside of the EBB handler to check MMCR0 is sane */
int ebb_check_mmcr0(void)
{
u64 val;
val = mfspr(SPRN_MMCR0);
if ((val & (MMCR0_FC | MMCR0_PMAO)) == MMCR0_FC) {
/* It's OK if we see FC & PMAO, but not FC by itself */
printf("Outside of loop, only FC set 0x%llx\n", val);
return 1;
}
return 0;
}
bool ebb_check_count(int pmc, u64 sample_period, int fudge)
{
u64 count, upper, lower;
count = ebb_state.stats.pmc_count[PMC_INDEX(pmc)];
lower = ebb_state.stats.ebb_count * (sample_period - fudge);
if (count < lower) {
printf("PMC%d count (0x%llx) below lower limit 0x%llx (-0x%llx)\n",
pmc, count, lower, lower - count);
return false;
}
upper = ebb_state.stats.ebb_count * (sample_period + fudge);
if (count > upper) {
printf("PMC%d count (0x%llx) above upper limit 0x%llx (+0x%llx)\n",
pmc, count, upper, count - upper);
return false;
}
printf("PMC%d count (0x%llx) is between 0x%llx and 0x%llx delta +0x%llx/-0x%llx\n",
pmc, count, lower, upper, count - lower, upper - count);
return true;
}
void standard_ebb_callee(void)
{
int found, i;
u64 val;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
val = mfspr(SPRN_MMCR0);
trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
found = 0;
for (i = 1; i <= 6; i++) {
if (ebb_state.pmc_enable[PMC_INDEX(i)])
found += count_pmc(i, sample_period);
}
if (!found)
ebb_state.stats.no_overflow++;
out:
reset_ebb();
}
extern void ebb_handler(void);
void setup_ebb_handler(void (*callee)(void))
{
u64 entry;
#if defined(_CALL_ELF) && _CALL_ELF == 2
entry = (u64)ebb_handler;
#else
struct opd
{
u64 entry;
u64 toc;
} *opd;
opd = (struct opd *)ebb_handler;
entry = opd->entry;
#endif
printf("EBB Handler is at %#llx\n", entry);
ebb_user_func = callee;
/* Ensure ebb_user_func is set before we set the handler */
mb();
mtspr(SPRN_EBBHR, entry);
/* Make sure the handler is set before we return */
mb();
}
void clear_ebb_stats(void)
{
memset(&ebb_state.stats, 0, sizeof(ebb_state.stats));
}
void dump_summary_ebb_state(void)
{
printf("ebb_state:\n" \
" ebb_count = %d\n" \
" spurious = %d\n" \
" negative = %d\n" \
" no_overflow = %d\n" \
" pmc[1] count = 0x%llx\n" \
" pmc[2] count = 0x%llx\n" \
" pmc[3] count = 0x%llx\n" \
" pmc[4] count = 0x%llx\n" \
" pmc[5] count = 0x%llx\n" \
" pmc[6] count = 0x%llx\n",
ebb_state.stats.ebb_count, ebb_state.stats.spurious,
ebb_state.stats.negative, ebb_state.stats.no_overflow,
ebb_state.stats.pmc_count[0], ebb_state.stats.pmc_count[1],
ebb_state.stats.pmc_count[2], ebb_state.stats.pmc_count[3],
ebb_state.stats.pmc_count[4], ebb_state.stats.pmc_count[5]);
}
static char *decode_mmcr0(u32 value)
{
static char buf[16];
buf[0] = '\0';
if (value & (1 << 31))
strcat(buf, "FC ");
if (value & (1 << 26))
strcat(buf, "PMAE ");
if (value & (1 << 7))
strcat(buf, "PMAO ");
return buf;
}
static char *decode_bescr(u64 value)
{
static char buf[16];
buf[0] = '\0';
if (value & (1ull << 63))
strcat(buf, "GE ");
if (value & (1ull << 32))
strcat(buf, "PMAE ");
if (value & 1)
strcat(buf, "PMAO ");
return buf;
}
void dump_ebb_hw_state(void)
{
u64 bescr;
u32 mmcr0;
mmcr0 = mfspr(SPRN_MMCR0);
bescr = mfspr(SPRN_BESCR);
printf("HW state:\n" \
"MMCR0 0x%016x %s\n" \
"MMCR2 0x%016lx\n" \
"EBBHR 0x%016lx\n" \
"BESCR 0x%016llx %s\n" \
"PMC1 0x%016lx\n" \
"PMC2 0x%016lx\n" \
"PMC3 0x%016lx\n" \
"PMC4 0x%016lx\n" \
"PMC5 0x%016lx\n" \
"PMC6 0x%016lx\n" \
"SIAR 0x%016lx\n",
mmcr0, decode_mmcr0(mmcr0), mfspr(SPRN_MMCR2),
mfspr(SPRN_EBBHR), bescr, decode_bescr(bescr),
mfspr(SPRN_PMC1), mfspr(SPRN_PMC2), mfspr(SPRN_PMC3),
mfspr(SPRN_PMC4), mfspr(SPRN_PMC5), mfspr(SPRN_PMC6),
mfspr(SPRN_SIAR));
}
void dump_ebb_state(void)
{
dump_summary_ebb_state();
dump_ebb_hw_state();
trace_buffer_print(ebb_state.trace);
}
int count_pmc(int pmc, uint32_t sample_period)
{
uint32_t start_value;
u64 val;
/* 0) Read PMC */
start_value = pmc_sample_period(sample_period);
val = read_pmc(pmc);
if (val < start_value)
ebb_state.stats.negative++;
else
ebb_state.stats.pmc_count[PMC_INDEX(pmc)] += val - start_value;
trace_log_reg(ebb_state.trace, SPRN_PMC1 + pmc - 1, val);
/* 1) Reset PMC */
write_pmc(pmc, start_value);
/* Report if we overflowed */
return val >= COUNTER_OVERFLOW;
}
int ebb_event_enable(struct event *e)
{
int rc;
/* Ensure any SPR writes are ordered vs us */
mb();
rc = ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
if (rc)
return rc;
rc = event_read(e);
/* Ditto */
mb();
return rc;
}
void ebb_freeze_pmcs(void)
{
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
mb();
}
void ebb_unfreeze_pmcs(void)
{
/* Unfreeze counters */
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
mb();
}
void ebb_global_enable(void)
{
/* Enable EBBs globally and PMU EBBs */
mtspr(SPRN_BESCR, 0x8000000100000000ull);
mb();
}
void ebb_global_disable(void)
{
/* Disable EBBs & freeze counters, events are still scheduled */
mtspr(SPRN_BESCRR, BESCR_PME);
mb();
}
bool ebb_is_supported(void)
{
#ifdef PPC_FEATURE2_EBB
/* EBB requires at least POWER8 */
return have_hwcap2(PPC_FEATURE2_EBB);
#else
return false;
#endif
}
void event_ebb_init(struct event *e)
{
e->attr.config |= (1ull << 63);
}
void event_bhrb_init(struct event *e, unsigned ifm)
{
e->attr.config |= (1ull << 62) | ((u64)ifm << 60);
}
void event_leader_ebb_init(struct event *e)
{
event_ebb_init(e);
e->attr.exclusive = 1;
e->attr.pinned = 1;
}
int ebb_child(union pipe read_pipe, union pipe write_pipe)
{
struct event event;
uint64_t val;
FAIL_IF(wait_for_parent(read_pipe));
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(event_enable(&event));
if (event_read(&event)) {
/*
* Some tests expect to fail here, so don't report an error on
* this line, and return a distinguisable error code. Tell the
* parent an error happened.
*/
notify_parent_of_error(write_pipe);
return 2;
}
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
FAIL_IF(notify_parent(write_pipe));
FAIL_IF(wait_for_parent(read_pipe));
FAIL_IF(notify_parent(write_pipe));
while (ebb_state.stats.ebb_count < 20) {
FAIL_IF(core_busy_loop());
/* To try and hit SIGILL case */
val = mfspr(SPRN_MMCRA);
val |= mfspr(SPRN_MMCR2);
val |= mfspr(SPRN_MMCR0);
}
ebb_global_disable();
ebb_freeze_pmcs();
dump_ebb_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
return 0;
}
static jmp_buf setjmp_env;
static void sigill_handler(int signal)
{
printf("Took sigill\n");
longjmp(setjmp_env, 1);
}
static struct sigaction sigill_action = {
.sa_handler = sigill_handler,
};
int catch_sigill(void (*func)(void))
{
if (sigaction(SIGILL, &sigill_action, NULL)) {
perror("sigaction");
return 1;
}
if (setjmp(setjmp_env) == 0) {
func();
return 1;
}
return 0;
}
void write_pmc1(void)
{
mtspr(SPRN_PMC1, 0);
}
void write_pmc(int pmc, u64 value)
{
switch (pmc) {
case 1: mtspr(SPRN_PMC1, value); break;
case 2: mtspr(SPRN_PMC2, value); break;
case 3: mtspr(SPRN_PMC3, value); break;
case 4: mtspr(SPRN_PMC4, value); break;
case 5: mtspr(SPRN_PMC5, value); break;
case 6: mtspr(SPRN_PMC6, value); break;
}
}
u64 read_pmc(int pmc)
{
switch (pmc) {
case 1: return mfspr(SPRN_PMC1);
case 2: return mfspr(SPRN_PMC2);
case 3: return mfspr(SPRN_PMC3);
case 4: return mfspr(SPRN_PMC4);
case 5: return mfspr(SPRN_PMC5);
case 6: return mfspr(SPRN_PMC6);
}
return 0;
}
static void term_handler(int signal)
{
dump_summary_ebb_state();
dump_ebb_hw_state();
abort();
}
struct sigaction term_action = {
.sa_handler = term_handler,
};
static void __attribute__((constructor)) ebb_init(void)
{
clear_ebb_stats();
if (sigaction(SIGTERM, &term_action, NULL))
perror("sigaction");
ebb_state.trace = trace_buffer_allocate(1 * 1024 * 1024);
}
@@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#ifndef _SELFTESTS_POWERPC_PMU_EBB_EBB_H
#define _SELFTESTS_POWERPC_PMU_EBB_EBB_H
#include "../event.h"
#include "../lib.h"
#include "trace.h"
#include "reg.h"
#define PMC_INDEX(pmc) ((pmc)-1)
#define NUM_PMC_VALUES 128
struct ebb_state
{
struct {
u64 pmc_count[6];
volatile int ebb_count;
int spurious;
int negative;
int no_overflow;
} stats;
bool pmc_enable[6];
struct trace_buffer *trace;
};
extern struct ebb_state ebb_state;
#define COUNTER_OVERFLOW 0x80000000ull
static inline uint32_t pmc_sample_period(uint32_t value)
{
return COUNTER_OVERFLOW - value;
}
static inline void ebb_enable_pmc_counting(int pmc)
{
ebb_state.pmc_enable[PMC_INDEX(pmc)] = true;
}
bool ebb_check_count(int pmc, u64 sample_period, int fudge);
void event_leader_ebb_init(struct event *e);
void event_ebb_init(struct event *e);
void event_bhrb_init(struct event *e, unsigned ifm);
void setup_ebb_handler(void (*callee)(void));
void standard_ebb_callee(void);
int ebb_event_enable(struct event *e);
void ebb_global_enable(void);
void ebb_global_disable(void);
bool ebb_is_supported(void);
void ebb_freeze_pmcs(void);
void ebb_unfreeze_pmcs(void);
int count_pmc(int pmc, uint32_t sample_period);
void dump_ebb_state(void);
void dump_summary_ebb_state(void);
void dump_ebb_hw_state(void);
void clear_ebb_stats(void);
void write_pmc(int pmc, u64 value);
u64 read_pmc(int pmc);
void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask);
void reset_ebb(void);
int ebb_check_mmcr0(void);
extern u64 sample_period;
int core_busy_loop(void);
int ebb_child(union pipe read_pipe, union pipe write_pipe);
int catch_sigill(void (*func)(void));
void write_pmc1(void);
#endif /* _SELFTESTS_POWERPC_PMU_EBB_EBB_H */
@@ -0,0 +1,365 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <ppc-asm.h>
#include "reg.h"
/* ppc-asm.h defines most of the reg aliases, but not r1/r2. */
#define r1 1
#define r2 2
#define RFEBB .long 0x4c000924
/* Stack layout:
*
* ^
* User stack |
* Back chain ------+ <- r1 <-------+
* ... |
* Red zone / ABI Gap |
* ... |
* vr63 <+ |
* vr0 | |
* VSCR | |
* FSCR | |
* r31 | Save area |
* r0 | |
* XER | |
* CTR | |
* LR | |
* CCR <+ |
* ... <+ |
* LR | Caller frame |
* CCR | |
* Back chain <+ <- updated r1 --------+
*
*/
#if defined(_CALL_ELF) && _CALL_ELF == 2
#define ABIGAP 512
#else
#define ABIGAP 288
#endif
#define NR_GPR 32
#define NR_SPR 6
#define NR_VSR 64
#define SAVE_AREA ((NR_GPR + NR_SPR) * 8 + (NR_VSR * 16))
#define CALLER_FRAME 112
#define STACK_FRAME (ABIGAP + SAVE_AREA + CALLER_FRAME)
#define CCR_SAVE (CALLER_FRAME)
#define LR_SAVE (CCR_SAVE + 8)
#define CTR_SAVE (LR_SAVE + 8)
#define XER_SAVE (CTR_SAVE + 8)
#define GPR_SAVE(n) (XER_SAVE + 8 + (8 * n))
#define FSCR_SAVE (GPR_SAVE(31) + 8)
#define VSCR_SAVE (FSCR_SAVE + 8)
#define VSR_SAVE(n) (VSCR_SAVE + 8 + (16 * n))
#define SAVE_GPR(n) std n,GPR_SAVE(n)(r1)
#define REST_GPR(n) ld n,GPR_SAVE(n)(r1)
#define TRASH_GPR(n) lis n,0xaaaa
#define SAVE_VSR(n, b) li b, VSR_SAVE(n); stxvd2x n,b,r1
#define LOAD_VSR(n, b) li b, VSR_SAVE(n); lxvd2x n,b,r1
#define LOAD_REG_IMMEDIATE(reg,expr) \
lis reg,(expr)@highest; \
ori reg,reg,(expr)@higher; \
rldicr reg,reg,32,31; \
oris reg,reg,(expr)@h; \
ori reg,reg,(expr)@l;
#if defined(_CALL_ELF) && _CALL_ELF == 2
#define ENTRY_POINT(name) \
.type FUNC_NAME(name),@function; \
.globl FUNC_NAME(name); \
FUNC_NAME(name):
#define RESTORE_TOC(name) \
/* Restore our TOC pointer using our entry point */ \
LOAD_REG_IMMEDIATE(r12, name) \
0: addis r2,r12,(.TOC.-0b)@ha; \
addi r2,r2,(.TOC.-0b)@l;
#else
#define ENTRY_POINT(name) FUNC_START(name)
#define RESTORE_TOC(name) \
/* Restore our TOC pointer via our opd entry */ \
LOAD_REG_IMMEDIATE(r2, name) \
ld r2,8(r2);
#endif
.text
ENTRY_POINT(ebb_handler)
stdu r1,-STACK_FRAME(r1)
SAVE_GPR(0)
mflr r0
std r0,LR_SAVE(r1)
mfcr r0
std r0,CCR_SAVE(r1)
mfctr r0
std r0,CTR_SAVE(r1)
mfxer r0
std r0,XER_SAVE(r1)
SAVE_GPR(2)
SAVE_GPR(3)
SAVE_GPR(4)
SAVE_GPR(5)
SAVE_GPR(6)
SAVE_GPR(7)
SAVE_GPR(8)
SAVE_GPR(9)
SAVE_GPR(10)
SAVE_GPR(11)
SAVE_GPR(12)
SAVE_GPR(13)
SAVE_GPR(14)
SAVE_GPR(15)
SAVE_GPR(16)
SAVE_GPR(17)
SAVE_GPR(18)
SAVE_GPR(19)
SAVE_GPR(20)
SAVE_GPR(21)
SAVE_GPR(22)
SAVE_GPR(23)
SAVE_GPR(24)
SAVE_GPR(25)
SAVE_GPR(26)
SAVE_GPR(27)
SAVE_GPR(28)
SAVE_GPR(29)
SAVE_GPR(30)
SAVE_GPR(31)
SAVE_VSR(0, r3)
mffs f0
stfd f0, FSCR_SAVE(r1)
mfvscr f0
stfd f0, VSCR_SAVE(r1)
SAVE_VSR(1, r3)
SAVE_VSR(2, r3)
SAVE_VSR(3, r3)
SAVE_VSR(4, r3)
SAVE_VSR(5, r3)
SAVE_VSR(6, r3)
SAVE_VSR(7, r3)
SAVE_VSR(8, r3)
SAVE_VSR(9, r3)
SAVE_VSR(10, r3)
SAVE_VSR(11, r3)
SAVE_VSR(12, r3)
SAVE_VSR(13, r3)
SAVE_VSR(14, r3)
SAVE_VSR(15, r3)
SAVE_VSR(16, r3)
SAVE_VSR(17, r3)
SAVE_VSR(18, r3)
SAVE_VSR(19, r3)
SAVE_VSR(20, r3)
SAVE_VSR(21, r3)
SAVE_VSR(22, r3)
SAVE_VSR(23, r3)
SAVE_VSR(24, r3)
SAVE_VSR(25, r3)
SAVE_VSR(26, r3)
SAVE_VSR(27, r3)
SAVE_VSR(28, r3)
SAVE_VSR(29, r3)
SAVE_VSR(30, r3)
SAVE_VSR(31, r3)
SAVE_VSR(32, r3)
SAVE_VSR(33, r3)
SAVE_VSR(34, r3)
SAVE_VSR(35, r3)
SAVE_VSR(36, r3)
SAVE_VSR(37, r3)
SAVE_VSR(38, r3)
SAVE_VSR(39, r3)
SAVE_VSR(40, r3)
SAVE_VSR(41, r3)
SAVE_VSR(42, r3)
SAVE_VSR(43, r3)
SAVE_VSR(44, r3)
SAVE_VSR(45, r3)
SAVE_VSR(46, r3)
SAVE_VSR(47, r3)
SAVE_VSR(48, r3)
SAVE_VSR(49, r3)
SAVE_VSR(50, r3)
SAVE_VSR(51, r3)
SAVE_VSR(52, r3)
SAVE_VSR(53, r3)
SAVE_VSR(54, r3)
SAVE_VSR(55, r3)
SAVE_VSR(56, r3)
SAVE_VSR(57, r3)
SAVE_VSR(58, r3)
SAVE_VSR(59, r3)
SAVE_VSR(60, r3)
SAVE_VSR(61, r3)
SAVE_VSR(62, r3)
SAVE_VSR(63, r3)
TRASH_GPR(2)
TRASH_GPR(3)
TRASH_GPR(4)
TRASH_GPR(5)
TRASH_GPR(6)
TRASH_GPR(7)
TRASH_GPR(8)
TRASH_GPR(9)
TRASH_GPR(10)
TRASH_GPR(11)
TRASH_GPR(12)
TRASH_GPR(14)
TRASH_GPR(15)
TRASH_GPR(16)
TRASH_GPR(17)
TRASH_GPR(18)
TRASH_GPR(19)
TRASH_GPR(20)
TRASH_GPR(21)
TRASH_GPR(22)
TRASH_GPR(23)
TRASH_GPR(24)
TRASH_GPR(25)
TRASH_GPR(26)
TRASH_GPR(27)
TRASH_GPR(28)
TRASH_GPR(29)
TRASH_GPR(30)
TRASH_GPR(31)
RESTORE_TOC(ebb_handler)
/*
* r13 is our TLS pointer. We leave whatever value was in there when the
* EBB fired. That seems to be OK because once set the TLS pointer is not
* changed - but presumably that could change in future.
*/
bl ebb_hook
nop
/* r2 may be changed here but we don't care */
lfd f0, FSCR_SAVE(r1)
mtfsf 0xff,f0
lfd f0, VSCR_SAVE(r1)
mtvscr f0
LOAD_VSR(0, r3)
LOAD_VSR(1, r3)
LOAD_VSR(2, r3)
LOAD_VSR(3, r3)
LOAD_VSR(4, r3)
LOAD_VSR(5, r3)
LOAD_VSR(6, r3)
LOAD_VSR(7, r3)
LOAD_VSR(8, r3)
LOAD_VSR(9, r3)
LOAD_VSR(10, r3)
LOAD_VSR(11, r3)
LOAD_VSR(12, r3)
LOAD_VSR(13, r3)
LOAD_VSR(14, r3)
LOAD_VSR(15, r3)
LOAD_VSR(16, r3)
LOAD_VSR(17, r3)
LOAD_VSR(18, r3)
LOAD_VSR(19, r3)
LOAD_VSR(20, r3)
LOAD_VSR(21, r3)
LOAD_VSR(22, r3)
LOAD_VSR(23, r3)
LOAD_VSR(24, r3)
LOAD_VSR(25, r3)
LOAD_VSR(26, r3)
LOAD_VSR(27, r3)
LOAD_VSR(28, r3)
LOAD_VSR(29, r3)
LOAD_VSR(30, r3)
LOAD_VSR(31, r3)
LOAD_VSR(32, r3)
LOAD_VSR(33, r3)
LOAD_VSR(34, r3)
LOAD_VSR(35, r3)
LOAD_VSR(36, r3)
LOAD_VSR(37, r3)
LOAD_VSR(38, r3)
LOAD_VSR(39, r3)
LOAD_VSR(40, r3)
LOAD_VSR(41, r3)
LOAD_VSR(42, r3)
LOAD_VSR(43, r3)
LOAD_VSR(44, r3)
LOAD_VSR(45, r3)
LOAD_VSR(46, r3)
LOAD_VSR(47, r3)
LOAD_VSR(48, r3)
LOAD_VSR(49, r3)
LOAD_VSR(50, r3)
LOAD_VSR(51, r3)
LOAD_VSR(52, r3)
LOAD_VSR(53, r3)
LOAD_VSR(54, r3)
LOAD_VSR(55, r3)
LOAD_VSR(56, r3)
LOAD_VSR(57, r3)
LOAD_VSR(58, r3)
LOAD_VSR(59, r3)
LOAD_VSR(60, r3)
LOAD_VSR(61, r3)
LOAD_VSR(62, r3)
LOAD_VSR(63, r3)
ld r0,XER_SAVE(r1)
mtxer r0
ld r0,CTR_SAVE(r1)
mtctr r0
ld r0,LR_SAVE(r1)
mtlr r0
ld r0,CCR_SAVE(r1)
mtcr r0
REST_GPR(0)
REST_GPR(2)
REST_GPR(3)
REST_GPR(4)
REST_GPR(5)
REST_GPR(6)
REST_GPR(7)
REST_GPR(8)
REST_GPR(9)
REST_GPR(10)
REST_GPR(11)
REST_GPR(12)
REST_GPR(13)
REST_GPR(14)
REST_GPR(15)
REST_GPR(16)
REST_GPR(17)
REST_GPR(18)
REST_GPR(19)
REST_GPR(20)
REST_GPR(21)
REST_GPR(22)
REST_GPR(23)
REST_GPR(24)
REST_GPR(25)
REST_GPR(26)
REST_GPR(27)
REST_GPR(28)
REST_GPR(29)
REST_GPR(30)
REST_GPR(31)
addi r1,r1,STACK_FRAME
RFEBB
FUNC_END(ebb_handler)
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests we can setup an EBB on our child. Nothing interesting happens, because
* even though the event is enabled and running the child hasn't enabled the
* actual delivery of the EBBs.
*/
static int victim_child(union pipe read_pipe, union pipe write_pipe)
{
int i;
FAIL_IF(wait_for_parent(read_pipe));
FAIL_IF(notify_parent(write_pipe));
/* Parent creates EBB event */
FAIL_IF(wait_for_parent(read_pipe));
FAIL_IF(notify_parent(write_pipe));
/* Check the EBB is enabled by writing PMC1 */
write_pmc1();
/* EBB event is enabled here */
for (i = 0; i < 1000000; i++) ;
return 0;
}
int ebb_on_child(void)
{
union pipe read_pipe, write_pipe;
struct event event;
pid_t pid;
SKIP_IF(!ebb_is_supported());
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(victim_child(write_pipe, read_pipe));
}
FAIL_IF(sync_with_child(read_pipe, write_pipe));
/* Child is running now */
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open_with_pid(&event, pid));
FAIL_IF(ebb_event_enable(&event));
FAIL_IF(sync_with_child(read_pipe, write_pipe));
/* Child should just exit happily */
FAIL_IF(wait_for_child(pid));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(ebb_on_child, "ebb_on_child");
}
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests we can setup an EBB on our child. The child expects this and enables
* EBBs, which are then delivered to the child, even though the event is
* created by the parent.
*/
static int victim_child(union pipe read_pipe, union pipe write_pipe)
{
FAIL_IF(wait_for_parent(read_pipe));
/* Setup our EBB handler, before the EBB event is created */
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(notify_parent(write_pipe));
while (ebb_state.stats.ebb_count < 20) {
FAIL_IF(core_busy_loop());
}
ebb_global_disable();
ebb_freeze_pmcs();
dump_ebb_state();
FAIL_IF(ebb_state.stats.ebb_count == 0);
return 0;
}
/* Tests we can setup an EBB on our child - if it's expecting it */
int ebb_on_willing_child(void)
{
union pipe read_pipe, write_pipe;
struct event event;
pid_t pid;
SKIP_IF(!ebb_is_supported());
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(victim_child(write_pipe, read_pipe));
}
/* Signal the child to setup its EBB handler */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
/* Child is running now */
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open_with_pid(&event, pid));
FAIL_IF(ebb_event_enable(&event));
/* Child show now take EBBs and then exit */
FAIL_IF(wait_for_child(pid));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(ebb_on_willing_child, "ebb_on_willing_child");
}
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests an EBB vs a cpu event - in that order. The EBB should force the cpu
* event off the PMU.
*/
static int setup_cpu_event(struct event *event, int cpu)
{
event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
event->attr.exclude_kernel = 1;
event->attr.exclude_hv = 1;
event->attr.exclude_idle = 1;
SKIP_IF(require_paranoia_below(1));
FAIL_IF(event_open_with_cpu(event, cpu));
FAIL_IF(event_enable(event));
return 0;
}
int ebb_vs_cpu_event(void)
{
union pipe read_pipe, write_pipe;
struct event event;
int cpu, rc;
pid_t pid;
SKIP_IF(!ebb_is_supported());
cpu = bind_to_cpu(BIND_CPU_ANY);
FAIL_IF(cpu < 0);
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(ebb_child(write_pipe, read_pipe));
}
/* Signal the child to install its EBB event and wait */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
/* Now try to install our CPU event */
rc = setup_cpu_event(&event, cpu);
if (rc) {
kill_child_and_wait(pid);
return rc;
}
/* Signal the child to run */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
/* .. and wait for it to complete */
FAIL_IF(wait_for_child(pid));
FAIL_IF(event_disable(&event));
FAIL_IF(event_read(&event));
event_report(&event);
/* The cpu event may have run, but we don't expect 100% */
FAIL_IF(event.result.enabled >= event.result.running);
return 0;
}
int main(void)
{
return test_harness(ebb_vs_cpu_event, "ebb_vs_cpu_event");
}
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
/*
* Test various attributes of the EBB event are enforced.
*/
int event_attributes(void)
{
struct event event, leader;
SKIP_IF(!ebb_is_supported());
event_init(&event, 0x1001e);
event_leader_ebb_init(&event);
/* Expected to succeed */
FAIL_IF(event_open(&event));
event_close(&event);
event_init(&event, 0x001e); /* CYCLES - no PMC specified */
event_leader_ebb_init(&event);
/* Expected to fail, no PMC specified */
FAIL_IF(event_open(&event) == 0);
event_init(&event, 0x2001e);
event_leader_ebb_init(&event);
event.attr.exclusive = 0;
/* Expected to fail, not exclusive */
FAIL_IF(event_open(&event) == 0);
event_init(&event, 0x3001e);
event_leader_ebb_init(&event);
event.attr.freq = 1;
/* Expected to fail, sets freq */
FAIL_IF(event_open(&event) == 0);
event_init(&event, 0x4001e);
event_leader_ebb_init(&event);
event.attr.sample_period = 1;
/* Expected to fail, sets sample_period */
FAIL_IF(event_open(&event) == 0);
event_init(&event, 0x1001e);
event_leader_ebb_init(&event);
event.attr.enable_on_exec = 1;
/* Expected to fail, sets enable_on_exec */
FAIL_IF(event_open(&event) == 0);
event_init(&event, 0x1001e);
event_leader_ebb_init(&event);
event.attr.inherit = 1;
/* Expected to fail, sets inherit */
FAIL_IF(event_open(&event) == 0);
event_init(&leader, 0x1001e);
event_leader_ebb_init(&leader);
FAIL_IF(event_open(&leader));
event_init(&event, 0x20002);
event_ebb_init(&event);
/* Expected to succeed */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
event_init(&leader, 0x1001e);
event_leader_ebb_init(&leader);
FAIL_IF(event_open(&leader));
event_init(&event, 0x20002);
/* Expected to fail, event doesn't request EBB, leader does */
FAIL_IF(event_open_with_group(&event, leader.fd) == 0);
event_close(&leader);
event_init(&leader, 0x1001e);
event_leader_ebb_init(&leader);
/* Clear the EBB flag */
leader.attr.config &= ~(1ull << 63);
FAIL_IF(event_open(&leader));
event_init(&event, 0x20002);
event_ebb_init(&event);
/* Expected to fail, leader doesn't request EBB */
FAIL_IF(event_open_with_group(&event, leader.fd) == 0);
event_close(&leader);
event_init(&leader, 0x1001e);
event_leader_ebb_init(&leader);
leader.attr.exclusive = 0;
/* Expected to fail, leader isn't exclusive */
FAIL_IF(event_open(&leader) == 0);
event_init(&leader, 0x1001e);
event_leader_ebb_init(&leader);
leader.attr.pinned = 0;
/* Expected to fail, leader isn't pinned */
FAIL_IF(event_open(&leader) == 0);
event_init(&event, 0x1001e);
event_leader_ebb_init(&event);
/* Expected to fail, not a task event */
SKIP_IF(require_paranoia_below(1));
FAIL_IF(event_open_with_cpu(&event, 0) == 0);
return 0;
}
int main(void)
{
return test_harness(event_attributes, "event_attributes");
}
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <setjmp.h>
#include "ebb.h"
/*
* Test that a fork clears the PMU state of the child. eg. BESCR/EBBHR/EBBRR
* are cleared, and MMCR0_PMCC is reset, preventing the child from accessing
* the PMU.
*/
static struct event event;
static int child(void)
{
/* Even though we have EBE=0 we can still see the EBB regs */
FAIL_IF(mfspr(SPRN_BESCR) != 0);
FAIL_IF(mfspr(SPRN_EBBHR) != 0);
FAIL_IF(mfspr(SPRN_EBBRR) != 0);
FAIL_IF(catch_sigill(write_pmc1));
/* We can still read from the event, though it is on our parent */
FAIL_IF(event_read(&event));
return 0;
}
/* Tests that fork clears EBB state */
int fork_cleanup(void)
{
pid_t pid;
SKIP_IF(!ebb_is_supported());
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_MMCR0, MMCR0_FC);
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
/* Don't need to actually take any EBBs */
pid = fork();
if (pid == 0)
exit(child());
/* Child does the actual testing */
FAIL_IF(wait_for_child(pid));
/* After fork */
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(fork_cleanup, "fork_cleanup");
}
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/prctl.h>
#include "ebb.h"
/*
* Run a calibrated instruction loop and count instructions executed using
* EBBs. Make sure the counts look right.
*/
extern void thirty_two_instruction_loop(uint64_t loops);
static bool counters_frozen = true;
static int do_count_loop(struct event *event, uint64_t instructions,
uint64_t overhead, bool report)
{
int64_t difference, expected;
double percentage;
clear_ebb_stats();
counters_frozen = false;
mb();
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
thirty_two_instruction_loop(instructions >> 5);
counters_frozen = true;
mb();
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
count_pmc(4, sample_period);
event->result.value = ebb_state.stats.pmc_count[4-1];
expected = instructions + overhead;
difference = event->result.value - expected;
percentage = (double)difference / event->result.value * 100;
if (report) {
printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead);
printf("Expected %lu\n", expected);
printf("Actual %llu\n", event->result.value);
printf("Delta %ld, %f%%\n", difference, percentage);
printf("Took %d EBBs\n", ebb_state.stats.ebb_count);
}
if (difference < 0)
difference = -difference;
/* Tolerate a difference of up to 0.0001 % */
difference *= 10000 * 100;
if (difference / event->result.value)
return -1;
return 0;
}
/* Count how many instructions it takes to do a null loop */
static uint64_t determine_overhead(struct event *event)
{
uint64_t current, overhead;
int i;
do_count_loop(event, 0, 0, false);
overhead = event->result.value;
for (i = 0; i < 100; i++) {
do_count_loop(event, 0, 0, false);
current = event->result.value;
if (current < overhead) {
printf("Replacing overhead %lu with %lu\n", overhead, current);
overhead = current;
}
}
return overhead;
}
static void pmc4_ebb_callee(void)
{
uint64_t val;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
count_pmc(4, sample_period);
out:
if (counters_frozen)
reset_ebb_with_clear_mask(MMCR0_PMAO);
else
reset_ebb();
}
int instruction_count(void)
{
struct event event;
uint64_t overhead;
SKIP_IF(!ebb_is_supported());
event_init_named(&event, 0x400FA, "PM_RUN_INST_CMPL");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
FAIL_IF(ebb_event_enable(&event));
sample_period = COUNTER_OVERFLOW;
setup_ebb_handler(pmc4_ebb_callee);
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
ebb_global_enable();
overhead = determine_overhead(&event);
printf("Overhead of null loop: %lu instructions\n", overhead);
/* Run for 1M instructions */
FAIL_IF(do_count_loop(&event, 0x100000, overhead, true));
/* Run for 10M instructions */
FAIL_IF(do_count_loop(&event, 0xa00000, overhead, true));
/* Run for 100M instructions */
FAIL_IF(do_count_loop(&event, 0x6400000, overhead, true));
/* Run for 1G instructions */
FAIL_IF(do_count_loop(&event, 0x40000000, overhead, true));
/* Run for 16G instructions */
FAIL_IF(do_count_loop(&event, 0x400000000, overhead, true));
/* Run for 64G instructions */
FAIL_IF(do_count_loop(&event, 0x1000000000, overhead, true));
/* Run for 128G instructions */
FAIL_IF(do_count_loop(&event, 0x2000000000, overhead, true));
ebb_global_disable();
event_close(&event);
printf("Finished OK\n");
return 0;
}
int main(void)
{
test_harness_set_timeout(300);
return test_harness(instruction_count, "instruction_count");
}
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include "ebb.h"
/*
* Test that tries to trigger CPU_FTR_PMAO_BUG. Which is a hardware defect
* where an exception triggers but we context switch before it is delivered and
* lose the exception.
*/
static int test_body(void)
{
int i, orig_period, max_period;
struct event event;
SKIP_IF(!ebb_is_supported());
/* We use PMC4 to make sure the kernel switches all counters correctly */
event_init_named(&event, 0x40002, "instructions");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(4);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
/*
* We want a low sample period, but we also want to get out of the EBB
* handler without tripping up again.
*
* This value picked after much experimentation.
*/
orig_period = max_period = sample_period = 400;
mtspr(SPRN_PMC4, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 1000000) {
/*
* We are trying to get the EBB exception to race exactly with
* us entering the kernel to do the syscall. We then need the
* kernel to decide our timeslice is up and context switch to
* the other thread. When we come back our EBB will have been
* lost and we'll spin in this while loop forever.
*/
for (i = 0; i < 100000; i++)
sched_yield();
/* Change the sample period slightly to try and hit the race */
if (sample_period >= (orig_period + 200))
sample_period = orig_period;
else
sample_period++;
if (sample_period > max_period)
max_period = sample_period;
}
ebb_freeze_pmcs();
ebb_global_disable();
mtspr(SPRN_PMC4, 0xdead);
dump_summary_ebb_state();
dump_ebb_hw_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
/* We vary our sample period so we need extra fudge here */
FAIL_IF(!ebb_check_count(4, orig_period, 2 * (max_period - orig_period)));
return 0;
}
static int lost_exception(void)
{
return eat_cpu(test_body);
}
int main(void)
{
test_harness_set_timeout(300);
return test_harness(lost_exception, "lost_exception");
}
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "ebb.h"
/*
* Test counting multiple events using EBBs.
*/
int multi_counter(void)
{
struct event events[6];
int i, group_fd;
SKIP_IF(!ebb_is_supported());
event_init_named(&events[0], 0x1001C, "PM_CMPLU_STALL_THRD");
event_init_named(&events[1], 0x2D016, "PM_CMPLU_STALL_FXU");
event_init_named(&events[2], 0x30006, "PM_CMPLU_STALL_OTHER_CMPL");
event_init_named(&events[3], 0x4000A, "PM_CMPLU_STALL");
event_init_named(&events[4], 0x600f4, "PM_RUN_CYC");
event_init_named(&events[5], 0x500fa, "PM_RUN_INST_CMPL");
event_leader_ebb_init(&events[0]);
for (i = 1; i < 6; i++)
event_ebb_init(&events[i]);
group_fd = -1;
for (i = 0; i < 6; i++) {
events[i].attr.exclude_kernel = 1;
events[i].attr.exclude_hv = 1;
events[i].attr.exclude_idle = 1;
FAIL_IF(event_open_with_group(&events[i], group_fd));
if (group_fd == -1)
group_fd = events[0].fd;
}
ebb_enable_pmc_counting(1);
ebb_enable_pmc_counting(2);
ebb_enable_pmc_counting(3);
ebb_enable_pmc_counting(4);
ebb_enable_pmc_counting(5);
ebb_enable_pmc_counting(6);
setup_ebb_handler(standard_ebb_callee);
FAIL_IF(ioctl(events[0].fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP));
FAIL_IF(event_read(&events[0]));
ebb_global_enable();
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
mtspr(SPRN_PMC2, pmc_sample_period(sample_period));
mtspr(SPRN_PMC3, pmc_sample_period(sample_period));
mtspr(SPRN_PMC4, pmc_sample_period(sample_period));
mtspr(SPRN_PMC5, pmc_sample_period(sample_period));
mtspr(SPRN_PMC6, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 50) {
FAIL_IF(core_busy_loop());
FAIL_IF(ebb_check_mmcr0());
}
ebb_global_disable();
ebb_freeze_pmcs();
dump_ebb_state();
for (i = 0; i < 6; i++)
event_close(&events[i]);
FAIL_IF(ebb_state.stats.ebb_count == 0);
return 0;
}
int main(void)
{
return test_harness(multi_counter, "multi_counter");
}
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "ebb.h"
/*
* Test running multiple EBB using processes at once on a single CPU. They
* should all run happily without interfering with each other.
*/
static bool child_should_exit;
static void sigint_handler(int signal)
{
child_should_exit = true;
}
struct sigaction sigint_action = {
.sa_handler = sigint_handler,
};
static int cycles_child(void)
{
struct event event;
if (sigaction(SIGINT, &sigint_action, NULL)) {
perror("sigaction");
return 1;
}
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
while (!child_should_exit) {
FAIL_IF(core_busy_loop());
FAIL_IF(ebb_check_mmcr0());
}
ebb_global_disable();
ebb_freeze_pmcs();
dump_summary_ebb_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
return 0;
}
#define NR_CHILDREN 4
int multi_ebb_procs(void)
{
pid_t pids[NR_CHILDREN];
int rc, i;
SKIP_IF(!ebb_is_supported());
FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
for (i = 0; i < NR_CHILDREN; i++) {
pids[i] = fork();
if (pids[i] == 0)
exit(cycles_child());
}
/* Have them all run for "a while" */
sleep(10);
rc = 0;
for (i = 0; i < NR_CHILDREN; i++) {
/* Tell them to stop */
kill(pids[i], SIGINT);
/* And wait */
rc |= wait_for_child(pids[i]);
}
return rc;
}
int main(void)
{
return test_harness(multi_ebb_procs, "multi_ebb_procs");
}
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>
#include "ebb.h"
/* Test that things work sanely if we have no handler */
static int no_handler_test(void)
{
struct event event;
u64 val;
int i;
SKIP_IF(!ebb_is_supported());
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
FAIL_IF(ebb_event_enable(&event));
val = mfspr(SPRN_EBBHR);
FAIL_IF(val != 0);
/* Make sure it overflows quickly */
sample_period = 1000;
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
/* Spin to make sure the event has time to overflow */
for (i = 0; i < 1000; i++)
mb();
dump_ebb_state();
/* We expect to see the PMU frozen & PMAO set */
val = mfspr(SPRN_MMCR0);
FAIL_IF(val != 0x0000000080000080);
event_close(&event);
/* The real test is that we never took an EBB at 0x0 */
return 0;
}
int main(void)
{
return test_harness(no_handler_test,"no_handler_test");
}
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
/*
* Test that the kernel properly handles PMAE across context switches.
*
* We test this by calling into the kernel inside our EBB handler, where PMAE
* is clear. A cpu eater companion thread is running on the same CPU as us to
* encourage the scheduler to switch us.
*
* The kernel must make sure that when it context switches us back in, it
* honours the fact that we had PMAE clear.
*
* Observed to hit the failing case on the first EBB with a broken kernel.
*/
static bool mmcr0_mismatch;
static uint64_t before, after;
static void syscall_ebb_callee(void)
{
uint64_t val;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
count_pmc(1, sample_period);
before = mfspr(SPRN_MMCR0);
/* Try and get ourselves scheduled, to force a PMU context switch */
sched_yield();
after = mfspr(SPRN_MMCR0);
if (before != after)
mmcr0_mismatch = true;
out:
reset_ebb();
}
static int test_body(void)
{
struct event event;
SKIP_IF(!ebb_is_supported());
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
setup_ebb_handler(syscall_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 20 && !mmcr0_mismatch)
FAIL_IF(core_busy_loop());
ebb_global_disable();
ebb_freeze_pmcs();
dump_ebb_state();
if (mmcr0_mismatch)
printf("Saw MMCR0 before 0x%lx after 0x%lx\n", before, after);
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
FAIL_IF(mmcr0_mismatch);
return 0;
}
int pmae_handling(void)
{
return eat_cpu(test_body);
}
int main(void)
{
return test_harness(pmae_handling, "pmae_handling");
}
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
/*
* Test that PMC5 & 6 are frozen (ie. don't overflow) when they are not being
* used. Tests the MMCR0_FC56 logic in the kernel.
*/
static int pmc56_overflowed;
static void ebb_callee(void)
{
uint64_t val;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
count_pmc(2, sample_period);
val = mfspr(SPRN_PMC5);
if (val >= COUNTER_OVERFLOW)
pmc56_overflowed++;
count_pmc(5, COUNTER_OVERFLOW);
val = mfspr(SPRN_PMC6);
if (val >= COUNTER_OVERFLOW)
pmc56_overflowed++;
count_pmc(6, COUNTER_OVERFLOW);
out:
reset_ebb();
}
int pmc56_overflow(void)
{
struct event event;
SKIP_IF(!ebb_is_supported());
/* Use PMC2 so we set PMCjCE, which enables PMC5/6 */
event_init(&event, 0x2001e);
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
setup_ebb_handler(ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC2, pmc_sample_period(sample_period));
mtspr(SPRN_PMC5, 0);
mtspr(SPRN_PMC6, 0);
while (ebb_state.stats.ebb_count < 10)
FAIL_IF(core_busy_loop());
ebb_global_disable();
ebb_freeze_pmcs();
dump_ebb_state();
printf("PMC5/6 overflow %d\n", pmc56_overflowed);
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0 || pmc56_overflowed != 0);
return 0;
}
int main(void)
{
return test_harness(pmc56_overflow, "pmc56_overflow");
}
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
/*
* Test basic access to the EBB regs, they should be user accessible with no
* kernel interaction required.
*/
int reg_access(void)
{
uint64_t val, expected;
SKIP_IF(!ebb_is_supported());
expected = 0x8000000100000000ull;
mtspr(SPRN_BESCR, expected);
val = mfspr(SPRN_BESCR);
FAIL_IF(val != expected);
expected = 0x0000000001000000ull;
mtspr(SPRN_EBBHR, expected);
val = mfspr(SPRN_EBBHR);
FAIL_IF(val != expected);
return 0;
}
int main(void)
{
return test_harness(reg_access, "reg_access");
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2021, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>
#include "ebb.h"
/*
* Test that closing the EBB event clears MMCR0_PMCC and
* sets MMCR0_PMCCEXT preventing further read access to the
* group B PMU registers.
*/
static int regs_access_pmccext(void)
{
struct event event;
SKIP_IF(!ebb_is_supported());
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 1)
FAIL_IF(core_busy_loop());
ebb_global_disable();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
/*
* For ISA v3.1, verify the test takes a SIGILL when reading
* PMU regs after the event is closed. With the control bit
* in MMCR0 (PMCCEXT) restricting access to group B PMU regs,
* sigill is expected.
*/
if (have_hwcap2(PPC_FEATURE2_ARCH_3_1))
FAIL_IF(catch_sigill(dump_ebb_state));
else
dump_ebb_state();
return 0;
}
int main(void)
{
return test_harness(regs_access_pmccext, "regs_access_pmccext");
}
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests a pinned per-task event vs an EBB - in that order. The pinned per-task
* event should prevent the EBB event from being enabled.
*/
static int setup_child_event(struct event *event, pid_t child_pid)
{
event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
event->attr.pinned = 1;
event->attr.exclude_kernel = 1;
event->attr.exclude_hv = 1;
event->attr.exclude_idle = 1;
FAIL_IF(event_open_with_pid(event, child_pid));
FAIL_IF(event_enable(event));
return 0;
}
int task_event_pinned_vs_ebb(void)
{
union pipe read_pipe, write_pipe;
struct event event;
pid_t pid;
int rc;
SKIP_IF(!ebb_is_supported());
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(ebb_child(write_pipe, read_pipe));
}
/* We setup the task event first */
rc = setup_child_event(&event, pid);
if (rc) {
kill_child_and_wait(pid);
return rc;
}
/* Signal the child to install its EBB event and wait */
if (sync_with_child(read_pipe, write_pipe))
/* If it fails, wait for it to exit */
goto wait;
/* Signal the child to run */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
wait:
/* We expect it to fail to read the event */
FAIL_IF(wait_for_child(pid) != 2);
FAIL_IF(event_disable(&event));
FAIL_IF(event_read(&event));
event_report(&event);
FAIL_IF(event.result.value == 0);
/*
* For reasons I don't understand enabled is usually just slightly
* lower than running. Would be good to confirm why.
*/
FAIL_IF(event.result.enabled == 0);
FAIL_IF(event.result.running == 0);
return 0;
}
int main(void)
{
return test_harness(task_event_pinned_vs_ebb, "task_event_pinned_vs_ebb");
}
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests a per-task event vs an EBB - in that order. The EBB should push the
* per-task event off the PMU.
*/
static int setup_child_event(struct event *event, pid_t child_pid)
{
event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
event->attr.exclude_kernel = 1;
event->attr.exclude_hv = 1;
event->attr.exclude_idle = 1;
FAIL_IF(event_open_with_pid(event, child_pid));
FAIL_IF(event_enable(event));
return 0;
}
int task_event_vs_ebb(void)
{
union pipe read_pipe, write_pipe;
struct event event;
pid_t pid;
int rc;
SKIP_IF(!ebb_is_supported());
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(ebb_child(write_pipe, read_pipe));
}
/* We setup the task event first */
rc = setup_child_event(&event, pid);
if (rc) {
kill_child_and_wait(pid);
return rc;
}
/* Signal the child to install its EBB event and wait */
if (sync_with_child(read_pipe, write_pipe))
/* If it fails, wait for it to exit */
goto wait;
/* Signal the child to run */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
wait:
/* The EBB event should push the task event off so the child should succeed */
FAIL_IF(wait_for_child(pid));
FAIL_IF(event_disable(&event));
FAIL_IF(event_read(&event));
event_report(&event);
/* The task event may have run, or not so we can't assert anything about it */
return 0;
}
int main(void)
{
return test_harness(task_event_vs_ebb, "task_event_vs_ebb");
}
@@ -0,0 +1,300 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include "trace.h"
struct trace_buffer *trace_buffer_allocate(u64 size)
{
struct trace_buffer *tb;
if (size < sizeof(*tb)) {
fprintf(stderr, "Error: trace buffer too small\n");
return NULL;
}
tb = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (tb == MAP_FAILED) {
perror("mmap");
return NULL;
}
tb->size = size;
tb->tail = tb->data;
tb->overflow = false;
return tb;
}
static bool trace_check_bounds(struct trace_buffer *tb, void *p)
{
return p < ((void *)tb + tb->size);
}
static bool trace_check_alloc(struct trace_buffer *tb, void *p)
{
/*
* If we ever overflowed don't allow any more input. This prevents us
* from dropping a large item and then later logging a small one. The
* buffer should just stop when overflow happened, not be patchy. If
* you're overflowing, make your buffer bigger.
*/
if (tb->overflow)
return false;
if (!trace_check_bounds(tb, p)) {
tb->overflow = true;
return false;
}
return true;
}
static void *trace_alloc(struct trace_buffer *tb, int bytes)
{
void *p, *newtail;
p = tb->tail;
newtail = tb->tail + bytes;
if (!trace_check_alloc(tb, newtail))
return NULL;
tb->tail = newtail;
return p;
}
static struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size)
{
struct trace_entry *e;
e = trace_alloc(tb, sizeof(*e) + payload_size);
if (e)
e->length = payload_size;
return e;
}
int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value)
{
struct trace_entry *e;
u64 *p;
e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value));
if (!e)
return -ENOSPC;
e->type = TRACE_TYPE_REG;
p = (u64 *)e->data;
*p++ = reg;
*p++ = value;
return 0;
}
int trace_log_counter(struct trace_buffer *tb, u64 value)
{
struct trace_entry *e;
u64 *p;
e = trace_alloc_entry(tb, sizeof(value));
if (!e)
return -ENOSPC;
e->type = TRACE_TYPE_COUNTER;
p = (u64 *)e->data;
*p++ = value;
return 0;
}
int trace_log_string(struct trace_buffer *tb, char *str)
{
struct trace_entry *e;
char *p;
int len;
len = strlen(str);
/* We NULL terminate to make printing easier */
e = trace_alloc_entry(tb, len + 1);
if (!e)
return -ENOSPC;
e->type = TRACE_TYPE_STRING;
p = (char *)e->data;
memcpy(p, str, len);
p += len;
*p = '\0';
return 0;
}
int trace_log_indent(struct trace_buffer *tb)
{
struct trace_entry *e;
e = trace_alloc_entry(tb, 0);
if (!e)
return -ENOSPC;
e->type = TRACE_TYPE_INDENT;
return 0;
}
int trace_log_outdent(struct trace_buffer *tb)
{
struct trace_entry *e;
e = trace_alloc_entry(tb, 0);
if (!e)
return -ENOSPC;
e->type = TRACE_TYPE_OUTDENT;
return 0;
}
static void trace_print_header(int seq, int prefix)
{
printf("%*s[%d]: ", prefix, "", seq);
}
static char *trace_decode_reg(int reg)
{
switch (reg) {
case 769: return "SPRN_MMCR2"; break;
case 770: return "SPRN_MMCRA"; break;
case 779: return "SPRN_MMCR0"; break;
case 804: return "SPRN_EBBHR"; break;
case 805: return "SPRN_EBBRR"; break;
case 806: return "SPRN_BESCR"; break;
case 800: return "SPRN_BESCRS"; break;
case 801: return "SPRN_BESCRSU"; break;
case 802: return "SPRN_BESCRR"; break;
case 803: return "SPRN_BESCRRU"; break;
case 771: return "SPRN_PMC1"; break;
case 772: return "SPRN_PMC2"; break;
case 773: return "SPRN_PMC3"; break;
case 774: return "SPRN_PMC4"; break;
case 775: return "SPRN_PMC5"; break;
case 776: return "SPRN_PMC6"; break;
case 780: return "SPRN_SIAR"; break;
case 781: return "SPRN_SDAR"; break;
case 768: return "SPRN_SIER"; break;
}
return NULL;
}
static void trace_print_reg(struct trace_entry *e)
{
u64 *p, *reg, *value;
char *name;
p = (u64 *)e->data;
reg = p++;
value = p;
name = trace_decode_reg(*reg);
if (name)
printf("register %-10s = 0x%016llx\n", name, *value);
else
printf("register %lld = 0x%016llx\n", *reg, *value);
}
static void trace_print_counter(struct trace_entry *e)
{
u64 *value;
value = (u64 *)e->data;
printf("counter = %lld\n", *value);
}
static void trace_print_string(struct trace_entry *e)
{
char *str;
str = (char *)e->data;
puts(str);
}
#define BASE_PREFIX 2
#define PREFIX_DELTA 8
static void trace_print_entry(struct trace_entry *e, int seq, int *prefix)
{
switch (e->type) {
case TRACE_TYPE_REG:
trace_print_header(seq, *prefix);
trace_print_reg(e);
break;
case TRACE_TYPE_COUNTER:
trace_print_header(seq, *prefix);
trace_print_counter(e);
break;
case TRACE_TYPE_STRING:
trace_print_header(seq, *prefix);
trace_print_string(e);
break;
case TRACE_TYPE_INDENT:
trace_print_header(seq, *prefix);
puts("{");
*prefix += PREFIX_DELTA;
break;
case TRACE_TYPE_OUTDENT:
*prefix -= PREFIX_DELTA;
if (*prefix < BASE_PREFIX)
*prefix = BASE_PREFIX;
trace_print_header(seq, *prefix);
puts("}");
break;
default:
trace_print_header(seq, *prefix);
printf("entry @ %p type %d\n", e, e->type);
break;
}
}
void trace_buffer_print(struct trace_buffer *tb)
{
struct trace_entry *e;
int i, prefix;
void *p;
printf("Trace buffer dump:\n");
printf(" address %p \n", tb);
printf(" tail %p\n", tb->tail);
printf(" size %llu\n", tb->size);
printf(" overflow %s\n", tb->overflow ? "TRUE" : "false");
printf(" Content:\n");
p = tb->data;
i = 0;
prefix = BASE_PREFIX;
while (trace_check_bounds(tb, p) && p < tb->tail) {
e = p;
trace_print_entry(e, i, &prefix);
i++;
p = (void *)e + sizeof(*e) + e->length;
}
}
void trace_print_location(struct trace_buffer *tb)
{
printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb);
}
@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#ifndef _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
#define _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
#include "utils.h"
#define TRACE_TYPE_REG 1
#define TRACE_TYPE_COUNTER 2
#define TRACE_TYPE_STRING 3
#define TRACE_TYPE_INDENT 4
#define TRACE_TYPE_OUTDENT 5
struct trace_entry
{
u8 type;
u8 length;
u8 data[];
};
struct trace_buffer
{
u64 size;
bool overflow;
void *tail;
u8 data[];
};
struct trace_buffer *trace_buffer_allocate(u64 size);
int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value);
int trace_log_counter(struct trace_buffer *tb, u64 value);
int trace_log_string(struct trace_buffer *tb, char *str);
int trace_log_indent(struct trace_buffer *tb);
int trace_log_outdent(struct trace_buffer *tb);
void trace_buffer_print(struct trace_buffer *tb);
void trace_print_location(struct trace_buffer *tb);
#endif /* _SELFTESTS_POWERPC_PMU_EBB_TRACE_H */
@@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/ioctl.h>
#include "event.h"
int 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 __event_init_opts(struct event *e, u64 config,
int type, char *name, bool sampling)
{
memset(e, 0, sizeof(*e));
e->name = name;
e->attr.type = type;
e->attr.config = config;
e->attr.size = sizeof(e->attr);
/* This has to match the structure layout in the header */
e->attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | \
PERF_FORMAT_TOTAL_TIME_RUNNING;
if (sampling) {
e->attr.sample_period = 1000;
e->attr.sample_type = PERF_SAMPLE_REGS_INTR;
e->attr.disabled = 1;
}
}
void event_init_opts(struct event *e, u64 config, int type, char *name)
{
__event_init_opts(e, config, type, name, false);
}
void event_init_named(struct event *e, u64 config, char *name)
{
event_init_opts(e, config, PERF_TYPE_RAW, name);
}
void event_init(struct event *e, u64 config)
{
event_init_opts(e, config, PERF_TYPE_RAW, "event");
}
void event_init_sampling(struct event *e, u64 config)
{
__event_init_opts(e, config, PERF_TYPE_RAW, "event", true);
}
#define PERF_CURRENT_PID 0
#define PERF_NO_PID -1
#define PERF_NO_CPU -1
#define PERF_NO_GROUP -1
int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd)
{
e->fd = perf_event_open(&e->attr, pid, cpu, group_fd, 0);
if (e->fd == -1) {
perror("perf_event_open");
return -1;
}
return 0;
}
int event_open_with_group(struct event *e, int group_fd)
{
return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, group_fd);
}
int event_open_with_pid(struct event *e, pid_t pid)
{
return event_open_with_options(e, pid, PERF_NO_CPU, PERF_NO_GROUP);
}
int event_open_with_cpu(struct event *e, int cpu)
{
return event_open_with_options(e, PERF_NO_PID, cpu, PERF_NO_GROUP);
}
int event_open(struct event *e)
{
return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, PERF_NO_GROUP);
}
void event_close(struct event *e)
{
close(e->fd);
}
int event_enable(struct event *e)
{
return ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
}
int event_disable(struct event *e)
{
return ioctl(e->fd, PERF_EVENT_IOC_DISABLE);
}
int event_reset(struct event *e)
{
return ioctl(e->fd, PERF_EVENT_IOC_RESET);
}
int event_read(struct event *e)
{
int rc;
rc = read(e->fd, &e->result, sizeof(e->result));
if (rc != sizeof(e->result)) {
fprintf(stderr, "read error on event %p!\n", e);
return -1;
}
return 0;
}
void event_report_justified(struct event *e, int name_width, int result_width)
{
printf("%*s: result %*llu ", name_width, e->name, result_width,
e->result.value);
if (e->result.running == e->result.enabled)
printf("running/enabled %llu\n", e->result.running);
else
printf("running %llu enabled %llu\n", e->result.running,
e->result.enabled);
}
void event_report(struct event *e)
{
event_report_justified(e, 0, 0);
}
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
*/
#ifndef _SELFTESTS_POWERPC_PMU_EVENT_H
#define _SELFTESTS_POWERPC_PMU_EVENT_H
#include <unistd.h>
#include <linux/perf_event.h>
#include "utils.h"
struct event {
struct perf_event_attr attr;
char *name;
int fd;
/* This must match the read_format we use */
struct {
u64 value;
u64 running;
u64 enabled;
} result;
/*
* mmap buffer used while recording sample.
* Accessed as "struct perf_event_mmap_page"
*/
void *mmap_buffer;
};
void event_init(struct event *e, u64 config);
void event_init_named(struct event *e, u64 config, char *name);
void event_init_opts(struct event *e, u64 config, int type, char *name);
void event_init_sampling(struct event *e, u64 config);
int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd);
int event_open_with_group(struct event *e, int group_fd);
int event_open_with_pid(struct event *e, pid_t pid);
int event_open_with_cpu(struct event *e, int cpu);
int event_open(struct event *e);
void event_close(struct event *e);
int event_enable(struct event *e);
int event_disable(struct event *e);
int event_reset(struct event *e);
int event_read(struct event *e);
void event_report_justified(struct event *e, int name_width, int result_width);
void event_report(struct event *e);
#endif /* _SELFTESTS_POWERPC_PMU_EVENT_H */
@@ -0,0 +1,20 @@
blacklisted_events_test
event_alternatives_tests_p10
event_alternatives_tests_p9
generic_events_valid_test
group_constraint_cache_test
group_constraint_l2l3_sel_test
group_constraint_mmcra_sample_test
group_constraint_pmc56_test
group_constraint_pmc_count_test
group_constraint_radix_scope_qual_test
group_constraint_repeat_test
group_constraint_thresh_cmp_test
group_constraint_thresh_ctl_test
group_constraint_thresh_sel_test
group_constraint_unit_test
group_pmc56_exclude_constraints_test
hw_cache_event_type_test
invalid_event_code_test
reserved_bits_mmcra_sample_elig_mode_test
reserved_bits_mmcra_thresh_ctl_test
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
TEST_GEN_PROGS := group_constraint_pmc56_test group_pmc56_exclude_constraints_test group_constraint_pmc_count_test \
group_constraint_repeat_test group_constraint_radix_scope_qual_test reserved_bits_mmcra_sample_elig_mode_test \
group_constraint_mmcra_sample_test invalid_event_code_test reserved_bits_mmcra_thresh_ctl_test \
blacklisted_events_test event_alternatives_tests_p9 event_alternatives_tests_p10 generic_events_valid_test \
group_constraint_l2l3_sel_test group_constraint_cache_test group_constraint_thresh_cmp_test \
group_constraint_unit_test group_constraint_thresh_ctl_test group_constraint_thresh_sel_test \
hw_cache_event_type_test
top_srcdir = ../../../../../..
include ../../../lib.mk
include ../../flags.mk
CFLAGS += -m64
$(TEST_GEN_PROGS): ../../harness.c ../../utils.c ../event.c ../lib.c ../sampling_tests/misc.h ../sampling_tests/misc.c
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <sys/prctl.h>
#include <limits.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
#define PM_DTLB_MISS_16G 0x1c058
#define PM_DERAT_MISS_2M 0x1c05a
#define PM_DTLB_MISS_2M 0x1c05c
#define PM_MRK_DTLB_MISS_1G 0x1d15c
#define PM_DTLB_MISS_4K 0x2c056
#define PM_DERAT_MISS_1G 0x2c05a
#define PM_MRK_DERAT_MISS_2M 0x2d152
#define PM_MRK_DTLB_MISS_4K 0x2d156
#define PM_MRK_DTLB_MISS_16G 0x2d15e
#define PM_DTLB_MISS_64K 0x3c056
#define PM_MRK_DERAT_MISS_1G 0x3d152
#define PM_MRK_DTLB_MISS_64K 0x3d156
#define PM_DISP_HELD_SYNC_HOLD 0x4003c
#define PM_DTLB_MISS_16M 0x4c056
#define PM_DTLB_MISS_1G 0x4c05a
#define PM_MRK_DTLB_MISS_16M 0x4c15e
#define PM_MRK_ST_DONE_L2 0x10134
#define PM_RADIX_PWC_L1_HIT 0x1f056
#define PM_FLOP_CMPL 0x100f4
#define PM_MRK_NTF_FIN 0x20112
#define PM_RADIX_PWC_L2_HIT 0x2d024
#define PM_IFETCH_THROTTLE 0x3405e
#define PM_MRK_L2_TM_ST_ABORT_SISTER 0x3e15c
#define PM_RADIX_PWC_L3_HIT 0x3f056
#define PM_RUN_CYC_SMT2_MODE 0x3006c
#define PM_TM_TX_PASS_RUN_INST 0x4e014
#define PVR_POWER9_CUMULUS 0x00002000
int blacklist_events_dd21[] = {
PM_MRK_ST_DONE_L2,
PM_RADIX_PWC_L1_HIT,
PM_FLOP_CMPL,
PM_MRK_NTF_FIN,
PM_RADIX_PWC_L2_HIT,
PM_IFETCH_THROTTLE,
PM_MRK_L2_TM_ST_ABORT_SISTER,
PM_RADIX_PWC_L3_HIT,
PM_RUN_CYC_SMT2_MODE,
PM_TM_TX_PASS_RUN_INST,
PM_DISP_HELD_SYNC_HOLD,
};
int blacklist_events_dd22[] = {
PM_DTLB_MISS_16G,
PM_DERAT_MISS_2M,
PM_DTLB_MISS_2M,
PM_MRK_DTLB_MISS_1G,
PM_DTLB_MISS_4K,
PM_DERAT_MISS_1G,
PM_MRK_DERAT_MISS_2M,
PM_MRK_DTLB_MISS_4K,
PM_MRK_DTLB_MISS_16G,
PM_DTLB_MISS_64K,
PM_MRK_DERAT_MISS_1G,
PM_MRK_DTLB_MISS_64K,
PM_DISP_HELD_SYNC_HOLD,
PM_DTLB_MISS_16M,
PM_DTLB_MISS_1G,
PM_MRK_DTLB_MISS_16M,
};
int pvr_min;
/*
* check for power9 support for 2.1 and
* 2.2 model where blacklist is applicable.
*/
int check_for_power9_version(void)
{
pvr_min = PVR_MIN(mfspr(SPRN_PVR));
SKIP_IF(PVR_VER(pvr) != POWER9);
SKIP_IF(!(pvr & PVR_POWER9_CUMULUS));
SKIP_IF(!(3 - pvr_min));
return 0;
}
/*
* Testcase to ensure that using blacklisted bits in
* event code should cause event_open to fail in power9
*/
static int blacklisted_events(void)
{
struct event event;
int i = 0;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/*
* check for power9 support for 2.1 and
* 2.2 model where blacklist is applicable.
*/
SKIP_IF(check_for_power9_version());
/* Skip for Generic compat mode */
SKIP_IF(check_for_generic_compat_pmu());
if (pvr_min == 1) {
for (i = 0; i < ARRAY_SIZE(blacklist_events_dd21); i++) {
event_init(&event, blacklist_events_dd21[i]);
FAIL_IF(!event_open(&event));
}
} else if (pvr_min == 2) {
for (i = 0; i < ARRAY_SIZE(blacklist_events_dd22); i++) {
event_init(&event, blacklist_events_dd22[i]);
FAIL_IF(!event_open(&event));
}
}
return 0;
}
int main(void)
{
return test_harness(blacklisted_events, "blacklisted_events");
}
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
#define PM_RUN_CYC_ALT 0x200f4
#define PM_INST_DISP 0x200f2
#define PM_BR_2PATH 0x20036
#define PM_LD_MISS_L1 0x3e054
#define PM_RUN_INST_CMPL_ALT 0x400fa
#define EventCode_1 0x100fc
#define EventCode_2 0x200fa
#define EventCode_3 0x300fc
#define EventCode_4 0x400fc
/*
* Check for event alternatives.
*/
static int event_alternatives_tests_p10(void)
{
struct event *e, events[5];
int i;
int pvr = PVR_VER(mfspr(SPRN_PVR));
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/*
* PVR check is used here since PMU specific data like
* alternative events is handled by respective PMU driver
* code and using PVR will work correctly for all cases
* including generic compat mode.
*/
SKIP_IF((pvr != POWER10) && (pvr != POWER11));
SKIP_IF(check_for_generic_compat_pmu());
/*
* Test for event alternative for 0x0001e
* and 0x00002.
*/
e = &events[0];
event_init(e, 0x0001e);
e = &events[1];
event_init(e, EventCode_1);
e = &events[2];
event_init(e, EventCode_2);
e = &events[3];
event_init(e, EventCode_3);
e = &events[4];
event_init(e, EventCode_4);
FAIL_IF(event_open(&events[0]));
/*
* Expected to pass since 0x0001e has alternative event
* 0x600f4 in PMC6. So it can go in with other events
* in PMC1 to PMC4.
*/
for (i = 1; i < 5; i++)
FAIL_IF(event_open_with_group(&events[i], events[0].fd));
for (i = 0; i < 5; i++)
event_close(&events[i]);
e = &events[0];
event_init(e, 0x00002);
e = &events[1];
event_init(e, EventCode_1);
e = &events[2];
event_init(e, EventCode_2);
e = &events[3];
event_init(e, EventCode_3);
e = &events[4];
event_init(e, EventCode_4);
FAIL_IF(event_open(&events[0]));
/*
* Expected to pass since 0x00020 has alternative event
* 0x500fa in PMC5. So it can go in with other events
* in PMC1 to PMC4.
*/
for (i = 1; i < 5; i++)
FAIL_IF(event_open_with_group(&events[i], events[0].fd));
for (i = 0; i < 5; i++)
event_close(&events[i]);
return 0;
}
int main(void)
{
return test_harness(event_alternatives_tests_p10, "event_alternatives_tests_p10");
}
@@ -0,0 +1,116 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
#define PM_RUN_CYC_ALT 0x200f4
#define PM_INST_DISP 0x200f2
#define PM_BR_2PATH 0x20036
#define PM_LD_MISS_L1 0x3e054
#define PM_RUN_INST_CMPL_ALT 0x400fa
#define EventCode_1 0x200fa
#define EventCode_2 0x200fc
#define EventCode_3 0x300fc
#define EventCode_4 0x400fc
/*
* Check for event alternatives.
*/
static int event_alternatives_tests_p9(void)
{
struct event event, leader;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/*
* PVR check is used here since PMU specific data like
* alternative events is handled by respective PMU driver
* code and using PVR will work correctly for all cases
* including generic compat mode.
*/
SKIP_IF(PVR_VER(mfspr(SPRN_PVR)) != POWER9);
/* Skip for generic compat PMU */
SKIP_IF(check_for_generic_compat_pmu());
/* Init the event for PM_RUN_CYC_ALT */
event_init(&leader, PM_RUN_CYC_ALT);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode_1);
/*
* Expected to pass since PM_RUN_CYC_ALT in PMC2 has alternative event
* 0x600f4. So it can go in with EventCode_1 which is using PMC2
*/
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
event_init(&leader, PM_INST_DISP);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode_2);
/*
* Expected to pass since PM_INST_DISP in PMC2 has alternative event
* 0x300f2 in PMC3. So it can go in with EventCode_2 which is using PMC2
*/
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
event_init(&leader, PM_BR_2PATH);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode_2);
/*
* Expected to pass since PM_BR_2PATH in PMC2 has alternative event
* 0x40036 in PMC4. So it can go in with EventCode_2 which is using PMC2
*/
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
event_init(&leader, PM_LD_MISS_L1);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode_3);
/*
* Expected to pass since PM_LD_MISS_L1 in PMC3 has alternative event
* 0x400f0 in PMC4. So it can go in with EventCode_3 which is using PMC3
*/
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
event_init(&leader, PM_RUN_INST_CMPL_ALT);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode_4);
/*
* Expected to pass since PM_RUN_INST_CMPL_ALT in PMC4 has alternative event
* 0x500fa in PMC5. So it can go in with EventCode_4 which is using PMC4
*/
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(event_alternatives_tests_p9, "event_alternatives_tests_p9");
}
@@ -0,0 +1,131 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <sys/prctl.h>
#include <limits.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
/*
* Testcase to ensure that using invalid event in generic
* event for PERF_TYPE_HARDWARE should fail
*/
static int generic_events_valid_test(void)
{
struct event event;
int pvr = mfspr(SPRN_PVR);
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/* generic events is different in compat_mode */
SKIP_IF(check_for_generic_compat_pmu());
/*
* Invalid generic events in power10:
* - PERF_COUNT_HW_BUS_CYCLES
* - PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
* - PERF_COUNT_HW_STALLED_CYCLES_BACKEND
* - PERF_COUNT_HW_REF_CPU_CYCLES
*/
if ((pvr == POWER10) || (pvr == POWER11)) {
event_init_opts(&event, PERF_COUNT_HW_CPU_CYCLES, PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_CACHE_REFERENCES,
PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_CACHE_MISSES, PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_BRANCH_MISSES, PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_BUS_CYCLES, PERF_TYPE_HARDWARE, "event");
FAIL_IF(!event_open(&event));
event_init_opts(&event, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND,
PERF_TYPE_HARDWARE, "event");
FAIL_IF(!event_open(&event));
event_init_opts(&event, PERF_COUNT_HW_STALLED_CYCLES_BACKEND,
PERF_TYPE_HARDWARE, "event");
FAIL_IF(!event_open(&event));
event_init_opts(&event, PERF_COUNT_HW_REF_CPU_CYCLES, PERF_TYPE_HARDWARE, "event");
FAIL_IF(!event_open(&event));
} else if (PVR_VER(mfspr(SPRN_PVR)) == POWER9) {
/*
* Invalid generic events in power9:
* - PERF_COUNT_HW_BUS_CYCLES
* - PERF_COUNT_HW_REF_CPU_CYCLES
*/
event_init_opts(&event, PERF_COUNT_HW_CPU_CYCLES, PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_INSTRUCTIONS, PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_CACHE_REFERENCES,
PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_CACHE_MISSES, PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_BRANCH_MISSES, PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_BUS_CYCLES, PERF_TYPE_HARDWARE, "event");
FAIL_IF(!event_open(&event));
event_init_opts(&event, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND,
PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_STALLED_CYCLES_BACKEND,
PERF_TYPE_HARDWARE, "event");
FAIL_IF(event_open(&event));
event_close(&event);
event_init_opts(&event, PERF_COUNT_HW_REF_CPU_CYCLES, PERF_TYPE_HARDWARE, "event");
FAIL_IF(!event_open(&event));
}
return 0;
}
int main(void)
{
return test_harness(generic_events_valid_test, "generic_events_valid_test");
}
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "utils.h"
#include "../sampling_tests/misc.h"
/* All L1 D cache load references counted at finish, gated by reject */
#define EventCode_1 0x1100fc
/* Load Missed L1 */
#define EventCode_2 0x23e054
/* Load Missed L1 */
#define EventCode_3 0x13e054
/*
* Testcase for group constraint check of data and instructions
* cache qualifier bits which is used to program cache select field in
* Monitor Mode Control Register 1 (MMCR1: 16-17) for l1 cache.
* All events in the group should match cache select bits otherwise
* event_open for the group will fail.
*/
static int group_constraint_cache(void)
{
struct event event, leader;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/* Init the events for the group contraint check for l1 cache select bits */
event_init(&leader, EventCode_1);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode_2);
/* Expected to fail as sibling event doesn't request same l1 cache select bits as leader */
FAIL_IF(!event_open_with_group(&event, leader.fd));
event_close(&event);
/* Init the event for the group contraint l1 cache select test */
event_init(&event, EventCode_3);
/* Expected to succeed as sibling event request same l1 cache select bits as leader */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(group_constraint_cache, "group_constraint_cache");
}
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "utils.h"
#include "../sampling_tests/misc.h"
/* All successful D-side store dispatches for this thread */
#define EventCode_1 0x010000046080
/* All successful D-side store dispatches for this thread that were L2 Miss */
#define EventCode_2 0x26880
/* All successful D-side store dispatches for this thread that were L2 Miss */
#define EventCode_3 0x010000026880
/*
* Testcase for group constraint check of l2l3_sel bits which is
* used to program l2l3 select field in Monitor Mode Control Register 0
* (MMCR0: 56-60).
* All events in the group should match l2l3_sel bits otherwise
* event_open for the group should fail.
*/
static int group_constraint_l2l3_sel(void)
{
struct event event, leader;
/*
* Check for platform support for the test.
* This test is only aplicable on ISA v3.1
*/
SKIP_IF(platform_check_for_tests());
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the events for the group contraint check for l2l3_sel bits */
event_init(&leader, EventCode_1);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode_2);
/* Expected to fail as sibling event doesn't request same l2l3_sel bits as leader */
FAIL_IF(!event_open_with_group(&event, leader.fd));
event_close(&event);
/* Init the event for the group contraint l2l3_sel test */
event_init(&event, EventCode_3);
/* Expected to succeed as sibling event request same l2l3_sel bits as leader */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(group_constraint_l2l3_sel, "group_constraint_l2l3_sel");
}
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
#define EventCode_1 0x35340401e0
#define EventCode_2 0x353c0101ec
#define EventCode_3 0x35340101ec
/*
* Test that using different sample bits in
* event code cause failure in schedule for
* group of events.
*/
static int group_constraint_mmcra_sample(void)
{
struct event event, leader;
SKIP_IF(platform_check_for_tests());
/*
* Events with different "sample" field values
* in a group will fail to schedule.
* Use event with load only sampling mode as
* group leader. Use event with store only sampling
* as sibling event.
*/
event_init(&leader, EventCode_1);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode_2);
/* Expected to fail as sibling event doesn't use same sampling bits as leader */
FAIL_IF(!event_open_with_group(&event, leader.fd));
event_init(&event, EventCode_3);
/* Expected to pass as sibling event use same sampling bits as leader */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(group_constraint_mmcra_sample, "group_constraint_mmcra_sample");
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
/*
* Testcase for checking constraint checks for
* Performance Monitor Counter 5 (PMC5) and also
* Performance Monitor Counter 6 (PMC6). Events using
* PMC5/PMC6 shouldn't have other fields in event
* code like cache bits, thresholding or marked bit.
*/
static int group_constraint_pmc56(void)
{
struct event event;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/*
* Events using PMC5 and PMC6 with cache bit
* set in event code is expected to fail.
*/
event_init(&event, 0x2500fa);
FAIL_IF(!event_open(&event));
event_init(&event, 0x2600f4);
FAIL_IF(!event_open(&event));
/*
* PMC5 and PMC6 only supports base events:
* ie 500fa and 600f4. Other combinations
* should fail.
*/
event_init(&event, 0x501e0);
FAIL_IF(!event_open(&event));
event_init(&event, 0x6001e);
FAIL_IF(!event_open(&event));
event_init(&event, 0x501fa);
FAIL_IF(!event_open(&event));
/*
* Events using PMC5 and PMC6 with random
* sampling bits set in event code should fail
* to schedule.
*/
event_init(&event, 0x35340500fa);
FAIL_IF(!event_open(&event));
return 0;
}
int main(void)
{
return test_harness(group_constraint_pmc56, "group_constraint_pmc56");
}
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
/*
* Testcase for number of counters in use.
* The number of programmable counters is from
* performance monitor counter 1 to performance
* monitor counter 4 (PMC1-PMC4). If number of
* counters in use exceeds the limit, next event
* should fail to schedule.
*/
static int group_constraint_pmc_count(void)
{
struct event *e, events[5];
int i;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/*
* Test for number of counters in use.
* Use PMC1 to PMC4 for leader and 3 sibling
* events. Trying to open fourth event should
* fail here.
*/
e = &events[0];
event_init(e, 0x1001a);
e = &events[1];
event_init(e, 0x200fc);
e = &events[2];
event_init(e, 0x30080);
e = &events[3];
event_init(e, 0x40054);
e = &events[4];
event_init(e, 0x0002c);
FAIL_IF(event_open(&events[0]));
/*
* The event_open will fail on event 4 if constraint
* check fails
*/
for (i = 1; i < 5; i++) {
if (i == 4)
FAIL_IF(!event_open_with_group(&events[i], events[0].fd));
else
FAIL_IF(event_open_with_group(&events[i], events[0].fd));
}
for (i = 1; i < 4; i++)
event_close(&events[i]);
return 0;
}
int main(void)
{
return test_harness(group_constraint_pmc_count, "group_constraint_pmc_count");
}
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
/* PM_DATA_RADIX_PROCESS_L2_PTE_FROM_L2 */
#define EventCode_1 0x14242
/* PM_DATA_RADIX_PROCESS_L2_PTE_FROM_L3 */
#define EventCode_2 0x24242
/*
* Testcase for group constraint check for radix_scope_qual
* field which is used to program Monitor Mode Control
* egister (MMCR1) bit 18.
* All events in the group should match radix_scope_qual,
* bits otherwise event_open for the group should fail.
*/
static int group_constraint_radix_scope_qual(void)
{
struct event event, leader;
/*
* Check for platform support for the test.
* This test is aplicable on ISA v3.1 only.
*/
SKIP_IF(platform_check_for_tests());
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the events for the group contraint check for radix_scope_qual bits */
event_init(&leader, EventCode_1);
FAIL_IF(event_open(&leader));
event_init(&event, 0x200fc);
/* Expected to fail as sibling event doesn't request same radix_scope_qual bits as leader */
FAIL_IF(!event_open_with_group(&event, leader.fd));
event_init(&event, EventCode_2);
/* Expected to pass as sibling event request same radix_scope_qual bits as leader */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(group_constraint_radix_scope_qual,
"group_constraint_radix_scope_qual");
}
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
/* The processor's L1 data cache was reloaded */
#define EventCode1 0x21C040
#define EventCode2 0x22C040
/*
* Testcase for group constraint check
* when using events with same PMC.
* Multiple events in a group shouldn't
* ask for same PMC. If so it should fail.
*/
static int group_constraint_repeat(void)
{
struct event event, leader;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/*
* Two events in a group using same PMC
* should fail to get scheduled. Usei same PMC2
* for leader and sibling event which is expected
* to fail.
*/
event_init(&leader, EventCode1);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode1);
/* Expected to fail since sibling event is requesting same PMC as leader */
FAIL_IF(!event_open_with_group(&event, leader.fd));
event_init(&event, EventCode2);
/* Expected to pass since sibling event is requesting different PMC */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(group_constraint_repeat, "group_constraint_repeat");
}
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "utils.h"
#include "../sampling_tests/misc.h"
/*
* Primary PMU events used here is PM_MRK_INST_CMPL (0x401e0) and
* PM_THRESH_MET (0x101ec)
* Threshold event selection used is issue to complete for cycles
* Sampling criteria is Load or Store only sampling
*/
#define p9_EventCode_1 0x13e35340401e0
#define p9_EventCode_2 0x17d34340101ec
#define p9_EventCode_3 0x13e35340101ec
#define p10_EventCode_1 0x35340401e0
#define p10_EventCode_2 0x35340101ec
/*
* Testcase for group constraint check of thresh_cmp bits which is
* used to program thresh compare field in Monitor Mode Control Register A
* (MMCRA: 9-18 bits for power9 and MMCRA: 8-18 bits for power10/power11).
* All events in the group should match thresh compare bits otherwise
* event_open for the group will fail.
*/
static int group_constraint_thresh_cmp(void)
{
struct event event, leader;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
if (have_hwcap2(PPC_FEATURE2_ARCH_3_1)) {
/* Init the events for the group contraint check for thresh_cmp bits */
event_init(&leader, p10_EventCode_1);
/* Add the thresh_cmp value for leader in config1 */
leader.attr.config1 = 1000;
FAIL_IF(event_open(&leader));
event_init(&event, p10_EventCode_2);
/* Add the different thresh_cmp value from the leader event in config1 */
event.attr.config1 = 2000;
/* Expected to fail as sibling and leader event request different thresh_cmp bits */
FAIL_IF(!event_open_with_group(&event, leader.fd));
event_close(&event);
/* Init the event for the group contraint thresh compare test */
event_init(&event, p10_EventCode_2);
/* Add the same thresh_cmp value for leader and sibling event in config1 */
event.attr.config1 = 1000;
/* Expected to succeed as sibling and leader event request same thresh_cmp bits */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
} else {
/* Init the events for the group contraint check for thresh_cmp bits */
event_init(&leader, p9_EventCode_1);
FAIL_IF(event_open(&leader));
event_init(&event, p9_EventCode_2);
/* Expected to fail as sibling and leader event request different thresh_cmp bits */
FAIL_IF(!event_open_with_group(&event, leader.fd));
event_close(&event);
/* Init the event for the group contraint thresh compare test */
event_init(&event, p9_EventCode_3);
/* Expected to succeed as sibling and leader event request same thresh_cmp bits */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
}
return 0;
}
int main(void)
{
return test_harness(group_constraint_thresh_cmp, "group_constraint_thresh_cmp");
}
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "utils.h"
#include "../sampling_tests/misc.h"
/*
* Primary PMU events used here are PM_MRK_INST_CMPL (0x401e0) and
* PM_THRESH_MET (0x101ec).
* Threshold event selection used is issue to complete and issue to
* finished for cycles
* Sampling criteria is Load or Store only sampling
*/
#define EventCode_1 0x35340401e0
#define EventCode_2 0x34340101ec
#define EventCode_3 0x35340101ec
/*
* Testcase for group constraint check of thresh_ctl bits which is
* used to program thresh compare field in Monitor Mode Control Register A
* (MMCR0: 48-55).
* All events in the group should match thresh ctl bits otherwise
* event_open for the group will fail.
*/
static int group_constraint_thresh_ctl(void)
{
struct event event, leader;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/* Init the events for the group contraint thresh control test */
event_init(&leader, EventCode_1);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode_2);
/* Expected to fail as sibling and leader event request different thresh_ctl bits */
FAIL_IF(!event_open_with_group(&event, leader.fd));
event_close(&event);
/* Init the event for the group contraint thresh control test */
event_init(&event, EventCode_3);
/* Expected to succeed as sibling and leader event request same thresh_ctl bits */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(group_constraint_thresh_ctl, "group_constraint_thresh_ctl");
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "utils.h"
#include "../sampling_tests/misc.h"
/*
* Primary PMU events used here are PM_MRK_INST_CMPL (0x401e0) and
* PM_THRESH_MET (0x101ec).
* Threshold event selection used is issue to complete
* Sampling criteria is Load or Store only sampling
*/
#define EventCode_1 0x35340401e0
#define EventCode_2 0x35540101ec
#define EventCode_3 0x35340101ec
/*
* Testcase for group constraint check of thresh_sel bits which is
* used to program thresh select field in Monitor Mode Control Register A
* (MMCRA: 45-57).
* All events in the group should match thresh sel bits otherwise
* event_open for the group will fail.
*/
static int group_constraint_thresh_sel(void)
{
struct event event, leader;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/* Init the events for the group contraint thresh select test */
event_init(&leader, EventCode_1);
FAIL_IF(event_open(&leader));
event_init(&event, EventCode_2);
/* Expected to fail as sibling and leader event request different thresh_sel bits */
FAIL_IF(!event_open_with_group(&event, leader.fd));
event_close(&event);
/* Init the event for the group contraint thresh select test */
event_init(&event, EventCode_3);
/* Expected to succeed as sibling and leader event request same thresh_sel bits */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(group_constraint_thresh_sel, "group_constraint_thresh_sel");
}
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "utils.h"
#include "../sampling_tests/misc.h"
/* All successful D-side store dispatches for this thread with PMC 2 */
#define EventCode_1 0x26080
/* All successful D-side store dispatches for this thread with PMC 4 */
#define EventCode_2 0x46080
/* All successful D-side store dispatches for this thread that were L2 Miss with PMC 3 */
#define EventCode_3 0x36880
/*
* Testcase for group constraint check of unit and pmc bits which is
* used to program corresponding unit and pmc field in Monitor Mode
* Control Register 1 (MMCR1)
* One of the event in the group should use PMC 4 incase units field
* value is within 6 to 9 otherwise event_open for the group will fail.
*/
static int group_constraint_unit(void)
{
struct event *e, events[3];
/*
* Check for platform support for the test.
* Constraint to use PMC4 with one of the event in group,
* when the unit is within 6 to 9 is only applicable on
* power9.
*/
SKIP_IF(platform_check_for_tests());
SKIP_IF(have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the events for the group contraint check for unit bits */
e = &events[0];
event_init(e, EventCode_1);
/* Expected to fail as PMC 4 is not used with unit field value 6 to 9 */
FAIL_IF(!event_open(&events[0]));
/* Init the events for the group contraint check for unit bits */
e = &events[1];
event_init(e, EventCode_2);
/* Expected to pass as PMC 4 is used with unit field value 6 to 9 */
FAIL_IF(event_open(&events[1]));
/* Init the event for the group contraint unit test */
e = &events[2];
event_init(e, EventCode_3);
/* Expected to fail as PMC4 is not being used */
FAIL_IF(!event_open_with_group(&events[2], events[0].fd));
/* Expected to succeed as event using PMC4 */
FAIL_IF(event_open_with_group(&events[2], events[1].fd));
event_close(&events[0]);
event_close(&events[1]);
event_close(&events[2]);
return 0;
}
int main(void)
{
return test_harness(group_constraint_unit, "group_constraint_unit");
}
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include "../event.h"
#include <sys/prctl.h>
#include <limits.h>
#include "../sampling_tests/misc.h"
/*
* Testcase for group constraint check for
* Performance Monitor Counter 5 (PMC5) and also
* Performance Monitor Counter 6 (PMC6).
* Test that pmc5/6 is excluded from constraint
* check when scheduled along with group of events.
*/
static int group_pmc56_exclude_constraints(void)
{
struct event *e, events[3];
int i;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/*
* PMC5/6 is excluded from constraint bit
* check along with group of events. Use
* group of events with PMC5, PMC6 and also
* event with cache bit (dc_ic) set. Test expects
* this set of events to go in as a group.
*/
e = &events[0];
event_init(e, 0x500fa);
e = &events[1];
event_init(e, 0x600f4);
e = &events[2];
event_init(e, 0x22C040);
FAIL_IF(event_open(&events[0]));
/*
* The event_open will fail if constraint check fails.
* Since we are asking for events in a group and since
* PMC5/PMC6 is excluded from group constraints, even_open
* should pass.
*/
for (i = 1; i < 3; i++)
FAIL_IF(event_open_with_group(&events[i], events[0].fd));
for (i = 0; i < 3; i++)
event_close(&events[i]);
return 0;
}
int main(void)
{
return test_harness(group_pmc56_exclude_constraints, "group_pmc56_exclude_constraints");
}
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "utils.h"
#include "../sampling_tests/misc.h"
/*
* Load Missed L1, for power9 its pointing to PM_LD_MISS_L1_FIN (0x2c04e) and
* for power10 its pointing to PM_LD_MISS_L1 (0x3e054)
*
* Hardware cache level : PERF_COUNT_HW_CACHE_L1D
* Hardware cache event operation type : PERF_COUNT_HW_CACHE_OP_READ
* Hardware cache event result type : PERF_COUNT_HW_CACHE_RESULT_MISS
*/
#define EventCode_1 0x10000
/*
* Hardware cache level : PERF_COUNT_HW_CACHE_L1D
* Hardware cache event operation type : PERF_COUNT_HW_CACHE_OP_WRITE
* Hardware cache event result type : PERF_COUNT_HW_CACHE_RESULT_ACCESS
*/
#define EventCode_2 0x0100
/*
* Hardware cache level : PERF_COUNT_HW_CACHE_DTLB
* Hardware cache event operation type : PERF_COUNT_HW_CACHE_OP_WRITE
* Hardware cache event result type : PERF_COUNT_HW_CACHE_RESULT_ACCESS
*/
#define EventCode_3 0x0103
/*
* Hardware cache level : PERF_COUNT_HW_CACHE_L1D
* Hardware cache event operation type : PERF_COUNT_HW_CACHE_OP_READ
* Hardware cache event result type : Invalid ( > PERF_COUNT_HW_CACHE_RESULT_MAX)
*/
#define EventCode_4 0x030000
/*
* A perf test to check valid hardware cache events.
*/
static int hw_cache_event_type_test(void)
{
struct event event;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/* Skip for Generic compat PMU */
SKIP_IF(check_for_generic_compat_pmu());
/* Init the event to test hardware cache event */
event_init_opts(&event, EventCode_1, PERF_TYPE_HW_CACHE, "event");
/* Expected to success as its pointing to L1 load miss */
FAIL_IF(event_open(&event));
event_close(&event);
/* Init the event to test hardware cache event */
event_init_opts(&event, EventCode_2, PERF_TYPE_HW_CACHE, "event");
/* Expected to fail as the corresponding cache event entry have 0 in that index */
FAIL_IF(!event_open(&event));
event_close(&event);
/* Init the event to test hardware cache event */
event_init_opts(&event, EventCode_3, PERF_TYPE_HW_CACHE, "event");
/* Expected to fail as the corresponding cache event entry have -1 in that index */
FAIL_IF(!event_open(&event));
event_close(&event);
/* Init the event to test hardware cache event */
event_init_opts(&event, EventCode_4, PERF_TYPE_HW_CACHE, "event");
/* Expected to fail as hardware cache event result type is Invalid */
FAIL_IF(!event_open(&event));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(hw_cache_event_type_test, "hw_cache_event_type_test");
}
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <sys/prctl.h>
#include <limits.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
/* The data cache was reloaded from local core's L3 due to a demand load */
#define EventCode_1 0x1340000001c040
/* PM_DATA_RADIX_PROCESS_L2_PTE_FROM_L2 */
#define EventCode_2 0x14242
/* Event code with IFM, EBB, BHRB bits set in event code */
#define EventCode_3 0xf00000000000001e
/*
* Some of the bits in the event code is
* reserved for specific platforms.
* Event code bits 52-59 are reserved in power9,
* whereas in ISA v3.1, these are used for programming
* Monitor Mode Control Register 3 (MMCR3).
* Bit 9 in event code is reserved in power9,
* whereas it is used for programming "radix_scope_qual"
* bit 18 in Monitor Mode Control Register 1 (MMCR1).
*
* Testcase to ensure that using reserved bits in
* event code should cause event_open to fail.
*/
static int invalid_event_code(void)
{
struct event event;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/*
* Events using MMCR3 bits and radix scope qual bits
* should fail in power9 and should succeed in power10 ( ISA v3.1 )
* Init the events and check for pass/fail in event open.
*/
if (have_hwcap2(PPC_FEATURE2_ARCH_3_1)) {
event_init(&event, EventCode_1);
FAIL_IF(event_open(&event));
event_close(&event);
event_init(&event, EventCode_2);
FAIL_IF(event_open(&event));
event_close(&event);
} else {
event_init(&event, EventCode_1);
FAIL_IF(!event_open(&event));
event_init(&event, EventCode_2);
FAIL_IF(!event_open(&event));
}
return 0;
}
int main(void)
{
return test_harness(invalid_event_code, "invalid_event_code");
}
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
/*
* Testcase for reserved bits in Monitor Mode Control
* Register A (MMCRA) Random Sampling Mode (SM) value.
* As per Instruction Set Architecture (ISA), the values
* 0x5, 0x9, 0xD, 0x19, 0x1D, 0x1A, 0x1E are reserved
* for sampling mode field. Test that having these reserved
* bit values should cause event_open to fail.
* Input event code uses these sampling bits along with
* 401e0 (PM_MRK_INST_CMPL).
*/
static int reserved_bits_mmcra_sample_elig_mode(void)
{
struct event event;
int pvr = PVR_VER(mfspr(SPRN_PVR));
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/* Skip for Generic compat PMU */
SKIP_IF(check_for_generic_compat_pmu());
/*
* MMCRA Random Sampling Mode (SM) values: 0x5
* 0x9, 0xD, 0x19, 0x1D, 0x1A, 0x1E is reserved.
* Expected to fail when using these reserved values.
*/
event_init(&event, 0x50401e0);
FAIL_IF(!event_open(&event));
event_init(&event, 0x90401e0);
FAIL_IF(!event_open(&event));
event_init(&event, 0xD0401e0);
FAIL_IF(!event_open(&event));
event_init(&event, 0x190401e0);
FAIL_IF(!event_open(&event));
event_init(&event, 0x1D0401e0);
FAIL_IF(!event_open(&event));
event_init(&event, 0x1A0401e0);
FAIL_IF(!event_open(&event));
event_init(&event, 0x1E0401e0);
FAIL_IF(!event_open(&event));
/*
* MMCRA Random Sampling Mode (SM) value 0x10
* is reserved in power10/power11 and 0xC is reserved in
* power9.
*/
if ((pvr == POWER10) || (pvr == POWER11)) {
event_init(&event, 0x100401e0);
FAIL_IF(!event_open(&event));
} else if (PVR_VER(mfspr(SPRN_PVR)) == POWER9) {
event_init(&event, 0xC0401e0);
FAIL_IF(!event_open(&event));
}
return 0;
}
int main(void)
{
return test_harness(reserved_bits_mmcra_sample_elig_mode,
"reserved_bits_mmcra_sample_elig_mode");
}
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include "../event.h"
#include "../sampling_tests/misc.h"
/*
* Testcase for reserved bits in Monitor Mode
* Control Register A (MMCRA) thresh_ctl bits.
* For MMCRA[48:51]/[52:55]) Threshold Start/Stop,
* 0b11110000/0b00001111 is reserved.
*/
static int reserved_bits_mmcra_thresh_ctl(void)
{
struct event event;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/* Skip for Generic compat PMU */
SKIP_IF(check_for_generic_compat_pmu());
/*
* MMCRA[48:51]/[52:55]) Threshold Start/Stop
* events Selection. 0b11110000/0b00001111 is reserved.
* Expected to fail when using these reserved values.
*/
event_init(&event, 0xf0340401e0);
FAIL_IF(!event_open(&event));
event_init(&event, 0x0f340401e0);
FAIL_IF(!event_open(&event));
return 0;
}
int main(void)
{
return test_harness(reserved_bits_mmcra_thresh_ctl, "reserved_bits_mmcra_thresh_ctl");
}
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "event.h"
#include "utils.h"
#define MALLOC_SIZE (0x10000 * 10) /* Ought to be enough .. */
/*
* Tests that the L3 bank handling is correct. We fixed it in commit e9aaac1.
*/
static int l3_bank_test(void)
{
struct event event;
char *p;
int i;
// The L3 bank logic is only used on Power8 or later
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
p = malloc(MALLOC_SIZE);
FAIL_IF(!p);
event_init(&event, 0x84918F);
FAIL_IF(event_open(&event));
for (i = 0; i < MALLOC_SIZE; i += 0x10000)
p[i] = i;
event_read(&event);
event_report(&event);
FAIL_IF(event.result.running == 0);
FAIL_IF(event.result.enabled == 0);
event_close(&event);
free(p);
return 0;
}
int main(void)
{
return test_harness(l3_bank_test, "l3_bank_test");
}
@@ -0,0 +1,188 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#define _GNU_SOURCE /* For CPU_ZERO etc. */
#include <errno.h>
#include <sched.h>
#include <setjmp.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "utils.h"
#include "lib.h"
#define PARENT_TOKEN 0xAA
#define CHILD_TOKEN 0x55
int sync_with_child(union pipe read_pipe, union pipe write_pipe)
{
char c = PARENT_TOKEN;
FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
if (c != CHILD_TOKEN) /* sometimes expected */
return 1;
return 0;
}
int wait_for_parent(union pipe read_pipe)
{
char c;
FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
FAIL_IF(c != PARENT_TOKEN);
return 0;
}
int notify_parent(union pipe write_pipe)
{
char c = CHILD_TOKEN;
FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
return 0;
}
int notify_parent_of_error(union pipe write_pipe)
{
char c = ~CHILD_TOKEN;
FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
return 0;
}
int wait_for_child(pid_t child_pid)
{
int rc;
if (waitpid(child_pid, &rc, 0) == -1) {
perror("waitpid");
return 1;
}
if (WIFEXITED(rc))
rc = WEXITSTATUS(rc);
else
rc = 1; /* Signal or other */
return rc;
}
int kill_child_and_wait(pid_t child_pid)
{
kill(child_pid, SIGTERM);
return wait_for_child(child_pid);
}
static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
{
volatile int i = 0;
/*
* We are just here to eat cpu and die. So make sure we can be killed,
* and also don't do any custom SIGTERM handling.
*/
signal(SIGTERM, SIG_DFL);
notify_parent(write_pipe);
wait_for_parent(read_pipe);
/* Soak up cpu forever */
while (1) i++;
return 0;
}
pid_t eat_cpu(int (test_function)(void))
{
union pipe read_pipe, write_pipe;
int rc;
pid_t pid;
FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
if (pipe(read_pipe.fds) == -1)
return -1;
if (pipe(write_pipe.fds) == -1)
return -1;
pid = fork();
if (pid == 0)
exit(eat_cpu_child(write_pipe, read_pipe));
if (sync_with_child(read_pipe, write_pipe)) {
rc = -1;
goto out;
}
printf("main test running as pid %d\n", getpid());
rc = test_function();
out:
kill(pid, SIGKILL);
return rc;
}
struct addr_range libc, vdso;
int parse_proc_maps(void)
{
unsigned long start, end;
char execute, name[128];
FILE *f;
int rc;
f = fopen("/proc/self/maps", "r");
if (!f) {
perror("fopen");
return -1;
}
do {
/* This skips line with no executable which is what we want */
rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
&start, &end, &execute, name);
if (rc <= 0)
break;
if (execute != 'x')
continue;
if (strstr(name, "libc")) {
libc.first = start;
libc.last = end - 1;
} else if (strstr(name, "[vdso]")) {
vdso.first = start;
vdso.last = end - 1;
}
} while(1);
fclose(f);
return 0;
}
#define PARANOID_PATH "/proc/sys/kernel/perf_event_paranoid"
bool require_paranoia_below(int level)
{
int err;
long current;
err = read_long(PARANOID_PATH, &current, 10);
if (err) {
printf("Couldn't parse " PARANOID_PATH "?\n");
return false;
}
return current < level;
}
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#ifndef __SELFTESTS_POWERPC_PMU_LIB_H
#define __SELFTESTS_POWERPC_PMU_LIB_H
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
union pipe {
struct {
int read_fd;
int write_fd;
};
int fds[2];
};
extern int kill_child_and_wait(pid_t child_pid);
extern int wait_for_child(pid_t child_pid);
extern int sync_with_child(union pipe read_pipe, union pipe write_pipe);
extern int wait_for_parent(union pipe read_pipe);
extern int notify_parent(union pipe write_pipe);
extern int notify_parent_of_error(union pipe write_pipe);
extern pid_t eat_cpu(int (test_function)(void));
extern bool require_paranoia_below(int level);
struct addr_range {
uint64_t first, last;
};
extern struct addr_range libc, vdso;
int parse_proc_maps(void);
#endif /* __SELFTESTS_POWERPC_PMU_LIB_H */
@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
*/
#include <ppc-asm.h>
.text
FUNC_START(thirty_two_instruction_loop)
cmpdi r3,0
beqlr
addi r4,r3,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1 # 28 addi's
subi r3,r3,1
b FUNC_NAME(thirty_two_instruction_loop)
FUNC_END(thirty_two_instruction_loop)
FUNC_START(thirty_two_instruction_loop_with_ll_sc)
cmpdi r3,0
beqlr
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1 # 5
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1
1: ldarx r6,0,r4 # 10
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1 # 15
addi r5,r5,1
addi r5,r5,1
stdcx. r6,0,r4
bne- 1b
addi r5,r5,1 # 20
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1 # 25
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1
addi r5,r5,1 # 30
subi r3,r3,1
b FUNC_NAME(thirty_two_instruction_loop_with_ll_sc)
FUNC_END(thirty_two_instruction_loop_with_ll_sc)
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
*/
#define _GNU_SOURCE
#include <elf.h>
#include <limits.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/prctl.h>
#include "event.h"
#include "lib.h"
#include "utils.h"
/*
* Test that per-event excludes work.
*/
static int per_event_excludes(void)
{
struct event *e, events[4];
int i;
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
/*
* We need to create the events disabled, otherwise the running/enabled
* counts don't match up.
*/
e = &events[0];
event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "instructions");
e->attr.disabled = 1;
e = &events[1];
event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "instructions(k)");
e->attr.disabled = 1;
e->attr.exclude_user = 1;
e->attr.exclude_hv = 1;
e = &events[2];
event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "instructions(h)");
e->attr.disabled = 1;
e->attr.exclude_user = 1;
e->attr.exclude_kernel = 1;
e = &events[3];
event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "instructions(u)");
e->attr.disabled = 1;
e->attr.exclude_hv = 1;
e->attr.exclude_kernel = 1;
FAIL_IF(event_open(&events[0]));
/*
* The open here will fail if we don't have per event exclude support,
* because the second event has an incompatible set of exclude settings
* and we're asking for the events to be in a group.
*/
for (i = 1; i < 4; i++)
FAIL_IF(event_open_with_group(&events[i], events[0].fd));
/*
* Even though the above will fail without per-event excludes we keep
* testing in order to be thorough.
*/
prctl(PR_TASK_PERF_EVENTS_ENABLE);
/* Spin for a while */
for (i = 0; i < INT_MAX; i++)
asm volatile("" : : : "memory");
prctl(PR_TASK_PERF_EVENTS_DISABLE);
for (i = 0; i < 4; i++) {
FAIL_IF(event_read(&events[i]));
event_report(&events[i]);
}
/*
* We should see that all events have enabled == running. That
* shows that they were all on the PMU at once.
*/
for (i = 0; i < 4; i++)
FAIL_IF(events[i].result.running != events[i].result.enabled);
/*
* We can also check that the result for instructions is >= all the
* other counts. That's because it is counting all instructions while
* the others are counting a subset.
*/
for (i = 1; i < 4; i++)
FAIL_IF(events[0].result.value < events[i].result.value);
for (i = 0; i < 4; i++)
event_close(&events[i]);
return 0;
}
int main(void)
{
return test_harness(per_event_excludes, "per_event_excludes");
}
@@ -0,0 +1,22 @@
bhrb_filter_map_test
bhrb_no_crash_wo_pmu_test
check_extended_reg_test
intr_regs_no_crash_wo_pmu_test
mmcr0_cc56run_test
mmcr0_exceptionbits_test
mmcr0_fc56_pmc1ce_test
mmcr0_fc56_pmc56_test
mmcr0_pmccext_test
mmcr0_pmcjce_test
mmcr1_comb_test
mmcr1_sel_unit_cache_test
mmcr2_fcs_fch_test
mmcr2_l2l3_test
mmcr3_src_test
mmcra_bhrb_any_test
mmcra_bhrb_cond_test
mmcra_bhrb_disable_no_branch_test
mmcra_bhrb_disable_test
mmcra_bhrb_ind_call_test
mmcra_thresh_cmp_test
mmcra_thresh_marked_sample_test
@@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0
TEST_GEN_PROGS := mmcr0_exceptionbits_test mmcr0_cc56run_test mmcr0_pmccext_test \
mmcr0_pmcjce_test mmcr0_fc56_pmc1ce_test mmcr0_fc56_pmc56_test \
mmcr1_comb_test mmcr2_l2l3_test mmcr2_fcs_fch_test \
mmcr3_src_test mmcra_thresh_marked_sample_test mmcra_thresh_cmp_test \
mmcra_bhrb_ind_call_test mmcra_bhrb_any_test mmcra_bhrb_cond_test \
mmcra_bhrb_disable_test bhrb_no_crash_wo_pmu_test intr_regs_no_crash_wo_pmu_test \
bhrb_filter_map_test mmcr1_sel_unit_cache_test mmcra_bhrb_disable_no_branch_test \
check_extended_reg_test
top_srcdir = ../../../../../..
include ../../../lib.mk
include ../../flags.mk
CFLAGS += -m64
$(TEST_GEN_PROGS): ../../harness.c ../../utils.c ../event.c ../lib.c misc.c misc.h ../loop.S ../branch_loops.S
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
/*
* A perf sampling test to check bhrb filter
* map. All the branch filters are not supported
* in powerpc. Supported filters in:
* power10/power11: any, any_call, ind_call, cond
* power9: any, any_call
*
* Testcase checks event open for invalid bhrb filter
* types should fail and valid filter types should pass.
* Testcase does validity check for these branch
* sample types.
*/
/* Invalid types for powerpc */
/* Valid bhrb filters in power9/power10/power11 */
int bhrb_filter_map_valid_common[] = {
PERF_SAMPLE_BRANCH_ANY,
PERF_SAMPLE_BRANCH_ANY_CALL,
};
/* Valid bhrb filters in power10/power11 */
int bhrb_filter_map_valid_p10[] = {
PERF_SAMPLE_BRANCH_IND_CALL,
PERF_SAMPLE_BRANCH_COND,
};
#define EventCode 0x1001e
static int bhrb_filter_map_test(void)
{
struct event event;
int i;
/* Check for platform support for the test */
SKIP_IF(platform_check_for_tests());
/*
* Skip for Generic compat PMU since
* bhrb filters is not supported
*/
SKIP_IF(check_for_generic_compat_pmu());
/* Init the event for the sampling test */
event_init(&event, EventCode);
event.attr.sample_period = 1000;
event.attr.sample_type = PERF_SAMPLE_BRANCH_STACK;
event.attr.disabled = 1;
/* Invalid filter maps which are expected to fail in event_open */
for (i = PERF_SAMPLE_BRANCH_USER_SHIFT; i < PERF_SAMPLE_BRANCH_MAX_SHIFT; i++) {
/* Skip the valid branch sample type */
if (i == PERF_SAMPLE_BRANCH_ANY_SHIFT || i == PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT \
|| i == PERF_SAMPLE_BRANCH_IND_CALL_SHIFT || i == PERF_SAMPLE_BRANCH_COND_SHIFT)
continue;
event.attr.branch_sample_type = 1U << i;
FAIL_IF(!event_open(&event));
}
/* valid filter maps for power9/power10/power11 which are expected to pass in event_open */
for (i = 0; i < ARRAY_SIZE(bhrb_filter_map_valid_common); i++) {
event.attr.branch_sample_type = bhrb_filter_map_valid_common[i];
FAIL_IF(event_open(&event));
event_close(&event);
}
/*
* filter maps which are valid in power10/power11 and invalid in power9.
* PVR check is used here since PMU specific data like bhrb filter
* alternative tests is handled by respective PMU driver code and
* using PVR will work correctly for all cases including generic
* compat mode.
*/
switch (PVR_VER(mfspr(SPRN_PVR))) {
case POWER11:
case POWER10:
for (i = 0; i < ARRAY_SIZE(bhrb_filter_map_valid_p10); i++) {
event.attr.branch_sample_type = bhrb_filter_map_valid_p10[i];
FAIL_IF(event_open(&event));
event_close(&event);
}
break;
default:
for (i = 0; i < ARRAY_SIZE(bhrb_filter_map_valid_p10); i++) {
event.attr.branch_sample_type = bhrb_filter_map_valid_p10[i];
FAIL_IF(!event_open(&event));
}
}
/*
* Combine filter maps which includes a valid branch filter and an invalid branch
* filter. Example: any ( PERF_SAMPLE_BRANCH_ANY) and any_call
* (PERF_SAMPLE_BRANCH_ANY_CALL).
* The perf_event_open should fail in this case.
*/
event.attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL;
FAIL_IF(!event_open(&event));
return 0;
}
int main(void)
{
return test_harness(bhrb_filter_map_test, "bhrb_filter_map_test");
}
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
/*
* A perf sampling test for making sure
* enabling branch stack doesn't crash in any
* environment, say:
* - With generic compat PMU
* - without any PMU registered
* - With platform specific PMU
* A fix for bhrb sampling crash was added in kernel
* via commit: b460b512417a ("powerpc/perf: Fix crashes
* with generic_compat_pmu & BHRB")
*
* This testcase exercises this code by doing branch
* stack enable for software event. s/w event is used
* since software event will work even in platform
* without PMU.
*/
static int bhrb_no_crash_wo_pmu_test(void)
{
struct event event;
/*
* Init the event for the sampling test.
* This uses software event which works on
* any platform.
*/
event_init_opts(&event, 0, PERF_TYPE_SOFTWARE, "cycles");
event.attr.sample_period = 1000;
event.attr.sample_type = PERF_SAMPLE_BRANCH_STACK;
event.attr.disabled = 1;
/*
* Return code of event_open is not
* considered since test just expects no crash from
* using PERF_SAMPLE_BRANCH_STACK. Also for environment
* like generic compat PMU, branch stack is unsupported.
*/
event_open(&event);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(bhrb_no_crash_wo_pmu_test, "bhrb_no_crash_wo_pmu_test");
}
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2024, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
/*
* A perf sampling test to check extended
* reg support.
*/
static int check_extended_reg_test(void)
{
/* Check for platform support for the test */
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00));
/* Skip for Generic compat PMU */
SKIP_IF(check_for_generic_compat_pmu());
/* Check if platform supports extended regs */
platform_extended_mask = perf_get_platform_reg_mask();
FAIL_IF(check_extended_regs_support());
return 0;
}
int main(void)
{
return test_harness(check_extended_reg_test, "check_extended_reg_test");
}
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
/*
* A perf sampling test for making sure
* sampling with -intr-regs doesn't crash
* in any environment, say:
* - With generic compat PMU
* - without any PMU registered
* - With platform specific PMU.
* A fix for crash with intr_regs was
* addressed in commit: f75e7d73bdf7 in kernel.
*
* This testcase exercises this code path by doing
* intr_regs using software event. Software event is
* used since s/w event will work even in platform
* without PMU.
*/
static int intr_regs_no_crash_wo_pmu_test(void)
{
struct event event;
/*
* Init the event for the sampling test.
* This uses software event which works on
* any platform.
*/
event_init_opts(&event, 0, PERF_TYPE_SOFTWARE, "cycles");
event.attr.sample_period = 1000;
event.attr.sample_type = PERF_SAMPLE_REGS_INTR;
event.attr.disabled = 1;
/*
* Return code of event_open is not considered
* since test just expects no crash from using
* PERF_SAMPLE_REGS_INTR.
*/
event_open(&event);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(intr_regs_no_crash_wo_pmu_test, "intr_regs_no_crash_wo_pmu_test");
}
@@ -0,0 +1,553 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
* Copyright 2022, Madhavan Srinivasan, IBM Corp.
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <ctype.h>
#include "misc.h"
#define PAGE_SIZE sysconf(_SC_PAGESIZE)
/* Storage for platform version */
int pvr;
u64 platform_extended_mask;
/* Mask and Shift for Event code fields */
int ev_mask_pmcxsel, ev_shift_pmcxsel; //pmcxsel field
int ev_mask_marked, ev_shift_marked; //marked filed
int ev_mask_comb, ev_shift_comb; //combine field
int ev_mask_unit, ev_shift_unit; //unit field
int ev_mask_pmc, ev_shift_pmc; //pmc field
int ev_mask_cache, ev_shift_cache; //Cache sel field
int ev_mask_sample, ev_shift_sample; //Random sampling field
int ev_mask_thd_sel, ev_shift_thd_sel; //thresh_sel field
int ev_mask_thd_start, ev_shift_thd_start; //thresh_start field
int ev_mask_thd_stop, ev_shift_thd_stop; //thresh_stop field
int ev_mask_thd_cmp, ev_shift_thd_cmp; //thresh cmp field
int ev_mask_sm, ev_shift_sm; //SDAR mode field
int ev_mask_rsq, ev_shift_rsq; //radix scope qual field
int ev_mask_l2l3, ev_shift_l2l3; //l2l3 sel field
int ev_mask_mmcr3_src, ev_shift_mmcr3_src; //mmcr3 field
static void init_ev_encodes(void)
{
ev_mask_pmcxsel = 0xff;
ev_shift_pmcxsel = 0;
ev_mask_marked = 1;
ev_shift_marked = 8;
ev_mask_unit = 0xf;
ev_shift_unit = 12;
ev_mask_pmc = 0xf;
ev_shift_pmc = 16;
ev_mask_sample = 0x1f;
ev_shift_sample = 24;
ev_mask_thd_sel = 0x7;
ev_shift_thd_sel = 29;
ev_mask_thd_start = 0xf;
ev_shift_thd_start = 36;
ev_mask_thd_stop = 0xf;
ev_shift_thd_stop = 32;
switch (pvr) {
case POWER11:
case POWER10:
ev_mask_thd_cmp = 0x3ffff;
ev_shift_thd_cmp = 0;
ev_mask_rsq = 1;
ev_shift_rsq = 9;
ev_mask_comb = 3;
ev_shift_comb = 10;
ev_mask_cache = 3;
ev_shift_cache = 20;
ev_mask_sm = 0x3;
ev_shift_sm = 22;
ev_mask_l2l3 = 0x1f;
ev_shift_l2l3 = 40;
ev_mask_mmcr3_src = 0x7fff;
ev_shift_mmcr3_src = 45;
break;
case POWER9:
ev_mask_comb = 3;
ev_shift_comb = 10;
ev_mask_cache = 0xf;
ev_shift_cache = 20;
ev_mask_thd_cmp = 0x3ff;
ev_shift_thd_cmp = 40;
ev_mask_sm = 0x3;
ev_shift_sm = 50;
break;
default:
FAIL_IF_EXIT(1);
}
}
/* Return the extended regs mask value */
u64 perf_get_platform_reg_mask(void)
{
if (have_hwcap2(PPC_FEATURE2_ARCH_3_1))
return PERF_POWER10_MASK;
if (have_hwcap2(PPC_FEATURE2_ARCH_3_00))
return PERF_POWER9_MASK;
return -1;
}
int check_extended_regs_support(void)
{
int fd;
struct event event;
event_init(&event, 0x1001e);
event.attr.type = 4;
event.attr.sample_period = 1;
event.attr.disabled = 1;
event.attr.sample_type = PERF_SAMPLE_REGS_INTR;
event.attr.sample_regs_intr = platform_extended_mask;
fd = event_open(&event);
if (fd != -1)
return 0;
return -1;
}
int platform_check_for_tests(void)
{
pvr = PVR_VER(mfspr(SPRN_PVR));
/*
* Check for supported platforms
* for sampling test
*/
switch (pvr) {
case POWER11:
case POWER10:
case POWER9:
break;
default:
goto out;
}
/*
* Check PMU driver registered by looking for
* PPC_FEATURE2_EBB bit in AT_HWCAP2
*/
if (!have_hwcap2(PPC_FEATURE2_EBB) || !have_hwcap2(PPC_FEATURE2_ARCH_3_00))
goto out;
return 0;
out:
printf("%s: Tests unsupported for this platform\n", __func__);
return -1;
}
int check_pvr_for_sampling_tests(void)
{
SKIP_IF(platform_check_for_tests());
platform_extended_mask = perf_get_platform_reg_mask();
/* check if platform supports extended regs */
if (check_extended_regs_support())
goto out;
init_ev_encodes();
return 0;
out:
printf("%s: Sampling tests un-supported\n", __func__);
return -1;
}
/*
* Allocate mmap buffer of "mmap_pages" number of
* pages.
*/
void *event_sample_buf_mmap(int fd, int mmap_pages)
{
size_t page_size = sysconf(_SC_PAGESIZE);
size_t mmap_size;
void *buff;
if (mmap_pages <= 0)
return NULL;
if (fd <= 0)
return NULL;
mmap_size = page_size * (1 + mmap_pages);
buff = mmap(NULL, mmap_size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buff == MAP_FAILED) {
perror("mmap() failed.");
return NULL;
}
return buff;
}
/*
* Post process the mmap buffer.
* - If sample_count != NULL then return count of total
* number of samples present in the mmap buffer.
* - If sample_count == NULL then return the address
* of first sample from the mmap buffer
*/
void *__event_read_samples(void *sample_buff, size_t *size, u64 *sample_count)
{
size_t page_size = sysconf(_SC_PAGESIZE);
struct perf_event_header *header = sample_buff + page_size;
struct perf_event_mmap_page *metadata_page = sample_buff;
unsigned long data_head, data_tail;
/*
* PERF_RECORD_SAMPLE:
* struct {
* struct perf_event_header hdr;
* u64 data[];
* };
*/
data_head = metadata_page->data_head;
/* sync memory before reading sample */
mb();
data_tail = metadata_page->data_tail;
/* Check for sample_count */
if (sample_count)
*sample_count = 0;
while (1) {
/*
* Reads the mmap data buffer by moving
* the data_tail to know the last read data.
* data_head points to head in data buffer.
* refer "struct perf_event_mmap_page" in
* "include/uapi/linux/perf_event.h".
*/
if (data_head - data_tail < sizeof(header))
return NULL;
data_tail += sizeof(header);
if (header->type == PERF_RECORD_SAMPLE) {
*size = (header->size - sizeof(header));
if (!sample_count)
return sample_buff + page_size + data_tail;
data_tail += *size;
*sample_count += 1;
} else {
*size = (header->size - sizeof(header));
if ((metadata_page->data_tail + *size) > metadata_page->data_head)
data_tail = metadata_page->data_head;
else
data_tail += *size;
}
header = (struct perf_event_header *)((void *)header + header->size);
}
return NULL;
}
int collect_samples(void *sample_buff)
{
u64 sample_count;
size_t size = 0;
__event_read_samples(sample_buff, &size, &sample_count);
return sample_count;
}
static void *perf_read_first_sample(void *sample_buff, size_t *size)
{
return __event_read_samples(sample_buff, size, NULL);
}
u64 *get_intr_regs(struct event *event, void *sample_buff)
{
u64 type = event->attr.sample_type;
u64 *intr_regs;
size_t size = 0;
if ((type ^ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_BRANCH_STACK)) &&
(type ^ PERF_SAMPLE_REGS_INTR))
return NULL;
intr_regs = (u64 *)perf_read_first_sample(sample_buff, &size);
if (!intr_regs)
return NULL;
if (type & PERF_SAMPLE_BRANCH_STACK) {
/*
* PERF_RECORD_SAMPLE and PERF_SAMPLE_BRANCH_STACK:
* struct {
* struct perf_event_header hdr;
* u64 number_of_branches;
* struct perf_branch_entry[number_of_branches];
* u64 data[];
* };
* struct perf_branch_entry {
* u64 from;
* u64 to;
* u64 misc;
* };
*/
intr_regs += ((*intr_regs) * 3) + 1;
}
/*
* First entry in the sample buffer used to specify
* PERF_SAMPLE_REGS_ABI_64, skip perf regs abi to access
* interrupt registers.
*/
++intr_regs;
return intr_regs;
}
static const int __perf_reg_mask(const char *register_name)
{
if (!strcmp(register_name, "R0"))
return 0;
else if (!strcmp(register_name, "R1"))
return 1;
else if (!strcmp(register_name, "R2"))
return 2;
else if (!strcmp(register_name, "R3"))
return 3;
else if (!strcmp(register_name, "R4"))
return 4;
else if (!strcmp(register_name, "R5"))
return 5;
else if (!strcmp(register_name, "R6"))
return 6;
else if (!strcmp(register_name, "R7"))
return 7;
else if (!strcmp(register_name, "R8"))
return 8;
else if (!strcmp(register_name, "R9"))
return 9;
else if (!strcmp(register_name, "R10"))
return 10;
else if (!strcmp(register_name, "R11"))
return 11;
else if (!strcmp(register_name, "R12"))
return 12;
else if (!strcmp(register_name, "R13"))
return 13;
else if (!strcmp(register_name, "R14"))
return 14;
else if (!strcmp(register_name, "R15"))
return 15;
else if (!strcmp(register_name, "R16"))
return 16;
else if (!strcmp(register_name, "R17"))
return 17;
else if (!strcmp(register_name, "R18"))
return 18;
else if (!strcmp(register_name, "R19"))
return 19;
else if (!strcmp(register_name, "R20"))
return 20;
else if (!strcmp(register_name, "R21"))
return 21;
else if (!strcmp(register_name, "R22"))
return 22;
else if (!strcmp(register_name, "R23"))
return 23;
else if (!strcmp(register_name, "R24"))
return 24;
else if (!strcmp(register_name, "R25"))
return 25;
else if (!strcmp(register_name, "R26"))
return 26;
else if (!strcmp(register_name, "R27"))
return 27;
else if (!strcmp(register_name, "R28"))
return 28;
else if (!strcmp(register_name, "R29"))
return 29;
else if (!strcmp(register_name, "R30"))
return 30;
else if (!strcmp(register_name, "R31"))
return 31;
else if (!strcmp(register_name, "NIP"))
return 32;
else if (!strcmp(register_name, "MSR"))
return 33;
else if (!strcmp(register_name, "ORIG_R3"))
return 34;
else if (!strcmp(register_name, "CTR"))
return 35;
else if (!strcmp(register_name, "LINK"))
return 36;
else if (!strcmp(register_name, "XER"))
return 37;
else if (!strcmp(register_name, "CCR"))
return 38;
else if (!strcmp(register_name, "SOFTE"))
return 39;
else if (!strcmp(register_name, "TRAP"))
return 40;
else if (!strcmp(register_name, "DAR"))
return 41;
else if (!strcmp(register_name, "DSISR"))
return 42;
else if (!strcmp(register_name, "SIER"))
return 43;
else if (!strcmp(register_name, "MMCRA"))
return 44;
else if (!strcmp(register_name, "MMCR0"))
return 45;
else if (!strcmp(register_name, "MMCR1"))
return 46;
else if (!strcmp(register_name, "MMCR2"))
return 47;
else if (!strcmp(register_name, "MMCR3"))
return 48;
else if (!strcmp(register_name, "SIER2"))
return 49;
else if (!strcmp(register_name, "SIER3"))
return 50;
else if (!strcmp(register_name, "PMC1"))
return 51;
else if (!strcmp(register_name, "PMC2"))
return 52;
else if (!strcmp(register_name, "PMC3"))
return 53;
else if (!strcmp(register_name, "PMC4"))
return 54;
else if (!strcmp(register_name, "PMC5"))
return 55;
else if (!strcmp(register_name, "PMC6"))
return 56;
else if (!strcmp(register_name, "SDAR"))
return 57;
else if (!strcmp(register_name, "SIAR"))
return 58;
else
return -1;
}
u64 get_reg_value(u64 *intr_regs, char *register_name)
{
int register_bit_position;
register_bit_position = __perf_reg_mask(register_name);
if (register_bit_position < 0 || (!((platform_extended_mask >>
(register_bit_position - 1)) & 1)))
return -1;
return *(intr_regs + register_bit_position);
}
int get_thresh_cmp_val(struct event event)
{
int exp = 0;
u64 result = 0;
u64 value;
if (!have_hwcap2(PPC_FEATURE2_ARCH_3_1))
return EV_CODE_EXTRACT(event.attr.config, thd_cmp);
value = EV_CODE_EXTRACT(event.attr.config1, thd_cmp);
if (!value)
return value;
/*
* Incase of P10, thresh_cmp value is not part of raw event code
* and provided via attr.config1 parameter. To program threshold in MMCRA,
* take a 18 bit number N and shift right 2 places and increment
* the exponent E by 1 until the upper 10 bits of N are zero.
* Write E to the threshold exponent and write the lower 8 bits of N
* to the threshold mantissa.
* The max threshold that can be written is 261120.
*/
if (value > 261120)
value = 261120;
while ((64 - __builtin_clzl(value)) > 8) {
exp++;
value >>= 2;
}
/*
* Note that it is invalid to write a mantissa with the
* upper 2 bits of mantissa being zero, unless the
* exponent is also zero.
*/
if (!(value & 0xC0) && exp)
result = -1;
else
result = (exp << 8) | value;
return result;
}
/*
* Utility function to check for generic compat PMU
* by comparing base_platform value from auxv and real
* PVR value.
* auxv_base_platform() func gives information of "base platform"
* corresponding to PVR value. Incase, if the distro doesn't
* support platform PVR (missing cputable support), base platform
* in auxv will have a default value other than the real PVR's.
* In this case, ISAv3 PMU (generic compat PMU) will be registered
* in the system. auxv_generic_compat_pmu() makes use of the base
* platform value from auxv to do this check.
*/
static bool auxv_generic_compat_pmu(void)
{
int base_pvr = 0;
if (!strcmp(auxv_base_platform(), "power9"))
base_pvr = POWER9;
else if (!strcmp(auxv_base_platform(), "power10"))
base_pvr = POWER10;
else if (!strcmp(auxv_base_platform(), "power11"))
base_pvr = POWER11;
return (!base_pvr);
}
/*
* Check for generic compat PMU.
* First check for presence of pmu_name from
* "/sys/bus/event_source/devices/cpu/caps".
* If doesn't exist, fallback to using value
* auxv.
*/
bool check_for_generic_compat_pmu(void)
{
char pmu_name[256];
memset(pmu_name, 0, sizeof(pmu_name));
if (read_sysfs_file("bus/event_source/devices/cpu/caps/pmu_name",
pmu_name, sizeof(pmu_name)) < 0)
return auxv_generic_compat_pmu();
if (!strcmp(pmu_name, "ISAv3"))
return true;
else
return false;
}
/*
* Check if system is booted in compat mode.
*/
bool check_for_compat_mode(void)
{
char *platform = auxv_platform();
char *base_platform = auxv_base_platform();
return strcmp(platform, base_platform);
}
@@ -0,0 +1,236 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
* Copyright 2022, Madhavan Srinivasan, IBM Corp.
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <sys/stat.h>
#include "../event.h"
#define POWER11 0x82
#define POWER10 0x80
#define POWER9 0x4e
#define PERF_POWER9_MASK 0x7f8ffffffffffff
#define PERF_POWER10_MASK 0x7ffffffffffffff
#define PERF_POWER11_MASK PERF_POWER10_MASK
#define MMCR0_FC56 0x00000010UL /* freeze counters 5 and 6 */
#define MMCR0_PMCCEXT 0x00000200UL /* PMCCEXT control */
#define MMCR1_RSQ 0x200000000000ULL /* radix scope qual field */
#define BHRB_DISABLE 0x2000000000ULL /* MMCRA BHRB DISABLE bit */
extern int ev_mask_pmcxsel, ev_shift_pmcxsel;
extern int ev_mask_marked, ev_shift_marked;
extern int ev_mask_comb, ev_shift_comb;
extern int ev_mask_unit, ev_shift_unit;
extern int ev_mask_pmc, ev_shift_pmc;
extern int ev_mask_cache, ev_shift_cache;
extern int ev_mask_sample, ev_shift_sample;
extern int ev_mask_thd_sel, ev_shift_thd_sel;
extern int ev_mask_thd_start, ev_shift_thd_start;
extern int ev_mask_thd_stop, ev_shift_thd_stop;
extern int ev_mask_thd_cmp, ev_shift_thd_cmp;
extern int ev_mask_sm, ev_shift_sm;
extern int ev_mask_rsq, ev_shift_rsq;
extern int ev_mask_l2l3, ev_shift_l2l3;
extern int ev_mask_mmcr3_src, ev_shift_mmcr3_src;
extern int pvr;
extern u64 platform_extended_mask;
extern int check_pvr_for_sampling_tests(void);
extern int platform_check_for_tests(void);
extern int check_extended_regs_support(void);
extern u64 perf_get_platform_reg_mask(void);
/*
* Event code field extraction macro.
* Raw event code is combination of multiple
* fields. Macro to extract individual fields
*
* x - Raw event code value
* y - Field to extract
*/
#define EV_CODE_EXTRACT(x, y) \
((x >> ev_shift_##y) & ev_mask_##y)
void *event_sample_buf_mmap(int fd, int mmap_pages);
void *__event_read_samples(void *sample_buff, size_t *size, u64 *sample_count);
int collect_samples(void *sample_buff);
u64 *get_intr_regs(struct event *event, void *sample_buff);
u64 get_reg_value(u64 *intr_regs, char *register_name);
int get_thresh_cmp_val(struct event event);
bool check_for_generic_compat_pmu(void);
bool check_for_compat_mode(void);
static inline int get_mmcr0_fc56(u64 mmcr0, int pmc)
{
return (mmcr0 & MMCR0_FC56);
}
static inline int get_mmcr0_pmccext(u64 mmcr0, int pmc)
{
return (mmcr0 & MMCR0_PMCCEXT);
}
static inline int get_mmcr0_pmao(u64 mmcr0, int pmc)
{
return ((mmcr0 >> 7) & 0x1);
}
static inline int get_mmcr0_cc56run(u64 mmcr0, int pmc)
{
return ((mmcr0 >> 8) & 0x1);
}
static inline int get_mmcr0_pmcjce(u64 mmcr0, int pmc)
{
return ((mmcr0 >> 14) & 0x1);
}
static inline int get_mmcr0_pmc1ce(u64 mmcr0, int pmc)
{
return ((mmcr0 >> 15) & 0x1);
}
static inline int get_mmcr0_pmae(u64 mmcr0, int pmc)
{
return ((mmcr0 >> 27) & 0x1);
}
static inline int get_mmcr1_pmcxsel(u64 mmcr1, int pmc)
{
return ((mmcr1 >> ((24 - (((pmc) - 1) * 8))) & 0xff));
}
static inline int get_mmcr1_unit(u64 mmcr1, int pmc)
{
return ((mmcr1 >> ((60 - (4 * ((pmc) - 1))))) & 0xf);
}
static inline int get_mmcr1_comb(u64 mmcr1, int pmc)
{
return ((mmcr1 >> (38 - ((pmc - 1) * 2))) & 0x3);
}
static inline int get_mmcr1_cache(u64 mmcr1, int pmc)
{
return ((mmcr1 >> 46) & 0x3);
}
static inline int get_mmcr1_rsq(u64 mmcr1, int pmc)
{
return mmcr1 & MMCR1_RSQ;
}
static inline int get_mmcr2_fcs(u64 mmcr2, int pmc)
{
return ((mmcr2 & (1ull << (63 - (((pmc) - 1) * 9)))) >> (63 - (((pmc) - 1) * 9)));
}
static inline int get_mmcr2_fcp(u64 mmcr2, int pmc)
{
return ((mmcr2 & (1ull << (62 - (((pmc) - 1) * 9)))) >> (62 - (((pmc) - 1) * 9)));
}
static inline int get_mmcr2_fcpc(u64 mmcr2, int pmc)
{
return ((mmcr2 & (1ull << (61 - (((pmc) - 1) * 9)))) >> (61 - (((pmc) - 1) * 9)));
}
static inline int get_mmcr2_fcm1(u64 mmcr2, int pmc)
{
return ((mmcr2 & (1ull << (60 - (((pmc) - 1) * 9)))) >> (60 - (((pmc) - 1) * 9)));
}
static inline int get_mmcr2_fcm0(u64 mmcr2, int pmc)
{
return ((mmcr2 & (1ull << (59 - (((pmc) - 1) * 9)))) >> (59 - (((pmc) - 1) * 9)));
}
static inline int get_mmcr2_fcwait(u64 mmcr2, int pmc)
{
return ((mmcr2 & (1ull << (58 - (((pmc) - 1) * 9)))) >> (58 - (((pmc) - 1) * 9)));
}
static inline int get_mmcr2_fch(u64 mmcr2, int pmc)
{
return ((mmcr2 & (1ull << (57 - (((pmc) - 1) * 9)))) >> (57 - (((pmc) - 1) * 9)));
}
static inline int get_mmcr2_fcti(u64 mmcr2, int pmc)
{
return ((mmcr2 & (1ull << (56 - (((pmc) - 1) * 9)))) >> (56 - (((pmc) - 1) * 9)));
}
static inline int get_mmcr2_fcta(u64 mmcr2, int pmc)
{
return ((mmcr2 & (1ull << (55 - (((pmc) - 1) * 9)))) >> (55 - (((pmc) - 1) * 9)));
}
static inline int get_mmcr2_l2l3(u64 mmcr2, int pmc)
{
if (have_hwcap2(PPC_FEATURE2_ARCH_3_1))
return ((mmcr2 & 0xf8) >> 3);
return 0;
}
static inline int get_mmcr3_src(u64 mmcr3, int pmc)
{
if (!have_hwcap2(PPC_FEATURE2_ARCH_3_1))
return 0;
return ((mmcr3 >> ((49 - (15 * ((pmc) - 1))))) & 0x7fff);
}
static inline int get_mmcra_thd_cmp(u64 mmcra, int pmc)
{
if (have_hwcap2(PPC_FEATURE2_ARCH_3_1))
return ((mmcra >> 45) & 0x7ff);
return ((mmcra >> 45) & 0x3ff);
}
static inline int get_mmcra_sm(u64 mmcra, int pmc)
{
return ((mmcra >> 42) & 0x3);
}
static inline u64 get_mmcra_bhrb_disable(u64 mmcra, int pmc)
{
if (have_hwcap2(PPC_FEATURE2_ARCH_3_1))
return mmcra & BHRB_DISABLE;
return 0;
}
static inline int get_mmcra_ifm(u64 mmcra, int pmc)
{
return ((mmcra >> 30) & 0x3);
}
static inline int get_mmcra_thd_sel(u64 mmcra, int pmc)
{
return ((mmcra >> 16) & 0x7);
}
static inline int get_mmcra_thd_start(u64 mmcra, int pmc)
{
return ((mmcra >> 12) & 0xf);
}
static inline int get_mmcra_thd_stop(u64 mmcra, int pmc)
{
return ((mmcra >> 8) & 0xf);
}
static inline int get_mmcra_rand_samp_elig(u64 mmcra, int pmc)
{
return ((mmcra >> 4) & 0x7);
}
static inline int get_mmcra_sample_mode(u64 mmcra, int pmc)
{
return ((mmcra >> 1) & 0x3);
}
static inline int get_mmcra_marked(u64 mmcra, int pmc)
{
return mmcra & 0x1;
}
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
/*
* A perf sampling test for mmcr0
* field: cc56run.
*/
static int mmcr0_cc56run(void)
{
struct event event;
u64 *intr_regs;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the event for the sampling test */
event_init_sampling(&event, 0x500fa);
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that cc56run bit is set in MMCR0 */
FAIL_IF(!get_mmcr0_cc56run(get_reg_value(intr_regs, "MMCR0"), 5));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcr0_cc56run, "mmcr0_cc56run");
}
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
/*
* A perf sampling test for mmcr0
* fields : pmae, pmao.
*/
static int mmcr0_exceptionbits(void)
{
struct event event;
u64 *intr_regs;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
/* Init the event for the sampling test */
event_init_sampling(&event, 0x500fa);
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that pmae is cleared and pmao is set in MMCR0 */
FAIL_IF(get_mmcr0_pmae(get_reg_value(intr_regs, "MMCR0"), 5));
FAIL_IF(!get_mmcr0_pmao(get_reg_value(intr_regs, "MMCR0"), 5));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcr0_exceptionbits, "mmcr0_exceptionbits");
}
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
/*
* A perf sampling test for mmcr0
* fields: fc56, pmc1ce.
*/
static int mmcr0_fc56_pmc1ce(void)
{
struct event event;
u64 *intr_regs;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
/* Init the event for the sampling test */
event_init_sampling(&event, 0x1001e);
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that fc56, pmc1ce fields are set in MMCR0 */
FAIL_IF(!get_mmcr0_fc56(get_reg_value(intr_regs, "MMCR0"), 1));
FAIL_IF(!get_mmcr0_pmc1ce(get_reg_value(intr_regs, "MMCR0"), 1));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcr0_fc56_pmc1ce, "mmcr0_fc56_pmc1ce");
}
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
/*
* A perf sampling test for mmcr0
* fields: fc56_pmc56
*/
static int mmcr0_fc56_pmc56(void)
{
struct event event;
u64 *intr_regs;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
/* Init the event for the sampling test */
event_init_sampling(&event, 0x500fa);
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that fc56 is not set in MMCR0 when using PMC5 */
FAIL_IF(get_mmcr0_fc56(get_reg_value(intr_regs, "MMCR0"), 5));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcr0_fc56_pmc56, "mmcr0_fc56_pmc56");
}
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
/*
* A perf sampling test for mmcr0
* field: pmccext
*/
static int mmcr0_pmccext(void)
{
struct event event;
u64 *intr_regs;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the event for the sampling test */
event_init_sampling(&event, 0x4001e);
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that pmccext field is set in MMCR0 */
FAIL_IF(!get_mmcr0_pmccext(get_reg_value(intr_regs, "MMCR0"), 4));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcr0_pmccext, "mmcr0_pmccext");
}
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
/*
* A perf sampling test for mmcr0
* field: pmcjce
*/
static int mmcr0_pmcjce(void)
{
struct event event;
u64 *intr_regs;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
/* Init the event for the sampling test */
event_init_sampling(&event, 0x500fa);
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that pmcjce field is set in MMCR0 */
FAIL_IF(!get_mmcr0_pmcjce(get_reg_value(intr_regs, "MMCR0"), 5));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcr0_pmcjce, "mmcr0_pmcjce");
}
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
/* All successful D-side store dispatches for this thread that were L2 Miss */
#define EventCode 0x46880
extern void thirty_two_instruction_loop_with_ll_sc(u64 loops, u64 *ll_sc_target);
/*
* A perf sampling test for mmcr1
* fields : comb.
*/
static int mmcr1_comb(void)
{
struct event event;
u64 *intr_regs;
u64 dummy;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
/* Init the event for the sampling test */
event_init_sampling(&event, EventCode);
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop_with_ll_sc(10000000, &dummy);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/*
* Verify that comb field match with
* corresponding event code fields
*/
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, comb) !=
get_mmcr1_comb(get_reg_value(intr_regs, "MMCR1"), 4));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcr1_comb, "mmcr1_comb");
}
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Athira Rajeev, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
#define MALLOC_SIZE (0x10000 * 10) /* Ought to be enough .. */
/* The data cache was reloaded from local core's L3 due to a demand load */
#define EventCode 0x21c040
/*
* A perf sampling test for mmcr1
* fields : pmcxsel, unit, cache.
*/
static int mmcr1_sel_unit_cache(void)
{
struct event event;
u64 *intr_regs;
char *p;
int i;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
p = malloc(MALLOC_SIZE);
FAIL_IF(!p);
/* Init the event for the sampling test */
event_init_sampling(&event, EventCode);
event.attr.sample_regs_intr = platform_extended_mask;
event.attr.sample_period = 1;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
event_enable(&event);
/* workload to make the event overflow */
for (i = 0; i < MALLOC_SIZE; i += 0x10000)
p[i] = i;
event_disable(&event);
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/*
* Verify that pmcxsel, unit and cache field of MMCR1
* match with corresponding event code fields
*/
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, pmcxsel) !=
get_mmcr1_pmcxsel(get_reg_value(intr_regs, "MMCR1"), 1));
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, unit) !=
get_mmcr1_unit(get_reg_value(intr_regs, "MMCR1"), 1));
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, cache) !=
get_mmcr1_cache(get_reg_value(intr_regs, "MMCR1"), 1));
free(p);
event_close(&event);
return 0;
}
int main(void)
{
FAIL_IF(test_harness(mmcr1_sel_unit_cache, "mmcr1_sel_unit_cache"));
}
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Madhavan Srinivasan, IBM Corp.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
static bool is_hv;
static void sig_usr2_handler(int signum, siginfo_t *info, void *data)
{
ucontext_t *uctx = data;
is_hv = !!(uctx->uc_mcontext.gp_regs[PT_MSR] & MSR_HV);
}
/*
* A perf sampling test for mmcr2
* fields : fcs, fch.
*/
static int mmcr2_fcs_fch(void)
{
struct sigaction sigact = {
.sa_sigaction = sig_usr2_handler,
.sa_flags = SA_SIGINFO
};
struct event event;
u64 *intr_regs;
FAIL_IF(sigaction(SIGUSR2, &sigact, NULL));
FAIL_IF(kill(getpid(), SIGUSR2));
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
/* Init the event for the sampling test */
event_init_sampling(&event, 0x1001e);
event.attr.sample_regs_intr = platform_extended_mask;
event.attr.exclude_kernel = 1;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/*
* Verify that fcs and fch field of MMCR2 match
* with corresponding modifier fields.
*/
if (is_hv)
FAIL_IF(event.attr.exclude_kernel !=
get_mmcr2_fch(get_reg_value(intr_regs, "MMCR2"), 1));
else
FAIL_IF(event.attr.exclude_kernel !=
get_mmcr2_fcs(get_reg_value(intr_regs, "MMCR2"), 1));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcr2_fcs_fch, "mmcr2_fcs_fch");
}
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Madhavan Srinivasan, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
/* All successful D-side store dispatches for this thread */
#define EventCode 0x010000046080
#define MALLOC_SIZE (0x10000 * 10) /* Ought to be enough .. */
/*
* A perf sampling test for mmcr2
* fields : l2l3
*/
static int mmcr2_l2l3(void)
{
struct event event;
u64 *intr_regs;
char *p;
int i;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the event for the sampling test */
event_init_sampling(&event, EventCode);
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
p = malloc(MALLOC_SIZE);
FAIL_IF(!p);
for (i = 0; i < MALLOC_SIZE; i += 0x10000)
p[i] = i;
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/*
* Verify that l2l3 field of MMCR2 match with
* corresponding event code field
*/
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, l2l3) !=
get_mmcr2_l2l3(get_reg_value(intr_regs, "MMCR2"), 4));
event_close(&event);
free(p);
return 0;
}
int main(void)
{
return test_harness(mmcr2_l2l3, "mmcr2_l2l3");
}
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop_with_ll_sc(u64 loops, u64 *ll_sc_target);
/* The data cache was reloaded from local core's L3 due to a demand load */
#define EventCode 0x1340000001c040
/*
* A perf sampling test for mmcr3
* fields.
*/
static int mmcr3_src(void)
{
struct event event;
u64 *intr_regs;
u64 dummy;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the event for the sampling test */
event_init_sampling(&event, EventCode);
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make event overflow */
thirty_two_instruction_loop_with_ll_sc(1000000, &dummy);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/*
* Verify that src field of MMCR3 match with
* corresponding event code field
*/
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, mmcr3_src) !=
get_mmcr3_src(get_reg_value(intr_regs, "MMCR3"), 1));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcr3_src, "mmcr3_src");
}
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
/* Instructions */
#define EventCode 0x500fa
/* ifm field for any branch mode */
#define IFM_ANY_BRANCH 0x0
/*
* A perf sampling test for mmcra
* field: ifm for bhrb any call.
*/
static int mmcra_bhrb_any_test(void)
{
struct event event;
u64 *intr_regs;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
/* Init the event for the sampling test */
event_init_sampling(&event, EventCode);
event.attr.sample_regs_intr = platform_extended_mask;
event.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
event.attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY;
event.attr.exclude_kernel = 1;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that ifm bit is set properly in MMCRA */
FAIL_IF(get_mmcra_ifm(get_reg_value(intr_regs, "MMCRA"), 5) != IFM_ANY_BRANCH);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcra_bhrb_any_test, "mmcra_bhrb_any_test");
}
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
/* Instructions */
#define EventCode 0x500fa
/* ifm field for conditional branch mode */
#define IFM_COND_BRANCH 0x3
/*
* A perf sampling test for mmcra
* field: ifm for bhrb cond call.
*/
static int mmcra_bhrb_cond_test(void)
{
struct event event;
u64 *intr_regs;
/*
* Check for platform support for the test.
* This test is only aplicable on ISA v3.1
*/
SKIP_IF(check_pvr_for_sampling_tests());
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the event for the sampling test */
event_init_sampling(&event, EventCode);
event.attr.sample_regs_intr = platform_extended_mask;
event.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
event.attr.branch_sample_type = PERF_SAMPLE_BRANCH_COND;
event.attr.exclude_kernel = 1;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that ifm bit is set properly in MMCRA */
FAIL_IF(get_mmcra_ifm(get_reg_value(intr_regs, "MMCRA"), 5) != IFM_COND_BRANCH);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcra_bhrb_cond_test, "mmcra_bhrb_cond_test");
}
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
/* Instructions */
#define EventCode 0x500fa
/*
* A perf sampling test for mmcra
* field: bhrb_disable.
*/
static int mmcra_bhrb_disable_no_branch_test(void)
{
struct event event;
u64 *intr_regs;
/*
* Check for platform support for the test.
* This test is only aplicable on ISA v3.1
*/
SKIP_IF(check_pvr_for_sampling_tests());
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the event for the sampling test */
event_init_sampling(&event, EventCode);
event.attr.sample_regs_intr = platform_extended_mask;
event.attr.exclude_kernel = 1;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that bhrb_disable bit is set in MMCRA for non-branch samples */
FAIL_IF(!get_mmcra_bhrb_disable(get_reg_value(intr_regs, "MMCRA"), 5));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcra_bhrb_disable_no_branch_test, "mmcra_bhrb_disable_no_branch_test");
}
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void thirty_two_instruction_loop(int loops);
/* Instructions */
#define EventCode 0x500fa
/*
* A perf sampling test for mmcra
* field: bhrb_disable.
*/
static int mmcra_bhrb_disable_test(void)
{
struct event event;
u64 *intr_regs;
/*
* Check for platform support for the test.
* This test is only aplicable on ISA v3.1
*/
SKIP_IF(check_pvr_for_sampling_tests());
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the event for the sampling test */
event_init_sampling(&event, EventCode);
event.attr.sample_regs_intr = platform_extended_mask;
event.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
event.attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY;
event.attr.exclude_kernel = 1;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop(10000);
FAIL_IF(event_disable(&event));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that bhrb_disable bit is set in MMCRA */
FAIL_IF(get_mmcra_bhrb_disable(get_reg_value(intr_regs, "MMCRA"), 5));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcra_bhrb_disable_test, "mmcra_bhrb_disable_test");
}
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
extern void indirect_branch_loop(void);
/* Instructions */
#define EventCode 0x500fa
/* ifm field for indirect branch mode */
#define IFM_IND_BRANCH 0x2
/*
* A perf sampling test for mmcra
* field: ifm for bhrb ind_call.
*/
static int mmcra_bhrb_ind_call_test(void)
{
struct event event;
u64 *intr_regs;
/*
* Check for platform support for the test.
* This test is only aplicable on ISA v3.1
*/
SKIP_IF(check_pvr_for_sampling_tests());
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
/* Init the event for the sampling test */
event_init_sampling(&event, EventCode);
event.attr.sample_regs_intr = platform_extended_mask;
event.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
event.attr.branch_sample_type = PERF_SAMPLE_BRANCH_IND_CALL;
event.attr.exclude_kernel = 1;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
indirect_branch_loop();
FAIL_IF(event_disable(&event));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that ifm bit is set properly in MMCRA */
FAIL_IF(get_mmcra_ifm(get_reg_value(intr_regs, "MMCRA"), 5) != IFM_IND_BRANCH);
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcra_bhrb_ind_call_test, "mmcra_bhrb_ind_call_test");
}
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
/*
* Primary PMU event used here is PM_MRK_INST_CMPL (0x401e0)
* Threshold event selection used is issue to complete for cycles
* Sampling criteria is Load only sampling
*/
#define p9_EventCode 0x13E35340401e0
#define p10_EventCode 0x35340401e0
extern void thirty_two_instruction_loop_with_ll_sc(u64 loops, u64 *ll_sc_target);
/* A perf sampling test to test mmcra fields */
static int mmcra_thresh_cmp(void)
{
struct event event;
u64 *intr_regs;
u64 dummy;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
/* Skip for comapt mode */
SKIP_IF(check_for_compat_mode());
/* Init the event for the sampling test */
if (!have_hwcap2(PPC_FEATURE2_ARCH_3_1)) {
event_init_sampling(&event, p9_EventCode);
} else {
event_init_sampling(&event, p10_EventCode);
event.attr.config1 = 1000;
}
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop_with_ll_sc(1000000, &dummy);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/* Verify that thresh cmp match with the corresponding event code fields */
FAIL_IF(get_thresh_cmp_val(event) !=
get_mmcra_thd_cmp(get_reg_value(intr_regs, "MMCRA"), 4));
event_close(&event);
return 0;
}
int main(void)
{
FAIL_IF(test_harness(mmcra_thresh_cmp, "mmcra_thresh_cmp"));
}
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022, Kajol Jain, IBM Corp.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../event.h"
#include "misc.h"
#include "utils.h"
/*
* Primary PMU event used here is PM_MRK_INST_CMPL (0x401e0)
* Threshold event selection used is issue to complete for cycles
* Sampling criteria is Load only sampling
*/
#define EventCode 0x35340401e0
extern void thirty_two_instruction_loop_with_ll_sc(u64 loops, u64 *ll_sc_target);
/* A perf sampling test to test mmcra fields */
static int mmcra_thresh_marked_sample(void)
{
struct event event;
u64 *intr_regs;
u64 dummy;
/* Check for platform support for the test */
SKIP_IF(check_pvr_for_sampling_tests());
/* Init the event for the sampling test */
event_init_sampling(&event, EventCode);
event.attr.sample_regs_intr = platform_extended_mask;
FAIL_IF(event_open(&event));
event.mmap_buffer = event_sample_buf_mmap(event.fd, 1);
FAIL_IF(event_enable(&event));
/* workload to make the event overflow */
thirty_two_instruction_loop_with_ll_sc(1000000, &dummy);
FAIL_IF(event_disable(&event));
/* Check for sample count */
FAIL_IF(!collect_samples(event.mmap_buffer));
intr_regs = get_intr_regs(&event, event.mmap_buffer);
/* Check for intr_regs */
FAIL_IF(!intr_regs);
/*
* Verify that thresh sel/start/stop, marked, random sample
* eligibility, sdar mode and sample mode fields match with
* the corresponding event code fields
*/
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, thd_sel) !=
get_mmcra_thd_sel(get_reg_value(intr_regs, "MMCRA"), 4));
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, thd_start) !=
get_mmcra_thd_start(get_reg_value(intr_regs, "MMCRA"), 4));
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, thd_stop) !=
get_mmcra_thd_stop(get_reg_value(intr_regs, "MMCRA"), 4));
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, marked) !=
get_mmcra_marked(get_reg_value(intr_regs, "MMCRA"), 4));
FAIL_IF((EV_CODE_EXTRACT(event.attr.config, sample) >> 2) !=
get_mmcra_rand_samp_elig(get_reg_value(intr_regs, "MMCRA"), 4));
FAIL_IF((EV_CODE_EXTRACT(event.attr.config, sample) & 0x3) !=
get_mmcra_sample_mode(get_reg_value(intr_regs, "MMCRA"), 4));
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, sm) !=
get_mmcra_sm(get_reg_value(intr_regs, "MMCRA"), 4));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(mmcra_thresh_marked_sample, "mmcra_thresh_marked_sample");
}
@@ -0,0 +1 @@
timeout=130