linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt
@ 2019-03-22 16:23 Julien Thierry
  2019-03-22 16:23 ` [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection Julien Thierry
                   ` (9 more replies)
  0 siblings, 10 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-22 16:23 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, Julien Thierry, peterz, will.deacon, acme,
	alexander.shishkin, mingo, namhyung, jolsa

Hi,

On arm64, perf reports that counter overflow very often (too often)
happen in function that potentially enabled interrutps:

$ perf record -a -- sleep 60; perf report -F overhead,symbol
[...]
# Overhead  Symbol
# ........  ..........................................
#
     6.58%  [k] _raw_spin_unlock_irq
     6.10%  [k] _raw_spin_unlock_irqrestore
     5.52%  [k] ___bpf_prog_run
     4.37%  [k] el0_svc_common
     2.58%  [k] arch_cpu_idle
     2.39%  [k] kmem_cache_alloc
     2.06%  [k] __seccomp_filter
     [...]

The root issue is, if an overflow happens while executing with
interrupts disabled, the perf event will only be handled when interrupts
are reenabled (i.e. when the PMU interrupt is taken). The result being
the event being reported at the interrupt enabling location rather than
where the overflow actually happened.

Now that we have support for pseudo-NMI on arm64 with GICv3, we can use
it to improve the profiling done using the PMU interrupt.

With these changes, on the same machine, we get:
# Overhead  Symbol
# ........  ..................................
#
     7.06%  [k] ___bpf_prog_run
     4.08%  [k] __update_load_avg_se
     4.02%  [k] ktime_get_ts64
     3.77%  [k] __ll_sc_arch_atomic_add_return
     3.71%  [k] file_ra_state_init
     3.62%  [k] __ll_sc_arch_atomic64_sub
     3.53%  [k] __ll_sc___cmpxchg_case_acq_32
     [...]

_raw_spin_unlock_irq/irqrestore don't event appear anymore in the
perf trace.

* Patches 1 to 4 remove the need to use spinlocks for the Arm PMU
  driver for Armv7 and Armv8 (aarch64).
* Patches 5 moves the locking to Armv6 specific code which is the sole
  user
* Patches 6 and 7 make the PMU interrupt handler NMI-safe
* Patches 8 and 9 enable using pseudo-NMI for the PMU interrupt when
  the feature is available

Changes since v1[1]:
- Rebased on v5.1-rc1
- Pseudo-NMI has changed a lot since then, use the (now merged) NMI API
- Remove locking from armv7 perf_event
- Use locking only in armv6 perf_event
- Use direct counter/type registers insted of selector register for armv8

[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2018-January/554611.html

Cheers,

Julien

-->

Julien Thierry (8):
  arm64: perf: Remove PMU locking
  arm: perf: save/resore pmsel
  arm: perf: Remove Remove PMU locking
  perf/arm_pmu: Move PMU lock to ARMv6 events
  arm64: perf: Do not call irq_work_run in NMI context
  arm/arm64: kvm: pmu: Make overflow handler NMI safe
  arm_pmu: Introduce pmu_irq_ops
  arm_pmu: Use NMIs for PMU

Mark Rutland (1):
  arm64: perf: avoid PMXEV* indirection

 arch/arm/kernel/perf_event_v6.c |  26 +++++---
 arch/arm/kernel/perf_event_v7.c |  77 +++++++---------------
 arch/arm64/kernel/perf_event.c  | 122 ++++++++++++++++++++++------------
 drivers/perf/arm_pmu.c          | 143 ++++++++++++++++++++++++++++++++++------
 include/kvm/arm_pmu.h           |   1 +
 include/linux/perf/arm_pmu.h    |   5 --
 virt/kvm/arm/pmu.c              |  37 +++++++++--
 7 files changed, 277 insertions(+), 134 deletions(-)

--
1.9.1

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection
  2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
@ 2019-03-22 16:23 ` Julien Thierry
  2019-03-25 12:36   ` liwei (GF)
                     ` (2 more replies)
  2019-03-22 16:23 ` [PATCH v2 2/9] arm64: perf: Remove PMU locking Julien Thierry
                   ` (8 subsequent siblings)
  9 siblings, 3 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-22 16:23 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, Julien Thierry, peterz, Catalin Marinas,
	will.deacon, acme, alexander.shishkin, mingo, namhyung, jolsa

From: Mark Rutland <mark.rutland@arm.com>

Currently we access the counter registers and their respective type
registers indirectly. This requires us to write to PMSELR, issue an ISB,
then access the relevant PMXEV* registers.

This is unfortunate, because:

* Under virtualization, accessing one registers requires two traps to
  the hypervisor, even though we could access the register directly with
  a single trap.

* We have to issue an ISB which we could otherwise avoid the cost of.

* When we use NMIs, the NMI handler will have to save/restore the select
  register in case the code it preempted was attempting to access a
  counter or its type register.

We can avoid these issues by directly accessing the relevant registers.
This patch adds helpers to do so.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
[Julien T.: Don't inline read/write functions to avoid big code-size
	increase, remove unused read_pmevtypern function.]
Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Cc: Will Deacon <will.deacon@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 | 87 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 74 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 4addb38..63d45e2 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -379,6 +379,77 @@ static inline bool armv8pmu_event_is_chained(struct perf_event *event)
 #define	ARMV8_IDX_TO_COUNTER(x)	\
 	(((x) - ARMV8_IDX_COUNTER0) & ARMV8_PMU_COUNTER_MASK)

+/*
+ * This code is really good
+ */
+
+#define PMEVN_CASE(__n, case_macro) \
+	case __n: case_macro(__n); break;
+
+#define PMEVN_SWITCH(__x, case_macro)				\
+	do {							\
+		switch (__x) {					\
+		PMEVN_CASE(0,  case_macro);			\
+		PMEVN_CASE(1,  case_macro);			\
+		PMEVN_CASE(2,  case_macro);			\
+		PMEVN_CASE(3,  case_macro);			\
+		PMEVN_CASE(4,  case_macro);			\
+		PMEVN_CASE(5,  case_macro);			\
+		PMEVN_CASE(6,  case_macro);			\
+		PMEVN_CASE(7,  case_macro);			\
+		PMEVN_CASE(8,  case_macro);			\
+		PMEVN_CASE(9,  case_macro);			\
+		PMEVN_CASE(10, case_macro);			\
+		PMEVN_CASE(11, case_macro);			\
+		PMEVN_CASE(12, case_macro);			\
+		PMEVN_CASE(13, case_macro);			\
+		PMEVN_CASE(14, case_macro);			\
+		PMEVN_CASE(15, case_macro);			\
+		PMEVN_CASE(16, case_macro);			\
+		PMEVN_CASE(17, case_macro);			\
+		PMEVN_CASE(18, case_macro);			\
+		PMEVN_CASE(19, case_macro);			\
+		PMEVN_CASE(21, case_macro);			\
+		PMEVN_CASE(22, case_macro);			\
+		PMEVN_CASE(23, case_macro);			\
+		PMEVN_CASE(24, case_macro);			\
+		PMEVN_CASE(25, case_macro);			\
+		PMEVN_CASE(26, case_macro);			\
+		PMEVN_CASE(27, case_macro);			\
+		PMEVN_CASE(28, case_macro);			\
+		PMEVN_CASE(29, case_macro);			\
+		PMEVN_CASE(30, case_macro);			\
+		default: WARN(1, "Inavlid PMEV* index");	\
+		}						\
+	} while (0)
+
+#define RETURN_READ_PMEVCNTRN(n) \
+	return read_sysreg(pmevcntr##n##_el0);
+static unsigned long read_pmevcntrn(int n)
+{
+	PMEVN_SWITCH(n, RETURN_READ_PMEVCNTRN);
+	return 0;
+}
+#undef RETURN_READ_PMEVCNTRN
+
+#define WRITE_PMEVCNTRN(n) \
+	write_sysreg(val, pmevcntr##n##_el0);
+static void write_pmevcntrn(int n, unsigned long val)
+{
+	PMEVN_SWITCH(n, WRITE_PMEVCNTRN);
+}
+#undef WRITE_PMEVCNTRN
+
+#define WRITE_PMEVTYPERN(n) \
+	write_sysreg(val, pmevtyper##n##_el0);
+static void write_pmevtypern(int n, unsigned long val)
+{
+	PMEVN_SWITCH(n, WRITE_PMEVTYPERN);
+}
+#undef WRITE_PMEVTYPERN
+
+#undef GENERATE_PMEVN_SWITCH
+
 static inline u32 armv8pmu_pmcr_read(void)
 {
 	return read_sysreg(pmcr_el0);
@@ -407,17 +478,9 @@ static inline int armv8pmu_counter_has_overflowed(u32 pmnc, int idx)
 	return pmnc & BIT(ARMV8_IDX_TO_COUNTER(idx));
 }

-static inline void armv8pmu_select_counter(int idx)
-{
-	u32 counter = ARMV8_IDX_TO_COUNTER(idx);
-	write_sysreg(counter, pmselr_el0);
-	isb();
-}
-
 static inline u32 armv8pmu_read_evcntr(int idx)
 {
-	armv8pmu_select_counter(idx);
-	return read_sysreg(pmxevcntr_el0);
+	return read_pmevcntrn(idx);
 }

 static inline u64 armv8pmu_read_hw_counter(struct perf_event *event)
@@ -451,8 +514,7 @@ static inline u64 armv8pmu_read_counter(struct perf_event *event)

 static inline void armv8pmu_write_evcntr(int idx, u32 value)
 {
-	armv8pmu_select_counter(idx);
-	write_sysreg(value, pmxevcntr_el0);
+	write_pmevcntrn(idx, value);
 }

 static inline void armv8pmu_write_hw_counter(struct perf_event *event,
@@ -493,9 +555,8 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)

 static inline void armv8pmu_write_evtype(int idx, u32 val)
 {
-	armv8pmu_select_counter(idx);
 	val &= ARMV8_PMU_EVTYPE_MASK;
-	write_sysreg(val, pmxevtyper_el0);
+	write_pmevtypern(idx, val);
 }

 static inline void armv8pmu_write_event_type(struct perf_event *event)
--
1.9.1

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 2/9] arm64: perf: Remove PMU locking
  2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
  2019-03-22 16:23 ` [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection Julien Thierry
@ 2019-03-22 16:23 ` Julien Thierry
  2019-03-22 16:23 ` [PATCH v2 3/9] arm: perf: save/resore pmsel Julien Thierry
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-22 16:23 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, Julien Thierry, peterz, Catalin Marinas,
	will.deacon, acme, alexander.shishkin, mingo, namhyung, jolsa

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(-)

diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 63d45e2..bae4242 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -659,15 +659,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
@@ -688,21 +683,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
 	 */
@@ -712,30 +696,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)
--
1.9.1

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 3/9] arm: perf: save/resore pmsel
  2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
  2019-03-22 16:23 ` [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection Julien Thierry
  2019-03-22 16:23 ` [PATCH v2 2/9] arm64: perf: Remove PMU locking Julien Thierry
