diff options
author | Mark Rutland <mark.rutland@arm.com> | 2017-03-10 11:46:13 +0100 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2017-03-31 19:19:45 +0200 |
commit | 2681f0184276d7fc934b6866a5a267f5b3369f7d (patch) | |
tree | 469b9046143dbf4fd3e13e8e9dbeae9d64f81736 /drivers/perf/arm_pmu.c | |
parent | MAINTAINERS: Add file patterns for perf device tree bindings (diff) | |
download | linux-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/arm_pmu.c')
-rw-r--r-- | drivers/perf/arm_pmu.c | 66 |
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; } |