summaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/cpu
diff options
context:
space:
mode:
authorRobert Richter <robert.richter@amd.com>2012-04-02 20:19:11 +0200
committerIngo Molnar <mingo@kernel.org>2012-05-09 15:23:13 +0200
commitd47e8238cd76f1ffa7c8cd30e08b8e9074fd597e (patch)
treec172174db097b3afd7b36cc90a5b790e35a9f5f6 /arch/x86/kernel/cpu
parent6accb9cf76080422d400a641d9068b6b2a2c216f (diff)
perf/x86-ibs: Take instruction pointer from ibs sample
Each IBS sample contains a linear address of the instruction that caused the sample to trigger. This address is more precise than the rip that was taken from the interrupt handler's stack. Update the rip with that address. We use this in the next patch to implement precise-event sampling on AMD systems using IBS. Signed-off-by: Robert Richter <robert.richter@amd.com> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: http://lkml.kernel.org/r/1333390758-10893-6-git-send-email-robert.richter@amd.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel/cpu')
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd_ibs.c48
1 files changed, 31 insertions, 17 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
index bc401bd9f14..cc1f3293d6c 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
@@ -9,6 +9,7 @@
#include <linux/perf_event.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/ptrace.h>
#include <asm/apic.h>
@@ -382,7 +383,7 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
struct perf_raw_record raw;
struct pt_regs regs;
struct perf_ibs_data ibs_data;
- int offset, size, overflow, reenable;
+ int offset, size, check_rip, offset_max, throttle = 0;
unsigned int msr;
u64 *buf, config;
@@ -413,28 +414,41 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
perf_ibs_event_update(perf_ibs, event, config);
perf_sample_data_init(&data, 0, hwc->last_period);
+ if (!perf_ibs_set_period(perf_ibs, hwc, &config))
+ goto out; /* no sw counter overflow */
+
+ ibs_data.caps = ibs_caps;
+ size = 1;
+ offset = 1;
+ check_rip = (perf_ibs == &perf_ibs_op && (ibs_caps & IBS_CAPS_RIPINVALIDCHK));
+ if (event->attr.sample_type & PERF_SAMPLE_RAW)
+ offset_max = perf_ibs->offset_max;
+ else if (check_rip)
+ offset_max = 2;
+ else
+ offset_max = 1;
+ do {
+ rdmsrl(msr + offset, *buf++);
+ size++;
+ offset = find_next_bit(perf_ibs->offset_mask,
+ perf_ibs->offset_max,
+ offset + 1);
+ } while (offset < offset_max);
+ ibs_data.size = sizeof(u64) * size;
+
+ regs = *iregs;
+ if (!check_rip || !(ibs_data.regs[2] & IBS_RIP_INVALID))
+ instruction_pointer_set(&regs, ibs_data.regs[1]);
if (event->attr.sample_type & PERF_SAMPLE_RAW) {
- ibs_data.caps = ibs_caps;
- size = 1;
- offset = 1;
- do {
- rdmsrl(msr + offset, *buf++);
- size++;
- offset = find_next_bit(perf_ibs->offset_mask,
- perf_ibs->offset_max,
- offset + 1);
- } while (offset < perf_ibs->offset_max);
- raw.size = sizeof(u32) + sizeof(u64) * size;
+ raw.size = sizeof(u32) + ibs_data.size;
raw.data = ibs_data.data;
data.raw = &raw;
}
- regs = *iregs; /* XXX: update ip from ibs sample */
-
- overflow = perf_ibs_set_period(perf_ibs, hwc, &config);
- reenable = !(overflow && perf_event_overflow(event, &data, &regs));
- config = (config >> 4) | (reenable ? perf_ibs->enable_mask : 0);
+ throttle = perf_event_overflow(event, &data, &regs);
+out:
+ config = (config >> 4) | (throttle ? 0 : perf_ibs->enable_mask);
perf_ibs_enable_event(hwc, config);
perf_event_update_userpage(event);