Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[deliverable/linux.git] / drivers / net / bonding / bond_alb.c
index 9f69e818b0009db7881b3f8c862393836e5a604b..7bbbf1ca08873ea51cec5c92a1072d8fe543251a 100644 (file)
@@ -82,7 +82,8 @@ static inline struct arp_pkt *arp_pkt(const struct sk_buff *skb)
 }
 
 /* Forward declaration */
-static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]);
+static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
+                                     bool strict_match);
 static void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp);
 static void rlb_src_unlink(struct bonding *bond, u32 index);
 static void rlb_src_link(struct bonding *bond, u32 ip_src_hash,
@@ -228,7 +229,7 @@ static struct slave *tlb_get_least_loaded_slave(struct bonding *bond)
 
        /* Find the slave with the largest gap */
        bond_for_each_slave_rcu(bond, slave, iter) {
-               if (SLAVE_IS_OK(slave)) {
+               if (bond_slave_can_tx(slave)) {
                        long long gap = compute_gap(slave);
 
                        if (max_gap < gap) {
@@ -383,7 +384,7 @@ static struct slave *rlb_next_rx_slave(struct bonding *bond)
        bool found = false;
 
        bond_for_each_slave(bond, slave, iter) {
-               if (!SLAVE_IS_OK(slave))
+               if (!bond_slave_can_tx(slave))
                        continue;
                if (!found) {
                        if (!before || before->speed < slave->speed)
@@ -416,7 +417,7 @@ static struct slave *__rlb_next_rx_slave(struct bonding *bond)
        bool found = false;
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               if (!SLAVE_IS_OK(slave))
+               if (!bond_slave_can_tx(slave))
                        continue;
                if (!found) {
                        if (!before || before->speed < slave->speed)
@@ -459,7 +460,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);
+       alb_send_learning_packets(bond->curr_active_slave, addr, true);
 }
 
 /* slave being removed should not be active at this point
@@ -995,7 +996,7 @@ static void rlb_clear_vlan(struct bonding *bond, unsigned short vlan_id)
 /*********************** tlb/rlb shared functions *********************/
 
 static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[],
-                           u16 vid)
+                           __be16 vlan_proto, u16 vid)
 {
        struct learning_pkt pkt;
        struct sk_buff *skb;
@@ -1021,7 +1022,7 @@ static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[],
        skb->dev = slave->dev;
 
        if (vid) {
-               skb = vlan_put_tag(skb, htons(ETH_P_8021Q), 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);
@@ -1032,22 +1033,32 @@ static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[],
        dev_queue_xmit(skb);
 }
 
-
-static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
+static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
+                                     bool strict_match)
 {
        struct bonding *bond = bond_get_bond_by_slave(slave);
        struct net_device *upper;
        struct list_head *iter;
 
        /* send untagged */
-       alb_send_lp_vid(slave, mac_addr, 0);
+       alb_send_lp_vid(slave, mac_addr, 0, 0);
 
        /* loop through vlans and send one packet for each */
        rcu_read_lock();
        netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
-               if (upper->priv_flags & IFF_802_1Q_VLAN)
-                       alb_send_lp_vid(slave, mac_addr,
-                                       vlan_dev_vlan_id(upper));
+               if (is_vlan_dev(upper) && vlan_get_encap_level(upper) == 0) {
+                       if (strict_match &&
+                           ether_addr_equal_64bits(mac_addr,
+                                                   upper->dev_addr)) {
+                               alb_send_lp_vid(slave, mac_addr,
+                                               vlan_dev_vlan_proto(upper),
+                                               vlan_dev_vlan_id(upper));
+                       } else if (!strict_match) {
+                               alb_send_lp_vid(slave, upper->dev_addr,
+                                               vlan_dev_vlan_proto(upper),
+                                               vlan_dev_vlan_id(upper));
+                       }
+               }
        }
        rcu_read_unlock();
 }
@@ -1057,7 +1068,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])
        struct net_device *dev = slave->dev;
        struct sockaddr s_addr;
 
-       if (slave->bond->params.mode == BOND_MODE_TLB) {
+       if (BOND_MODE(slave->bond) == BOND_MODE_TLB) {
                memcpy(dev->dev_addr, addr, dev->addr_len);
                return 0;
        }
@@ -1100,14 +1111,14 @@ static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2)
 static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
                                struct slave *slave2)
 {
-       int slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2));
+       int slaves_state_differ = (bond_slave_can_tx(slave1) != bond_slave_can_tx(slave2));
        struct slave *disabled_slave = NULL;
 
        ASSERT_RTNL();
 
        /* fasten the change in the switch */
-       if (SLAVE_IS_OK(slave1)) {
-               alb_send_learning_packets(slave1, slave1->dev->dev_addr);
+       if (bond_slave_can_tx(slave1)) {
+               alb_send_learning_packets(slave1, slave1->dev->dev_addr, false);
                if (bond->alb_info.rlb_enabled) {
                        /* inform the clients that the mac address
                         * has changed
@@ -1118,8 +1129,8 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
                disabled_slave = slave1;
        }
 
-       if (SLAVE_IS_OK(slave2)) {
-               alb_send_learning_packets(slave2, slave2->dev->dev_addr);
+       if (bond_slave_can_tx(slave2)) {
+               alb_send_learning_packets(slave2, slave2->dev->dev_addr, false);
                if (bond->alb_info.rlb_enabled) {
                        /* inform the clients that the mac address
                         * has changed
@@ -1347,6 +1358,77 @@ void bond_alb_deinitialize(struct bonding *bond)
                rlb_deinitialize(bond);
 }
 
+static int bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,
+               struct slave *tx_slave)
+{
+       struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
+       struct ethhdr *eth_data = eth_hdr(skb);
+
+       if (!tx_slave) {
+               /* unbalanced or unassigned, send through primary */
+               tx_slave = rcu_dereference(bond->curr_active_slave);
+               if (bond->params.tlb_dynamic_lb)
+                       bond_info->unbalanced_load += skb->len;
+       }
+
+       if (tx_slave && bond_slave_can_tx(tx_slave)) {
+               if (tx_slave != rcu_dereference(bond->curr_active_slave)) {
+                       ether_addr_copy(eth_data->h_source,
+                                       tx_slave->dev->dev_addr);
+               }
+
+               bond_dev_queue_xmit(bond, skb, tx_slave->dev);
+               goto out;
+       }
+
+       if (tx_slave && bond->params.tlb_dynamic_lb) {
+               _lock_tx_hashtbl(bond);
+               __tlb_clear_slave(bond, tx_slave, 0);
+               _unlock_tx_hashtbl(bond);
+       }
+
+       /* no suitable interface, frame not sent */
+       dev_kfree_skb_any(skb);
+out:
+       return NETDEV_TX_OK;
+}
+
+int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
+{
+       struct bonding *bond = netdev_priv(bond_dev);
+       struct ethhdr *eth_data;
+       struct slave *tx_slave = NULL;
+       u32 hash_index;
+
+       skb_reset_mac_header(skb);
+       eth_data = eth_hdr(skb);
+
+       /* Do not TX balance any multicast or broadcast */
+       if (!is_multicast_ether_addr(eth_data->h_dest)) {
+               switch (skb->protocol) {
+               case htons(ETH_P_IP):
+               case htons(ETH_P_IPX):
+                   /* In case of IPX, it will falback to L2 hash */
+               case htons(ETH_P_IPV6):
+                       hash_index = bond_xmit_hash(bond, skb);
+                       if (bond->params.tlb_dynamic_lb) {
+                               tx_slave = tlb_choose_channel(bond,
+                                                             hash_index & 0xFF,
+                                                             skb->len);
+                       } else {
+                               struct list_head *iter;
+                               int idx = hash_index % bond->slave_cnt;
+
+                               bond_for_each_slave_rcu(bond, tx_slave, iter)
+                                       if (--idx < 0)
+                                               break;
+                       }
+                       break;
+               }
+       }
+       return bond_do_alb_xmit(skb, bond, tx_slave);
+}
+
 int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
@@ -1355,7 +1437,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        struct slave *tx_slave = NULL;
        static const __be32 ip_bcast = htonl(0xffffffff);
        int hash_size = 0;
-       int do_tx_balance = 1;
+       bool do_tx_balance = true;
        u32 hash_index = 0;
        const u8 *hash_start = NULL;
        struct ipv6hdr *ip6hdr;
@@ -1370,7 +1452,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                if (ether_addr_equal_64bits(eth_data->h_dest, mac_bcast) ||
                    (iph->daddr == ip_bcast) ||
                    (iph->protocol == IPPROTO_IGMP)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
                hash_start = (char *)&(iph->daddr);
@@ -1382,7 +1464,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                 * that here just in case.
                 */
                if (ether_addr_equal_64bits(eth_data->h_dest, mac_bcast)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1390,7 +1472,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                 * broadcasts in IPv4.
                 */
                if (ether_addr_equal_64bits(eth_data->h_dest, mac_v6_allmcast)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1400,7 +1482,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                 */
                ip6hdr = ipv6_hdr(skb);
                if (ipv6_addr_any(&ip6hdr->saddr)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1410,7 +1492,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        case ETH_P_IPX:
                if (ipx_hdr(skb)->ipx_checksum != IPX_NO_CHECKSUM) {
                        /* something is wrong with this packet */
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1419,7 +1501,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                         * this family since it has an "ARP" like
                         * mechanism
                         */
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1427,12 +1509,12 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                hash_size = ETH_ALEN;
                break;
        case ETH_P_ARP:
-               do_tx_balance = 0;
+               do_tx_balance = false;
                if (bond_info->rlb_enabled)
                        tx_slave = rlb_arp_xmit(skb, bond);
                break;
        default:
-               do_tx_balance = 0;
+               do_tx_balance = false;
                break;
        }
 
@@ -1441,32 +1523,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                tx_slave = tlb_choose_channel(bond, hash_index, skb->len);
        }
 
-       if (!tx_slave) {
-               /* unbalanced or unassigned, send through primary */
-               tx_slave = rcu_dereference(bond->curr_active_slave);
-               bond_info->unbalanced_load += skb->len;
-       }
-
-       if (tx_slave && SLAVE_IS_OK(tx_slave)) {
-               if (tx_slave != rcu_dereference(bond->curr_active_slave)) {
-                       ether_addr_copy(eth_data->h_source,
-                                       tx_slave->dev->dev_addr);
-               }
-
-               bond_dev_queue_xmit(bond, skb, tx_slave->dev);
-               goto out;
-       }
-
-       if (tx_slave) {
-               _lock_tx_hashtbl(bond);
-               __tlb_clear_slave(bond, tx_slave, 0);
-               _unlock_tx_hashtbl(bond);
-       }
-
-       /* no suitable interface, frame not sent */
-       dev_kfree_skb_any(skb);
-out:
-       return NETDEV_TX_OK;
+       return bond_do_alb_xmit(skb, bond, tx_slave);
 }
 
 void bond_alb_monitor(struct work_struct *work)
@@ -1490,6 +1547,8 @@ void bond_alb_monitor(struct work_struct *work)
 
        /* send learning packets */
        if (bond_info->lp_counter >= BOND_ALB_LP_TICKS(bond)) {
+               bool strict_match;
+
                /* 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
@@ -1497,8 +1556,15 @@ void bond_alb_monitor(struct work_struct *work)
                 */
                read_lock(&bond->curr_slave_lock);
 
-               bond_for_each_slave_rcu(bond, slave, iter)
-                       alb_send_learning_packets(slave, slave->dev->dev_addr);
+               bond_for_each_slave_rcu(bond, slave, iter) {
+                       /* If updating current_active, use all currently
+                        * user mac addreses (!strict_match).  Otherwise, only
+                        * use mac of the slave device.
+                        */
+                       strict_match = (slave != bond->curr_active_slave);
+                       alb_send_learning_packets(slave, slave->dev->dev_addr,
+                                                 strict_match);
+               }
 
                read_unlock(&bond->curr_slave_lock);
 
@@ -1699,7 +1765,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
        /* in TLB mode, the slave might flip down/up with the old dev_addr,
         * and thus filter bond->dev_addr's packets, so force bond's mac
         */
-       if (bond->params.mode == BOND_MODE_TLB) {
+       if (BOND_MODE(bond) == BOND_MODE_TLB) {
                struct sockaddr sa;
                u8 tmp_addr[ETH_ALEN];
 
@@ -1721,7 +1787,8 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
        } else {
                /* set the new_slave to the bond mac address */
                alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr);
-               alb_send_learning_packets(new_slave, bond->dev->dev_addr);
+               alb_send_learning_packets(new_slave, bond->dev->dev_addr,
+                                         false);
        }
 
        write_lock_bh(&bond->curr_slave_lock);
@@ -1764,7 +1831,8 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
                alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr);
 
                read_lock(&bond->lock);
-               alb_send_learning_packets(bond->curr_active_slave, bond_dev->dev_addr);
+               alb_send_learning_packets(bond->curr_active_slave,
+                                         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);
This page took 0.033812 seconds and 5 git commands to generate.