Skip to contentSkip to conversation

arm_pmu: fix lockdep issues with ACPI systems

PATCH Superseded Mark Rutland 4 participants 19 messages Nov 1, 2017 – Dec 11, 2017 Arm PMU profiling and debugging

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Mark Rutland

author maintainer
#0 lore raw

cover arm_pmu: fix lockdep issues with ACPI systems

Currently the arm_pmu ACPI code is somewhat dubious. It attempts ot allocate memory any manpiulate IRQs in a hotplug callback, which is an atomic context.

These patches (based on the arm64 for-next/core branch [1]) attempt to address this by moving work out of hotplug callback, requiring a reorganisation of the common arm_pmu code.

I've given these a boot-test on a Juno R1 system, both with DT and ACPI. In either case the PMU works as expected, and lockdep seems happy.

I've pushed the series out to my arm64/acpi-pmu-lockdep branch [2].

Thanks,
Mark.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git/log/?h=for-next/core
[2] https://git.kernel.org/pub/scm/linux/kernel/git/mark/linux.git/log/?h=arm64/acpi-pmu-lockdep

Mark Rutland (5):
arm_pmu: fold platform helpers into platform code
arm_pmu: have armpmu_alloc() take GFP flags
arm_pmu: acpi: check for mismatched PPIs
arm_pmu: note IRQs/PMUs per-cpu
arm_pmu: acpi: request IRQs up-front

 drivers/perf/arm_pmu.c          | 107 ++++++++++++++--------------------------
 drivers/perf/arm_pmu_acpi.c     |  62 ++++++++++++++++-------
 drivers/perf/arm_pmu_platform.c |  42 +++++++++++++++-
 include/linux/perf/arm_pmu.h    |  20 +++++---
 4 files changed, 137 insertions(+), 94 deletions(-)
Signature · 1 line
2.11.0
author maintainer
#1 lore raw

PATCH 1/5 arm_pmu: fold platform helpers into platform code

The armpmu_{request,free}_irqs() helpers are only used by arm_pmu_platform.c, so let's fold them in and make them static.

---
 drivers/perf/arm_pmu.c          | 21 ---------------------
 drivers/perf/arm_pmu_platform.c | 21 +++++++++++++++++++++
 include/linux/perf/arm_pmu.h    |  2 --
 3 files changed, 21 insertions(+), 23 deletions(-)
drivers/perf/arm_pmu.c +0 -21
index 7bc5eee96b31..57df8dce8e19 100644
@@ -548,14 +548,6 @@ void armpmu_free_irq(struct arm_pmu *armpmu, int cpu)
 	free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, cpu));
 }
 
-void armpmu_free_irqs(struct arm_pmu *armpmu)
-{
-	int cpu;
-
-	for_each_cpu(cpu, &armpmu->supported_cpus)
-		armpmu_free_irq(armpmu, cpu);
-}
-
 int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
 {
 	int err = 0;
@@ -612,19 +604,6 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
 	return err;
 }
 
-int armpmu_request_irqs(struct arm_pmu *armpmu)
-{
-	int cpu, err;
-
-	for_each_cpu(cpu, &armpmu->supported_cpus) {
-		err = armpmu_request_irq(armpmu, cpu);
-		if (err)
-			break;
-	}
-
-	return err;
-}
-
 static int armpmu_get_cpu_irq(struct arm_pmu *pmu, int cpu)
 {
 	struct pmu_hw_events __percpu *hw_events = pmu->hw_events;
drivers/perf/arm_pmu_platform.c +21 -0
index bbc64ee6dd96..db863f14c406 100644
@@ -172,6 +172,27 @@ static int pmu_parse_irqs(struct arm_pmu *pmu)
 	return 0;
 }
 
+static int armpmu_request_irqs(struct arm_pmu *armpmu)
+{
+	int cpu, err;
+
+	for_each_cpu(cpu, &armpmu->supported_cpus) {
+		err = armpmu_request_irq(armpmu, cpu);
+		if (err)
+			break;
+	}
+
+	return err;
+}
+
+static void armpmu_free_irqs(struct arm_pmu *armpmu)
+{
+	int cpu;
+
+	for_each_cpu(cpu, &armpmu->supported_cpus)
+		armpmu_free_irq(armpmu, cpu);
+}
+
 int arm_pmu_device_probe(struct platform_device *pdev,
 			 const struct of_device_id *of_table,
 			 const struct pmu_probe_info *probe_table)
include/linux/perf/arm_pmu.h +0 -2
index af0f44effd44..97426407904d 100644
@@ -176,8 +176,6 @@ static inline int arm_pmu_acpi_probe(armpmu_init_fn init_fn) { return 0; }
 struct arm_pmu *armpmu_alloc(void);
 void armpmu_free(struct arm_pmu *pmu);
 int armpmu_register(struct arm_pmu *pmu);
-int armpmu_request_irqs(struct arm_pmu *armpmu);
-void armpmu_free_irqs(struct arm_pmu *armpmu);
 int armpmu_request_irq(struct arm_pmu *armpmu, int cpu);
 void armpmu_free_irq(struct arm_pmu *armpmu, int cpu);
 
Signature · 1 line
2.11.0
author maintainer
#2 lore raw

PATCH 2/5 arm_pmu: have armpmu_alloc() take GFP flags

In reply to Mark Rutland

In ACPI systems, we don't know the makeup of CPUs until we hotplug them on, and thus have to allocate the PMU datastrcutures at hotplug time. Thus, we must use GFP_ATOMIC allocations.

Reorganise the PMU allocators to take a GFP argument so that we can permit this.

---
 drivers/perf/arm_pmu.c          | 6 +++---
 drivers/perf/arm_pmu_acpi.c     | 2 +-
 drivers/perf/arm_pmu_platform.c | 2 +-
 include/linux/perf/arm_pmu.h    | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)
drivers/perf/arm_pmu.c +3 -3
index 57df8dce8e19..3d6d4c5f2356 100644
@@ -779,18 +779,18 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)
 					    &cpu_pmu->node);
 }
 
