diff options
Diffstat (limited to 'arch/powerpc/kernel/perf_event.c')
-rw-r--r-- | arch/powerpc/kernel/perf_event.c | 37 |
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 | ||
401 | static 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 | |||
401 | static void power_pmu_read(struct perf_event *event) | 420 | static 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 | /* |