diff options
author | Jay Vosburgh <fubar@us.ibm.com> | 2008-01-17 19:25:02 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2008-01-18 14:38:39 -0500 |
commit | 027ea0416c955778ceca7ef82e48a1dd6b4617c9 (patch) | |
tree | ad823fb28b414d948fc372a08c8c15cee31bd8e1 /drivers/net/bonding/bond_main.c | |
parent | ece95f7fefe3afae19e641e1b3f5e64b00d5b948 (diff) |
bonding: fix lock ordering for rtnl and bonding_rwsem
Fix the handling of rtnl and the bonding_rwsem to always be acquired
in a consistent order (rtnl, then bonding_rwsem).
The existing code sometimes acquired them in this order, and sometimes
in the opposite order, which opens a window for deadlock between ifenslave
and sysfs.
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/bonding/bond_main.c')
-rw-r--r-- | drivers/net/bonding/bond_main.c | 19 |
1 files changed, 19 insertions, 0 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 379c5d87c804..2c6da4969382 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c | |||
@@ -4874,9 +4874,22 @@ static struct lock_class_key bonding_netdev_xmit_lock_key; | |||
4874 | int bond_create(char *name, struct bond_params *params, struct bonding **newbond) | 4874 | int bond_create(char *name, struct bond_params *params, struct bonding **newbond) |
4875 | { | 4875 | { |
4876 | struct net_device *bond_dev; | 4876 | struct net_device *bond_dev; |
4877 | struct bonding *bond, *nxt; | ||
4877 | int res; | 4878 | int res; |
4878 | 4879 | ||
4879 | rtnl_lock(); | 4880 | rtnl_lock(); |
4881 | down_write(&bonding_rwsem); | ||
4882 | |||
4883 | /* Check to see if the bond already exists. */ | ||
4884 | list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) | ||
4885 | if (strnicmp(bond->dev->name, name, IFNAMSIZ) == 0) { | ||
4886 | printk(KERN_ERR DRV_NAME | ||
4887 | ": cannot add bond %s; it already exists\n", | ||
4888 | name); | ||
4889 | res = -EPERM; | ||
4890 | goto out_rtnl; | ||
4891 | } | ||
4892 | |||
4880 | bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "", | 4893 | bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "", |
4881 | ether_setup); | 4894 | ether_setup); |
4882 | if (!bond_dev) { | 4895 | if (!bond_dev) { |
@@ -4915,10 +4928,12 @@ int bond_create(char *name, struct bond_params *params, struct bonding **newbond | |||
4915 | 4928 | ||
4916 | netif_carrier_off(bond_dev); | 4929 | netif_carrier_off(bond_dev); |
4917 | 4930 | ||
4931 | up_write(&bonding_rwsem); | ||
4918 | rtnl_unlock(); /* allows sysfs registration of net device */ | 4932 | rtnl_unlock(); /* allows sysfs registration of net device */ |
4919 | res = bond_create_sysfs_entry(bond_dev->priv); | 4933 | res = bond_create_sysfs_entry(bond_dev->priv); |
4920 | if (res < 0) { | 4934 | if (res < 0) { |
4921 | rtnl_lock(); | 4935 | rtnl_lock(); |
4936 | down_write(&bonding_rwsem); | ||
4922 | goto out_bond; | 4937 | goto out_bond; |
4923 | } | 4938 | } |
4924 | 4939 | ||
@@ -4929,6 +4944,7 @@ out_bond: | |||
4929 | out_netdev: | 4944 | out_netdev: |
4930 | free_netdev(bond_dev); | 4945 | free_netdev(bond_dev); |
4931 | out_rtnl: | 4946 | out_rtnl: |
4947 | up_write(&bonding_rwsem); | ||
4932 | rtnl_unlock(); | 4948 | rtnl_unlock(); |
4933 | return res; | 4949 | return res; |
4934 | } | 4950 | } |
@@ -4949,6 +4965,9 @@ static int __init bonding_init(void) | |||
4949 | #ifdef CONFIG_PROC_FS | 4965 | #ifdef CONFIG_PROC_FS |
4950 | bond_create_proc_dir(); | 4966 | bond_create_proc_dir(); |
4951 | #endif | 4967 | #endif |
4968 | |||
4969 | init_rwsem(&bonding_rwsem); | ||
4970 | |||
4952 | for (i = 0; i < max_bonds; i++) { | 4971 | for (i = 0; i < max_bonds; i++) { |
4953 | res = bond_create(NULL, &bonding_defaults, NULL); | 4972 | res = bond_create(NULL, &bonding_defaults, NULL); |
4954 | if (res) | 4973 | if (res) |