From 5727347180ebc6b4a866fcbe00dcb39cc03acb37 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 6 Mar 2012 17:33:17 +0100 Subject: ARM: 7354/1: perf: limit sample_period to half max_period in non-sampling mode On ARM, the PMU does not stop counting after an overflow and therefore IRQ latency affects the new counter value read by the kernel. This is significant for non-sampling runs where it is possible for the new value to overtake the previous one, causing the delta to be out by up to max_period events. Commit a737823d ("ARM: 6835/1: perf: ensure overflows aren't missed due to IRQ latency") attempted to fix this problem by allowing interrupt handlers to pass an overflow flag to the event update function, causing the overflow calculation to assume that the counter passed through zero when going from prev to new. Unfortunately, this doesn't work when overflow occurs on the perf_task_tick path because we have the flag cleared and end up computing a large negative delta. This patch removes the overflow flag from armpmu_event_update and instead limits the sample_period to half of the max_period for non-sampling profiling runs. Cc: Signed-off-by: Ming Lei Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/perf_event_xscale.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/arm/kernel/perf_event_xscale.c') diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index 3b99d8269829..831e019b017c 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -259,7 +259,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) continue; hwc = &event->hw; - armpmu_event_update(event, hwc, idx, 1); + armpmu_event_update(event, hwc, idx); data.period = event->hw.last_period; if (!armpmu_event_set_period(event, hwc, idx)) continue; @@ -596,7 +596,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) continue; hwc = &event->hw; - armpmu_event_update(event, hwc, idx, 1); + armpmu_event_update(event, hwc, idx); data.period = event->hw.last_period; if (!armpmu_event_set_period(event, hwc, idx)) continue; -- cgit v1.2.3 From f6f5a30c834135c9f2fa10400c59ebbdd9188567 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 6 Mar 2012 17:34:50 +0100 Subject: ARM: 7356/1: perf: check that we have an event in the PMU IRQ handlers The PMU IRQ handlers in perf assume that if a counter has overflowed then perf must be responsible. In the paranoid world of crazy hardware, this could be false, so check that we do have a valid event before attempting to dereference NULL in the interrupt path. Cc: Signed-off-by: Ming Lei Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/perf_event_xscale.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'arch/arm/kernel/perf_event_xscale.c') diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index 831e019b017c..a5bbd360cc4b 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -255,6 +255,9 @@ xscale1pmu_handle_irq(int irq_num, void *dev) struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; + if (!event) + continue; + if (!xscale1_pmnc_counter_has_overflowed(pmnc, idx)) continue; @@ -592,6 +595,9 @@ xscale2pmu_handle_irq(int irq_num, void *dev) struct perf_event *event = cpuc->events[idx]; struct hw_perf_event *hwc; + if (!event) + continue; + if (!xscale2_pmnc_counter_has_overflowed(pmnc, idx)) continue; -- cgit v1.2.3 From 3f31ae121348afd9ed39700ea2a63c17cd7eeed1 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 6 Mar 2012 17:35:55 +0100 Subject: ARM: 7357/1: perf: fix overflow handling for xscale2 PMUs xscale2 PMUs indicate overflow not via the PMU control register, but by a separate overflow FLAG register instead. This patch fixes the xscale2 PMU code to use this register to detect to overflow and ensures that we clear any pending overflow when disabling a counter. Cc: Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/perf_event_xscale.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'arch/arm/kernel/perf_event_xscale.c') diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index a5bbd360cc4b..71a21e6712f5 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -598,7 +598,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) if (!event) continue; - if (!xscale2_pmnc_counter_has_overflowed(pmnc, idx)) + if (!xscale2_pmnc_counter_has_overflowed(of_flags, idx)) continue; hwc = &event->hw; @@ -669,7 +669,7 @@ xscale2pmu_enable_event(struct hw_perf_event *hwc, int idx) static void xscale2pmu_disable_event(struct hw_perf_event *hwc, int idx) { - unsigned long flags, ien, evtsel; + unsigned long flags, ien, evtsel, of_flags; struct pmu_hw_events *events = cpu_pmu->get_hw_events(); ien = xscale2pmu_read_int_enable(); @@ -678,26 +678,31 @@ xscale2pmu_disable_event(struct hw_perf_event *hwc, int idx) switch (idx) { case XSCALE_CYCLE_COUNTER: ien &= ~XSCALE2_CCOUNT_INT_EN; + of_flags = XSCALE2_CCOUNT_OVERFLOW; break; case XSCALE_COUNTER0: ien &= ~XSCALE2_COUNT0_INT_EN; evtsel &= ~XSCALE2_COUNT0_EVT_MASK; evtsel |= XSCALE_PERFCTR_UNUSED << XSCALE2_COUNT0_EVT_SHFT; + of_flags = XSCALE2_COUNT0_OVERFLOW; break; case XSCALE_COUNTER1: ien &= ~XSCALE2_COUNT1_INT_EN; evtsel &= ~XSCALE2_COUNT1_EVT_MASK; evtsel |= XSCALE_PERFCTR_UNUSED << XSCALE2_COUNT1_EVT_SHFT; + of_flags = XSCALE2_COUNT1_OVERFLOW; break; case XSCALE_COUNTER2: ien &= ~XSCALE2_COUNT2_INT_EN; evtsel &= ~XSCALE2_COUNT2_EVT_MASK; evtsel |= XSCALE_PERFCTR_UNUSED << XSCALE2_COUNT2_EVT_SHFT; + of_flags = XSCALE2_COUNT2_OVERFLOW; break; case XSCALE_COUNTER3: ien &= ~XSCALE2_COUNT3_INT_EN; evtsel &= ~XSCALE2_COUNT3_EVT_MASK; evtsel |= XSCALE_PERFCTR_UNUSED << XSCALE2_COUNT3_EVT_SHFT; + of_flags = XSCALE2_COUNT3_OVERFLOW; break; default: WARN_ONCE(1, "invalid counter number (%d)\n", idx); @@ -707,6 +712,7 @@ xscale2pmu_disable_event(struct hw_perf_event *hwc, int idx) raw_spin_lock_irqsave(&events->pmu_lock, flags); xscale2pmu_write_event_select(evtsel); xscale2pmu_write_int_enable(ien); + xscale2pmu_write_overflow_flags(of_flags); raw_spin_unlock_irqrestore(&events->pmu_lock, flags); } -- cgit v1.2.3