linux/drivers/oprofile/oprofile_perf.c
<<
>>
Prefs
   1/*
   2 * Copyright 2010 ARM Ltd.
   3 * Copyright 2012 Advanced Micro Devices, Inc., Robert Richter
   4 *
   5 * Perf-events backend for OProfile.
   6 */
   7#include <linux/perf_event.h>
   8#include <linux/platform_device.h>
   9#include <linux/oprofile.h>
  10#include <linux/slab.h>
  11
  12/*
  13 * Per performance monitor configuration as set via oprofilefs.
  14 */
  15struct op_counter_config {
  16        unsigned long count;
  17        unsigned long enabled;
  18        unsigned long event;
  19        unsigned long unit_mask;
  20        unsigned long kernel;
  21        unsigned long user;
  22        struct perf_event_attr attr;
  23};
  24
  25static int oprofile_perf_enabled;
  26static DEFINE_MUTEX(oprofile_perf_mutex);
  27
  28static struct op_counter_config *counter_config;
  29static DEFINE_PER_CPU(struct perf_event **, perf_events);
  30static int num_counters;
  31
  32/*
  33 * Overflow callback for oprofile.
  34 */
  35static void op_overflow_handler(struct perf_event *event,
  36                        struct perf_sample_data *data, struct pt_regs *regs)
  37{
  38        int id;
  39        u32 cpu = smp_processor_id();
  40
  41        for (id = 0; id < num_counters; ++id)
  42                if (per_cpu(perf_events, cpu)[id] == event)
  43                        break;
  44
  45        if (id != num_counters)
  46                oprofile_add_sample(regs, id);
  47        else
  48                pr_warning("oprofile: ignoring spurious overflow "
  49                                "on cpu %u\n", cpu);
  50}
  51
  52/*
  53 * Called by oprofile_perf_setup to create perf attributes to mirror the oprofile
  54 * settings in counter_config. Attributes are created as `pinned' events and
  55 * so are permanently scheduled on the PMU.
  56 */
  57static void op_perf_setup(void)
  58{
  59        int i;
  60        u32 size = sizeof(struct perf_event_attr);
  61        struct perf_event_attr *attr;
  62
  63        for (i = 0; i < num_counters; ++i) {
  64                attr = &counter_config[i].attr;
  65                memset(attr, 0, size);
  66                attr->type              = PERF_TYPE_RAW;
  67                attr->size              = size;
  68                attr->config            = counter_config[i].event;
  69                attr->sample_period     = counter_config[i].count;
  70                attr->pinned            = 1;
  71        }
  72}
  73
  74static int op_create_counter(int cpu, int event)
  75{
  76        struct perf_event *pevent;
  77
  78        if (!counter_config[event].enabled || per_cpu(perf_events, cpu)[event])
  79                return 0;
  80
  81        pevent = perf_event_create_kernel_counter(&counter_config[event].attr,
  82                                                  cpu, NULL,
  83                                                  op_overflow_handler, NULL);
  84
  85        if (IS_ERR(pevent))
  86                return PTR_ERR(pevent);
  87
  88        if (pevent->state != PERF_EVENT_STATE_ACTIVE) {
  89                perf_event_release_kernel(pevent);
  90                pr_warning("oprofile: failed to enable event %d "
  91                                "on CPU %d\n", event, cpu);
  92                return -EBUSY;
  93        }
  94
  95        per_cpu(perf_events, cpu)[event] = pevent;
  96
  97        return 0;
  98}
  99
 100static void op_destroy_counter(int cpu, int event)
 101{
 102        struct perf_event *pevent = per_cpu(perf_events, cpu)[event];
 103
 104        if (pevent) {
 105                perf_event_release_kernel(pevent);
 106                per_cpu(perf_events, cpu)[event] = NULL;
 107        }
 108}
 109
 110/*
 111 * Called by oprofile_perf_start to create active perf events based on the
 112 * perviously configured attributes.
 113 */
 114static int op_perf_start(void)
 115{
 116        int cpu, event, ret = 0;
 117
 118        for_each_online_cpu(cpu) {
 119                for (event = 0; event < num_counters; ++event) {
 120                        ret = op_create_counter(cpu, event);
 121                        if (ret)
 122                                return ret;
 123                }
 124        }
 125
 126        return ret;
 127}
 128
 129/*
 130 * Called by oprofile_perf_stop at the end of a profiling run.
 131 */
 132static void op_perf_stop(void)
 133{
 134        int cpu, event;
 135
 136        for_each_online_cpu(cpu)
 137                for (event = 0; event < num_counters; ++event)
 138                        op_destroy_counter(cpu, event);
 139}
 140
 141static int oprofile_perf_create_files(struct super_block *sb, struct dentry *root)
 142{
 143        unsigned int i;
 144
 145        for (i = 0; i < num_counters; i++) {
 146                struct dentry *dir;
 147                char buf[4];
 148
 149                snprintf(buf, sizeof buf, "%d", i);
 150                dir = oprofilefs_mkdir(sb, root, buf);
 151                oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled);
 152                oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event);
 153                oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count);
 154                oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask);
 155                oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel);
 156                oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user);
 157        }
 158
 159        return 0;
 160}
 161
 162static int oprofile_perf_setup(void)
 163{
 164        raw_spin_lock(&oprofilefs_lock);
 165        op_perf_setup();
 166        raw_spin_unlock(&oprofilefs_lock);
 167        return 0;
 168}
 169
 170static int oprofile_perf_start(void)
 171{
 172        int ret = -EBUSY;
 173
 174        mutex_lock(&oprofile_perf_mutex);
 175        if (!oprofile_perf_enabled) {
 176                ret = 0;
 177                op_perf_start();
 178                oprofile_perf_enabled = 1;
 179        }
 180        mutex_unlock(&oprofile_perf_mutex);
 181        return ret;
 182}
 183
 184static void oprofile_perf_stop(void)
 185{
 186        mutex_lock(&oprofile_perf_mutex);
 187        if (oprofile_perf_enabled)
 188                op_perf_stop();
 189        oprofile_perf_enabled = 0;
 190        mutex_unlock(&oprofile_perf_mutex);
 191}
 192
 193#ifdef CONFIG_PM
 194
 195static int oprofile_perf_suspend(struct platform_device *dev, pm_message_t state)
 196{
 197        mutex_lock(&oprofile_perf_mutex);
 198        if (oprofile_perf_enabled)
 199                op_perf_stop();
 200        mutex_unlock(&oprofile_perf_mutex);
 201        return 0;
 202}
 203
 204static int oprofile_perf_resume(struct platform_device *dev)
 205{
 206        mutex_lock(&oprofile_perf_mutex);
 207        if (oprofile_perf_enabled && op_perf_start())
 208                oprofile_perf_enabled = 0;
 209        mutex_unlock(&oprofile_perf_mutex);
 210        return 0;
 211}
 212
 213static struct platform_driver oprofile_driver = {
 214        .driver         = {
 215                .name           = "oprofile-perf",
 216        },
 217        .resume         = oprofile_perf_resume,
 218        .suspend        = oprofile_perf_suspend,
 219};
 220
 221static struct platform_device *oprofile_pdev;
 222
 223static int __init init_driverfs(void)
 224{
 225        int ret;
 226
 227        ret = platform_driver_register(&oprofile_driver);
 228        if (ret)
 229                return ret;
 230
 231        oprofile_pdev = platform_device_register_simple(
 232                                oprofile_driver.driver.name, 0, NULL, 0);
 233        if (IS_ERR(oprofile_pdev)) {
 234                ret = PTR_ERR(oprofile_pdev);
 235                platform_driver_unregister(&oprofile_driver);
 236        }
 237
 238        return ret;
 239}
 240
 241static void exit_driverfs(void)
 242{
 243        platform_device_unregister(oprofile_pdev);
 244        platform_driver_unregister(&oprofile_driver);
 245}
 246
 247#else
 248
 249static inline int  init_driverfs(void) { return 0; }
 250static inline void exit_driverfs(void) { }
 251
 252#endif /* CONFIG_PM */
 253
 254void oprofile_perf_exit(void)
 255{
 256        int cpu, id;
 257        struct perf_event *event;
 258
 259        for_each_possible_cpu(cpu) {
 260                for (id = 0; id < num_counters; ++id) {
 261                        event = per_cpu(perf_events, cpu)[id];
 262                        if (event)
 263                                perf_event_release_kernel(event);
 264                }
 265
 266                kfree(per_cpu(perf_events, cpu));
 267        }
 268
 269        kfree(counter_config);
 270        exit_driverfs();
 271}
 272
 273int __init oprofile_perf_init(struct oprofile_operations *ops)
 274{
 275        int cpu, ret = 0;
 276
 277        ret = init_driverfs();
 278        if (ret)
 279                return ret;
 280
 281        num_counters = perf_num_counters();
 282        if (num_counters <= 0) {
 283                pr_info("oprofile: no performance counters\n");
 284                ret = -ENODEV;
 285                goto out;
 286        }
 287
 288        counter_config = kcalloc(num_counters,
 289                        sizeof(struct op_counter_config), GFP_KERNEL);
 290
 291        if (!counter_config) {
 292                pr_info("oprofile: failed to allocate %d "
 293                                "counters\n", num_counters);
 294                ret = -ENOMEM;
 295                num_counters = 0;
 296                goto out;
 297        }
 298
 299        for_each_possible_cpu(cpu) {
 300                per_cpu(perf_events, cpu) = kcalloc(num_counters,
 301                                sizeof(struct perf_event *), GFP_KERNEL);
 302                if (!per_cpu(perf_events, cpu)) {
 303                        pr_info("oprofile: failed to allocate %d perf events "
 304                                        "for cpu %d\n", num_counters, cpu);
 305                        ret = -ENOMEM;
 306                        goto out;
 307                }
 308        }
 309
 310        ops->create_files       = oprofile_perf_create_files;
 311        ops->setup              = oprofile_perf_setup;
 312        ops->start              = oprofile_perf_start;
 313        ops->stop               = oprofile_perf_stop;
 314        ops->shutdown           = oprofile_perf_stop;
 315        ops->cpu_type           = op_name_from_perf_id();
 316
 317        if (!ops->cpu_type)
 318                ret = -ENODEV;
 319        else
 320                pr_info("oprofile: using %s\n", ops->cpu_type);
 321
 322out:
 323        if (ret)
 324                oprofile_perf_exit();
 325
 326        return ret;
 327}
 328
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.