-struct arm_pmu *armpmu_alloc(void)
+struct arm_pmu *armpmu_alloc(gfp_t flags)
 {
 	struct arm_pmu *pmu;
 	int cpu;
 
-	pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
+	pmu = kzalloc(sizeof(*pmu), flags);
 	if (!pmu) {
 		pr_info("failed to allocate PMU device!\n");
 		goto out;
 	}
 
-	pmu->hw_events = alloc_percpu(struct pmu_hw_events);
+	pmu->hw_events = alloc_percpu_gfp(struct pmu_hw_events, flags);
 	if (!pmu->hw_events) {
 		pr_info("failed to allocate per-cpu PMU data.\n");
 		goto out_free_pmu;
drivers/perf/arm_pmu_acpi.c +1 -1
index 705f1a390e31..a52f5b673a15 100644
@@ -127,7 +127,7 @@ static struct arm_pmu *arm_pmu_acpi_find_alloc_pmu(void)
 		return pmu;
 	}
 
-	pmu = armpmu_alloc();
+	pmu = armpmu_alloc(GFP_ATOMIC);
 	if (!pmu) {
 		pr_warn("Unable to allocate PMU for CPU%d\n",
 			smp_processor_id());
drivers/perf/arm_pmu_platform.c +1 -1
index db863f14c406..686b3f28c5d1 100644
@@ -203,7 +203,7 @@ int arm_pmu_device_probe(struct platform_device *pdev,
 	struct arm_pmu *pmu;
 	int ret = -ENODEV;
 
-	pmu = armpmu_alloc();
+	pmu = armpmu_alloc(GFP_KERNEL);
 	if (!pmu)
 		return -ENOMEM;
 
include/linux/perf/arm_pmu.h +1 -1
index 97426407904d..52e6d56a6a4e 100644
@@ -173,7 +173,7 @@ static inline int arm_pmu_acpi_probe(armpmu_init_fn init_fn) { return 0; }
 #endif
 
 /* Internal functions only for core arm_pmu code */
-struct arm_pmu *armpmu_alloc(void);
+struct arm_pmu *armpmu_alloc(gfp_t flags);
 void armpmu_free(struct arm_pmu *pmu);
 int armpmu_register(struct arm_pmu *pmu);
 int armpmu_request_irq(struct arm_pmu *armpmu, int cpu);
Signature · 1 line
2.11.0
#3 lore raw

PATCH 2/5 arm_pmu: have armpmu_alloc() take GFP flags

Hi Mark,

We (HiSilicon) also met this issue and it works happily with this patch, but it is still not in 4.15 version, any special reasons?

Thanks,
Shaokun

Quoted 82 lines · Mark Rutland

In ACPI systems, we don't know the makeup of CPUs until we hotplug them on, and thus have to allocate the PMU datastrcutures at hotplug time. Thus, we must use GFP_ATOMIC allocations.

Reorganise the PMU allocators to take a GFP argument so that we can permit this.

---
 drivers/perf/arm_pmu.c          | 6 +++---
 drivers/perf/arm_pmu_acpi.c     | 2 +-
 drivers/perf/arm_pmu_platform.c | 2 +-
 include/linux/perf/arm_pmu.h    | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)
drivers/perf/arm_pmu.c +3 -3
index 57df8dce8e19..3d6d4c5f2356 100644
@@ -779,18 +779,18 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)
 					    &cpu_pmu->node);
 }
 
-struct arm_pmu *armpmu_alloc(void)
+struct arm_pmu *armpmu_alloc(gfp_t flags)
 {
 	struct arm_pmu *pmu;
 	int cpu;
 
-	pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
+	pmu = kzalloc(sizeof(*pmu), flags);
 	if (!pmu) {
 		pr_info("failed to allocate PMU device!\n");
 		goto out;
 	}
 
-	pmu->hw_events = alloc_percpu(struct pmu_hw_events);
+	pmu->hw_events = alloc_percpu_gfp(struct pmu_hw_events, flags);
 	if (!pmu->hw_events) {
 		pr_info("failed to allocate per-cpu PMU data.\n");
 		goto out_free_pmu;
drivers/perf/arm_pmu_acpi.c +1 -1
index 705f1a390e31..a52f5b673a15 100644
@@ -127,7 +127,7 @@ static struct arm_pmu *arm_pmu_acpi_find_alloc_pmu(void)
 		return pmu;
 	}
 
-	pmu = armpmu_alloc();
+	pmu = armpmu_alloc(GFP_ATOMIC);
 	if (!pmu) {
 		pr_warn("Unable to allocate PMU for CPU%d\n",
 			smp_processor_id());
drivers/perf/arm_pmu_platform.c +1 -1
index db863f14c406..686b3f28c5d1 100644
@@ -203,7 +203,7 @@ int arm_pmu_device_probe(struct platform_device *pdev,
 	struct arm_pmu *pmu;
 	int ret = -ENODEV;
 
-	pmu = armpmu_alloc();
+	pmu = armpmu_alloc(GFP_KERNEL);
 	if (!pmu)
 		return -ENOMEM;
 
include/linux/perf/arm_pmu.h +1 -1
index 97426407904d..52e6d56a6a4e 100644
@@ -173,7 +173,7 @@ static inline int arm_pmu_acpi_probe(armpmu_init_fn init_fn) { return 0; }
 #endif
 
 /* Internal functions only for core arm_pmu code */
-struct arm_pmu *armpmu_alloc(void);
+struct arm_pmu *armpmu_alloc(gfp_t flags);
 void armpmu_free(struct arm_pmu *pmu);
 int armpmu_register(struct arm_pmu *pmu);
 int armpmu_request_irq(struct arm_pmu *armpmu, int cpu);
#4 lore raw

PATCH 2/5 arm_pmu: have armpmu_alloc() take GFP flags

In reply to Mark Rutland
Quoted 47 lines · Mark Rutland

In ACPI systems, we don't know the makeup of CPUs until we hotplug them on, and thus have to allocate the PMU datastrcutures at hotplug time. Thus, we must use GFP_ATOMIC allocations.

Reorganise the PMU allocators to take a GFP argument so that we can permit this.

---
 drivers/perf/arm_pmu.c          | 6 +++---
 drivers/perf/arm_pmu_acpi.c     | 2 +-
 drivers/perf/arm_pmu_platform.c | 2 +-
 include/linux/perf/arm_pmu.h    | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)
drivers/perf/arm_pmu.c +3 -3
index 57df8dce8e19..3d6d4c5f2356 100644
@@ -779,18 +779,18 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)
 					    &cpu_pmu->node);
 }
 
