Skip to content

Commit

Permalink
net/mlx4_en: Add DCB PFC support through CEE netlink commands
Browse files Browse the repository at this point in the history
This patch adds support for reading and updating priority flow
control (PFC) attributes in the driver via netlink.

Signed-off-by: Rana Shahout <[email protected]>
Signed-off-by: Eran Ben Elisha <[email protected]>
Signed-off-by: Eugenia Emantayev <[email protected]>
Signed-off-by: Tariq Toukan <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Rana Shahout authored and davem330 committed Jun 23, 2016
1 parent 51dca8a commit af7d518
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 14 deletions.
277 changes: 263 additions & 14 deletions drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -463,24 +690,46 @@ static int mlx4_en_dcbnl_ieee_getqcnstats(struct net_device *dev,
}

const struct dcbnl_rtnl_ops mlx4_en_dcbnl_ops = {
.ieee_getets = mlx4_en_dcbnl_ieee_getets,
.ieee_setets = mlx4_en_dcbnl_ieee_setets,
.ieee_getmaxrate = mlx4_en_dcbnl_ieee_getmaxrate,
.ieee_setmaxrate = mlx4_en_dcbnl_ieee_setmaxrate,
.ieee_getpfc = mlx4_en_dcbnl_ieee_getpfc,
.ieee_setpfc = mlx4_en_dcbnl_ieee_setpfc,
.ieee_getets = mlx4_en_dcbnl_ieee_getets,
.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 changes: 25 additions & 0 deletions drivers/net/ethernet/mellanox/mlx4/en_netdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/mellanox/mlx4/fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/mellanox/mlx4/fw.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/mellanox/mlx4/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit af7d518

Please sign in to comment.