diff options
author | Will Deacon <will.deacon@arm.com> | 2011-03-25 12:12:37 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-03-26 06:06:09 -0400 |
commit | a737823d37666255e3e74ce84bc9611a038e0888 (patch) | |
tree | 0424691dc1af593e08877cb1ee2b2e59334f532f /arch/arm | |
parent | 574b69cbb633037a9c305d2993aeb680f4a8badd (diff) |
ARM: 6835/1: perf: ensure overflows aren't missed due to IRQ latency
If a counter overflows during a perf stat profiling run it may overtake
the last known value of the counter:
0 prev new 0xffffffff
|----------|-------|----------------------|
In this case, the number of events that have occurred is
(0xffffffff - prev) + new. Unfortunately, the event update code will
not realise an overflow has occurred and will instead report the event
delta as (new - prev) which may be considerably smaller than the real
count.
This patch adds an extra argument to armpmu_event_update which indicates
whether or not an overflow has occurred. If an overflow has occurred
then we use the maximum period of the counter to calculate the elapsed
events.
Acked-by: Jamie Iles <jamie@jamieiles.com>
Reported-by: Ashwin Chaugule <ashwinc@codeaurora.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/kernel/perf_event.c | 19 | ||||
-rw-r--r-- | arch/arm/kernel/perf_event_v6.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/perf_event_v7.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/perf_event_xscale.c | 4 |
4 files changed, 15 insertions, 12 deletions
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index e422f4c269a0..69cfee0fe00f 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c | |||
@@ -205,11 +205,9 @@ armpmu_event_set_period(struct perf_event *event, | |||
205 | static u64 | 205 | static u64 |
206 | armpmu_event_update(struct perf_event *event, | 206 | armpmu_event_update(struct perf_event *event, |
207 | struct hw_perf_event *hwc, | 207 | struct hw_perf_event *hwc, |
208 | int idx) | 208 | int idx, int overflow) |
209 | { | 209 | { |
210 | int shift = 64 - 32; | 210 | u64 delta, prev_raw_count, new_raw_count; |
211 | s64 prev_raw_count, new_raw_count; | ||
212 | u64 delta; | ||
213 | 211 | ||
214 | again: | 212 | again: |
215 | prev_raw_count = local64_read(&hwc->prev_count); | 213 | prev_raw_count = local64_read(&hwc->prev_count); |
@@ -219,8 +217,13 @@ again: | |||
219 | new_raw_count) != prev_raw_count) | 217 | new_raw_count) != prev_raw_count) |
220 | goto again; | 218 | goto again; |
221 | 219 | ||
222 | delta = (new_raw_count << shift) - (prev_raw_count << shift); | 220 | new_raw_count &= armpmu->max_period; |
223 | delta >>= shift; | 221 | prev_raw_count &= armpmu->max_period; |
222 | |||
223 | if (overflow) | ||
224 | delta = armpmu->max_period - prev_raw_count + new_raw_count; | ||
225 | else | ||
226 | delta = new_raw_count - prev_raw_count; | ||
224 | 227 | ||
225 | local64_add(delta, &event->count); | 228 | local64_add(delta, &event->count); |
226 | local64_sub(delta, &hwc->period_left); | 229 | local64_sub(delta, &hwc->period_left); |
@@ -237,7 +240,7 @@ armpmu_read(struct perf_event *event) | |||
237 | if (hwc->idx < 0) | 240 | if (hwc->idx < 0) |
238 | return; | 241 | return; |
239 | 242 | ||
240 | armpmu_event_update(event, hwc, hwc->idx); | 243 | armpmu_event_update(event, hwc, hwc->idx, 0); |
241 | } | 244 | } |
242 | 245 | ||
243 | static void | 246 | static void |
@@ -255,7 +258,7 @@ armpmu_stop(struct perf_event *event, int flags) | |||
255 | if (!(hwc->state & PERF_HES_STOPPED)) { | 258 | if (!(hwc->state & PERF_HES_STOPPED)) { |
256 | armpmu->disable(hwc, hwc->idx); | 259 | armpmu->disable(hwc, hwc->idx); |
257 | barrier(); /* why? */ | 260 | barrier(); /* why? */ |
258 | armpmu_event_update(event, hwc, hwc->idx); | 261 | armpmu_event_update(event, hwc, hwc->idx, 0); |
259 | hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; | 262 | hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; |
260 | } | 263 | } |
261 | } | 264 | } |
diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index 6fc2d228db55..f1e8dd94afe8 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c | |||
@@ -474,7 +474,7 @@ armv6pmu_handle_irq(int irq_num, | |||
474 | continue; | 474 | continue; |
475 | 475 | ||
476 | hwc = &event->hw; | 476 | hwc = &event->hw; |
477 | armpmu_event_update(event, hwc, idx); | 477 | armpmu_event_update(event, hwc, idx, 1); |
478 | data.period = event->hw.last_period; | 478 | data.period = event->hw.last_period; |
479 | if (!armpmu_event_set_period(event, hwc, idx)) | 479 | if (!armpmu_event_set_period(event, hwc, idx)) |
480 | continue; | 480 | continue; |
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index c08d07a99fcc..4960686afb58 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c | |||
@@ -782,7 +782,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) | |||
782 | continue; | 782 | continue; |
783 | 783 | ||
784 | hwc = &event->hw; | 784 | hwc = &event->hw; |
785 | armpmu_event_update(event, hwc, idx); | 785 | armpmu_event_update(event, hwc, idx, 1); |
786 | data.period = event->hw.last_period; | 786 | data.period = event->hw.last_period; |
787 | if (!armpmu_event_set_period(event, hwc, idx)) | 787 | if (!armpmu_event_set_period(event, hwc, idx)) |
788 | continue; | 788 | continue; |
diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index 28cd3b025bc3..39affbe4fdb2 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c | |||
@@ -246,7 +246,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) | |||
246 | continue; | 246 | continue; |
247 | 247 | ||
248 | hwc = &event->hw; | 248 | hwc = &event->hw; |
249 | armpmu_event_update(event, hwc, idx); | 249 | armpmu_event_update(event, hwc, idx, 1); |
250 | data.period = event->hw.last_period; | 250 | data.period = event->hw.last_period; |
251 | if (!armpmu_event_set_period(event, hwc, idx)) | 251 | if (!armpmu_event_set_period(event, hwc, idx)) |
252 | continue; | 252 | continue; |
@@ -578,7 +578,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) | |||
578 | continue; | 578 | continue; |
579 | 579 | ||
580 | hwc = &event->hw; | 580 | hwc = &event->hw; |
581 | armpmu_event_update(event, hwc, idx); | 581 | armpmu_event_update(event, hwc, idx, 1); |
582 | data.period = event->hw.last_period; | 582 | data.period = event->hw.last_period; |
583 | if (!armpmu_event_set_period(event, hwc, idx)) | 583 | if (!armpmu_event_set_period(event, hwc, idx)) |
584 | continue; | 584 | continue; |