diff options
Diffstat (limited to 'drivers/net/bonding/bond_alb.c')
-rw-r--r-- | drivers/net/bonding/bond_alb.c | 146 |
1 files changed, 98 insertions, 48 deletions
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 76c0dade233f..95dd1f58c260 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -19,8 +19,6 @@ * */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> @@ -202,6 +200,7 @@ static int tlb_initialize(struct bonding *bond) static void tlb_deinitialize(struct bonding *bond) { struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct tlb_up_slave *arr; _lock_tx_hashtbl_bh(bond); @@ -209,6 +208,10 @@ static void tlb_deinitialize(struct bonding *bond) bond_info->tx_hashtbl = NULL; _unlock_tx_hashtbl_bh(bond); + + arr = rtnl_dereference(bond_info->slave_arr); + if (arr) + kfree_rcu(arr, rcu); } static long long compute_gap(struct slave *slave) @@ -369,7 +372,7 @@ static int rlb_arp_recv(const struct sk_buff *skb, struct bonding *bond, if (arp->op_code == htons(ARPOP_REPLY)) { /* update rx hash table for this ARP */ rlb_update_entry_from_arp(bond, arp); - pr_debug("Server received an ARP Reply from client\n"); + netdev_dbg(bond->dev, "Server received an ARP Reply from client\n"); } out: return RX_HANDLER_ANOTHER; @@ -448,11 +451,13 @@ static struct slave *__rlb_next_rx_slave(struct bonding *bond) */ static void rlb_teach_disabled_mac_on_primary(struct bonding *bond, u8 addr[]) { - if (!bond->curr_active_slave) + struct slave *curr_active = bond_deref_active_protected(bond); + + if (!curr_active) return; if (!bond->alb_info.primary_is_promisc) { - if (!dev_set_promiscuity(bond->curr_active_slave->dev, 1)) + if (!dev_set_promiscuity(curr_active->dev, 1)) bond->alb_info.primary_is_promisc = 1; else bond->alb_info.primary_is_promisc = 0; @@ -460,7 +465,7 @@ static void rlb_teach_disabled_mac_on_primary(struct bonding *bond, u8 addr[]) bond->alb_info.rlb_promisc_timeout_counter = 0; - alb_send_learning_packets(bond->curr_active_slave, addr, true); + alb_send_learning_packets(curr_active, addr, true); } /* slave being removed should not be active at this point @@ -509,7 +514,7 @@ static void rlb_clear_slave(struct bonding *bond, struct slave *slave) write_lock_bh(&bond->curr_slave_lock); - if (slave != bond->curr_active_slave) + if (slave != bond_deref_active_protected(bond)) rlb_teach_disabled_mac_on_primary(bond, slave->dev->dev_addr); write_unlock_bh(&bond->curr_slave_lock); @@ -533,8 +538,8 @@ static void rlb_update_client(struct rlb_client_info *client_info) client_info->slave->dev->dev_addr, client_info->mac_dst); if (!skb) { - pr_err("%s: Error: failed to create an ARP packet\n", - client_info->slave->bond->dev->name); + netdev_err(client_info->slave->bond->dev, + "failed to create an ARP packet\n"); continue; } @@ -543,8 +548,8 @@ static void rlb_update_client(struct rlb_client_info *client_info) if (client_info->vlan_id) { skb = vlan_put_tag(skb, htons(ETH_P_8021Q), client_info->vlan_id); if (!skb) { - pr_err("%s: Error: failed to insert VLAN tag\n", - client_info->slave->bond->dev->name); + netdev_err(client_info->slave->bond->dev, + "failed to insert VLAN tag\n"); continue; } } @@ -628,8 +633,7 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, __be32 src_ip) client_info = &(bond_info->rx_hashtbl[hash_index]); if (!client_info->slave) { - pr_err("%s: Error: found a client with no channel in the client's hash table\n", - bond->dev->name); + netdev_err(bond->dev, "found a client with no channel in the client's hash table\n"); continue; } /*update all clients using this src_ip, that are not assigned @@ -684,7 +688,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon * move the old client to primary (curr_active_slave) so * that the new client can be assigned to this entry. */ - if (bond->curr_active_slave && + if (curr_active_slave && client_info->slave != curr_active_slave) { client_info->slave = curr_active_slave; rlb_update_client(client_info); @@ -765,7 +769,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) tx_slave = rlb_choose_channel(skb, bond); if (tx_slave) ether_addr_copy(arp->mac_src, tx_slave->dev->dev_addr); - pr_debug("Server sent ARP Reply packet\n"); + netdev_dbg(bond->dev, "Server sent ARP Reply packet\n"); } else if (arp->op_code == htons(ARPOP_REQUEST)) { /* Create an entry in the rx_hashtbl for this client as a * place holder. @@ -785,7 +789,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) * updated with their assigned mac. */ rlb_req_update_subnet_clients(bond, arp->ip_src); - pr_debug("Server sent ARP Request packet\n"); + netdev_dbg(bond->dev, "Server sent ARP Request packet\n"); } return tx_slave; @@ -1024,8 +1028,7 @@ static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[], if (vid) { skb = vlan_put_tag(skb, vlan_proto, vid); if (!skb) { - pr_err("%s: Error: failed to insert VLAN tag\n", - slave->bond->dev->name); + netdev_err(slave->bond->dev, "failed to insert VLAN tag\n"); return; } } @@ -1039,7 +1042,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[], struct bonding *bond = bond_get_bond_by_slave(slave); struct net_device *upper; struct list_head *iter; - struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP]; + struct bond_vlan_tag *tags; /* send untagged */ alb_send_lp_vid(slave, mac_addr, 0, 0); @@ -1067,10 +1070,12 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[], * when strict_match is turned off. */ if (netif_is_macvlan(upper) && !strict_match) { - memset(tags, 0, sizeof(tags)); - bond_verify_device_path(bond->dev, upper, tags); + tags = bond_verify_device_path(bond->dev, upper, 0); + if (IS_ERR_OR_NULL(tags)) + BUG(); alb_send_lp_vid(slave, upper->dev_addr, tags[0].vlan_proto, tags[0].vlan_id); + kfree(tags); } } rcu_read_unlock(); @@ -1091,9 +1096,8 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) memcpy(s_addr.sa_data, addr, dev->addr_len); s_addr.sa_family = dev->type; if (dev_set_mac_address(dev, &s_addr)) { - pr_err("%s: Error: dev_set_mac_address of dev %s failed!\n" - "ALB mode requires that the base driver support setting the hw address also when the network device's interface is open\n", - slave->bond->dev->name, dev->name); + netdev_err(slave->bond->dev, "dev_set_mac_address of dev %s failed! ALB mode requires that the base driver support setting the hw address also when the network device's interface is open\n", + dev->name); return -EOPNOTSUPP; } return 0; @@ -1221,7 +1225,7 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla */ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slave *slave) { - struct slave *has_bond_addr = bond->curr_active_slave; + struct slave *has_bond_addr = rcu_access_pointer(bond->curr_active_slave); struct slave *tmp_slave1, *free_mac_slave = NULL; struct list_head *iter; @@ -1267,13 +1271,12 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav if (free_mac_slave) { alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr); - pr_warn("%s: Warning: the hw address of slave %s is in use by the bond; giving it the hw address of %s\n", - bond->dev->name, slave->dev->name, - free_mac_slave->dev->name); + netdev_warn(bond->dev, "the hw address of slave %s is in use by the bond; giving it the hw address of %s\n", + slave->dev->name, free_mac_slave->dev->name); } else if (has_bond_addr) { - pr_err("%s: Error: the hw address of slave %s is in use by the bond; couldn't find a slave with a free hw address to give it (this should not have happened)\n", - bond->dev->name, slave->dev->name); + netdev_err(bond->dev, "the hw address of slave %s is in use by the bond; couldn't find a slave with a free hw address to give it (this should not have happened)\n", + slave->dev->name); return -EFAULT; } @@ -1406,9 +1409,39 @@ out: return NETDEV_TX_OK; } +static int bond_tlb_update_slave_arr(struct bonding *bond, + struct slave *skipslave) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct slave *tx_slave; + struct list_head *iter; + struct tlb_up_slave *new_arr, *old_arr; + + new_arr = kzalloc(offsetof(struct tlb_up_slave, arr[bond->slave_cnt]), + GFP_ATOMIC); + if (!new_arr) + return -ENOMEM; + + bond_for_each_slave(bond, tx_slave, iter) { + if (!bond_slave_can_tx(tx_slave)) + continue; + if (skipslave == tx_slave) + continue; + new_arr->arr[new_arr->count++] = tx_slave; + } + + old_arr = rtnl_dereference(bond_info->slave_arr); + rcu_assign_pointer(bond_info->slave_arr, new_arr); + if (old_arr) + kfree_rcu(old_arr, rcu); + + return 0; +} + int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); struct ethhdr *eth_data; struct slave *tx_slave = NULL; u32 hash_index; @@ -1429,12 +1462,12 @@ int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev) hash_index & 0xFF, skb->len); } else { - struct list_head *iter; - int idx = hash_index % bond->slave_cnt; + struct tlb_up_slave *slaves; - bond_for_each_slave_rcu(bond, tx_slave, iter) - if (--idx < 0) - break; + slaves = rcu_dereference(bond_info->slave_arr); + if (slaves && slaves->count) + tx_slave = slaves->arr[hash_index % + slaves->count]; } break; } @@ -1575,7 +1608,7 @@ void bond_alb_monitor(struct work_struct *work) * use mac of the slave device. * In RLB mode, we always use strict matches. */ - strict_match = (slave != bond->curr_active_slave || + strict_match = (slave != rcu_access_pointer(bond->curr_active_slave) || bond_info->rlb_enabled); alb_send_learning_packets(slave, slave->dev->dev_addr, strict_match); @@ -1593,7 +1626,7 @@ void bond_alb_monitor(struct work_struct *work) bond_for_each_slave_rcu(bond, slave, iter) { tlb_clear_slave(bond, slave, 1); - if (slave == bond->curr_active_slave) { + if (slave == rcu_access_pointer(bond->curr_active_slave)) { SLAVE_TLB_INFO(slave).load = bond_info->unbalanced_load / BOND_TLB_REBALANCE_INTERVAL; @@ -1625,7 +1658,8 @@ void bond_alb_monitor(struct work_struct *work) * because a slave was disabled then * it can now leave promiscuous mode. */ - dev_set_promiscuity(bond->curr_active_slave->dev, -1); + dev_set_promiscuity(rtnl_dereference(bond->curr_active_slave)->dev, + -1); bond_info->primary_is_promisc = 0; rtnl_unlock(); @@ -1698,6 +1732,11 @@ void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave) bond->alb_info.rx_slave = NULL; rlb_clear_slave(bond, slave); } + + if (bond_is_nondyn_tlb(bond)) + if (bond_tlb_update_slave_arr(bond, slave)) + pr_err("Failed to build slave-array for TLB mode.\n"); + } /* Caller must hold bond lock for read */ @@ -1721,6 +1760,11 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char */ } } + + if (bond_is_nondyn_tlb(bond)) { + if (bond_tlb_update_slave_arr(bond, NULL)) + pr_err("Failed to build slave-array for TLB mode.\n"); + } } /** @@ -1742,17 +1786,21 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave __acquires(&bond->curr_slave_lock) { struct slave *swap_slave; + struct slave *curr_active; - if (bond->curr_active_slave == new_slave) + curr_active = rcu_dereference_protected(bond->curr_active_slave, + !new_slave || + lockdep_is_held(&bond->curr_slave_lock)); + if (curr_active == new_slave) return; - if (bond->curr_active_slave && bond->alb_info.primary_is_promisc) { - dev_set_promiscuity(bond->curr_active_slave->dev, -1); + if (curr_active && bond->alb_info.primary_is_promisc) { + dev_set_promiscuity(curr_active->dev, -1); bond->alb_info.primary_is_promisc = 0; bond->alb_info.rlb_promisc_timeout_counter = 0; } - swap_slave = bond->curr_active_slave; + swap_slave = curr_active; rcu_assign_pointer(bond->curr_active_slave, new_slave); if (!new_slave || !bond_has_slaves(bond)) @@ -1818,6 +1866,7 @@ 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 *curr_active; struct slave *swap_slave; int res; @@ -1834,23 +1883,24 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) * Otherwise we'll need to pass the new address to it and handle * duplications. */ - if (!bond->curr_active_slave) + curr_active = rtnl_dereference(bond->curr_active_slave); + if (!curr_active) return 0; swap_slave = bond_slave_has_mac(bond, bond_dev->dev_addr); if (swap_slave) { - alb_swap_mac_addr(swap_slave, bond->curr_active_slave); - alb_fasten_mac_swap(bond, swap_slave, bond->curr_active_slave); + alb_swap_mac_addr(swap_slave, curr_active); + alb_fasten_mac_swap(bond, swap_slave, curr_active); } else { - alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr); + alb_set_slave_mac_addr(curr_active, bond_dev->dev_addr); read_lock(&bond->lock); - alb_send_learning_packets(bond->curr_active_slave, + 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, bond->curr_active_slave); + rlb_req_update_slave_clients(bond, curr_active); } read_unlock(&bond->lock); } |