Skip to content

Commit 7b9ebcc

Browse files
Debarghya Kundukuba-moo
authored andcommitted
gve: Fix stats report corruption on queue count change
The driver and the NIC share a region in memory for stats reporting. The NIC calculates its offset into this region based on the total size of the stats region and the size of the NIC's stats. When the number of queues is changed, the driver's stats region is resized. If the queue count is increased, the NIC can write past the end of the allocated stats region, causing memory corruption. If the queue count is decreased, there is a gap between the driver and NIC stats, leading to incorrect stats reporting. This change fixes the issue by allocating stats region with maximum size, and the offset calculation for NIC stats is changed to match with the calculation of the NIC. Cc: stable@vger.kernel.org Fixes: 24aeb56 ("gve: Add Gvnic stats AQ command and ethtool show/set-priv-flags.") Signed-off-by: Debarghya Kundu <debarghyak@google.com> Reviewed-by: Joshua Washington <joshwash@google.com> Signed-off-by: Harshitha Ramamurthy <hramamurthy@google.com> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Link: https://patch.msgid.link/20260202193925.3106272-2-hramamurthy@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 6788d44 commit 7b9ebcc

File tree

2 files changed

+36
-22
lines changed

2 files changed

+36
-22
lines changed

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

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
156156
u64 rx_buf_alloc_fail, rx_desc_err_dropped_pkt, rx_hsplit_unsplit_pkt,
157157
rx_pkts, rx_hsplit_pkt, rx_skb_alloc_fail, rx_bytes, tx_pkts, tx_bytes,
158158
tx_dropped;
159-
int stats_idx, base_stats_idx, max_stats_idx;
159+
int rx_base_stats_idx, max_rx_stats_idx, max_tx_stats_idx;
160+
int stats_idx, stats_region_len, nic_stats_len;
160161
struct stats *report_stats;
161162
int *rx_qid_to_stats_idx;
162163
int *tx_qid_to_stats_idx;
@@ -265,20 +266,38 @@ gve_get_ethtool_stats(struct net_device *netdev,
265266
data[i++] = priv->stats_report_trigger_cnt;
266267
i = GVE_MAIN_STATS_LEN;
267268

268-
/* For rx cross-reporting stats, start from nic rx stats in report */
269-
base_stats_idx = GVE_TX_STATS_REPORT_NUM * num_tx_queues +
270-
GVE_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues;
271-
/* The boundary between driver stats and NIC stats shifts if there are
272-
* stopped queues.
273-
*/
274-
base_stats_idx += NIC_RX_STATS_REPORT_NUM * num_stopped_rxqs +
275-
NIC_TX_STATS_REPORT_NUM * num_stopped_txqs;
276-
max_stats_idx = NIC_RX_STATS_REPORT_NUM *
277-
(priv->rx_cfg.num_queues - num_stopped_rxqs) +
278-
base_stats_idx;
269+
rx_base_stats_idx = 0;
270+
max_rx_stats_idx = 0;
271+
max_tx_stats_idx = 0;
272+
stats_region_len = priv->stats_report_len -
273+
sizeof(struct gve_stats_report);
274+
nic_stats_len = (NIC_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues +
275+
NIC_TX_STATS_REPORT_NUM * num_tx_queues) * sizeof(struct stats);
276+
if (unlikely((stats_region_len -
277+
nic_stats_len) % sizeof(struct stats))) {
278+
net_err_ratelimited("Starting index of NIC stats should be multiple of stats size");
279+
} else {
280+
/* For rx cross-reporting stats,
281+
* start from nic rx stats in report
282+
*/
283+
rx_base_stats_idx = (stats_region_len - nic_stats_len) /
284+
sizeof(struct stats);
285+
/* The boundary between driver stats and NIC stats
286+
* shifts if there are stopped queues
287+
*/
288+
rx_base_stats_idx += NIC_RX_STATS_REPORT_NUM *
289+
num_stopped_rxqs + NIC_TX_STATS_REPORT_NUM *
290+
num_stopped_txqs;
291+
max_rx_stats_idx = NIC_RX_STATS_REPORT_NUM *
292+
(priv->rx_cfg.num_queues - num_stopped_rxqs) +
293+
rx_base_stats_idx;
294+
max_tx_stats_idx = NIC_TX_STATS_REPORT_NUM *
295+
(num_tx_queues - num_stopped_txqs) +
296+
max_rx_stats_idx;
297+
}
279298
/* Preprocess the stats report for rx, map queue id to start index */
280299
skip_nic_stats = false;
281-
for (stats_idx = base_stats_idx; stats_idx < max_stats_idx;
300+
for (stats_idx = rx_base_stats_idx; stats_idx < max_rx_stats_idx;
282301
stats_idx += NIC_RX_STATS_REPORT_NUM) {
283302
u32 stat_name = be32_to_cpu(report_stats[stats_idx].stat_name);
284303
u32 queue_id = be32_to_cpu(report_stats[stats_idx].queue_id);
@@ -354,14 +373,9 @@ gve_get_ethtool_stats(struct net_device *netdev,
354373
i += priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS;
355374
}
356375

357-
/* For tx cross-reporting stats, start from nic tx stats in report */
358-
base_stats_idx = max_stats_idx;
359-
max_stats_idx = NIC_TX_STATS_REPORT_NUM *
360-
(num_tx_queues - num_stopped_txqs) +
361-
max_stats_idx;
362-
/* Preprocess the stats report for tx, map queue id to start index */
363376
skip_nic_stats = false;
364-
for (stats_idx = base_stats_idx; stats_idx < max_stats_idx;
377+
/* NIC TX stats start right after NIC RX stats */
378+
for (stats_idx = max_rx_stats_idx; stats_idx < max_tx_stats_idx;
365379
stats_idx += NIC_TX_STATS_REPORT_NUM) {
366380
u32 stat_name = be32_to_cpu(report_stats[stats_idx].stat_name);
367381
u32 queue_id = be32_to_cpu(report_stats[stats_idx].queue_id);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,9 @@ static int gve_alloc_stats_report(struct gve_priv *priv)
283283
int tx_stats_num, rx_stats_num;
284284

285285
tx_stats_num = (GVE_TX_STATS_REPORT_NUM + NIC_TX_STATS_REPORT_NUM) *
286-
gve_num_tx_queues(priv);
286+
priv->tx_cfg.max_queues;
287287
rx_stats_num = (GVE_RX_STATS_REPORT_NUM + NIC_RX_STATS_REPORT_NUM) *
288-
priv->rx_cfg.num_queues;
288+
priv->rx_cfg.max_queues;
289289
priv->stats_report_len = struct_size(priv->stats_report, stats,
290290
size_add(tx_stats_num, rx_stats_num));
291291
priv->stats_report =

0 commit comments

Comments
 (0)