Skip to contentSkip to conversation

ARM: perf: updates for 3.2

PATCH New Will Deacon 4 participants 19 messages Aug 8, 2011 – Aug 25, 2011 1 message not in the archive

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover ARM: perf: updates for 3.2

Hello,

This patch series contains a number of updates to the ARM PMU and perf code so that we can support mode exclusion, which is new in debug architecture 7.1 (as implemented by the Cortex-A15). Some of these updates also coincide with work to support System PMUs (PMUs that are not affine to a single CPU) but that is a larger body of work which will be posted separately at a later date.

The patches do the following:

1.) Greatly simplify the PMU reservation mechanism so that the
handling of platform_devices is moved into perf.
2.) Cleans up the interrupt registration and some of the types used
to represent events and registers.
3.) Moves event indexing to start from zero rather than one, making
the code more readable and also easier to extend for mode exclusion.
4.) Adds support for mode exclusion (user / kernel / hyp) and
implements this for Cortex-A15.

I've been running these patches since 3.0, so they've been tested on 1176, 11MPCore, Cortex-A5, Cortex-A9 and Cortex-A15 platforms.

Once again, all feedback is welcome.

Will

Cc Jean Pihet <[email protected]>
Cc Jamie Iles <[email protected]>

Mark Rutland (1):
ARM: perf: de-const struct arm_pmu

Will Deacon (9):
ARM: PMU: move CPU PMU platform device handling and init into perf
ARM: perf: use cpumask_t to record active IRQs
ARM: perf: use u32 instead of unsigned long for PMNC register
ARM: perf: use integers for ARMv7 event indices
ARM: perf: index ARMv7 event counters starting from zero
ARM: perf: index Xscale and ARMv6 event counters starting from zero
ARM: perf: index PMU registers from zero
ARM: perf: allow armpmu to implement mode exclusion
ARM: perf: add mode exclusion for Cortex-A15 PMU

 arch/arm/include/asm/pmu.h          |   39 +---
 arch/arm/kernel/perf_event.c        |  176 ++++++++++++-------
 arch/arm/kernel/perf_event_v6.c     |   16 +-
 arch/arm/kernel/perf_event_v7.c     |  327 ++++++++++++++++-------------------
 arch/arm/kernel/perf_event_xscale.c |   18 +-
 arch/arm/kernel/pmu.c               |  181 +------------------
 6 files changed, 302 insertions(+), 455 deletions(-)
#1 lore raw

PATCH 1/10 ARM: perf: de-const struct arm_pmu

From: Mark Rutland <[email protected]>

This patch removes const qualifiers from instances of struct arm_pmu, and functions initialising them, in preparation for generalising arm_pmu usage to system (AKA uncore) PMUs.

This will allow for dynamically modifiable structures (locks, struct pmu) to be added as members of struct arm_pmu.

---
 arch/arm/kernel/perf_event.c        |    2 +-
 arch/arm/kernel/perf_event_v6.c     |   12 ++++++------
 arch/arm/kernel/perf_event_v7.c     |   16 ++++++++--------
 arch/arm/kernel/perf_event_xscale.c |   12 ++++++------
 4 files changed, 21 insertions(+), 21 deletions(-)
arch/arm/kernel/perf_event.c +1 -1
index 53c9c26..7304996 100644
@@ -90,7 +90,7 @@ struct arm_pmu {
 };
 
 /* Set at runtime when we know what CPU type we are. */
-static const struct arm_pmu *armpmu;
+static struct arm_pmu *armpmu;
 
 enum arm_perf_pmu_ids
 armpmu_get_pmu_id(void)
arch/arm/kernel/perf_event_v6.c +6 -6
index dd7f3b9..d1abe97 100644
@@ -635,7 +635,7 @@ armv6mpcore_pmu_disable_event(struct hw_perf_event *hwc,
 	raw_spin_unlock_irqrestore(&pmu_lock, flags);
 }
 
