Netdev List
 help / color / mirror / Atom feed
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


  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