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

Commit af7d5185 authored by Rana Shahout's avatar Rana Shahout Committed by David S. Miller
Browse files

net/mlx4_en: Add DCB PFC support through CEE netlink commands



This patch adds support for reading and updating priority flow
control (PFC) attributes in the driver via netlink.

Signed-off-by: default avatarRana Shahout <ranas@mellanox.com>
Signed-off-by: default avatarEran Ben Elisha <eranbe@mellanox.com>
Signed-off-by: default avatarEugenia Emantayev <eugenia@mellanox.com>
Signed-off-by: default avatarTariq Toukan <tariqt@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 51dca8a1
Loading
Loading
Loading
Loading
+263 −14
Original line number Diff line number Diff line
@@ -37,6 +37,11 @@
#include "mlx4_en.h"
#include "fw_qos.h"

enum {
	MLX4_CEE_STATE_DOWN   = 0,
	MLX4_CEE_STATE_UP     = 1,
};

/* Definitions for QCN
 */

@@ -80,13 +85,202 @@ struct mlx4_congestion_control_mb_prio_802_1_qau_statistics {
	__be32 reserved3[4];
};

static u8 mlx4_en_dcbnl_getcap(struct net_device *dev, int capid, u8 *cap)
{
	struct mlx4_en_priv *priv = netdev_priv(dev);

	switch (capid) {
	case DCB_CAP_ATTR_PFC:
		*cap = true;
		break;
	case DCB_CAP_ATTR_DCBX:
		*cap = priv->cee_params.dcbx_cap;
		break;
	case DCB_CAP_ATTR_PFC_TCS:
		*cap = 1 <<  mlx4_max_tc(priv->mdev->dev);
		break;
	default:
		*cap = false;
		break;
	}

	return 0;
}

static u8 mlx4_en_dcbnl_getpfcstate(struct net_device *netdev)
{
	struct mlx4_en_priv *priv = netdev_priv(netdev);

	return priv->cee_params.dcb_cfg.pfc_state;
}

static void mlx4_en_dcbnl_setpfcstate(struct net_device *netdev, u8 state)
{
	struct mlx4_en_priv *priv = netdev_priv(netdev);

	priv->cee_params.dcb_cfg.pfc_state = state;
}

static void mlx4_en_dcbnl_get_pfc_cfg(struct net_device *netdev, int priority,
				      u8 *setting)
{
	struct mlx4_en_priv *priv = netdev_priv(netdev);

	*setting = priv->cee_params.dcb_cfg.tc_config[priority].dcb_pfc;
}

static void mlx4_en_dcbnl_set_pfc_cfg(struct net_device *netdev, int priority,
				      u8 setting)
{
	struct mlx4_en_priv *priv = netdev_priv(netdev);

	priv->cee_params.dcb_cfg.tc_config[priority].dcb_pfc = setting;
	priv->cee_params.dcb_cfg.pfc_state = true;
}

static int mlx4_en_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num)
{
	struct mlx4_en_priv *priv = netdev_priv(netdev);

	if (!(priv->flags & MLX4_EN_FLAG_DCB_ENABLED))
		return -EINVAL;

	if (tcid == DCB_NUMTCS_ATTR_PFC)
		*num = mlx4_max_tc(priv->mdev->dev);
	else
		*num = 0;

	return 0;
}

