.setup() can run again within one registration: removing another switch from the tree tears the whole tree down, and re-probing sets every member switch up again. The hardware reset at the start of .setup() brings the MIB counters back to zero, but a MIB block kept across that cycle still holds the old software buffer, so the next yt921x_mib_read() treats the small value as a 32-bit wrap and adds 2^32 to every counter it extends. The old claim, get_stats64() not being synchronized with .port_teardown(), is not true, so free the MIB block in .port_teardown(), which pairs with every successful .port_setup() including the error unwinds, so the next setup allocates zeroed blocks. Not carrying a fix tag since there are no known multi-chip yt921x users. Signed-off-by: David Yang --- drivers/net/dsa/motorcomm/chip.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c index 33e5fc127244..fd737c3e2e18 100644 --- a/drivers/net/dsa/motorcomm/chip.c +++ b/drivers/net/dsa/motorcomm/chip.c @@ -1994,11 +1994,11 @@ static void yt921x_dsa_port_teardown(struct dsa_switch *ds, int port) 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) + if (pm) { disable_delayed_work_sync(&pm->work); + pp->mib = NULL; + kvfree(pm); + } } static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port) @@ -2006,12 +2006,11 @@ 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); + pm = kvzalloc_obj(*pm); if (!pm) return -ENOMEM; pp->mib = pm; @@ -2019,10 +2018,6 @@ static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port) pm->port = pp; u64_stats_init(&pm->syncp); 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); @@ -2031,9 +2026,6 @@ static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port) if (res) return res; - if (pm) - enable_delayed_work(&pm->work); - return 0; } -- 2.53.0