diff options
author | Michael Neuling <mikey@neuling.org> | 2013-05-13 18:44:58 +0000 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-05-14 16:00:22 +1000 |
commit | 691231846cebfe1fbbcf898c8af17a569dbb5463 (patch) | |
tree | edb500ca27ae25cfdae8526a9efb884143a12f5c /arch/powerpc | |
parent | 506e70d13236dfdb0bc15e0914298ab0a7b7f4df (diff) |
powerpc/perf: Fix setting of "to" addresses for BHRB
Currently we only set the "to" address in the branch stack when the CPU
explicitly gives us a value. Unfortunately it only does this for XL form
branches (eg blr, bctr, bctar) and not I and B form branches (eg b, bc).
Fortunately if we read the instruction from memory we can extract the offset of
a branch and calculate the target address.
This adds a function power_pmu_bhrb_to() to calculate the target/to address of
the corresponding I and B form branches. It handles branches in both user and
kernel spaces. It also plumbs this into the perf brhb reading code.
Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/perf/core-book3s.c | 31 |
1 files changed, 30 insertions, 1 deletions
diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 3fdfe4575b8f..426180b84978 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -13,11 +13,13 @@ #include <linux/perf_event.h> #include <linux/percpu.h> #include <linux/hardirq.h> +#include <linux/uaccess.h> #include <asm/reg.h> #include <asm/pmc.h> #include <asm/machdep.h> #include <asm/firmware.h> #include <asm/ptrace.h> +#include <asm/code-patching.h> #define BHRB_MAX_ENTRIES 32 #define BHRB_TARGET 0x0000000000000002 @@ -362,6 +364,32 @@ void power_pmu_flush_branch_stack(void) if (ppmu->bhrb_nr) power_pmu_bhrb_reset(); } +/* Calculate the to address for a branch */ +static __u64 power_pmu_bhrb_to(u64 addr) +{ + unsigned int instr; + int ret; + __u64 target; + + if (is_kernel_addr(addr)) + return branch_target((unsigned int *)addr); + + /* Userspace: need copy instruction here then translate it */ + pagefault_disable(); + ret = __get_user_inatomic(instr, (unsigned int __user *)addr); + if (ret) { + pagefault_enable(); + return 0; + } + pagefault_enable(); + + target = branch_target(&instr); + if ((!target) || (instr & BRANCH_ABSOLUTE)) + return target; + + /* Translate relative branch target from kernel to user address */ + return target - (unsigned long)&instr + addr; +} /* Processing BHRB entries */ void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) @@ -426,7 +454,8 @@ void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) /* Branches to immediate field (ie I or B form) */ cpuhw->bhrb_entries[u_index].from = addr; - cpuhw->bhrb_entries[u_index].to = 0; + cpuhw->bhrb_entries[u_index].to = + power_pmu_bhrb_to(addr); cpuhw->bhrb_entries[u_index].mispred = pred; cpuhw->bhrb_entries[u_index].predicted = ~pred; } |