[PATCH v4 5/7] PCI: endpoint: pci-epf-vntb: Allow DMA and MW to share a BAR
From: Koichiro Den
Date: Thu Sep 03 2026 - 05:14:49 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 the configured MW size as its size 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 v4:
- Keep the shared BAR mapping installed until EPF unbind. (Sashiko)
- Advertise and validate the full-size shared MW translation.
- Reject dma_bar values that overlap the control or doorbell BAR.
drivers/pci/endpoint/functions/pci-epf-vntb.c | 162 +++++++++++++-----
1 file changed, 117 insertions(+), 45 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index ad34530642fc..e800a317f059 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,18 +275,23 @@ static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
return false;
}
-static int epf_ntb_dma_validate_bar(struct epf_ntb *ntb,
- const struct pci_epc_features *features)
+static u64 epf_ntb_dma_bar_offset(struct epf_ntb *ntb, enum pci_barno barno)
{
- enum pci_barno barno = ntb->epf_ntb_bar[BAR_DMA];
+ unsigned int i;
- if (epf_ntb_is_bar_used(ntb, barno) ||
- pci_epc_get_next_free_bar(features, barno) != barno)
- return -EINVAL;
+ 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->dma->num_submap &&
+ ntb->epf_ntb_bar[BAR_DMA] == barno;
+}
+
struct epf_ntb_dma_filter {
struct device *dev;
int chan_id;
@@ -437,6 +444,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)
@@ -471,9 +479,28 @@ static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
if (!features->subrange_mapping ||
!features->dynamic_inbound_mapping)
return -EOPNOTSUPP;
- ret = epf_ntb_dma_validate_bar(ntb, features);
- if (ret)
- return ret;
+ 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;
+ 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;
@@ -502,7 +529,7 @@ static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
}
if (dma->num_submap) {
dma->ctrl.submap.bar = barno;
- dma->ctrl.submap.size = next;
+ dma->ctrl.submap.size = next - dma->ctrl.submap.offset;
}
dma_dev = ntb->epf->epc->dev.parent;
@@ -572,6 +599,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);
@@ -625,24 +654,28 @@ static void epf_ntb_dma_release(struct epf_ntb *ntb, bool quiesce)
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;
- struct pci_epf_bar *bar;
unsigned int old_num_submap;
+ struct pci_epf_bar *bar;
+ dma_addr_t old_mw_addr;
int restore, ret;
-
- if (!dma || !dma->num_submap)
- return 0;
+ lockdep_assert_held(&dma->lock);
bar = &ntb->epf->bar[ntb->epf_ntb_bar[BAR_DMA]];
- if (bar->submap == dma->submap &&
+ if (!mw_addr && bar->submap == dma->submap &&
bar->num_submap == dma->num_submap)
return 0;
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;
@@ -652,17 +685,31 @@ 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,
ntb->epf->vfunc_no, bar);
if (restore)
dev_warn(&ntb->epf->dev,
- "failed to restore DMA BAR mapping: %d\n", restore);
+ "failed to restore DMA/MW BAR mapping: %d\n", restore);
return ret;
}
+static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
+{
+ struct epf_ntb_dma *dma = ntb->dma;
+
+ if (!dma || !dma->num_submap)
+ 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
@@ -1205,6 +1252,7 @@ static int epf_ntb_dma_bar_init(struct epf_ntb *ntb)
struct pci_epf_bar *bar;
enum pci_barno barno;
size_t backing_size;
+ unsigned int i;
u32 mapped_size;
int ret;
@@ -1214,7 +1262,13 @@ 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;
+ for (i = 0; i < ntb->num_mws; i++) {
+ if (ntb->epf_ntb_bar[BAR_MW1 + i] == barno &&
+ !is_power_of_2(ntb->mws_size[i]))
+ return -EINVAL;
+ }
+
+ 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
@@ -1315,6 +1369,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;
@@ -1324,22 +1379,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 */
@@ -1356,10 +1414,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;
@@ -1377,10 +1436,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],
@@ -1982,6 +2042,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;
@@ -2159,15 +2229,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