static u8 mlx4_en_dcbnl_set_all(struct net_device *netdev)
{
	struct mlx4_en_priv *priv = netdev_priv(netdev);
	struct mlx4_en_dev *mdev = priv->mdev;
	struct mlx4_en_cee_config *dcb_cfg = &priv->cee_params.dcb_cfg;
	int err = 0;

	if (!(priv->cee_params.dcbx_cap & DCB_CAP_DCBX_VER_CEE))
		return -EINVAL;

	if (dcb_cfg->pfc_state) {
		int tc;

		priv->prof->rx_pause = 0;
		priv->prof->tx_pause = 0;
		for (tc = 0; tc < CEE_DCBX_MAX_PRIO; tc++) {
			u8 tc_mask = 1 << tc;

			switch (dcb_cfg->tc_config[tc].dcb_pfc) {
			case pfc_disabled:
				priv->prof->tx_ppp &= ~tc_mask;
				priv->prof->rx_ppp &= ~tc_mask;
				break;
			case pfc_enabled_full:
				priv->prof->tx_ppp |= tc_mask;
				priv->prof->rx_ppp |= tc_mask;
				break;
			case pfc_enabled_tx:
				priv->prof->tx_ppp |= tc_mask;
				priv->prof->rx_ppp &= ~tc_mask;
				break;
			case pfc_enabled_rx:
				priv->prof->tx_ppp &= ~tc_mask;
				priv->prof->rx_ppp |= tc_mask;
				break;
			default:
				break;
			}
		}
		en_dbg(DRV, priv, "Set pfc on\n");
	} else {
		priv->prof->rx_pause = 1;
		priv->prof->tx_pause = 1;
		en_dbg(DRV, priv, "Set pfc off\n");
	}

	err = mlx4_SET_PORT_general(mdev->dev, priv->port,
				    priv->rx_skb_size + ETH_FCS_LEN,
				    priv->prof->tx_pause,
				    priv->prof->tx_ppp,
				    priv->prof->rx_pause,
				    priv->prof->rx_ppp);
	if (err)
		en_err(priv, "Failed setting pause params\n");
	return err;
}

static u8 mlx4_en_dcbnl_get_state(struct net_device *dev)
{
	struct mlx4_en_priv *priv = netdev_priv(dev);

	if (priv->flags & MLX4_EN_FLAG_DCB_ENABLED)
		return MLX4_CEE_STATE_UP;

	return MLX4_CEE_STATE_DOWN;
}

static u8 mlx4_en_dcbnl_set_state(struct net_device *dev, u8 state)
{
	struct mlx4_en_priv *priv = netdev_priv(dev);
	int num_tcs = 0;

	if (!(priv->cee_params.dcbx_cap & DCB_CAP_DCBX_VER_CEE))
		return 1;

	if (!!(state) == !!(priv->flags & MLX4_EN_FLAG_DCB_ENABLED))
		return 0;

	if (state) {
		priv->flags |= MLX4_EN_FLAG_DCB_ENABLED;
		num_tcs = IEEE_8021QAZ_MAX_TCS;
	} else {
		priv->flags &= ~MLX4_EN_FLAG_DCB_ENABLED;
	}

	return mlx4_en_setup_tc(dev, num_tcs);
}

/* On success returns a non-zero 802.1p user priority bitmap
 * otherwise returns 0 as the invalid user priority bitmap to
 * indicate an error.
 */
static int mlx4_en_dcbnl_getapp(struct net_device *netdev, u8 idtype, u16 id)
{
	struct mlx4_en_priv *priv = netdev_priv(netdev);
	struct dcb_app app = {
				.selector = idtype,
				.protocol = id,
			     };
	if (!(priv->cee_params.dcbx_cap & DCB_CAP_DCBX_VER_CEE))
		return 0;

	return dcb_getapp(netdev, &app);
}

static int mlx4_en_dcbnl_setapp(struct net_device *netdev, u8 idtype,
				u16 id, u8 up)
{
	struct mlx4_en_priv *priv = netdev_priv(netdev);
	struct dcb_app app;

	if (!(priv->cee_params.dcbx_cap & DCB_CAP_DCBX_VER_CEE))
		return -EINVAL;

	memset(&app, 0, sizeof(struct dcb_app));
	app.selector = idtype;
	app.protocol = id;
	app.priority = up;

	return dcb_setapp(netdev, &app);
}

static int mlx4_en_dcbnl_ieee_getets(struct net_device *dev,
				   struct ieee_ets *ets)
{
	struct mlx4_en_priv *priv = netdev_priv(dev);
	struct ieee_ets *my_ets = &priv->ets;

