/* this if covers only a special case: when the configuration starts *withlinkdown,itsetsthespeedto0. *Thisisdoneinspiteofthefactthatthee100driverreports0 *tobecompatiblewithMVTinthefuture.
*/ if (slave->link != BOND_LINK_UP)
speed = 0; else { switch (slave->speed) { case SPEED_10:
speed = AD_LINK_SPEED_10MBPS; break;
case SPEED_100:
speed = AD_LINK_SPEED_100MBPS; break;
case SPEED_1000:
speed = AD_LINK_SPEED_1000MBPS; break;
case SPEED_2500:
speed = AD_LINK_SPEED_2500MBPS; break;
case SPEED_5000:
speed = AD_LINK_SPEED_5000MBPS; break;
case SPEED_10000:
speed = AD_LINK_SPEED_10000MBPS; break;
case SPEED_14000:
speed = AD_LINK_SPEED_14000MBPS; break;
case SPEED_20000:
speed = AD_LINK_SPEED_20000MBPS; break;
case SPEED_25000:
speed = AD_LINK_SPEED_25000MBPS; break;
case SPEED_40000:
speed = AD_LINK_SPEED_40000MBPS; break;
case SPEED_50000:
speed = AD_LINK_SPEED_50000MBPS; break;
case SPEED_56000:
speed = AD_LINK_SPEED_56000MBPS; break;
case SPEED_100000:
speed = AD_LINK_SPEED_100000MBPS; break;
case SPEED_200000:
speed = AD_LINK_SPEED_200000MBPS; break;
case SPEED_400000:
speed = AD_LINK_SPEED_400000MBPS; break;
case SPEED_800000:
speed = AD_LINK_SPEED_800000MBPS; break;
default: /* unknown speed value from ethtool. shouldn't happen */ if (slave->speed != SPEED_UNKNOWN)
pr_err_once("%s: (slave %s): unknown ethtool speed (%d) for port %d (set it to 0)\n",
slave->bond->dev->name,
slave->dev->name, slave->speed,
port->actor_port_number);
speed = 0; break;
}
}
slave_dbg(slave->bond->dev, slave->dev, "Port %d Received link speed %d update from adapter\n",
port->actor_port_number, speed); return speed;
}
/* handling a special case: when the configuration starts with *linkdown,itsetstheduplexto0.
*/ if (slave->link == BOND_LINK_UP) { switch (slave->duplex) { case DUPLEX_FULL:
retval = 0x1;
slave_dbg(slave->bond->dev, slave->dev, "Port %d Received status full duplex update from adapter\n",
port->actor_port_number); break; case DUPLEX_HALF: default:
retval = 0x0;
slave_dbg(slave->bond->dev, slave->dev, "Port %d Received status NOT full duplex update from adapter\n",
port->actor_port_number); break;
}
} return retval;
}
staticvoid __ad_actor_update_port(struct port *port)
{ conststruct bonding *bond = bond_get_bond_by_slave(port->slave);
/* check if any parameter is different then *updatethestatemachineselectedvariable.
*/ if (admin->port_number != oper->port_number ||
admin->port_priority != oper->port_priority ||
!MAC_ADDRESS_EQUAL(&admin->system, &oper->system) ||
admin->system_priority != oper->system_priority ||
admin->key != oper->key ||
(admin->port_state & LACP_STATE_AGGREGATION)
!= (oper->port_state & LACP_STATE_AGGREGATION)) {
port->sm_vars &= ~AD_PORT_SELECTED;
}
}
}
/** *__update_ntt-updateaport'snttvariablefromareceivedlacpdu *@lacpdu:thelacpduwe'vereceived *@port:theportwe'relookingat * *Updatesthevalueofthenttvariable,usingparametervaluesfromanewly *receivedlacpdu.Theparametervaluesforthepartnercarriedinthe *receivedPDUarecomparedwiththecorrespondingoperationalparameter *valuesfortheActor.Ifoneormoreofthecomparisonsshowsthatthe *value(s)receivedinthePDUdifferfromthecurrentoperationalvalues, *thennttissettoTRUE.Otherwise,nttremainsunchanged.
*/ staticvoid __update_ntt(struct lacpdu *lacpdu, struct port *port)
{ /* validate lacpdu and port */ if (lacpdu && port) { /* check if any parameter is different then *updatetheport->ntt.
*/ if ((ntohs(lacpdu->partner_port) != port->actor_port_number) ||
(ntohs(lacpdu->partner_port_priority) != port->actor_port_priority) ||
!MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) ||
(ntohs(lacpdu->partner_system_priority) != port->actor_system_priority) ||
(ntohs(lacpdu->partner_key) != port->actor_oper_port_key) ||
((lacpdu->partner_state & LACP_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & LACP_STATE_LACP_ACTIVITY)) ||
((lacpdu->partner_state & LACP_STATE_LACP_TIMEOUT) != (port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT)) ||
((lacpdu->partner_state & LACP_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) ||
((lacpdu->partner_state & LACP_STATE_AGGREGATION) != (port->actor_oper_port_state & LACP_STATE_AGGREGATION))
) {
port->ntt = true;
}
}
}
/** *__agg_ports_are_ready-checkifallportsinanaggregatorareready *@aggregator:theaggregatorwe'relookingat *
*/ staticint __agg_ports_are_ready(struct aggregator *aggregator)
{ struct port *port; int retval = 1;
if (aggregator) { /* scan all ports in this aggregator to verfy if they are *allready.
*/ for (port = aggregator->lag_ports;
port;
port = port->next_port_in_aggregator) { if (!(port->sm_vars & AD_PORT_READY_N)) {
retval = 0; break;
}
}
}
return retval;
}
/** *__set_agg_ports_ready-setvalueofReadybitinallportsofanaggregator *@aggregator:theaggregatorwe'relookingat *@val:Shouldtheports'readybitbesetonoroff *
*/ staticvoid __set_agg_ports_ready(struct aggregator *aggregator, int val)
{ struct port *port;
for (port = aggregator->lag_ports; port;
port = port->next_port_in_aggregator) { if (val)
port->sm_vars |= AD_PORT_READY; else
port->sm_vars &= ~AD_PORT_READY;
}
}
staticint __agg_active_ports(struct aggregator *agg)
{ struct port *port; int active = 0;
for (port = agg->lag_ports; port;
port = port->next_port_in_aggregator) { if (port->is_enabled)
active++;
}
/* update current actual Actor parameters *lacpdu->subtypeinitialized *lacpdu->version_numberinitialized *lacpdu->tlv_type_actor_infoinitialized *lacpdu->actor_information_lengthinitialized
*/
lacpdu->actor_system_priority = htons(port->actor_system_priority);
lacpdu->actor_system = port->actor_system;
lacpdu->actor_key = htons(port->actor_oper_port_key);
lacpdu->actor_port_priority = htons(port->actor_port_priority);
lacpdu->actor_port = htons(port->actor_port_number);
lacpdu->actor_state = port->actor_oper_port_state;
slave_dbg(port->slave->bond->dev, port->slave->dev, "update lacpdu: actor port state %x\n",
port->actor_oper_port_state);
ether_addr_copy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr); /* Note: source address is set to be the member's PERMANENT address, *becauseweuseittoidentifyloopbacklacpdusinreceive.
*/
ether_addr_copy(lacpdu_header->hdr.h_source, slave->perm_hwaddr);
lacpdu_header->hdr.h_proto = PKT_TYPE_LACPDU;
ether_addr_copy(marker_header->hdr.h_dest, lacpdu_mcast_addr); /* Note: source address is set to be the member's PERMANENT address, *becauseweuseittoidentifyloopbackMARKERsinreceive.
*/
ether_addr_copy(marker_header->hdr.h_source, slave->perm_hwaddr);
marker_header->hdr.h_proto = PKT_TYPE_LACPDU;
marker_header->marker = *marker;
dev_queue_xmit(skb);
return0;
}
staticvoid ad_cond_set_peer_notif(struct port *port)
{ struct bonding *bond = port->slave->bond;
/* keep current State Machine state to compare later if it was *changed
*/
last_state = port->sm_mux_state;
if (port->sm_vars & AD_PORT_BEGIN) {
port->sm_mux_state = AD_MUX_DETACHED;
} else { switch (port->sm_mux_state) { case AD_MUX_DETACHED: if ((port->sm_vars & AD_PORT_SELECTED)
|| (port->sm_vars & AD_PORT_STANDBY)) /* if SELECTED or STANDBY */
port->sm_mux_state = AD_MUX_WAITING; break; case AD_MUX_WAITING: /* if SELECTED == FALSE return to DETACH state */ if (!(port->sm_vars & AD_PORT_SELECTED)) {
port->sm_vars &= ~AD_PORT_READY_N; /* in order to withhold the Selection Logic to *checkallportsREADY_Nvalueeverycallback *cycletoupdatereadyvariable,wecheck *READY_NandupdateREADYhere
*/
__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
port->sm_mux_state = AD_MUX_DETACHED; break;
}
/* check if the wait_while_timer expired */ if (port->sm_mux_timer_counter
&& !(--port->sm_mux_timer_counter))
port->sm_vars |= AD_PORT_READY_N;
/* in order to withhold the selection logic to check *allportsREADY_Nvalueeverycallbackcycleto *updatereadyvariable,wecheckREADY_Nandupdate *READYhere
*/
__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
/* if the wait_while_timer expired, and the port is *inREADYstate,movetoATTACHEDstate
*/ if ((port->sm_vars & AD_PORT_READY)
&& !port->sm_mux_timer_counter)
port->sm_mux_state = AD_MUX_ATTACHED; break; case AD_MUX_ATTACHED: /* check also if agg_select_timer expired (so the *edableportwilltakeplaceonlyafterthistimer)
*/ if ((port->sm_vars & AD_PORT_SELECTED) &&
(port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) &&
!__check_agg_selection_timer(port)) { if (port->aggregator->is_active) { int state = AD_MUX_COLLECTING_DISTRIBUTING;
if (!bond->params.coupled_control)
state = AD_MUX_COLLECTING;
port->sm_mux_state = state;
}
} elseif (!(port->sm_vars & AD_PORT_SELECTED) ||
(port->sm_vars & AD_PORT_STANDBY)) { /* if UNSELECTED or STANDBY */
port->sm_vars &= ~AD_PORT_READY_N; /* in order to withhold the selection logic to *checkallportsREADY_Nvalueeverycallback *cycletoupdatereadyvariable,wecheck *READY_NandupdateREADYhere
*/
__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
port->sm_mux_state = AD_MUX_DETACHED;
} elseif (port->aggregator->is_active) {
port->actor_oper_port_state |=
LACP_STATE_SYNCHRONIZATION;
} break; case AD_MUX_COLLECTING_DISTRIBUTING: if (!__port_move_to_attached_state(port)) { /* if port state hasn't changed make *surethatacollectingdistributing *portinanactiveaggregatorisenabled
*/ if (port->aggregator->is_active &&
!__port_is_collecting_distributing(port)) {
__enable_port(port);
*update_slave_arr = true;
}
} break; case AD_MUX_COLLECTING: if (!__port_move_to_attached_state(port)) { if ((port->sm_vars & AD_PORT_SELECTED) &&
(port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) &&
(port->partner_oper.port_state & LACP_STATE_COLLECTING)) {
port->sm_mux_state = AD_MUX_DISTRIBUTING;
} else { /* If port state hasn't changed, make sure that a collecting *portisenabledforanactiveaggregator.
*/ struct slave *slave = port->slave;
if (port->aggregator->is_active &&
bond_is_slave_rx_disabled(slave)) {
ad_enable_collecting(port);
*update_slave_arr = true;
}
}
} break; case AD_MUX_DISTRIBUTING: if (!(port->sm_vars & AD_PORT_SELECTED) ||
(port->sm_vars & AD_PORT_STANDBY) ||
!(port->partner_oper.port_state & LACP_STATE_COLLECTING) ||
!(port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) ||
!(port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) {
port->sm_mux_state = AD_MUX_COLLECTING;
} else { /* if port state hasn't changed make *surethatacollectingdistributing *portinanactiveaggregatorisenabled
*/ if (port->aggregator &&
port->aggregator->is_active &&
!__port_is_collecting_distributing(port)) {
__enable_port(port);
*update_slave_arr = true;
}
} break; default: break;
}
}
/* check if the state machine was changed */ if (port->sm_mux_state != last_state) {
slave_dbg(port->slave->bond->dev, port->slave->dev, "Mux Machine: Port=%d, Last State=%d, Curr State=%d\n",
port->actor_port_number,
last_state,
port->sm_mux_state); switch (port->sm_mux_state) { case AD_MUX_DETACHED:
port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION;
ad_disable_collecting_distributing(port,
update_slave_arr);
port->actor_oper_port_state &= ~LACP_STATE_COLLECTING;
port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING;
port->ntt = true; break; case AD_MUX_WAITING:
port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0); break; case AD_MUX_ATTACHED: if (port->aggregator->is_active)
port->actor_oper_port_state |=
LACP_STATE_SYNCHRONIZATION; else
port->actor_oper_port_state &=
~LACP_STATE_SYNCHRONIZATION;
port->actor_oper_port_state &= ~LACP_STATE_COLLECTING;
port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING;
ad_disable_collecting_distributing(port,
update_slave_arr);
port->ntt = true; break; case AD_MUX_COLLECTING_DISTRIBUTING:
port->actor_oper_port_state |= LACP_STATE_COLLECTING;
port->actor_oper_port_state |= LACP_STATE_DISTRIBUTING;
port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION;
ad_enable_collecting_distributing(port,
update_slave_arr);
port->ntt = true; break; case AD_MUX_COLLECTING:
port->actor_oper_port_state |= LACP_STATE_COLLECTING;
port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING;
port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION;
ad_enable_collecting(port);
ad_disable_distributing(port, update_slave_arr);
port->ntt = true; break; case AD_MUX_DISTRIBUTING:
port->actor_oper_port_state |= LACP_STATE_DISTRIBUTING;
port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION;
ad_enable_collecting_distributing(port,
update_slave_arr); break; default: break;
}
}
}
/* keep current State Machine state to compare later if it was *changed
*/
last_state = port->sm_rx_state;
if (lacpdu) {
atomic64_inc(&SLAVE_AD_INFO(port->slave)->stats.lacpdu_rx);
atomic64_inc(&BOND_AD_INFO(port->slave->bond).stats.lacpdu_rx);
} /* check if state machine should change state */
/* first, check if port was reinitialized */ if (port->sm_vars & AD_PORT_BEGIN) {
port->sm_rx_state = AD_RX_INITIALIZE;
port->sm_vars |= AD_PORT_CHURNED; /* check if port is not enabled */
} elseif (!(port->sm_vars & AD_PORT_BEGIN) && !port->is_enabled)
port->sm_rx_state = AD_RX_PORT_DISABLED; /* check if new lacpdu arrived */ elseif (lacpdu && ((port->sm_rx_state == AD_RX_EXPIRED) ||
(port->sm_rx_state == AD_RX_DEFAULTED) ||
(port->sm_rx_state == AD_RX_CURRENT))) { if (port->sm_rx_state != AD_RX_CURRENT)
port->sm_vars |= AD_PORT_CHURNED;
port->sm_rx_timer_counter = 0;
port->sm_rx_state = AD_RX_CURRENT;
} else { /* if timer is on, and if it is expired */ if (port->sm_rx_timer_counter &&
!(--port->sm_rx_timer_counter)) { switch (port->sm_rx_state) { case AD_RX_EXPIRED:
port->sm_rx_state = AD_RX_DEFAULTED; break; case AD_RX_CURRENT:
port->sm_rx_state = AD_RX_EXPIRED; break; default: break;
}
} else { /* if no lacpdu arrived and no timer is on */ switch (port->sm_rx_state) { case AD_RX_PORT_DISABLED: if (port->is_enabled &&
(port->sm_vars & AD_PORT_LACP_ENABLED))
port->sm_rx_state = AD_RX_EXPIRED; elseif (port->is_enabled
&& ((port->sm_vars
& AD_PORT_LACP_ENABLED) == 0))
port->sm_rx_state = AD_RX_LACP_DISABLED; break; default: break;
}
}
}
/* check if the State machine was changed or new lacpdu arrived */ if ((port->sm_rx_state != last_state) || (lacpdu)) {
slave_dbg(port->slave->bond->dev, port->slave->dev, "Rx Machine: Port=%d, Last State=%d, Curr State=%d\n",
port->actor_port_number,
last_state,
port->sm_rx_state); switch (port->sm_rx_state) { case AD_RX_INITIALIZE: if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS))
port->sm_vars &= ~AD_PORT_LACP_ENABLED; else
port->sm_vars |= AD_PORT_LACP_ENABLED;
port->sm_vars &= ~AD_PORT_SELECTED;
__record_default(port);
port->actor_oper_port_state &= ~LACP_STATE_EXPIRED;
port->sm_rx_state = AD_RX_PORT_DISABLED;
fallthrough; case AD_RX_PORT_DISABLED:
port->sm_vars &= ~AD_PORT_MATCHED; break; case AD_RX_LACP_DISABLED:
port->sm_vars &= ~AD_PORT_SELECTED;
__record_default(port);
port->partner_oper.port_state &= ~LACP_STATE_AGGREGATION;
port->sm_vars |= AD_PORT_MATCHED;
port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; break; case AD_RX_EXPIRED: /* Reset of the Synchronization flag (Standard 43.4.12) *Thisresetcausetodisablethisportinthe *COLLECTING_DISTRIBUTINGstateofthemuxmachinein *caseofEXPIREDevenifLINK_DOWNdidn'tarrivefor *theport.
*/
port->sm_vars &= ~AD_PORT_MATCHED; /* Based on IEEE 8021AX-2014, Figure 6-18 - Receive *machinestatediagram,thestatueshouldbe *Partner_Oper_Port_State.Synchronization=FALSE; *Partner_Oper_Port_State.LACP_Timeout=ShortTimeout; *startcurrent_while_timer(ShortTimeout); *Actor_Oper_Port_State.Expired=TRUE;
*/
port->partner_oper.port_state &= ~LACP_STATE_SYNCHRONIZATION;
port->partner_oper.port_state |= LACP_STATE_LACP_TIMEOUT;
port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(AD_SHORT_TIMEOUT));
port->actor_oper_port_state |= LACP_STATE_EXPIRED;
port->sm_vars |= AD_PORT_CHURNED; break; case AD_RX_DEFAULTED:
__update_default_selected(port);
__record_default(port);
port->sm_vars |= AD_PORT_MATCHED;
port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; break; case AD_RX_CURRENT: /* detect loopback situation */ if (MAC_ADDRESS_EQUAL(&(lacpdu->actor_system),
&(port->actor_system))) {
slave_err(port->slave->bond->dev, port->slave->dev, "An illegal loopback occurred on slave\n" "Check the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n"); return;
}
__update_selected(lacpdu, port);
__update_ntt(lacpdu, port);
__record_pdu(lacpdu, port);
port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT));
port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; break; default: break;
}
}
}
/** *ad_tx_machine-handleaport'stxstatemachine *@port:theportwe'relookingat
*/ staticvoid ad_tx_machine(struct port *port)
{ /* check if tx timer expired, to verify that we do not send more than *3packetspersecond
*/ if (!port->sm_tx_timer_counter || !(--port->sm_tx_timer_counter)) { /* check if there is something to send */ if (port->ntt && (port->sm_vars & AD_PORT_LACP_ENABLED)) {
__update_lacpdu_from_port(port);
if (ad_lacpdu_send(port) >= 0) {
slave_dbg(port->slave->bond->dev,
port->slave->dev, "Sent LACPDU on port %d\n",
port->actor_port_number);
/* mark ntt as false, so it will not be sent *againuntildemanded
*/
port->ntt = false;
/* restart tx timer(to verify that we will not *exceedAD_MAX_TX_IN_SECOND
*/
port->sm_tx_timer_counter = ad_ticks_per_sec / AD_MAX_TX_IN_SECOND;
}
}
}
}
/* keep current state machine state to compare later if it was changed */
last_state = port->sm_periodic_state;
/* check if port was reinitialized */ if (((port->sm_vars & AD_PORT_BEGIN) || !(port->sm_vars & AD_PORT_LACP_ENABLED) || !port->is_enabled) ||
(!(port->actor_oper_port_state & LACP_STATE_LACP_ACTIVITY) && !(port->partner_oper.port_state & LACP_STATE_LACP_ACTIVITY))) {
port->sm_periodic_state = AD_NO_PERIODIC;
} /* check if state machine should change state */ elseif (port->sm_periodic_timer_counter) { /* check if periodic state machine expired */ if (!(--port->sm_periodic_timer_counter)) { /* if expired then do tx */
port->sm_periodic_state = AD_PERIODIC_TX;
} else { /* If not expired, check if there is some new timeout *parameterfromthepartnerstate
*/ switch (port->sm_periodic_state) { case AD_FAST_PERIODIC: if (!(port->partner_oper.port_state
& LACP_STATE_LACP_TIMEOUT))
port->sm_periodic_state = AD_SLOW_PERIODIC; break; case AD_SLOW_PERIODIC: if ((port->partner_oper.port_state & LACP_STATE_LACP_TIMEOUT)) {
port->sm_periodic_timer_counter = 0;
port->sm_periodic_state = AD_PERIODIC_TX;
} break; default: break;
}
}
} else { switch (port->sm_periodic_state) { case AD_NO_PERIODIC:
port->sm_periodic_state = AD_FAST_PERIODIC; break; case AD_PERIODIC_TX: if (!(port->partner_oper.port_state &
LACP_STATE_LACP_TIMEOUT))
port->sm_periodic_state = AD_SLOW_PERIODIC; else
port->sm_periodic_state = AD_FAST_PERIODIC; break; default: break;
}
}
/* check if the state machine was changed */ if (port->sm_periodic_state != last_state) {
slave_dbg(port->slave->bond->dev, port->slave->dev, "Periodic Machine: Port=%d, Last State=%d, Curr State=%d\n",
port->actor_port_number, last_state,
port->sm_periodic_state); switch (port->sm_periodic_state) { case AD_NO_PERIODIC:
port->sm_periodic_timer_counter = 0; break; case AD_FAST_PERIODIC: /* decrement 1 tick we lost in the PERIODIC_TX cycle */
port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_FAST_PERIODIC_TIME))-1; break; case AD_SLOW_PERIODIC: /* decrement 1 tick we lost in the PERIODIC_TX cycle */
port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_SLOW_PERIODIC_TIME))-1; break; case AD_PERIODIC_TX:
port->ntt = true; break; default: break;
}
}
}
/* if the port is already Selected, do nothing */ if (port->sm_vars & AD_PORT_SELECTED) return;
bond = __get_bond_by_port(port);
/* if the port is connected to other aggregator, detach it */ if (port->aggregator) { /* detach the port from its former aggregator */
temp_aggregator = port->aggregator; for (curr_port = temp_aggregator->lag_ports; curr_port;
last_port = curr_port,
curr_port = curr_port->next_port_in_aggregator) { if (curr_port == port) {
temp_aggregator->num_of_ports--; /* if it is the first port attached to the *aggregator
*/ if (!last_port) {
temp_aggregator->lag_ports =
port->next_port_in_aggregator;
} else { /* not the first port attached to the *aggregator
*/
last_port->next_port_in_aggregator =
port->next_port_in_aggregator;
}
/* clear the port's relations to this *aggregator
*/
port->aggregator = NULL;
port->next_port_in_aggregator = NULL;
port->actor_port_aggregator_identifier = 0;
slave_dbg(bond->dev, port->slave->dev, "Port %d left LAG %d\n",
port->actor_port_number,
temp_aggregator->aggregator_identifier); /* if the aggregator is empty, clear its *parameters,andsetitreadytobeattached
*/ if (!temp_aggregator->lag_ports)
ad_clear_agg(temp_aggregator); break;
}
} if (!curr_port) { /* meaning: the port was related to an aggregator *butwasnotontheaggregatorportlist
*/
net_warn_ratelimited("%s: (slave %s): Warning: Port %d was related to aggregator %d but was not on its port list\n",
port->slave->bond->dev->name,
port->slave->dev->name,
port->actor_port_number,
port->aggregator->aggregator_identifier);
}
} /* search on all aggregators for a suitable aggregator for this port */
bond_for_each_slave(bond, slave, iter) {
aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
/* keep a free aggregator for later use(if needed) */ if (!aggregator->lag_ports) { if (!free_aggregator)
free_aggregator = aggregator; continue;
} /* check if current aggregator suits us */ if (((aggregator->actor_oper_aggregator_key == port->actor_oper_port_key) && /* if all parameters match AND */
MAC_ADDRESS_EQUAL(&(aggregator->partner_system), &(port->partner_oper.system)) &&
(aggregator->partner_system_priority == port->partner_oper.system_priority) &&
(aggregator->partner_oper_aggregator_key == port->partner_oper.key)
) &&
((__agg_has_partner(aggregator) && /* partner answers */
!aggregator->is_individual) /* but is not individual OR */
)
) { /* attach to the founded aggregator */
port->aggregator = aggregator;
port->actor_port_aggregator_identifier =
port->aggregator->aggregator_identifier;
port->next_port_in_aggregator = aggregator->lag_ports;
port->aggregator->num_of_ports++;
aggregator->lag_ports = port;
slave_dbg(bond->dev, slave->dev, "Port %d joined LAG %d (existing LAG)\n",
port->actor_port_number,
port->aggregator->aggregator_identifier);
/* mark this port as selected */
port->sm_vars |= AD_PORT_SELECTED;
found = 1; break;
}
}
/* the port couldn't find an aggregator - attach it to a new *aggregator
*/ if (!found) { if (free_aggregator) { /* assign port a new aggregator */
port->aggregator = free_aggregator;
port->actor_port_aggregator_identifier =
port->aggregator->aggregator_identifier;
/* update the new aggregator's parameters *ifportwasresponsedfromtheend-user
*/ if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS) /* if port is full duplex */
port->aggregator->is_individual = false; else
port->aggregator->is_individual = true;
/* mark this port as selected */
port->sm_vars |= AD_PORT_SELECTED;
slave_dbg(bond->dev, port->slave->dev, "Port %d joined LAG %d (new LAG)\n",
port->actor_port_number,
port->aggregator->aggregator_identifier);
} else {
slave_err(bond->dev, port->slave->dev, "Port %d did not find a suitable aggregator\n",
port->actor_port_number); return;
}
} /* if all aggregator's ports are READY_N == TRUE, set ready=TRUE *inallaggregator'sports,elsesetready=FALSEinall *aggregator'sports
*/
__set_agg_ports_ready(port->aggregator,
__agg_ports_are_ready(port->aggregator));
staticint agg_device_up(conststruct aggregator *agg)
{ struct port *port = agg->lag_ports;
if (!port) return0;
for (port = agg->lag_ports; port;
port = port->next_port_in_aggregator) { if (netif_running(port->slave->dev) &&
netif_carrier_ok(port->slave->dev)) return1;
}
if (__agg_active_ports(agg) && agg_device_up(agg))
best = ad_agg_selection_test(best, agg);
}
if (best &&
__get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) { /* For the STABLE policy, don't replace the old active *aggregatorifit'sstillactive(ithasananswering *partner)orifboththebestandactivedon'thavean *answeringpartner.
*/ if (active && active->lag_ports &&
__agg_active_ports(active) &&
(__agg_has_partner(active) ||
(!__agg_has_partner(active) &&
!__agg_has_partner(best)))) { if (!(!active->actor_oper_aggregator_key &&
best->actor_oper_aggregator_key)) {
best = NULL;
active->is_active = 1;
}
}
}
if (best && (best == active)) {
best = NULL;
active->is_active = 1;
}
/* if there is new best aggregator, activate it */ if (best) {
netdev_dbg(bond->dev, "(slave %s): best Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
best->slave ? best->slave->dev->name : "NULL",
best->aggregator_identifier, best->num_of_ports,
best->actor_oper_aggregator_key,
best->partner_oper_aggregator_key,
best->is_individual, best->is_active);
netdev_dbg(bond->dev, "(slave %s): best ports %p slave %p\n",
best->slave ? best->slave->dev->name : "NULL",
best->lag_ports, best->slave);
slave_dbg(bond->dev, slave->dev, "Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
agg->aggregator_identifier, agg->num_of_ports,
agg->actor_oper_aggregator_key,
agg->partner_oper_aggregator_key,
agg->is_individual, agg->is_active);
}
/* check if any partner replies */ if (best->is_individual)
net_warn_ratelimited("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n",
bond->dev->name);
best->is_active = 1;
netdev_dbg(bond->dev, "(slave %s): LAG %d chosen as the active LAG\n",
best->slave ? best->slave->dev->name : "NULL",
best->aggregator_identifier);
netdev_dbg(bond->dev, "(slave %s): Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
best->slave ? best->slave->dev->name : "NULL",
best->aggregator_identifier, best->num_of_ports,
best->actor_oper_aggregator_key,
best->partner_oper_aggregator_key,
best->is_individual, best->is_active);
/* disable the ports that were related to the former *active_aggregator
*/ if (active) { for (port = active->lag_ports; port;
port = port->next_port_in_aggregator) {
__disable_port(port);
}
} /* Slave array needs update. */
*update_slave_arr = true;
}
/* if the selected aggregator is of join individuals *(partner_systemisNULL),enabletheirports
*/
active = __get_active_agg(origin);
if (active) { if (!__agg_has_partner(active)) { for (port = active->lag_ports; port;
port = port->next_port_in_aggregator) {
__enable_port(port);
}
*update_slave_arr = true;
}
}
/* copy the received marker data to the response marker */
memcpy(&marker, marker_info, sizeof(struct bond_marker)); /* change the marker subtype to marker response */
marker.tlv_type = AD_MARKER_RESPONSE_SUBTYPE;
/* send the marker response */ if (ad_marker_send(port, &marker) >= 0)
slave_dbg(port->slave->bond->dev, port->slave->dev, "Sent Marker Response on port %d\n",
port->actor_port_number);
}
/* check that the slave has not been initialized yet. */ if (SLAVE_AD_INFO(slave)->port.slave != slave) {
/* port initialization */
port = &(SLAVE_AD_INFO(slave)->port);
ad_initialize_port(port, &bond->params);
port->slave = slave;
port->actor_port_number = SLAVE_AD_INFO(slave)->id; /* key is determined according to the link speed, duplex and *userkey
*/
port->actor_admin_port_key = bond->params.ad_user_port_key << 6;
ad_update_actor_keys(port, false); /* actor system is the bond's system */
__ad_actor_update_port(port); /* tx timer(to verify that no more than MAX_TX_IN_SECOND *lacpdu'saresentinonesecond)
*/
port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND;
/** *bond_3ad_unbind_slave-deinitializeaslave'sport *@slave:slavestructtoworkon * *Searchfortheaggregatorthatisrelatedtothisport,removethe *aggregatorandassignanotheraggregatorforotherportrelatedtoit *(ifany),andremovetheport.
*/ void bond_3ad_unbind_slave(struct slave *slave)
{ struct port *port, *prev_port, *temp_port; struct aggregator *aggregator, *new_aggregator, *temp_aggregator; int select_new_active_agg = 0; struct bonding *bond = slave->bond; struct slave *slave_iter; struct list_head *iter; bool dummy_slave_update; /* Ignore this value as caller updates array */
/* Sync against bond_3ad_state_machine_handler() */
spin_lock_bh(&bond->mode_lock);
aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
port = &(SLAVE_AD_INFO(slave)->port);
/* if slave is null, the whole port is not initialized */ if (!port->slave) {
slave_warn(bond->dev, slave->dev, "Trying to unbind an uninitialized port\n"); goto out;
}
slave_dbg(bond->dev, slave->dev, "Unbinding Link Aggregation Group %d\n",
aggregator->aggregator_identifier);
/* Tell the partner that this port is not suitable for aggregation */
port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION;
port->actor_oper_port_state &= ~LACP_STATE_COLLECTING;
port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING;
port->actor_oper_port_state &= ~LACP_STATE_AGGREGATION;
__update_lacpdu_from_port(port);
ad_lacpdu_send(port);
/* check if this aggregator is occupied */ if (aggregator->lag_ports) { /* check if there are other ports related to this aggregator *excepttheportrelatedtothisslave(thatsensureusthat *thereisareasontosearchfornewaggregator,andthatwe *willfindone
*/ if ((aggregator->lag_ports != port) ||
(aggregator->lag_ports->next_port_in_aggregator)) { /* find new aggregator for the related port(s) */
bond_for_each_slave(bond, slave_iter, iter) {
new_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator); /* if the new aggregator is empty, or it is *connectedtoourportonly
*/ if (!new_aggregator->lag_ports ||
((new_aggregator->lag_ports == port) &&
!new_aggregator->lag_ports->next_port_in_aggregator)) break;
} if (!slave_iter)
new_aggregator = NULL;
/* if new aggregator found, copy the aggregator's *parametersandconnecttherelatedlag_portstothe *newaggregator
*/ if ((new_aggregator) && ((!new_aggregator->lag_ports) || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator))) {
slave_dbg(bond->dev, slave->dev, "Some port(s) related to LAG %d - replacing with LAG %d\n",
aggregator->aggregator_identifier,
new_aggregator->aggregator_identifier);
if ((new_aggregator->lag_ports == port) &&
new_aggregator->is_active) {
slave_info(bond->dev, slave->dev, "Removing an active aggregator\n");
select_new_active_agg = 1;
}
/* update the information that is written on *theportsabouttheaggregator
*/ for (temp_port = aggregator->lag_ports; temp_port;
temp_port = temp_port->next_port_in_aggregator) {
temp_port->aggregator = new_aggregator;
temp_port->actor_port_aggregator_identifier = new_aggregator->aggregator_identifier;
}
ad_clear_agg(aggregator);
if (select_new_active_agg)
ad_agg_selection_logic(__get_first_agg(port),
&dummy_slave_update);
} else {
slave_warn(bond->dev, slave->dev, "unbinding aggregator, and could not find a new aggregator for its ports\n");
}
} else { /* in case that the only port related to this *aggregatoristheonewewanttoremove
*/
select_new_active_agg = aggregator->is_active;
ad_clear_agg(aggregator); if (select_new_active_agg) {
slave_info(bond->dev, slave->dev, "Removing an active aggregator\n"); /* select new active aggregator */
temp_aggregator = __get_first_agg(port); if (temp_aggregator)
ad_agg_selection_logic(temp_aggregator,
&dummy_slave_update);
}
}
}
slave_dbg(bond->dev, slave->dev, "Unbinding port %d\n", port->actor_port_number);
/* find the aggregator that this port is connected to */
bond_for_each_slave(bond, slave_iter, iter) {
temp_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator);
prev_port = NULL; /* search the port in the aggregator's related ports */ for (temp_port = temp_aggregator->lag_ports; temp_port;
prev_port = temp_port,
temp_port = temp_port->next_port_in_aggregator) { if (temp_port == port) { /* the aggregator found - detach the port from *thisaggregator
*/ if (prev_port)
prev_port->next_port_in_aggregator = temp_port->next_port_in_aggregator; else
temp_aggregator->lag_ports = temp_port->next_port_in_aggregator;
temp_aggregator->num_of_ports--; if (__agg_active_ports(temp_aggregator) == 0) {
select_new_active_agg = temp_aggregator->is_active; if (temp_aggregator->num_of_ports == 0)
ad_clear_agg(temp_aggregator); if (select_new_active_agg) {
slave_info(bond->dev, slave->dev, "Removing an active aggregator\n"); /* select new active aggregator */
ad_agg_selection_logic(__get_first_agg(port),
&dummy_slave_update);
}
} break;
}
}
}
port->slave = NULL;
/* Lock to protect data accessed by all (e.g., port->sm_vars) and *againstrunningwithbond_3ad_unbind_slave.ad_rx_machinemayrun *concurrentlyduetoincomingLACPDUaswell.
*/
spin_lock_bh(&bond->mode_lock);
rcu_read_lock();
/* check if there are any slaves */ if (!bond_has_slaves(bond)) goto re_arm;
if (bond_agg_timer_advance(bond)) {
slave = bond_first_slave_rcu(bond);
port = slave ? &(SLAVE_AD_INFO(slave)->port) : NULL;
/* select the active aggregator for the bond */ if (port) { if (!port->slave) {
net_warn_ratelimited("%s: Warning: bond's first port is uninitialized\n",
bond->dev->name); goto re_arm;
}
/* for each port run the state machines */
bond_for_each_slave_rcu(bond, slave, iter) {
port = &(SLAVE_AD_INFO(slave)->port); if (!port->slave) {
net_warn_ratelimited("%s: Warning: Found an uninitialized port\n",
bond->dev->name); goto re_arm;
}
port = &(SLAVE_AD_INFO(slave)->port); if (!port->slave) {
net_warn_ratelimited("%s: Warning: port of slave %s is uninitialized\n",
slave->dev->name, slave->bond->dev->name); return ret;
}
switch (lacpdu->subtype) { case AD_TYPE_LACPDU:
ret = RX_HANDLER_CONSUMED;
slave_dbg(slave->bond->dev, slave->dev, "Received LACPDU on port %d\n",
port->actor_port_number); /* Protect against concurrent state machines */
spin_lock(&slave->bond->mode_lock);
ad_rx_machine(lacpdu, port);
spin_unlock(&slave->bond->mode_lock); break; case AD_TYPE_MARKER:
ret = RX_HANDLER_CONSUMED; /* No need to convert fields to Little Endian since we *don'tusethemarker'sfields.
*/
marker = (struct bond_marker *)lacpdu; switch (marker->tlv_type) { case AD_MARKER_INFORMATION_SUBTYPE:
slave_dbg(slave->bond->dev, slave->dev, "Received Marker Information on port %d\n",
port->actor_port_number);
ad_marker_info_received(marker, port); break; case AD_MARKER_RESPONSE_SUBTYPE:
slave_dbg(slave->bond->dev, slave->dev, "Received Marker Response on port %d\n",
port->actor_port_number);
ad_marker_response_received(marker, port); break; default:
slave_dbg(slave->bond->dev, slave->dev, "Received an unknown Marker subtype on port %d\n",
port->actor_port_number);
stat = &SLAVE_AD_INFO(slave)->stats.marker_unknown_rx;
atomic64_inc(stat);
stat = &BOND_AD_INFO(bond).stats.marker_unknown_rx;
atomic64_inc(stat);
} break; default:
atomic64_inc(&SLAVE_AD_INFO(slave)->stats.lacpdu_unknown_rx);
atomic64_inc(&BOND_AD_INFO(bond).stats.lacpdu_unknown_rx);
}
if (old_oper_key != port->actor_oper_port_key) { /* Only 'duplex' port participates in LACP */ if (duplex)
port->sm_vars |= AD_PORT_LACP_ENABLED; else
port->sm_vars &= ~AD_PORT_LACP_ENABLED;
if (!reset) { if (!speed) {
slave_err(port->slave->bond->dev,
port->slave->dev, "speed changed to 0 on port %d\n",
port->actor_port_number);
} elseif (duplex && ospeed != speed) { /* Speed change restarts LACP state-machine */
port->sm_vars |= AD_PORT_BEGIN;
}
}
}
}
/* if slave is null, the whole port is not initialized */ if (!port->slave) {
slave_warn(slave->bond->dev, slave->dev, "speed/duplex changed for uninitialized port\n"); return;
}
/* if slave is null, the whole port is not initialized */ if (!port->slave) {
slave_warn(slave->bond->dev, slave->dev, "link status changed for uninitialized port\n"); return;
}
spin_lock_bh(&slave->bond->mode_lock); /* on link down we are zeroing duplex and speed since *someoftheadaptors(ce1000.lan)reportfullduplex/speed *insteadofN/A(duplex)/0(speed). * *onlinkupweareforcingrecheckontheduplexandspeedsince *someofheadaptors(ce1000.lan)report.
*/ if (link == BOND_LINK_UP) {
port->is_enabled = true;
ad_update_actor_keys(port, false);
} else { /* link has failed */
port->is_enabled = false;
ad_update_actor_keys(port, true);
}
agg = __get_first_agg(port);
ad_agg_selection_logic(agg, &dummy);
spin_unlock_bh(&slave->bond->mode_lock);
slave_dbg(slave->bond->dev, slave->dev, "Port %d changed link status to %s\n",
port->actor_port_number,
link == BOND_LINK_UP ? "UP" : "DOWN");
/* RTNL is held and mode_lock is released so it's safe *toupdateslave_arrayhere.
*/
bond_update_slave_arr(slave->bond, NULL);
}
/** *bond_3ad_set_carrier-setlinkstateforbondingmaster *@bond:bondingstructure * *ifwehaveanactiveaggregator,we'reup,ifnot,we'redown. *Presumesthatwecannothaveanactiveaggregatorifthereare *noslaveswithlinkup. * *ThisbehaviorcomplieswithIEEE802.3section43.3.9. * *Calledbybond_set_carrier().Returnzeroifcarrierstatedoesnot *change,nonzeroifitdoes.
*/ int bond_3ad_set_carrier(struct bonding *bond)
{ struct aggregator *active; struct slave *first_slave; int ret = 1;
rcu_read_lock();
first_slave = bond_first_slave_rcu(bond); if (!first_slave) {
ret = 0; goto out;
}
active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator)); if (active) { /* are enough slaves available to consider link up? */ if (__agg_active_ports(active) < bond->params.min_links) { if (netif_carrier_ok(bond->dev)) {
netif_carrier_off(bond->dev); goto out;
}
} elseif (!netif_carrier_ok(bond->dev)) {
netif_carrier_on(bond->dev); goto out;
}
} elseif (netif_carrier_ok(bond->dev)) {
netif_carrier_off(bond->dev);
}
out:
rcu_read_unlock(); return ret;
}
int bond_3ad_stats_fill(struct sk_buff *skb, struct bond_3ad_stats *stats)
{
u64 val;
val = atomic64_read(&stats->lacpdu_rx); if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_RX, val,
BOND_3AD_STAT_PAD)) return -EMSGSIZE;
val = atomic64_read(&stats->lacpdu_tx); if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_TX, val,
BOND_3AD_STAT_PAD)) return -EMSGSIZE;
val = atomic64_read(&stats->lacpdu_unknown_rx); if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_UNKNOWN_RX, val,
BOND_3AD_STAT_PAD)) return -EMSGSIZE;
val = atomic64_read(&stats->lacpdu_illegal_rx); if (nla_put_u64_64bit(skb, BOND_3AD_STAT_LACPDU_ILLEGAL_RX, val,
BOND_3AD_STAT_PAD)) return -EMSGSIZE;
val = atomic64_read(&stats->marker_rx); if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RX, val,
BOND_3AD_STAT_PAD)) return -EMSGSIZE;
val = atomic64_read(&stats->marker_tx); if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_TX, val,
BOND_3AD_STAT_PAD)) return -EMSGSIZE;
val = atomic64_read(&stats->marker_resp_rx); if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RESP_RX, val,
BOND_3AD_STAT_PAD)) return -EMSGSIZE;
val = atomic64_read(&stats->marker_resp_tx); if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_RESP_TX, val,
BOND_3AD_STAT_PAD)) return -EMSGSIZE;
val = atomic64_read(&stats->marker_unknown_rx); if (nla_put_u64_64bit(skb, BOND_3AD_STAT_MARKER_UNKNOWN_RX, val,
BOND_3AD_STAT_PAD)) return -EMSGSIZE;
return0;
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.176Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.