[PATCH net-next v5 3/6] net: dsa: motorcomm: Split MIB buffers

From: David Yang

Date: Sun Oct 04 2026 - 12:09:32 EST


With MIB improvement introduced later, struct yt921x_priv will be over
20 KiB which is not very good for a single kmalloc(). Dynamically
allocate the MIB structures to reduce the size of struct yt921x_priv
(and adjust polling work lifecycle), and do not waste memory on dummy
ports.

Signed-off-by: David Yang <mmyangfl@xxxxxxxxx>
Reviewed-by: Andrew Lunn <andrew@xxxxxxx>
---
drivers/net/dsa/motorcomm/chip.c | 124 ++++++++++++++++++++++++-------
drivers/net/dsa/motorcomm/chip.h | 20 +++--
2 files changed, 112 insertions(+), 32 deletions(-)

diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 690ee0eb5192..8341d4d1c784 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -270,10 +270,13 @@ static int yt921x_mib_read(struct yt921x_priv *priv, int port)
{
struct device *dev = yt921x_priv_to_device(priv);
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;
int res = 0;

- /* Reading of yt921x_port::mib is not protected by a lock and it's vain
+ mib = &pm->stats;
+
+ /* Reading of yt921x_mib_stats 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.
*
@@ -310,11 +313,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;
@@ -327,8 +330,9 @@ 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_mib *pm = container_of_const(work, struct yt921x_mib,
+ work.work);
+ struct yt921x_port *pp = pm->port;
unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
struct yt921x_priv *priv = yt921x_port_to_priv(pp);
int port = pp->index;
@@ -340,7 +344,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
@@ -363,9 +367,14 @@ yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
{
struct yt921x_priv *priv = dsa_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);
@@ -405,16 +414,21 @@ yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = dsa_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;
@@ -441,7 +455,12 @@ yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = dsa_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);
@@ -470,7 +489,12 @@ yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = dsa_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);
@@ -506,7 +530,12 @@ yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = dsa_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;
@@ -522,8 +551,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 +
@@ -542,7 +571,12 @@ yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
{
struct yt921x_priv *priv = dsa_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);
@@ -3649,11 +3683,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 = dsa_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);
@@ -3672,6 +3709,7 @@ yt921x_phylink_mac_link_up(struct phylink_config *config,
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds);
+ struct yt921x_mib *pm;
int port = dp->index;
int res;

@@ -3684,7 +3722,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
@@ -3798,16 +3838,52 @@ 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 = dsa_to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *pm = pp->mib;
+
+ /* get_stats64() might not be synchronized with port_teardown(), hold
+ * the MIB block till the device teardown
+ */
+ if (pm)
+ disable_delayed_work_sync(&pm->work);
+}
+
static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)
{
struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ const struct yt921x_info *info = priv->info;
+ struct device *dev = yt921x_priv_to_device(priv);
+ struct yt921x_mib *pm = pp->mib;
int res;

+ if (!pm && (BIT(port) & yt921x_info_ports_mask(info))) {
+ 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);
+ /* Start disabled, so that the enable below always pairs with
+ * either this disable or the one from a previous teardown
+ */
+ disable_delayed_work_sync(&pm->work);
+ }
+
mutex_lock(&priv->reg_lock);
res = yt921x_port_setup(priv, port);
mutex_unlock(&priv->reg_lock);
+ if (res)
+ return res;

- return res;
+ if (pm)
+ enable_delayed_work(&pm->work);
+
+ return 0;
}

/* Not "port" - DSCP mapping is global */
@@ -4382,6 +4458,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 */
@@ -4411,12 +4488,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++) {
@@ -4464,7 +4535,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 c1b24b6d1d97..d1764b635630 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -885,6 +885,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];
@@ -917,11 +926,6 @@ struct yt921x_port {
/* BR_ISOLATED */
bool isolated:1;

- 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;
@@ -932,6 +936,7 @@ struct yt921x_port {
struct yt921x_led *leds[YT921X_LED_GROUP_NUM];
#endif

+ struct yt921x_mib *mib;
struct phylink_pcs pcs;
};

@@ -955,6 +960,11 @@ struct yt921x_info {
u16 serdes_mask;
};

+static inline u16 yt921x_info_ports_mask(const struct yt921x_info *info)
+{
+ return info->internal_mask | info->xmii_mask | info->serdes_mask;
+}
+
struct yt921x_priv {
struct dsa_switch ds;

--
2.53.0