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