2023-01-12 20:51:02

by Daniel Machon

[permalink] [raw]
Subject: [PATCH net-next 0/6] Introduce new DCB rewrite table

There is currently no support for per-port egress mapping of priority to PCP and
priority to DSCP. Some support for expressing egress mapping of PCP is supported
through ip link, with the 'egress-qos-map', however this command only maps
priority to PCP, and for vlan interfaces only. DCB APP already has support for
per-port ingress mapping of PCP/DEI, DSCP and a bunch of other stuff. So why not
take advantage of this fact, and add a new table that does the reverse.

This patch series introduces the new DCB rewrite table. Whereas the DCB
APP table deals with ingress mapping of PID (protocol identifier) to priority,
the rewrite table deals with egress mapping of priority to PID.

It is indeed possible to integrate rewrite in the existing APP table, by
introducing new dedicated rewrite selectors, and altering existing functions
to treat rewrite entries specially. However, I feel like this is not a good
solution, and will pollute the APP namespace. APP is well-defined in IEEE, and
some userspace relies of advertised entries - for this fact, separating APP and
rewrite into to completely separate objects, seems to me the best solution.

The new table shares much functionality with the APP table, and as such, much
existing code is reused, or slightly modified, to work for both.

================================================================================
DCB rewrite table in a nutshell
================================================================================
The table is implemented as a simple linked list, and uses the same lock as the
APP table. New functions for getting, setting and deleting entries have been
added, and these are exported, so they can be used by the stack or drivers.
Additionnaly, new dcbnl_setrewr and dcnl_delrewr hooks has been added, to
support hardware offload of the entries.

================================================================================
Sparx5 per-port PCP rewrite support
================================================================================
Sparx5 supports PCP egress mapping through two eight-entry switch tables.
One table maps QoS class 0-7 to PCP for DE0 (DP levels mapped to
drop-eligibility 0) and the other for DE1. DCB does currently not have support
for expressing DP/color, so instead, the tagged DEI bit will reflect the DP
levels, for any rewrite entries> 7 ('de').

The driver will take apptrust (contributed earlier) into consideration, so
that the mapping tables only be used, if PCP is trusted *and* the rewrite table
has active mappings, otherwise classified PCP (same as frame PCP) will be used
instead.

================================================================================
Sparx5 per-port DSCP rewrite support
================================================================================
Sparx5 support DSCP egress mapping through a single 32-entry table. This table
maps classified QoS class and DP level to classified DSCP, and is consulted by
the switch Analyzer Classifier at ingress. At egress, the frame DSCP can either
be rewritten to classified DSCP to frame DSCP.

The driver will take apptrust into consideration, so that the mapping tables
only be used, if DSCP is trusted *and* the rewrite table has active mappings,
otherwise frame DSCP will be used instead.

================================================================================
Patches
================================================================================
Patch #1 modifies dcb_app_add to work for both APP and rewrite

Patch #2 adds dcbnl_apprewr_setdel() for setting and deleting both APP and
rewrite entries.

Patch #3 adds the rewrite table and all required functions, offload hooks and
bookkeeping for maintaining it.

Patch #4 adds two new helper functions for getting a priority to PCP bitmask
map, and a priority to DSCP bitmask map.

Patch #5 adds support for PCP rewrite in the Sparx5 driver
Patch #6 adds support for DSCP rewrite in the Sparx5 driver

Daniel Machon (6):
net: dcb: modify dcb_app_add to take list_head ptr as parameter
net: dcb: add new common function for set/del of app/rewr entries
net: dcb: add new rewrite table
net: dcb: add helper functions to retrieve PCP and DSCP rewrite maps
net: microchip: sparx5: add support for PCP rewrite
net: microchip: sparx5: add support for DSCP rewrite

.../ethernet/microchip/sparx5/sparx5_dcb.c | 121 +++++++-
.../microchip/sparx5/sparx5_main_regs.h | 70 ++++-
.../ethernet/microchip/sparx5/sparx5_port.c | 97 ++++++
.../ethernet/microchip/sparx5/sparx5_port.h | 41 +++
include/net/dcbnl.h | 18 ++
include/uapi/linux/dcbnl.h | 1 +
net/dcb/dcbnl.c | 275 ++++++++++++++----
7 files changed, 550 insertions(+), 73 deletions(-)

--
2.34.1


2023-01-12 20:53:24

by Daniel Machon

[permalink] [raw]
Subject: [PATCH net-next 3/6] net: dcb: add new rewrite table

Add new rewrite table and all the required functions, offload hooks and
bookkeeping for maintaining it. The rewrite table reuses the app struct,
and the entire set of app selectors. As such, some bookeeping code can
be shared between the rewrite- and the APP table.

New functions for getting, setting and deleting entries has been added.
Apart from operating on the rewrite list, these functions do not emit a
DCB_APP_EVENT when the list os modified. The new dcb_getrewr does a
lookup based on selector and priority and returns the protocol, so that
mappings from priority to protocol, for a given selector and ifindex is
obtained.

Also, a new nested attribute has been added, that encapsulates one or
more app structs. This attribute is used to distinguish the two tables.

The dcb_lock used for the APP table is reused for the rewrite table.

Signed-off-by: Daniel Machon <[email protected]>
---
include/net/dcbnl.h | 8 +++
include/uapi/linux/dcbnl.h | 2 +
net/dcb/dcbnl.c | 112 ++++++++++++++++++++++++++++++++++++-
3 files changed, 121 insertions(+), 1 deletion(-)

diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h
index 8841ab6c2de7..fe7dfb8bcb5b 100644
--- a/include/net/dcbnl.h
+++ b/include/net/dcbnl.h
@@ -19,6 +19,10 @@ struct dcb_app_type {
u8 dcbx;
};

+u16 dcb_getrewr(struct net_device *dev, struct dcb_app *app);
+int dcb_setrewr(struct net_device *dev, struct dcb_app *app);
+int dcb_delrewr(struct net_device *dev, struct dcb_app *app);
+
int dcb_setapp(struct net_device *, struct dcb_app *);
u8 dcb_getapp(struct net_device *, struct dcb_app *);
int dcb_ieee_setapp(struct net_device *, struct dcb_app *);
@@ -113,6 +117,10 @@ struct dcbnl_rtnl_ops {
/* apptrust */
int (*dcbnl_setapptrust)(struct net_device *, u8 *, int);
int (*dcbnl_getapptrust)(struct net_device *, u8 *, int *);
+
+ /* rewrite */
+ int (*dcbnl_setrewr)(struct net_device *dev, struct dcb_app *app);
+ int (*dcbnl_delrewr)(struct net_device *dev, struct dcb_app *app);
};

#endif /* __NET_DCBNL_H__ */
diff --git a/include/uapi/linux/dcbnl.h b/include/uapi/linux/dcbnl.h
index 99047223ab26..7e15214aa5dd 100644
--- a/include/uapi/linux/dcbnl.h
+++ b/include/uapi/linux/dcbnl.h
@@ -411,6 +411,7 @@ enum dcbnl_attrs {
* @DCB_ATTR_IEEE_PEER_PFC: peer PFC configuration - get only
* @DCB_ATTR_IEEE_PEER_APP: peer APP tlv - get only
* @DCB_ATTR_DCB_APP_TRUST_TABLE: selector trust table
+ * @DCB_ATTR_DCB_REWR_TABLE: rewrite configuration
*/
enum ieee_attrs {
DCB_ATTR_IEEE_UNSPEC,
@@ -425,6 +426,7 @@ enum ieee_attrs {
DCB_ATTR_IEEE_QCN_STATS,
DCB_ATTR_DCB_BUFFER,
DCB_ATTR_DCB_APP_TRUST_TABLE,
+ DCB_ATTR_DCB_REWR_TABLE,
__DCB_ATTR_IEEE_MAX
};
#define DCB_ATTR_IEEE_MAX (__DCB_ATTR_IEEE_MAX - 1)
diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
index 6d19564e19a8..b52acf30a0bc 100644
--- a/net/dcb/dcbnl.c
+++ b/net/dcb/dcbnl.c
@@ -178,6 +178,7 @@ static const struct nla_policy dcbnl_featcfg_nest[DCB_FEATCFG_ATTR_MAX + 1] = {
};

static LIST_HEAD(dcb_app_list);
+static LIST_HEAD(dcb_rewr_list);
static DEFINE_SPINLOCK(dcb_lock);

static enum ieee_attrs_app dcbnl_app_attr_type_get(u8 selector)
@@ -1142,7 +1143,7 @@ static int dcbnl_apprewr_setdel(struct nlattr *attr, struct net_device *netdev,
static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
{
const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
- struct nlattr *ieee, *app;
+ struct nlattr *ieee, *app, *rewr;
struct dcb_app_type *itr;
int dcbx;
int err;
@@ -1245,6 +1246,26 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
spin_unlock_bh(&dcb_lock);
nla_nest_end(skb, app);

+ rewr = nla_nest_start_noflag(skb, DCB_ATTR_DCB_REWR_TABLE);
+ if (!rewr)
+ return -EMSGSIZE;
+
+ spin_lock_bh(&dcb_lock);
+ list_for_each_entry(itr, &dcb_rewr_list, list) {
+ if (itr->ifindex == netdev->ifindex) {
+ enum ieee_attrs_app type =
+ dcbnl_app_attr_type_get(itr->app.selector);
+ err = nla_put(skb, type, sizeof(itr->app), &itr->app);
+ if (err) {
+ spin_unlock_bh(&dcb_lock);
+ return -EMSGSIZE;
+ }
+ }
+ }
+
+ spin_unlock_bh(&dcb_lock);
+ nla_nest_end(skb, rewr);
+
if (ops->dcbnl_getapptrust) {
err = dcbnl_getapptrust(netdev, skb);
if (err)
@@ -1606,6 +1627,14 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh,
goto err;
}

+ if (ieee[DCB_ATTR_DCB_REWR_TABLE]) {
+ err = dcbnl_apprewr_setdel(ieee[DCB_ATTR_DCB_REWR_TABLE],
+ netdev, dcb_setrewr,
+ ops->dcbnl_setrewr);
+ if (err)
+ goto err;
+ }
+
if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
err = dcbnl_apprewr_setdel(ieee[DCB_ATTR_IEEE_APP_TABLE],
netdev, dcb_ieee_setapp,
@@ -1705,6 +1734,14 @@ static int dcbnl_ieee_del(struct net_device *netdev, struct nlmsghdr *nlh,
goto err;
}

+ if (ieee[DCB_ATTR_DCB_REWR_TABLE]) {
+ err = dcbnl_apprewr_setdel(ieee[DCB_ATTR_DCB_REWR_TABLE],
+ netdev, dcb_delrewr,
+ ops->dcbnl_delrewr);
+ if (err)
+ goto err;
+ }
+
err:
err = nla_put_u8(skb, DCB_ATTR_IEEE, err);
dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_DEL, seq, 0);
@@ -1933,6 +1970,22 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
return ret;
}

+static struct dcb_app_type *dcb_rewr_lookup(const struct dcb_app *app,
+ int ifindex, int proto)
+{
+ struct dcb_app_type *itr;
+
+ list_for_each_entry(itr, &dcb_rewr_list, list) {
+ if (itr->app.selector == app->selector &&
+ itr->app.priority == app->priority &&
+ itr->ifindex == ifindex &&
+ ((proto == -1) || itr->app.protocol == proto))
+ return itr;
+ }
+
+ return NULL;
+}
+
static struct dcb_app_type *dcb_app_lookup(const struct dcb_app *app,
int ifindex, int prio)
{
@@ -2056,6 +2109,63 @@ u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app)
}
EXPORT_SYMBOL(dcb_ieee_getapp_mask);

+/* Get protocol value from rewrite entry. */
+u16 dcb_getrewr(struct net_device *dev, struct dcb_app *app)
+{
+ struct dcb_app_type *itr;
+ u8 proto = 0;
+
+ spin_lock_bh(&dcb_lock);
+ itr = dcb_rewr_lookup(app, dev->ifindex, -1);
+ if (itr)
+ proto = itr->app.protocol;
+ spin_unlock_bh(&dcb_lock);
+
+ return proto;
+}
+EXPORT_SYMBOL(dcb_getrewr);
+
+ /* Add rewrite entry to the rewrite list. */
+int dcb_setrewr(struct net_device *dev, struct dcb_app *new)
+{
+ int err = 0;
+
+ spin_lock_bh(&dcb_lock);
+ /* Search for existing match and abort if found. */
+ if (dcb_rewr_lookup(new, dev->ifindex, new->protocol)) {
+ err = -EEXIST;
+ goto out;
+ }
+
+ err = dcb_app_add(&dcb_rewr_list, new, dev->ifindex);
+out:
+ spin_unlock_bh(&dcb_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(dcb_setrewr);
+
+/* Delete rewrite entry from the rewrite list. */
+int dcb_delrewr(struct net_device *dev, struct dcb_app *del)
+{
+ struct dcb_app_type *itr;
+ int err = -ENOENT;
+
+ spin_lock_bh(&dcb_lock);
+ /* Search for existing match and remove it. */
+ itr = dcb_rewr_lookup(del, dev->ifindex, del->protocol);
+ if (itr) {
+ list_del(&itr->list);
+ kfree(itr);
+ err = 0;
+ }
+
+ spin_unlock_bh(&dcb_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(dcb_delrewr);
+
/**
* dcb_ieee_setapp - add IEEE dcb application data to app list
* @dev: network interface
--
2.34.1

2023-01-12 20:55:06

by Daniel Machon

[permalink] [raw]
Subject: [PATCH net-next 6/6] net: microchip: sparx5: add support for DSCP rewrite

Add support for DSCP rewrite in Sparx5 driver. On egress DSCP is
rewritten from either classified DSCP, or frame DSCP. Classified DSCP is
determined by the Analyzer Classifier on ingress, and is mapped from
classified QoS class and DP level. Classification of DSCP is by default
enabled for all ports.

It is required that DSCP is trusted for the egress port *and* rewrite
table is not empty, in order to rewrite DSCP based on classified DSCP,
otherwise DSCP is always rewritten from frame DSCP.

classified_dscp = qos_dscp_map[8 * dp_level + qos_class];
if (active_mappings && dscp_is_trusted)
rewritten_dscp = classified_dscp
else
rewritten_dscp = frame_dscp

To rewrite DSCP to 20 for any frames with priority 7:

$ dcb apptrust set dev eth0 order dscp
$ dcb rewr add dev eth0 7:20 <-- not in iproute2/dcb yet

Signed-off-by: Daniel Machon <[email protected]>
---
.../ethernet/microchip/sparx5/sparx5_dcb.c | 35 ++++++++++++++++
.../microchip/sparx5/sparx5_main_regs.h | 26 ++++++++++++
.../ethernet/microchip/sparx5/sparx5_port.c | 40 +++++++++++++++++++
.../ethernet/microchip/sparx5/sparx5_port.h | 23 +++++++++++
4 files changed, 124 insertions(+)

diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c
index dd321dd9f223..871a3e62f852 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c
@@ -133,6 +133,7 @@ static bool sparx5_dcb_apptrust_contains(int portno, u8 selector)

static int sparx5_dcb_app_update(struct net_device *dev)
{
+ struct dcb_ieee_app_prio_map dscp_rewr_map = {0};
struct dcb_rewr_prio_pcp_map pcp_rewr_map = {0};
struct sparx5_port *port = netdev_priv(dev);
struct sparx5_port_qos_dscp_map *dscp_map;
@@ -140,7 +141,9 @@ static int sparx5_dcb_app_update(struct net_device *dev)
struct sparx5_port_qos qos = {0};
struct dcb_app app_itr = {0};
int portno = port->portno;
+ bool dscp_rewr = false;
bool pcp_rewr = false;
+ u16 dscp;
int i;

dscp_map = &qos.dscp.map;
@@ -174,6 +177,26 @@ static int sparx5_dcb_app_update(struct net_device *dev)
qos.pcp_rewr.map.map[i] = fls(pcp_rewr_map.map[i]) - 1;
}

+ /* Get dscp rewrite mapping */
+ dcb_getrewr_prio_dscp_mask_map(dev, &dscp_rewr_map);
+ for (i = 0; i < ARRAY_SIZE(dscp_rewr_map.map); i++) {
+ if (!dscp_rewr_map.map[i])
+ continue;
+
+ /* The rewrite table of the switch has 32 entries; one for each
+ * priority for each DP level. Currently, the rewrite map does
+ * not indicate DP level, so we map classified QoS class to
+ * classified DSCP, for each classified DP level. Rewrite of
+ * DSCP is only enabled, if we have active mappings.
+ */
+ dscp_rewr = true;
+ dscp = fls64(dscp_rewr_map.map[i]) - 1;
+ qos.dscp_rewr.map.map[i] = dscp; /* DP 0 */
+ qos.dscp_rewr.map.map[i + 8] = dscp; /* DP 1 */
+ qos.dscp_rewr.map.map[i + 16] = dscp; /* DP 2 */
+ qos.dscp_rewr.map.map[i + 24] = dscp; /* DP 3 */
+ }
+
/* Enable use of pcp for queue classification ? */
if (sparx5_dcb_apptrust_contains(portno, DCB_APP_SEL_PCP)) {
qos.pcp.qos_enable = true;
@@ -189,6 +212,12 @@ static int sparx5_dcb_app_update(struct net_device *dev)
if (sparx5_dcb_apptrust_contains(portno, IEEE_8021QAZ_APP_SEL_DSCP)) {
qos.dscp.qos_enable = true;
qos.dscp.dp_enable = qos.dscp.qos_enable;
+ if (dscp_rewr)
+ /* Do not enable rewrite if no mappings are active, as
+ * classified DSCP will then be zero for all classified
+ * QoS class and DP combinations.
+ */
+ qos.dscp_rewr.enable = true;
}

return sparx5_port_qos_set(port, &qos);
@@ -366,6 +395,12 @@ int sparx5_dcb_init(struct sparx5 *sparx5)
sparx5_port_apptrust[port->portno] =
&sparx5_dcb_apptrust_policies
[SPARX5_DCB_APPTRUST_DSCP_PCP];
+
+ /* Enable DSCP classification based on classified QoS class and
+ * DP, for all DSCP values, for all ports.
+ */
+ sparx5_port_qos_dscp_rewr_mode_set(port,
+ SPARX5_PORT_REW_DSCP_ALL);
}

return 0;
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
index 0d3bf2e84102..a4a4d893dcb2 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
@@ -885,6 +885,16 @@ enum sparx5_target {
#define ANA_CL_DSCP_CFG_DSCP_TRUST_ENA_GET(x)\
FIELD_GET(ANA_CL_DSCP_CFG_DSCP_TRUST_ENA, x)

+/* ANA_CL:COMMON:QOS_MAP_CFG */
+#define ANA_CL_QOS_MAP_CFG(r) \
+ __REG(TARGET_ANA_CL, 0, 1, 166912, 0, 1, 756, 512, r, 32, 4)
+
+#define ANA_CL_QOS_MAP_CFG_DSCP_REWR_VAL GENMASK(9, 4)
+#define ANA_CL_QOS_MAP_CFG_DSCP_REWR_VAL_SET(x)\
+ FIELD_PREP(ANA_CL_QOS_MAP_CFG_DSCP_REWR_VAL, x)
+#define ANA_CL_QOS_MAP_CFG_DSCP_REWR_VAL_GET(x)\
+ FIELD_GET(ANA_CL_QOS_MAP_CFG_DSCP_REWR_VAL, x)
+
/* ANA_L2:COMMON:AUTO_LRN_CFG */
#define ANA_L2_AUTO_LRN_CFG __REG(TARGET_ANA_L2, 0, 1, 566024, 0, 1, 700, 24, 0, 1, 4)

@@ -5385,6 +5395,22 @@ enum sparx5_target {
#define REW_DEI_MAP_DE1_DEI_DE1_GET(x)\
FIELD_GET(REW_DEI_MAP_DE1_DEI_DE1, x)

+/* REW:PORT:DSCP_MAP */
+#define REW_DSCP_MAP(g) \
+ __REG(TARGET_REW, 0, 1, 360448, g, 70, 256, 136, 0, 1, 4)
+
+#define REW_DSCP_MAP_DSCP_UPDATE_ENA BIT(1)
+#define REW_DSCP_MAP_DSCP_UPDATE_ENA_SET(x)\
+ FIELD_PREP(REW_DSCP_MAP_DSCP_UPDATE_ENA, x)
+#define REW_DSCP_MAP_DSCP_UPDATE_ENA_GET(x)\
+ FIELD_GET(REW_DSCP_MAP_DSCP_UPDATE_ENA, x)
+
+#define REW_DSCP_MAP_DSCP_REMAP_ENA BIT(0)
+#define REW_DSCP_MAP_DSCP_REMAP_ENA_SET(x)\
+ FIELD_PREP(REW_DSCP_MAP_DSCP_REMAP_ENA, x)
+#define REW_DSCP_MAP_DSCP_REMAP_ENA_GET(x)\
+ FIELD_GET(REW_DSCP_MAP_DSCP_REMAP_ENA, x)
+
/* REW:PORT:TAG_CTRL */
#define REW_TAG_CTRL(g) __REG(TARGET_REW, 0, 1, 360448, g, 70, 256, 132, 0, 1, 4)

diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
index c8b5087769ed..246259b2ae94 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
@@ -1152,6 +1152,7 @@ int sparx5_port_qos_set(struct sparx5_port *port,
sparx5_port_qos_dscp_set(port, &qos->dscp);
sparx5_port_qos_pcp_set(port, &qos->pcp);
sparx5_port_qos_pcp_rewr_set(port, &qos->pcp_rewr);
+ sparx5_port_qos_dscp_rewr_set(port, &qos->dscp_rewr);
sparx5_port_qos_default_set(port, qos);

return 0;
@@ -1241,6 +1242,45 @@ int sparx5_port_qos_pcp_set(const struct sparx5_port *port,
return 0;
}

+void sparx5_port_qos_dscp_rewr_mode_set(const struct sparx5_port *port,
+ int mode)
+{
+ spx5_rmw(ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL_SET(mode),
+ ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL, port->sparx5,
+ ANA_CL_QOS_CFG(port->portno));
+}
+
+int sparx5_port_qos_dscp_rewr_set(const struct sparx5_port *port,
+ struct sparx5_port_qos_dscp_rewr *qos)
+{
+ struct sparx5 *sparx5 = port->sparx5;
+ bool rewr = false;
+ u16 dscp;
+ int i;
+
+ /* On egress, rewrite DSCP value to either classified DSCP or frame
+ * DSCP. If enabled; classified DSCP, if disabled; frame DSCP.
+ */
+ if (qos->enable)
+ rewr = true;
+
+ spx5_rmw(REW_DSCP_MAP_DSCP_UPDATE_ENA_SET(rewr),
+ REW_DSCP_MAP_DSCP_UPDATE_ENA, sparx5,
+ REW_DSCP_MAP(port->portno));
+
+ /* On ingress, map each classified QoS class and DP to classified DSCP
+ * value. This mapping table is global for all ports.
+ */
+ for (i = 0; i < ARRAY_SIZE(qos->map.map); i++) {
+ dscp = qos->map.map[i];
+ spx5_rmw(ANA_CL_QOS_MAP_CFG_DSCP_REWR_VAL_SET(dscp),
+ ANA_CL_QOS_MAP_CFG_DSCP_REWR_VAL, sparx5,
+ ANA_CL_QOS_MAP_CFG(i));
+ }
+
+ return 0;
+}
+
int sparx5_port_qos_dscp_set(const struct sparx5_port *port,
struct sparx5_port_qos_dscp *qos)
{
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h
index b09c09d10a16..607c4ff1df6b 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h
@@ -14,6 +14,12 @@
#define SPARX5_PORT_REW_TAG_CTRL_DEFAULT 1
#define SPARX5_PORT_REW_TAG_CTRL_MAPPED 2

+/* Port DSCP rewrite mode */
+#define SPARX5_PORT_REW_DSCP_NONE 0
+#define SPARX5_PORT_REW_DSCP_IF_ZERO 1
+#define SPARX5_PORT_REW_DSCP_SELECTED 2
+#define SPARX5_PORT_REW_DSCP_ALL 3
+
static inline bool sparx5_port_is_2g5(int portno)
{
return portno >= 16 && portno <= 47;
@@ -108,6 +114,11 @@ struct sparx5_port_qos_pcp_rewr_map {
u16 map[SPX5_PRIOS];
};

+#define SPARX5_PORT_QOS_DP_NUM 4
+struct sparx5_port_qos_dscp_rewr_map {
+ u16 map[SPX5_PRIOS * SPARX5_PORT_QOS_DP_NUM];
+};
+
#define SPARX5_PORT_QOS_DSCP_COUNT 64
struct sparx5_port_qos_dscp_map {
u8 map[SPARX5_PORT_QOS_DSCP_COUNT];
@@ -130,10 +141,16 @@ struct sparx5_port_qos_dscp {
bool dp_enable;
};

+struct sparx5_port_qos_dscp_rewr {
+ struct sparx5_port_qos_dscp_rewr_map map;
+ bool enable;
+};
+
struct sparx5_port_qos {
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_rewr dscp_rewr;
u8 default_prio;
};

@@ -148,6 +165,12 @@ int sparx5_port_qos_pcp_rewr_set(const struct sparx5_port *port,
int sparx5_port_qos_dscp_set(const struct sparx5_port *port,
struct sparx5_port_qos_dscp *qos);

+void sparx5_port_qos_dscp_rewr_mode_set(const struct sparx5_port *port,
+ int mode);
+
+int sparx5_port_qos_dscp_rewr_set(const struct sparx5_port *port,
+ struct sparx5_port_qos_dscp_rewr *qos);
+
int sparx5_port_qos_default_set(const struct sparx5_port *port,
const struct sparx5_port_qos *qos);

--
2.34.1

2023-01-12 20:55:07

by Daniel Machon

[permalink] [raw]
Subject: [PATCH net-next 2/6] net: dcb: add new common function for set/del of app/rewr entries

In preparation for DCB rewrite. Add a new function for setting and
deleting both app and rewrite entries. Moving this into a separate
function reduces duplicate code, as both type of entries requires the
same set of checks. The function will now iterate through a configurable
nested attribute (app or rewrite attr), validate each attribute and call
the appropriate set- or delete function.

Note that this function always checks for nla_len(attr_itr) <
sizeof(struct dcb_app), which was only done in dcbnl_ieee_set and not in
dcbnl_ieee_del prior to this patch. This means, that any userspace tool
that used to shove in data < sizeof(struct dcb_app) would now receive
-ERANGE.

Signed-off-by: Daniel Machon <[email protected]>
---
net/dcb/dcbnl.c | 104 +++++++++++++++++++++++-------------------------
1 file changed, 49 insertions(+), 55 deletions(-)

diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
index a76bdf6f0198..6d19564e19a8 100644
--- a/net/dcb/dcbnl.c
+++ b/net/dcb/dcbnl.c
@@ -1099,6 +1099,45 @@ static int dcbnl_getapptrust(struct net_device *netdev, struct sk_buff *skb)
return err;
}

+/* Set or delete APP table or rewrite table entries. The APP struct is validated
+ * and the appropriate callback function is called.
+ */
+static int dcbnl_apprewr_setdel(struct nlattr *attr, struct net_device *netdev,
+ int (*setdel)(struct net_device *dev,
+ struct dcb_app *app),
+ int (*ops_setdel)(struct net_device *dev,
+ struct dcb_app *app))
+{
+ struct dcb_app *app_data;
+ enum ieee_attrs_app type;
+ struct nlattr *attr_itr;
+ int rem, err;
+
+ nla_for_each_nested(attr_itr, attr, rem) {
+ type = nla_type(attr_itr);
+
+ if (!dcbnl_app_attr_type_validate(type))
+ continue;
+
+ if (nla_len(attr_itr) < sizeof(struct dcb_app))
+ return -ERANGE;
+
+ app_data = nla_data(attr_itr);
+
+ if (!dcbnl_app_selector_validate(type, app_data->selector))
+ return -EINVAL;
+
+ if (ops_setdel)
+ err = ops_setdel(netdev, app_data);
+ else
+ err = setdel(netdev, app_data);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
/* Handle IEEE 802.1Qaz/802.1Qau/802.1Qbb GET commands. */
static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
{
@@ -1568,36 +1607,11 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh,
}

if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
- struct nlattr *attr;
- int rem;
-
- nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
- enum ieee_attrs_app type = nla_type(attr);
- struct dcb_app *app_data;
-
- if (!dcbnl_app_attr_type_validate(type))
- continue;
-
- if (nla_len(attr) < sizeof(struct dcb_app)) {
- err = -ERANGE;
- goto err;
- }
-
- app_data = nla_data(attr);
-
- if (!dcbnl_app_selector_validate(type,
- app_data->selector)) {
- err = -EINVAL;
- goto err;
- }
-
- if (ops->ieee_setapp)
- err = ops->ieee_setapp(netdev, app_data);
- else
- err = dcb_ieee_setapp(netdev, app_data);
- if (err)
- goto err;
- }
+ err = dcbnl_apprewr_setdel(ieee[DCB_ATTR_IEEE_APP_TABLE],
+ netdev, dcb_ieee_setapp,
+ ops->ieee_setapp);
+ if (err)
+ goto err;
}

if (ieee[DCB_ATTR_DCB_APP_TRUST_TABLE]) {
@@ -1684,31 +1698,11 @@ static int dcbnl_ieee_del(struct net_device *netdev, struct nlmsghdr *nlh,
return err;

if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
- struct nlattr *attr;
- int rem;
-
- nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
- enum ieee_attrs_app type = nla_type(attr);
- struct dcb_app *app_data;
-
- if (!dcbnl_app_attr_type_validate(type))
- continue;
-
- app_data = nla_data(attr);
-
- if (!dcbnl_app_selector_validate(type,
- app_data->selector)) {
- err = -EINVAL;
- goto err;
- }
-
- if (ops->ieee_delapp)
- err = ops->ieee_delapp(netdev, app_data);
- else
- err = dcb_ieee_delapp(netdev, app_data);
- if (err)
- goto err;
- }
+ err = dcbnl_apprewr_setdel(ieee[DCB_ATTR_IEEE_APP_TABLE],
+ netdev, dcb_ieee_delapp,
+ ops->ieee_delapp);
+ if (err)
+ goto err;
}

err:
--
2.34.1

2023-01-12 21:31:12

by Daniel Machon

[permalink] [raw]
Subject: [PATCH net-next 1/6] net: dcb: modify dcb_app_add to take list_head ptr as parameter

In preparation to DCB rewrite. Modify dcb_app_add to take new struct
list_head * as parameter, to make the used list configurable. This is
done to allow reusing the function for adding rewrite entries to the
rewrite table, which is introduced in a later patch.

Signed-off-by: Daniel Machon <[email protected]>
---
net/dcb/dcbnl.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
index f9949e051f49..a76bdf6f0198 100644
--- a/net/dcb/dcbnl.c
+++ b/net/dcb/dcbnl.c
@@ -1955,7 +1955,8 @@ static struct dcb_app_type *dcb_app_lookup(const struct dcb_app *app,
return NULL;
}

-static int dcb_app_add(const struct dcb_app *app, int ifindex)
+static int dcb_app_add(struct list_head *list, const struct dcb_app *app,
+ int ifindex)
{
struct dcb_app_type *entry;

@@ -1965,7 +1966,7 @@ static int dcb_app_add(const struct dcb_app *app, int ifindex)

memcpy(&entry->app, app, sizeof(*app));
entry->ifindex = ifindex;
- list_add(&entry->list, &dcb_app_list);
+ list_add(&entry->list, list);

return 0;
}
@@ -2028,7 +2029,7 @@ int dcb_setapp(struct net_device *dev, struct dcb_app *new)
}
/* App type does not exist add new application type */
if (new->priority)
- err = dcb_app_add(new, dev->ifindex);
+ err = dcb_app_add(&dcb_app_list, new, dev->ifindex);
out:
spin_unlock_bh(&dcb_lock);
if (!err)
@@ -2088,7 +2089,7 @@ int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new)
goto out;
}

- err = dcb_app_add(new, dev->ifindex);
+ err = dcb_app_add(&dcb_app_list, new, dev->ifindex);
out:
spin_unlock_bh(&dcb_lock);
if (!err)
--
2.34.1

2023-01-13 07:37:00

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH net-next 3/6] net: dcb: add new rewrite table

On Thu, Jan 12, 2023 at 09:15:51PM +0100, Daniel Machon wrote:
> +/* Get protocol value from rewrite entry. */
> +u16 dcb_getrewr(struct net_device *dev, struct dcb_app *app)
^^^

> +{
> + struct dcb_app_type *itr;
> + u8 proto = 0;

Should "proto" be a u16 to match itr->app.protocol and the return type?

> +
> + spin_lock_bh(&dcb_lock);
> + itr = dcb_rewr_lookup(app, dev->ifindex, -1);
> + if (itr)
> + proto = itr->app.protocol;
> + spin_unlock_bh(&dcb_lock);
> +
> + return proto;
> +}
> +EXPORT_SYMBOL(dcb_getrewr);
> +
> + /* Add rewrite entry to the rewrite list. */
> +int dcb_setrewr(struct net_device *dev, struct dcb_app *new)
> +{
> + int err = 0;

No need to initialize this. It only disables static checkers and
triggers a false positive about dead stores.

> +
> + spin_lock_bh(&dcb_lock);
> + /* Search for existing match and abort if found. */
> + if (dcb_rewr_lookup(new, dev->ifindex, new->protocol)) {
> + err = -EEXIST;
> + goto out;
> + }
> +
> + err = dcb_app_add(&dcb_rewr_list, new, dev->ifindex);
> +out:
> + spin_unlock_bh(&dcb_lock);
> +
> + return err;
> +}

regards,
dan carpenter

2023-01-13 15:48:06

by Petr Machata

[permalink] [raw]
Subject: Re: [PATCH net-next 2/6] net: dcb: add new common function for set/del of app/rewr entries


Daniel Machon <[email protected]> writes:

> In preparation for DCB rewrite. Add a new function for setting and
> deleting both app and rewrite entries. Moving this into a separate
> function reduces duplicate code, as both type of entries requires the
> same set of checks. The function will now iterate through a configurable
> nested attribute (app or rewrite attr), validate each attribute and call
> the appropriate set- or delete function.
>
> Note that this function always checks for nla_len(attr_itr) <
> sizeof(struct dcb_app), which was only done in dcbnl_ieee_set and not in
> dcbnl_ieee_del prior to this patch. This means, that any userspace tool
> that used to shove in data < sizeof(struct dcb_app) would now receive
> -ERANGE.

Good.

> Signed-off-by: Daniel Machon <[email protected]>
> ---
> net/dcb/dcbnl.c | 104 +++++++++++++++++++++++-------------------------
> 1 file changed, 49 insertions(+), 55 deletions(-)
>
> diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
> index a76bdf6f0198..6d19564e19a8 100644
> --- a/net/dcb/dcbnl.c
> +++ b/net/dcb/dcbnl.c
> @@ -1099,6 +1099,45 @@ static int dcbnl_getapptrust(struct net_device *netdev, struct sk_buff *skb)
> return err;
> }
>
> +/* Set or delete APP table or rewrite table entries. The APP struct is validated
> + * and the appropriate callback function is called.
> + */
> +static int dcbnl_apprewr_setdel(struct nlattr *attr, struct net_device *netdev,
> + int (*setdel)(struct net_device *dev,
> + struct dcb_app *app),
> + int (*ops_setdel)(struct net_device *dev,
> + struct dcb_app *app))

The name makes it look like it's rewrite-specific. Maybe make it
_app_table_? Given both DCB app and DCB rewrite use app table as the
database... Dunno. Not a big deal.

> +{
> + struct dcb_app *app_data;
> + enum ieee_attrs_app type;
> + struct nlattr *attr_itr;
> + int rem, err;
> +
> + nla_for_each_nested(attr_itr, attr, rem) {
> + type = nla_type(attr_itr);
> +
> + if (!dcbnl_app_attr_type_validate(type))
> + continue;
> +
> + if (nla_len(attr_itr) < sizeof(struct dcb_app))
> + return -ERANGE;
> +
> + app_data = nla_data(attr_itr);
> +
> + if (!dcbnl_app_selector_validate(type, app_data->selector))
> + return -EINVAL;
> +
> + if (ops_setdel)
> + err = ops_setdel(netdev, app_data);
> + else
> + err = setdel(netdev, app_data);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> /* Handle IEEE 802.1Qaz/802.1Qau/802.1Qbb GET commands. */
> static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
> {
> @@ -1568,36 +1607,11 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh,
> }
>
> if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
> - struct nlattr *attr;
> - int rem;
> -
> - nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
> - enum ieee_attrs_app type = nla_type(attr);
> - struct dcb_app *app_data;
> -
> - if (!dcbnl_app_attr_type_validate(type))
> - continue;
> -
> - if (nla_len(attr) < sizeof(struct dcb_app)) {
> - err = -ERANGE;
> - goto err;
> - }
> -
> - app_data = nla_data(attr);
> -
> - if (!dcbnl_app_selector_validate(type,
> - app_data->selector)) {
> - err = -EINVAL;
> - goto err;
> - }
> -
> - if (ops->ieee_setapp)
> - err = ops->ieee_setapp(netdev, app_data);
> - else
> - err = dcb_ieee_setapp(netdev, app_data);
> - if (err)
> - goto err;
> - }
> + err = dcbnl_apprewr_setdel(ieee[DCB_ATTR_IEEE_APP_TABLE],
> + netdev, dcb_ieee_setapp,
> + ops->ieee_setapp);

This could pre-resolve the callback to use and pass one pointer:

err = dcbnl_apprewr_setdel(ieee[DCB_ATTR_IEEE_APP_TABLE], netdev,
ops->ieee_setapp ?: dcb_ieee_setapp);

And the same below.

> + if (err)
> + goto err;
> }
>
> if (ieee[DCB_ATTR_DCB_APP_TRUST_TABLE]) {
> @@ -1684,31 +1698,11 @@ static int dcbnl_ieee_del(struct net_device *netdev, struct nlmsghdr *nlh,
> return err;
>
> if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
> - struct nlattr *attr;
> - int rem;
> -
> - nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
> - enum ieee_attrs_app type = nla_type(attr);
> - struct dcb_app *app_data;
> -
> - if (!dcbnl_app_attr_type_validate(type))
> - continue;
> - app_data = nla_data(attr);
> -
> - if (!dcbnl_app_selector_validate(type,
> - app_data->selector)) {
> - err = -EINVAL;
> - goto err;
> - }
> -
> - if (ops->ieee_delapp)
> - err = ops->ieee_delapp(netdev, app_data);
> - else
> - err = dcb_ieee_delapp(netdev, app_data);
> - if (err)
> - goto err;
> - }
> + err = dcbnl_apprewr_setdel(ieee[DCB_ATTR_IEEE_APP_TABLE],
> + netdev, dcb_ieee_delapp,
> + ops->ieee_delapp);
> + if (err)
> + goto err;
> }
>
> err:

2023-01-13 16:42:51

by Petr Machata

[permalink] [raw]
Subject: Re: [PATCH net-next 3/6] net: dcb: add new rewrite table


Daniel Machon <[email protected]> writes:

> Add new rewrite table and all the required functions, offload hooks and
> bookkeeping for maintaining it. The rewrite table reuses the app struct,
> and the entire set of app selectors. As such, some bookeeping code can
> be shared between the rewrite- and the APP table.
>
> New functions for getting, setting and deleting entries has been added.
> Apart from operating on the rewrite list, these functions do not emit a
> DCB_APP_EVENT when the list os modified. The new dcb_getrewr does a
> lookup based on selector and priority and returns the protocol, so that
> mappings from priority to protocol, for a given selector and ifindex is
> obtained.

Yeah, the call_dcbevent_notifiers() business can be added when there are
any users.

>
> Also, a new nested attribute has been added, that encapsulates one or
> more app structs. This attribute is used to distinguish the two tables.
>
> The dcb_lock used for the APP table is reused for the rewrite table.
>
> Signed-off-by: Daniel Machon <[email protected]>

No comments about the code besides what Dan has pointed out.

2023-01-13 17:00:50

by Petr Machata

[permalink] [raw]
Subject: Re: [PATCH net-next 0/6] Introduce new DCB rewrite table


Daniel Machon <[email protected]> writes:

> There is currently no support for per-port egress mapping of priority to PCP and
> priority to DSCP. Some support for expressing egress mapping of PCP is supported
> through ip link, with the 'egress-qos-map', however this command only maps
> priority to PCP, and for vlan interfaces only. DCB APP already has support for
> per-port ingress mapping of PCP/DEI, DSCP and a bunch of other stuff. So why not
> take advantage of this fact, and add a new table that does the reverse.
>
> This patch series introduces the new DCB rewrite table. Whereas the DCB
> APP table deals with ingress mapping of PID (protocol identifier) to priority,
> the rewrite table deals with egress mapping of priority to PID.
>
> It is indeed possible to integrate rewrite in the existing APP table, by
> introducing new dedicated rewrite selectors, and altering existing functions
> to treat rewrite entries specially. However, I feel like this is not a good
> solution, and will pollute the APP namespace. APP is well-defined in IEEE, and
> some userspace relies of advertised entries - for this fact, separating APP and
> rewrite into to completely separate objects, seems to me the best solution.
>
> The new table shares much functionality with the APP table, and as such, much
> existing code is reused, or slightly modified, to work for both.
>
> ================================================================================
> DCB rewrite table in a nutshell
> ================================================================================
> The table is implemented as a simple linked list, and uses the same lock as the
> APP table. New functions for getting, setting and deleting entries have been
> added, and these are exported, so they can be used by the stack or drivers.
> Additionnaly, new dcbnl_setrewr and dcnl_delrewr hooks has been added, to
> support hardware offload of the entries.

Looks good to me overall.

I just want to add that to configure rewrite, mlxsw currently reverses
the APP prioritization table. That's not ideal, and is lossy as
well--certain configurations simply can't be expressed however you set
up in-driver heuristics. The proposed interfaces would make
configuration of the rewrite functionality very straightforward.

2023-01-16 13:09:13

by Daniel Machon

[permalink] [raw]
Subject: Re: [PATCH net-next 3/6] net: dcb: add new rewrite table

Hi Dan,
Thank you for your feedback.

> On Thu, Jan 12, 2023 at 09:15:51PM +0100, Daniel Machon wrote:
> > +/* Get protocol value from rewrite entry. */
> > +u16 dcb_getrewr(struct net_device *dev, struct dcb_app *app)
> ^^^
>
> > +{
> > + struct dcb_app_type *itr;
> > + u8 proto = 0;
>
> Should "proto" be a u16 to match itr->app.protocol and the return type?

It should.

>
> > +
> > + spin_lock_bh(&dcb_lock);
> > + itr = dcb_rewr_lookup(app, dev->ifindex, -1);
> > + if (itr)
> > + proto = itr->app.protocol;
> > + spin_unlock_bh(&dcb_lock);
> > +
> > + return proto;
> > +}
> > +EXPORT_SYMBOL(dcb_getrewr);
> > +
> > + /* Add rewrite entry to the rewrite list. */
> > +int dcb_setrewr(struct net_device *dev, struct dcb_app *new)
> > +{
> > + int err = 0;
>
> No need to initialize this. It only disables static checkers and
> triggers a false positive about dead stores.

Yes, you are right :)

Will be fixed in next version.

/Daniel

2023-01-16 13:23:21

by Daniel Machon

[permalink] [raw]
Subject: Re: [PATCH net-next 2/6] net: dcb: add new common function for set/del of app/rewr entries

> > Signed-off-by: Daniel Machon <[email protected]>
> > ---
> > net/dcb/dcbnl.c | 104 +++++++++++++++++++++++-------------------------
> > 1 file changed, 49 insertions(+), 55 deletions(-)
> >
> > diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
> > index a76bdf6f0198..6d19564e19a8 100644
> > --- a/net/dcb/dcbnl.c
> > +++ b/net/dcb/dcbnl.c
> > @@ -1099,6 +1099,45 @@ static int dcbnl_getapptrust(struct net_device *netdev, struct sk_buff *skb)
> > return err;
> > }
> >
> > +/* Set or delete APP table or rewrite table entries. The APP struct is validated
> > + * and the appropriate callback function is called.
> > + */
> > +static int dcbnl_apprewr_setdel(struct nlattr *attr, struct net_device *netdev,
> > + int (*setdel)(struct net_device *dev,
> > + struct dcb_app *app),
> > + int (*ops_setdel)(struct net_device *dev,
> > + struct dcb_app *app))
>
> The name makes it look like it's rewrite-specific. Maybe make it
> _app_table_? Given both DCB app and DCB rewrite use app table as the
> database... Dunno. Not a big deal.

Yeah, the other shared funcs are called _app_* too, so lets stick with
that naming. Agreed.

> > /* Handle IEEE 802.1Qaz/802.1Qau/802.1Qbb GET commands. */
> > static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
> > {
> > @@ -1568,36 +1607,11 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh,
> > }
> >
> > if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
> > - struct nlattr *attr;
> > - int rem;
> > -
> > - nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
> > - enum ieee_attrs_app type = nla_type(attr);
> > - struct dcb_app *app_data;
> > -
> > - if (!dcbnl_app_attr_type_validate(type))
> > - continue;
> > -
> > - if (nla_len(attr) < sizeof(struct dcb_app)) {
> > - err = -ERANGE;
> > - goto err;
> > - }
> > -
> > - app_data = nla_data(attr);
> > -
> > - if (!dcbnl_app_selector_validate(type,
> > - app_data->selector)) {
> > - err = -EINVAL;
> > - goto err;
> > - }
> > -
> > - if (ops->ieee_setapp)
> > - err = ops->ieee_setapp(netdev, app_data);
> > - else
> > - err = dcb_ieee_setapp(netdev, app_data);
> > - if (err)
> > - goto err;
> > - }
> > + err = dcbnl_apprewr_setdel(ieee[DCB_ATTR_IEEE_APP_TABLE],
> > + netdev, dcb_ieee_setapp,
> > + ops->ieee_setapp);
>
> This could pre-resolve the callback to use and pass one pointer:
>
> err = dcbnl_apprewr_setdel(ieee[DCB_ATTR_IEEE_APP_TABLE], netdev,
> ops->ieee_setapp ?: dcb_ieee_setapp);

Agreed.

Will make the suggested changes in v2 - thank you Petr.

/Daniel