Skip to content

Commit fe40ffd

Browse files
mrutland-armwilldeacon
authored andcommitted
arm_pmu: rework ACPI probing
The current ACPI PMU probing logic tries to associate PMUs with CPUs when the CPU is first brought online, in order to handle late hotplug, though PMUs are only registered during early boot, and so for late hotplugged CPUs this can only associate the CPU with an existing PMU. We tried to be clever and the have the arm_pmu_acpi_cpu_starting() callback allocate a struct arm_pmu when no matching instance is found, in order to avoid duplication of logic. However, as above this doesn't do anything useful for late hotplugged CPUs, and this requires us to allocate memory in an atomic context, which is especially problematic for PREEMPT_RT, as reported by Valentin and Pierre. This patch reworks the probing to detect PMUs for all online CPUs in the arm_pmu_acpi_probe() function, which is more aligned with how DT probing works. The arm_pmu_acpi_cpu_starting() callback only tries to associate CPUs with an existing arm_pmu instance, avoiding the problem of allocating in atomic context. Note that as we didn't previously register PMUs for late-hotplugged CPUs, this change doesn't result in a loss of existing functionality, though we will now warn when we cannot associate a CPU with a PMU. This change allows us to pull the hotplug callback registration into the arm_pmu_acpi_probe() function, as we no longer need the callbacks to be invoked shortly after probing the boot CPUs, and can register it without invoking the calls. For the moment the arm_pmu_acpi_init() initcall remains to register the SPE PMU, though in future this should probably be moved elsewhere (e.g. the arm64 ACPI init code), since this doesn't need to be tied to the regular CPU PMU code. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Reported-by: Valentin Schneider <valentin.schneider@arm.com> Link: https://lore.kernel.org/r/20210810134127.1394269-2-valentin.schneider@arm.com/ Reported-by: Pierre Gondois <pierre.gondois@arm.com> Link: https://lore.kernel.org/linux-arm-kernel/20220912155105.1443303-1-pierre.gondois@arm.com/ Cc: Pierre Gondois <pierre.gondois@arm.com> Cc: Valentin Schneider <vschneid@redhat.com> Cc: Will Deacon <will@kernel.org> Reviewed-and-tested-by: Pierre Gondois <pierre.gondois@arm.com> Link: https://lore.kernel.org/r/20220930111844.1522365-4-mark.rutland@arm.com Signed-off-by: Will Deacon <will@kernel.org>
1 parent 6349a24 commit fe40ffd

3 files changed

Lines changed: 52 additions & 61 deletions

File tree

drivers/perf/arm_pmu.c

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -861,16 +861,16 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)
861861
&cpu_pmu->node);
862862
}
863863

864-
static struct arm_pmu *__armpmu_alloc(gfp_t flags)
864+
struct arm_pmu *armpmu_alloc(void)
865865
{
866866
struct arm_pmu *pmu;
867867
int cpu;
868868

869-
pmu = kzalloc(sizeof(*pmu), flags);
869+
pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
870870
if (!pmu)
871871
goto out;
872872

873-
pmu->hw_events = alloc_percpu_gfp(struct pmu_hw_events, flags);
873+
pmu->hw_events = alloc_percpu_gfp(struct pmu_hw_events, GFP_KERNEL);
874874
if (!pmu->hw_events) {
875875
pr_info("failed to allocate per-cpu PMU data.\n");
876876
goto out_free_pmu;
@@ -916,17 +916,6 @@ static struct arm_pmu *__armpmu_alloc(gfp_t flags)
916916
return NULL;
917917
}
918918

919-
struct arm_pmu *armpmu_alloc(void)
920-
{
921-
return __armpmu_alloc(GFP_KERNEL);
922-
}
923-
924-
struct arm_pmu *armpmu_alloc_atomic(void)
925-
{
926-
return __armpmu_alloc(GFP_ATOMIC);
927-
}
928-
929-
930919
void armpmu_free(struct arm_pmu *pmu)
931920
{
932921
free_percpu(pmu->hw_events);

drivers/perf/arm_pmu_acpi.c

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/percpu.h>
1414
#include <linux/perf/arm_pmu.h>
1515

16+
#include <asm/cpu.h>
1617
#include <asm/cputype.h>
1718

1819
static DEFINE_PER_CPU(struct arm_pmu *, probed_pmus);
@@ -204,26 +205,6 @@ static struct arm_pmu *arm_pmu_acpi_find_pmu(void)
204205
return NULL;
205206
}
206207

207-
static struct arm_pmu *arm_pmu_acpi_find_alloc_pmu(void)
208-
{
209-
struct arm_pmu *pmu;
210-
211-
pmu = arm_pmu_acpi_find_pmu();
212-
if (pmu)
213-
return pmu;
214-
215-
pmu = armpmu_alloc_atomic();
216-
if (!pmu) {
217-
pr_warn("Unable to allocate PMU for CPU%d\n",
218-
smp_processor_id());
219-
return NULL;
220-
}
221-
222-
pmu->acpi_cpuid = read_cpuid_id();
223-
224-
return pmu;
225-
}
226-
227208
/*
228209
* Check whether the new IRQ is compatible with those already associated with
229210
* the PMU (e.g. we don't have mismatched PPIs).
@@ -286,26 +267,45 @@ static int arm_pmu_acpi_cpu_starting(unsigned int cpu)
286267
if (per_cpu(probed_pmus, cpu))
287268
return 0;
288269

289-
pmu = arm_pmu_acpi_find_alloc_pmu();
290-
if (!pmu)
291-
return -ENOMEM;
270+
pmu = arm_pmu_acpi_find_pmu();
271+
if (!pmu) {
272+
pr_warn_ratelimited("Unable to associate CPU%d with a PMU\n",
273+
cpu);
274+
return 0;
275+
}
292276

293277
arm_pmu_acpi_associate_pmu_cpu(pmu, cpu);
294-
295-
/*
296-
* Ideally, we'd probe the PMU here when we find the first matching
297-
* CPU. We can't do that for several reasons; see the comment in
298-
* arm_pmu_acpi_init().
299-
*
300-
* So for the time being, we're done.
301-
*/
302278
return 0;
303279
}
304280