-struct arm_pmu *armpmu_alloc(void)
+struct arm_pmu *armpmu_alloc(gfp_t flags)
 {
 	struct arm_pmu *pmu;
 	int cpu;
 
-	pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
+	pmu = kzalloc(sizeof(*pmu), flags);
 	if (!pmu) {
 		pr_info("failed to allocate PMU device!\n");
 		goto out;
 	}
 
-	pmu->hw_events = alloc_percpu(struct pmu_hw_events);
+	pmu->hw_events = alloc_percpu_gfp(struct pmu_hw_events, flags);
 	if (!pmu->hw_events) {
 		pr_info("failed to allocate per-cpu PMU data.\n");
 		goto out_free_pmu;
drivers/perf/arm_pmu_acpi.c +0 -0
index 705f1a390e31..a52f5b673a15 100644

drivers/perf/arm_pmu_acpi.c static struct arm_pmu *arm_pmu_acpi_find_alloc_pmu(void) · line 130

Quoted 5 lines
drivers/perf/arm_pmu_acpi.c +1 -1
@@ -127,7 +127,7 @@ static struct arm_pmu *arm_pmu_acpi_find_alloc_pmu(void)
 		return pmu;
 	}
 
-	pmu = armpmu_alloc();
+	pmu = armpmu_alloc(GFP_ATOMIC);

I think I'd rather have armpmu_alloc_atomic as a wrapper around __armpmu_alloc(GFP_ATOMIC) and then leave the armpmu_alloc to pass GFP_KERNEL.

Will

author maintainer
#5 lore raw

PATCH 2/5 arm_pmu: have armpmu_alloc() take GFP flags

Quoted 9 lines · Will Deacon
Quoted 8 lines · Mark Rutland
drivers/perf/arm_pmu_acpi.c +0 -0
index 705f1a390e31..a52f5b673a15 100644
@@ -127,7 +127,7 @@ static struct arm_pmu *arm_pmu_acpi_find_alloc_pmu(void)
 		return pmu;
 	}
 
Quoted 6 lines

drivers/perf/arm_pmu_acpi.c static struct arm_pmu *arm_pmu_acpi_find_alloc_pmu(void) · line 130

Quoted 2 lines
drivers/perf/arm_pmu_acpi.c +1 -1
-	pmu = armpmu_alloc();
+	pmu = armpmu_alloc(GFP_ATOMIC);

I think I'd rather have armpmu_alloc_atomic as a wrapper around __armpmu_alloc(GFP_ATOMIC) and then leave the armpmu_alloc to pass GFP_KERNEL.

Sure; done.

Mark.

author maintainer
#6 lore raw

PATCH 3/5 arm_pmu: acpi: check for mismatched PPIs

In reply to Mark Rutland

The arm_pmu platform code explicitly checks for mismatched PPIs at probe time, while the ACPI code leaves this to the core code. Future refactoring will make this difficult for the core code to check, so let's have the ACPI code check this explicitly.

As before, upon a failure we'll continue on without an interrupt. Ho hum.

---
 drivers/perf/arm_pmu.c      | 16 ++++------------
 drivers/perf/arm_pmu_acpi.c | 42 ++++++++++++++++++++++++++++++++++++++----
 2 files changed, 42 insertions(+), 16 deletions(-)
drivers/perf/arm_pmu.c +4 -12
index 3d6d4c5f2356..e0242103d904 100644
@@ -557,18 +557,10 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
 	if (!irq)
 		return 0;
 
