Commit f76b130b authored by Julien Thierry's avatar Julien Thierry Committed by Will Deacon

arm_pmu: Introduce pmu_irq_ops

Currently the PMU interrupt can either be a normal irq or a percpu irq.
Supporting NMI will introduce two cases for each existing one. It becomes
a mess of 'if's when managing the interrupt.

Define sets of callbacks for operations commonly done on the interrupt. The
appropriate set of callbacks is selected at interrupt request time and
simplifies interrupt enabling/disabling and freeing.
Signed-off-by: default avatarJulien Thierry <julien.thierry@arm.com>
Signed-off-by: default avatarAlexandru Elisei <alexandru.elisei@arm.com>
Tested-by: Sumit Garg <sumit.garg@linaro.org> (Developerbox)
Cc: Julien Thierry <julien.thierry.kdev@gmail.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Link: https://lore.kernel.org/r/20200924110706.254996-7-alexandru.elisei@arm.comSigned-off-by: default avatarWill Deacon <will@kernel.org>
parent 95e92e45
......@@ -26,8 +26,46 @@
#include <asm/irq_regs.h>
static int armpmu_count_irq_users(const int irq);
struct pmu_irq_ops {
void (*enable_pmuirq)(unsigned int irq);
void (*disable_pmuirq)(unsigned int irq);
void (*free_pmuirq)(unsigned int irq, int cpu, void __percpu *devid);
};
static void armpmu_free_pmuirq(unsigned int irq, int cpu, void __percpu *devid)
{
free_irq(irq, per_cpu_ptr(devid, cpu));
}
static const struct pmu_irq_ops pmuirq_ops = {
.enable_pmuirq = enable_irq,
.disable_pmuirq = disable_irq_nosync,
.free_pmuirq = armpmu_free_pmuirq
};
static void armpmu_enable_percpu_pmuirq(unsigned int irq)
{
enable_percpu_irq(irq, IRQ_TYPE_NONE);
}
static void armpmu_free_percpu_pmuirq(unsigned int irq, int cpu,
void __percpu *devid)
{
if (armpmu_count_irq_users(irq) == 1)
free_percpu_irq(irq, devid);
}
static const struct pmu_irq_ops percpu_pmuirq_ops = {
.enable_pmuirq = armpmu_enable_percpu_pmuirq,
.disable_pmuirq = disable_percpu_irq,
.free_pmuirq = armpmu_free_percpu_pmuirq
};
static DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu);
static DEFINE_PER_CPU(int, cpu_irq);
static DEFINE_PER_CPU(const struct pmu_irq_ops *, cpu_irq_ops);
static inline u64 arm_pmu_event_max_period(struct perf_event *event)
{
......@@ -544,6 +582,23 @@ static int armpmu_count_irq_users(const int irq)
return count;
}
static const struct pmu_irq_ops *armpmu_find_irq_ops(int irq)
{
const struct pmu_irq_ops *ops = NULL;
int cpu;
for_each_possible_cpu(cpu) {
if (per_cpu(cpu_irq, cpu) != irq)
continue;
ops = per_cpu(cpu_irq_ops, cpu);
if (ops)
break;
}
return ops;
}
void armpmu_free_irq(int irq, int cpu)
{
if (per_cpu(cpu_irq, cpu) == 0)
......@@ -551,18 +606,18 @@ void armpmu_free_irq(int irq, int cpu)
if (WARN_ON(irq != per_cpu(cpu_irq, cpu)))
return;
if (!irq_is_percpu_devid(irq))
free_irq(irq, per_cpu_ptr(&cpu_armpmu, cpu));
else if (armpmu_count_irq_users(irq) == 1)
free_percpu_irq(irq, &cpu_armpmu);
per_cpu(cpu_irq_ops, cpu)->free_pmuirq(irq, cpu, &cpu_armpmu);
per_cpu(cpu_irq, cpu) = 0;
per_cpu(cpu_irq_ops, cpu) = NULL;
}
int armpmu_request_irq(int irq, int cpu)
{
int err = 0;
const irq_handler_t handler = armpmu_dispatch_irq;
const struct pmu_irq_ops *irq_ops;
if (!irq)
return 0;
......@@ -584,15 +639,26 @@ int armpmu_request_irq(int irq, int cpu)
irq_set_status_flags(irq, IRQ_NOAUTOEN);
err = request_irq(irq, handler, irq_flags, "arm-pmu",
per_cpu_ptr(&cpu_armpmu, cpu));
irq_ops = &pmuirq_ops;
} else if (armpmu_count_irq_users(irq) == 0) {
err = request_percpu_irq(irq, handler, "arm-pmu",
&cpu_armpmu);
irq_ops = &percpu_pmuirq_ops;
} else {
/* Per cpudevid irq was already requested by another CPU */
irq_ops = armpmu_find_irq_ops(irq);
if (WARN_ON(!irq_ops))
err = -EINVAL;
}
if (err)
goto err_out;
per_cpu(cpu_irq, cpu) = irq;
per_cpu(cpu_irq_ops, cpu) = irq_ops;
return 0;
err_out:
......@@ -625,12 +691,8 @@ static int arm_perf_starting_cpu(unsigned int cpu, struct hlist_node *node)
per_cpu(cpu_armpmu, cpu) = pmu;
irq = armpmu_get_cpu_irq(pmu, cpu);
if (irq) {
if (irq_is_percpu_devid(irq))
enable_percpu_irq(irq, IRQ_TYPE_NONE);
else
enable_irq(irq);
}
if (irq)
per_cpu(cpu_irq_ops, cpu)->enable_pmuirq(irq);
return 0;
}
......@@ -644,12 +706,8 @@ static int arm_perf_teardown_cpu(unsigned int cpu, struct hlist_node *node)
return 0;
irq = armpmu_get_cpu_irq(pmu, cpu);
if (irq) {
if (irq_is_percpu_devid(irq))
disable_percpu_irq(irq);
else
disable_irq_nosync(irq);
}
if (irq)
per_cpu(cpu_irq_ops, cpu)->disable_pmuirq(irq);
per_cpu(cpu_armpmu, cpu) = NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment