gve: Fix race condition on tx->dropped_pkt update

The tx->dropped_pkt counter is a 64-bit integer that is incremented
directly. On 32-bit architectures, this operation is not atomic and
can lead to read/write tearing if a reader accesses the counter during
the update. This can result in incorrect values being reported for
dropped packets.

To prevent this potential data corruption, wrap the increment
operation with u64_stats_update_begin() and u64_stats_update_end().
This ensures that updates to the 64-bit counter are atomic, even on
32-bit systems, by using a sequence lock.

The u64_stats_sync API requires the writer to have exclusive access,
which is already provided in this context by the network stack's
serialization of the transmit path (net_device_ops::ndo_start_xmit
[1]) for a given queue.

[1]: https://www.kernel.org/doc/Documentation/networking/netdevices.txt

Signed-off-by: Max Yuan <maxyuan@google.com>
Reviewed-by: Jordan Rhee <jordanrhee@google.com>
Signed-off-by: Harshitha Ramamurthy <hramamurthy@google.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Max Yuan
2025-11-27 00:07:51 +00:00
committed by Jakub Kicinski
parent 4c03592689
commit 858b1d07e4
2 changed files with 8 additions and 0 deletions

View File

@@ -730,7 +730,9 @@ unmap_drop:
gve_tx_unmap_buf(tx->dev, &tx->info[idx & tx->mask]);
}
drop:
u64_stats_update_begin(&tx->statss);
tx->dropped_pkt++;
u64_stats_update_end(&tx->statss);
return 0;
}

View File

@@ -1002,7 +1002,9 @@ static int gve_try_tx_skb(struct gve_priv *priv, struct gve_tx_ring *tx,
return 0;
drop:
u64_stats_update_begin(&tx->statss);
tx->dropped_pkt++;
u64_stats_update_end(&tx->statss);
dev_kfree_skb_any(skb);
return 0;
}
@@ -1324,7 +1326,11 @@ static void remove_miss_completions(struct gve_priv *priv,
/* This indicates the packet was dropped. */
dev_kfree_skb_any(pending_packet->skb);
pending_packet->skb = NULL;
u64_stats_update_begin(&tx->statss);
tx->dropped_pkt++;
u64_stats_update_end(&tx->statss);
net_err_ratelimited("%s: No reinjection completion was received for: %d.\n",
priv->dev->name,
(int)(pending_packet - tx->dqo.pending_packets));