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 <[email protected]>
Reported-by: Ashwin Chaugule <[email protected]>
Signed-off-by: Will Deacon <[email protected]>
Signed-off-by: Russell King <[email protected]>
static u64
armpmu_event_update(struct perf_event *event,
struct hw_perf_event *hwc,
static u64
armpmu_event_update(struct perf_event *event,
struct hw_perf_event *hwc,
- int shift = 64 - 32;
- s64 prev_raw_count, new_raw_count;
- u64 delta;
+ u64 delta, prev_raw_count, new_raw_count;
again:
prev_raw_count = local64_read(&hwc->prev_count);
again:
prev_raw_count = local64_read(&hwc->prev_count);
new_raw_count) != prev_raw_count)
goto again;
new_raw_count) != prev_raw_count)
goto again;
- delta = (new_raw_count << shift) - (prev_raw_count << shift);
- delta >>= shift;
+ new_raw_count &= armpmu->max_period;
+ prev_raw_count &= armpmu->max_period;
+
+ if (overflow)
+ delta = armpmu->max_period - prev_raw_count + new_raw_count;
+ else
+ delta = new_raw_count - prev_raw_count;
local64_add(delta, &event->count);
local64_sub(delta, &hwc->period_left);
local64_add(delta, &event->count);
local64_sub(delta, &hwc->period_left);
if (hwc->idx < 0)
return;
if (hwc->idx < 0)
return;
- armpmu_event_update(event, hwc, hwc->idx);
+ armpmu_event_update(event, hwc, hwc->idx, 0);
if (!(hwc->state & PERF_HES_STOPPED)) {
armpmu->disable(hwc, hwc->idx);
barrier(); /* why? */
if (!(hwc->state & PERF_HES_STOPPED)) {
armpmu->disable(hwc, hwc->idx);
barrier(); /* why? */
- armpmu_event_update(event, hwc, hwc->idx);
+ armpmu_event_update(event, hwc, hwc->idx, 0);
hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
}
}
hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
}
}
continue;
hwc = &event->hw;
continue;
hwc = &event->hw;
- armpmu_event_update(event, hwc, idx);
+ armpmu_event_update(event, hwc, idx, 1);
data.period = event->hw.last_period;
if (!armpmu_event_set_period(event, hwc, idx))
continue;
data.period = event->hw.last_period;
if (!armpmu_event_set_period(event, hwc, idx))
continue;
continue;
hwc = &event->hw;
continue;
hwc = &event->hw;
- armpmu_event_update(event, hwc, idx);
+ armpmu_event_update(event, hwc, idx, 1);
data.period = event->hw.last_period;
if (!armpmu_event_set_period(event, hwc, idx))
continue;
data.period = event->hw.last_period;
if (!armpmu_event_set_period(event, hwc, idx))
continue;
continue;
hwc = &event->hw;
continue;
hwc = &event->hw;
- armpmu_event_update(event, hwc, idx);
+ armpmu_event_update(event, hwc, idx, 1);
data.period = event->hw.last_period;
if (!armpmu_event_set_period(event, hwc, idx))
continue;
data.period = event->hw.last_period;
if (!armpmu_event_set_period(event, hwc, idx))
continue;
continue;
hwc = &event->hw;
continue;
hwc = &event->hw;
- armpmu_event_update(event, hwc, idx);
+ armpmu_event_update(event, hwc, idx, 1);
data.period = event->hw.last_period;
if (!armpmu_event_set_period(event, hwc, idx))
continue;
data.period = event->hw.last_period;
if (!armpmu_event_set_period(event, hwc, idx))
continue;