[PATCH v3 12/15] NTB: ntb_transport: Prepare remote RX info accesses for MW teardown
From: Koichiro Den
Date: Mon Sep 28 2026 - 11:44:11 EST
The next patch clears remote_rx_info when freeing its MW.
ntb_transport_tx_free_entry() and debugfs stats reads can run during
link cleanup, so make them handle a NULL pointer.
The pointer is accessed locklessly. Use READ_ONCE() and WRITE_ONCE()
to prevent compiler-induced tearing, and retain the read value so
the NULL check and dereference use the same pointer.
Also drop the redundant credit assertion in ntb_async_tx(). The caller
checks for space, but cleanup can clear remote_rx_info before this
second check.
Cc: stable@xxxxxxxxxxxxxxx
Reviewed-by: Logan Gunthorpe <logang@xxxxxxxxxxxx>
Reviewed-by: Dave Jiang <dave.jiang@xxxxxxxxx>
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v3:
- Drop the redundant TX credit assertion (Sashiko).
https://lore.kernel.org/r/hkrizcxjisnrkedwzycnkvhxynncgj66oj7crozf5ynuz4ys6c@7i7tixcnt2oa/
v2: https://lore.kernel.org/r/20260910040836.3792333-12-den@xxxxxxxxxxxxx/
@Dave and @Logan, one-line change after Sashiko's feedback. I would
appreciate it if you could take another look, thanks.
drivers/ntb/ntb_transport.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 8f1acf44bb53..c4dfef75f159 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -490,6 +490,7 @@ EXPORT_SYMBOL_GPL(ntb_transport_unregister_client);
static int ntb_qp_debugfs_stats_show(struct seq_file *s, void *v)
{
struct ntb_transport_qp *qp = s->private;
+ struct ntb_rx_info *remote_rx_info;
if (!qp || !qp->link_is_up)
return 0;
@@ -517,7 +518,9 @@ static int ntb_qp_debugfs_stats_show(struct seq_file *s, void *v)
seq_printf(s, "tx_err_no_buf - %llu\n", qp->tx_err_no_buf);
seq_printf(s, "tx_mw - \t0x%p\n", qp->tx_mw);
seq_printf(s, "tx_index (H) - \t%u\n", qp->tx_index);
- seq_printf(s, "RRI (T) - \t%u\n", qp->remote_rx_info->entry);
+ remote_rx_info = READ_ONCE(qp->remote_rx_info);
+ if (remote_rx_info)
+ seq_printf(s, "RRI (T) - \t%u\n", remote_rx_info->entry);
seq_printf(s, "tx_max_entry - \t%u\n", qp->tx_max_entry);
seq_printf(s, "free tx - \t%u\n", ntb_transport_tx_free_entry(qp));
seq_putc(s, '\n');
@@ -612,7 +615,7 @@ static int ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt,
qp->rx_buff = mw->virt_addr + rx_size * (qp_num / mw_count);
rx_size -= sizeof(struct ntb_rx_info);
- qp->remote_rx_info = qp->rx_buff + rx_size;
+ WRITE_ONCE(qp->remote_rx_info, qp->rx_buff + rx_size);
/* Due to housekeeping, there must be atleast 2 buffs */
qp->rx_max_frame = min(transport_mtu, rx_size / 2);
@@ -935,9 +938,12 @@ static void ntb_qp_link_context_reset(struct ntb_transport_qp *qp)
static void ntb_qp_link_down_reset(struct ntb_transport_qp *qp)
{
+ struct ntb_rx_info *remote_rx_info;
+
ntb_qp_link_context_reset(qp);
- if (qp->remote_rx_info)
- qp->remote_rx_info->entry = qp->rx_max_entry - 1;
+ remote_rx_info = READ_ONCE(qp->remote_rx_info);
+ if (remote_rx_info)
+ remote_rx_info->entry = qp->rx_max_entry - 1;
}
static void ntb_transport_schedule_qp_link(struct ntb_transport_qp *qp,
@@ -1988,7 +1994,6 @@ static void ntb_async_tx(struct ntb_transport_qp *qp,
hdr = offset + qp->tx_max_frame - sizeof(struct ntb_payload_header);
entry->tx_hdr = hdr;
- WARN_ON_ONCE(!ntb_transport_tx_free_entry(qp));
WRITE_ONCE(qp->tx_index, (qp->tx_index + 1) % qp->tx_max_entry);
iowrite32(entry->len, &hdr->len);
@@ -2555,8 +2560,14 @@ EXPORT_SYMBOL_GPL(ntb_transport_max_size);
unsigned int ntb_transport_tx_free_entry(struct ntb_transport_qp *qp)
{
+ struct ntb_rx_info *remote_rx_info = READ_ONCE(qp->remote_rx_info);
unsigned int head = qp->tx_index;
- unsigned int tail = qp->remote_rx_info->entry;
+ unsigned int tail;
+
+ if (!remote_rx_info)
+ return 0;
+
+ tail = remote_rx_info->entry;
return tail >= head ? tail - head : qp->tx_max_entry + tail - head;
}
--
2.51.0