This patch series is aimed at improving support for DCB (Data Center
Bridging) and DSCP (Differentiated Services Code Point) on KSZ switches.
The main goal is to introduce global DSCP and PCP (Priority Code Point)
mapping support, addressing the limitation of KSZ switches not having
per-port DSCP priority mapping. This involves extending the DSA
framework with new callbacks for managing trust settings for global DSCP
and PCP maps. Additionally, we introduce IEEE 802.1q helpers for default
configurations, benefiting other drivers too.
Change logs are in separate patches.
Oleksij Rempel (9):
net: dsa: add support for DCB get/set apptrust configuration
net: dsa: microchip: add IPV information support
net: add IEEE 802.1q specific helpers
net: dsa: microchip: add multi queue support for KSZ88X3 variants
net: dsa: microchip: add support for different DCB app configurations
net: dsa: microchip: dcb: add special handling for KSZ88X3 family
net: dsa: microchip: enable ETS support for KSZ989X variants
net: dsa: microchip: init predictable IPV to queue mapping for all non
KSZ8xxx variants
net: dsa: microchip: let DCB code do PCP and DSCP policy configuration
drivers/net/dsa/microchip/Kconfig | 2 +
drivers/net/dsa/microchip/Makefile | 2 +-
drivers/net/dsa/microchip/ksz8.h | 1 +
drivers/net/dsa/microchip/ksz8795.c | 106 ++--
drivers/net/dsa/microchip/ksz8795_reg.h | 9 +-
drivers/net/dsa/microchip/ksz9477.c | 6 -
drivers/net/dsa/microchip/ksz_common.c | 100 ++--
drivers/net/dsa/microchip/ksz_common.h | 11 +-
drivers/net/dsa/microchip/ksz_dcb.c | 764 ++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_dcb.h | 21 +
include/net/dsa.h | 4 +
include/net/dscp.h | 76 +++
include/net/ieee8021q.h | 55 ++
net/Kconfig | 3 +
net/core/Makefile | 1 +
net/core/ieee8021q_helpers.c | 208 +++++++
net/dsa/user.c | 28 +
17 files changed, 1311 insertions(+), 86 deletions(-)
create mode 100644 drivers/net/dsa/microchip/ksz_dcb.c
create mode 100644 drivers/net/dsa/microchip/ksz_dcb.h
create mode 100644 include/net/dscp.h
create mode 100644 include/net/ieee8021q.h
create mode 100644 net/core/ieee8021q_helpers.c
--
2.39.2
Add DCB support to get/set trust configuration for different packet
priority information sources. Some switch allow to chose different
source of packet priority classification. For example on KSZ switches it
is possible to configure VLAN PCP and/or DSCP sources.
Signed-off-by: Oleksij Rempel <[email protected]>
Reviewed-by: Florian Fainelli <[email protected]>
---
changes v6:
- port_set_apptrust pointer should be tested in dsa_user_dcbnl_set_apptrust()
changes v3:
- s/to choice/to chose/
---
include/net/dsa.h | 4 ++++
net/dsa/user.c | 28 ++++++++++++++++++++++++++++
2 files changed, 32 insertions(+)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 7c0da9effe4e9..96bde2aa86efd 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -955,6 +955,10 @@ struct dsa_switch_ops {
u8 prio);
int (*port_del_dscp_prio)(struct dsa_switch *ds, int port, u8 dscp,
u8 prio);
+ int (*port_set_apptrust)(struct dsa_switch *ds, int port,
+ const u8 *sel, int nsel);
+ int (*port_get_apptrust)(struct dsa_switch *ds, int port, u8 *sel,
+ int *nsel);
/*
* Suspend and resume
diff --git a/net/dsa/user.c b/net/dsa/user.c
index 16d395bb1a1fe..aa6c26cd63f56 100644
--- a/net/dsa/user.c
+++ b/net/dsa/user.c
@@ -2136,6 +2136,32 @@ int dsa_user_change_mtu(struct net_device *dev, int new_mtu)
return err;
}
+static int __maybe_unused
+dsa_user_dcbnl_set_apptrust(struct net_device *dev, u8 *sel, int nsel)
+{
+ struct dsa_port *dp = dsa_user_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+ int port = dp->index;
+
+ if (!ds->ops->port_set_apptrust)
+ return -EOPNOTSUPP;
+
+ return ds->ops->port_set_apptrust(ds, port, sel, nsel);
+}
+
+static int __maybe_unused
+dsa_user_dcbnl_get_apptrust(struct net_device *dev, u8 *sel, int *nsel)
+{
+ struct dsa_port *dp = dsa_user_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+ int port = dp->index;
+
+ if (!ds->ops->port_get_apptrust)
+ return -EOPNOTSUPP;
+
+ return ds->ops->port_get_apptrust(ds, port, sel, nsel);
+}
+
static int __maybe_unused
dsa_user_dcbnl_set_default_prio(struct net_device *dev, struct dcb_app *app)
{
@@ -2376,6 +2402,8 @@ static const struct ethtool_ops dsa_user_ethtool_ops = {
static const struct dcbnl_rtnl_ops __maybe_unused dsa_user_dcbnl_ops = {
.ieee_setapp = dsa_user_dcbnl_ieee_setapp,
.ieee_delapp = dsa_user_dcbnl_ieee_delapp,
+ .dcbnl_setapptrust = dsa_user_dcbnl_set_apptrust,
+ .dcbnl_getapptrust = dsa_user_dcbnl_get_apptrust,
};
static void dsa_user_get_stats64(struct net_device *dev,
--
2.39.2
Init priority to queue mapping in the way as it shown in IEEE 802.1Q
mapping example.
Signed-off-by: Oleksij Rempel <[email protected]>
---
changes v2:
- s/ksz_set_default_prio_queue_mapping/ksz9477_set_default_prio_queue_mapping
- remove error on queue < 0.
---
drivers/net/dsa/microchip/ksz_common.c | 57 +++++++++++++++-----------
1 file changed, 33 insertions(+), 24 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 87a807ac7900e..8a5d41025604a 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -24,6 +24,7 @@
#include <linux/of_net.h>
#include <linux/micrel_phy.h>
#include <net/dsa.h>
+#include <net/ieee8021q.h>
#include <net/pkt_cls.h>
#include <net/switchdev.h>
@@ -2672,9 +2673,33 @@ static int ksz_port_mdb_del(struct dsa_switch *ds, int port,
return dev->dev_ops->mdb_del(dev, port, mdb, db);
}
+static int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev,
+ int port)
+{
+ u32 queue_map = 0;
+ int ipv;
+
+ for (ipv = 0; ipv < dev->info->num_ipvs; ipv++) {
+ int queue;
+
+ /* Traffic Type (TT) is corresponding to the Internal Priority
+ * Value (IPV) in the switch. Traffic Class (TC) is
+ * corresponding to the queue in the switch.
+ */
+ queue = ieee8021q_tt_to_tc(ipv, dev->info->num_tx_queues);
+ if (queue < 0)
+ return queue;
+
+ queue_map |= queue << (ipv * KSZ9477_PORT_TC_MAP_S);
+ }
+
+ return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map);
+}
+
static int ksz_port_setup(struct dsa_switch *ds, int port)
{
struct ksz_device *dev = ds->priv;
+ int ret;
if (!dsa_is_user_port(ds, port))
return 0;
@@ -2682,6 +2707,12 @@ static int ksz_port_setup(struct dsa_switch *ds, int port)
/* setup user port */
dev->dev_ops->port_setup(dev, port, false);
+ if (!is_ksz8(dev)) {
+ ret = ksz9477_set_default_prio_queue_mapping(dev, port);
+ if (ret)
+ return ret;
+ }
+
/* port_stp_state_set() will be called after to enable the port so
* there is no need to do anything.
*/
@@ -3546,8 +3577,7 @@ static int ksz_tc_ets_add(struct ksz_device *dev, int port,
static int ksz_tc_ets_del(struct ksz_device *dev, int port)
{
- int ret, queue, tc_prio, s;
- u32 queue_map = 0;
+ int ret, queue;
/* To restore the default chip configuration, set all queues to use the
* WRR scheduler with a weight of 1.
@@ -3559,31 +3589,10 @@ static int ksz_tc_ets_del(struct ksz_device *dev, int port)
return ret;
}
- switch (dev->info->num_tx_queues) {
- case 2:
- s = 2;
- break;
- case 4:
- s = 1;
- break;
- case 8:
- s = 0;
- break;
- default:
- return -EINVAL;
- }
-
/* Revert the queue mapping for TC-priority to its default setting on
* the chip.
*/
- for (tc_prio = 0; tc_prio < dev->info->num_ipvs; tc_prio++) {
- int queue;
-
- queue = tc_prio >> s;
- queue_map |= queue << (tc_prio * KSZ9477_PORT_TC_MAP_S);
- }
-
- return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map);
+ return ksz9477_set_default_prio_queue_mapping(dev, port);
}
static int ksz_tc_ets_validate(struct ksz_device *dev, int port,
--
2.39.2
802.1P (PCP) and DiffServ (DSCP) are handled now by DCB code. Let it do
all needed initial configuration.
Signed-off-by: Oleksij Rempel <[email protected]>
Acked-by: Arun Ramadoss <[email protected]>
---
drivers/net/dsa/microchip/ksz8795.c | 6 ------
drivers/net/dsa/microchip/ksz9477.c | 6 ------
2 files changed, 12 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c
index 8511eb052dd2e..b2159810f0ed1 100644
--- a/drivers/net/dsa/microchip/ksz8795.c
+++ b/drivers/net/dsa/microchip/ksz8795.c
@@ -1565,16 +1565,10 @@ void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
ksz8_port_queue_split(dev, port, queues);
- /* disable DiffServ priority */
- ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_ENABLE, false);
-
/* replace priority */
ksz_port_cfg(dev, port, P_802_1P_CTRL,
masks[PORT_802_1P_REMAPPING], false);
- /* enable 802.1p priority */
- ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_ENABLE, true);
-
if (cpu_port)
member = dsa_user_ports(ds);
else
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 7f745628c84d1..f8ad7833f5d9d 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -1158,18 +1158,12 @@ void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
/* enable broadcast storm limit */
ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
- /* disable DiffServ priority */
- ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_PRIO_ENABLE, false);
-
/* replace priority */
ksz_port_cfg(dev, port, REG_PORT_MRI_MAC_CTRL, PORT_USER_PRIO_CEILING,
false);
ksz9477_port_cfg32(dev, port, REG_PORT_MTI_QUEUE_CTRL_0__4,
MTI_PVID_REPLACE, false);
- /* enable 802.1p priority */
- ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true);
-
/* force flow control for non-PHY ports only */
ksz_port_cfg(dev, port, REG_PORT_CTRL_0,
PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL,
--
2.39.2
KSZ88X3 switches support up to 4 queues. Rework ksz8795_set_prio_queue()
to support KSZ8795 and KSZ88X3 families of switches.
Per default, configure KSZ88X3 to use one queue, since it need special
handling due to priority related errata. Errata handling is implemented
in a separate patch.
Signed-off-by: Oleksij Rempel <[email protected]>
Acked-by: Arun Ramadoss <[email protected]>
---
changes v6:
- use if/else if, instead of if/if
---
drivers/net/dsa/microchip/ksz8795.c | 87 ++++++++++++++++---------
drivers/net/dsa/microchip/ksz8795_reg.h | 9 ++-
2 files changed, 61 insertions(+), 35 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c
index ecef6f6f830b3..33d6f990e1870 100644
--- a/drivers/net/dsa/microchip/ksz8795.c
+++ b/drivers/net/dsa/microchip/ksz8795.c
@@ -127,37 +127,56 @@ int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu)
return -EOPNOTSUPP;
}
-static void ksz8795_set_prio_queue(struct ksz_device *dev, int port, int queue)
+static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues)
{
- u8 hi, lo;
+ u8 mask_4q, mask_2q;
+ u8 reg_4q, reg_2q;
+ u8 data_4q = 0;
+ u8 data_2q = 0;
+ int ret;
- /* Number of queues can only be 1, 2, or 4. */
- switch (queue) {
- case 4:
- case 3:
- queue = PORT_QUEUE_SPLIT_4;
- break;
- case 2:
- queue = PORT_QUEUE_SPLIT_2;
- break;
- default:
- queue = PORT_QUEUE_SPLIT_1;
+ if (ksz_is_ksz88x3(dev)) {
+ mask_4q = KSZ8873_PORT_4QUEUE_SPLIT_EN;
+ mask_2q = KSZ8873_PORT_2QUEUE_SPLIT_EN;
+ reg_4q = REG_PORT_CTRL_0;
+ reg_2q = REG_PORT_CTRL_2;
+
+ /* KSZ8795 family switches have Weighted Fair Queueing (WFQ)
+ * enabled by default. Enable it for KSZ8873 family switches
+ * too. Default value for KSZ8873 family is strict priority,
+ * which should be enabled by using TC_SETUP_QDISC_ETS, not
+ * by default.
+ */
+ ret = ksz_rmw8(dev, REG_SW_CTRL_3, WEIGHTED_FAIR_QUEUE_ENABLE,
+ WEIGHTED_FAIR_QUEUE_ENABLE);
+ if (ret)
+ return ret;
+ } else {
+ mask_4q = KSZ8795_PORT_4QUEUE_SPLIT_EN;
+ mask_2q = KSZ8795_PORT_2QUEUE_SPLIT_EN;
+ reg_4q = REG_PORT_CTRL_13;
+ reg_2q = REG_PORT_CTRL_0;
+
+ /* TODO: this is legacy from initial KSZ8795 driver, should be
+ * moved to appropriate place in the future.
+ */
+ ret = ksz_rmw8(dev, REG_SW_CTRL_19,
+ SW_OUT_RATE_LIMIT_QUEUE_BASED,
+ SW_OUT_RATE_LIMIT_QUEUE_BASED);
+ if (ret)
+ return ret;
}
- ksz_pread8(dev, port, REG_PORT_CTRL_0, &lo);
- ksz_pread8(dev, port, P_DROP_TAG_CTRL, &hi);
- lo &= ~PORT_QUEUE_SPLIT_L;
- if (queue & PORT_QUEUE_SPLIT_2)
- lo |= PORT_QUEUE_SPLIT_L;
- hi &= ~PORT_QUEUE_SPLIT_H;
- if (queue & PORT_QUEUE_SPLIT_4)
- hi |= PORT_QUEUE_SPLIT_H;
- ksz_pwrite8(dev, port, REG_PORT_CTRL_0, lo);
- ksz_pwrite8(dev, port, P_DROP_TAG_CTRL, hi);
-
- /* Default is port based for egress rate limit. */
- if (queue != PORT_QUEUE_SPLIT_1)
- ksz_cfg(dev, REG_SW_CTRL_19, SW_OUT_RATE_LIMIT_QUEUE_BASED,
- true);
+
+ if (queues == 4)
+ data_4q = mask_4q;
+ else if (queues == 2)
+ data_2q = mask_2q;
+
+ ret = ksz_prmw8(dev, port, reg_4q, mask_4q, data_4q);
+ if (ret)
+ return ret;
+
+ return ksz_prmw8(dev, port, reg_2q, mask_2q, data_2q);
}
void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt)
@@ -1513,6 +1532,7 @@ void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
{
struct dsa_switch *ds = dev->ds;
const u32 *masks;
+ int queues;
u8 member;
masks = dev->info->masks;
@@ -1520,8 +1540,15 @@ void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
/* enable broadcast storm limit */
ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
- if (!ksz_is_ksz88x3(dev))
- ksz8795_set_prio_queue(dev, port, 4);
+ /* For KSZ88x3 enable only one queue by default, otherwise we won't
+ * be able to get rid of PCP prios on Port 2.
+ */
+ if (ksz_is_ksz88x3(dev))
+ queues = 1;
+ else
+ queues = dev->info->num_tx_queues;
+
+ ksz8_port_queue_split(dev, port, queues);
/* disable DiffServ priority */
ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_ENABLE, false);
diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h b/drivers/net/dsa/microchip/ksz8795_reg.h
index 0d13a6e29b0e6..69566a5d9cda1 100644
--- a/drivers/net/dsa/microchip/ksz8795_reg.h
+++ b/drivers/net/dsa/microchip/ksz8795_reg.h
@@ -124,7 +124,8 @@
#define PORT_BASED_PRIO_3 3
#define PORT_INSERT_TAG BIT(2)
#define PORT_REMOVE_TAG BIT(1)
-#define PORT_QUEUE_SPLIT_L BIT(0)
+#define KSZ8795_PORT_2QUEUE_SPLIT_EN BIT(0)
+#define KSZ8873_PORT_4QUEUE_SPLIT_EN BIT(0)
#define REG_PORT_1_CTRL_1 0x11
#define REG_PORT_2_CTRL_1 0x21
@@ -143,6 +144,7 @@
#define REG_PORT_4_CTRL_2 0x42
#define REG_PORT_5_CTRL_2 0x52
+#define KSZ8873_PORT_2QUEUE_SPLIT_EN BIT(7)
#define PORT_INGRESS_FILTER BIT(6)
#define PORT_DISCARD_NON_VID BIT(5)
#define PORT_FORCE_FLOW_CTRL BIT(4)
@@ -463,10 +465,7 @@
#define REG_PORT_4_CTRL_13 0xE1
#define REG_PORT_5_CTRL_13 0xF1
-#define PORT_QUEUE_SPLIT_H BIT(1)
-#define PORT_QUEUE_SPLIT_1 0
-#define PORT_QUEUE_SPLIT_2 1
-#define PORT_QUEUE_SPLIT_4 2
+#define KSZ8795_PORT_4QUEUE_SPLIT_EN BIT(1)
#define PORT_DROP_TAG BIT(0)
#define REG_PORT_1_CTRL_14 0xB2
--
2.39.2
KSZ88X3 switches have different behavior on different ports:
- It seems to be not possible to disable VLAN PCP classification on port
2. It means, as soon as mutliqueue support is enabled, frames with
VLAN tag will get PCP prios. This behavior do not affect Port 1 -
it is possible to disable PCP prios.
- DSCP classification is not working on Port 2.
Since there are still usable configuration combinations, I added some
quirks to make sure user will get appropriate error message if not
possible configuration is chosen.
Signed-off-by: Oleksij Rempel <[email protected]>
Acked-by: Arun Ramadoss <[email protected]>
---
changes v5:
- add KSZ_PORT_1/2 defines, to make code readable
- streamline function and variable naming with port names used in the
documentation
changes v3:
- move ksz8_port2_supported_apptrust[] to this patch, where it is
actually used.
---
drivers/net/dsa/microchip/ksz8.h | 1 +
drivers/net/dsa/microchip/ksz8795.c | 15 ++
drivers/net/dsa/microchip/ksz_common.h | 3 +
drivers/net/dsa/microchip/ksz_dcb.c | 226 ++++++++++++++++++++++++-
4 files changed, 242 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h
index 571c26ce71e47..6332751f96bdb 100644
--- a/drivers/net/dsa/microchip/ksz8.h
+++ b/drivers/net/dsa/microchip/ksz8.h
@@ -58,5 +58,6 @@ void ksz8_phylink_mac_link_up(struct ksz_device *dev, int port,
unsigned int mode, phy_interface_t interface,
struct phy_device *phydev, int speed, int duplex,
bool tx_pause, bool rx_pause);
+int ksz8_all_queues_split(struct ksz_device *dev, int queues);
#endif
diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c
index 33d6f990e1870..8511eb052dd2e 100644
--- a/drivers/net/dsa/microchip/ksz8795.c
+++ b/drivers/net/dsa/microchip/ksz8795.c
@@ -179,6 +179,21 @@ static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues)
return ksz_prmw8(dev, port, reg_2q, mask_2q, data_2q);
}
+int ksz8_all_queues_split(struct ksz_device *dev, int queues)
+{
+ struct dsa_switch *ds = dev->ds;
+ const struct dsa_port *dp;
+
+ dsa_switch_for_each_port(dp, ds) {
+ int ret = ksz8_port_queue_split(dev, dp->index, queues);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt)
{
const u32 *masks;
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index cbbaafca79379..0089d01a04b99 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -19,6 +19,9 @@
#include "ksz_ptp.h"
#define KSZ_MAX_NUM_PORTS 8
+/* all KSZ switches count ports from 1 */
+#define KSZ_PORT_1 0
+#define KSZ_PORT_2 1
struct ksz_device;
struct ksz_port;
diff --git a/drivers/net/dsa/microchip/ksz_dcb.c b/drivers/net/dsa/microchip/ksz_dcb.c
index d2122f844c80b..05d782908841f 100644
--- a/drivers/net/dsa/microchip/ksz_dcb.c
+++ b/drivers/net/dsa/microchip/ksz_dcb.c
@@ -83,6 +83,10 @@ static const u8 ksz_supported_apptrust[] = {
IEEE_8021QAZ_APP_SEL_DSCP,
};
+static const u8 ksz8_port2_supported_apptrust[] = {
+ DCB_APP_SEL_PCP,
+};
+
static const char * const ksz_supported_apptrust_variants[] = {
"empty", "dscp", "pcp", "dscp pcp"
};
@@ -182,6 +186,49 @@ int ksz_port_get_default_prio(struct dsa_switch *ds, int port)
return (data & mask) >> shift;
}
+/**
+ * ksz88x3_port_set_default_prio_quirks - Quirks for default priority
+ * @dev: Pointer to the KSZ switch device structure
+ * @port: Port number for which to set the default priority
+ * @prio: Priority value to set
+ *
+ * This function implements quirks for setting the default priority on KSZ88x3
+ * devices. On Port 2, no other priority providers are working
+ * except of PCP. So, configuring default priority on Port 2 is not possible.
+ * On Port 1, it is not possible to configure port priority if PCP
+ * apptrust on Port 2 is disabled. Since we disable multiple queues on the
+ * switch to disable PCP on Port 2, we need to ensure that the default priority
+ * configuration on Port 1 is in agreement with the configuration on Port 2.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+static int ksz88x3_port_set_default_prio_quirks(struct ksz_device *dev, int port,
+ u8 prio)
+{
+ if (!prio)
+ return 0;
+
+ if (port == KSZ_PORT_2) {
+ dev_err(dev->dev, "Port priority configuration is not working on Port 2\n");
+ return -EINVAL;
+ } else if (port == KSZ_PORT_1) {
+ u8 port2_data;
+ int ret;
+
+ ret = ksz_pread8(dev, KSZ_PORT_2, KSZ8_REG_PORT_1_CTRL_0,
+ &port2_data);
+ if (ret)
+ return ret;
+
+ if (!(port2_data & KSZ8_PORT_802_1P_ENABLE)) {
+ dev_err(dev->dev, "Not possible to configur port priority on Port 1 if PCP apptrust on Port 2 is disabled\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
/**
* ksz_port_set_default_prio - Sets the default priority for a port on a KSZ
* switch
@@ -197,12 +244,18 @@ int ksz_port_get_default_prio(struct dsa_switch *ds, int port)
int ksz_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
{
struct ksz_device *dev = ds->priv;
- int reg, shift;
+ int reg, shift, ret;
u8 mask;
if (prio >= dev->info->num_tx_queues)
return -EINVAL;
+ if (ksz_is_ksz88x3(dev)) {
+ ret = ksz88x3_port_set_default_prio_quirks(dev, port, prio);
+ if (ret)
+ return ret;
+ }
+
ksz_get_default_port_prio_reg(dev, ®, &mask, &shift);
return ksz_prmw8(dev, port, reg, mask, (prio << shift) & mask);
@@ -409,6 +462,155 @@ static int ksz_port_set_apptrust_validate(struct ksz_device *dev, int port,
return -EINVAL;
}
+/**
+ * ksz88x3_port1_apptrust_quirk - Quirk for apptrust configuration on Port 1
+ * of KSZ88x3 devices
+ * @dev: Pointer to the KSZ switch device structure
+ * @port: Port number for which to set the apptrust selectors
+ * @reg: Register address for the apptrust configuration
+ * @port1_data: Data to set for the apptrust configuration
+ *
+ * This function implements a quirk for apptrust configuration on Port 1 of
+ * KSZ88x3 devices. It ensures that apptrust configuration on Port 1 is not
+ * possible if PCP apptrust on Port 2 is disabled. This is because the Port 2
+ * seems to be permanently hardwired to PCP classification, so we need to
+ * do Port 1 configuration always in agreement with Port 2 configuration.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+static int ksz88x3_port1_apptrust_quirk(struct ksz_device *dev, int port,
+ int reg, u8 port1_data)
+{
+ u8 port2_data;
+ int ret;
+
+ /* If no apptrust is requested for Port 1, no need to care about Port 2
+ * configuration.
+ */
+ if (!(port1_data & (KSZ8_PORT_802_1P_ENABLE | KSZ8_PORT_DIFFSERV_ENABLE)))
+ return 0;
+
+ /* We got request to enable any apptrust on Port 1. To make it possible,
+ * we need to enable multiple queues on the switch. If we enable
+ * multiqueue support, PCP classification on Port 2 will be
+ * automatically activated by HW.
+ */
+ ret = ksz_pread8(dev, KSZ_PORT_2, reg, &port2_data);
+ if (ret)
+ return ret;
+
+ /* If KSZ8_PORT_802_1P_ENABLE bit is set on Port 2, the driver showed
+ * the interest in PCP classification on Port 2. In this case,
+ * multiqueue support is enabled and we can enable any apptrust on
+ * Port 1.
+ * If KSZ8_PORT_802_1P_ENABLE bit is not set on Port 2, the PCP
+ * classification on Port 2 is still active, but the driver disabled
+ * multiqueue support and made frame prioritization inactive for
+ * all ports. In this case, we can't enable any apptrust on Port 1.
+ */
+ if (!(port2_data & KSZ8_PORT_802_1P_ENABLE)) {
+ dev_err(dev->dev, "Not possible to enable any apptrust on Port 1 if PCP apptrust on Port 2 is disabled\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * ksz88x3_port2_apptrust_quirk - Quirk for apptrust configuration on Port 2
+ * of KSZ88x3 devices
+ * @dev: Pointer to the KSZ switch device structure
+ * @port: Port number for which to set the apptrust selectors
+ * @reg: Register address for the apptrust configuration
+ * @port2_data: Data to set for the apptrust configuration
+ *
+ * This function implements a quirk for apptrust configuration on Port 2 of
+ * KSZ88x3 devices. It ensures that DSCP apptrust is not working on Port 2 and
+ * that it is not possible to disable PCP on Port 2. The only way to disable PCP
+ * on Port 2 is to disable multiple queues on the switch.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+static int ksz88x3_port2_apptrust_quirk(struct ksz_device *dev, int port,
+ int reg, u8 port2_data)
+{
+ struct dsa_switch *ds = dev->ds;
+ u8 port1_data;
+ int ret;
+
+ /* First validate Port 2 configuration. DiffServ/DSCP is not working
+ * on this port.
+ */
+ if (port2_data & KSZ8_PORT_DIFFSERV_ENABLE) {
+ dev_err(dev->dev, "DSCP apptrust is not working on Port 2\n");
+ return -EINVAL;
+ }
+
+ /* If PCP support is requested, we need to enable all queues on the
+ * switch to make PCP priority working on Port 2.
+ */
+ if (port2_data & KSZ8_PORT_802_1P_ENABLE)
+ return ksz8_all_queues_split(dev, dev->info->num_tx_queues);
+
+ /* We got request to disable PCP priority on Port 2.
+ * Now, we need to compare Port 2 configuration with Port 1
+ * configuration.
+ */
+ ret = ksz_pread8(dev, KSZ_PORT_1, reg, &port1_data);
+ if (ret)
+ return ret;
+
+ /* If Port 1 has any apptrust enabled, we can't disable multiple queues
+ * on the switch, so we can't disable PCP on Port 2.
+ */
+ if (port1_data & (KSZ8_PORT_802_1P_ENABLE | KSZ8_PORT_DIFFSERV_ENABLE)) {
+ dev_err(dev->dev, "Not possible to disable PCP on Port 2 if any apptrust is enabled on Port 1\n");
+ return -EINVAL;
+ }
+
+ /* Now we need to ensure that default priority on Port 1 is set to 0
+ * otherwise we can't disable multiqueue support on the switch.
+ */
+ ret = ksz_port_get_default_prio(ds, KSZ_PORT_1);
+ if (ret < 0) {
+ return ret;
+ } else if (ret) {
+ dev_err(dev->dev, "Not possible to disable PCP on Port 2 if non zero default priority is set on Port 1\n");
+ return -EINVAL;
+ }
+
+ /* Port 1 has no apptrust or default priority set and we got request to
+ * disable PCP on Port 2. We can disable multiqueue support to disable
+ * PCP on Port 2.
+ */
+ return ksz8_all_queues_split(dev, 1);
+}
+
+/**
+ * ksz88x3_port_apptrust_quirk - Quirk for apptrust configuration on KSZ88x3
+ * devices
+ * @dev: Pointer to the KSZ switch device structure
+ * @port: Port number for which to set the apptrust selectors
+ * @reg: Register address for the apptrust configuration
+ * @data: Data to set for the apptrust configuration
+ *
+ * This function implements a quirk for apptrust configuration on KSZ88x3
+ * devices. It ensures that apptrust configuration on Port 1 and
+ * Port 2 is done in agreement with each other.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+static int ksz88x3_port_apptrust_quirk(struct ksz_device *dev, int port,
+ int reg, u8 data)
+{
+ if (port == KSZ_PORT_1)
+ return ksz88x3_port1_apptrust_quirk(dev, port, reg, data);
+ else if (port == KSZ_PORT_2)
+ return ksz88x3_port2_apptrust_quirk(dev, port, reg, data);
+
+ return 0;
+}
+
/**
* ksz_port_set_apptrust - Sets the apptrust selectors for a port on a KSZ
* switch
@@ -449,6 +651,12 @@ int ksz_port_set_apptrust(struct dsa_switch *ds, int port,
}
}
+ if (ksz_is_ksz88x3(dev)) {
+ ret = ksz88x3_port_apptrust_quirk(dev, port, reg, data);
+ if (ret)
+ return ret;
+ }
+
return ksz_prmw8(dev, port, reg, mask, data);
}
@@ -503,7 +711,9 @@ int ksz_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, int *nsel)
*/
int ksz_dcb_init_port(struct ksz_device *dev, int port)
{
+ const u8 *sel;
int ret, ipv;
+ int sel_len;
if (is_ksz8(dev)) {
ipv = ieee8021q_tt_to_tc(IEEE8021Q_TT_BE,
@@ -519,8 +729,18 @@ int ksz_dcb_init_port(struct ksz_device *dev, int port)
if (ret)
return ret;
- return ksz_port_set_apptrust(dev->ds, port, ksz_supported_apptrust,
- ARRAY_SIZE(ksz_supported_apptrust));
+ if (ksz_is_ksz88x3(dev) && port == KSZ_PORT_2) {
+ /* KSZ88x3 devices do not support DSCP classification on
+ * "Port 2.
+ */
+ sel = ksz8_port2_supported_apptrust;
+ sel_len = ARRAY_SIZE(ksz8_port2_supported_apptrust);
+ } else {
+ sel = ksz_supported_apptrust;
+ sel_len = ARRAY_SIZE(ksz_supported_apptrust);
+ }
+
+ return ksz_port_set_apptrust(dev->ds, port, sel, sel_len);
}
/**
--
2.39.2
Add DCB support to configure app trust sources and default port priority.
Following commands can be used for testing:
dcb apptrust set dev lan1 order pcp dscp
dcb app replace dev lan1 default-prio 3
Since it is not possible to configure DSCP-Prio mapping per port, this
patch provide only ability to read switch global dscp-prio mapping and
way to enable/disable app trust for DSCP.
Signed-off-by: Oleksij Rempel <[email protected]>
Acked-by: Arun Ramadoss <[email protected]>
---
changes v5:
- reorder port_set_apptrust and port_get_apptrust
- s/defult/default
- group all the get register function in top of file
- s/invalid/err_sel_not_vaild
- s/apptrus_/apptrust_
changes v3:
- move ksz8_port2_supported_apptrust[] to a followup patch
---
drivers/net/dsa/microchip/Kconfig | 2 +
drivers/net/dsa/microchip/Makefile | 2 +-
drivers/net/dsa/microchip/ksz_common.c | 12 +-
drivers/net/dsa/microchip/ksz_common.h | 5 +
drivers/net/dsa/microchip/ksz_dcb.c | 544 +++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_dcb.h | 21 +
6 files changed, 584 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/dsa/microchip/ksz_dcb.c
create mode 100644 drivers/net/dsa/microchip/ksz_dcb.h
diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig
index 394ca8678d2ba..c1b906c05a025 100644
--- a/drivers/net/dsa/microchip/Kconfig
+++ b/drivers/net/dsa/microchip/Kconfig
@@ -4,6 +4,8 @@ menuconfig NET_DSA_MICROCHIP_KSZ_COMMON
depends on NET_DSA
select NET_DSA_TAG_KSZ
select NET_DSA_TAG_NONE
+ select NET_IEEE8021Q_HELPERS
+ select DCB
help
This driver adds support for Microchip KSZ9477 series switch and
KSZ8795/KSZ88x3 switch chips.
diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile
index 49459a50dbc81..1cfba1ec9355a 100644
--- a/drivers/net/dsa/microchip/Makefile
+++ b/drivers/net/dsa/microchip/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) += ksz_switch.o
-ksz_switch-objs := ksz_common.o
+ksz_switch-objs := ksz_common.o ksz_dcb.o
ksz_switch-objs += ksz9477.o ksz9477_acl.o ksz9477_tc_flower.o
ksz_switch-objs += ksz8795.o
ksz_switch-objs += lan937x_main.o
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index b2d1c61400c51..78a9622adecde 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -28,6 +28,7 @@
#include <net/switchdev.h>
#include "ksz_common.h"
+#include "ksz_dcb.h"
#include "ksz_ptp.h"
#include "ksz8.h"
#include "ksz9477.h"
@@ -2364,6 +2365,10 @@ static int ksz_setup(struct dsa_switch *ds)
goto out_ptp_clock_unregister;
}
+ ret = ksz_dcb_init(dev);
+ if (ret)
+ goto out_ptp_clock_unregister;
+
/* start switch */
regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
SW_START, SW_START);
@@ -2691,7 +2696,7 @@ static int ksz_port_setup(struct dsa_switch *ds, int port)
* there is no need to do anything.
*/
- return 0;
+ return ksz_dcb_init_port(dev, port);
}
void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
@@ -3943,6 +3948,11 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.port_setup_tc = ksz_setup_tc,
.get_mac_eee = ksz_get_mac_eee,
.set_mac_eee = ksz_set_mac_eee,
+ .port_get_default_prio = ksz_port_get_default_prio,
+ .port_set_default_prio = ksz_port_set_default_prio,
+ .port_get_dscp_prio = ksz_port_get_dscp_prio,
+ .port_get_apptrust = ksz_port_get_apptrust,
+ .port_set_apptrust = ksz_port_set_apptrust,
};
struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 900e9ac06d013..cbbaafca79379 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -621,6 +621,11 @@ static inline bool ksz_is_ksz88x3(struct ksz_device *dev)
return dev->chip_id == KSZ8830_CHIP_ID;
}
+static inline bool is_ksz8(struct ksz_device *dev)
+{
+ return ksz_is_ksz87xx(dev) || ksz_is_ksz88x3(dev);
+}
+
static inline int is_lan937x(struct ksz_device *dev)
{
return dev->chip_id == LAN9370_CHIP_ID ||
diff --git a/drivers/net/dsa/microchip/ksz_dcb.c b/drivers/net/dsa/microchip/ksz_dcb.c
new file mode 100644
index 0000000000000..d2122f844c80b
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz_dcb.c
@@ -0,0 +1,544 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2024 Pengutronix, Oleksij Rempel <[email protected]>
+
+#include <linux/dsa/ksz_common.h>
+#include <net/dsa.h>
+#include <net/dscp.h>
+#include <net/ieee8021q.h>
+
+#include "ksz_common.h"
+#include "ksz_dcb.h"
+#include "ksz8.h"
+
+#define KSZ8_REG_PORT_1_CTRL_0 0x10
+#define KSZ8_PORT_DIFFSERV_ENABLE BIT(6)
+#define KSZ8_PORT_802_1P_ENABLE BIT(5)
+#define KSZ8_PORT_BASED_PRIO_M GENMASK(4, 3)
+
+#define KSZ88X3_REG_TOS_DSCP_CTRL 0x60
+#define KSZ8765_REG_TOS_DSCP_CTRL 0x90
+
+#define KSZ9477_REG_SW_MAC_TOS_CTRL 0x033e
+#define KSZ9477_SW_TOS_DSCP_REMAP BIT(0)
+#define KSZ9477_SW_TOS_DSCP_DEFAULT_PRIO_M GENMASK(5, 3)
+
+#define KSZ9477_REG_DIFFSERV_PRIO_MAP 0x0340
+
+#define KSZ9477_REG_PORT_MRI_PRIO_CTRL 0x0801
+#define KSZ9477_PORT_HIGHEST_PRIO BIT(7)
+#define KSZ9477_PORT_OR_PRIO BIT(6)
+#define KSZ9477_PORT_MAC_PRIO_ENABLE BIT(4)
+#define KSZ9477_PORT_VLAN_PRIO_ENABLE BIT(3)
+#define KSZ9477_PORT_802_1P_PRIO_ENABLE BIT(2)
+#define KSZ9477_PORT_DIFFSERV_PRIO_ENABLE BIT(1)
+#define KSZ9477_PORT_ACL_PRIO_ENABLE BIT(0)
+
+#define KSZ9477_REG_PORT_MRI_MAC_CTRL 0x0802
+#define KSZ9477_PORT_BASED_PRIO_M GENMASK(2, 0)
+
+struct ksz_apptrust_map {
+ u8 apptrust;
+ u8 bit;
+};
+
+static const struct ksz_apptrust_map ksz8_apptrust_map_to_bit[] = {
+ { DCB_APP_SEL_PCP, KSZ8_PORT_802_1P_ENABLE },
+ { IEEE_8021QAZ_APP_SEL_DSCP, KSZ8_PORT_DIFFSERV_ENABLE },
+};
+
+static const struct ksz_apptrust_map ksz9477_apptrust_map_to_bit[] = {
+ { DCB_APP_SEL_PCP, KSZ9477_PORT_802_1P_PRIO_ENABLE },
+ { IEEE_8021QAZ_APP_SEL_DSCP, KSZ9477_PORT_DIFFSERV_PRIO_ENABLE },
+};
+
+/**
+ * ksz_supported_apptrust[] - Supported apptrust selectors and Priority Order
+ * of Internal Priority Value (IPV) sources.
+ *
+ * This array defines the apptrust selectors supported by the hardware, where
+ * the index within the array indicates the priority of the selector - lower
+ * indices correspond to higher priority. This fixed priority scheme is due to
+ * the hardware's design, which does not support configurable priority among
+ * different priority sources.
+ *
+ * The priority sources, including Tail Tag, ACL, VLAN PCP and DSCP are ordered
+ * by the hardware's fixed logic, as detailed below. The order reflects a
+ * non-configurable precedence where certain types of priority information
+ * override others:
+ *
+ * 1. Tail Tag - Highest priority, overrides ACL, VLAN PCP, and DSCP priorities.
+ * 2. ACL - Overrides VLAN PCP and DSCP priorities.
+ * 3. VLAN PCP - Overrides DSCP priority.
+ * 4. DSCP - Lowest priority, does not override any other priority source.
+ *
+ * In this context, the array's lower index (higher priority) for
+ * 'DCB_APP_SEL_PCP' suggests its relative priority over
+ * 'IEEE_8021QAZ_APP_SEL_DSCP' within the system's fixed priority scheme.
+ *
+ * DCB_APP_SEL_PCP - Priority Code Point selector
+ * IEEE_8021QAZ_APP_SEL_DSCP - Differentiated Services Code Point selector
+ */
+static const u8 ksz_supported_apptrust[] = {
+ DCB_APP_SEL_PCP,
+ IEEE_8021QAZ_APP_SEL_DSCP,
+};
+
+static const char * const ksz_supported_apptrust_variants[] = {
+ "empty", "dscp", "pcp", "dscp pcp"
+};
+
+static void ksz_get_default_port_prio_reg(struct ksz_device *dev, int *reg,
+ u8 *mask, int *shift)
+{
+ if (is_ksz8(dev)) {
+ *reg = KSZ8_REG_PORT_1_CTRL_0;
+ *mask = KSZ8_PORT_BASED_PRIO_M;
+ *shift = __bf_shf(KSZ8_PORT_BASED_PRIO_M);
+ } else {
+ *reg = KSZ9477_REG_PORT_MRI_MAC_CTRL;
+ *mask = KSZ9477_PORT_BASED_PRIO_M;
+ *shift = __bf_shf(KSZ9477_PORT_BASED_PRIO_M);
+ }
+}
+
+/**
+ * ksz_get_dscp_prio_reg - Retrieves the DSCP-to-priority-mapping register
+ * @dev: Pointer to the KSZ switch device structure
+ * @reg: Pointer to the register address to be set
+ * @per_reg: Pointer to the number of DSCP values per register
+ * @mask: Pointer to the mask to be set
+ *
+ * This function retrieves the DSCP to priority mapping register, the number of
+ * DSCP values per register, and the mask to be set.
+ */
+static void ksz_get_dscp_prio_reg(struct ksz_device *dev, int *reg,
+ int *per_reg, u8 *mask)
+{
+ if (ksz_is_ksz87xx(dev)) {
+ *reg = KSZ8765_REG_TOS_DSCP_CTRL;
+ *per_reg = 4;
+ *mask = GENMASK(1, 0);
+ } else if (ksz_is_ksz88x3(dev)) {
+ *reg = KSZ88X3_REG_TOS_DSCP_CTRL;
+ *per_reg = 4;
+ *mask = GENMASK(1, 0);
+ } else {
+ *reg = KSZ9477_REG_DIFFSERV_PRIO_MAP;
+ *per_reg = 2;
+ *mask = GENMASK(2, 0);
+ }
+}
+
+/**
+ * ksz_get_apptrust_map_and_reg - Retrieves the apptrust map and register
+ * @dev: Pointer to the KSZ switch device structure
+ * @map: Pointer to the apptrust map to be set
+ * @reg: Pointer to the register address to be set
+ * @mask: Pointer to the mask to be set
+ *
+ * This function retrieves the apptrust map and register address for the
+ * apptrust configuration.
+ */
+static void ksz_get_apptrust_map_and_reg(struct ksz_device *dev,
+ const struct ksz_apptrust_map **map,
+ int *reg, u8 *mask)
+{
+ if (is_ksz8(dev)) {
+ *map = ksz8_apptrust_map_to_bit;
+ *reg = KSZ8_REG_PORT_1_CTRL_0;
+ *mask = KSZ8_PORT_DIFFSERV_ENABLE | KSZ8_PORT_802_1P_ENABLE;
+ } else {
+ *map = ksz9477_apptrust_map_to_bit;
+ *reg = KSZ9477_REG_PORT_MRI_PRIO_CTRL;
+ *mask = KSZ9477_PORT_802_1P_PRIO_ENABLE |
+ KSZ9477_PORT_DIFFSERV_PRIO_ENABLE;
+ }
+}
+
+/**
+ * ksz_port_get_default_prio - Retrieves the default priority for a port on a
+ * KSZ switch
+ * @ds: Pointer to the DSA switch structure
+ * @port: Port number from which to get the default priority
+ *
+ * This function fetches the default priority for the specified port on a KSZ
+ * switch.
+ *
+ * Return: The default priority of the port on success, or a negative error
+ * code on failure.
+ */
+int ksz_port_get_default_prio(struct dsa_switch *ds, int port)
+{
+ struct ksz_device *dev = ds->priv;
+ int ret, reg, shift;
+ u8 data, mask;
+
+ ksz_get_default_port_prio_reg(dev, ®, &mask, &shift);
+
+ ret = ksz_pread8(dev, port, reg, &data);
+ if (ret)
+ return ret;
+
+ return (data & mask) >> shift;
+}
+
+/**
+ * ksz_port_set_default_prio - Sets the default priority for a port on a KSZ
+ * switch
+ * @ds: Pointer to the DSA switch structure
+ * @port: Port number for which to set the default priority
+ * @prio: Priority value to set
+ *
+ * This function sets the default priority for the specified port on a KSZ
+ * switch.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int ksz_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
+{
+ struct ksz_device *dev = ds->priv;
+ int reg, shift;
+ u8 mask;
+
+ if (prio >= dev->info->num_tx_queues)
+ return -EINVAL;
+
+ ksz_get_default_port_prio_reg(dev, ®, &mask, &shift);
+
+ return ksz_prmw8(dev, port, reg, mask, (prio << shift) & mask);
+}
+
+/**
+ * ksz_port_get_dscp_prio - Retrieves the priority for a DSCP value on a KSZ
+ * switch
+ * @ds: Pointer to the DSA switch structure
+ * @port: Port number for which to get the priority
+ * @dscp: DSCP value for which to get the priority
+ *
+ * This function fetches the priority value from switch global DSCP-to-priorty
+ * mapping table for the specified DSCP value.
+ *
+ * Return: The priority value for the DSCP on success, or a negative error
+ * code on failure.
+ */
+int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp)
+{
+ struct ksz_device *dev = ds->priv;
+ int reg, per_reg, ret, shift;
+ u8 data, mask;
+
+ ksz_get_dscp_prio_reg(dev, ®, &per_reg, &mask);
+
+ /* If DSCP remapping is disabled, DSCP bits 3-5 are used as Internal
+ * Priority Value (IPV)
+ */
+ if (!is_ksz8(dev)) {
+ ret = ksz_read8(dev, KSZ9477_REG_SW_MAC_TOS_CTRL, &data);
+ if (ret)
+ return ret;
+
+ /* If DSCP remapping is disabled, DSCP bits 3-5 are used as
+ * Internal Priority Value (IPV)
+ */
+ if (!(data & KSZ9477_SW_TOS_DSCP_REMAP))
+ return FIELD_GET(KSZ9477_SW_TOS_DSCP_DEFAULT_PRIO_M,
+ dscp);
+ }
+
+ /* In case DSCP remapping is enabled, we need to write the DSCP to
+ * priority mapping table.
+ */
+ reg += dscp / per_reg;
+ ret = ksz_read8(dev, reg, &data);
+ if (ret)
+ return ret;
+
+ shift = (dscp % per_reg) * (8 / per_reg);
+
+ return (data >> shift) & mask;
+}
+
+/**
+ * ksz_init_global_dscp_map - Initializes the global DSCP-to-priority mapping
+ * @dev: Pointer to the KSZ switch device structure
+ *
+ * This function initializes the global DSCP-to-priority mapping table for the
+ * switch.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+static int ksz_init_global_dscp_map(struct ksz_device *dev)
+{
+ int reg, per_reg, ret, dscp;
+ u8 data = 0;
+ u8 mask;
+
+ /* On KSZ9xxx variants, DSCP remapping is disabled by default.
+ * Enable to have, predictable and reproducible behavior across
+ * different devices.
+ */
+ if (!is_ksz8(dev)) {
+ ret = ksz_rmw8(dev, KSZ9477_REG_SW_MAC_TOS_CTRL,
+ KSZ9477_SW_TOS_DSCP_REMAP,
+ KSZ9477_SW_TOS_DSCP_REMAP);
+ if (ret)
+ return ret;
+ }
+
+ ksz_get_dscp_prio_reg(dev, ®, &per_reg, &mask);
+
+ for (dscp = 0; dscp < DSCP_MAX; dscp++) {
+ int ipv, shift, tt;
+
+ /* Map DSCP to Traffic Type, which is corresponding to the
+ * Internal Priority Value (IPV) in the switch.
+ */
+ if (!is_ksz8(dev)) {
+ ipv = ietf_dscp_to_ieee8021q_tt(dscp);
+ } else {
+ /* On KSZ8xxx variants we do not have IPV to queue
+ * remapping table. We need to convert DSCP to Traffic
+ * Type and then to queue.
+ */
+ tt = ietf_dscp_to_ieee8021q_tt(dscp);
+ if (tt < 0)
+ return tt;
+
+ ipv = ieee8021q_tt_to_tc(tt, dev->info->num_tx_queues);
+ }
+
+ if (ipv < 0)
+ return ipv;
+
+ shift = (dscp % per_reg) * (8 / per_reg);
+ data |= (ipv & mask) << shift;
+
+ if (dscp % per_reg == per_reg - 1) {
+ ret = ksz_write8(dev, reg + (dscp / per_reg), data);
+ if (ret)
+ return ret;
+
+ data = 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ksz_apptrust_error - Prints an error message for an invalid apptrust selector
+ * @dev: Pointer to the KSZ switch device structure
+ *
+ * This function prints an error message when an invalid apptrust selector is
+ * provided.
+ */
+static void ksz_apptrust_error(struct ksz_device *dev)
+{
+ char supported_apptrust_variants[64];
+ int i;
+
+ supported_apptrust_variants[0] = '\0';
+ for (i = 0; i < ARRAY_SIZE(ksz_supported_apptrust_variants); i++) {
+ if (i > 0)
+ strlcat(supported_apptrust_variants, ", ",
+ sizeof(supported_apptrust_variants));
+ strlcat(supported_apptrust_variants,
+ ksz_supported_apptrust_variants[i],
+ sizeof(supported_apptrust_variants));
+ }
+
+ dev_err(dev->dev, "Invalid apptrust selector or priority order. Supported: %s\n",
+ supported_apptrust_variants);
+}
+
+/**
+ * ksz_port_set_apptrust_validate - Validates the apptrust selectors
+ * @dev: Pointer to the KSZ switch device structure
+ * @port: Port number for which to set the apptrust selectors
+ * @sel: Array of apptrust selectors to validate
+ * @nsel: Number of apptrust selectors in the array
+ *
+ * This function validates the apptrust selectors provided and ensures that
+ * they are in the correct order.
+ *
+ * This family of switches supports two apptrust selectors: DCB_APP_SEL_PCP and
+ * IEEE_8021QAZ_APP_SEL_DSCP. The priority order of the selectors is fixed and
+ * cannot be changed. The order is as follows:
+ * 1. DCB_APP_SEL_PCP - Priority Code Point selector (highest priority)
+ * 2. IEEE_8021QAZ_APP_SEL_DSCP - Differentiated Services Code Point selector
+ * (lowest priority)
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+static int ksz_port_set_apptrust_validate(struct ksz_device *dev, int port,
+ const u8 *sel, int nsel)
+{
+ int i, j, found;
+ int j_prev = 0;
+
+ /* Iterate through the requested selectors */
+ for (i = 0; i < nsel; i++) {
+ found = 0;
+
+ /* Check if the current selector is supported by the hardware */
+ for (j = 0; j < sizeof(ksz_supported_apptrust); j++) {
+ if (sel[i] != ksz_supported_apptrust[j])
+ continue;
+
+ found = 1;
+
+ /* Ensure that no higher priority selector (lower index)
+ * precedes a lower priority one
+ */
+ if (i > 0 && j <= j_prev)
+ goto err_sel_not_vaild;
+
+ j_prev = j;
+ break;
+ }
+
+ if (!found)
+ goto err_sel_not_vaild;
+ }
+
+ return 0;
+
+err_sel_not_vaild:
+ ksz_apptrust_error(dev);
+
+ return -EINVAL;
+}
+
+/**
+ * ksz_port_set_apptrust - Sets the apptrust selectors for a port on a KSZ
+ * switch
+ * @ds: Pointer to the DSA switch structure
+ * @port: Port number for which to set the apptrust selectors
+ * @sel: Array of apptrust selectors to set
+ * @nsel: Number of apptrust selectors in the array
+ *
+ * This function sets the apptrust selectors for the specified port on a KSZ
+ * switch.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+int ksz_port_set_apptrust(struct dsa_switch *ds, int port,
+ const u8 *sel, int nsel)
+{
+ const struct ksz_apptrust_map *map;
+ struct ksz_device *dev = ds->priv;
+ int reg, i, ret;
+ u8 data = 0;
+ u8 mask;
+
+ ret = ksz_port_set_apptrust_validate(dev, port, sel, nsel);
+ if (ret)
+ return ret;
+
+ ksz_get_apptrust_map_and_reg(dev, &map, ®, &mask);
+
+ for (i = 0; i < nsel; i++) {
+ int j;
+
+ for (j = 0; j < ARRAY_SIZE(ksz_supported_apptrust); j++) {
+ if (sel[i] != ksz_supported_apptrust[j])
+ continue;
+
+ data |= map[j].bit;
+ break;
+ }
+ }
+
+ return ksz_prmw8(dev, port, reg, mask, data);
+}
+
+/**
+ * ksz_port_get_apptrust - Retrieves the apptrust selectors for a port on a KSZ
+ * switch
+ * @ds: Pointer to the DSA switch structure
+ * @port: Port number for which to get the apptrust selectors
+ * @sel: Array to store the apptrust selectors
+ * @nsel: Number of apptrust selectors in the array
+ *
+ * This function fetches the apptrust selectors for the specified port on a KSZ
+ * switch.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+int ksz_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, int *nsel)
+{
+ const struct ksz_apptrust_map *map;
+ struct ksz_device *dev = ds->priv;
+ int reg, i, ret;
+ u8 data;
+ u8 mask;
+
+ ksz_get_apptrust_map_and_reg(dev, &map, ®, &mask);
+
+ ret = ksz_pread8(dev, port, reg, &data);
+ if (ret)
+ return ret;
+
+ *nsel = 0;
+ for (i = 0; i < ARRAY_SIZE(ksz_supported_apptrust); i++) {
+ if (data & map[i].bit)
+ sel[(*nsel)++] = ksz_supported_apptrust[i];
+ }
+
+ return 0;
+}
+
+/**
+ * ksz_dcb_init_port - Initializes the DCB configuration for a port on a KSZ
+ * @dev: Pointer to the KSZ switch device structure
+ * @port: Port number for which to initialize the DCB configuration
+ *
+ * This function initializes the DCB configuration for the specified port on a
+ * KSZ switch. Particular DCB configuration is set for the port, including the
+ * default priority and apptrust selectors.
+ * The default priority is set to Best Effort, and the apptrust selectors are
+ * set to all supported selectors.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+int ksz_dcb_init_port(struct ksz_device *dev, int port)
+{
+ int ret, ipv;
+
+ if (is_ksz8(dev)) {
+ ipv = ieee8021q_tt_to_tc(IEEE8021Q_TT_BE,
+ dev->info->num_tx_queues);
+ if (ipv < 0)
+ return ipv;
+ } else {
+ ipv = IEEE8021Q_TT_BE;
+ }
+
+ /* Set the default priority for the port to Best Effort */
+ ret = ksz_port_set_default_prio(dev->ds, port, ipv);
+ if (ret)
+ return ret;
+
+ return ksz_port_set_apptrust(dev->ds, port, ksz_supported_apptrust,
+ ARRAY_SIZE(ksz_supported_apptrust));
+}
+
+/**
+ * ksz_dcb_init - Initializes the DCB configuration for a KSZ switch
+ * @dev: Pointer to the KSZ switch device structure
+ *
+ * This function initializes the DCB configuration for a KSZ switch. The global
+ * DSCP-to-priority mapping table is initialized.
+ *
+ * Return: 0 on success, or a negative error code on failure
+ */
+int ksz_dcb_init(struct ksz_device *dev)
+{
+ int ret;
+
+ ret = ksz_init_global_dscp_map(dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/net/dsa/microchip/ksz_dcb.h b/drivers/net/dsa/microchip/ksz_dcb.h
new file mode 100644
index 0000000000000..254c0e7bdafca
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz_dcb.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2024 Pengutronix, Oleksij Rempel <[email protected]> */
+
+#ifndef __KSZ_DCB_H
+#define __KSZ_DCB_H
+
+#include <net/dsa.h>
+
+#include "ksz_common.h"
+
+int ksz_port_get_default_prio(struct dsa_switch *ds, int port);
+int ksz_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio);
+int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp);
+int ksz_port_set_apptrust(struct dsa_switch *ds, int port,
+ const unsigned char *sel,
+ int nsel);
+int ksz_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, int *nsel);
+int ksz_dcb_init_port(struct ksz_device *dev, int port);
+int ksz_dcb_init(struct ksz_device *dev);
+
+#endif /* __KSZ_DCB_H */
--
2.39.2
Most of Microchip KSZ switches use Internal Priority Value associated
with every frame. For example, it is possible to map any VLAN PCP or
DSCP value to IPV and at the end, map IPV to a queue.
Since amount of IPVs is not equal to amount of queues, add this
information and make use of it in some functions.
Signed-off-by: Oleksij Rempel <[email protected]>
Acked-by: Arun Ramadoss <[email protected]>
---
changes v3:
- rename max_ipvs to num_ipvs
- drop comparison of num_tx_queues and num_ipvs. It makes no much sense.
---
drivers/net/dsa/microchip/ksz_common.c | 21 +++++++++++++++++++--
drivers/net/dsa/microchip/ksz_common.h | 2 +-
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 42330e8fd26e7..b2d1c61400c51 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -1194,6 +1194,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 3, /* total port count */
.port_nirqs = 3,
.num_tx_queues = 4,
+ .num_ipvs = 8,
.tc_cbs_supported = true,
.tc_ets_supported = true,
.ops = &ksz9477_dev_ops,
@@ -1223,6 +1224,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.cpu_ports = 0x10, /* can be configured as cpu port */
.port_cnt = 5, /* total cpu and user ports */
.num_tx_queues = 4,
+ .num_ipvs = 4,
.ops = &ksz8_dev_ops,
.ksz87xx_eee_link_erratum = true,
.mib_names = ksz9477_mib_names,
@@ -1262,6 +1264,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.cpu_ports = 0x10, /* can be configured as cpu port */
.port_cnt = 5, /* total cpu and user ports */
.num_tx_queues = 4,
+ .num_ipvs = 4,
.ops = &ksz8_dev_ops,
.ksz87xx_eee_link_erratum = true,
.mib_names = ksz9477_mib_names,
@@ -1287,6 +1290,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.cpu_ports = 0x10, /* can be configured as cpu port */
.port_cnt = 5, /* total cpu and user ports */
.num_tx_queues = 4,
+ .num_ipvs = 4,
.ops = &ksz8_dev_ops,
.ksz87xx_eee_link_erratum = true,
.mib_names = ksz9477_mib_names,
@@ -1312,6 +1316,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.cpu_ports = 0x4, /* can be configured as cpu port */
.port_cnt = 3,
.num_tx_queues = 4,
+ .num_ipvs = 4,
.ops = &ksz8_dev_ops,
.mib_names = ksz88xx_mib_names,
.mib_cnt = ARRAY_SIZE(ksz88xx_mib_names),
@@ -1336,6 +1341,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 7, /* total physical port count */
.port_nirqs = 4,
.num_tx_queues = 4,
+ .num_ipvs = 8,
.tc_cbs_supported = true,
.tc_ets_supported = true,
.ops = &ksz9477_dev_ops,
@@ -1370,6 +1376,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 6, /* total physical port count */
.port_nirqs = 2,
.num_tx_queues = 4,
+ .num_ipvs = 8,
.ops = &ksz9477_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1402,6 +1409,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 7, /* total physical port count */
.port_nirqs = 2,
.num_tx_queues = 4,
+ .num_ipvs = 8,
.ops = &ksz9477_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1432,6 +1440,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 3, /* total port count */
.port_nirqs = 2,
.num_tx_queues = 4,
+ .num_ipvs = 8,
.ops = &ksz9477_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1458,6 +1467,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 3, /* total port count */
.port_nirqs = 3,
.num_tx_queues = 4,
+ .num_ipvs = 8,
.tc_cbs_supported = true,
.tc_ets_supported = true,
.ops = &ksz9477_dev_ops,
@@ -1486,6 +1496,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 7, /* total port count */
.port_nirqs = 3,
.num_tx_queues = 4,
+ .num_ipvs = 8,
.tc_cbs_supported = true,
.tc_ets_supported = true,
.ops = &ksz9477_dev_ops,
@@ -1519,6 +1530,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 7, /* total physical port count */
.port_nirqs = 3,
.num_tx_queues = 4,
+ .num_ipvs = 8,
.tc_cbs_supported = true,
.tc_ets_supported = true,
.ops = &ksz9477_dev_ops,
@@ -1551,6 +1563,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 5, /* total physical port count */
.port_nirqs = 6,
.num_tx_queues = 8,
+ .num_ipvs = 8,
.tc_cbs_supported = true,
.tc_ets_supported = true,
.ops = &lan937x_dev_ops,
@@ -1578,6 +1591,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 6, /* total physical port count */
.port_nirqs = 6,
.num_tx_queues = 8,
+ .num_ipvs = 8,
.tc_cbs_supported = true,
.tc_ets_supported = true,
.ops = &lan937x_dev_ops,
@@ -1605,6 +1619,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 8, /* total physical port count */
.port_nirqs = 6,
.num_tx_queues = 8,
+ .num_ipvs = 8,
.tc_cbs_supported = true,
.tc_ets_supported = true,
.ops = &lan937x_dev_ops,
@@ -1636,6 +1651,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 5, /* total physical port count */
.port_nirqs = 6,
.num_tx_queues = 8,
+ .num_ipvs = 8,
.tc_cbs_supported = true,
.tc_ets_supported = true,
.ops = &lan937x_dev_ops,
@@ -1667,6 +1683,7 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.port_cnt = 8, /* total physical port count */
.port_nirqs = 6,
.num_tx_queues = 8,
+ .num_ipvs = 8,
.tc_cbs_supported = true,
.tc_ets_supported = true,
.ops = &lan937x_dev_ops,
@@ -3522,7 +3539,7 @@ static int ksz_tc_ets_add(struct ksz_device *dev, int port,
for (tc_prio = 0; tc_prio < ARRAY_SIZE(p->priomap); tc_prio++) {
int queue;
- if (tc_prio > KSZ9477_MAX_TC_PRIO)
+ if (tc_prio >= dev->info->num_ipvs)
break;
queue = ksz_ets_band_to_queue(p, p->priomap[tc_prio]);
@@ -3564,7 +3581,7 @@ static int ksz_tc_ets_del(struct ksz_device *dev, int port)
/* Revert the queue mapping for TC-priority to its default setting on
* the chip.
*/
- for (tc_prio = 0; tc_prio <= KSZ9477_MAX_TC_PRIO; tc_prio++) {
+ for (tc_prio = 0; tc_prio < dev->info->num_ipvs; tc_prio++) {
int queue;
queue = tc_prio >> s;
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 40c11b0d6b625..900e9ac06d013 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -58,6 +58,7 @@ struct ksz_chip_data {
int port_cnt;
u8 port_nirqs;
u8 num_tx_queues;
+ u8 num_ipvs; /* number of Internal Priority Values */
bool tc_cbs_supported;
bool tc_ets_supported;
const struct ksz_dev_ops *ops;
@@ -722,7 +723,6 @@ static inline int is_lan937x(struct ksz_device *dev)
#define KSZ9477_PORT_MRI_TC_MAP__4 0x0808
#define KSZ9477_PORT_TC_MAP_S 4
-#define KSZ9477_MAX_TC_PRIO 7
/* CBS related registers */
#define REG_PORT_MTI_QUEUE_INDEX__4 0x0900
--
2.39.2
I tested ETS support on KSZ9893, so it should work other KSZ989X
variants too, which was till not listed as support.
With this change we now officially not support only ksz8 family of
chips.
Signed-off-by: Oleksij Rempel <[email protected]>
Acked-by: Arun Ramadoss <[email protected]>
---
changes v4:
- simplify ETS support check. At this point only ksz8 family is not
supported.
---
drivers/net/dsa/microchip/ksz_common.c | 12 +-----------
drivers/net/dsa/microchip/ksz_common.h | 1 -
2 files changed, 1 insertion(+), 12 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 78a9622adecde..87a807ac7900e 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -1197,7 +1197,6 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.num_tx_queues = 4,
.num_ipvs = 8,
.tc_cbs_supported = true,
- .tc_ets_supported = true,
.ops = &ksz9477_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1344,7 +1343,6 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.num_tx_queues = 4,
.num_ipvs = 8,
.tc_cbs_supported = true,
- .tc_ets_supported = true,
.ops = &ksz9477_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1470,7 +1468,6 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.num_tx_queues = 4,
.num_ipvs = 8,
.tc_cbs_supported = true,
- .tc_ets_supported = true,
.ops = &ksz9477_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1499,7 +1496,6 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.num_tx_queues = 4,
.num_ipvs = 8,
.tc_cbs_supported = true,
- .tc_ets_supported = true,
.ops = &ksz9477_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1533,7 +1529,6 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.num_tx_queues = 4,
.num_ipvs = 8,
.tc_cbs_supported = true,
- .tc_ets_supported = true,
.ops = &ksz9477_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1566,7 +1561,6 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.num_tx_queues = 8,
.num_ipvs = 8,
.tc_cbs_supported = true,
- .tc_ets_supported = true,
.ops = &lan937x_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1594,7 +1588,6 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.num_tx_queues = 8,
.num_ipvs = 8,
.tc_cbs_supported = true,
- .tc_ets_supported = true,
.ops = &lan937x_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1622,7 +1615,6 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.num_tx_queues = 8,
.num_ipvs = 8,
.tc_cbs_supported = true,
- .tc_ets_supported = true,
.ops = &lan937x_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1654,7 +1646,6 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.num_tx_queues = 8,
.num_ipvs = 8,
.tc_cbs_supported = true,
- .tc_ets_supported = true,
.ops = &lan937x_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -1686,7 +1677,6 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.num_tx_queues = 8,
.num_ipvs = 8,
.tc_cbs_supported = true,
- .tc_ets_supported = true,
.ops = &lan937x_dev_ops,
.mib_names = ksz9477_mib_names,
.mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
@@ -3638,7 +3628,7 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port,
struct ksz_device *dev = ds->priv;
int ret;
- if (!dev->info->tc_ets_supported)
+ if (is_ksz8(dev))
return -EOPNOTSUPP;
if (qopt->parent != TC_H_ROOT) {
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 0089d01a04b99..18cc6d1e42ede 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -63,7 +63,6 @@ struct ksz_chip_data {
u8 num_tx_queues;
u8 num_ipvs; /* number of Internal Priority Values */
bool tc_cbs_supported;
- bool tc_ets_supported;
const struct ksz_dev_ops *ops;
bool ksz87xx_eee_link_erratum;
const struct ksz_mib_names *mib_names;
--
2.39.2
IEEE 802.1q specification provides recommendation and examples which can
be used as good default values for different drivers.
This patch implements mapping examples documented in IEEE 802.1Q-2022 in
Annex I "I.3 Traffic type to traffic class mapping" and IETF DSCP naming
and mapping DSCP to Traffic Type inspired by RFC8325.
This helpers will be used in followup patches for dsa/microchip DCB
implementation.
Signed-off-by: Oleksij Rempel <[email protected]>
---
changes v6:
- do not set default n for NET_IEEE8021Q_HELPERS
- protect against accessing outside of the arrays in the
ieee8021q_tt_to_tc()
- use u8 for dscp instead of int
changes v4:
- use -EOPNOTSUPP instead of -ENOTSUP
- ieee8021q_tt_to_tc() return error if requested not supported amount
of queues
changes v2:
- properly export symbols with EXPORT_SYMBOL_GPL()
- return error if NET_IEEE8021Q_HELPERS is not enabled
---
include/net/dscp.h | 76 +++++++++++++
include/net/ieee8021q.h | 55 +++++++++
net/Kconfig | 3 +
net/core/Makefile | 1 +
net/core/ieee8021q_helpers.c | 208 +++++++++++++++++++++++++++++++++++
5 files changed, 343 insertions(+)
create mode 100644 include/net/dscp.h
create mode 100644 include/net/ieee8021q.h
create mode 100644 net/core/ieee8021q_helpers.c
diff --git a/include/net/dscp.h b/include/net/dscp.h
new file mode 100644
index 0000000000000..ba40540868c9c
--- /dev/null
+++ b/include/net/dscp.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2024 Pengutronix, Oleksij Rempel <[email protected]> */
+
+#ifndef __DSCP_H__
+#define __DSCP_H__
+
+/*
+ * DSCP Pools and Codepoint Space Division:
+ *
+ * The Differentiated Services (Diffserv) architecture defines a method for
+ * classifying and managing network traffic using the DS field in IPv4 and IPv6
+ * packet headers. This field can carry one of 64 distinct DSCP (Differentiated
+ * Services Code Point) values, which are divided into three pools based on
+ * their Least Significant Bits (LSB) patterns and intended usage. Each pool has
+ * a specific registration procedure for assigning DSCP values:
+ *
+ * Pool 1 (Standards Action Pool):
+ * - Codepoint Space: xxxxx0
+ * This pool includes DSCP values ending in '0' (binary), allocated via
+ * Standards Action. It is intended for globally recognized traffic classes,
+ * ensuring interoperability across the internet. This pool encompasses
+ * well-known DSCP values such as CS0-CS7, AFxx, EF, and VOICE-ADMIT.
+ *
+ * Pool 2 (Experimental/Local Use Pool):
+ * - Codepoint Space: xxxx11
+ * Reserved for DSCP values ending in '11' (binary), this pool is designated
+ * for Experimental or Local Use. It allows for private or temporary traffic
+ * marking schemes not intended for standardized global use, facilitating
+ * testing and network-specific configurations without impacting
+ * interoperability.
+ *
+ * Pool 3 (Preferential Standardization Pool):
+ * - Codepoint Space: xxxx01
+ * Initially reserved for experimental or local use, this pool now serves as
+ * a secondary standardization resource should Pool 1 become exhausted. DSCP
+ * values ending in '01' (binary) are assigned via Standards Action, with a
+ * focus on adopting new, standardized traffic classes as the need arises.
+ *
+ * For pool updates see:
+ * https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml
+ */
+
+/* Pool 1: Standardized DSCP values as per [RFC8126] */
+#define DSCP_CS0 0 /* 000000, [RFC2474] */
+/* CS0 is some times called default (DF) */
+#define DSCP_DF 0 /* 000000, [RFC2474] */
+#define DSCP_CS1 8 /* 001000, [RFC2474] */
+#define DSCP_CS2 16 /* 010000, [RFC2474] */
+#define DSCP_CS3 24 /* 011000, [RFC2474] */
+#define DSCP_CS4 32 /* 100000, [RFC2474] */
+#define DSCP_CS5 40 /* 101000, [RFC2474] */
+#define DSCP_CS6 48 /* 110000, [RFC2474] */
+#define DSCP_CS7 56 /* 111000, [RFC2474] */
+#define DSCP_AF11 10 /* 001010, [RFC2597] */
+#define DSCP_AF12 12 /* 001100, [RFC2597] */
+#define DSCP_AF13 14 /* 001110, [RFC2597] */
+#define DSCP_AF21 18 /* 010010, [RFC2597] */
+#define DSCP_AF22 20 /* 010100, [RFC2597] */
+#define DSCP_AF23 22 /* 010110, [RFC2597] */
+#define DSCP_AF31 26 /* 011010, [RFC2597] */
+#define DSCP_AF32 28 /* 011100, [RFC2597] */
+#define DSCP_AF33 30 /* 011110, [RFC2597] */
+#define DSCP_AF41 34 /* 100010, [RFC2597] */
+#define DSCP_AF42 36 /* 100100, [RFC2597] */
+#define DSCP_AF43 38 /* 100110, [RFC2597] */
+#define DSCP_EF 46 /* 101110, [RFC3246] */
+#define DSCP_VOICE_ADMIT 44 /* 101100, [RFC5865] */
+
+/* Pool 3: Standardized assignments, previously available for experimental/local
+ * use
+ */
+#define DSCP_LE 1 /* 000001, [RFC8622] */
+
+#define DSCP_MAX 64
+
+#endif /* __DSCP_H__ */
diff --git a/include/net/ieee8021q.h b/include/net/ieee8021q.h
new file mode 100644
index 0000000000000..3bec7ec951362
--- /dev/null
+++ b/include/net/ieee8021q.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2024 Pengutronix, Oleksij Rempel <[email protected]> */
+
+#ifndef _NET_IEEE8021Q_H
+#define _NET_IEEE8021Q_H
+
+#include <linux/errno.h>
+
+/**
+ * enum ieee8021q_traffic_type - 802.1Q traffic type priority values (802.1Q-2022)
+ *
+ * @IEEE8021Q_TT_BK: Background
+ * @IEEE8021Q_TT_BE: Best Effort (default). According to 802.1Q-2022, BE is 0
+ * but has higher priority than BK which is 1.
+ * @IEEE8021Q_TT_EE: Excellent Effort
+ * @IEEE8021Q_TT_CA: Critical Applications
+ * @IEEE8021Q_TT_VI: Video, < 100 ms latency and jitter
+ * @IEEE8021Q_TT_VO: Voice, < 10 ms latency and jitter
+ * @IEEE8021Q_TT_IC: Internetwork Control
+ * @IEEE8021Q_TT_NC: Network Control
+ */
+enum ieee8021q_traffic_type {
+ IEEE8021Q_TT_BK = 0,
+ IEEE8021Q_TT_BE = 1,
+ IEEE8021Q_TT_EE = 2,
+ IEEE8021Q_TT_CA = 3,
+ IEEE8021Q_TT_VI = 4,
+ IEEE8021Q_TT_VO = 5,
+ IEEE8021Q_TT_IC = 6,
+ IEEE8021Q_TT_NC = 7,
+
+ IEEE8021Q_TT_MAX,
+};
+
+#define SIMPLE_IETF_DSCP_TO_IEEE8021Q_TT(dscp) ((dscp >> 3) & 0x7)
+
+#if IS_ENABLED(CONFIG_NET_IEEE8021Q_HELPERS)
+
+int ietf_dscp_to_ieee8021q_tt(u8 dscp);
+int ieee8021q_tt_to_tc(unsigned int tt, unsigned int num_queues);
+
+#else
+
+static inline int ietf_dscp_to_ieee8021q_tt(int dscp)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int ieee8021q_tt_to_tc(int tt, int num_queues)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif
+#endif /* _NET_IEEE8021Q_H */
diff --git a/net/Kconfig b/net/Kconfig
index d5ab791f7afa2..f0a8692496ffa 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -452,6 +452,9 @@ config GRO_CELLS
config SOCK_VALIDATE_XMIT
bool
+config NET_IEEE8021Q_HELPERS
+ bool
+
config NET_SELFTESTS
def_tristate PHYLIB
depends on PHYLIB && INET
diff --git a/net/core/Makefile b/net/core/Makefile
index 21d6fbc7e884c..62be9aef25285 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_NETPOLL) += netpoll.o
obj-$(CONFIG_FIB_RULES) += fib_rules.o
obj-$(CONFIG_TRACEPOINTS) += net-traces.o
obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o
+obj-$(CONFIG_NET_IEEE8021Q_HELPERS) += ieee8021q_helpers.o
obj-$(CONFIG_NET_SELFTESTS) += selftests.o
obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o
obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o
diff --git a/net/core/ieee8021q_helpers.c b/net/core/ieee8021q_helpers.c
new file mode 100644
index 0000000000000..74b42334746da
--- /dev/null
+++ b/net/core/ieee8021q_helpers.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2024 Pengutronix, Oleksij Rempel <[email protected]>
+
+#include <linux/array_size.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+#include <net/dscp.h>
+#include <net/ieee8021q.h>
+
+/* The following arrays map Traffic Types (TT) to traffic classes (TC) for
+ * different number of queues as shown in the example provided by
+ * IEEE 802.1Q-2022 in Annex I "I.3 Traffic type to traffic class mapping" and
+ * Table I-1 "Traffic type to traffic class mapping".
+ */
+static const u8 ieee8021q_8queue_tt_tc_map[] = {
+ [IEEE8021Q_TT_BK] = 0,
+ [IEEE8021Q_TT_BE] = 1,
+ [IEEE8021Q_TT_EE] = 2,
+ [IEEE8021Q_TT_CA] = 3,
+ [IEEE8021Q_TT_VI] = 4,
+ [IEEE8021Q_TT_VO] = 5,
+ [IEEE8021Q_TT_IC] = 6,
+ [IEEE8021Q_TT_NC] = 7,
+};
+
+static const u8 ieee8021q_7queue_tt_tc_map[] = {
+ [IEEE8021Q_TT_BK] = 0,
+ [IEEE8021Q_TT_BE] = 1,
+ [IEEE8021Q_TT_EE] = 2,
+ [IEEE8021Q_TT_CA] = 3,
+ [IEEE8021Q_TT_VI] = 4, [IEEE8021Q_TT_VO] = 4,
+ [IEEE8021Q_TT_IC] = 5,
+ [IEEE8021Q_TT_NC] = 6,
+};
+
+static const u8 ieee8021q_6queue_tt_tc_map[] = {
+ [IEEE8021Q_TT_BK] = 0,
+ [IEEE8021Q_TT_BE] = 1,
+ [IEEE8021Q_TT_EE] = 2, [IEEE8021Q_TT_CA] = 2,
+ [IEEE8021Q_TT_VI] = 3, [IEEE8021Q_TT_VO] = 3,
+ [IEEE8021Q_TT_IC] = 4,
+ [IEEE8021Q_TT_NC] = 5,
+};
+
+static const u8 ieee8021q_5queue_tt_tc_map[] = {
+ [IEEE8021Q_TT_BK] = 0, [IEEE8021Q_TT_BE] = 0,
+ [IEEE8021Q_TT_EE] = 1, [IEEE8021Q_TT_CA] = 1,
+ [IEEE8021Q_TT_VI] = 2, [IEEE8021Q_TT_VO] = 2,
+ [IEEE8021Q_TT_IC] = 3,
+ [IEEE8021Q_TT_NC] = 4,
+};
+
+static const u8 ieee8021q_4queue_tt_tc_map[] = {
+ [IEEE8021Q_TT_BK] = 0, [IEEE8021Q_TT_BE] = 0,
+ [IEEE8021Q_TT_EE] = 1, [IEEE8021Q_TT_CA] = 1,
+ [IEEE8021Q_TT_VI] = 2, [IEEE8021Q_TT_VO] = 2,
+ [IEEE8021Q_TT_IC] = 3, [IEEE8021Q_TT_NC] = 3,
+};
+
+static const u8 ieee8021q_3queue_tt_tc_map[] = {
+ [IEEE8021Q_TT_BK] = 0, [IEEE8021Q_TT_BE] = 0,
+ [IEEE8021Q_TT_EE] = 0, [IEEE8021Q_TT_CA] = 0,
+ [IEEE8021Q_TT_VI] = 1, [IEEE8021Q_TT_VO] = 1,
+ [IEEE8021Q_TT_IC] = 2, [IEEE8021Q_TT_NC] = 2,
+};
+
+static const u8 ieee8021q_2queue_tt_tc_map[] = {
+ [IEEE8021Q_TT_BK] = 0, [IEEE8021Q_TT_BE] = 0,
+ [IEEE8021Q_TT_EE] = 0, [IEEE8021Q_TT_CA] = 0,
+ [IEEE8021Q_TT_VI] = 1, [IEEE8021Q_TT_VO] = 1,
+ [IEEE8021Q_TT_IC] = 1, [IEEE8021Q_TT_NC] = 1,
+};
+
+static const u8 ieee8021q_1queue_tt_tc_map[] = {
+ [IEEE8021Q_TT_BK] = 0, [IEEE8021Q_TT_BE] = 0,
+ [IEEE8021Q_TT_EE] = 0, [IEEE8021Q_TT_CA] = 0,
+ [IEEE8021Q_TT_VI] = 0, [IEEE8021Q_TT_VO] = 0,
+ [IEEE8021Q_TT_IC] = 0, [IEEE8021Q_TT_NC] = 0,
+};
+
+/**
+ * ieee8021q_tt_to_tc - Map IEEE 802.1Q Traffic Type to Traffic Class
+ * @tt: IEEE 802.1Q Traffic Type
+ * @num_queues: Number of queues
+ *
+ * This function maps an IEEE 802.1Q Traffic Type to a Traffic Class (TC) based
+ * on the number of queues configured on the switch. The mapping is based on the
+ * example provided by IEEE 802.1Q-2022 in Annex I "I.3 Traffic type to traffic
+ * class mapping" and Table I-1 "Traffic type to traffic class mapping".
+ *
+ * Return: Traffic Class corresponding to the given Traffic Type or -EINVAL if
+ * the number of queues is not supported. -EINVAL is used to differentiate from
+ * -EOPNOTSUPP which is used to indicate that this library function is not
+ * compiled in.
+ */
+int ieee8021q_tt_to_tc(unsigned int tt, unsigned int num_queues)
+{
+ if (tt >= IEEE8021Q_TT_MAX) {
+ pr_err("Requested Traffic Type (%d) is out of range (%d)\n", tt,
+ IEEE8021Q_TT_MAX);
+ return -EINVAL;
+ }
+
+ switch (num_queues) {
+ case 8:
+ compiletime_assert(ARRAY_SIZE(ieee8021q_8queue_tt_tc_map) !=
+ IEEE8021Q_TT_MAX - 1,
+ "ieee8021q_8queue_tt_tc_map != max - 1");
+ return ieee8021q_8queue_tt_tc_map[tt];
+ case 7:
+ compiletime_assert(ARRAY_SIZE(ieee8021q_7queue_tt_tc_map) !=
+ IEEE8021Q_TT_MAX - 1,
+ "ieee8021q_7queue_tt_tc_map != max - 1");
+
+ return ieee8021q_7queue_tt_tc_map[tt];
+ case 6:
+ compiletime_assert(ARRAY_SIZE(ieee8021q_6queue_tt_tc_map) !=
+ IEEE8021Q_TT_MAX - 1,
+ "ieee8021q_6queue_tt_tc_map != max - 1");
+
+ return ieee8021q_6queue_tt_tc_map[tt];
+ case 5:
+ compiletime_assert(ARRAY_SIZE(ieee8021q_5queue_tt_tc_map) !=
+ IEEE8021Q_TT_MAX - 1,
+ "ieee8021q_5queue_tt_tc_map != max - 1");
+
+ return ieee8021q_5queue_tt_tc_map[tt];
+ case 4:
+ compiletime_assert(ARRAY_SIZE(ieee8021q_4queue_tt_tc_map) !=
+ IEEE8021Q_TT_MAX - 1,
+ "ieee8021q_4queue_tt_tc_map != max - 1");
+
+ return ieee8021q_4queue_tt_tc_map[tt];
+ case 3:
+ compiletime_assert(ARRAY_SIZE(ieee8021q_3queue_tt_tc_map) !=
+ IEEE8021Q_TT_MAX - 1,
+ "ieee8021q_3queue_tt_tc_map != max - 1");
+
+ return ieee8021q_3queue_tt_tc_map[tt];
+ case 2:
+ compiletime_assert(ARRAY_SIZE(ieee8021q_2queue_tt_tc_map) !=
+ IEEE8021Q_TT_MAX - 1,
+ "ieee8021q_2queue_tt_tc_map != max - 1");
+
+ return ieee8021q_2queue_tt_tc_map[tt];
+ case 1:
+ compiletime_assert(ARRAY_SIZE(ieee8021q_1queue_tt_tc_map) !=
+ IEEE8021Q_TT_MAX - 1,
+ "ieee8021q_1queue_tt_tc_map != max - 1");
+
+ return ieee8021q_1queue_tt_tc_map[tt];
+ }
+
+ pr_err("Invalid number of queues %d\n", num_queues);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ieee8021q_tt_to_tc);
+
+/**
+ * ietf_dscp_to_ieee8021q_tt - Map IETF DSCP to IEEE 802.1Q Traffic Type
+ * @dscp: IETF DSCP value
+ *
+ * This function maps an IETF DSCP value to an IEEE 802.1Q Traffic Type (TT).
+ * Since there is no corresponding mapping between DSCP and IEEE 802.1Q Traffic
+ * Type, this function is inspired by the RFC8325 documentation which describe
+ * the mapping between DSCP and 802.11 User Priority (UP) values.
+ *
+ * Return: IEEE 802.1Q Traffic Type corresponding to the given DSCP value
+ */
+int ietf_dscp_to_ieee8021q_tt(u8 dscp)
+{
+ switch (dscp) {
+ case DSCP_CS0:
+ case DSCP_AF11:
+ case DSCP_AF12:
+ case DSCP_AF13:
+ return IEEE8021Q_TT_BE;
+ case DSCP_CS1:
+ return IEEE8021Q_TT_BK;
+ case DSCP_CS2:
+ case DSCP_AF21:
+ case DSCP_AF22:
+ case DSCP_AF23:
+ return IEEE8021Q_TT_EE;
+ case DSCP_CS3:
+ case DSCP_AF31:
+ case DSCP_AF32:
+ case DSCP_AF33:
+ return IEEE8021Q_TT_CA;
+ case DSCP_CS4:
+ case DSCP_AF41:
+ case DSCP_AF42:
+ case DSCP_AF43:
+ return IEEE8021Q_TT_VI;
+ case DSCP_CS5:
+ case DSCP_EF:
+ case DSCP_VOICE_ADMIT:
+ return IEEE8021Q_TT_VO;
+ case DSCP_CS6:
+ return IEEE8021Q_TT_IC;
+ case DSCP_CS7:
+ return IEEE8021Q_TT_NC;
+ }
+
+ return SIMPLE_IETF_DSCP_TO_IEEE8021Q_TT(dscp);
+}
+EXPORT_SYMBOL_GPL(ietf_dscp_to_ieee8021q_tt);
--
2.39.2
On Wed, Apr 10, 2024 at 10:05:52AM +0200, Oleksij Rempel wrote:
> diff --git a/drivers/net/dsa/microchip/ksz_dcb.c b/drivers/net/dsa/microchip/ksz_dcb.c
> new file mode 100644
> index 0000000000000..d2122f844c80b
> --- /dev/null
> +++ b/drivers/net/dsa/microchip/ksz_dcb.c
> +/**
> + * ksz_supported_apptrust[] - Supported apptrust selectors and Priority Order
> + * of Internal Priority Value (IPV) sources.
> + *
> + * This array defines the apptrust selectors supported by the hardware, where
> + * the index within the array indicates the priority of the selector - lower
> + * indices correspond to higher priority. This fixed priority scheme is due to
> + * the hardware's design, which does not support configurable priority among
> + * different priority sources.
> + *
> + * The priority sources, including Tail Tag, ACL, VLAN PCP and DSCP are ordered
> + * by the hardware's fixed logic, as detailed below. The order reflects a
> + * non-configurable precedence where certain types of priority information
> + * override others:
> + *
> + * 1. Tail Tag - Highest priority, overrides ACL, VLAN PCP, and DSCP priorities.
> + * 2. ACL - Overrides VLAN PCP and DSCP priorities.
> + * 3. VLAN PCP - Overrides DSCP priority.
> + * 4. DSCP - Lowest priority, does not override any other priority source.
> + *
> + * In this context, the array's lower index (higher priority) for
> + * 'DCB_APP_SEL_PCP' suggests its relative priority over
> + * 'IEEE_8021QAZ_APP_SEL_DSCP' within the system's fixed priority scheme.
> + *
> + * DCB_APP_SEL_PCP - Priority Code Point selector
> + * IEEE_8021QAZ_APP_SEL_DSCP - Differentiated Services Code Point selector
> + */
> +static const u8 ksz_supported_apptrust[] = {
> + DCB_APP_SEL_PCP,
> + IEEE_8021QAZ_APP_SEL_DSCP,
> +};
I've no idea how kernel-doc is supposed to document arrays. But this
generates a scripts/kernel-doc warning:
CHECK ../drivers/net/dsa/microchip/ksz_dcb.c
./drivers/net/dsa/microchip/ksz_dcb.c:81: warning: cannot understand function prototype: 'const u8 ksz_supported_apptrust[] = '
Worst case, it doesn't need to be a kernel-doc?
On Wed, Apr 10, 2024 at 06:32:36PM +0300, Vladimir Oltean wrote:
> On Wed, Apr 10, 2024 at 10:05:52AM +0200, Oleksij Rempel wrote:
> > diff --git a/drivers/net/dsa/microchip/ksz_dcb.c b/drivers/net/dsa/microchip/ksz_dcb.c
> > new file mode 100644
> > index 0000000000000..d2122f844c80b
> > --- /dev/null
> > +++ b/drivers/net/dsa/microchip/ksz_dcb.c
> > +/**
> > + * ksz_supported_apptrust[] - Supported apptrust selectors and Priority Order
> > + * of Internal Priority Value (IPV) sources.
> > + *
> > + * This array defines the apptrust selectors supported by the hardware, where
> > + * the index within the array indicates the priority of the selector - lower
> > + * indices correspond to higher priority. This fixed priority scheme is due to
> > + * the hardware's design, which does not support configurable priority among
> > + * different priority sources.
> > + *
> > + * The priority sources, including Tail Tag, ACL, VLAN PCP and DSCP are ordered
> > + * by the hardware's fixed logic, as detailed below. The order reflects a
> > + * non-configurable precedence where certain types of priority information
> > + * override others:
> > + *
> > + * 1. Tail Tag - Highest priority, overrides ACL, VLAN PCP, and DSCP priorities.
> > + * 2. ACL - Overrides VLAN PCP and DSCP priorities.
> > + * 3. VLAN PCP - Overrides DSCP priority.
> > + * 4. DSCP - Lowest priority, does not override any other priority source.
> > + *
> > + * In this context, the array's lower index (higher priority) for
> > + * 'DCB_APP_SEL_PCP' suggests its relative priority over
> > + * 'IEEE_8021QAZ_APP_SEL_DSCP' within the system's fixed priority scheme.
> > + *
> > + * DCB_APP_SEL_PCP - Priority Code Point selector
> > + * IEEE_8021QAZ_APP_SEL_DSCP - Differentiated Services Code Point selector
> > + */
> > +static const u8 ksz_supported_apptrust[] = {
> > + DCB_APP_SEL_PCP,
> > + IEEE_8021QAZ_APP_SEL_DSCP,
> > +};
>
> I've no idea how kernel-doc is supposed to document arrays. But this
> generates a scripts/kernel-doc warning:
>
> CHECK ../drivers/net/dsa/microchip/ksz_dcb.c
> ./drivers/net/dsa/microchip/ksz_dcb.c:81: warning: cannot understand function prototype: 'const u8 ksz_supported_apptrust[] = '
>
> Worst case, it doesn't need to be a kernel-doc?
I can remove the part which makes this comment a kenrel doc.
Are there other issues for this patch? Can it get your Reviewed-by if
fixed?
Regards,
Oleksij
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
Hi Oleksij,
On Wed, Apr 10, 2024 at 10:05:52AM +0200, Oleksij Rempel wrote:
> diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
> index b2d1c61400c51..78a9622adecde 100644
> --- a/drivers/net/dsa/microchip/ksz_common.c
> +++ b/drivers/net/dsa/microchip/ksz_common.c
> @@ -2364,6 +2365,10 @@ static int ksz_setup(struct dsa_switch *ds)
> goto out_ptp_clock_unregister;
> }
>
> + ret = ksz_dcb_init(dev);
> + if (ret)
> + goto out_ptp_clock_unregister;
> +
> /* start switch */
> regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
> SW_START, SW_START);
> @@ -2691,7 +2696,7 @@ static int ksz_port_setup(struct dsa_switch *ds, int port)
> * there is no need to do anything.
> */
>
> - return 0;
> + return ksz_dcb_init_port(dev, port);
> }
>
> void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
> @@ -3943,6 +3948,11 @@ static const struct dsa_switch_ops ksz_switch_ops = {
> .port_setup_tc = ksz_setup_tc,
> .get_mac_eee = ksz_get_mac_eee,
> .set_mac_eee = ksz_set_mac_eee,
> + .port_get_default_prio = ksz_port_get_default_prio,
> + .port_set_default_prio = ksz_port_set_default_prio,
> + .port_get_dscp_prio = ksz_port_get_dscp_prio,
> + .port_get_apptrust = ksz_port_get_apptrust,
> + .port_set_apptrust = ksz_port_set_apptrust,
> };
>
> struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
> diff --git a/drivers/net/dsa/microchip/ksz_dcb.c b/drivers/net/dsa/microchip/ksz_dcb.c
> new file mode 100644
> index 0000000000000..d2122f844c80b
> --- /dev/null
> +++ b/drivers/net/dsa/microchip/ksz_dcb.c
> +/**
> + * ksz_init_global_dscp_map - Initializes the global DSCP-to-priority mapping
> + * @dev: Pointer to the KSZ switch device structure
> + *
> + * This function initializes the global DSCP-to-priority mapping table for the
> + * switch.
> + *
> + * Return: 0 on success, or a negative error code on failure
> + */
> +static int ksz_init_global_dscp_map(struct ksz_device *dev)
> +{
> + int reg, per_reg, ret, dscp;
> + u8 data = 0;
> + u8 mask;
> +
> + /* On KSZ9xxx variants, DSCP remapping is disabled by default.
> + * Enable to have, predictable and reproducible behavior across
> + * different devices.
> + */
> + if (!is_ksz8(dev)) {
> + ret = ksz_rmw8(dev, KSZ9477_REG_SW_MAC_TOS_CTRL,
> + KSZ9477_SW_TOS_DSCP_REMAP,
> + KSZ9477_SW_TOS_DSCP_REMAP);
> + if (ret)
> + return ret;
> + }
> +
> + ksz_get_dscp_prio_reg(dev, ®, &per_reg, &mask);
> +
> + for (dscp = 0; dscp < DSCP_MAX; dscp++) {
> + int ipv, shift, tt;
> +
> + /* Map DSCP to Traffic Type, which is corresponding to the
> + * Internal Priority Value (IPV) in the switch.
> + */
> + if (!is_ksz8(dev)) {
> + ipv = ietf_dscp_to_ieee8021q_tt(dscp);
> + } else {
> + /* On KSZ8xxx variants we do not have IPV to queue
> + * remapping table. We need to convert DSCP to Traffic
> + * Type and then to queue.
> + */
> + tt = ietf_dscp_to_ieee8021q_tt(dscp);
> + if (tt < 0)
> + return tt;
> +
> + ipv = ieee8021q_tt_to_tc(tt, dev->info->num_tx_queues);
> + }
> +
> + if (ipv < 0)
> + return ipv;
> +
> + shift = (dscp % per_reg) * (8 / per_reg);
> + data |= (ipv & mask) << shift;
> +
> + if (dscp % per_reg == per_reg - 1) {
> + ret = ksz_write8(dev, reg + (dscp / per_reg), data);
> + if (ret)
> + return ret;
> +
> + data = 0;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * ksz_dcb_init_port - Initializes the DCB configuration for a port on a KSZ
> + * @dev: Pointer to the KSZ switch device structure
> + * @port: Port number for which to initialize the DCB configuration
> + *
> + * This function initializes the DCB configuration for the specified port on a
> + * KSZ switch. Particular DCB configuration is set for the port, including the
> + * default priority and apptrust selectors.
> + * The default priority is set to Best Effort, and the apptrust selectors are
> + * set to all supported selectors.
> + *
> + * Return: 0 on success, or a negative error code on failure
> + */
> +int ksz_dcb_init_port(struct ksz_device *dev, int port)
> +{
> + int ret, ipv;
> +
> + if (is_ksz8(dev)) {
> + ipv = ieee8021q_tt_to_tc(IEEE8021Q_TT_BE,
> + dev->info->num_tx_queues);
> + if (ipv < 0)
> + return ipv;
> + } else {
> + ipv = IEEE8021Q_TT_BE;
> + }
> +
> + /* Set the default priority for the port to Best Effort */
> + ret = ksz_port_set_default_prio(dev->ds, port, ipv);
> + if (ret)
> + return ret;
> +
> + return ksz_port_set_apptrust(dev->ds, port, ksz_supported_apptrust,
> + ARRAY_SIZE(ksz_supported_apptrust));
> +}
> +
> +/**
> + * ksz_dcb_init - Initializes the DCB configuration for a KSZ switch
> + * @dev: Pointer to the KSZ switch device structure
> + *
> + * This function initializes the DCB configuration for a KSZ switch. The global
> + * DSCP-to-priority mapping table is initialized.
> + *
> + * Return: 0 on success, or a negative error code on failure
> + */
> +int ksz_dcb_init(struct ksz_device *dev)
> +{
> + int ret;
> +
> + ret = ksz_init_global_dscp_map(dev);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
Sorry for not responding to your previous question about this:
https://lore.kernel.org/netdev/[email protected]/
Simply put, I had a period with not a lot of free time, even for reading
emails.
I'm on the fence on whether your solution to the "global DSCP-to-prio
mapping rather than per-port" problem is the right one.
We try to avoid baking policies into the kernel, no matter how well
intended the 802.1Q and IETF RFC8325 recommendations are. They are still
just recommendations and examples, and a particular use case may want to
configure things completely differently (or as hinted in the Wi-Fi specific
RFC8325: maybe the administrator doesn't want to assign the higher
traffic classes, for network control protocols, by using IP DSCP, and
doesn't want user flows to request DSCP values that would get access to
these traffic classes. It can indeed be seen as a security concern).
I empathize with the incovenience of having to map the per-netdev dcbnl
application priority table API with a piece of hardware where that table
is shared across all ports. But yet, I don't think it is a strong enough
justification for us to make an exception and say: "yeah, ok, let's not
even implement .port_set_dscp_prio() to make the thing configurable, but
let's bake into the kernel a fixed policy that's good for everyone".
No, I think we _need_ the thing to be configurable, and not try so hard
with the ieee8021q helpers to hardcode things just right in the kernel.
Have you tried the obvious: "every time there is a change to the global
DSCP mapping table, push the change into the dcbnl app table of all user
netdevs, so that the user becomes aware of what happens"? Kernel drivers
can do that, through direct calls to dcb_ieee_setapp(). DSA does it too,
to probe the initial QoS configuration of the ports and push it to the
application priority tables.
On Wed, Apr 10, 2024 at 10:05:47AM +0200, Oleksij Rempel wrote:
> This patch series is aimed at improving support for DCB (Data Center
> Bridging) and DSCP (Differentiated Services Code Point) on KSZ switches.
>
> The main goal is to introduce global DSCP and PCP (Priority Code Point)
> mapping support, addressing the limitation of KSZ switches not having
> per-port DSCP priority mapping. This involves extending the DSA
> framework with new callbacks for managing trust settings for global DSCP
> and PCP maps. Additionally, we introduce IEEE 802.1q helpers for default
> configurations, benefiting other drivers too.
>
> Change logs are in separate patches.
In whichever way this goes, could we also see a selftest in future patch
revisions (similar to tools/testing/selftests/drivers/net/ocelot/basic_qos.sh),
which (beyond the basic task of proving that it works) sets straight the
basic user expectations of _what_ works and can be done in terms of QoS
classification on these switches?
Hi Vladimir,
On Thu, Apr 11, 2024 at 02:12:51AM +0300, Vladimir Oltean wrote:
> > +/**
> > + * ksz_dcb_init - Initializes the DCB configuration for a KSZ switch
> > + * @dev: Pointer to the KSZ switch device structure
> > + *
> > + * This function initializes the DCB configuration for a KSZ switch. The global
> > + * DSCP-to-priority mapping table is initialized.
> > + *
> > + * Return: 0 on success, or a negative error code on failure
> > + */
> > +int ksz_dcb_init(struct ksz_device *dev)
> > +{
> > + int ret;
> > +
> > + ret = ksz_init_global_dscp_map(dev);
> > + if (ret)
> > + return ret;
> > +
> > + return 0;
> > +}
>
> Sorry for not responding to your previous question about this:
> https://lore.kernel.org/netdev/[email protected]/
> Simply put, I had a period with not a lot of free time, even for reading
> emails.
No problem. I'm in continues similar state permanently DoSed by my
children, parents, etc... :)
> I'm on the fence on whether your solution to the "global DSCP-to-prio
> mapping rather than per-port" problem is the right one.
>
> We try to avoid baking policies into the kernel, no matter how well
> intended the 802.1Q and IETF RFC8325 recommendations are. They are still
> just recommendations and examples, and a particular use case may want to
> configure things completely differently (or as hinted in the Wi-Fi specific
> RFC8325: maybe the administrator doesn't want to assign the higher
> traffic classes, for network control protocols, by using IP DSCP, and
> doesn't want user flows to request DSCP values that would get access to
> these traffic classes. It can indeed be seen as a security concern).
>
> I empathize with the incovenience of having to map the per-netdev dcbnl
> application priority table API with a piece of hardware where that table
> is shared across all ports. But yet, I don't think it is a strong enough
> justification for us to make an exception and say: "yeah, ok, let's not
> even implement .port_set_dscp_prio() to make the thing configurable, but
> let's bake into the kernel a fixed policy that's good for everyone".
>
> No, I think we _need_ the thing to be configurable, and not try so hard
> with the ieee8021q helpers to hardcode things just right in the kernel.
Yes, I agree with you.
ieee8021q helpers are not the attempt to avoid the work needed to
implement global DSCP configuration. The interface is still needed and
we need to agree on how it should be implemented.
The problem which I try to address with ieee8021q helpers are initial
defaults. KSZ8 and KSZ9 families of switches have different initial
defaults. So, if i need to align defaults for this driver, why not to
provide default which are reusable for every one?
> Have you tried the obvious: "every time there is a change to the global
> DSCP mapping table, push the change into the dcbnl app table of all user
> netdevs, so that the user becomes aware of what happens"? Kernel drivers
> can do that, through direct calls to dcb_ieee_setapp(). DSA does it too,
> to probe the initial QoS configuration of the ports and push it to the
> application priority tables.
Hm... what interface should be used for the global DSCP mapping table?
Regards,
Oleksij
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
On Thu, Apr 11, 2024 at 08:48:04AM +0200, Oleksij Rempel wrote:
> > No, I think we _need_ the thing to be configurable, and not try so hard
> > with the ieee8021q helpers to hardcode things just right in the kernel.
>
> Yes, I agree with you.
>
> ieee8021q helpers are not the attempt to avoid the work needed to
> implement global DSCP configuration. The interface is still needed and
> we need to agree on how it should be implemented.
>
> The problem which I try to address with ieee8021q helpers are initial
> defaults. KSZ8 and KSZ9 families of switches have different initial
> defaults. So, if i need to align defaults for this driver, why not to
> provide default which are reusable for every one?
Yeah, I don't know. "Don't trust DSCP" is not a good default?
Although at the same time I do realize that my own position is not
justified by my own actions (my drivers do trust VLAN PCP by default,
and the mapping is hardcoded and not configurable).
> > Have you tried the obvious: "every time there is a change to the global
> > DSCP mapping table, push the change into the dcbnl app table of all user
> > netdevs, so that the user becomes aware of what happens"? Kernel drivers
> > can do that, through direct calls to dcb_ieee_setapp(). DSA does it too,
> > to probe the initial QoS configuration of the ports and push it to the
> > application priority tables.
>
> Hm... what interface should be used for the global DSCP mapping table?
Not any different than if it was per port... Just access the global DSCP
mapping table per port, and the kernel updates the table for every other
user port. That was my idea.
On Wed, Apr 10, 2024 at 10:05:50AM +0200, Oleksij Rempel wrote:
> IEEE 802.1q specification provides recommendation and examples which can
> be used as good default values for different drivers.
>
> This patch implements mapping examples documented in IEEE 802.1Q-2022 in
> Annex I "I.3 Traffic type to traffic class mapping" and IETF DSCP naming
> and mapping DSCP to Traffic Type inspired by RFC8325.
>
> This helpers will be used in followup patches for dsa/microchip DCB
> implementation.
>
> Signed-off-by: Oleksij Rempel <[email protected]>
..
> diff --git a/include/net/ieee8021q.h b/include/net/ieee8021q.h
> new file mode 100644
> index 0000000000000..3bec7ec951362
> --- /dev/null
> +++ b/include/net/ieee8021q.h
> @@ -0,0 +1,55 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2024 Pengutronix, Oleksij Rempel <[email protected]> */
> +
> +#ifndef _NET_IEEE8021Q_H
> +#define _NET_IEEE8021Q_H
> +
> +#include <linux/errno.h>
> +
> +/**
> + * enum ieee8021q_traffic_type - 802.1Q traffic type priority values (802.1Q-2022)
> + *
> + * @IEEE8021Q_TT_BK: Background
> + * @IEEE8021Q_TT_BE: Best Effort (default). According to 802.1Q-2022, BE is 0
> + * but has higher priority than BK which is 1.
> + * @IEEE8021Q_TT_EE: Excellent Effort
> + * @IEEE8021Q_TT_CA: Critical Applications
> + * @IEEE8021Q_TT_VI: Video, < 100 ms latency and jitter
> + * @IEEE8021Q_TT_VO: Voice, < 10 ms latency and jitter
> + * @IEEE8021Q_TT_IC: Internetwork Control
> + * @IEEE8021Q_TT_NC: Network Control
> + */
> +enum ieee8021q_traffic_type {
> + IEEE8021Q_TT_BK = 0,
> + IEEE8021Q_TT_BE = 1,
> + IEEE8021Q_TT_EE = 2,
> + IEEE8021Q_TT_CA = 3,
> + IEEE8021Q_TT_VI = 4,
> + IEEE8021Q_TT_VO = 5,
> + IEEE8021Q_TT_IC = 6,
> + IEEE8021Q_TT_NC = 7,
> +
Hi Oleksij,
I think the following line should go here to keep ./scripts/kernel-doc -none
happy:
/* private: */
> + IEEE8021Q_TT_MAX,
> +};
..
I have the following comments already written; sending the email mainly
to close the window. All of these comments are guarded by a big:
"I'm not sure if this really belongs in the kernel." Anyway, here goes.
On Wed, Apr 10, 2024 at 10:05:50AM +0200, Oleksij Rempel wrote:
> IEEE 802.1q specification provides recommendation and examples which can
> be used as good default values for different drivers.
>
> This patch implements mapping examples documented in IEEE 802.1Q-2022 in
> Annex I "I.3 Traffic type to traffic class mapping" and IETF DSCP naming
> and mapping DSCP to Traffic Type inspired by RFC8325.
>
> This helpers will be used in followup patches for dsa/microchip DCB
> implementation.
>
> Signed-off-by: Oleksij Rempel <[email protected]>
> ---
> diff --git a/include/net/ieee8021q.h b/include/net/ieee8021q.h
> new file mode 100644
> index 0000000000000..3bec7ec951362
> --- /dev/null
> +++ b/include/net/ieee8021q.h
> @@ -0,0 +1,55 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2024 Pengutronix, Oleksij Rempel <[email protected]> */
> +
> +#ifndef _NET_IEEE8021Q_H
> +#define _NET_IEEE8021Q_H
> +
> +#include <linux/errno.h>
> +
> +/**
> + * enum ieee8021q_traffic_type - 802.1Q traffic type priority values (802.1Q-2022)
> + *
> + * @IEEE8021Q_TT_BK: Background
> + * @IEEE8021Q_TT_BE: Best Effort (default). According to 802.1Q-2022, BE is 0
> + * but has higher priority than BK which is 1.
> + * @IEEE8021Q_TT_EE: Excellent Effort
> + * @IEEE8021Q_TT_CA: Critical Applications
> + * @IEEE8021Q_TT_VI: Video, < 100 ms latency and jitter
> + * @IEEE8021Q_TT_VO: Voice, < 10 ms latency and jitter
> + * @IEEE8021Q_TT_IC: Internetwork Control
> + * @IEEE8021Q_TT_NC: Network Control
We get kernel-doc warnings about IEEE8021Q_TT_MAX not being documented.
Simon also suggested to make it private, which I guess will work.
> + */
> +enum ieee8021q_traffic_type {
> + IEEE8021Q_TT_BK = 0,
> + IEEE8021Q_TT_BE = 1,
> + IEEE8021Q_TT_EE = 2,
> + IEEE8021Q_TT_CA = 3,
> + IEEE8021Q_TT_VI = 4,
> + IEEE8021Q_TT_VO = 5,
> + IEEE8021Q_TT_IC = 6,
> + IEEE8021Q_TT_NC = 7,
> +
> + IEEE8021Q_TT_MAX,
> +};
> +
> +#define SIMPLE_IETF_DSCP_TO_IEEE8021Q_TT(dscp) ((dscp >> 3) & 0x7)
> +
> +#if IS_ENABLED(CONFIG_NET_IEEE8021Q_HELPERS)
> +
> +int ietf_dscp_to_ieee8021q_tt(u8 dscp);
> +int ieee8021q_tt_to_tc(unsigned int tt, unsigned int num_queues);
> +
> +#else
> +
> +static inline int ietf_dscp_to_ieee8021q_tt(int dscp)
Function prototype differs when CONFIG_NET_IEEE8021Q_HELPERS is disabled
and when it is enabled (u8 dscp vs int dscp).
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static inline int ieee8021q_tt_to_tc(int tt, int num_queues)
Same here (unsigned int tt vs int tt).
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +#endif
> +#endif /* _NET_IEEE8021Q_H */
> diff --git a/net/Kconfig b/net/Kconfig
> index d5ab791f7afa2..f0a8692496ffa 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -452,6 +452,9 @@ config GRO_CELLS
> config SOCK_VALIDATE_XMIT
> bool
>
> +config NET_IEEE8021Q_HELPERS
> + bool
> +
> config NET_SELFTESTS
> def_tristate PHYLIB
> depends on PHYLIB && INET
> diff --git a/net/core/Makefile b/net/core/Makefile
> index 21d6fbc7e884c..62be9aef25285 100644
> --- a/net/core/Makefile
> +++ b/net/core/Makefile
> @@ -26,6 +26,7 @@ obj-$(CONFIG_NETPOLL) += netpoll.o
> obj-$(CONFIG_FIB_RULES) += fib_rules.o
> obj-$(CONFIG_TRACEPOINTS) += net-traces.o
> obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o
> +obj-$(CONFIG_NET_IEEE8021Q_HELPERS) += ieee8021q_helpers.o
> obj-$(CONFIG_NET_SELFTESTS) += selftests.o
> obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o
> obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o
> diff --git a/net/core/ieee8021q_helpers.c b/net/core/ieee8021q_helpers.c
> new file mode 100644
> index 0000000000000..74b42334746da
> --- /dev/null
> +++ b/net/core/ieee8021q_helpers.c
> @@ -0,0 +1,208 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2024 Pengutronix, Oleksij Rempel <[email protected]>
> +
> +#include <linux/array_size.h>
> +#include <linux/printk.h>
> +#include <linux/types.h>
> +#include <net/dscp.h>
> +#include <net/ieee8021q.h>
> +
> +/* The following arrays map Traffic Types (TT) to traffic classes (TC) for
> + * different number of queues as shown in the example provided by
> + * IEEE 802.1Q-2022 in Annex I "I.3 Traffic type to traffic class mapping" and
> + * Table I-1 "Traffic type to traffic class mapping".
> + */
> +static const u8 ieee8021q_8queue_tt_tc_map[] = {
> + [IEEE8021Q_TT_BK] = 0,
> + [IEEE8021Q_TT_BE] = 1,
> + [IEEE8021Q_TT_EE] = 2,
> + [IEEE8021Q_TT_CA] = 3,
> + [IEEE8021Q_TT_VI] = 4,
> + [IEEE8021Q_TT_VO] = 5,
> + [IEEE8021Q_TT_IC] = 6,
> + [IEEE8021Q_TT_NC] = 7,
> +};
> +
> +static const u8 ieee8021q_7queue_tt_tc_map[] = {
> + [IEEE8021Q_TT_BK] = 0,
> + [IEEE8021Q_TT_BE] = 1,
> + [IEEE8021Q_TT_EE] = 2,
> + [IEEE8021Q_TT_CA] = 3,
> + [IEEE8021Q_TT_VI] = 4, [IEEE8021Q_TT_VO] = 4,
> + [IEEE8021Q_TT_IC] = 5,
> + [IEEE8021Q_TT_NC] = 6,
> +};
> +
> +static const u8 ieee8021q_6queue_tt_tc_map[] = {
> + [IEEE8021Q_TT_BK] = 0,
> + [IEEE8021Q_TT_BE] = 1,
> + [IEEE8021Q_TT_EE] = 2, [IEEE8021Q_TT_CA] = 2,
> + [IEEE8021Q_TT_VI] = 3, [IEEE8021Q_TT_VO] = 3,
> + [IEEE8021Q_TT_IC] = 4,
> + [IEEE8021Q_TT_NC] = 5,
> +};
> +
> +static const u8 ieee8021q_5queue_tt_tc_map[] = {
> + [IEEE8021Q_TT_BK] = 0, [IEEE8021Q_TT_BE] = 0,
> + [IEEE8021Q_TT_EE] = 1, [IEEE8021Q_TT_CA] = 1,
> + [IEEE8021Q_TT_VI] = 2, [IEEE8021Q_TT_VO] = 2,
> + [IEEE8021Q_TT_IC] = 3,
> + [IEEE8021Q_TT_NC] = 4,
> +};
> +
> +static const u8 ieee8021q_4queue_tt_tc_map[] = {
> + [IEEE8021Q_TT_BK] = 0, [IEEE8021Q_TT_BE] = 0,
> + [IEEE8021Q_TT_EE] = 1, [IEEE8021Q_TT_CA] = 1,
> + [IEEE8021Q_TT_VI] = 2, [IEEE8021Q_TT_VO] = 2,
> + [IEEE8021Q_TT_IC] = 3, [IEEE8021Q_TT_NC] = 3,
> +};
> +
> +static const u8 ieee8021q_3queue_tt_tc_map[] = {
> + [IEEE8021Q_TT_BK] = 0, [IEEE8021Q_TT_BE] = 0,
> + [IEEE8021Q_TT_EE] = 0, [IEEE8021Q_TT_CA] = 0,
> + [IEEE8021Q_TT_VI] = 1, [IEEE8021Q_TT_VO] = 1,
> + [IEEE8021Q_TT_IC] = 2, [IEEE8021Q_TT_NC] = 2,
> +};
> +
> +static const u8 ieee8021q_2queue_tt_tc_map[] = {
> + [IEEE8021Q_TT_BK] = 0, [IEEE8021Q_TT_BE] = 0,
> + [IEEE8021Q_TT_EE] = 0, [IEEE8021Q_TT_CA] = 0,
> + [IEEE8021Q_TT_VI] = 1, [IEEE8021Q_TT_VO] = 1,
> + [IEEE8021Q_TT_IC] = 1, [IEEE8021Q_TT_NC] = 1,
> +};
> +
> +static const u8 ieee8021q_1queue_tt_tc_map[] = {
> + [IEEE8021Q_TT_BK] = 0, [IEEE8021Q_TT_BE] = 0,
> + [IEEE8021Q_TT_EE] = 0, [IEEE8021Q_TT_CA] = 0,
> + [IEEE8021Q_TT_VI] = 0, [IEEE8021Q_TT_VO] = 0,
> + [IEEE8021Q_TT_IC] = 0, [IEEE8021Q_TT_NC] = 0,
> +};
> +
> +/**
> + * ieee8021q_tt_to_tc - Map IEEE 802.1Q Traffic Type to Traffic Class
> + * @tt: IEEE 802.1Q Traffic Type
> + * @num_queues: Number of queues
> + *
> + * This function maps an IEEE 802.1Q Traffic Type to a Traffic Class (TC) based
> + * on the number of queues configured on the switch. The mapping is based on the
s/switch/NIC/, ideally it should be useful beyond switches :)
> + * example provided by IEEE 802.1Q-2022 in Annex I "I.3 Traffic type to traffic
> + * class mapping" and Table I-1 "Traffic type to traffic class mapping".
> + *
> + * Return: Traffic Class corresponding to the given Traffic Type or -EINVAL if
> + * the number of queues is not supported. -EINVAL is used to differentiate from
Needs to also describe the other error case, or be less specific.
> + * -EOPNOTSUPP which is used to indicate that this library function is not
> + * compiled in.
> + */
> +int ieee8021q_tt_to_tc(unsigned int tt, unsigned int num_queues)
Can the enum ieee8021q_traffic_type be used instead of unsigned int?
> +{
> + if (tt >= IEEE8021Q_TT_MAX) {
> + pr_err("Requested Traffic Type (%d) is out of range (%d)\n", tt,
> + IEEE8021Q_TT_MAX);
> + return -EINVAL;
> + }
> +
> + switch (num_queues) {
> + case 8:
> + compiletime_assert(ARRAY_SIZE(ieee8021q_8queue_tt_tc_map) !=
> + IEEE8021Q_TT_MAX - 1,
> + "ieee8021q_8queue_tt_tc_map != max - 1");
> + return ieee8021q_8queue_tt_tc_map[tt];
> + case 7:
> + compiletime_assert(ARRAY_SIZE(ieee8021q_7queue_tt_tc_map) !=
> + IEEE8021Q_TT_MAX - 1,
> + "ieee8021q_7queue_tt_tc_map != max - 1");
> +
> + return ieee8021q_7queue_tt_tc_map[tt];
> + case 6:
> + compiletime_assert(ARRAY_SIZE(ieee8021q_6queue_tt_tc_map) !=
> + IEEE8021Q_TT_MAX - 1,
> + "ieee8021q_6queue_tt_tc_map != max - 1");
> +
> + return ieee8021q_6queue_tt_tc_map[tt];
> + case 5:
> + compiletime_assert(ARRAY_SIZE(ieee8021q_5queue_tt_tc_map) !=
> + IEEE8021Q_TT_MAX - 1,
> + "ieee8021q_5queue_tt_tc_map != max - 1");
> +
> + return ieee8021q_5queue_tt_tc_map[tt];
> + case 4:
> + compiletime_assert(ARRAY_SIZE(ieee8021q_4queue_tt_tc_map) !=
> + IEEE8021Q_TT_MAX - 1,
> + "ieee8021q_4queue_tt_tc_map != max - 1");
> +
> + return ieee8021q_4queue_tt_tc_map[tt];
> + case 3:
> + compiletime_assert(ARRAY_SIZE(ieee8021q_3queue_tt_tc_map) !=
> + IEEE8021Q_TT_MAX - 1,
> + "ieee8021q_3queue_tt_tc_map != max - 1");
> +
> + return ieee8021q_3queue_tt_tc_map[tt];
> + case 2:
> + compiletime_assert(ARRAY_SIZE(ieee8021q_2queue_tt_tc_map) !=
> + IEEE8021Q_TT_MAX - 1,
> + "ieee8021q_2queue_tt_tc_map != max - 1");
> +
> + return ieee8021q_2queue_tt_tc_map[tt];
> + case 1:
> + compiletime_assert(ARRAY_SIZE(ieee8021q_1queue_tt_tc_map) !=
> + IEEE8021Q_TT_MAX - 1,
> + "ieee8021q_1queue_tt_tc_map != max - 1");
> +
> + return ieee8021q_1queue_tt_tc_map[tt];
> + }
> +
> + pr_err("Invalid number of queues %d\n", num_queues);
> +
> + return -EINVAL;
> +}
> +EXPORT_SYMBOL_GPL(ieee8021q_tt_to_tc);
> +
> +/**
> + * ietf_dscp_to_ieee8021q_tt - Map IETF DSCP to IEEE 802.1Q Traffic Type
> + * @dscp: IETF DSCP value
> + *
> + * This function maps an IETF DSCP value to an IEEE 802.1Q Traffic Type (TT).
> + * Since there is no corresponding mapping between DSCP and IEEE 802.1Q Traffic
> + * Type, this function is inspired by the RFC8325 documentation which describe
> + * the mapping between DSCP and 802.11 User Priority (UP) values.
> + *
> + * Return: IEEE 802.1Q Traffic Type corresponding to the given DSCP value
> + */
> +int ietf_dscp_to_ieee8021q_tt(u8 dscp)
> +{
> + switch (dscp) {
> + case DSCP_CS0:
> + case DSCP_AF11:
> + case DSCP_AF12:
> + case DSCP_AF13:
Is it correct for AF11, AF12, AF13 to be classified together with CS0
rather than with CS1? It looks strange when their upper 3 bits are the
same as CS1.
> + return IEEE8021Q_TT_BE;
> + case DSCP_CS1:
> + return IEEE8021Q_TT_BK;
> + case DSCP_CS2:
> + case DSCP_AF21:
> + case DSCP_AF22:
> + case DSCP_AF23:
> + return IEEE8021Q_TT_EE;
> + case DSCP_CS3:
> + case DSCP_AF31:
> + case DSCP_AF32:
> + case DSCP_AF33:
> + return IEEE8021Q_TT_CA;
> + case DSCP_CS4:
> + case DSCP_AF41:
> + case DSCP_AF42:
> + case DSCP_AF43:
> + return IEEE8021Q_TT_VI;
> + case DSCP_CS5:
> + case DSCP_EF:
> + case DSCP_VOICE_ADMIT:
> + return IEEE8021Q_TT_VO;
> + case DSCP_CS6:
> + return IEEE8021Q_TT_IC;
> + case DSCP_CS7:
> + return IEEE8021Q_TT_NC;
> + }
> +
> + return SIMPLE_IETF_DSCP_TO_IEEE8021Q_TT(dscp);
> +}
> +EXPORT_SYMBOL_GPL(ietf_dscp_to_ieee8021q_tt);
> --
> 2.39.2
>