Skip to content

Commit c371893

Browse files
edumazetkuba-moo
authored andcommitted
ipv6: anycast: complete RCU handling of struct ifacaddr6
struct ifacaddr6 are already freed after RCU grace period. Add __rcu qualifier to aca_next pointer, and idev->ac_list Add relevant rcu_assign_pointer() and dereference accessors. ipv6_chk_acast_dev() no longer needs to acquire idev->lock. /proc/net/anycast6 is now purely RCU protected, it no longer acquires idev->lock. Similarly in6_dump_addrs() can use RCU protection to iterate through anycast addresses. It was relying on a mixture of RCU and RTNL but next patches will get rid of RTNL there. Signed-off-by: Eric Dumazet <[email protected]> Reviewed-by: Jiri Pirko <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 5c23796 commit c371893

File tree

3 files changed

+27
-42
lines changed

3 files changed

+27
-42
lines changed

include/net/if_inet6.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ struct ipv6_ac_socklist {
144144
struct ifacaddr6 {
145145
struct in6_addr aca_addr;
146146
struct fib6_info *aca_rt;
147-
struct ifacaddr6 *aca_next;
147+
struct ifacaddr6 __rcu *aca_next;
148148
struct hlist_node aca_addr_lst;
149149
int aca_users;
150150
refcount_t aca_refcnt;
@@ -196,7 +196,7 @@ struct inet6_dev {
196196
spinlock_t mc_report_lock; /* mld query report lock */
197197
struct mutex mc_lock; /* mld global lock */
198198

199-
struct ifacaddr6 *ac_list;
199+
struct ifacaddr6 __rcu *ac_list;
200200
rwlock_t lock;
201201
refcount_t refcnt;
202202
__u32 if_flags;

net/ipv6/addrconf.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5314,8 +5314,8 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb,
53145314
case ANYCAST_ADDR:
53155315
fillargs->event = RTM_GETANYCAST;
53165316
/* anycast address */
5317-
for (ifaca = idev->ac_list; ifaca;
5318-
ifaca = ifaca->aca_next, ip_idx++) {
5317+
for (ifaca = rcu_dereference(idev->ac_list); ifaca;
5318+
ifaca = rcu_dereference(ifaca->aca_next), ip_idx++) {
53195319
if (ip_idx < s_ip_idx)
53205320
continue;
53215321
err = inet6_fill_ifacaddr(skb, ifaca, fillargs);

net/ipv6/anycast.c

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@ int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr)
296296
goto out;
297297
}
298298

299-
for (aca = idev->ac_list; aca; aca = aca->aca_next) {
299+
for (aca = rtnl_dereference(idev->ac_list); aca;
300+
aca = rtnl_dereference(aca->aca_next)) {
300301
if (ipv6_addr_equal(&aca->aca_addr, addr)) {
301302
aca->aca_users++;
302303
err = 0;
@@ -317,13 +318,13 @@ int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr)
317318
goto out;
318319
}
319320

320-
aca->aca_next = idev->ac_list;
321-
idev->ac_list = aca;
322-
323321
/* Hold this for addrconf_join_solict() below before we unlock,
324322
* it is already exposed via idev->ac_list.
325323
*/
326324
aca_get(aca);
325+
aca->aca_next = idev->ac_list;
326+
rcu_assign_pointer(idev->ac_list, aca);
327+
327328
write_unlock_bh(&idev->lock);
328329

329330
ipv6_add_acaddr_hash(net, aca);
@@ -350,7 +351,8 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr)
350351

351352
write_lock_bh(&idev->lock);
352353
prev_aca = NULL;
353-
for (aca = idev->ac_list; aca; aca = aca->aca_next) {
354+
for (aca = rtnl_dereference(idev->ac_list); aca;
355+
aca = rtnl_dereference(aca->aca_next)) {
354356
if (ipv6_addr_equal(&aca->aca_addr, addr))
355357
break;
356358
prev_aca = aca;
@@ -364,9 +366,9 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr)
364366
return 0;
365367
}
366368
if (prev_aca)
367-
prev_aca->aca_next = aca->aca_next;
369+
rcu_assign_pointer(prev_aca->aca_next, aca->aca_next);
368370
else
369-
idev->ac_list = aca->aca_next;
371+
rcu_assign_pointer(idev->ac_list, aca->aca_next);
370372
write_unlock_bh(&idev->lock);
371373
ipv6_del_acaddr_hash(aca);
372374
addrconf_leave_solict(idev, &aca->aca_addr);
@@ -392,8 +394,8 @@ void ipv6_ac_destroy_dev(struct inet6_dev *idev)
392394
struct ifacaddr6 *aca;
393395

394396
write_lock_bh(&idev->lock);
395-
while ((aca = idev->ac_list) != NULL) {
396-
idev->ac_list = aca->aca_next;
397+
while ((aca = rtnl_dereference(idev->ac_list)) != NULL) {
398+
rcu_assign_pointer(idev->ac_list, aca->aca_next);
397399
write_unlock_bh(&idev->lock);
398400

399401
ipv6_del_acaddr_hash(aca);
@@ -420,11 +422,10 @@ static bool ipv6_chk_acast_dev(struct net_device *dev, const struct in6_addr *ad
420422

421423
idev = __in6_dev_get(dev);
422424
if (idev) {
423-
read_lock_bh(&idev->lock);
424-
for (aca = idev->ac_list; aca; aca = aca->aca_next)
425+
for (aca = rcu_dereference(idev->ac_list); aca;
426+
aca = rcu_dereference(aca->aca_next))
425427
if (ipv6_addr_equal(&aca->aca_addr, addr))
426428
break;
427-
read_unlock_bh(&idev->lock);
428429
return aca != NULL;
429430
}
430431
return false;
@@ -477,53 +478,43 @@ bool ipv6_chk_acast_addr_src(struct net *net, struct net_device *dev,
477478
struct ac6_iter_state {
478479
struct seq_net_private p;
479480
struct net_device *dev;
480-
struct inet6_dev *idev;
481481
};
482482

483483
#define ac6_seq_private(seq) ((struct ac6_iter_state *)(seq)->private)
484484

485485
static inline struct ifacaddr6 *ac6_get_first(struct seq_file *seq)
486486
{
487-
struct ifacaddr6 *im = NULL;
488487
struct ac6_iter_state *state = ac6_seq_private(seq);
489488
struct net *net = seq_file_net(seq);
489+
struct ifacaddr6 *im = NULL;
490490

491-
state->idev = NULL;
492491
for_each_netdev_rcu(net, state->dev) {
493492
struct inet6_dev *idev;
493+
494494
idev = __in6_dev_get(state->dev);
495495
if (!idev)
496496
continue;
497-
read_lock_bh(&idev->lock);
498-
im = idev->ac_list;
499-
if (im) {
500-
state->idev = idev;
497+
im = rcu_dereference(idev->ac_list);
498+
if (im)
501499
break;
502-
}
503-
read_unlock_bh(&idev->lock);
504500
}
505501
return im;
506502
}
507503

508504
static struct ifacaddr6 *ac6_get_next(struct seq_file *seq, struct ifacaddr6 *im)
509505
{
510506
struct ac6_iter_state *state = ac6_seq_private(seq);
507+
struct inet6_dev *idev;
511508

512-
im = im->aca_next;
509+
im = rcu_dereference(im->aca_next);
513510
while (!im) {
514-
if (likely(state->idev != NULL))
515-
read_unlock_bh(&state->idev->lock);
516-
517511
state->dev = next_net_device_rcu(state->dev);
518-
if (!state->dev) {
519-
state->idev = NULL;
512+
if (!state->dev)
520513
break;
521-
}
522-
state->idev = __in6_dev_get(state->dev);
523-
if (!state->idev)
514+
idev = __in6_dev_get(state->dev);
515+
if (!idev)
524516
continue;
525-
read_lock_bh(&state->idev->lock);
526-
im = state->idev->ac_list;
517+
im = rcu_dereference(idev->ac_list);
527518
}
528519
return im;
529520
}
@@ -555,12 +546,6 @@ static void *ac6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
555546
static void ac6_seq_stop(struct seq_file *seq, void *v)
556547
__releases(RCU)
557548
{
558-
struct ac6_iter_state *state = ac6_seq_private(seq);
559-
560-
if (likely(state->idev != NULL)) {
561-
read_unlock_bh(&state->idev->lock);
562-
state->idev = NULL;
563-
}
564549
rcu_read_unlock();
565550
}
566551

0 commit comments

Comments
 (0)