diff options
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r-- | net/ipv6/ip6_fib.c | 45 |
1 files changed, 26 insertions, 19 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index b2d1838897c9..f1c6d5e98322 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c | |||
@@ -659,6 +659,29 @@ static int fib6_commit_metrics(struct dst_entry *dst, | |||
659 | return 0; | 659 | return 0; |
660 | } | 660 | } |
661 | 661 | ||
662 | static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn, | ||
663 | struct net *net) | ||
664 | { | ||
665 | if (atomic_read(&rt->rt6i_ref) != 1) { | ||
666 | /* This route is used as dummy address holder in some split | ||
667 | * nodes. It is not leaked, but it still holds other resources, | ||
668 | * which must be released in time. So, scan ascendant nodes | ||
669 | * and replace dummy references to this route with references | ||
670 | * to still alive ones. | ||
671 | */ | ||
672 | while (fn) { | ||
673 | if (!(fn->fn_flags & RTN_RTINFO) && fn->leaf == rt) { | ||
674 | fn->leaf = fib6_find_prefix(net, fn); | ||
675 | atomic_inc(&fn->leaf->rt6i_ref); | ||
676 | rt6_release(rt); | ||
677 | } | ||
678 | fn = fn->parent; | ||
679 | } | ||
680 | /* No more references are possible at this point. */ | ||
681 | BUG_ON(atomic_read(&rt->rt6i_ref) != 1); | ||
682 | } | ||
683 | } | ||
684 | |||
662 | /* | 685 | /* |
663 | * Insert routing information in a node. | 686 | * Insert routing information in a node. |
664 | */ | 687 | */ |
@@ -807,11 +830,12 @@ add: | |||
807 | rt->dst.rt6_next = iter->dst.rt6_next; | 830 | rt->dst.rt6_next = iter->dst.rt6_next; |
808 | atomic_inc(&rt->rt6i_ref); | 831 | atomic_inc(&rt->rt6i_ref); |
809 | inet6_rt_notify(RTM_NEWROUTE, rt, info); | 832 | inet6_rt_notify(RTM_NEWROUTE, rt, info); |
810 | rt6_release(iter); | ||
811 | if (!(fn->fn_flags & RTN_RTINFO)) { | 833 | if (!(fn->fn_flags & RTN_RTINFO)) { |
812 | info->nl_net->ipv6.rt6_stats->fib_route_nodes++; | 834 | info->nl_net->ipv6.rt6_stats->fib_route_nodes++; |
813 | fn->fn_flags |= RTN_RTINFO; | 835 | fn->fn_flags |= RTN_RTINFO; |
814 | } | 836 | } |
837 | fib6_purge_rt(iter, fn, info->nl_net); | ||
838 | rt6_release(iter); | ||
815 | } | 839 | } |
816 | 840 | ||
817 | return 0; | 841 | return 0; |
@@ -1322,24 +1346,7 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, | |||
1322 | fn = fib6_repair_tree(net, fn); | 1346 | fn = fib6_repair_tree(net, fn); |
1323 | } | 1347 | } |
1324 | 1348 | ||
1325 | if (atomic_read(&rt->rt6i_ref) != 1) { | 1349 | fib6_purge_rt(rt, fn, net); |
1326 | /* This route is used as dummy address holder in some split | ||
1327 | * nodes. It is not leaked, but it still holds other resources, | ||
1328 | * which must be released in time. So, scan ascendant nodes | ||
1329 | * and replace dummy references to this route with references | ||
1330 | * to still alive ones. | ||
1331 | */ | ||
1332 | while (fn) { | ||
1333 | if (!(fn->fn_flags & RTN_RTINFO) && fn->leaf == rt) { | ||
1334 | fn->leaf = fib6_find_prefix(net, fn); | ||
1335 | atomic_inc(&fn->leaf->rt6i_ref); | ||
1336 | rt6_release(rt); | ||
1337 | } | ||
1338 | fn = fn->parent; | ||
1339 | } | ||
1340 | /* No more references are possible at this point. */ | ||
1341 | BUG_ON(atomic_read(&rt->rt6i_ref) != 1); | ||
1342 | } | ||
1343 | 1350 | ||
1344 | inet6_rt_notify(RTM_DELROUTE, rt, info); | 1351 | inet6_rt_notify(RTM_DELROUTE, rt, info); |
1345 | rt6_release(rt); | 1352 | rt6_release(rt); |