@ 2019-03-22 16:23 ` Julien Thierry
  2019-03-22 16:23 ` [PATCH v2 4/9] arm: perf: Remove Remove PMU locking Julien Thierry
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-22 16:23 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, Julien Thierry, peterz, will.deacon, Russell King,
	acme, alexander.shishkin, mingo, namhyung, jolsa

The callback pmu->read() can be called with interrupts enabled.
Currently, on ARM, this can cause the following callchain:

armpmu_read() -> armpmu_event_update() -> armv7pmu_read_counter()

The last function might modify the counter selector register and then
read the target counter, without taking any lock. With interrupts
enabled, a PMU interrupt could occur and modify the selector register
as well, between the selection and read of the interrupted context.

Save and restore the value of the selector register in the PMU interrupt
handler, ensuring the interrupted context is left with the correct PMU
registers selected.

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: Russell King <linux@armlinux.org.uk>
---
 arch/arm/kernel/perf_event_v7.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index a4fb0f8..c3da7a5 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -736,10 +736,22 @@ static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, int idx)
 	return pmnc & BIT(ARMV7_IDX_TO_COUNTER(idx));
 }

-static inline void armv7_pmnc_select_counter(int idx)
+static inline u32 armv7_pmsel_read(void)
+{
+	u32 pmsel;
+
+	asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=&r" (pmsel));
+	return pmsel;
+}
+
+static inline void armv7_pmsel_write(u32 counter)
 {
-	u32 counter = ARMV7_IDX_TO_COUNTER(idx);
 	asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter));
+}
+
+static inline void armv7_pmnc_select_counter(int idx)
+{
+	armv7_pmsel_write(ARMV7_IDX_TO_COUNTER(idx));
 	isb();
 }

@@ -952,8 +964,11 @@ static irqreturn_t armv7pmu_handle_irq(struct arm_pmu *cpu_pmu)
 	struct perf_sample_data data;
 	struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
 	struct pt_regs *regs;
+	u32 pmsel;
 	int idx;

+	pmsel = armv7_pmsel_read();
+
 	/*
 	 * Get and reset the IRQ flags
 	 */
@@ -1004,6 +1019,8 @@ static irqreturn_t armv7pmu_handle_irq(struct arm_pmu *cpu_pmu)
 	 */
 	irq_work_run();

+	armv7_pmsel_write(pmsel);
+
 	return IRQ_HANDLED;
 }

--
1.9.1

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 4/9] arm: perf: Remove Remove PMU locking
  2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
                   ` (2 preceding siblings ...)
  2019-03-22 16:23 ` [PATCH v2 3/9] arm: perf: save/resore pmsel Julien Thierry
@ 2019-03-22 16:23 ` Julien Thierry
  2019-03-22 16:24 ` [PATCH v2 5/9] perf/arm_pmu: Move PMU lock to ARMv6 events Julien Thierry
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-22 16:23 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, Julien Thierry, peterz, will.deacon, Russell King,
	acme, alexander.shishkin, mingo, namhyung, jolsa

Since the PMU interrupt saves and restores the value of the selector
register, there is no need to serialize register accesses against the
interrupt contexts.

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: Russell King <linux@armlinux.org.uk>
---
 arch/arm/kernel/perf_event_v7.c | 56 +++--------------------------------------
 1 file changed, 4 insertions(+), 52 deletions(-)

diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index c3da7a5..cb3b7a4 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -882,10 +882,8 @@ static void armv7_pmnc_dump_regs(struct arm_pmu *cpu_pmu)

 static void armv7pmu_enable_event(struct perf_event *event)
 {
-	unsigned long flags;
 	struct hw_perf_event *hwc = &event->hw;
 	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
-	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
 	int idx = hwc->idx;

 	if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
@@ -898,7 +896,6 @@ static void armv7pmu_enable_event(struct perf_event *event)
 	 * 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
@@ -922,16 +919,12 @@ static void armv7pmu_enable_event(struct perf_event *event)
 	 * Enable counter
 	 */
 	armv7_pmnc_enable_counter(idx);
-
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }

 static void armv7pmu_disable_event(struct perf_event *event)
 {
-	unsigned long flags;
 	struct hw_perf_event *hwc = &event->hw;
 	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
-	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
 	int idx = hwc->idx;

 	if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
@@ -941,11 +934,6 @@ static void armv7pmu_disable_event(struct perf_event *event)
 	}

 	/*
-	 * Disable counter and interrupt
-	 */
-	raw_spin_lock_irqsave(&events->pmu_lock, flags);
-
-	/*
 	 * Disable counter
 	 */
 	armv7_pmnc_disable_counter(idx);
@@ -954,8 +942,6 @@ static void armv7pmu_disable_event(struct perf_event *event)
 	 * Disable interrupt for this counter
 	 */
 	armv7_pmnc_disable_intens(idx);
-
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }

 static irqreturn_t armv7pmu_handle_irq(struct arm_pmu *cpu_pmu)
