itle>
aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/net/bonding/bond_3ad.c
blob: cb3c6faa7888c55b74771053a4c80660ef317a4a (plain) (tree)



















                                                                               










                             
                              























































                                                                                        
                                                  













                                                                          
























                                                                          
                                                                         











                                                                    

                                                                                        





























































































































































































                                                                                 

                                       

























                                                                                 



                                                                























































                                                                                                                          
























































                                                                                           

                                                                                      
                                                                 

                                                                                          

































































                                                                                                            

                                                                                               
                                                                                                 

                                                                                                   





























































                                                                                                                                       

                                                                                           
                                                                                              

                                                                                               




























                                                                                                                                 

                                                                                          
                                                                                            

                                                                                              











































































































                                                                                                                                       


                                                                     









































                                                                         
                                                                           
                                                  


                                                                       






                                                          
                                                                                    
                                                           


                                                                                










                                                              


























                                                                                      
                                  
                                                         

























                                                                                             
                                                                        


                                          

                                                       









                                                                            
                                  
                                                         

                                        
                                                                          

















































































































































































































































                                                                                                                                                                     



                                                                                                    















































































































































































                                                                                                                                                                                      
                                                                                          
                                                                                         
                                                              





































































                                                                                                                                           

                                                                                                              


































































































































                                                                                                                                                    


                                                                                               







































































































































































                                                                                                                         
                                  































                                                                                                                                                                                                                   

                                                                    
 
                                  


                                                               
                                                                 

















                                                                                      

                                                                   









































                                                                             
                                                                    




























































                                                                                                                   

                                                                                            









































































                                                                                                                 


                                                                        





























                                                                                                                                                                                 

                                                                                                          




























                                                                                                                                


                                                                                                    





                                                                                                              

                                                                                                  
























                                                                                                                                          

                                                                                                                  























                                                                                              
                                                             
 

                                                                 


















                                                                                              

                                                                                                   










                                                                                 

                                                                                             















                                                                      
                                                                        






















                                                                                          

                                                                                              




                                          






                                                                                                             
                                                                           

                                                                                                             
                                                                                            



                                                                                                          
                                                                                                






















                                                                                                                    


                                                                    
























                                                                                              


                                                                             

























                                                                                   


                                                                                      






















                                                                                                                                                         
  
                                                           


                                                                     

                                                         




                                                                      
                                                                               













                                                    



































                                                                                                     
















                                                           

                                                                           







                                           


                                                                 


                         
                                                                       












                                                                              

                                                                                       



























                                                                              
                                                                                                                             




                                         


                                     
                                       
                         

                               

                                                                             
                                












                                                                             
/*
 * Copyright(c) 1999 - 2004 Intel Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The full GNU General Public License is included in this distribution in the
 * file called LICENSE.
 *
 */

//#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/if_bonding.h>
#include <linux/pkt_sched.h>
#include <net/net_namespace.h>
#include "bonding.h"
#include "bond_3ad.h"

// 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)
#define AD_FAST_PERIODIC_TIME      1
#define AD_SLOW_PERIODIC_TIME      30
#define AD_SHORT_TIMEOUT_TIME      (3*AD_FAST_PERIODIC_TIME)
#define AD_LONG_TIMEOUT_TIME       (3*AD_SLOW_PERIODIC_TIME)
#define AD_CHURN_DETECTION_TIME    60
#define AD_AGGREGATE_WAIT_TIME     2

// 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
#define AD_STATE_SYNCHRONIZATION 0x8
#define AD_STATE_COLLECTING      0x10
#define AD_STATE_DISTRIBUTING    0x20
#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)
#define AD_PORT_BEGIN           0x1
#define AD_PORT_LACP_ENABLED    0x2
#define AD_PORT_ACTOR_CHURN     0x4
#define AD_PORT_PARTNER_CHURN   0x8
#define AD_PORT_READY           0x10
#define AD_PORT_READY_N         0x20
#define AD_PORT_MATCHED         0x40
#define AD_PORT_STANDBY         0x80
#define AD_PORT_SELECTED        0x100
#define AD_PORT_MOVED           0x200

// 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)

static struct mac_addr null_mac_addr = {{0, 0, 0, 0, 0, 0}};
static u16 ad_ticks_per_sec;
static const int ad_delta_in_ticks = (AD_TIMER_INTERVAL * HZ) / 1000;

