* [PATCH net-next 1/4] net: dsa: motorcomm: Fix stats64 error counter mapping
2026-11-07 13:20 [PATCH net-next 0/4] net: dsa: motorcomm: MIB fixup David Yang
@ 2026-11-07 13:20 ` David Yang
2026-11-07 13:20 ` [PATCH net-next 2/4] net: dsa: motorcomm: Fix RMON fragments stat David Yang
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: David Yang @ 2026-11-07 13:20 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
rtnl_link_stats64 wants aFrameTooLongErrors in rx_length_errors, and the
eth-mac stats already report the oversize counter there as
FrameTooLongErrors; move it from rx_over_errors, which is a RX FIFO
overflow counter.
Drop the tx_undersize to tx_fifo_errors mapping as well; a runt frame is
not a FIFO underrun. Report it as a TxUndersize ethtool stat instead.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/mib.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
index 6b170fc6e187..5c027edb71e3 100644
--- a/drivers/net/dsa/motorcomm/mib.c
+++ b/drivers/net/dsa/motorcomm/mib.c
@@ -50,7 +50,7 @@ static const struct yt921x_mib_desc yt921x_mib_descs[] = {
MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),
MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, "TxUndersize"),
MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),
MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),
MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),
@@ -376,8 +376,9 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
start = u64_stats_fetch_begin(&pm->syncp);
stats->rx_length_errors = u64_stats_read(&mib->rx_undersize_errors) +
- u64_stats_read(&mib->rx_fragment_errors);
- stats->rx_over_errors = u64_stats_read(&mib->rx_oversize_errors);
+ u64_stats_read(&mib->rx_fragment_errors) +
+ u64_stats_read(&mib->rx_oversize_errors);
+ /* stats->rx_over_errors */
stats->rx_crc_errors = u64_stats_read(&mib->rx_crc_errors);
stats->rx_frame_errors = u64_stats_read(&mib->rx_alignment_errors);
/* stats->rx_fifo_errors */
@@ -385,7 +386,7 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
stats->tx_aborted_errors = u64_stats_read(&mib->tx_aborted_errors);
/* stats->tx_carrier_errors */
- stats->tx_fifo_errors = u64_stats_read(&mib->tx_undersize_errors);
+ /* stats->tx_fifo_errors */
/* stats->tx_heartbeat_errors */
stats->tx_window_errors = u64_stats_read(&mib->tx_late_collisions);
@@ -401,10 +402,9 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
stats->collisions = u64_stats_read(&mib->tx_collisions);
} while (u64_stats_fetch_retry(&pm->syncp, start));
- stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
- stats->rx_crc_errors + stats->rx_frame_errors;
- stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
- stats->tx_window_errors;
+ stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
+ stats->rx_frame_errors;
+ stats->tx_errors = stats->tx_aborted_errors + stats->tx_window_errors;
}
void
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH net-next 2/4] net: dsa: motorcomm: Fix RMON fragments stat
2026-11-07 13:20 [PATCH net-next 0/4] net: dsa: motorcomm: MIB fixup David Yang
2026-11-07 13:20 ` [PATCH net-next 1/4] net: dsa: motorcomm: Fix stats64 error counter mapping David Yang
@ 2026-11-07 13:20 ` David Yang
2026-11-07 13:20 ` [PATCH net-next 3/4] net: dsa: motorcomm: Use safe 64-bit counter reader David Yang
2026-11-07 13:20 ` [PATCH net-next 4/4] net: dsa: motorcomm: Prevent rx_bytes/tx_bytes underflow David Yang
3 siblings, 0 replies; 6+ messages in thread
From: David Yang @ 2026-11-07 13:20 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
etherStatsFragments counts frames with a bad FCS due to fragmentation;
report the dedicated RX fragment error counter instead of the alignment
error one, which is already exported as AlignmentErrors and
rx_frame_errors.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/mib.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
index 5c027edb71e3..90b0ef670aa3 100644
--- a/drivers/net/dsa/motorcomm/mib.c
+++ b/drivers/net/dsa/motorcomm/mib.c
@@ -336,7 +336,7 @@ yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
rmon_stats->undersize_pkts = u64_stats_read(&mib->rx_undersize_errors);
rmon_stats->oversize_pkts = u64_stats_read(&mib->rx_oversize_errors);
- rmon_stats->fragments = u64_stats_read(&mib->rx_alignment_errors);
+ rmon_stats->fragments = u64_stats_read(&mib->rx_fragment_errors);
/* rmon_stats->jabbers */
rmon_stats->hist[0] = u64_stats_read(&mib->rx_64byte);
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next 3/4] net: dsa: motorcomm: Use safe 64-bit counter reader
2026-11-07 13:20 [PATCH net-next 0/4] net: dsa: motorcomm: MIB fixup David Yang
2026-11-07 13:20 ` [PATCH net-next 1/4] net: dsa: motorcomm: Fix stats64 error counter mapping David Yang
2026-11-07 13:20 ` [PATCH net-next 2/4] net: dsa: motorcomm: Fix RMON fragments stat David Yang
@ 2026-11-07 13:20 ` David Yang
2026-11-07 13:20 ` [PATCH net-next 4/4] net: dsa: motorcomm: Prevent rx_bytes/tx_bytes underflow David Yang
3 siblings, 0 replies; 6+ messages in thread
From: David Yang @ 2026-11-07 13:20 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
64-bit counter registers are read as two separate 32-bit transactions.
If the low word wraps from about 0xffffffff to a small value while the
high word increments between the two transactions, the recombined value
becomes ((new_high << 32) | old_low), producing a spurious ~4 GiB jump.
Use a safe counter reader for this.
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/motorcomm/mib.c | 21 +++++++++-----------
drivers/net/dsa/motorcomm/smi.c | 35 +++++++++++++++++++++++++++++++++
drivers/net/dsa/motorcomm/smi.h | 2 ++
3 files changed, 46 insertions(+), 12 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
index 90b0ef670aa3..b45bc8211741 100644
--- a/drivers/net/dsa/motorcomm/mib.c
+++ b/drivers/net/dsa/motorcomm/mib.c
@@ -102,29 +102,26 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
u32 reg = YT921X_MIBn_DATA0(port) + desc->offset;
- u32 val0;
- u64 val;
-
- res = yt921x_reg_read(priv, reg, &val0);
- if (res)
- break;
if (desc->size <= 1) {
u64 old_val = buf[i];
+ u32 val0;
+ u64 val;
+
+ res = yt921x_reg_read(priv, reg, &val0);
+ if (res)
+ break;
val = (old_val & ~(u64)U32_MAX) | val0;
if (val < old_val)
val += 1ull << 32;
- } else {
- u32 val1;
- res = yt921x_reg_read(priv, reg + 4, &val1);
+ buf[i] = val;
+ } else {
+ res = yt921x_counter_read(priv, reg, &buf[i]);
if (res)
break;
- val = ((u64)val1 << 32) | val0;
}
-
- buf[i] = val;
}
flags = u64_stats_update_begin_irqsave(&pm->syncp);
diff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c
index bf3adfd64165..16f62409422d 100644
--- a/drivers/net/dsa/motorcomm/smi.c
+++ b/drivers/net/dsa/motorcomm/smi.c
@@ -63,6 +63,41 @@ int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
return yt921x_reg_write(priv, reg, u);
}
+/* Reliably read a 64-bit counter, assuming the counter isn't that "fast" */
+int yt921x_counter_read(struct yt921x_priv *priv, u32 reg, u64 *valp)
+{
+ u32 old_lo;
+ int res;
+ u32 hi;
+ u32 lo;
+
+ res = yt921x_reg_read(priv, reg, &old_lo);
+ if (res)
+ return res;
+
+ res = yt921x_reg_read(priv, reg + 4, &hi);
+ if (res)
+ return res;
+
+ res = yt921x_reg_read(priv, reg, &lo);
+ if (res)
+ return res;
+
+ if (lo < old_lo) {
+ u32 new_hi;
+
+ res = yt921x_reg_read(priv, reg + 4, &new_hi);
+ if (res)
+ return res;
+ if (new_hi != hi + 1)
+ return -EIO;
+ hi = new_hi;
+ }
+
+ *valp = ((u64)hi << 32) | lo;
+ return 0;
+}
+
static int
yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
unsigned int num_regs)
diff --git a/drivers/net/dsa/motorcomm/smi.h b/drivers/net/dsa/motorcomm/smi.h
index 212e20f71d80..d34240548af7 100644
--- a/drivers/net/dsa/motorcomm/smi.h
+++ b/drivers/net/dsa/motorcomm/smi.h
@@ -35,6 +35,8 @@ yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
}
+int yt921x_counter_read(struct yt921x_priv *priv, u32 reg, u64 *valp);
+
/* Some multi-word registers, like VLANn_CTRL, should be treated as a single
* long register. More specifically, writes to parts of its words won't become
* visible, until the last word is written.
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH net-next 4/4] net: dsa: motorcomm: Prevent rx_bytes/tx_bytes underflow
2026-11-07 13:20 [PATCH net-next 0/4] net: dsa: motorcomm: MIB fixup David Yang
` (2 preceding siblings ...)
2026-11-07 13:20 ` [PATCH net-next 3/4] net: dsa: motorcomm: Use safe 64-bit counter reader David Yang
@ 2026-11-07 13:20 ` David Yang
3 siblings, 0 replies; 6+ messages in thread
From: David Yang @ 2026-11-07 13:20 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
There is no direct rx_bytes/tx_bytes counter excluding the FCS; both
have to be derived as good_bytes - ETH_FCS_LEN * frames. Three hazards
can pair a frame count with a mismatched byte count and make the
subtraction underflow, reporting rx_bytes or tx_bytes as about 2^64:
- a register read fails midway and the half-refreshed buffer is
published as is, pairing fresh frame counters with stale byte
counters;
- get_stats64() on 64-bit kernels reads the published counters without
any sequencing across fields (u64_stats_sync is a no-op there), so a
writer update can slip between the frame and byte reads;
- the MIB bank cannot be read as a whole; counters keep counting while
the read loop walks the table.
Handle this in the writer: do not publish a half-read buffer, keep the
last complete sample on read failure, do the arithmetic on the complete
sample under priv->reg_lock, and publish the derived byte counters only
when check_sub_overflow() succeeds, so that get_stats64() copies plain
published values.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/mib.c | 25 +++++++++++++++++--------
drivers/net/dsa/motorcomm/mib.h | 2 ++
2 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
index b45bc8211741..cb3912493cfb 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;
@@ -124,6 +126,12 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
}
}
+ 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]);
@@ -143,12 +151,15 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
u64_stats_read(&mib->tx_jumbo);
u64_stats_set(&pm->rx_frames, rx_frames);
u64_stats_set(&pm->tx_frames, tx_frames);
+ if (!check_sub_overflow(u64_stats_read(&mib->rx_good_bytes),
+ ETH_FCS_LEN * rx_frames, &rx_bytes))
+ u64_stats_set(&pm->rx_bytes, rx_bytes);
+ if (!check_sub_overflow(u64_stats_read(&mib->tx_good_bytes),
+ ETH_FCS_LEN * tx_frames, &tx_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)
@@ -389,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
^ permalink raw reply [flat|nested] 6+ messages in thread