aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/pseries/dtl.c
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2009-03-11 13:55:52 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2009-03-23 22:47:28 -0400
commitfc59a3fc8eed3a2c811e64ec65015d7eb1459ace (patch)
treeb2ba8d071b92e85ba74ca59c402e0c63d4be1b95 /arch/powerpc/platforms/pseries/dtl.c
parent098e8957afd86323db04cbb8deea3bd158f9cc71 (diff)
powerpc: Add virtual processor dispatch trace log
pseries SPLPAR machines are able to retrieve a log of dispatch and preempt events from the hypervisor. With this information, we can see when and why each dispatch & preempt is occuring. This change adds a set of debugfs files allowing userspace to read this dispatch log. Based on initial patches from Nishanth Aravamudan <nacc@us.ibm.com>. Signed-off-by: Jeremy Kerr <jk@ozlabs.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms/pseries/dtl.c')
-rw-r--r--arch/powerpc/platforms/pseries/dtl.c274
1 files changed, 274 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
new file mode 100644
index 000000000000..dc9b0f81e60f
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/dtl.c
@@ -0,0 +1,274 @@
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>
24#include <linux/debugfs.h>
25#include <asm/smp.h>
26#include <asm/system.h>
27#include <asm/uaccess.h>
28
29#include "plpar_wrappers.h"
30
31/*
32 * Layout of entries in the hypervisor's DTL buffer. Although we don't
33 * actually access the internals of an entry (we only need to know the size),
34 * we might as well define it here for reference.
35 */
36struct dtl_entry {
37 u8 dispatch_reason;
38 u8 preempt_reason;
39 u16 processor_id;
40 u32 enqueue_to_dispatch_time;
41 u32 ready_to_enqueue_time;
42 u32 waiting_to_ready_time;
43 u64 timebase;
44 u64 fault_addr;
45 u64 srr0;
46 u64 srr1;
47};
48
49struct dtl {
50 struct dtl_entry *buf;
51 struct dentry *file;
52 int cpu;
53 int buf_entries;
54 u64 last_idx;
55};
56static DEFINE_PER_CPU(struct dtl, dtl);
57
58/*
59 * Dispatch trace log event mask:
60 * 0x7: 0x1: voluntary virtual processor waits
61 * 0x2: time-slice preempts
62 * 0x4: virtual partition memory page faults
63 */
64static u8 dtl_event_mask = 0x7;
65
66
67/*
68 * Size of per-cpu log buffers. Default is just under 16 pages worth.
69 */
70static int dtl_buf_entries = (16 * 85);
71
72
73static int dtl_enable(struct dtl *dtl)
74{
75 unsigned long addr;
76 int ret, hwcpu;
77
78 /* only allow one reader */
79 if (dtl->buf)
80 return -EBUSY;
81
82 /* we need to store the original allocation size for use during read */
83 dtl->buf_entries = dtl_buf_entries;
84
85 dtl->buf = kmalloc_node(dtl->buf_entries * sizeof(struct dtl_entry),
86 GFP_KERNEL, cpu_to_node(dtl->cpu));
87 if (!dtl->buf) {
88 printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n",
89 __func__, dtl->cpu);
90 return -ENOMEM;
91 }
92
93 /* Register our dtl buffer with the hypervisor. The HV expects the
94 * buffer size to be passed in the second word of the buffer */
95 ((u32 *)dtl->buf)[1] = dtl->buf_entries * sizeof(struct dtl_entry);
96
97 hwcpu = get_hard_smp_processor_id(dtl->cpu);
98 addr = __pa(dtl->buf);
99 ret = register_dtl(hwcpu, addr);
100 if (ret) {
101 printk(KERN_WARNING "%s: DTL registration for cpu %d (hw %d) "
102 "failed with %d\n", __func__, dtl->cpu, hwcpu, ret);
103 kfree(dtl->buf);
104 return -EIO;
105 }
106
107 /* set our initial buffer indices */
108 dtl->last_idx = lppaca[dtl->cpu].dtl_idx = 0;
109
110 /* enable event logging */
111 lppaca[dtl->cpu].dtl_enable_mask = dtl_event_mask;
112
113 return 0;
114}
115
116static void dtl_disable(struct dtl *dtl)
117{
118 int hwcpu = get_hard_smp_processor_id(dtl->cpu);
119
120 lppaca[dtl->cpu].dtl_enable_mask = 0x0;
121
122 unregister_dtl(hwcpu, __pa(dtl->buf));
123
124 kfree(dtl->buf);
125 dtl->buf = NULL;
126 dtl->buf_entries = 0;
127}
128
129/* file interface */
130
131static int dtl_file_open(struct inode *inode, struct file *filp)
132{
133 struct dtl *dtl = inode->i_private;
134 int rc;
135
136 rc = dtl_enable(dtl);
137 if (rc)
138 return rc;
139
140 filp->private_data = dtl;
141 return 0;
142}
143
144static int dtl_file_release(struct inode *inode, struct file *filp)
145{
146 struct dtl *dtl = inode->i_private;
147 dtl_disable(dtl);
148 return 0;
149}
150
151static ssize_t dtl_file_read(struct file *filp, char __user *buf, size_t len,
152 loff_t *pos)
153{
154 int rc, cur_idx, last_idx, n_read, n_req, read_size;
155 struct dtl *dtl;
156
157 if ((len % sizeof(struct dtl_entry)) != 0)
158 return -EINVAL;
159
160 dtl = filp->private_data;
161
162 /* requested number of entries to read */
163 n_req = len / sizeof(struct dtl_entry);
164
165 /* actual number of entries read */
166 n_read = 0;
167
168 cur_idx = lppaca[dtl->cpu].dtl_idx;
169 last_idx = dtl->last_idx;
170
171 if (cur_idx - last_idx > dtl->buf_entries) {
172 pr_debug("%s: hv buffer overflow for cpu %d, samples lost\n",
173 __func__, dtl->cpu);
174 }
175
176 cur_idx %= dtl->buf_entries;
177 last_idx %= dtl->buf_entries;
178
179 /* read the tail of the buffer if we've wrapped */
180 if (last_idx > cur_idx) {
181 read_size = min(n_req, dtl->buf_entries - last_idx);
182
183 rc = copy_to_user(buf, &dtl->buf[last_idx],
184 read_size * sizeof(struct dtl_entry));
185 if (rc)
186 return -EFAULT;
187
188 last_idx = 0;
189 n_req -= read_size;
190 n_read += read_size;
191 buf += read_size * sizeof(struct dtl_entry);
192 }
193
194 /* .. and now the head */
195 read_size = min(n_req, cur_idx - last_idx);
196 rc = copy_to_user(buf, &dtl->buf[last_idx],
197 read_size * sizeof(struct dtl_entry));
198 if (rc)
199 return -EFAULT;
200
201 n_read += read_size;
202 dtl->last_idx += n_read;
203
204 return n_read * sizeof(struct dtl_entry);
205}
206
207static struct file_operations dtl_fops = {
208 .open = dtl_file_open,
209 .release = dtl_file_release,
210 .read = dtl_file_read,
211 .llseek = no_llseek,
212};
213
214static struct dentry *dtl_dir;
215
216static int dtl_setup_file(struct dtl *dtl)
217{
218 char name[10];
219
220 sprintf(name, "cpu-%d", dtl->cpu);
221
222 dtl->file = debugfs_create_file(name, 0400, dtl_dir, dtl, &dtl_fops);
223 if (!dtl->file)
224 return -ENOMEM;
225
226 return 0;
227}
228
229static int dtl_init(void)
230{
231 struct dentry *event_mask_file, *buf_entries_file;
232 int rc, i;
233
234 if (!firmware_has_feature(FW_FEATURE_SPLPAR))
235 return -ENODEV;
236
237 /* set up common debugfs structure */
238
239 rc = -ENOMEM;
240 dtl_dir = debugfs_create_dir("dtl", powerpc_debugfs_root);
241 if (!dtl_dir) {
242 printk(KERN_WARNING "%s: can't create dtl root dir\n",
243 __func__);
244 goto err;
245 }
246
247 event_mask_file = debugfs_create_x8("dtl_event_mask", 0600,
248 dtl_dir, &dtl_event_mask);
249 buf_entries_file = debugfs_create_u32("dtl_buf_entries", 0600,
250 dtl_dir, &dtl_buf_entries);
251
252 if (!event_mask_file || !buf_entries_file) {
253 printk(KERN_WARNING "%s: can't create dtl files\n", __func__);
254 goto err_remove_dir;
255 }
256
257 /* set up the per-cpu log structures */
258 for_each_possible_cpu(i) {
259 struct dtl *dtl = &per_cpu(dtl, i);
260 dtl->cpu = i;
261
262 rc = dtl_setup_file(dtl);
263 if (rc)
264 goto err_remove_dir;
265 }
266
267 return 0;
268
269err_remove_dir:
270 debugfs_remove_recursive(dtl_dir);
271err:
272 return rc;
273}
274arch_initcall(dtl_init);