From: Kuniyuki Iwashima <kuniyu@google.com>
To: "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>
Cc: Simon Horman <horms@kernel.org>,
Kuniyuki Iwashima <kuniyu@google.com>,
Kuniyuki Iwashima <kuni1840@gmail.com>,
netdev@vger.kernel.org
Subject: [PATCH v1 net-next 13/14] neighbour: Protect tbl->phash_buckets[] with a dedicated mutex.
Date: Fri, 11 Jul 2025 19:06:18 +0000 [thread overview]
Message-ID: <20250711191007.3591938-14-kuniyu@google.com> (raw)
In-Reply-To: <20250711191007.3591938-1-kuniyu@google.com>
tbl->phash_buckets[] is only modified in the slow path by pneigh_create()
and pneigh_delete() under the table lock.
Both of them are called under RTNL, so no extra lock is needed, but we
will remove RTNL from the paths.
pneigh_create() looks up a pneigh_entry, and this part can be lockless,
but it would complicate the logic like
1. lookup
2. allocate pengih_entry for GFP_KERNEL
3. lookup again but under lock
4. if found, return it after freeing the allocated memory
5. else, return the new one
Instead, let's add a per-table mutex and run lookup and allocation
under it.
Even though RTNL is removed, the neigh table is per-protocol one, so this
locking granularity is fine until we make the table per-netns.
Note that updating pneigh_entry part in neigh_add() is still protected
by RTNL and will be moved to pneigh_create() in the next patch.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
include/net/neighbour.h | 1 +
net/core/neighbour.c | 39 +++++++++++++++++++++------------------
2 files changed, 22 insertions(+), 18 deletions(-)
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 1670e2a388556..af6fe50703041 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -240,6 +240,7 @@ struct neigh_table {
unsigned long last_rand;
struct neigh_statistics __percpu *stats;
struct neigh_hash_table __rcu *nht;
+ struct mutex phash_lock;
struct pneigh_entry **phash_buckets;
};
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 9d716852e0e7d..78f2457a101c4 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -54,9 +54,8 @@ static void neigh_timer_handler(struct timer_list *t);
static void __neigh_notify(struct neighbour *n, int type, int flags,
u32 pid);
static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid);
-static void pneigh_ifdown_and_unlock(struct neigh_table *tbl,
- struct net_device *dev,
- bool skip_perm);
+static void pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
+ bool skip_perm);
#ifdef CONFIG_PROC_FS
static const struct seq_operations neigh_stat_seq_ops;
@@ -437,7 +436,9 @@ static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
{
write_lock_bh(&tbl->lock);
neigh_flush_dev(tbl, dev, skip_perm);
- pneigh_ifdown_and_unlock(tbl, dev, skip_perm);
+ write_unlock_bh(&tbl->lock);
+
+ pneigh_ifdown(tbl, dev, skip_perm);
pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL,
tbl->family);
if (skb_queue_empty_lockless(&tbl->proxy_queue))
@@ -731,7 +732,7 @@ struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,
key_len = tbl->key_len;
hash_val = pneigh_hash(pkey, key_len);
n = rcu_dereference_check(tbl->phash_buckets[hash_val],
- lockdep_is_held(&tbl->lock));
+ lockdep_is_held(&tbl->phash_lock));
while (n) {
if (!memcmp(n->key, pkey, key_len) &&
@@ -739,7 +740,7 @@ struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,
(n->dev == dev || !n->dev))
return n;
- n = rcu_dereference_check(n->next, lockdep_is_held(&tbl->lock));
+ n = rcu_dereference_check(n->next, lockdep_is_held(&tbl->phash_lock));
}
return NULL;
@@ -754,11 +755,9 @@ struct pneigh_entry *pneigh_create(struct neigh_table *tbl,
unsigned int key_len;
u32 hash_val;
- ASSERT_RTNL();
+ mutex_lock(&tbl->phash_lock);
- read_lock_bh(&tbl->lock);
n = pneigh_lookup(tbl, net, pkey, dev);
- read_unlock_bh(&tbl->lock);
if (n)
goto out;
@@ -780,11 +779,10 @@ struct pneigh_entry *pneigh_create(struct neigh_table *tbl,
}
hash_val = pneigh_hash(pkey, key_len);
- write_lock_bh(&tbl->lock);
n->next = tbl->phash_buckets[hash_val];
rcu_assign_pointer(tbl->phash_buckets[hash_val], n);
- write_unlock_bh(&tbl->lock);
out:
+ mutex_unlock(&tbl->phash_lock);
return n;
}
@@ -803,13 +801,15 @@ int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
unsigned int key_len = tbl->key_len;
u32 hash_val = pneigh_hash(pkey, key_len);
- write_lock_bh(&tbl->lock);
+ mutex_lock(&tbl->phash_lock);
+
for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL;
np = &n->next) {
if (!memcmp(n->key, pkey, key_len) && n->dev == dev &&
net_eq(pneigh_net(n), net)) {
rcu_assign_pointer(*np, n->next);
- write_unlock_bh(&tbl->lock);
+
+ mutex_unlock(&tbl->phash_lock);
if (tbl->pdestructor)
tbl->pdestructor(n);
@@ -818,18 +818,20 @@ int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
return 0;
}
}
- write_unlock_bh(&tbl->lock);
+
+ mutex_unlock(&tbl->phash_lock);
return -ENOENT;
}
-static void pneigh_ifdown_and_unlock(struct neigh_table *tbl,
- struct net_device *dev,
- bool skip_perm)
+static void pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
+ bool skip_perm)
{
struct pneigh_entry *n, **np;
LIST_HEAD(head);
u32 h;
+ mutex_lock(&tbl->phash_lock);
+
for (h = 0; h <= PNEIGH_HASHMASK; h++) {
np = &tbl->phash_buckets[h];
while ((n = *np) != NULL) {
@@ -845,7 +847,7 @@ static void pneigh_ifdown_and_unlock(struct neigh_table *tbl,
}
}
- write_unlock_bh(&tbl->lock);
+ mutex_unlock(&tbl->phash_lock);
while (!list_empty(&head)) {
n = list_first_entry(&head, typeof(*n), free_node);
@@ -1792,6 +1794,7 @@ void neigh_table_init(int index, struct neigh_table *tbl)
WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN);
rwlock_init(&tbl->lock);
+ mutex_init(&tbl->phash_lock);
INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work);
queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,
--
2.50.0.727.gbf7dc18ff4-goog
next prev parent reply other threads:[~2025-07-11 19:10 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-11 19:06 [PATCH v1 net-next 00/14] neighbour: Convert RTM_GETNEIGH to RCU and make pneigh RTNL-free Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 01/14] neighbour: Make neigh_valid_get_req() return ndmsg Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 02/14] neighbour: Move two validations from neigh_get() to neigh_valid_get_req() Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 03/14] neighbour: Allocate skb in neigh_get() Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 04/14] neighbour: Move neigh_find_table() to neigh_get() Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 05/14] neighbour: Split pneigh_lookup() Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 06/14] neighbour: Free pneigh_entry after RCU grace period Kuniyuki Iwashima
2025-07-12 15:01 ` Simon Horman
2025-07-12 18:07 ` Kuniyuki Iwashima
2025-07-14 10:56 ` Simon Horman
2025-07-12 17:14 ` kernel test robot
2025-07-11 19:06 ` [PATCH v1 net-next 07/14] neighbour: Annotate access to struct pneigh_entry.{flags,protocol} Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 08/14] neighbour: Convert RTM_GETNEIGH to RCU Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 09/14] neighbour: Drop read_lock_bh(&tbl->lock) in pneigh_dump_table() Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 10/14] neighbour: Use rcu_dereference() in pneigh_get_{first,next}() Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 11/14] neighbour: Remove __pneigh_lookup() Kuniyuki Iwashima
2025-07-11 19:06 ` [PATCH v1 net-next 12/14] neighbour: Drop read_lock_bh(&tbl->lock) in pneigh_lookup() Kuniyuki Iwashima
2025-07-11 19:06 ` Kuniyuki Iwashima [this message]
2025-07-11 19:06 ` [PATCH v1 net-next 14/14] neighbour: Update pneigh_entry in pneigh_create() 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=20250711191007.3591938-14-kuniyu@google.com \
--to=kuniyu@google.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--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.