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