diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index eb6cd43b1746387b7e9dd87b62272e5b92b880cc..00de28c70da6bc1914083c8b2d0c8ea5daa835a9 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -75,6 +75,7 @@ struct net { spinlock_t nsid_lock; atomic_t fnhe_genid; + KABI_FILL_HOLE(struct llist_node defer_free_list) struct list_head list; /* list of network namespaces */ struct list_head exit_list; /* To linked to call pernet exit * methods on dead net ( diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 018e213185a17f638177a8d98c757655bb37eca1..92b7fea4d495cfc0de9366101020677b188f2fc5 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -442,6 +442,21 @@ static struct net *net_alloc(void) goto out; } +static LLIST_HEAD(defer_free_list); + +static void net_complete_free(void) +{ + struct llist_node *kill_list; + struct net *net, *next; + + /* Get the list of namespaces to free from last round. */ + kill_list = llist_del_all(&defer_free_list); + + llist_for_each_entry_safe(net, next, kill_list, defer_free_list) + kmem_cache_free(net_cachep, net); + +} + static void net_free(struct net *net) { if (refcount_dec_and_test(&net->passive)) { @@ -450,7 +465,8 @@ static void net_free(struct net *net) /* There should not be any trackers left there. */ ref_tracker_dir_exit(&net->notrefcnt_tracker); - kmem_cache_free(net_cachep, net); + /* Wait for an extra rcu_barrier() before final free. */ + llist_add(&net->defer_free_list, &defer_free_list); } } @@ -627,6 +643,8 @@ static void cleanup_net(struct work_struct *work) */ rcu_barrier(); + net_complete_free(); + /* Finally it is safe to free my network namespace structure */ list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) { list_del_init(&net->exit_list);