-static const struct arm_pmu armv6pmu = {
+static struct arm_pmu armv6pmu = {
 	.id			= ARM_PERF_PMU_ID_V6,
 	.name			= "v6",
 	.handle_irq		= armv6pmu_handle_irq,
@@ -653,7 +653,7 @@ static const struct arm_pmu armv6pmu = {
 	.max_period		= (1LLU << 32) - 1,
 };
 
-static const struct arm_pmu *__init armv6pmu_init(void)
+static struct arm_pmu *__init armv6pmu_init(void)
 {
 	return &armv6pmu;
 }
@@ -665,7 +665,7 @@ static const struct arm_pmu *__init armv6pmu_init(void)
  * disable the interrupt reporting and update the event. When unthrottling we
  * reset the period and enable the interrupt reporting.
  */
-static const struct arm_pmu armv6mpcore_pmu = {
+static struct arm_pmu armv6mpcore_pmu = {
 	.id			= ARM_PERF_PMU_ID_V6MP,
 	.name			= "v6mpcore",
 	.handle_irq		= armv6pmu_handle_irq,
@@ -683,17 +683,17 @@ static const struct arm_pmu armv6mpcore_pmu = {
 	.max_period		= (1LLU << 32) - 1,
 };
 
-static const struct arm_pmu *__init armv6mpcore_pmu_init(void)
+static struct arm_pmu *__init armv6mpcore_pmu_init(void)
 {
 	return &armv6mpcore_pmu;
 }
 #else
-static const struct arm_pmu *__init armv6pmu_init(void)
+static struct arm_pmu *__init armv6pmu_init(void)
 {
 	return NULL;
 }
 
-static const struct arm_pmu *__init armv6mpcore_pmu_init(void)
+static struct arm_pmu *__init armv6mpcore_pmu_init(void)
 {
 	return NULL;
 }
arch/arm/kernel/perf_event_v7.c +8 -8
index 4c85183..74f9119 100644
@@ -1188,7 +1188,7 @@ static u32 __init armv7_read_num_pmnc_events(void)
 	return nb_cnt + 1;
 }
 
-static const struct arm_pmu *__init armv7_a8_pmu_init(void)
+static struct arm_pmu *__init armv7_a8_pmu_init(void)
 {
 	armv7pmu.id		= ARM_PERF_PMU_ID_CA8;
 	armv7pmu.name		= "ARMv7 Cortex-A8";
@@ -1198,7 +1198,7 @@ static const struct arm_pmu *__init armv7_a8_pmu_init(void)
 	return &armv7pmu;
 }
 
-static const struct arm_pmu *__init armv7_a9_pmu_init(void)
+static struct arm_pmu *__init armv7_a9_pmu_init(void)
 {
 	armv7pmu.id		= ARM_PERF_PMU_ID_CA9;
 	armv7pmu.name		= "ARMv7 Cortex-A9";
@@ -1208,7 +1208,7 @@ static const struct arm_pmu *__init armv7_a9_pmu_init(void)
 	return &armv7pmu;
 }
 
-static const struct arm_pmu *__init armv7_a5_pmu_init(void)
+static struct arm_pmu *__init armv7_a5_pmu_init(void)
 {
 	armv7pmu.id		= ARM_PERF_PMU_ID_CA5;
 	armv7pmu.name		= "ARMv7 Cortex-A5";
@@ -1218,7 +1218,7 @@ static const struct arm_pmu *__init armv7_a5_pmu_init(void)
 	return &armv7pmu;
 }
 
-static const struct arm_pmu *__init armv7_a15_pmu_init(void)
+static struct arm_pmu *__init armv7_a15_pmu_init(void)
 {
 	armv7pmu.id		= ARM_PERF_PMU_ID_CA15;
 	armv7pmu.name		= "ARMv7 Cortex-A15";
@@ -1228,22 +1228,22 @@ static const struct arm_pmu *__init armv7_a15_pmu_init(void)
 	return &armv7pmu;
 }
 #else
-static const struct arm_pmu *__init armv7_a8_pmu_init(void)
+static struct arm_pmu *__init armv7_a8_pmu_init(void)
 {
 	return NULL;
 }
 
-static const struct arm_pmu *__init armv7_a9_pmu_init(void)
+static struct arm_pmu *__init armv7_a9_pmu_init(void)
 {
 	return NULL;
 }
 
-static const struct arm_pmu *__init armv7_a5_pmu_init(void)
+static struct arm_pmu *__init armv7_a5_pmu_init(void)
 {
 	return NULL;
 }
 
-static const struct arm_pmu *__init armv7_a15_pmu_init(void)
+static struct arm_pmu *__init armv7_a15_pmu_init(void)
 {
 	return NULL;
 }
arch/arm/kernel/perf_event_xscale.c +6 -6
index 3c43974..730b1c2 100644
@@ -424,7 +424,7 @@ xscale1pmu_write_counter(int counter, u32 val)
 	}
 }
 
-static const struct arm_pmu xscale1pmu = {
+static struct arm_pmu xscale1pmu = {
 	.id		= ARM_PERF_PMU_ID_XSCALE1,
 	.name		= "xscale1",
 	.handle_irq	= xscale1pmu_handle_irq,
@@ -442,7 +442,7 @@ static const struct arm_pmu xscale1pmu = {
 	.max_period	= (1LLU << 32) - 1,
 };
 
-static const struct arm_pmu *__init xscale1pmu_init(void)
+static struct arm_pmu *__init xscale1pmu_init(void)
 {
 	return &xscale1pmu;
 }
@@ -786,7 +786,7 @@ xscale2pmu_write_counter(int counter, u32 val)
 	}
 }
 
-static const struct arm_pmu xscale2pmu = {
+static struct arm_pmu xscale2pmu = {
 	.id		= ARM_PERF_PMU_ID_XSCALE2,
 	.name		= "xscale2",
 	.handle_irq	= xscale2pmu_handle_irq,
@@ -804,17 +804,17 @@ static const struct arm_pmu xscale2pmu = {
 	.max_period	= (1LLU << 32) - 1,
 };
 
-static const struct arm_pmu *__init xscale2pmu_init(void)
+static struct arm_pmu *__init xscale2pmu_init(void)
 {
 	return &xscale2pmu;
 }
 #else
-static const struct arm_pmu *__init xscale1pmu_init(void)
+static struct arm_pmu *__init xscale1pmu_init(void)
 {
 	return NULL;
 }
 
-static const struct arm_pmu *__init xscale2pmu_init(void)
+static struct arm_pmu *__init xscale2pmu_init(void)
 {
 	return NULL;
 }
Signature · 1 line
1.7.0.4
#2 lore raw

PATCH 2/10 ARM: PMU: move CPU PMU platform device handling and init into perf

In reply to Will Deacon

Once upon a time, OProfile and Perf fought hard over who could play with the PMU. To stop all hell from breaking loose, pmu.c offered an internal reserve/release API and took care of parsing PMU platform data passed in from board support code.

Now that Perf has ingested OProfile, let's move the platform device handling into the Perf driver and out of the PMU locking code. Unfortunately, the lock has to remain to prevent Perf being bitten by out-of-tree modules such as LTTng, which still claim a right to the PMU when Perf isn't looking.

---
 arch/arm/include/asm/pmu.h   |   39 +++-------
 arch/arm/kernel/perf_event.c |   74 ++++++++++++++---
 arch/arm/kernel/pmu.c        |  181 ++----------------------------------------
 3 files changed, 80 insertions(+), 214 deletions(-)
arch/arm/include/asm/pmu.h +10 -29
index 67c70a3..f210708 100644
@@ -14,6 +14,10 @@
 
 #include <linux/interrupt.h>
 
+/*
+ * Types of PMUs that can be accessed directly and require mutual
+ * exclusion between profiling tools.
+ */
 enum arm_pmu_type {
 	ARM_PMU_DEVICE_CPU	= 0,
 	ARM_NUM_PMU_DEVICES,
@@ -37,54 +41,31 @@ struct arm_pmu_platdata {
  * reserve_pmu() - reserve the hardware performance counters
  *
  * Reserve the hardware performance counters in the system for exclusive use.
- * The platform_device for the system is returned on success, ERR_PTR()
- * encoded error on failure.
+ * Returns 0 on success or -EBUSY if the lock is already held.
  */
-extern struct platform_device *
+extern int
 reserve_pmu(enum arm_pmu_type device);
 
 /**
  * release_pmu() - Relinquish control of the performance counters
  *
  * Release the performance counters and allow someone else to use them.
- * Callers must have disabled the counters and released IRQs before calling
- * this. The platform_device returned from reserve_pmu() must be passed as
- * a cookie.
  */
-extern int
+extern void
 release_pmu(enum arm_pmu_type type);
 
-/**
- * init_pmu() - Initialise the PMU.
- *
- * Initialise the system ready for PMU enabling. This should typically set the
- * IRQ affinity and nothing else. The users (oprofile/perf events etc) will do
- * the actual hardware initialisation.
- */
-extern int
-init_pmu(enum arm_pmu_type device);
-
 #else /* CONFIG_CPU_HAS_PMU */
 
 #include <linux/err.h>
 
-static inline struct platform_device *
-reserve_pmu(enum arm_pmu_type device)
-{
-	return ERR_PTR(-ENODEV);
-}
-
 static inline int
-release_pmu(struct platform_device *pdev)
+reserve_pmu(enum arm_pmu_type device)
 {
 	return -ENODEV;
 }
 
-static inline int
-init_pmu(enum arm_pmu_type device)
-{
-	return -ENODEV;
-}
+static inline void
+release_pmu(struct platform_device *pdev)	{ }
 
 #endif /* CONFIG_CPU_HAS_PMU */
 
arch/arm/kernel/perf_event.c +62 -12

119 lines not on this page · raw message

arch/arm/kernel/pmu.c +8 -173

200 lines not on this page · raw message

Signature · 1 line
1.7.0.4
#3 lore raw

PATCH 3/10 ARM: perf: use cpumask_t to record active IRQs

In reply to Will Deacon

Commit 5dfc54e0 ("ARM: GIC: avoid routing interrupts to offline CPUs") prevents the GIC from setting the affinity of an IRQ to a CPU with id >= nr_cpu_ids. This was previously abused by perf on some platforms where more IRQs were registered than possible CPUs.

This patch fixes the problem by using a cpumask_t to keep track of the active (requested) interrupts in perf. The same effect could be achieved by limiting the number of IRQs to the number of CPUs, but using a mask instead will be useful for adding extended CPU hotplug support in the future.

---
 arch/arm/kernel/perf_event.c |   64 ++++++++++++++++++++---------------------
 1 files changed, 31 insertions(+), 33 deletions(-)
arch/arm/kernel/perf_event.c +31 -33
index 8514855..d507fe1 100644
@@ -69,6 +69,7 @@ static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
 
 struct arm_pmu {
 	enum arm_perf_pmu_ids id;
+	cpumask_t	active_irqs;
 	const char	*name;
 	irqreturn_t	(*handle_irq)(int irq_num, void *dev);
 	void		(*enable)(struct hw_perf_event *evt, int idx);
@@ -388,6 +389,25 @@ static irqreturn_t armpmu_platform_irq(int irq, void *dev)
 	return plat->handle_irq(irq, dev, armpmu->handle_irq);
 }
 
+static void
+armpmu_release_hardware(void)
+{
+	int i, irq, irqs;
+
+	irqs = min(pmu_device->num_resources, num_possible_cpus());
+
+	for (i = 0; i < irqs; ++i) {
+		if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs))
+			continue;
+		irq = platform_get_irq(pmu_device, i);
+		if (irq >= 0)
+			free_irq(irq, NULL);
+	}
+
+	armpmu->stop();
+	release_pmu(ARM_PMU_DEVICE_CPU);
+}
+
 static int
 armpmu_reserve_hardware(void)
 {
@@ -401,20 +421,20 @@ armpmu_reserve_hardware(void)
 		return err;
 	}
 
-	irqs = pmu_device->num_resources;
-
 	plat = dev_get_platdata(&pmu_device->dev);
 	if (plat && plat->handle_irq)
 		handle_irq = armpmu_platform_irq;
 	else
 		handle_irq = armpmu->handle_irq;
 
+	irqs = min(pmu_device->num_resources, num_possible_cpus());
 	if (irqs < 1) {
 		pr_err("no irqs for PMUs defined\n");
 		return -ENODEV;
 	}
 
 	for (i = 0; i < irqs; ++i) {
+		err = 0;
 		irq = platform_get_irq(pmu_device, i);
 		if (irq < 0)
 			continue;
@@ -422,13 +442,12 @@ armpmu_reserve_hardware(void)
 		/*
 		 * If we have a single PMU interrupt that we can't shift,
 		 * assume that we're running on a uniprocessor machine and
-		 * continue.
+		 * continue. Otherwise, continue without this interrupt.
 		 */
-		err = irq_set_affinity(irq, cpumask_of(i));
-		if (err && irqs > 1) {
-			pr_err("unable to set irq affinity (irq=%d, cpu=%u)\n",
-				irq, i);
-			break;
+		if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) {
+			pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n",
+				    irq, i);
+			continue;
 		}
 
 		err = request_irq(irq, handle_irq,
@@ -437,35 +456,14 @@ armpmu_reserve_hardware(void)
 		if (err) {
 			pr_err("unable to request IRQ%d for ARM PMU counters\n",
 				irq);
-			break;
+			armpmu_release_hardware();
+			return err;
 		}
-	}
 
-	if (err) {
-		for (i = i - 1; i >= 0; --i) {
-			irq = platform_get_irq(pmu_device, i);
-			if (irq >= 0)
-				free_irq(irq, NULL);
-		}
-		release_pmu(ARM_PMU_DEVICE_CPU);
+		cpumask_set_cpu(i, &armpmu->active_irqs);
 	}
 
-	return err;
-}
-
-static void
-armpmu_release_hardware(void)
-{
-	int i, irq;
-
-	for (i = pmu_device->num_resources - 1; i >= 0; --i) {
-		irq = platform_get_irq(pmu_device, i);
-		if (irq >= 0)
-			free_irq(irq, NULL);
-	}
-	armpmu->stop();
-
-	release_pmu(ARM_PMU_DEVICE_CPU);
+	return 0;
 }
 
 static atomic_t active_events = ATOMIC_INIT(0);
Signature · 1 line
1.7.0.4
#4 lore raw

PATCH 4/10 ARM: perf: use u32 instead of unsigned long for PMNC register

In reply to Will Deacon

The ARMv7 perf backend mixes up u32 and unsigned long, which is rather ugly.

This patch makes the ARMv7 PMU code consistently use the u32 type instead.

---
 arch/arm/kernel/perf_event_v7.c |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)
arch/arm/kernel/perf_event_v7.c +6 -6
index 74f9119..f4f260d 100644
@@ -758,26 +758,26 @@ enum armv7_counters {
 #define	ARMV7_FLAG_MASK		0xffffffff	/* Mask for writable bits */
 #define	ARMV7_OVERFLOWED_MASK	ARMV7_FLAG_MASK
 
-static inline unsigned long armv7_pmnc_read(void)
+static inline u32 armv7_pmnc_read(void)
 {
 	u32 val;
 	asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r"(val));
 	return val;
 }
 
-static inline void armv7_pmnc_write(unsigned long val)
+static inline void armv7_pmnc_write(u32 val)
 {
 	val &= ARMV7_PMNC_MASK;
 	isb();
 	asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r"(val));
 }
 
-static inline int armv7_pmnc_has_overflowed(unsigned long pmnc)
+static inline int armv7_pmnc_has_overflowed(u32 pmnc)
 {
 	return pmnc & ARMV7_OVERFLOWED_MASK;
 }
 
-static inline int armv7_pmnc_counter_has_overflowed(unsigned long pmnc,
+static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc,
 					enum armv7_counters counter)
 {
 	int ret = 0;
@@ -812,7 +812,7 @@ static inline int armv7_pmnc_select_counter(unsigned int idx)
 
 static inline u32 armv7pmu_read_counter(int idx)
 {
-	unsigned long value = 0;
+	u32 value = 0;
 
 	if (idx == ARMV7_CYCLE_COUNTER)
 		asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value));
@@ -1044,7 +1044,7 @@ static void armv7pmu_disable_event(struct hw_perf_event *hwc, int idx)
 
 static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
 {
-	unsigned long pmnc;
+	u32 pmnc;
 	struct perf_sample_data data;
 	struct cpu_hw_events *cpuc;
 	struct pt_regs *regs;
Signature · 1 line
1.7.0.4
#5 lore raw

PATCH 5/10 ARM: perf: use integers for ARMv7 event indices

In reply to Will Deacon

This patch ensures that integers are used to represent event indices in the ARMv7 PMU backend. This ensures consistency between functions and also with the arm_pmu structure.

---
 arch/arm/kernel/perf_event_v7.c |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)
arch/arm/kernel/perf_event_v7.c +6 -6
index f4f260d..e39bc89 100644
@@ -793,7 +793,7 @@ static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc,
 	return ret;
 }
 
-static inline int armv7_pmnc_select_counter(unsigned int idx)
+static inline int armv7_pmnc_select_counter(int idx)
 {
 	u32 val;
 
@@ -840,7 +840,7 @@ static inline void armv7pmu_write_counter(int idx, u32 value)
 			smp_processor_id(), idx);
 }
 
