[PATCH 03/11] PCI: endpoint: pci-epf-vntb: Add packed MW layout handling

Koichiro Den <[email protected]> Tue, 4 Aug 2026 03:04:30 +0900
Newsgroups dev.linux.lists.ntb,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci
Message-ID <[email protected]>
Teach pci-epf-vntb to build a packed layout and advertise its logical
MWs as one version 1 translation group. Non-packed configurations keep
the version 0 layout and one MW per BAR.

Signed-off-by: Koichiro Den <[email protected]>
---
 drivers/pci/endpoint/functions/pci-epf-vntb.c | 255 +++++++++++++++---
 1 file changed, 219 insertions(+), 36 deletions(-)

diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index c3caec927d74..f22459e5a8c6 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -68,6 +68,9 @@ static struct workqueue_struct *kpcintb_workqueue;
 #define DB_COUNT_MASK			GENMASK(15, 0)
 #define MSIX_ENABLE			BIT(16)
 #define MAX_MW				4
+#define EPF_NTB_MAX_MW			16
+#define EPF_NTB_CTRL_V0			0
+#define EPF_NTB_CTRL_V1			1
 
 /* Limit per-work execution to avoid monopolizing kworker on doorbell storms. */
 #define VNTB_PEER_DB_WORK_BUDGET	5
@@ -119,7 +122,7 @@ struct epf_ntb_ctrl {
 	u32 argument;
 	u16 command_status;
 	u16 link_status;
-	u32 topology;
+	u32 version;
 	u64 addr;
 	u64 size;
 	u32 num_mws;
@@ -129,17 +132,27 @@ struct epf_ntb_ctrl {
 	u32 db_entry_size;
 	u32 db_data[MAX_DB_COUNT];
 	u32 db_offset[MAX_DB_COUNT];
+	u32 mw_bar;
+	u32 mw_group_size;
 } __packed;
 
+struct epf_ntb_mw_layout {
+	enum pci_barno barno;
+	u64 offset;
+	u64 size;
+};
+
 struct epf_ntb {
 	struct ntb_dev ntb;
 	struct pci_epf *epf;
 	struct config_group group;
 
 	u32 num_mws;
+	u32 packed_mws;
 	u32 db_count;
 	u32 spad_count;
 	u64 mws_size[MAX_MW];
+	struct epf_ntb_mw_layout mw_layout[EPF_NTB_MAX_MW];
 	atomic64_t db;
 	atomic64_t peer_db_pending;
 	struct work_struct peer_db_work;
@@ -162,8 +175,8 @@ struct epf_ntb {
 
 	u32 *epf_db;
 
-	phys_addr_t vpci_mw_phy[MAX_MW];
-	void __iomem *vpci_mw_addr[MAX_MW];
+	phys_addr_t vpci_mw_phy[EPF_NTB_MAX_MW];
+	void __iomem *vpci_mw_addr[EPF_NTB_MAX_MW];
 
 	struct delayed_work cmd_handler;
 };
@@ -199,11 +212,25 @@ static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up)
 	return 0;
 }
 
+static int epf_ntb_get_mw_group(struct epf_ntb *ntb, unsigned int mw,
+				unsigned int *first, unsigned int *count)
+{
+	if (mw >= ntb->num_mws)
+		return -EINVAL;
+
+	if (first)
+		*first = ntb->packed_mws ? 0 : mw;
+	if (count)
+		*count = ntb->packed_mws ? ntb->num_mws : 1;
+
+	return 0;
+}
+
 /**
  * epf_ntb_configure_mw() - Configure the Outbound Address Space for VHOST
  *   to access the memory window of HOST
  * @ntb: NTB device that facilitates communication between HOST and VHOST
- * @mw: Index of the memory window (either 0, 1, 2 or 3)
+ * @mw: Index of the memory window
  *
  *                          EP Outbound Window
  * +--------+              +-----------+
@@ -227,13 +254,23 @@ static int epf_ntb_configure_mw(struct epf_ntb *ntb, u32 mw)
 {
 	phys_addr_t phys_addr;
 	u8 func_no, vfunc_no;
+	unsigned int count;
 	u64 addr, size;
-	int ret = 0;
+	int ret;
+
+	ret = epf_ntb_get_mw_group(ntb, mw, NULL, &count);
+	if (ret)
+		return ret;
+	if (count > 1)
+		return -EOPNOTSUPP;
 
 	phys_addr = ntb->vpci_mw_phy[mw];
 	addr = ntb->reg->addr;
 	size = ntb->reg->size;
 
+	if (!size || size > ntb->mw_layout[mw].size)
+		return -EINVAL;
+
 	func_no = ntb->epf->func_no;
 	vfunc_no = ntb->epf->vfunc_no;
 
@@ -247,17 +284,30 @@ static int epf_ntb_configure_mw(struct epf_ntb *ntb, u32 mw)
 /**
  * epf_ntb_teardown_mw() - Teardown the configured OB ATU
  * @ntb: NTB device that facilitates communication between HOST and VHOST
- * @mw: Index of the memory window (either 0, 1, 2 or 3)
+ * @mw: Index of the memory window
  *
  * Teardown the configured OB ATU configured in epf_ntb_configure_mw() using
  * pci_epc_unmap_addr()
+ *
+ * Returns: Zero for success, or an error code in case of failure
  */
