Commit 3a257a14 authored by Anirudh Venkataramanan's avatar Anirudh Venkataramanan Committed by Jeff Kirsher

ice: Add code to control FW LLDP and DCBX

This patch adds code to start or stop LLDP and DCBX in firmware through
use of ethtool private flags.
Signed-off-by: default avatarAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: default avatarAndrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent b832c2f6
......@@ -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 */
};
......
......@@ -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,
......
......@@ -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)
{
......@@ -77,6 +77,32 @@ ice_aq_cfg_lldp_mib_change(struct ice_hw *hw, bool ena_update,
return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
}
/**
* 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
......
......@@ -130,11 +130,25 @@ 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_ */
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment