162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(c) 1999 - 2004 Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/skbuff.h> 762306a36Sopenharmony_ci#include <linux/if_ether.h> 862306a36Sopenharmony_ci#include <linux/netdevice.h> 962306a36Sopenharmony_ci#include <linux/spinlock.h> 1062306a36Sopenharmony_ci#include <linux/ethtool.h> 1162306a36Sopenharmony_ci#include <linux/etherdevice.h> 1262306a36Sopenharmony_ci#include <linux/if_bonding.h> 1362306a36Sopenharmony_ci#include <linux/pkt_sched.h> 1462306a36Sopenharmony_ci#include <net/net_namespace.h> 1562306a36Sopenharmony_ci#include <net/bonding.h> 1662306a36Sopenharmony_ci#include <net/bond_3ad.h> 1762306a36Sopenharmony_ci#include <net/netlink.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* General definitions */ 2062306a36Sopenharmony_ci#define AD_SHORT_TIMEOUT 1 2162306a36Sopenharmony_ci#define AD_LONG_TIMEOUT 0 2262306a36Sopenharmony_ci#define AD_STANDBY 0x2 2362306a36Sopenharmony_ci#define AD_MAX_TX_IN_SECOND 3 2462306a36Sopenharmony_ci#define AD_COLLECTOR_MAX_DELAY 0 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Timer definitions (43.4.4 in the 802.3ad standard) */ 2762306a36Sopenharmony_ci#define AD_FAST_PERIODIC_TIME 1 2862306a36Sopenharmony_ci#define AD_SLOW_PERIODIC_TIME 30 2962306a36Sopenharmony_ci#define AD_SHORT_TIMEOUT_TIME (3*AD_FAST_PERIODIC_TIME) 3062306a36Sopenharmony_ci#define AD_LONG_TIMEOUT_TIME (3*AD_SLOW_PERIODIC_TIME) 3162306a36Sopenharmony_ci#define AD_CHURN_DETECTION_TIME 60 3262306a36Sopenharmony_ci#define AD_AGGREGATE_WAIT_TIME 2 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Port Variables definitions used by the State Machines (43.4.7 in the 3562306a36Sopenharmony_ci * 802.3ad standard) 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci#define AD_PORT_BEGIN 0x1 3862306a36Sopenharmony_ci#define AD_PORT_LACP_ENABLED 0x2 3962306a36Sopenharmony_ci#define AD_PORT_ACTOR_CHURN 0x4 4062306a36Sopenharmony_ci#define AD_PORT_PARTNER_CHURN 0x8 4162306a36Sopenharmony_ci#define AD_PORT_READY 0x10 4262306a36Sopenharmony_ci#define AD_PORT_READY_N 0x20 4362306a36Sopenharmony_ci#define AD_PORT_MATCHED 0x40 4462306a36Sopenharmony_ci#define AD_PORT_STANDBY 0x80 4562306a36Sopenharmony_ci#define AD_PORT_SELECTED 0x100 4662306a36Sopenharmony_ci#define AD_PORT_MOVED 0x200 4762306a36Sopenharmony_ci#define AD_PORT_CHURNED (AD_PORT_ACTOR_CHURN | AD_PORT_PARTNER_CHURN) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Port Key definitions 5062306a36Sopenharmony_ci * key is determined according to the link speed, duplex and 5162306a36Sopenharmony_ci * user key (which is yet not supported) 5262306a36Sopenharmony_ci * -------------------------------------------------------------- 5362306a36Sopenharmony_ci * Port key | User key (10 bits) | Speed (5 bits) | Duplex| 5462306a36Sopenharmony_ci * -------------------------------------------------------------- 5562306a36Sopenharmony_ci * |15 6|5 1|0 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#define AD_DUPLEX_KEY_MASKS 0x1 5862306a36Sopenharmony_ci#define AD_SPEED_KEY_MASKS 0x3E 5962306a36Sopenharmony_ci#define AD_USER_KEY_MASKS 0xFFC0 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cienum ad_link_speed_type { 6262306a36Sopenharmony_ci AD_LINK_SPEED_1MBPS = 1, 6362306a36Sopenharmony_ci AD_LINK_SPEED_10MBPS, 6462306a36Sopenharmony_ci AD_LINK_SPEED_100MBPS, 6562306a36Sopenharmony_ci AD_LINK_SPEED_1000MBPS, 6662306a36Sopenharmony_ci AD_LINK_SPEED_2500MBPS, 6762306a36Sopenharmony_ci AD_LINK_SPEED_5000MBPS, 6862306a36Sopenharmony_ci AD_LINK_SPEED_10000MBPS, 6962306a36Sopenharmony_ci AD_LINK_SPEED_14000MBPS, 7062306a36Sopenharmony_ci AD_LINK_SPEED_20000MBPS, 7162306a36Sopenharmony_ci AD_LINK_SPEED_25000MBPS, 7262306a36Sopenharmony_ci AD_LINK_SPEED_40000MBPS, 7362306a36Sopenharmony_ci AD_LINK_SPEED_50000MBPS, 7462306a36Sopenharmony_ci AD_LINK_SPEED_56000MBPS, 7562306a36Sopenharmony_ci AD_LINK_SPEED_100000MBPS, 7662306a36Sopenharmony_ci AD_LINK_SPEED_200000MBPS, 7762306a36Sopenharmony_ci AD_LINK_SPEED_400000MBPS, 7862306a36Sopenharmony_ci AD_LINK_SPEED_800000MBPS, 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* compare MAC addresses */ 8262306a36Sopenharmony_ci#define MAC_ADDRESS_EQUAL(A, B) \ 8362306a36Sopenharmony_ci ether_addr_equal_64bits((const u8 *)A, (const u8 *)B) 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic const u8 null_mac_addr[ETH_ALEN + 2] __long_aligned = { 8662306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic const u16 ad_ticks_per_sec = 1000 / AD_TIMER_INTERVAL; 9062306a36Sopenharmony_cistatic const int ad_delta_in_ticks = (AD_TIMER_INTERVAL * HZ) / 1000; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ciconst u8 lacpdu_mcast_addr[ETH_ALEN + 2] __long_aligned = { 9362306a36Sopenharmony_ci 0x01, 0x80, 0xC2, 0x00, 0x00, 0x02 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* ================= main 802.3ad protocol functions ================== */ 9762306a36Sopenharmony_cistatic int ad_lacpdu_send(struct port *port); 9862306a36Sopenharmony_cistatic int ad_marker_send(struct port *port, struct bond_marker *marker); 9962306a36Sopenharmony_cistatic void ad_mux_machine(struct port *port, bool *update_slave_arr); 10062306a36Sopenharmony_cistatic void ad_rx_machine(struct lacpdu *lacpdu, struct port *port); 10162306a36Sopenharmony_cistatic void ad_tx_machine(struct port *port); 10262306a36Sopenharmony_cistatic void ad_periodic_machine(struct port *port, struct bond_params *bond_params); 10362306a36Sopenharmony_cistatic void ad_port_selection_logic(struct port *port, bool *update_slave_arr); 10462306a36Sopenharmony_cistatic void ad_agg_selection_logic(struct aggregator *aggregator, 10562306a36Sopenharmony_ci bool *update_slave_arr); 10662306a36Sopenharmony_cistatic void ad_clear_agg(struct aggregator *aggregator); 10762306a36Sopenharmony_cistatic void ad_initialize_agg(struct aggregator *aggregator); 10862306a36Sopenharmony_cistatic void ad_initialize_port(struct port *port, int lacp_fast); 10962306a36Sopenharmony_cistatic void ad_enable_collecting_distributing(struct port *port, 11062306a36Sopenharmony_ci bool *update_slave_arr); 11162306a36Sopenharmony_cistatic void ad_disable_collecting_distributing(struct port *port, 11262306a36Sopenharmony_ci bool *update_slave_arr); 11362306a36Sopenharmony_cistatic void ad_marker_info_received(struct bond_marker *marker_info, 11462306a36Sopenharmony_ci struct port *port); 11562306a36Sopenharmony_cistatic void ad_marker_response_received(struct bond_marker *marker, 11662306a36Sopenharmony_ci struct port *port); 11762306a36Sopenharmony_cistatic void ad_update_actor_keys(struct port *port, bool reset); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* ================= api to bonding and kernel code ================== */ 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/** 12362306a36Sopenharmony_ci * __get_bond_by_port - get the port's bonding struct 12462306a36Sopenharmony_ci * @port: the port we're looking at 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * Return @port's bonding struct, or %NULL if it can't be found. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_cistatic inline struct bonding *__get_bond_by_port(struct port *port) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci if (port->slave == NULL) 13162306a36Sopenharmony_ci return NULL; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return bond_get_bond_by_slave(port->slave); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/** 13762306a36Sopenharmony_ci * __get_first_agg - get the first aggregator in the bond 13862306a36Sopenharmony_ci * @port: the port we're looking at 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * Return the aggregator of the first slave in @bond, or %NULL if it can't be 14162306a36Sopenharmony_ci * found. 14262306a36Sopenharmony_ci * The caller must hold RCU or RTNL lock. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_cistatic inline struct aggregator *__get_first_agg(struct port *port) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct bonding *bond = __get_bond_by_port(port); 14762306a36Sopenharmony_ci struct slave *first_slave; 14862306a36Sopenharmony_ci struct aggregator *agg; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* If there's no bond for this port, or bond has no slaves */ 15162306a36Sopenharmony_ci if (bond == NULL) 15262306a36Sopenharmony_ci return NULL; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci rcu_read_lock(); 15562306a36Sopenharmony_ci first_slave = bond_first_slave_rcu(bond); 15662306a36Sopenharmony_ci agg = first_slave ? &(SLAVE_AD_INFO(first_slave)->aggregator) : NULL; 15762306a36Sopenharmony_ci rcu_read_unlock(); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return agg; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/** 16362306a36Sopenharmony_ci * __agg_has_partner - see if we have a partner 16462306a36Sopenharmony_ci * @agg: the agregator we're looking at 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * Return nonzero if aggregator has a partner (denoted by a non-zero ether 16762306a36Sopenharmony_ci * address for the partner). Return 0 if not. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cistatic inline int __agg_has_partner(struct aggregator *agg) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci return !is_zero_ether_addr(agg->partner_system.mac_addr_value); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * __disable_port - disable the port's slave 17662306a36Sopenharmony_ci * @port: the port we're looking at 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_cistatic inline void __disable_port(struct port *port) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci bond_set_slave_inactive_flags(port->slave, BOND_SLAVE_NOTIFY_LATER); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/** 18462306a36Sopenharmony_ci * __enable_port - enable the port's slave, if it's up 18562306a36Sopenharmony_ci * @port: the port we're looking at 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_cistatic inline void __enable_port(struct port *port) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct slave *slave = port->slave; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if ((slave->link == BOND_LINK_UP) && bond_slave_is_up(slave)) 19262306a36Sopenharmony_ci bond_set_slave_active_flags(slave, BOND_SLAVE_NOTIFY_LATER); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/** 19662306a36Sopenharmony_ci * __port_is_enabled - check if the port's slave is in active state 19762306a36Sopenharmony_ci * @port: the port we're looking at 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_cistatic inline int __port_is_enabled(struct port *port) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci return bond_is_active_slave(port->slave); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/** 20562306a36Sopenharmony_ci * __get_agg_selection_mode - get the aggregator selection mode 20662306a36Sopenharmony_ci * @port: the port we're looking at 20762306a36Sopenharmony_ci * 20862306a36Sopenharmony_ci * Get the aggregator selection mode. Can be %STABLE, %BANDWIDTH or %COUNT. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_cistatic inline u32 __get_agg_selection_mode(struct port *port) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct bonding *bond = __get_bond_by_port(port); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (bond == NULL) 21562306a36Sopenharmony_ci return BOND_AD_STABLE; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return bond->params.ad_select; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/** 22162306a36Sopenharmony_ci * __check_agg_selection_timer - check if the selection timer has expired 22262306a36Sopenharmony_ci * @port: the port we're looking at 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_cistatic inline int __check_agg_selection_timer(struct port *port) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct bonding *bond = __get_bond_by_port(port); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (bond == NULL) 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return atomic_read(&BOND_AD_INFO(bond).agg_select_timer) ? 1 : 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/** 23562306a36Sopenharmony_ci * __get_link_speed - get a port's speed 23662306a36Sopenharmony_ci * @port: the port we're looking at 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * Return @port's speed in 802.3ad enum format. i.e. one of: 23962306a36Sopenharmony_ci * 0, 24062306a36Sopenharmony_ci * %AD_LINK_SPEED_10MBPS, 24162306a36Sopenharmony_ci * %AD_LINK_SPEED_100MBPS, 24262306a36Sopenharmony_ci * %AD_LINK_SPEED_1000MBPS, 24362306a36Sopenharmony_ci * %AD_LINK_SPEED_2500MBPS, 24462306a36Sopenharmony_ci * %AD_LINK_SPEED_5000MBPS, 24562306a36Sopenharmony_ci * %AD_LINK_SPEED_10000MBPS 24662306a36Sopenharmony_ci * %AD_LINK_SPEED_14000MBPS, 24762306a36Sopenharmony_ci * %AD_LINK_SPEED_20000MBPS 24862306a36Sopenharmony_ci * %AD_LINK_SPEED_25000MBPS 24962306a36Sopenharmony_ci * %AD_LINK_SPEED_40000MBPS 25062306a36Sopenharmony_ci * %AD_LINK_SPEED_50000MBPS 25162306a36Sopenharmony_ci * %AD_LINK_SPEED_56000MBPS 25262306a36Sopenharmony_ci * %AD_LINK_SPEED_100000MBPS 25362306a36Sopenharmony_ci * %AD_LINK_SPEED_200000MBPS 25462306a36Sopenharmony_ci * %AD_LINK_SPEED_400000MBPS 25562306a36Sopenharmony_ci * %AD_LINK_SPEED_800000MBPS 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_cistatic u16 __get_link_speed(struct port *port) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct slave *slave = port->slave; 26062306a36Sopenharmony_ci u16 speed; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* this if covers only a special case: when the configuration starts 26362306a36Sopenharmony_ci * with link down, it sets the speed to 0. 26462306a36Sopenharmony_ci * This is done in spite of the fact that the e100 driver reports 0 26562306a36Sopenharmony_ci * to be compatible with MVT in the future. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci if (slave->link != BOND_LINK_UP) 26862306a36Sopenharmony_ci speed = 0; 26962306a36Sopenharmony_ci else { 27062306a36Sopenharmony_ci switch (slave->speed) { 27162306a36Sopenharmony_ci case SPEED_10: 27262306a36Sopenharmony_ci speed = AD_LINK_SPEED_10MBPS; 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci case SPEED_100: 27662306a36Sopenharmony_ci speed = AD_LINK_SPEED_100MBPS; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci case SPEED_1000: 28062306a36Sopenharmony_ci speed = AD_LINK_SPEED_1000MBPS; 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci case SPEED_2500: 28462306a36Sopenharmony_ci speed = AD_LINK_SPEED_2500MBPS; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci case SPEED_5000: 28862306a36Sopenharmony_ci speed = AD_LINK_SPEED_5000MBPS; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci case SPEED_10000: 29262306a36Sopenharmony_ci speed = AD_LINK_SPEED_10000MBPS; 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci case SPEED_14000: 29662306a36Sopenharmony_ci speed = AD_LINK_SPEED_14000MBPS; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci case SPEED_20000: 30062306a36Sopenharmony_ci speed = AD_LINK_SPEED_20000MBPS; 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci case SPEED_25000: 30462306a36Sopenharmony_ci speed = AD_LINK_SPEED_25000MBPS; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci case SPEED_40000: 30862306a36Sopenharmony_ci speed = AD_LINK_SPEED_40000MBPS; 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci case SPEED_50000: 31262306a36Sopenharmony_ci speed = AD_LINK_SPEED_50000MBPS; 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci case SPEED_56000: 31662306a36Sopenharmony_ci speed = AD_LINK_SPEED_56000MBPS; 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci case SPEED_100000: 32062306a36Sopenharmony_ci speed = AD_LINK_SPEED_100000MBPS; 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci case SPEED_200000: 32462306a36Sopenharmony_ci speed = AD_LINK_SPEED_200000MBPS; 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci case SPEED_400000: 32862306a36Sopenharmony_ci speed = AD_LINK_SPEED_400000MBPS; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci case SPEED_800000: 33262306a36Sopenharmony_ci speed = AD_LINK_SPEED_800000MBPS; 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci default: 33662306a36Sopenharmony_ci /* unknown speed value from ethtool. shouldn't happen */ 33762306a36Sopenharmony_ci if (slave->speed != SPEED_UNKNOWN) 33862306a36Sopenharmony_ci pr_err_once("%s: (slave %s): unknown ethtool speed (%d) for port %d (set it to 0)\n", 33962306a36Sopenharmony_ci slave->bond->dev->name, 34062306a36Sopenharmony_ci slave->dev->name, slave->speed, 34162306a36Sopenharmony_ci port->actor_port_number); 34262306a36Sopenharmony_ci speed = 0; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci slave_dbg(slave->bond->dev, slave->dev, "Port %d Received link speed %d update from adapter\n", 34862306a36Sopenharmony_ci port->actor_port_number, speed); 34962306a36Sopenharmony_ci return speed; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/** 35362306a36Sopenharmony_ci * __get_duplex - get a port's duplex 35462306a36Sopenharmony_ci * @port: the port we're looking at 35562306a36Sopenharmony_ci * 35662306a36Sopenharmony_ci * Return @port's duplex in 802.3ad bitmask format. i.e.: 35762306a36Sopenharmony_ci * 0x01 if in full duplex 35862306a36Sopenharmony_ci * 0x00 otherwise 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_cistatic u8 __get_duplex(struct port *port) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct slave *slave = port->slave; 36362306a36Sopenharmony_ci u8 retval = 0x0; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* handling a special case: when the configuration starts with 36662306a36Sopenharmony_ci * link down, it sets the duplex to 0. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci if (slave->link == BOND_LINK_UP) { 36962306a36Sopenharmony_ci switch (slave->duplex) { 37062306a36Sopenharmony_ci case DUPLEX_FULL: 37162306a36Sopenharmony_ci retval = 0x1; 37262306a36Sopenharmony_ci slave_dbg(slave->bond->dev, slave->dev, "Port %d Received status full duplex update from adapter\n", 37362306a36Sopenharmony_ci port->actor_port_number); 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci case DUPLEX_HALF: 37662306a36Sopenharmony_ci default: 37762306a36Sopenharmony_ci retval = 0x0; 37862306a36Sopenharmony_ci slave_dbg(slave->bond->dev, slave->dev, "Port %d Received status NOT full duplex update from adapter\n", 37962306a36Sopenharmony_ci port->actor_port_number); 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci return retval; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void __ad_actor_update_port(struct port *port) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci const struct bonding *bond = bond_get_bond_by_slave(port->slave); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci port->actor_system = BOND_AD_INFO(bond).system.sys_mac_addr; 39162306a36Sopenharmony_ci port->actor_system_priority = BOND_AD_INFO(bond).system.sys_priority; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/* Conversions */ 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/** 39762306a36Sopenharmony_ci * __ad_timer_to_ticks - convert a given timer type to AD module ticks 39862306a36Sopenharmony_ci * @timer_type: which timer to operate 39962306a36Sopenharmony_ci * @par: timer parameter. see below 40062306a36Sopenharmony_ci * 40162306a36Sopenharmony_ci * If @timer_type is %current_while_timer, @par indicates long/short timer. 40262306a36Sopenharmony_ci * If @timer_type is %periodic_timer, @par is one of %FAST_PERIODIC_TIME, 40362306a36Sopenharmony_ci * %SLOW_PERIODIC_TIME. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_cistatic u16 __ad_timer_to_ticks(u16 timer_type, u16 par) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci u16 retval = 0; /* to silence the compiler */ 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci switch (timer_type) { 41062306a36Sopenharmony_ci case AD_CURRENT_WHILE_TIMER: /* for rx machine usage */ 41162306a36Sopenharmony_ci if (par) 41262306a36Sopenharmony_ci retval = (AD_SHORT_TIMEOUT_TIME*ad_ticks_per_sec); 41362306a36Sopenharmony_ci else 41462306a36Sopenharmony_ci retval = (AD_LONG_TIMEOUT_TIME*ad_ticks_per_sec); 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci case AD_ACTOR_CHURN_TIMER: /* for local churn machine */ 41762306a36Sopenharmony_ci retval = (AD_CHURN_DETECTION_TIME*ad_ticks_per_sec); 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci case AD_PERIODIC_TIMER: /* for periodic machine */ 42062306a36Sopenharmony_ci retval = (par*ad_ticks_per_sec); /* long timeout */ 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci case AD_PARTNER_CHURN_TIMER: /* for remote churn machine */ 42362306a36Sopenharmony_ci retval = (AD_CHURN_DETECTION_TIME*ad_ticks_per_sec); 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci case AD_WAIT_WHILE_TIMER: /* for selection machine */ 42662306a36Sopenharmony_ci retval = (AD_AGGREGATE_WAIT_TIME*ad_ticks_per_sec); 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return retval; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* ================= ad_rx_machine helper functions ================== */ 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * __choose_matched - update a port's matched variable from a received lacpdu 43862306a36Sopenharmony_ci * @lacpdu: the lacpdu we've received 43962306a36Sopenharmony_ci * @port: the port we're looking at 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * Update the value of the matched variable, using parameter values from a 44262306a36Sopenharmony_ci * newly received lacpdu. Parameter values for the partner carried in the 44362306a36Sopenharmony_ci * received PDU are compared with the corresponding operational parameter 44462306a36Sopenharmony_ci * values for the actor. Matched is set to TRUE if all of these parameters 44562306a36Sopenharmony_ci * match and the PDU parameter partner_state.aggregation has the same value as 44662306a36Sopenharmony_ci * actor_oper_port_state.aggregation and lacp will actively maintain the link 44762306a36Sopenharmony_ci * in the aggregation. Matched is also set to TRUE if the value of 44862306a36Sopenharmony_ci * actor_state.aggregation in the received PDU is set to FALSE, i.e., indicates 44962306a36Sopenharmony_ci * an individual link and lacp will actively maintain the link. Otherwise, 45062306a36Sopenharmony_ci * matched is set to FALSE. LACP is considered to be actively maintaining the 45162306a36Sopenharmony_ci * link if either the PDU's actor_state.lacp_activity variable is TRUE or both 45262306a36Sopenharmony_ci * the actor's actor_oper_port_state.lacp_activity and the PDU's 45362306a36Sopenharmony_ci * partner_state.lacp_activity variables are TRUE. 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * Note: the AD_PORT_MATCHED "variable" is not specified by 802.3ad; it is 45662306a36Sopenharmony_ci * used here to implement the language from 802.3ad 43.4.9 that requires 45762306a36Sopenharmony_ci * recordPDU to "match" the LACPDU parameters to the stored values. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_cistatic void __choose_matched(struct lacpdu *lacpdu, struct port *port) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci /* check if all parameters are alike 46262306a36Sopenharmony_ci * or this is individual link(aggregation == FALSE) 46362306a36Sopenharmony_ci * then update the state machine Matched variable. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci if (((ntohs(lacpdu->partner_port) == port->actor_port_number) && 46662306a36Sopenharmony_ci (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) && 46762306a36Sopenharmony_ci MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) && 46862306a36Sopenharmony_ci (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) && 46962306a36Sopenharmony_ci (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) && 47062306a36Sopenharmony_ci ((lacpdu->partner_state & LACP_STATE_AGGREGATION) == (port->actor_oper_port_state & LACP_STATE_AGGREGATION))) || 47162306a36Sopenharmony_ci ((lacpdu->actor_state & LACP_STATE_AGGREGATION) == 0) 47262306a36Sopenharmony_ci ) { 47362306a36Sopenharmony_ci port->sm_vars |= AD_PORT_MATCHED; 47462306a36Sopenharmony_ci } else { 47562306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_MATCHED; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/** 48062306a36Sopenharmony_ci * __record_pdu - record parameters from a received lacpdu 48162306a36Sopenharmony_ci * @lacpdu: the lacpdu we've received 48262306a36Sopenharmony_ci * @port: the port we're looking at 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci * Record the parameter values for the Actor carried in a received lacpdu as 48562306a36Sopenharmony_ci * the current partner operational parameter values and sets 48662306a36Sopenharmony_ci * actor_oper_port_state.defaulted to FALSE. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_cistatic void __record_pdu(struct lacpdu *lacpdu, struct port *port) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci if (lacpdu && port) { 49162306a36Sopenharmony_ci struct port_params *partner = &port->partner_oper; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci __choose_matched(lacpdu, port); 49462306a36Sopenharmony_ci /* record the new parameter values for the partner 49562306a36Sopenharmony_ci * operational 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ci partner->port_number = ntohs(lacpdu->actor_port); 49862306a36Sopenharmony_ci partner->port_priority = ntohs(lacpdu->actor_port_priority); 49962306a36Sopenharmony_ci partner->system = lacpdu->actor_system; 50062306a36Sopenharmony_ci partner->system_priority = ntohs(lacpdu->actor_system_priority); 50162306a36Sopenharmony_ci partner->key = ntohs(lacpdu->actor_key); 50262306a36Sopenharmony_ci partner->port_state = lacpdu->actor_state; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* set actor_oper_port_state.defaulted to FALSE */ 50562306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_DEFAULTED; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* set the partner sync. to on if the partner is sync, 50862306a36Sopenharmony_ci * and the port is matched 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci if ((port->sm_vars & AD_PORT_MATCHED) && 51162306a36Sopenharmony_ci (lacpdu->actor_state & LACP_STATE_SYNCHRONIZATION)) { 51262306a36Sopenharmony_ci partner->port_state |= LACP_STATE_SYNCHRONIZATION; 51362306a36Sopenharmony_ci slave_dbg(port->slave->bond->dev, port->slave->dev, 51462306a36Sopenharmony_ci "partner sync=1\n"); 51562306a36Sopenharmony_ci } else { 51662306a36Sopenharmony_ci partner->port_state &= ~LACP_STATE_SYNCHRONIZATION; 51762306a36Sopenharmony_ci slave_dbg(port->slave->bond->dev, port->slave->dev, 51862306a36Sopenharmony_ci "partner sync=0\n"); 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci/** 52462306a36Sopenharmony_ci * __record_default - record default parameters 52562306a36Sopenharmony_ci * @port: the port we're looking at 52662306a36Sopenharmony_ci * 52762306a36Sopenharmony_ci * This function records the default parameter values for the partner carried 52862306a36Sopenharmony_ci * in the Partner Admin parameters as the current partner operational parameter 52962306a36Sopenharmony_ci * values and sets actor_oper_port_state.defaulted to TRUE. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic void __record_default(struct port *port) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci if (port) { 53462306a36Sopenharmony_ci /* record the partner admin parameters */ 53562306a36Sopenharmony_ci memcpy(&port->partner_oper, &port->partner_admin, 53662306a36Sopenharmony_ci sizeof(struct port_params)); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* set actor_oper_port_state.defaulted to true */ 53962306a36Sopenharmony_ci port->actor_oper_port_state |= LACP_STATE_DEFAULTED; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci/** 54462306a36Sopenharmony_ci * __update_selected - update a port's Selected variable from a received lacpdu 54562306a36Sopenharmony_ci * @lacpdu: the lacpdu we've received 54662306a36Sopenharmony_ci * @port: the port we're looking at 54762306a36Sopenharmony_ci * 54862306a36Sopenharmony_ci * Update the value of the selected variable, using parameter values from a 54962306a36Sopenharmony_ci * newly received lacpdu. The parameter values for the Actor carried in the 55062306a36Sopenharmony_ci * received PDU are compared with the corresponding operational parameter 55162306a36Sopenharmony_ci * values for the ports partner. If one or more of the comparisons shows that 55262306a36Sopenharmony_ci * the value(s) received in the PDU differ from the current operational values, 55362306a36Sopenharmony_ci * then selected is set to FALSE and actor_oper_port_state.synchronization is 55462306a36Sopenharmony_ci * set to out_of_sync. Otherwise, selected remains unchanged. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_cistatic void __update_selected(struct lacpdu *lacpdu, struct port *port) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci if (lacpdu && port) { 55962306a36Sopenharmony_ci const struct port_params *partner = &port->partner_oper; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* check if any parameter is different then 56262306a36Sopenharmony_ci * update the state machine selected variable. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci if (ntohs(lacpdu->actor_port) != partner->port_number || 56562306a36Sopenharmony_ci ntohs(lacpdu->actor_port_priority) != partner->port_priority || 56662306a36Sopenharmony_ci !MAC_ADDRESS_EQUAL(&lacpdu->actor_system, &partner->system) || 56762306a36Sopenharmony_ci ntohs(lacpdu->actor_system_priority) != partner->system_priority || 56862306a36Sopenharmony_ci ntohs(lacpdu->actor_key) != partner->key || 56962306a36Sopenharmony_ci (lacpdu->actor_state & LACP_STATE_AGGREGATION) != (partner->port_state & LACP_STATE_AGGREGATION)) { 57062306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_SELECTED; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci/** 57662306a36Sopenharmony_ci * __update_default_selected - update a port's Selected variable from Partner 57762306a36Sopenharmony_ci * @port: the port we're looking at 57862306a36Sopenharmony_ci * 57962306a36Sopenharmony_ci * This function updates the value of the selected variable, using the partner 58062306a36Sopenharmony_ci * administrative parameter values. The administrative values are compared with 58162306a36Sopenharmony_ci * the corresponding operational parameter values for the partner. If one or 58262306a36Sopenharmony_ci * more of the comparisons shows that the administrative value(s) differ from 58362306a36Sopenharmony_ci * the current operational values, then Selected is set to FALSE and 58462306a36Sopenharmony_ci * actor_oper_port_state.synchronization is set to OUT_OF_SYNC. Otherwise, 58562306a36Sopenharmony_ci * Selected remains unchanged. 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_cistatic void __update_default_selected(struct port *port) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci if (port) { 59062306a36Sopenharmony_ci const struct port_params *admin = &port->partner_admin; 59162306a36Sopenharmony_ci const struct port_params *oper = &port->partner_oper; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* check if any parameter is different then 59462306a36Sopenharmony_ci * update the state machine selected variable. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci if (admin->port_number != oper->port_number || 59762306a36Sopenharmony_ci admin->port_priority != oper->port_priority || 59862306a36Sopenharmony_ci !MAC_ADDRESS_EQUAL(&admin->system, &oper->system) || 59962306a36Sopenharmony_ci admin->system_priority != oper->system_priority || 60062306a36Sopenharmony_ci admin->key != oper->key || 60162306a36Sopenharmony_ci (admin->port_state & LACP_STATE_AGGREGATION) 60262306a36Sopenharmony_ci != (oper->port_state & LACP_STATE_AGGREGATION)) { 60362306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_SELECTED; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci/** 60962306a36Sopenharmony_ci * __update_ntt - update a port's ntt variable from a received lacpdu 61062306a36Sopenharmony_ci * @lacpdu: the lacpdu we've received 61162306a36Sopenharmony_ci * @port: the port we're looking at 61262306a36Sopenharmony_ci * 61362306a36Sopenharmony_ci * Updates the value of the ntt variable, using parameter values from a newly 61462306a36Sopenharmony_ci * received lacpdu. The parameter values for the partner carried in the 61562306a36Sopenharmony_ci * received PDU are compared with the corresponding operational parameter 61662306a36Sopenharmony_ci * values for the Actor. If one or more of the comparisons shows that the 61762306a36Sopenharmony_ci * value(s) received in the PDU differ from the current operational values, 61862306a36Sopenharmony_ci * then ntt is set to TRUE. Otherwise, ntt remains unchanged. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_cistatic void __update_ntt(struct lacpdu *lacpdu, struct port *port) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci /* validate lacpdu and port */ 62362306a36Sopenharmony_ci if (lacpdu && port) { 62462306a36Sopenharmony_ci /* check if any parameter is different then 62562306a36Sopenharmony_ci * update the port->ntt. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci if ((ntohs(lacpdu->partner_port) != port->actor_port_number) || 62862306a36Sopenharmony_ci (ntohs(lacpdu->partner_port_priority) != port->actor_port_priority) || 62962306a36Sopenharmony_ci !MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) || 63062306a36Sopenharmony_ci (ntohs(lacpdu->partner_system_priority) != port->actor_system_priority) || 63162306a36Sopenharmony_ci (ntohs(lacpdu->partner_key) != port->actor_oper_port_key) || 63262306a36Sopenharmony_ci ((lacpdu->partner_state & LACP_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & LACP_STATE_LACP_ACTIVITY)) || 63362306a36Sopenharmony_ci ((lacpdu->partner_state & LACP_STATE_LACP_TIMEOUT) != (port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT)) || 63462306a36Sopenharmony_ci ((lacpdu->partner_state & LACP_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) || 63562306a36Sopenharmony_ci ((lacpdu->partner_state & LACP_STATE_AGGREGATION) != (port->actor_oper_port_state & LACP_STATE_AGGREGATION)) 63662306a36Sopenharmony_ci ) { 63762306a36Sopenharmony_ci port->ntt = true; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/** 64362306a36Sopenharmony_ci * __agg_ports_are_ready - check if all ports in an aggregator are ready 64462306a36Sopenharmony_ci * @aggregator: the aggregator we're looking at 64562306a36Sopenharmony_ci * 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_cistatic int __agg_ports_are_ready(struct aggregator *aggregator) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct port *port; 65062306a36Sopenharmony_ci int retval = 1; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (aggregator) { 65362306a36Sopenharmony_ci /* scan all ports in this aggregator to verfy if they are 65462306a36Sopenharmony_ci * all ready. 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_ci for (port = aggregator->lag_ports; 65762306a36Sopenharmony_ci port; 65862306a36Sopenharmony_ci port = port->next_port_in_aggregator) { 65962306a36Sopenharmony_ci if (!(port->sm_vars & AD_PORT_READY_N)) { 66062306a36Sopenharmony_ci retval = 0; 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return retval; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci/** 67062306a36Sopenharmony_ci * __set_agg_ports_ready - set value of Ready bit in all ports of an aggregator 67162306a36Sopenharmony_ci * @aggregator: the aggregator we're looking at 67262306a36Sopenharmony_ci * @val: Should the ports' ready bit be set on or off 67362306a36Sopenharmony_ci * 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_cistatic void __set_agg_ports_ready(struct aggregator *aggregator, int val) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct port *port; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci for (port = aggregator->lag_ports; port; 68062306a36Sopenharmony_ci port = port->next_port_in_aggregator) { 68162306a36Sopenharmony_ci if (val) 68262306a36Sopenharmony_ci port->sm_vars |= AD_PORT_READY; 68362306a36Sopenharmony_ci else 68462306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_READY; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int __agg_active_ports(struct aggregator *agg) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci struct port *port; 69162306a36Sopenharmony_ci int active = 0; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci for (port = agg->lag_ports; port; 69462306a36Sopenharmony_ci port = port->next_port_in_aggregator) { 69562306a36Sopenharmony_ci if (port->is_enabled) 69662306a36Sopenharmony_ci active++; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return active; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci/** 70362306a36Sopenharmony_ci * __get_agg_bandwidth - get the total bandwidth of an aggregator 70462306a36Sopenharmony_ci * @aggregator: the aggregator we're looking at 70562306a36Sopenharmony_ci * 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_cistatic u32 __get_agg_bandwidth(struct aggregator *aggregator) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci int nports = __agg_active_ports(aggregator); 71062306a36Sopenharmony_ci u32 bandwidth = 0; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (nports) { 71362306a36Sopenharmony_ci switch (__get_link_speed(aggregator->lag_ports)) { 71462306a36Sopenharmony_ci case AD_LINK_SPEED_1MBPS: 71562306a36Sopenharmony_ci bandwidth = nports; 71662306a36Sopenharmony_ci break; 71762306a36Sopenharmony_ci case AD_LINK_SPEED_10MBPS: 71862306a36Sopenharmony_ci bandwidth = nports * 10; 71962306a36Sopenharmony_ci break; 72062306a36Sopenharmony_ci case AD_LINK_SPEED_100MBPS: 72162306a36Sopenharmony_ci bandwidth = nports * 100; 72262306a36Sopenharmony_ci break; 72362306a36Sopenharmony_ci case AD_LINK_SPEED_1000MBPS: 72462306a36Sopenharmony_ci bandwidth = nports * 1000; 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci case AD_LINK_SPEED_2500MBPS: 72762306a36Sopenharmony_ci bandwidth = nports * 2500; 72862306a36Sopenharmony_ci break; 72962306a36Sopenharmony_ci case AD_LINK_SPEED_5000MBPS: 73062306a36Sopenharmony_ci bandwidth = nports * 5000; 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci case AD_LINK_SPEED_10000MBPS: 73362306a36Sopenharmony_ci bandwidth = nports * 10000; 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci case AD_LINK_SPEED_14000MBPS: 73662306a36Sopenharmony_ci bandwidth = nports * 14000; 73762306a36Sopenharmony_ci break; 73862306a36Sopenharmony_ci case AD_LINK_SPEED_20000MBPS: 73962306a36Sopenharmony_ci bandwidth = nports * 20000; 74062306a36Sopenharmony_ci break; 74162306a36Sopenharmony_ci case AD_LINK_SPEED_25000MBPS: 74262306a36Sopenharmony_ci bandwidth = nports * 25000; 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci case AD_LINK_SPEED_40000MBPS: 74562306a36Sopenharmony_ci bandwidth = nports * 40000; 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci case AD_LINK_SPEED_50000MBPS: 74862306a36Sopenharmony_ci bandwidth = nports * 50000; 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci case AD_LINK_SPEED_56000MBPS: 75162306a36Sopenharmony_ci bandwidth = nports * 56000; 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci case AD_LINK_SPEED_100000MBPS: 75462306a36Sopenharmony_ci bandwidth = nports * 100000; 75562306a36Sopenharmony_ci break; 75662306a36Sopenharmony_ci case AD_LINK_SPEED_200000MBPS: 75762306a36Sopenharmony_ci bandwidth = nports * 200000; 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci case AD_LINK_SPEED_400000MBPS: 76062306a36Sopenharmony_ci bandwidth = nports * 400000; 76162306a36Sopenharmony_ci break; 76262306a36Sopenharmony_ci case AD_LINK_SPEED_800000MBPS: 76362306a36Sopenharmony_ci bandwidth = nports * 800000; 76462306a36Sopenharmony_ci break; 76562306a36Sopenharmony_ci default: 76662306a36Sopenharmony_ci bandwidth = 0; /* to silence the compiler */ 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci return bandwidth; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci/** 77362306a36Sopenharmony_ci * __get_active_agg - get the current active aggregator 77462306a36Sopenharmony_ci * @aggregator: the aggregator we're looking at 77562306a36Sopenharmony_ci * 77662306a36Sopenharmony_ci * Caller must hold RCU lock. 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_cistatic struct aggregator *__get_active_agg(struct aggregator *aggregator) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct bonding *bond = aggregator->slave->bond; 78162306a36Sopenharmony_ci struct list_head *iter; 78262306a36Sopenharmony_ci struct slave *slave; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci bond_for_each_slave_rcu(bond, slave, iter) 78562306a36Sopenharmony_ci if (SLAVE_AD_INFO(slave)->aggregator.is_active) 78662306a36Sopenharmony_ci return &(SLAVE_AD_INFO(slave)->aggregator); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return NULL; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci/** 79262306a36Sopenharmony_ci * __update_lacpdu_from_port - update a port's lacpdu fields 79362306a36Sopenharmony_ci * @port: the port we're looking at 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_cistatic inline void __update_lacpdu_from_port(struct port *port) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct lacpdu *lacpdu = &port->lacpdu; 79862306a36Sopenharmony_ci const struct port_params *partner = &port->partner_oper; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* update current actual Actor parameters 80162306a36Sopenharmony_ci * lacpdu->subtype initialized 80262306a36Sopenharmony_ci * lacpdu->version_number initialized 80362306a36Sopenharmony_ci * lacpdu->tlv_type_actor_info initialized 80462306a36Sopenharmony_ci * lacpdu->actor_information_length initialized 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci lacpdu->actor_system_priority = htons(port->actor_system_priority); 80862306a36Sopenharmony_ci lacpdu->actor_system = port->actor_system; 80962306a36Sopenharmony_ci lacpdu->actor_key = htons(port->actor_oper_port_key); 81062306a36Sopenharmony_ci lacpdu->actor_port_priority = htons(port->actor_port_priority); 81162306a36Sopenharmony_ci lacpdu->actor_port = htons(port->actor_port_number); 81262306a36Sopenharmony_ci lacpdu->actor_state = port->actor_oper_port_state; 81362306a36Sopenharmony_ci slave_dbg(port->slave->bond->dev, port->slave->dev, 81462306a36Sopenharmony_ci "update lacpdu: actor port state %x\n", 81562306a36Sopenharmony_ci port->actor_oper_port_state); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* lacpdu->reserved_3_1 initialized 81862306a36Sopenharmony_ci * lacpdu->tlv_type_partner_info initialized 81962306a36Sopenharmony_ci * lacpdu->partner_information_length initialized 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci lacpdu->partner_system_priority = htons(partner->system_priority); 82362306a36Sopenharmony_ci lacpdu->partner_system = partner->system; 82462306a36Sopenharmony_ci lacpdu->partner_key = htons(partner->key); 82562306a36Sopenharmony_ci lacpdu->partner_port_priority = htons(partner->port_priority); 82662306a36Sopenharmony_ci lacpdu->partner_port = htons(partner->port_number); 82762306a36Sopenharmony_ci lacpdu->partner_state = partner->port_state; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* lacpdu->reserved_3_2 initialized 83062306a36Sopenharmony_ci * lacpdu->tlv_type_collector_info initialized 83162306a36Sopenharmony_ci * lacpdu->collector_information_length initialized 83262306a36Sopenharmony_ci * collector_max_delay initialized 83362306a36Sopenharmony_ci * reserved_12[12] initialized 83462306a36Sopenharmony_ci * tlv_type_terminator initialized 83562306a36Sopenharmony_ci * terminator_length initialized 83662306a36Sopenharmony_ci * reserved_50[50] initialized 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci/* ================= main 802.3ad protocol code ========================= */ 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/** 84362306a36Sopenharmony_ci * ad_lacpdu_send - send out a lacpdu packet on a given port 84462306a36Sopenharmony_ci * @port: the port we're looking at 84562306a36Sopenharmony_ci * 84662306a36Sopenharmony_ci * Returns: 0 on success 84762306a36Sopenharmony_ci * < 0 on error 84862306a36Sopenharmony_ci */ 84962306a36Sopenharmony_cistatic int ad_lacpdu_send(struct port *port) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct slave *slave = port->slave; 85262306a36Sopenharmony_ci struct sk_buff *skb; 85362306a36Sopenharmony_ci struct lacpdu_header *lacpdu_header; 85462306a36Sopenharmony_ci int length = sizeof(struct lacpdu_header); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci skb = dev_alloc_skb(length); 85762306a36Sopenharmony_ci if (!skb) 85862306a36Sopenharmony_ci return -ENOMEM; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci atomic64_inc(&SLAVE_AD_INFO(slave)->stats.lacpdu_tx); 86162306a36Sopenharmony_ci atomic64_inc(&BOND_AD_INFO(slave->bond).stats.lacpdu_tx); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci skb->dev = slave->dev; 86462306a36Sopenharmony_ci skb_reset_mac_header(skb); 86562306a36Sopenharmony_ci skb->network_header = skb->mac_header + ETH_HLEN; 86662306a36Sopenharmony_ci skb->protocol = PKT_TYPE_LACPDU; 86762306a36Sopenharmony_ci skb->priority = TC_PRIO_CONTROL; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci lacpdu_header = skb_put(skb, length); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci ether_addr_copy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr); 87262306a36Sopenharmony_ci /* Note: source address is set to be the member's PERMANENT address, 87362306a36Sopenharmony_ci * because we use it to identify loopback lacpdus in receive. 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_ci ether_addr_copy(lacpdu_header->hdr.h_source, slave->perm_hwaddr); 87662306a36Sopenharmony_ci lacpdu_header->hdr.h_proto = PKT_TYPE_LACPDU; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci lacpdu_header->lacpdu = port->lacpdu; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci dev_queue_xmit(skb); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci return 0; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci/** 88662306a36Sopenharmony_ci * ad_marker_send - send marker information/response on a given port 88762306a36Sopenharmony_ci * @port: the port we're looking at 88862306a36Sopenharmony_ci * @marker: marker data to send 88962306a36Sopenharmony_ci * 89062306a36Sopenharmony_ci * Returns: 0 on success 89162306a36Sopenharmony_ci * < 0 on error 89262306a36Sopenharmony_ci */ 89362306a36Sopenharmony_cistatic int ad_marker_send(struct port *port, struct bond_marker *marker) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci struct slave *slave = port->slave; 89662306a36Sopenharmony_ci struct sk_buff *skb; 89762306a36Sopenharmony_ci struct bond_marker_header *marker_header; 89862306a36Sopenharmony_ci int length = sizeof(struct bond_marker_header); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci skb = dev_alloc_skb(length + 16); 90162306a36Sopenharmony_ci if (!skb) 90262306a36Sopenharmony_ci return -ENOMEM; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci switch (marker->tlv_type) { 90562306a36Sopenharmony_ci case AD_MARKER_INFORMATION_SUBTYPE: 90662306a36Sopenharmony_ci atomic64_inc(&SLAVE_AD_INFO(slave)->stats.marker_tx); 90762306a36Sopenharmony_ci atomic64_inc(&BOND_AD_INFO(slave->bond).stats.marker_tx); 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci case AD_MARKER_RESPONSE_SUBTYPE: 91062306a36Sopenharmony_ci atomic64_inc(&SLAVE_AD_INFO(slave)->stats.marker_resp_tx); 91162306a36Sopenharmony_ci atomic64_inc(&BOND_AD_INFO(slave->bond).stats.marker_resp_tx); 91262306a36Sopenharmony_ci break; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci skb_reserve(skb, 16); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci skb->dev = slave->dev; 91862306a36Sopenharmony_ci skb_reset_mac_header(skb); 91962306a36Sopenharmony_ci skb->network_header = skb->mac_header + ETH_HLEN; 92062306a36Sopenharmony_ci skb->protocol = PKT_TYPE_LACPDU; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci marker_header = skb_put(skb, length); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci ether_addr_copy(marker_header->hdr.h_dest, lacpdu_mcast_addr); 92562306a36Sopenharmony_ci /* Note: source address is set to be the member's PERMANENT address, 92662306a36Sopenharmony_ci * because we use it to identify loopback MARKERs in receive. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_ci ether_addr_copy(marker_header->hdr.h_source, slave->perm_hwaddr); 92962306a36Sopenharmony_ci marker_header->hdr.h_proto = PKT_TYPE_LACPDU; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci marker_header->marker = *marker; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci dev_queue_xmit(skb); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci return 0; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci/** 93962306a36Sopenharmony_ci * ad_mux_machine - handle a port's mux state machine 94062306a36Sopenharmony_ci * @port: the port we're looking at 94162306a36Sopenharmony_ci * @update_slave_arr: Does slave array need update? 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_cistatic void ad_mux_machine(struct port *port, bool *update_slave_arr) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci mux_states_t last_state; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* keep current State Machine state to compare later if it was 94862306a36Sopenharmony_ci * changed 94962306a36Sopenharmony_ci */ 95062306a36Sopenharmony_ci last_state = port->sm_mux_state; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (port->sm_vars & AD_PORT_BEGIN) { 95362306a36Sopenharmony_ci port->sm_mux_state = AD_MUX_DETACHED; 95462306a36Sopenharmony_ci } else { 95562306a36Sopenharmony_ci switch (port->sm_mux_state) { 95662306a36Sopenharmony_ci case AD_MUX_DETACHED: 95762306a36Sopenharmony_ci if ((port->sm_vars & AD_PORT_SELECTED) 95862306a36Sopenharmony_ci || (port->sm_vars & AD_PORT_STANDBY)) 95962306a36Sopenharmony_ci /* if SELECTED or STANDBY */ 96062306a36Sopenharmony_ci port->sm_mux_state = AD_MUX_WAITING; 96162306a36Sopenharmony_ci break; 96262306a36Sopenharmony_ci case AD_MUX_WAITING: 96362306a36Sopenharmony_ci /* if SELECTED == FALSE return to DETACH state */ 96462306a36Sopenharmony_ci if (!(port->sm_vars & AD_PORT_SELECTED)) { 96562306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_READY_N; 96662306a36Sopenharmony_ci /* in order to withhold the Selection Logic to 96762306a36Sopenharmony_ci * check all ports READY_N value every callback 96862306a36Sopenharmony_ci * cycle to update ready variable, we check 96962306a36Sopenharmony_ci * READY_N and update READY here 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); 97262306a36Sopenharmony_ci port->sm_mux_state = AD_MUX_DETACHED; 97362306a36Sopenharmony_ci break; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* check if the wait_while_timer expired */ 97762306a36Sopenharmony_ci if (port->sm_mux_timer_counter 97862306a36Sopenharmony_ci && !(--port->sm_mux_timer_counter)) 97962306a36Sopenharmony_ci port->sm_vars |= AD_PORT_READY_N; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* in order to withhold the selection logic to check 98262306a36Sopenharmony_ci * all ports READY_N value every callback cycle to 98362306a36Sopenharmony_ci * update ready variable, we check READY_N and update 98462306a36Sopenharmony_ci * READY here 98562306a36Sopenharmony_ci */ 98662306a36Sopenharmony_ci __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* if the wait_while_timer expired, and the port is 98962306a36Sopenharmony_ci * in READY state, move to ATTACHED state 99062306a36Sopenharmony_ci */ 99162306a36Sopenharmony_ci if ((port->sm_vars & AD_PORT_READY) 99262306a36Sopenharmony_ci && !port->sm_mux_timer_counter) 99362306a36Sopenharmony_ci port->sm_mux_state = AD_MUX_ATTACHED; 99462306a36Sopenharmony_ci break; 99562306a36Sopenharmony_ci case AD_MUX_ATTACHED: 99662306a36Sopenharmony_ci /* check also if agg_select_timer expired (so the 99762306a36Sopenharmony_ci * edable port will take place only after this timer) 99862306a36Sopenharmony_ci */ 99962306a36Sopenharmony_ci if ((port->sm_vars & AD_PORT_SELECTED) && 100062306a36Sopenharmony_ci (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) && 100162306a36Sopenharmony_ci !__check_agg_selection_timer(port)) { 100262306a36Sopenharmony_ci if (port->aggregator->is_active) 100362306a36Sopenharmony_ci port->sm_mux_state = 100462306a36Sopenharmony_ci AD_MUX_COLLECTING_DISTRIBUTING; 100562306a36Sopenharmony_ci } else if (!(port->sm_vars & AD_PORT_SELECTED) || 100662306a36Sopenharmony_ci (port->sm_vars & AD_PORT_STANDBY)) { 100762306a36Sopenharmony_ci /* if UNSELECTED or STANDBY */ 100862306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_READY_N; 100962306a36Sopenharmony_ci /* in order to withhold the selection logic to 101062306a36Sopenharmony_ci * check all ports READY_N value every callback 101162306a36Sopenharmony_ci * cycle to update ready variable, we check 101262306a36Sopenharmony_ci * READY_N and update READY here 101362306a36Sopenharmony_ci */ 101462306a36Sopenharmony_ci __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); 101562306a36Sopenharmony_ci port->sm_mux_state = AD_MUX_DETACHED; 101662306a36Sopenharmony_ci } else if (port->aggregator->is_active) { 101762306a36Sopenharmony_ci port->actor_oper_port_state |= 101862306a36Sopenharmony_ci LACP_STATE_SYNCHRONIZATION; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci case AD_MUX_COLLECTING_DISTRIBUTING: 102262306a36Sopenharmony_ci if (!(port->sm_vars & AD_PORT_SELECTED) || 102362306a36Sopenharmony_ci (port->sm_vars & AD_PORT_STANDBY) || 102462306a36Sopenharmony_ci !(port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) || 102562306a36Sopenharmony_ci !(port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) { 102662306a36Sopenharmony_ci port->sm_mux_state = AD_MUX_ATTACHED; 102762306a36Sopenharmony_ci } else { 102862306a36Sopenharmony_ci /* if port state hasn't changed make 102962306a36Sopenharmony_ci * sure that a collecting distributing 103062306a36Sopenharmony_ci * port in an active aggregator is enabled 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_ci if (port->aggregator && 103362306a36Sopenharmony_ci port->aggregator->is_active && 103462306a36Sopenharmony_ci !__port_is_enabled(port)) { 103562306a36Sopenharmony_ci __enable_port(port); 103662306a36Sopenharmony_ci *update_slave_arr = true; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci default: 104162306a36Sopenharmony_ci break; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci /* check if the state machine was changed */ 104662306a36Sopenharmony_ci if (port->sm_mux_state != last_state) { 104762306a36Sopenharmony_ci slave_dbg(port->slave->bond->dev, port->slave->dev, 104862306a36Sopenharmony_ci "Mux Machine: Port=%d, Last State=%d, Curr State=%d\n", 104962306a36Sopenharmony_ci port->actor_port_number, 105062306a36Sopenharmony_ci last_state, 105162306a36Sopenharmony_ci port->sm_mux_state); 105262306a36Sopenharmony_ci switch (port->sm_mux_state) { 105362306a36Sopenharmony_ci case AD_MUX_DETACHED: 105462306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; 105562306a36Sopenharmony_ci ad_disable_collecting_distributing(port, 105662306a36Sopenharmony_ci update_slave_arr); 105762306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_COLLECTING; 105862306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING; 105962306a36Sopenharmony_ci port->ntt = true; 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci case AD_MUX_WAITING: 106262306a36Sopenharmony_ci port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0); 106362306a36Sopenharmony_ci break; 106462306a36Sopenharmony_ci case AD_MUX_ATTACHED: 106562306a36Sopenharmony_ci if (port->aggregator->is_active) 106662306a36Sopenharmony_ci port->actor_oper_port_state |= 106762306a36Sopenharmony_ci LACP_STATE_SYNCHRONIZATION; 106862306a36Sopenharmony_ci else 106962306a36Sopenharmony_ci port->actor_oper_port_state &= 107062306a36Sopenharmony_ci ~LACP_STATE_SYNCHRONIZATION; 107162306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_COLLECTING; 107262306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING; 107362306a36Sopenharmony_ci ad_disable_collecting_distributing(port, 107462306a36Sopenharmony_ci update_slave_arr); 107562306a36Sopenharmony_ci port->ntt = true; 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci case AD_MUX_COLLECTING_DISTRIBUTING: 107862306a36Sopenharmony_ci port->actor_oper_port_state |= LACP_STATE_COLLECTING; 107962306a36Sopenharmony_ci port->actor_oper_port_state |= LACP_STATE_DISTRIBUTING; 108062306a36Sopenharmony_ci port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION; 108162306a36Sopenharmony_ci ad_enable_collecting_distributing(port, 108262306a36Sopenharmony_ci update_slave_arr); 108362306a36Sopenharmony_ci port->ntt = true; 108462306a36Sopenharmony_ci break; 108562306a36Sopenharmony_ci default: 108662306a36Sopenharmony_ci break; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci/** 109262306a36Sopenharmony_ci * ad_rx_machine - handle a port's rx State Machine 109362306a36Sopenharmony_ci * @lacpdu: the lacpdu we've received 109462306a36Sopenharmony_ci * @port: the port we're looking at 109562306a36Sopenharmony_ci * 109662306a36Sopenharmony_ci * If lacpdu arrived, stop previous timer (if exists) and set the next state as 109762306a36Sopenharmony_ci * CURRENT. If timer expired set the state machine in the proper state. 109862306a36Sopenharmony_ci * In other cases, this function checks if we need to switch to other state. 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_cistatic void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci rx_states_t last_state; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* keep current State Machine state to compare later if it was 110562306a36Sopenharmony_ci * changed 110662306a36Sopenharmony_ci */ 110762306a36Sopenharmony_ci last_state = port->sm_rx_state; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci if (lacpdu) { 111062306a36Sopenharmony_ci atomic64_inc(&SLAVE_AD_INFO(port->slave)->stats.lacpdu_rx); 111162306a36Sopenharmony_ci atomic64_inc(&BOND_AD_INFO(port->slave->bond).stats.lacpdu_rx); 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci /* check if state machine should change state */ 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* first, check if port was reinitialized */ 111662306a36Sopenharmony_ci if (port->sm_vars & AD_PORT_BEGIN) { 111762306a36Sopenharmony_ci port->sm_rx_state = AD_RX_INITIALIZE; 111862306a36Sopenharmony_ci port->sm_vars |= AD_PORT_CHURNED; 111962306a36Sopenharmony_ci /* check if port is not enabled */ 112062306a36Sopenharmony_ci } else if (!(port->sm_vars & AD_PORT_BEGIN) && !port->is_enabled) 112162306a36Sopenharmony_ci port->sm_rx_state = AD_RX_PORT_DISABLED; 112262306a36Sopenharmony_ci /* check if new lacpdu arrived */ 112362306a36Sopenharmony_ci else if (lacpdu && ((port->sm_rx_state == AD_RX_EXPIRED) || 112462306a36Sopenharmony_ci (port->sm_rx_state == AD_RX_DEFAULTED) || 112562306a36Sopenharmony_ci (port->sm_rx_state == AD_RX_CURRENT))) { 112662306a36Sopenharmony_ci if (port->sm_rx_state != AD_RX_CURRENT) 112762306a36Sopenharmony_ci port->sm_vars |= AD_PORT_CHURNED; 112862306a36Sopenharmony_ci port->sm_rx_timer_counter = 0; 112962306a36Sopenharmony_ci port->sm_rx_state = AD_RX_CURRENT; 113062306a36Sopenharmony_ci } else { 113162306a36Sopenharmony_ci /* if timer is on, and if it is expired */ 113262306a36Sopenharmony_ci if (port->sm_rx_timer_counter && 113362306a36Sopenharmony_ci !(--port->sm_rx_timer_counter)) { 113462306a36Sopenharmony_ci switch (port->sm_rx_state) { 113562306a36Sopenharmony_ci case AD_RX_EXPIRED: 113662306a36Sopenharmony_ci port->sm_rx_state = AD_RX_DEFAULTED; 113762306a36Sopenharmony_ci break; 113862306a36Sopenharmony_ci case AD_RX_CURRENT: 113962306a36Sopenharmony_ci port->sm_rx_state = AD_RX_EXPIRED; 114062306a36Sopenharmony_ci break; 114162306a36Sopenharmony_ci default: 114262306a36Sopenharmony_ci break; 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci } else { 114562306a36Sopenharmony_ci /* if no lacpdu arrived and no timer is on */ 114662306a36Sopenharmony_ci switch (port->sm_rx_state) { 114762306a36Sopenharmony_ci case AD_RX_PORT_DISABLED: 114862306a36Sopenharmony_ci if (port->is_enabled && 114962306a36Sopenharmony_ci (port->sm_vars & AD_PORT_LACP_ENABLED)) 115062306a36Sopenharmony_ci port->sm_rx_state = AD_RX_EXPIRED; 115162306a36Sopenharmony_ci else if (port->is_enabled 115262306a36Sopenharmony_ci && ((port->sm_vars 115362306a36Sopenharmony_ci & AD_PORT_LACP_ENABLED) == 0)) 115462306a36Sopenharmony_ci port->sm_rx_state = AD_RX_LACP_DISABLED; 115562306a36Sopenharmony_ci break; 115662306a36Sopenharmony_ci default: 115762306a36Sopenharmony_ci break; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* check if the State machine was changed or new lacpdu arrived */ 116462306a36Sopenharmony_ci if ((port->sm_rx_state != last_state) || (lacpdu)) { 116562306a36Sopenharmony_ci slave_dbg(port->slave->bond->dev, port->slave->dev, 116662306a36Sopenharmony_ci "Rx Machine: Port=%d, Last State=%d, Curr State=%d\n", 116762306a36Sopenharmony_ci port->actor_port_number, 116862306a36Sopenharmony_ci last_state, 116962306a36Sopenharmony_ci port->sm_rx_state); 117062306a36Sopenharmony_ci switch (port->sm_rx_state) { 117162306a36Sopenharmony_ci case AD_RX_INITIALIZE: 117262306a36Sopenharmony_ci if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)) 117362306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_LACP_ENABLED; 117462306a36Sopenharmony_ci else 117562306a36Sopenharmony_ci port->sm_vars |= AD_PORT_LACP_ENABLED; 117662306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_SELECTED; 117762306a36Sopenharmony_ci __record_default(port); 117862306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; 117962306a36Sopenharmony_ci port->sm_rx_state = AD_RX_PORT_DISABLED; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci fallthrough; 118262306a36Sopenharmony_ci case AD_RX_PORT_DISABLED: 118362306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_MATCHED; 118462306a36Sopenharmony_ci break; 118562306a36Sopenharmony_ci case AD_RX_LACP_DISABLED: 118662306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_SELECTED; 118762306a36Sopenharmony_ci __record_default(port); 118862306a36Sopenharmony_ci port->partner_oper.port_state &= ~LACP_STATE_AGGREGATION; 118962306a36Sopenharmony_ci port->sm_vars |= AD_PORT_MATCHED; 119062306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; 119162306a36Sopenharmony_ci break; 119262306a36Sopenharmony_ci case AD_RX_EXPIRED: 119362306a36Sopenharmony_ci /* Reset of the Synchronization flag (Standard 43.4.12) 119462306a36Sopenharmony_ci * This reset cause to disable this port in the 119562306a36Sopenharmony_ci * COLLECTING_DISTRIBUTING state of the mux machine in 119662306a36Sopenharmony_ci * case of EXPIRED even if LINK_DOWN didn't arrive for 119762306a36Sopenharmony_ci * the port. 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_ci port->partner_oper.port_state &= ~LACP_STATE_SYNCHRONIZATION; 120062306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_MATCHED; 120162306a36Sopenharmony_ci port->partner_oper.port_state |= LACP_STATE_LACP_TIMEOUT; 120262306a36Sopenharmony_ci port->partner_oper.port_state |= LACP_STATE_LACP_ACTIVITY; 120362306a36Sopenharmony_ci port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(AD_SHORT_TIMEOUT)); 120462306a36Sopenharmony_ci port->actor_oper_port_state |= LACP_STATE_EXPIRED; 120562306a36Sopenharmony_ci port->sm_vars |= AD_PORT_CHURNED; 120662306a36Sopenharmony_ci break; 120762306a36Sopenharmony_ci case AD_RX_DEFAULTED: 120862306a36Sopenharmony_ci __update_default_selected(port); 120962306a36Sopenharmony_ci __record_default(port); 121062306a36Sopenharmony_ci port->sm_vars |= AD_PORT_MATCHED; 121162306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; 121262306a36Sopenharmony_ci break; 121362306a36Sopenharmony_ci case AD_RX_CURRENT: 121462306a36Sopenharmony_ci /* detect loopback situation */ 121562306a36Sopenharmony_ci if (MAC_ADDRESS_EQUAL(&(lacpdu->actor_system), 121662306a36Sopenharmony_ci &(port->actor_system))) { 121762306a36Sopenharmony_ci slave_err(port->slave->bond->dev, port->slave->dev, "An illegal loopback occurred on slave\n" 121862306a36Sopenharmony_ci "Check the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n"); 121962306a36Sopenharmony_ci return; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci __update_selected(lacpdu, port); 122262306a36Sopenharmony_ci __update_ntt(lacpdu, port); 122362306a36Sopenharmony_ci __record_pdu(lacpdu, port); 122462306a36Sopenharmony_ci port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT)); 122562306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; 122662306a36Sopenharmony_ci break; 122762306a36Sopenharmony_ci default: 122862306a36Sopenharmony_ci break; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci} 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci/** 123462306a36Sopenharmony_ci * ad_churn_machine - handle port churn's state machine 123562306a36Sopenharmony_ci * @port: the port we're looking at 123662306a36Sopenharmony_ci * 123762306a36Sopenharmony_ci */ 123862306a36Sopenharmony_cistatic void ad_churn_machine(struct port *port) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci if (port->sm_vars & AD_PORT_CHURNED) { 124162306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_CHURNED; 124262306a36Sopenharmony_ci port->sm_churn_actor_state = AD_CHURN_MONITOR; 124362306a36Sopenharmony_ci port->sm_churn_partner_state = AD_CHURN_MONITOR; 124462306a36Sopenharmony_ci port->sm_churn_actor_timer_counter = 124562306a36Sopenharmony_ci __ad_timer_to_ticks(AD_ACTOR_CHURN_TIMER, 0); 124662306a36Sopenharmony_ci port->sm_churn_partner_timer_counter = 124762306a36Sopenharmony_ci __ad_timer_to_ticks(AD_PARTNER_CHURN_TIMER, 0); 124862306a36Sopenharmony_ci return; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci if (port->sm_churn_actor_timer_counter && 125162306a36Sopenharmony_ci !(--port->sm_churn_actor_timer_counter) && 125262306a36Sopenharmony_ci port->sm_churn_actor_state == AD_CHURN_MONITOR) { 125362306a36Sopenharmony_ci if (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION) { 125462306a36Sopenharmony_ci port->sm_churn_actor_state = AD_NO_CHURN; 125562306a36Sopenharmony_ci } else { 125662306a36Sopenharmony_ci port->churn_actor_count++; 125762306a36Sopenharmony_ci port->sm_churn_actor_state = AD_CHURN; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci if (port->sm_churn_partner_timer_counter && 126162306a36Sopenharmony_ci !(--port->sm_churn_partner_timer_counter) && 126262306a36Sopenharmony_ci port->sm_churn_partner_state == AD_CHURN_MONITOR) { 126362306a36Sopenharmony_ci if (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) { 126462306a36Sopenharmony_ci port->sm_churn_partner_state = AD_NO_CHURN; 126562306a36Sopenharmony_ci } else { 126662306a36Sopenharmony_ci port->churn_partner_count++; 126762306a36Sopenharmony_ci port->sm_churn_partner_state = AD_CHURN; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci/** 127362306a36Sopenharmony_ci * ad_tx_machine - handle a port's tx state machine 127462306a36Sopenharmony_ci * @port: the port we're looking at 127562306a36Sopenharmony_ci */ 127662306a36Sopenharmony_cistatic void ad_tx_machine(struct port *port) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci /* check if tx timer expired, to verify that we do not send more than 127962306a36Sopenharmony_ci * 3 packets per second 128062306a36Sopenharmony_ci */ 128162306a36Sopenharmony_ci if (port->sm_tx_timer_counter && !(--port->sm_tx_timer_counter)) { 128262306a36Sopenharmony_ci /* check if there is something to send */ 128362306a36Sopenharmony_ci if (port->ntt && (port->sm_vars & AD_PORT_LACP_ENABLED)) { 128462306a36Sopenharmony_ci __update_lacpdu_from_port(port); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (ad_lacpdu_send(port) >= 0) { 128762306a36Sopenharmony_ci slave_dbg(port->slave->bond->dev, 128862306a36Sopenharmony_ci port->slave->dev, 128962306a36Sopenharmony_ci "Sent LACPDU on port %d\n", 129062306a36Sopenharmony_ci port->actor_port_number); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci /* mark ntt as false, so it will not be sent 129362306a36Sopenharmony_ci * again until demanded 129462306a36Sopenharmony_ci */ 129562306a36Sopenharmony_ci port->ntt = false; 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci /* restart tx timer(to verify that we will not exceed 129962306a36Sopenharmony_ci * AD_MAX_TX_IN_SECOND 130062306a36Sopenharmony_ci */ 130162306a36Sopenharmony_ci port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND; 130262306a36Sopenharmony_ci } 130362306a36Sopenharmony_ci} 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci/** 130662306a36Sopenharmony_ci * ad_periodic_machine - handle a port's periodic state machine 130762306a36Sopenharmony_ci * @port: the port we're looking at 130862306a36Sopenharmony_ci * @bond_params: bond parameters we will use 130962306a36Sopenharmony_ci * 131062306a36Sopenharmony_ci * Turn ntt flag on priodically to perform periodic transmission of lacpdu's. 131162306a36Sopenharmony_ci */ 131262306a36Sopenharmony_cistatic void ad_periodic_machine(struct port *port, struct bond_params *bond_params) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci periodic_states_t last_state; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci /* keep current state machine state to compare later if it was changed */ 131762306a36Sopenharmony_ci last_state = port->sm_periodic_state; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* check if port was reinitialized */ 132062306a36Sopenharmony_ci if (((port->sm_vars & AD_PORT_BEGIN) || !(port->sm_vars & AD_PORT_LACP_ENABLED) || !port->is_enabled) || 132162306a36Sopenharmony_ci (!(port->actor_oper_port_state & LACP_STATE_LACP_ACTIVITY) && !(port->partner_oper.port_state & LACP_STATE_LACP_ACTIVITY)) || 132262306a36Sopenharmony_ci !bond_params->lacp_active) { 132362306a36Sopenharmony_ci port->sm_periodic_state = AD_NO_PERIODIC; 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci /* check if state machine should change state */ 132662306a36Sopenharmony_ci else if (port->sm_periodic_timer_counter) { 132762306a36Sopenharmony_ci /* check if periodic state machine expired */ 132862306a36Sopenharmony_ci if (!(--port->sm_periodic_timer_counter)) { 132962306a36Sopenharmony_ci /* if expired then do tx */ 133062306a36Sopenharmony_ci port->sm_periodic_state = AD_PERIODIC_TX; 133162306a36Sopenharmony_ci } else { 133262306a36Sopenharmony_ci /* If not expired, check if there is some new timeout 133362306a36Sopenharmony_ci * parameter from the partner state 133462306a36Sopenharmony_ci */ 133562306a36Sopenharmony_ci switch (port->sm_periodic_state) { 133662306a36Sopenharmony_ci case AD_FAST_PERIODIC: 133762306a36Sopenharmony_ci if (!(port->partner_oper.port_state 133862306a36Sopenharmony_ci & LACP_STATE_LACP_TIMEOUT)) 133962306a36Sopenharmony_ci port->sm_periodic_state = AD_SLOW_PERIODIC; 134062306a36Sopenharmony_ci break; 134162306a36Sopenharmony_ci case AD_SLOW_PERIODIC: 134262306a36Sopenharmony_ci if ((port->partner_oper.port_state & LACP_STATE_LACP_TIMEOUT)) { 134362306a36Sopenharmony_ci port->sm_periodic_timer_counter = 0; 134462306a36Sopenharmony_ci port->sm_periodic_state = AD_PERIODIC_TX; 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci break; 134762306a36Sopenharmony_ci default: 134862306a36Sopenharmony_ci break; 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci } else { 135262306a36Sopenharmony_ci switch (port->sm_periodic_state) { 135362306a36Sopenharmony_ci case AD_NO_PERIODIC: 135462306a36Sopenharmony_ci port->sm_periodic_state = AD_FAST_PERIODIC; 135562306a36Sopenharmony_ci break; 135662306a36Sopenharmony_ci case AD_PERIODIC_TX: 135762306a36Sopenharmony_ci if (!(port->partner_oper.port_state & 135862306a36Sopenharmony_ci LACP_STATE_LACP_TIMEOUT)) 135962306a36Sopenharmony_ci port->sm_periodic_state = AD_SLOW_PERIODIC; 136062306a36Sopenharmony_ci else 136162306a36Sopenharmony_ci port->sm_periodic_state = AD_FAST_PERIODIC; 136262306a36Sopenharmony_ci break; 136362306a36Sopenharmony_ci default: 136462306a36Sopenharmony_ci break; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* check if the state machine was changed */ 136962306a36Sopenharmony_ci if (port->sm_periodic_state != last_state) { 137062306a36Sopenharmony_ci slave_dbg(port->slave->bond->dev, port->slave->dev, 137162306a36Sopenharmony_ci "Periodic Machine: Port=%d, Last State=%d, Curr State=%d\n", 137262306a36Sopenharmony_ci port->actor_port_number, last_state, 137362306a36Sopenharmony_ci port->sm_periodic_state); 137462306a36Sopenharmony_ci switch (port->sm_periodic_state) { 137562306a36Sopenharmony_ci case AD_NO_PERIODIC: 137662306a36Sopenharmony_ci port->sm_periodic_timer_counter = 0; 137762306a36Sopenharmony_ci break; 137862306a36Sopenharmony_ci case AD_FAST_PERIODIC: 137962306a36Sopenharmony_ci /* decrement 1 tick we lost in the PERIODIC_TX cycle */ 138062306a36Sopenharmony_ci port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_FAST_PERIODIC_TIME))-1; 138162306a36Sopenharmony_ci break; 138262306a36Sopenharmony_ci case AD_SLOW_PERIODIC: 138362306a36Sopenharmony_ci /* decrement 1 tick we lost in the PERIODIC_TX cycle */ 138462306a36Sopenharmony_ci port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_SLOW_PERIODIC_TIME))-1; 138562306a36Sopenharmony_ci break; 138662306a36Sopenharmony_ci case AD_PERIODIC_TX: 138762306a36Sopenharmony_ci port->ntt = true; 138862306a36Sopenharmony_ci break; 138962306a36Sopenharmony_ci default: 139062306a36Sopenharmony_ci break; 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci/** 139662306a36Sopenharmony_ci * ad_port_selection_logic - select aggregation groups 139762306a36Sopenharmony_ci * @port: the port we're looking at 139862306a36Sopenharmony_ci * @update_slave_arr: Does slave array need update? 139962306a36Sopenharmony_ci * 140062306a36Sopenharmony_ci * Select aggregation groups, and assign each port for it's aggregetor. The 140162306a36Sopenharmony_ci * selection logic is called in the inititalization (after all the handshkes), 140262306a36Sopenharmony_ci * and after every lacpdu receive (if selected is off). 140362306a36Sopenharmony_ci */ 140462306a36Sopenharmony_cistatic void ad_port_selection_logic(struct port *port, bool *update_slave_arr) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci struct aggregator *aggregator, *free_aggregator = NULL, *temp_aggregator; 140762306a36Sopenharmony_ci struct port *last_port = NULL, *curr_port; 140862306a36Sopenharmony_ci struct list_head *iter; 140962306a36Sopenharmony_ci struct bonding *bond; 141062306a36Sopenharmony_ci struct slave *slave; 141162306a36Sopenharmony_ci int found = 0; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci /* if the port is already Selected, do nothing */ 141462306a36Sopenharmony_ci if (port->sm_vars & AD_PORT_SELECTED) 141562306a36Sopenharmony_ci return; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci bond = __get_bond_by_port(port); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci /* if the port is connected to other aggregator, detach it */ 142062306a36Sopenharmony_ci if (port->aggregator) { 142162306a36Sopenharmony_ci /* detach the port from its former aggregator */ 142262306a36Sopenharmony_ci temp_aggregator = port->aggregator; 142362306a36Sopenharmony_ci for (curr_port = temp_aggregator->lag_ports; curr_port; 142462306a36Sopenharmony_ci last_port = curr_port, 142562306a36Sopenharmony_ci curr_port = curr_port->next_port_in_aggregator) { 142662306a36Sopenharmony_ci if (curr_port == port) { 142762306a36Sopenharmony_ci temp_aggregator->num_of_ports--; 142862306a36Sopenharmony_ci /* if it is the first port attached to the 142962306a36Sopenharmony_ci * aggregator 143062306a36Sopenharmony_ci */ 143162306a36Sopenharmony_ci if (!last_port) { 143262306a36Sopenharmony_ci temp_aggregator->lag_ports = 143362306a36Sopenharmony_ci port->next_port_in_aggregator; 143462306a36Sopenharmony_ci } else { 143562306a36Sopenharmony_ci /* not the first port attached to the 143662306a36Sopenharmony_ci * aggregator 143762306a36Sopenharmony_ci */ 143862306a36Sopenharmony_ci last_port->next_port_in_aggregator = 143962306a36Sopenharmony_ci port->next_port_in_aggregator; 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* clear the port's relations to this 144362306a36Sopenharmony_ci * aggregator 144462306a36Sopenharmony_ci */ 144562306a36Sopenharmony_ci port->aggregator = NULL; 144662306a36Sopenharmony_ci port->next_port_in_aggregator = NULL; 144762306a36Sopenharmony_ci port->actor_port_aggregator_identifier = 0; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci slave_dbg(bond->dev, port->slave->dev, "Port %d left LAG %d\n", 145062306a36Sopenharmony_ci port->actor_port_number, 145162306a36Sopenharmony_ci temp_aggregator->aggregator_identifier); 145262306a36Sopenharmony_ci /* if the aggregator is empty, clear its 145362306a36Sopenharmony_ci * parameters, and set it ready to be attached 145462306a36Sopenharmony_ci */ 145562306a36Sopenharmony_ci if (!temp_aggregator->lag_ports) 145662306a36Sopenharmony_ci ad_clear_agg(temp_aggregator); 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci if (!curr_port) { 146162306a36Sopenharmony_ci /* meaning: the port was related to an aggregator 146262306a36Sopenharmony_ci * but was not on the aggregator port list 146362306a36Sopenharmony_ci */ 146462306a36Sopenharmony_ci net_warn_ratelimited("%s: (slave %s): Warning: Port %d was related to aggregator %d but was not on its port list\n", 146562306a36Sopenharmony_ci port->slave->bond->dev->name, 146662306a36Sopenharmony_ci port->slave->dev->name, 146762306a36Sopenharmony_ci port->actor_port_number, 146862306a36Sopenharmony_ci port->aggregator->aggregator_identifier); 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci /* search on all aggregators for a suitable aggregator for this port */ 147262306a36Sopenharmony_ci bond_for_each_slave(bond, slave, iter) { 147362306a36Sopenharmony_ci aggregator = &(SLAVE_AD_INFO(slave)->aggregator); 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci /* keep a free aggregator for later use(if needed) */ 147662306a36Sopenharmony_ci if (!aggregator->lag_ports) { 147762306a36Sopenharmony_ci if (!free_aggregator) 147862306a36Sopenharmony_ci free_aggregator = aggregator; 147962306a36Sopenharmony_ci continue; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci /* check if current aggregator suits us */ 148262306a36Sopenharmony_ci if (((aggregator->actor_oper_aggregator_key == port->actor_oper_port_key) && /* if all parameters match AND */ 148362306a36Sopenharmony_ci MAC_ADDRESS_EQUAL(&(aggregator->partner_system), &(port->partner_oper.system)) && 148462306a36Sopenharmony_ci (aggregator->partner_system_priority == port->partner_oper.system_priority) && 148562306a36Sopenharmony_ci (aggregator->partner_oper_aggregator_key == port->partner_oper.key) 148662306a36Sopenharmony_ci ) && 148762306a36Sopenharmony_ci ((!MAC_ADDRESS_EQUAL(&(port->partner_oper.system), &(null_mac_addr)) && /* partner answers */ 148862306a36Sopenharmony_ci !aggregator->is_individual) /* but is not individual OR */ 148962306a36Sopenharmony_ci ) 149062306a36Sopenharmony_ci ) { 149162306a36Sopenharmony_ci /* attach to the founded aggregator */ 149262306a36Sopenharmony_ci port->aggregator = aggregator; 149362306a36Sopenharmony_ci port->actor_port_aggregator_identifier = 149462306a36Sopenharmony_ci port->aggregator->aggregator_identifier; 149562306a36Sopenharmony_ci port->next_port_in_aggregator = aggregator->lag_ports; 149662306a36Sopenharmony_ci port->aggregator->num_of_ports++; 149762306a36Sopenharmony_ci aggregator->lag_ports = port; 149862306a36Sopenharmony_ci slave_dbg(bond->dev, slave->dev, "Port %d joined LAG %d (existing LAG)\n", 149962306a36Sopenharmony_ci port->actor_port_number, 150062306a36Sopenharmony_ci port->aggregator->aggregator_identifier); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci /* mark this port as selected */ 150362306a36Sopenharmony_ci port->sm_vars |= AD_PORT_SELECTED; 150462306a36Sopenharmony_ci found = 1; 150562306a36Sopenharmony_ci break; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci /* the port couldn't find an aggregator - attach it to a new 151062306a36Sopenharmony_ci * aggregator 151162306a36Sopenharmony_ci */ 151262306a36Sopenharmony_ci if (!found) { 151362306a36Sopenharmony_ci if (free_aggregator) { 151462306a36Sopenharmony_ci /* assign port a new aggregator */ 151562306a36Sopenharmony_ci port->aggregator = free_aggregator; 151662306a36Sopenharmony_ci port->actor_port_aggregator_identifier = 151762306a36Sopenharmony_ci port->aggregator->aggregator_identifier; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci /* update the new aggregator's parameters 152062306a36Sopenharmony_ci * if port was responsed from the end-user 152162306a36Sopenharmony_ci */ 152262306a36Sopenharmony_ci if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS) 152362306a36Sopenharmony_ci /* if port is full duplex */ 152462306a36Sopenharmony_ci port->aggregator->is_individual = false; 152562306a36Sopenharmony_ci else 152662306a36Sopenharmony_ci port->aggregator->is_individual = true; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci port->aggregator->actor_admin_aggregator_key = 152962306a36Sopenharmony_ci port->actor_admin_port_key; 153062306a36Sopenharmony_ci port->aggregator->actor_oper_aggregator_key = 153162306a36Sopenharmony_ci port->actor_oper_port_key; 153262306a36Sopenharmony_ci port->aggregator->partner_system = 153362306a36Sopenharmony_ci port->partner_oper.system; 153462306a36Sopenharmony_ci port->aggregator->partner_system_priority = 153562306a36Sopenharmony_ci port->partner_oper.system_priority; 153662306a36Sopenharmony_ci port->aggregator->partner_oper_aggregator_key = port->partner_oper.key; 153762306a36Sopenharmony_ci port->aggregator->receive_state = 1; 153862306a36Sopenharmony_ci port->aggregator->transmit_state = 1; 153962306a36Sopenharmony_ci port->aggregator->lag_ports = port; 154062306a36Sopenharmony_ci port->aggregator->num_of_ports++; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci /* mark this port as selected */ 154362306a36Sopenharmony_ci port->sm_vars |= AD_PORT_SELECTED; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci slave_dbg(bond->dev, port->slave->dev, "Port %d joined LAG %d (new LAG)\n", 154662306a36Sopenharmony_ci port->actor_port_number, 154762306a36Sopenharmony_ci port->aggregator->aggregator_identifier); 154862306a36Sopenharmony_ci } else { 154962306a36Sopenharmony_ci slave_err(bond->dev, port->slave->dev, 155062306a36Sopenharmony_ci "Port %d did not find a suitable aggregator\n", 155162306a36Sopenharmony_ci port->actor_port_number); 155262306a36Sopenharmony_ci return; 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci /* if all aggregator's ports are READY_N == TRUE, set ready=TRUE 155662306a36Sopenharmony_ci * in all aggregator's ports, else set ready=FALSE in all 155762306a36Sopenharmony_ci * aggregator's ports 155862306a36Sopenharmony_ci */ 155962306a36Sopenharmony_ci __set_agg_ports_ready(port->aggregator, 156062306a36Sopenharmony_ci __agg_ports_are_ready(port->aggregator)); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci aggregator = __get_first_agg(port); 156362306a36Sopenharmony_ci ad_agg_selection_logic(aggregator, update_slave_arr); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (!port->aggregator->is_active) 156662306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; 156762306a36Sopenharmony_ci} 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci/* Decide if "agg" is a better choice for the new active aggregator that 157062306a36Sopenharmony_ci * the current best, according to the ad_select policy. 157162306a36Sopenharmony_ci */ 157262306a36Sopenharmony_cistatic struct aggregator *ad_agg_selection_test(struct aggregator *best, 157362306a36Sopenharmony_ci struct aggregator *curr) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci /* 0. If no best, select current. 157662306a36Sopenharmony_ci * 157762306a36Sopenharmony_ci * 1. If the current agg is not individual, and the best is 157862306a36Sopenharmony_ci * individual, select current. 157962306a36Sopenharmony_ci * 158062306a36Sopenharmony_ci * 2. If current agg is individual and the best is not, keep best. 158162306a36Sopenharmony_ci * 158262306a36Sopenharmony_ci * 3. Therefore, current and best are both individual or both not 158362306a36Sopenharmony_ci * individual, so: 158462306a36Sopenharmony_ci * 158562306a36Sopenharmony_ci * 3a. If current agg partner replied, and best agg partner did not, 158662306a36Sopenharmony_ci * select current. 158762306a36Sopenharmony_ci * 158862306a36Sopenharmony_ci * 3b. If current agg partner did not reply and best agg partner 158962306a36Sopenharmony_ci * did reply, keep best. 159062306a36Sopenharmony_ci * 159162306a36Sopenharmony_ci * 4. Therefore, current and best both have partner replies or 159262306a36Sopenharmony_ci * both do not, so perform selection policy: 159362306a36Sopenharmony_ci * 159462306a36Sopenharmony_ci * BOND_AD_COUNT: Select by count of ports. If count is equal, 159562306a36Sopenharmony_ci * select by bandwidth. 159662306a36Sopenharmony_ci * 159762306a36Sopenharmony_ci * BOND_AD_STABLE, BOND_AD_BANDWIDTH: Select by bandwidth. 159862306a36Sopenharmony_ci */ 159962306a36Sopenharmony_ci if (!best) 160062306a36Sopenharmony_ci return curr; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci if (!curr->is_individual && best->is_individual) 160362306a36Sopenharmony_ci return curr; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci if (curr->is_individual && !best->is_individual) 160662306a36Sopenharmony_ci return best; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci if (__agg_has_partner(curr) && !__agg_has_partner(best)) 160962306a36Sopenharmony_ci return curr; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci if (!__agg_has_partner(curr) && __agg_has_partner(best)) 161262306a36Sopenharmony_ci return best; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci switch (__get_agg_selection_mode(curr->lag_ports)) { 161562306a36Sopenharmony_ci case BOND_AD_COUNT: 161662306a36Sopenharmony_ci if (__agg_active_ports(curr) > __agg_active_ports(best)) 161762306a36Sopenharmony_ci return curr; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci if (__agg_active_ports(curr) < __agg_active_ports(best)) 162062306a36Sopenharmony_ci return best; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci fallthrough; 162362306a36Sopenharmony_ci case BOND_AD_STABLE: 162462306a36Sopenharmony_ci case BOND_AD_BANDWIDTH: 162562306a36Sopenharmony_ci if (__get_agg_bandwidth(curr) > __get_agg_bandwidth(best)) 162662306a36Sopenharmony_ci return curr; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci break; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci default: 163162306a36Sopenharmony_ci net_warn_ratelimited("%s: (slave %s): Impossible agg select mode %d\n", 163262306a36Sopenharmony_ci curr->slave->bond->dev->name, 163362306a36Sopenharmony_ci curr->slave->dev->name, 163462306a36Sopenharmony_ci __get_agg_selection_mode(curr->lag_ports)); 163562306a36Sopenharmony_ci break; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci return best; 163962306a36Sopenharmony_ci} 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_cistatic int agg_device_up(const struct aggregator *agg) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci struct port *port = agg->lag_ports; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci if (!port) 164662306a36Sopenharmony_ci return 0; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci for (port = agg->lag_ports; port; 164962306a36Sopenharmony_ci port = port->next_port_in_aggregator) { 165062306a36Sopenharmony_ci if (netif_running(port->slave->dev) && 165162306a36Sopenharmony_ci netif_carrier_ok(port->slave->dev)) 165262306a36Sopenharmony_ci return 1; 165362306a36Sopenharmony_ci } 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci return 0; 165662306a36Sopenharmony_ci} 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci/** 165962306a36Sopenharmony_ci * ad_agg_selection_logic - select an aggregation group for a team 166062306a36Sopenharmony_ci * @agg: the aggregator we're looking at 166162306a36Sopenharmony_ci * @update_slave_arr: Does slave array need update? 166262306a36Sopenharmony_ci * 166362306a36Sopenharmony_ci * It is assumed that only one aggregator may be selected for a team. 166462306a36Sopenharmony_ci * 166562306a36Sopenharmony_ci * The logic of this function is to select the aggregator according to 166662306a36Sopenharmony_ci * the ad_select policy: 166762306a36Sopenharmony_ci * 166862306a36Sopenharmony_ci * BOND_AD_STABLE: select the aggregator with the most ports attached to 166962306a36Sopenharmony_ci * it, and to reselect the active aggregator only if the previous 167062306a36Sopenharmony_ci * aggregator has no more ports related to it. 167162306a36Sopenharmony_ci * 167262306a36Sopenharmony_ci * BOND_AD_BANDWIDTH: select the aggregator with the highest total 167362306a36Sopenharmony_ci * bandwidth, and reselect whenever a link state change takes place or the 167462306a36Sopenharmony_ci * set of slaves in the bond changes. 167562306a36Sopenharmony_ci * 167662306a36Sopenharmony_ci * BOND_AD_COUNT: select the aggregator with largest number of ports 167762306a36Sopenharmony_ci * (slaves), and reselect whenever a link state change takes place or the 167862306a36Sopenharmony_ci * set of slaves in the bond changes. 167962306a36Sopenharmony_ci * 168062306a36Sopenharmony_ci * FIXME: this function MUST be called with the first agg in the bond, or 168162306a36Sopenharmony_ci * __get_active_agg() won't work correctly. This function should be better 168262306a36Sopenharmony_ci * called with the bond itself, and retrieve the first agg from it. 168362306a36Sopenharmony_ci */ 168462306a36Sopenharmony_cistatic void ad_agg_selection_logic(struct aggregator *agg, 168562306a36Sopenharmony_ci bool *update_slave_arr) 168662306a36Sopenharmony_ci{ 168762306a36Sopenharmony_ci struct aggregator *best, *active, *origin; 168862306a36Sopenharmony_ci struct bonding *bond = agg->slave->bond; 168962306a36Sopenharmony_ci struct list_head *iter; 169062306a36Sopenharmony_ci struct slave *slave; 169162306a36Sopenharmony_ci struct port *port; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci rcu_read_lock(); 169462306a36Sopenharmony_ci origin = agg; 169562306a36Sopenharmony_ci active = __get_active_agg(agg); 169662306a36Sopenharmony_ci best = (active && agg_device_up(active)) ? active : NULL; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci bond_for_each_slave_rcu(bond, slave, iter) { 169962306a36Sopenharmony_ci agg = &(SLAVE_AD_INFO(slave)->aggregator); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci agg->is_active = 0; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci if (__agg_active_ports(agg) && agg_device_up(agg)) 170462306a36Sopenharmony_ci best = ad_agg_selection_test(best, agg); 170562306a36Sopenharmony_ci } 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci if (best && 170862306a36Sopenharmony_ci __get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) { 170962306a36Sopenharmony_ci /* For the STABLE policy, don't replace the old active 171062306a36Sopenharmony_ci * aggregator if it's still active (it has an answering 171162306a36Sopenharmony_ci * partner) or if both the best and active don't have an 171262306a36Sopenharmony_ci * answering partner. 171362306a36Sopenharmony_ci */ 171462306a36Sopenharmony_ci if (active && active->lag_ports && 171562306a36Sopenharmony_ci __agg_active_ports(active) && 171662306a36Sopenharmony_ci (__agg_has_partner(active) || 171762306a36Sopenharmony_ci (!__agg_has_partner(active) && 171862306a36Sopenharmony_ci !__agg_has_partner(best)))) { 171962306a36Sopenharmony_ci if (!(!active->actor_oper_aggregator_key && 172062306a36Sopenharmony_ci best->actor_oper_aggregator_key)) { 172162306a36Sopenharmony_ci best = NULL; 172262306a36Sopenharmony_ci active->is_active = 1; 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci } 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci if (best && (best == active)) { 172862306a36Sopenharmony_ci best = NULL; 172962306a36Sopenharmony_ci active->is_active = 1; 173062306a36Sopenharmony_ci } 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci /* if there is new best aggregator, activate it */ 173362306a36Sopenharmony_ci if (best) { 173462306a36Sopenharmony_ci netdev_dbg(bond->dev, "(slave %s): best Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", 173562306a36Sopenharmony_ci best->slave ? best->slave->dev->name : "NULL", 173662306a36Sopenharmony_ci best->aggregator_identifier, best->num_of_ports, 173762306a36Sopenharmony_ci best->actor_oper_aggregator_key, 173862306a36Sopenharmony_ci best->partner_oper_aggregator_key, 173962306a36Sopenharmony_ci best->is_individual, best->is_active); 174062306a36Sopenharmony_ci netdev_dbg(bond->dev, "(slave %s): best ports %p slave %p\n", 174162306a36Sopenharmony_ci best->slave ? best->slave->dev->name : "NULL", 174262306a36Sopenharmony_ci best->lag_ports, best->slave); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci bond_for_each_slave_rcu(bond, slave, iter) { 174562306a36Sopenharmony_ci agg = &(SLAVE_AD_INFO(slave)->aggregator); 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci slave_dbg(bond->dev, slave->dev, "Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", 174862306a36Sopenharmony_ci agg->aggregator_identifier, agg->num_of_ports, 174962306a36Sopenharmony_ci agg->actor_oper_aggregator_key, 175062306a36Sopenharmony_ci agg->partner_oper_aggregator_key, 175162306a36Sopenharmony_ci agg->is_individual, agg->is_active); 175262306a36Sopenharmony_ci } 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci /* check if any partner replies */ 175562306a36Sopenharmony_ci if (best->is_individual) 175662306a36Sopenharmony_ci net_warn_ratelimited("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n", 175762306a36Sopenharmony_ci bond->dev->name); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci best->is_active = 1; 176062306a36Sopenharmony_ci netdev_dbg(bond->dev, "(slave %s): LAG %d chosen as the active LAG\n", 176162306a36Sopenharmony_ci best->slave ? best->slave->dev->name : "NULL", 176262306a36Sopenharmony_ci best->aggregator_identifier); 176362306a36Sopenharmony_ci netdev_dbg(bond->dev, "(slave %s): Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", 176462306a36Sopenharmony_ci best->slave ? best->slave->dev->name : "NULL", 176562306a36Sopenharmony_ci best->aggregator_identifier, best->num_of_ports, 176662306a36Sopenharmony_ci best->actor_oper_aggregator_key, 176762306a36Sopenharmony_ci best->partner_oper_aggregator_key, 176862306a36Sopenharmony_ci best->is_individual, best->is_active); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci /* disable the ports that were related to the former 177162306a36Sopenharmony_ci * active_aggregator 177262306a36Sopenharmony_ci */ 177362306a36Sopenharmony_ci if (active) { 177462306a36Sopenharmony_ci for (port = active->lag_ports; port; 177562306a36Sopenharmony_ci port = port->next_port_in_aggregator) { 177662306a36Sopenharmony_ci __disable_port(port); 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci /* Slave array needs update. */ 178062306a36Sopenharmony_ci *update_slave_arr = true; 178162306a36Sopenharmony_ci } 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci /* if the selected aggregator is of join individuals 178462306a36Sopenharmony_ci * (partner_system is NULL), enable their ports 178562306a36Sopenharmony_ci */ 178662306a36Sopenharmony_ci active = __get_active_agg(origin); 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci if (active) { 178962306a36Sopenharmony_ci if (!__agg_has_partner(active)) { 179062306a36Sopenharmony_ci for (port = active->lag_ports; port; 179162306a36Sopenharmony_ci port = port->next_port_in_aggregator) { 179262306a36Sopenharmony_ci __enable_port(port); 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci *update_slave_arr = true; 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci } 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci rcu_read_unlock(); 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci bond_3ad_set_carrier(bond); 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci/** 180462306a36Sopenharmony_ci * ad_clear_agg - clear a given aggregator's parameters 180562306a36Sopenharmony_ci * @aggregator: the aggregator we're looking at 180662306a36Sopenharmony_ci */ 180762306a36Sopenharmony_cistatic void ad_clear_agg(struct aggregator *aggregator) 180862306a36Sopenharmony_ci{ 180962306a36Sopenharmony_ci if (aggregator) { 181062306a36Sopenharmony_ci aggregator->is_individual = false; 181162306a36Sopenharmony_ci aggregator->actor_admin_aggregator_key = 0; 181262306a36Sopenharmony_ci aggregator->actor_oper_aggregator_key = 0; 181362306a36Sopenharmony_ci eth_zero_addr(aggregator->partner_system.mac_addr_value); 181462306a36Sopenharmony_ci aggregator->partner_system_priority = 0; 181562306a36Sopenharmony_ci aggregator->partner_oper_aggregator_key = 0; 181662306a36Sopenharmony_ci aggregator->receive_state = 0; 181762306a36Sopenharmony_ci aggregator->transmit_state = 0; 181862306a36Sopenharmony_ci aggregator->lag_ports = NULL; 181962306a36Sopenharmony_ci aggregator->is_active = 0; 182062306a36Sopenharmony_ci aggregator->num_of_ports = 0; 182162306a36Sopenharmony_ci pr_debug("%s: LAG %d was cleared\n", 182262306a36Sopenharmony_ci aggregator->slave ? 182362306a36Sopenharmony_ci aggregator->slave->dev->name : "NULL", 182462306a36Sopenharmony_ci aggregator->aggregator_identifier); 182562306a36Sopenharmony_ci } 182662306a36Sopenharmony_ci} 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci/** 182962306a36Sopenharmony_ci * ad_initialize_agg - initialize a given aggregator's parameters 183062306a36Sopenharmony_ci * @aggregator: the aggregator we're looking at 183162306a36Sopenharmony_ci */ 183262306a36Sopenharmony_cistatic void ad_initialize_agg(struct aggregator *aggregator) 183362306a36Sopenharmony_ci{ 183462306a36Sopenharmony_ci if (aggregator) { 183562306a36Sopenharmony_ci ad_clear_agg(aggregator); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci eth_zero_addr(aggregator->aggregator_mac_address.mac_addr_value); 183862306a36Sopenharmony_ci aggregator->aggregator_identifier = 0; 183962306a36Sopenharmony_ci aggregator->slave = NULL; 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci} 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci/** 184462306a36Sopenharmony_ci * ad_initialize_port - initialize a given port's parameters 184562306a36Sopenharmony_ci * @port: the port we're looking at 184662306a36Sopenharmony_ci * @lacp_fast: boolean. whether fast periodic should be used 184762306a36Sopenharmony_ci */ 184862306a36Sopenharmony_cistatic void ad_initialize_port(struct port *port, int lacp_fast) 184962306a36Sopenharmony_ci{ 185062306a36Sopenharmony_ci static const struct port_params tmpl = { 185162306a36Sopenharmony_ci .system_priority = 0xffff, 185262306a36Sopenharmony_ci .key = 1, 185362306a36Sopenharmony_ci .port_number = 1, 185462306a36Sopenharmony_ci .port_priority = 0xff, 185562306a36Sopenharmony_ci .port_state = 1, 185662306a36Sopenharmony_ci }; 185762306a36Sopenharmony_ci static const struct lacpdu lacpdu = { 185862306a36Sopenharmony_ci .subtype = 0x01, 185962306a36Sopenharmony_ci .version_number = 0x01, 186062306a36Sopenharmony_ci .tlv_type_actor_info = 0x01, 186162306a36Sopenharmony_ci .actor_information_length = 0x14, 186262306a36Sopenharmony_ci .tlv_type_partner_info = 0x02, 186362306a36Sopenharmony_ci .partner_information_length = 0x14, 186462306a36Sopenharmony_ci .tlv_type_collector_info = 0x03, 186562306a36Sopenharmony_ci .collector_information_length = 0x10, 186662306a36Sopenharmony_ci .collector_max_delay = htons(AD_COLLECTOR_MAX_DELAY), 186762306a36Sopenharmony_ci }; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci if (port) { 187062306a36Sopenharmony_ci port->actor_port_priority = 0xff; 187162306a36Sopenharmony_ci port->actor_port_aggregator_identifier = 0; 187262306a36Sopenharmony_ci port->ntt = false; 187362306a36Sopenharmony_ci port->actor_admin_port_state = LACP_STATE_AGGREGATION | 187462306a36Sopenharmony_ci LACP_STATE_LACP_ACTIVITY; 187562306a36Sopenharmony_ci port->actor_oper_port_state = LACP_STATE_AGGREGATION | 187662306a36Sopenharmony_ci LACP_STATE_LACP_ACTIVITY; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci if (lacp_fast) 187962306a36Sopenharmony_ci port->actor_oper_port_state |= LACP_STATE_LACP_TIMEOUT; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci memcpy(&port->partner_admin, &tmpl, sizeof(tmpl)); 188262306a36Sopenharmony_ci memcpy(&port->partner_oper, &tmpl, sizeof(tmpl)); 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci port->is_enabled = true; 188562306a36Sopenharmony_ci /* private parameters */ 188662306a36Sopenharmony_ci port->sm_vars = AD_PORT_BEGIN | AD_PORT_LACP_ENABLED; 188762306a36Sopenharmony_ci port->sm_rx_state = 0; 188862306a36Sopenharmony_ci port->sm_rx_timer_counter = 0; 188962306a36Sopenharmony_ci port->sm_periodic_state = 0; 189062306a36Sopenharmony_ci port->sm_periodic_timer_counter = 0; 189162306a36Sopenharmony_ci port->sm_mux_state = 0; 189262306a36Sopenharmony_ci port->sm_mux_timer_counter = 0; 189362306a36Sopenharmony_ci port->sm_tx_state = 0; 189462306a36Sopenharmony_ci port->aggregator = NULL; 189562306a36Sopenharmony_ci port->next_port_in_aggregator = NULL; 189662306a36Sopenharmony_ci port->transaction_id = 0; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci port->sm_churn_actor_timer_counter = 0; 189962306a36Sopenharmony_ci port->sm_churn_actor_state = 0; 190062306a36Sopenharmony_ci port->churn_actor_count = 0; 190162306a36Sopenharmony_ci port->sm_churn_partner_timer_counter = 0; 190262306a36Sopenharmony_ci port->sm_churn_partner_state = 0; 190362306a36Sopenharmony_ci port->churn_partner_count = 0; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci memcpy(&port->lacpdu, &lacpdu, sizeof(lacpdu)); 190662306a36Sopenharmony_ci } 190762306a36Sopenharmony_ci} 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci/** 191062306a36Sopenharmony_ci * ad_enable_collecting_distributing - enable a port's transmit/receive 191162306a36Sopenharmony_ci * @port: the port we're looking at 191262306a36Sopenharmony_ci * @update_slave_arr: Does slave array need update? 191362306a36Sopenharmony_ci * 191462306a36Sopenharmony_ci * Enable @port if it's in an active aggregator 191562306a36Sopenharmony_ci */ 191662306a36Sopenharmony_cistatic void ad_enable_collecting_distributing(struct port *port, 191762306a36Sopenharmony_ci bool *update_slave_arr) 191862306a36Sopenharmony_ci{ 191962306a36Sopenharmony_ci if (port->aggregator->is_active) { 192062306a36Sopenharmony_ci slave_dbg(port->slave->bond->dev, port->slave->dev, 192162306a36Sopenharmony_ci "Enabling port %d (LAG %d)\n", 192262306a36Sopenharmony_ci port->actor_port_number, 192362306a36Sopenharmony_ci port->aggregator->aggregator_identifier); 192462306a36Sopenharmony_ci __enable_port(port); 192562306a36Sopenharmony_ci /* Slave array needs update */ 192662306a36Sopenharmony_ci *update_slave_arr = true; 192762306a36Sopenharmony_ci } 192862306a36Sopenharmony_ci} 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci/** 193162306a36Sopenharmony_ci * ad_disable_collecting_distributing - disable a port's transmit/receive 193262306a36Sopenharmony_ci * @port: the port we're looking at 193362306a36Sopenharmony_ci * @update_slave_arr: Does slave array need update? 193462306a36Sopenharmony_ci */ 193562306a36Sopenharmony_cistatic void ad_disable_collecting_distributing(struct port *port, 193662306a36Sopenharmony_ci bool *update_slave_arr) 193762306a36Sopenharmony_ci{ 193862306a36Sopenharmony_ci if (port->aggregator && 193962306a36Sopenharmony_ci !MAC_ADDRESS_EQUAL(&(port->aggregator->partner_system), 194062306a36Sopenharmony_ci &(null_mac_addr))) { 194162306a36Sopenharmony_ci slave_dbg(port->slave->bond->dev, port->slave->dev, 194262306a36Sopenharmony_ci "Disabling port %d (LAG %d)\n", 194362306a36Sopenharmony_ci port->actor_port_number, 194462306a36Sopenharmony_ci port->aggregator->aggregator_identifier); 194562306a36Sopenharmony_ci __disable_port(port); 194662306a36Sopenharmony_ci /* Slave array needs an update */ 194762306a36Sopenharmony_ci *update_slave_arr = true; 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci} 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci/** 195262306a36Sopenharmony_ci * ad_marker_info_received - handle receive of a Marker information frame 195362306a36Sopenharmony_ci * @marker_info: Marker info received 195462306a36Sopenharmony_ci * @port: the port we're looking at 195562306a36Sopenharmony_ci */ 195662306a36Sopenharmony_cistatic void ad_marker_info_received(struct bond_marker *marker_info, 195762306a36Sopenharmony_ci struct port *port) 195862306a36Sopenharmony_ci{ 195962306a36Sopenharmony_ci struct bond_marker marker; 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci atomic64_inc(&SLAVE_AD_INFO(port->slave)->stats.marker_rx); 196262306a36Sopenharmony_ci atomic64_inc(&BOND_AD_INFO(port->slave->bond).stats.marker_rx); 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci /* copy the received marker data to the response marker */ 196562306a36Sopenharmony_ci memcpy(&marker, marker_info, sizeof(struct bond_marker)); 196662306a36Sopenharmony_ci /* change the marker subtype to marker response */ 196762306a36Sopenharmony_ci marker.tlv_type = AD_MARKER_RESPONSE_SUBTYPE; 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci /* send the marker response */ 197062306a36Sopenharmony_ci if (ad_marker_send(port, &marker) >= 0) 197162306a36Sopenharmony_ci slave_dbg(port->slave->bond->dev, port->slave->dev, 197262306a36Sopenharmony_ci "Sent Marker Response on port %d\n", 197362306a36Sopenharmony_ci port->actor_port_number); 197462306a36Sopenharmony_ci} 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci/** 197762306a36Sopenharmony_ci * ad_marker_response_received - handle receive of a marker response frame 197862306a36Sopenharmony_ci * @marker: marker PDU received 197962306a36Sopenharmony_ci * @port: the port we're looking at 198062306a36Sopenharmony_ci * 198162306a36Sopenharmony_ci * This function does nothing since we decided not to implement send and handle 198262306a36Sopenharmony_ci * response for marker PDU's, in this stage, but only to respond to marker 198362306a36Sopenharmony_ci * information. 198462306a36Sopenharmony_ci */ 198562306a36Sopenharmony_cistatic void ad_marker_response_received(struct bond_marker *marker, 198662306a36Sopenharmony_ci struct port *port) 198762306a36Sopenharmony_ci{ 198862306a36Sopenharmony_ci atomic64_inc(&SLAVE_AD_INFO(port->slave)->stats.marker_resp_rx); 198962306a36Sopenharmony_ci atomic64_inc(&BOND_AD_INFO(port->slave->bond).stats.marker_resp_rx); 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci /* DO NOTHING, SINCE WE DECIDED NOT TO IMPLEMENT THIS FEATURE FOR NOW */ 199262306a36Sopenharmony_ci} 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci/* ========= AD exported functions to the main bonding code ========= */ 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci/* Check aggregators status in team every T seconds */ 199762306a36Sopenharmony_ci#define AD_AGGREGATOR_SELECTION_TIMER 8 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci/** 200062306a36Sopenharmony_ci * bond_3ad_initiate_agg_selection - initate aggregator selection 200162306a36Sopenharmony_ci * @bond: bonding struct 200262306a36Sopenharmony_ci * @timeout: timeout value to set 200362306a36Sopenharmony_ci * 200462306a36Sopenharmony_ci * Set the aggregation selection timer, to initiate an agg selection in 200562306a36Sopenharmony_ci * the very near future. Called during first initialization, and during 200662306a36Sopenharmony_ci * any down to up transitions of the bond. 200762306a36Sopenharmony_ci */ 200862306a36Sopenharmony_civoid bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout) 200962306a36Sopenharmony_ci{ 201062306a36Sopenharmony_ci atomic_set(&BOND_AD_INFO(bond).agg_select_timer, timeout); 201162306a36Sopenharmony_ci} 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci/** 201462306a36Sopenharmony_ci * bond_3ad_initialize - initialize a bond's 802.3ad parameters and structures 201562306a36Sopenharmony_ci * @bond: bonding struct to work on 201662306a36Sopenharmony_ci * 201762306a36Sopenharmony_ci * Can be called only after the mac address of the bond is set. 201862306a36Sopenharmony_ci */ 201962306a36Sopenharmony_civoid bond_3ad_initialize(struct bonding *bond) 202062306a36Sopenharmony_ci{ 202162306a36Sopenharmony_ci BOND_AD_INFO(bond).aggregator_identifier = 0; 202262306a36Sopenharmony_ci BOND_AD_INFO(bond).system.sys_priority = 202362306a36Sopenharmony_ci bond->params.ad_actor_sys_prio; 202462306a36Sopenharmony_ci if (is_zero_ether_addr(bond->params.ad_actor_system)) 202562306a36Sopenharmony_ci BOND_AD_INFO(bond).system.sys_mac_addr = 202662306a36Sopenharmony_ci *((struct mac_addr *)bond->dev->dev_addr); 202762306a36Sopenharmony_ci else 202862306a36Sopenharmony_ci BOND_AD_INFO(bond).system.sys_mac_addr = 202962306a36Sopenharmony_ci *((struct mac_addr *)bond->params.ad_actor_system); 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci bond_3ad_initiate_agg_selection(bond, 203262306a36Sopenharmony_ci AD_AGGREGATOR_SELECTION_TIMER * 203362306a36Sopenharmony_ci ad_ticks_per_sec); 203462306a36Sopenharmony_ci} 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci/** 203762306a36Sopenharmony_ci * bond_3ad_bind_slave - initialize a slave's port 203862306a36Sopenharmony_ci * @slave: slave struct to work on 203962306a36Sopenharmony_ci * 204062306a36Sopenharmony_ci * Returns: 0 on success 204162306a36Sopenharmony_ci * < 0 on error 204262306a36Sopenharmony_ci */ 204362306a36Sopenharmony_civoid bond_3ad_bind_slave(struct slave *slave) 204462306a36Sopenharmony_ci{ 204562306a36Sopenharmony_ci struct bonding *bond = bond_get_bond_by_slave(slave); 204662306a36Sopenharmony_ci struct port *port; 204762306a36Sopenharmony_ci struct aggregator *aggregator; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci /* check that the slave has not been initialized yet. */ 205062306a36Sopenharmony_ci if (SLAVE_AD_INFO(slave)->port.slave != slave) { 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci /* port initialization */ 205362306a36Sopenharmony_ci port = &(SLAVE_AD_INFO(slave)->port); 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci ad_initialize_port(port, bond->params.lacp_fast); 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci port->slave = slave; 205862306a36Sopenharmony_ci port->actor_port_number = SLAVE_AD_INFO(slave)->id; 205962306a36Sopenharmony_ci /* key is determined according to the link speed, duplex and 206062306a36Sopenharmony_ci * user key 206162306a36Sopenharmony_ci */ 206262306a36Sopenharmony_ci port->actor_admin_port_key = bond->params.ad_user_port_key << 6; 206362306a36Sopenharmony_ci ad_update_actor_keys(port, false); 206462306a36Sopenharmony_ci /* actor system is the bond's system */ 206562306a36Sopenharmony_ci __ad_actor_update_port(port); 206662306a36Sopenharmony_ci /* tx timer(to verify that no more than MAX_TX_IN_SECOND 206762306a36Sopenharmony_ci * lacpdu's are sent in one second) 206862306a36Sopenharmony_ci */ 206962306a36Sopenharmony_ci port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci __disable_port(port); 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci /* aggregator initialization */ 207462306a36Sopenharmony_ci aggregator = &(SLAVE_AD_INFO(slave)->aggregator); 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci ad_initialize_agg(aggregator); 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci aggregator->aggregator_mac_address = *((struct mac_addr *)bond->dev->dev_addr); 207962306a36Sopenharmony_ci aggregator->aggregator_identifier = ++BOND_AD_INFO(bond).aggregator_identifier; 208062306a36Sopenharmony_ci aggregator->slave = slave; 208162306a36Sopenharmony_ci aggregator->is_active = 0; 208262306a36Sopenharmony_ci aggregator->num_of_ports = 0; 208362306a36Sopenharmony_ci } 208462306a36Sopenharmony_ci} 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci/** 208762306a36Sopenharmony_ci * bond_3ad_unbind_slave - deinitialize a slave's port 208862306a36Sopenharmony_ci * @slave: slave struct to work on 208962306a36Sopenharmony_ci * 209062306a36Sopenharmony_ci * Search for the aggregator that is related to this port, remove the 209162306a36Sopenharmony_ci * aggregator and assign another aggregator for other port related to it 209262306a36Sopenharmony_ci * (if any), and remove the port. 209362306a36Sopenharmony_ci */ 209462306a36Sopenharmony_civoid bond_3ad_unbind_slave(struct slave *slave) 209562306a36Sopenharmony_ci{ 209662306a36Sopenharmony_ci struct port *port, *prev_port, *temp_port; 209762306a36Sopenharmony_ci struct aggregator *aggregator, *new_aggregator, *temp_aggregator; 209862306a36Sopenharmony_ci int select_new_active_agg = 0; 209962306a36Sopenharmony_ci struct bonding *bond = slave->bond; 210062306a36Sopenharmony_ci struct slave *slave_iter; 210162306a36Sopenharmony_ci struct list_head *iter; 210262306a36Sopenharmony_ci bool dummy_slave_update; /* Ignore this value as caller updates array */ 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci /* Sync against bond_3ad_state_machine_handler() */ 210562306a36Sopenharmony_ci spin_lock_bh(&bond->mode_lock); 210662306a36Sopenharmony_ci aggregator = &(SLAVE_AD_INFO(slave)->aggregator); 210762306a36Sopenharmony_ci port = &(SLAVE_AD_INFO(slave)->port); 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci /* if slave is null, the whole port is not initialized */ 211062306a36Sopenharmony_ci if (!port->slave) { 211162306a36Sopenharmony_ci slave_warn(bond->dev, slave->dev, "Trying to unbind an uninitialized port\n"); 211262306a36Sopenharmony_ci goto out; 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci slave_dbg(bond->dev, slave->dev, "Unbinding Link Aggregation Group %d\n", 211662306a36Sopenharmony_ci aggregator->aggregator_identifier); 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci /* Tell the partner that this port is not suitable for aggregation */ 211962306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; 212062306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_COLLECTING; 212162306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING; 212262306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_AGGREGATION; 212362306a36Sopenharmony_ci __update_lacpdu_from_port(port); 212462306a36Sopenharmony_ci ad_lacpdu_send(port); 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci /* check if this aggregator is occupied */ 212762306a36Sopenharmony_ci if (aggregator->lag_ports) { 212862306a36Sopenharmony_ci /* check if there are other ports related to this aggregator 212962306a36Sopenharmony_ci * except the port related to this slave(thats ensure us that 213062306a36Sopenharmony_ci * there is a reason to search for new aggregator, and that we 213162306a36Sopenharmony_ci * will find one 213262306a36Sopenharmony_ci */ 213362306a36Sopenharmony_ci if ((aggregator->lag_ports != port) || 213462306a36Sopenharmony_ci (aggregator->lag_ports->next_port_in_aggregator)) { 213562306a36Sopenharmony_ci /* find new aggregator for the related port(s) */ 213662306a36Sopenharmony_ci bond_for_each_slave(bond, slave_iter, iter) { 213762306a36Sopenharmony_ci new_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator); 213862306a36Sopenharmony_ci /* if the new aggregator is empty, or it is 213962306a36Sopenharmony_ci * connected to our port only 214062306a36Sopenharmony_ci */ 214162306a36Sopenharmony_ci if (!new_aggregator->lag_ports || 214262306a36Sopenharmony_ci ((new_aggregator->lag_ports == port) && 214362306a36Sopenharmony_ci !new_aggregator->lag_ports->next_port_in_aggregator)) 214462306a36Sopenharmony_ci break; 214562306a36Sopenharmony_ci } 214662306a36Sopenharmony_ci if (!slave_iter) 214762306a36Sopenharmony_ci new_aggregator = NULL; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci /* if new aggregator found, copy the aggregator's 215062306a36Sopenharmony_ci * parameters and connect the related lag_ports to the 215162306a36Sopenharmony_ci * new aggregator 215262306a36Sopenharmony_ci */ 215362306a36Sopenharmony_ci if ((new_aggregator) && ((!new_aggregator->lag_ports) || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator))) { 215462306a36Sopenharmony_ci slave_dbg(bond->dev, slave->dev, "Some port(s) related to LAG %d - replacing with LAG %d\n", 215562306a36Sopenharmony_ci aggregator->aggregator_identifier, 215662306a36Sopenharmony_ci new_aggregator->aggregator_identifier); 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci if ((new_aggregator->lag_ports == port) && 215962306a36Sopenharmony_ci new_aggregator->is_active) { 216062306a36Sopenharmony_ci slave_info(bond->dev, slave->dev, "Removing an active aggregator\n"); 216162306a36Sopenharmony_ci select_new_active_agg = 1; 216262306a36Sopenharmony_ci } 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci new_aggregator->is_individual = aggregator->is_individual; 216562306a36Sopenharmony_ci new_aggregator->actor_admin_aggregator_key = aggregator->actor_admin_aggregator_key; 216662306a36Sopenharmony_ci new_aggregator->actor_oper_aggregator_key = aggregator->actor_oper_aggregator_key; 216762306a36Sopenharmony_ci new_aggregator->partner_system = aggregator->partner_system; 216862306a36Sopenharmony_ci new_aggregator->partner_system_priority = aggregator->partner_system_priority; 216962306a36Sopenharmony_ci new_aggregator->partner_oper_aggregator_key = aggregator->partner_oper_aggregator_key; 217062306a36Sopenharmony_ci new_aggregator->receive_state = aggregator->receive_state; 217162306a36Sopenharmony_ci new_aggregator->transmit_state = aggregator->transmit_state; 217262306a36Sopenharmony_ci new_aggregator->lag_ports = aggregator->lag_ports; 217362306a36Sopenharmony_ci new_aggregator->is_active = aggregator->is_active; 217462306a36Sopenharmony_ci new_aggregator->num_of_ports = aggregator->num_of_ports; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci /* update the information that is written on 217762306a36Sopenharmony_ci * the ports about the aggregator 217862306a36Sopenharmony_ci */ 217962306a36Sopenharmony_ci for (temp_port = aggregator->lag_ports; temp_port; 218062306a36Sopenharmony_ci temp_port = temp_port->next_port_in_aggregator) { 218162306a36Sopenharmony_ci temp_port->aggregator = new_aggregator; 218262306a36Sopenharmony_ci temp_port->actor_port_aggregator_identifier = new_aggregator->aggregator_identifier; 218362306a36Sopenharmony_ci } 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci ad_clear_agg(aggregator); 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci if (select_new_active_agg) 218862306a36Sopenharmony_ci ad_agg_selection_logic(__get_first_agg(port), 218962306a36Sopenharmony_ci &dummy_slave_update); 219062306a36Sopenharmony_ci } else { 219162306a36Sopenharmony_ci slave_warn(bond->dev, slave->dev, "unbinding aggregator, and could not find a new aggregator for its ports\n"); 219262306a36Sopenharmony_ci } 219362306a36Sopenharmony_ci } else { 219462306a36Sopenharmony_ci /* in case that the only port related to this 219562306a36Sopenharmony_ci * aggregator is the one we want to remove 219662306a36Sopenharmony_ci */ 219762306a36Sopenharmony_ci select_new_active_agg = aggregator->is_active; 219862306a36Sopenharmony_ci ad_clear_agg(aggregator); 219962306a36Sopenharmony_ci if (select_new_active_agg) { 220062306a36Sopenharmony_ci slave_info(bond->dev, slave->dev, "Removing an active aggregator\n"); 220162306a36Sopenharmony_ci /* select new active aggregator */ 220262306a36Sopenharmony_ci temp_aggregator = __get_first_agg(port); 220362306a36Sopenharmony_ci if (temp_aggregator) 220462306a36Sopenharmony_ci ad_agg_selection_logic(temp_aggregator, 220562306a36Sopenharmony_ci &dummy_slave_update); 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci } 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci slave_dbg(bond->dev, slave->dev, "Unbinding port %d\n", port->actor_port_number); 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci /* find the aggregator that this port is connected to */ 221362306a36Sopenharmony_ci bond_for_each_slave(bond, slave_iter, iter) { 221462306a36Sopenharmony_ci temp_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator); 221562306a36Sopenharmony_ci prev_port = NULL; 221662306a36Sopenharmony_ci /* search the port in the aggregator's related ports */ 221762306a36Sopenharmony_ci for (temp_port = temp_aggregator->lag_ports; temp_port; 221862306a36Sopenharmony_ci prev_port = temp_port, 221962306a36Sopenharmony_ci temp_port = temp_port->next_port_in_aggregator) { 222062306a36Sopenharmony_ci if (temp_port == port) { 222162306a36Sopenharmony_ci /* the aggregator found - detach the port from 222262306a36Sopenharmony_ci * this aggregator 222362306a36Sopenharmony_ci */ 222462306a36Sopenharmony_ci if (prev_port) 222562306a36Sopenharmony_ci prev_port->next_port_in_aggregator = temp_port->next_port_in_aggregator; 222662306a36Sopenharmony_ci else 222762306a36Sopenharmony_ci temp_aggregator->lag_ports = temp_port->next_port_in_aggregator; 222862306a36Sopenharmony_ci temp_aggregator->num_of_ports--; 222962306a36Sopenharmony_ci if (__agg_active_ports(temp_aggregator) == 0) { 223062306a36Sopenharmony_ci select_new_active_agg = temp_aggregator->is_active; 223162306a36Sopenharmony_ci if (temp_aggregator->num_of_ports == 0) 223262306a36Sopenharmony_ci ad_clear_agg(temp_aggregator); 223362306a36Sopenharmony_ci if (select_new_active_agg) { 223462306a36Sopenharmony_ci slave_info(bond->dev, slave->dev, "Removing an active aggregator\n"); 223562306a36Sopenharmony_ci /* select new active aggregator */ 223662306a36Sopenharmony_ci ad_agg_selection_logic(__get_first_agg(port), 223762306a36Sopenharmony_ci &dummy_slave_update); 223862306a36Sopenharmony_ci } 223962306a36Sopenharmony_ci } 224062306a36Sopenharmony_ci break; 224162306a36Sopenharmony_ci } 224262306a36Sopenharmony_ci } 224362306a36Sopenharmony_ci } 224462306a36Sopenharmony_ci port->slave = NULL; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ciout: 224762306a36Sopenharmony_ci spin_unlock_bh(&bond->mode_lock); 224862306a36Sopenharmony_ci} 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci/** 225162306a36Sopenharmony_ci * bond_3ad_update_ad_actor_settings - reflect change of actor settings to ports 225262306a36Sopenharmony_ci * @bond: bonding struct to work on 225362306a36Sopenharmony_ci * 225462306a36Sopenharmony_ci * If an ad_actor setting gets changed we need to update the individual port 225562306a36Sopenharmony_ci * settings so the bond device will use the new values when it gets upped. 225662306a36Sopenharmony_ci */ 225762306a36Sopenharmony_civoid bond_3ad_update_ad_actor_settings(struct bonding *bond) 225862306a36Sopenharmony_ci{ 225962306a36Sopenharmony_ci struct list_head *iter; 226062306a36Sopenharmony_ci struct slave *slave; 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci ASSERT_RTNL(); 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci BOND_AD_INFO(bond).system.sys_priority = bond->params.ad_actor_sys_prio; 226562306a36Sopenharmony_ci if (is_zero_ether_addr(bond->params.ad_actor_system)) 226662306a36Sopenharmony_ci BOND_AD_INFO(bond).system.sys_mac_addr = 226762306a36Sopenharmony_ci *((struct mac_addr *)bond->dev->dev_addr); 226862306a36Sopenharmony_ci else 226962306a36Sopenharmony_ci BOND_AD_INFO(bond).system.sys_mac_addr = 227062306a36Sopenharmony_ci *((struct mac_addr *)bond->params.ad_actor_system); 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci spin_lock_bh(&bond->mode_lock); 227362306a36Sopenharmony_ci bond_for_each_slave(bond, slave, iter) { 227462306a36Sopenharmony_ci struct port *port = &(SLAVE_AD_INFO(slave))->port; 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci __ad_actor_update_port(port); 227762306a36Sopenharmony_ci port->ntt = true; 227862306a36Sopenharmony_ci } 227962306a36Sopenharmony_ci spin_unlock_bh(&bond->mode_lock); 228062306a36Sopenharmony_ci} 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci/** 228362306a36Sopenharmony_ci * bond_agg_timer_advance - advance agg_select_timer 228462306a36Sopenharmony_ci * @bond: bonding structure 228562306a36Sopenharmony_ci * 228662306a36Sopenharmony_ci * Return true when agg_select_timer reaches 0. 228762306a36Sopenharmony_ci */ 228862306a36Sopenharmony_cistatic bool bond_agg_timer_advance(struct bonding *bond) 228962306a36Sopenharmony_ci{ 229062306a36Sopenharmony_ci int val, nval; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci while (1) { 229362306a36Sopenharmony_ci val = atomic_read(&BOND_AD_INFO(bond).agg_select_timer); 229462306a36Sopenharmony_ci if (!val) 229562306a36Sopenharmony_ci return false; 229662306a36Sopenharmony_ci nval = val - 1; 229762306a36Sopenharmony_ci if (atomic_cmpxchg(&BOND_AD_INFO(bond).agg_select_timer, 229862306a36Sopenharmony_ci val, nval) == val) 229962306a36Sopenharmony_ci break; 230062306a36Sopenharmony_ci } 230162306a36Sopenharmony_ci return nval == 0; 230262306a36Sopenharmony_ci} 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci/** 230562306a36Sopenharmony_ci * bond_3ad_state_machine_handler - handle state machines timeout 230662306a36Sopenharmony_ci * @work: work context to fetch bonding struct to work on from 230762306a36Sopenharmony_ci * 230862306a36Sopenharmony_ci * The state machine handling concept in this module is to check every tick 230962306a36Sopenharmony_ci * which state machine should operate any function. The execution order is 231062306a36Sopenharmony_ci * round robin, so when we have an interaction between state machines, the 231162306a36Sopenharmony_ci * reply of one to each other might be delayed until next tick. 231262306a36Sopenharmony_ci * 231362306a36Sopenharmony_ci * This function also complete the initialization when the agg_select_timer 231462306a36Sopenharmony_ci * times out, and it selects an aggregator for the ports that are yet not 231562306a36Sopenharmony_ci * related to any aggregator, and selects the active aggregator for a bond. 231662306a36Sopenharmony_ci */ 231762306a36Sopenharmony_civoid bond_3ad_state_machine_handler(struct work_struct *work) 231862306a36Sopenharmony_ci{ 231962306a36Sopenharmony_ci struct bonding *bond = container_of(work, struct bonding, 232062306a36Sopenharmony_ci ad_work.work); 232162306a36Sopenharmony_ci struct aggregator *aggregator; 232262306a36Sopenharmony_ci struct list_head *iter; 232362306a36Sopenharmony_ci struct slave *slave; 232462306a36Sopenharmony_ci struct port *port; 232562306a36Sopenharmony_ci bool should_notify_rtnl = BOND_SLAVE_NOTIFY_LATER; 232662306a36Sopenharmony_ci bool update_slave_arr = false; 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci /* Lock to protect data accessed by all (e.g., port->sm_vars) and 232962306a36Sopenharmony_ci * against running with bond_3ad_unbind_slave. ad_rx_machine may run 233062306a36Sopenharmony_ci * concurrently due to incoming LACPDU as well. 233162306a36Sopenharmony_ci */ 233262306a36Sopenharmony_ci spin_lock_bh(&bond->mode_lock); 233362306a36Sopenharmony_ci rcu_read_lock(); 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci /* check if there are any slaves */ 233662306a36Sopenharmony_ci if (!bond_has_slaves(bond)) 233762306a36Sopenharmony_ci goto re_arm; 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci if (bond_agg_timer_advance(bond)) { 234062306a36Sopenharmony_ci slave = bond_first_slave_rcu(bond); 234162306a36Sopenharmony_ci port = slave ? &(SLAVE_AD_INFO(slave)->port) : NULL; 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci /* select the active aggregator for the bond */ 234462306a36Sopenharmony_ci if (port) { 234562306a36Sopenharmony_ci if (!port->slave) { 234662306a36Sopenharmony_ci net_warn_ratelimited("%s: Warning: bond's first port is uninitialized\n", 234762306a36Sopenharmony_ci bond->dev->name); 234862306a36Sopenharmony_ci goto re_arm; 234962306a36Sopenharmony_ci } 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci aggregator = __get_first_agg(port); 235262306a36Sopenharmony_ci ad_agg_selection_logic(aggregator, &update_slave_arr); 235362306a36Sopenharmony_ci } 235462306a36Sopenharmony_ci bond_3ad_set_carrier(bond); 235562306a36Sopenharmony_ci } 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci /* for each port run the state machines */ 235862306a36Sopenharmony_ci bond_for_each_slave_rcu(bond, slave, iter) { 235962306a36Sopenharmony_ci port = &(SLAVE_AD_INFO(slave)->port); 236062306a36Sopenharmony_ci if (!port->slave) { 236162306a36Sopenharmony_ci net_warn_ratelimited("%s: Warning: Found an uninitialized port\n", 236262306a36Sopenharmony_ci bond->dev->name); 236362306a36Sopenharmony_ci goto re_arm; 236462306a36Sopenharmony_ci } 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci ad_rx_machine(NULL, port); 236762306a36Sopenharmony_ci ad_periodic_machine(port, &bond->params); 236862306a36Sopenharmony_ci ad_port_selection_logic(port, &update_slave_arr); 236962306a36Sopenharmony_ci ad_mux_machine(port, &update_slave_arr); 237062306a36Sopenharmony_ci ad_tx_machine(port); 237162306a36Sopenharmony_ci ad_churn_machine(port); 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci /* turn off the BEGIN bit, since we already handled it */ 237462306a36Sopenharmony_ci if (port->sm_vars & AD_PORT_BEGIN) 237562306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_BEGIN; 237662306a36Sopenharmony_ci } 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_cire_arm: 237962306a36Sopenharmony_ci bond_for_each_slave_rcu(bond, slave, iter) { 238062306a36Sopenharmony_ci if (slave->should_notify) { 238162306a36Sopenharmony_ci should_notify_rtnl = BOND_SLAVE_NOTIFY_NOW; 238262306a36Sopenharmony_ci break; 238362306a36Sopenharmony_ci } 238462306a36Sopenharmony_ci } 238562306a36Sopenharmony_ci rcu_read_unlock(); 238662306a36Sopenharmony_ci spin_unlock_bh(&bond->mode_lock); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci if (update_slave_arr) 238962306a36Sopenharmony_ci bond_slave_arr_work_rearm(bond, 0); 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci if (should_notify_rtnl && rtnl_trylock()) { 239262306a36Sopenharmony_ci bond_slave_state_notify(bond); 239362306a36Sopenharmony_ci rtnl_unlock(); 239462306a36Sopenharmony_ci } 239562306a36Sopenharmony_ci queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks); 239662306a36Sopenharmony_ci} 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci/** 239962306a36Sopenharmony_ci * bond_3ad_rx_indication - handle a received frame 240062306a36Sopenharmony_ci * @lacpdu: received lacpdu 240162306a36Sopenharmony_ci * @slave: slave struct to work on 240262306a36Sopenharmony_ci * 240362306a36Sopenharmony_ci * It is assumed that frames that were sent on this NIC don't returned as new 240462306a36Sopenharmony_ci * received frames (loopback). Since only the payload is given to this 240562306a36Sopenharmony_ci * function, it check for loopback. 240662306a36Sopenharmony_ci */ 240762306a36Sopenharmony_cistatic int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave) 240862306a36Sopenharmony_ci{ 240962306a36Sopenharmony_ci struct bonding *bond = slave->bond; 241062306a36Sopenharmony_ci int ret = RX_HANDLER_ANOTHER; 241162306a36Sopenharmony_ci struct bond_marker *marker; 241262306a36Sopenharmony_ci struct port *port; 241362306a36Sopenharmony_ci atomic64_t *stat; 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci port = &(SLAVE_AD_INFO(slave)->port); 241662306a36Sopenharmony_ci if (!port->slave) { 241762306a36Sopenharmony_ci net_warn_ratelimited("%s: Warning: port of slave %s is uninitialized\n", 241862306a36Sopenharmony_ci slave->dev->name, slave->bond->dev->name); 241962306a36Sopenharmony_ci return ret; 242062306a36Sopenharmony_ci } 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci switch (lacpdu->subtype) { 242362306a36Sopenharmony_ci case AD_TYPE_LACPDU: 242462306a36Sopenharmony_ci ret = RX_HANDLER_CONSUMED; 242562306a36Sopenharmony_ci slave_dbg(slave->bond->dev, slave->dev, 242662306a36Sopenharmony_ci "Received LACPDU on port %d\n", 242762306a36Sopenharmony_ci port->actor_port_number); 242862306a36Sopenharmony_ci /* Protect against concurrent state machines */ 242962306a36Sopenharmony_ci spin_lock(&slave->bond->mode_lock); 243062306a36Sopenharmony_ci ad_rx_machine(lacpdu, port); 243162306a36Sopenharmony_ci spin_unlock(&slave->bond->mode_lock); 243262306a36Sopenharmony_ci break; 243362306a36Sopenharmony_ci case AD_TYPE_MARKER: 243462306a36Sopenharmony_ci ret = RX_HANDLER_CONSUMED; 243562306a36Sopenharmony_ci /* No need to convert fields to Little Endian since we 243662306a36Sopenharmony_ci * don't use the marker's fields. 243762306a36Sopenharmony_ci */ 243862306a36Sopenharmony_ci marker = (struct bond_marker *)lacpdu; 243962306a36Sopenharmony_ci switch (marker->tlv_type) { 244062306a36Sopenharmony_ci case AD_MARKER_INFORMATION_SUBTYPE: 244162306a36Sopenharmony_ci slave_dbg(slave->bond->dev, slave->dev, "Received Marker Information on port %d\n", 244262306a36Sopenharmony_ci port->actor_port_number); 244362306a36Sopenharmony_ci ad_marker_info_received(marker, port); 244462306a36Sopenharmony_ci break; 244562306a36Sopenharmony_ci case AD_MARKER_RESPONSE_SUBTYPE: 244662306a36Sopenharmony_ci slave_dbg(slave->bond->dev, slave->dev, "Received Marker Response on port %d\n", 244762306a36Sopenharmony_ci port->actor_port_number); 244862306a36Sopenharmony_ci ad_marker_response_received(marker, port); 244962306a36Sopenharmony_ci break; 245062306a36Sopenharmony_ci default: 245162306a36Sopenharmony_ci slave_dbg(slave->bond->dev, slave->dev, "Received an unknown Marker subtype on port %d\n", 245262306a36Sopenharmony_ci port->actor_port_number); 245362306a36Sopenharmony_ci stat = &SLAVE_AD_INFO(slave)->stats.marker_unknown_rx; 245462306a36Sopenharmony_ci atomic64_inc(stat); 245562306a36Sopenharmony_ci stat = &BOND_AD_INFO(bond).stats.marker_unknown_rx; 245662306a36Sopenharmony_ci atomic64_inc(stat); 245762306a36Sopenharmony_ci } 245862306a36Sopenharmony_ci break; 245962306a36Sopenharmony_ci default: 246062306a36Sopenharmony_ci atomic64_inc(&SLAVE_AD_INFO(slave)->stats.lacpdu_unknown_rx); 246162306a36Sopenharmony_ci atomic64_inc(&BOND_AD_INFO(bond).stats.lacpdu_unknown_rx); 246262306a36Sopenharmony_ci } 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci return ret; 246562306a36Sopenharmony_ci} 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci/** 246862306a36Sopenharmony_ci * ad_update_actor_keys - Update the oper / admin keys for a port based on 246962306a36Sopenharmony_ci * its current speed and duplex settings. 247062306a36Sopenharmony_ci * 247162306a36Sopenharmony_ci * @port: the port we'are looking at 247262306a36Sopenharmony_ci * @reset: Boolean to just reset the speed and the duplex part of the key 247362306a36Sopenharmony_ci * 247462306a36Sopenharmony_ci * The logic to change the oper / admin keys is: 247562306a36Sopenharmony_ci * (a) A full duplex port can participate in LACP with partner. 247662306a36Sopenharmony_ci * (b) When the speed is changed, LACP need to be reinitiated. 247762306a36Sopenharmony_ci */ 247862306a36Sopenharmony_cistatic void ad_update_actor_keys(struct port *port, bool reset) 247962306a36Sopenharmony_ci{ 248062306a36Sopenharmony_ci u8 duplex = 0; 248162306a36Sopenharmony_ci u16 ospeed = 0, speed = 0; 248262306a36Sopenharmony_ci u16 old_oper_key = port->actor_oper_port_key; 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci port->actor_admin_port_key &= ~(AD_SPEED_KEY_MASKS|AD_DUPLEX_KEY_MASKS); 248562306a36Sopenharmony_ci if (!reset) { 248662306a36Sopenharmony_ci speed = __get_link_speed(port); 248762306a36Sopenharmony_ci ospeed = (old_oper_key & AD_SPEED_KEY_MASKS) >> 1; 248862306a36Sopenharmony_ci duplex = __get_duplex(port); 248962306a36Sopenharmony_ci port->actor_admin_port_key |= (speed << 1) | duplex; 249062306a36Sopenharmony_ci } 249162306a36Sopenharmony_ci port->actor_oper_port_key = port->actor_admin_port_key; 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci if (old_oper_key != port->actor_oper_port_key) { 249462306a36Sopenharmony_ci /* Only 'duplex' port participates in LACP */ 249562306a36Sopenharmony_ci if (duplex) 249662306a36Sopenharmony_ci port->sm_vars |= AD_PORT_LACP_ENABLED; 249762306a36Sopenharmony_ci else 249862306a36Sopenharmony_ci port->sm_vars &= ~AD_PORT_LACP_ENABLED; 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_ci if (!reset) { 250162306a36Sopenharmony_ci if (!speed) { 250262306a36Sopenharmony_ci slave_err(port->slave->bond->dev, 250362306a36Sopenharmony_ci port->slave->dev, 250462306a36Sopenharmony_ci "speed changed to 0 on port %d\n", 250562306a36Sopenharmony_ci port->actor_port_number); 250662306a36Sopenharmony_ci } else if (duplex && ospeed != speed) { 250762306a36Sopenharmony_ci /* Speed change restarts LACP state-machine */ 250862306a36Sopenharmony_ci port->sm_vars |= AD_PORT_BEGIN; 250962306a36Sopenharmony_ci } 251062306a36Sopenharmony_ci } 251162306a36Sopenharmony_ci } 251262306a36Sopenharmony_ci} 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci/** 251562306a36Sopenharmony_ci * bond_3ad_adapter_speed_duplex_changed - handle a slave's speed / duplex 251662306a36Sopenharmony_ci * change indication 251762306a36Sopenharmony_ci * 251862306a36Sopenharmony_ci * @slave: slave struct to work on 251962306a36Sopenharmony_ci * 252062306a36Sopenharmony_ci * Handle reselection of aggregator (if needed) for this port. 252162306a36Sopenharmony_ci */ 252262306a36Sopenharmony_civoid bond_3ad_adapter_speed_duplex_changed(struct slave *slave) 252362306a36Sopenharmony_ci{ 252462306a36Sopenharmony_ci struct port *port; 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci port = &(SLAVE_AD_INFO(slave)->port); 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci /* if slave is null, the whole port is not initialized */ 252962306a36Sopenharmony_ci if (!port->slave) { 253062306a36Sopenharmony_ci slave_warn(slave->bond->dev, slave->dev, 253162306a36Sopenharmony_ci "speed/duplex changed for uninitialized port\n"); 253262306a36Sopenharmony_ci return; 253362306a36Sopenharmony_ci } 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci spin_lock_bh(&slave->bond->mode_lock); 253662306a36Sopenharmony_ci ad_update_actor_keys(port, false); 253762306a36Sopenharmony_ci spin_unlock_bh(&slave->bond->mode_lock); 253862306a36Sopenharmony_ci slave_dbg(slave->bond->dev, slave->dev, "Port %d changed speed/duplex\n", 253962306a36Sopenharmony_ci port->actor_port_number); 254062306a36Sopenharmony_ci} 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci/** 254362306a36Sopenharmony_ci * bond_3ad_handle_link_change - handle a slave's link status change indication 254462306a36Sopenharmony_ci * @slave: slave struct to work on 254562306a36Sopenharmony_ci * @link: whether the link is now up or down 254662306a36Sopenharmony_ci * 254762306a36Sopenharmony_ci * Handle reselection of aggregator (if needed) for this port. 254862306a36Sopenharmony_ci */ 254962306a36Sopenharmony_civoid bond_3ad_handle_link_change(struct slave *slave, char link) 255062306a36Sopenharmony_ci{ 255162306a36Sopenharmony_ci struct aggregator *agg; 255262306a36Sopenharmony_ci struct port *port; 255362306a36Sopenharmony_ci bool dummy; 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_ci port = &(SLAVE_AD_INFO(slave)->port); 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci /* if slave is null, the whole port is not initialized */ 255862306a36Sopenharmony_ci if (!port->slave) { 255962306a36Sopenharmony_ci slave_warn(slave->bond->dev, slave->dev, "link status changed for uninitialized port\n"); 256062306a36Sopenharmony_ci return; 256162306a36Sopenharmony_ci } 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci spin_lock_bh(&slave->bond->mode_lock); 256462306a36Sopenharmony_ci /* on link down we are zeroing duplex and speed since 256562306a36Sopenharmony_ci * some of the adaptors(ce1000.lan) report full duplex/speed 256662306a36Sopenharmony_ci * instead of N/A(duplex) / 0(speed). 256762306a36Sopenharmony_ci * 256862306a36Sopenharmony_ci * on link up we are forcing recheck on the duplex and speed since 256962306a36Sopenharmony_ci * some of he adaptors(ce1000.lan) report. 257062306a36Sopenharmony_ci */ 257162306a36Sopenharmony_ci if (link == BOND_LINK_UP) { 257262306a36Sopenharmony_ci port->is_enabled = true; 257362306a36Sopenharmony_ci ad_update_actor_keys(port, false); 257462306a36Sopenharmony_ci } else { 257562306a36Sopenharmony_ci /* link has failed */ 257662306a36Sopenharmony_ci port->is_enabled = false; 257762306a36Sopenharmony_ci ad_update_actor_keys(port, true); 257862306a36Sopenharmony_ci } 257962306a36Sopenharmony_ci agg = __get_first_agg(port); 258062306a36Sopenharmony_ci ad_agg_selection_logic(agg, &dummy); 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci spin_unlock_bh(&slave->bond->mode_lock); 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci slave_dbg(slave->bond->dev, slave->dev, "Port %d changed link status to %s\n", 258562306a36Sopenharmony_ci port->actor_port_number, 258662306a36Sopenharmony_ci link == BOND_LINK_UP ? "UP" : "DOWN"); 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci /* RTNL is held and mode_lock is released so it's safe 258962306a36Sopenharmony_ci * to update slave_array here. 259062306a36Sopenharmony_ci */ 259162306a36Sopenharmony_ci bond_update_slave_arr(slave->bond, NULL); 259262306a36Sopenharmony_ci} 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ci/** 259562306a36Sopenharmony_ci * bond_3ad_set_carrier - set link state for bonding master 259662306a36Sopenharmony_ci * @bond: bonding structure 259762306a36Sopenharmony_ci * 259862306a36Sopenharmony_ci * if we have an active aggregator, we're up, if not, we're down. 259962306a36Sopenharmony_ci * Presumes that we cannot have an active aggregator if there are 260062306a36Sopenharmony_ci * no slaves with link up. 260162306a36Sopenharmony_ci * 260262306a36Sopenharmony_ci * This behavior complies with IEEE 802.3 section 43.3.9. 260362306a36Sopenharmony_ci * 260462306a36Sopenharmony_ci * Called by bond_set_carrier(). Return zero if carrier state does not 260562306a36Sopenharmony_ci * change, nonzero if it does. 260662306a36Sopenharmony_ci */ 260762306a36Sopenharmony_ciint bond_3ad_set_carrier(struct bonding *bond) 260862306a36Sopenharmony_ci{ 260962306a36Sopenharmony_ci struct aggregator *active; 261062306a36Sopenharmony_ci struct slave *first_slave; 261162306a36Sopenharmony_ci int ret = 1; 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci rcu_read_lock(); 261462306a36Sopenharmony_ci first_slave = bond_first_slave_rcu(bond); 261562306a36Sopenharmony_ci if (!first_slave) { 261662306a36Sopenharmony_ci ret = 0; 261762306a36Sopenharmony_ci goto out; 261862306a36Sopenharmony_ci } 261962306a36Sopenharmony_ci active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator)); 262062306a36Sopenharmony_ci if (active) { 262162306a36Sopenharmony_ci /* are enough slaves available to consider link up? */ 262262306a36Sopenharmony_ci if (__agg_active_ports(active) < bond->params.min_links) { 262362306a36Sopenharmony_ci if (netif_carrier_ok(bond->dev)) { 262462306a36Sopenharmony_ci netif_carrier_off(bond->dev); 262562306a36Sopenharmony_ci goto out; 262662306a36Sopenharmony_ci } 262762306a36Sopenharmony_ci } else if (!netif_carrier_ok(bond->dev)) { 262862306a36Sopenharmony_ci netif_carrier_on(bond->dev); 262962306a36Sopenharmony_ci goto out; 263062306a36Sopenharmony_ci } 263162306a36Sopenharmony_ci } else if (netif_carrier_ok(bond->dev)) { 263262306a36Sopenharmony_ci netif_carrier_off(bond->dev); 263362306a36Sopenharmony_ci } 263462306a36Sopenharmony_ciout: 263562306a36Sopenharmony_ci rcu_read_unlock(); 263662306a36Sopenharmony_ci return ret; 263762306a36Sopenharmony_ci} 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci/** 264062306a36Sopenharmony_ci * __bond_3ad_get_active_agg_info - get information of the active aggregator 264162306a36Sopenharmony_ci * @bond: bonding struct to work on 264262306a36Sopenharmony_ci * @ad_info: ad_info struct to fill with the bond's info 264362306a36Sopenharmony_ci * 264462306a36Sopenharmony_ci * Returns: 0 on success 264562306a36Sopenharmony_ci * < 0 on error 264662306a36Sopenharmony_ci */ 264762306a36Sopenharmony_ciint __bond_3ad_get_active_agg_info(struct bonding *bond, 264862306a36Sopenharmony_ci struct ad_info *ad_info) 264962306a36Sopenharmony_ci{ 265062306a36Sopenharmony_ci struct aggregator *aggregator = NULL; 265162306a36Sopenharmony_ci struct list_head *iter; 265262306a36Sopenharmony_ci struct slave *slave; 265362306a36Sopenharmony_ci struct port *port; 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci bond_for_each_slave_rcu(bond, slave, iter) { 265662306a36Sopenharmony_ci port = &(SLAVE_AD_INFO(slave)->port); 265762306a36Sopenharmony_ci if (port->aggregator && port->aggregator->is_active) { 265862306a36Sopenharmony_ci aggregator = port->aggregator; 265962306a36Sopenharmony_ci break; 266062306a36Sopenharmony_ci } 266162306a36Sopenharmony_ci } 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci if (!aggregator) 266462306a36Sopenharmony_ci return -1; 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci ad_info->aggregator_id = aggregator->aggregator_identifier; 266762306a36Sopenharmony_ci ad_info->ports = __agg_active_ports(aggregator); 266862306a36Sopenharmony_ci ad_info->actor_key = aggregator->actor_oper_aggregator_key; 266962306a36Sopenharmony_ci ad_info->partner_key = aggregator->partner_oper_aggregator_key; 267062306a36Sopenharmony_ci ether_addr_copy(ad_info->partner_system, 267162306a36Sopenharmony_ci aggregator->partner_system.mac_addr_value); 267262306a36Sopenharmony_ci return 0; 267362306a36Sopenharmony_ci} 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ciint bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info) 267662306a36Sopenharmony_ci{ 267762306a36Sopenharmony_ci int ret; 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci rcu_read_lock(); 268062306a36Sopenharmony_ci ret = __bond_3ad_get_active_agg_info(bond, ad_info); 268162306a36Sopenharmony_ci rcu_read_unlock(); 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_ci return ret; 268462306a36Sopenharmony_ci} 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ciint bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, 268762306a36Sopenharmony_ci struct slave *slave) 268862306a36Sopenharmony_ci{ 268962306a36Sopenharmony_ci struct lacpdu *lacpdu, _lacpdu; 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci if (skb->protocol != PKT_TYPE_LACPDU) 269262306a36Sopenharmony_ci return RX_HANDLER_ANOTHER; 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci if (!MAC_ADDRESS_EQUAL(eth_hdr(skb)->h_dest, lacpdu_mcast_addr)) 269562306a36Sopenharmony_ci return RX_HANDLER_ANOTHER; 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci lacpdu = skb_header_pointer(skb, 0, sizeof(_lacpdu), &_lacpdu); 269862306a36Sopenharmony_ci if (!lacpdu) { 269962306a36Sopenharmony_ci atomic64_inc(&SLAVE_AD_INFO(slave)->stats.lacpdu_illegal_rx); 270062306a36Sopenharmony_ci atomic64_inc(&BOND_AD_INFO(bond).stats.lacpdu_illegal_rx); 270162306a36Sopenharmony_ci return RX_HANDLER_ANOTHER; 270262306a36Sopenharmony_ci } 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci return bond_3ad_rx_indication(lacpdu, slave); 270562306a36Sopenharmony_ci} 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci/** 270862306a36Sopenharmony_ci * bond_3ad_update_lacp_rate - change the lacp rate 270962306a36Sopenharmony_ci * @bond: bonding struct 271062306a36Sopenharmony_ci * 271162306a36Sopenharmony_ci * When modify lacp_rate parameter via sysfs, 271262306a36Sopenharmony_ci * update actor_oper_port_state of each port. 271362306a36Sopenharmony_ci * 271462306a36Sopenharmony_ci * Hold bond->mode_lock, 271562306a36Sopenharmony_ci * so we can modify port->actor_oper_port_state, 271662306a36Sopenharmony_ci * no matter bond is up or down. 271762306a36Sopenharmony_ci */ 271862306a36Sopenharmony_civoid bond_3ad_update_lacp_rate(struct bonding *bond) 271962306a36Sopenharmony_ci{ 272062306a36Sopenharmony_ci struct port *port = NULL; 272162306a36Sopenharmony_ci struct list_head *iter; 272262306a36Sopenharmony_ci struct slave *slave; 272362306a36Sopenharmony_ci int lacp_fast; 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_ci lacp_fast = bond->params.lacp_fast; 272662306a36Sopenharmony_ci spin_lock_bh(&bond->mode_lock); 272762306a36Sopenharmony_ci bond_for_each_slave(bond, slave, iter) { 272862306a36Sopenharmony_ci port = &(SLAVE_AD_INFO(slave)->port); 272962306a36Sopenharmony_ci if (lacp_fast) 273062306a36Sopenharmony_ci port->actor_oper_port_state |= LACP_STATE_LACP_TIMEOUT; 273162306a36Sopenharmony_ci else 273262306a36Sopenharmony_ci port->actor_oper_port_state &= ~LACP_STATE_LACP_TIMEOUT; 273362306a36Sopenharmony_ci } 273462306a36Sopenharmony_ci spin_unlock_bh(&bond->mode_lock); 273562306a36Sopenharmony_ci} 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_cisize_t bond_3ad_stats_size(void) 273862306a36Sopenharmony_ci{ 273962306a36Sopenharmony_ci return nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_RX */ 274062306a36Sopenharmony_ci nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_TX */ 274162306a36Sopenharmony_ci nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_UNKNOWN_RX */ 274262306a36Sopenharmony_ci nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_LACPDU_ILLEGAL_RX */ 274362306a36Sopenharmony_ci nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_RX */ 274462306a36Sopenharmony_ci nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_TX */ 274562306a36Sopenharmony_ci nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_RESP_RX */ 274662306a36Sopenharmony_ci nla_total_size_64bit(sizeof(u64)) + /* BOND_3AD_STAT_MARKER_RESP_TX */ 274762306a36Sopenharmony_ci nla_total_size_64bit(sizeof(u64)); /* BOND_3AD_STAT_MARKER_UNKNOWN_RX */ 274862306a36Sopenharmony_ci} 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ciint bond_3ad_stats_fill(struct sk_buff *skb, struct bond_3ad_stats *stats) 275162306a36Sopenharmony_ci{ 275262306a36Sopenharmony_ci u64 val; 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_ci val = atomic64_read(&stats->lacpdu_rx); 275562306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_RX, val, 275662306a36Sopenharmony_ci BOND_3AD_STAT_PAD)) 275762306a36Sopenharmony_ci return -EMSGSIZE; 275862306a36Sopenharmony_ci val = atomic64_read(&stats->lacpdu_tx); 275962306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_TX, val, 276062306a36Sopenharmony_ci BOND_3AD_STAT_PAD)) 276162306a36Sopenharmony_ci return -EMSGSIZE; 276262306a36Sopenharmony_ci val = atomic64_read(&stats->lacpdu_unknown_rx); 276362306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_UNKNOWN_RX, val, 276462306a36Sopenharmony_ci BOND_3AD_STAT_PAD)) 276562306a36Sopenharmony_ci return -EMSGSIZE; 276662306a36Sopenharmony_ci val = atomic64_read(&stats->lacpdu_illegal_rx); 276762306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_ILLEGAL_RX, val, 276862306a36Sopenharmony_ci BOND_3AD_STAT_PAD)) 276962306a36Sopenharmony_ci return -EMSGSIZE; 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ci val = atomic64_read(&stats->marker_rx); 277262306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RX, val, 277362306a36Sopenharmony_ci BOND_3AD_STAT_PAD)) 277462306a36Sopenharmony_ci return -EMSGSIZE; 277562306a36Sopenharmony_ci val = atomic64_read(&stats->marker_tx); 277662306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_TX, val, 277762306a36Sopenharmony_ci BOND_3AD_STAT_PAD)) 277862306a36Sopenharmony_ci return -EMSGSIZE; 277962306a36Sopenharmony_ci val = atomic64_read(&stats->marker_resp_rx); 278062306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RESP_RX, val, 278162306a36Sopenharmony_ci BOND_3AD_STAT_PAD)) 278262306a36Sopenharmony_ci return -EMSGSIZE; 278362306a36Sopenharmony_ci val = atomic64_read(&stats->marker_resp_tx); 278462306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RESP_TX, val, 278562306a36Sopenharmony_ci BOND_3AD_STAT_PAD)) 278662306a36Sopenharmony_ci return -EMSGSIZE; 278762306a36Sopenharmony_ci val = atomic64_read(&stats->marker_unknown_rx); 278862306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_UNKNOWN_RX, val, 278962306a36Sopenharmony_ci BOND_3AD_STAT_PAD)) 279062306a36Sopenharmony_ci return -EMSGSIZE; 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci return 0; 279362306a36Sopenharmony_ci} 2794