@@ -1026,24 +1012,18 @@ static irqreturn_t armv7pmu_handle_irq(struct arm_pmu *cpu_pmu)

 static void armv7pmu_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 */
 	armv7_pmnc_write(armv7_pmnc_read() | ARMV7_PMNC_E);
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+	preempt_enable();
 }

 static void armv7pmu_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 */
 	armv7_pmnc_write(armv7_pmnc_read() & ~ARMV7_PMNC_E);
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+	preempt_enable();
 }

 static int armv7pmu_get_event_idx(struct pmu_hw_events *cpuc,
@@ -1509,14 +1489,8 @@ static void krait_clearpmu(u32 config_base)

 static void krait_pmu_disable_event(struct perf_event *event)
 {
-	unsigned long flags;
 	struct hw_perf_event *hwc = &event->hw;
 	int idx = hwc->idx;
-	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 */
 	armv7_pmnc_disable_counter(idx);
@@ -1529,23 +1503,17 @@ static void krait_pmu_disable_event(struct perf_event *event)

 	/* Disable interrupt for this counter */
 	armv7_pmnc_disable_intens(idx);
-
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }

 static void krait_pmu_enable_event(struct perf_event *event)
 {
-	unsigned long flags;
 	struct hw_perf_event *hwc = &event->hw;
 	int idx = hwc->idx;
-	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 */
 	armv7_pmnc_disable_counter(idx);
@@ -1565,8 +1533,6 @@ static void krait_pmu_enable_event(struct perf_event *event)

 	/* Enable counter */
 	armv7_pmnc_enable_counter(idx);
-
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }

 static void krait_pmu_reset(void *info)
@@ -1842,14 +1808,8 @@ static void scorpion_clearpmu(u32 config_base)

 static void scorpion_pmu_disable_event(struct perf_event *event)
 {
-	unsigned long flags;
 	struct hw_perf_event *hwc = &event->hw;
 	int idx = hwc->idx;
-	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 */
 	armv7_pmnc_disable_counter(idx);
@@ -1862,23 +1822,17 @@ static void scorpion_pmu_disable_event(struct perf_event *event)

 	/* Disable interrupt for this counter */
 	armv7_pmnc_disable_intens(idx);
-
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }

 static void scorpion_pmu_enable_event(struct perf_event *event)
 {
-	unsigned long flags;
 	struct hw_perf_event *hwc = &event->hw;
 	int idx = hwc->idx;
-	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 */
 	armv7_pmnc_disable_counter(idx);
@@ -1898,8 +1852,6 @@ static void scorpion_pmu_enable_event(struct perf_event *event)

 	/* Enable counter */
 	armv7_pmnc_enable_counter(idx);
-
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }

 static void scorpion_pmu_reset(void *info)
--
1.9.1

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 5/9] perf/arm_pmu: Move PMU lock to ARMv6 events
  2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
                   ` (3 preceding siblings ...)
  2019-03-22 16:23 ` [PATCH v2 4/9] arm: perf: Remove Remove PMU locking Julien Thierry
@ 2019-03-22 16:24 ` Julien Thierry
  2019-03-22 16:24 ` [PATCH v2 6/9] arm64: perf: Do not call irq_work_run in NMI context Julien Thierry
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-22 16:24 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, Julien Thierry, peterz, will.deacon, Russell King,
	acme, alexander.shishkin, mingo, namhyung, jolsa

Perf event backend for ARMv8 and ARMv7 no longer uses the pmu_lock.
The only remaining user is the ARMv6 event backend.

Move the pmu_lock out of the generic arm_pmu driver into the ARMv6 code.

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: Russell King <linux@armlinux.org.uk>
---
 arch/arm/kernel/perf_event_v6.c | 26 +++++++++++++++++---------
 drivers/perf/arm_pmu.c          |  1 -
 include/linux/perf/arm_pmu.h    |  5 -----
 3 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c
index 1ae99de..4106a03 100644
--- a/arch/arm/kernel/perf_event_v6.c
+++ b/arch/arm/kernel/perf_event_v6.c
@@ -69,6 +69,12 @@ enum armv6_counters {
 };

 /*
+ * Hardware lock to serialize accesses to PMU registers. Needed for the
+ * read/modify/write sequences.
+ */
+DEFINE_PER_CPU(raw_spinlock_t, pmu_lock);
+
+/*
  * The hardware events that we support. We do support cache operations but
  * we have harvard caches and no way to combine instruction and data
  * accesses/misses in hardware.
@@ -271,7 +277,7 @@ static void armv6pmu_enable_event(struct perf_event *event)
 	unsigned long val, mask, evt, flags;
 	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
 	struct hw_perf_event *hwc = &event->hw;
-	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
+	struct raw_spinlock_t *lock = this_cpu_ptr(&pmu_lock);
 	int idx = hwc->idx;

 	if (ARMV6_CYCLE_COUNTER == idx) {
@@ -294,12 +300,12 @@ static void armv6pmu_enable_event(struct perf_event *event)
 	 * Mask out the current event and set the counter to count the event
 	 * that we're interested in.
 	 */
-	raw_spin_lock_irqsave(&events->pmu_lock, flags);
+	raw_spin_lock_irqsave(lock, flags);
 	val = armv6_pmcr_read();
 	val &= ~mask;
 	val |= evt;
 	armv6_pmcr_write(val);
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+	raw_spin_unlock_irqrestore(lock, flags);
 }

 static irqreturn_t
@@ -363,25 +369,25 @@ static void armv6pmu_enable_event(struct perf_event *event)
 static void armv6pmu_start(struct arm_pmu *cpu_pmu)
 {
 	unsigned long flags, val;
-	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
+	raw_spinlock_t *lock = this_cpu_ptr(&pmu_lock);

-	raw_spin_lock_irqsave(&events->pmu_lock, flags);
+	raw_spin_lock_irqsave(lock, flags);
 	val = armv6_pmcr_read();
 	val |= ARMV6_PMCR_ENABLE;
 	armv6_pmcr_write(val);
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+	raw_spin_unlock_irqrestore(lock, flags);
 }

 static void armv6pmu_stop(struct arm_pmu *cpu_pmu)
 {
 	unsigned long flags, val;
-	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
+	raw_spinlock_t *lock = this_cpu_ptr(&pmu_lock);

-	raw_spin_lock_irqsave(&events->pmu_lock, flags);
+	raw_spin_lock_irqsave(lock, flags);
 	val = armv6_pmcr_read();
 	val &= ~ARMV6_PMCR_ENABLE;
 	armv6_pmcr_write(val);
-	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+	raw_spin_unlock_irqrestore(lock, flags);
 }

 static int
@@ -502,6 +508,8 @@ static void armv6pmu_init(struct arm_pmu *cpu_pmu)
 	cpu_pmu->stop		= armv6pmu_stop;
 	cpu_pmu->map_event	= armv6_map_event;
 	cpu_pmu->num_events	= 3;
+
+	raw_spin_lock_init(this_cpu_ptr(&pmu_lock));
 }

 static int armv6_1136_pmu_init(struct arm_pmu *cpu_pmu)
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index eec75b9..cd23acd 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -822,7 +822,6 @@ static struct arm_pmu *__armpmu_alloc(gfp_t flags)
 		struct pmu_hw_events *events;

 		events = per_cpu_ptr(pmu->hw_events, cpu);
-		raw_spin_lock_init(&events->pmu_lock);
 		events->percpu_pmu = pmu;
 	}

diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 4641e85..5913981 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -58,11 +58,6 @@ struct pmu_hw_events {
 	 */
 	DECLARE_BITMAP(used_mask, ARMPMU_MAX_HWEVENTS);

-	/*
-	 * Hardware lock to serialize accesses to PMU registers. Needed for the
-	 * read/modify/write sequences.
-	 */
-	raw_spinlock_t		pmu_lock;

 	/*
 	 * When using percpu IRQs, we need a percpu dev_id. Place it here as we
--
1.9.1

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 6/9] arm64: perf: Do not call irq_work_run in NMI context
  2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
                   ` (4 preceding siblings ...)
  2019-03-22 16:24 ` [PATCH v2 5/9] perf/arm_pmu: Move PMU lock to ARMv6 events Julien Thierry
@ 2019-03-22 16:24 ` Julien Thierry
  2019-03-22 16:24 ` [PATCH v2 7/9] arm/arm64: kvm: pmu: Make overflow handler NMI safe Julien Thierry
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-22 16:24 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, Julien Thierry, peterz, Catalin Marinas,
	will.deacon, acme, alexander.shishkin, mingo, namhyung, jolsa

Function irq_work_run is not NMI safe and should not be called from NMI
context.

When PMU interrupt is an NMI do not call irq_work_run. Instead rely on the
IRQ work IPI to run the irq_work queue once NMI/IRQ contexts have been
exited.

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 | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index bae4242..316566d 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -776,7 +776,8 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
 	 * platforms that can have the PMU interrupts raised as an NMI, this
 	 * will not work.
 	 */
-	irq_work_run();
+	if (!in_nmi())
+		irq_work_run();
 
 	return IRQ_HANDLED;
 }
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 7/9] arm/arm64: kvm: pmu: Make overflow handler NMI safe
  2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
                   ` (5 preceding siblings ...)
  2019-03-22 16:24 ` [PATCH v2 6/9] arm64: perf: Do not call irq_work_run in NMI context Julien Thierry
@ 2019-03-22 16:24 ` Julien Thierry
  2019-03-22 16:24 ` [PATCH v2 8/9] arm_pmu: Introduce pmu_irq_ops Julien Thierry
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-22 16:24 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, Julien Thierry, peterz, Suzuki K Pouloze,
	will.deacon, Christoffer Dall, acme, alexander.shishkin, mingo,
	James Morse, Marc Zyngier, namhyung, jolsa, kvmarm

