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