diff mbox series

[v3,2/9] arm64: perf: Remove PMU locking

Message ID 1562596377-33196-3-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 8, 2019, 2:32 p.m. UTC
Since the PMU driver uses direct registers for counter
setup/manipulation, locking around these operations is no longer needed.

For operations that can be called with interrupts enabled, preemption
still needs to be disabled to ensure the programming of the PMU is
done on the expected CPU and not migrated mid-programming.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
---
 arch/arm64/kernel/perf_event.c | 32 ++++----------------------------
 1 file changed, 4 insertions(+), 28 deletions(-)

--
1.9.1

Comments

Mark Rutland July 8, 2019, 3:03 p.m. UTC | #1
On Mon, Jul 08, 2019 at 03:32:50PM +0100, Julien Thierry wrote:
> Since the PMU driver uses direct registers for counter
> setup/manipulation, locking around these operations is no longer needed.
> 
> For operations that can be called with interrupts enabled, preemption
> still needs to be disabled to ensure the programming of the PMU is
> done on the expected CPU and not migrated mid-programming.

[...]

>  static void armv8pmu_start(struct arm_pmu *cpu_pmu)
>  {
> -	unsigned long flags;
> -	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
> -
> -	raw_spin_lock_irqsave(&events->pmu_lock, flags);
> +	preempt_disable();
>  	/* Enable all counters */
>  	armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
> -	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
> +	preempt_enable();
>  }
> 
>  static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
>  {
> -	unsigned long flags;
> -	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
> -
> -	raw_spin_lock_irqsave(&events->pmu_lock, flags);
> +	preempt_disable();
>  	/* Disable all counters */
>  	armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E);
> -	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
> +	preempt_enable();
>  }

I think that we'd have bigger problems if these could be called in
preemptible context, since we couldn't guarantee which HW PMU instance
they'd operate on.

I also thought that the interrupt disable/enable was a hold-over from
the old days of perf core, and these days all of the synchronous
operations are held with the pmu ctx lock held (and interrupts
disabled).

Do you have an example of when these are called with interrupts enabled?
Or in a preemptible context?

Thanks,
Mark.

> 
>  static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
> --
> 1.9.1
Julien Thierry July 8, 2019, 3:34 p.m. UTC | #2
On 08/07/2019 16:03, Mark Rutland wrote:
> On Mon, Jul 08, 2019 at 03:32:50PM +0100, Julien Thierry wrote:
>> Since the PMU driver uses direct registers for counter
>> setup/manipulation, locking around these operations is no longer needed.
>>
>> For operations that can be called with interrupts enabled, preemption
>> still needs to be disabled to ensure the programming of the PMU is
>> done on the expected CPU and not migrated mid-programming.
> 
> [...]
> 
>>  static void armv8pmu_start(struct arm_pmu *cpu_pmu)
>>  {
>> -	unsigned long flags;
>> -	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
>> -
>> -	raw_spin_lock_irqsave(&events->pmu_lock, flags);
>> +	preempt_disable();
>>  	/* Enable all counters */
>>  	armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
>> -	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
>> +	preempt_enable();
>>  }
>>
>>  static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
>>  {
>> -	unsigned long flags;
>> -	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
>> -
>> -	raw_spin_lock_irqsave(&events->pmu_lock, flags);
>> +	preempt_disable();
>>  	/* Disable all counters */
>>  	armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E);
>> -	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
>> +	preempt_enable();
>>  }
> 
> I think that we'd have bigger problems if these could be called in
> preemptible context, since we couldn't guarantee which HW PMU instance
> they'd operate on.
> 
> I also thought that the interrupt disable/enable was a hold-over from
> the old days of perf core, and these days all of the synchronous
> operations are held with the pmu ctx lock held (and interrupts
> disabled).
> 
> Do you have an example of when these are called with interrupts enabled?
> Or in a preemptible context?
> 

I must admit that not seeing mention of the pmu_enable/disable callbacks
being called with preemption disabled in perf_event.h, I assumed the
worst. But looking at it, it does look like they are always called
either with interrupts or preemption disabled, and the x86
implementation doesn't seem to worry about being preempted in those
callbacks.

I guess I can get rid of the preempt_enable/disable.

