[PATCH net v2] net: dsa: motorcomm: Publish a consistent MIB sample
From: David Yang
Date: Tue Nov 03 2026 - 08:52:46 EST
get_stats64() derived rx_bytes and tx_bytes by subtracting ETH_FCS_LEN
per packet from the published byte counters, but u64_stats_sync gives
neither ordering nor retry across a multi-field read on 64-bit kernels,
and a failed register read published a partially refreshed buffer as
well. A fresh packet count could then pair with a stale byte counter,
and the subtraction underflowed on unsigned arithmetic, reporting
rx_bytes or tx_bytes as about 2^64.
Do the arithmetic in the writer, on the complete sample collected under
priv->reg_lock, and publish the adjusted byte counters next to the
frame totals, so that get_stats64() only copies published values. On a
failed register read, keep the last complete sample.
Found by LLM. The issue is transient and abnormal values will be flushed
by the next function call.
Fixes: 186623f4aa72 ("net: dsa: yt921x: Add support for Motorcomm YT921x")
Signed-off-by: David Yang <mmyangfl@xxxxxxxxx>
---
v2:
- more issues found by sashiko
v1: https://lore.kernel.org/r/20261102101334.3525485-1-mmyangfl@xxxxxxxxx
drivers/net/dsa/motorcomm/mib.c | 23 +++++++++++++++--------
drivers/net/dsa/motorcomm/mib.h | 2 ++
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
index 6b170fc6e187..d47aafc468a3 100644
--- a/drivers/net/dsa/motorcomm/mib.c
+++ b/drivers/net/dsa/motorcomm/mib.c
@@ -95,6 +95,8 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
unsigned long flags;
u64 rx_frames;
u64 tx_frames;
+ u64 rx_bytes;
+ u64 tx_bytes;
int res = 0;
mib = &pm->stats;
@@ -127,6 +129,12 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
buf[i] = val;
}
+ if (res) {
+ dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
+ port, res);
+ return res;
+ }
+
flags = u64_stats_update_begin_irqsave(&pm->syncp);
for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++)
u64_stats_set(&((u64_stats_t *)mib)[i], buf[i]);
@@ -144,14 +152,15 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
u64_stats_read(&mib->tx_512_1023byte) +
u64_stats_read(&mib->tx_1024_1518byte) +
u64_stats_read(&mib->tx_jumbo);
+ rx_bytes = u64_stats_read(&mib->rx_good_bytes) - ETH_FCS_LEN * rx_frames;
+ tx_bytes = u64_stats_read(&mib->tx_good_bytes) - ETH_FCS_LEN * tx_frames;
u64_stats_set(&pm->rx_frames, rx_frames);
u64_stats_set(&pm->tx_frames, tx_frames);
+ u64_stats_set(&pm->rx_bytes, rx_bytes);
+ u64_stats_set(&pm->tx_bytes, tx_bytes);
u64_stats_update_end_irqrestore(&pm->syncp, flags);
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
- port, res);
- return res;
+ return 0;
}
void yt921x_mib_poll(struct work_struct *work)
@@ -391,10 +400,8 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
stats->rx_packets = u64_stats_read(&pm->rx_frames);
stats->tx_packets = u64_stats_read(&pm->tx_frames);
- stats->rx_bytes = u64_stats_read(&mib->rx_good_bytes) -
- ETH_FCS_LEN * stats->rx_packets;
- stats->tx_bytes = u64_stats_read(&mib->tx_good_bytes) -
- ETH_FCS_LEN * stats->tx_packets;
+ stats->rx_bytes = u64_stats_read(&pm->rx_bytes);
+ stats->tx_bytes = u64_stats_read(&pm->tx_bytes);
stats->rx_dropped = u64_stats_read(&mib->rx_dropped);
/* stats->tx_dropped */
stats->multicast = u64_stats_read(&mib->rx_multicast);
diff --git a/drivers/net/dsa/motorcomm/mib.h b/drivers/net/dsa/motorcomm/mib.h
index 9c24a52d46cc..2e2b832cb245 100644
--- a/drivers/net/dsa/motorcomm/mib.h
+++ b/drivers/net/dsa/motorcomm/mib.h
@@ -136,6 +136,8 @@ struct yt921x_mib {
struct yt921x_mib_stats stats;
u64_stats_t rx_frames;
u64_stats_t tx_frames;
+ u64_stats_t rx_bytes;
+ u64_stats_t tx_bytes;
/* protected by priv->reg_lock */
u64 data[YT921X_MIB_STAT_NUM];
};
--
2.53.0