xref: /openbmc/linux/drivers/net/bonding/bond_3ad.c (revision ea68a3e9d14e9e0bf017d178fb4bd53b6deb1482)
19e567af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Copyright(c) 1999 - 2004 Intel Corporation. All rights reserved.
41da177e4SLinus Torvalds  */
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds #include <linux/skbuff.h>
71da177e4SLinus Torvalds #include <linux/if_ether.h>
81da177e4SLinus Torvalds #include <linux/netdevice.h>
91da177e4SLinus Torvalds #include <linux/spinlock.h>
101da177e4SLinus Torvalds #include <linux/ethtool.h>
11fd989c83SJay Vosburgh #include <linux/etherdevice.h>
121da177e4SLinus Torvalds #include <linux/if_bonding.h>
131da177e4SLinus Torvalds #include <linux/pkt_sched.h>
14e730c155SEric W. Biederman #include <net/net_namespace.h>
151ef8019bSDavid S. Miller #include <net/bonding.h>
161ef8019bSDavid S. Miller #include <net/bond_3ad.h>
17a258aeacSNikolay Aleksandrov #include <net/netlink.h>
181da177e4SLinus Torvalds 
193bf2d28aSVeaceslav Falico /* General definitions */
201da177e4SLinus Torvalds #define AD_SHORT_TIMEOUT           1
211da177e4SLinus Torvalds #define AD_LONG_TIMEOUT            0
221da177e4SLinus Torvalds #define AD_STANDBY                 0x2
231da177e4SLinus Torvalds #define AD_MAX_TX_IN_SECOND        3
241da177e4SLinus Torvalds #define AD_COLLECTOR_MAX_DELAY     0
251da177e4SLinus Torvalds 
263bf2d28aSVeaceslav Falico /* Timer definitions (43.4.4 in the 802.3ad standard) */
271da177e4SLinus Torvalds #define AD_FAST_PERIODIC_TIME      1
281da177e4SLinus Torvalds #define AD_SLOW_PERIODIC_TIME      30
291da177e4SLinus Torvalds #define AD_SHORT_TIMEOUT_TIME      (3*AD_FAST_PERIODIC_TIME)
301da177e4SLinus Torvalds #define AD_LONG_TIMEOUT_TIME       (3*AD_SLOW_PERIODIC_TIME)
311da177e4SLinus Torvalds #define AD_CHURN_DETECTION_TIME    60
321da177e4SLinus Torvalds #define AD_AGGREGATE_WAIT_TIME     2
331da177e4SLinus Torvalds 
343bf2d28aSVeaceslav Falico /* Port Variables definitions used by the State Machines (43.4.7 in the
353bf2d28aSVeaceslav Falico  * 802.3ad standard)
363bf2d28aSVeaceslav Falico  */
371da177e4SLinus Torvalds #define AD_PORT_BEGIN           0x1
381da177e4SLinus Torvalds #define AD_PORT_LACP_ENABLED    0x2
391da177e4SLinus Torvalds #define AD_PORT_ACTOR_CHURN     0x4
401da177e4SLinus Torvalds #define AD_PORT_PARTNER_CHURN   0x8
411da177e4SLinus Torvalds #define AD_PORT_READY           0x10
421da177e4SLinus Torvalds #define AD_PORT_READY_N         0x20
431da177e4SLinus Torvalds #define AD_PORT_MATCHED         0x40
441da177e4SLinus Torvalds #define AD_PORT_STANDBY         0x80
451da177e4SLinus Torvalds #define AD_PORT_SELECTED        0x100
461da177e4SLinus Torvalds #define AD_PORT_MOVED           0x200
47ef015d72SMahesh Bandewar #define AD_PORT_CHURNED         (AD_PORT_ACTOR_CHURN | AD_PORT_PARTNER_CHURN)
481da177e4SLinus Torvalds 
493bf2d28aSVeaceslav Falico /* Port Key definitions
503bf2d28aSVeaceslav Falico  * key is determined according to the link speed, duplex and
513bf2d28aSVeaceslav Falico  * user key (which is yet not supported)
523bf2d28aSVeaceslav Falico  *           --------------------------------------------------------------
53d22a5fc0SMahesh Bandewar  * Port key  | User key (10 bits)           | Speed (5 bits)      | Duplex|
543bf2d28aSVeaceslav Falico  *           --------------------------------------------------------------
55d22a5fc0SMahesh Bandewar  *           |15                           6|5                   1|0
563bf2d28aSVeaceslav Falico  */
57cb8dda90SJianhua Xie #define  AD_DUPLEX_KEY_MASKS    0x1
58cb8dda90SJianhua Xie #define  AD_SPEED_KEY_MASKS     0x3E
59cb8dda90SJianhua Xie #define  AD_USER_KEY_MASKS      0xFFC0
601da177e4SLinus Torvalds 
61cb8dda90SJianhua Xie enum ad_link_speed_type {
62cb8dda90SJianhua Xie 	AD_LINK_SPEED_1MBPS = 1,
63cb8dda90SJianhua Xie 	AD_LINK_SPEED_10MBPS,
64cb8dda90SJianhua Xie 	AD_LINK_SPEED_100MBPS,
65cb8dda90SJianhua Xie 	AD_LINK_SPEED_1000MBPS,
66424c3232SJianhua Xie 	AD_LINK_SPEED_2500MBPS,
67c7c55067SThibaut Collet 	AD_LINK_SPEED_5000MBPS,
68424c3232SJianhua Xie 	AD_LINK_SPEED_10000MBPS,
693fcd64cfSNicolas Dichtel 	AD_LINK_SPEED_14000MBPS,
70424c3232SJianhua Xie 	AD_LINK_SPEED_20000MBPS,
7119ddde1eSJarod Wilson 	AD_LINK_SPEED_25000MBPS,
72424c3232SJianhua Xie 	AD_LINK_SPEED_40000MBPS,
73c7c55067SThibaut Collet 	AD_LINK_SPEED_50000MBPS,
743952af4dSJiri Pirko 	AD_LINK_SPEED_56000MBPS,
753952af4dSJiri Pirko 	AD_LINK_SPEED_100000MBPS,
76ab73447cSNikolay Aleksandrov 	AD_LINK_SPEED_200000MBPS,
77138e3b3cSNikolay Aleksandrov 	AD_LINK_SPEED_400000MBPS,
7841305d37SAmit Cohen 	AD_LINK_SPEED_800000MBPS,
79cb8dda90SJianhua Xie };
801da177e4SLinus Torvalds 
81815117adSdingtianhong /* compare MAC addresses */
82815117adSdingtianhong #define MAC_ADDRESS_EQUAL(A, B)	\
83815117adSdingtianhong 	ether_addr_equal_64bits((const u8 *)A, (const u8 *)B)
841da177e4SLinus Torvalds 
85f87fda00SEric Dumazet static const u8 null_mac_addr[ETH_ALEN + 2] __long_aligned = {
86f87fda00SEric Dumazet 	0, 0, 0, 0, 0, 0
87f87fda00SEric Dumazet };
88f2e44dffSJonathan Toppins 
89f2e44dffSJonathan Toppins static const u16 ad_ticks_per_sec = 1000 / AD_TIMER_INTERVAL;
901da177e4SLinus Torvalds static const int ad_delta_in_ticks = (AD_TIMER_INTERVAL * HZ) / 1000;
911da177e4SLinus Torvalds 
921d9a143eSBenjamin Poirier const u8 lacpdu_mcast_addr[ETH_ALEN + 2] __long_aligned = {
931d9a143eSBenjamin Poirier 	0x01, 0x80, 0xC2, 0x00, 0x00, 0x02
941d9a143eSBenjamin Poirier };
95e4ac4320SHolger Eitzenberger 
963bf2d28aSVeaceslav Falico /* ================= main 802.3ad protocol functions ================== */
971da177e4SLinus Torvalds static int ad_lacpdu_send(struct port *port);
981c3f0b8eSMathieu Desnoyers static int ad_marker_send(struct port *port, struct bond_marker *marker);
99ee637714SMahesh Bandewar static void ad_mux_machine(struct port *port, bool *update_slave_arr);
1001da177e4SLinus Torvalds static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port);
1011da177e4SLinus Torvalds static void ad_tx_machine(struct port *port);
102bbef56d8SColin Ian King static void ad_periodic_machine(struct port *port, struct bond_params *bond_params);
103ee637714SMahesh Bandewar static void ad_port_selection_logic(struct port *port, bool *update_slave_arr);
104ee637714SMahesh Bandewar static void ad_agg_selection_logic(struct aggregator *aggregator,
105ee637714SMahesh Bandewar 				   bool *update_slave_arr);
1061da177e4SLinus Torvalds static void ad_clear_agg(struct aggregator *aggregator);
1071da177e4SLinus Torvalds static void ad_initialize_agg(struct aggregator *aggregator);
1081da177e4SLinus Torvalds static void ad_initialize_port(struct port *port, int lacp_fast);
109ee637714SMahesh Bandewar static void ad_enable_collecting_distributing(struct port *port,
110ee637714SMahesh Bandewar 					      bool *update_slave_arr);
111ee637714SMahesh Bandewar static void ad_disable_collecting_distributing(struct port *port,
112ee637714SMahesh Bandewar 					       bool *update_slave_arr);
1133bf2d28aSVeaceslav Falico static void ad_marker_info_received(struct bond_marker *marker_info,
1143bf2d28aSVeaceslav Falico 				    struct port *port);
1153bf2d28aSVeaceslav Falico static void ad_marker_response_received(struct bond_marker *marker,
1163bf2d28aSVeaceslav Falico 					struct port *port);
1177bb11dc9SMahesh Bandewar static void ad_update_actor_keys(struct port *port, bool reset);
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 
1203bf2d28aSVeaceslav Falico /* ================= api to bonding and kernel code ================== */
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds /**
1231da177e4SLinus Torvalds  * __get_bond_by_port - get the port's bonding struct
1241da177e4SLinus Torvalds  * @port: the port we're looking at
1251da177e4SLinus Torvalds  *
1261da177e4SLinus Torvalds  * Return @port's bonding struct, or %NULL if it can't be found.
1271da177e4SLinus Torvalds  */
__get_bond_by_port(struct port * port)1281da177e4SLinus Torvalds static inline struct bonding *__get_bond_by_port(struct port *port)
1291da177e4SLinus Torvalds {
1307bfc4753SBandan Das 	if (port->slave == NULL)
1311da177e4SLinus Torvalds 		return NULL;
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	return bond_get_bond_by_slave(port->slave);
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds /**
1371da177e4SLinus Torvalds  * __get_first_agg - get the first aggregator in the bond
138a35e5478SLee Jones  * @port: the port we're looking at
1391da177e4SLinus Torvalds  *
1401da177e4SLinus Torvalds  * Return the aggregator of the first slave in @bond, or %NULL if it can't be
1411da177e4SLinus Torvalds  * found.
142768b9549SVeaceslav Falico  * The caller must hold RCU or RTNL lock.
1431da177e4SLinus Torvalds  */
__get_first_agg(struct port * port)1441da177e4SLinus Torvalds static inline struct aggregator *__get_first_agg(struct port *port)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds 	struct bonding *bond = __get_bond_by_port(port);
147dec1e90eSnikolay@redhat.com 	struct slave *first_slave;
148768b9549SVeaceslav Falico 	struct aggregator *agg;
1491da177e4SLinus Torvalds 
150be79bd04Sdingtianhong 	/* If there's no bond for this port, or bond has no slaves */
151dec1e90eSnikolay@redhat.com 	if (bond == NULL)
1521da177e4SLinus Torvalds 		return NULL;
1533bf2d28aSVeaceslav Falico 
154be79bd04Sdingtianhong 	rcu_read_lock();
155be79bd04Sdingtianhong 	first_slave = bond_first_slave_rcu(bond);
1563fdddd85Sdingtianhong 	agg = first_slave ? &(SLAVE_AD_INFO(first_slave)->aggregator) : NULL;
157be79bd04Sdingtianhong 	rcu_read_unlock();
1583bf2d28aSVeaceslav Falico 
159768b9549SVeaceslav Falico 	return agg;
1601da177e4SLinus Torvalds }
1611da177e4SLinus Torvalds 
1623bf2d28aSVeaceslav Falico /**
1633bf2d28aSVeaceslav Falico  * __agg_has_partner - see if we have a partner
1643bf2d28aSVeaceslav Falico  * @agg: the agregator we're looking at
165fd989c83SJay Vosburgh  *
166fd989c83SJay Vosburgh  * Return nonzero if aggregator has a partner (denoted by a non-zero ether
167fd989c83SJay Vosburgh  * address for the partner). Return 0 if not.
168fd989c83SJay Vosburgh  */
__agg_has_partner(struct aggregator * agg)169fd989c83SJay Vosburgh static inline int __agg_has_partner(struct aggregator *agg)
170fd989c83SJay Vosburgh {
171fd989c83SJay Vosburgh 	return !is_zero_ether_addr(agg->partner_system.mac_addr_value);
172fd989c83SJay Vosburgh }
173fd989c83SJay Vosburgh 
1741da177e4SLinus Torvalds /**
1751da177e4SLinus Torvalds  * __disable_port - disable the port's slave
1761da177e4SLinus Torvalds  * @port: the port we're looking at
1771da177e4SLinus Torvalds  */
__disable_port(struct port * port)1781da177e4SLinus Torvalds static inline void __disable_port(struct port *port)
1791da177e4SLinus Torvalds {
1805e5b0665Sdingtianhong 	bond_set_slave_inactive_flags(port->slave, BOND_SLAVE_NOTIFY_LATER);
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds /**
1841da177e4SLinus Torvalds  * __enable_port - enable the port's slave, if it's up
1851da177e4SLinus Torvalds  * @port: the port we're looking at
1861da177e4SLinus Torvalds  */
__enable_port(struct port * port)1871da177e4SLinus Torvalds static inline void __enable_port(struct port *port)
1881da177e4SLinus Torvalds {
1891da177e4SLinus Torvalds 	struct slave *slave = port->slave;
1901da177e4SLinus Torvalds 
191b6adc610SVeaceslav Falico 	if ((slave->link == BOND_LINK_UP) && bond_slave_is_up(slave))
1925e5b0665Sdingtianhong 		bond_set_slave_active_flags(slave, BOND_SLAVE_NOTIFY_LATER);
1931da177e4SLinus Torvalds }
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds /**
1961da177e4SLinus Torvalds  * __port_is_enabled - check if the port's slave is in active state
1971da177e4SLinus Torvalds  * @port: the port we're looking at
1981da177e4SLinus Torvalds  */
__port_is_enabled(struct port * port)1991da177e4SLinus Torvalds static inline int __port_is_enabled(struct port *port)
2001da177e4SLinus Torvalds {
201e30bc066SJiri Pirko 	return bond_is_active_slave(port->slave);
2021da177e4SLinus Torvalds }
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds /**
2051da177e4SLinus Torvalds  * __get_agg_selection_mode - get the aggregator selection mode
2061da177e4SLinus Torvalds  * @port: the port we're looking at
2071da177e4SLinus Torvalds  *
208fd989c83SJay Vosburgh  * Get the aggregator selection mode. Can be %STABLE, %BANDWIDTH or %COUNT.
2091da177e4SLinus Torvalds  */
__get_agg_selection_mode(struct port * port)2101da177e4SLinus Torvalds static inline u32 __get_agg_selection_mode(struct port *port)
2111da177e4SLinus Torvalds {
2121da177e4SLinus Torvalds 	struct bonding *bond = __get_bond_by_port(port);
2131da177e4SLinus Torvalds 
2147bfc4753SBandan Das 	if (bond == NULL)
215fd989c83SJay Vosburgh 		return BOND_AD_STABLE;
2161da177e4SLinus Torvalds 
2171a14fbcbSPeter Pan(潘卫平) 	return bond->params.ad_select;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds /**
2211da177e4SLinus Torvalds  * __check_agg_selection_timer - check if the selection timer has expired
2221da177e4SLinus Torvalds  * @port: the port we're looking at
2231da177e4SLinus Torvalds  */
__check_agg_selection_timer(struct port * port)2241da177e4SLinus Torvalds static inline int __check_agg_selection_timer(struct port *port)
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds 	struct bonding *bond = __get_bond_by_port(port);
2271da177e4SLinus Torvalds 
2287bfc4753SBandan Das 	if (bond == NULL)
2291da177e4SLinus Torvalds 		return 0;
2301da177e4SLinus Torvalds 
2319ceaf6f7SEric Dumazet 	return atomic_read(&BOND_AD_INFO(bond).agg_select_timer) ? 1 : 0;
2321da177e4SLinus Torvalds }
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds /**
2351da177e4SLinus Torvalds  * __get_link_speed - get a port's speed
2361da177e4SLinus Torvalds  * @port: the port we're looking at
2371da177e4SLinus Torvalds  *
238cb8dda90SJianhua Xie  * Return @port's speed in 802.3ad enum format. i.e. one of:
2391da177e4SLinus Torvalds  *     0,
240cb8dda90SJianhua Xie  *     %AD_LINK_SPEED_10MBPS,
241cb8dda90SJianhua Xie  *     %AD_LINK_SPEED_100MBPS,
242cb8dda90SJianhua Xie  *     %AD_LINK_SPEED_1000MBPS,
243424c3232SJianhua Xie  *     %AD_LINK_SPEED_2500MBPS,
244c7c55067SThibaut Collet  *     %AD_LINK_SPEED_5000MBPS,
245cb8dda90SJianhua Xie  *     %AD_LINK_SPEED_10000MBPS
2463fcd64cfSNicolas Dichtel  *     %AD_LINK_SPEED_14000MBPS,
247424c3232SJianhua Xie  *     %AD_LINK_SPEED_20000MBPS
24819ddde1eSJarod Wilson  *     %AD_LINK_SPEED_25000MBPS
249424c3232SJianhua Xie  *     %AD_LINK_SPEED_40000MBPS
250c7c55067SThibaut Collet  *     %AD_LINK_SPEED_50000MBPS
251424c3232SJianhua Xie  *     %AD_LINK_SPEED_56000MBPS
2523952af4dSJiri Pirko  *     %AD_LINK_SPEED_100000MBPS
253ab73447cSNikolay Aleksandrov  *     %AD_LINK_SPEED_200000MBPS
254138e3b3cSNikolay Aleksandrov  *     %AD_LINK_SPEED_400000MBPS
25541305d37SAmit Cohen  *     %AD_LINK_SPEED_800000MBPS
2561da177e4SLinus Torvalds  */
__get_link_speed(struct port * port)2571da177e4SLinus Torvalds static u16 __get_link_speed(struct port *port)
2581da177e4SLinus Torvalds {
2591da177e4SLinus Torvalds 	struct slave *slave = port->slave;
2601da177e4SLinus Torvalds 	u16 speed;
2611da177e4SLinus Torvalds 
2623bf2d28aSVeaceslav Falico 	/* this if covers only a special case: when the configuration starts
2633bf2d28aSVeaceslav Falico 	 * with link down, it sets the speed to 0.
2643bf2d28aSVeaceslav Falico 	 * This is done in spite of the fact that the e100 driver reports 0
2653bf2d28aSVeaceslav Falico 	 * to be compatible with MVT in the future.
2663bf2d28aSVeaceslav Falico 	 */
2677bfc4753SBandan Das 	if (slave->link != BOND_LINK_UP)
2681da177e4SLinus Torvalds 		speed = 0;
2697bfc4753SBandan Das 	else {
2701da177e4SLinus Torvalds 		switch (slave->speed) {
2711da177e4SLinus Torvalds 		case SPEED_10:
272cb8dda90SJianhua Xie 			speed = AD_LINK_SPEED_10MBPS;
2731da177e4SLinus Torvalds 			break;
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds 		case SPEED_100:
276cb8dda90SJianhua Xie 			speed = AD_LINK_SPEED_100MBPS;
2771da177e4SLinus Torvalds 			break;
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds 		case SPEED_1000:
280cb8dda90SJianhua Xie 			speed = AD_LINK_SPEED_1000MBPS;
2811da177e4SLinus Torvalds 			break;
2821da177e4SLinus Torvalds 
283424c3232SJianhua Xie 		case SPEED_2500:
284424c3232SJianhua Xie 			speed = AD_LINK_SPEED_2500MBPS;
285424c3232SJianhua Xie 			break;
286424c3232SJianhua Xie 
287c7c55067SThibaut Collet 		case SPEED_5000:
288c7c55067SThibaut Collet 			speed = AD_LINK_SPEED_5000MBPS;
289c7c55067SThibaut Collet 			break;
290c7c55067SThibaut Collet 
29194dbffd5SJay Vosburgh 		case SPEED_10000:
292cb8dda90SJianhua Xie 			speed = AD_LINK_SPEED_10000MBPS;
29394dbffd5SJay Vosburgh 			break;
29494dbffd5SJay Vosburgh 
2953fcd64cfSNicolas Dichtel 		case SPEED_14000:
2963fcd64cfSNicolas Dichtel 			speed = AD_LINK_SPEED_14000MBPS;
2973fcd64cfSNicolas Dichtel 			break;
2983fcd64cfSNicolas Dichtel 
299424c3232SJianhua Xie 		case SPEED_20000:
300424c3232SJianhua Xie 			speed = AD_LINK_SPEED_20000MBPS;
301424c3232SJianhua Xie 			break;
302424c3232SJianhua Xie 
30319ddde1eSJarod Wilson 		case SPEED_25000:
30419ddde1eSJarod Wilson 			speed = AD_LINK_SPEED_25000MBPS;
30519ddde1eSJarod Wilson 			break;
30619ddde1eSJarod Wilson 
307424c3232SJianhua Xie 		case SPEED_40000:
308424c3232SJianhua Xie 			speed = AD_LINK_SPEED_40000MBPS;
309424c3232SJianhua Xie 			break;
310424c3232SJianhua Xie 
311c7c55067SThibaut Collet 		case SPEED_50000:
312c7c55067SThibaut Collet 			speed = AD_LINK_SPEED_50000MBPS;
313c7c55067SThibaut Collet 			break;
314c7c55067SThibaut Collet 
315424c3232SJianhua Xie 		case SPEED_56000:
316424c3232SJianhua Xie 			speed = AD_LINK_SPEED_56000MBPS;
317424c3232SJianhua Xie 			break;
318424c3232SJianhua Xie 
3193952af4dSJiri Pirko 		case SPEED_100000:
3203952af4dSJiri Pirko 			speed = AD_LINK_SPEED_100000MBPS;
3213952af4dSJiri Pirko 			break;
3223952af4dSJiri Pirko 
323ab73447cSNikolay Aleksandrov 		case SPEED_200000:
324ab73447cSNikolay Aleksandrov 			speed = AD_LINK_SPEED_200000MBPS;
325ab73447cSNikolay Aleksandrov 			break;
326ab73447cSNikolay Aleksandrov 
327138e3b3cSNikolay Aleksandrov 		case SPEED_400000:
328138e3b3cSNikolay Aleksandrov 			speed = AD_LINK_SPEED_400000MBPS;
329138e3b3cSNikolay Aleksandrov 			break;
330138e3b3cSNikolay Aleksandrov 
33141305d37SAmit Cohen 		case SPEED_800000:
33241305d37SAmit Cohen 			speed = AD_LINK_SPEED_800000MBPS;
33341305d37SAmit Cohen 			break;
33441305d37SAmit Cohen 
3351da177e4SLinus Torvalds 		default:
3363bf2d28aSVeaceslav Falico 			/* unknown speed value from ethtool. shouldn't happen */
337cd99c377SNicolas Dichtel 			if (slave->speed != SPEED_UNKNOWN)
3385edf55adSIdo Schimmel 				pr_err_once("%s: (slave %s): unknown ethtool speed (%d) for port %d (set it to 0)\n",
339cd99c377SNicolas Dichtel 					    slave->bond->dev->name,
34017720981SJarod Wilson 					    slave->dev->name, slave->speed,
341cd99c377SNicolas Dichtel 					    port->actor_port_number);
3423bf2d28aSVeaceslav Falico 			speed = 0;
3431da177e4SLinus Torvalds 			break;
3441da177e4SLinus Torvalds 		}
3451da177e4SLinus Torvalds 	}
3461da177e4SLinus Torvalds 
34717720981SJarod Wilson 	slave_dbg(slave->bond->dev, slave->dev, "Port %d Received link speed %d update from adapter\n",
348a4aee5c8SJoe Perches 		  port->actor_port_number, speed);
3491da177e4SLinus Torvalds 	return speed;
3501da177e4SLinus Torvalds }
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds /**
3531da177e4SLinus Torvalds  * __get_duplex - get a port's duplex
3541da177e4SLinus Torvalds  * @port: the port we're looking at
3551da177e4SLinus Torvalds  *
3561da177e4SLinus Torvalds  * Return @port's duplex in 802.3ad bitmask format. i.e.:
3571da177e4SLinus Torvalds  *     0x01 if in full duplex
3581da177e4SLinus Torvalds  *     0x00 otherwise
3591da177e4SLinus Torvalds  */
__get_duplex(struct port * port)3601da177e4SLinus Torvalds static u8 __get_duplex(struct port *port)
3611da177e4SLinus Torvalds {
3621da177e4SLinus Torvalds 	struct slave *slave = port->slave;
363b25c2e7dSMahesh Bandewar 	u8 retval = 0x0;
3641da177e4SLinus Torvalds 
3653bf2d28aSVeaceslav Falico 	/* handling a special case: when the configuration starts with
3663bf2d28aSVeaceslav Falico 	 * link down, it sets the duplex to 0.
3673bf2d28aSVeaceslav Falico 	 */
368b25c2e7dSMahesh Bandewar 	if (slave->link == BOND_LINK_UP) {
3691da177e4SLinus Torvalds 		switch (slave->duplex) {
3701da177e4SLinus Torvalds 		case DUPLEX_FULL:
3711da177e4SLinus Torvalds 			retval = 0x1;
37217720981SJarod Wilson 			slave_dbg(slave->bond->dev, slave->dev, "Port %d Received status full duplex update from adapter\n",
373a4aee5c8SJoe Perches 				  port->actor_port_number);
3741da177e4SLinus Torvalds 			break;
3751da177e4SLinus Torvalds 		case DUPLEX_HALF:
3761da177e4SLinus Torvalds 		default:
3771da177e4SLinus Torvalds 			retval = 0x0;
37817720981SJarod Wilson 			slave_dbg(slave->bond->dev, slave->dev, "Port %d Received status NOT full duplex update from adapter\n",
379a4aee5c8SJoe Perches 				  port->actor_port_number);
3801da177e4SLinus Torvalds 			break;
3811da177e4SLinus Torvalds 		}
3821da177e4SLinus Torvalds 	}
3831da177e4SLinus Torvalds 	return retval;
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds 
__ad_actor_update_port(struct port * port)3865ee14e6dSNikolay Aleksandrov static void __ad_actor_update_port(struct port *port)
3875ee14e6dSNikolay Aleksandrov {
3885ee14e6dSNikolay Aleksandrov 	const struct bonding *bond = bond_get_bond_by_slave(port->slave);
3895ee14e6dSNikolay Aleksandrov 
3905ee14e6dSNikolay Aleksandrov 	port->actor_system = BOND_AD_INFO(bond).system.sys_mac_addr;
3915ee14e6dSNikolay Aleksandrov 	port->actor_system_priority = BOND_AD_INFO(bond).system.sys_priority;
3925ee14e6dSNikolay Aleksandrov }
3935ee14e6dSNikolay Aleksandrov 
3943bf2d28aSVeaceslav Falico /* Conversions */
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds /**
3971da177e4SLinus Torvalds  * __ad_timer_to_ticks - convert a given timer type to AD module ticks
3981da177e4SLinus Torvalds  * @timer_type:	which timer to operate
3991da177e4SLinus Torvalds  * @par: timer parameter. see below
4001da177e4SLinus Torvalds  *
4011da177e4SLinus Torvalds  * If @timer_type is %current_while_timer, @par indicates long/short timer.
4021da177e4SLinus Torvalds  * If @timer_type is %periodic_timer, @par is one of %FAST_PERIODIC_TIME,
4031da177e4SLinus Torvalds  *						     %SLOW_PERIODIC_TIME.
4041da177e4SLinus Torvalds  */
__ad_timer_to_ticks(u16 timer_type,u16 par)4051da177e4SLinus Torvalds static u16 __ad_timer_to_ticks(u16 timer_type, u16 par)
4061da177e4SLinus Torvalds {
407128ea6c3SBandan Das 	u16 retval = 0; /* to silence the compiler */
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	switch (timer_type) {
4103bf2d28aSVeaceslav Falico 	case AD_CURRENT_WHILE_TIMER:	/* for rx machine usage */
4117bfc4753SBandan Das 		if (par)
4123bf2d28aSVeaceslav Falico 			retval = (AD_SHORT_TIMEOUT_TIME*ad_ticks_per_sec);
4137bfc4753SBandan Das 		else
4143bf2d28aSVeaceslav Falico 			retval = (AD_LONG_TIMEOUT_TIME*ad_ticks_per_sec);
4151da177e4SLinus Torvalds 		break;
4163bf2d28aSVeaceslav Falico 	case AD_ACTOR_CHURN_TIMER:	/* for local churn machine */
4171da177e4SLinus Torvalds 		retval = (AD_CHURN_DETECTION_TIME*ad_ticks_per_sec);
4181da177e4SLinus Torvalds 		break;
4193bf2d28aSVeaceslav Falico 	case AD_PERIODIC_TIMER:		/* for periodic machine */
4203bf2d28aSVeaceslav Falico 		retval = (par*ad_ticks_per_sec); /* long timeout */
4211da177e4SLinus Torvalds 		break;
4223bf2d28aSVeaceslav Falico 	case AD_PARTNER_CHURN_TIMER:	/* for remote churn machine */
4231da177e4SLinus Torvalds 		retval = (AD_CHURN_DETECTION_TIME*ad_ticks_per_sec);
4241da177e4SLinus Torvalds 		break;
4253bf2d28aSVeaceslav Falico 	case AD_WAIT_WHILE_TIMER:	/* for selection machine */
4261da177e4SLinus Torvalds 		retval = (AD_AGGREGATE_WAIT_TIME*ad_ticks_per_sec);
4271da177e4SLinus Torvalds 		break;
4281da177e4SLinus Torvalds 	}
4293bf2d28aSVeaceslav Falico 
4301da177e4SLinus Torvalds 	return retval;
4311da177e4SLinus Torvalds }
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 
4343bf2d28aSVeaceslav Falico /* ================= ad_rx_machine helper functions ================== */
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds /**
4372d6682dbSJay Vosburgh  * __choose_matched - update a port's matched variable from a received lacpdu
4382d6682dbSJay Vosburgh  * @lacpdu: the lacpdu we've received
4392d6682dbSJay Vosburgh  * @port: the port we're looking at
4402d6682dbSJay Vosburgh  *
4412d6682dbSJay Vosburgh  * Update the value of the matched variable, using parameter values from a
4422d6682dbSJay Vosburgh  * newly received lacpdu. Parameter values for the partner carried in the
4432d6682dbSJay Vosburgh  * received PDU are compared with the corresponding operational parameter
4442d6682dbSJay Vosburgh  * values for the actor. Matched is set to TRUE if all of these parameters
4452d6682dbSJay Vosburgh  * match and the PDU parameter partner_state.aggregation has the same value as
4462d6682dbSJay Vosburgh  * actor_oper_port_state.aggregation and lacp will actively maintain the link
4472d6682dbSJay Vosburgh  * in the aggregation. Matched is also set to TRUE if the value of
4482d6682dbSJay Vosburgh  * actor_state.aggregation in the received PDU is set to FALSE, i.e., indicates
4492d6682dbSJay Vosburgh  * an individual link and lacp will actively maintain the link. Otherwise,
4502d6682dbSJay Vosburgh  * matched is set to FALSE. LACP is considered to be actively maintaining the
4512d6682dbSJay Vosburgh  * link if either the PDU's actor_state.lacp_activity variable is TRUE or both
4522d6682dbSJay Vosburgh  * the actor's actor_oper_port_state.lacp_activity and the PDU's
4532d6682dbSJay Vosburgh  * partner_state.lacp_activity variables are TRUE.
4542d6682dbSJay Vosburgh  *
4552d6682dbSJay Vosburgh  * Note: the AD_PORT_MATCHED "variable" is not specified by 802.3ad; it is
4562d6682dbSJay Vosburgh  * used here to implement the language from 802.3ad 43.4.9 that requires
4572d6682dbSJay Vosburgh  * recordPDU to "match" the LACPDU parameters to the stored values.
4582d6682dbSJay Vosburgh  */
__choose_matched(struct lacpdu * lacpdu,struct port * port)4592d6682dbSJay Vosburgh static void __choose_matched(struct lacpdu *lacpdu, struct port *port)
4602d6682dbSJay Vosburgh {
461815117adSdingtianhong 	/* check if all parameters are alike
462815117adSdingtianhong 	 * or this is individual link(aggregation == FALSE)
463815117adSdingtianhong 	 * then update the state machine Matched variable.
464815117adSdingtianhong 	 */
4652d6682dbSJay Vosburgh 	if (((ntohs(lacpdu->partner_port) == port->actor_port_number) &&
4662d6682dbSJay Vosburgh 	     (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) &&
467815117adSdingtianhong 	     MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) &&
4682d6682dbSJay Vosburgh 	     (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) &&
4692d6682dbSJay Vosburgh 	     (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) &&
470c1e46990SAndy Roulin 	     ((lacpdu->partner_state & LACP_STATE_AGGREGATION) == (port->actor_oper_port_state & LACP_STATE_AGGREGATION))) ||
471c1e46990SAndy Roulin 	    ((lacpdu->actor_state & LACP_STATE_AGGREGATION) == 0)
4722d6682dbSJay Vosburgh 		) {
4732d6682dbSJay Vosburgh 		port->sm_vars |= AD_PORT_MATCHED;
4742d6682dbSJay Vosburgh 	} else {
4752d6682dbSJay Vosburgh 		port->sm_vars &= ~AD_PORT_MATCHED;
4762d6682dbSJay Vosburgh 	}
4772d6682dbSJay Vosburgh }
4782d6682dbSJay Vosburgh 
4792d6682dbSJay Vosburgh /**
4801da177e4SLinus Torvalds  * __record_pdu - record parameters from a received lacpdu
4811da177e4SLinus Torvalds  * @lacpdu: the lacpdu we've received
4821da177e4SLinus Torvalds  * @port: the port we're looking at
4831da177e4SLinus Torvalds  *
4841da177e4SLinus Torvalds  * Record the parameter values for the Actor carried in a received lacpdu as
4851da177e4SLinus Torvalds  * the current partner operational parameter values and sets
4861da177e4SLinus Torvalds  * actor_oper_port_state.defaulted to FALSE.
4871da177e4SLinus Torvalds  */
__record_pdu(struct lacpdu * lacpdu,struct port * port)4881da177e4SLinus Torvalds static void __record_pdu(struct lacpdu *lacpdu, struct port *port)
4891da177e4SLinus Torvalds {
4901da177e4SLinus Torvalds 	if (lacpdu && port) {
491b99d6ba9SHolger Eitzenberger 		struct port_params *partner = &port->partner_oper;
492b99d6ba9SHolger Eitzenberger 
4932d6682dbSJay Vosburgh 		__choose_matched(lacpdu, port);
4943bf2d28aSVeaceslav Falico 		/* record the new parameter values for the partner
4953bf2d28aSVeaceslav Falico 		 * operational
4963bf2d28aSVeaceslav Falico 		 */
497b99d6ba9SHolger Eitzenberger 		partner->port_number = ntohs(lacpdu->actor_port);
498b99d6ba9SHolger Eitzenberger 		partner->port_priority = ntohs(lacpdu->actor_port_priority);
499b99d6ba9SHolger Eitzenberger 		partner->system = lacpdu->actor_system;
500b99d6ba9SHolger Eitzenberger 		partner->system_priority = ntohs(lacpdu->actor_system_priority);
501b99d6ba9SHolger Eitzenberger 		partner->key = ntohs(lacpdu->actor_key);
502b99d6ba9SHolger Eitzenberger 		partner->port_state = lacpdu->actor_state;
5031da177e4SLinus Torvalds 
5043bf2d28aSVeaceslav Falico 		/* set actor_oper_port_state.defaulted to FALSE */
505c1e46990SAndy Roulin 		port->actor_oper_port_state &= ~LACP_STATE_DEFAULTED;
5061da177e4SLinus Torvalds 
5073bf2d28aSVeaceslav Falico 		/* set the partner sync. to on if the partner is sync,
5083bf2d28aSVeaceslav Falico 		 * and the port is matched
5093bf2d28aSVeaceslav Falico 		 */
51063b46242SWilson Kok 		if ((port->sm_vars & AD_PORT_MATCHED) &&
511c1e46990SAndy Roulin 		    (lacpdu->actor_state & LACP_STATE_SYNCHRONIZATION)) {
512c1e46990SAndy Roulin 			partner->port_state |= LACP_STATE_SYNCHRONIZATION;
51317720981SJarod Wilson 			slave_dbg(port->slave->bond->dev, port->slave->dev,
51417720981SJarod Wilson 				  "partner sync=1\n");
51563b46242SWilson Kok 		} else {
516c1e46990SAndy Roulin 			partner->port_state &= ~LACP_STATE_SYNCHRONIZATION;
51717720981SJarod Wilson 			slave_dbg(port->slave->bond->dev, port->slave->dev,
51817720981SJarod Wilson 				  "partner sync=0\n");
51963b46242SWilson Kok 		}
5201da177e4SLinus Torvalds 	}
5211da177e4SLinus Torvalds }
5221da177e4SLinus Torvalds 
5231da177e4SLinus Torvalds /**
5241da177e4SLinus Torvalds  * __record_default - record default parameters
5251da177e4SLinus Torvalds  * @port: the port we're looking at
5261da177e4SLinus Torvalds  *
5271da177e4SLinus Torvalds  * This function records the default parameter values for the partner carried
5281da177e4SLinus Torvalds  * in the Partner Admin parameters as the current partner operational parameter
5291da177e4SLinus Torvalds  * values and sets actor_oper_port_state.defaulted to TRUE.
5301da177e4SLinus Torvalds  */
__record_default(struct port * port)5311da177e4SLinus Torvalds static void __record_default(struct port *port)
5321da177e4SLinus Torvalds {
5331da177e4SLinus Torvalds 	if (port) {
5343bf2d28aSVeaceslav Falico 		/* record the partner admin parameters */
5355eefd1adSHolger Eitzenberger 		memcpy(&port->partner_oper, &port->partner_admin,
5365eefd1adSHolger Eitzenberger 		       sizeof(struct port_params));
5371da177e4SLinus Torvalds 
5383bf2d28aSVeaceslav Falico 		/* set actor_oper_port_state.defaulted to true */
539c1e46990SAndy Roulin 		port->actor_oper_port_state |= LACP_STATE_DEFAULTED;
5401da177e4SLinus Torvalds 	}
5411da177e4SLinus Torvalds }
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds /**
5441da177e4SLinus Torvalds  * __update_selected - update a port's Selected variable from a received lacpdu
5451da177e4SLinus Torvalds  * @lacpdu: the lacpdu we've received
5461da177e4SLinus Torvalds  * @port: the port we're looking at
5471da177e4SLinus Torvalds  *
5481da177e4SLinus Torvalds  * Update the value of the selected variable, using parameter values from a
5491da177e4SLinus Torvalds  * newly received lacpdu. The parameter values for the Actor carried in the
5501da177e4SLinus Torvalds  * received PDU are compared with the corresponding operational parameter
5511da177e4SLinus Torvalds  * values for the ports partner. If one or more of the comparisons shows that
5521da177e4SLinus Torvalds  * the value(s) received in the PDU differ from the current operational values,
5531da177e4SLinus Torvalds  * then selected is set to FALSE and actor_oper_port_state.synchronization is
5541da177e4SLinus Torvalds  * set to out_of_sync. Otherwise, selected remains unchanged.
5551da177e4SLinus Torvalds  */
__update_selected(struct lacpdu * lacpdu,struct port * port)5561da177e4SLinus Torvalds static void __update_selected(struct lacpdu *lacpdu, struct port *port)
5571da177e4SLinus Torvalds {
5581da177e4SLinus Torvalds 	if (lacpdu && port) {
559ce6a49adSHolger Eitzenberger 		const struct port_params *partner = &port->partner_oper;
560ce6a49adSHolger Eitzenberger 
561815117adSdingtianhong 		/* check if any parameter is different then
562815117adSdingtianhong 		 * update the state machine selected variable.
563815117adSdingtianhong 		 */
5648e95a202SJoe Perches 		if (ntohs(lacpdu->actor_port) != partner->port_number ||
5658e95a202SJoe Perches 		    ntohs(lacpdu->actor_port_priority) != partner->port_priority ||
566815117adSdingtianhong 		    !MAC_ADDRESS_EQUAL(&lacpdu->actor_system, &partner->system) ||
5678e95a202SJoe Perches 		    ntohs(lacpdu->actor_system_priority) != partner->system_priority ||
5688e95a202SJoe Perches 		    ntohs(lacpdu->actor_key) != partner->key ||
569c1e46990SAndy Roulin 		    (lacpdu->actor_state & LACP_STATE_AGGREGATION) != (partner->port_state & LACP_STATE_AGGREGATION)) {
5701da177e4SLinus Torvalds 			port->sm_vars &= ~AD_PORT_SELECTED;
5711da177e4SLinus Torvalds 		}
5721da177e4SLinus Torvalds 	}
5731da177e4SLinus Torvalds }
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds /**
5761da177e4SLinus Torvalds  * __update_default_selected - update a port's Selected variable from Partner
5771da177e4SLinus Torvalds  * @port: the port we're looking at
5781da177e4SLinus Torvalds  *
5791da177e4SLinus Torvalds  * This function updates the value of the selected variable, using the partner
5801da177e4SLinus Torvalds  * administrative parameter values. The administrative values are compared with
5811da177e4SLinus Torvalds  * the corresponding operational parameter values for the partner. If one or
5821da177e4SLinus Torvalds  * more of the comparisons shows that the administrative value(s) differ from
5831da177e4SLinus Torvalds  * the current operational values, then Selected is set to FALSE and
5841da177e4SLinus Torvalds  * actor_oper_port_state.synchronization is set to OUT_OF_SYNC. Otherwise,
5851da177e4SLinus Torvalds  * Selected remains unchanged.
5861da177e4SLinus Torvalds  */
__update_default_selected(struct port * port)5871da177e4SLinus Torvalds static void __update_default_selected(struct port *port)
5881da177e4SLinus Torvalds {
5891da177e4SLinus Torvalds 	if (port) {
5903c52065fSHolger Eitzenberger 		const struct port_params *admin = &port->partner_admin;
5913c52065fSHolger Eitzenberger 		const struct port_params *oper = &port->partner_oper;
5923c52065fSHolger Eitzenberger 
593815117adSdingtianhong 		/* check if any parameter is different then
594815117adSdingtianhong 		 * update the state machine selected variable.
595815117adSdingtianhong 		 */
5968e95a202SJoe Perches 		if (admin->port_number != oper->port_number ||
5978e95a202SJoe Perches 		    admin->port_priority != oper->port_priority ||
598815117adSdingtianhong 		    !MAC_ADDRESS_EQUAL(&admin->system, &oper->system) ||
5998e95a202SJoe Perches 		    admin->system_priority != oper->system_priority ||
6008e95a202SJoe Perches 		    admin->key != oper->key ||
601c1e46990SAndy Roulin 		    (admin->port_state & LACP_STATE_AGGREGATION)
602c1e46990SAndy Roulin 			!= (oper->port_state & LACP_STATE_AGGREGATION)) {
6031da177e4SLinus Torvalds 			port->sm_vars &= ~AD_PORT_SELECTED;
6041da177e4SLinus Torvalds 		}
6051da177e4SLinus Torvalds 	}
6061da177e4SLinus Torvalds }
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds /**
6091da177e4SLinus Torvalds  * __update_ntt - update a port's ntt variable from a received lacpdu
6101da177e4SLinus Torvalds  * @lacpdu: the lacpdu we've received
6111da177e4SLinus Torvalds  * @port: the port we're looking at
6121da177e4SLinus Torvalds  *
6131da177e4SLinus Torvalds  * Updates the value of the ntt variable, using parameter values from a newly
6141da177e4SLinus Torvalds  * received lacpdu. The parameter values for the partner carried in the
6151da177e4SLinus Torvalds  * received PDU are compared with the corresponding operational parameter
6161da177e4SLinus Torvalds  * values for the Actor. If one or more of the comparisons shows that the
6171da177e4SLinus Torvalds  * value(s) received in the PDU differ from the current operational values,
6181da177e4SLinus Torvalds  * then ntt is set to TRUE. Otherwise, ntt remains unchanged.
6191da177e4SLinus Torvalds  */
__update_ntt(struct lacpdu * lacpdu,struct port * port)6201da177e4SLinus Torvalds static void __update_ntt(struct lacpdu *lacpdu, struct port *port)
6211da177e4SLinus Torvalds {
622815117adSdingtianhong 	/* validate lacpdu and port */
6231da177e4SLinus Torvalds 	if (lacpdu && port) {
624815117adSdingtianhong 		/* check if any parameter is different then
625815117adSdingtianhong 		 * update the port->ntt.
626815117adSdingtianhong 		 */
62789cc76f9SJay Vosburgh 		if ((ntohs(lacpdu->partner_port) != port->actor_port_number) ||
62889cc76f9SJay Vosburgh 		    (ntohs(lacpdu->partner_port_priority) != port->actor_port_priority) ||
629815117adSdingtianhong 		    !MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) ||
63089cc76f9SJay Vosburgh 		    (ntohs(lacpdu->partner_system_priority) != port->actor_system_priority) ||
63189cc76f9SJay Vosburgh 		    (ntohs(lacpdu->partner_key) != port->actor_oper_port_key) ||
632c1e46990SAndy Roulin 		    ((lacpdu->partner_state & LACP_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & LACP_STATE_LACP_ACTIVITY)) ||
633c1e46990SAndy Roulin 		    ((lacpdu->partner_state & LACP_STATE_LACP_TIMEOUT) != (port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT)) ||
634c1e46990SAndy Roulin 		    ((lacpdu->partner_state & LACP_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) ||
635c1e46990SAndy Roulin 		    ((lacpdu->partner_state & LACP_STATE_AGGREGATION) != (port->actor_oper_port_state & LACP_STATE_AGGREGATION))
6361da177e4SLinus Torvalds 		   ) {
637d238d458SHolger Eitzenberger 			port->ntt = true;
6381da177e4SLinus Torvalds 		}
6391da177e4SLinus Torvalds 	}
6401da177e4SLinus Torvalds }
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds /**
6431da177e4SLinus Torvalds  * __agg_ports_are_ready - check if all ports in an aggregator are ready
6441da177e4SLinus Torvalds  * @aggregator: the aggregator we're looking at
6451da177e4SLinus Torvalds  *
6461da177e4SLinus Torvalds  */
__agg_ports_are_ready(struct aggregator * aggregator)6471da177e4SLinus Torvalds static int __agg_ports_are_ready(struct aggregator *aggregator)
6481da177e4SLinus Torvalds {
6491da177e4SLinus Torvalds 	struct port *port;
6501da177e4SLinus Torvalds 	int retval = 1;
6511da177e4SLinus Torvalds 
6521da177e4SLinus Torvalds 	if (aggregator) {
6533bf2d28aSVeaceslav Falico 		/* scan all ports in this aggregator to verfy if they are
6543bf2d28aSVeaceslav Falico 		 * all ready.
6553bf2d28aSVeaceslav Falico 		 */
656128ea6c3SBandan Das 		for (port = aggregator->lag_ports;
657128ea6c3SBandan Das 		     port;
658128ea6c3SBandan Das 		     port = port->next_port_in_aggregator) {
6591da177e4SLinus Torvalds 			if (!(port->sm_vars & AD_PORT_READY_N)) {
6601da177e4SLinus Torvalds 				retval = 0;
6611da177e4SLinus Torvalds 				break;
6621da177e4SLinus Torvalds 			}
6631da177e4SLinus Torvalds 		}
6641da177e4SLinus Torvalds 	}
6651da177e4SLinus Torvalds 
6661da177e4SLinus Torvalds 	return retval;
6671da177e4SLinus Torvalds }
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds /**
6701da177e4SLinus Torvalds  * __set_agg_ports_ready - set value of Ready bit in all ports of an aggregator
6711da177e4SLinus Torvalds  * @aggregator: the aggregator we're looking at
6721da177e4SLinus Torvalds  * @val: Should the ports' ready bit be set on or off
6731da177e4SLinus Torvalds  *
6741da177e4SLinus Torvalds  */
__set_agg_ports_ready(struct aggregator * aggregator,int val)6751da177e4SLinus Torvalds static void __set_agg_ports_ready(struct aggregator *aggregator, int val)
6761da177e4SLinus Torvalds {
6771da177e4SLinus Torvalds 	struct port *port;
6781da177e4SLinus Torvalds 
679128ea6c3SBandan Das 	for (port = aggregator->lag_ports; port;
680128ea6c3SBandan Das 	     port = port->next_port_in_aggregator) {
6817bfc4753SBandan Das 		if (val)
6821da177e4SLinus Torvalds 			port->sm_vars |= AD_PORT_READY;
6837bfc4753SBandan Das 		else
6841da177e4SLinus Torvalds 			port->sm_vars &= ~AD_PORT_READY;
6851da177e4SLinus Torvalds 	}
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds 
__agg_active_ports(struct aggregator * agg)6880622cab0SJay Vosburgh static int __agg_active_ports(struct aggregator *agg)
6890622cab0SJay Vosburgh {
6900622cab0SJay Vosburgh 	struct port *port;
6910622cab0SJay Vosburgh 	int active = 0;
6920622cab0SJay Vosburgh 
6930622cab0SJay Vosburgh 	for (port = agg->lag_ports; port;
6940622cab0SJay Vosburgh 	     port = port->next_port_in_aggregator) {
6950622cab0SJay Vosburgh 		if (port->is_enabled)
6960622cab0SJay Vosburgh 			active++;
6970622cab0SJay Vosburgh 	}
6980622cab0SJay Vosburgh 
6990622cab0SJay Vosburgh 	return active;
7000622cab0SJay Vosburgh }
7010622cab0SJay Vosburgh 
7021da177e4SLinus Torvalds /**
7031da177e4SLinus Torvalds  * __get_agg_bandwidth - get the total bandwidth of an aggregator
7041da177e4SLinus Torvalds  * @aggregator: the aggregator we're looking at
7051da177e4SLinus Torvalds  *
7061da177e4SLinus Torvalds  */
__get_agg_bandwidth(struct aggregator * aggregator)7071da177e4SLinus Torvalds static u32 __get_agg_bandwidth(struct aggregator *aggregator)
7081da177e4SLinus Torvalds {
7090622cab0SJay Vosburgh 	int nports = __agg_active_ports(aggregator);
7101da177e4SLinus Torvalds 	u32 bandwidth = 0;
7111da177e4SLinus Torvalds 
7120622cab0SJay Vosburgh 	if (nports) {
71365cce19cSDavid Decotigny 		switch (__get_link_speed(aggregator->lag_ports)) {
714cb8dda90SJianhua Xie 		case AD_LINK_SPEED_1MBPS:
7150622cab0SJay Vosburgh 			bandwidth = nports;
7161da177e4SLinus Torvalds 			break;
717cb8dda90SJianhua Xie 		case AD_LINK_SPEED_10MBPS:
7180622cab0SJay Vosburgh 			bandwidth = nports * 10;
7191da177e4SLinus Torvalds 			break;
720cb8dda90SJianhua Xie 		case AD_LINK_SPEED_100MBPS:
7210622cab0SJay Vosburgh 			bandwidth = nports * 100;
7221da177e4SLinus Torvalds 			break;
723cb8dda90SJianhua Xie 		case AD_LINK_SPEED_1000MBPS:
7240622cab0SJay Vosburgh 			bandwidth = nports * 1000;
7251da177e4SLinus Torvalds 			break;
726424c3232SJianhua Xie 		case AD_LINK_SPEED_2500MBPS:
7270622cab0SJay Vosburgh 			bandwidth = nports * 2500;
728424c3232SJianhua Xie 			break;
729c7c55067SThibaut Collet 		case AD_LINK_SPEED_5000MBPS:
730c7c55067SThibaut Collet 			bandwidth = nports * 5000;
731c7c55067SThibaut Collet 			break;
732cb8dda90SJianhua Xie 		case AD_LINK_SPEED_10000MBPS:
7330622cab0SJay Vosburgh 			bandwidth = nports * 10000;
73494dbffd5SJay Vosburgh 			break;
7353fcd64cfSNicolas Dichtel 		case AD_LINK_SPEED_14000MBPS:
7363fcd64cfSNicolas Dichtel 			bandwidth = nports * 14000;
7373fcd64cfSNicolas Dichtel 			break;
738424c3232SJianhua Xie 		case AD_LINK_SPEED_20000MBPS:
7390622cab0SJay Vosburgh 			bandwidth = nports * 20000;
740424c3232SJianhua Xie 			break;
74119ddde1eSJarod Wilson 		case AD_LINK_SPEED_25000MBPS:
74219ddde1eSJarod Wilson 			bandwidth = nports * 25000;
74319ddde1eSJarod Wilson 			break;
744424c3232SJianhua Xie 		case AD_LINK_SPEED_40000MBPS:
7450622cab0SJay Vosburgh 			bandwidth = nports * 40000;
746424c3232SJianhua Xie 			break;
747c7c55067SThibaut Collet 		case AD_LINK_SPEED_50000MBPS:
748c7c55067SThibaut Collet 			bandwidth = nports * 50000;
749c7c55067SThibaut Collet 			break;
750424c3232SJianhua Xie 		case AD_LINK_SPEED_56000MBPS:
7510622cab0SJay Vosburgh 			bandwidth = nports * 56000;
752424c3232SJianhua Xie 			break;
7533952af4dSJiri Pirko 		case AD_LINK_SPEED_100000MBPS:
7540622cab0SJay Vosburgh 			bandwidth = nports * 100000;
7553952af4dSJiri Pirko 			break;
756ab73447cSNikolay Aleksandrov 		case AD_LINK_SPEED_200000MBPS:
757ab73447cSNikolay Aleksandrov 			bandwidth = nports * 200000;
758ab73447cSNikolay Aleksandrov 			break;
759138e3b3cSNikolay Aleksandrov 		case AD_LINK_SPEED_400000MBPS:
760138e3b3cSNikolay Aleksandrov 			bandwidth = nports * 400000;
761138e3b3cSNikolay Aleksandrov 			break;
76241305d37SAmit Cohen 		case AD_LINK_SPEED_800000MBPS:
76341305d37SAmit Cohen 			bandwidth = nports * 800000;
76441305d37SAmit Cohen 			break;
7651da177e4SLinus Torvalds 		default:
7663bf2d28aSVeaceslav Falico 			bandwidth = 0; /* to silence the compiler */
7671da177e4SLinus Torvalds 		}
7681da177e4SLinus Torvalds 	}
7691da177e4SLinus Torvalds 	return bandwidth;
7701da177e4SLinus Torvalds }
7711da177e4SLinus Torvalds 
7721da177e4SLinus Torvalds /**
7731da177e4SLinus Torvalds  * __get_active_agg - get the current active aggregator
7741da177e4SLinus Torvalds  * @aggregator: the aggregator we're looking at
77549b7624eSVeaceslav Falico  *
77649b7624eSVeaceslav Falico  * Caller must hold RCU lock.
7771da177e4SLinus Torvalds  */
__get_active_agg(struct aggregator * aggregator)7781da177e4SLinus Torvalds static struct aggregator *__get_active_agg(struct aggregator *aggregator)
7791da177e4SLinus Torvalds {
78019177e7dSVeaceslav Falico 	struct bonding *bond = aggregator->slave->bond;
78119177e7dSVeaceslav Falico 	struct list_head *iter;
78219177e7dSVeaceslav Falico 	struct slave *slave;
7831da177e4SLinus Torvalds 
784be79bd04Sdingtianhong 	bond_for_each_slave_rcu(bond, slave, iter)
7853fdddd85Sdingtianhong 		if (SLAVE_AD_INFO(slave)->aggregator.is_active)
7863fdddd85Sdingtianhong 			return &(SLAVE_AD_INFO(slave)->aggregator);
7871da177e4SLinus Torvalds 
78819177e7dSVeaceslav Falico 	return NULL;
7891da177e4SLinus Torvalds }
7901da177e4SLinus Torvalds 
7911da177e4SLinus Torvalds /**
7921da177e4SLinus Torvalds  * __update_lacpdu_from_port - update a port's lacpdu fields
7931da177e4SLinus Torvalds  * @port: the port we're looking at
7941da177e4SLinus Torvalds  */
__update_lacpdu_from_port(struct port * port)7951da177e4SLinus Torvalds static inline void __update_lacpdu_from_port(struct port *port)
7961da177e4SLinus Torvalds {
7971da177e4SLinus Torvalds 	struct lacpdu *lacpdu = &port->lacpdu;
7983b5b35d0SHolger Eitzenberger 	const struct port_params *partner = &port->partner_oper;
7991da177e4SLinus Torvalds 
8003bf2d28aSVeaceslav Falico 	/* update current actual Actor parameters
8013bf2d28aSVeaceslav Falico 	 * lacpdu->subtype                   initialized
8021da177e4SLinus Torvalds 	 * lacpdu->version_number            initialized
8031da177e4SLinus Torvalds 	 * lacpdu->tlv_type_actor_info       initialized
8041da177e4SLinus Torvalds 	 * lacpdu->actor_information_length  initialized
8051da177e4SLinus Torvalds 	 */
8061da177e4SLinus Torvalds 
807d3bb52b0SAl Viro 	lacpdu->actor_system_priority = htons(port->actor_system_priority);
8081da177e4SLinus Torvalds 	lacpdu->actor_system = port->actor_system;
809d3bb52b0SAl Viro 	lacpdu->actor_key = htons(port->actor_oper_port_key);
810d3bb52b0SAl Viro 	lacpdu->actor_port_priority = htons(port->actor_port_priority);
811d3bb52b0SAl Viro 	lacpdu->actor_port = htons(port->actor_port_number);
8121da177e4SLinus Torvalds 	lacpdu->actor_state = port->actor_oper_port_state;
81317720981SJarod Wilson 	slave_dbg(port->slave->bond->dev, port->slave->dev,
81417720981SJarod Wilson 		  "update lacpdu: actor port state %x\n",
81517720981SJarod Wilson 		  port->actor_oper_port_state);
8161da177e4SLinus Torvalds 
8171da177e4SLinus Torvalds 	/* lacpdu->reserved_3_1              initialized
8181da177e4SLinus Torvalds 	 * lacpdu->tlv_type_partner_info     initialized
8191da177e4SLinus Torvalds 	 * lacpdu->partner_information_length initialized
8201da177e4SLinus Torvalds 	 */
8211da177e4SLinus Torvalds 
8223b5b35d0SHolger Eitzenberger 	lacpdu->partner_system_priority = htons(partner->system_priority);
8233b5b35d0SHolger Eitzenberger 	lacpdu->partner_system = partner->system;
8243b5b35d0SHolger Eitzenberger 	lacpdu->partner_key = htons(partner->key);
8253b5b35d0SHolger Eitzenberger 	lacpdu->partner_port_priority = htons(partner->port_priority);
8263b5b35d0SHolger Eitzenberger 	lacpdu->partner_port = htons(partner->port_number);
8273b5b35d0SHolger Eitzenberger 	lacpdu->partner_state = partner->port_state;
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds 	/* lacpdu->reserved_3_2              initialized
8301da177e4SLinus Torvalds 	 * lacpdu->tlv_type_collector_info   initialized
8311da177e4SLinus Torvalds 	 * lacpdu->collector_information_length initialized
8321da177e4SLinus Torvalds 	 * collector_max_delay                initialized
8331da177e4SLinus Torvalds 	 * reserved_12[12]                   initialized
8341da177e4SLinus Torvalds 	 * tlv_type_terminator               initialized
8351da177e4SLinus Torvalds 	 * terminator_length                 initialized
8361da177e4SLinus Torvalds 	 * reserved_50[50]                   initialized
8371da177e4SLinus Torvalds 	 */
8381da177e4SLinus Torvalds }
8391da177e4SLinus Torvalds 
8403bf2d28aSVeaceslav Falico /* ================= main 802.3ad protocol code ========================= */
8411da177e4SLinus Torvalds 
8421da177e4SLinus Torvalds /**
8431da177e4SLinus Torvalds  * ad_lacpdu_send - send out a lacpdu packet on a given port
8441da177e4SLinus Torvalds  * @port: the port we're looking at
8451da177e4SLinus Torvalds  *
8461da177e4SLinus Torvalds  * Returns:   0 on success
8471da177e4SLinus Torvalds  *          < 0 on error
8481da177e4SLinus Torvalds  */
ad_lacpdu_send(struct port * port)8491da177e4SLinus Torvalds static int ad_lacpdu_send(struct port *port)
8501da177e4SLinus Torvalds {
8511da177e4SLinus Torvalds 	struct slave *slave = port->slave;
8521da177e4SLinus Torvalds 	struct sk_buff *skb;
8531da177e4SLinus Torvalds 	struct lacpdu_header *lacpdu_header;
8541da177e4SLinus Torvalds 	int length = sizeof(struct lacpdu_header);
8551da177e4SLinus Torvalds 
8561da177e4SLinus Torvalds 	skb = dev_alloc_skb(length);
8577bfc4753SBandan Das 	if (!skb)
8581da177e4SLinus Torvalds 		return -ENOMEM;
8591da177e4SLinus Torvalds 
860267c095aSNikolay Aleksandrov 	atomic64_inc(&SLAVE_AD_INFO(slave)->stats.lacpdu_tx);
861949e7ceaSNikolay Aleksandrov 	atomic64_inc(&BOND_AD_INFO(slave->bond).stats.lacpdu_tx);
862267c095aSNikolay Aleksandrov 
8631da177e4SLinus Torvalds 	skb->dev = slave->dev;
864459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
865b0e380b1SArnaldo Carvalho de Melo 	skb->network_header = skb->mac_header + ETH_HLEN;
8661da177e4SLinus Torvalds 	skb->protocol = PKT_TYPE_LACPDU;
8671da177e4SLinus Torvalds 	skb->priority = TC_PRIO_CONTROL;
8681da177e4SLinus Torvalds 
8694df864c1SJohannes Berg 	lacpdu_header = skb_put(skb, length);
8701da177e4SLinus Torvalds 
871ada0f863SJoe Perches 	ether_addr_copy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr);
872b595076aSUwe Kleine-König 	/* Note: source address is set to be the member's PERMANENT address,
8733bf2d28aSVeaceslav Falico 	 * because we use it to identify loopback lacpdus in receive.
8743bf2d28aSVeaceslav Falico 	 */
875ada0f863SJoe Perches 	ether_addr_copy(lacpdu_header->hdr.h_source, slave->perm_hwaddr);
876e727149eSHolger Eitzenberger 	lacpdu_header->hdr.h_proto = PKT_TYPE_LACPDU;
8771da177e4SLinus Torvalds 
8783bf2d28aSVeaceslav Falico 	lacpdu_header->lacpdu = port->lacpdu;
8791da177e4SLinus Torvalds 
8801da177e4SLinus Torvalds 	dev_queue_xmit(skb);
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 	return 0;
8831da177e4SLinus Torvalds }
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds /**
8861da177e4SLinus Torvalds  * ad_marker_send - send marker information/response on a given port
8871da177e4SLinus Torvalds  * @port: the port we're looking at
8881da177e4SLinus Torvalds  * @marker: marker data to send
8891da177e4SLinus Torvalds  *
8901da177e4SLinus Torvalds  * Returns:   0 on success
8911da177e4SLinus Torvalds  *          < 0 on error
8921da177e4SLinus Torvalds  */
ad_marker_send(struct port * port,struct bond_marker * marker)8931c3f0b8eSMathieu Desnoyers static int ad_marker_send(struct port *port, struct bond_marker *marker)
8941da177e4SLinus Torvalds {
8951da177e4SLinus Torvalds 	struct slave *slave = port->slave;
8961da177e4SLinus Torvalds 	struct sk_buff *skb;
8971c3f0b8eSMathieu Desnoyers 	struct bond_marker_header *marker_header;
8981c3f0b8eSMathieu Desnoyers 	int length = sizeof(struct bond_marker_header);
8991da177e4SLinus Torvalds 
9001da177e4SLinus Torvalds 	skb = dev_alloc_skb(length + 16);
9017bfc4753SBandan Das 	if (!skb)
9021da177e4SLinus Torvalds 		return -ENOMEM;
9031da177e4SLinus Torvalds 
904267c095aSNikolay Aleksandrov 	switch (marker->tlv_type) {
905267c095aSNikolay Aleksandrov 	case AD_MARKER_INFORMATION_SUBTYPE:
906267c095aSNikolay Aleksandrov 		atomic64_inc(&SLAVE_AD_INFO(slave)->stats.marker_tx);
907949e7ceaSNikolay Aleksandrov 		atomic64_inc(&BOND_AD_INFO(slave->bond).stats.marker_tx);
908267c095aSNikolay Aleksandrov 		break;
909267c095aSNikolay Aleksandrov 	case AD_MARKER_RESPONSE_SUBTYPE:
910267c095aSNikolay Aleksandrov 		atomic64_inc(&SLAVE_AD_INFO(slave)->stats.marker_resp_tx);
911949e7ceaSNikolay Aleksandrov 		atomic64_inc(&BOND_AD_INFO(slave->bond).stats.marker_resp_tx);
912267c095aSNikolay Aleksandrov 		break;
913267c095aSNikolay Aleksandrov 	}
914267c095aSNikolay Aleksandrov 
9151da177e4SLinus Torvalds 	skb_reserve(skb, 16);
9161da177e4SLinus Torvalds 
9171da177e4SLinus Torvalds 	skb->dev = slave->dev;
918459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
919b0e380b1SArnaldo Carvalho de Melo 	skb->network_header = skb->mac_header + ETH_HLEN;
9201da177e4SLinus Torvalds 	skb->protocol = PKT_TYPE_LACPDU;
9211da177e4SLinus Torvalds 
9224df864c1SJohannes Berg 	marker_header = skb_put(skb, length);
9231da177e4SLinus Torvalds 
924ada0f863SJoe Perches 	ether_addr_copy(marker_header->hdr.h_dest, lacpdu_mcast_addr);
925b595076aSUwe Kleine-König 	/* Note: source address is set to be the member's PERMANENT address,
9263bf2d28aSVeaceslav Falico 	 * because we use it to identify loopback MARKERs in receive.
9273bf2d28aSVeaceslav Falico 	 */
928ada0f863SJoe Perches 	ether_addr_copy(marker_header->hdr.h_source, slave->perm_hwaddr);
929e727149eSHolger Eitzenberger 	marker_header->hdr.h_proto = PKT_TYPE_LACPDU;
9301da177e4SLinus Torvalds 
9313bf2d28aSVeaceslav Falico 	marker_header->marker = *marker;
9321da177e4SLinus Torvalds 
9331da177e4SLinus Torvalds 	dev_queue_xmit(skb);
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds 	return 0;
9361da177e4SLinus Torvalds }
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds /**
9391da177e4SLinus Torvalds  * ad_mux_machine - handle a port's mux state machine
9401da177e4SLinus Torvalds  * @port: the port we're looking at
941ee637714SMahesh Bandewar  * @update_slave_arr: Does slave array need update?
9421da177e4SLinus Torvalds  */
ad_mux_machine(struct port * port,bool * update_slave_arr)943ee637714SMahesh Bandewar static void ad_mux_machine(struct port *port, bool *update_slave_arr)
9441da177e4SLinus Torvalds {
9451da177e4SLinus Torvalds 	mux_states_t last_state;
9461da177e4SLinus Torvalds 
9473bf2d28aSVeaceslav Falico 	/* keep current State Machine state to compare later if it was
9483bf2d28aSVeaceslav Falico 	 * changed
9493bf2d28aSVeaceslav Falico 	 */
9501da177e4SLinus Torvalds 	last_state = port->sm_mux_state;
9511da177e4SLinus Torvalds 
9521da177e4SLinus Torvalds 	if (port->sm_vars & AD_PORT_BEGIN) {
9533bf2d28aSVeaceslav Falico 		port->sm_mux_state = AD_MUX_DETACHED;
9541da177e4SLinus Torvalds 	} else {
9551da177e4SLinus Torvalds 		switch (port->sm_mux_state) {
9561da177e4SLinus Torvalds 		case AD_MUX_DETACHED:
9577bfc4753SBandan Das 			if ((port->sm_vars & AD_PORT_SELECTED)
9587bfc4753SBandan Das 			    || (port->sm_vars & AD_PORT_STANDBY))
9597bfc4753SBandan Das 				/* if SELECTED or STANDBY */
9603bf2d28aSVeaceslav Falico 				port->sm_mux_state = AD_MUX_WAITING;
9611da177e4SLinus Torvalds 			break;
9621da177e4SLinus Torvalds 		case AD_MUX_WAITING:
9633bf2d28aSVeaceslav Falico 			/* if SELECTED == FALSE return to DETACH state */
9643bf2d28aSVeaceslav Falico 			if (!(port->sm_vars & AD_PORT_SELECTED)) {
9651da177e4SLinus Torvalds 				port->sm_vars &= ~AD_PORT_READY_N;
9663bf2d28aSVeaceslav Falico 				/* in order to withhold the Selection Logic to
9673bf2d28aSVeaceslav Falico 				 * check all ports READY_N value every callback
9683bf2d28aSVeaceslav Falico 				 * cycle to update ready variable, we check
9693bf2d28aSVeaceslav Falico 				 * READY_N and update READY here
9703bf2d28aSVeaceslav Falico 				 */
9711da177e4SLinus Torvalds 				__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
9723bf2d28aSVeaceslav Falico 				port->sm_mux_state = AD_MUX_DETACHED;
9731da177e4SLinus Torvalds 				break;
9741da177e4SLinus Torvalds 			}
9751da177e4SLinus Torvalds 
9763bf2d28aSVeaceslav Falico 			/* check if the wait_while_timer expired */
9777bfc4753SBandan Das 			if (port->sm_mux_timer_counter
9787bfc4753SBandan Das 			    && !(--port->sm_mux_timer_counter))
9791da177e4SLinus Torvalds 				port->sm_vars |= AD_PORT_READY_N;
9801da177e4SLinus Torvalds 
9813bf2d28aSVeaceslav Falico 			/* in order to withhold the selection logic to check
9823bf2d28aSVeaceslav Falico 			 * all ports READY_N value every callback cycle to
9833bf2d28aSVeaceslav Falico 			 * update ready variable, we check READY_N and update
9843bf2d28aSVeaceslav Falico 			 * READY here
9853bf2d28aSVeaceslav Falico 			 */
9861da177e4SLinus Torvalds 			__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
9871da177e4SLinus Torvalds 
9883bf2d28aSVeaceslav Falico 			/* if the wait_while_timer expired, and the port is
9893bf2d28aSVeaceslav Falico 			 * in READY state, move to ATTACHED state
9903bf2d28aSVeaceslav Falico 			 */
9917bfc4753SBandan Das 			if ((port->sm_vars & AD_PORT_READY)
9927bfc4753SBandan Das 			    && !port->sm_mux_timer_counter)
9933bf2d28aSVeaceslav Falico 				port->sm_mux_state = AD_MUX_ATTACHED;
9941da177e4SLinus Torvalds 			break;
9951da177e4SLinus Torvalds 		case AD_MUX_ATTACHED:
9963bf2d28aSVeaceslav Falico 			/* check also if agg_select_timer expired (so the
9973bf2d28aSVeaceslav Falico 			 * edable port will take place only after this timer)
9983bf2d28aSVeaceslav Falico 			 */
9993bf2d28aSVeaceslav Falico 			if ((port->sm_vars & AD_PORT_SELECTED) &&
1000c1e46990SAndy Roulin 			    (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) &&
10013bf2d28aSVeaceslav Falico 			    !__check_agg_selection_timer(port)) {
100263b46242SWilson Kok 				if (port->aggregator->is_active)
100363b46242SWilson Kok 					port->sm_mux_state =
100463b46242SWilson Kok 					    AD_MUX_COLLECTING_DISTRIBUTING;
10053bf2d28aSVeaceslav Falico 			} else if (!(port->sm_vars & AD_PORT_SELECTED) ||
10063bf2d28aSVeaceslav Falico 				   (port->sm_vars & AD_PORT_STANDBY)) {
10073bf2d28aSVeaceslav Falico 				/* if UNSELECTED or STANDBY */
10081da177e4SLinus Torvalds 				port->sm_vars &= ~AD_PORT_READY_N;
10093bf2d28aSVeaceslav Falico 				/* in order to withhold the selection logic to
10103bf2d28aSVeaceslav Falico 				 * check all ports READY_N value every callback
10113bf2d28aSVeaceslav Falico 				 * cycle to update ready variable, we check
10123bf2d28aSVeaceslav Falico 				 * READY_N and update READY here
10133bf2d28aSVeaceslav Falico 				 */
10141da177e4SLinus Torvalds 				__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
10153bf2d28aSVeaceslav Falico 				port->sm_mux_state = AD_MUX_DETACHED;
101663b46242SWilson Kok 			} else if (port->aggregator->is_active) {
101763b46242SWilson Kok 				port->actor_oper_port_state |=
1018c1e46990SAndy Roulin 				    LACP_STATE_SYNCHRONIZATION;
10191da177e4SLinus Torvalds 			}
10201da177e4SLinus Torvalds 			break;
10211da177e4SLinus Torvalds 		case AD_MUX_COLLECTING_DISTRIBUTING:
10223bf2d28aSVeaceslav Falico 			if (!(port->sm_vars & AD_PORT_SELECTED) ||
10233bf2d28aSVeaceslav Falico 			    (port->sm_vars & AD_PORT_STANDBY) ||
1024c1e46990SAndy Roulin 			    !(port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) ||
1025c1e46990SAndy Roulin 			    !(port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) {
10263bf2d28aSVeaceslav Falico 				port->sm_mux_state = AD_MUX_ATTACHED;
10271da177e4SLinus Torvalds 			} else {
10283bf2d28aSVeaceslav Falico 				/* if port state hasn't changed make
10293bf2d28aSVeaceslav Falico 				 * sure that a collecting distributing
10303bf2d28aSVeaceslav Falico 				 * port in an active aggregator is enabled
10313bf2d28aSVeaceslav Falico 				 */
10321da177e4SLinus Torvalds 				if (port->aggregator &&
10331da177e4SLinus Torvalds 				    port->aggregator->is_active &&
10341da177e4SLinus Torvalds 				    !__port_is_enabled(port)) {
10351da177e4SLinus Torvalds 					__enable_port(port);
103623de0d7bSMahesh Bandewar 					*update_slave_arr = true;
10371da177e4SLinus Torvalds 				}
10381da177e4SLinus Torvalds 			}
10391da177e4SLinus Torvalds 			break;
10403bf2d28aSVeaceslav Falico 		default:
10411da177e4SLinus Torvalds 			break;
10421da177e4SLinus Torvalds 		}
10431da177e4SLinus Torvalds 	}
10441da177e4SLinus Torvalds 
10453bf2d28aSVeaceslav Falico 	/* check if the state machine was changed */
10461da177e4SLinus Torvalds 	if (port->sm_mux_state != last_state) {
104717720981SJarod Wilson 		slave_dbg(port->slave->bond->dev, port->slave->dev,
104817720981SJarod Wilson 			  "Mux Machine: Port=%d, Last State=%d, Curr State=%d\n",
104963b46242SWilson Kok 			  port->actor_port_number,
105063b46242SWilson Kok 			  last_state,
1051a4aee5c8SJoe Perches 			  port->sm_mux_state);
10521da177e4SLinus Torvalds 		switch (port->sm_mux_state) {
10531da177e4SLinus Torvalds 		case AD_MUX_DETACHED:
1054c1e46990SAndy Roulin 			port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION;
1055ee637714SMahesh Bandewar 			ad_disable_collecting_distributing(port,
1056ee637714SMahesh Bandewar 							   update_slave_arr);
1057c1e46990SAndy Roulin 			port->actor_oper_port_state &= ~LACP_STATE_COLLECTING;
1058c1e46990SAndy Roulin 			port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING;
1059d238d458SHolger Eitzenberger 			port->ntt = true;
10601da177e4SLinus Torvalds 			break;
10611da177e4SLinus Torvalds 		case AD_MUX_WAITING:
10621da177e4SLinus Torvalds 			port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0);
10631da177e4SLinus Torvalds 			break;
10641da177e4SLinus Torvalds 		case AD_MUX_ATTACHED:
106563b46242SWilson Kok 			if (port->aggregator->is_active)
106663b46242SWilson Kok 				port->actor_oper_port_state |=
1067c1e46990SAndy Roulin 				    LACP_STATE_SYNCHRONIZATION;
106863b46242SWilson Kok 			else
106963b46242SWilson Kok 				port->actor_oper_port_state &=
1070c1e46990SAndy Roulin 				    ~LACP_STATE_SYNCHRONIZATION;
1071c1e46990SAndy Roulin 			port->actor_oper_port_state &= ~LACP_STATE_COLLECTING;
1072c1e46990SAndy Roulin 			port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING;
1073ee637714SMahesh Bandewar 			ad_disable_collecting_distributing(port,
1074ee637714SMahesh Bandewar 							   update_slave_arr);
1075d238d458SHolger Eitzenberger 			port->ntt = true;
10761da177e4SLinus Torvalds 			break;
10771da177e4SLinus Torvalds 		case AD_MUX_COLLECTING_DISTRIBUTING:
1078c1e46990SAndy Roulin 			port->actor_oper_port_state |= LACP_STATE_COLLECTING;
1079c1e46990SAndy Roulin 			port->actor_oper_port_state |= LACP_STATE_DISTRIBUTING;
1080c1e46990SAndy Roulin 			port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION;
1081ee637714SMahesh Bandewar 			ad_enable_collecting_distributing(port,
1082ee637714SMahesh Bandewar 							  update_slave_arr);
1083d238d458SHolger Eitzenberger 			port->ntt = true;
10841da177e4SLinus Torvalds 			break;
10853bf2d28aSVeaceslav Falico 		default:
10861da177e4SLinus Torvalds 			break;
10871da177e4SLinus Torvalds 		}
10881da177e4SLinus Torvalds 	}
10891da177e4SLinus Torvalds }
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds /**
10921da177e4SLinus Torvalds  * ad_rx_machine - handle a port's rx State Machine
10931da177e4SLinus Torvalds  * @lacpdu: the lacpdu we've received
10941da177e4SLinus Torvalds  * @port: the port we're looking at
10951da177e4SLinus Torvalds  *
10961da177e4SLinus Torvalds  * If lacpdu arrived, stop previous timer (if exists) and set the next state as
10971da177e4SLinus Torvalds  * CURRENT. If timer expired set the state machine in the proper state.
10981da177e4SLinus Torvalds  * In other cases, this function checks if we need to switch to other state.
10991da177e4SLinus Torvalds  */
ad_rx_machine(struct lacpdu * lacpdu,struct port * port)11001da177e4SLinus Torvalds static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
11011da177e4SLinus Torvalds {
11021da177e4SLinus Torvalds 	rx_states_t last_state;
11031da177e4SLinus Torvalds 
11043bf2d28aSVeaceslav Falico 	/* keep current State Machine state to compare later if it was
11053bf2d28aSVeaceslav Falico 	 * changed
11063bf2d28aSVeaceslav Falico 	 */
11071da177e4SLinus Torvalds 	last_state = port->sm_rx_state;
11081da177e4SLinus Torvalds 
1109949e7ceaSNikolay Aleksandrov 	if (lacpdu) {
1110267c095aSNikolay Aleksandrov 		atomic64_inc(&SLAVE_AD_INFO(port->slave)->stats.lacpdu_rx);
1111949e7ceaSNikolay Aleksandrov 		atomic64_inc(&BOND_AD_INFO(port->slave->bond).stats.lacpdu_rx);
1112949e7ceaSNikolay Aleksandrov 	}
11133bf2d28aSVeaceslav Falico 	/* check if state machine should change state */
11143bf2d28aSVeaceslav Falico 
11153bf2d28aSVeaceslav Falico 	/* first, check if port was reinitialized */
111614c9551aSMahesh Bandewar 	if (port->sm_vars & AD_PORT_BEGIN) {
11177bfc4753SBandan Das 		port->sm_rx_state = AD_RX_INITIALIZE;
1118ef015d72SMahesh Bandewar 		port->sm_vars |= AD_PORT_CHURNED;
11193bf2d28aSVeaceslav Falico 	/* check if port is not enabled */
1120ec891c8bSMahesh Bandewar 	} else if (!(port->sm_vars & AD_PORT_BEGIN) && !port->is_enabled)
11217bfc4753SBandan Das 		port->sm_rx_state = AD_RX_PORT_DISABLED;
11223bf2d28aSVeaceslav Falico 	/* check if new lacpdu arrived */
11233bf2d28aSVeaceslav Falico 	else if (lacpdu && ((port->sm_rx_state == AD_RX_EXPIRED) ||
11243bf2d28aSVeaceslav Falico 		 (port->sm_rx_state == AD_RX_DEFAULTED) ||
11253bf2d28aSVeaceslav Falico 		 (port->sm_rx_state == AD_RX_CURRENT))) {
112614c9551aSMahesh Bandewar 		if (port->sm_rx_state != AD_RX_CURRENT)
1127ef015d72SMahesh Bandewar 			port->sm_vars |= AD_PORT_CHURNED;
11283bf2d28aSVeaceslav Falico 		port->sm_rx_timer_counter = 0;
11291da177e4SLinus Torvalds 		port->sm_rx_state = AD_RX_CURRENT;
11301da177e4SLinus Torvalds 	} else {
11313bf2d28aSVeaceslav Falico 		/* if timer is on, and if it is expired */
11323bf2d28aSVeaceslav Falico 		if (port->sm_rx_timer_counter &&
11333bf2d28aSVeaceslav Falico 		    !(--port->sm_rx_timer_counter)) {
11341da177e4SLinus Torvalds 			switch (port->sm_rx_state) {
11351da177e4SLinus Torvalds 			case AD_RX_EXPIRED:
11363bf2d28aSVeaceslav Falico 				port->sm_rx_state = AD_RX_DEFAULTED;
11371da177e4SLinus Torvalds 				break;
11381da177e4SLinus Torvalds 			case AD_RX_CURRENT:
11393bf2d28aSVeaceslav Falico 				port->sm_rx_state = AD_RX_EXPIRED;
11401da177e4SLinus Torvalds 				break;
11413bf2d28aSVeaceslav Falico 			default:
11421da177e4SLinus Torvalds 				break;
11431da177e4SLinus Torvalds 			}
11441da177e4SLinus Torvalds 		} else {
11453bf2d28aSVeaceslav Falico 			/* if no lacpdu arrived and no timer is on */
11461da177e4SLinus Torvalds 			switch (port->sm_rx_state) {
11471da177e4SLinus Torvalds 			case AD_RX_PORT_DISABLED:
1148ec891c8bSMahesh Bandewar 				if (port->is_enabled &&
1149ec891c8bSMahesh Bandewar 				    (port->sm_vars & AD_PORT_LACP_ENABLED))
11503bf2d28aSVeaceslav Falico 					port->sm_rx_state = AD_RX_EXPIRED;
11517bfc4753SBandan Das 				else if (port->is_enabled
11527bfc4753SBandan Das 					 && ((port->sm_vars
11537bfc4753SBandan Das 					      & AD_PORT_LACP_ENABLED) == 0))
11543bf2d28aSVeaceslav Falico 					port->sm_rx_state = AD_RX_LACP_DISABLED;
11551da177e4SLinus Torvalds 				break;
11563bf2d28aSVeaceslav Falico 			default:
11571da177e4SLinus Torvalds 				break;
11581da177e4SLinus Torvalds 
11591da177e4SLinus Torvalds 			}
11601da177e4SLinus Torvalds 		}
11611da177e4SLinus Torvalds 	}
11621da177e4SLinus Torvalds 
11633bf2d28aSVeaceslav Falico 	/* check if the State machine was changed or new lacpdu arrived */
11641da177e4SLinus Torvalds 	if ((port->sm_rx_state != last_state) || (lacpdu)) {
116517720981SJarod Wilson 		slave_dbg(port->slave->bond->dev, port->slave->dev,
116617720981SJarod Wilson 			  "Rx Machine: Port=%d, Last State=%d, Curr State=%d\n",
116763b46242SWilson Kok 			  port->actor_port_number,
116863b46242SWilson Kok 			  last_state,
1169a4aee5c8SJoe Perches 			  port->sm_rx_state);
11701da177e4SLinus Torvalds 		switch (port->sm_rx_state) {
11711da177e4SLinus Torvalds 		case AD_RX_INITIALIZE:
1172cb8dda90SJianhua Xie 			if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS))
11731da177e4SLinus Torvalds 				port->sm_vars &= ~AD_PORT_LACP_ENABLED;
11747bfc4753SBandan Das 			else
11751da177e4SLinus Torvalds 				port->sm_vars |= AD_PORT_LACP_ENABLED;
11761da177e4SLinus Torvalds 			port->sm_vars &= ~AD_PORT_SELECTED;
11771da177e4SLinus Torvalds 			__record_default(port);
1178c1e46990SAndy Roulin 			port->actor_oper_port_state &= ~LACP_STATE_EXPIRED;
11793bf2d28aSVeaceslav Falico 			port->sm_rx_state = AD_RX_PORT_DISABLED;
11801da177e4SLinus Torvalds 
1181df561f66SGustavo A. R. Silva 			fallthrough;
11821da177e4SLinus Torvalds 		case AD_RX_PORT_DISABLED:
11831da177e4SLinus Torvalds 			port->sm_vars &= ~AD_PORT_MATCHED;
11841da177e4SLinus Torvalds 			break;
11851da177e4SLinus Torvalds 		case AD_RX_LACP_DISABLED:
11861da177e4SLinus Torvalds 			port->sm_vars &= ~AD_PORT_SELECTED;
11871da177e4SLinus Torvalds 			__record_default(port);
1188c1e46990SAndy Roulin 			port->partner_oper.port_state &= ~LACP_STATE_AGGREGATION;
11891da177e4SLinus Torvalds 			port->sm_vars |= AD_PORT_MATCHED;
1190c1e46990SAndy Roulin 			port->actor_oper_port_state &= ~LACP_STATE_EXPIRED;
11911da177e4SLinus Torvalds 			break;
11921da177e4SLinus Torvalds 		case AD_RX_EXPIRED:
11933bf2d28aSVeaceslav Falico 			/* Reset of the Synchronization flag (Standard 43.4.12)
11943bf2d28aSVeaceslav Falico 			 * This reset cause to disable this port in the
11953bf2d28aSVeaceslav Falico 			 * COLLECTING_DISTRIBUTING state of the mux machine in
11963bf2d28aSVeaceslav Falico 			 * case of EXPIRED even if LINK_DOWN didn't arrive for
11973bf2d28aSVeaceslav Falico 			 * the port.
11983bf2d28aSVeaceslav Falico 			 */
1199c1e46990SAndy Roulin 			port->partner_oper.port_state &= ~LACP_STATE_SYNCHRONIZATION;
12001da177e4SLinus Torvalds 			port->sm_vars &= ~AD_PORT_MATCHED;
1201c1e46990SAndy Roulin 			port->partner_oper.port_state |= LACP_STATE_LACP_TIMEOUT;
1202c1e46990SAndy Roulin 			port->partner_oper.port_state |= LACP_STATE_LACP_ACTIVITY;
12031da177e4SLinus Torvalds 			port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(AD_SHORT_TIMEOUT));
1204c1e46990SAndy Roulin 			port->actor_oper_port_state |= LACP_STATE_EXPIRED;
1205ef015d72SMahesh Bandewar 			port->sm_vars |= AD_PORT_CHURNED;
12061da177e4SLinus Torvalds 			break;
12071da177e4SLinus Torvalds 		case AD_RX_DEFAULTED:
12081da177e4SLinus Torvalds 			__update_default_selected(port);
12091da177e4SLinus Torvalds 			__record_default(port);
12101da177e4SLinus Torvalds 			port->sm_vars |= AD_PORT_MATCHED;
1211c1e46990SAndy Roulin 			port->actor_oper_port_state &= ~LACP_STATE_EXPIRED;
12121da177e4SLinus Torvalds 			break;
12131da177e4SLinus Torvalds 		case AD_RX_CURRENT:
1214815117adSdingtianhong 			/* detect loopback situation */
12153bf2d28aSVeaceslav Falico 			if (MAC_ADDRESS_EQUAL(&(lacpdu->actor_system),
12163bf2d28aSVeaceslav Falico 					      &(port->actor_system))) {
121717720981SJarod Wilson 				slave_err(port->slave->bond->dev, port->slave->dev, "An illegal loopback occurred on slave\n"
121817720981SJarod Wilson 					  "Check the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n");
12191da177e4SLinus Torvalds 				return;
12201da177e4SLinus Torvalds 			}
12211da177e4SLinus Torvalds 			__update_selected(lacpdu, port);
12221da177e4SLinus Torvalds 			__update_ntt(lacpdu, port);
12231da177e4SLinus Torvalds 			__record_pdu(lacpdu, port);
1224c1e46990SAndy Roulin 			port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT));
1225c1e46990SAndy Roulin 			port->actor_oper_port_state &= ~LACP_STATE_EXPIRED;
12261da177e4SLinus Torvalds 			break;
12273bf2d28aSVeaceslav Falico 		default:
12281da177e4SLinus Torvalds 			break;
12291da177e4SLinus Torvalds 		}
12301da177e4SLinus Torvalds 	}
12311da177e4SLinus Torvalds }
12321da177e4SLinus Torvalds 
12331da177e4SLinus Torvalds /**
123414c9551aSMahesh Bandewar  * ad_churn_machine - handle port churn's state machine
123514c9551aSMahesh Bandewar  * @port: the port we're looking at
123614c9551aSMahesh Bandewar  *
123714c9551aSMahesh Bandewar  */
ad_churn_machine(struct port * port)123814c9551aSMahesh Bandewar static void ad_churn_machine(struct port *port)
123914c9551aSMahesh Bandewar {
1240ef015d72SMahesh Bandewar 	if (port->sm_vars & AD_PORT_CHURNED) {
1241ef015d72SMahesh Bandewar 		port->sm_vars &= ~AD_PORT_CHURNED;
124214c9551aSMahesh Bandewar 		port->sm_churn_actor_state = AD_CHURN_MONITOR;
124314c9551aSMahesh Bandewar 		port->sm_churn_partner_state = AD_CHURN_MONITOR;
124414c9551aSMahesh Bandewar 		port->sm_churn_actor_timer_counter =
124514c9551aSMahesh Bandewar 			__ad_timer_to_ticks(AD_ACTOR_CHURN_TIMER, 0);
124614c9551aSMahesh Bandewar 		port->sm_churn_partner_timer_counter =
124714c9551aSMahesh Bandewar 			 __ad_timer_to_ticks(AD_PARTNER_CHURN_TIMER, 0);
124814c9551aSMahesh Bandewar 		return;
124914c9551aSMahesh Bandewar 	}
125014c9551aSMahesh Bandewar 	if (port->sm_churn_actor_timer_counter &&
125114c9551aSMahesh Bandewar 	    !(--port->sm_churn_actor_timer_counter) &&
125214c9551aSMahesh Bandewar 	    port->sm_churn_actor_state == AD_CHURN_MONITOR) {
1253c1e46990SAndy Roulin 		if (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION) {
125414c9551aSMahesh Bandewar 			port->sm_churn_actor_state = AD_NO_CHURN;
125514c9551aSMahesh Bandewar 		} else {
125614c9551aSMahesh Bandewar 			port->churn_actor_count++;
125714c9551aSMahesh Bandewar 			port->sm_churn_actor_state = AD_CHURN;
125814c9551aSMahesh Bandewar 		}
125914c9551aSMahesh Bandewar 	}
126014c9551aSMahesh Bandewar 	if (port->sm_churn_partner_timer_counter &&
126114c9551aSMahesh Bandewar 	    !(--port->sm_churn_partner_timer_counter) &&
126214c9551aSMahesh Bandewar 	    port->sm_churn_partner_state == AD_CHURN_MONITOR) {
1263c1e46990SAndy Roulin 		if (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) {
126414c9551aSMahesh Bandewar 			port->sm_churn_partner_state = AD_NO_CHURN;
126514c9551aSMahesh Bandewar 		} else {
126614c9551aSMahesh Bandewar 			port->churn_partner_count++;
126714c9551aSMahesh Bandewar 			port->sm_churn_partner_state = AD_CHURN;
126814c9551aSMahesh Bandewar 		}
126914c9551aSMahesh Bandewar 	}
127014c9551aSMahesh Bandewar }
127114c9551aSMahesh Bandewar 
127214c9551aSMahesh Bandewar /**
12731da177e4SLinus Torvalds  * ad_tx_machine - handle a port's tx state machine
12741da177e4SLinus Torvalds  * @port: the port we're looking at
12751da177e4SLinus Torvalds  */
ad_tx_machine(struct port * port)12761da177e4SLinus Torvalds static void ad_tx_machine(struct port *port)
12771da177e4SLinus Torvalds {
12783bf2d28aSVeaceslav Falico 	/* check if tx timer expired, to verify that we do not send more than
12793bf2d28aSVeaceslav Falico 	 * 3 packets per second
12803bf2d28aSVeaceslav Falico 	 */
12811da177e4SLinus Torvalds 	if (port->sm_tx_timer_counter && !(--port->sm_tx_timer_counter)) {
12823bf2d28aSVeaceslav Falico 		/* check if there is something to send */
12831da177e4SLinus Torvalds 		if (port->ntt && (port->sm_vars & AD_PORT_LACP_ENABLED)) {
12841da177e4SLinus Torvalds 			__update_lacpdu_from_port(port);
1285d238d458SHolger Eitzenberger 
12861da177e4SLinus Torvalds 			if (ad_lacpdu_send(port) >= 0) {
128717720981SJarod Wilson 				slave_dbg(port->slave->bond->dev,
128817720981SJarod Wilson 					  port->slave->dev,
128917720981SJarod Wilson 					  "Sent LACPDU on port %d\n",
1290a4aee5c8SJoe Perches 					  port->actor_port_number);
1291d238d458SHolger Eitzenberger 
12923bf2d28aSVeaceslav Falico 				/* mark ntt as false, so it will not be sent
12933bf2d28aSVeaceslav Falico 				 * again until demanded
12943bf2d28aSVeaceslav Falico 				 */
1295d238d458SHolger Eitzenberger 				port->ntt = false;
12961da177e4SLinus Torvalds 			}
12971da177e4SLinus Torvalds 		}
12983bf2d28aSVeaceslav Falico 		/* restart tx timer(to verify that we will not exceed
12993bf2d28aSVeaceslav Falico 		 * AD_MAX_TX_IN_SECOND
13003bf2d28aSVeaceslav Falico 		 */
13013bf2d28aSVeaceslav Falico 		port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND;
13021da177e4SLinus Torvalds 	}
13031da177e4SLinus Torvalds }
13041da177e4SLinus Torvalds 
13051da177e4SLinus Torvalds /**
13061da177e4SLinus Torvalds  * ad_periodic_machine - handle a port's periodic state machine
13071da177e4SLinus Torvalds  * @port: the port we're looking at
13083a755cd8SHangbin Liu  * @bond_params: bond parameters we will use
13091da177e4SLinus Torvalds  *
13101da177e4SLinus Torvalds  * Turn ntt flag on priodically to perform periodic transmission of lacpdu's.
13111da177e4SLinus Torvalds  */
ad_periodic_machine(struct port * port,struct bond_params * bond_params)1312bbef56d8SColin Ian King static void ad_periodic_machine(struct port *port, struct bond_params *bond_params)
13131da177e4SLinus Torvalds {
13141da177e4SLinus Torvalds 	periodic_states_t last_state;
13151da177e4SLinus Torvalds 
13163bf2d28aSVeaceslav Falico 	/* keep current state machine state to compare later if it was changed */
13171da177e4SLinus Torvalds 	last_state = port->sm_periodic_state;
13181da177e4SLinus Torvalds 
13193bf2d28aSVeaceslav Falico 	/* check if port was reinitialized */
13201da177e4SLinus Torvalds 	if (((port->sm_vars & AD_PORT_BEGIN) || !(port->sm_vars & AD_PORT_LACP_ENABLED) || !port->is_enabled) ||
13213a755cd8SHangbin Liu 	    (!(port->actor_oper_port_state & LACP_STATE_LACP_ACTIVITY) && !(port->partner_oper.port_state & LACP_STATE_LACP_ACTIVITY)) ||
1322bbef56d8SColin Ian King 	    !bond_params->lacp_active) {
13233bf2d28aSVeaceslav Falico 		port->sm_periodic_state = AD_NO_PERIODIC;
13241da177e4SLinus Torvalds 	}
13253bf2d28aSVeaceslav Falico 	/* check if state machine should change state */
13261da177e4SLinus Torvalds 	else if (port->sm_periodic_timer_counter) {
13273bf2d28aSVeaceslav Falico 		/* check if periodic state machine expired */
13281da177e4SLinus Torvalds 		if (!(--port->sm_periodic_timer_counter)) {
13293bf2d28aSVeaceslav Falico 			/* if expired then do tx */
13303bf2d28aSVeaceslav Falico 			port->sm_periodic_state = AD_PERIODIC_TX;
13311da177e4SLinus Torvalds 		} else {
13323bf2d28aSVeaceslav Falico 			/* If not expired, check if there is some new timeout
13333bf2d28aSVeaceslav Falico 			 * parameter from the partner state
13343bf2d28aSVeaceslav Falico 			 */
13351da177e4SLinus Torvalds 			switch (port->sm_periodic_state) {
13361da177e4SLinus Torvalds 			case AD_FAST_PERIODIC:
13377bfc4753SBandan Das 				if (!(port->partner_oper.port_state
1338c1e46990SAndy Roulin 				      & LACP_STATE_LACP_TIMEOUT))
13393bf2d28aSVeaceslav Falico 					port->sm_periodic_state = AD_SLOW_PERIODIC;
13401da177e4SLinus Torvalds 				break;
13411da177e4SLinus Torvalds 			case AD_SLOW_PERIODIC:
1342c1e46990SAndy Roulin 				if ((port->partner_oper.port_state & LACP_STATE_LACP_TIMEOUT)) {
13431da177e4SLinus Torvalds 					port->sm_periodic_timer_counter = 0;
13443bf2d28aSVeaceslav Falico 					port->sm_periodic_state = AD_PERIODIC_TX;
13451da177e4SLinus Torvalds 				}
13461da177e4SLinus Torvalds 				break;
13473bf2d28aSVeaceslav Falico 			default:
13481da177e4SLinus Torvalds 				break;
13491da177e4SLinus Torvalds 			}
13501da177e4SLinus Torvalds 		}
13511da177e4SLinus Torvalds 	} else {
13521da177e4SLinus Torvalds 		switch (port->sm_periodic_state) {
13531da177e4SLinus Torvalds 		case AD_NO_PERIODIC:
13543bf2d28aSVeaceslav Falico 			port->sm_periodic_state = AD_FAST_PERIODIC;
13551da177e4SLinus Torvalds 			break;
13561da177e4SLinus Torvalds 		case AD_PERIODIC_TX:
13573bf2d28aSVeaceslav Falico 			if (!(port->partner_oper.port_state &
1358c1e46990SAndy Roulin 			    LACP_STATE_LACP_TIMEOUT))
13593bf2d28aSVeaceslav Falico 				port->sm_periodic_state = AD_SLOW_PERIODIC;
13607bfc4753SBandan Das 			else
13613bf2d28aSVeaceslav Falico 				port->sm_periodic_state = AD_FAST_PERIODIC;
13621da177e4SLinus Torvalds 			break;
13633bf2d28aSVeaceslav Falico 		default:
13641da177e4SLinus Torvalds 			break;
13651da177e4SLinus Torvalds 		}
13661da177e4SLinus Torvalds 	}
13671da177e4SLinus Torvalds 
13683bf2d28aSVeaceslav Falico 	/* check if the state machine was changed */
13691da177e4SLinus Torvalds 	if (port->sm_periodic_state != last_state) {
137017720981SJarod Wilson 		slave_dbg(port->slave->bond->dev, port->slave->dev,
137117720981SJarod Wilson 			  "Periodic Machine: Port=%d, Last State=%d, Curr State=%d\n",
1372a4aee5c8SJoe Perches 			  port->actor_port_number, last_state,
1373a4aee5c8SJoe Perches 			  port->sm_periodic_state);
13741da177e4SLinus Torvalds 		switch (port->sm_periodic_state) {
13751da177e4SLinus Torvalds 		case AD_NO_PERIODIC:
13763bf2d28aSVeaceslav Falico 			port->sm_periodic_timer_counter = 0;
13771da177e4SLinus Torvalds 			break;
13781da177e4SLinus Torvalds 		case AD_FAST_PERIODIC:
13793bf2d28aSVeaceslav Falico 			/* decrement 1 tick we lost in the PERIODIC_TX cycle */
13803bf2d28aSVeaceslav Falico 			port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_FAST_PERIODIC_TIME))-1;
13811da177e4SLinus Torvalds 			break;
13821da177e4SLinus Torvalds 		case AD_SLOW_PERIODIC:
13833bf2d28aSVeaceslav Falico 			/* decrement 1 tick we lost in the PERIODIC_TX cycle */
13843bf2d28aSVeaceslav Falico 			port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_SLOW_PERIODIC_TIME))-1;
13851da177e4SLinus Torvalds 			break;
13861da177e4SLinus Torvalds 		case AD_PERIODIC_TX:
1387d238d458SHolger Eitzenberger 			port->ntt = true;
13881da177e4SLinus Torvalds 			break;
13893bf2d28aSVeaceslav Falico 		default:
13901da177e4SLinus Torvalds 			break;
13911da177e4SLinus Torvalds 		}
13921da177e4SLinus Torvalds 	}
13931da177e4SLinus Torvalds }
13941da177e4SLinus Torvalds 
13951da177e4SLinus Torvalds /**
13961da177e4SLinus Torvalds  * ad_port_selection_logic - select aggregation groups
13971da177e4SLinus Torvalds  * @port: the port we're looking at
1398ee637714SMahesh Bandewar  * @update_slave_arr: Does slave array need update?
13991da177e4SLinus Torvalds  *
14001da177e4SLinus Torvalds  * Select aggregation groups, and assign each port for it's aggregetor. The
14011da177e4SLinus Torvalds  * selection logic is called in the inititalization (after all the handshkes),
14021da177e4SLinus Torvalds  * and after every lacpdu receive (if selected is off).
14031da177e4SLinus Torvalds  */
ad_port_selection_logic(struct port * port,bool * update_slave_arr)1404ee637714SMahesh Bandewar static void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
14051da177e4SLinus Torvalds {
14061da177e4SLinus Torvalds 	struct aggregator *aggregator, *free_aggregator = NULL, *temp_aggregator;
14071da177e4SLinus Torvalds 	struct port *last_port = NULL, *curr_port;
14083e36bb75SVeaceslav Falico 	struct list_head *iter;
14093e36bb75SVeaceslav Falico 	struct bonding *bond;
14103e36bb75SVeaceslav Falico 	struct slave *slave;
14111da177e4SLinus Torvalds 	int found = 0;
14121da177e4SLinus Torvalds 
14133bf2d28aSVeaceslav Falico 	/* if the port is already Selected, do nothing */
14147bfc4753SBandan Das 	if (port->sm_vars & AD_PORT_SELECTED)
14151da177e4SLinus Torvalds 		return;
14161da177e4SLinus Torvalds 
14173e36bb75SVeaceslav Falico 	bond = __get_bond_by_port(port);
14183e36bb75SVeaceslav Falico 
14193bf2d28aSVeaceslav Falico 	/* if the port is connected to other aggregator, detach it */
14201da177e4SLinus Torvalds 	if (port->aggregator) {
14213bf2d28aSVeaceslav Falico 		/* detach the port from its former aggregator */
14221da177e4SLinus Torvalds 		temp_aggregator = port->aggregator;
1423128ea6c3SBandan Das 		for (curr_port = temp_aggregator->lag_ports; curr_port;
1424128ea6c3SBandan Das 		     last_port = curr_port,
1425128ea6c3SBandan Das 		     curr_port = curr_port->next_port_in_aggregator) {
14261da177e4SLinus Torvalds 			if (curr_port == port) {
14271da177e4SLinus Torvalds 				temp_aggregator->num_of_ports--;
14283bf2d28aSVeaceslav Falico 				/* if it is the first port attached to the
14293bf2d28aSVeaceslav Falico 				 * aggregator
14303bf2d28aSVeaceslav Falico 				 */
14313bf2d28aSVeaceslav Falico 				if (!last_port) {
1432128ea6c3SBandan Das 					temp_aggregator->lag_ports =
1433128ea6c3SBandan Das 						port->next_port_in_aggregator;
14343bf2d28aSVeaceslav Falico 				} else {
14353bf2d28aSVeaceslav Falico 					/* not the first port attached to the
14363bf2d28aSVeaceslav Falico 					 * aggregator
14373bf2d28aSVeaceslav Falico 					 */
1438128ea6c3SBandan Das 					last_port->next_port_in_aggregator =
1439128ea6c3SBandan Das 						port->next_port_in_aggregator;
14401da177e4SLinus Torvalds 				}
14411da177e4SLinus Torvalds 
14423bf2d28aSVeaceslav Falico 				/* clear the port's relations to this
14433bf2d28aSVeaceslav Falico 				 * aggregator
14443bf2d28aSVeaceslav Falico 				 */
14451da177e4SLinus Torvalds 				port->aggregator = NULL;
14461da177e4SLinus Torvalds 				port->next_port_in_aggregator = NULL;
14471da177e4SLinus Torvalds 				port->actor_port_aggregator_identifier = 0;
14481da177e4SLinus Torvalds 
144917720981SJarod Wilson 				slave_dbg(bond->dev, port->slave->dev, "Port %d left LAG %d\n",
1450a4aee5c8SJoe Perches 					  port->actor_port_number,
1451a4aee5c8SJoe Perches 					  temp_aggregator->aggregator_identifier);
14523bf2d28aSVeaceslav Falico 				/* if the aggregator is empty, clear its
14533bf2d28aSVeaceslav Falico 				 * parameters, and set it ready to be attached
14543bf2d28aSVeaceslav Falico 				 */
14557bfc4753SBandan Das 				if (!temp_aggregator->lag_ports)
14561da177e4SLinus Torvalds 					ad_clear_agg(temp_aggregator);
14571da177e4SLinus Torvalds 				break;
14581da177e4SLinus Torvalds 			}
14591da177e4SLinus Torvalds 		}
14603bf2d28aSVeaceslav Falico 		if (!curr_port) {
14613bf2d28aSVeaceslav Falico 			/* meaning: the port was related to an aggregator
14623bf2d28aSVeaceslav Falico 			 * but was not on the aggregator port list
14633bf2d28aSVeaceslav Falico 			 */
146417720981SJarod Wilson 			net_warn_ratelimited("%s: (slave %s): Warning: Port %d was related to aggregator %d but was not on its port list\n",
1465471cb5a3SJiri Pirko 					     port->slave->bond->dev->name,
1466e5e2a8fdSJiri Pirko 					     port->slave->dev->name,
146717720981SJarod Wilson 					     port->actor_port_number,
14681da177e4SLinus Torvalds 					     port->aggregator->aggregator_identifier);
14691da177e4SLinus Torvalds 		}
14701da177e4SLinus Torvalds 	}
14713bf2d28aSVeaceslav Falico 	/* search on all aggregators for a suitable aggregator for this port */
14723e36bb75SVeaceslav Falico 	bond_for_each_slave(bond, slave, iter) {
14733fdddd85Sdingtianhong 		aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
14741da177e4SLinus Torvalds 
14753bf2d28aSVeaceslav Falico 		/* keep a free aggregator for later use(if needed) */
14761da177e4SLinus Torvalds 		if (!aggregator->lag_ports) {
14777bfc4753SBandan Das 			if (!free_aggregator)
14781da177e4SLinus Torvalds 				free_aggregator = aggregator;
14791da177e4SLinus Torvalds 			continue;
14801da177e4SLinus Torvalds 		}
1481815117adSdingtianhong 		/* check if current aggregator suits us */
1482815117adSdingtianhong 		if (((aggregator->actor_oper_aggregator_key == port->actor_oper_port_key) && /* if all parameters match AND */
1483815117adSdingtianhong 		     MAC_ADDRESS_EQUAL(&(aggregator->partner_system), &(port->partner_oper.system)) &&
14841055c9abSHolger Eitzenberger 		     (aggregator->partner_system_priority == port->partner_oper.system_priority) &&
14851055c9abSHolger Eitzenberger 		     (aggregator->partner_oper_aggregator_key == port->partner_oper.key)
14861da177e4SLinus Torvalds 		    ) &&
1487815117adSdingtianhong 		    ((!MAC_ADDRESS_EQUAL(&(port->partner_oper.system), &(null_mac_addr)) && /* partner answers */
1488815117adSdingtianhong 		      !aggregator->is_individual)  /* but is not individual OR */
14891da177e4SLinus Torvalds 		    )
14901da177e4SLinus Torvalds 		   ) {
1491815117adSdingtianhong 			/* attach to the founded aggregator */
14921da177e4SLinus Torvalds 			port->aggregator = aggregator;
1493128ea6c3SBandan Das 			port->actor_port_aggregator_identifier =
1494128ea6c3SBandan Das 				port->aggregator->aggregator_identifier;
14951da177e4SLinus Torvalds 			port->next_port_in_aggregator = aggregator->lag_ports;
14961da177e4SLinus Torvalds 			port->aggregator->num_of_ports++;
14971da177e4SLinus Torvalds 			aggregator->lag_ports = port;
149817720981SJarod Wilson 			slave_dbg(bond->dev, slave->dev, "Port %d joined LAG %d (existing LAG)\n",
1499a4aee5c8SJoe Perches 				  port->actor_port_number,
1500a4aee5c8SJoe Perches 				  port->aggregator->aggregator_identifier);
15011da177e4SLinus Torvalds 
15023bf2d28aSVeaceslav Falico 			/* mark this port as selected */
15031da177e4SLinus Torvalds 			port->sm_vars |= AD_PORT_SELECTED;
15041da177e4SLinus Torvalds 			found = 1;
15051da177e4SLinus Torvalds 			break;
15061da177e4SLinus Torvalds 		}
15071da177e4SLinus Torvalds 	}
15081da177e4SLinus Torvalds 
15093bf2d28aSVeaceslav Falico 	/* the port couldn't find an aggregator - attach it to a new
15103bf2d28aSVeaceslav Falico 	 * aggregator
15113bf2d28aSVeaceslav Falico 	 */
15121da177e4SLinus Torvalds 	if (!found) {
15131da177e4SLinus Torvalds 		if (free_aggregator) {
15143bf2d28aSVeaceslav Falico 			/* assign port a new aggregator */
15151da177e4SLinus Torvalds 			port->aggregator = free_aggregator;
1516128ea6c3SBandan Das 			port->actor_port_aggregator_identifier =
1517128ea6c3SBandan Das 				port->aggregator->aggregator_identifier;
15181da177e4SLinus Torvalds 
15193bf2d28aSVeaceslav Falico 			/* update the new aggregator's parameters
15203bf2d28aSVeaceslav Falico 			 * if port was responsed from the end-user
15213bf2d28aSVeaceslav Falico 			 */
1522cb8dda90SJianhua Xie 			if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)
15237bfc4753SBandan Das 				/* if port is full duplex */
15241624db7bSHolger Eitzenberger 				port->aggregator->is_individual = false;
15257bfc4753SBandan Das 			else
15261624db7bSHolger Eitzenberger 				port->aggregator->is_individual = true;
15271da177e4SLinus Torvalds 
1528c3cd9ee1SMahesh Bandewar 			port->aggregator->actor_admin_aggregator_key =
1529c3cd9ee1SMahesh Bandewar 				port->actor_admin_port_key;
1530c3cd9ee1SMahesh Bandewar 			port->aggregator->actor_oper_aggregator_key =
1531c3cd9ee1SMahesh Bandewar 				port->actor_oper_port_key;
1532128ea6c3SBandan Das 			port->aggregator->partner_system =
1533128ea6c3SBandan Das 				port->partner_oper.system;
1534128ea6c3SBandan Das 			port->aggregator->partner_system_priority =
1535128ea6c3SBandan Das 				port->partner_oper.system_priority;
15361055c9abSHolger Eitzenberger 			port->aggregator->partner_oper_aggregator_key = port->partner_oper.key;
15371da177e4SLinus Torvalds 			port->aggregator->receive_state = 1;
15381da177e4SLinus Torvalds 			port->aggregator->transmit_state = 1;
15391da177e4SLinus Torvalds 			port->aggregator->lag_ports = port;
15401da177e4SLinus Torvalds 			port->aggregator->num_of_ports++;
15411da177e4SLinus Torvalds 
15423bf2d28aSVeaceslav Falico 			/* mark this port as selected */
15431da177e4SLinus Torvalds 			port->sm_vars |= AD_PORT_SELECTED;
15441da177e4SLinus Torvalds 
154517720981SJarod Wilson 			slave_dbg(bond->dev, port->slave->dev, "Port %d joined LAG %d (new LAG)\n",
1546a4aee5c8SJoe Perches 				  port->actor_port_number,
1547a4aee5c8SJoe Perches 				  port->aggregator->aggregator_identifier);
15481da177e4SLinus Torvalds 		} else {
154917720981SJarod Wilson 			slave_err(bond->dev, port->slave->dev,
155017720981SJarod Wilson 				  "Port %d did not find a suitable aggregator\n",
155117720981SJarod Wilson 				  port->actor_port_number);
1552*9c807965SDaniil Tatianin 			return;
15531da177e4SLinus Torvalds 		}
15541da177e4SLinus Torvalds 	}
15553bf2d28aSVeaceslav Falico 	/* if all aggregator's ports are READY_N == TRUE, set ready=TRUE
15563bf2d28aSVeaceslav Falico 	 * in all aggregator's ports, else set ready=FALSE in all
15573bf2d28aSVeaceslav Falico 	 * aggregator's ports
15583bf2d28aSVeaceslav Falico 	 */
15593bf2d28aSVeaceslav Falico 	__set_agg_ports_ready(port->aggregator,
15603bf2d28aSVeaceslav Falico 			      __agg_ports_are_ready(port->aggregator));
15611da177e4SLinus Torvalds 
1562fd989c83SJay Vosburgh 	aggregator = __get_first_agg(port);
1563ee637714SMahesh Bandewar 	ad_agg_selection_logic(aggregator, update_slave_arr);
156463b46242SWilson Kok 
156563b46242SWilson Kok 	if (!port->aggregator->is_active)
1566c1e46990SAndy Roulin 		port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION;
15671da177e4SLinus Torvalds }
1568fd989c83SJay Vosburgh 
15693bf2d28aSVeaceslav Falico /* Decide if "agg" is a better choice for the new active aggregator that
1570fd989c83SJay Vosburgh  * the current best, according to the ad_select policy.
1571fd989c83SJay Vosburgh  */
ad_agg_selection_test(struct aggregator * best,struct aggregator * curr)1572fd989c83SJay Vosburgh static struct aggregator *ad_agg_selection_test(struct aggregator *best,
1573fd989c83SJay Vosburgh 						struct aggregator *curr)
1574fd989c83SJay Vosburgh {
15753bf2d28aSVeaceslav Falico 	/* 0. If no best, select current.
1576fd989c83SJay Vosburgh 	 *
1577fd989c83SJay Vosburgh 	 * 1. If the current agg is not individual, and the best is
1578fd989c83SJay Vosburgh 	 *    individual, select current.
1579fd989c83SJay Vosburgh 	 *
1580fd989c83SJay Vosburgh 	 * 2. If current agg is individual and the best is not, keep best.
1581fd989c83SJay Vosburgh 	 *
1582fd989c83SJay Vosburgh 	 * 3. Therefore, current and best are both individual or both not
1583fd989c83SJay Vosburgh 	 *    individual, so:
1584fd989c83SJay Vosburgh 	 *
1585fd989c83SJay Vosburgh 	 * 3a. If current agg partner replied, and best agg partner did not,
1586fd989c83SJay Vosburgh 	 *     select current.
1587fd989c83SJay Vosburgh 	 *
1588fd989c83SJay Vosburgh 	 * 3b. If current agg partner did not reply and best agg partner
1589fd989c83SJay Vosburgh 	 *     did reply, keep best.
1590fd989c83SJay Vosburgh 	 *
1591fd989c83SJay Vosburgh 	 * 4.  Therefore, current and best both have partner replies or
1592fd989c83SJay Vosburgh 	 *     both do not, so perform selection policy:
1593fd989c83SJay Vosburgh 	 *
1594fd989c83SJay Vosburgh 	 * BOND_AD_COUNT: Select by count of ports.  If count is equal,
1595fd989c83SJay Vosburgh 	 *     select by bandwidth.
1596fd989c83SJay Vosburgh 	 *
1597fd989c83SJay Vosburgh 	 * BOND_AD_STABLE, BOND_AD_BANDWIDTH: Select by bandwidth.
1598fd989c83SJay Vosburgh 	 */
1599fd989c83SJay Vosburgh 	if (!best)
1600fd989c83SJay Vosburgh 		return curr;
1601fd989c83SJay Vosburgh 
1602fd989c83SJay Vosburgh 	if (!curr->is_individual && best->is_individual)
1603fd989c83SJay Vosburgh 		return curr;
1604fd989c83SJay Vosburgh 
1605fd989c83SJay Vosburgh 	if (curr->is_individual && !best->is_individual)
1606fd989c83SJay Vosburgh 		return best;
1607fd989c83SJay Vosburgh 
1608fd989c83SJay Vosburgh 	if (__agg_has_partner(curr) && !__agg_has_partner(best))
1609fd989c83SJay Vosburgh 		return curr;
1610fd989c83SJay Vosburgh 
1611fd989c83SJay Vosburgh 	if (!__agg_has_partner(curr) && __agg_has_partner(best))
1612fd989c83SJay Vosburgh 		return best;
1613fd989c83SJay Vosburgh 
1614fd989c83SJay Vosburgh 	switch (__get_agg_selection_mode(curr->lag_ports)) {
1615fd989c83SJay Vosburgh 	case BOND_AD_COUNT:
16160622cab0SJay Vosburgh 		if (__agg_active_ports(curr) > __agg_active_ports(best))
1617fd989c83SJay Vosburgh 			return curr;
1618fd989c83SJay Vosburgh 
16190622cab0SJay Vosburgh 		if (__agg_active_ports(curr) < __agg_active_ports(best))
1620fd989c83SJay Vosburgh 			return best;
1621fd989c83SJay Vosburgh 
1622df561f66SGustavo A. R. Silva 		fallthrough;
1623fd989c83SJay Vosburgh 	case BOND_AD_STABLE:
1624fd989c83SJay Vosburgh 	case BOND_AD_BANDWIDTH:
1625fd989c83SJay Vosburgh 		if (__get_agg_bandwidth(curr) > __get_agg_bandwidth(best))
1626fd989c83SJay Vosburgh 			return curr;
1627fd989c83SJay Vosburgh 
1628fd989c83SJay Vosburgh 		break;
1629fd989c83SJay Vosburgh 
1630fd989c83SJay Vosburgh 	default:
163117720981SJarod Wilson 		net_warn_ratelimited("%s: (slave %s): Impossible agg select mode %d\n",
1632471cb5a3SJiri Pirko 				     curr->slave->bond->dev->name,
163317720981SJarod Wilson 				     curr->slave->dev->name,
1634fd989c83SJay Vosburgh 				     __get_agg_selection_mode(curr->lag_ports));
1635fd989c83SJay Vosburgh 		break;
1636fd989c83SJay Vosburgh 	}
1637fd989c83SJay Vosburgh 
1638fd989c83SJay Vosburgh 	return best;
16391da177e4SLinus Torvalds }
16401da177e4SLinus Torvalds 
agg_device_up(const struct aggregator * agg)16414cd6fe1cSStephen Hemminger static int agg_device_up(const struct aggregator *agg)
16424cd6fe1cSStephen Hemminger {
16432430af8bSJiri Bohac 	struct port *port = agg->lag_ports;
16443bf2d28aSVeaceslav Falico 
16452430af8bSJiri Bohac 	if (!port)
16462430af8bSJiri Bohac 		return 0;
16473bf2d28aSVeaceslav Falico 
16480622cab0SJay Vosburgh 	for (port = agg->lag_ports; port;
16490622cab0SJay Vosburgh 	     port = port->next_port_in_aggregator) {
16500622cab0SJay Vosburgh 		if (netif_running(port->slave->dev) &&
16510622cab0SJay Vosburgh 		    netif_carrier_ok(port->slave->dev))
16520622cab0SJay Vosburgh 			return 1;
16530622cab0SJay Vosburgh 	}
16540622cab0SJay Vosburgh 
16550622cab0SJay Vosburgh 	return 0;
16564cd6fe1cSStephen Hemminger }
16574cd6fe1cSStephen Hemminger 
16581da177e4SLinus Torvalds /**
16591da177e4SLinus Torvalds  * ad_agg_selection_logic - select an aggregation group for a team
1660a35e5478SLee Jones  * @agg: the aggregator we're looking at
1661ee637714SMahesh Bandewar  * @update_slave_arr: Does slave array need update?
16621da177e4SLinus Torvalds  *
16631da177e4SLinus Torvalds  * It is assumed that only one aggregator may be selected for a team.
1664fd989c83SJay Vosburgh  *
1665fd989c83SJay Vosburgh  * The logic of this function is to select the aggregator according to
1666fd989c83SJay Vosburgh  * the ad_select policy:
1667fd989c83SJay Vosburgh  *
1668fd989c83SJay Vosburgh  * BOND_AD_STABLE: select the aggregator with the most ports attached to
1669fd989c83SJay Vosburgh  * it, and to reselect the active aggregator only if the previous
1670fd989c83SJay Vosburgh  * aggregator has no more ports related to it.
1671fd989c83SJay Vosburgh  *
1672fd989c83SJay Vosburgh  * BOND_AD_BANDWIDTH: select the aggregator with the highest total
1673fd989c83SJay Vosburgh  * bandwidth, and reselect whenever a link state change takes place or the
1674fd989c83SJay Vosburgh  * set of slaves in the bond changes.
1675fd989c83SJay Vosburgh  *
1676fd989c83SJay Vosburgh  * BOND_AD_COUNT: select the aggregator with largest number of ports
1677fd989c83SJay Vosburgh  * (slaves), and reselect whenever a link state change takes place or the
1678fd989c83SJay Vosburgh  * set of slaves in the bond changes.
16791da177e4SLinus Torvalds  *
16801da177e4SLinus Torvalds  * FIXME: this function MUST be called with the first agg in the bond, or
16811da177e4SLinus Torvalds  * __get_active_agg() won't work correctly. This function should be better
16821da177e4SLinus Torvalds  * called with the bond itself, and retrieve the first agg from it.
16831da177e4SLinus Torvalds  */
ad_agg_selection_logic(struct aggregator * agg,bool * update_slave_arr)1684ee637714SMahesh Bandewar static void ad_agg_selection_logic(struct aggregator *agg,
1685ee637714SMahesh Bandewar 				   bool *update_slave_arr)
16861da177e4SLinus Torvalds {
1687fd989c83SJay Vosburgh 	struct aggregator *best, *active, *origin;
1688bef1fcceSVeaceslav Falico 	struct bonding *bond = agg->slave->bond;
1689bef1fcceSVeaceslav Falico 	struct list_head *iter;
1690bef1fcceSVeaceslav Falico 	struct slave *slave;
16911da177e4SLinus Torvalds 	struct port *port;
16921da177e4SLinus Torvalds 
169349b7624eSVeaceslav Falico 	rcu_read_lock();
1694fd989c83SJay Vosburgh 	origin = agg;
1695fd989c83SJay Vosburgh 	active = __get_active_agg(agg);
16964cd6fe1cSStephen Hemminger 	best = (active && agg_device_up(active)) ? active : NULL;
16971da177e4SLinus Torvalds 
1698be79bd04Sdingtianhong 	bond_for_each_slave_rcu(bond, slave, iter) {
16993fdddd85Sdingtianhong 		agg = &(SLAVE_AD_INFO(slave)->aggregator);
1700bef1fcceSVeaceslav Falico 
1701fd989c83SJay Vosburgh 		agg->is_active = 0;
17021da177e4SLinus Torvalds 
17030622cab0SJay Vosburgh 		if (__agg_active_ports(agg) && agg_device_up(agg))
1704fd989c83SJay Vosburgh 			best = ad_agg_selection_test(best, agg);
1705bef1fcceSVeaceslav Falico 	}
17061da177e4SLinus Torvalds 
1707fd989c83SJay Vosburgh 	if (best &&
1708fd989c83SJay Vosburgh 	    __get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) {
17093bf2d28aSVeaceslav Falico 		/* For the STABLE policy, don't replace the old active
1710fd989c83SJay Vosburgh 		 * aggregator if it's still active (it has an answering
1711fd989c83SJay Vosburgh 		 * partner) or if both the best and active don't have an
1712fd989c83SJay Vosburgh 		 * answering partner.
1713fd989c83SJay Vosburgh 		 */
1714fd989c83SJay Vosburgh 		if (active && active->lag_ports &&
17150622cab0SJay Vosburgh 		    __agg_active_ports(active) &&
1716fd989c83SJay Vosburgh 		    (__agg_has_partner(active) ||
17173bf2d28aSVeaceslav Falico 		     (!__agg_has_partner(active) &&
17183bf2d28aSVeaceslav Falico 		     !__agg_has_partner(best)))) {
1719fd989c83SJay Vosburgh 			if (!(!active->actor_oper_aggregator_key &&
1720fd989c83SJay Vosburgh 			      best->actor_oper_aggregator_key)) {
1721fd989c83SJay Vosburgh 				best = NULL;
1722fd989c83SJay Vosburgh 				active->is_active = 1;
17231da177e4SLinus Torvalds 			}
17241da177e4SLinus Torvalds 		}
17251da177e4SLinus Torvalds 	}
17261da177e4SLinus Torvalds 
1727fd989c83SJay Vosburgh 	if (best && (best == active)) {
1728fd989c83SJay Vosburgh 		best = NULL;
1729fd989c83SJay Vosburgh 		active->is_active = 1;
1730fd989c83SJay Vosburgh 	}
1731fd989c83SJay Vosburgh 
1732be79bd04Sdingtianhong 	/* if there is new best aggregator, activate it */
1733fd989c83SJay Vosburgh 	if (best) {
173417720981SJarod Wilson 		netdev_dbg(bond->dev, "(slave %s): best Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
173517720981SJarod Wilson 			   best->slave ? best->slave->dev->name : "NULL",
1736fd989c83SJay Vosburgh 			   best->aggregator_identifier, best->num_of_ports,
1737fd989c83SJay Vosburgh 			   best->actor_oper_aggregator_key,
1738fd989c83SJay Vosburgh 			   best->partner_oper_aggregator_key,
1739fd989c83SJay Vosburgh 			   best->is_individual, best->is_active);
174017720981SJarod Wilson 		netdev_dbg(bond->dev, "(slave %s): best ports %p slave %p\n",
174117720981SJarod Wilson 			   best->slave ? best->slave->dev->name : "NULL",
174217720981SJarod Wilson 			   best->lag_ports, best->slave);
17431da177e4SLinus Torvalds 
1744be79bd04Sdingtianhong 		bond_for_each_slave_rcu(bond, slave, iter) {
17453fdddd85Sdingtianhong 			agg = &(SLAVE_AD_INFO(slave)->aggregator);
1746fd989c83SJay Vosburgh 
174717720981SJarod Wilson 			slave_dbg(bond->dev, slave->dev, "Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
1748fd989c83SJay Vosburgh 				  agg->aggregator_identifier, agg->num_of_ports,
1749fd989c83SJay Vosburgh 				  agg->actor_oper_aggregator_key,
1750fd989c83SJay Vosburgh 				  agg->partner_oper_aggregator_key,
1751fd989c83SJay Vosburgh 				  agg->is_individual, agg->is_active);
17521da177e4SLinus Torvalds 		}
17531da177e4SLinus Torvalds 
175417720981SJarod Wilson 		/* check if any partner replies */
175517720981SJarod Wilson 		if (best->is_individual)
1756d4471f5eSVeaceslav Falico 			net_warn_ratelimited("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n",
175717720981SJarod Wilson 					     bond->dev->name);
17581da177e4SLinus Torvalds 
1759fd989c83SJay Vosburgh 		best->is_active = 1;
176017720981SJarod Wilson 		netdev_dbg(bond->dev, "(slave %s): LAG %d chosen as the active LAG\n",
176117720981SJarod Wilson 			   best->slave ? best->slave->dev->name : "NULL",
1762fd989c83SJay Vosburgh 			   best->aggregator_identifier);
176317720981SJarod Wilson 		netdev_dbg(bond->dev, "(slave %s): Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
176417720981SJarod Wilson 			   best->slave ? best->slave->dev->name : "NULL",
1765fd989c83SJay Vosburgh 			   best->aggregator_identifier, best->num_of_ports,
1766fd989c83SJay Vosburgh 			   best->actor_oper_aggregator_key,
1767fd989c83SJay Vosburgh 			   best->partner_oper_aggregator_key,
1768fd989c83SJay Vosburgh 			   best->is_individual, best->is_active);
17691da177e4SLinus Torvalds 
17703bf2d28aSVeaceslav Falico 		/* disable the ports that were related to the former
17713bf2d28aSVeaceslav Falico 		 * active_aggregator
17723bf2d28aSVeaceslav Falico 		 */
1773fd989c83SJay Vosburgh 		if (active) {
1774fd989c83SJay Vosburgh 			for (port = active->lag_ports; port;
1775fd989c83SJay Vosburgh 			     port = port->next_port_in_aggregator) {
17761da177e4SLinus Torvalds 				__disable_port(port);
17771da177e4SLinus Torvalds 			}
17781da177e4SLinus Torvalds 		}
1779ee637714SMahesh Bandewar 		/* Slave array needs update. */
1780ee637714SMahesh Bandewar 		*update_slave_arr = true;
17811da177e4SLinus Torvalds 	}
17821da177e4SLinus Torvalds 
17833bf2d28aSVeaceslav Falico 	/* if the selected aggregator is of join individuals
1784fd989c83SJay Vosburgh 	 * (partner_system is NULL), enable their ports
1785fd989c83SJay Vosburgh 	 */
1786fd989c83SJay Vosburgh 	active = __get_active_agg(origin);
17871da177e4SLinus Torvalds 
1788fd989c83SJay Vosburgh 	if (active) {
1789fd989c83SJay Vosburgh 		if (!__agg_has_partner(active)) {
1790fd989c83SJay Vosburgh 			for (port = active->lag_ports; port;
1791fd989c83SJay Vosburgh 			     port = port->next_port_in_aggregator) {
17921da177e4SLinus Torvalds 				__enable_port(port);
17931da177e4SLinus Torvalds 			}
179423de0d7bSMahesh Bandewar 			*update_slave_arr = true;
17951da177e4SLinus Torvalds 		}
17961da177e4SLinus Torvalds 	}
1797fd989c83SJay Vosburgh 
1798be79bd04Sdingtianhong 	rcu_read_unlock();
1799be79bd04Sdingtianhong 
1800fd989c83SJay Vosburgh 	bond_3ad_set_carrier(bond);
1801fd989c83SJay Vosburgh }
18021da177e4SLinus Torvalds 
18031da177e4SLinus Torvalds /**
18041da177e4SLinus Torvalds  * ad_clear_agg - clear a given aggregator's parameters
18051da177e4SLinus Torvalds  * @aggregator: the aggregator we're looking at
18061da177e4SLinus Torvalds  */
ad_clear_agg(struct aggregator * aggregator)18071da177e4SLinus Torvalds static void ad_clear_agg(struct aggregator *aggregator)
18081da177e4SLinus Torvalds {
18091da177e4SLinus Torvalds 	if (aggregator) {
18101624db7bSHolger Eitzenberger 		aggregator->is_individual = false;
18111da177e4SLinus Torvalds 		aggregator->actor_admin_aggregator_key = 0;
18121da177e4SLinus Torvalds 		aggregator->actor_oper_aggregator_key = 0;
1813f87fda00SEric Dumazet 		eth_zero_addr(aggregator->partner_system.mac_addr_value);
18141da177e4SLinus Torvalds 		aggregator->partner_system_priority = 0;
18151da177e4SLinus Torvalds 		aggregator->partner_oper_aggregator_key = 0;
18161da177e4SLinus Torvalds 		aggregator->receive_state = 0;
18171da177e4SLinus Torvalds 		aggregator->transmit_state = 0;
18181da177e4SLinus Torvalds 		aggregator->lag_ports = NULL;
18191da177e4SLinus Torvalds 		aggregator->is_active = 0;
18201da177e4SLinus Torvalds 		aggregator->num_of_ports = 0;
182117720981SJarod Wilson 		pr_debug("%s: LAG %d was cleared\n",
182217720981SJarod Wilson 			 aggregator->slave ?
182317720981SJarod Wilson 			 aggregator->slave->dev->name : "NULL",
1824a4aee5c8SJoe Perches 			 aggregator->aggregator_identifier);
18251da177e4SLinus Torvalds 	}
18261da177e4SLinus Torvalds }
18271da177e4SLinus Torvalds 
18281da177e4SLinus Torvalds /**
18291da177e4SLinus Torvalds  * ad_initialize_agg - initialize a given aggregator's parameters
18301da177e4SLinus Torvalds  * @aggregator: the aggregator we're looking at
18311da177e4SLinus Torvalds  */
ad_initialize_agg(struct aggregator * aggregator)18321da177e4SLinus Torvalds static void ad_initialize_agg(struct aggregator *aggregator)
18331da177e4SLinus Torvalds {
18341da177e4SLinus Torvalds 	if (aggregator) {
18351da177e4SLinus Torvalds 		ad_clear_agg(aggregator);
18361da177e4SLinus Torvalds 
1837f87fda00SEric Dumazet 		eth_zero_addr(aggregator->aggregator_mac_address.mac_addr_value);
18381da177e4SLinus Torvalds 		aggregator->aggregator_identifier = 0;
18391da177e4SLinus Torvalds 		aggregator->slave = NULL;
18401da177e4SLinus Torvalds 	}
18411da177e4SLinus Torvalds }
18421da177e4SLinus Torvalds 
18431da177e4SLinus Torvalds /**
18441da177e4SLinus Torvalds  * ad_initialize_port - initialize a given port's parameters
1845a35e5478SLee Jones  * @port: the port we're looking at
18461da177e4SLinus Torvalds  * @lacp_fast: boolean. whether fast periodic should be used
18471da177e4SLinus Torvalds  */
ad_initialize_port(struct port * port,int lacp_fast)18481da177e4SLinus Torvalds static void ad_initialize_port(struct port *port, int lacp_fast)
18491da177e4SLinus Torvalds {
1850c7e703d0SHolger Eitzenberger 	static const struct port_params tmpl = {
1851c7e703d0SHolger Eitzenberger 		.system_priority = 0xffff,
1852c7e703d0SHolger Eitzenberger 		.key             = 1,
1853c7e703d0SHolger Eitzenberger 		.port_number     = 1,
1854c7e703d0SHolger Eitzenberger 		.port_priority   = 0xff,
1855c7e703d0SHolger Eitzenberger 		.port_state      = 1,
1856c7e703d0SHolger Eitzenberger 	};
18577addeef6SHolger Eitzenberger 	static const struct lacpdu lacpdu = {
18587addeef6SHolger Eitzenberger 		.subtype		= 0x01,
18597addeef6SHolger Eitzenberger 		.version_number = 0x01,
18607addeef6SHolger Eitzenberger 		.tlv_type_actor_info = 0x01,
18617addeef6SHolger Eitzenberger 		.actor_information_length = 0x14,
18627addeef6SHolger Eitzenberger 		.tlv_type_partner_info = 0x02,
18637addeef6SHolger Eitzenberger 		.partner_information_length = 0x14,
18647addeef6SHolger Eitzenberger 		.tlv_type_collector_info = 0x03,
18657addeef6SHolger Eitzenberger 		.collector_information_length = 0x10,
18667addeef6SHolger Eitzenberger 		.collector_max_delay = htons(AD_COLLECTOR_MAX_DELAY),
18677addeef6SHolger Eitzenberger 	};
1868c7e703d0SHolger Eitzenberger 
18691da177e4SLinus Torvalds 	if (port) {
18701da177e4SLinus Torvalds 		port->actor_port_priority = 0xff;
18711da177e4SLinus Torvalds 		port->actor_port_aggregator_identifier = 0;
1872d238d458SHolger Eitzenberger 		port->ntt = false;
1873c1e46990SAndy Roulin 		port->actor_admin_port_state = LACP_STATE_AGGREGATION |
1874c1e46990SAndy Roulin 					       LACP_STATE_LACP_ACTIVITY;
1875c1e46990SAndy Roulin 		port->actor_oper_port_state  = LACP_STATE_AGGREGATION |
1876c1e46990SAndy Roulin 					       LACP_STATE_LACP_ACTIVITY;
18771da177e4SLinus Torvalds 
18787bfc4753SBandan Das 		if (lacp_fast)
1879c1e46990SAndy Roulin 			port->actor_oper_port_state |= LACP_STATE_LACP_TIMEOUT;
18801da177e4SLinus Torvalds 
1881c7e703d0SHolger Eitzenberger 		memcpy(&port->partner_admin, &tmpl, sizeof(tmpl));
1882c7e703d0SHolger Eitzenberger 		memcpy(&port->partner_oper, &tmpl, sizeof(tmpl));
1883c7e703d0SHolger Eitzenberger 
1884f48127b6SHolger Eitzenberger 		port->is_enabled = true;
18853bf2d28aSVeaceslav Falico 		/* private parameters */
188619a12049SMahesh Bandewar 		port->sm_vars = AD_PORT_BEGIN | AD_PORT_LACP_ENABLED;
18871da177e4SLinus Torvalds 		port->sm_rx_state = 0;
18881da177e4SLinus Torvalds 		port->sm_rx_timer_counter = 0;
18891da177e4SLinus Torvalds 		port->sm_periodic_state = 0;
18901da177e4SLinus Torvalds 		port->sm_periodic_timer_counter = 0;
18911da177e4SLinus Torvalds 		port->sm_mux_state = 0;
18921da177e4SLinus Torvalds 		port->sm_mux_timer_counter = 0;
18931da177e4SLinus Torvalds 		port->sm_tx_state = 0;
18941da177e4SLinus Torvalds 		port->aggregator = NULL;
18951da177e4SLinus Torvalds 		port->next_port_in_aggregator = NULL;
18961da177e4SLinus Torvalds 		port->transaction_id = 0;
18971da177e4SLinus Torvalds 
189814c9551aSMahesh Bandewar 		port->sm_churn_actor_timer_counter = 0;
189914c9551aSMahesh Bandewar 		port->sm_churn_actor_state = 0;
190014c9551aSMahesh Bandewar 		port->churn_actor_count = 0;
190114c9551aSMahesh Bandewar 		port->sm_churn_partner_timer_counter = 0;
190214c9551aSMahesh Bandewar 		port->sm_churn_partner_state = 0;
190314c9551aSMahesh Bandewar 		port->churn_partner_count = 0;
190414c9551aSMahesh Bandewar 
19057addeef6SHolger Eitzenberger 		memcpy(&port->lacpdu, &lacpdu, sizeof(lacpdu));
19061da177e4SLinus Torvalds 	}
19071da177e4SLinus Torvalds }
19081da177e4SLinus Torvalds 
19091da177e4SLinus Torvalds /**
19101da177e4SLinus Torvalds  * ad_enable_collecting_distributing - enable a port's transmit/receive
19111da177e4SLinus Torvalds  * @port: the port we're looking at
1912ee637714SMahesh Bandewar  * @update_slave_arr: Does slave array need update?
19131da177e4SLinus Torvalds  *
19141da177e4SLinus Torvalds  * Enable @port if it's in an active aggregator
19151da177e4SLinus Torvalds  */
ad_enable_collecting_distributing(struct port * port,bool * update_slave_arr)1916ee637714SMahesh Bandewar static void ad_enable_collecting_distributing(struct port *port,
1917ee637714SMahesh Bandewar 					      bool *update_slave_arr)
19181da177e4SLinus Torvalds {
19191da177e4SLinus Torvalds 	if (port->aggregator->is_active) {
192017720981SJarod Wilson 		slave_dbg(port->slave->bond->dev, port->slave->dev,
192117720981SJarod Wilson 			  "Enabling port %d (LAG %d)\n",
1922a4aee5c8SJoe Perches 			  port->actor_port_number,
1923a4aee5c8SJoe Perches 			  port->aggregator->aggregator_identifier);
19241da177e4SLinus Torvalds 		__enable_port(port);
1925ee637714SMahesh Bandewar 		/* Slave array needs update */
1926ee637714SMahesh Bandewar 		*update_slave_arr = true;
19271da177e4SLinus Torvalds 	}
19281da177e4SLinus Torvalds }
19291da177e4SLinus Torvalds 
19301da177e4SLinus Torvalds /**
19311da177e4SLinus Torvalds  * ad_disable_collecting_distributing - disable a port's transmit/receive
19321da177e4SLinus Torvalds  * @port: the port we're looking at
1933ee637714SMahesh Bandewar  * @update_slave_arr: Does slave array need update?
19341da177e4SLinus Torvalds  */
ad_disable_collecting_distributing(struct port * port,bool * update_slave_arr)1935ee637714SMahesh Bandewar static void ad_disable_collecting_distributing(struct port *port,
1936ee637714SMahesh Bandewar 					       bool *update_slave_arr)
19371da177e4SLinus Torvalds {
19383bf2d28aSVeaceslav Falico 	if (port->aggregator &&
19393bf2d28aSVeaceslav Falico 	    !MAC_ADDRESS_EQUAL(&(port->aggregator->partner_system),
19403bf2d28aSVeaceslav Falico 			       &(null_mac_addr))) {
194117720981SJarod Wilson 		slave_dbg(port->slave->bond->dev, port->slave->dev,
194217720981SJarod Wilson 			  "Disabling port %d (LAG %d)\n",
1943a4aee5c8SJoe Perches 			  port->actor_port_number,
1944a4aee5c8SJoe Perches 			  port->aggregator->aggregator_identifier);
19451da177e4SLinus Torvalds 		__disable_port(port);
1946ee637714SMahesh Bandewar 		/* Slave array needs an update */
1947ee637714SMahesh Bandewar 		*update_slave_arr = true;
19481da177e4SLinus Torvalds 	}
19491da177e4SLinus Torvalds }
19501da177e4SLinus Torvalds 
19511da177e4SLinus Torvalds /**
19521da177e4SLinus Torvalds  * ad_marker_info_received - handle receive of a Marker information frame
19531da177e4SLinus Torvalds  * @marker_info: Marker info received
19541da177e4SLinus Torvalds  * @port: the port we're looking at
19551da177e4SLinus Torvalds  */
ad_marker_info_received(struct bond_marker * marker_info,struct port * port)19561c3f0b8eSMathieu Desnoyers static void ad_marker_info_received(struct bond_marker *marker_info,
19571c3f0b8eSMathieu Desnoyers 				    struct port *port)
19581da177e4SLinus Torvalds {
19591c3f0b8eSMathieu Desnoyers 	struct bond_marker marker;
19601da177e4SLinus Torvalds 
1961267c095aSNikolay Aleksandrov 	atomic64_inc(&SLAVE_AD_INFO(port->slave)->stats.marker_rx);
1962949e7ceaSNikolay Aleksandrov 	atomic64_inc(&BOND_AD_INFO(port->slave->bond).stats.marker_rx);
1963267c095aSNikolay Aleksandrov 
19643bf2d28aSVeaceslav Falico 	/* copy the received marker data to the response marker */
19651c3f0b8eSMathieu Desnoyers 	memcpy(&marker, marker_info, sizeof(struct bond_marker));
19663bf2d28aSVeaceslav Falico 	/* change the marker subtype to marker response */
19671da177e4SLinus Torvalds 	marker.tlv_type = AD_MARKER_RESPONSE_SUBTYPE;
19681da177e4SLinus Torvalds 
19693bf2d28aSVeaceslav Falico 	/* send the marker response */
197017720981SJarod Wilson 	if (ad_marker_send(port, &marker) >= 0)
197117720981SJarod Wilson 		slave_dbg(port->slave->bond->dev, port->slave->dev,
197217720981SJarod Wilson 			  "Sent Marker Response on port %d\n",
1973a4aee5c8SJoe Perches 			  port->actor_port_number);
19741da177e4SLinus Torvalds }
19751da177e4SLinus Torvalds 
19761da177e4SLinus Torvalds /**
19771da177e4SLinus Torvalds  * ad_marker_response_received - handle receive of a marker response frame
19781da177e4SLinus Torvalds  * @marker: marker PDU received
19791da177e4SLinus Torvalds  * @port: the port we're looking at
19801da177e4SLinus Torvalds  *
19811da177e4SLinus Torvalds  * This function does nothing since we decided not to implement send and handle
19821da177e4SLinus Torvalds  * response for marker PDU's, in this stage, but only to respond to marker
19831da177e4SLinus Torvalds  * information.
19841da177e4SLinus Torvalds  */
ad_marker_response_received(struct bond_marker * marker,struct port * port)19851c3f0b8eSMathieu Desnoyers static void ad_marker_response_received(struct bond_marker *marker,
19861c3f0b8eSMathieu Desnoyers 					struct port *port)
19871da177e4SLinus Torvalds {
1988267c095aSNikolay Aleksandrov 	atomic64_inc(&SLAVE_AD_INFO(port->slave)->stats.marker_resp_rx);
1989949e7ceaSNikolay Aleksandrov 	atomic64_inc(&BOND_AD_INFO(port->slave->bond).stats.marker_resp_rx);
1990267c095aSNikolay Aleksandrov 
19913bf2d28aSVeaceslav Falico 	/* DO NOTHING, SINCE WE DECIDED NOT TO IMPLEMENT THIS FEATURE FOR NOW */
19921da177e4SLinus Torvalds }
19931da177e4SLinus Torvalds 
19943bf2d28aSVeaceslav Falico /* ========= AD exported functions to the main bonding code ========= */
19951da177e4SLinus Torvalds 
19963bf2d28aSVeaceslav Falico /* Check aggregators status in team every T seconds */
19971da177e4SLinus Torvalds #define AD_AGGREGATOR_SELECTION_TIMER  8
19981da177e4SLinus Torvalds 
19993bf2d28aSVeaceslav Falico /**
20003bf2d28aSVeaceslav Falico  * bond_3ad_initiate_agg_selection - initate aggregator selection
20013bf2d28aSVeaceslav Falico  * @bond: bonding struct
2002a35e5478SLee Jones  * @timeout: timeout value to set
2003fd989c83SJay Vosburgh  *
2004fd989c83SJay Vosburgh  * Set the aggregation selection timer, to initiate an agg selection in
2005fd989c83SJay Vosburgh  * the very near future.  Called during first initialization, and during
2006fd989c83SJay Vosburgh  * any down to up transitions of the bond.
2007fd989c83SJay Vosburgh  */
bond_3ad_initiate_agg_selection(struct bonding * bond,int timeout)2008fd989c83SJay Vosburgh void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout)
2009fd989c83SJay Vosburgh {
20109ceaf6f7SEric Dumazet 	atomic_set(&BOND_AD_INFO(bond).agg_select_timer, timeout);
2011fd989c83SJay Vosburgh }
2012fd989c83SJay Vosburgh 
20131da177e4SLinus Torvalds /**
20141da177e4SLinus Torvalds  * bond_3ad_initialize - initialize a bond's 802.3ad parameters and structures
20151da177e4SLinus Torvalds  * @bond: bonding struct to work on
20161da177e4SLinus Torvalds  *
20171da177e4SLinus Torvalds  * Can be called only after the mac address of the bond is set.
20181da177e4SLinus Torvalds  */
bond_3ad_initialize(struct bonding * bond)2019f2e44dffSJonathan Toppins void bond_3ad_initialize(struct bonding *bond)
20201da177e4SLinus Torvalds {
2021163c8ff3SJiri Bohac 	BOND_AD_INFO(bond).aggregator_identifier = 0;
20226791e466SMahesh Bandewar 	BOND_AD_INFO(bond).system.sys_priority =
20236791e466SMahesh Bandewar 		bond->params.ad_actor_sys_prio;
202474514957SMahesh Bandewar 	if (is_zero_ether_addr(bond->params.ad_actor_system))
202574514957SMahesh Bandewar 		BOND_AD_INFO(bond).system.sys_mac_addr =
202674514957SMahesh Bandewar 		    *((struct mac_addr *)bond->dev->dev_addr);
202774514957SMahesh Bandewar 	else
202874514957SMahesh Bandewar 		BOND_AD_INFO(bond).system.sys_mac_addr =
202974514957SMahesh Bandewar 		    *((struct mac_addr *)bond->params.ad_actor_system);
20301da177e4SLinus Torvalds 
2031fd989c83SJay Vosburgh 	bond_3ad_initiate_agg_selection(bond,
2032fd989c83SJay Vosburgh 					AD_AGGREGATOR_SELECTION_TIMER *
2033fd989c83SJay Vosburgh 					ad_ticks_per_sec);
20341da177e4SLinus Torvalds }
20351da177e4SLinus Torvalds 
20361da177e4SLinus Torvalds /**
20371da177e4SLinus Torvalds  * bond_3ad_bind_slave - initialize a slave's port
20381da177e4SLinus Torvalds  * @slave: slave struct to work on
20391da177e4SLinus Torvalds  *
20401da177e4SLinus Torvalds  * Returns:   0 on success
20411da177e4SLinus Torvalds  *          < 0 on error
20421da177e4SLinus Torvalds  */
bond_3ad_bind_slave(struct slave * slave)2043359632e5Sdingtianhong void bond_3ad_bind_slave(struct slave *slave)
20441da177e4SLinus Torvalds {
20451da177e4SLinus Torvalds 	struct bonding *bond = bond_get_bond_by_slave(slave);
20461da177e4SLinus Torvalds 	struct port *port;
20471da177e4SLinus Torvalds 	struct aggregator *aggregator;
20481da177e4SLinus Torvalds 
2049359632e5Sdingtianhong 	/* check that the slave has not been initialized yet. */
20503fdddd85Sdingtianhong 	if (SLAVE_AD_INFO(slave)->port.slave != slave) {
20511da177e4SLinus Torvalds 
2052359632e5Sdingtianhong 		/* port initialization */
20533fdddd85Sdingtianhong 		port = &(SLAVE_AD_INFO(slave)->port);
20541da177e4SLinus Torvalds 
2055bf0239a9SPeter Pan(潘卫平) 		ad_initialize_port(port, bond->params.lacp_fast);
20561da177e4SLinus Torvalds 
20571da177e4SLinus Torvalds 		port->slave = slave;
20583fdddd85Sdingtianhong 		port->actor_port_number = SLAVE_AD_INFO(slave)->id;
2059d22a5fc0SMahesh Bandewar 		/* key is determined according to the link speed, duplex and
2060d22a5fc0SMahesh Bandewar 		 * user key
2061359632e5Sdingtianhong 		 */
2062d22a5fc0SMahesh Bandewar 		port->actor_admin_port_key = bond->params.ad_user_port_key << 6;
20637bb11dc9SMahesh Bandewar 		ad_update_actor_keys(port, false);
2064359632e5Sdingtianhong 		/* actor system is the bond's system */
20655ee14e6dSNikolay Aleksandrov 		__ad_actor_update_port(port);
20663bf2d28aSVeaceslav Falico 		/* tx timer(to verify that no more than MAX_TX_IN_SECOND
20673bf2d28aSVeaceslav Falico 		 * lacpdu's are sent in one second)
20683bf2d28aSVeaceslav Falico 		 */
20691da177e4SLinus Torvalds 		port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND;
20701da177e4SLinus Torvalds 
20711da177e4SLinus Torvalds 		__disable_port(port);
20721da177e4SLinus Torvalds 
2073359632e5Sdingtianhong 		/* aggregator initialization */
20743fdddd85Sdingtianhong 		aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
20751da177e4SLinus Torvalds 
20761da177e4SLinus Torvalds 		ad_initialize_agg(aggregator);
20771da177e4SLinus Torvalds 
20781da177e4SLinus Torvalds 		aggregator->aggregator_mac_address = *((struct mac_addr *)bond->dev->dev_addr);
2079163c8ff3SJiri Bohac 		aggregator->aggregator_identifier = ++BOND_AD_INFO(bond).aggregator_identifier;
20801da177e4SLinus Torvalds 		aggregator->slave = slave;
20811da177e4SLinus Torvalds 		aggregator->is_active = 0;
20821da177e4SLinus Torvalds 		aggregator->num_of_ports = 0;
20831da177e4SLinus Torvalds 	}
20841da177e4SLinus Torvalds }
20851da177e4SLinus Torvalds 
20861da177e4SLinus Torvalds /**
20871da177e4SLinus Torvalds  * bond_3ad_unbind_slave - deinitialize a slave's port
20881da177e4SLinus Torvalds  * @slave: slave struct to work on
20891da177e4SLinus Torvalds  *
20901da177e4SLinus Torvalds  * Search for the aggregator that is related to this port, remove the
20911da177e4SLinus Torvalds  * aggregator and assign another aggregator for other port related to it
20921da177e4SLinus Torvalds  * (if any), and remove the port.
20931da177e4SLinus Torvalds  */
bond_3ad_unbind_slave(struct slave * slave)20941da177e4SLinus Torvalds void bond_3ad_unbind_slave(struct slave *slave)
20951da177e4SLinus Torvalds {
20961da177e4SLinus Torvalds 	struct port *port, *prev_port, *temp_port;
20971da177e4SLinus Torvalds 	struct aggregator *aggregator, *new_aggregator, *temp_aggregator;
20981da177e4SLinus Torvalds 	int select_new_active_agg = 0;
20990b088264SVeaceslav Falico 	struct bonding *bond = slave->bond;
21000b088264SVeaceslav Falico 	struct slave *slave_iter;
21010b088264SVeaceslav Falico 	struct list_head *iter;
2102ee637714SMahesh Bandewar 	bool dummy_slave_update; /* Ignore this value as caller updates array */
21031da177e4SLinus Torvalds 
2104e470259fSNikolay Aleksandrov 	/* Sync against bond_3ad_state_machine_handler() */
2105e470259fSNikolay Aleksandrov 	spin_lock_bh(&bond->mode_lock);
21063fdddd85Sdingtianhong 	aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
21073fdddd85Sdingtianhong 	port = &(SLAVE_AD_INFO(slave)->port);
21081da177e4SLinus Torvalds 
21093bf2d28aSVeaceslav Falico 	/* if slave is null, the whole port is not initialized */
21101da177e4SLinus Torvalds 	if (!port->slave) {
211117720981SJarod Wilson 		slave_warn(bond->dev, slave->dev, "Trying to unbind an uninitialized port\n");
2112e470259fSNikolay Aleksandrov 		goto out;
21131da177e4SLinus Torvalds 	}
21141da177e4SLinus Torvalds 
211517720981SJarod Wilson 	slave_dbg(bond->dev, slave->dev, "Unbinding Link Aggregation Group %d\n",
2116a4aee5c8SJoe Perches 		  aggregator->aggregator_identifier);
21171da177e4SLinus Torvalds 
21181da177e4SLinus Torvalds 	/* Tell the partner that this port is not suitable for aggregation */
2119c1e46990SAndy Roulin 	port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION;
2120c1e46990SAndy Roulin 	port->actor_oper_port_state &= ~LACP_STATE_COLLECTING;
2121c1e46990SAndy Roulin 	port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING;
2122c1e46990SAndy Roulin 	port->actor_oper_port_state &= ~LACP_STATE_AGGREGATION;
21231da177e4SLinus Torvalds 	__update_lacpdu_from_port(port);
21241da177e4SLinus Torvalds 	ad_lacpdu_send(port);
21251da177e4SLinus Torvalds 
21263bf2d28aSVeaceslav Falico 	/* check if this aggregator is occupied */
21271da177e4SLinus Torvalds 	if (aggregator->lag_ports) {
21283bf2d28aSVeaceslav Falico 		/* check if there are other ports related to this aggregator
21293bf2d28aSVeaceslav Falico 		 * except the port related to this slave(thats ensure us that
21303bf2d28aSVeaceslav Falico 		 * there is a reason to search for new aggregator, and that we
21313bf2d28aSVeaceslav Falico 		 * will find one
21323bf2d28aSVeaceslav Falico 		 */
21333bf2d28aSVeaceslav Falico 		if ((aggregator->lag_ports != port) ||
21343bf2d28aSVeaceslav Falico 		    (aggregator->lag_ports->next_port_in_aggregator)) {
21353bf2d28aSVeaceslav Falico 			/* find new aggregator for the related port(s) */
21360b088264SVeaceslav Falico 			bond_for_each_slave(bond, slave_iter, iter) {
21373fdddd85Sdingtianhong 				new_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator);
21383bf2d28aSVeaceslav Falico 				/* if the new aggregator is empty, or it is
21393bf2d28aSVeaceslav Falico 				 * connected to our port only
21403bf2d28aSVeaceslav Falico 				 */
21413bf2d28aSVeaceslav Falico 				if (!new_aggregator->lag_ports ||
21423bf2d28aSVeaceslav Falico 				    ((new_aggregator->lag_ports == port) &&
21433bf2d28aSVeaceslav Falico 				     !new_aggregator->lag_ports->next_port_in_aggregator))
21441da177e4SLinus Torvalds 					break;
21451da177e4SLinus Torvalds 			}
21460b088264SVeaceslav Falico 			if (!slave_iter)
21470b088264SVeaceslav Falico 				new_aggregator = NULL;
21483bf2d28aSVeaceslav Falico 
21493bf2d28aSVeaceslav Falico 			/* if new aggregator found, copy the aggregator's
21503bf2d28aSVeaceslav Falico 			 * parameters and connect the related lag_ports to the
21513bf2d28aSVeaceslav Falico 			 * new aggregator
21523bf2d28aSVeaceslav Falico 			 */
21531da177e4SLinus Torvalds 			if ((new_aggregator) && ((!new_aggregator->lag_ports) || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator))) {
215417720981SJarod Wilson 				slave_dbg(bond->dev, slave->dev, "Some port(s) related to LAG %d - replacing with LAG %d\n",
2155a4aee5c8SJoe Perches 					  aggregator->aggregator_identifier,
2156a4aee5c8SJoe Perches 					  new_aggregator->aggregator_identifier);
21571da177e4SLinus Torvalds 
21583bf2d28aSVeaceslav Falico 				if ((new_aggregator->lag_ports == port) &&
21593bf2d28aSVeaceslav Falico 				    new_aggregator->is_active) {
216017720981SJarod Wilson 					slave_info(bond->dev, slave->dev, "Removing an active aggregator\n");
21611da177e4SLinus Torvalds 					select_new_active_agg = 1;
21621da177e4SLinus Torvalds 				}
21631da177e4SLinus Torvalds 
21641da177e4SLinus Torvalds 				new_aggregator->is_individual = aggregator->is_individual;
21651da177e4SLinus Torvalds 				new_aggregator->actor_admin_aggregator_key = aggregator->actor_admin_aggregator_key;
21661da177e4SLinus Torvalds 				new_aggregator->actor_oper_aggregator_key = aggregator->actor_oper_aggregator_key;
21671da177e4SLinus Torvalds 				new_aggregator->partner_system = aggregator->partner_system;
21681da177e4SLinus Torvalds 				new_aggregator->partner_system_priority = aggregator->partner_system_priority;
21691da177e4SLinus Torvalds 				new_aggregator->partner_oper_aggregator_key = aggregator->partner_oper_aggregator_key;
21701da177e4SLinus Torvalds 				new_aggregator->receive_state = aggregator->receive_state;
21711da177e4SLinus Torvalds 				new_aggregator->transmit_state = aggregator->transmit_state;
21721da177e4SLinus Torvalds 				new_aggregator->lag_ports = aggregator->lag_ports;
21731da177e4SLinus Torvalds 				new_aggregator->is_active = aggregator->is_active;
21741da177e4SLinus Torvalds 				new_aggregator->num_of_ports = aggregator->num_of_ports;
21751da177e4SLinus Torvalds 
21763bf2d28aSVeaceslav Falico 				/* update the information that is written on
21773bf2d28aSVeaceslav Falico 				 * the ports about the aggregator
21783bf2d28aSVeaceslav Falico 				 */
2179128ea6c3SBandan Das 				for (temp_port = aggregator->lag_ports; temp_port;
2180128ea6c3SBandan Das 				     temp_port = temp_port->next_port_in_aggregator) {
21811da177e4SLinus Torvalds 					temp_port->aggregator = new_aggregator;
21821da177e4SLinus Torvalds 					temp_port->actor_port_aggregator_identifier = new_aggregator->aggregator_identifier;
21831da177e4SLinus Torvalds 				}
21841da177e4SLinus Torvalds 
21851da177e4SLinus Torvalds 				ad_clear_agg(aggregator);
21861da177e4SLinus Torvalds 
21877bfc4753SBandan Das 				if (select_new_active_agg)
2188ee637714SMahesh Bandewar 					ad_agg_selection_logic(__get_first_agg(port),
2189ee637714SMahesh Bandewar 							       &dummy_slave_update);
21901da177e4SLinus Torvalds 			} else {
219117720981SJarod Wilson 				slave_warn(bond->dev, slave->dev, "unbinding aggregator, and could not find a new aggregator for its ports\n");
21921da177e4SLinus Torvalds 			}
21933bf2d28aSVeaceslav Falico 		} else {
21943bf2d28aSVeaceslav Falico 			/* in case that the only port related to this
21953bf2d28aSVeaceslav Falico 			 * aggregator is the one we want to remove
21963bf2d28aSVeaceslav Falico 			 */
21971da177e4SLinus Torvalds 			select_new_active_agg = aggregator->is_active;
21981da177e4SLinus Torvalds 			ad_clear_agg(aggregator);
21991da177e4SLinus Torvalds 			if (select_new_active_agg) {
220017720981SJarod Wilson 				slave_info(bond->dev, slave->dev, "Removing an active aggregator\n");
22013bf2d28aSVeaceslav Falico 				/* select new active aggregator */
220274684493SVeaceslav Falico 				temp_aggregator = __get_first_agg(port);
220374684493SVeaceslav Falico 				if (temp_aggregator)
2204ee637714SMahesh Bandewar 					ad_agg_selection_logic(temp_aggregator,
2205ee637714SMahesh Bandewar 							       &dummy_slave_update);
22061da177e4SLinus Torvalds 			}
22071da177e4SLinus Torvalds 		}
22081da177e4SLinus Torvalds 	}
22091da177e4SLinus Torvalds 
221017720981SJarod Wilson 	slave_dbg(bond->dev, slave->dev, "Unbinding port %d\n", port->actor_port_number);
22113bf2d28aSVeaceslav Falico 
22123bf2d28aSVeaceslav Falico 	/* find the aggregator that this port is connected to */
22130b088264SVeaceslav Falico 	bond_for_each_slave(bond, slave_iter, iter) {
22143fdddd85Sdingtianhong 		temp_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator);
22151da177e4SLinus Torvalds 		prev_port = NULL;
22163bf2d28aSVeaceslav Falico 		/* search the port in the aggregator's related ports */
2217128ea6c3SBandan Das 		for (temp_port = temp_aggregator->lag_ports; temp_port;
2218128ea6c3SBandan Das 		     prev_port = temp_port,
2219128ea6c3SBandan Das 		     temp_port = temp_port->next_port_in_aggregator) {
22203bf2d28aSVeaceslav Falico 			if (temp_port == port) {
22213bf2d28aSVeaceslav Falico 				/* the aggregator found - detach the port from
22223bf2d28aSVeaceslav Falico 				 * this aggregator
22233bf2d28aSVeaceslav Falico 				 */
22247bfc4753SBandan Das 				if (prev_port)
22251da177e4SLinus Torvalds 					prev_port->next_port_in_aggregator = temp_port->next_port_in_aggregator;
22267bfc4753SBandan Das 				else
22271da177e4SLinus Torvalds 					temp_aggregator->lag_ports = temp_port->next_port_in_aggregator;
22281da177e4SLinus Torvalds 				temp_aggregator->num_of_ports--;
22290622cab0SJay Vosburgh 				if (__agg_active_ports(temp_aggregator) == 0) {
22301da177e4SLinus Torvalds 					select_new_active_agg = temp_aggregator->is_active;
2231050133e1SYevhen Orlov 					if (temp_aggregator->num_of_ports == 0)
22321da177e4SLinus Torvalds 						ad_clear_agg(temp_aggregator);
22331da177e4SLinus Torvalds 					if (select_new_active_agg) {
223417720981SJarod Wilson 						slave_info(bond->dev, slave->dev, "Removing an active aggregator\n");
22353bf2d28aSVeaceslav Falico 						/* select new active aggregator */
2236ee637714SMahesh Bandewar 						ad_agg_selection_logic(__get_first_agg(port),
2237ee637714SMahesh Bandewar 							               &dummy_slave_update);
22381da177e4SLinus Torvalds 					}
22391da177e4SLinus Torvalds 				}
22401da177e4SLinus Torvalds 				break;
22411da177e4SLinus Torvalds 			}
22421da177e4SLinus Torvalds 		}
22431da177e4SLinus Torvalds 	}
22441da177e4SLinus Torvalds 	port->slave = NULL;
2245e470259fSNikolay Aleksandrov 
2246e470259fSNikolay Aleksandrov out:
2247e470259fSNikolay Aleksandrov 	spin_unlock_bh(&bond->mode_lock);
22481da177e4SLinus Torvalds }
22491da177e4SLinus Torvalds 
22501da177e4SLinus Torvalds /**
22515ee14e6dSNikolay Aleksandrov  * bond_3ad_update_ad_actor_settings - reflect change of actor settings to ports
22525ee14e6dSNikolay Aleksandrov  * @bond: bonding struct to work on
22535ee14e6dSNikolay Aleksandrov  *
22545ee14e6dSNikolay Aleksandrov  * If an ad_actor setting gets changed we need to update the individual port
22555ee14e6dSNikolay Aleksandrov  * settings so the bond device will use the new values when it gets upped.
22565ee14e6dSNikolay Aleksandrov  */
bond_3ad_update_ad_actor_settings(struct bonding * bond)22575ee14e6dSNikolay Aleksandrov void bond_3ad_update_ad_actor_settings(struct bonding *bond)
22585ee14e6dSNikolay Aleksandrov {
22595ee14e6dSNikolay Aleksandrov 	struct list_head *iter;
22605ee14e6dSNikolay Aleksandrov 	struct slave *slave;
22615ee14e6dSNikolay Aleksandrov 
22625ee14e6dSNikolay Aleksandrov 	ASSERT_RTNL();
22635ee14e6dSNikolay Aleksandrov 
22645ee14e6dSNikolay Aleksandrov 	BOND_AD_INFO(bond).system.sys_priority = bond->params.ad_actor_sys_prio;
22655ee14e6dSNikolay Aleksandrov 	if (is_zero_ether_addr(bond->params.ad_actor_system))
22665ee14e6dSNikolay Aleksandrov 		BOND_AD_INFO(bond).system.sys_mac_addr =
22675ee14e6dSNikolay Aleksandrov 		    *((struct mac_addr *)bond->dev->dev_addr);
22685ee14e6dSNikolay Aleksandrov 	else
22695ee14e6dSNikolay Aleksandrov 		BOND_AD_INFO(bond).system.sys_mac_addr =
22705ee14e6dSNikolay Aleksandrov 		    *((struct mac_addr *)bond->params.ad_actor_system);
22715ee14e6dSNikolay Aleksandrov 
22725ee14e6dSNikolay Aleksandrov 	spin_lock_bh(&bond->mode_lock);
22737f20cd25SNikolay Aleksandrov 	bond_for_each_slave(bond, slave, iter) {
22747f20cd25SNikolay Aleksandrov 		struct port *port = &(SLAVE_AD_INFO(slave))->port;
22757f20cd25SNikolay Aleksandrov 
22767f20cd25SNikolay Aleksandrov 		__ad_actor_update_port(port);
22777f20cd25SNikolay Aleksandrov 		port->ntt = true;
22787f20cd25SNikolay Aleksandrov 	}
22795ee14e6dSNikolay Aleksandrov 	spin_unlock_bh(&bond->mode_lock);
22805ee14e6dSNikolay Aleksandrov }
22815ee14e6dSNikolay Aleksandrov 
22825ee14e6dSNikolay Aleksandrov /**
22839ceaf6f7SEric Dumazet  * bond_agg_timer_advance - advance agg_select_timer
22849ceaf6f7SEric Dumazet  * @bond:  bonding structure
22859ceaf6f7SEric Dumazet  *
22869ceaf6f7SEric Dumazet  * Return true when agg_select_timer reaches 0.
22879ceaf6f7SEric Dumazet  */
bond_agg_timer_advance(struct bonding * bond)22889ceaf6f7SEric Dumazet static bool bond_agg_timer_advance(struct bonding *bond)
22899ceaf6f7SEric Dumazet {
22909ceaf6f7SEric Dumazet 	int val, nval;
22919ceaf6f7SEric Dumazet 
22929ceaf6f7SEric Dumazet 	while (1) {
22939ceaf6f7SEric Dumazet 		val = atomic_read(&BOND_AD_INFO(bond).agg_select_timer);
22949ceaf6f7SEric Dumazet 		if (!val)
22959ceaf6f7SEric Dumazet 			return false;
22969ceaf6f7SEric Dumazet 		nval = val - 1;
22979ceaf6f7SEric Dumazet 		if (atomic_cmpxchg(&BOND_AD_INFO(bond).agg_select_timer,
22989ceaf6f7SEric Dumazet 				   val, nval) == val)
22999ceaf6f7SEric Dumazet 			break;
23009ceaf6f7SEric Dumazet 	}
23019ceaf6f7SEric Dumazet 	return nval == 0;
23029ceaf6f7SEric Dumazet }
23039ceaf6f7SEric Dumazet 
23049ceaf6f7SEric Dumazet /**
23051da177e4SLinus Torvalds  * bond_3ad_state_machine_handler - handle state machines timeout
2306a35e5478SLee Jones  * @work: work context to fetch bonding struct to work on from
23071da177e4SLinus Torvalds  *
23081da177e4SLinus Torvalds  * The state machine handling concept in this module is to check every tick
23091da177e4SLinus Torvalds  * which state machine should operate any function. The execution order is
23101da177e4SLinus Torvalds  * round robin, so when we have an interaction between state machines, the
23111da177e4SLinus Torvalds  * reply of one to each other might be delayed until next tick.
23121da177e4SLinus Torvalds  *
23131da177e4SLinus Torvalds  * This function also complete the initialization when the agg_select_timer
23141da177e4SLinus Torvalds  * times out, and it selects an aggregator for the ports that are yet not
23151da177e4SLinus Torvalds  * related to any aggregator, and selects the active aggregator for a bond.
23161da177e4SLinus Torvalds  */
bond_3ad_state_machine_handler(struct work_struct * work)23171b76b316SJay Vosburgh void bond_3ad_state_machine_handler(struct work_struct *work)
23181da177e4SLinus Torvalds {
23191b76b316SJay Vosburgh 	struct bonding *bond = container_of(work, struct bonding,
23201b76b316SJay Vosburgh 					    ad_work.work);
23211da177e4SLinus Torvalds 	struct aggregator *aggregator;
23223c4c88a1SVeaceslav Falico 	struct list_head *iter;
23233c4c88a1SVeaceslav Falico 	struct slave *slave;
23243c4c88a1SVeaceslav Falico 	struct port *port;
23255e5b0665Sdingtianhong 	bool should_notify_rtnl = BOND_SLAVE_NOTIFY_LATER;
2326ee637714SMahesh Bandewar 	bool update_slave_arr = false;
23271da177e4SLinus Torvalds 
2328e470259fSNikolay Aleksandrov 	/* Lock to protect data accessed by all (e.g., port->sm_vars) and
2329e470259fSNikolay Aleksandrov 	 * against running with bond_3ad_unbind_slave. ad_rx_machine may run
2330e470259fSNikolay Aleksandrov 	 * concurrently due to incoming LACPDU as well.
2331e470259fSNikolay Aleksandrov 	 */
2332b7435628SNikolay Aleksandrov 	spin_lock_bh(&bond->mode_lock);
2333be79bd04Sdingtianhong 	rcu_read_lock();
23341f2cd845SDavid S. Miller 
2335be79bd04Sdingtianhong 	/* check if there are any slaves */
23360965a1f3SVeaceslav Falico 	if (!bond_has_slaves(bond))
23371da177e4SLinus Torvalds 		goto re_arm;
23381da177e4SLinus Torvalds 
23399ceaf6f7SEric Dumazet 	if (bond_agg_timer_advance(bond)) {
2340be79bd04Sdingtianhong 		slave = bond_first_slave_rcu(bond);
23413fdddd85Sdingtianhong 		port = slave ? &(SLAVE_AD_INFO(slave)->port) : NULL;
2342fe9323daSVeaceslav Falico 
2343be79bd04Sdingtianhong 		/* select the active aggregator for the bond */
2344fe9323daSVeaceslav Falico 		if (port) {
23451da177e4SLinus Torvalds 			if (!port->slave) {
2346d4471f5eSVeaceslav Falico 				net_warn_ratelimited("%s: Warning: bond's first port is uninitialized\n",
2347a4aee5c8SJoe Perches 						     bond->dev->name);
23481da177e4SLinus Torvalds 				goto re_arm;
23491da177e4SLinus Torvalds 			}
23501da177e4SLinus Torvalds 
23511da177e4SLinus Torvalds 			aggregator = __get_first_agg(port);
2352ee637714SMahesh Bandewar 			ad_agg_selection_logic(aggregator, &update_slave_arr);
23531da177e4SLinus Torvalds 		}
2354f0c76d61SJay Vosburgh 		bond_3ad_set_carrier(bond);
23551da177e4SLinus Torvalds 	}
23561da177e4SLinus Torvalds 
2357be79bd04Sdingtianhong 	/* for each port run the state machines */
2358be79bd04Sdingtianhong 	bond_for_each_slave_rcu(bond, slave, iter) {
23593fdddd85Sdingtianhong 		port = &(SLAVE_AD_INFO(slave)->port);
23601da177e4SLinus Torvalds 		if (!port->slave) {
2361d4471f5eSVeaceslav Falico 			net_warn_ratelimited("%s: Warning: Found an uninitialized port\n",
2362a4aee5c8SJoe Perches 					    bond->dev->name);
23631da177e4SLinus Torvalds 			goto re_arm;
23641da177e4SLinus Torvalds 		}
23651da177e4SLinus Torvalds 
23661da177e4SLinus Torvalds 		ad_rx_machine(NULL, port);
2367bbef56d8SColin Ian King 		ad_periodic_machine(port, &bond->params);
2368ee637714SMahesh Bandewar 		ad_port_selection_logic(port, &update_slave_arr);
2369ee637714SMahesh Bandewar 		ad_mux_machine(port, &update_slave_arr);
23701da177e4SLinus Torvalds 		ad_tx_machine(port);
237114c9551aSMahesh Bandewar 		ad_churn_machine(port);
23721da177e4SLinus Torvalds 
2373be79bd04Sdingtianhong 		/* turn off the BEGIN bit, since we already handled it */
23747bfc4753SBandan Das 		if (port->sm_vars & AD_PORT_BEGIN)
23751da177e4SLinus Torvalds 			port->sm_vars &= ~AD_PORT_BEGIN;
23761da177e4SLinus Torvalds 	}
23771da177e4SLinus Torvalds 
23781da177e4SLinus Torvalds re_arm:
23795e5b0665Sdingtianhong 	bond_for_each_slave_rcu(bond, slave, iter) {
23805e5b0665Sdingtianhong 		if (slave->should_notify) {
23815e5b0665Sdingtianhong 			should_notify_rtnl = BOND_SLAVE_NOTIFY_NOW;
23825e5b0665Sdingtianhong 			break;
23835e5b0665Sdingtianhong 		}
23845e5b0665Sdingtianhong 	}
2385be79bd04Sdingtianhong 	rcu_read_unlock();
2386b7435628SNikolay Aleksandrov 	spin_unlock_bh(&bond->mode_lock);
23875e5b0665Sdingtianhong 
2388ee637714SMahesh Bandewar 	if (update_slave_arr)
2389ee637714SMahesh Bandewar 		bond_slave_arr_work_rearm(bond, 0);
2390ee637714SMahesh Bandewar 
23915e5b0665Sdingtianhong 	if (should_notify_rtnl && rtnl_trylock()) {
2392b0929915Sdingtianhong 		bond_slave_state_notify(bond);
23935e5b0665Sdingtianhong 		rtnl_unlock();
23945e5b0665Sdingtianhong 	}
2395be79bd04Sdingtianhong 	queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks);
23961da177e4SLinus Torvalds }
23971da177e4SLinus Torvalds 
23981da177e4SLinus Torvalds /**
23991da177e4SLinus Torvalds  * bond_3ad_rx_indication - handle a received frame
24001da177e4SLinus Torvalds  * @lacpdu: received lacpdu
24011da177e4SLinus Torvalds  * @slave: slave struct to work on
24021da177e4SLinus Torvalds  *
24031da177e4SLinus Torvalds  * It is assumed that frames that were sent on this NIC don't returned as new
24041da177e4SLinus Torvalds  * received frames (loopback). Since only the payload is given to this
24051da177e4SLinus Torvalds  * function, it check for loopback.
24061da177e4SLinus Torvalds  */
bond_3ad_rx_indication(struct lacpdu * lacpdu,struct slave * slave)2407dadeb61dSNikolay Aleksandrov static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave)
24081da177e4SLinus Torvalds {
2409949e7ceaSNikolay Aleksandrov 	struct bonding *bond = slave->bond;
241013a8e0c8SJiri Bohac 	int ret = RX_HANDLER_ANOTHER;
24113d021715SNikolay Aleksandrov 	struct bond_marker *marker;
24123d021715SNikolay Aleksandrov 	struct port *port;
2413267c095aSNikolay Aleksandrov 	atomic64_t *stat;
24141da177e4SLinus Torvalds 
24153fdddd85Sdingtianhong 	port = &(SLAVE_AD_INFO(slave)->port);
24161da177e4SLinus Torvalds 	if (!port->slave) {
2417d4471f5eSVeaceslav Falico 		net_warn_ratelimited("%s: Warning: port of slave %s is uninitialized\n",
2418471cb5a3SJiri Pirko 				     slave->dev->name, slave->bond->dev->name);
241913a8e0c8SJiri Bohac 		return ret;
24201da177e4SLinus Torvalds 	}
24211da177e4SLinus Torvalds 
24221da177e4SLinus Torvalds 	switch (lacpdu->subtype) {
24231da177e4SLinus Torvalds 	case AD_TYPE_LACPDU:
242413a8e0c8SJiri Bohac 		ret = RX_HANDLER_CONSUMED;
242517720981SJarod Wilson 		slave_dbg(slave->bond->dev, slave->dev,
242617720981SJarod Wilson 			  "Received LACPDU on port %d\n",
242717720981SJarod Wilson 			  port->actor_port_number);
242816d79d7dSNils Carlson 		/* Protect against concurrent state machines */
2429e470259fSNikolay Aleksandrov 		spin_lock(&slave->bond->mode_lock);
24301da177e4SLinus Torvalds 		ad_rx_machine(lacpdu, port);
2431e470259fSNikolay Aleksandrov 		spin_unlock(&slave->bond->mode_lock);
24321da177e4SLinus Torvalds 		break;
24331da177e4SLinus Torvalds 	case AD_TYPE_MARKER:
243413a8e0c8SJiri Bohac 		ret = RX_HANDLER_CONSUMED;
24353bf2d28aSVeaceslav Falico 		/* No need to convert fields to Little Endian since we
24363bf2d28aSVeaceslav Falico 		 * don't use the marker's fields.
24373bf2d28aSVeaceslav Falico 		 */
24383d021715SNikolay Aleksandrov 		marker = (struct bond_marker *)lacpdu;
24393d021715SNikolay Aleksandrov 		switch (marker->tlv_type) {
24401da177e4SLinus Torvalds 		case AD_MARKER_INFORMATION_SUBTYPE:
244117720981SJarod Wilson 			slave_dbg(slave->bond->dev, slave->dev, "Received Marker Information on port %d\n",
2442a4aee5c8SJoe Perches 				  port->actor_port_number);
24433d021715SNikolay Aleksandrov 			ad_marker_info_received(marker, port);
24441da177e4SLinus Torvalds 			break;
24451da177e4SLinus Torvalds 		case AD_MARKER_RESPONSE_SUBTYPE:
244617720981SJarod Wilson 			slave_dbg(slave->bond->dev, slave->dev, "Received Marker Response on port %d\n",
2447a4aee5c8SJoe Perches 				  port->actor_port_number);
24483d021715SNikolay Aleksandrov 			ad_marker_response_received(marker, port);
24491da177e4SLinus Torvalds 			break;
24501da177e4SLinus Torvalds 		default:
245117720981SJarod Wilson 			slave_dbg(slave->bond->dev, slave->dev, "Received an unknown Marker subtype on port %d\n",
2452a4aee5c8SJoe Perches 				  port->actor_port_number);
2453267c095aSNikolay Aleksandrov 			stat = &SLAVE_AD_INFO(slave)->stats.marker_unknown_rx;
2454267c095aSNikolay Aleksandrov 			atomic64_inc(stat);
2455949e7ceaSNikolay Aleksandrov 			stat = &BOND_AD_INFO(bond).stats.marker_unknown_rx;
2456949e7ceaSNikolay Aleksandrov 			atomic64_inc(stat);
24571da177e4SLinus Torvalds 		}
2458267c095aSNikolay Aleksandrov 		break;
2459267c095aSNikolay Aleksandrov 	default:
2460267c095aSNikolay Aleksandrov 		atomic64_inc(&SLAVE_AD_INFO(slave)->stats.lacpdu_unknown_rx);
2461949e7ceaSNikolay Aleksandrov 		atomic64_inc(&BOND_AD_INFO(bond).stats.lacpdu_unknown_rx);
24621da177e4SLinus Torvalds 	}
24633d021715SNikolay Aleksandrov 
246413a8e0c8SJiri Bohac 	return ret;
24651da177e4SLinus Torvalds }
24661da177e4SLinus Torvalds 
24671da177e4SLinus Torvalds /**
24687bb11dc9SMahesh Bandewar  * ad_update_actor_keys - Update the oper / admin keys for a port based on
24697bb11dc9SMahesh Bandewar  * its current speed and duplex settings.
24707bb11dc9SMahesh Bandewar  *
24717bb11dc9SMahesh Bandewar  * @port: the port we'are looking at
24727bb11dc9SMahesh Bandewar  * @reset: Boolean to just reset the speed and the duplex part of the key
24737bb11dc9SMahesh Bandewar  *
24747bb11dc9SMahesh Bandewar  * The logic to change the oper / admin keys is:
24757bb11dc9SMahesh Bandewar  * (a) A full duplex port can participate in LACP with partner.
24767bb11dc9SMahesh Bandewar  * (b) When the speed is changed, LACP need to be reinitiated.
24777bb11dc9SMahesh Bandewar  */
ad_update_actor_keys(struct port * port,bool reset)24787bb11dc9SMahesh Bandewar static void ad_update_actor_keys(struct port *port, bool reset)
24797bb11dc9SMahesh Bandewar {
24807bb11dc9SMahesh Bandewar 	u8 duplex = 0;
24817bb11dc9SMahesh Bandewar 	u16 ospeed = 0, speed = 0;
24827bb11dc9SMahesh Bandewar 	u16 old_oper_key = port->actor_oper_port_key;
24837bb11dc9SMahesh Bandewar 
24847bb11dc9SMahesh Bandewar 	port->actor_admin_port_key &= ~(AD_SPEED_KEY_MASKS|AD_DUPLEX_KEY_MASKS);
24857bb11dc9SMahesh Bandewar 	if (!reset) {
24867bb11dc9SMahesh Bandewar 		speed = __get_link_speed(port);
24877bb11dc9SMahesh Bandewar 		ospeed = (old_oper_key & AD_SPEED_KEY_MASKS) >> 1;
24887bb11dc9SMahesh Bandewar 		duplex = __get_duplex(port);
24897bb11dc9SMahesh Bandewar 		port->actor_admin_port_key |= (speed << 1) | duplex;
24907bb11dc9SMahesh Bandewar 	}
24917bb11dc9SMahesh Bandewar 	port->actor_oper_port_key = port->actor_admin_port_key;
24927bb11dc9SMahesh Bandewar 
24937bb11dc9SMahesh Bandewar 	if (old_oper_key != port->actor_oper_port_key) {
24947bb11dc9SMahesh Bandewar 		/* Only 'duplex' port participates in LACP */
24957bb11dc9SMahesh Bandewar 		if (duplex)
24967bb11dc9SMahesh Bandewar 			port->sm_vars |= AD_PORT_LACP_ENABLED;
24977bb11dc9SMahesh Bandewar 		else
24987bb11dc9SMahesh Bandewar 			port->sm_vars &= ~AD_PORT_LACP_ENABLED;
24997bb11dc9SMahesh Bandewar 
25007bb11dc9SMahesh Bandewar 		if (!reset) {
25017bb11dc9SMahesh Bandewar 			if (!speed) {
250217720981SJarod Wilson 				slave_err(port->slave->bond->dev,
250317720981SJarod Wilson 					  port->slave->dev,
250417720981SJarod Wilson 					  "speed changed to 0 on port %d\n",
250517720981SJarod Wilson 					  port->actor_port_number);
25067bb11dc9SMahesh Bandewar 			} else if (duplex && ospeed != speed) {
25077bb11dc9SMahesh Bandewar 				/* Speed change restarts LACP state-machine */
25087bb11dc9SMahesh Bandewar 				port->sm_vars |= AD_PORT_BEGIN;
25097bb11dc9SMahesh Bandewar 			}
25107bb11dc9SMahesh Bandewar 		}
25117bb11dc9SMahesh Bandewar 	}
25127bb11dc9SMahesh Bandewar }
25137bb11dc9SMahesh Bandewar 
25147bb11dc9SMahesh Bandewar /**
251552bc6716SMahesh Bandewar  * bond_3ad_adapter_speed_duplex_changed - handle a slave's speed / duplex
251652bc6716SMahesh Bandewar  * change indication
251752bc6716SMahesh Bandewar  *
25181da177e4SLinus Torvalds  * @slave: slave struct to work on
25191da177e4SLinus Torvalds  *
25201da177e4SLinus Torvalds  * Handle reselection of aggregator (if needed) for this port.
25211da177e4SLinus Torvalds  */
bond_3ad_adapter_speed_duplex_changed(struct slave * slave)252252bc6716SMahesh Bandewar void bond_3ad_adapter_speed_duplex_changed(struct slave *slave)
25231da177e4SLinus Torvalds {
25241da177e4SLinus Torvalds 	struct port *port;
25251da177e4SLinus Torvalds 
25263fdddd85Sdingtianhong 	port = &(SLAVE_AD_INFO(slave)->port);
25271da177e4SLinus Torvalds 
252871a06c59Sdingtianhong 	/* if slave is null, the whole port is not initialized */
25291da177e4SLinus Torvalds 	if (!port->slave) {
253017720981SJarod Wilson 		slave_warn(slave->bond->dev, slave->dev,
253117720981SJarod Wilson 			   "speed/duplex changed for uninitialized port\n");
25321da177e4SLinus Torvalds 		return;
25331da177e4SLinus Torvalds 	}
25341da177e4SLinus Torvalds 
2535e470259fSNikolay Aleksandrov 	spin_lock_bh(&slave->bond->mode_lock);
25367bb11dc9SMahesh Bandewar 	ad_update_actor_keys(port, false);
2537e292dcaeSMahesh Bandewar 	spin_unlock_bh(&slave->bond->mode_lock);
253817720981SJarod Wilson 	slave_dbg(slave->bond->dev, slave->dev, "Port %d changed speed/duplex\n",
253917720981SJarod Wilson 		  port->actor_port_number);
25401da177e4SLinus Torvalds }
25411da177e4SLinus Torvalds 
25421da177e4SLinus Torvalds /**
25431da177e4SLinus Torvalds  * bond_3ad_handle_link_change - handle a slave's link status change indication
25441da177e4SLinus Torvalds  * @slave: slave struct to work on
2545a35e5478SLee Jones  * @link: whether the link is now up or down
25461da177e4SLinus Torvalds  *
25471da177e4SLinus Torvalds  * Handle reselection of aggregator (if needed) for this port.
25481da177e4SLinus Torvalds  */
bond_3ad_handle_link_change(struct slave * slave,char link)25491da177e4SLinus Torvalds void bond_3ad_handle_link_change(struct slave *slave, char link)
25501da177e4SLinus Torvalds {
25510622cab0SJay Vosburgh 	struct aggregator *agg;
25521da177e4SLinus Torvalds 	struct port *port;
25530622cab0SJay Vosburgh 	bool dummy;
25541da177e4SLinus Torvalds 
25553fdddd85Sdingtianhong 	port = &(SLAVE_AD_INFO(slave)->port);
25561da177e4SLinus Torvalds 
2557108db736Sdingtianhong 	/* if slave is null, the whole port is not initialized */
25581da177e4SLinus Torvalds 	if (!port->slave) {
255917720981SJarod Wilson 		slave_warn(slave->bond->dev, slave->dev, "link status changed for uninitialized port\n");
25601da177e4SLinus Torvalds 		return;
25611da177e4SLinus Torvalds 	}
25621da177e4SLinus Torvalds 
2563e470259fSNikolay Aleksandrov 	spin_lock_bh(&slave->bond->mode_lock);
2564108db736Sdingtianhong 	/* on link down we are zeroing duplex and speed since
2565108db736Sdingtianhong 	 * some of the adaptors(ce1000.lan) report full duplex/speed
2566108db736Sdingtianhong 	 * instead of N/A(duplex) / 0(speed).
2567108db736Sdingtianhong 	 *
2568108db736Sdingtianhong 	 * on link up we are forcing recheck on the duplex and speed since
2569108db736Sdingtianhong 	 * some of he adaptors(ce1000.lan) report.
2570108db736Sdingtianhong 	 */
25711da177e4SLinus Torvalds 	if (link == BOND_LINK_UP) {
2572f48127b6SHolger Eitzenberger 		port->is_enabled = true;
25737bb11dc9SMahesh Bandewar 		ad_update_actor_keys(port, false);
25741da177e4SLinus Torvalds 	} else {
25751da177e4SLinus Torvalds 		/* link has failed */
2576f48127b6SHolger Eitzenberger 		port->is_enabled = false;
25777bb11dc9SMahesh Bandewar 		ad_update_actor_keys(port, true);
25781da177e4SLinus Torvalds 	}
25790622cab0SJay Vosburgh 	agg = __get_first_agg(port);
25800622cab0SJay Vosburgh 	ad_agg_selection_logic(agg, &dummy);
25810622cab0SJay Vosburgh 
2582e292dcaeSMahesh Bandewar 	spin_unlock_bh(&slave->bond->mode_lock);
2583e292dcaeSMahesh Bandewar 
258417720981SJarod Wilson 	slave_dbg(slave->bond->dev, slave->dev, "Port %d changed link status to %s\n",
2585108db736Sdingtianhong 		  port->actor_port_number,
258690194264SJoe Perches 		  link == BOND_LINK_UP ? "UP" : "DOWN");
2587108db736Sdingtianhong 
2588ee637714SMahesh Bandewar 	/* RTNL is held and mode_lock is released so it's safe
2589ee637714SMahesh Bandewar 	 * to update slave_array here.
2590ee637714SMahesh Bandewar 	 */
2591ee637714SMahesh Bandewar 	bond_update_slave_arr(slave->bond, NULL);
25921da177e4SLinus Torvalds }
25931da177e4SLinus Torvalds 
25943bf2d28aSVeaceslav Falico /**
25953bf2d28aSVeaceslav Falico  * bond_3ad_set_carrier - set link state for bonding master
2596a35e5478SLee Jones  * @bond: bonding structure
25973bf2d28aSVeaceslav Falico  *
25983bf2d28aSVeaceslav Falico  * if we have an active aggregator, we're up, if not, we're down.
25993bf2d28aSVeaceslav Falico  * Presumes that we cannot have an active aggregator if there are
26003bf2d28aSVeaceslav Falico  * no slaves with link up.
2601ff59c456SJay Vosburgh  *
2602031ae4deSJay Vosburgh  * This behavior complies with IEEE 802.3 section 43.3.9.
2603031ae4deSJay Vosburgh  *
2604ff59c456SJay Vosburgh  * Called by bond_set_carrier(). Return zero if carrier state does not
2605ff59c456SJay Vosburgh  * change, nonzero if it does.
2606ff59c456SJay Vosburgh  */
bond_3ad_set_carrier(struct bonding * bond)2607ff59c456SJay Vosburgh int bond_3ad_set_carrier(struct bonding *bond)
2608ff59c456SJay Vosburgh {
2609655f8919Sstephen hemminger 	struct aggregator *active;
2610dec1e90eSnikolay@redhat.com 	struct slave *first_slave;
2611c1bc9644SVeaceslav Falico 	int ret = 1;
2612655f8919Sstephen hemminger 
2613be79bd04Sdingtianhong 	rcu_read_lock();
2614be79bd04Sdingtianhong 	first_slave = bond_first_slave_rcu(bond);
2615c1bc9644SVeaceslav Falico 	if (!first_slave) {
2616c1bc9644SVeaceslav Falico 		ret = 0;
2617c1bc9644SVeaceslav Falico 		goto out;
2618c1bc9644SVeaceslav Falico 	}
26193fdddd85Sdingtianhong 	active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator));
2620655f8919Sstephen hemminger 	if (active) {
2621655f8919Sstephen hemminger 		/* are enough slaves available to consider link up? */
26220622cab0SJay Vosburgh 		if (__agg_active_ports(active) < bond->params.min_links) {
2623655f8919Sstephen hemminger 			if (netif_carrier_ok(bond->dev)) {
2624655f8919Sstephen hemminger 				netif_carrier_off(bond->dev);
2625c1bc9644SVeaceslav Falico 				goto out;
2626655f8919Sstephen hemminger 			}
2627655f8919Sstephen hemminger 		} else if (!netif_carrier_ok(bond->dev)) {
2628ff59c456SJay Vosburgh 			netif_carrier_on(bond->dev);
2629c1bc9644SVeaceslav Falico 			goto out;
2630ff59c456SJay Vosburgh 		}
2631c1bc9644SVeaceslav Falico 	} else if (netif_carrier_ok(bond->dev)) {
2632ff59c456SJay Vosburgh 		netif_carrier_off(bond->dev);
2633ff59c456SJay Vosburgh 	}
2634c1bc9644SVeaceslav Falico out:
2635c1bc9644SVeaceslav Falico 	rcu_read_unlock();
2636c1bc9644SVeaceslav Falico 	return ret;
2637ff59c456SJay Vosburgh }
2638ff59c456SJay Vosburgh 
26391da177e4SLinus Torvalds /**
2640318debd8Snikolay@redhat.com  * __bond_3ad_get_active_agg_info - get information of the active aggregator
26411da177e4SLinus Torvalds  * @bond: bonding struct to work on
26421da177e4SLinus Torvalds  * @ad_info: ad_info struct to fill with the bond's info
26431da177e4SLinus Torvalds  *
26441da177e4SLinus Torvalds  * Returns:   0 on success
26451da177e4SLinus Torvalds  *          < 0 on error
26461da177e4SLinus Torvalds  */
__bond_3ad_get_active_agg_info(struct bonding * bond,struct ad_info * ad_info)2647318debd8Snikolay@redhat.com int __bond_3ad_get_active_agg_info(struct bonding *bond,
2648318debd8Snikolay@redhat.com 				   struct ad_info *ad_info)
26491da177e4SLinus Torvalds {
26501da177e4SLinus Torvalds 	struct aggregator *aggregator = NULL;
26513c4c88a1SVeaceslav Falico 	struct list_head *iter;
26523c4c88a1SVeaceslav Falico 	struct slave *slave;
26531da177e4SLinus Torvalds 	struct port *port;
26541da177e4SLinus Torvalds 
265547e91f56Sdingtianhong 	bond_for_each_slave_rcu(bond, slave, iter) {
26563fdddd85Sdingtianhong 		port = &(SLAVE_AD_INFO(slave)->port);
26571da177e4SLinus Torvalds 		if (port->aggregator && port->aggregator->is_active) {
26581da177e4SLinus Torvalds 			aggregator = port->aggregator;
26591da177e4SLinus Torvalds 			break;
26601da177e4SLinus Torvalds 		}
26611da177e4SLinus Torvalds 	}
26621da177e4SLinus Torvalds 
266321f374c6SJoe Perches 	if (!aggregator)
266421f374c6SJoe Perches 		return -1;
266521f374c6SJoe Perches 
26661da177e4SLinus Torvalds 	ad_info->aggregator_id = aggregator->aggregator_identifier;
2667751da2a6SJarod Wilson 	ad_info->ports = __agg_active_ports(aggregator);
26681da177e4SLinus Torvalds 	ad_info->actor_key = aggregator->actor_oper_aggregator_key;
26691da177e4SLinus Torvalds 	ad_info->partner_key = aggregator->partner_oper_aggregator_key;
26702a7c183bSJoe Perches 	ether_addr_copy(ad_info->partner_system,
26712a7c183bSJoe Perches 			aggregator->partner_system.mac_addr_value);
26721da177e4SLinus Torvalds 	return 0;
26731da177e4SLinus Torvalds }
26741da177e4SLinus Torvalds 
bond_3ad_get_active_agg_info(struct bonding * bond,struct ad_info * ad_info)2675318debd8Snikolay@redhat.com int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
2676318debd8Snikolay@redhat.com {
2677318debd8Snikolay@redhat.com 	int ret;
2678318debd8Snikolay@redhat.com 
267947e91f56Sdingtianhong 	rcu_read_lock();
2680318debd8Snikolay@redhat.com 	ret = __bond_3ad_get_active_agg_info(bond, ad_info);
268147e91f56Sdingtianhong 	rcu_read_unlock();
2682318debd8Snikolay@redhat.com 
2683318debd8Snikolay@redhat.com 	return ret;
2684318debd8Snikolay@redhat.com }
2685318debd8Snikolay@redhat.com 
bond_3ad_lacpdu_recv(const struct sk_buff * skb,struct bonding * bond,struct slave * slave)2686de063b70SEric Dumazet int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
26873aba891dSJiri Pirko 			 struct slave *slave)
26881da177e4SLinus Torvalds {
2689de063b70SEric Dumazet 	struct lacpdu *lacpdu, _lacpdu;
2690de063b70SEric Dumazet 
26913aba891dSJiri Pirko 	if (skb->protocol != PKT_TYPE_LACPDU)
269286e74986SNikolay Aleksandrov 		return RX_HANDLER_ANOTHER;
2693b3053251SNeil Horman 
2694bb54e589SMahesh Bandewar 	if (!MAC_ADDRESS_EQUAL(eth_hdr(skb)->h_dest, lacpdu_mcast_addr))
2695bb54e589SMahesh Bandewar 		return RX_HANDLER_ANOTHER;
2696bb54e589SMahesh Bandewar 
2697de063b70SEric Dumazet 	lacpdu = skb_header_pointer(skb, 0, sizeof(_lacpdu), &_lacpdu);
2698267c095aSNikolay Aleksandrov 	if (!lacpdu) {
2699267c095aSNikolay Aleksandrov 		atomic64_inc(&SLAVE_AD_INFO(slave)->stats.lacpdu_illegal_rx);
2700949e7ceaSNikolay Aleksandrov 		atomic64_inc(&BOND_AD_INFO(bond).stats.lacpdu_illegal_rx);
270186e74986SNikolay Aleksandrov 		return RX_HANDLER_ANOTHER;
2702267c095aSNikolay Aleksandrov 	}
2703ab12811cSAndy Gospodarek 
2704dadeb61dSNikolay Aleksandrov 	return bond_3ad_rx_indication(lacpdu, slave);
27051da177e4SLinus Torvalds }
2706ba824a8bSPeter Pan(潘卫平) 
27073bf2d28aSVeaceslav Falico /**
27083bf2d28aSVeaceslav Falico  * bond_3ad_update_lacp_rate - change the lacp rate
2709a35e5478SLee Jones  * @bond: bonding struct
27103bf2d28aSVeaceslav Falico  *
2711ba824a8bSPeter Pan(潘卫平)  * When modify lacp_rate parameter via sysfs,
2712ba824a8bSPeter Pan(潘卫平)  * update actor_oper_port_state of each port.
2713ba824a8bSPeter Pan(潘卫平)  *
2714e470259fSNikolay Aleksandrov  * Hold bond->mode_lock,
2715ba824a8bSPeter Pan(潘卫平)  * so we can modify port->actor_oper_port_state,
2716ba824a8bSPeter Pan(潘卫平)  * no matter bond is up or down.
2717ba824a8bSPeter Pan(潘卫平)  */
bond_3ad_update_lacp_rate(struct bonding * bond)2718ba824a8bSPeter Pan(潘卫平) void bond_3ad_update_lacp_rate(struct bonding *bond)
2719ba824a8bSPeter Pan(潘卫平) {
2720ba824a8bSPeter Pan(潘卫平) 	struct port *port = NULL;
27219caff1e7SVeaceslav Falico 	struct list_head *iter;
2722c509316bSnikolay@redhat.com 	struct slave *slave;
2723ba824a8bSPeter Pan(潘卫平) 	int lacp_fast;
2724ba824a8bSPeter Pan(潘卫平) 
2725ba824a8bSPeter Pan(潘卫平) 	lacp_fast = bond->params.lacp_fast;
2726e470259fSNikolay Aleksandrov 	spin_lock_bh(&bond->mode_lock);
27279caff1e7SVeaceslav Falico 	bond_for_each_slave(bond, slave, iter) {
27283fdddd85Sdingtianhong 		port = &(SLAVE_AD_INFO(slave)->port);
2729ba824a8bSPeter Pan(潘卫平) 		if (lacp_fast)
2730c1e46990SAndy Roulin 			port->actor_oper_port_state |= LACP_STATE_LACP_TIMEOUT;
2731ba824a8bSPeter Pan(潘卫平) 		else
2732c1e46990SAndy Roulin 			port->actor_oper_port_state &= ~LACP_STATE_LACP_TIMEOUT;
2733ba824a8bSPeter Pan(潘卫平) 	}
2734e470259fSNikolay Aleksandrov 	spin_unlock_bh(&bond->mode_lock);
2735ba824a8bSPeter Pan(潘卫平) }
2736a258aeacSNikolay Aleksandrov 
bond_3ad_stats_size(void)2737a258aeacSNikolay Aleksandrov size_t bond_3ad_stats_size(void)
2738a258aeacSNikolay Aleksandrov {
2739a258aeacSNikolay Aleksandrov 	return nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_RX */
2740a258aeacSNikolay Aleksandrov 	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_TX */
2741a258aeacSNikolay Aleksandrov 	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_UNKNOWN_RX */
2742a258aeacSNikolay Aleksandrov 	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_ILLEGAL_RX */
2743a258aeacSNikolay Aleksandrov 	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_RX */
2744a258aeacSNikolay Aleksandrov 	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_TX */
2745a258aeacSNikolay Aleksandrov 	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_RESP_RX */
2746a258aeacSNikolay Aleksandrov 	       nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_RESP_TX */
2747a258aeacSNikolay Aleksandrov 	       nla_total_size_64bit(sizeof(u64)); /* BOND_3AD_STAT_MARKER_UNKNOWN_RX */
2748a258aeacSNikolay Aleksandrov }
2749a258aeacSNikolay Aleksandrov 
bond_3ad_stats_fill(struct sk_buff * skb,struct bond_3ad_stats * stats)2750a258aeacSNikolay Aleksandrov int bond_3ad_stats_fill(struct sk_buff *skb, struct bond_3ad_stats *stats)
2751a258aeacSNikolay Aleksandrov {
2752a258aeacSNikolay Aleksandrov 	u64 val;
2753a258aeacSNikolay Aleksandrov 
2754a258aeacSNikolay Aleksandrov 	val = atomic64_read(&stats->lacpdu_rx);
2755a258aeacSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_RX, val,
2756a258aeacSNikolay Aleksandrov 			      BOND_3AD_STAT_PAD))
2757a258aeacSNikolay Aleksandrov 		return -EMSGSIZE;
2758a258aeacSNikolay Aleksandrov 	val = atomic64_read(&stats->lacpdu_tx);
2759a258aeacSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_TX, val,
2760a258aeacSNikolay Aleksandrov 			      BOND_3AD_STAT_PAD))
2761a258aeacSNikolay Aleksandrov 		return -EMSGSIZE;
2762a258aeacSNikolay Aleksandrov 	val = atomic64_read(&stats->lacpdu_unknown_rx);
2763a258aeacSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_UNKNOWN_RX, val,
2764a258aeacSNikolay Aleksandrov 			      BOND_3AD_STAT_PAD))
2765a258aeacSNikolay Aleksandrov 		return -EMSGSIZE;
2766a258aeacSNikolay Aleksandrov 	val = atomic64_read(&stats->lacpdu_illegal_rx);
2767a258aeacSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_ILLEGAL_RX, val,
2768a258aeacSNikolay Aleksandrov 			      BOND_3AD_STAT_PAD))
2769a258aeacSNikolay Aleksandrov 		return -EMSGSIZE;
2770a258aeacSNikolay Aleksandrov 
2771a258aeacSNikolay Aleksandrov 	val = atomic64_read(&stats->marker_rx);
2772a258aeacSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RX, val,
2773a258aeacSNikolay Aleksandrov 			      BOND_3AD_STAT_PAD))
2774a258aeacSNikolay Aleksandrov 		return -EMSGSIZE;
2775a258aeacSNikolay Aleksandrov 	val = atomic64_read(&stats->marker_tx);
2776a258aeacSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_TX, val,
2777a258aeacSNikolay Aleksandrov 			      BOND_3AD_STAT_PAD))
2778a258aeacSNikolay Aleksandrov 		return -EMSGSIZE;
2779a258aeacSNikolay Aleksandrov 	val = atomic64_read(&stats->marker_resp_rx);
2780a258aeacSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RESP_RX, val,
2781a258aeacSNikolay Aleksandrov 			      BOND_3AD_STAT_PAD))
2782a258aeacSNikolay Aleksandrov 		return -EMSGSIZE;
2783a258aeacSNikolay Aleksandrov 	val = atomic64_read(&stats->marker_resp_tx);
2784a258aeacSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RESP_TX, val,
2785a258aeacSNikolay Aleksandrov 			      BOND_3AD_STAT_PAD))
2786a258aeacSNikolay Aleksandrov 		return -EMSGSIZE;
2787a258aeacSNikolay Aleksandrov 	val = atomic64_read(&stats->marker_unknown_rx);
2788a258aeacSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_UNKNOWN_RX, val,
2789a258aeacSNikolay Aleksandrov 			      BOND_3AD_STAT_PAD))
2790a258aeacSNikolay Aleksandrov 		return -EMSGSIZE;
2791a258aeacSNikolay Aleksandrov 
2792a258aeacSNikolay Aleksandrov 	return 0;
2793a258aeacSNikolay Aleksandrov }
2794