diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-04 14:13:49 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-04 14:13:49 -0500 |
commit | d3658c2266012f270da52e3e0365536e394bd3bd (patch) | |
tree | 3ea28fa631873cb7bbc9eec4e8405ee886cd779a | |
parent | 8ac4840a3c90cf45830b265c0a4d0876358e8f59 (diff) | |
parent | 3b28c987fb9547ca9aac73241d0e281cf646387c (diff) |
Merge tag 'ntb-4.16' of git://github.com/jonmason/ntb
Pull NTB updates from Jon Mason:
"Bug fixes galore, removal of the ntb atom driver, and updates to the
ntb tools and tests to support the multi-port interface"
* tag 'ntb-4.16' of git://github.com/jonmason/ntb: (37 commits)
NTB: ntb_perf: fix cast to restricted __le32
ntb_perf: Fix an error code in perf_copy_chunk()
ntb_hw_switchtec: Make function switchtec_ntb_remove() static
NTB: ntb_tool: fix memory leak on 'buf' on error exit path
NTB: ntb_perf: fix printing of resource_size_t
NTB: ntb_hw_idt: Set NTB_TOPO_SWITCH topology
NTB: ntb_test: Update ntb_perf tests
NTB: ntb_test: Update ntb_tool MW tests
NTB: ntb_test: Add ntb_tool Message tests
NTB: ntb_test: Update ntb_tool Scratchpad tests
NTB: ntb_test: Update ntb_tool DB tests
NTB: ntb_test: Update ntb_tool link tests
NTB: ntb_test: Add ntb_tool port tests
NTB: ntb_test: Safely use paths with whitespace
NTB: ntb_perf: Add full multi-port NTB API support
NTB: ntb_tool: Add full multi-port NTB API support
NTB: ntb_pp: Add full multi-port NTB API support
NTB: Fix UB/bug in ntb_mw_get_align()
NTB: Set dma mask and dma coherent mask to NTB devices
NTB: Rename NTB messaging API methods
...
-rw-r--r-- | MAINTAINERS | 2 | ||||
-rw-r--r-- | drivers/ntb/hw/amd/ntb_hw_amd.c | 4 | ||||
-rw-r--r-- | drivers/ntb/hw/idt/ntb_hw_idt.c | 37 | ||||
-rw-r--r-- | drivers/ntb/hw/intel/ntb_hw_intel.c | 313 | ||||
-rw-r--r-- | drivers/ntb/hw/intel/ntb_hw_intel.h | 58 | ||||
-rw-r--r-- | drivers/ntb/hw/mscc/ntb_hw_switchtec.c | 603 | ||||
-rw-r--r-- | drivers/ntb/ntb.c | 4 | ||||
-rw-r--r-- | drivers/ntb/ntb_transport.c | 3 | ||||
-rw-r--r-- | drivers/ntb/test/ntb_perf.c | 1824 | ||||
-rw-r--r-- | drivers/ntb/test/ntb_pingpong.c | 450 | ||||
-rw-r--r-- | drivers/ntb/test/ntb_tool.c | 1825 | ||||
-rw-r--r-- | include/linux/ntb.h | 51 | ||||
-rw-r--r-- | include/linux/switchtec.h | 23 | ||||
-rwxr-xr-x | tools/testing/selftests/ntb/ntb_test.sh | 307 |
14 files changed, 3550 insertions, 1954 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index e15f4e201471..b59a8cdfbe66 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -9801,7 +9801,7 @@ F: drivers/ntb/hw/amd/ | |||
9801 | NTB DRIVER CORE | 9801 | NTB DRIVER CORE |
9802 | M: Jon Mason <jdmason@kudzu.us> | 9802 | M: Jon Mason <jdmason@kudzu.us> |
9803 | M: Dave Jiang <dave.jiang@intel.com> | 9803 | M: Dave Jiang <dave.jiang@intel.com> |
9804 | M: Allen Hubbe <Allen.Hubbe@emc.com> | 9804 | M: Allen Hubbe <allenbh@gmail.com> |
9805 | L: linux-ntb@googlegroups.com | 9805 | L: linux-ntb@googlegroups.com |
9806 | S: Supported | 9806 | S: Supported |
9807 | W: https://github.com/jonmason/ntb/wiki | 9807 | W: https://github.com/jonmason/ntb/wiki |
diff --git a/drivers/ntb/hw/amd/ntb_hw_amd.c b/drivers/ntb/hw/amd/ntb_hw_amd.c index f0788aae05c9..3cfa46876239 100644 --- a/drivers/ntb/hw/amd/ntb_hw_amd.c +++ b/drivers/ntb/hw/amd/ntb_hw_amd.c | |||
@@ -1020,6 +1020,10 @@ static int amd_ntb_init_pci(struct amd_ntb_dev *ndev, | |||
1020 | goto err_dma_mask; | 1020 | goto err_dma_mask; |
1021 | dev_warn(&pdev->dev, "Cannot DMA consistent highmem\n"); | 1021 | dev_warn(&pdev->dev, "Cannot DMA consistent highmem\n"); |
1022 | } | 1022 | } |
1023 | rc = dma_coerce_mask_and_coherent(&ndev->ntb.dev, | ||
1024 | dma_get_mask(&pdev->dev)); | ||
1025 | if (rc) | ||
1026 | goto err_dma_mask; | ||
1023 | 1027 | ||
1024 | ndev->self_mmio = pci_iomap(pdev, 0, 0); | 1028 | ndev->self_mmio = pci_iomap(pdev, 0, 0); |
1025 | if (!ndev->self_mmio) { | 1029 | if (!ndev->self_mmio) { |
diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c index 0cd79f367f7c..8d98872d0983 100644 --- a/drivers/ntb/hw/idt/ntb_hw_idt.c +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c | |||
@@ -1744,20 +1744,19 @@ static int idt_ntb_msg_clear_mask(struct ntb_dev *ntb, u64 mask_bits) | |||
1744 | * idt_ntb_msg_read() - read message register with specified index | 1744 | * idt_ntb_msg_read() - read message register with specified index |
1745 | * (NTB API callback) | 1745 | * (NTB API callback) |
1746 | * @ntb: NTB device context. | 1746 | * @ntb: NTB device context. |
1747 | * @midx: Message register index | ||
1748 | * @pidx: OUT - Port index of peer device a message retrieved from | 1747 | * @pidx: OUT - Port index of peer device a message retrieved from |
1749 | * @msg: OUT - Data | 1748 | * @midx: Message register index |
1750 | * | 1749 | * |
1751 | * Read data from the specified message register and source register. | 1750 | * Read data from the specified message register and source register. |
1752 | * | 1751 | * |
1753 | * Return: zero on success, negative error if invalid argument passed. | 1752 | * Return: inbound message register value. |
1754 | */ | 1753 | */ |
1755 | static int idt_ntb_msg_read(struct ntb_dev *ntb, int midx, int *pidx, u32 *msg) | 1754 | static u32 idt_ntb_msg_read(struct ntb_dev *ntb, int *pidx, int midx) |
1756 | { | 1755 | { |
1757 | struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); | 1756 | struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); |
1758 | 1757 | ||
1759 | if (midx < 0 || IDT_MSG_CNT <= midx) | 1758 | if (midx < 0 || IDT_MSG_CNT <= midx) |
1760 | return -EINVAL; | 1759 | return ~(u32)0; |
1761 | 1760 | ||
1762 | /* Retrieve source port index of the message */ | 1761 | /* Retrieve source port index of the message */ |
1763 | if (pidx != NULL) { | 1762 | if (pidx != NULL) { |
@@ -1772,18 +1771,15 @@ static int idt_ntb_msg_read(struct ntb_dev *ntb, int midx, int *pidx, u32 *msg) | |||
1772 | } | 1771 | } |
1773 | 1772 | ||
1774 | /* Retrieve data of the corresponding message register */ | 1773 | /* Retrieve data of the corresponding message register */ |
1775 | if (msg != NULL) | 1774 | return idt_nt_read(ndev, ntdata_tbl.msgs[midx].in); |
1776 | *msg = idt_nt_read(ndev, ntdata_tbl.msgs[midx].in); | ||
1777 | |||
1778 | return 0; | ||
1779 | } | 1775 | } |
1780 | 1776 | ||
1781 | /* | 1777 | /* |
1782 | * idt_ntb_msg_write() - write data to the specified message register | 1778 | * idt_ntb_peer_msg_write() - write data to the specified message register |
1783 | * (NTB API callback) | 1779 | * (NTB API callback) |
1784 | * @ntb: NTB device context. | 1780 | * @ntb: NTB device context. |
1785 | * @midx: Message register index | ||
1786 | * @pidx: Port index of peer device a message being sent to | 1781 | * @pidx: Port index of peer device a message being sent to |
1782 | * @midx: Message register index | ||
1787 | * @msg: Data to send | 1783 | * @msg: Data to send |
1788 | * | 1784 | * |
1789 | * Just try to send data to a peer. Message status register should be | 1785 | * Just try to send data to a peer. Message status register should be |
@@ -1791,7 +1787,8 @@ static int idt_ntb_msg_read(struct ntb_dev *ntb, int midx, int *pidx, u32 *msg) | |||
1791 | * | 1787 | * |
1792 | * Return: zero on success, negative error if invalid argument passed. | 1788 | * Return: zero on success, negative error if invalid argument passed. |
1793 | */ | 1789 | */ |
1794 | static int idt_ntb_msg_write(struct ntb_dev *ntb, int midx, int pidx, u32 msg) | 1790 | static int idt_ntb_peer_msg_write(struct ntb_dev *ntb, int pidx, int midx, |
1791 | u32 msg) | ||
1795 | { | 1792 | { |
1796 | struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); | 1793 | struct idt_ntb_dev *ndev = to_ndev_ntb(ntb); |
1797 | unsigned long irqflags; | 1794 | unsigned long irqflags; |
@@ -2058,7 +2055,7 @@ static const struct ntb_dev_ops idt_ntb_ops = { | |||
2058 | .msg_set_mask = idt_ntb_msg_set_mask, | 2055 | .msg_set_mask = idt_ntb_msg_set_mask, |
2059 | .msg_clear_mask = idt_ntb_msg_clear_mask, | 2056 | .msg_clear_mask = idt_ntb_msg_clear_mask, |
2060 | .msg_read = idt_ntb_msg_read, | 2057 | .msg_read = idt_ntb_msg_read, |
2061 | .msg_write = idt_ntb_msg_write | 2058 | .peer_msg_write = idt_ntb_peer_msg_write |
2062 | }; | 2059 | }; |
2063 | 2060 | ||
2064 | /* | 2061 | /* |
@@ -2073,7 +2070,7 @@ static int idt_register_device(struct idt_ntb_dev *ndev) | |||
2073 | 2070 | ||
2074 | /* Initialize the rest of NTB device structure and register it */ | 2071 | /* Initialize the rest of NTB device structure and register it */ |
2075 | ndev->ntb.ops = &idt_ntb_ops; | 2072 | ndev->ntb.ops = &idt_ntb_ops; |
2076 | ndev->ntb.topo = NTB_TOPO_PRI; | 2073 | ndev->ntb.topo = NTB_TOPO_SWITCH; |
2077 | 2074 | ||
2078 | ret = ntb_register_device(&ndev->ntb); | 2075 | ret = ntb_register_device(&ndev->ntb); |
2079 | if (ret != 0) { | 2076 | if (ret != 0) { |
@@ -2269,7 +2266,7 @@ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, | |||
2269 | "Message data:\n"); | 2266 | "Message data:\n"); |
2270 | for (idx = 0; idx < IDT_MSG_CNT; idx++) { | 2267 | for (idx = 0; idx < IDT_MSG_CNT; idx++) { |
2271 | int src; | 2268 | int src; |
2272 | (void)idt_ntb_msg_read(&ndev->ntb, idx, &src, &data); | 2269 | data = idt_ntb_msg_read(&ndev->ntb, &src, idx); |
2273 | off += scnprintf(strbuf + off, size - off, | 2270 | off += scnprintf(strbuf + off, size - off, |
2274 | "\t%hhu. 0x%08x from peer %hhu (Port %hhu)\n", | 2271 | "\t%hhu. 0x%08x from peer %hhu (Port %hhu)\n", |
2275 | idx, data, src, ndev->peers[src].port); | 2272 | idx, data, src, ndev->peers[src].port); |
@@ -2429,7 +2426,7 @@ static int idt_init_pci(struct idt_ntb_dev *ndev) | |||
2429 | struct pci_dev *pdev = ndev->ntb.pdev; | 2426 | struct pci_dev *pdev = ndev->ntb.pdev; |
2430 | int ret; | 2427 | int ret; |
2431 | 2428 | ||
2432 | /* Initialize the bit mask of DMA */ | 2429 | /* Initialize the bit mask of PCI/NTB DMA */ |
2433 | ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); | 2430 | ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); |
2434 | if (ret != 0) { | 2431 | if (ret != 0) { |
2435 | ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); | 2432 | ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); |
@@ -2450,6 +2447,12 @@ static int idt_init_pci(struct idt_ntb_dev *ndev) | |||
2450 | dev_warn(&pdev->dev, | 2447 | dev_warn(&pdev->dev, |
2451 | "Cannot set consistent DMA highmem bit mask\n"); | 2448 | "Cannot set consistent DMA highmem bit mask\n"); |
2452 | } | 2449 | } |
2450 | ret = dma_coerce_mask_and_coherent(&ndev->ntb.dev, | ||
2451 | dma_get_mask(&pdev->dev)); | ||
2452 | if (ret != 0) { | ||
2453 | dev_err(&pdev->dev, "Failed to set NTB device DMA bit mask\n"); | ||
2454 | return ret; | ||
2455 | } | ||
2453 | 2456 | ||
2454 | /* | 2457 | /* |
2455 | * Enable the device advanced error reporting. It's not critical to | 2458 | * Enable the device advanced error reporting. It's not critical to |
diff --git a/drivers/ntb/hw/intel/ntb_hw_intel.c b/drivers/ntb/hw/intel/ntb_hw_intel.c index 4de074a86073..156b45cd4a19 100644 --- a/drivers/ntb/hw/intel/ntb_hw_intel.c +++ b/drivers/ntb/hw/intel/ntb_hw_intel.c | |||
@@ -74,12 +74,6 @@ MODULE_AUTHOR("Intel Corporation"); | |||
74 | #define bar0_off(base, bar) ((base) + ((bar) << 2)) | 74 | #define bar0_off(base, bar) ((base) + ((bar) << 2)) |
75 | #define bar2_off(base, bar) bar0_off(base, (bar) - 2) | 75 | #define bar2_off(base, bar) bar0_off(base, (bar) - 2) |
76 | 76 | ||
77 | static const struct intel_ntb_reg atom_reg; | ||
78 | static const struct intel_ntb_alt_reg atom_pri_reg; | ||
79 | static const struct intel_ntb_alt_reg atom_sec_reg; | ||
80 | static const struct intel_ntb_alt_reg atom_b2b_reg; | ||
81 | static const struct intel_ntb_xlat_reg atom_pri_xlat; | ||
82 | static const struct intel_ntb_xlat_reg atom_sec_xlat; | ||
83 | static const struct intel_ntb_reg xeon_reg; | 77 | static const struct intel_ntb_reg xeon_reg; |
84 | static const struct intel_ntb_alt_reg xeon_pri_reg; | 78 | static const struct intel_ntb_alt_reg xeon_pri_reg; |
85 | static const struct intel_ntb_alt_reg xeon_sec_reg; | 79 | static const struct intel_ntb_alt_reg xeon_sec_reg; |
@@ -184,15 +178,6 @@ static inline void _iowrite64(u64 val, void __iomem *mmio) | |||
184 | #endif | 178 | #endif |
185 | #endif | 179 | #endif |
186 | 180 | ||
187 | static inline int pdev_is_atom(struct pci_dev *pdev) | ||
188 | { | ||
189 | switch (pdev->device) { | ||
190 | case PCI_DEVICE_ID_INTEL_NTB_B2B_BWD: | ||
191 | return 1; | ||
192 | } | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static inline int pdev_is_xeon(struct pci_dev *pdev) | 181 | static inline int pdev_is_xeon(struct pci_dev *pdev) |
197 | { | 182 | { |
198 | switch (pdev->device) { | 183 | switch (pdev->device) { |
@@ -1006,8 +991,7 @@ static ssize_t ndev_debugfs_read(struct file *filp, char __user *ubuf, | |||
1006 | { | 991 | { |
1007 | struct intel_ntb_dev *ndev = filp->private_data; | 992 | struct intel_ntb_dev *ndev = filp->private_data; |
1008 | 993 | ||
1009 | if (pdev_is_xeon(ndev->ntb.pdev) || | 994 | if (pdev_is_xeon(ndev->ntb.pdev)) |
1010 | pdev_is_atom(ndev->ntb.pdev)) | ||
1011 | return ndev_ntb_debugfs_read(filp, ubuf, count, offp); | 995 | return ndev_ntb_debugfs_read(filp, ubuf, count, offp); |
1012 | else if (pdev_is_skx_xeon(ndev->ntb.pdev)) | 996 | else if (pdev_is_skx_xeon(ndev->ntb.pdev)) |
1013 | return ndev_ntb3_debugfs_read(filp, ubuf, count, offp); | 997 | return ndev_ntb3_debugfs_read(filp, ubuf, count, offp); |
@@ -1439,242 +1423,6 @@ static int intel_ntb_peer_spad_write(struct ntb_dev *ntb, int pidx, | |||
1439 | ndev->peer_reg->spad); | 1423 | ndev->peer_reg->spad); |
1440 | } | 1424 | } |
1441 | 1425 | ||
1442 | /* ATOM */ | ||
1443 | |||
1444 | static u64 atom_db_ioread(void __iomem *mmio) | ||
1445 | { | ||
1446 | return ioread64(mmio); | ||
1447 | } | ||
1448 | |||
1449 | static void atom_db_iowrite(u64 bits, void __iomem *mmio) | ||
1450 | { | ||
1451 | iowrite64(bits, mmio); | ||
1452 | } | ||
1453 | |||
1454 | static int atom_poll_link(struct intel_ntb_dev *ndev) | ||
1455 | { | ||
1456 | u32 ntb_ctl; | ||
1457 | |||
1458 | ntb_ctl = ioread32(ndev->self_mmio + ATOM_NTBCNTL_OFFSET); | ||
1459 | |||
1460 | if (ntb_ctl == ndev->ntb_ctl) | ||
1461 | return 0; | ||
1462 | |||
1463 | ndev->ntb_ctl = ntb_ctl; | ||
1464 | |||
1465 | ndev->lnk_sta = ioread32(ndev->self_mmio + ATOM_LINK_STATUS_OFFSET); | ||
1466 | |||
1467 | return 1; | ||
1468 | } | ||
1469 | |||
1470 | static int atom_link_is_up(struct intel_ntb_dev *ndev) | ||
1471 | { | ||
1472 | return ATOM_NTB_CTL_ACTIVE(ndev->ntb_ctl); | ||
1473 | } | ||
1474 | |||
1475 | static int atom_link_is_err(struct intel_ntb_dev *ndev) | ||
1476 | { | ||
1477 | if (ioread32(ndev->self_mmio + ATOM_LTSSMSTATEJMP_OFFSET) | ||
1478 | & ATOM_LTSSMSTATEJMP_FORCEDETECT) | ||
1479 | return 1; | ||
1480 | |||
1481 | if (ioread32(ndev->self_mmio + ATOM_IBSTERRRCRVSTS0_OFFSET) | ||
1482 | & ATOM_IBIST_ERR_OFLOW) | ||
1483 | return 1; | ||
1484 | |||
1485 | return 0; | ||
1486 | } | ||
1487 | |||
1488 | static inline enum ntb_topo atom_ppd_topo(struct intel_ntb_dev *ndev, u32 ppd) | ||
1489 | { | ||
1490 | struct device *dev = &ndev->ntb.pdev->dev; | ||
1491 | |||
1492 | switch (ppd & ATOM_PPD_TOPO_MASK) { | ||
1493 | case ATOM_PPD_TOPO_B2B_USD: | ||
1494 | dev_dbg(dev, "PPD %d B2B USD\n", ppd); | ||
1495 | return NTB_TOPO_B2B_USD; | ||
1496 | |||
1497 | case ATOM_PPD_TOPO_B2B_DSD: | ||
1498 | dev_dbg(dev, "PPD %d B2B DSD\n", ppd); | ||
1499 | return NTB_TOPO_B2B_DSD; | ||
1500 | |||
1501 | case ATOM_PPD_TOPO_PRI_USD: | ||
1502 | case ATOM_PPD_TOPO_PRI_DSD: /* accept bogus PRI_DSD */ | ||
1503 | case ATOM_PPD_TOPO_SEC_USD: | ||
1504 | case ATOM_PPD_TOPO_SEC_DSD: /* accept bogus SEC_DSD */ | ||
1505 | dev_dbg(dev, "PPD %d non B2B disabled\n", ppd); | ||
1506 | return NTB_TOPO_NONE; | ||
1507 | } | ||
1508 | |||
1509 | dev_dbg(dev, "PPD %d invalid\n", ppd); | ||
1510 | return NTB_TOPO_NONE; | ||
1511 | } | ||
1512 | |||
1513 | static void atom_link_hb(struct work_struct *work) | ||
1514 | { | ||
1515 | struct intel_ntb_dev *ndev = hb_ndev(work); | ||
1516 | struct device *dev = &ndev->ntb.pdev->dev; | ||
1517 | unsigned long poll_ts; | ||
1518 | void __iomem *mmio; | ||
1519 | u32 status32; | ||
1520 | |||
1521 | poll_ts = ndev->last_ts + ATOM_LINK_HB_TIMEOUT; | ||
1522 | |||
1523 | /* Delay polling the link status if an interrupt was received, | ||
1524 | * unless the cached link status says the link is down. | ||
1525 | */ | ||
1526 | if (time_after(poll_ts, jiffies) && atom_link_is_up(ndev)) { | ||
1527 | schedule_delayed_work(&ndev->hb_timer, poll_ts - jiffies); | ||
1528 | return; | ||
1529 | } | ||
1530 | |||
1531 | if (atom_poll_link(ndev)) | ||
1532 | ntb_link_event(&ndev->ntb); | ||
1533 | |||
1534 | if (atom_link_is_up(ndev) || !atom_link_is_err(ndev)) { | ||
1535 | schedule_delayed_work(&ndev->hb_timer, ATOM_LINK_HB_TIMEOUT); | ||
1536 | return; | ||
1537 | } | ||
1538 | |||
1539 | /* Link is down with error: recover the link! */ | ||
1540 | |||
1541 | mmio = ndev->self_mmio; | ||
1542 | |||
1543 | /* Driver resets the NTB ModPhy lanes - magic! */ | ||
1544 | iowrite8(0xe0, mmio + ATOM_MODPHY_PCSREG6); | ||
1545 | iowrite8(0x40, mmio + ATOM_MODPHY_PCSREG4); | ||
1546 | iowrite8(0x60, mmio + ATOM_MODPHY_PCSREG4); | ||
1547 | iowrite8(0x60, mmio + ATOM_MODPHY_PCSREG6); | ||
1548 | |||
1549 | /* Driver waits 100ms to allow the NTB ModPhy to settle */ | ||
1550 | msleep(100); | ||
1551 | |||
1552 | /* Clear AER Errors, write to clear */ | ||
1553 | status32 = ioread32(mmio + ATOM_ERRCORSTS_OFFSET); | ||
1554 | dev_dbg(dev, "ERRCORSTS = %x\n", status32); | ||
1555 | status32 &= PCI_ERR_COR_REP_ROLL; | ||
1556 | iowrite32(status32, mmio + ATOM_ERRCORSTS_OFFSET); | ||
1557 | |||
1558 | /* Clear unexpected electrical idle event in LTSSM, write to clear */ | ||
1559 | status32 = ioread32(mmio + ATOM_LTSSMERRSTS0_OFFSET); | ||
1560 | dev_dbg(dev, "LTSSMERRSTS0 = %x\n", status32); | ||
1561 | status32 |= ATOM_LTSSMERRSTS0_UNEXPECTEDEI; | ||
1562 | iowrite32(status32, mmio + ATOM_LTSSMERRSTS0_OFFSET); | ||
1563 | |||
1564 | /* Clear DeSkew Buffer error, write to clear */ | ||
1565 | status32 = ioread32(mmio + ATOM_DESKEWSTS_OFFSET); | ||
1566 | dev_dbg(dev, "DESKEWSTS = %x\n", status32); | ||
1567 | status32 |= ATOM_DESKEWSTS_DBERR; | ||
1568 | iowrite32(status32, mmio + ATOM_DESKEWSTS_OFFSET); | ||
1569 | |||
1570 | status32 = ioread32(mmio + ATOM_IBSTERRRCRVSTS0_OFFSET); | ||
1571 | dev_dbg(dev, "IBSTERRRCRVSTS0 = %x\n", status32); | ||
1572 | status32 &= ATOM_IBIST_ERR_OFLOW; | ||
1573 | iowrite32(status32, mmio + ATOM_IBSTERRRCRVSTS0_OFFSET); | ||
1574 | |||
1575 | /* Releases the NTB state machine to allow the link to retrain */ | ||
1576 | status32 = ioread32(mmio + ATOM_LTSSMSTATEJMP_OFFSET); | ||
1577 | dev_dbg(dev, "LTSSMSTATEJMP = %x\n", status32); | ||
1578 | status32 &= ~ATOM_LTSSMSTATEJMP_FORCEDETECT; | ||
1579 | iowrite32(status32, mmio + ATOM_LTSSMSTATEJMP_OFFSET); | ||
1580 | |||
1581 | /* There is a potential race between the 2 NTB devices recovering at the | ||
1582 | * same time. If the times are the same, the link will not recover and | ||
1583 | * the driver will be stuck in this loop forever. Add a random interval | ||
1584 | * to the recovery time to prevent this race. | ||
1585 | */ | ||
1586 | schedule_delayed_work(&ndev->hb_timer, ATOM_LINK_RECOVERY_TIME | ||
1587 | + prandom_u32() % ATOM_LINK_RECOVERY_TIME); | ||
1588 | } | ||
1589 | |||
1590 | static int atom_init_isr(struct intel_ntb_dev *ndev) | ||
1591 | { | ||
1592 | int rc; | ||
1593 | |||
1594 | rc = ndev_init_isr(ndev, 1, ATOM_DB_MSIX_VECTOR_COUNT, | ||
1595 | ATOM_DB_MSIX_VECTOR_SHIFT, ATOM_DB_TOTAL_SHIFT); | ||
1596 | if (rc) | ||
1597 | return rc; | ||
1598 | |||
1599 | /* ATOM doesn't have link status interrupt, poll on that platform */ | ||
1600 | ndev->last_ts = jiffies; | ||
1601 | INIT_DELAYED_WORK(&ndev->hb_timer, atom_link_hb); | ||
1602 | schedule_delayed_work(&ndev->hb_timer, ATOM_LINK_HB_TIMEOUT); | ||
1603 | |||
1604 | return 0; | ||
1605 | } | ||
1606 | |||
1607 | static void atom_deinit_isr(struct intel_ntb_dev *ndev) | ||
1608 | { | ||
1609 | cancel_delayed_work_sync(&ndev->hb_timer); | ||
1610 | ndev_deinit_isr(ndev); | ||
1611 | } | ||
1612 | |||
1613 | static int atom_init_ntb(struct intel_ntb_dev *ndev) | ||
1614 | { | ||
1615 | ndev->mw_count = ATOM_MW_COUNT; | ||
1616 | ndev->spad_count = ATOM_SPAD_COUNT; | ||
1617 | ndev->db_count = ATOM_DB_COUNT; | ||
1618 | |||
1619 | switch (ndev->ntb.topo) { | ||
1620 | case NTB_TOPO_B2B_USD: | ||
1621 | case NTB_TOPO_B2B_DSD: | ||
1622 | ndev->self_reg = &atom_pri_reg; | ||
1623 | ndev->peer_reg = &atom_b2b_reg; | ||
1624 | ndev->xlat_reg = &atom_sec_xlat; | ||
1625 | |||
1626 | /* Enable Bus Master and Memory Space on the secondary side */ | ||
1627 | iowrite16(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER, | ||
1628 | ndev->self_mmio + ATOM_SPCICMD_OFFSET); | ||
1629 | |||
1630 | break; | ||
1631 | |||
1632 | default: | ||
1633 | return -EINVAL; | ||
1634 | } | ||
1635 | |||
1636 | ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; | ||
1637 | |||
1638 | return 0; | ||
1639 | } | ||
1640 | |||
1641 | static int atom_init_dev(struct intel_ntb_dev *ndev) | ||
1642 | { | ||
1643 | u32 ppd; | ||
1644 | int rc; | ||
1645 | |||
1646 | rc = pci_read_config_dword(ndev->ntb.pdev, ATOM_PPD_OFFSET, &ppd); | ||
1647 | if (rc) | ||
1648 | return -EIO; | ||
1649 | |||
1650 | ndev->ntb.topo = atom_ppd_topo(ndev, ppd); | ||
1651 | if (ndev->ntb.topo == NTB_TOPO_NONE) | ||
1652 | return -EINVAL; | ||
1653 | |||
1654 | rc = atom_init_ntb(ndev); | ||
1655 | if (rc) | ||
1656 | return rc; | ||
1657 | |||
1658 | rc = atom_init_isr(ndev); | ||
1659 | if (rc) | ||
1660 | return rc; | ||
1661 | |||
1662 | if (ndev->ntb.topo != NTB_TOPO_SEC) { | ||
1663 | /* Initiate PCI-E link training */ | ||
1664 | rc = pci_write_config_dword(ndev->ntb.pdev, ATOM_PPD_OFFSET, | ||
1665 | ppd | ATOM_PPD_INIT_LINK); | ||
1666 | if (rc) | ||
1667 | return rc; | ||
1668 | } | ||
1669 | |||
1670 | return 0; | ||
1671 | } | ||
1672 | |||
1673 | static void atom_deinit_dev(struct intel_ntb_dev *ndev) | ||
1674 | { | ||
1675 | atom_deinit_isr(ndev); | ||
1676 | } | ||
1677 | |||
1678 | /* Skylake Xeon NTB */ | 1426 | /* Skylake Xeon NTB */ |
1679 | 1427 | ||
1680 | static int skx_poll_link(struct intel_ntb_dev *ndev) | 1428 | static int skx_poll_link(struct intel_ntb_dev *ndev) |
@@ -2586,6 +2334,10 @@ static int intel_ntb_init_pci(struct intel_ntb_dev *ndev, struct pci_dev *pdev) | |||
2586 | goto err_dma_mask; | 2334 | goto err_dma_mask; |
2587 | dev_warn(&pdev->dev, "Cannot DMA consistent highmem\n"); | 2335 | dev_warn(&pdev->dev, "Cannot DMA consistent highmem\n"); |
2588 | } | 2336 | } |
2337 | rc = dma_coerce_mask_and_coherent(&ndev->ntb.dev, | ||
2338 | dma_get_mask(&pdev->dev)); | ||
2339 | if (rc) | ||
2340 | goto err_dma_mask; | ||
2589 | 2341 | ||
2590 | ndev->self_mmio = pci_iomap(pdev, 0, 0); | 2342 | ndev->self_mmio = pci_iomap(pdev, 0, 0); |
2591 | if (!ndev->self_mmio) { | 2343 | if (!ndev->self_mmio) { |
@@ -2658,24 +2410,7 @@ static int intel_ntb_pci_probe(struct pci_dev *pdev, | |||
2658 | 2410 | ||
2659 | node = dev_to_node(&pdev->dev); | 2411 | node = dev_to_node(&pdev->dev); |
2660 | 2412 | ||
2661 | if (pdev_is_atom(pdev)) { | 2413 | if (pdev_is_xeon(pdev)) { |
2662 | ndev = kzalloc_node(sizeof(*ndev), GFP_KERNEL, node); | ||
2663 | if (!ndev) { | ||
2664 | rc = -ENOMEM; | ||
2665 | goto err_ndev; | ||
2666 | } | ||
2667 | |||
2668 | ndev_init_struct(ndev, pdev); | ||
2669 | |||
2670 | rc = intel_ntb_init_pci(ndev, pdev); | ||
2671 | if (rc) | ||
2672 | goto err_init_pci; | ||
2673 | |||
2674 | rc = atom_init_dev(ndev); | ||
2675 | if (rc) | ||
2676 | goto err_init_dev; | ||
2677 | |||
2678 | } else if (pdev_is_xeon(pdev)) { | ||
2679 | ndev = kzalloc_node(sizeof(*ndev), GFP_KERNEL, node); | 2414 | ndev = kzalloc_node(sizeof(*ndev), GFP_KERNEL, node); |
2680 | if (!ndev) { | 2415 | if (!ndev) { |
2681 | rc = -ENOMEM; | 2416 | rc = -ENOMEM; |
@@ -2731,9 +2466,7 @@ static int intel_ntb_pci_probe(struct pci_dev *pdev, | |||
2731 | 2466 | ||
2732 | err_register: | 2467 | err_register: |
2733 | ndev_deinit_debugfs(ndev); | 2468 | ndev_deinit_debugfs(ndev); |
2734 | if (pdev_is_atom(pdev)) | 2469 | if (pdev_is_xeon(pdev) || pdev_is_skx_xeon(pdev)) |
2735 | atom_deinit_dev(ndev); | ||
2736 | else if (pdev_is_xeon(pdev) || pdev_is_skx_xeon(pdev)) | ||
2737 | xeon_deinit_dev(ndev); | 2470 | xeon_deinit_dev(ndev); |
2738 | err_init_dev: | 2471 | err_init_dev: |
2739 | intel_ntb_deinit_pci(ndev); | 2472 | intel_ntb_deinit_pci(ndev); |
@@ -2749,41 +2482,12 @@ static void intel_ntb_pci_remove(struct pci_dev *pdev) | |||
2749 | 2482 | ||
2750 | ntb_unregister_device(&ndev->ntb); | 2483 | ntb_unregister_device(&ndev->ntb); |
2751 | ndev_deinit_debugfs(ndev); | 2484 | ndev_deinit_debugfs(ndev); |
2752 | if (pdev_is_atom(pdev)) | 2485 | if (pdev_is_xeon(pdev) || pdev_is_skx_xeon(pdev)) |
2753 | atom_deinit_dev(ndev); | ||
2754 | else if (pdev_is_xeon(pdev) || pdev_is_skx_xeon(pdev)) | ||
2755 | xeon_deinit_dev(ndev); | 2486 | xeon_deinit_dev(ndev); |
2756 | intel_ntb_deinit_pci(ndev); | 2487 | intel_ntb_deinit_pci(ndev); |
2757 | kfree(ndev); | 2488 | kfree(ndev); |
2758 | } | 2489 | } |
2759 | 2490 | ||
2760 | static const struct intel_ntb_reg atom_reg = { | ||
2761 | .poll_link = atom_poll_link, | ||
2762 | .link_is_up = atom_link_is_up, | ||
2763 | .db_ioread = atom_db_ioread, | ||
2764 | .db_iowrite = atom_db_iowrite, | ||
2765 | .db_size = sizeof(u64), | ||
2766 | .ntb_ctl = ATOM_NTBCNTL_OFFSET, | ||
2767 | .mw_bar = {2, 4}, | ||
2768 | }; | ||
2769 | |||
2770 | static const struct intel_ntb_alt_reg atom_pri_reg = { | ||
2771 | .db_bell = ATOM_PDOORBELL_OFFSET, | ||
2772 | .db_mask = ATOM_PDBMSK_OFFSET, | ||
2773 | .spad = ATOM_SPAD_OFFSET, | ||
2774 | }; | ||
2775 | |||
2776 | static const struct intel_ntb_alt_reg atom_b2b_reg = { | ||
2777 | .db_bell = ATOM_B2B_DOORBELL_OFFSET, | ||
2778 | .spad = ATOM_B2B_SPAD_OFFSET, | ||
2779 | }; | ||
2780 | |||
2781 | static const struct intel_ntb_xlat_reg atom_sec_xlat = { | ||
2782 | /* FIXME : .bar0_base = ATOM_SBAR0BASE_OFFSET, */ | ||
2783 | /* FIXME : .bar2_limit = ATOM_SBAR2LMT_OFFSET, */ | ||
2784 | .bar2_xlat = ATOM_SBAR2XLAT_OFFSET, | ||
2785 | }; | ||
2786 | |||
2787 | static const struct intel_ntb_reg xeon_reg = { | 2491 | static const struct intel_ntb_reg xeon_reg = { |
2788 | .poll_link = xeon_poll_link, | 2492 | .poll_link = xeon_poll_link, |
2789 | .link_is_up = xeon_link_is_up, | 2493 | .link_is_up = xeon_link_is_up, |
@@ -2940,7 +2644,6 @@ static const struct file_operations intel_ntb_debugfs_info = { | |||
2940 | }; | 2644 | }; |
2941 | 2645 | ||
2942 | static const struct pci_device_id intel_ntb_pci_tbl[] = { | 2646 | static const struct pci_device_id intel_ntb_pci_tbl[] = { |
2943 | {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_BWD)}, | ||
2944 | {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_JSF)}, | 2647 | {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_JSF)}, |
2945 | {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_SNB)}, | 2648 | {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_SNB)}, |
2946 | {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_IVT)}, | 2649 | {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_IVT)}, |
diff --git a/drivers/ntb/hw/intel/ntb_hw_intel.h b/drivers/ntb/hw/intel/ntb_hw_intel.h index 2d6c38afb128..4415aa7ea775 100644 --- a/drivers/ntb/hw/intel/ntb_hw_intel.h +++ b/drivers/ntb/hw/intel/ntb_hw_intel.h | |||
@@ -66,7 +66,6 @@ | |||
66 | #define PCI_DEVICE_ID_INTEL_NTB_B2B_HSX 0x2F0D | 66 | #define PCI_DEVICE_ID_INTEL_NTB_B2B_HSX 0x2F0D |
67 | #define PCI_DEVICE_ID_INTEL_NTB_PS_HSX 0x2F0E | 67 | #define PCI_DEVICE_ID_INTEL_NTB_PS_HSX 0x2F0E |
68 | #define PCI_DEVICE_ID_INTEL_NTB_SS_HSX 0x2F0F | 68 | #define PCI_DEVICE_ID_INTEL_NTB_SS_HSX 0x2F0F |
69 | #define PCI_DEVICE_ID_INTEL_NTB_B2B_BWD 0x0C4E | ||
70 | #define PCI_DEVICE_ID_INTEL_NTB_B2B_BDX 0x6F0D | 69 | #define PCI_DEVICE_ID_INTEL_NTB_B2B_BDX 0x6F0D |
71 | #define PCI_DEVICE_ID_INTEL_NTB_PS_BDX 0x6F0E | 70 | #define PCI_DEVICE_ID_INTEL_NTB_PS_BDX 0x6F0E |
72 | #define PCI_DEVICE_ID_INTEL_NTB_SS_BDX 0x6F0F | 71 | #define PCI_DEVICE_ID_INTEL_NTB_SS_BDX 0x6F0F |
@@ -196,63 +195,6 @@ | |||
196 | #define SKX_DB_TOTAL_SHIFT 33 | 195 | #define SKX_DB_TOTAL_SHIFT 33 |
197 | #define SKX_SPAD_COUNT 16 | 196 | #define SKX_SPAD_COUNT 16 |
198 | 197 | ||
199 | /* Intel Atom hardware */ | ||
200 | |||
201 | #define ATOM_SBAR2XLAT_OFFSET 0x0008 | ||
202 | #define ATOM_PDOORBELL_OFFSET 0x0020 | ||
203 | #define ATOM_PDBMSK_OFFSET 0x0028 | ||
204 | #define ATOM_NTBCNTL_OFFSET 0x0060 | ||
205 | #define ATOM_SPAD_OFFSET 0x0080 | ||
206 | #define ATOM_PPD_OFFSET 0x00d4 | ||
207 | #define ATOM_PBAR2XLAT_OFFSET 0x8008 | ||
208 | #define ATOM_B2B_DOORBELL_OFFSET 0x8020 | ||
209 | #define ATOM_B2B_SPAD_OFFSET 0x8080 | ||
210 | #define ATOM_SPCICMD_OFFSET 0xb004 | ||
211 | #define ATOM_LINK_STATUS_OFFSET 0xb052 | ||
212 | #define ATOM_ERRCORSTS_OFFSET 0xb110 | ||
213 | #define ATOM_IP_BASE 0xc000 | ||
214 | #define ATOM_DESKEWSTS_OFFSET (ATOM_IP_BASE + 0x3024) | ||
215 | #define ATOM_LTSSMERRSTS0_OFFSET (ATOM_IP_BASE + 0x3180) | ||
216 | #define ATOM_LTSSMSTATEJMP_OFFSET (ATOM_IP_BASE + 0x3040) | ||
217 | #define ATOM_IBSTERRRCRVSTS0_OFFSET (ATOM_IP_BASE + 0x3324) | ||
218 | #define ATOM_MODPHY_PCSREG4 0x1c004 | ||
219 | #define ATOM_MODPHY_PCSREG6 0x1c006 | ||
220 | |||
221 | #define ATOM_PPD_INIT_LINK 0x0008 | ||
222 | #define ATOM_PPD_CONN_MASK 0x0300 | ||
223 | #define ATOM_PPD_CONN_TRANSPARENT 0x0000 | ||
224 | #define ATOM_PPD_CONN_B2B 0x0100 | ||
225 | #define ATOM_PPD_CONN_RP 0x0200 | ||
226 | #define ATOM_PPD_DEV_MASK 0x1000 | ||
227 | #define ATOM_PPD_DEV_USD 0x0000 | ||
228 | #define ATOM_PPD_DEV_DSD 0x1000 | ||
229 | #define ATOM_PPD_TOPO_MASK (ATOM_PPD_CONN_MASK | ATOM_PPD_DEV_MASK) | ||
230 | #define ATOM_PPD_TOPO_PRI_USD (ATOM_PPD_CONN_TRANSPARENT | ATOM_PPD_DEV_USD) | ||
231 | #define ATOM_PPD_TOPO_PRI_DSD (ATOM_PPD_CONN_TRANSPARENT | ATOM_PPD_DEV_DSD) | ||
232 | #define ATOM_PPD_TOPO_SEC_USD (ATOM_PPD_CONN_RP | ATOM_PPD_DEV_USD) | ||
233 | #define ATOM_PPD_TOPO_SEC_DSD (ATOM_PPD_CONN_RP | ATOM_PPD_DEV_DSD) | ||
234 | #define ATOM_PPD_TOPO_B2B_USD (ATOM_PPD_CONN_B2B | ATOM_PPD_DEV_USD) | ||
235 | #define ATOM_PPD_TOPO_B2B_DSD (ATOM_PPD_CONN_B2B | ATOM_PPD_DEV_DSD) | ||
236 | |||
237 | #define ATOM_MW_COUNT 2 | ||
238 | #define ATOM_DB_COUNT 34 | ||
239 | #define ATOM_DB_VALID_MASK (BIT_ULL(ATOM_DB_COUNT) - 1) | ||
240 | #define ATOM_DB_MSIX_VECTOR_COUNT 34 | ||
241 | #define ATOM_DB_MSIX_VECTOR_SHIFT 1 | ||
242 | #define ATOM_DB_TOTAL_SHIFT 34 | ||
243 | #define ATOM_SPAD_COUNT 16 | ||
244 | |||
245 | #define ATOM_NTB_CTL_DOWN_BIT BIT(16) | ||
246 | #define ATOM_NTB_CTL_ACTIVE(x) !(x & ATOM_NTB_CTL_DOWN_BIT) | ||
247 | |||
248 | #define ATOM_DESKEWSTS_DBERR BIT(15) | ||
249 | #define ATOM_LTSSMERRSTS0_UNEXPECTEDEI BIT(20) | ||
250 | #define ATOM_LTSSMSTATEJMP_FORCEDETECT BIT(2) | ||
251 | #define ATOM_IBIST_ERR_OFLOW 0x7FFF7FFF | ||
252 | |||
253 | #define ATOM_LINK_HB_TIMEOUT msecs_to_jiffies(1000) | ||
254 | #define ATOM_LINK_RECOVERY_TIME msecs_to_jiffies(500) | ||
255 | |||
256 | /* Ntb control and link status */ | 198 | /* Ntb control and link status */ |
257 | 199 | ||
258 | #define NTB_CTL_CFG_LOCK BIT(0) | 200 | #define NTB_CTL_CFG_LOCK BIT(0) |
diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c index afe8ed6f3b23..f624ae27eabe 100644 --- a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c +++ b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c | |||
@@ -94,6 +94,9 @@ struct switchtec_ntb { | |||
94 | struct ntb_ctrl_regs __iomem *mmio_self_ctrl; | 94 | struct ntb_ctrl_regs __iomem *mmio_self_ctrl; |
95 | struct ntb_ctrl_regs __iomem *mmio_peer_ctrl; | 95 | struct ntb_ctrl_regs __iomem *mmio_peer_ctrl; |
96 | struct ntb_dbmsg_regs __iomem *mmio_self_dbmsg; | 96 | struct ntb_dbmsg_regs __iomem *mmio_self_dbmsg; |
97 | struct ntb_dbmsg_regs __iomem *mmio_peer_dbmsg; | ||
98 | |||
99 | void __iomem *mmio_xlink_win; | ||
97 | 100 | ||
98 | struct shared_mw *self_shared; | 101 | struct shared_mw *self_shared; |
99 | struct shared_mw __iomem *peer_shared; | 102 | struct shared_mw __iomem *peer_shared; |
@@ -109,6 +112,7 @@ struct switchtec_ntb { | |||
109 | 112 | ||
110 | int nr_direct_mw; | 113 | int nr_direct_mw; |
111 | int nr_lut_mw; | 114 | int nr_lut_mw; |
115 | int nr_rsvd_luts; | ||
112 | int direct_mw_to_bar[MAX_DIRECT_MW]; | 116 | int direct_mw_to_bar[MAX_DIRECT_MW]; |
113 | 117 | ||
114 | int peer_nr_direct_mw; | 118 | int peer_nr_direct_mw; |
@@ -118,6 +122,7 @@ struct switchtec_ntb { | |||
118 | bool link_is_up; | 122 | bool link_is_up; |
119 | enum ntb_speed link_speed; | 123 | enum ntb_speed link_speed; |
120 | enum ntb_width link_width; | 124 | enum ntb_width link_width; |
125 | struct work_struct link_reinit_work; | ||
121 | }; | 126 | }; |
122 | 127 | ||
123 | static struct switchtec_ntb *ntb_sndev(struct ntb_dev *ntb) | 128 | static struct switchtec_ntb *ntb_sndev(struct ntb_dev *ntb) |
@@ -172,7 +177,7 @@ static int switchtec_ntb_part_op(struct switchtec_ntb *sndev, | |||
172 | 177 | ||
173 | if (ps == status) { | 178 | if (ps == status) { |
174 | dev_err(&sndev->stdev->dev, | 179 | dev_err(&sndev->stdev->dev, |
175 | "Timed out while peforming %s (%d). (%08x)", | 180 | "Timed out while performing %s (%d). (%08x)\n", |
176 | op_text[op], op, | 181 | op_text[op], op, |
177 | ioread32(&ctl->partition_status)); | 182 | ioread32(&ctl->partition_status)); |
178 | 183 | ||
@@ -185,10 +190,10 @@ static int switchtec_ntb_part_op(struct switchtec_ntb *sndev, | |||
185 | static int switchtec_ntb_send_msg(struct switchtec_ntb *sndev, int idx, | 190 | static int switchtec_ntb_send_msg(struct switchtec_ntb *sndev, int idx, |
186 | u32 val) | 191 | u32 val) |
187 | { | 192 | { |
188 | if (idx < 0 || idx >= ARRAY_SIZE(sndev->mmio_self_dbmsg->omsg)) | 193 | if (idx < 0 || idx >= ARRAY_SIZE(sndev->mmio_peer_dbmsg->omsg)) |
189 | return -EINVAL; | 194 | return -EINVAL; |
190 | 195 | ||
191 | iowrite32(val, &sndev->mmio_self_dbmsg->omsg[idx].msg); | 196 | iowrite32(val, &sndev->mmio_peer_dbmsg->omsg[idx].msg); |
192 | 197 | ||
193 | return 0; | 198 | return 0; |
194 | } | 199 | } |
@@ -197,7 +202,7 @@ static int switchtec_ntb_mw_count(struct ntb_dev *ntb, int pidx) | |||
197 | { | 202 | { |
198 | struct switchtec_ntb *sndev = ntb_sndev(ntb); | 203 | struct switchtec_ntb *sndev = ntb_sndev(ntb); |
199 | int nr_direct_mw = sndev->peer_nr_direct_mw; | 204 | int nr_direct_mw = sndev->peer_nr_direct_mw; |
200 | int nr_lut_mw = sndev->peer_nr_lut_mw - 1; | 205 | int nr_lut_mw = sndev->peer_nr_lut_mw - sndev->nr_rsvd_luts; |
201 | 206 | ||
202 | if (pidx != NTB_DEF_PEER_IDX) | 207 | if (pidx != NTB_DEF_PEER_IDX) |
203 | return -EINVAL; | 208 | return -EINVAL; |
@@ -210,12 +215,12 @@ static int switchtec_ntb_mw_count(struct ntb_dev *ntb, int pidx) | |||
210 | 215 | ||
211 | static int lut_index(struct switchtec_ntb *sndev, int mw_idx) | 216 | static int lut_index(struct switchtec_ntb *sndev, int mw_idx) |
212 | { | 217 | { |
213 | return mw_idx - sndev->nr_direct_mw + 1; | 218 | return mw_idx - sndev->nr_direct_mw + sndev->nr_rsvd_luts; |
214 | } | 219 | } |
215 | 220 | ||
216 | static int peer_lut_index(struct switchtec_ntb *sndev, int mw_idx) | 221 | static int peer_lut_index(struct switchtec_ntb *sndev, int mw_idx) |
217 | { | 222 | { |
218 | return mw_idx - sndev->peer_nr_direct_mw + 1; | 223 | return mw_idx - sndev->peer_nr_direct_mw + sndev->nr_rsvd_luts; |
219 | } | 224 | } |
220 | 225 | ||
221 | static int switchtec_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, | 226 | static int switchtec_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, |
@@ -306,7 +311,7 @@ static int switchtec_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx, | |||
306 | if (pidx != NTB_DEF_PEER_IDX) | 311 | if (pidx != NTB_DEF_PEER_IDX) |
307 | return -EINVAL; | 312 | return -EINVAL; |
308 | 313 | ||
309 | dev_dbg(&sndev->stdev->dev, "MW %d: part %d addr %pad size %pap", | 314 | dev_dbg(&sndev->stdev->dev, "MW %d: part %d addr %pad size %pap\n", |
310 | widx, pidx, &addr, &size); | 315 | widx, pidx, &addr, &size); |
311 | 316 | ||
312 | if (widx >= switchtec_ntb_mw_count(ntb, pidx)) | 317 | if (widx >= switchtec_ntb_mw_count(ntb, pidx)) |
@@ -315,6 +320,19 @@ static int switchtec_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx, | |||
315 | if (xlate_pos < 12) | 320 | if (xlate_pos < 12) |
316 | return -EINVAL; | 321 | return -EINVAL; |
317 | 322 | ||
323 | if (!IS_ALIGNED(addr, BIT_ULL(xlate_pos))) { | ||
324 | /* | ||
325 | * In certain circumstances we can get a buffer that is | ||
326 | * not aligned to its size. (Most of the time | ||
327 | * dma_alloc_coherent ensures this). This can happen when | ||
328 | * using large buffers allocated by the CMA | ||
329 | * (see CMA_CONFIG_ALIGNMENT) | ||
330 | */ | ||
331 | dev_err(&sndev->stdev->dev, | ||
332 | "ERROR: Memory window address is not aligned to it's size!\n"); | ||
333 | return -EINVAL; | ||
334 | } | ||
335 | |||
318 | rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK, | 336 | rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK, |
319 | NTB_CTRL_PART_STATUS_LOCKED); | 337 | NTB_CTRL_PART_STATUS_LOCKED); |
320 | if (rc) | 338 | if (rc) |
@@ -337,7 +355,7 @@ static int switchtec_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx, | |||
337 | 355 | ||
338 | if (rc == -EIO) { | 356 | if (rc == -EIO) { |
339 | dev_err(&sndev->stdev->dev, | 357 | dev_err(&sndev->stdev->dev, |
340 | "Hardware reported an error configuring mw %d: %08x", | 358 | "Hardware reported an error configuring mw %d: %08x\n", |
341 | widx, ioread32(&ctl->bar_error)); | 359 | widx, ioread32(&ctl->bar_error)); |
342 | 360 | ||
343 | if (widx < nr_direct_mw) | 361 | if (widx < nr_direct_mw) |
@@ -355,8 +373,9 @@ static int switchtec_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx, | |||
355 | static int switchtec_ntb_peer_mw_count(struct ntb_dev *ntb) | 373 | static int switchtec_ntb_peer_mw_count(struct ntb_dev *ntb) |
356 | { | 374 | { |
357 | struct switchtec_ntb *sndev = ntb_sndev(ntb); | 375 | struct switchtec_ntb *sndev = ntb_sndev(ntb); |
376 | int nr_lut_mw = sndev->nr_lut_mw - sndev->nr_rsvd_luts; | ||
358 | 377 | ||
359 | return sndev->nr_direct_mw + (use_lut_mws ? sndev->nr_lut_mw - 1 : 0); | 378 | return sndev->nr_direct_mw + (use_lut_mws ? nr_lut_mw : 0); |
360 | } | 379 | } |
361 | 380 | ||
362 | static int switchtec_ntb_direct_get_addr(struct switchtec_ntb *sndev, | 381 | static int switchtec_ntb_direct_get_addr(struct switchtec_ntb *sndev, |
@@ -463,18 +482,69 @@ static void switchtec_ntb_set_link_speed(struct switchtec_ntb *sndev) | |||
463 | sndev->link_width = min(self_width, peer_width); | 482 | sndev->link_width = min(self_width, peer_width); |
464 | } | 483 | } |
465 | 484 | ||
466 | enum { | 485 | static int crosslink_is_enabled(struct switchtec_ntb *sndev) |
486 | { | ||
487 | struct ntb_info_regs __iomem *inf = sndev->mmio_ntb; | ||
488 | |||
489 | return ioread8(&inf->ntp_info[sndev->peer_partition].xlink_enabled); | ||
490 | } | ||
491 | |||
492 | static void crosslink_init_dbmsgs(struct switchtec_ntb *sndev) | ||
493 | { | ||
494 | int i; | ||
495 | u32 msg_map = 0; | ||
496 | |||
497 | if (!crosslink_is_enabled(sndev)) | ||
498 | return; | ||
499 | |||
500 | for (i = 0; i < ARRAY_SIZE(sndev->mmio_peer_dbmsg->imsg); i++) { | ||
501 | int m = i | sndev->self_partition << 2; | ||
502 | |||
503 | msg_map |= m << i * 8; | ||
504 | } | ||
505 | |||
506 | iowrite32(msg_map, &sndev->mmio_peer_dbmsg->msg_map); | ||
507 | iowrite64(sndev->db_valid_mask << sndev->db_peer_shift, | ||
508 | &sndev->mmio_peer_dbmsg->odb_mask); | ||
509 | } | ||
510 | |||
511 | enum switchtec_msg { | ||
467 | LINK_MESSAGE = 0, | 512 | LINK_MESSAGE = 0, |
468 | MSG_LINK_UP = 1, | 513 | MSG_LINK_UP = 1, |
469 | MSG_LINK_DOWN = 2, | 514 | MSG_LINK_DOWN = 2, |
470 | MSG_CHECK_LINK = 3, | 515 | MSG_CHECK_LINK = 3, |
516 | MSG_LINK_FORCE_DOWN = 4, | ||
471 | }; | 517 | }; |
472 | 518 | ||
473 | static void switchtec_ntb_check_link(struct switchtec_ntb *sndev) | 519 | static int switchtec_ntb_reinit_peer(struct switchtec_ntb *sndev); |
520 | |||
521 | static void link_reinit_work(struct work_struct *work) | ||
522 | { | ||
523 | struct switchtec_ntb *sndev; | ||
524 | |||
525 | sndev = container_of(work, struct switchtec_ntb, link_reinit_work); | ||
526 | |||
527 | switchtec_ntb_reinit_peer(sndev); | ||
528 | } | ||
529 | |||
530 | static void switchtec_ntb_check_link(struct switchtec_ntb *sndev, | ||
531 | enum switchtec_msg msg) | ||
474 | { | 532 | { |
475 | int link_sta; | 533 | int link_sta; |
476 | int old = sndev->link_is_up; | 534 | int old = sndev->link_is_up; |
477 | 535 | ||
536 | if (msg == MSG_LINK_FORCE_DOWN) { | ||
537 | schedule_work(&sndev->link_reinit_work); | ||
538 | |||
539 | if (sndev->link_is_up) { | ||
540 | sndev->link_is_up = 0; | ||
541 | ntb_link_event(&sndev->ntb); | ||
542 | dev_info(&sndev->stdev->dev, "ntb link forced down\n"); | ||
543 | } | ||
544 | |||
545 | return; | ||
546 | } | ||
547 | |||
478 | link_sta = sndev->self_shared->link_sta; | 548 | link_sta = sndev->self_shared->link_sta; |
479 | if (link_sta) { | 549 | if (link_sta) { |
480 | u64 peer = ioread64(&sndev->peer_shared->magic); | 550 | u64 peer = ioread64(&sndev->peer_shared->magic); |
@@ -491,8 +561,11 @@ static void switchtec_ntb_check_link(struct switchtec_ntb *sndev) | |||
491 | if (link_sta != old) { | 561 | if (link_sta != old) { |
492 | switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_CHECK_LINK); | 562 | switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_CHECK_LINK); |
493 | ntb_link_event(&sndev->ntb); | 563 | ntb_link_event(&sndev->ntb); |
494 | dev_info(&sndev->stdev->dev, "ntb link %s", | 564 | dev_info(&sndev->stdev->dev, "ntb link %s\n", |
495 | link_sta ? "up" : "down"); | 565 | link_sta ? "up" : "down"); |
566 | |||
567 | if (link_sta) | ||
568 | crosslink_init_dbmsgs(sndev); | ||
496 | } | 569 | } |
497 | } | 570 | } |
498 | 571 | ||
@@ -500,7 +573,7 @@ static void switchtec_ntb_link_notification(struct switchtec_dev *stdev) | |||
500 | { | 573 | { |
501 | struct switchtec_ntb *sndev = stdev->sndev; | 574 | struct switchtec_ntb *sndev = stdev->sndev; |
502 | 575 | ||
503 | switchtec_ntb_check_link(sndev); | 576 | switchtec_ntb_check_link(sndev, MSG_CHECK_LINK); |
504 | } | 577 | } |
505 | 578 | ||
506 | static u64 switchtec_ntb_link_is_up(struct ntb_dev *ntb, | 579 | static u64 switchtec_ntb_link_is_up(struct ntb_dev *ntb, |
@@ -523,12 +596,12 @@ static int switchtec_ntb_link_enable(struct ntb_dev *ntb, | |||
523 | { | 596 | { |
524 | struct switchtec_ntb *sndev = ntb_sndev(ntb); | 597 | struct switchtec_ntb *sndev = ntb_sndev(ntb); |
525 | 598 | ||
526 | dev_dbg(&sndev->stdev->dev, "enabling link"); | 599 | dev_dbg(&sndev->stdev->dev, "enabling link\n"); |
527 | 600 | ||
528 | sndev->self_shared->link_sta = 1; | 601 | sndev->self_shared->link_sta = 1; |
529 | switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_UP); | 602 | switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_UP); |
530 | 603 | ||
531 | switchtec_ntb_check_link(sndev); | 604 | switchtec_ntb_check_link(sndev, MSG_CHECK_LINK); |
532 | 605 | ||
533 | return 0; | 606 | return 0; |
534 | } | 607 | } |
@@ -537,12 +610,12 @@ static int switchtec_ntb_link_disable(struct ntb_dev *ntb) | |||
537 | { | 610 | { |
538 | struct switchtec_ntb *sndev = ntb_sndev(ntb); | 611 | struct switchtec_ntb *sndev = ntb_sndev(ntb); |
539 | 612 | ||
540 | dev_dbg(&sndev->stdev->dev, "disabling link"); | 613 | dev_dbg(&sndev->stdev->dev, "disabling link\n"); |
541 | 614 | ||
542 | sndev->self_shared->link_sta = 0; | 615 | sndev->self_shared->link_sta = 0; |
543 | switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_UP); | 616 | switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_DOWN); |
544 | 617 | ||
545 | switchtec_ntb_check_link(sndev); | 618 | switchtec_ntb_check_link(sndev, MSG_CHECK_LINK); |
546 | 619 | ||
547 | return 0; | 620 | return 0; |
548 | } | 621 | } |
@@ -638,7 +711,7 @@ static int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb, | |||
638 | struct switchtec_ntb *sndev = ntb_sndev(ntb); | 711 | struct switchtec_ntb *sndev = ntb_sndev(ntb); |
639 | unsigned long offset; | 712 | unsigned long offset; |
640 | 713 | ||
641 | offset = (unsigned long)sndev->mmio_self_dbmsg->odb - | 714 | offset = (unsigned long)sndev->mmio_peer_dbmsg->odb - |
642 | (unsigned long)sndev->stdev->mmio; | 715 | (unsigned long)sndev->stdev->mmio; |
643 | 716 | ||
644 | offset += sndev->db_shift / 8; | 717 | offset += sndev->db_shift / 8; |
@@ -656,7 +729,7 @@ static int switchtec_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits) | |||
656 | struct switchtec_ntb *sndev = ntb_sndev(ntb); | 729 | struct switchtec_ntb *sndev = ntb_sndev(ntb); |
657 | 730 | ||
658 | iowrite64(db_bits << sndev->db_peer_shift, | 731 | iowrite64(db_bits << sndev->db_peer_shift, |
659 | &sndev->mmio_self_dbmsg->odb); | 732 | &sndev->mmio_peer_dbmsg->odb); |
660 | 733 | ||
661 | return 0; | 734 | return 0; |
662 | } | 735 | } |
@@ -777,24 +850,63 @@ static const struct ntb_dev_ops switchtec_ntb_ops = { | |||
777 | .peer_spad_addr = switchtec_ntb_peer_spad_addr, | 850 | .peer_spad_addr = switchtec_ntb_peer_spad_addr, |
778 | }; | 851 | }; |
779 | 852 | ||
780 | static void switchtec_ntb_init_sndev(struct switchtec_ntb *sndev) | 853 | static int switchtec_ntb_init_sndev(struct switchtec_ntb *sndev) |
781 | { | 854 | { |
855 | u64 tpart_vec; | ||
856 | int self; | ||
782 | u64 part_map; | 857 | u64 part_map; |
858 | int bit; | ||
783 | 859 | ||
784 | sndev->ntb.pdev = sndev->stdev->pdev; | 860 | sndev->ntb.pdev = sndev->stdev->pdev; |
785 | sndev->ntb.topo = NTB_TOPO_SWITCH; | 861 | sndev->ntb.topo = NTB_TOPO_SWITCH; |
786 | sndev->ntb.ops = &switchtec_ntb_ops; | 862 | sndev->ntb.ops = &switchtec_ntb_ops; |
787 | 863 | ||
864 | INIT_WORK(&sndev->link_reinit_work, link_reinit_work); | ||
865 | |||
788 | sndev->self_partition = sndev->stdev->partition; | 866 | sndev->self_partition = sndev->stdev->partition; |
789 | 867 | ||
790 | sndev->mmio_ntb = sndev->stdev->mmio_ntb; | 868 | sndev->mmio_ntb = sndev->stdev->mmio_ntb; |
869 | |||
870 | self = sndev->self_partition; | ||
871 | tpart_vec = ioread32(&sndev->mmio_ntb->ntp_info[self].target_part_high); | ||
872 | tpart_vec <<= 32; | ||
873 | tpart_vec |= ioread32(&sndev->mmio_ntb->ntp_info[self].target_part_low); | ||
874 | |||
791 | part_map = ioread64(&sndev->mmio_ntb->ep_map); | 875 | part_map = ioread64(&sndev->mmio_ntb->ep_map); |
792 | part_map &= ~(1 << sndev->self_partition); | 876 | part_map &= ~(1 << sndev->self_partition); |
793 | sndev->peer_partition = ffs(part_map) - 1; | ||
794 | 877 | ||
795 | dev_dbg(&sndev->stdev->dev, "Partition ID %d of %d (%llx)", | 878 | if (!ffs(tpart_vec)) { |
796 | sndev->self_partition, sndev->stdev->partition_count, | 879 | if (sndev->stdev->partition_count != 2) { |
797 | part_map); | 880 | dev_err(&sndev->stdev->dev, |
881 | "ntb target partition not defined\n"); | ||
882 | return -ENODEV; | ||
883 | } | ||
884 | |||
885 | bit = ffs(part_map); | ||
886 | if (!bit) { | ||
887 | dev_err(&sndev->stdev->dev, | ||
888 | "peer partition is not NT partition\n"); | ||
889 | return -ENODEV; | ||
890 | } | ||
891 | |||
892 | sndev->peer_partition = bit - 1; | ||
893 | } else { | ||
894 | if (ffs(tpart_vec) != fls(tpart_vec)) { | ||
895 | dev_err(&sndev->stdev->dev, | ||
896 | "ntb driver only supports 1 pair of 1-1 ntb mapping\n"); | ||
897 | return -ENODEV; | ||
898 | } | ||
899 | |||
900 | sndev->peer_partition = ffs(tpart_vec) - 1; | ||
901 | if (!(part_map & (1 << sndev->peer_partition))) { | ||
902 | dev_err(&sndev->stdev->dev, | ||
903 | "ntb target partition is not NT partition\n"); | ||
904 | return -ENODEV; | ||
905 | } | ||
906 | } | ||
907 | |||
908 | dev_dbg(&sndev->stdev->dev, "Partition ID %d of %d\n", | ||
909 | sndev->self_partition, sndev->stdev->partition_count); | ||
798 | 910 | ||
799 | sndev->mmio_ctrl = (void * __iomem)sndev->mmio_ntb + | 911 | sndev->mmio_ctrl = (void * __iomem)sndev->mmio_ntb + |
800 | SWITCHTEC_NTB_REG_CTRL_OFFSET; | 912 | SWITCHTEC_NTB_REG_CTRL_OFFSET; |
@@ -804,6 +916,283 @@ static void switchtec_ntb_init_sndev(struct switchtec_ntb *sndev) | |||
804 | sndev->mmio_self_ctrl = &sndev->mmio_ctrl[sndev->self_partition]; | 916 | sndev->mmio_self_ctrl = &sndev->mmio_ctrl[sndev->self_partition]; |
805 | sndev->mmio_peer_ctrl = &sndev->mmio_ctrl[sndev->peer_partition]; | 917 | sndev->mmio_peer_ctrl = &sndev->mmio_ctrl[sndev->peer_partition]; |
806 | sndev->mmio_self_dbmsg = &sndev->mmio_dbmsg[sndev->self_partition]; | 918 | sndev->mmio_self_dbmsg = &sndev->mmio_dbmsg[sndev->self_partition]; |
919 | sndev->mmio_peer_dbmsg = sndev->mmio_self_dbmsg; | ||
920 | |||
921 | return 0; | ||
922 | } | ||
923 | |||
924 | static int config_rsvd_lut_win(struct switchtec_ntb *sndev, | ||
925 | struct ntb_ctrl_regs __iomem *ctl, | ||
926 | int lut_idx, int partition, u64 addr) | ||
927 | { | ||
928 | int peer_bar = sndev->peer_direct_mw_to_bar[0]; | ||
929 | u32 ctl_val; | ||
930 | int rc; | ||
931 | |||
932 | rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK, | ||
933 | NTB_CTRL_PART_STATUS_LOCKED); | ||
934 | if (rc) | ||
935 | return rc; | ||
936 | |||
937 | ctl_val = ioread32(&ctl->bar_entry[peer_bar].ctl); | ||
938 | ctl_val &= 0xFF; | ||
939 | ctl_val |= NTB_CTRL_BAR_LUT_WIN_EN; | ||
940 | ctl_val |= ilog2(LUT_SIZE) << 8; | ||
941 | ctl_val |= (sndev->nr_lut_mw - 1) << 14; | ||
942 | iowrite32(ctl_val, &ctl->bar_entry[peer_bar].ctl); | ||
943 | |||
944 | iowrite64((NTB_CTRL_LUT_EN | (partition << 1) | addr), | ||
945 | &ctl->lut_entry[lut_idx]); | ||
946 | |||
947 | rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG, | ||
948 | NTB_CTRL_PART_STATUS_NORMAL); | ||
949 | if (rc) { | ||
950 | u32 bar_error, lut_error; | ||
951 | |||
952 | bar_error = ioread32(&ctl->bar_error); | ||
953 | lut_error = ioread32(&ctl->lut_error); | ||
954 | dev_err(&sndev->stdev->dev, | ||
955 | "Error setting up reserved lut window: %08x / %08x\n", | ||
956 | bar_error, lut_error); | ||
957 | return rc; | ||
958 | } | ||
959 | |||
960 | return 0; | ||
961 | } | ||
962 | |||
963 | static int config_req_id_table(struct switchtec_ntb *sndev, | ||
964 | struct ntb_ctrl_regs __iomem *mmio_ctrl, | ||
965 | int *req_ids, int count) | ||
966 | { | ||
967 | int i, rc = 0; | ||
968 | u32 error; | ||
969 | u32 proxy_id; | ||
970 | |||
971 | if (ioread32(&mmio_ctrl->req_id_table_size) < count) { | ||
972 | dev_err(&sndev->stdev->dev, | ||
973 | "Not enough requester IDs available.\n"); | ||
974 | return -EFAULT; | ||
975 | } | ||
976 | |||
977 | rc = switchtec_ntb_part_op(sndev, mmio_ctrl, | ||
978 | NTB_CTRL_PART_OP_LOCK, | ||
979 | NTB_CTRL_PART_STATUS_LOCKED); | ||
980 | if (rc) | ||
981 | return rc; | ||
982 | |||
983 | iowrite32(NTB_PART_CTRL_ID_PROT_DIS, | ||
984 | &mmio_ctrl->partition_ctrl); | ||
985 | |||
986 | for (i = 0; i < count; i++) { | ||
987 | iowrite32(req_ids[i] << 16 | NTB_CTRL_REQ_ID_EN, | ||
988 | &mmio_ctrl->req_id_table[i]); | ||
989 | |||
990 | proxy_id = ioread32(&mmio_ctrl->req_id_table[i]); | ||
991 | dev_dbg(&sndev->stdev->dev, | ||
992 | "Requester ID %02X:%02X.%X -> BB:%02X.%X\n", | ||
993 | req_ids[i] >> 8, (req_ids[i] >> 3) & 0x1F, | ||
994 | req_ids[i] & 0x7, (proxy_id >> 4) & 0x1F, | ||
995 | (proxy_id >> 1) & 0x7); | ||
996 | } | ||
997 | |||
998 | rc = switchtec_ntb_part_op(sndev, mmio_ctrl, | ||
999 | NTB_CTRL_PART_OP_CFG, | ||
1000 | NTB_CTRL_PART_STATUS_NORMAL); | ||
1001 | |||
1002 | if (rc == -EIO) { | ||
1003 | error = ioread32(&mmio_ctrl->req_id_error); | ||
1004 | dev_err(&sndev->stdev->dev, | ||
1005 | "Error setting up the requester ID table: %08x\n", | ||
1006 | error); | ||
1007 | } | ||
1008 | |||
1009 | return 0; | ||
1010 | } | ||
1011 | |||
1012 | static int crosslink_setup_mws(struct switchtec_ntb *sndev, int ntb_lut_idx, | ||
1013 | u64 *mw_addrs, int mw_count) | ||
1014 | { | ||
1015 | int rc, i; | ||
1016 | struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_self_ctrl; | ||
1017 | u64 addr; | ||
1018 | size_t size, offset; | ||
1019 | int bar; | ||
1020 | int xlate_pos; | ||
1021 | u32 ctl_val; | ||
1022 | |||
1023 | rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK, | ||
1024 | NTB_CTRL_PART_STATUS_LOCKED); | ||
1025 | if (rc) | ||
1026 | return rc; | ||
1027 | |||
1028 | for (i = 0; i < sndev->nr_lut_mw; i++) { | ||
1029 | if (i == ntb_lut_idx) | ||
1030 | continue; | ||
1031 | |||
1032 | addr = mw_addrs[0] + LUT_SIZE * i; | ||
1033 | |||
1034 | iowrite64((NTB_CTRL_LUT_EN | (sndev->peer_partition << 1) | | ||
1035 | addr), | ||
1036 | &ctl->lut_entry[i]); | ||
1037 | } | ||
1038 | |||
1039 | sndev->nr_direct_mw = min_t(int, sndev->nr_direct_mw, mw_count); | ||
1040 | |||
1041 | for (i = 0; i < sndev->nr_direct_mw; i++) { | ||
1042 | bar = sndev->direct_mw_to_bar[i]; | ||
1043 | offset = (i == 0) ? LUT_SIZE * sndev->nr_lut_mw : 0; | ||
1044 | addr = mw_addrs[i] + offset; | ||
1045 | size = pci_resource_len(sndev->ntb.pdev, bar) - offset; | ||
1046 | xlate_pos = ilog2(size); | ||
1047 | |||
1048 | if (offset && size > offset) | ||
1049 | size = offset; | ||
1050 | |||
1051 | ctl_val = ioread32(&ctl->bar_entry[bar].ctl); | ||
1052 | ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN; | ||
1053 | |||
1054 | iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); | ||
1055 | iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size); | ||
1056 | iowrite64(sndev->peer_partition | addr, | ||
1057 | &ctl->bar_entry[bar].xlate_addr); | ||
1058 | } | ||
1059 | |||
1060 | rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG, | ||
1061 | NTB_CTRL_PART_STATUS_NORMAL); | ||
1062 | if (rc) { | ||
1063 | u32 bar_error, lut_error; | ||
1064 | |||
1065 | bar_error = ioread32(&ctl->bar_error); | ||
1066 | lut_error = ioread32(&ctl->lut_error); | ||
1067 | dev_err(&sndev->stdev->dev, | ||
1068 | "Error setting up cross link windows: %08x / %08x\n", | ||
1069 | bar_error, lut_error); | ||
1070 | return rc; | ||
1071 | } | ||
1072 | |||
1073 | return 0; | ||
1074 | } | ||
1075 | |||
1076 | static int crosslink_setup_req_ids(struct switchtec_ntb *sndev, | ||
1077 | struct ntb_ctrl_regs __iomem *mmio_ctrl) | ||
1078 | { | ||
1079 | int req_ids[16]; | ||
1080 | int i; | ||
1081 | u32 proxy_id; | ||
1082 | |||
1083 | for (i = 0; i < ARRAY_SIZE(req_ids); i++) { | ||
1084 | proxy_id = ioread32(&sndev->mmio_self_ctrl->req_id_table[i]); | ||
1085 | |||
1086 | if (!(proxy_id & NTB_CTRL_REQ_ID_EN)) | ||
1087 | break; | ||
1088 | |||
1089 | req_ids[i] = ((proxy_id >> 1) & 0xFF); | ||
1090 | } | ||
1091 | |||
1092 | return config_req_id_table(sndev, mmio_ctrl, req_ids, i); | ||
1093 | } | ||
1094 | |||
1095 | /* | ||
1096 | * In crosslink configuration there is a virtual partition in the | ||
1097 | * middle of the two switches. The BARs in this partition have to be | ||
1098 | * enumerated and assigned addresses. | ||
1099 | */ | ||
1100 | static int crosslink_enum_partition(struct switchtec_ntb *sndev, | ||
1101 | u64 *bar_addrs) | ||
1102 | { | ||
1103 | struct part_cfg_regs __iomem *part_cfg = | ||
1104 | &sndev->stdev->mmio_part_cfg_all[sndev->peer_partition]; | ||
1105 | u32 pff = ioread32(&part_cfg->vep_pff_inst_id); | ||
1106 | struct pff_csr_regs __iomem *mmio_pff = | ||
1107 | &sndev->stdev->mmio_pff_csr[pff]; | ||
1108 | const u64 bar_space = 0x1000000000LL; | ||
1109 | u64 bar_addr; | ||
1110 | int bar_cnt = 0; | ||
1111 | int i; | ||
1112 | |||
1113 | iowrite16(0x6, &mmio_pff->pcicmd); | ||
1114 | |||
1115 | for (i = 0; i < ARRAY_SIZE(mmio_pff->pci_bar64); i++) { | ||
1116 | iowrite64(bar_space * i, &mmio_pff->pci_bar64[i]); | ||
1117 | bar_addr = ioread64(&mmio_pff->pci_bar64[i]); | ||
1118 | bar_addr &= ~0xf; | ||
1119 | |||
1120 | dev_dbg(&sndev->stdev->dev, | ||
1121 | "Crosslink BAR%d addr: %llx\n", | ||
1122 | i, bar_addr); | ||
1123 | |||
1124 | if (bar_addr != bar_space * i) | ||
1125 | continue; | ||
1126 | |||
1127 | bar_addrs[bar_cnt++] = bar_addr; | ||
1128 | } | ||
1129 | |||
1130 | return bar_cnt; | ||
1131 | } | ||
1132 | |||
1133 | static int switchtec_ntb_init_crosslink(struct switchtec_ntb *sndev) | ||
1134 | { | ||
1135 | int rc; | ||
1136 | int bar = sndev->direct_mw_to_bar[0]; | ||
1137 | const int ntb_lut_idx = 1; | ||
1138 | u64 bar_addrs[6]; | ||
1139 | u64 addr; | ||
1140 | int offset; | ||
1141 | int bar_cnt; | ||
1142 | |||
1143 | if (!crosslink_is_enabled(sndev)) | ||
1144 | return 0; | ||
1145 | |||
1146 | dev_info(&sndev->stdev->dev, "Using crosslink configuration\n"); | ||
1147 | sndev->ntb.topo = NTB_TOPO_CROSSLINK; | ||
1148 | |||
1149 | bar_cnt = crosslink_enum_partition(sndev, bar_addrs); | ||
1150 | if (bar_cnt < sndev->nr_direct_mw + 1) { | ||
1151 | dev_err(&sndev->stdev->dev, | ||
1152 | "Error enumerating crosslink partition\n"); | ||
1153 | return -EINVAL; | ||
1154 | } | ||
1155 | |||
1156 | addr = (bar_addrs[0] + SWITCHTEC_GAS_NTB_OFFSET + | ||
1157 | SWITCHTEC_NTB_REG_DBMSG_OFFSET + | ||
1158 | sizeof(struct ntb_dbmsg_regs) * sndev->peer_partition); | ||
1159 | |||
1160 | offset = addr & (LUT_SIZE - 1); | ||
1161 | addr -= offset; | ||
1162 | |||
1163 | rc = config_rsvd_lut_win(sndev, sndev->mmio_self_ctrl, ntb_lut_idx, | ||
1164 | sndev->peer_partition, addr); | ||
1165 | if (rc) | ||
1166 | return rc; | ||
1167 | |||
1168 | rc = crosslink_setup_mws(sndev, ntb_lut_idx, &bar_addrs[1], | ||
1169 | bar_cnt - 1); | ||
1170 | if (rc) | ||
1171 | return rc; | ||
1172 | |||
1173 | rc = crosslink_setup_req_ids(sndev, sndev->mmio_peer_ctrl); | ||
1174 | if (rc) | ||
1175 | return rc; | ||
1176 | |||
1177 | sndev->mmio_xlink_win = pci_iomap_range(sndev->stdev->pdev, bar, | ||
1178 | LUT_SIZE, LUT_SIZE); | ||
1179 | if (!sndev->mmio_xlink_win) { | ||
1180 | rc = -ENOMEM; | ||
1181 | return rc; | ||
1182 | } | ||
1183 | |||
1184 | sndev->mmio_peer_dbmsg = sndev->mmio_xlink_win + offset; | ||
1185 | sndev->nr_rsvd_luts++; | ||
1186 | |||
1187 | crosslink_init_dbmsgs(sndev); | ||
1188 | |||
1189 | return 0; | ||
1190 | } | ||
1191 | |||
1192 | static void switchtec_ntb_deinit_crosslink(struct switchtec_ntb *sndev) | ||
1193 | { | ||
1194 | if (sndev->mmio_xlink_win) | ||
1195 | pci_iounmap(sndev->stdev->pdev, sndev->mmio_xlink_win); | ||
807 | } | 1196 | } |
808 | 1197 | ||
809 | static int map_bars(int *map, struct ntb_ctrl_regs __iomem *ctrl) | 1198 | static int map_bars(int *map, struct ntb_ctrl_regs __iomem *ctrl) |
@@ -829,7 +1218,7 @@ static void switchtec_ntb_init_mw(struct switchtec_ntb *sndev) | |||
829 | sndev->nr_lut_mw = ioread16(&sndev->mmio_self_ctrl->lut_table_entries); | 1218 | sndev->nr_lut_mw = ioread16(&sndev->mmio_self_ctrl->lut_table_entries); |
830 | sndev->nr_lut_mw = rounddown_pow_of_two(sndev->nr_lut_mw); | 1219 | sndev->nr_lut_mw = rounddown_pow_of_two(sndev->nr_lut_mw); |
831 | 1220 | ||
832 | dev_dbg(&sndev->stdev->dev, "MWs: %d direct, %d lut", | 1221 | dev_dbg(&sndev->stdev->dev, "MWs: %d direct, %d lut\n", |
833 | sndev->nr_direct_mw, sndev->nr_lut_mw); | 1222 | sndev->nr_direct_mw, sndev->nr_lut_mw); |
834 | 1223 | ||
835 | sndev->peer_nr_direct_mw = map_bars(sndev->peer_direct_mw_to_bar, | 1224 | sndev->peer_nr_direct_mw = map_bars(sndev->peer_direct_mw_to_bar, |
@@ -839,7 +1228,7 @@ static void switchtec_ntb_init_mw(struct switchtec_ntb *sndev) | |||
839 | ioread16(&sndev->mmio_peer_ctrl->lut_table_entries); | 1228 | ioread16(&sndev->mmio_peer_ctrl->lut_table_entries); |
840 | sndev->peer_nr_lut_mw = rounddown_pow_of_two(sndev->peer_nr_lut_mw); | 1229 | sndev->peer_nr_lut_mw = rounddown_pow_of_two(sndev->peer_nr_lut_mw); |
841 | 1230 | ||
842 | dev_dbg(&sndev->stdev->dev, "Peer MWs: %d direct, %d lut", | 1231 | dev_dbg(&sndev->stdev->dev, "Peer MWs: %d direct, %d lut\n", |
843 | sndev->peer_nr_direct_mw, sndev->peer_nr_lut_mw); | 1232 | sndev->peer_nr_direct_mw, sndev->peer_nr_lut_mw); |
844 | 1233 | ||
845 | } | 1234 | } |
@@ -849,24 +1238,35 @@ static void switchtec_ntb_init_mw(struct switchtec_ntb *sndev) | |||
849 | * shared among all partitions. So we must split them in half | 1238 | * shared among all partitions. So we must split them in half |
850 | * (32 for each partition). However, the message interrupts are | 1239 | * (32 for each partition). However, the message interrupts are |
851 | * also shared with the top 4 doorbells so we just limit this to | 1240 | * also shared with the top 4 doorbells so we just limit this to |
852 | * 28 doorbells per partition | 1241 | * 28 doorbells per partition. |
1242 | * | ||
1243 | * In crosslink mode, each side has it's own dbmsg register so | ||
1244 | * they can each use all 60 of the available doorbells. | ||
853 | */ | 1245 | */ |
854 | static void switchtec_ntb_init_db(struct switchtec_ntb *sndev) | 1246 | static void switchtec_ntb_init_db(struct switchtec_ntb *sndev) |
855 | { | 1247 | { |
856 | sndev->db_valid_mask = 0x0FFFFFFF; | 1248 | sndev->db_mask = 0x0FFFFFFFFFFFFFFFULL; |
857 | 1249 | ||
858 | if (sndev->self_partition < sndev->peer_partition) { | 1250 | if (sndev->mmio_peer_dbmsg != sndev->mmio_self_dbmsg) { |
1251 | sndev->db_shift = 0; | ||
1252 | sndev->db_peer_shift = 0; | ||
1253 | sndev->db_valid_mask = sndev->db_mask; | ||
1254 | } else if (sndev->self_partition < sndev->peer_partition) { | ||
859 | sndev->db_shift = 0; | 1255 | sndev->db_shift = 0; |
860 | sndev->db_peer_shift = 32; | 1256 | sndev->db_peer_shift = 32; |
1257 | sndev->db_valid_mask = 0x0FFFFFFF; | ||
861 | } else { | 1258 | } else { |
862 | sndev->db_shift = 32; | 1259 | sndev->db_shift = 32; |
863 | sndev->db_peer_shift = 0; | 1260 | sndev->db_peer_shift = 0; |
1261 | sndev->db_valid_mask = 0x0FFFFFFF; | ||
864 | } | 1262 | } |
865 | 1263 | ||
866 | sndev->db_mask = 0x0FFFFFFFFFFFFFFFULL; | ||
867 | iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask); | 1264 | iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask); |
868 | iowrite64(sndev->db_valid_mask << sndev->db_peer_shift, | 1265 | iowrite64(sndev->db_valid_mask << sndev->db_peer_shift, |
869 | &sndev->mmio_self_dbmsg->odb_mask); | 1266 | &sndev->mmio_peer_dbmsg->odb_mask); |
1267 | |||
1268 | dev_dbg(&sndev->stdev->dev, "dbs: shift %d/%d, mask %016llx\n", | ||
1269 | sndev->db_shift, sndev->db_peer_shift, sndev->db_valid_mask); | ||
870 | } | 1270 | } |
871 | 1271 | ||
872 | static void switchtec_ntb_init_msgs(struct switchtec_ntb *sndev) | 1272 | static void switchtec_ntb_init_msgs(struct switchtec_ntb *sndev) |
@@ -887,52 +1287,23 @@ static void switchtec_ntb_init_msgs(struct switchtec_ntb *sndev) | |||
887 | &sndev->mmio_self_dbmsg->imsg[i]); | 1287 | &sndev->mmio_self_dbmsg->imsg[i]); |
888 | } | 1288 | } |
889 | 1289 | ||
890 | static int switchtec_ntb_init_req_id_table(struct switchtec_ntb *sndev) | 1290 | static int |
1291 | switchtec_ntb_init_req_id_table(struct switchtec_ntb *sndev) | ||
891 | { | 1292 | { |
892 | int rc = 0; | 1293 | int req_ids[2]; |
893 | u16 req_id; | ||
894 | u32 error; | ||
895 | |||
896 | req_id = ioread16(&sndev->mmio_ntb->requester_id); | ||
897 | |||
898 | if (ioread32(&sndev->mmio_self_ctrl->req_id_table_size) < 2) { | ||
899 | dev_err(&sndev->stdev->dev, | ||
900 | "Not enough requester IDs available."); | ||
901 | return -EFAULT; | ||
902 | } | ||
903 | |||
904 | rc = switchtec_ntb_part_op(sndev, sndev->mmio_self_ctrl, | ||
905 | NTB_CTRL_PART_OP_LOCK, | ||
906 | NTB_CTRL_PART_STATUS_LOCKED); | ||
907 | if (rc) | ||
908 | return rc; | ||
909 | |||
910 | iowrite32(NTB_PART_CTRL_ID_PROT_DIS, | ||
911 | &sndev->mmio_self_ctrl->partition_ctrl); | ||
912 | 1294 | ||
913 | /* | 1295 | /* |
914 | * Root Complex Requester ID (which is 0:00.0) | 1296 | * Root Complex Requester ID (which is 0:00.0) |
915 | */ | 1297 | */ |
916 | iowrite32(0 << 16 | NTB_CTRL_REQ_ID_EN, | 1298 | req_ids[0] = 0; |
917 | &sndev->mmio_self_ctrl->req_id_table[0]); | ||
918 | 1299 | ||
919 | /* | 1300 | /* |
920 | * Host Bridge Requester ID (as read from the mmap address) | 1301 | * Host Bridge Requester ID (as read from the mmap address) |
921 | */ | 1302 | */ |
922 | iowrite32(req_id << 16 | NTB_CTRL_REQ_ID_EN, | 1303 | req_ids[1] = ioread16(&sndev->mmio_ntb->requester_id); |
923 | &sndev->mmio_self_ctrl->req_id_table[1]); | ||
924 | |||
925 | rc = switchtec_ntb_part_op(sndev, sndev->mmio_self_ctrl, | ||
926 | NTB_CTRL_PART_OP_CFG, | ||
927 | NTB_CTRL_PART_STATUS_NORMAL); | ||
928 | if (rc == -EIO) { | ||
929 | error = ioread32(&sndev->mmio_self_ctrl->req_id_error); | ||
930 | dev_err(&sndev->stdev->dev, | ||
931 | "Error setting up the requester ID table: %08x", | ||
932 | error); | ||
933 | } | ||
934 | 1304 | ||
935 | return rc; | 1305 | return config_req_id_table(sndev, sndev->mmio_self_ctrl, req_ids, |
1306 | ARRAY_SIZE(req_ids)); | ||
936 | } | 1307 | } |
937 | 1308 | ||
938 | static void switchtec_ntb_init_shared(struct switchtec_ntb *sndev) | 1309 | static void switchtec_ntb_init_shared(struct switchtec_ntb *sndev) |
@@ -963,59 +1334,35 @@ static void switchtec_ntb_init_shared(struct switchtec_ntb *sndev) | |||
963 | 1334 | ||
964 | static int switchtec_ntb_init_shared_mw(struct switchtec_ntb *sndev) | 1335 | static int switchtec_ntb_init_shared_mw(struct switchtec_ntb *sndev) |
965 | { | 1336 | { |
966 | struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; | 1337 | int self_bar = sndev->direct_mw_to_bar[0]; |
967 | int bar = sndev->direct_mw_to_bar[0]; | ||
968 | u32 ctl_val; | ||
969 | int rc; | 1338 | int rc; |
970 | 1339 | ||
1340 | sndev->nr_rsvd_luts++; | ||
971 | sndev->self_shared = dma_zalloc_coherent(&sndev->stdev->pdev->dev, | 1341 | sndev->self_shared = dma_zalloc_coherent(&sndev->stdev->pdev->dev, |
972 | LUT_SIZE, | 1342 | LUT_SIZE, |
973 | &sndev->self_shared_dma, | 1343 | &sndev->self_shared_dma, |
974 | GFP_KERNEL); | 1344 | GFP_KERNEL); |
975 | if (!sndev->self_shared) { | 1345 | if (!sndev->self_shared) { |
976 | dev_err(&sndev->stdev->dev, | 1346 | dev_err(&sndev->stdev->dev, |
977 | "unable to allocate memory for shared mw"); | 1347 | "unable to allocate memory for shared mw\n"); |
978 | return -ENOMEM; | 1348 | return -ENOMEM; |
979 | } | 1349 | } |
980 | 1350 | ||
981 | switchtec_ntb_init_shared(sndev); | 1351 | switchtec_ntb_init_shared(sndev); |
982 | 1352 | ||
983 | rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK, | 1353 | rc = config_rsvd_lut_win(sndev, sndev->mmio_peer_ctrl, 0, |
984 | NTB_CTRL_PART_STATUS_LOCKED); | 1354 | sndev->self_partition, |
1355 | sndev->self_shared_dma); | ||
985 | if (rc) | 1356 | if (rc) |
986 | goto unalloc_and_exit; | 1357 | goto unalloc_and_exit; |
987 | 1358 | ||
988 | ctl_val = ioread32(&ctl->bar_entry[bar].ctl); | 1359 | sndev->peer_shared = pci_iomap(sndev->stdev->pdev, self_bar, LUT_SIZE); |
989 | ctl_val &= 0xFF; | ||
990 | ctl_val |= NTB_CTRL_BAR_LUT_WIN_EN; | ||
991 | ctl_val |= ilog2(LUT_SIZE) << 8; | ||
992 | ctl_val |= (sndev->nr_lut_mw - 1) << 14; | ||
993 | iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); | ||
994 | |||
995 | iowrite64((NTB_CTRL_LUT_EN | (sndev->self_partition << 1) | | ||
996 | sndev->self_shared_dma), | ||
997 | &ctl->lut_entry[0]); | ||
998 | |||
999 | rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG, | ||
1000 | NTB_CTRL_PART_STATUS_NORMAL); | ||
1001 | if (rc) { | ||
1002 | u32 bar_error, lut_error; | ||
1003 | |||
1004 | bar_error = ioread32(&ctl->bar_error); | ||
1005 | lut_error = ioread32(&ctl->lut_error); | ||
1006 | dev_err(&sndev->stdev->dev, | ||
1007 | "Error setting up shared MW: %08x / %08x", | ||
1008 | bar_error, lut_error); | ||
1009 | goto unalloc_and_exit; | ||
1010 | } | ||
1011 | |||
1012 | sndev->peer_shared = pci_iomap(sndev->stdev->pdev, bar, LUT_SIZE); | ||
1013 | if (!sndev->peer_shared) { | 1360 | if (!sndev->peer_shared) { |
1014 | rc = -ENOMEM; | 1361 | rc = -ENOMEM; |
1015 | goto unalloc_and_exit; | 1362 | goto unalloc_and_exit; |
1016 | } | 1363 | } |
1017 | 1364 | ||
1018 | dev_dbg(&sndev->stdev->dev, "Shared MW Ready"); | 1365 | dev_dbg(&sndev->stdev->dev, "Shared MW Ready\n"); |
1019 | return 0; | 1366 | return 0; |
1020 | 1367 | ||
1021 | unalloc_and_exit: | 1368 | unalloc_and_exit: |
@@ -1034,6 +1381,7 @@ static void switchtec_ntb_deinit_shared_mw(struct switchtec_ntb *sndev) | |||
1034 | dma_free_coherent(&sndev->stdev->pdev->dev, LUT_SIZE, | 1381 | dma_free_coherent(&sndev->stdev->pdev->dev, LUT_SIZE, |
1035 | sndev->self_shared, | 1382 | sndev->self_shared, |
1036 | sndev->self_shared_dma); | 1383 | sndev->self_shared_dma); |
1384 | sndev->nr_rsvd_luts--; | ||
1037 | } | 1385 | } |
1038 | 1386 | ||
1039 | static irqreturn_t switchtec_ntb_doorbell_isr(int irq, void *dev) | 1387 | static irqreturn_t switchtec_ntb_doorbell_isr(int irq, void *dev) |
@@ -1056,12 +1404,12 @@ static irqreturn_t switchtec_ntb_message_isr(int irq, void *dev) | |||
1056 | u64 msg = ioread64(&sndev->mmio_self_dbmsg->imsg[i]); | 1404 | u64 msg = ioread64(&sndev->mmio_self_dbmsg->imsg[i]); |
1057 | 1405 | ||
1058 | if (msg & NTB_DBMSG_IMSG_STATUS) { | 1406 | if (msg & NTB_DBMSG_IMSG_STATUS) { |
1059 | dev_dbg(&sndev->stdev->dev, "message: %d %08x\n", i, | 1407 | dev_dbg(&sndev->stdev->dev, "message: %d %08x\n", |
1060 | (u32)msg); | 1408 | i, (u32)msg); |
1061 | iowrite8(1, &sndev->mmio_self_dbmsg->imsg[i].status); | 1409 | iowrite8(1, &sndev->mmio_self_dbmsg->imsg[i].status); |
1062 | 1410 | ||
1063 | if (i == LINK_MESSAGE) | 1411 | if (i == LINK_MESSAGE) |
1064 | switchtec_ntb_check_link(sndev); | 1412 | switchtec_ntb_check_link(sndev, msg); |
1065 | } | 1413 | } |
1066 | } | 1414 | } |
1067 | 1415 | ||
@@ -1085,7 +1433,7 @@ static int switchtec_ntb_init_db_msg_irq(struct switchtec_ntb *sndev) | |||
1085 | message_irq == event_irq) | 1433 | message_irq == event_irq) |
1086 | message_irq++; | 1434 | message_irq++; |
1087 | 1435 | ||
1088 | dev_dbg(&sndev->stdev->dev, "irqs - event: %d, db: %d, msgs: %d", | 1436 | dev_dbg(&sndev->stdev->dev, "irqs - event: %d, db: %d, msgs: %d\n", |
1089 | event_irq, doorbell_irq, message_irq); | 1437 | event_irq, doorbell_irq, message_irq); |
1090 | 1438 | ||
1091 | for (i = 0; i < idb_vecs - 4; i++) | 1439 | for (i = 0; i < idb_vecs - 4; i++) |
@@ -1122,6 +1470,14 @@ static void switchtec_ntb_deinit_db_msg_irq(struct switchtec_ntb *sndev) | |||
1122 | free_irq(sndev->message_irq, sndev); | 1470 | free_irq(sndev->message_irq, sndev); |
1123 | } | 1471 | } |
1124 | 1472 | ||
1473 | static int switchtec_ntb_reinit_peer(struct switchtec_ntb *sndev) | ||
1474 | { | ||
1475 | dev_info(&sndev->stdev->dev, "peer reinitialized\n"); | ||
1476 | switchtec_ntb_deinit_shared_mw(sndev); | ||
1477 | switchtec_ntb_init_mw(sndev); | ||
1478 | return switchtec_ntb_init_shared_mw(sndev); | ||
1479 | } | ||
1480 | |||
1125 | static int switchtec_ntb_add(struct device *dev, | 1481 | static int switchtec_ntb_add(struct device *dev, |
1126 | struct class_interface *class_intf) | 1482 | struct class_interface *class_intf) |
1127 | { | 1483 | { |
@@ -1134,38 +1490,50 @@ static int switchtec_ntb_add(struct device *dev, | |||
1134 | if (stdev->pdev->class != MICROSEMI_NTB_CLASSCODE) | 1490 | if (stdev->pdev->class != MICROSEMI_NTB_CLASSCODE) |
1135 | return -ENODEV; | 1491 | return -ENODEV; |
1136 | 1492 | ||
1137 | if (stdev->partition_count != 2) | ||
1138 | dev_warn(dev, "ntb driver only supports 2 partitions"); | ||
1139 | |||
1140 | sndev = kzalloc_node(sizeof(*sndev), GFP_KERNEL, dev_to_node(dev)); | 1493 | sndev = kzalloc_node(sizeof(*sndev), GFP_KERNEL, dev_to_node(dev)); |
1141 | if (!sndev) | 1494 | if (!sndev) |
1142 | return -ENOMEM; | 1495 | return -ENOMEM; |
1143 | 1496 | ||
1144 | sndev->stdev = stdev; | 1497 | sndev->stdev = stdev; |
1145 | switchtec_ntb_init_sndev(sndev); | 1498 | rc = switchtec_ntb_init_sndev(sndev); |
1499 | if (rc) | ||
1500 | goto free_and_exit; | ||
1501 | |||
1146 | switchtec_ntb_init_mw(sndev); | 1502 | switchtec_ntb_init_mw(sndev); |
1147 | switchtec_ntb_init_db(sndev); | ||
1148 | switchtec_ntb_init_msgs(sndev); | ||
1149 | 1503 | ||
1150 | rc = switchtec_ntb_init_req_id_table(sndev); | 1504 | rc = switchtec_ntb_init_req_id_table(sndev); |
1151 | if (rc) | 1505 | if (rc) |
1152 | goto free_and_exit; | 1506 | goto free_and_exit; |
1153 | 1507 | ||
1154 | rc = switchtec_ntb_init_shared_mw(sndev); | 1508 | rc = switchtec_ntb_init_crosslink(sndev); |
1155 | if (rc) | 1509 | if (rc) |
1156 | goto free_and_exit; | 1510 | goto free_and_exit; |
1157 | 1511 | ||
1512 | switchtec_ntb_init_db(sndev); | ||
1513 | switchtec_ntb_init_msgs(sndev); | ||
1514 | |||
1515 | rc = switchtec_ntb_init_shared_mw(sndev); | ||
1516 | if (rc) | ||
1517 | goto deinit_crosslink; | ||
1518 | |||
1158 | rc = switchtec_ntb_init_db_msg_irq(sndev); | 1519 | rc = switchtec_ntb_init_db_msg_irq(sndev); |
1159 | if (rc) | 1520 | if (rc) |
1160 | goto deinit_shared_and_exit; | 1521 | goto deinit_shared_and_exit; |
1161 | 1522 | ||
1523 | /* | ||
1524 | * If this host crashed, the other host may think the link is | ||
1525 | * still up. Tell them to force it down (it will go back up | ||
1526 | * once we register the ntb device). | ||
1527 | */ | ||
1528 | switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_FORCE_DOWN); | ||
1529 | |||
1162 | rc = ntb_register_device(&sndev->ntb); | 1530 | rc = ntb_register_device(&sndev->ntb); |
1163 | if (rc) | 1531 | if (rc) |
1164 | goto deinit_and_exit; | 1532 | goto deinit_and_exit; |
1165 | 1533 | ||
1166 | stdev->sndev = sndev; | 1534 | stdev->sndev = sndev; |
1167 | stdev->link_notifier = switchtec_ntb_link_notification; | 1535 | stdev->link_notifier = switchtec_ntb_link_notification; |
1168 | dev_info(dev, "NTB device registered"); | 1536 | dev_info(dev, "NTB device registered\n"); |
1169 | 1537 | ||
1170 | return 0; | 1538 | return 0; |
1171 | 1539 | ||
@@ -1173,14 +1541,16 @@ deinit_and_exit: | |||
1173 | switchtec_ntb_deinit_db_msg_irq(sndev); | 1541 | switchtec_ntb_deinit_db_msg_irq(sndev); |
1174 | deinit_shared_and_exit: | 1542 | deinit_shared_and_exit: |
1175 | switchtec_ntb_deinit_shared_mw(sndev); | 1543 | switchtec_ntb_deinit_shared_mw(sndev); |
1544 | deinit_crosslink: | ||
1545 | switchtec_ntb_deinit_crosslink(sndev); | ||
1176 | free_and_exit: | 1546 | free_and_exit: |
1177 | kfree(sndev); | 1547 | kfree(sndev); |
1178 | dev_err(dev, "failed to register ntb device: %d", rc); | 1548 | dev_err(dev, "failed to register ntb device: %d\n", rc); |
1179 | return rc; | 1549 | return rc; |
1180 | } | 1550 | } |
1181 | 1551 | ||
1182 | void switchtec_ntb_remove(struct device *dev, | 1552 | static void switchtec_ntb_remove(struct device *dev, |
1183 | struct class_interface *class_intf) | 1553 | struct class_interface *class_intf) |
1184 | { | 1554 | { |
1185 | struct switchtec_dev *stdev = to_stdev(dev); | 1555 | struct switchtec_dev *stdev = to_stdev(dev); |
1186 | struct switchtec_ntb *sndev = stdev->sndev; | 1556 | struct switchtec_ntb *sndev = stdev->sndev; |
@@ -1193,8 +1563,9 @@ void switchtec_ntb_remove(struct device *dev, | |||
1193 | ntb_unregister_device(&sndev->ntb); | 1563 | ntb_unregister_device(&sndev->ntb); |
1194 | switchtec_ntb_deinit_db_msg_irq(sndev); | 1564 | switchtec_ntb_deinit_db_msg_irq(sndev); |
1195 | switchtec_ntb_deinit_shared_mw(sndev); | 1565 | switchtec_ntb_deinit_shared_mw(sndev); |
1566 | switchtec_ntb_deinit_crosslink(sndev); | ||
1196 | kfree(sndev); | 1567 | kfree(sndev); |
1197 | dev_info(dev, "ntb device unregistered"); | 1568 | dev_info(dev, "ntb device unregistered\n"); |
1198 | } | 1569 | } |
1199 | 1570 | ||
1200 | static struct class_interface switchtec_interface = { | 1571 | static struct class_interface switchtec_interface = { |
diff --git a/drivers/ntb/ntb.c b/drivers/ntb/ntb.c index 03b80d89b980..2581ab724c34 100644 --- a/drivers/ntb/ntb.c +++ b/drivers/ntb/ntb.c | |||
@@ -63,12 +63,11 @@ | |||
63 | #define DRIVER_NAME "ntb" | 63 | #define DRIVER_NAME "ntb" |
64 | #define DRIVER_DESCRIPTION "PCIe NTB Driver Framework" | 64 | #define DRIVER_DESCRIPTION "PCIe NTB Driver Framework" |
65 | 65 | ||
66 | #define DRIVER_LICENSE "Dual BSD/GPL" | ||
67 | #define DRIVER_VERSION "1.0" | 66 | #define DRIVER_VERSION "1.0" |
68 | #define DRIVER_RELDATE "24 March 2015" | 67 | #define DRIVER_RELDATE "24 March 2015" |
69 | #define DRIVER_AUTHOR "Allen Hubbe <Allen.Hubbe@emc.com>" | 68 | #define DRIVER_AUTHOR "Allen Hubbe <Allen.Hubbe@emc.com>" |
70 | 69 | ||
71 | MODULE_LICENSE(DRIVER_LICENSE); | 70 | MODULE_LICENSE("Dual BSD/GPL"); |
72 | MODULE_VERSION(DRIVER_VERSION); | 71 | MODULE_VERSION(DRIVER_VERSION); |
73 | MODULE_AUTHOR(DRIVER_AUTHOR); | 72 | MODULE_AUTHOR(DRIVER_AUTHOR); |
74 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION); | 73 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION); |
@@ -112,7 +111,6 @@ int ntb_register_device(struct ntb_dev *ntb) | |||
112 | 111 | ||
113 | init_completion(&ntb->released); | 112 | init_completion(&ntb->released); |
114 | 113 | ||
115 | memset(&ntb->dev, 0, sizeof(ntb->dev)); | ||
116 | ntb->dev.bus = &ntb_bus; | 114 | ntb->dev.bus = &ntb_bus; |
117 | ntb->dev.parent = &ntb->pdev->dev; | 115 | ntb->dev.parent = &ntb->pdev->dev; |
118 | ntb->dev.release = ntb_dev_release; | 116 | ntb->dev.release = ntb_dev_release; |
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 045e3dd4750e..9878c48826e3 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c | |||
@@ -1003,6 +1003,9 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt, | |||
1003 | mw_base = nt->mw_vec[mw_num].phys_addr; | 1003 | mw_base = nt->mw_vec[mw_num].phys_addr; |
1004 | mw_size = nt->mw_vec[mw_num].phys_size; | 1004 | mw_size = nt->mw_vec[mw_num].phys_size; |
1005 | 1005 | ||
1006 | if (max_mw_size && mw_size > max_mw_size) | ||
1007 | mw_size = max_mw_size; | ||
1008 | |||
1006 | tx_size = (unsigned int)mw_size / num_qps_mw; | 1009 | tx_size = (unsigned int)mw_size / num_qps_mw; |
1007 | qp_offset = tx_size * (qp_num / mw_count); | 1010 | qp_offset = tx_size * (qp_num / mw_count); |
1008 | 1011 | ||
diff --git a/drivers/ntb/test/ntb_perf.c b/drivers/ntb/test/ntb_perf.c index 427112cf101a..2a9d6b0d1f19 100644 --- a/drivers/ntb/test/ntb_perf.c +++ b/drivers/ntb/test/ntb_perf.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * GPL LICENSE SUMMARY | 5 | * GPL LICENSE SUMMARY |
6 | * | 6 | * |
7 | * Copyright(c) 2015 Intel Corporation. All rights reserved. | 7 | * Copyright(c) 2015 Intel Corporation. All rights reserved. |
8 | * Copyright(c) 2017 T-Platforms. All Rights Reserved. | ||
8 | * | 9 | * |
9 | * This program is free software; you can redistribute it and/or modify | 10 | * 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 | * it under the terms of version 2 of the GNU General Public License as |
@@ -13,6 +14,7 @@ | |||
13 | * BSD LICENSE | 14 | * BSD LICENSE |
14 | * | 15 | * |
15 | * Copyright(c) 2015 Intel Corporation. All rights reserved. | 16 | * Copyright(c) 2015 Intel Corporation. All rights reserved. |
17 | * Copyright(c) 2017 T-Platforms. All Rights Reserved. | ||
16 | * | 18 | * |
17 | * Redistribution and use in source and binary forms, with or without | 19 | * Redistribution and use in source and binary forms, with or without |
18 | * modification, are permitted provided that the following conditions | 20 | * modification, are permitted provided that the following conditions |
@@ -40,860 +42,1474 @@ | |||
40 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 42 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
41 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 43 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
42 | * | 44 | * |
43 | * PCIe NTB Perf Linux driver | 45 | * PCIe NTB Perf Linux driver |
46 | */ | ||
47 | |||
48 | /* | ||
49 | * How to use this tool, by example. | ||
50 | * | ||
51 | * Assuming $DBG_DIR is something like: | ||
52 | * '/sys/kernel/debug/ntb_perf/0000:00:03.0' | ||
53 | * Suppose aside from local device there is at least one remote device | ||
54 | * connected to NTB with index 0. | ||
55 | *----------------------------------------------------------------------------- | ||
56 | * Eg: install driver with specified chunk/total orders and dma-enabled flag | ||
57 | * | ||
58 | * root@self# insmod ntb_perf.ko chunk_order=19 total_order=28 use_dma | ||
59 | *----------------------------------------------------------------------------- | ||
60 | * Eg: check NTB ports (index) and MW mapping information | ||
61 | * | ||
62 | * root@self# cat $DBG_DIR/info | ||
63 | *----------------------------------------------------------------------------- | ||
64 | * Eg: start performance test with peer (index 0) and get the test metrics | ||
65 | * | ||
66 | * root@self# echo 0 > $DBG_DIR/run | ||
67 | * root@self# cat $DBG_DIR/run | ||
44 | */ | 68 | */ |
45 | 69 | ||
46 | #include <linux/init.h> | 70 | #include <linux/init.h> |
47 | #include <linux/kernel.h> | 71 | #include <linux/kernel.h> |
48 | #include <linux/module.h> | 72 | #include <linux/module.h> |
49 | #include <linux/kthread.h> | 73 | #include <linux/sched.h> |
50 | #include <linux/time.h> | 74 | #include <linux/wait.h> |
51 | #include <linux/timer.h> | ||
52 | #include <linux/dma-mapping.h> | 75 | #include <linux/dma-mapping.h> |
76 | #include <linux/dmaengine.h> | ||
53 | #include <linux/pci.h> | 77 | #include <linux/pci.h> |
78 | #include <linux/ktime.h> | ||
54 | #include <linux/slab.h> | 79 | #include <linux/slab.h> |
55 | #include <linux/spinlock.h> | ||
56 | #include <linux/debugfs.h> | ||
57 | #include <linux/dmaengine.h> | ||
58 | #include <linux/delay.h> | 80 | #include <linux/delay.h> |
59 | #include <linux/sizes.h> | 81 | #include <linux/sizes.h> |
82 | #include <linux/workqueue.h> | ||
83 | #include <linux/debugfs.h> | ||
84 | #include <linux/random.h> | ||
60 | #include <linux/ntb.h> | 85 | #include <linux/ntb.h> |
61 | #include <linux/mutex.h> | ||
62 | 86 | ||
63 | #define DRIVER_NAME "ntb_perf" | 87 | #define DRIVER_NAME "ntb_perf" |
64 | #define DRIVER_DESCRIPTION "PCIe NTB Performance Measurement Tool" | 88 | #define DRIVER_VERSION "2.0" |
65 | 89 | ||
66 | #define DRIVER_LICENSE "Dual BSD/GPL" | 90 | MODULE_LICENSE("Dual BSD/GPL"); |
67 | #define DRIVER_VERSION "1.0" | ||
68 | #define DRIVER_AUTHOR "Dave Jiang <dave.jiang@intel.com>" | ||
69 | |||
70 | #define PERF_LINK_DOWN_TIMEOUT 10 | ||
71 | #define PERF_VERSION 0xffff0001 | ||
72 | #define MAX_THREADS 32 | ||
73 | #define MAX_TEST_SIZE SZ_1M | ||
74 | #define MAX_SRCS 32 | ||
75 | #define DMA_OUT_RESOURCE_TO msecs_to_jiffies(50) | ||
76 | #define DMA_RETRIES 20 | ||
77 | #define SZ_4G (1ULL << 32) | ||
78 | #define MAX_SEG_ORDER 20 /* no larger than 1M for kmalloc buffer */ | ||
79 | #define PIDX NTB_DEF_PEER_IDX | ||
80 | |||
81 | MODULE_LICENSE(DRIVER_LICENSE); | ||
82 | MODULE_VERSION(DRIVER_VERSION); | 91 | MODULE_VERSION(DRIVER_VERSION); |
83 | MODULE_AUTHOR(DRIVER_AUTHOR); | 92 | MODULE_AUTHOR("Dave Jiang <dave.jiang@intel.com>"); |
84 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION); | 93 | MODULE_DESCRIPTION("PCIe NTB Performance Measurement Tool"); |
94 | |||
95 | #define MAX_THREADS_CNT 32 | ||
96 | #define DEF_THREADS_CNT 1 | ||
97 | #define MAX_CHUNK_SIZE SZ_1M | ||
98 | #define MAX_CHUNK_ORDER 20 /* no larger than 1M */ | ||
85 | 99 | ||
86 | static struct dentry *perf_debugfs_dir; | 100 | #define DMA_TRIES 100 |
101 | #define DMA_MDELAY 10 | ||
102 | |||
103 | #define MSG_TRIES 500 | ||
104 | #define MSG_UDELAY_LOW 1000 | ||
105 | #define MSG_UDELAY_HIGH 2000 | ||
106 | |||
107 | #define PERF_BUF_LEN 1024 | ||
87 | 108 | ||
88 | static unsigned long max_mw_size; | 109 | static unsigned long max_mw_size; |
89 | module_param(max_mw_size, ulong, 0644); | 110 | module_param(max_mw_size, ulong, 0644); |
90 | MODULE_PARM_DESC(max_mw_size, "Limit size of large memory windows"); | 111 | MODULE_PARM_DESC(max_mw_size, "Upper limit of memory window size"); |
91 | 112 | ||
92 | static unsigned int seg_order = 19; /* 512K */ | 113 | static unsigned char chunk_order = 19; /* 512K */ |
93 | module_param(seg_order, uint, 0644); | 114 | module_param(chunk_order, byte, 0644); |
94 | MODULE_PARM_DESC(seg_order, "size order [2^n] of buffer segment for testing"); | 115 | MODULE_PARM_DESC(chunk_order, "Data chunk order [2^n] to transfer"); |
95 | 116 | ||
96 | static unsigned int run_order = 32; /* 4G */ | 117 | static unsigned char total_order = 30; /* 1G */ |
97 | module_param(run_order, uint, 0644); | 118 | module_param(total_order, byte, 0644); |
98 | MODULE_PARM_DESC(run_order, "size order [2^n] of total data to transfer"); | 119 | MODULE_PARM_DESC(total_order, "Total data order [2^n] to transfer"); |
99 | 120 | ||
100 | static bool use_dma; /* default to 0 */ | 121 | static bool use_dma; /* default to 0 */ |
101 | module_param(use_dma, bool, 0644); | 122 | module_param(use_dma, bool, 0644); |
102 | MODULE_PARM_DESC(use_dma, "Using DMA engine to measure performance"); | 123 | MODULE_PARM_DESC(use_dma, "Use DMA engine to measure performance"); |
103 | 124 | ||
104 | static bool on_node = true; /* default to 1 */ | 125 | /*============================================================================== |
105 | module_param(on_node, bool, 0644); | 126 | * Perf driver data definition |
106 | MODULE_PARM_DESC(on_node, "Run threads only on NTB device node (default: true)"); | 127 | *============================================================================== |
107 | 128 | */ | |
108 | struct perf_mw { | 129 | |
109 | phys_addr_t phys_addr; | 130 | enum perf_cmd { |
110 | resource_size_t phys_size; | 131 | PERF_CMD_INVAL = -1,/* invalid spad command */ |
111 | void __iomem *vbase; | 132 | PERF_CMD_SSIZE = 0, /* send out buffer size */ |
112 | size_t xlat_size; | 133 | PERF_CMD_RSIZE = 1, /* recv in buffer size */ |
113 | size_t buf_size; | 134 | PERF_CMD_SXLAT = 2, /* send in buffer xlat */ |
114 | void *virt_addr; | 135 | PERF_CMD_RXLAT = 3, /* recv out buffer xlat */ |
115 | dma_addr_t dma_addr; | 136 | PERF_CMD_CLEAR = 4, /* clear allocated memory */ |
137 | PERF_STS_DONE = 5, /* init is done */ | ||
138 | PERF_STS_LNKUP = 6, /* link up state flag */ | ||
116 | }; | 139 | }; |
117 | 140 | ||
118 | struct perf_ctx; | 141 | struct perf_ctx; |
119 | 142 | ||
120 | struct pthr_ctx { | 143 | struct perf_peer { |
121 | struct task_struct *thread; | 144 | struct perf_ctx *perf; |
122 | struct perf_ctx *perf; | 145 | int pidx; |
123 | atomic_t dma_sync; | 146 | int gidx; |
124 | struct dma_chan *dma_chan; | 147 | |
125 | int dma_prep_err; | 148 | /* Outbound MW params */ |
126 | int src_idx; | 149 | u64 outbuf_xlat; |
127 | void *srcs[MAX_SRCS]; | 150 | resource_size_t outbuf_size; |
128 | wait_queue_head_t *wq; | 151 | void __iomem *outbuf; |
129 | int status; | 152 | |
130 | u64 copied; | 153 | /* Inbound MW params */ |
131 | u64 diff_us; | 154 | dma_addr_t inbuf_xlat; |
155 | resource_size_t inbuf_size; | ||
156 | void *inbuf; | ||
157 | |||
158 | /* NTB connection setup service */ | ||
159 | struct work_struct service; | ||
160 | unsigned long sts; | ||
132 | }; | 161 | }; |
162 | #define to_peer_service(__work) \ | ||
163 | container_of(__work, struct perf_peer, service) | ||
133 | 164 | ||
134 | struct perf_ctx { | 165 | struct perf_thread { |
135 | struct ntb_dev *ntb; | 166 | struct perf_ctx *perf; |
136 | spinlock_t db_lock; | 167 | int tidx; |
137 | struct perf_mw mw; | 168 | |
138 | bool link_is_up; | 169 | /* DMA-based test sync parameters */ |
139 | struct delayed_work link_work; | 170 | atomic_t dma_sync; |
140 | wait_queue_head_t link_wq; | 171 | wait_queue_head_t dma_wait; |
141 | u8 perf_threads; | 172 | struct dma_chan *dma_chan; |
142 | /* mutex ensures only one set of threads run at once */ | 173 | |
143 | struct mutex run_mutex; | 174 | /* Data source and measured statistics */ |
144 | struct pthr_ctx pthr_ctx[MAX_THREADS]; | 175 | void *src; |
145 | atomic_t tsync; | 176 | u64 copied; |
146 | atomic_t tdone; | 177 | ktime_t duration; |
178 | int status; | ||
179 | struct work_struct work; | ||
147 | }; | 180 | }; |
181 | #define to_thread_work(__work) \ | ||
182 | container_of(__work, struct perf_thread, work) | ||
148 | 183 | ||
149 | enum { | 184 | struct perf_ctx { |
150 | VERSION = 0, | 185 | struct ntb_dev *ntb; |
151 | MW_SZ_HIGH, | 186 | |
152 | MW_SZ_LOW, | 187 | /* Global device index and peers descriptors */ |
153 | MAX_SPAD | 188 | int gidx; |
189 | int pcnt; | ||
190 | struct perf_peer *peers; | ||
191 | |||
192 | /* Performance measuring work-threads interface */ | ||
193 | unsigned long busy_flag; | ||
194 | wait_queue_head_t twait; | ||
195 | atomic_t tsync; | ||
196 | u8 tcnt; | ||
197 | struct perf_peer *test_peer; | ||
198 | struct perf_thread threads[MAX_THREADS_CNT]; | ||
199 | |||
200 | /* Scratchpad/Message IO operations */ | ||
201 | int (*cmd_send)(struct perf_peer *peer, enum perf_cmd cmd, u64 data); | ||
202 | int (*cmd_recv)(struct perf_ctx *perf, int *pidx, enum perf_cmd *cmd, | ||
203 | u64 *data); | ||
204 | |||
205 | struct dentry *dbgfs_dir; | ||
154 | }; | 206 | }; |
155 | 207 | ||
208 | /* | ||
209 | * Scratchpads-base commands interface | ||
210 | */ | ||
211 | #define PERF_SPAD_CNT(_pcnt) \ | ||
212 | (3*((_pcnt) + 1)) | ||
213 | #define PERF_SPAD_CMD(_gidx) \ | ||
214 | (3*(_gidx)) | ||
215 | #define PERF_SPAD_LDATA(_gidx) \ | ||
216 | (3*(_gidx) + 1) | ||
217 | #define PERF_SPAD_HDATA(_gidx) \ | ||
218 | (3*(_gidx) + 2) | ||
219 | #define PERF_SPAD_NOTIFY(_gidx) \ | ||
220 | (BIT_ULL(_gidx)) | ||
221 | |||
222 | /* | ||
223 | * Messages-base commands interface | ||
224 | */ | ||
225 | #define PERF_MSG_CNT 3 | ||
226 | #define PERF_MSG_CMD 0 | ||
227 | #define PERF_MSG_LDATA 1 | ||
228 | #define PERF_MSG_HDATA 2 | ||
229 | |||
230 | /*============================================================================== | ||
231 | * Static data declarations | ||
232 | *============================================================================== | ||
233 | */ | ||
234 | |||
235 | static struct dentry *perf_dbgfs_topdir; | ||
236 | |||
237 | static struct workqueue_struct *perf_wq __read_mostly; | ||
238 | |||
239 | /*============================================================================== | ||
240 | * NTB cross-link commands execution service | ||
241 | *============================================================================== | ||
242 | */ | ||
243 | |||
244 | static void perf_terminate_test(struct perf_ctx *perf); | ||
245 | |||
246 | static inline bool perf_link_is_up(struct perf_peer *peer) | ||
247 | { | ||
248 | u64 link; | ||
249 | |||
250 | link = ntb_link_is_up(peer->perf->ntb, NULL, NULL); | ||
251 | return !!(link & BIT_ULL_MASK(peer->pidx)); | ||
252 | } | ||
253 | |||
254 | static int perf_spad_cmd_send(struct perf_peer *peer, enum perf_cmd cmd, | ||
255 | u64 data) | ||
256 | { | ||
257 | struct perf_ctx *perf = peer->perf; | ||
258 | int try; | ||
259 | u32 sts; | ||
260 | |||
261 | dev_dbg(&perf->ntb->dev, "CMD send: %d 0x%llx\n", cmd, data); | ||
262 | |||
263 | /* | ||
264 | * Perform predefined number of attempts before give up. | ||
265 | * We are sending the data to the port specific scratchpad, so | ||
266 | * to prevent a multi-port access race-condition. Additionally | ||
267 | * there is no need in local locking since only thread-safe | ||
268 | * service work is using this method. | ||
269 | */ | ||
270 | for (try = 0; try < MSG_TRIES; try++) { | ||
271 | if (!perf_link_is_up(peer)) | ||
272 | return -ENOLINK; | ||
273 | |||
274 | sts = ntb_peer_spad_read(perf->ntb, peer->pidx, | ||
275 | PERF_SPAD_CMD(perf->gidx)); | ||
276 | if (sts != PERF_CMD_INVAL) { | ||
277 | usleep_range(MSG_UDELAY_LOW, MSG_UDELAY_HIGH); | ||
278 | continue; | ||
279 | } | ||
280 | |||
281 | ntb_peer_spad_write(perf->ntb, peer->pidx, | ||
282 | PERF_SPAD_LDATA(perf->gidx), | ||
283 | lower_32_bits(data)); | ||
284 | ntb_peer_spad_write(perf->ntb, peer->pidx, | ||
285 | PERF_SPAD_HDATA(perf->gidx), | ||
286 | upper_32_bits(data)); | ||
287 | mmiowb(); | ||
288 | ntb_peer_spad_write(perf->ntb, peer->pidx, | ||
289 | PERF_SPAD_CMD(perf->gidx), | ||
290 | cmd); | ||
291 | mmiowb(); | ||
292 | ntb_peer_db_set(perf->ntb, PERF_SPAD_NOTIFY(peer->gidx)); | ||
293 | |||
294 | dev_dbg(&perf->ntb->dev, "DB ring peer %#llx\n", | ||
295 | PERF_SPAD_NOTIFY(peer->gidx)); | ||
296 | |||
297 | break; | ||
298 | } | ||
299 | |||
300 | return try < MSG_TRIES ? 0 : -EAGAIN; | ||
301 | } | ||
302 | |||
303 | static int perf_spad_cmd_recv(struct perf_ctx *perf, int *pidx, | ||
304 | enum perf_cmd *cmd, u64 *data) | ||
305 | { | ||
306 | struct perf_peer *peer; | ||
307 | u32 val; | ||
308 | |||
309 | ntb_db_clear(perf->ntb, PERF_SPAD_NOTIFY(perf->gidx)); | ||
310 | |||
311 | /* | ||
312 | * We start scanning all over, since cleared DB may have been set | ||
313 | * by any peer. Yes, it makes peer with smaller index being | ||
314 | * serviced with greater priority, but it's convenient for spad | ||
315 | * and message code unification and simplicity. | ||
316 | */ | ||
317 | for (*pidx = 0; *pidx < perf->pcnt; (*pidx)++) { | ||
318 | peer = &perf->peers[*pidx]; | ||
319 | |||
320 | if (!perf_link_is_up(peer)) | ||
321 | continue; | ||
322 | |||
323 | val = ntb_spad_read(perf->ntb, PERF_SPAD_CMD(peer->gidx)); | ||
324 | if (val == PERF_CMD_INVAL) | ||
325 | continue; | ||
326 | |||
327 | *cmd = val; | ||
328 | |||
329 | val = ntb_spad_read(perf->ntb, PERF_SPAD_LDATA(peer->gidx)); | ||
330 | *data = val; | ||
331 | |||
332 | val = ntb_spad_read(perf->ntb, PERF_SPAD_HDATA(peer->gidx)); | ||
333 | *data |= (u64)val << 32; | ||
334 | |||
335 | /* Next command can be retrieved from now */ | ||
336 | ntb_spad_write(perf->ntb, PERF_SPAD_CMD(peer->gidx), | ||
337 | PERF_CMD_INVAL); | ||
338 | |||
339 | dev_dbg(&perf->ntb->dev, "CMD recv: %d 0x%llx\n", *cmd, *data); | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | return -ENODATA; | ||
345 | } | ||
346 | |||
347 | static int perf_msg_cmd_send(struct perf_peer *peer, enum perf_cmd cmd, | ||
348 | u64 data) | ||
349 | { | ||
350 | struct perf_ctx *perf = peer->perf; | ||
351 | int try, ret; | ||
352 | u64 outbits; | ||
353 | |||
354 | dev_dbg(&perf->ntb->dev, "CMD send: %d 0x%llx\n", cmd, data); | ||
355 | |||
356 | /* | ||
357 | * Perform predefined number of attempts before give up. Message | ||
358 | * registers are free of race-condition problem when accessed | ||
359 | * from different ports, so we don't need splitting registers | ||
360 | * by global device index. We also won't have local locking, | ||
361 | * since the method is used from service work only. | ||
362 | */ | ||
363 | outbits = ntb_msg_outbits(perf->ntb); | ||
364 | for (try = 0; try < MSG_TRIES; try++) { | ||
365 | if (!perf_link_is_up(peer)) | ||
366 | return -ENOLINK; | ||
367 | |||
368 | ret = ntb_msg_clear_sts(perf->ntb, outbits); | ||
369 | if (ret) | ||
370 | return ret; | ||
371 | |||
372 | ntb_peer_msg_write(perf->ntb, peer->pidx, PERF_MSG_LDATA, | ||
373 | lower_32_bits(data)); | ||
374 | |||
375 | if (ntb_msg_read_sts(perf->ntb) & outbits) { | ||
376 | usleep_range(MSG_UDELAY_LOW, MSG_UDELAY_HIGH); | ||
377 | continue; | ||
378 | } | ||
379 | |||
380 | ntb_peer_msg_write(perf->ntb, peer->pidx, PERF_MSG_HDATA, | ||
381 | upper_32_bits(data)); | ||
382 | mmiowb(); | ||
383 | |||
384 | /* This call shall trigger peer message event */ | ||
385 | ntb_peer_msg_write(perf->ntb, peer->pidx, PERF_MSG_CMD, cmd); | ||
386 | |||
387 | break; | ||
388 | } | ||
389 | |||
390 | return try < MSG_TRIES ? 0 : -EAGAIN; | ||
391 | } | ||
392 | |||
393 | static int perf_msg_cmd_recv(struct perf_ctx *perf, int *pidx, | ||
394 | enum perf_cmd *cmd, u64 *data) | ||
395 | { | ||
396 | u64 inbits; | ||
397 | u32 val; | ||
398 | |||
399 | inbits = ntb_msg_inbits(perf->ntb); | ||
400 | |||
401 | if (hweight64(ntb_msg_read_sts(perf->ntb) & inbits) < 3) | ||
402 | return -ENODATA; | ||
403 | |||
404 | val = ntb_msg_read(perf->ntb, pidx, PERF_MSG_CMD); | ||
405 | *cmd = val; | ||
406 | |||
407 | val = ntb_msg_read(perf->ntb, pidx, PERF_MSG_LDATA); | ||
408 | *data = val; | ||
409 | |||
410 | val = ntb_msg_read(perf->ntb, pidx, PERF_MSG_HDATA); | ||
411 | *data |= (u64)val << 32; | ||
412 | |||
413 | /* Next command can be retrieved from now */ | ||
414 | ntb_msg_clear_sts(perf->ntb, inbits); | ||
415 | |||
416 | dev_dbg(&perf->ntb->dev, "CMD recv: %d 0x%llx\n", *cmd, *data); | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static int perf_cmd_send(struct perf_peer *peer, enum perf_cmd cmd, u64 data) | ||
422 | { | ||
423 | struct perf_ctx *perf = peer->perf; | ||
424 | |||
425 | if (cmd == PERF_CMD_SSIZE || cmd == PERF_CMD_SXLAT) | ||
426 | return perf->cmd_send(peer, cmd, data); | ||
427 | |||
428 | dev_err(&perf->ntb->dev, "Send invalid command\n"); | ||
429 | return -EINVAL; | ||
430 | } | ||
431 | |||
432 | static int perf_cmd_exec(struct perf_peer *peer, enum perf_cmd cmd) | ||
433 | { | ||
434 | switch (cmd) { | ||
435 | case PERF_CMD_SSIZE: | ||
436 | case PERF_CMD_RSIZE: | ||
437 | case PERF_CMD_SXLAT: | ||
438 | case PERF_CMD_RXLAT: | ||
439 | case PERF_CMD_CLEAR: | ||
440 | break; | ||
441 | default: | ||
442 | dev_err(&peer->perf->ntb->dev, "Exec invalid command\n"); | ||
443 | return -EINVAL; | ||
444 | } | ||
445 | |||
446 | /* No need of memory barrier, since bit ops have invernal lock */ | ||
447 | set_bit(cmd, &peer->sts); | ||
448 | |||
449 | dev_dbg(&peer->perf->ntb->dev, "CMD exec: %d\n", cmd); | ||
450 | |||
451 | (void)queue_work(system_highpri_wq, &peer->service); | ||
452 | |||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | static int perf_cmd_recv(struct perf_ctx *perf) | ||
457 | { | ||
458 | struct perf_peer *peer; | ||
459 | int ret, pidx, cmd; | ||
460 | u64 data; | ||
461 | |||
462 | while (!(ret = perf->cmd_recv(perf, &pidx, &cmd, &data))) { | ||
463 | peer = &perf->peers[pidx]; | ||
464 | |||
465 | switch (cmd) { | ||
466 | case PERF_CMD_SSIZE: | ||
467 | peer->inbuf_size = data; | ||
468 | return perf_cmd_exec(peer, PERF_CMD_RSIZE); | ||
469 | case PERF_CMD_SXLAT: | ||
470 | peer->outbuf_xlat = data; | ||
471 | return perf_cmd_exec(peer, PERF_CMD_RXLAT); | ||
472 | default: | ||
473 | dev_err(&perf->ntb->dev, "Recv invalid command\n"); | ||
474 | return -EINVAL; | ||
475 | } | ||
476 | } | ||
477 | |||
478 | /* Return 0 if no data left to process, otherwise an error */ | ||
479 | return ret == -ENODATA ? 0 : ret; | ||
480 | } | ||
481 | |||
156 | static void perf_link_event(void *ctx) | 482 | static void perf_link_event(void *ctx) |
157 | { | 483 | { |
158 | struct perf_ctx *perf = ctx; | 484 | struct perf_ctx *perf = ctx; |
485 | struct perf_peer *peer; | ||
486 | bool lnk_up; | ||
487 | int pidx; | ||
159 | 488 | ||
160 | if (ntb_link_is_up(perf->ntb, NULL, NULL) == 1) { | 489 | for (pidx = 0; pidx < perf->pcnt; pidx++) { |
161 | schedule_delayed_work(&perf->link_work, 2*HZ); | 490 | peer = &perf->peers[pidx]; |
162 | } else { | ||
163 | dev_dbg(&perf->ntb->pdev->dev, "link down\n"); | ||
164 | 491 | ||
165 | if (!perf->link_is_up) | 492 | lnk_up = perf_link_is_up(peer); |
166 | cancel_delayed_work_sync(&perf->link_work); | ||
167 | 493 | ||
168 | perf->link_is_up = false; | 494 | if (lnk_up && |
495 | !test_and_set_bit(PERF_STS_LNKUP, &peer->sts)) { | ||
496 | perf_cmd_exec(peer, PERF_CMD_SSIZE); | ||
497 | } else if (!lnk_up && | ||
498 | test_and_clear_bit(PERF_STS_LNKUP, &peer->sts)) { | ||
499 | perf_cmd_exec(peer, PERF_CMD_CLEAR); | ||
500 | } | ||
169 | } | 501 | } |
170 | } | 502 | } |
171 | 503 | ||
172 | static void perf_db_event(void *ctx, int vec) | 504 | static void perf_db_event(void *ctx, int vec) |
173 | { | 505 | { |
174 | struct perf_ctx *perf = ctx; | 506 | struct perf_ctx *perf = ctx; |
175 | u64 db_bits, db_mask; | ||
176 | 507 | ||
177 | db_mask = ntb_db_vector_mask(perf->ntb, vec); | 508 | dev_dbg(&perf->ntb->dev, "DB vec %d mask %#llx bits %#llx\n", vec, |
178 | db_bits = ntb_db_read(perf->ntb); | 509 | ntb_db_vector_mask(perf->ntb, vec), ntb_db_read(perf->ntb)); |
510 | |||
511 | /* Just receive all available commands */ | ||
512 | (void)perf_cmd_recv(perf); | ||
513 | } | ||
514 | |||
515 | static void perf_msg_event(void *ctx) | ||
516 | { | ||
517 | struct perf_ctx *perf = ctx; | ||
518 | |||
519 | dev_dbg(&perf->ntb->dev, "Msg status bits %#llx\n", | ||
520 | ntb_msg_read_sts(perf->ntb)); | ||
179 | 521 | ||
180 | dev_dbg(&perf->ntb->dev, "doorbell vec %d mask %#llx bits %#llx\n", | 522 | /* Messages are only sent one-by-one */ |
181 | vec, db_mask, db_bits); | 523 | (void)perf_cmd_recv(perf); |
182 | } | 524 | } |
183 | 525 | ||
184 | static const struct ntb_ctx_ops perf_ops = { | 526 | static const struct ntb_ctx_ops perf_ops = { |
185 | .link_event = perf_link_event, | 527 | .link_event = perf_link_event, |
186 | .db_event = perf_db_event, | 528 | .db_event = perf_db_event, |
529 | .msg_event = perf_msg_event | ||
187 | }; | 530 | }; |
188 | 531 | ||
189 | static void perf_copy_callback(void *data) | 532 | static void perf_free_outbuf(struct perf_peer *peer) |
533 | { | ||
534 | (void)ntb_peer_mw_clear_trans(peer->perf->ntb, peer->pidx, peer->gidx); | ||
535 | } | ||
536 | |||
537 | static int perf_setup_outbuf(struct perf_peer *peer) | ||
190 | { | 538 | { |
191 | struct pthr_ctx *pctx = data; | 539 | struct perf_ctx *perf = peer->perf; |
540 | int ret; | ||
541 | |||
542 | /* Outbuf size can be unaligned due to custom max_mw_size */ | ||
543 | ret = ntb_peer_mw_set_trans(perf->ntb, peer->pidx, peer->gidx, | ||
544 | peer->outbuf_xlat, peer->outbuf_size); | ||
545 | if (ret) { | ||
546 | dev_err(&perf->ntb->dev, "Failed to set outbuf translation\n"); | ||
547 | return ret; | ||
548 | } | ||
192 | 549 | ||
193 | atomic_dec(&pctx->dma_sync); | 550 | /* Initialization is finally done */ |
551 | set_bit(PERF_STS_DONE, &peer->sts); | ||
552 | |||
553 | return 0; | ||
194 | } | 554 | } |
195 | 555 | ||
196 | static ssize_t perf_copy(struct pthr_ctx *pctx, char __iomem *dst, | 556 | static void perf_free_inbuf(struct perf_peer *peer) |
197 | char *src, size_t size) | ||
198 | { | 557 | { |
199 | struct perf_ctx *perf = pctx->perf; | 558 | if (!peer->inbuf) |
200 | struct dma_async_tx_descriptor *txd; | 559 | return; |
201 | struct dma_chan *chan = pctx->dma_chan; | ||
202 | struct dma_device *device; | ||
203 | struct dmaengine_unmap_data *unmap; | ||
204 | dma_cookie_t cookie; | ||
205 | size_t src_off, dst_off; | ||
206 | struct perf_mw *mw = &perf->mw; | ||
207 | void __iomem *vbase; | ||
208 | void __iomem *dst_vaddr; | ||
209 | dma_addr_t dst_phys; | ||
210 | int retries = 0; | ||
211 | 560 | ||
212 | if (!use_dma) { | 561 | (void)ntb_mw_clear_trans(peer->perf->ntb, peer->pidx, peer->gidx); |
213 | memcpy_toio(dst, src, size); | 562 | dma_free_coherent(&peer->perf->ntb->dev, peer->inbuf_size, |
214 | return size; | 563 | peer->inbuf, peer->inbuf_xlat); |
564 | peer->inbuf = NULL; | ||
565 | } | ||
566 | |||
567 | static int perf_setup_inbuf(struct perf_peer *peer) | ||
568 | { | ||
569 | resource_size_t xlat_align, size_align, size_max; | ||
570 | struct perf_ctx *perf = peer->perf; | ||
571 | int ret; | ||
572 | |||
573 | /* Get inbound MW parameters */ | ||
574 | ret = ntb_mw_get_align(perf->ntb, peer->pidx, perf->gidx, | ||
575 | &xlat_align, &size_align, &size_max); | ||
576 | if (ret) { | ||
577 | dev_err(&perf->ntb->dev, "Couldn't get inbuf restrictions\n"); | ||
578 | return ret; | ||
215 | } | 579 | } |
216 | 580 | ||
217 | if (!chan) { | 581 | if (peer->inbuf_size > size_max) { |
218 | dev_err(&perf->ntb->dev, "DMA engine does not exist\n"); | 582 | dev_err(&perf->ntb->dev, "Too big inbuf size %pa > %pa\n", |
583 | &peer->inbuf_size, &size_max); | ||
219 | return -EINVAL; | 584 | return -EINVAL; |
220 | } | 585 | } |
221 | 586 | ||
222 | device = chan->device; | 587 | peer->inbuf_size = round_up(peer->inbuf_size, size_align); |
223 | src_off = (uintptr_t)src & ~PAGE_MASK; | ||
224 | dst_off = (uintptr_t __force)dst & ~PAGE_MASK; | ||
225 | |||
226 | if (!is_dma_copy_aligned(device, src_off, dst_off, size)) | ||
227 | return -ENODEV; | ||
228 | 588 | ||
229 | vbase = mw->vbase; | 589 | perf_free_inbuf(peer); |
230 | dst_vaddr = dst; | ||
231 | dst_phys = mw->phys_addr + (dst_vaddr - vbase); | ||
232 | 590 | ||
233 | unmap = dmaengine_get_unmap_data(device->dev, 1, GFP_NOWAIT); | 591 | peer->inbuf = dma_alloc_coherent(&perf->ntb->dev, peer->inbuf_size, |
234 | if (!unmap) | 592 | &peer->inbuf_xlat, GFP_KERNEL); |
593 | if (!peer->inbuf) { | ||
594 | dev_err(&perf->ntb->dev, "Failed to alloc inbuf of %pa\n", | ||
595 | &peer->inbuf_size); | ||
235 | return -ENOMEM; | 596 | return -ENOMEM; |
597 | } | ||
598 | if (!IS_ALIGNED(peer->inbuf_xlat, xlat_align)) { | ||
599 | dev_err(&perf->ntb->dev, "Unaligned inbuf allocated\n"); | ||
600 | goto err_free_inbuf; | ||
601 | } | ||
236 | 602 | ||
237 | unmap->len = size; | 603 | ret = ntb_mw_set_trans(perf->ntb, peer->pidx, peer->gidx, |
238 | unmap->addr[0] = dma_map_page(device->dev, virt_to_page(src), | 604 | peer->inbuf_xlat, peer->inbuf_size); |
239 | src_off, size, DMA_TO_DEVICE); | 605 | if (ret) { |
240 | if (dma_mapping_error(device->dev, unmap->addr[0])) | 606 | dev_err(&perf->ntb->dev, "Failed to set inbuf translation\n"); |
241 | goto err_get_unmap; | 607 | goto err_free_inbuf; |
608 | } | ||
242 | 609 | ||
243 | unmap->to_cnt = 1; | 610 | /* |
611 | * We submit inbuf xlat transmission cmd for execution here to follow | ||
612 | * the code architecture, even though this method is called from service | ||
613 | * work itself so the command will be executed right after it returns. | ||
614 | */ | ||
615 | (void)perf_cmd_exec(peer, PERF_CMD_SXLAT); | ||
244 | 616 | ||
245 | do { | 617 | return 0; |
246 | txd = device->device_prep_dma_memcpy(chan, dst_phys, | ||
247 | unmap->addr[0], | ||
248 | size, DMA_PREP_INTERRUPT); | ||
249 | if (!txd) { | ||
250 | set_current_state(TASK_INTERRUPTIBLE); | ||
251 | schedule_timeout(DMA_OUT_RESOURCE_TO); | ||
252 | } | ||
253 | } while (!txd && (++retries < DMA_RETRIES)); | ||
254 | 618 | ||
255 | if (!txd) { | 619 | err_free_inbuf: |
256 | pctx->dma_prep_err++; | 620 | perf_free_inbuf(peer); |
257 | goto err_get_unmap; | ||
258 | } | ||
259 | 621 | ||
260 | txd->callback = perf_copy_callback; | 622 | return ret; |
261 | txd->callback_param = pctx; | 623 | } |
262 | dma_set_unmap(txd, unmap); | ||
263 | 624 | ||
264 | cookie = dmaengine_submit(txd); | 625 | static void perf_service_work(struct work_struct *work) |
265 | if (dma_submit_error(cookie)) | 626 | { |
266 | goto err_set_unmap; | 627 | struct perf_peer *peer = to_peer_service(work); |
267 | 628 | ||
268 | dmaengine_unmap_put(unmap); | 629 | if (test_and_clear_bit(PERF_CMD_SSIZE, &peer->sts)) |
630 | perf_cmd_send(peer, PERF_CMD_SSIZE, peer->outbuf_size); | ||
269 | 631 | ||
270 | atomic_inc(&pctx->dma_sync); | 632 | if (test_and_clear_bit(PERF_CMD_RSIZE, &peer->sts)) |
271 | dma_async_issue_pending(chan); | 633 | perf_setup_inbuf(peer); |
272 | 634 | ||
273 | return size; | 635 | if (test_and_clear_bit(PERF_CMD_SXLAT, &peer->sts)) |
636 | perf_cmd_send(peer, PERF_CMD_SXLAT, peer->inbuf_xlat); | ||
274 | 637 | ||
275 | err_set_unmap: | 638 | if (test_and_clear_bit(PERF_CMD_RXLAT, &peer->sts)) |
276 | dmaengine_unmap_put(unmap); | 639 | perf_setup_outbuf(peer); |
277 | err_get_unmap: | ||
278 | dmaengine_unmap_put(unmap); | ||
279 | return 0; | ||
280 | } | ||
281 | 640 | ||
282 | static int perf_move_data(struct pthr_ctx *pctx, char __iomem *dst, char *src, | 641 | if (test_and_clear_bit(PERF_CMD_CLEAR, &peer->sts)) { |
283 | u64 buf_size, u64 win_size, u64 total) | 642 | clear_bit(PERF_STS_DONE, &peer->sts); |
284 | { | 643 | if (test_bit(0, &peer->perf->busy_flag) && |
285 | int chunks, total_chunks, i; | 644 | peer == peer->perf->test_peer) { |
286 | int copied_chunks = 0; | 645 | dev_warn(&peer->perf->ntb->dev, |
287 | u64 copied = 0, result; | 646 | "Freeing while test on-fly\n"); |
288 | char __iomem *tmp = dst; | 647 | perf_terminate_test(peer->perf); |
289 | u64 perf, diff_us; | ||
290 | ktime_t kstart, kstop, kdiff; | ||
291 | unsigned long last_sleep = jiffies; | ||
292 | |||
293 | chunks = div64_u64(win_size, buf_size); | ||
294 | total_chunks = div64_u64(total, buf_size); | ||
295 | kstart = ktime_get(); | ||
296 | |||
297 | for (i = 0; i < total_chunks; i++) { | ||
298 | result = perf_copy(pctx, tmp, src, buf_size); | ||
299 | copied += result; | ||
300 | copied_chunks++; | ||
301 | if (copied_chunks == chunks) { | ||
302 | tmp = dst; | ||
303 | copied_chunks = 0; | ||
304 | } else | ||
305 | tmp += buf_size; | ||
306 | |||
307 | /* Probably should schedule every 5s to prevent soft hang. */ | ||
308 | if (unlikely((jiffies - last_sleep) > 5 * HZ)) { | ||
309 | last_sleep = jiffies; | ||
310 | set_current_state(TASK_INTERRUPTIBLE); | ||
311 | schedule_timeout(1); | ||
312 | } | 648 | } |
649 | perf_free_outbuf(peer); | ||
650 | perf_free_inbuf(peer); | ||
651 | } | ||
652 | } | ||
653 | |||
654 | static int perf_init_service(struct perf_ctx *perf) | ||
655 | { | ||
656 | u64 mask; | ||
313 | 657 | ||
314 | if (unlikely(kthread_should_stop())) | 658 | if (ntb_peer_mw_count(perf->ntb) < perf->pcnt + 1) { |
315 | break; | 659 | dev_err(&perf->ntb->dev, "Not enough memory windows\n"); |
660 | return -EINVAL; | ||
316 | } | 661 | } |
317 | 662 | ||
318 | if (use_dma) { | 663 | if (ntb_msg_count(perf->ntb) >= PERF_MSG_CNT) { |
319 | pr_debug("%s: All DMA descriptors submitted\n", current->comm); | 664 | perf->cmd_send = perf_msg_cmd_send; |
320 | while (atomic_read(&pctx->dma_sync) != 0) { | 665 | perf->cmd_recv = perf_msg_cmd_recv; |
321 | if (kthread_should_stop()) | 666 | |
322 | break; | 667 | dev_dbg(&perf->ntb->dev, "Message service initialized\n"); |
323 | msleep(20); | 668 | |
324 | } | 669 | return 0; |
325 | } | 670 | } |
326 | 671 | ||
327 | kstop = ktime_get(); | 672 | dev_dbg(&perf->ntb->dev, "Message service unsupported\n"); |
328 | kdiff = ktime_sub(kstop, kstart); | ||
329 | diff_us = ktime_to_us(kdiff); | ||
330 | 673 | ||
331 | pr_debug("%s: copied %llu bytes\n", current->comm, copied); | 674 | mask = GENMASK_ULL(perf->pcnt, 0); |
675 | if (ntb_spad_count(perf->ntb) >= PERF_SPAD_CNT(perf->pcnt) && | ||
676 | (ntb_db_valid_mask(perf->ntb) & mask) == mask) { | ||
677 | perf->cmd_send = perf_spad_cmd_send; | ||
678 | perf->cmd_recv = perf_spad_cmd_recv; | ||
332 | 679 | ||
333 | pr_debug("%s: lasted %llu usecs\n", current->comm, diff_us); | 680 | dev_dbg(&perf->ntb->dev, "Scratchpad service initialized\n"); |
334 | 681 | ||
335 | perf = div64_u64(copied, diff_us); | 682 | return 0; |
683 | } | ||
336 | 684 | ||
337 | pr_debug("%s: MBytes/s: %llu\n", current->comm, perf); | 685 | dev_dbg(&perf->ntb->dev, "Scratchpad service unsupported\n"); |
338 | 686 | ||
339 | pctx->copied = copied; | 687 | dev_err(&perf->ntb->dev, "Command services unsupported\n"); |
340 | pctx->diff_us = diff_us; | ||
341 | 688 | ||
342 | return 0; | 689 | return -EINVAL; |
343 | } | 690 | } |
344 | 691 | ||
345 | static bool perf_dma_filter_fn(struct dma_chan *chan, void *node) | 692 | static int perf_enable_service(struct perf_ctx *perf) |
346 | { | 693 | { |
347 | /* Is the channel required to be on the same node as the device? */ | 694 | u64 mask, incmd_bit; |
348 | if (!on_node) | 695 | int ret, sidx, scnt; |
349 | return true; | ||
350 | 696 | ||
351 | return dev_to_node(&chan->dev->device) == (int)(unsigned long)node; | 697 | mask = ntb_db_valid_mask(perf->ntb); |
352 | } | 698 | (void)ntb_db_set_mask(perf->ntb, mask); |
353 | 699 | ||
354 | static int ntb_perf_thread(void *data) | 700 | ret = ntb_set_ctx(perf->ntb, perf, &perf_ops); |
355 | { | 701 | if (ret) |
356 | struct pthr_ctx *pctx = data; | 702 | return ret; |
357 | struct perf_ctx *perf = pctx->perf; | ||
358 | struct pci_dev *pdev = perf->ntb->pdev; | ||
359 | struct perf_mw *mw = &perf->mw; | ||
360 | char __iomem *dst; | ||
361 | u64 win_size, buf_size, total; | ||
362 | void *src; | ||
363 | int rc, node, i; | ||
364 | struct dma_chan *dma_chan = NULL; | ||
365 | 703 | ||
366 | pr_debug("kthread %s starting...\n", current->comm); | 704 | if (perf->cmd_send == perf_msg_cmd_send) { |
705 | u64 inbits, outbits; | ||
367 | 706 | ||
368 | node = on_node ? dev_to_node(&pdev->dev) : NUMA_NO_NODE; | 707 | inbits = ntb_msg_inbits(perf->ntb); |
708 | outbits = ntb_msg_outbits(perf->ntb); | ||
709 | (void)ntb_msg_set_mask(perf->ntb, inbits | outbits); | ||
369 | 710 | ||
370 | if (use_dma && !pctx->dma_chan) { | 711 | incmd_bit = BIT_ULL(__ffs64(inbits)); |
371 | dma_cap_mask_t dma_mask; | 712 | ret = ntb_msg_clear_mask(perf->ntb, incmd_bit); |
372 | 713 | ||
373 | dma_cap_zero(dma_mask); | 714 | dev_dbg(&perf->ntb->dev, "MSG sts unmasked %#llx\n", incmd_bit); |
374 | dma_cap_set(DMA_MEMCPY, dma_mask); | 715 | } else { |
375 | dma_chan = dma_request_channel(dma_mask, perf_dma_filter_fn, | 716 | scnt = ntb_spad_count(perf->ntb); |
376 | (void *)(unsigned long)node); | 717 | for (sidx = 0; sidx < scnt; sidx++) |
377 | if (!dma_chan) { | 718 | ntb_spad_write(perf->ntb, sidx, PERF_CMD_INVAL); |
378 | pr_warn("%s: cannot acquire DMA channel, quitting\n", | 719 | incmd_bit = PERF_SPAD_NOTIFY(perf->gidx); |
379 | current->comm); | 720 | ret = ntb_db_clear_mask(perf->ntb, incmd_bit); |
380 | return -ENODEV; | 721 | |
381 | } | 722 | dev_dbg(&perf->ntb->dev, "DB bits unmasked %#llx\n", incmd_bit); |
382 | pctx->dma_chan = dma_chan; | 723 | } |
724 | if (ret) { | ||
725 | ntb_clear_ctx(perf->ntb); | ||
726 | return ret; | ||
383 | } | 727 | } |
384 | 728 | ||
385 | for (i = 0; i < MAX_SRCS; i++) { | 729 | ntb_link_enable(perf->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); |
386 | pctx->srcs[i] = kmalloc_node(MAX_TEST_SIZE, GFP_KERNEL, node); | 730 | /* Might be not necessary */ |
387 | if (!pctx->srcs[i]) { | 731 | ntb_link_event(perf->ntb); |
388 | rc = -ENOMEM; | 732 | |
389 | goto err; | 733 | return 0; |
390 | } | 734 | } |
735 | |||
736 | static void perf_disable_service(struct perf_ctx *perf) | ||
737 | { | ||
738 | int pidx; | ||
739 | |||
740 | ntb_link_disable(perf->ntb); | ||
741 | |||
742 | if (perf->cmd_send == perf_msg_cmd_send) { | ||
743 | u64 inbits; | ||
744 | |||
745 | inbits = ntb_msg_inbits(perf->ntb); | ||
746 | (void)ntb_msg_set_mask(perf->ntb, inbits); | ||
747 | } else { | ||
748 | (void)ntb_db_set_mask(perf->ntb, PERF_SPAD_NOTIFY(perf->gidx)); | ||
391 | } | 749 | } |
392 | 750 | ||
393 | win_size = mw->phys_size; | 751 | ntb_clear_ctx(perf->ntb); |
394 | buf_size = 1ULL << seg_order; | ||
395 | total = 1ULL << run_order; | ||
396 | 752 | ||
397 | if (buf_size > MAX_TEST_SIZE) | 753 | for (pidx = 0; pidx < perf->pcnt; pidx++) |
398 | buf_size = MAX_TEST_SIZE; | 754 | perf_cmd_exec(&perf->peers[pidx], PERF_CMD_CLEAR); |
399 | 755 | ||
400 | dst = (char __iomem *)mw->vbase; | 756 | for (pidx = 0; pidx < perf->pcnt; pidx++) |
757 | flush_work(&perf->peers[pidx].service); | ||
758 | } | ||
401 | 759 | ||
402 | atomic_inc(&perf->tsync); | 760 | /*============================================================================== |
403 | while (atomic_read(&perf->tsync) != perf->perf_threads) | 761 | * Performance measuring work-thread |
404 | schedule(); | 762 | *============================================================================== |
763 | */ | ||
405 | 764 | ||
406 | src = pctx->srcs[pctx->src_idx]; | 765 | static void perf_dma_copy_callback(void *data) |
407 | pctx->src_idx = (pctx->src_idx + 1) & (MAX_SRCS - 1); | 766 | { |
767 | struct perf_thread *pthr = data; | ||
408 | 768 | ||
409 | rc = perf_move_data(pctx, dst, src, buf_size, win_size, total); | 769 | atomic_dec(&pthr->dma_sync); |
770 | wake_up(&pthr->dma_wait); | ||
771 | } | ||
410 | 772 | ||
411 | atomic_dec(&perf->tsync); | 773 | static int perf_copy_chunk(struct perf_thread *pthr, |
774 | void __iomem *dst, void *src, size_t len) | ||
775 | { | ||
776 | struct dma_async_tx_descriptor *tx; | ||
777 | struct dmaengine_unmap_data *unmap; | ||
778 | struct device *dma_dev; | ||
779 | int try = 0, ret = 0; | ||
412 | 780 | ||
413 | if (rc < 0) { | 781 | if (!use_dma) { |
414 | pr_err("%s: failed\n", current->comm); | 782 | memcpy_toio(dst, src, len); |
415 | rc = -ENXIO; | 783 | goto ret_check_tsync; |
416 | goto err; | ||
417 | } | 784 | } |
418 | 785 | ||
419 | for (i = 0; i < MAX_SRCS; i++) { | 786 | dma_dev = pthr->dma_chan->device->dev; |
420 | kfree(pctx->srcs[i]); | 787 | |
421 | pctx->srcs[i] = NULL; | 788 | if (!is_dma_copy_aligned(pthr->dma_chan->device, offset_in_page(src), |
789 | offset_in_page(dst), len)) | ||
790 | return -EIO; | ||
791 | |||
792 | unmap = dmaengine_get_unmap_data(dma_dev, 2, GFP_NOWAIT); | ||
793 | if (!unmap) | ||
794 | return -ENOMEM; | ||
795 | |||
796 | unmap->len = len; | ||
797 | unmap->addr[0] = dma_map_page(dma_dev, virt_to_page(src), | ||
798 | offset_in_page(src), len, DMA_TO_DEVICE); | ||
799 | if (dma_mapping_error(dma_dev, unmap->addr[0])) { | ||
800 | ret = -EIO; | ||
801 | goto err_free_resource; | ||
422 | } | 802 | } |
803 | unmap->to_cnt = 1; | ||
423 | 804 | ||
424 | atomic_inc(&perf->tdone); | 805 | unmap->addr[1] = dma_map_page(dma_dev, virt_to_page(dst), |
425 | wake_up(pctx->wq); | 806 | offset_in_page(dst), len, DMA_FROM_DEVICE); |
426 | rc = 0; | 807 | if (dma_mapping_error(dma_dev, unmap->addr[1])) { |
427 | goto done; | 808 | ret = -EIO; |
809 | goto err_free_resource; | ||
810 | } | ||
811 | unmap->from_cnt = 1; | ||
428 | 812 | ||
429 | err: | 813 | do { |
430 | for (i = 0; i < MAX_SRCS; i++) { | 814 | tx = dmaengine_prep_dma_memcpy(pthr->dma_chan, unmap->addr[1], |
431 | kfree(pctx->srcs[i]); | 815 | unmap->addr[0], len, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
432 | pctx->srcs[i] = NULL; | 816 | if (!tx) |
817 | msleep(DMA_MDELAY); | ||
818 | } while (!tx && (try++ < DMA_TRIES)); | ||
819 | |||
820 | if (!tx) { | ||
821 | ret = -EIO; | ||
822 | goto err_free_resource; | ||
433 | } | 823 | } |
434 | 824 | ||
435 | if (dma_chan) { | 825 | tx->callback = perf_dma_copy_callback; |
436 | dma_release_channel(dma_chan); | 826 | tx->callback_param = pthr; |
437 | pctx->dma_chan = NULL; | 827 | dma_set_unmap(tx, unmap); |
828 | |||
829 | ret = dma_submit_error(dmaengine_submit(tx)); | ||
830 | if (ret) { | ||
831 | dmaengine_unmap_put(unmap); | ||
832 | goto err_free_resource; | ||
438 | } | 833 | } |
439 | 834 | ||
440 | done: | 835 | dmaengine_unmap_put(unmap); |
441 | /* Wait until we are told to stop */ | 836 | |
442 | for (;;) { | 837 | atomic_inc(&pthr->dma_sync); |
443 | set_current_state(TASK_INTERRUPTIBLE); | 838 | dma_async_issue_pending(pthr->dma_chan); |
444 | if (kthread_should_stop()) | 839 | |
445 | break; | 840 | ret_check_tsync: |
446 | schedule(); | 841 | return likely(atomic_read(&pthr->perf->tsync) > 0) ? 0 : -EINTR; |
842 | |||
843 | err_free_resource: | ||
844 | dmaengine_unmap_put(unmap); | ||
845 | |||
846 | return ret; | ||
847 | } | ||
848 | |||
849 | static bool perf_dma_filter(struct dma_chan *chan, void *data) | ||
850 | { | ||
851 | struct perf_ctx *perf = data; | ||
852 | int node; | ||
853 | |||
854 | node = dev_to_node(&perf->ntb->dev); | ||
855 | |||
856 | return node == NUMA_NO_NODE || node == dev_to_node(chan->device->dev); | ||
857 | } | ||
858 | |||
859 | static int perf_init_test(struct perf_thread *pthr) | ||
860 | { | ||
861 | struct perf_ctx *perf = pthr->perf; | ||
862 | dma_cap_mask_t dma_mask; | ||
863 | |||
864 | pthr->src = kmalloc_node(perf->test_peer->outbuf_size, GFP_KERNEL, | ||
865 | dev_to_node(&perf->ntb->dev)); | ||
866 | if (!pthr->src) | ||
867 | return -ENOMEM; | ||
868 | |||
869 | get_random_bytes(pthr->src, perf->test_peer->outbuf_size); | ||
870 | |||
871 | if (!use_dma) | ||
872 | return 0; | ||
873 | |||
874 | dma_cap_zero(dma_mask); | ||
875 | dma_cap_set(DMA_MEMCPY, dma_mask); | ||
876 | pthr->dma_chan = dma_request_channel(dma_mask, perf_dma_filter, perf); | ||
877 | if (!pthr->dma_chan) { | ||
878 | dev_err(&perf->ntb->dev, "%d: Failed to get DMA channel\n", | ||
879 | pthr->tidx); | ||
880 | atomic_dec(&perf->tsync); | ||
881 | wake_up(&perf->twait); | ||
882 | kfree(pthr->src); | ||
883 | return -ENODEV; | ||
447 | } | 884 | } |
448 | __set_current_state(TASK_RUNNING); | ||
449 | 885 | ||
450 | return rc; | 886 | atomic_set(&pthr->dma_sync, 0); |
887 | |||
888 | return 0; | ||
451 | } | 889 | } |
452 | 890 | ||
453 | static void perf_free_mw(struct perf_ctx *perf) | 891 | static int perf_run_test(struct perf_thread *pthr) |
454 | { | 892 | { |
455 | struct perf_mw *mw = &perf->mw; | 893 | struct perf_peer *peer = pthr->perf->test_peer; |
456 | struct pci_dev *pdev = perf->ntb->pdev; | 894 | struct perf_ctx *perf = pthr->perf; |
895 | void __iomem *flt_dst, *bnd_dst; | ||
896 | u64 total_size, chunk_size; | ||
897 | void *flt_src; | ||
898 | int ret = 0; | ||
899 | |||
900 | total_size = 1ULL << total_order; | ||
901 | chunk_size = 1ULL << chunk_order; | ||
902 | chunk_size = min_t(u64, peer->outbuf_size, chunk_size); | ||
903 | |||
904 | flt_src = pthr->src; | ||
905 | bnd_dst = peer->outbuf + peer->outbuf_size; | ||
906 | flt_dst = peer->outbuf; | ||
907 | |||
908 | pthr->duration = ktime_get(); | ||
909 | |||
910 | /* Copied field is cleared on test launch stage */ | ||
911 | while (pthr->copied < total_size) { | ||
912 | ret = perf_copy_chunk(pthr, flt_dst, flt_src, chunk_size); | ||
913 | if (ret) { | ||
914 | dev_err(&perf->ntb->dev, "%d: Got error %d on test\n", | ||
915 | pthr->tidx, ret); | ||
916 | return ret; | ||
917 | } | ||
457 | 918 | ||
458 | if (!mw->virt_addr) | 919 | pthr->copied += chunk_size; |
459 | return; | 920 | |
921 | flt_dst += chunk_size; | ||
922 | flt_src += chunk_size; | ||
923 | if (flt_dst >= bnd_dst || flt_dst < peer->outbuf) { | ||
924 | flt_dst = peer->outbuf; | ||
925 | flt_src = pthr->src; | ||
926 | } | ||
460 | 927 | ||
461 | ntb_mw_clear_trans(perf->ntb, PIDX, 0); | 928 | /* Give up CPU to give a chance for other threads to use it */ |
462 | dma_free_coherent(&pdev->dev, mw->buf_size, | 929 | schedule(); |
463 | mw->virt_addr, mw->dma_addr); | 930 | } |
464 | mw->xlat_size = 0; | 931 | |
465 | mw->buf_size = 0; | 932 | return 0; |
466 | mw->virt_addr = NULL; | ||
467 | } | 933 | } |
468 | 934 | ||
469 | static int perf_set_mw(struct perf_ctx *perf, resource_size_t size) | 935 | static int perf_sync_test(struct perf_thread *pthr) |
470 | { | 936 | { |
471 | struct perf_mw *mw = &perf->mw; | 937 | struct perf_ctx *perf = pthr->perf; |
472 | size_t xlat_size, buf_size; | ||
473 | resource_size_t xlat_align; | ||
474 | resource_size_t xlat_align_size; | ||
475 | int rc; | ||
476 | 938 | ||
477 | if (!size) | 939 | if (!use_dma) |
478 | return -EINVAL; | 940 | goto no_dma_ret; |
479 | 941 | ||
480 | rc = ntb_mw_get_align(perf->ntb, PIDX, 0, &xlat_align, | 942 | wait_event(pthr->dma_wait, |
481 | &xlat_align_size, NULL); | 943 | (atomic_read(&pthr->dma_sync) == 0 || |
482 | if (rc) | 944 | atomic_read(&perf->tsync) < 0)); |
483 | return rc; | ||
484 | 945 | ||
485 | xlat_size = round_up(size, xlat_align_size); | 946 | if (atomic_read(&perf->tsync) < 0) |
486 | buf_size = round_up(size, xlat_align); | 947 | return -EINTR; |
487 | 948 | ||
488 | if (mw->xlat_size == xlat_size) | 949 | no_dma_ret: |
489 | return 0; | 950 | pthr->duration = ktime_sub(ktime_get(), pthr->duration); |
490 | 951 | ||
491 | if (mw->buf_size) | 952 | dev_dbg(&perf->ntb->dev, "%d: copied %llu bytes\n", |
492 | perf_free_mw(perf); | 953 | pthr->tidx, pthr->copied); |
493 | 954 | ||
494 | mw->xlat_size = xlat_size; | 955 | dev_dbg(&perf->ntb->dev, "%d: lasted %llu usecs\n", |
495 | mw->buf_size = buf_size; | 956 | pthr->tidx, ktime_to_us(pthr->duration)); |
957 | |||
958 | dev_dbg(&perf->ntb->dev, "%d: %llu MBytes/s\n", pthr->tidx, | ||
959 | div64_u64(pthr->copied, ktime_to_us(pthr->duration))); | ||
960 | |||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | static void perf_clear_test(struct perf_thread *pthr) | ||
965 | { | ||
966 | struct perf_ctx *perf = pthr->perf; | ||
967 | |||
968 | if (!use_dma) | ||
969 | goto no_dma_notify; | ||
970 | |||
971 | /* | ||
972 | * If test finished without errors, termination isn't needed. | ||
973 | * We call it anyway just to be sure of the transfers completion. | ||
974 | */ | ||
975 | (void)dmaengine_terminate_sync(pthr->dma_chan); | ||
976 | |||
977 | dma_release_channel(pthr->dma_chan); | ||
978 | |||
979 | no_dma_notify: | ||
980 | atomic_dec(&perf->tsync); | ||
981 | wake_up(&perf->twait); | ||
982 | kfree(pthr->src); | ||
983 | } | ||
496 | 984 | ||
497 | mw->virt_addr = dma_alloc_coherent(&perf->ntb->pdev->dev, buf_size, | 985 | static void perf_thread_work(struct work_struct *work) |
498 | &mw->dma_addr, GFP_KERNEL); | 986 | { |
499 | if (!mw->virt_addr) { | 987 | struct perf_thread *pthr = to_thread_work(work); |
500 | mw->xlat_size = 0; | 988 | int ret; |
501 | mw->buf_size = 0; | 989 | |
990 | /* | ||
991 | * Perform stages in compliance with use_dma flag value. | ||
992 | * Test status is changed only if error happened, otherwise | ||
993 | * status -ENODATA is kept while test is on-fly. Results | ||
994 | * synchronization is performed only if test fininshed | ||
995 | * without an error or interruption. | ||
996 | */ | ||
997 | ret = perf_init_test(pthr); | ||
998 | if (ret) { | ||
999 | pthr->status = ret; | ||
1000 | return; | ||
502 | } | 1001 | } |
503 | 1002 | ||
504 | rc = ntb_mw_set_trans(perf->ntb, PIDX, 0, mw->dma_addr, mw->xlat_size); | 1003 | ret = perf_run_test(pthr); |
505 | if (rc) { | 1004 | if (ret) { |
506 | dev_err(&perf->ntb->dev, "Unable to set mw0 translation\n"); | 1005 | pthr->status = ret; |
507 | perf_free_mw(perf); | 1006 | goto err_clear_test; |
508 | return -EIO; | ||
509 | } | 1007 | } |
510 | 1008 | ||
511 | return 0; | 1009 | pthr->status = perf_sync_test(pthr); |
1010 | |||
1011 | err_clear_test: | ||
1012 | perf_clear_test(pthr); | ||
512 | } | 1013 | } |
513 | 1014 | ||
514 | static void perf_link_work(struct work_struct *work) | 1015 | static int perf_set_tcnt(struct perf_ctx *perf, u8 tcnt) |
515 | { | 1016 | { |
516 | struct perf_ctx *perf = | 1017 | if (tcnt == 0 || tcnt > MAX_THREADS_CNT) |
517 | container_of(work, struct perf_ctx, link_work.work); | 1018 | return -EINVAL; |
518 | struct ntb_dev *ndev = perf->ntb; | ||
519 | struct pci_dev *pdev = ndev->pdev; | ||
520 | u32 val; | ||
521 | u64 size; | ||
522 | int rc; | ||
523 | 1019 | ||
524 | dev_dbg(&perf->ntb->pdev->dev, "%s called\n", __func__); | 1020 | if (test_and_set_bit_lock(0, &perf->busy_flag)) |
1021 | return -EBUSY; | ||
1022 | |||
1023 | perf->tcnt = tcnt; | ||
1024 | |||
1025 | clear_bit_unlock(0, &perf->busy_flag); | ||
525 | 1026 | ||
526 | size = perf->mw.phys_size; | 1027 | return 0; |
1028 | } | ||
527 | 1029 | ||
528 | if (max_mw_size && size > max_mw_size) | 1030 | static void perf_terminate_test(struct perf_ctx *perf) |
529 | size = max_mw_size; | 1031 | { |
1032 | int tidx; | ||
530 | 1033 | ||
531 | ntb_peer_spad_write(ndev, PIDX, MW_SZ_HIGH, upper_32_bits(size)); | 1034 | atomic_set(&perf->tsync, -1); |
532 | ntb_peer_spad_write(ndev, PIDX, MW_SZ_LOW, lower_32_bits(size)); | 1035 | wake_up(&perf->twait); |
533 | ntb_peer_spad_write(ndev, PIDX, VERSION, PERF_VERSION); | ||
534 | 1036 | ||
535 | /* now read what peer wrote */ | 1037 | for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) { |
536 | val = ntb_spad_read(ndev, VERSION); | 1038 | wake_up(&perf->threads[tidx].dma_wait); |
537 | if (val != PERF_VERSION) { | 1039 | cancel_work_sync(&perf->threads[tidx].work); |
538 | dev_dbg(&pdev->dev, "Remote version = %#x\n", val); | ||
539 | goto out; | ||
540 | } | 1040 | } |
1041 | } | ||
1042 | |||
1043 | static int perf_submit_test(struct perf_peer *peer) | ||
1044 | { | ||
1045 | struct perf_ctx *perf = peer->perf; | ||
1046 | struct perf_thread *pthr; | ||
1047 | int tidx, ret; | ||
541 | 1048 | ||
542 | val = ntb_spad_read(ndev, MW_SZ_HIGH); | 1049 | if (!test_bit(PERF_STS_DONE, &peer->sts)) |
543 | size = (u64)val << 32; | 1050 | return -ENOLINK; |
544 | 1051 | ||
545 | val = ntb_spad_read(ndev, MW_SZ_LOW); | 1052 | if (test_and_set_bit_lock(0, &perf->busy_flag)) |
546 | size |= val; | 1053 | return -EBUSY; |
547 | 1054 | ||
548 | dev_dbg(&pdev->dev, "Remote MW size = %#llx\n", size); | 1055 | perf->test_peer = peer; |
1056 | atomic_set(&perf->tsync, perf->tcnt); | ||
549 | 1057 | ||
550 | rc = perf_set_mw(perf, size); | 1058 | for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) { |
551 | if (rc) | 1059 | pthr = &perf->threads[tidx]; |
552 | goto out1; | ||
553 | 1060 | ||
554 | perf->link_is_up = true; | 1061 | pthr->status = -ENODATA; |
555 | wake_up(&perf->link_wq); | 1062 | pthr->copied = 0; |
1063 | pthr->duration = ktime_set(0, 0); | ||
1064 | if (tidx < perf->tcnt) | ||
1065 | (void)queue_work(perf_wq, &pthr->work); | ||
1066 | } | ||
556 | 1067 | ||
557 | return; | 1068 | ret = wait_event_interruptible(perf->twait, |
1069 | atomic_read(&perf->tsync) <= 0); | ||
1070 | if (ret == -ERESTARTSYS) { | ||
1071 | perf_terminate_test(perf); | ||
1072 | ret = -EINTR; | ||
1073 | } | ||
558 | 1074 | ||
559 | out1: | 1075 | clear_bit_unlock(0, &perf->busy_flag); |
560 | perf_free_mw(perf); | ||
561 | 1076 | ||
562 | out: | 1077 | return ret; |
563 | if (ntb_link_is_up(ndev, NULL, NULL) == 1) | ||
564 | schedule_delayed_work(&perf->link_work, | ||
565 | msecs_to_jiffies(PERF_LINK_DOWN_TIMEOUT)); | ||
566 | } | 1078 | } |
567 | 1079 | ||
568 | static int perf_setup_mw(struct ntb_dev *ntb, struct perf_ctx *perf) | 1080 | static int perf_read_stats(struct perf_ctx *perf, char *buf, |
1081 | size_t size, ssize_t *pos) | ||
569 | { | 1082 | { |
570 | struct perf_mw *mw; | 1083 | struct perf_thread *pthr; |
571 | int rc; | 1084 | int tidx; |
1085 | |||
1086 | if (test_and_set_bit_lock(0, &perf->busy_flag)) | ||
1087 | return -EBUSY; | ||
572 | 1088 | ||
573 | mw = &perf->mw; | 1089 | (*pos) += scnprintf(buf + *pos, size - *pos, |
1090 | " Peer %d test statistics:\n", perf->test_peer->pidx); | ||
574 | 1091 | ||
575 | rc = ntb_peer_mw_get_addr(ntb, 0, &mw->phys_addr, &mw->phys_size); | 1092 | for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) { |
576 | if (rc) | 1093 | pthr = &perf->threads[tidx]; |
577 | return rc; | ||
578 | 1094 | ||
579 | perf->mw.vbase = ioremap_wc(mw->phys_addr, mw->phys_size); | 1095 | if (pthr->status == -ENODATA) |
580 | if (!mw->vbase) | 1096 | continue; |
581 | return -ENOMEM; | 1097 | |
1098 | if (pthr->status) { | ||
1099 | (*pos) += scnprintf(buf + *pos, size - *pos, | ||
1100 | "%d: error status %d\n", tidx, pthr->status); | ||
1101 | continue; | ||
1102 | } | ||
1103 | |||
1104 | (*pos) += scnprintf(buf + *pos, size - *pos, | ||
1105 | "%d: copied %llu bytes in %llu usecs, %llu MBytes/s\n", | ||
1106 | tidx, pthr->copied, ktime_to_us(pthr->duration), | ||
1107 | div64_u64(pthr->copied, ktime_to_us(pthr->duration))); | ||
1108 | } | ||
1109 | |||
1110 | clear_bit_unlock(0, &perf->busy_flag); | ||
582 | 1111 | ||
583 | return 0; | 1112 | return 0; |
584 | } | 1113 | } |
585 | 1114 | ||
586 | static ssize_t debugfs_run_read(struct file *filp, char __user *ubuf, | 1115 | static void perf_init_threads(struct perf_ctx *perf) |
587 | size_t count, loff_t *offp) | ||
588 | { | 1116 | { |
589 | struct perf_ctx *perf = filp->private_data; | 1117 | struct perf_thread *pthr; |
1118 | int tidx; | ||
1119 | |||
1120 | perf->tcnt = DEF_THREADS_CNT; | ||
1121 | perf->test_peer = &perf->peers[0]; | ||
1122 | init_waitqueue_head(&perf->twait); | ||
1123 | |||
1124 | for (tidx = 0; tidx < MAX_THREADS_CNT; tidx++) { | ||
1125 | pthr = &perf->threads[tidx]; | ||
1126 | |||
1127 | pthr->perf = perf; | ||
1128 | pthr->tidx = tidx; | ||
1129 | pthr->status = -ENODATA; | ||
1130 | init_waitqueue_head(&pthr->dma_wait); | ||
1131 | INIT_WORK(&pthr->work, perf_thread_work); | ||
1132 | } | ||
1133 | } | ||
1134 | |||
1135 | static void perf_clear_threads(struct perf_ctx *perf) | ||
1136 | { | ||
1137 | perf_terminate_test(perf); | ||
1138 | } | ||
1139 | |||
1140 | /*============================================================================== | ||
1141 | * DebugFS nodes | ||
1142 | *============================================================================== | ||
1143 | */ | ||
1144 | |||
1145 | static ssize_t perf_dbgfs_read_info(struct file *filep, char __user *ubuf, | ||
1146 | size_t size, loff_t *offp) | ||
1147 | { | ||
1148 | struct perf_ctx *perf = filep->private_data; | ||
1149 | struct perf_peer *peer; | ||
1150 | size_t buf_size; | ||
1151 | ssize_t pos = 0; | ||
1152 | int ret, pidx; | ||
590 | char *buf; | 1153 | char *buf; |
591 | ssize_t ret, out_off = 0; | ||
592 | struct pthr_ctx *pctx; | ||
593 | int i; | ||
594 | u64 rate; | ||
595 | 1154 | ||
596 | if (!perf) | 1155 | buf_size = min_t(size_t, size, 0x1000U); |
597 | return 0; | ||
598 | 1156 | ||
599 | buf = kmalloc(1024, GFP_KERNEL); | 1157 | buf = kmalloc(buf_size, GFP_KERNEL); |
600 | if (!buf) | 1158 | if (!buf) |
601 | return -ENOMEM; | 1159 | return -ENOMEM; |
602 | 1160 | ||
603 | if (mutex_is_locked(&perf->run_mutex)) { | 1161 | pos += scnprintf(buf + pos, buf_size - pos, |
604 | out_off = scnprintf(buf, 64, "running\n"); | 1162 | " Performance measuring tool info:\n\n"); |
605 | goto read_from_buf; | 1163 | |
1164 | pos += scnprintf(buf + pos, buf_size - pos, | ||
1165 | "Local port %d, Global index %d\n", ntb_port_number(perf->ntb), | ||
1166 | perf->gidx); | ||
1167 | pos += scnprintf(buf + pos, buf_size - pos, "Test status: "); | ||
1168 | if (test_bit(0, &perf->busy_flag)) { | ||
1169 | pos += scnprintf(buf + pos, buf_size - pos, | ||
1170 | "on-fly with port %d (%d)\n", | ||
1171 | ntb_peer_port_number(perf->ntb, perf->test_peer->pidx), | ||
1172 | perf->test_peer->pidx); | ||
1173 | } else { | ||
1174 | pos += scnprintf(buf + pos, buf_size - pos, "idle\n"); | ||
606 | } | 1175 | } |
607 | 1176 | ||
608 | for (i = 0; i < MAX_THREADS; i++) { | 1177 | for (pidx = 0; pidx < perf->pcnt; pidx++) { |
609 | pctx = &perf->pthr_ctx[i]; | 1178 | peer = &perf->peers[pidx]; |
1179 | |||
1180 | pos += scnprintf(buf + pos, buf_size - pos, | ||
1181 | "Port %d (%d), Global index %d:\n", | ||
1182 | ntb_peer_port_number(perf->ntb, peer->pidx), peer->pidx, | ||
1183 | peer->gidx); | ||
1184 | |||
1185 | pos += scnprintf(buf + pos, buf_size - pos, | ||
1186 | "\tLink status: %s\n", | ||
1187 | test_bit(PERF_STS_LNKUP, &peer->sts) ? "up" : "down"); | ||
1188 | |||
1189 | pos += scnprintf(buf + pos, buf_size - pos, | ||
1190 | "\tOut buffer addr 0x%pK\n", peer->outbuf); | ||
610 | 1191 | ||
611 | if (pctx->status == -ENODATA) | 1192 | pos += scnprintf(buf + pos, buf_size - pos, |
612 | break; | 1193 | "\tOut buffer size %pa\n", &peer->outbuf_size); |
613 | 1194 | ||
614 | if (pctx->status) { | 1195 | pos += scnprintf(buf + pos, buf_size - pos, |
615 | out_off += scnprintf(buf + out_off, 1024 - out_off, | 1196 | "\tOut buffer xlat 0x%016llx[p]\n", peer->outbuf_xlat); |
616 | "%d: error %d\n", i, | 1197 | |
617 | pctx->status); | 1198 | if (!peer->inbuf) { |
1199 | pos += scnprintf(buf + pos, buf_size - pos, | ||
1200 | "\tIn buffer addr: unallocated\n"); | ||
618 | continue; | 1201 | continue; |
619 | } | 1202 | } |
620 | 1203 | ||
621 | rate = div64_u64(pctx->copied, pctx->diff_us); | 1204 | pos += scnprintf(buf + pos, buf_size - pos, |
622 | out_off += scnprintf(buf + out_off, 1024 - out_off, | 1205 | "\tIn buffer addr 0x%pK\n", peer->inbuf); |
623 | "%d: copied %llu bytes in %llu usecs, %llu MBytes/s\n", | 1206 | |
624 | i, pctx->copied, pctx->diff_us, rate); | 1207 | pos += scnprintf(buf + pos, buf_size - pos, |
1208 | "\tIn buffer size %pa\n", &peer->inbuf_size); | ||
1209 | |||
1210 | pos += scnprintf(buf + pos, buf_size - pos, | ||
1211 | "\tIn buffer xlat %pad[p]\n", &peer->inbuf_xlat); | ||
625 | } | 1212 | } |
626 | 1213 | ||
627 | read_from_buf: | 1214 | ret = simple_read_from_buffer(ubuf, size, offp, buf, pos); |
628 | ret = simple_read_from_buffer(ubuf, count, offp, buf, out_off); | ||
629 | kfree(buf); | 1215 | kfree(buf); |
630 | 1216 | ||
631 | return ret; | 1217 | return ret; |
632 | } | 1218 | } |
633 | 1219 | ||
634 | static void threads_cleanup(struct perf_ctx *perf) | 1220 | static const struct file_operations perf_dbgfs_info = { |
1221 | .open = simple_open, | ||
1222 | .read = perf_dbgfs_read_info | ||
1223 | }; | ||
1224 | |||
1225 | static ssize_t perf_dbgfs_read_run(struct file *filep, char __user *ubuf, | ||
1226 | size_t size, loff_t *offp) | ||
635 | { | 1227 | { |
636 | struct pthr_ctx *pctx; | 1228 | struct perf_ctx *perf = filep->private_data; |
637 | int i; | 1229 | ssize_t ret, pos = 0; |
1230 | char *buf; | ||
638 | 1231 | ||
639 | for (i = 0; i < MAX_THREADS; i++) { | 1232 | buf = kmalloc(PERF_BUF_LEN, GFP_KERNEL); |
640 | pctx = &perf->pthr_ctx[i]; | 1233 | if (!buf) |
641 | if (pctx->thread) { | 1234 | return -ENOMEM; |
642 | pctx->status = kthread_stop(pctx->thread); | ||
643 | pctx->thread = NULL; | ||
644 | } | ||
645 | } | ||
646 | } | ||
647 | 1235 | ||
648 | static void perf_clear_thread_status(struct perf_ctx *perf) | 1236 | ret = perf_read_stats(perf, buf, PERF_BUF_LEN, &pos); |
649 | { | 1237 | if (ret) |
650 | int i; | 1238 | goto err_free; |
1239 | |||
1240 | ret = simple_read_from_buffer(ubuf, size, offp, buf, pos); | ||
1241 | err_free: | ||
1242 | kfree(buf); | ||
651 | 1243 | ||
652 | for (i = 0; i < MAX_THREADS; i++) | 1244 | return ret; |
653 | perf->pthr_ctx[i].status = -ENODATA; | ||
654 | } | 1245 | } |
655 | 1246 | ||
656 | static ssize_t debugfs_run_write(struct file *filp, const char __user *ubuf, | 1247 | static ssize_t perf_dbgfs_write_run(struct file *filep, const char __user *ubuf, |
657 | size_t count, loff_t *offp) | 1248 | size_t size, loff_t *offp) |
658 | { | 1249 | { |
659 | struct perf_ctx *perf = filp->private_data; | 1250 | struct perf_ctx *perf = filep->private_data; |
660 | int node, i; | 1251 | struct perf_peer *peer; |
661 | DECLARE_WAIT_QUEUE_HEAD(wq); | 1252 | int pidx, ret; |
662 | 1253 | ||
663 | if (wait_event_interruptible(perf->link_wq, perf->link_is_up)) | 1254 | ret = kstrtoint_from_user(ubuf, size, 0, &pidx); |
664 | return -ENOLINK; | 1255 | if (ret) |
1256 | return ret; | ||
665 | 1257 | ||
666 | if (perf->perf_threads == 0) | 1258 | if (pidx < 0 || pidx >= perf->pcnt) |
667 | return -EINVAL; | 1259 | return -EINVAL; |
668 | 1260 | ||
669 | if (!mutex_trylock(&perf->run_mutex)) | 1261 | peer = &perf->peers[pidx]; |
670 | return -EBUSY; | ||
671 | 1262 | ||
672 | perf_clear_thread_status(perf); | 1263 | ret = perf_submit_test(peer); |
1264 | if (ret) | ||
1265 | return ret; | ||
673 | 1266 | ||
674 | if (perf->perf_threads > MAX_THREADS) { | 1267 | return size; |
675 | perf->perf_threads = MAX_THREADS; | 1268 | } |
676 | pr_info("Reset total threads to: %u\n", MAX_THREADS); | ||
677 | } | ||
678 | 1269 | ||
679 | /* no greater than 1M */ | 1270 | static const struct file_operations perf_dbgfs_run = { |
680 | if (seg_order > MAX_SEG_ORDER) { | 1271 | .open = simple_open, |
681 | seg_order = MAX_SEG_ORDER; | 1272 | .read = perf_dbgfs_read_run, |
682 | pr_info("Fix seg_order to %u\n", seg_order); | 1273 | .write = perf_dbgfs_write_run |
683 | } | 1274 | }; |
684 | 1275 | ||
685 | if (run_order < seg_order) { | 1276 | static ssize_t perf_dbgfs_read_tcnt(struct file *filep, char __user *ubuf, |
686 | run_order = seg_order; | 1277 | size_t size, loff_t *offp) |
687 | pr_info("Fix run_order to %u\n", run_order); | 1278 | { |
688 | } | 1279 | struct perf_ctx *perf = filep->private_data; |
1280 | char buf[8]; | ||
1281 | ssize_t pos; | ||
689 | 1282 | ||
690 | node = on_node ? dev_to_node(&perf->ntb->pdev->dev) | 1283 | pos = scnprintf(buf, sizeof(buf), "%hhu\n", perf->tcnt); |
691 | : NUMA_NO_NODE; | ||
692 | atomic_set(&perf->tdone, 0); | ||
693 | 1284 | ||
694 | /* launch kernel thread */ | 1285 | return simple_read_from_buffer(ubuf, size, offp, buf, pos); |
695 | for (i = 0; i < perf->perf_threads; i++) { | 1286 | } |
696 | struct pthr_ctx *pctx; | ||
697 | 1287 | ||
698 | pctx = &perf->pthr_ctx[i]; | 1288 | static ssize_t perf_dbgfs_write_tcnt(struct file *filep, |
699 | atomic_set(&pctx->dma_sync, 0); | 1289 | const char __user *ubuf, |
700 | pctx->perf = perf; | 1290 | size_t size, loff_t *offp) |
701 | pctx->wq = &wq; | 1291 | { |
702 | pctx->thread = | 1292 | struct perf_ctx *perf = filep->private_data; |
703 | kthread_create_on_node(ntb_perf_thread, | 1293 | int ret; |
704 | (void *)pctx, | 1294 | u8 val; |
705 | node, "ntb_perf %d", i); | ||
706 | if (IS_ERR(pctx->thread)) { | ||
707 | pctx->thread = NULL; | ||
708 | goto err; | ||
709 | } else { | ||
710 | wake_up_process(pctx->thread); | ||
711 | } | ||
712 | } | ||
713 | 1295 | ||
714 | wait_event_interruptible(wq, | 1296 | ret = kstrtou8_from_user(ubuf, size, 0, &val); |
715 | atomic_read(&perf->tdone) == perf->perf_threads); | 1297 | if (ret) |
1298 | return ret; | ||
716 | 1299 | ||
717 | threads_cleanup(perf); | 1300 | ret = perf_set_tcnt(perf, val); |
718 | mutex_unlock(&perf->run_mutex); | 1301 | if (ret) |
719 | return count; | 1302 | return ret; |
720 | 1303 | ||
721 | err: | 1304 | return size; |
722 | threads_cleanup(perf); | ||
723 | mutex_unlock(&perf->run_mutex); | ||
724 | return -ENXIO; | ||
725 | } | 1305 | } |
726 | 1306 | ||
727 | static const struct file_operations ntb_perf_debugfs_run = { | 1307 | static const struct file_operations perf_dbgfs_tcnt = { |
728 | .owner = THIS_MODULE, | ||
729 | .open = simple_open, | 1308 | .open = simple_open, |
730 | .read = debugfs_run_read, | 1309 | .read = perf_dbgfs_read_tcnt, |
731 | .write = debugfs_run_write, | 1310 | .write = perf_dbgfs_write_tcnt |
732 | }; | 1311 | }; |
733 | 1312 | ||
734 | static int perf_debugfs_setup(struct perf_ctx *perf) | 1313 | static void perf_setup_dbgfs(struct perf_ctx *perf) |
735 | { | 1314 | { |
736 | struct pci_dev *pdev = perf->ntb->pdev; | 1315 | struct pci_dev *pdev = perf->ntb->pdev; |
737 | struct dentry *debugfs_node_dir; | ||
738 | struct dentry *debugfs_run; | ||
739 | struct dentry *debugfs_threads; | ||
740 | struct dentry *debugfs_seg_order; | ||
741 | struct dentry *debugfs_run_order; | ||
742 | struct dentry *debugfs_use_dma; | ||
743 | struct dentry *debugfs_on_node; | ||
744 | |||
745 | if (!debugfs_initialized()) | ||
746 | return -ENODEV; | ||
747 | 1316 | ||
748 | /* Assumpion: only one NTB device in the system */ | 1317 | perf->dbgfs_dir = debugfs_create_dir(pci_name(pdev), perf_dbgfs_topdir); |
749 | if (!perf_debugfs_dir) { | 1318 | if (!perf->dbgfs_dir) { |
750 | perf_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); | 1319 | dev_warn(&perf->ntb->dev, "DebugFS unsupported\n"); |
751 | if (!perf_debugfs_dir) | 1320 | return; |
752 | return -ENODEV; | 1321 | } |
753 | } | 1322 | |
754 | 1323 | debugfs_create_file("info", 0600, perf->dbgfs_dir, perf, | |
755 | debugfs_node_dir = debugfs_create_dir(pci_name(pdev), | 1324 | &perf_dbgfs_info); |
756 | perf_debugfs_dir); | ||
757 | if (!debugfs_node_dir) | ||
758 | goto err; | ||
759 | |||
760 | debugfs_run = debugfs_create_file("run", S_IRUSR | S_IWUSR, | ||
761 | debugfs_node_dir, perf, | ||
762 | &ntb_perf_debugfs_run); | ||
763 | if (!debugfs_run) | ||
764 | goto err; | ||
765 | |||
766 | debugfs_threads = debugfs_create_u8("threads", S_IRUSR | S_IWUSR, | ||
767 | debugfs_node_dir, | ||
768 | &perf->perf_threads); | ||
769 | if (!debugfs_threads) | ||
770 | goto err; | ||
771 | |||
772 | debugfs_seg_order = debugfs_create_u32("seg_order", 0600, | ||
773 | debugfs_node_dir, | ||
774 | &seg_order); | ||
775 | if (!debugfs_seg_order) | ||
776 | goto err; | ||
777 | |||
778 | debugfs_run_order = debugfs_create_u32("run_order", 0600, | ||
779 | debugfs_node_dir, | ||
780 | &run_order); | ||
781 | if (!debugfs_run_order) | ||
782 | goto err; | ||
783 | |||
784 | debugfs_use_dma = debugfs_create_bool("use_dma", 0600, | ||
785 | debugfs_node_dir, | ||
786 | &use_dma); | ||
787 | if (!debugfs_use_dma) | ||
788 | goto err; | ||
789 | |||
790 | debugfs_on_node = debugfs_create_bool("on_node", 0600, | ||
791 | debugfs_node_dir, | ||
792 | &on_node); | ||
793 | if (!debugfs_on_node) | ||
794 | goto err; | ||
795 | 1325 | ||
796 | return 0; | 1326 | debugfs_create_file("run", 0600, perf->dbgfs_dir, perf, |
1327 | &perf_dbgfs_run); | ||
797 | 1328 | ||
798 | err: | 1329 | debugfs_create_file("threads_count", 0600, perf->dbgfs_dir, perf, |
799 | debugfs_remove_recursive(perf_debugfs_dir); | 1330 | &perf_dbgfs_tcnt); |
800 | perf_debugfs_dir = NULL; | 1331 | |
801 | return -ENODEV; | 1332 | /* They are made read-only for test exec safety and integrity */ |
1333 | debugfs_create_u8("chunk_order", 0500, perf->dbgfs_dir, &chunk_order); | ||
1334 | |||
1335 | debugfs_create_u8("total_order", 0500, perf->dbgfs_dir, &total_order); | ||
1336 | |||
1337 | debugfs_create_bool("use_dma", 0500, perf->dbgfs_dir, &use_dma); | ||
802 | } | 1338 | } |
803 | 1339 | ||
804 | static int perf_probe(struct ntb_client *client, struct ntb_dev *ntb) | 1340 | static void perf_clear_dbgfs(struct perf_ctx *perf) |
1341 | { | ||
1342 | debugfs_remove_recursive(perf->dbgfs_dir); | ||
1343 | } | ||
1344 | |||
1345 | /*============================================================================== | ||
1346 | * Basic driver initialization | ||
1347 | *============================================================================== | ||
1348 | */ | ||
1349 | |||
1350 | static struct perf_ctx *perf_create_data(struct ntb_dev *ntb) | ||
805 | { | 1351 | { |
806 | struct pci_dev *pdev = ntb->pdev; | ||
807 | struct perf_ctx *perf; | 1352 | struct perf_ctx *perf; |
808 | int node; | ||
809 | int rc = 0; | ||
810 | 1353 | ||
811 | if (ntb_spad_count(ntb) < MAX_SPAD) { | 1354 | perf = devm_kzalloc(&ntb->dev, sizeof(*perf), GFP_KERNEL); |
812 | dev_err(&ntb->dev, "Not enough scratch pad registers for %s", | 1355 | if (!perf) |
813 | DRIVER_NAME); | 1356 | return ERR_PTR(-ENOMEM); |
814 | return -EIO; | ||
815 | } | ||
816 | 1357 | ||
817 | if (!ntb->ops->mw_set_trans) { | 1358 | perf->pcnt = ntb_peer_port_count(ntb); |
818 | dev_err(&ntb->dev, "Need inbound MW based NTB API\n"); | 1359 | perf->peers = devm_kcalloc(&ntb->dev, perf->pcnt, sizeof(*perf->peers), |
819 | return -EINVAL; | 1360 | GFP_KERNEL); |
1361 | if (!perf->peers) | ||
1362 | return ERR_PTR(-ENOMEM); | ||
1363 | |||
1364 | perf->ntb = ntb; | ||
1365 | |||
1366 | return perf; | ||
1367 | } | ||
1368 | |||
1369 | static int perf_setup_peer_mw(struct perf_peer *peer) | ||
1370 | { | ||
1371 | struct perf_ctx *perf = peer->perf; | ||
1372 | phys_addr_t phys_addr; | ||
1373 | int ret; | ||
1374 | |||
1375 | /* Get outbound MW parameters and map it */ | ||
1376 | ret = ntb_peer_mw_get_addr(perf->ntb, peer->gidx, &phys_addr, | ||
1377 | &peer->outbuf_size); | ||
1378 | if (ret) | ||
1379 | return ret; | ||
1380 | |||
1381 | peer->outbuf = devm_ioremap_wc(&perf->ntb->dev, phys_addr, | ||
1382 | peer->outbuf_size); | ||
1383 | if (!peer->outbuf) | ||
1384 | return -ENOMEM; | ||
1385 | |||
1386 | if (max_mw_size && peer->outbuf_size > max_mw_size) { | ||
1387 | peer->outbuf_size = max_mw_size; | ||
1388 | dev_warn(&peer->perf->ntb->dev, | ||
1389 | "Peer %d outbuf reduced to %pa\n", peer->pidx, | ||
1390 | &peer->outbuf_size); | ||
820 | } | 1391 | } |
821 | 1392 | ||
822 | if (ntb_peer_port_count(ntb) != NTB_DEF_PEER_CNT) | 1393 | return 0; |
823 | dev_warn(&ntb->dev, "Multi-port NTB devices unsupported\n"); | 1394 | } |
824 | 1395 | ||
825 | node = on_node ? dev_to_node(&pdev->dev) : NUMA_NO_NODE; | 1396 | static int perf_init_peers(struct perf_ctx *perf) |
826 | perf = kzalloc_node(sizeof(*perf), GFP_KERNEL, node); | 1397 | { |
827 | if (!perf) { | 1398 | struct perf_peer *peer; |
828 | rc = -ENOMEM; | 1399 | int pidx, lport, ret; |
829 | goto err_perf; | 1400 | |
1401 | lport = ntb_port_number(perf->ntb); | ||
1402 | perf->gidx = -1; | ||
1403 | for (pidx = 0; pidx < perf->pcnt; pidx++) { | ||
1404 | peer = &perf->peers[pidx]; | ||
1405 | |||
1406 | peer->perf = perf; | ||
1407 | peer->pidx = pidx; | ||
1408 | if (lport < ntb_peer_port_number(perf->ntb, pidx)) { | ||
1409 | if (perf->gidx == -1) | ||
1410 | perf->gidx = pidx; | ||
1411 | peer->gidx = pidx + 1; | ||
1412 | } else { | ||
1413 | peer->gidx = pidx; | ||
1414 | } | ||
1415 | INIT_WORK(&peer->service, perf_service_work); | ||
830 | } | 1416 | } |
1417 | if (perf->gidx == -1) | ||
1418 | perf->gidx = pidx; | ||
831 | 1419 | ||
832 | perf->ntb = ntb; | 1420 | for (pidx = 0; pidx < perf->pcnt; pidx++) { |
833 | perf->perf_threads = 1; | 1421 | ret = perf_setup_peer_mw(&perf->peers[pidx]); |
834 | atomic_set(&perf->tsync, 0); | 1422 | if (ret) |
835 | mutex_init(&perf->run_mutex); | 1423 | return ret; |
836 | spin_lock_init(&perf->db_lock); | 1424 | } |
837 | perf_setup_mw(ntb, perf); | 1425 | |
838 | init_waitqueue_head(&perf->link_wq); | 1426 | dev_dbg(&perf->ntb->dev, "Global port index %d\n", perf->gidx); |
839 | INIT_DELAYED_WORK(&perf->link_work, perf_link_work); | 1427 | |
1428 | return 0; | ||
1429 | } | ||
840 | 1430 | ||
841 | rc = ntb_set_ctx(ntb, perf, &perf_ops); | 1431 | static int perf_probe(struct ntb_client *client, struct ntb_dev *ntb) |
842 | if (rc) | 1432 | { |
843 | goto err_ctx; | 1433 | struct perf_ctx *perf; |
1434 | int ret; | ||
844 | 1435 | ||
845 | perf->link_is_up = false; | 1436 | perf = perf_create_data(ntb); |
846 | ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); | 1437 | if (IS_ERR(perf)) |
847 | ntb_link_event(ntb); | 1438 | return PTR_ERR(perf); |
848 | 1439 | ||
849 | rc = perf_debugfs_setup(perf); | 1440 | ret = perf_init_peers(perf); |
850 | if (rc) | 1441 | if (ret) |
851 | goto err_ctx; | 1442 | return ret; |
852 | 1443 | ||
853 | perf_clear_thread_status(perf); | 1444 | perf_init_threads(perf); |
854 | 1445 | ||
855 | return 0; | 1446 | ret = perf_init_service(perf); |
1447 | if (ret) | ||
1448 | return ret; | ||
856 | 1449 | ||
857 | err_ctx: | 1450 | ret = perf_enable_service(perf); |
858 | cancel_delayed_work_sync(&perf->link_work); | 1451 | if (ret) |
859 | kfree(perf); | 1452 | return ret; |
860 | err_perf: | 1453 | |
861 | return rc; | 1454 | perf_setup_dbgfs(perf); |
1455 | |||
1456 | return 0; | ||
862 | } | 1457 | } |
863 | 1458 | ||
864 | static void perf_remove(struct ntb_client *client, struct ntb_dev *ntb) | 1459 | static void perf_remove(struct ntb_client *client, struct ntb_dev *ntb) |
865 | { | 1460 | { |
866 | struct perf_ctx *perf = ntb->ctx; | 1461 | struct perf_ctx *perf = ntb->ctx; |
867 | int i; | ||
868 | 1462 | ||
869 | dev_dbg(&perf->ntb->dev, "%s called\n", __func__); | 1463 | perf_clear_dbgfs(perf); |
870 | 1464 | ||
871 | mutex_lock(&perf->run_mutex); | 1465 | perf_disable_service(perf); |
872 | 1466 | ||
873 | cancel_delayed_work_sync(&perf->link_work); | 1467 | perf_clear_threads(perf); |
1468 | } | ||
874 | 1469 | ||
875 | ntb_clear_ctx(ntb); | 1470 | static struct ntb_client perf_client = { |
876 | ntb_link_disable(ntb); | 1471 | .ops = { |
1472 | .probe = perf_probe, | ||
1473 | .remove = perf_remove | ||
1474 | } | ||
1475 | }; | ||
877 | 1476 | ||
878 | debugfs_remove_recursive(perf_debugfs_dir); | 1477 | static int __init perf_init(void) |
879 | perf_debugfs_dir = NULL; | 1478 | { |
1479 | int ret; | ||
880 | 1480 | ||
881 | if (use_dma) { | 1481 | if (chunk_order > MAX_CHUNK_ORDER) { |
882 | for (i = 0; i < MAX_THREADS; i++) { | 1482 | chunk_order = MAX_CHUNK_ORDER; |
883 | struct pthr_ctx *pctx = &perf->pthr_ctx[i]; | 1483 | pr_info("Chunk order reduced to %hhu\n", chunk_order); |
1484 | } | ||
884 | 1485 | ||
885 | if (pctx->dma_chan) | 1486 | if (total_order < chunk_order) { |
886 | dma_release_channel(pctx->dma_chan); | 1487 | total_order = chunk_order; |
887 | } | 1488 | pr_info("Total data order reduced to %hhu\n", total_order); |
888 | } | 1489 | } |
889 | 1490 | ||
890 | kfree(perf); | 1491 | perf_wq = alloc_workqueue("perf_wq", WQ_UNBOUND | WQ_SYSFS, 0); |
1492 | if (!perf_wq) | ||
1493 | return -ENOMEM; | ||
1494 | |||
1495 | if (debugfs_initialized()) | ||
1496 | perf_dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL); | ||
1497 | |||
1498 | ret = ntb_register_client(&perf_client); | ||
1499 | if (ret) { | ||
1500 | debugfs_remove_recursive(perf_dbgfs_topdir); | ||
1501 | destroy_workqueue(perf_wq); | ||
1502 | } | ||
1503 | |||
1504 | return ret; | ||
891 | } | 1505 | } |
1506 | module_init(perf_init); | ||
1507 | |||
1508 | static void __exit perf_exit(void) | ||
1509 | { | ||
1510 | ntb_unregister_client(&perf_client); | ||
1511 | debugfs_remove_recursive(perf_dbgfs_topdir); | ||
1512 | destroy_workqueue(perf_wq); | ||
1513 | } | ||
1514 | module_exit(perf_exit); | ||
892 | 1515 | ||
893 | static struct ntb_client perf_client = { | ||
894 | .ops = { | ||
895 | .probe = perf_probe, | ||
896 | .remove = perf_remove, | ||
897 | }, | ||
898 | }; | ||
899 | module_ntb_client(perf_client); | ||
diff --git a/drivers/ntb/test/ntb_pingpong.c b/drivers/ntb/test/ntb_pingpong.c index 3f5a92bae6f8..65865e460ab8 100644 --- a/drivers/ntb/test/ntb_pingpong.c +++ b/drivers/ntb/test/ntb_pingpong.c | |||
@@ -1,10 +1,11 @@ | |||
1 | /* | 1 | /* |
2 | * This file is provided under a dual BSD/GPLv2 license. When using or | 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. | 3 | * redistributing this file, you may do so under either license. |
4 | * | 4 | * |
5 | * GPL LICENSE SUMMARY | 5 | * GPL LICENSE SUMMARY |
6 | * | 6 | * |
7 | * Copyright (C) 2015 EMC Corporation. All Rights Reserved. | 7 | * Copyright (C) 2015 EMC Corporation. All Rights Reserved. |
8 | * Copyright (C) 2017 T-Platforms. All Rights Reserved. | ||
8 | * | 9 | * |
9 | * This program is free software; you can redistribute it and/or modify | 10 | * 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 | * it under the terms of version 2 of the GNU General Public License as |
@@ -18,6 +19,7 @@ | |||
18 | * BSD LICENSE | 19 | * BSD LICENSE |
19 | * | 20 | * |
20 | * Copyright (C) 2015 EMC Corporation. All Rights Reserved. | 21 | * Copyright (C) 2015 EMC Corporation. All Rights Reserved. |
22 | * Copyright (C) 2017 T-Platforms. All Rights Reserved. | ||
21 | * | 23 | * |
22 | * Redistribution and use in source and binary forms, with or without | 24 | * Redistribution and use in source and binary forms, with or without |
23 | * modification, are permitted provided that the following conditions | 25 | * modification, are permitted provided that the following conditions |
@@ -46,37 +48,45 @@ | |||
46 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 48 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
47 | * | 49 | * |
48 | * PCIe NTB Pingpong Linux driver | 50 | * PCIe NTB Pingpong Linux driver |
49 | * | ||
50 | * Contact Information: | ||
51 | * Allen Hubbe <Allen.Hubbe@emc.com> | ||
52 | */ | 51 | */ |
53 | 52 | ||
54 | /* Note: load this module with option 'dyndbg=+p' */ | 53 | /* |
54 | * How to use this tool, by example. | ||
55 | * | ||
56 | * Assuming $DBG_DIR is something like: | ||
57 | * '/sys/kernel/debug/ntb_perf/0000:00:03.0' | ||
58 | * Suppose aside from local device there is at least one remote device | ||
59 | * connected to NTB with index 0. | ||
60 | *----------------------------------------------------------------------------- | ||
61 | * Eg: install driver with specified delay between doorbell event and response | ||
62 | * | ||
63 | * root@self# insmod ntb_pingpong.ko delay_ms=1000 | ||
64 | *----------------------------------------------------------------------------- | ||
65 | * Eg: get number of ping-pong cycles performed | ||
66 | * | ||
67 | * root@self# cat $DBG_DIR/count | ||
68 | */ | ||
55 | 69 | ||
56 | #include <linux/init.h> | 70 | #include <linux/init.h> |
57 | #include <linux/kernel.h> | 71 | #include <linux/kernel.h> |
58 | #include <linux/module.h> | 72 | #include <linux/module.h> |
73 | #include <linux/device.h> | ||
74 | #include <linux/bitops.h> | ||
59 | 75 | ||
60 | #include <linux/dma-mapping.h> | ||
61 | #include <linux/pci.h> | 76 | #include <linux/pci.h> |
62 | #include <linux/slab.h> | 77 | #include <linux/slab.h> |
63 | #include <linux/spinlock.h> | 78 | #include <linux/hrtimer.h> |
64 | #include <linux/debugfs.h> | 79 | #include <linux/debugfs.h> |
65 | 80 | ||
66 | #include <linux/ntb.h> | 81 | #include <linux/ntb.h> |
67 | 82 | ||
68 | #define DRIVER_NAME "ntb_pingpong" | 83 | #define DRIVER_NAME "ntb_pingpong" |
69 | #define DRIVER_DESCRIPTION "PCIe NTB Simple Pingpong Client" | 84 | #define DRIVER_VERSION "2.0" |
70 | |||
71 | #define DRIVER_LICENSE "Dual BSD/GPL" | ||
72 | #define DRIVER_VERSION "1.0" | ||
73 | #define DRIVER_RELDATE "24 March 2015" | ||
74 | #define DRIVER_AUTHOR "Allen Hubbe <Allen.Hubbe@emc.com>" | ||
75 | 85 | ||
76 | MODULE_LICENSE(DRIVER_LICENSE); | 86 | MODULE_LICENSE("Dual BSD/GPL"); |
77 | MODULE_VERSION(DRIVER_VERSION); | 87 | MODULE_VERSION(DRIVER_VERSION); |
78 | MODULE_AUTHOR(DRIVER_AUTHOR); | 88 | MODULE_AUTHOR("Allen Hubbe <Allen.Hubbe@emc.com>"); |
79 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION); | 89 | MODULE_DESCRIPTION("PCIe NTB Simple Pingpong Client"); |
80 | 90 | ||
81 | static unsigned int unsafe; | 91 | static unsigned int unsafe; |
82 | module_param(unsafe, uint, 0644); | 92 | module_param(unsafe, uint, 0644); |
@@ -86,237 +96,343 @@ static unsigned int delay_ms = 1000; | |||
86 | module_param(delay_ms, uint, 0644); | 96 | module_param(delay_ms, uint, 0644); |
87 | MODULE_PARM_DESC(delay_ms, "Milliseconds to delay the response to peer"); | 97 | MODULE_PARM_DESC(delay_ms, "Milliseconds to delay the response to peer"); |
88 | 98 | ||
89 | static unsigned long db_init = 0x7; | ||
90 | module_param(db_init, ulong, 0644); | ||
91 | MODULE_PARM_DESC(db_init, "Initial doorbell bits to ring on the peer"); | ||
92 | |||
93 | /* Only two-ports NTB devices are supported */ | ||
94 | #define PIDX NTB_DEF_PEER_IDX | ||
95 | |||
96 | struct pp_ctx { | 99 | struct pp_ctx { |
97 | struct ntb_dev *ntb; | 100 | struct ntb_dev *ntb; |
98 | u64 db_bits; | 101 | struct hrtimer timer; |
99 | /* synchronize access to db_bits by ping and pong */ | 102 | u64 in_db; |
100 | spinlock_t db_lock; | 103 | u64 out_db; |
101 | struct timer_list db_timer; | 104 | int out_pidx; |
102 | unsigned long db_delay; | 105 | u64 nmask; |
103 | struct dentry *debugfs_node_dir; | 106 | u64 pmask; |
104 | struct dentry *debugfs_count; | 107 | atomic_t count; |
105 | atomic_t count; | 108 | spinlock_t lock; |
109 | struct dentry *dbgfs_dir; | ||
106 | }; | 110 | }; |
111 | #define to_pp_timer(__timer) \ | ||
112 | container_of(__timer, struct pp_ctx, timer) | ||
107 | 113 | ||
108 | static struct dentry *pp_debugfs_dir; | 114 | static struct dentry *pp_dbgfs_topdir; |
109 | 115 | ||
110 | static void pp_ping(struct timer_list *t) | 116 | static int pp_find_next_peer(struct pp_ctx *pp) |
111 | { | 117 | { |
112 | struct pp_ctx *pp = from_timer(pp, t, db_timer); | 118 | u64 link, out_db; |
113 | unsigned long irqflags; | 119 | int pidx; |
114 | u64 db_bits, db_mask; | 120 | |
115 | u32 spad_rd, spad_wr; | 121 | link = ntb_link_is_up(pp->ntb, NULL, NULL); |
122 | |||
123 | /* Find next available peer */ | ||
124 | if (link & pp->nmask) { | ||
125 | pidx = __ffs64(link & pp->nmask); | ||
126 | out_db = BIT_ULL(pidx + 1); | ||
127 | } else if (link & pp->pmask) { | ||
128 | pidx = __ffs64(link & pp->pmask); | ||
129 | out_db = BIT_ULL(pidx); | ||
130 | } else { | ||
131 | return -ENODEV; | ||
132 | } | ||
116 | 133 | ||
117 | spin_lock_irqsave(&pp->db_lock, irqflags); | 134 | spin_lock(&pp->lock); |
118 | { | 135 | pp->out_pidx = pidx; |
119 | db_mask = ntb_db_valid_mask(pp->ntb); | 136 | pp->out_db = out_db; |
120 | db_bits = ntb_db_read(pp->ntb); | 137 | spin_unlock(&pp->lock); |
121 | 138 | ||
122 | if (db_bits) { | 139 | return 0; |
123 | dev_dbg(&pp->ntb->dev, | 140 | } |
124 | "Masked pongs %#llx\n", | ||
125 | db_bits); | ||
126 | ntb_db_clear(pp->ntb, db_bits); | ||
127 | } | ||
128 | 141 | ||
129 | db_bits = ((pp->db_bits | db_bits) << 1) & db_mask; | 142 | static void pp_setup(struct pp_ctx *pp) |
143 | { | ||
144 | int ret; | ||
130 | 145 | ||
131 | if (!db_bits) | 146 | ntb_db_set_mask(pp->ntb, pp->in_db); |
132 | db_bits = db_init; | ||
133 | 147 | ||
134 | spad_rd = ntb_spad_read(pp->ntb, 0); | 148 | hrtimer_cancel(&pp->timer); |
135 | spad_wr = spad_rd + 1; | ||
136 | 149 | ||
137 | dev_dbg(&pp->ntb->dev, | 150 | ret = pp_find_next_peer(pp); |
138 | "Ping bits %#llx read %#x write %#x\n", | 151 | if (ret == -ENODEV) { |
139 | db_bits, spad_rd, spad_wr); | 152 | dev_dbg(&pp->ntb->dev, "Got no peers, so cancel\n"); |
153 | return; | ||
154 | } | ||
140 | 155 | ||
141 | ntb_peer_spad_write(pp->ntb, PIDX, 0, spad_wr); | 156 | dev_dbg(&pp->ntb->dev, "Ping-pong started with port %d, db %#llx\n", |
142 | ntb_peer_db_set(pp->ntb, db_bits); | 157 | ntb_peer_port_number(pp->ntb, pp->out_pidx), pp->out_db); |
143 | ntb_db_clear_mask(pp->ntb, db_mask); | ||
144 | 158 | ||
145 | pp->db_bits = 0; | 159 | hrtimer_start(&pp->timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL); |
146 | } | ||
147 | spin_unlock_irqrestore(&pp->db_lock, irqflags); | ||
148 | } | 160 | } |
149 | 161 | ||
150 | static void pp_link_event(void *ctx) | 162 | static void pp_clear(struct pp_ctx *pp) |
151 | { | 163 | { |
152 | struct pp_ctx *pp = ctx; | 164 | hrtimer_cancel(&pp->timer); |
153 | 165 | ||
154 | if (ntb_link_is_up(pp->ntb, NULL, NULL) == 1) { | 166 | ntb_db_set_mask(pp->ntb, pp->in_db); |
155 | dev_dbg(&pp->ntb->dev, "link is up\n"); | 167 | |
156 | pp_ping(&pp->db_timer); | 168 | dev_dbg(&pp->ntb->dev, "Ping-pong cancelled\n"); |
157 | } else { | ||
158 | dev_dbg(&pp->ntb->dev, "link is down\n"); | ||
159 | del_timer(&pp->db_timer); | ||
160 | } | ||
161 | } | 169 | } |
162 | 170 | ||
163 | static void pp_db_event(void *ctx, int vec) | 171 | static void pp_ping(struct pp_ctx *pp) |
164 | { | 172 | { |
165 | struct pp_ctx *pp = ctx; | 173 | u32 count; |
166 | u64 db_bits, db_mask; | ||
167 | unsigned long irqflags; | ||
168 | 174 | ||
169 | spin_lock_irqsave(&pp->db_lock, irqflags); | 175 | count = atomic_read(&pp->count); |
170 | { | ||
171 | db_mask = ntb_db_vector_mask(pp->ntb, vec); | ||
172 | db_bits = db_mask & ntb_db_read(pp->ntb); | ||
173 | ntb_db_set_mask(pp->ntb, db_mask); | ||
174 | ntb_db_clear(pp->ntb, db_bits); | ||
175 | 176 | ||
176 | pp->db_bits |= db_bits; | 177 | spin_lock(&pp->lock); |
178 | ntb_peer_spad_write(pp->ntb, pp->out_pidx, 0, count); | ||
179 | ntb_peer_msg_write(pp->ntb, pp->out_pidx, 0, count); | ||
177 | 180 | ||
178 | mod_timer(&pp->db_timer, jiffies + pp->db_delay); | 181 | dev_dbg(&pp->ntb->dev, "Ping port %d spad %#x, msg %#x\n", |
182 | ntb_peer_port_number(pp->ntb, pp->out_pidx), count, count); | ||
179 | 183 | ||
180 | dev_dbg(&pp->ntb->dev, | 184 | ntb_peer_db_set(pp->ntb, pp->out_db); |
181 | "Pong vec %d bits %#llx\n", | 185 | ntb_db_clear_mask(pp->ntb, pp->in_db); |
182 | vec, db_bits); | 186 | spin_unlock(&pp->lock); |
183 | atomic_inc(&pp->count); | ||
184 | } | ||
185 | spin_unlock_irqrestore(&pp->db_lock, irqflags); | ||
186 | } | 187 | } |
187 | 188 | ||
188 | static int pp_debugfs_setup(struct pp_ctx *pp) | 189 | static void pp_pong(struct pp_ctx *pp) |
189 | { | 190 | { |
190 | struct pci_dev *pdev = pp->ntb->pdev; | 191 | u32 msg_data = -1, spad_data = -1; |
192 | int pidx = 0; | ||
191 | 193 | ||
192 | if (!pp_debugfs_dir) | 194 | /* Read pong data */ |
193 | return -ENODEV; | 195 | spad_data = ntb_spad_read(pp->ntb, 0); |
196 | msg_data = ntb_msg_read(pp->ntb, &pidx, 0); | ||
197 | ntb_msg_clear_sts(pp->ntb, -1); | ||
194 | 198 | ||
195 | pp->debugfs_node_dir = debugfs_create_dir(pci_name(pdev), | 199 | /* |
196 | pp_debugfs_dir); | 200 | * Scratchpad and message data may differ, since message register can't |
197 | if (!pp->debugfs_node_dir) | 201 | * be rewritten unless status is cleared. Additionally either of them |
198 | return -ENODEV; | 202 | * might be unsupported |
203 | */ | ||
204 | dev_dbg(&pp->ntb->dev, "Pong spad %#x, msg %#x (port %d)\n", | ||
205 | spad_data, msg_data, ntb_peer_port_number(pp->ntb, pidx)); | ||
199 | 206 | ||
200 | pp->debugfs_count = debugfs_create_atomic_t("count", S_IRUSR | S_IWUSR, | 207 | atomic_inc(&pp->count); |
201 | pp->debugfs_node_dir, | ||
202 | &pp->count); | ||
203 | if (!pp->debugfs_count) | ||
204 | return -ENODEV; | ||
205 | 208 | ||
206 | return 0; | 209 | ntb_db_set_mask(pp->ntb, pp->in_db); |
210 | ntb_db_clear(pp->ntb, pp->in_db); | ||
211 | |||
212 | hrtimer_start(&pp->timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL); | ||
213 | } | ||
214 | |||
215 | static enum hrtimer_restart pp_timer_func(struct hrtimer *t) | ||
216 | { | ||
217 | struct pp_ctx *pp = to_pp_timer(t); | ||
218 | |||
219 | pp_ping(pp); | ||
220 | |||
221 | return HRTIMER_NORESTART; | ||
222 | } | ||
223 | |||
224 | static void pp_link_event(void *ctx) | ||
225 | { | ||
226 | struct pp_ctx *pp = ctx; | ||
227 | |||
228 | pp_setup(pp); | ||
229 | } | ||
230 | |||
231 | static void pp_db_event(void *ctx, int vec) | ||
232 | { | ||
233 | struct pp_ctx *pp = ctx; | ||
234 | |||
235 | pp_pong(pp); | ||
207 | } | 236 | } |
208 | 237 | ||
209 | static const struct ntb_ctx_ops pp_ops = { | 238 | static const struct ntb_ctx_ops pp_ops = { |
210 | .link_event = pp_link_event, | 239 | .link_event = pp_link_event, |
211 | .db_event = pp_db_event, | 240 | .db_event = pp_db_event |
212 | }; | 241 | }; |
213 | 242 | ||
214 | static int pp_probe(struct ntb_client *client, | 243 | static int pp_check_ntb(struct ntb_dev *ntb) |
215 | struct ntb_dev *ntb) | ||
216 | { | 244 | { |
217 | struct pp_ctx *pp; | 245 | u64 pmask; |
218 | int rc; | ||
219 | 246 | ||
220 | if (ntb_db_is_unsafe(ntb)) { | 247 | if (ntb_db_is_unsafe(ntb)) { |
221 | dev_dbg(&ntb->dev, "doorbell is unsafe\n"); | 248 | dev_dbg(&ntb->dev, "Doorbell is unsafe\n"); |
222 | if (!unsafe) { | 249 | if (!unsafe) |
223 | rc = -EINVAL; | 250 | return -EINVAL; |
224 | goto err_pp; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | if (ntb_spad_count(ntb) < 1) { | ||
229 | dev_dbg(&ntb->dev, "no enough scratchpads\n"); | ||
230 | rc = -EINVAL; | ||
231 | goto err_pp; | ||
232 | } | 251 | } |
233 | 252 | ||
234 | if (ntb_spad_is_unsafe(ntb)) { | 253 | if (ntb_spad_is_unsafe(ntb)) { |
235 | dev_dbg(&ntb->dev, "scratchpad is unsafe\n"); | 254 | dev_dbg(&ntb->dev, "Scratchpad is unsafe\n"); |
236 | if (!unsafe) { | 255 | if (!unsafe) |
237 | rc = -EINVAL; | 256 | return -EINVAL; |
238 | goto err_pp; | ||
239 | } | ||
240 | } | 257 | } |
241 | 258 | ||
242 | if (ntb_peer_port_count(ntb) != NTB_DEF_PEER_CNT) | 259 | pmask = GENMASK_ULL(ntb_peer_port_count(ntb), 0); |
243 | dev_warn(&ntb->dev, "multi-port NTB is unsupported\n"); | 260 | if ((ntb_db_valid_mask(ntb) & pmask) != pmask) { |
261 | dev_err(&ntb->dev, "Unsupported DB configuration\n"); | ||
262 | return -EINVAL; | ||
263 | } | ||
244 | 264 | ||
245 | pp = kmalloc(sizeof(*pp), GFP_KERNEL); | 265 | if (ntb_spad_count(ntb) < 1 && ntb_msg_count(ntb) < 1) { |
246 | if (!pp) { | 266 | dev_err(&ntb->dev, "Scratchpads and messages unsupported\n"); |
247 | rc = -ENOMEM; | 267 | return -EINVAL; |
248 | goto err_pp; | 268 | } else if (ntb_spad_count(ntb) < 1) { |
269 | dev_dbg(&ntb->dev, "Scratchpads unsupported\n"); | ||
270 | } else if (ntb_msg_count(ntb) < 1) { | ||
271 | dev_dbg(&ntb->dev, "Messages unsupported\n"); | ||
249 | } | 272 | } |
250 | 273 | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static struct pp_ctx *pp_create_data(struct ntb_dev *ntb) | ||
278 | { | ||
279 | struct pp_ctx *pp; | ||
280 | |||
281 | pp = devm_kzalloc(&ntb->dev, sizeof(*pp), GFP_KERNEL); | ||
282 | if (!pp) | ||
283 | return ERR_PTR(-ENOMEM); | ||
284 | |||
251 | pp->ntb = ntb; | 285 | pp->ntb = ntb; |
252 | pp->db_bits = 0; | ||
253 | atomic_set(&pp->count, 0); | 286 | atomic_set(&pp->count, 0); |
254 | spin_lock_init(&pp->db_lock); | 287 | spin_lock_init(&pp->lock); |
255 | timer_setup(&pp->db_timer, pp_ping, 0); | 288 | hrtimer_init(&pp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
256 | pp->db_delay = msecs_to_jiffies(delay_ms); | 289 | pp->timer.function = pp_timer_func; |
290 | |||
291 | return pp; | ||
292 | } | ||
293 | |||
294 | static void pp_init_flds(struct pp_ctx *pp) | ||
295 | { | ||
296 | int pidx, lport, pcnt; | ||
297 | |||
298 | /* Find global port index */ | ||
299 | lport = ntb_port_number(pp->ntb); | ||
300 | pcnt = ntb_peer_port_count(pp->ntb); | ||
301 | for (pidx = 0; pidx < pcnt; pidx++) { | ||
302 | if (lport < ntb_peer_port_number(pp->ntb, pidx)) | ||
303 | break; | ||
304 | } | ||
257 | 305 | ||
258 | rc = ntb_set_ctx(ntb, pp, &pp_ops); | 306 | pp->in_db = BIT_ULL(pidx); |
259 | if (rc) | 307 | pp->pmask = GENMASK_ULL(pidx, 0) >> 1; |
260 | goto err_ctx; | 308 | pp->nmask = GENMASK_ULL(pcnt - 1, pidx); |
261 | 309 | ||
262 | rc = pp_debugfs_setup(pp); | 310 | dev_dbg(&pp->ntb->dev, "Inbound db %#llx, prev %#llx, next %#llx\n", |
263 | if (rc) | 311 | pp->in_db, pp->pmask, pp->nmask); |
264 | goto err_ctx; | 312 | } |
313 | |||
314 | static int pp_mask_events(struct pp_ctx *pp) | ||
315 | { | ||
316 | u64 db_mask, msg_mask; | ||
317 | int ret; | ||
318 | |||
319 | db_mask = ntb_db_valid_mask(pp->ntb); | ||
320 | ret = ntb_db_set_mask(pp->ntb, db_mask); | ||
321 | if (ret) | ||
322 | return ret; | ||
265 | 323 | ||
266 | ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); | 324 | /* Skip message events masking if unsupported */ |
267 | ntb_link_event(ntb); | 325 | if (ntb_msg_count(pp->ntb) < 1) |
326 | return 0; | ||
327 | |||
328 | msg_mask = ntb_msg_outbits(pp->ntb) | ntb_msg_inbits(pp->ntb); | ||
329 | return ntb_msg_set_mask(pp->ntb, msg_mask); | ||
330 | } | ||
331 | |||
332 | static int pp_setup_ctx(struct pp_ctx *pp) | ||
333 | { | ||
334 | int ret; | ||
335 | |||
336 | ret = ntb_set_ctx(pp->ntb, pp, &pp_ops); | ||
337 | if (ret) | ||
338 | return ret; | ||
339 | |||
340 | ntb_link_enable(pp->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); | ||
341 | /* Might be not necessary */ | ||
342 | ntb_link_event(pp->ntb); | ||
268 | 343 | ||
269 | return 0; | 344 | return 0; |
345 | } | ||
346 | |||
347 | static void pp_clear_ctx(struct pp_ctx *pp) | ||
348 | { | ||
349 | ntb_link_disable(pp->ntb); | ||
270 | 350 | ||
271 | err_ctx: | 351 | ntb_clear_ctx(pp->ntb); |
272 | kfree(pp); | ||
273 | err_pp: | ||
274 | return rc; | ||
275 | } | 352 | } |
276 | 353 | ||
277 | static void pp_remove(struct ntb_client *client, | 354 | static void pp_setup_dbgfs(struct pp_ctx *pp) |
278 | struct ntb_dev *ntb) | 355 | { |
356 | struct pci_dev *pdev = pp->ntb->pdev; | ||
357 | void *ret; | ||
358 | |||
359 | pp->dbgfs_dir = debugfs_create_dir(pci_name(pdev), pp_dbgfs_topdir); | ||
360 | |||
361 | ret = debugfs_create_atomic_t("count", 0600, pp->dbgfs_dir, &pp->count); | ||
362 | if (!ret) | ||
363 | dev_warn(&pp->ntb->dev, "DebugFS unsupported\n"); | ||
364 | } | ||
365 | |||
366 | static void pp_clear_dbgfs(struct pp_ctx *pp) | ||
367 | { | ||
368 | debugfs_remove_recursive(pp->dbgfs_dir); | ||
369 | } | ||
370 | |||
371 | static int pp_probe(struct ntb_client *client, struct ntb_dev *ntb) | ||
372 | { | ||
373 | struct pp_ctx *pp; | ||
374 | int ret; | ||
375 | |||
376 | ret = pp_check_ntb(ntb); | ||
377 | if (ret) | ||
378 | return ret; | ||
379 | |||
380 | pp = pp_create_data(ntb); | ||
381 | if (IS_ERR(pp)) | ||
382 | return PTR_ERR(pp); | ||
383 | |||
384 | pp_init_flds(pp); | ||
385 | |||
386 | ret = pp_mask_events(pp); | ||
387 | if (ret) | ||
388 | return ret; | ||
389 | |||
390 | ret = pp_setup_ctx(pp); | ||
391 | if (ret) | ||
392 | return ret; | ||
393 | |||
394 | pp_setup_dbgfs(pp); | ||
395 | |||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static void pp_remove(struct ntb_client *client, struct ntb_dev *ntb) | ||
279 | { | 400 | { |
280 | struct pp_ctx *pp = ntb->ctx; | 401 | struct pp_ctx *pp = ntb->ctx; |
281 | 402 | ||
282 | debugfs_remove_recursive(pp->debugfs_node_dir); | 403 | pp_clear_dbgfs(pp); |
283 | 404 | ||
284 | ntb_clear_ctx(ntb); | 405 | pp_clear_ctx(pp); |
285 | del_timer_sync(&pp->db_timer); | ||
286 | ntb_link_disable(ntb); | ||
287 | 406 | ||
288 | kfree(pp); | 407 | pp_clear(pp); |
289 | } | 408 | } |
290 | 409 | ||
291 | static struct ntb_client pp_client = { | 410 | static struct ntb_client pp_client = { |
292 | .ops = { | 411 | .ops = { |
293 | .probe = pp_probe, | 412 | .probe = pp_probe, |
294 | .remove = pp_remove, | 413 | .remove = pp_remove |
295 | }, | 414 | } |
296 | }; | 415 | }; |
297 | 416 | ||
298 | static int __init pp_init(void) | 417 | static int __init pp_init(void) |
299 | { | 418 | { |
300 | int rc; | 419 | int ret; |
301 | 420 | ||
302 | if (debugfs_initialized()) | 421 | if (debugfs_initialized()) |
303 | pp_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); | 422 | pp_dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL); |
304 | 423 | ||
305 | rc = ntb_register_client(&pp_client); | 424 | ret = ntb_register_client(&pp_client); |
306 | if (rc) | 425 | if (ret) |
307 | goto err_client; | 426 | debugfs_remove_recursive(pp_dbgfs_topdir); |
308 | 427 | ||
309 | return 0; | 428 | return ret; |
310 | |||
311 | err_client: | ||
312 | debugfs_remove_recursive(pp_debugfs_dir); | ||
313 | return rc; | ||
314 | } | 429 | } |
315 | module_init(pp_init); | 430 | module_init(pp_init); |
316 | 431 | ||
317 | static void __exit pp_exit(void) | 432 | static void __exit pp_exit(void) |
318 | { | 433 | { |
319 | ntb_unregister_client(&pp_client); | 434 | ntb_unregister_client(&pp_client); |
320 | debugfs_remove_recursive(pp_debugfs_dir); | 435 | debugfs_remove_recursive(pp_dbgfs_topdir); |
321 | } | 436 | } |
322 | module_exit(pp_exit); | 437 | module_exit(pp_exit); |
438 | |||
diff --git a/drivers/ntb/test/ntb_tool.c b/drivers/ntb/test/ntb_tool.c index 91526a986caa..d592c0ffbd19 100644 --- a/drivers/ntb/test/ntb_tool.c +++ b/drivers/ntb/test/ntb_tool.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * GPL LICENSE SUMMARY | 5 | * GPL LICENSE SUMMARY |
6 | * | 6 | * |
7 | * Copyright (C) 2015 EMC Corporation. All Rights Reserved. | 7 | * Copyright (C) 2015 EMC Corporation. All Rights Reserved. |
8 | * Copyright (C) 2017 T-Platforms All Rights Reserved. | ||
8 | * | 9 | * |
9 | * This program is free software; you can redistribute it and/or modify | 10 | * 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 | * it under the terms of version 2 of the GNU General Public License as |
@@ -18,6 +19,7 @@ | |||
18 | * BSD LICENSE | 19 | * BSD LICENSE |
19 | * | 20 | * |
20 | * Copyright (C) 2015 EMC Corporation. All Rights Reserved. | 21 | * Copyright (C) 2015 EMC Corporation. All Rights Reserved. |
22 | * Copyright (C) 2017 T-Platforms All Rights Reserved. | ||
21 | * | 23 | * |
22 | * Redistribution and use in source and binary forms, with or without | 24 | * Redistribution and use in source and binary forms, with or without |
23 | * modification, are permitted provided that the following conditions | 25 | * modification, are permitted provided that the following conditions |
@@ -46,9 +48,6 @@ | |||
46 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 48 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
47 | * | 49 | * |
48 | * PCIe NTB Debugging Tool Linux driver | 50 | * PCIe NTB Debugging Tool Linux driver |
49 | * | ||
50 | * Contact Information: | ||
51 | * Allen Hubbe <Allen.Hubbe@emc.com> | ||
52 | */ | 51 | */ |
53 | 52 | ||
54 | /* | 53 | /* |
@@ -56,42 +55,125 @@ | |||
56 | * | 55 | * |
57 | * Assuming $DBG_DIR is something like: | 56 | * Assuming $DBG_DIR is something like: |
58 | * '/sys/kernel/debug/ntb_tool/0000:00:03.0' | 57 | * '/sys/kernel/debug/ntb_tool/0000:00:03.0' |
58 | * Suppose aside from local device there is at least one remote device | ||
59 | * connected to NTB with index 0. | ||
60 | *----------------------------------------------------------------------------- | ||
61 | * Eg: check local/peer device information. | ||
62 | * | ||
63 | * # Get local device port number | ||
64 | * root@self# cat $DBG_DIR/port | ||
65 | * | ||
66 | * # Check local device functionality | ||
67 | * root@self# ls $DBG_DIR | ||
68 | * db msg1 msg_sts peer4/ port | ||
69 | * db_event msg2 peer0/ peer5/ spad0 | ||
70 | * db_mask msg3 peer1/ peer_db spad1 | ||
71 | * link msg_event peer2/ peer_db_mask spad2 | ||
72 | * msg0 msg_mask peer3/ peer_spad spad3 | ||
73 | * # As one can see it supports: | ||
74 | * # 1) four inbound message registers | ||
75 | * # 2) four inbound scratchpads | ||
76 | * # 3) up to six peer devices | ||
77 | * | ||
78 | * # Check peer device port number | ||
79 | * root@self# cat $DBG_DIR/peer0/port | ||
80 | * | ||
81 | * # Check peer device(s) functionality to be used | ||
82 | * root@self# ls $DBG_DIR/peer0 | ||
83 | * link mw_trans0 mw_trans6 port | ||
84 | * link_event mw_trans1 mw_trans7 spad0 | ||
85 | * msg0 mw_trans2 peer_mw_trans0 spad1 | ||
86 | * msg1 mw_trans3 peer_mw_trans1 spad2 | ||
87 | * msg2 mw_trans4 peer_mw_trans2 spad3 | ||
88 | * msg3 mw_trans5 peer_mw_trans3 | ||
89 | * # As one can see we got: | ||
90 | * # 1) four outbound message registers | ||
91 | * # 2) four outbound scratchpads | ||
92 | * # 3) eight inbound memory windows | ||
93 | * # 4) four outbound memory windows | ||
94 | *----------------------------------------------------------------------------- | ||
95 | * Eg: NTB link tests | ||
59 | * | 96 | * |
60 | * Eg: check if clearing the doorbell mask generates an interrupt. | 97 | * # Set local link up/down |
98 | * root@self# echo Y > $DBG_DIR/link | ||
99 | * root@self# echo N > $DBG_DIR/link | ||
61 | * | 100 | * |
62 | * # Check the link status | 101 | * # Check if link with peer device is up/down: |
63 | * root@self# cat $DBG_DIR/link | 102 | * root@self# cat $DBG_DIR/peer0/link |
64 | * | 103 | * |
65 | * # Block until the link is up | 104 | * # Block until the link is up/down |
66 | * root@self# echo Y > $DBG_DIR/link_event | 105 | * root@self# echo Y > $DBG_DIR/peer0/link_event |
106 | * root@self# echo N > $DBG_DIR/peer0/link_event | ||
107 | *----------------------------------------------------------------------------- | ||
108 | * Eg: Doorbell registers tests (some functionality might be absent) | ||
67 | * | 109 | * |
68 | * # Set the doorbell mask | 110 | * # Set/clear/get local doorbell |
69 | * root@self# echo 's 1' > $DBG_DIR/mask | 111 | * root@self# echo 's 1' > $DBG_DIR/db |
112 | * root@self# echo 'c 1' > $DBG_DIR/db | ||
113 | * root@self# cat $DBG_DIR/db | ||
70 | * | 114 | * |
71 | * # Ring the doorbell from the peer | 115 | * # Set/clear/get local doorbell mask |
116 | * root@self# echo 's 1' > $DBG_DIR/db_mask | ||
117 | * root@self# echo 'c 1' > $DBG_DIR/db_mask | ||
118 | * root@self# cat $DBG_DIR/db_mask | ||
119 | * | ||
120 | * # Ring/clear/get peer doorbell | ||
72 | * root@peer# echo 's 1' > $DBG_DIR/peer_db | 121 | * root@peer# echo 's 1' > $DBG_DIR/peer_db |
122 | * root@peer# echo 'c 1' > $DBG_DIR/peer_db | ||
123 | * root@peer# cat $DBG_DIR/peer_db | ||
124 | * | ||
125 | * # Set/clear/get peer doorbell mask | ||
126 | * root@self# echo 's 1' > $DBG_DIR/peer_db_mask | ||
127 | * root@self# echo 'c 1' > $DBG_DIR/peer_db_mask | ||
128 | * root@self# cat $DBG_DIR/peer_db_mask | ||
129 | * | ||
130 | * # Block until local doorbell is set with specified value | ||
131 | * root@self# echo 1 > $DBG_DIR/db_event | ||
132 | *----------------------------------------------------------------------------- | ||
133 | * Eg: Message registers tests (functionality might be absent) | ||
73 | * | 134 | * |
74 | * # Clear the doorbell mask | 135 | * # Set/clear/get in/out message registers status |
75 | * root@self# echo 'c 1' > $DBG_DIR/mask | 136 | * root@self# echo 's 1' > $DBG_DIR/msg_sts |
137 | * root@self# echo 'c 1' > $DBG_DIR/msg_sts | ||
138 | * root@self# cat $DBG_DIR/msg_sts | ||
76 | * | 139 | * |
77 | * Observe debugging output in dmesg or your console. You should see a | 140 | * # Set/clear in/out message registers mask |
78 | * doorbell event triggered by clearing the mask. If not, this may indicate an | 141 | * root@self# echo 's 1' > $DBG_DIR/msg_mask |
79 | * issue with the hardware that needs to be worked around in the driver. | 142 | * root@self# echo 'c 1' > $DBG_DIR/msg_mask |
80 | * | 143 | * |
81 | * Eg: read and write scratchpad registers | 144 | * # Get inbound message register #0 value and source of port index |
145 | * root@self# cat $DBG_DIR/msg0 | ||
82 | * | 146 | * |
83 | * root@peer# echo '0 0x01010101 1 0x7f7f7f7f' > $DBG_DIR/peer_spad | 147 | * # Send some data to peer over outbound message register #0 |
148 | * root@self# echo 0x01020304 > $DBG_DIR/peer0/msg0 | ||
149 | *----------------------------------------------------------------------------- | ||
150 | * Eg: Scratchpad registers tests (functionality might be absent) | ||
84 | * | 151 | * |
85 | * root@self# cat $DBG_DIR/spad | 152 | * # Write/read to/from local scratchpad register #0 |
153 | * root@peer# echo 0x01020304 > $DBG_DIR/spad0 | ||
154 | * root@peer# cat $DBG_DIR/spad0 | ||
86 | * | 155 | * |
87 | * Observe that spad 0 and 1 have the values set by the peer. | 156 | * # Write/read to/from peer scratchpad register #0 |
157 | * root@peer# echo 0x01020304 > $DBG_DIR/peer0/spad0 | ||
158 | * root@peer# cat $DBG_DIR/peer0/spad0 | ||
159 | *----------------------------------------------------------------------------- | ||
160 | * Eg: Memory windows tests | ||
88 | * | 161 | * |
89 | * # Check the memory window translation info | 162 | * # Create inbound memory window buffer of specified size/get its base address |
90 | * cat $DBG_DIR/peer_trans0 | 163 | * root@peer# echo 16384 > $DBG_DIR/peer0/mw_trans0 |
164 | * root@peer# cat $DBG_DIR/peer0/mw_trans0 | ||
91 | * | 165 | * |
92 | * # Setup a 16k memory window buffer | 166 | * # Write/read data to/from inbound memory window |
93 | * echo 16384 > $DBG_DIR/peer_trans0 | 167 | * root@peer# echo Hello > $DBG_DIR/peer0/mw0 |
168 | * root@peer# head -c 7 $DBG_DIR/peer0/mw0 | ||
94 | * | 169 | * |
170 | * # Map outbound memory window/check it settings (on peer device) | ||
171 | * root@peer# echo 0xADD0BA5E:16384 > $DBG_DIR/peer0/peer_mw_trans0 | ||
172 | * root@peer# cat $DBG_DIR/peer0/peer_mw_trans0 | ||
173 | * | ||
174 | * # Write/read data to/from outbound memory window (on peer device) | ||
175 | * root@peer# echo olleH > $DBG_DIR/peer0/peer_mw0 | ||
176 | * root@peer# head -c 7 $DBG_DIR/peer0/peer_mw0 | ||
95 | */ | 177 | */ |
96 | 178 | ||
97 | #include <linux/init.h> | 179 | #include <linux/init.h> |
@@ -106,49 +188,87 @@ | |||
106 | 188 | ||
107 | #include <linux/ntb.h> | 189 | #include <linux/ntb.h> |
108 | 190 | ||
109 | #define DRIVER_NAME "ntb_tool" | 191 | #define DRIVER_NAME "ntb_tool" |
110 | #define DRIVER_DESCRIPTION "PCIe NTB Debugging Tool" | 192 | #define DRIVER_VERSION "2.0" |
111 | |||
112 | #define DRIVER_LICENSE "Dual BSD/GPL" | ||
113 | #define DRIVER_VERSION "1.0" | ||
114 | #define DRIVER_RELDATE "22 April 2015" | ||
115 | #define DRIVER_AUTHOR "Allen Hubbe <Allen.Hubbe@emc.com>" | ||
116 | 193 | ||
117 | MODULE_LICENSE(DRIVER_LICENSE); | 194 | MODULE_LICENSE("Dual BSD/GPL"); |
118 | MODULE_VERSION(DRIVER_VERSION); | 195 | MODULE_VERSION(DRIVER_VERSION); |
119 | MODULE_AUTHOR(DRIVER_AUTHOR); | 196 | MODULE_AUTHOR("Allen Hubbe <Allen.Hubbe@emc.com>"); |
120 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION); | 197 | MODULE_DESCRIPTION("PCIe NTB Debugging Tool"); |
121 | |||
122 | /* It is rare to have hadrware with greater than six MWs */ | ||
123 | #define MAX_MWS 6 | ||
124 | /* Only two-ports devices are supported */ | ||
125 | #define PIDX NTB_DEF_PEER_IDX | ||
126 | |||
127 | static struct dentry *tool_dbgfs; | ||
128 | 198 | ||
199 | /* | ||
200 | * Inbound and outbound memory windows descriptor. Union members selection | ||
201 | * depends on the MW type the structure describes. mm_base/dma_base are the | ||
202 | * virtual and DMA address of an inbound MW. io_base/tr_base are the MMIO | ||
203 | * mapped virtual and xlat addresses of an outbound MW respectively. | ||
204 | */ | ||
129 | struct tool_mw { | 205 | struct tool_mw { |
130 | int idx; | 206 | int widx; |
207 | int pidx; | ||
131 | struct tool_ctx *tc; | 208 | struct tool_ctx *tc; |
132 | resource_size_t win_size; | 209 | union { |
210 | u8 *mm_base; | ||
211 | u8 __iomem *io_base; | ||
212 | }; | ||
213 | union { | ||
214 | dma_addr_t dma_base; | ||
215 | u64 tr_base; | ||
216 | }; | ||
133 | resource_size_t size; | 217 | resource_size_t size; |
134 | u8 __iomem *local; | 218 | struct dentry *dbgfs_file; |
135 | u8 *peer; | 219 | }; |
136 | dma_addr_t peer_dma; | 220 | |
137 | struct dentry *peer_dbg_file; | 221 | /* |
222 | * Wrapper structure is used to distinguish the outbound MW peers reference | ||
223 | * within the corresponding DebugFS directory IO operation. | ||
224 | */ | ||
225 | struct tool_mw_wrap { | ||
226 | int pidx; | ||
227 | struct tool_mw *mw; | ||
228 | }; | ||
229 | |||
230 | struct tool_msg { | ||
231 | int midx; | ||
232 | int pidx; | ||
233 | struct tool_ctx *tc; | ||
234 | }; | ||
235 | |||
236 | struct tool_spad { | ||
237 | int sidx; | ||
238 | int pidx; | ||
239 | struct tool_ctx *tc; | ||
240 | }; | ||
241 | |||
242 | struct tool_peer { | ||
243 | int pidx; | ||
244 | struct tool_ctx *tc; | ||
245 | int inmw_cnt; | ||
246 | struct tool_mw *inmws; | ||
247 | int outmw_cnt; | ||
248 | struct tool_mw_wrap *outmws; | ||
249 | int outmsg_cnt; | ||
250 | struct tool_msg *outmsgs; | ||
251 | int outspad_cnt; | ||
252 | struct tool_spad *outspads; | ||
253 | struct dentry *dbgfs_dir; | ||
138 | }; | 254 | }; |
139 | 255 | ||
140 | struct tool_ctx { | 256 | struct tool_ctx { |
141 | struct ntb_dev *ntb; | 257 | struct ntb_dev *ntb; |
142 | struct dentry *dbgfs; | ||
143 | wait_queue_head_t link_wq; | 258 | wait_queue_head_t link_wq; |
144 | int mw_count; | 259 | wait_queue_head_t db_wq; |
145 | struct tool_mw mws[MAX_MWS]; | 260 | wait_queue_head_t msg_wq; |
261 | int outmw_cnt; | ||
262 | struct tool_mw *outmws; | ||
263 | int peer_cnt; | ||
264 | struct tool_peer *peers; | ||
265 | int inmsg_cnt; | ||
266 | struct tool_msg *inmsgs; | ||
267 | int inspad_cnt; | ||
268 | struct tool_spad *inspads; | ||
269 | struct dentry *dbgfs_dir; | ||
146 | }; | 270 | }; |
147 | 271 | ||
148 | #define SPAD_FNAME_SIZE 0x10 | ||
149 | #define INT_PTR(x) ((void *)(unsigned long)x) | ||
150 | #define PTR_INT(x) ((int)(unsigned long)x) | ||
151 | |||
152 | #define TOOL_FOPS_RDWR(__name, __read, __write) \ | 272 | #define TOOL_FOPS_RDWR(__name, __read, __write) \ |
153 | const struct file_operations __name = { \ | 273 | const struct file_operations __name = { \ |
154 | .owner = THIS_MODULE, \ | 274 | .owner = THIS_MODULE, \ |
@@ -157,6 +277,15 @@ struct tool_ctx { | |||
157 | .write = __write, \ | 277 | .write = __write, \ |
158 | } | 278 | } |
159 | 279 | ||
280 | #define TOOL_BUF_LEN 32 | ||
281 | |||
282 | static struct dentry *tool_dbgfs_topdir; | ||
283 | |||
284 | /*============================================================================== | ||
285 | * NTB events handlers | ||
286 | *============================================================================== | ||
287 | */ | ||
288 | |||
160 | static void tool_link_event(void *ctx) | 289 | static void tool_link_event(void *ctx) |
161 | { | 290 | { |
162 | struct tool_ctx *tc = ctx; | 291 | struct tool_ctx *tc = ctx; |
@@ -182,580 +311,578 @@ static void tool_db_event(void *ctx, int vec) | |||
182 | 311 | ||
183 | dev_dbg(&tc->ntb->dev, "doorbell vec %d mask %#llx bits %#llx\n", | 312 | dev_dbg(&tc->ntb->dev, "doorbell vec %d mask %#llx bits %#llx\n", |
184 | vec, db_mask, db_bits); | 313 | vec, db_mask, db_bits); |
314 | |||
315 | wake_up(&tc->db_wq); | ||
316 | } | ||
317 | |||
318 | static void tool_msg_event(void *ctx) | ||
319 | { | ||
320 | struct tool_ctx *tc = ctx; | ||
321 | u64 msg_sts; | ||
322 | |||
323 | msg_sts = ntb_msg_read_sts(tc->ntb); | ||
324 | |||
325 | dev_dbg(&tc->ntb->dev, "message bits %#llx\n", msg_sts); | ||
326 | |||
327 | wake_up(&tc->msg_wq); | ||
185 | } | 328 | } |
186 | 329 | ||
187 | static const struct ntb_ctx_ops tool_ops = { | 330 | static const struct ntb_ctx_ops tool_ops = { |
188 | .link_event = tool_link_event, | 331 | .link_event = tool_link_event, |
189 | .db_event = tool_db_event, | 332 | .db_event = tool_db_event, |
333 | .msg_event = tool_msg_event | ||
190 | }; | 334 | }; |
191 | 335 | ||
192 | static ssize_t tool_dbfn_read(struct tool_ctx *tc, char __user *ubuf, | 336 | /*============================================================================== |
193 | size_t size, loff_t *offp, | 337 | * Common read/write methods |
194 | u64 (*db_read_fn)(struct ntb_dev *)) | 338 | *============================================================================== |
339 | */ | ||
340 | |||
341 | static ssize_t tool_fn_read(struct tool_ctx *tc, char __user *ubuf, | ||
342 | size_t size, loff_t *offp, | ||
343 | u64 (*fn_read)(struct ntb_dev *)) | ||
195 | { | 344 | { |
196 | size_t buf_size; | 345 | size_t buf_size; |
197 | char *buf; | 346 | char buf[TOOL_BUF_LEN]; |
198 | ssize_t pos, rc; | 347 | ssize_t pos; |
199 | 348 | ||
200 | if (!db_read_fn) | 349 | if (!fn_read) |
201 | return -EINVAL; | 350 | return -EINVAL; |
202 | 351 | ||
203 | buf_size = min_t(size_t, size, 0x20); | 352 | buf_size = min(size, sizeof(buf)); |
204 | |||
205 | buf = kmalloc(buf_size, GFP_KERNEL); | ||
206 | if (!buf) | ||
207 | return -ENOMEM; | ||
208 | |||
209 | pos = scnprintf(buf, buf_size, "%#llx\n", | ||
210 | db_read_fn(tc->ntb)); | ||
211 | 353 | ||
212 | rc = simple_read_from_buffer(ubuf, size, offp, buf, pos); | 354 | pos = scnprintf(buf, buf_size, "%#llx\n", fn_read(tc->ntb)); |
213 | 355 | ||
214 | kfree(buf); | 356 | return simple_read_from_buffer(ubuf, size, offp, buf, pos); |
215 | |||
216 | return rc; | ||
217 | } | 357 | } |
218 | 358 | ||
219 | static ssize_t tool_dbfn_write(struct tool_ctx *tc, | 359 | static ssize_t tool_fn_write(struct tool_ctx *tc, |
220 | const char __user *ubuf, | 360 | const char __user *ubuf, |
221 | size_t size, loff_t *offp, | 361 | size_t size, loff_t *offp, |
222 | int (*db_set_fn)(struct ntb_dev *, u64), | 362 | int (*fn_set)(struct ntb_dev *, u64), |
223 | int (*db_clear_fn)(struct ntb_dev *, u64)) | 363 | int (*fn_clear)(struct ntb_dev *, u64)) |
224 | { | 364 | { |
225 | u64 db_bits; | ||
226 | char *buf, cmd; | 365 | char *buf, cmd; |
227 | ssize_t rc; | 366 | ssize_t ret; |
367 | u64 bits; | ||
228 | int n; | 368 | int n; |
229 | 369 | ||
230 | buf = kmalloc(size + 1, GFP_KERNEL); | 370 | buf = kmalloc(size + 1, GFP_KERNEL); |
231 | if (!buf) | 371 | if (!buf) |
232 | return -ENOMEM; | 372 | return -ENOMEM; |
233 | 373 | ||
234 | rc = simple_write_to_buffer(buf, size, offp, ubuf, size); | 374 | ret = simple_write_to_buffer(buf, size, offp, ubuf, size); |
235 | if (rc < 0) { | 375 | if (ret < 0) { |
236 | kfree(buf); | 376 | kfree(buf); |
237 | return rc; | 377 | return ret; |
238 | } | 378 | } |
239 | 379 | ||
240 | buf[size] = 0; | 380 | buf[size] = 0; |
241 | 381 | ||
242 | n = sscanf(buf, "%c %lli", &cmd, &db_bits); | 382 | n = sscanf(buf, "%c %lli", &cmd, &bits); |
243 | 383 | ||
244 | kfree(buf); | 384 | kfree(buf); |
245 | 385 | ||
246 | if (n != 2) { | 386 | if (n != 2) { |
247 | rc = -EINVAL; | 387 | ret = -EINVAL; |
248 | } else if (cmd == 's') { | 388 | } else if (cmd == 's') { |
249 | if (!db_set_fn) | 389 | if (!fn_set) |
250 | rc = -EINVAL; | 390 | ret = -EINVAL; |
251 | else | 391 | else |
252 | rc = db_set_fn(tc->ntb, db_bits); | 392 | ret = fn_set(tc->ntb, bits); |
253 | } else if (cmd == 'c') { | 393 | } else if (cmd == 'c') { |
254 | if (!db_clear_fn) | 394 | if (!fn_clear) |
255 | rc = -EINVAL; | 395 | ret = -EINVAL; |
256 | else | 396 | else |
257 | rc = db_clear_fn(tc->ntb, db_bits); | 397 | ret = fn_clear(tc->ntb, bits); |
258 | } else { | 398 | } else { |
259 | rc = -EINVAL; | 399 | ret = -EINVAL; |
260 | } | 400 | } |
261 | 401 | ||
262 | return rc ? : size; | 402 | return ret ? : size; |
263 | } | 403 | } |
264 | 404 | ||
265 | static ssize_t tool_spadfn_read(struct tool_ctx *tc, char __user *ubuf, | 405 | /*============================================================================== |
266 | size_t size, loff_t *offp, | 406 | * Port read/write methods |
267 | u32 (*spad_read_fn)(struct ntb_dev *, int)) | 407 | *============================================================================== |
268 | { | 408 | */ |
269 | size_t buf_size; | ||
270 | char *buf; | ||
271 | ssize_t pos, rc; | ||
272 | int i, spad_count; | ||
273 | |||
274 | if (!spad_read_fn) | ||
275 | return -EINVAL; | ||
276 | |||
277 | spad_count = ntb_spad_count(tc->ntb); | ||
278 | 409 | ||
279 | /* | 410 | static ssize_t tool_port_read(struct file *filep, char __user *ubuf, |
280 | * We multiply the number of spads by 15 to get the buffer size | 411 | size_t size, loff_t *offp) |
281 | * this is from 3 for the %d, 10 for the largest hex value | 412 | { |
282 | * (0x00000000) and 2 for the tab and line feed. | 413 | struct tool_ctx *tc = filep->private_data; |
283 | */ | 414 | char buf[TOOL_BUF_LEN]; |
284 | buf_size = min_t(size_t, size, spad_count * 15); | 415 | int pos; |
285 | 416 | ||
286 | buf = kmalloc(buf_size, GFP_KERNEL); | 417 | pos = scnprintf(buf, sizeof(buf), "%d\n", ntb_port_number(tc->ntb)); |
287 | if (!buf) | ||
288 | return -ENOMEM; | ||
289 | 418 | ||
290 | pos = 0; | 419 | return simple_read_from_buffer(ubuf, size, offp, buf, pos); |
420 | } | ||
291 | 421 | ||
292 | for (i = 0; i < spad_count; ++i) { | 422 | static TOOL_FOPS_RDWR(tool_port_fops, |
293 | pos += scnprintf(buf + pos, buf_size - pos, "%d\t%#x\n", | 423 | tool_port_read, |
294 | i, spad_read_fn(tc->ntb, i)); | 424 | NULL); |
295 | } | ||
296 | 425 | ||
297 | rc = simple_read_from_buffer(ubuf, size, offp, buf, pos); | 426 | static ssize_t tool_peer_port_read(struct file *filep, char __user *ubuf, |
427 | size_t size, loff_t *offp) | ||
428 | { | ||
429 | struct tool_peer *peer = filep->private_data; | ||
430 | struct tool_ctx *tc = peer->tc; | ||
431 | char buf[TOOL_BUF_LEN]; | ||
432 | int pos; | ||
298 | 433 | ||
299 | kfree(buf); | 434 | pos = scnprintf(buf, sizeof(buf), "%d\n", |
435 | ntb_peer_port_number(tc->ntb, peer->pidx)); | ||
300 | 436 | ||
301 | return rc; | 437 | return simple_read_from_buffer(ubuf, size, offp, buf, pos); |
302 | } | 438 | } |
303 | 439 | ||
304 | static ssize_t tool_spadfn_write(struct tool_ctx *tc, | 440 | static TOOL_FOPS_RDWR(tool_peer_port_fops, |
305 | const char __user *ubuf, | 441 | tool_peer_port_read, |
306 | size_t size, loff_t *offp, | 442 | NULL); |
307 | int (*spad_write_fn)(struct ntb_dev *, | 443 | |
308 | int, u32)) | 444 | static int tool_init_peers(struct tool_ctx *tc) |
309 | { | 445 | { |
310 | int spad_idx; | 446 | int pidx; |
311 | u32 spad_val; | ||
312 | char *buf, *buf_ptr; | ||
313 | int pos, n; | ||
314 | ssize_t rc; | ||
315 | |||
316 | if (!spad_write_fn) { | ||
317 | dev_dbg(&tc->ntb->dev, "no spad write fn\n"); | ||
318 | return -EINVAL; | ||
319 | } | ||
320 | 447 | ||
321 | buf = kmalloc(size + 1, GFP_KERNEL); | 448 | tc->peer_cnt = ntb_peer_port_count(tc->ntb); |
322 | if (!buf) | 449 | tc->peers = devm_kcalloc(&tc->ntb->dev, tc->peer_cnt, |
450 | sizeof(*tc->peers), GFP_KERNEL); | ||
451 | if (tc->peers == NULL) | ||
323 | return -ENOMEM; | 452 | return -ENOMEM; |
324 | 453 | ||
325 | rc = simple_write_to_buffer(buf, size, offp, ubuf, size); | 454 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) { |
326 | if (rc < 0) { | 455 | tc->peers[pidx].pidx = pidx; |
327 | kfree(buf); | 456 | tc->peers[pidx].tc = tc; |
328 | return rc; | ||
329 | } | 457 | } |
330 | 458 | ||
331 | buf[size] = 0; | 459 | return 0; |
332 | buf_ptr = buf; | ||
333 | n = sscanf(buf_ptr, "%d %i%n", &spad_idx, &spad_val, &pos); | ||
334 | while (n == 2) { | ||
335 | buf_ptr += pos; | ||
336 | rc = spad_write_fn(tc->ntb, spad_idx, spad_val); | ||
337 | if (rc) | ||
338 | break; | ||
339 | |||
340 | n = sscanf(buf_ptr, "%d %i%n", &spad_idx, &spad_val, &pos); | ||
341 | } | ||
342 | |||
343 | if (n < 0) | ||
344 | rc = n; | ||
345 | |||
346 | kfree(buf); | ||
347 | |||
348 | return rc ? : size; | ||
349 | } | 460 | } |
350 | 461 | ||
351 | static ssize_t tool_db_read(struct file *filep, char __user *ubuf, | 462 | /*============================================================================== |
352 | size_t size, loff_t *offp) | 463 | * Link state read/write methods |
353 | { | 464 | *============================================================================== |
354 | struct tool_ctx *tc = filep->private_data; | 465 | */ |
355 | |||
356 | return tool_dbfn_read(tc, ubuf, size, offp, | ||
357 | tc->ntb->ops->db_read); | ||
358 | } | ||
359 | 466 | ||
360 | static ssize_t tool_db_write(struct file *filep, const char __user *ubuf, | 467 | static ssize_t tool_link_write(struct file *filep, const char __user *ubuf, |
361 | size_t size, loff_t *offp) | 468 | size_t size, loff_t *offp) |
362 | { | 469 | { |
363 | struct tool_ctx *tc = filep->private_data; | 470 | struct tool_ctx *tc = filep->private_data; |
471 | bool val; | ||
472 | int ret; | ||
364 | 473 | ||
365 | return tool_dbfn_write(tc, ubuf, size, offp, | 474 | ret = kstrtobool_from_user(ubuf, size, &val); |
366 | tc->ntb->ops->db_set, | 475 | if (ret) |
367 | tc->ntb->ops->db_clear); | 476 | return ret; |
368 | } | ||
369 | 477 | ||
370 | static TOOL_FOPS_RDWR(tool_db_fops, | 478 | if (val) |
371 | tool_db_read, | 479 | ret = ntb_link_enable(tc->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); |
372 | tool_db_write); | 480 | else |
481 | ret = ntb_link_disable(tc->ntb); | ||
373 | 482 | ||
374 | static ssize_t tool_mask_read(struct file *filep, char __user *ubuf, | 483 | if (ret) |
375 | size_t size, loff_t *offp) | 484 | return ret; |
376 | { | ||
377 | struct tool_ctx *tc = filep->private_data; | ||
378 | 485 | ||
379 | return tool_dbfn_read(tc, ubuf, size, offp, | 486 | return size; |
380 | tc->ntb->ops->db_read_mask); | ||
381 | } | 487 | } |
382 | 488 | ||
383 | static ssize_t tool_mask_write(struct file *filep, const char __user *ubuf, | 489 | static TOOL_FOPS_RDWR(tool_link_fops, |
384 | size_t size, loff_t *offp) | 490 | NULL, |
491 | tool_link_write); | ||
492 | |||
493 | static ssize_t tool_peer_link_read(struct file *filep, char __user *ubuf, | ||
494 | size_t size, loff_t *offp) | ||
385 | { | 495 | { |
386 | struct tool_ctx *tc = filep->private_data; | 496 | struct tool_peer *peer = filep->private_data; |
497 | struct tool_ctx *tc = peer->tc; | ||
498 | char buf[3]; | ||
387 | 499 | ||
388 | return tool_dbfn_write(tc, ubuf, size, offp, | 500 | if (ntb_link_is_up(tc->ntb, NULL, NULL) & BIT(peer->pidx)) |
389 | tc->ntb->ops->db_set_mask, | 501 | buf[0] = 'Y'; |
390 | tc->ntb->ops->db_clear_mask); | 502 | else |
503 | buf[0] = 'N'; | ||
504 | buf[1] = '\n'; | ||
505 | buf[2] = '\0'; | ||
506 | |||
507 | return simple_read_from_buffer(ubuf, size, offp, buf, 3); | ||
391 | } | 508 | } |
392 | 509 | ||
393 | static TOOL_FOPS_RDWR(tool_mask_fops, | 510 | static TOOL_FOPS_RDWR(tool_peer_link_fops, |
394 | tool_mask_read, | 511 | tool_peer_link_read, |
395 | tool_mask_write); | 512 | NULL); |
396 | 513 | ||
397 | static ssize_t tool_peer_db_read(struct file *filep, char __user *ubuf, | 514 | static ssize_t tool_peer_link_event_write(struct file *filep, |
398 | size_t size, loff_t *offp) | 515 | const char __user *ubuf, |
516 | size_t size, loff_t *offp) | ||
399 | { | 517 | { |
400 | struct tool_ctx *tc = filep->private_data; | 518 | struct tool_peer *peer = filep->private_data; |
519 | struct tool_ctx *tc = peer->tc; | ||
520 | u64 link_msk; | ||
521 | bool val; | ||
522 | int ret; | ||
401 | 523 | ||
402 | return tool_dbfn_read(tc, ubuf, size, offp, | 524 | ret = kstrtobool_from_user(ubuf, size, &val); |
403 | tc->ntb->ops->peer_db_read); | 525 | if (ret) |
404 | } | 526 | return ret; |
405 | 527 | ||
406 | static ssize_t tool_peer_db_write(struct file *filep, const char __user *ubuf, | 528 | link_msk = BIT_ULL_MASK(peer->pidx); |
407 | size_t size, loff_t *offp) | ||
408 | { | ||
409 | struct tool_ctx *tc = filep->private_data; | ||
410 | 529 | ||
411 | return tool_dbfn_write(tc, ubuf, size, offp, | 530 | if (wait_event_interruptible(tc->link_wq, |
412 | tc->ntb->ops->peer_db_set, | 531 | !!(ntb_link_is_up(tc->ntb, NULL, NULL) & link_msk) == val)) |
413 | tc->ntb->ops->peer_db_clear); | 532 | return -ERESTART; |
533 | |||
534 | return size; | ||
414 | } | 535 | } |
415 | 536 | ||
416 | static TOOL_FOPS_RDWR(tool_peer_db_fops, | 537 | static TOOL_FOPS_RDWR(tool_peer_link_event_fops, |
417 | tool_peer_db_read, | 538 | NULL, |
418 | tool_peer_db_write); | 539 | tool_peer_link_event_write); |
419 | 540 | ||
420 | static ssize_t tool_peer_mask_read(struct file *filep, char __user *ubuf, | 541 | /*============================================================================== |
421 | size_t size, loff_t *offp) | 542 | * Memory windows read/write/setting methods |
543 | *============================================================================== | ||
544 | */ | ||
545 | |||
546 | static ssize_t tool_mw_read(struct file *filep, char __user *ubuf, | ||
547 | size_t size, loff_t *offp) | ||
422 | { | 548 | { |
423 | struct tool_ctx *tc = filep->private_data; | 549 | struct tool_mw *inmw = filep->private_data; |
550 | |||
551 | if (inmw->mm_base == NULL) | ||
552 | return -ENXIO; | ||
424 | 553 | ||
425 | return tool_dbfn_read(tc, ubuf, size, offp, | 554 | return simple_read_from_buffer(ubuf, size, offp, |
426 | tc->ntb->ops->peer_db_read_mask); | 555 | inmw->mm_base, inmw->size); |
427 | } | 556 | } |
428 | 557 | ||
429 | static ssize_t tool_peer_mask_write(struct file *filep, const char __user *ubuf, | 558 | static ssize_t tool_mw_write(struct file *filep, const char __user *ubuf, |
430 | size_t size, loff_t *offp) | 559 | size_t size, loff_t *offp) |
431 | { | 560 | { |
432 | struct tool_ctx *tc = filep->private_data; | 561 | struct tool_mw *inmw = filep->private_data; |
433 | 562 | ||
434 | return tool_dbfn_write(tc, ubuf, size, offp, | 563 | if (inmw->mm_base == NULL) |
435 | tc->ntb->ops->peer_db_set_mask, | 564 | return -ENXIO; |
436 | tc->ntb->ops->peer_db_clear_mask); | 565 | |
566 | return simple_write_to_buffer(inmw->mm_base, inmw->size, offp, | ||
567 | ubuf, size); | ||
437 | } | 568 | } |
438 | 569 | ||
439 | static TOOL_FOPS_RDWR(tool_peer_mask_fops, | 570 | static TOOL_FOPS_RDWR(tool_mw_fops, |
440 | tool_peer_mask_read, | 571 | tool_mw_read, |
441 | tool_peer_mask_write); | 572 | tool_mw_write); |
442 | 573 | ||
443 | static ssize_t tool_spad_read(struct file *filep, char __user *ubuf, | 574 | static int tool_setup_mw(struct tool_ctx *tc, int pidx, int widx, |
444 | size_t size, loff_t *offp) | 575 | size_t req_size) |
445 | { | 576 | { |
446 | struct tool_ctx *tc = filep->private_data; | 577 | resource_size_t size, addr_align, size_align; |
578 | struct tool_mw *inmw = &tc->peers[pidx].inmws[widx]; | ||
579 | char buf[TOOL_BUF_LEN]; | ||
580 | int ret; | ||
447 | 581 | ||
448 | return tool_spadfn_read(tc, ubuf, size, offp, | 582 | if (inmw->mm_base != NULL) |
449 | tc->ntb->ops->spad_read); | 583 | return 0; |
450 | } | ||
451 | 584 | ||
452 | static ssize_t tool_spad_write(struct file *filep, const char __user *ubuf, | 585 | ret = ntb_mw_get_align(tc->ntb, pidx, widx, &addr_align, |
453 | size_t size, loff_t *offp) | 586 | &size_align, &size); |
454 | { | 587 | if (ret) |
455 | struct tool_ctx *tc = filep->private_data; | 588 | return ret; |
589 | |||
590 | inmw->size = min_t(resource_size_t, req_size, size); | ||
591 | inmw->size = round_up(inmw->size, addr_align); | ||
592 | inmw->size = round_up(inmw->size, size_align); | ||
593 | inmw->mm_base = dma_alloc_coherent(&tc->ntb->dev, inmw->size, | ||
594 | &inmw->dma_base, GFP_KERNEL); | ||
595 | if (!inmw->mm_base) | ||
596 | return -ENOMEM; | ||
456 | 597 | ||
457 | return tool_spadfn_write(tc, ubuf, size, offp, | 598 | if (!IS_ALIGNED(inmw->dma_base, addr_align)) { |
458 | tc->ntb->ops->spad_write); | 599 | ret = -ENOMEM; |
459 | } | 600 | goto err_free_dma; |
601 | } | ||
460 | 602 | ||
461 | static TOOL_FOPS_RDWR(tool_spad_fops, | 603 | ret = ntb_mw_set_trans(tc->ntb, pidx, widx, inmw->dma_base, inmw->size); |
462 | tool_spad_read, | 604 | if (ret) |
463 | tool_spad_write); | 605 | goto err_free_dma; |
464 | 606 | ||
465 | static u32 ntb_tool_peer_spad_read(struct ntb_dev *ntb, int sidx) | 607 | snprintf(buf, sizeof(buf), "mw%d", widx); |
466 | { | 608 | inmw->dbgfs_file = debugfs_create_file(buf, 0600, |
467 | return ntb_peer_spad_read(ntb, PIDX, sidx); | 609 | tc->peers[pidx].dbgfs_dir, inmw, |
468 | } | 610 | &tool_mw_fops); |
469 | 611 | ||
470 | static ssize_t tool_peer_spad_read(struct file *filep, char __user *ubuf, | 612 | return 0; |
471 | size_t size, loff_t *offp) | ||
472 | { | ||
473 | struct tool_ctx *tc = filep->private_data; | ||
474 | 613 | ||
475 | return tool_spadfn_read(tc, ubuf, size, offp, ntb_tool_peer_spad_read); | 614 | err_free_dma: |
476 | } | 615 | dma_free_coherent(&tc->ntb->dev, inmw->size, inmw->mm_base, |
616 | inmw->dma_base); | ||
617 | inmw->mm_base = NULL; | ||
618 | inmw->dma_base = 0; | ||
619 | inmw->size = 0; | ||
477 | 620 | ||
478 | static int ntb_tool_peer_spad_write(struct ntb_dev *ntb, int sidx, u32 val) | 621 | return ret; |
479 | { | ||
480 | return ntb_peer_spad_write(ntb, PIDX, sidx, val); | ||
481 | } | 622 | } |
482 | 623 | ||
483 | static ssize_t tool_peer_spad_write(struct file *filep, const char __user *ubuf, | 624 | static void tool_free_mw(struct tool_ctx *tc, int pidx, int widx) |
484 | size_t size, loff_t *offp) | ||
485 | { | 625 | { |
486 | struct tool_ctx *tc = filep->private_data; | 626 | struct tool_mw *inmw = &tc->peers[pidx].inmws[widx]; |
487 | 627 | ||
488 | return tool_spadfn_write(tc, ubuf, size, offp, | 628 | debugfs_remove(inmw->dbgfs_file); |
489 | ntb_tool_peer_spad_write); | ||
490 | } | ||
491 | 629 | ||
492 | static TOOL_FOPS_RDWR(tool_peer_spad_fops, | 630 | if (inmw->mm_base != NULL) { |
493 | tool_peer_spad_read, | 631 | ntb_mw_clear_trans(tc->ntb, pidx, widx); |
494 | tool_peer_spad_write); | 632 | dma_free_coherent(&tc->ntb->dev, inmw->size, |
495 | 633 | inmw->mm_base, inmw->dma_base); | |
496 | static ssize_t tool_link_read(struct file *filep, char __user *ubuf, | 634 | } |
497 | size_t size, loff_t *offp) | ||
498 | { | ||
499 | struct tool_ctx *tc = filep->private_data; | ||
500 | char buf[3]; | ||
501 | |||
502 | buf[0] = ntb_link_is_up(tc->ntb, NULL, NULL) ? 'Y' : 'N'; | ||
503 | buf[1] = '\n'; | ||
504 | buf[2] = '\0'; | ||
505 | 635 | ||
506 | return simple_read_from_buffer(ubuf, size, offp, buf, 2); | 636 | inmw->mm_base = NULL; |
637 | inmw->dma_base = 0; | ||
638 | inmw->size = 0; | ||
639 | inmw->dbgfs_file = NULL; | ||
507 | } | 640 | } |
508 | 641 | ||
509 | static ssize_t tool_link_write(struct file *filep, const char __user *ubuf, | 642 | static ssize_t tool_mw_trans_read(struct file *filep, char __user *ubuf, |
510 | size_t size, loff_t *offp) | 643 | size_t size, loff_t *offp) |
511 | { | 644 | { |
512 | struct tool_ctx *tc = filep->private_data; | 645 | struct tool_mw *inmw = filep->private_data; |
513 | char buf[32]; | 646 | resource_size_t addr_align; |
647 | resource_size_t size_align; | ||
648 | resource_size_t size_max; | ||
649 | ssize_t ret, off = 0; | ||
514 | size_t buf_size; | 650 | size_t buf_size; |
515 | bool val; | 651 | char *buf; |
516 | int rc; | ||
517 | 652 | ||
518 | buf_size = min(size, (sizeof(buf) - 1)); | 653 | buf_size = min_t(size_t, size, 512); |
519 | if (copy_from_user(buf, ubuf, buf_size)) | ||
520 | return -EFAULT; | ||
521 | 654 | ||
522 | buf[buf_size] = '\0'; | 655 | buf = kmalloc(buf_size, GFP_KERNEL); |
656 | if (!buf) | ||
657 | return -ENOMEM; | ||
523 | 658 | ||
524 | rc = strtobool(buf, &val); | 659 | ret = ntb_mw_get_align(inmw->tc->ntb, inmw->pidx, inmw->widx, |
525 | if (rc) | 660 | &addr_align, &size_align, &size_max); |
526 | return rc; | 661 | if (ret) |
662 | goto err; | ||
527 | 663 | ||
528 | if (val) | 664 | off += scnprintf(buf + off, buf_size - off, |
529 | rc = ntb_link_enable(tc->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); | 665 | "Inbound MW \t%d\n", |
530 | else | 666 | inmw->widx); |
531 | rc = ntb_link_disable(tc->ntb); | ||
532 | 667 | ||
533 | if (rc) | 668 | off += scnprintf(buf + off, buf_size - off, |
534 | return rc; | 669 | "Port \t%d (%d)\n", |
670 | ntb_peer_port_number(inmw->tc->ntb, inmw->pidx), | ||
671 | inmw->pidx); | ||
535 | 672 | ||
536 | return size; | 673 | off += scnprintf(buf + off, buf_size - off, |
537 | } | 674 | "Window Address \t0x%pK\n", inmw->mm_base); |
538 | 675 | ||
539 | static TOOL_FOPS_RDWR(tool_link_fops, | 676 | off += scnprintf(buf + off, buf_size - off, |
540 | tool_link_read, | 677 | "DMA Address \t%pad\n", |
541 | tool_link_write); | 678 | &inmw->dma_base); |
542 | 679 | ||
543 | static ssize_t tool_link_event_write(struct file *filep, | 680 | off += scnprintf(buf + off, buf_size - off, |
544 | const char __user *ubuf, | 681 | "Window Size \t%pa[p]\n", |
545 | size_t size, loff_t *offp) | 682 | &inmw->size); |
546 | { | ||
547 | struct tool_ctx *tc = filep->private_data; | ||
548 | char buf[32]; | ||
549 | size_t buf_size; | ||
550 | bool val; | ||
551 | int rc; | ||
552 | 683 | ||
553 | buf_size = min(size, (sizeof(buf) - 1)); | 684 | off += scnprintf(buf + off, buf_size - off, |
554 | if (copy_from_user(buf, ubuf, buf_size)) | 685 | "Alignment \t%pa[p]\n", |
555 | return -EFAULT; | 686 | &addr_align); |
556 | 687 | ||
557 | buf[buf_size] = '\0'; | 688 | off += scnprintf(buf + off, buf_size - off, |
689 | "Size Alignment \t%pa[p]\n", | ||
690 | &size_align); | ||
558 | 691 | ||
559 | rc = strtobool(buf, &val); | 692 | off += scnprintf(buf + off, buf_size - off, |
560 | if (rc) | 693 | "Size Max \t%pa[p]\n", |
561 | return rc; | 694 | &size_max); |
562 | 695 | ||
563 | if (wait_event_interruptible(tc->link_wq, | 696 | ret = simple_read_from_buffer(ubuf, size, offp, buf, off); |
564 | ntb_link_is_up(tc->ntb, NULL, NULL) == val)) | 697 | |
565 | return -ERESTART; | 698 | err: |
699 | kfree(buf); | ||
700 | |||
701 | return ret; | ||
702 | } | ||
703 | |||
704 | static ssize_t tool_mw_trans_write(struct file *filep, const char __user *ubuf, | ||
705 | size_t size, loff_t *offp) | ||
706 | { | ||
707 | struct tool_mw *inmw = filep->private_data; | ||
708 | unsigned int val; | ||
709 | int ret; | ||
710 | |||
711 | ret = kstrtouint_from_user(ubuf, size, 0, &val); | ||
712 | if (ret) | ||
713 | return ret; | ||
714 | |||
715 | tool_free_mw(inmw->tc, inmw->pidx, inmw->widx); | ||
716 | if (val) { | ||
717 | ret = tool_setup_mw(inmw->tc, inmw->pidx, inmw->widx, val); | ||
718 | if (ret) | ||
719 | return ret; | ||
720 | } | ||
566 | 721 | ||
567 | return size; | 722 | return size; |
568 | } | 723 | } |
569 | 724 | ||
570 | static TOOL_FOPS_RDWR(tool_link_event_fops, | 725 | static TOOL_FOPS_RDWR(tool_mw_trans_fops, |
571 | NULL, | 726 | tool_mw_trans_read, |
572 | tool_link_event_write); | 727 | tool_mw_trans_write); |
573 | 728 | ||
574 | static ssize_t tool_mw_read(struct file *filep, char __user *ubuf, | 729 | static ssize_t tool_peer_mw_read(struct file *filep, char __user *ubuf, |
575 | size_t size, loff_t *offp) | 730 | size_t size, loff_t *offp) |
576 | { | 731 | { |
577 | struct tool_mw *mw = filep->private_data; | 732 | struct tool_mw *outmw = filep->private_data; |
578 | ssize_t rc; | ||
579 | loff_t pos = *offp; | 733 | loff_t pos = *offp; |
734 | ssize_t ret; | ||
580 | void *buf; | 735 | void *buf; |
581 | 736 | ||
582 | if (mw->local == NULL) | 737 | if (outmw->io_base == NULL) |
583 | return -EIO; | 738 | return -EIO; |
584 | if (pos < 0) | 739 | |
585 | return -EINVAL; | 740 | if (pos >= outmw->size || !size) |
586 | if (pos >= mw->win_size || !size) | ||
587 | return 0; | 741 | return 0; |
588 | if (size > mw->win_size - pos) | 742 | |
589 | size = mw->win_size - pos; | 743 | if (size > outmw->size - pos) |
744 | size = outmw->size - pos; | ||
590 | 745 | ||
591 | buf = kmalloc(size, GFP_KERNEL); | 746 | buf = kmalloc(size, GFP_KERNEL); |
592 | if (!buf) | 747 | if (!buf) |
593 | return -ENOMEM; | 748 | return -ENOMEM; |
594 | 749 | ||
595 | memcpy_fromio(buf, mw->local + pos, size); | 750 | memcpy_fromio(buf, outmw->io_base + pos, size); |
596 | rc = copy_to_user(ubuf, buf, size); | 751 | ret = copy_to_user(ubuf, buf, size); |
597 | if (rc == size) { | 752 | if (ret == size) { |
598 | rc = -EFAULT; | 753 | ret = -EFAULT; |
599 | goto err_free; | 754 | goto err_free; |
600 | } | 755 | } |
601 | 756 | ||
602 | size -= rc; | 757 | size -= ret; |
603 | *offp = pos + size; | 758 | *offp = pos + size; |
604 | rc = size; | 759 | ret = size; |
605 | 760 | ||
606 | err_free: | 761 | err_free: |
607 | kfree(buf); | 762 | kfree(buf); |
608 | 763 | ||
609 | return rc; | 764 | return ret; |
610 | } | 765 | } |
611 | 766 | ||
612 | static ssize_t tool_mw_write(struct file *filep, const char __user *ubuf, | 767 | static ssize_t tool_peer_mw_write(struct file *filep, const char __user *ubuf, |
613 | size_t size, loff_t *offp) | 768 | size_t size, loff_t *offp) |
614 | { | 769 | { |
615 | struct tool_mw *mw = filep->private_data; | 770 | struct tool_mw *outmw = filep->private_data; |
616 | ssize_t rc; | 771 | ssize_t ret; |
617 | loff_t pos = *offp; | 772 | loff_t pos = *offp; |
618 | void *buf; | 773 | void *buf; |
619 | 774 | ||
620 | if (pos < 0) | 775 | if (outmw->io_base == NULL) |
621 | return -EINVAL; | 776 | return -EIO; |
622 | if (pos >= mw->win_size || !size) | 777 | |
778 | if (pos >= outmw->size || !size) | ||
623 | return 0; | 779 | return 0; |
624 | if (size > mw->win_size - pos) | 780 | if (size > outmw->size - pos) |
625 | size = mw->win_size - pos; | 781 | size = outmw->size - pos; |
626 | 782 | ||
627 | buf = kmalloc(size, GFP_KERNEL); | 783 | buf = kmalloc(size, GFP_KERNEL); |
628 | if (!buf) | 784 | if (!buf) |
629 | return -ENOMEM; | 785 | return -ENOMEM; |
630 | 786 | ||
631 | rc = copy_from_user(buf, ubuf, size); | 787 | ret = copy_from_user(buf, ubuf, size); |
632 | if (rc == size) { | 788 | if (ret == size) { |
633 | rc = -EFAULT; | 789 | ret = -EFAULT; |
634 | goto err_free; | 790 | goto err_free; |
635 | } | 791 | } |
636 | 792 | ||
637 | size -= rc; | 793 | size -= ret; |
638 | *offp = pos + size; | 794 | *offp = pos + size; |
639 | rc = size; | 795 | ret = size; |
640 | 796 | ||
641 | memcpy_toio(mw->local + pos, buf, size); | 797 | memcpy_toio(outmw->io_base + pos, buf, size); |
642 | 798 | ||
643 | err_free: | 799 | err_free: |
644 | kfree(buf); | 800 | kfree(buf); |
645 | 801 | ||
646 | return rc; | 802 | return ret; |
647 | } | ||
648 | |||
649 | static TOOL_FOPS_RDWR(tool_mw_fops, | ||
650 | tool_mw_read, | ||
651 | tool_mw_write); | ||
652 | |||
653 | static ssize_t tool_peer_mw_read(struct file *filep, char __user *ubuf, | ||
654 | size_t size, loff_t *offp) | ||
655 | { | ||
656 | struct tool_mw *mw = filep->private_data; | ||
657 | |||
658 | if (!mw->peer) | ||
659 | return -ENXIO; | ||
660 | |||
661 | return simple_read_from_buffer(ubuf, size, offp, mw->peer, mw->size); | ||
662 | } | ||
663 | |||
664 | static ssize_t tool_peer_mw_write(struct file *filep, const char __user *ubuf, | ||
665 | size_t size, loff_t *offp) | ||
666 | { | ||
667 | struct tool_mw *mw = filep->private_data; | ||
668 | |||
669 | if (!mw->peer) | ||
670 | return -ENXIO; | ||
671 | |||
672 | return simple_write_to_buffer(mw->peer, mw->size, offp, ubuf, size); | ||
673 | } | 803 | } |
674 | 804 | ||
675 | static TOOL_FOPS_RDWR(tool_peer_mw_fops, | 805 | static TOOL_FOPS_RDWR(tool_peer_mw_fops, |
676 | tool_peer_mw_read, | 806 | tool_peer_mw_read, |
677 | tool_peer_mw_write); | 807 | tool_peer_mw_write); |
678 | 808 | ||
679 | static int tool_setup_mw(struct tool_ctx *tc, int idx, size_t req_size) | 809 | static int tool_setup_peer_mw(struct tool_ctx *tc, int pidx, int widx, |
810 | u64 req_addr, size_t req_size) | ||
680 | { | 811 | { |
681 | int rc; | 812 | struct tool_mw *outmw = &tc->outmws[widx]; |
682 | struct tool_mw *mw = &tc->mws[idx]; | 813 | resource_size_t map_size; |
683 | resource_size_t size, align_addr, align_size; | 814 | phys_addr_t map_base; |
684 | char buf[16]; | 815 | char buf[TOOL_BUF_LEN]; |
816 | int ret; | ||
685 | 817 | ||
686 | if (mw->peer) | 818 | if (outmw->io_base != NULL) |
687 | return 0; | 819 | return 0; |
688 | 820 | ||
689 | rc = ntb_mw_get_align(tc->ntb, PIDX, idx, &align_addr, | 821 | ret = ntb_peer_mw_get_addr(tc->ntb, widx, &map_base, &map_size); |
690 | &align_size, &size); | 822 | if (ret) |
691 | if (rc) | 823 | return ret; |
692 | return rc; | ||
693 | 824 | ||
694 | mw->size = min_t(resource_size_t, req_size, size); | 825 | ret = ntb_peer_mw_set_trans(tc->ntb, pidx, widx, req_addr, req_size); |
695 | mw->size = round_up(mw->size, align_addr); | 826 | if (ret) |
696 | mw->size = round_up(mw->size, align_size); | 827 | return ret; |
697 | mw->peer = dma_alloc_coherent(&tc->ntb->pdev->dev, mw->size, | ||
698 | &mw->peer_dma, GFP_KERNEL); | ||
699 | 828 | ||
700 | if (!mw->peer || !IS_ALIGNED(mw->peer_dma, align_addr)) | 829 | outmw->io_base = ioremap_wc(map_base, map_size); |
701 | return -ENOMEM; | 830 | if (outmw->io_base == NULL) { |
831 | ret = -EFAULT; | ||
832 | goto err_clear_trans; | ||
833 | } | ||
702 | 834 | ||
703 | rc = ntb_mw_set_trans(tc->ntb, PIDX, idx, mw->peer_dma, mw->size); | 835 | outmw->tr_base = req_addr; |
704 | if (rc) | 836 | outmw->size = req_size; |
705 | goto err_free_dma; | 837 | outmw->pidx = pidx; |
706 | 838 | ||
707 | snprintf(buf, sizeof(buf), "peer_mw%d", idx); | 839 | snprintf(buf, sizeof(buf), "peer_mw%d", widx); |
708 | mw->peer_dbg_file = debugfs_create_file(buf, S_IRUSR | S_IWUSR, | 840 | outmw->dbgfs_file = debugfs_create_file(buf, 0600, |
709 | mw->tc->dbgfs, mw, | 841 | tc->peers[pidx].dbgfs_dir, outmw, |
710 | &tool_peer_mw_fops); | 842 | &tool_peer_mw_fops); |
711 | 843 | ||
712 | return 0; | 844 | return 0; |
713 | 845 | ||
714 | err_free_dma: | 846 | err_clear_trans: |
715 | dma_free_coherent(&tc->ntb->pdev->dev, mw->size, | 847 | ntb_peer_mw_clear_trans(tc->ntb, pidx, widx); |
716 | mw->peer, | 848 | |
717 | mw->peer_dma); | 849 | return ret; |
718 | mw->peer = NULL; | ||
719 | mw->peer_dma = 0; | ||
720 | mw->size = 0; | ||
721 | |||
722 | return rc; | ||
723 | } | 850 | } |
724 | 851 | ||
725 | static void tool_free_mw(struct tool_ctx *tc, int idx) | 852 | static void tool_free_peer_mw(struct tool_ctx *tc, int widx) |
726 | { | 853 | { |
727 | struct tool_mw *mw = &tc->mws[idx]; | 854 | struct tool_mw *outmw = &tc->outmws[widx]; |
728 | |||
729 | if (mw->peer) { | ||
730 | ntb_mw_clear_trans(tc->ntb, PIDX, idx); | ||
731 | dma_free_coherent(&tc->ntb->pdev->dev, mw->size, | ||
732 | mw->peer, | ||
733 | mw->peer_dma); | ||
734 | } | ||
735 | 855 | ||
736 | mw->peer = NULL; | 856 | debugfs_remove(outmw->dbgfs_file); |
737 | mw->peer_dma = 0; | ||
738 | 857 | ||
739 | debugfs_remove(mw->peer_dbg_file); | 858 | if (outmw->io_base != NULL) { |
859 | iounmap(tc->outmws[widx].io_base); | ||
860 | ntb_peer_mw_clear_trans(tc->ntb, outmw->pidx, widx); | ||
861 | } | ||
740 | 862 | ||
741 | mw->peer_dbg_file = NULL; | 863 | outmw->io_base = NULL; |
864 | outmw->tr_base = 0; | ||
865 | outmw->size = 0; | ||
866 | outmw->pidx = -1; | ||
867 | outmw->dbgfs_file = NULL; | ||
742 | } | 868 | } |
743 | 869 | ||
744 | static ssize_t tool_peer_mw_trans_read(struct file *filep, | 870 | static ssize_t tool_peer_mw_trans_read(struct file *filep, char __user *ubuf, |
745 | char __user *ubuf, | 871 | size_t size, loff_t *offp) |
746 | size_t size, loff_t *offp) | ||
747 | { | 872 | { |
748 | struct tool_mw *mw = filep->private_data; | 873 | struct tool_mw_wrap *outmw_wrap = filep->private_data; |
749 | 874 | struct tool_mw *outmw = outmw_wrap->mw; | |
750 | char *buf; | 875 | resource_size_t map_size; |
876 | phys_addr_t map_base; | ||
877 | ssize_t off = 0; | ||
751 | size_t buf_size; | 878 | size_t buf_size; |
752 | ssize_t ret, off = 0; | 879 | char *buf; |
880 | int ret; | ||
753 | 881 | ||
754 | phys_addr_t base; | 882 | ret = ntb_peer_mw_get_addr(outmw->tc->ntb, outmw->widx, |
755 | resource_size_t mw_size; | 883 | &map_base, &map_size); |
756 | resource_size_t align_addr = 0; | 884 | if (ret) |
757 | resource_size_t align_size = 0; | 885 | return ret; |
758 | resource_size_t max_size = 0; | ||
759 | 886 | ||
760 | buf_size = min_t(size_t, size, 512); | 887 | buf_size = min_t(size_t, size, 512); |
761 | 888 | ||
@@ -763,43 +890,37 @@ static ssize_t tool_peer_mw_trans_read(struct file *filep, | |||
763 | if (!buf) | 890 | if (!buf) |
764 | return -ENOMEM; | 891 | return -ENOMEM; |
765 | 892 | ||
766 | ntb_mw_get_align(mw->tc->ntb, PIDX, mw->idx, | ||
767 | &align_addr, &align_size, &max_size); | ||
768 | ntb_peer_mw_get_addr(mw->tc->ntb, mw->idx, &base, &mw_size); | ||
769 | |||
770 | off += scnprintf(buf + off, buf_size - off, | 893 | off += scnprintf(buf + off, buf_size - off, |
771 | "Peer MW %d Information:\n", mw->idx); | 894 | "Outbound MW: \t%d\n", outmw->widx); |
772 | 895 | ||
773 | off += scnprintf(buf + off, buf_size - off, | 896 | if (outmw->io_base != NULL) { |
774 | "Physical Address \t%pa[p]\n", | 897 | off += scnprintf(buf + off, buf_size - off, |
775 | &base); | 898 | "Port attached \t%d (%d)\n", |
776 | 899 | ntb_peer_port_number(outmw->tc->ntb, outmw->pidx), | |
777 | off += scnprintf(buf + off, buf_size - off, | 900 | outmw->pidx); |
778 | "Window Size \t%lld\n", | 901 | } else { |
779 | (unsigned long long)mw_size); | 902 | off += scnprintf(buf + off, buf_size - off, |
903 | "Port attached \t-1 (-1)\n"); | ||
904 | } | ||
780 | 905 | ||
781 | off += scnprintf(buf + off, buf_size - off, | 906 | off += scnprintf(buf + off, buf_size - off, |
782 | "Alignment \t%lld\n", | 907 | "Virtual address \t0x%pK\n", outmw->io_base); |
783 | (unsigned long long)align_addr); | ||
784 | 908 | ||
785 | off += scnprintf(buf + off, buf_size - off, | 909 | off += scnprintf(buf + off, buf_size - off, |
786 | "Size Alignment \t%lld\n", | 910 | "Phys Address \t%pa[p]\n", &map_base); |
787 | (unsigned long long)align_size); | ||
788 | 911 | ||
789 | off += scnprintf(buf + off, buf_size - off, | 912 | off += scnprintf(buf + off, buf_size - off, |
790 | "Size Max \t%lld\n", | 913 | "Mapping Size \t%pa[p]\n", &map_size); |
791 | (unsigned long long)max_size); | ||
792 | 914 | ||
793 | off += scnprintf(buf + off, buf_size - off, | 915 | off += scnprintf(buf + off, buf_size - off, |
794 | "Ready \t%c\n", | 916 | "Translation Address \t0x%016llx\n", outmw->tr_base); |
795 | (mw->peer) ? 'Y' : 'N'); | ||
796 | 917 | ||
797 | off += scnprintf(buf + off, buf_size - off, | 918 | off += scnprintf(buf + off, buf_size - off, |
798 | "Allocated Size \t%zd\n", | 919 | "Window Size \t%pa[p]\n", &outmw->size); |
799 | (mw->peer) ? (size_t)mw->size : 0); | ||
800 | 920 | ||
801 | ret = simple_read_from_buffer(ubuf, size, offp, buf, off); | 921 | ret = simple_read_from_buffer(ubuf, size, offp, buf, off); |
802 | kfree(buf); | 922 | kfree(buf); |
923 | |||
803 | return ret; | 924 | return ret; |
804 | } | 925 | } |
805 | 926 | ||
@@ -807,12 +928,12 @@ static ssize_t tool_peer_mw_trans_write(struct file *filep, | |||
807 | const char __user *ubuf, | 928 | const char __user *ubuf, |
808 | size_t size, loff_t *offp) | 929 | size_t size, loff_t *offp) |
809 | { | 930 | { |
810 | struct tool_mw *mw = filep->private_data; | 931 | struct tool_mw_wrap *outmw_wrap = filep->private_data; |
811 | 932 | struct tool_mw *outmw = outmw_wrap->mw; | |
812 | char buf[32]; | 933 | size_t buf_size, wsize; |
813 | size_t buf_size; | 934 | char buf[TOOL_BUF_LEN]; |
814 | unsigned long long val; | 935 | int ret, n; |
815 | int rc; | 936 | u64 addr; |
816 | 937 | ||
817 | buf_size = min(size, (sizeof(buf) - 1)); | 938 | buf_size = min(size, (sizeof(buf) - 1)); |
818 | if (copy_from_user(buf, ubuf, buf_size)) | 939 | if (copy_from_user(buf, ubuf, buf_size)) |
@@ -820,16 +941,17 @@ static ssize_t tool_peer_mw_trans_write(struct file *filep, | |||
820 | 941 | ||
821 | buf[buf_size] = '\0'; | 942 | buf[buf_size] = '\0'; |
822 | 943 | ||
823 | rc = kstrtoull(buf, 0, &val); | 944 | n = sscanf(buf, "%lli:%zi", &addr, &wsize); |
824 | if (rc) | 945 | if (n != 2) |
825 | return rc; | 946 | return -EINVAL; |
826 | |||
827 | tool_free_mw(mw->tc, mw->idx); | ||
828 | if (val) | ||
829 | rc = tool_setup_mw(mw->tc, mw->idx, val); | ||
830 | 947 | ||
831 | if (rc) | 948 | tool_free_peer_mw(outmw->tc, outmw->widx); |
832 | return rc; | 949 | if (wsize) { |
950 | ret = tool_setup_peer_mw(outmw->tc, outmw_wrap->pidx, | ||
951 | outmw->widx, addr, wsize); | ||
952 | if (ret) | ||
953 | return ret; | ||
954 | } | ||
833 | 955 | ||
834 | return size; | 956 | return size; |
835 | } | 957 | } |
@@ -838,195 +960,734 @@ static TOOL_FOPS_RDWR(tool_peer_mw_trans_fops, | |||
838 | tool_peer_mw_trans_read, | 960 | tool_peer_mw_trans_read, |
839 | tool_peer_mw_trans_write); | 961 | tool_peer_mw_trans_write); |
840 | 962 | ||
841 | static int tool_init_mw(struct tool_ctx *tc, int idx) | 963 | static int tool_init_mws(struct tool_ctx *tc) |
842 | { | 964 | { |
843 | struct tool_mw *mw = &tc->mws[idx]; | 965 | int widx, pidx; |
844 | phys_addr_t base; | 966 | |
845 | int rc; | 967 | /* Initialize outbound memory windows */ |
846 | 968 | tc->outmw_cnt = ntb_peer_mw_count(tc->ntb); | |
847 | rc = ntb_peer_mw_get_addr(tc->ntb, idx, &base, &mw->win_size); | 969 | tc->outmws = devm_kcalloc(&tc->ntb->dev, tc->outmw_cnt, |
848 | if (rc) | 970 | sizeof(*tc->outmws), GFP_KERNEL); |
849 | return rc; | 971 | if (tc->outmws == NULL) |
850 | 972 | return -ENOMEM; | |
851 | mw->tc = tc; | 973 | |
852 | mw->idx = idx; | 974 | for (widx = 0; widx < tc->outmw_cnt; widx++) { |
853 | mw->local = ioremap_wc(base, mw->win_size); | 975 | tc->outmws[widx].widx = widx; |
854 | if (!mw->local) | 976 | tc->outmws[widx].pidx = -1; |
855 | return -EFAULT; | 977 | tc->outmws[widx].tc = tc; |
978 | } | ||
979 | |||
980 | /* Initialize inbound memory windows and outbound MWs wrapper */ | ||
981 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) { | ||
982 | tc->peers[pidx].inmw_cnt = ntb_mw_count(tc->ntb, pidx); | ||
983 | tc->peers[pidx].inmws = | ||
984 | devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].inmw_cnt, | ||
985 | sizeof(*tc->peers[pidx].inmws), GFP_KERNEL); | ||
986 | if (tc->peers[pidx].inmws == NULL) | ||
987 | return -ENOMEM; | ||
988 | |||
989 | for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) { | ||
990 | tc->peers[pidx].inmws[widx].widx = widx; | ||
991 | tc->peers[pidx].inmws[widx].pidx = pidx; | ||
992 | tc->peers[pidx].inmws[widx].tc = tc; | ||
993 | } | ||
994 | |||
995 | tc->peers[pidx].outmw_cnt = ntb_peer_mw_count(tc->ntb); | ||
996 | tc->peers[pidx].outmws = | ||
997 | devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].outmw_cnt, | ||
998 | sizeof(*tc->peers[pidx].outmws), GFP_KERNEL); | ||
999 | |||
1000 | for (widx = 0; widx < tc->peers[pidx].outmw_cnt; widx++) { | ||
1001 | tc->peers[pidx].outmws[widx].pidx = pidx; | ||
1002 | tc->peers[pidx].outmws[widx].mw = &tc->outmws[widx]; | ||
1003 | } | ||
1004 | } | ||
856 | 1005 | ||
857 | return 0; | 1006 | return 0; |
858 | } | 1007 | } |
859 | 1008 | ||
860 | static void tool_free_mws(struct tool_ctx *tc) | 1009 | static void tool_clear_mws(struct tool_ctx *tc) |
861 | { | 1010 | { |
862 | int i; | 1011 | int widx, pidx; |
1012 | |||
1013 | /* Free outbound memory windows */ | ||
1014 | for (widx = 0; widx < tc->outmw_cnt; widx++) | ||
1015 | tool_free_peer_mw(tc, widx); | ||
863 | 1016 | ||
864 | for (i = 0; i < tc->mw_count; i++) { | 1017 | /* Free outbound memory windows */ |
865 | tool_free_mw(tc, i); | 1018 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) |
1019 | for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) | ||
1020 | tool_free_mw(tc, pidx, widx); | ||
1021 | } | ||
866 | 1022 | ||
867 | if (tc->mws[i].local) | 1023 | /*============================================================================== |
868 | iounmap(tc->mws[i].local); | 1024 | * Doorbell read/write methods |
1025 | *============================================================================== | ||
1026 | */ | ||
869 | 1027 | ||
870 | tc->mws[i].local = NULL; | 1028 | static ssize_t tool_db_read(struct file *filep, char __user *ubuf, |
871 | } | 1029 | size_t size, loff_t *offp) |
1030 | { | ||
1031 | struct tool_ctx *tc = filep->private_data; | ||
1032 | |||
1033 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->db_read); | ||
872 | } | 1034 | } |
873 | 1035 | ||
874 | static void tool_setup_dbgfs(struct tool_ctx *tc) | 1036 | static ssize_t tool_db_write(struct file *filep, const char __user *ubuf, |
1037 | size_t size, loff_t *offp) | ||
875 | { | 1038 | { |
876 | int i; | 1039 | struct tool_ctx *tc = filep->private_data; |
877 | 1040 | ||
878 | /* This modules is useless without dbgfs... */ | 1041 | return tool_fn_write(tc, ubuf, size, offp, tc->ntb->ops->db_set, |
879 | if (!tool_dbgfs) { | 1042 | tc->ntb->ops->db_clear); |
880 | tc->dbgfs = NULL; | 1043 | } |
881 | return; | 1044 | |
1045 | static TOOL_FOPS_RDWR(tool_db_fops, | ||
1046 | tool_db_read, | ||
1047 | tool_db_write); | ||
1048 | |||
1049 | static ssize_t tool_db_valid_mask_read(struct file *filep, char __user *ubuf, | ||
1050 | size_t size, loff_t *offp) | ||
1051 | { | ||
1052 | struct tool_ctx *tc = filep->private_data; | ||
1053 | |||
1054 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->db_valid_mask); | ||
1055 | } | ||
1056 | |||
1057 | static TOOL_FOPS_RDWR(tool_db_valid_mask_fops, | ||
1058 | tool_db_valid_mask_read, | ||
1059 | NULL); | ||
1060 | |||
1061 | static ssize_t tool_db_mask_read(struct file *filep, char __user *ubuf, | ||
1062 | size_t size, loff_t *offp) | ||
1063 | { | ||
1064 | struct tool_ctx *tc = filep->private_data; | ||
1065 | |||
1066 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->db_read_mask); | ||
1067 | } | ||
1068 | |||
1069 | static ssize_t tool_db_mask_write(struct file *filep, const char __user *ubuf, | ||
1070 | size_t size, loff_t *offp) | ||
1071 | { | ||
1072 | struct tool_ctx *tc = filep->private_data; | ||
1073 | |||
1074 | return tool_fn_write(tc, ubuf, size, offp, tc->ntb->ops->db_set_mask, | ||
1075 | tc->ntb->ops->db_clear_mask); | ||
1076 | } | ||
1077 | |||
1078 | static TOOL_FOPS_RDWR(tool_db_mask_fops, | ||
1079 | tool_db_mask_read, | ||
1080 | tool_db_mask_write); | ||
1081 | |||
1082 | static ssize_t tool_peer_db_read(struct file *filep, char __user *ubuf, | ||
1083 | size_t size, loff_t *offp) | ||
1084 | { | ||
1085 | struct tool_ctx *tc = filep->private_data; | ||
1086 | |||
1087 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->peer_db_read); | ||
1088 | } | ||
1089 | |||
1090 | static ssize_t tool_peer_db_write(struct file *filep, const char __user *ubuf, | ||
1091 | size_t size, loff_t *offp) | ||
1092 | { | ||
1093 | struct tool_ctx *tc = filep->private_data; | ||
1094 | |||
1095 | return tool_fn_write(tc, ubuf, size, offp, tc->ntb->ops->peer_db_set, | ||
1096 | tc->ntb->ops->peer_db_clear); | ||
1097 | } | ||
1098 | |||
1099 | static TOOL_FOPS_RDWR(tool_peer_db_fops, | ||
1100 | tool_peer_db_read, | ||
1101 | tool_peer_db_write); | ||
1102 | |||
1103 | static ssize_t tool_peer_db_mask_read(struct file *filep, char __user *ubuf, | ||
1104 | size_t size, loff_t *offp) | ||
1105 | { | ||
1106 | struct tool_ctx *tc = filep->private_data; | ||
1107 | |||
1108 | return tool_fn_read(tc, ubuf, size, offp, | ||
1109 | tc->ntb->ops->peer_db_read_mask); | ||
1110 | } | ||
1111 | |||
1112 | static ssize_t tool_peer_db_mask_write(struct file *filep, | ||
1113 | const char __user *ubuf, | ||
1114 | size_t size, loff_t *offp) | ||
1115 | { | ||
1116 | struct tool_ctx *tc = filep->private_data; | ||
1117 | |||
1118 | return tool_fn_write(tc, ubuf, size, offp, | ||
1119 | tc->ntb->ops->peer_db_set_mask, | ||
1120 | tc->ntb->ops->peer_db_clear_mask); | ||
1121 | } | ||
1122 | |||
1123 | static TOOL_FOPS_RDWR(tool_peer_db_mask_fops, | ||
1124 | tool_peer_db_mask_read, | ||
1125 | tool_peer_db_mask_write); | ||
1126 | |||
1127 | static ssize_t tool_db_event_write(struct file *filep, | ||
1128 | const char __user *ubuf, | ||
1129 | size_t size, loff_t *offp) | ||
1130 | { | ||
1131 | struct tool_ctx *tc = filep->private_data; | ||
1132 | u64 val; | ||
1133 | int ret; | ||
1134 | |||
1135 | ret = kstrtou64_from_user(ubuf, size, 0, &val); | ||
1136 | if (ret) | ||
1137 | return ret; | ||
1138 | |||
1139 | if (wait_event_interruptible(tc->db_wq, ntb_db_read(tc->ntb) == val)) | ||
1140 | return -ERESTART; | ||
1141 | |||
1142 | return size; | ||
1143 | } | ||
1144 | |||
1145 | static TOOL_FOPS_RDWR(tool_db_event_fops, | ||
1146 | NULL, | ||
1147 | tool_db_event_write); | ||
1148 | |||
1149 | /*============================================================================== | ||
1150 | * Scratchpads read/write methods | ||
1151 | *============================================================================== | ||
1152 | */ | ||
1153 | |||
1154 | static ssize_t tool_spad_read(struct file *filep, char __user *ubuf, | ||
1155 | size_t size, loff_t *offp) | ||
1156 | { | ||
1157 | struct tool_spad *spad = filep->private_data; | ||
1158 | char buf[TOOL_BUF_LEN]; | ||
1159 | ssize_t pos; | ||
1160 | |||
1161 | if (!spad->tc->ntb->ops->spad_read) | ||
1162 | return -EINVAL; | ||
1163 | |||
1164 | pos = scnprintf(buf, sizeof(buf), "%#x\n", | ||
1165 | ntb_spad_read(spad->tc->ntb, spad->sidx)); | ||
1166 | |||
1167 | return simple_read_from_buffer(ubuf, size, offp, buf, pos); | ||
1168 | } | ||
1169 | |||
1170 | static ssize_t tool_spad_write(struct file *filep, const char __user *ubuf, | ||
1171 | size_t size, loff_t *offp) | ||
1172 | { | ||
1173 | struct tool_spad *spad = filep->private_data; | ||
1174 | u32 val; | ||
1175 | int ret; | ||
1176 | |||
1177 | if (!spad->tc->ntb->ops->spad_write) { | ||
1178 | dev_dbg(&spad->tc->ntb->dev, "no spad write fn\n"); | ||
1179 | return -EINVAL; | ||
882 | } | 1180 | } |
883 | 1181 | ||
884 | tc->dbgfs = debugfs_create_dir(dev_name(&tc->ntb->dev), | 1182 | ret = kstrtou32_from_user(ubuf, size, 0, &val); |
885 | tool_dbgfs); | 1183 | if (ret) |
886 | if (!tc->dbgfs) | 1184 | return ret; |
887 | return; | ||
888 | 1185 | ||
889 | debugfs_create_file("db", S_IRUSR | S_IWUSR, tc->dbgfs, | 1186 | ret = ntb_spad_write(spad->tc->ntb, spad->sidx, val); |
890 | tc, &tool_db_fops); | ||
891 | 1187 | ||
892 | debugfs_create_file("mask", S_IRUSR | S_IWUSR, tc->dbgfs, | 1188 | return ret ?: size; |
893 | tc, &tool_mask_fops); | 1189 | } |
894 | 1190 | ||
895 | debugfs_create_file("peer_db", S_IRUSR | S_IWUSR, tc->dbgfs, | 1191 | static TOOL_FOPS_RDWR(tool_spad_fops, |
896 | tc, &tool_peer_db_fops); | 1192 | tool_spad_read, |
1193 | tool_spad_write); | ||
897 | 1194 | ||
898 | debugfs_create_file("peer_mask", S_IRUSR | S_IWUSR, tc->dbgfs, | 1195 | static ssize_t tool_peer_spad_read(struct file *filep, char __user *ubuf, |
899 | tc, &tool_peer_mask_fops); | 1196 | size_t size, loff_t *offp) |
1197 | { | ||
1198 | struct tool_spad *spad = filep->private_data; | ||
1199 | char buf[TOOL_BUF_LEN]; | ||
1200 | ssize_t pos; | ||
900 | 1201 | ||
901 | debugfs_create_file("spad", S_IRUSR | S_IWUSR, tc->dbgfs, | 1202 | if (!spad->tc->ntb->ops->peer_spad_read) |
902 | tc, &tool_spad_fops); | 1203 | return -EINVAL; |
903 | 1204 | ||
904 | debugfs_create_file("peer_spad", S_IRUSR | S_IWUSR, tc->dbgfs, | 1205 | pos = scnprintf(buf, sizeof(buf), "%#x\n", |
905 | tc, &tool_peer_spad_fops); | 1206 | ntb_peer_spad_read(spad->tc->ntb, spad->pidx, spad->sidx)); |
906 | 1207 | ||
907 | debugfs_create_file("link", S_IRUSR | S_IWUSR, tc->dbgfs, | 1208 | return simple_read_from_buffer(ubuf, size, offp, buf, pos); |
908 | tc, &tool_link_fops); | 1209 | } |
909 | 1210 | ||
910 | debugfs_create_file("link_event", S_IWUSR, tc->dbgfs, | 1211 | static ssize_t tool_peer_spad_write(struct file *filep, const char __user *ubuf, |
911 | tc, &tool_link_event_fops); | 1212 | size_t size, loff_t *offp) |
1213 | { | ||
1214 | struct tool_spad *spad = filep->private_data; | ||
1215 | u32 val; | ||
1216 | int ret; | ||
1217 | |||
1218 | if (!spad->tc->ntb->ops->peer_spad_write) { | ||
1219 | dev_dbg(&spad->tc->ntb->dev, "no spad write fn\n"); | ||
1220 | return -EINVAL; | ||
1221 | } | ||
1222 | |||
1223 | ret = kstrtou32_from_user(ubuf, size, 0, &val); | ||
1224 | if (ret) | ||
1225 | return ret; | ||
1226 | |||
1227 | ret = ntb_peer_spad_write(spad->tc->ntb, spad->pidx, spad->sidx, val); | ||
1228 | |||
1229 | return ret ?: size; | ||
1230 | } | ||
912 | 1231 | ||
913 | for (i = 0; i < tc->mw_count; i++) { | 1232 | static TOOL_FOPS_RDWR(tool_peer_spad_fops, |
914 | char buf[30]; | 1233 | tool_peer_spad_read, |
1234 | tool_peer_spad_write); | ||
915 | 1235 | ||
916 | snprintf(buf, sizeof(buf), "mw%d", i); | 1236 | static int tool_init_spads(struct tool_ctx *tc) |
917 | debugfs_create_file(buf, S_IRUSR | S_IWUSR, tc->dbgfs, | 1237 | { |
918 | &tc->mws[i], &tool_mw_fops); | 1238 | int sidx, pidx; |
1239 | |||
1240 | /* Initialize inbound scratchpad structures */ | ||
1241 | tc->inspad_cnt = ntb_spad_count(tc->ntb); | ||
1242 | tc->inspads = devm_kcalloc(&tc->ntb->dev, tc->inspad_cnt, | ||
1243 | sizeof(*tc->inspads), GFP_KERNEL); | ||
1244 | if (tc->inspads == NULL) | ||
1245 | return -ENOMEM; | ||
1246 | |||
1247 | for (sidx = 0; sidx < tc->inspad_cnt; sidx++) { | ||
1248 | tc->inspads[sidx].sidx = sidx; | ||
1249 | tc->inspads[sidx].pidx = -1; | ||
1250 | tc->inspads[sidx].tc = tc; | ||
1251 | } | ||
919 | 1252 | ||
920 | snprintf(buf, sizeof(buf), "peer_trans%d", i); | 1253 | /* Initialize outbound scratchpad structures */ |
921 | debugfs_create_file(buf, S_IRUSR | S_IWUSR, tc->dbgfs, | 1254 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) { |
922 | &tc->mws[i], &tool_peer_mw_trans_fops); | 1255 | tc->peers[pidx].outspad_cnt = ntb_spad_count(tc->ntb); |
1256 | tc->peers[pidx].outspads = | ||
1257 | devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].outspad_cnt, | ||
1258 | sizeof(*tc->peers[pidx].outspads), GFP_KERNEL); | ||
1259 | if (tc->peers[pidx].outspads == NULL) | ||
1260 | return -ENOMEM; | ||
1261 | |||
1262 | for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) { | ||
1263 | tc->peers[pidx].outspads[sidx].sidx = sidx; | ||
1264 | tc->peers[pidx].outspads[sidx].pidx = pidx; | ||
1265 | tc->peers[pidx].outspads[sidx].tc = tc; | ||
1266 | } | ||
923 | } | 1267 | } |
1268 | |||
1269 | return 0; | ||
924 | } | 1270 | } |
925 | 1271 | ||
926 | static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb) | 1272 | /*============================================================================== |
1273 | * Messages read/write methods | ||
1274 | *============================================================================== | ||
1275 | */ | ||
1276 | |||
1277 | static ssize_t tool_inmsg_read(struct file *filep, char __user *ubuf, | ||
1278 | size_t size, loff_t *offp) | ||
927 | { | 1279 | { |
928 | struct tool_ctx *tc; | 1280 | struct tool_msg *msg = filep->private_data; |
929 | int rc; | 1281 | char buf[TOOL_BUF_LEN]; |
930 | int i; | 1282 | ssize_t pos; |
1283 | u32 data; | ||
1284 | int pidx; | ||
1285 | |||
1286 | data = ntb_msg_read(msg->tc->ntb, &pidx, msg->midx); | ||
1287 | |||
1288 | pos = scnprintf(buf, sizeof(buf), "0x%08x<-%d\n", data, pidx); | ||
1289 | |||
1290 | return simple_read_from_buffer(ubuf, size, offp, buf, pos); | ||
1291 | } | ||
1292 | |||
1293 | static TOOL_FOPS_RDWR(tool_inmsg_fops, | ||
1294 | tool_inmsg_read, | ||
1295 | NULL); | ||
1296 | |||
1297 | static ssize_t tool_outmsg_write(struct file *filep, | ||
1298 | const char __user *ubuf, | ||
1299 | size_t size, loff_t *offp) | ||
1300 | { | ||
1301 | struct tool_msg *msg = filep->private_data; | ||
1302 | u32 val; | ||
1303 | int ret; | ||
1304 | |||
1305 | ret = kstrtou32_from_user(ubuf, size, 0, &val); | ||
1306 | if (ret) | ||
1307 | return ret; | ||
1308 | |||
1309 | ret = ntb_peer_msg_write(msg->tc->ntb, msg->pidx, msg->midx, val); | ||
1310 | |||
1311 | return ret ? : size; | ||
1312 | } | ||
1313 | |||
1314 | static TOOL_FOPS_RDWR(tool_outmsg_fops, | ||
1315 | NULL, | ||
1316 | tool_outmsg_write); | ||
1317 | |||
1318 | static ssize_t tool_msg_sts_read(struct file *filep, char __user *ubuf, | ||
1319 | size_t size, loff_t *offp) | ||
1320 | { | ||
1321 | struct tool_ctx *tc = filep->private_data; | ||
1322 | |||
1323 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->msg_read_sts); | ||
1324 | } | ||
1325 | |||
1326 | static ssize_t tool_msg_sts_write(struct file *filep, const char __user *ubuf, | ||
1327 | size_t size, loff_t *offp) | ||
1328 | { | ||
1329 | struct tool_ctx *tc = filep->private_data; | ||
1330 | |||
1331 | return tool_fn_write(tc, ubuf, size, offp, NULL, | ||
1332 | tc->ntb->ops->msg_clear_sts); | ||
1333 | } | ||
1334 | |||
1335 | static TOOL_FOPS_RDWR(tool_msg_sts_fops, | ||
1336 | tool_msg_sts_read, | ||
1337 | tool_msg_sts_write); | ||
1338 | |||
1339 | static ssize_t tool_msg_inbits_read(struct file *filep, char __user *ubuf, | ||
1340 | size_t size, loff_t *offp) | ||
1341 | { | ||
1342 | struct tool_ctx *tc = filep->private_data; | ||
1343 | |||
1344 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->msg_inbits); | ||
1345 | } | ||
1346 | |||
1347 | static TOOL_FOPS_RDWR(tool_msg_inbits_fops, | ||
1348 | tool_msg_inbits_read, | ||
1349 | NULL); | ||
931 | 1350 | ||
932 | if (!ntb->ops->mw_set_trans) { | 1351 | static ssize_t tool_msg_outbits_read(struct file *filep, char __user *ubuf, |
933 | dev_dbg(&ntb->dev, "need inbound MW based NTB API\n"); | 1352 | size_t size, loff_t *offp) |
934 | rc = -EINVAL; | 1353 | { |
935 | goto err_tc; | 1354 | struct tool_ctx *tc = filep->private_data; |
1355 | |||
1356 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->msg_outbits); | ||
1357 | } | ||
1358 | |||
1359 | static TOOL_FOPS_RDWR(tool_msg_outbits_fops, | ||
1360 | tool_msg_outbits_read, | ||
1361 | NULL); | ||
1362 | |||
1363 | static ssize_t tool_msg_mask_write(struct file *filep, const char __user *ubuf, | ||
1364 | size_t size, loff_t *offp) | ||
1365 | { | ||
1366 | struct tool_ctx *tc = filep->private_data; | ||
1367 | |||
1368 | return tool_fn_write(tc, ubuf, size, offp, | ||
1369 | tc->ntb->ops->msg_set_mask, | ||
1370 | tc->ntb->ops->msg_clear_mask); | ||
1371 | } | ||
1372 | |||
1373 | static TOOL_FOPS_RDWR(tool_msg_mask_fops, | ||
1374 | NULL, | ||
1375 | tool_msg_mask_write); | ||
1376 | |||
1377 | static ssize_t tool_msg_event_write(struct file *filep, | ||
1378 | const char __user *ubuf, | ||
1379 | size_t size, loff_t *offp) | ||
1380 | { | ||
1381 | struct tool_ctx *tc = filep->private_data; | ||
1382 | u64 val; | ||
1383 | int ret; | ||
1384 | |||
1385 | ret = kstrtou64_from_user(ubuf, size, 0, &val); | ||
1386 | if (ret) | ||
1387 | return ret; | ||
1388 | |||
1389 | if (wait_event_interruptible(tc->msg_wq, | ||
1390 | ntb_msg_read_sts(tc->ntb) == val)) | ||
1391 | return -ERESTART; | ||
1392 | |||
1393 | return size; | ||
1394 | } | ||
1395 | |||
1396 | static TOOL_FOPS_RDWR(tool_msg_event_fops, | ||
1397 | NULL, | ||
1398 | tool_msg_event_write); | ||
1399 | |||
1400 | static int tool_init_msgs(struct tool_ctx *tc) | ||
1401 | { | ||
1402 | int midx, pidx; | ||
1403 | |||
1404 | /* Initialize inbound message structures */ | ||
1405 | tc->inmsg_cnt = ntb_msg_count(tc->ntb); | ||
1406 | tc->inmsgs = devm_kcalloc(&tc->ntb->dev, tc->inmsg_cnt, | ||
1407 | sizeof(*tc->inmsgs), GFP_KERNEL); | ||
1408 | if (tc->inmsgs == NULL) | ||
1409 | return -ENOMEM; | ||
1410 | |||
1411 | for (midx = 0; midx < tc->inmsg_cnt; midx++) { | ||
1412 | tc->inmsgs[midx].midx = midx; | ||
1413 | tc->inmsgs[midx].pidx = -1; | ||
1414 | tc->inmsgs[midx].tc = tc; | ||
936 | } | 1415 | } |
937 | 1416 | ||
938 | if (ntb_spad_count(ntb) < 1) { | 1417 | /* Initialize outbound message structures */ |
939 | dev_dbg(&ntb->dev, "no enough scratchpads\n"); | 1418 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) { |
940 | rc = -EINVAL; | 1419 | tc->peers[pidx].outmsg_cnt = ntb_msg_count(tc->ntb); |
941 | goto err_tc; | 1420 | tc->peers[pidx].outmsgs = |
1421 | devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].outmsg_cnt, | ||
1422 | sizeof(*tc->peers[pidx].outmsgs), GFP_KERNEL); | ||
1423 | if (tc->peers[pidx].outmsgs == NULL) | ||
1424 | return -ENOMEM; | ||
1425 | |||
1426 | for (midx = 0; midx < tc->peers[pidx].outmsg_cnt; midx++) { | ||
1427 | tc->peers[pidx].outmsgs[midx].midx = midx; | ||
1428 | tc->peers[pidx].outmsgs[midx].pidx = pidx; | ||
1429 | tc->peers[pidx].outmsgs[midx].tc = tc; | ||
1430 | } | ||
942 | } | 1431 | } |
943 | 1432 | ||
1433 | return 0; | ||
1434 | } | ||
1435 | |||
1436 | /*============================================================================== | ||
1437 | * Initialization methods | ||
1438 | *============================================================================== | ||
1439 | */ | ||
1440 | |||
1441 | static struct tool_ctx *tool_create_data(struct ntb_dev *ntb) | ||
1442 | { | ||
1443 | struct tool_ctx *tc; | ||
1444 | |||
1445 | tc = devm_kzalloc(&ntb->dev, sizeof(*tc), GFP_KERNEL); | ||
1446 | if (tc == NULL) | ||
1447 | return ERR_PTR(-ENOMEM); | ||
1448 | |||
1449 | tc->ntb = ntb; | ||
1450 | init_waitqueue_head(&tc->link_wq); | ||
1451 | init_waitqueue_head(&tc->db_wq); | ||
1452 | init_waitqueue_head(&tc->msg_wq); | ||
1453 | |||
944 | if (ntb_db_is_unsafe(ntb)) | 1454 | if (ntb_db_is_unsafe(ntb)) |
945 | dev_dbg(&ntb->dev, "doorbell is unsafe\n"); | 1455 | dev_dbg(&ntb->dev, "doorbell is unsafe\n"); |
946 | 1456 | ||
947 | if (ntb_spad_is_unsafe(ntb)) | 1457 | if (ntb_spad_is_unsafe(ntb)) |
948 | dev_dbg(&ntb->dev, "scratchpad is unsafe\n"); | 1458 | dev_dbg(&ntb->dev, "scratchpad is unsafe\n"); |
949 | 1459 | ||
950 | if (ntb_peer_port_count(ntb) != NTB_DEF_PEER_CNT) | 1460 | return tc; |
951 | dev_warn(&ntb->dev, "multi-port NTB is unsupported\n"); | 1461 | } |
1462 | |||
1463 | static void tool_clear_data(struct tool_ctx *tc) | ||
1464 | { | ||
1465 | wake_up(&tc->link_wq); | ||
1466 | wake_up(&tc->db_wq); | ||
1467 | wake_up(&tc->msg_wq); | ||
1468 | } | ||
1469 | |||
1470 | static int tool_init_ntb(struct tool_ctx *tc) | ||
1471 | { | ||
1472 | return ntb_set_ctx(tc->ntb, tc, &tool_ops); | ||
1473 | } | ||
952 | 1474 | ||
953 | tc = kzalloc(sizeof(*tc), GFP_KERNEL); | 1475 | static void tool_clear_ntb(struct tool_ctx *tc) |
954 | if (!tc) { | 1476 | { |
955 | rc = -ENOMEM; | 1477 | ntb_clear_ctx(tc->ntb); |
956 | goto err_tc; | 1478 | ntb_link_disable(tc->ntb); |
1479 | } | ||
1480 | |||
1481 | static void tool_setup_dbgfs(struct tool_ctx *tc) | ||
1482 | { | ||
1483 | int pidx, widx, sidx, midx; | ||
1484 | char buf[TOOL_BUF_LEN]; | ||
1485 | |||
1486 | /* This modules is useless without dbgfs... */ | ||
1487 | if (!tool_dbgfs_topdir) { | ||
1488 | tc->dbgfs_dir = NULL; | ||
1489 | return; | ||
957 | } | 1490 | } |
958 | 1491 | ||
959 | tc->ntb = ntb; | 1492 | tc->dbgfs_dir = debugfs_create_dir(dev_name(&tc->ntb->dev), |
960 | init_waitqueue_head(&tc->link_wq); | 1493 | tool_dbgfs_topdir); |
1494 | if (!tc->dbgfs_dir) | ||
1495 | return; | ||
1496 | |||
1497 | debugfs_create_file("port", 0600, tc->dbgfs_dir, | ||
1498 | tc, &tool_port_fops); | ||
1499 | |||
1500 | debugfs_create_file("link", 0600, tc->dbgfs_dir, | ||
1501 | tc, &tool_link_fops); | ||
1502 | |||
1503 | debugfs_create_file("db", 0600, tc->dbgfs_dir, | ||
1504 | tc, &tool_db_fops); | ||
1505 | |||
1506 | debugfs_create_file("db_valid_mask", 0600, tc->dbgfs_dir, | ||
1507 | tc, &tool_db_valid_mask_fops); | ||
1508 | |||
1509 | debugfs_create_file("db_mask", 0600, tc->dbgfs_dir, | ||
1510 | tc, &tool_db_mask_fops); | ||
961 | 1511 | ||
962 | tc->mw_count = min(ntb_peer_mw_count(tc->ntb), MAX_MWS); | 1512 | debugfs_create_file("db_event", 0600, tc->dbgfs_dir, |
963 | for (i = 0; i < tc->mw_count; i++) { | 1513 | tc, &tool_db_event_fops); |
964 | rc = tool_init_mw(tc, i); | 1514 | |
965 | if (rc) | 1515 | debugfs_create_file("peer_db", 0600, tc->dbgfs_dir, |
966 | goto err_ctx; | 1516 | tc, &tool_peer_db_fops); |
1517 | |||
1518 | debugfs_create_file("peer_db_mask", 0600, tc->dbgfs_dir, | ||
1519 | tc, &tool_peer_db_mask_fops); | ||
1520 | |||
1521 | if (tc->inspad_cnt != 0) { | ||
1522 | for (sidx = 0; sidx < tc->inspad_cnt; sidx++) { | ||
1523 | snprintf(buf, sizeof(buf), "spad%d", sidx); | ||
1524 | |||
1525 | debugfs_create_file(buf, 0600, tc->dbgfs_dir, | ||
1526 | &tc->inspads[sidx], &tool_spad_fops); | ||
1527 | } | ||
967 | } | 1528 | } |
968 | 1529 | ||
969 | tool_setup_dbgfs(tc); | 1530 | if (tc->inmsg_cnt != 0) { |
1531 | for (midx = 0; midx < tc->inmsg_cnt; midx++) { | ||
1532 | snprintf(buf, sizeof(buf), "msg%d", midx); | ||
1533 | debugfs_create_file(buf, 0600, tc->dbgfs_dir, | ||
1534 | &tc->inmsgs[midx], &tool_inmsg_fops); | ||
1535 | } | ||
1536 | |||
1537 | debugfs_create_file("msg_sts", 0600, tc->dbgfs_dir, | ||
1538 | tc, &tool_msg_sts_fops); | ||
1539 | |||
1540 | debugfs_create_file("msg_inbits", 0600, tc->dbgfs_dir, | ||
1541 | tc, &tool_msg_inbits_fops); | ||
970 | 1542 | ||
971 | rc = ntb_set_ctx(ntb, tc, &tool_ops); | 1543 | debugfs_create_file("msg_outbits", 0600, tc->dbgfs_dir, |
972 | if (rc) | 1544 | tc, &tool_msg_outbits_fops); |
973 | goto err_ctx; | ||
974 | 1545 | ||
975 | ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); | 1546 | debugfs_create_file("msg_mask", 0600, tc->dbgfs_dir, |
976 | ntb_link_event(ntb); | 1547 | tc, &tool_msg_mask_fops); |
1548 | |||
1549 | debugfs_create_file("msg_event", 0600, tc->dbgfs_dir, | ||
1550 | tc, &tool_msg_event_fops); | ||
1551 | } | ||
1552 | |||
1553 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) { | ||
1554 | snprintf(buf, sizeof(buf), "peer%d", pidx); | ||
1555 | tc->peers[pidx].dbgfs_dir = | ||
1556 | debugfs_create_dir(buf, tc->dbgfs_dir); | ||
1557 | |||
1558 | debugfs_create_file("port", 0600, | ||
1559 | tc->peers[pidx].dbgfs_dir, | ||
1560 | &tc->peers[pidx], &tool_peer_port_fops); | ||
1561 | |||
1562 | debugfs_create_file("link", 0200, | ||
1563 | tc->peers[pidx].dbgfs_dir, | ||
1564 | &tc->peers[pidx], &tool_peer_link_fops); | ||
1565 | |||
1566 | debugfs_create_file("link_event", 0200, | ||
1567 | tc->peers[pidx].dbgfs_dir, | ||
1568 | &tc->peers[pidx], &tool_peer_link_event_fops); | ||
1569 | |||
1570 | for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) { | ||
1571 | snprintf(buf, sizeof(buf), "mw_trans%d", widx); | ||
1572 | debugfs_create_file(buf, 0600, | ||
1573 | tc->peers[pidx].dbgfs_dir, | ||
1574 | &tc->peers[pidx].inmws[widx], | ||
1575 | &tool_mw_trans_fops); | ||
1576 | } | ||
1577 | |||
1578 | for (widx = 0; widx < tc->peers[pidx].outmw_cnt; widx++) { | ||
1579 | snprintf(buf, sizeof(buf), "peer_mw_trans%d", widx); | ||
1580 | debugfs_create_file(buf, 0600, | ||
1581 | tc->peers[pidx].dbgfs_dir, | ||
1582 | &tc->peers[pidx].outmws[widx], | ||
1583 | &tool_peer_mw_trans_fops); | ||
1584 | } | ||
1585 | |||
1586 | for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) { | ||
1587 | snprintf(buf, sizeof(buf), "spad%d", sidx); | ||
1588 | |||
1589 | debugfs_create_file(buf, 0600, | ||
1590 | tc->peers[pidx].dbgfs_dir, | ||
1591 | &tc->peers[pidx].outspads[sidx], | ||
1592 | &tool_peer_spad_fops); | ||
1593 | } | ||
1594 | |||
1595 | for (midx = 0; midx < tc->peers[pidx].outmsg_cnt; midx++) { | ||
1596 | snprintf(buf, sizeof(buf), "msg%d", midx); | ||
1597 | debugfs_create_file(buf, 0600, | ||
1598 | tc->peers[pidx].dbgfs_dir, | ||
1599 | &tc->peers[pidx].outmsgs[midx], | ||
1600 | &tool_outmsg_fops); | ||
1601 | } | ||
1602 | } | ||
1603 | } | ||
1604 | |||
1605 | static void tool_clear_dbgfs(struct tool_ctx *tc) | ||
1606 | { | ||
1607 | debugfs_remove_recursive(tc->dbgfs_dir); | ||
1608 | } | ||
1609 | |||
1610 | static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb) | ||
1611 | { | ||
1612 | struct tool_ctx *tc; | ||
1613 | int ret; | ||
1614 | |||
1615 | tc = tool_create_data(ntb); | ||
1616 | if (IS_ERR(tc)) | ||
1617 | return PTR_ERR(tc); | ||
1618 | |||
1619 | ret = tool_init_peers(tc); | ||
1620 | if (ret != 0) | ||
1621 | goto err_clear_data; | ||
1622 | |||
1623 | ret = tool_init_mws(tc); | ||
1624 | if (ret != 0) | ||
1625 | goto err_clear_data; | ||
1626 | |||
1627 | ret = tool_init_spads(tc); | ||
1628 | if (ret != 0) | ||
1629 | goto err_clear_mws; | ||
1630 | |||
1631 | ret = tool_init_msgs(tc); | ||
1632 | if (ret != 0) | ||
1633 | goto err_clear_mws; | ||
1634 | |||
1635 | ret = tool_init_ntb(tc); | ||
1636 | if (ret != 0) | ||
1637 | goto err_clear_mws; | ||
1638 | |||
1639 | tool_setup_dbgfs(tc); | ||
977 | 1640 | ||
978 | return 0; | 1641 | return 0; |
979 | 1642 | ||
980 | err_ctx: | 1643 | err_clear_mws: |
981 | tool_free_mws(tc); | 1644 | tool_clear_mws(tc); |
982 | debugfs_remove_recursive(tc->dbgfs); | 1645 | |
983 | kfree(tc); | 1646 | err_clear_data: |
984 | err_tc: | 1647 | tool_clear_data(tc); |
985 | return rc; | 1648 | |
1649 | return ret; | ||
986 | } | 1650 | } |
987 | 1651 | ||
988 | static void tool_remove(struct ntb_client *self, struct ntb_dev *ntb) | 1652 | static void tool_remove(struct ntb_client *self, struct ntb_dev *ntb) |
989 | { | 1653 | { |
990 | struct tool_ctx *tc = ntb->ctx; | 1654 | struct tool_ctx *tc = ntb->ctx; |
991 | 1655 | ||
992 | tool_free_mws(tc); | 1656 | tool_clear_dbgfs(tc); |
993 | 1657 | ||
994 | ntb_clear_ctx(ntb); | 1658 | tool_clear_ntb(tc); |
995 | ntb_link_disable(ntb); | ||
996 | 1659 | ||
997 | debugfs_remove_recursive(tc->dbgfs); | 1660 | tool_clear_mws(tc); |
998 | kfree(tc); | 1661 | |
1662 | tool_clear_data(tc); | ||
999 | } | 1663 | } |
1000 | 1664 | ||
1001 | static struct ntb_client tool_client = { | 1665 | static struct ntb_client tool_client = { |
1002 | .ops = { | 1666 | .ops = { |
1003 | .probe = tool_probe, | 1667 | .probe = tool_probe, |
1004 | .remove = tool_remove, | 1668 | .remove = tool_remove, |
1005 | }, | 1669 | } |
1006 | }; | 1670 | }; |
1007 | 1671 | ||
1008 | static int __init tool_init(void) | 1672 | static int __init tool_init(void) |
1009 | { | 1673 | { |
1010 | int rc; | 1674 | int ret; |
1011 | 1675 | ||
1012 | if (debugfs_initialized()) | 1676 | if (debugfs_initialized()) |
1013 | tool_dbgfs = debugfs_create_dir(KBUILD_MODNAME, NULL); | 1677 | tool_dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL); |
1014 | 1678 | ||
1015 | rc = ntb_register_client(&tool_client); | 1679 | ret = ntb_register_client(&tool_client); |
1016 | if (rc) | 1680 | if (ret) |
1017 | goto err_client; | 1681 | debugfs_remove_recursive(tool_dbgfs_topdir); |
1018 | 1682 | ||
1019 | return 0; | 1683 | return ret; |
1020 | |||
1021 | err_client: | ||
1022 | debugfs_remove_recursive(tool_dbgfs); | ||
1023 | return rc; | ||
1024 | } | 1684 | } |
1025 | module_init(tool_init); | 1685 | module_init(tool_init); |
1026 | 1686 | ||
1027 | static void __exit tool_exit(void) | 1687 | static void __exit tool_exit(void) |
1028 | { | 1688 | { |
1029 | ntb_unregister_client(&tool_client); | 1689 | ntb_unregister_client(&tool_client); |
1030 | debugfs_remove_recursive(tool_dbgfs); | 1690 | debugfs_remove_recursive(tool_dbgfs_topdir); |
1031 | } | 1691 | } |
1032 | module_exit(tool_exit); | 1692 | module_exit(tool_exit); |
1693 | |||
diff --git a/include/linux/ntb.h b/include/linux/ntb.h index c308964777eb..181d16601dd9 100644 --- a/include/linux/ntb.h +++ b/include/linux/ntb.h | |||
@@ -71,6 +71,7 @@ struct pci_dev; | |||
71 | * @NTB_TOPO_B2B_USD: On primary side of local ntb upstream of remote ntb. | 71 | * @NTB_TOPO_B2B_USD: On primary side of local ntb upstream of remote ntb. |
72 | * @NTB_TOPO_B2B_DSD: On primary side of local ntb downstream of remote ntb. | 72 | * @NTB_TOPO_B2B_DSD: On primary side of local ntb downstream of remote ntb. |
73 | * @NTB_TOPO_SWITCH: Connected via a switch which supports ntb. | 73 | * @NTB_TOPO_SWITCH: Connected via a switch which supports ntb. |
74 | * @NTB_TOPO_CROSSLINK: Connected via two symmetric switchecs | ||
74 | */ | 75 | */ |
75 | enum ntb_topo { | 76 | enum ntb_topo { |
76 | NTB_TOPO_NONE = -1, | 77 | NTB_TOPO_NONE = -1, |
@@ -79,6 +80,7 @@ enum ntb_topo { | |||
79 | NTB_TOPO_B2B_USD, | 80 | NTB_TOPO_B2B_USD, |
80 | NTB_TOPO_B2B_DSD, | 81 | NTB_TOPO_B2B_DSD, |
81 | NTB_TOPO_SWITCH, | 82 | NTB_TOPO_SWITCH, |
83 | NTB_TOPO_CROSSLINK, | ||
82 | }; | 84 | }; |
83 | 85 | ||
84 | static inline int ntb_topo_is_b2b(enum ntb_topo topo) | 86 | static inline int ntb_topo_is_b2b(enum ntb_topo topo) |
@@ -94,12 +96,13 @@ static inline int ntb_topo_is_b2b(enum ntb_topo topo) | |||
94 | static inline char *ntb_topo_string(enum ntb_topo topo) | 96 | static inline char *ntb_topo_string(enum ntb_topo topo) |
95 | { | 97 | { |
96 | switch (topo) { | 98 | switch (topo) { |
97 | case NTB_TOPO_NONE: return "NTB_TOPO_NONE"; | 99 | case NTB_TOPO_NONE: return "NTB_TOPO_NONE"; |
98 | case NTB_TOPO_PRI: return "NTB_TOPO_PRI"; | 100 | case NTB_TOPO_PRI: return "NTB_TOPO_PRI"; |
99 | case NTB_TOPO_SEC: return "NTB_TOPO_SEC"; | 101 | case NTB_TOPO_SEC: return "NTB_TOPO_SEC"; |
100 | case NTB_TOPO_B2B_USD: return "NTB_TOPO_B2B_USD"; | 102 | case NTB_TOPO_B2B_USD: return "NTB_TOPO_B2B_USD"; |
101 | case NTB_TOPO_B2B_DSD: return "NTB_TOPO_B2B_DSD"; | 103 | case NTB_TOPO_B2B_DSD: return "NTB_TOPO_B2B_DSD"; |
102 | case NTB_TOPO_SWITCH: return "NTB_TOPO_SWITCH"; | 104 | case NTB_TOPO_SWITCH: return "NTB_TOPO_SWITCH"; |
105 | case NTB_TOPO_CROSSLINK: return "NTB_TOPO_CROSSLINK"; | ||
103 | } | 106 | } |
104 | return "NTB_TOPO_INVALID"; | 107 | return "NTB_TOPO_INVALID"; |
105 | } | 108 | } |
@@ -250,7 +253,7 @@ static inline int ntb_ctx_ops_is_valid(const struct ntb_ctx_ops *ops) | |||
250 | * @msg_set_mask: See ntb_msg_set_mask(). | 253 | * @msg_set_mask: See ntb_msg_set_mask(). |
251 | * @msg_clear_mask: See ntb_msg_clear_mask(). | 254 | * @msg_clear_mask: See ntb_msg_clear_mask(). |
252 | * @msg_read: See ntb_msg_read(). | 255 | * @msg_read: See ntb_msg_read(). |
253 | * @msg_write: See ntb_msg_write(). | 256 | * @peer_msg_write: See ntb_peer_msg_write(). |
254 | */ | 257 | */ |
255 | struct ntb_dev_ops { | 258 | struct ntb_dev_ops { |
256 | int (*port_number)(struct ntb_dev *ntb); | 259 | int (*port_number)(struct ntb_dev *ntb); |
@@ -321,8 +324,8 @@ struct ntb_dev_ops { | |||
321 | int (*msg_clear_sts)(struct ntb_dev *ntb, u64 sts_bits); | 324 | int (*msg_clear_sts)(struct ntb_dev *ntb, u64 sts_bits); |
322 | int (*msg_set_mask)(struct ntb_dev *ntb, u64 mask_bits); | 325 | int (*msg_set_mask)(struct ntb_dev *ntb, u64 mask_bits); |
323 | int (*msg_clear_mask)(struct ntb_dev *ntb, u64 mask_bits); | 326 | int (*msg_clear_mask)(struct ntb_dev *ntb, u64 mask_bits); |
324 | int (*msg_read)(struct ntb_dev *ntb, int midx, int *pidx, u32 *msg); | 327 | u32 (*msg_read)(struct ntb_dev *ntb, int *pidx, int midx); |
325 | int (*msg_write)(struct ntb_dev *ntb, int midx, int pidx, u32 msg); | 328 | int (*peer_msg_write)(struct ntb_dev *ntb, int pidx, int midx, u32 msg); |
326 | }; | 329 | }; |
327 | 330 | ||
328 | static inline int ntb_dev_ops_is_valid(const struct ntb_dev_ops *ops) | 331 | static inline int ntb_dev_ops_is_valid(const struct ntb_dev_ops *ops) |
@@ -384,7 +387,7 @@ static inline int ntb_dev_ops_is_valid(const struct ntb_dev_ops *ops) | |||
384 | /* !ops->msg_set_mask == !ops->msg_count && */ | 387 | /* !ops->msg_set_mask == !ops->msg_count && */ |
385 | /* !ops->msg_clear_mask == !ops->msg_count && */ | 388 | /* !ops->msg_clear_mask == !ops->msg_count && */ |
386 | !ops->msg_read == !ops->msg_count && | 389 | !ops->msg_read == !ops->msg_count && |
387 | !ops->msg_write == !ops->msg_count && | 390 | !ops->peer_msg_write == !ops->msg_count && |
388 | 1; | 391 | 1; |
389 | } | 392 | } |
390 | 393 | ||
@@ -764,7 +767,7 @@ static inline int ntb_mw_get_align(struct ntb_dev *ntb, int pidx, int widx, | |||
764 | resource_size_t *size_align, | 767 | resource_size_t *size_align, |
765 | resource_size_t *size_max) | 768 | resource_size_t *size_max) |
766 | { | 769 | { |
767 | if (!(ntb_link_is_up(ntb, NULL, NULL) & (1 << pidx))) | 770 | if (!(ntb_link_is_up(ntb, NULL, NULL) & BIT_ULL(pidx))) |
768 | return -ENOTCONN; | 771 | return -ENOTCONN; |
769 | 772 | ||
770 | return ntb->ops->mw_get_align(ntb, pidx, widx, addr_align, size_align, | 773 | return ntb->ops->mw_get_align(ntb, pidx, widx, addr_align, size_align, |
@@ -1459,31 +1462,29 @@ static inline int ntb_msg_clear_mask(struct ntb_dev *ntb, u64 mask_bits) | |||
1459 | } | 1462 | } |
1460 | 1463 | ||
1461 | /** | 1464 | /** |
1462 | * ntb_msg_read() - read message register with specified index | 1465 | * ntb_msg_read() - read inbound message register with specified index |
1463 | * @ntb: NTB device context. | 1466 | * @ntb: NTB device context. |
1464 | * @midx: Message register index | ||
1465 | * @pidx: OUT - Port index of peer device a message retrieved from | 1467 | * @pidx: OUT - Port index of peer device a message retrieved from |
1466 | * @msg: OUT - Data | 1468 | * @midx: Message register index |
1467 | * | 1469 | * |
1468 | * Read data from the specified message register. Source port index of a | 1470 | * Read data from the specified message register. Source port index of a |
1469 | * message is retrieved as well. | 1471 | * message is retrieved as well. |
1470 | * | 1472 | * |
1471 | * Return: Zero on success, otherwise a negative error number. | 1473 | * Return: The value of the inbound message register. |
1472 | */ | 1474 | */ |
1473 | static inline int ntb_msg_read(struct ntb_dev *ntb, int midx, int *pidx, | 1475 | static inline u32 ntb_msg_read(struct ntb_dev *ntb, int *pidx, int midx) |
1474 | u32 *msg) | ||
1475 | { | 1476 | { |
1476 | if (!ntb->ops->msg_read) | 1477 | if (!ntb->ops->msg_read) |
1477 | return -EINVAL; | 1478 | return ~(u32)0; |
1478 | 1479 | ||
1479 | return ntb->ops->msg_read(ntb, midx, pidx, msg); | 1480 | return ntb->ops->msg_read(ntb, pidx, midx); |
1480 | } | 1481 | } |
1481 | 1482 | ||
1482 | /** | 1483 | /** |
1483 | * ntb_msg_write() - write data to the specified message register | 1484 | * ntb_peer_msg_write() - write data to the specified peer message register |
1484 | * @ntb: NTB device context. | 1485 | * @ntb: NTB device context. |
1485 | * @midx: Message register index | ||
1486 | * @pidx: Port index of peer device a message being sent to | 1486 | * @pidx: Port index of peer device a message being sent to |
1487 | * @midx: Message register index | ||
1487 | * @msg: Data to send | 1488 | * @msg: Data to send |
1488 | * | 1489 | * |
1489 | * Send data to a specified peer device using the defined message register. | 1490 | * Send data to a specified peer device using the defined message register. |
@@ -1492,13 +1493,13 @@ static inline int ntb_msg_read(struct ntb_dev *ntb, int midx, int *pidx, | |||
1492 | * | 1493 | * |
1493 | * Return: Zero on success, otherwise a negative error number. | 1494 | * Return: Zero on success, otherwise a negative error number. |
1494 | */ | 1495 | */ |
1495 | static inline int ntb_msg_write(struct ntb_dev *ntb, int midx, int pidx, | 1496 | static inline int ntb_peer_msg_write(struct ntb_dev *ntb, int pidx, int midx, |
1496 | u32 msg) | 1497 | u32 msg) |
1497 | { | 1498 | { |
1498 | if (!ntb->ops->msg_write) | 1499 | if (!ntb->ops->peer_msg_write) |
1499 | return -EINVAL; | 1500 | return -EINVAL; |
1500 | 1501 | ||
1501 | return ntb->ops->msg_write(ntb, midx, pidx, msg); | 1502 | return ntb->ops->peer_msg_write(ntb, pidx, midx, msg); |
1502 | } | 1503 | } |
1503 | 1504 | ||
1504 | #endif | 1505 | #endif |
diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index 09d73d0d1aa8..6d325a7a0c19 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h | |||
@@ -168,6 +168,14 @@ struct ntb_info_regs { | |||
168 | u16 reserved1; | 168 | u16 reserved1; |
169 | u64 ep_map; | 169 | u64 ep_map; |
170 | u16 requester_id; | 170 | u16 requester_id; |
171 | u16 reserved2; | ||
172 | u32 reserved3[4]; | ||
173 | struct nt_partition_info { | ||
174 | u32 xlink_enabled; | ||
175 | u32 target_part_low; | ||
176 | u32 target_part_high; | ||
177 | u32 reserved; | ||
178 | } ntp_info[48]; | ||
171 | } __packed; | 179 | } __packed; |
172 | 180 | ||
173 | struct part_cfg_regs { | 181 | struct part_cfg_regs { |
@@ -284,7 +292,20 @@ enum { | |||
284 | struct pff_csr_regs { | 292 | struct pff_csr_regs { |
285 | u16 vendor_id; | 293 | u16 vendor_id; |
286 | u16 device_id; | 294 | u16 device_id; |
287 | u32 pci_cfg_header[15]; | 295 | u16 pcicmd; |
296 | u16 pcists; | ||
297 | u32 pci_class; | ||
298 | u32 pci_opts; | ||
299 | union { | ||
300 | u32 pci_bar[6]; | ||
301 | u64 pci_bar64[3]; | ||
302 | }; | ||
303 | u32 pci_cardbus; | ||
304 | u32 pci_subsystem_id; | ||
305 | u32 pci_expansion_rom; | ||
306 | u32 pci_cap_ptr; | ||
307 | u32 reserved1; | ||
308 | u32 pci_irq; | ||
288 | u32 pci_cap_region[48]; | 309 | u32 pci_cap_region[48]; |
289 | u32 pcie_cap_region[448]; | 310 | u32 pcie_cap_region[448]; |
290 | u32 indirect_gas_window[128]; | 311 | u32 indirect_gas_window[128]; |
diff --git a/tools/testing/selftests/ntb/ntb_test.sh b/tools/testing/selftests/ntb/ntb_test.sh index 5fc7ad359e21..08cbfbbc7029 100755 --- a/tools/testing/selftests/ntb/ntb_test.sh +++ b/tools/testing/selftests/ntb/ntb_test.sh | |||
@@ -18,7 +18,6 @@ LIST_DEVS=FALSE | |||
18 | 18 | ||
19 | DEBUGFS=${DEBUGFS-/sys/kernel/debug} | 19 | DEBUGFS=${DEBUGFS-/sys/kernel/debug} |
20 | 20 | ||
21 | DB_BITMASK=0x7FFF | ||
22 | PERF_RUN_ORDER=32 | 21 | PERF_RUN_ORDER=32 |
23 | MAX_MW_SIZE=0 | 22 | MAX_MW_SIZE=0 |
24 | RUN_DMA_TESTS= | 23 | RUN_DMA_TESTS= |
@@ -39,15 +38,17 @@ function show_help() | |||
39 | echo "be highly recommended." | 38 | echo "be highly recommended." |
40 | echo | 39 | echo |
41 | echo "Options:" | 40 | echo "Options:" |
42 | echo " -b BITMASK doorbell clear bitmask for ntb_tool" | ||
43 | echo " -C don't cleanup ntb modules on exit" | 41 | echo " -C don't cleanup ntb modules on exit" |
44 | echo " -d run dma tests" | ||
45 | echo " -h show this help message" | 42 | echo " -h show this help message" |
46 | echo " -l list available local and remote PCI ids" | 43 | echo " -l list available local and remote PCI ids" |
47 | echo " -r REMOTE_HOST specify the remote's hostname to connect" | 44 | echo " -r REMOTE_HOST specify the remote's hostname to connect" |
48 | echo " to for the test (using ssh)" | 45 | echo " to for the test (using ssh)" |
49 | echo " -p NUM ntb_perf run order (default: $PERF_RUN_ORDER)" | 46 | echo " -m MW_SIZE memory window size for ntb_tool" |
50 | echo " -w max_mw_size maxmium memory window size" | 47 | echo " (default: $MW_SIZE)" |
48 | echo " -d run dma tests for ntb_perf" | ||
49 | echo " -p ORDER total data order for ntb_perf" | ||
50 | echo " (default: $PERF_RUN_ORDER)" | ||
51 | echo " -w MAX_MW_SIZE maxmium memory window size for ntb_perf" | ||
51 | echo | 52 | echo |
52 | } | 53 | } |
53 | 54 | ||
@@ -56,7 +57,6 @@ function parse_args() | |||
56 | OPTIND=0 | 57 | OPTIND=0 |
57 | while getopts "b:Cdhlm:r:p:w:" opt; do | 58 | while getopts "b:Cdhlm:r:p:w:" opt; do |
58 | case "$opt" in | 59 | case "$opt" in |
59 | b) DB_BITMASK=${OPTARG} ;; | ||
60 | C) DONT_CLEANUP=1 ;; | 60 | C) DONT_CLEANUP=1 ;; |
61 | d) RUN_DMA_TESTS=1 ;; | 61 | d) RUN_DMA_TESTS=1 ;; |
62 | h) show_help; exit 0 ;; | 62 | h) show_help; exit 0 ;; |
@@ -87,7 +87,7 @@ set -e | |||
87 | 87 | ||
88 | function _modprobe() | 88 | function _modprobe() |
89 | { | 89 | { |
90 | modprobe "$@" | 90 | modprobe "$@" |
91 | 91 | ||
92 | if [[ "$REMOTE_HOST" != "" ]]; then | 92 | if [[ "$REMOTE_HOST" != "" ]]; then |
93 | ssh "$REMOTE_HOST" modprobe "$@" | 93 | ssh "$REMOTE_HOST" modprobe "$@" |
@@ -127,15 +127,70 @@ function write_file() | |||
127 | fi | 127 | fi |
128 | } | 128 | } |
129 | 129 | ||
130 | function check_file() | ||
131 | { | ||
132 | split_remote $1 | ||
133 | |||
134 | if [[ "$REMOTE" != "" ]]; then | ||
135 | ssh "$REMOTE" "[[ -e ${VPATH} ]]" | ||
136 | else | ||
137 | [[ -e ${VPATH} ]] | ||
138 | fi | ||
139 | } | ||
140 | |||
141 | function subdirname() | ||
142 | { | ||
143 | echo $(basename $(dirname $1)) 2> /dev/null | ||
144 | } | ||
145 | |||
146 | function find_pidx() | ||
147 | { | ||
148 | PORT=$1 | ||
149 | PPATH=$2 | ||
150 | |||
151 | for ((i = 0; i < 64; i++)); do | ||
152 | PEER_DIR="$PPATH/peer$i" | ||
153 | |||
154 | check_file ${PEER_DIR} || break | ||
155 | |||
156 | PEER_PORT=$(read_file "${PEER_DIR}/port") | ||
157 | if [[ ${PORT} -eq $PEER_PORT ]]; then | ||
158 | echo $i | ||
159 | return 0 | ||
160 | fi | ||
161 | done | ||
162 | |||
163 | return 1 | ||
164 | } | ||
165 | |||
166 | function port_test() | ||
167 | { | ||
168 | LOC=$1 | ||
169 | REM=$2 | ||
170 | |||
171 | echo "Running port tests on: $(basename $LOC) / $(basename $REM)" | ||
172 | |||
173 | LOCAL_PORT=$(read_file "$LOC/port") | ||
174 | REMOTE_PORT=$(read_file "$REM/port") | ||
175 | |||
176 | LOCAL_PIDX=$(find_pidx ${REMOTE_PORT} "$LOC") | ||
177 | REMOTE_PIDX=$(find_pidx ${LOCAL_PORT} "$REM") | ||
178 | |||
179 | echo "Local port ${LOCAL_PORT} with index ${REMOTE_PIDX} on remote host" | ||
180 | echo "Peer port ${REMOTE_PORT} with index ${LOCAL_PIDX} on local host" | ||
181 | |||
182 | echo " Passed" | ||
183 | } | ||
184 | |||
130 | function link_test() | 185 | function link_test() |
131 | { | 186 | { |
132 | LOC=$1 | 187 | LOC=$1 |
133 | REM=$2 | 188 | REM=$2 |
134 | EXP=0 | 189 | EXP=0 |
135 | 190 | ||
136 | echo "Running link tests on: $(basename $LOC) / $(basename $REM)" | 191 | echo "Running link tests on: $(subdirname $LOC) / $(subdirname $REM)" |
137 | 192 | ||
138 | if ! write_file "N" "$LOC/link" 2> /dev/null; then | 193 | if ! write_file "N" "$LOC/../link" 2> /dev/null; then |
139 | echo " Unsupported" | 194 | echo " Unsupported" |
140 | return | 195 | return |
141 | fi | 196 | fi |
@@ -143,12 +198,11 @@ function link_test() | |||
143 | write_file "N" "$LOC/link_event" | 198 | write_file "N" "$LOC/link_event" |
144 | 199 | ||
145 | if [[ $(read_file "$REM/link") != "N" ]]; then | 200 | if [[ $(read_file "$REM/link") != "N" ]]; then |
146 | echo "Expected remote link to be down in $REM/link" >&2 | 201 | echo "Expected link to be down in $REM/link" >&2 |
147 | exit -1 | 202 | exit -1 |
148 | fi | 203 | fi |
149 | 204 | ||
150 | write_file "Y" "$LOC/link" | 205 | write_file "Y" "$LOC/../link" |
151 | write_file "Y" "$LOC/link_event" | ||
152 | 206 | ||
153 | echo " Passed" | 207 | echo " Passed" |
154 | } | 208 | } |
@@ -161,58 +215,136 @@ function doorbell_test() | |||
161 | 215 | ||
162 | echo "Running db tests on: $(basename $LOC) / $(basename $REM)" | 216 | echo "Running db tests on: $(basename $LOC) / $(basename $REM)" |
163 | 217 | ||
164 | write_file "c $DB_BITMASK" "$REM/db" | 218 | DB_VALID_MASK=$(read_file "$LOC/db_valid_mask") |
219 | |||
220 | write_file "c $DB_VALID_MASK" "$REM/db" | ||
165 | 221 | ||
166 | for ((i=1; i <= 8; i++)); do | 222 | for ((i = 0; i < 64; i++)); do |
167 | let DB=$(read_file "$REM/db") || true | 223 | DB=$(read_file "$REM/db") |
168 | if [[ "$DB" != "$EXP" ]]; then | 224 | if [[ "$DB" -ne "$EXP" ]]; then |
169 | echo "Doorbell doesn't match expected value $EXP " \ | 225 | echo "Doorbell doesn't match expected value $EXP " \ |
170 | "in $REM/db" >&2 | 226 | "in $REM/db" >&2 |
171 | exit -1 | 227 | exit -1 |
172 | fi | 228 | fi |
173 | 229 | ||
174 | let "MASK=1 << ($i-1)" || true | 230 | let "MASK = (1 << $i) & $DB_VALID_MASK" || true |
175 | let "EXP=$EXP | $MASK" || true | 231 | let "EXP = $EXP | $MASK" || true |
232 | |||
176 | write_file "s $MASK" "$LOC/peer_db" | 233 | write_file "s $MASK" "$LOC/peer_db" |
177 | done | 234 | done |
178 | 235 | ||
236 | write_file "c $DB_VALID_MASK" "$REM/db_mask" | ||
237 | write_file $DB_VALID_MASK "$REM/db_event" | ||
238 | write_file "s $DB_VALID_MASK" "$REM/db_mask" | ||
239 | |||
240 | write_file "c $DB_VALID_MASK" "$REM/db" | ||
241 | |||
179 | echo " Passed" | 242 | echo " Passed" |
180 | } | 243 | } |
181 | 244 | ||
182 | function read_spad() | 245 | function get_files_count() |
183 | { | 246 | { |
184 | VPATH=$1 | 247 | NAME=$1 |
185 | IDX=$2 | 248 | LOC=$2 |
249 | |||
250 | split_remote $LOC | ||
186 | 251 | ||
187 | ROW=($(read_file "$VPATH" | grep -e "^$IDX")) | 252 | if [[ "$REMOTE" == "" ]]; then |
188 | let VAL=${ROW[1]} || true | 253 | echo $(ls -1 "$LOC"/${NAME}* 2>/dev/null | wc -l) |
189 | echo $VAL | 254 | else |
255 | echo $(ssh "$REMOTE" "ls -1 \"$VPATH\"/${NAME}* | \ | ||
256 | wc -l" 2> /dev/null) | ||
257 | fi | ||
190 | } | 258 | } |
191 | 259 | ||
192 | function scratchpad_test() | 260 | function scratchpad_test() |
193 | { | 261 | { |
194 | LOC=$1 | 262 | LOC=$1 |
195 | REM=$2 | 263 | REM=$2 |
196 | CNT=$(read_file "$LOC/spad" | wc -l) | ||
197 | 264 | ||
198 | echo "Running spad tests on: $(basename $LOC) / $(basename $REM)" | 265 | echo "Running spad tests on: $(subdirname $LOC) / $(subdirname $REM)" |
266 | |||
267 | CNT=$(get_files_count "spad" "$LOC") | ||
268 | |||
269 | if [[ $CNT -eq 0 ]]; then | ||
270 | echo " Unsupported" | ||
271 | return | ||
272 | fi | ||
199 | 273 | ||
200 | for ((i = 0; i < $CNT; i++)); do | 274 | for ((i = 0; i < $CNT; i++)); do |
201 | VAL=$RANDOM | 275 | VAL=$RANDOM |
202 | write_file "$i $VAL" "$LOC/peer_spad" | 276 | write_file "$VAL" "$LOC/spad$i" |
203 | RVAL=$(read_spad "$REM/spad" $i) | 277 | RVAL=$(read_file "$REM/../spad$i") |
204 | 278 | ||
205 | if [[ "$VAL" != "$RVAL" ]]; then | 279 | if [[ "$VAL" -ne "$RVAL" ]]; then |
206 | echo "Scratchpad doesn't match expected value $VAL " \ | 280 | echo "Scratchpad $i value $RVAL doesn't match $VAL" >&2 |
207 | "in $REM/spad, got $RVAL" >&2 | ||
208 | exit -1 | 281 | exit -1 |
209 | fi | 282 | fi |
283 | done | ||
284 | |||
285 | echo " Passed" | ||
286 | } | ||
287 | |||
288 | function message_test() | ||
289 | { | ||
290 | LOC=$1 | ||
291 | REM=$2 | ||
292 | |||
293 | echo "Running msg tests on: $(subdirname $LOC) / $(subdirname $REM)" | ||
294 | |||
295 | CNT=$(get_files_count "msg" "$LOC") | ||
210 | 296 | ||
297 | if [[ $CNT -eq 0 ]]; then | ||
298 | echo " Unsupported" | ||
299 | return | ||
300 | fi | ||
301 | |||
302 | MSG_OUTBITS_MASK=$(read_file "$LOC/../msg_inbits") | ||
303 | MSG_INBITS_MASK=$(read_file "$REM/../msg_inbits") | ||
304 | |||
305 | write_file "c $MSG_OUTBITS_MASK" "$LOC/../msg_sts" | ||
306 | write_file "c $MSG_INBITS_MASK" "$REM/../msg_sts" | ||
307 | |||
308 | for ((i = 0; i < $CNT; i++)); do | ||
309 | VAL=$RANDOM | ||
310 | write_file "$VAL" "$LOC/msg$i" | ||
311 | RVAL=$(read_file "$REM/../msg$i") | ||
312 | |||
313 | if [[ "$VAL" -ne "${RVAL%%<-*}" ]]; then | ||
314 | echo "Message $i value $RVAL doesn't match $VAL" >&2 | ||
315 | exit -1 | ||
316 | fi | ||
211 | done | 317 | done |
212 | 318 | ||
213 | echo " Passed" | 319 | echo " Passed" |
214 | } | 320 | } |
215 | 321 | ||
322 | function get_number() | ||
323 | { | ||
324 | KEY=$1 | ||
325 | |||
326 | sed -n "s/^\(${KEY}\)[ \t]*\(0x[0-9a-fA-F]*\)\(\[p\]\)\?$/\2/p" | ||
327 | } | ||
328 | |||
329 | function mw_alloc() | ||
330 | { | ||
331 | IDX=$1 | ||
332 | LOC=$2 | ||
333 | REM=$3 | ||
334 | |||
335 | write_file $MW_SIZE "$LOC/mw_trans$IDX" | ||
336 | |||
337 | INB_MW=$(read_file "$LOC/mw_trans$IDX") | ||
338 | MW_ALIGNED_SIZE=$(echo "$INB_MW" | get_number "Window Size") | ||
339 | MW_DMA_ADDR=$(echo "$INB_MW" | get_number "DMA Address") | ||
340 | |||
341 | write_file "$MW_DMA_ADDR:$(($MW_ALIGNED_SIZE))" "$REM/peer_mw_trans$IDX" | ||
342 | |||
343 | if [[ $MW_SIZE -ne $MW_ALIGNED_SIZE ]]; then | ||
344 | echo "MW $IDX size aligned to $MW_ALIGNED_SIZE" | ||
345 | fi | ||
346 | } | ||
347 | |||
216 | function write_mw() | 348 | function write_mw() |
217 | { | 349 | { |
218 | split_remote $2 | 350 | split_remote $2 |
@@ -225,17 +357,15 @@ function write_mw() | |||
225 | fi | 357 | fi |
226 | } | 358 | } |
227 | 359 | ||
228 | function mw_test() | 360 | function mw_check() |
229 | { | 361 | { |
230 | IDX=$1 | 362 | IDX=$1 |
231 | LOC=$2 | 363 | LOC=$2 |
232 | REM=$3 | 364 | REM=$3 |
233 | 365 | ||
234 | echo "Running $IDX tests on: $(basename $LOC) / $(basename $REM)" | 366 | write_mw "$LOC/mw$IDX" |
235 | 367 | ||
236 | write_mw "$LOC/$IDX" | 368 | split_remote "$LOC/mw$IDX" |
237 | |||
238 | split_remote "$LOC/$IDX" | ||
239 | if [[ "$REMOTE" == "" ]]; then | 369 | if [[ "$REMOTE" == "" ]]; then |
240 | A=$VPATH | 370 | A=$VPATH |
241 | else | 371 | else |
@@ -243,7 +373,7 @@ function mw_test() | |||
243 | ssh "$REMOTE" cat "$VPATH" > "$A" | 373 | ssh "$REMOTE" cat "$VPATH" > "$A" |
244 | fi | 374 | fi |
245 | 375 | ||
246 | split_remote "$REM/peer_$IDX" | 376 | split_remote "$REM/peer_mw$IDX" |
247 | if [[ "$REMOTE" == "" ]]; then | 377 | if [[ "$REMOTE" == "" ]]; then |
248 | B=$VPATH | 378 | B=$VPATH |
249 | else | 379 | else |
@@ -251,7 +381,7 @@ function mw_test() | |||
251 | ssh "$REMOTE" cat "$VPATH" > "$B" | 381 | ssh "$REMOTE" cat "$VPATH" > "$B" |
252 | fi | 382 | fi |
253 | 383 | ||
254 | cmp -n $MW_SIZE "$A" "$B" | 384 | cmp -n $MW_ALIGNED_SIZE "$A" "$B" |
255 | if [[ $? != 0 ]]; then | 385 | if [[ $? != 0 ]]; then |
256 | echo "Memory window $MW did not match!" >&2 | 386 | echo "Memory window $MW did not match!" >&2 |
257 | fi | 387 | fi |
@@ -263,8 +393,39 @@ function mw_test() | |||
263 | if [[ "$B" == "/tmp/*" ]]; then | 393 | if [[ "$B" == "/tmp/*" ]]; then |
264 | rm "$B" | 394 | rm "$B" |
265 | fi | 395 | fi |
396 | } | ||
397 | |||
398 | function mw_free() | ||
399 | { | ||
400 | IDX=$1 | ||
401 | LOC=$2 | ||
402 | REM=$3 | ||
403 | |||
404 | write_file "$MW_DMA_ADDR:0" "$REM/peer_mw_trans$IDX" | ||
405 | |||
406 | write_file 0 "$LOC/mw_trans$IDX" | ||
407 | } | ||
408 | |||
409 | function mw_test() | ||
410 | { | ||
411 | LOC=$1 | ||
412 | REM=$2 | ||
413 | |||
414 | CNT=$(get_files_count "mw_trans" "$LOC") | ||
415 | |||
416 | for ((i = 0; i < $CNT; i++)); do | ||
417 | echo "Running mw$i tests on: $(subdirname $LOC) / " \ | ||
418 | "$(subdirname $REM)" | ||
419 | |||
420 | mw_alloc $i $LOC $REM | ||
421 | |||
422 | mw_check $i $LOC $REM | ||
423 | |||
424 | mw_free $i $LOC $REM | ||
425 | |||
426 | echo " Passed" | ||
427 | done | ||
266 | 428 | ||
267 | echo " Passed" | ||
268 | } | 429 | } |
269 | 430 | ||
270 | function pingpong_test() | 431 | function pingpong_test() |
@@ -274,13 +435,13 @@ function pingpong_test() | |||
274 | 435 | ||
275 | echo "Running ping pong tests on: $(basename $LOC) / $(basename $REM)" | 436 | echo "Running ping pong tests on: $(basename $LOC) / $(basename $REM)" |
276 | 437 | ||
277 | LOC_START=$(read_file $LOC/count) | 438 | LOC_START=$(read_file "$LOC/count") |
278 | REM_START=$(read_file $REM/count) | 439 | REM_START=$(read_file "$REM/count") |
279 | 440 | ||
280 | sleep 7 | 441 | sleep 7 |
281 | 442 | ||
282 | LOC_END=$(read_file $LOC/count) | 443 | LOC_END=$(read_file "$LOC/count") |
283 | REM_END=$(read_file $REM/count) | 444 | REM_END=$(read_file "$REM/count") |
284 | 445 | ||
285 | if [[ $LOC_START == $LOC_END ]] || [[ $REM_START == $REM_END ]]; then | 446 | if [[ $LOC_START == $LOC_END ]] || [[ $REM_START == $REM_END ]]; then |
286 | echo "Ping pong counter not incrementing!" >&2 | 447 | echo "Ping pong counter not incrementing!" >&2 |
@@ -300,19 +461,19 @@ function perf_test() | |||
300 | WITH="without" | 461 | WITH="without" |
301 | fi | 462 | fi |
302 | 463 | ||
303 | _modprobe ntb_perf run_order=$PERF_RUN_ORDER \ | 464 | _modprobe ntb_perf total_order=$PERF_RUN_ORDER \ |
304 | max_mw_size=$MAX_MW_SIZE use_dma=$USE_DMA | 465 | max_mw_size=$MAX_MW_SIZE use_dma=$USE_DMA |
305 | 466 | ||
306 | echo "Running local perf test $WITH DMA" | 467 | echo "Running local perf test $WITH DMA" |
307 | write_file "" $LOCAL_PERF/run | 468 | write_file "$LOCAL_PIDX" "$LOCAL_PERF/run" |
308 | echo -n " " | 469 | echo -n " " |
309 | read_file $LOCAL_PERF/run | 470 | read_file "$LOCAL_PERF/run" |
310 | echo " Passed" | 471 | echo " Passed" |
311 | 472 | ||
312 | echo "Running remote perf test $WITH DMA" | 473 | echo "Running remote perf test $WITH DMA" |
313 | write_file "" $REMOTE_PERF/run | 474 | write_file "$REMOTE_PIDX" "$REMOTE_PERF/run" |
314 | echo -n " " | 475 | echo -n " " |
315 | read_file $REMOTE_PERF/run | 476 | read_file "$REMOTE_PERF/run" |
316 | echo " Passed" | 477 | echo " Passed" |
317 | 478 | ||
318 | _modprobe -r ntb_perf | 479 | _modprobe -r ntb_perf |
@@ -320,48 +481,44 @@ function perf_test() | |||
320 | 481 | ||
321 | function ntb_tool_tests() | 482 | function ntb_tool_tests() |
322 | { | 483 | { |
323 | LOCAL_TOOL=$DEBUGFS/ntb_tool/$LOCAL_DEV | 484 | LOCAL_TOOL="$DEBUGFS/ntb_tool/$LOCAL_DEV" |
324 | REMOTE_TOOL=$REMOTE_HOST:$DEBUGFS/ntb_tool/$REMOTE_DEV | 485 | REMOTE_TOOL="$REMOTE_HOST:$DEBUGFS/ntb_tool/$REMOTE_DEV" |
325 | 486 | ||
326 | echo "Starting ntb_tool tests..." | 487 | echo "Starting ntb_tool tests..." |
327 | 488 | ||
328 | _modprobe ntb_tool | 489 | _modprobe ntb_tool |
329 | 490 | ||
330 | write_file Y $LOCAL_TOOL/link_event | 491 | port_test "$LOCAL_TOOL" "$REMOTE_TOOL" |
331 | write_file Y $REMOTE_TOOL/link_event | ||
332 | 492 | ||
333 | link_test $LOCAL_TOOL $REMOTE_TOOL | 493 | LOCAL_PEER_TOOL="$LOCAL_TOOL/peer$LOCAL_PIDX" |
334 | link_test $REMOTE_TOOL $LOCAL_TOOL | 494 | REMOTE_PEER_TOOL="$REMOTE_TOOL/peer$REMOTE_PIDX" |
495 | |||
496 | link_test "$LOCAL_PEER_TOOL" "$REMOTE_PEER_TOOL" | ||
497 | link_test "$REMOTE_PEER_TOOL" "$LOCAL_PEER_TOOL" | ||
335 | 498 | ||
336 | #Ensure the link is up on both sides before continuing | 499 | #Ensure the link is up on both sides before continuing |
337 | write_file Y $LOCAL_TOOL/link_event | 500 | write_file "Y" "$LOCAL_PEER_TOOL/link_event" |
338 | write_file Y $REMOTE_TOOL/link_event | 501 | write_file "Y" "$REMOTE_PEER_TOOL/link_event" |
339 | 502 | ||
340 | for PEER_TRANS in $(ls $LOCAL_TOOL/peer_trans*); do | 503 | doorbell_test "$LOCAL_TOOL" "$REMOTE_TOOL" |
341 | PT=$(basename $PEER_TRANS) | 504 | doorbell_test "$REMOTE_TOOL" "$LOCAL_TOOL" |
342 | write_file $MW_SIZE $LOCAL_TOOL/$PT | ||
343 | write_file $MW_SIZE $REMOTE_TOOL/$PT | ||
344 | done | ||
345 | 505 | ||
346 | doorbell_test $LOCAL_TOOL $REMOTE_TOOL | 506 | scratchpad_test "$LOCAL_PEER_TOOL" "$REMOTE_PEER_TOOL" |
347 | doorbell_test $REMOTE_TOOL $LOCAL_TOOL | 507 | scratchpad_test "$REMOTE_PEER_TOOL" "$LOCAL_PEER_TOOL" |
348 | scratchpad_test $LOCAL_TOOL $REMOTE_TOOL | ||
349 | scratchpad_test $REMOTE_TOOL $LOCAL_TOOL | ||
350 | 508 | ||
351 | for MW in $(ls $LOCAL_TOOL/mw*); do | 509 | message_test "$LOCAL_PEER_TOOL" "$REMOTE_PEER_TOOL" |
352 | MW=$(basename $MW) | 510 | message_test "$REMOTE_PEER_TOOL" "$LOCAL_PEER_TOOL" |
353 | 511 | ||
354 | mw_test $MW $LOCAL_TOOL $REMOTE_TOOL | 512 | mw_test "$LOCAL_PEER_TOOL" "$REMOTE_PEER_TOOL" |
355 | mw_test $MW $REMOTE_TOOL $LOCAL_TOOL | 513 | mw_test "$REMOTE_PEER_TOOL" "$LOCAL_PEER_TOOL" |
356 | done | ||
357 | 514 | ||
358 | _modprobe -r ntb_tool | 515 | _modprobe -r ntb_tool |
359 | } | 516 | } |
360 | 517 | ||
361 | function ntb_pingpong_tests() | 518 | function ntb_pingpong_tests() |
362 | { | 519 | { |
363 | LOCAL_PP=$DEBUGFS/ntb_pingpong/$LOCAL_DEV | 520 | LOCAL_PP="$DEBUGFS/ntb_pingpong/$LOCAL_DEV" |
364 | REMOTE_PP=$REMOTE_HOST:$DEBUGFS/ntb_pingpong/$REMOTE_DEV | 521 | REMOTE_PP="$REMOTE_HOST:$DEBUGFS/ntb_pingpong/$REMOTE_DEV" |
365 | 522 | ||
366 | echo "Starting ntb_pingpong tests..." | 523 | echo "Starting ntb_pingpong tests..." |
367 | 524 | ||
@@ -374,8 +531,8 @@ function ntb_pingpong_tests() | |||
374 | 531 | ||
375 | function ntb_perf_tests() | 532 | function ntb_perf_tests() |
376 | { | 533 | { |
377 | LOCAL_PERF=$DEBUGFS/ntb_perf/$LOCAL_DEV | 534 | LOCAL_PERF="$DEBUGFS/ntb_perf/$LOCAL_DEV" |
378 | REMOTE_PERF=$REMOTE_HOST:$DEBUGFS/ntb_perf/$REMOTE_DEV | 535 | REMOTE_PERF="$REMOTE_HOST:$DEBUGFS/ntb_perf/$REMOTE_DEV" |
379 | 536 | ||
380 | echo "Starting ntb_perf tests..." | 537 | echo "Starting ntb_perf tests..." |
381 | 538 | ||