281+
static void arm_pmu_acpi_probe_matching_cpus(struct arm_pmu *pmu,
282+
unsigned long cpuid)
283+
{
284+
int cpu;
285+
286+
for_each_online_cpu(cpu) {
287+
unsigned long cpu_cpuid = per_cpu(cpu_data, cpu).reg_midr;
288+
289+
if (cpu_cpuid == cpuid)
290+
arm_pmu_acpi_associate_pmu_cpu(pmu, cpu);
291+
}
292+
}
293+
305294
int arm_pmu_acpi_probe(armpmu_init_fn init_fn)
306295
{
307296
int pmu_idx = 0;
308-
int cpu, ret;
297+
unsigned int cpu;
298+
int ret;
299+
300+
ret = arm_pmu_acpi_parse_irqs();
301+
if (ret)
302+
return ret;
303+
304+
ret = cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_ACPI_STARTING,
305+
"perf/arm/pmu_acpi:starting",
306+
arm_pmu_acpi_cpu_starting, NULL);
307+
if (ret)
308+
return ret;
309309

310310
/*
311311
* Initialise and register the set of PMUs which we know about right
@@ -320,13 +320,26 @@ int arm_pmu_acpi_probe(armpmu_init_fn init_fn)
320320
* For the moment, as with the platform/DT case, we need at least one
321321
* of a PMU's CPUs to be online at probe time.
322322
*/
323-
for_each_possible_cpu(cpu) {
323+
for_each_online_cpu(cpu) {
324324
struct arm_pmu *pmu = per_cpu(probed_pmus, cpu);
325+
unsigned long cpuid;
325326
char *base_name;
326327

327-
if (!pmu || pmu->name)
328+
/* If we've already probed this CPU, we have nothing to do */
329+
if (pmu)
328330
continue;
329331

332+
pmu = armpmu_alloc();
333+
if (!pmu) {
334+
pr_warn("Unable to allocate PMU for CPU%d\n",
335+
cpu);
336+
}
337+
338+
cpuid = per_cpu(cpu_data, cpu).reg_midr;
339+
pmu->acpi_cpuid = cpuid;
340+
341+
arm_pmu_acpi_probe_matching_cpus(pmu, cpuid);
342+
330343
ret = init_fn(pmu);
331344
if (ret == -ENODEV) {
332345
/* PMU not handled by this driver, or not present */
@@ -351,26 +364,16 @@ int arm_pmu_acpi_probe(armpmu_init_fn init_fn)
351364
}
352365
}
353366

354-
return 0;
367+
return ret;
355368
}
356369

357370
static int arm_pmu_acpi_init(void)
358371
{
359-
int ret;
360-
361372
if (acpi_disabled)
362373
return 0;
363374

364375
arm_spe_acpi_register_device();
365376

366-
ret = arm_pmu_acpi_parse_irqs();
367-
if (ret)
368-
return ret;
369-
370-
ret = cpuhp_setup_state(CPUHP_AP_PERF_ARM_ACPI_STARTING,
371-
"perf/arm/pmu_acpi:starting",
372-
arm_pmu_acpi_cpu_starting, NULL);
373-
374-
return ret;
377+
return 0;
375378
}
376379
subsys_initcall(arm_pmu_acpi_init)

include/linux/perf/arm_pmu.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ void kvm_host_pmu_init(struct arm_pmu *pmu);
174174

175175
/* Internal functions only for core arm_pmu code */
176176
struct arm_pmu *armpmu_alloc(void);
177-
struct arm_pmu *armpmu_alloc_atomic(void);
178177
void armpmu_free(struct arm_pmu *pmu);
179178
int armpmu_register(struct arm_pmu *pmu);
180179
int armpmu_request_irq(int irq, int cpu);

0 commit comments

Comments
 (0)