162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 1999 - 2004 Intel Corporation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/skbuff.h>
762306a36Sopenharmony_ci#include <linux/netdevice.h>
862306a36Sopenharmony_ci#include <linux/etherdevice.h>
962306a36Sopenharmony_ci#include <linux/pkt_sched.h>
1062306a36Sopenharmony_ci#include <linux/spinlock.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/timer.h>
1362306a36Sopenharmony_ci#include <linux/ip.h>
1462306a36Sopenharmony_ci#include <linux/ipv6.h>
1562306a36Sopenharmony_ci#include <linux/if_arp.h>
1662306a36Sopenharmony_ci#include <linux/if_ether.h>
1762306a36Sopenharmony_ci#include <linux/if_bonding.h>
1862306a36Sopenharmony_ci#include <linux/if_vlan.h>
1962306a36Sopenharmony_ci#include <linux/in.h>
2062306a36Sopenharmony_ci#include <net/arp.h>
2162306a36Sopenharmony_ci#include <net/ipv6.h>
2262306a36Sopenharmony_ci#include <net/ndisc.h>
2362306a36Sopenharmony_ci#include <asm/byteorder.h>
2462306a36Sopenharmony_ci#include <net/bonding.h>
2562306a36Sopenharmony_ci#include <net/bond_alb.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const u8 mac_v6_allmcast[ETH_ALEN + 2] __long_aligned = {
2862306a36Sopenharmony_ci	0x33, 0x33, 0x00, 0x00, 0x00, 0x01
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_cistatic const int alb_delta_in_ticks = HZ / ALB_TIMER_TICKS_PER_SEC;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#pragma pack(1)
3362306a36Sopenharmony_cistruct learning_pkt {
3462306a36Sopenharmony_ci	u8 mac_dst[ETH_ALEN];
3562306a36Sopenharmony_ci	u8 mac_src[ETH_ALEN];
3662306a36Sopenharmony_ci	__be16 type;
3762306a36Sopenharmony_ci	u8 padding[ETH_ZLEN - ETH_HLEN];
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct arp_pkt {
4162306a36Sopenharmony_ci	__be16  hw_addr_space;
4262306a36Sopenharmony_ci	__be16  prot_addr_space;
4362306a36Sopenharmony_ci	u8      hw_addr_len;
4462306a36Sopenharmony_ci	u8      prot_addr_len;
4562306a36Sopenharmony_ci	__be16  op_code;
4662306a36Sopenharmony_ci	u8      mac_src[ETH_ALEN];	/* sender hardware address */
4762306a36Sopenharmony_ci	__be32  ip_src;			/* sender IP address */
4862306a36Sopenharmony_ci	u8      mac_dst[ETH_ALEN];	/* target hardware address */
4962306a36Sopenharmony_ci	__be32  ip_dst;			/* target IP address */
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci#pragma pack()
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Forward declaration */
5462306a36Sopenharmony_cistatic void alb_send_learning_packets(struct slave *slave, const u8 mac_addr[],
5562306a36Sopenharmony_ci				      bool strict_match);
5662306a36Sopenharmony_cistatic void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp);
5762306a36Sopenharmony_cistatic void rlb_src_unlink(struct bonding *bond, u32 index);
5862306a36Sopenharmony_cistatic void rlb_src_link(struct bonding *bond, u32 ip_src_hash,
5962306a36Sopenharmony_ci			 u32 ip_dst_hash);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic inline u8 _simple_hash(const u8 *hash_start, int hash_size)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	int i;
6462306a36Sopenharmony_ci	u8 hash = 0;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	for (i = 0; i < hash_size; i++)
6762306a36Sopenharmony_ci		hash ^= hash_start[i];
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return hash;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/*********************** tlb specific functions ***************************/
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic inline void tlb_init_table_entry(struct tlb_client_info *entry, int save_load)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	if (save_load) {
7762306a36Sopenharmony_ci		entry->load_history = 1 + entry->tx_bytes /
7862306a36Sopenharmony_ci				      BOND_TLB_REBALANCE_INTERVAL;
7962306a36Sopenharmony_ci		entry->tx_bytes = 0;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	entry->tx_slave = NULL;
8362306a36Sopenharmony_ci	entry->next = TLB_NULL_INDEX;
8462306a36Sopenharmony_ci	entry->prev = TLB_NULL_INDEX;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline void tlb_init_slave(struct slave *slave)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	SLAVE_TLB_INFO(slave).load = 0;
9062306a36Sopenharmony_ci	SLAVE_TLB_INFO(slave).head = TLB_NULL_INDEX;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic void __tlb_clear_slave(struct bonding *bond, struct slave *slave,
9462306a36Sopenharmony_ci			 int save_load)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct tlb_client_info *tx_hash_table;
9762306a36Sopenharmony_ci	u32 index;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* clear slave from tx_hashtbl */
10062306a36Sopenharmony_ci	tx_hash_table = BOND_ALB_INFO(bond).tx_hashtbl;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* skip this if we've already freed the tx hash table */
10362306a36Sopenharmony_ci	if (tx_hash_table) {
10462306a36Sopenharmony_ci		index = SLAVE_TLB_INFO(slave).head;
10562306a36Sopenharmony_ci		while (index != TLB_NULL_INDEX) {
10662306a36Sopenharmony_ci			u32 next_index = tx_hash_table[index].next;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci			tlb_init_table_entry(&tx_hash_table[index], save_load);
10962306a36Sopenharmony_ci			index = next_index;
11062306a36Sopenharmony_ci		}
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	tlb_init_slave(slave);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void tlb_clear_slave(struct bonding *bond, struct slave *slave,
11762306a36Sopenharmony_ci			 int save_load)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
12062306a36Sopenharmony_ci	__tlb_clear_slave(bond, slave, save_load);
12162306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* Must be called before starting the monitor timer */
12562306a36Sopenharmony_cistatic int tlb_initialize(struct bonding *bond)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
12862306a36Sopenharmony_ci	int size = TLB_HASH_TABLE_SIZE * sizeof(struct tlb_client_info);
12962306a36Sopenharmony_ci	struct tlb_client_info *new_hashtbl;
13062306a36Sopenharmony_ci	int i;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	new_hashtbl = kzalloc(size, GFP_KERNEL);
13362306a36Sopenharmony_ci	if (!new_hashtbl)
13462306a36Sopenharmony_ci		return -ENOMEM;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	bond_info->tx_hashtbl = new_hashtbl;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	for (i = 0; i < TLB_HASH_TABLE_SIZE; i++)
14162306a36Sopenharmony_ci		tlb_init_table_entry(&bond_info->tx_hashtbl[i], 0);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/* Must be called only after all slaves have been released */
14962306a36Sopenharmony_cistatic void tlb_deinitialize(struct bonding *bond)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	kfree(bond_info->tx_hashtbl);
15662306a36Sopenharmony_ci	bond_info->tx_hashtbl = NULL;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic long long compute_gap(struct slave *slave)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	return (s64) (slave->speed << 20) - /* Convert to Megabit per sec */
16462306a36Sopenharmony_ci	       (s64) (SLAVE_TLB_INFO(slave).load << 3); /* Bytes to bits */
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic struct slave *tlb_get_least_loaded_slave(struct bonding *bond)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct slave *slave, *least_loaded;
17062306a36Sopenharmony_ci	struct list_head *iter;
17162306a36Sopenharmony_ci	long long max_gap;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	least_loaded = NULL;
17462306a36Sopenharmony_ci	max_gap = LLONG_MIN;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* Find the slave with the largest gap */
17762306a36Sopenharmony_ci	bond_for_each_slave_rcu(bond, slave, iter) {
17862306a36Sopenharmony_ci		if (bond_slave_can_tx(slave)) {
17962306a36Sopenharmony_ci			long long gap = compute_gap(slave);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci			if (max_gap < gap) {
18262306a36Sopenharmony_ci				least_loaded = slave;
18362306a36Sopenharmony_ci				max_gap = gap;
18462306a36Sopenharmony_ci			}
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return least_loaded;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic struct slave *__tlb_choose_channel(struct bonding *bond, u32 hash_index,
19262306a36Sopenharmony_ci						u32 skb_len)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
19562306a36Sopenharmony_ci	struct tlb_client_info *hash_table;
19662306a36Sopenharmony_ci	struct slave *assigned_slave;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	hash_table = bond_info->tx_hashtbl;
19962306a36Sopenharmony_ci	assigned_slave = hash_table[hash_index].tx_slave;
20062306a36Sopenharmony_ci	if (!assigned_slave) {
20162306a36Sopenharmony_ci		assigned_slave = tlb_get_least_loaded_slave(bond);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		if (assigned_slave) {
20462306a36Sopenharmony_ci			struct tlb_slave_info *slave_info =
20562306a36Sopenharmony_ci				&(SLAVE_TLB_INFO(assigned_slave));
20662306a36Sopenharmony_ci			u32 next_index = slave_info->head;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci			hash_table[hash_index].tx_slave = assigned_slave;
20962306a36Sopenharmony_ci			hash_table[hash_index].next = next_index;
21062306a36Sopenharmony_ci			hash_table[hash_index].prev = TLB_NULL_INDEX;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci			if (next_index != TLB_NULL_INDEX)
21362306a36Sopenharmony_ci				hash_table[next_index].prev = hash_index;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci			slave_info->head = hash_index;
21662306a36Sopenharmony_ci			slave_info->load +=
21762306a36Sopenharmony_ci				hash_table[hash_index].load_history;
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (assigned_slave)
22262306a36Sopenharmony_ci		hash_table[hash_index].tx_bytes += skb_len;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return assigned_slave;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic struct slave *tlb_choose_channel(struct bonding *bond, u32 hash_index,
22862306a36Sopenharmony_ci					u32 skb_len)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct slave *tx_slave;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* We don't need to disable softirq here, because
23362306a36Sopenharmony_ci	 * tlb_choose_channel() is only called by bond_alb_xmit()
23462306a36Sopenharmony_ci	 * which already has softirq disabled.
23562306a36Sopenharmony_ci	 */
23662306a36Sopenharmony_ci	spin_lock(&bond->mode_lock);
23762306a36Sopenharmony_ci	tx_slave = __tlb_choose_channel(bond, hash_index, skb_len);
23862306a36Sopenharmony_ci	spin_unlock(&bond->mode_lock);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	return tx_slave;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci/*********************** rlb specific functions ***************************/
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/* when an ARP REPLY is received from a client update its info
24662306a36Sopenharmony_ci * in the rx_hashtbl
24762306a36Sopenharmony_ci */
24862306a36Sopenharmony_cistatic void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
25162306a36Sopenharmony_ci	struct rlb_client_info *client_info;
25262306a36Sopenharmony_ci	u32 hash_index;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	hash_index = _simple_hash((u8 *)&(arp->ip_src), sizeof(arp->ip_src));
25762306a36Sopenharmony_ci	client_info = &(bond_info->rx_hashtbl[hash_index]);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if ((client_info->assigned) &&
26062306a36Sopenharmony_ci	    (client_info->ip_src == arp->ip_dst) &&
26162306a36Sopenharmony_ci	    (client_info->ip_dst == arp->ip_src) &&
26262306a36Sopenharmony_ci	    (!ether_addr_equal_64bits(client_info->mac_dst, arp->mac_src))) {
26362306a36Sopenharmony_ci		/* update the clients MAC address */
26462306a36Sopenharmony_ci		ether_addr_copy(client_info->mac_dst, arp->mac_src);
26562306a36Sopenharmony_ci		client_info->ntt = 1;
26662306a36Sopenharmony_ci		bond_info->rx_ntt = 1;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int rlb_arp_recv(const struct sk_buff *skb, struct bonding *bond,
27362306a36Sopenharmony_ci			struct slave *slave)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct arp_pkt *arp, _arp;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (skb->protocol != cpu_to_be16(ETH_P_ARP))
27862306a36Sopenharmony_ci		goto out;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	arp = skb_header_pointer(skb, 0, sizeof(_arp), &_arp);
28162306a36Sopenharmony_ci	if (!arp)
28262306a36Sopenharmony_ci		goto out;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* We received an ARP from arp->ip_src.
28562306a36Sopenharmony_ci	 * We might have used this IP address previously (on the bonding host
28662306a36Sopenharmony_ci	 * itself or on a system that is bridged together with the bond).
28762306a36Sopenharmony_ci	 * However, if arp->mac_src is different than what is stored in
28862306a36Sopenharmony_ci	 * rx_hashtbl, some other host is now using the IP and we must prevent
28962306a36Sopenharmony_ci	 * sending out client updates with this IP address and the old MAC
29062306a36Sopenharmony_ci	 * address.
29162306a36Sopenharmony_ci	 * Clean up all hash table entries that have this address as ip_src but
29262306a36Sopenharmony_ci	 * have a different mac_src.
29362306a36Sopenharmony_ci	 */
29462306a36Sopenharmony_ci	rlb_purge_src_ip(bond, arp);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (arp->op_code == htons(ARPOP_REPLY)) {
29762306a36Sopenharmony_ci		/* update rx hash table for this ARP */
29862306a36Sopenharmony_ci		rlb_update_entry_from_arp(bond, arp);
29962306a36Sopenharmony_ci		slave_dbg(bond->dev, slave->dev, "Server received an ARP Reply from client\n");
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ciout:
30262306a36Sopenharmony_ci	return RX_HANDLER_ANOTHER;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/* Caller must hold rcu_read_lock() */
30662306a36Sopenharmony_cistatic struct slave *__rlb_next_rx_slave(struct bonding *bond)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
30962306a36Sopenharmony_ci	struct slave *before = NULL, *rx_slave = NULL, *slave;
31062306a36Sopenharmony_ci	struct list_head *iter;
31162306a36Sopenharmony_ci	bool found = false;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	bond_for_each_slave_rcu(bond, slave, iter) {
31462306a36Sopenharmony_ci		if (!bond_slave_can_tx(slave))
31562306a36Sopenharmony_ci			continue;
31662306a36Sopenharmony_ci		if (!found) {
31762306a36Sopenharmony_ci			if (!before || before->speed < slave->speed)
31862306a36Sopenharmony_ci				before = slave;
31962306a36Sopenharmony_ci		} else {
32062306a36Sopenharmony_ci			if (!rx_slave || rx_slave->speed < slave->speed)
32162306a36Sopenharmony_ci				rx_slave = slave;
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci		if (slave == bond_info->rx_slave)
32462306a36Sopenharmony_ci			found = true;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci	/* we didn't find anything after the current or we have something
32762306a36Sopenharmony_ci	 * better before and up to the current slave
32862306a36Sopenharmony_ci	 */
32962306a36Sopenharmony_ci	if (!rx_slave || (before && rx_slave->speed < before->speed))
33062306a36Sopenharmony_ci		rx_slave = before;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (rx_slave)
33362306a36Sopenharmony_ci		bond_info->rx_slave = rx_slave;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return rx_slave;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci/* Caller must hold RTNL, rcu_read_lock is obtained only to silence checkers */
33962306a36Sopenharmony_cistatic struct slave *rlb_next_rx_slave(struct bonding *bond)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct slave *rx_slave;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ASSERT_RTNL();
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	rcu_read_lock();
34662306a36Sopenharmony_ci	rx_slave = __rlb_next_rx_slave(bond);
34762306a36Sopenharmony_ci	rcu_read_unlock();
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return rx_slave;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/* teach the switch the mac of a disabled slave
35362306a36Sopenharmony_ci * on the primary for fault tolerance
35462306a36Sopenharmony_ci *
35562306a36Sopenharmony_ci * Caller must hold RTNL
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_cistatic void rlb_teach_disabled_mac_on_primary(struct bonding *bond,
35862306a36Sopenharmony_ci					      const u8 addr[])
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct slave *curr_active = rtnl_dereference(bond->curr_active_slave);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (!curr_active)
36362306a36Sopenharmony_ci		return;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (!bond->alb_info.primary_is_promisc) {
36662306a36Sopenharmony_ci		if (!dev_set_promiscuity(curr_active->dev, 1))
36762306a36Sopenharmony_ci			bond->alb_info.primary_is_promisc = 1;
36862306a36Sopenharmony_ci		else
36962306a36Sopenharmony_ci			bond->alb_info.primary_is_promisc = 0;
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	bond->alb_info.rlb_promisc_timeout_counter = 0;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	alb_send_learning_packets(curr_active, addr, true);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci/* slave being removed should not be active at this point
37862306a36Sopenharmony_ci *
37962306a36Sopenharmony_ci * Caller must hold rtnl.
38062306a36Sopenharmony_ci */
38162306a36Sopenharmony_cistatic void rlb_clear_slave(struct bonding *bond, struct slave *slave)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
38462306a36Sopenharmony_ci	struct rlb_client_info *rx_hash_table;
38562306a36Sopenharmony_ci	u32 index, next_index;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* clear slave from rx_hashtbl */
38862306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	rx_hash_table = bond_info->rx_hashtbl;
39162306a36Sopenharmony_ci	index = bond_info->rx_hashtbl_used_head;
39262306a36Sopenharmony_ci	for (; index != RLB_NULL_INDEX; index = next_index) {
39362306a36Sopenharmony_ci		next_index = rx_hash_table[index].used_next;
39462306a36Sopenharmony_ci		if (rx_hash_table[index].slave == slave) {
39562306a36Sopenharmony_ci			struct slave *assigned_slave = rlb_next_rx_slave(bond);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci			if (assigned_slave) {
39862306a36Sopenharmony_ci				rx_hash_table[index].slave = assigned_slave;
39962306a36Sopenharmony_ci				if (is_valid_ether_addr(rx_hash_table[index].mac_dst)) {
40062306a36Sopenharmony_ci					bond_info->rx_hashtbl[index].ntt = 1;
40162306a36Sopenharmony_ci					bond_info->rx_ntt = 1;
40262306a36Sopenharmony_ci					/* A slave has been removed from the
40362306a36Sopenharmony_ci					 * table because it is either disabled
40462306a36Sopenharmony_ci					 * or being released. We must retry the
40562306a36Sopenharmony_ci					 * update to avoid clients from not
40662306a36Sopenharmony_ci					 * being updated & disconnecting when
40762306a36Sopenharmony_ci					 * there is stress
40862306a36Sopenharmony_ci					 */
40962306a36Sopenharmony_ci					bond_info->rlb_update_retry_counter =
41062306a36Sopenharmony_ci						RLB_UPDATE_RETRY;
41162306a36Sopenharmony_ci				}
41262306a36Sopenharmony_ci			} else {  /* there is no active slave */
41362306a36Sopenharmony_ci				rx_hash_table[index].slave = NULL;
41462306a36Sopenharmony_ci			}
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (slave != rtnl_dereference(bond->curr_active_slave))
42162306a36Sopenharmony_ci		rlb_teach_disabled_mac_on_primary(bond, slave->dev->dev_addr);
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic void rlb_update_client(struct rlb_client_info *client_info)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	int i;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (!client_info->slave || !is_valid_ether_addr(client_info->mac_dst))
42962306a36Sopenharmony_ci		return;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	for (i = 0; i < RLB_ARP_BURST_SIZE; i++) {
43262306a36Sopenharmony_ci		struct sk_buff *skb;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
43562306a36Sopenharmony_ci				 client_info->ip_dst,
43662306a36Sopenharmony_ci				 client_info->slave->dev,
43762306a36Sopenharmony_ci				 client_info->ip_src,
43862306a36Sopenharmony_ci				 client_info->mac_dst,
43962306a36Sopenharmony_ci				 client_info->slave->dev->dev_addr,
44062306a36Sopenharmony_ci				 client_info->mac_dst);
44162306a36Sopenharmony_ci		if (!skb) {
44262306a36Sopenharmony_ci			slave_err(client_info->slave->bond->dev,
44362306a36Sopenharmony_ci				  client_info->slave->dev,
44462306a36Sopenharmony_ci				  "failed to create an ARP packet\n");
44562306a36Sopenharmony_ci			continue;
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		skb->dev = client_info->slave->dev;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		if (client_info->vlan_id) {
45162306a36Sopenharmony_ci			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
45262306a36Sopenharmony_ci					       client_info->vlan_id);
45362306a36Sopenharmony_ci		}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		arp_xmit(skb);
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci/* sends ARP REPLIES that update the clients that need updating */
46062306a36Sopenharmony_cistatic void rlb_update_rx_clients(struct bonding *bond)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
46362306a36Sopenharmony_ci	struct rlb_client_info *client_info;
46462306a36Sopenharmony_ci	u32 hash_index;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	hash_index = bond_info->rx_hashtbl_used_head;
46962306a36Sopenharmony_ci	for (; hash_index != RLB_NULL_INDEX;
47062306a36Sopenharmony_ci	     hash_index = client_info->used_next) {
47162306a36Sopenharmony_ci		client_info = &(bond_info->rx_hashtbl[hash_index]);
47262306a36Sopenharmony_ci		if (client_info->ntt) {
47362306a36Sopenharmony_ci			rlb_update_client(client_info);
47462306a36Sopenharmony_ci			if (bond_info->rlb_update_retry_counter == 0)
47562306a36Sopenharmony_ci				client_info->ntt = 0;
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* do not update the entries again until this counter is zero so that
48062306a36Sopenharmony_ci	 * not to confuse the clients.
48162306a36Sopenharmony_ci	 */
48262306a36Sopenharmony_ci	bond_info->rlb_update_delay_counter = RLB_UPDATE_DELAY;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci/* The slave was assigned a new mac address - update the clients */
48862306a36Sopenharmony_cistatic void rlb_req_update_slave_clients(struct bonding *bond, struct slave *slave)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
49162306a36Sopenharmony_ci	struct rlb_client_info *client_info;
49262306a36Sopenharmony_ci	int ntt = 0;
49362306a36Sopenharmony_ci	u32 hash_index;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	hash_index = bond_info->rx_hashtbl_used_head;
49862306a36Sopenharmony_ci	for (; hash_index != RLB_NULL_INDEX;
49962306a36Sopenharmony_ci	     hash_index = client_info->used_next) {
50062306a36Sopenharmony_ci		client_info = &(bond_info->rx_hashtbl[hash_index]);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		if ((client_info->slave == slave) &&
50362306a36Sopenharmony_ci		    is_valid_ether_addr(client_info->mac_dst)) {
50462306a36Sopenharmony_ci			client_info->ntt = 1;
50562306a36Sopenharmony_ci			ntt = 1;
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* update the team's flag only after the whole iteration */
51062306a36Sopenharmony_ci	if (ntt) {
51162306a36Sopenharmony_ci		bond_info->rx_ntt = 1;
51262306a36Sopenharmony_ci		/* fasten the change */
51362306a36Sopenharmony_ci		bond_info->rlb_update_retry_counter = RLB_UPDATE_RETRY;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci/* mark all clients using src_ip to be updated */
52062306a36Sopenharmony_cistatic void rlb_req_update_subnet_clients(struct bonding *bond, __be32 src_ip)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
52362306a36Sopenharmony_ci	struct rlb_client_info *client_info;
52462306a36Sopenharmony_ci	u32 hash_index;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	spin_lock(&bond->mode_lock);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	hash_index = bond_info->rx_hashtbl_used_head;
52962306a36Sopenharmony_ci	for (; hash_index != RLB_NULL_INDEX;
53062306a36Sopenharmony_ci	     hash_index = client_info->used_next) {
53162306a36Sopenharmony_ci		client_info = &(bond_info->rx_hashtbl[hash_index]);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci		if (!client_info->slave) {
53462306a36Sopenharmony_ci			netdev_err(bond->dev, "found a client with no channel in the client's hash table\n");
53562306a36Sopenharmony_ci			continue;
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci		/* update all clients using this src_ip, that are not assigned
53862306a36Sopenharmony_ci		 * to the team's address (curr_active_slave) and have a known
53962306a36Sopenharmony_ci		 * unicast mac address.
54062306a36Sopenharmony_ci		 */
54162306a36Sopenharmony_ci		if ((client_info->ip_src == src_ip) &&
54262306a36Sopenharmony_ci		    !ether_addr_equal_64bits(client_info->slave->dev->dev_addr,
54362306a36Sopenharmony_ci					     bond->dev->dev_addr) &&
54462306a36Sopenharmony_ci		    is_valid_ether_addr(client_info->mac_dst)) {
54562306a36Sopenharmony_ci			client_info->ntt = 1;
54662306a36Sopenharmony_ci			bond_info->rx_ntt = 1;
54762306a36Sopenharmony_ci		}
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	spin_unlock(&bond->mode_lock);
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic struct slave *rlb_choose_channel(struct sk_buff *skb,
55462306a36Sopenharmony_ci					struct bonding *bond,
55562306a36Sopenharmony_ci					const struct arp_pkt *arp)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
55862306a36Sopenharmony_ci	struct slave *assigned_slave, *curr_active_slave;
55962306a36Sopenharmony_ci	struct rlb_client_info *client_info;
56062306a36Sopenharmony_ci	u32 hash_index = 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	spin_lock(&bond->mode_lock);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	curr_active_slave = rcu_dereference(bond->curr_active_slave);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	hash_index = _simple_hash((u8 *)&arp->ip_dst, sizeof(arp->ip_dst));
56762306a36Sopenharmony_ci	client_info = &(bond_info->rx_hashtbl[hash_index]);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (client_info->assigned) {
57062306a36Sopenharmony_ci		if ((client_info->ip_src == arp->ip_src) &&
57162306a36Sopenharmony_ci		    (client_info->ip_dst == arp->ip_dst)) {
57262306a36Sopenharmony_ci			/* the entry is already assigned to this client */
57362306a36Sopenharmony_ci			if (!is_broadcast_ether_addr(arp->mac_dst)) {
57462306a36Sopenharmony_ci				/* update mac address from arp */
57562306a36Sopenharmony_ci				ether_addr_copy(client_info->mac_dst, arp->mac_dst);
57662306a36Sopenharmony_ci			}
57762306a36Sopenharmony_ci			ether_addr_copy(client_info->mac_src, arp->mac_src);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci			assigned_slave = client_info->slave;
58062306a36Sopenharmony_ci			if (assigned_slave) {
58162306a36Sopenharmony_ci				spin_unlock(&bond->mode_lock);
58262306a36Sopenharmony_ci				return assigned_slave;
58362306a36Sopenharmony_ci			}
58462306a36Sopenharmony_ci		} else {
58562306a36Sopenharmony_ci			/* the entry is already assigned to some other client,
58662306a36Sopenharmony_ci			 * move the old client to primary (curr_active_slave) so
58762306a36Sopenharmony_ci			 * that the new client can be assigned to this entry.
58862306a36Sopenharmony_ci			 */
58962306a36Sopenharmony_ci			if (curr_active_slave &&
59062306a36Sopenharmony_ci			    client_info->slave != curr_active_slave) {
59162306a36Sopenharmony_ci				client_info->slave = curr_active_slave;
59262306a36Sopenharmony_ci				rlb_update_client(client_info);
59362306a36Sopenharmony_ci			}
59462306a36Sopenharmony_ci		}
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci	/* assign a new slave */
59762306a36Sopenharmony_ci	assigned_slave = __rlb_next_rx_slave(bond);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (assigned_slave) {
60062306a36Sopenharmony_ci		if (!(client_info->assigned &&
60162306a36Sopenharmony_ci		      client_info->ip_src == arp->ip_src)) {
60262306a36Sopenharmony_ci			/* ip_src is going to be updated,
60362306a36Sopenharmony_ci			 * fix the src hash list
60462306a36Sopenharmony_ci			 */
60562306a36Sopenharmony_ci			u32 hash_src = _simple_hash((u8 *)&arp->ip_src,
60662306a36Sopenharmony_ci						    sizeof(arp->ip_src));
60762306a36Sopenharmony_ci			rlb_src_unlink(bond, hash_index);
60862306a36Sopenharmony_ci			rlb_src_link(bond, hash_src, hash_index);
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		client_info->ip_src = arp->ip_src;
61262306a36Sopenharmony_ci		client_info->ip_dst = arp->ip_dst;
61362306a36Sopenharmony_ci		/* arp->mac_dst is broadcast for arp requests.
61462306a36Sopenharmony_ci		 * will be updated with clients actual unicast mac address
61562306a36Sopenharmony_ci		 * upon receiving an arp reply.
61662306a36Sopenharmony_ci		 */
61762306a36Sopenharmony_ci		ether_addr_copy(client_info->mac_dst, arp->mac_dst);
61862306a36Sopenharmony_ci		ether_addr_copy(client_info->mac_src, arp->mac_src);
61962306a36Sopenharmony_ci		client_info->slave = assigned_slave;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		if (is_valid_ether_addr(client_info->mac_dst)) {
62262306a36Sopenharmony_ci			client_info->ntt = 1;
62362306a36Sopenharmony_ci			bond->alb_info.rx_ntt = 1;
62462306a36Sopenharmony_ci		} else {
62562306a36Sopenharmony_ci			client_info->ntt = 0;
62662306a36Sopenharmony_ci		}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		if (vlan_get_tag(skb, &client_info->vlan_id))
62962306a36Sopenharmony_ci			client_info->vlan_id = 0;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		if (!client_info->assigned) {
63262306a36Sopenharmony_ci			u32 prev_tbl_head = bond_info->rx_hashtbl_used_head;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci			bond_info->rx_hashtbl_used_head = hash_index;
63562306a36Sopenharmony_ci			client_info->used_next = prev_tbl_head;
63662306a36Sopenharmony_ci			if (prev_tbl_head != RLB_NULL_INDEX) {
63762306a36Sopenharmony_ci				bond_info->rx_hashtbl[prev_tbl_head].used_prev =
63862306a36Sopenharmony_ci					hash_index;
63962306a36Sopenharmony_ci			}
64062306a36Sopenharmony_ci			client_info->assigned = 1;
64162306a36Sopenharmony_ci		}
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	spin_unlock(&bond->mode_lock);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	return assigned_slave;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci/* chooses (and returns) transmit channel for arp reply
65062306a36Sopenharmony_ci * does not choose channel for other arp types since they are
65162306a36Sopenharmony_ci * sent on the curr_active_slave
65262306a36Sopenharmony_ci */
65362306a36Sopenharmony_cistatic struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	struct slave *tx_slave = NULL;
65662306a36Sopenharmony_ci	struct net_device *dev;
65762306a36Sopenharmony_ci	struct arp_pkt *arp;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	if (!pskb_network_may_pull(skb, sizeof(*arp)))
66062306a36Sopenharmony_ci		return NULL;
66162306a36Sopenharmony_ci	arp = (struct arp_pkt *)skb_network_header(skb);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* Don't modify or load balance ARPs that do not originate
66462306a36Sopenharmony_ci	 * from the bond itself or a VLAN directly above the bond.
66562306a36Sopenharmony_ci	 */
66662306a36Sopenharmony_ci	if (!bond_slave_has_mac_rcu(bond, arp->mac_src))
66762306a36Sopenharmony_ci		return NULL;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	dev = ip_dev_find(dev_net(bond->dev), arp->ip_src);
67062306a36Sopenharmony_ci	if (dev) {
67162306a36Sopenharmony_ci		if (netif_is_any_bridge_master(dev)) {
67262306a36Sopenharmony_ci			dev_put(dev);
67362306a36Sopenharmony_ci			return NULL;
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci		dev_put(dev);
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (arp->op_code == htons(ARPOP_REPLY)) {
67962306a36Sopenharmony_ci		/* the arp must be sent on the selected rx channel */
68062306a36Sopenharmony_ci		tx_slave = rlb_choose_channel(skb, bond, arp);
68162306a36Sopenharmony_ci		if (tx_slave)
68262306a36Sopenharmony_ci			bond_hw_addr_copy(arp->mac_src, tx_slave->dev->dev_addr,
68362306a36Sopenharmony_ci					  tx_slave->dev->addr_len);
68462306a36Sopenharmony_ci		netdev_dbg(bond->dev, "(slave %s): Server sent ARP Reply packet\n",
68562306a36Sopenharmony_ci			   tx_slave ? tx_slave->dev->name : "NULL");
68662306a36Sopenharmony_ci	} else if (arp->op_code == htons(ARPOP_REQUEST)) {
68762306a36Sopenharmony_ci		/* Create an entry in the rx_hashtbl for this client as a
68862306a36Sopenharmony_ci		 * place holder.
68962306a36Sopenharmony_ci		 * When the arp reply is received the entry will be updated
69062306a36Sopenharmony_ci		 * with the correct unicast address of the client.
69162306a36Sopenharmony_ci		 */
69262306a36Sopenharmony_ci		tx_slave = rlb_choose_channel(skb, bond, arp);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		/* The ARP reply packets must be delayed so that
69562306a36Sopenharmony_ci		 * they can cancel out the influence of the ARP request.
69662306a36Sopenharmony_ci		 */
69762306a36Sopenharmony_ci		bond->alb_info.rlb_update_delay_counter = RLB_UPDATE_DELAY;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci		/* arp requests are broadcast and are sent on the primary
70062306a36Sopenharmony_ci		 * the arp request will collapse all clients on the subnet to
70162306a36Sopenharmony_ci		 * the primary slave. We must register these clients to be
70262306a36Sopenharmony_ci		 * updated with their assigned mac.
70362306a36Sopenharmony_ci		 */
70462306a36Sopenharmony_ci		rlb_req_update_subnet_clients(bond, arp->ip_src);
70562306a36Sopenharmony_ci		netdev_dbg(bond->dev, "(slave %s): Server sent ARP Request packet\n",
70662306a36Sopenharmony_ci			   tx_slave ? tx_slave->dev->name : "NULL");
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	return tx_slave;
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic void rlb_rebalance(struct bonding *bond)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
71562306a36Sopenharmony_ci	struct slave *assigned_slave;
71662306a36Sopenharmony_ci	struct rlb_client_info *client_info;
71762306a36Sopenharmony_ci	int ntt;
71862306a36Sopenharmony_ci	u32 hash_index;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	ntt = 0;
72362306a36Sopenharmony_ci	hash_index = bond_info->rx_hashtbl_used_head;
72462306a36Sopenharmony_ci	for (; hash_index != RLB_NULL_INDEX;
72562306a36Sopenharmony_ci	     hash_index = client_info->used_next) {
72662306a36Sopenharmony_ci		client_info = &(bond_info->rx_hashtbl[hash_index]);
72762306a36Sopenharmony_ci		assigned_slave = __rlb_next_rx_slave(bond);
72862306a36Sopenharmony_ci		if (assigned_slave && (client_info->slave != assigned_slave)) {
72962306a36Sopenharmony_ci			client_info->slave = assigned_slave;
73062306a36Sopenharmony_ci			if (!is_zero_ether_addr(client_info->mac_dst)) {
73162306a36Sopenharmony_ci				client_info->ntt = 1;
73262306a36Sopenharmony_ci				ntt = 1;
73362306a36Sopenharmony_ci			}
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/* update the team's flag only after the whole iteration */
73862306a36Sopenharmony_ci	if (ntt)
73962306a36Sopenharmony_ci		bond_info->rx_ntt = 1;
74062306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/* Caller must hold mode_lock */
74462306a36Sopenharmony_cistatic void rlb_init_table_entry_dst(struct rlb_client_info *entry)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	entry->used_next = RLB_NULL_INDEX;
74762306a36Sopenharmony_ci	entry->used_prev = RLB_NULL_INDEX;
74862306a36Sopenharmony_ci	entry->assigned = 0;
74962306a36Sopenharmony_ci	entry->slave = NULL;
75062306a36Sopenharmony_ci	entry->vlan_id = 0;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_cistatic void rlb_init_table_entry_src(struct rlb_client_info *entry)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	entry->src_first = RLB_NULL_INDEX;
75562306a36Sopenharmony_ci	entry->src_prev = RLB_NULL_INDEX;
75662306a36Sopenharmony_ci	entry->src_next = RLB_NULL_INDEX;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic void rlb_init_table_entry(struct rlb_client_info *entry)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	memset(entry, 0, sizeof(struct rlb_client_info));
76262306a36Sopenharmony_ci	rlb_init_table_entry_dst(entry);
76362306a36Sopenharmony_ci	rlb_init_table_entry_src(entry);
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_cistatic void rlb_delete_table_entry_dst(struct bonding *bond, u32 index)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
76962306a36Sopenharmony_ci	u32 next_index = bond_info->rx_hashtbl[index].used_next;
77062306a36Sopenharmony_ci	u32 prev_index = bond_info->rx_hashtbl[index].used_prev;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (index == bond_info->rx_hashtbl_used_head)
77362306a36Sopenharmony_ci		bond_info->rx_hashtbl_used_head = next_index;
77462306a36Sopenharmony_ci	if (prev_index != RLB_NULL_INDEX)
77562306a36Sopenharmony_ci		bond_info->rx_hashtbl[prev_index].used_next = next_index;
77662306a36Sopenharmony_ci	if (next_index != RLB_NULL_INDEX)
77762306a36Sopenharmony_ci		bond_info->rx_hashtbl[next_index].used_prev = prev_index;
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci/* unlink a rlb hash table entry from the src list */
78162306a36Sopenharmony_cistatic void rlb_src_unlink(struct bonding *bond, u32 index)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
78462306a36Sopenharmony_ci	u32 next_index = bond_info->rx_hashtbl[index].src_next;
78562306a36Sopenharmony_ci	u32 prev_index = bond_info->rx_hashtbl[index].src_prev;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	bond_info->rx_hashtbl[index].src_next = RLB_NULL_INDEX;
78862306a36Sopenharmony_ci	bond_info->rx_hashtbl[index].src_prev = RLB_NULL_INDEX;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (next_index != RLB_NULL_INDEX)
79162306a36Sopenharmony_ci		bond_info->rx_hashtbl[next_index].src_prev = prev_index;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	if (prev_index == RLB_NULL_INDEX)
79462306a36Sopenharmony_ci		return;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* is prev_index pointing to the head of this list? */
79762306a36Sopenharmony_ci	if (bond_info->rx_hashtbl[prev_index].src_first == index)
79862306a36Sopenharmony_ci		bond_info->rx_hashtbl[prev_index].src_first = next_index;
79962306a36Sopenharmony_ci	else
80062306a36Sopenharmony_ci		bond_info->rx_hashtbl[prev_index].src_next = next_index;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic void rlb_delete_table_entry(struct bonding *bond, u32 index)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
80762306a36Sopenharmony_ci	struct rlb_client_info *entry = &(bond_info->rx_hashtbl[index]);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	rlb_delete_table_entry_dst(bond, index);
81062306a36Sopenharmony_ci	rlb_init_table_entry_dst(entry);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	rlb_src_unlink(bond, index);
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci/* add the rx_hashtbl[ip_dst_hash] entry to the list
81662306a36Sopenharmony_ci * of entries with identical ip_src_hash
81762306a36Sopenharmony_ci */
81862306a36Sopenharmony_cistatic void rlb_src_link(struct bonding *bond, u32 ip_src_hash, u32 ip_dst_hash)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
82162306a36Sopenharmony_ci	u32 next;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	bond_info->rx_hashtbl[ip_dst_hash].src_prev = ip_src_hash;
82462306a36Sopenharmony_ci	next = bond_info->rx_hashtbl[ip_src_hash].src_first;
82562306a36Sopenharmony_ci	bond_info->rx_hashtbl[ip_dst_hash].src_next = next;
82662306a36Sopenharmony_ci	if (next != RLB_NULL_INDEX)
82762306a36Sopenharmony_ci		bond_info->rx_hashtbl[next].src_prev = ip_dst_hash;
82862306a36Sopenharmony_ci	bond_info->rx_hashtbl[ip_src_hash].src_first = ip_dst_hash;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci/* deletes all rx_hashtbl entries with arp->ip_src if their mac_src does
83262306a36Sopenharmony_ci * not match arp->mac_src
83362306a36Sopenharmony_ci */
83462306a36Sopenharmony_cistatic void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
83762306a36Sopenharmony_ci	u32 ip_src_hash = _simple_hash((u8 *)&(arp->ip_src), sizeof(arp->ip_src));
83862306a36Sopenharmony_ci	u32 index;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	index = bond_info->rx_hashtbl[ip_src_hash].src_first;
84362306a36Sopenharmony_ci	while (index != RLB_NULL_INDEX) {
84462306a36Sopenharmony_ci		struct rlb_client_info *entry = &(bond_info->rx_hashtbl[index]);
84562306a36Sopenharmony_ci		u32 next_index = entry->src_next;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci		if (entry->ip_src == arp->ip_src &&
84862306a36Sopenharmony_ci		    !ether_addr_equal_64bits(arp->mac_src, entry->mac_src))
84962306a36Sopenharmony_ci			rlb_delete_table_entry(bond, index);
85062306a36Sopenharmony_ci		index = next_index;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic int rlb_initialize(struct bonding *bond)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
85862306a36Sopenharmony_ci	struct rlb_client_info	*new_hashtbl;
85962306a36Sopenharmony_ci	int size = RLB_HASH_TABLE_SIZE * sizeof(struct rlb_client_info);
86062306a36Sopenharmony_ci	int i;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	new_hashtbl = kmalloc(size, GFP_KERNEL);
86362306a36Sopenharmony_ci	if (!new_hashtbl)
86462306a36Sopenharmony_ci		return -1;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	bond_info->rx_hashtbl = new_hashtbl;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	for (i = 0; i < RLB_HASH_TABLE_SIZE; i++)
87362306a36Sopenharmony_ci		rlb_init_table_entry(bond_info->rx_hashtbl + i);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* register to receive ARPs */
87862306a36Sopenharmony_ci	bond->recv_probe = rlb_arp_recv;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	return 0;
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic void rlb_deinitialize(struct bonding *bond)
88462306a36Sopenharmony_ci{
88562306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	kfree(bond_info->rx_hashtbl);
89062306a36Sopenharmony_ci	bond_info->rx_hashtbl = NULL;
89162306a36Sopenharmony_ci	bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cistatic void rlb_clear_vlan(struct bonding *bond, unsigned short vlan_id)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
89962306a36Sopenharmony_ci	u32 curr_index;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	curr_index = bond_info->rx_hashtbl_used_head;
90462306a36Sopenharmony_ci	while (curr_index != RLB_NULL_INDEX) {
90562306a36Sopenharmony_ci		struct rlb_client_info *curr = &(bond_info->rx_hashtbl[curr_index]);
90662306a36Sopenharmony_ci		u32 next_index = bond_info->rx_hashtbl[curr_index].used_next;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci		if (curr->vlan_id == vlan_id)
90962306a36Sopenharmony_ci			rlb_delete_table_entry(bond, curr_index);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		curr_index = next_index;
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci/*********************** tlb/rlb shared functions *********************/
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic void alb_send_lp_vid(struct slave *slave, const u8 mac_addr[],
92062306a36Sopenharmony_ci			    __be16 vlan_proto, u16 vid)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct learning_pkt pkt;
92362306a36Sopenharmony_ci	struct sk_buff *skb;
92462306a36Sopenharmony_ci	int size = sizeof(struct learning_pkt);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	memset(&pkt, 0, size);
92762306a36Sopenharmony_ci	ether_addr_copy(pkt.mac_dst, mac_addr);
92862306a36Sopenharmony_ci	ether_addr_copy(pkt.mac_src, mac_addr);
92962306a36Sopenharmony_ci	pkt.type = cpu_to_be16(ETH_P_LOOPBACK);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	skb = dev_alloc_skb(size);
93262306a36Sopenharmony_ci	if (!skb)
93362306a36Sopenharmony_ci		return;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	skb_put_data(skb, &pkt, size);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	skb_reset_mac_header(skb);
93862306a36Sopenharmony_ci	skb->network_header = skb->mac_header + ETH_HLEN;
93962306a36Sopenharmony_ci	skb->protocol = pkt.type;
94062306a36Sopenharmony_ci	skb->priority = TC_PRIO_CONTROL;
94162306a36Sopenharmony_ci	skb->dev = slave->dev;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	slave_dbg(slave->bond->dev, slave->dev,
94462306a36Sopenharmony_ci		  "Send learning packet: mac %pM vlan %d\n", mac_addr, vid);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (vid)
94762306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, vlan_proto, vid);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	dev_queue_xmit(skb);
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_cistruct alb_walk_data {
95362306a36Sopenharmony_ci	struct bonding *bond;
95462306a36Sopenharmony_ci	struct slave *slave;
95562306a36Sopenharmony_ci	const u8 *mac_addr;
95662306a36Sopenharmony_ci	bool strict_match;
95762306a36Sopenharmony_ci};
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic int alb_upper_dev_walk(struct net_device *upper,
96062306a36Sopenharmony_ci			      struct netdev_nested_priv *priv)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct alb_walk_data *data = (struct alb_walk_data *)priv->data;
96362306a36Sopenharmony_ci	bool strict_match = data->strict_match;
96462306a36Sopenharmony_ci	const u8 *mac_addr = data->mac_addr;
96562306a36Sopenharmony_ci	struct bonding *bond = data->bond;
96662306a36Sopenharmony_ci	struct slave *slave = data->slave;
96762306a36Sopenharmony_ci	struct bond_vlan_tag *tags;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	if (is_vlan_dev(upper) &&
97062306a36Sopenharmony_ci	    bond->dev->lower_level == upper->lower_level - 1) {
97162306a36Sopenharmony_ci		if (upper->addr_assign_type == NET_ADDR_STOLEN) {
97262306a36Sopenharmony_ci			alb_send_lp_vid(slave, mac_addr,
97362306a36Sopenharmony_ci					vlan_dev_vlan_proto(upper),
97462306a36Sopenharmony_ci					vlan_dev_vlan_id(upper));
97562306a36Sopenharmony_ci		} else {
97662306a36Sopenharmony_ci			alb_send_lp_vid(slave, upper->dev_addr,
97762306a36Sopenharmony_ci					vlan_dev_vlan_proto(upper),
97862306a36Sopenharmony_ci					vlan_dev_vlan_id(upper));
97962306a36Sopenharmony_ci		}
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/* If this is a macvlan device, then only send updates
98362306a36Sopenharmony_ci	 * when strict_match is turned off.
98462306a36Sopenharmony_ci	 */
98562306a36Sopenharmony_ci	if (netif_is_macvlan(upper) && !strict_match) {
98662306a36Sopenharmony_ci		tags = bond_verify_device_path(bond->dev, upper, 0);
98762306a36Sopenharmony_ci		if (IS_ERR_OR_NULL(tags))
98862306a36Sopenharmony_ci			return -ENOMEM;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci		alb_send_lp_vid(slave, upper->dev_addr,
99162306a36Sopenharmony_ci				tags[0].vlan_proto, tags[0].vlan_id);
99262306a36Sopenharmony_ci		kfree(tags);
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	return 0;
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic void alb_send_learning_packets(struct slave *slave, const u8 mac_addr[],
99962306a36Sopenharmony_ci				      bool strict_match)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	struct bonding *bond = bond_get_bond_by_slave(slave);
100262306a36Sopenharmony_ci	struct netdev_nested_priv priv;
100362306a36Sopenharmony_ci	struct alb_walk_data data = {
100462306a36Sopenharmony_ci		.strict_match = strict_match,
100562306a36Sopenharmony_ci		.mac_addr = mac_addr,
100662306a36Sopenharmony_ci		.slave = slave,
100762306a36Sopenharmony_ci		.bond = bond,
100862306a36Sopenharmony_ci	};
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	priv.data = (void *)&data;
101162306a36Sopenharmony_ci	/* send untagged */
101262306a36Sopenharmony_ci	alb_send_lp_vid(slave, mac_addr, 0, 0);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	/* loop through all devices and see if we need to send a packet
101562306a36Sopenharmony_ci	 * for that device.
101662306a36Sopenharmony_ci	 */
101762306a36Sopenharmony_ci	rcu_read_lock();
101862306a36Sopenharmony_ci	netdev_walk_all_upper_dev_rcu(bond->dev, alb_upper_dev_walk, &priv);
101962306a36Sopenharmony_ci	rcu_read_unlock();
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_cistatic int alb_set_slave_mac_addr(struct slave *slave, const u8 addr[],
102362306a36Sopenharmony_ci				  unsigned int len)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	struct net_device *dev = slave->dev;
102662306a36Sopenharmony_ci	struct sockaddr_storage ss;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (BOND_MODE(slave->bond) == BOND_MODE_TLB) {
102962306a36Sopenharmony_ci		__dev_addr_set(dev, addr, len);
103062306a36Sopenharmony_ci		return 0;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/* for rlb each slave must have a unique hw mac addresses so that
103462306a36Sopenharmony_ci	 * each slave will receive packets destined to a different mac
103562306a36Sopenharmony_ci	 */
103662306a36Sopenharmony_ci	memcpy(ss.__data, addr, len);
103762306a36Sopenharmony_ci	ss.ss_family = dev->type;
103862306a36Sopenharmony_ci	if (dev_set_mac_address(dev, (struct sockaddr *)&ss, NULL)) {
103962306a36Sopenharmony_ci		slave_err(slave->bond->dev, dev, "dev_set_mac_address on slave failed! ALB mode requires that the base driver support setting the hw address also when the network device's interface is open\n");
104062306a36Sopenharmony_ci		return -EOPNOTSUPP;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci	return 0;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci/* Swap MAC addresses between two slaves.
104662306a36Sopenharmony_ci *
104762306a36Sopenharmony_ci * Called with RTNL held, and no other locks.
104862306a36Sopenharmony_ci */
104962306a36Sopenharmony_cistatic void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	u8 tmp_mac_addr[MAX_ADDR_LEN];
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	bond_hw_addr_copy(tmp_mac_addr, slave1->dev->dev_addr,
105462306a36Sopenharmony_ci			  slave1->dev->addr_len);
105562306a36Sopenharmony_ci	alb_set_slave_mac_addr(slave1, slave2->dev->dev_addr,
105662306a36Sopenharmony_ci			       slave2->dev->addr_len);
105762306a36Sopenharmony_ci	alb_set_slave_mac_addr(slave2, tmp_mac_addr,
105862306a36Sopenharmony_ci			       slave1->dev->addr_len);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci/* Send learning packets after MAC address swap.
106362306a36Sopenharmony_ci *
106462306a36Sopenharmony_ci * Called with RTNL and no other locks
106562306a36Sopenharmony_ci */
106662306a36Sopenharmony_cistatic void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
106762306a36Sopenharmony_ci				struct slave *slave2)
106862306a36Sopenharmony_ci{
106962306a36Sopenharmony_ci	int slaves_state_differ = (bond_slave_can_tx(slave1) != bond_slave_can_tx(slave2));
107062306a36Sopenharmony_ci	struct slave *disabled_slave = NULL;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	ASSERT_RTNL();
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	/* fasten the change in the switch */
107562306a36Sopenharmony_ci	if (bond_slave_can_tx(slave1)) {
107662306a36Sopenharmony_ci		alb_send_learning_packets(slave1, slave1->dev->dev_addr, false);
107762306a36Sopenharmony_ci		if (bond->alb_info.rlb_enabled) {
107862306a36Sopenharmony_ci			/* inform the clients that the mac address
107962306a36Sopenharmony_ci			 * has changed
108062306a36Sopenharmony_ci			 */
108162306a36Sopenharmony_ci			rlb_req_update_slave_clients(bond, slave1);
108262306a36Sopenharmony_ci		}
108362306a36Sopenharmony_ci	} else {
108462306a36Sopenharmony_ci		disabled_slave = slave1;
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	if (bond_slave_can_tx(slave2)) {
108862306a36Sopenharmony_ci		alb_send_learning_packets(slave2, slave2->dev->dev_addr, false);
108962306a36Sopenharmony_ci		if (bond->alb_info.rlb_enabled) {
109062306a36Sopenharmony_ci			/* inform the clients that the mac address
109162306a36Sopenharmony_ci			 * has changed
109262306a36Sopenharmony_ci			 */
109362306a36Sopenharmony_ci			rlb_req_update_slave_clients(bond, slave2);
109462306a36Sopenharmony_ci		}
109562306a36Sopenharmony_ci	} else {
109662306a36Sopenharmony_ci		disabled_slave = slave2;
109762306a36Sopenharmony_ci	}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	if (bond->alb_info.rlb_enabled && slaves_state_differ) {
110062306a36Sopenharmony_ci		/* A disabled slave was assigned an active mac addr */
110162306a36Sopenharmony_ci		rlb_teach_disabled_mac_on_primary(bond,
110262306a36Sopenharmony_ci						  disabled_slave->dev->dev_addr);
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci/**
110762306a36Sopenharmony_ci * alb_change_hw_addr_on_detach
110862306a36Sopenharmony_ci * @bond: bonding we're working on
110962306a36Sopenharmony_ci * @slave: the slave that was just detached
111062306a36Sopenharmony_ci *
111162306a36Sopenharmony_ci * We assume that @slave was already detached from the slave list.
111262306a36Sopenharmony_ci *
111362306a36Sopenharmony_ci * If @slave's permanent hw address is different both from its current
111462306a36Sopenharmony_ci * address and from @bond's address, then somewhere in the bond there's
111562306a36Sopenharmony_ci * a slave that has @slave's permanet address as its current address.
111662306a36Sopenharmony_ci * We'll make sure that slave no longer uses @slave's permanent address.
111762306a36Sopenharmony_ci *
111862306a36Sopenharmony_ci * Caller must hold RTNL and no other locks
111962306a36Sopenharmony_ci */
112062306a36Sopenharmony_cistatic void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *slave)
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	int perm_curr_diff;
112362306a36Sopenharmony_ci	int perm_bond_diff;
112462306a36Sopenharmony_ci	struct slave *found_slave;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	perm_curr_diff = !ether_addr_equal_64bits(slave->perm_hwaddr,
112762306a36Sopenharmony_ci						  slave->dev->dev_addr);
112862306a36Sopenharmony_ci	perm_bond_diff = !ether_addr_equal_64bits(slave->perm_hwaddr,
112962306a36Sopenharmony_ci						  bond->dev->dev_addr);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	if (perm_curr_diff && perm_bond_diff) {
113262306a36Sopenharmony_ci		found_slave = bond_slave_has_mac(bond, slave->perm_hwaddr);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci		if (found_slave) {
113562306a36Sopenharmony_ci			alb_swap_mac_addr(slave, found_slave);
113662306a36Sopenharmony_ci			alb_fasten_mac_swap(bond, slave, found_slave);
113762306a36Sopenharmony_ci		}
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci/**
114262306a36Sopenharmony_ci * alb_handle_addr_collision_on_attach
114362306a36Sopenharmony_ci * @bond: bonding we're working on
114462306a36Sopenharmony_ci * @slave: the slave that was just attached
114562306a36Sopenharmony_ci *
114662306a36Sopenharmony_ci * checks uniqueness of slave's mac address and handles the case the
114762306a36Sopenharmony_ci * new slave uses the bonds mac address.
114862306a36Sopenharmony_ci *
114962306a36Sopenharmony_ci * If the permanent hw address of @slave is @bond's hw address, we need to
115062306a36Sopenharmony_ci * find a different hw address to give @slave, that isn't in use by any other
115162306a36Sopenharmony_ci * slave in the bond. This address must be, of course, one of the permanent
115262306a36Sopenharmony_ci * addresses of the other slaves.
115362306a36Sopenharmony_ci *
115462306a36Sopenharmony_ci * We go over the slave list, and for each slave there we compare its
115562306a36Sopenharmony_ci * permanent hw address with the current address of all the other slaves.
115662306a36Sopenharmony_ci * If no match was found, then we've found a slave with a permanent address
115762306a36Sopenharmony_ci * that isn't used by any other slave in the bond, so we can assign it to
115862306a36Sopenharmony_ci * @slave.
115962306a36Sopenharmony_ci *
116062306a36Sopenharmony_ci * assumption: this function is called before @slave is attached to the
116162306a36Sopenharmony_ci *	       bond slave list.
116262306a36Sopenharmony_ci */
116362306a36Sopenharmony_cistatic int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slave *slave)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	struct slave *has_bond_addr = rcu_access_pointer(bond->curr_active_slave);
116662306a36Sopenharmony_ci	struct slave *tmp_slave1, *free_mac_slave = NULL;
116762306a36Sopenharmony_ci	struct list_head *iter;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (!bond_has_slaves(bond)) {
117062306a36Sopenharmony_ci		/* this is the first slave */
117162306a36Sopenharmony_ci		return 0;
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	/* if slave's mac address differs from bond's mac address
117562306a36Sopenharmony_ci	 * check uniqueness of slave's mac address against the other
117662306a36Sopenharmony_ci	 * slaves in the bond.
117762306a36Sopenharmony_ci	 */
117862306a36Sopenharmony_ci	if (!ether_addr_equal_64bits(slave->perm_hwaddr, bond->dev->dev_addr)) {
117962306a36Sopenharmony_ci		if (!bond_slave_has_mac(bond, slave->dev->dev_addr))
118062306a36Sopenharmony_ci			return 0;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci		/* Try setting slave mac to bond address and fall-through
118362306a36Sopenharmony_ci		 * to code handling that situation below...
118462306a36Sopenharmony_ci		 */
118562306a36Sopenharmony_ci		alb_set_slave_mac_addr(slave, bond->dev->dev_addr,
118662306a36Sopenharmony_ci				       bond->dev->addr_len);
118762306a36Sopenharmony_ci	}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	/* The slave's address is equal to the address of the bond.
119062306a36Sopenharmony_ci	 * Search for a spare address in the bond for this slave.
119162306a36Sopenharmony_ci	 */
119262306a36Sopenharmony_ci	bond_for_each_slave(bond, tmp_slave1, iter) {
119362306a36Sopenharmony_ci		if (!bond_slave_has_mac(bond, tmp_slave1->perm_hwaddr)) {
119462306a36Sopenharmony_ci			/* no slave has tmp_slave1's perm addr
119562306a36Sopenharmony_ci			 * as its curr addr
119662306a36Sopenharmony_ci			 */
119762306a36Sopenharmony_ci			free_mac_slave = tmp_slave1;
119862306a36Sopenharmony_ci			break;
119962306a36Sopenharmony_ci		}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		if (!has_bond_addr) {
120262306a36Sopenharmony_ci			if (ether_addr_equal_64bits(tmp_slave1->dev->dev_addr,
120362306a36Sopenharmony_ci						    bond->dev->dev_addr)) {
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci				has_bond_addr = tmp_slave1;
120662306a36Sopenharmony_ci			}
120762306a36Sopenharmony_ci		}
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	if (free_mac_slave) {
121162306a36Sopenharmony_ci		alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr,
121262306a36Sopenharmony_ci				       free_mac_slave->dev->addr_len);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci		slave_warn(bond->dev, slave->dev, "the slave hw address is in use by the bond; giving it the hw address of %s\n",
121562306a36Sopenharmony_ci			   free_mac_slave->dev->name);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	} else if (has_bond_addr) {
121862306a36Sopenharmony_ci		slave_err(bond->dev, slave->dev, "the slave hw address 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");
121962306a36Sopenharmony_ci		return -EFAULT;
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	return 0;
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci/**
122662306a36Sopenharmony_ci * alb_set_mac_address
122762306a36Sopenharmony_ci * @bond: bonding we're working on
122862306a36Sopenharmony_ci * @addr: MAC address to set
122962306a36Sopenharmony_ci *
123062306a36Sopenharmony_ci * In TLB mode all slaves are configured to the bond's hw address, but set
123162306a36Sopenharmony_ci * their dev_addr field to different addresses (based on their permanent hw
123262306a36Sopenharmony_ci * addresses).
123362306a36Sopenharmony_ci *
123462306a36Sopenharmony_ci * For each slave, this function sets the interface to the new address and then
123562306a36Sopenharmony_ci * changes its dev_addr field to its previous value.
123662306a36Sopenharmony_ci *
123762306a36Sopenharmony_ci * Unwinding assumes bond's mac address has not yet changed.
123862306a36Sopenharmony_ci */
123962306a36Sopenharmony_cistatic int alb_set_mac_address(struct bonding *bond, void *addr)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	struct slave *slave, *rollback_slave;
124262306a36Sopenharmony_ci	struct list_head *iter;
124362306a36Sopenharmony_ci	struct sockaddr_storage ss;
124462306a36Sopenharmony_ci	char tmp_addr[MAX_ADDR_LEN];
124562306a36Sopenharmony_ci	int res;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	if (bond->alb_info.rlb_enabled)
124862306a36Sopenharmony_ci		return 0;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	bond_for_each_slave(bond, slave, iter) {
125162306a36Sopenharmony_ci		/* save net_device's current hw address */
125262306a36Sopenharmony_ci		bond_hw_addr_copy(tmp_addr, slave->dev->dev_addr,
125362306a36Sopenharmony_ci				  slave->dev->addr_len);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci		res = dev_set_mac_address(slave->dev, addr, NULL);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci		/* restore net_device's hw address */
125862306a36Sopenharmony_ci		dev_addr_set(slave->dev, tmp_addr);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci		if (res)
126162306a36Sopenharmony_ci			goto unwind;
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	return 0;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ciunwind:
126762306a36Sopenharmony_ci	memcpy(ss.__data, bond->dev->dev_addr, bond->dev->addr_len);
126862306a36Sopenharmony_ci	ss.ss_family = bond->dev->type;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	/* unwind from head to the slave that failed */
127162306a36Sopenharmony_ci	bond_for_each_slave(bond, rollback_slave, iter) {
127262306a36Sopenharmony_ci		if (rollback_slave == slave)
127362306a36Sopenharmony_ci			break;
127462306a36Sopenharmony_ci		bond_hw_addr_copy(tmp_addr, rollback_slave->dev->dev_addr,
127562306a36Sopenharmony_ci				  rollback_slave->dev->addr_len);
127662306a36Sopenharmony_ci		dev_set_mac_address(rollback_slave->dev,
127762306a36Sopenharmony_ci				    (struct sockaddr *)&ss, NULL);
127862306a36Sopenharmony_ci		dev_addr_set(rollback_slave->dev, tmp_addr);
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	return res;
128262306a36Sopenharmony_ci}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci/* determine if the packet is NA or NS */
128562306a36Sopenharmony_cistatic bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	struct ipv6hdr *ip6hdr;
128862306a36Sopenharmony_ci	struct icmp6hdr *hdr;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	if (!pskb_network_may_pull(skb, sizeof(*ip6hdr)))
129162306a36Sopenharmony_ci		return true;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	ip6hdr = ipv6_hdr(skb);
129462306a36Sopenharmony_ci	if (ip6hdr->nexthdr != IPPROTO_ICMPV6)
129562306a36Sopenharmony_ci		return false;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	if (!pskb_network_may_pull(skb, sizeof(*ip6hdr) + sizeof(*hdr)))
129862306a36Sopenharmony_ci		return true;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	hdr = icmp6_hdr(skb);
130162306a36Sopenharmony_ci	return hdr->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT ||
130262306a36Sopenharmony_ci		hdr->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION;
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci/************************ exported alb functions ************************/
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ciint bond_alb_initialize(struct bonding *bond, int rlb_enabled)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	int res;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	res = tlb_initialize(bond);
131262306a36Sopenharmony_ci	if (res)
131362306a36Sopenharmony_ci		return res;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	if (rlb_enabled) {
131662306a36Sopenharmony_ci		res = rlb_initialize(bond);
131762306a36Sopenharmony_ci		if (res) {
131862306a36Sopenharmony_ci			tlb_deinitialize(bond);
131962306a36Sopenharmony_ci			return res;
132062306a36Sopenharmony_ci		}
132162306a36Sopenharmony_ci		bond->alb_info.rlb_enabled = 1;
132262306a36Sopenharmony_ci	} else {
132362306a36Sopenharmony_ci		bond->alb_info.rlb_enabled = 0;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	return 0;
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_civoid bond_alb_deinitialize(struct bonding *bond)
133062306a36Sopenharmony_ci{
133162306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	tlb_deinitialize(bond);
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	if (bond_info->rlb_enabled)
133662306a36Sopenharmony_ci		rlb_deinitialize(bond);
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_cistatic netdev_tx_t bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,
134062306a36Sopenharmony_ci				    struct slave *tx_slave)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
134362306a36Sopenharmony_ci	struct ethhdr *eth_data = eth_hdr(skb);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	if (!tx_slave) {
134662306a36Sopenharmony_ci		/* unbalanced or unassigned, send through primary */
134762306a36Sopenharmony_ci		tx_slave = rcu_dereference(bond->curr_active_slave);
134862306a36Sopenharmony_ci		if (bond->params.tlb_dynamic_lb)
134962306a36Sopenharmony_ci			bond_info->unbalanced_load += skb->len;
135062306a36Sopenharmony_ci	}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	if (tx_slave && bond_slave_can_tx(tx_slave)) {
135362306a36Sopenharmony_ci		if (tx_slave != rcu_access_pointer(bond->curr_active_slave)) {
135462306a36Sopenharmony_ci			ether_addr_copy(eth_data->h_source,
135562306a36Sopenharmony_ci					tx_slave->dev->dev_addr);
135662306a36Sopenharmony_ci		}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci		return bond_dev_queue_xmit(bond, skb, tx_slave->dev);
135962306a36Sopenharmony_ci	}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	if (tx_slave && bond->params.tlb_dynamic_lb) {
136262306a36Sopenharmony_ci		spin_lock(&bond->mode_lock);
136362306a36Sopenharmony_ci		__tlb_clear_slave(bond, tx_slave, 0);
136462306a36Sopenharmony_ci		spin_unlock(&bond->mode_lock);
136562306a36Sopenharmony_ci	}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	/* no suitable interface, frame not sent */
136862306a36Sopenharmony_ci	return bond_tx_drop(bond->dev, skb);
136962306a36Sopenharmony_ci}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_cistruct slave *bond_xmit_tlb_slave_get(struct bonding *bond,
137262306a36Sopenharmony_ci				      struct sk_buff *skb)
137362306a36Sopenharmony_ci{
137462306a36Sopenharmony_ci	struct slave *tx_slave = NULL;
137562306a36Sopenharmony_ci	struct ethhdr *eth_data;
137662306a36Sopenharmony_ci	u32 hash_index;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	skb_reset_mac_header(skb);
137962306a36Sopenharmony_ci	eth_data = eth_hdr(skb);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	/* Do not TX balance any multicast or broadcast */
138262306a36Sopenharmony_ci	if (!is_multicast_ether_addr(eth_data->h_dest)) {
138362306a36Sopenharmony_ci		switch (skb->protocol) {
138462306a36Sopenharmony_ci		case htons(ETH_P_IPV6):
138562306a36Sopenharmony_ci			if (alb_determine_nd(skb, bond))
138662306a36Sopenharmony_ci				break;
138762306a36Sopenharmony_ci			fallthrough;
138862306a36Sopenharmony_ci		case htons(ETH_P_IP):
138962306a36Sopenharmony_ci			hash_index = bond_xmit_hash(bond, skb);
139062306a36Sopenharmony_ci			if (bond->params.tlb_dynamic_lb) {
139162306a36Sopenharmony_ci				tx_slave = tlb_choose_channel(bond,
139262306a36Sopenharmony_ci							      hash_index & 0xFF,
139362306a36Sopenharmony_ci							      skb->len);
139462306a36Sopenharmony_ci			} else {
139562306a36Sopenharmony_ci				struct bond_up_slave *slaves;
139662306a36Sopenharmony_ci				unsigned int count;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci				slaves = rcu_dereference(bond->usable_slaves);
139962306a36Sopenharmony_ci				count = slaves ? READ_ONCE(slaves->count) : 0;
140062306a36Sopenharmony_ci				if (likely(count))
140162306a36Sopenharmony_ci					tx_slave = slaves->arr[hash_index %
140262306a36Sopenharmony_ci							       count];
140362306a36Sopenharmony_ci			}
140462306a36Sopenharmony_ci			break;
140562306a36Sopenharmony_ci		}
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci	return tx_slave;
140862306a36Sopenharmony_ci}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_cinetdev_tx_t bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
141162306a36Sopenharmony_ci{
141262306a36Sopenharmony_ci	struct bonding *bond = netdev_priv(bond_dev);
141362306a36Sopenharmony_ci	struct slave *tx_slave;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	tx_slave = bond_xmit_tlb_slave_get(bond, skb);
141662306a36Sopenharmony_ci	return bond_do_alb_xmit(skb, bond, tx_slave);
141762306a36Sopenharmony_ci}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_cistruct slave *bond_xmit_alb_slave_get(struct bonding *bond,
142062306a36Sopenharmony_ci				      struct sk_buff *skb)
142162306a36Sopenharmony_ci{
142262306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
142362306a36Sopenharmony_ci	static const __be32 ip_bcast = htonl(0xffffffff);
142462306a36Sopenharmony_ci	struct slave *tx_slave = NULL;
142562306a36Sopenharmony_ci	const u8 *hash_start = NULL;
142662306a36Sopenharmony_ci	bool do_tx_balance = true;
142762306a36Sopenharmony_ci	struct ethhdr *eth_data;
142862306a36Sopenharmony_ci	u32 hash_index = 0;
142962306a36Sopenharmony_ci	int hash_size = 0;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	skb_reset_mac_header(skb);
143262306a36Sopenharmony_ci	eth_data = eth_hdr(skb);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	switch (ntohs(skb->protocol)) {
143562306a36Sopenharmony_ci	case ETH_P_IP: {
143662306a36Sopenharmony_ci		const struct iphdr *iph;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci		if (is_broadcast_ether_addr(eth_data->h_dest) ||
143962306a36Sopenharmony_ci		    !pskb_network_may_pull(skb, sizeof(*iph))) {
144062306a36Sopenharmony_ci			do_tx_balance = false;
144162306a36Sopenharmony_ci			break;
144262306a36Sopenharmony_ci		}
144362306a36Sopenharmony_ci		iph = ip_hdr(skb);
144462306a36Sopenharmony_ci		if (iph->daddr == ip_bcast || iph->protocol == IPPROTO_IGMP) {
144562306a36Sopenharmony_ci			do_tx_balance = false;
144662306a36Sopenharmony_ci			break;
144762306a36Sopenharmony_ci		}
144862306a36Sopenharmony_ci		hash_start = (char *)&(iph->daddr);
144962306a36Sopenharmony_ci		hash_size = sizeof(iph->daddr);
145062306a36Sopenharmony_ci		break;
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci	case ETH_P_IPV6: {
145362306a36Sopenharmony_ci		const struct ipv6hdr *ip6hdr;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci		/* IPv6 doesn't really use broadcast mac address, but leave
145662306a36Sopenharmony_ci		 * that here just in case.
145762306a36Sopenharmony_ci		 */
145862306a36Sopenharmony_ci		if (is_broadcast_ether_addr(eth_data->h_dest)) {
145962306a36Sopenharmony_ci			do_tx_balance = false;
146062306a36Sopenharmony_ci			break;
146162306a36Sopenharmony_ci		}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci		/* IPv6 uses all-nodes multicast as an equivalent to
146462306a36Sopenharmony_ci		 * broadcasts in IPv4.
146562306a36Sopenharmony_ci		 */
146662306a36Sopenharmony_ci		if (ether_addr_equal_64bits(eth_data->h_dest, mac_v6_allmcast)) {
146762306a36Sopenharmony_ci			do_tx_balance = false;
146862306a36Sopenharmony_ci			break;
146962306a36Sopenharmony_ci		}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci		if (alb_determine_nd(skb, bond)) {
147262306a36Sopenharmony_ci			do_tx_balance = false;
147362306a36Sopenharmony_ci			break;
147462306a36Sopenharmony_ci		}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci		/* The IPv6 header is pulled by alb_determine_nd */
147762306a36Sopenharmony_ci		/* Additionally, DAD probes should not be tx-balanced as that
147862306a36Sopenharmony_ci		 * will lead to false positives for duplicate addresses and
147962306a36Sopenharmony_ci		 * prevent address configuration from working.
148062306a36Sopenharmony_ci		 */
148162306a36Sopenharmony_ci		ip6hdr = ipv6_hdr(skb);
148262306a36Sopenharmony_ci		if (ipv6_addr_any(&ip6hdr->saddr)) {
148362306a36Sopenharmony_ci			do_tx_balance = false;
148462306a36Sopenharmony_ci			break;
148562306a36Sopenharmony_ci		}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		hash_start = (char *)&ip6hdr->daddr;
148862306a36Sopenharmony_ci		hash_size = sizeof(ip6hdr->daddr);
148962306a36Sopenharmony_ci		break;
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci	case ETH_P_ARP:
149262306a36Sopenharmony_ci		do_tx_balance = false;
149362306a36Sopenharmony_ci		if (bond_info->rlb_enabled)
149462306a36Sopenharmony_ci			tx_slave = rlb_arp_xmit(skb, bond);
149562306a36Sopenharmony_ci		break;
149662306a36Sopenharmony_ci	default:
149762306a36Sopenharmony_ci		do_tx_balance = false;
149862306a36Sopenharmony_ci		break;
149962306a36Sopenharmony_ci	}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	if (do_tx_balance) {
150262306a36Sopenharmony_ci		if (bond->params.tlb_dynamic_lb) {
150362306a36Sopenharmony_ci			hash_index = _simple_hash(hash_start, hash_size);
150462306a36Sopenharmony_ci			tx_slave = tlb_choose_channel(bond, hash_index, skb->len);
150562306a36Sopenharmony_ci		} else {
150662306a36Sopenharmony_ci			/*
150762306a36Sopenharmony_ci			 * do_tx_balance means we are free to select the tx_slave
150862306a36Sopenharmony_ci			 * So we do exactly what tlb would do for hash selection
150962306a36Sopenharmony_ci			 */
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci			struct bond_up_slave *slaves;
151262306a36Sopenharmony_ci			unsigned int count;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci			slaves = rcu_dereference(bond->usable_slaves);
151562306a36Sopenharmony_ci			count = slaves ? READ_ONCE(slaves->count) : 0;
151662306a36Sopenharmony_ci			if (likely(count))
151762306a36Sopenharmony_ci				tx_slave = slaves->arr[bond_xmit_hash(bond, skb) %
151862306a36Sopenharmony_ci						       count];
151962306a36Sopenharmony_ci		}
152062306a36Sopenharmony_ci	}
152162306a36Sopenharmony_ci	return tx_slave;
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cinetdev_tx_t bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	struct bonding *bond = netdev_priv(bond_dev);
152762306a36Sopenharmony_ci	struct slave *tx_slave = NULL;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	tx_slave = bond_xmit_alb_slave_get(bond, skb);
153062306a36Sopenharmony_ci	return bond_do_alb_xmit(skb, bond, tx_slave);
153162306a36Sopenharmony_ci}
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_civoid bond_alb_monitor(struct work_struct *work)
153462306a36Sopenharmony_ci{
153562306a36Sopenharmony_ci	struct bonding *bond = container_of(work, struct bonding,
153662306a36Sopenharmony_ci					    alb_work.work);
153762306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
153862306a36Sopenharmony_ci	struct list_head *iter;
153962306a36Sopenharmony_ci	struct slave *slave;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	if (!bond_has_slaves(bond)) {
154262306a36Sopenharmony_ci		atomic_set(&bond_info->tx_rebalance_counter, 0);
154362306a36Sopenharmony_ci		bond_info->lp_counter = 0;
154462306a36Sopenharmony_ci		goto re_arm;
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	rcu_read_lock();
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	atomic_inc(&bond_info->tx_rebalance_counter);
155062306a36Sopenharmony_ci	bond_info->lp_counter++;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	/* send learning packets */
155362306a36Sopenharmony_ci	if (bond_info->lp_counter >= BOND_ALB_LP_TICKS(bond)) {
155462306a36Sopenharmony_ci		bool strict_match;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci		bond_for_each_slave_rcu(bond, slave, iter) {
155762306a36Sopenharmony_ci			/* If updating current_active, use all currently
155862306a36Sopenharmony_ci			 * user mac addresses (!strict_match).  Otherwise, only
155962306a36Sopenharmony_ci			 * use mac of the slave device.
156062306a36Sopenharmony_ci			 * In RLB mode, we always use strict matches.
156162306a36Sopenharmony_ci			 */
156262306a36Sopenharmony_ci			strict_match = (slave != rcu_access_pointer(bond->curr_active_slave) ||
156362306a36Sopenharmony_ci					bond_info->rlb_enabled);
156462306a36Sopenharmony_ci			alb_send_learning_packets(slave, slave->dev->dev_addr,
156562306a36Sopenharmony_ci						  strict_match);
156662306a36Sopenharmony_ci		}
156762306a36Sopenharmony_ci		bond_info->lp_counter = 0;
156862306a36Sopenharmony_ci	}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	/* rebalance tx traffic */
157162306a36Sopenharmony_ci	if (atomic_read(&bond_info->tx_rebalance_counter) >= BOND_TLB_REBALANCE_TICKS) {
157262306a36Sopenharmony_ci		bond_for_each_slave_rcu(bond, slave, iter) {
157362306a36Sopenharmony_ci			tlb_clear_slave(bond, slave, 1);
157462306a36Sopenharmony_ci			if (slave == rcu_access_pointer(bond->curr_active_slave)) {
157562306a36Sopenharmony_ci				SLAVE_TLB_INFO(slave).load =
157662306a36Sopenharmony_ci					bond_info->unbalanced_load /
157762306a36Sopenharmony_ci						BOND_TLB_REBALANCE_INTERVAL;
157862306a36Sopenharmony_ci				bond_info->unbalanced_load = 0;
157962306a36Sopenharmony_ci			}
158062306a36Sopenharmony_ci		}
158162306a36Sopenharmony_ci		atomic_set(&bond_info->tx_rebalance_counter, 0);
158262306a36Sopenharmony_ci	}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	if (bond_info->rlb_enabled) {
158562306a36Sopenharmony_ci		if (bond_info->primary_is_promisc &&
158662306a36Sopenharmony_ci		    (++bond_info->rlb_promisc_timeout_counter >= RLB_PROMISC_TIMEOUT)) {
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci			/* dev_set_promiscuity requires rtnl and
158962306a36Sopenharmony_ci			 * nothing else.  Avoid race with bond_close.
159062306a36Sopenharmony_ci			 */
159162306a36Sopenharmony_ci			rcu_read_unlock();
159262306a36Sopenharmony_ci			if (!rtnl_trylock())
159362306a36Sopenharmony_ci				goto re_arm;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci			bond_info->rlb_promisc_timeout_counter = 0;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci			/* If the primary was set to promiscuous mode
159862306a36Sopenharmony_ci			 * because a slave was disabled then
159962306a36Sopenharmony_ci			 * it can now leave promiscuous mode.
160062306a36Sopenharmony_ci			 */
160162306a36Sopenharmony_ci			dev_set_promiscuity(rtnl_dereference(bond->curr_active_slave)->dev,
160262306a36Sopenharmony_ci					    -1);
160362306a36Sopenharmony_ci			bond_info->primary_is_promisc = 0;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci			rtnl_unlock();
160662306a36Sopenharmony_ci			rcu_read_lock();
160762306a36Sopenharmony_ci		}
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci		if (bond_info->rlb_rebalance) {
161062306a36Sopenharmony_ci			bond_info->rlb_rebalance = 0;
161162306a36Sopenharmony_ci			rlb_rebalance(bond);
161262306a36Sopenharmony_ci		}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci		/* check if clients need updating */
161562306a36Sopenharmony_ci		if (bond_info->rx_ntt) {
161662306a36Sopenharmony_ci			if (bond_info->rlb_update_delay_counter) {
161762306a36Sopenharmony_ci				--bond_info->rlb_update_delay_counter;
161862306a36Sopenharmony_ci			} else {
161962306a36Sopenharmony_ci				rlb_update_rx_clients(bond);
162062306a36Sopenharmony_ci				if (bond_info->rlb_update_retry_counter)
162162306a36Sopenharmony_ci					--bond_info->rlb_update_retry_counter;
162262306a36Sopenharmony_ci				else
162362306a36Sopenharmony_ci					bond_info->rx_ntt = 0;
162462306a36Sopenharmony_ci			}
162562306a36Sopenharmony_ci		}
162662306a36Sopenharmony_ci	}
162762306a36Sopenharmony_ci	rcu_read_unlock();
162862306a36Sopenharmony_cire_arm:
162962306a36Sopenharmony_ci	queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks);
163062306a36Sopenharmony_ci}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci/* assumption: called before the slave is attached to the bond
163362306a36Sopenharmony_ci * and not locked by the bond lock
163462306a36Sopenharmony_ci */
163562306a36Sopenharmony_ciint bond_alb_init_slave(struct bonding *bond, struct slave *slave)
163662306a36Sopenharmony_ci{
163762306a36Sopenharmony_ci	int res;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	res = alb_set_slave_mac_addr(slave, slave->perm_hwaddr,
164062306a36Sopenharmony_ci				     slave->dev->addr_len);
164162306a36Sopenharmony_ci	if (res)
164262306a36Sopenharmony_ci		return res;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	res = alb_handle_addr_collision_on_attach(bond, slave);
164562306a36Sopenharmony_ci	if (res)
164662306a36Sopenharmony_ci		return res;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	tlb_init_slave(slave);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	/* order a rebalance ASAP */
165162306a36Sopenharmony_ci	atomic_set(&bond->alb_info.tx_rebalance_counter,
165262306a36Sopenharmony_ci		   BOND_TLB_REBALANCE_TICKS);
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	if (bond->alb_info.rlb_enabled)
165562306a36Sopenharmony_ci		bond->alb_info.rlb_rebalance = 1;
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	return 0;
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci/* Remove slave from tlb and rlb hash tables, and fix up MAC addresses
166162306a36Sopenharmony_ci * if necessary.
166262306a36Sopenharmony_ci *
166362306a36Sopenharmony_ci * Caller must hold RTNL and no other locks
166462306a36Sopenharmony_ci */
166562306a36Sopenharmony_civoid bond_alb_deinit_slave(struct bonding *bond, struct slave *slave)
166662306a36Sopenharmony_ci{
166762306a36Sopenharmony_ci	if (bond_has_slaves(bond))
166862306a36Sopenharmony_ci		alb_change_hw_addr_on_detach(bond, slave);
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	tlb_clear_slave(bond, slave, 0);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	if (bond->alb_info.rlb_enabled) {
167362306a36Sopenharmony_ci		bond->alb_info.rx_slave = NULL;
167462306a36Sopenharmony_ci		rlb_clear_slave(bond, slave);
167562306a36Sopenharmony_ci	}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_civoid bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char link)
168062306a36Sopenharmony_ci{
168162306a36Sopenharmony_ci	struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	if (link == BOND_LINK_DOWN) {
168462306a36Sopenharmony_ci		tlb_clear_slave(bond, slave, 0);
168562306a36Sopenharmony_ci		if (bond->alb_info.rlb_enabled)
168662306a36Sopenharmony_ci			rlb_clear_slave(bond, slave);
168762306a36Sopenharmony_ci	} else if (link == BOND_LINK_UP) {
168862306a36Sopenharmony_ci		/* order a rebalance ASAP */
168962306a36Sopenharmony_ci		atomic_set(&bond_info->tx_rebalance_counter,
169062306a36Sopenharmony_ci			   BOND_TLB_REBALANCE_TICKS);
169162306a36Sopenharmony_ci		if (bond->alb_info.rlb_enabled) {
169262306a36Sopenharmony_ci			bond->alb_info.rlb_rebalance = 1;
169362306a36Sopenharmony_ci			/* If the updelay module parameter is smaller than the
169462306a36Sopenharmony_ci			 * forwarding delay of the switch the rebalance will
169562306a36Sopenharmony_ci			 * not work because the rebalance arp replies will
169662306a36Sopenharmony_ci			 * not be forwarded to the clients..
169762306a36Sopenharmony_ci			 */
169862306a36Sopenharmony_ci		}
169962306a36Sopenharmony_ci	}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	if (bond_is_nondyn_tlb(bond)) {
170262306a36Sopenharmony_ci		if (bond_update_slave_arr(bond, NULL))
170362306a36Sopenharmony_ci			pr_err("Failed to build slave-array for TLB mode.\n");
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci/**
170862306a36Sopenharmony_ci * bond_alb_handle_active_change - assign new curr_active_slave
170962306a36Sopenharmony_ci * @bond: our bonding struct
171062306a36Sopenharmony_ci * @new_slave: new slave to assign
171162306a36Sopenharmony_ci *
171262306a36Sopenharmony_ci * Set the bond->curr_active_slave to @new_slave and handle
171362306a36Sopenharmony_ci * mac address swapping and promiscuity changes as needed.
171462306a36Sopenharmony_ci *
171562306a36Sopenharmony_ci * Caller must hold RTNL
171662306a36Sopenharmony_ci */
171762306a36Sopenharmony_civoid bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave)
171862306a36Sopenharmony_ci{
171962306a36Sopenharmony_ci	struct slave *swap_slave;
172062306a36Sopenharmony_ci	struct slave *curr_active;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	curr_active = rtnl_dereference(bond->curr_active_slave);
172362306a36Sopenharmony_ci	if (curr_active == new_slave)
172462306a36Sopenharmony_ci		return;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	if (curr_active && bond->alb_info.primary_is_promisc) {
172762306a36Sopenharmony_ci		dev_set_promiscuity(curr_active->dev, -1);
172862306a36Sopenharmony_ci		bond->alb_info.primary_is_promisc = 0;
172962306a36Sopenharmony_ci		bond->alb_info.rlb_promisc_timeout_counter = 0;
173062306a36Sopenharmony_ci	}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	swap_slave = curr_active;
173362306a36Sopenharmony_ci	rcu_assign_pointer(bond->curr_active_slave, new_slave);
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	if (!new_slave || !bond_has_slaves(bond))
173662306a36Sopenharmony_ci		return;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	/* set the new curr_active_slave to the bonds mac address
173962306a36Sopenharmony_ci	 * i.e. swap mac addresses of old curr_active_slave and new curr_active_slave
174062306a36Sopenharmony_ci	 */
174162306a36Sopenharmony_ci	if (!swap_slave)
174262306a36Sopenharmony_ci		swap_slave = bond_slave_has_mac(bond, bond->dev->dev_addr);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	/* Arrange for swap_slave and new_slave to temporarily be
174562306a36Sopenharmony_ci	 * ignored so we can mess with their MAC addresses without
174662306a36Sopenharmony_ci	 * fear of interference from transmit activity.
174762306a36Sopenharmony_ci	 */
174862306a36Sopenharmony_ci	if (swap_slave)
174962306a36Sopenharmony_ci		tlb_clear_slave(bond, swap_slave, 1);
175062306a36Sopenharmony_ci	tlb_clear_slave(bond, new_slave, 1);
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	/* in TLB mode, the slave might flip down/up with the old dev_addr,
175362306a36Sopenharmony_ci	 * and thus filter bond->dev_addr's packets, so force bond's mac
175462306a36Sopenharmony_ci	 */
175562306a36Sopenharmony_ci	if (BOND_MODE(bond) == BOND_MODE_TLB) {
175662306a36Sopenharmony_ci		struct sockaddr_storage ss;
175762306a36Sopenharmony_ci		u8 tmp_addr[MAX_ADDR_LEN];
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci		bond_hw_addr_copy(tmp_addr, new_slave->dev->dev_addr,
176062306a36Sopenharmony_ci				  new_slave->dev->addr_len);
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci		bond_hw_addr_copy(ss.__data, bond->dev->dev_addr,
176362306a36Sopenharmony_ci				  bond->dev->addr_len);
176462306a36Sopenharmony_ci		ss.ss_family = bond->dev->type;
176562306a36Sopenharmony_ci		/* we don't care if it can't change its mac, best effort */
176662306a36Sopenharmony_ci		dev_set_mac_address(new_slave->dev, (struct sockaddr *)&ss,
176762306a36Sopenharmony_ci				    NULL);
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci		dev_addr_set(new_slave->dev, tmp_addr);
177062306a36Sopenharmony_ci	}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	/* curr_active_slave must be set before calling alb_swap_mac_addr */
177362306a36Sopenharmony_ci	if (swap_slave) {
177462306a36Sopenharmony_ci		/* swap mac address */
177562306a36Sopenharmony_ci		alb_swap_mac_addr(swap_slave, new_slave);
177662306a36Sopenharmony_ci		alb_fasten_mac_swap(bond, swap_slave, new_slave);
177762306a36Sopenharmony_ci	} else {
177862306a36Sopenharmony_ci		/* set the new_slave to the bond mac address */
177962306a36Sopenharmony_ci		alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr,
178062306a36Sopenharmony_ci				       bond->dev->addr_len);
178162306a36Sopenharmony_ci		alb_send_learning_packets(new_slave, bond->dev->dev_addr,
178262306a36Sopenharmony_ci					  false);
178362306a36Sopenharmony_ci	}
178462306a36Sopenharmony_ci}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci/* Called with RTNL */
178762306a36Sopenharmony_ciint bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
178862306a36Sopenharmony_ci{
178962306a36Sopenharmony_ci	struct bonding *bond = netdev_priv(bond_dev);
179062306a36Sopenharmony_ci	struct sockaddr_storage *ss = addr;
179162306a36Sopenharmony_ci	struct slave *curr_active;
179262306a36Sopenharmony_ci	struct slave *swap_slave;
179362306a36Sopenharmony_ci	int res;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	if (!is_valid_ether_addr(ss->__data))
179662306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	res = alb_set_mac_address(bond, addr);
179962306a36Sopenharmony_ci	if (res)
180062306a36Sopenharmony_ci		return res;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	dev_addr_set(bond_dev, ss->__data);
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	/* If there is no curr_active_slave there is nothing else to do.
180562306a36Sopenharmony_ci	 * Otherwise we'll need to pass the new address to it and handle
180662306a36Sopenharmony_ci	 * duplications.
180762306a36Sopenharmony_ci	 */
180862306a36Sopenharmony_ci	curr_active = rtnl_dereference(bond->curr_active_slave);
180962306a36Sopenharmony_ci	if (!curr_active)
181062306a36Sopenharmony_ci		return 0;
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	swap_slave = bond_slave_has_mac(bond, bond_dev->dev_addr);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	if (swap_slave) {
181562306a36Sopenharmony_ci		alb_swap_mac_addr(swap_slave, curr_active);
181662306a36Sopenharmony_ci		alb_fasten_mac_swap(bond, swap_slave, curr_active);
181762306a36Sopenharmony_ci	} else {
181862306a36Sopenharmony_ci		alb_set_slave_mac_addr(curr_active, bond_dev->dev_addr,
181962306a36Sopenharmony_ci				       bond_dev->addr_len);
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci		alb_send_learning_packets(curr_active,
182262306a36Sopenharmony_ci					  bond_dev->dev_addr, false);
182362306a36Sopenharmony_ci		if (bond->alb_info.rlb_enabled) {
182462306a36Sopenharmony_ci			/* inform clients mac address has changed */
182562306a36Sopenharmony_ci			rlb_req_update_slave_clients(bond, curr_active);
182662306a36Sopenharmony_ci		}
182762306a36Sopenharmony_ci	}
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	return 0;
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_civoid bond_alb_clear_vlan(struct bonding *bond, unsigned short vlan_id)
183362306a36Sopenharmony_ci{
183462306a36Sopenharmony_ci	if (bond->alb_info.rlb_enabled)
183562306a36Sopenharmony_ci		rlb_clear_vlan(bond, vlan_id);
183662306a36Sopenharmony_ci}
183762306a36Sopenharmony_ci
1838