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>
This commit is contained in:
parent
770f52d5a0
commit
af9bcf910b
@ -8,6 +8,7 @@
|
||||
#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)
|
||||
@ -165,6 +166,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
|
||||
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;
|
||||
@ -181,12 +184,23 @@ gve_get_ethtool_stats(struct net_device *netdev,
|
||||
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,
|
||||
@ -260,7 +274,13 @@ gve_get_ethtool_stats(struct net_device *netdev,
|
||||
/* 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;
|
||||
@ -274,6 +294,10 @@ gve_get_ethtool_stats(struct net_device *netdev,
|
||||
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 */
|
||||
@ -308,11 +332,11 @@ gve_get_ethtool_stats(struct net_device *netdev,
|
||||
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);
|
||||
@ -338,7 +362,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
|
||||
|
||||
/* 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;
|
||||
@ -352,6 +377,10 @@ gve_get_ethtool_stats(struct net_device *netdev,
|
||||
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 */
|
||||
@ -383,11 +412,11 @@ gve_get_ethtool_stats(struct net_device *netdev,
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user