mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
neighbour: Convert neigh_table.entries to refcount_t.
We will allocate neigh_table for each netns and free it when netns is destroyed. neigh_ifdown() cleans up all neighbour entries during netns dismantle, but there is no synchronisation between timers because neigh_del_timer() uses timer_delete() to stop a timer. If neigh_table were freed while timer were running, neigh_destroy() would touch the freed table. If we called timer_delete_sync() in neigh_flush_one() under tbl->lock, lockdep would complain although it is false-positive. Let's convert neigh_table.entries to refcount_t and destruct neigh_table only when the count reaches 0. 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-12-kuniyu@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
e265c2c432
commit
1c2b205094
|
|
@ -234,7 +234,7 @@ struct neigh_table {
|
|||
struct delayed_work managed_work;
|
||||
struct timer_list proxy_timer;
|
||||
struct sk_buff_head proxy_queue;
|
||||
atomic_t entries;
|
||||
refcount_t entries;
|
||||
atomic_t gc_entries;
|
||||
struct list_head gc_list;
|
||||
struct list_head managed_list;
|
||||
|
|
|
|||
|
|
@ -55,6 +55,23 @@ static void neigh_notify(struct neighbour *n, int type, int flags, u32 pid);
|
|||
static void __neigh_notify(struct neighbour *n, int type, int flags, u32 pid);
|
||||
static void pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
|
||||
bool skip_perm);
|
||||
static void neigh_table_free(struct neigh_table *tbl);
|
||||
|
||||
static void neigh_table_get(struct neigh_table *tbl)
|
||||
{
|
||||
refcount_inc(&tbl->entries);
|
||||
}
|
||||
|
||||
static void neigh_table_put(struct neigh_table *tbl)
|
||||
{
|
||||
if (refcount_dec_and_test(&tbl->entries))
|
||||
neigh_table_free(tbl);
|
||||
}
|
||||
|
||||
static int neigh_table_entries(struct neigh_table *tbl)
|
||||
{
|
||||
return refcount_read(&tbl->entries) - 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static const struct seq_operations neigh_stat_seq_ops;
|
||||
|
|
@ -522,7 +539,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl,
|
|||
INIT_LIST_HEAD(&n->gc_list);
|
||||
INIT_LIST_HEAD(&n->managed_list);
|
||||
|
||||
atomic_inc(&tbl->entries);
|
||||
neigh_table_get(tbl);
|
||||
out:
|
||||
return n;
|
||||
|
||||
|
|
@ -672,7 +689,7 @@ ___neigh_create(struct neigh_table *tbl, const void *pkey,
|
|||
nht = rcu_dereference_protected(tbl->nht,
|
||||
lockdep_is_held(&tbl->lock));
|
||||
|
||||
if (atomic_read(&tbl->entries) > (1 << nht->hash_shift))
|
||||
if (neigh_table_entries(tbl) > (1 << nht->hash_shift))
|
||||
nht = neigh_hash_grow(tbl, nht->hash_shift + 1);
|
||||
|
||||
hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift);
|
||||
|
|
@ -924,7 +941,7 @@ void neigh_destroy(struct neighbour *neigh)
|
|||
|
||||
neigh_dbg(2, "neigh %p is destroyed\n", neigh);
|
||||
|
||||
atomic_dec(&neigh->tbl->entries);
|
||||
neigh_table_put(neigh->tbl);
|
||||
kfree_rcu(neigh, rcu);
|
||||
}
|
||||
EXPORT_SYMBOL(neigh_destroy);
|
||||
|
|
@ -979,7 +996,7 @@ static void neigh_periodic_work(struct work_struct *work)
|
|||
neigh_set_reach_time(p);
|
||||
}
|
||||
|
||||
if (atomic_read(&tbl->entries) < READ_ONCE(tbl->gc_thresh1))
|
||||
if (neigh_table_entries(tbl) < READ_ONCE(tbl->gc_thresh1))
|
||||
goto out;
|
||||
|
||||
for (i = 0 ; i < (1 << nht->hash_shift); i++) {
|
||||
|
|
@ -1845,6 +1862,7 @@ void neigh_table_init(struct neigh_table *tbl)
|
|||
tbl->last_flush = now;
|
||||
tbl->last_rand = now + tbl->parms.reachable_time * 20;
|
||||
|
||||
refcount_set(&tbl->entries, 1);
|
||||
spin_lock_init(&tbl->lock);
|
||||
mutex_init(&tbl->phash_lock);
|
||||
skb_queue_head_init_class(&tbl->proxy_queue,
|
||||
|
|
@ -1874,24 +1892,10 @@ void neigh_table_init(struct neigh_table *tbl)
|
|||
panic("cannot allocate memory");
|
||||
}
|
||||
|
||||
/*
|
||||
* Only called from ndisc_cleanup(), which means this is dead code
|
||||
* because we no longer can unload IPv6 module.
|
||||
*/
|
||||
int neigh_table_clear(struct neigh_table *tbl)
|
||||
static void neigh_table_free(struct neigh_table *tbl)
|
||||
{
|
||||
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_shutdown_sync(&tbl->proxy_timer);
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -1901,6 +1905,24 @@ int neigh_table_clear(struct neigh_table *tbl)
|
|||
nht = rcu_dereference_protected(tbl->nht, 1);
|
||||
tbl->nht = NULL;
|
||||
neigh_hash_free_rcu(&nht->rcu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only called from ndisc_cleanup(), which means this is dead code
|
||||
* because we no longer can unload IPv6 module.
|
||||
*/
|
||||
int neigh_table_clear(struct neigh_table *tbl)
|
||||
{
|
||||
struct net *net __maybe_unused = &init_net;
|
||||
|
||||
cancel_delayed_work_sync(&tbl->managed_work);
|
||||
cancel_delayed_work_sync(&tbl->gc_work);
|
||||
timer_shutdown_sync(&tbl->proxy_timer);
|
||||
|
||||
neigh_ifdown(tbl, NULL);
|
||||
remove_proc_entry(tbl->id, net->proc_net_stat);
|
||||
|
||||
neigh_table_put(tbl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2275,7 +2297,7 @@ static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl,
|
|||
struct ndt_config ndc = {
|
||||
.ndtc_key_len = tbl->key_len,
|
||||
.ndtc_entry_size = tbl->entry_size,
|
||||
.ndtc_entries = atomic_read(&tbl->entries),
|
||||
.ndtc_entries = neigh_table_entries(tbl),
|
||||
.ndtc_last_flush = jiffies_to_msecs(flush_delta),
|
||||
.ndtc_last_rand = jiffies_to_msecs(rand_delta),
|
||||
.ndtc_proxy_qlen = READ_ONCE(tbl->proxy_queue.qlen),
|
||||
|
|
@ -3495,7 +3517,7 @@ static int neigh_stat_seq_show(struct seq_file *seq, void *v)
|
|||
seq_printf(seq, "%08x %08lx %08lx %08lx %08lx %08lx %08lx "
|
||||
"%08lx %08lx %08lx "
|
||||
"%08lx %08lx %08lx\n",
|
||||
atomic_read(&tbl->entries),
|
||||
neigh_table_entries(tbl),
|
||||
|
||||
st->allocs,
|
||||
st->destroys,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user