OSDN Git Service

ice: Add code to control FW LLDP and DCBX
authorAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Thu, 28 Feb 2019 23:24:31 +0000 (15:24 -0800)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Thu, 18 Apr 2019 15:38:48 +0000 (08:38 -0700)
This patch adds code to start or stop LLDP and DCBX in firmware through
use of ethtool private flags.

Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ice/ice.h
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
drivers/net/ethernet/intel/ice/ice_dcb.c
drivers/net/ethernet/intel/ice/ice_dcb.h
drivers/net/ethernet/intel/ice/ice_ethtool.c

index 6ca1094..878a751 100644 (file)
@@ -325,6 +325,8 @@ enum ice_pf_flags {
        ICE_FLAG_DCB_CAPABLE,
        ICE_FLAG_DCB_ENA,
        ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA,
+       ICE_FLAG_DISABLE_FW_LLDP,
+       ICE_FLAG_ETHTOOL_CTXT,          /* set when ethtool holds RTNL lock */
        ICE_PF_FLAGS_NBITS              /* must be last */
 };
 
index cda9382..583f92d 100644 (file)
@@ -1200,6 +1200,16 @@ struct ice_aqc_lldp_set_mib_change {
        u8 reserved[15];
 };
 
+/* Stop LLDP (direct 0x0A05) */
+struct ice_aqc_lldp_stop {
+       u8 command;
+#define ICE_AQ_LLDP_AGENT_STATE_MASK   BIT(0)
+#define ICE_AQ_LLDP_AGENT_STOP         0x0
+#define ICE_AQ_LLDP_AGENT_SHUTDOWN     ICE_AQ_LLDP_AGENT_STATE_MASK
+#define ICE_AQ_LLDP_AGENT_PERSIST_DIS  BIT(1)
+       u8 reserved[15];
+};
+
 /* Start LLDP (direct 0x0A06) */
 struct ice_aqc_lldp_start {
        u8 command;
@@ -1529,6 +1539,7 @@ struct ice_aq_desc {
                struct ice_aqc_pf_vf_msg virt;
                struct ice_aqc_lldp_get_mib lldp_get_mib;
                struct ice_aqc_lldp_set_mib_change lldp_set_event;
+               struct ice_aqc_lldp_stop lldp_stop;
                struct ice_aqc_lldp_start lldp_start;
                struct ice_aqc_lldp_set_local_mib lldp_set_mib;
                struct ice_aqc_lldp_stop_start_specific_agent lldp_agent_ctrl;
@@ -1639,6 +1650,7 @@ enum ice_adminq_opc {
        /* LLDP commands */
        ice_aqc_opc_lldp_get_mib                        = 0x0A00,
        ice_aqc_opc_lldp_set_mib_change                 = 0x0A01,
+       ice_aqc_opc_lldp_stop                           = 0x0A05,
        ice_aqc_opc_lldp_start                          = 0x0A06,
        ice_aqc_opc_get_cee_dcb_cfg                     = 0x0A07,
        ice_aqc_opc_lldp_set_local_mib                  = 0x0A08,
index 0df244c..8bbf48e 100644 (file)
@@ -60,7 +60,7 @@ ice_aq_get_lldp_mib(struct ice_hw *hw, u8 bridge_type, u8 mib_type, void *buf,
  * Enable or Disable posting of an event on ARQ when LLDP MIB
  * associated with the interface changes (0x0A01)
  */
-static enum ice_status
+enum ice_status
 ice_aq_cfg_lldp_mib_change(struct ice_hw *hw, bool ena_update,
                           struct ice_sq_cd *cd)
 {
@@ -78,6 +78,32 @@ ice_aq_cfg_lldp_mib_change(struct ice_hw *hw, bool ena_update,
 }
 
 /**
+ * ice_aq_stop_lldp
+ * @hw: pointer to the HW struct
+ * @shutdown_lldp_agent: True if LLDP Agent needs to be Shutdown
+ *                      False if LLDP Agent needs to be Stopped
+ * @cd: pointer to command details structure or NULL
+ *
+ * Stop or Shutdown the embedded LLDP Agent (0x0A05)
+ */
+enum ice_status
+ice_aq_stop_lldp(struct ice_hw *hw, bool shutdown_lldp_agent,
+                struct ice_sq_cd *cd)
+{
+       struct ice_aqc_lldp_stop *cmd;
+       struct ice_aq_desc desc;
+
+       cmd = &desc.params.lldp_stop;
+
+       ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_lldp_stop);
+
+       if (shutdown_lldp_agent)
+               cmd->command |= ICE_AQ_LLDP_AGENT_SHUTDOWN;
+
+       return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+}
+
+/**
  * ice_aq_start_lldp
  * @hw: pointer to the HW struct
  * @cd: pointer to command details structure or NULL
index 39fb20c..e7d4416 100644 (file)
@@ -130,12 +130,26 @@ ice_query_port_ets(struct ice_port_info *pi,
                   struct ice_aqc_port_ets_elem *buf, u16 buf_size,
                   struct ice_sq_cd *cmd_details);
 #ifdef CONFIG_DCB
+enum ice_status
+ice_aq_stop_lldp(struct ice_hw *hw, bool shutdown_lldp_agent,
+                struct ice_sq_cd *cd);
 enum ice_status ice_aq_start_lldp(struct ice_hw *hw, struct ice_sq_cd *cd);
 enum ice_status
 ice_aq_start_stop_dcbx(struct ice_hw *hw, bool start_dcbx_agent,
                       bool *dcbx_agent_status, struct ice_sq_cd *cd);
+enum ice_status
+ice_aq_cfg_lldp_mib_change(struct ice_hw *hw, bool ena_update,
+                          struct ice_sq_cd *cd);
 #else /* CONFIG_DCB */
 static inline enum ice_status
+ice_aq_stop_lldp(struct ice_hw __always_unused *hw,
+                bool __always_unused shutdown_lldp_agent,
+                struct ice_sq_cd __always_unused *cd)
+{
+       return 0;
+}
+
+static inline enum ice_status
 ice_aq_start_lldp(struct ice_hw __always_unused *hw,
                  struct ice_sq_cd __always_unused *cd)
 {
@@ -152,5 +166,14 @@ ice_aq_start_stop_dcbx(struct ice_hw __always_unused *hw,
 
        return 0;
 }
+
+static inline enum ice_status
+ice_aq_cfg_lldp_mib_change(struct ice_hw __always_unused *hw,
+                          bool __always_unused ena_update,
+                          struct ice_sq_cd __always_unused *cd)
+{
+       return 0;
+}
+
 #endif /* CONFIG_DCB */
 #endif /* _ICE_DCB_H_ */
index ea85589..64a4c44 100644 (file)
@@ -4,6 +4,8 @@
 /* ethtool support for ice */
 
 #include "ice.h"
+#include "ice_lib.h"
+#include "ice_dcb_lib.h"
 
 struct ice_stats {
        char stat_string[ETH_GSTRING_LEN];
@@ -132,6 +134,7 @@ struct ice_priv_flag {
 
 static const struct ice_priv_flag ice_gstrings_priv_flags[] = {
        ICE_PRIV_FLAG("link-down-on-close", ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA),
+       ICE_PRIV_FLAG("disable-fw-lldp", ICE_FLAG_DISABLE_FW_LLDP),
 };
 
 #define ICE_PRIV_FLAG_ARRAY_SIZE       ARRAY_SIZE(ice_gstrings_priv_flags)
@@ -404,13 +407,19 @@ static u32 ice_get_priv_flags(struct net_device *netdev)
 static int ice_set_priv_flags(struct net_device *netdev, u32 flags)
 {
        struct ice_netdev_priv *np = netdev_priv(netdev);
+       DECLARE_BITMAP(change_flags, ICE_PF_FLAGS_NBITS);
+       DECLARE_BITMAP(orig_flags, ICE_PF_FLAGS_NBITS);
        struct ice_vsi *vsi = np->vsi;
        struct ice_pf *pf = vsi->back;
+       int ret = 0;
        u32 i;
 
        if (flags > BIT(ICE_PRIV_FLAG_ARRAY_SIZE))
                return -EINVAL;
 
+       set_bit(ICE_FLAG_ETHTOOL_CTXT, pf->flags);
+
+       bitmap_copy(orig_flags, pf->flags, ICE_PF_FLAGS_NBITS);
        for (i = 0; i < ICE_PRIV_FLAG_ARRAY_SIZE; i++) {
                const struct ice_priv_flag *priv_flag;
 
@@ -422,7 +431,79 @@ static int ice_set_priv_flags(struct net_device *netdev, u32 flags)
                        clear_bit(priv_flag->bitno, pf->flags);
        }
 
-       return 0;
+       bitmap_xor(change_flags, pf->flags, orig_flags, ICE_PF_FLAGS_NBITS);
+
+       if (test_bit(ICE_FLAG_DISABLE_FW_LLDP, change_flags)) {
+               if (test_bit(ICE_FLAG_DISABLE_FW_LLDP, pf->flags)) {
+                       enum ice_status status;
+
+                       status = ice_aq_cfg_lldp_mib_change(&pf->hw, false,
+                                                           NULL);
+                       /* If unregistering for LLDP events fails, this is
+                        * not an error state, as there shouldn't be any
+                        * events to respond to.
+                        */
+                       if (status)
+                               dev_info(&pf->pdev->dev,
+                                        "Failed to unreg for LLDP events\n");
+
+                       /* The AQ call to stop the FW LLDP agent will generate
+                        * an error if the agent is already stopped.
+                        */
+                       status = ice_aq_stop_lldp(&pf->hw, true, NULL);
+                       if (status)
+                               dev_warn(&pf->pdev->dev,
+                                        "Fail to stop LLDP agent\n");
+                       /* Use case for having the FW LLDP agent stopped
+                        * will likely not need DCB, so failure to init is
+                        * not a concern of ethtool
+                        */
+                       status = ice_init_pf_dcb(pf);
+                       if (status)
+                               dev_warn(&pf->pdev->dev, "Fail to init DCB\n");
+               } else {
+                       enum ice_status status;
+                       bool dcbx_agent_status;
+
+                       /* AQ command to start FW LLDP agent will return an
+                        * error if the agent is already started
+                        */
+                       status = ice_aq_start_lldp(&pf->hw, NULL);
+                       if (status)
+                               dev_warn(&pf->pdev->dev,
+                                        "Fail to start LLDP Agent\n");
+
+                       /* AQ command to start FW DCBx agent will fail if
+                        * the agent is already started
+                        */
+                       status = ice_aq_start_stop_dcbx(&pf->hw, true,
+                                                       &dcbx_agent_status,
+                                                       NULL);
+                       if (status)
+                               dev_dbg(&pf->pdev->dev,
+                                       "Failed to start FW DCBX\n");
+
+                       dev_info(&pf->pdev->dev, "FW DCBX agent is %s\n",
+                                dcbx_agent_status ? "ACTIVE" : "DISABLED");
+
+                       /* Failure to configure MIB change or init DCB is not
+                        * relevant to ethtool.  Print notification that
+                        * registration/init failed but do not return error
+                        * state to ethtool
+                        */
+                       status = ice_aq_cfg_lldp_mib_change(&pf->hw, false,
+                                                           NULL);
+                       if (status)
+                               dev_dbg(&pf->pdev->dev,
+                                       "Fail to reg for MIB change\n");
+
+                       status = ice_init_pf_dcb(pf);
+                       if (status)
+                               dev_dbg(&pf->pdev->dev, "Fail to init DCB\n");
+               }
+       }
+       clear_bit(ICE_FLAG_ETHTOOL_CTXT, pf->flags);
+       return ret;
 }
 
 static int ice_get_sset_count(struct net_device *netdev, int sset)
@@ -1854,12 +1935,15 @@ ice_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
        struct ice_port_info *pi = np->vsi->port_info;
        struct ice_aqc_get_phy_caps_data *pcaps;
        struct ice_vsi *vsi = np->vsi;
+       struct ice_dcbx_cfg *dcbx_cfg;
        enum ice_status status;
 
        /* Initialize pause params */
        pause->rx_pause = 0;
        pause->tx_pause = 0;
 
+       dcbx_cfg = &pi->local_dcbx_cfg;
+
        pcaps = devm_kzalloc(&vsi->back->pdev->dev, sizeof(*pcaps),
                             GFP_KERNEL);
        if (!pcaps)
@@ -1874,6 +1958,10 @@ ice_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
        pause->autoneg = ((pcaps->caps & ICE_AQC_PHY_AN_MODE) ?
                        AUTONEG_ENABLE : AUTONEG_DISABLE);
 
+       if (dcbx_cfg->pfc.pfcena)
+               /* PFC enabled so report LFC as off */
+               goto out;
+
        if (pcaps->caps & ICE_AQC_PHY_EN_TX_LINK_PAUSE)
                pause->tx_pause = 1;
        if (pcaps->caps & ICE_AQC_PHY_EN_RX_LINK_PAUSE)
@@ -1894,6 +1982,7 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
        struct ice_netdev_priv *np = netdev_priv(netdev);
        struct ice_link_status *hw_link_info;
        struct ice_pf *pf = np->vsi->back;
+       struct ice_dcbx_cfg *dcbx_cfg;
        struct ice_vsi *vsi = np->vsi;
        struct ice_hw *hw = &pf->hw;
        struct ice_port_info *pi;
@@ -1904,6 +1993,7 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
 
        pi = vsi->port_info;
        hw_link_info = &pi->phy.link_info;
+       dcbx_cfg = &pi->local_dcbx_cfg;
        link_up = hw_link_info->link_info & ICE_AQ_LINK_UP;
 
        /* Changing the port's flow control is not supported if this isn't the
@@ -1926,6 +2016,10 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
                netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n");
        }
 
+       if (dcbx_cfg->pfc.pfcena) {
+               netdev_info(netdev, "Priority flow control enabled. Cannot set link flow control.\n");
+               return -EOPNOTSUPP;
+       }
        if (pause->rx_pause && pause->tx_pause)
                pi->fc.req_mode = ICE_FC_FULL;
        else if (pause->rx_pause && !pause->tx_pause)