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 | |
| 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>
| -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) |
