]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/net/bonding/bond_3ad.c
bonding: init port_params from template
[mirror_ubuntu-zesty-kernel.git] / drivers / net / bonding / bond_3ad.c
index 6106660a4a44b9a68f7a5b9a9c9c26a5b45ee689..f659f97140c7ea749bb63530a6a532b08ddc1c8d 100644 (file)
  *
  */
 
-//#define BONDING_DEBUG 1
-
 #include <linux/skbuff.h>
 #include <linux/if_ether.h>
 #include <linux/netdevice.h>
 #include <linux/spinlock.h>
 #include <linux/ethtool.h>
+#include <linux/etherdevice.h>
 #include <linux/if_bonding.h>
 #include <linux/pkt_sched.h>
 #include <net/net_namespace.h>
@@ -236,6 +235,17 @@ static inline struct aggregator *__get_next_agg(struct aggregator *aggregator)
        return &(SLAVE_AD_INFO(slave->next).aggregator);
 }
 
+/*
+ * __agg_has_partner
+ *
+ * Return nonzero if aggregator has a partner (denoted by a non-zero ether
+ * address for the partner).  Return 0 if not.
+ */
+static inline int __agg_has_partner(struct aggregator *agg)
+{
+       return !is_zero_ether_addr(agg->partner_system.mac_addr_value);
+}
+
 /**
  * __disable_port - disable the port's slave
  * @port: the port we're looking at
@@ -274,14 +284,14 @@ static inline int __port_is_enabled(struct port *port)
  * __get_agg_selection_mode - get the aggregator selection mode
  * @port: the port we're looking at
  *
- * Get the aggregator selection mode. Can be %BANDWIDTH or %COUNT.
+ * Get the aggregator selection mode. Can be %STABLE, %BANDWIDTH or %COUNT.
  */
 static inline u32 __get_agg_selection_mode(struct port *port)
 {
        struct bonding *bond = __get_bond_by_port(port);
 
        if (bond == NULL) {
-               return AD_BANDWIDTH;
+               return BOND_AD_STABLE;
        }
 
        return BOND_AD_INFO(bond).agg_select_mode;
@@ -369,7 +379,7 @@ static u16 __get_link_speed(struct port *port)
                }
        }
 
-       dprintk("Port %d Received link speed %d update from adapter\n", port->actor_port_number, speed);
+       pr_debug("Port %d Received link speed %d update from adapter\n", port->actor_port_number, speed);
        return speed;
 }
 
@@ -395,12 +405,12 @@ static u8 __get_duplex(struct port *port)
                switch (slave->duplex) {
                case DUPLEX_FULL:
                        retval=0x1;
-                       dprintk("Port %d Received status full duplex update from adapter\n", port->actor_port_number);
+                       pr_debug("Port %d Received status full duplex update from adapter\n", port->actor_port_number);
                        break;
                case DUPLEX_HALF:
                default:
                        retval=0x0;
-                       dprintk("Port %d Received status NOT full duplex update from adapter\n", port->actor_port_number);
+                       pr_debug("Port %d Received status NOT full duplex update from adapter\n", port->actor_port_number);
                        break;
                }
        }
@@ -473,33 +483,25 @@ static u16 __ad_timer_to_ticks(u16 timer_type, u16 par)
  */
 static void __record_pdu(struct lacpdu *lacpdu, struct port *port)
 {
-       // validate lacpdu and port
        if (lacpdu && port) {
+               struct port_params *partner = &port->partner_oper;
+
                // record the new parameter values for the partner operational
-               port->partner_oper_port_number = ntohs(lacpdu->actor_port);
-               port->partner_oper_port_priority = ntohs(lacpdu->actor_port_priority);
-               port->partner_oper_system = lacpdu->actor_system;
-               port->partner_oper_system_priority = ntohs(lacpdu->actor_system_priority);
-               port->partner_oper_key = ntohs(lacpdu->actor_key);
-               // zero partener's lase states
-               port->partner_oper_port_state = 0;
-               port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_LACP_ACTIVITY);
-               port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_LACP_TIMEOUT);
-               port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_AGGREGATION);
-               port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_SYNCHRONIZATION);
-               port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_COLLECTING);
-               port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_DISTRIBUTING);
-               port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_DEFAULTED);
-               port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_EXPIRED);
+               partner->port_number = ntohs(lacpdu->actor_port);
+               partner->port_priority = ntohs(lacpdu->actor_port_priority);
+               partner->system = lacpdu->actor_system;
+               partner->system_priority = ntohs(lacpdu->actor_system_priority);
+               partner->key = ntohs(lacpdu->actor_key);
+               partner->port_state = lacpdu->actor_state;
 
                // 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)) {
