aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/bonding
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/bonding
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/net/bonding')
-rw-r--r--drivers/net/bonding/Makefile8
-rw-r--r--drivers/net/bonding/bond_3ad.c2451
-rw-r--r--drivers/net/bonding/bond_3ad.h300
-rw-r--r--drivers/net/bonding/bond_alb.c1696
-rw-r--r--drivers/net/bonding/bond_alb.h141
-rw-r--r--drivers/net/bonding/bond_main.c4708
-rw-r--r--drivers/net/bonding/bonding.h252
7 files changed, 9556 insertions, 0 deletions
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
new file mode 100644
index 00000000000..cf50384b469
--- /dev/null
+++ b/drivers/net/bonding/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the Ethernet Bonding driver
+#
+
+obj-$(CONFIG_BONDING) += bonding.o
+
+bonding-objs := bond_main.o bond_3ad.o bond_alb.o
+
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
new file mode 100644
index 00000000000..6233c4ffb80
--- /dev/null
+++ b/drivers/net/bonding/bond_3ad.c
@@ -0,0 +1,2451 @@
+/*
+ * 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.
+ *
+ *
+ * Changes:
+ *
+ * 2003/05/01 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
+ * Amir Noam <amir.noam at intel dot com>
+ * - Added support for lacp_rate module param.
+ *
+ * 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
+ * - Based on discussion on mailing list, changed locking scheme
+ * to use lock/unlock or lock_bh/unlock_bh appropriately instead
+ * of lock_irqsave/unlock_irqrestore. The new scheme helps exposing
+ * hidden bugs and solves system hangs that occurred due to the fact
+ * that holding lock_irqsave doesn't prevent softirqs from running.
+ * This also increases total throughput since interrupts are not
+ * blocked on each transmitted packets or monitor timeout.
+ *
+ * 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
+ * - Renamed bond_3ad_link_status_changed() to
+ * bond_3ad_handle_link_change() for compatibility with TLB.
+ *
+ * 2003/05/20 - Amir Noam <amir.noam at intel dot com>
+ * - Fix long fail over time when releasing last slave of an active
+ * aggregator - send LACPDU on unbind of slave to tell partner this
+ * port is no longer aggregatable.
+ *
+ * 2003/06/25 - Tsippy Mendelson <tsippy.mendelson at intel dot com>
+ * - Send LACPDU as highest priority packet to further fix the above
+ * problem on very high Tx traffic load where packets may get dropped
+ * by the slave.
+ *
+ * 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com>
+ * - Code cleanup and style changes
+ */
+
+//#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 "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
+//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 void __ntohs_lacpdu(struct lacpdu *lacpdu);
+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 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 marker *marker_info, struct port *port);
+static void ad_marker_response_received(struct 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
+ */
+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;
+
+ 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
+/**
+ * __ntohs_lacpdu - convert the contents of a LACPDU to host byte order
+ * @lacpdu: the speicifed lacpdu
+ *
+ * For each multi-byte field in the lacpdu, convert its content
+ */
+static void __ntohs_lacpdu(struct lacpdu *lacpdu)
+{
+ if (lacpdu) {
+ lacpdu->actor_system_priority = ntohs(lacpdu->actor_system_priority);
+ lacpdu->actor_key = ntohs(lacpdu->actor_key);
+ lacpdu->actor_port_priority = ntohs(lacpdu->actor_port_priority);
+ lacpdu->actor_port = ntohs(lacpdu->actor_port);
+ lacpdu->partner_system_priority = ntohs(lacpdu->partner_system_priority);
+ lacpdu->partner_key = ntohs(lacpdu->partner_key);
+ lacpdu->partner_port_priority = ntohs(lacpdu->partner_port_priority);
+ lacpdu->partner_port = ntohs(lacpdu->partner_port);
+ lacpdu->collector_max_delay = ntohs(lacpdu->collector_max_delay);
+ }
+}
+
+/**
+ * __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 = lacpdu->actor_port;
+ port->partner_oper_port_priority = lacpdu->actor_port_priority;
+ port->partner_oper_system = lacpdu->actor_system;
+ port->partner_oper_system_priority = lacpdu->actor_system_priority;
+ port->partner_oper_key = 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 ((lacpdu->actor_port != port->partner_oper_port_number) ||
+ (lacpdu->actor_port_priority != port->partner_oper_port_priority) ||
+ MAC_ADDRESS_COMPARE(&(lacpdu->actor_system), &(port->partner_oper_system)) ||
+ (lacpdu->actor_system_priority != port->partner_oper_system_priority) ||
+ (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
+ * match and the PDU parameter partner_state.aggregation has the same value as
+ * actor_oper_port_state.aggregation and lacp will actively maintain the link
+ * in the aggregation. Matched is also set to TRUE if the value of
+ * actor_state.aggregation in the received PDU is set to FALSE, i.e., indicates
+ * an individual link and lacp will actively maintain the link. Otherwise,
+ * matched is set to FALSE. LACP is considered to be actively maintaining the
+ * link if either the PDU's actor_state.lacp_activity variable is TRUE or both
+ * the actor's actor_oper_port_state.lacp_activity and the PDU's
+ * partner_state.lacp_activity variables are TRUE.
+ */
+static void __choose_matched(struct lacpdu *lacpdu, struct port *port)
+{
+ // validate lacpdu and port
+ if (lacpdu && port) {
+ // check if all parameters are alike
+ if (((lacpdu->partner_port == port->actor_port_number) &&
+ (lacpdu->partner_port_priority == port->actor_port_priority) &&
+ !MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) &&
+ (lacpdu->partner_system_priority == port->actor_system_priority) &&
+ (lacpdu->partner_key == port->actor_oper_port_key) &&
+ ((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) ||
+ // or this is individual link(aggregation == FALSE)
+ ((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0)
+ ) {
+ // update the state machine Matched variable
+ port->sm_vars |= AD_PORT_MATCHED;
+ } else {
+ port->sm_vars &= ~AD_PORT_MATCHED;
+ }
+ }
+}
+
+/**
+ * __update_ntt - update a port's ntt variable from a received lacpdu
+ * @lacpdu: the lacpdu we've received
+ * @port: the port we're looking at
+ *
+ * Updates the value of the ntt variable, using parameter values from a newly
+ * received lacpdu. The parameter values for the partner carried in the
+ * received PDU are compared with the corresponding operational parameter
+ * values for the Actor. If one or more of the comparisons shows that the
+ * value(s) received in the PDU differ from the current operational values,
+ * then ntt is set to TRUE. Otherwise, ntt remains unchanged.
+ */
+static void __update_ntt(struct lacpdu *lacpdu, struct port *port)
+{
+ // validate lacpdu and port
+ if (lacpdu && port) {
+ // check if any parameter is different
+ if ((lacpdu->partner_port != port->actor_port_number) ||
+ (lacpdu->partner_port_priority != port->actor_port_priority) ||
+ MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) ||
+ (lacpdu->partner_system_priority != port->actor_system_priority) ||
+ (lacpdu->partner_key != port->actor_oper_port_key) ||
+ ((lacpdu->partner_state & AD_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & AD_STATE_LACP_ACTIVITY)) ||
+ ((lacpdu->partner_state & AD_STATE_LACP_TIMEOUT) != (port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT)) ||
+ ((lacpdu->partner_state & AD_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION)) ||
+ ((lacpdu->partner_state & AD_STATE_AGGREGATION) != (port->actor_oper_port_state & AD_STATE_AGGREGATION))
+ ) {
+ // set ntt to be TRUE
+ port->ntt = 1;
+ }
+ }
+}
+
+/**
+ * __attach_bond_to_agg
+ * @port: the port we're looking at
+ *
+ * Handle the attaching of the port's control parser/multiplexer and the
+ * aggregator. This function does nothing since the parser/multiplexer of the
+ * receive and the parser/multiplexer of the aggregator are already combined.
+ */
+static void __attach_bond_to_agg(struct port *port)
+{
+ port=NULL; // just to satisfy the compiler
+ // This function does nothing since the parser/multiplexer of the receive
+ // and the parser/multiplexer of the aggregator are already combined
+}
+
+/**
+ * __detach_bond_from_agg
+ * @port: the port we're looking at
+ *
+ * Handle the detaching of the port's control parser/multiplexer from the
+ * aggregator. This function does nothing since the parser/multiplexer of the
+ * receive and the parser/multiplexer of the aggregator are already combined.
+ */
+static void __detach_bond_from_agg(struct port *port)
+{
+ port=NULL; // just to satisfy the compiler
+ // This function does nothing sience the parser/multiplexer of the receive
+ // and the parser/multiplexer of the aggregator are already combined
+}
+
+/**
+ * __agg_ports_are_ready - check if all ports in an aggregator are ready
+ * @aggregator: the aggregator we're looking at
+ *
+ */
+static int __agg_ports_are_ready(struct aggregator *aggregator)
+{
+ struct port *port;
+ int retval = 1;
+
+ if (aggregator) {
+ // scan all ports in this aggregator to verfy if they are all ready
+ for (port=aggregator->lag_ports; port; port=port->next_port_in_aggregator) {
+ if (!(port->sm_vars & AD_PORT_READY_N)) {
+ retval = 0;
+ break;
+ }
+ }
+ }
+
+ return retval;
+}
+
+/**
+ * __set_agg_ports_ready - set value of Ready bit in all ports of an aggregator
+ * @aggregator: the aggregator we're looking at
+ * @val: Should the ports' ready bit be set on or off
+ *
+ */
+static void __set_agg_ports_ready(struct aggregator *aggregator, int val)
+{
+ struct port *port;
+
+ for (port=aggregator->lag_ports; port; port=port->next_port_in_aggregator) {
+ if (val) {
+ port->sm_vars |= AD_PORT_READY;
+ } else {
+ port->sm_vars &= ~AD_PORT_READY;
+ }
+ }
+}
+
+/**
+ * __get_agg_bandwidth - get the total bandwidth of an aggregator
+ * @aggregator: the aggregator we're looking at
+ *
+ */
+static u32 __get_agg_bandwidth(struct aggregator *aggregator)
+{
+ u32 bandwidth=0;
+ u32 basic_speed;
+
+ if (aggregator->num_of_ports) {
+ basic_speed = __get_link_speed(aggregator->lag_ports);
+ switch (basic_speed) {
+ case AD_LINK_SPEED_BITMASK_1MBPS:
+ bandwidth = aggregator->num_of_ports;
+ break;
+ case AD_LINK_SPEED_BITMASK_10MBPS:
+ bandwidth = aggregator->num_of_ports * 10;
+ break;
+ case AD_LINK_SPEED_BITMASK_100MBPS:
+ bandwidth = aggregator->num_of_ports * 100;
+ break;
+ case AD_LINK_SPEED_BITMASK_1000MBPS:
+ bandwidth = aggregator->num_of_ports * 1000;
+ break;
+ default:
+ bandwidth=0; // to silent the compilor ....
+ }
+ }
+ return bandwidth;
+}
+
+/**
+ * __get_active_agg - get the current active aggregator
+ * @aggregator: the aggregator we're looking at
+ *
+ */
+static struct aggregator *__get_active_agg(struct aggregator *aggregator)
+{
+ struct aggregator *retval = NULL;
+
+ for (; aggregator; aggregator = __get_next_agg(aggregator)) {
+ if (aggregator->is_active) {
+ retval = aggregator;
+ break;
+ }
+ }
+
+ return retval;
+}
+
+/**
+ * __update_lacpdu_from_port - update a port's lacpdu fields
+ * @port: the port we're looking at
+ *
+ */
+static inline void __update_lacpdu_from_port(struct port *port)
+{
+ struct lacpdu *lacpdu = &port->lacpdu;
+
+ /* update current actual Actor parameters */
+ /* lacpdu->subtype initialized
+ * lacpdu->version_number initialized
+ * lacpdu->tlv_type_actor_info initialized
+ * lacpdu->actor_information_length initialized
+ */
+
+ lacpdu->actor_system_priority = port->actor_system_priority;
+ lacpdu->actor_system = port->actor_system;
+ lacpdu->actor_key = port->actor_oper_port_key;
+ lacpdu->actor_port_priority = port->actor_port_priority;
+ lacpdu->actor_port = port->actor_port_number;
+ lacpdu->actor_state = port->actor_oper_port_state;
+
+ /* lacpdu->reserved_3_1 initialized
+ * lacpdu->tlv_type_partner_info initialized
+ * lacpdu->partner_information_length initialized
+ */
+
+ lacpdu->partner_system_priority = port->partner_oper_system_priority;
+ lacpdu->partner_system = port->partner_oper_system;
+ lacpdu->partner_key = port->partner_oper_key;
+ lacpdu->partner_port_priority = port->partner_oper_port_priority;
+ lacpdu->partner_port = port->partner_oper_port_number;
+ lacpdu->partner_state = port->partner_oper_port_state;
+
+ /* lacpdu->reserved_3_2 initialized
+ * lacpdu->tlv_type_collector_info initialized
+ * lacpdu->collector_information_length initialized
+ * collector_max_delay initialized
+ * reserved_12[12] initialized
+ * tlv_type_terminator initialized
+ * terminator_length initialized
+ * reserved_50[50] initialized
+ */
+
+ /* Convert all non u8 parameters to Big Endian for transmit */
+ __ntohs_lacpdu(lacpdu);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+// ================= main 802.3ad protocol code ======================================
+//////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * ad_lacpdu_send - send out a lacpdu packet on a given port
+ * @port: the port we're looking at
+ *
+ * Returns: 0 on success
+ * < 0 on error
+ */
+static int ad_lacpdu_send(struct port *port)
+{
+ struct slave *slave = port->slave;
+ struct sk_buff *skb;
+ struct lacpdu_header *lacpdu_header;
+ int length = sizeof(struct lacpdu_header);
+ struct mac_addr lacpdu_multicast_address = AD_MULTICAST_LACPDU_ADDR;
+
+ skb = dev_alloc_skb(length);
+ if (!skb) {
+ return -ENOMEM;
+ }
+
+ skb->dev = slave->dev;
+ skb->mac.raw = skb->data;
+ skb->nh.raw = skb->data + ETH_HLEN;
+ skb->protocol = PKT_TYPE_LACPDU;
+ skb->priority = TC_PRIO_CONTROL;
+
+ lacpdu_header = (struct lacpdu_header *)skb_put(skb, length);
+
+ lacpdu_header->ad_header.destination_address = lacpdu_multicast_address;
+ /* Note: source addres is set to be the member's PERMANENT address, because we use it
+ to identify loopback lacpdus in receive. */
+ lacpdu_header->ad_header.source_address = *((struct mac_addr *)(slave->perm_hwaddr));
+ lacpdu_header->ad_header.length_type = PKT_TYPE_LACPDU;
+
+ lacpdu_header->lacpdu = port->lacpdu; // struct copy
+
+ dev_queue_xmit(skb);
+
+ return 0;
+}
+
+/**
+ * ad_marker_send - send marker information/response on a given port
+ * @port: the port we're looking at
+ * @marker: marker data to send
+ *
+ * Returns: 0 on success
+ * < 0 on error
+ */
+static int ad_marker_send(struct port *port, struct marker *marker)
+{
+ struct slave *slave = port->slave;
+ struct sk_buff *skb;