diff options
| -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 c4063b7f49a0..822f63008ae1 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 | /* |