-static inline void armv7_pmnc_write_evtsel(unsigned int idx, u32 val)
+static inline void armv7_pmnc_write_evtsel(int idx, u32 val)
 {
 	if (armv7_pmnc_select_counter(idx) == idx) {
 		val &= ARMV7_EVTSEL_MASK;
@@ -848,7 +848,7 @@ static inline void armv7_pmnc_write_evtsel(unsigned int idx, u32 val)
 	}
 }
 
-static inline u32 armv7_pmnc_enable_counter(unsigned int idx)
+static inline int armv7_pmnc_enable_counter(int idx)
 {
 	u32 val;
 
@@ -869,7 +869,7 @@ static inline u32 armv7_pmnc_enable_counter(unsigned int idx)
 	return idx;
 }
 
-static inline u32 armv7_pmnc_disable_counter(unsigned int idx)
+static inline int armv7_pmnc_disable_counter(int idx)
 {
 	u32 val;
 
@@ -891,7 +891,7 @@ static inline u32 armv7_pmnc_disable_counter(unsigned int idx)
 	return idx;
 }
 
-static inline u32 armv7_pmnc_enable_intens(unsigned int idx)
+static inline int armv7_pmnc_enable_intens(int idx)
 {
 	u32 val;
 
@@ -912,7 +912,7 @@ static inline u32 armv7_pmnc_enable_intens(unsigned int idx)
 	return idx;
 }
 