-static void epf_ntb_teardown_mw(struct epf_ntb *ntb, u32 mw)
+static int epf_ntb_teardown_mw(struct epf_ntb *ntb, u32 mw)
 {
+	unsigned int count;
+	int ret;
+
+	ret = epf_ntb_get_mw_group(ntb, mw, NULL, &count);
+	if (ret)
+		return ret;
+	if (count > 1)
+		return -EOPNOTSUPP;
+
 	pci_epc_unmap_addr(ntb->epf->epc,
 			   ntb->epf->func_no,
 			   ntb->epf->vfunc_no,
 			   ntb->vpci_mw_phy[mw]);
+
+	return 0;
 }
 
 /**
@@ -316,8 +366,8 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
 			ctrl->command_status = COMMAND_STATUS_OK;
 		break;
 	case COMMAND_TEARDOWN_MW:
-		epf_ntb_teardown_mw(ntb, argument);
-		ctrl->command_status = COMMAND_STATUS_OK;
+		ret = epf_ntb_teardown_mw(ntb, argument);
+		ctrl->command_status = ret ? COMMAND_STATUS_ERROR : COMMAND_STATUS_OK;
 		break;
 	case COMMAND_LINK_UP:
 		ntb->linkup = true;
@@ -434,6 +484,78 @@ static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
 	pci_epf_free_space(ntb->epf, ntb->reg, barno, 0);
 }
 
+static int epf_ntb_build_mw_layout(struct epf_ntb *ntb)
+{
+	struct device *dev = &ntb->epf->dev;
+	u64 size;
+	int i;
+
+	if (ntb->packed_mws) {
+		enum pci_barno barno = ntb->epf_ntb_bar[BAR_MW1];
+
+		/*
+		 * A packed group requires contiguous backing for the entire BAR.
+		 * Per-MW size limits such as max_mw_size are not supported.
+		 */
+		if (ntb->packed_mws != ntb->num_mws) {
+			dev_err(dev, "packed_mws must match num_mws\n");
+			return -EINVAL;
+		}
+
+		if (barno < 0) {
+			dev_err(dev, "packed MW has no BAR\n");
+			return -EINVAL;
+		}
+
+		if (!ntb->mws_size[0] ||
+		    ntb->mws_size[0] > U32_MAX ||
+		    !is_power_of_2(ntb->mws_size[0]) ||
+		    ntb->mws_size[0] % ntb->packed_mws) {
+			dev_err(dev, "invalid packed MW size\n");
+			return -EINVAL;
+		}
+
+		size = ntb->mws_size[0] / ntb->packed_mws;
+		if (!IS_ALIGNED(size, SZ_4K)) {
+			dev_err(dev, "invalid packed MW member size\n");
+			return -EINVAL;
+		}
+
+		for (i = 0; i < ntb->num_mws; i++) {
+			ntb->mw_layout[i].barno = barno;
+			ntb->mw_layout[i].offset = i * size;
+			ntb->mw_layout[i].size = size;
+		}
+
+		return 0;
+	}
+
+	if (ntb->num_mws > MAX_MW) {
+		dev_err(dev, "num_mws=%u requires packed_mws\n", ntb->num_mws);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ntb->num_mws; i++) {
+		enum pci_barno barno = ntb->epf_ntb_bar[BAR_MW1 + i];
+
+		if (barno < 0) {
+			dev_err(dev, "MW%d has no BAR\n", i + 1);
+			return -EINVAL;
+		}
+
+		if (!ntb->mws_size[i]) {
+			dev_err(dev, "MW%d has no size\n", i + 1);
+			return -EINVAL;
+		}
+
+		ntb->mw_layout[i].barno = barno;
+		ntb->mw_layout[i].offset = 0;
+		ntb->mw_layout[i].size = ntb->mws_size[i];
+	}
+
+	return 0;
+}
+
 /**
  * epf_ntb_config_spad_bar_alloc() - Allocate memory for config + scratchpad
  *   region
@@ -474,6 +596,7 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
 	ntb->reg = base;
 
 	ctrl = ntb->reg;
+	ctrl->version = ntb->packed_mws ? EPF_NTB_CTRL_V1 : EPF_NTB_CTRL_V0;
 	ctrl->spad_offset = ctrl_size;
 
 	ctrl->spad_count = spad_count;
@@ -487,6 +610,11 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
 		ntb->reg->db_offset[i] = 0;
 	}
 
+	if (ntb->packed_mws) {
+		ctrl->mw_bar = ntb->mw_layout[0].barno;
+		ctrl->mw_group_size = ntb->mws_size[0];
+	}
+
 	return 0;
 }
 
@@ -761,16 +889,28 @@ static int epf_ntb_mw_bar_init(struct epf_ntb *ntb)
 	u64 size;
 	enum pci_barno barno;
 	struct device *dev = &ntb->epf->dev;
+	u64 bar_size[BAR_5 + 1] = {};
+	bool bar_set[BAR_5 + 1] = {};
 
 	for (i = 0; i < ntb->num_mws; i++) {
-		size = ntb->mws_size[i];
-		barno = ntb->epf_ntb_bar[BAR_MW1 + i];
+		size = ntb->mw_layout[i].size;
+		barno = ntb->mw_layout[i].barno;
+		bar_size[barno] = max(bar_size[barno],
+				      ntb->mw_layout[i].offset + size);
+	}
+
+	for (i = 0; i < ntb->num_mws; i++) {
+		size = ntb->mw_layout[i].size;
+		barno = ntb->mw_layout[i].barno;
+
+		if (bar_set[barno])
+			goto alloc_vpci_mw;
 
 		ntb->epf->bar[barno].barno = barno;
-		ntb->epf->bar[barno].size = size;
+		ntb->epf->bar[barno].size = bar_size[barno];
 		ntb->epf->bar[barno].addr = NULL;
 		ntb->epf->bar[barno].phys_addr = 0;
-		ntb->epf->bar[barno].flags |= upper_32_bits(size) ?
+		ntb->epf->bar[barno].flags |= upper_32_bits(bar_size[barno]) ?
 				PCI_BASE_ADDRESS_MEM_TYPE_64 :
 				PCI_BASE_ADDRESS_MEM_TYPE_32;
 
@@ -782,7 +922,9 @@ static int epf_ntb_mw_bar_init(struct epf_ntb *ntb)
 			dev_err(dev, "MW set failed\n");
 			goto err_alloc_mem;
 		}
+		bar_set[barno] = true;
 
+alloc_vpci_mw:
 		/* Allocate EPC outbound memory windows to vpci vntb device */
 		ntb->vpci_mw_addr[i] = pci_epc_mem_alloc_addr(ntb->epf->epc,
 							      &ntb->vpci_mw_phy[i],
@@ -790,17 +932,13 @@ static int epf_ntb_mw_bar_init(struct epf_ntb *ntb)
 		if (!ntb->vpci_mw_addr[i]) {
 			ret = -ENOMEM;
 			dev_err(dev, "Failed to allocate source address\n");
-			goto err_set_bar;
+			i++;
+			goto err_alloc_mem;
 		}
 	}
 
 	return ret;
 
