#include "gve.h"
 #include "gve_adminq.h"
 #include "gve_dqo.h"
+#include "gve_utils.h"
 
 static void gve_get_drvinfo(struct net_device *netdev,
                            struct ethtool_drvinfo *info)
        struct stats *report_stats;
        int *rx_qid_to_stats_idx;
        int *tx_qid_to_stats_idx;
+       int num_stopped_rxqs = 0;
+       int num_stopped_txqs = 0;
        struct gve_priv *priv;
        bool skip_nic_stats;
        unsigned int start;
                                            sizeof(int), GFP_KERNEL);
        if (!rx_qid_to_stats_idx)
                return;
+       for (ring = 0; ring < priv->rx_cfg.num_queues; ring++) {
+               rx_qid_to_stats_idx[ring] = -1;
+               if (!gve_rx_was_added_to_block(priv, ring))
+                       num_stopped_rxqs++;
+       }
        tx_qid_to_stats_idx = kmalloc_array(num_tx_queues,
                                            sizeof(int), GFP_KERNEL);
        if (!tx_qid_to_stats_idx) {
                kfree(rx_qid_to_stats_idx);
                return;
        }
+       for (ring = 0; ring < num_tx_queues; ring++) {
+               tx_qid_to_stats_idx[ring] = -1;
+               if (!gve_tx_was_added_to_block(priv, ring))
+                       num_stopped_txqs++;
+       }
+
        for (rx_pkts = 0, rx_bytes = 0, rx_hsplit_pkt = 0,
             rx_skb_alloc_fail = 0, rx_buf_alloc_fail = 0,
             rx_desc_err_dropped_pkt = 0, rx_hsplit_unsplit_pkt = 0,
        /* For rx cross-reporting stats, start from nic rx stats in report */
        base_stats_idx = GVE_TX_STATS_REPORT_NUM * num_tx_queues +
                GVE_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues;
-       max_stats_idx = NIC_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues +
+       /* The boundary between driver stats and NIC stats shifts if there are
+        * stopped queues.
+        */
+       base_stats_idx += NIC_RX_STATS_REPORT_NUM * num_stopped_rxqs +
+               NIC_TX_STATS_REPORT_NUM * num_stopped_txqs;
+       max_stats_idx = NIC_RX_STATS_REPORT_NUM *
+               (priv->rx_cfg.num_queues - num_stopped_rxqs) +
                base_stats_idx;
        /* Preprocess the stats report for rx, map queue id to start index */
        skip_nic_stats = false;
                        skip_nic_stats = true;
                        break;
                }
+               if (queue_id < 0 || queue_id >= priv->rx_cfg.num_queues) {
+                       net_err_ratelimited("Invalid rxq id in NIC stats\n");
+                       continue;
+               }
                rx_qid_to_stats_idx[queue_id] = stats_idx;
        }
        /* walk RX rings */
                        data[i++] = rx->rx_copybreak_pkt;
                        data[i++] = rx->rx_copied_pkt;
                        /* stats from NIC */
-                       if (skip_nic_stats) {
+                       stats_idx = rx_qid_to_stats_idx[ring];
+                       if (skip_nic_stats || stats_idx < 0) {
                                /* skip NIC rx stats */
                                i += NIC_RX_STATS_REPORT_NUM;
                        } else {
-                               stats_idx = rx_qid_to_stats_idx[ring];
                                for (j = 0; j < NIC_RX_STATS_REPORT_NUM; j++) {
                                        u64 value =
                                                be64_to_cpu(report_stats[stats_idx + j].value);
 
        /* For tx cross-reporting stats, start from nic tx stats in report */
        base_stats_idx = max_stats_idx;
-       max_stats_idx = NIC_TX_STATS_REPORT_NUM * num_tx_queues +
+       max_stats_idx = NIC_TX_STATS_REPORT_NUM *
+               (num_tx_queues - num_stopped_txqs) +
                max_stats_idx;
        /* Preprocess the stats report for tx, map queue id to start index */
        skip_nic_stats = false;
                        skip_nic_stats = true;
                        break;
                }
+               if (queue_id < 0 || queue_id >= num_tx_queues) {
+                       net_err_ratelimited("Invalid txq id in NIC stats\n");
+                       continue;
+               }
                tx_qid_to_stats_idx[queue_id] = stats_idx;
        }
        /* walk TX rings */
                        data[i++] = gve_tx_load_event_counter(priv, tx);
                        data[i++] = tx->dma_mapping_error;
                        /* stats from NIC */
-                       if (skip_nic_stats) {
+                       stats_idx = tx_qid_to_stats_idx[ring];
+                       if (skip_nic_stats || stats_idx < 0) {
                                /* skip NIC tx stats */
                                i += NIC_TX_STATS_REPORT_NUM;
                        } else {
-                               stats_idx = tx_qid_to_stats_idx[ring];
                                for (j = 0; j < NIC_TX_STATS_REPORT_NUM; j++) {
                                        u64 value =
                                                be64_to_cpu(report_stats[stats_idx + j].value);