bonding: fix lock ordering for rtnl and bonding_rwsem
[cascardo/linux.git] / drivers / net / bonding / bond_main.c
index 379c5d8..2c6da49 100644 (file)
@@ -4874,9 +4874,22 @@ static struct lock_class_key bonding_netdev_xmit_lock_key;
 int bond_create(char *name, struct bond_params *params, struct bonding **newbond)
 {
        struct net_device *bond_dev;
+       struct bonding *bond, *nxt;
        int res;
 
        rtnl_lock();
+       down_write(&bonding_rwsem);
+
+       /* Check to see if the bond already exists. */
+       list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list)
+               if (strnicmp(bond->dev->name, name, IFNAMSIZ) == 0) {
+                       printk(KERN_ERR DRV_NAME
+                              ": cannot add bond %s; it already exists\n",
+                              name);
+                       res = -EPERM;
+                       goto out_rtnl;
+               }
+
        bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "",
                                ether_setup);
        if (!bond_dev) {
@@ -4915,10 +4928,12 @@ int bond_create(char *name, struct bond_params *params, struct bonding **newbond
 
        netif_carrier_off(bond_dev);
 
+       up_write(&bonding_rwsem);
        rtnl_unlock(); /* allows sysfs registration of net device */
        res = bond_create_sysfs_entry(bond_dev->priv);
        if (res < 0) {
                rtnl_lock();
+               down_write(&bonding_rwsem);
                goto out_bond;
        }
 
@@ -4929,6 +4944,7 @@ out_bond:
 out_netdev:
        free_netdev(bond_dev);
 out_rtnl:
+       up_write(&bonding_rwsem);
        rtnl_unlock();
        return res;
 }
@@ -4949,6 +4965,9 @@ static int __init bonding_init(void)
 #ifdef CONFIG_PROC_FS
        bond_create_proc_dir();
 #endif
+
+       init_rwsem(&bonding_rwsem);
+
        for (i = 0; i < max_bonds; i++) {
                res = bond_create(NULL, &bonding_defaults, NULL);
                if (res)