Commit 3925f46b authored by Anshuman Khandual's avatar Anshuman Khandual Committed by Benjamin Herrenschmidt

powerpc/perf: Enable branch stack sampling framework

Provides basic enablement for perf branch stack sampling framework on
POWER8 processor based platforms. Adds new BHRB related elements into
cpu_hw_event structure to represent current BHRB config, BHRB filter
configuration, manage context and to hold output BHRB buffer during
PMU interrupt before passing to the user space. This also enables
processing of BHRB data and converts them into generic perf branch
stack data format.
Signed-off-by: default avatarAnshuman Khandual <khandual@linux.vnet.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent b1113557
...@@ -73,6 +73,7 @@ extern int register_power_pmu(struct power_pmu *); ...@@ -73,6 +73,7 @@ extern int register_power_pmu(struct power_pmu *);
struct pt_regs; struct pt_regs;
extern unsigned long perf_misc_flags(struct pt_regs *regs); extern unsigned long perf_misc_flags(struct pt_regs *regs);
extern unsigned long perf_instruction_pointer(struct pt_regs *regs); extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
extern unsigned long int read_bhrb(int n);
/* /*
* Only override the default definitions in include/linux/perf_event.h * Only override the default definitions in include/linux/perf_event.h
......
...@@ -19,6 +19,11 @@ ...@@ -19,6 +19,11 @@
#include <asm/firmware.h> #include <asm/firmware.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#define BHRB_MAX_ENTRIES 32
#define BHRB_TARGET 0x0000000000000002
#define BHRB_PREDICTION 0x0000000000000001
#define BHRB_EA 0xFFFFFFFFFFFFFFFC
struct cpu_hw_events { struct cpu_hw_events {
int n_events; int n_events;
int n_percpu; int n_percpu;
...@@ -38,7 +43,15 @@ struct cpu_hw_events { ...@@ -38,7 +43,15 @@ struct cpu_hw_events {
unsigned int group_flag; unsigned int group_flag;
int n_txn_start; int n_txn_start;
/* BHRB bits */
u64 bhrb_filter; /* BHRB HW branch filter */
int bhrb_users;
void *bhrb_context;
struct perf_branch_stack bhrb_stack;
struct perf_branch_entry bhrb_entries[BHRB_MAX_ENTRIES];
}; };
DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
struct power_pmu *ppmu; struct power_pmu *ppmu;
...@@ -858,6 +871,9 @@ static void power_pmu_enable(struct pmu *pmu) ...@@ -858,6 +871,9 @@ static void power_pmu_enable(struct pmu *pmu)
} }
out: out:
if (cpuhw->bhrb_users)
ppmu->config_bhrb(cpuhw->bhrb_filter);
local_irq_restore(flags); local_irq_restore(flags);
} }
...@@ -888,6 +904,47 @@ static int collect_events(struct perf_event *group, int max_count, ...@@ -888,6 +904,47 @@ static int collect_events(struct perf_event *group, int max_count,
return n; return n;
} }
/* Reset all possible BHRB entries */
static void power_pmu_bhrb_reset(void)
{
asm volatile(PPC_CLRBHRB);
}
void power_pmu_bhrb_enable(struct perf_event *event)
{
struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
if (!ppmu->bhrb_nr)
return;
/* Clear BHRB if we changed task context to avoid data leaks */
if (event->ctx->task && cpuhw->bhrb_context != event->ctx) {
power_pmu_bhrb_reset();
cpuhw->bhrb_context = event->ctx;
}
cpuhw->bhrb_users++;
}
void power_pmu_bhrb_disable(struct perf_event *event)
{
struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
if (!ppmu->bhrb_nr)
return;
cpuhw->bhrb_users--;
WARN_ON_ONCE(cpuhw->bhrb_users < 0);
if (!cpuhw->disabled && !cpuhw->bhrb_users) {
/* BHRB cannot be turned off when other
* events are active on the PMU.
*/
/* avoid stale pointer */
cpuhw->bhrb_context = NULL;
}
}
/* /*
* Add a event to the PMU. * Add a event to the PMU.
* If all events are not already frozen, then we disable and * If all events are not already frozen, then we disable and
...@@ -947,6 +1004,9 @@ static int power_pmu_add(struct perf_event *event, int ef_flags) ...@@ -947,6 +1004,9 @@ static int power_pmu_add(struct perf_event *event, int ef_flags)
ret = 0; ret = 0;
out: out:
if (has_branch_stack(event))
power_pmu_bhrb_enable(event);
perf_pmu_enable(event->pmu); perf_pmu_enable(event->pmu);
local_irq_restore(flags); local_irq_restore(flags);
return ret; return ret;
...@@ -999,6 +1059,9 @@ static void power_pmu_del(struct perf_event *event, int ef_flags) ...@@ -999,6 +1059,9 @@ static void power_pmu_del(struct perf_event *event, int ef_flags)
cpuhw->mmcr[0] &= ~(MMCR0_PMXE | MMCR0_FCECE); cpuhw->mmcr[0] &= ~(MMCR0_PMXE | MMCR0_FCECE);
} }
if (has_branch_stack(event))
power_pmu_bhrb_disable(event);
perf_pmu_enable(event->pmu); perf_pmu_enable(event->pmu);
local_irq_restore(flags); local_irq_restore(flags);
} }
...@@ -1117,6 +1180,15 @@ int power_pmu_commit_txn(struct pmu *pmu) ...@@ -1117,6 +1180,15 @@ int power_pmu_commit_txn(struct pmu *pmu)
return 0; return 0;
} }
/* Called from ctxsw to prevent one process's branch entries to
* mingle with the other process's entries during context switch.
*/
void power_pmu_flush_branch_stack(void)
{
if (ppmu->bhrb_nr)
power_pmu_bhrb_reset();
}
/* /*
* Return 1 if we might be able to put event on a limited PMC, * Return 1 if we might be able to put event on a limited PMC,
* or 0 if not. * or 0 if not.
...@@ -1231,9 +1303,11 @@ static int power_pmu_event_init(struct perf_event *event) ...@@ -1231,9 +1303,11 @@ static int power_pmu_event_init(struct perf_event *event)
if (!ppmu) if (!ppmu)
return -ENOENT; return -ENOENT;
/* does not support taken branch sampling */ if (has_branch_stack(event)) {
if (has_branch_stack(event)) /* PMU has BHRB enabled */
return -EOPNOTSUPP; if (!(ppmu->flags & PPMU_BHRB))
return -EOPNOTSUPP;
}
switch (event->attr.type) { switch (event->attr.type) {
case PERF_TYPE_HARDWARE: case PERF_TYPE_HARDWARE:
...@@ -1314,6 +1388,15 @@ static int power_pmu_event_init(struct perf_event *event) ...@@ -1314,6 +1388,15 @@ static int power_pmu_event_init(struct perf_event *event)
cpuhw = &get_cpu_var(cpu_hw_events); cpuhw = &get_cpu_var(cpu_hw_events);
err = power_check_constraints(cpuhw, events, cflags, n + 1); err = power_check_constraints(cpuhw, events, cflags, n + 1);
if (has_branch_stack(event)) {
cpuhw->bhrb_filter = ppmu->bhrb_filter_map(
event->attr.branch_sample_type);
if(cpuhw->bhrb_filter == -1)
return -EOPNOTSUPP;
}
put_cpu_var(cpu_hw_events); put_cpu_var(cpu_hw_events);
if (err) if (err)
return -EINVAL; return -EINVAL;
...@@ -1372,8 +1455,79 @@ struct pmu power_pmu = { ...@@ -1372,8 +1455,79 @@ struct pmu power_pmu = {
.cancel_txn = power_pmu_cancel_txn, .cancel_txn = power_pmu_cancel_txn,
.commit_txn = power_pmu_commit_txn, .commit_txn = power_pmu_commit_txn,
.event_idx = power_pmu_event_idx, .event_idx = power_pmu_event_idx,
.flush_branch_stack = power_pmu_flush_branch_stack,
}; };
/* Processing BHRB entries */
void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
{
u64 val;
u64 addr;
int r_index, u_index, target, pred;
r_index = 0;
u_index = 0;
while (r_index < ppmu->bhrb_nr) {
/* Assembly read function */
val = read_bhrb(r_index);
/* Terminal marker: End of valid BHRB entries */
if (val == 0) {
break;
} else {
/* BHRB field break up */
addr = val & BHRB_EA;
pred = val & BHRB_PREDICTION;
target = val & BHRB_TARGET;
/* Probable Missed entry: Not applicable for POWER8 */
if ((addr == 0) && (target == 0) && (pred == 1)) {
r_index++;
continue;
}
/* Real Missed entry: Power8 based missed entry */
if ((addr == 0) && (target == 1) && (pred == 1)) {
r_index++;
continue;
}
/* Reserved condition: Not a valid entry */
if ((addr == 0) && (target == 1) && (pred == 0)) {
r_index++;
continue;
}
/* Is a target address */
if (val & BHRB_TARGET) {
/* First address cannot be a target address */
if (r_index == 0) {
r_index++;
continue;
}
/* Update target address for the previous entry */
cpuhw->bhrb_entries[u_index - 1].to = addr;
cpuhw->bhrb_entries[u_index - 1].mispred = pred;
cpuhw->bhrb_entries[u_index - 1].predicted = ~pred;
/* Dont increment u_index */
r_index++;
} else {
/* Update address, flags for current entry */
cpuhw->bhrb_entries[u_index].from = addr;
cpuhw->bhrb_entries[u_index].mispred = pred;
cpuhw->bhrb_entries[u_index].predicted = ~pred;
/* Successfully popullated one entry */
u_index++;
r_index++;
}
}
}
cpuhw->bhrb_stack.nr = u_index;
return;
}
/* /*
* A counter has overflowed; update its count and record * A counter has overflowed; update its count and record
...@@ -1433,6 +1587,13 @@ static void record_and_restart(struct perf_event *event, unsigned long val, ...@@ -1433,6 +1587,13 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
if (event->attr.sample_type & PERF_SAMPLE_ADDR) if (event->attr.sample_type & PERF_SAMPLE_ADDR)
perf_get_data_addr(regs, &data.addr); perf_get_data_addr(regs, &data.addr);
if (event->attr.sample_type & PERF_SAMPLE_BRANCH_STACK) {
struct cpu_hw_events *cpuhw;
cpuhw = &__get_cpu_var(cpu_hw_events);
power_pmu_bhrb_read(cpuhw);
data.br_stack = &cpuhw->bhrb_stack;
}
if (perf_event_overflow(event, &data, regs)) if (perf_event_overflow(event, &data, regs))
power_pmu_stop(event, 0); power_pmu_stop(event, 0);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment