diff --git a/include/net/route.h b/include/net/route.h index 6b55de2e4df8..3fccd31eb74d 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -129,7 +129,13 @@ struct in_device; int ip_rt_init(void); void rt_cache_flush(struct net *net); +#ifdef CONFIG_INET void rt_flush_dev(struct net_device *dev); +#else +static inline void rt_flush_dev(struct net_device *dev) +{ +} +#endif static inline void inet_sk_init_flowi4(const struct inet_sock *inet, struct flowi4 *fl4) diff --git a/net/core/dev.c b/net/core/dev.c index db765cb5811e..f76a4459d8e1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -129,6 +129,7 @@ #include #include #include +#include #include #include #include @@ -11886,6 +11887,9 @@ void netdev_run_todo(void) linkwatch_sync_dev(dev); } + if (!list_empty(&list)) + rt_flush_dev(NULL); + cnt = 0; while (!list_empty(&list)) { dev = netdev_wait_allrefs_any(&list); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d7da2f1acbb5..1b641901f10d 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1587,6 +1587,9 @@ void rt_flush_dev(struct net_device *dev) struct rtable *rt, *safe; int cpu; + if (dev && dev->dismantle) + return; + for_each_possible_cpu(cpu) { struct uncached_list *ul = &per_cpu(rt_uncached_list, cpu); @@ -1595,10 +1598,14 @@ void rt_flush_dev(struct net_device *dev) spin_lock_bh(&ul->lock); list_for_each_entry_safe(rt, safe, &ul->head, dst.rt_uncached) { - if (rt->dst.dev != dev) + struct net_device *rt_dev = rt->dst.dev; + + if (dev ? rt_dev != dev : + READ_ONCE(rt_dev->reg_state) != NETREG_UNREGISTERED) continue; + rcu_assign_pointer(rt->dst.dev_rcu, blackhole_netdev); - netdev_ref_replace(dev, blackhole_netdev, + netdev_ref_replace(rt_dev, blackhole_netdev, &rt->dst.dev_tracker, GFP_ATOMIC); list_del_init(&rt->dst.rt_uncached); }