diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 34da4cdc813d..36488dbd1512 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1810,57 +1810,68 @@ static struct lock_class_key neigh_table_proxy_queue_class; void neigh_table_init(struct neigh_table *tbl) { unsigned long now = jiffies; + struct net *net = &init_net; unsigned long phsize; - INIT_LIST_HEAD(&tbl->parms_list); - INIT_LIST_HEAD(&tbl->gc_list); - INIT_LIST_HEAD(&tbl->managed_list); - - list_add(&tbl->parms.list, &tbl->parms_list); - write_pnet(&tbl->parms.net, &init_net); - refcount_set(&tbl->parms.refcnt, 1); - neigh_set_reach_time(&tbl->parms); - tbl->parms.qlen = 0; - - tbl->stats = alloc_percpu(struct neigh_statistics); - if (!tbl->stats) - panic("cannot create neighbour cache statistics"); - -#ifdef CONFIG_PROC_FS - if (!proc_create_seq_data(tbl->id, 0, init_net.proc_net_stat, - &neigh_stat_seq_ops, tbl)) - panic("cannot create neighbour proc dir entry"); -#endif - RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3)); + if (!tbl->nht) + goto err_hash; phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *); tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL); + if (!tbl->phash_buckets) + goto err_phash; - if (!tbl->nht || !tbl->phash_buckets) - panic("cannot allocate neighbour cache hashes"); + tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) + + tbl->key_len, NEIGH_PRIV_ALIGN); - if (!tbl->entry_size) - tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) + - tbl->key_len, NEIGH_PRIV_ALIGN); - else - WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN); + tbl->stats = alloc_percpu(struct neigh_statistics); + if (!tbl->stats) + goto err_stats; + +#ifdef CONFIG_PROC_FS + if (!proc_create_seq_data(tbl->id, 0, net->proc_net_stat, + &neigh_stat_seq_ops, tbl)) + goto err_proc; +#endif + + tbl->parms.tbl = tbl; + tbl->parms.qlen = 0; + INIT_LIST_HEAD(&tbl->parms_list); + list_add(&tbl->parms.list, &tbl->parms_list); + write_pnet(&tbl->parms.net, net); + refcount_set(&tbl->parms.refcnt, 1); + neigh_set_reach_time(&tbl->parms); + tbl->last_flush = now; + tbl->last_rand = now + tbl->parms.reachable_time * 20; spin_lock_init(&tbl->lock); mutex_init(&tbl->phash_lock); + skb_queue_head_init_class(&tbl->proxy_queue, + &neigh_table_proxy_queue_class); + timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0); + INIT_LIST_HEAD(&tbl->gc_list); INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work); queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, - tbl->parms.reachable_time); + tbl->parms.reachable_time); + + INIT_LIST_HEAD(&tbl->managed_list); INIT_DEFERRABLE_WORK(&tbl->managed_work, neigh_managed_work); queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 0); - timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0); - skb_queue_head_init_class(&tbl->proxy_queue, - &neigh_table_proxy_queue_class); + return; - tbl->last_flush = now; - tbl->last_rand = now + tbl->parms.reachable_time * 20; +#ifdef CONFIG_PROC_FS +err_proc: + free_percpu(tbl->stats); +#endif +err_stats: + kfree(tbl->phash_buckets); +err_phash: + neigh_hash_free_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu); +err_hash: + panic("cannot allocate memory"); } /* @@ -1869,26 +1880,27 @@ void neigh_table_init(struct neigh_table *tbl) */ int neigh_table_clear(struct neigh_table *tbl) { - /* It is not clean... Fix it to unload IPv6 module safely */ + struct net *net __maybe_unused = &init_net; + struct neigh_hash_table *nht; + cancel_delayed_work_sync(&tbl->managed_work); cancel_delayed_work_sync(&tbl->gc_work); - timer_delete_sync(&tbl->proxy_timer); - pneigh_queue_purge(&tbl->proxy_queue, NULL, tbl->family); - neigh_ifdown(tbl, NULL); - if (atomic_read(&tbl->entries)) - pr_crit("neighbour leakage\n"); + timer_shutdown_sync(&tbl->proxy_timer); - call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu, - neigh_hash_free_rcu); - tbl->nht = NULL; + neigh_ifdown(tbl, NULL); + DEBUG_NET_WARN_ON_ONCE(atomic_read(&tbl->entries)); + + remove_proc_entry(tbl->id, net->proc_net_stat); + + free_percpu(tbl->stats); + tbl->stats = NULL; kfree(tbl->phash_buckets); tbl->phash_buckets = NULL; - remove_proc_entry(tbl->id, init_net.proc_net_stat); - - free_percpu(tbl->stats); - tbl->stats = NULL; + nht = rcu_dereference_protected(tbl->nht, 1); + tbl->nht = NULL; + neigh_hash_free_rcu(&nht->rcu); return 0; }