-static inline u32 armv7_pmnc_disable_intens(unsigned int idx)
+static inline int armv7_pmnc_disable_intens(int idx)
 {
 	u32 val;
 
Signature · 1 line
1.7.0.4
#6 lore raw

PATCH 6/10 ARM: perf: index ARMv7 event counters starting from zero

In reply to Will Deacon

The current ARMv7 PMU backend indexes event counters from two, with index zero being reserved and index one being used to represent the cycle counter.

This patch tidies up the code by indexing from one instead (with zero for the cycle counter). This allows us to remove many of the accessor macros along with the counter enumeration and makes the code much more readable.

---
 arch/arm/kernel/perf_event_v7.c |  239 ++++++++++++++------------------------
 1 files changed, 88 insertions(+), 151 deletions(-)
arch/arm/kernel/perf_event_v7.c +88 -151
index e39bc89..0934c82 100644
@@ -676,23 +676,24 @@ static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 };
 
 /*
- * Perf Events counters
+ * Perf Events' indices
  */
-enum armv7_counters {
-	ARMV7_CYCLE_COUNTER		= 1,	/* Cycle counter */
-	ARMV7_COUNTER0			= 2,	/* First event counter */
-};
+#define	ARMV7_IDX_CYCLE_COUNTER	0
+#define	ARMV7_IDX_COUNTER0	1
+#define	ARMV7_IDX_COUNTER_LAST	(ARMV7_IDX_CYCLE_COUNTER + armpmu->num_events - 1)
+
+#define	ARMV7_MAX_COUNTERS	32
+#define	ARMV7_COUNTER_MASK	(ARMV7_MAX_COUNTERS - 1)
 
 /*
- * The cycle counter is ARMV7_CYCLE_COUNTER.
- * The first event counter is ARMV7_COUNTER0.
- * The last event counter is (ARMV7_COUNTER0 + armpmu->num_events - 1).
+ * ARMv7 low level PMNC access
  */
-#define	ARMV7_COUNTER_LAST	(ARMV7_COUNTER0 + armpmu->num_events - 1)
 
 /*
- * ARMv7 low level PMNC access
+ * Perf Event to low level counters mapping
  */
+#define	ARMV7_IDX_TO_COUNTER(x)	\
+	(((x) - ARMV7_IDX_COUNTER0) & ARMV7_COUNTER_MASK)
 
 /*
  * Per-CPU PMNC: config reg
@@ -708,53 +709,13 @@ enum armv7_counters {
 #define	ARMV7_PMNC_MASK		0x3f	 /* Mask for writable bits */
 
 /*
- * Available counters
- */
-#define ARMV7_CNT0		0	/* First event counter */
-#define ARMV7_CCNT		31	/* Cycle counter */
-
-/* Perf Event to low level counters mapping */
-#define ARMV7_EVENT_CNT_TO_CNTx	(ARMV7_COUNTER0 - ARMV7_CNT0)
-
-/*
- * CNTENS: counters enable reg
- */
-#define ARMV7_CNTENS_P(idx)	(1 << (idx - ARMV7_EVENT_CNT_TO_CNTx))
-#define ARMV7_CNTENS_C		(1 << ARMV7_CCNT)
-
-/*
- * CNTENC: counters disable reg
- */
-#define ARMV7_CNTENC_P(idx)	(1 << (idx - ARMV7_EVENT_CNT_TO_CNTx))
-#define ARMV7_CNTENC_C		(1 << ARMV7_CCNT)
-
-/*
- * INTENS: counters overflow interrupt enable reg
- */
-#define ARMV7_INTENS_P(idx)	(1 << (idx - ARMV7_EVENT_CNT_TO_CNTx))
-#define ARMV7_INTENS_C		(1 << ARMV7_CCNT)
-
-/*
- * INTENC: counters overflow interrupt disable reg
- */
-#define ARMV7_INTENC_P(idx)	(1 << (idx - ARMV7_EVENT_CNT_TO_CNTx))
-#define ARMV7_INTENC_C		(1 << ARMV7_CCNT)
-
-/*
  * EVTSEL: Event selection reg
  */
 #define	ARMV7_EVTSEL_MASK	0xff		/* Mask for writable bits */
 
 /*
- * SELECT: Counter selection reg
- */
-#define	ARMV7_SELECT_MASK	0x1f		/* Mask for writable bits */
-
-/*
  * FLAG: counters overflow flag status reg
  */
-#define ARMV7_FLAG_P(idx)	(1 << (idx - ARMV7_EVENT_CNT_TO_CNTx))
-#define ARMV7_FLAG_C		(1 << ARMV7_CCNT)
 #define	ARMV7_FLAG_MASK		0xffffffff	/* Mask for writable bits */
 #define	ARMV7_OVERFLOWED_MASK	ARMV7_FLAG_MASK
 
@@ -777,34 +738,39 @@ static inline int armv7_pmnc_has_overflowed(u32 pmnc)
 	return pmnc & ARMV7_OVERFLOWED_MASK;
 }
 
-static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc,
-					enum armv7_counters counter)
+static inline int armv7_pmnc_counter_valid(int idx)
+{
+	return idx >= ARMV7_IDX_CYCLE_COUNTER && idx <= ARMV7_IDX_COUNTER_LAST;
+}
+
+static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, int idx)
 {
 	int ret = 0;
+	u32 counter;
 
-	if (counter == ARMV7_CYCLE_COUNTER)
-		ret = pmnc & ARMV7_FLAG_C;
-	else if ((counter >= ARMV7_COUNTER0) && (counter <= ARMV7_COUNTER_LAST))
-		ret = pmnc & ARMV7_FLAG_P(counter);
-	else
+	if (!armv7_pmnc_counter_valid(idx)) {
 		pr_err("CPU%u checking wrong counter %d overflow status\n",
-			smp_processor_id(), counter);
+			smp_processor_id(), idx);
+	} else {
+		counter = ARMV7_IDX_TO_COUNTER(idx);
+		ret = pmnc & BIT(counter);
+	}
 
 	return ret;
 }
 
 static inline int armv7_pmnc_select_counter(int idx)
 {
-	u32 val;
+	u32 counter;
 
-	if ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST)) {
-		pr_err("CPU%u selecting wrong PMNC counter"
-			" %d\n", smp_processor_id(), idx);
-		return -1;
+	if (!armv7_pmnc_counter_valid(idx)) {
+		pr_err("CPU%u selecting wrong PMNC counter %d\n",
+			smp_processor_id(), idx);
+		return -EINVAL;
 	}
 
-	val = (idx - ARMV7_EVENT_CNT_TO_CNTx) & ARMV7_SELECT_MASK;
-	asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
+	counter = ARMV7_IDX_TO_COUNTER(idx);
+	asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter));
 	isb();
 
 	return idx;
@@ -814,30 +780,26 @@ static inline u32 armv7pmu_read_counter(int idx)
 {
 	u32 value = 0;
 
-	if (idx == ARMV7_CYCLE_COUNTER)
-		asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value));
-	else if ((idx >= ARMV7_COUNTER0) && (idx <= ARMV7_COUNTER_LAST)) {
-		if (armv7_pmnc_select_counter(idx) == idx)
-			asm volatile("mrc p15, 0, %0, c9, c13, 2"
-				     : "=r" (value));
-	} else
+	if (!armv7_pmnc_counter_valid(idx))
 		pr_err("CPU%u reading wrong counter %d\n",
 			smp_processor_id(), idx);