-err_set_bar:
-	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;
@@ -809,24 +947,32 @@ static int epf_ntb_mw_bar_init(struct epf_ntb *ntb)
 /**
  * epf_ntb_mw_bar_clear() - Clear Memory window BARs
  * @ntb: NTB device that facilitates communication between HOST and VHOST
- * @num_mws: the number of Memory window BARs that to be cleared
+ * @num_mws: Number of logical memory windows to clean up
  */
 static void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws)
 {
+	bool bar_cleared[BAR_5 + 1] = {};
 	enum pci_barno barno;
 	int i;
 
 	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]);
+		barno = ntb->mw_layout[i].barno;
+		if (!bar_cleared[barno]) {
+			pci_epc_clear_bar(ntb->epf->epc,
+					  ntb->epf->func_no,
+					  ntb->epf->vfunc_no,
+					  &ntb->epf->bar[barno]);
+			bar_cleared[barno] = true;
+		}
+
+		if (!ntb->vpci_mw_addr[i])
+			continue;
 
 		pci_epc_mem_free_addr(ntb->epf->epc,
 				      ntb->vpci_mw_phy[i],
 				      ntb->vpci_mw_addr[i],
-				      ntb->mws_size[i]);
+				      ntb->mw_layout[i].size);
+		ntb->vpci_mw_addr[i] = NULL;
 	}
 }
 