-	if (irq_is_percpu_devid(irq) && cpumask_empty(&armpmu->active_irqs)) {
-		err = request_percpu_irq(irq, handler, "arm-pmu",
-					 &hw_events->percpu_pmu);
-	} else if (irq_is_percpu_devid(irq)) {
-		int other_cpu = cpumask_first(&armpmu->active_irqs);
-		int other_irq = per_cpu(hw_events->irq, other_cpu);
-
-		if (irq != other_irq) {
-			pr_warn("mismatched PPIs detected.\n");
-			err = -EINVAL;
-			goto err_out;
-		}
+	if (irq_is_percpu_devid(irq)) {
+		if (cpumask_empty(&armpmu->active_irqs))
+			err = request_percpu_irq(irq, handler, "arm-pmu",
+						 &hw_events->percpu_pmu);
 	} else {
 		struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu);
 		unsigned long irq_flags;
drivers/perf/arm_pmu_acpi.c +38 -4

68 lines not on this page · raw message

Signature · 1 line
2.11.0
#7 lore raw

PATCH 3/5 arm_pmu: acpi: check for mismatched PPIs

Quoted 27 lines · Mark Rutland

The arm_pmu platform code explicitly checks for mismatched PPIs at probe time, while the ACPI code leaves this to the core code. Future refactoring will make this difficult for the core code to check, so let's have the ACPI code check this explicitly.

As before, upon a failure we'll continue on without an interrupt. Ho hum.

---
 drivers/perf/arm_pmu.c      | 16 ++++------------
 drivers/perf/arm_pmu_acpi.c | 42 ++++++++++++++++++++++++++++++++++++++----
 2 files changed, 42 insertions(+), 16 deletions(-)
drivers/perf/arm_pmu.c +0 -4
index 3d6d4c5f2356..e0242103d904 100644
@@ -557,18 +557,10 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
 	if (!irq)
 		return 0;
 
-	if (irq_is_percpu_devid(irq) && cpumask_empty(&armpmu->active_irqs)) {
-		err = request_percpu_irq(irq, handler, "arm-pmu",
-					 &hw_events->percpu_pmu);
-	} else if (irq_is_percpu_devid(irq)) {

drivers/perf/arm_pmu.c int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) · line 561

Quoted 10 lines
drivers/perf/arm_pmu.c +2 -8
-		int other_cpu = cpumask_first(&armpmu->active_irqs);
-		int other_irq = per_cpu(hw_events->irq, other_cpu);
-
-		if (irq != other_irq) {
-			pr_warn("mismatched PPIs detected.\n");
-			err = -EINVAL;
-			goto err_out;
-		}
+	if (irq_is_percpu_devid(irq)) {
+		if (cpumask_empty(&armpmu->active_irqs))

Why not leave this as before, with a '&&' operator?

Will

author maintainer
#8 lore raw

PATCH 3/5 arm_pmu: acpi: check for mismatched PPIs

Quoted 24 lines · Will Deacon
Quoted 23 lines · Mark Rutland

The arm_pmu platform code explicitly checks for mismatched PPIs at probe time, while the ACPI code leaves this to the core code. Future refactoring will make this difficult for the core code to check, so let's have the ACPI code check this explicitly.

As before, upon a failure we'll continue on without an interrupt. Ho hum.

---
 drivers/perf/arm_pmu.c      | 16 ++++------------
 drivers/perf/arm_pmu_acpi.c | 42 ++++++++++++++++++++++++++++++++++++++----
 2 files changed, 42 insertions(+), 16 deletions(-)
drivers/perf/arm_pmu.c +0 -0
index 3d6d4c5f2356..e0242103d904 100644
@@ -557,18 +557,10 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
 	if (!irq)
 		return 0;
 
Quoted 16 lines
Quoted 4 lines
drivers/perf/arm_pmu.c +0 -4
-	if (irq_is_percpu_devid(irq) && cpumask_empty(&armpmu->active_irqs)) {
-		err = request_percpu_irq(irq, handler, "arm-pmu",
-					 &hw_events->percpu_pmu);
-	} else if (irq_is_percpu_devid(irq)) {

drivers/perf/arm_pmu.c int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) · line 561

Quoted 10 lines
drivers/perf/arm_pmu.c +2 -8
-		int other_cpu = cpumask_first(&armpmu->active_irqs);
-		int other_irq = per_cpu(hw_events->irq, other_cpu);
-
-		if (irq != other_irq) {
-			pr_warn("mismatched PPIs detected.\n");
-			err = -EINVAL;
-			goto err_out;
-		}
+	if (irq_is_percpu_devid(irq)) {
+		if (cpumask_empty(&armpmu->active_irqs))

Why not leave this as before, with a '&&' operator?

Because then we'd fall into the else case (for SPIs), were the active_irqs mask empty.

Previously, that would have been caught by the irq_is_percpu_devid(irq) case that got removed.

I can instead make this:

	if (irq_is_percpu_devid(irq) && cpumask_empty(&armpmu->active_irqs)) {
		err = request_percpu_irq(irq, handler, "arm-pmu",
					 &hw_events->percpu_pmu);
	} else if (irq_is_percpu_devid(irq)) {
		/* nothing to do */
	} else  { 
		< SPI case >
	}

... but that seemed more painful to read.

Mark.

#9 lore raw

PATCH 3/5 arm_pmu: acpi: check for mismatched PPIs

Quoted 59 lines · Mark Rutland
Quoted 24 lines · Will Deacon
Quoted 23 lines · Mark Rutland

The arm_pmu platform code explicitly checks for mismatched PPIs at probe time, while the ACPI code leaves this to the core code. Future refactoring will make this difficult for the core code to check, so let's have the ACPI code check this explicitly.

As before, upon a failure we'll continue on without an interrupt. Ho hum.

---
 drivers/perf/arm_pmu.c      | 16 ++++------------
 drivers/perf/arm_pmu_acpi.c | 42 ++++++++++++++++++++++++++++++++++++++----
 2 files changed, 42 insertions(+), 16 deletions(-)
drivers/perf/arm_pmu.c +0 -0
index 3d6d4c5f2356..e0242103d904 100644
@@ -557,18 +557,10 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
 	if (!irq)
 		return 0;
 
Quoted 16 lines
Quoted 4 lines
drivers/perf/arm_pmu.c +0 -4
-	if (irq_is_percpu_devid(irq) && cpumask_empty(&armpmu->active_irqs)) {
-		err = request_percpu_irq(irq, handler, "arm-pmu",
-					 &hw_events->percpu_pmu);
-	} else if (irq_is_percpu_devid(irq)) {

drivers/perf/arm_pmu.c int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) · line 561

Quoted 10 lines
drivers/perf/arm_pmu.c +2 -8
-		int other_cpu = cpumask_first(&armpmu->active_irqs);
-		int other_irq = per_cpu(hw_events->irq, other_cpu);
-
-		if (irq != other_irq) {
-			pr_warn("mismatched PPIs detected.\n");
-			err = -EINVAL;
-			goto err_out;
-		}
+	if (irq_is_percpu_devid(irq)) {
+		if (cpumask_empty(&armpmu->active_irqs))

Why not leave this as before, with a '&&' operator?

Because then we'd fall into the else case (for SPIs), were the active_irqs mask empty.

Previously, that would have been caught by the irq_is_percpu_devid(irq) case that got removed.

I can instead make this:

	if (irq_is_percpu_devid(irq) && cpumask_empty(&armpmu->active_irqs)) {
		err = request_percpu_irq(irq, handler, "arm-pmu",
					 &hw_events->percpu_pmu);
	} else if (irq_is_percpu_devid(irq)) {
		/* nothing to do */
	} else  { 
		< SPI case >
	}
Quoted 1 line

... but that seemed more painful to read.

Yeah, that's crazy :)

How about:

  if (!irq_is_percpu_devid(irq)) {
	/* SPI case */
  } else if (cpumask_empty(&armpmu->active_irqs)) {
	/* PPI case */

}

?

Will

author maintainer
#10 lore raw

PATCH 4/5 arm_pmu: note IRQs/PMUs per-cpu

In reply to Mark Rutland

The current way we manage IRQs forces the ACPI PMU driver to request IRQs in the cpu bringup path, which isn't safe due to implicit memory allocations in the request_irq() path.

To solve that, we need to decouple requesting IRQs from PMU management, requesting IRQs up-front, before we know the associated PMU. We will separately (and perhaps later) associate each IRQ with its PMU.

This patch allows the IRQ handlers to be registered without a PMU dev argument, using a percpu pointer instead.

---
 drivers/perf/arm_pmu.c       | 93 ++++++++++++++++++++++++++++----------------
 include/linux/perf/arm_pmu.h |  3 +-
 2 files changed, 62 insertions(+), 34 deletions(-)
drivers/perf/arm_pmu.c +60 -33
index e0242103d904..287b3edfb4cc 100644
@@ -26,6 +26,9 @@
 
 #include <asm/irq_regs.h>
 
+static DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu);
+static DEFINE_PER_CPU(int, cpu_irq);
+
 static int
 armpmu_map_cache_event(const unsigned (*cache_map)
 				      [PERF_COUNT_HW_CACHE_MAX]
@@ -334,13 +337,9 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
 	int ret;
 	u64 start_clock, finish_clock;
 
-	/*
-	 * we request the IRQ with a (possibly percpu) struct arm_pmu**, but
-	 * the handlers expect a struct arm_pmu*. The percpu_irq framework will
-	 * do any necessary shifting, we just need to perform the first
-	 * dereference.
-	 */
-	armpmu = *(void **)dev;
+	armpmu = this_cpu_read(cpu_armpmu);
+	if (WARN_ON_ONCE(!armpmu))
+		return IRQ_NONE;
 
 	plat = armpmu_get_platdata(armpmu);
 
@@ -531,40 +530,56 @@ int perf_num_counters(void)
 }
 EXPORT_SYMBOL_GPL(perf_num_counters);
 
-void armpmu_free_irq(struct arm_pmu *armpmu, int cpu)
+int armpmu_count_irq_users(const int irq)
 {
-	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
-	int irq = per_cpu(hw_events->irq, cpu);
+	int cpu, count = 0;
+
+	for_each_possible_cpu(cpu) {
+		if (per_cpu(cpu_irq, cpu) == irq)
+			count++;
+	}
+
+	return count;
+}
 
-	if (!cpumask_test_and_clear_cpu(cpu, &armpmu->active_irqs))
+void __armpmu_free_irq(int irq, int cpu)
+{
+	if (per_cpu(cpu_irq, cpu) == 0)
+		return;
+	if (WARN_ON(irq != per_cpu(cpu_irq, cpu)))
 		return;
 
 	if (irq_is_percpu_devid(irq)) {
-		free_percpu_irq(irq, &hw_events->percpu_pmu);
-		cpumask_clear(&armpmu->active_irqs);
-		return;
+		if (armpmu_count_irq_users(irq) == 1)
+			free_percpu_irq(irq, &cpu_armpmu);
+	} else {
+		free_irq(irq, NULL);
 	}
 
-	free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, cpu));
+	per_cpu(cpu_irq, cpu) = 0;
 }
 
-int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
+void armpmu_free_irq(struct arm_pmu *armpmu, int cpu)
 {
-	int err = 0;
 	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
-	const irq_handler_t handler = armpmu_dispatch_irq;
 	int irq = per_cpu(hw_events->irq, cpu);
+
+	__armpmu_free_irq(irq, cpu);
+	per_cpu(cpu_armpmu, cpu) = NULL;
+}
+
+int armpmu_request_irq_flags(int irq, unsigned long irq_flags, int cpu)
+{
+	int err = 0;
+	const irq_handler_t handler = armpmu_dispatch_irq;
 	if (!irq)
 		return 0;
 
 	if (irq_is_percpu_devid(irq)) {
-		if (cpumask_empty(&armpmu->active_irqs))
+		if (armpmu_count_irq_users(irq) == 0)
 			err = request_percpu_irq(irq, handler, "arm-pmu",
-						 &hw_events->percpu_pmu);
+						 &cpu_armpmu);
 	} else {
-		struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu);
-		unsigned long irq_flags;
-
 		err = irq_force_affinity(irq, cpumask_of(cpu));
 
 		if (err && num_possible_cpus() > 1) {
@@ -573,22 +588,14 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
 			goto err_out;
 		}
 
-		if (platdata && platdata->irq_flags) {
-			irq_flags = platdata->irq_flags;
-		} else {
-			irq_flags = IRQF_PERCPU |
-				    IRQF_NOBALANCING |
-				    IRQF_NO_THREAD;
-		}
-
 		err = request_irq(irq, handler, irq_flags, "arm-pmu",
-				  per_cpu_ptr(&hw_events->percpu_pmu, cpu));
+				  NULL);
 	}
 
 	if (err)
 		goto err_out;
 
-	cpumask_set_cpu(cpu, &armpmu->active_irqs);
+	per_cpu(cpu_irq, cpu) = irq;
 	return 0;
 
 err_out:
@@ -596,6 +603,26 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
 	return err;
 }
 
+int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
+{
+	struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu);
+	unsigned long irq_flags;
+	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
+	int irq = per_cpu(hw_events->irq, cpu);
+	if (!irq)
+		return 0;
+
+	if (platdata && platdata->irq_flags) {
+		irq_flags = platdata->irq_flags;
+	} else {
+		irq_flags = ARM_PMU_IRQ_FLAGS;
+	}
+
+	per_cpu(cpu_armpmu, cpu) = armpmu;
+
+	return armpmu_request_irq_flags(irq, irq_flags, cpu);
+}
+
 static int armpmu_get_cpu_irq(struct arm_pmu *pmu, int cpu)
 {
 	struct pmu_hw_events __percpu *hw_events = pmu->hw_events;
include/linux/perf/arm_pmu.h +2 -1
index 52e6d56a6a4e..2ce6db4e00b2 100644
@@ -92,7 +92,6 @@ enum armpmu_attr_groups {
 
 struct arm_pmu {
 	struct pmu	pmu;
-	cpumask_t	active_irqs;
 	cpumask_t	supported_cpus;
 	char		*name;
 	irqreturn_t	(*handle_irq)(int irq_num, void *dev);
@@ -179,6 +178,8 @@ int armpmu_register(struct arm_pmu *pmu);
 int armpmu_request_irq(struct arm_pmu *armpmu, int cpu);
 void armpmu_free_irq(struct arm_pmu *armpmu, int cpu);
 
+#define ARM_PMU_IRQ_FLAGS (IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_THREAD)
+
 #define ARMV8_PMU_PDEV_NAME "armv8-pmu"
 
 #endif /* CONFIG_ARM_PMU */
Signature · 1 line
2.11.0
#11 lore raw

PATCH 4/5 arm_pmu: note IRQs/PMUs per-cpu

Quoted 72 lines · Mark Rutland

The current way we manage IRQs forces the ACPI PMU driver to request IRQs in the cpu bringup path, which isn't safe due to implicit memory allocations in the request_irq() path.

To solve that, we need to decouple requesting IRQs from PMU management, requesting IRQs up-front, before we know the associated PMU. We will separately (and perhaps later) associate each IRQ with its PMU.

This patch allows the IRQ handlers to be registered without a PMU dev argument, using a percpu pointer instead.

---
 drivers/perf/arm_pmu.c       | 93 ++++++++++++++++++++++++++++----------------
 include/linux/perf/arm_pmu.h |  3 +-
 2 files changed, 62 insertions(+), 34 deletions(-)
drivers/perf/arm_pmu.c +19 -11
index e0242103d904..287b3edfb4cc 100644
@@ -26,6 +26,9 @@
 
 #include <asm/irq_regs.h>
 
+static DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu);
+static DEFINE_PER_CPU(int, cpu_irq);
+
 static int
 armpmu_map_cache_event(const unsigned (*cache_map)
 				      [PERF_COUNT_HW_CACHE_MAX]
@@ -334,13 +337,9 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
 	int ret;
 	u64 start_clock, finish_clock;
 
-	/*
-	 * we request the IRQ with a (possibly percpu) struct arm_pmu**, but
-	 * the handlers expect a struct arm_pmu*. The percpu_irq framework will
-	 * do any necessary shifting, we just need to perform the first
-	 * dereference.
-	 */
-	armpmu = *(void **)dev;
+	armpmu = this_cpu_read(cpu_armpmu);
+	if (WARN_ON_ONCE(!armpmu))
+		return IRQ_NONE;
 
 	plat = armpmu_get_platdata(armpmu);
 
@@ -531,40 +530,56 @@ int perf_num_counters(void)
 }
 EXPORT_SYMBOL_GPL(perf_num_counters);
 
-void armpmu_free_irq(struct arm_pmu *armpmu, int cpu)
+int armpmu_count_irq_users(const int irq)
 {
-	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
-	int irq = per_cpu(hw_events->irq, cpu);
+	int cpu, count = 0;
+
+	for_each_possible_cpu(cpu) {
+		if (per_cpu(cpu_irq, cpu) == irq)
+			count++;
+	}
+
+	return count;
+}
 
-	if (!cpumask_test_and_clear_cpu(cpu, &armpmu->active_irqs))
+void __armpmu_free_irq(int irq, int cpu)
+{
+	if (per_cpu(cpu_irq, cpu) == 0)

drivers/perf/arm_pmu.c int perf_num_counters(void) · line 554

Quoted 10 lines
drivers/perf/arm_pmu.c +4 -3
+		return;
+	if (WARN_ON(irq != per_cpu(cpu_irq, cpu)))
 		return;
 
 	if (irq_is_percpu_devid(irq)) {
-		free_percpu_irq(irq, &hw_events->percpu_pmu);
-		cpumask_clear(&armpmu->active_irqs);
-		return;
+		if (armpmu_count_irq_users(irq) == 1)
+			free_percpu_irq(irq, &cpu_armpmu);

Do you actually need the count, or could you just free the irq the first time this is called and set all of the cpu_irqs to 0?

Will

author maintainer
#12 lore raw

PATCH 4/5 arm_pmu: note IRQs/PMUs per-cpu

Quoted 28 lines · Will Deacon
Quoted 27 lines · Mark Rutland
drivers/perf/arm_pmu.c +15 -4
@@ -531,40 +530,56 @@ int perf_num_counters(void)
 }
 EXPORT_SYMBOL_GPL(perf_num_counters);
 
-void armpmu_free_irq(struct arm_pmu *armpmu, int cpu)
+int armpmu_count_irq_users(const int irq)
 {
-	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
-	int irq = per_cpu(hw_events->irq, cpu);
+	int cpu, count = 0;
+
+	for_each_possible_cpu(cpu) {
+		if (per_cpu(cpu_irq, cpu) == irq)
+			count++;
+	}
+
+	return count;
+}
 
-	if (!cpumask_test_and_clear_cpu(cpu, &armpmu->active_irqs))
+void __armpmu_free_irq(int irq, int cpu)
+{
+	if (per_cpu(cpu_irq, cpu) == 0)
+		return;
+	if (WARN_ON(irq != per_cpu(cpu_irq, cpu)))
 		return;
 
Quoted 9 lines

drivers/perf/arm_pmu.c int perf_num_counters(void) · line 554

Quoted 6 lines
drivers/perf/arm_pmu.c +2 -3
 	if (irq_is_percpu_devid(irq)) {
-		free_percpu_irq(irq, &hw_events->percpu_pmu);
-		cpumask_clear(&armpmu->active_irqs);
-		return;
+		if (armpmu_count_irq_users(irq) == 1)
+			free_percpu_irq(irq, &cpu_armpmu);

Do you actually need the count, or could you just free the irq the first time this is called and set all of the cpu_irqs to 0?

It might be safe to blat all the matching cpu_irq entries. I'll take a look.

Thanks,
Mark.

author maintainer
#13 lore raw

PATCH 5/5 arm_pmu: acpi: request IRQs up-front

In reply to Mark Rutland

We can't request IRQs in atomic context, so for ACPI systems we'll have to request them up-front, and later associate them with CPUs.

This patch reorganises the arm_pmu code to do so. As we no longer have the arm_pmu strucutre at probe time, a number of prototypes need to be adjusted, requiring changes to the common arm_pmu code and arm_pmu platform code.

---
 drivers/perf/arm_pmu.c          | 37 +++----------------------------------
 drivers/perf/arm_pmu_acpi.c     | 20 +++++++-------------
 drivers/perf/arm_pmu_platform.c | 25 ++++++++++++++++++++++---
 include/linux/perf/arm_pmu.h    | 13 +++++++++++--
 4 files changed, 43 insertions(+), 52 deletions(-)
drivers/perf/arm_pmu.c +3 -34
index 287b3edfb4cc..534b4b3fb440 100644
@@ -323,13 +323,6 @@ validate_group(struct perf_event *event)
 	return 0;
 }
 
-static struct arm_pmu_platdata *armpmu_get_platdata(struct arm_pmu *armpmu)
-{
-	struct platform_device *pdev = armpmu->plat_device;
-
-	return pdev ? dev_get_platdata(&pdev->dev) : NULL;
-}
-
 static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
 {
 	struct arm_pmu *armpmu;
@@ -542,7 +535,7 @@ int armpmu_count_irq_users(const int irq)
 	return count;
 }
 
-void __armpmu_free_irq(int irq, int cpu)
+void armpmu_free_irq(int irq, int cpu)
 {
 	if (per_cpu(cpu_irq, cpu) == 0)
 		return;
@@ -559,16 +552,7 @@ void __armpmu_free_irq(int irq, int cpu)
 	per_cpu(cpu_irq, cpu) = 0;
 }
 
-void armpmu_free_irq(struct arm_pmu *armpmu, int cpu)
-{
-	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
-	int irq = per_cpu(hw_events->irq, cpu);
-
-	__armpmu_free_irq(irq, cpu);
-	per_cpu(cpu_armpmu, cpu) = NULL;
-}
-
-int armpmu_request_irq_flags(int irq, unsigned long irq_flags, int cpu)
+int armpmu_request_irq(int irq, unsigned long irq_flags, int cpu)
 {
 	int err = 0;
 	const irq_handler_t handler = armpmu_dispatch_irq;
@@ -603,24 +587,9 @@ int armpmu_request_irq_flags(int irq, unsigned long irq_flags, int cpu)
 	return err;
 }
 
-int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
+void armpmu_bind_cpu(struct arm_pmu *armpmu, int cpu)
 {
-	struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu);
-	unsigned long irq_flags;
-	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
-	int irq = per_cpu(hw_events->irq, cpu);
-	if (!irq)
-		return 0;
-
-	if (platdata && platdata->irq_flags) {
-		irq_flags = platdata->irq_flags;
-	} else {
-		irq_flags = ARM_PMU_IRQ_FLAGS;
-	}
-
 	per_cpu(cpu_armpmu, cpu) = armpmu;
-
-	return armpmu_request_irq_flags(irq, irq_flags, cpu);
 }
 
 static int armpmu_get_cpu_irq(struct arm_pmu *pmu, int cpu)
drivers/perf/arm_pmu_acpi.c +7 -13
index 07a113b8a3b2..8740314584aa 100644
@@ -89,7 +89,13 @@ static int arm_pmu_acpi_parse_irqs(void)
 			pr_warn("No ACPI PMU IRQ for CPU%d\n", cpu);
 		}
 
+		/*
+		 * Log and request the IRQ so the core arm_pmu code can manage
+		 * it. We'll have to sanity-check IRQs later when we associate
+		 * them with their PMUs.
+		 */
 		per_cpu(pmu_irqs, cpu) = irq;
+		armpmu_request_irq(irq, ARM_PMU_IRQ_FLAGS, cpu);
 	}
 
 	return 0;
@@ -203,14 +209,7 @@ static int arm_pmu_acpi_cpu_starting(unsigned int cpu)
 	}
 
 	cpumask_set_cpu(cpu, &pmu->supported_cpus);
-
-	/*
-	 * Log and request the IRQ so the core arm_pmu code can manage it.  In
-	 * some situations (e.g. mismatched PPIs), we may fail to request the
-	 * IRQ. However, it may be too late for us to do anything about it.
-	 * The common ARM PMU code will log a warning in this case.
-	 */
-	armpmu_request_irq(pmu, cpu);
+	armpmu_bind_cpu(pmu, cpu);
 
 	/*
 	 * Ideally, we'd probe the PMU here when we find the first matching
@@ -281,11 +280,6 @@ static int arm_pmu_acpi_init(void)
 	if (acpi_disabled)
 		return 0;
 
-	/*
-	 * We can't request IRQs yet, since we don't know the cookie value
-	 * until we know which CPUs share the same logical PMU. We'll handle
-	 * that in arm_pmu_acpi_cpu_starting().
-	 */
 	ret = arm_pmu_acpi_parse_irqs();
 	if (ret)
 		return ret;
drivers/perf/arm_pmu_platform.c +22 -3
index 686b3f28c5d1..f5aba6bafe1d 100644
@@ -174,10 +174,24 @@ static int pmu_parse_irqs(struct arm_pmu *pmu)
 
 static int armpmu_request_irqs(struct arm_pmu *armpmu)
 {
+	struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu);
+	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
+	unsigned long irq_flags;
 	int cpu, err;
 
