Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3752e453 authored by Michael Ellerman's avatar Michael Ellerman Committed by Benjamin Herrenschmidt
Browse files

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: default avatarMichael Ellerman <mpe@ellerman.id.au>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 33b4819f
Loading
Loading
Loading
Loading
+22 −4
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
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 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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");
}
+93 −0
Original line number Diff line number Diff line
/*
 * 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