aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/cpu/perf_event_intel_ds.c
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2010-03-05 10:29:14 -0500
committerIngo Molnar <mingo@elte.hu>2010-03-10 07:23:35 -0500
commita562b1871f7f7d2f3a835c3c1e07fa58d473cfb7 (patch)
tree7e86700841691eb86b2ec41ca7f9bc1e29588a42 /arch/x86/kernel/cpu/perf_event_intel_ds.c
parent74846d35b24b6efd61bb88a0a750b6bb257e6e78 (diff)
perf, x86: Robustify PEBS fixup
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> Cc: Arnaldo Carvalho de Melo <acme@infradead.org> Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100305154129.042271287@chello.nl> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event_intel_ds.c')
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_ds.c21
1 files changed, 19 insertions, 2 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c
index a67fff14475e..e7ac51770d4d 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_ds.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c
@@ -399,10 +399,23 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs)
399 if (!x86_pmu.intel_cap.pebs_trap) 399 if (!x86_pmu.intel_cap.pebs_trap)
400 return 1; 400 return 1;
401 401
402 /*
403 * No LBR entry, no basic block, no rewinding
404 */
402 if (!cpuc->lbr_stack.nr || !from || !to) 405 if (!cpuc->lbr_stack.nr || !from || !to)
403 return 0; 406 return 0;
404 407
405 if (ip < to) 408 /*
409 * Basic blocks should never cross user/kernel boundaries
410 */
411 if (kernel_ip(ip) != kernel_ip(to))
412 return 0;
413
414 /*
415 * unsigned math, either ip is before the start (impossible) or
416 * the basic block is larger than 1 page (sanity)
417 */
418 if ((ip - to) > PAGE_SIZE)
406 return 0; 419 return 0;
407 420
408 /* 421 /*
@@ -420,7 +433,7 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs)
420 433
421 old_to = to; 434 old_to = to;
422 if (!kernel_ip(ip)) { 435 if (!kernel_ip(ip)) {
423 int bytes, size = min_t(int, MAX_INSN_SIZE, ip - to); 436 int bytes, size = MAX_INSN_SIZE;
424 437
425 bytes = copy_from_user_nmi(buf, (void __user *)to, size); 438 bytes = copy_from_user_nmi(buf, (void __user *)to, size);
426 if (bytes != size) 439 if (bytes != size)
@@ -440,6 +453,10 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs)
440 return 1; 453 return 1;
441 } 454 }
442 455
456 /*
457 * Even though we decoded the basic block, the instruction stream
458 * never matched the given IP, either the TO or the IP got corrupted.
459 */
443 return 0; 460 return 0;
444} 461}
445 462