// ================= 3AD api to bonding and kernel code ==================
static u16 __get_link_speed(struct port *port);
static u8 __get_duplex(struct port *port);
static inline void __initialize_port_locks(struct port *port);
//conversions
static u16 __ad_timer_to_ticks(u16 timer_type, u16 Par);


// ================= ad code helper functions ==================
//needed by ad_rx_machine(...)
static void __record_pdu(struct lacpdu *lacpdu, struct port *port);
static void __record_default(struct port *port);
static void __update_selected(struct lacpdu *lacpdu, struct port *port);
static void __update_default_selected(struct port *port);
static void __choose_matched(struct lacpdu *lacpdu, struct port *port);
static void __update_ntt(struct lacpdu *lacpdu, struct port *port);

//needed for ad_mux_machine(..)
static void __attach_bond_to_agg(struct port *port);
static void __detach_bond_from_agg(struct port *port);
static int __agg_ports_are_ready(struct aggregator *aggregator);
static void __set_agg_ports_ready(struct aggregator *aggregator, int val);

//needed for ad_agg_selection_logic(...)
static u32 __get_agg_bandwidth(struct aggregator *aggregator);
static struct aggregator *__get_active_agg(struct aggregator *aggregator);


// ================= 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_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_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_initialize_lacpdu(struct lacpdu *Lacpdu);
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);


/////////////////////////////////////////////////////////////////////////////////
// ================= api to bonding and kernel code ==================
/////////////////////////////////////////////////////////////////////////////////

/**
 * __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)
{
	if (port->slave == NULL) {
		return NULL;
	}

	return bond_get_bond_by_slave(port->slave);
}

/**
 * __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)
{
	if (bond->slave_cnt == 0) {
		return NULL;
	}

	return &(SLAVE_AD_INFO(bond->first_slave).port);
}

/**
 * __get_next_port - get the next port in the bond
 * @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.
 */
static inline struct port *__get_next_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)) {
		return NULL;
	}

	return &(SLAVE_AD_INFO(slave->next).port);
}

/**
 * __get_first_agg - get the first aggregator in the bond
 * @bond: the bond we're looking at
 *
 * Return the aggregator of the first slave in @bond, or %NULL if it can't be
 * found.
 */
static inline struct aggregator *__get_first_agg(struct port *port)
{
	struct bonding *bond = __get_bond_by_port(port);

	// If there's no bond for this port, or bond has no slaves
	if ((bond == NULL) || (bond->slave_cnt == 0)) {
		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;
	}

	return &(SLAVE_AD_INFO(slave->next).aggregator);
}

/**
 * __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);
}

/**
 * __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);
	}
}

/**
 * __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)
{
	return(port->slave->state == BOND_STATE_ACTIVE);
}

/**
 * __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.
 */
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_INFO(bond).agg_select_mode;
}

/**
 * __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)
{
	struct bonding *bond = __get_bond_by_port(port);

	if (bond == NULL) {
		return 0;
	}

	return BOND_AD_INFO(bond).agg_select_timer ? 1 : 0;
}

/**
 * __get_rx_machine_lock - lock the port's RX machine
 * @port: the port we're looking at
 *
 */
static inline void __get_rx_machine_lock(struct port *port)
{
	spin_lock(&(SLAVE_AD_INFO(port->slave).rx_machine_lock));
}

/**
 * __release_rx_machine_lock - unlock the port's RX machine
 * @port: the port we're looking at
 *
 */
