powerpc: Account time using timebase rather than PURR
[linux-2.6-block.git] / arch / powerpc / platforms / pseries / dtl.c
CommitLineData
fc59a3fc
JK
1/*
2 * Virtual Processor Dispatch Trace Log
3 *
4 * (C) Copyright IBM Corporation 2009
5 *
6 * Author: Jeremy Kerr <jk@ozlabs.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23#include <linux/init.h>
5a0e3ad6 24#include <linux/slab.h>
fc59a3fc
JK
25#include <linux/debugfs.h>
26#include <asm/smp.h>
27#include <asm/system.h>
28#include <asm/uaccess.h>
b71a0c29 29#include <asm/firmware.h>
cf9efce0 30#include <asm/lppaca.h>
fc59a3fc
JK
31
32#include "plpar_wrappers.h"
33
fc59a3fc
JK
34struct dtl {
35 struct dtl_entry *buf;
36 struct dentry *file;
37 int cpu;
38 int buf_entries;
39 u64 last_idx;
40};
6b7487fc 41static DEFINE_PER_CPU(struct dtl, cpu_dtl);
fc59a3fc
JK
42
43/*
44 * Dispatch trace log event mask:
45 * 0x7: 0x1: voluntary virtual processor waits
46 * 0x2: time-slice preempts
47 * 0x4: virtual partition memory page faults
48 */
49static u8 dtl_event_mask = 0x7;
50
51
52/*
53 * Size of per-cpu log buffers. Default is just under 16 pages worth.
54 */
55static int dtl_buf_entries = (16 * 85);
56
57
58static int dtl_enable(struct dtl *dtl)
59{
60 unsigned long addr;
61 int ret, hwcpu;
62
63 /* only allow one reader */
64 if (dtl->buf)
65 return -EBUSY;
66
67 /* we need to store the original allocation size for use during read */
68 dtl->buf_entries = dtl_buf_entries;
69
70 dtl->buf = kmalloc_node(dtl->buf_entries * sizeof(struct dtl_entry),
71 GFP_KERNEL, cpu_to_node(dtl->cpu));
72 if (!dtl->buf) {
73 printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n",
74 __func__, dtl->cpu);
75 return -ENOMEM;
76 }
77
78 /* Register our dtl buffer with the hypervisor. The HV expects the
79 * buffer size to be passed in the second word of the buffer */
80 ((u32 *)dtl->buf)[1] = dtl->buf_entries * sizeof(struct dtl_entry);
81
82 hwcpu = get_hard_smp_processor_id(dtl->cpu);
83 addr = __pa(dtl->buf);
84 ret = register_dtl(hwcpu, addr);
85 if (ret) {
86 printk(KERN_WARNING "%s: DTL registration for cpu %d (hw %d) "
87 "failed with %d\n", __func__, dtl->cpu, hwcpu, ret);
88 kfree(dtl->buf);
89 return -EIO;
90 }
91
92 /* set our initial buffer indices */
8154c5d2 93 dtl->last_idx = lppaca_of(dtl->cpu).dtl_idx = 0;
fc59a3fc 94
82631f5d
JK
95 /* ensure that our updates to the lppaca fields have occurred before
96 * we actually enable the logging */
97 smp_wmb();
98
fc59a3fc 99 /* enable event logging */
8154c5d2 100 lppaca_of(dtl->cpu).dtl_enable_mask = dtl_event_mask;
fc59a3fc
JK
101
102 return 0;
103}
104
105static void dtl_disable(struct dtl *dtl)
106{
107 int hwcpu = get_hard_smp_processor_id(dtl->cpu);
108
8154c5d2 109 lppaca_of(dtl->cpu).dtl_enable_mask = 0x0;
fc59a3fc
JK
110
111 unregister_dtl(hwcpu, __pa(dtl->buf));
112
113 kfree(dtl->buf);
114 dtl->buf = NULL;
115 dtl->buf_entries = 0;
116}
117
118/* file interface */
119
120static int dtl_file_open(struct inode *inode, struct file *filp)
121{
122 struct dtl *dtl = inode->i_private;
123 int rc;
124
125 rc = dtl_enable(dtl);
126 if (rc)
127 return rc;
128
129 filp->private_data = dtl;
130 return 0;
131}
132
133static int dtl_file_release(struct inode *inode, struct file *filp)
134{
135 struct dtl *dtl = inode->i_private;
136 dtl_disable(dtl);
137 return 0;
138}
139
140static ssize_t dtl_file_read(struct file *filp, char __user *buf, size_t len,
141 loff_t *pos)
142{
143 int rc, cur_idx, last_idx, n_read, n_req, read_size;
144 struct dtl *dtl;
145
146 if ((len % sizeof(struct dtl_entry)) != 0)
147 return -EINVAL;
148
149 dtl = filp->private_data;
150
151 /* requested number of entries to read */
152 n_req = len / sizeof(struct dtl_entry);
153
154 /* actual number of entries read */
155 n_read = 0;
156
8154c5d2 157 cur_idx = lppaca_of(dtl->cpu).dtl_idx;
fc59a3fc
JK
158 last_idx = dtl->last_idx;
159
160 if (cur_idx - last_idx > dtl->buf_entries) {
161 pr_debug("%s: hv buffer overflow for cpu %d, samples lost\n",
162 __func__, dtl->cpu);
163 }
164
165 cur_idx %= dtl->buf_entries;
166 last_idx %= dtl->buf_entries;
167
168 /* read the tail of the buffer if we've wrapped */
169 if (last_idx > cur_idx) {
170 read_size = min(n_req, dtl->buf_entries - last_idx);
171
172 rc = copy_to_user(buf, &dtl->buf[last_idx],
173 read_size * sizeof(struct dtl_entry));
174 if (rc)
175 return -EFAULT;
176
177 last_idx = 0;
178 n_req -= read_size;
179 n_read += read_size;
180 buf += read_size * sizeof(struct dtl_entry);
181 }
182
183 /* .. and now the head */
184 read_size = min(n_req, cur_idx - last_idx);
185 rc = copy_to_user(buf, &dtl->buf[last_idx],
186 read_size * sizeof(struct dtl_entry));
187 if (rc)
188 return -EFAULT;
189
190 n_read += read_size;
191 dtl->last_idx += n_read;
192
193 return n_read * sizeof(struct dtl_entry);
194}
195
828c0950 196static const struct file_operations dtl_fops = {
fc59a3fc
JK
197 .open = dtl_file_open,
198 .release = dtl_file_release,
199 .read = dtl_file_read,
200 .llseek = no_llseek,
201};
202
203static struct dentry *dtl_dir;
204
205static int dtl_setup_file(struct dtl *dtl)
206{
207 char name[10];
208
209 sprintf(name, "cpu-%d", dtl->cpu);
210
211 dtl->file = debugfs_create_file(name, 0400, dtl_dir, dtl, &dtl_fops);
212 if (!dtl->file)
213 return -ENOMEM;
214
215 return 0;
216}
217
218static int dtl_init(void)
219{
220 struct dentry *event_mask_file, *buf_entries_file;
221 int rc, i;
222
cf9efce0
PM
223#ifdef CONFIG_VIRT_CPU_ACCOUNTING
224 /* disable this for now */
225 return -ENODEV;
226#endif
227
fc59a3fc
JK
228 if (!firmware_has_feature(FW_FEATURE_SPLPAR))
229 return -ENODEV;
230
231 /* set up common debugfs structure */
232
233 rc = -ENOMEM;
234 dtl_dir = debugfs_create_dir("dtl", powerpc_debugfs_root);
235 if (!dtl_dir) {
236 printk(KERN_WARNING "%s: can't create dtl root dir\n",
237 __func__);
238 goto err;
239 }
240
241 event_mask_file = debugfs_create_x8("dtl_event_mask", 0600,
242 dtl_dir, &dtl_event_mask);
243 buf_entries_file = debugfs_create_u32("dtl_buf_entries", 0600,
244 dtl_dir, &dtl_buf_entries);
245
246 if (!event_mask_file || !buf_entries_file) {
247 printk(KERN_WARNING "%s: can't create dtl files\n", __func__);
248 goto err_remove_dir;
249 }
250
251 /* set up the per-cpu log structures */
252 for_each_possible_cpu(i) {
6b7487fc 253 struct dtl *dtl = &per_cpu(cpu_dtl, i);
fc59a3fc
JK
254 dtl->cpu = i;
255
256 rc = dtl_setup_file(dtl);
257 if (rc)
258 goto err_remove_dir;
259 }
260
261 return 0;
262
263err_remove_dir:
264 debugfs_remove_recursive(dtl_dir);
265err:
266 return rc;
267}
268arch_initcall(dtl_init);