+	else if (idx == ARMV7_IDX_CYCLE_COUNTER)
+		asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value));
+	else if (armv7_pmnc_select_counter(idx) == idx)
+		asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value));
 
 	return value;
 }
 
 static inline void armv7pmu_write_counter(int idx, u32 value)
 {
-	if (idx == ARMV7_CYCLE_COUNTER)
-		asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value));
-	else if ((idx >= ARMV7_COUNTER0) && (idx <= ARMV7_COUNTER_LAST)) {
-		if (armv7_pmnc_select_counter(idx) == idx)
-			asm volatile("mcr p15, 0, %0, c9, c13, 2"
-				     : : "r" (value));
-	} else
+	if (!armv7_pmnc_counter_valid(idx))
 		pr_err("CPU%u writing wrong counter %d\n",
 			smp_processor_id(), idx);
+	else if (idx == ARMV7_IDX_CYCLE_COUNTER)
+		asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value));
+	else if (armv7_pmnc_select_counter(idx) == idx)
+		asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (value));
 }
 
 static inline void armv7_pmnc_write_evtsel(int idx, u32 val)
@@ -850,86 +812,61 @@ static inline void armv7_pmnc_write_evtsel(int idx, u32 val)
 
 static inline int armv7_pmnc_enable_counter(int idx)
 {
-	u32 val;
+	u32 counter;
 
-	if ((idx != ARMV7_CYCLE_COUNTER) &&
-	    ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) {
-		pr_err("CPU%u enabling wrong PMNC counter"
-			" %d\n", smp_processor_id(), idx);
-		return -1;
+	if (!armv7_pmnc_counter_valid(idx)) {
+		pr_err("CPU%u enabling wrong PMNC counter %d\n",
+			smp_processor_id(), idx);
+		return -EINVAL;
 	}
 
-	if (idx == ARMV7_CYCLE_COUNTER)
-		val = ARMV7_CNTENS_C;
-	else
-		val = ARMV7_CNTENS_P(idx);
-
-	asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
-
+	counter = ARMV7_IDX_TO_COUNTER(idx);
+	asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (BIT(counter)));
 	return idx;
 }
 
 static inline int armv7_pmnc_disable_counter(int idx)
 {
-	u32 val;
+	u32 counter;
 
-
-	if ((idx != ARMV7_CYCLE_COUNTER) &&
-	    ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) {
-		pr_err("CPU%u disabling wrong PMNC counter"
-			" %d\n", smp_processor_id(), idx);
-		return -1;
+	if (!armv7_pmnc_counter_valid(idx)) {
+		pr_err("CPU%u disabling wrong PMNC counter %d\n",
+			smp_processor_id(), idx);
+		return -EINVAL;
 	}
 
-	if (idx == ARMV7_CYCLE_COUNTER)
-		val = ARMV7_CNTENC_C;
-	else
-		val = ARMV7_CNTENC_P(idx);
-
-	asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
-
+	counter = ARMV7_IDX_TO_COUNTER(idx);
+	asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (BIT(counter)));
 	return idx;
 }
 
 static inline int armv7_pmnc_enable_intens(int idx)
 {
-	u32 val;
+	u32 counter;
 
-	if ((idx != ARMV7_CYCLE_COUNTER) &&
-	    ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) {
-		pr_err("CPU%u enabling wrong PMNC counter"
-			" interrupt enable %d\n", smp_processor_id(), idx);
-		return -1;
+	if (!armv7_pmnc_counter_valid(idx)) {
+		pr_err("CPU%u enabling wrong PMNC counter IRQ enable %d\n",
+			smp_processor_id(), idx);
+		return -EINVAL;
 	}
 
-	if (idx == ARMV7_CYCLE_COUNTER)
-		val = ARMV7_INTENS_C;
-	else
-		val = ARMV7_INTENS_P(idx);
-
-	asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
-
+	counter = ARMV7_IDX_TO_COUNTER(idx);
+	asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (BIT(counter)));
 	return idx;
 }
 
 static inline int armv7_pmnc_disable_intens(int idx)
 {
-	u32 val;
+	u32 counter;
 
-	if ((idx != ARMV7_CYCLE_COUNTER) &&
-	    ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) {
-		pr_err("CPU%u disabling wrong PMNC counter"
-			" interrupt enable %d\n", smp_processor_id(), idx);
-		return -1;
+	if (!armv7_pmnc_counter_valid(idx)) {
+		pr_err("CPU%u disabling wrong PMNC counter IRQ enable %d\n",
+			smp_processor_id(), idx);
+		return -EINVAL;
 	}
 
-	if (idx == ARMV7_CYCLE_COUNTER)
-		val = ARMV7_INTENC_C;
-	else
-		val = ARMV7_INTENC_P(idx);
-
-	asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val));
-
+	counter = ARMV7_IDX_TO_COUNTER(idx);
+	asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (BIT(counter)));
 	return idx;
 }
 
@@ -973,14 +910,14 @@ static void armv7_pmnc_dump_regs(void)
 	asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
 	printk(KERN_INFO "CCNT  =0x%08x\n", val);
 
-	for (cnt = ARMV7_COUNTER0; cnt < ARMV7_COUNTER_LAST; cnt++) {
+	for (cnt = ARMV7_IDX_COUNTER0; cnt <= ARMV7_IDX_COUNTER_LAST; cnt++) {
 		armv7_pmnc_select_counter(cnt);
 		asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
 		printk(KERN_INFO "CNT[%d] count =0x%08x\n",
-			cnt-ARMV7_EVENT_CNT_TO_CNTx, val);
+			ARMV7_IDX_TO_COUNTER(cnt), val);
 		asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
 		printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n",
-			cnt-ARMV7_EVENT_CNT_TO_CNTx, val);
+			ARMV7_IDX_TO_COUNTER(cnt), val);
 	}
 }
 #endif
@@ -1004,7 +941,7 @@ static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx)
 	 * Set event (if destined for PMNx counters)
 	 * We don't need to set the event if it's a cycle count
 	 */
-	if (idx != ARMV7_CYCLE_COUNTER)
+	if (idx != ARMV7_IDX_CYCLE_COUNTER)
 		armv7_pmnc_write_evtsel(idx, hwc->config_base);
 
 	/*
@@ -1069,7 +1006,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
 	perf_sample_data_init(&data, 0);
 
 	cpuc = &__get_cpu_var(cpu_hw_events);
-	for (idx = 0; idx <= armpmu->num_events; ++idx) {
+	for (idx = 0; idx < armpmu->num_events; ++idx) {
 		struct perf_event *event = cpuc->events[idx];
 		struct hw_perf_event *hwc;
 
@@ -1132,23 +1069,23 @@ static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc,
 
 	/* Always place a cycle counter into the cycle counter. */
 	if (event->config_base == ARMV7_PERFCTR_CPU_CYCLES) {
-		if (test_and_set_bit(ARMV7_CYCLE_COUNTER, cpuc->used_mask))
+		if (test_and_set_bit(ARMV7_IDX_CYCLE_COUNTER, cpuc->used_mask))
 			return -EAGAIN;
 
-		return ARMV7_CYCLE_COUNTER;
-	} else {
-		/*
-		 * For anything other than a cycle counter, try and use
-		 * the events counters
-		 */
-		for (idx = ARMV7_COUNTER0; idx <= armpmu->num_events; ++idx) {
-			if (!test_and_set_bit(idx, cpuc->used_mask))
-				return idx;
-		}
+		return ARMV7_IDX_CYCLE_COUNTER;
+	}
 
-		/* The counters are all in use. */
-		return -EAGAIN;
+	/*
+	 * For anything other than a cycle counter, try and use
+	 * the events counters
+	 */
+	for (idx = ARMV7_IDX_COUNTER0; idx < armpmu->num_events; ++idx) {
+		if (!test_and_set_bit(idx, cpuc->used_mask))
+			return idx;
 	}
+
+	/* The counters are all in use. */
+	return -EAGAIN;
 }
 
 static void armv7pmu_reset(void *info)
