diff options
Diffstat (limited to 'drivers/net/bonding')
-rw-r--r-- | drivers/net/bonding/bond_alb.c | 99 | ||||
-rw-r--r-- | drivers/net/bonding/bond_main.c | 290 | ||||
-rw-r--r-- | drivers/net/bonding/bond_sysfs.c | 133 | ||||
-rw-r--r-- | drivers/net/bonding/bonding.h | 49 |
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[]; |