You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
selftests/powerpc: Add tests of PMU EBBs
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>
This commit is contained in:
committed by
Benjamin Herrenschmidt
parent
33b4819f3b
commit
3752e453f6
@@ -4,7 +4,7 @@ noarg:
|
||||
PROGS := count_instructions
|
||||
EXTRA_SOURCES := ../harness.c event.c
|
||||
|
||||
all: $(PROGS)
|
||||
all: $(PROGS) sub_all
|
||||
|
||||
$(PROGS): $(EXTRA_SOURCES)
|
||||
|
||||
@@ -12,12 +12,30 @@ $(PROGS): $(EXTRA_SOURCES)
|
||||
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
tools/testing/selftests/powerpc/pmu/ebb/Makefile
Normal file
32
tools/testing/selftests/powerpc/pmu/ebb/Makefile
Normal file
@@ -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
tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
Normal file
106
tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
Normal file
@@ -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");
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 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;
|
||||
|
||||
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 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");
|
||||
}
|
||||
58
tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
Normal file
58
tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2014, Michael Ellerman, IBM Corp.
|
||||
* Licensed under GPLv2.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ebb.h"
|
||||
|
||||
|
||||
/*
|
||||
* Basic test that counts user cycles and takes EBBs.
|
||||
*/
|
||||
int cycles(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));
|
||||
|
||||
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();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
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 @@
|
||||
/*
|
||||
* Copyright 2014, Michael Ellerman, IBM Corp.
|
||||
* Licensed under GPLv2.
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
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();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
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");
|
||||
}
|
||||
727
tools/testing/selftests/powerpc/pmu/ebb/ebb.c
Normal file
727
tools/testing/selftests/powerpc/pmu/ebb/ebb.c
Normal file
File diff suppressed because it is too large
Load Diff
78
tools/testing/selftests/powerpc/pmu/ebb/ebb.h
Normal file
78
tools/testing/selftests/powerpc/pmu/ebb/ebb.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2014, Michael Ellerman, IBM Corp.
|
||||
* Licensed under GPLv2.
|
||||
*/
|
||||
|
||||
#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);
|
||||
void ebb_freeze_pmcs(void);
|
||||
void ebb_unfreeze_pmcs(void);
|
||||
void event_ebb_init(struct event *e);
|
||||
void event_leader_ebb_init(struct event *e);
|
||||
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 core_busy_loop_with_freeze(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 */
|
||||
365
tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
Normal file
365
tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
Normal file
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* Copyright 2014, Michael Ellerman, IBM Corp.
|
||||
* Licensed under GPLv2.
|
||||
*/
|
||||
|
||||
#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)
|
||||
86
tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
Normal file
86
tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 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;
|
||||
|
||||
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 @@
|
||||
/*
|
||||
* 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 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();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
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;
|
||||
|
||||
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,86 @@
|
||||
/*
|
||||
* 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 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;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/* 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");
|
||||
}
|
||||
131
tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
Normal file
131
tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2014, Michael Ellerman, IBM Corp.
|
||||
* Licensed under GPLv2.
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
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,43 @@
|
||||
/*
|
||||
* Copyright 2014, Michael Ellerman, IBM Corp.
|
||||
* Licensed under GPLv2.
|
||||
*/
|
||||
|
||||
#include <ppc-asm.h>
|
||||
|
||||
.text
|
||||
|
||||
FUNC_START(thirty_two_instruction_loop)
|
||||
cmpwi 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)
|
||||
79
tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
Normal file
79
tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 <setjmp.h>
|
||||
#include <signal.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;
|
||||
|
||||
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");
|
||||
}
|
||||
164
tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
Normal file
164
tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2014, Michael Ellerman, IBM Corp.
|
||||
* Licensed under GPLv2.
|
||||
*/
|
||||
|
||||
#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("Error %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;
|
||||
|
||||
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)
|
||||
{
|
||||
return test_harness(instruction_count, "instruction_count");
|
||||
}
|
||||
100
tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
Normal file
100
tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2014, Michael Ellerman, IBM Corp.
|
||||
* Licensed under GPLv2.
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
/* 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();
|
||||
|
||||
count_pmc(4, sample_period);
|
||||
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)
|
||||
{
|
||||
return test_harness(lost_exception, "lost_exception");
|
||||
}
|
||||
91
tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
Normal file
91
tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2014, Michael Ellerman, IBM Corp.
|
||||
* Licensed under GPLv2.
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
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();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
count_pmc(2, sample_period);
|
||||
count_pmc(3, sample_period);
|
||||
count_pmc(4, sample_period);
|
||||
count_pmc(5, sample_period);
|
||||
count_pmc(6, sample_period);
|
||||
|
||||
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");
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user