Merge branch 'for-linus' of git://git.selinuxproject.org/~jmorris/linux-security
[pandora-kernel.git] / arch / arm / kernel / perf_event_v7.c
index 0934c82..1ef6d00 100644 (file)
@@ -17,6 +17,9 @@
  */
 
 #ifdef CONFIG_CPU_V7
+
+static struct arm_pmu armv7pmu;
+
 /*
  * Common ARMv7 event types
  *
@@ -321,8 +324,8 @@ static const unsigned armv7_a9_perf_map[PERF_COUNT_HW_MAX] = {
        [PERF_COUNT_HW_CPU_CYCLES]          = ARMV7_PERFCTR_CPU_CYCLES,
        [PERF_COUNT_HW_INSTRUCTIONS]        =
                                        ARMV7_PERFCTR_INST_OUT_OF_RENAME_STAGE,
-       [PERF_COUNT_HW_CACHE_REFERENCES]    = ARMV7_PERFCTR_COHERENT_LINE_HIT,
-       [PERF_COUNT_HW_CACHE_MISSES]        = ARMV7_PERFCTR_COHERENT_LINE_MISS,
+       [PERF_COUNT_HW_CACHE_REFERENCES]    = ARMV7_PERFCTR_DCACHE_ACCESS,
+       [PERF_COUNT_HW_CACHE_MISSES]        = ARMV7_PERFCTR_DCACHE_REFILL,
        [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE,
        [PERF_COUNT_HW_BRANCH_MISSES]       = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
        [PERF_COUNT_HW_BUS_CYCLES]          = ARMV7_PERFCTR_CLOCK_CYCLES,
@@ -680,7 +683,7 @@ static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
  */
 #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_IDX_COUNTER_LAST  (ARMV7_IDX_CYCLE_COUNTER + cpu_pmu->num_events - 1)
 
 #define        ARMV7_MAX_COUNTERS      32
 #define        ARMV7_COUNTER_MASK      (ARMV7_MAX_COUNTERS - 1)
@@ -708,17 +711,25 @@ static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 #define        ARMV7_PMNC_N_MASK       0x1f
 #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));
        }
 }
@@ -925,12 +936,13 @@ static void armv7_pmnc_dump_regs(void)
 static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx)
 {
        unsigned long flags;
+       struct pmu_hw_events *events = cpu_pmu->get_hw_events();
 
        /*
         * Enable counter and interrupt, and set the counter to count
         * the event that we're interested in.
         */
-       raw_spin_lock_irqsave(&pmu_lock, flags);
+       raw_spin_lock_irqsave(&events->pmu_lock, flags);
 
        /*
         * Disable counter
@@ -939,9 +951,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);
 
        /*
@@ -954,17 +967,18 @@ static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx)
         */
        armv7_pmnc_enable_counter(idx);
 
-       raw_spin_unlock_irqrestore(&pmu_lock, flags);
+       raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }
 
 static void armv7pmu_disable_event(struct hw_perf_event *hwc, int idx)
 {
        unsigned long flags;
+       struct pmu_hw_events *events = cpu_pmu->get_hw_events();
 
        /*
         * Disable counter and interrupt
         */
-       raw_spin_lock_irqsave(&pmu_lock, flags);
+       raw_spin_lock_irqsave(&events->pmu_lock, flags);
 
        /*
         * Disable counter
@@ -976,14 +990,14 @@ static void armv7pmu_disable_event(struct hw_perf_event *hwc, int idx)
         */
        armv7_pmnc_disable_intens(idx);
 
-       raw_spin_unlock_irqrestore(&pmu_lock, flags);
+       raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }
 
 static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
 {
        u32 pmnc;
        struct perf_sample_data data;
-       struct cpu_hw_events *cpuc;
+       struct pmu_hw_events *cpuc;
        struct pt_regs *regs;
        int idx;
 
@@ -1006,13 +1020,10 @@ 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 < cpu_pmu->num_events; ++idx) {
                struct perf_event *event = cpuc->events[idx];
                struct hw_perf_event *hwc;
 
-               if (!test_bit(idx, cpuc->active_mask))
-                       continue;
-
                /*
                 * We have a single interrupt for all counters. Check that
                 * each counter has overflowed before we process it.
@@ -1027,7 +1038,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
                        continue;
 
                if (perf_event_overflow(event, &data, regs))
-                       armpmu->disable(hwc, idx);
+                       cpu_pmu->disable(hwc, idx);
        }
 
        /*
@@ -1045,30 +1056,33 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
 static void armv7pmu_start(void)
 {
        unsigned long flags;
+       struct pmu_hw_events *events = cpu_pmu->get_hw_events();
 
-       raw_spin_lock_irqsave(&pmu_lock, flags);
+       raw_spin_lock_irqsave(&events->pmu_lock, flags);
        /* Enable all counters */
        armv7_pmnc_write(armv7_pmnc_read() | ARMV7_PMNC_E);
-       raw_spin_unlock_irqrestore(&pmu_lock, flags);
+       raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }
 
 static void armv7pmu_stop(void)
 {
        unsigned long flags;
+       struct pmu_hw_events *events = cpu_pmu->get_hw_events();
 
-       raw_spin_lock_irqsave(&pmu_lock, flags);
+       raw_spin_lock_irqsave(&events->pmu_lock, flags);
        /* Disable all counters */
        armv7_pmnc_write(armv7_pmnc_read() & ~ARMV7_PMNC_E);
-       raw_spin_unlock_irqrestore(&pmu_lock, flags);
+       raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }
 
