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