]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blobdiff - net/core/net_namespace.c
Merge tag 'mac80211-next-for-davem-2018-03-29' of git://git.kernel.org/pub/scm/linux...
[mirror_ubuntu-eoan-kernel.git] / net / core / net_namespace.c
index 27a55236ad64fda06723af58eec66e3efb244f2d..7fdf321d4997d1da2541cccbe158a74daeafd7f3 100644 (file)
@@ -33,6 +33,10 @@ static struct list_head *first_device = &pernet_list;
 LIST_HEAD(net_namespace_list);
 EXPORT_SYMBOL_GPL(net_namespace_list);
 
+/* Protects net_namespace_list. Nests iside rtnl_lock() */
+DECLARE_RWSEM(net_rwsem);
+EXPORT_SYMBOL_GPL(net_rwsem);
+
 struct net init_net = {
        .count          = REFCOUNT_INIT(1),
        .dev_base_head  = LIST_HEAD_INIT(init_net.dev_base_head),
@@ -40,12 +44,13 @@ struct net init_net = {
 EXPORT_SYMBOL(init_net);
 
 static bool init_net_initialized;
-static unsigned nr_sync_pernet_ops;
 /*
- * net_sem: protects: pernet_list, net_generic_ids, nr_sync_pernet_ops,
+ * pernet_ops_rwsem: protects: pernet_list, net_generic_ids,
  * init_net_initialized and first_device pointer.
+ * This is internal net namespace object. Please, don't use it
+ * outside.
  */
-DECLARE_RWSEM(net_sem);
+DECLARE_RWSEM(pernet_ops_rwsem);
 
 #define MIN_PERNET_OPS_ID      \
        ((sizeof(struct net_generic) + sizeof(void *) - 1) / sizeof(void *))
@@ -73,7 +78,7 @@ static int net_assign_generic(struct net *net, unsigned int id, void *data)
        BUG_ON(id < MIN_PERNET_OPS_ID);
 
        old_ng = rcu_dereference_protected(net->gen,
-                                          lockdep_is_held(&net_sem));
+                                          lockdep_is_held(&pernet_ops_rwsem));
        if (old_ng->s.len > id) {
                old_ng->ptr[id] = data;
                return 0;
@@ -290,7 +295,7 @@ struct net *get_net_ns_by_id(struct net *net, int id)
  */
 static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
 {
-       /* Must be called with net_sem held */
+       /* Must be called with pernet_ops_rwsem held */
        const struct pernet_operations *ops, *saved_ops;
        int error = 0;
        LIST_HEAD(net_exit_list);
@@ -301,15 +306,16 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
        net->user_ns = user_ns;
        idr_init(&net->netns_ids);
        spin_lock_init(&net->nsid_lock);
+       mutex_init(&net->ipv4.ra_mutex);
 
        list_for_each_entry(ops, &pernet_list, list) {
                error = ops_init(ops, net);
                if (error < 0)
                        goto out_undo;
        }
-       rtnl_lock();
+       down_write(&net_rwsem);
        list_add_tail_rcu(&net->list, &net_namespace_list);
-       rtnl_unlock();
+       up_write(&net_rwsem);
 out:
        return error;
 
@@ -338,7 +344,6 @@ static int __net_init net_defaults_init_net(struct net *net)
 
 static struct pernet_operations net_defaults_ops = {
        .init = net_defaults_init_net,
-       .async = true,
 };
 
 static __init int net_defaults_init(void)
@@ -362,7 +367,7 @@ static void dec_net_namespaces(struct ucounts *ucounts)
        dec_ucount(ucounts, UCOUNT_NET_NAMESPACES);
 }
 
-static struct kmem_cache *net_cachep;
+static struct kmem_cache *net_cachep __ro_after_init;
 static struct workqueue_struct *netns_wq;
 
 static struct net *net_alloc(void)
@@ -405,7 +410,6 @@ struct net *copy_net_ns(unsigned long flags,
 {
        struct ucounts *ucounts;
        struct net *net;
-       unsigned write;
        int rv;
 
        if (!(flags & CLONE_NEWNET))
@@ -423,25 +427,14 @@ struct net *copy_net_ns(unsigned long flags,
        refcount_set(&net->passive, 1);
        net->ucounts = ucounts;
        get_user_ns(user_ns);
-again:
-       write = READ_ONCE(nr_sync_pernet_ops);
-       if (write)
-               rv = down_write_killable(&net_sem);
-       else
-               rv = down_read_killable(&net_sem);
+
+       rv = down_read_killable(&pernet_ops_rwsem);
        if (rv < 0)
                goto put_userns;
 
-       if (!write && unlikely(READ_ONCE(nr_sync_pernet_ops))) {
-               up_read(&net_sem);
-               goto again;
-       }
        rv = setup_net(net, user_ns);
 
-       if (write)
-               up_write(&net_sem);
-       else
-               up_read(&net_sem);
+       up_read(&pernet_ops_rwsem);
 
        if (rv < 0) {
 put_userns:
@@ -461,7 +454,7 @@ static void unhash_nsid(struct net *net, struct net *last)
         * and this work is the only process, that may delete
         * a net from net_namespace_list. So, when the below
         * is executing, the list may only grow. Thus, we do not
-        * use for_each_net_rcu() or rtnl_lock().
+        * use for_each_net_rcu() or net_rwsem.
         */
        for_each_net(tmp) {
                int id;
@@ -489,24 +482,14 @@ static void cleanup_net(struct work_struct *work)
        struct net *net, *tmp, *last;
        struct llist_node *net_kill_list;
        LIST_HEAD(net_exit_list);
-       unsigned write;
 
        /* Atomically snapshot the list of namespaces to cleanup */
        net_kill_list = llist_del_all(&cleanup_list);
-again:
-       write = READ_ONCE(nr_sync_pernet_ops);
-       if (write)
-               down_write(&net_sem);
-       else
-               down_read(&net_sem);
 
-       if (!write && unlikely(READ_ONCE(nr_sync_pernet_ops))) {
-               up_read(&net_sem);
-               goto again;
-       }
+       down_read(&pernet_ops_rwsem);
 
        /* Don't let anyone else find us. */
-       rtnl_lock();
+       down_write(&net_rwsem);
        llist_for_each_entry(net, net_kill_list, cleanup_list)
                list_del_rcu(&net->list);
        /* Cache last net. After we unlock rtnl, no one new net
@@ -520,7 +503,7 @@ again:
         * useless anyway, as netns_ids are destroyed there.
         */
        last = list_last_entry(&net_namespace_list, struct net, list);
-       rtnl_unlock();
+       up_write(&net_rwsem);
 
        llist_for_each_entry(net, net_kill_list, cleanup_list) {
                unhash_nsid(net, last);
@@ -542,10 +525,7 @@ again:
        list_for_each_entry_reverse(ops, &pernet_list, list)
                ops_free_list(ops, &net_exit_list);
 
-       if (write)
-               up_write(&net_sem);
-       else
-               up_read(&net_sem);
+       up_read(&pernet_ops_rwsem);
 
        /* Ensure there are no outstanding rcu callbacks using this
         * network namespace.
@@ -572,8 +552,8 @@ again:
  */
 void net_ns_barrier(void)
 {
-       down_write(&net_sem);
-       up_write(&net_sem);
+       down_write(&pernet_ops_rwsem);
+       up_write(&pernet_ops_rwsem);
 }
 EXPORT_SYMBOL(net_ns_barrier);
 
@@ -653,7 +633,6 @@ static __net_exit void net_ns_net_exit(struct net *net)
 static struct pernet_operations __net_initdata net_ns_ops = {
        .init = net_ns_net_init,
        .exit = net_ns_net_exit,
-       .async = true,
 };
 
 static const struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = {
@@ -882,7 +861,7 @@ static int __init net_ns_init(void)
 #ifdef CONFIG_NET_NS
        net_cachep = kmem_cache_create("net_namespace", sizeof(struct net),
                                        SMP_CACHE_BYTES,
-                                       SLAB_PANIC, NULL);
+                                       SLAB_PANIC|SLAB_ACCOUNT, NULL);
 
        /* Create workqueue for cleanup */
        netns_wq = create_singlethread_workqueue("netns");
@@ -896,12 +875,12 @@ static int __init net_ns_init(void)
 
        rcu_assign_pointer(init_net.gen, ng);
 
-       down_write(&net_sem);
+       down_write(&pernet_ops_rwsem);
        if (setup_net(&init_net, &init_user_ns))
                panic("Could not setup the initial network namespace");
 
        init_net_initialized = true;
-       up_write(&net_sem);
+       up_write(&pernet_ops_rwsem);
 
        register_pernet_subsys(&net_ns_ops);
 
@@ -925,6 +904,9 @@ static int __register_pernet_operations(struct list_head *list,
 
        list_add_tail(&ops->list, list);
        if (ops->init || (ops->id && ops->size)) {
+               /* We held write locked pernet_ops_rwsem, and parallel
+                * setup_net() and cleanup_net() are not possible.
+                */
                for_each_net(net) {
                        error = ops_init(ops, net);
                        if (error)
@@ -948,6 +930,7 @@ static void __unregister_pernet_operations(struct pernet_operations *ops)
        LIST_HEAD(net_exit_list);
 
        list_del(&ops->list);
+       /* See comment in __register_pernet_operations() */
        for_each_net(net)
                list_add_tail(&net->exit_list, &net_exit_list);
        ops_exit_list(ops, &net_exit_list);
@@ -1005,9 +988,6 @@ again:
                rcu_barrier();
                if (ops->id)
                        ida_remove(&net_generic_ids, *ops->id);
-       } else if (!ops->async) {
-               pr_info_once("Pernet operations %ps are sync.\n", ops);
-               nr_sync_pernet_ops++;
        }
 
        return error;
@@ -1015,8 +995,6 @@ again:
 
 static void unregister_pernet_operations(struct pernet_operations *ops)
 {
-       if (!ops->async)
-               BUG_ON(nr_sync_pernet_ops-- == 0);
        __unregister_pernet_operations(ops);
        rcu_barrier();
        if (ops->id)
@@ -1045,9 +1023,9 @@ static void unregister_pernet_operations(struct pernet_operations *ops)
 int register_pernet_subsys(struct pernet_operations *ops)
 {
        int error;
-       down_write(&net_sem);
+       down_write(&pernet_ops_rwsem);
        error =  register_pernet_operations(first_device, ops);
-       up_write(&net_sem);
+       up_write(&pernet_ops_rwsem);
        return error;
 }
 EXPORT_SYMBOL_GPL(register_pernet_subsys);
@@ -1063,9 +1041,9 @@ EXPORT_SYMBOL_GPL(register_pernet_subsys);
  */
 void unregister_pernet_subsys(struct pernet_operations *ops)
 {
-       down_write(&net_sem);
+       down_write(&pernet_ops_rwsem);
        unregister_pernet_operations(ops);
-       up_write(&net_sem);
+       up_write(&pernet_ops_rwsem);
 }
 EXPORT_SYMBOL_GPL(unregister_pernet_subsys);
 
@@ -1091,11 +1069,11 @@ EXPORT_SYMBOL_GPL(unregister_pernet_subsys);
 int register_pernet_device(struct pernet_operations *ops)
 {
        int error;
-       down_write(&net_sem);
+       down_write(&pernet_ops_rwsem);
        error = register_pernet_operations(&pernet_list, ops);
        if (!error && (first_device == &pernet_list))
                first_device = &ops->list;
-       up_write(&net_sem);
+       up_write(&pernet_ops_rwsem);
        return error;
 }
 EXPORT_SYMBOL_GPL(register_pernet_device);
@@ -1111,11 +1089,11 @@ EXPORT_SYMBOL_GPL(register_pernet_device);
  */
 void unregister_pernet_device(struct pernet_operations *ops)
 {
-       down_write(&net_sem);
+       down_write(&pernet_ops_rwsem);
        if (&ops->list == first_device)
                first_device = first_device->next;
        unregister_pernet_operations(ops);
-       up_write(&net_sem);
+       up_write(&pernet_ops_rwsem);
 }
 EXPORT_SYMBOL_GPL(unregister_pernet_device);