Commit b3b3a9b63f2deacfd59137e3781211d21a568ca9
Committed by
Robert Richter
1 parent
277dd98417
Exists in
master
and in
7 other branches
oprofile: fix linker errors
Commit e9677b3ce (oprofile, ARM: Use oprofile_arch_exit() to cleanup on failure) caused oprofile_perf_exit to be called in the cleanup path of oprofile_perf_init. The __exit tag for oprofile_perf_exit should therefore be dropped. The same has to be done for exit_driverfs as well, as this function is called from oprofile_perf_exit. Else, we get the following two linker errors. LD .tmp_vmlinux1 `oprofile_perf_exit' referenced in section `.init.text' of arch/arm/oprofile/built-in.o: defined in discarded section `.exit.text' of arch/arm/oprofile/built-in.o make: *** [.tmp_vmlinux1] Error 1 LD .tmp_vmlinux1 `exit_driverfs' referenced in section `.text' of arch/arm/oprofile/built-in.o: defined in discarded section `.exit.text' of arch/arm/oprofile/built-in.o make: *** [.tmp_vmlinux1] Error 1 Signed-off-by: Anand Gadiyar <gadiyar@ti.com> Cc: Will Deacon <will.deacon@arm.com> Signed-off-by: Robert Richter <robert.richter@amd.com>
Showing 2 changed files with 2 additions and 2 deletions Inline Diff
drivers/oprofile/oprofile_perf.c
1 | /* | 1 | /* |
2 | * Copyright 2010 ARM Ltd. | 2 | * Copyright 2010 ARM Ltd. |
3 | * | 3 | * |
4 | * Perf-events backend for OProfile. | 4 | * Perf-events backend for OProfile. |
5 | */ | 5 | */ |
6 | #include <linux/perf_event.h> | 6 | #include <linux/perf_event.h> |
7 | #include <linux/platform_device.h> | 7 | #include <linux/platform_device.h> |
8 | #include <linux/oprofile.h> | 8 | #include <linux/oprofile.h> |
9 | #include <linux/slab.h> | 9 | #include <linux/slab.h> |
10 | 10 | ||
11 | /* | 11 | /* |
12 | * Per performance monitor configuration as set via oprofilefs. | 12 | * Per performance monitor configuration as set via oprofilefs. |
13 | */ | 13 | */ |
14 | struct op_counter_config { | 14 | struct op_counter_config { |
15 | unsigned long count; | 15 | unsigned long count; |
16 | unsigned long enabled; | 16 | unsigned long enabled; |
17 | unsigned long event; | 17 | unsigned long event; |
18 | unsigned long unit_mask; | 18 | unsigned long unit_mask; |
19 | unsigned long kernel; | 19 | unsigned long kernel; |
20 | unsigned long user; | 20 | unsigned long user; |
21 | struct perf_event_attr attr; | 21 | struct perf_event_attr attr; |
22 | }; | 22 | }; |
23 | 23 | ||
24 | static int oprofile_perf_enabled; | 24 | static int oprofile_perf_enabled; |
25 | static DEFINE_MUTEX(oprofile_perf_mutex); | 25 | static DEFINE_MUTEX(oprofile_perf_mutex); |
26 | 26 | ||
27 | static struct op_counter_config *counter_config; | 27 | static struct op_counter_config *counter_config; |
28 | static struct perf_event **perf_events[nr_cpumask_bits]; | 28 | static struct perf_event **perf_events[nr_cpumask_bits]; |
29 | static int num_counters; | 29 | static int num_counters; |
30 | 30 | ||
31 | /* | 31 | /* |
32 | * Overflow callback for oprofile. | 32 | * Overflow callback for oprofile. |
33 | */ | 33 | */ |
34 | static void op_overflow_handler(struct perf_event *event, int unused, | 34 | static void op_overflow_handler(struct perf_event *event, int unused, |
35 | struct perf_sample_data *data, struct pt_regs *regs) | 35 | struct perf_sample_data *data, struct pt_regs *regs) |
36 | { | 36 | { |
37 | int id; | 37 | int id; |
38 | u32 cpu = smp_processor_id(); | 38 | u32 cpu = smp_processor_id(); |
39 | 39 | ||
40 | for (id = 0; id < num_counters; ++id) | 40 | for (id = 0; id < num_counters; ++id) |
41 | if (perf_events[cpu][id] == event) | 41 | if (perf_events[cpu][id] == event) |
42 | break; | 42 | break; |
43 | 43 | ||
44 | if (id != num_counters) | 44 | if (id != num_counters) |
45 | oprofile_add_sample(regs, id); | 45 | oprofile_add_sample(regs, id); |
46 | else | 46 | else |
47 | pr_warning("oprofile: ignoring spurious overflow " | 47 | pr_warning("oprofile: ignoring spurious overflow " |
48 | "on cpu %u\n", cpu); | 48 | "on cpu %u\n", cpu); |
49 | } | 49 | } |
50 | 50 | ||
51 | /* | 51 | /* |
52 | * Called by oprofile_perf_setup to create perf attributes to mirror the oprofile | 52 | * Called by oprofile_perf_setup to create perf attributes to mirror the oprofile |
53 | * settings in counter_config. Attributes are created as `pinned' events and | 53 | * settings in counter_config. Attributes are created as `pinned' events and |
54 | * so are permanently scheduled on the PMU. | 54 | * so are permanently scheduled on the PMU. |
55 | */ | 55 | */ |
56 | static void op_perf_setup(void) | 56 | static void op_perf_setup(void) |
57 | { | 57 | { |
58 | int i; | 58 | int i; |
59 | u32 size = sizeof(struct perf_event_attr); | 59 | u32 size = sizeof(struct perf_event_attr); |
60 | struct perf_event_attr *attr; | 60 | struct perf_event_attr *attr; |
61 | 61 | ||
62 | for (i = 0; i < num_counters; ++i) { | 62 | for (i = 0; i < num_counters; ++i) { |
63 | attr = &counter_config[i].attr; | 63 | attr = &counter_config[i].attr; |
64 | memset(attr, 0, size); | 64 | memset(attr, 0, size); |
65 | attr->type = PERF_TYPE_RAW; | 65 | attr->type = PERF_TYPE_RAW; |
66 | attr->size = size; | 66 | attr->size = size; |
67 | attr->config = counter_config[i].event; | 67 | attr->config = counter_config[i].event; |
68 | attr->sample_period = counter_config[i].count; | 68 | attr->sample_period = counter_config[i].count; |
69 | attr->pinned = 1; | 69 | attr->pinned = 1; |
70 | } | 70 | } |
71 | } | 71 | } |
72 | 72 | ||
73 | static int op_create_counter(int cpu, int event) | 73 | static int op_create_counter(int cpu, int event) |
74 | { | 74 | { |
75 | struct perf_event *pevent; | 75 | struct perf_event *pevent; |
76 | 76 | ||
77 | if (!counter_config[event].enabled || perf_events[cpu][event]) | 77 | if (!counter_config[event].enabled || perf_events[cpu][event]) |
78 | return 0; | 78 | return 0; |
79 | 79 | ||
80 | pevent = perf_event_create_kernel_counter(&counter_config[event].attr, | 80 | pevent = perf_event_create_kernel_counter(&counter_config[event].attr, |
81 | cpu, NULL, | 81 | cpu, NULL, |
82 | op_overflow_handler); | 82 | op_overflow_handler); |
83 | 83 | ||
84 | if (IS_ERR(pevent)) | 84 | if (IS_ERR(pevent)) |
85 | return PTR_ERR(pevent); | 85 | return PTR_ERR(pevent); |
86 | 86 | ||
87 | if (pevent->state != PERF_EVENT_STATE_ACTIVE) { | 87 | if (pevent->state != PERF_EVENT_STATE_ACTIVE) { |
88 | perf_event_release_kernel(pevent); | 88 | perf_event_release_kernel(pevent); |
89 | pr_warning("oprofile: failed to enable event %d " | 89 | pr_warning("oprofile: failed to enable event %d " |
90 | "on CPU %d\n", event, cpu); | 90 | "on CPU %d\n", event, cpu); |
91 | return -EBUSY; | 91 | return -EBUSY; |
92 | } | 92 | } |
93 | 93 | ||
94 | perf_events[cpu][event] = pevent; | 94 | perf_events[cpu][event] = pevent; |
95 | 95 | ||
96 | return 0; | 96 | return 0; |
97 | } | 97 | } |
98 | 98 | ||
99 | static void op_destroy_counter(int cpu, int event) | 99 | static void op_destroy_counter(int cpu, int event) |
100 | { | 100 | { |
101 | struct perf_event *pevent = perf_events[cpu][event]; | 101 | struct perf_event *pevent = perf_events[cpu][event]; |
102 | 102 | ||
103 | if (pevent) { | 103 | if (pevent) { |
104 | perf_event_release_kernel(pevent); | 104 | perf_event_release_kernel(pevent); |
105 | perf_events[cpu][event] = NULL; | 105 | perf_events[cpu][event] = NULL; |
106 | } | 106 | } |
107 | } | 107 | } |
108 | 108 | ||
109 | /* | 109 | /* |
110 | * Called by oprofile_perf_start to create active perf events based on the | 110 | * Called by oprofile_perf_start to create active perf events based on the |
111 | * perviously configured attributes. | 111 | * perviously configured attributes. |
112 | */ | 112 | */ |
113 | static int op_perf_start(void) | 113 | static int op_perf_start(void) |
114 | { | 114 | { |
115 | int cpu, event, ret = 0; | 115 | int cpu, event, ret = 0; |
116 | 116 | ||
117 | for_each_online_cpu(cpu) { | 117 | for_each_online_cpu(cpu) { |
118 | for (event = 0; event < num_counters; ++event) { | 118 | for (event = 0; event < num_counters; ++event) { |
119 | ret = op_create_counter(cpu, event); | 119 | ret = op_create_counter(cpu, event); |
120 | if (ret) | 120 | if (ret) |
121 | return ret; | 121 | return ret; |
122 | } | 122 | } |
123 | } | 123 | } |
124 | 124 | ||
125 | return ret; | 125 | return ret; |
126 | } | 126 | } |
127 | 127 | ||
128 | /* | 128 | /* |
129 | * Called by oprofile_perf_stop at the end of a profiling run. | 129 | * Called by oprofile_perf_stop at the end of a profiling run. |
130 | */ | 130 | */ |
131 | static void op_perf_stop(void) | 131 | static void op_perf_stop(void) |
132 | { | 132 | { |
133 | int cpu, event; | 133 | int cpu, event; |
134 | 134 | ||
135 | for_each_online_cpu(cpu) | 135 | for_each_online_cpu(cpu) |
136 | for (event = 0; event < num_counters; ++event) | 136 | for (event = 0; event < num_counters; ++event) |
137 | op_destroy_counter(cpu, event); | 137 | op_destroy_counter(cpu, event); |
138 | } | 138 | } |
139 | 139 | ||
140 | static int oprofile_perf_create_files(struct super_block *sb, struct dentry *root) | 140 | static int oprofile_perf_create_files(struct super_block *sb, struct dentry *root) |
141 | { | 141 | { |
142 | unsigned int i; | 142 | unsigned int i; |
143 | 143 | ||
144 | for (i = 0; i < num_counters; i++) { | 144 | for (i = 0; i < num_counters; i++) { |
145 | struct dentry *dir; | 145 | struct dentry *dir; |
146 | char buf[4]; | 146 | char buf[4]; |
147 | 147 | ||
148 | snprintf(buf, sizeof buf, "%d", i); | 148 | snprintf(buf, sizeof buf, "%d", i); |
149 | dir = oprofilefs_mkdir(sb, root, buf); | 149 | dir = oprofilefs_mkdir(sb, root, buf); |
150 | oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); | 150 | oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); |
151 | oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event); | 151 | oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event); |
152 | oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count); | 152 | oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count); |
153 | oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask); | 153 | oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask); |
154 | oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel); | 154 | oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel); |
155 | oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user); | 155 | oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user); |
156 | } | 156 | } |
157 | 157 | ||
158 | return 0; | 158 | return 0; |
159 | } | 159 | } |
160 | 160 | ||
161 | static int oprofile_perf_setup(void) | 161 | static int oprofile_perf_setup(void) |
162 | { | 162 | { |
163 | spin_lock(&oprofilefs_lock); | 163 | spin_lock(&oprofilefs_lock); |
164 | op_perf_setup(); | 164 | op_perf_setup(); |
165 | spin_unlock(&oprofilefs_lock); | 165 | spin_unlock(&oprofilefs_lock); |
166 | return 0; | 166 | return 0; |
167 | } | 167 | } |
168 | 168 | ||
169 | static int oprofile_perf_start(void) | 169 | static int oprofile_perf_start(void) |
170 | { | 170 | { |
171 | int ret = -EBUSY; | 171 | int ret = -EBUSY; |
172 | 172 | ||
173 | mutex_lock(&oprofile_perf_mutex); | 173 | mutex_lock(&oprofile_perf_mutex); |
174 | if (!oprofile_perf_enabled) { | 174 | if (!oprofile_perf_enabled) { |
175 | ret = 0; | 175 | ret = 0; |
176 | op_perf_start(); | 176 | op_perf_start(); |
177 | oprofile_perf_enabled = 1; | 177 | oprofile_perf_enabled = 1; |
178 | } | 178 | } |
179 | mutex_unlock(&oprofile_perf_mutex); | 179 | mutex_unlock(&oprofile_perf_mutex); |
180 | return ret; | 180 | return ret; |
181 | } | 181 | } |
182 | 182 | ||
183 | static void oprofile_perf_stop(void) | 183 | static void oprofile_perf_stop(void) |
184 | { | 184 | { |
185 | mutex_lock(&oprofile_perf_mutex); | 185 | mutex_lock(&oprofile_perf_mutex); |
186 | if (oprofile_perf_enabled) | 186 | if (oprofile_perf_enabled) |
187 | op_perf_stop(); | 187 | op_perf_stop(); |
188 | oprofile_perf_enabled = 0; | 188 | oprofile_perf_enabled = 0; |
189 | mutex_unlock(&oprofile_perf_mutex); | 189 | mutex_unlock(&oprofile_perf_mutex); |
190 | } | 190 | } |
191 | 191 | ||
192 | #ifdef CONFIG_PM | 192 | #ifdef CONFIG_PM |
193 | static int oprofile_perf_suspend(struct platform_device *dev, pm_message_t state) | 193 | static int oprofile_perf_suspend(struct platform_device *dev, pm_message_t state) |
194 | { | 194 | { |
195 | mutex_lock(&oprofile_perf_mutex); | 195 | mutex_lock(&oprofile_perf_mutex); |
196 | if (oprofile_perf_enabled) | 196 | if (oprofile_perf_enabled) |
197 | op_perf_stop(); | 197 | op_perf_stop(); |
198 | mutex_unlock(&oprofile_perf_mutex); | 198 | mutex_unlock(&oprofile_perf_mutex); |
199 | return 0; | 199 | return 0; |
200 | } | 200 | } |
201 | 201 | ||
202 | static int oprofile_perf_resume(struct platform_device *dev) | 202 | static int oprofile_perf_resume(struct platform_device *dev) |
203 | { | 203 | { |
204 | mutex_lock(&oprofile_perf_mutex); | 204 | mutex_lock(&oprofile_perf_mutex); |
205 | if (oprofile_perf_enabled && op_perf_start()) | 205 | if (oprofile_perf_enabled && op_perf_start()) |
206 | oprofile_perf_enabled = 0; | 206 | oprofile_perf_enabled = 0; |
207 | mutex_unlock(&oprofile_perf_mutex); | 207 | mutex_unlock(&oprofile_perf_mutex); |
208 | return 0; | 208 | return 0; |
209 | } | 209 | } |
210 | 210 | ||
211 | static struct platform_driver oprofile_driver = { | 211 | static struct platform_driver oprofile_driver = { |
212 | .driver = { | 212 | .driver = { |
213 | .name = "oprofile-perf", | 213 | .name = "oprofile-perf", |
214 | }, | 214 | }, |
215 | .resume = oprofile_perf_resume, | 215 | .resume = oprofile_perf_resume, |
216 | .suspend = oprofile_perf_suspend, | 216 | .suspend = oprofile_perf_suspend, |
217 | }; | 217 | }; |
218 | 218 | ||
219 | static struct platform_device *oprofile_pdev; | 219 | static struct platform_device *oprofile_pdev; |
220 | 220 | ||
221 | static int __init init_driverfs(void) | 221 | static int __init init_driverfs(void) |
222 | { | 222 | { |
223 | int ret; | 223 | int ret; |
224 | 224 | ||
225 | ret = platform_driver_register(&oprofile_driver); | 225 | ret = platform_driver_register(&oprofile_driver); |
226 | if (ret) | 226 | if (ret) |
227 | return ret; | 227 | return ret; |
228 | 228 | ||
229 | oprofile_pdev = platform_device_register_simple( | 229 | oprofile_pdev = platform_device_register_simple( |
230 | oprofile_driver.driver.name, 0, NULL, 0); | 230 | oprofile_driver.driver.name, 0, NULL, 0); |
231 | if (IS_ERR(oprofile_pdev)) { | 231 | if (IS_ERR(oprofile_pdev)) { |
232 | ret = PTR_ERR(oprofile_pdev); | 232 | ret = PTR_ERR(oprofile_pdev); |
233 | platform_driver_unregister(&oprofile_driver); | 233 | platform_driver_unregister(&oprofile_driver); |
234 | } | 234 | } |
235 | 235 | ||
236 | return ret; | 236 | return ret; |
237 | } | 237 | } |
238 | 238 | ||
239 | static void __exit exit_driverfs(void) | 239 | static void exit_driverfs(void) |
240 | { | 240 | { |
241 | platform_device_unregister(oprofile_pdev); | 241 | platform_device_unregister(oprofile_pdev); |
242 | platform_driver_unregister(&oprofile_driver); | 242 | platform_driver_unregister(&oprofile_driver); |
243 | } | 243 | } |
244 | #else | 244 | #else |
245 | static int __init init_driverfs(void) { return 0; } | 245 | static int __init init_driverfs(void) { return 0; } |
246 | #define exit_driverfs() do { } while (0) | 246 | #define exit_driverfs() do { } while (0) |
247 | #endif /* CONFIG_PM */ | 247 | #endif /* CONFIG_PM */ |
248 | 248 | ||
249 | void oprofile_perf_exit(void) | 249 | void oprofile_perf_exit(void) |
250 | { | 250 | { |
251 | int cpu, id; | 251 | int cpu, id; |
252 | struct perf_event *event; | 252 | struct perf_event *event; |
253 | 253 | ||
254 | for_each_possible_cpu(cpu) { | 254 | for_each_possible_cpu(cpu) { |
255 | for (id = 0; id < num_counters; ++id) { | 255 | for (id = 0; id < num_counters; ++id) { |
256 | event = perf_events[cpu][id]; | 256 | event = perf_events[cpu][id]; |
257 | if (event) | 257 | if (event) |
258 | perf_event_release_kernel(event); | 258 | perf_event_release_kernel(event); |
259 | } | 259 | } |
260 | 260 | ||
261 | kfree(perf_events[cpu]); | 261 | kfree(perf_events[cpu]); |
262 | } | 262 | } |
263 | 263 | ||
264 | kfree(counter_config); | 264 | kfree(counter_config); |
265 | exit_driverfs(); | 265 | exit_driverfs(); |
266 | } | 266 | } |
267 | 267 | ||
268 | int __init oprofile_perf_init(struct oprofile_operations *ops) | 268 | int __init oprofile_perf_init(struct oprofile_operations *ops) |
269 | { | 269 | { |
270 | int cpu, ret = 0; | 270 | int cpu, ret = 0; |
271 | 271 | ||
272 | ret = init_driverfs(); | 272 | ret = init_driverfs(); |
273 | if (ret) | 273 | if (ret) |
274 | return ret; | 274 | return ret; |
275 | 275 | ||
276 | memset(&perf_events, 0, sizeof(perf_events)); | 276 | memset(&perf_events, 0, sizeof(perf_events)); |
277 | 277 | ||
278 | num_counters = perf_num_counters(); | 278 | num_counters = perf_num_counters(); |
279 | if (num_counters <= 0) { | 279 | if (num_counters <= 0) { |
280 | pr_info("oprofile: no performance counters\n"); | 280 | pr_info("oprofile: no performance counters\n"); |
281 | ret = -ENODEV; | 281 | ret = -ENODEV; |
282 | goto out; | 282 | goto out; |
283 | } | 283 | } |
284 | 284 | ||
285 | counter_config = kcalloc(num_counters, | 285 | counter_config = kcalloc(num_counters, |
286 | sizeof(struct op_counter_config), GFP_KERNEL); | 286 | sizeof(struct op_counter_config), GFP_KERNEL); |
287 | 287 | ||
288 | if (!counter_config) { | 288 | if (!counter_config) { |
289 | pr_info("oprofile: failed to allocate %d " | 289 | pr_info("oprofile: failed to allocate %d " |
290 | "counters\n", num_counters); | 290 | "counters\n", num_counters); |
291 | ret = -ENOMEM; | 291 | ret = -ENOMEM; |
292 | num_counters = 0; | 292 | num_counters = 0; |
293 | goto out; | 293 | goto out; |
294 | } | 294 | } |
295 | 295 | ||
296 | for_each_possible_cpu(cpu) { | 296 | for_each_possible_cpu(cpu) { |
297 | perf_events[cpu] = kcalloc(num_counters, | 297 | perf_events[cpu] = kcalloc(num_counters, |
298 | sizeof(struct perf_event *), GFP_KERNEL); | 298 | sizeof(struct perf_event *), GFP_KERNEL); |
299 | if (!perf_events[cpu]) { | 299 | if (!perf_events[cpu]) { |
300 | pr_info("oprofile: failed to allocate %d perf events " | 300 | pr_info("oprofile: failed to allocate %d perf events " |
301 | "for cpu %d\n", num_counters, cpu); | 301 | "for cpu %d\n", num_counters, cpu); |
302 | ret = -ENOMEM; | 302 | ret = -ENOMEM; |
303 | goto out; | 303 | goto out; |
304 | } | 304 | } |
305 | } | 305 | } |
306 | 306 | ||
307 | ops->create_files = oprofile_perf_create_files; | 307 | ops->create_files = oprofile_perf_create_files; |
308 | ops->setup = oprofile_perf_setup; | 308 | ops->setup = oprofile_perf_setup; |
309 | ops->start = oprofile_perf_start; | 309 | ops->start = oprofile_perf_start; |
310 | ops->stop = oprofile_perf_stop; | 310 | ops->stop = oprofile_perf_stop; |
311 | ops->shutdown = oprofile_perf_stop; | 311 | ops->shutdown = oprofile_perf_stop; |
312 | ops->cpu_type = op_name_from_perf_id(); | 312 | ops->cpu_type = op_name_from_perf_id(); |
313 | 313 | ||
314 | if (!ops->cpu_type) | 314 | if (!ops->cpu_type) |
315 | ret = -ENODEV; | 315 | ret = -ENODEV; |
316 | else | 316 | else |
317 | pr_info("oprofile: using %s\n", ops->cpu_type); | 317 | pr_info("oprofile: using %s\n", ops->cpu_type); |
318 | 318 | ||
319 | out: | 319 | out: |
320 | if (ret) | 320 | if (ret) |
321 | oprofile_perf_exit(); | 321 | oprofile_perf_exit(); |
322 | 322 | ||
323 | return ret; | 323 | return ret; |
324 | } | 324 | } |
325 | 325 |
include/linux/oprofile.h
1 | /** | 1 | /** |
2 | * @file oprofile.h | 2 | * @file oprofile.h |
3 | * | 3 | * |
4 | * API for machine-specific interrupts to interface | 4 | * API for machine-specific interrupts to interface |
5 | * to oprofile. | 5 | * to oprofile. |
6 | * | 6 | * |
7 | * @remark Copyright 2002 OProfile authors | 7 | * @remark Copyright 2002 OProfile authors |
8 | * @remark Read the file COPYING | 8 | * @remark Read the file COPYING |
9 | * | 9 | * |
10 | * @author John Levon <levon@movementarian.org> | 10 | * @author John Levon <levon@movementarian.org> |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #ifndef OPROFILE_H | 13 | #ifndef OPROFILE_H |
14 | #define OPROFILE_H | 14 | #define OPROFILE_H |
15 | 15 | ||
16 | #include <linux/types.h> | 16 | #include <linux/types.h> |
17 | #include <linux/spinlock.h> | 17 | #include <linux/spinlock.h> |
18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
19 | #include <asm/atomic.h> | 19 | #include <asm/atomic.h> |
20 | 20 | ||
21 | /* Each escaped entry is prefixed by ESCAPE_CODE | 21 | /* Each escaped entry is prefixed by ESCAPE_CODE |
22 | * then one of the following codes, then the | 22 | * then one of the following codes, then the |
23 | * relevant data. | 23 | * relevant data. |
24 | * These #defines live in this file so that arch-specific | 24 | * These #defines live in this file so that arch-specific |
25 | * buffer sync'ing code can access them. | 25 | * buffer sync'ing code can access them. |
26 | */ | 26 | */ |
27 | #define ESCAPE_CODE ~0UL | 27 | #define ESCAPE_CODE ~0UL |
28 | #define CTX_SWITCH_CODE 1 | 28 | #define CTX_SWITCH_CODE 1 |
29 | #define CPU_SWITCH_CODE 2 | 29 | #define CPU_SWITCH_CODE 2 |
30 | #define COOKIE_SWITCH_CODE 3 | 30 | #define COOKIE_SWITCH_CODE 3 |
31 | #define KERNEL_ENTER_SWITCH_CODE 4 | 31 | #define KERNEL_ENTER_SWITCH_CODE 4 |
32 | #define KERNEL_EXIT_SWITCH_CODE 5 | 32 | #define KERNEL_EXIT_SWITCH_CODE 5 |
33 | #define MODULE_LOADED_CODE 6 | 33 | #define MODULE_LOADED_CODE 6 |
34 | #define CTX_TGID_CODE 7 | 34 | #define CTX_TGID_CODE 7 |
35 | #define TRACE_BEGIN_CODE 8 | 35 | #define TRACE_BEGIN_CODE 8 |
36 | #define TRACE_END_CODE 9 | 36 | #define TRACE_END_CODE 9 |
37 | #define XEN_ENTER_SWITCH_CODE 10 | 37 | #define XEN_ENTER_SWITCH_CODE 10 |
38 | #define SPU_PROFILING_CODE 11 | 38 | #define SPU_PROFILING_CODE 11 |
39 | #define SPU_CTX_SWITCH_CODE 12 | 39 | #define SPU_CTX_SWITCH_CODE 12 |
40 | #define IBS_FETCH_CODE 13 | 40 | #define IBS_FETCH_CODE 13 |
41 | #define IBS_OP_CODE 14 | 41 | #define IBS_OP_CODE 14 |
42 | 42 | ||
43 | struct super_block; | 43 | struct super_block; |
44 | struct dentry; | 44 | struct dentry; |
45 | struct file_operations; | 45 | struct file_operations; |
46 | struct pt_regs; | 46 | struct pt_regs; |
47 | 47 | ||
48 | /* Operations structure to be filled in */ | 48 | /* Operations structure to be filled in */ |
49 | struct oprofile_operations { | 49 | struct oprofile_operations { |
50 | /* create any necessary configuration files in the oprofile fs. | 50 | /* create any necessary configuration files in the oprofile fs. |
51 | * Optional. */ | 51 | * Optional. */ |
52 | int (*create_files)(struct super_block * sb, struct dentry * root); | 52 | int (*create_files)(struct super_block * sb, struct dentry * root); |
53 | /* Do any necessary interrupt setup. Optional. */ | 53 | /* Do any necessary interrupt setup. Optional. */ |
54 | int (*setup)(void); | 54 | int (*setup)(void); |
55 | /* Do any necessary interrupt shutdown. Optional. */ | 55 | /* Do any necessary interrupt shutdown. Optional. */ |
56 | void (*shutdown)(void); | 56 | void (*shutdown)(void); |
57 | /* Start delivering interrupts. */ | 57 | /* Start delivering interrupts. */ |
58 | int (*start)(void); | 58 | int (*start)(void); |
59 | /* Stop delivering interrupts. */ | 59 | /* Stop delivering interrupts. */ |
60 | void (*stop)(void); | 60 | void (*stop)(void); |
61 | /* Arch-specific buffer sync functions. | 61 | /* Arch-specific buffer sync functions. |
62 | * Return value = 0: Success | 62 | * Return value = 0: Success |
63 | * Return value = -1: Failure | 63 | * Return value = -1: Failure |
64 | * Return value = 1: Run generic sync function | 64 | * Return value = 1: Run generic sync function |
65 | */ | 65 | */ |
66 | int (*sync_start)(void); | 66 | int (*sync_start)(void); |
67 | int (*sync_stop)(void); | 67 | int (*sync_stop)(void); |
68 | 68 | ||
69 | /* Initiate a stack backtrace. Optional. */ | 69 | /* Initiate a stack backtrace. Optional. */ |
70 | void (*backtrace)(struct pt_regs * const regs, unsigned int depth); | 70 | void (*backtrace)(struct pt_regs * const regs, unsigned int depth); |
71 | 71 | ||
72 | /* Multiplex between different events. Optional. */ | 72 | /* Multiplex between different events. Optional. */ |
73 | int (*switch_events)(void); | 73 | int (*switch_events)(void); |
74 | /* CPU identification string. */ | 74 | /* CPU identification string. */ |
75 | char * cpu_type; | 75 | char * cpu_type; |
76 | }; | 76 | }; |
77 | 77 | ||
78 | /** | 78 | /** |
79 | * One-time initialisation. *ops must be set to a filled-in | 79 | * One-time initialisation. *ops must be set to a filled-in |
80 | * operations structure. This is called even in timer interrupt | 80 | * operations structure. This is called even in timer interrupt |
81 | * mode so an arch can set a backtrace callback. | 81 | * mode so an arch can set a backtrace callback. |
82 | * | 82 | * |
83 | * If an error occurs, the fields should be left untouched. | 83 | * If an error occurs, the fields should be left untouched. |
84 | */ | 84 | */ |
85 | int oprofile_arch_init(struct oprofile_operations * ops); | 85 | int oprofile_arch_init(struct oprofile_operations * ops); |
86 | 86 | ||
87 | /** | 87 | /** |
88 | * One-time exit/cleanup for the arch. | 88 | * One-time exit/cleanup for the arch. |
89 | */ | 89 | */ |
90 | void oprofile_arch_exit(void); | 90 | void oprofile_arch_exit(void); |
91 | 91 | ||
92 | /** | 92 | /** |
93 | * Add a sample. This may be called from any context. | 93 | * Add a sample. This may be called from any context. |
94 | */ | 94 | */ |
95 | void oprofile_add_sample(struct pt_regs * const regs, unsigned long event); | 95 | void oprofile_add_sample(struct pt_regs * const regs, unsigned long event); |
96 | 96 | ||
97 | /** | 97 | /** |
98 | * Add an extended sample. Use this when the PC is not from the regs, and | 98 | * Add an extended sample. Use this when the PC is not from the regs, and |
99 | * we cannot determine if we're in kernel mode from the regs. | 99 | * we cannot determine if we're in kernel mode from the regs. |
100 | * | 100 | * |
101 | * This function does perform a backtrace. | 101 | * This function does perform a backtrace. |
102 | * | 102 | * |
103 | */ | 103 | */ |
104 | void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, | 104 | void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, |
105 | unsigned long event, int is_kernel); | 105 | unsigned long event, int is_kernel); |
106 | 106 | ||
107 | /* Use this instead when the PC value is not from the regs. Doesn't | 107 | /* Use this instead when the PC value is not from the regs. Doesn't |
108 | * backtrace. */ | 108 | * backtrace. */ |
109 | void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event); | 109 | void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event); |
110 | 110 | ||
111 | /* add a backtrace entry, to be called from the ->backtrace callback */ | 111 | /* add a backtrace entry, to be called from the ->backtrace callback */ |
112 | void oprofile_add_trace(unsigned long eip); | 112 | void oprofile_add_trace(unsigned long eip); |
113 | 113 | ||
114 | 114 | ||
115 | /** | 115 | /** |
116 | * Create a file of the given name as a child of the given root, with | 116 | * Create a file of the given name as a child of the given root, with |
117 | * the specified file operations. | 117 | * the specified file operations. |
118 | */ | 118 | */ |
119 | int oprofilefs_create_file(struct super_block * sb, struct dentry * root, | 119 | int oprofilefs_create_file(struct super_block * sb, struct dentry * root, |
120 | char const * name, const struct file_operations * fops); | 120 | char const * name, const struct file_operations * fops); |
121 | 121 | ||
122 | int oprofilefs_create_file_perm(struct super_block * sb, struct dentry * root, | 122 | int oprofilefs_create_file_perm(struct super_block * sb, struct dentry * root, |
123 | char const * name, const struct file_operations * fops, int perm); | 123 | char const * name, const struct file_operations * fops, int perm); |
124 | 124 | ||
125 | /** Create a file for read/write access to an unsigned long. */ | 125 | /** Create a file for read/write access to an unsigned long. */ |
126 | int oprofilefs_create_ulong(struct super_block * sb, struct dentry * root, | 126 | int oprofilefs_create_ulong(struct super_block * sb, struct dentry * root, |
127 | char const * name, ulong * val); | 127 | char const * name, ulong * val); |
128 | 128 | ||
129 | /** Create a file for read-only access to an unsigned long. */ | 129 | /** Create a file for read-only access to an unsigned long. */ |
130 | int oprofilefs_create_ro_ulong(struct super_block * sb, struct dentry * root, | 130 | int oprofilefs_create_ro_ulong(struct super_block * sb, struct dentry * root, |
131 | char const * name, ulong * val); | 131 | char const * name, ulong * val); |
132 | 132 | ||
133 | /** Create a file for read-only access to an atomic_t. */ | 133 | /** Create a file for read-only access to an atomic_t. */ |
134 | int oprofilefs_create_ro_atomic(struct super_block * sb, struct dentry * root, | 134 | int oprofilefs_create_ro_atomic(struct super_block * sb, struct dentry * root, |
135 | char const * name, atomic_t * val); | 135 | char const * name, atomic_t * val); |
136 | 136 | ||
137 | /** create a directory */ | 137 | /** create a directory */ |
138 | struct dentry * oprofilefs_mkdir(struct super_block * sb, struct dentry * root, | 138 | struct dentry * oprofilefs_mkdir(struct super_block * sb, struct dentry * root, |
139 | char const * name); | 139 | char const * name); |
140 | 140 | ||
141 | /** | 141 | /** |
142 | * Write the given asciz string to the given user buffer @buf, updating *offset | 142 | * Write the given asciz string to the given user buffer @buf, updating *offset |
143 | * appropriately. Returns bytes written or -EFAULT. | 143 | * appropriately. Returns bytes written or -EFAULT. |
144 | */ | 144 | */ |
145 | ssize_t oprofilefs_str_to_user(char const * str, char __user * buf, size_t count, loff_t * offset); | 145 | ssize_t oprofilefs_str_to_user(char const * str, char __user * buf, size_t count, loff_t * offset); |
146 | 146 | ||
147 | /** | 147 | /** |
148 | * Convert an unsigned long value into ASCII and copy it to the user buffer @buf, | 148 | * Convert an unsigned long value into ASCII and copy it to the user buffer @buf, |
149 | * updating *offset appropriately. Returns bytes written or -EFAULT. | 149 | * updating *offset appropriately. Returns bytes written or -EFAULT. |
150 | */ | 150 | */ |
151 | ssize_t oprofilefs_ulong_to_user(unsigned long val, char __user * buf, size_t count, loff_t * offset); | 151 | ssize_t oprofilefs_ulong_to_user(unsigned long val, char __user * buf, size_t count, loff_t * offset); |
152 | 152 | ||
153 | /** | 153 | /** |
154 | * Read an ASCII string for a number from a userspace buffer and fill *val on success. | 154 | * Read an ASCII string for a number from a userspace buffer and fill *val on success. |
155 | * Returns 0 on success, < 0 on error. | 155 | * Returns 0 on success, < 0 on error. |
156 | */ | 156 | */ |
157 | int oprofilefs_ulong_from_user(unsigned long * val, char const __user * buf, size_t count); | 157 | int oprofilefs_ulong_from_user(unsigned long * val, char const __user * buf, size_t count); |
158 | 158 | ||
159 | /** lock for read/write safety */ | 159 | /** lock for read/write safety */ |
160 | extern spinlock_t oprofilefs_lock; | 160 | extern spinlock_t oprofilefs_lock; |
161 | 161 | ||
162 | /** | 162 | /** |
163 | * Add the contents of a circular buffer to the event buffer. | 163 | * Add the contents of a circular buffer to the event buffer. |
164 | */ | 164 | */ |
165 | void oprofile_put_buff(unsigned long *buf, unsigned int start, | 165 | void oprofile_put_buff(unsigned long *buf, unsigned int start, |
166 | unsigned int stop, unsigned int max); | 166 | unsigned int stop, unsigned int max); |
167 | 167 | ||
168 | unsigned long oprofile_get_cpu_buffer_size(void); | 168 | unsigned long oprofile_get_cpu_buffer_size(void); |
169 | void oprofile_cpu_buffer_inc_smpl_lost(void); | 169 | void oprofile_cpu_buffer_inc_smpl_lost(void); |
170 | 170 | ||
171 | /* cpu buffer functions */ | 171 | /* cpu buffer functions */ |
172 | 172 | ||
173 | struct op_sample; | 173 | struct op_sample; |
174 | 174 | ||
175 | struct op_entry { | 175 | struct op_entry { |
176 | struct ring_buffer_event *event; | 176 | struct ring_buffer_event *event; |
177 | struct op_sample *sample; | 177 | struct op_sample *sample; |
178 | unsigned long size; | 178 | unsigned long size; |
179 | unsigned long *data; | 179 | unsigned long *data; |
180 | }; | 180 | }; |
181 | 181 | ||
182 | void oprofile_write_reserve(struct op_entry *entry, | 182 | void oprofile_write_reserve(struct op_entry *entry, |
183 | struct pt_regs * const regs, | 183 | struct pt_regs * const regs, |
184 | unsigned long pc, int code, int size); | 184 | unsigned long pc, int code, int size); |
185 | int oprofile_add_data(struct op_entry *entry, unsigned long val); | 185 | int oprofile_add_data(struct op_entry *entry, unsigned long val); |
186 | int oprofile_add_data64(struct op_entry *entry, u64 val); | 186 | int oprofile_add_data64(struct op_entry *entry, u64 val); |
187 | int oprofile_write_commit(struct op_entry *entry); | 187 | int oprofile_write_commit(struct op_entry *entry); |
188 | 188 | ||
189 | #ifdef CONFIG_PERF_EVENTS | 189 | #ifdef CONFIG_PERF_EVENTS |
190 | int __init oprofile_perf_init(struct oprofile_operations *ops); | 190 | int __init oprofile_perf_init(struct oprofile_operations *ops); |
191 | void __exit oprofile_perf_exit(void); | 191 | void oprofile_perf_exit(void); |
192 | char *op_name_from_perf_id(void); | 192 | char *op_name_from_perf_id(void); |
193 | #endif /* CONFIG_PERF_EVENTS */ | 193 | #endif /* CONFIG_PERF_EVENTS */ |
194 | 194 | ||
195 | #endif /* OPROFILE_H */ | 195 | #endif /* OPROFILE_H */ |
196 | 196 |