diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 93b9f4189525..3a90c8d28b94 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2494,6 +2494,8 @@ ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, if (unlikely(!rt_cache_route(nhc, rth))) rt_add_uncached_list(rth); + } else { + rt_add_uncached_list(rth); } skb_dst_set(skb, &rth->dst); reason = SKB_NOT_DROPPED_YET; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 616b86f89f63..2eb884558c24 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -179,12 +179,20 @@ void rt6_uncached_list_flush_dev(struct net_device *dev) list_for_each_entry_safe(rt, safe, &ul->head, dst.rt_uncached) { struct inet6_dev *rt_idev = rt->rt6i_idev; struct net_device *rt_dev = rt->dst.dev; + struct fib6_info *from; + bool release_fib; + + rcu_read_lock(); + from = rcu_dereference(rt->from); + release_fib = from && from->fib6_destroying; + rcu_read_unlock(); if (rt_idev && (dev ? rt_idev->dev == dev : READ_ONCE(rt_idev->dev->reg_state) == NETREG_UNREGISTERED)) { rt->rt6i_idev = in6_dev_get(blackhole_netdev); in6_dev_put(rt_idev); + release_fib = true; } if (dev ? rt_dev == dev : @@ -193,6 +201,12 @@ void rt6_uncached_list_flush_dev(struct net_device *dev) netdev_ref_replace(rt_dev, blackhole_netdev, &rt->dst.dev_tracker, GFP_ATOMIC); + release_fib = true; + } + + if (release_fib) { + from = unrcu_pointer(xchg(&rt->from, NULL)); + fib6_info_release(from); } if (rt->dst.dev == blackhole_netdev && @@ -3701,9 +3715,10 @@ void fib6_nh_release(struct fib6_nh *fib6_nh) { struct rt6_exception_bucket *bucket; + fib6_nh_release_dsts(fib6_nh); + rcu_read_lock(); - fib6_nh_flush_exceptions(fib6_nh, NULL); bucket = fib6_nh_get_excptn_bucket(fib6_nh, NULL); if (bucket) { rcu_assign_pointer(fib6_nh->rt6i_exception_bucket, NULL); @@ -3712,7 +3727,6 @@ void fib6_nh_release(struct fib6_nh *fib6_nh) rcu_read_unlock(); - fib6_nh_release_dsts(fib6_nh); free_percpu(fib6_nh->rt6i_pcpu); fib_nh_common_release(&fib6_nh->nh_common); @@ -3722,6 +3736,8 @@ void fib6_nh_release_dsts(struct fib6_nh *fib6_nh) { int cpu; + fib6_nh_flush_exceptions(fib6_nh, NULL); + if (!fib6_nh->rt6i_pcpu) return;