From: Koichiro Den <den@valinux.co.jp>
To: Jon Mason <jdmason@kudzu.us>, Dave Jiang <dave.jiang@intel.com>,
Frank Li <Frank.Li@kernel.org>, Allen Hubbe <allenbh@gmail.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Niklas Cassel <cassel@kernel.org>,
Nicholas Bellinger <nab@linux-iscsi.org>
Cc: ntb@lists.linux.dev, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH 07/16] NTB: ntb_transport: Negotiate direct-DMA queue layout
Date: Tue, 11 Aug 2026 01:51:26 +0900 [thread overview]
Message-ID: <20260810165136.2292436-8-den@valinux.co.jp> (raw)
In-Reply-To: <20260810165136.2292436-1-den@valinux.co.jp>
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 <den@valinux.co.jp>
---
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
next prev parent reply other threads:[~2026-08-10 16:51 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-10 16:51 [PATCH 00/16] NTB: Add direct TX/RX using PCI endpoint DMA Koichiro Den
2026-08-10 16:51 ` [PATCH 01/16] NTB: ntb_transport: Abort link setup on QP MW allocation failure Koichiro Den
2026-08-10 18:41 ` Frank Li
2026-08-10 16:51 ` [PATCH 02/16] NTB: ntb_transport: Reject oversized TX buffers Koichiro Den
2026-08-10 16:51 ` [PATCH 03/16] NTB: ntb_transport: Start TX offload thread after queue setup Koichiro Den
2026-08-10 16:51 ` [PATCH 04/16] NTB: ntb_transport: Stop QP work before freeing a queue Koichiro Den
2026-08-10 16:51 ` [PATCH 05/16] NTB: ntb_transport: Run RX processing on system workqueue Koichiro Den
2026-08-10 16:51 ` [PATCH 06/16] NTB: ntb_transport: Define direct-DMA shared state Koichiro Den
2026-08-10 16:51 ` Koichiro Den [this message]
2026-08-10 16:51 ` [PATCH 08/16] NTB: ntb_transport: Add opt-in direct-DMA channel reservation Koichiro Den
2026-08-10 16:51 ` [PATCH 09/16] NTB: ntb_transport: Allocate direct-DMA queue state Koichiro Den
2026-08-10 16:51 ` [PATCH 10/16] NTB: ntb_transport: Implement direct-DMA QP session handshake Koichiro Den
2026-08-10 16:51 ` [PATCH 11/16] NTB: ntb_transport: Implement direct-DMA RX buffer publication Koichiro Den
2026-08-10 16:51 ` [PATCH 12/16] NTB: ntb_transport: Implement direct-DMA TX submission Koichiro Den
2026-08-10 16:51 ` [PATCH 13/16] NTB: ntb_transport: Implement safe direct-DMA teardown Koichiro Den
2026-08-10 16:51 ` [PATCH 14/16] NTB: ntb_transport: Enable direct-DMA queues Koichiro Den
2026-08-10 17:04 ` Koichiro Den
2026-08-10 16:51 ` [PATCH 15/16] NTB: ntb_transport: Report the direct-DMA payload limit Koichiro Den
2026-08-10 16:51 ` [PATCH 16/16] NTB: ntb_transport: Add optional polling for direct-DMA RX Koichiro Den
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260810165136.2292436-8-den@valinux.co.jp \
--to=den@valinux.co.jp \
--cc=Frank.Li@kernel.org \
--cc=allenbh@gmail.com \
--cc=cassel@kernel.org \
--cc=dave.jiang@intel.com \
--cc=gregkh@linuxfoundation.org \
--cc=jdmason@kudzu.us \
--cc=linux-kernel@vger.kernel.org \
--cc=nab@linux-iscsi.org \
--cc=netdev@vger.kernel.org \
--cc=ntb@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox