[PATCH v2 07/14] NTB: ntb_transport: Stop QP work before freeing a queue

From: Koichiro Den

Date: Thu Sep 10 2026 - 00:12:44 EST


ntb_transport_free_queue() cancels qp->link_work but not qp->link_cleanup.
A peer link-down message can queue cleanup while ntb_netdev is freeing
the QP. Cleanup can then requeue link work after the queue resources
have been freed.

Disable and wait for cleanup, then link work, before freeing resources.
Unlike cancel, disable also prevents the RX tasklet and transport link
setup from queuing more work. Enable the works only after queue creation
succeeds.

Clear client_ready first so RX completions and transport link setup see
that the client is no longer ready. Clear link_is_up and active after
the workers stop, since link work can set both back to true.

Fixes: 7b4f2d3c3b82 ("NTB: No sleeping in interrupt context")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v2:
- No changes.

Reworked from the corresponding patch in the direct TX/RX series v1:
https://lore.kernel.org/r/20260810165136.2292436-5-den@xxxxxxxxxxxxx/

drivers/ntb/ntb_transport.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 8dd1770aaaf1..e5599c7ca93f 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -1242,6 +1242,8 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt,

INIT_DELAYED_WORK(&qp->link_work, ntb_qp_link_work);
INIT_WORK(&qp->link_cleanup, ntb_qp_link_cleanup_work);
+ disable_delayed_work(&qp->link_work);
+ disable_work(&qp->link_cleanup);

spin_lock_init(&qp->ntb_rx_q_lock);
spin_lock_init(&qp->ntb_tx_free_q_lock);
@@ -2155,6 +2157,9 @@ ntb_transport_create_queue(void *data, struct device *client_dev,
}
}

+ enable_work(&qp->link_cleanup);
+ enable_delayed_work(&qp->link_work);
+
ntb_db_clear(qp->ndev, qp_bit);
ntb_db_clear_mask(qp->ndev, qp_bit);

@@ -2200,6 +2205,10 @@ void ntb_transport_free_queue(struct ntb_transport_qp *qp)

pdev = qp->ndev->pdev;

+ qp->client_ready = false;
+ disable_work_sync(&qp->link_cleanup);
+ disable_delayed_work_sync(&qp->link_work);
+ qp->link_is_up = false;
qp->active = false;

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

- cancel_delayed_work_sync(&qp->link_work);
-
qp->cb_data = NULL;
qp->rx_handler = NULL;
qp->tx_handler = NULL;
--
2.51.0