diff mbox series

[net-next,v6,3/9] net: add IEEE 802.1q specific helpers

Message ID 20240410080556.1241048-4-o.rempel@pengutronix.de (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series Enhanced DCB and DSCP Support for KSZ Switches | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 948 this patch: 948
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 5 of 5 maintainers
netdev/build_clang success Errors and warnings before: 954 this patch: 954
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 960 this patch: 960
netdev/checkpatch warning CHECK: Macro argument 'dscp' may be better as '(dscp)' to avoid precedence issues WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 82 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc fail Errors and warnings before: 0 this patch: 1
netdev/source_inline success Was 0 now: 0

Commit Message

Oleksij Rempel April 10, 2024, 8:05 a.m. UTC
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 <o.rempel@pengutronix.de>
---
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

Comments

Simon Horman April 11, 2024, 11:46 a.m. UTC | #1
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 <o.rempel@pengutronix.de>

...

> 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 <kernel@pengutronix.de> */
> +
> +#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,
> +};

...
Vladimir Oltean April 11, 2024, 11:51 a.m. UTC | #2
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 <o.rempel@pengutronix.de>
> ---
> 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 <kernel@pengutronix.de> */
> +
> +#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 <kernel@pengutronix.de>
> +
> +#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
>
diff mbox series

Patch

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 <kernel@pengutronix.de> */
+
+#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 <kernel@pengutronix.de> */
+
+#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 <kernel@pengutronix.de>
+
+#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);