Add a new test mode controlled by a flag, PCITEST_FLAGS_USE_REMOTE_EDMA. When requested, the driver: - Issues COMMAND_REMOTE_EDMA_SETUP to the endpoint and locates the BAR containing a pcitest_edma_info header (magic/version). - Creates a remote dw-edma instance by ioremapping the endpoint's exposed eDMA registers and linked-list regions and probing dw-edma on top of it. - Requests one DMA_SLAVE channel per direction and performs the transfer. - Uses COMMAND_REMOTE_EDMA_CHECKSUM to validate the result when the transfer direction is host-to-endpoint. For the opposite direction, the endpoint provides the expected checksum up front.
One MSI/MSI-X vector is reserved for the remote dw-edma instance by freeing the last test IRQ vector. This keeps existing MSI/MSI-X tests unchanged unless the remote-eDMA mode is invoked. BAR read/write tests skip the BAR reserved for remote-eDMA metadata to avoid corrupting the eDMA window. Signed-off-by: Koichiro Den <[email protected]> --- drivers/misc/pci_endpoint_test.c | 633 +++++++++++++++++++++++++++++++ include/uapi/linux/pcitest.h | 3 +- 2 files changed, 635 insertions(+), 1 deletion(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 1c0fd185114f..52d700374ac6 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -8,7 +8,10 @@ #include <linux/crc32.h> #include <linux/cleanup.h> +#include <linux/completion.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> #include <linux/fs.h> #include <linux/io.h> #include <linux/interrupt.h> @@ -17,6 +20,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/random.h> +#include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/pci.h> @@ -39,6 +43,8 @@ #define COMMAND_COPY BIT(5) #define COMMAND_ENABLE_DOORBELL BIT(6) #define COMMAND_DISABLE_DOORBELL BIT(7) +#define COMMAND_REMOTE_EDMA_SETUP BIT(8) +#define COMMAND_REMOTE_EDMA_CHECKSUM BIT(9) #define PCI_ENDPOINT_TEST_STATUS 0x8 #define STATUS_READ_SUCCESS BIT(0) @@ -55,6 +61,10 @@ #define STATUS_DOORBELL_ENABLE_FAIL BIT(11) #define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12) #define STATUS_DOORBELL_DISABLE_FAIL BIT(13) +#define STATUS_REMOTE_EDMA_SETUP_SUCCESS BIT(14) +#define STATUS_REMOTE_EDMA_SETUP_FAIL BIT(15) +#define STATUS_REMOTE_EDMA_CHECKSUM_SUCCESS BIT(16) +#define STATUS_REMOTE_EDMA_CHECKSUM_FAIL BIT(17) #define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0x0c #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10 @@ -130,6 +140,9 @@ struct pci_endpoint_test { size_t alignment; u32 ep_caps; const char *name; + + /* For extended tests that rely on vendor-specific features */ + void *data; }; struct pci_endpoint_test_data { @@ -149,6 +162,610 @@ static inline void pci_endpoint_test_writel(struct pci_endpoint_test *test, writel(value, test->base + offset); } +static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id); + +#if IS_REACHABLE(CONFIG_DW_EDMA) +#include <linux/dma/edma.h> + +#define PCITEST_EDMA_INFO_MAGIC 0x414d4445U /* 'EDMA' */ +#define PCITEST_EDMA_INFO_VERSION 0x00010000U + +struct pci_endpoint_test_edma { + bool probed; + void __iomem *bar_base; + int irq; + + /* Remote dw-edma instance */ + struct dw_edma_chip chip; + + /* One channel per direction */ + struct dma_chan *m2d; + struct dma_chan *d2m; +}; + +struct pcitest_edma_info { + __le32 magic; + __le32 version; + + __le32 reg_off; + __le32 reg_size; + + __le64 ll_rd_phys; + __le32 ll_rd_off; + __le32 ll_rd_size; + + __le64 ll_wr_phys; + __le32 ll_wr_off; + __le32 ll_wr_size; + + __le64 test_buf_phys; + __le32 test_buf_size; +}; + +struct pci_endpoint_test_edma_filter { + struct device *dma_dev; + unsigned long direction; +}; + +static bool test_edma_filter_fn(struct dma_chan *chan, void *param) +{ + struct pci_endpoint_test_edma_filter *filter = param; + u32 dir = filter->direction; + struct dma_slave_caps caps; + int ret; + + if (chan->device->dev != filter->dma_dev) + return false; + + ret = dma_get_slave_caps(chan, &caps); + if (ret < 0) + return false; + + return !!(caps.directions & dir); +} + +static int pci_endpoint_test_edma_irq_vector(struct device *dev, unsigned int nr) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pci_endpoint_test *test = pci_get_drvdata(pdev); + struct pci_endpoint_test_edma *edma; + + if (!test) + return -EINVAL; + + edma = test->data; + if (!edma) + return -EINVAL; + + /* + * Only one vector is reserved for remote eDMA use, thus 'nr' is + * ignored. See pci_endpoint_test_edma_reserve_irq(). + */ + return pci_irq_vector(pdev, edma->irq); +} + +static enum pci_barno pci_endpoint_test_edma_bar(struct pci_dev *pdev) +{ + int bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + void __iomem *base; + u32 magic; + + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) + continue; + if (!pci_resource_len(pdev, bar)) + continue; + + base = pci_iomap_range(pdev, bar, 0, sizeof(u32)); + if (!base) + continue; + + magic = ioread32(base); + pci_iounmap(pdev, base); + + if (magic == PCITEST_EDMA_INFO_MAGIC) + return bar; + } + return NO_BAR; +} + +static bool pci_endpoint_test_bar_is_reserved(struct pci_endpoint_test *test, + enum pci_barno barno) +{ + struct pci_dev *pdev = test->pdev; + enum pci_barno edma_bar = pci_endpoint_test_edma_bar(pdev); + + return barno == NO_BAR || barno == edma_bar; +} + +static void pci_endpoint_test_dw_edma_cleanup(struct pci_endpoint_test *test, + struct pci_endpoint_test_edma *edma) +{ + if (!edma) + return; + + if (edma->m2d) { + dmaengine_terminate_sync(edma->m2d); + dma_release_channel(edma->m2d); + edma->m2d = NULL; + } + + if (edma->d2m) { + dmaengine_terminate_sync(edma->d2m); + dma_release_channel(edma->d2m); + edma->d2m = NULL; + } + + if (edma->probed) { + dw_edma_remove(&edma->chip); + edma->probed = false; + } + + if (edma->bar_base) { + pci_iounmap(test->pdev, edma->bar_base); + edma->bar_base = NULL; + } +} + +static void pci_endpoint_test_remote_edma_teardown(struct pci_endpoint_test *test) +{ + struct pci_endpoint_test_edma *edma = test->data; + + pci_endpoint_test_dw_edma_cleanup(test, edma); + kfree(edma); + test->data = NULL; +} + +/* + * Reserve exactly one IRQ vector for dw-edma by freeing the last handler. + * This avoids changing existing MSI/MSI-X tests unless remote eDMA is used. + */ +static int pci_endpoint_test_edma_reserve_irq(struct pci_endpoint_test *test) +{ + struct pci_dev *pdev = test->pdev; + + if (test->irq_type != PCITEST_IRQ_TYPE_MSI && + test->irq_type != PCITEST_IRQ_TYPE_MSIX) + return -EOPNOTSUPP; + + if (test->num_irqs < 2) + return -ENOSPC; + + /* use the last vector for remote eDMA use */ + free_irq(pci_irq_vector(pdev, test->num_irqs - 1), test); + return test->num_irqs - 1; +} + +static void pci_endpoint_test_edma_restore_irq(struct pci_endpoint_test *test) +{ + struct pci_dev *pdev = test->pdev; + int ret; + + ret = request_irq(pci_irq_vector(pdev, test->num_irqs - 1), + pci_endpoint_test_irqhandler, IRQF_SHARED, test->name, + test); + if (ret) + dev_warn(&pdev->dev, + "failed to restore IRQ vector %d after remote eDMA: %d\n", + test->num_irqs - 1, ret); +} + +static const struct dw_edma_plat_ops test_edma_ops = { + .irq_vector = pci_endpoint_test_edma_irq_vector, +}; + +static int pci_endpoint_test_dw_edma_setup(struct pci_endpoint_test *test) +{ + struct pci_endpoint_test_edma *edma = test->data; + struct pci_endpoint_test_edma_filter f; + struct pci_endpoint_test_edma *new; + struct pci_dev *pdev = test->pdev; + struct device *dev = &pdev->dev; + struct pcitest_edma_info info; + resource_size_t bar_size; + u32 ll_rd_off, ll_rd_size; + u32 ll_wr_off, ll_wr_size; + u32 reg_off, reg_size; + dma_cap_mask_t mask; + enum pci_barno bar; + int ret; + + if (edma && edma->probed) + return 0; + + new = kzalloc_obj(*new, GFP_KERNEL); + if (!new) + return -ENOMEM; + + ret = pci_endpoint_test_edma_reserve_irq(test); + if (ret < 0) + goto err_free; + new->irq = ret; + + bar = pci_endpoint_test_edma_bar(pdev); + if (bar == NO_BAR) { + ret = -EOPNOTSUPP; + goto err_restore_irq; + } + + new->bar_base = pci_iomap(pdev, bar, 0); + if (!new->bar_base) { + ret = -ENOMEM; + goto err_restore_irq; + } + bar_size = pci_resource_len(pdev, bar); + + /* Snapshot the info (avoid repeated __iomem reads). */ + memcpy_fromio(&info, new->bar_base, sizeof(info)); + if (le32_to_cpu(info.magic) != PCITEST_EDMA_INFO_MAGIC || + le32_to_cpu(info.version) != PCITEST_EDMA_INFO_VERSION) { + dev_err(&pdev->dev, "Invalid eDMA info\n"); + ret = -EINVAL; + goto err_cleanup; + } + + reg_off = le32_to_cpu(info.reg_off); + reg_size = le32_to_cpu(info.reg_size); + ll_rd_off = le32_to_cpu(info.ll_rd_off); + ll_rd_size = le32_to_cpu(info.ll_rd_size); + ll_wr_off = le32_to_cpu(info.ll_wr_off); + ll_wr_size = le32_to_cpu(info.ll_wr_size); + + if (reg_off > bar_size || reg_size > bar_size - reg_off || + ll_rd_off > bar_size || ll_rd_size > bar_size - ll_rd_off || + ll_wr_off > bar_size || ll_wr_size > bar_size - ll_wr_off) { + dev_err(&pdev->dev, "eDMA info offsets out of BAR range\n"); + ret = -EINVAL; + goto err_cleanup; + } + + memset(&new->chip, 0, sizeof(new->chip)); + new->chip.dev = &pdev->dev; + new->chip.mf = EDMA_MF_EDMA_UNROLL; + new->chip.nr_irqs = 1; + new->chip.ops = &test_edma_ops; + new->chip.reg_base = new->bar_base + reg_off; + new->chip.ll_rd_cnt = 1; + new->chip.ll_region_rd[0].paddr = le64_to_cpu(info.ll_rd_phys); + new->chip.ll_region_rd[0].vaddr.io = new->bar_base + ll_rd_off; + new->chip.ll_region_rd[0].sz = ll_rd_size; + new->chip.ll_wr_cnt = 1; + new->chip.ll_region_wr[0].paddr = le64_to_cpu(info.ll_wr_phys); + new->chip.ll_region_wr[0].vaddr.io = new->bar_base + ll_wr_off; + new->chip.ll_region_wr[0].sz = ll_wr_size; + + test->data = new; + ret = dw_edma_probe(&new->chip); + if (ret) { + dev_err(&pdev->dev, "Failed to probe eDMA: %d\n", ret); + goto err_cleanup; + } + new->probed = true; + + /* Request one channel per direction. */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + f.dma_dev = dev; + f.direction = BIT(DMA_MEM_TO_DEV); + new->m2d = dma_request_channel(mask, test_edma_filter_fn, &f); + f.direction = BIT(DMA_DEV_TO_MEM); + new->d2m = dma_request_channel(mask, test_edma_filter_fn, &f); + if (!new->m2d || !new->d2m) { + ret = -ENODEV; + goto err_cleanup; + } + + /* + * Best-effort attempt, ie. even if it fails for some reason, the + * endpoint will ignore endpoint-local interrupts (edma_int bus). + */ + dw_edma_chan_irq_config(new->m2d, DW_EDMA_CH_IRQ_REMOTE); + dw_edma_chan_irq_config(new->d2m, DW_EDMA_CH_IRQ_REMOTE); + + return 0; +err_cleanup: + pci_endpoint_test_dw_edma_cleanup(test, new); +err_restore_irq: + pci_endpoint_test_edma_restore_irq(test); +err_free: + kfree(new); + test->data = NULL; + return ret; +} + +static int pci_endpoint_test_remote_edma_setup(struct pci_endpoint_test *test, + size_t size) +{ + struct pci_dev *pdev = test->pdev; + struct device *dev = &pdev->dev; + unsigned long left; + u32 status; + + /* Same rule as existing tests: IRQ type must be configured first */ + if (test->irq_type != PCITEST_IRQ_TYPE_MSI && + test->irq_type != PCITEST_IRQ_TYPE_MSIX) { + dev_err(dev, "Invalid IRQ type for remote eDMA\n"); + return -EINVAL; + } + + /* Need one spare vector for dw-edma */ + if (test->num_irqs < 2) + return -ENOSPC; + + /* + * Ensure EP command handler won't reject us due to stale flags. + * (remote-eDMA setup itself is not "FLAG_USE_DMA") + */ + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, 0); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, + test->irq_type); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1); + + reinit_completion(&test->irq_raised); + test->last_irq = -ENODATA; + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, + COMMAND_REMOTE_EDMA_SETUP); + + left = wait_for_completion_timeout(&test->irq_raised, + msecs_to_jiffies(1000)); + if (!left) + return -ETIMEDOUT; + + status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS); + if (status & STATUS_REMOTE_EDMA_SETUP_FAIL) { + dev_err(dev, "Endpoint failed to setup remote eDMA window\n"); + return -EIO; + } + if (!(status & STATUS_REMOTE_EDMA_SETUP_SUCCESS)) { + dev_err(dev, + "Endpoint did not report remote eDMA setup success\n"); + return -EIO; + } + + return pci_endpoint_test_dw_edma_setup(test); +} + +static int pci_endpoint_test_edma_xfer(struct pci_dev *pdev, + struct pci_endpoint_test_edma *edma, + void *buf, size_t len, + dma_addr_t dev_addr, + enum dma_transfer_direction dir) +{ + struct dma_async_tx_descriptor *tx; + enum dma_data_direction map_dir; + struct device *dev = &pdev->dev; + struct dma_slave_config cfg; + struct completion done; + struct dma_chan *chan; + struct scatterlist sg; + dma_cookie_t cookie; + int ret; + + memset(&cfg, 0, sizeof(cfg)); + if (dir == DMA_MEM_TO_DEV) { + chan = edma->m2d; + map_dir = DMA_TO_DEVICE; + cfg.direction = DMA_MEM_TO_DEV; + cfg.dst_addr = dev_addr; + } else if (dir == DMA_DEV_TO_MEM) { + chan = edma->d2m; + map_dir = DMA_FROM_DEVICE; + cfg.direction = DMA_DEV_TO_MEM; + cfg.src_addr = dev_addr; + } else { + return -EINVAL; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) + return ret; + + sg_init_one(&sg, buf, len); + if (!dma_map_sg(dev, &sg, 1, map_dir)) { + dev_err(dev, "unable to map local address\n"); + return -EIO; + } + + tx = dmaengine_prep_slave_sg(chan, &sg, 1, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx) { + dev_err(dev, "failed to prepare slave for sg\n"); + ret = -EIO; + goto unmap; + } + + init_completion(&done); + tx->callback = (dma_async_tx_callback)complete; + tx->callback_param = &done; + + cookie = dmaengine_submit(tx); + ret = dma_submit_error(cookie); + if (ret) { + dev_err(dev, "remote eDMA submission error: %d\n", ret); + goto unmap; + } + + dma_async_issue_pending(chan); + + if (!wait_for_completion_timeout(&done, msecs_to_jiffies(5000))) { + dev_err(dev, "remote eDMA transfer timeout\n"); + dmaengine_terminate_sync(chan); + ret = -ETIMEDOUT; + goto unmap; + } + + ret = 0; +unmap: + dma_unmap_sg(dev, &sg, 1, map_dir); + return ret; +} + +static int pci_endpoint_test_edma_write(struct pci_endpoint_test *test, + size_t size) +{ + struct pci_endpoint_test_edma *edma; + struct pci_dev *pdev = test->pdev; + struct device *dev = &pdev->dev; + struct pcitest_edma_info info; + u32 reg, crc32, peer_crc32; + unsigned long left; + int ret; + + /* + * Note that test->alignment does not apply here. If some vendor + * dmaengine for remote use may impose some alignment restriction, we + * may as well introduce another field such as + * test->remote_dma_alignment. + */ + void *orig_addr __free(kfree) = kzalloc(size, GFP_KERNEL); + if (!orig_addr) + return -ENOMEM; + + ret = pci_endpoint_test_remote_edma_setup(test, size); + if (ret) + return ret; + + edma = test->data; + if (!edma) { + ret = -ENODEV; + goto err; + } + + get_random_bytes(orig_addr, size); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0); + + memcpy_fromio(&info, edma->bar_base, sizeof(info)); + if (le32_to_cpu(info.test_buf_size) < size) { + ret = -EINVAL; + goto err; + } + + ret = pci_endpoint_test_edma_xfer(test->pdev, edma, orig_addr, size, + le64_to_cpu(info.test_buf_phys), + DMA_MEM_TO_DEV); + if (ret) { + dev_err(dev, "pci_endpoint_test_edma_xfer error: %d\n", ret); + goto err; + } + + reinit_completion(&test->irq_raised); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, + COMMAND_REMOTE_EDMA_CHECKSUM); + + left = wait_for_completion_timeout(&test->irq_raised, + msecs_to_jiffies(1000)); + + reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS); + + if (!left || !(reg & STATUS_REMOTE_EDMA_CHECKSUM_SUCCESS)) { + dev_err(dev, "Failed to get checksum\n"); + ret = -EINVAL; + goto err; + } + + crc32 = crc32_le(~0, orig_addr, size); + peer_crc32 = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM); + if (crc32 != peer_crc32) { + dev_err(dev, + "Checksum mismatch: %#x vs %#x\n", crc32, peer_crc32); + ret = -EINVAL; + } +err: + pci_endpoint_test_remote_edma_teardown(test); + pci_endpoint_test_edma_restore_irq(test); + return ret; +} + +static int pci_endpoint_test_edma_read(struct pci_endpoint_test *test, + size_t size) +{ + struct pci_endpoint_test_edma *edma; + struct pci_dev *pdev = test->pdev; + struct device *dev = &pdev->dev; + struct pcitest_edma_info info; + u32 crc32, peer_crc32; + int ret; + + /* + * Note that test->alignment does not apply here. If some vendor + * dmaengine for remote use may impose some alignment restriction, we + * may as well introduce another field such as + * test->remote_dma_alignment. + */ + void *orig_addr __free(kfree) = kzalloc(size, GFP_KERNEL); + if (!orig_addr) + return -ENOMEM; + + ret = pci_endpoint_test_remote_edma_setup(test, size); + if (ret) + return ret; + + peer_crc32 = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM); + + edma = test->data; + if (!edma) { + ret = -ENODEV; + goto err; + } + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0); + + memcpy_fromio(&info, edma->bar_base, sizeof(info)); + if (le32_to_cpu(info.test_buf_size) < size) { + ret = -EINVAL; + goto err; + } + + ret = pci_endpoint_test_edma_xfer(test->pdev, edma, orig_addr, size, + le64_to_cpu(info.test_buf_phys), + DMA_DEV_TO_MEM); + if (ret) { + dev_err(dev, "pci_endpoint_test_edma_xfer error: %d\n", ret); + goto err; + } + + crc32 = crc32_le(~0, orig_addr, size); + if (crc32 != peer_crc32) { + dev_err(dev, + "Checksum mismatch: %#x vs %#x\n", crc32, peer_crc32); + ret = -EINVAL; + } +err: + pci_endpoint_test_remote_edma_teardown(test); + pci_endpoint_test_edma_restore_irq(test); + return ret; +} +#else +static bool pci_endpoint_test_bar_is_reserved(struct pci_endpoint_test *test, + enum pci_barno barno) +{ + return 0; +} + +static void pci_endpoint_test_remote_edma_teardown(struct pci_endpoint_test *test) +{ +} + +static int pci_endpoint_test_edma_write(struct pci_endpoint_test *test, + size_t size) +{ + return -EOPNOTSUPP; +} + +static int pci_endpoint_test_edma_read(struct pci_endpoint_test *test, + size_t size) +{ + return -EOPNOTSUPP; +} +#endif + static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id) { struct pci_endpoint_test *test = dev_id; @@ -307,6 +924,9 @@ static int pci_endpoint_test_bar(struct pci_endpoint_test *test, if (barno == test->test_reg_bar) bar_size = 0x4; + if (pci_endpoint_test_bar_is_reserved(test, barno)) + return -EOPNOTSUPP; + /* * Allocate a buffer of max size 1MB, and reuse that buffer while * iterating over the whole BAR size (which might be much larger). @@ -354,6 +974,9 @@ static void pci_endpoint_test_bars_write_bar(struct pci_endpoint_test *test, if (barno == test->test_reg_bar) size = 0x4; + if (pci_endpoint_test_bar_is_reserved(test, barno)) + return; + for (j = 0; j < size; j += 4) writel_relaxed(bar_test_pattern_with_offset(barno, j), test->bar[barno] + j); @@ -372,6 +995,9 @@ static int pci_endpoint_test_bars_read_bar(struct pci_endpoint_test *test, if (barno == test->test_reg_bar) size = 0x4; + if (pci_endpoint_test_bar_is_reserved(test, barno)) + return 0; + for (j = 0; j < size; j += 4) { u32 expected = bar_test_pattern_with_offset(barno, j); @@ -645,6 +1271,9 @@ static int pci_endpoint_test_write(struct pci_endpoint_test *test, size = param.size; + if (param.flags & PCITEST_FLAGS_USE_REMOTE_EDMA) + return pci_endpoint_test_edma_write(test, size); + use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA); if (use_dma) flags |= FLAG_USE_DMA; @@ -742,6 +1371,9 @@ static int pci_endpoint_test_read(struct pci_endpoint_test *test, size = param.size; + if (param.flags & PCITEST_FLAGS_USE_REMOTE_EDMA) + return pci_endpoint_test_edma_read(test, size); + use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA); if (use_dma) flags |= FLAG_USE_DMA; @@ -1139,6 +1771,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) if (id < 0) return; + pci_endpoint_test_remote_edma_teardown(test); pci_endpoint_test_release_irq(test); pci_endpoint_test_free_irq_vectors(test); diff --git a/include/uapi/linux/pcitest.h b/include/uapi/linux/pcitest.h index d6023a45a9d0..c72d999cecf7 100644 --- a/include/uapi/linux/pcitest.h +++ b/include/uapi/linux/pcitest.h @@ -30,7 +30,8 @@ #define PCITEST_IRQ_TYPE_MSIX 2 #define PCITEST_IRQ_TYPE_AUTO 3 -#define PCITEST_FLAGS_USE_DMA 0x00000001 +#define PCITEST_FLAGS_USE_DMA 0x00000001 +#define PCITEST_FLAGS_USE_REMOTE_EDMA 0x00000002 struct pci_endpoint_test_xfer_param { unsigned long size; -- 2.51.0
