Commit 027ea041 authored by Jay Vosburgh's avatar Jay Vosburgh Committed by Jeff Garzik

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: default avatarJay Vosburgh <fubar@us.ibm.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent ece95f7f
...@@ -4874,9 +4874,22 @@ static struct lock_class_key bonding_netdev_xmit_lock_key; ...@@ -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) int bond_create(char *name, struct bond_params *params, struct bonding **newbond)
{ {
struct net_device *bond_dev; struct net_device *bond_dev;
struct bonding *bond, *nxt;
int res; int res;
rtnl_lock(); 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 : "", bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "",
ether_setup); ether_setup);
if (!bond_dev) { if (!bond_dev) {
...@@ -4915,10 +4928,12 @@ int bond_create(char *name, struct bond_params *params, struct bonding **newbond ...@@ -4915,10 +4928,12 @@ int bond_create(char *name, struct bond_params *params, struct bonding **newbond
netif_carrier_off(bond_dev); netif_carrier_off(bond_dev);
up_write(&bonding_rwsem);
rtnl_unlock(); /* allows sysfs registration of net device */ rtnl_unlock(); /* allows sysfs registration of net device */
res = bond_create_sysfs_entry(bond_dev->priv); res = bond_create_sysfs_entry(bond_dev->priv);
if (res < 0) { if (res < 0) {
rtnl_lock(); rtnl_lock();
down_write(&bonding_rwsem);
goto out_bond; goto out_bond;
} }
...@@ -4929,6 +4944,7 @@ out_bond: ...@@ -4929,6 +4944,7 @@ out_bond:
out_netdev: out_netdev:
free_netdev(bond_dev); free_netdev(bond_dev);
out_rtnl: out_rtnl:
up_write(&bonding_rwsem);
rtnl_unlock(); rtnl_unlock();
return res; return res;
} }
...@@ -4949,6 +4965,9 @@ static int __init bonding_init(void) ...@@ -4949,6 +4965,9 @@ static int __init bonding_init(void)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
bond_create_proc_dir(); bond_create_proc_dir();
#endif #endif
init_rwsem(&bonding_rwsem);
for (i = 0; i < max_bonds; i++) { for (i = 0; i < max_bonds; i++) {
res = bond_create(NULL, &bonding_defaults, NULL); res = bond_create(NULL, &bonding_defaults, NULL);
if (res) if (res)
......
...@@ -109,11 +109,10 @@ static ssize_t bonding_store_bonds(struct class *cls, const char *buffer, size_t ...@@ -109,11 +109,10 @@ static ssize_t bonding_store_bonds(struct class *cls, const char *buffer, size_t
{ {
char command[IFNAMSIZ + 1] = {0, }; char command[IFNAMSIZ + 1] = {0, };
char *ifname; char *ifname;
int res = count; int rv, res = count;
struct bonding *bond; struct bonding *bond;
struct bonding *nxt; struct bonding *nxt;
down_write(&(bonding_rwsem));
sscanf(buffer, "%16s", command); /* IFNAMSIZ*/ sscanf(buffer, "%16s", command); /* IFNAMSIZ*/
ifname = command + 1; ifname = command + 1;
if ((strlen(command) <= 1) || if ((strlen(command) <= 1) ||
...@@ -121,39 +120,28 @@ static ssize_t bonding_store_bonds(struct class *cls, const char *buffer, size_t ...@@ -121,39 +120,28 @@ static ssize_t bonding_store_bonds(struct class *cls, const char *buffer, size_t
goto err_no_cmd; goto err_no_cmd;
if (command[0] == '+') { 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 printk(KERN_INFO DRV_NAME
": %s is being created...\n", ifname); ": %s is being created...\n", ifname);
if (bond_create(ifname, &bonding_defaults, &bond)) { rv = bond_create(ifname, &bonding_defaults, &bond);
printk(KERN_INFO DRV_NAME if (rv) {
": %s interface already exists. Bond creation failed.\n", printk(KERN_INFO DRV_NAME ": Bond creation failed.\n");
ifname); res = rv;
res = -EPERM;
} }
goto out; goto out;
} }
if (command[0] == '-') { if (command[0] == '-') {
rtnl_lock();
down_write(&bonding_rwsem);
list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list)
if (strnicmp(bond->dev->name, ifname, IFNAMSIZ) == 0) { if (strnicmp(bond->dev->name, ifname, IFNAMSIZ) == 0) {
rtnl_lock();
/* check the ref count on the bond's kobject. /* check the ref count on the bond's kobject.
* If it's > expected, then there's a file open, * If it's > expected, then there's a file open,
* and we have to fail. * and we have to fail.
*/ */
if (atomic_read(&bond->dev->dev.kobj.kref.refcount) if (atomic_read(&bond->dev->dev.kobj.kref.refcount)
> expected_refcount){ > expected_refcount){
rtnl_unlock();
printk(KERN_INFO DRV_NAME printk(KERN_INFO DRV_NAME
": Unable remove bond %s due to open references.\n", ": Unable remove bond %s due to open references.\n",
ifname); ifname);
...@@ -164,6 +152,7 @@ static ssize_t bonding_store_bonds(struct class *cls, const char *buffer, size_t ...@@ -164,6 +152,7 @@ static ssize_t bonding_store_bonds(struct class *cls, const char *buffer, size_t
": %s is being deleted...\n", ": %s is being deleted...\n",
bond->dev->name); bond->dev->name);
bond_destroy(bond); bond_destroy(bond);
up_write(&bonding_rwsem);
rtnl_unlock(); rtnl_unlock();
goto out; goto out;
} }
...@@ -171,6 +160,8 @@ static ssize_t bonding_store_bonds(struct class *cls, const char *buffer, size_t ...@@ -171,6 +160,8 @@ static ssize_t bonding_store_bonds(struct class *cls, const char *buffer, size_t
printk(KERN_ERR DRV_NAME printk(KERN_ERR DRV_NAME
": unable to delete non-existent bond %s\n", ifname); ": unable to delete non-existent bond %s\n", ifname);
res = -ENODEV; res = -ENODEV;
up_write(&bonding_rwsem);
rtnl_unlock();
goto out; goto out;
} }
...@@ -183,7 +174,6 @@ err_no_cmd: ...@@ -183,7 +174,6 @@ err_no_cmd:
* get called forever, which is bad. * get called forever, which is bad.
*/ */
out: out:
up_write(&(bonding_rwsem));
return res; return res;
} }
/* class attribute for bond_masters file. This ends up in /sys/class/net */ /* 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, ...@@ -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. */ /* Note: We can't hold bond->lock here, as bond_create grabs it. */
rtnl_lock();
down_write(&(bonding_rwsem));
sscanf(buffer, "%16s", command); /* IFNAMSIZ*/ sscanf(buffer, "%16s", command); /* IFNAMSIZ*/
ifname = command + 1; ifname = command + 1;
if ((strlen(command) <= 1) || if ((strlen(command) <= 1) ||
...@@ -336,12 +329,10 @@ static ssize_t bonding_store_slaves(struct device *d, ...@@ -336,12 +329,10 @@ static ssize_t bonding_store_slaves(struct device *d,
dev->mtu = bond->dev->mtu; dev->mtu = bond->dev->mtu;
} }
} }
rtnl_lock();
res = bond_enslave(bond->dev, dev); res = bond_enslave(bond->dev, dev);
bond_for_each_slave(bond, slave, i) bond_for_each_slave(bond, slave, i)
if (strnicmp(slave->dev->name, ifname, IFNAMSIZ) == 0) if (strnicmp(slave->dev->name, ifname, IFNAMSIZ) == 0)
slave->original_mtu = original_mtu; slave->original_mtu = original_mtu;
rtnl_unlock();
if (res) { if (res) {
ret = res; ret = res;
} }
...@@ -359,12 +350,10 @@ static ssize_t bonding_store_slaves(struct device *d, ...@@ -359,12 +350,10 @@ static ssize_t bonding_store_slaves(struct device *d,
if (dev) { if (dev) {
printk(KERN_INFO DRV_NAME ": %s: Removing slave %s\n", printk(KERN_INFO DRV_NAME ": %s: Removing slave %s\n",
bond->dev->name, dev->name); bond->dev->name, dev->name);
rtnl_lock();
if (bond->setup_by_slave) if (bond->setup_by_slave)
res = bond_release_and_destroy(bond->dev, dev); res = bond_release_and_destroy(bond->dev, dev);
else else
res = bond_release(bond->dev, dev); res = bond_release(bond->dev, dev);
rtnl_unlock();
if (res) { if (res) {
ret = res; ret = res;
goto out; goto out;
...@@ -389,6 +378,8 @@ err_no_cmd: ...@@ -389,6 +378,8 @@ err_no_cmd:
ret = -EPERM; ret = -EPERM;
out: out:
up_write(&(bonding_rwsem));
rtnl_unlock();
return ret; return ret;
} }
...@@ -1423,8 +1414,6 @@ int bond_create_sysfs(void) ...@@ -1423,8 +1414,6 @@ int bond_create_sysfs(void)
int ret = 0; int ret = 0;
struct bonding *firstbond; struct bonding *firstbond;
init_rwsem(&bonding_rwsem);
/* get the netdev class pointer */ /* get the netdev class pointer */
firstbond = container_of(bond_dev_list.next, struct bonding, bond_list); firstbond = container_of(bond_dev_list.next, struct bonding, bond_list);
if (!firstbond) if (!firstbond)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment