summaryrefslogtreecommitdiff
path: root/drivers/net/bonding/bond_3ad.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/bonding/bond_3ad.c')
-rw-r--r--drivers/net/bonding/bond_3ad.c113
1 files changed, 51 insertions, 62 deletions
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 3c4535884..940e2ebbd 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -127,6 +127,7 @@ static void ad_marker_info_received(struct bond_marker *marker_info,
struct port *port);
static void ad_marker_response_received(struct bond_marker *marker,
struct port *port);
+static void ad_update_actor_keys(struct port *port, bool reset);
/* ================= api to bonding and kernel code ================== */
@@ -327,14 +328,12 @@ static u16 __get_link_speed(struct port *port)
static u8 __get_duplex(struct port *port)
{
struct slave *slave = port->slave;
- u8 retval;
+ u8 retval = 0x0;
/* 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 {
+ if (slave->link == BOND_LINK_UP) {
switch (slave->duplex) {
case DUPLEX_FULL:
retval = 0x1;
@@ -1953,14 +1952,7 @@ void bond_3ad_bind_slave(struct slave *slave)
* user key
*/
port->actor_admin_port_key = bond->params.ad_user_port_key << 6;
- port->actor_admin_port_key |= __get_duplex(port);
- port->actor_admin_port_key |= (__get_link_speed(port) << 1);
- port->actor_oper_port_key = port->actor_admin_port_key;
- /* if the port is not full duplex, then the port should be not
- * lacp Enabled
- */
- if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS))
- port->sm_vars &= ~AD_PORT_LACP_ENABLED;
+ ad_update_actor_keys(port, false);
/* actor system is the bond's system */
port->actor_system = BOND_AD_INFO(bond).system.sys_mac_addr;
port->actor_system_priority =
@@ -2310,45 +2302,60 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave,
}
/**
- * bond_3ad_adapter_speed_changed - handle a slave's speed change indication
- * @slave: slave struct to work on
+ * ad_update_actor_keys - Update the oper / admin keys for a port based on
+ * its current speed and duplex settings.
*
- * Handle reselection of aggregator (if needed) for this port.
+ * @port: the port we'are looking at
+ * @reset: Boolean to just reset the speed and the duplex part of the key
+ *
+ * The logic to change the oper / admin keys is:
+ * (a) A full duplex port can participate in LACP with partner.
+ * (b) When the speed is changed, LACP need to be reinitiated.
*/
-void bond_3ad_adapter_speed_changed(struct slave *slave)
+static void ad_update_actor_keys(struct port *port, bool reset)
{
- struct port *port;
-
- port = &(SLAVE_AD_INFO(slave)->port);
-
- /* if slave is null, the whole port is not initialized */
- if (!port->slave) {
- netdev_warn(slave->bond->dev, "speed changed for uninitialized port on %s\n",
- slave->dev->name);
- return;
+ u8 duplex = 0;
+ u16 ospeed = 0, speed = 0;
+ u16 old_oper_key = port->actor_oper_port_key;
+
+ port->actor_admin_port_key &= ~(AD_SPEED_KEY_MASKS|AD_DUPLEX_KEY_MASKS);
+ if (!reset) {
+ speed = __get_link_speed(port);
+ ospeed = (old_oper_key & AD_SPEED_KEY_MASKS) >> 1;
+ duplex = __get_duplex(port);
+ port->actor_admin_port_key |= (speed << 1) | duplex;
}
-
- spin_lock_bh(&slave->bond->mode_lock);
-
- port->actor_admin_port_key &= ~AD_SPEED_KEY_MASKS;
- port->actor_admin_port_key |= __get_link_speed(port) << 1;
port->actor_oper_port_key = port->actor_admin_port_key;
- netdev_dbg(slave->bond->dev, "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;
- spin_unlock_bh(&slave->bond->mode_lock);
+ if (old_oper_key != port->actor_oper_port_key) {
+ /* Only 'duplex' port participates in LACP */
+ if (duplex)
+ port->sm_vars |= AD_PORT_LACP_ENABLED;
+ else
+ port->sm_vars &= ~AD_PORT_LACP_ENABLED;
+
+ if (!reset) {
+ if (!speed) {
+ netdev_err(port->slave->dev,
+ "speed changed to 0 for port %s",
+ port->slave->dev->name);
+ } else if (duplex && ospeed != speed) {
+ /* Speed change restarts LACP state-machine */
+ port->sm_vars |= AD_PORT_BEGIN;
+ }
+ }
+ }
}
/**
- * bond_3ad_adapter_duplex_changed - handle a slave's duplex change indication
+ * bond_3ad_adapter_speed_duplex_changed - handle a slave's speed / duplex
+ * change indication
+ *
* @slave: slave struct to work on
*
* Handle reselection of aggregator (if needed) for this port.
*/
-void bond_3ad_adapter_duplex_changed(struct slave *slave)
+void bond_3ad_adapter_speed_duplex_changed(struct slave *slave)
{
struct port *port;
@@ -2356,25 +2363,16 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
/* if slave is null, the whole port is not initialized */
if (!port->slave) {
- netdev_warn(slave->bond->dev, "duplex changed for uninitialized port on %s\n",
+ netdev_warn(slave->bond->dev,
+ "speed/duplex changed for uninitialized port %s\n",
slave->dev->name);
return;
}
spin_lock_bh(&slave->bond->mode_lock);
-
- port->actor_admin_port_key &= ~AD_DUPLEX_KEY_MASKS;
- port->actor_admin_port_key |= __get_duplex(port);
- port->actor_oper_port_key = port->actor_admin_port_key;
- netdev_dbg(slave->bond->dev, "Port %d slave %s changed duplex\n",
+ ad_update_actor_keys(port, false);
+ netdev_dbg(slave->bond->dev, "Port %d slave %s changed speed/duplex\n",
port->actor_port_number, slave->dev->name);
- if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)
- port->sm_vars |= AD_PORT_LACP_ENABLED;
- /* there is no need to reselect a new aggregator, just signal the
- * state machines to reinitialize
- */
- port->sm_vars |= AD_PORT_BEGIN;
-
spin_unlock_bh(&slave->bond->mode_lock);
}
@@ -2406,26 +2404,17 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
* on link up we are forcing recheck on the duplex and speed since
* some of he adaptors(ce1000.lan) report.
*/
- port->actor_admin_port_key &= ~(AD_DUPLEX_KEY_MASKS|AD_SPEED_KEY_MASKS);
if (link == BOND_LINK_UP) {
port->is_enabled = true;
- port->actor_admin_port_key |=
- (__get_link_speed(port) << 1) | __get_duplex(port);
- if (port->actor_admin_port_key & AD_DUPLEX_KEY_MASKS)
- port->sm_vars |= AD_PORT_LACP_ENABLED;
+ ad_update_actor_keys(port, false);
} else {
/* link has failed */
port->is_enabled = false;
- port->sm_vars &= ~AD_PORT_LACP_ENABLED;
+ ad_update_actor_keys(port, true);
}
- port->actor_oper_port_key = port->actor_admin_port_key;
netdev_dbg(slave->bond->dev, "Port %d changed link status to %s\n",
port->actor_port_number,
link == BOND_LINK_UP ? "UP" : "DOWN");
- /* there is no need to reselect a new aggregator, just signal the
- * state machines to reinitialize
- */
- port->sm_vars |= AD_PORT_BEGIN;
spin_unlock_bh(&slave->bond->mode_lock);