in kernel/perf_event.c [1103:1158]
static irqreturn_t csky_pmu_handle_irq(int irq_num, void *dev)
{
struct perf_sample_data data;
struct pmu_hw_events *cpuc = this_cpu_ptr(csky_pmu.hw_events);
struct pt_regs *regs;
int idx;
/*
* Did an overflow occur?
*/
if (!cprcr(HPOFSR))
return IRQ_NONE;
/*
* Handle the counter(s) overflow(s)
*/
regs = get_irq_regs();
csky_pmu_disable(&csky_pmu.pmu);
for (idx = 0; idx < CSKY_PMU_MAX_EVENTS; ++idx) {
struct perf_event *event = cpuc->events[idx];
struct hw_perf_event *hwc;
/* Ignore if we don't have an event. */
if (!event)
continue;
/*
* We have a single interrupt for all counters. Check that
* each counter has overflowed before we process it.
*/
if (!(cprcr(HPOFSR) & BIT(idx)))
continue;
hwc = &event->hw;
csky_perf_event_update(event, &event->hw);
perf_sample_data_init(&data, 0, hwc->last_period);
csky_pmu_event_set_period(event);
if (perf_event_overflow(event, &data, regs))
csky_pmu_stop_event(event);
}
csky_pmu_enable(&csky_pmu.pmu);
/*
* Handle the pending perf events.
*
* Note: this call *must* be run with interrupts disabled. For
* platforms that can have the PMU interrupts raised as an NMI, this
* will not work.
*/
irq_work_run();
return IRQ_HANDLED;
}