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 /net | |
| 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>
Diffstat (limited to 'net')
| -rw-r--r-- | net/ipv6/addrconf.c | 85 |
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 | */ |
| 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(); |
