Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
oprofile_perf.c
Go to the documentation of this file.
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>
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;
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 static void op_overflow_handler(struct perf_event *event,
36  struct perf_sample_data *data, struct pt_regs *regs)
37 {
38  int 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  */
57 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  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 
74 static 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) {
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 
100 static void op_destroy_counter(int cpu, int event)
101 {
102  struct perf_event *pevent = per_cpu(perf_events, cpu)[event];
103 
104  if (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  */
114 static 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  */
132 static void op_perf_stop(void)
133 {
134  int cpu, event;
135 
137  for (event = 0; event < num_counters; ++event)
138  op_destroy_counter(cpu, event);
139 }
140 
141 static 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 
162 static 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 
170 static 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 
184 static 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 
195 static 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 
204 static 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 
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 static 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 
241 static void exit_driverfs(void)
242 {
243  platform_device_unregister(oprofile_pdev);
244  platform_driver_unregister(&oprofile_driver);
245 }
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 
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)
264  }
265 
266  kfree(per_cpu(perf_events, cpu));
267  }
268 
269  kfree(counter_config);
270  exit_driverfs();
271 }
272 
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 
322 out:
323  if (ret)
325 
326  return ret;
327 }