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