diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2010-03-05 10:29:14 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-03-10 07:23:35 -0500 |
commit | a562b1871f7f7d2f3a835c3c1e07fa58d473cfb7 (patch) | |
tree | 7e86700841691eb86b2ec41ca7f9bc1e29588a42 /arch | |
parent | 74846d35b24b6efd61bb88a0a750b6bb257e6e78 (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')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel_ds.c | 21 |
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 | ||