Skip to content

Commit d539d8f

Browse files
q2venkuba-moo
authored andcommitted
neighbour: Free pneigh_entry after RCU grace period.
We will convert RTM_GETNEIGH to RCU. neigh_get() looks up pneigh_entry by pneigh_lookup() and passes it to pneigh_fill_info(). Then, we must ensure that the entry is alive till pneigh_fill_info() completes, but read_lock_bh(&tbl->lock) in pneigh_lookup() does not guarantee that. Also, we will convert all readers of tbl->phash_buckets[] to RCU. Let's use call_rcu() to free pneigh_entry and update phash_buckets[] and ->next by rcu_assign_pointer(). pneigh_ifdown_and_unlock() uses list_head to avoid overwriting ->next and moving RCU iterators to another list. pndisc_destructor() (only IPv6 ndisc uses this) uses a mutex, so it is not delayed to call_rcu(), where we cannot sleep. This is fine because the mcast code works with RCU and ipv6_dev_mc_dec() frees mcast objects after RCU grace period. While at it, we change the return type of pneigh_ifdown_and_unlock() to void. Signed-off-by: Kuniyuki Iwashima <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent d63382a commit d539d8f

File tree

2 files changed

+32
-17
lines changed

2 files changed

+32
-17
lines changed

include/net/neighbour.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ struct pneigh_entry {
180180
possible_net_t net;
181181
struct net_device *dev;
182182
netdevice_tracker dev_tracker;
183+
union {
184+
struct list_head free_node;
185+
struct rcu_head rcu;
186+
};
183187
u32 flags;
184188
u8 protocol;
185189
bool permanent;

net/core/neighbour.c

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ 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 int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
58-
struct net_device *dev,
59-
bool skip_perm);
57+
static void pneigh_ifdown_and_unlock(struct neigh_table *tbl,
58+
struct net_device *dev,
59+
bool skip_perm);
6060

6161
#ifdef CONFIG_PROC_FS
6262
static const struct seq_operations neigh_stat_seq_ops;
@@ -810,6 +810,14 @@ struct pneigh_entry *pneigh_create(struct neigh_table *tbl,
810810
return n;
811811
}
812812

813+
static void pneigh_destroy(struct rcu_head *rcu)
814+
{
815+
struct pneigh_entry *n = container_of(rcu, struct pneigh_entry, rcu);
816+
817+
netdev_put(n->dev, &n->dev_tracker);
818+
kfree(n);
819+
}
820+
813821
int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
814822
struct net_device *dev)
815823
{
@@ -828,22 +836,24 @@ int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
828836
net_eq(pneigh_net(n), net)) {
829837
rcu_assign_pointer(*np, n->next);
830838
write_unlock_bh(&tbl->lock);
839+
831840
if (tbl->pdestructor)
832841
tbl->pdestructor(n);
833-
netdev_put(n->dev, &n->dev_tracker);
834-
kfree(n);
842+
843+
call_rcu(&n->rcu, pneigh_destroy);
835844
return 0;
836845
}
837846
}
838847
write_unlock_bh(&tbl->lock);
839848
return -ENOENT;
840849
}
841850

842-
static int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
843-
struct net_device *dev,
844-
bool skip_perm)
851+
static void pneigh_ifdown_and_unlock(struct neigh_table *tbl,
852+
struct net_device *dev,
853+
bool skip_perm)
845854
{
846-
struct pneigh_entry *n, __rcu **np, *freelist = NULL;
855+
struct pneigh_entry *n, __rcu **np;
856+
LIST_HEAD(head);
847857
u32 h;
848858

849859
for (h = 0; h <= PNEIGH_HASHMASK; h++) {
@@ -853,24 +863,25 @@ static int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
853863
goto skip;
854864
if (!dev || n->dev == dev) {
855865
rcu_assign_pointer(*np, n->next);
856-
rcu_assign_pointer(n->next, freelist);
857-
freelist = n;
866+
list_add(&n->free_node, &head);
858867
continue;
859868
}
860869
skip:
861870
np = &n->next;
862871
}
863872
}
873+
864874
write_unlock_bh(&tbl->lock);
865-
while ((n = freelist)) {
866-
freelist = rcu_dereference_protected(n->next, 1);
867-
n->next = NULL;
875+
876+
while (!list_empty(&head)) {
877+
n = list_first_entry(&head, typeof(*n), free_node);
878+
list_del(&n->free_node);
879+
868880
if (tbl->pdestructor)
869881
tbl->pdestructor(n);
870-
netdev_put(n->dev, &n->dev_tracker);
871-
kfree(n);
882+
883+
call_rcu(&n->rcu, pneigh_destroy);
872884
}
873-
return -ENOENT;
874885
}
875886

876887
static inline void neigh_parms_put(struct neigh_parms *parms)

0 commit comments

Comments
 (0)