When using an NMI for the PMU interrupt, taking any lock migh cause a
deadlock. The current PMU overflow handler in KVM takes takes locks when
trying to wake up a vcpu.

When overflow handler is called by an NMI, defer the vcpu waking in an
irq_work queue.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Cc: Christoffer Dall <christoffer.dall@arm.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: James Morse <james.morse@arm.com>
Cc: Suzuki K Pouloze <suzuki.poulose@arm.com>
Cc: kvmarm@lists.cs.columbia.edu
---
 include/kvm/arm_pmu.h |  1 +
 virt/kvm/arm/pmu.c    | 37 +++++++++++++++++++++++++++++++++----
 2 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index f87fe20..6a7c9dd 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -37,6 +37,7 @@ struct kvm_pmu {
 	bool ready;
 	bool created;
 	bool irq_level;
+	struct irq_work overflow_work;
 };

 #define kvm_arm_pmu_v3_ready(v)		((v)->arch.pmu.ready)
diff --git a/virt/kvm/arm/pmu.c b/virt/kvm/arm/pmu.c
index 1c5b76c..a72c972 100644
--- a/virt/kvm/arm/pmu.c
+++ b/virt/kvm/arm/pmu.c
@@ -273,15 +273,37 @@ void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu)
 	kvm_pmu_update_state(vcpu);
 }

+static inline struct kvm_vcpu *kvm_pmu_to_vcpu(struct kvm_pmu *pmu)
+{
+	struct kvm_vcpu_arch *vcpu_arch;
+
+	vcpu_arch = container_of(pmu, struct kvm_vcpu_arch, pmu);
+	return container_of(vcpu_arch, struct kvm_vcpu, arch);
+}
+
 static inline struct kvm_vcpu *kvm_pmc_to_vcpu(struct kvm_pmc *pmc)
 {
 	struct kvm_pmu *pmu;
-	struct kvm_vcpu_arch *vcpu_arch;

 	pmc -= pmc->idx;
 	pmu = container_of(pmc, struct kvm_pmu, pmc[0]);
-	vcpu_arch = container_of(pmu, struct kvm_vcpu_arch, pmu);
-	return container_of(vcpu_arch, struct kvm_vcpu, arch);
+	return kvm_pmu_to_vcpu(pmu);
+}
+
+/**
+ * When perf interrupt is an NMI, we cannot safely notify the vcpu corresponding
+ * to the even.
+ * This is why we need a callback to do it once outside of the NMI context.
+ */
+static void kvm_pmu_perf_overflow_notify_vcpu(struct irq_work *work)
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm_pmu *pmu;
+
+	pmu = container_of(work, struct kvm_pmu, overflow_work);
+	vcpu = kvm_pmu_to_vcpu(pmu);
+
+	kvm_vcpu_kick(vcpu);
 }

 /**
@@ -299,7 +321,11 @@ static void kvm_pmu_perf_overflow(struct perf_event *perf_event,

 	if (kvm_pmu_overflow_status(vcpu)) {
 		kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu);
-		kvm_vcpu_kick(vcpu);
+
+		if (!in_nmi())
+			kvm_vcpu_kick(vcpu);
+		else
+			irq_work_queue(&vcpu->arch.pmu.overflow_work);
 	}
 }

@@ -501,6 +527,9 @@ static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
 			return ret;
 	}

+	init_irq_work(&vcpu->arch.pmu.overflow_work,
+		      kvm_pmu_perf_overflow_notify_vcpu);
+
 	vcpu->arch.pmu.created = true;
 	return 0;
 }
--
1.9.1

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 8/9] arm_pmu: Introduce pmu_irq_ops
  2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
                   ` (6 preceding siblings ...)
  2019-03-22 16:24 ` [PATCH v2 7/9] arm/arm64: kvm: pmu: Make overflow handler NMI safe Julien Thierry
@ 2019-03-22 16:24 ` Julien Thierry
  2019-03-22 16:24 ` [PATCH v2 9/9] arm_pmu: Use NMIs for PMU Julien Thierry
  2019-03-22 16:49 ` [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Peter Zijlstra
  9 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-22 16:24 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, Julien Thierry, peterz, will.deacon, acme,
	alexander.shishkin, mingo, namhyung, jolsa

Currently the PMU interrupt can either be a normal irq or a percpu irq.
Supporting NMI will introduce two cases for each existing one. It becomes
a mess of 'if's when managing the interrupt.

Define sets of callbacks for operations commonly done on the interrupt. The
appropriate set of callbacks is selected at interrupt request time and
simplifies interrupt enabling/disabling and freeing.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
---
 drivers/perf/arm_pmu.c | 86 ++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 70 insertions(+), 16 deletions(-)

diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index cd23acd..944bbecd 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -25,8 +25,46 @@
 
 #include <asm/irq_regs.h>
 
+static int armpmu_count_irq_users(const int irq);
+
+struct pmu_irq_ops {
+	void (*enable_pmuirq)(unsigned int irq);
+	void (*disable_pmuirq)(unsigned int irq);
+	void (*free_pmuirq)(unsigned int irq, int cpu, void __percpu *devid);
+};
+
+static void armpmu_free_pmuirq(unsigned int irq, int cpu, void __percpu *devid)
+{
+	free_irq(irq, per_cpu_ptr(devid, cpu));
+}
+
+static const struct pmu_irq_ops pmuirq_ops = {
+	.enable_pmuirq = enable_irq,
+	.disable_pmuirq = disable_irq_nosync,
+	.free_pmuirq = armpmu_free_pmuirq
+};
+
+static void armpmu_enable_percpu_pmuirq(unsigned int irq)
+{
+	enable_percpu_irq(irq, IRQ_TYPE_NONE);
+}
+
+static void armpmu_free_percpu_pmuirq(unsigned int irq, int cpu,
+				   void __percpu *devid)
+{
+	if (armpmu_count_irq_users(irq) == 1)
+		free_percpu_irq(irq, devid);
+}
+
+static const struct pmu_irq_ops percpu_pmuirq_ops = {
+	.enable_pmuirq = armpmu_enable_percpu_pmuirq,
+	.disable_pmuirq = disable_percpu_irq,
+	.free_pmuirq = armpmu_free_percpu_pmuirq
+};
+
 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);
 
 static inline u64 arm_pmu_event_max_period(struct perf_event *event)
 {
@@ -543,6 +581,19 @@ static int armpmu_count_irq_users(const int irq)
 	return count;
 }
 
+static const struct pmu_irq_ops *armpmu_find_irq_ops(int irq)
+{
+	int cpu;
+
+	for_each_possible_cpu(cpu) {
+		if (per_cpu(cpu_irq, cpu) == irq
+		    && per_cpu(cpu_irq_ops, cpu))
+			return per_cpu(cpu_irq_ops, cpu);
+	}
+
+	return NULL;
+}
+
 void armpmu_free_irq(int irq, int cpu)
 {
 	if (per_cpu(cpu_irq, cpu) == 0)
@@ -550,18 +601,18 @@ void armpmu_free_irq(int irq, int cpu)
 	if (WARN_ON(irq != per_cpu(cpu_irq, cpu)))
 		return;
 
-	if (!irq_is_percpu_devid(irq))
-		free_irq(irq, per_cpu_ptr(&cpu_armpmu, cpu));
-	else if (armpmu_count_irq_users(irq) == 1)
-		free_percpu_irq(irq, &cpu_armpmu);
+	per_cpu(cpu_irq_ops, cpu)->free_pmuirq(irq, cpu, &cpu_armpmu);
 
 	per_cpu(cpu_irq, cpu) = 0;
+	per_cpu(cpu_irq_ops, cpu) = NULL;
 }
 
 int armpmu_request_irq(int irq, int cpu)
 {
 	int err = 0;
 	const irq_handler_t handler = armpmu_dispatch_irq;
+	const struct pmu_irq_ops *irq_ops;
+
 	if (!irq)
 		return 0;
 
@@ -583,15 +634,26 @@ int armpmu_request_irq(int irq, int cpu)
 		irq_set_status_flags(irq, IRQ_NOAUTOEN);
 		err = request_irq(irq, handler, irq_flags, "arm-pmu",
 				  per_cpu_ptr(&cpu_armpmu, cpu));
+
+		irq_ops = &pmuirq_ops;
 	} else if (armpmu_count_irq_users(irq) == 0) {
 		err = request_percpu_irq(irq, handler, "arm-pmu",
 					 &cpu_armpmu);
+
+		irq_ops = &percpu_pmuirq_ops;
+	} else {
+		/* Per cpudevid irq was already requested by another CPU */
+		irq_ops = armpmu_find_irq_ops(irq);
+
+		if (WARN_ON(!irq_ops))
+			err = -EINVAL;
 	}
 
 	if (err)
 		goto err_out;
 
 	per_cpu(cpu_irq, cpu) = irq;