-                       port->partner_oper_port_state |= AD_STATE_SYNCHRONIZATION;
+                       partner->port_state |= AD_STATE_SYNCHRONIZATION;
                } else {
-                       port->partner_oper_port_state &= ~AD_STATE_SYNCHRONIZATION;
+                       partner->port_state &= ~AD_STATE_SYNCHRONIZATION;
                }
        }
 }
@@ -514,15 +516,10 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port)
  */
 static void __record_default(struct port *port)
 {
-       // validate the port
        if (port) {
                // record the partner admin parameters
-               port->partner_oper_port_number = port->partner_admin_port_number;
-               port->partner_oper_port_priority = port->partner_admin_port_priority;
-               port->partner_oper_system = port->partner_admin_system;
-               port->partner_oper_system_priority = port->partner_admin_system_priority;
-               port->partner_oper_key = port->partner_admin_key;
-               port->partner_oper_port_state = port->partner_admin_port_state;
+               memcpy(&port->partner_oper, &port->partner_admin,
+                      sizeof(struct port_params));
 
                // set actor_oper_port_state.defaulted to true
                port->actor_oper_port_state |= AD_STATE_DEFAULTED;
@@ -547,12 +544,12 @@ static void __update_selected(struct lacpdu *lacpdu, struct port *port)
        // validate lacpdu and port
        if (lacpdu && port) {
                // check if any parameter is different
-               if ((ntohs(lacpdu->actor_port) != port->partner_oper_port_number) ||
-                   (ntohs(lacpdu->actor_port_priority) != port->partner_oper_port_priority) ||
-                   MAC_ADDRESS_COMPARE(&(lacpdu->actor_system), &(port->partner_oper_system)) ||
-                   (ntohs(lacpdu->actor_system_priority) != port->partner_oper_system_priority) ||
-                   (ntohs(lacpdu->actor_key) != port->partner_oper_key) ||
-                   ((lacpdu->actor_state & AD_STATE_AGGREGATION) != (port->partner_oper_port_state & AD_STATE_AGGREGATION))
+               if ((ntohs(lacpdu->actor_port) != port->partner_oper.port_number) ||
+                   (ntohs(lacpdu->actor_port_priority) != port->partner_oper.port_priority) ||
+                   MAC_ADDRESS_COMPARE(&(lacpdu->actor_system), &(port->partner_oper.system)) ||
+                   (ntohs(lacpdu->actor_system_priority) != port->partner_oper.system_priority) ||
+                   (ntohs(lacpdu->actor_key) != port->partner_oper.key) ||
+                   ((lacpdu->actor_state & AD_STATE_AGGREGATION) != (port->partner_oper.port_state & AD_STATE_AGGREGATION))
                   ) {
                        // update the state machine Selected variable
                        port->sm_vars &= ~AD_PORT_SELECTED;
@@ -577,12 +574,12 @@ static void __update_default_selected(struct port *port)
        // validate the port
        if (port) {
                // check if any parameter is different
-               if ((port->partner_admin_port_number != port->partner_oper_port_number) ||
-                   (port->partner_admin_port_priority != port->partner_oper_port_priority) ||
-                   MAC_ADDRESS_COMPARE(&(port->partner_admin_system), &(port->partner_oper_system)) ||
-                   (port->partner_admin_system_priority != port->partner_oper_system_priority) ||
-                   (port->partner_admin_key != port->partner_oper_key) ||
-                   ((port->partner_admin_port_state & AD_STATE_AGGREGATION) != (port->partner_oper_port_state & AD_STATE_AGGREGATION))
+               if ((port->partner_admin.port_number != port->partner_oper.port_number) ||
+                   (port->partner_admin.port_priority != port->partner_oper.port_priority) ||
+                   MAC_ADDRESS_COMPARE(&(port->partner_admin.system), &(port->partner_oper.system)) ||
+                   (port->partner_admin.system_priority != port->partner_oper.system_priority) ||
+                   (port->partner_admin.key != port->partner_oper.key) ||
+                   ((port->partner_admin.port_state & AD_STATE_AGGREGATION) != (port->partner_oper.port_state & AD_STATE_AGGREGATION))
                   ) {
                        // update the state machine Selected variable
                        port->sm_vars &= ~AD_PORT_SELECTED;
@@ -818,12 +815,12 @@ static inline void __update_lacpdu_from_port(struct port *port)
         * lacpdu->partner_information_length initialized
         */
 
-       lacpdu->partner_system_priority = htons(port->partner_oper_system_priority);
-       lacpdu->partner_system = port->partner_oper_system;
-       lacpdu->partner_key = htons(port->partner_oper_key);
-       lacpdu->partner_port_priority = htons(port->partner_oper_port_priority);
-       lacpdu->partner_port = htons(port->partner_oper_port_number);
-       lacpdu->partner_state = port->partner_oper_port_state;
+       lacpdu->partner_system_priority = htons(port->partner_oper.system_priority);
+       lacpdu->partner_system = port->partner_oper.system;
+       lacpdu->partner_key = htons(port->partner_oper.key);
+       lacpdu->partner_port_priority = htons(port->partner_oper.port_priority);
+       lacpdu->partner_port = htons(port->partner_oper.port_number);
+       lacpdu->partner_state = port->partner_oper.port_state;
 
        /* lacpdu->reserved_3_2              initialized
         * lacpdu->tlv_type_collector_info   initialized
@@ -972,7 +969,7 @@ static void ad_mux_machine(struct port *port)
                        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)) {
+                       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
                                port->sm_vars &= ~AD_PORT_READY_N;
@@ -984,7 +981,7 @@ static void ad_mux_machine(struct port *port)
                        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->partner_oper.port_state & AD_STATE_SYNCHRONIZATION)
                           ) {
                                port->sm_mux_state = AD_MUX_ATTACHED;// next state
 
@@ -1007,7 +1004,7 @@ static void ad_mux_machine(struct port *port)
 
        // check if the state machine was changed
        if (port->sm_mux_state != last_state) {
-               dprintk("Mux Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_mux_state);
+               pr_debug("Mux Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_mux_state);
                switch (port->sm_mux_state) {
                case AD_MUX_DETACHED:
                        __detach_bond_from_agg(port);
@@ -1106,7 +1103,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
 
        // check if the State machine was changed or new lacpdu arrived
        if ((port->sm_rx_state != last_state) || (lacpdu)) {
-               dprintk("Rx Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_rx_state);
+               pr_debug("Rx Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_rx_state);
                switch (port->sm_rx_state) {
                case AD_RX_INITIALIZE:
                        if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_BITS)) {
@@ -1128,7 +1125,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
                case AD_RX_LACP_DISABLED:
                        port->sm_vars &= ~AD_PORT_SELECTED;
                        __record_default(port);
-                       port->partner_oper_port_state &= ~AD_STATE_AGGREGATION;
+                       port->partner_oper.port_state &= ~AD_STATE_AGGREGATION;
                        port->sm_vars |= AD_PORT_MATCHED;
                        port->actor_oper_port_state &= ~AD_STATE_EXPIRED;
                        break;
@@ -1136,9 +1133,9 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *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->partner_oper.port_state &= ~AD_STATE_SYNCHRONIZATION;
                        port->sm_vars &= ~AD_PORT_MATCHED;
-                       port->partner_oper_port_state |= AD_SHORT_TIMEOUT;
+                       port->partner_oper.port_state |= AD_SHORT_TIMEOUT;
                        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;
                        break;
@@ -1193,7 +1190,7 @@ static void ad_tx_machine(struct port *port)
                        __update_lacpdu_from_port(port);
                        // send the lacpdu
                        if (ad_lacpdu_send(port) >= 0) {
-                               dprintk("Sent LACPDU on port %d\n", port->actor_port_number);
+                               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
                                port->ntt = 0;
                        }
@@ -1218,7 +1215,7 @@ static void ad_periodic_machine(struct port *port)
 
        // 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->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
        }
@@ -1232,12 +1229,12 @@ static void ad_periodic_machine(struct port *port)
                        // 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)) {
+                               if (!(port->partner_oper.port_state & AD_STATE_LACP_TIMEOUT)) {
                                        port->sm_periodic_state = AD_SLOW_PERIODIC;  // next state
                                }
                                break;
                        case AD_SLOW_PERIODIC:
-                               if ((port->partner_oper_port_state & AD_STATE_LACP_TIMEOUT)) {
+                               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
@@ -1253,7 +1250,7 @@ static void ad_periodic_machine(struct port *port)
                        port->sm_periodic_state = AD_FAST_PERIODIC;      // next state
                        break;
                case AD_PERIODIC_TX:
-                       if (!(port->partner_oper_port_state & AD_STATE_LACP_TIMEOUT)) {
+                       if (!(port->partner_oper.port_state & AD_STATE_LACP_TIMEOUT)) {
                                port->sm_periodic_state = AD_SLOW_PERIODIC;  // next state
                        } else {
                                port->sm_periodic_state = AD_FAST_PERIODIC;  // next state
@@ -1266,7 +1263,7 @@ static void ad_periodic_machine(struct port *port)
 
        // check if the state machine was changed
        if (port->sm_periodic_state != last_state) {
-               dprintk("Periodic Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_periodic_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
@@ -1323,7 +1320,7 @@ static void ad_port_selection_logic(struct port *port)
                                port->next_port_in_aggregator=NULL;
                                port->actor_port_aggregator_identifier=0;
 
-                               dprintk("Port %d left LAG %d\n", port->actor_port_number, temp_aggregator->aggregator_identifier);
+                               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
                                if (!temp_aggregator->lag_ports) {
                                        ad_clear_agg(temp_aggregator);
@@ -1352,11 +1349,11 @@ static void ad_port_selection_logic(struct port *port)
                }
                // 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)) &&
-                    (aggregator->partner_system_priority == port->partner_oper_system_priority) &&
-                    (aggregator->partner_oper_aggregator_key == port->partner_oper_key)
+                    !MAC_ADDRESS_COMPARE(&(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
+                   ((MAC_ADDRESS_COMPARE(&(port->partner_oper.system), &(null_mac_addr)) && // partner answers
                      !aggregator->is_individual)  // but is not individual OR
                    )
                   ) {
@@ -1366,7 +1363,7 @@ static void ad_port_selection_logic(struct port *port)
                        port->next_port_in_aggregator=aggregator->lag_ports;
                        port->aggregator->num_of_ports++;
                        aggregator->lag_ports=port;
-                       dprintk("Port %d joined LAG %d(existing LAG)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
+                       pr_debug("Port %d joined LAG %d(existing LAG)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
 
                        // mark this port as selected
                        port->sm_vars |= AD_PORT_SELECTED;
@@ -1392,9 +1389,9 @@ static void ad_port_selection_logic(struct port *port)
 
                        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 = port->partner_oper_system_priority;
-                       port->aggregator->partner_oper_aggregator_key = port->partner_oper_key;
+                       port->aggregator->partner_system=port->partner_oper.system;
+                       port->aggregator->partner_system_priority = port->partner_oper.system_priority;
+                       port->aggregator->partner_oper_aggregator_key = port->partner_oper.key;
                        port->aggregator->receive_state = 1;
                        port->aggregator->transmit_state = 1;
                        port->aggregator->lag_ports = port;
@@ -1403,7 +1400,7 @@ static void ad_port_selection_logic(struct port *port)
                        // mark this port as selected
                        port->sm_vars |= AD_PORT_SELECTED;
 
-                       dprintk("Port %d joined LAG %d(new LAG)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
+                       pr_debug("Port %d joined LAG %d(new LAG)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
                } else {
                        printk(KERN_ERR DRV_NAME ": %s: Port %d (on %s) did not find a suitable aggregator\n",
                               port->slave->dev->master->name,
@@ -1414,9 +1411,82 @@ static void ad_port_selection_logic(struct port *port)
        // else set ready=FALSE in all aggregator's ports
        __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
 
-       if (!__check_agg_selection_timer(port) && (aggregator = __get_first_agg(port))) {
-               ad_agg_selection_logic(aggregator);
+       aggregator = __get_first_agg(port);
+       ad_agg_selection_logic(aggregator);
+}
+
+/*
+ * 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.
+        *
+        * 1. If the current agg is not individual, and the best is
+        *    individual, select current.
+        *
+        * 2. If current agg is individual and the best is not, keep best.
+        *
+        * 3. Therefore, current and best are both individual or both not
+        *    individual, so:
+        *
+        * 3a. If current agg partner replied, and best agg partner did not,
+        *     select current.
+        *
+        * 3b. If current agg partner did not reply and best agg partner
+        *     did reply, keep best.
+        *
+        * 4.  Therefore, current and best both have partner replies or
+        *     both do not, so perform selection policy:
+        *
+        * BOND_AD_COUNT: Select by count of ports.  If count is equal,
+        *     select by bandwidth.
+        *
+        * BOND_AD_STABLE, BOND_AD_BANDWIDTH: Select by bandwidth.
+        */
+       if (!best)
+               return curr;
+
+       if (!curr->is_individual && best->is_individual)
+               return curr;
+
+       if (curr->is_individual && !best->is_individual)
+               return best;
+
+       if (__agg_has_partner(curr) && !__agg_has_partner(best))
+               return curr;
+
+       if (!__agg_has_partner(curr) && __agg_has_partner(best))
+               return best;
+
+       switch (__get_agg_selection_mode(curr->lag_ports)) {
+       case BOND_AD_COUNT:
+               if (curr->num_of_ports > best->num_of_ports)
+                       return curr;
+
+               if (curr->num_of_ports < best->num_of_ports)
+                       return best;
+
+               /*FALLTHROUGH*/
+       case BOND_AD_STABLE:
+       case BOND_AD_BANDWIDTH:
+               if (__get_agg_bandwidth(curr) > __get_agg_bandwidth(best))
+                       return curr;
+
+               break;
+
+       default:
+               printk(KERN_WARNING DRV_NAME
+                      ": %s: Impossible agg select mode %d\n",
+                      curr->slave->dev->master->name,
+                      __get_agg_selection_mode(curr->lag_ports));
+               break;
        }
+
+       return best;
 }
 
 /**
@@ -1424,156 +1494,138 @@ static void ad_port_selection_logic(struct port *port)
  * @aggregator: the aggregator we're looking at
  *
  * It is assumed that only one aggregator may be selected for a team.
- * The logic of this function is to select (at first time) the aggregator with
- * the most ports attached to it, and to reselect the active aggregator only if
- * the previous aggregator has no more ports related to it.
+ *
+ * The logic of this function is to select the aggregator according to
+ * the ad_select policy:
+ *
+ * BOND_AD_STABLE: select the aggregator with the most ports attached to
+ * it, and to reselect the active aggregator only if the previous
+ * aggregator has no more ports related to it.
+ *
+ * BOND_AD_BANDWIDTH: select the aggregator with the highest total
+ * bandwidth, and reselect whenever a link state change takes place or the
+ * set of slaves in the bond changes.
+ *
+ * BOND_AD_COUNT: select the aggregator with largest number of ports
+ * (slaves), and reselect whenever a link state change takes place or the
+ * set of slaves in the bond changes.
  *
  * FIXME: this function MUST be called with the first agg in the bond, or
  * __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 *aggregator)
+static void ad_agg_selection_logic(struct aggregator *agg)
 {
-       struct aggregator *best_aggregator = NULL, *active_aggregator = NULL;
-       struct aggregator *last_active_aggregator = NULL, *origin_aggregator;
+       struct aggregator *best, *active, *origin;
        struct port *port;
-       u16 num_of_aggs=0;
 
-       origin_aggregator = aggregator;
+       origin = agg;
 
-       //get current active aggregator
-       last_active_aggregator = __get_active_agg(aggregator);
+       active = __get_active_agg(agg);
+       best = active;
 
-       // search for the aggregator with the most ports attached to it.
        do {
-               // count how many candidate lag's we have
-               if (aggregator->lag_ports) {
-                       num_of_aggs++;
-               }
-               if (aggregator->is_active && !aggregator->is_individual &&   // if current aggregator is the active aggregator
-                   MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr))) {   // and partner answers to 802.3ad PDUs
-                       if (aggregator->num_of_ports) { // if any ports attached to the current aggregator
-                               best_aggregator=NULL;    // disregard the best aggregator that was chosen by now
-                               break;           // stop the selection of other aggregator if there are any ports attached to this active aggregator
-                       } else { // no ports attached to this active aggregator
-                               aggregator->is_active = 0; // mark this aggregator as not active anymore
+               agg->is_active = 0;
+
+               if (agg->num_of_ports)
+                       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
+                * aggregator if it's still active (it has an answering
+                * partner) or if both the best and active don't have an
+                * answering partner.
+                */
+               if (active && active->lag_ports &&
+                   active->lag_ports->is_enabled &&
+                   (__agg_has_partner(active) ||
+                    (!__agg_has_partner(active) && !__agg_has_partner(best)))) {
+                       if (!(!active->actor_oper_aggregator_key &&
+                             best->actor_oper_aggregator_key)) {
+                               best = NULL;
+                               active->is_active = 1;
                        }
                }
-               if (aggregator->num_of_ports) { // if any ports attached
-                       if (best_aggregator) {  // if there is a candidte aggregator
-                               //The reasons for choosing new best aggregator:
-                               // 1. if current agg is NOT individual and the best agg chosen so far is individual OR
-                               // current and best aggs are both individual or both not individual, AND
-                               // 2a.  current agg partner reply but best agg partner do not reply OR
-                               // 2b.  current agg partner reply OR current agg partner do not reply AND best agg partner also do not reply AND
-                               //      current has more ports/bandwidth, or same amount of ports but current has faster ports, THEN
-                               //      current agg become best agg so far
-
-                               //if current agg is NOT individual and the best agg chosen so far is individual change best_aggregator
-                               if (!aggregator->is_individual && best_aggregator->is_individual) {
-                                       best_aggregator=aggregator;
-                               }
-                               // current and best aggs are both individual or both not individual
-                               else if ((aggregator->is_individual && best_aggregator->is_individual) ||
-                                        (!aggregator->is_individual && !best_aggregator->is_individual)) {
-                                       //  current and best aggs are both individual or both not individual AND
-                                       //  current agg partner reply but best agg partner do not reply
-                                       if ((MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr)) &&
-                                            !MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) {
-                                               best_aggregator=aggregator;
-                                       }
-                                       //  current agg partner reply OR current agg partner do not reply AND best agg partner also do not reply
-                                       else if (! (!MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr)) &&
-                                                   MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) {
-                                               if ((__get_agg_selection_mode(aggregator->lag_ports) == AD_BANDWIDTH)&&
-                                                   (__get_agg_bandwidth(aggregator) > __get_agg_bandwidth(best_aggregator))) {
-                                                       best_aggregator=aggregator;
-                                               } else if (__get_agg_selection_mode(aggregator->lag_ports) == AD_COUNT) {
-                                                       if (((aggregator->num_of_ports > best_aggregator->num_of_ports) &&
-                                                            (aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS))||
-                                                           ((aggregator->num_of_ports == best_aggregator->num_of_ports) &&
-                                                            ((u16)(aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS) >
-                                                             (u16)(best_aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS)))) {
-                                                               best_aggregator=aggregator;
-                                                       }
-                                               }
-                                       }
-                               }
-                       } else {
-                               best_aggregator=aggregator;
-                       }
-               }
-               aggregator->is_active = 0; // mark all aggregators as not active anymore
-       } while ((aggregator = __get_next_agg(aggregator)));
-
-       // if we have new aggregator selected, don't replace the old aggregator if it has an answering partner,
-       // or if both old aggregator and new aggregator don't have answering partner
-       if (best_aggregator) {
-               if (last_active_aggregator && last_active_aggregator->lag_ports && last_active_aggregator->lag_ports->is_enabled &&
-                   (MAC_ADDRESS_COMPARE(&(last_active_aggregator->partner_system), &(null_mac_addr)) ||   // partner answers OR
-                    (!MAC_ADDRESS_COMPARE(&(last_active_aggregator->partner_system), &(null_mac_addr)) &&      // both old and new
-                     !MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr))))     // partner do not answer
-                  ) {
-                       // if new aggregator has link, and old aggregator does not, replace old aggregator.(do nothing)
-                       // -> don't replace otherwise.
-                       if (!(!last_active_aggregator->actor_oper_aggregator_key && best_aggregator->actor_oper_aggregator_key)) {
-                               best_aggregator=NULL;
-                               last_active_aggregator->is_active = 1; // don't replace good old aggregator
+       }
 
-                       }
-               }
+       if (best && (best == active)) {
+               best = NULL;
+               active->is_active = 1;
        }
 
        // if there is new best aggregator, activate it
-       if (best_aggregator) {
-               for (aggregator = __get_first_agg(best_aggregator->lag_ports);
-                   aggregator;
-                   aggregator = __get_next_agg(aggregator)) {
-
-                       dprintk("Agg=%d; Ports=%d; a key=%d; p key=%d; Indiv=%d; Active=%d\n",
-                                       aggregator->aggregator_identifier, aggregator->num_of_ports,
-                                       aggregator->actor_oper_aggregator_key, aggregator->partner_oper_aggregator_key,
-                                       aggregator->is_individual, aggregator->is_active);
+       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);
                }
 
                // check if any partner replys
-               if (best_aggregator->is_individual) {
-                       printk(KERN_WARNING DRV_NAME ": %s: Warning: No 802.3ad response from "
-                              "the link partner for any adapters in the bond\n",
-                              best_aggregator->slave->dev->master->name);
+               if (best->is_individual) {
+                       printk(KERN_WARNING DRV_NAME ": %s: Warning: No 802.3ad"
+                              " response from the link partner for any"
+                              " adapters in the bond\n",
+                              best->slave->dev->master->name);
                }
 
-               // check if there are more than one aggregator
-               if (num_of_aggs > 1) {
-                       dprintk("Warning: More than one Link Aggregation Group was "
-                               "found in the bond. Only one group will function in the bond\n");
-               }
-
-               best_aggregator->is_active = 1;
-               dprintk("LAG %d choosed as the active LAG\n", best_aggregator->aggregator_identifier);
-               dprintk("Agg=%d; Ports=%d; a key=%d; p key=%d; Indiv=%d; Active=%d\n",
-                               best_aggregator->aggregator_identifier, best_aggregator->num_of_ports,
-                               best_aggregator->actor_oper_aggregator_key, best_aggregator->partner_oper_aggregator_key,
-                               best_aggregator->is_individual, best_aggregator->is_active);
+               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);
 
                // disable the ports that were related to the former active_aggregator
-               if (last_active_aggregator) {
-                       for (port=last_active_aggregator->lag_ports; port; port=port->next_port_in_aggregator) {
+               if (active) {
+                       for (port = active->lag_ports; port;
+                            port = port->next_port_in_aggregator) {
                                __disable_port(port);
                        }
                }
        }
 
-       // if the selected aggregator is of join individuals(partner_system is NULL), enable their ports
-       active_aggregator = __get_active_agg(origin_aggregator);
+       /*
+        * if the selected aggregator is of join individuals
+        * (partner_system is NULL), enable their ports
+        */
+       active = __get_active_agg(origin);
 
-       if (active_aggregator) {
-               if (!MAC_ADDRESS_COMPARE(&(active_aggregator->partner_system), &(null_mac_addr))) {
-                       for (port=active_aggregator->lag_ports; port; port=port->next_port_in_aggregator) {
+       if (active) {
+               if (!__agg_has_partner(active)) {
+                       for (port = active->lag_ports; port;
+                            port = port->next_port_in_aggregator) {
                                __enable_port(port);
                        }
                }
        }
+
+       if (origin->slave) {
+               struct bonding *bond;
+
+               bond = bond_get_bond_by_slave(origin->slave);
+               if (bond)
+                       bond_3ad_set_carrier(bond);
+       }
 }
 
 /**
@@ -1595,7 +1647,7 @@ static void ad_clear_agg(struct aggregator *aggregator)
                aggregator->lag_ports = NULL;
                aggregator->is_active = 0;
                aggregator->num_of_ports = 0;
-               dprintk("LAG %d was cleared\n", aggregator->aggregator_identifier);
+               pr_debug("LAG %d was cleared\n", aggregator->aggregator_identifier);
        }
 }
 
@@ -1623,6 +1675,14 @@ static void ad_initialize_agg(struct aggregator *aggregator)
  */
 static void ad_initialize_port(struct port *port, int lacp_fast)
 {
+       static const struct port_params tmpl = {
+               .system_priority = 0xffff,
+               .key             = 1,
+               .port_number     = 1,
+               .port_priority   = 0xff,
+               .port_state      = 1,
+       };
+
        if (port) {
                port->actor_port_number = 1;
                port->actor_port_priority = 0xff;
@@ -1639,18 +1699,9 @@ static void ad_initialize_port(struct port *port, int lacp_fast)
                        port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT;
                }
 
-               port->partner_admin_system = null_mac_addr;
-               port->partner_oper_system  = null_mac_addr;
-               port->partner_admin_system_priority = 0xffff;
-               port->partner_oper_system_priority  = 0xffff;
-               port->partner_admin_key = 1;
-               port->partner_oper_key  = 1;
-               port->partner_admin_port_number = 1;
-               port->partner_oper_port_number  = 1;
-               port->partner_admin_port_priority = 0xff;
-               port->partner_oper_port_priority  = 0xff;
-               port->partner_admin_port_state = 1;
-               port->partner_oper_port_state  = 1;
+               memcpy(&port->partner_admin, &tmpl, sizeof(tmpl));
+               memcpy(&port->partner_oper, &tmpl, sizeof(tmpl));
+
                port->is_enabled = 1;
                // ****** private parameters ******
                port->sm_vars = 0x3;
@@ -1680,7 +1731,7 @@ static void ad_initialize_port(struct port *port, int lacp_fast)
 static void ad_enable_collecting_distributing(struct port *port)
 {
        if (port->aggregator->is_active) {
-               dprintk("Enabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
+               pr_debug("Enabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
                __enable_port(port);
        }
 }
@@ -1693,7 +1744,7 @@ static void ad_enable_collecting_distributing(struct port *port)
 static void ad_disable_collecting_distributing(struct port *port)
 {
        if (port->aggregator && MAC_ADDRESS_COMPARE(&(port->aggregator->partner_system), &(null_mac_addr))) {
-               dprintk("Disabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
+               pr_debug("Disabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
                __disable_port(port);
        }
 }
@@ -1731,7 +1782,7 @@ static void ad_marker_info_send(struct port *port)
 
        // send the marker information
        if (ad_marker_send(port, &marker) >= 0) {
-               dprintk("Sent Marker Information on port %d\n", port->actor_port_number);
+               pr_debug("Sent Marker Information on port %d\n", port->actor_port_number);
        }
 }
 #endif
@@ -1755,7 +1806,7 @@ static void ad_marker_info_received(struct bond_marker *marker_info,
        // send the marker response
 
        if (ad_marker_send(port, &marker) >= 0) {
-               dprintk("Sent Marker Response on port %d\n", port->actor_port_number);
+               pr_debug("Sent Marker Response on port %d\n", port->actor_port_number);
        }
 }
 
@@ -1830,6 +1881,19 @@ static void ad_initialize_lacpdu(struct lacpdu *lacpdu)
 // Check aggregators status in team every T seconds
 #define AD_AGGREGATOR_SELECTION_TIMER  8
 
+/*
+ * bond_3ad_initiate_agg_selection(struct bonding *bond)
+ *
+ * Set the aggregation selection timer, to initiate an agg selection in
+ * the very near future.  Called during first initialization, and during
+ * any down to up transitions of the bond.
+ */
+void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout)
+{
+       BOND_AD_INFO(bond).agg_select_timer = timeout;
+       BOND_AD_INFO(bond).agg_select_mode = bond->params.ad_select;
+}
+
 static u16 aggregator_identifier;
 
 /**
@@ -1854,9 +1918,9 @@ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fas
                // initialize how many times this module is called in one second(should be about every 100ms)
                ad_ticks_per_sec = tick_resolution;
 
-               // initialize the aggregator selection timer(to activate an aggregation selection after initialize)
-               BOND_AD_INFO(bond).agg_select_timer = (AD_AGGREGATOR_SELECTION_TIMER * ad_ticks_per_sec);
-               BOND_AD_INFO(bond).agg_select_mode = AD_BANDWIDTH;
+               bond_3ad_initiate_agg_selection(bond,
+                                               AD_AGGREGATOR_SELECTION_TIMER *
+                                               ad_ticks_per_sec);
        }
 }
 
@@ -1956,7 +2020,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
                return;
        }
 
-       dprintk("Unbinding Link Aggregation Group %d\n", aggregator->aggregator_identifier);
+       pr_debug("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;
@@ -1980,7 +2044,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
                        // 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))) {
-                               dprintk("Some port(s) related to LAG %d - replaceing with LAG %d\n", aggregator->aggregator_identifier, new_aggregator->aggregator_identifier);
+                               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) {
                                        printk(KERN_INFO DRV_NAME ": %s: Removing an active aggregator\n",
@@ -2031,7 +2095,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
                }
        }
 
-       dprintk("Unbinding port %d\n", port->actor_port_number);
+       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)) {
@@ -2162,7 +2226,7 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u
 
                switch (lacpdu->subtype) {
                case AD_TYPE_LACPDU:
-                       dprintk("Received LACPDU on port %d\n", port->actor_port_number);
+                       pr_debug("Received LACPDU on port %d\n", port->actor_port_number);
                        ad_rx_machine(lacpdu, port);
                        break;
 
@@ -2171,17 +2235,17 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u
 
                        switch (((struct bond_marker *)lacpdu)->tlv_type) {
                        case AD_MARKER_INFORMATION_SUBTYPE:
-                               dprintk("Received Marker Information on port %d\n", port->actor_port_number);
+                               pr_debug("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:
-                               dprintk("Received Marker Response on port %d\n", port->actor_port_number);
+                               pr_debug("Received Marker Response on port %d\n", port->actor_port_number);
                                ad_marker_response_received((struct bond_marker *)lacpdu, port);
                                break;
 
                        default:
-                               dprintk("Received an unknown Marker subtype on slot %d\n", port->actor_port_number);
+                               pr_debug("Received an unknown Marker subtype on slot %d\n", port->actor_port_number);
                        }
                }
        }
@@ -2209,7 +2273,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave)
 
        port->actor_admin_port_key &= ~AD_SPEED_KEY_BITS;
        port->actor_oper_port_key=port->actor_admin_port_key |= (__get_link_speed(port) << 1);
-       dprintk("Port %d changed speed\n", port->actor_port_number);
+       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;
@@ -2237,7 +2301,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
 
        port->actor_admin_port_key &= ~AD_DUPLEX_KEY_BITS;
        port->actor_oper_port_key=port->actor_admin_port_key |= __get_duplex(port);
-       dprintk("Port %d changed duplex\n", port->actor_port_number);
+       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;
@@ -2346,7 +2410,7 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
 int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
 {
        struct slave *slave, *start_at;
-       struct bonding *bond = dev->priv;
+       struct bonding *bond = netdev_priv(dev);
        int slave_agg_no;
        int slaves_in_agg;
        int agg_id;
@@ -2426,7 +2490,7 @@ out:
 
 int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type* ptype, struct net_device *orig_dev)
 {
-       struct bonding *bond = dev->priv;
+       struct bonding *bond = netdev_priv(dev);
        struct slave *slave = NULL;
        int ret = NET_RX_DROP;
 
@@ -2437,7 +2501,8 @@ int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct pac
                goto out;
 
        read_lock(&bond->lock);
-       slave = bond_get_slave_by_dev((struct bonding *)dev->priv, orig_dev);
+       slave = bond_get_slave_by_dev((struct bonding *)netdev_priv(dev),
+                                       orig_dev);
        if (!slave)
                goto out_unlock;