Skip to content

Commit 13a936b

Browse files
q2venkuba-moo
authored andcommitted
neighbour: Protect tbl->phash_buckets[] with a dedicated mutex.
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. 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 <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent b8b7ed1 commit 13a936b

File tree

2 files changed

+22
-18
lines changed

2 files changed

+22
-18
lines changed

include/net/neighbour.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ struct neigh_table {
240240
unsigned long last_rand;
241241
struct neigh_statistics __percpu *stats;
242242
struct neigh_hash_table __rcu *nht;
243+
struct mutex phash_lock;
243244
struct pneigh_entry __rcu **phash_buckets;
244245
};
245246

net/core/neighbour.c

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,8 @@ static void neigh_timer_handler(struct timer_list *t);
5454
static void __neigh_notify(struct neighbour *n, int type, int flags,
5555
u32 pid);
5656
static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid);
57-
static void pneigh_ifdown_and_unlock(struct neigh_table *tbl,
58-
struct net_device *dev,
59-
bool skip_perm);
57+
static void pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
58+
bool skip_perm);
6059

6160
#ifdef CONFIG_PROC_FS
6261
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,
437436
{
438437
write_lock_bh(&tbl->lock);
439438
neigh_flush_dev(tbl, dev, skip_perm);
440-
pneigh_ifdown_and_unlock(tbl, dev, skip_perm);
439+
write_unlock_bh(&tbl->lock);
440+
441+
pneigh_ifdown(tbl, dev, skip_perm);
441442
pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL,
442443
tbl->family);
443444
if (skb_queue_empty_lockless(&tbl->proxy_queue))
@@ -731,15 +732,15 @@ struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,
731732
key_len = tbl->key_len;
732733
hash_val = pneigh_hash(pkey, key_len);
733734
n = rcu_dereference_check(tbl->phash_buckets[hash_val],
734-
lockdep_is_held(&tbl->lock));
735+
lockdep_is_held(&tbl->phash_lock));
735736

736737
while (n) {
737738
if (!memcmp(n->key, pkey, key_len) &&
738739
net_eq(pneigh_net(n), net) &&
739740
(n->dev == dev || !n->dev))
740741
return n;
741742

742-
n = rcu_dereference_check(n->next, lockdep_is_held(&tbl->lock));
743+
n = rcu_dereference_check(n->next, lockdep_is_held(&tbl->phash_lock));
743744
}
744745

745746
return NULL;
@@ -754,11 +755,9 @@ struct pneigh_entry *pneigh_create(struct neigh_table *tbl,
754755
unsigned int key_len;
755756
u32 hash_val;
756757

757-
ASSERT_RTNL();
758+
mutex_lock(&tbl->phash_lock);
758759

759-
read_lock_bh(&tbl->lock);
760760
n = pneigh_lookup(tbl, net, pkey, dev);
761-
read_unlock_bh(&tbl->lock);
762761
if (n)
763762
goto out;
764763

@@ -780,11 +779,10 @@ struct pneigh_entry *pneigh_create(struct neigh_table *tbl,
780779
}
781780

782781
hash_val = pneigh_hash(pkey, key_len);
783-
write_lock_bh(&tbl->lock);
784782
n->next = tbl->phash_buckets[hash_val];
785783
rcu_assign_pointer(tbl->phash_buckets[hash_val], n);
786-
write_unlock_bh(&tbl->lock);
787784
out:
785+
mutex_unlock(&tbl->phash_lock);
788786
return n;
789787
}
790788

@@ -806,14 +804,16 @@ int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
806804
key_len = tbl->key_len;
807805
hash_val = pneigh_hash(pkey, key_len);
808806

809-
write_lock_bh(&tbl->lock);
807+
mutex_lock(&tbl->phash_lock);
808+
810809
for (np = &tbl->phash_buckets[hash_val];
811810
(n = rcu_dereference_protected(*np, 1)) != NULL;
812811
np = &n->next) {
813812
if (!memcmp(n->key, pkey, key_len) && n->dev == dev &&
814813
net_eq(pneigh_net(n), net)) {
815814
rcu_assign_pointer(*np, n->next);
816-
write_unlock_bh(&tbl->lock);
815+
816+
mutex_unlock(&tbl->phash_lock);
817817

818818
if (tbl->pdestructor)
819819
tbl->pdestructor(n);
@@ -822,18 +822,20 @@ int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
822822
return 0;
823823
}
824824
}
825-
write_unlock_bh(&tbl->lock);
825+
826+
mutex_unlock(&tbl->phash_lock);
826827
return -ENOENT;
827828
}
828829

829-
static void pneigh_ifdown_and_unlock(struct neigh_table *tbl,
830-
struct net_device *dev,
831-
bool skip_perm)
830+
static void pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
831+
bool skip_perm)
832832
{
833833
struct pneigh_entry *n, __rcu **np;
834834
LIST_HEAD(head);
835835
u32 h;
836836

837+
mutex_lock(&tbl->phash_lock);
838+
837839
for (h = 0; h <= PNEIGH_HASHMASK; h++) {
838840
np = &tbl->phash_buckets[h];
839841
while ((n = rcu_dereference_protected(*np, 1)) != NULL) {
@@ -849,7 +851,7 @@ static void pneigh_ifdown_and_unlock(struct neigh_table *tbl,
849851
}
850852
}
851853

852-
write_unlock_bh(&tbl->lock);
854+
mutex_unlock(&tbl->phash_lock);
853855

854856
while (!list_empty(&head)) {
855857
n = list_first_entry(&head, typeof(*n), free_node);
@@ -1796,6 +1798,7 @@ void neigh_table_init(int index, struct neigh_table *tbl)
17961798
WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN);
17971799

17981800
rwlock_init(&tbl->lock);
1801+
mutex_init(&tbl->phash_lock);
17991802

18001803
INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work);
18011804
queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,

0 commit comments

Comments
 (0)