+	per_cpu(cpu_irq_ops, cpu) = irq_ops;
 	return 0;
 
 err_out:
@@ -624,12 +686,8 @@ static int arm_perf_starting_cpu(unsigned int cpu, struct hlist_node *node)
 	per_cpu(cpu_armpmu, cpu) = pmu;
 
 	irq = armpmu_get_cpu_irq(pmu, cpu);
-	if (irq) {
-		if (irq_is_percpu_devid(irq))
-			enable_percpu_irq(irq, IRQ_TYPE_NONE);
-		else
-			enable_irq(irq);
-	}
+	if (irq)
+		per_cpu(cpu_irq_ops, cpu)->enable_pmuirq(irq);
 
 	return 0;
 }
@@ -643,12 +701,8 @@ static int arm_perf_teardown_cpu(unsigned int cpu, struct hlist_node *node)
 		return 0;
 
 	irq = armpmu_get_cpu_irq(pmu, cpu);
-	if (irq) {
-		if (irq_is_percpu_devid(irq))
-			disable_percpu_irq(irq);
-		else
-			disable_irq_nosync(irq);
-	}
+	if (irq)
+		per_cpu(cpu_irq_ops, cpu)->disable_pmuirq(irq);
 
 	per_cpu(cpu_armpmu, cpu) = NULL;
 
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 9/9] arm_pmu: Use NMIs for PMU
  2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
                   ` (7 preceding siblings ...)
  2019-03-22 16:24 ` [PATCH v2 8/9] arm_pmu: Introduce pmu_irq_ops Julien Thierry
@ 2019-03-22 16:24 ` Julien Thierry
  2019-03-22 16:49 ` [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Peter Zijlstra
  9 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-22 16:24 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, Julien Thierry, peterz, will.deacon, acme,
	alexander.shishkin, mingo, namhyung, jolsa

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>
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(-)

diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 944bbecd..83e150c 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -44,6 +44,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);
@@ -62,6 +73,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);
@@ -632,15 +668,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);
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt
  2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
                   ` (8 preceding siblings ...)
  2019-03-22 16:24 ` [PATCH v2 9/9] arm_pmu: Use NMIs for PMU Julien Thierry
@ 2019-03-22 16:49 ` Peter Zijlstra
  9 siblings, 0 replies; 19+ messages in thread
From: Peter Zijlstra @ 2019-03-22 16:49 UTC (permalink / raw)
  To: Julien Thierry
  Cc: mark.rutland, alexander.shishkin, will.deacon, acme, mingo,
	namhyung, jolsa, linux-arm-kernel

On Fri, Mar 22, 2019 at 04:23:55PM +0000, Julien Thierry wrote:
> Hi,
> 
> On arm64, perf reports that counter overflow very often (too often)
> happen in function that potentially enabled interrutps:
> 
> $ perf record -a -- sleep 60; perf report -F overhead,symbol
> [...]
> # Overhead  Symbol
> # ........  ..........................................
> #
>      6.58%  [k] _raw_spin_unlock_irq
>      6.10%  [k] _raw_spin_unlock_irqrestore
>      5.52%  [k] ___bpf_prog_run
>      4.37%  [k] el0_svc_common
>      2.58%  [k] arch_cpu_idle
>      2.39%  [k] kmem_cache_alloc
>      2.06%  [k] __seccomp_filter
>      [...]
> 
> The root issue is, if an overflow happens while executing with
> interrupts disabled, the perf event will only be handled when interrupts
> are reenabled (i.e. when the PMU interrupt is taken). The result being
> the event being reported at the interrupt enabling location rather than
> where the overflow actually happened.
> 
> Now that we have support for pseudo-NMI on arm64 with GICv3, we can use
> it to improve the profiling done using the PMU interrupt.
> 
> With these changes, on the same machine, we get:
> # Overhead  Symbol
> # ........  ..................................
> #
>      7.06%  [k] ___bpf_prog_run
>      4.08%  [k] __update_load_avg_se
>      4.02%  [k] ktime_get_ts64
>      3.77%  [k] __ll_sc_arch_atomic_add_return
>      3.71%  [k] file_ra_state_init
>      3.62%  [k] __ll_sc_arch_atomic64_sub
>      3.53%  [k] __ll_sc___cmpxchg_case_acq_32
>      [...]
> 
> _raw_spin_unlock_irq/irqrestore don't event appear anymore in the
> perf trace.

Very nice; I've little useful to say about the patches themselves, they
appear ok, but this is not code I know well. But it is good to see perf
becoming more useful on arm64.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection
  2019-03-22 16:23 ` [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection Julien Thierry
@ 2019-03-25 12:36   ` liwei (GF)
  2019-03-25 13:59     ` Julien Thierry
  2019-03-25 12:37   ` liwei (GF)
  2019-03-25 13:01   ` Marc Gonzalez
  2 siblings, 1 reply; 19+ messages in thread
From: liwei (GF) @ 2019-03-25 12:36 UTC (permalink / raw)
  To: Julien Thierry, linux-arm-kernel
  Cc: mark.rutland, peterz, Catalin Marinas, will.deacon, acme,
	alexander.shishkin, mingo, libin 00196512, guohanjun 00470146,
	namhyung, jolsa

Calling the ARMV8_IDX_TO_COUNTER() is missed before accessing these counters.
It will access the wrong counters and may even cause undefinstr exception.
I think we need add it as armv8pmu_select_counter() do before.

Signed-off-by: Wei Li <liwei391@huawei.com>
---
 arch/arm64/kernel/perf_event.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index c6fe36f5a9f5..42fe7109468f 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -599,7 +599,9 @@ static inline int armv8pmu_counter_has_overflowed(u32 pmnc, int idx)
 
 static inline u32 armv8pmu_read_evcntr(int idx)
 {
-	return read_pmevcntrn(idx);
+	u32 counter = ARMV8_IDX_TO_COUNTER(idx);
+
+	return read_pmevcntrn(counter);
 }
 
 static inline u64 armv8pmu_read_hw_counter(struct perf_event *event)
@@ -633,7 +635,9 @@ static inline u64 armv8pmu_read_counter(struct perf_event *event)
 
 static inline void armv8pmu_write_evcntr(int idx, u32 value)
 {
-	write_pmevcntrn(idx, value);
+	u32 counter = ARMV8_IDX_TO_COUNTER(idx);
+
+	write_pmevcntrn(counter, value);
 }
 
 static inline void armv8pmu_write_hw_counter(struct perf_event *event,
@@ -674,8 +678,10 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
 
 static inline void armv8pmu_write_evtype(int idx, u32 val)
 {
+	u32 counter = ARMV8_IDX_TO_COUNTER(idx);
+
 	val &= ARMV8_PMU_EVTYPE_MASK;
-	write_pmevtypern(idx, val);
+	write_pmevtypern(counter, val);
 }
 
 static inline void armv8pmu_write_event_type(struct perf_event *event)
-- 
2.17.1




_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection
  2019-03-22 16:23 ` [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection Julien Thierry
  2019-03-25 12:36   ` liwei (GF)
@ 2019-03-25 12:37   ` liwei (GF)
  2019-03-25 13:59     ` Julien Thierry
  2019-03-25 13:01   ` Marc Gonzalez
  2 siblings, 1 reply; 19+ messages in thread
From: liwei (GF) @ 2019-03-25 12:37 UTC (permalink / raw)
  To: Julien Thierry, linux-arm-kernel
  Cc: mark.rutland, peterz, Catalin Marinas, will.deacon, acme,
	alexander.shishkin, mingo, libin 00196512, guohanjun 00470146,
	namhyung, jolsa

When PMSELR_EL0.SEL == 31, the register PMXEVTYPER_EL0 accesses PMCCFILTR_EL0.
I think we need to add a branch to process such condition.

Signed-off-by: Wei Li <liwei391@huawei.com>
---
 arch/arm64/kernel/perf_event.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 42fe7109468f..76bc55e67137 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -538,7 +538,7 @@ static inline bool armv8pmu_event_is_chained(struct perf_event *event)
 		PMEVN_CASE(28, case_macro);			\
 		PMEVN_CASE(29, case_macro);			\
 		PMEVN_CASE(30, case_macro);			\
-		default: WARN(1, "Inavlid PMEV* index");	\
+		default: WARN(1, "Inavlid PMEV* index %#x", __x);	\
 		}						\
 	} while (0)
 
@@ -684,7 +684,7 @@ static inline void armv8pmu_write_evtype(int idx, u32 val)
 	write_pmevtypern(counter, val);
 }
 
-static inline void armv8pmu_write_event_type(struct perf_event *event)
+static inline void armv8pmu_write_hw_type(struct perf_event *event)
 {
 	struct hw_perf_event *hwc = &event->hw;
 	int idx = hwc->idx;
@@ -705,6 +705,21 @@ static inline void armv8pmu_write_event_type(struct perf_event *event)
 	}
 }
 
+static inline void armv8pmu_write_event_type(struct perf_event *event)
+{
+	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+
+	if (!armv8pmu_counter_valid(cpu_pmu, idx))
+		pr_err("CPU%u reading wrong counter %d\n",
+			smp_processor_id(), idx);
+	else if (idx == ARMV8_IDX_CYCLE_COUNTER)
+		write_sysreg(hwc->config_base & ARMV8_PMU_EVTYPE_MASK, pmccfiltr_el0);
+	else
+		armv8pmu_write_hw_type(event);
+}
+
 static inline int armv8pmu_enable_counter(int idx)
 {
 	u32 counter = ARMV8_IDX_TO_COUNTER(idx);
-- 
2.17.1




_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection
  2019-03-22 16:23 ` [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection Julien Thierry
  2019-03-25 12:36   ` liwei (GF)
  2019-03-25 12:37   ` liwei (GF)
@ 2019-03-25 13:01   ` Marc Gonzalez
  2019-07-08 11:40     ` Julien Thierry
  2 siblings, 1 reply; 19+ messages in thread
From: Marc Gonzalez @ 2019-03-25 13:01 UTC (permalink / raw)
  To: Julien Thierry, Mark Rutland; +Cc: Linux ARM

On 22/03/2019 17:23, Julien Thierry wrote:

> +/*
> + * This code is really good
> + */

IMHO, nothing involving preprocessor tom-foolery should qualify as "good".

> +
> +#define PMEVN_CASE(__n, case_macro) \
> +	case __n: case_macro(__n); break;
> +
> +#define PMEVN_SWITCH(__x, case_macro)				\

I've never understood: what's the point of prefixing macro parameters
with underscores?

What does the PMEVN_CASE macro buy us?

Regards.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection
  2019-03-25 12:36   ` liwei (GF)
@ 2019-03-25 13:59     ` Julien Thierry
  0 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-25 13:59 UTC (permalink / raw)
  To: liwei (GF), linux-arm-kernel
  Cc: mark.rutland, peterz, Catalin Marinas, will.deacon, acme,
	alexander.shishkin, mingo, libin 00196512, guohanjun 00470146,
	namhyung, jolsa

Hi,

On 25/03/2019 12:36, liwei (GF) wrote:
> Calling the ARMV8_IDX_TO_COUNTER() is missed before accessing these counters.
> It will access the wrong counters and may even cause undefinstr exception.
> I think we need add it as armv8pmu_select_counter() do before.
> 

Thank you for reporting and fixing that issue. I'll include the fix in
my next version.

Thanks,

Julien

> Signed-off-by: Wei Li <liwei391@huawei.com>
> ---
>  arch/arm64/kernel/perf_event.c | 12 +++++++++---
>  1 file changed, 9 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
> index c6fe36f5a9f5..42fe7109468f 100644
> --- a/arch/arm64/kernel/perf_event.c
> +++ b/arch/arm64/kernel/perf_event.c
> @@ -599,7 +599,9 @@ static inline int armv8pmu_counter_has_overflowed(u32 pmnc, int idx)
>  
>  static inline u32 armv8pmu_read_evcntr(int idx)
>  {
> -	return read_pmevcntrn(idx);
> +	u32 counter = ARMV8_IDX_TO_COUNTER(idx);
> +
> +	return read_pmevcntrn(counter);
>  }
>  
>  static inline u64 armv8pmu_read_hw_counter(struct perf_event *event)
> @@ -633,7 +635,9 @@ static inline u64 armv8pmu_read_counter(struct perf_event *event)
>  
>  static inline void armv8pmu_write_evcntr(int idx, u32 value)
>  {
> -	write_pmevcntrn(idx, value);
> +	u32 counter = ARMV8_IDX_TO_COUNTER(idx);
> +
> +	write_pmevcntrn(counter, value);
>  }
>  
>  static inline void armv8pmu_write_hw_counter(struct perf_event *event,
> @@ -674,8 +678,10 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
>  
>  static inline void armv8pmu_write_evtype(int idx, u32 val)
>  {
> +	u32 counter = ARMV8_IDX_TO_COUNTER(idx);
> +
>  	val &= ARMV8_PMU_EVTYPE_MASK;
> -	write_pmevtypern(idx, val);
> +	write_pmevtypern(counter, val);
>  }
>  
>  static inline void armv8pmu_write_event_type(struct perf_event *event)
> 

-- 
Julien Thierry

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection
  2019-03-25 12:37   ` liwei (GF)
@ 2019-03-25 13:59     ` Julien Thierry
  2019-03-26  2:10       ` liwei (GF)
  0 siblings, 1 reply; 19+ messages in thread
From: Julien Thierry @ 2019-03-25 13:59 UTC (permalink / raw)
  To: liwei (GF), linux-arm-kernel
  Cc: mark.rutland, peterz, Catalin Marinas, will.deacon, acme,
	alexander.shishkin, mingo, libin 00196512, guohanjun 00470146,
	namhyung, jolsa

Hi

On 25/03/2019 12:37, liwei (GF) wrote:
> When PMSELR_EL0.SEL == 31, the register PMXEVTYPER_EL0 accesses PMCCFILTR_EL0.
> I think we need to add a branch to process such condition.
> 
> Signed-off-by: Wei Li <liwei391@huawei.com>
> ---
>  arch/arm64/kernel/perf_event.c | 19 +++++++++++++++++--
>  1 file changed, 17 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
> index 42fe7109468f..76bc55e67137 100644
> --- a/arch/arm64/kernel/perf_event.c
> +++ b/arch/arm64/kernel/perf_event.c
> @@ -538,7 +538,7 @@ static inline bool armv8pmu_event_is_chained(struct perf_event *event)
>  		PMEVN_CASE(28, case_macro);			\
>  		PMEVN_CASE(29, case_macro);			\
>  		PMEVN_CASE(30, case_macro);			\
> -		default: WARN(1, "Inavlid PMEV* index");	\
> +		default: WARN(1, "Inavlid PMEV* index %#x", __x);	\

I'm not convinced we need that. We're generating register accesses. If
we try to access a register that doesn't exist, the compiler/assembler
will complain and fail to build. So that default case would not be run.

Thanks,

Julien

>  		}						\
>  	} while (0)
>  
> @@ -684,7 +684,7 @@ static inline void armv8pmu_write_evtype(int idx, u32 val)
>  	write_pmevtypern(counter, val);
>  }
>  
> -static inline void armv8pmu_write_event_type(struct perf_event *event)
> +static inline void armv8pmu_write_hw_type(struct perf_event *event)
>  {
>  	struct hw_perf_event *hwc = &event->hw;
>  	int idx = hwc->idx;
> @@ -705,6 +705,21 @@ static inline void armv8pmu_write_event_type(struct perf_event *event)
>  	}
>  }
>  
> +static inline void armv8pmu_write_event_type(struct perf_event *event)
> +{
> +	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
> +	struct hw_perf_event *hwc = &event->hw;
> +	int idx = hwc->idx;
> +
> +	if (!armv8pmu_counter_valid(cpu_pmu, idx))
> +		pr_err("CPU%u reading wrong counter %d\n",
> +			smp_processor_id(), idx);
> +	else if (idx == ARMV8_IDX_CYCLE_COUNTER)
> +		write_sysreg(hwc->config_base & ARMV8_PMU_EVTYPE_MASK, pmccfiltr_el0);
> +	else
> +		armv8pmu_write_hw_type(event);
> +}
> +
>  static inline int armv8pmu_enable_counter(int idx)
>  {
>  	u32 counter = ARMV8_IDX_TO_COUNTER(idx);
> 

-- 
Julien Thierry

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection
  2019-03-25 13:59     ` Julien Thierry
@ 2019-03-26  2:10       ` liwei (GF)
  2019-03-28  9:48         ` Julien Thierry
  0 siblings, 1 reply; 19+ messages in thread
From: liwei (GF) @ 2019-03-26  2:10 UTC (permalink / raw)
  To: Julien Thierry, linux-arm-kernel
  Cc: mark.rutland, peterz, Catalin Marinas, will.deacon, acme,
	alexander.shishkin, mingo, libin 00196512, guohanjun 00470146,
	namhyung, jolsa

Hi Julien,

On 2019/3/25 21:59, Julien Thierry wrote:
> 
> On 25/03/2019 12:37, liwei (GF) wrote:
>> When PMSELR_EL0.SEL == 31, the register PMXEVTYPER_EL0 accesses PMCCFILTR_EL0.
>> I think we need to add a branch to process such condition.
>>
>> Signed-off-by: Wei Li <liwei391@huawei.com>
>> ---
>>  arch/arm64/kernel/perf_event.c | 19 +++++++++++++++++--
>>  1 file changed, 17 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
>> index 42fe7109468f..76bc55e67137 100644
>> --- a/arch/arm64/kernel/perf_event.c
>> +++ b/arch/arm64/kernel/perf_event.c
>> @@ -538,7 +538,7 @@ static inline bool armv8pmu_event_is_chained(struct perf_event *event)
>>  		PMEVN_CASE(28, case_macro);			\
>>  		PMEVN_CASE(29, case_macro);			\
>>  		PMEVN_CASE(30, case_macro);			\
>> -		default: WARN(1, "Inavlid PMEV* index");	\
>> +		default: WARN(1, "Inavlid PMEV* index %#x", __x);	\
> 
> I'm not convinced we need that. We're generating register accesses. If
> we try to access a register that doesn't exist, the compiler/assembler
> will complain and fail to build. So that default case would not be run.
> 
This issue is covered by the lack of ARMV8_IDX_TO_COUNTER(), the ARMV8_IDX_TO_COUNTER()
will convert the CYCLE_COUNTER sw_index 0 to hw_index 31.
It goes well when accessing PMCCFILTR_EL0 though armv8pmu_select_counter(), but it will just go
into the default case above when doing armv8pmu_enable_event(CYCLE_COUNTER_EVENT).

So we may not need the WARN info anymore, but we need the process to access the PMCCFILTR_EL0
here indeed, like what armv8pmu_write_counter() and armv8pmu_read_counter() do.

[   22.925659] ------------[ cut here ]------------
[   22.930264] Inavlid PMEV* index 0x1f
[   22.930269] WARNING: CPU: 58 PID: 411 at /home/liwei/main_code/hulk/arch/arm64/kernel/perf_event.c:566 write_pmevtypern+0x34/0x150
[   22.945552] Modules linked in:
[   22.948598] CPU: 58 PID: 411 Comm: kworker/58:1 Tainted: G        W         4.19.30+ #13
[   22.956674] Hardware name: Huawei TaiShan 2280 /BC11SPCD, BIOS 1.40 01/10/2018
[   22.963888] Workqueue: events smp_call_on_cpu_callback
[   22.969016] pstate: 60000005 (nZCv daif -PAN -UAO)
[   22.973796] pc : write_pmevtypern+0x34/0x150
[   22.978054] lr : write_pmevtypern+0x34/0x150
[   22.982312] sp : ffff0000187fb820
[   22.985614] pmr_save: 00000070
[   22.988657] x29: ffff0000187fb820 x28: 0000000000000000 
[   22.993960] x27: ffff00000b81bca8 x26: ffff000008255750 
[   22.999263] x25: ffff80dffbe59640 x24: ffff80dffbe59640 
[   23.004566] x23: ffff805f780dd000 x22: 0000000000000000 
[   23.009868] x21: 0000000000000002 x20: 0000000008000011 
[   23.015171] x19: 000000000000001f x18: ffffffffffffffff 
[   23.020474] x17: 0000000000000000 x16: 0000000000000000 
[   23.025776] x15: ffff0000097b9708 x14: ffff0000099ef938 
[   23.031079] x13: ffff0000099ef570 x12: ffffffffffffffff 
[   23.036381] x11: ffff0000097e3000 x10: 0000000005f5e0ff 
[   23.041684] x9 : 00000000ffffffd0 x8 : 6631783020786564 
[   23.046987] x7 : ffff0000099eeba0 x6 : 000000000000114f 
[   23.052290] x5 : 00ffffffffffffff x4 : 0000000000000000 
[   23.057592] x3 : 0000000000000000 x2 : ffffffffffffffff 
[   23.062895] x1 : 2a15229c4b9a7500 x0 : 0000000000000000 
[   23.068197] Call trace:
[   23.070633]  write_pmevtypern+0x34/0x150
[   23.074544]  armv8pmu_enable_event+0x7c/0x158
[   23.078889]  armpmu_start+0x4c/0x68
[   23.082366]  armpmu_add+0xcc/0xd8
[   23.085669]  event_sched_in.isra.56+0xc0/0x220
[   23.090100]  group_sched_in+0x60/0x188
[   23.093837]  pinned_sched_in+0x104/0x1e8
[   23.097748]  visit_groups_merge+0x12c/0x1d0
[   23.101920]  ctx_sched_in+0x108/0x168
[   23.105571]  perf_event_sched_in+0x2c/0xa8
[   23.109655]  ctx_resched+0x60/0xa8
[   23.113045]  __perf_event_enable+0x180/0x1f8
[   23.117303]  event_function+0x78/0xd8
[   23.120954]  remote_function+0x60/0x70
[   23.124692]  generic_exec_single+0x114/0x168
[   23.128950]  smp_call_function_single+0x15c/0x1b0
[   23.133643]  event_function_call+0x16c/0x178
[   23.137901]  _perf_event_enable+0xa4/0xd0
[   23.141899]  perf_event_enable+0x20/0x40

>>  
>> +static inline void armv8pmu_write_event_type(struct perf_event *event)
>> +{
>> +	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
>> +	struct hw_perf_event *hwc = &event->hw;
>> +	int idx = hwc->idx;
>> +
>> +	if (!armv8pmu_counter_valid(cpu_pmu, idx))
>> +		pr_err("CPU%u reading wrong counter %d\n",
This print info need to be replaced to "CPU%u writing wrong event %d\n".

>> +			smp_processor_id(), idx);
>> +	else if (idx == ARMV8_IDX_CYCLE_COUNTER)
>> +		write_sysreg(hwc->config_base & ARMV8_PMU_EVTYPE_MASK, pmccfiltr_el0);
>> +	else
>> +		armv8pmu_write_hw_type(event);
>> +}
>> +
>>  static inline int armv8pmu_enable_counter(int idx)
>>  {
>>  	u32 counter = ARMV8_IDX_TO_COUNTER(idx);
>>
> 

Thanks,
Wei


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection
  2019-03-26  2:10       ` liwei (GF)
@ 2019-03-28  9:48         ` Julien Thierry
  0 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-03-28  9:48 UTC (permalink / raw)
  To: liwei (GF), linux-arm-kernel
  Cc: mark.rutland, peterz, Catalin Marinas, will.deacon, acme,
	alexander.shishkin, mingo, libin 00196512, guohanjun 00470146,
	namhyung, jolsa

Hi,

On 26/03/2019 02:10, liwei (GF) wrote:
> Hi Julien,
> 
> On 2019/3/25 21:59, Julien Thierry wrote:
>>
>> On 25/03/2019 12:37, liwei (GF) wrote:
>>> When PMSELR_EL0.SEL == 31, the register PMXEVTYPER_EL0 accesses PMCCFILTR_EL0.
>>> I think we need to add a branch to process such condition.
>>>
>>> Signed-off-by: Wei Li <liwei391@huawei.com>
>>> ---
>>>  arch/arm64/kernel/perf_event.c | 19 +++++++++++++++++--
>>>  1 file changed, 17 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
>>> index 42fe7109468f..76bc55e67137 100644
>>> --- a/arch/arm64/kernel/perf_event.c
>>> +++ b/arch/arm64/kernel/perf_event.c
>>> @@ -538,7 +538,7 @@ static inline bool armv8pmu_event_is_chained(struct perf_event *event)
>>>  		PMEVN_CASE(28, case_macro);			\
>>>  		PMEVN_CASE(29, case_macro);			\
>>>  		PMEVN_CASE(30, case_macro);			\
>>> -		default: WARN(1, "Inavlid PMEV* index");	\
>>> +		default: WARN(1, "Inavlid PMEV* index %#x", __x);	\
>>
>> I'm not convinced we need that. We're generating register accesses. If
>> we try to access a register that doesn't exist, the compiler/assembler
>> will complain and fail to build. So that default case would not be run.
>>
> This issue is covered by the lack of ARMV8_IDX_TO_COUNTER(), the ARMV8_IDX_TO_COUNTER()
> will convert the CYCLE_COUNTER sw_index 0 to hw_index 31.

As I said, pmevcntr31_el0 and pmevtyper31_el0 do not exist, and the
compiler/assembler should reject it. So I still don't think that default
case brings anything.

> It goes well when accessing PMCCFILTR_EL0 though armv8pmu_select_counter(), but it will just go
> into the default case above when doing armv8pmu_enable_event(CYCLE_COUNTER_EVENT).
> 
> So we may not need the WARN info anymore, but we need the process to access the PMCCFILTR_EL0
> here indeed, like what armv8pmu_write_counter() and armv8pmu_read_counter() do.
> 
> [   22.925659] ------------[ cut here ]------------
> [   22.930264] Inavlid PMEV* index 0x1f
> [   22.930269] WARNING: CPU: 58 PID: 411 at /home/liwei/main_code/hulk/arch/arm64/kernel/perf_event.c:566 write_pmevtypern+0x34/0x150
> [   22.945552] Modules linked in:
> [   22.948598] CPU: 58 PID: 411 Comm: kworker/58:1 Tainted: G        W         4.19.30+ #13
> [   22.956674] Hardware name: Huawei TaiShan 2280 /BC11SPCD, BIOS 1.40 01/10/2018
> [   22.963888] Workqueue: events smp_call_on_cpu_callback
> [   22.969016] pstate: 60000005 (nZCv daif -PAN -UAO)
> [   22.973796] pc : write_pmevtypern+0x34/0x150
> [   22.978054] lr : write_pmevtypern+0x34/0x150
> [   22.982312] sp : ffff0000187fb820
> [   22.985614] pmr_save: 00000070
> [   22.988657] x29: ffff0000187fb820 x28: 0000000000000000 
> [   22.993960] x27: ffff00000b81bca8 x26: ffff000008255750 
> [   22.999263] x25: ffff80dffbe59640 x24: ffff80dffbe59640 
> [   23.004566] x23: ffff805f780dd000 x22: 0000000000000000 
> [   23.009868] x21: 0000000000000002 x20: 0000000008000011 
> [   23.015171] x19: 000000000000001f x18: ffffffffffffffff 
> [   23.020474] x17: 0000000000000000 x16: 0000000000000000 
> [   23.025776] x15: ffff0000097b9708 x14: ffff0000099ef938 
> [   23.031079] x13: ffff0000099ef570 x12: ffffffffffffffff 
> [   23.036381] x11: ffff0000097e3000 x10: 0000000005f5e0ff 
> [   23.041684] x9 : 00000000ffffffd0 x8 : 6631783020786564 
> [   23.046987] x7 : ffff0000099eeba0 x6 : 000000000000114f 
> [   23.052290] x5 : 00ffffffffffffff x4 : 0000000000000000 
> [   23.057592] x3 : 0000000000000000 x2 : ffffffffffffffff 
> [   23.062895] x1 : 2a15229c4b9a7500 x0 : 0000000000000000 
> [   23.068197] Call trace:
> [   23.070633]  write_pmevtypern+0x34/0x150
> [   23.074544]  armv8pmu_enable_event+0x7c/0x158
> [   23.078889]  armpmu_start+0x4c/0x68
> [   23.082366]  armpmu_add+0xcc/0xd8
> [   23.085669]  event_sched_in.isra.56+0xc0/0x220
> [   23.090100]  group_sched_in+0x60/0x188
> [   23.093837]  pinned_sched_in+0x104/0x1e8
> [   23.097748]  visit_groups_merge+0x12c/0x1d0
> [   23.101920]  ctx_sched_in+0x108/0x168
> [   23.105571]  perf_event_sched_in+0x2c/0xa8
> [   23.109655]  ctx_resched+0x60/0xa8
> [   23.113045]  __perf_event_enable+0x180/0x1f8
> [   23.117303]  event_function+0x78/0xd8
> [   23.120954]  remote_function+0x60/0x70
> [   23.124692]  generic_exec_single+0x114/0x168
> [   23.128950]  smp_call_function_single+0x15c/0x1b0
> [   23.133643]  event_function_call+0x16c/0x178
> [   23.137901]  _perf_event_enable+0xa4/0xd0
> [   23.141899]  perf_event_enable+0x20/0x40
> 
>>>  
>>> +static inline void armv8pmu_write_event_type(struct perf_event *event)
>>> +{
>>> +	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
>>> +	struct hw_perf_event *hwc = &event->hw;
>>> +	int idx = hwc->idx;
>>> +
>>> +	if (!armv8pmu_counter_valid(cpu_pmu, idx))
>>> +		pr_err("CPU%u reading wrong counter %d\n",
> This print info need to be replaced to "CPU%u writing wrong event %d\n".
> 
>>> +			smp_processor_id(), idx);
>>> +	else if (idx == ARMV8_IDX_CYCLE_COUNTER)

However, after looking a bit more into it, I do think you've spotted an
issue that exists with armv8pmu_write_event_type() prior to my series
(and should be fixed out of it).

I'll send a fix shortly and put you in copy.

Thanks,

-- 
Julien Thierry

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection
  2019-03-25 13:01   ` Marc Gonzalez
@ 2019-07-08 11:40     ` Julien Thierry
  0 siblings, 0 replies; 19+ messages in thread
From: Julien Thierry @ 2019-07-08 11:40 UTC (permalink / raw)
  To: Marc Gonzalez, Mark Rutland; +Cc: Linux ARM

Hi Marc,

(Picking that series back after fixing NMIs, sorry for the late reply)

On 25/03/2019 13:01, Marc Gonzalez wrote:
> On 22/03/2019 17:23, Julien Thierry wrote:
> 
>> +/*
>> + * This code is really good
>> + */
> 
> IMHO, nothing involving preprocessor tom-foolery should qualify as "good".
> 
>> +
>> +#define PMEVN_CASE(__n, case_macro) \
>> +	case __n: case_macro(__n); break;
>> +
>> +#define PMEVN_SWITCH(__x, case_macro)				\
> 
> I've never understood: what's the point of prefixing macro parameters
> with underscores?
> 

I picked the patch like this, but I admit I agree with you. So unless
there is a reason to do otherwise, I'll remove those underscores.

> What does the PMEVN_CASE macro buy us?

Best I can think of is avoiding to make the mistake of:

	case 1: case_macro(10); break;


Thanks,

-- 
Julien Thierry

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2019-07-08 11:41 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-22 16:23 [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Julien Thierry
2019-03-22 16:23 ` [PATCH v2 1/9] arm64: perf: avoid PMXEV* indirection Julien Thierry
2019-03-25 12:36   ` liwei (GF)
2019-03-25 13:59     ` Julien Thierry
2019-03-25 12:37   ` liwei (GF)
2019-03-25 13:59     ` Julien Thierry
2019-03-26  2:10       ` liwei (GF)
2019-03-28  9:48         ` Julien Thierry
2019-03-25 13:01   ` Marc Gonzalez
2019-07-08 11:40     ` Julien Thierry
2019-03-22 16:23 ` [PATCH v2 2/9] arm64: perf: Remove PMU locking Julien Thierry
2019-03-22 16:23 ` [PATCH v2 3/9] arm: perf: save/resore pmsel Julien Thierry
2019-03-22 16:23 ` [PATCH v2 4/9] arm: perf: Remove Remove PMU locking Julien Thierry
2019-03-22 16:24 ` [PATCH v2 5/9] perf/arm_pmu: Move PMU lock to ARMv6 events Julien Thierry
2019-03-22 16:24 ` [PATCH v2 6/9] arm64: perf: Do not call irq_work_run in NMI context Julien Thierry
2019-03-22 16:24 ` [PATCH v2 7/9] arm/arm64: kvm: pmu: Make overflow handler NMI safe Julien Thierry
2019-03-22 16:24 ` [PATCH v2 8/9] arm_pmu: Introduce pmu_irq_ops Julien Thierry
2019-03-22 16:24 ` [PATCH v2 9/9] arm_pmu: Use NMIs for PMU Julien Thierry
2019-03-22 16:49 ` [PATCH v2 0/9] arm_pmu: Use NMI for perf interrupt Peter Zijlstra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).