]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/ipv6/route.c
ipv6: remove from fib tree aged out RTF_CACHE dst
[linux.git] / net / ipv6 / route.c
index 05dc450af4416c1dcb775bfb54dfcd858a7edda4..074fac966018be8a3943ccc438045f77718f98a1 100644 (file)
@@ -143,9 +143,11 @@ static void rt6_uncached_list_del(struct rt6_info *rt)
 {
        if (!list_empty(&rt->rt6i_uncached)) {
                struct uncached_list *ul = rt->rt6i_uncached_list;
+               struct net *net = dev_net(rt->dst.dev);
 
                spin_lock_bh(&ul->lock);
                list_del(&rt->rt6i_uncached);
+               atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache);
                spin_unlock_bh(&ul->lock);
        }
 }
@@ -359,8 +361,10 @@ static struct rt6_info *__ip6_dst_alloc(struct net *net,
        struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
                                        1, DST_OBSOLETE_FORCE_CHK, flags);
 
-       if (rt)
+       if (rt) {
                rt6_info_init(rt);
+               atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
+       }
 
        return rt;
 }
@@ -373,17 +377,7 @@ struct rt6_info *ip6_dst_alloc(struct net *net,
 
        if (rt) {
                rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC);
-               if (rt->rt6i_pcpu) {
-                       int cpu;
-
-                       for_each_possible_cpu(cpu) {
-                               struct rt6_info **p;
-
-                               p = per_cpu_ptr(rt->rt6i_pcpu, cpu);
-                               /* no one shares rt */
-                               *p =  NULL;
-                       }
-               } else {
+               if (!rt->rt6i_pcpu) {
                        dst_release_immediate(&rt->dst);
                        return NULL;
                }
@@ -488,7 +482,7 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
 }
 
 /*
- *     Route lookup. Any table->tb6_lock is implied.
+ *     Route lookup. rcu_read_lock() should be held.
  */
 
 static inline struct rt6_info *rt6_device_match(struct net *net,
@@ -503,7 +497,7 @@ static inline struct rt6_info *rt6_device_match(struct net *net,
        if (!oif && ipv6_addr_any(saddr))
                goto out;
 
-       for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
+       for (sprt = rt; sprt; sprt = rcu_dereference(sprt->dst.rt6_next)) {
                struct net_device *dev = sprt->dst.dev;
 
                if (oif) {
@@ -722,7 +716,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
 
        match = NULL;
        cont = NULL;
-       for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
+       for (rt = rr_head; rt; rt = rcu_dereference(rt->dst.rt6_next)) {
                if (rt->rt6i_metric != metric) {
                        cont = rt;
                        break;
@@ -731,7 +725,8 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
                match = find_match(rt, oif, strict, &mpri, match, do_rr);
        }
 
-       for (rt = leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
+       for (rt = leaf; rt && rt != rr_head;
+            rt = rcu_dereference(rt->dst.rt6_next)) {
                if (rt->rt6i_metric != metric) {
                        cont = rt;
                        break;
@@ -743,7 +738,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
        if (match || !cont)
                return match;
 
-       for (rt = cont; rt; rt = rt->dst.rt6_next)
+       for (rt = cont; rt; rt = rcu_dereference(rt->dst.rt6_next))
                match = find_match(rt, oif, strict, &mpri, match, do_rr);
 
        return match;
@@ -752,29 +747,48 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
 static struct rt6_info *rt6_select(struct net *net, struct fib6_node *fn,
                                   int oif, int strict)
 {
-       struct rt6_info *leaf = fn->leaf;
+       struct rt6_info *leaf = rcu_dereference(fn->leaf);
        struct rt6_info *match, *rt0;
        bool do_rr = false;
+       int key_plen;
 
        if (!leaf)
                return net->ipv6.ip6_null_entry;
 
-       rt0 = fn->rr_ptr;
+       rt0 = rcu_dereference(fn->rr_ptr);
        if (!rt0)
-               fn->rr_ptr = rt0 = leaf;
+               rt0 = leaf;
+
+       /* Double check to make sure fn is not an intermediate node
+        * and fn->leaf does not points to its child's leaf
+        * (This might happen if all routes under fn are deleted from
+        * the tree and fib6_repair_tree() is called on the node.)
+        */
+       key_plen = rt0->rt6i_dst.plen;
+#ifdef CONFIG_IPV6_SUBTREES
+       if (rt0->rt6i_src.plen)
+               key_plen = rt0->rt6i_src.plen;
+#endif
+       if (fn->fn_bit != key_plen)
+               return net->ipv6.ip6_null_entry;
 
        match = find_rr_leaf(fn, leaf, rt0, rt0->rt6i_metric, oif, strict,
                             &do_rr);
 
        if (do_rr) {
-               struct rt6_info *next = rt0->dst.rt6_next;
+               struct rt6_info *next = rcu_dereference(rt0->dst.rt6_next);
 
                /* no entries matched; do round-robin */
                if (!next || next->rt6i_metric != rt0->rt6i_metric)
                        next = leaf;
 
-               if (next != rt0)
-                       fn->rr_ptr = next;
+               if (next != rt0) {
+                       spin_lock_bh(&leaf->rt6i_table->tb6_lock);
+                       /* make sure next is not being deleted from the tree */
+                       if (next->rt6i_node)
+                               rcu_assign_pointer(fn->rr_ptr, next);
+                       spin_unlock_bh(&leaf->rt6i_table->tb6_lock);
+               }
        }
 
        return match ? match : net->ipv6.ip6_null_entry;
@@ -864,13 +878,14 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
 static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
                                        struct in6_addr *saddr)
 {
-       struct fib6_node *pn;
+       struct fib6_node *pn, *sn;
        while (1) {
                if (fn->fn_flags & RTN_TL_ROOT)
                        return NULL;
-               pn = fn->parent;
-               if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
-                       fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
+               pn = rcu_dereference(fn->parent);
+               sn = FIB6_SUBTREE(pn);
+               if (sn && sn != fn)
+                       fn = fib6_lookup(sn, NULL, saddr);
                else
                        fn = pn;
                if (fn->fn_flags & RTN_RTINFO)
@@ -902,13 +917,19 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
        struct rt6_info *rt, *rt_cache;
        struct fib6_node *fn;
 
-       read_lock_bh(&table->tb6_lock);
+       rcu_read_lock();
        fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
 restart:
-       rt = fn->leaf;
-       rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
-       if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
-               rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
+       rt = rcu_dereference(fn->leaf);
+       if (!rt) {
+               rt = net->ipv6.ip6_null_entry;
+       } else {
+               rt = rt6_device_match(net, rt, &fl6->saddr,
+                                     fl6->flowi6_oif, flags);
+               if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
+                       rt = rt6_multipath_select(rt, fl6,
+                                                 fl6->flowi6_oif, flags);
+       }
        if (rt == net->ipv6.ip6_null_entry) {
                fn = fib6_backtrack(fn, &fl6->saddr);
                if (fn)
@@ -922,7 +943,7 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
        if (ip6_hold_safe(net, &rt, true))
                dst_use_noref(&rt->dst, jiffies);
 
-       read_unlock_bh(&table->tb6_lock);
+       rcu_read_unlock();
 
        trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
 
@@ -976,9 +997,9 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
        struct fib6_table *table;
 
        table = rt->rt6i_table;
-       write_lock_bh(&table->tb6_lock);
+       spin_lock_bh(&table->tb6_lock);
        err = fib6_add(&table->tb6_root, rt, info, mxc, extack);
-       write_unlock_bh(&table->tb6_lock);
+       spin_unlock_bh(&table->tb6_lock);
 
        return err;
 }
@@ -1076,7 +1097,7 @@ static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt)
        return pcpu_rt;
 }
 
-/* It should be called with read_lock_bh(&tb6_lock) acquired */
+/* It should be called with rcu_read_lock() acquired */
 static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
 {
        struct rt6_info *pcpu_rt, **p;
@@ -1105,15 +1126,7 @@ static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt)
        dst_hold(&pcpu_rt->dst);
        p = this_cpu_ptr(rt->rt6i_pcpu);
        prev = cmpxchg(p, NULL, pcpu_rt);
-       if (prev) {
-               /* If someone did it before us, return prev instead */
-               /* release refcnt taken by ip6_rt_pcpu_alloc() */
-               dst_release_immediate(&pcpu_rt->dst);
-               /* release refcnt taken by above dst_hold() */
-               dst_release_immediate(&pcpu_rt->dst);
-               dst_hold(&prev->dst);
-               pcpu_rt = prev;
-       }
+       BUG_ON(prev);
 
        rt6_dst_from_metrics_check(pcpu_rt);
        return pcpu_rt;
@@ -1129,14 +1142,19 @@ static DEFINE_SPINLOCK(rt6_exception_lock);
 static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
                                 struct rt6_exception *rt6_ex)
 {
+       struct net *net;
+
        if (!bucket || !rt6_ex)
                return;
+
+       net = dev_net(rt6_ex->rt6i->dst.dev);
        rt6_ex->rt6i->rt6i_node = NULL;
        hlist_del_rcu(&rt6_ex->hlist);
        rt6_release(rt6_ex->rt6i);
        kfree_rcu(rt6_ex, rcu);
        WARN_ON_ONCE(!bucket->depth);
        bucket->depth--;
+       net->ipv6.rt6_stats->fib_rt_cache--;
 }
 
 /* Remove oldest rt6_ex in bucket and free the memory
@@ -1243,6 +1261,7 @@ __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
 static int rt6_insert_exception(struct rt6_info *nrt,
                                struct rt6_info *ort)
 {
+       struct net *net = dev_net(ort->dst.dev);
        struct rt6_exception_bucket *bucket;
        struct in6_addr *src_key = NULL;
        struct rt6_exception *rt6_ex;
@@ -1312,6 +1331,7 @@ static int rt6_insert_exception(struct rt6_info *nrt,
        nrt->rt6i_node = ort->rt6i_node;
        hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
        bucket->depth++;
+       net->ipv6.rt6_stats->fib_rt_cache++;
 
        if (bucket->depth > FIB6_MAX_DEPTH)
                rt6_exception_remove_oldest(bucket);
@@ -1320,8 +1340,10 @@ static int rt6_insert_exception(struct rt6_info *nrt,
        spin_unlock_bh(&rt6_exception_lock);
 
        /* Update fn->fn_sernum to invalidate all cached dst */
-       if (!err)
+       if (!err) {
                fib6_update_sernum(ort);
+               fib6_force_start_gc(net);
+       }
 
        return err;
 }
@@ -1395,7 +1417,7 @@ int rt6_remove_exception_rt(struct rt6_info *rt)
        int err;
 
        if (!from ||
-           !(rt->rt6i_flags | RTF_CACHE))
+           !(rt->rt6i_flags & RTF_CACHE))
                return -EINVAL;
 
        if (!rcu_access_pointer(from->rt6i_exception_bucket))
@@ -1439,7 +1461,7 @@ static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
        struct rt6_exception *rt6_ex;
 
        if (!from ||
-           !(rt->rt6i_flags | RTF_CACHE))
+           !(rt->rt6i_flags & RTF_CACHE))
                return;
 
        rcu_read_lock();
@@ -1553,7 +1575,13 @@ static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
 {
        struct rt6_info *rt = rt6_ex->rt6i;
 
-       if (atomic_read(&rt->dst.__refcnt) == 1 &&
+       /* we are pruning and obsoleting aged-out and non gateway exceptions
+        * even if others have still references to them, so that on next
+        * dst_check() such references can be dropped.
+        * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
+        * expired, independently from their aging, as per RFC 8201 section 4
+        */
+       if (!(rt->rt6i_flags & RTF_EXPIRES) &&
            time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
                RT6_TRACE("aging clone %p\n", rt);
                rt6_remove_exception(bucket, rt6_ex);
@@ -1573,6 +1601,10 @@ static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
                        rt6_remove_exception(bucket, rt6_ex);
                        return;
                }
+       } else if (__rt6_check_expired(rt)) {
+               RT6_TRACE("purging expired route %p\n", rt);
+               rt6_remove_exception(bucket, rt6_ex);
+               return;
        }
        gc_args->more++;
 }
@@ -1618,7 +1650,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
        if (net->ipv6.devconf_all->forwarding == 0)
                strict |= RT6_LOOKUP_F_REACHABLE;
 
-       read_lock_bh(&table->tb6_lock);
+       rcu_read_lock();
 
        fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
        saved_fn = fn;
@@ -1648,7 +1680,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
                rt = rt_cache;
 
        if (rt == net->ipv6.ip6_null_entry) {
-               read_unlock_bh(&table->tb6_lock);
+               rcu_read_unlock();
                dst_hold(&rt->dst);
                trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
                return rt;
@@ -1657,7 +1689,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
                        dst_use_noref(&rt->dst, jiffies);
                        rt6_dst_from_metrics_check(rt);
                }
-               read_unlock_bh(&table->tb6_lock);
+               rcu_read_unlock();
                trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
                return rt;
        } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
@@ -1673,11 +1705,11 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
                if (ip6_hold_safe(net, &rt, true)) {
                        dst_use_noref(&rt->dst, jiffies);
                } else {
-                       read_unlock_bh(&table->tb6_lock);
+                       rcu_read_unlock();
                        uncached_rt = rt;
                        goto uncached_rt_out;
                }
-               read_unlock_bh(&table->tb6_lock);
+               rcu_read_unlock();
 
                uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL);
                dst_release(&rt->dst);
@@ -1687,6 +1719,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
                         * No need for another dst_hold()
                         */
                        rt6_uncached_list_add(uncached_rt);
+                       atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
                } else {
                        uncached_rt = net->ipv6.ip6_null_entry;
                        dst_hold(&uncached_rt->dst);
@@ -1702,31 +1735,25 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
                struct rt6_info *pcpu_rt;
 
                dst_use_noref(&rt->dst, jiffies);
+               local_bh_disable();
                pcpu_rt = rt6_get_pcpu_route(rt);
 
-               if (pcpu_rt) {
-                       read_unlock_bh(&table->tb6_lock);
-               } else {
+               if (!pcpu_rt) {
                        /* atomic_inc_not_zero() is needed when using rcu */
                        if (atomic_inc_not_zero(&rt->rt6i_ref)) {
-                               /* We have to do the read_unlock first
-                                * because rt6_make_pcpu_route() may trigger
-                                * ip6_dst_gc() which will take the write_lock.
-                                *
-                                * No dst_hold() on rt is needed because grabbing
+                               /* No dst_hold() on rt is needed because grabbing
                                 * rt->rt6i_ref makes sure rt can't be released.
                                 */
-                               read_unlock_bh(&table->tb6_lock);
                                pcpu_rt = rt6_make_pcpu_route(rt);
                                rt6_release(rt);
                        } else {
                                /* rt is already removed from tree */
-                               read_unlock_bh(&table->tb6_lock);
                                pcpu_rt = net->ipv6.ip6_null_entry;
                                dst_hold(&pcpu_rt->dst);
                        }
                }
-
+               local_bh_enable();
+               rcu_read_unlock();
                trace_fib6_table_lookup(net, pcpu_rt, table->tb6_id, fl6);
                return pcpu_rt;
        }
@@ -1864,9 +1891,10 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori
        struct dst_entry *new = NULL;
 
        rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
-                      DST_OBSOLETE_NONE, 0);
+                      DST_OBSOLETE_DEAD, 0);
        if (rt) {
                rt6_info_init(rt);
+               atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
 
                new = &rt->dst;
                new->__use = 1;
@@ -2117,10 +2145,10 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
         * routes.
         */
 
-       read_lock_bh(&table->tb6_lock);
+       rcu_read_lock();
        fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
 restart:
-       for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
+       for_each_fib6_node_rt_rcu(fn) {
                if (rt6_check_expired(rt))
                        continue;
                if (rt->dst.error)
@@ -2165,7 +2193,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
 out:
        ip6_hold_safe(net, &rt, true);
 
-       read_unlock_bh(&table->tb6_lock);
+       rcu_read_unlock();
 
        trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
        return rt;
@@ -2314,6 +2342,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
         * do proper release of the net_device
         */
        rt6_uncached_list_add(rt);
+       atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
 
        dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
 
@@ -2764,9 +2793,9 @@ static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
        }
 
        table = rt->rt6i_table;
-       write_lock_bh(&table->tb6_lock);
+       spin_lock_bh(&table->tb6_lock);
        err = fib6_del(rt, info);
-       write_unlock_bh(&table->tb6_lock);
+       spin_unlock_bh(&table->tb6_lock);
 
 out:
        ip6_rt_put(rt);
@@ -2792,7 +2821,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
        if (rt == net->ipv6.ip6_null_entry)
                goto out_put;
        table = rt->rt6i_table;
-       write_lock_bh(&table->tb6_lock);
+       spin_lock_bh(&table->tb6_lock);
 
        if (rt->rt6i_nsiblings && cfg->fc_delete_all_nh) {
                struct rt6_info *sibling, *next_sibling;
@@ -2822,7 +2851,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
 
        err = fib6_del(rt, info);
 out_unlock:
-       write_unlock_bh(&table->tb6_lock);
+       spin_unlock_bh(&table->tb6_lock);
 out_put:
        ip6_rt_put(rt);
 
@@ -2847,7 +2876,7 @@ static int ip6_route_del(struct fib6_config *cfg,
                return err;
        }
 
-       read_lock_bh(&table->tb6_lock);
+       rcu_read_lock();
 
        fn = fib6_locate(&table->tb6_root,
                         &cfg->fc_dst, cfg->fc_dst_len,
@@ -2855,7 +2884,7 @@ static int ip6_route_del(struct fib6_config *cfg,
                         !(cfg->fc_flags & RTF_CACHE));
 
        if (fn) {
-               for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
+               for_each_fib6_node_rt_rcu(fn) {
                        if (cfg->fc_flags & RTF_CACHE) {
                                rt_cache = rt6_find_cached_rt(rt, &cfg->fc_dst,
                                                              &cfg->fc_src);
@@ -2876,7 +2905,7 @@ static int ip6_route_del(struct fib6_config *cfg,
                                continue;
                        if (!dst_hold_safe(&rt->dst))
                                break;
-                       read_unlock_bh(&table->tb6_lock);
+                       rcu_read_unlock();
 
                        /* if gateway was specified only delete the one hop */
                        if (cfg->fc_flags & RTF_GATEWAY)
@@ -2885,7 +2914,7 @@ static int ip6_route_del(struct fib6_config *cfg,
                        return __ip6_del_rt_siblings(rt, cfg);
                }
        }
-       read_unlock_bh(&table->tb6_lock);
+       rcu_read_unlock();
 
        return err;
 }
@@ -3060,12 +3089,12 @@ static struct rt6_info *rt6_get_route_info(struct net *net,
        if (!table)
                return NULL;
 
-       read_lock_bh(&table->tb6_lock);
+       rcu_read_lock();
        fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
        if (!fn)
                goto out;
 
-       for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
+       for_each_fib6_node_rt_rcu(fn) {
                if (rt->dst.dev->ifindex != ifindex)
                        continue;
                if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
@@ -3076,7 +3105,7 @@ static struct rt6_info *rt6_get_route_info(struct net *net,
                break;
        }
 out:
-       read_unlock_bh(&table->tb6_lock);
+       rcu_read_unlock();
        return rt;
 }
 
@@ -3122,8 +3151,8 @@ struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_dev
        if (!table)
                return NULL;
 
-       read_lock_bh(&table->tb6_lock);
-       for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
+       rcu_read_lock();
+       for_each_fib6_node_rt_rcu(&table->tb6_root) {
                if (dev == rt->dst.dev &&
                    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
                    ipv6_addr_equal(&rt->rt6i_gateway, addr))
@@ -3131,7 +3160,7 @@ struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_dev
        }
        if (rt)
                ip6_hold_safe(NULL, &rt, false);
-       read_unlock_bh(&table->tb6_lock);
+       rcu_read_unlock();
        return rt;
 }
 
@@ -3169,20 +3198,20 @@ static void __rt6_purge_dflt_routers(struct fib6_table *table)
        struct rt6_info *rt;
 
 restart:
-       read_lock_bh(&table->tb6_lock);
-       for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
+       rcu_read_lock();
+       for_each_fib6_node_rt_rcu(&table->tb6_root) {
                if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
                    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
                        if (dst_hold_safe(&rt->dst)) {
-                               read_unlock_bh(&table->tb6_lock);
+                               rcu_read_unlock();
                                ip6_del_rt(rt);
                        } else {
-                               read_unlock_bh(&table->tb6_lock);
+                               rcu_read_unlock();
                        }
                        goto restart;
                }
        }
-       read_unlock_bh(&table->tb6_lock);
+       rcu_read_unlock();
 
        table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
 }
@@ -4395,7 +4424,7 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v)
        seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
                   net->ipv6.rt6_stats->fib_nodes,
                   net->ipv6.rt6_stats->fib_route_nodes,
-                  net->ipv6.rt6_stats->fib_rt_alloc,
+                  atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
                   net->ipv6.rt6_stats->fib_rt_entries,
                   net->ipv6.rt6_stats->fib_rt_cache,
                   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),