summaryrefslogtreecommitdiffstats
path: root/net/ipv6/route.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-08-21 14:44:04 -0400
committerDavid S. Miller <davem@davemloft.net>2015-08-21 14:44:04 -0400
commitdc25b25897289bad4907f30151ffe5baf75ff369 (patch)
treec99301de2cdf66d7af48fc43b6161bda6faaae70 /net/ipv6/route.c
parent1a69205c471221a7f9101df164642611e37c52e4 (diff)
parent0bad90985d39e69ca035fdd70bcc743812641d18 (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.c79
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 */
322static struct rt6_info *__ip6_dst_alloc(struct net *net, 322static 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
340static struct rt6_info *ip6_dst_alloc(struct net *net, 339static 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 */
1007static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) 1003static 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
1017static 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
1032done:
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