summaryrefslogtreecommitdiffstats
path: root/arch/s390/oprofile/hwsampler_files.c
blob: 2e1da2449ba9ddc895e86e7d300a5fada9826d92 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/**
 * arch/s390/oprofile/hwsampler_files.c
 *
 * Copyright IBM Corp. 2010
 * Author: Mahesh Salgaonkar (mahesh@linux.vnet.ibm.com)
 */
#include <linux/oprofile.h>
#include <linux/errno.h>
#include <linux/fs.h>

#include "../../../drivers/oprofile/oprof.h"
#include "hwsampler.h"

#define DEFAULT_INTERVAL	4096

#define DEFAULT_SDBT_BLOCKS	1
#define DEFAULT_SDB_BLOCKS	511

static unsigned long oprofile_hw_interval = DEFAULT_INTERVAL;
static unsigned long oprofile_min_interval;
static unsigned long oprofile_max_interval;

static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS;
static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS;

static int hwsampler_file;
static int hwsampler_running;	/* start_mutex must be held to change */

static struct oprofile_operations timer_ops;

static int oprofile_hwsampler_start(void)
{
	int retval;

	hwsampler_running = hwsampler_file;

	if (!hwsampler_running)
		return timer_ops.start();

	retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks);
	if (retval)
		return retval;

	retval = hwsampler_start_all(oprofile_hw_interval);
	if (retval)
		hwsampler_deallocate();

	return retval;
}

static void oprofile_hwsampler_stop(void)
{
	if (!hwsampler_running) {
		timer_ops.stop();
		return;
	}

	hwsampler_stop_all();
	hwsampler_deallocate();
	return;
}

static ssize_t hwsampler_read(struct file *file, char __user *buf,
		size_t count, loff_t *offset)
{
	return oprofilefs_ulong_to_user(hwsampler_file, buf, count, offset);
}

static ssize_t hwsampler_write(struct file *file, char const __user *buf,
		size_t count, loff_t *offset)
{
	unsigned long val;
	int retval;

	if (*offset)
		return -EINVAL;

	retval = oprofilefs_ulong_from_user(&val, buf, count);
	if (retval)
		return retval;

	if (oprofile_started)
		/*
		 * save to do without locking as we set
		 * hwsampler_running in start() when start_mutex is
		 * held
		 */
		return -EBUSY;

	hwsampler_file = val;

	return count;
}

static const struct file_operations hwsampler_fops = {
	.read		= hwsampler_read,
	.write		= hwsampler_write,
};

static int oprofile_create_hwsampling_files(struct super_block *sb,
						struct dentry *root)
{
	struct dentry *hw_dir;

	/* reinitialize default values */
	hwsampler_file = 1;

	hw_dir = oprofilefs_mkdir(sb, root, "hwsampling");
	if (!hw_dir)
		return -EINVAL;

	oprofilefs_create_file(sb, hw_dir, "hwsampler", &hwsampler_fops);
	oprofilefs_create_ulong(sb, hw_dir, "hw_interval",
				&oprofile_hw_interval);
	oprofilefs_create_ro_ulong(sb, hw_dir, "hw_min_interval",
				&oprofile_min_interval);
	oprofilefs_create_ro_ulong(sb, hw_dir, "hw_max_interval",
				&oprofile_max_interval);
	oprofilefs_create_ulong(sb, hw_dir, "hw_sdbt_blocks",
				&oprofile_sdbt_blocks);

	return 0;
}

int oprofile_hwsampler_init(struct oprofile_operations* ops)
{
	if (hwsampler_setup())
		return -ENODEV;

	/*
	 * create hwsampler files only if hwsampler_setup() succeeds.
	 */
	oprofile_min_interval = hwsampler_query_min_interval();
	if (oprofile_min_interval < 0) {
		oprofile_min_interval = 0;
		return -ENODEV;
	}
	oprofile_max_interval = hwsampler_query_max_interval();
	if (oprofile_max_interval < 0) {
		oprofile_max_interval = 0;
		return -ENODEV;
	}

	if (oprofile_timer_init(ops))
		return -ENODEV;

	printk(KERN_INFO "oprofile: using hardware sampling\n");

	memcpy(&timer_ops, ops, sizeof(timer_ops));

	ops->start = oprofile_hwsampler_start;
	ops->stop = oprofile_hwsampler_stop;
	ops->create_files = oprofile_create_hwsampling_files;

	return 0;
}

void oprofile_hwsampler_exit(void)
{
	oprofile_timer_exit();
	hwsampler_shutdown();
}