diff options
author | David S. Miller <davem@davemloft.net> | 2015-08-21 14:44:04 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-08-21 14:44:04 -0400 |
commit | dc25b25897289bad4907f30151ffe5baf75ff369 (patch) | |
tree | c99301de2cdf66d7af48fc43b6161bda6faaae70 /net/ipv6/route.c | |
parent | 1a69205c471221a7f9101df164642611e37c52e4 (diff) | |
parent | 0bad90985d39e69ca035fdd70bcc743812641d18 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Conflicts:
drivers/net/usb/qmi_wwan.c
Overlapping additions of new device IDs to qmi_wwan.c
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r-- | net/ipv6/route.c | 79 |
1 files changed, 55 insertions, 24 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6c0fe4c7ce8d..e476f01add87 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -321,8 +321,7 @@ static const struct rt6_info ip6_blk_hole_entry_template = { | |||
321 | /* allocate dst with ip6_dst_ops */ | 321 | /* allocate dst with ip6_dst_ops */ |
322 | static struct rt6_info *__ip6_dst_alloc(struct net *net, | 322 | static struct rt6_info *__ip6_dst_alloc(struct net *net, |
323 | struct net_device *dev, | 323 | struct net_device *dev, |
324 | int flags, | 324 | int flags) |
325 | struct fib6_table *table) | ||
326 | { | 325 | { |
327 | struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, | 326 | struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, |
328 | 0, DST_OBSOLETE_FORCE_CHK, flags); | 327 | 0, DST_OBSOLETE_FORCE_CHK, flags); |
@@ -339,10 +338,9 @@ static struct rt6_info *__ip6_dst_alloc(struct net *net, | |||
339 | 338 | ||
340 | static struct rt6_info *ip6_dst_alloc(struct net *net, | 339 | static struct rt6_info *ip6_dst_alloc(struct net *net, |
341 | struct net_device *dev, | 340 | struct net_device *dev, |
342 | int flags, | 341 | int flags) |
343 | struct fib6_table *table) | ||
344 | { | 342 | { |
345 | struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags, table); | 343 | struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags); |
346 | 344 | ||
347 | if (rt) { | 345 | if (rt) { |
348 | rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC); | 346 | rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC); |
@@ -959,8 +957,7 @@ static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, | |||
959 | if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) | 957 | if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) |
960 | ort = (struct rt6_info *)ort->dst.from; | 958 | ort = (struct rt6_info *)ort->dst.from; |
961 | 959 | ||
962 | rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, | 960 | rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0); |
963 | 0, ort->rt6i_table); | ||
964 | 961 | ||
965 | if (!rt) | 962 | if (!rt) |
966 | return NULL; | 963 | return NULL; |
@@ -992,8 +989,7 @@ static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) | |||
992 | struct rt6_info *pcpu_rt; | 989 | struct rt6_info *pcpu_rt; |
993 | 990 | ||
994 | pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev), | 991 | pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev), |
995 | rt->dst.dev, rt->dst.flags, | 992 | rt->dst.dev, rt->dst.flags); |
996 | rt->rt6i_table); | ||
997 | 993 | ||
998 | if (!pcpu_rt) | 994 | if (!pcpu_rt) |
999 | return NULL; | 995 | return NULL; |
@@ -1006,32 +1002,53 @@ static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) | |||
1006 | /* It should be called with read_lock_bh(&tb6_lock) acquired */ | 1002 | /* It should be called with read_lock_bh(&tb6_lock) acquired */ |
1007 | static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) | 1003 | static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) |
1008 | { | 1004 | { |
1009 | struct rt6_info *pcpu_rt, *prev, **p; | 1005 | struct rt6_info *pcpu_rt, **p; |
1010 | 1006 | ||
1011 | p = this_cpu_ptr(rt->rt6i_pcpu); | 1007 | p = this_cpu_ptr(rt->rt6i_pcpu); |
1012 | pcpu_rt = *p; | 1008 | pcpu_rt = *p; |
1013 | 1009 | ||
1014 | if (pcpu_rt) | 1010 | if (pcpu_rt) { |
1015 | goto done; | 1011 | dst_hold(&pcpu_rt->dst); |
1012 | rt6_dst_from_metrics_check(pcpu_rt); | ||
1013 | } | ||
1014 | return pcpu_rt; | ||
1015 | } | ||
1016 | |||
1017 | static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt) | ||
1018 | { | ||
1019 | struct fib6_table *table = rt->rt6i_table; | ||
1020 | struct rt6_info *pcpu_rt, *prev, **p; | ||
1016 | 1021 | ||
1017 | pcpu_rt = ip6_rt_pcpu_alloc(rt); | 1022 | pcpu_rt = ip6_rt_pcpu_alloc(rt); |
1018 | if (!pcpu_rt) { | 1023 | if (!pcpu_rt) { |
1019 | struct net *net = dev_net(rt->dst.dev); | 1024 | struct net *net = dev_net(rt->dst.dev); |
1020 | 1025 | ||
1021 | pcpu_rt = net->ipv6.ip6_null_entry; | 1026 | dst_hold(&net->ipv6.ip6_null_entry->dst); |
1022 | goto done; | 1027 | return net->ipv6.ip6_null_entry; |
1023 | } | 1028 | } |
1024 | 1029 | ||
1025 | prev = cmpxchg(p, NULL, pcpu_rt); | 1030 | read_lock_bh(&table->tb6_lock); |
1026 | if (prev) { | 1031 | if (rt->rt6i_pcpu) { |
1027 | /* If someone did it before us, return prev instead */ | 1032 | p = this_cpu_ptr(rt->rt6i_pcpu); |
1033 | prev = cmpxchg(p, NULL, pcpu_rt); | ||
1034 | if (prev) { | ||
1035 | /* If someone did it before us, return prev instead */ | ||
1036 | dst_destroy(&pcpu_rt->dst); | ||
1037 | pcpu_rt = prev; | ||
1038 | } | ||
1039 | } else { | ||
1040 | /* rt has been removed from the fib6 tree | ||
1041 | * before we have a chance to acquire the read_lock. | ||
1042 | * In this case, don't brother to create a pcpu rt | ||
1043 | * since rt is going away anyway. The next | ||
1044 | * dst_check() will trigger a re-lookup. | ||
1045 | */ | ||
1028 | dst_destroy(&pcpu_rt->dst); | 1046 | dst_destroy(&pcpu_rt->dst); |
1029 | pcpu_rt = prev; | 1047 | pcpu_rt = rt; |
1030 | } | 1048 | } |
1031 | |||
1032 | done: | ||
1033 | dst_hold(&pcpu_rt->dst); | 1049 | dst_hold(&pcpu_rt->dst); |
1034 | rt6_dst_from_metrics_check(pcpu_rt); | 1050 | rt6_dst_from_metrics_check(pcpu_rt); |
1051 | read_unlock_bh(&table->tb6_lock); | ||
1035 | return pcpu_rt; | 1052 | return pcpu_rt; |
1036 | } | 1053 | } |
1037 | 1054 | ||
@@ -1106,9 +1123,22 @@ redo_rt6_select: | |||
1106 | rt->dst.lastuse = jiffies; | 1123 | rt->dst.lastuse = jiffies; |
1107 | rt->dst.__use++; | 1124 | rt->dst.__use++; |
1108 | pcpu_rt = rt6_get_pcpu_route(rt); | 1125 | pcpu_rt = rt6_get_pcpu_route(rt); |
1109 | read_unlock_bh(&table->tb6_lock); | 1126 | |
1127 | if (pcpu_rt) { | ||
1128 | read_unlock_bh(&table->tb6_lock); | ||
1129 | } else { | ||
1130 | /* We have to do the read_unlock first | ||
1131 | * because rt6_make_pcpu_route() may trigger | ||
1132 | * ip6_dst_gc() which will take the write_lock. | ||
1133 | */ | ||
1134 | dst_hold(&rt->dst); | ||
1135 | read_unlock_bh(&table->tb6_lock); | ||
1136 | pcpu_rt = rt6_make_pcpu_route(rt); | ||
1137 | dst_release(&rt->dst); | ||
1138 | } | ||
1110 | 1139 | ||
1111 | return pcpu_rt; | 1140 | return pcpu_rt; |
1141 | |||
1112 | } | 1142 | } |
1113 | } | 1143 | } |
1114 | 1144 | ||
@@ -1569,7 +1599,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, | |||
1569 | if (unlikely(!idev)) | 1599 | if (unlikely(!idev)) |
1570 | return ERR_PTR(-ENODEV); | 1600 | return ERR_PTR(-ENODEV); |
1571 | 1601 | ||
1572 | rt = ip6_dst_alloc(net, dev, 0, NULL); | 1602 | rt = ip6_dst_alloc(net, dev, 0); |
1573 | if (unlikely(!rt)) { | 1603 | if (unlikely(!rt)) { |
1574 | in6_dev_put(idev); | 1604 | in6_dev_put(idev); |
1575 | dst = ERR_PTR(-ENOMEM); | 1605 | dst = ERR_PTR(-ENOMEM); |
@@ -1756,7 +1786,8 @@ int ip6_route_add(struct fib6_config *cfg) | |||
1756 | if (!table) | 1786 | if (!table) |
1757 | goto out; | 1787 | goto out; |
1758 | 1788 | ||
1759 | rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table); | 1789 | rt = ip6_dst_alloc(net, NULL, |
1790 | (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT); | ||
1760 | 1791 | ||
1761 | if (!rt) { | 1792 | if (!rt) { |
1762 | err = -ENOMEM; | 1793 | err = -ENOMEM; |
@@ -2432,7 +2463,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, | |||
2432 | { | 2463 | { |
2433 | struct net *net = dev_net(idev->dev); | 2464 | struct net *net = dev_net(idev->dev); |
2434 | struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, | 2465 | struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, |
2435 | DST_NOCOUNT, NULL); | 2466 | DST_NOCOUNT); |
2436 | if (!rt) | 2467 | if (!rt) |
2437 | return ERR_PTR(-ENOMEM); | 2468 | return ERR_PTR(-ENOMEM); |
2438 | 2469 | ||