/* Caller must hold bond lock for write or ptrlock for write*/ static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct slave *slave2) { u8 tmp_mac_addr[ETH_ALEN]; struct slave *disabled_slave = NULL; u8 slaves_state_differ; slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2)); memcpy(tmp_mac_addr, slave1->dev->dev_addr, ETH_ALEN); alb_set_mac_addr(slave1, slave2->dev->dev_addr, bond->alb_info.rlb_enabled); alb_set_mac_addr(slave2, tmp_mac_addr, bond->alb_info.rlb_enabled); /* fasten the change in the switch */ if (SLAVE_IS_OK(slave1)) { alb_send_learning_packets(slave1, slave1->dev->dev_addr); if (bond->alb_info.rlb_enabled) { /* inform the clients that the mac address * has changed */ rlb_req_update_slave_clients(bond, slave1); } } else { disabled_slave = slave1; } if (SLAVE_IS_OK(slave2)) { alb_send_learning_packets(slave2, slave2->dev->dev_addr); if (bond->alb_info.rlb_enabled) { /* inform the clients that the mac address * has changed */ rlb_req_update_slave_clients(bond, slave2); } } else { disabled_slave = slave2; } if (bond->alb_info.rlb_enabled && slaves_state_differ) { /* A disabled slave was assigned an active mac addr */ rlb_teach_disabled_mac_on_primary(bond, disabled_slave->dev->dev_addr); } }
/* Caller must hold bond lock for read */ static struct slave* rlb_next_rx_slave(struct bonding *bond) { struct slave *rx_slave = NULL, *slave = NULL; unsigned int i = 0; struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); slave = bond_info->next_rx_slave; if (slave == NULL) { slave = bond->next; } /* this loop uses the circular linked list property of the * slave's list to go through all slaves */ for (i = 0; i < bond->slave_cnt; i++, slave = slave->next) { if (SLAVE_IS_OK(slave)) { if (!rx_slave) { rx_slave = slave; } else if (slave->speed > rx_slave->speed) { rx_slave = slave; } } } if (rx_slave) { bond_info->next_rx_slave = rx_slave->next; } return rx_slave; }
/* Caller must hold bond lock for read */ static struct slave* tlb_get_least_loaded_slave(struct bonding *bond) { struct slave *slave; struct slave *least_loaded; s64 curr_gap, max_gap; /* Find the first enabled slave */ slave = bond_get_first_slave(bond); while (slave) { if (SLAVE_IS_OK(slave)) { break; } slave = bond_get_next_slave(bond, slave); } if (!slave) { return NULL; } least_loaded = slave; max_gap = (s64)(slave->speed * 1000000) - (s64)(SLAVE_TLB_INFO(slave).load * 8); /* Find the slave with the largest gap */ slave = bond_get_next_slave(bond, slave); while (slave) { if (SLAVE_IS_OK(slave)) { curr_gap = (s64)(slave->speed * 1000000) - (s64)(SLAVE_TLB_INFO(slave).load * 8); if (max_gap < curr_gap) { least_loaded = slave; max_gap = curr_gap; } } slave = bond_get_next_slave(bond, slave); } return least_loaded; }
/* Caller must hold bond lock for read */ static struct slave *tlb_get_least_loaded_slave(struct bonding *bond) { struct slave *slave, *least_loaded; long long max_gap; int i; least_loaded = NULL; max_gap = LLONG_MIN; /* Find the slave with the largest gap */ bond_for_each_slave(bond, slave, i) { if (SLAVE_IS_OK(slave)) { long long gap = compute_gap(slave); if (max_gap < gap) { least_loaded = slave; max_gap = gap; } } } return least_loaded; }
int bond_alb_xmit(struct sk_buff *skb, struct net_device *dev) { struct bonding *bond = (struct bonding *) dev->priv; struct ethhdr *eth_data = (struct ethhdr *)skb->data; struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); struct slave *tx_slave = NULL; char do_tx_balance = 1; int hash_size = 0; u32 hash_index = 0; u8 *hash_start = NULL; u8 mac_bcast[ETH_ALEN] = {0xff,0xff,0xff,0xff,0xff,0xff}; if (!IS_UP(dev)) { /* bond down */ dev_kfree_skb(skb); return 0; } /* make sure that the current_slave and the slaves list do * not change during tx */ read_lock(&bond->lock); if (bond->slave_cnt == 0) { /* no suitable interface, frame not sent */ dev_kfree_skb(skb); read_unlock(&bond->lock); return 0; } read_lock(&bond->ptrlock); switch (ntohs(skb->protocol)) { case ETH_P_IP: if ((memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) || (skb->nh.iph->daddr == 0xffffffff)) { do_tx_balance = 0; break; } hash_start = (char*)&(skb->nh.iph->daddr); hash_size = 4; 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 = 16; break; case ETH_P_IPX: if (skb->nh.ipxh->ipx_checksum != __constant_htons(IPX_NO_CHECKSUM)) { /* something is wrong with this packet */ do_tx_balance = 0; break; } if (skb->nh.ipxh->ipx_type != __constant_htons(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->current_slave; bond_info->unbalanced_load += skb->len; } if (tx_slave && SLAVE_IS_OK(tx_slave)) { skb->dev = tx_slave->dev; if (tx_slave != bond->current_slave) { memcpy(eth_data->h_source, tx_slave->dev->dev_addr, ETH_ALEN); } dev_queue_xmit(skb); } else { /* no suitable interface, frame not sent */ if (tx_slave) { tlb_clear_slave(bond, tx_slave, 0); } dev_kfree_skb(skb); } read_unlock(&bond->ptrlock); read_unlock(&bond->lock); return 0; }