Skip to content

Commit af9bcf9

Browse files
shailend-gdavem330
authored andcommitted
gve: Account for stopped queues when reading NIC stats
We now account for the fact that the NIC might send us stats for a subset of queues. Without this change, gve_get_ethtool_stats might make an invalid access on the priv->stats_report->stats array. Tested-by: Mina Almasry <almasrymina@google.com> Reviewed-by: Praveen Kaligineedi <pkaligineedi@google.com> Reviewed-by: Harshitha Ramamurthy <hramamurthy@google.com> Signed-off-by: Shailend Chand <shailend@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 770f52d commit af9bcf9

1 file changed

Lines changed: 35 additions & 6 deletions

File tree

drivers/net/ethernet/google/gve/gve_ethtool.c

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "gve.h"
99
#include "gve_adminq.h"
1010
#include "gve_dqo.h"
11+
#include "gve_utils.h"
1112

1213
static void gve_get_drvinfo(struct net_device *netdev,
1314
struct ethtool_drvinfo *info)
@@ -165,6 +166,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
165166
struct stats *report_stats;
166167
int *rx_qid_to_stats_idx;
167168
int *tx_qid_to_stats_idx;
169+
int num_stopped_rxqs = 0;
170+
int num_stopped_txqs = 0;
168171
struct gve_priv *priv;
169172
bool skip_nic_stats;
170173
unsigned int start;
@@ -181,12 +184,23 @@ gve_get_ethtool_stats(struct net_device *netdev,
181184
sizeof(int), GFP_KERNEL);
182185
if (!rx_qid_to_stats_idx)
183186
return;
187+
for (ring = 0; ring < priv->rx_cfg.num_queues; ring++) {
188+
rx_qid_to_stats_idx[ring] = -1;
189+
if (!gve_rx_was_added_to_block(priv, ring))
190+
num_stopped_rxqs++;
191+
}
184192
tx_qid_to_stats_idx = kmalloc_array(num_tx_queues,
185193
sizeof(int), GFP_KERNEL);
186194
if (!tx_qid_to_stats_idx) {
187195
kfree(rx_qid_to_stats_idx);
188196
return;
189197
}
198+
for (ring = 0; ring < num_tx_queues; ring++) {
199+
tx_qid_to_stats_idx[ring] = -1;
200+
if (!gve_tx_was_added_to_block(priv, ring))
201+
num_stopped_txqs++;
202+
}
203+
190204
for (rx_pkts = 0, rx_bytes = 0, rx_hsplit_pkt = 0,
191205
rx_skb_alloc_fail = 0, rx_buf_alloc_fail = 0,
192206
rx_desc_err_dropped_pkt = 0, rx_hsplit_unsplit_pkt = 0,
@@ -260,7 +274,13 @@ gve_get_ethtool_stats(struct net_device *netdev,
260274
/* For rx cross-reporting stats, start from nic rx stats in report */
261275
base_stats_idx = GVE_TX_STATS_REPORT_NUM * num_tx_queues +
262276
GVE_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues;
263-
max_stats_idx = NIC_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues +
277+
/* The boundary between driver stats and NIC stats shifts if there are
278+
* stopped queues.
279+
*/
280+
base_stats_idx += NIC_RX_STATS_REPORT_NUM * num_stopped_rxqs +
281+
NIC_TX_STATS_REPORT_NUM * num_stopped_txqs;
282+
max_stats_idx = NIC_RX_STATS_REPORT_NUM *
283+
(priv->rx_cfg.num_queues - num_stopped_rxqs) +
264284
base_stats_idx;
265285
/* Preprocess the stats report for rx, map queue id to start index */
266286
skip_nic_stats = false;
@@ -274,6 +294,10 @@ gve_get_ethtool_stats(struct net_device *netdev,
274294
skip_nic_stats = true;
275295
break;
276296
}
297+
if (queue_id < 0 || queue_id >= priv->rx_cfg.num_queues) {
298+
net_err_ratelimited("Invalid rxq id in NIC stats\n");
299+
continue;
300+
}
277301
rx_qid_to_stats_idx[queue_id] = stats_idx;
278302
}
279303
/* walk RX rings */
@@ -308,11 +332,11 @@ gve_get_ethtool_stats(struct net_device *netdev,
308332
data[i++] = rx->rx_copybreak_pkt;
309333
data[i++] = rx->rx_copied_pkt;
310334
/* stats from NIC */
311-
if (skip_nic_stats) {
335+
stats_idx = rx_qid_to_stats_idx[ring];
336+
if (skip_nic_stats || stats_idx < 0) {
312337
/* skip NIC rx stats */
313338
i += NIC_RX_STATS_REPORT_NUM;
314339
} else {
315-
stats_idx = rx_qid_to_stats_idx[ring];
316340
for (j = 0; j < NIC_RX_STATS_REPORT_NUM; j++) {
317341
u64 value =
318342
be64_to_cpu(report_stats[stats_idx + j].value);
@@ -338,7 +362,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
338362

339363
/* For tx cross-reporting stats, start from nic tx stats in report */
340364
base_stats_idx = max_stats_idx;
341-
max_stats_idx = NIC_TX_STATS_REPORT_NUM * num_tx_queues +
365+
max_stats_idx = NIC_TX_STATS_REPORT_NUM *
366+
(num_tx_queues - num_stopped_txqs) +
342367
max_stats_idx;
343368
/* Preprocess the stats report for tx, map queue id to start index */
344369
skip_nic_stats = false;
@@ -352,6 +377,10 @@ gve_get_ethtool_stats(struct net_device *netdev,
352377
skip_nic_stats = true;
353378
break;
354379
}
380+
if (queue_id < 0 || queue_id >= num_tx_queues) {
381+
net_err_ratelimited("Invalid txq id in NIC stats\n");
382+
continue;
383+
}
355384
tx_qid_to_stats_idx[queue_id] = stats_idx;
356385
}
357386
/* walk TX rings */
@@ -383,11 +412,11 @@ gve_get_ethtool_stats(struct net_device *netdev,
383412
data[i++] = gve_tx_load_event_counter(priv, tx);
384413
data[i++] = tx->dma_mapping_error;
385414
/* stats from NIC */
386-
if (skip_nic_stats) {
415+
stats_idx = tx_qid_to_stats_idx[ring];
416+
if (skip_nic_stats || stats_idx < 0) {
387417
/* skip NIC tx stats */
388418
i += NIC_TX_STATS_REPORT_NUM;
389419
} else {
390-
stats_idx = tx_qid_to_stats_idx[ring];
391420
for (j = 0; j < NIC_TX_STATS_REPORT_NUM; j++) {
392421
u64 value =
393422
be64_to_cpu(report_stats[stats_idx + j].value);

0 commit comments

Comments
 (0)