diff mbox series

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

Message ID 1563351432-55652-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 17, 2019, 8:17 a.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 | 30 ++----------------------------
 1 file changed, 2 insertions(+), 28 deletions(-)

--
1.9.1

Comments

Will Deacon Aug. 1, 2019, 12:58 p.m. UTC | #1
On Wed, Jul 17, 2019 at 09:17:05AM +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.
> 
> 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 | 30 ++----------------------------
>  1 file changed, 2 insertions(+), 28 deletions(-)
> 
> diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
> index 838758f..0e2cf5d 100644
> --- a/arch/arm64/kernel/perf_event.c
> +++ b/arch/arm64/kernel/perf_event.c
> @@ -673,15 +673,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
> @@ -702,21 +697,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);
>  }

With the implicit ISBs now removed by virtue of addressing the counter
register directly, what prevents the programming of the evtype being
reordered with respect to disabling/enabling the counter?

>  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
>  	 */
> @@ -726,30 +710,20 @@ 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);
> +	WARN_ON_ONCE(preemptible());
>  	/* Enable all counters */
>  	armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
> -	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
>  }
> 
>  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);
> +	WARN_ON_ONCE(preemptible());

I don't think we need these WARN_ONs -- these are very much per-CPU
operations from the context of the perf core, so we'll be ok.

Will
Julien Thierry Aug. 2, 2019, 2:26 p.m. UTC | #2
Hi Will,

On 01/08/2019 13:58, Will Deacon wrote:
> On Wed, Jul 17, 2019 at 09:17:05AM +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.
>>
>> 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 | 30 ++----------------------------
>>  1 file changed, 2 insertions(+), 28 deletions(-)
>>
>> diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
>> index 838758f..0e2cf5d 100644
>> --- a/arch/arm64/kernel/perf_event.c
>> +++ b/arch/arm64/kernel/perf_event.c
>> @@ -673,15 +673,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
>> @@ -702,21 +697,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);
>>  }
> 
> With the implicit ISBs now removed by virtue of addressing the counter
> register directly, what prevents the programming of the evtype being
> reordered with respect to disabling/enabling the counter?

I agree, it seems an ISB is missing here. It should probably be fixed in
the previous patch.

However, I notice that even before that patch, there is no ISB between
the enabling of the IRQ for the counter and the enabling of the counter
itself.
Meaning we might start counting events before the IRQ is enabled.

Should we have something like the following?

        armv8pmu_disable_event_counter(event);
        isb();
        armv8pmu_write_event_type(event);
        armv8pmu_enable_event_irq(event);
        isb();
        armv8pmu_enable_event_counter(event);

> 
>>  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
>>  	 */
>> @@ -726,30 +710,20 @@ 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);
>> +	WARN_ON_ONCE(preemptible());
>>  	/* Enable all counters */
>>  	armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
>> -	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
>>  }
>>
>>  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);
>> +	WARN_ON_ONCE(preemptible());
> 
> I don't think we need these WARN_ONs -- these are very much per-CPU
> operations from the context of the perf core, so we'll be ok.
> 

If they are not necessary we can get rid of them.

Thanks,
diff mbox series

Patch

diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 838758f..0e2cf5d 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -673,15 +673,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
@@ -702,21 +697,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
 	 */
@@ -726,30 +710,20 @@  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);
+	WARN_ON_ONCE(preemptible());
 	/* Enable all counters */
 	armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }

 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);
+	WARN_ON_ONCE(preemptible());
 	/* Disable all counters */
 	armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E);
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }

 static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)