diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 3a17fd207ec6ccdfcd0f9d132e1f7779db2fc651..b9816b7f319fb9db40a0b935435e54eeb9aa79e0 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 ================== */
@@ -1951,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 =
@@ -2307,6 +2301,52 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave,
 	return ret;
 }
 
+/**
+ * ad_update_actor_keys - Update the oper / admin keys for a port based on
+ * its current speed and duplex settings.
+ *
+ * @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.
+ */
+static void ad_update_actor_keys(struct port *port, bool reset)
+{
+	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;
+	}
+	port->actor_oper_port_key = port->actor_admin_port_key;
+
+	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_speed_changed - handle a slave's speed change indication
  * @slave: slave struct to work on
@@ -2328,14 +2368,8 @@ void bond_3ad_adapter_speed_changed(struct slave *slave)
 
 	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;
+	ad_update_actor_keys(port, false);
 	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);
 }
@@ -2361,17 +2395,9 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
 
 	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;
+	ad_update_actor_keys(port, false);
 	netdev_dbg(slave->bond->dev, "Port %d slave %s changed 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);
 }
@@ -2404,26 +2430,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);