Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.10-rc2 156 lines 3.1 kB view raw
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_online(unsigned int cpu) 63{ 64 nmi_timer_start_cpu(cpu); 65 return 0; 66} 67static int nmi_timer_cpu_predown(unsigned int cpu) 68{ 69 nmi_timer_stop_cpu(cpu); 70 return 0; 71} 72 73static int nmi_timer_start(void) 74{ 75 int cpu; 76 77 get_online_cpus(); 78 ctr_running = 1; 79 for_each_online_cpu(cpu) 80 nmi_timer_start_cpu(cpu); 81 put_online_cpus(); 82 83 return 0; 84} 85 86static void nmi_timer_stop(void) 87{ 88 int cpu; 89 90 get_online_cpus(); 91 for_each_online_cpu(cpu) 92 nmi_timer_stop_cpu(cpu); 93 ctr_running = 0; 94 put_online_cpus(); 95} 96 97static enum cpuhp_state hp_online; 98 99static void nmi_timer_shutdown(void) 100{ 101 struct perf_event *event; 102 int cpu; 103 104 cpuhp_remove_state(hp_online); 105 for_each_possible_cpu(cpu) { 106 event = per_cpu(nmi_timer_events, cpu); 107 if (!event) 108 continue; 109 perf_event_disable(event); 110 per_cpu(nmi_timer_events, cpu) = NULL; 111 perf_event_release_kernel(event); 112 } 113} 114 115static int nmi_timer_setup(void) 116{ 117 int err; 118 u64 period; 119 120 /* clock cycles per tick: */ 121 period = (u64)cpu_khz * 1000; 122 do_div(period, HZ); 123 nmi_timer_attr.sample_period = period; 124 125 err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "oprofile/nmi:online", 126 nmi_timer_cpu_online, nmi_timer_cpu_predown); 127 if (err < 0) { 128 nmi_timer_shutdown(); 129 return err; 130 } 131 hp_online = err; 132 return 0; 133} 134 135int __init op_nmi_timer_init(struct oprofile_operations *ops) 136{ 137 int err = 0; 138 139 err = nmi_timer_setup(); 140 if (err) 141 return err; 142 nmi_timer_shutdown(); /* only check, don't alloc */ 143 144 ops->create_files = NULL; 145 ops->setup = nmi_timer_setup; 146 ops->shutdown = nmi_timer_shutdown; 147 ops->start = nmi_timer_start; 148 ops->stop = nmi_timer_stop; 149 ops->cpu_type = "timer"; 150 151 printk(KERN_INFO "oprofile: using NMI timer interrupt.\n"); 152 153 return 0; 154} 155 156#endif