netns: don't allocate an id for dead netns
[cascardo/linux.git] / net / core / net_namespace.c
index 5221f97..70d3450 100644 (file)
@@ -198,8 +198,10 @@ static int __peernet2id(struct net *net, struct net *peer, bool alloc)
  */
 int peernet2id(struct net *net, struct net *peer)
 {
-       int id = __peernet2id(net, peer, true);
+       bool alloc = atomic_read(&peer->count) == 0 ? false : true;
+       int id;
 
+       id = __peernet2id(net, peer, alloc);
        return id >= 0 ? id : NETNSA_NSID_NOT_ASSIGNED;
 }
 EXPORT_SYMBOL(peernet2id);
@@ -349,7 +351,7 @@ static LIST_HEAD(cleanup_list);  /* Must hold cleanup_list_lock to touch */
 static void cleanup_net(struct work_struct *work)
 {
        const struct pernet_operations *ops;
-       struct net *net, *tmp, *peer;
+       struct net *net, *tmp;
        struct list_head net_kill_list;
        LIST_HEAD(net_exit_list);
 
@@ -365,6 +367,14 @@ static void cleanup_net(struct work_struct *work)
        list_for_each_entry(net, &net_kill_list, cleanup_list) {
                list_del_rcu(&net->list);
                list_add_tail(&net->exit_list, &net_exit_list);
+               for_each_net(tmp) {
+                       int id = __peernet2id(tmp, net, false);
+
+                       if (id >= 0)
+                               idr_remove(&tmp->netns_ids, id);
+               }
+               idr_destroy(&net->netns_ids);
+
        }
        rtnl_unlock();
 
@@ -390,26 +400,12 @@ static void cleanup_net(struct work_struct *work)
         */
        rcu_barrier();
 
-       rtnl_lock();
        /* Finally it is safe to free my network namespace structure */
        list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
-               /* Unreference net from all peers (no need to loop over
-                * net_exit_list because idr_destroy() will be called for each
-                * element of this list.
-                */
-               for_each_net(peer) {
-                       int id = __peernet2id(peer, net, false);
-
-                       if (id >= 0)
-                               idr_remove(&peer->netns_ids, id);
-               }
-               idr_destroy(&net->netns_ids);
-
                list_del_init(&net->exit_list);
                put_user_ns(net->user_ns);
                net_drop_ns(net);
        }
-       rtnl_unlock();
 }
 static DECLARE_WORK(net_cleanup_work, cleanup_net);