]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[gve] Cancel pending transmissions when closing device
authorMichael Brown <mcb30@ipxe.org>
Mon, 6 Oct 2025 12:06:06 +0000 (13:06 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 6 Oct 2025 12:16:22 +0000 (13:16 +0100)
We cancel any pending transmissions when (re)starting the device since
any transmissions that were initiated before the admin queue reset
will not complete.

The network device core will also cancel any pending transmissions
after the device is closed.  If the device is closed with some
transmissions still pending and is then reopened, this will therefore
result in a stale I/O buffer being passed to netdev_tx_complete_err()
when the device is restarted.

This error has not been observed in practice since transmissions
generally complete almost immediately and it is therefore unlikely
that the device will ever be closed with transmissions still pending.
With out-of-order queues, the device seems to delay transmit
completions (with no upper time limit) until a complete batch is
available to be written out as a block of 128 bytes.  It is therefore
very likely that the device will be closed with transmissions still
pending.

Fix by ensuring that we have dropped all references to transmit I/O
buffers before returning from gve_close().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/net/gve.c

index cf796a8b5e3a4a1d1d4cf11d5f979c1483d75348..fa8a15407ac2663de49cb4166ff054d565391a19 100644 (file)
@@ -1073,18 +1073,14 @@ static void gve_free_queue ( struct gve_nic *gve, struct gve_queue *queue ) {
 }
 
 /**
- * Start up device
+ * Cancel any pending transmissions
  *
  * @v gve              GVE device
- * @ret rc             Return status code
  */
-static int gve_start ( struct gve_nic *gve ) {
+static void gve_cancel_tx ( struct gve_nic *gve ) {
        struct net_device *netdev = gve->netdev;
-       struct gve_queue *tx = &gve->tx;
-       struct gve_queue *rx = &gve->rx;
        struct io_buffer *iobuf;
        unsigned int i;
-       int rc;
 
        /* Cancel any pending transmissions */
        for ( i = 0 ; i < ( sizeof ( gve->tx_iobuf ) /
@@ -1094,6 +1090,21 @@ static int gve_start ( struct gve_nic *gve ) {
                if ( iobuf )
                        netdev_tx_complete_err ( netdev, iobuf, -ECANCELED );
        }
+}
+
+/**
+ * Start up device
+ *
+ * @v gve              GVE device
+ * @ret rc             Return status code
+ */
+static int gve_start ( struct gve_nic *gve ) {
+       struct gve_queue *tx = &gve->tx;
+       struct gve_queue *rx = &gve->rx;
+       int rc;
+
+       /* Cancel any pending transmissions */
+       gve_cancel_tx ( gve );
 
        /* Reset receive sequence */
        gve->seq = gve_next ( 0 );
@@ -1307,6 +1318,9 @@ static void gve_close ( struct net_device *netdev ) {
        gve_stop ( gve );
        gve_reset ( gve );
 
+       /* Cancel any pending transmissions */
+       gve_cancel_tx ( gve );
+
        /* Free queues */
        gve_free_queue ( gve, rx );
        gve_free_queue ( gve, tx );