diff options
Diffstat (limited to 'net/ipv6/anycast.c')
-rw-r--r-- | net/ipv6/anycast.c | 80 |
1 files changed, 76 insertions, 4 deletions
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 4e0ff7031edd..94999058e110 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c | |||
@@ -44,8 +44,22 @@ | |||
44 | 44 | ||
45 | #include <net/checksum.h> | 45 | #include <net/checksum.h> |
46 | 46 | ||
47 | #define IN6_ADDR_HSIZE_SHIFT 8 | ||
48 | #define IN6_ADDR_HSIZE BIT(IN6_ADDR_HSIZE_SHIFT) | ||
49 | /* anycast address hash table | ||
50 | */ | ||
51 | static struct hlist_head inet6_acaddr_lst[IN6_ADDR_HSIZE]; | ||
52 | static DEFINE_SPINLOCK(acaddr_hash_lock); | ||
53 | |||
47 | static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr); | 54 | static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr); |
48 | 55 | ||
56 | static u32 inet6_acaddr_hash(struct net *net, const struct in6_addr *addr) | ||
57 | { | ||
58 | u32 val = ipv6_addr_hash(addr) ^ net_hash_mix(net); | ||
59 | |||
60 | return hash_32(val, IN6_ADDR_HSIZE_SHIFT); | ||
61 | } | ||
62 | |||
49 | /* | 63 | /* |
50 | * socket join an anycast group | 64 | * socket join an anycast group |
51 | */ | 65 | */ |
@@ -204,16 +218,39 @@ void ipv6_sock_ac_close(struct sock *sk) | |||
204 | rtnl_unlock(); | 218 | rtnl_unlock(); |
205 | } | 219 | } |
206 | 220 | ||
221 | static void ipv6_add_acaddr_hash(struct net *net, struct ifacaddr6 *aca) | ||
222 | { | ||
223 | unsigned int hash = inet6_acaddr_hash(net, &aca->aca_addr); | ||
224 | |||
225 | spin_lock(&acaddr_hash_lock); | ||
226 | hlist_add_head_rcu(&aca->aca_addr_lst, &inet6_acaddr_lst[hash]); | ||
227 | spin_unlock(&acaddr_hash_lock); | ||
228 | } | ||
229 | |||
230 | static void ipv6_del_acaddr_hash(struct ifacaddr6 *aca) | ||
231 | { | ||
232 | spin_lock(&acaddr_hash_lock); | ||
233 | hlist_del_init_rcu(&aca->aca_addr_lst); | ||
234 | spin_unlock(&acaddr_hash_lock); | ||
235 | } | ||
236 | |||
207 | static void aca_get(struct ifacaddr6 *aca) | 237 | static void aca_get(struct ifacaddr6 *aca) |
208 | { | 238 | { |
209 | refcount_inc(&aca->aca_refcnt); | 239 | refcount_inc(&aca->aca_refcnt); |
210 | } | 240 | } |
211 | 241 | ||
242 | static void aca_free_rcu(struct rcu_head *h) | ||
243 | { | ||
244 | struct ifacaddr6 *aca = container_of(h, struct ifacaddr6, rcu); | ||
245 | |||
246 | fib6_info_release(aca->aca_rt); | ||
247 | kfree(aca); | ||
248 | } | ||
249 | |||
212 | static void aca_put(struct ifacaddr6 *ac) | 250 | static void aca_put(struct ifacaddr6 *ac) |
213 | { | 251 | { |
214 | if (refcount_dec_and_test(&ac->aca_refcnt)) { | 252 | if (refcount_dec_and_test(&ac->aca_refcnt)) { |
215 | fib6_info_release(ac->aca_rt); | 253 | call_rcu(&ac->rcu, aca_free_rcu); |
216 | kfree(ac); | ||
217 | } | 254 | } |
218 | } | 255 | } |
219 | 256 | ||
@@ -229,6 +266,7 @@ static struct ifacaddr6 *aca_alloc(struct fib6_info *f6i, | |||
229 | aca->aca_addr = *addr; | 266 | aca->aca_addr = *addr; |
230 | fib6_info_hold(f6i); | 267 | fib6_info_hold(f6i); |
231 | aca->aca_rt = f6i; | 268 | aca->aca_rt = f6i; |
269 | INIT_HLIST_NODE(&aca->aca_addr_lst); | ||
232 | aca->aca_users = 1; | 270 | aca->aca_users = 1; |
233 | /* aca_tstamp should be updated upon changes */ | 271 | /* aca_tstamp should be updated upon changes */ |
234 | aca->aca_cstamp = aca->aca_tstamp = jiffies; | 272 | aca->aca_cstamp = aca->aca_tstamp = jiffies; |
@@ -285,6 +323,8 @@ int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr) | |||
285 | aca_get(aca); | 323 | aca_get(aca); |
286 | write_unlock_bh(&idev->lock); | 324 | write_unlock_bh(&idev->lock); |
287 | 325 | ||
326 | ipv6_add_acaddr_hash(net, aca); | ||
327 | |||
288 | ip6_ins_rt(net, f6i); | 328 | ip6_ins_rt(net, f6i); |
289 | 329 | ||
290 | addrconf_join_solict(idev->dev, &aca->aca_addr); | 330 | addrconf_join_solict(idev->dev, &aca->aca_addr); |
@@ -325,6 +365,7 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr) | |||
325 | else | 365 | else |
326 | idev->ac_list = aca->aca_next; | 366 | idev->ac_list = aca->aca_next; |
327 | write_unlock_bh(&idev->lock); | 367 | write_unlock_bh(&idev->lock); |
368 | ipv6_del_acaddr_hash(aca); | ||
328 | addrconf_leave_solict(idev, &aca->aca_addr); | 369 | addrconf_leave_solict(idev, &aca->aca_addr); |
329 | 370 | ||
330 | ip6_del_rt(dev_net(idev->dev), aca->aca_rt); | 371 | ip6_del_rt(dev_net(idev->dev), aca->aca_rt); |
@@ -352,6 +393,8 @@ void ipv6_ac_destroy_dev(struct inet6_dev *idev) | |||
352 | idev->ac_list = aca->aca_next; | 393 | idev->ac_list = aca->aca_next; |
353 | write_unlock_bh(&idev->lock); | 394 | write_unlock_bh(&idev->lock); |
354 | 395 | ||
396 | ipv6_del_acaddr_hash(aca); | ||
397 | |||
355 | addrconf_leave_solict(idev, &aca->aca_addr); | 398 | addrconf_leave_solict(idev, &aca->aca_addr); |
356 | 399 | ||
357 | ip6_del_rt(dev_net(idev->dev), aca->aca_rt); | 400 | ip6_del_rt(dev_net(idev->dev), aca->aca_rt); |
@@ -390,17 +433,25 @@ static bool ipv6_chk_acast_dev(struct net_device *dev, const struct in6_addr *ad | |||
390 | bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev, | 433 | bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev, |
391 | const struct in6_addr *addr) | 434 | const struct in6_addr *addr) |
392 | { | 435 | { |
436 | unsigned int hash = inet6_acaddr_hash(net, addr); | ||
437 | struct net_device *nh_dev; | ||
438 | struct ifacaddr6 *aca; | ||
393 | bool found = false; | 439 | bool found = false; |
394 | 440 | ||
395 | rcu_read_lock(); | 441 | rcu_read_lock(); |
396 | if (dev) | 442 | if (dev) |
397 | found = ipv6_chk_acast_dev(dev, addr); | 443 | found = ipv6_chk_acast_dev(dev, addr); |
398 | else | 444 | else |
399 | for_each_netdev_rcu(net, dev) | 445 | hlist_for_each_entry_rcu(aca, &inet6_acaddr_lst[hash], |
400 | if (ipv6_chk_acast_dev(dev, addr)) { | 446 | aca_addr_lst) { |
447 | nh_dev = fib6_info_nh_dev(aca->aca_rt); | ||
448 | if (!nh_dev || !net_eq(dev_net(nh_dev), net)) | ||
449 | continue; | ||
450 | if (ipv6_addr_equal(&aca->aca_addr, addr)) { | ||
401 | found = true; | 451 | found = true; |
402 | break; | 452 | break; |
403 | } | 453 | } |
454 | } | ||
404 | rcu_read_unlock(); | 455 | rcu_read_unlock(); |
405 | return found; | 456 | return found; |
406 | } | 457 | } |
@@ -540,3 +591,24 @@ void ac6_proc_exit(struct net *net) | |||
540 | remove_proc_entry("anycast6", net->proc_net); | 591 | remove_proc_entry("anycast6", net->proc_net); |
541 | } | 592 | } |
542 | #endif | 593 | #endif |
594 | |||
595 | /* Init / cleanup code | ||
596 | */ | ||
597 | int __init ipv6_anycast_init(void) | ||
598 | { | ||
599 | int i; | ||
600 | |||
601 | for (i = 0; i < IN6_ADDR_HSIZE; i++) | ||
602 | INIT_HLIST_HEAD(&inet6_acaddr_lst[i]); | ||
603 | return 0; | ||
604 | } | ||
605 | |||
606 | void ipv6_anycast_cleanup(void) | ||
607 | { | ||
608 | int i; | ||
609 | |||
610 | spin_lock(&acaddr_hash_lock); | ||
611 | for (i = 0; i < IN6_ADDR_HSIZE; i++) | ||
612 | WARN_ON(!hlist_empty(&inet6_acaddr_lst[i])); | ||
613 | spin_unlock(&acaddr_hash_lock); | ||
614 | } | ||