@@ -916,16 +1062,21 @@ static int epf_ntb_init_epc_bar(struct epf_ntb *ntb)
 		}
 	}
 
-	/* These are optional BARs which don't impact NTB functionality */
-	for (bar = BAR_MW1, i = 1; i < num_mws; bar++, i++) {
-		barno = epf_ntb_find_bar(ntb, epc_features, bar, barno);
-		if (barno < 0) {
-			ntb->num_mws = i;
-			dev_dbg(dev, "BAR not available for > MW%d\n", i + 1);
+	if (!ntb->packed_mws) {
+		/* These are optional BARs which don't impact NTB functionality */
+		for (bar = BAR_MW1, i = 1;
+		     i < num_mws && bar <= BAR_MW4; bar++, i++) {
+			barno = epf_ntb_find_bar(ntb, epc_features, bar, barno);
+			if (barno < 0) {
+				ntb->num_mws = i;
+				dev_dbg(dev, "BAR not available for > MW%d\n",
+					i + 1);
+				break;
+			}
 		}
 	}
 
-	return 0;
+	return epf_ntb_build_mw_layout(ntb);
 }
 
 /**
@@ -1352,6 +1503,28 @@ static int vntb_epf_mw_count(struct ntb_dev *ntb, int pidx)
 	return ndev->num_mws;
 }
 
+static int vntb_epf_mw_get_trans_group(struct ntb_dev *ndev, int pidx,
+				       int idx, int *gidx, int *gcount)
+{
+	struct epf_ntb *ntb = ntb_ndev(ndev);
+	unsigned int first, count;
+	int ret;
+
+	if (pidx != NTB_DEF_PEER_IDX || idx < 0 || idx >= ntb->num_mws)
+		return -EINVAL;
+
+	ret = epf_ntb_get_mw_group(ntb, idx, &first, &count);
+	if (ret)
+		return ret;
+
+	if (gidx)
+		*gidx = first;
+	if (gcount)
+		*gcount = count;
+
+	return 0;
+}
+
 static int vntb_epf_spad_count(struct ntb_dev *ntb)
 {
 	return ntb_ndev(ntb)->spad_count;
@@ -1415,13 +1588,22 @@ static int vntb_epf_mw_set_trans(struct ntb_dev *ndev, int pidx, int idx,
 		dma_addr_t addr, resource_size_t size)
 {
 	struct epf_ntb *ntb = ntb_ndev(ndev);
+	struct epf_ntb_mw_layout *mw;
 	struct pci_epf_bar *epf_bar;
 	enum pci_barno barno;
 	int ret;
 	struct device *dev;
 
 	dev = &ntb->ntb.dev;
-	barno = ntb->epf_ntb_bar[BAR_MW1 + idx];
+	if (pidx != NTB_DEF_PEER_IDX ||
+	    idx < 0 || idx >= ntb->num_mws)
+		return -EINVAL;
+
+	mw = &ntb->mw_layout[idx];
+	if (size > mw->size)
+		return -EINVAL;
+
+	barno = mw->barno;
 	epf_bar = &ntb->epf->bar[barno];
 	epf_bar->phys_addr = addr;
 	epf_bar->barno = barno;
@@ -1450,7 +1632,7 @@ static int vntb_epf_peer_mw_get_addr(struct ntb_dev *ndev, int idx,
 		*base = ntb->vpci_mw_phy[idx];
 
 	if (size)
-		*size = ntb->mws_size[idx];
+		*size = ntb->mw_layout[idx].size;
 
 	return 0;
 }
@@ -1608,7 +1790,7 @@ static int vntb_epf_mw_get_align(struct ntb_dev *ndev, int pidx, int idx,
 		*size_align = 1;
 
 	if (size_max)
-		*size_max = ntb->mws_size[idx];
+		*size_max = ntb->mw_layout[idx].size;
 
 	return 0;
 }
@@ -1656,6 +1838,7 @@ static const struct ntb_dev_ops vntb_epf_ops = {
 	.db_vector_count	= vntb_epf_db_vector_count,
 	.db_vector_mask		= vntb_epf_db_vector_mask,
 	.db_set_mask		= vntb_epf_db_set_mask,
+	.mw_get_trans_group	= vntb_epf_mw_get_trans_group,
 	.mw_set_trans		= vntb_epf_mw_set_trans,
 	.mw_clear_trans		= vntb_epf_mw_clear_trans,
 	.peer_mw_get_addr	= vntb_epf_peer_mw_get_addr,
-- 
2.51.0