Thanks,
Mark Rutland July 9, 2019, 11:22 a.m. UTC | #3
On Mon, Jul 08, 2019 at 04:34:01PM +0100, Julien Thierry wrote:
> 
> 
> On 08/07/2019 16:03, Mark Rutland wrote:
> > On Mon, Jul 08, 2019 at 03:32:50PM +0100, Julien Thierry wrote:
> >> Since the PMU driver uses direct registers for counter
> >> setup/manipulation, locking around these operations is no longer needed.
> >>
> >> For operations that can be called with interrupts enabled, preemption
> >> still needs to be disabled to ensure the programming of the PMU is
> >> done on the expected CPU and not migrated mid-programming.
> > 
> > [...]
> > 
> >>  static void armv8pmu_start(struct arm_pmu *cpu_pmu)
> >>  {
> >> -	unsigned long flags;
> >> -	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
> >> -
> >> -	raw_spin_lock_irqsave(&events->pmu_lock, flags);
> >> +	preempt_disable();
> >>  	/* Enable all counters */
> >>  	armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
> >> -	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
> >> +	preempt_enable();
> >>  }
> >>
> >>  static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
> >>  {
> >> -	unsigned long flags;
> >> -	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
> >> -
> >> -	raw_spin_lock_irqsave(&events->pmu_lock, flags);
> >> +	preempt_disable();
> >>  	/* Disable all counters */
> >>  	armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E);
> >> -	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
> >> +	preempt_enable();
> >>  }
> > 
> > I think that we'd have bigger problems if these could be called in
> > preemptible context, since we couldn't guarantee which HW PMU instance
> > they'd operate on.
> > 
> > I also thought that the interrupt disable/enable was a hold-over from
> > the old days of perf core, and these days all of the synchronous
> > operations are held with the pmu ctx lock held (and interrupts
> > disabled).
> > 
> > Do you have an example of when these are called with interrupts enabled?
> > Or in a preemptible context?
> 
> I must admit that not seeing mention of the pmu_enable/disable callbacks
> being called with preemption disabled in perf_event.h, I assumed the
> worst. But looking at it, it does look like they are always called
> either with interrupts or preemption disabled, and the x86
> implementation doesn't seem to worry about being preempted in those
> callbacks.
> 
> I guess I can get rid of the preempt_enable/disable.

Yes please!

As an aside, this is something I'd like to explicitly annotate on
functions so that we could statically and dnyamically test it. For now
I'm happy to rely on the semantics documented in perf_event.h.

Thanks,
Mark.
diff mbox series

Patch

diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 7759f8a..878c142 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -677,15 +677,10 @@  static inline u32 armv8pmu_getreset_flags(void)

 static void armv8pmu_enable_event(struct perf_event *event)
 {
-	unsigned long flags;
-	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
-	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
-
 	/*
 	 * Enable counter and interrupt, and set the counter to count
 	 * the event that we're interested in.
 	 */
-	raw_spin_lock_irqsave(&events->pmu_lock, flags);

 	/*
 	 * Disable counter
@@ -706,21 +701,10 @@  static void armv8pmu_enable_event(struct perf_event *event)
 	 * Enable counter
 	 */
 	armv8pmu_enable_event_counter(event);
-
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }

 static void armv8pmu_disable_event(struct perf_event *event)
 {
-	unsigned long flags;
-	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
-	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
-
-	/*
-	 * Disable counter and interrupt
-	 */
-	raw_spin_lock_irqsave(&events->pmu_lock, flags);
-
 	/*
 	 * Disable counter
 	 */
@@ -730,30 +714,22 @@  static void armv8pmu_disable_event(struct perf_event *event)
 	 * Disable interrupt for this counter
 	 */
 	armv8pmu_disable_event_irq(event);
-
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }

 static void armv8pmu_start(struct arm_pmu *cpu_pmu)
 {
-	unsigned long flags;
-	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
-
-	raw_spin_lock_irqsave(&events->pmu_lock, flags);
+	preempt_disable();
 	/* Enable all counters */
 	armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+	preempt_enable();
 }

 static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
 {
-	unsigned long flags;
-	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
-
-	raw_spin_lock_irqsave(&events->pmu_lock, flags);
+	preempt_disable();
 	/* Disable all counters */
 	armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E);
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+	preempt_enable();
 }

 static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)