[PATCH v2 2/4] net: ntb_netdev: Gate subqueue stop/wake by transport link
From: Koichiro Den
Date: Sat Feb 28 2026 - 09:57:34 EST
When ntb_netdev is extended to multiple ntb_transport queue pairs, the
netdev carrier can be up as long as at least one QP link is up. In that
setup, a given QP may be link-down while the carrier remains on.
Make the link event handler start/stop the corresponding netdev TX
subqueue and drive carrier state based on whether any QP link is up.
Also guard subqueue wake/start points in the TX completion and timer
paths so a subqueue is not restarted while its QP link is down.
Stop all queues in ndo_open() and let the link event handler wake each
subqueue once ntb_transport link negotiation succeeds.
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
drivers/net/ntb_netdev.c | 42 ++++++++++++++++++++++++++++++----------
1 file changed, 32 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c
index 7437b4580dff..19a3383d86f8 100644
--- a/drivers/net/ntb_netdev.c
+++ b/drivers/net/ntb_netdev.c
@@ -99,16 +99,30 @@ static void ntb_netdev_event_handler(void *data, int link_is_up)
struct ntb_netdev_queue *q = data;
struct ntb_netdev *dev = q->ntdev;
struct net_device *ndev = dev->ndev;
+ bool any_up = false;
+ unsigned int i;
netdev_dbg(ndev, "Event %x, Link %x, qp %u\n", link_is_up,
ntb_transport_link_query(q->qp), q->qid);
- if (link_is_up) {
- if (ntb_transport_link_query(q->qp))
- netif_carrier_on(ndev);
- } else {
+ if (netif_running(ndev)) {
+ if (link_is_up)
+ netif_wake_subqueue(ndev, q->qid);
+ else
+ netif_stop_subqueue(ndev, q->qid);
+ }
+
+ for (i = 0; i < dev->num_queues; i++) {
+ if (ntb_transport_link_query(dev->queues[i].qp)) {
+ any_up = true;
+ break;
+ }
+ }
+
+ if (any_up)
+ netif_carrier_on(ndev);
+ else
netif_carrier_off(ndev);
- }
}
static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
@@ -177,7 +191,10 @@ static int __ntb_netdev_maybe_stop_tx(struct net_device *netdev,
return -EBUSY;
}
- netif_start_subqueue(netdev, q->qid);
+ /* The subqueue must be kept stopped if the link is down */
+ if (ntb_transport_link_query(q->qp))
+ netif_start_subqueue(netdev, q->qid);
+
return 0;
}
@@ -218,7 +235,8 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data,
* value of ntb_transport_tx_free_entry()
*/
smp_mb();
- if (__netif_subqueue_stopped(ndev, q->qid))
+ if (__netif_subqueue_stopped(ndev, q->qid) &&
+ ntb_transport_link_query(q->qp))
netif_wake_subqueue(ndev, q->qid);
}
}
@@ -269,7 +287,10 @@ static void ntb_netdev_tx_timer(struct timer_list *t)
* value of ntb_transport_tx_free_entry()
*/
smp_mb();
- if (__netif_subqueue_stopped(ndev, q->qid))
+
+ /* The subqueue must be kept stopped if the link is down */
+ if (__netif_subqueue_stopped(ndev, q->qid) &&
+ ntb_transport_link_query(q->qp))
netif_wake_subqueue(ndev, q->qid);
}
}
@@ -305,12 +326,11 @@ static int ntb_netdev_open(struct net_device *ndev)
}
netif_carrier_off(ndev);
+ netif_tx_stop_all_queues(ndev);
for (q = 0; q < dev->num_queues; q++)
ntb_transport_link_up(dev->queues[q].qp);
- netif_start_queue(ndev);
-
return 0;
err:
@@ -331,6 +351,8 @@ static int ntb_netdev_close(struct net_device *ndev)
unsigned int q;
int len;
+ netif_tx_stop_all_queues(ndev);
+ netif_carrier_off(ndev);
for (q = 0; q < dev->num_queues; q++) {
queue = &dev->queues[q];
--
2.51.0