summaryrefslogtreecommitdiff
path: root/drivers/net/bonding
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/bonding')
-rw-r--r--drivers/net/bonding/bond_alb.c99
-rw-r--r--drivers/net/bonding/bond_main.c290
-rw-r--r--drivers/net/bonding/bond_sysfs.c133
-rw-r--r--drivers/net/bonding/bonding.h49
4 files changed, 271 insertions, 300 deletions
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index e02cc265723a..4ea8ed150d46 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -1056,7 +1056,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])
*
*/
-static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct slave *slave2)
+static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2)
{
u8 tmp_mac_addr[ETH_ALEN];
@@ -1129,6 +1129,7 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla
{
int perm_curr_diff;
int perm_bond_diff;
+ struct slave *found_slave;
perm_curr_diff = !ether_addr_equal_64bits(slave->perm_hwaddr,
slave->dev->dev_addr);
@@ -1136,21 +1137,12 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla
bond->dev->dev_addr);
if (perm_curr_diff && perm_bond_diff) {
- struct slave *tmp_slave;
- int i, found = 0;
-
- bond_for_each_slave(bond, tmp_slave, i) {
- if (ether_addr_equal_64bits(slave->perm_hwaddr,
- tmp_slave->dev->dev_addr)) {
- found = 1;
- break;
- }
- }
+ found_slave = bond_slave_has_mac(bond, slave->perm_hwaddr);
- if (found) {
+ if (found_slave) {
/* locking: needs RTNL and nothing else */
- alb_swap_mac_addr(bond, slave, tmp_slave);
- alb_fasten_mac_swap(bond, slave, tmp_slave);
+ alb_swap_mac_addr(slave, found_slave);
+ alb_fasten_mac_swap(bond, slave, found_slave);
}
}
}
@@ -1175,16 +1167,13 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla
* @slave.
*
* assumption: this function is called before @slave is attached to the
- * bond slave list.
- *
- * caller must hold the bond lock for write since the mac addresses are compared
- * and may be swapped.
+ * bond slave list.
*/
static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slave *slave)
{
- struct slave *tmp_slave1, *tmp_slave2, *free_mac_slave;
+ struct slave *tmp_slave1, *free_mac_slave = NULL;
struct slave *has_bond_addr = bond->curr_active_slave;
- int i, j, found = 0;
+ int i;
if (bond->slave_cnt == 0) {
/* this is the first slave */
@@ -1196,15 +1185,7 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav
* slaves in the bond.
*/
if (!ether_addr_equal_64bits(slave->perm_hwaddr, bond->dev->dev_addr)) {
- bond_for_each_slave(bond, tmp_slave1, i) {
- if (ether_addr_equal_64bits(tmp_slave1->dev->dev_addr,
- slave->dev->dev_addr)) {
- found = 1;
- break;
- }
- }
-
- if (!found)
+ if (!bond_slave_has_mac(bond, slave->dev->dev_addr))
return 0;
/* Try setting slave mac to bond address and fall-through
@@ -1215,19 +1196,8 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav
/* The slave's address is equal to the address of the bond.
* Search for a spare address in the bond for this slave.
*/
- free_mac_slave = NULL;
-
bond_for_each_slave(bond, tmp_slave1, i) {
- found = 0;
- bond_for_each_slave(bond, tmp_slave2, j) {
- if (ether_addr_equal_64bits(tmp_slave1->perm_hwaddr,
- tmp_slave2->dev->dev_addr)) {
- found = 1;
- break;
- }
- }
-
- if (!found) {
+ if (!bond_slave_has_mac(bond, tmp_slave1->perm_hwaddr)) {
/* no slave has tmp_slave1's perm addr
* as its curr addr
*/
@@ -1607,15 +1577,7 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave)
return res;
}
- /* caller must hold the bond lock for write since the mac addresses
- * are compared and may be swapped.
- */
- read_lock(&bond->lock);
-
res = alb_handle_addr_collision_on_attach(bond, slave);
-
- read_unlock(&bond->lock);
-
if (res) {
return res;
}
@@ -1698,7 +1660,6 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
__acquires(&bond->curr_slave_lock)
{
struct slave *swap_slave;
- int i;
if (bond->curr_active_slave == new_slave) {
return;
@@ -1720,17 +1681,8 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
/* set the new curr_active_slave to the bonds mac address
* i.e. swap mac addresses of old curr_active_slave and new curr_active_slave
*/
- if (!swap_slave) {
- struct slave *tmp_slave;
- /* find slave that is holding the bond's mac address */
- bond_for_each_slave(bond, tmp_slave, i) {
- if (ether_addr_equal_64bits(tmp_slave->dev->dev_addr,
- bond->dev->dev_addr)) {
- swap_slave = tmp_slave;
- break;
- }
- }
- }
+ if (!swap_slave)
+ swap_slave = bond_slave_has_mac(bond, bond->dev->dev_addr);
/*
* Arrange for swap_slave and new_slave to temporarily be
@@ -1750,16 +1702,12 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
/* curr_active_slave must be set before calling alb_swap_mac_addr */
if (swap_slave) {
/* swap mac address */
- alb_swap_mac_addr(bond, swap_slave, new_slave);
- } else {
- /* set the new_slave to the bond mac address */
- alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr);
- }
-
- if (swap_slave) {
+ alb_swap_mac_addr(swap_slave, new_slave);
alb_fasten_mac_swap(bond, swap_slave, new_slave);
read_lock(&bond->lock);
} else {
+ /* set the new_slave to the bond mac address */
+ alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr);
read_lock(&bond->lock);
alb_send_learning_packets(new_slave, bond->dev->dev_addr);
}
@@ -1776,9 +1724,8 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
{
struct bonding *bond = netdev_priv(bond_dev);
struct sockaddr *sa = addr;
- struct slave *slave, *swap_slave;
+ struct slave *swap_slave;
int res;
- int i;
if (!is_valid_ether_addr(sa->sa_data)) {
return -EADDRNOTAVAIL;
@@ -1799,18 +1746,10 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
return 0;
}
- swap_slave = NULL;
-
- bond_for_each_slave(bond, slave, i) {
- if (ether_addr_equal_64bits(slave->dev->dev_addr,
- bond_dev->dev_addr)) {
- swap_slave = slave;
- break;
- }
- }
+ swap_slave = bond_slave_has_mac(bond, bond_dev->dev_addr);
if (swap_slave) {
- alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave);
+ alb_swap_mac_addr(swap_slave, bond->curr_active_slave);
alb_fasten_mac_swap(bond, swap_slave, bond->curr_active_slave);
} else {
alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr);
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index f97569613526..07f257d44a1e 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -104,6 +104,7 @@ static char *xmit_hash_policy;
static int arp_interval = BOND_LINK_ARP_INTERV;
static char *arp_ip_target[BOND_MAX_ARP_TARGETS];
static char *arp_validate;
+static char *arp_all_targets;
static char *fail_over_mac;
static int all_slaves_active = 0;
static struct bond_params bonding_defaults;
@@ -166,6 +167,8 @@ module_param(arp_validate, charp, 0);
MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes; "
"0 for none (default), 1 for active, "
"2 for backup, 3 for all");
+module_param(arp_all_targets, charp, 0);
+MODULE_PARM_DESC(arp_all_targets, "fail on any/all arp targets timeout; 0 for any (default), 1 for all");
module_param(fail_over_mac, charp, 0);
MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to "
"the same MAC; 0 for none (default), "
@@ -216,6 +219,12 @@ const struct bond_parm_tbl xmit_hashtype_tbl[] = {
{ NULL, -1},
};
+const struct bond_parm_tbl arp_all_targets_tbl[] = {
+{ "any", BOND_ARP_TARGETS_ANY},
+{ "all", BOND_ARP_TARGETS_ALL},
+{ NULL, -1},
+};
+
const struct bond_parm_tbl arp_validate_tbl[] = {
{ "none", BOND_ARP_VALIDATE_NONE},
{ "active", BOND_ARP_VALIDATE_ACTIVE},
@@ -706,45 +715,6 @@ static int bond_set_allmulti(struct bonding *bond, int inc)
return err;
}
-/*
- * Add a Multicast address to slaves
- * according to mode
- */
-static void bond_mc_add(struct bonding *bond, void *addr)
-{
- if (USES_PRIMARY(bond->params.mode)) {
- /* write lock already acquired */
- if (bond->curr_active_slave)
- dev_mc_add(bond->curr_active_slave->dev, addr);
- } else {
- struct slave *slave;
- int i;
-
- bond_for_each_slave(bond, slave, i)
- dev_mc_add(slave->dev, addr);
- }
-}
-
-/*
- * Remove a multicast address from slave
- * according to mode
- */
-static void bond_mc_del(struct bonding *bond, void *addr)
-{
- if (USES_PRIMARY(bond->params.mode)) {
- /* write lock already acquired */
- if (bond->curr_active_slave)
- dev_mc_del(bond->curr_active_slave->dev, addr);
- } else {
- struct slave *slave;
- int i;
- bond_for_each_slave(bond, slave, i) {
- dev_mc_del(slave->dev, addr);
- }
- }
-}
-
-
static void __bond_resend_igmp_join_requests(struct net_device *dev)
{
struct in_device *in_dev;
@@ -810,17 +780,15 @@ static void bond_resend_igmp_join_requests_delayed(struct work_struct *work)
bond_resend_igmp_join_requests(bond);
}
-/*
- * flush all members of flush->mc_list from device dev->mc_list
+/* Flush bond's hardware addresses from slave
*/
-static void bond_mc_list_flush(struct net_device *bond_dev,
+static void bond_hw_addr_flush(struct net_device *bond_dev,
struct net_device *slave_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
- struct netdev_hw_addr *ha;
- netdev_for_each_mc_addr(ha, bond_dev)
- dev_mc_del(slave_dev, ha->addr);
+ dev_uc_unsync(slave_dev, bond_dev);
+ dev_mc_unsync(slave_dev, bond_dev);
if (bond->params.mode == BOND_MODE_8023AD) {
/* del lacpdu mc addr from mc list */
@@ -832,22 +800,14 @@ static void bond_mc_list_flush(struct net_device *bond_dev,
/*--------------------------- Active slave change ---------------------------*/
-/*
- * Update the mc list and multicast-related flags for the new and
- * old active slaves (if any) according to the multicast mode, and
- * promiscuous flags unconditionally.
+/* Update the hardware address list and promisc/allmulti for the new and
+ * old active slaves (if any). Modes that are !USES_PRIMARY keep all
+ * slaves up date at all times; only the USES_PRIMARY modes need to call
+ * this function to swap these settings during a failover.
*/
-static void bond_mc_swap(struct bonding *bond, struct slave *new_active,
- struct slave *old_active)
+static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active,
+ struct slave *old_active)
{
- struct netdev_hw_addr *ha;
-
- if (!USES_PRIMARY(bond->params.mode))
- /* nothing to do - mc list is already up-to-date on
- * all slaves
- */
- return;
-
if (old_active) {
if (bond->dev->flags & IFF_PROMISC)
dev_set_promiscuity(old_active->dev, -1);
@@ -855,10 +815,7 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active,
if (bond->dev->flags & IFF_ALLMULTI)
dev_set_allmulti(old_active->dev, -1);
- netif_addr_lock_bh(bond->dev);
- netdev_for_each_mc_addr(ha, bond->dev)
- dev_mc_del(old_active->dev, ha->addr);
- netif_addr_unlock_bh(bond->dev);
+ bond_hw_addr_flush(bond->dev, old_active->dev);
}
if (new_active) {
@@ -870,12 +827,29 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active,
dev_set_allmulti(new_active->dev, 1);
netif_addr_lock_bh(bond->dev);
- netdev_for_each_mc_addr(ha, bond->dev)
- dev_mc_add(new_active->dev, ha->addr);
+ dev_uc_sync(new_active->dev, bond->dev);
+ dev_mc_sync(new_active->dev, bond->dev);
netif_addr_unlock_bh(bond->dev);
}
}
+/**
+ * bond_set_dev_addr - clone slave's address to bond
+ * @bond_dev: bond net device
+ * @slave_dev: slave net device
+ *
+ * Should be called with RTNL held.
+ */
+static void bond_set_dev_addr(struct net_device *bond_dev,
+ struct net_device *slave_dev)
+{
+ pr_debug("bond_dev=%p slave_dev=%p slave_dev->addr_len=%d\n",
+ bond_dev, slave_dev, slave_dev->addr_len);
+ memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
+ bond_dev->addr_assign_type = NET_ADDR_STOLEN;
+ call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev);
+}
+
/*
* bond_do_fail_over_mac
*
@@ -898,11 +872,9 @@ static void bond_do_fail_over_mac(struct bonding *bond,
switch (bond->params.fail_over_mac) {
case BOND_FOM_ACTIVE:
if (new_active) {
- memcpy(bond->dev->dev_addr, new_active->dev->dev_addr,
- new_active->dev->addr_len);
write_unlock_bh(&bond->curr_slave_lock);
read_unlock(&bond->lock);
- call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev);
+ bond_set_dev_addr(bond->dev, new_active->dev);
read_lock(&bond->lock);
write_lock_bh(&bond->curr_slave_lock);
}
@@ -1090,7 +1062,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
}
if (USES_PRIMARY(bond->params.mode))
- bond_mc_swap(bond, new_active, old_active);
+ bond_hw_addr_swap(bond, new_active, old_active);
if (bond_is_lb(bond)) {
bond_alb_handle_active_change(bond, new_active);
@@ -1333,17 +1305,6 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
/*---------------------------------- IOCTL ----------------------------------*/
-static void bond_set_dev_addr(struct net_device *bond_dev,
- struct net_device *slave_dev)
-{
- pr_debug("bond_dev=%p\n", bond_dev);
- pr_debug("slave_dev=%p\n", slave_dev);
- pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len);
- memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
- bond_dev->addr_assign_type = NET_ADDR_SET;
- call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev);
-}
-
static netdev_features_t bond_fix_features(struct net_device *dev,
netdev_features_t features)
{
@@ -1425,8 +1386,6 @@ done:
static void bond_setup_by_slave(struct net_device *bond_dev,
struct net_device *slave_dev)
{
- struct bonding *bond = netdev_priv(bond_dev);
-
bond_dev->header_ops = slave_dev->header_ops;
bond_dev->type = slave_dev->type;
@@ -1435,7 +1394,6 @@ static void bond_setup_by_slave(struct net_device *bond_dev,
memcpy(bond_dev->broadcast, slave_dev->broadcast,
slave_dev->addr_len);
- bond->setup_by_slave = 1;
}
/* On bonding slaves other than the currently active slave, suppress
@@ -1533,10 +1491,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
struct bonding *bond = netdev_priv(bond_dev);
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
struct slave *new_slave = NULL;
- struct netdev_hw_addr *ha;
struct sockaddr addr;
int link_reporting;
- int res = 0;
+ int res = 0, i;
if (!bond->params.use_carrier &&
slave_dev->ethtool_ops->get_link == NULL &&
@@ -1643,7 +1600,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
/* If this is the first slave, then we need to set the master's hardware
* address to be the same as the slave's. */
- if (bond->slave_cnt == 0 && bond->dev_addr_from_first)
+ if (!bond->slave_cnt && bond->dev->addr_assign_type == NET_ADDR_RANDOM)
bond_set_dev_addr(bond->dev, slave_dev);
new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
@@ -1713,10 +1670,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
goto err_close;
}
- /* If the mode USES_PRIMARY, then the new slave gets the
- * master's promisc (and mc) settings only if it becomes the
- * curr_active_slave, and that is taken care of later when calling
- * bond_change_active()
+ /* If the mode USES_PRIMARY, then the following is handled by
+ * bond_change_active_slave().
*/
if (!USES_PRIMARY(bond->params.mode)) {
/* set promiscuity level to new slave */
@@ -1734,9 +1689,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
}
netif_addr_lock_bh(bond_dev);
- /* upload master's mc_list to new slave */
- netdev_for_each_mc_addr(ha, bond_dev)
- dev_mc_add(slave_dev, ha->addr);
+
+ dev_mc_sync_multiple(slave_dev, bond_dev);
+ dev_uc_sync_multiple(slave_dev, bond_dev);
+
netif_addr_unlock_bh(bond_dev);
}
@@ -1766,6 +1722,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
new_slave->last_arp_rx = jiffies -
(msecs_to_jiffies(bond->params.arp_interval) + 1);
+ for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
+ new_slave->target_last_arp_rx[i] = new_slave->last_arp_rx;
if (bond->params.miimon && !bond->params.use_carrier) {
link_reporting = bond_check_dev_link(bond, slave_dev, 1);
@@ -1915,11 +1873,9 @@ err_dest_symlinks:
bond_destroy_slave_symlinks(bond_dev, slave_dev);
err_detach:
- if (!USES_PRIMARY(bond->params.mode)) {
- netif_addr_lock_bh(bond_dev);
- bond_mc_list_flush(bond_dev, slave_dev);
- netif_addr_unlock_bh(bond_dev);
- }
+ if (!USES_PRIMARY(bond->params.mode))
+ bond_hw_addr_flush(bond_dev, slave_dev);
+
bond_del_vlans_from_slave(bond, slave_dev);
write_lock_bh(&bond->lock);
bond_detach_slave(bond, new_slave);
@@ -2089,7 +2045,6 @@ static int __bond_release_one(struct net_device *bond_dev,
if (bond->slave_cnt == 0) {
bond_set_carrier(bond);
eth_hw_addr_random(bond_dev);
- bond->dev_addr_from_first = true;
if (bond_vlan_used(bond)) {
pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
@@ -2118,9 +2073,8 @@ static int __bond_release_one(struct net_device *bond_dev,
bond_del_vlans_from_slave(bond, slave_dev);
- /* If the mode USES_PRIMARY, then we should only remove its
- * promisc and mc settings if it was the curr_active_slave, but that was
- * already taken care of above when we detached the slave
+ /* If the mode USES_PRIMARY, then this cases was handled above by
+ * bond_change_active_slave(..., NULL)
*/
if (!USES_PRIMARY(bond->params.mode)) {
/* unset promiscuity level from slave */
@@ -2131,10 +2085,7 @@ static int __bond_release_one(struct net_device *bond_dev,
if (bond_dev->flags & IFF_ALLMULTI)
dev_set_allmulti(slave_dev, -1);
- /* flush master's mc_list from slave */
- netif_addr_lock_bh(bond_dev);
- bond_mc_list_flush(bond_dev, slave_dev);
- netif_addr_unlock_bh(bond_dev);
+ bond_hw_addr_flush(bond_dev, slave_dev);
}
bond_upper_dev_unlink(bond_dev, slave_dev);
@@ -2672,18 +2623,19 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 sip, __be32 tip)
{
int i;
- __be32 *targets = bond->params.arp_targets;
- for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) {
- pr_debug("bva: sip %pI4 tip %pI4 t[%d] %pI4 bhti(tip) %d\n",
- &sip, &tip, i, &targets[i],
- bond_has_this_ip(bond, tip));
- if (sip == targets[i]) {
- if (bond_has_this_ip(bond, tip))
- slave->last_arp_rx = jiffies;
- return;
- }
+ if (!sip || !bond_has_this_ip(bond, tip)) {
+ pr_debug("bva: sip %pI4 tip %pI4 not found\n", &sip, &tip);
+ return;
+ }
+
+ i = bond_get_targets_ip(bond->params.arp_targets, sip);
+ if (i == -1) {
+ pr_debug("bva: sip %pI4 not found in targets\n", &sip);
+ return;
}
+ slave->last_arp_rx = jiffies;
+ slave->target_last_arp_rx[i] = jiffies;
}
static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -2698,6 +2650,10 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
return RX_HANDLER_ANOTHER;
read_lock(&bond->lock);
+
+ if (!slave_do_arp_validate(bond, slave))
+ goto out_unlock;
+
alen = arp_hdr_len(bond->dev);
pr_debug("bond_arp_rcv: bond %s skb->dev %s\n",
@@ -2737,10 +2693,17 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
* configuration, the ARP probe will (hopefully) travel from
* the active, through one switch, the router, then the other
* switch before reaching the backup.
+ *
+ * We 'trust' the arp requests if there is an active slave and
+ * it received valid arp reply(s) after it became active. This
+ * is done to avoid endless looping when we can't reach the
+ * arp_ip_target and fool ourselves with our own arp requests.
*/
if (bond_is_active_slave(slave))
bond_validate_arp(bond, slave, sip, tip);
- else
+ else if (bond->curr_active_slave &&
+ time_after(slave_last_rx(bond, bond->curr_active_slave),
+ bond->curr_active_slave->jiffies))
bond_validate_arp(bond, slave, tip, sip);
out_unlock:
@@ -3225,7 +3188,7 @@ static int bond_slave_netdev_event(unsigned long event,
switch (event) {
case NETDEV_UNREGISTER:
- if (bond->setup_by_slave)
+ if (bond_dev->type != ARPHRD_ETHER)
bond_release_and_destroy(bond_dev, slave_dev);
else
bond_release(bond_dev, slave_dev);
@@ -3289,7 +3252,7 @@ static int bond_slave_netdev_event(unsigned long event,
static int bond_netdev_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
- struct net_device *event_dev = (struct net_device *)ptr;
+ struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
pr_debug("event_dev: %s, event: %lx\n",
event_dev ? event_dev->name : "None",
@@ -3672,19 +3635,6 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
return res;
}
-static bool bond_addr_in_mc_list(unsigned char *addr,
- struct netdev_hw_addr_list *list,
- int addrlen)
-{
- struct netdev_hw_addr *ha;
-
- netdev_hw_addr_list_for_each(ha, list)
- if (!memcmp(ha->addr, addr, addrlen))
- return true;
-
- return false;
-}
-
static void bond_change_rx_flags(struct net_device *bond_dev, int change)
{
struct bonding *bond = netdev_priv(bond_dev);
@@ -3698,35 +3648,29 @@ static void bond_change_rx_flags(struct net_device *bond_dev, int change)
bond_dev->flags & IFF_ALLMULTI ? 1 : -1);
}
-static void bond_set_multicast_list(struct net_device *bond_dev)
+static void bond_set_rx_mode(struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
- struct netdev_hw_addr *ha;
- bool found;
+ struct slave *slave;
+ int i;
read_lock(&bond->lock);
- /* looking for addresses to add to slaves' mc list */
- netdev_for_each_mc_addr(ha, bond_dev) {
- found = bond_addr_in_mc_list(ha->addr, &bond->mc_list,
- bond_dev->addr_len);
- if (!found)
- bond_mc_add(bond, ha->addr);
- }
-
- /* looking for addresses to delete from slaves' list */
- netdev_hw_addr_list_for_each(ha, &bond->mc_list) {
- found = bond_addr_in_mc_list(ha->addr, &bond_dev->mc,
- bond_dev->addr_len);
- if (!found)
- bond_mc_del(bond, ha->addr);
+ if (USES_PRIMARY(bond->params.mode)) {
+ read_lock(&bond->curr_slave_lock);
+ slave = bond->curr_active_slave;
+ if (slave) {
+ dev_uc_sync(slave->dev, bond_dev);
+ dev_mc_sync(slave->dev, bond_dev);
+ }
+ read_unlock(&bond->curr_slave_lock);
+ } else {
+ bond_for_each_slave(bond, slave, i) {
+ dev_uc_sync_multiple(slave->dev, bond_dev);
+ dev_mc_sync_multiple(slave->dev, bond_dev);
+ }
}
- /* save master's multicast list */
- __hw_addr_flush(&bond->mc_list);
- __hw_addr_add_multiple(&bond->mc_list, &bond_dev->mc,
- bond_dev->addr_len, NETDEV_HW_ADDR_T_MULTICAST);
-
read_unlock(&bond->lock);
}
@@ -3871,11 +3815,10 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
pr_debug("bond=%p, name=%s\n",
bond, bond_dev ? bond_dev->name : "None");
- /*
- * If fail_over_mac is set to active, do nothing and return
- * success. Returning an error causes ifenslave to fail.
+ /* If fail_over_mac is enabled, do nothing and return success.
+ * Returning an error causes ifenslave to fail.
*/
- if (bond->params.fail_over_mac == BOND_FOM_ACTIVE)
+ if (bond->params.fail_over_mac)
return 0;
if (!is_valid_ether_addr(sa->sa_data))
@@ -4333,7 +4276,7 @@ static const struct net_device_ops bond_netdev_ops = {
.ndo_get_stats64 = bond_get_stats,
.ndo_do_ioctl = bond_do_ioctl,
.ndo_change_rx_flags = bond_change_rx_flags,
- .ndo_set_rx_mode = bond_set_multicast_list,
+ .ndo_set_rx_mode = bond_set_rx_mode,
.ndo_change_mtu = bond_change_mtu,
.ndo_set_mac_address = bond_set_mac_address,
.ndo_neigh_setup = bond_neigh_setup,
@@ -4438,8 +4381,6 @@ static void bond_uninit(struct net_device *bond_dev)
bond_debug_unregister(bond);
- __hw_addr_flush(&bond->mc_list);
-
list_for_each_entry_safe(vlan, tmp, &bond->vlan_list, vlan_list) {
list_del(&vlan->vlan_list);
kfree(vlan);
@@ -4484,6 +4425,7 @@ int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl)
static int bond_check_params(struct bond_params *params)
{
int arp_validate_value, fail_over_mac_value, primary_reselect_value, i;
+ int arp_all_targets_value;
/*
* Convert string parameters.
@@ -4674,7 +4616,11 @@ static int bond_check_params(struct bond_params *params)
arp_ip_target[i]);
arp_interval = 0;
} else {
- arp_target[arp_ip_count++] = ip;
+ if (bond_get_targets_ip(arp_target, ip) == -1)
+ arp_target[arp_ip_count++] = ip;
+ else
+ pr_warning("Warning: duplicate address %pI4 in arp_ip_target, skipping\n",
+ &ip);
}
}
@@ -4705,6 +4651,18 @@ static int bond_check_params(struct bond_params *params)
} else
arp_validate_value = 0;
+ arp_all_targets_value = 0;
+ if (arp_all_targets) {
+ arp_all_targets_value = bond_parse_parm(arp_all_targets,
+ arp_all_targets_tbl);
+
+ if (arp_all_targets_value == -1) {
+ pr_err("Error: invalid arp_all_targets_value \"%s\"\n",
+ arp_all_targets);
+ arp_all_targets_value = 0;
+ }
+ }
+
if (miimon) {
pr_info("MII link monitoring set to %d ms\n", miimon);
} else if (arp_interval) {
@@ -4769,6 +4727,7 @@ static int bond_check_params(struct bond_params *params)
params->num_peer_notif = num_peer_notif;
params->arp_interval = arp_interval;
params->arp_validate = arp_validate_value;
+ params->arp_all_targets = arp_all_targets_value;
params->updelay = updelay;
params->downdelay = downdelay;
params->use_carrier = use_carrier;
@@ -4845,12 +4804,9 @@ static int bond_init(struct net_device *bond_dev)
/* Ensure valid dev_addr */
if (is_zero_ether_addr(bond_dev->dev_addr) &&
- bond_dev->addr_assign_type == NET_ADDR_PERM) {
+ bond_dev->addr_assign_type == NET_ADDR_PERM)
eth_hw_addr_random(bond_dev);
- bond->dev_addr_from_first = true;
- }
- __hw_addr_init(&bond->mc_list);
return 0;
}
@@ -4923,7 +4879,7 @@ static int __net_init bond_net_init(struct net *net)
bond_create_proc_dir(bn);
bond_create_sysfs(bn);
-
+
return 0;
}
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index d7434e0a610e..dc36a3d7d9e9 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -231,8 +231,7 @@ static ssize_t bonding_show_slaves(struct device *d,
}
/*
- * Set the slaves in the current bond. The bond interface must be
- * up for this to succeed.
+ * Set the slaves in the current bond.
* This is supposed to be only thin wrapper for bond_enslave and bond_release.
* All hard work should be done there.
*/
@@ -363,7 +362,6 @@ static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
/*
* Show and set the bonding transmit hash method.
- * The bond interface must be down to change the xmit hash policy.
*/
static ssize_t bonding_show_xmit_hash(struct device *d,
struct device_attribute *attr,
@@ -383,20 +381,12 @@ static ssize_t bonding_store_xmit_hash(struct device *d,
int new_value, ret = count;
struct bonding *bond = to_bond(d);
- if (bond->dev->flags & IFF_UP) {
- pr_err("%s: Interface is up. Unable to update xmit policy.\n",
- bond->dev->name);
- ret = -EPERM;
- goto out;
- }
-
new_value = bond_parse_parm(buf, xmit_hashtype_tbl);
if (new_value < 0) {
pr_err("%s: Ignoring invalid xmit hash policy value %.*s.\n",
bond->dev->name,
(int)strlen(buf) - 1, buf);
ret = -EINVAL;
- goto out;
} else {
bond->params.xmit_policy = new_value;
bond_set_mode_ops(bond, bond->params.mode);
@@ -404,7 +394,7 @@ static ssize_t bonding_store_xmit_hash(struct device *d,
bond->dev->name,
xmit_hashtype_tbl[new_value].modename, new_value);
}
-out:
+
return ret;
}
static DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR,
@@ -453,6 +443,44 @@ static ssize_t bonding_store_arp_validate(struct device *d,
static DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate,
bonding_store_arp_validate);
+/*
+ * Show and set arp_all_targets.
+ */
+static ssize_t bonding_show_arp_all_targets(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bonding *bond = to_bond(d);
+ int value = bond->params.arp_all_targets;
+
+ return sprintf(buf, "%s %d\n", arp_all_targets_tbl[value].modename,
+ value);
+}
+
+static ssize_t bonding_store_arp_all_targets(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bonding *bond = to_bond(d);
+ int new_value;
+
+ new_value = bond_parse_parm(buf, arp_all_targets_tbl);
+ if (new_value < 0) {
+ pr_err("%s: Ignoring invalid arp_all_targets value %s\n",
+ bond->dev->name, buf);
+ return -EINVAL;
+ }
+ pr_info("%s: setting arp_all_targets to %s (%d).\n",
+ bond->dev->name, arp_all_targets_tbl[new_value].modename,
+ new_value);
+
+ bond->params.arp_all_targets = new_value;
+
+ return count;
+}
+
+static DEVICE_ATTR(arp_all_targets, S_IRUGO | S_IWUSR,
+ bonding_show_arp_all_targets, bonding_store_arp_all_targets);
/*
* Show and store fail_over_mac. User only allowed to change the
@@ -600,10 +628,11 @@ static ssize_t bonding_store_arp_targets(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
- __be32 newtarget;
- int i = 0, done = 0, ret = count;
struct bonding *bond = to_bond(d);
- __be32 *targets;
+ struct slave *slave;
+ __be32 newtarget, *targets;
+ unsigned long *targets_rx;
+ int ind, i, j, ret = -EINVAL;
targets = bond->params.arp_targets;
newtarget = in_aton(buf + 1);
@@ -612,57 +641,63 @@ static ssize_t bonding_store_arp_targets(struct device *d,
if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) {
pr_err("%s: invalid ARP target %pI4 specified for addition\n",
bond->dev->name, &newtarget);
- ret = -EINVAL;
goto out;
}
- /* look for an empty slot to put the target in, and check for dupes */
- for (i = 0; (i < BOND_MAX_ARP_TARGETS) && !done; i++) {
- if (targets[i] == newtarget) { /* duplicate */
- pr_err("%s: ARP target %pI4 is already present\n",
- bond->dev->name, &newtarget);
- ret = -EINVAL;
- goto out;
- }
- if (targets[i] == 0) {
- pr_info("%s: adding ARP target %pI4.\n",
- bond->dev->name, &newtarget);
- done = 1;
- targets[i] = newtarget;
- }
+
+ if (bond_get_targets_ip(targets, newtarget) != -1) { /* dup */
+ pr_err("%s: ARP target %pI4 is already present\n",
+ bond->dev->name, &newtarget);
+ goto out;
}
- if (!done) {
+
+ ind = bond_get_targets_ip(targets, 0); /* first free slot */
+ if (ind == -1) {
pr_err("%s: ARP target table is full!\n",
bond->dev->name);
- ret = -EINVAL;
goto out;
}
+ pr_info("%s: adding ARP target %pI4.\n", bond->dev->name,
+ &newtarget);
+ /* not to race with bond_arp_rcv */
+ write_lock_bh(&bond->lock);
+ bond_for_each_slave(bond, slave, i)
+ slave->target_last_arp_rx[ind] = jiffies;
+ targets[ind] = newtarget;
+ write_unlock_bh(&bond->lock);
} else if (buf[0] == '-') {
if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) {
pr_err("%s: invalid ARP target %pI4 specified for removal\n",
bond->dev->name, &newtarget);
- ret = -EINVAL;
goto out;
}
- for (i = 0; (i < BOND_MAX_ARP_TARGETS) && !done; i++) {
- if (targets[i] == newtarget) {
- int j;
- pr_info("%s: removing ARP target %pI4.\n",
- bond->dev->name, &newtarget);
- for (j = i; (j < (BOND_MAX_ARP_TARGETS-1)) && targets[j+1]; j++)
- targets[j] = targets[j+1];
-
- targets[j] = 0;
- done = 1;
- }
- }
- if (!done) {
- pr_info("%s: unable to remove nonexistent ARP target %pI4.\n",
+ ind = bond_get_targets_ip(targets, newtarget);
+ if (ind == -1) {
+ pr_err("%s: unable to remove nonexistent ARP target %pI4.\n",
bond->dev->name, &newtarget);
- ret = -EINVAL;
goto out;
}
+
+ if (ind == 0 && !targets[1] && bond->params.arp_interval)
+ pr_warn("%s: removing last arp target with arp_interval on\n",
+ bond->dev->name);
+
+ pr_info("%s: removing ARP target %pI4.\n", bond->dev->name,
+ &newtarget);
+
+ write_lock_bh(&bond->lock);
+ bond_for_each_slave(bond, slave, i) {
+ targets_rx = slave->target_last_arp_rx;
+ j = ind;
+ for (; (j < BOND_MAX_ARP_TARGETS-1) && targets[j+1]; j++)
+ targets_rx[j] = targets_rx[j+1];
+ targets_rx[j] = 0;
+ }
+ for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
+ targets[i] = targets[i+1];
+ targets[i] = 0;
+ write_unlock_bh(&bond->lock);
} else {
pr_err("no command found in arp_ip_targets file for bond %s. Use +<addr> or -<addr>.\n",
bond->dev->name);
@@ -670,6 +705,7 @@ static ssize_t bonding_store_arp_targets(struct device *d,
goto out;
}
+ ret = count;
out:
return ret;
}
@@ -1645,6 +1681,7 @@ static struct attribute *per_bond_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_fail_over_mac.attr,
&dev_attr_arp_validate.attr,
+ &dev_attr_arp_all_targets.attr,
&dev_attr_arp_interval.attr,
&dev_attr_arp_ip_target.attr,
&dev_attr_downdelay.attr,
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index f989e1529a29..42d1c6599cba 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -144,6 +144,7 @@ struct bond_params {
u8 num_peer_notif;
int arp_interval;
int arp_validate;
+ int arp_all_targets;
int use_carrier;
int fail_over_mac;
int updelay;
@@ -179,6 +180,7 @@ struct slave {
int delay;
unsigned long jiffies;
unsigned long last_arp_rx;
+ unsigned long target_last_arp_rx[BOND_MAX_ARP_TARGETS];
s8 link; /* one of BOND_LINK_XXXX */
s8 new_link;
u8 backup:1, /* indicates backup slave. Value corresponds with
@@ -224,14 +226,12 @@ struct bonding {
rwlock_t lock;
rwlock_t curr_slave_lock;
u8 send_peer_notif;
- s8 setup_by_slave;
u8 igmp_retrans;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_entry;
char proc_file_name[IFNAMSIZ];
#endif /* CONFIG_PROC_FS */
struct list_head bond_list;
- struct netdev_hw_addr_list mc_list;
int (*xmit_hash_policy)(struct sk_buff *, int);
u16 rr_tx_counter;
struct ad_bond_info ad_info;
@@ -248,7 +248,6 @@ struct bonding {
/* debugging support via debugfs */
struct dentry *debug_dir;
#endif /* CONFIG_DEBUG_FS */
- bool dev_addr_from_first;
};
static inline bool bond_vlan_used(struct bonding *bond)
@@ -323,6 +322,9 @@ static inline bool bond_is_active_slave(struct slave *slave)
#define BOND_FOM_ACTIVE 1
#define BOND_FOM_FOLLOW 2
+#define BOND_ARP_TARGETS_ANY 0
+#define BOND_ARP_TARGETS_ALL 1
+
#define BOND_ARP_VALIDATE_NONE 0
#define BOND_ARP_VALIDATE_ACTIVE (1 << BOND_STATE_ACTIVE)
#define BOND_ARP_VALIDATE_BACKUP (1 << BOND_STATE_BACKUP)
@@ -335,11 +337,31 @@ static inline int slave_do_arp_validate(struct bonding *bond,
return bond->params.arp_validate & (1 << bond_slave_state(slave));
}
+/* Get the oldest arp which we've received on this slave for bond's
+ * arp_targets.
+ */
+static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond,
+ struct slave *slave)
+{
+ int i = 1;
+ unsigned long ret = slave->target_last_arp_rx[0];
+
+ for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++)
+ if (time_before(slave->target_last_arp_rx[i], ret))
+ ret = slave->target_last_arp_rx[i];
+
+ return ret;
+}
+
static inline unsigned long slave_last_rx(struct bonding *bond,
struct slave *slave)
{
- if (slave_do_arp_validate(bond, slave))
- return slave->last_arp_rx;
+ if (slave_do_arp_validate(bond, slave)) {
+ if (bond->params.arp_all_targets == BOND_ARP_TARGETS_ALL)
+ return slave_oldest_target_arp_rx(bond, slave);
+ else
+ return slave->last_arp_rx;
+ }
return slave->dev->last_rx;
}
@@ -465,12 +487,29 @@ static inline struct slave *bond_slave_has_mac(struct bonding *bond,
return NULL;
}
+/* Check if the ip is present in arp ip list, or first free slot if ip == 0
+ * Returns -1 if not found, index if found
+ */
+static inline int bond_get_targets_ip(__be32 *targets, __be32 ip)
+{
+ int i;
+
+ for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
+ if (targets[i] == ip)
+ return i;
+ else if (targets[i] == 0)
+ break;
+
+ return -1;
+}
+
/* exported from bond_main.c */
extern int bond_net_id;
extern const struct bond_parm_tbl bond_lacp_tbl[];
extern const struct bond_parm_tbl bond_mode_tbl[];
extern const struct bond_parm_tbl xmit_hashtype_tbl[];
extern const struct bond_parm_tbl arp_validate_tbl[];
+extern const struct bond_parm_tbl arp_all_targets_tbl[];
extern const struct bond_parm_tbl fail_over_mac_tbl[];
extern const struct bond_parm_tbl pri_reselect_tbl[];
extern struct bond_parm_tbl ad_select_tbl[];