Commit | Line | Data |
---|---|---|
a53c8fab | 1 | /* |
1da177e4 | 2 | * S390 Version |
a53c8fab | 3 | * Copyright IBM Corp. 2002, 2011 |
1da177e4 | 4 | * Author(s): Thomas Spatzier (tspat@de.ibm.com) |
c814d160 HG |
5 | * Author(s): Mahesh Salgaonkar (mahesh@linux.vnet.ibm.com) |
6 | * Author(s): Heinz Graalfs (graalfs@linux.vnet.ibm.com) | |
dd3c4670 | 7 | * Author(s): Andreas Krebbel (krebbel@linux.vnet.ibm.com) |
1da177e4 | 8 | * |
c814d160 | 9 | * @remark Copyright 2002-2011 OProfile authors |
1da177e4 LT |
10 | */ |
11 | ||
12 | #include <linux/oprofile.h> | |
e28bb79d | 13 | #include <linux/perf_event.h> |
1da177e4 LT |
14 | #include <linux/init.h> |
15 | #include <linux/errno.h> | |
c814d160 | 16 | #include <linux/fs.h> |
dd3c4670 AK |
17 | #include <linux/module.h> |
18 | #include <asm/processor.h> | |
c814d160 HG |
19 | |
20 | #include "../../../drivers/oprofile/oprof.h" | |
984e8486 HC |
21 | |
22 | extern void s390_backtrace(struct pt_regs * const regs, unsigned int depth); | |
23 | ||
24 | #ifdef CONFIG_64BIT | |
25 | ||
c814d160 | 26 | #include "hwsampler.h" |
dd3c4670 | 27 | #include "op_counter.h" |
c814d160 | 28 | |
68158234 | 29 | #define DEFAULT_INTERVAL 4127518 |
c814d160 HG |
30 | |
31 | #define DEFAULT_SDBT_BLOCKS 1 | |
32 | #define DEFAULT_SDB_BLOCKS 511 | |
33 | ||
34 | static unsigned long oprofile_hw_interval = DEFAULT_INTERVAL; | |
35 | static unsigned long oprofile_min_interval; | |
36 | static unsigned long oprofile_max_interval; | |
37 | ||
38 | static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS; | |
39 | static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS; | |
1da177e4 | 40 | |
dd3c4670 | 41 | static int hwsampler_enabled; |
c814d160 | 42 | static int hwsampler_running; /* start_mutex must be held to change */ |
dd3c4670 | 43 | static int hwsampler_available; |
c814d160 HG |
44 | |
45 | static struct oprofile_operations timer_ops; | |
d0f4c16f | 46 | |
dd3c4670 AK |
47 | struct op_counter_config counter_config; |
48 | ||
49 | enum __force_cpu_type { | |
50 | reserved = 0, /* do not force */ | |
51 | timer, | |
52 | }; | |
53 | static int force_cpu_type; | |
54 | ||
55 | static int set_cpu_type(const char *str, struct kernel_param *kp) | |
56 | { | |
57 | if (!strcmp(str, "timer")) { | |
58 | force_cpu_type = timer; | |
59 | printk(KERN_INFO "oprofile: forcing timer to be returned " | |
60 | "as cpu type\n"); | |
61 | } else { | |
62 | force_cpu_type = 0; | |
63 | } | |
64 | ||
65 | return 0; | |
66 | } | |
67 | module_param_call(cpu_type, set_cpu_type, NULL, NULL, 0); | |
68 | MODULE_PARM_DESC(cpu_type, "Force legacy basic mode sampling" | |
69 | "(report cpu_type \"timer\""); | |
70 | ||
e28bb79d HB |
71 | static int __oprofile_hwsampler_start(void) |
72 | { | |
73 | int retval; | |
74 | ||
75 | retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks); | |
76 | if (retval) | |
77 | return retval; | |
78 | ||
79 | retval = hwsampler_start_all(oprofile_hw_interval); | |
80 | if (retval) | |
81 | hwsampler_deallocate(); | |
82 | ||
83 | return retval; | |
84 | } | |
85 | ||
c814d160 HG |
86 | static int oprofile_hwsampler_start(void) |
87 | { | |
88 | int retval; | |
89 | ||
dd3c4670 | 90 | hwsampler_running = hwsampler_enabled; |
c814d160 HG |
91 | |
92 | if (!hwsampler_running) | |
93 | return timer_ops.start(); | |
94 | ||
e28bb79d | 95 | retval = perf_reserve_sampling(); |
c814d160 HG |
96 | if (retval) |
97 | return retval; | |
98 | ||
e28bb79d | 99 | retval = __oprofile_hwsampler_start(); |
c814d160 | 100 | if (retval) |
e28bb79d | 101 | perf_release_sampling(); |
c814d160 HG |
102 | |
103 | return retval; | |
104 | } | |
105 | ||
106 | static void oprofile_hwsampler_stop(void) | |
107 | { | |
108 | if (!hwsampler_running) { | |
109 | timer_ops.stop(); | |
110 | return; | |
111 | } | |
112 | ||
113 | hwsampler_stop_all(); | |
114 | hwsampler_deallocate(); | |
e28bb79d | 115 | perf_release_sampling(); |
c814d160 HG |
116 | return; |
117 | } | |
118 | ||
dd3c4670 AK |
119 | /* |
120 | * File ops used for: | |
121 | * /dev/oprofile/0/enabled | |
122 | * /dev/oprofile/hwsampling/hwsampler (cpu_type = timer) | |
123 | */ | |
124 | ||
c814d160 HG |
125 | static ssize_t hwsampler_read(struct file *file, char __user *buf, |
126 | size_t count, loff_t *offset) | |
127 | { | |
dd3c4670 | 128 | return oprofilefs_ulong_to_user(hwsampler_enabled, buf, count, offset); |
c814d160 HG |
129 | } |
130 | ||
131 | static ssize_t hwsampler_write(struct file *file, char const __user *buf, | |
132 | size_t count, loff_t *offset) | |
133 | { | |
134 | unsigned long val; | |
135 | int retval; | |
136 | ||
137 | if (*offset) | |
138 | return -EINVAL; | |
139 | ||
140 | retval = oprofilefs_ulong_from_user(&val, buf, count); | |
913050b9 | 141 | if (retval <= 0) |
c814d160 HG |
142 | return retval; |
143 | ||
dd3c4670 AK |
144 | if (val != 0 && val != 1) |
145 | return -EINVAL; | |
146 | ||
c814d160 HG |
147 | if (oprofile_started) |
148 | /* | |
149 | * save to do without locking as we set | |
150 | * hwsampler_running in start() when start_mutex is | |
151 | * held | |
152 | */ | |
153 | return -EBUSY; | |
154 | ||
dd3c4670 | 155 | hwsampler_enabled = val; |
c814d160 HG |
156 | |
157 | return count; | |
158 | } | |
159 | ||
160 | static const struct file_operations hwsampler_fops = { | |
161 | .read = hwsampler_read, | |
162 | .write = hwsampler_write, | |
163 | }; | |
164 | ||
dd3c4670 AK |
165 | /* |
166 | * File ops used for: | |
167 | * /dev/oprofile/0/count | |
168 | * /dev/oprofile/hwsampling/hw_interval (cpu_type = timer) | |
169 | * | |
170 | * Make sure that the value is within the hardware range. | |
171 | */ | |
172 | ||
173 | static ssize_t hw_interval_read(struct file *file, char __user *buf, | |
174 | size_t count, loff_t *offset) | |
175 | { | |
176 | return oprofilefs_ulong_to_user(oprofile_hw_interval, buf, | |
177 | count, offset); | |
178 | } | |
179 | ||
180 | static ssize_t hw_interval_write(struct file *file, char const __user *buf, | |
181 | size_t count, loff_t *offset) | |
182 | { | |
183 | unsigned long val; | |
184 | int retval; | |
185 | ||
186 | if (*offset) | |
187 | return -EINVAL; | |
188 | retval = oprofilefs_ulong_from_user(&val, buf, count); | |
81ff3478 | 189 | if (retval <= 0) |
dd3c4670 AK |
190 | return retval; |
191 | if (val < oprofile_min_interval) | |
192 | oprofile_hw_interval = oprofile_min_interval; | |
193 | else if (val > oprofile_max_interval) | |
194 | oprofile_hw_interval = oprofile_max_interval; | |
195 | else | |
196 | oprofile_hw_interval = val; | |
197 | ||
198 | return count; | |
199 | } | |
200 | ||
201 | static const struct file_operations hw_interval_fops = { | |
202 | .read = hw_interval_read, | |
203 | .write = hw_interval_write, | |
204 | }; | |
205 | ||
206 | /* | |
207 | * File ops used for: | |
208 | * /dev/oprofile/0/event | |
209 | * Only a single event with number 0 is supported with this counter. | |
210 | * | |
211 | * /dev/oprofile/0/unit_mask | |
212 | * This is a dummy file needed by the user space tools. | |
213 | * No value other than 0 is accepted or returned. | |
214 | */ | |
215 | ||
216 | static ssize_t hwsampler_zero_read(struct file *file, char __user *buf, | |
217 | size_t count, loff_t *offset) | |
218 | { | |
219 | return oprofilefs_ulong_to_user(0, buf, count, offset); | |
220 | } | |
221 | ||
222 | static ssize_t hwsampler_zero_write(struct file *file, char const __user *buf, | |
223 | size_t count, loff_t *offset) | |
224 | { | |
225 | unsigned long val; | |
226 | int retval; | |
227 | ||
228 | if (*offset) | |
229 | return -EINVAL; | |
230 | ||
231 | retval = oprofilefs_ulong_from_user(&val, buf, count); | |
81ff3478 | 232 | if (retval <= 0) |
dd3c4670 AK |
233 | return retval; |
234 | if (val != 0) | |
235 | return -EINVAL; | |
236 | return count; | |
237 | } | |
238 | ||
239 | static const struct file_operations zero_fops = { | |
240 | .read = hwsampler_zero_read, | |
241 | .write = hwsampler_zero_write, | |
242 | }; | |
243 | ||
244 | /* /dev/oprofile/0/kernel file ops. */ | |
245 | ||
246 | static ssize_t hwsampler_kernel_read(struct file *file, char __user *buf, | |
247 | size_t count, loff_t *offset) | |
248 | { | |
249 | return oprofilefs_ulong_to_user(counter_config.kernel, | |
250 | buf, count, offset); | |
251 | } | |
252 | ||
253 | static ssize_t hwsampler_kernel_write(struct file *file, char const __user *buf, | |
254 | size_t count, loff_t *offset) | |
255 | { | |
256 | unsigned long val; | |
257 | int retval; | |
258 | ||
259 | if (*offset) | |
260 | return -EINVAL; | |
261 | ||
262 | retval = oprofilefs_ulong_from_user(&val, buf, count); | |
81ff3478 | 263 | if (retval <= 0) |
dd3c4670 AK |
264 | return retval; |
265 | ||
266 | if (val != 0 && val != 1) | |
267 | return -EINVAL; | |
268 | ||
269 | counter_config.kernel = val; | |
270 | ||
271 | return count; | |
272 | } | |
273 | ||
274 | static const struct file_operations kernel_fops = { | |
275 | .read = hwsampler_kernel_read, | |
276 | .write = hwsampler_kernel_write, | |
277 | }; | |
278 | ||
279 | /* /dev/oprofile/0/user file ops. */ | |
280 | ||
281 | static ssize_t hwsampler_user_read(struct file *file, char __user *buf, | |
282 | size_t count, loff_t *offset) | |
283 | { | |
284 | return oprofilefs_ulong_to_user(counter_config.user, | |
285 | buf, count, offset); | |
286 | } | |
287 | ||
288 | static ssize_t hwsampler_user_write(struct file *file, char const __user *buf, | |
289 | size_t count, loff_t *offset) | |
290 | { | |
291 | unsigned long val; | |
292 | int retval; | |
293 | ||
294 | if (*offset) | |
295 | return -EINVAL; | |
296 | ||
297 | retval = oprofilefs_ulong_from_user(&val, buf, count); | |
81ff3478 | 298 | if (retval <= 0) |
dd3c4670 AK |
299 | return retval; |
300 | ||
301 | if (val != 0 && val != 1) | |
302 | return -EINVAL; | |
303 | ||
304 | counter_config.user = val; | |
305 | ||
306 | return count; | |
307 | } | |
308 | ||
309 | static const struct file_operations user_fops = { | |
310 | .read = hwsampler_user_read, | |
311 | .write = hwsampler_user_write, | |
312 | }; | |
313 | ||
314 | ||
315 | /* | |
316 | * File ops used for: /dev/oprofile/timer/enabled | |
317 | * The value always has to be the inverted value of hwsampler_enabled. So | |
318 | * no separate variable is created. That way we do not need locking. | |
319 | */ | |
320 | ||
321 | static ssize_t timer_enabled_read(struct file *file, char __user *buf, | |
322 | size_t count, loff_t *offset) | |
323 | { | |
324 | return oprofilefs_ulong_to_user(!hwsampler_enabled, buf, count, offset); | |
325 | } | |
326 | ||
327 | static ssize_t timer_enabled_write(struct file *file, char const __user *buf, | |
328 | size_t count, loff_t *offset) | |
329 | { | |
330 | unsigned long val; | |
331 | int retval; | |
332 | ||
333 | if (*offset) | |
334 | return -EINVAL; | |
335 | ||
336 | retval = oprofilefs_ulong_from_user(&val, buf, count); | |
81ff3478 | 337 | if (retval <= 0) |
dd3c4670 AK |
338 | return retval; |
339 | ||
340 | if (val != 0 && val != 1) | |
341 | return -EINVAL; | |
342 | ||
343 | /* Timer cannot be disabled without having hardware sampling. */ | |
344 | if (val == 0 && !hwsampler_available) | |
345 | return -EINVAL; | |
346 | ||
347 | if (oprofile_started) | |
348 | /* | |
349 | * save to do without locking as we set | |
350 | * hwsampler_running in start() when start_mutex is | |
351 | * held | |
352 | */ | |
353 | return -EBUSY; | |
354 | ||
355 | hwsampler_enabled = !val; | |
356 | ||
357 | return count; | |
358 | } | |
359 | ||
360 | static const struct file_operations timer_enabled_fops = { | |
361 | .read = timer_enabled_read, | |
362 | .write = timer_enabled_write, | |
363 | }; | |
364 | ||
365 | ||
ef7bca14 | 366 | static int oprofile_create_hwsampling_files(struct dentry *root) |
c814d160 | 367 | { |
dd3c4670 AK |
368 | struct dentry *dir; |
369 | ||
ecde2823 | 370 | dir = oprofilefs_mkdir(root, "timer"); |
dd3c4670 AK |
371 | if (!dir) |
372 | return -EINVAL; | |
373 | ||
6af4ea0b | 374 | oprofilefs_create_file(dir, "enabled", &timer_enabled_fops); |
dd3c4670 AK |
375 | |
376 | if (!hwsampler_available) | |
377 | return 0; | |
c814d160 HG |
378 | |
379 | /* reinitialize default values */ | |
dd3c4670 AK |
380 | hwsampler_enabled = 1; |
381 | counter_config.kernel = 1; | |
382 | counter_config.user = 1; | |
c814d160 | 383 | |
dd3c4670 AK |
384 | if (!force_cpu_type) { |
385 | /* | |
386 | * Create the counter file system. A single virtual | |
387 | * counter is created which can be used to | |
388 | * enable/disable hardware sampling dynamically from | |
389 | * user space. The user space will configure a single | |
390 | * counter with a single event. The value of 'event' | |
391 | * and 'unit_mask' are not evaluated by the kernel code | |
392 | * and can only be set to 0. | |
393 | */ | |
394 | ||
ecde2823 | 395 | dir = oprofilefs_mkdir(root, "0"); |
dd3c4670 AK |
396 | if (!dir) |
397 | return -EINVAL; | |
c814d160 | 398 | |
6af4ea0b AV |
399 | oprofilefs_create_file(dir, "enabled", &hwsampler_fops); |
400 | oprofilefs_create_file(dir, "event", &zero_fops); | |
401 | oprofilefs_create_file(dir, "count", &hw_interval_fops); | |
402 | oprofilefs_create_file(dir, "unit_mask", &zero_fops); | |
403 | oprofilefs_create_file(dir, "kernel", &kernel_fops); | |
404 | oprofilefs_create_file(dir, "user", &user_fops); | |
405 | oprofilefs_create_ulong(dir, "hw_sdbt_blocks", | |
dd3c4670 | 406 | &oprofile_sdbt_blocks); |
c814d160 | 407 | |
dd3c4670 AK |
408 | } else { |
409 | /* | |
410 | * Hardware sampling can be used but the cpu_type is | |
411 | * forced to timer in order to deal with legacy user | |
412 | * space tools. The /dev/oprofile/hwsampling fs is | |
413 | * provided in that case. | |
414 | */ | |
ecde2823 | 415 | dir = oprofilefs_mkdir(root, "hwsampling"); |
dd3c4670 AK |
416 | if (!dir) |
417 | return -EINVAL; | |
418 | ||
6af4ea0b | 419 | oprofilefs_create_file(dir, "hwsampler", |
dd3c4670 | 420 | &hwsampler_fops); |
6af4ea0b | 421 | oprofilefs_create_file(dir, "hw_interval", |
dd3c4670 | 422 | &hw_interval_fops); |
6af4ea0b | 423 | oprofilefs_create_ro_ulong(dir, "hw_min_interval", |
dd3c4670 | 424 | &oprofile_min_interval); |
6af4ea0b | 425 | oprofilefs_create_ro_ulong(dir, "hw_max_interval", |
dd3c4670 | 426 | &oprofile_max_interval); |
6af4ea0b | 427 | oprofilefs_create_ulong(dir, "hw_sdbt_blocks", |
dd3c4670 AK |
428 | &oprofile_sdbt_blocks); |
429 | } | |
c814d160 HG |
430 | return 0; |
431 | } | |
432 | ||
ec6b426c | 433 | static int oprofile_hwsampler_init(struct oprofile_operations *ops) |
c814d160 | 434 | { |
dd3c4670 AK |
435 | /* |
436 | * Initialize the timer mode infrastructure as well in order | |
437 | * to be able to switch back dynamically. oprofile_timer_init | |
438 | * is not supposed to fail. | |
439 | */ | |
440 | if (oprofile_timer_init(ops)) | |
441 | BUG(); | |
442 | ||
443 | memcpy(&timer_ops, ops, sizeof(timer_ops)); | |
444 | ops->create_files = oprofile_create_hwsampling_files; | |
445 | ||
446 | /* | |
447 | * If the user space tools do not support newer cpu types, | |
448 | * the force_cpu_type module parameter | |
449 | * can be used to always return \"timer\" as cpu type. | |
450 | */ | |
451 | if (force_cpu_type != timer) { | |
452 | struct cpuid id; | |
453 | ||
454 | get_cpu_id (&id); | |
455 | ||
456 | switch (id.machine) { | |
457 | case 0x2097: case 0x2098: ops->cpu_type = "s390/z10"; break; | |
458 | case 0x2817: case 0x2818: ops->cpu_type = "s390/z196"; break; | |
59471227 | 459 | case 0x2827: case 0x2828: ops->cpu_type = "s390/zEC12"; break; |
dd3c4670 AK |
460 | default: return -ENODEV; |
461 | } | |
462 | } | |
463 | ||
c814d160 HG |
464 | if (hwsampler_setup()) |
465 | return -ENODEV; | |
466 | ||
467 | /* | |
dd3c4670 AK |
468 | * Query the range for the sampling interval from the |
469 | * hardware. | |
c814d160 HG |
470 | */ |
471 | oprofile_min_interval = hwsampler_query_min_interval(); | |
3d8dcb3c | 472 | if (oprofile_min_interval == 0) |
c814d160 | 473 | return -ENODEV; |
c814d160 | 474 | oprofile_max_interval = hwsampler_query_max_interval(); |
3d8dcb3c | 475 | if (oprofile_max_interval == 0) |
c814d160 | 476 | return -ENODEV; |
c814d160 | 477 | |
b530ce7a CB |
478 | /* The initial value should be sane */ |
479 | if (oprofile_hw_interval < oprofile_min_interval) | |
480 | oprofile_hw_interval = oprofile_min_interval; | |
481 | if (oprofile_hw_interval > oprofile_max_interval) | |
482 | oprofile_hw_interval = oprofile_max_interval; | |
483 | ||
dd3c4670 AK |
484 | printk(KERN_INFO "oprofile: System z hardware sampling " |
485 | "facility found.\n"); | |
c814d160 HG |
486 | |
487 | ops->start = oprofile_hwsampler_start; | |
488 | ops->stop = oprofile_hwsampler_stop; | |
c814d160 HG |
489 | |
490 | return 0; | |
491 | } | |
492 | ||
ec6b426c | 493 | static void oprofile_hwsampler_exit(void) |
c814d160 | 494 | { |
c814d160 HG |
495 | hwsampler_shutdown(); |
496 | } | |
497 | ||
984e8486 HC |
498 | #endif /* CONFIG_64BIT */ |
499 | ||
ec6b426c | 500 | int __init oprofile_arch_init(struct oprofile_operations *ops) |
1da177e4 | 501 | { |
d0f4c16f | 502 | ops->backtrace = s390_backtrace; |
997dbb49 | 503 | |
984e8486 | 504 | #ifdef CONFIG_64BIT |
dd3c4670 AK |
505 | |
506 | /* | |
507 | * -ENODEV is not reported to the caller. The module itself | |
508 | * will use the timer mode sampling as fallback and this is | |
509 | * always available. | |
510 | */ | |
511 | hwsampler_available = oprofile_hwsampler_init(ops) == 0; | |
512 | ||
513 | return 0; | |
984e8486 HC |
514 | #else |
515 | return -ENODEV; | |
516 | #endif | |
1da177e4 LT |
517 | } |
518 | ||
519 | void oprofile_arch_exit(void) | |
520 | { | |
984e8486 | 521 | #ifdef CONFIG_64BIT |
997dbb49 | 522 | oprofile_hwsampler_exit(); |
984e8486 | 523 | #endif |
1da177e4 | 524 | } |