net: microchip: sparx5: add support for PCP rewrite

Add support for rewrite of PCP and DEI, based on classified Quality of
Service (QoS) class and Drop-Precedence (DP) level.

The DCB rewrite table is queried for mappings between priority and
PCP/DEI. The classified DP level is then encoded in the DEI bit, if a
mapping for DEI exists.

Sparx5 has four DP levels, where by default, 0 is mapped to DE0 and 1-3
are mapped to DE1. If a mapping exists where DEI=1, then all classified
DP levels mapped to DE1 will set the DEI bit. The other way around for
DEI=0. Effectively, this means that the tagged DEI bit will reflect the
DP level for any mappings where DEI=1.

Map priority=1 to PCP=1 and DEI=1:
$ dcb rewr add dev eth0 pcp-prio 1:1de

Map priority=7 to PCP=2 and DEI=0
$ dcb rewr add dev eth0 pcp-prio 7:2nd

Also, sparx5_dcb_ieee_dscp_setdel() has been refactored, to work for
both APP and rewrite entries.

Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Daniel Machon 2023-01-18 22:08:29 +01:00 committed by David S. Miller
parent 1df99338e6
commit 2234879f4c
4 changed files with 191 additions and 14 deletions

View File

@ -133,12 +133,14 @@ static bool sparx5_dcb_apptrust_contains(int portno, u8 selector)
static int sparx5_dcb_app_update(struct net_device *dev) static int sparx5_dcb_app_update(struct net_device *dev)
{ {
struct dcb_rewr_prio_pcp_map pcp_rewr_map = {0};
struct sparx5_port *port = netdev_priv(dev); struct sparx5_port *port = netdev_priv(dev);
struct sparx5_port_qos_dscp_map *dscp_map; struct sparx5_port_qos_dscp_map *dscp_map;
struct sparx5_port_qos_pcp_map *pcp_map; struct sparx5_port_qos_pcp_map *pcp_map;
struct sparx5_port_qos qos = {0}; struct sparx5_port_qos qos = {0};
struct dcb_app app_itr = {0}; struct dcb_app app_itr = {0};
int portno = port->portno; int portno = port->portno;
bool pcp_rewr = false;
int i; int i;
dscp_map = &qos.dscp.map; dscp_map = &qos.dscp.map;
@ -163,10 +165,24 @@ static int sparx5_dcb_app_update(struct net_device *dev)
pcp_map->map[i] = dcb_getapp(dev, &app_itr); pcp_map->map[i] = dcb_getapp(dev, &app_itr);
} }
/* Get pcp rewrite mapping */
dcb_getrewr_prio_pcp_mask_map(dev, &pcp_rewr_map);
for (i = 0; i < ARRAY_SIZE(pcp_rewr_map.map); i++) {
if (!pcp_rewr_map.map[i])
continue;
pcp_rewr = true;
qos.pcp_rewr.map.map[i] = fls(pcp_rewr_map.map[i]) - 1;
}
/* Enable use of pcp for queue classification ? */ /* Enable use of pcp for queue classification ? */
if (sparx5_dcb_apptrust_contains(portno, DCB_APP_SEL_PCP)) { if (sparx5_dcb_apptrust_contains(portno, DCB_APP_SEL_PCP)) {
qos.pcp.qos_enable = true; qos.pcp.qos_enable = true;
qos.pcp.dp_enable = qos.pcp.qos_enable; qos.pcp.dp_enable = qos.pcp.qos_enable;
/* Enable rewrite of PCP and DEI if PCP is trusted *and* rewrite
* table is not empty.
*/
if (pcp_rewr)
qos.pcp_rewr.enable = true;
} }
/* Enable use of dscp for queue classification ? */ /* Enable use of dscp for queue classification ? */
@ -178,16 +194,17 @@ static int sparx5_dcb_app_update(struct net_device *dev)
return sparx5_port_qos_set(port, &qos); return sparx5_port_qos_set(port, &qos);
} }
/* Set or delete dscp app entry. /* Set or delete DSCP app entry.
* *
* Dscp mapping is global for all ports, so set and delete app entries are * DSCP mapping is global for all ports, so set and delete app entries are
* replicated for each port. * replicated for each port.
*/ */
static int sparx5_dcb_ieee_dscp_setdel_app(struct net_device *dev, static int sparx5_dcb_ieee_dscp_setdel(struct net_device *dev,
struct dcb_app *app, bool del) struct dcb_app *app,
int (*setdel)(struct net_device *,
struct dcb_app *))
{ {
struct sparx5_port *port = netdev_priv(dev); struct sparx5_port *port = netdev_priv(dev);
struct dcb_app apps[SPX5_PORTS];
struct sparx5_port *port_itr; struct sparx5_port *port_itr;
int err, i; int err, i;
@ -195,11 +212,7 @@ static int sparx5_dcb_ieee_dscp_setdel_app(struct net_device *dev,
port_itr = port->sparx5->ports[i]; port_itr = port->sparx5->ports[i];
if (!port_itr) if (!port_itr)
continue; continue;
memcpy(&apps[i], app, sizeof(struct dcb_app)); err = setdel(port_itr->ndev, app);
if (del)
err = dcb_ieee_delapp(port_itr->ndev, &apps[i]);
else
err = dcb_ieee_setapp(port_itr->ndev, &apps[i]);
if (err) if (err)
return err; return err;
} }
@ -226,7 +239,7 @@ static int sparx5_dcb_ieee_setapp(struct net_device *dev, struct dcb_app *app)
} }
if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP)
err = sparx5_dcb_ieee_dscp_setdel_app(dev, app, false); err = sparx5_dcb_ieee_dscp_setdel(dev, app, dcb_ieee_setapp);
else else
err = dcb_ieee_setapp(dev, app); err = dcb_ieee_setapp(dev, app);
@ -244,7 +257,7 @@ static int sparx5_dcb_ieee_delapp(struct net_device *dev, struct dcb_app *app)
int err; int err;
if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP)
err = sparx5_dcb_ieee_dscp_setdel_app(dev, app, true); err = sparx5_dcb_ieee_dscp_setdel(dev, app, dcb_ieee_delapp);
else else
err = dcb_ieee_delapp(dev, app); err = dcb_ieee_delapp(dev, app);
@ -283,11 +296,60 @@ static int sparx5_dcb_getapptrust(struct net_device *dev, u8 *selectors,
return 0; return 0;
} }
static int sparx5_dcb_delrewr(struct net_device *dev, struct dcb_app *app)
{
int err;
if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP)
err = sparx5_dcb_ieee_dscp_setdel(dev, app, dcb_delrewr);
else
err = dcb_delrewr(dev, app);
if (err < 0)
return err;
return sparx5_dcb_app_update(dev);
}
static int sparx5_dcb_setrewr(struct net_device *dev, struct dcb_app *app)
{
struct dcb_app app_itr;
int err = 0;
u16 proto;
err = sparx5_dcb_app_validate(dev, app);
if (err)
goto out;
/* Delete current mapping, if it exists. */
proto = dcb_getrewr(dev, app);
if (proto) {
app_itr = *app;
app_itr.protocol = proto;
sparx5_dcb_delrewr(dev, &app_itr);
}
if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP)
err = sparx5_dcb_ieee_dscp_setdel(dev, app, dcb_setrewr);
else
err = dcb_setrewr(dev, app);
if (err)
goto out;
sparx5_dcb_app_update(dev);
out:
return err;
}
const struct dcbnl_rtnl_ops sparx5_dcbnl_ops = { const struct dcbnl_rtnl_ops sparx5_dcbnl_ops = {
.ieee_setapp = sparx5_dcb_ieee_setapp, .ieee_setapp = sparx5_dcb_ieee_setapp,
.ieee_delapp = sparx5_dcb_ieee_delapp, .ieee_delapp = sparx5_dcb_ieee_delapp,
.dcbnl_setapptrust = sparx5_dcb_setapptrust, .dcbnl_setapptrust = sparx5_dcb_setapptrust,
.dcbnl_getapptrust = sparx5_dcb_getapptrust, .dcbnl_getapptrust = sparx5_dcb_getapptrust,
.dcbnl_setrewr = sparx5_dcb_setrewr,
.dcbnl_delrewr = sparx5_dcb_delrewr,
}; };
int sparx5_dcb_init(struct sparx5 *sparx5) int sparx5_dcb_init(struct sparx5 *sparx5)

