📄 bond_alb.c
字号:
break; } } if (!found) { /* no slave has tmp_slave1's perm addr * as its curr addr */ free_mac_slave = tmp_slave1; break; } if (!has_bond_addr) { if (!memcmp(tmp_slave1->dev->dev_addr, bond->dev->dev_addr, ETH_ALEN)) { has_bond_addr = tmp_slave1; } } } if (free_mac_slave) { alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr, bond->alb_info.rlb_enabled); printk(KERN_WARNING DRV_NAME ": Warning: 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) { printk(KERN_ERR DRV_NAME ": 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", slave->dev->name); return -EFAULT; } return 0;}/** * alb_set_mac_address * @bond: * @addr: * * In TLB mode all slaves are configured to the bond's hw address, but set * their dev_addr field to different addresses (based on their permanent hw * addresses). * * For each slave, this function sets the interface to the new address and then * changes its dev_addr field to its previous value. * * Unwinding assumes bond's mac address has not yet changed. */static int alb_set_mac_address(struct bonding *bond, void *addr){ struct sockaddr sa; struct slave *slave, *stop_at; char tmp_addr[ETH_ALEN]; int res; int i; if (bond->alb_info.rlb_enabled) { return 0; } bond_for_each_slave(bond, slave, i) { if (slave->dev->set_mac_address == NULL) { res = -EOPNOTSUPP; goto unwind; } /* save net_device's current hw address */ memcpy(tmp_addr, slave->dev->dev_addr, ETH_ALEN); res = dev_set_mac_address(slave->dev, addr); /* restore net_device's hw address */ memcpy(slave->dev->dev_addr, tmp_addr, ETH_ALEN); if (res) { goto unwind; } } return 0;unwind: memcpy(sa.sa_data, bond->dev->dev_addr, bond->dev->addr_len); sa.sa_family = bond->dev->type; /* unwind from head to the slave that failed */ stop_at = slave; bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at) { memcpy(tmp_addr, slave->dev->dev_addr, ETH_ALEN); dev_set_mac_address(slave->dev, &sa); memcpy(slave->dev->dev_addr, tmp_addr, ETH_ALEN); } return res;}/************************ exported alb funcions ************************/int bond_alb_initialize(struct bonding *bond, int rlb_enabled){ int res; res = tlb_initialize(bond); if (res) { return res; } if (rlb_enabled) { bond->alb_info.rlb_enabled = 1; /* initialize rlb */ res = rlb_initialize(bond); if (res) { tlb_deinitialize(bond); return res; } } return 0;}void bond_alb_deinitialize(struct bonding *bond){ struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); tlb_deinitialize(bond); if (bond_info->rlb_enabled) { rlb_deinitialize(bond); }}int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev){ struct bonding *bond = bond_dev->priv; struct ethhdr *eth_data; struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); struct slave *tx_slave = NULL; static u32 ip_bcast = 0xffffffff; int hash_size = 0; int do_tx_balance = 1; u32 hash_index = 0; u8 *hash_start = NULL; int res = 1; skb->mac.raw = (unsigned char *)skb->data; eth_data = eth_hdr(skb); /* make sure that the curr_active_slave and the slaves list do * not change during tx */ read_lock(&bond->lock); read_lock(&bond->curr_slave_lock); if (!BOND_IS_OK(bond)) { goto out; } switch (ntohs(skb->protocol)) { case ETH_P_IP: if ((memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) || (skb->nh.iph->daddr == ip_bcast) || (skb->nh.iph->protocol == IPPROTO_IGMP)) { do_tx_balance = 0; break; } hash_start = (char*)&(skb->nh.iph->daddr); hash_size = sizeof(skb->nh.iph->daddr); break; case ETH_P_IPV6: if (memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) { do_tx_balance = 0; break; } hash_start = (char*)&(skb->nh.ipv6h->daddr); hash_size = sizeof(skb->nh.ipv6h->daddr); break; case ETH_P_IPX: if (ipx_hdr(skb)->ipx_checksum != __constant_htons(IPX_NO_CHECKSUM)) { /* something is wrong with this packet */ do_tx_balance = 0; break; } if (ipx_hdr(skb)->ipx_type != IPX_TYPE_NCP) { /* The only protocol worth balancing in * this family since it has an "ARP" like * mechanism */ do_tx_balance = 0; break; } hash_start = (char*)eth_data->h_dest; hash_size = ETH_ALEN; break; case ETH_P_ARP: do_tx_balance = 0; if (bond_info->rlb_enabled) { tx_slave = rlb_arp_xmit(skb, bond); } break; default: do_tx_balance = 0; break; } if (do_tx_balance) { hash_index = _simple_hash(hash_start, hash_size); tx_slave = tlb_choose_channel(bond, hash_index, skb->len); } if (!tx_slave) { /* unbalanced or unassigned, send through primary */ tx_slave = bond->curr_active_slave; bond_info->unbalanced_load += skb->len; } if (tx_slave && SLAVE_IS_OK(tx_slave)) { if (tx_slave != bond->curr_active_slave) { memcpy(eth_data->h_source, tx_slave->dev->dev_addr, ETH_ALEN); } res = bond_dev_queue_xmit(bond, skb, tx_slave->dev); } else { if (tx_slave) { tlb_clear_slave(bond, tx_slave, 0); } }out: if (res) { /* no suitable interface, frame not sent */ dev_kfree_skb(skb); } read_unlock(&bond->curr_slave_lock); read_unlock(&bond->lock); return 0;}void bond_alb_monitor(struct bonding *bond){ struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); struct slave *slave; int i; read_lock(&bond->lock); if (bond->kill_timers) { goto out; } if (bond->slave_cnt == 0) { bond_info->tx_rebalance_counter = 0; bond_info->lp_counter = 0; goto re_arm; } bond_info->tx_rebalance_counter++; bond_info->lp_counter++; /* send learning packets */ if (bond_info->lp_counter >= BOND_ALB_LP_TICKS) { /* change of curr_active_slave involves swapping of mac addresses. * in order to avoid this swapping from happening while * sending the learning packets, the curr_slave_lock must be held for * read. */ read_lock(&bond->curr_slave_lock); bond_for_each_slave(bond, slave, i) { alb_send_learning_packets(slave,slave->dev->dev_addr); } read_unlock(&bond->curr_slave_lock); bond_info->lp_counter = 0; } /* rebalance tx traffic */ if (bond_info->tx_rebalance_counter >= BOND_TLB_REBALANCE_TICKS) { read_lock(&bond->curr_slave_lock); bond_for_each_slave(bond, slave, i) { tlb_clear_slave(bond, slave, 1); if (slave == bond->curr_active_slave) { SLAVE_TLB_INFO(slave).load = bond_info->unbalanced_load / BOND_TLB_REBALANCE_INTERVAL; bond_info->unbalanced_load = 0; } } read_unlock(&bond->curr_slave_lock); bond_info->tx_rebalance_counter = 0; } /* handle rlb stuff */ if (bond_info->rlb_enabled) { /* the following code changes the promiscuity of the * the curr_active_slave. It needs to be locked with a * write lock to protect from other code that also * sets the promiscuity. */ write_lock(&bond->curr_slave_lock); if (bond_info->primary_is_promisc && (++bond_info->rlb_promisc_timeout_counter >= RLB_PROMISC_TIMEOUT)) { bond_info->rlb_promisc_timeout_counter = 0; /* If the primary was set to promiscuous mode * because a slave was disabled then * it can now leave promiscuous mode. */ dev_set_promiscuity(bond->curr_active_slave->dev, -1); bond_info->primary_is_promisc = 0; } write_unlock(&bond->curr_slave_lock); if (bond_info->rlb_rebalance) { bond_info->rlb_rebalance = 0; rlb_rebalance(bond); } /* check if clients need updating */ if (bond_info->rx_ntt) { if (bond_info->rlb_update_delay_counter) { --bond_info->rlb_update_delay_counter; } else { rlb_update_rx_clients(bond); if (bond_info->rlb_update_retry_counter) { --bond_info->rlb_update_retry_counter; } else { bond_info->rx_ntt = 0; } } } }re_arm: mod_timer(&(bond_info->alb_timer), jiffies + alb_delta_in_ticks);out: read_unlock(&bond->lock);}/* assumption: called before the slave is attached to the bond * and not locked by the bond lock */int bond_alb_init_slave(struct bonding *bond, struct slave *slave){ int res; res = alb_set_slave_mac_addr(slave, slave->perm_hwaddr, bond->alb_info.rlb_enabled); if (res) { return res; } /* caller must hold the bond lock for write since the mac addresses * are compared and may be swapped. */ write_lock_bh(&bond->lock); res = alb_handle_addr_collision_on_attach(bond, slave); write_unlock_bh(&bond->lock); if (res) { return res; } tlb_init_slave(slave); /* order a rebalance ASAP */ bond->alb_info.tx_rebalance_counter = BOND_TLB_REBALANCE_TICKS; if (bond->alb_info.rlb_enabled) { bond->alb_info.rlb_rebalance = 1; } return 0;}/* Caller must hold bond lock for write */void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave){ if (bond->slave_cnt > 1) { alb_change_hw_addr_on_detach(bond, slave); } tlb_clear_slave(bond, slave, 0); if (bond->alb_info.rlb_enabled) { bond->alb_info.next_rx_slave = NULL; rlb_clear_slave(bond, slave); }}/* Caller must hold bond lock for read */void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char link){ struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); if (link == BOND_LINK_DOWN) { tlb_clear_slave(bond, slave, 0); if (bond->alb_info.rlb_enabled) { rlb_clear_slave(bond, slave); } } else if (link == BOND_LINK_UP) { /* order a rebalance ASAP */ bond_info->tx_rebalance_counter = BOND_TLB_REBALANCE_TICKS; if (bond->alb_info.rlb_enabled) { bond->alb_info.rlb_rebalance = 1; /* If the updelay module parameter is smaller than the * forwarding delay of the switch the rebalance will * not work because the rebalance arp replies will * not be forwarded to the clients.. */ } }}/** * bond_alb_handle_active_change - assign new curr_active_slave * @bond: our bonding struct * @new_slave: new slave to assign * * Set the bond->curr_active_slave to @new_slave and handle * mac address swapping and promiscuity changes as needed. * * Caller must hold bond curr_slave_lock for write (or bond lock for write) */void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave){ struct slave *swap_slave; int i; if (bond->curr_active_slave == new_slave) { return; } if (bond->curr_active_slave && bond->alb_info.primary_is_promisc) { dev_set_promiscuity(bond->curr_active_slave->dev, -1); bond->alb_info.primary_is_promisc = 0; bond->alb_info.rlb_promisc_timeout_counter = 0; } swap_slave = bond->curr_active_slave; bond->curr_active_slave = new_slave; if (!new_slave || (bond->slave_cnt == 0)) { return; } /* 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 (!memcmp(tmp_slave->dev->dev_addr, bond->dev->dev_addr, ETH_ALEN)) { swap_slave = tmp_slave; break; } } } /* 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, bond->alb_info.rlb_enabled); /* fasten bond mac on new current slave */ alb_send_learning_packets(new_slave, bond->dev->dev_addr); }}int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr){ struct bonding *bond = bond_dev->priv; struct sockaddr *sa = addr; struct slave *slave, *swap_slave; int res; int i; if (!is_valid_ether_addr(sa->sa_data)) { return -EADDRNOTAVAIL; } res = alb_set_mac_address(bond, addr); if (res) { return res; } memcpy(bond_dev->dev_addr, sa->sa_data, bond_dev->addr_len); /* If there is no curr_active_slave there is nothing else to do. * Otherwise we'll need to pass the new address to it and handle * duplications. */ if (!bond->curr_active_slave) { return 0; } swap_slave = NULL; bond_for_each_slave(bond, slave, i) { if (!memcmp(slave->dev->dev_addr, bond_dev->dev_addr, ETH_ALEN)) { swap_slave = slave; break; } } if (swap_slave) { alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave); } else { alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr, bond->alb_info.rlb_enabled); alb_send_learning_packets(bond->curr_active_slave, bond_dev->dev_addr); if (bond->alb_info.rlb_enabled) { /* inform clients mac address has changed */ rlb_req_update_slave_clients(bond, bond->curr_active_slave); } } return 0;}void bond_alb_clear_vlan(struct bonding *bond, unsigned short vlan_id){ if (bond->alb_info.current_alb_vlan && (bond->alb_info.current_alb_vlan->vlan_id == vlan_id)) { bond->alb_info.current_alb_vlan = NULL; } if (bond->alb_info.rlb_enabled) { rlb_clear_vlan(bond, vlan_id); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -