Skip to content

Commit cc88116

Browse files
mrutland-armwildea01
authored andcommitted
arm: perf: treat PMUs as CPU affine
In multi-cluster systems, the PMUs can be different across clusters, and so our logical PMU may not be able to schedule events on all CPUs. This patch adds a cpumask to encode which CPUs a PMU driver supports controlling events for, and limits the driver to scheduling events on those CPUs, and enabling and disabling the physical PMUs on those CPUs. The cpumask is built based on the interrupt-affinity property, and in the absence of such a property a homogenous system is assumed. Acked-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
1 parent 64d0d39 commit cc88116

File tree

3 files changed

+38
-3
lines changed

3 files changed

+38
-3
lines changed

arch/arm/include/asm/pmu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ struct pmu_hw_events {
9292
struct arm_pmu {
9393
struct pmu pmu;
9494
cpumask_t active_irqs;
95+
cpumask_t supported_cpus;
9596
int *irq_affinity;
9697
char *name;
9798
irqreturn_t (*handle_irq)(int irq_num, void *dev);

arch/arm/kernel/perf_event.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212
#define pr_fmt(fmt) "hw perfevents: " fmt
1313

14+
#include <linux/cpumask.h>
1415
#include <linux/kernel.h>
1516
#include <linux/platform_device.h>
1617
#include <linux/pm_runtime.h>
@@ -229,6 +230,10 @@ armpmu_add(struct perf_event *event, int flags)
229230
int idx;
230231
int err = 0;
231232

233+
/* An event following a process won't be stopped earlier */
234+
if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus))
235+
return -ENOENT;
236+
232237
perf_pmu_disable(event->pmu);
233238

234239
/* If we don't have a space for the counter then finish early. */
@@ -454,6 +459,17 @@ static int armpmu_event_init(struct perf_event *event)
454459
int err = 0;
455460
atomic_t *active_events = &armpmu->active_events;
456461

462+
/*
463+
* Reject CPU-affine events for CPUs that are of a different class to
464+
* that which this PMU handles. Process-following events (where
465+
* event->cpu == -1) can be migrated between CPUs, and thus we have to
466+
* reject them later (in armpmu_add) if they're scheduled on a
467+
* different class of CPU.
468+
*/
469+
if (event->cpu != -1 &&
470+
!cpumask_test_cpu(event->cpu, &armpmu->supported_cpus))
471+
return -ENOENT;
472+
457473
/* does not support taken branch sampling */
458474
if (has_branch_stack(event))
459475
return -EOPNOTSUPP;
@@ -489,13 +505,22 @@ static void armpmu_enable(struct pmu *pmu)
489505
struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events);
490506
int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events);
491507

508+
/* For task-bound events we may be called on other CPUs */
509+
if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus))
510+
return;
511+
492512
if (enabled)
493513
armpmu->start(armpmu);
494514
}
495515

496516
static void armpmu_disable(struct pmu *pmu)
497517
{
498518
struct arm_pmu *armpmu = to_arm_pmu(pmu);
519+
520+
/* For task-bound events we may be called on other CPUs */
521+
if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus))
522+
return;
523+
499524
armpmu->stop(armpmu);
500525
}
501526

arch/arm/kernel/perf_event_cpu.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,15 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
179179
static int cpu_pmu_notify(struct notifier_block *b, unsigned long action,
180180
void *hcpu)
181181
{
182+
int cpu = (unsigned long)hcpu;
182183
struct arm_pmu *pmu = container_of(b, struct arm_pmu, hotplug_nb);
183184

184185
if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING)
185186
return NOTIFY_DONE;
186187

188+
if (!cpumask_test_cpu(cpu, &pmu->supported_cpus))
189+
return NOTIFY_DONE;
190+
187191
if (pmu->reset)
188192
pmu->reset(pmu);
189193
else
@@ -219,7 +223,8 @@ static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
219223

220224
/* Ensure the PMU has sane values out of reset. */
221225
if (cpu_pmu->reset)
222-
on_each_cpu(cpu_pmu->reset, cpu_pmu, 1);
226+
on_each_cpu_mask(&cpu_pmu->supported_cpus, cpu_pmu->reset,
227+
cpu_pmu, 1);
223228

224229
/* If no interrupts available, set the corresponding capability flag */
225230
if (!platform_get_irq(cpu_pmu->plat_device, 0))
@@ -334,12 +339,15 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
334339
}
335340

336341
irqs[i] = cpu;
342+
cpumask_set_cpu(cpu, &pmu->supported_cpus);
337343
}
338344

339-
if (i == pdev->num_resources)
345+
if (i == pdev->num_resources) {
340346
pmu->irq_affinity = irqs;
341-
else
347+
} else {
342348
kfree(irqs);
349+
cpumask_setall(&pmu->supported_cpus);
350+
}
343351

344352
return 0;
345353
}
@@ -374,6 +382,7 @@ static int cpu_pmu_device_probe(struct platform_device *pdev)
374382
ret = init_fn(pmu);
375383
} else {
376384
ret = probe_current_pmu(pmu);
385+
cpumask_setall(&pmu->supported_cpus);
377386
}
378387

379388
if (ret) {

0 commit comments

Comments
 (0)