View File

@ -4,8 +4,8 @@
* Copyright (c) 2021 Microchip Technology Inc. * Copyright (c) 2021 Microchip Technology Inc.
*/ */
/* This file is autogenerated by cml-utils 2022-09-28 11:17:02 +0200. /* This file is autogenerated by cml-utils 2022-11-04 11:22:22 +0100.
* Commit ID: 385c8a11d71a9f6a60368d3a3cb648fa257b479a * Commit ID: 498242727be5db9b423cc0923bc966fc7b40607e
*/ */
#ifndef _SPARX5_MAIN_REGS_H_ #ifndef _SPARX5_MAIN_REGS_H_
@ -5345,6 +5345,46 @@ enum sparx5_target {
#define REW_PORT_VLAN_CFG_PORT_VID_GET(x)\ #define REW_PORT_VLAN_CFG_PORT_VID_GET(x)\
FIELD_GET(REW_PORT_VLAN_CFG_PORT_VID, x) FIELD_GET(REW_PORT_VLAN_CFG_PORT_VID, x)
/* REW:PORT:PCP_MAP_DE0 */
#define REW_PCP_MAP_DE0(g, r) \
__REG(TARGET_REW, 0, 1, 360448, g, 70, 256, 4, r, 8, 4)
#define REW_PCP_MAP_DE0_PCP_DE0 GENMASK(2, 0)
#define REW_PCP_MAP_DE0_PCP_DE0_SET(x)\
FIELD_PREP(REW_PCP_MAP_DE0_PCP_DE0, x)
#define REW_PCP_MAP_DE0_PCP_DE0_GET(x)\
FIELD_GET(REW_PCP_MAP_DE0_PCP_DE0, x)
/* REW:PORT:PCP_MAP_DE1 */
#define REW_PCP_MAP_DE1(g, r) \
__REG(TARGET_REW, 0, 1, 360448, g, 70, 256, 36, r, 8, 4)
#define REW_PCP_MAP_DE1_PCP_DE1 GENMASK(2, 0)
#define REW_PCP_MAP_DE1_PCP_DE1_SET(x)\
FIELD_PREP(REW_PCP_MAP_DE1_PCP_DE1, x)
#define REW_PCP_MAP_DE1_PCP_DE1_GET(x)\
FIELD_GET(REW_PCP_MAP_DE1_PCP_DE1, x)
/* REW:PORT:DEI_MAP_DE0 */
#define REW_DEI_MAP_DE0(g, r) \
__REG(TARGET_REW, 0, 1, 360448, g, 70, 256, 68, r, 8, 4)
#define REW_DEI_MAP_DE0_DEI_DE0 BIT(0)
#define REW_DEI_MAP_DE0_DEI_DE0_SET(x)\
FIELD_PREP(REW_DEI_MAP_DE0_DEI_DE0, x)
#define REW_DEI_MAP_DE0_DEI_DE0_GET(x)\
FIELD_GET(REW_DEI_MAP_DE0_DEI_DE0, x)
/* REW:PORT:DEI_MAP_DE1 */
#define REW_DEI_MAP_DE1(g, r) \
__REG(TARGET_REW, 0, 1, 360448, g, 70, 256, 100, r, 8, 4)
#define REW_DEI_MAP_DE1_DEI_DE1 BIT(0)
#define REW_DEI_MAP_DE1_DEI_DE1_SET(x)\
FIELD_PREP(REW_DEI_MAP_DE1_DEI_DE1, x)
#define REW_DEI_MAP_DE1_DEI_DE1_GET(x)\
FIELD_GET(REW_DEI_MAP_DE1_DEI_DE1, x)
/* REW:PORT:TAG_CTRL */ /* REW:PORT:TAG_CTRL */
#define REW_TAG_CTRL(g) __REG(TARGET_REW, 0, 1, 360448, g, 70, 256, 132, 0, 1, 4) #define REW_TAG_CTRL(g) __REG(TARGET_REW, 0, 1, 360448, g, 70, 256, 132, 0, 1, 4)

View File

@ -1151,11 +1151,68 @@ int sparx5_port_qos_set(struct sparx5_port *port,
{ {
sparx5_port_qos_dscp_set(port, &qos->dscp); sparx5_port_qos_dscp_set(port, &qos->dscp);
sparx5_port_qos_pcp_set(port, &qos->pcp); sparx5_port_qos_pcp_set(port, &qos->pcp);
sparx5_port_qos_pcp_rewr_set(port, &qos->pcp_rewr);
sparx5_port_qos_default_set(port, qos); sparx5_port_qos_default_set(port, qos);
return 0; return 0;
} }
int sparx5_port_qos_pcp_rewr_set(const struct sparx5_port *port,
struct sparx5_port_qos_pcp_rewr *qos)
{
int i, mode = SPARX5_PORT_REW_TAG_CTRL_CLASSIFIED;
struct sparx5 *sparx5 = port->sparx5;
u8 pcp, dei;
/* Use mapping table, with classified QoS as index, to map QoS and DP
* to tagged PCP and DEI, if PCP is trusted. Otherwise use classified
* PCP. Classified PCP equals frame PCP.
*/
if (qos->enable)
mode = SPARX5_PORT_REW_TAG_CTRL_MAPPED;
spx5_rmw(REW_TAG_CTRL_TAG_PCP_CFG_SET(mode) |
REW_TAG_CTRL_TAG_DEI_CFG_SET(mode),
REW_TAG_CTRL_TAG_PCP_CFG | REW_TAG_CTRL_TAG_DEI_CFG,
port->sparx5, REW_TAG_CTRL(port->portno));
for (i = 0; i < ARRAY_SIZE(qos->map.map); i++) {
/* Extract PCP and DEI */
pcp = qos->map.map[i];
if (pcp > SPARX5_PORT_QOS_PCP_COUNT)
dei = 1;
else
dei = 0;
/* Rewrite PCP and DEI, for each classified QoS class and DP
* level. This table is only used if tag ctrl mode is set to
* 'mapped'.
*
* 0:0nd - prio=0 and dp:0 => pcp=0 and dei=0
* 0:0de - prio=0 and dp:1 => pcp=0 and dei=1
*/
if (dei) {
spx5_rmw(REW_PCP_MAP_DE1_PCP_DE1_SET(pcp),
REW_PCP_MAP_DE1_PCP_DE1, sparx5,
REW_PCP_MAP_DE1(port->portno, i));
spx5_rmw(REW_DEI_MAP_DE1_DEI_DE1_SET(dei),
REW_DEI_MAP_DE1_DEI_DE1, port->sparx5,
REW_DEI_MAP_DE1(port->portno, i));
} else {
spx5_rmw(REW_PCP_MAP_DE0_PCP_DE0_SET(pcp),
REW_PCP_MAP_DE0_PCP_DE0, sparx5,
REW_PCP_MAP_DE0(port->portno, i));
spx5_rmw(REW_DEI_MAP_DE0_DEI_DE0_SET(dei),
REW_DEI_MAP_DE0_DEI_DE0, port->sparx5,
REW_DEI_MAP_DE0(port->portno, i));
}
}
return 0;
}
int sparx5_port_qos_pcp_set(const struct sparx5_port *port, int sparx5_port_qos_pcp_set(const struct sparx5_port *port,
struct sparx5_port_qos_pcp *qos) struct sparx5_port_qos_pcp *qos)
{ {

View File

@ -9,6 +9,11 @@
#include "sparx5_main.h" #include "sparx5_main.h"
/* Port PCP rewrite mode */
#define SPARX5_PORT_REW_TAG_CTRL_CLASSIFIED 0
#define SPARX5_PORT_REW_TAG_CTRL_DEFAULT 1
#define SPARX5_PORT_REW_TAG_CTRL_MAPPED 2
static inline bool sparx5_port_is_2g5(int portno) static inline bool sparx5_port_is_2g5(int portno)
{ {
return portno >= 16 && portno <= 47; return portno >= 16 && portno <= 47;
@ -99,6 +104,10 @@ struct sparx5_port_qos_pcp_map {
u8 map[SPARX5_PORT_QOS_PCP_DEI_COUNT]; u8 map[SPARX5_PORT_QOS_PCP_DEI_COUNT];
}; };
struct sparx5_port_qos_pcp_rewr_map {
u16 map[SPX5_PRIOS];
};
#define SPARX5_PORT_QOS_DSCP_COUNT 64 #define SPARX5_PORT_QOS_DSCP_COUNT 64
struct sparx5_port_qos_dscp_map { struct sparx5_port_qos_dscp_map {
u8 map[SPARX5_PORT_QOS_DSCP_COUNT]; u8 map[SPARX5_PORT_QOS_DSCP_COUNT];
@ -110,6 +119,11 @@ struct sparx5_port_qos_pcp {
bool dp_enable; bool dp_enable;
}; };
struct sparx5_port_qos_pcp_rewr {
struct sparx5_port_qos_pcp_rewr_map map;
bool enable;
};
struct sparx5_port_qos_dscp { struct sparx5_port_qos_dscp {
struct sparx5_port_qos_dscp_map map; struct sparx5_port_qos_dscp_map map;
bool qos_enable; bool qos_enable;
@ -118,6 +132,7 @@ struct sparx5_port_qos_dscp {
struct sparx5_port_qos { struct sparx5_port_qos {
struct sparx5_port_qos_pcp pcp; struct sparx5_port_qos_pcp pcp;
struct sparx5_port_qos_pcp_rewr pcp_rewr;
struct sparx5_port_qos_dscp dscp; struct sparx5_port_qos_dscp dscp;
u8 default_prio; u8 default_prio;
}; };
@ -127,6 +142,9 @@ int sparx5_port_qos_set(struct sparx5_port *port, struct sparx5_port_qos *qos);
int sparx5_port_qos_pcp_set(const struct sparx5_port *port, int sparx5_port_qos_pcp_set(const struct sparx5_port *port,
struct sparx5_port_qos_pcp *qos); struct sparx5_port_qos_pcp *qos);
int sparx5_port_qos_pcp_rewr_set(const struct sparx5_port *port,
struct sparx5_port_qos_pcp_rewr *qos);
int sparx5_port_qos_dscp_set(const struct sparx5_port *port, int sparx5_port_qos_dscp_set(const struct sparx5_port *port,
struct sparx5_port_qos_dscp *qos); struct sparx5_port_qos_dscp *qos);