-static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc,
+static int armv7pmu_get_event_idx(struct pmu_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;
 
@@ -1079,7 +1093,7 @@ static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc,
         * For anything other than a cycle counter, try and use
         * the events counters
         */
-       for (idx = ARMV7_IDX_COUNTER0; idx < armpmu->num_events; ++idx) {
+       for (idx = ARMV7_IDX_COUNTER0; idx < cpu_pmu->num_events; ++idx) {
                if (!test_and_set_bit(idx, cpuc->used_mask))
                        return idx;
        }
@@ -1088,9 +1102,35 @@ 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;
+       u32 idx, nb_cnt = cpu_pmu->num_events;
 
        /* The counter and interrupt enable registers are unknown at reset. */
        for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx)
@@ -1100,6 +1140,30 @@ static void armv7pmu_reset(void *info)
        armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C);
 }
 
+static int armv7_a8_map_event(struct perf_event *event)
+{
+       return map_cpu_event(event, &armv7_a8_perf_map,
+                               &armv7_a8_perf_cache_map, 0xFF);
+}
+
+static int armv7_a9_map_event(struct perf_event *event)
+{
+       return map_cpu_event(event, &armv7_a9_perf_map,
+                               &armv7_a9_perf_cache_map, 0xFF);
+}
+
+static int armv7_a5_map_event(struct perf_event *event)
+{
+       return map_cpu_event(event, &armv7_a5_perf_map,
+                               &armv7_a5_perf_cache_map, 0xFF);
+}
+
+static int armv7_a15_map_event(struct perf_event *event)
+{
+       return map_cpu_event(event, &armv7_a15_perf_map,
+                               &armv7_a15_perf_cache_map, 0xFF);
+}
+
 static struct arm_pmu armv7pmu = {
        .handle_irq             = armv7pmu_handle_irq,
        .enable                 = armv7pmu_enable_event,
@@ -1110,7 +1174,6 @@ static struct arm_pmu armv7pmu = {
        .start                  = armv7pmu_start,
        .stop                   = armv7pmu_stop,
        .reset                  = armv7pmu_reset,
-       .raw_event_mask         = 0xFF,
        .max_period             = (1LLU << 32) - 1,
 };
 
@@ -1129,8 +1192,7 @@ static struct arm_pmu *__init armv7_a8_pmu_init(void)
 {
        armv7pmu.id             = ARM_PERF_PMU_ID_CA8;
        armv7pmu.name           = "ARMv7 Cortex-A8";
-       armv7pmu.cache_map      = &armv7_a8_perf_cache_map;
-       armv7pmu.event_map      = &armv7_a8_perf_map;
+       armv7pmu.map_event      = armv7_a8_map_event;
        armv7pmu.num_events     = armv7_read_num_pmnc_events();
        return &armv7pmu;
 }
@@ -1139,8 +1201,7 @@ static struct arm_pmu *__init armv7_a9_pmu_init(void)
 {
        armv7pmu.id             = ARM_PERF_PMU_ID_CA9;
        armv7pmu.name           = "ARMv7 Cortex-A9";
-       armv7pmu.cache_map      = &armv7_a9_perf_cache_map;
-       armv7pmu.event_map      = &armv7_a9_perf_map;
+       armv7pmu.map_event      = armv7_a9_map_event;
        armv7pmu.num_events     = armv7_read_num_pmnc_events();
        return &armv7pmu;
 }
@@ -1149,8 +1210,7 @@ static struct arm_pmu *__init armv7_a5_pmu_init(void)
 {
        armv7pmu.id             = ARM_PERF_PMU_ID_CA5;
        armv7pmu.name           = "ARMv7 Cortex-A5";
-       armv7pmu.cache_map      = &armv7_a5_perf_cache_map;
-       armv7pmu.event_map      = &armv7_a5_perf_map;
+       armv7pmu.map_event      = armv7_a5_map_event;
        armv7pmu.num_events     = armv7_read_num_pmnc_events();
        return &armv7pmu;
 }
@@ -1159,9 +1219,9 @@ static struct arm_pmu *__init armv7_a15_pmu_init(void)
 {
        armv7pmu.id             = ARM_PERF_PMU_ID_CA15;
        armv7pmu.name           = "ARMv7 Cortex-A15";
-       armv7pmu.cache_map      = &armv7_a15_perf_cache_map;
-       armv7pmu.event_map      = &armv7_a15_perf_map;
+       armv7pmu.map_event      = armv7_a15_map_event;
        armv7pmu.num_events     = armv7_read_num_pmnc_events();
+       armv7pmu.set_event_filter = armv7pmu_set_event_filter;
        return &armv7pmu;
 }
 #else