aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2011-03-25 12:12:37 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2011-03-26 06:06:09 -0400
commita737823d37666255e3e74ce84bc9611a038e0888 (patch)
tree0424691dc1af593e08877cb1ee2b2e59334f532f /arch/arm
parent574b69cbb633037a9c305d2993aeb680f4a8badd (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.c19
-rw-r--r--arch/arm/kernel/perf_event_v6.c2
-rw-r--r--arch/arm/kernel/perf_event_v7.c2
-rw-r--r--arch/arm/kernel/perf_event_xscale.c4
4 files changed, 15 insertions, 12 deletions
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index e422f4c269a..69cfee0fe00 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,
205static u64 205static u64
206armpmu_event_update(struct perf_event *event, 206armpmu_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
214again: 212again:
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
243static void 246static 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 6fc2d228db5..f1e8dd94afe 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 c08d07a99fc..4960686afb5 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 28cd3b025bc..39affbe4fdb 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;