Commit | Line | Data |
---|---|---|
77241056 MM |
1 | #ifdef CONFIG_DEBUG_FS |
2 | /* | |
3 | * | |
4 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
5 | * redistributing this file, you may do so under either license. | |
6 | * | |
7 | * GPL LICENSE SUMMARY | |
8 | * | |
9 | * Copyright(c) 2015 Intel Corporation. | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of version 2 of the GNU General Public License as | |
13 | * published by the Free Software Foundation. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, but | |
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | * General Public License for more details. | |
19 | * | |
20 | * BSD LICENSE | |
21 | * | |
22 | * Copyright(c) 2015 Intel Corporation. | |
23 | * | |
24 | * Redistribution and use in source and binary forms, with or without | |
25 | * modification, are permitted provided that the following conditions | |
26 | * are met: | |
27 | * | |
28 | * - Redistributions of source code must retain the above copyright | |
29 | * notice, this list of conditions and the following disclaimer. | |
30 | * - Redistributions in binary form must reproduce the above copyright | |
31 | * notice, this list of conditions and the following disclaimer in | |
32 | * the documentation and/or other materials provided with the | |
33 | * distribution. | |
34 | * - Neither the name of Intel Corporation nor the names of its | |
35 | * contributors may be used to endorse or promote products derived | |
36 | * from this software without specific prior written permission. | |
37 | * | |
38 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
39 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
40 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
41 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
42 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
43 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
44 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
45 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
46 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
47 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
48 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
49 | * | |
50 | */ | |
51 | #include <linux/debugfs.h> | |
52 | #include <linux/seq_file.h> | |
53 | #include <linux/kernel.h> | |
54 | #include <linux/export.h> | |
55 | ||
56 | #include "hfi.h" | |
57 | #include "debugfs.h" | |
58 | #include "device.h" | |
59 | #include "qp.h" | |
60 | #include "sdma.h" | |
61 | ||
62 | static struct dentry *hfi1_dbg_root; | |
63 | ||
64 | #define private2dd(file) (file_inode(file)->i_private) | |
65 | #define private2ppd(file) (file_inode(file)->i_private) | |
66 | ||
67 | #define DEBUGFS_SEQ_FILE_OPS(name) \ | |
68 | static const struct seq_operations _##name##_seq_ops = { \ | |
69 | .start = _##name##_seq_start, \ | |
70 | .next = _##name##_seq_next, \ | |
71 | .stop = _##name##_seq_stop, \ | |
72 | .show = _##name##_seq_show \ | |
73 | } | |
74 | #define DEBUGFS_SEQ_FILE_OPEN(name) \ | |
75 | static int _##name##_open(struct inode *inode, struct file *s) \ | |
76 | { \ | |
77 | struct seq_file *seq; \ | |
78 | int ret; \ | |
79 | ret = seq_open(s, &_##name##_seq_ops); \ | |
80 | if (ret) \ | |
81 | return ret; \ | |
82 | seq = s->private_data; \ | |
83 | seq->private = inode->i_private; \ | |
84 | return 0; \ | |
85 | } | |
86 | ||
87 | #define DEBUGFS_FILE_OPS(name) \ | |
88 | static const struct file_operations _##name##_file_ops = { \ | |
89 | .owner = THIS_MODULE, \ | |
90 | .open = _##name##_open, \ | |
91 | .read = seq_read, \ | |
92 | .llseek = seq_lseek, \ | |
93 | .release = seq_release \ | |
94 | } | |
95 | ||
96 | #define DEBUGFS_FILE_CREATE(name, parent, data, ops, mode) \ | |
97 | do { \ | |
98 | struct dentry *ent; \ | |
99 | ent = debugfs_create_file(name, mode, parent, \ | |
100 | data, ops); \ | |
101 | if (!ent) \ | |
102 | pr_warn("create of %s failed\n", name); \ | |
103 | } while (0) | |
104 | ||
105 | ||
106 | #define DEBUGFS_SEQ_FILE_CREATE(name, parent, data) \ | |
107 | DEBUGFS_FILE_CREATE(#name, parent, data, &_##name##_file_ops, S_IRUGO) | |
108 | ||
109 | static void *_opcode_stats_seq_start(struct seq_file *s, loff_t *pos) | |
110 | __acquires(RCU) | |
111 | { | |
112 | struct hfi1_opcode_stats_perctx *opstats; | |
113 | ||
114 | rcu_read_lock(); | |
115 | if (*pos >= ARRAY_SIZE(opstats->stats)) | |
116 | return NULL; | |
117 | return pos; | |
118 | } | |
119 | ||
120 | static void *_opcode_stats_seq_next(struct seq_file *s, void *v, loff_t *pos) | |
121 | { | |
122 | struct hfi1_opcode_stats_perctx *opstats; | |
123 | ||
124 | ++*pos; | |
125 | if (*pos >= ARRAY_SIZE(opstats->stats)) | |
126 | return NULL; | |
127 | return pos; | |
128 | } | |
129 | ||
130 | ||
131 | static void _opcode_stats_seq_stop(struct seq_file *s, void *v) | |
132 | __releases(RCU) | |
133 | { | |
134 | rcu_read_unlock(); | |
135 | } | |
136 | ||
137 | static int _opcode_stats_seq_show(struct seq_file *s, void *v) | |
138 | { | |
139 | loff_t *spos = v; | |
140 | loff_t i = *spos, j; | |
141 | u64 n_packets = 0, n_bytes = 0; | |
142 | struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private; | |
143 | struct hfi1_devdata *dd = dd_from_dev(ibd); | |
144 | ||
145 | for (j = 0; j < dd->first_user_ctxt; j++) { | |
146 | if (!dd->rcd[j]) | |
147 | continue; | |
148 | n_packets += dd->rcd[j]->opstats->stats[i].n_packets; | |
149 | n_bytes += dd->rcd[j]->opstats->stats[i].n_bytes; | |
150 | } | |
151 | if (!n_packets && !n_bytes) | |
152 | return SEQ_SKIP; | |
153 | seq_printf(s, "%02llx %llu/%llu\n", i, | |
154 | (unsigned long long) n_packets, | |
155 | (unsigned long long) n_bytes); | |
156 | ||
157 | return 0; | |
158 | } | |
159 | ||
160 | DEBUGFS_SEQ_FILE_OPS(opcode_stats); | |
161 | DEBUGFS_SEQ_FILE_OPEN(opcode_stats) | |
162 | DEBUGFS_FILE_OPS(opcode_stats); | |
163 | ||
164 | static void *_ctx_stats_seq_start(struct seq_file *s, loff_t *pos) | |
165 | { | |
166 | struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private; | |
167 | struct hfi1_devdata *dd = dd_from_dev(ibd); | |
168 | ||
169 | if (!*pos) | |
170 | return SEQ_START_TOKEN; | |
171 | if (*pos >= dd->first_user_ctxt) | |
172 | return NULL; | |
173 | return pos; | |
174 | } | |
175 | ||
176 | static void *_ctx_stats_seq_next(struct seq_file *s, void *v, loff_t *pos) | |
177 | { | |
178 | struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private; | |
179 | struct hfi1_devdata *dd = dd_from_dev(ibd); | |
180 | ||
181 | if (v == SEQ_START_TOKEN) | |
182 | return pos; | |
183 | ||
184 | ++*pos; | |
185 | if (*pos >= dd->first_user_ctxt) | |
186 | return NULL; | |
187 | return pos; | |
188 | } | |
189 | ||
190 | static void _ctx_stats_seq_stop(struct seq_file *s, void *v) | |
191 | { | |
192 | /* nothing allocated */ | |
193 | } | |
194 | ||
195 | static int _ctx_stats_seq_show(struct seq_file *s, void *v) | |
196 | { | |
197 | loff_t *spos; | |
198 | loff_t i, j; | |
199 | u64 n_packets = 0; | |
200 | struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private; | |
201 | struct hfi1_devdata *dd = dd_from_dev(ibd); | |
202 | ||
203 | if (v == SEQ_START_TOKEN) { | |
204 | seq_puts(s, "Ctx:npkts\n"); | |
205 | return 0; | |
206 | } | |
207 | ||
208 | spos = v; | |
209 | i = *spos; | |
210 | ||
211 | if (!dd->rcd[i]) | |
212 | return SEQ_SKIP; | |
213 | ||
214 | for (j = 0; j < ARRAY_SIZE(dd->rcd[i]->opstats->stats); j++) | |
215 | n_packets += dd->rcd[i]->opstats->stats[j].n_packets; | |
216 | ||
217 | if (!n_packets) | |
218 | return SEQ_SKIP; | |
219 | ||
220 | seq_printf(s, " %llu:%llu\n", i, n_packets); | |
221 | return 0; | |
222 | } | |
223 | ||
224 | DEBUGFS_SEQ_FILE_OPS(ctx_stats); | |
225 | DEBUGFS_SEQ_FILE_OPEN(ctx_stats) | |
226 | DEBUGFS_FILE_OPS(ctx_stats); | |
227 | ||
228 | static void *_qp_stats_seq_start(struct seq_file *s, loff_t *pos) | |
229 | __acquires(RCU) | |
230 | { | |
231 | struct qp_iter *iter; | |
232 | loff_t n = *pos; | |
233 | ||
234 | rcu_read_lock(); | |
235 | iter = qp_iter_init(s->private); | |
236 | if (!iter) | |
237 | return NULL; | |
238 | ||
239 | while (n--) { | |
240 | if (qp_iter_next(iter)) { | |
241 | kfree(iter); | |
242 | return NULL; | |
243 | } | |
244 | } | |
245 | ||
246 | return iter; | |
247 | } | |
248 | ||
249 | static void *_qp_stats_seq_next(struct seq_file *s, void *iter_ptr, | |
250 | loff_t *pos) | |
251 | { | |
252 | struct qp_iter *iter = iter_ptr; | |
253 | ||
254 | (*pos)++; | |
255 | ||
256 | if (qp_iter_next(iter)) { | |
257 | kfree(iter); | |
258 | return NULL; | |
259 | } | |
260 | ||
261 | return iter; | |
262 | } | |
263 | ||
264 | static void _qp_stats_seq_stop(struct seq_file *s, void *iter_ptr) | |
265 | __releases(RCU) | |
266 | { | |
267 | rcu_read_unlock(); | |
268 | } | |
269 | ||
270 | static int _qp_stats_seq_show(struct seq_file *s, void *iter_ptr) | |
271 | { | |
272 | struct qp_iter *iter = iter_ptr; | |
273 | ||
274 | if (!iter) | |
275 | return 0; | |
276 | ||
277 | qp_iter_print(s, iter); | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | DEBUGFS_SEQ_FILE_OPS(qp_stats); | |
283 | DEBUGFS_SEQ_FILE_OPEN(qp_stats) | |
284 | DEBUGFS_FILE_OPS(qp_stats); | |
285 | ||
286 | static void *_sdes_seq_start(struct seq_file *s, loff_t *pos) | |
287 | __acquires(RCU) | |
288 | { | |
289 | struct hfi1_ibdev *ibd; | |
290 | struct hfi1_devdata *dd; | |
291 | ||
292 | rcu_read_lock(); | |
293 | ibd = (struct hfi1_ibdev *)s->private; | |
294 | dd = dd_from_dev(ibd); | |
295 | if (!dd->per_sdma || *pos >= dd->num_sdma) | |
296 | return NULL; | |
297 | return pos; | |
298 | } | |
299 | ||
300 | static void *_sdes_seq_next(struct seq_file *s, void *v, loff_t *pos) | |
301 | { | |
302 | struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private; | |
303 | struct hfi1_devdata *dd = dd_from_dev(ibd); | |
304 | ||
305 | ++*pos; | |
306 | if (!dd->per_sdma || *pos >= dd->num_sdma) | |
307 | return NULL; | |
308 | return pos; | |
309 | } | |
310 | ||
311 | ||
312 | static void _sdes_seq_stop(struct seq_file *s, void *v) | |
313 | __releases(RCU) | |
314 | { | |
315 | rcu_read_unlock(); | |
316 | } | |
317 | ||
318 | static int _sdes_seq_show(struct seq_file *s, void *v) | |
319 | { | |
320 | struct hfi1_ibdev *ibd = (struct hfi1_ibdev *)s->private; | |
321 | struct hfi1_devdata *dd = dd_from_dev(ibd); | |
322 | loff_t *spos = v; | |
323 | loff_t i = *spos; | |
324 | ||
325 | sdma_seqfile_dump_sde(s, &dd->per_sdma[i]); | |
326 | return 0; | |
327 | } | |
328 | ||
329 | DEBUGFS_SEQ_FILE_OPS(sdes); | |
330 | DEBUGFS_SEQ_FILE_OPEN(sdes) | |
331 | DEBUGFS_FILE_OPS(sdes); | |
332 | ||
333 | /* read the per-device counters */ | |
334 | static ssize_t dev_counters_read(struct file *file, char __user *buf, | |
335 | size_t count, loff_t *ppos) | |
336 | { | |
337 | u64 *counters; | |
338 | size_t avail; | |
339 | struct hfi1_devdata *dd; | |
340 | ssize_t rval; | |
341 | ||
342 | rcu_read_lock(); | |
343 | dd = private2dd(file); | |
344 | avail = hfi1_read_cntrs(dd, *ppos, NULL, &counters); | |
345 | rval = simple_read_from_buffer(buf, count, ppos, counters, avail); | |
346 | rcu_read_unlock(); | |
347 | return rval; | |
348 | } | |
349 | ||
350 | /* read the per-device counters */ | |
351 | static ssize_t dev_names_read(struct file *file, char __user *buf, | |
352 | size_t count, loff_t *ppos) | |
353 | { | |
354 | char *names; | |
355 | size_t avail; | |
356 | struct hfi1_devdata *dd; | |
357 | ssize_t rval; | |
358 | ||
359 | rcu_read_lock(); | |
360 | dd = private2dd(file); | |
361 | avail = hfi1_read_cntrs(dd, *ppos, &names, NULL); | |
362 | rval = simple_read_from_buffer(buf, count, ppos, names, avail); | |
363 | rcu_read_unlock(); | |
364 | return rval; | |
365 | } | |
366 | ||
367 | struct counter_info { | |
368 | char *name; | |
369 | const struct file_operations ops; | |
370 | }; | |
371 | ||
372 | /* | |
373 | * Could use file_inode(file)->i_ino to figure out which file, | |
374 | * instead of separate routine for each, but for now, this works... | |
375 | */ | |
376 | ||
377 | /* read the per-port names (same for each port) */ | |
378 | static ssize_t portnames_read(struct file *file, char __user *buf, | |
379 | size_t count, loff_t *ppos) | |
380 | { | |
381 | char *names; | |
382 | size_t avail; | |
383 | struct hfi1_devdata *dd; | |
384 | ssize_t rval; | |
385 | ||
386 | rcu_read_lock(); | |
387 | dd = private2dd(file); | |
388 | /* port number n/a here since names are constant */ | |
389 | avail = hfi1_read_portcntrs(dd, *ppos, 0, &names, NULL); | |
390 | rval = simple_read_from_buffer(buf, count, ppos, names, avail); | |
391 | rcu_read_unlock(); | |
392 | return rval; | |
393 | } | |
394 | ||
395 | /* read the per-port counters */ | |
396 | static ssize_t portcntrs_debugfs_read(struct file *file, char __user *buf, | |
397 | size_t count, loff_t *ppos) | |
398 | { | |
399 | u64 *counters; | |
400 | size_t avail; | |
401 | struct hfi1_devdata *dd; | |
402 | struct hfi1_pportdata *ppd; | |
403 | ssize_t rval; | |
404 | ||
405 | rcu_read_lock(); | |
406 | ppd = private2ppd(file); | |
407 | dd = ppd->dd; | |
408 | avail = hfi1_read_portcntrs(dd, *ppos, ppd->port - 1, NULL, &counters); | |
409 | rval = simple_read_from_buffer(buf, count, ppos, counters, avail); | |
410 | rcu_read_unlock(); | |
411 | return rval; | |
412 | } | |
413 | ||
414 | /* | |
415 | * read the per-port QSFP data for ppd | |
416 | */ | |
417 | static ssize_t qsfp_debugfs_dump(struct file *file, char __user *buf, | |
418 | size_t count, loff_t *ppos) | |
419 | { | |
420 | struct hfi1_pportdata *ppd; | |
421 | char *tmp; | |
422 | int ret; | |
423 | ||
424 | rcu_read_lock(); | |
425 | ppd = private2ppd(file); | |
426 | tmp = kmalloc(PAGE_SIZE, GFP_KERNEL); | |
427 | if (!tmp) { | |
428 | rcu_read_unlock(); | |
429 | return -ENOMEM; | |
430 | } | |
431 | ||
432 | ret = qsfp_dump(ppd, tmp, PAGE_SIZE); | |
433 | if (ret > 0) | |
434 | ret = simple_read_from_buffer(buf, count, ppos, tmp, ret); | |
435 | rcu_read_unlock(); | |
436 | kfree(tmp); | |
437 | return ret; | |
438 | } | |
439 | ||
440 | /* Do an i2c write operation on the chain for the given HFI. */ | |
441 | static ssize_t __i2c_debugfs_write(struct file *file, const char __user *buf, | |
442 | size_t count, loff_t *ppos, u32 target) | |
443 | { | |
444 | struct hfi1_pportdata *ppd; | |
445 | char *buff; | |
446 | int ret; | |
447 | int i2c_addr; | |
448 | int offset; | |
449 | int total_written; | |
450 | ||
451 | rcu_read_lock(); | |
452 | ppd = private2ppd(file); | |
453 | ||
454 | buff = kmalloc(count, GFP_KERNEL); | |
455 | if (!buff) { | |
456 | ret = -ENOMEM; | |
457 | goto _return; | |
458 | } | |
459 | ||
460 | ret = copy_from_user(buff, buf, count); | |
461 | if (ret > 0) { | |
462 | ret = -EFAULT; | |
463 | goto _free; | |
464 | } | |
465 | ||
466 | i2c_addr = (*ppos >> 16) & 0xff; | |
467 | offset = *ppos & 0xffff; | |
468 | ||
469 | total_written = i2c_write(ppd, target, i2c_addr, offset, buff, count); | |
470 | if (total_written < 0) { | |
471 | ret = total_written; | |
472 | goto _free; | |
473 | } | |
474 | ||
475 | *ppos += total_written; | |
476 | ||
477 | ret = total_written; | |
478 | ||
479 | _free: | |
480 | kfree(buff); | |
481 | _return: | |
482 | rcu_read_unlock(); | |
483 | return ret; | |
484 | } | |
485 | ||
486 | /* Do an i2c write operation on chain for HFI 0. */ | |
487 | static ssize_t i2c1_debugfs_write(struct file *file, const char __user *buf, | |
488 | size_t count, loff_t *ppos) | |
489 | { | |
490 | return __i2c_debugfs_write(file, buf, count, ppos, 0); | |
491 | } | |
492 | ||
493 | /* Do an i2c write operation on chain for HFI 1. */ | |
494 | static ssize_t i2c2_debugfs_write(struct file *file, const char __user *buf, | |
495 | size_t count, loff_t *ppos) | |
496 | { | |
497 | return __i2c_debugfs_write(file, buf, count, ppos, 1); | |
498 | } | |
499 | ||
500 | /* Do an i2c read operation on the chain for the given HFI. */ | |
501 | static ssize_t __i2c_debugfs_read(struct file *file, char __user *buf, | |
502 | size_t count, loff_t *ppos, u32 target) | |
503 | { | |
504 | struct hfi1_pportdata *ppd; | |
505 | char *buff; | |
506 | int ret; | |
507 | int i2c_addr; | |
508 | int offset; | |
509 | int total_read; | |
510 | ||
511 | rcu_read_lock(); | |
512 | ppd = private2ppd(file); | |
513 | ||
514 | buff = kmalloc(count, GFP_KERNEL); | |
515 | if (!buff) { | |
516 | ret = -ENOMEM; | |
517 | goto _return; | |
518 | } | |
519 | ||
520 | i2c_addr = (*ppos >> 16) & 0xff; | |
521 | offset = *ppos & 0xffff; | |
522 | ||
523 | total_read = i2c_read(ppd, target, i2c_addr, offset, buff, count); | |
524 | if (total_read < 0) { | |
525 | ret = total_read; | |
526 | goto _free; | |
527 | } | |
528 | ||
529 | *ppos += total_read; | |
530 | ||
531 | ret = copy_to_user(buf, buff, total_read); | |
532 | if (ret > 0) { | |
533 | ret = -EFAULT; | |
534 | goto _free; | |
535 | } | |
536 | ||
537 | ret = total_read; | |
538 | ||
539 | _free: | |
540 | kfree(buff); | |
541 | _return: | |
542 | rcu_read_unlock(); | |
543 | return ret; | |
544 | } | |
545 | ||
546 | /* Do an i2c read operation on chain for HFI 0. */ | |
547 | static ssize_t i2c1_debugfs_read(struct file *file, char __user *buf, | |
548 | size_t count, loff_t *ppos) | |
549 | { | |
550 | return __i2c_debugfs_read(file, buf, count, ppos, 0); | |
551 | } | |
552 | ||
553 | /* Do an i2c read operation on chain for HFI 1. */ | |
554 | static ssize_t i2c2_debugfs_read(struct file *file, char __user *buf, | |
555 | size_t count, loff_t *ppos) | |
556 | { | |
557 | return __i2c_debugfs_read(file, buf, count, ppos, 1); | |
558 | } | |
559 | ||
560 | /* Do a QSFP write operation on the i2c chain for the given HFI. */ | |
561 | static ssize_t __qsfp_debugfs_write(struct file *file, const char __user *buf, | |
562 | size_t count, loff_t *ppos, u32 target) | |
563 | { | |
564 | struct hfi1_pportdata *ppd; | |
565 | char *buff; | |
566 | int ret; | |
567 | int total_written; | |
568 | ||
569 | rcu_read_lock(); | |
570 | if (*ppos + count > QSFP_PAGESIZE * 4) { /* base page + page00-page03 */ | |
571 | ret = -EINVAL; | |
572 | goto _return; | |
573 | } | |
574 | ||
575 | ppd = private2ppd(file); | |
576 | ||
577 | buff = kmalloc(count, GFP_KERNEL); | |
578 | if (!buff) { | |
579 | ret = -ENOMEM; | |
580 | goto _return; | |
581 | } | |
582 | ||
583 | ret = copy_from_user(buff, buf, count); | |
584 | if (ret > 0) { | |
585 | ret = -EFAULT; | |
586 | goto _free; | |
587 | } | |
588 | ||
589 | total_written = qsfp_write(ppd, target, *ppos, buff, count); | |
590 | if (total_written < 0) { | |
591 | ret = total_written; | |
592 | goto _free; | |
593 | } | |
594 | ||
595 | *ppos += total_written; | |
596 | ||
597 | ret = total_written; | |
598 | ||
599 | _free: | |
600 | kfree(buff); | |
601 | _return: | |
602 | rcu_read_unlock(); | |
603 | return ret; | |
604 | } | |
605 | ||
606 | /* Do a QSFP write operation on i2c chain for HFI 0. */ | |
607 | static ssize_t qsfp1_debugfs_write(struct file *file, const char __user *buf, | |
608 | size_t count, loff_t *ppos) | |
609 | { | |
610 | return __qsfp_debugfs_write(file, buf, count, ppos, 0); | |
611 | } | |
612 | ||
613 | /* Do a QSFP write operation on i2c chain for HFI 1. */ | |
614 | static ssize_t qsfp2_debugfs_write(struct file *file, const char __user *buf, | |
615 | size_t count, loff_t *ppos) | |
616 | { | |
617 | return __qsfp_debugfs_write(file, buf, count, ppos, 1); | |
618 | } | |
619 | ||
620 | /* Do a QSFP read operation on the i2c chain for the given HFI. */ | |
621 | static ssize_t __qsfp_debugfs_read(struct file *file, char __user *buf, | |
622 | size_t count, loff_t *ppos, u32 target) | |
623 | { | |
624 | struct hfi1_pportdata *ppd; | |
625 | char *buff; | |
626 | int ret; | |
627 | int total_read; | |
628 | ||
629 | rcu_read_lock(); | |
630 | if (*ppos + count > QSFP_PAGESIZE * 4) { /* base page + page00-page03 */ | |
631 | ret = -EINVAL; | |
632 | goto _return; | |
633 | } | |
634 | ||
635 | ppd = private2ppd(file); | |
636 | ||
637 | buff = kmalloc(count, GFP_KERNEL); | |
638 | if (!buff) { | |
639 | ret = -ENOMEM; | |
640 | goto _return; | |
641 | } | |
642 | ||
643 | total_read = qsfp_read(ppd, target, *ppos, buff, count); | |
644 | if (total_read < 0) { | |
645 | ret = total_read; | |
646 | goto _free; | |
647 | } | |
648 | ||
649 | *ppos += total_read; | |
650 | ||
651 | ret = copy_to_user(buf, buff, total_read); | |
652 | if (ret > 0) { | |
653 | ret = -EFAULT; | |
654 | goto _free; | |
655 | } | |
656 | ||
657 | ret = total_read; | |
658 | ||
659 | _free: | |
660 | kfree(buff); | |
661 | _return: | |
662 | rcu_read_unlock(); | |
663 | return ret; | |
664 | } | |
665 | ||
666 | /* Do a QSFP read operation on i2c chain for HFI 0. */ | |
667 | static ssize_t qsfp1_debugfs_read(struct file *file, char __user *buf, | |
668 | size_t count, loff_t *ppos) | |
669 | { | |
670 | return __qsfp_debugfs_read(file, buf, count, ppos, 0); | |
671 | } | |
672 | ||
673 | /* Do a QSFP read operation on i2c chain for HFI 1. */ | |
674 | static ssize_t qsfp2_debugfs_read(struct file *file, char __user *buf, | |
675 | size_t count, loff_t *ppos) | |
676 | { | |
677 | return __qsfp_debugfs_read(file, buf, count, ppos, 1); | |
678 | } | |
679 | ||
680 | #define DEBUGFS_OPS(nm, readroutine, writeroutine) \ | |
681 | { \ | |
682 | .name = nm, \ | |
683 | .ops = { \ | |
684 | .read = readroutine, \ | |
685 | .write = writeroutine, \ | |
686 | .llseek = generic_file_llseek, \ | |
687 | }, \ | |
688 | } | |
689 | ||
690 | static const struct counter_info cntr_ops[] = { | |
691 | DEBUGFS_OPS("counter_names", dev_names_read, NULL), | |
692 | DEBUGFS_OPS("counters", dev_counters_read, NULL), | |
693 | DEBUGFS_OPS("portcounter_names", portnames_read, NULL), | |
694 | }; | |
695 | ||
696 | static const struct counter_info port_cntr_ops[] = { | |
697 | DEBUGFS_OPS("port%dcounters", portcntrs_debugfs_read, NULL), | |
698 | DEBUGFS_OPS("i2c1", i2c1_debugfs_read, i2c1_debugfs_write), | |
699 | DEBUGFS_OPS("i2c2", i2c2_debugfs_read, i2c2_debugfs_write), | |
700 | DEBUGFS_OPS("qsfp_dump%d", qsfp_debugfs_dump, NULL), | |
701 | DEBUGFS_OPS("qsfp1", qsfp1_debugfs_read, qsfp1_debugfs_write), | |
702 | DEBUGFS_OPS("qsfp2", qsfp2_debugfs_read, qsfp2_debugfs_write), | |
703 | }; | |
704 | ||
705 | void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd) | |
706 | { | |
707 | char name[sizeof("port0counters") + 1]; | |
708 | char link[10]; | |
709 | struct hfi1_devdata *dd = dd_from_dev(ibd); | |
710 | struct hfi1_pportdata *ppd; | |
711 | int unit = dd->unit; | |
712 | int i, j; | |
713 | ||
714 | if (!hfi1_dbg_root) | |
715 | return; | |
716 | snprintf(name, sizeof(name), "%s_%d", class_name(), unit); | |
717 | snprintf(link, sizeof(link), "%d", unit); | |
718 | ibd->hfi1_ibdev_dbg = debugfs_create_dir(name, hfi1_dbg_root); | |
719 | if (!ibd->hfi1_ibdev_dbg) { | |
720 | pr_warn("create of %s failed\n", name); | |
721 | return; | |
722 | } | |
723 | ibd->hfi1_ibdev_link = | |
724 | debugfs_create_symlink(link, hfi1_dbg_root, name); | |
725 | if (!ibd->hfi1_ibdev_link) { | |
726 | pr_warn("create of %s symlink failed\n", name); | |
727 | return; | |
728 | } | |
729 | DEBUGFS_SEQ_FILE_CREATE(opcode_stats, ibd->hfi1_ibdev_dbg, ibd); | |
730 | DEBUGFS_SEQ_FILE_CREATE(ctx_stats, ibd->hfi1_ibdev_dbg, ibd); | |
731 | DEBUGFS_SEQ_FILE_CREATE(qp_stats, ibd->hfi1_ibdev_dbg, ibd); | |
732 | DEBUGFS_SEQ_FILE_CREATE(sdes, ibd->hfi1_ibdev_dbg, ibd); | |
733 | /* dev counter files */ | |
734 | for (i = 0; i < ARRAY_SIZE(cntr_ops); i++) | |
735 | DEBUGFS_FILE_CREATE(cntr_ops[i].name, | |
736 | ibd->hfi1_ibdev_dbg, | |
737 | dd, | |
738 | &cntr_ops[i].ops, S_IRUGO); | |
739 | /* per port files */ | |
740 | for (ppd = dd->pport, j = 0; j < dd->num_pports; j++, ppd++) | |
741 | for (i = 0; i < ARRAY_SIZE(port_cntr_ops); i++) { | |
742 | snprintf(name, | |
743 | sizeof(name), | |
744 | port_cntr_ops[i].name, | |
745 | j + 1); | |
746 | DEBUGFS_FILE_CREATE(name, | |
747 | ibd->hfi1_ibdev_dbg, | |
748 | ppd, | |
749 | &port_cntr_ops[i].ops, | |
750 | port_cntr_ops[i].ops.write == NULL ? | |
751 | S_IRUGO : S_IRUGO|S_IWUSR); | |
752 | } | |
753 | } | |
754 | ||
755 | void hfi1_dbg_ibdev_exit(struct hfi1_ibdev *ibd) | |
756 | { | |
757 | if (!hfi1_dbg_root) | |
758 | goto out; | |
759 | debugfs_remove(ibd->hfi1_ibdev_link); | |
760 | debugfs_remove_recursive(ibd->hfi1_ibdev_dbg); | |
761 | out: | |
762 | ibd->hfi1_ibdev_dbg = NULL; | |
763 | synchronize_rcu(); | |
764 | } | |
765 | ||
766 | /* | |
767 | * driver stats field names, one line per stat, single string. Used by | |
768 | * programs like hfistats to print the stats in a way which works for | |
769 | * different versions of drivers, without changing program source. | |
770 | * if hfi1_ib_stats changes, this needs to change. Names need to be | |
771 | * 12 chars or less (w/o newline), for proper display by hfistats utility. | |
772 | */ | |
773 | static const char * const hfi1_statnames[] = { | |
774 | /* must be element 0*/ | |
775 | "KernIntr", | |
776 | "ErrorIntr", | |
777 | "Tx_Errs", | |
778 | "Rcv_Errs", | |
779 | "H/W_Errs", | |
780 | "NoPIOBufs", | |
781 | "CtxtsOpen", | |
782 | "RcvLen_Errs", | |
783 | "EgrBufFull", | |
784 | "EgrHdrFull" | |
785 | }; | |
786 | ||
787 | static void *_driver_stats_names_seq_start(struct seq_file *s, loff_t *pos) | |
788 | __acquires(RCU) | |
789 | { | |
790 | rcu_read_lock(); | |
791 | if (*pos >= ARRAY_SIZE(hfi1_statnames)) | |
792 | return NULL; | |
793 | return pos; | |
794 | } | |
795 | ||
796 | static void *_driver_stats_names_seq_next( | |
797 | struct seq_file *s, | |
798 | void *v, | |
799 | loff_t *pos) | |
800 | { | |
801 | ++*pos; | |
802 | if (*pos >= ARRAY_SIZE(hfi1_statnames)) | |
803 | return NULL; | |
804 | return pos; | |
805 | } | |
806 | ||
807 | static void _driver_stats_names_seq_stop(struct seq_file *s, void *v) | |
808 | __releases(RCU) | |
809 | { | |
810 | rcu_read_unlock(); | |
811 | } | |
812 | ||
813 | static int _driver_stats_names_seq_show(struct seq_file *s, void *v) | |
814 | { | |
815 | loff_t *spos = v; | |
816 | ||
817 | seq_printf(s, "%s\n", hfi1_statnames[*spos]); | |
818 | return 0; | |
819 | } | |
820 | ||
821 | DEBUGFS_SEQ_FILE_OPS(driver_stats_names); | |
822 | DEBUGFS_SEQ_FILE_OPEN(driver_stats_names) | |
823 | DEBUGFS_FILE_OPS(driver_stats_names); | |
824 | ||
825 | static void *_driver_stats_seq_start(struct seq_file *s, loff_t *pos) | |
826 | __acquires(RCU) | |
827 | { | |
828 | rcu_read_lock(); | |
829 | if (*pos >= ARRAY_SIZE(hfi1_statnames)) | |
830 | return NULL; | |
831 | return pos; | |
832 | } | |
833 | ||
834 | static void *_driver_stats_seq_next(struct seq_file *s, void *v, loff_t *pos) | |
835 | { | |
836 | ++*pos; | |
837 | if (*pos >= ARRAY_SIZE(hfi1_statnames)) | |
838 | return NULL; | |
839 | return pos; | |
840 | } | |
841 | ||
842 | static void _driver_stats_seq_stop(struct seq_file *s, void *v) | |
843 | __releases(RCU) | |
844 | { | |
845 | rcu_read_unlock(); | |
846 | } | |
847 | ||
848 | static u64 hfi1_sps_ints(void) | |
849 | { | |
850 | unsigned long flags; | |
851 | struct hfi1_devdata *dd; | |
852 | u64 sps_ints = 0; | |
853 | ||
854 | spin_lock_irqsave(&hfi1_devs_lock, flags); | |
855 | list_for_each_entry(dd, &hfi1_dev_list, list) { | |
856 | sps_ints += get_all_cpu_total(dd->int_counter); | |
857 | } | |
858 | spin_unlock_irqrestore(&hfi1_devs_lock, flags); | |
859 | return sps_ints; | |
860 | } | |
861 | ||
862 | static int _driver_stats_seq_show(struct seq_file *s, void *v) | |
863 | { | |
864 | loff_t *spos = v; | |
865 | char *buffer; | |
866 | u64 *stats = (u64 *)&hfi1_stats; | |
867 | size_t sz = seq_get_buf(s, &buffer); | |
868 | ||
869 | if (sz < sizeof(u64)) | |
870 | return SEQ_SKIP; | |
871 | /* special case for interrupts */ | |
872 | if (*spos == 0) | |
873 | *(u64 *)buffer = hfi1_sps_ints(); | |
874 | else | |
875 | *(u64 *)buffer = stats[*spos]; | |
876 | seq_commit(s, sizeof(u64)); | |
877 | return 0; | |
878 | } | |
879 | ||
880 | DEBUGFS_SEQ_FILE_OPS(driver_stats); | |
881 | DEBUGFS_SEQ_FILE_OPEN(driver_stats) | |
882 | DEBUGFS_FILE_OPS(driver_stats); | |
883 | ||
884 | void hfi1_dbg_init(void) | |
885 | { | |
886 | hfi1_dbg_root = debugfs_create_dir(DRIVER_NAME, NULL); | |
887 | if (!hfi1_dbg_root) | |
888 | pr_warn("init of debugfs failed\n"); | |
889 | DEBUGFS_SEQ_FILE_CREATE(driver_stats_names, hfi1_dbg_root, NULL); | |
890 | DEBUGFS_SEQ_FILE_CREATE(driver_stats, hfi1_dbg_root, NULL); | |
891 | } | |
892 | ||
893 | void hfi1_dbg_exit(void) | |
894 | { | |
895 | debugfs_remove_recursive(hfi1_dbg_root); | |
896 | hfi1_dbg_root = NULL; | |
897 | } | |
898 | ||
899 | #endif |