diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2014-06-05 00:53:02 +0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2014-06-05 00:53:02 +0400 |
commit | f8647b506d7116a1a3accd8d618184096e85f50b (patch) | |
tree | f10bc7201fda2a36c035548a0ea62210ad57adb6 /net/core/rtnetlink.c | |
parent | 1d421ca9d7edbac1eb118631ee039d50ab54771e (diff) | |
parent | f7a89f1b8eb598ac5da61c9795b3d847baa73d12 (diff) | |
download | linux-f8647b506d7116a1a3accd8d618184096e85f50b.tar.xz |
Merge branch '3.15-fixes' into mips-for-linux-next
Diffstat (limited to 'net/core/rtnetlink.c')
-rw-r--r-- | net/core/rtnetlink.c | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9837bebf93ce..2d8d8fcfa060 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -353,15 +353,46 @@ void __rtnl_link_unregister(struct rtnl_link_ops *ops) } EXPORT_SYMBOL_GPL(__rtnl_link_unregister); +/* Return with the rtnl_lock held when there are no network + * devices unregistering in any network namespace. + */ +static void rtnl_lock_unregistering_all(void) +{ + struct net *net; + bool unregistering; + DEFINE_WAIT(wait); + + for (;;) { + prepare_to_wait(&netdev_unregistering_wq, &wait, + TASK_UNINTERRUPTIBLE); + unregistering = false; + rtnl_lock(); + for_each_net(net) { + if (net->dev_unreg_count > 0) { + unregistering = true; + break; + } + } + if (!unregistering) + break; + __rtnl_unlock(); + schedule(); + } + finish_wait(&netdev_unregistering_wq, &wait); +} + /** * rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink. * @ops: struct rtnl_link_ops * to unregister */ void rtnl_link_unregister(struct rtnl_link_ops *ops) { - rtnl_lock(); + /* Close the race with cleanup_net() */ + mutex_lock(&net_mutex); + rtnl_lock_unregistering_all(); __rtnl_link_unregister(ops); rtnl_unlock(); + mutex_unlock(&net_mutex); } EXPORT_SYMBOL_GPL(rtnl_link_unregister); |