@@ -1156,7 +1093,7 @@ static void armv7pmu_reset(void *info)
 	u32 idx, nb_cnt = armpmu->num_events;
 
 	/* The counter and interrupt enable registers are unknown at reset. */
-	for (idx = 1; idx < nb_cnt; ++idx)
+	for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx)
 		armv7pmu_disable_event(NULL, idx);
 
 	/* Initialize & Reset PMNC: C and P bits */
Signature · 1 line
1.7.0.4
#7 lore raw

PATCH 7/10 ARM: perf: index Xscale and ARMv6 event counters starting from zero

In reply to Will Deacon

Now that the ARMv7 PMU backend indexes event counters from zero, follow suit and do the same for ARMv6 and Xscale.

---
 arch/arm/kernel/perf_event_v6.c     |    4 ++--
 arch/arm/kernel/perf_event_xscale.c |    6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)
arch/arm/kernel/perf_event_v6.c +2 -2
index d1abe97..87f29b5 100644
@@ -54,7 +54,7 @@ enum armv6_perf_types {
 };
 
 enum armv6_counters {
-	ARMV6_CYCLE_COUNTER = 1,
+	ARMV6_CYCLE_COUNTER = 0,
 	ARMV6_COUNTER0,
 	ARMV6_COUNTER1,
 };
@@ -487,7 +487,7 @@ armv6pmu_handle_irq(int irq_num,
 	perf_sample_data_init(&data, 0);
 
 	cpuc = &__get_cpu_var(cpu_hw_events);
-	for (idx = 0; idx <= armpmu->num_events; ++idx) {
+	for (idx = 0; idx < armpmu->num_events; ++idx) {
 		struct perf_event *event = cpuc->events[idx];
 		struct hw_perf_event *hwc;
 
arch/arm/kernel/perf_event_xscale.c +3 -3
index 730b1c2..54312fc 100644
@@ -40,7 +40,7 @@ enum xscale_perf_types {
 };
 
 enum xscale_counters {
-	XSCALE_CYCLE_COUNTER	= 1,
+	XSCALE_CYCLE_COUNTER	= 0,
 	XSCALE_COUNTER0,
 	XSCALE_COUNTER1,
 	XSCALE_COUNTER2,
@@ -249,7 +249,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev)
 	perf_sample_data_init(&data, 0);
 
 	cpuc = &__get_cpu_var(cpu_hw_events);
-	for (idx = 0; idx <= armpmu->num_events; ++idx) {
+	for (idx = 0; idx < armpmu->num_events; ++idx) {
 		struct perf_event *event = cpuc->events[idx];
 		struct hw_perf_event *hwc;
 
@@ -581,7 +581,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev)
 	perf_sample_data_init(&data, 0);
 
 	cpuc = &__get_cpu_var(cpu_hw_events);
-	for (idx = 0; idx <= armpmu->num_events; ++idx) {
+	for (idx = 0; idx < armpmu->num_events; ++idx) {
 		struct perf_event *event = cpuc->events[idx];
 		struct hw_perf_event *hwc;
 
Signature · 1 line
1.7.0.4
#8 lore raw

PATCH 8/10 ARM: perf: index PMU registers from zero

In reply to Will Deacon

ARM PMU code used to use 1-based indices for PMU registers. This caused several data structures (pmu_hw_events::{active_events, used_mask, events}) to have an unused element at index zero. ARMPMU_MAX_HWEVENTS still takes this indexing into account, and currently equates to 33.

This patch updates the core ARM perf code to use the 0th index again.

---
 arch/arm/kernel/perf_event.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)
arch/arm/kernel/perf_event.c +3 -3
index d507fe1..00e57f1 100644
@@ -35,7 +35,7 @@ static struct platform_device *pmu_device;
 static DEFINE_RAW_SPINLOCK(pmu_lock);
 
 /*
- * ARMv6 supports a maximum of 3 events, starting from index 1. If we add
+ * ARMv6 supports a maximum of 3 events, starting from index 0. If we add
  * another platform that supports more, we need to increase this to be the
  * largest of all platforms.
  *
@@ -43,7 +43,7 @@ static DEFINE_RAW_SPINLOCK(pmu_lock);
  *  cycle counter CCNT + 31 events counters CNT0..30.
  *  Cortex-A8 has 1+4 counters, Cortex-A9 has 1+6 counters.
  */
-#define ARMPMU_MAX_HWEVENTS		33
+#define ARMPMU_MAX_HWEVENTS		32
 
 /* The events for a given CPU. */
 struct cpu_hw_events {
@@ -597,7 +597,7 @@ static void armpmu_enable(struct pmu *pmu)
 	if (!armpmu)
 		return;
 
-	for (idx = 0; idx <= armpmu->num_events; ++idx) {
+	for (idx = 0; idx < armpmu->num_events; ++idx) {
 		struct perf_event *event = cpuc->events[idx];
 
 		if (!event)
Signature · 1 line
1.7.0.4
#9 lore raw

PATCH 9/10 ARM: perf: allow armpmu to implement mode exclusion

In reply to Will Deacon

Modern PMUs allow for mode exclusion, so we no longer wish to return -EPERM if it is requested.

This patch provides a hook in the armpmu structure for implementing mode exclusion. The hw_perf_event initialisation is slightly delayed so that the backend code can update the structure if required.

---
 arch/arm/kernel/perf_event.c |   44 +++++++++++++++++++++++------------------
 1 files changed, 25 insertions(+), 19 deletions(-)
arch/arm/kernel/perf_event.c +25 -19
index 00e57f1..8a5e2ac 100644
@@ -76,6 +76,8 @@ struct arm_pmu {
 	void		(*disable)(struct hw_perf_event *evt, int idx);
 	int		(*get_event_idx)(struct cpu_hw_events *cpuc,
 					 struct hw_perf_event *hwc);
+	int		(*set_event_filter)(struct hw_perf_event *evt,
+					    struct perf_event_attr *attr);
 	u32		(*read_counter)(int idx);
 	void		(*write_counter)(int idx, u32 val);
 	void		(*start)(void);
@@ -479,6 +481,13 @@ hw_perf_event_destroy(struct perf_event *event)
 }
 
 static int
+event_requires_mode_exclusion(struct perf_event_attr *attr)
+{
+	return attr->exclude_idle || attr->exclude_user ||
+	       attr->exclude_kernel || attr->exclude_hv;
+}
+
+static int
 __hw_perf_event_init(struct perf_event *event)
 {
 	struct hw_perf_event *hwc = &event->hw;
@@ -503,34 +512,31 @@ __hw_perf_event_init(struct perf_event *event)
 	}
 
 	/*
+	 * We don't assign an index until we actually place the event onto
+	 * hardware. Use -1 to signify that we haven't decided where to put it
+	 * yet. For SMP systems, each core has it's own PMU so we can't do any
+	 * clever allocation or constraints checking at this point.
+	 */
+	hwc->idx		= -1;
+	hwc->config_base	= 0;
+	hwc->config		= 0;
+	hwc->event_base		= 0;
+
+	/*
 	 * Check whether we need to exclude the counter from certain modes.
-	 * The ARM performance counters are on all of the time so if someone
-	 * has asked us for some excludes then we have to fail.
 	 */
-	if (event->attr.exclude_kernel || event->attr.exclude_user ||
-	    event->attr.exclude_hv || event->attr.exclude_idle) {
+	if ((!armpmu->set_event_filter ||
+	     armpmu->set_event_filter(hwc, &event->attr)) &&
+	     event_requires_mode_exclusion(&event->attr)) {
 		pr_debug("ARM performance counters do not support "
 			 "mode exclusion\n");
 		return -EPERM;
 	}
 
 	/*
-	 * We don't assign an index until we actually place the event onto
-	 * hardware. Use -1 to signify that we haven't decided where to put it
-	 * yet. For SMP systems, each core has it's own PMU so we can't do any
-	 * clever allocation or constraints checking at this point.
-	 */
-	hwc->idx = -1;
-
-	/*
-	 * Store the event encoding into the config_base field. config and
-	 * event_base are unused as the only 2 things we need to know are
-	 * the event mapping and the counter to use. The counter to use is
-	 * also the indx and the config_base is the event type.
+	 * Store the event encoding into the config_base field.
 	 */
-	hwc->config_base	    = (unsigned long)mapping;
-	hwc->config		    = 0;
-	hwc->event_base		    = 0;
+	hwc->config_base	    |= (unsigned long)mapping;
 
 	if (!hwc->sample_period) {
 		hwc->sample_period  = armpmu->max_period;
Signature · 1 line
1.7.0.4
#10 lore raw

PATCH 10/10 ARM: perf: add mode exclusion for Cortex-A15 PMU

In reply to Will Deacon

The Cortex-A15 PMU implements the PMUv2 specification and therefore has support for some mode exclusion.

This patch adds support for excluding user, kernel and hypervisor counts from a given event.

---
 arch/arm/kernel/perf_event_v7.c |   58 +++++++++++++++++++++++++++++++++------
 1 files changed, 49 insertions(+), 9 deletions(-)
arch/arm/kernel/perf_event_v7.c +49 -9
index 0934c82..fe6c931 100644
@@ -17,6 +17,9 @@
  */
 
 #ifdef CONFIG_CPU_V7
+
+static struct arm_pmu armv7pmu;
+
 /*
  * Common ARMv7 event types
  *
@@ -709,16 +712,24 @@ static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 #define	ARMV7_PMNC_MASK		0x3f	 /* Mask for writable bits */
 
 /*
- * EVTSEL: Event selection reg
- */
-#define	ARMV7_EVTSEL_MASK	0xff		/* Mask for writable bits */
-
-/*
  * FLAG: counters overflow flag status reg
  */
 #define	ARMV7_FLAG_MASK		0xffffffff	/* Mask for writable bits */
 #define	ARMV7_OVERFLOWED_MASK	ARMV7_FLAG_MASK
 
+/*
+ * PMXEVTYPER: Event selection reg
+ */
+#define	ARMV7_EVTYPE_MASK	0xc00000ff	/* Mask for writable bits */
+#define	ARMV7_EVTYPE_EVENT	0xff		/* Mask for EVENT bits */
+
+/*
+ * Event filters for PMUv2
+ */
+#define	ARMV7_EXCLUDE_PL1	(1 << 31)
+#define	ARMV7_EXCLUDE_USER	(1 << 30)
+#define	ARMV7_INCLUDE_HYP	(1 << 27)
+
 static inline u32 armv7_pmnc_read(void)
 {
 	u32 val;
@@ -805,7 +816,7 @@ static inline void armv7pmu_write_counter(int idx, u32 value)
 static inline void armv7_pmnc_write_evtsel(int idx, u32 val)
 {
 	if (armv7_pmnc_select_counter(idx) == idx) {
-		val &= ARMV7_EVTSEL_MASK;
+		val &= ARMV7_EVTYPE_MASK;
 		asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
 	}
 }
@@ -939,9 +950,10 @@ static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx)
 
 	/*
 	 * Set event (if destined for PMNx counters)
-	 * We don't need to set the event if it's a cycle count
+	 * We only need to set the event for the cycle counter if we
+	 * have the ability to perform event filtering.
 	 */
-	if (idx != ARMV7_IDX_CYCLE_COUNTER)
+	if (armv7pmu.set_event_filter || idx != ARMV7_IDX_CYCLE_COUNTER)
 		armv7_pmnc_write_evtsel(idx, hwc->config_base);
 
 	/*
@@ -1066,9 +1078,10 @@ static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc,
 				  struct hw_perf_event *event)
 {
 	int idx;
+	unsigned long evtype = event->config_base & ARMV7_EVTYPE_EVENT;
 
 	/* Always place a cycle counter into the cycle counter. */
-	if (event->config_base == ARMV7_PERFCTR_CPU_CYCLES) {
+	if (evtype == ARMV7_PERFCTR_CPU_CYCLES) {
 		if (test_and_set_bit(ARMV7_IDX_CYCLE_COUNTER, cpuc->used_mask))
 			return -EAGAIN;
 
@@ -1088,6 +1101,32 @@ static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc,
 	return -EAGAIN;
 }
 
+/*
+ * Add an event filter to a given event. This will only work for PMUv2 PMUs.
+ */
+static int armv7pmu_set_event_filter(struct hw_perf_event *event,
+				     struct perf_event_attr *attr)
+{
+	unsigned long config_base = 0;
+
+	if (attr->exclude_idle)
+		return -EPERM;
+	if (attr->exclude_user)
+		config_base |= ARMV7_EXCLUDE_USER;
+	if (attr->exclude_kernel)
+		config_base |= ARMV7_EXCLUDE_PL1;
+	if (!attr->exclude_hv)
+		config_base |= ARMV7_INCLUDE_HYP;
+
+	/*
+	 * Install the filter into config_base as this is used to
+	 * construct the event type.
+	 */
+	event->config_base = config_base;
+
+	return 0;
+}
+
 static void armv7pmu_reset(void *info)
 {
 	u32 idx, nb_cnt = armpmu->num_events;
@@ -1162,6 +1201,7 @@ static struct arm_pmu *__init armv7_a15_pmu_init(void)
 	armv7pmu.cache_map	= &armv7_a15_perf_cache_map;
 	armv7pmu.event_map	= &armv7_a15_perf_map;
 	armv7pmu.num_events	= armv7_read_num_pmnc_events();
+	armv7pmu.set_event_filter = armv7pmu_set_event_filter;
 	return &armv7pmu;
 }
 #else
Signature · 1 line
1.7.0.4

A message that is not in the archive (sent off-list or not yet received): CAKHPGBZ1eXzhsjFvLt_KTYKsR_OH7rgbeSGedTY5g8dhi90uzQ@mail.gmail.com

#12 lore raw

PATCH 10/10 ARM: perf: add mode exclusion for Cortex-A15 PMU

Hey Will,

Quoted 10 lines

From: Will Deacon <[email protected]>

The Cortex-A15 PMU implements the PMUv2 specification and therefore has support for some mode exclusion.

This patch adds support for excluding user, kernel and hypervisor counts from a given event.

---

--8<---

arch/arm/kernel/perf_event_v7.c static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] · line 732

Quoted 8 lines
arch/arm/kernel/perf_event_v7.c +8 -0
+
+/*
+ * Event filters for PMUv2
+ */
+#define        ARMV7_EXCLUDE_PL1       (1 << 31)
+#define        ARMV7_EXCLUDE_USER      (1 << 30)
+#define        ARMV7_INCLUDE_HYP       (1 << 27)
+

This mode exclusion stuff is confusing me.
For exclude user mode, shouldn't PMXEVTYPER[PL1,U] = 0b11

With this patch the counters will spin with secure mode activity as well, if exclude user mode is selected ?

/me goes to bother the h/w guys.

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#13 lore raw

PATCH 10/10 ARM: perf: add mode exclusion for Cortex-A15 PMU

Quoted 1 line · Ashwin Chaugule

Hey Will,

Hi Ashwin,

Quoted 14 lines
Quoted 10 lines

From: Will Deacon <[email protected]>

The Cortex-A15 PMU implements the PMUv2 specification and therefore has support for some mode exclusion.

This patch adds support for excluding user, kernel and hypervisor counts from a given event.

---

--8<---

Quoted 10 lines

arch/arm/kernel/perf_event_v7.c static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] · line 732

Quoted 8 lines
arch/arm/kernel/perf_event_v7.c +8 -0
+
+/*
+ * Event filters for PMUv2
+ */
+#define        ARMV7_EXCLUDE_PL1       (1 << 31)
+#define        ARMV7_EXCLUDE_USER      (1 << 30)
+#define        ARMV7_INCLUDE_HYP       (1 << 27)
+

This mode exclusion stuff is confusing me.

I'm not surprised, the wording is pretty hard to follow in the documentation.

Quoted 1 line

For exclude user mode, shouldn't PMXEVTYPER[PL1,U] = 0b11

No - this is a reserved encoding and results in the counters stopping for *all* modes!

Quoted 2 lines

With this patch the counters will spin with secure mode activity as well, if exclude user mode is selected ?

Yes, but they won't tick for either secure or non-secure user-mode, which is what we want. Note that secure software can stop the counters ticking by messing with the SDER.

Quoted 1 line

/me goes to bother the h/w guys.

Bah, they deserve it :)

Will

#14 lore raw

cover ARM: perf: updates for 3.2

In reply to Will Deacon

Hi Will,

Quoted 21 lines · Will Deacon

Hello,

This patch series contains a number of updates to the ARM PMU and perf code so that we can support mode exclusion, which is new in debug architecture 7.1 (as implemented by the Cortex-A15). Some of these updates also coincide with work to support System PMUs (PMUs that are not affine to a single CPU) but that is a larger body of work which will be posted separately at a later date.

The patches do the following:

1.) Greatly simplify the PMU reservation mechanism so that the
handling of platform_devices is moved into perf.
2.) Cleans up the interrupt registration and some of the types used
to represent events and registers.
3.) Moves event indexing to start from zero rather than one, making
the code more readable and also easier to extend for mode exclusion.
4.) Adds support for mode exclusion (user / kernel / hyp) and
implements this for Cortex-A15.

Quoted 2 lines

I've been running these patches since 3.0, so they've been tested on 1176, 11MPCore, Cortex-A5, Cortex-A9 and Cortex-A15 platforms.

A quick glance through them and they all look nice to me, but I can't get them to apply to any tree (next and master). It looks like they're based on a tree with changes to the v7 perf support so I'm guessing it's your patches with A15 support.

Jamie

#15 lore raw

cover ARM: perf: updates for 3.2

Quoted 1 line · Jamie Iles

Hi Will,

Hi Jamie,

Quoted 22 lines · Will Deacon
Quoted 21 lines

Hello,

This patch series contains a number of updates to the ARM PMU and perf code so that we can support mode exclusion, which is new in debug architecture 7.1 (as implemented by the Cortex-A15). Some of these updates also coincide with work to support System PMUs (PMUs that are not affine to a single CPU) but that is a larger body of work which will be posted separately at a later date.

The patches do the following:

1.) Greatly simplify the PMU reservation mechanism so that the
handling of platform_devices is moved into perf.
2.) Cleans up the interrupt registration and some of the types used
to represent events and registers.
3.) Moves event indexing to start from zero rather than one, making
the code more readable and also easier to extend for mode exclusion.
4.) Adds support for mode exclusion (user / kernel / hyp) and
implements this for Cortex-A15.

Quoted 7 lines
Quoted 2 lines

I've been running these patches since 3.0, so they've been tested on 1176, 11MPCore, Cortex-A5, Cortex-A9 and Cortex-A15 platforms.

A quick glance through them and they all look nice to me, but I can't get them to apply to any tree (next and master). It looks like they're based on a tree with changes to the v7 perf support so I'm guessing it's your patches with A15 support.

Thanks for taking a look. The patches should just apply against -rc1:

http://www.linux-arm.org/git?p=linux-2.6-wd.git;a=shortlog;h=refs/heads/perf-updates

Cheers,

Will

#16 lore raw

cover ARM: perf: updates for 3.2

Quoted 30 lines · Will Deacon
Quoted 1 line · Jamie Iles

Hi Will,

Hi Jamie,

Quoted 25 lines · Will Deacon
Quoted 23 lines

Hello,

This patch series contains a number of updates to the ARM PMU and perf code so that we can support mode exclusion, which is new in debug architecture 7.1 (as implemented by the Cortex-A15). Some of these updates also coincide with work to support System PMUs (PMUs that are not affine to a single CPU) but that is a larger body of work which will be posted separately at a later date.

The patches do the following:

1.) Greatly simplify the PMU reservation mechanism so that the
handling of platform_devices is moved into perf.
2.) Cleans up the interrupt registration and some of the types used
to represent events and registers.
3.) Moves event indexing to start from zero rather than one, making
the code more readable and also easier to extend for mode exclusion.
4.) Adds support for mode exclusion (user / kernel / hyp) and
implements this for Cortex-A15.

I've been running these patches since 3.0, so they've been tested on 1176, 11MPCore, Cortex-A5, Cortex-A9 and Cortex-A15 platforms.

Quoted 8 lines
Quoted 4 lines

A quick glance through them and they all look nice to me, but I can't get them to apply to any tree (next and master). It looks like they're based on a tree with changes to the v7 perf support so I'm guessing it's your patches with A15 support.

Thanks for taking a look. The patches should just apply against -rc1:

http://www.linux-arm.org/git?p=linux-2.6-wd.git;a=shortlog;h=refs/heads/perf-updates

Yes, you're quite right. I seem to have lost patches 1 and 4. Probably finger trouble on my part!

Anyhow, I've pulled from your git tree and they appear to work nicely, so:

Jamie

#17 lore raw

cover ARM: perf: updates for 3.2

Quoted 5 lines · Jamie Iles
Quoted 3 lines · Will Deacon

Thanks for taking a look. The patches should just apply against -rc1:

http://www.linux-arm.org/git?p=linux-2.6-wd.git;a=shortlog;h=refs/heads/perf-updates

Quoted 7 lines

Yes, you're quite right. I seem to have lost patches 1 and 4. Probably finger trouble on my part!

Anyhow, I've pulled from your git tree and they appear to work nicely, so:

Thanks a lot Jamie, I'll update that branch with your acks.

Will

#18 lore raw

cover ARM: perf: updates for 3.2

Hi Will,

I had a review of the patches and they all look all good to me. Although [06/10] is not easy to look at it seems correct and indeed the resulting code is easier to read.
Not tested though, sorry ;(

Feel free to add:

Thanks!
Jean

Quoted 23 lines · Will Deacon
Quoted 5 lines · Jamie Iles
Quoted 3 lines · Will Deacon

Thanks for taking a look. The patches should just apply against -rc1:

http://www.linux-arm.org/git?p=linux-2.6-wd.git;a=shortlog;h=refs/heads/perf-updates

Quoted 7 lines

Yes, you're quite right. ?I seem to have lost patches 1 and 4. ?Probably finger trouble on my part!

Anyhow, I've pulled from your git tree and they appear to work nicely, so:

Thanks a lot Jamie, I'll update that branch with your acks.

Will

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

#19 lore raw

cover ARM: perf: updates for 3.2

Quoted 1 line · Jean Pihet

Hi Will,

Hello,

Quoted 4 lines

I had a review of the patches and they all look all good to me. Although [06/10] is not easy to look at it seems correct and indeed the resulting code is easier to read.
Not tested though, sorry ;(

Thanks for having a look. If you do get a chance to test, please let me know how you get on.

Quoted 2 lines

Feel free to add:

Will do, cheers.

Will