aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllen Hubbe <Allen.Hubbe@emc.com>2015-05-21 02:51:39 -0400
committerJon Mason <jdmason@kudzu.us>2015-07-04 14:08:17 -0400
commit578b881ba9c4b253482903bf8fae438318f5629b (patch)
treeecbe76c342d3bd0c1725fa141503393b4825cd3a
parent963de4739fb4f8fa8d5cd87969109a7d3282ed13 (diff)
NTB: Add tool test client
This is a simple debugging driver that enables the doorbell and scratch pad registers to be read and written from the debugfs. This tool enables more complicated debugging to be scripted from user space. This driver may be used to test that your ntb hardware and drivers are functioning at a basic level. Signed-off-by: Allen Hubbe <Allen.Hubbe@emc.com> Signed-off-by: Jon Mason <jdmason@kudzu.us>
-rw-r--r--Documentation/ntb.txt32
-rw-r--r--drivers/ntb/test/Kconfig11
-rw-r--r--drivers/ntb/test/Makefile1
-rw-r--r--drivers/ntb/test/ntb_tool.c556
4 files changed, 600 insertions, 0 deletions
diff --git a/Documentation/ntb.txt b/Documentation/ntb.txt
index e5d1591478ca..b48249a7b607 100644
--- a/Documentation/ntb.txt
+++ b/Documentation/ntb.txt
@@ -64,6 +64,38 @@ Module Parameters:
64* dyndbg - It is suggested to specify dyndbg=+p when loading this module, and 64* dyndbg - It is suggested to specify dyndbg=+p when loading this module, and
65 then to observe debugging output on the console. 65 then to observe debugging output on the console.
66 66
67### NTB Tool Test Client (ntb\_tool)
68
69The Tool test client serves for debugging, primarily, ntb hardware and drivers.
70The Tool provides access through debugfs for reading, setting, and clearing the
71NTB doorbell, and reading and writing scratchpads.
72
73The Tool does not currently have any module parameters.
74
75Debugfs Files:
76
77* *debugfs*/ntb\_tool/*hw*/ - A directory in debugfs will be created for each
78 NTB device probed by the tool. This directory is shortened to *hw*
79 below.
80* *hw*/db - This file is used to read, set, and clear the local doorbell. Not
81 all operations may be supported by all hardware. To read the doorbell,
82 read the file. To set the doorbell, write `s` followed by the bits to
83 set (eg: `echo 's 0x0101' > db`). To clear the doorbell, write `c`
84 followed by the bits to clear.
85* *hw*/mask - This file is used to read, set, and clear the local doorbell mask.
86 See *db* for details.
87* *hw*/peer\_db - This file is used to read, set, and clear the peer doorbell.
88 See *db* for details.
89* *hw*/peer\_mask - This file is used to read, set, and clear the peer doorbell
90 mask. See *db* for details.
91* *hw*/spad - This file is used to read and write local scratchpads. To read
92 the values of all scratchpads, read the file. To write values, write a
93 series of pairs of scratchpad number and value
94 (eg: `echo '4 0x123 7 0xabc' > spad`
95 # to set scratchpads `4` and `7` to `0x123` and `0xabc`, respectively).
96* *hw*/peer\_spad - This file is used to read and write peer scratchpads. See
97 *spad* for details.
98
67## NTB Hardware Drivers 99## NTB Hardware Drivers
68 100
69NTB hardware drivers should register devices with the NTB core driver. After 101NTB hardware drivers should register devices with the NTB core driver. After
diff --git a/drivers/ntb/test/Kconfig b/drivers/ntb/test/Kconfig
index 72d255d220c8..01852f98a843 100644
--- a/drivers/ntb/test/Kconfig
+++ b/drivers/ntb/test/Kconfig
@@ -6,3 +6,14 @@ config NTB_PINGPONG
6 your ntb hardware and drivers are functioning at a basic level. 6 your ntb hardware and drivers are functioning at a basic level.
7 7
8 If unsure, say N. 8 If unsure, say N.
9
10config NTB_TOOL
11 tristate "NTB Debugging Tool Test Client"
12 help
13 This is a simple debugging driver that enables the doorbell and
14 scratchpad registers to be read and written from the debugfs. This
15 enables more complicated debugging to be scripted from user space.
16 This driver may be used to test that your ntb hardware and drivers are
17 functioning at a basic level.
18
19 If unsure, say N.
diff --git a/drivers/ntb/test/Makefile b/drivers/ntb/test/Makefile
index b32309307771..0ea32a324b6c 100644
--- a/drivers/ntb/test/Makefile
+++ b/drivers/ntb/test/Makefile
@@ -1 +1,2 @@
1obj-$(CONFIG_NTB_PINGPONG) += ntb_pingpong.o 1obj-$(CONFIG_NTB_PINGPONG) += ntb_pingpong.o
2obj-$(CONFIG_NTB_TOOL) += ntb_tool.o
diff --git a/drivers/ntb/test/ntb_tool.c b/drivers/ntb/test/ntb_tool.c
new file mode 100644
index 000000000000..6f5dc6ca673d
--- /dev/null
+++ b/drivers/ntb/test/ntb_tool.c
@@ -0,0 +1,556 @@
1/*
2 * This file is provided under a dual BSD/GPLv2 license. When using or
3 * redistributing this file, you may do so under either license.
4 *
5 * GPL LICENSE SUMMARY
6 *
7 * Copyright (C) 2015 EMC Corporation. All Rights Reserved.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of version 2 of the GNU General Public License as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * BSD LICENSE
19 *
20 * Copyright (C) 2015 EMC Corporation. All Rights Reserved.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 *
26 * * Redistributions of source code must retain the above copyright
27 * notice, this list of conditions and the following disclaimer.
28 * * Redistributions in binary form must reproduce the above copy
29 * notice, this list of conditions and the following disclaimer in
30 * the documentation and/or other materials provided with the
31 * distribution.
32 * * Neither the name of Intel Corporation nor the names of its
33 * contributors may be used to endorse or promote products derived
34 * from this software without specific prior written permission.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
37 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
38 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
39 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
40 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
43 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
45 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
46 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47 *
48 * PCIe NTB Debugging Tool Linux driver
49 *
50 * Contact Information:
51 * Allen Hubbe <Allen.Hubbe@emc.com>
52 */
53
54/*
55 * How to use this tool, by example.
56 *
57 * Assuming $DBG_DIR is something like:
58 * '/sys/kernel/debug/ntb_tool/0000:00:03.0'
59 *
60 * Eg: check if clearing the doorbell mask generates an interrupt.
61 *
62 * # Set the doorbell mask
63 * root@self# echo 's 1' > $DBG_DIR/mask
64 *
65 * # Ring the doorbell from the peer
66 * root@peer# echo 's 1' > $DBG_DIR/peer_db
67 *
68 * # Clear the doorbell mask
69 * root@self# echo 'c 1' > $DBG_DIR/mask
70 *
71 * Observe debugging output in dmesg or your console. You should see a
72 * doorbell event triggered by clearing the mask. If not, this may indicate an
73 * issue with the hardware that needs to be worked around in the driver.
74 *
75 * Eg: read and write scratchpad registers
76 *
77 * root@peer# echo '0 0x01010101 1 0x7f7f7f7f' > $DBG_DIR/peer_spad
78 *
79 * root@self# cat $DBG_DIR/spad
80 *
81 * Observe that spad 0 and 1 have the values set by the peer.
82 */
83
84#include <linux/init.h>
85#include <linux/kernel.h>
86#include <linux/module.h>
87
88#include <linux/debugfs.h>
89#include <linux/dma-mapping.h>
90#include <linux/pci.h>
91#include <linux/slab.h>
92
93#include <linux/ntb.h>
94
95#define DRIVER_NAME "ntb_tool"
96#define DRIVER_DESCRIPTION "PCIe NTB Debugging Tool"
97
98#define DRIVER_LICENSE "Dual BSD/GPL"
99#define DRIVER_VERSION "1.0"
100#define DRIVER_RELDATE "22 April 2015"
101#define DRIVER_AUTHOR "Allen Hubbe <Allen.Hubbe@emc.com>"
102
103MODULE_LICENSE(DRIVER_LICENSE);
104MODULE_VERSION(DRIVER_VERSION);
105MODULE_AUTHOR(DRIVER_AUTHOR);
106MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
107
108static struct dentry *tool_dbgfs;
109
110struct tool_ctx {
111 struct ntb_dev *ntb;
112 struct dentry *dbgfs;
113};
114
115#define SPAD_FNAME_SIZE 0x10
116#define INT_PTR(x) ((void *)(unsigned long)x)
117#define PTR_INT(x) ((int)(unsigned long)x)
118
119#define TOOL_FOPS_RDWR(__name, __read, __write) \
120 const struct file_operations __name = { \
121 .owner = THIS_MODULE, \
122 .open = simple_open, \
123 .read = __read, \
124 .write = __write, \
125 }
126
127static void tool_link_event(void *ctx)
128{
129 struct tool_ctx *tc = ctx;
130 enum ntb_speed speed;
131 enum ntb_width width;
132 int up;
133
134 up = ntb_link_is_up(tc->ntb, &speed, &width);
135
136 dev_dbg(&tc->ntb->dev, "link is %s speed %d width %d\n",
137 up ? "up" : "down", speed, width);
138}
139
140static void tool_db_event(void *ctx, int vec)
141{
142 struct tool_ctx *tc = ctx;
143 u64 db_bits, db_mask;
144
145 db_mask = ntb_db_vector_mask(tc->ntb, vec);
146 db_bits = ntb_db_read(tc->ntb);
147
148 dev_dbg(&tc->ntb->dev, "doorbell vec %d mask %#llx bits %#llx\n",
149 vec, db_mask, db_bits);
150}
151
152static const struct ntb_ctx_ops tool_ops = {
153 .link_event = tool_link_event,
154 .db_event = tool_db_event,
155};
156
157static ssize_t tool_dbfn_read(struct tool_ctx *tc, char __user *ubuf,
158 size_t size, loff_t *offp,
159 u64 (*db_read_fn)(struct ntb_dev *))
160{
161 size_t buf_size;
162 char *buf;
163 ssize_t pos, rc;
164
165 if (!db_read_fn)
166 return -EINVAL;
167
168 buf_size = min_t(size_t, size, 0x20);
169
170 buf = kmalloc(buf_size, GFP_KERNEL);
171 if (!buf)
172 return -ENOMEM;
173
174 pos = scnprintf(buf, buf_size, "%#llx\n",
175 db_read_fn(tc->ntb));
176
177 rc = simple_read_from_buffer(ubuf, size, offp, buf, pos);
178
179 kfree(buf);
180
181 return rc;
182}
183
184static ssize_t tool_dbfn_write(struct tool_ctx *tc,
185 const char __user *ubuf,
186 size_t size, loff_t *offp,
187 int (*db_set_fn)(struct ntb_dev *, u64),
188 int (*db_clear_fn)(struct ntb_dev *, u64))
189{
190 u64 db_bits;
191 char *buf, cmd;
192 ssize_t rc;
193 int n;
194
195 buf = kmalloc(size + 1, GFP_KERNEL);
196 if (!buf)
197 return -ENOMEM;
198
199 rc = simple_write_to_buffer(buf, size, offp, ubuf, size);
200 if (rc < 0) {
201 kfree(buf);
202 return rc;
203 }
204
205 buf[size] = 0;
206
207 n = sscanf(buf, "%c %lli", &cmd, &db_bits);
208
209 kfree(buf);
210
211 if (n != 2) {
212 rc = -EINVAL;
213 } else if (cmd == 's') {
214 if (!db_set_fn)
215 rc = -EINVAL;
216 else
217 rc = db_set_fn(tc->ntb, db_bits);
218 } else if (cmd == 'c') {
219 if (!db_clear_fn)
220 rc = -EINVAL;
221 else
222 rc = db_clear_fn(tc->ntb, db_bits);
223 } else {
224 rc = -EINVAL;
225 }
226
227 return rc ? : size;
228}
229
230static ssize_t tool_spadfn_read(struct tool_ctx *tc, char __user *ubuf,
231 size_t size, loff_t *offp,
232 u32 (*spad_read_fn)(struct ntb_dev *, int))
233{
234 size_t buf_size;
235 char *buf;
236 ssize_t pos, rc;
237 int i, spad_count;
238
239 if (!spad_read_fn)
240 return -EINVAL;
241
242 buf_size = min_t(size_t, size, 0x100);
243
244 buf = kmalloc(buf_size, GFP_KERNEL);
245 if (!buf)
246 return -ENOMEM;
247
248 pos = 0;
249
250 spad_count = ntb_spad_count(tc->ntb);
251 for (i = 0; i < spad_count; ++i) {
252 pos += scnprintf(buf + pos, buf_size - pos, "%d\t%#x\n",
253 i, spad_read_fn(tc->ntb, i));
254 }
255
256 rc = simple_read_from_buffer(ubuf, size, offp, buf, pos);
257
258 kfree(buf);
259
260 return rc;
261}
262
263static ssize_t tool_spadfn_write(struct tool_ctx *tc,
264 const char __user *ubuf,
265 size_t size, loff_t *offp,
266 int (*spad_write_fn)(struct ntb_dev *,
267 int, u32))
268{
269 int spad_idx;
270 u32 spad_val;
271 char *buf;
272 int pos, n;
273 ssize_t rc;
274
275 if (!spad_write_fn) {
276 dev_dbg(&tc->ntb->dev, "no spad write fn\n");
277 return -EINVAL;
278 }
279
280 buf = kmalloc(size + 1, GFP_KERNEL);
281 if (!buf)
282 return -ENOMEM;
283
284 rc = simple_write_to_buffer(buf, size, offp, ubuf, size);
285 if (rc < 0) {
286 kfree(buf);
287 return rc;
288 }
289
290 buf[size] = 0;
291
292 n = sscanf(buf, "%d %i%n", &spad_idx, &spad_val, &pos);
293 while (n == 2) {
294 rc = spad_write_fn(tc->ntb, spad_idx, spad_val);
295 if (rc)
296 break;
297
298 n = sscanf(buf + pos, "%d %i%n", &spad_idx, &spad_val, &pos);
299 }
300
301 if (n < 0)
302 rc = n;
303
304 kfree(buf);
305
306 return rc ? : size;
307}
308
309static ssize_t tool_db_read(struct file *filep, char __user *ubuf,
310 size_t size, loff_t *offp)
311{
312 struct tool_ctx *tc = filep->private_data;
313
314 return tool_dbfn_read(tc, ubuf, size, offp,
315 tc->ntb->ops->db_read);
316}
317
318static ssize_t tool_db_write(struct file *filep, const char __user *ubuf,
319 size_t size, loff_t *offp)
320{
321 struct tool_ctx *tc = filep->private_data;
322
323 return tool_dbfn_write(tc, ubuf, size, offp,
324 tc->ntb->ops->db_set,
325 tc->ntb->ops->db_clear);
326}
327
328static TOOL_FOPS_RDWR(tool_db_fops,
329 tool_db_read,
330 tool_db_write);
331
332static ssize_t tool_mask_read(struct file *filep, char __user *ubuf,
333 size_t size, loff_t *offp)
334{
335 struct tool_ctx *tc = filep->private_data;
336
337 return tool_dbfn_read(tc, ubuf, size, offp,
338 tc->ntb->ops->db_read_mask);
339}
340
341static ssize_t tool_mask_write(struct file *filep, const char __user *ubuf,
342 size_t size, loff_t *offp)
343{
344 struct tool_ctx *tc = filep->private_data;
345
346 return tool_dbfn_write(tc, ubuf, size, offp,
347 tc->ntb->ops->db_set_mask,
348 tc->ntb->ops->db_clear_mask);
349}
350
351static TOOL_FOPS_RDWR(tool_mask_fops,
352 tool_mask_read,
353 tool_mask_write);
354
355static ssize_t tool_peer_db_read(struct file *filep, char __user *ubuf,
356 size_t size, loff_t *offp)
357{
358 struct tool_ctx *tc = filep->private_data;
359
360 return tool_dbfn_read(tc, ubuf, size, offp,
361 tc->ntb->ops->peer_db_read);
362}
363
364static ssize_t tool_peer_db_write(struct file *filep, const char __user *ubuf,
365 size_t size, loff_t *offp)
366{
367 struct tool_ctx *tc = filep->private_data;
368
369 return tool_dbfn_write(tc, ubuf, size, offp,
370 tc->ntb->ops->peer_db_set,
371 tc->ntb->ops->peer_db_clear);
372}
373
374static TOOL_FOPS_RDWR(tool_peer_db_fops,
375 tool_peer_db_read,
376 tool_peer_db_write);
377
378static ssize_t tool_peer_mask_read(struct file *filep, char __user *ubuf,
379 size_t size, loff_t *offp)
380{
381 struct tool_ctx *tc = filep->private_data;
382
383 return tool_dbfn_read(tc, ubuf, size, offp,
384 tc->ntb->ops->peer_db_read_mask);
385}
386
387static ssize_t tool_peer_mask_write(struct file *filep, const char __user *ubuf,
388 size_t size, loff_t *offp)
389{
390 struct tool_ctx *tc = filep->private_data;
391
392 return tool_dbfn_write(tc, ubuf, size, offp,
393 tc->ntb->ops->peer_db_set_mask,
394 tc->ntb->ops->peer_db_clear_mask);
395}
396
397static TOOL_FOPS_RDWR(tool_peer_mask_fops,
398 tool_peer_mask_read,
399 tool_peer_mask_write);
400
401static ssize_t tool_spad_read(struct file *filep, char __user *ubuf,
402 size_t size, loff_t *offp)
403{
404 struct tool_ctx *tc = filep->private_data;
405
406 return tool_spadfn_read(tc, ubuf, size, offp,
407 tc->ntb->ops->spad_read);
408}
409
410static ssize_t tool_spad_write(struct file *filep, const char __user *ubuf,
411 size_t size, loff_t *offp)
412{
413 struct tool_ctx *tc = filep->private_data;
414
415 return tool_spadfn_write(tc, ubuf, size, offp,
416 tc->ntb->ops->spad_write);
417}
418
419static TOOL_FOPS_RDWR(tool_spad_fops,
420 tool_spad_read,
421 tool_spad_write);
422
423static ssize_t tool_peer_spad_read(struct file *filep, char __user *ubuf,
424 size_t size, loff_t *offp)
425{
426 struct tool_ctx *tc = filep->private_data;
427
428 return tool_spadfn_read(tc, ubuf, size, offp,
429 tc->ntb->ops->peer_spad_read);
430}
431
432static ssize_t tool_peer_spad_write(struct file *filep, const char __user *ubuf,
433 size_t size, loff_t *offp)
434{
435 struct tool_ctx *tc = filep->private_data;
436
437 return tool_spadfn_write(tc, ubuf, size, offp,
438 tc->ntb->ops->peer_spad_write);
439}
440
441static TOOL_FOPS_RDWR(tool_peer_spad_fops,
442 tool_peer_spad_read,
443 tool_peer_spad_write);
444
445static void tool_setup_dbgfs(struct tool_ctx *tc)
446{
447 /* This modules is useless without dbgfs... */
448 if (!tool_dbgfs) {
449 tc->dbgfs = NULL;
450 return;
451 }
452
453 tc->dbgfs = debugfs_create_dir(dev_name(&tc->ntb->dev),
454 tool_dbgfs);
455 if (!tc->dbgfs)
456 return;
457
458 debugfs_create_file("db", S_IRUSR | S_IWUSR, tc->dbgfs,
459 tc, &tool_db_fops);
460
461 debugfs_create_file("mask", S_IRUSR | S_IWUSR, tc->dbgfs,
462 tc, &tool_mask_fops);
463
464 debugfs_create_file("peer_db", S_IRUSR | S_IWUSR, tc->dbgfs,
465 tc, &tool_peer_db_fops);
466
467 debugfs_create_file("peer_mask", S_IRUSR | S_IWUSR, tc->dbgfs,
468 tc, &tool_peer_mask_fops);
469
470 debugfs_create_file("spad", S_IRUSR | S_IWUSR, tc->dbgfs,
471 tc, &tool_spad_fops);
472
473 debugfs_create_file("peer_spad", S_IRUSR | S_IWUSR, tc->dbgfs,
474 tc, &tool_peer_spad_fops);
475}
476
477static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb)
478{
479 struct tool_ctx *tc;
480 int rc;
481
482 if (ntb_db_is_unsafe(ntb))
483 dev_dbg(&ntb->dev, "doorbell is unsafe\n");
484
485 if (ntb_spad_is_unsafe(ntb))
486 dev_dbg(&ntb->dev, "scratchpad is unsafe\n");
487
488 tc = kmalloc(sizeof(*tc), GFP_KERNEL);
489 if (!tc) {
490 rc = -ENOMEM;
491 goto err_tc;
492 }
493
494 tc->ntb = ntb;
495
496 tool_setup_dbgfs(tc);
497
498 rc = ntb_set_ctx(ntb, tc, &tool_ops);
499 if (rc)
500 goto err_ctx;
501
502 ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
503 ntb_link_event(ntb);
504
505 return 0;
506
507err_ctx:
508 debugfs_remove_recursive(tc->dbgfs);
509 kfree(tc);
510err_tc:
511 return rc;
512}
513
514static void tool_remove(struct ntb_client *self, struct ntb_dev *ntb)
515{
516 struct tool_ctx *tc = ntb->ctx;
517
518 ntb_clear_ctx(ntb);
519 ntb_link_disable(ntb);
520
521 debugfs_remove_recursive(tc->dbgfs);
522 kfree(tc);
523}
524
525static struct ntb_client tool_client = {
526 .ops = {
527 .probe = tool_probe,
528 .remove = tool_remove,
529 },
530};
531
532static int __init tool_init(void)
533{
534 int rc;
535
536 if (debugfs_initialized())
537 tool_dbgfs = debugfs_create_dir(KBUILD_MODNAME, NULL);
538
539 rc = ntb_register_client(&tool_client);
540 if (rc)
541 goto err_client;
542
543 return 0;
544
545err_client:
546 debugfs_remove_recursive(tool_dbgfs);
547 return rc;
548}
549module_init(tool_init);
550
551static void __exit tool_exit(void)
552{
553 ntb_unregister_client(&tool_client);
554 debugfs_remove_recursive(tool_dbgfs);
555}
556module_exit(tool_exit);