Skip to content

Commit

Permalink
devlink: Fix netdev notifier chain corruption
Browse files Browse the repository at this point in the history
Cited commit changed devlink to register its netdev notifier block on
the global netdev notifier chain instead of on the per network namespace
one.

However, when changing the network namespace of the devlink instance,
devlink still tries to unregister its notifier block from the chain of
the old namespace and register it on the chain of the new namespace.
This results in corruption of the notifier chains, as the same notifier
block is registered on two different chains: The global one and the per
network namespace one. In turn, this causes other problems such as the
inability to dismantle namespaces due to netdev reference count issues.

Fix by preventing devlink from moving its notifier block between
namespaces.

Reproducer:

 # echo "10 1" > /sys/bus/netdevsim/new_device
 # ip netns add test123
 # devlink dev reload netdevsim/netdevsim10 netns test123
 # ip netns del test123
 [   71.935619] unregister_netdevice: waiting for lo to become free. Usage count = 2
 [   71.938348] leaked reference.

Fixes: 565b482 ("devlink: change port event netdev notifier from per-net to global")
Signed-off-by: Ido Schimmel <[email protected]>
Reviewed-by: Jiri Pirko <[email protected]>
Reviewed-by: Jacob Keller <[email protected]>
Reviewed-by: Jakub Kicinski <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Paolo Abeni <[email protected]>
  • Loading branch information
idosch authored and Paolo Abeni committed Feb 16, 2023
1 parent 5d54cb1 commit b20b8ae
Show file tree
Hide file tree
Showing 3 changed files with 1 addition and 14 deletions.
2 changes: 0 additions & 2 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -2839,8 +2839,6 @@ int unregister_netdevice_notifier(struct notifier_block *nb);
int register_netdevice_notifier_net(struct net *net, struct notifier_block *nb);
int unregister_netdevice_notifier_net(struct net *net,
struct notifier_block *nb);
void move_netdevice_notifier_net(struct net *src_net, struct net *dst_net,
struct notifier_block *nb);
int register_netdevice_notifier_dev_net(struct net_device *dev,
struct notifier_block *nb,
struct netdev_net_notifier *nn);
Expand Down
8 changes: 0 additions & 8 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1869,14 +1869,6 @@ static void __move_netdevice_notifier_net(struct net *src_net,
__register_netdevice_notifier_net(dst_net, nb, true);
}

void move_netdevice_notifier_net(struct net *src_net, struct net *dst_net,
struct notifier_block *nb)
{
rtnl_lock();
__move_netdevice_notifier_net(src_net, dst_net, nb);
rtnl_unlock();
}

int register_netdevice_notifier_dev_net(struct net_device *dev,
struct notifier_block *nb,
struct netdev_net_notifier *nn)
Expand Down
5 changes: 1 addition & 4 deletions net/core/devlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -4742,11 +4742,8 @@ static int devlink_reload(struct devlink *devlink, struct net *dest_net,
if (err)
return err;

if (dest_net && !net_eq(dest_net, curr_net)) {
move_netdevice_notifier_net(curr_net, dest_net,
&devlink->netdevice_nb);
if (dest_net && !net_eq(dest_net, curr_net))
write_pnet(&devlink->_net, dest_net);
}

err = devlink->ops->reload_up(devlink, action, limit, actions_performed, extack);
devlink_reload_failed_set(devlink, !!err);
Expand Down

0 comments on commit b20b8ae

Please sign in to comment.