aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2010-08-30 21:59:53 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2010-09-02 00:07:32 -0400
commit872e439a45ed4a4bd499bc55cb0dffa74027f749 (patch)
tree2f07a64553b25fb0a457832722b46ee9549b74cb /arch/powerpc/platforms
parentcf9efce0ce3136fa076f53e53154e98455229514 (diff)
powerpc/pseries: Re-enable dispatch trace log userspace interface
Since the cpu accounting code uses the hypervisor dispatch trace log now when CONFIG_VIRT_CPU_ACCOUNTING = y, the previous commit disabled access to it via files in the /sys/kernel/debug/powerpc/dtl/ directory in that case. This restores those files. To do this, we now have a hook that the cpu accounting code will call as it processes each entry from the hypervisor dispatch trace log. The code in dtl.c now uses that to fill up its ring buffer, rather than having the hypervisor fill the ring buffer directly. This also fixes dtl_file_read() to handle overflow conditions a bit better and adds a spinlock to ensure that race conditions (multiple processes opening or reading the file concurrently) are handled correctly. Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r--arch/powerpc/platforms/pseries/dtl.c206
1 files changed, 166 insertions, 40 deletions
diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
index 0357655db49d..c371bc06434b 100644
--- a/arch/powerpc/platforms/pseries/dtl.c
+++ b/arch/powerpc/platforms/pseries/dtl.c
@@ -23,6 +23,7 @@
23#include <linux/init.h> 23#include <linux/init.h>
24#include <linux/slab.h> 24#include <linux/slab.h>
25#include <linux/debugfs.h> 25#include <linux/debugfs.h>
26#include <linux/spinlock.h>
26#include <asm/smp.h> 27#include <asm/smp.h>
27#include <asm/system.h> 28#include <asm/system.h>
28#include <asm/uaccess.h> 29#include <asm/uaccess.h>
@@ -37,6 +38,7 @@ struct dtl {
37 int cpu; 38 int cpu;
38 int buf_entries; 39 int buf_entries;
39 u64 last_idx; 40 u64 last_idx;
41 spinlock_t lock;
40}; 42};
41static DEFINE_PER_CPU(struct dtl, cpu_dtl); 43static DEFINE_PER_CPU(struct dtl, cpu_dtl);
42 44
@@ -55,25 +57,97 @@ static u8 dtl_event_mask = 0x7;
55static int dtl_buf_entries = (16 * 85); 57static int dtl_buf_entries = (16 * 85);
56 58
57 59
58static int dtl_enable(struct dtl *dtl) 60#ifdef CONFIG_VIRT_CPU_ACCOUNTING
61struct dtl_ring {
62 u64 write_index;
63 struct dtl_entry *write_ptr;
64 struct dtl_entry *buf;
65 struct dtl_entry *buf_end;
66 u8 saved_dtl_mask;
67};
68
69static DEFINE_PER_CPU(struct dtl_ring, dtl_rings);
70
71static atomic_t dtl_count;
72
73/*
74 * The cpu accounting code controls the DTL ring buffer, and we get
75 * given entries as they are processed.
76 */
77static void consume_dtle(struct dtl_entry *dtle, u64 index)
59{ 78{
60 unsigned long addr; 79 struct dtl_ring *dtlr = &__get_cpu_var(dtl_rings);
61 int ret, hwcpu; 80 struct dtl_entry *wp = dtlr->write_ptr;
81 struct lppaca *vpa = local_paca->lppaca_ptr;
62 82
63 /* only allow one reader */ 83 if (!wp)
64 if (dtl->buf) 84 return;
65 return -EBUSY;
66 85
67 /* we need to store the original allocation size for use during read */ 86 *wp = *dtle;
68 dtl->buf_entries = dtl_buf_entries; 87 barrier();
69 88
70 dtl->buf = kmalloc_node(dtl->buf_entries * sizeof(struct dtl_entry), 89 /* check for hypervisor ring buffer overflow, ignore this entry if so */
71 GFP_KERNEL, cpu_to_node(dtl->cpu)); 90 if (index + N_DISPATCH_LOG < vpa->dtl_idx)
72 if (!dtl->buf) { 91 return;
73 printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n", 92
74 __func__, dtl->cpu); 93 ++wp;
75 return -ENOMEM; 94 if (wp == dtlr->buf_end)
76 } 95 wp = dtlr->buf;
96 dtlr->write_ptr = wp;
97
98 /* incrementing write_index makes the new entry visible */
99 smp_wmb();
100 ++dtlr->write_index;
101}
102
103static int dtl_start(struct dtl *dtl)
104{
105 struct dtl_ring *dtlr = &per_cpu(dtl_rings, dtl->cpu);
106
107 dtlr->buf = dtl->buf;
108 dtlr->buf_end = dtl->buf + dtl->buf_entries;
109 dtlr->write_index = 0;
110
111 /* setting write_ptr enables logging into our buffer */
112 smp_wmb();
113 dtlr->write_ptr = dtl->buf;
114
115 /* enable event logging */
116 dtlr->saved_dtl_mask = lppaca_of(dtl->cpu).dtl_enable_mask;
117 lppaca_of(dtl->cpu).dtl_enable_mask |= dtl_event_mask;
118
119 dtl_consumer = consume_dtle;
120 atomic_inc(&dtl_count);
121 return 0;
122}
123
124static void dtl_stop(struct dtl *dtl)
125{
126 struct dtl_ring *dtlr = &per_cpu(dtl_rings, dtl->cpu);
127
128 dtlr->write_ptr = NULL;
129 smp_wmb();
130
131 dtlr->buf = NULL;
132
133 /* restore dtl_enable_mask */
134 lppaca_of(dtl->cpu).dtl_enable_mask = dtlr->saved_dtl_mask;
135
136 if (atomic_dec_and_test(&dtl_count))
137 dtl_consumer = NULL;
138}
139
140static u64 dtl_current_index(struct dtl *dtl)
141{
142 return per_cpu(dtl_rings, dtl->cpu).write_index;
143}
144
145#else /* CONFIG_VIRT_CPU_ACCOUNTING */
146
147static int dtl_start(struct dtl *dtl)
148{
149 unsigned long addr;
150 int ret, hwcpu;
77 151
78 /* Register our dtl buffer with the hypervisor. The HV expects the 152 /* Register our dtl buffer with the hypervisor. The HV expects the
79 * buffer size to be passed in the second word of the buffer */ 153 * buffer size to be passed in the second word of the buffer */
@@ -85,12 +159,11 @@ static int dtl_enable(struct dtl *dtl)
85 if (ret) { 159 if (ret) {
86 printk(KERN_WARNING "%s: DTL registration for cpu %d (hw %d) " 160 printk(KERN_WARNING "%s: DTL registration for cpu %d (hw %d) "
87 "failed with %d\n", __func__, dtl->cpu, hwcpu, ret); 161 "failed with %d\n", __func__, dtl->cpu, hwcpu, ret);
88 kfree(dtl->buf);
89 return -EIO; 162 return -EIO;
90 } 163 }
91 164
92 /* set our initial buffer indices */ 165 /* set our initial buffer indices */
93 dtl->last_idx = lppaca_of(dtl->cpu).dtl_idx = 0; 166 lppaca_of(dtl->cpu).dtl_idx = 0;
94 167
95 /* ensure that our updates to the lppaca fields have occurred before 168 /* ensure that our updates to the lppaca fields have occurred before
96 * we actually enable the logging */ 169 * we actually enable the logging */
@@ -102,17 +175,66 @@ static int dtl_enable(struct dtl *dtl)
102 return 0; 175 return 0;
103} 176}
104 177
105static void dtl_disable(struct dtl *dtl) 178static void dtl_stop(struct dtl *dtl)
106{ 179{
107 int hwcpu = get_hard_smp_processor_id(dtl->cpu); 180 int hwcpu = get_hard_smp_processor_id(dtl->cpu);
108 181
109 lppaca_of(dtl->cpu).dtl_enable_mask = 0x0; 182 lppaca_of(dtl->cpu).dtl_enable_mask = 0x0;
110 183
111 unregister_dtl(hwcpu, __pa(dtl->buf)); 184 unregister_dtl(hwcpu, __pa(dtl->buf));
185}
186
187static u64 dtl_current_index(struct dtl *dtl)
188{
189 return lppaca_of(dtl->cpu).dtl_idx;
190}
191#endif /* CONFIG_VIRT_CPU_ACCOUNTING */
112 192
193static int dtl_enable(struct dtl *dtl)
194{
195 long int n_entries;
196 long int rc;
197 struct dtl_entry *buf = NULL;
198
199 /* only allow one reader */
200 if (dtl->buf)
201 return -EBUSY;
202
203 n_entries = dtl_buf_entries;
204 buf = kmalloc_node(n_entries * sizeof(struct dtl_entry),
205 GFP_KERNEL, cpu_to_node(dtl->cpu));
206 if (!buf) {
207 printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n",
208 __func__, dtl->cpu);
209 return -ENOMEM;
210 }
211
212 spin_lock(&dtl->lock);
213 rc = -EBUSY;
214 if (!dtl->buf) {
215 /* store the original allocation size for use during read */
216 dtl->buf_entries = n_entries;
217 dtl->buf = buf;
218 dtl->last_idx = 0;
219 rc = dtl_start(dtl);
220 if (rc)
221 dtl->buf = NULL;
222 }
223 spin_unlock(&dtl->lock);
224
225 if (rc)
226 kfree(buf);
227 return rc;
228}
229
230static void dtl_disable(struct dtl *dtl)
231{
232 spin_lock(&dtl->lock);
233 dtl_stop(dtl);
113 kfree(dtl->buf); 234 kfree(dtl->buf);
114 dtl->buf = NULL; 235 dtl->buf = NULL;
115 dtl->buf_entries = 0; 236 dtl->buf_entries = 0;
237 spin_unlock(&dtl->lock);
116} 238}
117 239
118/* file interface */ 240/* file interface */
@@ -140,8 +262,9 @@ static int dtl_file_release(struct inode *inode, struct file *filp)
140static ssize_t dtl_file_read(struct file *filp, char __user *buf, size_t len, 262static ssize_t dtl_file_read(struct file *filp, char __user *buf, size_t len,
141 loff_t *pos) 263 loff_t *pos)
142{ 264{
143 int rc, cur_idx, last_idx, n_read, n_req, read_size; 265 long int rc, n_read, n_req, read_size;
144 struct dtl *dtl; 266 struct dtl *dtl;
267 u64 cur_idx, last_idx, i;
145 268
146 if ((len % sizeof(struct dtl_entry)) != 0) 269 if ((len % sizeof(struct dtl_entry)) != 0)
147 return -EINVAL; 270 return -EINVAL;
@@ -154,41 +277,48 @@ static ssize_t dtl_file_read(struct file *filp, char __user *buf, size_t len,
154 /* actual number of entries read */ 277 /* actual number of entries read */
155 n_read = 0; 278 n_read = 0;
156 279
157 cur_idx = lppaca_of(dtl->cpu).dtl_idx; 280 spin_lock(&dtl->lock);
281
282 cur_idx = dtl_current_index(dtl);
158 last_idx = dtl->last_idx; 283 last_idx = dtl->last_idx;
159 284
160 if (cur_idx - last_idx > dtl->buf_entries) { 285 if (last_idx + dtl->buf_entries <= cur_idx)
161 pr_debug("%s: hv buffer overflow for cpu %d, samples lost\n", 286 last_idx = cur_idx - dtl->buf_entries + 1;
162 __func__, dtl->cpu); 287
163 } 288 if (last_idx + n_req > cur_idx)
289 n_req = cur_idx - last_idx;
164 290
165 cur_idx %= dtl->buf_entries; 291 if (n_req > 0)
166 last_idx %= dtl->buf_entries; 292 dtl->last_idx = last_idx + n_req;
293
294 spin_unlock(&dtl->lock);
295
296 if (n_req <= 0)
297 return 0;
298
299 i = last_idx % dtl->buf_entries;
167 300
168 /* read the tail of the buffer if we've wrapped */ 301 /* read the tail of the buffer if we've wrapped */
169 if (last_idx > cur_idx) { 302 if (i + n_req > dtl->buf_entries) {
170 read_size = min(n_req, dtl->buf_entries - last_idx); 303 read_size = dtl->buf_entries - i;
171 304
172 rc = copy_to_user(buf, &dtl->buf[last_idx], 305 rc = copy_to_user(buf, &dtl->buf[i],
173 read_size * sizeof(struct dtl_entry)); 306 read_size * sizeof(struct dtl_entry));
174 if (rc) 307 if (rc)
175 return -EFAULT; 308 return -EFAULT;
176 309
177 last_idx = 0; 310 i = 0;
178 n_req -= read_size; 311 n_req -= read_size;
179 n_read += read_size; 312 n_read += read_size;
180 buf += read_size * sizeof(struct dtl_entry); 313 buf += read_size * sizeof(struct dtl_entry);
181 } 314 }
182 315
183 /* .. and now the head */ 316 /* .. and now the head */
184 read_size = min(n_req, cur_idx - last_idx); 317 rc = copy_to_user(buf, &dtl->buf[i], n_req * sizeof(struct dtl_entry));
185 rc = copy_to_user(buf, &dtl->buf[last_idx],
186 read_size * sizeof(struct dtl_entry));
187 if (rc) 318 if (rc)
188 return -EFAULT; 319 return -EFAULT;
189 320
190 n_read += read_size; 321 n_read += n_req;
191 dtl->last_idx += n_read;
192 322
193 return n_read * sizeof(struct dtl_entry); 323 return n_read * sizeof(struct dtl_entry);
194} 324}
@@ -220,11 +350,6 @@ static int dtl_init(void)
220 struct dentry *event_mask_file, *buf_entries_file; 350 struct dentry *event_mask_file, *buf_entries_file;
221 int rc, i; 351 int rc, i;
222 352
223#ifdef CONFIG_VIRT_CPU_ACCOUNTING
224 /* disable this for now */
225 return -ENODEV;
226#endif
227
228 if (!firmware_has_feature(FW_FEATURE_SPLPAR)) 353 if (!firmware_has_feature(FW_FEATURE_SPLPAR))
229 return -ENODEV; 354 return -ENODEV;
230 355
@@ -251,6 +376,7 @@ static int dtl_init(void)
251 /* set up the per-cpu log structures */ 376 /* set up the per-cpu log structures */
252 for_each_possible_cpu(i) { 377 for_each_possible_cpu(i) {
253 struct dtl *dtl = &per_cpu(cpu_dtl, i); 378 struct dtl *dtl = &per_cpu(cpu_dtl, i);
379 spin_lock_init(&dtl->lock);
254 dtl->cpu = i; 380 dtl->cpu = i;
255 381
256 rc = dtl_setup_file(dtl); 382 rc = dtl_setup_file(dtl);