static inline void __release_rx_machine_lock(struct port *port)
{
	spin_unlock(&(SLAVE_AD_INFO(port->slave).rx_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:
 *     0,
 *     %AD_LINK_SPEED_BITMASK_10MBPS,
 *     %AD_LINK_SPEED_BITMASK_100MBPS,
 *     %AD_LINK_SPEED_BITMASK_1000MBPS,
 *     %AD_LINK_SPEED_BITMASK_10000MBPS
 */
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.*/
	if (slave->link != BOND_LINK_UP) {
		speed=0;
	} else {
		switch (slave->speed) {
		case SPEED_10:
			speed = AD_LINK_SPEED_BITMASK_10MBPS;
			break;

		case SPEED_100:
			speed = AD_LINK_SPEED_BITMASK_100MBPS;
			break;

		case SPEED_1000:
			speed = AD_LINK_SPEED_BITMASK_1000MBPS;
			break;

		case SPEED_10000:
			speed = AD_LINK_SPEED_BITMASK_10000MBPS;
			break;

		default:
			speed = 0; // unknown speed value from ethtool. shouldn't happen
			break;
		}
	}

	dprintk("Port %d Received link speed %d update from adapter\n", port->actor_port_number, speed);
	return speed;
}

/**
 * __get_duplex - get a port's duplex
 * @port: the port we're looking at
 *
 * Return @port's duplex in 802.3ad bitmask format. i.e.:
 *     0x01 if in full duplex
 *     0x00 otherwise
 */
static u8 __get_duplex(struct port *port)
{
	struct slave *slave = port->slave;

	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 {
		switch (slave->duplex) {
		case DUPLEX_FULL:
			retval=0x1;
			dprintk("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);
			break;
		}
	}
	return retval;
}

/**
 * __initialize_port_locks - initialize a port's RX machine spinlock
 * @port: the port we're looking at
 *
 */
static inline void __initialize_port_locks(struct port *port)
{
	// make sure it isn't called twice
	spin_lock_init(&(SLAVE_AD_INFO(port->slave).rx_machine_lock));
}

//conversions

/**
 * __ad_timer_to_ticks - convert a given timer type to AD module ticks
 * @timer_type:	which timer to operate
 * @par: timer parameter. see below
 *
 * 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.
 */
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
		if (par) {	      // for short or long timeout
			retval = (AD_SHORT_TIMEOUT_TIME*ad_ticks_per_sec); // short timeout
		} else {
			retval = (AD_LONG_TIMEOUT_TIME*ad_ticks_per_sec); // long timeout
		}
		break;
	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
		break;
	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
		retval = (AD_AGGREGATE_WAIT_TIME*ad_ticks_per_sec);
		break;
	}
	return retval;
}


/////////////////////////////////////////////////////////////////////////////////
// ================= ad_rx_machine helper functions ==================
/////////////////////////////////////////////////////////////////////////////////

/**
 * __record_pdu - record parameters from a received lacpdu
 * @lacpdu: the lacpdu we've received
 * @port: the port we're looking at
 *
 * Record the parameter values for the Actor carried in a received lacpdu as
 * the current partner operational parameter values and sets
 * actor_oper_port_state.defaulted to FALSE.
 */
static void __record_pdu(struct lacpdu *lacpdu, struct port *port)
{
	// validate lacpdu and port
	if (lacpdu && port) {
		// 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);

		// 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;
		} else {
			port->partner_oper_port_state &= ~AD_STATE_SYNCHRONIZATION;
		}
	}
}

/**
 * __record_default - record default parameters
 * @port: the port we're looking at
 *
 * This function records the default parameter values for the partner carried
 * in the Partner Admin parameters as the current partner operational parameter
 * values and sets actor_oper_port_state.defaulted to TRUE.
 */
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;

		// set actor_oper_port_state.defaulted to true
		port->actor_oper_port_state |= AD_STATE_DEFAULTED;
	}
}

/**
 * __update_selected - update a port's Selected variable from a received lacpdu
 * @lacpdu: the lacpdu we've received
 * @port: the port we're looking at
 *
 * Update the value of the selected variable, using parameter values from a
 * newly received lacpdu. The parameter values for the Actor carried in the
 * received PDU are compared with the corresponding operational parameter
 * values for the ports partner. If one or more of the comparisons shows that
 * the value(s) received in the PDU differ from the current operational values,
 * then selected is set to FALSE and actor_oper_port_state.synchronization is
 * set to out_of_sync. Otherwise, selected remains unchanged.
 */
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))
		   ) {
			// update the state machine Selected variable
			port->sm_vars &= ~AD_PORT_SELECTED;
		}
	}
}

/**
 * __update_default_selected - update a port's Selected variable from Partner
 * @port: the port we're looking at
 *
 * This function updates the value of the selected variable, using the partner
 * administrative parameter values. The administrative values are compared with
 * the corresponding operational parameter values for the partner. If one or
 * more of the comparisons shows that the administrative value(s) differ from
 * the current operational values, then Selected is set to FALSE and
 * actor_oper_port_state.synchronization is set to OUT_OF_SYNC. Otherwise,
 * Selected remains unchanged.
 */
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))
		   ) {
			// update the state machine Selected variable
			port->sm_vars &= ~AD_PORT_SELECTED;
		}
	}
}

/**
 * __choose_matched - update a port's matched variable from a received lacpdu
 * @lacpdu: the lacpdu we've received
 * @port: the port we're looking at
 *
 * Update the value of the matched variable, using parameter values from a
 * newly received lacpdu. Parameter values for the partner carried in the
 * received PDU are compared with the corresponding operational parameter
 * values for the actor. Matched is set to TRUE if all of these parameters