Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bonding: fix 802.3ad standards compliance error

The language of 802.3ad 43.4.9 requires the "recordPDU" function
to, in part, compare the Partner parameter values in a received LACPDU
to the stored Actor values. If those match, then the Partner's
synchronization state is set to true.

The current 802.3ad implementation is performing these steps out
of order; first, the synchronization check is done, then the paramters are
checked to see if they match (the synch check being done against a match
check of a prior LACPDU). This causes delays in establishing aggregators
in some circumstances.

This patch modifies the 802.3ad code to call __choose_matched,
the function that does the "match" comparisions, as the first step of
__record_pdu, instead of immediately afterwards. This new behavior is
in compliance with the language of the standard.

Some additional commentary relating to code vs. standard is also
added.

Reported by Martin Patterson <martin@gear6.com> who also supplied
the logic of the fix and verified the patch.

Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jay Vosburgh and committed by
David S. Miller
2d6682db b93ab837

+43 -42
+43 -42
drivers/net/bonding/bond_3ad.c
··· 446 446 ///////////////////////////////////////////////////////////////////////////////// 447 447 448 448 /** 449 + * __choose_matched - update a port's matched variable from a received lacpdu 450 + * @lacpdu: the lacpdu we've received 451 + * @port: the port we're looking at 452 + * 453 + * Update the value of the matched variable, using parameter values from a 454 + * newly received lacpdu. Parameter values for the partner carried in the 455 + * received PDU are compared with the corresponding operational parameter 456 + * values for the actor. Matched is set to TRUE if all of these parameters 457 + * match and the PDU parameter partner_state.aggregation has the same value as 458 + * actor_oper_port_state.aggregation and lacp will actively maintain the link 459 + * in the aggregation. Matched is also set to TRUE if the value of 460 + * actor_state.aggregation in the received PDU is set to FALSE, i.e., indicates 461 + * an individual link and lacp will actively maintain the link. Otherwise, 462 + * matched is set to FALSE. LACP is considered to be actively maintaining the 463 + * link if either the PDU's actor_state.lacp_activity variable is TRUE or both 464 + * the actor's actor_oper_port_state.lacp_activity and the PDU's 465 + * partner_state.lacp_activity variables are TRUE. 466 + * 467 + * Note: the AD_PORT_MATCHED "variable" is not specified by 802.3ad; it is 468 + * used here to implement the language from 802.3ad 43.4.9 that requires 469 + * recordPDU to "match" the LACPDU parameters to the stored values. 470 + */ 471 + static void __choose_matched(struct lacpdu *lacpdu, struct port *port) 472 + { 473 + // check if all parameters are alike 474 + if (((ntohs(lacpdu->partner_port) == port->actor_port_number) && 475 + (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) && 476 + !MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) && 477 + (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) && 478 + (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) && 479 + ((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) || 480 + // or this is individual link(aggregation == FALSE) 481 + ((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0) 482 + ) { 483 + // update the state machine Matched variable 484 + port->sm_vars |= AD_PORT_MATCHED; 485 + } else { 486 + port->sm_vars &= ~AD_PORT_MATCHED; 487 + } 488 + } 489 + 490 + /** 449 491 * __record_pdu - record parameters from a received lacpdu 450 492 * @lacpdu: the lacpdu we've received 451 493 * @port: the port we're looking at ··· 501 459 if (lacpdu && port) { 502 460 struct port_params *partner = &port->partner_oper; 503 461 462 + __choose_matched(lacpdu, port); 504 463 // record the new parameter values for the partner operational 505 464 partner->port_number = ntohs(lacpdu->actor_port); 506 465 partner->port_priority = ntohs(lacpdu->actor_port_priority); ··· 601 558 != (oper->port_state & AD_STATE_AGGREGATION)) { 602 559 // update the state machine Selected variable 603 560 port->sm_vars &= ~AD_PORT_SELECTED; 604 - } 605 - } 606 - } 607 - 608 - /** 609 - * __choose_matched - update a port's matched variable from a received lacpdu 610 - * @lacpdu: the lacpdu we've received 611 - * @port: the port we're looking at 612 - * 613 - * Update the value of the matched variable, using parameter values from a 614 - * newly received lacpdu. Parameter values for the partner carried in the 615 - * received PDU are compared with the corresponding operational parameter 616 - * values for the actor. Matched is set to TRUE if all of these parameters 617 - * match and the PDU parameter partner_state.aggregation has the same value as 618 - * actor_oper_port_state.aggregation and lacp will actively maintain the link 619 - * in the aggregation. Matched is also set to TRUE if the value of 620 - * actor_state.aggregation in the received PDU is set to FALSE, i.e., indicates 621 - * an individual link and lacp will actively maintain the link. Otherwise, 622 - * matched is set to FALSE. LACP is considered to be actively maintaining the 623 - * link if either the PDU's actor_state.lacp_activity variable is TRUE or both 624 - * the actor's actor_oper_port_state.lacp_activity and the PDU's 625 - * partner_state.lacp_activity variables are TRUE. 626 - */ 627 - static void __choose_matched(struct lacpdu *lacpdu, struct port *port) 628 - { 629 - // validate lacpdu and port 630 - if (lacpdu && port) { 631 - // check if all parameters are alike 632 - if (((ntohs(lacpdu->partner_port) == port->actor_port_number) && 633 - (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) && 634 - !MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) && 635 - (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) && 636 - (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) && 637 - ((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) || 638 - // or this is individual link(aggregation == FALSE) 639 - ((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0) 640 - ) { 641 - // update the state machine Matched variable 642 - port->sm_vars |= AD_PORT_MATCHED; 643 - } else { 644 - port->sm_vars &= ~AD_PORT_MATCHED; 645 561 } 646 562 } 647 563 } ··· 1136 1134 __update_selected(lacpdu, port); 1137 1135 __update_ntt(lacpdu, port); 1138 1136 __record_pdu(lacpdu, port); 1139 - __choose_matched(lacpdu, port); 1140 1137 port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT)); 1141 1138 port->actor_oper_port_state &= ~AD_STATE_EXPIRED; 1142 1139 // verify that if the aggregator is enabled, the port is enabled too.