ARM: Perfevents: Add mode exclusion support for Krait P2
Mode exclusion allows for counting filtered by the mode
the CPU in running in.
e.g.
perf stat -e rXXX:u to count only userspace activity
perf stat -e rXXX:k to count only kernel activity
Change-Id: I6cf6035d3cfe8d3ee8534ffe130eac6e965aa899
Signed-off-by: Ashwin Chaugule <ashwinc@codeaurora.org>
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index be324ac..9144461 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -82,6 +82,8 @@
struct hw_perf_event *hwc);
u32 (*read_counter)(int idx);
void (*write_counter)(int idx, u32 val);
+ int (*set_event_filter) (struct hw_perf_event *evt,
+ struct perf_event_attr *attr);
void (*start)(void);
void (*stop)(void);
void (*reset)(void *);
@@ -472,6 +474,13 @@
pmu_device = NULL;
}
+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 atomic_t active_events = ATOMIC_INIT(0);
static DEFINE_MUTEX(pmu_reserve_mutex);
@@ -508,17 +517,6 @@
return mapping;
}
- /*
- * 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) {
- 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
@@ -534,13 +532,23 @@
* the event mapping and the counter to use. The counter to use is
* also the indx and the config_base is the event type.
*/
- hwc->config_base = (unsigned long)mapping;
- hwc->config = 0;
- hwc->event_base = 0;
+ hwc->config_base = 0;
+ hwc->config = 0;
+ hwc->event_base = 0;
+
+ 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;
+ }
+
+ hwc->config_base |= (unsigned long)mapping;
if (!hwc->sample_period) {
hwc->sample_period = armpmu->max_period;
- hwc->last_period = hwc->sample_period;
+ hwc->last_period = hwc->sample_period;
local64_set(&hwc->period_left, hwc->sample_period);
}
diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c
index 1c2206f..259617a 100644
--- a/arch/arm/kernel/perf_event_msm.c
+++ b/arch/arm/kernel/perf_event_msm.c
@@ -585,6 +585,8 @@
*/
if (idx != ARMV7_CYCLE_COUNTER) {
val = hwc->config_base;
+ val &= ARMV7_EVTYPE_EVENT;
+
if (val > 0x40) {
event = get_scorpion_evtinfo(val, &evtinfo);
if (event == -EINVAL)
@@ -624,6 +626,8 @@
*/
if (idx != ARMV7_CYCLE_COUNTER) {
val = hwc->config_base;
+ val &= ARMV7_EVTYPE_EVENT;
+
if (val < 0x40) {
armv7_pmnc_write_evtsel(idx, hwc->config_base);
} else {
diff --git a/arch/arm/kernel/perf_event_msm_krait.c b/arch/arm/kernel/perf_event_msm_krait.c
index 978d216..34abb48 100644
--- a/arch/arm/kernel/perf_event_msm_krait.c
+++ b/arch/arm/kernel/perf_event_msm_krait.c
@@ -33,6 +33,9 @@
#define KRAIT_P2_L1_ITLB_ACCESS 0x12222
#define KRAIT_P2_L1_DTLB_ACCESS 0x12210
+#define KRAIT_EVENT_MASK 0xfffff
+#define KRAIT_MODE_EXCL_MASK 0xc0000000
+
u32 evt_type_base[][4] = {
{0x4c, 0x50, 0x54}, /* Pass 1 */
{0xcc, 0xd0, 0xd4, 0xd8}, /* Pass 2 */
@@ -391,6 +394,8 @@
*/
if (idx != ARMV7_CYCLE_COUNTER) {
val = hwc->config_base;
+ val &= KRAIT_EVENT_MASK;
+
if (val > 0x40) {
event = get_krait_evtinfo(val, &evtinfo);
if (event == -EINVAL)
@@ -430,6 +435,8 @@
*/
if (idx != ARMV7_CYCLE_COUNTER) {
val = hwc->config_base;
+ val &= KRAIT_EVENT_MASK;
+
if (val < 0x40) {
armv7_pmnc_write_evtsel(idx, hwc->config_base);
} else {
@@ -437,6 +444,10 @@
if (event == -EINVAL)
goto krait_out;
+
+ /* Restore Mode-exclusion bits */
+ event |= (hwc->config_base & KRAIT_MODE_EXCL_MASK);
+
/*
* Set event (if destined for PMNx counters)
* We don't need to set the event if it's a cycle count
@@ -505,6 +516,9 @@
if (krait_ver > 0) {
evt_index = 1;
krait_max_l1_reg = 3;
+
+ krait_pmu.set_event_filter = armv7pmu_set_event_filter,
+
armv7_krait_perf_cache_map[C(ITLB)]
[C(OP_READ)]
[C(RESULT_ACCESS)] = KRAIT_P2_L1_ITLB_ACCESS;
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 4031c7b..277f1ce 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -715,7 +715,8 @@
/*
* EVTSEL: Event selection reg
*/
-#define ARMV7_EVTSEL_MASK 0xff /* Mask for writable bits */
+#define ARMV7_EVTYPE_EVENT 0xff /* Mask for writable bits */
+#define ARMV7_EVTYPE_MASK 0xc00000ff
/*
* SELECT: Counter selection reg
@@ -730,6 +731,39 @@
#define ARMV7_FLAG_MASK 0xffffffff /* Mask for writable bits */
#define ARMV7_OVERFLOWED_MASK ARMV7_FLAG_MASK
+/*
+ * Event filters for PMUv2
+ */
+#define ARMV7_EXCLUDE_PL1 (1 << 31)
+#define ARMV7_EXCLUDE_USER (1 << 30)
+#define ARMV7_INCLUDE_HYP (1 << 27)
+
+/*
+ * Add an event filter to a given event. This will only work for PMUv2 PMUs.
+ */
+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 inline unsigned long armv7_pmnc_read(void)
{
u32 val;
@@ -815,7 +849,8 @@
static inline void armv7_pmnc_write_evtsel(unsigned 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));
}
}
@@ -976,7 +1011,7 @@
* 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 (armpmu->set_event_filter || idx != ARMV7_CYCLE_COUNTER)
armv7_pmnc_write_evtsel(idx, hwc->config_base);
/*
@@ -1101,9 +1136,11 @@
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_CYCLE_COUNTER, cpuc->used_mask))
return -EAGAIN;