Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=027ea0416c955778ceca7ef82e48a1dd6b4617c9
Commit:     027ea0416c955778ceca7ef82e48a1dd6b4617c9
Parent:     ece95f7fefe3afae19e641e1b3f5e64b00d5b948
Author:     Jay Vosburgh <[EMAIL PROTECTED]>
AuthorDate: Thu Jan 17 16:25:02 2008 -0800
Committer:  Jeff Garzik <[EMAIL PROTECTED]>
CommitDate: Fri Jan 18 14:38:39 2008 -0500

    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 <[EMAIL PROTECTED]>
    Signed-off-by: Jeff Garzik <[EMAIL PROTECTED]>
---
 drivers/net/bonding/bond_main.c  |   19 ++++++++++++++++
 drivers/net/bonding/bond_sysfs.c |   43 ++++++++++++++-----------------------
 2 files changed, 35 insertions(+), 27 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 379c5d8..2c6da49 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;
 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)
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index bff4f2b..90a1f31 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -109,11 +109,10 @@ static ssize_t bonding_store_bonds(struct class *cls, 
const char *buffer, size_t
 {
        char command[IFNAMSIZ + 1] = {0, };
        char *ifname;
-       int res = count;
+       int rv, res = count;
        struct bonding *bond;
        struct bonding *nxt;
 
-       down_write(&(bonding_rwsem));
        sscanf(buffer, "%16s", command); /* IFNAMSIZ*/
        ifname = command + 1;
        if ((strlen(command) <= 1) ||
@@ -121,39 +120,28 @@ static ssize_t bonding_store_bonds(struct class *cls, 
const char *buffer, size_t
                goto err_no_cmd;
 
        if (command[0] == '+') {
-
-               /* 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, ifname, IFNAMSIZ) == 0) {
-                               printk(KERN_ERR DRV_NAME
-                                       ": cannot add bond %s; it already 
exists\n",
-                                       ifname);
-                               res = -EPERM;
-                               goto out;
-                       }
-
                printk(KERN_INFO DRV_NAME
                        ": %s is being created...\n", ifname);
-               if (bond_create(ifname, &bonding_defaults, &bond)) {
-                       printk(KERN_INFO DRV_NAME
-                       ": %s interface already exists. Bond creation 
failed.\n",
-                       ifname);
-                       res = -EPERM;
+               rv = bond_create(ifname, &bonding_defaults, &bond);
+               if (rv) {
+                       printk(KERN_INFO DRV_NAME ": Bond creation failed.\n");
+                       res = rv;
                }
                goto out;
        }
 
        if (command[0] == '-') {
+               rtnl_lock();
+               down_write(&bonding_rwsem);
+
                list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list)
                        if (strnicmp(bond->dev->name, ifname, IFNAMSIZ) == 0) {
-                               rtnl_lock();
                                /* check the ref count on the bond's kobject.
                                 * If it's > expected, then there's a file open,
                                 * and we have to fail.
                                 */
                                if 
(atomic_read(&bond->dev->dev.kobj.kref.refcount)
                                                        > expected_refcount){
-                                       rtnl_unlock();
                                        printk(KERN_INFO DRV_NAME
                                                ": Unable remove bond %s due to 
open references.\n",
                                                ifname);
@@ -164,6 +152,7 @@ static ssize_t bonding_store_bonds(struct class *cls, const 
char *buffer, size_t
                                        ": %s is being deleted...\n",
                                        bond->dev->name);
                                bond_destroy(bond);
+                               up_write(&bonding_rwsem);
                                rtnl_unlock();
                                goto out;
                        }
@@ -171,6 +160,8 @@ static ssize_t bonding_store_bonds(struct class *cls, const 
char *buffer, size_t
                printk(KERN_ERR DRV_NAME
                        ": unable to delete non-existent bond %s\n", ifname);
                res = -ENODEV;
+               up_write(&bonding_rwsem);
+               rtnl_unlock();
                goto out;
        }
 
@@ -183,7 +174,6 @@ err_no_cmd:
         * get called forever, which is bad.
         */
 out:
-       up_write(&(bonding_rwsem));
        return res;
 }
 /* class attribute for bond_masters file.  This ends up in /sys/class/net */
@@ -271,6 +261,9 @@ static ssize_t bonding_store_slaves(struct device *d,
 
        /* Note:  We can't hold bond->lock here, as bond_create grabs it. */
 
+       rtnl_lock();
+       down_write(&(bonding_rwsem));
+
        sscanf(buffer, "%16s", command); /* IFNAMSIZ*/
        ifname = command + 1;
        if ((strlen(command) <= 1) ||
@@ -336,12 +329,10 @@ static ssize_t bonding_store_slaves(struct device *d,
                                dev->mtu = bond->dev->mtu;
                        }
                }
-               rtnl_lock();
                res = bond_enslave(bond->dev, dev);
                bond_for_each_slave(bond, slave, i)
                        if (strnicmp(slave->dev->name, ifname, IFNAMSIZ) == 0)
                                slave->original_mtu = original_mtu;
-               rtnl_unlock();
                if (res) {
                        ret = res;
                }
@@ -359,12 +350,10 @@ static ssize_t bonding_store_slaves(struct device *d,
                if (dev) {
                        printk(KERN_INFO DRV_NAME ": %s: Removing slave %s\n",
                                bond->dev->name, dev->name);
-                       rtnl_lock();
                        if (bond->setup_by_slave)
                                res = bond_release_and_destroy(bond->dev, dev);
                        else
                                res = bond_release(bond->dev, dev);
-                       rtnl_unlock();
                        if (res) {
                                ret = res;
                                goto out;
@@ -389,6 +378,8 @@ err_no_cmd:
        ret = -EPERM;
 
 out:
+       up_write(&(bonding_rwsem));
+       rtnl_unlock();
        return ret;
 }
 
@@ -1423,8 +1414,6 @@ int bond_create_sysfs(void)
        int ret = 0;
        struct bonding *firstbond;
 
-       init_rwsem(&bonding_rwsem);
-
        /* get the netdev class pointer */
        firstbond = container_of(bond_dev_list.next, struct bonding, bond_list);
        if (!firstbond)
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to