bonding: fix potential deadlock in bond_uninit()
Subsystems (3)
PATCH v2 1/1 bonding: fix potential deadlock in bond_uninit()
bond_uninit() is invoked with rtnl_lock held, when it does destroy_workqueue() which will potentially flush all works in this workqueue, if we hold rtnl_lock again in the work function, it will deadlock.
So move destroy_workqueue() to destructor where rtnl_lock is not held any more, suggested by Eric.
---
drivers/net/bonding/bond_main.c +9 -4
@@ -4450,6 +4450,14 @@ static const struct net_device_ops bond_netdev_ops = { .ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid, }; +static void bond_destructor (struct net_device *bond_dev) +{ +struct bonding *bond =netdev_priv (bond_dev); +if (bond->wq) +destroy_workqueue (bond->wq); +free_netdev (bond_dev); +} +static void bond_setup (struct net_device *bond_dev) {struct bonding *bond =netdev_priv (bond_dev);
@@ -4470,7 +4478,7 @@ static void bond_setup(struct net_device *bond_dev) bond_dev->ethtool_ops = &bond_ethtool_ops;bond_set_mode_ops (bond, bond->params.mode); - bond_dev->destructor = free_netdev; + bond_dev->destructor = bond_destructor;/* Initialize the device options */ bond_dev->tx_queue_len =0 ;
@@ -4542,9 +4550,6 @@ static void bond_uninit(struct net_device *bond_dev)bond_remove_proc_entry (bond); -if (bond->wq) -destroy_workqueue (bond->wq); -netif_addr_lock_bh (bond_dev);bond_mc_list_destroy (bond);netif_addr_unlock_bh (bond_dev);
Quoted 6 lines · Amerigo Wang
bond_uninit()is invoked withrtnl_lockheld, when it doesdestroy_workqueue()which will potentially flush all works in this workqueue, if we holdrtnl_lockagain in the work function, it will deadlock.So move
destroy_workqueue()to destructor wherertnl_lockis not held any more, suggested by Eric.
The error handling on creating a bond device needs to be updated as well.
Eric
Quoted 47 lines
Signed-off-by WANG Cong <[email protected]>Cc Jay Vosburgh <[email protected]>Cc "David S. Miller" <[email protected]>Cc Stephen Hemminger <[email protected]>Cc Jiri Pirko <[email protected]>Cc "Eric W. Biederman" <[email protected]>---
drivers/net/bonding/bond_main.c +9
-4@@ -4450,6 +4450,14 @@ static const struct net_device_ops bond_netdev_ops = { .ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid, }; +static void bond_destructor (struct net_device *bond_dev) +{ +struct bonding *bond =netdev_priv (bond_dev); +if (bond->wq) +destroy_workqueue (bond->wq); +free_netdev (bond_dev); +} +static void bond_setup (struct net_device *bond_dev) {struct bonding *bond =netdev_priv (bond_dev);@@ -4470,7 +4478,7 @@ static void bond_setup(struct net_device *bond_dev) bond_dev->ethtool_ops = &bond_ethtool_ops;bond_set_mode_ops (bond, bond->params.mode); - bond_dev->destructor = free_netdev; + bond_dev->destructor = bond_destructor;/* Initialize the device options */ bond_dev->tx_queue_len =0 ;@@ -4542,9 +4550,6 @@ static void bond_uninit(struct net_device *bond_dev)bond_remove_proc_entry (bond); -if (bond->wq) -destroy_workqueue (bond->wq); -netif_addr_lock_bh (bond_dev);bond_mc_list_destroy (bond);netif_addr_unlock_bh (bond_dev);
Quoted 11 lines · Eric W. Biederman
Quoted 6 lines · Amerigo Wang
bond_uninit()is invoked withrtnl_lockheld, when it doesdestroy_workqueue()which will potentially flush all works in this workqueue, if we holdrtnl_lockagain in the work function, it will deadlock.So move
destroy_workqueue()to destructor wherertnl_lockis not held any more, suggested by Eric.The error handling on creating a bond device needs to be updated as well.
You're right, I missed that part. Will update it soon.
Thanks.
PATCH v3 1/1 bonding: fix potential deadlock in bond_uninit()
In reply to Eric W. BiedermanQuoted 11 lines · Eric W. Biederman
Quoted 6 lines · Amerigo Wang
bond_uninit()is invoked withrtnl_lockheld, when it doesdestroy_workqueue()which will potentially flush all works in this workqueue, if we holdrtnl_lockagain in the work function, it will deadlock.So move
destroy_workqueue()to destructor wherertnl_lockis not held any more, suggested by Eric.The error handling on creating a bond device needs to be updated as well.
Done.
V3: fix error handling path of bond_create()
bond_uninit() is invoked with rtnl_lock held, when it does destroy_workqueue() which will potentially flush all works in this workqueue, if we hold rtnl_lock again in the work function, it will deadlock.
So move destroy_workqueue() to destructor where rtnl_lock is not held any more, suggested by Eric.
---
drivers/net/bonding/bond_main.c +14 -12
@@ -4450,6 +4450,14 @@ static const struct net_device_ops bond_netdev_ops = { .ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid, }; +static void bond_destructor (struct net_device *bond_dev) +{ +struct bonding *bond =netdev_priv (bond_dev); +if (bond->wq) +destroy_workqueue (bond->wq); +free_netdev (bond_dev); +} +static void bond_setup (struct net_device *bond_dev) {struct bonding *bond =netdev_priv (bond_dev);
@@ -4470,7 +4478,7 @@ static void bond_setup(struct net_device *bond_dev) bond_dev->ethtool_ops = &bond_ethtool_ops;bond_set_mode_ops (bond, bond->params.mode); - bond_dev->destructor = free_netdev; + bond_dev->destructor = bond_destructor;/* Initialize the device options */ bond_dev->tx_queue_len =0 ;
@@ -4542,9 +4550,6 @@ static void bond_uninit(struct net_device *bond_dev)bond_remove_proc_entry (bond); -if (bond->wq) -destroy_workqueue (bond->wq); -netif_addr_lock_bh (bond_dev);bond_mc_list_destroy (bond);netif_addr_unlock_bh (bond_dev);
@@ -4956,8 +4961,8 @@ int bond_create(struct net *net, const char *name) bond_setup);if (!bond_dev) {pr_err ("%s: eek! can't alloc netdev!\n" , name); - res = -ENOMEM ; - goto out; +rtnl_unlock (); +return -ENOMEM ; }dev_net_set (bond_dev, net);
@@ -4966,19 +4971,16 @@ int bond_create(struct net *net, const char *name)if (!name) { res =dev_alloc_name (bond_dev,"bond%d" );if (res <0 ) - goto out_netdev; + goto out; } res =register_netdevice (bond_dev); -if (res <0 ) - goto out_netdev; out:rtnl_unlock (); +if (res <0 ) +bond_destructor (bond_dev);return res; -out_netdev: -free_netdev (bond_dev); - goto out; }static int __net_init bond_net_init (struct net *net)
From: Cong Wang <[email protected]>
Date: Thu, 01 Apr 2010 15:30:52 +0800
Quoted 9 lines
Quoted 8 lines · Eric W. Biederman
Quoted 6 lines · Amerigo Wang
bond_uninit()is invoked withrtnl_lockheld, when it doesdestroy_workqueue()
which will potentially flush all works in this workqueue, if we holdrtnl_lock
again in the work function, it will deadlock.
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
So move
destroy_workqueue()to destructor wherertnl_lockis not held any more,
suggested by Eric.The error handling on creating a bond device needs to be updated as well.
Done.
Applied, thanks.