Merge branch 'for-linus' of git://git.selinuxproject.org/~jmorris/linux-security
[pandora-kernel.git] / arch / arm / kernel / perf_event.c
index 1a2ebbf..e6e5d7c 100644 (file)
@@ -12,6 +12,7 @@
  */
 #define pr_fmt(fmt) "hw perfevents: " fmt
 
+#include <linux/bitmap.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
  */
 #define ARMPMU_MAX_HWEVENTS            32
 
-/* The events for a given CPU. */
-struct cpu_hw_events {
-       /*
-        * The events that are active on the CPU for the given index.
-        */
-       struct perf_event       *events[ARMPMU_MAX_HWEVENTS];
+static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events);
+static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask);
+static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events);
 
-       /*
-        * A 1 bit for an index indicates that the counter is being used for
-        * an event. A 0 means that the counter can be used.
-        */
-       unsigned long           used_mask[BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)];
-
-       /*
-        * Hardware lock to serialize accesses to PMU registers. Needed for the
-        * read/modify/write sequences.
-        */
-       raw_spinlock_t          pmu_lock;
-};
-static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
-
-struct arm_pmu {
-       enum arm_perf_pmu_ids id;
-       enum arm_pmu_type type;
-       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);
-       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);
-       void            (*stop)(void);
-       void            (*reset)(void *);
-       const unsigned  (*cache_map)[PERF_COUNT_HW_CACHE_MAX]
-                                   [PERF_COUNT_HW_CACHE_OP_MAX]
-                                   [PERF_COUNT_HW_CACHE_RESULT_MAX];
-       const unsigned  (*event_map)[PERF_COUNT_HW_MAX];
-       u32             raw_event_mask;
-       int             num_events;
-       atomic_t        active_events;
-       struct mutex    reserve_mutex;
-       u64             max_period;
-       struct platform_device  *plat_device;
-       struct cpu_hw_events    *(*get_hw_events)(void);
-};
+#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
 
 /* Set at runtime when we know what CPU type we are. */
-static struct arm_pmu *armpmu;
+static struct arm_pmu *cpu_pmu;
 
 enum arm_perf_pmu_ids
 armpmu_get_pmu_id(void)
 {
        int id = -ENODEV;
 
-       if (armpmu != NULL)
-               id = armpmu->id;
+       if (cpu_pmu != NULL)
+               id = cpu_pmu->id;
 
        return id;
 }
@@ -108,8 +64,8 @@ armpmu_get_max_events(void)
 {
        int max_events = 0;
 
-       if (armpmu != NULL)
-               max_events = armpmu->num_events;
+       if (cpu_pmu != NULL)
+               max_events = cpu_pmu->num_events;
 
        return max_events;
 }
@@ -129,7 +85,11 @@ EXPORT_SYMBOL_GPL(perf_num_counters);
 #define CACHE_OP_UNSUPPORTED           0xFFFF
 
 static int
-armpmu_map_cache_event(u64 config)
+armpmu_map_cache_event(const unsigned (*cache_map)
+                                     [PERF_COUNT_HW_CACHE_MAX]
+                                     [PERF_COUNT_HW_CACHE_OP_MAX]
+                                     [PERF_COUNT_HW_CACHE_RESULT_MAX],
+                      u64 config)
 {
        unsigned int cache_type, cache_op, cache_result, ret;
 
@@ -145,7 +105,7 @@ armpmu_map_cache_event(u64 config)
        if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
                return -EINVAL;
 
-       ret = (int)(*armpmu->cache_map)[cache_type][cache_op][cache_result];
+       ret = (int)(*cache_map)[cache_type][cache_op][cache_result];
 
        if (ret == CACHE_OP_UNSUPPORTED)
                return -ENOENT;
@@ -154,23 +114,46 @@ armpmu_map_cache_event(u64 config)
 }
 
 static int
-armpmu_map_event(u64 config)
+armpmu_map_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config)
 {
-       int mapping = (*armpmu->event_map)[config];
-       return mapping == HW_OP_UNSUPPORTED ? -EOPNOTSUPP : mapping;
+       int mapping = (*event_map)[config];
+       return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping;
 }
 
 static int
-armpmu_map_raw_event(u64 config)
+armpmu_map_raw_event(u32 raw_event_mask, u64 config)
 {
-       return (int)(config & armpmu->raw_event_mask);
+       return (int)(config & raw_event_mask);
 }
 
-static int
+static int map_cpu_event(struct perf_event *event,
+                        const unsigned (*event_map)[PERF_COUNT_HW_MAX],
+                        const unsigned (*cache_map)
+                                       [PERF_COUNT_HW_CACHE_MAX]
+                                       [PERF_COUNT_HW_CACHE_OP_MAX]
+                                       [PERF_COUNT_HW_CACHE_RESULT_MAX],
+                        u32 raw_event_mask)
+{
+       u64 config = event->attr.config;
+
+       switch (event->attr.type) {
+       case PERF_TYPE_HARDWARE:
+               return armpmu_map_event(event_map, config);
+       case PERF_TYPE_HW_CACHE:
+               return armpmu_map_cache_event(cache_map, config);
+       case PERF_TYPE_RAW:
+               return armpmu_map_raw_event(raw_event_mask, config);
+       }
+
+       return -ENOENT;
+}
+
+int
 armpmu_event_set_period(struct perf_event *event,
                        struct hw_perf_event *hwc,
                        int idx)
 {
+       struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
        s64 left = local64_read(&hwc->period_left);
        s64 period = hwc->sample_period;
        int ret = 0;
@@ -201,11 +184,12 @@ armpmu_event_set_period(struct perf_event *event,
        return ret;
 }
 
-static u64
+u64
 armpmu_event_update(struct perf_event *event,
                    struct hw_perf_event *hwc,
                    int idx, int overflow)
 {
+       struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
        u64 delta, prev_raw_count, new_raw_count;
 
 again:
@@ -245,6 +229,7 @@ armpmu_read(struct perf_event *event)
 static void
 armpmu_stop(struct perf_event *event, int flags)
 {
+       struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
        struct hw_perf_event *hwc = &event->hw;
 
        /*
@@ -262,6 +247,7 @@ armpmu_stop(struct perf_event *event, int flags)
 static void
 armpmu_start(struct perf_event *event, int flags)
 {
+       struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
        struct hw_perf_event *hwc = &event->hw;
 
        /*
@@ -286,15 +272,16 @@ armpmu_start(struct perf_event *event, int flags)
 static void
 armpmu_del(struct perf_event *event, int flags)
 {
-       struct cpu_hw_events *cpuc = armpmu->get_hw_events();
+       struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+       struct pmu_hw_events *hw_events = armpmu->get_hw_events();
        struct hw_perf_event *hwc = &event->hw;
        int idx = hwc->idx;
 
        WARN_ON(idx < 0);
 
        armpmu_stop(event, PERF_EF_UPDATE);
-       cpuc->events[idx] = NULL;
-       clear_bit(idx, cpuc->used_mask);
+       hw_events->events[idx] = NULL;
+       clear_bit(idx, hw_events->used_mask);
 
        perf_event_update_userpage(event);
 }
@@ -302,7 +289,8 @@ armpmu_del(struct perf_event *event, int flags)
 static int
 armpmu_add(struct perf_event *event, int flags)
 {
-       struct cpu_hw_events *cpuc = armpmu->get_hw_events();
+       struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+       struct pmu_hw_events *hw_events = armpmu->get_hw_events();
        struct hw_perf_event *hwc = &event->hw;
        int idx;
        int err = 0;
@@ -310,7 +298,7 @@ armpmu_add(struct perf_event *event, int flags)
        perf_pmu_disable(event->pmu);
 
        /* If we don't have a space for the counter then finish early. */
-       idx = armpmu->get_event_idx(cpuc, hwc);
+       idx = armpmu->get_event_idx(hw_events, hwc);
        if (idx < 0) {
                err = idx;
                goto out;
@@ -322,7 +310,7 @@ armpmu_add(struct perf_event *event, int flags)
         */
        event->hw.idx = idx;
        armpmu->disable(hwc, idx);
-       cpuc->events[idx] = event;
+       hw_events->events[idx] = event;
 
        hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
        if (flags & PERF_EF_START)
@@ -336,26 +324,25 @@ out:
        return err;
 }
 
-static struct pmu pmu;
-
 static int
-validate_event(struct cpu_hw_events *cpuc,
+validate_event(struct pmu_hw_events *hw_events,
               struct perf_event *event)
 {
+       struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
        struct hw_perf_event fake_event = event->hw;
        struct pmu *leader_pmu = event->group_leader->pmu;
 
        if (event->pmu != leader_pmu || event->state <= PERF_EVENT_STATE_OFF)
                return 1;
 
-       return armpmu->get_event_idx(cpuc, &fake_event) >= 0;
+       return armpmu->get_event_idx(hw_events, &fake_event) >= 0;
 }
 
 static int
 validate_group(struct perf_event *event)
 {
        struct perf_event *sibling, *leader = event->group_leader;
-       struct cpu_hw_events fake_pmu;
+       struct pmu_hw_events fake_pmu;
 
        memset(&fake_pmu, 0, sizeof(fake_pmu));
 
@@ -375,6 +362,7 @@ validate_group(struct perf_event *event)
 
 static irqreturn_t armpmu_platform_irq(int irq, void *dev)
 {
+       struct arm_pmu *armpmu = (struct arm_pmu *) dev;
        struct platform_device *plat_device = armpmu->plat_device;
        struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev);
 
@@ -382,7 +370,7 @@ static irqreturn_t armpmu_platform_irq(int irq, void *dev)
 }
 
 static void
-armpmu_release_hardware(void)
+armpmu_release_hardware(struct arm_pmu *armpmu)
 {
        int i, irq, irqs;
        struct platform_device *pmu_device = armpmu->plat_device;
@@ -394,14 +382,14 @@ armpmu_release_hardware(void)
                        continue;
                irq = platform_get_irq(pmu_device, i);
                if (irq >= 0)
-                       free_irq(irq, NULL);
+                       free_irq(irq, armpmu);
        }
 
        release_pmu(armpmu->type);
 }
 
 static int
-armpmu_reserve_hardware(void)
+armpmu_reserve_hardware(struct arm_pmu *armpmu)
 {
        struct arm_pmu_platdata *plat;
        irq_handler_t handle_irq;
@@ -445,11 +433,11 @@ armpmu_reserve_hardware(void)
 
                err = request_irq(irq, handle_irq,
                                  IRQF_DISABLED | IRQF_NOBALANCING,
-                                 "arm-pmu", NULL);
+                                 "arm-pmu", armpmu);
                if (err) {
                        pr_err("unable to request IRQ%d for ARM PMU counters\n",
                                irq);
-                       armpmu_release_hardware();
+                       armpmu_release_hardware(armpmu);
                        return err;
                }
 
@@ -462,11 +450,12 @@ armpmu_reserve_hardware(void)
 static void
 hw_perf_event_destroy(struct perf_event *event)
 {
+       struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
        atomic_t *active_events  = &armpmu->active_events;
        struct mutex *pmu_reserve_mutex = &armpmu->reserve_mutex;
 
        if (atomic_dec_and_mutex_lock(active_events, pmu_reserve_mutex)) {
-               armpmu_release_hardware();
+               armpmu_release_hardware(armpmu);
                mutex_unlock(pmu_reserve_mutex);
        }
 }
@@ -481,20 +470,11 @@ event_requires_mode_exclusion(struct perf_event_attr *attr)
 static int
 __hw_perf_event_init(struct perf_event *event)
 {
+       struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
        struct hw_perf_event *hwc = &event->hw;
        int mapping, err;
 
-       /* Decode the generic type into an ARM event identifier. */
-       if (PERF_TYPE_HARDWARE == event->attr.type) {
-               mapping = armpmu_map_event(event->attr.config);
-       } else if (PERF_TYPE_HW_CACHE == event->attr.type) {
-               mapping = armpmu_map_cache_event(event->attr.config);
-       } else if (PERF_TYPE_RAW == event->attr.type) {
-               mapping = armpmu_map_raw_event(event->attr.config);
-       } else {
-               pr_debug("event type %x not supported\n", event->attr.type);
-               return -EOPNOTSUPP;
-       }
+       mapping = armpmu->map_event(event);
 
        if (mapping < 0) {
                pr_debug("event %x:%llx not supported\n", event->attr.type,
@@ -547,25 +527,19 @@ __hw_perf_event_init(struct perf_event *event)
 
 static int armpmu_event_init(struct perf_event *event)
 {
+       struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
        int err = 0;
        atomic_t *active_events = &armpmu->active_events;
 
-       switch (event->attr.type) {
-       case PERF_TYPE_RAW:
-       case PERF_TYPE_HARDWARE:
-       case PERF_TYPE_HW_CACHE:
-               break;
-
-       default:
+       if (armpmu->map_event(event) == -ENOENT)
                return -ENOENT;
-       }
 
        event->destroy = hw_perf_event_destroy;
 
        if (!atomic_inc_not_zero(active_events)) {
                mutex_lock(&armpmu->reserve_mutex);
                if (atomic_read(active_events) == 0)
-                       err = armpmu_reserve_hardware();
+                       err = armpmu_reserve_hardware(armpmu);
 
                if (!err)
                        atomic_inc(active_events);
@@ -584,19 +558,9 @@ static int armpmu_event_init(struct perf_event *event)
 
 static void armpmu_enable(struct pmu *pmu)
 {
-       /* Enable all of the perf events on hardware. */
-       int idx, enabled = 0;
-       struct cpu_hw_events *cpuc = armpmu->get_hw_events();
-
-       for (idx = 0; idx < armpmu->num_events; ++idx) {
-               struct perf_event *event = cpuc->events[idx];
-
-               if (!event)
-                       continue;
-
-               armpmu->enable(&event->hw, idx);
-               enabled = 1;
-       }
+       struct arm_pmu *armpmu = to_arm_pmu(pmu);
+       struct pmu_hw_events *hw_events = armpmu->get_hw_events();
+       int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events);
 
        if (enabled)
                armpmu->start();
@@ -604,24 +568,31 @@ static void armpmu_enable(struct pmu *pmu)
 
 static void armpmu_disable(struct pmu *pmu)
 {
+       struct arm_pmu *armpmu = to_arm_pmu(pmu);
        armpmu->stop();
 }
 
-static struct pmu pmu = {
-       .pmu_enable     = armpmu_enable,
-       .pmu_disable    = armpmu_disable,
-       .event_init     = armpmu_event_init,
-       .add            = armpmu_add,
-       .del            = armpmu_del,
-       .start          = armpmu_start,
-       .stop           = armpmu_stop,
-       .read           = armpmu_read,
-};
-
 static void __init armpmu_init(struct arm_pmu *armpmu)
 {
        atomic_set(&armpmu->active_events, 0);
        mutex_init(&armpmu->reserve_mutex);
+
+       armpmu->pmu = (struct pmu) {
+               .pmu_enable     = armpmu_enable,
+               .pmu_disable    = armpmu_disable,
+               .event_init     = armpmu_event_init,
+               .add            = armpmu_add,
+               .del            = armpmu_del,
+               .start          = armpmu_start,
+               .stop           = armpmu_stop,
+               .read           = armpmu_read,
+       };
+}
+
+int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type)
+{
+       armpmu_init(armpmu);
+       return perf_pmu_register(&armpmu->pmu, name, type);
 }
 
 /* Include the PMU-specific implementations. */
@@ -634,13 +605,13 @@ static void __init armpmu_init(struct arm_pmu *armpmu)
  * This requires SMP to be available, so exists as a separate initcall.
  */
 static int __init
-armpmu_reset(void)
+cpu_pmu_reset(void)
 {
-       if (armpmu && armpmu->reset)
-               return on_each_cpu(armpmu->reset, NULL, 1);
+       if (cpu_pmu && cpu_pmu->reset)
+               return on_each_cpu(cpu_pmu->reset, NULL, 1);
        return 0;
 }
-arch_initcall(armpmu_reset);
+arch_initcall(cpu_pmu_reset);
 
 /*
  * PMU platform driver and devicetree bindings.
@@ -660,7 +631,7 @@ static struct platform_device_id armpmu_plat_device_ids[] = {
 
 static int __devinit armpmu_device_probe(struct platform_device *pdev)
 {
-       armpmu->plat_device = pdev;
+       cpu_pmu->plat_device = pdev;
        return 0;
 }
 
@@ -679,7 +650,7 @@ static int __init register_pmu_driver(void)
 }
 device_initcall(register_pmu_driver);
 
-static struct cpu_hw_events *armpmu_get_cpu_events(void)
+static struct pmu_hw_events *armpmu_get_cpu_events(void)
 {
        return &__get_cpu_var(cpu_hw_events);
 }
@@ -688,7 +659,9 @@ static void __init cpu_pmu_init(struct arm_pmu *armpmu)
 {
        int cpu;
        for_each_possible_cpu(cpu) {
-               struct cpu_hw_events *events = &per_cpu(cpu_hw_events, cpu);
+               struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu);
+               events->events = per_cpu(hw_events, cpu);
+               events->used_mask = per_cpu(used_mask, cpu);
                raw_spin_lock_init(&events->pmu_lock);
        }
        armpmu->get_hw_events = armpmu_get_cpu_events;
@@ -711,22 +684,22 @@ init_hw_perf_events(void)
                case 0xB360:    /* ARM1136 */
                case 0xB560:    /* ARM1156 */
                case 0xB760:    /* ARM1176 */
-                       armpmu = armv6pmu_init();
+                       cpu_pmu = armv6pmu_init();
                        break;
                case 0xB020:    /* ARM11mpcore */
-                       armpmu = armv6mpcore_pmu_init();
+                       cpu_pmu = armv6mpcore_pmu_init();
                        break;
                case 0xC080:    /* Cortex-A8 */
-                       armpmu = armv7_a8_pmu_init();
+                       cpu_pmu = armv7_a8_pmu_init();
                        break;
                case 0xC090:    /* Cortex-A9 */
-                       armpmu = armv7_a9_pmu_init();
+                       cpu_pmu = armv7_a9_pmu_init();
                        break;
                case 0xC050:    /* Cortex-A5 */
-                       armpmu = armv7_a5_pmu_init();
+                       cpu_pmu = armv7_a5_pmu_init();
                        break;
                case 0xC0F0:    /* Cortex-A15 */
-                       armpmu = armv7_a15_pmu_init();
+                       cpu_pmu = armv7_a15_pmu_init();
                        break;
                }
        /* Intel CPUs [xscale]. */
@@ -734,20 +707,19 @@ init_hw_perf_events(void)
                part_number = (cpuid >> 13) & 0x7;
                switch (part_number) {
                case 1:
-                       armpmu = xscale1pmu_init();
+                       cpu_pmu = xscale1pmu_init();
                        break;
                case 2:
-                       armpmu = xscale2pmu_init();
+                       cpu_pmu = xscale2pmu_init();
                        break;
                }
        }
 
-       if (armpmu) {
+       if (cpu_pmu) {
                pr_info("enabled with %s PMU driver, %d counters available\n",
-                       armpmu->name, armpmu->num_events);
-               cpu_pmu_init(armpmu);
-               armpmu_init(armpmu);
-               perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
+                       cpu_pmu->name, cpu_pmu->num_events);
+               cpu_pmu_init(cpu_pmu);
+               armpmu_register(cpu_pmu, "cpu", PERF_TYPE_RAW);
        } else {
                pr_info("no hardware support available\n");
        }