Skip to content

Commit

Permalink
Merge branch 'bond_lock_removal'
Browse files Browse the repository at this point in the history
Nikolay Aleksandrov says:

====================
bonding: get rid of bond->lock

This patch-set removes the last users of bond->lock and converts the places
that needed it for sync to use curr_slave_lock or RCU as appropriate.
I've run this with lockdep and have stress-tested it via loading/unloading
and enslaving/releasing in parallel while outputting bond's proc, I didn't
see any issues. Please pay special attention to the procfs change, I've
done about an hour of stress-testing on it and have checked that the event
that causes the bonding to delete its proc entry (NETDEV_UNREGISTER) is
called before ndo_uninit() and the freeing of the dev so any readers will
sync with that. Also ran sparse checks and there were no splats.

v2: Add patch 0001/cxgb4 bond->lock removal, RTNL should be held in the
    notifier call, the other patches are the same. Also tested with
    allmodconfig to make sure there're no more users of bond->lock.
Changes from the RFC:
 use RCU in procfs instead of RTNL since RTNL might lead to a deadlock with
 unloading and also is much slower. The bond destruction syncs with proc
 via the proc locks. There's one new patch that converts primary_slave to
 use RCU as it was necessary to fix a longstanding bugs in sysfs and
 procfs and to make it easy to migrate bond's procfs to RCU. And of course
 rebased on top of net-next current.

This is the first patch-set in a series that should simplify the bond's
locking requirements and will make it easier to define the locking
conditions necessary for the various paths. The goal is to rely on RTNL
and rcu alone, an extra lock would be needed in a few special cases that
would be documented very well.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 10, 2014
2 parents c6ec956 + 87163ef commit 29c10a8
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 131 deletions.
9 changes: 4 additions & 5 deletions drivers/net/bonding/bond_3ad.c
Original file line number Diff line number Diff line change
Expand Up @@ -2057,7 +2057,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
struct port *port;
bool should_notify_rtnl = BOND_SLAVE_NOTIFY_LATER;

read_lock(&bond->lock);
read_lock(&bond->curr_slave_lock);
rcu_read_lock();

/* check if there are any slaves */
Expand Down Expand Up @@ -2120,7 +2120,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
}
}
rcu_read_unlock();
read_unlock(&bond->lock);
read_unlock(&bond->curr_slave_lock);

if (should_notify_rtnl && rtnl_trylock()) {
bond_slave_state_notify(bond);
Expand Down Expand Up @@ -2395,7 +2395,6 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond,
return 0;
}

/* Wrapper used to hold bond->lock so no slave manipulation can occur */
int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
{
int ret;
Expand Down Expand Up @@ -2487,9 +2486,9 @@ int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
if (!lacpdu)
return ret;

read_lock(&bond->lock);
read_lock(&bond->curr_slave_lock);
ret = bond_3ad_rx_indication(lacpdu, slave, skb->len);
read_unlock(&bond->lock);
read_unlock(&bond->curr_slave_lock);
return ret;
}

Expand Down
11 changes: 2 additions & 9 deletions drivers/net/bonding/bond_alb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1775,8 +1775,7 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char
* Set the bond->curr_active_slave to @new_slave and handle
* mac address swapping and promiscuity changes as needed.
*
* If new_slave is NULL, caller must hold curr_slave_lock or
* bond->lock for write.
* If new_slave is NULL, caller must hold curr_slave_lock for write
*
* If new_slave is not NULL, caller must hold RTNL, curr_slave_lock
* for write. Processing here may sleep, so no other locks may be held.
Expand Down Expand Up @@ -1857,12 +1856,8 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
write_lock_bh(&bond->curr_slave_lock);
}

/*
* Called with RTNL
*/
/* Called with RTNL */
int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
__acquires(&bond->lock)
__releases(&bond->lock)
{
struct bonding *bond = netdev_priv(bond_dev);
struct sockaddr *sa = addr;
Expand Down Expand Up @@ -1895,14 +1890,12 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
} else {
alb_set_slave_mac_addr(curr_active, bond_dev->dev_addr);

read_lock(&bond->lock);
alb_send_learning_packets(curr_active,
bond_dev->dev_addr, false);
if (bond->alb_info.rlb_enabled) {
/* inform clients mac address has changed */
rlb_req_update_slave_clients(bond, curr_active);
}
read_unlock(&bond->lock);
}

