Commit 2543331d authored by Jay Vosburgh's avatar Jay Vosburgh Committed by Jeff Garzik

bonding: fix locking during alb failover and slave removal

	alb_fasten_mac_swap (actually rlb_teach_disabled_mac_on_primary)
requries RTNL and no other locks.  This could cause dev_set_promiscuity
and/or dev_set_mac_address to be called with improper locking.

	Changed callers to hold only RTNL during calls to alb_fasten_mac_swap
or functions calling it.  Updated header comments in affected functions to
reflect proper reality of locking requirements.
Signed-off-by: default avatarJay Vosburgh <fubar@us.ibm.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent e0138a66
...@@ -979,7 +979,7 @@ static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct ...@@ -979,7 +979,7 @@ static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct
/* /*
* Send learning packets after MAC address swap. * Send learning packets after MAC address swap.
* *
* Called with RTNL and bond->lock held for read. * Called with RTNL and no other locks
*/ */
static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1, static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
struct slave *slave2) struct slave *slave2)
...@@ -987,6 +987,8 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1, ...@@ -987,6 +987,8 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
int slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2)); int slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2));
struct slave *disabled_slave = NULL; struct slave *disabled_slave = NULL;
ASSERT_RTNL();
/* fasten the change in the switch */ /* fasten the change in the switch */
if (SLAVE_IS_OK(slave1)) { if (SLAVE_IS_OK(slave1)) {
alb_send_learning_packets(slave1, slave1->dev->dev_addr); alb_send_learning_packets(slave1, slave1->dev->dev_addr);
...@@ -1031,7 +1033,7 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1, ...@@ -1031,7 +1033,7 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
* a slave that has @slave's permanet address as its current address. * a slave that has @slave's permanet address as its current address.
* We'll make sure that that slave no longer uses @slave's permanent address. * We'll make sure that that slave no longer uses @slave's permanent address.
* *
* Caller must hold bond lock * Caller must hold RTNL and no other locks
*/ */
static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *slave) static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *slave)
{ {
...@@ -1542,7 +1544,12 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave) ...@@ -1542,7 +1544,12 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave)
return 0; return 0;
} }
/* Caller must hold bond lock for write */ /*
* Remove slave from tlb and rlb hash tables, and fix up MAC addresses
* if necessary.
*
* Caller must hold RTNL and no other locks
*/
void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave) void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave)
{ {
if (bond->slave_cnt > 1) { if (bond->slave_cnt > 1) {
...@@ -1658,12 +1665,11 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave ...@@ -1658,12 +1665,11 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
bond->alb_info.rlb_enabled); bond->alb_info.rlb_enabled);
} }
read_lock(&bond->lock);
if (swap_slave) { if (swap_slave) {
alb_fasten_mac_swap(bond, swap_slave, new_slave); alb_fasten_mac_swap(bond, swap_slave, new_slave);
read_lock(&bond->lock);
} else { } else {
/* fasten bond mac on new current slave */ read_lock(&bond->lock);
alb_send_learning_packets(new_slave, bond->dev->dev_addr); alb_send_learning_packets(new_slave, bond->dev->dev_addr);
} }
......
...@@ -1746,7 +1746,9 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) ...@@ -1746,7 +1746,9 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
* has been cleared (if our_slave == old_current), * has been cleared (if our_slave == old_current),
* but before a new active slave is selected. * but before a new active slave is selected.
*/ */
write_unlock_bh(&bond->lock);
bond_alb_deinit_slave(bond, slave); bond_alb_deinit_slave(bond, slave);
write_lock_bh(&bond->lock);
} }
if (oldcurrent == slave) { if (oldcurrent == slave) {
...@@ -1905,6 +1907,12 @@ static int bond_release_all(struct net_device *bond_dev) ...@@ -1905,6 +1907,12 @@ static int bond_release_all(struct net_device *bond_dev)
slave_dev = slave->dev; slave_dev = slave->dev;
bond_detach_slave(bond, slave); bond_detach_slave(bond, slave);
/* now that the slave is detached, unlock and perform
* all the undo steps that should not be called from
* within a lock.
*/
write_unlock_bh(&bond->lock);
if ((bond->params.mode == BOND_MODE_TLB) || if ((bond->params.mode == BOND_MODE_TLB) ||
(bond->params.mode == BOND_MODE_ALB)) { (bond->params.mode == BOND_MODE_ALB)) {
/* must be called only after the slave /* must be called only after the slave
...@@ -1915,12 +1923,6 @@ static int bond_release_all(struct net_device *bond_dev) ...@@ -1915,12 +1923,6 @@ static int bond_release_all(struct net_device *bond_dev)
bond_compute_features(bond); bond_compute_features(bond);
/* now that the slave is detached, unlock and perform
* all the undo steps that should not be called from
* within a lock.
*/
write_unlock_bh(&bond->lock);
bond_destroy_slave_symlinks(bond_dev, slave_dev); bond_destroy_slave_symlinks(bond_dev, slave_dev);
bond_del_vlans_from_slave(bond, slave_dev); bond_del_vlans_from_slave(bond, slave_dev);
......
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