Skip to content

Commit 767e97e

Browse files
Eric Dumazetdavem330
Eric Dumazet
authored andcommitted
neigh: RCU conversion of struct neighbour
This is the second step for neighbour RCU conversion. (first was commit d6bf781 : RCU conversion of neigh hash table) neigh_lookup() becomes lockless, but still take a reference on found neighbour. (no more read_lock()/read_unlock() on tbl->lock) struct neighbour gets an additional rcu_head field and is freed after an RCU grace period. Future work would need to eventually not take a reference on neighbour for temporary dst (DST_NOCACHE), but this would need dst->_neighbour to use a noref bit like we did for skb->_dst. Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 546add7 commit 767e97e

File tree

2 files changed

+88
-54
lines changed

2 files changed

+88
-54
lines changed

include/net/neighbour.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ struct neigh_statistics {
9191
#define NEIGH_CACHE_STAT_INC(tbl, field) this_cpu_inc((tbl)->stats->field)
9292

9393
struct neighbour {
94-
struct neighbour *next;
94+
struct neighbour __rcu *next;
9595
struct neigh_table *tbl;
9696
struct neigh_parms *parms;
9797
struct net_device *dev;
@@ -111,6 +111,7 @@ struct neighbour {
111111
struct sk_buff_head arp_queue;
112112
struct timer_list timer;
113113
const struct neigh_ops *ops;
114+
struct rcu_head rcu;
114115
u8 primary_key[0];
115116
};
116117

@@ -139,7 +140,7 @@ struct pneigh_entry {
139140
*/
140141

141142
struct neigh_hash_table {
142-
struct neighbour **hash_buckets;
143+
struct neighbour __rcu **hash_buckets;
143144
unsigned int hash_mask;
144145
__u32 hash_rnd;
145146
struct rcu_head rcu;

net/core/neighbour.c

Lines changed: 85 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -139,18 +139,22 @@ static int neigh_forced_gc(struct neigh_table *tbl)
139139
nht = rcu_dereference_protected(tbl->nht,
140140
lockdep_is_held(&tbl->lock));
141141
for (i = 0; i <= nht->hash_mask; i++) {
142-
struct neighbour *n, **np;
142+
struct neighbour *n;
143+
struct neighbour __rcu **np;
143144

144145
np = &nht->hash_buckets[i];
145-
while ((n = *np) != NULL) {
146+
while ((n = rcu_dereference_protected(*np,
147+
lockdep_is_held(&tbl->lock))) != NULL) {
146148
/* Neighbour record may be discarded if:
147149
* - nobody refers to it.
148150
* - it is not permanent
149151
*/
150152
write_lock(&n->lock);
151153
if (atomic_read(&n->refcnt) == 1 &&
152154
!(n->nud_state & NUD_PERMANENT)) {
153-
*np = n->next;
155+
rcu_assign_pointer(*np,
156+
rcu_dereference_protected(n->next,
157+
lockdep_is_held(&tbl->lock)));
154158
n->dead = 1;
155159
shrunk = 1;
156160
write_unlock(&n->lock);
@@ -208,14 +212,18 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
208212
lockdep_is_held(&tbl->lock));
209213

210214
for (i = 0; i <= nht->hash_mask; i++) {
211-
struct neighbour *n, **np = &nht->hash_buckets[i];
215+
struct neighbour *n;
216+
struct neighbour __rcu **np = &nht->hash_buckets[i];
212217

213-
while ((n = *np) != NULL) {
218+
while ((n = rcu_dereference_protected(*np,
219+
lockdep_is_held(&tbl->lock))) != NULL) {
214220
if (dev && n->dev != dev) {
215221
np = &n->next;
216222
continue;
217223
}
218-
*np = n->next;
224+
rcu_assign_pointer(*np,
225+
rcu_dereference_protected(n->next,
226+
lockdep_is_held(&tbl->lock)));
219227
write_lock(&n->lock);
220228
neigh_del_timer(n);
221229
n->dead = 1;
@@ -323,7 +331,7 @@ static struct neigh_hash_table *neigh_hash_alloc(unsigned int entries)
323331
kfree(ret);
324332
return NULL;
325333
}
326-
ret->hash_buckets = buckets;
334+
rcu_assign_pointer(ret->hash_buckets, buckets);
327335
ret->hash_mask = entries - 1;
328336
get_random_bytes(&ret->hash_rnd, sizeof(ret->hash_rnd));
329337
return ret;
@@ -362,17 +370,22 @@ static struct neigh_hash_table *neigh_hash_grow(struct neigh_table *tbl,
362370
for (i = 0; i <= old_nht->hash_mask; i++) {
363371
struct neighbour *n, *next;
364372

365-
for (n = old_nht->hash_buckets[i];
373+
for (n = rcu_dereference_protected(old_nht->hash_buckets[i],
374+
lockdep_is_held(&tbl->lock));
366375
n != NULL;
367376
n = next) {
368377
hash = tbl->hash(n->primary_key, n->dev,
369378
new_nht->hash_rnd);
370379

371380
hash &= new_nht->hash_mask;
372-
next = n->next;
373-
374-
n->next = new_nht->hash_buckets[hash];
375-
new_nht->hash_buckets[hash] = n;
381+
next = rcu_dereference_protected(n->next,
382+
lockdep_is_held(&tbl->lock));
383+
384+
rcu_assign_pointer(n->next,
385+
rcu_dereference_protected(
386+
new_nht->hash_buckets[hash],
387+
lockdep_is_held(&tbl->lock)));
388+
rcu_assign_pointer(new_nht->hash_buckets[hash], n);
376389
}
377390
}
378391

@@ -394,15 +407,18 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
394407
rcu_read_lock_bh();
395408
nht = rcu_dereference_bh(tbl->nht);
396409
hash_val = tbl->hash(pkey, dev, nht->hash_rnd) & nht->hash_mask;
397-
read_lock(&tbl->lock);
398-
for (n = nht->hash_buckets[hash_val]; n; n = n->next) {
410+
411+
for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]);
412+
n != NULL;
413+
n = rcu_dereference_bh(n->next)) {
399414
if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
400-
neigh_hold(n);
415+
if (!atomic_inc_not_zero(&n->refcnt))
416+
n = NULL;
401417
NEIGH_CACHE_STAT_INC(tbl, hits);
402418
break;
403419
}
404420
}
405-
read_unlock(&tbl->lock);
421+
406422
rcu_read_unlock_bh();
407423
return n;
408424
}
@@ -421,16 +437,19 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
421437
rcu_read_lock_bh();
422438
nht = rcu_dereference_bh(tbl->nht);
423439
hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) & nht->hash_mask;
424-
read_lock(&tbl->lock);
425-
for (n = nht->hash_buckets[hash_val]; n; n = n->next) {
440+
441+
for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]);
442+
n != NULL;
443+
n = rcu_dereference_bh(n->next)) {
426444
if (!memcmp(n->primary_key, pkey, key_len) &&
427445
net_eq(dev_net(n->dev), net)) {
428-
neigh_hold(n);
446+
if (!atomic_inc_not_zero(&n->refcnt))
447+
n = NULL;
429448
NEIGH_CACHE_STAT_INC(tbl, hits);
430449
break;
431450
}
432451
}
433-
read_unlock(&tbl->lock);
452+
434453
rcu_read_unlock_bh();
435454
return n;
436455
}
@@ -483,18 +502,24 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
483502
goto out_tbl_unlock;
484503
}
485504

486-
for (n1 = nht->hash_buckets[hash_val]; n1; n1 = n1->next) {
505+
for (n1 = rcu_dereference_protected(nht->hash_buckets[hash_val],
506+
lockdep_is_held(&tbl->lock));
507+
n1 != NULL;
508+
n1 = rcu_dereference_protected(n1->next,
509+
lockdep_is_held(&tbl->lock))) {
487510
if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
488511
neigh_hold(n1);
489512
rc = n1;
490513
goto out_tbl_unlock;
491514
}
492515
}
493516

494-
n->next = nht->hash_buckets[hash_val];
495-
nht->hash_buckets[hash_val] = n;
496517
n->dead = 0;
497518
neigh_hold(n);
519+
rcu_assign_pointer(n->next,
520+
rcu_dereference_protected(nht->hash_buckets[hash_val],
521+
lockdep_is_held(&tbl->lock)));
522+
rcu_assign_pointer(nht->hash_buckets[hash_val], n);
498523
write_unlock_bh(&tbl->lock);
499524
NEIGH_PRINTK2("neigh %p is created.\n", n);
500525
rc = n;
@@ -651,6 +676,12 @@ static inline void neigh_parms_put(struct neigh_parms *parms)
651676
neigh_parms_destroy(parms);
652677
}
653678

679+
static void neigh_destroy_rcu(struct rcu_head *head)
680+
{
681+
struct neighbour *neigh = container_of(head, struct neighbour, rcu);
682+
683+
kmem_cache_free(neigh->tbl->kmem_cachep, neigh);
684+
}
654685
/*
655686
* neighbour must already be out of the table;
656687
*
@@ -690,7 +721,7 @@ void neigh_destroy(struct neighbour *neigh)
690721
NEIGH_PRINTK2("neigh %p is destroyed.\n", neigh);
691722

692723
atomic_dec(&neigh->tbl->entries);
693-
kmem_cache_free(neigh->tbl->kmem_cachep, neigh);
724+
call_rcu(&neigh->rcu, neigh_destroy_rcu);
694725
}
695726
EXPORT_SYMBOL(neigh_destroy);
696727

@@ -731,7 +762,8 @@ static void neigh_connect(struct neighbour *neigh)
731762
static void neigh_periodic_work(struct work_struct *work)
732763
{
733764
struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work);
734-
struct neighbour *n, **np;
765+
struct neighbour *n;
766+
struct neighbour __rcu **np;
735767
unsigned int i;
736768
struct neigh_hash_table *nht;
737769

@@ -756,7 +788,8 @@ static void neigh_periodic_work(struct work_struct *work)
756788
for (i = 0 ; i <= nht->hash_mask; i++) {
757789
np = &nht->hash_buckets[i];
758790

759-
while ((n = *np) != NULL) {
791+
while ((n = rcu_dereference_protected(*np,
792+
lockdep_is_held(&tbl->lock))) != NULL) {
760793
unsigned int state;
761794

762795
write_lock(&n->lock);
@@ -1213,8 +1246,8 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,
12131246
}
12141247

12151248
/* This function can be used in contexts, where only old dev_queue_xmit
1216-
worked, f.e. if you want to override normal output path (eql, shaper),
1217-
but resolution is not made yet.
1249+
* worked, f.e. if you want to override normal output path (eql, shaper),
1250+
* but resolution is not made yet.
12181251
*/
12191252

12201253
int neigh_compat_output(struct sk_buff *skb)
@@ -2123,7 +2156,7 @@ static void neigh_update_notify(struct neighbour *neigh)
21232156
static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
21242157
struct netlink_callback *cb)
21252158
{
2126-
struct net * net = sock_net(skb->sk);
2159+
struct net *net = sock_net(skb->sk);
21272160
struct neighbour *n;
21282161
int rc, h, s_h = cb->args[1];
21292162
int idx, s_idx = idx = cb->args[2];
@@ -2132,13 +2165,14 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
21322165
rcu_read_lock_bh();
21332166
nht = rcu_dereference_bh(tbl->nht);
21342167

2135-
read_lock(&tbl->lock);
21362168
for (h = 0; h <= nht->hash_mask; h++) {
21372169
if (h < s_h)
21382170
continue;
21392171
if (h > s_h)
21402172
s_idx = 0;
2141-
for (n = nht->hash_buckets[h], idx = 0; n; n = n->next) {
2173+
for (n = rcu_dereference_bh(nht->hash_buckets[h]), idx = 0;
2174+
n != NULL;
2175+
n = rcu_dereference_bh(n->next)) {
21422176
if (!net_eq(dev_net(n->dev), net))
21432177
continue;
21442178
if (idx < s_idx)
@@ -2150,13 +2184,12 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
21502184
rc = -1;
21512185
goto out;
21522186
}
2153-
next:
2187+
next:
21542188
idx++;
21552189
}
21562190
}
21572191
rc = skb->len;
21582192
out:
2159-
read_unlock(&tbl->lock);
21602193
rcu_read_unlock_bh();
21612194
cb->args[1] = h;
21622195
cb->args[2] = idx;
@@ -2195,11 +2228,13 @@ void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void
21952228
rcu_read_lock_bh();
21962229
nht = rcu_dereference_bh(tbl->nht);
21972230

2198-
read_lock(&tbl->lock);
2231+
read_lock(&tbl->lock); /* avoid resizes */
21992232
for (chain = 0; chain <= nht->hash_mask; chain++) {
22002233
struct neighbour *n;
22012234

2202-
for (n = nht->hash_buckets[chain]; n; n = n->next)
2235+
for (n = rcu_dereference_bh(nht->hash_buckets[chain]);
2236+
n != NULL;
2237+
n = rcu_dereference_bh(n->next))
22032238
cb(n, cookie);
22042239
}
22052240
read_unlock(&tbl->lock);
@@ -2217,16 +2252,20 @@ void __neigh_for_each_release(struct neigh_table *tbl,
22172252
nht = rcu_dereference_protected(tbl->nht,
22182253
lockdep_is_held(&tbl->lock));
22192254
for (chain = 0; chain <= nht->hash_mask; chain++) {
2220-
struct neighbour *n, **np;
2255+
struct neighbour *n;
2256+
struct neighbour __rcu **np;
22212257

22222258
np = &nht->hash_buckets[chain];
2223-
while ((n = *np) != NULL) {
2259+
while ((n = rcu_dereference_protected(*np,
2260+
lockdep_is_held(&tbl->lock))) != NULL) {
22242261
int release;
22252262

22262263
write_lock(&n->lock);
22272264
release = cb(n);
22282265
if (release) {
2229-
*np = n->next;
2266+
rcu_assign_pointer(*np,
2267+
rcu_dereference_protected(n->next,
2268+
lockdep_is_held(&tbl->lock)));
22302269
n->dead = 1;
22312270
} else
22322271
np = &n->next;
@@ -2250,7 +2289,7 @@ static struct neighbour *neigh_get_first(struct seq_file *seq)
22502289

22512290
state->flags &= ~NEIGH_SEQ_IS_PNEIGH;
22522291
for (bucket = 0; bucket <= nht->hash_mask; bucket++) {
2253-
n = nht->hash_buckets[bucket];
2292+
n = rcu_dereference_bh(nht->hash_buckets[bucket]);
22542293

22552294
while (n) {
22562295
if (!net_eq(dev_net(n->dev), net))
@@ -2267,8 +2306,8 @@ static struct neighbour *neigh_get_first(struct seq_file *seq)
22672306
break;
22682307
if (n->nud_state & ~NUD_NOARP)
22692308
break;
2270-
next:
2271-
n = n->next;
2309+
next:
2310+
n = rcu_dereference_bh(n->next);
22722311
}
22732312

22742313
if (n)
@@ -2292,7 +2331,7 @@ static struct neighbour *neigh_get_next(struct seq_file *seq,
22922331
if (v)
22932332
return n;
22942333
}
2295-
n = n->next;
2334+
n = rcu_dereference_bh(n->next);
22962335

22972336
while (1) {
22982337
while (n) {
@@ -2309,8 +2348,8 @@ static struct neighbour *neigh_get_next(struct seq_file *seq,
23092348

23102349
if (n->nud_state & ~NUD_NOARP)
23112350
break;
2312-
next:
2313-
n = n->next;
2351+
next:
2352+
n = rcu_dereference_bh(n->next);
23142353
}
23152354

23162355
if (n)
@@ -2319,7 +2358,7 @@ static struct neighbour *neigh_get_next(struct seq_file *seq,
23192358
if (++state->bucket > nht->hash_mask)
23202359
break;
23212360

2322-
n = nht->hash_buckets[state->bucket];
2361+
n = rcu_dereference_bh(nht->hash_buckets[state->bucket]);
23232362
}
23242363

23252364
if (n && pos)
@@ -2417,7 +2456,6 @@ static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos)
24172456
}
24182457

24192458
void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags)
2420-
__acquires(tbl->lock)
24212459
__acquires(rcu_bh)
24222460
{
24232461
struct neigh_seq_state *state = seq->private;
@@ -2428,7 +2466,7 @@ void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl
24282466

24292467
rcu_read_lock_bh();
24302468
state->nht = rcu_dereference_bh(tbl->nht);
2431-
read_lock(&tbl->lock);
2469+
24322470
return *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN;
24332471
}
24342472
EXPORT_SYMBOL(neigh_seq_start);
@@ -2461,13 +2499,8 @@ void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
24612499
EXPORT_SYMBOL(neigh_seq_next);
24622500

24632501
void neigh_seq_stop(struct seq_file *seq, void *v)
2464-
__releases(tbl->lock)
24652502
__releases(rcu_bh)
24662503
{
2467-
struct neigh_seq_state *state = seq->private;
2468-
struct neigh_table *tbl = state->tbl;
2469-
2470-
read_unlock(&tbl->lock);
24712504
rcu_read_unlock_bh();
24722505
}
24732506
EXPORT_SYMBOL(neigh_seq_stop);

0 commit comments

Comments
 (0)