mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
neighbour: Clean up neigh_table_init() and neigh_table_clear().
Currently, neigh_table_init() and neigh_table_clear() are called
once at the boot time, but we will call them for each netns.
Let's clean up them.
For neigh_table_init(),
* Allocate all memory first and unwind on failure
* Remove !tbl->entry_size check since it is always true
for arp_tbl and nd_tbl
* Initialise everything before starting delayed works
For neigh_table_clear(),
* Reorder function to match neigh_table_init()
* Use timer_shutdown_sync() instead of timer_delete_sync()
* Remove pneigh_queue_purge() since it is called
via neigh_ifdown()
* Replace pr_crit() with DEBUG_NET_WARN_ON_ONCE()
* Call neigh_hash_free_rcu() directly without call_rcu()
In a later patch, neigh_table_clear() will be split so that
tbl->{stats,phash_buckets,nht} will be freed only when all
neighbour entries are freed.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Link: https://patch.msgid.link/20260902203722.926528-11-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
df5f4f372d
commit
e265c2c432
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user