	/* No IEEE PFC settings available */
	if (!my_ets)
		return -EINVAL;

@@ -237,18 +431,51 @@ static int mlx4_en_dcbnl_ieee_setpfc(struct net_device *dev,

static u8 mlx4_en_dcbnl_getdcbx(struct net_device *dev)
{
	return DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
	struct mlx4_en_priv *priv = netdev_priv(dev);

	return priv->cee_params.dcbx_cap;
}

static u8 mlx4_en_dcbnl_setdcbx(struct net_device *dev, u8 mode)
{
	struct mlx4_en_priv *priv = netdev_priv(dev);
	struct ieee_ets ets = {0};
	struct ieee_pfc pfc = {0};

	if (mode == priv->cee_params.dcbx_cap)
		return 0;

	if ((mode & DCB_CAP_DCBX_LLD_MANAGED) ||
	    (mode & DCB_CAP_DCBX_VER_CEE) ||
	    !(mode & DCB_CAP_DCBX_VER_IEEE) ||
	    ((mode & DCB_CAP_DCBX_VER_IEEE) &&
	     (mode & DCB_CAP_DCBX_VER_CEE)) ||
	    !(mode & DCB_CAP_DCBX_HOST))
		return 1;
		goto err;

	priv->cee_params.dcbx_cap = mode;

	ets.ets_cap = IEEE_8021QAZ_MAX_TCS;
	pfc.pfc_cap = IEEE_8021QAZ_MAX_TCS;

	if (mode & DCB_CAP_DCBX_VER_IEEE) {
		if (mlx4_en_dcbnl_ieee_setets(dev, &ets))
			goto err;
		if (mlx4_en_dcbnl_ieee_setpfc(dev, &pfc))
			goto err;
	} else if (mode & DCB_CAP_DCBX_VER_CEE) {
		if (mlx4_en_dcbnl_set_all(dev))
			goto err;
	} else {
		if (mlx4_en_dcbnl_ieee_setets(dev, &ets))
			goto err;
		if (mlx4_en_dcbnl_ieee_setpfc(dev, &pfc))
			goto err;
		if (mlx4_en_setup_tc(dev, 0))
			goto err;
	}

	return 0;
err:
	return 1;
}

#define MLX4_RATELIMIT_UNITS_IN_KB 100000 /* rate-limit HW unit in Kbps */
@@ -467,20 +694,42 @@ const struct dcbnl_rtnl_ops mlx4_en_dcbnl_ops = {
	.ieee_setets		= mlx4_en_dcbnl_ieee_setets,
	.ieee_getmaxrate	= mlx4_en_dcbnl_ieee_getmaxrate,
	.ieee_setmaxrate	= mlx4_en_dcbnl_ieee_setmaxrate,
	.ieee_getqcn		= mlx4_en_dcbnl_ieee_getqcn,
	.ieee_setqcn		= mlx4_en_dcbnl_ieee_setqcn,
	.ieee_getqcnstats	= mlx4_en_dcbnl_ieee_getqcnstats,
	.ieee_getpfc		= mlx4_en_dcbnl_ieee_getpfc,
	.ieee_setpfc		= mlx4_en_dcbnl_ieee_setpfc,

	.getstate	= mlx4_en_dcbnl_get_state,
	.setstate	= mlx4_en_dcbnl_set_state,
	.getpfccfg	= mlx4_en_dcbnl_get_pfc_cfg,
	.setpfccfg	= mlx4_en_dcbnl_set_pfc_cfg,
	.setall		= mlx4_en_dcbnl_set_all,
	.getcap		= mlx4_en_dcbnl_getcap,
	.getnumtcs	= mlx4_en_dcbnl_getnumtcs,
	.getpfcstate	= mlx4_en_dcbnl_getpfcstate,
	.setpfcstate	= mlx4_en_dcbnl_setpfcstate,
	.getapp		= mlx4_en_dcbnl_getapp,
	.setapp		= mlx4_en_dcbnl_setapp,

	.getdcbx	= mlx4_en_dcbnl_getdcbx,
	.setdcbx	= mlx4_en_dcbnl_setdcbx,
	.ieee_getqcn	= mlx4_en_dcbnl_ieee_getqcn,
	.ieee_setqcn	= mlx4_en_dcbnl_ieee_setqcn,
	.ieee_getqcnstats = mlx4_en_dcbnl_ieee_getqcnstats,
};

const struct dcbnl_rtnl_ops mlx4_en_dcbnl_pfc_ops = {
	.ieee_getpfc	= mlx4_en_dcbnl_ieee_getpfc,
	.ieee_setpfc	= mlx4_en_dcbnl_ieee_setpfc,

	.setstate	= mlx4_en_dcbnl_set_state,
	.getpfccfg	= mlx4_en_dcbnl_get_pfc_cfg,
	.setpfccfg	= mlx4_en_dcbnl_set_pfc_cfg,
	.setall		= mlx4_en_dcbnl_set_all,
	.getnumtcs	= mlx4_en_dcbnl_getnumtcs,
	.getpfcstate	= mlx4_en_dcbnl_getpfcstate,
	.setpfcstate	= mlx4_en_dcbnl_setpfcstate,
	.getapp		= mlx4_en_dcbnl_getapp,
	.setapp		= mlx4_en_dcbnl_setapp,

	.getdcbx	= mlx4_en_dcbnl_getdcbx,
	.setdcbx	= mlx4_en_dcbnl_setdcbx,
};
+25 −0
Original line number Diff line number Diff line
@@ -67,6 +67,17 @@ int mlx4_en_setup_tc(struct net_device *dev, u8 up)
		offset += priv->num_tx_rings_p_up;
	}

#ifdef CONFIG_MLX4_EN_DCB
	if (!mlx4_is_slave(priv->mdev->dev)) {
		if (up) {
			priv->flags |= MLX4_EN_FLAG_DCB_ENABLED;
		} else {
			priv->flags &= ~MLX4_EN_FLAG_DCB_ENABLED;
			priv->cee_params.dcb_cfg.pfc_state = false;
		}
	}
#endif /* CONFIG_MLX4_EN_DCB */

