summaryrefslogtreecommitdiffstats
path: root/drivers/perf
diff options
context:
space:
mode:
authorMark Rutland <mark.rutland@arm.com>2017-03-10 11:46:13 +0100
committerWill Deacon <will.deacon@arm.com>2017-03-31 19:19:45 +0200
commit2681f0184276d7fc934b6866a5a267f5b3369f7d (patch)
tree469b9046143dbf4fd3e13e8e9dbeae9d64f81736 /drivers/perf
parentMAINTAINERS: Add file patterns for perf device tree bindings (diff)
downloadlinux-2681f0184276d7fc934b6866a5a267f5b3369f7d.tar.xz
linux-2681f0184276d7fc934b6866a5a267f5b3369f7d.zip
drivers/perf: arm_pmu: rework per-cpu allocation
For historical reasons, we allocate per-cpu data associated with a PMU rather late, in cpu_pmu_init, after we've parsed whatever hardware information we were provided with. In order to allow use to store some per-cpu data early in the probe path, we need to allocate (and initialise) the per-cpu data earlier. This patch reworks the way we allocate the pmu and associated per-cpu data in order to make that possible. Signed-off-by: Mark Rutland <mark.rutland@arm.com> [will: make armpmu_{alloc,free} static Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'drivers/perf')
-rw-r--r--drivers/perf/arm_pmu.c66
1 files changed, 44 insertions, 22 deletions
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 9612b84bc3e0..ad60e966f174 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -828,29 +828,16 @@ static inline void cpu_pm_pmu_unregister(struct arm_pmu *cpu_pmu) { }
static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
{
int err;
- int cpu;
- struct pmu_hw_events __percpu *cpu_hw_events;
-
- cpu_hw_events = alloc_percpu(struct pmu_hw_events);
- if (!cpu_hw_events)
- return -ENOMEM;
err = cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_STARTING,
&cpu_pmu->node);
if (err)
- goto out_free;
+ goto out;
err = cpu_pm_pmu_register(cpu_pmu);
if (err)
goto out_unregister;
- for_each_possible_cpu(cpu) {
- struct pmu_hw_events *events = per_cpu_ptr(cpu_hw_events, cpu);
- raw_spin_lock_init(&events->pmu_lock);
- events->percpu_pmu = cpu_pmu;
- }
-
- cpu_pmu->hw_events = cpu_hw_events;
cpu_pmu->request_irq = cpu_pmu_request_irq;
cpu_pmu->free_irq = cpu_pmu_free_irq;
@@ -876,8 +863,7 @@ static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
out_unregister:
cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_STARTING,
&cpu_pmu->node);
-out_free:
- free_percpu(cpu_hw_events);
+out:
return err;
}
@@ -886,7 +872,6 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)
cpu_pm_pmu_unregister(cpu_pmu);
cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_STARTING,
&cpu_pmu->node);
- free_percpu(cpu_pmu->hw_events);
}
/*
@@ -1008,6 +993,45 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
return 0;
}
+static struct arm_pmu *armpmu_alloc(void)
+{
+ struct arm_pmu *pmu;
+ int cpu;
+
+ pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
+ if (!pmu) {
+ pr_info("failed to allocate PMU device!\n");
+ goto out;
+ }
+
+ pmu->hw_events = alloc_percpu(struct pmu_hw_events);
+ if (!pmu->hw_events) {
+ pr_info("failed to allocate per-cpu PMU data.\n");
+ goto out_free_pmu;
+ }
+
+ for_each_possible_cpu(cpu) {
+ struct pmu_hw_events *events;
+
+ events = per_cpu_ptr(pmu->hw_events, cpu);
+ raw_spin_lock_init(&events->pmu_lock);
+ events->percpu_pmu = pmu;
+ }
+
+ return pmu;
+
+out_free_pmu:
+ kfree(pmu);
+out:
+ return NULL;
+}
+
+static void armpmu_free(struct arm_pmu *pmu)
+{
+ free_percpu(pmu->hw_events);
+ kfree(pmu);
+}
+
int arm_pmu_device_probe(struct platform_device *pdev,
const struct of_device_id *of_table,
const struct pmu_probe_info *probe_table)
@@ -1018,11 +1042,9 @@ int arm_pmu_device_probe(struct platform_device *pdev,
struct arm_pmu *pmu;
int ret = -ENODEV;
- pmu = kzalloc(sizeof(struct arm_pmu), GFP_KERNEL);
- if (!pmu) {
- pr_info("failed to allocate PMU device!\n");
+ pmu = armpmu_alloc();
+ if (!pmu)
return -ENOMEM;
- }
armpmu_init(pmu);
@@ -1076,7 +1098,7 @@ out_free:
pr_info("%s: failed to register PMU devices!\n",
of_node_full_name(node));
kfree(pmu->irq_affinity);
- kfree(pmu);
+ armpmu_free(pmu);
return ret;
}