diff options
author | Stephen Boyd <sboyd@codeaurora.org> | 2014-02-07 16:01:19 -0500 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2014-02-21 06:10:44 -0500 |
commit | bbd64559376fa25732994c4181c8ec493fa57871 (patch) | |
tree | 33172744ac66ee5cfe64af4cd8e74c20b7195ce6 | |
parent | 6d0abeca3242a88cab8232e4acd7e2bf088f3bc2 (diff) |
ARM: perf: support percpu irqs for the CPU PMU
Some CPU PMUs are wired up with one PPI for all the CPUs instead
of with a different SPI for each CPU. Add support for these
devices.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r-- | arch/arm/kernel/perf_event.c | 14 | ||||
-rw-r--r-- | arch/arm/kernel/perf_event_cpu.c | 97 |
2 files changed, 80 insertions, 31 deletions
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 789d846a9184..b0c8489018d3 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c | |||
@@ -16,6 +16,8 @@ | |||
16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
17 | #include <linux/pm_runtime.h> | 17 | #include <linux/pm_runtime.h> |
18 | #include <linux/uaccess.h> | 18 | #include <linux/uaccess.h> |
19 | #include <linux/irq.h> | ||
20 | #include <linux/irqdesc.h> | ||
19 | 21 | ||
20 | #include <asm/irq_regs.h> | 22 | #include <asm/irq_regs.h> |
21 | #include <asm/pmu.h> | 23 | #include <asm/pmu.h> |
@@ -295,9 +297,15 @@ validate_group(struct perf_event *event) | |||
295 | 297 | ||
296 | static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) | 298 | static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) |
297 | { | 299 | { |
298 | struct arm_pmu *armpmu = (struct arm_pmu *) dev; | 300 | struct arm_pmu *armpmu; |
299 | struct platform_device *plat_device = armpmu->plat_device; | 301 | struct platform_device *plat_device; |
300 | struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev); | 302 | struct arm_pmu_platdata *plat; |
303 | |||
304 | if (irq_is_percpu(irq)) | ||
305 | dev = *(void **)dev; | ||
306 | armpmu = dev; | ||
307 | plat_device = armpmu->plat_device; | ||
308 | plat = dev_get_platdata(&plat_device->dev); | ||
301 | 309 | ||
302 | if (plat && plat->handle_irq) | 310 | if (plat && plat->handle_irq) |
303 | return plat->handle_irq(irq, dev, armpmu->handle_irq); | 311 | return plat->handle_irq(irq, dev, armpmu->handle_irq); |
diff --git a/arch/arm/kernel/perf_event_cpu.c b/arch/arm/kernel/perf_event_cpu.c index 20d553c9f5e2..6efd8aab15df 100644 --- a/arch/arm/kernel/perf_event_cpu.c +++ b/arch/arm/kernel/perf_event_cpu.c | |||
@@ -25,6 +25,8 @@ | |||
25 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/spinlock.h> | 27 | #include <linux/spinlock.h> |
28 | #include <linux/irq.h> | ||
29 | #include <linux/irqdesc.h> | ||
28 | 30 | ||
29 | #include <asm/cputype.h> | 31 | #include <asm/cputype.h> |
30 | #include <asm/irq_regs.h> | 32 | #include <asm/irq_regs.h> |
@@ -33,6 +35,7 @@ | |||
33 | /* Set at runtime when we know what CPU type we are. */ | 35 | /* Set at runtime when we know what CPU type we are. */ |
34 | static struct arm_pmu *cpu_pmu; | 36 | static struct arm_pmu *cpu_pmu; |
35 | 37 | ||
38 | static DEFINE_PER_CPU(struct arm_pmu *, percpu_pmu); | ||
36 | static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); | 39 | static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); |
37 | static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); | 40 | static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); |
38 | static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); | 41 | static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); |
@@ -71,6 +74,26 @@ static struct pmu_hw_events *cpu_pmu_get_cpu_events(void) | |||
71 | return this_cpu_ptr(&cpu_hw_events); | 74 | return this_cpu_ptr(&cpu_hw_events); |
72 | } | 75 | } |
73 | 76 | ||
77 | static void cpu_pmu_enable_percpu_irq(void *data) | ||
78 | { | ||
79 | struct arm_pmu *cpu_pmu = data; | ||
80 | struct platform_device *pmu_device = cpu_pmu->plat_device; | ||
81 | int irq = platform_get_irq(pmu_device, 0); | ||
82 | |||
83 | enable_percpu_irq(irq, IRQ_TYPE_NONE); | ||
84 | cpumask_set_cpu(smp_processor_id(), &cpu_pmu->active_irqs); | ||
85 | } | ||
86 | |||
87 | static void cpu_pmu_disable_percpu_irq(void *data) | ||
88 | { | ||
89 | struct arm_pmu *cpu_pmu = data; | ||
90 | struct platform_device *pmu_device = cpu_pmu->plat_device; | ||
91 | int irq = platform_get_irq(pmu_device, 0); | ||
92 | |||
93 | cpumask_clear_cpu(smp_processor_id(), &cpu_pmu->active_irqs); | ||
94 | disable_percpu_irq(irq); | ||
95 | } | ||
96 | |||
74 | static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu) | 97 | static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu) |
75 | { | 98 | { |
76 | int i, irq, irqs; | 99 | int i, irq, irqs; |
@@ -78,12 +101,18 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu) | |||
78 | 101 | ||
79 | irqs = min(pmu_device->num_resources, num_possible_cpus()); | 102 | irqs = min(pmu_device->num_resources, num_possible_cpus()); |
80 | 103 | ||
81 | for (i = 0; i < irqs; ++i) { | 104 | irq = platform_get_irq(pmu_device, 0); |
82 | if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs)) | 105 | if (irq >= 0 && irq_is_percpu(irq)) { |
83 | continue; | 106 | on_each_cpu(cpu_pmu_disable_percpu_irq, cpu_pmu, 1); |
84 | irq = platform_get_irq(pmu_device, i); | 107 | free_percpu_irq(irq, &percpu_pmu); |
85 | if (irq >= 0) | 108 | } else { |
86 | free_irq(irq, cpu_pmu); | 109 | for (i = 0; i < irqs; ++i) { |
110 | if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs)) | ||
111 | continue; | ||
112 | irq = platform_get_irq(pmu_device, i); | ||
113 | if (irq >= 0) | ||
114 | free_irq(irq, cpu_pmu); | ||
115 | } | ||
87 | } | 116 | } |
88 | } | 117 | } |
89 | 118 | ||
@@ -101,33 +130,44 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) | |||
101 | return -ENODEV; | 130 | return -ENODEV; |
102 | } | 131 | } |
103 | 132 | ||
104 | for (i = 0; i < irqs; ++i) { | 133 | irq = platform_get_irq(pmu_device, 0); |
105 | err = 0; | 134 | if (irq >= 0 && irq_is_percpu(irq)) { |
106 | irq = platform_get_irq(pmu_device, i); | 135 | err = request_percpu_irq(irq, handler, "arm-pmu", &percpu_pmu); |
107 | if (irq < 0) | ||
108 | continue; | ||
109 | |||
110 | /* | ||
111 | * If we have a single PMU interrupt that we can't shift, | ||
112 | * assume that we're running on a uniprocessor machine and | ||
113 | * continue. Otherwise, continue without this interrupt. | ||
114 | */ | ||
115 | if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { | ||
116 | pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", | ||
117 | irq, i); | ||
118 | continue; | ||
119 | } | ||
120 | |||
121 | err = request_irq(irq, handler, | ||
122 | IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu", | ||
123 | cpu_pmu); | ||
124 | if (err) { | 136 | if (err) { |
125 | pr_err("unable to request IRQ%d for ARM PMU counters\n", | 137 | pr_err("unable to request IRQ%d for ARM PMU counters\n", |
126 | irq); | 138 | irq); |
127 | return err; | 139 | return err; |
128 | } | 140 | } |
129 | 141 | on_each_cpu(cpu_pmu_enable_percpu_irq, cpu_pmu, 1); | |
130 | cpumask_set_cpu(i, &cpu_pmu->active_irqs); | 142 | } else { |
143 | for (i = 0; i < irqs; ++i) { | ||
144 | err = 0; | ||
145 | irq = platform_get_irq(pmu_device, i); | ||
146 | if (irq < 0) | ||
147 | continue; | ||
148 | |||
149 | /* | ||
150 | * If we have a single PMU interrupt that we can't shift, | ||
151 | * assume that we're running on a uniprocessor machine and | ||
152 | * continue. Otherwise, continue without this interrupt. | ||
153 | */ | ||
154 | if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { | ||
155 | pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", | ||
156 | irq, i); | ||
157 | continue; | ||
158 | } | ||
159 | |||
160 | err = request_irq(irq, handler, | ||
161 | IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu", | ||
162 | cpu_pmu); | ||
163 | if (err) { | ||
164 | pr_err("unable to request IRQ%d for ARM PMU counters\n", | ||
165 | irq); | ||
166 | return err; | ||
167 | } | ||
168 | |||
169 | cpumask_set_cpu(i, &cpu_pmu->active_irqs); | ||
170 | } | ||
131 | } | 171 | } |
132 | 172 | ||
133 | return 0; | 173 | return 0; |
@@ -141,6 +181,7 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu) | |||
141 | events->events = per_cpu(hw_events, cpu); | 181 | events->events = per_cpu(hw_events, cpu); |
142 | events->used_mask = per_cpu(used_mask, cpu); | 182 | events->used_mask = per_cpu(used_mask, cpu); |
143 | raw_spin_lock_init(&events->pmu_lock); | 183 | raw_spin_lock_init(&events->pmu_lock); |
184 | per_cpu(percpu_pmu, cpu) = cpu_pmu; | ||
144 | } | 185 | } |
145 | 186 | ||
146 | cpu_pmu->get_hw_events = cpu_pmu_get_cpu_events; | 187 | cpu_pmu->get_hw_events = cpu_pmu_get_cpu_events; |