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@vger.kernel.org Reviewed-by: Logan Gunthorpe Reviewed-by: Dave Jiang Signed-off-by: Koichiro Den --- 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@valinux.co.jp/ @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