--- zzzz-none-000/linux-3.10.107/drivers/net/bonding/bond_3ad.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/net/bonding/bond_3ad.c 2021-02-04 17:41:59.000000000 +0000 @@ -1,4 +1,6 @@ /* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * * Copyright(c) 1999 - 2004 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it @@ -20,8 +22,6 @@ * */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include @@ -31,17 +31,17 @@ #include #include #include -#include "bonding.h" -#include "bond_3ad.h" +#include +#include -// General definitions +/* General definitions */ #define AD_SHORT_TIMEOUT 1 #define AD_LONG_TIMEOUT 0 #define AD_STANDBY 0x2 #define AD_MAX_TX_IN_SECOND 3 #define AD_COLLECTOR_MAX_DELAY 0 -// Timer definitions(43.4.4 in the 802.3ad standard) +/* Timer definitions (43.4.4 in the 802.3ad standard) */ #define AD_FAST_PERIODIC_TIME 1 #define AD_SLOW_PERIODIC_TIME 30 #define AD_SHORT_TIMEOUT_TIME (3*AD_FAST_PERIODIC_TIME) @@ -49,7 +49,7 @@ #define AD_CHURN_DETECTION_TIME 60 #define AD_AGGREGATE_WAIT_TIME 2 -// Port state definitions(43.4.2.2 in the 802.3ad standard) +/* Port state definitions (43.4.2.2 in the 802.3ad standard) */ #define AD_STATE_LACP_ACTIVITY 0x1 #define AD_STATE_LACP_TIMEOUT 0x2 #define AD_STATE_AGGREGATION 0x4 @@ -59,7 +59,9 @@ #define AD_STATE_DEFAULTED 0x40 #define AD_STATE_EXPIRED 0x80 -// Port Variables definitions used by the State Machines(43.4.7 in the 802.3ad standard) +/* Port Variables definitions used by the State Machines (43.4.7 in the + * 802.3ad standard) + */ #define AD_PORT_BEGIN 0x1 #define AD_PORT_LACP_ENABLED 0x2 #define AD_PORT_ACTOR_CHURN 0x4 @@ -70,28 +72,35 @@ #define AD_PORT_STANDBY 0x80 #define AD_PORT_SELECTED 0x100 #define AD_PORT_MOVED 0x200 +#define AD_PORT_CHURNED (AD_PORT_ACTOR_CHURN | AD_PORT_PARTNER_CHURN) -// Port Key definitions -// key is determined according to the link speed, duplex and -// user key(which is yet not supported) -// ------------------------------------------------------------ -// Port key : | User key | Speed |Duplex| -// ------------------------------------------------------------ -// 16 6 1 0 -#define AD_DUPLEX_KEY_BITS 0x1 -#define AD_SPEED_KEY_BITS 0x3E -#define AD_USER_KEY_BITS 0xFFC0 - -//dalloun -#define AD_LINK_SPEED_BITMASK_1MBPS 0x1 -#define AD_LINK_SPEED_BITMASK_10MBPS 0x2 -#define AD_LINK_SPEED_BITMASK_100MBPS 0x4 -#define AD_LINK_SPEED_BITMASK_1000MBPS 0x8 -#define AD_LINK_SPEED_BITMASK_10000MBPS 0x10 -//endalloun - -// compare MAC addresses -#define MAC_ADDRESS_COMPARE(A, B) memcmp(A, B, ETH_ALEN) +/* Port Key definitions + * key is determined according to the link speed, duplex and + * user key (which is yet not supported) + * -------------------------------------------------------------- + * Port key | User key (10 bits) | Speed (5 bits) | Duplex| + * -------------------------------------------------------------- + * |15 6|5 1|0 + */ +#define AD_DUPLEX_KEY_MASKS 0x1 +#define AD_SPEED_KEY_MASKS 0x3E +#define AD_USER_KEY_MASKS 0xFFC0 + +enum ad_link_speed_type { + AD_LINK_SPEED_1MBPS = 1, + AD_LINK_SPEED_10MBPS, + AD_LINK_SPEED_100MBPS, + AD_LINK_SPEED_1000MBPS, + AD_LINK_SPEED_2500MBPS, + AD_LINK_SPEED_10000MBPS, + AD_LINK_SPEED_20000MBPS, + AD_LINK_SPEED_40000MBPS, + AD_LINK_SPEED_56000MBPS +}; + +/* compare MAC addresses */ +#define MAC_ADDRESS_EQUAL(A, B) \ + ether_addr_equal_64bits((const u8 *)A, (const u8 *)B) static struct mac_addr null_mac_addr = { { 0, 0, 0, 0, 0, 0 } }; static u16 ad_ticks_per_sec; @@ -99,73 +108,75 @@ static const u8 lacpdu_mcast_addr[ETH_ALEN] = MULTICAST_LACPDU_ADDR; -// ================= main 802.3ad protocol functions ================== +/* ================= main 802.3ad protocol functions ================== */ static int ad_lacpdu_send(struct port *port); static int ad_marker_send(struct port *port, struct bond_marker *marker); -static void ad_mux_machine(struct port *port); +static void ad_mux_machine(struct port *port, bool *update_slave_arr); static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port); static void ad_tx_machine(struct port *port); static void ad_periodic_machine(struct port *port); -static void ad_port_selection_logic(struct port *port); -static void ad_agg_selection_logic(struct aggregator *aggregator); +static void ad_port_selection_logic(struct port *port, bool *update_slave_arr); +static void ad_agg_selection_logic(struct aggregator *aggregator, + bool *update_slave_arr); static void ad_clear_agg(struct aggregator *aggregator); static void ad_initialize_agg(struct aggregator *aggregator); static void ad_initialize_port(struct port *port, int lacp_fast); -static void ad_enable_collecting_distributing(struct port *port); -static void ad_disable_collecting_distributing(struct port *port); -static void ad_marker_info_received(struct bond_marker *marker_info, struct port *port); -static void ad_marker_response_received(struct bond_marker *marker, struct port *port); - +static void ad_enable_collecting_distributing(struct port *port, + bool *update_slave_arr); +static void ad_disable_collecting_distributing(struct port *port, + bool *update_slave_arr); +static void ad_marker_info_received(struct bond_marker *marker_info, + struct port *port); +static void ad_marker_response_received(struct bond_marker *marker, + struct port *port); +static void ad_update_actor_keys(struct port *port, bool reset); -///////////////////////////////////////////////////////////////////////////////// -// ================= api to bonding and kernel code ================== -///////////////////////////////////////////////////////////////////////////////// +struct bond_cb __rcu *bond_cb; -/** - * __get_bond_by_port - get the port's bonding struct - * @port: the port we're looking at - * - * Return @port's bonding struct, or %NULL if it can't be found. - */ -static inline struct bonding *__get_bond_by_port(struct port *port) +int bond_register_cb(struct bond_cb *cb) { - if (port->slave == NULL) - return NULL; + struct bond_cb *lag_cb; - return bond_get_bond_by_slave(port->slave); + rcu_read_lock(); + lag_cb = kzalloc(sizeof(*lag_cb), GFP_ATOMIC | __GFP_NOWARN); + if (!lag_cb) { + rcu_read_unlock(); + return -1; + } + + memcpy((void *)lag_cb, (void *)cb, sizeof(*cb)); + rcu_assign_pointer(bond_cb, lag_cb); + rcu_read_unlock(); + return 0; } +EXPORT_SYMBOL(bond_register_cb); -/** - * __get_first_port - get the first port in the bond - * @bond: the bond we're looking at - * - * Return the port of the first slave in @bond, or %NULL if it can't be found. - */ -static inline struct port *__get_first_port(struct bonding *bond) +void bond_unregister_cb(void) { - if (bond->slave_cnt == 0) - return NULL; + struct bond_cb *lag_cb_main; - return &(SLAVE_AD_INFO(bond->first_slave).port); + rcu_read_lock(); + lag_cb_main = rcu_dereference(bond_cb); + kfree(lag_cb_main); + rcu_assign_pointer(bond_cb, NULL); + rcu_read_unlock(); } +EXPORT_SYMBOL(bond_unregister_cb); + +/* ================= api to bonding and kernel code ================== */ /** - * __get_next_port - get the next port in the bond + * __get_bond_by_port - get the port's bonding struct * @port: the port we're looking at * - * Return the port of the slave that is next in line of @port's slave in the - * bond, or %NULL if it can't be found. + * Return @port's bonding struct, or %NULL if it can't be found. */ -static inline struct port *__get_next_port(struct port *port) +static inline struct bonding *__get_bond_by_port(struct port *port) { - struct bonding *bond = __get_bond_by_port(port); - struct slave *slave = port->slave; - - // If there's no bond for this port, or this is the last slave - if ((bond == NULL) || (slave->next == bond->first_slave)) + if (port->slave == NULL) return NULL; - return &(SLAVE_AD_INFO(slave->next).port); + return bond_get_bond_by_slave(port->slave); } /** @@ -174,42 +185,32 @@ * * Return the aggregator of the first slave in @bond, or %NULL if it can't be * found. + * The caller must hold RCU or RTNL lock. */ static inline struct aggregator *__get_first_agg(struct port *port) { struct bonding *bond = __get_bond_by_port(port); + struct slave *first_slave; + struct aggregator *agg; - // If there's no bond for this port, or bond has no slaves - if ((bond == NULL) || (bond->slave_cnt == 0)) + /* If there's no bond for this port, or bond has no slaves */ + if (bond == NULL) return NULL; - return &(SLAVE_AD_INFO(bond->first_slave).aggregator); -} - -/** - * __get_next_agg - get the next aggregator in the bond - * @aggregator: the aggregator we're looking at - * - * Return the aggregator of the slave that is next in line of @aggregator's - * slave in the bond, or %NULL if it can't be found. - */ -static inline struct aggregator *__get_next_agg(struct aggregator *aggregator) -{ - struct slave *slave = aggregator->slave; - struct bonding *bond = bond_get_bond_by_slave(slave); - - // If there's no bond for this aggregator, or this is the last slave - if ((bond == NULL) || (slave->next == bond->first_slave)) - return NULL; + rcu_read_lock(); + first_slave = bond_first_slave_rcu(bond); + agg = first_slave ? &(SLAVE_AD_INFO(first_slave)->aggregator) : NULL; + rcu_read_unlock(); - return &(SLAVE_AD_INFO(slave->next).aggregator); + return agg; } -/* - * __agg_has_partner +/** + * __agg_has_partner - see if we have a partner + * @agg: the agregator we're looking at * * Return nonzero if aggregator has a partner (denoted by a non-zero ether - * address for the partner). Return 0 if not. + * address for the partner). Return 0 if not. */ static inline int __agg_has_partner(struct aggregator *agg) { @@ -219,30 +220,27 @@ /** * __disable_port - disable the port's slave * @port: the port we're looking at - * */ static inline void __disable_port(struct port *port) { - bond_set_slave_inactive_flags(port->slave); + bond_set_slave_inactive_flags(port->slave, BOND_SLAVE_NOTIFY_LATER); } /** * __enable_port - enable the port's slave, if it's up * @port: the port we're looking at - * */ static inline void __enable_port(struct port *port) { struct slave *slave = port->slave; - if ((slave->link == BOND_LINK_UP) && IS_UP(slave->dev)) - bond_set_slave_active_flags(slave); + if ((slave->link == BOND_LINK_UP) && bond_slave_is_up(slave)) + bond_set_slave_active_flags(slave, BOND_SLAVE_NOTIFY_LATER); } /** * __port_is_enabled - check if the port's slave is in active state * @port: the port we're looking at - * */ static inline int __port_is_enabled(struct port *port) { @@ -268,7 +266,6 @@ /** * __check_agg_selection_timer - check if the selection timer has expired * @port: the port we're looking at - * */ static inline int __check_agg_selection_timer(struct port *port) { @@ -281,73 +278,75 @@ } /** - * __get_state_machine_lock - lock the port's state machines - * @port: the port we're looking at - * - */ -static inline void __get_state_machine_lock(struct port *port) -{ - spin_lock_bh(&(SLAVE_AD_INFO(port->slave).state_machine_lock)); -} - -/** - * __release_state_machine_lock - unlock the port's state machines - * @port: the port we're looking at - * - */ -static inline void __release_state_machine_lock(struct port *port) -{ - spin_unlock_bh(&(SLAVE_AD_INFO(port->slave).state_machine_lock)); -} - -/** * __get_link_speed - get a port's speed * @port: the port we're looking at * - * Return @port's speed in 802.3ad bitmask format. i.e. one of: + * Return @port's speed in 802.3ad enum format. i.e. one of: * 0, - * %AD_LINK_SPEED_BITMASK_10MBPS, - * %AD_LINK_SPEED_BITMASK_100MBPS, - * %AD_LINK_SPEED_BITMASK_1000MBPS, - * %AD_LINK_SPEED_BITMASK_10000MBPS + * %AD_LINK_SPEED_10MBPS, + * %AD_LINK_SPEED_100MBPS, + * %AD_LINK_SPEED_1000MBPS, + * %AD_LINK_SPEED_2500MBPS, + * %AD_LINK_SPEED_10000MBPS + * %AD_LINK_SPEED_20000MBPS + * %AD_LINK_SPEED_40000MBPS + * %AD_LINK_SPEED_56000MBPS */ static u16 __get_link_speed(struct port *port) { struct slave *slave = port->slave; u16 speed; - /* this if covers only a special case: when the configuration starts with - * link down, it sets the speed to 0. - * This is done in spite of the fact that the e100 driver reports 0 to be - * compatible with MVT in the future.*/ + /* this if covers only a special case: when the configuration starts + * with link down, it sets the speed to 0. + * This is done in spite of the fact that the e100 driver reports 0 + * to be compatible with MVT in the future. + */ if (slave->link != BOND_LINK_UP) speed = 0; else { switch (slave->speed) { case SPEED_10: - speed = AD_LINK_SPEED_BITMASK_10MBPS; + speed = AD_LINK_SPEED_10MBPS; break; case SPEED_100: - speed = AD_LINK_SPEED_BITMASK_100MBPS; + speed = AD_LINK_SPEED_100MBPS; break; case SPEED_1000: - speed = AD_LINK_SPEED_BITMASK_1000MBPS; + speed = AD_LINK_SPEED_1000MBPS; + break; + + case SPEED_2500: + speed = AD_LINK_SPEED_2500MBPS; break; case SPEED_10000: - speed = AD_LINK_SPEED_BITMASK_10000MBPS; + speed = AD_LINK_SPEED_10000MBPS; + break; + + case SPEED_20000: + speed = AD_LINK_SPEED_20000MBPS; + break; + + case SPEED_40000: + speed = AD_LINK_SPEED_40000MBPS; + break; + + case SPEED_56000: + speed = AD_LINK_SPEED_56000MBPS; break; default: - speed = 0; // unknown speed value from ethtool. shouldn't happen + /* unknown speed value from ethtool. shouldn't happen */ + speed = 0; break; } } - pr_debug("Port %d Received link speed %d update from adapter\n", - port->actor_port_number, speed); + netdev_dbg(slave->bond->dev, "Port %d Received link speed %d update from adapter\n", + port->actor_port_number, speed); return speed; } @@ -362,43 +361,30 @@ static u8 __get_duplex(struct port *port) { struct slave *slave = port->slave; + u8 retval = 0x0; - u8 retval; - - // handling a special case: when the configuration starts with - // link down, it sets the duplex to 0. - if (slave->link != BOND_LINK_UP) - retval = 0x0; - else { + /* handling a special case: when the configuration starts with + * link down, it sets the duplex to 0. + */ + if (slave->link == BOND_LINK_UP) { switch (slave->duplex) { case DUPLEX_FULL: retval = 0x1; - pr_debug("Port %d Received status full duplex update from adapter\n", - port->actor_port_number); + netdev_dbg(slave->bond->dev, "Port %d Received status full duplex update from adapter\n", + port->actor_port_number); break; case DUPLEX_HALF: default: retval = 0x0; - pr_debug("Port %d Received status NOT full duplex update from adapter\n", - port->actor_port_number); + netdev_dbg(slave->bond->dev, "Port %d Received status NOT full duplex update from adapter\n", + port->actor_port_number); break; } } return retval; } -/** - * __initialize_port_locks - initialize a port's STATE machine spinlock - * @port: the slave of the port we're looking at - * - */ -static inline void __initialize_port_locks(struct slave *slave) -{ - // make sure it isn't called twice - spin_lock_init(&(SLAVE_AD_INFO(slave).state_machine_lock)); -} - -//conversions +/* Conversions */ /** * __ad_timer_to_ticks - convert a given timer type to AD module ticks @@ -407,39 +393,38 @@ * * If @timer_type is %current_while_timer, @par indicates long/short timer. * If @timer_type is %periodic_timer, @par is one of %FAST_PERIODIC_TIME, - * %SLOW_PERIODIC_TIME. + * %SLOW_PERIODIC_TIME. */ static u16 __ad_timer_to_ticks(u16 timer_type, u16 par) { u16 retval = 0; /* to silence the compiler */ switch (timer_type) { - case AD_CURRENT_WHILE_TIMER: // for rx machine usage + case AD_CURRENT_WHILE_TIMER: /* for rx machine usage */ if (par) - retval = (AD_SHORT_TIMEOUT_TIME*ad_ticks_per_sec); // short timeout + retval = (AD_SHORT_TIMEOUT_TIME*ad_ticks_per_sec); else - retval = (AD_LONG_TIMEOUT_TIME*ad_ticks_per_sec); // long timeout + retval = (AD_LONG_TIMEOUT_TIME*ad_ticks_per_sec); break; - case AD_ACTOR_CHURN_TIMER: // for local churn machine + case AD_ACTOR_CHURN_TIMER: /* for local churn machine */ retval = (AD_CHURN_DETECTION_TIME*ad_ticks_per_sec); break; - case AD_PERIODIC_TIMER: // for periodic machine - retval = (par*ad_ticks_per_sec); // long timeout + case AD_PERIODIC_TIMER: /* for periodic machine */ + retval = (par*ad_ticks_per_sec); /* long timeout */ break; - case AD_PARTNER_CHURN_TIMER: // for remote churn machine + case AD_PARTNER_CHURN_TIMER: /* for remote churn machine */ retval = (AD_CHURN_DETECTION_TIME*ad_ticks_per_sec); break; - case AD_WAIT_WHILE_TIMER: // for selection machine + case AD_WAIT_WHILE_TIMER: /* for selection machine */ retval = (AD_AGGREGATE_WAIT_TIME*ad_ticks_per_sec); break; } + return retval; } -///////////////////////////////////////////////////////////////////////////////// -// ================= ad_rx_machine helper functions ================== -///////////////////////////////////////////////////////////////////////////////// +/* ================= ad_rx_machine helper functions ================== */ /** * __choose_matched - update a port's matched variable from a received lacpdu @@ -466,17 +451,18 @@ */ static void __choose_matched(struct lacpdu *lacpdu, struct port *port) { - // check if all parameters are alike + /* check if all parameters are alike + * or this is individual link(aggregation == FALSE) + * then update the state machine Matched variable. + */ if (((ntohs(lacpdu->partner_port) == port->actor_port_number) && (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) && - !MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) && + 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 & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) || - // or this is individual link(aggregation == FALSE) ((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0) ) { - // update the state machine Matched variable port->sm_vars |= AD_PORT_MATCHED; } else { port->sm_vars &= ~AD_PORT_MATCHED; @@ -498,7 +484,9 @@ struct port_params *partner = &port->partner_oper; __choose_matched(lacpdu, port); - // record the new parameter values for the partner operational + /* record the new parameter values for the partner + * operational + */ partner->port_number = ntohs(lacpdu->actor_port); partner->port_priority = ntohs(lacpdu->actor_port_priority); partner->system = lacpdu->actor_system; @@ -506,15 +494,20 @@ partner->key = ntohs(lacpdu->actor_key); partner->port_state = lacpdu->actor_state; - // set actor_oper_port_state.defaulted to FALSE + /* set actor_oper_port_state.defaulted to FALSE */ port->actor_oper_port_state &= ~AD_STATE_DEFAULTED; - // set the partner sync. to on if the partner is sync. and the port is matched - if ((port->sm_vars & AD_PORT_MATCHED) - && (lacpdu->actor_state & AD_STATE_SYNCHRONIZATION)) + /* set the partner sync. to on if the partner is sync, + * and the port is matched + */ + if ((port->sm_vars & AD_PORT_MATCHED) && + (lacpdu->actor_state & AD_STATE_SYNCHRONIZATION)) { partner->port_state |= AD_STATE_SYNCHRONIZATION; - else + pr_debug("%s partner sync=1\n", port->slave->dev->name); + } else { partner->port_state &= ~AD_STATE_SYNCHRONIZATION; + pr_debug("%s partner sync=0\n", port->slave->dev->name); + } } } @@ -529,11 +522,11 @@ static void __record_default(struct port *port) { if (port) { - // record the partner admin parameters + /* record the partner admin parameters */ memcpy(&port->partner_oper, &port->partner_admin, sizeof(struct port_params)); - // set actor_oper_port_state.defaulted to true + /* set actor_oper_port_state.defaulted to true */ port->actor_oper_port_state |= AD_STATE_DEFAULTED; } } @@ -556,14 +549,15 @@ if (lacpdu && port) { const struct port_params *partner = &port->partner_oper; - // check if any parameter is different + /* check if any parameter is different then + * update the state machine selected variable. + */ if (ntohs(lacpdu->actor_port) != partner->port_number || ntohs(lacpdu->actor_port_priority) != partner->port_priority || - MAC_ADDRESS_COMPARE(&lacpdu->actor_system, &partner->system) || + !MAC_ADDRESS_EQUAL(&lacpdu->actor_system, &partner->system) || ntohs(lacpdu->actor_system_priority) != partner->system_priority || ntohs(lacpdu->actor_key) != partner->key || (lacpdu->actor_state & AD_STATE_AGGREGATION) != (partner->port_state & AD_STATE_AGGREGATION)) { - // update the state machine Selected variable port->sm_vars &= ~AD_PORT_SELECTED; } } @@ -587,15 +581,16 @@ const struct port_params *admin = &port->partner_admin; const struct port_params *oper = &port->partner_oper; - // check if any parameter is different + /* check if any parameter is different then + * update the state machine selected variable. + */ if (admin->port_number != oper->port_number || admin->port_priority != oper->port_priority || - MAC_ADDRESS_COMPARE(&admin->system, &oper->system) || + !MAC_ADDRESS_EQUAL(&admin->system, &oper->system) || admin->system_priority != oper->system_priority || admin->key != oper->key || (admin->port_state & AD_STATE_AGGREGATION) != (oper->port_state & AD_STATE_AGGREGATION)) { - // update the state machine Selected variable port->sm_vars &= ~AD_PORT_SELECTED; } } @@ -615,12 +610,14 @@ */ static void __update_ntt(struct lacpdu *lacpdu, struct port *port) { - // validate lacpdu and port + /* validate lacpdu and port */ if (lacpdu && port) { - // check if any parameter is different + /* check if any parameter is different then + * update the port->ntt. + */ if ((ntohs(lacpdu->partner_port) != port->actor_port_number) || (ntohs(lacpdu->partner_port_priority) != port->actor_port_priority) || - MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) || + !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 & AD_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & AD_STATE_LACP_ACTIVITY)) || @@ -628,43 +625,12 @@ ((lacpdu->partner_state & AD_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION)) || ((lacpdu->partner_state & AD_STATE_AGGREGATION) != (port->actor_oper_port_state & AD_STATE_AGGREGATION)) ) { - port->ntt = true; } } } /** - * __attach_bond_to_agg - * @port: the port we're looking at - * - * Handle the attaching of the port's control parser/multiplexer and the - * aggregator. This function does nothing since the parser/multiplexer of the - * receive and the parser/multiplexer of the aggregator are already combined. - */ -static void __attach_bond_to_agg(struct port *port) -{ - port = NULL; /* just to satisfy the compiler */ - // This function does nothing since the parser/multiplexer of the receive - // and the parser/multiplexer of the aggregator are already combined -} - -/** - * __detach_bond_from_agg - * @port: the port we're looking at - * - * Handle the detaching of the port's control parser/multiplexer from the - * aggregator. This function does nothing since the parser/multiplexer of the - * receive and the parser/multiplexer of the aggregator are already combined. - */ -static void __detach_bond_from_agg(struct port *port) -{ - port = NULL; /* just to satisfy the compiler */ - // This function does nothing since the parser/multiplexer of the receive - // and the parser/multiplexer of the aggregator are already combined -} - -/** * __agg_ports_are_ready - check if all ports in an aggregator are ready * @aggregator: the aggregator we're looking at * @@ -675,7 +641,9 @@ int retval = 1; if (aggregator) { - // scan all ports in this aggregator to verfy if they are all ready + /* scan all ports in this aggregator to verfy if they are + * all ready. + */ for (port = aggregator->lag_ports; port; port = port->next_port_in_aggregator) { @@ -719,23 +687,35 @@ if (aggregator->num_of_ports) { switch (__get_link_speed(aggregator->lag_ports)) { - case AD_LINK_SPEED_BITMASK_1MBPS: + case AD_LINK_SPEED_1MBPS: bandwidth = aggregator->num_of_ports; break; - case AD_LINK_SPEED_BITMASK_10MBPS: + case AD_LINK_SPEED_10MBPS: bandwidth = aggregator->num_of_ports * 10; break; - case AD_LINK_SPEED_BITMASK_100MBPS: + case AD_LINK_SPEED_100MBPS: bandwidth = aggregator->num_of_ports * 100; break; - case AD_LINK_SPEED_BITMASK_1000MBPS: + case AD_LINK_SPEED_1000MBPS: bandwidth = aggregator->num_of_ports * 1000; break; - case AD_LINK_SPEED_BITMASK_10000MBPS: + case AD_LINK_SPEED_2500MBPS: + bandwidth = aggregator->num_of_ports * 2500; + break; + case AD_LINK_SPEED_10000MBPS: bandwidth = aggregator->num_of_ports * 10000; break; + case AD_LINK_SPEED_20000MBPS: + bandwidth = aggregator->num_of_ports * 20000; + break; + case AD_LINK_SPEED_40000MBPS: + bandwidth = aggregator->num_of_ports * 40000; + break; + case AD_LINK_SPEED_56000MBPS: + bandwidth = aggregator->num_of_ports * 56000; + break; default: - bandwidth = 0; /*to silence the compiler ....*/ + bandwidth = 0; /* to silence the compiler */ } } return bandwidth; @@ -745,33 +725,32 @@ * __get_active_agg - get the current active aggregator * @aggregator: the aggregator we're looking at * + * Caller must hold RCU lock. */ static struct aggregator *__get_active_agg(struct aggregator *aggregator) { - struct aggregator *retval = NULL; + struct bonding *bond = aggregator->slave->bond; + struct list_head *iter; + struct slave *slave; - for (; aggregator; aggregator = __get_next_agg(aggregator)) { - if (aggregator->is_active) { - retval = aggregator; - break; - } - } + bond_for_each_slave_rcu(bond, slave, iter) + if (SLAVE_AD_INFO(slave)->aggregator.is_active) + return &(SLAVE_AD_INFO(slave)->aggregator); - return retval; + return NULL; } /** * __update_lacpdu_from_port - update a port's lacpdu fields * @port: the port we're looking at - * */ static inline void __update_lacpdu_from_port(struct port *port) { struct lacpdu *lacpdu = &port->lacpdu; const struct port_params *partner = &port->partner_oper; - /* update current actual Actor parameters */ - /* lacpdu->subtype initialized + /* update current actual Actor parameters + * lacpdu->subtype initialized * lacpdu->version_number initialized * lacpdu->tlv_type_actor_info initialized * lacpdu->actor_information_length initialized @@ -783,6 +762,8 @@ 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; + pr_debug("update lacpdu: %s, actor port state %x\n", + port->slave->dev->name, port->actor_oper_port_state); /* lacpdu->reserved_3_1 initialized * lacpdu->tlv_type_partner_info initialized @@ -807,9 +788,7 @@ */ } -////////////////////////////////////////////////////////////////////////////////////// -// ================= main 802.3ad protocol code ====================================== -////////////////////////////////////////////////////////////////////////////////////// +/* ================= main 802.3ad protocol code ========================= */ /** * ad_lacpdu_send - send out a lacpdu packet on a given port @@ -837,13 +816,14 @@ lacpdu_header = (struct lacpdu_header *)skb_put(skb, length); - memcpy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr, ETH_ALEN); + ether_addr_copy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr); /* Note: source address is set to be the member's PERMANENT address, - because we use it to identify loopback lacpdus in receive. */ - memcpy(lacpdu_header->hdr.h_source, slave->perm_hwaddr, ETH_ALEN); + * because we use it to identify loopback lacpdus in receive. + */ + ether_addr_copy(lacpdu_header->hdr.h_source, slave->perm_hwaddr); lacpdu_header->hdr.h_proto = PKT_TYPE_LACPDU; - lacpdu_header->lacpdu = port->lacpdu; // struct copy + lacpdu_header->lacpdu = port->lacpdu; dev_queue_xmit(skb); @@ -878,13 +858,14 @@ marker_header = (struct bond_marker_header *)skb_put(skb, length); - memcpy(marker_header->hdr.h_dest, lacpdu_mcast_addr, ETH_ALEN); + ether_addr_copy(marker_header->hdr.h_dest, lacpdu_mcast_addr); /* Note: source address is set to be the member's PERMANENT address, - because we use it to identify loopback MARKERs in receive. */ - memcpy(marker_header->hdr.h_source, slave->perm_hwaddr, ETH_ALEN); + * because we use it to identify loopback MARKERs in receive. + */ + ether_addr_copy(marker_header->hdr.h_source, slave->perm_hwaddr); marker_header->hdr.h_proto = PKT_TYPE_LACPDU; - marker_header->marker = *marker; // struct copy + marker_header->marker = *marker; dev_queue_xmit(skb); @@ -894,72 +875,97 @@ /** * ad_mux_machine - handle a port's mux state machine * @port: the port we're looking at - * + * @update_slave_arr: Does slave array need update? */ -static void ad_mux_machine(struct port *port) +static void ad_mux_machine(struct port *port, bool *update_slave_arr) { mux_states_t last_state; - // keep current State Machine state to compare later if it was changed + /* 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; // next state + 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; // next state + 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)) { // if UNSELECTED + /* 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 check all ports READY_N value - // every callback cycle to update ready variable, we check READY_N and update READY here + /* in order to withhold the Selection Logic to + * check all ports READY_N value every callback + * cycle to update ready variable, we check + * READY_N and update READY here + */ __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); - port->sm_mux_state = AD_MUX_DETACHED; // next state + port->sm_mux_state = AD_MUX_DETACHED; break; } - // check if the wait_while_timer expired + /* 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 all ports READY_N value - // every callback cycle to update ready variable, we check READY_N and update READY here + /* in order to withhold the selection logic to check + * all ports READY_N value every callback cycle to + * update ready variable, we check READY_N and update + * READY here + */ __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); - // if the wait_while_timer expired, and the port is in READY state, move to ATTACHED state + /* if the wait_while_timer expired, and the port is + * in READY state, move to ATTACHED state + */ if ((port->sm_vars & AD_PORT_READY) && !port->sm_mux_timer_counter) - port->sm_mux_state = AD_MUX_ATTACHED; // next state + port->sm_mux_state = AD_MUX_ATTACHED; break; case AD_MUX_ATTACHED: - // check also if agg_select_timer expired(so the edable port will take place only after this timer) - if ((port->sm_vars & AD_PORT_SELECTED) && (port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) && !__check_agg_selection_timer(port)) { - port->sm_mux_state = AD_MUX_COLLECTING_DISTRIBUTING;// next state - } else if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY)) { // if UNSELECTED or STANDBY + /* check also if agg_select_timer expired (so the + * edable port will take place only after this timer) + */ + if ((port->sm_vars & AD_PORT_SELECTED) && + (port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) && + !__check_agg_selection_timer(port)) { + if (port->aggregator->is_active) + port->sm_mux_state = + AD_MUX_COLLECTING_DISTRIBUTING; + } else if (!(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 check all ports READY_N value - // every callback cycle to update ready variable, we check READY_N and update READY here + /* in order to withhold the selection logic to + * check all ports READY_N value every callback + * cycle to update ready variable, we check + * READY_N and update READY here + */ __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); - port->sm_mux_state = AD_MUX_DETACHED;// next state + port->sm_mux_state = AD_MUX_DETACHED; + } else if (port->aggregator->is_active) { + port->actor_oper_port_state |= + AD_STATE_SYNCHRONIZATION; } break; case AD_MUX_COLLECTING_DISTRIBUTING: - if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY) || - !(port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) - ) { - port->sm_mux_state = AD_MUX_ATTACHED;// next state - + if (!(port->sm_vars & AD_PORT_SELECTED) || + (port->sm_vars & AD_PORT_STANDBY) || + !(port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) || + !(port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION)) { + port->sm_mux_state = AD_MUX_ATTACHED; } else { - // if port state hasn't changed make - // sure that a collecting distributing - // port in an active aggregator is enabled + /* if port state hasn't changed make + * sure that a collecting distributing + * port in an active aggregator is enabled + */ if (port->aggregator && port->aggregator->is_active && !__port_is_enabled(port)) { @@ -968,21 +974,23 @@ } } break; - default: //to silence the compiler + default: break; } } - // check if the state machine was changed + /* check if the state machine was changed */ if (port->sm_mux_state != last_state) { - pr_debug("Mux Machine: Port=%d, Last State=%d, Curr State=%d\n", - port->actor_port_number, last_state, + pr_debug("Mux Machine: Port=%d (%s), Last State=%d, Curr State=%d\n", + port->actor_port_number, + port->slave->dev->name, + last_state, port->sm_mux_state); switch (port->sm_mux_state) { case AD_MUX_DETACHED: - __detach_bond_from_agg(port); port->actor_oper_port_state &= ~AD_STATE_SYNCHRONIZATION; - ad_disable_collecting_distributing(port); + ad_disable_collecting_distributing(port, + update_slave_arr); port->actor_oper_port_state &= ~AD_STATE_COLLECTING; port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING; port->ntt = true; @@ -991,20 +999,48 @@ port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0); break; case AD_MUX_ATTACHED: - __attach_bond_to_agg(port); - port->actor_oper_port_state |= AD_STATE_SYNCHRONIZATION; + if (port->aggregator->is_active) + port->actor_oper_port_state |= + AD_STATE_SYNCHRONIZATION; + else + port->actor_oper_port_state &= + ~AD_STATE_SYNCHRONIZATION; port->actor_oper_port_state &= ~AD_STATE_COLLECTING; port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING; - ad_disable_collecting_distributing(port); + ad_disable_collecting_distributing(port, + update_slave_arr); port->ntt = true; + + /* Send a notificaton about change in state of this + * port. We only want to handle case where port moves + * from AD_MUX_COLLECTING_DISTRIBUTING -> + * AD_MUX_ATTACHED. + */ + if (bond_slave_is_up(port->slave) && + (last_state == AD_MUX_COLLECTING_DISTRIBUTING)) { + struct bond_cb *lag_cb_main; + + rcu_read_lock(); + lag_cb_main = rcu_dereference(bond_cb); + if (lag_cb_main && + lag_cb_main->bond_cb_link_down) { + struct net_device *dev; + + dev = port->slave->dev; + lag_cb_main->bond_cb_link_down(dev); + } + rcu_read_unlock(); + } break; case AD_MUX_COLLECTING_DISTRIBUTING: port->actor_oper_port_state |= AD_STATE_COLLECTING; port->actor_oper_port_state |= AD_STATE_DISTRIBUTING; - ad_enable_collecting_distributing(port); + port->actor_oper_port_state |= AD_STATE_SYNCHRONIZATION; + ad_enable_collecting_distributing(port, + update_slave_arr); port->ntt = true; break; - default: //to silence the compiler + default: break; } } @@ -1023,66 +1059,75 @@ { rx_states_t last_state; - // keep current State Machine state to compare later if it was changed + /* keep current State Machine state to compare later if it was + * changed + */ last_state = port->sm_rx_state; - // check if state machine should change state - // first, check if port was reinitialized - if (port->sm_vars & AD_PORT_BEGIN) - /* next state */ + /* 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; - // check if port is not enabled - else if (!(port->sm_vars & AD_PORT_BEGIN) + port->sm_vars |= AD_PORT_CHURNED; + /* check if port is not enabled */ + } else if (!(port->sm_vars & AD_PORT_BEGIN) && !port->is_enabled && !(port->sm_vars & AD_PORT_MOVED)) - /* next state */ port->sm_rx_state = AD_RX_PORT_DISABLED; - // check if new lacpdu arrived - else if (lacpdu && ((port->sm_rx_state == AD_RX_EXPIRED) || (port->sm_rx_state == AD_RX_DEFAULTED) || (port->sm_rx_state == AD_RX_CURRENT))) { - port->sm_rx_timer_counter = 0; // zero timer + /* check if new lacpdu arrived */ + else if (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)) { + /* 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; // next state + port->sm_rx_state = AD_RX_DEFAULTED; break; case AD_RX_CURRENT: - port->sm_rx_state = AD_RX_EXPIRED; // next state + port->sm_rx_state = AD_RX_EXPIRED; break; - default: //to silence the compiler + default: break; } } else { - // if no lacpdu arrived and no timer is on + /* if no lacpdu arrived and no timer is on */ switch (port->sm_rx_state) { case AD_RX_PORT_DISABLED: if (port->sm_vars & AD_PORT_MOVED) - port->sm_rx_state = AD_RX_INITIALIZE; // next state + port->sm_rx_state = AD_RX_INITIALIZE; else if (port->is_enabled && (port->sm_vars & AD_PORT_LACP_ENABLED)) - port->sm_rx_state = AD_RX_EXPIRED; // next state + port->sm_rx_state = AD_RX_EXPIRED; else if (port->is_enabled && ((port->sm_vars & AD_PORT_LACP_ENABLED) == 0)) - port->sm_rx_state = AD_RX_LACP_DISABLED; // next state + port->sm_rx_state = AD_RX_LACP_DISABLED; break; - default: //to silence the compiler + default: break; } } } - // check if the State machine was changed or new lacpdu arrived + /* check if the State machine was changed or new lacpdu arrived */ if ((port->sm_rx_state != last_state) || (lacpdu)) { - pr_debug("Rx Machine: Port=%d, Last State=%d, Curr State=%d\n", - port->actor_port_number, last_state, + pr_debug("Rx Machine: Port=%d (%s), Last State=%d, Curr State=%d\n", + port->actor_port_number, + port->slave->dev->name, + last_state, port->sm_rx_state); switch (port->sm_rx_state) { case AD_RX_INITIALIZE: - if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_BITS)) + 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; @@ -1090,10 +1135,9 @@ __record_default(port); port->actor_oper_port_state &= ~AD_STATE_EXPIRED; port->sm_vars &= ~AD_PORT_MOVED; - port->sm_rx_state = AD_RX_PORT_DISABLED; // next state - - /*- Fall Through -*/ + port->sm_rx_state = AD_RX_PORT_DISABLED; + /* Fall Through */ case AD_RX_PORT_DISABLED: port->sm_vars &= ~AD_PORT_MATCHED; break; @@ -1105,15 +1149,19 @@ port->actor_oper_port_state &= ~AD_STATE_EXPIRED; break; case AD_RX_EXPIRED: - //Reset of the Synchronization flag. (Standard 43.4.12) - //This reset cause to disable this port in the COLLECTING_DISTRIBUTING state of the - //mux machine in case of EXPIRED even if LINK_DOWN didn't arrive for the port. + /* Reset of the Synchronization flag (Standard 43.4.12) + * This reset cause to disable this port in the + * COLLECTING_DISTRIBUTING state of the mux machine in + * case of EXPIRED even if LINK_DOWN didn't arrive for + * the port. + */ port->partner_oper.port_state &= ~AD_STATE_SYNCHRONIZATION; port->sm_vars &= ~AD_PORT_MATCHED; - port->partner_oper.port_state |= - AD_STATE_LACP_ACTIVITY; + port->partner_oper.port_state |= AD_STATE_LACP_TIMEOUT; + port->partner_oper.port_state |= AD_STATE_LACP_ACTIVITY; port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(AD_SHORT_TIMEOUT)); port->actor_oper_port_state |= AD_STATE_EXPIRED; + port->sm_vars |= AD_PORT_CHURNED; break; case AD_RX_DEFAULTED: __update_default_selected(port); @@ -1122,12 +1170,12 @@ port->actor_oper_port_state &= ~AD_STATE_EXPIRED; break; case AD_RX_CURRENT: - // detect loopback situation - if (!MAC_ADDRESS_COMPARE(&(lacpdu->actor_system), &(port->actor_system))) { - // INFO_RECEIVED_LOOPBACK_FRAMES - pr_err("%s: An illegal loopback occurred on adapter (%s).\n" + /* detect loopback situation */ + if (MAC_ADDRESS_EQUAL(&(lacpdu->actor_system), + &(port->actor_system))) { + netdev_err(port->slave->bond->dev, "An illegal loopback occurred on adapter (%s)\n" "Check the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n", - port->slave->bond->dev->name, port->slave->dev->name); + port->slave->dev->name); return; } __update_selected(lacpdu, port); @@ -1136,22 +1184,62 @@ port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT)); port->actor_oper_port_state &= ~AD_STATE_EXPIRED; break; - default: //to silence the compiler + default: break; } } } /** - * ad_tx_machine - handle a port's tx state machine + * ad_churn_machine - handle port churn's state machine * @port: the port we're looking at * */ +static void ad_churn_machine(struct port *port) +{ + if (port->sm_vars & AD_PORT_CHURNED) { + port->sm_vars &= ~AD_PORT_CHURNED; + port->sm_churn_actor_state = AD_CHURN_MONITOR; + port->sm_churn_partner_state = AD_CHURN_MONITOR; + port->sm_churn_actor_timer_counter = + __ad_timer_to_ticks(AD_ACTOR_CHURN_TIMER, 0); + port->sm_churn_partner_timer_counter = + __ad_timer_to_ticks(AD_PARTNER_CHURN_TIMER, 0); + return; + } + if (port->sm_churn_actor_timer_counter && + !(--port->sm_churn_actor_timer_counter) && + port->sm_churn_actor_state == AD_CHURN_MONITOR) { + if (port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION) { + port->sm_churn_actor_state = AD_NO_CHURN; + } else { + port->churn_actor_count++; + port->sm_churn_actor_state = AD_CHURN; + } + } + if (port->sm_churn_partner_timer_counter && + !(--port->sm_churn_partner_timer_counter) && + port->sm_churn_partner_state == AD_CHURN_MONITOR) { + if (port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) { + port->sm_churn_partner_state = AD_NO_CHURN; + } else { + port->churn_partner_count++; + port->sm_churn_partner_state = AD_CHURN; + } + } +} + +/** + * ad_tx_machine - handle a port's tx state machine + * @port: the port we're looking at + */ static void ad_tx_machine(struct port *port) { - // check if tx timer expired, to verify that we do not send more than 3 packets per second + /* check if tx timer expired, to verify that we do not send more than + * 3 packets per second + */ if (port->sm_tx_timer_counter && !(--port->sm_tx_timer_counter)) { - // check if there is something to send + /* check if there is something to send */ if (port->ntt && (port->sm_vars & AD_PORT_LACP_ENABLED)) { __update_lacpdu_from_port(port); @@ -1159,14 +1247,16 @@ pr_debug("Sent LACPDU on port %d\n", port->actor_port_number); - /* mark ntt as false, so it will not be sent again until - demanded */ + /* mark ntt as false, so it will not be sent + * again until demanded + */ port->ntt = false; } } - // restart tx timer(to verify that we will not exceed AD_MAX_TX_IN_SECOND - port->sm_tx_timer_counter = - ad_ticks_per_sec/AD_MAX_TX_IN_SECOND; + /* restart tx timer(to verify that we will not exceed + * AD_MAX_TX_IN_SECOND + */ + port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND; } } @@ -1180,76 +1270,79 @@ { periodic_states_t last_state; - // keep current state machine state to compare later if it was changed + /* keep current state machine state to compare later if it was changed */ last_state = port->sm_periodic_state; - // check if port was reinitialized + /* 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 & AD_STATE_LACP_ACTIVITY) && !(port->partner_oper.port_state & AD_STATE_LACP_ACTIVITY)) ) { - port->sm_periodic_state = AD_NO_PERIODIC; // next state + port->sm_periodic_state = AD_NO_PERIODIC; } - // check if state machine should change state + /* check if state machine should change state */ else if (port->sm_periodic_timer_counter) { - // check if periodic state machine expired + /* check if periodic state machine expired */ if (!(--port->sm_periodic_timer_counter)) { - // if expired then do tx - port->sm_periodic_state = AD_PERIODIC_TX; // next state + /* if expired then do tx */ + port->sm_periodic_state = AD_PERIODIC_TX; } else { - // If not expired, check if there is some new timeout parameter from the partner state + /* If not expired, check if there is some new timeout + * parameter from the partner state + */ switch (port->sm_periodic_state) { case AD_FAST_PERIODIC: if (!(port->partner_oper.port_state & AD_STATE_LACP_TIMEOUT)) - port->sm_periodic_state = AD_SLOW_PERIODIC; // next state + port->sm_periodic_state = AD_SLOW_PERIODIC; break; case AD_SLOW_PERIODIC: if ((port->partner_oper.port_state & AD_STATE_LACP_TIMEOUT)) { - // stop current timer port->sm_periodic_timer_counter = 0; - port->sm_periodic_state = AD_PERIODIC_TX; // next state + port->sm_periodic_state = AD_PERIODIC_TX; } break; - default: //to silence the compiler + default: break; } } } else { switch (port->sm_periodic_state) { case AD_NO_PERIODIC: - port->sm_periodic_state = AD_FAST_PERIODIC; // next state + port->sm_periodic_state = AD_FAST_PERIODIC; break; case AD_PERIODIC_TX: - if (!(port->partner_oper.port_state - & AD_STATE_LACP_TIMEOUT)) - port->sm_periodic_state = AD_SLOW_PERIODIC; // next state + if (!(port->partner_oper.port_state & + AD_STATE_LACP_TIMEOUT)) + port->sm_periodic_state = AD_SLOW_PERIODIC; else - port->sm_periodic_state = AD_FAST_PERIODIC; // next state + port->sm_periodic_state = AD_FAST_PERIODIC; break; - default: //to silence the compiler + default: break; } } - // check if the state machine was changed + /* check if the state machine was changed */ if (port->sm_periodic_state != last_state) { pr_debug("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; // zero timer + port->sm_periodic_timer_counter = 0; break; case AD_FAST_PERIODIC: - port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_FAST_PERIODIC_TIME))-1; // decrement 1 tick we lost in the PERIODIC_TX cycle + /* 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: - port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_SLOW_PERIODIC_TIME))-1; // decrement 1 tick we lost in the PERIODIC_TX cycle + /* 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: //to silence the compiler + default: break; } } @@ -1258,116 +1351,140 @@ /** * ad_port_selection_logic - select aggregation groups * @port: the port we're looking at + * @update_slave_arr: Does slave array need update? * * Select aggregation groups, and assign each port for it's aggregetor. The * selection logic is called in the inititalization (after all the handshkes), * and after every lacpdu receive (if selected is off). */ -static void ad_port_selection_logic(struct port *port) +static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) { struct aggregator *aggregator, *free_aggregator = NULL, *temp_aggregator; struct port *last_port = NULL, *curr_port; + struct list_head *iter; + struct bonding *bond; + struct slave *slave; int found = 0; - // if the port is already Selected, do nothing + /* if the port is already Selected, do nothing */ if (port->sm_vars & AD_PORT_SELECTED) return; - // if the port is connected to other aggregator, detach it + 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 + /* 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) { + curr_port = curr_port->next_port_in_aggregator) { if (curr_port == port) { temp_aggregator->num_of_ports--; - if (!last_port) {// if it is the first port attached to the aggregator + /* 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 + } 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 + /* clear the port's relations to this + * aggregator + */ port->aggregator = NULL; port->next_port_in_aggregator = NULL; port->actor_port_aggregator_identifier = 0; - pr_debug("Port %d left LAG %d\n", - port->actor_port_number, - temp_aggregator->aggregator_identifier); - // if the aggregator is empty, clear its parameters, and set it ready to be attached + netdev_dbg(bond->dev, "Port %d left LAG %d\n", + port->actor_port_number, + temp_aggregator->aggregator_identifier); + /* if the aggregator is empty, clear its + * parameters, and set it ready to be attached + */ if (!temp_aggregator->lag_ports) ad_clear_agg(temp_aggregator); break; } } - if (!curr_port) { // meaning: the port was related to an aggregator but was not on the aggregator port list - pr_warning("%s: Warning: Port %d (on %s) was related to aggregator %d but was not on its port list\n", - port->slave->bond->dev->name, - port->actor_port_number, - port->slave->dev->name, - port->aggregator->aggregator_identifier); - } - } - // search on all aggregators for a suitable aggregator for this port - for (aggregator = __get_first_agg(port); aggregator; - aggregator = __get_next_agg(aggregator)) { + if (!curr_port) { + /* meaning: the port was related to an aggregator + * but was not on the aggregator port list + */ + net_warn_ratelimited("%s: Warning: Port %d (on %s) was related to aggregator %d but was not on its port list\n", + port->slave->bond->dev->name, + port->actor_port_number, + port->slave->dev->name, + 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) + /* 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_COMPARE(&(aggregator->partner_system), &(port->partner_oper.system)) && + /* 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) ) && - ((MAC_ADDRESS_COMPARE(&(port->partner_oper.system), &(null_mac_addr)) && // partner answers - !aggregator->is_individual) // but is not individual OR + ((!MAC_ADDRESS_EQUAL(&(port->partner_oper.system), &(null_mac_addr)) && /* partner answers */ + !aggregator->is_individual) /* but is not individual OR */ ) ) { - // attach to the founded aggregator + /* 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; - pr_debug("Port %d joined LAG %d(existing LAG)\n", - port->actor_port_number, - port->aggregator->aggregator_identifier); + netdev_dbg(bond->dev, "Port %d joined LAG %d(existing LAG)\n", + port->actor_port_number, + port->aggregator->aggregator_identifier); - // mark this port as selected + /* 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 + /* the port couldn't find an aggregator - attach it to a new + * aggregator + */ if (!found) { if (free_aggregator) { - // assign port a new 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 - // if port was responsed from the end-user - if (port->actor_oper_port_key & AD_DUPLEX_KEY_BITS) + /* update the new aggregator's parameters + * if port was responsed from the end-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; - port->aggregator->actor_admin_aggregator_key = port->actor_admin_port_key; - port->aggregator->actor_oper_aggregator_key = port->actor_oper_port_key; + port->aggregator->actor_admin_aggregator_key = + port->actor_admin_port_key; + port->aggregator->actor_oper_aggregator_key = + port->actor_oper_port_key; port->aggregator->partner_system = port->partner_oper.system; port->aggregator->partner_system_priority = @@ -1378,35 +1495,38 @@ port->aggregator->lag_ports = port; port->aggregator->num_of_ports++; - // mark this port as selected + /* mark this port as selected */ port->sm_vars |= AD_PORT_SELECTED; - pr_debug("Port %d joined LAG %d(new LAG)\n", - port->actor_port_number, - port->aggregator->aggregator_identifier); + netdev_dbg(bond->dev, "Port %d joined LAG %d(new LAG)\n", + port->actor_port_number, + port->aggregator->aggregator_identifier); } else { - pr_err("%s: Port %d (on %s) did not find a suitable aggregator\n", - port->slave->bond->dev->name, + netdev_err(bond->dev, "Port %d (on %s) did not find a suitable aggregator\n", port->actor_port_number, port->slave->dev->name); } } - // if all aggregator's ports are READY_N == TRUE, set ready=TRUE in all aggregator's ports - // else set ready=FALSE in all aggregator's ports - __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); + /* if all aggregator's ports are READY_N == TRUE, set ready=TRUE + * in all aggregator's ports, else set ready=FALSE in all + * aggregator's ports + */ + __set_agg_ports_ready(port->aggregator, + __agg_ports_are_ready(port->aggregator)); aggregator = __get_first_agg(port); - ad_agg_selection_logic(aggregator); + ad_agg_selection_logic(aggregator, update_slave_arr); + + if (!port->aggregator->is_active) + port->actor_oper_port_state &= ~AD_STATE_SYNCHRONIZATION; } -/* - * Decide if "agg" is a better choice for the new active aggregator that +/* Decide if "agg" is a better choice for the new active aggregator that * the current best, according to the ad_select policy. */ static struct aggregator *ad_agg_selection_test(struct aggregator *best, struct aggregator *curr) { - /* - * 0. If no best, select current. + /* 0. If no best, select current. * * 1. If the current agg is not individual, and the best is * individual, select current. @@ -1462,9 +1582,9 @@ break; default: - pr_warning("%s: Impossible agg select mode %d\n", - curr->slave->bond->dev->name, - __get_agg_selection_mode(curr->lag_ports)); + net_warn_ratelimited("%s: Impossible agg select mode %d\n", + curr->slave->bond->dev->name, + __get_agg_selection_mode(curr->lag_ports)); break; } @@ -1474,15 +1594,18 @@ static int agg_device_up(const struct aggregator *agg) { struct port *port = agg->lag_ports; + if (!port) return 0; - return (netif_running(port->slave->dev) && - netif_carrier_ok(port->slave->dev)); + + return netif_running(port->slave->dev) && + netif_carrier_ok(port->slave->dev); } /** * ad_agg_selection_logic - select an aggregation group for a team * @aggregator: the aggregator we're looking at + * @update_slave_arr: Does slave array need update? * * It is assumed that only one aggregator may be selected for a team. * @@ -1505,27 +1628,32 @@ * __get_active_agg() won't work correctly. This function should be better * called with the bond itself, and retrieve the first agg from it. */ -static void ad_agg_selection_logic(struct aggregator *agg) +static void ad_agg_selection_logic(struct aggregator *agg, + bool *update_slave_arr) { struct aggregator *best, *active, *origin; + struct bonding *bond = agg->slave->bond; + struct list_head *iter; + struct slave *slave; struct port *port; + rcu_read_lock(); origin = agg; active = __get_active_agg(agg); best = (active && agg_device_up(active)) ? active : NULL; - do { + bond_for_each_slave_rcu(bond, slave, iter) { + agg = &(SLAVE_AD_INFO(slave)->aggregator); + agg->is_active = 0; if (agg->num_of_ports && agg_device_up(agg)) best = ad_agg_selection_test(best, agg); - - } while ((agg = __get_next_agg(agg))); + } if (best && __get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) { - /* - * For the STABLE policy, don't replace the old active + /* For the STABLE policy, don't replace the old active * aggregator if it's still active (it has an answering * partner) or if both the best and active don't have an * answering partner. @@ -1533,7 +1661,8 @@ if (active && active->lag_ports && active->lag_ports->is_enabled && (__agg_has_partner(active) || - (!__agg_has_partner(active) && !__agg_has_partner(best)))) { + (!__agg_has_partner(active) && + !__agg_has_partner(best)))) { if (!(!active->actor_oper_aggregator_key && best->actor_oper_aggregator_key)) { best = NULL; @@ -1547,53 +1676,57 @@ active->is_active = 1; } - // if there is new best aggregator, activate it + /* if there is new best aggregator, activate it */ if (best) { - pr_debug("best Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", - best->aggregator_identifier, best->num_of_ports, - best->actor_oper_aggregator_key, - best->partner_oper_aggregator_key, - best->is_individual, best->is_active); - pr_debug("best ports %p slave %p %s\n", - best->lag_ports, best->slave, - best->slave ? best->slave->dev->name : "NULL"); - - for (agg = __get_first_agg(best->lag_ports); agg; - agg = __get_next_agg(agg)) { - - pr_debug("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); + netdev_dbg(bond->dev, "best Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", + 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, "best ports %p slave %p %s\n", + best->lag_ports, best->slave, + best->slave ? best->slave->dev->name : "NULL"); + + bond_for_each_slave_rcu(bond, slave, iter) { + agg = &(SLAVE_AD_INFO(slave)->aggregator); + + netdev_dbg(bond->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 replys + /* check if any partner replys */ if (best->is_individual) { - pr_warning("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n", - best->slave ? best->slave->bond->dev->name : "NULL"); + net_warn_ratelimited("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n", + best->slave ? + best->slave->bond->dev->name : "NULL"); } best->is_active = 1; - pr_debug("LAG %d chosen as the active LAG\n", - best->aggregator_identifier); - pr_debug("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", - 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, "LAG %d chosen as the active LAG\n", + best->aggregator_identifier); + netdev_dbg(bond->dev, "Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", + 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 + /* 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 + /* if the selected aggregator is of join individuals * (partner_system is NULL), enable their ports */ active = __get_active_agg(origin); @@ -1607,19 +1740,14 @@ } } - if (origin->slave) { - struct bonding *bond; + rcu_read_unlock(); - bond = bond_get_bond_by_slave(origin->slave); - if (bond) - bond_3ad_set_carrier(bond); - } + bond_3ad_set_carrier(bond); } /** * ad_clear_agg - clear a given aggregator's parameters * @aggregator: the aggregator we're looking at - * */ static void ad_clear_agg(struct aggregator *aggregator) { @@ -1643,7 +1771,6 @@ /** * ad_initialize_agg - initialize a given aggregator's parameters * @aggregator: the aggregator we're looking at - * */ static void ad_initialize_agg(struct aggregator *aggregator) { @@ -1660,7 +1787,6 @@ * ad_initialize_port - initialize a given port's parameters * @aggregator: the aggregator we're looking at * @lacp_fast: boolean. whether fast periodic should be used - * */ static void ad_initialize_port(struct port *port, int lacp_fast) { @@ -1684,16 +1810,13 @@ }; if (port) { - port->actor_port_number = 1; port->actor_port_priority = 0xff; - port->actor_system = null_mac_addr; - port->actor_system_priority = 0xffff; port->actor_port_aggregator_identifier = 0; port->ntt = false; - port->actor_admin_port_key = 1; - port->actor_oper_port_key = 1; - port->actor_admin_port_state = AD_STATE_AGGREGATION | AD_STATE_LACP_ACTIVITY; - port->actor_oper_port_state = AD_STATE_AGGREGATION | AD_STATE_LACP_ACTIVITY; + port->actor_admin_port_state = AD_STATE_AGGREGATION | + AD_STATE_LACP_ACTIVITY; + port->actor_oper_port_state = AD_STATE_AGGREGATION | + AD_STATE_LACP_ACTIVITY; if (lacp_fast) port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT; @@ -1702,8 +1825,8 @@ memcpy(&port->partner_oper, &tmpl, sizeof(tmpl)); port->is_enabled = true; - // ****** private parameters ****** - port->sm_vars = 0x3; + /* private parameters */ + port->sm_vars = AD_PORT_BEGIN | AD_PORT_LACP_ENABLED; port->sm_rx_state = 0; port->sm_rx_timer_counter = 0; port->sm_periodic_state = 0; @@ -1711,12 +1834,17 @@ port->sm_mux_state = 0; port->sm_mux_timer_counter = 0; port->sm_tx_state = 0; - port->sm_tx_timer_counter = 0; - port->slave = NULL; port->aggregator = NULL; port->next_port_in_aggregator = NULL; port->transaction_id = 0; + port->sm_churn_actor_timer_counter = 0; + port->sm_churn_actor_state = 0; + port->churn_actor_count = 0; + port->sm_churn_partner_timer_counter = 0; + port->sm_churn_partner_state = 0; + port->churn_partner_count = 0; + memcpy(&port->lacpdu, &lacpdu, sizeof(lacpdu)); } } @@ -1724,94 +1852,68 @@ /** * ad_enable_collecting_distributing - enable a port's transmit/receive * @port: the port we're looking at + * @update_slave_arr: Does slave array need update? * * Enable @port if it's in an active aggregator */ -static void ad_enable_collecting_distributing(struct port *port) +static void ad_enable_collecting_distributing(struct port *port, + bool *update_slave_arr) { if (port->aggregator->is_active) { + struct bond_cb *lag_cb_main; pr_debug("Enabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier); __enable_port(port); + /* Slave array needs update */ + *update_slave_arr = true; + + rcu_read_lock(); + lag_cb_main = rcu_dereference(bond_cb); + + if (lag_cb_main && lag_cb_main->bond_cb_link_up) + lag_cb_main->bond_cb_link_up(port->slave->dev); + + rcu_read_unlock(); } } /** * ad_disable_collecting_distributing - disable a port's transmit/receive * @port: the port we're looking at - * + * @update_slave_arr: Does slave array need update? */ -static void ad_disable_collecting_distributing(struct port *port) +static void ad_disable_collecting_distributing(struct port *port, + bool *update_slave_arr) { - if (port->aggregator && MAC_ADDRESS_COMPARE(&(port->aggregator->partner_system), &(null_mac_addr))) { + if (port->aggregator && + !MAC_ADDRESS_EQUAL(&(port->aggregator->partner_system), + &(null_mac_addr))) { pr_debug("Disabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier); __disable_port(port); + /* Slave array needs an update */ + *update_slave_arr = true; } } -#if 0 -/** - * ad_marker_info_send - send a marker information frame - * @port: the port we're looking at - * - * This function does nothing since we decided not to implement send and handle - * response for marker PDU's, in this stage, but only to respond to marker - * information. - */ -static void ad_marker_info_send(struct port *port) -{ - struct bond_marker marker; - u16 index; - - // fill the marker PDU with the appropriate values - marker.subtype = 0x02; - marker.version_number = 0x01; - marker.tlv_type = AD_MARKER_INFORMATION_SUBTYPE; - marker.marker_length = 0x16; - // convert requester_port to Big Endian - marker.requester_port = (((port->actor_port_number & 0xFF) << 8) |((u16)(port->actor_port_number & 0xFF00) >> 8)); - marker.requester_system = port->actor_system; - // convert requester_port(u32) to Big Endian - marker.requester_transaction_id = - (((++port->transaction_id & 0xFF) << 24) - | ((port->transaction_id & 0xFF00) << 8) - | ((port->transaction_id & 0xFF0000) >> 8) - | ((port->transaction_id & 0xFF000000) >> 24)); - marker.pad = 0; - marker.tlv_type_terminator = 0x00; - marker.terminator_length = 0x00; - for (index = 0; index < 90; index++) - marker.reserved_90[index] = 0; - - // send the marker information - if (ad_marker_send(port, &marker) >= 0) { - pr_debug("Sent Marker Information on port %d\n", - port->actor_port_number); - } -} -#endif - /** * ad_marker_info_received - handle receive of a Marker information frame * @marker_info: Marker info received * @port: the port we're looking at - * */ static void ad_marker_info_received(struct bond_marker *marker_info, struct port *port) { struct bond_marker marker; - // copy the received marker data to the response marker - //marker = *marker_info; + /* copy the received marker data to the response marker */ memcpy(&marker, marker_info, sizeof(struct bond_marker)); - // change the marker subtype to marker response + /* change the marker subtype to marker response */ marker.tlv_type = AD_MARKER_RESPONSE_SUBTYPE; - // send the marker response + /* send the marker response */ if (ad_marker_send(port, &marker) >= 0) { pr_debug("Sent Marker Response on port %d\n", port->actor_port_number); @@ -1828,22 +1930,19 @@ * information. */ static void ad_marker_response_received(struct bond_marker *marker, - struct port *port) + struct port *port) { - marker = NULL; /* just to satisfy the compiler */ - port = NULL; /* just to satisfy the compiler */ - // DO NOTHING, SINCE WE DECIDED NOT TO IMPLEMENT THIS FEATURE FOR NOW + /* DO NOTHING, SINCE WE DECIDED NOT TO IMPLEMENT THIS FEATURE FOR NOW */ } -////////////////////////////////////////////////////////////////////////////////////// -// ================= AD exported functions to the main bonding code ================== -////////////////////////////////////////////////////////////////////////////////////// +/* ========= AD exported functions to the main bonding code ========= */ -// Check aggregators status in team every T seconds +/* Check aggregators status in team every T seconds */ #define AD_AGGREGATOR_SELECTION_TIMER 8 -/* - * bond_3ad_initiate_agg_selection(struct bonding *bond) +/** + * bond_3ad_initiate_agg_selection - initate aggregator selection + * @bond: bonding struct * * Set the aggregation selection timer, to initiate an agg selection in * the very near future. Called during first initialization, and during @@ -1863,16 +1962,24 @@ */ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution) { - // check that the bond is not initialized yet - if (MAC_ADDRESS_COMPARE(&(BOND_AD_INFO(bond).system.sys_mac_addr), + /* check that the bond is not initialized yet */ + if (!MAC_ADDRESS_EQUAL(&(BOND_AD_INFO(bond).system.sys_mac_addr), bond->dev->dev_addr)) { BOND_AD_INFO(bond).aggregator_identifier = 0; - BOND_AD_INFO(bond).system.sys_priority = 0xFFFF; - BOND_AD_INFO(bond).system.sys_mac_addr = *((struct mac_addr *)bond->dev->dev_addr); + BOND_AD_INFO(bond).system.sys_priority = + bond->params.ad_actor_sys_prio; + if (is_zero_ether_addr(bond->params.ad_actor_system)) + BOND_AD_INFO(bond).system.sys_mac_addr = + *((struct mac_addr *)bond->dev->dev_addr); + else + BOND_AD_INFO(bond).system.sys_mac_addr = + *((struct mac_addr *)bond->params.ad_actor_system); - // initialize how many times this module is called in one second(should be about every 100ms) + /* initialize how many times this module is called in one + * second (should be about every 100ms) + */ ad_ticks_per_sec = tick_resolution; bond_3ad_initiate_agg_selection(bond, @@ -1888,52 +1995,40 @@ * Returns: 0 on success * < 0 on error */ -int bond_3ad_bind_slave(struct slave *slave) +void bond_3ad_bind_slave(struct slave *slave) { struct bonding *bond = bond_get_bond_by_slave(slave); struct port *port; struct aggregator *aggregator; - if (bond == NULL) { - pr_err("%s: The slave %s is not attached to its bond\n", - slave->bond->dev->name, slave->dev->name); - return -1; - } + /* check that the slave has not been initialized yet. */ + if (SLAVE_AD_INFO(slave)->port.slave != slave) { - //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); + /* port initialization */ + port = &(SLAVE_AD_INFO(slave)->port); ad_initialize_port(port, bond->params.lacp_fast); - __initialize_port_locks(slave); port->slave = slave; - port->actor_port_number = SLAVE_AD_INFO(slave).id; - // key is determined according to the link speed, duplex and user key(which is yet not supported) - // ------------------------------------------------------------ - // Port key : | User key | Speed |Duplex| - // ------------------------------------------------------------ - // 16 6 1 0 - port->actor_admin_port_key = 0; // initialize this parameter - port->actor_admin_port_key |= __get_duplex(port); - port->actor_admin_port_key |= (__get_link_speed(port) << 1); - port->actor_oper_port_key = port->actor_admin_port_key; - // if the port is not full duplex, then the port should be not lacp Enabled - if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_BITS)) - port->sm_vars &= ~AD_PORT_LACP_ENABLED; - // actor system is the bond's system + port->actor_port_number = SLAVE_AD_INFO(slave)->id; + /* key is determined according to the link speed, duplex and + * user key + */ + 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 */ port->actor_system = BOND_AD_INFO(bond).system.sys_mac_addr; - // tx timer(to verify that no more than MAX_TX_IN_SECOND lacpdu's are sent in one second) + port->actor_system_priority = + BOND_AD_INFO(bond).system.sys_priority; + /* tx timer(to verify that no more than MAX_TX_IN_SECOND + * lacpdu's are sent in one second) + */ port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND; - port->aggregator = NULL; - port->next_port_in_aggregator = NULL; __disable_port(port); - // aggregator initialization - aggregator = &(SLAVE_AD_INFO(slave).aggregator); + /* aggregator initialization */ + aggregator = &(SLAVE_AD_INFO(slave)->aggregator); ad_initialize_agg(aggregator); @@ -1943,8 +2038,6 @@ aggregator->is_active = 0; aggregator->num_of_ports = 0; } - - return 0; } /** @@ -1960,54 +2053,66 @@ 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); - // find the aggregator related to this slave - aggregator = &(SLAVE_AD_INFO(slave).aggregator); - - // find the port related to this slave - port = &(SLAVE_AD_INFO(slave).port); - - // if slave is null, the whole port is not initialized + /* if slave is null, the whole port is not initialized */ if (!port->slave) { - pr_warning("Warning: %s: Trying to unbind an uninitialized port on %s\n", - slave->bond->dev->name, slave->dev->name); - return; + netdev_warn(bond->dev, "Trying to unbind an uninitialized port on %s\n", + slave->dev->name); + goto out; } - pr_debug("Unbinding Link Aggregation Group %d\n", - aggregator->aggregator_identifier); + netdev_dbg(bond->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 &= ~AD_STATE_AGGREGATION; __update_lacpdu_from_port(port); ad_lacpdu_send(port); - // check if this aggregator is occupied + /* check if this aggregator is occupied */ if (aggregator->lag_ports) { - // check if there are other ports related to this aggregator except - // the port related to this slave(thats ensure us that there is a - // reason to search for new aggregator, and that we will find one - if ((aggregator->lag_ports != port) || (aggregator->lag_ports->next_port_in_aggregator)) { - // find new aggregator for the related port(s) - new_aggregator = __get_first_agg(port); - for (; new_aggregator; new_aggregator = __get_next_agg(new_aggregator)) { - // if the new aggregator is empty, or it is connected to our port only - if (!new_aggregator->lag_ports - || ((new_aggregator->lag_ports == port) - && !new_aggregator->lag_ports->next_port_in_aggregator)) + /* check if there are other ports related to this aggregator + * except the port related to this slave(thats ensure us that + * there is a reason to search for new aggregator, and that we + * will find one + */ + 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 + * connected to our port only + */ + if (!new_aggregator->lag_ports || + ((new_aggregator->lag_ports == port) && + !new_aggregator->lag_ports->next_port_in_aggregator)) break; } - // if new aggregator found, copy the aggregator's parameters - // and connect the related lag_ports to the new aggregator + if (!slave_iter) + new_aggregator = NULL; + + /* if new aggregator found, copy the aggregator's + * parameters and connect the related lag_ports to the + * new aggregator + */ if ((new_aggregator) && ((!new_aggregator->lag_ports) || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator))) { - pr_debug("Some port(s) related to LAG %d - replaceing with LAG %d\n", - aggregator->aggregator_identifier, - new_aggregator->aggregator_identifier); - - if ((new_aggregator->lag_ports == port) && new_aggregator->is_active) { - pr_info("%s: Removing an active aggregator\n", - aggregator->slave->bond->dev->name); - // select new active aggregator + netdev_dbg(bond->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) { + netdev_info(bond->dev, "Removing an active aggregator\n"); select_new_active_agg = 1; } @@ -2023,45 +2128,54 @@ new_aggregator->is_active = aggregator->is_active; new_aggregator->num_of_ports = aggregator->num_of_ports; - // update the information that is written on the ports about the aggregator + /* update the information that is written on + * the ports about the aggregator + */ 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; } - // clear the aggregator ad_clear_agg(aggregator); if (select_new_active_agg) - ad_agg_selection_logic(__get_first_agg(port)); + ad_agg_selection_logic(__get_first_agg(port), + &dummy_slave_update); } else { - pr_warning("%s: Warning: unbinding aggregator, and could not find a new aggregator for its ports\n", - slave->bond->dev->name); + netdev_warn(bond->dev, "unbinding aggregator, and could not find a new aggregator for its ports\n"); } - } else { // in case that the only port related to this aggregator is the one we want to remove + } else { + /* in case that the only port related to this + * aggregator is the one we want to remove + */ select_new_active_agg = aggregator->is_active; - // clear the aggregator ad_clear_agg(aggregator); if (select_new_active_agg) { - pr_info("%s: Removing an active aggregator\n", - slave->bond->dev->name); - // select new active aggregator - ad_agg_selection_logic(__get_first_agg(port)); + netdev_info(bond->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); } } } - pr_debug("Unbinding port %d\n", port->actor_port_number); - // find the aggregator that this port is connected to - temp_aggregator = __get_first_agg(port); - for (; temp_aggregator; temp_aggregator = __get_next_agg(temp_aggregator)) { + netdev_dbg(bond->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 + /* 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 this aggregator + temp_port = temp_port->next_port_in_aggregator) { + if (temp_port == port) { + /* the aggregator found - detach the port from + * this aggregator + */ if (prev_port) prev_port->next_port_in_aggregator = temp_port->next_port_in_aggregator; else @@ -2069,13 +2183,12 @@ temp_aggregator->num_of_ports--; if (temp_aggregator->num_of_ports == 0) { select_new_active_agg = temp_aggregator->is_active; - // clear the aggregator ad_clear_agg(temp_aggregator); if (select_new_active_agg) { - pr_info("%s: Removing an active aggregator\n", - slave->bond->dev->name); - // select new active aggregator - ad_agg_selection_logic(__get_first_agg(port)); + netdev_info(bond->dev, "Removing an active aggregator\n"); + /* select new active aggregator */ + ad_agg_selection_logic(__get_first_agg(port), + &dummy_slave_update); } } break; @@ -2083,6 +2196,9 @@ } } port->slave = NULL; + +out: + spin_unlock_bh(&bond->mode_lock); } /** @@ -2102,62 +2218,83 @@ { struct bonding *bond = container_of(work, struct bonding, ad_work.work); - struct port *port; struct aggregator *aggregator; + struct list_head *iter; + struct slave *slave; + struct port *port; + bool should_notify_rtnl = BOND_SLAVE_NOTIFY_LATER; + bool update_slave_arr = false; - read_lock(&bond->lock); + /* Lock to protect data accessed by all (e.g., port->sm_vars) and + * against running with bond_3ad_unbind_slave. ad_rx_machine may run + * concurrently due to incoming LACPDU as well. + */ + spin_lock_bh(&bond->mode_lock); + rcu_read_lock(); - //check if there are any slaves - if (bond->slave_cnt == 0) + /* check if there are any slaves */ + if (!bond_has_slaves(bond)) goto re_arm; - // check if agg_select_timer timer after initialize is timed out - if (BOND_AD_INFO(bond).agg_select_timer && !(--BOND_AD_INFO(bond).agg_select_timer)) { - // select the active aggregator for the bond - if ((port = __get_first_port(bond))) { + /* check if agg_select_timer timer after initialize is timed out */ + if (BOND_AD_INFO(bond).agg_select_timer && + !(--BOND_AD_INFO(bond).agg_select_timer)) { + 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) { - pr_warning("%s: Warning: bond's first port is uninitialized\n", - bond->dev->name); + net_warn_ratelimited("%s: Warning: bond's first port is uninitialized\n", + bond->dev->name); goto re_arm; } aggregator = __get_first_agg(port); - ad_agg_selection_logic(aggregator); + ad_agg_selection_logic(aggregator, &update_slave_arr); } bond_3ad_set_carrier(bond); } - // for each port run the state machines - for (port = __get_first_port(bond); port; port = __get_next_port(port)) { + /* for each port run the state machines */ + bond_for_each_slave_rcu(bond, slave, iter) { + port = &(SLAVE_AD_INFO(slave)->port); if (!port->slave) { - pr_warning("%s: Warning: Found an uninitialized port\n", - bond->dev->name); + net_warn_ratelimited("%s: Warning: Found an uninitialized port\n", + bond->dev->name); goto re_arm; } - /* Lock around state machines to protect data accessed - * by all (e.g., port->sm_vars). ad_rx_machine may run - * concurrently due to incoming LACPDU. - */ - __get_state_machine_lock(port); - ad_rx_machine(NULL, port); ad_periodic_machine(port); - ad_port_selection_logic(port); - ad_mux_machine(port); + ad_port_selection_logic(port, &update_slave_arr); + ad_mux_machine(port, &update_slave_arr); ad_tx_machine(port); + ad_churn_machine(port); - // turn off the BEGIN bit, since we already handled it + /* turn off the BEGIN bit, since we already handled it */ if (port->sm_vars & AD_PORT_BEGIN) port->sm_vars &= ~AD_PORT_BEGIN; - - __release_state_machine_lock(port); } re_arm: - queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks); + bond_for_each_slave_rcu(bond, slave, iter) { + if (slave->should_notify) { + should_notify_rtnl = BOND_SLAVE_NOTIFY_NOW; + break; + } + } + rcu_read_unlock(); + spin_unlock_bh(&bond->mode_lock); + + if (update_slave_arr) + bond_slave_arr_work_rearm(bond, 0); - read_unlock(&bond->lock); + if (should_notify_rtnl && rtnl_trylock()) { + bond_slave_state_notify(bond); + rtnl_unlock(); + } + queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks); } /** @@ -2170,52 +2307,57 @@ * received frames (loopback). Since only the payload is given to this * function, it check for loopback. */ -static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u16 length) +static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, + u16 length) { struct port *port; int ret = RX_HANDLER_ANOTHER; if (length >= sizeof(struct lacpdu)) { - port = &(SLAVE_AD_INFO(slave).port); + port = &(SLAVE_AD_INFO(slave)->port); if (!port->slave) { - pr_warning("%s: Warning: port of slave %s is uninitialized\n", - slave->dev->name, slave->bond->dev->name); + 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; - pr_debug("Received LACPDU on port %d\n", - port->actor_port_number); + netdev_dbg(slave->bond->dev, + "Received LACPDU on port %d slave %s\n", + port->actor_port_number, + slave->dev->name); /* Protect against concurrent state machines */ - __get_state_machine_lock(port); + spin_lock(&slave->bond->mode_lock); ad_rx_machine(lacpdu, port); - __release_state_machine_lock(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't use the marker's fields. + /* No need to convert fields to Little Endian since we + * don't use the marker's fields. + */ switch (((struct bond_marker *)lacpdu)->tlv_type) { case AD_MARKER_INFORMATION_SUBTYPE: - pr_debug("Received Marker Information on port %d\n", - port->actor_port_number); + netdev_dbg(slave->bond->dev, "Received Marker Information on port %d\n", + port->actor_port_number); ad_marker_info_received((struct bond_marker *)lacpdu, port); break; case AD_MARKER_RESPONSE_SUBTYPE: - pr_debug("Received Marker Response on port %d\n", - port->actor_port_number); + netdev_dbg(slave->bond->dev, "Received Marker Response on port %d\n", + port->actor_port_number); ad_marker_response_received((struct bond_marker *)lacpdu, port); break; default: - pr_debug("Received an unknown Marker subtype on slot %d\n", - port->actor_port_number); + netdev_dbg(slave->bond->dev, "Received an unknown Marker subtype on slot %d\n", + port->actor_port_number); } } } @@ -2223,59 +2365,78 @@ } /** - * bond_3ad_adapter_speed_changed - handle a slave's speed change indication - * @slave: slave struct to work on + * ad_update_actor_keys - Update the oper / admin keys for a port based on + * its current speed and duplex settings. * - * Handle reselection of aggregator (if needed) for this port. - */ -void bond_3ad_adapter_speed_changed(struct slave *slave) -{ - struct port *port; - - port = &(SLAVE_AD_INFO(slave).port); + * @port: the port we'are looking at + * @reset: Boolean to just reset the speed and the duplex part of the key + * + * The logic to change the oper / admin keys is: + * (a) A full duplex port can participate in LACP with partner. + * (b) When the speed is changed, LACP need to be reinitiated. + */ +static void ad_update_actor_keys(struct port *port, bool reset) +{ + u8 duplex = 0; + u16 ospeed = 0, speed = 0; + u16 old_oper_key = port->actor_oper_port_key; + + port->actor_admin_port_key &= ~(AD_SPEED_KEY_MASKS|AD_DUPLEX_KEY_MASKS); + if (!reset) { + speed = __get_link_speed(port); + ospeed = (old_oper_key & AD_SPEED_KEY_MASKS) >> 1; + duplex = __get_duplex(port); + port->actor_admin_port_key |= (speed << 1) | duplex; + } + port->actor_oper_port_key = port->actor_admin_port_key; + + 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 slave is null, the whole port is not initialized - if (!port->slave) { - pr_warning("Warning: %s: speed changed for uninitialized port on %s\n", - slave->bond->dev->name, slave->dev->name); - return; + if (!reset) { + if (!speed) { + netdev_err(port->slave->dev, + "speed changed to 0 for port %s", + port->slave->dev->name); + } else if (duplex && ospeed != speed) { + /* Speed change restarts LACP state-machine */ + port->sm_vars |= AD_PORT_BEGIN; + } + } } - - port->actor_admin_port_key &= ~AD_SPEED_KEY_BITS; - port->actor_oper_port_key = port->actor_admin_port_key |= - (__get_link_speed(port) << 1); - pr_debug("Port %d changed speed\n", port->actor_port_number); - // there is no need to reselect a new aggregator, just signal the - // state machines to reinitialize - port->sm_vars |= AD_PORT_BEGIN; } /** - * bond_3ad_adapter_duplex_changed - handle a slave's duplex change indication + * bond_3ad_adapter_speed_duplex_changed - handle a slave's speed / duplex + * change indication + * * @slave: slave struct to work on * * Handle reselection of aggregator (if needed) for this port. */ -void bond_3ad_adapter_duplex_changed(struct slave *slave) +void bond_3ad_adapter_speed_duplex_changed(struct slave *slave) { struct port *port; - port = &(SLAVE_AD_INFO(slave).port); + port = &(SLAVE_AD_INFO(slave)->port); - // if slave is null, the whole port is not initialized + /* if slave is null, the whole port is not initialized */ if (!port->slave) { - pr_warning("%s: Warning: duplex changed for uninitialized port on %s\n", - slave->bond->dev->name, slave->dev->name); + netdev_warn(slave->bond->dev, + "speed/duplex changed for uninitialized port %s\n", + slave->dev->name); return; } - port->actor_admin_port_key &= ~AD_DUPLEX_KEY_BITS; - port->actor_oper_port_key = port->actor_admin_port_key |= - __get_duplex(port); - pr_debug("Port %d changed duplex\n", port->actor_port_number); - // there is no need to reselect a new aggregator, just signal the - // state machines to reinitialize - port->sm_vars |= AD_PORT_BEGIN; + spin_lock_bh(&slave->bond->mode_lock); + ad_update_actor_keys(port, false); + netdev_dbg(slave->bond->dev, "Port %d slave %s changed speed/duplex\n", + port->actor_port_number, slave->dev->name); + spin_unlock_bh(&slave->bond->mode_lock); } /** @@ -2289,42 +2450,50 @@ { struct port *port; - port = &(SLAVE_AD_INFO(slave).port); + port = &(SLAVE_AD_INFO(slave)->port); - // if slave is null, the whole port is not initialized + /* if slave is null, the whole port is not initialized */ if (!port->slave) { - pr_warning("Warning: %s: link status changed for uninitialized port on %s\n", - slave->bond->dev->name, slave->dev->name); + netdev_warn(slave->bond->dev, "link status changed for uninitialized port on %s\n", + slave->dev->name); return; } - // on link down we are zeroing duplex and speed since some of the adaptors(ce1000.lan) report full duplex/speed instead of N/A(duplex) / 0(speed) - // on link up we are forcing recheck on the duplex and speed since some of he adaptors(ce1000.lan) report + spin_lock_bh(&slave->bond->mode_lock); + /* on link down we are zeroing duplex and speed since + * some of the adaptors(ce1000.lan) report full duplex/speed + * instead of N/A(duplex) / 0(speed). + * + * on link up we are forcing recheck on the duplex and speed since + * some of he adaptors(ce1000.lan) report. + */ if (link == BOND_LINK_UP) { port->is_enabled = true; - port->actor_admin_port_key &= ~AD_DUPLEX_KEY_BITS; - port->actor_oper_port_key = port->actor_admin_port_key |= - __get_duplex(port); - port->actor_admin_port_key &= ~AD_SPEED_KEY_BITS; - port->actor_oper_port_key = port->actor_admin_port_key |= - (__get_link_speed(port) << 1); + ad_update_actor_keys(port, false); } else { /* link has failed */ port->is_enabled = false; - port->actor_admin_port_key &= ~AD_DUPLEX_KEY_BITS; - port->actor_oper_port_key = (port->actor_admin_port_key &= - ~AD_SPEED_KEY_BITS); - } - //BOND_PRINT_DBG(("Port %d changed link status to %s", port->actor_port_number, ((link == BOND_LINK_UP)?"UP":"DOWN"))); - // there is no need to reselect a new aggregator, just signal the - // state machines to reinitialize - port->sm_vars |= AD_PORT_BEGIN; + ad_update_actor_keys(port, true); + } + netdev_dbg(slave->bond->dev, "Port %d changed link status to %s\n", + port->actor_port_number, + link == BOND_LINK_UP ? "UP" : "DOWN"); + + spin_unlock_bh(&slave->bond->mode_lock); + + /* RTNL is held and mode_lock is released so it's safe + * to update slave_array here. + */ + bond_update_slave_arr(slave->bond, NULL); } -/* - * set link state for bonding master: if we have an active - * aggregator, we're up, if not, we're down. Presumes that we cannot - * have an active aggregator if there are no slaves with link up. +/** + * bond_3ad_set_carrier - set link state for bonding master + * @bond - bonding structure + * + * if we have an active aggregator, we're up, if not, we're down. + * Presumes that we cannot have an active aggregator if there are + * no slaves with link up. * * This behavior complies with IEEE 802.3 section 43.3.9. * @@ -2334,27 +2503,33 @@ int bond_3ad_set_carrier(struct bonding *bond) { struct aggregator *active; + struct slave *first_slave; + int ret = 1; - active = __get_active_agg(&(SLAVE_AD_INFO(bond->first_slave).aggregator)); + 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 (active->num_of_ports < bond->params.min_links) { if (netif_carrier_ok(bond->dev)) { netif_carrier_off(bond->dev); - return 1; + goto out; } } else if (!netif_carrier_ok(bond->dev)) { netif_carrier_on(bond->dev); - return 1; + goto out; } - return 0; - } - - if (netif_carrier_ok(bond->dev)) { + } else if (netif_carrier_ok(bond->dev)) { netif_carrier_off(bond->dev); - return 1; } - return 0; +out: + rcu_read_unlock(); + return ret; } /** @@ -2369,155 +2544,181 @@ struct ad_info *ad_info) { struct aggregator *aggregator = NULL; + struct list_head *iter; + struct slave *slave; struct port *port; - for (port = __get_first_port(bond); port; port = __get_next_port(port)) { + bond_for_each_slave_rcu(bond, slave, iter) { + port = &(SLAVE_AD_INFO(slave)->port); if (port->aggregator && port->aggregator->is_active) { aggregator = port->aggregator; break; } } - if (aggregator) { - ad_info->aggregator_id = aggregator->aggregator_identifier; - ad_info->ports = aggregator->num_of_ports; - ad_info->actor_key = aggregator->actor_oper_aggregator_key; - ad_info->partner_key = aggregator->partner_oper_aggregator_key; - memcpy(ad_info->partner_system, aggregator->partner_system.mac_addr_value, ETH_ALEN); - return 0; - } + if (!aggregator) + return -1; - return -1; + ad_info->aggregator_id = aggregator->aggregator_identifier; + ad_info->ports = aggregator->num_of_ports; + ad_info->actor_key = aggregator->actor_oper_aggregator_key; + ad_info->partner_key = aggregator->partner_oper_aggregator_key; + ether_addr_copy(ad_info->partner_system, + aggregator->partner_system.mac_addr_value); + return 0; } -/* Wrapper used to hold bond->lock so no slave manipulation can occur */ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info) { int ret; - read_lock(&bond->lock); + rcu_read_lock(); ret = __bond_3ad_get_active_agg_info(bond, ad_info); - read_unlock(&bond->lock); + rcu_read_unlock(); return ret; } -int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) +/* bond_3ad_get_tx_dev - Calculate egress interface for a given packet, + * for a LAG that is configured in 802.3AD mode + * @skb: pointer to skb to be egressed + * @src_mac: pointer to source L2 address + * @dst_mac: pointer to destination L2 address + * @src: pointer to source L3 address + * @dst: pointer to destination L3 address + * @protocol: L3 protocol id from L2 header + * @bond_dev: pointer to bond master device + * + * If @skb is NULL, bond_xmit_hash is used to calculate hash using L2/L3 + * addresses. + * + * Returns: Either valid slave device, or NULL otherwise + */ +struct net_device *bond_3ad_get_tx_dev(struct sk_buff *skb, u8 *src_mac, + u8 *dst_mac, void *src, + void *dst, u16 protocol, + struct net_device *bond_dev, + __be16 *layer4hdr) { - struct slave *slave, *start_at; - struct bonding *bond = netdev_priv(dev); - int slave_agg_no; + struct bonding *bond = netdev_priv(bond_dev); + struct aggregator *agg; + struct ad_info ad_info; + struct list_head *iter; + struct slave *slave; + struct slave *first_ok_slave = NULL; + u32 hash = 0; int slaves_in_agg; + int slave_agg_no = 0; int agg_id; - int i; - struct ad_info ad_info; - int res = 1; if (__bond_3ad_get_active_agg_info(bond, &ad_info)) { pr_debug("%s: Error: __bond_3ad_get_active_agg_info failed\n", - dev->name); - goto out; + bond_dev->name); + return NULL; } slaves_in_agg = ad_info.ports; agg_id = ad_info.aggregator_id; if (slaves_in_agg == 0) { - /*the aggregator is empty*/ - pr_debug("%s: Error: active aggregator is empty\n", dev->name); - goto out; + pr_debug("%s: Error: active aggregator is empty\n", + bond_dev->name); + return NULL; } - slave_agg_no = bond->xmit_hash_policy(skb, slaves_in_agg); - - bond_for_each_slave(bond, slave, i) { - struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator; + if (skb) { + hash = bond_xmit_hash(bond, skb); + slave_agg_no = hash % slaves_in_agg; + } else { + if (bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER23 && + bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER2 && + bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER34) { + pr_debug("%s: Error: Unsupported hash policy for 802.3AD fast path\n", + bond_dev->name); + return NULL; + } + + hash = bond_xmit_hash_without_skb(src_mac, dst_mac, + src, dst, protocol, + bond_dev, layer4hdr); + slave_agg_no = hash % slaves_in_agg; + } + + bond_for_each_slave_rcu(bond, slave, iter) { + agg = SLAVE_AD_INFO(slave)->port.aggregator; + if (!agg || agg->aggregator_identifier != agg_id) + continue; - if (agg && (agg->aggregator_identifier == agg_id)) { + if (slave_agg_no >= 0) { + if (!first_ok_slave && bond_slave_can_tx(slave)) + first_ok_slave = slave; slave_agg_no--; - if (slave_agg_no < 0) - break; + continue; } + + if (bond_slave_can_tx(slave)) + return slave->dev; } if (slave_agg_no >= 0) { pr_err("%s: Error: Couldn't find a slave to tx on for aggregator ID %d\n", - dev->name, agg_id); - goto out; - } - - start_at = slave; - - bond_for_each_slave_from(bond, slave, i, start_at) { - int slave_agg_id = 0; - struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator; - - if (agg) - slave_agg_id = agg->aggregator_identifier; - - if (SLAVE_IS_OK(slave) && agg && (slave_agg_id == agg_id)) { - res = bond_dev_queue_xmit(bond, skb, slave->dev); - break; - } + bond_dev->name, agg_id); + return NULL; } -out: - if (res) { - /* no suitable interface, frame not sent */ - kfree_skb(skb); - } + /* we couldn't find any suitable slave after the agg_no, so use the + * first suitable found, if found. + */ + if (first_ok_slave) + return first_ok_slave->dev; - return NETDEV_TX_OK; + return NULL; } int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave) { - int ret = RX_HANDLER_ANOTHER; struct lacpdu *lacpdu, _lacpdu; if (skb->protocol != PKT_TYPE_LACPDU) - return ret; + return RX_HANDLER_ANOTHER; + + if (!MAC_ADDRESS_EQUAL(eth_hdr(skb)->h_dest, lacpdu_mcast_addr)) + return RX_HANDLER_ANOTHER; lacpdu = skb_header_pointer(skb, 0, sizeof(_lacpdu), &_lacpdu); if (!lacpdu) - return ret; + return RX_HANDLER_ANOTHER; - read_lock(&bond->lock); - ret = bond_3ad_rx_indication(lacpdu, slave, skb->len); - read_unlock(&bond->lock); - return ret; + return bond_3ad_rx_indication(lacpdu, slave, skb->len); } -/* +/** + * bond_3ad_update_lacp_rate - change the lacp rate + * @bond - bonding struct + * * When modify lacp_rate parameter via sysfs, * update actor_oper_port_state of each port. * - * Hold slave->state_machine_lock, + * Hold bond->mode_lock, * so we can modify port->actor_oper_port_state, * no matter bond is up or down. */ void bond_3ad_update_lacp_rate(struct bonding *bond) { - int i; - struct slave *slave; struct port *port = NULL; + struct list_head *iter; + struct slave *slave; int lacp_fast; - write_lock_bh(&bond->lock); lacp_fast = bond->params.lacp_fast; - - bond_for_each_slave(bond, slave, i) { - port = &(SLAVE_AD_INFO(slave).port); - if (port->slave == NULL) - continue; - __get_state_machine_lock(port); + spin_lock_bh(&bond->mode_lock); + bond_for_each_slave(bond, slave, iter) { + port = &(SLAVE_AD_INFO(slave)->port); if (lacp_fast) port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT; else port->actor_oper_port_state &= ~AD_STATE_LACP_TIMEOUT; - __release_state_machine_lock(port); } - - write_unlock_bh(&bond->lock); + spin_unlock_bh(&bond->mode_lock); }