return 0;
Expand Down
93 changes: 30 additions & 63 deletions drivers/net/bonding/bond_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ static void bond_do_fail_over_mac(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);

if (!prim || !curr || curr->link != BOND_LINK_UP)
Expand All @@ -732,13 +732,14 @@ static bool bond_should_change_active(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;
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))
return bond->primary_slave;
return primary;

bond_for_each_slave(bond, slave, iter) {
if (slave->link == BOND_LINK_UP)
Expand Down Expand 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 there is a primary slave, remember it */
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;
}
}
Expand Down Expand Up @@ -1596,8 +1597,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
bond_hw_addr_flush(bond_dev, slave_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) {
block_netpoll_tx();
write_lock_bh(&bond->curr_slave_lock);
Expand All @@ -1606,6 +1607,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
write_unlock_bh(&bond->curr_slave_lock);
unblock_netpoll_tx();
}
/* either primary_slave or curr_active_slave might've changed */
synchronize_rcu();
slave_disable_netpoll(new_slave);

err_close:
Expand Down Expand Up @@ -1687,13 +1690,15 @@ static int __bond_release_one(struct net_device *bond_dev,
* for this slave anymore.
*/
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);

write_unlock_bh(&bond->lock);
write_unlock_bh(&bond->curr_slave_lock);
}

