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 v2 net-next 09/13] neighbour: Clean up neigh_table_init() and neigh_table_clear().
Date: Fri, 7 Aug 2026 23:28:47 +0000 [thread overview]
Message-ID: <20260807232932.3986667-10-kuniyu@google.com> (raw)
In-Reply-To: <20260807232932.3986667-1-kuniyu@google.com>
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 the next patch, neigh_table_clear() will be called from
neigh_table_unregister(), which is called from arp_net_exit()
and ndisc_net_exit().
As of the time, all devices are already unregistered and no one
can access (p)neigh entry, so neigh_table is freed without waiting
RCU grace period.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
v2: Add __maybe_unused to net in neigh_table_clear() to
avoid transient W=1 warning for CONFIG_PROC_FS=n
(next patch removes it anyway)
---
net/core/neighbour.c | 98 +++++++++++++++++++++++++-------------------
1 file changed, 55 insertions(+), 43 deletions(-)
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);
+ RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3));
+ if (!tbl->nht)
+ goto err_hash;
- 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;
+ phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
+ tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);
+ if (!tbl->phash_buckets)
+ goto err_phash;
+
+ tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) +
+ tbl->key_len, NEIGH_PRIV_ALIGN);
tbl->stats = alloc_percpu(struct neigh_statistics);
if (!tbl->stats)
- panic("cannot create neighbour cache statistics");
+ goto err_stats;
#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");
+ if (!proc_create_seq_data(tbl->id, 0, net->proc_net_stat,
+ &neigh_stat_seq_ops, tbl))
+ goto err_proc;
#endif
- RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3));
-
- phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
- tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);
-
- if (!tbl->nht || !tbl->phash_buckets)
- panic("cannot allocate neighbour cache hashes");
-
- 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->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);
+ timer_shutdown_sync(&tbl->proxy_timer);
+
neigh_ifdown(tbl, NULL);
- if (atomic_read(&tbl->entries))
- pr_crit("neighbour leakage\n");
+ DEBUG_NET_WARN_ON_ONCE(atomic_read(&tbl->entries));
- call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu,
- neigh_hash_free_rcu);
- tbl->nht = NULL;
+ 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;
}
--
2.55.0.679.g6767b8d81c-goog
next prev parent reply other threads:[~2026-08-07 23:29 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-07 23:28 [PATCH v2 net-next 00/13] neighbour: Namespacify arp_tbl and nd_tbl Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 01/13] neighbour: Remove __neigh_for_each_release() Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 02/13] neighbour: Remove lock dance for neigh_update_{gc,managed}_list() Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 03/13] neighbour: Remove unnecessary EXPORT_SYMBOL() Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 04/13] neighbour: Remove __rcu from neigh_tables[] Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 05/13] neighbour: Store arp_tbl and nd_tbl in net->neigh_tables[] Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 06/13] neighbour: Remove neigh_tables[] Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 07/13] ipv4: Replace &arp_tbl with arp_table(net) Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 08/13] ipv6: Replace &nd_tbl with nd_table(net) Kuniyuki Iwashima
2026-08-07 23:28 ` Kuniyuki Iwashima [this message]
2026-08-07 23:28 ` [PATCH v2 net-next 10/13] neighbour: Namespacify neigh_tables Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 11/13] neighbour: Don't store net in struct pneigh_entry Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 12/13] neighbour: Remove unnecessary net_eq() Kuniyuki Iwashima
2026-08-07 23:28 ` [PATCH v2 net-next 13/13] selftest: net: Specify netns for ip ntable in test_neigh.sh Kuniyuki Iwashima
2026-08-08 20:03 ` [PATCH v2 net-next 00/13] neighbour: Namespacify arp_tbl and nd_tbl Jakub Kicinski
2026-08-08 20:47 ` 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=20260807232932.3986667-10-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.