+	if (platdata && platdata->irq_flags)
+		irq_flags = platdata->irq_flags;
+	else
+		irq_flags = ARM_PMU_IRQ_FLAGS;
+
 	for_each_cpu(cpu, &armpmu->supported_cpus) {
-		err = armpmu_request_irq(armpmu, cpu);
+		int irq = per_cpu(hw_events->irq, cpu);
+		if (!irq)
+			continue;
+
+		armpmu_bind_cpu(armpmu, cpu);
+
+		err = armpmu_request_irq(irq, irq_flags, cpu);
 		if (err)
 			break;
 	}
@@ -188,9 +202,14 @@ static int armpmu_request_irqs(struct arm_pmu *armpmu)
 static void armpmu_free_irqs(struct arm_pmu *armpmu)
 {
 	int cpu;
+	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
 
-	for_each_cpu(cpu, &armpmu->supported_cpus)
-		armpmu_free_irq(armpmu, cpu);
+	for_each_cpu(cpu, &armpmu->supported_cpus) {
+		int irq = per_cpu(hw_events->irq, cpu);
+
+		armpmu_free_irq(irq, cpu);
+		armpmu_bind_cpu(NULL, cpu);
+	}
 }
 
 int arm_pmu_device_probe(struct platform_device *pdev,
include/linux/perf/arm_pmu.h +11 -2
index 2ce6db4e00b2..00f11de8775e 100644
@@ -14,6 +14,7 @@
 
 #include <linux/interrupt.h>
 #include <linux/perf_event.h>
+#include <linux/platform_device.h>
 #include <linux/sysfs.h>
 #include <asm/cputype.h>
 
@@ -175,8 +176,16 @@ static inline int arm_pmu_acpi_probe(armpmu_init_fn init_fn) { return 0; }
 struct arm_pmu *armpmu_alloc(gfp_t flags);
 void armpmu_free(struct arm_pmu *pmu);
 int armpmu_register(struct arm_pmu *pmu);
-int armpmu_request_irq(struct arm_pmu *armpmu, int cpu);
-void armpmu_free_irq(struct arm_pmu *armpmu, int cpu);
+void armpmu_bind_cpu(struct arm_pmu *armpmu, int cpu);
+int armpmu_request_irq(int irq, unsigned long flags, int cpu);
+void armpmu_free_irq(int irq, int cpu);
+
+static inline struct arm_pmu_platdata *armpmu_get_platdata(struct arm_pmu *armpmu)
+{
+	struct platform_device *pdev = armpmu->plat_device;
+
+	return pdev ? dev_get_platdata(&pdev->dev) : NULL;
+}
 
 #define ARM_PMU_IRQ_FLAGS (IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_THREAD)
 
Signature · 1 line
2.11.0
#14 lore raw

PATCH 5/5 arm_pmu: acpi: request IRQs up-front

Quoted 73 lines · Mark Rutland

We can't request IRQs in atomic context, so for ACPI systems we'll have to request them up-front, and later associate them with CPUs.

This patch reorganises the arm_pmu code to do so. As we no longer have the arm_pmu strucutre at probe time, a number of prototypes need to be adjusted, requiring changes to the common arm_pmu code and arm_pmu platform code.

---
 drivers/perf/arm_pmu.c          | 37 +++----------------------------------
 drivers/perf/arm_pmu_acpi.c     | 20 +++++++-------------
 drivers/perf/arm_pmu_platform.c | 25 ++++++++++++++++++++++---
 include/linux/perf/arm_pmu.h    | 13 +++++++++++--
 4 files changed, 43 insertions(+), 52 deletions(-)
drivers/perf/arm_pmu.c +3 -23
index 287b3edfb4cc..534b4b3fb440 100644
@@ -323,13 +323,6 @@ validate_group(struct perf_event *event)
 	return 0;
 }
 
-static struct arm_pmu_platdata *armpmu_get_platdata(struct arm_pmu *armpmu)
-{
-	struct platform_device *pdev = armpmu->plat_device;
-
-	return pdev ? dev_get_platdata(&pdev->dev) : NULL;
-}
-
 static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
 {
 	struct arm_pmu *armpmu;
@@ -542,7 +535,7 @@ int armpmu_count_irq_users(const int irq)
 	return count;
 }
 
-void __armpmu_free_irq(int irq, int cpu)
+void armpmu_free_irq(int irq, int cpu)
 {
 	if (per_cpu(cpu_irq, cpu) == 0)
 		return;
@@ -559,16 +552,7 @@ void __armpmu_free_irq(int irq, int cpu)
 	per_cpu(cpu_irq, cpu) = 0;
 }
 
