diff mbox series

[v4,9/9] arm_pmu: Use NMIs for PMU

Message ID 1563351432-55652-10-git-send-email-julien.thierry@arm.com (mailing list archive)
State New, archived
Headers show
Series arm_pmu: Use NMI for perf interrupt | expand

Commit Message

Julien Thierry July 17, 2019, 8:17 a.m. UTC
Add required PMU interrupt operations for NMIs. Request interrupt lines as
NMIs when possible, otherwise fall back to normal interrupts.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Tested-by: Shijith Thotton <sthotton@marvell.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
---
 drivers/perf/arm_pmu.c | 62 +++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 56 insertions(+), 6 deletions(-)

--
1.9.1

Comments

Russell King (Oracle) July 30, 2019, 9:11 a.m. UTC | #1
On Wed, Jul 17, 2019 at 09:17:12AM +0100, Julien Thierry wrote:
> Add required PMU interrupt operations for NMIs. Request interrupt lines as
> NMIs when possible, otherwise fall back to normal interrupts.
> 
> Signed-off-by: Julien Thierry <julien.thierry@arm.com>
> Tested-by: Shijith Thotton <sthotton@marvell.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Mark Rutland <mark.rutland@arm.com>

This has no effect on 32-bit ARM?
Julien Thierry July 30, 2019, 9:18 a.m. UTC | #2
Hi Russell,

On 30/07/2019 10:11, Russell King - ARM Linux admin wrote:
> On Wed, Jul 17, 2019 at 09:17:12AM +0100, Julien Thierry wrote:
>> Add required PMU interrupt operations for NMIs. Request interrupt lines as
>> NMIs when possible, otherwise fall back to normal interrupts.
>>
>> Signed-off-by: Julien Thierry <julien.thierry@arm.com>
>> Tested-by: Shijith Thotton <sthotton@marvell.com>
>> Cc: Will Deacon <will.deacon@arm.com>
>> Cc: Mark Rutland <mark.rutland@arm.com>
> 
> This has no effect on 32-bit ARM?
> 

It shouldn't. request_nmi()/request_percpu_nmi() should fail on a
platform that doesn't have the NMI (through IRQ framework) support .
Currently, only arm64 with GICv3 provides that support.

So the pmu driver should fallback to request_irq()/request_percpu_irq()
for a 32-bit ARM kernel platforms and work as before. I can clarify that
in the commit message if there is a respin (or if maintainers agree to
amend).

Thanks,
Russell King (Oracle) July 30, 2019, 9:28 a.m. UTC | #3
On Tue, Jul 30, 2019 at 10:18:16AM +0100, Julien Thierry wrote:
> Hi Russell,
> 
> On 30/07/2019 10:11, Russell King - ARM Linux admin wrote:
> > On Wed, Jul 17, 2019 at 09:17:12AM +0100, Julien Thierry wrote:
> >> Add required PMU interrupt operations for NMIs. Request interrupt lines as
> >> NMIs when possible, otherwise fall back to normal interrupts.
> >>
> >> Signed-off-by: Julien Thierry <julien.thierry@arm.com>
> >> Tested-by: Shijith Thotton <sthotton@marvell.com>
> >> Cc: Will Deacon <will.deacon@arm.com>
> >> Cc: Mark Rutland <mark.rutland@arm.com>
> > 
> > This has no effect on 32-bit ARM?
> > 
> 
> It shouldn't. request_nmi()/request_percpu_nmi() should fail on a
> platform that doesn't have the NMI (through IRQ framework) support .
> Currently, only arm64 with GICv3 provides that support.
> 
> So the pmu driver should fallback to request_irq()/request_percpu_irq()
> for a 32-bit ARM kernel platforms and work as before. I can clarify that
> in the commit message if there is a respin (or if maintainers agree to
> amend).

Has it been tested with a 32-bit guest kernel running on ARM64?
Julien Thierry July 30, 2019, 2:06 p.m. UTC | #4
On 30/07/2019 10:28, Russell King - ARM Linux admin wrote:
> On Tue, Jul 30, 2019 at 10:18:16AM +0100, Julien Thierry wrote:
>> Hi Russell,
>>
>> On 30/07/2019 10:11, Russell King - ARM Linux admin wrote:
>>> On Wed, Jul 17, 2019 at 09:17:12AM +0100, Julien Thierry wrote:
>>>> Add required PMU interrupt operations for NMIs. Request interrupt lines as
>>>> NMIs when possible, otherwise fall back to normal interrupts.
>>>>
>>>> Signed-off-by: Julien Thierry <julien.thierry@arm.com>
>>>> Tested-by: Shijith Thotton <sthotton@marvell.com>
>>>> Cc: Will Deacon <will.deacon@arm.com>
>>>> Cc: Mark Rutland <mark.rutland@arm.com>
>>>
>>> This has no effect on 32-bit ARM?
>>>
>>
>> It shouldn't. request_nmi()/request_percpu_nmi() should fail on a
>> platform that doesn't have the NMI (through IRQ framework) support .
>> Currently, only arm64 with GICv3 provides that support.
>>
>> So the pmu driver should fallback to request_irq()/request_percpu_irq()
>> for a 32-bit ARM kernel platforms and work as before. I can clarify that
>> in the commit message if there is a respin (or if maintainers agree to
>> amend).
> 
> Has it been tested with a 32-bit guest kernel running on ARM64?

