]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: ntb_netdev: Gate subqueue stop/wake by transport link
authorKoichiro Den <den@valinux.co.jp>
Thu, 5 Mar 2026 15:56:37 +0000 (00:56 +0900)
committerJakub Kicinski <kuba@kernel.org>
Sat, 7 Mar 2026 03:15:20 +0000 (19:15 -0800)
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@valinux.co.jp>
Link: https://patch.msgid.link/20260305155639.1885517-3-den@valinux.co.jp
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ntb_netdev.c

index 4b65e938d549fa742bf3b54725338ecd226b5153..e4c1422d1d7ae594f70a26adf40bdbabbe5d69f8 100644 (file)
@@ -99,18 +99,32 @@ 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;
+       bool any_up = false;
+       unsigned int i;
 
        ndev = dev->ndev;
 
        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 {
-               netif_carrier_off(ndev);
+       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,
@@ -179,7 +193,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;
 }
 
@@ -221,7 +238,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);
        }
 }
@@ -268,7 +286,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);
        }
 }
@@ -304,12 +325,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:
@@ -330,6 +350,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];