aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorEric B Munson <emunson@mgebm.net>2011-04-15 04:12:30 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-04-17 23:08:23 -0400
commit86c74ab317c1ef4d37325e0d7ca8a01a796b0bd7 (patch)
tree49afb7aae9c12b0cd0361b996a9b9e630a25ea63 /arch
parent09597cfe93d3cc2c6e064a3ead5956b882511560 (diff)
powerpc/perf_event: Skip updating kernel counters if register value shrinks
Because of speculative event roll back, it is possible for some event coutners to decrease between reads on POWER7. This causes a problem with the way that counters are updated. Delta calues are calculated in a 64 bit value and the top 32 bits are masked. If the register value has decreased, this leaves us with a very large positive value added to the kernel counters. This patch protects against this by skipping the update if the delta would be negative. This can lead to a lack of precision in the coutner values, but from my testing the value is typcially fewer than 10 samples at a time. Signed-off-by: Eric B Munson <emunson@mgebm.net> Cc: stable@kernel.org Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/kernel/perf_event.c37
1 files changed, 30 insertions, 7 deletions
diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c
index c4063b7f49a..822f63008ae 100644
--- a/arch/powerpc/kernel/perf_event.c
+++ b/arch/powerpc/kernel/perf_event.c
@@ -398,6 +398,25 @@ static int check_excludes(struct perf_event **ctrs, unsigned int cflags[],
398 return 0; 398 return 0;
399} 399}
400 400
401static u64 check_and_compute_delta(u64 prev, u64 val)
402{
403 u64 delta = (val - prev) & 0xfffffffful;
404
405 /*
406 * POWER7 can roll back counter values, if the new value is smaller
407 * than the previous value it will cause the delta and the counter to
408 * have bogus values unless we rolled a counter over. If a coutner is
409 * rolled back, it will be smaller, but within 256, which is the maximum
410 * number of events to rollback at once. If we dectect a rollback
411 * return 0. This can lead to a small lack of precision in the
412 * counters.
413 */
414 if (prev > val && (prev - val) < 256)
415 delta = 0;
416
417 return delta;
418}
419
401static void power_pmu_read(struct perf_event *event) 420static void power_pmu_read(struct perf_event *event)
402{ 421{
403 s64 val, delta, prev; 422 s64 val, delta, prev;
@@ -416,10 +435,11 @@ static void power_pmu_read(struct perf_event *event)
416 prev = local64_read(&event->hw.prev_count); 435 prev = local64_read(&event->hw.prev_count);
417 barrier(); 436 barrier();
418 val = read_pmc(event->hw.idx); 437 val = read_pmc(event->hw.idx);
438 delta = check_and_compute_delta(prev, val);
439 if (!delta)
440 return;
419 } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev); 441 } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev);
420 442
421 /* The counters are only 32 bits wide */
422 delta = (val - prev) & 0xfffffffful;
423 local64_add(delta, &event->count); 443 local64_add(delta, &event->count);
424 local64_sub(delta, &event->hw.period_left); 444 local64_sub(delta, &event->hw.period_left);
425} 445}
@@ -449,8 +469,9 @@ static void freeze_limited_counters(struct cpu_hw_events *cpuhw,
449 val = (event->hw.idx == 5) ? pmc5 : pmc6; 469 val = (event->hw.idx == 5) ? pmc5 : pmc6;
450 prev = local64_read(&event->hw.prev_count); 470 prev = local64_read(&event->hw.prev_count);
451 event->hw.idx = 0; 471 event->hw.idx = 0;
452 delta = (val - prev) & 0xfffffffful; 472 delta = check_and_compute_delta(prev, val);
453 local64_add(delta, &event->count); 473 if (delta)
474 local64_add(delta, &event->count);
454 } 475 }
455} 476}
456 477
@@ -458,14 +479,16 @@ static void thaw_limited_counters(struct cpu_hw_events *cpuhw,
458 unsigned long pmc5, unsigned long pmc6) 479 unsigned long pmc5, unsigned long pmc6)
459{ 480{
460 struct perf_event *event; 481 struct perf_event *event;
461 u64 val; 482 u64 val, prev;
462 int i; 483 int i;
463 484
464 for (i = 0; i < cpuhw->n_limited; ++i) { 485 for (i = 0; i < cpuhw->n_limited; ++i) {
465 event = cpuhw->limited_counter[i]; 486 event = cpuhw->limited_counter[i];
466 event->hw.idx = cpuhw->limited_hwidx[i]; 487 event->hw.idx = cpuhw->limited_hwidx[i];
467 val = (event->hw.idx == 5) ? pmc5 : pmc6; 488 val = (event->hw.idx == 5) ? pmc5 : pmc6;
468 local64_set(&event->hw.prev_count, val); 489 prev = local64_read(&event->hw.prev_count);
490 if (check_and_compute_delta(prev, val))
491 local64_set(&event->hw.prev_count, val);
469 perf_event_update_userpage(event); 492 perf_event_update_userpage(event);
470 } 493 }
471} 494}
@@ -1197,7 +1220,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
1197 1220
1198 /* we don't have to worry about interrupts here */ 1221 /* we don't have to worry about interrupts here */
1199 prev = local64_read(&event->hw.prev_count); 1222 prev = local64_read(&event->hw.prev_count);
1200 delta = (val - prev) & 0xfffffffful; 1223 delta = check_and_compute_delta(prev, val);
1201 local64_add(delta, &event->count); 1224 local64_add(delta, &event->count);
1202 1225
1203 /* 1226 /*