* [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB
@ 2026-10-20 5:49 Koichiro Den
2026-10-20 5:49 ` [PATCH v6 1/8] PCI: endpoint: Add DMA auxiliary resource metadata Koichiro Den
` (7 more replies)
0 siblings, 8 replies; 13+ messages in thread
From: Koichiro Den @ 2026-10-20 5:49 UTC (permalink / raw)
To: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel
Cc: Bjorn Helgaas, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Jingoo Han, Lorenzo Pieralisi, Rob Herring, Jerome Brunet,
linux-pci, linux-doc, linux-kernel, ntb
Hi,
This series lets host Linux use EPC-local DMA read (RC-to-EP) channels
through vNTB. I also tried a separate PCI DMA EPF, which reached v7 [1],
but came back to this vNTB-embedded approach after discussion.
An EPC-local DMA engine can use host DMA addresses (IOVAs when an IOMMU
is enabled) as PCI addresses to reach any host buffer mapped for the
endpoint. Letting the RC side drive its read channels also gives the RC
a direct path into endpoint-local buffers.
A major motivation is direct TX in ntb_transport. Both sides could use
the same dmaengine-based TX path (ie. symmetric), sending directly to RX
buffer DMA addresses supplied by the peer. This would avoid MW bounce
buffers and per-transfer MW remapping for payloads in both directions.
This assumes both sides trust each other. The RC side still programs DMA
over PCIe MMIO, so this is expected to pay off for large data transfers.
DesignWare eDMA is the only supported dmaengine backend for now. Other
EPC-local DMA engines can be added if there is a need and they have a
suitable programming interface.
This series does not include a consumer for the dmaengine channels exposed
to RC Linux. Native HDMA support is also left out to keep the series from
growing too large.
Setting pci-epf-vntb's 'dma_bar' configfs attribute enables DMA export.
The default is -1, which leaves it disabled. pci-epf-ntb only switches to
the shared control layout header and is functionally unchanged.
Dependency
==========
This series has a build dependency on the following dmaengine series,
already in linux-next but not yet in pci/endpoint:
[PATCH v4 0/3] dmaengine: dw-edma: Prepare channels for remote use
https://lore.kernel.org/r/20260912174023.1227397-1-den@valinux.co.jp/
And the following two patches already landed in pci/endpoint.
17c4115cbe99 ("PCI: endpoint: pci-epf-vntb: Use a local epf pointer in interrupt setup")
a5401ce3ab23 ("PCI: endpoint: pci-epf-vntb: Honor MSI-X selection")
Therefore, this submission is (re)based on next-20261019 (3d229ce89862),
with the two patches above applied on top.
Testing
=======
The existing MW-based ntb_netdev path worked with DMA export disabled
(dma_bar = -1) and enabled with the following EP-side BAR settings:
* Dedicated DMA BAR configuration on RK3588 Rock 5B:
ctrl_bar = 0
mw1_bar = 2
db_bar = 4
dma_bar = 5
* MW-shared DMA BAR configuration on R-Car S4 Spider:
ctrl_bar = 0
mw1_bar = 2
dma_bar = 2
db_bar = 4
Best regards,
Koichiro
---
Changes in v6:
- Add a prep patch that moves the control region layout to
include/linux/pci-epf-ntb.h, shared by pci-epf-ntb, pci-epf-vntb and
ntb_hw_epf. (Frank)
For earlier changes, see v5:
https://lore.kernel.org/r/20261009045949.1109871-1-den@valinux.co.jp/
[1] Superseded approach, PCI DMA EPF, v7:
https://lore.kernel.org/r/20260813063757.3131865-1-den@valinux.co.jp/
Koichiro Den (8):
PCI: endpoint: Add DMA auxiliary resource metadata
PCI: dwc: Expose endpoint DMA resources
PCI: endpoint: pci-epf-vntb: Move epf_ntb_is_bar_used() up
PCI: endpoint: Add pci-epf-ntb.h for the shared NTB control layout
PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels
NTB: ntb_hw_epf: Discover vNTB-embedded DMA
PCI/NTB: endpoint: Allow DMA and MW to share a BAR
Documentation: PCI: endpoint: Document vNTB DMA export
Documentation/PCI/endpoint/pci-vntb-howto.rst | 31 +-
MAINTAINERS | 1 +
drivers/ntb/hw/epf/ntb_hw_epf.c | 409 +++++++--
.../pci/controller/dwc/pcie-designware-ep.c | 107 ++-
drivers/pci/endpoint/functions/pci-epf-ntb.c | 85 +-
drivers/pci/endpoint/functions/pci-epf-vntb.c | 783 +++++++++++++++---
include/linux/pci-epc.h | 34 +-
include/linux/pci-epf-ntb.h | 87 ++
8 files changed, 1261 insertions(+), 276 deletions(-)
create mode 100644 include/linux/pci-epf-ntb.h
base-commit: 3d229ce89862b8e82d71a9e11d6f840ca272a6d6
prerequisite-patch-id: f58157e3e05c3723bf60a1e42744c2fbea7d4da8
prerequisite-patch-id: 9e6690c769a60159d50bd9f0a7b66dd269104856
--
2.54.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v6 1/8] PCI: endpoint: Add DMA auxiliary resource metadata
2026-10-20 5:49 [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
@ 2026-10-20 5:49 ` Koichiro Den
2026-10-20 5:49 ` [PATCH v6 2/8] PCI: dwc: Expose endpoint DMA resources Koichiro Den
` (6 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Koichiro Den @ 2026-10-20 5:49 UTC (permalink / raw)
To: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel
Cc: Bjorn Helgaas, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Jingoo Han, Lorenzo Pieralisi, Rob Herring, Jerome Brunet,
linux-pci, linux-doc, linux-kernel, ntb
Endpoint functions that export controller-local DMA channels need the
controller register and descriptor-memory layout.
Add DMA control and descriptor-memory resource types. The control
resource carries the register layout and channel counts. Each descriptor
region is associated with the static channel ID assigned by the local
DMA driver.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes since v4:
- No changes.
include/linux/pci-epc.h | 34 +++++++++++++++++++++++++++++++---
1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index f247cf9bcf1a..e0348e7bc534 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -65,6 +65,8 @@ struct pci_epc_map {
* enum pci_epc_aux_resource_type - auxiliary resource type identifiers
* @PCI_EPC_AUX_DOORBELL_MMIO: Doorbell MMIO, that might be outside the DMA
* controller register window
+ * @PCI_EPC_AUX_DMA_CTRL_MMIO: DMA controller MMIO register window
+ * @PCI_EPC_AUX_DMA_DESC_MEM: DMA descriptor memory
*
* EPC backends may expose auxiliary blocks (e.g. DMA engines) by mapping their
* register windows and descriptor memories into BAR space. This enum
@@ -72,13 +74,25 @@ struct pci_epc_map {
*/
enum pci_epc_aux_resource_type {
PCI_EPC_AUX_DOORBELL_MMIO,
+ PCI_EPC_AUX_DMA_CTRL_MMIO,
+ PCI_EPC_AUX_DMA_DESC_MEM,
};
/**
- * struct pci_epc_aux_resource - a physical auxiliary resource that may be
- * exposed for peer use
+ * enum pci_epc_aux_dma_reg_layout - DMA controller register layout
+ * @PCI_EPC_AUX_DMA_REG_LAYOUT_UNKNOWN: unknown or uninitialized layout
+ * @PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA: Synopsys DesignWare eDMA/HDMA layout
+ */
+enum pci_epc_aux_dma_reg_layout {
+ PCI_EPC_AUX_DMA_REG_LAYOUT_UNKNOWN = 0,
+ PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA,
+};
+
+/**
+ * struct pci_epc_aux_resource - an auxiliary resource that may be exposed for
+ * peer use
* @type: resource type, see enum pci_epc_aux_resource_type
- * @phys_addr: physical base address of the resource
+ * @phys_addr: CPU physical base address of an MMIO resource
* @size: size of the resource in bytes
* @bar: BAR number where this resource is already exposed to the RC
* (NO_BAR if not)
@@ -99,6 +113,20 @@ struct pci_epc_aux_resource {
int irq; /* IRQ number for the doorbell handler */
u32 data; /* write value to ring the doorbell */
} db_mmio;
+
+ /* PCI_EPC_AUX_DMA_CTRL_MMIO */
+ struct {
+ enum pci_epc_aux_dma_reg_layout reg_layout;
+ u32 reg_layout_data;
+ u16 ep_to_rc_ch_cnt;
+ u16 rc_to_ep_ch_cnt;
+ } dma_ctrl;
+
+ /* PCI_EPC_AUX_DMA_DESC_MEM */
+ struct {
+ dma_addr_t dma_addr; /* Endpoint-local DMA address */
+ u16 chan_id;
+ } dma_desc;
} u;
};
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v6 2/8] PCI: dwc: Expose endpoint DMA resources
2026-10-20 5:49 [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
2026-10-20 5:49 ` [PATCH v6 1/8] PCI: endpoint: Add DMA auxiliary resource metadata Koichiro Den
@ 2026-10-20 5:49 ` Koichiro Den
2026-10-20 5:49 ` [PATCH v6 3/8] PCI: endpoint: pci-epf-vntb: Move epf_ntb_is_bar_used() up Koichiro Den
` (5 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Koichiro Den @ 2026-10-20 5:49 UTC (permalink / raw)
To: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel
Cc: Bjorn Helgaas, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Jingoo Han, Lorenzo Pieralisi, Rob Herring, Jerome Brunet,
linux-pci, linux-doc, linux-kernel, ntb
Expose the endpoint-integrated eDMA register window and linked-list memory
through the EPC auxiliary resource API. Endpoint functions can then choose
which channels to export and map the required windows.
Associate each linked-list region with the direction-flattened static
channel ID used by the local dw-edma device. Report these resources only
after that device is registered and only for channels with linked-list
memory.
When the register window is already part of a reserved BAR, report its BAR
and offset. Otherwise report its CPU physical address.
While at it, harden the existing doorbell resource query. Report it
only after the local dw-edma device is registered, and reject VF
queries because DWC cannot assign DMA/HDMA registers to VFs.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes since v4:
- No changes.
.../pci/controller/dwc/pcie-designware-ep.c | 107 ++++++++++++++++--
1 file changed, 99 insertions(+), 8 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 6c18579466b4..2b2988011c3a 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -858,6 +858,15 @@ dw_pcie_ep_find_bar_rsvd_region(struct dw_pcie_ep *ep,
return NULL;
}
+static int dw_pcie_ep_check_edma_vfunc(u8 vfunc_no)
+{
+ /*
+ * The DWC endpoint databook says it is not possible to assign the
+ * DMA/HDMA registers to any Virtual Function.
+ */
+ return vfunc_no ? -EOPNOTSUPP : 0;
+}
+
static int
dw_pcie_ep_get_aux_resources_count(struct pci_epc *epc, u8 func_no,
u8 vfunc_no)
@@ -865,14 +874,30 @@ dw_pcie_ep_get_aux_resources_count(struct pci_epc *epc, u8 func_no,
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct dw_edma_chip *edma = &pci->edma;
+ u16 ll_wr_cnt, ll_rd_cnt;
+ int count = 0;
+ int ret;
if (!pci->edma_reg_size)
return 0;
- if (edma->db_offset == ~0)
+ ret = dw_pcie_ep_check_edma_vfunc(vfunc_no);
+ if (ret)
+ return ret;
+
+ if (!edma->dw)
return 0;
- return 1;
+ ll_wr_cnt = edma->ll_wr_cnt;
+ ll_rd_cnt = edma->ll_rd_cnt;
+
+ if (!edma->cfg_non_ll)
+ count += 1 + ll_wr_cnt + ll_rd_cnt;
+
+ if (edma->db_offset != ~0)
+ count++;
+
+ return count;
}
static int
@@ -885,14 +910,32 @@ dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
const struct pci_epc_bar_rsvd_region *rsvd;
struct dw_edma_chip *edma = &pci->edma;
enum pci_barno dma_ctrl_bar = NO_BAR;
- resource_size_t db_offset = edma->db_offset;
+ resource_size_t db_offset;
resource_size_t dma_ctrl_bar_offset = 0;
resource_size_t dma_reg_size;
- int count;
+ u16 ll_wr_cnt, ll_rd_cnt;
+ bool has_ll;
+ unsigned int i;
+ int count, ret;
- count = dw_pcie_ep_get_aux_resources_count(epc, func_no, vfunc_no);
- if (count < 0)
- return count;
+ if (!pci->edma_reg_size)
+ return 0;
+
+ ret = dw_pcie_ep_check_edma_vfunc(vfunc_no);
+ if (ret)
+ return ret;
+
+ if (!edma->dw)
+ return 0;
+
+ ll_wr_cnt = edma->ll_wr_cnt;
+ ll_rd_cnt = edma->ll_rd_cnt;
+ db_offset = edma->db_offset;
+ has_ll = !edma->cfg_non_ll;
+
+ count = db_offset != ~0;
+ if (has_ll)
+ count += 1 + ll_wr_cnt + ll_rd_cnt;
if (num_resources < count)
return -ENOSPC;
@@ -909,6 +952,54 @@ dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
if (rsvd && rsvd->size < dma_reg_size)
dma_reg_size = rsvd->size;
+ count = 0;
+ if (has_ll) {
+ resources[count++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_CTRL_MMIO,
+ .phys_addr = pci->edma_reg_phys,
+ .size = dma_reg_size,
+ .bar = dma_ctrl_bar,
+ .bar_offset = dma_ctrl_bar_offset,
+ .u.dma_ctrl = {
+ .reg_layout = PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA,
+ .reg_layout_data = edma->mf,
+ .ep_to_rc_ch_cnt = ll_wr_cnt,
+ .rc_to_ep_ch_cnt = ll_rd_cnt,
+ },
+ };
+
+ for (i = 0; i < ll_wr_cnt; i++) {
+ struct dw_edma_region *ll = &edma->ll_region_wr[i];
+
+ resources[count++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_DESC_MEM,
+ .size = ll->sz,
+ .bar = NO_BAR,
+ .u.dma_desc = {
+ .dma_addr = ll->paddr,
+ .chan_id = i,
+ },
+ };
+ }
+
+ for (i = 0; i < ll_rd_cnt; i++) {
+ struct dw_edma_region *ll = &edma->ll_region_rd[i];
+
+ resources[count++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_DESC_MEM,
+ .size = ll->sz,
+ .bar = NO_BAR,
+ .u.dma_desc = {
+ .dma_addr = ll->paddr,
+ .chan_id = ll_wr_cnt + i,
+ },
+ };
+ }
+ }
+
+ if (db_offset == ~0)
+ return 0;
+
/*
* For interrupt-emulation doorbells, report a standalone resource
* instead of bundling it into the DMA controller MMIO resource.
@@ -917,7 +1008,7 @@ dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
sizeof(u32), dma_reg_size))
return -EINVAL;
- resources[0] = (struct pci_epc_aux_resource) {
+ resources[count] = (struct pci_epc_aux_resource) {
.type = PCI_EPC_AUX_DOORBELL_MMIO,
.phys_addr = pci->edma_reg_phys + db_offset,
.size = sizeof(u32),
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v6 3/8] PCI: endpoint: pci-epf-vntb: Move epf_ntb_is_bar_used() up
2026-10-20 5:49 [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
2026-10-20 5:49 ` [PATCH v6 1/8] PCI: endpoint: Add DMA auxiliary resource metadata Koichiro Den
2026-10-20 5:49 ` [PATCH v6 2/8] PCI: dwc: Expose endpoint DMA resources Koichiro Den
@ 2026-10-20 5:49 ` Koichiro Den
2026-10-20 5:49 ` [PATCH v6 4/8] PCI: endpoint: Add pci-epf-ntb.h for the shared NTB control layout Koichiro Den
` (4 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Koichiro Den @ 2026-10-20 5:49 UTC (permalink / raw)
To: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel
Cc: Bjorn Helgaas, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Jingoo Han, Lorenzo Pieralisi, Rob Herring, Jerome Brunet,
linux-pci, linux-doc, linux-kernel, ntb
Move epf_ntb_is_bar_used() up so later BAR selection code can call it
without a forward declaration. No functional change.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes since v4:
- No changes.
drivers/pci/endpoint/functions/pci-epf-vntb.c | 40 +++++++++----------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index f77e28f2fe96..1a0ce31e680c 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -244,6 +244,26 @@ static int epf_ntb_set_link_enabled(struct epf_ntb *ntb, bool enabled)
return 0;
}
+/**
+ * epf_ntb_is_bar_used() - Check if a bar is used in the ntb configuration
+ * @ntb: NTB device that facilitates communication between HOST and VHOST
+ * @barno: Checked bar number
+ *
+ * Returns: true if used, false if free.
+ */
+static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
+ enum pci_barno barno)
+{
+ int i;
+
+ for (i = 0; i < VNTB_BAR_NUM; i++) {
+ if (ntb->epf_ntb_bar[i] == barno)
+ return true;
+ }
+
+ return false;
+}
+
/**
* epf_ntb_configure_mw() - Configure the Outbound Address Space for VHOST
* to access the memory window of HOST
@@ -906,26 +926,6 @@ static void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws)
}
}
-/**
- * epf_ntb_is_bar_used() - Check if a bar is used in the ntb configuration
- * @ntb: NTB device that facilitates communication between HOST and VHOST
- * @barno: Checked bar number
- *
- * Returns: true if used, false if free.
- */
-static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
- enum pci_barno barno)
-{
- int i;
-
- for (i = 0; i < VNTB_BAR_NUM; i++) {
- if (ntb->epf_ntb_bar[i] == barno)
- return true;
- }
-
- return false;
-}
-
/**
* epf_ntb_find_bar() - Assign BAR number when no configuration is provided
* @ntb: NTB device that facilitates communication between HOST and VHOST
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v6 4/8] PCI: endpoint: Add pci-epf-ntb.h for the shared NTB control layout
2026-10-20 5:49 [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
` (2 preceding siblings ...)
2026-10-20 5:49 ` [PATCH v6 3/8] PCI: endpoint: pci-epf-vntb: Move epf_ntb_is_bar_used() up Koichiro Den
@ 2026-10-20 5:49 ` Koichiro Den
2026-10-20 5:49 ` [PATCH v6 5/8] PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels Koichiro Den
` (3 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Koichiro Den @ 2026-10-20 5:49 UTC (permalink / raw)
To: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel
Cc: Bjorn Helgaas, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Jingoo Han, Lorenzo Pieralisi, Rob Herring, Jerome Brunet,
linux-pci, linux-doc, linux-kernel, ntb
Currently, pci-epf-ntb.c and pci-epf-vntb.c each define struct
epf_ntb_ctrl, and ntb_hw_epf.c mirrors the same layout with hard-coded
register offsets.
Move the shared struct and macros to include/linux/pci-epf-ntb.h, and
have ntb_hw_epf.c derive offsets from the struct with offsetof(). The
vNTB "reserved" field takes the pci-epf-ntb.c name mw1_offset, which is
what ntb_hw_epf.c reads at that offset.
No functional changes.
Suggested-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes since v5:
- New patch. (Frank)
https://lore.kernel.org/r/as5F0EQB3owMYmFV@SMW015318/
MAINTAINERS | 1 +
drivers/ntb/hw/epf/ntb_hw_epf.c | 95 +++++--------
drivers/pci/endpoint/functions/pci-epf-ntb.c | 85 ++++--------
drivers/pci/endpoint/functions/pci-epf-vntb.c | 131 +++++++-----------
include/linux/pci-epf-ntb.h | 48 +++++++
5 files changed, 163 insertions(+), 197 deletions(-)
create mode 100644 include/linux/pci-epf-ntb.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 160304cdff7a..f192be243435 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19882,6 +19882,7 @@ F: drivers/ntb/
F: drivers/pci/endpoint/functions/pci-epf-*ntb.c
F: include/linux/ntb.h
F: include/linux/ntb_transport.h
+F: include/linux/pci-epf-ntb.h
F: tools/testing/selftests/ntb/
NTB INTEL DRIVER
diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c
index c47607d4f8a7..ff92c2a61ce7 100644
--- a/drivers/ntb/hw/epf/ntb_hw_epf.c
+++ b/drivers/ntb/hw/epf/ntb_hw_epf.c
@@ -8,41 +8,15 @@
#include <linux/atomic.h>
#include <linux/delay.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/pci-epf-ntb.h>
#include <linux/slab.h>
#include <linux/ntb.h>
-#define NTB_EPF_COMMAND 0x0
-#define CMD_CONFIGURE_DOORBELL 1
-#define CMD_TEARDOWN_DOORBELL 2
-#define CMD_CONFIGURE_MW 3
-#define CMD_TEARDOWN_MW 4
-#define CMD_LINK_UP 5
-#define CMD_LINK_DOWN 6
-
-#define NTB_EPF_ARGUMENT 0x4
-#define MSIX_ENABLE BIT(16)
-
-#define NTB_EPF_CMD_STATUS 0x8
-#define COMMAND_STATUS_OK 1
-#define COMMAND_STATUS_ERROR 2
-
-#define NTB_EPF_LINK_STATUS 0x0A
-#define LINK_STATUS_UP BIT(0)
-
-#define NTB_EPF_TOPOLOGY 0x0C
-#define NTB_EPF_LOWER_ADDR 0x10
-#define NTB_EPF_UPPER_ADDR 0x14
-#define NTB_EPF_LOWER_SIZE 0x18
-#define NTB_EPF_UPPER_SIZE 0x1C
-#define NTB_EPF_MW_COUNT 0x20
-#define NTB_EPF_MW1_OFFSET 0x24
-#define NTB_EPF_SPAD_OFFSET 0x28
-#define NTB_EPF_SPAD_COUNT 0x2C
-#define NTB_EPF_DB_ENTRY_SIZE 0x30
-#define NTB_EPF_DB_DATA(n) (0x34 + (n) * 4)
-#define NTB_EPF_DB_OFFSET(n) (0xB4 + (n) * 4)
+/* Offsets into the control region, see include/linux/pci-epf-ntb.h. */
+#define NTB_EPF_CTRL(field) offsetof(struct epf_ntb_ctrl, field)
/*
* Legacy doorbell slot layout when paired with pci-epf-*ntb:
@@ -132,20 +106,20 @@ static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command,
u32 status;
mutex_lock(&ndev->cmd_lock);
- writel(argument, ndev->ctrl_reg + NTB_EPF_ARGUMENT);
- writel(command, ndev->ctrl_reg + NTB_EPF_COMMAND);
+ writel(argument, ndev->ctrl_reg + NTB_EPF_CTRL(argument));
+ writel(command, ndev->ctrl_reg + NTB_EPF_CTRL(command));
timeout = ktime_add_ms(ktime_get(), NTB_EPF_COMMAND_TIMEOUT);
while (1) {
timedout = ktime_after(ktime_get(), timeout);
- status = readw(ndev->ctrl_reg + NTB_EPF_CMD_STATUS);
+ status = readw(ndev->ctrl_reg + NTB_EPF_CTRL(command_status));
- if (status == COMMAND_STATUS_ERROR) {
+ if (status == EPF_NTB_COMMAND_STATUS_ERROR) {
ret = -EINVAL;
break;
}
- if (status == COMMAND_STATUS_OK)
+ if (status == EPF_NTB_COMMAND_STATUS_OK)
break;
if (WARN_ON(timedout)) {
@@ -156,7 +130,7 @@ static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command,
usleep_range(5, 10);
}
- writew(0, ndev->ctrl_reg + NTB_EPF_CMD_STATUS);
+ writew(0, ndev->ctrl_reg + NTB_EPF_CTRL(command_status));
mutex_unlock(&ndev->cmd_lock);
return ret;
@@ -224,9 +198,9 @@ static u64 ntb_epf_link_is_up(struct ntb_dev *ntb,
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
u32 status;
- status = readw(ndev->ctrl_reg + NTB_EPF_LINK_STATUS);
+ status = readw(ndev->ctrl_reg + NTB_EPF_CTRL(link_status));
- return status & LINK_STATUS_UP;
+ return status & EPF_NTB_LINK_STATUS_UP;
}
static u32 ntb_epf_spad_read(struct ntb_dev *ntb, int idx)
@@ -240,7 +214,7 @@ static u32 ntb_epf_spad_read(struct ntb_dev *ntb, int idx)
return 0;
}
- offset = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET);
+ offset = readl(ndev->ctrl_reg + NTB_EPF_CTRL(spad_offset));
offset += (idx << 2);
return readl(ndev->ctrl_reg + offset);
@@ -258,7 +232,7 @@ static int ntb_epf_spad_write(struct ntb_dev *ntb,
return -EINVAL;
}
- offset = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET);
+ offset = readl(ndev->ctrl_reg + NTB_EPF_CTRL(spad_offset));
offset += (idx << 2);
writel(val, ndev->ctrl_reg + offset);
@@ -316,7 +290,7 @@ static int ntb_epf_link_enable(struct ntb_dev *ntb,
struct device *dev = ndev->dev;
int ret;
- ret = ntb_epf_send_command(ndev, CMD_LINK_UP, 0);
+ ret = ntb_epf_send_command(ndev, EPF_NTB_COMMAND_LINK_UP, 0);
if (ret) {
dev_err(dev, "Fail to enable link\n");
return ret;
@@ -331,7 +305,7 @@ static int ntb_epf_link_disable(struct ntb_dev *ntb)
struct device *dev = ndev->dev;
int ret;
- ret = ntb_epf_send_command(ndev, CMD_LINK_DOWN, 0);
+ ret = ntb_epf_send_command(ndev, EPF_NTB_COMMAND_LINK_DOWN, 0);
if (ret) {
dev_err(dev, "Fail to disable link\n");
return ret;
@@ -373,7 +347,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
{
struct pci_dev *pdev = ndev->ntb.pdev;
struct device *dev = ndev->dev;
- u32 argument = MSIX_ENABLE;
+ u32 argument = EPF_NTB_MSIX_ENABLE;
int irq;
int ret;
int i;
@@ -387,7 +361,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
dev_err(dev, "Failed to get MSI interrupts\n");
return irq;
}
- argument &= ~MSIX_ENABLE;
+ argument &= ~EPF_NTB_MSIX_ENABLE;
}
ndev->db_count = irq - 1;
@@ -402,7 +376,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
}
}
- ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL,
+ ret = ntb_epf_send_command(ndev, EPF_NTB_COMMAND_CONFIGURE_DOORBELL,
argument | irq);
if (ret) {
dev_err(dev, "Failed to configure doorbell\n");
@@ -494,11 +468,9 @@ static int ntb_epf_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx,
return -EINVAL;
}
- writel(lower_32_bits(addr), ndev->ctrl_reg + NTB_EPF_LOWER_ADDR);
- writel(upper_32_bits(addr), ndev->ctrl_reg + NTB_EPF_UPPER_ADDR);
- writel(lower_32_bits(size), ndev->ctrl_reg + NTB_EPF_LOWER_SIZE);
- writel(upper_32_bits(size), ndev->ctrl_reg + NTB_EPF_UPPER_SIZE);
- ntb_epf_send_command(ndev, CMD_CONFIGURE_MW, idx);
+ lo_hi_writeq(addr, ndev->ctrl_reg + NTB_EPF_CTRL(addr));
+ lo_hi_writeq(size, ndev->ctrl_reg + NTB_EPF_CTRL(size));
+ ntb_epf_send_command(ndev, EPF_NTB_COMMAND_CONFIGURE_MW, idx);
return 0;
}
@@ -509,7 +481,7 @@ static int ntb_epf_mw_clear_trans(struct ntb_dev *ntb, int pidx, int idx)
struct device *dev = ndev->dev;
int ret = 0;
- ntb_epf_send_command(ndev, CMD_TEARDOWN_MW, idx);
+ ntb_epf_send_command(ndev, EPF_NTB_COMMAND_TEARDOWN_MW, idx);
if (ret)
dev_err(dev, "Failed to teardown memory window\n");
@@ -524,7 +496,7 @@ static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx,
int bar;
if (idx == 0)
- offset = readl(ndev->ctrl_reg + NTB_EPF_MW1_OFFSET);
+ offset = readl(ndev->ctrl_reg + NTB_EPF_CTRL(mw1_offset));
bar = ntb_epf_mw_to_bar(ndev, idx);
if (bar < 0)
@@ -562,10 +534,12 @@ static int ntb_epf_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
return -EINVAL;
}
- db_entry_size = readl(ndev->ctrl_reg + NTB_EPF_DB_ENTRY_SIZE);
+ db_entry_size = readl(ndev->ctrl_reg + NTB_EPF_CTRL(db_entry_size));
- db_data = readl(ndev->ctrl_reg + NTB_EPF_DB_DATA(interrupt_num));
- db_offset = readl(ndev->ctrl_reg + NTB_EPF_DB_OFFSET(interrupt_num));
+ db_data = readl(ndev->ctrl_reg +
+ NTB_EPF_CTRL(db_data[interrupt_num]));
+ db_offset = readl(ndev->ctrl_reg +
+ NTB_EPF_CTRL(db_offset[interrupt_num]));
writel(db_data, ndev->db_reg + (db_entry_size * interrupt_num) +
db_offset);
@@ -631,7 +605,7 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
struct device *dev = ndev->dev;
int ret;
- ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT);
+ ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_CTRL(num_mws));
if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) {
dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count);
return -EINVAL;
@@ -650,7 +624,7 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
* doorbell layout, hence -1.
*/
ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1;
- ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT);
+ ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_CTRL(spad_count));
return 0;
}
@@ -702,8 +676,8 @@ static int ntb_epf_init_pci(struct ntb_epf_dev *ndev,
goto err_pci_regions;
}
} else {
- spad_sz = 4 * readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT);
- spad_off = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET);
+ spad_sz = 4 * readl(ndev->ctrl_reg + NTB_EPF_CTRL(spad_count));
+ spad_off = readl(ndev->ctrl_reg + NTB_EPF_CTRL(spad_offset));
ndev->peer_spad_reg = ndev->ctrl_reg + spad_off + spad_sz;
}
@@ -743,7 +717,8 @@ static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev)
struct pci_dev *pdev = ndev->ntb.pdev;
int i;
- ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1);
+ ntb_epf_send_command(ndev, EPF_NTB_COMMAND_TEARDOWN_DOORBELL,
+ ndev->db_count + 1);
for (i = 0; i < ndev->db_count + 1; i++)
free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]);
diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c
index 5314aca2188a..925cf46219d7 100644
--- a/drivers/pci/endpoint/functions/pci-epf-ntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c
@@ -41,27 +41,13 @@
#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
+#include <linux/pci-epf-ntb.h>
static struct workqueue_struct *kpcintb_workqueue;
-#define COMMAND_CONFIGURE_DOORBELL 1
-#define COMMAND_TEARDOWN_DOORBELL 2
-#define COMMAND_CONFIGURE_MW 3
-#define COMMAND_TEARDOWN_MW 4
-#define COMMAND_LINK_UP 5
-#define COMMAND_LINK_DOWN 6
-
-#define COMMAND_STATUS_OK 1
-#define COMMAND_STATUS_ERROR 2
-
-#define LINK_STATUS_UP BIT(0)
-
#define SPAD_COUNT 64
#define DB_COUNT 4
#define NTB_MW_OFFSET 2
-#define DB_COUNT_MASK GENMASK(15, 0)
-#define MSIX_ENABLE BIT(16)
-#define MAX_DB_COUNT 32
#define MAX_MW 4
enum epf_ntb_bar {
@@ -104,23 +90,6 @@ struct epf_ntb_epc {
const struct pci_epc_features *epc_features;
};
-struct epf_ntb_ctrl {
- u32 command;
- u32 argument;
- u16 command_status;
- u16 link_status;
- u32 topology;
- u64 addr;
- u64 size;
- u32 num_mws;
- u32 mw1_offset;
- u32 spad_offset;
- u32 spad_count;
- u32 db_entry_size;
- u32 db_data[MAX_DB_COUNT];
- u32 db_offset[MAX_DB_COUNT];
-} __packed;
-
static struct pci_epf_header epf_ntb_header = {
.vendorid = PCI_ANY_ID,
.deviceid = PCI_ANY_ID,
@@ -156,9 +125,9 @@ static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up)
is_msix = ntb_epc->is_msix;
ctrl = ntb_epc->reg;
if (link_up)
- ctrl->link_status |= LINK_STATUS_UP;
+ ctrl->link_status |= EPF_NTB_LINK_STATUS_UP;
else
- ctrl->link_status &= ~LINK_STATUS_UP;
+ ctrl->link_status &= ~EPF_NTB_LINK_STATUS_UP;
irq_type = is_msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI;
ret = pci_epc_raise_irq(epc, func_no, vfunc_no, irq_type, 1);
if (ret) {
@@ -562,9 +531,9 @@ static int epf_ntb_configure_db(struct epf_ntb *ntb,
ntb_epc = ntb->epc[type];
epc = ntb_epc->epc;
- if (!db_count || db_count > MAX_DB_COUNT) {
+ if (!db_count || db_count > EPF_NTB_MAX_DB_COUNT) {
dev_err(&epc->dev, "DB count %d out of range (1 - %d)\n",
- db_count, MAX_DB_COUNT);
+ db_count, EPF_NTB_MAX_DB_COUNT);
return -EINVAL;
}
@@ -648,50 +617,52 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
dev = &ntb->epf->dev;
switch (command) {
- case COMMAND_CONFIGURE_DOORBELL:
- db_count = argument & DB_COUNT_MASK;
- is_msix = argument & MSIX_ENABLE;
+ case EPF_NTB_COMMAND_CONFIGURE_DOORBELL:
+ db_count = argument & EPF_NTB_DB_COUNT_MASK;
+ is_msix = argument & EPF_NTB_MSIX_ENABLE;
ret = epf_ntb_configure_db(ntb, type, db_count, is_msix);
if (ret < 0)
- ctrl->command_status = COMMAND_STATUS_ERROR;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_ERROR;
else
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
- case COMMAND_TEARDOWN_DOORBELL:
+ case EPF_NTB_COMMAND_TEARDOWN_DOORBELL:
epf_ntb_teardown_db(ntb, type);
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
- case COMMAND_CONFIGURE_MW:
+ case EPF_NTB_COMMAND_CONFIGURE_MW:
ret = epf_ntb_configure_mw(ntb, type, argument);
if (ret < 0)
- ctrl->command_status = COMMAND_STATUS_ERROR;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_ERROR;
else
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
- case COMMAND_TEARDOWN_MW:
+ case EPF_NTB_COMMAND_TEARDOWN_MW:
epf_ntb_teardown_mw(ntb, type, argument);
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
- case COMMAND_LINK_UP:
+ case EPF_NTB_COMMAND_LINK_UP:
ntb_epc->linkup = true;
if (ntb->epc[PRIMARY_INTERFACE]->linkup &&
ntb->epc[SECONDARY_INTERFACE]->linkup) {
ret = epf_ntb_link_up(ntb, true);
if (ret < 0)
- ctrl->command_status = COMMAND_STATUS_ERROR;
+ ctrl->command_status =
+ EPF_NTB_COMMAND_STATUS_ERROR;
else
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status =
+ EPF_NTB_COMMAND_STATUS_OK;
goto reset_handler;
}
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
- case COMMAND_LINK_DOWN:
+ case EPF_NTB_COMMAND_LINK_DOWN:
ntb_epc->linkup = false;
ret = epf_ntb_link_up(ntb, false);
if (ret < 0)
- ctrl->command_status = COMMAND_STATUS_ERROR;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_ERROR;
else
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
default:
dev_err(dev, "%s intf UNKNOWN command: %d\n",
@@ -1298,9 +1269,9 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb,
func_no = ntb_epc->func_no;
vfunc_no = ntb_epc->vfunc_no;
- if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) {
+ if (!ntb->db_count || ntb->db_count > EPF_NTB_MAX_DB_COUNT) {
dev_err(dev, "DB count %d out of range (1 - %d)\n",
- ntb->db_count, MAX_DB_COUNT);
+ ntb->db_count, EPF_NTB_MAX_DB_COUNT);
return -EINVAL;
}
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index 1a0ce31e680c..3adbaa9d85db 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -47,27 +47,14 @@
#include <linux/pci-ep-msi.h>
#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
+#include <linux/pci-epf-ntb.h>
#include <linux/ntb.h>
static struct workqueue_struct *kpcintb_workqueue;
-#define COMMAND_CONFIGURE_DOORBELL 1
-#define COMMAND_TEARDOWN_DOORBELL 2
-#define COMMAND_CONFIGURE_MW 3
-#define COMMAND_TEARDOWN_MW 4
-#define COMMAND_LINK_UP 5
-#define COMMAND_LINK_DOWN 6
-
-#define COMMAND_STATUS_OK 1
-#define COMMAND_STATUS_ERROR 2
-
-#define LINK_STATUS_UP BIT(0)
-
#define SPAD_COUNT 64
#define DB_COUNT 4
#define NTB_MW_OFFSET 2
-#define DB_COUNT_MASK GENMASK(15, 0)
-#define MSIX_ENABLE BIT(16)
#define MAX_MW 4
/* Limit per-work execution to avoid monopolizing kworker on doorbell storms. */
@@ -90,47 +77,6 @@ enum epf_irq_slot {
};
#define MIN_DB_COUNT (EPF_IRQ_DB_START + 1)
-#define MAX_DB_COUNT 32
-
-/*
- * +--------------------------------------------------+ Base
- * | |
- * | |
- * | |
- * | Common Control Register |
- * | |
- * | |
- * | |
- * +-----------------------+--------------------------+ Base+spad_offset
- * | | |
- * | Peer Spad Space | Spad Space |
- * | | |
- * | | |
- * +-----------------------+--------------------------+ Base+spad_offset
- * | | | +spad_count * 4
- * | | |
- * | Spad Space | Peer Spad Space |
- * | | |
- * +-----------------------+--------------------------+
- * Virtual PCI PCIe Endpoint
- * NTB Driver NTB Driver
- */
-struct epf_ntb_ctrl {
- u32 command;
- u32 argument;
- u16 command_status;
- u16 link_status;
- u32 topology;
- u64 addr;
- u64 size;
- u32 num_mws;
- u32 reserved;
- u32 spad_offset;
- u32 spad_count;
- u32 db_entry_size;
- u32 db_data[MAX_DB_COUNT];
- u32 db_offset[MAX_DB_COUNT];
-} __packed;
struct epf_ntb {
struct ntb_dev *ntb;
@@ -195,9 +141,9 @@ static void epf_ntb_update_link(struct epf_ntb *ntb)
/* The link is usable only after both sides have enabled it. */
if (ntb->host_link_enabled && ntb->vhost_link_enabled)
- link_status |= LINK_STATUS_UP;
+ link_status |= EPF_NTB_LINK_STATUS_UP;
else
- link_status &= ~LINK_STATUS_UP;
+ link_status &= ~EPF_NTB_LINK_STATUS_UP;
WRITE_ONCE(ntb->reg->link_status, link_status);
}
@@ -368,45 +314,47 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
dev = &ntb->epf->dev;
switch (command) {
- case COMMAND_CONFIGURE_DOORBELL:
+ case EPF_NTB_COMMAND_CONFIGURE_DOORBELL:
epc_features = pci_epc_get_features(ntb->epf->epc,
ntb->epf->func_no,
ntb->epf->vfunc_no);
- if (((argument & MSIX_ENABLE) && !epc_features->msix_capable) ||
- (!(argument & MSIX_ENABLE) && !epc_features->msi_capable)) {
- ctrl->command_status = COMMAND_STATUS_ERROR;
+ if (((argument & EPF_NTB_MSIX_ENABLE) &&
+ !epc_features->msix_capable) ||
+ (!(argument & EPF_NTB_MSIX_ENABLE) &&
+ !epc_features->msi_capable)) {
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_ERROR;
break;
}
- ntb->peer_msix = argument & MSIX_ENABLE;
- ctrl->command_status = COMMAND_STATUS_OK;
+ ntb->peer_msix = argument & EPF_NTB_MSIX_ENABLE;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
- case COMMAND_TEARDOWN_DOORBELL:
- ctrl->command_status = COMMAND_STATUS_OK;
+ case EPF_NTB_COMMAND_TEARDOWN_DOORBELL:
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
- case COMMAND_CONFIGURE_MW:
+ case EPF_NTB_COMMAND_CONFIGURE_MW:
ret = epf_ntb_configure_mw(ntb, argument);
if (ret < 0)
- ctrl->command_status = COMMAND_STATUS_ERROR;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_ERROR;
else
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
- case COMMAND_TEARDOWN_MW:
+ case EPF_NTB_COMMAND_TEARDOWN_MW:
epf_ntb_teardown_mw(ntb, argument);
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
- case COMMAND_LINK_UP:
+ case EPF_NTB_COMMAND_LINK_UP:
ret = epf_ntb_set_link_enabled(ntb, true);
if (ret < 0)
- ctrl->command_status = COMMAND_STATUS_ERROR;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_ERROR;
else
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
goto reset_handler;
- case COMMAND_LINK_DOWN:
+ case EPF_NTB_COMMAND_LINK_DOWN:
ret = epf_ntb_set_link_enabled(ntb, false);
if (ret < 0)
- ctrl->command_status = COMMAND_STATUS_ERROR;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_ERROR;
else
- ctrl->command_status = COMMAND_STATUS_OK;
+ ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
default:
dev_err(dev, "UNKNOWN command: %d\n", command);
@@ -518,6 +466,28 @@ static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
* region
* @ntb: NTB device that facilitates communication between HOST and VHOST
*
+ * +--------------------------------------------------+ Base
+ * | |
+ * | |
+ * | |
+ * | Common Control Register |
+ * | |
+ * | |
+ * | |
+ * +-----------------------+--------------------------+ Base+spad_offset
+ * | | |
+ * | Peer Spad Space | Spad Space |
+ * | | |
+ * | | |
+ * +-----------------------+--------------------------+ Base+spad_offset
+ * | | | +spad_count * 4
+ * | | |
+ * | Spad Space | Peer Spad Space |
+ * | | |
+ * +-----------------------+--------------------------+
+ * Virtual PCI PCIe Endpoint
+ * NTB Driver NTB Driver
+ *
* Allocate the Local Memory mentioned in the above diagram. The size of
* CONFIG REGION is sizeof(struct epf_ntb_ctrl), plus space for the MSI-X
* Table and PBA if supported. The size of SCRATCHPAD REGION is obtained
@@ -602,9 +572,10 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
return -EINVAL;
}
- if (ntb->db_count < MIN_DB_COUNT || ntb->db_count > MAX_DB_COUNT) {
+ if (ntb->db_count < MIN_DB_COUNT ||
+ ntb->db_count > EPF_NTB_MAX_DB_COUNT) {
dev_err(dev, "DB count %d out of range (%d - %d)\n",
- ntb->db_count, MIN_DB_COUNT, MAX_DB_COUNT);
+ ntb->db_count, MIN_DB_COUNT, EPF_NTB_MAX_DB_COUNT);
return -EINVAL;
}
@@ -1258,7 +1229,7 @@ static ssize_t epf_ntb_db_count_store(struct config_item *item,
if (ret)
return ret;
- if (val < MIN_DB_COUNT || val > MAX_DB_COUNT)
+ if (val < MIN_DB_COUNT || val > EPF_NTB_MAX_DB_COUNT)
return -EINVAL;
WRITE_ONCE(ntb->db_count, val);
@@ -1456,7 +1427,7 @@ static int vntb_epf_db_vector_count(struct ntb_dev *ntb)
*
* Report only usable per-vector doorbell interrupts.
*/
- if (db_count < MIN_DB_COUNT || db_count > MAX_DB_COUNT)
+ if (db_count < MIN_DB_COUNT || db_count > EPF_NTB_MAX_DB_COUNT)
return 0;
return db_count - EPF_IRQ_DB_START;
diff --git a/include/linux/pci-epf-ntb.h b/include/linux/pci-epf-ntb.h
new file mode 100644
index 000000000000..0c02d709aa08
--- /dev/null
+++ b/include/linux/pci-epf-ntb.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Control region layout shared by the PCI endpoint NTB function drivers
+ * (pci-epf-ntb, pci-epf-vntb) and the host side ntb_hw_epf driver.
+ */
+
+#ifndef __LINUX_PCI_EPF_NTB_H
+#define __LINUX_PCI_EPF_NTB_H
+
+#include <linux/bits.h>
+#include <linux/types.h>
+
+#define EPF_NTB_COMMAND_CONFIGURE_DOORBELL 1
+#define EPF_NTB_COMMAND_TEARDOWN_DOORBELL 2
+#define EPF_NTB_COMMAND_CONFIGURE_MW 3
+#define EPF_NTB_COMMAND_TEARDOWN_MW 4
+#define EPF_NTB_COMMAND_LINK_UP 5
+#define EPF_NTB_COMMAND_LINK_DOWN 6
+
+/* EPF_NTB_COMMAND_CONFIGURE_DOORBELL argument */
+#define EPF_NTB_DB_COUNT_MASK GENMASK(15, 0)
+#define EPF_NTB_MSIX_ENABLE BIT(16)
+
+#define EPF_NTB_COMMAND_STATUS_OK 1
+#define EPF_NTB_COMMAND_STATUS_ERROR 2
+
+#define EPF_NTB_LINK_STATUS_UP BIT(0)
+
+#define EPF_NTB_MAX_DB_COUNT 32
+
+struct epf_ntb_ctrl {
+ u32 command;
+ u32 argument;
+ u16 command_status;
+ u16 link_status;
+ u32 topology;
+ u64 addr;
+ u64 size;
+ u32 num_mws;
+ u32 mw1_offset;
+ u32 spad_offset;
+ u32 spad_count;
+ u32 db_entry_size;
+ u32 db_data[EPF_NTB_MAX_DB_COUNT];
+ u32 db_offset[EPF_NTB_MAX_DB_COUNT];
+} __packed;
+
+#endif /* __LINUX_PCI_EPF_NTB_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v6 5/8] PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels
2026-10-20 5:49 [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
` (3 preceding siblings ...)
2026-10-20 5:49 ` [PATCH v6 4/8] PCI: endpoint: Add pci-epf-ntb.h for the shared NTB control layout Koichiro Den
@ 2026-10-20 5:49 ` Koichiro Den
2026-10-20 17:00 ` Frank Li
2026-10-20 5:49 ` [PATCH v6 6/8] NTB: ntb_hw_epf: Discover vNTB-embedded DMA Koichiro Den
` (2 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Koichiro Den @ 2026-10-20 5:49 UTC (permalink / raw)
To: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel
Cc: Bjorn Helgaas, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Jingoo Han, Lorenzo Pieralisi, Rob Herring, Jerome Brunet,
linux-pci, linux-doc, linux-kernel, ntb
An RC may use endpoint-local DMA read channels to transfer data directly
to an endpoint DMA address once both sides agree to use them. Quiescing
an unrolled eDMA channel disables its whole direction, so reserve the
complete read direction and route its interrupts to the RC when dma_bar
is configured.
Describe the controller and per-channel descriptor memory in a private
control-region extension.
Add a dma_bar configfs attribute. An explicit BAR selection enables DMA
export. Leaving it unassigned keeps the feature disabled.
Keep resources already assigned to a BAR in place, and map the rest
through the selected dma_bar.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes since v5:
- Move the DMA extension layout to include/linux/pci-epf-ntb.h and
place it right after struct epf_ntb_ctrl instead of embedding it,
since pci-epf-ntb now shares the structure. (Frank)
- Small refactorings:
* Minimize the kernel-doc change in epf_ntb_config_spad_bar_alloc().
* Group DMA BAR init and clear with the common DMA helpers.
- Link to v5: https://lore.kernel.org/r/20261009045949.1109871-5-den@valinux.co.jp/
drivers/pci/endpoint/functions/pci-epf-vntb.c | 487 +++++++++++++++++-
include/linux/pci-epf-ntb.h | 39 ++
2 files changed, 517 insertions(+), 9 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index 3adbaa9d85db..ac83af54122f 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -39,9 +39,13 @@
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/delay.h>
+#include <linux/dma/edma.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
#include <linux/pci-ep-msi.h>
@@ -67,6 +71,7 @@ enum epf_ntb_bar {
BAR_MW2,
BAR_MW3,
BAR_MW4,
+ BAR_DMA,
VNTB_BAR_NUM,
};
@@ -78,6 +83,18 @@ enum epf_irq_slot {
#define MIN_DB_COUNT (EPF_IRQ_DB_START + 1)
+struct epf_ntb_dma {
+ struct epf_ntb_dma_ctrl ctrl;
+ struct dma_chan *dchan[EDMA_MAX_RD_CH];
+ void *bar_scratch;
+ dma_addr_t bar_scratch_phys;
+ size_t bar_scratch_size;
+ struct pci_epf_bar_submap submap[EDMA_MAX_RD_CH + 2];
+ struct pci_epf_bar_submap *reg_submap;
+ unsigned int num_submap;
+ u16 rd_ch_cnt;
+};
+
struct epf_ntb {
struct ntb_dev *ntb;
struct pci_epf *epf;
@@ -111,6 +128,7 @@ struct epf_ntb {
enum pci_barno epf_ntb_bar[VNTB_BAR_NUM];
struct epf_ntb_ctrl *reg;
+ struct epf_ntb_dma *dma;
u32 *epf_db;
@@ -202,7 +220,8 @@ static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
{
int i;
- for (i = 0; i < VNTB_BAR_NUM; i++) {
+ /* BAR_DMA is checked separately because it may share an MW BAR. */
+ for (i = 0; i < BAR_DMA; i++) {
if (ntb->epf_ntb_bar[i] == barno)
return true;
}
@@ -210,6 +229,419 @@ static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
return false;
}
+struct epf_ntb_dma_filter {
+ struct device *dev;
+ int chan_id;
+};
+
+static bool epf_ntb_dma_filter(struct dma_chan *chan, void *data)
+{
+ struct epf_ntb_dma_filter *filter = data;
+
+ return chan->device->dev == filter->dev &&
+ chan->chan_id == filter->chan_id;
+}
+
+static int epf_ntb_dma_add_region(struct epf_ntb_dma *dma,
+ const struct pci_epc_aux_resource *resource,
+ dma_addr_t target_addr,
+ enum pci_barno barno, size_t align, u32 *next,
+ struct epf_ntb_dma_region_ctrl *region)
+{
+ resource_size_t delta, map_size, size;
+ struct pci_epf_bar_submap *submap;
+ dma_addr_t base;
+
+ if (!resource->size || resource->size > U32_MAX)
+ return -EINVAL;
+
+ region->size = resource->size;
+ if (resource->bar != NO_BAR) {
+ if (resource->bar < BAR_0 || resource->bar > BAR_5 ||
+ resource->bar_offset > U32_MAX)
+ return -EINVAL;
+
+ region->bar = resource->bar;
+ region->offset = resource->bar_offset;
+ return 0;
+ }
+ submap = &dma->submap[dma->num_submap];
+
+ /*
+ * The RC is trusted with endpoint-local DMA. Map the aligned span
+ * required by the EPC, but advertise only the resource itself.
+ */
+ base = ALIGN_DOWN(target_addr, align);
+ delta = target_addr - base;
+ if (check_add_overflow(delta, resource->size, &size))
+ return -EOVERFLOW;
+ map_size = ALIGN(size, align);
+ if (map_size < size || map_size > U32_MAX - *next)
+ return -EOVERFLOW;
+
+ submap->phys_addr = base;
+ submap->size = map_size;
+ region->bar = barno;
+ region->offset = *next + delta;
+ *next += map_size;
+ dma->num_submap++;
+
+ return 0;
+}
+
+/* DW eDMA */
+
+static int epf_ntb_dw_edma_claim(struct device *dev, int chan_id,
+ struct dma_chan **dchan)
+{
+ struct epf_ntb_dma_filter filter = {
+ .dev = dev,
+ .chan_id = chan_id,
+ };
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ *dchan = dma_request_channel(mask, epf_ntb_dma_filter, &filter);
+
+ return *dchan ? 0 : -EBUSY;
+}
+
+static void epf_ntb_dw_edma_release_channels(struct epf_ntb_dma *dma)
+{
+ unsigned int i;
+
+ for (i = 0; i < dma->rd_ch_cnt; i++) {
+ if (!dma->dchan[i])
+ continue;
+
+ dma_release_channel(dma->dchan[i]);
+ }
+}
+
+static const struct pci_epc_aux_resource *
+epf_ntb_dw_edma_find_desc(const struct pci_epc_aux_resource *resources,
+ unsigned int count, u16 chan_id)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ if (resources[i].type == PCI_EPC_AUX_DMA_DESC_MEM &&
+ resources[i].u.dma_desc.chan_id == chan_id)
+ return &resources[i];
+
+ return NULL;
+}
+
+static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
+ struct epf_ntb_dma *dma,
+ const struct pci_epc_aux_resource *ctrl,
+ const struct pci_epc_aux_resource *resources,
+ unsigned int count)
+{
+ struct dw_edma_chan_config chan_config = {
+ .flags = DW_EDMA_CH_CONFIG_IRQ_MODE,
+ .irq_mode = DW_EDMA_CH_IRQ_REMOTE,
+ };
+ struct dma_slave_config config = {
+ .peripheral_config = &chan_config,
+ .peripheral_size = sizeof(chan_config),
+ };
+ enum pci_barno barno = ntb->epf_ntb_bar[BAR_DMA];
+ const struct pci_epc_features *features;
+ struct device *dma_dev;
+ unsigned int i;
+ size_t align;
+ u32 next = 0;
+ int ret;
+
+ if (ctrl->u.dma_ctrl.reg_layout_data != EDMA_MF_EDMA_UNROLL)
+ return -EOPNOTSUPP;
+
+ dma->rd_ch_cnt = ctrl->u.dma_ctrl.rc_to_ep_ch_cnt;
+
+ features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no,
+ ntb->epf->vfunc_no);
+ /* DWC descriptor memory always needs subrange mappings. */
+ if (!features || !features->subrange_mapping ||
+ !features->dynamic_inbound_mapping)
+ return -EOPNOTSUPP;
+
+ align = features->align ?: 1;
+ if (!is_power_of_2(align))
+ return -EINVAL;
+
+ if (epf_ntb_is_bar_used(ntb, barno) ||
+ pci_epc_get_next_free_bar(features, barno) != barno)
+ return -EINVAL;
+
+ dma->ctrl.magic = EPF_NTB_DMA_MAGIC;
+ dma->ctrl.revision = EPF_NTB_DMA_REVISION;
+ dma->ctrl.type = EPF_NTB_DMA_TYPE_DW_EDMA;
+ dma->ctrl.submap.bar = barno;
+ dma->ctrl.length = offsetof(struct epf_ntb_dma_ctrl,
+ chan[dma->rd_ch_cnt]);
+
+ if (ctrl->bar == NO_BAR)
+ dma->reg_submap = &dma->submap[dma->num_submap];
+ ret = epf_ntb_dma_add_region(dma, ctrl, ctrl->phys_addr,
+ barno, align, &next, &dma->ctrl.reg);
+ if (ret)
+ return ret;
+
+ /* DW eDMA static IDs place read channels after all write channels. */
+ for (i = 0; i < dma->rd_ch_cnt; i++) {
+ u16 chan_id = ctrl->u.dma_ctrl.ep_to_rc_ch_cnt + i;
+ struct epf_ntb_dma_chan_ctrl *chan = &dma->ctrl.chan[i];
+ const struct pci_epc_aux_resource *desc;
+ dma_addr_t dma_addr;
+
+ desc = epf_ntb_dw_edma_find_desc(resources, count, chan_id);
+ if (!desc)
+ return -EINVAL;
+ dma_addr = desc->u.dma_desc.dma_addr;
+
+ ret = epf_ntb_dma_add_region(dma, desc, dma_addr, barno,
+ align, &next, &chan->desc);
+ if (ret)
+ return ret;
+ chan->desc_addr_lo = lower_32_bits(dma_addr);
+ chan->desc_addr_hi = upper_32_bits(dma_addr);
+ }
+ dma->ctrl.submap.size = next;
+
+ dma_dev = ntb->epf->epc->dev.parent;
+ for (i = 0; i < dma->rd_ch_cnt; i++) {
+ u16 chan_id = ctrl->u.dma_ctrl.ep_to_rc_ch_cnt + i;
+
+ ret = epf_ntb_dw_edma_claim(dma_dev, chan_id, &dma->dchan[i]);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < dma->rd_ch_cnt; i++) {
+ ret = dmaengine_slave_config(dma->dchan[i], &config);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Common endpoint DMA */
+
+static void epf_ntb_dma_release_channels(struct epf_ntb_dma *dma)
+{
+ switch (dma->ctrl.type) {
+ case EPF_NTB_DMA_TYPE_DW_EDMA:
+ epf_ntb_dw_edma_release_channels(dma);
+ break;
+ }
+}
+
+static int epf_ntb_dma_collect(struct epf_ntb *ntb)
+{
+ const struct pci_epc_aux_resource *ctrl = NULL;
+ struct device *dma_dev;
+ dma_addr_t dma_addr;
+ unsigned int i;
+ int count, ret;
+
+ if (ntb->epf_ntb_bar[BAR_DMA] == NO_BAR)
+ return 0;
+
+ count = pci_epc_get_aux_resources_count(ntb->epf->epc,
+ ntb->epf->func_no,
+ ntb->epf->vfunc_no);
+ if (count <= 0)
+ return count ?: -ENODEV;
+
+ struct pci_epc_aux_resource *resources __free(kfree) =
+ kcalloc(count, sizeof(*resources), GFP_KERNEL);
+ if (!resources)
+ return -ENOMEM;
+
+ ret = pci_epc_get_aux_resources(ntb->epf->epc, ntb->epf->func_no,
+ ntb->epf->vfunc_no, resources, count);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < count; i++) {
+ if (resources[i].type != PCI_EPC_AUX_DMA_CTRL_MMIO)
+ continue;
+ if (ctrl)
+ return -EINVAL;
+ ctrl = &resources[i];
+ }
+ if (!ctrl)
+ return -ENODEV;
+
+ struct epf_ntb_dma *dma __free(kfree) =
+ kzalloc(sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+
+ switch (ctrl->u.dma_ctrl.reg_layout) {
+ case PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA:
+ ret = epf_ntb_dw_edma_collect(ntb, dma, ctrl, resources, count);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ if (ret) {
+ epf_ntb_dma_release_channels(dma);
+ return ret;
+ }
+
+ /*
+ * CTRL_MMIO carries a CPU physical address, while DMA_DESC_MEM already
+ * carries an endpoint DMA address. Convert only the former.
+ */
+ if (dma->reg_submap) {
+ dma_dev = ntb->epf->epc->dev.parent;
+ dma_addr = dma_map_resource(dma_dev, dma->reg_submap->phys_addr,
+ dma->reg_submap->size,
+ DMA_BIDIRECTIONAL, 0);
+ if (dma_mapping_error(dma_dev, dma_addr)) {
+ epf_ntb_dma_release_channels(dma);
+ return -EIO;
+ }
+ dma->reg_submap->phys_addr = dma_addr;
+ }
+
+ ntb->dma = no_free_ptr(dma);
+
+ return 0;
+}
+
+static void epf_ntb_dma_release(struct epf_ntb *ntb)
+{
+ struct epf_ntb_dma *dma = ntb->dma;
+ struct device *dev;
+
+ if (!dma)
+ return;
+
+ epf_ntb_dma_release_channels(dma);
+ dev = ntb->epf->epc->dev.parent;
+ if (dma->reg_submap)
+ dma_unmap_resource(dev, dma->reg_submap->phys_addr,
+ dma->reg_submap->size, DMA_BIDIRECTIONAL, 0);
+ if (dma->bar_scratch)
+ dma_free_coherent(dev, dma->bar_scratch_size,
+ dma->bar_scratch, dma->bar_scratch_phys);
+ kfree(dma);
+ ntb->dma = NULL;
+}
+
+static int epf_ntb_dma_bar_init(struct epf_ntb *ntb)
+{
+ struct device *dev = ntb->epf->epc->dev.parent;
+ const struct pci_epc_features *features;
+ struct epf_ntb_dma *dma = ntb->dma;
+ struct pci_epf_bar *bar;
+ enum pci_barno barno;
+ size_t backing_size;
+ u32 mapped_size;
+ int ret;
+
+ features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no,
+ ntb->epf->vfunc_no);
+ if (!features)
+ return -EOPNOTSUPP;
+
+ barno = ntb->epf_ntb_bar[BAR_DMA];
+ mapped_size = dma->ctrl.submap.size;
+ /*
+ * Submaps cannot be installed until the host assigns the BAR address.
+ * Use address 0 for the temporary BAR Match Mode mapping, as is done
+ * for regular vNTB MW BARs.
+ */
+ ret = pci_epf_assign_bar_space(ntb->epf, mapped_size, barno, features,
+ PRIMARY_INTERFACE, 0);
+ if (ret)
+ return ret;
+
+ bar = &ntb->epf->bar[barno];
+ if (bar->size > U32_MAX)
+ return -EOVERFLOW;
+
+ backing_size = bar->size - mapped_size;
+ if (backing_size) {
+ /* Back the BAR tail added by the power-of-two size rounding. */
+ dma->bar_scratch = dma_alloc_coherent(dev, backing_size,
+ &dma->bar_scratch_phys,
+ GFP_KERNEL);
+ if (!dma->bar_scratch)
+ return -ENOMEM;
+ dma->bar_scratch_size = backing_size;
+ if (!IS_ALIGNED(dma->bar_scratch_phys, features->align ?: 1))
+ return -EINVAL;
+
+ dma->submap[dma->num_submap++] = (struct pci_epf_bar_submap) {
+ .phys_addr = dma->bar_scratch_phys,
+ .size = backing_size,
+ };
+ }
+
+ return pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
+ ntb->epf->vfunc_no, bar);
+}
+
+static void epf_ntb_dma_bar_clear(struct epf_ntb *ntb)
+{
+ struct epf_ntb_dma *dma = ntb->dma;
+ struct pci_epf_bar *bar;
+ enum pci_barno barno;
+
+ if (!dma)
+ return;
+
+ barno = ntb->epf_ntb_bar[BAR_DMA];
+ bar = &ntb->epf->bar[barno];
+ if (!bar->size)
+ return;
+
+ pci_epc_clear_bar(ntb->epf->epc, ntb->epf->func_no,
+ ntb->epf->vfunc_no, bar);
+ memset(bar, 0, sizeof(*bar));
+}
+
+static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
+{
+ struct pci_epf_bar_submap *old_submap;
+ struct epf_ntb_dma *dma = ntb->dma;
+ unsigned int old_num_submap;
+ struct pci_epf_bar *bar;
+ int restore, ret;
+
+ if (!dma)
+ return 0;
+
+ bar = &ntb->epf->bar[ntb->epf_ntb_bar[BAR_DMA]];
+
+ old_submap = bar->submap;
+ old_num_submap = bar->num_submap;
+ bar->submap = dma->submap;
+ bar->num_submap = dma->num_submap;
+
+ ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
+ ntb->epf->vfunc_no, bar);
+ if (!ret)
+ return 0;
+
+ /* A failed dynamic update may have already removed the old mapping. */
+ bar->submap = old_submap;
+ bar->num_submap = old_num_submap;
+ restore = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
+ ntb->epf->vfunc_no, bar);
+ if (restore)
+ dev_warn(&ntb->epf->dev,
+ "failed to restore DMA BAR mapping: %d\n", restore);
+
+ return ret;
+}
+
/**
* epf_ntb_configure_mw() - Configure the Outbound Address Space for VHOST
* to access the memory window of HOST
@@ -342,6 +774,11 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
epf_ntb_teardown_mw(ntb, argument);
ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
break;
+ case EPF_NTB_COMMAND_SETUP_DMA_BAR:
+ ret = epf_ntb_dma_set_bar(ntb);
+ ctrl->command_status = ret ? EPF_NTB_COMMAND_STATUS_ERROR :
+ EPF_NTB_COMMAND_STATUS_OK;
+ break;
case EPF_NTB_COMMAND_LINK_UP:
ret = epf_ntb_set_link_enabled(ntb, true);
if (ret < 0)
@@ -488,16 +925,17 @@ static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
* Virtual PCI PCIe Endpoint
* NTB Driver NTB Driver
*
- * Allocate the Local Memory mentioned in the above diagram. The size of
- * CONFIG REGION is sizeof(struct epf_ntb_ctrl), plus space for the MSI-X
- * Table and PBA if supported. The size of SCRATCHPAD REGION is obtained
- * from "spad-count" configfs entry.
+ * Allocate the Local Memory mentioned in the above diagram. CONFIG REGION
+ * contains struct epf_ntb_ctrl, the DMA extension if enabled, and the MSI-X
+ * Table and PBA if supported, in that order. The size of SCRATCHPAD REGION
+ * is obtained from "spad-count" configfs entry.
*
* Returns: Zero for success, or an error code in case of failure
*/
static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
{
enum pci_barno barno;
+ struct epf_ntb_dma_ctrl *dma_ctrl;
struct epf_ntb_ctrl *ctrl;
u32 spad_size, ctrl_size;
struct pci_epf *epf = ntb->epf;
@@ -511,7 +949,10 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
barno = ntb->epf_ntb_bar[BAR_CONFIG];
spad_count = ntb->spad_count;
- ctrl_size = ALIGN(sizeof(struct epf_ntb_ctrl), sizeof(u32));
+ ctrl_size = sizeof(struct epf_ntb_ctrl);
+ if (ntb->dma)
+ ctrl_size += sizeof(struct epf_ntb_dma_ctrl);
+ ctrl_size = ALIGN(ctrl_size, sizeof(u32));
if (epc_features->msix_capable) {
ntb->msix_table_offset = ALIGN(ctrl_size, 8);
ctrl_size = ntb->msix_table_offset +
@@ -543,6 +984,11 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
ntb->reg->db_offset[i] = 0;
}
+ if (ntb->dma) {
+ dma_ctrl = base + EPF_NTB_DMA_CTRL_OFFSET;
+ *dma_ctrl = ntb->dma->ctrl;
+ }
+
return 0;
}
@@ -924,7 +1370,8 @@ static int epf_ntb_find_bar(struct epf_ntb *ntb,
* Verify if the BAR found is not already assigned
* through the provided configuration
*/
- if (!epf_ntb_is_bar_used(ntb, barno))
+ if (ntb->epf_ntb_bar[BAR_DMA] != barno &&
+ !epf_ntb_is_bar_used(ntb, barno))
ntb->epf_ntb_bar[bar] = barno;
barno += 1;
@@ -1023,11 +1470,19 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
goto err_mw_bar_init;
}
+ if (ntb->dma) {
+ ret = epf_ntb_dma_bar_init(ntb);
+ if (ret) {
+ dev_err(dev, "DMA BAR init failed\n");
+ goto err_dma_bar_init;
+ }
+ }
+
if (vfunc_no <= 1) {
ret = pci_epc_write_header(epc, func_no, vfunc_no, epf->header);
if (ret) {
dev_err(dev, "Configuration header write failed\n");
- goto err_write_header;
+ goto err_dma_bar_init;
}
}
@@ -1038,7 +1493,8 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
return 0;
-err_write_header:
+err_dma_bar_init:
+ epf_ntb_dma_bar_clear(ntb);
epf_ntb_mw_bar_clear(ntb, ntb->num_mws);
err_mw_bar_init:
epf_ntb_db_bar_clear(ntb);
@@ -1061,6 +1517,7 @@ static void epf_ntb_epc_cleanup(struct epf_ntb *ntb)
disable_delayed_work_sync(&ntb->cmd_handler);
disable_work_sync(&ntb->peer_db_work);
atomic64_set(&ntb->peer_db_pending, 0);
+ epf_ntb_dma_bar_clear(ntb);
epf_ntb_mw_bar_clear(ntb, ntb->num_mws);
epf_ntb_db_bar_clear(ntb);
epf_ntb_config_sspad_bar_clear(ntb);
@@ -1267,10 +1724,13 @@ EPF_NTB_BAR_R(mw3_bar, BAR_MW3)
EPF_NTB_BAR_W(mw3_bar, BAR_MW3)
EPF_NTB_BAR_R(mw4_bar, BAR_MW4)
EPF_NTB_BAR_W(mw4_bar, BAR_MW4)
+EPF_NTB_BAR_R(dma_bar, BAR_DMA)
+EPF_NTB_BAR_W(dma_bar, BAR_DMA)
CONFIGFS_ATTR(epf_ntb_, spad_count);
CONFIGFS_ATTR(epf_ntb_, db_count);
CONFIGFS_ATTR(epf_ntb_, num_mws);
+CONFIGFS_ATTR(epf_ntb_, dma_bar);
CONFIGFS_ATTR(epf_ntb_, mw1);
CONFIGFS_ATTR(epf_ntb_, mw2);
CONFIGFS_ATTR(epf_ntb_, mw3);
@@ -1289,6 +1749,7 @@ static struct configfs_attribute *epf_ntb_attrs[] = {
&epf_ntb_attr_spad_count,
&epf_ntb_attr_db_count,
&epf_ntb_attr_num_mws,
+ &epf_ntb_attr_dma_bar,
&epf_ntb_attr_mw1,
&epf_ntb_attr_mw2,
&epf_ntb_attr_mw3,
@@ -1847,6 +2308,12 @@ static int epf_ntb_bind(struct pci_epf *epf)
return ret;
}
+ ret = epf_ntb_dma_collect(ntb);
+ if (ret) {
+ dev_err(dev, "Failed to prepare NTB DMA export\n");
+ return ret;
+ }
+
ret = epf_ntb_config_spad_bar_alloc(ntb);
if (ret) {
dev_err(dev, "Failed to allocate BAR memory\n");
@@ -1887,6 +2354,7 @@ static int epf_ntb_bind(struct pci_epf *epf)
epf_ntb_epc_cleanup(ntb);
err_bar_alloc:
epf_ntb_config_spad_bar_free(ntb);
+ epf_ntb_dma_release(ntb);
return ret;
}
@@ -1918,6 +2386,7 @@ static void epf_ntb_unbind(struct pci_epf *epf)
epf_ntb_epc_cleanup(ntb);
epf_ntb_config_spad_bar_free(ntb);
+ epf_ntb_dma_release(ntb);
}
// EPF driver probe
diff --git a/include/linux/pci-epf-ntb.h b/include/linux/pci-epf-ntb.h
index 0c02d709aa08..5cc0f0badae0 100644
--- a/include/linux/pci-epf-ntb.h
+++ b/include/linux/pci-epf-ntb.h
@@ -8,6 +8,8 @@
#define __LINUX_PCI_EPF_NTB_H
#include <linux/bits.h>
+#include <linux/dma/edma.h>
+#include <linux/stddef.h>
#include <linux/types.h>
#define EPF_NTB_COMMAND_CONFIGURE_DOORBELL 1
@@ -16,6 +18,7 @@
#define EPF_NTB_COMMAND_TEARDOWN_MW 4
#define EPF_NTB_COMMAND_LINK_UP 5
#define EPF_NTB_COMMAND_LINK_DOWN 6
+#define EPF_NTB_COMMAND_SETUP_DMA_BAR 7
/* EPF_NTB_COMMAND_CONFIGURE_DOORBELL argument */
#define EPF_NTB_DB_COUNT_MASK GENMASK(15, 0)
@@ -45,4 +48,40 @@ struct epf_ntb_ctrl {
u32 db_offset[EPF_NTB_MAX_DB_COUNT];
} __packed;
+/*
+ * DMA wire extension produced by pci-epf-vntb and consumed by
+ * ntb_hw_epf. It immediately follows struct epf_ntb_ctrl.
+ */
+#define EPF_NTB_DMA_CTRL_OFFSET sizeof(struct epf_ntb_ctrl)
+
+#define EPF_NTB_DMA_MAGIC 0x414d444e /* "NDMA": NTB DMA */
+#define EPF_NTB_DMA_REVISION 1
+#define EPF_NTB_DMA_TYPE_DW_EDMA 1
+
+struct epf_ntb_dma_region_ctrl {
+ u32 bar;
+ u32 offset;
+ u32 size;
+} __packed;
+
+struct epf_ntb_dma_chan_ctrl {
+ struct epf_ntb_dma_region_ctrl desc;
+ u32 desc_addr_lo;
+ u32 desc_addr_hi;
+} __packed;
+
+struct epf_ntb_dma_ctrl {
+ u32 magic;
+ u16 revision;
+ u16 length;
+ u32 type;
+ /* BAR range occupied by resources without a fixed BAR assignment. */
+ struct epf_ntb_dma_region_ctrl submap;
+ struct epf_ntb_dma_region_ctrl reg;
+ struct epf_ntb_dma_chan_ctrl chan[EDMA_MAX_RD_CH];
+} __packed;
+
+/* Common header needed even when endpoint DMA is unavailable. */
+#define EPF_NTB_DMA_CTRL_COMMON_SIZE offsetof(struct epf_ntb_dma_ctrl, reg)
+
#endif /* __LINUX_PCI_EPF_NTB_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v6 6/8] NTB: ntb_hw_epf: Discover vNTB-embedded DMA
2026-10-20 5:49 [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
` (4 preceding siblings ...)
2026-10-20 5:49 ` [PATCH v6 5/8] PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels Koichiro Den
@ 2026-10-20 5:49 ` Koichiro Den
2026-10-20 17:03 ` Frank Li
2026-10-20 5:49 ` [PATCH v6 7/8] PCI/NTB: endpoint: Allow DMA and MW to share a BAR Koichiro Den
2026-10-20 5:49 ` [PATCH v6 8/8] Documentation: PCI: endpoint: Document vNTB DMA export Koichiro Den
7 siblings, 1 reply; 13+ messages in thread
From: Koichiro Den @ 2026-10-20 5:49 UTC (permalink / raw)
To: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel
Cc: Bjorn Helgaas, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Jingoo Han, Lorenzo Pieralisi, Rob Herring, Jerome Brunet,
linux-pci, linux-doc, linux-kernel, ntb
A vNTB PCI function can now expose endpoint-local DMA channels to its
host. Decode the extension and register the advertised channels before
the NTB device, so clients of that NTB device can use the DMA
functionality in the same PCI function. Endpoints without the extension,
or hosts without support for its DMA type or with no DMA IRQ available,
continue without DMA.
Only DW eDMA is supported for now. Dispatch on the advertised DMA type
and keep type-specific setup in local helpers. Support for another
implementation can add a new case and its own helpers.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes since v5:
- Derive the control region and DMA extension offsets from
include/linux/pci-epf-ntb.h instead of hard-coding them. (Frank)
- Move shared-MW boundary handling to the BAR-sharing patch.
- Link to v5: https://lore.kernel.org/r/20261009045949.1109871-7-den@valinux.co.jp/
drivers/ntb/hw/epf/ntb_hw_epf.c | 273 +++++++++++++++++++++++++++++++-
1 file changed, 267 insertions(+), 6 deletions(-)
diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c
index ff92c2a61ce7..e0016128b95c 100644
--- a/drivers/ntb/hw/epf/ntb_hw_epf.c
+++ b/drivers/ntb/hw/epf/ntb_hw_epf.c
@@ -8,6 +8,7 @@
#include <linux/atomic.h>
#include <linux/delay.h>
+#include <linux/dma/edma.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -18,6 +19,12 @@
/* Offsets into the control region, see include/linux/pci-epf-ntb.h. */
#define NTB_EPF_CTRL(field) offsetof(struct epf_ntb_ctrl, field)
+/* Private DMA wire extension produced by pci-epf-vntb. */
+#define NTB_EPF_DMA(field) (EPF_NTB_DMA_CTRL_OFFSET + \
+ offsetof(struct epf_ntb_dma_ctrl, field))
+#define NTB_EPF_DMA_REGION(field) \
+ offsetof(struct epf_ntb_dma_region_ctrl, field)
+
/*
* Legacy doorbell slot layout when paired with pci-epf-*ntb:
*
@@ -71,6 +78,24 @@ struct ntb_epf_irq_ctx {
unsigned int irq_no;
};
+struct ntb_epf_dma_region {
+ unsigned int bar;
+ resource_size_t offset;
+ resource_size_t size;
+};
+
+struct ntb_epf_dw_edma {
+ struct dw_edma_chip chip;
+ struct ntb_epf_dma_region reg;
+ struct ntb_epf_dma_region ll[EDMA_MAX_RD_CH];
+};
+
+struct ntb_epf_dma {
+ struct ntb_epf_dma_region submap;
+ unsigned int nr_irqs;
+ u32 type;
+};
+
struct ntb_epf_dev {
struct ntb_dev ntb;
struct device *dev;
@@ -82,6 +107,8 @@ struct ntb_epf_dev {
unsigned int mw_count;
unsigned int spad_count;
unsigned int db_count;
+ struct ntb_epf_dma dma;
+ struct ntb_epf_dw_edma dw_edma;
void __iomem *ctrl_reg;
void __iomem *db_reg;
@@ -136,6 +163,213 @@ static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command,
return ret;
}
+static bool ntb_epf_dma_region_parse(struct ntb_epf_dev *ndev, u32 base,
+ bool optional,
+ struct ntb_epf_dma_region *region)
+{
+ void __iomem *reg = ndev->ctrl_reg + base;
+ resource_size_t bar_len;
+ u32 bar, offset, size;
+
+ bar = readl(reg + NTB_EPF_DMA_REGION(bar));
+ offset = readl(reg + NTB_EPF_DMA_REGION(offset));
+ size = readl(reg + NTB_EPF_DMA_REGION(size));
+ region->bar = bar;
+ region->offset = offset;
+ region->size = size;
+ if (!size)
+ return optional && bar == U32_MAX && !offset;
+ if (bar > BAR_5)
+ return false;
+
+ bar_len = pci_resource_len(ndev->ntb.pdev, bar);
+ if (offset > bar_len || size > bar_len - offset)
+ return false;
+
+ return true;
+}
+
+/* DW eDMA */
+
+static int ntb_epf_dw_edma_parse(struct ntb_epf_dev *ndev, u32 length)
+{
+ struct ntb_epf_dw_edma *edma = &ndev->dw_edma;
+ struct dw_edma_chip *chip = &edma->chip;
+ u32 count, chan_offset, chan_size;
+ unsigned int i;
+
+ chan_offset = offsetof(struct epf_ntb_dma_ctrl, chan);
+ chan_size = sizeof(struct epf_ntb_dma_chan_ctrl);
+ if (length < chan_offset + chan_size ||
+ (length - chan_offset) % chan_size)
+ return -EINVAL;
+
+ count = (length - chan_offset) / chan_size;
+ if (count > EDMA_MAX_RD_CH)
+ return -EINVAL;
+
+ if (!ntb_epf_dma_region_parse(ndev, NTB_EPF_DMA(reg), false,
+ &edma->reg) ||
+ !IS_ALIGNED(edma->reg.offset, sizeof(u32)))
+ return -EINVAL;
+
+ chip->mf = EDMA_MF_EDMA_UNROLL;
+ chip->ll_rd_cnt = count;
+ chip->func_no = PCI_FUNC(ndev->ntb.pdev->devfn);
+
+ for (i = 0; i < count; i++) {
+ u64 paddr;
+
+ if (!ntb_epf_dma_region_parse(ndev, NTB_EPF_DMA(chan[i].desc),
+ false, &edma->ll[i]) ||
+ !IS_ALIGNED(edma->ll[i].offset, sizeof(u32)))
+ return -EINVAL;
+
+ paddr = readl(ndev->ctrl_reg +
+ NTB_EPF_DMA(chan[i].desc_addr_lo));
+ paddr |= (u64)readl(ndev->ctrl_reg +
+ NTB_EPF_DMA(chan[i].desc_addr_hi)) << 32;
+ if (paddr == U64_MAX)
+ return -EINVAL;
+
+ chip->ll_region_rd[i].sz = edma->ll[i].size;
+ chip->ll_region_rd[i].paddr = paddr;
+ }
+ ndev->dma.nr_irqs = count;
+
+ return 0;
+}
+
+static int ntb_epf_dw_edma_irq_vector(struct device *dev, unsigned int nr)
+{
+ struct ntb_epf_dev *ndev = dev_get_drvdata(dev);
+
+ /* DMA vectors follow the NTB link and doorbell vector block. */
+ return pci_irq_vector(ndev->ntb.pdev, ndev->db_count + 1 + nr);
+}
+
+static const struct dw_edma_plat_ops ntb_epf_dw_edma_ops = {
+ .irq_vector = ntb_epf_dw_edma_irq_vector,
+};
+
+static void __iomem *
+ntb_epf_dw_edma_map_region(struct pci_dev *pdev,
+ const struct ntb_epf_dma_region *region)
+{
+ return pcim_iomap_range(pdev, region->bar, region->offset, region->size);
+}
+
+static int ntb_epf_dw_edma_init(struct ntb_epf_dev *ndev)
+{
+ struct ntb_epf_dw_edma *edma = &ndev->dw_edma;
+ struct dw_edma_chip *chip = &edma->chip;
+ struct pci_dev *pdev = ndev->ntb.pdev;
+ unsigned int i;
+ int ret;
+
+ /* Install BAR submaps before mapping the advertised DMA regions. */
+ ret = ntb_epf_send_command(ndev, EPF_NTB_COMMAND_SETUP_DMA_BAR, 0);
+ if (ret)
+ return ret;
+
+ chip->reg_base = ntb_epf_dw_edma_map_region(pdev, &edma->reg);
+ if (IS_ERR(chip->reg_base))
+ return PTR_ERR(chip->reg_base);
+ for (i = 0; i < chip->ll_rd_cnt; i++) {
+ chip->ll_region_rd[i].vaddr.io =
+ ntb_epf_dw_edma_map_region(pdev, &edma->ll[i]);
+ if (IS_ERR(chip->ll_region_rd[i].vaddr.io))
+ return PTR_ERR(chip->ll_region_rd[i].vaddr.io);
+ }
+
+ chip->dev = ndev->dev;
+ chip->nr_irqs = ndev->dma.nr_irqs;
+ chip->ops = &ntb_epf_dw_edma_ops;
+ chip->flags = DW_EDMA_CHIP_PARTIAL;
+
+ return dw_edma_probe(chip);
+}
+
+static void ntb_epf_dw_edma_deinit(struct ntb_epf_dev *ndev)
+{
+ dw_edma_remove(&ndev->dw_edma.chip);
+}
+
+/* Common endpoint DMA */
+
+static int ntb_epf_dma_parse(struct ntb_epf_dev *ndev)
+{
+ u32 magic, type, spad_off;
+ u16 revision, length;
+ int ret;
+
+ /*
+ * Legacy endpoints place scratchpads where this extension would begin.
+ * Once the extension is present, malformed metadata is fatal.
+ */
+ spad_off = readl(ndev->ctrl_reg + NTB_EPF_CTRL(spad_offset));
+ if (spad_off < EPF_NTB_DMA_CTRL_OFFSET + EPF_NTB_DMA_CTRL_COMMON_SIZE)
+ return 0;
+
+ magic = readl(ndev->ctrl_reg + NTB_EPF_DMA(magic));
+ if (!magic)
+ return 0;
+ if (magic == U32_MAX)
+ return -EIO;
+ if (magic != EPF_NTB_DMA_MAGIC)
+ return -EINVAL;
+
+ revision = readw(ndev->ctrl_reg + NTB_EPF_DMA(revision));
+ length = readw(ndev->ctrl_reg + NTB_EPF_DMA(length));
+ if (revision != EPF_NTB_DMA_REVISION ||
+ length < EPF_NTB_DMA_CTRL_COMMON_SIZE ||
+ spad_off < EPF_NTB_DMA_CTRL_OFFSET + length)
+ return -EINVAL;
+ if (!ntb_epf_dma_region_parse(ndev, NTB_EPF_DMA(submap), true,
+ &ndev->dma.submap))
+ return -EINVAL;
+
+ type = readl(ndev->ctrl_reg + NTB_EPF_DMA(type));
+ if (type == U32_MAX)
+ return -EIO;
+
+ switch (type) {
+ case EPF_NTB_DMA_TYPE_DW_EDMA:
+ if (!IS_REACHABLE(CONFIG_DW_EDMA))
+ break;
+ ret = ntb_epf_dw_edma_parse(ndev, length);
+ if (!ret)
+ ndev->dma.type = type;
+ return ret;
+ default:
+ break;
+ }
+
+ dev_warn(ndev->dev,
+ "Endpoint DMA type %u unavailable, continuing without DMA\n",
+ type);
+ return 0;
+}
+
+static int ntb_epf_dma_init(struct ntb_epf_dev *ndev)
+{
+ switch (ndev->dma.type) {
+ case EPF_NTB_DMA_TYPE_DW_EDMA:
+ return ntb_epf_dw_edma_init(ndev);
+ default:
+ return 0;
+ }
+}
+
+static void ntb_epf_dma_deinit(struct ntb_epf_dev *ndev)
+{
+ switch (ndev->dma.type) {
+ case EPF_NTB_DMA_TYPE_DW_EDMA:
+ ntb_epf_dw_edma_deinit(ndev);
+ break;
+ }
+}
+
static int ntb_epf_mw_to_bar(struct ntb_epf_dev *ndev, int idx)
{
struct device *dev = ndev->dev;
@@ -347,16 +581,20 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
{
struct pci_dev *pdev = ndev->ntb.pdev;
struct device *dev = ndev->dev;
+ unsigned int dma_max = ndev->dma.nr_irqs;
+ unsigned int dma_min;
u32 argument = EPF_NTB_MSIX_ENABLE;
+ unsigned int ntb_irqs;
int irq;
int ret;
int i;
- irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max, PCI_IRQ_MSIX);
+ irq = pci_alloc_irq_vectors(pdev, msi_min,
+ msi_max + dma_max, PCI_IRQ_MSIX);
if (irq < 0) {
dev_dbg(dev, "Failed to get MSIX interrupts\n");
- irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max,
- PCI_IRQ_MSI);
+ irq = pci_alloc_irq_vectors(pdev, msi_min,
+ msi_max + dma_max, PCI_IRQ_MSI);
if (irq < 0) {
dev_err(dev, "Failed to get MSI interrupts\n");
return irq;
@@ -364,8 +602,17 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
argument &= ~EPF_NTB_MSIX_ENABLE;
}
- ndev->db_count = irq - 1;
- for (i = 0; i < irq; i++) {
+ dma_min = dma_max && irq > msi_min;
+ /* Reserve one shared DMA IRQ when available, then give the rest to NTB. */
+ ntb_irqs = min_t(unsigned int, irq - dma_min, msi_max);
+ ndev->dma.nr_irqs = irq - ntb_irqs;
+ if (dma_max && !ndev->dma.nr_irqs) {
+ dev_warn(dev,
+ "No IRQ available for endpoint DMA, continuing without DMA\n");
+ ndev->dma.type = 0;
+ }
+ ndev->db_count = ntb_irqs - 1;
+ for (i = 0; i < ntb_irqs; i++) {
ndev->irq_ctx[i].ndev = ndev;
ndev->irq_ctx[i].irq_no = i;
ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr,
@@ -377,7 +624,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
}
ret = ntb_epf_send_command(ndev, EPF_NTB_COMMAND_CONFIGURE_DOORBELL,
- argument | irq);
+ argument | ntb_irqs);
if (ret) {
dev_err(dev, "Failed to configure doorbell\n");
goto err_free_irq;
@@ -610,6 +857,11 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count);
return -EINVAL;
}
+ ret = ntb_epf_dma_parse(ndev);
+ if (ret) {
+ dev_err(dev, "Invalid endpoint DMA layout\n");
+ return ret;
+ }
/* One Link interrupt and rest doorbell interrupt */
ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
@@ -760,6 +1012,12 @@ static int ntb_epf_pci_probe(struct pci_dev *pdev,
goto err_init_dev;
}
+ ret = ntb_epf_dma_init(ndev);
+ if (ret) {
+ dev_err(dev, "Failed to initialize endpoint DMA\n");
+ goto err_dma_init;
+ }
+
ret = ntb_register_device(&ndev->ntb);
if (ret) {
dev_err(dev, "Failed to register NTB device\n");
@@ -769,6 +1027,8 @@ static int ntb_epf_pci_probe(struct pci_dev *pdev,
return 0;
err_register_dev:
+ ntb_epf_dma_deinit(ndev);
+err_dma_init:
ntb_epf_cleanup_isr(ndev);
err_init_dev:
@@ -782,6 +1042,7 @@ static void ntb_epf_pci_remove(struct pci_dev *pdev)
struct ntb_epf_dev *ndev = pci_get_drvdata(pdev);
ntb_unregister_device(&ndev->ntb);
+ ntb_epf_dma_deinit(ndev);
ntb_epf_cleanup_isr(ndev);
ntb_epf_deinit_pci(ndev);
}
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v6 7/8] PCI/NTB: endpoint: Allow DMA and MW to share a BAR
2026-10-20 5:49 [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
` (5 preceding siblings ...)
2026-10-20 5:49 ` [PATCH v6 6/8] NTB: ntb_hw_epf: Discover vNTB-embedded DMA Koichiro Den
@ 2026-10-20 5:49 ` Koichiro Den
2026-10-20 17:11 ` Frank Li
2026-10-20 5:49 ` [PATCH v6 8/8] Documentation: PCI: endpoint: Document vNTB DMA export Koichiro Den
7 siblings, 1 reply; 13+ messages in thread
From: Koichiro Den @ 2026-10-20 5:49 UTC (permalink / raw)
To: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel
Cc: Bjorn Helgaas, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Jingoo Han, Lorenzo Pieralisi, Rob Herring, Jerome Brunet,
linux-pci, linux-doc, linux-kernel, ntb
Some endpoint configurations have no spare BAR for DMA resources. Allow
dma_bar to select an MW BAR and place the DMA ranges after that MW.
DMA submaps have fixed BAR offsets, so a shared MW translation must
cover its configured size. Report that size as the translation alignment
and require it to be a power of two.
Limit the host-visible MW size to exclude the DMA ranges. Older versions
of ntb_hw_epf treat the whole BAR as an MW.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes since v5:
- Include the host-side MW boundary handling and place this patch
after DMA discovery.
drivers/ntb/hw/epf/ntb_hw_epf.c | 43 ++++-
drivers/pci/endpoint/functions/pci-epf-vntb.c | 147 +++++++++++++-----
2 files changed, 148 insertions(+), 42 deletions(-)
diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c
index e0016128b95c..975ea424de6a 100644
--- a/drivers/ntb/hw/epf/ntb_hw_epf.c
+++ b/drivers/ntb/hw/epf/ntb_hw_epf.c
@@ -382,6 +382,23 @@ static int ntb_epf_mw_to_bar(struct ntb_epf_dev *ndev, int idx)
return ndev->barno_map[BAR_MW1 + idx];
}
+static resource_size_t ntb_epf_mw_offset(struct ntb_epf_dev *ndev, int idx)
+{
+ return !idx ? readl(ndev->ctrl_reg + NTB_EPF_CTRL(mw1_offset)) : 0;
+}
+
+static resource_size_t ntb_epf_mw_size(struct ntb_epf_dev *ndev, int bar)
+{
+ resource_size_t end;
+
+ end = pci_resource_len(ndev->ntb.pdev, bar);
+ /* The DMA submap offset marks the end of an MW sharing this BAR. */
+ if (ndev->dma.submap.size && ndev->dma.submap.bar == bar)
+ end = min(end, ndev->dma.submap.offset);
+
+ return end;
+}
+
static int ntb_epf_mw_count(struct ntb_dev *ntb, int pidx)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
@@ -420,7 +437,7 @@ static int ntb_epf_mw_get_align(struct ntb_dev *ntb, int pidx, int idx,
*size_align = 1;
if (size_max)
- *size_max = pci_resource_len(ndev->ntb.pdev, bar);
+ *size_max = ntb_epf_mw_size(ndev, bar);
return 0;
}
@@ -707,7 +724,7 @@ static int ntb_epf_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx,
if (bar < 0)
return bar;
- mw_size = pci_resource_len(ntb->pdev, bar);
+ mw_size = ntb_epf_mw_size(ndev, bar);
if (size > mw_size) {
dev_err(dev, "Size:%pa is greater than the MW size %pa\n",
@@ -739,21 +756,20 @@ static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx,
phys_addr_t *base, resource_size_t *size)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
- u32 offset = 0;
+ resource_size_t offset;
int bar;
- if (idx == 0)
- offset = readl(ndev->ctrl_reg + NTB_EPF_CTRL(mw1_offset));
-
bar = ntb_epf_mw_to_bar(ndev, idx);
if (bar < 0)
return bar;
+ offset = ntb_epf_mw_offset(ndev, idx);
+
if (base)
*base = pci_resource_start(ndev->ntb.pdev, bar) + offset;
if (size)
- *size = pci_resource_len(ndev->ntb.pdev, bar) - offset;
+ *size = ntb_epf_mw_size(ndev, bar) - offset;
return 0;
}
@@ -851,6 +867,7 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
{
struct device *dev = ndev->dev;
int ret;
+ int i;
ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_CTRL(num_mws));
if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) {
@@ -862,6 +879,18 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
dev_err(dev, "Invalid endpoint DMA layout\n");
return ret;
}
+ if (ndev->dma.submap.size) {
+ for (i = 0; i < ndev->mw_count; i++) {
+ int bar = ntb_epf_mw_to_bar(ndev, i);
+
+ if (bar == ndev->dma.submap.bar &&
+ ntb_epf_mw_size(ndev, bar) <=
+ ntb_epf_mw_offset(ndev, i)) {
+ dev_err(dev, "Invalid DMA/MW boundary\n");
+ return -EINVAL;
+ }
+ }
+ }
/* One Link interrupt and rest doorbell interrupt */
ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index ac83af54122f..89f828234ca7 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -84,12 +84,13 @@ enum epf_irq_slot {
#define MIN_DB_COUNT (EPF_IRQ_DB_START + 1)
struct epf_ntb_dma {
+ struct mutex lock; /* Serialize DMA submap and MW updates */
struct epf_ntb_dma_ctrl ctrl;
struct dma_chan *dchan[EDMA_MAX_RD_CH];
void *bar_scratch;
dma_addr_t bar_scratch_phys;
size_t bar_scratch_size;
- struct pci_epf_bar_submap submap[EDMA_MAX_RD_CH + 2];
+ struct pci_epf_bar_submap submap[EDMA_MAX_RD_CH + 3];
struct pci_epf_bar_submap *reg_submap;
unsigned int num_submap;
u16 rd_ch_cnt;
@@ -229,6 +230,22 @@ static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
return false;
}
+static u64 epf_ntb_dma_bar_offset(struct epf_ntb *ntb, enum pci_barno barno)
+{
+ unsigned int i;
+
+ for (i = 0; i < ntb->num_mws; i++)
+ if (ntb->epf_ntb_bar[BAR_MW1 + i] == barno)
+ return ntb->mws_size[i];
+
+ return 0;
+}
+
+static bool epf_ntb_dma_shares_bar(struct epf_ntb *ntb, enum pci_barno barno)
+{
+ return ntb->dma && ntb->epf_ntb_bar[BAR_DMA] == barno;
+}
+
struct epf_ntb_dma_filter {
struct device *dev;
int chan_id;
@@ -353,6 +370,7 @@ static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
unsigned int i;
size_t align;
u32 next = 0;
+ u64 offset;
int ret;
if (ctrl->u.dma_ctrl.reg_layout_data != EDMA_MF_EDMA_UNROLL)
@@ -371,10 +389,31 @@ static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
if (!is_power_of_2(align))
return -EINVAL;
- if (epf_ntb_is_bar_used(ntb, barno) ||
- pci_epc_get_next_free_bar(features, barno) != barno)
+ if (barno == ntb->epf_ntb_bar[BAR_CONFIG] ||
+ barno == ntb->epf_ntb_bar[BAR_DB])
return -EINVAL;
+ offset = epf_ntb_dma_bar_offset(ntb, barno);
+ if (!offset &&
+ (epf_ntb_is_bar_used(ntb, barno) ||
+ pci_epc_get_next_free_bar(features, barno) != barno))
+ return -EINVAL;
+ if (offset > U32_MAX)
+ return -EOVERFLOW;
+ if (offset && !is_power_of_2(offset))
+ return -EINVAL;
+ dma->ctrl.submap.offset = offset;
+ next = offset;
+ if (next) {
+ /*
+ * submap[0] covers the MW prefix. Keep its temporary
+ * target at address 0 until the MW translation is
+ * installed.
+ */
+ dma->submap[0].size = next;
+ dma->num_submap = 1;
+ }
+
dma->ctrl.magic = EPF_NTB_DMA_MAGIC;
dma->ctrl.revision = EPF_NTB_DMA_REVISION;
dma->ctrl.type = EPF_NTB_DMA_TYPE_DW_EDMA;
@@ -408,7 +447,7 @@ static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
chan->desc_addr_lo = lower_32_bits(dma_addr);
chan->desc_addr_hi = upper_32_bits(dma_addr);
}
- dma->ctrl.submap.size = next;
+ dma->ctrl.submap.size = next - dma->ctrl.submap.offset;
dma_dev = ntb->epf->epc->dev.parent;
for (i = 0; i < dma->rd_ch_cnt; i++) {
@@ -481,6 +520,8 @@ static int epf_ntb_dma_collect(struct epf_ntb *ntb)
if (!dma)
return -ENOMEM;
+ mutex_init(&dma->lock);
+
switch (ctrl->u.dma_ctrl.reg_layout) {
case PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA:
ret = epf_ntb_dw_edma_collect(ntb, dma, ctrl, resources, count);
@@ -551,7 +592,7 @@ static int epf_ntb_dma_bar_init(struct epf_ntb *ntb)
return -EOPNOTSUPP;
barno = ntb->epf_ntb_bar[BAR_DMA];
- mapped_size = dma->ctrl.submap.size;
+ mapped_size = dma->ctrl.submap.offset + dma->ctrl.submap.size;
/*
* Submaps cannot be installed until the host assigns the BAR address.
* Use address 0 for the temporary BAR Match Mode mapping, as is done
@@ -607,21 +648,25 @@ static void epf_ntb_dma_bar_clear(struct epf_ntb *ntb)
memset(bar, 0, sizeof(*bar));
}
-static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
+static int epf_ntb_dma_set_bar_locked(struct epf_ntb *ntb,
+ const dma_addr_t *mw_addr)
{
struct pci_epf_bar_submap *old_submap;
struct epf_ntb_dma *dma = ntb->dma;
unsigned int old_num_submap;
struct pci_epf_bar *bar;
+ dma_addr_t old_mw_addr;
int restore, ret;
-
- if (!dma)
- return 0;
+ lockdep_assert_held(&dma->lock);
bar = &ntb->epf->bar[ntb->epf_ntb_bar[BAR_DMA]];
old_submap = bar->submap;
old_num_submap = bar->num_submap;
+ if (mw_addr) {
+ old_mw_addr = dma->submap[0].phys_addr;
+ dma->submap[0].phys_addr = *mw_addr;
+ }
bar->submap = dma->submap;
bar->num_submap = dma->num_submap;
@@ -631,6 +676,8 @@ static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
return 0;
/* A failed dynamic update may have already removed the old mapping. */
+ if (mw_addr)
+ dma->submap[0].phys_addr = old_mw_addr;
bar->submap = old_submap;
bar->num_submap = old_num_submap;
restore = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
@@ -642,6 +689,18 @@ static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
return ret;
}
+static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
+{
+ struct epf_ntb_dma *dma = ntb->dma;
+
+ if (!dma)
+ return 0;
+
+ guard(mutex)(&dma->lock);
+
+ return epf_ntb_dma_set_bar_locked(ntb, NULL);
+}
+
/**
* epf_ntb_configure_mw() - Configure the Outbound Address Space for VHOST
* to access the memory window of HOST
@@ -1269,6 +1328,7 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb)
*/
static int epf_ntb_mw_bar_init(struct epf_ntb *ntb)
{
+ bool shared;
int ret = 0;
int i;
u64 size;
@@ -1278,22 +1338,25 @@ static int epf_ntb_mw_bar_init(struct epf_ntb *ntb)
for (i = 0; i < ntb->num_mws; i++) {
size = ntb->mws_size[i];
barno = ntb->epf_ntb_bar[BAR_MW1 + i];
-
- ntb->epf->bar[barno].barno = barno;
- ntb->epf->bar[barno].size = size;
- ntb->epf->bar[barno].addr = NULL;
- ntb->epf->bar[barno].phys_addr = 0;
- ntb->epf->bar[barno].flags |= upper_32_bits(size) ?
- PCI_BASE_ADDRESS_MEM_TYPE_64 :
- PCI_BASE_ADDRESS_MEM_TYPE_32;
-
- ret = pci_epc_set_bar(ntb->epf->epc,
- ntb->epf->func_no,
- ntb->epf->vfunc_no,
- &ntb->epf->bar[barno]);
- if (ret) {
- dev_err(dev, "MW set failed\n");
- goto err_alloc_mem;
+ shared = epf_ntb_dma_shares_bar(ntb, barno);
+
+ if (!shared) {
+ ntb->epf->bar[barno].barno = barno;
+ ntb->epf->bar[barno].size = size;
+ ntb->epf->bar[barno].addr = NULL;
+ ntb->epf->bar[barno].phys_addr = 0;
+ ntb->epf->bar[barno].flags |= upper_32_bits(size) ?
+ PCI_BASE_ADDRESS_MEM_TYPE_64 :
+ PCI_BASE_ADDRESS_MEM_TYPE_32;
+
+ ret = pci_epc_set_bar(ntb->epf->epc,
+ ntb->epf->func_no,
+ ntb->epf->vfunc_no,
+ &ntb->epf->bar[barno]);
+ if (ret) {
+ dev_err(dev, "MW set failed\n");
+ goto err_alloc_mem;
+ }
}
/* Allocate EPC outbound memory windows to vpci vntb device */
@@ -1310,10 +1373,11 @@ static int epf_ntb_mw_bar_init(struct epf_ntb *ntb)
return ret;
err_set_bar:
- pci_epc_clear_bar(ntb->epf->epc,
- ntb->epf->func_no,
- ntb->epf->vfunc_no,
- &ntb->epf->bar[barno]);
+ if (!shared)
+ pci_epc_clear_bar(ntb->epf->epc,
+ ntb->epf->func_no,
+ ntb->epf->vfunc_no,
+ &ntb->epf->bar[barno]);
err_alloc_mem:
epf_ntb_mw_bar_clear(ntb, i);
return ret;
@@ -1331,10 +1395,11 @@ static void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws)
for (i = 0; i < num_mws; i++) {
barno = ntb->epf_ntb_bar[BAR_MW1 + i];
- pci_epc_clear_bar(ntb->epf->epc,
- ntb->epf->func_no,
- ntb->epf->vfunc_no,
- &ntb->epf->bar[barno]);
+ if (!epf_ntb_dma_shares_bar(ntb, barno))
+ pci_epc_clear_bar(ntb->epf->epc,
+ ntb->epf->func_no,
+ ntb->epf->vfunc_no,
+ &ntb->epf->bar[barno]);
pci_epc_mem_free_addr(ntb->epf->epc,
ntb->vpci_mw_phy[i],
@@ -1936,6 +2001,16 @@ static int vntb_epf_mw_set_trans(struct ntb_dev *ndev, int pidx, int idx,
dev = &ndev->dev;
barno = ntb->epf_ntb_bar[BAR_MW1 + idx];
epf_bar = &ntb->epf->bar[barno];
+ if (epf_ntb_dma_shares_bar(ntb, barno)) {
+ /* DMA submaps start after the configured MW size. */
+ if (size != ntb->mws_size[idx])
+ return -EINVAL;
+
+ guard(mutex)(&ntb->dma->lock);
+
+ return epf_ntb_dma_set_bar_locked(ntb, &addr);
+ }
+
epf_bar->phys_addr = addr;
epf_bar->barno = barno;
epf_bar->size = size;
@@ -2134,15 +2209,17 @@ static int vntb_epf_mw_get_align(struct ntb_dev *ndev, int pidx, int idx,
resource_size_t *size_max)
{
struct epf_ntb *ntb = ntb_ndev(ndev);
+ enum pci_barno barno = ntb->epf_ntb_bar[BAR_MW1 + idx];
+ u64 size = ntb->mws_size[idx];
if (addr_align)
*addr_align = SZ_4K;
if (size_align)
- *size_align = 1;
+ *size_align = epf_ntb_dma_shares_bar(ntb, barno) ? size : 1;
if (size_max)
- *size_max = ntb->mws_size[idx];
+ *size_max = size;
return 0;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v6 8/8] Documentation: PCI: endpoint: Document vNTB DMA export
2026-10-20 5:49 [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
` (6 preceding siblings ...)
2026-10-20 5:49 ` [PATCH v6 7/8] PCI/NTB: endpoint: Allow DMA and MW to share a BAR Koichiro Den
@ 2026-10-20 5:49 ` Koichiro Den
2026-10-20 18:49 ` Frank Li
7 siblings, 1 reply; 13+ messages in thread
From: Koichiro Den @ 2026-10-20 5:49 UTC (permalink / raw)
To: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel
Cc: Bjorn Helgaas, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Jingoo Han, Lorenzo Pieralisi, Rob Herring, Jerome Brunet,
linux-pci, linux-doc, linux-kernel, ntb
Document the dma_bar attribute and the requirements for exporting
endpoint DMA channels. Also document how the selected BAR may be shared
with an MW.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes since v5:
- No changes.
Documentation/PCI/endpoint/pci-vntb-howto.rst | 31 +++++++++++++++++--
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/Documentation/PCI/endpoint/pci-vntb-howto.rst b/Documentation/PCI/endpoint/pci-vntb-howto.rst
index 3679f5c30254..7ca4e1ff095c 100644
--- a/Documentation/PCI/endpoint/pci-vntb-howto.rst
+++ b/Documentation/PCI/endpoint/pci-vntb-howto.rst
@@ -90,9 +90,9 @@ of the function device and is populated with the following NTB specific
attributes that can be configured by the user::
# ls functions/pci_epf_vntb/func1/pci_epf_vntb.0/
- ctrl_bar db_count mw1_bar mw2_bar mw3_bar mw4_bar spad_count
- db_bar mw1 mw2 mw3 mw4 num_mws vbus_number
- vntb_vid vntb_pid
+ ctrl_bar dma_bar mw2 mw3_bar num_mws vntb_pid
+ db_bar mw1 mw2_bar mw4 spad_count vntb_vid
+ db_count mw1_bar mw3 mw4_bar vbus_number
A sample configuration for NTB function is given below::
@@ -105,6 +105,31 @@ By default, each construct is assigned a BAR, as needed and in order.
Should a specific BAR setup be required by the platform, BAR may be assigned
to each construct using the related ``XYZ_bar`` entry.
+To export DMA channels, set ``dma_bar`` before binding the function.
+For example::
+
+ # echo 5 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/dma_bar
+
+This currently supports the unrolled DesignWare eDMA layout. The EPC must
+support subrange and dynamic inbound mappings.
+
+``dma_bar`` cannot select the control or doorbell BAR.
+Any rounded-up BAR tail is scratch-backed.
+
+Leaving ``dma_bar`` unassigned disables DMA export.
+With a separate DMA BAR, an older ``ntb_hw_epf`` peer can ignore the extension
+and continue without DMA. A peer that understands the extension also continues
+without DMA if the advertised type is unavailable or no DMA IRQ can be
+allocated.
+
+If no spare BAR is available, ``dma_bar`` can select the BAR of one MW instead.
+DMA resources then follow that MW. In this configuration:
+
+* The shared MW size must be a power of two and its translation must cover the
+ full configured size.
+* An updated peer is required because older peers treat the whole BAR as an
+ MW.
+
A sample configuration for virtual NTB driver for virtual PCI bus::
# echo 0x1957 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_vid
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v6 5/8] PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels
2026-10-20 5:49 ` [PATCH v6 5/8] PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels Koichiro Den
@ 2026-10-20 17:00 ` Frank Li
0 siblings, 0 replies; 13+ messages in thread
From: Frank Li @ 2026-10-20 17:00 UTC (permalink / raw)
To: Koichiro Den
Cc: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel, Bjorn Helgaas, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Jingoo Han, Lorenzo Pieralisi, Rob Herring,
Jerome Brunet, linux-pci, linux-doc, linux-kernel, ntb
On Tue, Oct 20, 2026 at 02:49:45PM +0900, Koichiro Den wrote:
> An RC may use endpoint-local DMA read channels to transfer data directly
> to an endpoint DMA address once both sides agree to use them. Quiescing
> an unrolled eDMA channel disables its whole direction, so reserve the
> complete read direction and route its interrupts to the RC when dma_bar
> is configured.
>
> Describe the controller and per-channel descriptor memory in a private
> control-region extension.
>
> Add a dma_bar configfs attribute. An explicit BAR selection enables DMA
> export. Leaving it unassigned keeps the feature disabled.
>
> Keep resources already assigned to a BAR in place, and map the rest
> through the selected dma_bar.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Changes since v5:
> - Move the DMA extension layout to include/linux/pci-epf-ntb.h and
> place it right after struct epf_ntb_ctrl instead of embedding it,
> since pci-epf-ntb now shares the structure. (Frank)
> - Small refactorings:
> * Minimize the kernel-doc change in epf_ntb_config_spad_bar_alloc().
> * Group DMA BAR init and clear with the common DMA helpers.
> - Link to v5: https://lore.kernel.org/r/20261009045949.1109871-5-den@valinux.co.jp/
>
> drivers/pci/endpoint/functions/pci-epf-vntb.c | 487 +++++++++++++++++-
> include/linux/pci-epf-ntb.h | 39 ++
> 2 files changed, 517 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> index 3adbaa9d85db..ac83af54122f 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> @@ -39,9 +39,13 @@
> #include <linux/atomic.h>
> #include <linux/bitops.h>
> #include <linux/delay.h>
> +#include <linux/dma/edma.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dmaengine.h>
> #include <linux/io.h>
> #include <linux/module.h>
> #include <linux/mutex.h>
> +#include <linux/overflow.h>
> #include <linux/slab.h>
>
> #include <linux/pci-ep-msi.h>
> @@ -67,6 +71,7 @@ enum epf_ntb_bar {
> BAR_MW2,
> BAR_MW3,
> BAR_MW4,
> + BAR_DMA,
> VNTB_BAR_NUM,
> };
>
> @@ -78,6 +83,18 @@ enum epf_irq_slot {
>
> #define MIN_DB_COUNT (EPF_IRQ_DB_START + 1)
>
> +struct epf_ntb_dma {
> + struct epf_ntb_dma_ctrl ctrl;
> + struct dma_chan *dchan[EDMA_MAX_RD_CH];
> + void *bar_scratch;
> + dma_addr_t bar_scratch_phys;
> + size_t bar_scratch_size;
> + struct pci_epf_bar_submap submap[EDMA_MAX_RD_CH + 2];
> + struct pci_epf_bar_submap *reg_submap;
> + unsigned int num_submap;
> + u16 rd_ch_cnt;
> +};
> +
> struct epf_ntb {
> struct ntb_dev *ntb;
> struct pci_epf *epf;
> @@ -111,6 +128,7 @@ struct epf_ntb {
> enum pci_barno epf_ntb_bar[VNTB_BAR_NUM];
>
> struct epf_ntb_ctrl *reg;
> + struct epf_ntb_dma *dma;
>
> u32 *epf_db;
>
> @@ -202,7 +220,8 @@ static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
> {
> int i;
>
> - for (i = 0; i < VNTB_BAR_NUM; i++) {
> + /* BAR_DMA is checked separately because it may share an MW BAR. */
> + for (i = 0; i < BAR_DMA; i++) {
> if (ntb->epf_ntb_bar[i] == barno)
> return true;
> }
> @@ -210,6 +229,419 @@ static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
> return false;
> }
>
> +struct epf_ntb_dma_filter {
> + struct device *dev;
> + int chan_id;
> +};
> +
> +static bool epf_ntb_dma_filter(struct dma_chan *chan, void *data)
> +{
> + struct epf_ntb_dma_filter *filter = data;
> +
> + return chan->device->dev == filter->dev &&
> + chan->chan_id == filter->chan_id;
> +}
> +
> +static int epf_ntb_dma_add_region(struct epf_ntb_dma *dma,
> + const struct pci_epc_aux_resource *resource,
> + dma_addr_t target_addr,
> + enum pci_barno barno, size_t align, u32 *next,
> + struct epf_ntb_dma_region_ctrl *region)
> +{
> + resource_size_t delta, map_size, size;
> + struct pci_epf_bar_submap *submap;
> + dma_addr_t base;
> +
> + if (!resource->size || resource->size > U32_MAX)
> + return -EINVAL;
> +
> + region->size = resource->size;
> + if (resource->bar != NO_BAR) {
> + if (resource->bar < BAR_0 || resource->bar > BAR_5 ||
> + resource->bar_offset > U32_MAX)
> + return -EINVAL;
> +
> + region->bar = resource->bar;
> + region->offset = resource->bar_offset;
> + return 0;
> + }
> + submap = &dma->submap[dma->num_submap];
> +
> + /*
> + * The RC is trusted with endpoint-local DMA. Map the aligned span
> + * required by the EPC, but advertise only the resource itself.
> + */
> + base = ALIGN_DOWN(target_addr, align);
> + delta = target_addr - base;
> + if (check_add_overflow(delta, resource->size, &size))
> + return -EOVERFLOW;
> + map_size = ALIGN(size, align);
> + if (map_size < size || map_size > U32_MAX - *next)
> + return -EOVERFLOW;
> +
> + submap->phys_addr = base;
> + submap->size = map_size;
> + region->bar = barno;
> + region->offset = *next + delta;
> + *next += map_size;
> + dma->num_submap++;
> +
> + return 0;
> +}
> +
> +/* DW eDMA */
> +
> +static int epf_ntb_dw_edma_claim(struct device *dev, int chan_id,
> + struct dma_chan **dchan)
> +{
> + struct epf_ntb_dma_filter filter = {
> + .dev = dev,
> + .chan_id = chan_id,
> + };
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_SLAVE, mask);
> + *dchan = dma_request_channel(mask, epf_ntb_dma_filter, &filter);
> +
> + return *dchan ? 0 : -EBUSY;
> +}
> +
> +static void epf_ntb_dw_edma_release_channels(struct epf_ntb_dma *dma)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < dma->rd_ch_cnt; i++) {
> + if (!dma->dchan[i])
> + continue;
> +
> + dma_release_channel(dma->dchan[i]);
> + }
> +}
> +
> +static const struct pci_epc_aux_resource *
> +epf_ntb_dw_edma_find_desc(const struct pci_epc_aux_resource *resources,
> + unsigned int count, u16 chan_id)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < count; i++)
> + if (resources[i].type == PCI_EPC_AUX_DMA_DESC_MEM &&
> + resources[i].u.dma_desc.chan_id == chan_id)
> + return &resources[i];
> +
> + return NULL;
> +}
> +
> +static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
> + struct epf_ntb_dma *dma,
> + const struct pci_epc_aux_resource *ctrl,
> + const struct pci_epc_aux_resource *resources,
> + unsigned int count)
> +{
> + struct dw_edma_chan_config chan_config = {
> + .flags = DW_EDMA_CH_CONFIG_IRQ_MODE,
> + .irq_mode = DW_EDMA_CH_IRQ_REMOTE,
> + };
> + struct dma_slave_config config = {
> + .peripheral_config = &chan_config,
> + .peripheral_size = sizeof(chan_config),
> + };
> + enum pci_barno barno = ntb->epf_ntb_bar[BAR_DMA];
> + const struct pci_epc_features *features;
> + struct device *dma_dev;
> + unsigned int i;
> + size_t align;
> + u32 next = 0;
> + int ret;
> +
> + if (ctrl->u.dma_ctrl.reg_layout_data != EDMA_MF_EDMA_UNROLL)
> + return -EOPNOTSUPP;
> +
> + dma->rd_ch_cnt = ctrl->u.dma_ctrl.rc_to_ep_ch_cnt;
> +
> + features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no);
> + /* DWC descriptor memory always needs subrange mappings. */
> + if (!features || !features->subrange_mapping ||
> + !features->dynamic_inbound_mapping)
> + return -EOPNOTSUPP;
> +
> + align = features->align ?: 1;
> + if (!is_power_of_2(align))
> + return -EINVAL;
> +
> + if (epf_ntb_is_bar_used(ntb, barno) ||
> + pci_epc_get_next_free_bar(features, barno) != barno)
> + return -EINVAL;
> +
> + dma->ctrl.magic = EPF_NTB_DMA_MAGIC;
> + dma->ctrl.revision = EPF_NTB_DMA_REVISION;
> + dma->ctrl.type = EPF_NTB_DMA_TYPE_DW_EDMA;
> + dma->ctrl.submap.bar = barno;
> + dma->ctrl.length = offsetof(struct epf_ntb_dma_ctrl,
> + chan[dma->rd_ch_cnt]);
> +
> + if (ctrl->bar == NO_BAR)
> + dma->reg_submap = &dma->submap[dma->num_submap];
> + ret = epf_ntb_dma_add_region(dma, ctrl, ctrl->phys_addr,
> + barno, align, &next, &dma->ctrl.reg);
> + if (ret)
> + return ret;
> +
> + /* DW eDMA static IDs place read channels after all write channels. */
> + for (i = 0; i < dma->rd_ch_cnt; i++) {
> + u16 chan_id = ctrl->u.dma_ctrl.ep_to_rc_ch_cnt + i;
> + struct epf_ntb_dma_chan_ctrl *chan = &dma->ctrl.chan[i];
> + const struct pci_epc_aux_resource *desc;
> + dma_addr_t dma_addr;
> +
> + desc = epf_ntb_dw_edma_find_desc(resources, count, chan_id);
> + if (!desc)
> + return -EINVAL;
> + dma_addr = desc->u.dma_desc.dma_addr;
> +
> + ret = epf_ntb_dma_add_region(dma, desc, dma_addr, barno,
> + align, &next, &chan->desc);
> + if (ret)
> + return ret;
> + chan->desc_addr_lo = lower_32_bits(dma_addr);
> + chan->desc_addr_hi = upper_32_bits(dma_addr);
> + }
> + dma->ctrl.submap.size = next;
> +
> + dma_dev = ntb->epf->epc->dev.parent;
> + for (i = 0; i < dma->rd_ch_cnt; i++) {
> + u16 chan_id = ctrl->u.dma_ctrl.ep_to_rc_ch_cnt + i;
> +
> + ret = epf_ntb_dw_edma_claim(dma_dev, chan_id, &dma->dchan[i]);
> + if (ret)
> + return ret;
> + }
> +
> + for (i = 0; i < dma->rd_ch_cnt; i++) {
> + ret = dmaengine_slave_config(dma->dchan[i], &config);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/* Common endpoint DMA */
> +
> +static void epf_ntb_dma_release_channels(struct epf_ntb_dma *dma)
> +{
> + switch (dma->ctrl.type) {
> + case EPF_NTB_DMA_TYPE_DW_EDMA:
> + epf_ntb_dw_edma_release_channels(dma);
> + break;
> + }
> +}
> +
> +static int epf_ntb_dma_collect(struct epf_ntb *ntb)
> +{
> + const struct pci_epc_aux_resource *ctrl = NULL;
> + struct device *dma_dev;
> + dma_addr_t dma_addr;
> + unsigned int i;
> + int count, ret;
> +
> + if (ntb->epf_ntb_bar[BAR_DMA] == NO_BAR)
> + return 0;
> +
> + count = pci_epc_get_aux_resources_count(ntb->epf->epc,
> + ntb->epf->func_no,
> + ntb->epf->vfunc_no);
> + if (count <= 0)
> + return count ?: -ENODEV;
> +
> + struct pci_epc_aux_resource *resources __free(kfree) =
> + kcalloc(count, sizeof(*resources), GFP_KERNEL);
> + if (!resources)
> + return -ENOMEM;
> +
> + ret = pci_epc_get_aux_resources(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no, resources, count);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < count; i++) {
> + if (resources[i].type != PCI_EPC_AUX_DMA_CTRL_MMIO)
> + continue;
> + if (ctrl)
> + return -EINVAL;
> + ctrl = &resources[i];
> + }
> + if (!ctrl)
> + return -ENODEV;
> +
> + struct epf_ntb_dma *dma __free(kfree) =
> + kzalloc(sizeof(*dma), GFP_KERNEL);
> + if (!dma)
> + return -ENOMEM;
> +
> + switch (ctrl->u.dma_ctrl.reg_layout) {
> + case PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA:
> + ret = epf_ntb_dw_edma_collect(ntb, dma, ctrl, resources, count);
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> + if (ret) {
> + epf_ntb_dma_release_channels(dma);
> + return ret;
> + }
> +
> + /*
> + * CTRL_MMIO carries a CPU physical address, while DMA_DESC_MEM already
> + * carries an endpoint DMA address. Convert only the former.
> + */
> + if (dma->reg_submap) {
> + dma_dev = ntb->epf->epc->dev.parent;
> + dma_addr = dma_map_resource(dma_dev, dma->reg_submap->phys_addr,
> + dma->reg_submap->size,
> + DMA_BIDIRECTIONAL, 0);
> + if (dma_mapping_error(dma_dev, dma_addr)) {
> + epf_ntb_dma_release_channels(dma);
> + return -EIO;
> + }
> + dma->reg_submap->phys_addr = dma_addr;
> + }
> +
> + ntb->dma = no_free_ptr(dma);
> +
> + return 0;
> +}
> +
> +static void epf_ntb_dma_release(struct epf_ntb *ntb)
> +{
> + struct epf_ntb_dma *dma = ntb->dma;
> + struct device *dev;
> +
> + if (!dma)
> + return;
> +
> + epf_ntb_dma_release_channels(dma);
> + dev = ntb->epf->epc->dev.parent;
> + if (dma->reg_submap)
> + dma_unmap_resource(dev, dma->reg_submap->phys_addr,
> + dma->reg_submap->size, DMA_BIDIRECTIONAL, 0);
> + if (dma->bar_scratch)
> + dma_free_coherent(dev, dma->bar_scratch_size,
> + dma->bar_scratch, dma->bar_scratch_phys);
> + kfree(dma);
> + ntb->dma = NULL;
> +}
> +
> +static int epf_ntb_dma_bar_init(struct epf_ntb *ntb)
> +{
> + struct device *dev = ntb->epf->epc->dev.parent;
> + const struct pci_epc_features *features;
> + struct epf_ntb_dma *dma = ntb->dma;
> + struct pci_epf_bar *bar;
> + enum pci_barno barno;
> + size_t backing_size;
> + u32 mapped_size;
> + int ret;
> +
> + features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no);
> + if (!features)
> + return -EOPNOTSUPP;
> +
> + barno = ntb->epf_ntb_bar[BAR_DMA];
> + mapped_size = dma->ctrl.submap.size;
> + /*
> + * Submaps cannot be installed until the host assigns the BAR address.
> + * Use address 0 for the temporary BAR Match Mode mapping, as is done
> + * for regular vNTB MW BARs.
> + */
> + ret = pci_epf_assign_bar_space(ntb->epf, mapped_size, barno, features,
> + PRIMARY_INTERFACE, 0);
> + if (ret)
> + return ret;
> +
> + bar = &ntb->epf->bar[barno];
> + if (bar->size > U32_MAX)
> + return -EOVERFLOW;
> +
> + backing_size = bar->size - mapped_size;
> + if (backing_size) {
> + /* Back the BAR tail added by the power-of-two size rounding. */
> + dma->bar_scratch = dma_alloc_coherent(dev, backing_size,
> + &dma->bar_scratch_phys,
> + GFP_KERNEL);
> + if (!dma->bar_scratch)
> + return -ENOMEM;
> + dma->bar_scratch_size = backing_size;
> + if (!IS_ALIGNED(dma->bar_scratch_phys, features->align ?: 1))
> + return -EINVAL;
> +
> + dma->submap[dma->num_submap++] = (struct pci_epf_bar_submap) {
> + .phys_addr = dma->bar_scratch_phys,
> + .size = backing_size,
> + };
> + }
> +
> + return pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no, bar);
> +}
> +
> +static void epf_ntb_dma_bar_clear(struct epf_ntb *ntb)
> +{
> + struct epf_ntb_dma *dma = ntb->dma;
> + struct pci_epf_bar *bar;
> + enum pci_barno barno;
> +
> + if (!dma)
> + return;
> +
> + barno = ntb->epf_ntb_bar[BAR_DMA];
> + bar = &ntb->epf->bar[barno];
> + if (!bar->size)
> + return;
> +
> + pci_epc_clear_bar(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no, bar);
> + memset(bar, 0, sizeof(*bar));
> +}
> +
> +static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
> +{
> + struct pci_epf_bar_submap *old_submap;
> + struct epf_ntb_dma *dma = ntb->dma;
> + unsigned int old_num_submap;
> + struct pci_epf_bar *bar;
> + int restore, ret;
> +
> + if (!dma)
> + return 0;
> +
> + bar = &ntb->epf->bar[ntb->epf_ntb_bar[BAR_DMA]];
> +
> + old_submap = bar->submap;
> + old_num_submap = bar->num_submap;
> + bar->submap = dma->submap;
> + bar->num_submap = dma->num_submap;
> +
> + ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no, bar);
> + if (!ret)
> + return 0;
> +
> + /* A failed dynamic update may have already removed the old mapping. */
> + bar->submap = old_submap;
> + bar->num_submap = old_num_submap;
> + restore = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no, bar);
> + if (restore)
> + dev_warn(&ntb->epf->dev,
> + "failed to restore DMA BAR mapping: %d\n", restore);
> +
> + return ret;
> +}
> +
> /**
> * epf_ntb_configure_mw() - Configure the Outbound Address Space for VHOST
> * to access the memory window of HOST
> @@ -342,6 +774,11 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
> epf_ntb_teardown_mw(ntb, argument);
> ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
> break;
> + case EPF_NTB_COMMAND_SETUP_DMA_BAR:
> + ret = epf_ntb_dma_set_bar(ntb);
> + ctrl->command_status = ret ? EPF_NTB_COMMAND_STATUS_ERROR :
> + EPF_NTB_COMMAND_STATUS_OK;
> + break;
> case EPF_NTB_COMMAND_LINK_UP:
> ret = epf_ntb_set_link_enabled(ntb, true);
> if (ret < 0)
> @@ -488,16 +925,17 @@ static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
> * Virtual PCI PCIe Endpoint
> * NTB Driver NTB Driver
> *
> - * Allocate the Local Memory mentioned in the above diagram. The size of
> - * CONFIG REGION is sizeof(struct epf_ntb_ctrl), plus space for the MSI-X
> - * Table and PBA if supported. The size of SCRATCHPAD REGION is obtained
> - * from "spad-count" configfs entry.
> + * Allocate the Local Memory mentioned in the above diagram. CONFIG REGION
> + * contains struct epf_ntb_ctrl, the DMA extension if enabled, and the MSI-X
> + * Table and PBA if supported, in that order. The size of SCRATCHPAD REGION
> + * is obtained from "spad-count" configfs entry.
> *
> * Returns: Zero for success, or an error code in case of failure
> */
> static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
> {
> enum pci_barno barno;
> + struct epf_ntb_dma_ctrl *dma_ctrl;
> struct epf_ntb_ctrl *ctrl;
> u32 spad_size, ctrl_size;
> struct pci_epf *epf = ntb->epf;
> @@ -511,7 +949,10 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
> barno = ntb->epf_ntb_bar[BAR_CONFIG];
> spad_count = ntb->spad_count;
>
> - ctrl_size = ALIGN(sizeof(struct epf_ntb_ctrl), sizeof(u32));
> + ctrl_size = sizeof(struct epf_ntb_ctrl);
> + if (ntb->dma)
> + ctrl_size += sizeof(struct epf_ntb_dma_ctrl);
> + ctrl_size = ALIGN(ctrl_size, sizeof(u32));
> if (epc_features->msix_capable) {
> ntb->msix_table_offset = ALIGN(ctrl_size, 8);
> ctrl_size = ntb->msix_table_offset +
> @@ -543,6 +984,11 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
> ntb->reg->db_offset[i] = 0;
> }
>
> + if (ntb->dma) {
> + dma_ctrl = base + EPF_NTB_DMA_CTRL_OFFSET;
> + *dma_ctrl = ntb->dma->ctrl;
> + }
> +
> return 0;
> }
>
> @@ -924,7 +1370,8 @@ static int epf_ntb_find_bar(struct epf_ntb *ntb,
> * Verify if the BAR found is not already assigned
> * through the provided configuration
> */
> - if (!epf_ntb_is_bar_used(ntb, barno))
> + if (ntb->epf_ntb_bar[BAR_DMA] != barno &&
> + !epf_ntb_is_bar_used(ntb, barno))
> ntb->epf_ntb_bar[bar] = barno;
>
> barno += 1;
> @@ -1023,11 +1470,19 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
> goto err_mw_bar_init;
> }
>
> + if (ntb->dma) {
> + ret = epf_ntb_dma_bar_init(ntb);
> + if (ret) {
> + dev_err(dev, "DMA BAR init failed\n");
> + goto err_dma_bar_init;
> + }
> + }
> +
> if (vfunc_no <= 1) {
> ret = pci_epc_write_header(epc, func_no, vfunc_no, epf->header);
> if (ret) {
> dev_err(dev, "Configuration header write failed\n");
> - goto err_write_header;
> + goto err_dma_bar_init;
> }
> }
>
> @@ -1038,7 +1493,8 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
>
> return 0;
>
> -err_write_header:
> +err_dma_bar_init:
> + epf_ntb_dma_bar_clear(ntb);
> epf_ntb_mw_bar_clear(ntb, ntb->num_mws);
> err_mw_bar_init:
> epf_ntb_db_bar_clear(ntb);
> @@ -1061,6 +1517,7 @@ static void epf_ntb_epc_cleanup(struct epf_ntb *ntb)
> disable_delayed_work_sync(&ntb->cmd_handler);
> disable_work_sync(&ntb->peer_db_work);
> atomic64_set(&ntb->peer_db_pending, 0);
> + epf_ntb_dma_bar_clear(ntb);
> epf_ntb_mw_bar_clear(ntb, ntb->num_mws);
> epf_ntb_db_bar_clear(ntb);
> epf_ntb_config_sspad_bar_clear(ntb);
> @@ -1267,10 +1724,13 @@ EPF_NTB_BAR_R(mw3_bar, BAR_MW3)
> EPF_NTB_BAR_W(mw3_bar, BAR_MW3)
> EPF_NTB_BAR_R(mw4_bar, BAR_MW4)
> EPF_NTB_BAR_W(mw4_bar, BAR_MW4)
> +EPF_NTB_BAR_R(dma_bar, BAR_DMA)
> +EPF_NTB_BAR_W(dma_bar, BAR_DMA)
>
> CONFIGFS_ATTR(epf_ntb_, spad_count);
> CONFIGFS_ATTR(epf_ntb_, db_count);
> CONFIGFS_ATTR(epf_ntb_, num_mws);
> +CONFIGFS_ATTR(epf_ntb_, dma_bar);
> CONFIGFS_ATTR(epf_ntb_, mw1);
> CONFIGFS_ATTR(epf_ntb_, mw2);
> CONFIGFS_ATTR(epf_ntb_, mw3);
> @@ -1289,6 +1749,7 @@ static struct configfs_attribute *epf_ntb_attrs[] = {
> &epf_ntb_attr_spad_count,
> &epf_ntb_attr_db_count,
> &epf_ntb_attr_num_mws,
> + &epf_ntb_attr_dma_bar,
> &epf_ntb_attr_mw1,
> &epf_ntb_attr_mw2,
> &epf_ntb_attr_mw3,
> @@ -1847,6 +2308,12 @@ static int epf_ntb_bind(struct pci_epf *epf)
> return ret;
> }
>
> + ret = epf_ntb_dma_collect(ntb);
> + if (ret) {
> + dev_err(dev, "Failed to prepare NTB DMA export\n");
> + return ret;
> + }
> +
> ret = epf_ntb_config_spad_bar_alloc(ntb);
> if (ret) {
> dev_err(dev, "Failed to allocate BAR memory\n");
> @@ -1887,6 +2354,7 @@ static int epf_ntb_bind(struct pci_epf *epf)
> epf_ntb_epc_cleanup(ntb);
> err_bar_alloc:
> epf_ntb_config_spad_bar_free(ntb);
> + epf_ntb_dma_release(ntb);
>
> return ret;
> }
> @@ -1918,6 +2386,7 @@ static void epf_ntb_unbind(struct pci_epf *epf)
>
> epf_ntb_epc_cleanup(ntb);
> epf_ntb_config_spad_bar_free(ntb);
> + epf_ntb_dma_release(ntb);
> }
>
> // EPF driver probe
> diff --git a/include/linux/pci-epf-ntb.h b/include/linux/pci-epf-ntb.h
> index 0c02d709aa08..5cc0f0badae0 100644
> --- a/include/linux/pci-epf-ntb.h
> +++ b/include/linux/pci-epf-ntb.h
> @@ -8,6 +8,8 @@
> #define __LINUX_PCI_EPF_NTB_H
>
> #include <linux/bits.h>
> +#include <linux/dma/edma.h>
> +#include <linux/stddef.h>
> #include <linux/types.h>
>
> #define EPF_NTB_COMMAND_CONFIGURE_DOORBELL 1
> @@ -16,6 +18,7 @@
> #define EPF_NTB_COMMAND_TEARDOWN_MW 4
> #define EPF_NTB_COMMAND_LINK_UP 5
> #define EPF_NTB_COMMAND_LINK_DOWN 6
> +#define EPF_NTB_COMMAND_SETUP_DMA_BAR 7
>
> /* EPF_NTB_COMMAND_CONFIGURE_DOORBELL argument */
> #define EPF_NTB_DB_COUNT_MASK GENMASK(15, 0)
> @@ -45,4 +48,40 @@ struct epf_ntb_ctrl {
> u32 db_offset[EPF_NTB_MAX_DB_COUNT];
> } __packed;
>
> +/*
> + * DMA wire extension produced by pci-epf-vntb and consumed by
> + * ntb_hw_epf. It immediately follows struct epf_ntb_ctrl.
> + */
> +#define EPF_NTB_DMA_CTRL_OFFSET sizeof(struct epf_ntb_ctrl)
> +
> +#define EPF_NTB_DMA_MAGIC 0x414d444e /* "NDMA": NTB DMA */
> +#define EPF_NTB_DMA_REVISION 1
> +#define EPF_NTB_DMA_TYPE_DW_EDMA 1
> +
> +struct epf_ntb_dma_region_ctrl {
> + u32 bar;
> + u32 offset;
> + u32 size;
> +} __packed;
> +
> +struct epf_ntb_dma_chan_ctrl {
> + struct epf_ntb_dma_region_ctrl desc;
> + u32 desc_addr_lo;
> + u32 desc_addr_hi;
> +} __packed;
> +
> +struct epf_ntb_dma_ctrl {
> + u32 magic;
> + u16 revision;
> + u16 length;
> + u32 type;
> + /* BAR range occupied by resources without a fixed BAR assignment. */
> + struct epf_ntb_dma_region_ctrl submap;
> + struct epf_ntb_dma_region_ctrl reg;
> + struct epf_ntb_dma_chan_ctrl chan[EDMA_MAX_RD_CH];
> +} __packed;
> +
> +/* Common header needed even when endpoint DMA is unavailable. */
> +#define EPF_NTB_DMA_CTRL_COMMON_SIZE offsetof(struct epf_ntb_dma_ctrl, reg)
> +
> #endif /* __LINUX_PCI_EPF_NTB_H */
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v6 6/8] NTB: ntb_hw_epf: Discover vNTB-embedded DMA
2026-10-20 5:49 ` [PATCH v6 6/8] NTB: ntb_hw_epf: Discover vNTB-embedded DMA Koichiro Den
@ 2026-10-20 17:03 ` Frank Li
0 siblings, 0 replies; 13+ messages in thread
From: Frank Li @ 2026-10-20 17:03 UTC (permalink / raw)
To: Koichiro Den
Cc: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel, Bjorn Helgaas, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Jingoo Han, Lorenzo Pieralisi, Rob Herring,
Jerome Brunet, linux-pci, linux-doc, linux-kernel, ntb
On Tue, Oct 20, 2026 at 02:49:46PM +0900, Koichiro Den wrote:
> A vNTB PCI function can now expose endpoint-local DMA channels to its
> host. Decode the extension and register the advertised channels before
> the NTB device, so clients of that NTB device can use the DMA
> functionality in the same PCI function. Endpoints without the extension,
> or hosts without support for its DMA type or with no DMA IRQ available,
> continue without DMA.
>
> Only DW eDMA is supported for now. Dispatch on the advertised DMA type
> and keep type-specific setup in local helpers. Support for another
> implementation can add a new case and its own helpers.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Changes since v5:
> - Derive the control region and DMA extension offsets from
> include/linux/pci-epf-ntb.h instead of hard-coding them. (Frank)
> - Move shared-MW boundary handling to the BAR-sharing patch.
> - Link to v5: https://lore.kernel.org/r/20261009045949.1109871-7-den@valinux.co.jp/
>
> drivers/ntb/hw/epf/ntb_hw_epf.c | 273 +++++++++++++++++++++++++++++++-
> 1 file changed, 267 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c
> index ff92c2a61ce7..e0016128b95c 100644
> --- a/drivers/ntb/hw/epf/ntb_hw_epf.c
> +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c
> @@ -8,6 +8,7 @@
>
> #include <linux/atomic.h>
> #include <linux/delay.h>
> +#include <linux/dma/edma.h>
> #include <linux/io-64-nonatomic-lo-hi.h>
> #include <linux/module.h>
> #include <linux/pci.h>
> @@ -18,6 +19,12 @@
> /* Offsets into the control region, see include/linux/pci-epf-ntb.h. */
> #define NTB_EPF_CTRL(field) offsetof(struct epf_ntb_ctrl, field)
>
> +/* Private DMA wire extension produced by pci-epf-vntb. */
> +#define NTB_EPF_DMA(field) (EPF_NTB_DMA_CTRL_OFFSET + \
> + offsetof(struct epf_ntb_dma_ctrl, field))
> +#define NTB_EPF_DMA_REGION(field) \
> + offsetof(struct epf_ntb_dma_region_ctrl, field)
> +
> /*
> * Legacy doorbell slot layout when paired with pci-epf-*ntb:
> *
> @@ -71,6 +78,24 @@ struct ntb_epf_irq_ctx {
> unsigned int irq_no;
> };
>
> +struct ntb_epf_dma_region {
> + unsigned int bar;
> + resource_size_t offset;
> + resource_size_t size;
> +};
> +
> +struct ntb_epf_dw_edma {
> + struct dw_edma_chip chip;
> + struct ntb_epf_dma_region reg;
> + struct ntb_epf_dma_region ll[EDMA_MAX_RD_CH];
> +};
> +
> +struct ntb_epf_dma {
> + struct ntb_epf_dma_region submap;
> + unsigned int nr_irqs;
> + u32 type;
> +};
> +
> struct ntb_epf_dev {
> struct ntb_dev ntb;
> struct device *dev;
> @@ -82,6 +107,8 @@ struct ntb_epf_dev {
> unsigned int mw_count;
> unsigned int spad_count;
> unsigned int db_count;
> + struct ntb_epf_dma dma;
> + struct ntb_epf_dw_edma dw_edma;
>
> void __iomem *ctrl_reg;
> void __iomem *db_reg;
> @@ -136,6 +163,213 @@ static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command,
> return ret;
> }
>
> +static bool ntb_epf_dma_region_parse(struct ntb_epf_dev *ndev, u32 base,
> + bool optional,
> + struct ntb_epf_dma_region *region)
> +{
> + void __iomem *reg = ndev->ctrl_reg + base;
> + resource_size_t bar_len;
> + u32 bar, offset, size;
> +
> + bar = readl(reg + NTB_EPF_DMA_REGION(bar));
> + offset = readl(reg + NTB_EPF_DMA_REGION(offset));
> + size = readl(reg + NTB_EPF_DMA_REGION(size));
> + region->bar = bar;
> + region->offset = offset;
> + region->size = size;
> + if (!size)
> + return optional && bar == U32_MAX && !offset;
> + if (bar > BAR_5)
> + return false;
> +
> + bar_len = pci_resource_len(ndev->ntb.pdev, bar);
> + if (offset > bar_len || size > bar_len - offset)
> + return false;
> +
> + return true;
> +}
> +
> +/* DW eDMA */
> +
> +static int ntb_epf_dw_edma_parse(struct ntb_epf_dev *ndev, u32 length)
> +{
> + struct ntb_epf_dw_edma *edma = &ndev->dw_edma;
> + struct dw_edma_chip *chip = &edma->chip;
> + u32 count, chan_offset, chan_size;
> + unsigned int i;
> +
> + chan_offset = offsetof(struct epf_ntb_dma_ctrl, chan);
> + chan_size = sizeof(struct epf_ntb_dma_chan_ctrl);
> + if (length < chan_offset + chan_size ||
> + (length - chan_offset) % chan_size)
> + return -EINVAL;
> +
> + count = (length - chan_offset) / chan_size;
> + if (count > EDMA_MAX_RD_CH)
> + return -EINVAL;
> +
> + if (!ntb_epf_dma_region_parse(ndev, NTB_EPF_DMA(reg), false,
> + &edma->reg) ||
> + !IS_ALIGNED(edma->reg.offset, sizeof(u32)))
> + return -EINVAL;
> +
> + chip->mf = EDMA_MF_EDMA_UNROLL;
> + chip->ll_rd_cnt = count;
> + chip->func_no = PCI_FUNC(ndev->ntb.pdev->devfn);
> +
> + for (i = 0; i < count; i++) {
> + u64 paddr;
> +
> + if (!ntb_epf_dma_region_parse(ndev, NTB_EPF_DMA(chan[i].desc),
> + false, &edma->ll[i]) ||
> + !IS_ALIGNED(edma->ll[i].offset, sizeof(u32)))
> + return -EINVAL;
> +
> + paddr = readl(ndev->ctrl_reg +
> + NTB_EPF_DMA(chan[i].desc_addr_lo));
> + paddr |= (u64)readl(ndev->ctrl_reg +
> + NTB_EPF_DMA(chan[i].desc_addr_hi)) << 32;
> + if (paddr == U64_MAX)
> + return -EINVAL;
> +
> + chip->ll_region_rd[i].sz = edma->ll[i].size;
> + chip->ll_region_rd[i].paddr = paddr;
> + }
> + ndev->dma.nr_irqs = count;
> +
> + return 0;
> +}
> +
> +static int ntb_epf_dw_edma_irq_vector(struct device *dev, unsigned int nr)
> +{
> + struct ntb_epf_dev *ndev = dev_get_drvdata(dev);
> +
> + /* DMA vectors follow the NTB link and doorbell vector block. */
> + return pci_irq_vector(ndev->ntb.pdev, ndev->db_count + 1 + nr);
> +}
> +
> +static const struct dw_edma_plat_ops ntb_epf_dw_edma_ops = {
> + .irq_vector = ntb_epf_dw_edma_irq_vector,
> +};
> +
> +static void __iomem *
> +ntb_epf_dw_edma_map_region(struct pci_dev *pdev,
> + const struct ntb_epf_dma_region *region)
> +{
> + return pcim_iomap_range(pdev, region->bar, region->offset, region->size);
> +}
> +
> +static int ntb_epf_dw_edma_init(struct ntb_epf_dev *ndev)
> +{
> + struct ntb_epf_dw_edma *edma = &ndev->dw_edma;
> + struct dw_edma_chip *chip = &edma->chip;
> + struct pci_dev *pdev = ndev->ntb.pdev;
> + unsigned int i;
> + int ret;
> +
> + /* Install BAR submaps before mapping the advertised DMA regions. */
> + ret = ntb_epf_send_command(ndev, EPF_NTB_COMMAND_SETUP_DMA_BAR, 0);
> + if (ret)
> + return ret;
> +
> + chip->reg_base = ntb_epf_dw_edma_map_region(pdev, &edma->reg);
> + if (IS_ERR(chip->reg_base))
> + return PTR_ERR(chip->reg_base);
> + for (i = 0; i < chip->ll_rd_cnt; i++) {
> + chip->ll_region_rd[i].vaddr.io =
> + ntb_epf_dw_edma_map_region(pdev, &edma->ll[i]);
> + if (IS_ERR(chip->ll_region_rd[i].vaddr.io))
> + return PTR_ERR(chip->ll_region_rd[i].vaddr.io);
> + }
> +
> + chip->dev = ndev->dev;
> + chip->nr_irqs = ndev->dma.nr_irqs;
> + chip->ops = &ntb_epf_dw_edma_ops;
> + chip->flags = DW_EDMA_CHIP_PARTIAL;
> +
> + return dw_edma_probe(chip);
> +}
> +
> +static void ntb_epf_dw_edma_deinit(struct ntb_epf_dev *ndev)
> +{
> + dw_edma_remove(&ndev->dw_edma.chip);
> +}
> +
> +/* Common endpoint DMA */
> +
> +static int ntb_epf_dma_parse(struct ntb_epf_dev *ndev)
> +{
> + u32 magic, type, spad_off;
> + u16 revision, length;
> + int ret;
> +
> + /*
> + * Legacy endpoints place scratchpads where this extension would begin.
> + * Once the extension is present, malformed metadata is fatal.
> + */
> + spad_off = readl(ndev->ctrl_reg + NTB_EPF_CTRL(spad_offset));
> + if (spad_off < EPF_NTB_DMA_CTRL_OFFSET + EPF_NTB_DMA_CTRL_COMMON_SIZE)
> + return 0;
> +
> + magic = readl(ndev->ctrl_reg + NTB_EPF_DMA(magic));
> + if (!magic)
> + return 0;
> + if (magic == U32_MAX)
> + return -EIO;
> + if (magic != EPF_NTB_DMA_MAGIC)
> + return -EINVAL;
> +
> + revision = readw(ndev->ctrl_reg + NTB_EPF_DMA(revision));
> + length = readw(ndev->ctrl_reg + NTB_EPF_DMA(length));
> + if (revision != EPF_NTB_DMA_REVISION ||
> + length < EPF_NTB_DMA_CTRL_COMMON_SIZE ||
> + spad_off < EPF_NTB_DMA_CTRL_OFFSET + length)
> + return -EINVAL;
> + if (!ntb_epf_dma_region_parse(ndev, NTB_EPF_DMA(submap), true,
> + &ndev->dma.submap))
> + return -EINVAL;
> +
> + type = readl(ndev->ctrl_reg + NTB_EPF_DMA(type));
> + if (type == U32_MAX)
> + return -EIO;
> +
> + switch (type) {
> + case EPF_NTB_DMA_TYPE_DW_EDMA:
> + if (!IS_REACHABLE(CONFIG_DW_EDMA))
> + break;
> + ret = ntb_epf_dw_edma_parse(ndev, length);
> + if (!ret)
> + ndev->dma.type = type;
> + return ret;
> + default:
> + break;
> + }
> +
> + dev_warn(ndev->dev,
> + "Endpoint DMA type %u unavailable, continuing without DMA\n",
> + type);
> + return 0;
> +}
> +
> +static int ntb_epf_dma_init(struct ntb_epf_dev *ndev)
> +{
> + switch (ndev->dma.type) {
> + case EPF_NTB_DMA_TYPE_DW_EDMA:
> + return ntb_epf_dw_edma_init(ndev);
> + default:
> + return 0;
> + }
> +}
> +
> +static void ntb_epf_dma_deinit(struct ntb_epf_dev *ndev)
> +{
> + switch (ndev->dma.type) {
> + case EPF_NTB_DMA_TYPE_DW_EDMA:
> + ntb_epf_dw_edma_deinit(ndev);
> + break;
> + }
> +}
> +
> static int ntb_epf_mw_to_bar(struct ntb_epf_dev *ndev, int idx)
> {
> struct device *dev = ndev->dev;
> @@ -347,16 +581,20 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
> {
> struct pci_dev *pdev = ndev->ntb.pdev;
> struct device *dev = ndev->dev;
> + unsigned int dma_max = ndev->dma.nr_irqs;
> + unsigned int dma_min;
> u32 argument = EPF_NTB_MSIX_ENABLE;
> + unsigned int ntb_irqs;
> int irq;
> int ret;
> int i;
>
> - irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max, PCI_IRQ_MSIX);
> + irq = pci_alloc_irq_vectors(pdev, msi_min,
> + msi_max + dma_max, PCI_IRQ_MSIX);
> if (irq < 0) {
> dev_dbg(dev, "Failed to get MSIX interrupts\n");
> - irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max,
> - PCI_IRQ_MSI);
> + irq = pci_alloc_irq_vectors(pdev, msi_min,
> + msi_max + dma_max, PCI_IRQ_MSI);
> if (irq < 0) {
> dev_err(dev, "Failed to get MSI interrupts\n");
> return irq;
> @@ -364,8 +602,17 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
> argument &= ~EPF_NTB_MSIX_ENABLE;
> }
>
> - ndev->db_count = irq - 1;
> - for (i = 0; i < irq; i++) {
> + dma_min = dma_max && irq > msi_min;
> + /* Reserve one shared DMA IRQ when available, then give the rest to NTB. */
> + ntb_irqs = min_t(unsigned int, irq - dma_min, msi_max);
> + ndev->dma.nr_irqs = irq - ntb_irqs;
> + if (dma_max && !ndev->dma.nr_irqs) {
> + dev_warn(dev,
> + "No IRQ available for endpoint DMA, continuing without DMA\n");
> + ndev->dma.type = 0;
> + }
> + ndev->db_count = ntb_irqs - 1;
> + for (i = 0; i < ntb_irqs; i++) {
> ndev->irq_ctx[i].ndev = ndev;
> ndev->irq_ctx[i].irq_no = i;
> ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr,
> @@ -377,7 +624,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
> }
>
> ret = ntb_epf_send_command(ndev, EPF_NTB_COMMAND_CONFIGURE_DOORBELL,
> - argument | irq);
> + argument | ntb_irqs);
> if (ret) {
> dev_err(dev, "Failed to configure doorbell\n");
> goto err_free_irq;
> @@ -610,6 +857,11 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
> dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count);
> return -EINVAL;
> }
> + ret = ntb_epf_dma_parse(ndev);
> + if (ret) {
> + dev_err(dev, "Invalid endpoint DMA layout\n");
> + return ret;
> + }
>
> /* One Link interrupt and rest doorbell interrupt */
> ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
> @@ -760,6 +1012,12 @@ static int ntb_epf_pci_probe(struct pci_dev *pdev,
> goto err_init_dev;
> }
>
> + ret = ntb_epf_dma_init(ndev);
> + if (ret) {
> + dev_err(dev, "Failed to initialize endpoint DMA\n");
> + goto err_dma_init;
> + }
> +
> ret = ntb_register_device(&ndev->ntb);
> if (ret) {
> dev_err(dev, "Failed to register NTB device\n");
> @@ -769,6 +1027,8 @@ static int ntb_epf_pci_probe(struct pci_dev *pdev,
> return 0;
>
> err_register_dev:
> + ntb_epf_dma_deinit(ndev);
> +err_dma_init:
> ntb_epf_cleanup_isr(ndev);
>
> err_init_dev:
> @@ -782,6 +1042,7 @@ static void ntb_epf_pci_remove(struct pci_dev *pdev)
> struct ntb_epf_dev *ndev = pci_get_drvdata(pdev);
>
> ntb_unregister_device(&ndev->ntb);
> + ntb_epf_dma_deinit(ndev);
> ntb_epf_cleanup_isr(ndev);
> ntb_epf_deinit_pci(ndev);
> }
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v6 7/8] PCI/NTB: endpoint: Allow DMA and MW to share a BAR
2026-10-20 5:49 ` [PATCH v6 7/8] PCI/NTB: endpoint: Allow DMA and MW to share a BAR Koichiro Den
@ 2026-10-20 17:11 ` Frank Li
0 siblings, 0 replies; 13+ messages in thread
From: Frank Li @ 2026-10-20 17:11 UTC (permalink / raw)
To: Koichiro Den
Cc: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel, Bjorn Helgaas, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Jingoo Han, Lorenzo Pieralisi, Rob Herring,
Jerome Brunet, linux-pci, linux-doc, linux-kernel, ntb
On Tue, Oct 20, 2026 at 02:49:47PM +0900, Koichiro Den wrote:
> Some endpoint configurations have no spare BAR for DMA resources. Allow
> dma_bar to select an MW BAR and place the DMA ranges after that MW.
>
> DMA submaps have fixed BAR offsets, so a shared MW translation must
> cover its configured size. Report that size as the translation alignment
> and require it to be a power of two.
>
> Limit the host-visible MW size to exclude the DMA ranges. Older versions
> of ntb_hw_epf treat the whole BAR as an MW.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> Changes since v5:
> - Include the host-side MW boundary handling and place this patch
> after DMA discovery.
>
> drivers/ntb/hw/epf/ntb_hw_epf.c | 43 ++++-
> drivers/pci/endpoint/functions/pci-epf-vntb.c | 147 +++++++++++++-----
what's happen if miss match? host and endpoint may not run the same version
kernel.
ntb should be independ on endpoint part. So suggest split patch, so
ntb can go though ntb subsystem.
Frank
> 2 files changed, 148 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c
> index e0016128b95c..975ea424de6a 100644
> --- a/drivers/ntb/hw/epf/ntb_hw_epf.c
> +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c
> @@ -382,6 +382,23 @@ static int ntb_epf_mw_to_bar(struct ntb_epf_dev *ndev, int idx)
> return ndev->barno_map[BAR_MW1 + idx];
> }
>
> +static resource_size_t ntb_epf_mw_offset(struct ntb_epf_dev *ndev, int idx)
> +{
> + return !idx ? readl(ndev->ctrl_reg + NTB_EPF_CTRL(mw1_offset)) : 0;
> +}
> +
> +static resource_size_t ntb_epf_mw_size(struct ntb_epf_dev *ndev, int bar)
> +{
> + resource_size_t end;
> +
> + end = pci_resource_len(ndev->ntb.pdev, bar);
> + /* The DMA submap offset marks the end of an MW sharing this BAR. */
> + if (ndev->dma.submap.size && ndev->dma.submap.bar == bar)
> + end = min(end, ndev->dma.submap.offset);
> +
> + return end;
> +}
> +
> static int ntb_epf_mw_count(struct ntb_dev *ntb, int pidx)
> {
> struct ntb_epf_dev *ndev = ntb_ndev(ntb);
> @@ -420,7 +437,7 @@ static int ntb_epf_mw_get_align(struct ntb_dev *ntb, int pidx, int idx,
> *size_align = 1;
>
> if (size_max)
> - *size_max = pci_resource_len(ndev->ntb.pdev, bar);
> + *size_max = ntb_epf_mw_size(ndev, bar);
>
> return 0;
> }
> @@ -707,7 +724,7 @@ static int ntb_epf_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx,
> if (bar < 0)
> return bar;
>
> - mw_size = pci_resource_len(ntb->pdev, bar);
> + mw_size = ntb_epf_mw_size(ndev, bar);
>
> if (size > mw_size) {
> dev_err(dev, "Size:%pa is greater than the MW size %pa\n",
> @@ -739,21 +756,20 @@ static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx,
> phys_addr_t *base, resource_size_t *size)
> {
> struct ntb_epf_dev *ndev = ntb_ndev(ntb);
> - u32 offset = 0;
> + resource_size_t offset;
> int bar;
>
> - if (idx == 0)
> - offset = readl(ndev->ctrl_reg + NTB_EPF_CTRL(mw1_offset));
> -
> bar = ntb_epf_mw_to_bar(ndev, idx);
> if (bar < 0)
> return bar;
>
> + offset = ntb_epf_mw_offset(ndev, idx);
> +
> if (base)
> *base = pci_resource_start(ndev->ntb.pdev, bar) + offset;
>
> if (size)
> - *size = pci_resource_len(ndev->ntb.pdev, bar) - offset;
> + *size = ntb_epf_mw_size(ndev, bar) - offset;
>
> return 0;
> }
> @@ -851,6 +867,7 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
> {
> struct device *dev = ndev->dev;
> int ret;
> + int i;
>
> ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_CTRL(num_mws));
> if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) {
> @@ -862,6 +879,18 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
> dev_err(dev, "Invalid endpoint DMA layout\n");
> return ret;
> }
> + if (ndev->dma.submap.size) {
> + for (i = 0; i < ndev->mw_count; i++) {
> + int bar = ntb_epf_mw_to_bar(ndev, i);
> +
> + if (bar == ndev->dma.submap.bar &&
> + ntb_epf_mw_size(ndev, bar) <=
> + ntb_epf_mw_offset(ndev, i)) {
> + dev_err(dev, "Invalid DMA/MW boundary\n");
> + return -EINVAL;
> + }
> + }
> + }
>
> /* One Link interrupt and rest doorbell interrupt */
> ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
> diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> index ac83af54122f..89f828234ca7 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> @@ -84,12 +84,13 @@ enum epf_irq_slot {
> #define MIN_DB_COUNT (EPF_IRQ_DB_START + 1)
>
> struct epf_ntb_dma {
> + struct mutex lock; /* Serialize DMA submap and MW updates */
> struct epf_ntb_dma_ctrl ctrl;
> struct dma_chan *dchan[EDMA_MAX_RD_CH];
> void *bar_scratch;
> dma_addr_t bar_scratch_phys;
> size_t bar_scratch_size;
> - struct pci_epf_bar_submap submap[EDMA_MAX_RD_CH + 2];
> + struct pci_epf_bar_submap submap[EDMA_MAX_RD_CH + 3];
> struct pci_epf_bar_submap *reg_submap;
> unsigned int num_submap;
> u16 rd_ch_cnt;
> @@ -229,6 +230,22 @@ static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
> return false;
> }
>
> +static u64 epf_ntb_dma_bar_offset(struct epf_ntb *ntb, enum pci_barno barno)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < ntb->num_mws; i++)
> + if (ntb->epf_ntb_bar[BAR_MW1 + i] == barno)
> + return ntb->mws_size[i];
> +
> + return 0;
> +}
> +
> +static bool epf_ntb_dma_shares_bar(struct epf_ntb *ntb, enum pci_barno barno)
> +{
> + return ntb->dma && ntb->epf_ntb_bar[BAR_DMA] == barno;
> +}
> +
> struct epf_ntb_dma_filter {
> struct device *dev;
> int chan_id;
> @@ -353,6 +370,7 @@ static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
> unsigned int i;
> size_t align;
> u32 next = 0;
> + u64 offset;
> int ret;
>
> if (ctrl->u.dma_ctrl.reg_layout_data != EDMA_MF_EDMA_UNROLL)
> @@ -371,10 +389,31 @@ static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
> if (!is_power_of_2(align))
> return -EINVAL;
>
> - if (epf_ntb_is_bar_used(ntb, barno) ||
> - pci_epc_get_next_free_bar(features, barno) != barno)
> + if (barno == ntb->epf_ntb_bar[BAR_CONFIG] ||
> + barno == ntb->epf_ntb_bar[BAR_DB])
> return -EINVAL;
>
> + offset = epf_ntb_dma_bar_offset(ntb, barno);
> + if (!offset &&
> + (epf_ntb_is_bar_used(ntb, barno) ||
> + pci_epc_get_next_free_bar(features, barno) != barno))
> + return -EINVAL;
> + if (offset > U32_MAX)
> + return -EOVERFLOW;
> + if (offset && !is_power_of_2(offset))
> + return -EINVAL;
> + dma->ctrl.submap.offset = offset;
> + next = offset;
> + if (next) {
> + /*
> + * submap[0] covers the MW prefix. Keep its temporary
> + * target at address 0 until the MW translation is
> + * installed.
> + */
> + dma->submap[0].size = next;
> + dma->num_submap = 1;
> + }
> +
> dma->ctrl.magic = EPF_NTB_DMA_MAGIC;
> dma->ctrl.revision = EPF_NTB_DMA_REVISION;
> dma->ctrl.type = EPF_NTB_DMA_TYPE_DW_EDMA;
> @@ -408,7 +447,7 @@ static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
> chan->desc_addr_lo = lower_32_bits(dma_addr);
> chan->desc_addr_hi = upper_32_bits(dma_addr);
> }
> - dma->ctrl.submap.size = next;
> + dma->ctrl.submap.size = next - dma->ctrl.submap.offset;
>
> dma_dev = ntb->epf->epc->dev.parent;
> for (i = 0; i < dma->rd_ch_cnt; i++) {
> @@ -481,6 +520,8 @@ static int epf_ntb_dma_collect(struct epf_ntb *ntb)
> if (!dma)
> return -ENOMEM;
>
> + mutex_init(&dma->lock);
> +
> switch (ctrl->u.dma_ctrl.reg_layout) {
> case PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA:
> ret = epf_ntb_dw_edma_collect(ntb, dma, ctrl, resources, count);
> @@ -551,7 +592,7 @@ static int epf_ntb_dma_bar_init(struct epf_ntb *ntb)
> return -EOPNOTSUPP;
>
> barno = ntb->epf_ntb_bar[BAR_DMA];
> - mapped_size = dma->ctrl.submap.size;
> + mapped_size = dma->ctrl.submap.offset + dma->ctrl.submap.size;
> /*
> * Submaps cannot be installed until the host assigns the BAR address.
> * Use address 0 for the temporary BAR Match Mode mapping, as is done
> @@ -607,21 +648,25 @@ static void epf_ntb_dma_bar_clear(struct epf_ntb *ntb)
> memset(bar, 0, sizeof(*bar));
> }
>
> -static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
> +static int epf_ntb_dma_set_bar_locked(struct epf_ntb *ntb,
> + const dma_addr_t *mw_addr)
> {
> struct pci_epf_bar_submap *old_submap;
> struct epf_ntb_dma *dma = ntb->dma;
> unsigned int old_num_submap;
> struct pci_epf_bar *bar;
> + dma_addr_t old_mw_addr;
> int restore, ret;
> -
> - if (!dma)
> - return 0;
> + lockdep_assert_held(&dma->lock);
>
> bar = &ntb->epf->bar[ntb->epf_ntb_bar[BAR_DMA]];
>
> old_submap = bar->submap;
> old_num_submap = bar->num_submap;
> + if (mw_addr) {
> + old_mw_addr = dma->submap[0].phys_addr;
> + dma->submap[0].phys_addr = *mw_addr;
> + }
> bar->submap = dma->submap;
> bar->num_submap = dma->num_submap;
>
> @@ -631,6 +676,8 @@ static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
> return 0;
>
> /* A failed dynamic update may have already removed the old mapping. */
> + if (mw_addr)
> + dma->submap[0].phys_addr = old_mw_addr;
> bar->submap = old_submap;
> bar->num_submap = old_num_submap;
> restore = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
> @@ -642,6 +689,18 @@ static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
> return ret;
> }
>
> +static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
> +{
> + struct epf_ntb_dma *dma = ntb->dma;
> +
> + if (!dma)
> + return 0;
> +
> + guard(mutex)(&dma->lock);
> +
> + return epf_ntb_dma_set_bar_locked(ntb, NULL);
> +}
> +
> /**
> * epf_ntb_configure_mw() - Configure the Outbound Address Space for VHOST
> * to access the memory window of HOST
> @@ -1269,6 +1328,7 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb)
> */
> static int epf_ntb_mw_bar_init(struct epf_ntb *ntb)
> {
> + bool shared;
> int ret = 0;
> int i;
> u64 size;
> @@ -1278,22 +1338,25 @@ static int epf_ntb_mw_bar_init(struct epf_ntb *ntb)
> for (i = 0; i < ntb->num_mws; i++) {
> size = ntb->mws_size[i];
> barno = ntb->epf_ntb_bar[BAR_MW1 + i];
> -
> - ntb->epf->bar[barno].barno = barno;
> - ntb->epf->bar[barno].size = size;
> - ntb->epf->bar[barno].addr = NULL;
> - ntb->epf->bar[barno].phys_addr = 0;
> - ntb->epf->bar[barno].flags |= upper_32_bits(size) ?
> - PCI_BASE_ADDRESS_MEM_TYPE_64 :
> - PCI_BASE_ADDRESS_MEM_TYPE_32;
> -
> - ret = pci_epc_set_bar(ntb->epf->epc,
> - ntb->epf->func_no,
> - ntb->epf->vfunc_no,
> - &ntb->epf->bar[barno]);
> - if (ret) {
> - dev_err(dev, "MW set failed\n");
> - goto err_alloc_mem;
> + shared = epf_ntb_dma_shares_bar(ntb, barno);
> +
> + if (!shared) {
> + ntb->epf->bar[barno].barno = barno;
> + ntb->epf->bar[barno].size = size;
> + ntb->epf->bar[barno].addr = NULL;
> + ntb->epf->bar[barno].phys_addr = 0;
> + ntb->epf->bar[barno].flags |= upper_32_bits(size) ?
> + PCI_BASE_ADDRESS_MEM_TYPE_64 :
> + PCI_BASE_ADDRESS_MEM_TYPE_32;
> +
> + ret = pci_epc_set_bar(ntb->epf->epc,
> + ntb->epf->func_no,
> + ntb->epf->vfunc_no,
> + &ntb->epf->bar[barno]);
> + if (ret) {
> + dev_err(dev, "MW set failed\n");
> + goto err_alloc_mem;
> + }
> }
>
> /* Allocate EPC outbound memory windows to vpci vntb device */
> @@ -1310,10 +1373,11 @@ static int epf_ntb_mw_bar_init(struct epf_ntb *ntb)
> return ret;
>
> err_set_bar:
> - pci_epc_clear_bar(ntb->epf->epc,
> - ntb->epf->func_no,
> - ntb->epf->vfunc_no,
> - &ntb->epf->bar[barno]);
> + if (!shared)
> + pci_epc_clear_bar(ntb->epf->epc,
> + ntb->epf->func_no,
> + ntb->epf->vfunc_no,
> + &ntb->epf->bar[barno]);
> err_alloc_mem:
> epf_ntb_mw_bar_clear(ntb, i);
> return ret;
> @@ -1331,10 +1395,11 @@ static void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws)
>
> for (i = 0; i < num_mws; i++) {
> barno = ntb->epf_ntb_bar[BAR_MW1 + i];
> - pci_epc_clear_bar(ntb->epf->epc,
> - ntb->epf->func_no,
> - ntb->epf->vfunc_no,
> - &ntb->epf->bar[barno]);
> + if (!epf_ntb_dma_shares_bar(ntb, barno))
> + pci_epc_clear_bar(ntb->epf->epc,
> + ntb->epf->func_no,
> + ntb->epf->vfunc_no,
> + &ntb->epf->bar[barno]);
>
> pci_epc_mem_free_addr(ntb->epf->epc,
> ntb->vpci_mw_phy[i],
> @@ -1936,6 +2001,16 @@ static int vntb_epf_mw_set_trans(struct ntb_dev *ndev, int pidx, int idx,
> dev = &ndev->dev;
> barno = ntb->epf_ntb_bar[BAR_MW1 + idx];
> epf_bar = &ntb->epf->bar[barno];
> + if (epf_ntb_dma_shares_bar(ntb, barno)) {
> + /* DMA submaps start after the configured MW size. */
> + if (size != ntb->mws_size[idx])
> + return -EINVAL;
> +
> + guard(mutex)(&ntb->dma->lock);
> +
> + return epf_ntb_dma_set_bar_locked(ntb, &addr);
> + }
> +
> epf_bar->phys_addr = addr;
> epf_bar->barno = barno;
> epf_bar->size = size;
> @@ -2134,15 +2209,17 @@ static int vntb_epf_mw_get_align(struct ntb_dev *ndev, int pidx, int idx,
> resource_size_t *size_max)
> {
> struct epf_ntb *ntb = ntb_ndev(ndev);
> + enum pci_barno barno = ntb->epf_ntb_bar[BAR_MW1 + idx];
> + u64 size = ntb->mws_size[idx];
>
> if (addr_align)
> *addr_align = SZ_4K;
>
> if (size_align)
> - *size_align = 1;
> + *size_align = epf_ntb_dma_shares_bar(ntb, barno) ? size : 1;
>
> if (size_max)
> - *size_max = ntb->mws_size[idx];
> + *size_max = size;
>
> return 0;
> }
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v6 8/8] Documentation: PCI: endpoint: Document vNTB DMA export
2026-10-20 5:49 ` [PATCH v6 8/8] Documentation: PCI: endpoint: Document vNTB DMA export Koichiro Den
@ 2026-10-20 18:49 ` Frank Li
0 siblings, 0 replies; 13+ messages in thread
From: Frank Li @ 2026-10-20 18:49 UTC (permalink / raw)
To: Koichiro Den
Cc: Frank Li, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Jon Mason, Dave Jiang, Allen Hubbe,
Niklas Cassel, Bjorn Helgaas, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Jingoo Han, Lorenzo Pieralisi, Rob Herring,
Jerome Brunet, linux-pci, linux-doc, linux-kernel, ntb
On Tue, Oct 20, 2026 at 02:49:48PM +0900, Koichiro Den wrote:
> Document the dma_bar attribute and the requirements for exporting
> endpoint DMA channels. Also document how the selected BAR may be shared
> with an MW.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Changes since v5:
> - No changes.
>
> Documentation/PCI/endpoint/pci-vntb-howto.rst | 31 +++++++++++++++++--
> 1 file changed, 28 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/PCI/endpoint/pci-vntb-howto.rst b/Documentation/PCI/endpoint/pci-vntb-howto.rst
> index 3679f5c30254..7ca4e1ff095c 100644
> --- a/Documentation/PCI/endpoint/pci-vntb-howto.rst
> +++ b/Documentation/PCI/endpoint/pci-vntb-howto.rst
> @@ -90,9 +90,9 @@ of the function device and is populated with the following NTB specific
> attributes that can be configured by the user::
>
> # ls functions/pci_epf_vntb/func1/pci_epf_vntb.0/
> - ctrl_bar db_count mw1_bar mw2_bar mw3_bar mw4_bar spad_count
> - db_bar mw1 mw2 mw3 mw4 num_mws vbus_number
> - vntb_vid vntb_pid
> + ctrl_bar dma_bar mw2 mw3_bar num_mws vntb_pid
> + db_bar mw1 mw2_bar mw4 spad_count vntb_vid
> + db_count mw1_bar mw3 mw4_bar vbus_number
>
> A sample configuration for NTB function is given below::
>
> @@ -105,6 +105,31 @@ By default, each construct is assigned a BAR, as needed and in order.
> Should a specific BAR setup be required by the platform, BAR may be assigned
> to each construct using the related ``XYZ_bar`` entry.
>
> +To export DMA channels, set ``dma_bar`` before binding the function.
> +For example::
> +
> + # echo 5 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/dma_bar
> +
> +This currently supports the unrolled DesignWare eDMA layout. The EPC must
> +support subrange and dynamic inbound mappings.
> +
> +``dma_bar`` cannot select the control or doorbell BAR.
> +Any rounded-up BAR tail is scratch-backed.
> +
> +Leaving ``dma_bar`` unassigned disables DMA export.
> +With a separate DMA BAR, an older ``ntb_hw_epf`` peer can ignore the extension
> +and continue without DMA. A peer that understands the extension also continues
> +without DMA if the advertised type is unavailable or no DMA IRQ can be
> +allocated.
> +
> +If no spare BAR is available, ``dma_bar`` can select the BAR of one MW instead.
> +DMA resources then follow that MW. In this configuration:
> +
> +* The shared MW size must be a power of two and its translation must cover the
> + full configured size.
> +* An updated peer is required because older peers treat the whole BAR as an
> + MW.
> +
> A sample configuration for virtual NTB driver for virtual PCI bus::
>
> # echo 0x1957 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_vid
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-10-20 18:50 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-20 5:49 [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
2026-10-20 5:49 ` [PATCH v6 1/8] PCI: endpoint: Add DMA auxiliary resource metadata Koichiro Den
2026-10-20 5:49 ` [PATCH v6 2/8] PCI: dwc: Expose endpoint DMA resources Koichiro Den
2026-10-20 5:49 ` [PATCH v6 3/8] PCI: endpoint: pci-epf-vntb: Move epf_ntb_is_bar_used() up Koichiro Den
2026-10-20 5:49 ` [PATCH v6 4/8] PCI: endpoint: Add pci-epf-ntb.h for the shared NTB control layout Koichiro Den
2026-10-20 5:49 ` [PATCH v6 5/8] PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels Koichiro Den
2026-10-20 17:00 ` Frank Li
2026-10-20 5:49 ` [PATCH v6 6/8] NTB: ntb_hw_epf: Discover vNTB-embedded DMA Koichiro Den
2026-10-20 17:03 ` Frank Li
2026-10-20 5:49 ` [PATCH v6 7/8] PCI/NTB: endpoint: Allow DMA and MW to share a BAR Koichiro Den
2026-10-20 17:11 ` Frank Li
2026-10-20 5:49 ` [PATCH v6 8/8] Documentation: PCI: endpoint: Document vNTB DMA export Koichiro Den
2026-10-20 18:49 ` Frank Li
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.