[PATCH 07/16] NTB: ntb_transport: Negotiate direct-DMA queue layout

Koichiro Den <[email protected]>
Newsgroups dev.linux.lists.ntb,org.kernel.vger.linux-kernel,org.kernel.vger.netdev
Message-ID <[email protected]>
The direct-DMA shared area occupies part of each QP memory-window slice,
so both peers must agree on its size and placement. Exchange direct-DMA
capabilities and ring depth in spare scratchpads, and reserve the area
only when both peers advertise compatible settings. Otherwise retain the
protocol v4 copy layout.

Signed-off-by: Koichiro Den <[email protected]>
---
 drivers/ntb/ntb_transport.c | 170 ++++++++++++++++++++++++++++++++++--
 1 file changed, 161 insertions(+), 9 deletions(-)

diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 44957c11d1c8..f873eacd532b 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -98,6 +98,11 @@ static bool use_dma;
 module_param(use_dma, bool, 0644);
 MODULE_PARM_DESC(use_dma, "Use DMA engine to perform large data copy");
 
+static unsigned int direct_dma_ring_entries = 64;
+module_param(direct_dma_ring_entries, uint, 0644);
+MODULE_PARM_DESC(direct_dma_ring_entries,
+		 "Number of entries in each direct-DMA queue");
+
 static bool use_msi;
 #ifdef CONFIG_NTB_MSI
 module_param(use_msi, bool, 0644);
