aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorMatt Fleming <matt@console-pimps.org>2010-09-27 15:45:08 -0400
committerRobert Richter <robert.richter@amd.com>2010-10-11 11:46:16 -0400
commit3d90a00763b51e1db344a7430c966be723b67a29 (patch)
tree16d089339f161eb1e719b4b9b61e59e572587656 /arch/arm
parent58850e210cd207399cf6461326e322541b2ec81c (diff)
oprofile: Abstract the perf-events backend
Move the perf-events backend from arch/arm/oprofile into drivers/oprofile so that the code can be shared between architectures. This allows each architecture to maintain only a single copy of the PMU accessor functions instead of one for both perf and OProfile. It also becomes possible for other architectures to delete much of their OProfile code in favour of the common code now available in drivers/oprofile/oprofile_perf.c. Signed-off-by: Matt Fleming <matt@console-pimps.org> Tested-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Robert Richter <robert.richter@amd.com>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/oprofile/Makefile4
-rw-r--r--arch/arm/oprofile/common.c319
2 files changed, 4 insertions, 319 deletions
diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile
index e666eafed152..b2215c61cdf0 100644
--- a/arch/arm/oprofile/Makefile
+++ b/arch/arm/oprofile/Makefile
@@ -6,4 +6,8 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
6 oprofilefs.o oprofile_stats.o \ 6 oprofilefs.o oprofile_stats.o \
7 timer_int.o ) 7 timer_int.o )
8 8
9ifeq ($(CONFIG_HW_PERF_EVENTS),y)
10DRIVER_OBJS += $(addprefix ../../../drivers/oprofile/, oprofile_perf.o)
11endif
12
9oprofile-y := $(DRIVER_OBJS) common.o 13oprofile-y := $(DRIVER_OBJS) common.o
diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c
index 8718311cb530..8aa974491dfc 100644
--- a/arch/arm/oprofile/common.c
+++ b/arch/arm/oprofile/common.c
@@ -25,136 +25,6 @@
25#include <asm/ptrace.h> 25#include <asm/ptrace.h>
26 26
27#ifdef CONFIG_HW_PERF_EVENTS 27#ifdef CONFIG_HW_PERF_EVENTS
28/*
29 * Per performance monitor configuration as set via oprofilefs.
30 */
31struct op_counter_config {
32 unsigned long count;
33 unsigned long enabled;
34 unsigned long event;
35 unsigned long unit_mask;
36 unsigned long kernel;
37 unsigned long user;
38 struct perf_event_attr attr;
39};
40
41static int oprofile_perf_enabled;
42static DEFINE_MUTEX(oprofile_perf_mutex);
43
44static struct op_counter_config *counter_config;
45static struct perf_event **perf_events[nr_cpumask_bits];
46static int num_counters;
47
48/*
49 * Overflow callback for oprofile.
50 */
51static void op_overflow_handler(struct perf_event *event, int unused,
52 struct perf_sample_data *data, struct pt_regs *regs)
53{
54 int id;
55 u32 cpu = smp_processor_id();
56
57 for (id = 0; id < num_counters; ++id)
58 if (perf_events[cpu][id] == event)
59 break;
60
61 if (id != num_counters)
62 oprofile_add_sample(regs, id);
63 else
64 pr_warning("oprofile: ignoring spurious overflow "
65 "on cpu %u\n", cpu);
66}
67
68/*
69 * Called by oprofile_perf_setup to create perf attributes to mirror the oprofile
70 * settings in counter_config. Attributes are created as `pinned' events and
71 * so are permanently scheduled on the PMU.
72 */
73static void op_perf_setup(void)
74{
75 int i;
76 u32 size = sizeof(struct perf_event_attr);
77 struct perf_event_attr *attr;
78
79 for (i = 0; i < num_counters; ++i) {
80 attr = &counter_config[i].attr;
81 memset(attr, 0, size);
82 attr->type = PERF_TYPE_RAW;
83 attr->size = size;
84 attr->config = counter_config[i].event;
85 attr->sample_period = counter_config[i].count;
86 attr->pinned = 1;
87 }
88}
89
90static int op_create_counter(int cpu, int event)
91{
92 int ret = 0;
93 struct perf_event *pevent;
94
95 if (!counter_config[event].enabled || (perf_events[cpu][event] != NULL))
96 return ret;
97
98 pevent = perf_event_create_kernel_counter(&counter_config[event].attr,
99 cpu, -1,
100 op_overflow_handler);
101
102 if (IS_ERR(pevent)) {
103 ret = PTR_ERR(pevent);
104 } else if (pevent->state != PERF_EVENT_STATE_ACTIVE) {
105 pr_warning("oprofile: failed to enable event %d "
106 "on CPU %d\n", event, cpu);
107 ret = -EBUSY;
108 } else {
109 perf_events[cpu][event] = pevent;
110 }
111
112 return ret;
113}
114
115static void op_destroy_counter(int cpu, int event)
116{
117 struct perf_event *pevent = perf_events[cpu][event];
118
119 if (pevent) {
120 perf_event_release_kernel(pevent);
121 perf_events[cpu][event] = NULL;
122 }
123}
124
125/*
126 * Called by oprofile_perf_start to create active perf events based on the
127 * perviously configured attributes.
128 */
129static int op_perf_start(void)
130{
131 int cpu, event, ret = 0;
132
133 for_each_online_cpu(cpu) {
134 for (event = 0; event < num_counters; ++event) {
135 ret = op_create_counter(cpu, event);
136 if (ret)
137 goto out;
138 }
139 }
140
141out:
142 return ret;
143}
144
145/*
146 * Called by oprofile_perf_stop at the end of a profiling run.
147 */
148static void op_perf_stop(void)
149{
150 int cpu, event;
151
152 for_each_online_cpu(cpu)
153 for (event = 0; event < num_counters; ++event)
154 op_destroy_counter(cpu, event);
155}
156
157
158char *op_name_from_perf_id(void) 28char *op_name_from_perf_id(void)
159{ 29{
160 enum arm_perf_pmu_ids id = armpmu_get_pmu_id(); 30 enum arm_perf_pmu_ids id = armpmu_get_pmu_id();
@@ -177,116 +47,6 @@ char *op_name_from_perf_id(void)
177 } 47 }
178} 48}
179 49
180static int oprofile_perf_create_files(struct super_block *sb, struct dentry *root)
181{
182 unsigned int i;
183
184 for (i = 0; i < num_counters; i++) {
185 struct dentry *dir;
186 char buf[4];
187
188 snprintf(buf, sizeof buf, "%d", i);
189 dir = oprofilefs_mkdir(sb, root, buf);
190 oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled);
191 oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event);
192 oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count);
193 oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask);
194 oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel);
195 oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user);
196 }
197
198 return 0;
199}
200
201static int oprofile_perf_setup(void)
202{
203 spin_lock(&oprofilefs_lock);
204 op_perf_setup();
205 spin_unlock(&oprofilefs_lock);
206 return 0;
207}
208
209static int oprofile_perf_start(void)
210{
211 int ret = -EBUSY;
212
213 mutex_lock(&oprofile_perf_mutex);
214 if (!oprofile_perf_enabled) {
215 ret = 0;
216 op_perf_start();
217 oprofile_perf_enabled = 1;
218 }
219 mutex_unlock(&oprofile_perf_mutex);
220 return ret;
221}
222
223static void oprofile_perf_stop(void)
224{
225 mutex_lock(&oprofile_perf_mutex);
226 if (oprofile_perf_enabled)
227 op_perf_stop();
228 oprofile_perf_enabled = 0;
229 mutex_unlock(&oprofile_perf_mutex);
230}
231
232#ifdef CONFIG_PM
233static int oprofile_perf_suspend(struct platform_device *dev, pm_message_t state)
234{
235 mutex_lock(&oprofile_perf_mutex);
236 if (oprofile_perf_enabled)
237 op_perf_stop();
238 mutex_unlock(&oprofile_perf_mutex);
239 return 0;
240}
241
242static int oprofile_perf_resume(struct platform_device *dev)
243{
244 mutex_lock(&oprofile_perf_mutex);
245 if (oprofile_perf_enabled && op_perf_start())
246 oprofile_perf_enabled = 0;
247 mutex_unlock(&oprofile_perf_mutex);
248 return 0;
249}
250
251static struct platform_driver oprofile_driver = {
252 .driver = {
253 .name = "oprofile-perf",
254 },
255 .resume = oprofile_perf_resume,
256 .suspend = oprofile_perf_suspend,
257};
258
259static struct platform_device *oprofile_pdev;
260
261static int __init init_driverfs(void)
262{
263 int ret;
264
265 ret = platform_driver_register(&oprofile_driver);
266 if (ret)
267 goto out;
268
269 oprofile_pdev = platform_device_register_simple(
270 oprofile_driver.driver.name, 0, NULL, 0);
271 if (IS_ERR(oprofile_pdev)) {
272 ret = PTR_ERR(oprofile_pdev);
273 platform_driver_unregister(&oprofile_driver);
274 }
275
276out:
277 return ret;
278}
279
280static void __exit exit_driverfs(void)
281{
282 platform_device_unregister(oprofile_pdev);
283 platform_driver_unregister(&oprofile_driver);
284}
285#else
286static int __init init_driverfs(void) { return 0; }
287#define exit_driverfs() do { } while (0)
288#endif /* CONFIG_PM */
289
290static int report_trace(struct stackframe *frame, void *d) 50static int report_trace(struct stackframe *frame, void *d)
291{ 51{
292 unsigned int *depth = d; 52 unsigned int *depth = d;
@@ -349,66 +109,6 @@ static void arm_backtrace(struct pt_regs * const regs, unsigned int depth)
349 tail = user_backtrace(tail); 109 tail = user_backtrace(tail);
350} 110}
351 111
352int __init oprofile_perf_init(struct oprofile_operations *ops)
353{
354 int cpu, ret = 0;
355
356 memset(&perf_events, 0, sizeof(perf_events));
357
358 num_counters = perf_num_counters();
359 if (num_counters <= 0) {
360 pr_info("oprofile: no performance counters\n");
361 ret = -ENODEV;
362 goto out;
363 }
364
365 counter_config = kcalloc(num_counters,
366 sizeof(struct op_counter_config), GFP_KERNEL);
367
368 if (!counter_config) {
369 pr_info("oprofile: failed to allocate %d "
370 "counters\n", num_counters);
371 ret = -ENOMEM;
372 goto out;
373 }
374
375 ret = init_driverfs();
376 if (ret)
377 goto out;
378
379 for_each_possible_cpu(cpu) {
380 perf_events[cpu] = kcalloc(num_counters,
381 sizeof(struct perf_event *), GFP_KERNEL);
382 if (!perf_events[cpu]) {
383 pr_info("oprofile: failed to allocate %d perf events "
384 "for cpu %d\n", num_counters, cpu);
385 ret = -ENOMEM;
386 goto out;
387 }
388 }
389
390 ops->create_files = oprofile_perf_create_files;
391 ops->setup = oprofile_perf_setup;
392 ops->start = oprofile_perf_start;
393 ops->stop = oprofile_perf_stop;
394 ops->shutdown = oprofile_perf_stop;
395 ops->cpu_type = op_name_from_perf_id();
396
397 if (!ops->cpu_type)
398 ret = -ENODEV;
399 else
400 pr_info("oprofile: using %s\n", ops->cpu_type);
401
402out:
403 if (ret) {
404 for_each_possible_cpu(cpu)
405 kfree(perf_events[cpu]);
406 kfree(counter_config);
407 }
408
409 return ret;
410}
411
412int __init oprofile_arch_init(struct oprofile_operations *ops) 112int __init oprofile_arch_init(struct oprofile_operations *ops)
413{ 113{
414 ops->backtrace = arm_backtrace; 114 ops->backtrace = arm_backtrace;
@@ -416,25 +116,6 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
416 return oprofile_perf_init(ops); 116 return oprofile_perf_init(ops);
417} 117}
418 118
419void __exit oprofile_perf_exit(void)
420{
421 int cpu, id;
422 struct perf_event *event;
423
424 for_each_possible_cpu(cpu) {
425 for (id = 0; id < num_counters; ++id) {
426 event = perf_events[cpu][id];
427 if (event)
428 perf_event_release_kernel(event);
429 }
430
431 kfree(perf_events[cpu]);
432 }
433
434 kfree(counter_config);
435 exit_driverfs();
436}
437
438void __exit oprofile_arch_exit(void) 119void __exit oprofile_arch_exit(void)
439{ 120{
440 oprofile_perf_exit(); 121 oprofile_perf_exit();