From: Kuniyuki Iwashima <kuniyu@google.com>
To: Andrew Lunn <andrew+netdev@lunn.ch>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
David Ahern <dsahern@kernel.org>,
Ido Schimmel <idosch@nvidia.com>
Cc: Simon Horman <horms@kernel.org>,
Kuniyuki Iwashima <kuniyu@google.com>,
Kuniyuki Iwashima <kuni1840@gmail.com>,
netdev@vger.kernel.org
Subject: [PATCH v3 net-next 11/15] neighbour: Convert neigh_table.entries to refcount_t.
Date: Tue, 11 Aug 2026 02:23:45 +0000 [thread overview]
Message-ID: <20260811022448.116235-12-kuniyu@google.com> (raw)
In-Reply-To: <20260811022448.116235-1-kuniyu@google.com>
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>
---
include/net/neighbour.h | 2 +-
net/core/neighbour.c | 58 ++++++++++++++++++++++++++++-------------
2 files changed, 41 insertions(+), 19 deletions(-)
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 3e31eebf8663..762c8e4cdd96 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -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;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 36488dbd1512..7dc8f0cdbb45 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -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,6 +1892,21 @@ void neigh_table_init(struct neigh_table *tbl)
panic("cannot allocate memory");
}
+static void neigh_table_free(struct neigh_table *tbl)
+{
+ struct neigh_hash_table *nht;
+
+ free_percpu(tbl->stats);
+ tbl->stats = NULL;
+
+ kfree(tbl->phash_buckets);
+ tbl->phash_buckets = NULL;
+
+ 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.
@@ -1881,26 +1914,15 @@ void neigh_table_init(struct neigh_table *tbl)
int neigh_table_clear(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;
-
- kfree(tbl->phash_buckets);
- tbl->phash_buckets = NULL;
-
- nht = rcu_dereference_protected(tbl->nht, 1);
- tbl->nht = NULL;
- neigh_hash_free_rcu(&nht->rcu);
+ 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,
--
2.55.0.691.gc56d675ccc-goog
next prev parent reply other threads:[~2026-08-11 2:25 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-11 2:23 [PATCH v3 net-next 00/15] neighbour: Namespacify arp_tbl and nd_tbl Kuniyuki Iwashima
2026-08-11 2:23 ` [PATCH v3 net-next 01/15] selftest: net: Deflake Periodic GC test in test_neigh.sh Kuniyuki Iwashima
2026-08-11 2:23 ` [PATCH v3 net-next 02/15] neighbour: Remove __neigh_for_each_release() Kuniyuki Iwashima
2026-08-11 14:27 ` David Ahern
2026-08-11 2:23 ` [PATCH v3 net-next 03/15] neighbour: Remove lock dance for neigh_update_{gc,managed}_list() Kuniyuki Iwashima
2026-08-11 14:34 ` David Ahern
2026-08-11 2:23 ` [PATCH v3 net-next 04/15] neighbour: Remove unnecessary EXPORT_SYMBOL() Kuniyuki Iwashima
2026-08-11 14:28 ` David Ahern
2026-08-11 2:23 ` [PATCH v3 net-next 05/15] neighbour: Remove __rcu from neigh_tables[] Kuniyuki Iwashima
2026-08-11 14:42 ` David Ahern
2026-08-11 2:23 ` [PATCH v3 net-next 06/15] neighbour: Store arp_tbl and nd_tbl in net->neigh_tables[] Kuniyuki Iwashima
2026-08-11 15:19 ` David Ahern
2026-08-11 2:23 ` [PATCH v3 net-next 07/15] neighbour: Remove neigh_tables[] Kuniyuki Iwashima
2026-08-11 2:23 ` [PATCH v3 net-next 08/15] ipv4: Replace &arp_tbl with arp_table(net) Kuniyuki Iwashima
2026-08-11 12:31 ` Nikolay Aleksandrov
2026-08-11 2:23 ` [PATCH v3 net-next 09/15] ipv6: Replace &nd_tbl with nd_table(net) Kuniyuki Iwashima
2026-08-11 13:07 ` Nikolay Aleksandrov
2026-08-11 2:23 ` [PATCH v3 net-next 10/15] neighbour: Clean up neigh_table_init() and neigh_table_clear() Kuniyuki Iwashima
2026-08-11 2:23 ` Kuniyuki Iwashima [this message]
2026-08-11 2:23 ` [PATCH v3 net-next 12/15] neighbour: Namespacify neigh_tables Kuniyuki Iwashima
2026-08-11 2:23 ` [PATCH v3 net-next 13/15] neighbour: Don't store net in struct pneigh_entry Kuniyuki Iwashima
2026-08-11 2:23 ` [PATCH v3 net-next 14/15] neighbour: Remove unnecessary net_eq() Kuniyuki Iwashima
2026-08-11 2:23 ` [PATCH v3 net-next 15/15] selftest: net: Specify netns for ip ntable in test_neigh.sh Kuniyuki Iwashima
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260811022448.116235-12-kuniyu@google.com \
--to=kuniyu@google.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=kuni1840@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox