summaryrefslogtreecommitdiff
path: root/drivers/net/bonding/bond_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/bonding/bond_main.c')
-rw-r--r--drivers/net/bonding/bond_main.c66
1 files changed, 46 insertions, 20 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 22682f1c8473..85e813c7762b 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1162,6 +1162,11 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
write_lock_bh(&bond->curr_slave_lock);
}
}
+
+ /* resend IGMP joins since all were sent on curr_active_slave */
+ if (bond->params.mode == BOND_MODE_ROUNDROBIN) {
+ bond_resend_igmp_join_requests(bond);
+ }
}
/**
@@ -4096,22 +4101,41 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave, *start_at;
int i, slave_no, res = 1;
+ struct iphdr *iph = ip_hdr(skb);
read_lock(&bond->lock);
if (!BOND_IS_OK(bond))
goto out;
-
/*
- * Concurrent TX may collide on rr_tx_counter; we accept that
- * as being rare enough not to justify using an atomic op here
+ * Start with the curr_active_slave that joined the bond as the
+ * default for sending IGMP traffic. For failover purposes one
+ * needs to maintain some consistency for the interface that will
+ * send the join/membership reports. The curr_active_slave found
+ * will send all of this type of traffic.
*/
- slave_no = bond->rr_tx_counter++ % bond->slave_cnt;
+ if ((iph->protocol == IPPROTO_IGMP) &&
+ (skb->protocol == htons(ETH_P_IP))) {
- bond_for_each_slave(bond, slave, i) {
- slave_no--;
- if (slave_no < 0)
- break;
+ read_lock(&bond->curr_slave_lock);
+ slave = bond->curr_active_slave;
+ read_unlock(&bond->curr_slave_lock);
+
+ if (!slave)
+ goto out;
+ } else {
+ /*
+ * Concurrent TX may collide on rr_tx_counter; we accept
+ * that as being rare enough not to justify using an
+ * atomic op here.
+ */
+ slave_no = bond->rr_tx_counter++ % bond->slave_cnt;
+
+ bond_for_each_slave(bond, slave, i) {
+ slave_no--;
+ if (slave_no < 0)
+ break;
+ }
}
start_at = slave;
@@ -4384,6 +4408,14 @@ static const struct net_device_ops bond_netdev_ops = {
.ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid,
};
+static void bond_destructor(struct net_device *bond_dev)
+{
+ struct bonding *bond = netdev_priv(bond_dev);
+ if (bond->wq)
+ destroy_workqueue(bond->wq);
+ free_netdev(bond_dev);
+}
+
static void bond_setup(struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
@@ -4404,7 +4436,7 @@ static void bond_setup(struct net_device *bond_dev)
bond_dev->ethtool_ops = &bond_ethtool_ops;
bond_set_mode_ops(bond, bond->params.mode);
- bond_dev->destructor = free_netdev;
+ bond_dev->destructor = bond_destructor;
/* Initialize the device options */
bond_dev->tx_queue_len = 0;
@@ -4476,9 +4508,6 @@ static void bond_uninit(struct net_device *bond_dev)
bond_remove_proc_entry(bond);
- if (bond->wq)
- destroy_workqueue(bond->wq);
-
__hw_addr_flush(&bond->mc_list);
}
@@ -4890,8 +4919,8 @@ int bond_create(struct net *net, const char *name)
bond_setup);
if (!bond_dev) {
pr_err("%s: eek! can't alloc netdev!\n", name);
- res = -ENOMEM;
- goto out;
+ rtnl_unlock();
+ return -ENOMEM;
}
dev_net_set(bond_dev, net);
@@ -4900,19 +4929,16 @@ int bond_create(struct net *net, const char *name)
if (!name) {
res = dev_alloc_name(bond_dev, "bond%d");
if (res < 0)
- goto out_netdev;
+ goto out;
}
res = register_netdevice(bond_dev);
- if (res < 0)
- goto out_netdev;
out:
rtnl_unlock();
+ if (res < 0)
+ bond_destructor(bond_dev);
return res;
-out_netdev:
- free_netdev(bond_dev);
- goto out;
}
static int __net_init bond_net_init(struct net *net)