Skip to content

Commit

Permalink
selftests/powerpc: Add tests of PMU EBBs
Browse files Browse the repository at this point in the history
The Power8 Performance Monitor Unit (PMU) has a new feature called Event
Based Branches (EBB). This commit adds tests of the kernel API for using
EBBs.

Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
  • Loading branch information
mpe authored and ozbenh committed Jun 11, 2014
1 parent 33b4819 commit 3752e45
Show file tree
Hide file tree
Showing 34 changed files with 3,913 additions and 4 deletions.
26 changes: 22 additions & 4 deletions tools/testing/selftests/powerpc/pmu/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,38 @@ noarg:
PROGS := count_instructions
EXTRA_SOURCES := ../harness.c event.c

all: $(PROGS)
all: $(PROGS) sub_all

$(PROGS): $(EXTRA_SOURCES)

# loop.S can only be built 64-bit
count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES)
$(CC) $(CFLAGS) -m64 -o $@ $^

run_tests: all
run_tests: all sub_run_tests
@-for PROG in $(PROGS); do \
./$$PROG; \
done;

clean:
clean: sub_clean
rm -f $(PROGS) loop.o

.PHONY: all run_tests clean

SUB_TARGETS = ebb

sub_all:
@for TARGET in $(SUB_TARGETS); do \
$(MAKE) -C $$TARGET all; \
done;

sub_run_tests: all
@for TARGET in $(SUB_TARGETS); do \
$(MAKE) -C $$TARGET run_tests; \
done;

sub_clean:
@for TARGET in $(SUB_TARGETS); do \
$(MAKE) -C $$TARGET clean; \
done;

.PHONY: all run_tests clean sub_all sub_run_tests sub_clean
32 changes: 32 additions & 0 deletions tools/testing/selftests/powerpc/pmu/ebb/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
noarg:
$(MAKE) -C ../../

# The EBB handler is 64-bit code and everything links against it
CFLAGS += -m64

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

all: $(PROGS)

$(PROGS): ../../harness.c ../event.c ../lib.c ebb.c ebb_handler.S trace.c

instruction_count_test: ../loop.S

lost_exception_test: ../lib.c

run_tests: all
@-for PROG in $(PROGS); do \
./$$PROG; \
done;

clean:
rm -f $(PROGS)
106 changes: 106 additions & 0 deletions tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/

#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;

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();

count_pmc(1, sample_period);

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");
}
59 changes: 59 additions & 0 deletions tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/

#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;

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");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/

#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;

cpu = pick_online_cpu();
FAIL_IF(cpu < 0);
FAIL_IF(bind_to_cpu(cpu));

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");
}
Loading

0 comments on commit 3752e45

Please sign in to comment.