Commit b2b1dab6 authored by Petr Machata's avatar Petr Machata Committed by David S. Miller

mlxsw: spectrum: Support ieee_setapp, ieee_delapp

The APP TLVs are used for communicating priority-to-protocol ID maps for
a given netdevice. Support the following APP TLVs:

- DSCP (selector 5) to configure priority-to-DSCP code point maps. Use
  these maps to configure packet priority on ingress, and DSCP code
  point rewrite on egress.

- Default priority (selector 1, PID 0) to configure priority for the
  DSCP code points that don't have one assigned by the DSCP selector. In
  future this could also be used for assigning default port priority
  when a packet arrives without DSCP tagging.

Besides setting up the maps themselves, also configure port trust level
and rewrite bits.

Port trust level determines whether, for a packet arriving through a
certain port, the priority should be determined based on PCP or DSCP
header fields. So far, mlxsw kept the device default of trust-PCP. Now,
as soon as the first DSCP APP TLV is configured, switch to trust-DSCP.
Only when all DSCP APP TLVs are removed, switch back to trust-PCP again.
Note that the default priority APP TLV doesn't impact the trust level
configuration.

Rewrite bits determine whether DSCP and PCP fields of egressing packets
should be updated according to switch priority. When port trust is
switched to DSCP, enable rewrite of DSCP field.
Signed-off-by: default avatarPetr Machata <petrm@mellanox.com>
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 55fb71f4
/* /*
* drivers/net/ethernet/mellanox/mlxsw/spectrum.h * drivers/net/ethernet/mellanox/mlxsw/spectrum.h
* Copyright (c) 2015-2017 Mellanox Technologies. All rights reserved. * Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2015-2017 Jiri Pirko <jiri@mellanox.com> * Copyright (c) 2015-2017 Jiri Pirko <jiri@mellanox.com>
* Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com> * Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com>
* Copyright (c) 2015 Elad Raz <eladr@mellanox.com> * Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include "core.h" #include "core.h"
#include "core_acl_flex_keys.h" #include "core_acl_flex_keys.h"
#include "core_acl_flex_actions.h" #include "core_acl_flex_actions.h"
#include "reg.h"
#define MLXSW_SP_FID_8021D_MAX 1024 #define MLXSW_SP_FID_8021D_MAX 1024
...@@ -243,6 +244,7 @@ struct mlxsw_sp_port { ...@@ -243,6 +244,7 @@ struct mlxsw_sp_port {
struct ieee_ets *ets; struct ieee_ets *ets;
struct ieee_maxrate *maxrate; struct ieee_maxrate *maxrate;
struct ieee_pfc *pfc; struct ieee_pfc *pfc;
enum mlxsw_reg_qpts_trust_state trust_state;
} dcb; } dcb;
struct { struct {
u8 module; u8 module;
......
/* /*
* drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c * drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
* Copyright (c) 2016 Mellanox Technologies. All rights reserved. * Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com> * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -255,6 +255,270 @@ static int mlxsw_sp_dcbnl_ieee_setets(struct net_device *dev, ...@@ -255,6 +255,270 @@ static int mlxsw_sp_dcbnl_ieee_setets(struct net_device *dev,
return 0; return 0;
} }
static int mlxsw_sp_dcbnl_app_validate(struct net_device *dev,
struct dcb_app *app)
{
int prio;
if (app->priority >= IEEE_8021QAZ_MAX_TCS) {
netdev_err(dev, "APP entry with priority value %u is invalid\n",
app->priority);
return -EINVAL;
}
switch (app->selector) {
case IEEE_8021QAZ_APP_SEL_DSCP:
if (app->protocol >= 64) {
netdev_err(dev, "DSCP APP entry with protocol value %u is invalid\n",
app->protocol);
return -EINVAL;
}
/* Warn about any DSCP APP entries with the same PID. */
prio = fls(dcb_ieee_getapp_mask(dev, app));
if (prio--) {
if (prio < app->priority)
netdev_warn(dev, "Choosing priority %d for DSCP %d in favor of previously-active value of %d\n",
app->priority, app->protocol, prio);
else if (prio > app->priority)
netdev_warn(dev, "Ignoring new priority %d for DSCP %d in favor of current value of %d\n",
app->priority, app->protocol, prio);
}
break;
case IEEE_8021QAZ_APP_SEL_ETHERTYPE:
if (app->protocol) {
netdev_err(dev, "EtherType APP entries with protocol value != 0 not supported\n");
return -EINVAL;
}
break;
default:
netdev_err(dev, "APP entries with selector %u not supported\n",
app->selector);
return -EINVAL;
}
return 0;
}
static u8
mlxsw_sp_port_dcb_app_default_prio(struct mlxsw_sp_port *mlxsw_sp_port)
{
u8 prio_mask;
prio_mask = dcb_ieee_getapp_default_prio_mask(mlxsw_sp_port->dev);
if (prio_mask)
/* Take the highest configured priority. */
return fls(prio_mask) - 1;
return 0;
}
static void
mlxsw_sp_port_dcb_app_dscp_prio_map(struct mlxsw_sp_port *mlxsw_sp_port,
u8 default_prio,
struct dcb_ieee_app_dscp_map *map)
{
int i;
dcb_ieee_getapp_dscp_prio_mask_map(mlxsw_sp_port->dev, map);
for (i = 0; i < ARRAY_SIZE(map->map); ++i) {
if (map->map[i])
map->map[i] = fls(map->map[i]) - 1;
else
map->map[i] = default_prio;
}
}
static bool
mlxsw_sp_port_dcb_app_prio_dscp_map(struct mlxsw_sp_port *mlxsw_sp_port,
struct dcb_ieee_app_prio_map *map)
{
bool have_dscp = false;
int i;
dcb_ieee_getapp_prio_dscp_mask_map(mlxsw_sp_port->dev, map);
for (i = 0; i < ARRAY_SIZE(map->map); ++i) {
if (map->map[i]) {
map->map[i] = fls64(map->map[i]) - 1;
have_dscp = true;
}
}
return have_dscp;
}
static int
mlxsw_sp_port_dcb_app_update_qpts(struct mlxsw_sp_port *mlxsw_sp_port,
enum mlxsw_reg_qpts_trust_state ts)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char qpts_pl[MLXSW_REG_QPTS_LEN];
mlxsw_reg_qpts_pack(qpts_pl, mlxsw_sp_port->local_port, ts);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpts), qpts_pl);
}
static int
mlxsw_sp_port_dcb_app_update_qrwe(struct mlxsw_sp_port *mlxsw_sp_port,
bool rewrite_dscp)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char qrwe_pl[MLXSW_REG_QRWE_LEN];
mlxsw_reg_qrwe_pack(qrwe_pl, mlxsw_sp_port->local_port,
false, rewrite_dscp);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qrwe), qrwe_pl);
}
static int
mlxsw_sp_port_dcb_toggle_trust(struct mlxsw_sp_port *mlxsw_sp_port,
enum mlxsw_reg_qpts_trust_state ts)
{
bool rewrite_dscp = ts == MLXSW_REG_QPTS_TRUST_STATE_DSCP;
int err;
if (mlxsw_sp_port->dcb.trust_state == ts)
return 0;
err = mlxsw_sp_port_dcb_app_update_qpts(mlxsw_sp_port, ts);
if (err)
return err;
err = mlxsw_sp_port_dcb_app_update_qrwe(mlxsw_sp_port, rewrite_dscp);
if (err)
goto err_update_qrwe;
mlxsw_sp_port->dcb.trust_state = ts;
return 0;
err_update_qrwe:
mlxsw_sp_port_dcb_app_update_qpts(mlxsw_sp_port,
mlxsw_sp_port->dcb.trust_state);
return err;
}
static int
mlxsw_sp_port_dcb_app_update_qpdpm(struct mlxsw_sp_port *mlxsw_sp_port,
struct dcb_ieee_app_dscp_map *map)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char qpdpm_pl[MLXSW_REG_QPDPM_LEN];
short int i;
mlxsw_reg_qpdpm_pack(qpdpm_pl, mlxsw_sp_port->local_port);
for (i = 0; i < ARRAY_SIZE(map->map); ++i)
mlxsw_reg_qpdpm_dscp_pack(qpdpm_pl, i, map->map[i]);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpdpm), qpdpm_pl);
}
static int
mlxsw_sp_port_dcb_app_update_qpdsm(struct mlxsw_sp_port *mlxsw_sp_port,
struct dcb_ieee_app_prio_map *map)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char qpdsm_pl[MLXSW_REG_QPDSM_LEN];
short int i;
mlxsw_reg_qpdsm_pack(qpdsm_pl, mlxsw_sp_port->local_port);
for (i = 0; i < ARRAY_SIZE(map->map); ++i)
mlxsw_reg_qpdsm_prio_pack(qpdsm_pl, i, map->map[i]);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpdsm), qpdsm_pl);
}
static int mlxsw_sp_port_dcb_app_update(struct mlxsw_sp_port *mlxsw_sp_port)
{
struct dcb_ieee_app_prio_map prio_map;
struct dcb_ieee_app_dscp_map dscp_map;
u8 default_prio;
bool have_dscp;
int err;
default_prio = mlxsw_sp_port_dcb_app_default_prio(mlxsw_sp_port);
have_dscp = mlxsw_sp_port_dcb_app_prio_dscp_map(mlxsw_sp_port,
&prio_map);
if (!have_dscp) {
err = mlxsw_sp_port_dcb_toggle_trust(mlxsw_sp_port,
MLXSW_REG_QPTS_TRUST_STATE_PCP);
if (err)
netdev_err(mlxsw_sp_port->dev, "Couldn't switch to trust L2\n");
return err;
}
mlxsw_sp_port_dcb_app_dscp_prio_map(mlxsw_sp_port, default_prio,
&dscp_map);
err = mlxsw_sp_port_dcb_app_update_qpdpm(mlxsw_sp_port,
&dscp_map);
if (err) {
netdev_err(mlxsw_sp_port->dev, "Couldn't configure priority map\n");
return err;
}
err = mlxsw_sp_port_dcb_app_update_qpdsm(mlxsw_sp_port,
&prio_map);
if (err) {
netdev_err(mlxsw_sp_port->dev, "Couldn't configure DSCP rewrite map\n");
return err;
}
err = mlxsw_sp_port_dcb_toggle_trust(mlxsw_sp_port,
MLXSW_REG_QPTS_TRUST_STATE_DSCP);
if (err) {
/* A failure to set trust DSCP means that the QPDPM and QPDSM
* maps installed above are not in effect. And since we are here
* attempting to set trust DSCP, we couldn't have attempted to
* switch trust to PCP. Thus no cleanup is necessary.
*/
netdev_err(mlxsw_sp_port->dev, "Couldn't switch to trust L3\n");
return err;
}
return 0;
}
static int mlxsw_sp_dcbnl_ieee_setapp(struct net_device *dev,
struct dcb_app *app)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
int err;
err = mlxsw_sp_dcbnl_app_validate(dev, app);
if (err)
return err;
err = dcb_ieee_setapp(dev, app);
if (err)
return err;
err = mlxsw_sp_port_dcb_app_update(mlxsw_sp_port);
if (err)
goto err_update;
return 0;
err_update:
dcb_ieee_delapp(dev, app);
return err;
}
static int mlxsw_sp_dcbnl_ieee_delapp(struct net_device *dev,
struct dcb_app *app)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
int err;
err = dcb_ieee_delapp(dev, app);
if (err)
return err;
err = mlxsw_sp_port_dcb_app_update(mlxsw_sp_port);
if (err)
netdev_err(dev, "Failed to update DCB APP configuration\n");
return 0;
}
static int mlxsw_sp_dcbnl_ieee_getmaxrate(struct net_device *dev, static int mlxsw_sp_dcbnl_ieee_getmaxrate(struct net_device *dev,
struct ieee_maxrate *maxrate) struct ieee_maxrate *maxrate)
{ {
...@@ -394,6 +658,8 @@ static const struct dcbnl_rtnl_ops mlxsw_sp_dcbnl_ops = { ...@@ -394,6 +658,8 @@ static const struct dcbnl_rtnl_ops mlxsw_sp_dcbnl_ops = {
.ieee_setmaxrate = mlxsw_sp_dcbnl_ieee_setmaxrate, .ieee_setmaxrate = mlxsw_sp_dcbnl_ieee_setmaxrate,
.ieee_getpfc = mlxsw_sp_dcbnl_ieee_getpfc, .ieee_getpfc = mlxsw_sp_dcbnl_ieee_getpfc,
.ieee_setpfc = mlxsw_sp_dcbnl_ieee_setpfc, .ieee_setpfc = mlxsw_sp_dcbnl_ieee_setpfc,
.ieee_setapp = mlxsw_sp_dcbnl_ieee_setapp,
.ieee_delapp = mlxsw_sp_dcbnl_ieee_delapp,
.getdcbx = mlxsw_sp_dcbnl_getdcbx, .getdcbx = mlxsw_sp_dcbnl_getdcbx,
.setdcbx = mlxsw_sp_dcbnl_setdcbx, .setdcbx = mlxsw_sp_dcbnl_setdcbx,
...@@ -467,6 +733,7 @@ int mlxsw_sp_port_dcb_init(struct mlxsw_sp_port *mlxsw_sp_port) ...@@ -467,6 +733,7 @@ int mlxsw_sp_port_dcb_init(struct mlxsw_sp_port *mlxsw_sp_port)
if (err) if (err)
goto err_port_pfc_init; goto err_port_pfc_init;
mlxsw_sp_port->dcb.trust_state = MLXSW_REG_QPTS_TRUST_STATE_PCP;
mlxsw_sp_port->dev->dcbnl_ops = &mlxsw_sp_dcbnl_ops; mlxsw_sp_port->dev->dcbnl_ops = &mlxsw_sp_dcbnl_ops;
return 0; return 0;
......
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