[PATCH 04/16] NTB: ntb_transport: Stop QP work before freeing a queue

From: Koichiro Den

Date: Mon Aug 10 2026 - 12:56:15 EST


ntb_transport_link_down() clears client_ready, but asynchronous QP work
can outlive that transition. This leaves three teardown races:

1. Queued link_work can mark the QP active again and schedule RX
processing.
2. A per-QP MSI can schedule RX processing after client link-down.
3. link_cleanup can run while ntb_transport_free_queue() releases queue
state and can rearm link_work. RX processing can also queue another
cleanup while it is being drained.

Gate link_work and the MSI handler with client_ready. Before releasing
queue resources, drain link_cleanup and link_work. After stopping RX
processing, drain link_cleanup once more, then link_work because cleanup
can rearm it.

Fixes: fce8a7bb5b4b ("PCI-Express Non-Transparent Bridge Support")
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
drivers/ntb/ntb_transport.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 044d8b5747fc..4afad4489772 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -653,7 +653,8 @@ static irqreturn_t ntb_transport_isr(int irq, void *dev)
{
struct ntb_transport_qp *qp = dev;

- tasklet_schedule(&qp->rxc_db_work);
+ if (qp->client_ready)
+ tasklet_schedule(&qp->rxc_db_work);

return IRQ_HANDLED;
}
@@ -1133,6 +1134,9 @@ static void ntb_qp_link_work(struct work_struct *work)
struct ntb_transport_ctx *nt = qp->transport;
int val;

+ if (!qp->client_ready)
+ return;
+
WARN_ON(!nt->link_is_up);

val = ntb_spad_read(nt->ndev, QP_LINKS);
@@ -2182,6 +2186,8 @@ void ntb_transport_free_queue(struct ntb_transport_qp *qp)

pdev = qp->ndev->pdev;

+ cancel_work_sync(&qp->link_cleanup);
+ cancel_delayed_work_sync(&qp->link_work);
qp->active = false;

if (qp->tx_offload_thread) {
@@ -2229,6 +2235,8 @@ void ntb_transport_free_queue(struct ntb_transport_qp *qp)
ntb_db_set_mask(qp->ndev, qp_bit);
tasklet_kill(&qp->rxc_db_work);

+ /* Catch cleanup queued while draining RX processing. */
+ cancel_work_sync(&qp->link_cleanup);
cancel_delayed_work_sync(&qp->link_work);

qp->cb_data = NULL;
--
2.51.0