|
@@ -708,7 +708,7 @@ out:
|
|
|
|
|
|
static bool bond_should_change_active(struct bonding *bond)
|
|
static bool bond_should_change_active(struct bonding *bond)
|
|
{
|
|
{
|
|
- struct slave *prim = bond->primary_slave;
|
|
|
|
|
|
+ struct slave *prim = rtnl_dereference(bond->primary_slave);
|
|
struct slave *curr = bond_deref_active_protected(bond);
|
|
struct slave *curr = bond_deref_active_protected(bond);
|
|
|
|
|
|
if (!prim || !curr || curr->link != BOND_LINK_UP)
|
|
if (!prim || !curr || curr->link != BOND_LINK_UP)
|
|
@@ -732,13 +732,14 @@ static bool bond_should_change_active(struct bonding *bond)
|
|
*/
|
|
*/
|
|
static struct slave *bond_find_best_slave(struct bonding *bond)
|
|
static struct slave *bond_find_best_slave(struct bonding *bond)
|
|
{
|
|
{
|
|
- struct slave *slave, *bestslave = NULL;
|
|
|
|
|
|
+ struct slave *slave, *bestslave = NULL, *primary;
|
|
struct list_head *iter;
|
|
struct list_head *iter;
|
|
int mintime = bond->params.updelay;
|
|
int mintime = bond->params.updelay;
|
|
|
|
|
|
- if (bond->primary_slave && bond->primary_slave->link == BOND_LINK_UP &&
|
|
|
|
|
|
+ primary = rtnl_dereference(bond->primary_slave);
|
|
|
|
+ if (primary && primary->link == BOND_LINK_UP &&
|
|
bond_should_change_active(bond))
|
|
bond_should_change_active(bond))
|
|
- return bond->primary_slave;
|
|
|
|
|
|
+ return primary;
|
|
|
|
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
if (slave->link == BOND_LINK_UP)
|
|
if (slave->link == BOND_LINK_UP)
|
|
@@ -1482,7 +1483,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|
if (bond_uses_primary(bond) && bond->params.primary[0]) {
|
|
if (bond_uses_primary(bond) && bond->params.primary[0]) {
|
|
/* if there is a primary slave, remember it */
|
|
/* if there is a primary slave, remember it */
|
|
if (strcmp(bond->params.primary, new_slave->dev->name) == 0) {
|
|
if (strcmp(bond->params.primary, new_slave->dev->name) == 0) {
|
|
- bond->primary_slave = new_slave;
|
|
|
|
|
|
+ rcu_assign_pointer(bond->primary_slave, new_slave);
|
|
bond->force_primary = true;
|
|
bond->force_primary = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -1596,8 +1597,8 @@ err_detach:
|
|
bond_hw_addr_flush(bond_dev, slave_dev);
|
|
bond_hw_addr_flush(bond_dev, slave_dev);
|
|
|
|
|
|
vlan_vids_del_by_dev(slave_dev, bond_dev);
|
|
vlan_vids_del_by_dev(slave_dev, bond_dev);
|
|
- if (bond->primary_slave == new_slave)
|
|
|
|
- bond->primary_slave = NULL;
|
|
|
|
|
|
+ if (rcu_access_pointer(bond->primary_slave) == new_slave)
|
|
|
|
+ RCU_INIT_POINTER(bond->primary_slave, NULL);
|
|
if (rcu_access_pointer(bond->curr_active_slave) == new_slave) {
|
|
if (rcu_access_pointer(bond->curr_active_slave) == new_slave) {
|
|
block_netpoll_tx();
|
|
block_netpoll_tx();
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
@@ -1606,6 +1607,8 @@ err_detach:
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
write_unlock_bh(&bond->curr_slave_lock);
|
|
unblock_netpoll_tx();
|
|
unblock_netpoll_tx();
|
|
}
|
|
}
|
|
|
|
+ /* either primary_slave or curr_active_slave might've changed */
|
|
|
|
+ synchronize_rcu();
|
|
slave_disable_netpoll(new_slave);
|
|
slave_disable_netpoll(new_slave);
|
|
|
|
|
|
err_close:
|
|
err_close:
|
|
@@ -1687,13 +1690,15 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|
* for this slave anymore.
|
|
* for this slave anymore.
|
|
*/
|
|
*/
|
|
netdev_rx_handler_unregister(slave_dev);
|
|
netdev_rx_handler_unregister(slave_dev);
|
|
- write_lock_bh(&bond->lock);
|
|
|
|
|
|
|
|
- /* Inform AD package of unbinding of slave. */
|
|
|
|
- if (BOND_MODE(bond) == BOND_MODE_8023AD)
|
|
|
|
|
|
+ if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
|
|
|
+ /* Sync against bond_3ad_rx_indication and
|
|
|
|
+ * bond_3ad_state_machine_handler
|
|
|
|
+ */
|
|
|
|
+ write_lock_bh(&bond->curr_slave_lock);
|
|
bond_3ad_unbind_slave(slave);
|
|
bond_3ad_unbind_slave(slave);
|
|
-
|
|
|
|
- write_unlock_bh(&bond->lock);
|
|
|
|
|
|
+ write_unlock_bh(&bond->curr_slave_lock);
|
|
|
|
+ }
|
|
|
|
|
|
netdev_info(bond_dev, "Releasing %s interface %s\n",
|
|
netdev_info(bond_dev, "Releasing %s interface %s\n",
|
|
bond_is_active_slave(slave) ? "active" : "backup",
|
|
bond_is_active_slave(slave) ? "active" : "backup",
|
|
@@ -1712,8 +1717,8 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|
bond_dev->name, slave_dev->name);
|
|
bond_dev->name, slave_dev->name);
|
|
}
|
|
}
|
|
|
|
|
|
- if (bond->primary_slave == slave)
|
|
|
|
- bond->primary_slave = NULL;
|
|
|
|
|
|
+ if (rtnl_dereference(bond->primary_slave) == slave)
|
|
|
|
+ RCU_INIT_POINTER(bond->primary_slave, NULL);
|
|
|
|
|
|
if (oldcurrent == slave) {
|
|
if (oldcurrent == slave) {
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
@@ -1974,7 +1979,7 @@ static int bond_miimon_inspect(struct bonding *bond)
|
|
static void bond_miimon_commit(struct bonding *bond)
|
|
static void bond_miimon_commit(struct bonding *bond)
|
|
{
|
|
{
|
|
struct list_head *iter;
|
|
struct list_head *iter;
|
|
- struct slave *slave;
|
|
|
|
|
|
+ struct slave *slave, *primary;
|
|
|
|
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
switch (slave->new_link) {
|
|
switch (slave->new_link) {
|
|
@@ -1985,13 +1990,14 @@ static void bond_miimon_commit(struct bonding *bond)
|
|
slave->link = BOND_LINK_UP;
|
|
slave->link = BOND_LINK_UP;
|
|
slave->last_link_up = jiffies;
|
|
slave->last_link_up = jiffies;
|
|
|
|
|
|
|
|
+ primary = rtnl_dereference(bond->primary_slave);
|
|
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
|
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
|
/* prevent it from being the active one */
|
|
/* prevent it from being the active one */
|
|
bond_set_backup_slave(slave);
|
|
bond_set_backup_slave(slave);
|
|
} else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
|
|
} else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
|
|
/* make it immediately active */
|
|
/* make it immediately active */
|
|
bond_set_active_slave(slave);
|
|
bond_set_active_slave(slave);
|
|
- } else if (slave != bond->primary_slave) {
|
|
|
|
|
|
+ } else if (slave != primary) {
|
|
/* prevent it from being the active one */
|
|
/* prevent it from being the active one */
|
|
bond_set_backup_slave(slave);
|
|
bond_set_backup_slave(slave);
|
|
}
|
|
}
|
|
@@ -2009,8 +2015,7 @@ static void bond_miimon_commit(struct bonding *bond)
|
|
bond_alb_handle_link_change(bond, slave,
|
|
bond_alb_handle_link_change(bond, slave,
|
|
BOND_LINK_UP);
|
|
BOND_LINK_UP);
|
|
|
|
|
|
- if (!bond->curr_active_slave ||
|
|
|
|
- (slave == bond->primary_slave))
|
|
|
|
|
|
+ if (!bond->curr_active_slave || slave == primary)
|
|
goto do_failover;
|
|
goto do_failover;
|
|
|
|
|
|
continue;
|
|
continue;
|
|
@@ -2631,7 +2636,7 @@ static void bond_ab_arp_commit(struct bonding *bond)
|
|
slave->dev->name);
|
|
slave->dev->name);
|
|
|
|
|
|
if (!rtnl_dereference(bond->curr_active_slave) ||
|
|
if (!rtnl_dereference(bond->curr_active_slave) ||
|
|
- (slave == bond->primary_slave))
|
|
|
|
|
|
+ slave == rtnl_dereference(bond->primary_slave))
|
|
goto do_failover;
|
|
goto do_failover;
|
|
|
|
|
|
}
|
|
}
|
|
@@ -2858,7 +2863,7 @@ static int bond_master_netdev_event(unsigned long event,
|
|
static int bond_slave_netdev_event(unsigned long event,
|
|
static int bond_slave_netdev_event(unsigned long event,
|
|
struct net_device *slave_dev)
|
|
struct net_device *slave_dev)
|
|
{
|
|
{
|
|
- struct slave *slave = bond_slave_get_rtnl(slave_dev);
|
|
|
|
|
|
+ struct slave *slave = bond_slave_get_rtnl(slave_dev), *primary;
|
|
struct bonding *bond;
|
|
struct bonding *bond;
|
|
struct net_device *bond_dev;
|
|
struct net_device *bond_dev;
|
|
u32 old_speed;
|
|
u32 old_speed;
|
|
@@ -2872,6 +2877,7 @@ static int bond_slave_netdev_event(unsigned long event,
|
|
return NOTIFY_DONE;
|
|
return NOTIFY_DONE;
|
|
bond_dev = slave->bond->dev;
|
|
bond_dev = slave->bond->dev;
|
|
bond = slave->bond;
|
|
bond = slave->bond;
|
|
|
|
+ primary = rtnl_dereference(bond->primary_slave);
|
|
|
|
|
|
switch (event) {
|
|
switch (event) {
|
|
case NETDEV_UNREGISTER:
|
|
case NETDEV_UNREGISTER:
|
|
@@ -2919,18 +2925,18 @@ static int bond_slave_netdev_event(unsigned long event,
|
|
!bond->params.primary[0])
|
|
!bond->params.primary[0])
|
|
break;
|
|
break;
|
|
|
|
|
|
- if (slave == bond->primary_slave) {
|
|
|
|
|
|
+ if (slave == primary) {
|
|
/* slave's name changed - he's no longer primary */
|
|
/* slave's name changed - he's no longer primary */
|
|
- bond->primary_slave = NULL;
|
|
|
|
|
|
+ RCU_INIT_POINTER(bond->primary_slave, NULL);
|
|
} else if (!strcmp(slave_dev->name, bond->params.primary)) {
|
|
} else if (!strcmp(slave_dev->name, bond->params.primary)) {
|
|
/* we have a new primary slave */
|
|
/* we have a new primary slave */
|
|
- bond->primary_slave = slave;
|
|
|
|
|
|
+ rcu_assign_pointer(bond->primary_slave, slave);
|
|
} else { /* we didn't change primary - exit */
|
|
} else { /* we didn't change primary - exit */
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
netdev_info(bond->dev, "Primary slave changed to %s, reselecting active slave\n",
|
|
netdev_info(bond->dev, "Primary slave changed to %s, reselecting active slave\n",
|
|
- bond->primary_slave ? slave_dev->name : "none");
|
|
|
|
|
|
+ primary ? slave_dev->name : "none");
|
|
|
|
|
|
block_netpoll_tx();
|
|
block_netpoll_tx();
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
write_lock_bh(&bond->curr_slave_lock);
|
|
@@ -3099,7 +3105,6 @@ static int bond_open(struct net_device *bond_dev)
|
|
struct slave *slave;
|
|
struct slave *slave;
|
|
|
|
|
|
/* reset slave->backup and slave->inactive */
|
|
/* reset slave->backup and slave->inactive */
|
|
- read_lock(&bond->lock);
|
|
|
|
if (bond_has_slaves(bond)) {
|
|
if (bond_has_slaves(bond)) {
|
|
read_lock(&bond->curr_slave_lock);
|
|
read_lock(&bond->curr_slave_lock);
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
@@ -3114,7 +3119,6 @@ static int bond_open(struct net_device *bond_dev)
|
|
}
|
|
}
|
|
read_unlock(&bond->curr_slave_lock);
|
|
read_unlock(&bond->curr_slave_lock);
|
|
}
|
|
}
|
|
- read_unlock(&bond->lock);
|
|
|
|
|
|
|
|
bond_work_init_all(bond);
|
|
bond_work_init_all(bond);
|
|
|
|
|
|
@@ -3169,7 +3173,6 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
|
|
|
|
|
|
memset(stats, 0, sizeof(*stats));
|
|
memset(stats, 0, sizeof(*stats));
|
|
|
|
|
|
- read_lock_bh(&bond->lock);
|
|
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
const struct rtnl_link_stats64 *sstats =
|
|
const struct rtnl_link_stats64 *sstats =
|
|
dev_get_stats(slave->dev, &temp);
|
|
dev_get_stats(slave->dev, &temp);
|
|
@@ -3200,7 +3203,6 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
|
|
stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
|
|
stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
|
|
stats->tx_window_errors += sstats->tx_window_errors;
|
|
stats->tx_window_errors += sstats->tx_window_errors;
|
|
}
|
|
}
|
|
- read_unlock_bh(&bond->lock);
|
|
|
|
|
|
|
|
return stats;
|
|
return stats;
|
|
}
|
|
}
|
|
@@ -3240,13 +3242,11 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
|
|
|
|
|
|
if (mii->reg_num == 1) {
|
|
if (mii->reg_num == 1) {
|
|
mii->val_out = 0;
|
|
mii->val_out = 0;
|
|
- read_lock(&bond->lock);
|
|
|
|
read_lock(&bond->curr_slave_lock);
|
|
read_lock(&bond->curr_slave_lock);
|
|
if (netif_carrier_ok(bond->dev))
|
|
if (netif_carrier_ok(bond->dev))
|
|
mii->val_out = BMSR_LSTATUS;
|
|
mii->val_out = BMSR_LSTATUS;
|
|
|
|
|
|
read_unlock(&bond->curr_slave_lock);
|
|
read_unlock(&bond->curr_slave_lock);
|
|
- read_unlock(&bond->lock);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -3422,21 +3422,6 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
|
|
|
|
|
|
netdev_dbg(bond_dev, "bond=%p, new_mtu=%d\n", bond, new_mtu);
|
|
netdev_dbg(bond_dev, "bond=%p, new_mtu=%d\n", bond, new_mtu);
|
|
|
|
|
|
- /* Can't hold bond->lock with bh disabled here since
|
|
|
|
- * some base drivers panic. On the other hand we can't
|
|
|
|
- * hold bond->lock without bh disabled because we'll
|
|
|
|
- * deadlock. The only solution is to rely on the fact
|
|
|
|
- * that we're under rtnl_lock here, and the slaves
|
|
|
|
- * list won't change. This doesn't solve the problem
|
|
|
|
- * of setting the slave's MTU while it is
|
|
|
|
- * transmitting, but the assumption is that the base
|
|
|
|
- * driver can handle that.
|
|
|
|
- *
|
|
|
|
- * TODO: figure out a way to safely iterate the slaves
|
|
|
|
- * list, but without holding a lock around the actual
|
|
|
|
- * call to the base driver.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
netdev_dbg(bond_dev, "s %p c_m %p\n",
|
|
netdev_dbg(bond_dev, "s %p c_m %p\n",
|
|
slave, slave->dev->netdev_ops->ndo_change_mtu);
|
|
slave, slave->dev->netdev_ops->ndo_change_mtu);
|
|
@@ -3511,21 +3496,6 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
|
|
if (!is_valid_ether_addr(sa->sa_data))
|
|
if (!is_valid_ether_addr(sa->sa_data))
|
|
return -EADDRNOTAVAIL;
|
|
return -EADDRNOTAVAIL;
|
|
|
|
|
|
- /* Can't hold bond->lock with bh disabled here since
|
|
|
|
- * some base drivers panic. On the other hand we can't
|
|
|
|
- * hold bond->lock without bh disabled because we'll
|
|
|
|
- * deadlock. The only solution is to rely on the fact
|
|
|
|
- * that we're under rtnl_lock here, and the slaves
|
|
|
|
- * list won't change. This doesn't solve the problem
|
|
|
|
- * of setting the slave's hw address while it is
|
|
|
|
- * transmitting, but the assumption is that the base
|
|
|
|
- * driver can handle that.
|
|
|
|
- *
|
|
|
|
- * TODO: figure out a way to safely iterate the slaves
|
|
|
|
- * list, but without holding a lock around the actual
|
|
|
|
- * call to the base driver.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
netdev_dbg(bond_dev, "slave %p %s\n", slave, slave->dev->name);
|
|
netdev_dbg(bond_dev, "slave %p %s\n", slave, slave->dev->name);
|
|
res = dev_set_mac_address(slave->dev, addr);
|
|
res = dev_set_mac_address(slave->dev, addr);
|
|
@@ -3851,7 +3821,6 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev,
|
|
* the true receive or transmit bandwidth (not all modes are symmetric)
|
|
* the true receive or transmit bandwidth (not all modes are symmetric)
|
|
* this is an accurate maximum.
|
|
* this is an accurate maximum.
|
|
*/
|
|
*/
|
|
- read_lock(&bond->lock);
|
|
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
bond_for_each_slave(bond, slave, iter) {
|
|
if (bond_slave_can_tx(slave)) {
|
|
if (bond_slave_can_tx(slave)) {
|
|
if (slave->speed != SPEED_UNKNOWN)
|
|
if (slave->speed != SPEED_UNKNOWN)
|
|
@@ -3862,7 +3831,6 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ethtool_cmd_speed_set(ecmd, speed ? : SPEED_UNKNOWN);
|
|
ethtool_cmd_speed_set(ecmd, speed ? : SPEED_UNKNOWN);
|
|
- read_unlock(&bond->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -3925,7 +3893,6 @@ void bond_setup(struct net_device *bond_dev)
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
struct bonding *bond = netdev_priv(bond_dev);
|
|
|
|
|
|
/* initialize rwlocks */
|
|
/* initialize rwlocks */
|
|
- rwlock_init(&bond->lock);
|
|
|
|
rwlock_init(&bond->curr_slave_lock);
|
|
rwlock_init(&bond->curr_slave_lock);
|
|
bond->params = bonding_defaults;
|
|
bond->params = bonding_defaults;
|
|
|
|
|