linux/drivers/oprofile/nmi_timer_int.c
<<
>>
Prefs
   1/**
   2 * @file nmi_timer_int.c
   3 *
   4 * @remark Copyright 2011 Advanced Micro Devices, Inc.
   5 *
   6 * @author Robert Richter <robert.richter@amd.com>
   7 */
   8
   9#include <linux/init.h>
  10#include <linux/smp.h>
  11#include <linux/errno.h>
  12#include <linux/oprofile.h>
  13#include <linux/perf_event.h>
  14
  15#ifdef CONFIG_OPROFILE_NMI_TIMER
  16
  17static DEFINE_PER_CPU(struct perf_event *, nmi_timer_events);
  18static int ctr_running;
  19
  20static struct perf_event_attr nmi_timer_attr = {
  21        .type           = PERF_TYPE_HARDWARE,
  22        .config         = PERF_COUNT_HW_CPU_CYCLES,
  23        .size           = sizeof(struct perf_event_attr),
  24        .pinned         = 1,
  25        .disabled       = 1,
  26};
  27
  28static void nmi_timer_callback(struct perf_event *event,
  29                               struct perf_sample_data *data,
  30                               struct pt_regs *regs)
  31{
  32        event->hw.interrupts = 0;       /* don't throttle interrupts */
  33        oprofile_add_sample(regs, 0);
  34}
  35
  36static int nmi_timer_start_cpu(int cpu)
  37{
  38        struct perf_event *event = per_cpu(nmi_timer_events, cpu);
  39
  40        if (!event) {
  41                event = perf_event_create_kernel_counter(&nmi_timer_attr, cpu, NULL,
  42                                                         nmi_timer_callback, NULL);
  43                if (IS_ERR(event))
  44                        return PTR_ERR(event);
  45                per_cpu(nmi_timer_events, cpu) = event;
  46        }
  47
  48        if (event && ctr_running)
  49                perf_event_enable(event);
  50
  51        return 0;
  52}
  53
  54static void nmi_timer_stop_cpu(int cpu)
  55{
  56        struct perf_event *event = per_cpu(nmi_timer_events, cpu);
  57
  58        if (event && ctr_running)
  59                perf_event_disable(event);
  60}
  61
  62static int nmi_timer_cpu_notifier(struct notifier_block *b, unsigned long action,
  63                                  void *data)
  64{
  65        int cpu = (unsigned long)data;
  66        switch (action) {
  67        case CPU_DOWN_FAILED:
  68        case CPU_ONLINE:
  69                nmi_timer_start_cpu(cpu);
  70                break;
  71        case CPU_DOWN_PREPARE:
  72                nmi_timer_stop_cpu(cpu);
  73                break;
  74        }
  75        return NOTIFY_DONE;
  76}
  77
  78static struct notifier_block nmi_timer_cpu_nb = {
  79        .notifier_call = nmi_timer_cpu_notifier
  80};
  81
  82static int nmi_timer_start(void)
  83{
  84        int cpu;
  85
  86        get_online_cpus();
  87        ctr_running = 1;
  88        for_each_online_cpu(cpu)
  89                nmi_timer_start_cpu(cpu);
  90        put_online_cpus();
  91
  92        return 0;
  93}
  94
  95static void nmi_timer_stop(void)
  96{
  97        int cpu;
  98
  99        get_online_cpus();
 100        for_each_online_cpu(cpu)
 101                nmi_timer_stop_cpu(cpu);
 102        ctr_running = 0;
 103        put_online_cpus();
 104}
 105
 106static void nmi_timer_shutdown(void)
 107{
 108        struct perf_event *event;
 109        int cpu;
 110
 111        cpu_notifier_register_begin();
 112        __unregister_cpu_notifier(&nmi_timer_cpu_nb);
 113        for_each_possible_cpu(cpu) {
 114                event = per_cpu(nmi_timer_events, cpu);
 115                if (!event)
 116                        continue;
 117                perf_event_disable(event);
 118                per_cpu(nmi_timer_events, cpu) = NULL;
 119                perf_event_release_kernel(event);
 120        }
 121
 122        cpu_notifier_register_done();
 123}
 124
 125static int nmi_timer_setup(void)
 126{
 127        int cpu, err;
 128        u64 period;
 129
 130        /* clock cycles per tick: */
 131        period = (u64)cpu_khz * 1000;
 132        do_div(period, HZ);
 133        nmi_timer_attr.sample_period = period;
 134
 135        cpu_notifier_register_begin();
 136        err = __register_cpu_notifier(&nmi_timer_cpu_nb);
 137        if (err)
 138                goto out;
 139
 140        /* can't attach events to offline cpus: */
 141        for_each_online_cpu(cpu) {
 142                err = nmi_timer_start_cpu(cpu);
 143                if (err) {
 144                        cpu_notifier_register_done();
 145                        nmi_timer_shutdown();
 146                        return err;
 147                }
 148        }
 149
 150out:
 151        cpu_notifier_register_done();
 152        return err;
 153}
 154
 155int __init op_nmi_timer_init(struct oprofile_operations *ops)
 156{
 157        int err = 0;
 158
 159        err = nmi_timer_setup();
 160        if (err)
 161                return err;
 162        nmi_timer_shutdown();           /* only check, don't alloc */
 163
 164        ops->create_files       = NULL;
 165        ops->setup              = nmi_timer_setup;
 166        ops->shutdown           = nmi_timer_shutdown;
 167        ops->start              = nmi_timer_start;
 168        ops->stop               = nmi_timer_stop;
 169        ops->cpu_type           = "timer";
 170
 171        printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
 172
 173        return 0;
 174}
 175
 176#endif
 177