diff options
author | Amerigo Wang <amwang@redhat.com> | 2010-03-31 17:30:52 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-04-01 20:26:01 -0400 |
commit | 9e2e61fbf8ad016d24e4af0afff13505f3dd2a2a (patch) | |
tree | e469e69cc59b860713ac18227932c017a739564f /drivers/net/bonding | |
parent | 6503d96168f891ffa3b70ae6c9698a1a722025a0 (diff) |
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.
Signed-off-by: WANG Cong <amwang@redhat.com>
Cc: Jay Vosburgh <fubar@us.ibm.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Stephen Hemminger <shemminger@vyatta.com>
Cc: Jiri Pirko <jpirko@redhat.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/bonding')
-rw-r--r-- | drivers/net/bonding/bond_main.c | 26 |
1 files changed, 14 insertions, 12 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 5972a52a330c..0075514bf32f 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c | |||
@@ -4450,6 +4450,14 @@ static const struct net_device_ops bond_netdev_ops = { | |||
4450 | .ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid, | 4450 | .ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid, |
4451 | }; | 4451 | }; |
4452 | 4452 | ||
4453 | static void bond_destructor(struct net_device *bond_dev) | ||
4454 | { | ||
4455 | struct bonding *bond = netdev_priv(bond_dev); | ||
4456 | if (bond->wq) | ||
4457 | destroy_workqueue(bond->wq); | ||
4458 | free_netdev(bond_dev); | ||
4459 | } | ||
4460 | |||
4453 | static void bond_setup(struct net_device *bond_dev) | 4461 | static void bond_setup(struct net_device *bond_dev) |
4454 | { | 4462 | { |
4455 | struct bonding *bond = netdev_priv(bond_dev); | 4463 | struct bonding *bond = netdev_priv(bond_dev); |
@@ -4470,7 +4478,7 @@ static void bond_setup(struct net_device *bond_dev) | |||
4470 | bond_dev->ethtool_ops = &bond_ethtool_ops; | 4478 | bond_dev->ethtool_ops = &bond_ethtool_ops; |
4471 | bond_set_mode_ops(bond, bond->params.mode); | 4479 | bond_set_mode_ops(bond, bond->params.mode); |
4472 | 4480 | ||
4473 | bond_dev->destructor = free_netdev; | 4481 | bond_dev->destructor = bond_destructor; |
4474 | 4482 | ||
4475 | /* Initialize the device options */ | 4483 | /* Initialize the device options */ |
4476 | bond_dev->tx_queue_len = 0; | 4484 | bond_dev->tx_queue_len = 0; |
@@ -4542,9 +4550,6 @@ static void bond_uninit(struct net_device *bond_dev) | |||
4542 | 4550 | ||
4543 | bond_remove_proc_entry(bond); | 4551 | bond_remove_proc_entry(bond); |
4544 | 4552 | ||
4545 | if (bond->wq) | ||
4546 | destroy_workqueue(bond->wq); | ||
4547 | |||
4548 | netif_addr_lock_bh(bond_dev); | 4553 | netif_addr_lock_bh(bond_dev); |
4549 | bond_mc_list_destroy(bond); | 4554 | bond_mc_list_destroy(bond); |
4550 | netif_addr_unlock_bh(bond_dev); | 4555 | netif_addr_unlock_bh(bond_dev); |
@@ -4956,8 +4961,8 @@ int bond_create(struct net *net, const char *name) | |||
4956 | bond_setup); | 4961 | bond_setup); |
4957 | if (!bond_dev) { | 4962 | if (!bond_dev) { |
4958 | pr_err("%s: eek! can't alloc netdev!\n", name); | 4963 | pr_err("%s: eek! can't alloc netdev!\n", name); |
4959 | res = -ENOMEM; | 4964 | rtnl_unlock(); |
4960 | goto out; | 4965 | return -ENOMEM; |
4961 | } | 4966 | } |
4962 | 4967 | ||
4963 | dev_net_set(bond_dev, net); | 4968 | dev_net_set(bond_dev, net); |
@@ -4966,19 +4971,16 @@ int bond_create(struct net *net, const char *name) | |||
4966 | if (!name) { | 4971 | if (!name) { |
4967 | res = dev_alloc_name(bond_dev, "bond%d"); | 4972 | res = dev_alloc_name(bond_dev, "bond%d"); |
4968 | if (res < 0) | 4973 | if (res < 0) |
4969 | goto out_netdev; | 4974 | goto out; |
4970 | } | 4975 | } |
4971 | 4976 | ||
4972 | res = register_netdevice(bond_dev); | 4977 | res = register_netdevice(bond_dev); |
4973 | if (res < 0) | ||
4974 | goto out_netdev; | ||
4975 | 4978 | ||
4976 | out: | 4979 | out: |
4977 | rtnl_unlock(); | 4980 | rtnl_unlock(); |
4981 | if (res < 0) | ||
4982 | bond_destructor(bond_dev); | ||
4978 | return res; | 4983 | return res; |
4979 | out_netdev: | ||
4980 | free_netdev(bond_dev); | ||
4981 | goto out; | ||
4982 | } | 4984 | } |
4983 | 4985 | ||
4984 | static int __net_init bond_net_init(struct net *net) | 4986 | static int __net_init bond_net_init(struct net *net) |