In theory, this shouldn't change anything. Even if the host has a PMU as
NMI, interrupts are presented to the guest the same way as before, not
in an host NMI context. And as long as the guest handles it's interrupts
the same way (i.e. doesn't use priorities and sticks to PSTATE/CPSR I
bit to block interrupts) things should behave like before.

I still gave this a try, just in case there would be other surprises.
Running (and profiling) a 32bit kvm guest from a host that is using
Pseudo-NMIs for the PMU interrupt works and using the virtual PMU from
within the guest also works.

Thanks,
diff mbox series

Patch

diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 9ac072a..a9c00cd 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -45,6 +45,17 @@  static void armpmu_free_pmuirq(unsigned int irq, int cpu, void __percpu *devid)
 	.free_pmuirq = armpmu_free_pmuirq
 };

+static void armpmu_free_pmunmi(unsigned int irq, int cpu, void __percpu *devid)
+{
+	free_nmi(irq, per_cpu_ptr(devid, cpu));
+}
+
+static const struct pmu_irq_ops pmunmi_ops = {
+	.enable_pmuirq = enable_nmi,
+	.disable_pmuirq = disable_nmi_nosync,
+	.free_pmuirq = armpmu_free_pmunmi
+};
+
 static void armpmu_enable_percpu_pmuirq(unsigned int irq)
 {
 	enable_percpu_irq(irq, IRQ_TYPE_NONE);
@@ -63,6 +74,31 @@  static void armpmu_free_percpu_pmuirq(unsigned int irq, int cpu,
 	.free_pmuirq = armpmu_free_percpu_pmuirq
 };

+static void armpmu_enable_percpu_pmunmi(unsigned int irq)
+{
+	if (!prepare_percpu_nmi(irq))
+		enable_percpu_nmi(irq, IRQ_TYPE_NONE);
+}
+
+static void armpmu_disable_percpu_pmunmi(unsigned int irq)
+{
+	disable_percpu_nmi(irq);
+	teardown_percpu_nmi(irq);
+}
+
+static void armpmu_free_percpu_pmunmi(unsigned int irq, int cpu,
+				      void __percpu *devid)
+{
+	if (armpmu_count_irq_users(irq) == 1)
+		free_percpu_nmi(irq, devid);
+}
+
+static const struct pmu_irq_ops percpu_pmunmi_ops = {
+	.enable_pmuirq = armpmu_enable_percpu_pmunmi,
+	.disable_pmuirq = armpmu_disable_percpu_pmunmi,
+	.free_pmuirq = armpmu_free_percpu_pmunmi
+};
+
 static DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu);
 static DEFINE_PER_CPU(int, cpu_irq);
 static DEFINE_PER_CPU(const struct pmu_irq_ops *, cpu_irq_ops);
@@ -633,15 +669,29 @@  int armpmu_request_irq(int irq, int cpu)
 			    IRQF_NO_THREAD;

 		irq_set_status_flags(irq, IRQ_NOAUTOEN);
-		err = request_irq(irq, handler, irq_flags, "arm-pmu",
+
+		err = request_nmi(irq, handler, irq_flags, "arm-pmu",
 				  per_cpu_ptr(&cpu_armpmu, cpu));

-		irq_ops = &pmuirq_ops;
+		/* If cannot get an NMI, get a normal interrupt */
+		if (err) {
+			err = request_irq(irq, handler, irq_flags, "arm-pmu",
+					  per_cpu_ptr(&cpu_armpmu, cpu));
+			irq_ops = &pmuirq_ops;
+		} else {
+			irq_ops = &pmunmi_ops;
+		}
 	} else if (armpmu_count_irq_users(irq) == 0) {
-		err = request_percpu_irq(irq, handler, "arm-pmu",
-					 &cpu_armpmu);
-
-		irq_ops = &percpu_pmuirq_ops;
+		err = request_percpu_nmi(irq, handler, "arm-pmu", &cpu_armpmu);
+
+		/* If cannot get an NMI, get a normal interrupt */
+		if (err) {
+			err = request_percpu_irq(irq, handler, "arm-pmu",
+						 &cpu_armpmu);
+			irq_ops = &percpu_pmuirq_ops;
+		} else {
+			irq_ops = &percpu_pmunmi_ops;
+		}
 	} else {
 		/* Per cpudevid irq was already requested by another CPU */
 		irq_ops = armpmu_find_irq_ops(irq);