[PATCH v5 5/7] PCI: endpoint: pci-epf-vntb: Allow DMA and MW to share a BAR
From: Koichiro Den
Date: Fri Oct 09 2026 - 01:01:59 EST
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.
BAR sharing requires ntb_hw_epf to parse the DMA extension, since older
versions treat the whole BAR as an MW.
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v5:
- Simplify the implementation by:
* dropping redundant submap count checks
* checking the shared MW size during collection to avoid a second MW
scan.
drivers/pci/endpoint/functions/pci-epf-vntb.c | 148 +++++++++++++-----
1 file changed, 113 insertions(+), 35 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index 9b376ab87225..7ab3b94c6aa0 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -44,6 +44,7 @@
#include <linux/dmaengine.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/overflow.h>
#include <linux/slab.h>
@@ -167,12 +168,13 @@ struct epf_ntb_ctrl {
} __packed;
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;
@@ -273,6 +275,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;
@@ -397,6 +415,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)
@@ -415,9 +434,30 @@ 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;
@@ -452,7 +492,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++) {
@@ -525,6 +565,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);
@@ -578,21 +620,25 @@ static void epf_ntb_dma_release(struct epf_ntb *ntb)
ntb->dma = NULL;
}
-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;
@@ -602,6 +648,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,
@@ -613,6 +661,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
@@ -1164,7 +1224,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
@@ -1258,6 +1318,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;
@@ -1267,22 +1328,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 */
@@ -1299,10 +1363,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;
@@ -1320,10 +1385,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],
@@ -1925,6 +1991,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;
@@ -2103,15 +2179,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.51.0