-void armpmu_free_irq(struct arm_pmu *armpmu, int cpu)
-{
-	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
-	int irq = per_cpu(hw_events->irq, cpu);
-
-	__armpmu_free_irq(irq, cpu);
-	per_cpu(cpu_armpmu, cpu) = NULL;
-}
-
-int armpmu_request_irq_flags(int irq, unsigned long irq_flags, int cpu)
+int armpmu_request_irq(int irq, unsigned long irq_flags, int cpu)
 {
 	int err = 0;
 	const irq_handler_t handler = armpmu_dispatch_irq;
@@ -603,24 +587,9 @@ int armpmu_request_irq_flags(int irq, unsigned long irq_flags, int cpu)
 	return err;
 }
 
-int armpmu_request_irq(struct arm_pmu *armpmu, int cpu)
+void armpmu_bind_cpu(struct arm_pmu *armpmu, int cpu)
 {
-	struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu);
-	unsigned long irq_flags;
-	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
-	int irq = per_cpu(hw_events->irq, cpu);

drivers/perf/arm_pmu.c int armpmu_request_irq_flags(int irq, unsigned long irq_flags, int cpu) · line 592

Quoted 10 lines
drivers/perf/arm_pmu.c +0 -9
-	if (!irq)
-		return 0;
-
-	if (platdata && platdata->irq_flags) {
-		irq_flags = platdata->irq_flags;
-	} else {
-		irq_flags = ARM_PMU_IRQ_FLAGS;
-	}
-
 	per_cpu(cpu_armpmu, cpu) = armpmu;

Can we not make the binding implicit in armpmu_{request,free}_irq?

Will

author maintainer
#15 lore raw

PATCH 5/5 arm_pmu: acpi: request IRQs up-front

Quoted 19 lines · Will Deacon
Quoted 18 lines · Mark Rutland

We can't request IRQs in atomic context, so for ACPI systems we'll have to request them up-front, and later associate them with CPUs.

This patch reorganises the arm_pmu code to do so. As we no longer have the arm_pmu strucutre at probe time, a number of prototypes need to be adjusted, requiring changes to the common arm_pmu code and arm_pmu platform code.

drivers/perf/arm_pmu.c +1 -8
+void armpmu_bind_cpu(struct arm_pmu *armpmu, int cpu)
 {
-	struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu);
-	unsigned long irq_flags;
-	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
-	int irq = per_cpu(hw_events->irq, cpu);
-	if (!irq)
-		return 0;
-
-	if (platdata && platdata->irq_flags) {
Quoted 8 lines

drivers/perf/arm_pmu.c int armpmu_request_irq_flags(int irq, unsigned long irq_flags, int cpu) · line 592

Quoted 6 lines
drivers/perf/arm_pmu.c +0 -5
-		irq_flags = platdata->irq_flags;
-	} else {
-		irq_flags = ARM_PMU_IRQ_FLAGS;
-	}
-
 	per_cpu(cpu_armpmu, cpu) = armpmu;

Can we not make the binding implicit in armpmu_{request,free}_irq?

Unfortunately not.

As mentioned in the commit message (typo and all), in the ACPI case, we need to request/free IRQs before we know the PMU.

Mark.

#16 lore raw

PATCH 5/5 arm_pmu: acpi: request IRQs up-front

Quoted 29 lines · Mark Rutland
Quoted 19 lines · Will Deacon
Quoted 18 lines · Mark Rutland

We can't request IRQs in atomic context, so for ACPI systems we'll have to request them up-front, and later associate them with CPUs.

This patch reorganises the arm_pmu code to do so. As we no longer have the arm_pmu strucutre at probe time, a number of prototypes need to be adjusted, requiring changes to the common arm_pmu code and arm_pmu platform code.

drivers/perf/arm_pmu.c +1 -8
+void armpmu_bind_cpu(struct arm_pmu *armpmu, int cpu)
 {
-	struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu);
-	unsigned long irq_flags;
-	struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
-	int irq = per_cpu(hw_events->irq, cpu);
-	if (!irq)
-		return 0;
-
-	if (platdata && platdata->irq_flags) {
Quoted 8 lines

drivers/perf/arm_pmu.c int armpmu_request_irq_flags(int irq, unsigned long irq_flags, int cpu) · line 592

Quoted 6 lines
drivers/perf/arm_pmu.c +0 -5
-		irq_flags = platdata->irq_flags;
-	} else {
-		irq_flags = ARM_PMU_IRQ_FLAGS;
-	}
-
 	per_cpu(cpu_armpmu, cpu) = armpmu;

Can we not make the binding implicit in armpmu_{request,free}_irq?

Quoted 4 lines

Unfortunately not.

As mentioned in the commit message (typo and all), in the ACPI case, we need to request/free IRQs before we know the PMU.

Urgh. This is hideous! Just try reading the imnplementation of armpmu_bind_cpu out loud.

Could we use {enable,disable}_irq in the hotplug notifier and request the interrupts with NOAUTOEN instead? That would mean we have a similar flow for SPI and PPIs and could potentially hide some of the book-keeping behind armpmu_{enable,disable}_irq functions.

Will

#17 lore raw

cover arm_pmu: fix lockdep issues with ACPI systems

In reply to Mark Rutland
Quoted 10 lines · Mark Rutland

Currently the arm_pmu ACPI code is somewhat dubious. It attempts ot allocate memory any manpiulate IRQs in a hotplug callback, which is an atomic context.

These patches (based on the arm64 for-next/core branch [1]) attempt to address this by moving work out of hotplug callback, requiring a reorganisation of the common arm_pmu code.

I've given these a boot-test on a Juno R1 system, both with DT and ACPI. In either case the PMU works as expected, and lockdep seems happy.

Boot tested on Qualcomm Centriq 2400 and verified I no longer see the BUG print.

Thanks,
Tyler

Signature · 3 lines
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.
#18 lore raw

cover arm_pmu: fix lockdep issues with ACPI systems

In reply to Mark Rutland
Quoted 4 lines · Mark Rutland

Currently the arm_pmu ACPI code is somewhat dubious. It attempts ot allocate memory any manpiulate IRQs in a hotplug callback, which is an atomic context.

Quoted 8 lines

These patches (based on the arm64 for-next/core branch [1]) attempt to address this by moving work out of hotplug callback, requiring a reorganisation of the common arm_pmu code.

I've given these a boot-test on a Juno R1 system, both with DT and ACPI. In either case the PMU works as expected, and lockdep seems happy.

I've pushed the series out to my arm64/acpi-pmu-lockdep branch [2].

This mostly looks ok to me; I've left some minor comments on individual patches. My main concern is taking this as a fix, because there's an awful lot here.

Is there anything simpler we can do for 4.15/stable?

Will