Skip to content

Commit 5c578ae

Browse files
stephen hemmingerdavem330
stephen hemminger
authored andcommitted
IPv6: convert addrconf hash list to RCU
Convert from reader/writer lock to RCU and spinlock for addrconf hash list. Adds an additional helper macro for hlist_for_each_entry_continue_rcu to handle the continue case. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent c2e2129 commit 5c578ae

File tree

3 files changed

+58
-41
lines changed

3 files changed

+58
-41
lines changed

include/linux/rculist.h

+13
Original file line numberDiff line numberDiff line change
@@ -428,5 +428,18 @@ static inline void hlist_add_after_rcu(struct hlist_node *prev,
428428
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \
429429
pos = rcu_dereference_raw(pos->next))
430430

431+
/**
432+
* hlist_for_each_entry_continue_rcu - iterate over a hlist continuing after current point
433+
* @tpos: the type * to use as a loop cursor.
434+
* @pos: the &struct hlist_node to use as a loop cursor.
435+
* @member: the name of the hlist_node within the struct.
436+
*/
437+
#define hlist_for_each_entry_continue_rcu(tpos, pos, member) \
438+
for (pos = rcu_dereference((pos)->next); \
439+
pos && ({ prefetch(pos->next); 1; }) && \
440+
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \
441+
pos = rcu_dereference(pos->next))
442+
443+
431444
#endif /* __KERNEL__ */
432445
#endif

include/net/if_inet6.h

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct inet6_ifaddr {
6464
#endif
6565

6666
int dead;
67+
struct rcu_head rcu;
6768
};
6869

6970
struct ip6_sf_socklist {

net/ipv6/addrconf.c

+44-41
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ static int ipv6_count_addresses(struct inet6_dev *idev);
127127
* Configured unicast address hash table
128128
*/
129129
static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE];
130-
static DEFINE_RWLOCK(addrconf_hash_lock);
130+
static DEFINE_SPINLOCK(addrconf_hash_lock);
131131

132132
static void addrconf_verify(unsigned long);
133133

@@ -523,8 +523,13 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
523523
}
524524
#endif
525525

526-
/* Nobody refers to this ifaddr, destroy it */
526+
static void inet6_ifa_finish_destroy_rcu(struct rcu_head *head)
527+
{
528+
struct inet6_ifaddr *ifp = container_of(head, struct inet6_ifaddr, rcu);
529+
kfree(ifp);
530+
}
527531

532+
/* Nobody refers to this ifaddr, destroy it */
528533
void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
529534
{
530535
WARN_ON(ifp->if_next != NULL);
@@ -545,7 +550,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
545550
}
546551
dst_release(&ifp->rt->u.dst);
547552

548-
kfree(ifp);
553+
call_rcu(&ifp->rcu, inet6_ifa_finish_destroy_rcu);
549554
}
550555

551556
static void
@@ -616,7 +621,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
616621
goto out2;
617622
}
618623

619-
write_lock(&addrconf_hash_lock);
624+
spin_lock(&addrconf_hash_lock);
620625

621626
/* Ignore adding duplicate addresses on an interface */
622627
if (ipv6_chk_same_addr(dev_net(idev->dev), addr, idev->dev)) {
@@ -670,9 +675,9 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
670675
/* Add to big hash table */
671676
hash = ipv6_addr_hash(addr);
672677

673-
hlist_add_head(&ifa->addr_lst, &inet6_addr_lst[hash]);
678+
hlist_add_head_rcu(&ifa->addr_lst, &inet6_addr_lst[hash]);
674679
in6_ifa_hold(ifa);
675-
write_unlock(&addrconf_hash_lock);
680+
spin_unlock(&addrconf_hash_lock);
676681

677682
write_lock(&idev->lock);
678683
/* Add to inet6_dev unicast addr list. */
@@ -699,7 +704,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
699704

700705
return ifa;
701706
out:
702-
write_unlock(&addrconf_hash_lock);
707+
spin_unlock(&addrconf_hash_lock);
703708
goto out2;
704709
}
705710

@@ -717,10 +722,10 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
717722

718723
ifp->dead = 1;
719724

720-
write_lock_bh(&addrconf_hash_lock);
721-
hlist_del_init(&ifp->addr_lst);
725+
spin_lock_bh(&addrconf_hash_lock);
726+
hlist_del_init_rcu(&ifp->addr_lst);
722727
__in6_ifa_put(ifp);
723-
write_unlock_bh(&addrconf_hash_lock);
728+
spin_unlock_bh(&addrconf_hash_lock);
724729

725730
write_lock_bh(&idev->lock);
726731
#ifdef CONFIG_IPV6_PRIVACY
@@ -1274,8 +1279,8 @@ int ipv6_chk_addr(struct net *net, struct in6_addr *addr,
12741279
struct hlist_node *node;
12751280
u8 hash = ipv6_addr_hash(addr);
12761281

1277-
read_lock_bh(&addrconf_hash_lock);
1278-
hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) {
1282+
rcu_read_lock_bh();
1283+
hlist_for_each_entry_rcu(ifp, node, &inet6_addr_lst[hash], addr_lst) {
12791284
if (!net_eq(dev_net(ifp->idev->dev), net))
12801285
continue;
12811286
if (ipv6_addr_equal(&ifp->addr, addr) &&
@@ -1285,7 +1290,8 @@ int ipv6_chk_addr(struct net *net, struct in6_addr *addr,
12851290
break;
12861291
}
12871292
}
1288-
read_unlock_bh(&addrconf_hash_lock);
1293+
rcu_read_unlock_bh();
1294+
12891295
return ifp != NULL;
12901296
}
12911297
EXPORT_SYMBOL(ipv6_chk_addr);
@@ -1341,8 +1347,8 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add
13411347
struct hlist_node *node;
13421348
u8 hash = ipv6_addr_hash(addr);
13431349

1344-
read_lock_bh(&addrconf_hash_lock);
1345-
hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) {
1350+
rcu_read_lock_bh();
1351+
hlist_for_each_entry_rcu(ifp, node, &inet6_addr_lst[hash], addr_lst) {
13461352
if (!net_eq(dev_net(ifp->idev->dev), net))
13471353
continue;
13481354
if (ipv6_addr_equal(&ifp->addr, addr)) {
@@ -1353,7 +1359,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add
13531359
}
13541360
}
13551361
}
1356-
read_unlock_bh(&addrconf_hash_lock);
1362+
rcu_read_unlock_bh();
13571363

13581364
return ifp;
13591365
}
@@ -2698,10 +2704,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
26982704
write_unlock_bh(&idev->lock);
26992705

27002706
/* clear hash table */
2701-
write_lock_bh(&addrconf_hash_lock);
2702-
hlist_del_init(&ifa->addr_lst);
2707+
spin_lock_bh(&addrconf_hash_lock);
2708+
hlist_del_init_rcu(&ifa->addr_lst);
27032709
__in6_ifa_put(ifa);
2704-
write_unlock_bh(&addrconf_hash_lock);
2710+
spin_unlock_bh(&addrconf_hash_lock);
27052711

27062712
__ipv6_ifa_notify(RTM_DELADDR, ifa);
27072713
atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
@@ -2946,11 +2952,10 @@ static struct inet6_ifaddr *if6_get_first(struct seq_file *seq)
29462952

29472953
for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) {
29482954
struct hlist_node *n;
2949-
hlist_for_each_entry(ifa, n,
2950-
&inet6_addr_lst[state->bucket], addr_lst) {
2955+
hlist_for_each_entry_rcu(ifa, n, &inet6_addr_lst[state->bucket],
2956+
addr_lst)
29512957
if (net_eq(dev_net(ifa->idev->dev), net))
29522958
return ifa;
2953-
}
29542959
}
29552960
return NULL;
29562961
}
@@ -2962,10 +2967,9 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq,
29622967
struct net *net = seq_file_net(seq);
29632968
struct hlist_node *n = &ifa->addr_lst;
29642969

2965-
hlist_for_each_entry_continue(ifa, n, addr_lst) {
2970+
hlist_for_each_entry_continue_rcu(ifa, n, addr_lst)
29662971
if (net_eq(dev_net(ifa->idev->dev), net))
29672972
return ifa;
2968-
}
29692973

29702974
while (++state->bucket < IN6_ADDR_HSIZE) {
29712975
hlist_for_each_entry(ifa, n,
@@ -2989,9 +2993,9 @@ static struct inet6_ifaddr *if6_get_idx(struct seq_file *seq, loff_t pos)
29892993
}
29902994

29912995
static void *if6_seq_start(struct seq_file *seq, loff_t *pos)
2992-
__acquires(addrconf_hash_lock)
2996+
__acquires(rcu)
29932997
{
2994-
read_lock_bh(&addrconf_hash_lock);
2998+
rcu_read_lock_bh();
29952999
return if6_get_idx(seq, *pos);
29963000
}
29973001

@@ -3005,9 +3009,9 @@ static void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
30053009
}
30063010

30073011
static void if6_seq_stop(struct seq_file *seq, void *v)
3008-
__releases(addrconf_hash_lock)
3012+
__releases(rcu)
30093013
{
3010-
read_unlock_bh(&addrconf_hash_lock);
3014+
rcu_read_unlock_bh();
30113015
}
30123016

30133017
static int if6_seq_show(struct seq_file *seq, void *v)
@@ -3081,8 +3085,8 @@ int ipv6_chk_home_addr(struct net *net, struct in6_addr *addr)
30813085
struct hlist_node *n;
30823086
u8 hash = ipv6_addr_hash(addr);
30833087

3084-
read_lock_bh(&addrconf_hash_lock);
3085-
hlist_for_each_entry(ifp, n, &inet6_addr_lst[hash], addr_lst) {
3088+
rcu_read_lock_bh();
3089+
hlist_for_each_entry_rcu(ifp, n, &inet6_addr_lst[hash], addr_lst) {
30863090
if (!net_eq(dev_net(ifp->idev->dev), net))
30873091
continue;
30883092
if (ipv6_addr_equal(&ifp->addr, addr) &&
@@ -3091,7 +3095,7 @@ int ipv6_chk_home_addr(struct net *net, struct in6_addr *addr)
30913095
break;
30923096
}
30933097
}
3094-
read_unlock_bh(&addrconf_hash_lock);
3098+
rcu_read_unlock_bh();
30953099
return ret;
30963100
}
30973101
#endif
@@ -3107,7 +3111,8 @@ static void addrconf_verify(unsigned long foo)
31073111
unsigned long now, next;
31083112
int i;
31093113

3110-
spin_lock_bh(&addrconf_verify_lock);
3114+
rcu_read_lock_bh();
3115+
spin_lock(&addrconf_verify_lock);
31113116
now = jiffies;
31123117
next = now + ADDR_CHECK_FREQUENCY;
31133118

@@ -3116,8 +3121,8 @@ static void addrconf_verify(unsigned long foo)
31163121
for (i=0; i < IN6_ADDR_HSIZE; i++) {
31173122

31183123
restart:
3119-
read_lock(&addrconf_hash_lock);
3120-
hlist_for_each_entry(ifp, node, &inet6_addr_lst[i], addr_lst) {
3124+
hlist_for_each_entry_rcu(ifp, node,
3125+
&inet6_addr_lst[i], addr_lst) {
31213126
unsigned long age;
31223127
#ifdef CONFIG_IPV6_PRIVACY
31233128
unsigned long regen_advance;
@@ -3139,7 +3144,6 @@ static void addrconf_verify(unsigned long foo)
31393144
age >= ifp->valid_lft) {
31403145
spin_unlock(&ifp->lock);
31413146
in6_ifa_hold(ifp);
3142-
read_unlock(&addrconf_hash_lock);
31433147
ipv6_del_addr(ifp);
31443148
goto restart;
31453149
} else if (ifp->prefered_lft == INFINITY_LIFE_TIME) {
@@ -3161,7 +3165,6 @@ static void addrconf_verify(unsigned long foo)
31613165

31623166
if (deprecate) {
31633167
in6_ifa_hold(ifp);
3164-
read_unlock(&addrconf_hash_lock);
31653168

31663169
ipv6_ifa_notify(0, ifp);
31673170
in6_ifa_put(ifp);
@@ -3179,7 +3182,7 @@ static void addrconf_verify(unsigned long foo)
31793182
in6_ifa_hold(ifp);
31803183
in6_ifa_hold(ifpub);
31813184
spin_unlock(&ifp->lock);
3182-
read_unlock(&addrconf_hash_lock);
3185+
31833186
spin_lock(&ifpub->lock);
31843187
ifpub->regen_count = 0;
31853188
spin_unlock(&ifpub->lock);
@@ -3199,12 +3202,12 @@ static void addrconf_verify(unsigned long foo)
31993202
spin_unlock(&ifp->lock);
32003203
}
32013204
}
3202-
read_unlock(&addrconf_hash_lock);
32033205
}
32043206

32053207
addr_chk_timer.expires = time_before(next, jiffies + HZ) ? jiffies + HZ : next;
32063208
add_timer(&addr_chk_timer);
3207-
spin_unlock_bh(&addrconf_verify_lock);
3209+
spin_unlock(&addrconf_verify_lock);
3210+
rcu_read_unlock_bh();
32083211
}
32093212

32103213
static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local)
@@ -4621,10 +4624,10 @@ void addrconf_cleanup(void)
46214624
/*
46224625
* Check hash table.
46234626
*/
4624-
write_lock_bh(&addrconf_hash_lock);
4627+
spin_lock_bh(&addrconf_hash_lock);
46254628
for (i = 0; i < IN6_ADDR_HSIZE; i++)
46264629
WARN_ON(!hlist_empty(&inet6_addr_lst[i]));
4627-
write_unlock_bh(&addrconf_hash_lock);
4630+
spin_unlock_bh(&addrconf_hash_lock);
46284631

46294632
del_timer(&addr_chk_timer);
46304633
rtnl_unlock();

0 commit comments

Comments
 (0)