From: Jakub Kicinski <kuba@kernel.org> Date: 2022-02-15 22:53:21
In prep for unregistering netdevs out of order move the netdev
state validation and change outside of the loop.
While at it modernize this code and use WARN() instead of
pr_err() + dump_stack().
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
net/core/dev.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
@@ -9926,24 +9927,23 @@ void netdev_run_todo(void)__rtnl_unlock();-/* Wait for rcu callbacks to finish before next phase */if(!list_empty(&list))rcu_barrier();-while(!list_empty(&list)){-structnet_device*dev-=list_first_entry(&list,structnet_device,todo_list);-list_del(&dev->todo_list);-+list_for_each_entry_safe(dev,tmp,&list,todo_list){if(unlikely(dev->reg_state!=NETREG_UNREGISTERING)){-pr_err("network todo '%s' but state %d\n",-dev->name,dev->reg_state);-dump_stack();+netdev_WARN(dev,"run_todo but not unregistering\n");+list_del(&dev->todo_list);continue;}dev->reg_state=NETREG_UNREGISTERED;+}++while(!list_empty(&list)){+dev=list_first_entry(&list,structnet_device,todo_list);+list_del(&dev->todo_list);netdev_wait_allrefs(dev);
From: Jakub Kicinski <kuba@kernel.org> Date: 2022-02-15 22:53:21
Sprinkle for each loops to allow netdevices to be unregistered
out of order, as their refs are released.
This prevents problems caused by dependencies between netdevs
which want to release references in their ->priv_destructor.
See commit d6ff94afd90b ("vlan: move dev_put into vlan_dev_uninit")
for example.
Eric has removed the only known ordering requirement in
commit c002496babfd ("Merge branch 'ipv6-loopback'")
so let's try this and see if anything explodes...
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
net/core/dev.c | 64 +++++++++++++++++++++++++++++---------------------
1 file changed, 37 insertions(+), 27 deletions(-)
@@ -9822,37 +9822,45 @@ int netdev_unregister_timeout_secs __read_mostly = 10;*Wecangetstuckhereifbuggyprotocolsdon'tcorrectly*calldev_put.*/-staticvoidnetdev_wait_allrefs(structnet_device*dev)+staticstructnet_device*netdev_wait_allrefs_any(structlist_head*list){unsignedlongrebroadcast_time,warning_time;-intwait=0,refcnt;+structnet_device*dev;+intwait=0;-linkwatch_forget_dev(dev);+list_for_each_entry(dev,list,todo_list)+linkwatch_forget_dev(dev);rebroadcast_time=warning_time=jiffies;-refcnt=netdev_refcnt_read(dev);-while(refcnt!=1){+list_for_each_entry(dev,list,todo_list)+if(netdev_refcnt_read(dev)==1)+returndev;++while(true){if(time_after(jiffies,rebroadcast_time+1*HZ)){rtnl_lock();/* Rebroadcast unregister notification */-call_netdevice_notifiers(NETDEV_UNREGISTER,dev);+list_for_each_entry(dev,list,todo_list)+call_netdevice_notifiers(NETDEV_UNREGISTER,dev);__rtnl_unlock();rcu_barrier();rtnl_lock();-if(test_bit(__LINK_STATE_LINKWATCH_PENDING,-&dev->state)){-/* We must not have linkwatch events-*pendingonunregister.Ifthis-*happens,wesimplyrunthequeue-*unscheduled,resultinginanoop-*forthisdevice.-*/-linkwatch_run_queue();-}+list_for_each_entry(dev,list,todo_list)+if(test_bit(__LINK_STATE_LINKWATCH_PENDING,+&dev->state)){+/* We must not have linkwatch events+*pendingonunregister.Ifthis+*happens,wesimplyrunthequeue+*unscheduled,resultinginanoop+*forthisdevice.+*/+linkwatch_run_queue();+break;+}__rtnl_unlock();
@@ -9867,14 +9875,18 @@ static void netdev_wait_allrefs(struct net_device *dev)wait=min(wait<<1,WAIT_REFS_MAX_MSECS);}-refcnt=netdev_refcnt_read(dev);+list_for_each_entry(dev,list,todo_list)+if(netdev_refcnt_read(dev)==1)+returndev;-if(refcnt!=1&&-time_after(jiffies,warning_time++if(time_after(jiffies,warning_time+netdev_unregister_timeout_secs*HZ)){-pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n",-dev->name,refcnt);-ref_tracker_dir_print(&dev->refcnt_tracker,10);+list_for_each_entry(dev,list,todo_list){+pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n",+dev->name,netdev_refcnt_read(dev));+ref_tracker_dir_print(&dev->refcnt_tracker,10);+}+warning_time=jiffies;}}
From: Eric Dumazet <edumazet@google.com> Date: 2022-02-15 23:57:04
On Tue, Feb 15, 2022 at 2:53 PM Jakub Kicinski [off-list ref] wrote:
In prep for unregistering netdevs out of order move the netdev
state validation and change outside of the loop.
While at it modernize this code and use WARN() instead of
pr_err() + dump_stack().
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
From: Eric Dumazet <edumazet@google.com> Date: 2022-02-16 00:02:11
On Tue, Feb 15, 2022 at 2:53 PM Jakub Kicinski [off-list ref] wrote:
Sprinkle for each loops to allow netdevices to be unregistered
out of order, as their refs are released.
This prevents problems caused by dependencies between netdevs
which want to release references in their ->priv_destructor.
See commit d6ff94afd90b ("vlan: move dev_put into vlan_dev_uninit")
for example.
Eric has removed the only known ordering requirement in
commit c002496babfd ("Merge branch 'ipv6-loopback'")
so let's try this and see if anything explodes...
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
SGTM, thanks !
Reviewed-by: Eric Dumazet <edumazet@google.com>
From: Xin Long <lucien.xin@gmail.com> Date: 2022-02-16 04:12:23
On Wed, Feb 16, 2022 at 6:53 AM Jakub Kicinski [off-list ref] wrote:
quoted hunk
In prep for unregistering netdevs out of order move the netdev
state validation and change outside of the loop.
While at it modernize this code and use WARN() instead of
pr_err() + dump_stack().
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
net/core/dev.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
@@ -9926,24 +9927,23 @@ void netdev_run_todo(void)__rtnl_unlock();-/* Wait for rcu callbacks to finish before next phase */if(!list_empty(&list))rcu_barrier();-while(!list_empty(&list)){-structnet_device*dev-=list_first_entry(&list,structnet_device,todo_list);-list_del(&dev->todo_list);-+list_for_each_entry_safe(dev,tmp,&list,todo_list){if(unlikely(dev->reg_state!=NETREG_UNREGISTERING)){-pr_err("network todo '%s' but state %d\n",-dev->name,dev->reg_state);-dump_stack();+netdev_WARN(dev,"run_todo but not unregistering\n");+list_del(&dev->todo_list);continue;}dev->reg_state=NETREG_UNREGISTERED;+}++while(!list_empty(&list)){+dev=list_first_entry(&list,structnet_device,todo_list);+list_del(&dev->todo_list);netdev_wait_allrefs(dev);--
From: Xin Long <lucien.xin@gmail.com> Date: 2022-02-16 04:13:43
On Wed, Feb 16, 2022 at 6:53 AM Jakub Kicinski [off-list ref] wrote:
quoted hunk
Sprinkle for each loops to allow netdevices to be unregistered
out of order, as their refs are released.
This prevents problems caused by dependencies between netdevs
which want to release references in their ->priv_destructor.
See commit d6ff94afd90b ("vlan: move dev_put into vlan_dev_uninit")
for example.
Eric has removed the only known ordering requirement in
commit c002496babfd ("Merge branch 'ipv6-loopback'")
so let's try this and see if anything explodes...
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
net/core/dev.c | 64 +++++++++++++++++++++++++++++---------------------
1 file changed, 37 insertions(+), 27 deletions(-)
@@ -9822,37 +9822,45 @@ int netdev_unregister_timeout_secs __read_mostly = 10;*Wecangetstuckhereifbuggyprotocolsdon'tcorrectly*calldev_put.*/-staticvoidnetdev_wait_allrefs(structnet_device*dev)+staticstructnet_device*netdev_wait_allrefs_any(structlist_head*list){unsignedlongrebroadcast_time,warning_time;-intwait=0,refcnt;+structnet_device*dev;+intwait=0;-linkwatch_forget_dev(dev);+list_for_each_entry(dev,list,todo_list)+linkwatch_forget_dev(dev);rebroadcast_time=warning_time=jiffies;-refcnt=netdev_refcnt_read(dev);-while(refcnt!=1){+list_for_each_entry(dev,list,todo_list)+if(netdev_refcnt_read(dev)==1)+returndev;++while(true){if(time_after(jiffies,rebroadcast_time+1*HZ)){rtnl_lock();/* Rebroadcast unregister notification */-call_netdevice_notifiers(NETDEV_UNREGISTER,dev);+list_for_each_entry(dev,list,todo_list)+call_netdevice_notifiers(NETDEV_UNREGISTER,dev);__rtnl_unlock();rcu_barrier();rtnl_lock();-if(test_bit(__LINK_STATE_LINKWATCH_PENDING,-&dev->state)){-/* We must not have linkwatch events-*pendingonunregister.Ifthis-*happens,wesimplyrunthequeue-*unscheduled,resultinginanoop-*forthisdevice.-*/-linkwatch_run_queue();-}+list_for_each_entry(dev,list,todo_list)+if(test_bit(__LINK_STATE_LINKWATCH_PENDING,+&dev->state)){+/* We must not have linkwatch events+*pendingonunregister.Ifthis+*happens,wesimplyrunthequeue+*unscheduled,resultinginanoop+*forthisdevice.+*/+linkwatch_run_queue();+break;+}__rtnl_unlock();
@@ -9867,14 +9875,18 @@ static void netdev_wait_allrefs(struct net_device *dev)wait=min(wait<<1,WAIT_REFS_MAX_MSECS);}-refcnt=netdev_refcnt_read(dev);+list_for_each_entry(dev,list,todo_list)+if(netdev_refcnt_read(dev)==1)+returndev;-if(refcnt!=1&&-time_after(jiffies,warning_time++if(time_after(jiffies,warning_time+netdev_unregister_timeout_secs*HZ)){-pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n",-dev->name,refcnt);-ref_tracker_dir_print(&dev->refcnt_tracker,10);+list_for_each_entry(dev,list,todo_list){+pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n",+dev->name,netdev_refcnt_read(dev));+ref_tracker_dir_print(&dev->refcnt_tracker,10);+}+warning_time=jiffies;}}
Hello:
This series was applied to netdev/net-next.git (master)
by Jakub Kicinski [off-list ref]:
On Tue, 15 Feb 2022 14:53:09 -0800 you wrote:
In prep for unregistering netdevs out of order move the netdev
state validation and change outside of the loop.
While at it modernize this code and use WARN() instead of
pr_err() + dump_stack().
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
[...]
From: Eric Dumazet <edumazet@google.com> Date: 2022-02-18 06:37:02
On Tue, Feb 15, 2022 at 2:53 PM Jakub Kicinski [off-list ref] wrote:
quoted hunk
Sprinkle for each loops to allow netdevices to be unregistered
out of order, as their refs are released.
This prevents problems caused by dependencies between netdevs
which want to release references in their ->priv_destructor.
See commit d6ff94afd90b ("vlan: move dev_put into vlan_dev_uninit")
for example.
Eric has removed the only known ordering requirement in
commit c002496babfd ("Merge branch 'ipv6-loopback'")
so let's try this and see if anything explodes...
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
net/core/dev.c | 64 +++++++++++++++++++++++++++++---------------------
1 file changed, 37 insertions(+), 27 deletions(-)
Jakub, this part of the code added quadratic behavior (and soft lockups)
when a large list of devices is dismantled at once,
because we call netdev_wait_allrefs_any() N times.
I will test this fix, unless I have missed something ?