[PATCH net-next] net: dsa: unsync host addresses when destroying user port

Daniel Golle posted 1 patch 2 days, 23 hours ago
net/dsa/user.c | 1 +
1 file changed, 1 insertion(+)
[PATCH net-next] net: dsa: unsync host addresses when destroying user port
Posted by Daniel Golle 2 days, 23 hours ago
When a user port is destroyed while addresses are still synced to it,
e.g. multicast addresses synced by a bridge the port is a member of,
the host FDB/MDB entries these addresses installed on the CPU port are
never removed: the only removal path is dsa_user_unsync_uc()/_mc() via
ndo_set_rx_mode, and __dev_set_rx_mode() does not call the ndo on a
device which is down. By the time the bridge unsyncs its addresses in
del_nbp() during unregistration, the netdev has already been closed,
so the unsync never reaches DSA and the entries linger until
dsa_switch_release_ports() reports them:

  Cleaning up multicast address 33:33:00:00:00:01 vid 0 from port 9

This happens on every unbind of a DSA driver supporting host address
filtering while its ports are up.

Unsync the host addresses in dsa_user_destroy() before unregistering
the netdev, at a point where the driver can still process the
deletion, just like dsa_user_change_conduit() already does when
migrating host addresses to a new conduit.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
 net/dsa/user.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/net/dsa/user.c b/net/dsa/user.c
index 03c7af6abe18..a7dabb645036 100644
--- a/net/dsa/user.c
+++ b/net/dsa/user.c
@@ -2885,6 +2885,7 @@ void dsa_user_destroy(struct net_device *user_dev)
 
 	netif_carrier_off(user_dev);
 	rtnl_lock();
+	dsa_user_unsync_ha(user_dev);
 	netdev_upper_dev_unlink(conduit, user_dev);
 	unregister_netdevice(user_dev);
 	phylink_disconnect_phy(dp->pl);
-- 
2.55.0
Re: [PATCH net-next] net: dsa: unsync host addresses when destroying user port
Posted by Vladimir Oltean 1 day ago
On Wed, Jul 22, 2026 at 01:12:47AM +0100, Daniel Golle wrote:
> When a user port is destroyed while addresses are still synced to it,
> e.g. multicast addresses synced by a bridge the port is a member of,
> the host FDB/MDB entries these addresses installed on the CPU port are
> never removed: the only removal path is dsa_user_unsync_uc()/_mc() via
> ndo_set_rx_mode, and __dev_set_rx_mode() does not call the ndo on a
> device which is down. By the time the bridge unsyncs its addresses in
> del_nbp() during unregistration, the netdev has already been closed,
> so the unsync never reaches DSA and the entries linger until
> dsa_switch_release_ports() reports them:
> 
>   Cleaning up multicast address 33:33:00:00:00:01 vid 0 from port 9
> 
> This happens on every unbind of a DSA driver supporting host address
> filtering while its ports are up.
> 
> Unsync the host addresses in dsa_user_destroy() before unregistering
> the netdev, at a point where the driver can still process the
> deletion, just like dsa_user_change_conduit() already does when
> migrating host addresses to a new conduit.
> 
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
>  net/dsa/user.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/net/dsa/user.c b/net/dsa/user.c
> index 03c7af6abe18..a7dabb645036 100644
> --- a/net/dsa/user.c
> +++ b/net/dsa/user.c
> @@ -2885,6 +2885,7 @@ void dsa_user_destroy(struct net_device *user_dev)
>  
>  	netif_carrier_off(user_dev);
>  	rtnl_lock();
> +	dsa_user_unsync_ha(user_dev);
>  	netdev_upper_dev_unlink(conduit, user_dev);
>  	unregister_netdevice(user_dev);
>  	phylink_disconnect_phy(dp->pl);
> -- 
> 2.55.0

Sorry, I noticed this patch late. Something doesn't add up - I don't
understand what makes the unregistration path unique, since according to
all you've said, it should be enough to remove the user port from the
bridge while administratively down, and it should lead to the same
effect (no unsync event triggered). In that case, maybe the
dsa_user_unsync_ha() belongs somewhere in dsa_user_close(), near
dsa_user_host_uc_uninstall().

I will return tomorrow with more comments after I do some testing.
Re: [PATCH net-next] net: dsa: unsync host addresses when destroying user port
Posted by Vladimir Oltean 10 hours ago
On Fri, Jul 24, 2026 at 01:57:35AM +0300, Vladimir Oltean wrote:
> On Wed, Jul 22, 2026 at 01:12:47AM +0100, Daniel Golle wrote:
> > When a user port is destroyed while addresses are still synced to it,
> > e.g. multicast addresses synced by a bridge the port is a member of,
> > the host FDB/MDB entries these addresses installed on the CPU port are
> > never removed: the only removal path is dsa_user_unsync_uc()/_mc() via
> > ndo_set_rx_mode, and __dev_set_rx_mode() does not call the ndo on a
> > device which is down. By the time the bridge unsyncs its addresses in
> > del_nbp() during unregistration, the netdev has already been closed,
> > so the unsync never reaches DSA and the entries linger until
> > dsa_switch_release_ports() reports them:
> > 
> >   Cleaning up multicast address 33:33:00:00:00:01 vid 0 from port 9
> > 
> > This happens on every unbind of a DSA driver supporting host address
> > filtering while its ports are up.
> > 
> > Unsync the host addresses in dsa_user_destroy() before unregistering
> > the netdev, at a point where the driver can still process the
> > deletion, just like dsa_user_change_conduit() already does when
> > migrating host addresses to a new conduit.
> > 
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > ---
> >  net/dsa/user.c | 1 +
> >  1 file changed, 1 insertion(+)
> > 
> > diff --git a/net/dsa/user.c b/net/dsa/user.c
> > index 03c7af6abe18..a7dabb645036 100644
> > --- a/net/dsa/user.c
> > +++ b/net/dsa/user.c
> > @@ -2885,6 +2885,7 @@ void dsa_user_destroy(struct net_device *user_dev)
> >  
> >  	netif_carrier_off(user_dev);
> >  	rtnl_lock();
> > +	dsa_user_unsync_ha(user_dev);
> >  	netdev_upper_dev_unlink(conduit, user_dev);
> >  	unregister_netdevice(user_dev);
> >  	phylink_disconnect_phy(dp->pl);
> > -- 
> > 2.55.0
> 
> Sorry, I noticed this patch late. Something doesn't add up - I don't
> understand what makes the unregistration path unique, since according to
> all you've said, it should be enough to remove the user port from the
> bridge while administratively down, and it should lead to the same
> effect (no unsync event triggered). In that case, maybe the
> dsa_user_unsync_ha() belongs somewhere in dsa_user_close(), near
> dsa_user_host_uc_uninstall().
> 
> I will return tomorrow with more comments after I do some testing.

Back with some more comments.

Your statement "multicast addresses synced by a bridge the port is a
member of" is not correct. The bridge does not call dev_mc_add().
The multicast addresses come from a different place - likely from
net/ipv6/mcast.c instead.

Therefore, the part of the explanation that ties del_nbp() to the chain
of events truly has no relationship and should be dropped. The
host-joined multicast groups for the bridge are all synced to hardware
through the SWITCHDEV_OBJ_ID_HOST_MDB mechanism.

The minimal reproducer for the problem you observed should be:

$ ip link set swp0 up
$ ip link set swp0 down
$ echo <device-name> > /path/to/driver/unbind

and the correct fix is to put the dsa_user_unsync_ha() call where I
suggested earlier - in dsa_user_close().

pw-bot: cr