diff mbox

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

Message ID 1312823771-9952-11-git-send-email-will.deacon@arm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Will Deacon Aug. 8, 2011, 5:16 p.m. UTC
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.

Signed-off-by: Will Deacon <will.deacon@arm.com>
---
 arch/arm/kernel/perf_event_v7.c |   58 +++++++++++++++++++++++++++++++++------
 1 files changed, 49 insertions(+), 9 deletions(-)

Comments

Ashwin Chaugule Aug. 25, 2011, 3:09 a.m. UTC | #1
Hey Will,

> From: Will Deacon <will.deacon@arm.com>
> 
> 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.
> 
> Signed-off-by: Will Deacon <will.deacon@arm.com>
> ---


--8<---

> +
> +/*
> + * 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.
Will Deacon Aug. 25, 2011, 9:51 a.m. UTC | #2
On Thu, Aug 25, 2011 at 04:09:13AM +0100, Ashwin Chaugule wrote:
> Hey Will,

Hi Ashwin,

> > From: Will Deacon <will.deacon@arm.com>
> > 
> > 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.
> > 
> > Signed-off-by: Will Deacon <will.deacon@arm.com>
> > ---
> 
> 
> --8<---
> 
> > +
> > +/*
> > + * 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.

> 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!

> 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.

> /me goes to bother the h/w guys.

Bah, they deserve it :)

Will
diff mbox

Patch

diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 0934c82..fe6c931 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -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