@@ -174,6 +179,15 @@ struct ntb_transport_qp {
 	struct list_head rx_free_q;
 	/* ntb_rx_q_lock: synchronize access to rx_XXXX_q */
 	spinlock_t ntb_rx_q_lock;
+	struct ntb_direct_shared *direct_shared;
+	struct ntb_direct_shared __iomem *peer_direct_shared;
+	unsigned int direct_ring_entries;
+	u32 *direct_tx_cpl;
+	dma_addr_t direct_tx_cpl_dma;
+	u32 direct_rx_prod;
+	u32 direct_rx_cons;
+	u32 direct_tx_issue;
+	u32 direct_tx_cons;
 	void *rx_buff;
 	unsigned int rx_index;
 	unsigned int rx_max_entry;
@@ -255,6 +269,9 @@ struct ntb_transport_ctx {
 	struct work_struct link_cleanup;
 
 	struct dentry *debugfs_node_dir;
+	u32 direct_features;
+	u32 peer_direct_features;
+	unsigned int direct_ring_entries;
 
 	/* Make sure workq of link event be executed serially */
 	struct mutex link_event_lock;
@@ -315,11 +332,26 @@ struct ntb_direct_shared {
 /* Zero is pending, U32_MAX is an error, and other values are lengths. */
 #define NTB_DIRECT_CPL_ERROR		U32_MAX
 
+enum {
+	NTB_DIRECT_FEAT_RX = BIT(0),
+	NTB_DIRECT_FEAT_TX = BIT(1),
+};
+
 static inline size_t ntb_direct_shared_size(unsigned int entries)
 {
 	return struct_size_t(struct ntb_direct_shared, pub, entries);
 }
 
+static inline u32 ntb_direct_ring_idx(struct ntb_transport_qp *qp, u32 val)
+{
+	return val & (qp->direct_ring_entries - 1);
+}
+
+static inline u32 ntb_direct_ring_used(u32 head, u32 tail)
+{
+	return head - tail;
+}
+
 struct ntb_payload_header {
 	unsigned int ver;
 	unsigned int len;
@@ -335,6 +367,32 @@ enum {
 	MW0_SZ_LOW,
 };
 
+enum {
+	DIRECT_SPAD_FEATURES,
+	DIRECT_SPAD_RING_ENTRIES,
+	DIRECT_SPAD_COUNT,
+};
+
+static unsigned int ntb_direct_spad_offset(struct ntb_transport_ctx *nt)
+{
+	/* Skip two SPADs per QP reserved for optional v4 MSI descriptors. */
+	return nt->msi_spad_offset + nt->qp_count * 2;
+}
+
+static bool ntb_direct_spads_available(struct ntb_transport_ctx *nt)
+{
+	return ntb_direct_spad_offset(nt) + DIRECT_SPAD_COUNT <=
+		ntb_spad_count(nt->ndev);
+}
+
+static bool ntb_direct_layout(struct ntb_transport_ctx *nt)
+{
+	return ((nt->direct_features & NTB_DIRECT_FEAT_RX) &&
+		(nt->peer_direct_features & NTB_DIRECT_FEAT_TX)) ||
+	       ((nt->direct_features & NTB_DIRECT_FEAT_TX) &&
+		(nt->peer_direct_features & NTB_DIRECT_FEAT_RX));
+}
+
 #define dev_client_dev(__dev) \
 	container_of((__dev), struct ntb_transport_client_dev, dev)
 
@@ -635,6 +693,38 @@ static struct ntb_queue_entry *ntb_list_mv(spinlock_t *lock,
 	return entry;
 }
 
+/*
+ * Protocol v4 uses the whole area before rx_info for copy slots. Reserve the
+ * tail for direct state only when both peers negotiated a direct direction.
+ */
+static int ntb_transport_setup_qp_tx_layout(struct ntb_transport_qp *qp)
+{
+	unsigned int tx_size = qp->tx_mw_size;
+	size_t direct_size = 0;
+
+	if (ntb_direct_layout(qp->transport))
+		direct_size = ntb_direct_shared_size(qp->direct_ring_entries);
+	if (direct_size &&
+	    tx_size < direct_size + sizeof(struct ntb_rx_info) +
+		      2 * sizeof(struct ntb_payload_header))
+		return -ENOSPC;
+
+	qp->rx_info = qp->tx_mw + tx_size - sizeof(struct ntb_rx_info);
+	tx_size -= sizeof(struct ntb_rx_info);
+	if (direct_size) {
+		tx_size -= direct_size;
+		qp->peer_direct_shared = qp->tx_mw + tx_size;
+	} else {
+		qp->peer_direct_shared = NULL;
+	}
+
+	/* Ring housekeeping requires at least two buffers. */
+	qp->tx_max_frame = min(transport_mtu, tx_size / 2);
+	qp->tx_max_entry = tx_size / qp->tx_max_frame;
+
+	return 0;
+}
+
 static int ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt,
 				     unsigned int qp_num)
 {
@@ -644,6 +734,8 @@ static int ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt,
 	struct ntb_queue_entry *entry;
 	unsigned int rx_size, num_qps_mw;
 	unsigned int mw_num, mw_count, qp_count;
+	size_t direct_size = 0;
+	void *rx_base;
 	unsigned int i;
 	int node;
 
@@ -662,10 +754,24 @@ static int ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt,
 		num_qps_mw = qp_count / mw_count;
 
 	rx_size = (unsigned int)mw->xlat_size / num_qps_mw;
-	qp->rx_buff = mw->virt_addr + rx_size * (qp_num / mw_count);
+	rx_base = mw->virt_addr + rx_size * (qp_num / mw_count);
+	if (ntb_direct_layout(nt))
+		direct_size = ntb_direct_shared_size(qp->direct_ring_entries);
+	if (direct_size &&
+	    rx_size < direct_size + sizeof(struct ntb_rx_info) +
+		      2 * sizeof(struct ntb_payload_header))
+		return -ENOSPC;
+
+	qp->rx_buff = rx_base;
 	rx_size -= sizeof(struct ntb_rx_info);
-
 	qp->remote_rx_info = qp->rx_buff + rx_size;
+	if (direct_size) {
+		rx_size -= direct_size;
+		qp->direct_shared = qp->rx_buff + rx_size;
+		memset(qp->direct_shared, 0, direct_size);
+	} else {
+		qp->direct_shared = NULL;
+	}
 
 	/* Due to housekeeping, there must be atleast 2 buffs */
 	qp->rx_max_frame = min(transport_mtu, rx_size / 2);
@@ -1070,6 +1176,7 @@ static void ntb_transport_link_work(struct work_struct *work)
 	struct ntb_dev *ndev = nt->ndev;
 	struct pci_dev *pdev = ndev->pdev;
 	resource_size_t size;
+	unsigned int direct_spad;
 	u32 val;
 	int rc = 0, i, spad;
 
@@ -1106,6 +1213,15 @@ static void ntb_transport_link_work(struct work_struct *work)
 	ntb_peer_spad_write(ndev, PIDX, NUM_MWS, nt->mw_count);
 
 	ntb_peer_spad_write(ndev, PIDX, NUM_QPS, nt->qp_count);
+	if (ntb_direct_spads_available(nt)) {
+		direct_spad = ntb_direct_spad_offset(nt);
+		ntb_peer_spad_write(ndev, PIDX,
+				    direct_spad + DIRECT_SPAD_FEATURES,
+				    nt->direct_features);
+		ntb_peer_spad_write(ndev, PIDX,
+				    direct_spad + DIRECT_SPAD_RING_ENTRIES,
+				    nt->direct_ring_entries);
+	}
 
 	ntb_peer_spad_write(ndev, PIDX, VERSION, NTB_TRANSPORT_VERSION);
 
@@ -1125,6 +1241,26 @@ static void ntb_transport_link_work(struct work_struct *work)
 	if (val != nt->mw_count)
 		goto out;
 
+	nt->peer_direct_features = 0;
+	if (ntb_direct_spads_available(nt)) {
+		direct_spad = ntb_direct_spad_offset(nt);
+		val = ntb_spad_read(ndev,
+				    direct_spad + DIRECT_SPAD_FEATURES);
+		val &= NTB_DIRECT_FEAT_RX | NTB_DIRECT_FEAT_TX;
+		if (val) {
+			u32 entries;
+
+			entries = ntb_spad_read(ndev,
+						direct_spad +
+						DIRECT_SPAD_RING_ENTRIES);
+			dev_dbg(&pdev->dev,
+				"Remote direct DMA features = %#x, ring entries = %u\n",
+				val, entries);
+			if (entries == nt->direct_ring_entries)
+				nt->peer_direct_features = val;
+		}
+	}
+
 	for (i = 0; i < nt->mw_count; i++) {
 		u64 val64;
 
@@ -1143,6 +1279,9 @@ static void ntb_transport_link_work(struct work_struct *work)
 
 	nt->link_is_up = false;
 	for (i = 0; i < nt->qp_count; i++) {
+		rc = ntb_transport_setup_qp_tx_layout(&nt->qp_vec[i]);
+		if (rc)
+			goto out1;
 		rc = ntb_transport_setup_qp_mw(nt, i);
 		if (rc)
 			goto out1;
@@ -1164,6 +1303,8 @@ static void ntb_transport_link_work(struct work_struct *work)
 	for (i = 0; i < nt->qp_count; i++) {
 		struct ntb_transport_qp *qp = &nt->qp_vec[i];
 
+		qp->direct_shared = NULL;
+		qp->peer_direct_shared = NULL;
 		qp->rx_buff = NULL;
 		qp->remote_rx_info = NULL;
 	}
@@ -1254,22 +1395,20 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt,
 
 	tx_size = (unsigned int)mw_size / num_qps_mw;
 	qp_offset = tx_size * (qp_num / mw_count);
+	mw_base += qp_offset;
 
+	qp->direct_ring_entries = nt->direct_ring_entries;
 	qp->tx_mw_size = tx_size;
 	qp->tx_mw = nt->mw_vec[mw_num].vbase + qp_offset;
 	if (!qp->tx_mw)
 		return -EINVAL;
 
-	qp->tx_mw_phys = mw_base + qp_offset;
+	qp->tx_mw_phys = mw_base;
 	if (!qp->tx_mw_phys)
 		return -EINVAL;
 
-	tx_size -= sizeof(struct ntb_rx_info);
-	qp->rx_info = qp->tx_mw + tx_size;
-
-	/* Due to housekeeping, there must be atleast 2 buffs */
-	qp->tx_max_frame = min(transport_mtu, tx_size / 2);
-	qp->tx_max_entry = tx_size / qp->tx_max_frame;
+	if (ntb_transport_setup_qp_tx_layout(qp))
+		return -ENOSPC;
 
 	if (nt->debugfs_node_dir) {
 		char debugfs_name[8];
@@ -1337,6 +1476,7 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev)
 		return -ENOMEM;
 
 	nt->ndev = ndev;
+	nt->direct_ring_entries = direct_dma_ring_entries;
 
 	/*
 	 * If we are using MSI, and have at least one extra memory window,
@@ -1412,6 +1552,18 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev)
 	nt->qp_bitmap = qp_bitmap;
 	nt->qp_bitmap_free = qp_bitmap;
 
+	if (ntb_direct_spads_available(nt)) {
+		unsigned int spad = ntb_direct_spad_offset(nt);
+
+		/* Old v4 peers do not overwrite stale extension SPADs. */
+		rc = ntb_spad_write(ndev, spad + DIRECT_SPAD_FEATURES, 0);
+		if (rc)
+			goto err1;
+		rc = ntb_spad_write(ndev, spad + DIRECT_SPAD_RING_ENTRIES, 0);
+		if (rc)
+			goto err1;
+	}
+
 	nt->qp_vec = kcalloc_node(qp_count, sizeof(*nt->qp_vec),
 				  GFP_KERNEL, node);
 	if (!nt->qp_vec) {
-- 
2.51.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.