aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
authorstephen hemminger <shemminger@vyatta.com>2010-03-17 16:31:11 -0400
committerDavid S. Miller <davem@davemloft.net>2010-03-20 18:44:35 -0400
commit5c578aedcb21d79eeb4e9cf04ca5b276ac82614c (patch)
treee6d682d3072fda2ae3977beca2beb23d222b6911 /net/ipv6/addrconf.c
parentc2e21293c054817c42eb5fa9c613d2ad51954136 (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>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r--net/ipv6/addrconf.c85
1 files changed, 44 insertions, 41 deletions
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 */
129static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE]; 129static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE];
130static DEFINE_RWLOCK(addrconf_hash_lock); 130static DEFINE_SPINLOCK(addrconf_hash_lock);
131 131
132static void addrconf_verify(unsigned long); 132static 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 */ 526static 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 */
528void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) 533void 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
551static void 556static 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;
701out: 706out:
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}
1291EXPORT_SYMBOL(ipv6_chk_addr); 1297EXPORT_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
2991static void *if6_seq_start(struct seq_file *seq, loff_t *pos) 2995static 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
3007static void if6_seq_stop(struct seq_file *seq, void *v) 3011static 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
3013static int if6_seq_show(struct seq_file *seq, void *v) 3017static 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
3118restart: 3123restart:
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
3210static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local) 3213static 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();