gve: Recover from queue stall due to missed IRQ
authorJohn Fraker <jfraker@google.com>
Mon, 11 Oct 2021 15:36:47 +0000 (08:36 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 11 Oct 2021 22:25:36 +0000 (23:25 +0100)
Don't always reset the driver on a TX timeout. Attempt to
recover by kicking the queue in case an IRQ was missed.

Fixes: 9e5f7d26a4c08 ("gve: Add workqueue and reset support")
Signed-off-by: John Fraker <jfraker@google.com>
Signed-off-by: David Awogbemila <awogbemila@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/google/gve/gve.h
drivers/net/ethernet/google/gve/gve_adminq.h
drivers/net/ethernet/google/gve/gve_main.c

index 3de561e22659c015569525c441f8326134bca16d..51ed8fe71d2dcd3a9ce120983a874b85a8e1b33a 100644 (file)
@@ -30,7 +30,7 @@
 #define GVE_MIN_MSIX 3
 
 /* Numbers of gve tx/rx stats in stats report. */
-#define GVE_TX_STATS_REPORT_NUM        5
+#define GVE_TX_STATS_REPORT_NUM        6
 #define GVE_RX_STATS_REPORT_NUM        2
 
 /* Interval to schedule a stats report update, 20000ms. */
@@ -413,7 +413,9 @@ struct gve_tx_ring {
        u32 q_num ____cacheline_aligned; /* queue idx */
        u32 stop_queue; /* count of queue stops */
        u32 wake_queue; /* count of queue wakes */
+       u32 queue_timeout; /* count of queue timeouts */
        u32 ntfy_id; /* notification block index */
+       u32 last_kick_msec; /* Last time the queue was kicked */
        dma_addr_t bus; /* dma address of the descr ring */
        dma_addr_t q_resources_bus; /* dma address of the queue resources */
        dma_addr_t complq_bus_dqo; /* dma address of the dqo.compl_ring */
index 47c3d8f313fcf680039cf88f39fc3ca2bfc58bbb..3953f6f7a42731d4b2c2b586b64bbefb627dfc50 100644 (file)
@@ -270,6 +270,7 @@ enum gve_stat_names {
        TX_LAST_COMPLETION_PROCESSED    = 5,
        RX_NEXT_EXPECTED_SEQUENCE       = 6,
        RX_BUFFERS_POSTED               = 7,
+       TX_TIMEOUT_CNT                  = 8,
        // stats from NIC
        RX_QUEUE_DROP_CNT               = 65,
        RX_NO_BUFFERS_POSTED            = 66,
index b6805ad2011be0f7c000a92582f7b5c0e2ee153c..7647cd05b1d2ecfc92bbecee93755636de2d104d 100644 (file)
@@ -24,6 +24,9 @@
 #define GVE_VERSION            "1.0.0"
 #define GVE_VERSION_PREFIX     "GVE-"
 
+// Minimum amount of time between queue kicks in msec (10 seconds)
+#define MIN_TX_TIMEOUT_GAP (1000 * 10)
+
 const char gve_version_str[] = GVE_VERSION;
 static const char gve_version_prefix[] = GVE_VERSION_PREFIX;
 
@@ -1121,9 +1124,47 @@ static void gve_turnup(struct gve_priv *priv)
 
 static void gve_tx_timeout(struct net_device *dev, unsigned int txqueue)
 {
-       struct gve_priv *priv = netdev_priv(dev);
+       struct gve_notify_block *block;
+       struct gve_tx_ring *tx = NULL;
+       struct gve_priv *priv;
+       u32 last_nic_done;
+       u32 current_time;
+       u32 ntfy_idx;
+
+       netdev_info(dev, "Timeout on tx queue, %d", txqueue);
+       priv = netdev_priv(dev);
+       if (txqueue > priv->tx_cfg.num_queues)
+               goto reset;
+
+       ntfy_idx = gve_tx_idx_to_ntfy(priv, txqueue);
+       if (ntfy_idx > priv->num_ntfy_blks)
+               goto reset;
+
+       block = &priv->ntfy_blocks[ntfy_idx];
+       tx = block->tx;
 
+       current_time = jiffies_to_msecs(jiffies);
+       if (tx->last_kick_msec + MIN_TX_TIMEOUT_GAP > current_time)
+               goto reset;
+
+       /* Check to see if there are missed completions, which will allow us to
+        * kick the queue.
+        */
+       last_nic_done = gve_tx_load_event_counter(priv, tx);
+       if (last_nic_done - tx->done) {
+               netdev_info(dev, "Kicking queue %d", txqueue);
+               iowrite32be(GVE_IRQ_MASK, gve_irq_doorbell(priv, block));
+               napi_schedule(&block->napi);
+               tx->last_kick_msec = current_time;
+               goto out;
+       } // Else reset.
+
+reset:
        gve_schedule_reset(priv);
+
+out:
+       if (tx)
+               tx->queue_timeout++;
        priv->tx_timeo_cnt++;
 }
 
@@ -1252,6 +1293,11 @@ void gve_handle_report_stats(struct gve_priv *priv)
                                .value = cpu_to_be64(last_completion),
                                .queue_id = cpu_to_be32(idx),
                        };
+                       stats[stats_idx++] = (struct stats) {
+                               .stat_name = cpu_to_be32(TX_TIMEOUT_CNT),
+                               .value = cpu_to_be64(priv->tx[idx].queue_timeout),
+                               .queue_id = cpu_to_be32(idx),
+                       };
                }
        }
        /* rx stats */