	return 0;
}

@@ -2815,6 +2826,9 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
	struct mlx4_en_priv *priv;
	int i;
	int err;
#ifdef CONFIG_MLX4_EN_DCB
	struct tc_configuration *tc;
#endif

	dev = alloc_etherdev_mqs(sizeof(struct mlx4_en_priv),
				 MAX_TX_RINGS, MAX_RX_RINGS);
@@ -2881,6 +2895,17 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
	priv->msg_enable = MLX4_EN_MSG_LEVEL;
#ifdef CONFIG_MLX4_EN_DCB
	if (!mlx4_is_slave(priv->mdev->dev)) {
		priv->cee_params.dcbx_cap = DCB_CAP_DCBX_VER_CEE |
					    DCB_CAP_DCBX_HOST |
					    DCB_CAP_DCBX_VER_IEEE;
		priv->flags |= MLX4_EN_DCB_ENABLED;
		priv->cee_params.dcb_cfg.pfc_state = false;

		for (i = 0; i < MLX4_EN_NUM_UP; i++) {
			tc = &priv->cee_params.dcb_cfg.tc_config[i];
			tc->dcb_pfc = pfc_disabled;
		}

		if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETS_CFG) {
			dev->dcbnl_ops = &mlx4_en_dcbnl_ops;
		} else {
+1 −0
Original line number Diff line number Diff line
@@ -1128,6 +1128,7 @@ int mlx4_QUERY_PORT(struct mlx4_dev *dev, int port, struct mlx4_port_cap *port_c
		port_cap->max_pkeys	   = 1 << (field & 0xf);
		MLX4_GET(field, outbox, QUERY_PORT_MAX_VL_OFFSET);
		port_cap->max_vl	   = field & 0xf;
		port_cap->max_tc_eth	   = field >> 4;
		MLX4_GET(field, outbox, QUERY_PORT_MAX_MACVLAN_OFFSET);
		port_cap->log_max_macs  = field & 0xf;
		port_cap->log_max_vlans = field >> 4;
+1 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ struct mlx4_port_cap {
	int ib_mtu;
	int max_port_width;
	int max_vl;
	int max_tc_eth;
	int max_gids;
	int max_pkeys;
	u64 def_mac;
+1 −0
Original line number Diff line number Diff line
@@ -292,6 +292,7 @@ static int _mlx4_dev_port(struct mlx4_dev *dev, int port,
	dev->caps.pkey_table_len[port] = port_cap->max_pkeys;
	dev->caps.port_width_cap[port] = port_cap->max_port_width;
	dev->caps.eth_mtu_cap[port]    = port_cap->eth_mtu;
	dev->caps.max_tc_eth	       = port_cap->max_tc_eth;
	dev->caps.def_mac[port]        = port_cap->def_mac;
	dev->caps.supported_type[port] = port_cap->supported_port_types;
	dev->caps.suggested_type[port] = port_cap->suggested_type;
Loading