[PATCH 5/5] perf, x86: Robustify PEBS fixup

Peter Zijlstra
2010-03-05T15:43:20Z

[-- Attachment #1: pebs-robustify-fixup.patch --]
[-- Type: text/plain, Size: 1650 bytes --]

It turns out the LBR is massively unreliable on certain CPUs, so code
the fixup a little more defensive to avoid crashing the kernel.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
---
arch/x86/kernel/cpu/perf_event_intel_ds.c | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)

Index: linux-2.6/arch/x86/kernel/cpu/perf_event_intel_ds.c
===================================================================
--- linux-2.6.orig/arch/x86/kernel/cpu/perf_event_intel_ds.c
+++ linux-2.6/arch/x86/kernel/cpu/perf_event_intel_ds.c
@@ -399,10 +399,23 @@ static int intel_pmu_pebs_fixup_ip(struc
if (!x86_pmu.intel_cap.pebs_trap)
return 1;

+ /*
+ * No LBR entry, no basic block, no rewinding
+ */
if (!cpuc->lbr_stack.nr || !from || !to)
return 0;

- if (ip < to)
+ /*
+ * Basic blocks should never cross user/kernel boundaries
+ */
+ if (kernel_ip(ip) != kernel_ip(to))
+ return 0;
+
+ /*
+ * unsigned math, either ip is before the start (impossible) or
+ * the basic block is larger than 1 page (sanity)
+ */
+ if ((ip - to) > PAGE_SIZE)
return 0;

/*
@@ -420,7 +433,7 @@ static int intel_pmu_pebs_fixup_ip(struc

old_to = to;
if (!kernel_ip(ip)) {
- int bytes, size = min_t(int, MAX_INSN_SIZE, ip - to);
+ int bytes, size = MAX_INSN_SIZE;

bytes = copy_from_user_nmi(buf, (void __user *)to, size);
if (bytes != size)
@@ -440,6 +453,10 @@ static int intel_pmu_pebs_fixup_ip(struc
return 1;
}

+ /*
+ * Even though we decoded the basic block, the instruction stream
+ * never matched the given IP, either the TO or the IP got corrupted.
+ */
return 0;
}

--