diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index 30b1c85120490afb487f7d28e88f52ff9cf5860b..0d15a12a4560b37a4ed21d8e4bdd3bb853adb894 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -219,6 +219,7 @@ static int __init dummy_init_module(void) { int i, err = 0; + down_write(&pernet_ops_rwsem); rtnl_lock(); err = __rtnl_link_register(&dummy_link_ops); if (err < 0) @@ -233,6 +234,7 @@ static int __init dummy_init_module(void) out: rtnl_unlock(); + up_write(&pernet_ops_rwsem); return err; } diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index 0008da7e9d4cc5f3d716803dd8855bd1a341f516..5f2897ec0edc516fcd846d0384f71888641d9b05 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -330,6 +330,7 @@ static int __init ifb_init_module(void) { int i, err; + down_write(&pernet_ops_rwsem); rtnl_lock(); err = __rtnl_link_register(&ifb_link_ops); if (err < 0) @@ -344,6 +345,7 @@ static int __init ifb_init_module(void) out: rtnl_unlock(); + up_write(&pernet_ops_rwsem); return err; } diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 7fdf321d4997d1da2541cccbe158a74daeafd7f3..a11e03f920d3a7d9d50a37f699ffd66a26a8a057 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -51,6 +51,7 @@ static bool init_net_initialized; * outside. */ DECLARE_RWSEM(pernet_ops_rwsem); +EXPORT_SYMBOL_GPL(pernet_ops_rwsem); #define MIN_PERNET_OPS_ID \ ((sizeof(struct net_generic) + sizeof(void *) - 1) / sizeof(void *)) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index e86b28482ca71d192e62bde5deb1f8ba41a14a3a..45936922d7e231acaad4dc06ebf445b5c1149b20 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -412,17 +412,17 @@ static void __rtnl_kill_links(struct net *net, struct rtnl_link_ops *ops) * __rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink. * @ops: struct rtnl_link_ops * to unregister * - * The caller must hold the rtnl_mutex. + * The caller must hold the rtnl_mutex and guarantee net_namespace_list + * integrity (hold pernet_ops_rwsem for writing to close the race + * with setup_net() and cleanup_net()). */ void __rtnl_link_unregister(struct rtnl_link_ops *ops) { struct net *net; - down_read(&net_rwsem); for_each_net(net) { __rtnl_kill_links(net, ops); } - up_read(&net_rwsem); list_del(&ops->list); } EXPORT_SYMBOL_GPL(__rtnl_link_unregister);