aboutsummaryrefslogtreecommitdiffstats
path: root/arch
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
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')
-rw-r--r--arch/powerpc/platforms/pseries/Kconfig10
-rw-r--r--arch/powerpc/platforms/pseries/Makefile1
-rw-r--r--arch/powerpc/platforms/pseries/dtl.c274
-rw-r--r--arch/powerpc/platforms/pseries/plpar_wrappers.h10
4 files changed, 295 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index c0e6ec240f46..f0e6f28427bd 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -68,3 +68,13 @@ config CMM
68 makes sense for a system running in an LPAR where the unused pages 68 makes sense for a system running in an LPAR where the unused pages
69 will be reused for other LPARs. The interface allows firmware to 69 will be reused for other LPARs. The interface allows firmware to
70 balance memory across many LPARs. 70 balance memory across many LPARs.
71
72config DTL
73 bool "Dispatch Trace Log"
74 depends on PPC_SPLPAR && DEBUG_FS
75 help
76 SPLPAR machines can log hypervisor preempt & dispatch events to a
77 kernel buffer. Saying Y here will enable logging these events,
78 which are accessible through a debugfs file.
79
80 Say N if you are unsure.
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 0ce691df595f..790c0b872d4f 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_HVCS) += hvcserver.o
25obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o 25obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o
26obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o 26obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o
27obj-$(CONFIG_CMM) += cmm.o 27obj-$(CONFIG_CMM) += cmm.o
28obj-$(CONFIG_DTL) += dtl.o
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);
diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h
index d967c1893ab5..a24a6b2333b2 100644
--- a/arch/powerpc/platforms/pseries/plpar_wrappers.h
+++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h
@@ -43,6 +43,16 @@ static inline long register_slb_shadow(unsigned long cpu, unsigned long vpa)
43 return vpa_call(0x3, cpu, vpa); 43 return vpa_call(0x3, cpu, vpa);
44} 44}
45 45
46static inline long unregister_dtl(unsigned long cpu, unsigned long vpa)
47{
48 return vpa_call(0x6, cpu, vpa);
49}
50
51static inline long register_dtl(unsigned long cpu, unsigned long vpa)
52{
53 return vpa_call(0x2, cpu, vpa);
54}
55
46static inline long plpar_page_set_loaned(unsigned long vpa) 56static inline long plpar_page_set_loaned(unsigned long vpa)
47{ 57{
48 unsigned long cmo_page_sz = cmo_get_page_size(); 58 unsigned long cmo_page_sz = cmo_get_page_size();