diff options
author | stephen hemminger <shemminger@vyatta.com> | 2010-03-17 16:31:11 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-03-20 18:44:35 -0400 |
commit | 5c578aedcb21d79eeb4e9cf04ca5b276ac82614c (patch) | |
tree | e6d682d3072fda2ae3977beca2beb23d222b6911 | |
parent | c2e21293c054817c42eb5fa9c613d2ad51954136 (diff) |
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>
-rw-r--r-- | include/linux/rculist.h | 13 | ||||
-rw-r--r-- | include/net/if_inet6.h | 1 | ||||
-rw-r--r-- | net/ipv6/addrconf.c | 85 |
3 files changed, 58 insertions, 41 deletions
diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 2c9b46cff3d7..004908b104d5 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h | |||
@@ -428,5 +428,18 @@ static inline void hlist_add_after_rcu(struct hlist_node *prev, | |||
428 | ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \ | 428 | ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \ |
429 | pos = rcu_dereference_raw(pos->next)) | 429 | pos = rcu_dereference_raw(pos->next)) |
430 | 430 | ||
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 | |||
431 | #endif /* __KERNEL__ */ | 444 | #endif /* __KERNEL__ */ |
432 | #endif | 445 | #endif |
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 498401541519..22a00b1d2c38 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h | |||
@@ -64,6 +64,7 @@ struct inet6_ifaddr { | |||
64 | #endif | 64 | #endif |
65 | 65 | ||
66 | int dead; | 66 | int dead; |
67 | struct rcu_head rcu; | ||
67 | }; | 68 | }; |
68 | 69 | ||
69 | struct ip6_sf_socklist { | 70 | struct ip6_sf_socklist { |
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 0488b9f8071d..7ffd5eeab967 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -127,7 +127,7 @@ static int ipv6_count_addresses(struct inet6_dev *idev); | |||
127 | * Configured unicast address hash table | 127 | * Configured unicast address hash table |
128 | */ | 128 | */ |
129 | static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE]; | 129 | static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE]; |
130 | static DEFINE_RWLOCK(addrconf_hash_lock); | 130 | static DEFINE_SPINLOCK(addrconf_hash_lock); |
131 | 131 | ||
132 | static void addrconf_verify(unsigned long); | 132 | static void addrconf_verify(unsigned long); |
133 | 133 | ||
@@ -523,8 +523,13 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) | |||
523 | } | 523 | } |
524 | #endif | 524 | #endif |
525 | 525 | ||
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 | } | ||
527 | 531 | ||
532 | /* Nobody refers to this ifaddr, destroy it */ | ||
528 | void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) | 533 | void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) |
529 | { | 534 | { |
530 | WARN_ON(ifp->if_next != NULL); | 535 | WARN_ON(ifp->if_next != NULL); |
@@ -545,7 +550,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) | |||
545 | } | 550 | } |
546 | dst_release(&ifp->rt->u.dst); | 551 | dst_release(&ifp->rt->u.dst); |
547 | 552 | ||
548 | kfree(ifp); | 553 | call_rcu(&ifp->rcu, inet6_ifa_finish_destroy_rcu); |
549 | } | 554 | } |
550 | 555 | ||
551 | static void | 556 | static void |
@@ -616,7 +621,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, | |||
616 | goto out2; | 621 | goto out2; |
617 | } | 622 | } |
618 | 623 | ||
619 | write_lock(&addrconf_hash_lock); | 624 | spin_lock(&addrconf_hash_lock); |
620 | 625 | ||
621 | /* Ignore adding duplicate addresses on an interface */ | 626 | /* Ignore adding duplicate addresses on an interface */ |
622 | if (ipv6_chk_same_addr(dev_net(idev->dev), addr, idev->dev)) { | 627 | 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, | |||
670 | /* Add to big hash table */ | 675 | /* Add to big hash table */ |
671 | hash = ipv6_addr_hash(addr); | 676 | hash = ipv6_addr_hash(addr); |
672 | 677 | ||
673 | hlist_add_head(&ifa->addr_lst, &inet6_addr_lst[hash]); | 678 | hlist_add_head_rcu(&ifa->addr_lst, &inet6_addr_lst[hash]); |
674 | in6_ifa_hold(ifa); | 679 | in6_ifa_hold(ifa); |
675 | write_unlock(&addrconf_hash_lock); | 680 | spin_unlock(&addrconf_hash_lock); |
676 | 681 | ||
677 | write_lock(&idev->lock); | 682 | write_lock(&idev->lock); |
678 | /* Add to inet6_dev unicast addr list. */ | 683 | /* Add to inet6_dev unicast addr list. */ |
@@ -699,7 +704,7 @@ out2: | |||
699 | 704 | ||
700 | return ifa; | 705 | return ifa; |
701 | out: | 706 | out: |
702 | write_unlock(&addrconf_hash_lock); | 707 | spin_unlock(&addrconf_hash_lock); |
703 | goto out2; | 708 | goto out2; |
704 | } | 709 | } |
705 | 710 | ||
@@ -717,10 +722,10 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) | |||
717 | 722 | ||
718 | ifp->dead = 1; | 723 | ifp->dead = 1; |
719 | 724 | ||
720 | write_lock_bh(&addrconf_hash_lock); | 725 | spin_lock_bh(&addrconf_hash_lock); |
721 | hlist_del_init(&ifp->addr_lst); | 726 | hlist_del_init_rcu(&ifp->addr_lst); |
722 | __in6_ifa_put(ifp); | 727 | __in6_ifa_put(ifp); |
723 | write_unlock_bh(&addrconf_hash_lock); | 728 | spin_unlock_bh(&addrconf_hash_lock); |
724 | 729 | ||
725 | write_lock_bh(&idev->lock); | 730 | write_lock_bh(&idev->lock); |
726 | #ifdef CONFIG_IPV6_PRIVACY | 731 | #ifdef CONFIG_IPV6_PRIVACY |
@@ -1274,8 +1279,8 @@ int ipv6_chk_addr(struct net *net, struct in6_addr *addr, | |||
1274 | struct hlist_node *node; | 1279 | struct hlist_node *node; |
1275 | u8 hash = ipv6_addr_hash(addr); | 1280 | u8 hash = ipv6_addr_hash(addr); |
1276 | 1281 | ||
1277 | read_lock_bh(&addrconf_hash_lock); | 1282 | rcu_read_lock_bh(); |
1278 | hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) { | 1283 | hlist_for_each_entry_rcu(ifp, node, &inet6_addr_lst[hash], addr_lst) { |
1279 | if (!net_eq(dev_net(ifp->idev->dev), net)) | 1284 | if (!net_eq(dev_net(ifp->idev->dev), net)) |
1280 | continue; | 1285 | continue; |
1281 | if (ipv6_addr_equal(&ifp->addr, addr) && | 1286 | if (ipv6_addr_equal(&ifp->addr, addr) && |
@@ -1285,7 +1290,8 @@ int ipv6_chk_addr(struct net *net, struct in6_addr *addr, | |||
1285 | break; | 1290 | break; |
1286 | } | 1291 | } |
1287 | } | 1292 | } |
1288 | read_unlock_bh(&addrconf_hash_lock); | 1293 | rcu_read_unlock_bh(); |
1294 | |||
1289 | return ifp != NULL; | 1295 | return ifp != NULL; |
1290 | } | 1296 | } |
1291 | EXPORT_SYMBOL(ipv6_chk_addr); | 1297 | EXPORT_SYMBOL(ipv6_chk_addr); |
@@ -1341,8 +1347,8 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add | |||
1341 | struct hlist_node *node; | 1347 | struct hlist_node *node; |
1342 | u8 hash = ipv6_addr_hash(addr); | 1348 | u8 hash = ipv6_addr_hash(addr); |
1343 | 1349 | ||
1344 | read_lock_bh(&addrconf_hash_lock); | 1350 | rcu_read_lock_bh(); |
1345 | hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) { | 1351 | hlist_for_each_entry_rcu(ifp, node, &inet6_addr_lst[hash], addr_lst) { |
1346 | if (!net_eq(dev_net(ifp->idev->dev), net)) | 1352 | if (!net_eq(dev_net(ifp->idev->dev), net)) |
1347 | continue; | 1353 | continue; |
1348 | if (ipv6_addr_equal(&ifp->addr, addr)) { | 1354 | 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 | |||
1353 | } | 1359 | } |
1354 | } | 1360 | } |
1355 | } | 1361 | } |
1356 | read_unlock_bh(&addrconf_hash_lock); | 1362 | rcu_read_unlock_bh(); |
1357 | 1363 | ||
1358 | return ifp; | 1364 | return ifp; |
1359 | } | 1365 | } |
@@ -2698,10 +2704,10 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
2698 | write_unlock_bh(&idev->lock); | 2704 | write_unlock_bh(&idev->lock); |
2699 | 2705 | ||
2700 | /* clear hash table */ | 2706 | /* clear hash table */ |
2701 | write_lock_bh(&addrconf_hash_lock); | 2707 | spin_lock_bh(&addrconf_hash_lock); |
2702 | hlist_del_init(&ifa->addr_lst); | 2708 | hlist_del_init_rcu(&ifa->addr_lst); |
2703 | __in6_ifa_put(ifa); | 2709 | __in6_ifa_put(ifa); |
2704 | write_unlock_bh(&addrconf_hash_lock); | 2710 | spin_unlock_bh(&addrconf_hash_lock); |
2705 | 2711 | ||
2706 | __ipv6_ifa_notify(RTM_DELADDR, ifa); | 2712 | __ipv6_ifa_notify(RTM_DELADDR, ifa); |
2707 | atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); | 2713 | atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); |
@@ -2946,11 +2952,10 @@ static struct inet6_ifaddr *if6_get_first(struct seq_file *seq) | |||
2946 | 2952 | ||
2947 | for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) { | 2953 | for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) { |
2948 | struct hlist_node *n; | 2954 | struct hlist_node *n; |
2949 | hlist_for_each_entry(ifa, n, | 2955 | hlist_for_each_entry_rcu(ifa, n, &inet6_addr_lst[state->bucket], |
2950 | &inet6_addr_lst[state->bucket], addr_lst) { | 2956 | addr_lst) |
2951 | if (net_eq(dev_net(ifa->idev->dev), net)) | 2957 | if (net_eq(dev_net(ifa->idev->dev), net)) |
2952 | return ifa; | 2958 | return ifa; |
2953 | } | ||
2954 | } | 2959 | } |
2955 | return NULL; | 2960 | return NULL; |
2956 | } | 2961 | } |
@@ -2962,10 +2967,9 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq, | |||
2962 | struct net *net = seq_file_net(seq); | 2967 | struct net *net = seq_file_net(seq); |
2963 | struct hlist_node *n = &ifa->addr_lst; | 2968 | struct hlist_node *n = &ifa->addr_lst; |
2964 | 2969 | ||
2965 | hlist_for_each_entry_continue(ifa, n, addr_lst) { | 2970 | hlist_for_each_entry_continue_rcu(ifa, n, addr_lst) |
2966 | if (net_eq(dev_net(ifa->idev->dev), net)) | 2971 | if (net_eq(dev_net(ifa->idev->dev), net)) |
2967 | return ifa; | 2972 | return ifa; |
2968 | } | ||
2969 | 2973 | ||
2970 | while (++state->bucket < IN6_ADDR_HSIZE) { | 2974 | while (++state->bucket < IN6_ADDR_HSIZE) { |
2971 | hlist_for_each_entry(ifa, n, | 2975 | hlist_for_each_entry(ifa, n, |
@@ -2989,9 +2993,9 @@ static struct inet6_ifaddr *if6_get_idx(struct seq_file *seq, loff_t pos) | |||
2989 | } | 2993 | } |
2990 | 2994 | ||
2991 | static void *if6_seq_start(struct seq_file *seq, loff_t *pos) | 2995 | static void *if6_seq_start(struct seq_file *seq, loff_t *pos) |
2992 | __acquires(addrconf_hash_lock) | 2996 | __acquires(rcu) |
2993 | { | 2997 | { |
2994 | read_lock_bh(&addrconf_hash_lock); | 2998 | rcu_read_lock_bh(); |
2995 | return if6_get_idx(seq, *pos); | 2999 | return if6_get_idx(seq, *pos); |
2996 | } | 3000 | } |
2997 | 3001 | ||
@@ -3005,9 +3009,9 @@ static void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |||
3005 | } | 3009 | } |
3006 | 3010 | ||
3007 | static void if6_seq_stop(struct seq_file *seq, void *v) | 3011 | static void if6_seq_stop(struct seq_file *seq, void *v) |
3008 | __releases(addrconf_hash_lock) | 3012 | __releases(rcu) |
3009 | { | 3013 | { |
3010 | read_unlock_bh(&addrconf_hash_lock); | 3014 | rcu_read_unlock_bh(); |
3011 | } | 3015 | } |
3012 | 3016 | ||
3013 | static int if6_seq_show(struct seq_file *seq, void *v) | 3017 | 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) | |||
3081 | struct hlist_node *n; | 3085 | struct hlist_node *n; |
3082 | u8 hash = ipv6_addr_hash(addr); | 3086 | u8 hash = ipv6_addr_hash(addr); |
3083 | 3087 | ||
3084 | read_lock_bh(&addrconf_hash_lock); | 3088 | rcu_read_lock_bh(); |
3085 | hlist_for_each_entry(ifp, n, &inet6_addr_lst[hash], addr_lst) { | 3089 | hlist_for_each_entry_rcu(ifp, n, &inet6_addr_lst[hash], addr_lst) { |
3086 | if (!net_eq(dev_net(ifp->idev->dev), net)) | 3090 | if (!net_eq(dev_net(ifp->idev->dev), net)) |
3087 | continue; | 3091 | continue; |
3088 | if (ipv6_addr_equal(&ifp->addr, addr) && | 3092 | if (ipv6_addr_equal(&ifp->addr, addr) && |
@@ -3091,7 +3095,7 @@ int ipv6_chk_home_addr(struct net *net, struct in6_addr *addr) | |||
3091 | break; | 3095 | break; |
3092 | } | 3096 | } |
3093 | } | 3097 | } |
3094 | read_unlock_bh(&addrconf_hash_lock); | 3098 | rcu_read_unlock_bh(); |
3095 | return ret; | 3099 | return ret; |
3096 | } | 3100 | } |
3097 | #endif | 3101 | #endif |
@@ -3107,7 +3111,8 @@ static void addrconf_verify(unsigned long foo) | |||
3107 | unsigned long now, next; | 3111 | unsigned long now, next; |
3108 | int i; | 3112 | int i; |
3109 | 3113 | ||
3110 | spin_lock_bh(&addrconf_verify_lock); | 3114 | rcu_read_lock_bh(); |
3115 | spin_lock(&addrconf_verify_lock); | ||
3111 | now = jiffies; | 3116 | now = jiffies; |
3112 | next = now + ADDR_CHECK_FREQUENCY; | 3117 | next = now + ADDR_CHECK_FREQUENCY; |
3113 | 3118 | ||
@@ -3116,8 +3121,8 @@ static void addrconf_verify(unsigned long foo) | |||
3116 | for (i=0; i < IN6_ADDR_HSIZE; i++) { | 3121 | for (i=0; i < IN6_ADDR_HSIZE; i++) { |
3117 | 3122 | ||
3118 | restart: | 3123 | restart: |
3119 | read_lock(&addrconf_hash_lock); | 3124 | hlist_for_each_entry_rcu(ifp, node, |
3120 | hlist_for_each_entry(ifp, node, &inet6_addr_lst[i], addr_lst) { | 3125 | &inet6_addr_lst[i], addr_lst) { |
3121 | unsigned long age; | 3126 | unsigned long age; |
3122 | #ifdef CONFIG_IPV6_PRIVACY | 3127 | #ifdef CONFIG_IPV6_PRIVACY |
3123 | unsigned long regen_advance; | 3128 | unsigned long regen_advance; |
@@ -3139,7 +3144,6 @@ restart: | |||
3139 | age >= ifp->valid_lft) { | 3144 | age >= ifp->valid_lft) { |
3140 | spin_unlock(&ifp->lock); | 3145 | spin_unlock(&ifp->lock); |
3141 | in6_ifa_hold(ifp); | 3146 | in6_ifa_hold(ifp); |
3142 | read_unlock(&addrconf_hash_lock); | ||
3143 | ipv6_del_addr(ifp); | 3147 | ipv6_del_addr(ifp); |
3144 | goto restart; | 3148 | goto restart; |
3145 | } else if (ifp->prefered_lft == INFINITY_LIFE_TIME) { | 3149 | } else if (ifp->prefered_lft == INFINITY_LIFE_TIME) { |
@@ -3161,7 +3165,6 @@ restart: | |||
3161 | 3165 | ||
3162 | if (deprecate) { | 3166 | if (deprecate) { |
3163 | in6_ifa_hold(ifp); | 3167 | in6_ifa_hold(ifp); |
3164 | read_unlock(&addrconf_hash_lock); | ||
3165 | 3168 | ||
3166 | ipv6_ifa_notify(0, ifp); | 3169 | ipv6_ifa_notify(0, ifp); |
3167 | in6_ifa_put(ifp); | 3170 | in6_ifa_put(ifp); |
@@ -3179,7 +3182,7 @@ restart: | |||
3179 | in6_ifa_hold(ifp); | 3182 | in6_ifa_hold(ifp); |
3180 | in6_ifa_hold(ifpub); | 3183 | in6_ifa_hold(ifpub); |
3181 | spin_unlock(&ifp->lock); | 3184 | spin_unlock(&ifp->lock); |
3182 | read_unlock(&addrconf_hash_lock); | 3185 | |
3183 | spin_lock(&ifpub->lock); | 3186 | spin_lock(&ifpub->lock); |
3184 | ifpub->regen_count = 0; | 3187 | ifpub->regen_count = 0; |
3185 | spin_unlock(&ifpub->lock); | 3188 | spin_unlock(&ifpub->lock); |
@@ -3199,12 +3202,12 @@ restart: | |||
3199 | spin_unlock(&ifp->lock); | 3202 | spin_unlock(&ifp->lock); |
3200 | } | 3203 | } |
3201 | } | 3204 | } |
3202 | read_unlock(&addrconf_hash_lock); | ||
3203 | } | 3205 | } |
3204 | 3206 | ||
3205 | addr_chk_timer.expires = time_before(next, jiffies + HZ) ? jiffies + HZ : next; | 3207 | addr_chk_timer.expires = time_before(next, jiffies + HZ) ? jiffies + HZ : next; |
3206 | add_timer(&addr_chk_timer); | 3208 | add_timer(&addr_chk_timer); |
3207 | spin_unlock_bh(&addrconf_verify_lock); | 3209 | spin_unlock(&addrconf_verify_lock); |
3210 | rcu_read_unlock_bh(); | ||
3208 | } | 3211 | } |
3209 | 3212 | ||
3210 | static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local) | 3213 | static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local) |
@@ -4621,10 +4624,10 @@ void addrconf_cleanup(void) | |||
4621 | /* | 4624 | /* |
4622 | * Check hash table. | 4625 | * Check hash table. |
4623 | */ | 4626 | */ |
4624 | write_lock_bh(&addrconf_hash_lock); | 4627 | spin_lock_bh(&addrconf_hash_lock); |
4625 | for (i = 0; i < IN6_ADDR_HSIZE; i++) | 4628 | for (i = 0; i < IN6_ADDR_HSIZE; i++) |
4626 | WARN_ON(!hlist_empty(&inet6_addr_lst[i])); | 4629 | WARN_ON(!hlist_empty(&inet6_addr_lst[i])); |
4627 | write_unlock_bh(&addrconf_hash_lock); | 4630 | spin_unlock_bh(&addrconf_hash_lock); |
4628 | 4631 | ||
4629 | del_timer(&addr_chk_timer); | 4632 | del_timer(&addr_chk_timer); |
4630 | rtnl_unlock(); | 4633 | rtnl_unlock(); |