18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright(c) 1999 - 2004 Intel Corporation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
78c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
88c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
98c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
108c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/if_bonding.h>
138c2ecf20Sopenharmony_ci#include <linux/pkt_sched.h>
148c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
158c2ecf20Sopenharmony_ci#include <net/bonding.h>
168c2ecf20Sopenharmony_ci#include <net/bond_3ad.h>
178c2ecf20Sopenharmony_ci#include <net/netlink.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* General definitions */
208c2ecf20Sopenharmony_ci#define AD_SHORT_TIMEOUT           1
218c2ecf20Sopenharmony_ci#define AD_LONG_TIMEOUT            0
228c2ecf20Sopenharmony_ci#define AD_STANDBY                 0x2
238c2ecf20Sopenharmony_ci#define AD_MAX_TX_IN_SECOND        3
248c2ecf20Sopenharmony_ci#define AD_COLLECTOR_MAX_DELAY     0
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* Timer definitions (43.4.4 in the 802.3ad standard) */
278c2ecf20Sopenharmony_ci#define AD_FAST_PERIODIC_TIME      1
288c2ecf20Sopenharmony_ci#define AD_SLOW_PERIODIC_TIME      30
298c2ecf20Sopenharmony_ci#define AD_SHORT_TIMEOUT_TIME      (3*AD_FAST_PERIODIC_TIME)
308c2ecf20Sopenharmony_ci#define AD_LONG_TIMEOUT_TIME       (3*AD_SLOW_PERIODIC_TIME)
318c2ecf20Sopenharmony_ci#define AD_CHURN_DETECTION_TIME    60
328c2ecf20Sopenharmony_ci#define AD_AGGREGATE_WAIT_TIME     2
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* Port Variables definitions used by the State Machines (43.4.7 in the
358c2ecf20Sopenharmony_ci * 802.3ad standard)
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci#define AD_PORT_BEGIN           0x1
388c2ecf20Sopenharmony_ci#define AD_PORT_LACP_ENABLED    0x2
398c2ecf20Sopenharmony_ci#define AD_PORT_ACTOR_CHURN     0x4
408c2ecf20Sopenharmony_ci#define AD_PORT_PARTNER_CHURN   0x8
418c2ecf20Sopenharmony_ci#define AD_PORT_READY           0x10
428c2ecf20Sopenharmony_ci#define AD_PORT_READY_N         0x20
438c2ecf20Sopenharmony_ci#define AD_PORT_MATCHED         0x40
448c2ecf20Sopenharmony_ci#define AD_PORT_STANDBY         0x80
458c2ecf20Sopenharmony_ci#define AD_PORT_SELECTED        0x100
468c2ecf20Sopenharmony_ci#define AD_PORT_MOVED           0x200
478c2ecf20Sopenharmony_ci#define AD_PORT_CHURNED         (AD_PORT_ACTOR_CHURN | AD_PORT_PARTNER_CHURN)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* Port Key definitions
508c2ecf20Sopenharmony_ci * key is determined according to the link speed, duplex and
518c2ecf20Sopenharmony_ci * user key (which is yet not supported)
528c2ecf20Sopenharmony_ci *           --------------------------------------------------------------
538c2ecf20Sopenharmony_ci * Port key  | User key (10 bits)           | Speed (5 bits)      | Duplex|
548c2ecf20Sopenharmony_ci *           --------------------------------------------------------------
558c2ecf20Sopenharmony_ci *           |15                           6|5                   1|0
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_ci#define  AD_DUPLEX_KEY_MASKS    0x1
588c2ecf20Sopenharmony_ci#define  AD_SPEED_KEY_MASKS     0x3E
598c2ecf20Sopenharmony_ci#define  AD_USER_KEY_MASKS      0xFFC0
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cienum ad_link_speed_type {
628c2ecf20Sopenharmony_ci	AD_LINK_SPEED_1MBPS = 1,
638c2ecf20Sopenharmony_ci	AD_LINK_SPEED_10MBPS,
648c2ecf20Sopenharmony_ci	AD_LINK_SPEED_100MBPS,
658c2ecf20Sopenharmony_ci	AD_LINK_SPEED_1000MBPS,
668c2ecf20Sopenharmony_ci	AD_LINK_SPEED_2500MBPS,
678c2ecf20Sopenharmony_ci	AD_LINK_SPEED_5000MBPS,
688c2ecf20Sopenharmony_ci	AD_LINK_SPEED_10000MBPS,
698c2ecf20Sopenharmony_ci	AD_LINK_SPEED_14000MBPS,
708c2ecf20Sopenharmony_ci	AD_LINK_SPEED_20000MBPS,
718c2ecf20Sopenharmony_ci	AD_LINK_SPEED_25000MBPS,
728c2ecf20Sopenharmony_ci	AD_LINK_SPEED_40000MBPS,
738c2ecf20Sopenharmony_ci	AD_LINK_SPEED_50000MBPS,
748c2ecf20Sopenharmony_ci	AD_LINK_SPEED_56000MBPS,
758c2ecf20Sopenharmony_ci	AD_LINK_SPEED_100000MBPS,
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* compare MAC addresses */
798c2ecf20Sopenharmony_ci#define MAC_ADDRESS_EQUAL(A, B)	\
808c2ecf20Sopenharmony_ci	ether_addr_equal_64bits((const u8 *)A, (const u8 *)B)
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic const u8 null_mac_addr[ETH_ALEN + 2] __long_aligned = {
838c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_cistatic u16 ad_ticks_per_sec;
868c2ecf20Sopenharmony_cistatic const int ad_delta_in_ticks = (AD_TIMER_INTERVAL * HZ) / 1000;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ciconst u8 lacpdu_mcast_addr[ETH_ALEN + 2] __long_aligned = {
898c2ecf20Sopenharmony_ci	0x01, 0x80, 0xC2, 0x00, 0x00, 0x02
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* ================= main 802.3ad protocol functions ================== */
938c2ecf20Sopenharmony_cistatic int ad_lacpdu_send(struct port *port);
948c2ecf20Sopenharmony_cistatic int ad_marker_send(struct port *port, struct bond_marker *marker);
958c2ecf20Sopenharmony_cistatic void ad_mux_machine(struct port *port, bool *update_slave_arr);
968c2ecf20Sopenharmony_cistatic void ad_rx_machine(struct lacpdu *lacpdu, struct port *port);
978c2ecf20Sopenharmony_cistatic void ad_tx_machine(struct port *port);
988c2ecf20Sopenharmony_cistatic void ad_periodic_machine(struct port *port);
998c2ecf20Sopenharmony_cistatic void ad_port_selection_logic(struct port *port, bool *update_slave_arr);
1008c2ecf20Sopenharmony_cistatic void ad_agg_selection_logic(struct aggregator *aggregator,
1018c2ecf20Sopenharmony_ci				   bool *update_slave_arr);
1028c2ecf20Sopenharmony_cistatic void ad_clear_agg(struct aggregator *aggregator);
1038c2ecf20Sopenharmony_cistatic void ad_initialize_agg(struct aggregator *aggregator);
1048c2ecf20Sopenharmony_cistatic void ad_initialize_port(struct port *port, int lacp_fast);
1058c2ecf20Sopenharmony_cistatic void ad_enable_collecting_distributing(struct port *port,
1068c2ecf20Sopenharmony_ci					      bool *update_slave_arr);
1078c2ecf20Sopenharmony_cistatic void ad_disable_collecting_distributing(struct port *port,
1088c2ecf20Sopenharmony_ci					       bool *update_slave_arr);
1098c2ecf20Sopenharmony_cistatic void ad_marker_info_received(struct bond_marker *marker_info,
1108c2ecf20Sopenharmony_ci				    struct port *port);
1118c2ecf20Sopenharmony_cistatic void ad_marker_response_received(struct bond_marker *marker,
1128c2ecf20Sopenharmony_ci					struct port *port);
1138c2ecf20Sopenharmony_cistatic void ad_update_actor_keys(struct port *port, bool reset);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/* ================= api to bonding and kernel code ================== */
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/**
1198c2ecf20Sopenharmony_ci * __get_bond_by_port - get the port's bonding struct
1208c2ecf20Sopenharmony_ci * @port: the port we're looking at
1218c2ecf20Sopenharmony_ci *
1228c2ecf20Sopenharmony_ci * Return @port's bonding struct, or %NULL if it can't be found.
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_cistatic inline struct bonding *__get_bond_by_port(struct port *port)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	if (port->slave == NULL)
1278c2ecf20Sopenharmony_ci		return NULL;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return bond_get_bond_by_slave(port->slave);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/**
1338c2ecf20Sopenharmony_ci * __get_first_agg - get the first aggregator in the bond
1348c2ecf20Sopenharmony_ci * @port: the port we're looking at
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * Return the aggregator of the first slave in @bond, or %NULL if it can't be
1378c2ecf20Sopenharmony_ci * found.
1388c2ecf20Sopenharmony_ci * The caller must hold RCU or RTNL lock.
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_cistatic inline struct aggregator *__get_first_agg(struct port *port)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct bonding *bond = __get_bond_by_port(port);
1438c2ecf20Sopenharmony_ci	struct slave *first_slave;
1448c2ecf20Sopenharmony_ci	struct aggregator *agg;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* If there's no bond for this port, or bond has no slaves */
1478c2ecf20Sopenharmony_ci	if (bond == NULL)
1488c2ecf20Sopenharmony_ci		return NULL;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	rcu_read_lock();
1518c2ecf20Sopenharmony_ci	first_slave = bond_first_slave_rcu(bond);
1528c2ecf20Sopenharmony_ci	agg = first_slave ? &(SLAVE_AD_INFO(first_slave)->aggregator) : NULL;
1538c2ecf20Sopenharmony_ci	rcu_read_unlock();
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	return agg;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/**
1598c2ecf20Sopenharmony_ci * __agg_has_partner - see if we have a partner
1608c2ecf20Sopenharmony_ci * @agg: the agregator we're looking at
1618c2ecf20Sopenharmony_ci *
1628c2ecf20Sopenharmony_ci * Return nonzero if aggregator has a partner (denoted by a non-zero ether
1638c2ecf20Sopenharmony_ci * address for the partner). Return 0 if not.
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic inline int __agg_has_partner(struct aggregator *agg)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	return !is_zero_ether_addr(agg->partner_system.mac_addr_value);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/**
1718c2ecf20Sopenharmony_ci * __disable_port - disable the port's slave
1728c2ecf20Sopenharmony_ci * @port: the port we're looking at
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_cistatic inline void __disable_port(struct port *port)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	bond_set_slave_inactive_flags(port->slave, BOND_SLAVE_NOTIFY_LATER);
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci/**
1808c2ecf20Sopenharmony_ci * __enable_port - enable the port's slave, if it's up
1818c2ecf20Sopenharmony_ci * @port: the port we're looking at
1828c2ecf20Sopenharmony_ci */
1838c2ecf20Sopenharmony_cistatic inline void __enable_port(struct port *port)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct slave *slave = port->slave;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if ((slave->link == BOND_LINK_UP) && bond_slave_is_up(slave))
1888c2ecf20Sopenharmony_ci		bond_set_slave_active_flags(slave, BOND_SLAVE_NOTIFY_LATER);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/**
1928c2ecf20Sopenharmony_ci * __port_is_enabled - check if the port's slave is in active state
1938c2ecf20Sopenharmony_ci * @port: the port we're looking at
1948c2ecf20Sopenharmony_ci */
1958c2ecf20Sopenharmony_cistatic inline int __port_is_enabled(struct port *port)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	return bond_is_active_slave(port->slave);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/**
2018c2ecf20Sopenharmony_ci * __get_agg_selection_mode - get the aggregator selection mode
2028c2ecf20Sopenharmony_ci * @port: the port we're looking at
2038c2ecf20Sopenharmony_ci *
2048c2ecf20Sopenharmony_ci * Get the aggregator selection mode. Can be %STABLE, %BANDWIDTH or %COUNT.
2058c2ecf20Sopenharmony_ci */
2068c2ecf20Sopenharmony_cistatic inline u32 __get_agg_selection_mode(struct port *port)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct bonding *bond = __get_bond_by_port(port);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (bond == NULL)
2118c2ecf20Sopenharmony_ci		return BOND_AD_STABLE;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return bond->params.ad_select;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/**
2178c2ecf20Sopenharmony_ci * __check_agg_selection_timer - check if the selection timer has expired
2188c2ecf20Sopenharmony_ci * @port: the port we're looking at
2198c2ecf20Sopenharmony_ci */
2208c2ecf20Sopenharmony_cistatic inline int __check_agg_selection_timer(struct port *port)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct bonding *bond = __get_bond_by_port(port);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (bond == NULL)
2258c2ecf20Sopenharmony_ci		return 0;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return atomic_read(&BOND_AD_INFO(bond).agg_select_timer) ? 1 : 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/**
2318c2ecf20Sopenharmony_ci * __get_link_speed - get a port's speed
2328c2ecf20Sopenharmony_ci * @port: the port we're looking at
2338c2ecf20Sopenharmony_ci *
2348c2ecf20Sopenharmony_ci * Return @port's speed in 802.3ad enum format. i.e. one of:
2358c2ecf20Sopenharmony_ci *     0,
2368c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_10MBPS,
2378c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_100MBPS,
2388c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_1000MBPS,
2398c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_2500MBPS,
2408c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_5000MBPS,
2418c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_10000MBPS
2428c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_14000MBPS,
2438c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_20000MBPS
2448c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_25000MBPS
2458c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_40000MBPS
2468c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_50000MBPS
2478c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_56000MBPS
2488c2ecf20Sopenharmony_ci *     %AD_LINK_SPEED_100000MBPS
2498c2ecf20Sopenharmony_ci */
2508c2ecf20Sopenharmony_cistatic u16 __get_link_speed(struct port *port)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct slave *slave = port->slave;
2538c2ecf20Sopenharmony_ci	u16 speed;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* this if covers only a special case: when the configuration starts
2568c2ecf20Sopenharmony_ci	 * with link down, it sets the speed to 0.
2578c2ecf20Sopenharmony_ci	 * This is done in spite of the fact that the e100 driver reports 0
2588c2ecf20Sopenharmony_ci	 * to be compatible with MVT in the future.
2598c2ecf20Sopenharmony_ci	 */
2608c2ecf20Sopenharmony_ci	if (slave->link != BOND_LINK_UP)
2618c2ecf20Sopenharmony_ci		speed = 0;
2628c2ecf20Sopenharmony_ci	else {
2638c2ecf20Sopenharmony_ci		switch (slave->speed) {
2648c2ecf20Sopenharmony_ci		case SPEED_10:
2658c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_10MBPS;
2668c2ecf20Sopenharmony_ci			break;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		case SPEED_100:
2698c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_100MBPS;
2708c2ecf20Sopenharmony_ci			break;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		case SPEED_1000:
2738c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_1000MBPS;
2748c2ecf20Sopenharmony_ci			break;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		case SPEED_2500:
2778c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_2500MBPS;
2788c2ecf20Sopenharmony_ci			break;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		case SPEED_5000:
2818c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_5000MBPS;
2828c2ecf20Sopenharmony_ci			break;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		case SPEED_10000:
2858c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_10000MBPS;
2868c2ecf20Sopenharmony_ci			break;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		case SPEED_14000:
2898c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_14000MBPS;
2908c2ecf20Sopenharmony_ci			break;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		case SPEED_20000:
2938c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_20000MBPS;
2948c2ecf20Sopenharmony_ci			break;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		case SPEED_25000:
2978c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_25000MBPS;
2988c2ecf20Sopenharmony_ci			break;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		case SPEED_40000:
3018c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_40000MBPS;
3028c2ecf20Sopenharmony_ci			break;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		case SPEED_50000:
3058c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_50000MBPS;
3068c2ecf20Sopenharmony_ci			break;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		case SPEED_56000:
3098c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_56000MBPS;
3108c2ecf20Sopenharmony_ci			break;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci		case SPEED_100000:
3138c2ecf20Sopenharmony_ci			speed = AD_LINK_SPEED_100000MBPS;
3148c2ecf20Sopenharmony_ci			break;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		default:
3178c2ecf20Sopenharmony_ci			/* unknown speed value from ethtool. shouldn't happen */
3188c2ecf20Sopenharmony_ci			if (slave->speed != SPEED_UNKNOWN)
3198c2ecf20Sopenharmony_ci				pr_warn_once("%s: (slave %s): unknown ethtool speed (%d) for port %d (set it to 0)\n",
3208c2ecf20Sopenharmony_ci					     slave->bond->dev->name,
3218c2ecf20Sopenharmony_ci					     slave->dev->name, slave->speed,
3228c2ecf20Sopenharmony_ci					     port->actor_port_number);
3238c2ecf20Sopenharmony_ci			speed = 0;
3248c2ecf20Sopenharmony_ci			break;
3258c2ecf20Sopenharmony_ci		}
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	slave_dbg(slave->bond->dev, slave->dev, "Port %d Received link speed %d update from adapter\n",
3298c2ecf20Sopenharmony_ci		  port->actor_port_number, speed);
3308c2ecf20Sopenharmony_ci	return speed;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci/**
3348c2ecf20Sopenharmony_ci * __get_duplex - get a port's duplex
3358c2ecf20Sopenharmony_ci * @port: the port we're looking at
3368c2ecf20Sopenharmony_ci *
3378c2ecf20Sopenharmony_ci * Return @port's duplex in 802.3ad bitmask format. i.e.:
3388c2ecf20Sopenharmony_ci *     0x01 if in full duplex
3398c2ecf20Sopenharmony_ci *     0x00 otherwise
3408c2ecf20Sopenharmony_ci */
3418c2ecf20Sopenharmony_cistatic u8 __get_duplex(struct port *port)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct slave *slave = port->slave;
3448c2ecf20Sopenharmony_ci	u8 retval = 0x0;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* handling a special case: when the configuration starts with
3478c2ecf20Sopenharmony_ci	 * link down, it sets the duplex to 0.
3488c2ecf20Sopenharmony_ci	 */
3498c2ecf20Sopenharmony_ci	if (slave->link == BOND_LINK_UP) {
3508c2ecf20Sopenharmony_ci		switch (slave->duplex) {
3518c2ecf20Sopenharmony_ci		case DUPLEX_FULL:
3528c2ecf20Sopenharmony_ci			retval = 0x1;
3538c2ecf20Sopenharmony_ci			slave_dbg(slave->bond->dev, slave->dev, "Port %d Received status full duplex update from adapter\n",
3548c2ecf20Sopenharmony_ci				  port->actor_port_number);
3558c2ecf20Sopenharmony_ci			break;
3568c2ecf20Sopenharmony_ci		case DUPLEX_HALF:
3578c2ecf20Sopenharmony_ci		default:
3588c2ecf20Sopenharmony_ci			retval = 0x0;
3598c2ecf20Sopenharmony_ci			slave_dbg(slave->bond->dev, slave->dev, "Port %d Received status NOT full duplex update from adapter\n",
3608c2ecf20Sopenharmony_ci				  port->actor_port_number);
3618c2ecf20Sopenharmony_ci			break;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci	return retval;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic void __ad_actor_update_port(struct port *port)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	const struct bonding *bond = bond_get_bond_by_slave(port->slave);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	port->actor_system = BOND_AD_INFO(bond).system.sys_mac_addr;
3728c2ecf20Sopenharmony_ci	port->actor_system_priority = BOND_AD_INFO(bond).system.sys_priority;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci/* Conversions */
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci/**
3788c2ecf20Sopenharmony_ci * __ad_timer_to_ticks - convert a given timer type to AD module ticks
3798c2ecf20Sopenharmony_ci * @timer_type:	which timer to operate
3808c2ecf20Sopenharmony_ci * @par: timer parameter. see below
3818c2ecf20Sopenharmony_ci *
3828c2ecf20Sopenharmony_ci * If @timer_type is %current_while_timer, @par indicates long/short timer.
3838c2ecf20Sopenharmony_ci * If @timer_type is %periodic_timer, @par is one of %FAST_PERIODIC_TIME,
3848c2ecf20Sopenharmony_ci *						     %SLOW_PERIODIC_TIME.
3858c2ecf20Sopenharmony_ci */
3868c2ecf20Sopenharmony_cistatic u16 __ad_timer_to_ticks(u16 timer_type, u16 par)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	u16 retval = 0; /* to silence the compiler */
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	switch (timer_type) {
3918c2ecf20Sopenharmony_ci	case AD_CURRENT_WHILE_TIMER:	/* for rx machine usage */
3928c2ecf20Sopenharmony_ci		if (par)
3938c2ecf20Sopenharmony_ci			retval = (AD_SHORT_TIMEOUT_TIME*ad_ticks_per_sec);
3948c2ecf20Sopenharmony_ci		else
3958c2ecf20Sopenharmony_ci			retval = (AD_LONG_TIMEOUT_TIME*ad_ticks_per_sec);
3968c2ecf20Sopenharmony_ci		break;
3978c2ecf20Sopenharmony_ci	case AD_ACTOR_CHURN_TIMER:	/* for local churn machine */
3988c2ecf20Sopenharmony_ci		retval = (AD_CHURN_DETECTION_TIME*ad_ticks_per_sec);
3998c2ecf20Sopenharmony_ci		break;
4008c2ecf20Sopenharmony_ci	case AD_PERIODIC_TIMER:		/* for periodic machine */
4018c2ecf20Sopenharmony_ci		retval = (par*ad_ticks_per_sec); /* long timeout */
4028c2ecf20Sopenharmony_ci		break;
4038c2ecf20Sopenharmony_ci	case AD_PARTNER_CHURN_TIMER:	/* for remote churn machine */
4048c2ecf20Sopenharmony_ci		retval = (AD_CHURN_DETECTION_TIME*ad_ticks_per_sec);
4058c2ecf20Sopenharmony_ci		break;
4068c2ecf20Sopenharmony_ci	case AD_WAIT_WHILE_TIMER:	/* for selection machine */
4078c2ecf20Sopenharmony_ci		retval = (AD_AGGREGATE_WAIT_TIME*ad_ticks_per_sec);
4088c2ecf20Sopenharmony_ci		break;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return retval;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci/* ================= ad_rx_machine helper functions ================== */
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci/**
4188c2ecf20Sopenharmony_ci * __choose_matched - update a port's matched variable from a received lacpdu
4198c2ecf20Sopenharmony_ci * @lacpdu: the lacpdu we've received
4208c2ecf20Sopenharmony_ci * @port: the port we're looking at
4218c2ecf20Sopenharmony_ci *
4228c2ecf20Sopenharmony_ci * Update the value of the matched variable, using parameter values from a
4238c2ecf20Sopenharmony_ci * newly received lacpdu. Parameter values for the partner carried in the
4248c2ecf20Sopenharmony_ci * received PDU are compared with the corresponding operational parameter
4258c2ecf20Sopenharmony_ci * values for the actor. Matched is set to TRUE if all of these parameters
4268c2ecf20Sopenharmony_ci * match and the PDU parameter partner_state.aggregation has the same value as
4278c2ecf20Sopenharmony_ci * actor_oper_port_state.aggregation and lacp will actively maintain the link
4288c2ecf20Sopenharmony_ci * in the aggregation. Matched is also set to TRUE if the value of
4298c2ecf20Sopenharmony_ci * actor_state.aggregation in the received PDU is set to FALSE, i.e., indicates
4308c2ecf20Sopenharmony_ci * an individual link and lacp will actively maintain the link. Otherwise,
4318c2ecf20Sopenharmony_ci * matched is set to FALSE. LACP is considered to be actively maintaining the
4328c2ecf20Sopenharmony_ci * link if either the PDU's actor_state.lacp_activity variable is TRUE or both
4338c2ecf20Sopenharmony_ci * the actor's actor_oper_port_state.lacp_activity and the PDU's
4348c2ecf20Sopenharmony_ci * partner_state.lacp_activity variables are TRUE.
4358c2ecf20Sopenharmony_ci *
4368c2ecf20Sopenharmony_ci * Note: the AD_PORT_MATCHED "variable" is not specified by 802.3ad; it is
4378c2ecf20Sopenharmony_ci * used here to implement the language from 802.3ad 43.4.9 that requires
4388c2ecf20Sopenharmony_ci * recordPDU to "match" the LACPDU parameters to the stored values.
4398c2ecf20Sopenharmony_ci */
4408c2ecf20Sopenharmony_cistatic void __choose_matched(struct lacpdu *lacpdu, struct port *port)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	/* check if all parameters are alike
4438c2ecf20Sopenharmony_ci	 * or this is individual link(aggregation == FALSE)
4448c2ecf20Sopenharmony_ci	 * then update the state machine Matched variable.
4458c2ecf20Sopenharmony_ci	 */
4468c2ecf20Sopenharmony_ci	if (((ntohs(lacpdu->partner_port) == port->actor_port_number) &&
4478c2ecf20Sopenharmony_ci	     (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) &&
4488c2ecf20Sopenharmony_ci	     MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) &&
4498c2ecf20Sopenharmony_ci	     (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) &&
4508c2ecf20Sopenharmony_ci	     (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) &&
4518c2ecf20Sopenharmony_ci	     ((lacpdu->partner_state & LACP_STATE_AGGREGATION) == (port->actor_oper_port_state & LACP_STATE_AGGREGATION))) ||
4528c2ecf20Sopenharmony_ci	    ((lacpdu->actor_state & LACP_STATE_AGGREGATION) == 0)
4538c2ecf20Sopenharmony_ci		) {
4548c2ecf20Sopenharmony_ci		port->sm_vars |= AD_PORT_MATCHED;
4558c2ecf20Sopenharmony_ci	} else {
4568c2ecf20Sopenharmony_ci		port->sm_vars &= ~AD_PORT_MATCHED;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci/**
4618c2ecf20Sopenharmony_ci * __record_pdu - record parameters from a received lacpdu
4628c2ecf20Sopenharmony_ci * @lacpdu: the lacpdu we've received
4638c2ecf20Sopenharmony_ci * @port: the port we're looking at
4648c2ecf20Sopenharmony_ci *
4658c2ecf20Sopenharmony_ci * Record the parameter values for the Actor carried in a received lacpdu as
4668c2ecf20Sopenharmony_ci * the current partner operational parameter values and sets
4678c2ecf20Sopenharmony_ci * actor_oper_port_state.defaulted to FALSE.
4688c2ecf20Sopenharmony_ci */
4698c2ecf20Sopenharmony_cistatic void __record_pdu(struct lacpdu *lacpdu, struct port *port)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	if (lacpdu && port) {
4728c2ecf20Sopenharmony_ci		struct port_params *partner = &port->partner_oper;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		__choose_matched(lacpdu, port);
4758c2ecf20Sopenharmony_ci		/* record the new parameter values for the partner
4768c2ecf20Sopenharmony_ci		 * operational
4778c2ecf20Sopenharmony_ci		 */
4788c2ecf20Sopenharmony_ci		partner->port_number = ntohs(lacpdu->actor_port);
4798c2ecf20Sopenharmony_ci		partner->port_priority = ntohs(lacpdu->actor_port_priority);
4808c2ecf20Sopenharmony_ci		partner->system = lacpdu->actor_system;
4818c2ecf20Sopenharmony_ci		partner->system_priority = ntohs(lacpdu->actor_system_priority);
4828c2ecf20Sopenharmony_ci		partner->key = ntohs(lacpdu->actor_key);
4838c2ecf20Sopenharmony_ci		partner->port_state = lacpdu->actor_state;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		/* set actor_oper_port_state.defaulted to FALSE */
4868c2ecf20Sopenharmony_ci		port->actor_oper_port_state &= ~LACP_STATE_DEFAULTED;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci		/* set the partner sync. to on if the partner is sync,
4898c2ecf20Sopenharmony_ci		 * and the port is matched
4908c2ecf20Sopenharmony_ci		 */
4918c2ecf20Sopenharmony_ci		if ((port->sm_vars & AD_PORT_MATCHED) &&
4928c2ecf20Sopenharmony_ci		    (lacpdu->actor_state & LACP_STATE_SYNCHRONIZATION)) {
4938c2ecf20Sopenharmony_ci			partner->port_state |= LACP_STATE_SYNCHRONIZATION;
4948c2ecf20Sopenharmony_ci			slave_dbg(port->slave->bond->dev, port->slave->dev,
4958c2ecf20Sopenharmony_ci				  "partner sync=1\n");
4968c2ecf20Sopenharmony_ci		} else {
4978c2ecf20Sopenharmony_ci			partner->port_state &= ~LACP_STATE_SYNCHRONIZATION;
4988c2ecf20Sopenharmony_ci			slave_dbg(port->slave->bond->dev, port->slave->dev,
4998c2ecf20Sopenharmony_ci				  "partner sync=0\n");
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci/**
5058c2ecf20Sopenharmony_ci * __record_default - record default parameters
5068c2ecf20Sopenharmony_ci * @port: the port we're looking at
5078c2ecf20Sopenharmony_ci *
5088c2ecf20Sopenharmony_ci * This function records the default parameter values for the partner carried
5098c2ecf20Sopenharmony_ci * in the Partner Admin parameters as the current partner operational parameter
5108c2ecf20Sopenharmony_ci * values and sets actor_oper_port_state.defaulted to TRUE.
5118c2ecf20Sopenharmony_ci */
5128c2ecf20Sopenharmony_cistatic void __record_default(struct port *port)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	if (port) {
5158c2ecf20Sopenharmony_ci		/* record the partner admin parameters */
5168c2ecf20Sopenharmony_ci		memcpy(&port->partner_oper, &port->partner_admin,
5178c2ecf20Sopenharmony_ci		       sizeof(struct port_params));
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		/* set actor_oper_port_state.defaulted to true */
5208c2ecf20Sopenharmony_ci		port->actor_oper_port_state |= LACP_STATE_DEFAULTED;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci/**
5258c2ecf20Sopenharmony_ci * __update_selected - update a port's Selected variable from a received lacpdu
5268c2ecf20Sopenharmony_ci * @lacpdu: the lacpdu we've received
5278c2ecf20Sopenharmony_ci * @port: the port we're looking at
5288c2ecf20Sopenharmony_ci *
5298c2ecf20Sopenharmony_ci * Update the value of the selected variable, using parameter values from a
5308c2ecf20Sopenharmony_ci * newly received lacpdu. The parameter values for the Actor carried in the
5318c2ecf20Sopenharmony_ci * received PDU are compared with the corresponding operational parameter
5328c2ecf20Sopenharmony_ci * values for the ports partner. If one or more of the comparisons shows that
5338c2ecf20Sopenharmony_ci * the value(s) received in the PDU differ from the current operational values,
5348c2ecf20Sopenharmony_ci * then selected is set to FALSE and actor_oper_port_state.synchronization is
5358c2ecf20Sopenharmony_ci * set to out_of_sync. Otherwise, selected remains unchanged.
5368c2ecf20Sopenharmony_ci */
5378c2ecf20Sopenharmony_cistatic void __update_selected(struct lacpdu *lacpdu, struct port *port)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	if (lacpdu && port) {
5408c2ecf20Sopenharmony_ci		const struct port_params *partner = &port->partner_oper;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		/* check if any parameter is different then
5438c2ecf20Sopenharmony_ci		 * update the state machine selected variable.
5448c2ecf20Sopenharmony_ci		 */
5458c2ecf20Sopenharmony_ci		if (ntohs(lacpdu->actor_port) != partner->port_number ||
5468c2ecf20Sopenharmony_ci		    ntohs(lacpdu->actor_port_priority) != partner->port_priority ||
5478c2ecf20Sopenharmony_ci		    !MAC_ADDRESS_EQUAL(&lacpdu->actor_system, &partner->system) ||
5488c2ecf20Sopenharmony_ci		    ntohs(lacpdu->actor_system_priority) != partner->system_priority ||
5498c2ecf20Sopenharmony_ci		    ntohs(lacpdu->actor_key) != partner->key ||
5508c2ecf20Sopenharmony_ci		    (lacpdu->actor_state & LACP_STATE_AGGREGATION) != (partner->port_state & LACP_STATE_AGGREGATION)) {
5518c2ecf20Sopenharmony_ci			port->sm_vars &= ~AD_PORT_SELECTED;
5528c2ecf20Sopenharmony_ci		}
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci/**
5578c2ecf20Sopenharmony_ci * __update_default_selected - update a port's Selected variable from Partner
5588c2ecf20Sopenharmony_ci * @port: the port we're looking at
5598c2ecf20Sopenharmony_ci *
5608c2ecf20Sopenharmony_ci * This function updates the value of the selected variable, using the partner
5618c2ecf20Sopenharmony_ci * administrative parameter values. The administrative values are compared with
5628c2ecf20Sopenharmony_ci * the corresponding operational parameter values for the partner. If one or
5638c2ecf20Sopenharmony_ci * more of the comparisons shows that the administrative value(s) differ from
5648c2ecf20Sopenharmony_ci * the current operational values, then Selected is set to FALSE and
5658c2ecf20Sopenharmony_ci * actor_oper_port_state.synchronization is set to OUT_OF_SYNC. Otherwise,
5668c2ecf20Sopenharmony_ci * Selected remains unchanged.
5678c2ecf20Sopenharmony_ci */
5688c2ecf20Sopenharmony_cistatic void __update_default_selected(struct port *port)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	if (port) {
5718c2ecf20Sopenharmony_ci		const struct port_params *admin = &port->partner_admin;
5728c2ecf20Sopenharmony_ci		const struct port_params *oper = &port->partner_oper;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci		/* check if any parameter is different then
5758c2ecf20Sopenharmony_ci		 * update the state machine selected variable.
5768c2ecf20Sopenharmony_ci		 */
5778c2ecf20Sopenharmony_ci		if (admin->port_number != oper->port_number ||
5788c2ecf20Sopenharmony_ci		    admin->port_priority != oper->port_priority ||
5798c2ecf20Sopenharmony_ci		    !MAC_ADDRESS_EQUAL(&admin->system, &oper->system) ||
5808c2ecf20Sopenharmony_ci		    admin->system_priority != oper->system_priority ||
5818c2ecf20Sopenharmony_ci		    admin->key != oper->key ||
5828c2ecf20Sopenharmony_ci		    (admin->port_state & LACP_STATE_AGGREGATION)
5838c2ecf20Sopenharmony_ci			!= (oper->port_state & LACP_STATE_AGGREGATION)) {
5848c2ecf20Sopenharmony_ci			port->sm_vars &= ~AD_PORT_SELECTED;
5858c2ecf20Sopenharmony_ci		}
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci/**
5908c2ecf20Sopenharmony_ci * __update_ntt - update a port's ntt variable from a received lacpdu
5918c2ecf20Sopenharmony_ci * @lacpdu: the lacpdu we've received
5928c2ecf20Sopenharmony_ci * @port: the port we're looking at
5938c2ecf20Sopenharmony_ci *
5948c2ecf20Sopenharmony_ci * Updates the value of the ntt variable, using parameter values from a newly
5958c2ecf20Sopenharmony_ci * received lacpdu. The parameter values for the partner carried in the
5968c2ecf20Sopenharmony_ci * received PDU are compared with the corresponding operational parameter
5978c2ecf20Sopenharmony_ci * values for the Actor. If one or more of the comparisons shows that the
5988c2ecf20Sopenharmony_ci * value(s) received in the PDU differ from the current operational values,
5998c2ecf20Sopenharmony_ci * then ntt is set to TRUE. Otherwise, ntt remains unchanged.
6008c2ecf20Sopenharmony_ci */
6018c2ecf20Sopenharmony_cistatic void __update_ntt(struct lacpdu *lacpdu, struct port *port)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	/* validate lacpdu and port */
6048c2ecf20Sopenharmony_ci	if (lacpdu && port) {
6058c2ecf20Sopenharmony_ci		/* check if any parameter is different then
6068c2ecf20Sopenharmony_ci		 * update the port->ntt.
6078c2ecf20Sopenharmony_ci		 */
6088c2ecf20Sopenharmony_ci		if ((ntohs(lacpdu->partner_port) != port->actor_port_number) ||
6098c2ecf20Sopenharmony_ci		    (ntohs(lacpdu->partner_port_priority) != port->actor_port_priority) ||
6108c2ecf20Sopenharmony_ci		    !MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) ||
6118c2ecf20Sopenharmony_ci		    (ntohs(lacpdu->partner_system_priority) != port->actor_system_priority) ||
6128c2ecf20Sopenharmony_ci		    (ntohs(lacpdu->partner_key) != port->actor_oper_port_key) ||
6138c2ecf20Sopenharmony_ci		    ((lacpdu->partner_state & LACP_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & LACP_STATE_LACP_ACTIVITY)) ||
6148c2ecf20Sopenharmony_ci		    ((lacpdu->partner_state & LACP_STATE_LACP_TIMEOUT) != (port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT)) ||
6158c2ecf20Sopenharmony_ci		    ((lacpdu->partner_state & LACP_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) ||
6168c2ecf20Sopenharmony_ci		    ((lacpdu->partner_state & LACP_STATE_AGGREGATION) != (port->actor_oper_port_state & LACP_STATE_AGGREGATION))
6178c2ecf20Sopenharmony_ci		   ) {
6188c2ecf20Sopenharmony_ci			port->ntt = true;
6198c2ecf20Sopenharmony_ci		}
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci/**
6248c2ecf20Sopenharmony_ci * __agg_ports_are_ready - check if all ports in an aggregator are ready
6258c2ecf20Sopenharmony_ci * @aggregator: the aggregator we're looking at
6268c2ecf20Sopenharmony_ci *
6278c2ecf20Sopenharmony_ci */
6288c2ecf20Sopenharmony_cistatic int __agg_ports_are_ready(struct aggregator *aggregator)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	struct port *port;
6318c2ecf20Sopenharmony_ci	int retval = 1;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	if (aggregator) {
6348c2ecf20Sopenharmony_ci		/* scan all ports in this aggregator to verfy if they are
6358c2ecf20Sopenharmony_ci		 * all ready.
6368c2ecf20Sopenharmony_ci		 */
6378c2ecf20Sopenharmony_ci		for (port = aggregator->lag_ports;
6388c2ecf20Sopenharmony_ci		     port;
6398c2ecf20Sopenharmony_ci		     port = port->next_port_in_aggregator) {
6408c2ecf20Sopenharmony_ci			if (!(port->sm_vars & AD_PORT_READY_N)) {
6418c2ecf20Sopenharmony_ci				retval = 0;
6428c2ecf20Sopenharmony_ci				break;
6438c2ecf20Sopenharmony_ci			}
6448c2ecf20Sopenharmony_ci		}
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	return retval;
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci/**
6518c2ecf20Sopenharmony_ci * __set_agg_ports_ready - set value of Ready bit in all ports of an aggregator
6528c2ecf20Sopenharmony_ci * @aggregator: the aggregator we're looking at
6538c2ecf20Sopenharmony_ci * @val: Should the ports' ready bit be set on or off
6548c2ecf20Sopenharmony_ci *
6558c2ecf20Sopenharmony_ci */
6568c2ecf20Sopenharmony_cistatic void __set_agg_ports_ready(struct aggregator *aggregator, int val)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	struct port *port;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	for (port = aggregator->lag_ports; port;
6618c2ecf20Sopenharmony_ci	     port = port->next_port_in_aggregator) {
6628c2ecf20Sopenharmony_ci		if (val)
6638c2ecf20Sopenharmony_ci			port->sm_vars |= AD_PORT_READY;
6648c2ecf20Sopenharmony_ci		else
6658c2ecf20Sopenharmony_ci			port->sm_vars &= ~AD_PORT_READY;
6668c2ecf20Sopenharmony_ci	}
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic int __agg_active_ports(struct aggregator *agg)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct port *port;
6728c2ecf20Sopenharmony_ci	int active = 0;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	for (port = agg->lag_ports; port;
6758c2ecf20Sopenharmony_ci	     port = port->next_port_in_aggregator) {
6768c2ecf20Sopenharmony_ci		if (port->is_enabled)
6778c2ecf20Sopenharmony_ci			active++;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return active;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/**
6848c2ecf20Sopenharmony_ci * __get_agg_bandwidth - get the total bandwidth of an aggregator
6858c2ecf20Sopenharmony_ci * @aggregator: the aggregator we're looking at
6868c2ecf20Sopenharmony_ci *
6878c2ecf20Sopenharmony_ci */
6888c2ecf20Sopenharmony_cistatic u32 __get_agg_bandwidth(struct aggregator *aggregator)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	int nports = __agg_active_ports(aggregator);
6918c2ecf20Sopenharmony_ci	u32 bandwidth = 0;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	if (nports) {
6948c2ecf20Sopenharmony_ci		switch (__get_link_speed(aggregator->lag_ports)) {
6958c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_1MBPS:
6968c2ecf20Sopenharmony_ci			bandwidth = nports;
6978c2ecf20Sopenharmony_ci			break;
6988c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_10MBPS:
6998c2ecf20Sopenharmony_ci			bandwidth = nports * 10;
7008c2ecf20Sopenharmony_ci			break;
7018c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_100MBPS:
7028c2ecf20Sopenharmony_ci			bandwidth = nports * 100;
7038c2ecf20Sopenharmony_ci			break;
7048c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_1000MBPS:
7058c2ecf20Sopenharmony_ci			bandwidth = nports * 1000;
7068c2ecf20Sopenharmony_ci			break;
7078c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_2500MBPS:
7088c2ecf20Sopenharmony_ci			bandwidth = nports * 2500;
7098c2ecf20Sopenharmony_ci			break;
7108c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_5000MBPS:
7118c2ecf20Sopenharmony_ci			bandwidth = nports * 5000;
7128c2ecf20Sopenharmony_ci			break;
7138c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_10000MBPS:
7148c2ecf20Sopenharmony_ci			bandwidth = nports * 10000;
7158c2ecf20Sopenharmony_ci			break;
7168c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_14000MBPS:
7178c2ecf20Sopenharmony_ci			bandwidth = nports * 14000;
7188c2ecf20Sopenharmony_ci			break;
7198c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_20000MBPS:
7208c2ecf20Sopenharmony_ci			bandwidth = nports * 20000;
7218c2ecf20Sopenharmony_ci			break;
7228c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_25000MBPS:
7238c2ecf20Sopenharmony_ci			bandwidth = nports * 25000;
7248c2ecf20Sopenharmony_ci			break;
7258c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_40000MBPS:
7268c2ecf20Sopenharmony_ci			bandwidth = nports * 40000;
7278c2ecf20Sopenharmony_ci			break;
7288c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_50000MBPS:
7298c2ecf20Sopenharmony_ci			bandwidth = nports * 50000;
7308c2ecf20Sopenharmony_ci			break;
7318c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_56000MBPS:
7328c2ecf20Sopenharmony_ci			bandwidth = nports * 56000;
7338c2ecf20Sopenharmony_ci			break;
7348c2ecf20Sopenharmony_ci		case AD_LINK_SPEED_100000MBPS:
7358c2ecf20Sopenharmony_ci			bandwidth = nports * 100000;
7368c2ecf20Sopenharmony_ci			break;
7378c2ecf20Sopenharmony_ci		default:
7388c2ecf20Sopenharmony_ci			bandwidth = 0; /* to silence the compiler */
7398c2ecf20Sopenharmony_ci		}
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci	return bandwidth;
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci/**
7458c2ecf20Sopenharmony_ci * __get_active_agg - get the current active aggregator
7468c2ecf20Sopenharmony_ci * @aggregator: the aggregator we're looking at
7478c2ecf20Sopenharmony_ci *
7488c2ecf20Sopenharmony_ci * Caller must hold RCU lock.
7498c2ecf20Sopenharmony_ci */
7508c2ecf20Sopenharmony_cistatic struct aggregator *__get_active_agg(struct aggregator *aggregator)
7518c2ecf20Sopenharmony_ci{
7528c2ecf20Sopenharmony_ci	struct bonding *bond = aggregator->slave->bond;
7538c2ecf20Sopenharmony_ci	struct list_head *iter;
7548c2ecf20Sopenharmony_ci	struct slave *slave;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	bond_for_each_slave_rcu(bond, slave, iter)
7578c2ecf20Sopenharmony_ci		if (SLAVE_AD_INFO(slave)->aggregator.is_active)
7588c2ecf20Sopenharmony_ci			return &(SLAVE_AD_INFO(slave)->aggregator);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	return NULL;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/**
7648c2ecf20Sopenharmony_ci * __update_lacpdu_from_port - update a port's lacpdu fields
7658c2ecf20Sopenharmony_ci * @port: the port we're looking at
7668c2ecf20Sopenharmony_ci */
7678c2ecf20Sopenharmony_cistatic inline void __update_lacpdu_from_port(struct port *port)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	struct lacpdu *lacpdu = &port->lacpdu;
7708c2ecf20Sopenharmony_ci	const struct port_params *partner = &port->partner_oper;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	/* update current actual Actor parameters
7738c2ecf20Sopenharmony_ci	 * lacpdu->subtype                   initialized
7748c2ecf20Sopenharmony_ci	 * lacpdu->version_number            initialized
7758c2ecf20Sopenharmony_ci	 * lacpdu->tlv_type_actor_info       initialized
7768c2ecf20Sopenharmony_ci	 * lacpdu->actor_information_length  initialized
7778c2ecf20Sopenharmony_ci	 */
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	lacpdu->actor_system_priority = htons(port->actor_system_priority);
7808c2ecf20Sopenharmony_ci	lacpdu->actor_system = port->actor_system;
7818c2ecf20Sopenharmony_ci	lacpdu->actor_key = htons(port->actor_oper_port_key);
7828c2ecf20Sopenharmony_ci	lacpdu->actor_port_priority = htons(port->actor_port_priority);
7838c2ecf20Sopenharmony_ci	lacpdu->actor_port = htons(port->actor_port_number);
7848c2ecf20Sopenharmony_ci	lacpdu->actor_state = port->actor_oper_port_state;
7858c2ecf20Sopenharmony_ci	slave_dbg(port->slave->bond->dev, port->slave->dev,
7868c2ecf20Sopenharmony_ci		  "update lacpdu: actor port state %x\n",
7878c2ecf20Sopenharmony_ci		  port->actor_oper_port_state);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	/* lacpdu->reserved_3_1              initialized
7908c2ecf20Sopenharmony_ci	 * lacpdu->tlv_type_partner_info     initialized
7918c2ecf20Sopenharmony_ci	 * lacpdu->partner_information_length initialized
7928c2ecf20Sopenharmony_ci	 */
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	lacpdu->partner_system_priority = htons(partner->system_priority);
7958c2ecf20Sopenharmony_ci	lacpdu->partner_system = partner->system;
7968c2ecf20Sopenharmony_ci	lacpdu->partner_key = htons(partner->key);
7978c2ecf20Sopenharmony_ci	lacpdu->partner_port_priority = htons(partner->port_priority);
7988c2ecf20Sopenharmony_ci	lacpdu->partner_port = htons(partner->port_number);
7998c2ecf20Sopenharmony_ci	lacpdu->partner_state = partner->port_state;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	/* lacpdu->reserved_3_2              initialized
8028c2ecf20Sopenharmony_ci	 * lacpdu->tlv_type_collector_info   initialized
8038c2ecf20Sopenharmony_ci	 * lacpdu->collector_information_length initialized
8048c2ecf20Sopenharmony_ci	 * collector_max_delay                initialized
8058c2ecf20Sopenharmony_ci	 * reserved_12[12]                   initialized
8068c2ecf20Sopenharmony_ci	 * tlv_type_terminator               initialized
8078c2ecf20Sopenharmony_ci	 * terminator_length                 initialized
8088c2ecf20Sopenharmony_ci	 * reserved_50[50]                   initialized
8098c2ecf20Sopenharmony_ci	 */
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci/* ================= main 802.3ad protocol code ========================= */
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci/**
8158c2ecf20Sopenharmony_ci * ad_lacpdu_send - send out a lacpdu packet on a given port
8168c2ecf20Sopenharmony_ci * @port: the port we're looking at
8178c2ecf20Sopenharmony_ci *
8188c2ecf20Sopenharmony_ci * Returns:   0 on success
8198c2ecf20Sopenharmony_ci *          < 0 on error
8208c2ecf20Sopenharmony_ci */
8218c2ecf20Sopenharmony_cistatic int ad_lacpdu_send(struct port *port)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	struct slave *slave = port->slave;
8248c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8258c2ecf20Sopenharmony_ci	struct lacpdu_header *lacpdu_header;
8268c2ecf20Sopenharmony_ci	int length = sizeof(struct lacpdu_header);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(length);
8298c2ecf20Sopenharmony_ci	if (!skb)
8308c2ecf20Sopenharmony_ci		return -ENOMEM;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	atomic64_inc(&SLAVE_AD_INFO(slave)->stats.lacpdu_tx);
8338c2ecf20Sopenharmony_ci	atomic64_inc(&BOND_AD_INFO(slave->bond).stats.lacpdu_tx);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	skb->dev = slave->dev;
8368c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
8378c2ecf20Sopenharmony_ci	skb->network_header = skb->mac_header + ETH_HLEN;
8388c2ecf20Sopenharmony_ci	skb->protocol = PKT_TYPE_LACPDU;
8398c2ecf20Sopenharmony_ci	skb->priority = TC_PRIO_CONTROL;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	lacpdu_header = skb_put(skb, length);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	ether_addr_copy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr);
8448c2ecf20Sopenharmony_ci	/* Note: source address is set to be the member's PERMANENT address,
8458c2ecf20Sopenharmony_ci	 * because we use it to identify loopback lacpdus in receive.
8468c2ecf20Sopenharmony_ci	 */
8478c2ecf20Sopenharmony_ci	ether_addr_copy(lacpdu_header->hdr.h_source, slave->perm_hwaddr);
8488c2ecf20Sopenharmony_ci	lacpdu_header->hdr.h_proto = PKT_TYPE_LACPDU;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	lacpdu_header->lacpdu = port->lacpdu;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	dev_queue_xmit(skb);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	return 0;
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci/**
8588c2ecf20Sopenharmony_ci * ad_marker_send - send marker information/response on a given port
8598c2ecf20Sopenharmony_ci * @port: the port we're looking at
8608c2ecf20Sopenharmony_ci * @marker: marker data to send
8618c2ecf20Sopenharmony_ci *
8628c2ecf20Sopenharmony_ci * Returns:   0 on success
8638c2ecf20Sopenharmony_ci *          < 0 on error
8648c2ecf20Sopenharmony_ci */
8658c2ecf20Sopenharmony_cistatic int ad_marker_send(struct port *port, struct bond_marker *marker)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	struct slave *slave = port->slave;
8688c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8698c2ecf20Sopenharmony_ci	struct bond_marker_header *marker_header;
8708c2ecf20Sopenharmony_ci	int length = sizeof(struct bond_marker_header);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(length + 16);
8738c2ecf20Sopenharmony_ci	if (!skb)
8748c2ecf20Sopenharmony_ci		return -ENOMEM;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	switch (marker->tlv_type) {
8778c2ecf20Sopenharmony_ci	case AD_MARKER_INFORMATION_SUBTYPE:
8788c2ecf20Sopenharmony_ci		atomic64_inc(&SLAVE_AD_INFO(slave)->stats.marker_tx);
8798c2ecf20Sopenharmony_ci		atomic64_inc(&BOND_AD_INFO(slave->bond).stats.marker_tx);
8808c2ecf20Sopenharmony_ci		break;
8818c2ecf20Sopenharmony_ci	case AD_MARKER_RESPONSE_SUBTYPE:
8828c2ecf20Sopenharmony_ci		atomic64_inc(&SLAVE_AD_INFO(slave)->stats.marker_resp_tx);
8838c2ecf20Sopenharmony_ci		atomic64_inc(&BOND_AD_INFO(slave->bond).stats.marker_resp_tx);
8848c2ecf20Sopenharmony_ci		break;
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	skb_reserve(skb, 16);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	skb->dev = slave->dev;
8908c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
8918c2ecf20Sopenharmony_ci	skb->network_header = skb->mac_header + ETH_HLEN;
8928c2ecf20Sopenharmony_ci	skb->protocol = PKT_TYPE_LACPDU;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	marker_header = skb_put(skb, length);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	ether_addr_copy(marker_header->hdr.h_dest, lacpdu_mcast_addr);
8978c2ecf20Sopenharmony_ci	/* Note: source address is set to be the member's PERMANENT address,
8988c2ecf20Sopenharmony_ci	 * because we use it to identify loopback MARKERs in receive.
8998c2ecf20Sopenharmony_ci	 */
9008c2ecf20Sopenharmony_ci	ether_addr_copy(marker_header->hdr.h_source, slave->perm_hwaddr);
9018c2ecf20Sopenharmony_ci	marker_header->hdr.h_proto = PKT_TYPE_LACPDU;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	marker_header->marker = *marker;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	dev_queue_xmit(skb);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	return 0;
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci/**
9118c2ecf20Sopenharmony_ci * ad_mux_machine - handle a port's mux state machine
9128c2ecf20Sopenharmony_ci * @port: the port we're looking at
9138c2ecf20Sopenharmony_ci * @update_slave_arr: Does slave array need update?
9148c2ecf20Sopenharmony_ci */
9158c2ecf20Sopenharmony_cistatic void ad_mux_machine(struct port *port, bool *update_slave_arr)
9168c2ecf20Sopenharmony_ci{
9178c2ecf20Sopenharmony_ci	mux_states_t last_state;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* keep current State Machine state to compare later if it was
9208c2ecf20Sopenharmony_ci	 * changed
9218c2ecf20Sopenharmony_ci	 */
9228c2ecf20Sopenharmony_ci	last_state = port->sm_mux_state;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if (port->sm_vars & AD_PORT_BEGIN) {
9258c2ecf20Sopenharmony_ci		port->sm_mux_state = AD_MUX_DETACHED;
9268c2ecf20Sopenharmony_ci	} else {
9278c2ecf20Sopenharmony_ci		switch (port->sm_mux_state) {
9288c2ecf20Sopenharmony_ci		case AD_MUX_DETACHED:
9298c2ecf20Sopenharmony_ci			if ((port->sm_vars & AD_PORT_SELECTED)
9308c2ecf20Sopenharmony_ci			    || (port->sm_vars & AD_PORT_STANDBY))
9318c2ecf20Sopenharmony_ci				/* if SELECTED or STANDBY */
9328c2ecf20Sopenharmony_ci				port->sm_mux_state = AD_MUX_WAITING;
9338c2ecf20Sopenharmony_ci			break;
9348c2ecf20Sopenharmony_ci		case AD_MUX_WAITING:
9358c2ecf20Sopenharmony_ci			/* if SELECTED == FALSE return to DETACH state */
9368c2ecf20Sopenharmony_ci			if (!(port->sm_vars & AD_PORT_SELECTED)) {
9378c2ecf20Sopenharmony_ci				port->sm_vars &= ~AD_PORT_READY_N;
9388c2ecf20Sopenharmony_ci				/* in order to withhold the Selection Logic to
9398c2ecf20Sopenharmony_ci				 * check all ports READY_N value every callback
9408c2ecf20Sopenharmony_ci				 * cycle to update ready variable, we check
9418c2ecf20Sopenharmony_ci				 * READY_N and update READY here
9428c2ecf20Sopenharmony_ci				 */
9438c2ecf20Sopenharmony_ci				__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
9448c2ecf20Sopenharmony_ci				port->sm_mux_state = AD_MUX_DETACHED;
9458c2ecf20Sopenharmony_ci				break;
9468c2ecf20Sopenharmony_ci			}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci			/* check if the wait_while_timer expired */
9498c2ecf20Sopenharmony_ci			if (port->sm_mux_timer_counter
9508c2ecf20Sopenharmony_ci			    && !(--port->sm_mux_timer_counter))
9518c2ecf20Sopenharmony_ci				port->sm_vars |= AD_PORT_READY_N;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci			/* in order to withhold the selection logic to check
9548c2ecf20Sopenharmony_ci			 * all ports READY_N value every callback cycle to
9558c2ecf20Sopenharmony_ci			 * update ready variable, we check READY_N and update
9568c2ecf20Sopenharmony_ci			 * READY here
9578c2ecf20Sopenharmony_ci			 */
9588c2ecf20Sopenharmony_ci			__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci			/* if the wait_while_timer expired, and the port is
9618c2ecf20Sopenharmony_ci			 * in READY state, move to ATTACHED state
9628c2ecf20Sopenharmony_ci			 */
9638c2ecf20Sopenharmony_ci			if ((port->sm_vars & AD_PORT_READY)
9648c2ecf20Sopenharmony_ci			    && !port->sm_mux_timer_counter)
9658c2ecf20Sopenharmony_ci				port->sm_mux_state = AD_MUX_ATTACHED;
9668c2ecf20Sopenharmony_ci			break;
9678c2ecf20Sopenharmony_ci		case AD_MUX_ATTACHED:
9688c2ecf20Sopenharmony_ci			/* check also if agg_select_timer expired (so the
9698c2ecf20Sopenharmony_ci			 * edable port will take place only after this timer)
9708c2ecf20Sopenharmony_ci			 */
9718c2ecf20Sopenharmony_ci			if ((port->sm_vars & AD_PORT_SELECTED) &&
9728c2ecf20Sopenharmony_ci			    (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) &&
9738c2ecf20Sopenharmony_ci			    !__check_agg_selection_timer(port)) {
9748c2ecf20Sopenharmony_ci				if (port->aggregator->is_active)
9758c2ecf20Sopenharmony_ci					port->sm_mux_state =
9768c2ecf20Sopenharmony_ci					    AD_MUX_COLLECTING_DISTRIBUTING;
9778c2ecf20Sopenharmony_ci			} else if (!(port->sm_vars & AD_PORT_SELECTED) ||
9788c2ecf20Sopenharmony_ci				   (port->sm_vars & AD_PORT_STANDBY)) {
9798c2ecf20Sopenharmony_ci				/* if UNSELECTED or STANDBY */
9808c2ecf20Sopenharmony_ci				port->sm_vars &= ~AD_PORT_READY_N;
9818c2ecf20Sopenharmony_ci				/* in order to withhold the selection logic to
9828c2ecf20Sopenharmony_ci				 * check all ports READY_N value every callback
9838c2ecf20Sopenharmony_ci				 * cycle to update ready variable, we check
9848c2ecf20Sopenharmony_ci				 * READY_N and update READY here
9858c2ecf20Sopenharmony_ci				 */
9868c2ecf20Sopenharmony_ci				__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
9878c2ecf20Sopenharmony_ci				port->sm_mux_state = AD_MUX_DETACHED;
9888c2ecf20Sopenharmony_ci			} else if (port->aggregator->is_active) {
9898c2ecf20Sopenharmony_ci				port->actor_oper_port_state |=
9908c2ecf20Sopenharmony_ci				    LACP_STATE_SYNCHRONIZATION;
9918c2ecf20Sopenharmony_ci			}
9928c2ecf20Sopenharmony_ci			break;
9938c2ecf20Sopenharmony_ci		case AD_MUX_COLLECTING_DISTRIBUTING:
9948c2ecf20Sopenharmony_ci			if (!(port->sm_vars & AD_PORT_SELECTED) ||
9958c2ecf20Sopenharmony_ci			    (port->sm_vars & AD_PORT_STANDBY) ||
9968c2ecf20Sopenharmony_ci			    !(port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) ||
9978c2ecf20Sopenharmony_ci			    !(port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) {
9988c2ecf20Sopenharmony_ci				port->sm_mux_state = AD_MUX_ATTACHED;
9998c2ecf20Sopenharmony_ci			} else {
10008c2ecf20Sopenharmony_ci				/* if port state hasn't changed make
10018c2ecf20Sopenharmony_ci				 * sure that a collecting distributing
10028c2ecf20Sopenharmony_ci				 * port in an active aggregator is enabled
10038c2ecf20Sopenharmony_ci				 */
10048c2ecf20Sopenharmony_ci				if (port->aggregator &&
10058c2ecf20Sopenharmony_ci				    port->aggregator->is_active &&
10068c2ecf20Sopenharmony_ci				    !__port_is_enabled(port)) {
10078c2ecf20Sopenharmony_ci					__enable_port(port);
10088c2ecf20Sopenharmony_ci					*update_slave_arr = true;
10098c2ecf20Sopenharmony_ci				}
10108c2ecf20Sopenharmony_ci			}
10118c2ecf20Sopenharmony_ci			break;
10128c2ecf20Sopenharmony_ci		default:
10138c2ecf20Sopenharmony_ci			break;
10148c2ecf20Sopenharmony_ci		}
10158c2ecf20Sopenharmony_ci	}
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* check if the state machine was changed */
10188c2ecf20Sopenharmony_ci	if (port->sm_mux_state != last_state) {
10198c2ecf20Sopenharmony_ci		slave_dbg(port->slave->bond->dev, port->slave->dev,
10208c2ecf20Sopenharmony_ci			  "Mux Machine: Port=%d, Last State=%d, Curr State=%d\n",
10218c2ecf20Sopenharmony_ci			  port->actor_port_number,
10228c2ecf20Sopenharmony_ci			  last_state,
10238c2ecf20Sopenharmony_ci			  port->sm_mux_state);
10248c2ecf20Sopenharmony_ci		switch (port->sm_mux_state) {
10258c2ecf20Sopenharmony_ci		case AD_MUX_DETACHED:
10268c2ecf20Sopenharmony_ci			port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION;
10278c2ecf20Sopenharmony_ci			ad_disable_collecting_distributing(port,
10288c2ecf20Sopenharmony_ci							   update_slave_arr);
10298c2ecf20Sopenharmony_ci			port->actor_oper_port_state &= ~LACP_STATE_COLLECTING;
10308c2ecf20Sopenharmony_ci			port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING;
10318c2ecf20Sopenharmony_ci			port->ntt = true;
10328c2ecf20Sopenharmony_ci			break;
10338c2ecf20Sopenharmony_ci		case AD_MUX_WAITING:
10348c2ecf20Sopenharmony_ci			port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0);
10358c2ecf20Sopenharmony_ci			break;
10368c2ecf20Sopenharmony_ci		case AD_MUX_ATTACHED:
10378c2ecf20Sopenharmony_ci			if (port->aggregator->is_active)
10388c2ecf20Sopenharmony_ci				port->actor_oper_port_state |=
10398c2ecf20Sopenharmony_ci				    LACP_STATE_SYNCHRONIZATION;
10408c2ecf20Sopenharmony_ci			else
10418c2ecf20Sopenharmony_ci				port->actor_oper_port_state &=
10428c2ecf20Sopenharmony_ci				    ~LACP_STATE_SYNCHRONIZATION;
10438c2ecf20Sopenharmony_ci			port->actor_oper_port_state &= ~LACP_STATE_COLLECTING;
10448c2ecf20Sopenharmony_ci			port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING;
10458c2ecf20Sopenharmony_ci			ad_disable_collecting_distributing(port,
10468c2ecf20Sopenharmony_ci							   update_slave_arr);
10478c2ecf20Sopenharmony_ci			port->ntt = true;
10488c2ecf20Sopenharmony_ci			break;
10498c2ecf20Sopenharmony_ci		case AD_MUX_COLLECTING_DISTRIBUTING:
10508c2ecf20Sopenharmony_ci			port->actor_oper_port_state |= LACP_STATE_COLLECTING;
10518c2ecf20Sopenharmony_ci			port->actor_oper_port_state |= LACP_STATE_DISTRIBUTING;
10528c2ecf20Sopenharmony_ci			port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION;
10538c2ecf20Sopenharmony_ci			ad_enable_collecting_distributing(port,
10548c2ecf20Sopenharmony_ci							  update_slave_arr);
10558c2ecf20Sopenharmony_ci			port->ntt = true;
10568c2ecf20Sopenharmony_ci			break;
10578c2ecf20Sopenharmony_ci		default:
10588c2ecf20Sopenharmony_ci			break;
10598c2ecf20Sopenharmony_ci		}
10608c2ecf20Sopenharmony_ci	}
10618c2ecf20Sopenharmony_ci}
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci/**
10648c2ecf20Sopenharmony_ci * ad_rx_machine - handle a port's rx State Machine
10658c2ecf20Sopenharmony_ci * @lacpdu: the lacpdu we've received
10668c2ecf20Sopenharmony_ci * @port: the port we're looking at
10678c2ecf20Sopenharmony_ci *
10688c2ecf20Sopenharmony_ci * If lacpdu arrived, stop previous timer (if exists) and set the next state as
10698c2ecf20Sopenharmony_ci * CURRENT. If timer expired set the state machine in the proper state.
10708c2ecf20Sopenharmony_ci * In other cases, this function checks if we need to switch to other state.
10718c2ecf20Sopenharmony_ci */
10728c2ecf20Sopenharmony_cistatic void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	rx_states_t last_state;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	/* keep current State Machine state to compare later if it was
10778c2ecf20Sopenharmony_ci	 * changed
10788c2ecf20Sopenharmony_ci	 */
10798c2ecf20Sopenharmony_ci	last_state = port->sm_rx_state;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	if (lacpdu) {
10828c2ecf20Sopenharmony_ci		atomic64_inc(&SLAVE_AD_INFO(port->slave)->stats.lacpdu_rx);
10838c2ecf20Sopenharmony_ci		atomic64_inc(&BOND_AD_INFO(port->slave->bond).stats.lacpdu_rx);
10848c2ecf20Sopenharmony_ci	}
10858c2ecf20Sopenharmony_ci	/* check if state machine should change state */
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	/* first, check if port was reinitialized */
10888c2ecf20Sopenharmony_ci	if (port->sm_vars & AD_PORT_BEGIN) {
10898c2ecf20Sopenharmony_ci		port->sm_rx_state = AD_RX_INITIALIZE;
10908c2ecf20Sopenharmony_ci		port->sm_vars |= AD_PORT_CHURNED;
10918c2ecf20Sopenharmony_ci	/* check if port is not enabled */
10928c2ecf20Sopenharmony_ci	} else if (!(port->sm_vars & AD_PORT_BEGIN) && !port->is_enabled)
10938c2ecf20Sopenharmony_ci		port->sm_rx_state = AD_RX_PORT_DISABLED;
10948c2ecf20Sopenharmony_ci	/* check if new lacpdu arrived */
10958c2ecf20Sopenharmony_ci	else if (lacpdu && ((port->sm_rx_state == AD_RX_EXPIRED) ||
10968c2ecf20Sopenharmony_ci		 (port->sm_rx_state == AD_RX_DEFAULTED) ||
10978c2ecf20Sopenharmony_ci		 (port->sm_rx_state == AD_RX_CURRENT))) {
10988c2ecf20Sopenharmony_ci		if (port->sm_rx_state != AD_RX_CURRENT)
10998c2ecf20Sopenharmony_ci			port->sm_vars |= AD_PORT_CHURNED;
11008c2ecf20Sopenharmony_ci		port->sm_rx_timer_counter = 0;
11018c2ecf20Sopenharmony_ci		port->sm_rx_state = AD_RX_CURRENT;
11028c2ecf20Sopenharmony_ci	} else {
11038c2ecf20Sopenharmony_ci		/* if timer is on, and if it is expired */
11048c2ecf20Sopenharmony_ci		if (port->sm_rx_timer_counter &&
11058c2ecf20Sopenharmony_ci		    !(--port->sm_rx_timer_counter)) {
11068c2ecf20Sopenharmony_ci			switch (port->sm_rx_state) {
11078c2ecf20Sopenharmony_ci			case AD_RX_EXPIRED:
11088c2ecf20Sopenharmony_ci				port->sm_rx_state = AD_RX_DEFAULTED;
11098c2ecf20Sopenharmony_ci				break;
11108c2ecf20Sopenharmony_ci			case AD_RX_CURRENT:
11118c2ecf20Sopenharmony_ci				port->sm_rx_state = AD_RX_EXPIRED;
11128c2ecf20Sopenharmony_ci				break;
11138c2ecf20Sopenharmony_ci			default:
11148c2ecf20Sopenharmony_ci				break;
11158c2ecf20Sopenharmony_ci			}
11168c2ecf20Sopenharmony_ci		} else {
11178c2ecf20Sopenharmony_ci			/* if no lacpdu arrived and no timer is on */
11188c2ecf20Sopenharmony_ci			switch (port->sm_rx_state) {
11198c2ecf20Sopenharmony_ci			case AD_RX_PORT_DISABLED:
11208c2ecf20Sopenharmony_ci				if (port->is_enabled &&
11218c2ecf20Sopenharmony_ci				    (port->sm_vars & AD_PORT_LACP_ENABLED))
11228c2ecf20Sopenharmony_ci					port->sm_rx_state = AD_RX_EXPIRED;
11238c2ecf20Sopenharmony_ci				else if (port->is_enabled
11248c2ecf20Sopenharmony_ci					 && ((port->sm_vars
11258c2ecf20Sopenharmony_ci					      & AD_PORT_LACP_ENABLED) == 0))
11268c2ecf20Sopenharmony_ci					port->sm_rx_state = AD_RX_LACP_DISABLED;
11278c2ecf20Sopenharmony_ci				break;
11288c2ecf20Sopenharmony_ci			default:
11298c2ecf20Sopenharmony_ci				break;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci			}
11328c2ecf20Sopenharmony_ci		}
11338c2ecf20Sopenharmony_ci	}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	/* check if the State machine was changed or new lacpdu arrived */
11368c2ecf20Sopenharmony_ci	if ((port->sm_rx_state != last_state) || (lacpdu)) {
11378c2ecf20Sopenharmony_ci		slave_dbg(port->slave->bond->dev, port->slave->dev,
11388c2ecf20Sopenharmony_ci			  "Rx Machine: Port=%d, Last State=%d, Curr State=%d\n",
11398c2ecf20Sopenharmony_ci			  port->actor_port_number,
11408c2ecf20Sopenharmony_ci			  last_state,
11418c2ecf20Sopenharmony_ci			  port->sm_rx_state);
11428c2ecf20Sopenharmony_ci		switch (port->sm_rx_state) {
11438c2ecf20Sopenharmony_ci		case AD_RX_INITIALIZE:
11448c2ecf20Sopenharmony_ci			if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS))
11458c2ecf20Sopenharmony_ci				port->sm_vars &= ~AD_PORT_LACP_ENABLED;
11468c2ecf20Sopenharmony_ci			else
11478c2ecf20Sopenharmony_ci				port->sm_vars |= AD_PORT_LACP_ENABLED;
11488c2ecf20Sopenharmony_ci			port->sm_vars &= ~AD_PORT_SELECTED;
11498c2ecf20Sopenharmony_ci			__record_default(port);
11508c2ecf20Sopenharmony_ci			port->actor_oper_port_state &= ~LACP_STATE_EXPIRED;
11518c2ecf20Sopenharmony_ci			port->sm_rx_state = AD_RX_PORT_DISABLED;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci			fallthrough;
11548c2ecf20Sopenharmony_ci		case AD_RX_PORT_DISABLED:
11558c2ecf20Sopenharmony_ci			port->sm_vars &= ~AD_PORT_MATCHED;
11568c2ecf20Sopenharmony_ci			break;
11578c2ecf20Sopenharmony_ci		case AD_RX_LACP_DISABLED:
11588c2ecf20Sopenharmony_ci			port->sm_vars &= ~AD_PORT_SELECTED;
11598c2ecf20Sopenharmony_ci			__record_default(port);
11608c2ecf20Sopenharmony_ci			port->partner_oper.port_state &= ~LACP_STATE_AGGREGATION;
11618c2ecf20Sopenharmony_ci			port->sm_vars |= AD_PORT_MATCHED;
11628c2ecf20Sopenharmony_ci			port->actor_oper_port_state &= ~LACP_STATE_EXPIRED;
11638c2ecf20Sopenharmony_ci			break;
11648c2ecf20Sopenharmony_ci		case AD_RX_EXPIRED:
11658c2ecf20Sopenharmony_ci			/* Reset of the Synchronization flag (Standard 43.4.12)
11668c2ecf20Sopenharmony_ci			 * This reset cause to disable this port in the
11678c2ecf20Sopenharmony_ci			 * COLLECTING_DISTRIBUTING state of the mux machine in
11688c2ecf20Sopenharmony_ci			 * case of EXPIRED even if LINK_DOWN didn't arrive for
11698c2ecf20Sopenharmony_ci			 * the port.
11708c2ecf20Sopenharmony_ci			 */
11718c2ecf20Sopenharmony_ci			port->partner_oper.port_state &= ~LACP_STATE_SYNCHRONIZATION;
11728c2ecf20Sopenharmony_ci			port->sm_vars &= ~AD_PORT_MATCHED;
11738c2ecf20Sopenharmony_ci			port->partner_oper.port_state |= LACP_STATE_LACP_TIMEOUT;
11748c2ecf20Sopenharmony_ci			port->partner_oper.port_state |= LACP_STATE_LACP_ACTIVITY;
11758c2ecf20Sopenharmony_ci			port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(AD_SHORT_TIMEOUT));
11768c2ecf20Sopenharmony_ci			port->actor_oper_port_state |= LACP_STATE_EXPIRED;
11778c2ecf20Sopenharmony_ci			port->sm_vars |= AD_PORT_CHURNED;
11788c2ecf20Sopenharmony_ci			break;
11798c2ecf20Sopenharmony_ci		case AD_RX_DEFAULTED:
11808c2ecf20Sopenharmony_ci			__update_default_selected(port);
11818c2ecf20Sopenharmony_ci			__record_default(port);
11828c2ecf20Sopenharmony_ci			port->sm_vars |= AD_PORT_MATCHED;
11838c2ecf20Sopenharmony_ci			port->actor_oper_port_state &= ~LACP_STATE_EXPIRED;
11848c2ecf20Sopenharmony_ci			break;
11858c2ecf20Sopenharmony_ci		case AD_RX_CURRENT:
11868c2ecf20Sopenharmony_ci			/* detect loopback situation */
11878c2ecf20Sopenharmony_ci			if (MAC_ADDRESS_EQUAL(&(lacpdu->actor_system),
11888c2ecf20Sopenharmony_ci					      &(port->actor_system))) {
11898c2ecf20Sopenharmony_ci				slave_err(port->slave->bond->dev, port->slave->dev, "An illegal loopback occurred on slave\n"
11908c2ecf20Sopenharmony_ci					  "Check the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n");
11918c2ecf20Sopenharmony_ci				return;
11928c2ecf20Sopenharmony_ci			}
11938c2ecf20Sopenharmony_ci			__update_selected(lacpdu, port);
11948c2ecf20Sopenharmony_ci			__update_ntt(lacpdu, port);
11958c2ecf20Sopenharmony_ci			__record_pdu(lacpdu, port);
11968c2ecf20Sopenharmony_ci			port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT));
11978c2ecf20Sopenharmony_ci			port->actor_oper_port_state &= ~LACP_STATE_EXPIRED;
11988c2ecf20Sopenharmony_ci			break;
11998c2ecf20Sopenharmony_ci		default:
12008c2ecf20Sopenharmony_ci			break;
12018c2ecf20Sopenharmony_ci		}
12028c2ecf20Sopenharmony_ci	}
12038c2ecf20Sopenharmony_ci}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci/**
12068c2ecf20Sopenharmony_ci * ad_churn_machine - handle port churn's state machine
12078c2ecf20Sopenharmony_ci * @port: the port we're looking at
12088c2ecf20Sopenharmony_ci *
12098c2ecf20Sopenharmony_ci */
12108c2ecf20Sopenharmony_cistatic void ad_churn_machine(struct port *port)
12118c2ecf20Sopenharmony_ci{
12128c2ecf20Sopenharmony_ci	if (port->sm_vars & AD_PORT_CHURNED) {
12138c2ecf20Sopenharmony_ci		port->sm_vars &= ~AD_PORT_CHURNED;
12148c2ecf20Sopenharmony_ci		port->sm_churn_actor_state = AD_CHURN_MONITOR;
12158c2ecf20Sopenharmony_ci		port->sm_churn_partner_state = AD_CHURN_MONITOR;
12168c2ecf20Sopenharmony_ci		port->sm_churn_actor_timer_counter =
12178c2ecf20Sopenharmony_ci			__ad_timer_to_ticks(AD_ACTOR_CHURN_TIMER, 0);
12188c2ecf20Sopenharmony_ci		port->sm_churn_partner_timer_counter =
12198c2ecf20Sopenharmony_ci			 __ad_timer_to_ticks(AD_PARTNER_CHURN_TIMER, 0);
12208c2ecf20Sopenharmony_ci		return;
12218c2ecf20Sopenharmony_ci	}
12228c2ecf20Sopenharmony_ci	if (port->sm_churn_actor_timer_counter &&
12238c2ecf20Sopenharmony_ci	    !(--port->sm_churn_actor_timer_counter) &&
12248c2ecf20Sopenharmony_ci	    port->sm_churn_actor_state == AD_CHURN_MONITOR) {
12258c2ecf20Sopenharmony_ci		if (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION) {
12268c2ecf20Sopenharmony_ci			port->sm_churn_actor_state = AD_NO_CHURN;
12278c2ecf20Sopenharmony_ci		} else {
12288c2ecf20Sopenharmony_ci			port->churn_actor_count++;
12298c2ecf20Sopenharmony_ci			port->sm_churn_actor_state = AD_CHURN;
12308c2ecf20Sopenharmony_ci		}
12318c2ecf20Sopenharmony_ci	}
12328c2ecf20Sopenharmony_ci	if (port->sm_churn_partner_timer_counter &&
12338c2ecf20Sopenharmony_ci	    !(--port->sm_churn_partner_timer_counter) &&
12348c2ecf20Sopenharmony_ci	    port->sm_churn_partner_state == AD_CHURN_MONITOR) {
12358c2ecf20Sopenharmony_ci		if (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) {
12368c2ecf20Sopenharmony_ci			port->sm_churn_partner_state = AD_NO_CHURN;
12378c2ecf20Sopenharmony_ci		} else {
12388c2ecf20Sopenharmony_ci			port->churn_partner_count++;
12398c2ecf20Sopenharmony_ci			port->sm_churn_partner_state = AD_CHURN;
12408c2ecf20Sopenharmony_ci		}
12418c2ecf20Sopenharmony_ci	}
12428c2ecf20Sopenharmony_ci}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci/**
12458c2ecf20Sopenharmony_ci * ad_tx_machine - handle a port's tx state machine
12468c2ecf20Sopenharmony_ci * @port: the port we're looking at
12478c2ecf20Sopenharmony_ci */
12488c2ecf20Sopenharmony_cistatic void ad_tx_machine(struct port *port)
12498c2ecf20Sopenharmony_ci{
12508c2ecf20Sopenharmony_ci	/* check if tx timer expired, to verify that we do not send more than
12518c2ecf20Sopenharmony_ci	 * 3 packets per second
12528c2ecf20Sopenharmony_ci	 */
12538c2ecf20Sopenharmony_ci	if (port->sm_tx_timer_counter && !(--port->sm_tx_timer_counter)) {
12548c2ecf20Sopenharmony_ci		/* check if there is something to send */
12558c2ecf20Sopenharmony_ci		if (port->ntt && (port->sm_vars & AD_PORT_LACP_ENABLED)) {
12568c2ecf20Sopenharmony_ci			__update_lacpdu_from_port(port);
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci			if (ad_lacpdu_send(port) >= 0) {
12598c2ecf20Sopenharmony_ci				slave_dbg(port->slave->bond->dev,
12608c2ecf20Sopenharmony_ci					  port->slave->dev,
12618c2ecf20Sopenharmony_ci					  "Sent LACPDU on port %d\n",
12628c2ecf20Sopenharmony_ci					  port->actor_port_number);
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci				/* mark ntt as false, so it will not be sent
12658c2ecf20Sopenharmony_ci				 * again until demanded
12668c2ecf20Sopenharmony_ci				 */
12678c2ecf20Sopenharmony_ci				port->ntt = false;
12688c2ecf20Sopenharmony_ci			}
12698c2ecf20Sopenharmony_ci		}
12708c2ecf20Sopenharmony_ci		/* restart tx timer(to verify that we will not exceed
12718c2ecf20Sopenharmony_ci		 * AD_MAX_TX_IN_SECOND
12728c2ecf20Sopenharmony_ci		 */
12738c2ecf20Sopenharmony_ci		port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND;
12748c2ecf20Sopenharmony_ci	}
12758c2ecf20Sopenharmony_ci}
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci/**
12788c2ecf20Sopenharmony_ci * ad_periodic_machine - handle a port's periodic state machine
12798c2ecf20Sopenharmony_ci * @port: the port we're looking at
12808c2ecf20Sopenharmony_ci *
12818c2ecf20Sopenharmony_ci * Turn ntt flag on priodically to perform periodic transmission of lacpdu's.
12828c2ecf20Sopenharmony_ci */
12838c2ecf20Sopenharmony_cistatic void ad_periodic_machine(struct port *port)
12848c2ecf20Sopenharmony_ci{
12858c2ecf20Sopenharmony_ci	periodic_states_t last_state;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	/* keep current state machine state to compare later if it was changed */
12888c2ecf20Sopenharmony_ci	last_state = port->sm_periodic_state;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	/* check if port was reinitialized */
12918c2ecf20Sopenharmony_ci	if (((port->sm_vars & AD_PORT_BEGIN) || !(port->sm_vars & AD_PORT_LACP_ENABLED) || !port->is_enabled) ||
12928c2ecf20Sopenharmony_ci	    (!(port->actor_oper_port_state & LACP_STATE_LACP_ACTIVITY) && !(port->partner_oper.port_state & LACP_STATE_LACP_ACTIVITY))
12938c2ecf20Sopenharmony_ci	   ) {
12948c2ecf20Sopenharmony_ci		port->sm_periodic_state = AD_NO_PERIODIC;
12958c2ecf20Sopenharmony_ci	}
12968c2ecf20Sopenharmony_ci	/* check if state machine should change state */
12978c2ecf20Sopenharmony_ci	else if (port->sm_periodic_timer_counter) {
12988c2ecf20Sopenharmony_ci		/* check if periodic state machine expired */
12998c2ecf20Sopenharmony_ci		if (!(--port->sm_periodic_timer_counter)) {
13008c2ecf20Sopenharmony_ci			/* if expired then do tx */
13018c2ecf20Sopenharmony_ci			port->sm_periodic_state = AD_PERIODIC_TX;
13028c2ecf20Sopenharmony_ci		} else {
13038c2ecf20Sopenharmony_ci			/* If not expired, check if there is some new timeout
13048c2ecf20Sopenharmony_ci			 * parameter from the partner state
13058c2ecf20Sopenharmony_ci			 */
13068c2ecf20Sopenharmony_ci			switch (port->sm_periodic_state) {
13078c2ecf20Sopenharmony_ci			case AD_FAST_PERIODIC:
13088c2ecf20Sopenharmony_ci				if (!(port->partner_oper.port_state
13098c2ecf20Sopenharmony_ci				      & LACP_STATE_LACP_TIMEOUT))
13108c2ecf20Sopenharmony_ci					port->sm_periodic_state = AD_SLOW_PERIODIC;
13118c2ecf20Sopenharmony_ci				break;
13128c2ecf20Sopenharmony_ci			case AD_SLOW_PERIODIC:
13138c2ecf20Sopenharmony_ci				if ((port->partner_oper.port_state & LACP_STATE_LACP_TIMEOUT)) {
13148c2ecf20Sopenharmony_ci					port->sm_periodic_timer_counter = 0;
13158c2ecf20Sopenharmony_ci					port->sm_periodic_state = AD_PERIODIC_TX;
13168c2ecf20Sopenharmony_ci				}
13178c2ecf20Sopenharmony_ci				break;
13188c2ecf20Sopenharmony_ci			default:
13198c2ecf20Sopenharmony_ci				break;
13208c2ecf20Sopenharmony_ci			}
13218c2ecf20Sopenharmony_ci		}
13228c2ecf20Sopenharmony_ci	} else {
13238c2ecf20Sopenharmony_ci		switch (port->sm_periodic_state) {
13248c2ecf20Sopenharmony_ci		case AD_NO_PERIODIC:
13258c2ecf20Sopenharmony_ci			port->sm_periodic_state = AD_FAST_PERIODIC;
13268c2ecf20Sopenharmony_ci			break;
13278c2ecf20Sopenharmony_ci		case AD_PERIODIC_TX:
13288c2ecf20Sopenharmony_ci			if (!(port->partner_oper.port_state &
13298c2ecf20Sopenharmony_ci			    LACP_STATE_LACP_TIMEOUT))
13308c2ecf20Sopenharmony_ci				port->sm_periodic_state = AD_SLOW_PERIODIC;
13318c2ecf20Sopenharmony_ci			else
13328c2ecf20Sopenharmony_ci				port->sm_periodic_state = AD_FAST_PERIODIC;
13338c2ecf20Sopenharmony_ci			break;
13348c2ecf20Sopenharmony_ci		default:
13358c2ecf20Sopenharmony_ci			break;
13368c2ecf20Sopenharmony_ci		}
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	/* check if the state machine was changed */
13408c2ecf20Sopenharmony_ci	if (port->sm_periodic_state != last_state) {
13418c2ecf20Sopenharmony_ci		slave_dbg(port->slave->bond->dev, port->slave->dev,
13428c2ecf20Sopenharmony_ci			  "Periodic Machine: Port=%d, Last State=%d, Curr State=%d\n",
13438c2ecf20Sopenharmony_ci			  port->actor_port_number, last_state,
13448c2ecf20Sopenharmony_ci			  port->sm_periodic_state);
13458c2ecf20Sopenharmony_ci		switch (port->sm_periodic_state) {
13468c2ecf20Sopenharmony_ci		case AD_NO_PERIODIC:
13478c2ecf20Sopenharmony_ci			port->sm_periodic_timer_counter = 0;
13488c2ecf20Sopenharmony_ci			break;
13498c2ecf20Sopenharmony_ci		case AD_FAST_PERIODIC:
13508c2ecf20Sopenharmony_ci			/* decrement 1 tick we lost in the PERIODIC_TX cycle */
13518c2ecf20Sopenharmony_ci			port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_FAST_PERIODIC_TIME))-1;
13528c2ecf20Sopenharmony_ci			break;
13538c2ecf20Sopenharmony_ci		case AD_SLOW_PERIODIC:
13548c2ecf20Sopenharmony_ci			/* decrement 1 tick we lost in the PERIODIC_TX cycle */
13558c2ecf20Sopenharmony_ci			port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_SLOW_PERIODIC_TIME))-1;
13568c2ecf20Sopenharmony_ci			break;
13578c2ecf20Sopenharmony_ci		case AD_PERIODIC_TX:
13588c2ecf20Sopenharmony_ci			port->ntt = true;
13598c2ecf20Sopenharmony_ci			break;
13608c2ecf20Sopenharmony_ci		default:
13618c2ecf20Sopenharmony_ci			break;
13628c2ecf20Sopenharmony_ci		}
13638c2ecf20Sopenharmony_ci	}
13648c2ecf20Sopenharmony_ci}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci/**
13678c2ecf20Sopenharmony_ci * ad_port_selection_logic - select aggregation groups
13688c2ecf20Sopenharmony_ci * @port: the port we're looking at
13698c2ecf20Sopenharmony_ci * @update_slave_arr: Does slave array need update?
13708c2ecf20Sopenharmony_ci *
13718c2ecf20Sopenharmony_ci * Select aggregation groups, and assign each port for it's aggregetor. The
13728c2ecf20Sopenharmony_ci * selection logic is called in the inititalization (after all the handshkes),
13738c2ecf20Sopenharmony_ci * and after every lacpdu receive (if selected is off).
13748c2ecf20Sopenharmony_ci */
13758c2ecf20Sopenharmony_cistatic void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
13768c2ecf20Sopenharmony_ci{
13778c2ecf20Sopenharmony_ci	struct aggregator *aggregator, *free_aggregator = NULL, *temp_aggregator;
13788c2ecf20Sopenharmony_ci	struct port *last_port = NULL, *curr_port;
13798c2ecf20Sopenharmony_ci	struct list_head *iter;
13808c2ecf20Sopenharmony_ci	struct bonding *bond;
13818c2ecf20Sopenharmony_ci	struct slave *slave;
13828c2ecf20Sopenharmony_ci	int found = 0;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	/* if the port is already Selected, do nothing */
13858c2ecf20Sopenharmony_ci	if (port->sm_vars & AD_PORT_SELECTED)
13868c2ecf20Sopenharmony_ci		return;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	bond = __get_bond_by_port(port);
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	/* if the port is connected to other aggregator, detach it */
13918c2ecf20Sopenharmony_ci	if (port->aggregator) {
13928c2ecf20Sopenharmony_ci		/* detach the port from its former aggregator */
13938c2ecf20Sopenharmony_ci		temp_aggregator = port->aggregator;
13948c2ecf20Sopenharmony_ci		for (curr_port = temp_aggregator->lag_ports; curr_port;
13958c2ecf20Sopenharmony_ci		     last_port = curr_port,
13968c2ecf20Sopenharmony_ci		     curr_port = curr_port->next_port_in_aggregator) {
13978c2ecf20Sopenharmony_ci			if (curr_port == port) {
13988c2ecf20Sopenharmony_ci				temp_aggregator->num_of_ports--;
13998c2ecf20Sopenharmony_ci				/* if it is the first port attached to the
14008c2ecf20Sopenharmony_ci				 * aggregator
14018c2ecf20Sopenharmony_ci				 */
14028c2ecf20Sopenharmony_ci				if (!last_port) {
14038c2ecf20Sopenharmony_ci					temp_aggregator->lag_ports =
14048c2ecf20Sopenharmony_ci						port->next_port_in_aggregator;
14058c2ecf20Sopenharmony_ci				} else {
14068c2ecf20Sopenharmony_ci					/* not the first port attached to the
14078c2ecf20Sopenharmony_ci					 * aggregator
14088c2ecf20Sopenharmony_ci					 */
14098c2ecf20Sopenharmony_ci					last_port->next_port_in_aggregator =
14108c2ecf20Sopenharmony_ci						port->next_port_in_aggregator;
14118c2ecf20Sopenharmony_ci				}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci				/* clear the port's relations to this
14148c2ecf20Sopenharmony_ci				 * aggregator
14158c2ecf20Sopenharmony_ci				 */
14168c2ecf20Sopenharmony_ci				port->aggregator = NULL;
14178c2ecf20Sopenharmony_ci				port->next_port_in_aggregator = NULL;
14188c2ecf20Sopenharmony_ci				port->actor_port_aggregator_identifier = 0;
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci				slave_dbg(bond->dev, port->slave->dev, "Port %d left LAG %d\n",
14218c2ecf20Sopenharmony_ci					  port->actor_port_number,
14228c2ecf20Sopenharmony_ci					  temp_aggregator->aggregator_identifier);
14238c2ecf20Sopenharmony_ci				/* if the aggregator is empty, clear its
14248c2ecf20Sopenharmony_ci				 * parameters, and set it ready to be attached
14258c2ecf20Sopenharmony_ci				 */
14268c2ecf20Sopenharmony_ci				if (!temp_aggregator->lag_ports)
14278c2ecf20Sopenharmony_ci					ad_clear_agg(temp_aggregator);
14288c2ecf20Sopenharmony_ci				break;
14298c2ecf20Sopenharmony_ci			}
14308c2ecf20Sopenharmony_ci		}
14318c2ecf20Sopenharmony_ci		if (!curr_port) {
14328c2ecf20Sopenharmony_ci			/* meaning: the port was related to an aggregator
14338c2ecf20Sopenharmony_ci			 * but was not on the aggregator port list
14348c2ecf20Sopenharmony_ci			 */
14358c2ecf20Sopenharmony_ci			net_warn_ratelimited("%s: (slave %s): Warning: Port %d was related to aggregator %d but was not on its port list\n",
14368c2ecf20Sopenharmony_ci					     port->slave->bond->dev->name,
14378c2ecf20Sopenharmony_ci					     port->slave->dev->name,
14388c2ecf20Sopenharmony_ci					     port->actor_port_number,
14398c2ecf20Sopenharmony_ci					     port->aggregator->aggregator_identifier);
14408c2ecf20Sopenharmony_ci		}
14418c2ecf20Sopenharmony_ci	}
14428c2ecf20Sopenharmony_ci	/* search on all aggregators for a suitable aggregator for this port */
14438c2ecf20Sopenharmony_ci	bond_for_each_slave(bond, slave, iter) {
14448c2ecf20Sopenharmony_ci		aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci		/* keep a free aggregator for later use(if needed) */
14478c2ecf20Sopenharmony_ci		if (!aggregator->lag_ports) {
14488c2ecf20Sopenharmony_ci			if (!free_aggregator)
14498c2ecf20Sopenharmony_ci				free_aggregator = aggregator;
14508c2ecf20Sopenharmony_ci			continue;
14518c2ecf20Sopenharmony_ci		}
14528c2ecf20Sopenharmony_ci		/* check if current aggregator suits us */
14538c2ecf20Sopenharmony_ci		if (((aggregator->actor_oper_aggregator_key == port->actor_oper_port_key) && /* if all parameters match AND */
14548c2ecf20Sopenharmony_ci		     MAC_ADDRESS_EQUAL(&(aggregator->partner_system), &(port->partner_oper.system)) &&
14558c2ecf20Sopenharmony_ci		     (aggregator->partner_system_priority == port->partner_oper.system_priority) &&
14568c2ecf20Sopenharmony_ci		     (aggregator->partner_oper_aggregator_key == port->partner_oper.key)
14578c2ecf20Sopenharmony_ci		    ) &&
14588c2ecf20Sopenharmony_ci		    ((!MAC_ADDRESS_EQUAL(&(port->partner_oper.system), &(null_mac_addr)) && /* partner answers */
14598c2ecf20Sopenharmony_ci		      !aggregator->is_individual)  /* but is not individual OR */
14608c2ecf20Sopenharmony_ci		    )
14618c2ecf20Sopenharmony_ci		   ) {
14628c2ecf20Sopenharmony_ci			/* attach to the founded aggregator */
14638c2ecf20Sopenharmony_ci			port->aggregator = aggregator;
14648c2ecf20Sopenharmony_ci			port->actor_port_aggregator_identifier =
14658c2ecf20Sopenharmony_ci				port->aggregator->aggregator_identifier;
14668c2ecf20Sopenharmony_ci			port->next_port_in_aggregator = aggregator->lag_ports;
14678c2ecf20Sopenharmony_ci			port->aggregator->num_of_ports++;
14688c2ecf20Sopenharmony_ci			aggregator->lag_ports = port;
14698c2ecf20Sopenharmony_ci			slave_dbg(bond->dev, slave->dev, "Port %d joined LAG %d (existing LAG)\n",
14708c2ecf20Sopenharmony_ci				  port->actor_port_number,
14718c2ecf20Sopenharmony_ci				  port->aggregator->aggregator_identifier);
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci			/* mark this port as selected */
14748c2ecf20Sopenharmony_ci			port->sm_vars |= AD_PORT_SELECTED;
14758c2ecf20Sopenharmony_ci			found = 1;
14768c2ecf20Sopenharmony_ci			break;
14778c2ecf20Sopenharmony_ci		}
14788c2ecf20Sopenharmony_ci	}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	/* the port couldn't find an aggregator - attach it to a new
14818c2ecf20Sopenharmony_ci	 * aggregator
14828c2ecf20Sopenharmony_ci	 */
14838c2ecf20Sopenharmony_ci	if (!found) {
14848c2ecf20Sopenharmony_ci		if (free_aggregator) {
14858c2ecf20Sopenharmony_ci			/* assign port a new aggregator */
14868c2ecf20Sopenharmony_ci			port->aggregator = free_aggregator;
14878c2ecf20Sopenharmony_ci			port->actor_port_aggregator_identifier =
14888c2ecf20Sopenharmony_ci				port->aggregator->aggregator_identifier;
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci			/* update the new aggregator's parameters
14918c2ecf20Sopenharmony_ci			 * if port was responsed from the end-user
14928c2ecf20Sopenharmony_ci			 */
14938c2ecf20Sopenharmony_ci			if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)
14948c2ecf20Sopenharmony_ci				/* if port is full duplex */
14958c2ecf20Sopenharmony_ci				port->aggregator->is_individual = false;
14968c2ecf20Sopenharmony_ci			else
14978c2ecf20Sopenharmony_ci				port->aggregator->is_individual = true;
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci			port->aggregator->actor_admin_aggregator_key =
15008c2ecf20Sopenharmony_ci				port->actor_admin_port_key;
15018c2ecf20Sopenharmony_ci			port->aggregator->actor_oper_aggregator_key =
15028c2ecf20Sopenharmony_ci				port->actor_oper_port_key;
15038c2ecf20Sopenharmony_ci			port->aggregator->partner_system =
15048c2ecf20Sopenharmony_ci				port->partner_oper.system;
15058c2ecf20Sopenharmony_ci			port->aggregator->partner_system_priority =
15068c2ecf20Sopenharmony_ci				port->partner_oper.system_priority;
15078c2ecf20Sopenharmony_ci			port->aggregator->partner_oper_aggregator_key = port->partner_oper.key;
15088c2ecf20Sopenharmony_ci			port->aggregator->receive_state = 1;
15098c2ecf20Sopenharmony_ci			port->aggregator->transmit_state = 1;
15108c2ecf20Sopenharmony_ci			port->aggregator->lag_ports = port;
15118c2ecf20Sopenharmony_ci			port->aggregator->num_of_ports++;
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci			/* mark this port as selected */
15148c2ecf20Sopenharmony_ci			port->sm_vars |= AD_PORT_SELECTED;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci			slave_dbg(bond->dev, port->slave->dev, "Port %d joined LAG %d (new LAG)\n",
15178c2ecf20Sopenharmony_ci				  port->actor_port_number,
15188c2ecf20Sopenharmony_ci				  port->aggregator->aggregator_identifier);
15198c2ecf20Sopenharmony_ci		} else {
15208c2ecf20Sopenharmony_ci			slave_err(bond->dev, port->slave->dev,
15218c2ecf20Sopenharmony_ci				  "Port %d did not find a suitable aggregator\n",
15228c2ecf20Sopenharmony_ci				  port->actor_port_number);
15238c2ecf20Sopenharmony_ci		}
15248c2ecf20Sopenharmony_ci	}
15258c2ecf20Sopenharmony_ci	/* if all aggregator's ports are READY_N == TRUE, set ready=TRUE
15268c2ecf20Sopenharmony_ci	 * in all aggregator's ports, else set ready=FALSE in all
15278c2ecf20Sopenharmony_ci	 * aggregator's ports
15288c2ecf20Sopenharmony_ci	 */
15298c2ecf20Sopenharmony_ci	__set_agg_ports_ready(port->aggregator,
15308c2ecf20Sopenharmony_ci			      __agg_ports_are_ready(port->aggregator));
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	aggregator = __get_first_agg(port);
15338c2ecf20Sopenharmony_ci	ad_agg_selection_logic(aggregator, update_slave_arr);
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	if (!port->aggregator->is_active)
15368c2ecf20Sopenharmony_ci		port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION;
15378c2ecf20Sopenharmony_ci}
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci/* Decide if "agg" is a better choice for the new active aggregator that
15408c2ecf20Sopenharmony_ci * the current best, according to the ad_select policy.
15418c2ecf20Sopenharmony_ci */
15428c2ecf20Sopenharmony_cistatic struct aggregator *ad_agg_selection_test(struct aggregator *best,
15438c2ecf20Sopenharmony_ci						struct aggregator *curr)
15448c2ecf20Sopenharmony_ci{
15458c2ecf20Sopenharmony_ci	/* 0. If no best, select current.
15468c2ecf20Sopenharmony_ci	 *
15478c2ecf20Sopenharmony_ci	 * 1. If the current agg is not individual, and the best is
15488c2ecf20Sopenharmony_ci	 *    individual, select current.
15498c2ecf20Sopenharmony_ci	 *
15508c2ecf20Sopenharmony_ci	 * 2. If current agg is individual and the best is not, keep best.
15518c2ecf20Sopenharmony_ci	 *
15528c2ecf20Sopenharmony_ci	 * 3. Therefore, current and best are both individual or both not
15538c2ecf20Sopenharmony_ci	 *    individual, so:
15548c2ecf20Sopenharmony_ci	 *
15558c2ecf20Sopenharmony_ci	 * 3a. If current agg partner replied, and best agg partner did not,
15568c2ecf20Sopenharmony_ci	 *     select current.
15578c2ecf20Sopenharmony_ci	 *
15588c2ecf20Sopenharmony_ci	 * 3b. If current agg partner did not reply and best agg partner
15598c2ecf20Sopenharmony_ci	 *     did reply, keep best.
15608c2ecf20Sopenharmony_ci	 *
15618c2ecf20Sopenharmony_ci	 * 4.  Therefore, current and best both have partner replies or
15628c2ecf20Sopenharmony_ci	 *     both do not, so perform selection policy:
15638c2ecf20Sopenharmony_ci	 *
15648c2ecf20Sopenharmony_ci	 * BOND_AD_COUNT: Select by count of ports.  If count is equal,
15658c2ecf20Sopenharmony_ci	 *     select by bandwidth.
15668c2ecf20Sopenharmony_ci	 *
15678c2ecf20Sopenharmony_ci	 * BOND_AD_STABLE, BOND_AD_BANDWIDTH: Select by bandwidth.
15688c2ecf20Sopenharmony_ci	 */
15698c2ecf20Sopenharmony_ci	if (!best)
15708c2ecf20Sopenharmony_ci		return curr;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	if (!curr->is_individual && best->is_individual)
15738c2ecf20Sopenharmony_ci		return curr;
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	if (curr->is_individual && !best->is_individual)
15768c2ecf20Sopenharmony_ci		return best;
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	if (__agg_has_partner(curr) && !__agg_has_partner(best))
15798c2ecf20Sopenharmony_ci		return curr;
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	if (!__agg_has_partner(curr) && __agg_has_partner(best))
15828c2ecf20Sopenharmony_ci		return best;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	switch (__get_agg_selection_mode(curr->lag_ports)) {
15858c2ecf20Sopenharmony_ci	case BOND_AD_COUNT:
15868c2ecf20Sopenharmony_ci		if (__agg_active_ports(curr) > __agg_active_ports(best))
15878c2ecf20Sopenharmony_ci			return curr;
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci		if (__agg_active_ports(curr) < __agg_active_ports(best))
15908c2ecf20Sopenharmony_ci			return best;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci		fallthrough;
15938c2ecf20Sopenharmony_ci	case BOND_AD_STABLE:
15948c2ecf20Sopenharmony_ci	case BOND_AD_BANDWIDTH:
15958c2ecf20Sopenharmony_ci		if (__get_agg_bandwidth(curr) > __get_agg_bandwidth(best))
15968c2ecf20Sopenharmony_ci			return curr;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci		break;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	default:
16018c2ecf20Sopenharmony_ci		net_warn_ratelimited("%s: (slave %s): Impossible agg select mode %d\n",
16028c2ecf20Sopenharmony_ci				     curr->slave->bond->dev->name,
16038c2ecf20Sopenharmony_ci				     curr->slave->dev->name,
16048c2ecf20Sopenharmony_ci				     __get_agg_selection_mode(curr->lag_ports));
16058c2ecf20Sopenharmony_ci		break;
16068c2ecf20Sopenharmony_ci	}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	return best;
16098c2ecf20Sopenharmony_ci}
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_cistatic int agg_device_up(const struct aggregator *agg)
16128c2ecf20Sopenharmony_ci{
16138c2ecf20Sopenharmony_ci	struct port *port = agg->lag_ports;
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	if (!port)
16168c2ecf20Sopenharmony_ci		return 0;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	for (port = agg->lag_ports; port;
16198c2ecf20Sopenharmony_ci	     port = port->next_port_in_aggregator) {
16208c2ecf20Sopenharmony_ci		if (netif_running(port->slave->dev) &&
16218c2ecf20Sopenharmony_ci		    netif_carrier_ok(port->slave->dev))
16228c2ecf20Sopenharmony_ci			return 1;
16238c2ecf20Sopenharmony_ci	}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	return 0;
16268c2ecf20Sopenharmony_ci}
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci/**
16298c2ecf20Sopenharmony_ci * ad_agg_selection_logic - select an aggregation group for a team
16308c2ecf20Sopenharmony_ci * @agg: the aggregator we're looking at
16318c2ecf20Sopenharmony_ci * @update_slave_arr: Does slave array need update?
16328c2ecf20Sopenharmony_ci *
16338c2ecf20Sopenharmony_ci * It is assumed that only one aggregator may be selected for a team.
16348c2ecf20Sopenharmony_ci *
16358c2ecf20Sopenharmony_ci * The logic of this function is to select the aggregator according to
16368c2ecf20Sopenharmony_ci * the ad_select policy:
16378c2ecf20Sopenharmony_ci *
16388c2ecf20Sopenharmony_ci * BOND_AD_STABLE: select the aggregator with the most ports attached to
16398c2ecf20Sopenharmony_ci * it, and to reselect the active aggregator only if the previous
16408c2ecf20Sopenharmony_ci * aggregator has no more ports related to it.
16418c2ecf20Sopenharmony_ci *
16428c2ecf20Sopenharmony_ci * BOND_AD_BANDWIDTH: select the aggregator with the highest total
16438c2ecf20Sopenharmony_ci * bandwidth, and reselect whenever a link state change takes place or the
16448c2ecf20Sopenharmony_ci * set of slaves in the bond changes.
16458c2ecf20Sopenharmony_ci *
16468c2ecf20Sopenharmony_ci * BOND_AD_COUNT: select the aggregator with largest number of ports
16478c2ecf20Sopenharmony_ci * (slaves), and reselect whenever a link state change takes place or the
16488c2ecf20Sopenharmony_ci * set of slaves in the bond changes.
16498c2ecf20Sopenharmony_ci *
16508c2ecf20Sopenharmony_ci * FIXME: this function MUST be called with the first agg in the bond, or
16518c2ecf20Sopenharmony_ci * __get_active_agg() won't work correctly. This function should be better
16528c2ecf20Sopenharmony_ci * called with the bond itself, and retrieve the first agg from it.
16538c2ecf20Sopenharmony_ci */
16548c2ecf20Sopenharmony_cistatic void ad_agg_selection_logic(struct aggregator *agg,
16558c2ecf20Sopenharmony_ci				   bool *update_slave_arr)
16568c2ecf20Sopenharmony_ci{
16578c2ecf20Sopenharmony_ci	struct aggregator *best, *active, *origin;
16588c2ecf20Sopenharmony_ci	struct bonding *bond = agg->slave->bond;
16598c2ecf20Sopenharmony_ci	struct list_head *iter;
16608c2ecf20Sopenharmony_ci	struct slave *slave;
16618c2ecf20Sopenharmony_ci	struct port *port;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	rcu_read_lock();
16648c2ecf20Sopenharmony_ci	origin = agg;
16658c2ecf20Sopenharmony_ci	active = __get_active_agg(agg);
16668c2ecf20Sopenharmony_ci	best = (active && agg_device_up(active)) ? active : NULL;
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	bond_for_each_slave_rcu(bond, slave, iter) {
16698c2ecf20Sopenharmony_ci		agg = &(SLAVE_AD_INFO(slave)->aggregator);
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci		agg->is_active = 0;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci		if (__agg_active_ports(agg) && agg_device_up(agg))
16748c2ecf20Sopenharmony_ci			best = ad_agg_selection_test(best, agg);
16758c2ecf20Sopenharmony_ci	}
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	if (best &&
16788c2ecf20Sopenharmony_ci	    __get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) {
16798c2ecf20Sopenharmony_ci		/* For the STABLE policy, don't replace the old active
16808c2ecf20Sopenharmony_ci		 * aggregator if it's still active (it has an answering
16818c2ecf20Sopenharmony_ci		 * partner) or if both the best and active don't have an
16828c2ecf20Sopenharmony_ci		 * answering partner.
16838c2ecf20Sopenharmony_ci		 */
16848c2ecf20Sopenharmony_ci		if (active && active->lag_ports &&
16858c2ecf20Sopenharmony_ci		    __agg_active_ports(active) &&
16868c2ecf20Sopenharmony_ci		    (__agg_has_partner(active) ||
16878c2ecf20Sopenharmony_ci		     (!__agg_has_partner(active) &&
16888c2ecf20Sopenharmony_ci		     !__agg_has_partner(best)))) {
16898c2ecf20Sopenharmony_ci			if (!(!active->actor_oper_aggregator_key &&
16908c2ecf20Sopenharmony_ci			      best->actor_oper_aggregator_key)) {
16918c2ecf20Sopenharmony_ci				best = NULL;
16928c2ecf20Sopenharmony_ci				active->is_active = 1;
16938c2ecf20Sopenharmony_ci			}
16948c2ecf20Sopenharmony_ci		}
16958c2ecf20Sopenharmony_ci	}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	if (best && (best == active)) {
16988c2ecf20Sopenharmony_ci		best = NULL;
16998c2ecf20Sopenharmony_ci		active->is_active = 1;
17008c2ecf20Sopenharmony_ci	}
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	/* if there is new best aggregator, activate it */
17038c2ecf20Sopenharmony_ci	if (best) {
17048c2ecf20Sopenharmony_ci		netdev_dbg(bond->dev, "(slave %s): best Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
17058c2ecf20Sopenharmony_ci			   best->slave ? best->slave->dev->name : "NULL",
17068c2ecf20Sopenharmony_ci			   best->aggregator_identifier, best->num_of_ports,
17078c2ecf20Sopenharmony_ci			   best->actor_oper_aggregator_key,
17088c2ecf20Sopenharmony_ci			   best->partner_oper_aggregator_key,
17098c2ecf20Sopenharmony_ci			   best->is_individual, best->is_active);
17108c2ecf20Sopenharmony_ci		netdev_dbg(bond->dev, "(slave %s): best ports %p slave %p\n",
17118c2ecf20Sopenharmony_ci			   best->slave ? best->slave->dev->name : "NULL",
17128c2ecf20Sopenharmony_ci			   best->lag_ports, best->slave);
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci		bond_for_each_slave_rcu(bond, slave, iter) {
17158c2ecf20Sopenharmony_ci			agg = &(SLAVE_AD_INFO(slave)->aggregator);
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci			slave_dbg(bond->dev, slave->dev, "Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
17188c2ecf20Sopenharmony_ci				  agg->aggregator_identifier, agg->num_of_ports,
17198c2ecf20Sopenharmony_ci				  agg->actor_oper_aggregator_key,
17208c2ecf20Sopenharmony_ci				  agg->partner_oper_aggregator_key,
17218c2ecf20Sopenharmony_ci				  agg->is_individual, agg->is_active);
17228c2ecf20Sopenharmony_ci		}
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci		/* check if any partner replies */
17258c2ecf20Sopenharmony_ci		if (best->is_individual)
17268c2ecf20Sopenharmony_ci			net_warn_ratelimited("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n",
17278c2ecf20Sopenharmony_ci					     bond->dev->name);
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci		best->is_active = 1;
17308c2ecf20Sopenharmony_ci		netdev_dbg(bond->dev, "(slave %s): LAG %d chosen as the active LAG\n",
17318c2ecf20Sopenharmony_ci			   best->slave ? best->slave->dev->name : "NULL",
17328c2ecf20Sopenharmony_ci			   best->aggregator_identifier);
17338c2ecf20Sopenharmony_ci		netdev_dbg(bond->dev, "(slave %s): Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
17348c2ecf20Sopenharmony_ci			   best->slave ? best->slave->dev->name : "NULL",
17358c2ecf20Sopenharmony_ci			   best->aggregator_identifier, best->num_of_ports,
17368c2ecf20Sopenharmony_ci			   best->actor_oper_aggregator_key,
17378c2ecf20Sopenharmony_ci			   best->partner_oper_aggregator_key,
17388c2ecf20Sopenharmony_ci			   best->is_individual, best->is_active);
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci		/* disable the ports that were related to the former
17418c2ecf20Sopenharmony_ci		 * active_aggregator
17428c2ecf20Sopenharmony_ci		 */
17438c2ecf20Sopenharmony_ci		if (active) {
17448c2ecf20Sopenharmony_ci			for (port = active->lag_ports; port;
17458c2ecf20Sopenharmony_ci			     port = port->next_port_in_aggregator) {
17468c2ecf20Sopenharmony_ci				__disable_port(port);
17478c2ecf20Sopenharmony_ci			}
17488c2ecf20Sopenharmony_ci		}
17498c2ecf20Sopenharmony_ci		/* Slave array needs update. */
17508c2ecf20Sopenharmony_ci		*update_slave_arr = true;
17518c2ecf20Sopenharmony_ci	}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	/* if the selected aggregator is of join individuals
17548c2ecf20Sopenharmony_ci	 * (partner_system is NULL), enable their ports
17558c2ecf20Sopenharmony_ci	 */
17568c2ecf20Sopenharmony_ci	active = __get_active_agg(origin);
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	if (active) {
17598c2ecf20Sopenharmony_ci		if (!__agg_has_partner(active)) {
17608c2ecf20Sopenharmony_ci			for (port = active->lag_ports; port;
17618c2ecf20Sopenharmony_ci			     port = port->next_port_in_aggregator) {
17628c2ecf20Sopenharmony_ci				__enable_port(port);
17638c2ecf20Sopenharmony_ci			}
17648c2ecf20Sopenharmony_ci			*update_slave_arr = true;
17658c2ecf20Sopenharmony_ci		}
17668c2ecf20Sopenharmony_ci	}
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_ci	rcu_read_unlock();
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	bond_3ad_set_carrier(bond);
17718c2ecf20Sopenharmony_ci}
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci/**
17748c2ecf20Sopenharmony_ci * ad_clear_agg - clear a given aggregator's parameters
17758c2ecf20Sopenharmony_ci * @aggregator: the aggregator we're looking at
17768c2ecf20Sopenharmony_ci */
17778c2ecf20Sopenharmony_cistatic void ad_clear_agg(struct aggregator *aggregator)
17788c2ecf20Sopenharmony_ci{
17798c2ecf20Sopenharmony_ci	if (aggregator) {
17808c2ecf20Sopenharmony_ci		aggregator->is_individual = false;
17818c2ecf20Sopenharmony_ci		aggregator->actor_admin_aggregator_key = 0;
17828c2ecf20Sopenharmony_ci		aggregator->actor_oper_aggregator_key = 0;
17838c2ecf20Sopenharmony_ci		eth_zero_addr(aggregator->partner_system.mac_addr_value);
17848c2ecf20Sopenharmony_ci		aggregator->partner_system_priority = 0;
17858c2ecf20Sopenharmony_ci		aggregator->partner_oper_aggregator_key = 0;
17868c2ecf20Sopenharmony_ci		aggregator->receive_state = 0;
17878c2ecf20Sopenharmony_ci		aggregator->transmit_state = 0;
17888c2ecf20Sopenharmony_ci		aggregator->lag_ports = NULL;
17898c2ecf20Sopenharmony_ci		aggregator->is_active = 0;
17908c2ecf20Sopenharmony_ci		aggregator->num_of_ports = 0;
17918c2ecf20Sopenharmony_ci		pr_debug("%s: LAG %d was cleared\n",
17928c2ecf20Sopenharmony_ci			 aggregator->slave ?
17938c2ecf20Sopenharmony_ci			 aggregator->slave->dev->name : "NULL",
17948c2ecf20Sopenharmony_ci			 aggregator->aggregator_identifier);
17958c2ecf20Sopenharmony_ci	}
17968c2ecf20Sopenharmony_ci}
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci/**
17998c2ecf20Sopenharmony_ci * ad_initialize_agg - initialize a given aggregator's parameters
18008c2ecf20Sopenharmony_ci * @aggregator: the aggregator we're looking at
18018c2ecf20Sopenharmony_ci */
18028c2ecf20Sopenharmony_cistatic void ad_initialize_agg(struct aggregator *aggregator)
18038c2ecf20Sopenharmony_ci{
18048c2ecf20Sopenharmony_ci	if (aggregator) {
18058c2ecf20Sopenharmony_ci		ad_clear_agg(aggregator);
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci		eth_zero_addr(aggregator->aggregator_mac_address.mac_addr_value);
18088c2ecf20Sopenharmony_ci		aggregator->aggregator_identifier = 0;
18098c2ecf20Sopenharmony_ci		aggregator->slave = NULL;
18108c2ecf20Sopenharmony_ci	}
18118c2ecf20Sopenharmony_ci}
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci/**
18148c2ecf20Sopenharmony_ci * ad_initialize_port - initialize a given port's parameters
18158c2ecf20Sopenharmony_ci * @port: the port we're looking at
18168c2ecf20Sopenharmony_ci * @lacp_fast: boolean. whether fast periodic should be used
18178c2ecf20Sopenharmony_ci */
18188c2ecf20Sopenharmony_cistatic void ad_initialize_port(struct port *port, int lacp_fast)
18198c2ecf20Sopenharmony_ci{
18208c2ecf20Sopenharmony_ci	static const struct port_params tmpl = {
18218c2ecf20Sopenharmony_ci		.system_priority = 0xffff,
18228c2ecf20Sopenharmony_ci		.key             = 1,
18238c2ecf20Sopenharmony_ci		.port_number     = 1,
18248c2ecf20Sopenharmony_ci		.port_priority   = 0xff,
18258c2ecf20Sopenharmony_ci		.port_state      = 1,
18268c2ecf20Sopenharmony_ci	};
18278c2ecf20Sopenharmony_ci	static const struct lacpdu lacpdu = {
18288c2ecf20Sopenharmony_ci		.subtype		= 0x01,
18298c2ecf20Sopenharmony_ci		.version_number = 0x01,
18308c2ecf20Sopenharmony_ci		.tlv_type_actor_info = 0x01,
18318c2ecf20Sopenharmony_ci		.actor_information_length = 0x14,
18328c2ecf20Sopenharmony_ci		.tlv_type_partner_info = 0x02,
18338c2ecf20Sopenharmony_ci		.partner_information_length = 0x14,
18348c2ecf20Sopenharmony_ci		.tlv_type_collector_info = 0x03,
18358c2ecf20Sopenharmony_ci		.collector_information_length = 0x10,
18368c2ecf20Sopenharmony_ci		.collector_max_delay = htons(AD_COLLECTOR_MAX_DELAY),
18378c2ecf20Sopenharmony_ci	};
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	if (port) {
18408c2ecf20Sopenharmony_ci		port->actor_port_priority = 0xff;
18418c2ecf20Sopenharmony_ci		port->actor_port_aggregator_identifier = 0;
18428c2ecf20Sopenharmony_ci		port->ntt = false;
18438c2ecf20Sopenharmony_ci		port->actor_admin_port_state = LACP_STATE_AGGREGATION |
18448c2ecf20Sopenharmony_ci					       LACP_STATE_LACP_ACTIVITY;
18458c2ecf20Sopenharmony_ci		port->actor_oper_port_state  = LACP_STATE_AGGREGATION |
18468c2ecf20Sopenharmony_ci					       LACP_STATE_LACP_ACTIVITY;
18478c2ecf20Sopenharmony_ci
18488c2ecf20Sopenharmony_ci		if (lacp_fast)
18498c2ecf20Sopenharmony_ci			port->actor_oper_port_state |= LACP_STATE_LACP_TIMEOUT;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci		memcpy(&port->partner_admin, &tmpl, sizeof(tmpl));
18528c2ecf20Sopenharmony_ci		memcpy(&port->partner_oper, &tmpl, sizeof(tmpl));
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci		port->is_enabled = true;
18558c2ecf20Sopenharmony_ci		/* private parameters */
18568c2ecf20Sopenharmony_ci		port->sm_vars = AD_PORT_BEGIN | AD_PORT_LACP_ENABLED;
18578c2ecf20Sopenharmony_ci		port->sm_rx_state = 0;
18588c2ecf20Sopenharmony_ci		port->sm_rx_timer_counter = 0;
18598c2ecf20Sopenharmony_ci		port->sm_periodic_state = 0;
18608c2ecf20Sopenharmony_ci		port->sm_periodic_timer_counter = 0;
18618c2ecf20Sopenharmony_ci		port->sm_mux_state = 0;
18628c2ecf20Sopenharmony_ci		port->sm_mux_timer_counter = 0;
18638c2ecf20Sopenharmony_ci		port->sm_tx_state = 0;
18648c2ecf20Sopenharmony_ci		port->aggregator = NULL;
18658c2ecf20Sopenharmony_ci		port->next_port_in_aggregator = NULL;
18668c2ecf20Sopenharmony_ci		port->transaction_id = 0;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci		port->sm_churn_actor_timer_counter = 0;
18698c2ecf20Sopenharmony_ci		port->sm_churn_actor_state = 0;
18708c2ecf20Sopenharmony_ci		port->churn_actor_count = 0;
18718c2ecf20Sopenharmony_ci		port->sm_churn_partner_timer_counter = 0;
18728c2ecf20Sopenharmony_ci		port->sm_churn_partner_state = 0;
18738c2ecf20Sopenharmony_ci		port->churn_partner_count = 0;
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci		memcpy(&port->lacpdu, &lacpdu, sizeof(lacpdu));
18768c2ecf20Sopenharmony_ci	}
18778c2ecf20Sopenharmony_ci}
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci/**
18808c2ecf20Sopenharmony_ci * ad_enable_collecting_distributing - enable a port's transmit/receive
18818c2ecf20Sopenharmony_ci * @port: the port we're looking at
18828c2ecf20Sopenharmony_ci * @update_slave_arr: Does slave array need update?
18838c2ecf20Sopenharmony_ci *
18848c2ecf20Sopenharmony_ci * Enable @port if it's in an active aggregator
18858c2ecf20Sopenharmony_ci */
18868c2ecf20Sopenharmony_cistatic void ad_enable_collecting_distributing(struct port *port,
18878c2ecf20Sopenharmony_ci					      bool *update_slave_arr)
18888c2ecf20Sopenharmony_ci{
18898c2ecf20Sopenharmony_ci	if (port->aggregator->is_active) {
18908c2ecf20Sopenharmony_ci		slave_dbg(port->slave->bond->dev, port->slave->dev,
18918c2ecf20Sopenharmony_ci			  "Enabling port %d (LAG %d)\n",
18928c2ecf20Sopenharmony_ci			  port->actor_port_number,
18938c2ecf20Sopenharmony_ci			  port->aggregator->aggregator_identifier);
18948c2ecf20Sopenharmony_ci		__enable_port(port);
18958c2ecf20Sopenharmony_ci		/* Slave array needs update */
18968c2ecf20Sopenharmony_ci		*update_slave_arr = true;
18978c2ecf20Sopenharmony_ci	}
18988c2ecf20Sopenharmony_ci}
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci/**
19018c2ecf20Sopenharmony_ci * ad_disable_collecting_distributing - disable a port's transmit/receive
19028c2ecf20Sopenharmony_ci * @port: the port we're looking at
19038c2ecf20Sopenharmony_ci * @update_slave_arr: Does slave array need update?
19048c2ecf20Sopenharmony_ci */
19058c2ecf20Sopenharmony_cistatic void ad_disable_collecting_distributing(struct port *port,
19068c2ecf20Sopenharmony_ci					       bool *update_slave_arr)
19078c2ecf20Sopenharmony_ci{
19088c2ecf20Sopenharmony_ci	if (port->aggregator &&
19098c2ecf20Sopenharmony_ci	    !MAC_ADDRESS_EQUAL(&(port->aggregator->partner_system),
19108c2ecf20Sopenharmony_ci			       &(null_mac_addr))) {
19118c2ecf20Sopenharmony_ci		slave_dbg(port->slave->bond->dev, port->slave->dev,
19128c2ecf20Sopenharmony_ci			  "Disabling port %d (LAG %d)\n",
19138c2ecf20Sopenharmony_ci			  port->actor_port_number,
19148c2ecf20Sopenharmony_ci			  port->aggregator->aggregator_identifier);
19158c2ecf20Sopenharmony_ci		__disable_port(port);
19168c2ecf20Sopenharmony_ci		/* Slave array needs an update */
19178c2ecf20Sopenharmony_ci		*update_slave_arr = true;
19188c2ecf20Sopenharmony_ci	}
19198c2ecf20Sopenharmony_ci}
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci/**
19228c2ecf20Sopenharmony_ci * ad_marker_info_received - handle receive of a Marker information frame
19238c2ecf20Sopenharmony_ci * @marker_info: Marker info received
19248c2ecf20Sopenharmony_ci * @port: the port we're looking at
19258c2ecf20Sopenharmony_ci */
19268c2ecf20Sopenharmony_cistatic void ad_marker_info_received(struct bond_marker *marker_info,
19278c2ecf20Sopenharmony_ci				    struct port *port)
19288c2ecf20Sopenharmony_ci{
19298c2ecf20Sopenharmony_ci	struct bond_marker marker;
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	atomic64_inc(&SLAVE_AD_INFO(port->slave)->stats.marker_rx);
19328c2ecf20Sopenharmony_ci	atomic64_inc(&BOND_AD_INFO(port->slave->bond).stats.marker_rx);
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	/* copy the received marker data to the response marker */
19358c2ecf20Sopenharmony_ci	memcpy(&marker, marker_info, sizeof(struct bond_marker));
19368c2ecf20Sopenharmony_ci	/* change the marker subtype to marker response */
19378c2ecf20Sopenharmony_ci	marker.tlv_type = AD_MARKER_RESPONSE_SUBTYPE;
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci	/* send the marker response */
19408c2ecf20Sopenharmony_ci	if (ad_marker_send(port, &marker) >= 0)
19418c2ecf20Sopenharmony_ci		slave_dbg(port->slave->bond->dev, port->slave->dev,
19428c2ecf20Sopenharmony_ci			  "Sent Marker Response on port %d\n",
19438c2ecf20Sopenharmony_ci			  port->actor_port_number);
19448c2ecf20Sopenharmony_ci}
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci/**
19478c2ecf20Sopenharmony_ci * ad_marker_response_received - handle receive of a marker response frame
19488c2ecf20Sopenharmony_ci * @marker: marker PDU received
19498c2ecf20Sopenharmony_ci * @port: the port we're looking at
19508c2ecf20Sopenharmony_ci *
19518c2ecf20Sopenharmony_ci * This function does nothing since we decided not to implement send and handle
19528c2ecf20Sopenharmony_ci * response for marker PDU's, in this stage, but only to respond to marker
19538c2ecf20Sopenharmony_ci * information.
19548c2ecf20Sopenharmony_ci */
19558c2ecf20Sopenharmony_cistatic void ad_marker_response_received(struct bond_marker *marker,
19568c2ecf20Sopenharmony_ci					struct port *port)
19578c2ecf20Sopenharmony_ci{
19588c2ecf20Sopenharmony_ci	atomic64_inc(&SLAVE_AD_INFO(port->slave)->stats.marker_resp_rx);
19598c2ecf20Sopenharmony_ci	atomic64_inc(&BOND_AD_INFO(port->slave->bond).stats.marker_resp_rx);
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci	/* DO NOTHING, SINCE WE DECIDED NOT TO IMPLEMENT THIS FEATURE FOR NOW */
19628c2ecf20Sopenharmony_ci}
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci/* ========= AD exported functions to the main bonding code ========= */
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci/* Check aggregators status in team every T seconds */
19678c2ecf20Sopenharmony_ci#define AD_AGGREGATOR_SELECTION_TIMER  8
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci/**
19708c2ecf20Sopenharmony_ci * bond_3ad_initiate_agg_selection - initate aggregator selection
19718c2ecf20Sopenharmony_ci * @bond: bonding struct
19728c2ecf20Sopenharmony_ci * @timeout: timeout value to set
19738c2ecf20Sopenharmony_ci *
19748c2ecf20Sopenharmony_ci * Set the aggregation selection timer, to initiate an agg selection in
19758c2ecf20Sopenharmony_ci * the very near future.  Called during first initialization, and during
19768c2ecf20Sopenharmony_ci * any down to up transitions of the bond.
19778c2ecf20Sopenharmony_ci */
19788c2ecf20Sopenharmony_civoid bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout)
19798c2ecf20Sopenharmony_ci{
19808c2ecf20Sopenharmony_ci	atomic_set(&BOND_AD_INFO(bond).agg_select_timer, timeout);
19818c2ecf20Sopenharmony_ci}
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci/**
19848c2ecf20Sopenharmony_ci * bond_3ad_initialize - initialize a bond's 802.3ad parameters and structures
19858c2ecf20Sopenharmony_ci * @bond: bonding struct to work on
19868c2ecf20Sopenharmony_ci * @tick_resolution: tick duration (millisecond resolution)
19878c2ecf20Sopenharmony_ci *
19888c2ecf20Sopenharmony_ci * Can be called only after the mac address of the bond is set.
19898c2ecf20Sopenharmony_ci */
19908c2ecf20Sopenharmony_civoid bond_3ad_initialize(struct bonding *bond, u16 tick_resolution)
19918c2ecf20Sopenharmony_ci{
19928c2ecf20Sopenharmony_ci	BOND_AD_INFO(bond).aggregator_identifier = 0;
19938c2ecf20Sopenharmony_ci	BOND_AD_INFO(bond).system.sys_priority =
19948c2ecf20Sopenharmony_ci		bond->params.ad_actor_sys_prio;
19958c2ecf20Sopenharmony_ci	if (is_zero_ether_addr(bond->params.ad_actor_system))
19968c2ecf20Sopenharmony_ci		BOND_AD_INFO(bond).system.sys_mac_addr =
19978c2ecf20Sopenharmony_ci		    *((struct mac_addr *)bond->dev->dev_addr);
19988c2ecf20Sopenharmony_ci	else
19998c2ecf20Sopenharmony_ci		BOND_AD_INFO(bond).system.sys_mac_addr =
20008c2ecf20Sopenharmony_ci		    *((struct mac_addr *)bond->params.ad_actor_system);
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci	/* initialize how many times this module is called in one
20038c2ecf20Sopenharmony_ci	 * second (should be about every 100ms)
20048c2ecf20Sopenharmony_ci	 */
20058c2ecf20Sopenharmony_ci	ad_ticks_per_sec = tick_resolution;
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	bond_3ad_initiate_agg_selection(bond,
20088c2ecf20Sopenharmony_ci					AD_AGGREGATOR_SELECTION_TIMER *
20098c2ecf20Sopenharmony_ci					ad_ticks_per_sec);
20108c2ecf20Sopenharmony_ci}
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci/**
20138c2ecf20Sopenharmony_ci * bond_3ad_bind_slave - initialize a slave's port
20148c2ecf20Sopenharmony_ci * @slave: slave struct to work on
20158c2ecf20Sopenharmony_ci *
20168c2ecf20Sopenharmony_ci * Returns:   0 on success
20178c2ecf20Sopenharmony_ci *          < 0 on error
20188c2ecf20Sopenharmony_ci */
20198c2ecf20Sopenharmony_civoid bond_3ad_bind_slave(struct slave *slave)
20208c2ecf20Sopenharmony_ci{
20218c2ecf20Sopenharmony_ci	struct bonding *bond = bond_get_bond_by_slave(slave);
20228c2ecf20Sopenharmony_ci	struct port *port;
20238c2ecf20Sopenharmony_ci	struct aggregator *aggregator;
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_ci	/* check that the slave has not been initialized yet. */
20268c2ecf20Sopenharmony_ci	if (SLAVE_AD_INFO(slave)->port.slave != slave) {
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_ci		/* port initialization */
20298c2ecf20Sopenharmony_ci		port = &(SLAVE_AD_INFO(slave)->port);
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci		ad_initialize_port(port, bond->params.lacp_fast);
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci		port->slave = slave;
20348c2ecf20Sopenharmony_ci		port->actor_port_number = SLAVE_AD_INFO(slave)->id;
20358c2ecf20Sopenharmony_ci		/* key is determined according to the link speed, duplex and
20368c2ecf20Sopenharmony_ci		 * user key
20378c2ecf20Sopenharmony_ci		 */
20388c2ecf20Sopenharmony_ci		port->actor_admin_port_key = bond->params.ad_user_port_key << 6;
20398c2ecf20Sopenharmony_ci		ad_update_actor_keys(port, false);
20408c2ecf20Sopenharmony_ci		/* actor system is the bond's system */
20418c2ecf20Sopenharmony_ci		__ad_actor_update_port(port);
20428c2ecf20Sopenharmony_ci		/* tx timer(to verify that no more than MAX_TX_IN_SECOND
20438c2ecf20Sopenharmony_ci		 * lacpdu's are sent in one second)
20448c2ecf20Sopenharmony_ci		 */
20458c2ecf20Sopenharmony_ci		port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND;
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci		__disable_port(port);
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci		/* aggregator initialization */
20508c2ecf20Sopenharmony_ci		aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci		ad_initialize_agg(aggregator);
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci		aggregator->aggregator_mac_address = *((struct mac_addr *)bond->dev->dev_addr);
20558c2ecf20Sopenharmony_ci		aggregator->aggregator_identifier = ++BOND_AD_INFO(bond).aggregator_identifier;
20568c2ecf20Sopenharmony_ci		aggregator->slave = slave;
20578c2ecf20Sopenharmony_ci		aggregator->is_active = 0;
20588c2ecf20Sopenharmony_ci		aggregator->num_of_ports = 0;
20598c2ecf20Sopenharmony_ci	}
20608c2ecf20Sopenharmony_ci}
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci/**
20638c2ecf20Sopenharmony_ci * bond_3ad_unbind_slave - deinitialize a slave's port
20648c2ecf20Sopenharmony_ci * @slave: slave struct to work on
20658c2ecf20Sopenharmony_ci *
20668c2ecf20Sopenharmony_ci * Search for the aggregator that is related to this port, remove the
20678c2ecf20Sopenharmony_ci * aggregator and assign another aggregator for other port related to it
20688c2ecf20Sopenharmony_ci * (if any), and remove the port.
20698c2ecf20Sopenharmony_ci */
20708c2ecf20Sopenharmony_civoid bond_3ad_unbind_slave(struct slave *slave)
20718c2ecf20Sopenharmony_ci{
20728c2ecf20Sopenharmony_ci	struct port *port, *prev_port, *temp_port;
20738c2ecf20Sopenharmony_ci	struct aggregator *aggregator, *new_aggregator, *temp_aggregator;
20748c2ecf20Sopenharmony_ci	int select_new_active_agg = 0;
20758c2ecf20Sopenharmony_ci	struct bonding *bond = slave->bond;
20768c2ecf20Sopenharmony_ci	struct slave *slave_iter;
20778c2ecf20Sopenharmony_ci	struct list_head *iter;
20788c2ecf20Sopenharmony_ci	bool dummy_slave_update; /* Ignore this value as caller updates array */
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	/* Sync against bond_3ad_state_machine_handler() */
20818c2ecf20Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
20828c2ecf20Sopenharmony_ci	aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
20838c2ecf20Sopenharmony_ci	port = &(SLAVE_AD_INFO(slave)->port);
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_ci	/* if slave is null, the whole port is not initialized */
20868c2ecf20Sopenharmony_ci	if (!port->slave) {
20878c2ecf20Sopenharmony_ci		slave_warn(bond->dev, slave->dev, "Trying to unbind an uninitialized port\n");
20888c2ecf20Sopenharmony_ci		goto out;
20898c2ecf20Sopenharmony_ci	}
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	slave_dbg(bond->dev, slave->dev, "Unbinding Link Aggregation Group %d\n",
20928c2ecf20Sopenharmony_ci		  aggregator->aggregator_identifier);
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	/* Tell the partner that this port is not suitable for aggregation */
20958c2ecf20Sopenharmony_ci	port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION;
20968c2ecf20Sopenharmony_ci	port->actor_oper_port_state &= ~LACP_STATE_COLLECTING;
20978c2ecf20Sopenharmony_ci	port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING;
20988c2ecf20Sopenharmony_ci	port->actor_oper_port_state &= ~LACP_STATE_AGGREGATION;
20998c2ecf20Sopenharmony_ci	__update_lacpdu_from_port(port);
21008c2ecf20Sopenharmony_ci	ad_lacpdu_send(port);
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	/* check if this aggregator is occupied */
21038c2ecf20Sopenharmony_ci	if (aggregator->lag_ports) {
21048c2ecf20Sopenharmony_ci		/* check if there are other ports related to this aggregator
21058c2ecf20Sopenharmony_ci		 * except the port related to this slave(thats ensure us that
21068c2ecf20Sopenharmony_ci		 * there is a reason to search for new aggregator, and that we
21078c2ecf20Sopenharmony_ci		 * will find one
21088c2ecf20Sopenharmony_ci		 */
21098c2ecf20Sopenharmony_ci		if ((aggregator->lag_ports != port) ||
21108c2ecf20Sopenharmony_ci		    (aggregator->lag_ports->next_port_in_aggregator)) {
21118c2ecf20Sopenharmony_ci			/* find new aggregator for the related port(s) */
21128c2ecf20Sopenharmony_ci			bond_for_each_slave(bond, slave_iter, iter) {
21138c2ecf20Sopenharmony_ci				new_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator);
21148c2ecf20Sopenharmony_ci				/* if the new aggregator is empty, or it is
21158c2ecf20Sopenharmony_ci				 * connected to our port only
21168c2ecf20Sopenharmony_ci				 */
21178c2ecf20Sopenharmony_ci				if (!new_aggregator->lag_ports ||
21188c2ecf20Sopenharmony_ci				    ((new_aggregator->lag_ports == port) &&
21198c2ecf20Sopenharmony_ci				     !new_aggregator->lag_ports->next_port_in_aggregator))
21208c2ecf20Sopenharmony_ci					break;
21218c2ecf20Sopenharmony_ci			}
21228c2ecf20Sopenharmony_ci			if (!slave_iter)
21238c2ecf20Sopenharmony_ci				new_aggregator = NULL;
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci			/* if new aggregator found, copy the aggregator's
21268c2ecf20Sopenharmony_ci			 * parameters and connect the related lag_ports to the
21278c2ecf20Sopenharmony_ci			 * new aggregator
21288c2ecf20Sopenharmony_ci			 */
21298c2ecf20Sopenharmony_ci			if ((new_aggregator) && ((!new_aggregator->lag_ports) || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator))) {
21308c2ecf20Sopenharmony_ci				slave_dbg(bond->dev, slave->dev, "Some port(s) related to LAG %d - replacing with LAG %d\n",
21318c2ecf20Sopenharmony_ci					  aggregator->aggregator_identifier,
21328c2ecf20Sopenharmony_ci					  new_aggregator->aggregator_identifier);
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci				if ((new_aggregator->lag_ports == port) &&
21358c2ecf20Sopenharmony_ci				    new_aggregator->is_active) {
21368c2ecf20Sopenharmony_ci					slave_info(bond->dev, slave->dev, "Removing an active aggregator\n");
21378c2ecf20Sopenharmony_ci					select_new_active_agg = 1;
21388c2ecf20Sopenharmony_ci				}
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci				new_aggregator->is_individual = aggregator->is_individual;
21418c2ecf20Sopenharmony_ci				new_aggregator->actor_admin_aggregator_key = aggregator->actor_admin_aggregator_key;
21428c2ecf20Sopenharmony_ci				new_aggregator->actor_oper_aggregator_key = aggregator->actor_oper_aggregator_key;
21438c2ecf20Sopenharmony_ci				new_aggregator->partner_system = aggregator->partner_system;
21448c2ecf20Sopenharmony_ci				new_aggregator->partner_system_priority = aggregator->partner_system_priority;
21458c2ecf20Sopenharmony_ci				new_aggregator->partner_oper_aggregator_key = aggregator->partner_oper_aggregator_key;
21468c2ecf20Sopenharmony_ci				new_aggregator->receive_state = aggregator->receive_state;
21478c2ecf20Sopenharmony_ci				new_aggregator->transmit_state = aggregator->transmit_state;
21488c2ecf20Sopenharmony_ci				new_aggregator->lag_ports = aggregator->lag_ports;
21498c2ecf20Sopenharmony_ci				new_aggregator->is_active = aggregator->is_active;
21508c2ecf20Sopenharmony_ci				new_aggregator->num_of_ports = aggregator->num_of_ports;
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci				/* update the information that is written on
21538c2ecf20Sopenharmony_ci				 * the ports about the aggregator
21548c2ecf20Sopenharmony_ci				 */
21558c2ecf20Sopenharmony_ci				for (temp_port = aggregator->lag_ports; temp_port;
21568c2ecf20Sopenharmony_ci				     temp_port = temp_port->next_port_in_aggregator) {
21578c2ecf20Sopenharmony_ci					temp_port->aggregator = new_aggregator;
21588c2ecf20Sopenharmony_ci					temp_port->actor_port_aggregator_identifier = new_aggregator->aggregator_identifier;
21598c2ecf20Sopenharmony_ci				}
21608c2ecf20Sopenharmony_ci
21618c2ecf20Sopenharmony_ci				ad_clear_agg(aggregator);
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci				if (select_new_active_agg)
21648c2ecf20Sopenharmony_ci					ad_agg_selection_logic(__get_first_agg(port),
21658c2ecf20Sopenharmony_ci							       &dummy_slave_update);
21668c2ecf20Sopenharmony_ci			} else {
21678c2ecf20Sopenharmony_ci				slave_warn(bond->dev, slave->dev, "unbinding aggregator, and could not find a new aggregator for its ports\n");
21688c2ecf20Sopenharmony_ci			}
21698c2ecf20Sopenharmony_ci		} else {
21708c2ecf20Sopenharmony_ci			/* in case that the only port related to this
21718c2ecf20Sopenharmony_ci			 * aggregator is the one we want to remove
21728c2ecf20Sopenharmony_ci			 */
21738c2ecf20Sopenharmony_ci			select_new_active_agg = aggregator->is_active;
21748c2ecf20Sopenharmony_ci			ad_clear_agg(aggregator);
21758c2ecf20Sopenharmony_ci			if (select_new_active_agg) {
21768c2ecf20Sopenharmony_ci				slave_info(bond->dev, slave->dev, "Removing an active aggregator\n");
21778c2ecf20Sopenharmony_ci				/* select new active aggregator */
21788c2ecf20Sopenharmony_ci				temp_aggregator = __get_first_agg(port);
21798c2ecf20Sopenharmony_ci				if (temp_aggregator)
21808c2ecf20Sopenharmony_ci					ad_agg_selection_logic(temp_aggregator,
21818c2ecf20Sopenharmony_ci							       &dummy_slave_update);
21828c2ecf20Sopenharmony_ci			}
21838c2ecf20Sopenharmony_ci		}
21848c2ecf20Sopenharmony_ci	}
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	slave_dbg(bond->dev, slave->dev, "Unbinding port %d\n", port->actor_port_number);
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_ci	/* find the aggregator that this port is connected to */
21898c2ecf20Sopenharmony_ci	bond_for_each_slave(bond, slave_iter, iter) {
21908c2ecf20Sopenharmony_ci		temp_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator);
21918c2ecf20Sopenharmony_ci		prev_port = NULL;
21928c2ecf20Sopenharmony_ci		/* search the port in the aggregator's related ports */
21938c2ecf20Sopenharmony_ci		for (temp_port = temp_aggregator->lag_ports; temp_port;
21948c2ecf20Sopenharmony_ci		     prev_port = temp_port,
21958c2ecf20Sopenharmony_ci		     temp_port = temp_port->next_port_in_aggregator) {
21968c2ecf20Sopenharmony_ci			if (temp_port == port) {
21978c2ecf20Sopenharmony_ci				/* the aggregator found - detach the port from
21988c2ecf20Sopenharmony_ci				 * this aggregator
21998c2ecf20Sopenharmony_ci				 */
22008c2ecf20Sopenharmony_ci				if (prev_port)
22018c2ecf20Sopenharmony_ci					prev_port->next_port_in_aggregator = temp_port->next_port_in_aggregator;
22028c2ecf20Sopenharmony_ci				else
22038c2ecf20Sopenharmony_ci					temp_aggregator->lag_ports = temp_port->next_port_in_aggregator;
22048c2ecf20Sopenharmony_ci				temp_aggregator->num_of_ports--;
22058c2ecf20Sopenharmony_ci				if (__agg_active_ports(temp_aggregator) == 0) {
22068c2ecf20Sopenharmony_ci					select_new_active_agg = temp_aggregator->is_active;
22078c2ecf20Sopenharmony_ci					if (temp_aggregator->num_of_ports == 0)
22088c2ecf20Sopenharmony_ci						ad_clear_agg(temp_aggregator);
22098c2ecf20Sopenharmony_ci					if (select_new_active_agg) {
22108c2ecf20Sopenharmony_ci						slave_info(bond->dev, slave->dev, "Removing an active aggregator\n");
22118c2ecf20Sopenharmony_ci						/* select new active aggregator */
22128c2ecf20Sopenharmony_ci						ad_agg_selection_logic(__get_first_agg(port),
22138c2ecf20Sopenharmony_ci							               &dummy_slave_update);
22148c2ecf20Sopenharmony_ci					}
22158c2ecf20Sopenharmony_ci				}
22168c2ecf20Sopenharmony_ci				break;
22178c2ecf20Sopenharmony_ci			}
22188c2ecf20Sopenharmony_ci		}
22198c2ecf20Sopenharmony_ci	}
22208c2ecf20Sopenharmony_ci	port->slave = NULL;
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_ciout:
22238c2ecf20Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
22248c2ecf20Sopenharmony_ci}
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci/**
22278c2ecf20Sopenharmony_ci * bond_3ad_update_ad_actor_settings - reflect change of actor settings to ports
22288c2ecf20Sopenharmony_ci * @bond: bonding struct to work on
22298c2ecf20Sopenharmony_ci *
22308c2ecf20Sopenharmony_ci * If an ad_actor setting gets changed we need to update the individual port
22318c2ecf20Sopenharmony_ci * settings so the bond device will use the new values when it gets upped.
22328c2ecf20Sopenharmony_ci */
22338c2ecf20Sopenharmony_civoid bond_3ad_update_ad_actor_settings(struct bonding *bond)
22348c2ecf20Sopenharmony_ci{
22358c2ecf20Sopenharmony_ci	struct list_head *iter;
22368c2ecf20Sopenharmony_ci	struct slave *slave;
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci	ASSERT_RTNL();
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	BOND_AD_INFO(bond).system.sys_priority = bond->params.ad_actor_sys_prio;
22418c2ecf20Sopenharmony_ci	if (is_zero_ether_addr(bond->params.ad_actor_system))
22428c2ecf20Sopenharmony_ci		BOND_AD_INFO(bond).system.sys_mac_addr =
22438c2ecf20Sopenharmony_ci		    *((struct mac_addr *)bond->dev->dev_addr);
22448c2ecf20Sopenharmony_ci	else
22458c2ecf20Sopenharmony_ci		BOND_AD_INFO(bond).system.sys_mac_addr =
22468c2ecf20Sopenharmony_ci		    *((struct mac_addr *)bond->params.ad_actor_system);
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
22498c2ecf20Sopenharmony_ci	bond_for_each_slave(bond, slave, iter) {
22508c2ecf20Sopenharmony_ci		struct port *port = &(SLAVE_AD_INFO(slave))->port;
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci		__ad_actor_update_port(port);
22538c2ecf20Sopenharmony_ci		port->ntt = true;
22548c2ecf20Sopenharmony_ci	}
22558c2ecf20Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
22568c2ecf20Sopenharmony_ci}
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci/**
22598c2ecf20Sopenharmony_ci * bond_agg_timer_advance - advance agg_select_timer
22608c2ecf20Sopenharmony_ci * @bond:  bonding structure
22618c2ecf20Sopenharmony_ci *
22628c2ecf20Sopenharmony_ci * Return true when agg_select_timer reaches 0.
22638c2ecf20Sopenharmony_ci */
22648c2ecf20Sopenharmony_cistatic bool bond_agg_timer_advance(struct bonding *bond)
22658c2ecf20Sopenharmony_ci{
22668c2ecf20Sopenharmony_ci	int val, nval;
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	while (1) {
22698c2ecf20Sopenharmony_ci		val = atomic_read(&BOND_AD_INFO(bond).agg_select_timer);
22708c2ecf20Sopenharmony_ci		if (!val)
22718c2ecf20Sopenharmony_ci			return false;
22728c2ecf20Sopenharmony_ci		nval = val - 1;
22738c2ecf20Sopenharmony_ci		if (atomic_cmpxchg(&BOND_AD_INFO(bond).agg_select_timer,
22748c2ecf20Sopenharmony_ci				   val, nval) == val)
22758c2ecf20Sopenharmony_ci			break;
22768c2ecf20Sopenharmony_ci	}
22778c2ecf20Sopenharmony_ci	return nval == 0;
22788c2ecf20Sopenharmony_ci}
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci/**
22818c2ecf20Sopenharmony_ci * bond_3ad_state_machine_handler - handle state machines timeout
22828c2ecf20Sopenharmony_ci * @work: work context to fetch bonding struct to work on from
22838c2ecf20Sopenharmony_ci *
22848c2ecf20Sopenharmony_ci * The state machine handling concept in this module is to check every tick
22858c2ecf20Sopenharmony_ci * which state machine should operate any function. The execution order is
22868c2ecf20Sopenharmony_ci * round robin, so when we have an interaction between state machines, the
22878c2ecf20Sopenharmony_ci * reply of one to each other might be delayed until next tick.
22888c2ecf20Sopenharmony_ci *
22898c2ecf20Sopenharmony_ci * This function also complete the initialization when the agg_select_timer
22908c2ecf20Sopenharmony_ci * times out, and it selects an aggregator for the ports that are yet not
22918c2ecf20Sopenharmony_ci * related to any aggregator, and selects the active aggregator for a bond.
22928c2ecf20Sopenharmony_ci */
22938c2ecf20Sopenharmony_civoid bond_3ad_state_machine_handler(struct work_struct *work)
22948c2ecf20Sopenharmony_ci{
22958c2ecf20Sopenharmony_ci	struct bonding *bond = container_of(work, struct bonding,
22968c2ecf20Sopenharmony_ci					    ad_work.work);
22978c2ecf20Sopenharmony_ci	struct aggregator *aggregator;
22988c2ecf20Sopenharmony_ci	struct list_head *iter;
22998c2ecf20Sopenharmony_ci	struct slave *slave;
23008c2ecf20Sopenharmony_ci	struct port *port;
23018c2ecf20Sopenharmony_ci	bool should_notify_rtnl = BOND_SLAVE_NOTIFY_LATER;
23028c2ecf20Sopenharmony_ci	bool update_slave_arr = false;
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci	/* Lock to protect data accessed by all (e.g., port->sm_vars) and
23058c2ecf20Sopenharmony_ci	 * against running with bond_3ad_unbind_slave. ad_rx_machine may run
23068c2ecf20Sopenharmony_ci	 * concurrently due to incoming LACPDU as well.
23078c2ecf20Sopenharmony_ci	 */
23088c2ecf20Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
23098c2ecf20Sopenharmony_ci	rcu_read_lock();
23108c2ecf20Sopenharmony_ci
23118c2ecf20Sopenharmony_ci	/* check if there are any slaves */
23128c2ecf20Sopenharmony_ci	if (!bond_has_slaves(bond))
23138c2ecf20Sopenharmony_ci		goto re_arm;
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_ci	if (bond_agg_timer_advance(bond)) {
23168c2ecf20Sopenharmony_ci		slave = bond_first_slave_rcu(bond);
23178c2ecf20Sopenharmony_ci		port = slave ? &(SLAVE_AD_INFO(slave)->port) : NULL;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci		/* select the active aggregator for the bond */
23208c2ecf20Sopenharmony_ci		if (port) {
23218c2ecf20Sopenharmony_ci			if (!port->slave) {
23228c2ecf20Sopenharmony_ci				net_warn_ratelimited("%s: Warning: bond's first port is uninitialized\n",
23238c2ecf20Sopenharmony_ci						     bond->dev->name);
23248c2ecf20Sopenharmony_ci				goto re_arm;
23258c2ecf20Sopenharmony_ci			}
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci			aggregator = __get_first_agg(port);
23288c2ecf20Sopenharmony_ci			ad_agg_selection_logic(aggregator, &update_slave_arr);
23298c2ecf20Sopenharmony_ci		}
23308c2ecf20Sopenharmony_ci		bond_3ad_set_carrier(bond);
23318c2ecf20Sopenharmony_ci	}
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	/* for each port run the state machines */
23348c2ecf20Sopenharmony_ci	bond_for_each_slave_rcu(bond, slave, iter) {
23358c2ecf20Sopenharmony_ci		port = &(SLAVE_AD_INFO(slave)->port);
23368c2ecf20Sopenharmony_ci		if (!port->slave) {
23378c2ecf20Sopenharmony_ci			net_warn_ratelimited("%s: Warning: Found an uninitialized port\n",
23388c2ecf20Sopenharmony_ci					    bond->dev->name);
23398c2ecf20Sopenharmony_ci			goto re_arm;
23408c2ecf20Sopenharmony_ci		}
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_ci		ad_rx_machine(NULL, port);
23438c2ecf20Sopenharmony_ci		ad_periodic_machine(port);
23448c2ecf20Sopenharmony_ci		ad_port_selection_logic(port, &update_slave_arr);
23458c2ecf20Sopenharmony_ci		ad_mux_machine(port, &update_slave_arr);
23468c2ecf20Sopenharmony_ci		ad_tx_machine(port);
23478c2ecf20Sopenharmony_ci		ad_churn_machine(port);
23488c2ecf20Sopenharmony_ci
23498c2ecf20Sopenharmony_ci		/* turn off the BEGIN bit, since we already handled it */
23508c2ecf20Sopenharmony_ci		if (port->sm_vars & AD_PORT_BEGIN)
23518c2ecf20Sopenharmony_ci			port->sm_vars &= ~AD_PORT_BEGIN;
23528c2ecf20Sopenharmony_ci	}
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_cire_arm:
23558c2ecf20Sopenharmony_ci	bond_for_each_slave_rcu(bond, slave, iter) {
23568c2ecf20Sopenharmony_ci		if (slave->should_notify) {
23578c2ecf20Sopenharmony_ci			should_notify_rtnl = BOND_SLAVE_NOTIFY_NOW;
23588c2ecf20Sopenharmony_ci			break;
23598c2ecf20Sopenharmony_ci		}
23608c2ecf20Sopenharmony_ci	}
23618c2ecf20Sopenharmony_ci	rcu_read_unlock();
23628c2ecf20Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci	if (update_slave_arr)
23658c2ecf20Sopenharmony_ci		bond_slave_arr_work_rearm(bond, 0);
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	if (should_notify_rtnl && rtnl_trylock()) {
23688c2ecf20Sopenharmony_ci		bond_slave_state_notify(bond);
23698c2ecf20Sopenharmony_ci		rtnl_unlock();
23708c2ecf20Sopenharmony_ci	}
23718c2ecf20Sopenharmony_ci	queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks);
23728c2ecf20Sopenharmony_ci}
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci/**
23758c2ecf20Sopenharmony_ci * bond_3ad_rx_indication - handle a received frame
23768c2ecf20Sopenharmony_ci * @lacpdu: received lacpdu
23778c2ecf20Sopenharmony_ci * @slave: slave struct to work on
23788c2ecf20Sopenharmony_ci *
23798c2ecf20Sopenharmony_ci * It is assumed that frames that were sent on this NIC don't returned as new
23808c2ecf20Sopenharmony_ci * received frames (loopback). Since only the payload is given to this
23818c2ecf20Sopenharmony_ci * function, it check for loopback.
23828c2ecf20Sopenharmony_ci */
23838c2ecf20Sopenharmony_cistatic int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave)
23848c2ecf20Sopenharmony_ci{
23858c2ecf20Sopenharmony_ci	struct bonding *bond = slave->bond;
23868c2ecf20Sopenharmony_ci	int ret = RX_HANDLER_ANOTHER;
23878c2ecf20Sopenharmony_ci	struct bond_marker *marker;
23888c2ecf20Sopenharmony_ci	struct port *port;
23898c2ecf20Sopenharmony_ci	atomic64_t *stat;
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_ci	port = &(SLAVE_AD_INFO(slave)->port);
23928c2ecf20Sopenharmony_ci	if (!port->slave) {
23938c2ecf20Sopenharmony_ci		net_warn_ratelimited("%s: Warning: port of slave %s is uninitialized\n",
23948c2ecf20Sopenharmony_ci				     slave->dev->name, slave->bond->dev->name);
23958c2ecf20Sopenharmony_ci		return ret;
23968c2ecf20Sopenharmony_ci	}
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_ci	switch (lacpdu->subtype) {
23998c2ecf20Sopenharmony_ci	case AD_TYPE_LACPDU:
24008c2ecf20Sopenharmony_ci		ret = RX_HANDLER_CONSUMED;
24018c2ecf20Sopenharmony_ci		slave_dbg(slave->bond->dev, slave->dev,
24028c2ecf20Sopenharmony_ci			  "Received LACPDU on port %d\n",
24038c2ecf20Sopenharmony_ci			  port->actor_port_number);
24048c2ecf20Sopenharmony_ci		/* Protect against concurrent state machines */
24058c2ecf20Sopenharmony_ci		spin_lock(&slave->bond->mode_lock);
24068c2ecf20Sopenharmony_ci		ad_rx_machine(lacpdu, port);
24078c2ecf20Sopenharmony_ci		spin_unlock(&slave->bond->mode_lock);
24088c2ecf20Sopenharmony_ci		break;
24098c2ecf20Sopenharmony_ci	case AD_TYPE_MARKER:
24108c2ecf20Sopenharmony_ci		ret = RX_HANDLER_CONSUMED;
24118c2ecf20Sopenharmony_ci		/* No need to convert fields to Little Endian since we
24128c2ecf20Sopenharmony_ci		 * don't use the marker's fields.
24138c2ecf20Sopenharmony_ci		 */
24148c2ecf20Sopenharmony_ci		marker = (struct bond_marker *)lacpdu;
24158c2ecf20Sopenharmony_ci		switch (marker->tlv_type) {
24168c2ecf20Sopenharmony_ci		case AD_MARKER_INFORMATION_SUBTYPE:
24178c2ecf20Sopenharmony_ci			slave_dbg(slave->bond->dev, slave->dev, "Received Marker Information on port %d\n",
24188c2ecf20Sopenharmony_ci				  port->actor_port_number);
24198c2ecf20Sopenharmony_ci			ad_marker_info_received(marker, port);
24208c2ecf20Sopenharmony_ci			break;
24218c2ecf20Sopenharmony_ci		case AD_MARKER_RESPONSE_SUBTYPE:
24228c2ecf20Sopenharmony_ci			slave_dbg(slave->bond->dev, slave->dev, "Received Marker Response on port %d\n",
24238c2ecf20Sopenharmony_ci				  port->actor_port_number);
24248c2ecf20Sopenharmony_ci			ad_marker_response_received(marker, port);
24258c2ecf20Sopenharmony_ci			break;
24268c2ecf20Sopenharmony_ci		default:
24278c2ecf20Sopenharmony_ci			slave_dbg(slave->bond->dev, slave->dev, "Received an unknown Marker subtype on port %d\n",
24288c2ecf20Sopenharmony_ci				  port->actor_port_number);
24298c2ecf20Sopenharmony_ci			stat = &SLAVE_AD_INFO(slave)->stats.marker_unknown_rx;
24308c2ecf20Sopenharmony_ci			atomic64_inc(stat);
24318c2ecf20Sopenharmony_ci			stat = &BOND_AD_INFO(bond).stats.marker_unknown_rx;
24328c2ecf20Sopenharmony_ci			atomic64_inc(stat);
24338c2ecf20Sopenharmony_ci		}
24348c2ecf20Sopenharmony_ci		break;
24358c2ecf20Sopenharmony_ci	default:
24368c2ecf20Sopenharmony_ci		atomic64_inc(&SLAVE_AD_INFO(slave)->stats.lacpdu_unknown_rx);
24378c2ecf20Sopenharmony_ci		atomic64_inc(&BOND_AD_INFO(bond).stats.lacpdu_unknown_rx);
24388c2ecf20Sopenharmony_ci	}
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	return ret;
24418c2ecf20Sopenharmony_ci}
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci/**
24448c2ecf20Sopenharmony_ci * ad_update_actor_keys - Update the oper / admin keys for a port based on
24458c2ecf20Sopenharmony_ci * its current speed and duplex settings.
24468c2ecf20Sopenharmony_ci *
24478c2ecf20Sopenharmony_ci * @port: the port we'are looking at
24488c2ecf20Sopenharmony_ci * @reset: Boolean to just reset the speed and the duplex part of the key
24498c2ecf20Sopenharmony_ci *
24508c2ecf20Sopenharmony_ci * The logic to change the oper / admin keys is:
24518c2ecf20Sopenharmony_ci * (a) A full duplex port can participate in LACP with partner.
24528c2ecf20Sopenharmony_ci * (b) When the speed is changed, LACP need to be reinitiated.
24538c2ecf20Sopenharmony_ci */
24548c2ecf20Sopenharmony_cistatic void ad_update_actor_keys(struct port *port, bool reset)
24558c2ecf20Sopenharmony_ci{
24568c2ecf20Sopenharmony_ci	u8 duplex = 0;
24578c2ecf20Sopenharmony_ci	u16 ospeed = 0, speed = 0;
24588c2ecf20Sopenharmony_ci	u16 old_oper_key = port->actor_oper_port_key;
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci	port->actor_admin_port_key &= ~(AD_SPEED_KEY_MASKS|AD_DUPLEX_KEY_MASKS);
24618c2ecf20Sopenharmony_ci	if (!reset) {
24628c2ecf20Sopenharmony_ci		speed = __get_link_speed(port);
24638c2ecf20Sopenharmony_ci		ospeed = (old_oper_key & AD_SPEED_KEY_MASKS) >> 1;
24648c2ecf20Sopenharmony_ci		duplex = __get_duplex(port);
24658c2ecf20Sopenharmony_ci		port->actor_admin_port_key |= (speed << 1) | duplex;
24668c2ecf20Sopenharmony_ci	}
24678c2ecf20Sopenharmony_ci	port->actor_oper_port_key = port->actor_admin_port_key;
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_ci	if (old_oper_key != port->actor_oper_port_key) {
24708c2ecf20Sopenharmony_ci		/* Only 'duplex' port participates in LACP */
24718c2ecf20Sopenharmony_ci		if (duplex)
24728c2ecf20Sopenharmony_ci			port->sm_vars |= AD_PORT_LACP_ENABLED;
24738c2ecf20Sopenharmony_ci		else
24748c2ecf20Sopenharmony_ci			port->sm_vars &= ~AD_PORT_LACP_ENABLED;
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci		if (!reset) {
24778c2ecf20Sopenharmony_ci			if (!speed) {
24788c2ecf20Sopenharmony_ci				slave_err(port->slave->bond->dev,
24798c2ecf20Sopenharmony_ci					  port->slave->dev,
24808c2ecf20Sopenharmony_ci					  "speed changed to 0 on port %d\n",
24818c2ecf20Sopenharmony_ci					  port->actor_port_number);
24828c2ecf20Sopenharmony_ci			} else if (duplex && ospeed != speed) {
24838c2ecf20Sopenharmony_ci				/* Speed change restarts LACP state-machine */
24848c2ecf20Sopenharmony_ci				port->sm_vars |= AD_PORT_BEGIN;
24858c2ecf20Sopenharmony_ci			}
24868c2ecf20Sopenharmony_ci		}
24878c2ecf20Sopenharmony_ci	}
24888c2ecf20Sopenharmony_ci}
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci/**
24918c2ecf20Sopenharmony_ci * bond_3ad_adapter_speed_duplex_changed - handle a slave's speed / duplex
24928c2ecf20Sopenharmony_ci * change indication
24938c2ecf20Sopenharmony_ci *
24948c2ecf20Sopenharmony_ci * @slave: slave struct to work on
24958c2ecf20Sopenharmony_ci *
24968c2ecf20Sopenharmony_ci * Handle reselection of aggregator (if needed) for this port.
24978c2ecf20Sopenharmony_ci */
24988c2ecf20Sopenharmony_civoid bond_3ad_adapter_speed_duplex_changed(struct slave *slave)
24998c2ecf20Sopenharmony_ci{
25008c2ecf20Sopenharmony_ci	struct port *port;
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	port = &(SLAVE_AD_INFO(slave)->port);
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_ci	/* if slave is null, the whole port is not initialized */
25058c2ecf20Sopenharmony_ci	if (!port->slave) {
25068c2ecf20Sopenharmony_ci		slave_warn(slave->bond->dev, slave->dev,
25078c2ecf20Sopenharmony_ci			   "speed/duplex changed for uninitialized port\n");
25088c2ecf20Sopenharmony_ci		return;
25098c2ecf20Sopenharmony_ci	}
25108c2ecf20Sopenharmony_ci
25118c2ecf20Sopenharmony_ci	spin_lock_bh(&slave->bond->mode_lock);
25128c2ecf20Sopenharmony_ci	ad_update_actor_keys(port, false);
25138c2ecf20Sopenharmony_ci	spin_unlock_bh(&slave->bond->mode_lock);
25148c2ecf20Sopenharmony_ci	slave_dbg(slave->bond->dev, slave->dev, "Port %d changed speed/duplex\n",
25158c2ecf20Sopenharmony_ci		  port->actor_port_number);
25168c2ecf20Sopenharmony_ci}
25178c2ecf20Sopenharmony_ci
25188c2ecf20Sopenharmony_ci/**
25198c2ecf20Sopenharmony_ci * bond_3ad_handle_link_change - handle a slave's link status change indication
25208c2ecf20Sopenharmony_ci * @slave: slave struct to work on
25218c2ecf20Sopenharmony_ci * @link: whether the link is now up or down
25228c2ecf20Sopenharmony_ci *
25238c2ecf20Sopenharmony_ci * Handle reselection of aggregator (if needed) for this port.
25248c2ecf20Sopenharmony_ci */
25258c2ecf20Sopenharmony_civoid bond_3ad_handle_link_change(struct slave *slave, char link)
25268c2ecf20Sopenharmony_ci{
25278c2ecf20Sopenharmony_ci	struct aggregator *agg;
25288c2ecf20Sopenharmony_ci	struct port *port;
25298c2ecf20Sopenharmony_ci	bool dummy;
25308c2ecf20Sopenharmony_ci
25318c2ecf20Sopenharmony_ci	port = &(SLAVE_AD_INFO(slave)->port);
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci	/* if slave is null, the whole port is not initialized */
25348c2ecf20Sopenharmony_ci	if (!port->slave) {
25358c2ecf20Sopenharmony_ci		slave_warn(slave->bond->dev, slave->dev, "link status changed for uninitialized port\n");
25368c2ecf20Sopenharmony_ci		return;
25378c2ecf20Sopenharmony_ci	}
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci	spin_lock_bh(&slave->bond->mode_lock);
25408c2ecf20Sopenharmony_ci	/* on link down we are zeroing duplex and speed since
25418c2ecf20Sopenharmony_ci	 * some of the adaptors(ce1000.lan) report full duplex/speed
25428c2ecf20Sopenharmony_ci	 * instead of N/A(duplex) / 0(speed).
25438c2ecf20Sopenharmony_ci	 *
25448c2ecf20Sopenharmony_ci	 * on link up we are forcing recheck on the duplex and speed since
25458c2ecf20Sopenharmony_ci	 * some of he adaptors(ce1000.lan) report.
25468c2ecf20Sopenharmony_ci	 */
25478c2ecf20Sopenharmony_ci	if (link == BOND_LINK_UP) {
25488c2ecf20Sopenharmony_ci		port->is_enabled = true;
25498c2ecf20Sopenharmony_ci		ad_update_actor_keys(port, false);
25508c2ecf20Sopenharmony_ci	} else {
25518c2ecf20Sopenharmony_ci		/* link has failed */
25528c2ecf20Sopenharmony_ci		port->is_enabled = false;
25538c2ecf20Sopenharmony_ci		ad_update_actor_keys(port, true);
25548c2ecf20Sopenharmony_ci	}
25558c2ecf20Sopenharmony_ci	agg = __get_first_agg(port);
25568c2ecf20Sopenharmony_ci	ad_agg_selection_logic(agg, &dummy);
25578c2ecf20Sopenharmony_ci
25588c2ecf20Sopenharmony_ci	spin_unlock_bh(&slave->bond->mode_lock);
25598c2ecf20Sopenharmony_ci
25608c2ecf20Sopenharmony_ci	slave_dbg(slave->bond->dev, slave->dev, "Port %d changed link status to %s\n",
25618c2ecf20Sopenharmony_ci		  port->actor_port_number,
25628c2ecf20Sopenharmony_ci		  link == BOND_LINK_UP ? "UP" : "DOWN");
25638c2ecf20Sopenharmony_ci
25648c2ecf20Sopenharmony_ci	/* RTNL is held and mode_lock is released so it's safe
25658c2ecf20Sopenharmony_ci	 * to update slave_array here.
25668c2ecf20Sopenharmony_ci	 */
25678c2ecf20Sopenharmony_ci	bond_update_slave_arr(slave->bond, NULL);
25688c2ecf20Sopenharmony_ci}
25698c2ecf20Sopenharmony_ci
25708c2ecf20Sopenharmony_ci/**
25718c2ecf20Sopenharmony_ci * bond_3ad_set_carrier - set link state for bonding master
25728c2ecf20Sopenharmony_ci * @bond: bonding structure
25738c2ecf20Sopenharmony_ci *
25748c2ecf20Sopenharmony_ci * if we have an active aggregator, we're up, if not, we're down.
25758c2ecf20Sopenharmony_ci * Presumes that we cannot have an active aggregator if there are
25768c2ecf20Sopenharmony_ci * no slaves with link up.
25778c2ecf20Sopenharmony_ci *
25788c2ecf20Sopenharmony_ci * This behavior complies with IEEE 802.3 section 43.3.9.
25798c2ecf20Sopenharmony_ci *
25808c2ecf20Sopenharmony_ci * Called by bond_set_carrier(). Return zero if carrier state does not
25818c2ecf20Sopenharmony_ci * change, nonzero if it does.
25828c2ecf20Sopenharmony_ci */
25838c2ecf20Sopenharmony_ciint bond_3ad_set_carrier(struct bonding *bond)
25848c2ecf20Sopenharmony_ci{
25858c2ecf20Sopenharmony_ci	struct aggregator *active;
25868c2ecf20Sopenharmony_ci	struct slave *first_slave;
25878c2ecf20Sopenharmony_ci	int ret = 1;
25888c2ecf20Sopenharmony_ci
25898c2ecf20Sopenharmony_ci	rcu_read_lock();
25908c2ecf20Sopenharmony_ci	first_slave = bond_first_slave_rcu(bond);
25918c2ecf20Sopenharmony_ci	if (!first_slave) {
25928c2ecf20Sopenharmony_ci		ret = 0;
25938c2ecf20Sopenharmony_ci		goto out;
25948c2ecf20Sopenharmony_ci	}
25958c2ecf20Sopenharmony_ci	active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator));
25968c2ecf20Sopenharmony_ci	if (active) {
25978c2ecf20Sopenharmony_ci		/* are enough slaves available to consider link up? */
25988c2ecf20Sopenharmony_ci		if (__agg_active_ports(active) < bond->params.min_links) {
25998c2ecf20Sopenharmony_ci			if (netif_carrier_ok(bond->dev)) {
26008c2ecf20Sopenharmony_ci				netif_carrier_off(bond->dev);
26018c2ecf20Sopenharmony_ci				goto out;
26028c2ecf20Sopenharmony_ci			}
26038c2ecf20Sopenharmony_ci		} else if (!netif_carrier_ok(bond->dev)) {
26048c2ecf20Sopenharmony_ci			netif_carrier_on(bond->dev);
26058c2ecf20Sopenharmony_ci			goto out;
26068c2ecf20Sopenharmony_ci		}
26078c2ecf20Sopenharmony_ci	} else if (netif_carrier_ok(bond->dev)) {
26088c2ecf20Sopenharmony_ci		netif_carrier_off(bond->dev);
26098c2ecf20Sopenharmony_ci	}
26108c2ecf20Sopenharmony_ciout:
26118c2ecf20Sopenharmony_ci	rcu_read_unlock();
26128c2ecf20Sopenharmony_ci	return ret;
26138c2ecf20Sopenharmony_ci}
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci/**
26168c2ecf20Sopenharmony_ci * __bond_3ad_get_active_agg_info - get information of the active aggregator
26178c2ecf20Sopenharmony_ci * @bond: bonding struct to work on
26188c2ecf20Sopenharmony_ci * @ad_info: ad_info struct to fill with the bond's info
26198c2ecf20Sopenharmony_ci *
26208c2ecf20Sopenharmony_ci * Returns:   0 on success
26218c2ecf20Sopenharmony_ci *          < 0 on error
26228c2ecf20Sopenharmony_ci */
26238c2ecf20Sopenharmony_ciint __bond_3ad_get_active_agg_info(struct bonding *bond,
26248c2ecf20Sopenharmony_ci				   struct ad_info *ad_info)
26258c2ecf20Sopenharmony_ci{
26268c2ecf20Sopenharmony_ci	struct aggregator *aggregator = NULL;
26278c2ecf20Sopenharmony_ci	struct list_head *iter;
26288c2ecf20Sopenharmony_ci	struct slave *slave;
26298c2ecf20Sopenharmony_ci	struct port *port;
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_ci	bond_for_each_slave_rcu(bond, slave, iter) {
26328c2ecf20Sopenharmony_ci		port = &(SLAVE_AD_INFO(slave)->port);
26338c2ecf20Sopenharmony_ci		if (port->aggregator && port->aggregator->is_active) {
26348c2ecf20Sopenharmony_ci			aggregator = port->aggregator;
26358c2ecf20Sopenharmony_ci			break;
26368c2ecf20Sopenharmony_ci		}
26378c2ecf20Sopenharmony_ci	}
26388c2ecf20Sopenharmony_ci
26398c2ecf20Sopenharmony_ci	if (!aggregator)
26408c2ecf20Sopenharmony_ci		return -1;
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci	ad_info->aggregator_id = aggregator->aggregator_identifier;
26438c2ecf20Sopenharmony_ci	ad_info->ports = __agg_active_ports(aggregator);
26448c2ecf20Sopenharmony_ci	ad_info->actor_key = aggregator->actor_oper_aggregator_key;
26458c2ecf20Sopenharmony_ci	ad_info->partner_key = aggregator->partner_oper_aggregator_key;
26468c2ecf20Sopenharmony_ci	ether_addr_copy(ad_info->partner_system,
26478c2ecf20Sopenharmony_ci			aggregator->partner_system.mac_addr_value);
26488c2ecf20Sopenharmony_ci	return 0;
26498c2ecf20Sopenharmony_ci}
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ciint bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
26528c2ecf20Sopenharmony_ci{
26538c2ecf20Sopenharmony_ci	int ret;
26548c2ecf20Sopenharmony_ci
26558c2ecf20Sopenharmony_ci	rcu_read_lock();
26568c2ecf20Sopenharmony_ci	ret = __bond_3ad_get_active_agg_info(bond, ad_info);
26578c2ecf20Sopenharmony_ci	rcu_read_unlock();
26588c2ecf20Sopenharmony_ci
26598c2ecf20Sopenharmony_ci	return ret;
26608c2ecf20Sopenharmony_ci}
26618c2ecf20Sopenharmony_ci
26628c2ecf20Sopenharmony_ciint bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
26638c2ecf20Sopenharmony_ci			 struct slave *slave)
26648c2ecf20Sopenharmony_ci{
26658c2ecf20Sopenharmony_ci	struct lacpdu *lacpdu, _lacpdu;
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci	if (skb->protocol != PKT_TYPE_LACPDU)
26688c2ecf20Sopenharmony_ci		return RX_HANDLER_ANOTHER;
26698c2ecf20Sopenharmony_ci
26708c2ecf20Sopenharmony_ci	if (!MAC_ADDRESS_EQUAL(eth_hdr(skb)->h_dest, lacpdu_mcast_addr))
26718c2ecf20Sopenharmony_ci		return RX_HANDLER_ANOTHER;
26728c2ecf20Sopenharmony_ci
26738c2ecf20Sopenharmony_ci	lacpdu = skb_header_pointer(skb, 0, sizeof(_lacpdu), &_lacpdu);
26748c2ecf20Sopenharmony_ci	if (!lacpdu) {
26758c2ecf20Sopenharmony_ci		atomic64_inc(&SLAVE_AD_INFO(slave)->stats.lacpdu_illegal_rx);
26768c2ecf20Sopenharmony_ci		atomic64_inc(&BOND_AD_INFO(bond).stats.lacpdu_illegal_rx);
26778c2ecf20Sopenharmony_ci		return RX_HANDLER_ANOTHER;
26788c2ecf20Sopenharmony_ci	}
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_ci	return bond_3ad_rx_indication(lacpdu, slave);
26818c2ecf20Sopenharmony_ci}
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_ci/**
26848c2ecf20Sopenharmony_ci * bond_3ad_update_lacp_rate - change the lacp rate
26858c2ecf20Sopenharmony_ci * @bond: bonding struct
26868c2ecf20Sopenharmony_ci *
26878c2ecf20Sopenharmony_ci * When modify lacp_rate parameter via sysfs,
26888c2ecf20Sopenharmony_ci * update actor_oper_port_state of each port.
26898c2ecf20Sopenharmony_ci *
26908c2ecf20Sopenharmony_ci * Hold bond->mode_lock,
26918c2ecf20Sopenharmony_ci * so we can modify port->actor_oper_port_state,
26928c2ecf20Sopenharmony_ci * no matter bond is up or down.
26938c2ecf20Sopenharmony_ci */
26948c2ecf20Sopenharmony_civoid bond_3ad_update_lacp_rate(struct bonding *bond)
26958c2ecf20Sopenharmony_ci{
26968c2ecf20Sopenharmony_ci	struct port *port = NULL;
26978c2ecf20Sopenharmony_ci	struct list_head *iter;
26988c2ecf20Sopenharmony_ci	struct slave *slave;
26998c2ecf20Sopenharmony_ci	int lacp_fast;
27008c2ecf20Sopenharmony_ci
27018c2ecf20Sopenharmony_ci	lacp_fast = bond->params.lacp_fast;
27028c2ecf20Sopenharmony_ci	spin_lock_bh(&bond->mode_lock);
27038c2ecf20Sopenharmony_ci	bond_for_each_slave(bond, slave, iter) {
27048c2ecf20Sopenharmony_ci		port = &(SLAVE_AD_INFO(slave)->port);
27058c2ecf20Sopenharmony_ci		if (lacp_fast)
27068c2ecf20Sopenharmony_ci			port->actor_oper_port_state |= LACP_STATE_LACP_TIMEOUT;
27078c2ecf20Sopenharmony_ci		else
27088c2ecf20Sopenharmony_ci			port->actor_oper_port_state &= ~LACP_STATE_LACP_TIMEOUT;
27098c2ecf20Sopenharmony_ci	}
27108c2ecf20Sopenharmony_ci	spin_unlock_bh(&bond->mode_lock);
27118c2ecf20Sopenharmony_ci}
27128c2ecf20Sopenharmony_ci
27138c2ecf20Sopenharmony_cisize_t bond_3ad_stats_size(void)
27148c2ecf20Sopenharmony_ci{
27158c2ecf20Sopenharmony_ci	return nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_RX */
27168c2ecf20Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_TX */
27178c2ecf20Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_UNKNOWN_RX */
27188c2ecf20Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_ILLEGAL_RX */
27198c2ecf20Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_RX */
27208c2ecf20Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_TX */
27218c2ecf20Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_RESP_RX */
27228c2ecf20Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_RESP_TX */
27238c2ecf20Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)); /* BOND_3AD_STAT_MARKER_UNKNOWN_RX */
27248c2ecf20Sopenharmony_ci}
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_ciint bond_3ad_stats_fill(struct sk_buff *skb, struct bond_3ad_stats *stats)
27278c2ecf20Sopenharmony_ci{
27288c2ecf20Sopenharmony_ci	u64 val;
27298c2ecf20Sopenharmony_ci
27308c2ecf20Sopenharmony_ci	val = atomic64_read(&stats->lacpdu_rx);
27318c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_RX, val,
27328c2ecf20Sopenharmony_ci			      BOND_3AD_STAT_PAD))
27338c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27348c2ecf20Sopenharmony_ci	val = atomic64_read(&stats->lacpdu_tx);
27358c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_TX, val,
27368c2ecf20Sopenharmony_ci			      BOND_3AD_STAT_PAD))
27378c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27388c2ecf20Sopenharmony_ci	val = atomic64_read(&stats->lacpdu_unknown_rx);
27398c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_UNKNOWN_RX, val,
27408c2ecf20Sopenharmony_ci			      BOND_3AD_STAT_PAD))
27418c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27428c2ecf20Sopenharmony_ci	val = atomic64_read(&stats->lacpdu_illegal_rx);
27438c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_ILLEGAL_RX, val,
27448c2ecf20Sopenharmony_ci			      BOND_3AD_STAT_PAD))
27458c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_ci	val = atomic64_read(&stats->marker_rx);
27488c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RX, val,
27498c2ecf20Sopenharmony_ci			      BOND_3AD_STAT_PAD))
27508c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27518c2ecf20Sopenharmony_ci	val = atomic64_read(&stats->marker_tx);
27528c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_TX, val,
27538c2ecf20Sopenharmony_ci			      BOND_3AD_STAT_PAD))
27548c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27558c2ecf20Sopenharmony_ci	val = atomic64_read(&stats->marker_resp_rx);
27568c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RESP_RX, val,
27578c2ecf20Sopenharmony_ci			      BOND_3AD_STAT_PAD))
27588c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27598c2ecf20Sopenharmony_ci	val = atomic64_read(&stats->marker_resp_tx);
27608c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RESP_TX, val,
27618c2ecf20Sopenharmony_ci			      BOND_3AD_STAT_PAD))
27628c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27638c2ecf20Sopenharmony_ci	val = atomic64_read(&stats->marker_unknown_rx);
27648c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_UNKNOWN_RX, val,
27658c2ecf20Sopenharmony_ci			      BOND_3AD_STAT_PAD))
27668c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_ci	return 0;
27698c2ecf20Sopenharmony_ci}
2770