aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/bonding/bond_main.c
diff options
context:
space:
mode:
authorJay Vosburgh <fubar@us.ibm.com>2008-01-17 19:25:02 -0500
committerJeff Garzik <jeff@garzik.org>2008-01-18 14:38:39 -0500
commit027ea0416c955778ceca7ef82e48a1dd6b4617c9 (patch)
treead823fb28b414d948fc372a08c8c15cee31bd8e1 /drivers/net/bonding/bond_main.c
parentece95f7fefe3afae19e641e1b3f5e64b00d5b948 (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.c19
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;
4874int bond_create(char *name, struct bond_params *params, struct bonding **newbond) 4874int 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:
4929out_netdev: 4944out_netdev:
4930 free_netdev(bond_dev); 4945 free_netdev(bond_dev);
4931out_rtnl: 4946out_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)