diff options
author | Logan Gunthorpe <logang@deltatee.com> | 2016-06-03 16:50:33 -0400 |
---|---|---|
committer | Jon Mason <jdmason@kudzu.us> | 2016-08-05 10:21:06 -0400 |
commit | 8b71d285061181f91194114cc7dabce73185eed1 (patch) | |
tree | 8307d79700534732dbd8e2e6d5d9e81c6b8a0711 /drivers/ntb/test | |
parent | 4aae977721f0367809cdc94584b6945073d9fe10 (diff) |
ntb_tool: Add memory window debug support
We allocate some memory window buffers when the link comes up, then we
provide debugfs files to read/write each side of the link.
This is useful for debugging the mapping when writing new drivers.
Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Acked-by: Allen Hubbe <Allen.Hubbe@emc.com>
Signed-off-by: Jon Mason <jdmason@kudzu.us>
Diffstat (limited to 'drivers/ntb/test')
-rw-r--r-- | drivers/ntb/test/ntb_tool.c | 258 |
1 files changed, 257 insertions, 1 deletions
diff --git a/drivers/ntb/test/ntb_tool.c b/drivers/ntb/test/ntb_tool.c index a0ead31cd799..cba31fd0a07a 100644 --- a/drivers/ntb/test/ntb_tool.c +++ b/drivers/ntb/test/ntb_tool.c | |||
@@ -89,6 +89,7 @@ | |||
89 | #include <linux/dma-mapping.h> | 89 | #include <linux/dma-mapping.h> |
90 | #include <linux/pci.h> | 90 | #include <linux/pci.h> |
91 | #include <linux/slab.h> | 91 | #include <linux/slab.h> |
92 | #include <linux/uaccess.h> | ||
92 | 93 | ||
93 | #include <linux/ntb.h> | 94 | #include <linux/ntb.h> |
94 | 95 | ||
@@ -105,11 +106,29 @@ MODULE_VERSION(DRIVER_VERSION); | |||
105 | MODULE_AUTHOR(DRIVER_AUTHOR); | 106 | MODULE_AUTHOR(DRIVER_AUTHOR); |
106 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION); | 107 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION); |
107 | 108 | ||
109 | #define MAX_MWS 16 | ||
110 | |||
111 | static unsigned long mw_size = 16; | ||
112 | module_param(mw_size, ulong, 0644); | ||
113 | MODULE_PARM_DESC(mw_size, "size order [n^2] of the memory window for testing"); | ||
114 | |||
108 | static struct dentry *tool_dbgfs; | 115 | static struct dentry *tool_dbgfs; |
109 | 116 | ||
117 | struct tool_mw { | ||
118 | resource_size_t size; | ||
119 | u8 __iomem *local; | ||
120 | u8 *peer; | ||
121 | dma_addr_t peer_dma; | ||
122 | }; | ||
123 | |||
110 | struct tool_ctx { | 124 | struct tool_ctx { |
111 | struct ntb_dev *ntb; | 125 | struct ntb_dev *ntb; |
112 | struct dentry *dbgfs; | 126 | struct dentry *dbgfs; |
127 | struct work_struct link_cleanup; | ||
128 | bool link_is_up; | ||
129 | struct delayed_work link_work; | ||
130 | int mw_count; | ||
131 | struct tool_mw mws[MAX_MWS]; | ||
113 | }; | 132 | }; |
114 | 133 | ||
115 | #define SPAD_FNAME_SIZE 0x10 | 134 | #define SPAD_FNAME_SIZE 0x10 |
@@ -124,6 +143,111 @@ struct tool_ctx { | |||
124 | .write = __write, \ | 143 | .write = __write, \ |
125 | } | 144 | } |
126 | 145 | ||
146 | static int tool_setup_mw(struct tool_ctx *tc, int idx) | ||
147 | { | ||
148 | int rc; | ||
149 | struct tool_mw *mw = &tc->mws[idx]; | ||
150 | phys_addr_t base; | ||
151 | resource_size_t size, align, align_size; | ||
152 | |||
153 | if (mw->local) | ||
154 | return 0; | ||
155 | |||
156 | rc = ntb_mw_get_range(tc->ntb, idx, &base, &size, &align, | ||
157 | &align_size); | ||
158 | if (rc) | ||
159 | return rc; | ||
160 | |||
161 | mw->size = min_t(resource_size_t, 1 << mw_size, size); | ||
162 | mw->size = round_up(mw->size, align); | ||
163 | mw->size = round_up(mw->size, align_size); | ||
164 | |||
165 | mw->local = ioremap_wc(base, size); | ||
166 | if (mw->local == NULL) | ||
167 | return -EFAULT; | ||
168 | |||
169 | mw->peer = dma_alloc_coherent(&tc->ntb->pdev->dev, mw->size, | ||
170 | &mw->peer_dma, GFP_KERNEL); | ||
171 | |||
172 | if (mw->peer == NULL) | ||
173 | return -ENOMEM; | ||
174 | |||
175 | rc = ntb_mw_set_trans(tc->ntb, idx, mw->peer_dma, mw->size); | ||
176 | if (rc) | ||
177 | return rc; | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static void tool_free_mws(struct tool_ctx *tc) | ||
183 | { | ||
184 | int i; | ||
185 | |||
186 | for (i = 0; i < tc->mw_count; i++) { | ||
187 | if (tc->mws[i].peer) { | ||
188 | ntb_mw_clear_trans(tc->ntb, i); | ||
189 | dma_free_coherent(&tc->ntb->pdev->dev, tc->mws[i].size, | ||
190 | tc->mws[i].peer, | ||
191 | tc->mws[i].peer_dma); | ||
192 | |||
193 | } | ||
194 | |||
195 | tc->mws[i].peer = NULL; | ||
196 | tc->mws[i].peer_dma = 0; | ||
197 | |||
198 | if (tc->mws[i].local) | ||
199 | iounmap(tc->mws[i].local); | ||
200 | |||
201 | tc->mws[i].local = NULL; | ||
202 | } | ||
203 | |||
204 | tc->mw_count = 0; | ||
205 | } | ||
206 | |||
207 | static int tool_setup_mws(struct tool_ctx *tc) | ||
208 | { | ||
209 | int i; | ||
210 | int rc; | ||
211 | |||
212 | tc->mw_count = min(ntb_mw_count(tc->ntb), MAX_MWS); | ||
213 | |||
214 | for (i = 0; i < tc->mw_count; i++) { | ||
215 | rc = tool_setup_mw(tc, i); | ||
216 | if (rc) | ||
217 | goto err_out; | ||
218 | } | ||
219 | |||
220 | return 0; | ||
221 | |||
222 | err_out: | ||
223 | tool_free_mws(tc); | ||
224 | return rc; | ||
225 | } | ||
226 | |||
227 | static void tool_link_work(struct work_struct *work) | ||
228 | { | ||
229 | int rc; | ||
230 | struct tool_ctx *tc = container_of(work, struct tool_ctx, | ||
231 | link_work.work); | ||
232 | |||
233 | tool_free_mws(tc); | ||
234 | rc = tool_setup_mws(tc); | ||
235 | if (rc) | ||
236 | dev_err(&tc->ntb->dev, | ||
237 | "Error setting up memory windows: %d\n", rc); | ||
238 | |||
239 | tc->link_is_up = true; | ||
240 | } | ||
241 | |||
242 | static void tool_link_cleanup(struct work_struct *work) | ||
243 | { | ||
244 | struct tool_ctx *tc = container_of(work, struct tool_ctx, | ||
245 | link_cleanup); | ||
246 | |||
247 | if (!tc->link_is_up) | ||
248 | cancel_delayed_work_sync(&tc->link_work); | ||
249 | } | ||
250 | |||
127 | static void tool_link_event(void *ctx) | 251 | static void tool_link_event(void *ctx) |
128 | { | 252 | { |
129 | struct tool_ctx *tc = ctx; | 253 | struct tool_ctx *tc = ctx; |
@@ -135,6 +259,11 @@ static void tool_link_event(void *ctx) | |||
135 | 259 | ||
136 | dev_dbg(&tc->ntb->dev, "link is %s speed %d width %d\n", | 260 | dev_dbg(&tc->ntb->dev, "link is %s speed %d width %d\n", |
137 | up ? "up" : "down", speed, width); | 261 | up ? "up" : "down", speed, width); |
262 | |||
263 | if (up) | ||
264 | schedule_delayed_work(&tc->link_work, 2*HZ); | ||
265 | else | ||
266 | schedule_work(&tc->link_cleanup); | ||
138 | } | 267 | } |
139 | 268 | ||
140 | static void tool_db_event(void *ctx, int vec) | 269 | static void tool_db_event(void *ctx, int vec) |
@@ -449,8 +578,112 @@ static TOOL_FOPS_RDWR(tool_peer_spad_fops, | |||
449 | tool_peer_spad_read, | 578 | tool_peer_spad_read, |
450 | tool_peer_spad_write); | 579 | tool_peer_spad_write); |
451 | 580 | ||
581 | |||
582 | static ssize_t tool_mw_read(struct file *filep, char __user *ubuf, | ||
583 | size_t size, loff_t *offp) | ||
584 | { | ||
585 | struct tool_mw *mw = filep->private_data; | ||
586 | ssize_t rc; | ||
587 | loff_t pos = *offp; | ||
588 | void *buf; | ||
589 | |||
590 | if (mw->local == NULL) | ||
591 | return -EIO; | ||
592 | if (pos < 0) | ||
593 | return -EINVAL; | ||
594 | if (pos >= mw->size || !size) | ||
595 | return 0; | ||
596 | if (size > mw->size - pos) | ||
597 | size = mw->size - pos; | ||
598 | |||
599 | buf = kmalloc(size, GFP_KERNEL); | ||
600 | if (!buf) | ||
601 | return -ENOMEM; | ||
602 | |||
603 | memcpy_fromio(buf, mw->local + pos, size); | ||
604 | rc = copy_to_user(ubuf, buf, size); | ||
605 | if (rc == size) { | ||
606 | rc = -EFAULT; | ||
607 | goto err_free; | ||
608 | } | ||
609 | |||
610 | size -= rc; | ||
611 | *offp = pos + size; | ||
612 | rc = size; | ||
613 | |||
614 | err_free: | ||
615 | kfree(buf); | ||
616 | |||
617 | return rc; | ||
618 | } | ||
619 | |||
620 | static ssize_t tool_mw_write(struct file *filep, const char __user *ubuf, | ||
621 | size_t size, loff_t *offp) | ||
622 | { | ||
623 | struct tool_mw *mw = filep->private_data; | ||
624 | ssize_t rc; | ||
625 | loff_t pos = *offp; | ||
626 | void *buf; | ||
627 | |||
628 | if (pos < 0) | ||
629 | return -EINVAL; | ||
630 | if (pos >= mw->size || !size) | ||
631 | return 0; | ||
632 | if (size > mw->size - pos) | ||
633 | size = mw->size - pos; | ||
634 | |||
635 | buf = kmalloc(size, GFP_KERNEL); | ||
636 | if (!buf) | ||
637 | return -ENOMEM; | ||
638 | |||
639 | rc = copy_from_user(buf, ubuf, size); | ||
640 | if (rc == size) { | ||
641 | rc = -EFAULT; | ||
642 | goto err_free; | ||
643 | } | ||
644 | |||
645 | size -= rc; | ||
646 | *offp = pos + size; | ||
647 | rc = size; | ||
648 | |||
649 | memcpy_toio(mw->local + pos, buf, size); | ||
650 | |||
651 | err_free: | ||
652 | kfree(buf); | ||
653 | |||
654 | return rc; | ||
655 | } | ||
656 | |||
657 | static TOOL_FOPS_RDWR(tool_mw_fops, | ||
658 | tool_mw_read, | ||
659 | tool_mw_write); | ||
660 | |||
661 | |||
662 | static ssize_t tool_peer_mw_read(struct file *filep, char __user *ubuf, | ||
663 | size_t size, loff_t *offp) | ||
664 | { | ||
665 | struct tool_mw *mw = filep->private_data; | ||
666 | |||
667 | return simple_read_from_buffer(ubuf, size, offp, mw->peer, mw->size); | ||
668 | } | ||
669 | |||
670 | static ssize_t tool_peer_mw_write(struct file *filep, const char __user *ubuf, | ||
671 | size_t size, loff_t *offp) | ||
672 | { | ||
673 | struct tool_mw *mw = filep->private_data; | ||
674 | |||
675 | return simple_write_to_buffer(mw->peer, mw->size, offp, ubuf, size); | ||
676 | } | ||
677 | |||
678 | static TOOL_FOPS_RDWR(tool_peer_mw_fops, | ||
679 | tool_peer_mw_read, | ||
680 | tool_peer_mw_write); | ||
681 | |||
452 | static void tool_setup_dbgfs(struct tool_ctx *tc) | 682 | static void tool_setup_dbgfs(struct tool_ctx *tc) |
453 | { | 683 | { |
684 | int mw_count; | ||
685 | int i; | ||
686 | |||
454 | /* This modules is useless without dbgfs... */ | 687 | /* This modules is useless without dbgfs... */ |
455 | if (!tool_dbgfs) { | 688 | if (!tool_dbgfs) { |
456 | tc->dbgfs = NULL; | 689 | tc->dbgfs = NULL; |
@@ -479,6 +712,20 @@ static void tool_setup_dbgfs(struct tool_ctx *tc) | |||
479 | 712 | ||
480 | debugfs_create_file("peer_spad", S_IRUSR | S_IWUSR, tc->dbgfs, | 713 | debugfs_create_file("peer_spad", S_IRUSR | S_IWUSR, tc->dbgfs, |
481 | tc, &tool_peer_spad_fops); | 714 | tc, &tool_peer_spad_fops); |
715 | |||
716 | mw_count = min(ntb_mw_count(tc->ntb), MAX_MWS); | ||
717 | for (i = 0; i < mw_count; i++) { | ||
718 | char buf[30]; | ||
719 | |||
720 | snprintf(buf, sizeof(buf), "mw%d", i); | ||
721 | debugfs_create_file(buf, S_IRUSR | S_IWUSR, tc->dbgfs, | ||
722 | &tc->mws[i], &tool_mw_fops); | ||
723 | |||
724 | snprintf(buf, sizeof(buf), "peer_mw%d", i); | ||
725 | debugfs_create_file(buf, S_IRUSR | S_IWUSR, tc->dbgfs, | ||
726 | &tc->mws[i], &tool_peer_mw_fops); | ||
727 | |||
728 | } | ||
482 | } | 729 | } |
483 | 730 | ||
484 | static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb) | 731 | static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb) |
@@ -492,13 +739,15 @@ static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb) | |||
492 | if (ntb_spad_is_unsafe(ntb)) | 739 | if (ntb_spad_is_unsafe(ntb)) |
493 | dev_dbg(&ntb->dev, "scratchpad is unsafe\n"); | 740 | dev_dbg(&ntb->dev, "scratchpad is unsafe\n"); |
494 | 741 | ||
495 | tc = kmalloc(sizeof(*tc), GFP_KERNEL); | 742 | tc = kzalloc(sizeof(*tc), GFP_KERNEL); |
496 | if (!tc) { | 743 | if (!tc) { |
497 | rc = -ENOMEM; | 744 | rc = -ENOMEM; |
498 | goto err_tc; | 745 | goto err_tc; |
499 | } | 746 | } |
500 | 747 | ||
501 | tc->ntb = ntb; | 748 | tc->ntb = ntb; |
749 | INIT_DELAYED_WORK(&tc->link_work, tool_link_work); | ||
750 | INIT_WORK(&tc->link_cleanup, tool_link_cleanup); | ||
502 | 751 | ||
503 | tool_setup_dbgfs(tc); | 752 | tool_setup_dbgfs(tc); |
504 | 753 | ||
@@ -513,6 +762,8 @@ static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb) | |||
513 | 762 | ||
514 | err_ctx: | 763 | err_ctx: |
515 | debugfs_remove_recursive(tc->dbgfs); | 764 | debugfs_remove_recursive(tc->dbgfs); |
765 | cancel_delayed_work_sync(&tc->link_work); | ||
766 | cancel_work_sync(&tc->link_cleanup); | ||
516 | kfree(tc); | 767 | kfree(tc); |
517 | err_tc: | 768 | err_tc: |
518 | return rc; | 769 | return rc; |
@@ -522,6 +773,11 @@ static void tool_remove(struct ntb_client *self, struct ntb_dev *ntb) | |||
522 | { | 773 | { |
523 | struct tool_ctx *tc = ntb->ctx; | 774 | struct tool_ctx *tc = ntb->ctx; |
524 | 775 | ||
776 | cancel_delayed_work_sync(&tc->link_work); | ||
777 | cancel_work_sync(&tc->link_cleanup); | ||
778 | |||
779 | tool_free_mws(tc); | ||
780 | |||
525 | ntb_clear_ctx(ntb); | 781 | ntb_clear_ctx(ntb); |
526 | ntb_link_disable(ntb); | 782 | ntb_link_disable(ntb); |
527 | 783 | ||