netdev_info(bond_dev, "Releasing %s interface %s\n",
bond_is_active_slave(slave) ? "active" : "backup",
Expand All @@ -1712,8 +1717,8 @@ static int __bond_release_one(struct net_device *bond_dev,
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) {
write_lock_bh(&bond->curr_slave_lock);
Expand Down Expand Up @@ -1974,7 +1979,7 @@ static int bond_miimon_inspect(struct bonding *bond)
static void bond_miimon_commit(struct bonding *bond)
{
struct list_head *iter;
struct slave *slave;
struct slave *slave, *primary;

bond_for_each_slave(bond, slave, iter) {
switch (slave->new_link) {
Expand All @@ -1985,13 +1990,14 @@ static void bond_miimon_commit(struct bonding *bond)
slave->link = BOND_LINK_UP;
slave->last_link_up = jiffies;

primary = rtnl_dereference(bond->primary_slave);
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
/* prevent it from being the active one */
bond_set_backup_slave(slave);
} else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
/* make it immediately active */
bond_set_active_slave(slave);
} else if (slave != bond->primary_slave) {
} else if (slave != primary) {
/* prevent it from being the active one */
bond_set_backup_slave(slave);
}
Expand All @@ -2009,8 +2015,7 @@ static void bond_miimon_commit(struct bonding *bond)
bond_alb_handle_link_change(bond, slave,
BOND_LINK_UP);

if (!bond->curr_active_slave ||
(slave == bond->primary_slave))
if (!bond->curr_active_slave || slave == primary)
goto do_failover;

continue;
Expand Down Expand Up @@ -2631,7 +2636,7 @@ static void bond_ab_arp_commit(struct bonding *bond)
slave->dev->name);

if (!rtnl_dereference(bond->curr_active_slave) ||
(slave == bond->primary_slave))
slave == rtnl_dereference(bond->primary_slave))
goto do_failover;

}
Expand Down Expand Up @@ -2858,7 +2863,7 @@ static int bond_master_netdev_event(unsigned long event,
static int bond_slave_netdev_event(unsigned long event,
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 net_device *bond_dev;
u32 old_speed;
Expand All @@ -2872,6 +2877,7 @@ static int bond_slave_netdev_event(unsigned long event,
return NOTIFY_DONE;
bond_dev = slave->bond->dev;
bond = slave->bond;
primary = rtnl_dereference(bond->primary_slave);

switch (event) {
case NETDEV_UNREGISTER:
Expand Down Expand Up @@ -2919,18 +2925,18 @@ static int bond_slave_netdev_event(unsigned long event,
!bond->params.primary[0])
break;

if (slave == bond->primary_slave) {
if (slave == 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)) {
/* we have a new primary slave */
bond->primary_slave = slave;
rcu_assign_pointer(bond->primary_slave, slave);
} else { /* we didn't change primary - exit */
break;
}

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();
write_lock_bh(&bond->curr_slave_lock);
Expand Down Expand Up @@ -3099,7 +3105,6 @@ static int bond_open(struct net_device *bond_dev)
struct slave *slave;

/* reset slave->backup and slave->inactive */
read_lock(&bond->lock);
if (bond_has_slaves(bond)) {
read_lock(&bond->curr_slave_lock);
bond_for_each_slave(bond, slave, iter) {
Expand All @@ -3114,7 +3119,6 @@ static int bond_open(struct net_device *bond_dev)
}
read_unlock(&bond->curr_slave_lock);
}
read_unlock(&bond->lock);

bond_work_init_all(bond);

Expand Down Expand Up @@ -3169,7 +3173,6 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,

memset(stats, 0, sizeof(*stats));

read_lock_bh(&bond->lock);
bond_for_each_slave(bond, slave, iter) {
const struct rtnl_link_stats64 *sstats =
dev_get_stats(slave->dev, &temp);
Expand Down Expand Up @@ -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_window_errors += sstats->tx_window_errors;
}
read_unlock_bh(&bond->lock);

return stats;
}
Expand Down Expand Up @@ -3240,13 +3242,11 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd

if (mii->reg_num == 1) {
mii->val_out = 0;
read_lock(&bond->lock);
read_lock(&bond->curr_slave_lock);
if (netif_carrier_ok(bond->dev))
mii->val_out = BMSR_LSTATUS;

read_unlock(&bond->curr_slave_lock);
read_unlock(&bond->lock);
}

return 0;
Expand Down Expand Up @@ -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);

/* 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) {
netdev_dbg(bond_dev, "s %p c_m %p\n",
slave, slave->dev->netdev_ops->ndo_change_mtu);
Expand Down Expand Up @@ -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))
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) {
netdev_dbg(bond_dev, "slave %p %s\n", slave, slave->dev->name);
res = dev_set_mac_address(slave->dev, addr);
Expand Down Expand Up @@ -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)
* this is an accurate maximum.
*/
read_lock(&bond->lock);
bond_for_each_slave(bond, slave, iter) {
if (bond_slave_can_tx(slave)) {
if (slave->speed != SPEED_UNKNOWN)
Expand All @@ -3862,7 +3831,6 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev,
}
}
ethtool_cmd_speed_set(ecmd, speed ? : SPEED_UNKNOWN);
read_unlock(&bond->lock);

return 0;
}
Expand Down Expand Up @@ -3925,7 +3893,6 @@ void bond_setup(struct net_device *bond_dev)
struct bonding *bond = netdev_priv(bond_dev);

/* initialize rwlocks */
rwlock_init(&bond->lock);
rwlock_init(&bond->curr_slave_lock);
bond->params = bonding_defaults;

Expand Down
7 changes: 4 additions & 3 deletions drivers/net/bonding/bond_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ static int bond_fill_info(struct sk_buff *skb,
unsigned int packets_per_slave;
int ifindex, i, targets_added;
struct nlattr *targets;
struct slave *primary;

if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond)))
goto nla_put_failure;
Expand Down Expand Up @@ -492,9 +493,9 @@ static int bond_fill_info(struct sk_buff *skb,
bond->params.arp_all_targets))
goto nla_put_failure;

if (bond->primary_slave &&
nla_put_u32(skb, IFLA_BOND_PRIMARY,
bond->primary_slave->dev->ifindex))
primary = rtnl_dereference(bond->primary_slave);
if (primary &&
nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex))
goto nla_put_failure;

if (nla_put_u8(skb, IFLA_BOND_PRIMARY_RESELECT,
Expand Down
Loading

0 comments on commit 29c10a8

Please sign in to comment.