@@ -11436,6 +11436,18 @@ static bool from_cleanup_net(void)
return current == cleanup_net_task;
}
+static void rtnl_drop_if_cleanup_net(void)
+{
+ if (from_cleanup_net())
+ __rtnl_unlock();
+}
+
+static void rtnl_acquire_if_cleanup_net(void)
+{
+ if (from_cleanup_net())
+ rtnl_lock();
+}
+
/**
* synchronize_net - Synchronize with packet receive processing
*
@@ -11548,8 +11560,10 @@ void unregister_netdevice_many_notify(struct list_head *head,
unlist_netdevice(dev);
WRITE_ONCE(dev->reg_state, NETREG_UNREGISTERING);
}
- flush_all_backlogs();
+ rtnl_drop_if_cleanup_net();
+ flush_all_backlogs();
+ rtnl_acquire_if_cleanup_net();
synchronize_net();
list_for_each_entry(dev, head, unreg_list) {
flush_all_backlogs() is called from unregister_netdevice_many_notify() as part of netdevice dismantles. This is currently called under RTNL, and can last up to 20ms on busy hosts. There is no reason to hold RTNL at this stage, if our caller is cleanup_net() : netns are no more visible, devices are in NETREG_UNREGISTERING state and no other thread could mess our state while RTNL is temporarily released. Signed-off-by: Eric Dumazet <edumazet@google.com> --- net/core/dev.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)