Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1/*
  2 * S390 Version
  3 *   Copyright IBM Corp. 2002, 2011
  4 *   Author(s): Thomas Spatzier (tspat@de.ibm.com)
  5 *   Author(s): Mahesh Salgaonkar (mahesh@linux.vnet.ibm.com)
  6 *   Author(s): Heinz Graalfs (graalfs@linux.vnet.ibm.com)
  7 *   Author(s): Andreas Krebbel (krebbel@linux.vnet.ibm.com)
  8 *
  9 * @remark Copyright 2002-2011 OProfile authors
 10 */
 11
 12#include <linux/oprofile.h>
 13#include <linux/perf_event.h>
 14#include <linux/init.h>
 15#include <linux/errno.h>
 16#include <linux/fs.h>
 17#include <linux/module.h>
 18#include <asm/processor.h>
 19
 20#include "../../../drivers/oprofile/oprof.h"
 21
 22extern void s390_backtrace(struct pt_regs * const regs, unsigned int depth);
 23
 24#ifdef CONFIG_64BIT
 25
 26#include "hwsampler.h"
 27#include "op_counter.h"
 28
 29#define DEFAULT_INTERVAL	4127518
 30
 31#define DEFAULT_SDBT_BLOCKS	1
 32#define DEFAULT_SDB_BLOCKS	511
 33
 34static unsigned long oprofile_hw_interval = DEFAULT_INTERVAL;
 35static unsigned long oprofile_min_interval;
 36static unsigned long oprofile_max_interval;
 37
 38static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS;
 39static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS;
 40
 41static int hwsampler_enabled;
 42static int hwsampler_running;	/* start_mutex must be held to change */
 43static int hwsampler_available;
 44
 45static struct oprofile_operations timer_ops;
 46
 47struct op_counter_config counter_config;
 48
 49enum __force_cpu_type {
 50	reserved = 0,		/* do not force */
 51	timer,
 52};
 53static int force_cpu_type;
 54
 55static 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}
 67module_param_call(cpu_type, set_cpu_type, NULL, NULL, 0);
 68MODULE_PARM_DESC(cpu_type, "Force legacy basic mode sampling"
 69		           "(report cpu_type \"timer\"");
 70
 71static 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
 86static int oprofile_hwsampler_start(void)
 87{
 88	int retval;
 89
 90	hwsampler_running = hwsampler_enabled;
 91
 92	if (!hwsampler_running)
 93		return timer_ops.start();
 94
 95	retval = perf_reserve_sampling();
 96	if (retval)
 97		return retval;
 98
 99	retval = __oprofile_hwsampler_start();
100	if (retval)
101		perf_release_sampling();
102
103	return retval;
104}
105
106static 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();
115	perf_release_sampling();
116	return;
117}
118
119/*
120 * File ops used for:
121 * /dev/oprofile/0/enabled
122 * /dev/oprofile/hwsampling/hwsampler  (cpu_type = timer)
123 */
124
125static ssize_t hwsampler_read(struct file *file, char __user *buf,
126		size_t count, loff_t *offset)
127{
128	return oprofilefs_ulong_to_user(hwsampler_enabled, buf, count, offset);
129}
130
131static 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);
141	if (retval <= 0)
142		return retval;
143
144	if (val != 0 && val != 1)
145		return -EINVAL;
146
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
155	hwsampler_enabled = val;
156
157	return count;
158}
159
160static const struct file_operations hwsampler_fops = {
161	.read		= hwsampler_read,
162	.write		= hwsampler_write,
163};
164
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
173static 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
180static 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);
189	if (retval <= 0)
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
201static 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
216static 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
222static 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);
232	if (retval <= 0)
233		return retval;
234	if (val != 0)
235		return -EINVAL;
236	return count;
237}
238
239static 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
246static 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
253static 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);
263	if (retval <= 0)
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
274static 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
281static 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
288static 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);
298	if (retval <= 0)
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
309static 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
321static 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
327static 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);
337	if (retval <= 0)
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
360static const struct file_operations timer_enabled_fops = {
361	.read		= timer_enabled_read,
362	.write		= timer_enabled_write,
363};
364
365
366static int oprofile_create_hwsampling_files(struct dentry *root)
367{
368	struct dentry *dir;
369
370	dir = oprofilefs_mkdir(root, "timer");
371	if (!dir)
372		return -EINVAL;
373
374	oprofilefs_create_file(dir, "enabled", &timer_enabled_fops);
375
376	if (!hwsampler_available)
377		return 0;
378
379	/* reinitialize default values */
380	hwsampler_enabled = 1;
381	counter_config.kernel = 1;
382	counter_config.user = 1;
383
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
395		dir = oprofilefs_mkdir(root, "0");
396		if (!dir)
397			return -EINVAL;
398
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",
406					&oprofile_sdbt_blocks);
407
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		 */
415		dir = oprofilefs_mkdir(root, "hwsampling");
416		if (!dir)
417			return -EINVAL;
418
419		oprofilefs_create_file(dir, "hwsampler",
420				       &hwsampler_fops);
421		oprofilefs_create_file(dir, "hw_interval",
422				       &hw_interval_fops);
423		oprofilefs_create_ro_ulong(dir, "hw_min_interval",
424					   &oprofile_min_interval);
425		oprofilefs_create_ro_ulong(dir, "hw_max_interval",
426					   &oprofile_max_interval);
427		oprofilefs_create_ulong(dir, "hw_sdbt_blocks",
428					&oprofile_sdbt_blocks);
429	}
430	return 0;
431}
432
433static int oprofile_hwsampler_init(struct oprofile_operations *ops)
434{
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;
459		case 0x2827: case 0x2828: ops->cpu_type = "s390/zEC12"; break;
460		default: return -ENODEV;
461		}
462	}
463
464	if (hwsampler_setup())
465		return -ENODEV;
466
467	/*
468	 * Query the range for the sampling interval from the
469	 * hardware.
470	 */
471	oprofile_min_interval = hwsampler_query_min_interval();
472	if (oprofile_min_interval == 0)
473		return -ENODEV;
474	oprofile_max_interval = hwsampler_query_max_interval();
475	if (oprofile_max_interval == 0)
476		return -ENODEV;
477
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
484	printk(KERN_INFO "oprofile: System z hardware sampling "
485	       "facility found.\n");
486
487	ops->start = oprofile_hwsampler_start;
488	ops->stop = oprofile_hwsampler_stop;
489
490	return 0;
491}
492
493static void oprofile_hwsampler_exit(void)
494{
495	hwsampler_shutdown();
496}
497
498#endif /* CONFIG_64BIT */
499
500int __init oprofile_arch_init(struct oprofile_operations *ops)
501{
502	ops->backtrace = s390_backtrace;
503
504#ifdef CONFIG_64BIT
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;
514#else
515	return -ENODEV;
516#endif
517}
518
519void oprofile_arch_exit(void)
520{
521#ifdef CONFIG_64BIT
522	oprofile_hwsampler_exit();
523#endif
524}