Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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 */
};

+12 −0
Original line number Diff line number Diff line
@@ -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,
+27 −1
Original line number Diff line number Diff line
@@ -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
+23 −0
Original line number Diff line number Diff line
@@ -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_ */
+95 −1
Original line number Diff line number Diff line
@@ -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)