* [PATCH net-next v2 0/6] net: dsa: motorcomm: MIB fixup
@ 2026-09-04 16:29 David Yang
2026-09-04 16:29 ` [PATCH net-next v2 1/6] net: dsa: motorcomm: Rename MIB stuffs David Yang
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: David Yang @ 2026-09-04 16:29 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
Fix (theoretical) MIB inconsistency and prepare for new devices. No new
features are introduced.
v2:
- split patches
v1: https://lore.kernel.org/r/20260903143514.532023-1-mmyangfl@gmail.com
David Yang (6):
net: dsa: motorcomm: Rename MIB stuffs
net: dsa: motorcomm: Split MIB buffers
net: dsa: motorcomm: Split MIB module
net: dsa: motorcomm: Use u64_stats_t for MIB stats
net: dsa: motorcomm: Fix MIB synchronization
net: dsa: motorcomm: Use safe 64-bit counter reader
drivers/net/dsa/motorcomm/Makefile | 1 +
drivers/net/dsa/motorcomm/chip.c | 408 +++------------------------
drivers/net/dsa/motorcomm/chip.h | 122 +-------
drivers/net/dsa/motorcomm/mib.c | 431 +++++++++++++++++++++++++++++
drivers/net/dsa/motorcomm/mib.h | 167 +++++++++++
drivers/net/dsa/motorcomm/smi.c | 30 ++
drivers/net/dsa/motorcomm/smi.h | 2 +
7 files changed, 669 insertions(+), 492 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/mib.c
create mode 100644 drivers/net/dsa/motorcomm/mib.h
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next v2 1/6] net: dsa: motorcomm: Rename MIB stuffs
2026-09-04 16:29 [PATCH net-next v2 0/6] net: dsa: motorcomm: MIB fixup David Yang
@ 2026-09-04 16:29 ` David Yang
2026-09-04 16:29 ` [PATCH net-next v2 2/6] net: dsa: motorcomm: Split MIB buffers David Yang
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-09-04 16:29 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
Rename them in preparation of major MIB refactor.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 32 ++++++++++++++++----------------
drivers/net/dsa/motorcomm/chip.h | 4 ++--
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index d663af010f43..ad5fe1ccb1d7 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -545,11 +545,11 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
}
/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
-static int yt921x_read_mib(struct yt921x_priv *priv, int port)
+static int yt921x_mib_read(struct yt921x_priv *priv, int port)
{
struct yt921x_port *pp = &priv->ports[port];
struct device *dev = to_device(priv);
- struct yt921x_mib *mib = &pp->mib;
+ struct yt921x_mib_stats *mib = &pp->mib;
int res = 0;
/* Reading of yt921x_port::mib is not protected by a lock and it's vain
@@ -604,7 +604,7 @@ static int yt921x_read_mib(struct yt921x_priv *priv, int port)
return res;
}
-static void yt921x_poll_mib(struct work_struct *work)
+static void yt921x_mib_poll(struct work_struct *work)
{
struct yt921x_port *pp = container_of_const(work, struct yt921x_port,
mib_read.work);
@@ -615,7 +615,7 @@ static void yt921x_poll_mib(struct work_struct *work)
int res;
mutex_lock(&priv->reg_lock);
- res = yt921x_read_mib(priv, port);
+ res = yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
if (res)
delay *= 4;
@@ -643,11 +643,11 @@ yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
+ struct yt921x_mib_stats *mib = &pp->mib;
size_t j;
mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
+ yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
j = 0;
@@ -685,10 +685,10 @@ yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
+ struct yt921x_mib_stats *mib = &pp->mib;
mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
+ yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
mac_stats->FramesTransmittedOK = pp->tx_frames;
@@ -721,10 +721,10 @@ yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
+ struct yt921x_mib_stats *mib = &pp->mib;
mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
+ yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
@@ -750,10 +750,10 @@ yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
+ struct yt921x_mib_stats *mib = &pp->mib;
mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
+ yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
*ranges = yt921x_rmon_ranges;
@@ -786,7 +786,7 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
+ struct yt921x_mib_stats *mib = &pp->mib;
stats->rx_length_errors = mib->rx_undersize_errors +
mib->rx_fragment_errors;
@@ -822,10 +822,10 @@ yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
+ struct yt921x_mib_stats *mib = &pp->mib;
mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
+ yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
pause_stats->tx_pause_frames = mib->tx_pause;
@@ -4749,7 +4749,7 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)
struct yt921x_port *pp = &priv->ports[i];
pp->index = i;
- INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
+ INIT_DELAYED_WORK(&pp->mib_read, yt921x_mib_poll);
}
ds = &priv->ds;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 83cd454955dd..805febb48683 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -861,7 +861,7 @@ enum yt921x_fdb_entry_status {
#define yt921x_port_is_internal(port) ((port) < 8)
#define yt921x_port_is_external(port) ((port) == 8 || (port) == 9)
-struct yt921x_mib {
+struct yt921x_mib_stats {
u64 rx_broadcast;
u64 rx_pause;
u64 rx_multicast;
@@ -940,7 +940,7 @@ struct yt921x_port {
bool isolated;
struct delayed_work mib_read;
- struct yt921x_mib mib;
+ struct yt921x_mib_stats mib;
u64 rx_frames;
u64 tx_frames;
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 2/6] net: dsa: motorcomm: Split MIB buffers
2026-09-04 16:29 [PATCH net-next v2 0/6] net: dsa: motorcomm: MIB fixup David Yang
2026-09-04 16:29 ` [PATCH net-next v2 1/6] net: dsa: motorcomm: Rename MIB stuffs David Yang
@ 2026-09-04 16:29 ` David Yang
2026-09-04 16:29 ` [PATCH net-next v2 3/6] net: dsa: motorcomm: Split MIB module David Yang
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-09-04 16:29 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
With MIB improvement introduced later, struct yt921x_priv will be over
20k which is not very good for a single kmalloc(). Dynamically allocate
the MIB structures to reduce the size of struct yt921x_priv, and do not
waste memory on dummy ports.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 112 +++++++++++++++++++++++--------
drivers/net/dsa/motorcomm/chip.h | 16 +++--
2 files changed, 96 insertions(+), 32 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index ad5fe1ccb1d7..bd8486cc10ac 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -549,9 +549,12 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
{
struct yt921x_port *pp = &priv->ports[port];
struct device *dev = to_device(priv);
- struct yt921x_mib_stats *mib = &pp->mib;
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
int res = 0;
+ mib = &pm->stats;
+
/* Reading of yt921x_port::mib is not protected by a lock and it's vain
* to keep its consistency, since we have to read registers one by one
* and there is no way to make a snapshot of MIB stats.
@@ -589,11 +592,11 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
WRITE_ONCE(*valp, val);
}
- pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
+ pm->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
mib->rx_128_255byte + mib->rx_256_511byte +
mib->rx_512_1023byte + mib->rx_1024_1518byte +
mib->rx_jumbo;
- pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
+ pm->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
mib->tx_128_255byte + mib->tx_256_511byte +
mib->tx_512_1023byte + mib->tx_1024_1518byte +
mib->tx_jumbo;
@@ -606,10 +609,11 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
static void yt921x_mib_poll(struct work_struct *work)
{
- struct yt921x_port *pp = container_of_const(work, struct yt921x_port,
- mib_read.work);
- struct yt921x_priv *priv = (void *)(pp - pp->index) -
- offsetof(struct yt921x_priv, ports);
+ struct yt921x_mib *pm = container_of_const(work, struct yt921x_mib,
+ work.work);
+ struct yt921x_port *pp = pm->port;
+ struct yt921x_priv *priv = container_of_const(pp, struct yt921x_priv,
+ ports[pp->index]);
unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
int port = pp->index;
int res;
@@ -620,7 +624,7 @@ static void yt921x_mib_poll(struct work_struct *work)
if (res)
delay *= 4;
- schedule_delayed_work(&pp->mib_read, delay);
+ schedule_delayed_work(&pm->work, delay);
}
static void
@@ -643,9 +647,14 @@ yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib_stats *mib = &pp->mib;
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
size_t j;
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
mutex_lock(&priv->reg_lock);
yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
@@ -685,16 +694,21 @@ yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib_stats *mib = &pp->mib;
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
mutex_lock(&priv->reg_lock);
yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
- mac_stats->FramesTransmittedOK = pp->tx_frames;
+ mac_stats->FramesTransmittedOK = pm->tx_frames;
mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
- mac_stats->FramesReceivedOK = pp->rx_frames;
+ mac_stats->FramesReceivedOK = pm->rx_frames;
mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
mac_stats->AlignmentErrors = mib->rx_alignment_errors;
mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
@@ -721,7 +735,12 @@ yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib_stats *mib = &pp->mib;
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
mutex_lock(&priv->reg_lock);
yt921x_mib_read(priv, port);
@@ -750,7 +769,12 @@ yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib_stats *mib = &pp->mib;
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
mutex_lock(&priv->reg_lock);
yt921x_mib_read(priv, port);
@@ -786,7 +810,12 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib_stats *mib = &pp->mib;
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
stats->rx_length_errors = mib->rx_undersize_errors +
mib->rx_fragment_errors;
@@ -802,8 +831,8 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
/* stats->tx_heartbeat_errors */
stats->tx_window_errors = mib->tx_late_collisions;
- stats->rx_packets = pp->rx_frames;
- stats->tx_packets = pp->tx_frames;
+ stats->rx_packets = pm->rx_frames;
+ stats->tx_packets = pm->tx_frames;
stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
@@ -822,7 +851,12 @@ yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib_stats *mib = &pp->mib;
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
mutex_lock(&priv->reg_lock);
yt921x_mib_read(priv, port);
@@ -3953,11 +3987,14 @@ yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ struct yt921x_mib *pm;
int port = dp->index;
int res;
/* No need to sync; port control block is hold until device remove */
- cancel_delayed_work(&priv->ports[port].mib_read);
+ pm = priv->ports[port].mib;
+ if (pm)
+ cancel_delayed_work(&pm->work);
mutex_lock(&priv->reg_lock);
res = yt921x_port_down(priv, port);
@@ -3976,6 +4013,7 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ struct yt921x_mib *pm;
int port = dp->index;
int res;
@@ -3988,7 +4026,9 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,
dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
port, res);
- schedule_delayed_work(&priv->ports[port].mib_read, 0);
+ pm = priv->ports[port].mib;
+ if (pm)
+ schedule_delayed_work(&pm->work, 0);
}
static void
@@ -4108,11 +4148,35 @@ yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port,
return DSA_TAG_PROTO_YT921X;
}
+static void yt921x_dsa_port_teardown(struct dsa_switch *ds, int port)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+
+ if (pm)
+ disable_delayed_work_sync(&pm->work);
+}
+
static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)
{
struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct device *dev = to_device(priv);
+ struct yt921x_mib *pm = pp->mib;
int res;
+ if (!pm && !(BIT(port) & (priv->info->internal_mask |
+ priv->info->external_mask))) {
+ pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL);
+ if (!pm)
+ return -ENOMEM;
+ pp->mib = pm;
+
+ pm->port = pp;
+ INIT_DELAYED_WORK(&pm->work, yt921x_mib_poll);
+ }
+
mutex_lock(&priv->reg_lock);
res = yt921x_port_setup(priv, port);
mutex_unlock(&priv->reg_lock);
@@ -4667,6 +4731,7 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
/* port */
.get_tag_protocol = yt921x_dsa_get_tag_protocol,
.phylink_get_caps = yt921x_dsa_phylink_get_caps,
+ .port_teardown = yt921x_dsa_port_teardown,
.port_setup = yt921x_dsa_port_setup,
#if IS_ENABLED(CONFIG_DCB)
/* dscp */
@@ -4696,12 +4761,6 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)
if (!priv)
return;
- for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) {
- struct yt921x_port *pp = &priv->ports[i];
-
- disable_delayed_work_sync(&pp->mib_read);
- }
-
dsa_unregister_switch(&priv->ds);
for (unsigned int i = 0; i < ARRAY_SIZE(priv->acl_blks); i++) {
@@ -4749,7 +4808,6 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)
struct yt921x_port *pp = &priv->ports[i];
pp->index = i;
- INIT_DELAYED_WORK(&pp->mib_read, yt921x_mib_poll);
}
ds = &priv->ds;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 805febb48683..5bf2d3409f4b 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -913,6 +913,15 @@ struct yt921x_mib_stats {
u64 tx_oam;
};
+struct yt921x_mib {
+ struct yt921x_port *port;
+
+ struct delayed_work work;
+ struct yt921x_mib_stats stats;
+ u64 rx_frames;
+ u64 tx_frames;
+};
+
struct yt921x_acl_entry {
u32 key[2];
u32 mask[2];
@@ -939,11 +948,6 @@ struct yt921x_port {
bool hairpin;
bool isolated;
- struct delayed_work mib_read;
- struct yt921x_mib_stats mib;
- u64 rx_frames;
- u64 tx_frames;
-
#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
unsigned char led_duty;
unsigned short led_cycle;
@@ -953,6 +957,8 @@ struct yt921x_port {
struct yt921x_led *leds[YT921X_LED_GROUP_NUM];
#endif
+
+ struct yt921x_mib *mib;
};
struct yt921x_reg_ops {
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 3/6] net: dsa: motorcomm: Split MIB module
2026-09-04 16:29 [PATCH net-next v2 0/6] net: dsa: motorcomm: MIB fixup David Yang
2026-09-04 16:29 ` [PATCH net-next v2 1/6] net: dsa: motorcomm: Rename MIB stuffs David Yang
2026-09-04 16:29 ` [PATCH net-next v2 2/6] net: dsa: motorcomm: Split MIB buffers David Yang
@ 2026-09-04 16:29 ` David Yang
2026-09-04 16:29 ` [PATCH net-next v2 4/6] net: dsa: motorcomm: Use u64_stats_t for MIB stats David Yang
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-09-04 16:29 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
yt922x is going to use a different MIB scheme, split it to reduce the
length of chip.c.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/Makefile | 1 +
drivers/net/dsa/motorcomm/chip.c | 398 +---------------------------
drivers/net/dsa/motorcomm/chip.h | 124 ---------
drivers/net/dsa/motorcomm/mib.c | 408 +++++++++++++++++++++++++++++
drivers/net/dsa/motorcomm/mib.h | 159 +++++++++++
5 files changed, 569 insertions(+), 521 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/mib.c
create mode 100644 drivers/net/dsa/motorcomm/mib.h
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
index aeb12cb91f93..5c2d34ec05fc 100644
--- a/drivers/net/dsa/motorcomm/Makefile
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -2,4 +2,5 @@
obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
yt921x-objs := chip.o
yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o
+yt921x-objs += mib.o
yt921x-objs += smi.o
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index bd8486cc10ac..d8520c38f9c7 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -27,75 +27,9 @@
#include "chip.h"
#include "leds.h"
+#include "mib.h"
#include "smi.h"
-struct yt921x_mib_desc {
- unsigned int size;
- unsigned int offset;
- const char *name;
-};
-
-#define MIB_DESC(_size, _offset, _name) \
- {_size, _offset, _name}
-
-/* Must agree with yt921x_mib
- *
- * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
- * structured go to their *_stats() methods, but we need their sizes and offsets
- * to perform 32bit MIB overflow wraparound.
- */
-static const struct yt921x_mib_desc yt921x_mib_descs[] = {
- MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),
- MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),
-
- MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"),
- MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),
- 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_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),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),
-
- MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),
-
- MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"),
- MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
-};
-
struct yt921x_info {
const char *name;
u16 major;
@@ -154,14 +88,6 @@ static const struct yt921x_info yt921x_infos[] = {
#define YT921X_VID_UNWARE 4095
-/* The interval should be small enough to avoid overflow of 32bit MIBs.
- *
- * Until we can read MIBs from stats64 call directly (i.e. sleep
- * there), we have to poll stats more frequently then it is actually needed.
- * For overflow protection, normally, 100 sec interval should have been OK.
- */
-#define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ)
-
struct yt921x_reg_mdio {
struct mii_bus *bus;
int addr;
@@ -544,328 +470,6 @@ yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
return 0;
}
-/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
-static int yt921x_mib_read(struct yt921x_priv *priv, int port)
-{
- struct yt921x_port *pp = &priv->ports[port];
- struct device *dev = to_device(priv);
- struct yt921x_mib *pm = pp->mib;
- struct yt921x_mib_stats *mib;
- int res = 0;
-
- mib = &pm->stats;
-
- /* Reading of yt921x_port::mib is not protected by a lock and it's vain
- * to keep its consistency, since we have to read registers one by one
- * and there is no way to make a snapshot of MIB stats.
- *
- * Writing (by this function only) is and should be protected by
- * reg_lock.
- */
-
- 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;
- u64 *valp = &((u64 *)mib)[i];
- u32 val0;
- u64 val;
-
- res = yt921x_reg_read(priv, reg, &val0);
- if (res)
- break;
-
- if (desc->size <= 1) {
- u64 old_val = *valp;
-
- 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);
- if (res)
- break;
- val = ((u64)val1 << 32) | val0;
- }
-
- WRITE_ONCE(*valp, val);
- }
-
- pm->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
- mib->rx_128_255byte + mib->rx_256_511byte +
- mib->rx_512_1023byte + mib->rx_1024_1518byte +
- mib->rx_jumbo;
- pm->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
- mib->tx_128_255byte + mib->tx_256_511byte +
- mib->tx_512_1023byte + mib->tx_1024_1518byte +
- mib->tx_jumbo;
-
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
- port, res);
- return res;
-}
-
-static void yt921x_mib_poll(struct work_struct *work)
-{
- struct yt921x_mib *pm = container_of_const(work, struct yt921x_mib,
- work.work);
- struct yt921x_port *pp = pm->port;
- struct yt921x_priv *priv = container_of_const(pp, struct yt921x_priv,
- ports[pp->index]);
- unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
- int port = pp->index;
- int res;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
- if (res)
- delay *= 4;
-
- schedule_delayed_work(&pm->work, delay);
-}
-
-static void
-yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
- uint8_t *data)
-{
- if (stringset != ETH_SS_STATS)
- return;
-
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
- if (desc->name)
- ethtool_puts(&data, desc->name);
- }
-}
-
-static void
-yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *pm = pp->mib;
- struct yt921x_mib_stats *mib;
- size_t j;
-
- if (!pm)
- return;
- mib = &pm->stats;
-
- mutex_lock(&priv->reg_lock);
- yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- j = 0;
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
- if (!desc->name)
- continue;
-
- data[j] = ((u64 *)mib)[i];
- j++;
- }
-}
-
-static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
-{
- int cnt = 0;
-
- if (sset != ETH_SS_STATS)
- return 0;
-
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
- if (desc->name)
- cnt++;
- }
-
- return cnt;
-}
-
-static void
-yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
- struct ethtool_eth_mac_stats *mac_stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *pm = pp->mib;
- struct yt921x_mib_stats *mib;
-
- if (!pm)
- return;
- mib = &pm->stats;
-
- mutex_lock(&priv->reg_lock);
- yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- mac_stats->FramesTransmittedOK = pm->tx_frames;
- mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
- mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
- mac_stats->FramesReceivedOK = pm->rx_frames;
- mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
- mac_stats->AlignmentErrors = mib->rx_alignment_errors;
- mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
- mac_stats->FramesWithDeferredXmissions = mib->tx_deferred;
- mac_stats->LateCollisions = mib->tx_late_collisions;
- mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors;
- /* mac_stats->FramesLostDueToIntMACXmitError */
- /* mac_stats->CarrierSenseErrors */
- mac_stats->OctetsReceivedOK = mib->rx_good_bytes;
- /* mac_stats->FramesLostDueToIntMACRcvError */
- mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
- mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
- /* mac_stats->FramesWithExcessiveDeferral */
- mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
- mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
- /* mac_stats->InRangeLengthErrors */
- /* mac_stats->OutOfRangeLengthField */
- mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
-}
-
-static void
-yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
- struct ethtool_eth_ctrl_stats *ctrl_stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *pm = pp->mib;
- struct yt921x_mib_stats *mib;
-
- if (!pm)
- return;
- mib = &pm->stats;
-
- mutex_lock(&priv->reg_lock);
- yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
- ctrl_stats->MACControlFramesReceived = mib->rx_pause;
- /* ctrl_stats->UnsupportedOpcodesReceived */
-}
-
-static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
- { 0, 64 },
- { 65, 127 },
- { 128, 255 },
- { 256, 511 },
- { 512, 1023 },
- { 1024, 1518 },
- { 1519, YT921X_FRAME_SIZE_MAX },
- {}
-};
-
-static void
-yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
- struct ethtool_rmon_stats *rmon_stats,
- const struct ethtool_rmon_hist_range **ranges)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *pm = pp->mib;
- struct yt921x_mib_stats *mib;
-
- if (!pm)
- return;
- mib = &pm->stats;
-
- mutex_lock(&priv->reg_lock);
- yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- *ranges = yt921x_rmon_ranges;
-
- rmon_stats->undersize_pkts = mib->rx_undersize_errors;
- rmon_stats->oversize_pkts = mib->rx_oversize_errors;
- rmon_stats->fragments = mib->rx_alignment_errors;
- /* rmon_stats->jabbers */
-
- rmon_stats->hist[0] = mib->rx_64byte;
- rmon_stats->hist[1] = mib->rx_65_127byte;
- rmon_stats->hist[2] = mib->rx_128_255byte;
- rmon_stats->hist[3] = mib->rx_256_511byte;
- rmon_stats->hist[4] = mib->rx_512_1023byte;
- rmon_stats->hist[5] = mib->rx_1024_1518byte;
- rmon_stats->hist[6] = mib->rx_jumbo;
-
- rmon_stats->hist_tx[0] = mib->tx_64byte;
- rmon_stats->hist_tx[1] = mib->tx_65_127byte;
- rmon_stats->hist_tx[2] = mib->tx_128_255byte;
- rmon_stats->hist_tx[3] = mib->tx_256_511byte;
- rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
- rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
- rmon_stats->hist_tx[6] = mib->tx_jumbo;
-}
-
-static void
-yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
- struct rtnl_link_stats64 *stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *pm = pp->mib;
- struct yt921x_mib_stats *mib;
-
- if (!pm)
- return;
- mib = &pm->stats;
-
- stats->rx_length_errors = mib->rx_undersize_errors +
- mib->rx_fragment_errors;
- stats->rx_over_errors = mib->rx_oversize_errors;
- stats->rx_crc_errors = mib->rx_crc_errors;
- stats->rx_frame_errors = mib->rx_alignment_errors;
- /* stats->rx_fifo_errors */
- /* stats->rx_missed_errors */
-
- stats->tx_aborted_errors = mib->tx_aborted_errors;
- /* stats->tx_carrier_errors */
- stats->tx_fifo_errors = mib->tx_undersize_errors;
- /* stats->tx_heartbeat_errors */
- stats->tx_window_errors = mib->tx_late_collisions;
-
- stats->rx_packets = pm->rx_frames;
- stats->tx_packets = pm->tx_frames;
- stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
- stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
- 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_dropped = mib->rx_dropped;
- /* stats->tx_dropped */
- stats->multicast = mib->rx_multicast;
- stats->collisions = mib->tx_collisions;
-}
-
-static void
-yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
- struct ethtool_pause_stats *pause_stats)
-{
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *pm = pp->mib;
- struct yt921x_mib_stats *mib;
-
- if (!pm)
- return;
- mib = &pm->stats;
-
- mutex_lock(&priv->reg_lock);
- yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
-
- pause_stats->tx_pause_frames = mib->tx_pause;
- pause_stats->rx_pause_frames = mib->rx_pause;
-}
-
static int
yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)
{
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 5bf2d3409f4b..ce63865bb905 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -167,69 +167,6 @@
#define YT921X_EEE_CTRL 0xb0000
#define YT921X_EEE_CTRL_ENn(port) BIT(port)
-#define YT921X_MIB_CTRL 0xc0004
-#define YT921X_MIB_CTRL_CLEAN BIT(30)
-#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3)
-#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))
-#define YT921X_MIB_CTRL_ONE_PORT BIT(1)
-#define YT921X_MIB_CTRL_ALL_PORT BIT(0)
-#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port))
-#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x))
-#define YT921X_MIB_DATA_RX_BROADCAST 0x00
-#define YT921X_MIB_DATA_RX_PAUSE 0x04
-#define YT921X_MIB_DATA_RX_MULTICAST 0x08
-#define YT921X_MIB_DATA_RX_CRC_ERR 0x0c
-
-#define YT921X_MIB_DATA_RX_ALIGN_ERR 0x10
-#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR 0x14
-#define YT921X_MIB_DATA_RX_FRAG_ERR 0x18
-#define YT921X_MIB_DATA_RX_PKT_SZ_64 0x1c
-
-#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127 0x20
-#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255 0x24
-#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511 0x28
-#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023 0x2c
-
-#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518 0x30
-#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX 0x34
-/* 0x38: unused */
-#define YT921X_MIB_DATA_RX_GOOD_BYTES 0x3c
-
-/* 0x40: 64 bytes */
-#define YT921X_MIB_DATA_RX_BAD_BYTES 0x44
-/* 0x48: 64 bytes */
-#define YT921X_MIB_DATA_RX_OVERSIZE_ERR 0x4c
-
-#define YT921X_MIB_DATA_RX_DROPPED 0x50
-#define YT921X_MIB_DATA_TX_BROADCAST 0x54
-#define YT921X_MIB_DATA_TX_PAUSE 0x58
-#define YT921X_MIB_DATA_TX_MULTICAST 0x5c
-
-#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR 0x60
-#define YT921X_MIB_DATA_TX_PKT_SZ_64 0x64
-#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127 0x68
-#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255 0x6c
-
-#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511 0x70
-#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023 0x74
-#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518 0x78
-#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX 0x7c
-
-/* 0x80: unused */
-#define YT921X_MIB_DATA_TX_GOOD_BYTES 0x84
-/* 0x88: 64 bytes */
-#define YT921X_MIB_DATA_TX_COLLISION 0x8c
-
-#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION 0x90
-#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION 0x94
-#define YT921X_MIB_DATA_TX_SINGLE_COLLISION 0x98
-#define YT921X_MIB_DATA_TX_PKT 0x9c
-
-#define YT921X_MIB_DATA_TX_DEFERRED 0xa0
-#define YT921X_MIB_DATA_TX_LATE_COLLISION 0xa4
-#define YT921X_MIB_DATA_RX_OAM 0xa8
-#define YT921X_MIB_DATA_TX_OAM 0xac
-
#define YT921X_EDATA_CTRL 0xe0000
#define YT921X_EDATA_CTRL_ADDR_M GENMASK(15, 8)
#define YT921X_EDATA_CTRL_ADDR(x) FIELD_PREP(YT921X_EDATA_CTRL_ADDR_M, (x))
@@ -861,67 +798,6 @@ enum yt921x_fdb_entry_status {
#define yt921x_port_is_internal(port) ((port) < 8)
#define yt921x_port_is_external(port) ((port) == 8 || (port) == 9)
-struct yt921x_mib_stats {
- u64 rx_broadcast;
- u64 rx_pause;
- u64 rx_multicast;
- u64 rx_crc_errors;
-
- u64 rx_alignment_errors;
- u64 rx_undersize_errors;
- u64 rx_fragment_errors;
- u64 rx_64byte;
-
- u64 rx_65_127byte;
- u64 rx_128_255byte;
- u64 rx_256_511byte;
- u64 rx_512_1023byte;
-
- u64 rx_1024_1518byte;
- u64 rx_jumbo;
- u64 rx_good_bytes;
-
- u64 rx_bad_bytes;
- u64 rx_oversize_errors;
-
- u64 rx_dropped;
- u64 tx_broadcast;
- u64 tx_pause;
- u64 tx_multicast;
-
- u64 tx_undersize_errors;
- u64 tx_64byte;
- u64 tx_65_127byte;
- u64 tx_128_255byte;
-
- u64 tx_256_511byte;
- u64 tx_512_1023byte;
- u64 tx_1024_1518byte;
- u64 tx_jumbo;
-
- u64 tx_good_bytes;
- u64 tx_collisions;
-
- u64 tx_aborted_errors;
- u64 tx_multiple_collisions;
- u64 tx_single_collisions;
- u64 tx_good;
-
- u64 tx_deferred;
- u64 tx_late_collisions;
- u64 rx_oam;
- u64 tx_oam;
-};
-
-struct yt921x_mib {
- struct yt921x_port *port;
-
- struct delayed_work work;
- struct yt921x_mib_stats stats;
- u64 rx_frames;
- u64 tx_frames;
-};
-
struct yt921x_acl_entry {
u32 key[2];
u32 mask[2];
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
new file mode 100644
index 000000000000..9e0d40a26d08
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/mib.c
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#include "chip.h"
+#include "mib.h"
+#include "smi.h"
+
+struct yt921x_mib_desc {
+ unsigned int size;
+ unsigned int offset;
+ const char *name;
+};
+
+#define MIB_DESC(_size, _offset, _name) \
+ {_size, _offset, _name}
+
+/* Must agree with yt921x_mib
+ *
+ * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
+ * structured go to their *_stats() methods, but we need their sizes and offsets
+ * to perform 32bit MIB overflow wraparound.
+ */
+static const struct yt921x_mib_desc yt921x_mib_descs[] = {
+ MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),
+ MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),
+
+ MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),
+ 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_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),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),
+
+ MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
+};
+
+/* The interval should be small enough to avoid overflow of 32bit MIBs.
+ *
+ * Until we can read MIBs from stats64 call directly (i.e. sleep
+ * there), we have to poll stats more frequently then it is actually needed.
+ * For overflow protection, normally, 100 sec interval should have been OK.
+ */
+#define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ)
+
+#define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds)
+#define to_device(priv) ((priv)->ds.dev)
+
+/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
+static int yt921x_mib_read(struct yt921x_priv *priv, int port)
+{
+ struct yt921x_port *pp = &priv->ports[port];
+ struct device *dev = to_device(priv);
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+ int res = 0;
+
+ mib = &pm->stats;
+
+ /* Reading of yt921x_port::mib is not protected by a lock and it's vain
+ * to keep its consistency, since we have to read registers one by one
+ * and there is no way to make a snapshot of MIB stats.
+ *
+ * Writing (by this function only) is and should be protected by
+ * reg_lock.
+ */
+
+ 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;
+ u64 *valp = &((u64 *)mib)[i];
+ u32 val0;
+ u64 val;
+
+ res = yt921x_reg_read(priv, reg, &val0);
+ if (res)
+ break;
+
+ if (desc->size <= 1) {
+ u64 old_val = *valp;
+
+ 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);
+ if (res)
+ break;
+ val = ((u64)val1 << 32) | val0;
+ }
+
+ WRITE_ONCE(*valp, val);
+ }
+
+ pm->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
+ mib->rx_128_255byte + mib->rx_256_511byte +
+ mib->rx_512_1023byte + mib->rx_1024_1518byte +
+ mib->rx_jumbo;
+ pm->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
+ mib->tx_128_255byte + mib->tx_256_511byte +
+ mib->tx_512_1023byte + mib->tx_1024_1518byte +
+ mib->tx_jumbo;
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
+ port, res);
+ return res;
+}
+
+void yt921x_mib_poll(struct work_struct *work)
+{
+ struct yt921x_mib *pm = container_of_const(work, struct yt921x_mib,
+ work.work);
+ struct yt921x_port *pp = pm->port;
+ struct yt921x_priv *priv = container_of_const(pp, struct yt921x_priv,
+ ports[pp->index]);
+ unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
+ int port = pp->index;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_mib_read(priv, port);
+ mutex_unlock(&priv->reg_lock);
+ if (res)
+ delay *= 4;
+
+ schedule_delayed_work(&pm->work, delay);
+}
+
+void
+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ uint8_t *data)
+{
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (desc->name)
+ ethtool_puts(&data, desc->name);
+ }
+}
+
+void
+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+ size_t j;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_mib_read(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ j = 0;
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (!desc->name)
+ continue;
+
+ data[j] = ((u64 *)mib)[i];
+ j++;
+ }
+}
+
+int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+ int cnt = 0;
+
+ if (sset != ETH_SS_STATS)
+ return 0;
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (desc->name)
+ cnt++;
+ }
+
+ return cnt;
+}
+
+void
+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_mib_read(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ mac_stats->FramesTransmittedOK = pm->tx_frames;
+ mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
+ mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
+ mac_stats->FramesReceivedOK = pm->rx_frames;
+ mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
+ mac_stats->AlignmentErrors = mib->rx_alignment_errors;
+ mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
+ mac_stats->FramesWithDeferredXmissions = mib->tx_deferred;
+ mac_stats->LateCollisions = mib->tx_late_collisions;
+ mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors;
+ /* mac_stats->FramesLostDueToIntMACXmitError */
+ /* mac_stats->CarrierSenseErrors */
+ mac_stats->OctetsReceivedOK = mib->rx_good_bytes;
+ /* mac_stats->FramesLostDueToIntMACRcvError */
+ mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
+ mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
+ /* mac_stats->FramesWithExcessiveDeferral */
+ mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
+ mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
+ /* mac_stats->InRangeLengthErrors */
+ /* mac_stats->OutOfRangeLengthField */
+ mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
+}
+
+void
+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_mib_read(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
+ ctrl_stats->MACControlFramesReceived = mib->rx_pause;
+ /* ctrl_stats->UnsupportedOpcodesReceived */
+}
+
+const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
+ { 0, 64 },
+ { 65, 127 },
+ { 128, 255 },
+ { 256, 511 },
+ { 512, 1023 },
+ { 1024, 1518 },
+ { 1519, YT921X_FRAME_SIZE_MAX },
+ {}
+};
+
+void
+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_mib_read(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ *ranges = yt921x_rmon_ranges;
+
+ rmon_stats->undersize_pkts = mib->rx_undersize_errors;
+ rmon_stats->oversize_pkts = mib->rx_oversize_errors;
+ rmon_stats->fragments = mib->rx_alignment_errors;
+ /* rmon_stats->jabbers */
+
+ rmon_stats->hist[0] = mib->rx_64byte;
+ rmon_stats->hist[1] = mib->rx_65_127byte;
+ rmon_stats->hist[2] = mib->rx_128_255byte;
+ rmon_stats->hist[3] = mib->rx_256_511byte;
+ rmon_stats->hist[4] = mib->rx_512_1023byte;
+ rmon_stats->hist[5] = mib->rx_1024_1518byte;
+ rmon_stats->hist[6] = mib->rx_jumbo;
+
+ rmon_stats->hist_tx[0] = mib->tx_64byte;
+ rmon_stats->hist_tx[1] = mib->tx_65_127byte;
+ rmon_stats->hist_tx[2] = mib->tx_128_255byte;
+ rmon_stats->hist_tx[3] = mib->tx_256_511byte;
+ rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
+ rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
+ rmon_stats->hist_tx[6] = mib->tx_jumbo;
+}
+
+void
+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
+ struct rtnl_link_stats64 *stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ stats->rx_length_errors = mib->rx_undersize_errors +
+ mib->rx_fragment_errors;
+ stats->rx_over_errors = mib->rx_oversize_errors;
+ stats->rx_crc_errors = mib->rx_crc_errors;
+ stats->rx_frame_errors = mib->rx_alignment_errors;
+ /* stats->rx_fifo_errors */
+ /* stats->rx_missed_errors */
+
+ stats->tx_aborted_errors = mib->tx_aborted_errors;
+ /* stats->tx_carrier_errors */
+ stats->tx_fifo_errors = mib->tx_undersize_errors;
+ /* stats->tx_heartbeat_errors */
+ stats->tx_window_errors = mib->tx_late_collisions;
+
+ stats->rx_packets = pm->rx_frames;
+ stats->tx_packets = pm->tx_frames;
+ stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
+ stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
+ 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_dropped = mib->rx_dropped;
+ /* stats->tx_dropped */
+ stats->multicast = mib->rx_multicast;
+ stats->collisions = mib->tx_collisions;
+}
+
+void
+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
+ struct ethtool_pause_stats *pause_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+ struct yt921x_mib_stats *mib;
+
+ if (!pm)
+ return;
+ mib = &pm->stats;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_mib_read(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ pause_stats->tx_pause_frames = mib->tx_pause;
+ pause_stats->rx_pause_frames = mib->rx_pause;
+}
diff --git a/drivers/net/dsa/motorcomm/mib.h b/drivers/net/dsa/motorcomm/mib.h
new file mode 100644
index 000000000000..cfad0665baae
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/mib.h
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_MIB_H
+#define _YT_MIB_H
+
+#include <net/dsa.h>
+
+#define YT921X_MIB_CTRL 0xc0004
+#define YT921X_MIB_CTRL_CLEAN BIT(30)
+#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3)
+#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))
+#define YT921X_MIB_CTRL_ONE_PORT BIT(1)
+#define YT921X_MIB_CTRL_ALL_PORT BIT(0)
+#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port))
+#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x))
+#define YT921X_MIB_DATA_RX_BROADCAST 0x00
+#define YT921X_MIB_DATA_RX_PAUSE 0x04
+#define YT921X_MIB_DATA_RX_MULTICAST 0x08
+#define YT921X_MIB_DATA_RX_CRC_ERR 0x0c
+
+#define YT921X_MIB_DATA_RX_ALIGN_ERR 0x10
+#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR 0x14
+#define YT921X_MIB_DATA_RX_FRAG_ERR 0x18
+#define YT921X_MIB_DATA_RX_PKT_SZ_64 0x1c
+
+#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127 0x20
+#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255 0x24
+#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511 0x28
+#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023 0x2c
+
+#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518 0x30
+#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX 0x34
+/* 0x38: unused */
+#define YT921X_MIB_DATA_RX_GOOD_BYTES 0x3c
+
+/* 0x40: 64 bytes */
+#define YT921X_MIB_DATA_RX_BAD_BYTES 0x44
+/* 0x48: 64 bytes */
+#define YT921X_MIB_DATA_RX_OVERSIZE_ERR 0x4c
+
+#define YT921X_MIB_DATA_RX_DROPPED 0x50
+#define YT921X_MIB_DATA_TX_BROADCAST 0x54
+#define YT921X_MIB_DATA_TX_PAUSE 0x58
+#define YT921X_MIB_DATA_TX_MULTICAST 0x5c
+
+#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR 0x60
+#define YT921X_MIB_DATA_TX_PKT_SZ_64 0x64
+#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127 0x68
+#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255 0x6c
+
+#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511 0x70
+#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023 0x74
+#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518 0x78
+#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX 0x7c
+
+/* 0x80: unused */
+#define YT921X_MIB_DATA_TX_GOOD_BYTES 0x84
+/* 0x88: 64 bytes */
+#define YT921X_MIB_DATA_TX_COLLISION 0x8c
+
+#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION 0x90
+#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION 0x94
+#define YT921X_MIB_DATA_TX_SINGLE_COLLISION 0x98
+#define YT921X_MIB_DATA_TX_PKT 0x9c
+
+#define YT921X_MIB_DATA_TX_DEFERRED 0xa0
+#define YT921X_MIB_DATA_TX_LATE_COLLISION 0xa4
+#define YT921X_MIB_DATA_RX_OAM 0xa8
+#define YT921X_MIB_DATA_TX_OAM 0xac
+
+struct yt921x_mib_stats {
+ u64 rx_broadcast;
+ u64 rx_pause;
+ u64 rx_multicast;
+ u64 rx_crc_errors;
+
+ u64 rx_alignment_errors;
+ u64 rx_undersize_errors;
+ u64 rx_fragment_errors;
+ u64 rx_64byte;
+
+ u64 rx_65_127byte;
+ u64 rx_128_255byte;
+ u64 rx_256_511byte;
+ u64 rx_512_1023byte;
+
+ u64 rx_1024_1518byte;
+ u64 rx_jumbo;
+ u64 rx_good_bytes;
+
+ u64 rx_bad_bytes;
+ u64 rx_oversize_errors;
+
+ u64 rx_dropped;
+ u64 tx_broadcast;
+ u64 tx_pause;
+ u64 tx_multicast;
+
+ u64 tx_undersize_errors;
+ u64 tx_64byte;
+ u64 tx_65_127byte;
+ u64 tx_128_255byte;
+
+ u64 tx_256_511byte;
+ u64 tx_512_1023byte;
+ u64 tx_1024_1518byte;
+ u64 tx_jumbo;
+
+ u64 tx_good_bytes;
+ u64 tx_collisions;
+
+ u64 tx_aborted_errors;
+ u64 tx_multiple_collisions;
+ u64 tx_single_collisions;
+ u64 tx_good;
+
+ u64 tx_deferred;
+ u64 tx_late_collisions;
+ u64 rx_oam;
+ u64 tx_oam;
+};
+
+struct yt921x_mib {
+ struct yt921x_port *port;
+
+ struct delayed_work work;
+ struct yt921x_mib_stats stats;
+ u64 rx_frames;
+ u64 tx_frames;
+};
+
+void yt921x_mib_poll(struct work_struct *work);
+void
+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ uint8_t *data);
+void
+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);
+int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset);
+void
+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_mac_stats *mac_stats);
+void
+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_stats);
+void
+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges);
+void
+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
+ struct rtnl_link_stats64 *stats);
+void
+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
+ struct ethtool_pause_stats *pause_stats);
+
+#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 4/6] net: dsa: motorcomm: Use u64_stats_t for MIB stats
2026-09-04 16:29 [PATCH net-next v2 0/6] net: dsa: motorcomm: MIB fixup David Yang
` (2 preceding siblings ...)
2026-09-04 16:29 ` [PATCH net-next v2 3/6] net: dsa: motorcomm: Split MIB module David Yang
@ 2026-09-04 16:29 ` David Yang
2026-09-04 16:29 ` [PATCH net-next v2 5/6] net: dsa: motorcomm: Fix MIB synchronization David Yang
2026-09-04 16:29 ` [PATCH net-next v2 6/6] net: dsa: motorcomm: Use safe 64-bit counter reader David Yang
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-09-04 16:29 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
64-bit variables might not be atomic on 32-bit architectures, and could
lead to load/store tearing.
This is a verbatim change that replace every u64 statistics into
u64_stats_t. u64_stats_sync will be equipped in the next commit.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/mib.c | 152 ++++++++++++++++----------------
drivers/net/dsa/motorcomm/mib.h | 104 +++++++++++-----------
2 files changed, 131 insertions(+), 125 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
index 9e0d40a26d08..d8a564d83bad 100644
--- a/drivers/net/dsa/motorcomm/mib.c
+++ b/drivers/net/dsa/motorcomm/mib.c
@@ -92,22 +92,16 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
struct device *dev = to_device(priv);
struct yt921x_mib *pm = pp->mib;
struct yt921x_mib_stats *mib;
+ u64 rx_frames;
+ u64 tx_frames;
int res = 0;
mib = &pm->stats;
- /* Reading of yt921x_port::mib is not protected by a lock and it's vain
- * to keep its consistency, since we have to read registers one by one
- * and there is no way to make a snapshot of MIB stats.
- *
- * Writing (by this function only) is and should be protected by
- * reg_lock.
- */
-
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;
- u64 *valp = &((u64 *)mib)[i];
+ u64_stats_t *valp = &((u64_stats_t *)mib)[i];
u32 val0;
u64 val;
@@ -116,7 +110,7 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
break;
if (desc->size <= 1) {
- u64 old_val = *valp;
+ u64 old_val = u64_stats_read(valp);
val = (old_val & ~(u64)U32_MAX) | val0;
if (val < old_val)
@@ -130,17 +124,25 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
val = ((u64)val1 << 32) | val0;
}
- WRITE_ONCE(*valp, val);
+ u64_stats_set(valp, val);
}
- pm->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
- mib->rx_128_255byte + mib->rx_256_511byte +
- mib->rx_512_1023byte + mib->rx_1024_1518byte +
- mib->rx_jumbo;
- pm->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
- mib->tx_128_255byte + mib->tx_256_511byte +
- mib->tx_512_1023byte + mib->tx_1024_1518byte +
- mib->tx_jumbo;
+ rx_frames = u64_stats_read(&mib->rx_64byte) +
+ u64_stats_read(&mib->rx_65_127byte) +
+ u64_stats_read(&mib->rx_128_255byte) +
+ u64_stats_read(&mib->rx_256_511byte) +
+ u64_stats_read(&mib->rx_512_1023byte) +
+ u64_stats_read(&mib->rx_1024_1518byte) +
+ u64_stats_read(&mib->rx_jumbo);
+ tx_frames = u64_stats_read(&mib->tx_64byte) +
+ u64_stats_read(&mib->tx_65_127byte) +
+ u64_stats_read(&mib->tx_128_255byte) +
+ u64_stats_read(&mib->tx_256_511byte) +
+ u64_stats_read(&mib->tx_512_1023byte) +
+ u64_stats_read(&mib->tx_1024_1518byte) +
+ u64_stats_read(&mib->tx_jumbo);
+ u64_stats_set(&pm->rx_frames, rx_frames);
+ u64_stats_set(&pm->tx_frames, tx_frames);
if (res)
dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
@@ -207,7 +209,7 @@ yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
if (!desc->name)
continue;
- data[j] = ((u64 *)mib)[i];
+ data[j] = u64_stats_read(&((u64_stats_t *)mib)[i]);
j++;
}
}
@@ -246,28 +248,28 @@ yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
- mac_stats->FramesTransmittedOK = pm->tx_frames;
- mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
- mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
- mac_stats->FramesReceivedOK = pm->rx_frames;
- mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
- mac_stats->AlignmentErrors = mib->rx_alignment_errors;
- mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
- mac_stats->FramesWithDeferredXmissions = mib->tx_deferred;
- mac_stats->LateCollisions = mib->tx_late_collisions;
- mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors;
+ mac_stats->FramesTransmittedOK = u64_stats_read(&pm->tx_frames);
+ mac_stats->SingleCollisionFrames = u64_stats_read(&mib->tx_single_collisions);
+ mac_stats->MultipleCollisionFrames = u64_stats_read(&mib->tx_multiple_collisions);
+ mac_stats->FramesReceivedOK = u64_stats_read(&pm->rx_frames);
+ mac_stats->FrameCheckSequenceErrors = u64_stats_read(&mib->rx_crc_errors);
+ mac_stats->AlignmentErrors = u64_stats_read(&mib->rx_alignment_errors);
+ mac_stats->OctetsTransmittedOK = u64_stats_read(&mib->tx_good_bytes);
+ mac_stats->FramesWithDeferredXmissions = u64_stats_read(&mib->tx_deferred);
+ mac_stats->LateCollisions = u64_stats_read(&mib->tx_late_collisions);
+ mac_stats->FramesAbortedDueToXSColls = u64_stats_read(&mib->tx_aborted_errors);
/* mac_stats->FramesLostDueToIntMACXmitError */
/* mac_stats->CarrierSenseErrors */
- mac_stats->OctetsReceivedOK = mib->rx_good_bytes;
+ mac_stats->OctetsReceivedOK = u64_stats_read(&mib->rx_good_bytes);
/* mac_stats->FramesLostDueToIntMACRcvError */
- mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
- mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
+ mac_stats->MulticastFramesXmittedOK = u64_stats_read(&mib->tx_multicast);
+ mac_stats->BroadcastFramesXmittedOK = u64_stats_read(&mib->tx_broadcast);
/* mac_stats->FramesWithExcessiveDeferral */
- mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
- mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
+ mac_stats->MulticastFramesReceivedOK = u64_stats_read(&mib->rx_multicast);
+ mac_stats->BroadcastFramesReceivedOK = u64_stats_read(&mib->rx_broadcast);
/* mac_stats->InRangeLengthErrors */
/* mac_stats->OutOfRangeLengthField */
- mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
+ mac_stats->FrameTooLongErrors = u64_stats_read(&mib->rx_oversize_errors);
}
void
@@ -287,8 +289,8 @@ yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
- ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
- ctrl_stats->MACControlFramesReceived = mib->rx_pause;
+ ctrl_stats->MACControlFramesTransmitted = u64_stats_read(&mib->tx_pause);
+ ctrl_stats->MACControlFramesReceived = u64_stats_read(&mib->rx_pause);
/* ctrl_stats->UnsupportedOpcodesReceived */
}
@@ -323,26 +325,26 @@ yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
*ranges = yt921x_rmon_ranges;
- rmon_stats->undersize_pkts = mib->rx_undersize_errors;
- rmon_stats->oversize_pkts = mib->rx_oversize_errors;
- rmon_stats->fragments = mib->rx_alignment_errors;
+ 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->jabbers */
- rmon_stats->hist[0] = mib->rx_64byte;
- rmon_stats->hist[1] = mib->rx_65_127byte;
- rmon_stats->hist[2] = mib->rx_128_255byte;
- rmon_stats->hist[3] = mib->rx_256_511byte;
- rmon_stats->hist[4] = mib->rx_512_1023byte;
- rmon_stats->hist[5] = mib->rx_1024_1518byte;
- rmon_stats->hist[6] = mib->rx_jumbo;
-
- rmon_stats->hist_tx[0] = mib->tx_64byte;
- rmon_stats->hist_tx[1] = mib->tx_65_127byte;
- rmon_stats->hist_tx[2] = mib->tx_128_255byte;
- rmon_stats->hist_tx[3] = mib->tx_256_511byte;
- rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
- rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
- rmon_stats->hist_tx[6] = mib->tx_jumbo;
+ rmon_stats->hist[0] = u64_stats_read(&mib->rx_64byte);
+ rmon_stats->hist[1] = u64_stats_read(&mib->rx_65_127byte);
+ rmon_stats->hist[2] = u64_stats_read(&mib->rx_128_255byte);
+ rmon_stats->hist[3] = u64_stats_read(&mib->rx_256_511byte);
+ rmon_stats->hist[4] = u64_stats_read(&mib->rx_512_1023byte);
+ rmon_stats->hist[5] = u64_stats_read(&mib->rx_1024_1518byte);
+ rmon_stats->hist[6] = u64_stats_read(&mib->rx_jumbo);
+
+ rmon_stats->hist_tx[0] = u64_stats_read(&mib->tx_64byte);
+ rmon_stats->hist_tx[1] = u64_stats_read(&mib->tx_65_127byte);
+ rmon_stats->hist_tx[2] = u64_stats_read(&mib->tx_128_255byte);
+ rmon_stats->hist_tx[3] = u64_stats_read(&mib->tx_256_511byte);
+ rmon_stats->hist_tx[4] = u64_stats_read(&mib->tx_512_1023byte);
+ rmon_stats->hist_tx[5] = u64_stats_read(&mib->tx_1024_1518byte);
+ rmon_stats->hist_tx[6] = u64_stats_read(&mib->tx_jumbo);
}
void
@@ -358,32 +360,34 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
return;
mib = &pm->stats;
- stats->rx_length_errors = mib->rx_undersize_errors +
- mib->rx_fragment_errors;
- stats->rx_over_errors = mib->rx_oversize_errors;
- stats->rx_crc_errors = mib->rx_crc_errors;
- stats->rx_frame_errors = mib->rx_alignment_errors;
+ 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);
+ 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 */
/* stats->rx_missed_errors */
- stats->tx_aborted_errors = mib->tx_aborted_errors;
+ stats->tx_aborted_errors = u64_stats_read(&mib->tx_aborted_errors);
/* stats->tx_carrier_errors */
- stats->tx_fifo_errors = mib->tx_undersize_errors;
+ stats->tx_fifo_errors = u64_stats_read(&mib->tx_undersize_errors);
/* stats->tx_heartbeat_errors */
- stats->tx_window_errors = mib->tx_late_collisions;
-
- stats->rx_packets = pm->rx_frames;
- stats->tx_packets = pm->tx_frames;
- stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
- stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
+ stats->tx_window_errors = u64_stats_read(&mib->tx_late_collisions);
+
+ 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_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_dropped = mib->rx_dropped;
+ stats->rx_dropped = u64_stats_read(&mib->rx_dropped);
/* stats->tx_dropped */
- stats->multicast = mib->rx_multicast;
- stats->collisions = mib->tx_collisions;
+ stats->multicast = u64_stats_read(&mib->rx_multicast);
+ stats->collisions = u64_stats_read(&mib->tx_collisions);
}
void
@@ -403,6 +407,6 @@ yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
yt921x_mib_read(priv, port);
mutex_unlock(&priv->reg_lock);
- pause_stats->tx_pause_frames = mib->tx_pause;
- pause_stats->rx_pause_frames = mib->rx_pause;
+ pause_stats->tx_pause_frames = u64_stats_read(&mib->tx_pause);
+ pause_stats->rx_pause_frames = u64_stats_read(&mib->rx_pause);
}
diff --git a/drivers/net/dsa/motorcomm/mib.h b/drivers/net/dsa/motorcomm/mib.h
index cfad0665baae..d84ae2f449ac 100644
--- a/drivers/net/dsa/motorcomm/mib.h
+++ b/drivers/net/dsa/motorcomm/mib.h
@@ -6,6 +6,8 @@
#ifndef _YT_MIB_H
#define _YT_MIB_H
+#include <linux/u64_stats_sync.h>
+
#include <net/dsa.h>
#define YT921X_MIB_CTRL 0xc0004
@@ -72,55 +74,55 @@
#define YT921X_MIB_DATA_TX_OAM 0xac
struct yt921x_mib_stats {
- u64 rx_broadcast;
- u64 rx_pause;
- u64 rx_multicast;
- u64 rx_crc_errors;
-
- u64 rx_alignment_errors;
- u64 rx_undersize_errors;
- u64 rx_fragment_errors;
- u64 rx_64byte;
-
- u64 rx_65_127byte;
- u64 rx_128_255byte;
- u64 rx_256_511byte;
- u64 rx_512_1023byte;
-
- u64 rx_1024_1518byte;
- u64 rx_jumbo;
- u64 rx_good_bytes;
-
- u64 rx_bad_bytes;
- u64 rx_oversize_errors;
-
- u64 rx_dropped;
- u64 tx_broadcast;
- u64 tx_pause;
- u64 tx_multicast;
-
- u64 tx_undersize_errors;
- u64 tx_64byte;
- u64 tx_65_127byte;
- u64 tx_128_255byte;
-
- u64 tx_256_511byte;
- u64 tx_512_1023byte;
- u64 tx_1024_1518byte;
- u64 tx_jumbo;
-
- u64 tx_good_bytes;
- u64 tx_collisions;
-
- u64 tx_aborted_errors;
- u64 tx_multiple_collisions;
- u64 tx_single_collisions;
- u64 tx_good;
-
- u64 tx_deferred;
- u64 tx_late_collisions;
- u64 rx_oam;
- u64 tx_oam;
+ u64_stats_t rx_broadcast;
+ u64_stats_t rx_pause;
+ u64_stats_t rx_multicast;
+ u64_stats_t rx_crc_errors;
+
+ u64_stats_t rx_alignment_errors;
+ u64_stats_t rx_undersize_errors;
+ u64_stats_t rx_fragment_errors;
+ u64_stats_t rx_64byte;
+
+ u64_stats_t rx_65_127byte;
+ u64_stats_t rx_128_255byte;
+ u64_stats_t rx_256_511byte;
+ u64_stats_t rx_512_1023byte;
+
+ u64_stats_t rx_1024_1518byte;
+ u64_stats_t rx_jumbo;
+ u64_stats_t rx_good_bytes;
+
+ u64_stats_t rx_bad_bytes;
+ u64_stats_t rx_oversize_errors;
+
+ u64_stats_t rx_dropped;
+ u64_stats_t tx_broadcast;
+ u64_stats_t tx_pause;
+ u64_stats_t tx_multicast;
+
+ u64_stats_t tx_undersize_errors;
+ u64_stats_t tx_64byte;
+ u64_stats_t tx_65_127byte;
+ u64_stats_t tx_128_255byte;
+
+ u64_stats_t tx_256_511byte;
+ u64_stats_t tx_512_1023byte;
+ u64_stats_t tx_1024_1518byte;
+ u64_stats_t tx_jumbo;
+
+ u64_stats_t tx_good_bytes;
+ u64_stats_t tx_collisions;
+
+ u64_stats_t tx_aborted_errors;
+ u64_stats_t tx_multiple_collisions;
+ u64_stats_t tx_single_collisions;
+ u64_stats_t tx_good;
+
+ u64_stats_t tx_deferred;
+ u64_stats_t tx_late_collisions;
+ u64_stats_t rx_oam;
+ u64_stats_t tx_oam;
};
struct yt921x_mib {
@@ -128,8 +130,8 @@ struct yt921x_mib {
struct delayed_work work;
struct yt921x_mib_stats stats;
- u64 rx_frames;
- u64 tx_frames;
+ u64_stats_t rx_frames;
+ u64_stats_t tx_frames;
};
void yt921x_mib_poll(struct work_struct *work);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 5/6] net: dsa: motorcomm: Fix MIB synchronization
2026-09-04 16:29 [PATCH net-next v2 0/6] net: dsa: motorcomm: MIB fixup David Yang
` (3 preceding siblings ...)
2026-09-04 16:29 ` [PATCH net-next v2 4/6] net: dsa: motorcomm: Use u64_stats_t for MIB stats David Yang
@ 2026-09-04 16:29 ` David Yang
2026-09-04 16:29 ` [PATCH net-next v2 6/6] net: dsa: motorcomm: Use safe 64-bit counter reader David Yang
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-09-04 16:29 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
The previous MIB sync was based on the false assumption that 64-bit
variables are always atomic. To fix that:
* .get_stats64() uses u64_stats_sync, together with u64_stats_t;
* others take the free mutex lock if possible.
To minimize time of the write side of u64_stats_sync, a per-port buffer
is used so that all the stat can be written back altogether after IO
operations.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 2 +
drivers/net/dsa/motorcomm/mib.c | 90 +++++++++++++++++++-------------
drivers/net/dsa/motorcomm/mib.h | 6 +++
3 files changed, 63 insertions(+), 35 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index d8520c38f9c7..b4059053ef15 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -19,6 +19,7 @@
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/sort.h>
+#include <linux/u64_stats_sync.h>
#include <net/dsa.h>
#include <net/dscp.h>
@@ -3778,6 +3779,7 @@ static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)
pp->mib = pm;
pm->port = pp;
+ u64_stats_init(&pm->syncp);
INIT_DELAYED_WORK(&pm->work, yt921x_mib_poll);
}
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
index d8a564d83bad..31d86c3122c7 100644
--- a/drivers/net/dsa/motorcomm/mib.c
+++ b/drivers/net/dsa/motorcomm/mib.c
@@ -92,6 +92,7 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
struct device *dev = to_device(priv);
struct yt921x_mib *pm = pp->mib;
struct yt921x_mib_stats *mib;
+ u64 *buf = pm->data;
u64 rx_frames;
u64 tx_frames;
int res = 0;
@@ -101,7 +102,6 @@ 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;
- u64_stats_t *valp = &((u64_stats_t *)mib)[i];
u32 val0;
u64 val;
@@ -110,7 +110,7 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
break;
if (desc->size <= 1) {
- u64 old_val = u64_stats_read(valp);
+ u64 old_val = buf[i];
val = (old_val & ~(u64)U32_MAX) | val0;
if (val < old_val)
@@ -124,9 +124,12 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
val = ((u64)val1 << 32) | val0;
}
- u64_stats_set(valp, val);
+ buf[i] = val;
}
+ u64_stats_update_begin(&pm->syncp);
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++)
+ u64_stats_set(&((u64_stats_t *)mib)[i], buf[i]);
rx_frames = u64_stats_read(&mib->rx_64byte) +
u64_stats_read(&mib->rx_65_127byte) +
u64_stats_read(&mib->rx_128_255byte) +
@@ -143,6 +146,7 @@ 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);
+ u64_stats_update_end(&pm->syncp);
if (res)
dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
@@ -191,16 +195,16 @@ yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
struct yt921x_priv *priv = to_yt921x_priv(ds);
struct yt921x_port *pp = &priv->ports[port];
struct yt921x_mib *pm = pp->mib;
- struct yt921x_mib_stats *mib;
+ u64 *buf;
size_t j;
if (!pm)
return;
- mib = &pm->stats;
+ buf = pm->data;
mutex_lock(&priv->reg_lock);
+
yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
j = 0;
for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
@@ -209,9 +213,11 @@ yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
if (!desc->name)
continue;
- data[j] = u64_stats_read(&((u64_stats_t *)mib)[i]);
+ data[j] = buf[i];
j++;
}
+
+ mutex_unlock(&priv->reg_lock);
}
int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
@@ -245,8 +251,8 @@ yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
mib = &pm->stats;
mutex_lock(&priv->reg_lock);
+
yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
mac_stats->FramesTransmittedOK = u64_stats_read(&pm->tx_frames);
mac_stats->SingleCollisionFrames = u64_stats_read(&mib->tx_single_collisions);
@@ -270,6 +276,8 @@ yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
/* mac_stats->InRangeLengthErrors */
/* mac_stats->OutOfRangeLengthField */
mac_stats->FrameTooLongErrors = u64_stats_read(&mib->rx_oversize_errors);
+
+ mutex_unlock(&priv->reg_lock);
}
void
@@ -286,12 +294,14 @@ yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
mib = &pm->stats;
mutex_lock(&priv->reg_lock);
+
yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
ctrl_stats->MACControlFramesTransmitted = u64_stats_read(&mib->tx_pause);
ctrl_stats->MACControlFramesReceived = u64_stats_read(&mib->rx_pause);
/* ctrl_stats->UnsupportedOpcodesReceived */
+
+ mutex_unlock(&priv->reg_lock);
}
const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
@@ -320,8 +330,8 @@ yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
mib = &pm->stats;
mutex_lock(&priv->reg_lock);
+
yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
*ranges = yt921x_rmon_ranges;
@@ -345,6 +355,8 @@ yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
rmon_stats->hist_tx[4] = u64_stats_read(&mib->tx_512_1023byte);
rmon_stats->hist_tx[5] = u64_stats_read(&mib->tx_1024_1518byte);
rmon_stats->hist_tx[6] = u64_stats_read(&mib->tx_jumbo);
+
+ mutex_unlock(&priv->reg_lock);
}
void
@@ -355,39 +367,45 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
struct yt921x_port *pp = &priv->ports[port];
struct yt921x_mib *pm = pp->mib;
struct yt921x_mib_stats *mib;
+ unsigned int start;
if (!pm)
return;
mib = &pm->stats;
- 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);
- 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 */
- /* stats->rx_missed_errors */
-
- 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_heartbeat_errors */
- stats->tx_window_errors = u64_stats_read(&mib->tx_late_collisions);
-
- 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;
+ do {
+ 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);
+ 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 */
+ /* stats->rx_missed_errors */
+
+ 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_heartbeat_errors */
+ stats->tx_window_errors = u64_stats_read(&mib->tx_late_collisions);
+
+ 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_dropped = u64_stats_read(&mib->rx_dropped);
+ /* stats->tx_dropped */
+ stats->multicast = u64_stats_read(&mib->rx_multicast);
+ 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_dropped = u64_stats_read(&mib->rx_dropped);
- /* stats->tx_dropped */
- stats->multicast = u64_stats_read(&mib->rx_multicast);
- stats->collisions = u64_stats_read(&mib->tx_collisions);
}
void
@@ -404,9 +422,11 @@ yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
mib = &pm->stats;
mutex_lock(&priv->reg_lock);
+
yt921x_mib_read(priv, port);
- mutex_unlock(&priv->reg_lock);
pause_stats->tx_pause_frames = u64_stats_read(&mib->tx_pause);
pause_stats->rx_pause_frames = u64_stats_read(&mib->rx_pause);
+
+ mutex_unlock(&priv->reg_lock);
}
diff --git a/drivers/net/dsa/motorcomm/mib.h b/drivers/net/dsa/motorcomm/mib.h
index d84ae2f449ac..33992a5f9bdc 100644
--- a/drivers/net/dsa/motorcomm/mib.h
+++ b/drivers/net/dsa/motorcomm/mib.h
@@ -125,13 +125,19 @@ struct yt921x_mib_stats {
u64_stats_t tx_oam;
};
+#define YT921X_MIB_NUM (sizeof(struct yt921x_mib_stats) / sizeof(u64_stats_t))
+
struct yt921x_mib {
struct yt921x_port *port;
struct delayed_work work;
+ struct u64_stats_sync syncp;
+ /* protected by syncp OR priv->reg_lock */
struct yt921x_mib_stats stats;
u64_stats_t rx_frames;
u64_stats_t tx_frames;
+ /* protected by priv->reg_lock */
+ u64 data[YT921X_MIB_NUM];
};
void yt921x_mib_poll(struct work_struct *work);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 6/6] net: dsa: motorcomm: Use safe 64-bit counter reader
2026-09-04 16:29 [PATCH net-next v2 0/6] net: dsa: motorcomm: MIB fixup David Yang
` (4 preceding siblings ...)
2026-09-04 16:29 ` [PATCH net-next v2 5/6] net: dsa: motorcomm: Fix MIB synchronization David Yang
@ 2026-09-04 16:29 ` David Yang
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-09-04 16:29 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
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>
---
drivers/net/dsa/motorcomm/mib.c | 19 +++++++++----------
drivers/net/dsa/motorcomm/smi.c | 30 ++++++++++++++++++++++++++++++
drivers/net/dsa/motorcomm/smi.h | 2 ++
3 files changed, 41 insertions(+), 10 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c
index 31d86c3122c7..8929476a976f 100644
--- a/drivers/net/dsa/motorcomm/mib.c
+++ b/drivers/net/dsa/motorcomm/mib.c
@@ -102,26 +102,25 @@ 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;
diff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c
index bf3adfd64165..27086602997b 100644
--- a/drivers/net/dsa/motorcomm/smi.c
+++ b/drivers/net/dsa/motorcomm/smi.c
@@ -63,6 +63,36 @@ 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 64bit counter */
+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;
+
+ for (int i = 0; i < 16; i++) {
+ 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) {
+ *valp = ((u64)hi << 32) | lo;
+ return 0;
+ }
+ old_lo = lo;
+ }
+
+ return -ETIMEDOUT;
+}
+
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 related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-04 16:31 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04 16:29 [PATCH net-next v2 0/6] net: dsa: motorcomm: MIB fixup David Yang
2026-09-04 16:29 ` [PATCH net-next v2 1/6] net: dsa: motorcomm: Rename MIB stuffs David Yang
2026-09-04 16:29 ` [PATCH net-next v2 2/6] net: dsa: motorcomm: Split MIB buffers David Yang
2026-09-04 16:29 ` [PATCH net-next v2 3/6] net: dsa: motorcomm: Split MIB module David Yang
2026-09-04 16:29 ` [PATCH net-next v2 4/6] net: dsa: motorcomm: Use u64_stats_t for MIB stats David Yang
2026-09-04 16:29 ` [PATCH net-next v2 5/6] net: dsa: motorcomm: Fix MIB synchronization David Yang
2026-09-04 16:29 ` [PATCH net-next v2 6/6] net: dsa: motorcomm: Use safe 64-bit counter reader David Yang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox