--- /dev/null
+From a32e16b3c2fc1954ad6e09737439f60e5890278e Mon Sep 17 00:00:00 2001
+From: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Date: Fri, 4 Nov 2022 18:41:49 +0100
+Subject: [PATCH] net: dsa: add out-of-band tagging protocol
+
+This tagging protocol is designed for the situation where the link
+between the MAC and the Switch is designed such that the Destination
+Port, which is usually embedded in some part of the Ethernet Header, is
+sent out-of-band, and isn't present at all in the Ethernet frame.
+
+This can happen when the MAC and Switch are tightly integrated on an
+SoC, as is the case with the Qualcomm IPQ4019 for example, where the DSA
+tag is inserted directly into the DMA descriptors. In that case,
+the MAC driver is responsible for sending the tag to the switch using
+the out-of-band medium. To do so, the MAC driver needs to have the
+information of the destination port for that skb.
+
+Add a new tagging protocol based on SKB extensions to convey the
+information about the destination port to the MAC driver
+
+Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
+---
+ Documentation/networking/dsa/dsa.rst | 13 +++++++-
+ MAINTAINERS | 1 +
+ include/linux/dsa/oob.h | 16 +++++++++
+ include/linux/skbuff.h | 3 ++
+ include/net/dsa.h | 2 ++
+ net/core/skbuff.c | 10 ++++++
+ net/dsa/Kconfig | 9 +++++
+ net/dsa/Makefile | 1 +
+ net/dsa/tag_oob.c | 49 ++++++++++++++++++++++++++++
+ 9 files changed, 103 insertions(+), 1 deletion(-)
+ create mode 100644 include/linux/dsa/oob.h
+ create mode 100644 net/dsa/tag_oob.c
+
+--- a/Documentation/networking/dsa/dsa.rst
++++ b/Documentation/networking/dsa/dsa.rst
+@@ -70,7 +70,8 @@ Switch tagging protocols
+ ------------------------
+
+ DSA supports many vendor-specific tagging protocols, one software-defined
+-tagging protocol, and a tag-less mode as well (``DSA_TAG_PROTO_NONE``).
++tagging protocol, a tag-less mode as well (``DSA_TAG_PROTO_NONE``) and an
++out-of-band tagging protocol (``DSA_TAG_PROTO_OOB``).
+
+ The exact format of the tag protocol is vendor specific, but in general, they
+ all contain something which:
+@@ -221,6 +222,16 @@ receive all frames regardless of the val
+ setting the ``promisc_on_conduit`` property of the ``struct dsa_device_ops``.
+ Note that this assumes a DSA-unaware conduit driver, which is the norm.
+
++Some SoCs have a tight integration between the conduit network interface and the
++embedded switch, such that the DSA tag isn't transmitted in the packet data,
++but through another media, using so-called out-of-band tagging. In that case,
++the host MAC driver is in charge of transmitting the tag to the switch.
++An example is the IPQ4019 SoC, that transmits the tag between the ipqess
++ethernet controller and the qca8k switch using the DMA descriptor. In that
++configuration, tag-chaining is permitted, but the OOB tag will always be the
++top-most switch in the tree. The tagger (``DSA_TAG_PROTO_OOB``) uses skb
++extensions to transmit the tag to and from the MAC driver.
++
+ Conduit network devices
+ -----------------------
+
+--- /dev/null
++++ b/include/linux/dsa/oob.h
+@@ -0,0 +1,16 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (C) 2022 Maxime Chevallier <maxime.chevallier@bootlin.com>
++ */
++
++#ifndef _NET_DSA_OOB_H
++#define _NET_DSA_OOB_H
++
++#include <linux/skbuff.h>
++
++struct dsa_oob_tag_info {
++ u16 port;
++};
++
++int dsa_oob_tag_push(struct sk_buff *skb, struct dsa_oob_tag_info *ti);
++int dsa_oob_tag_pop(struct sk_buff *skb, struct dsa_oob_tag_info *ti);
++#endif
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -4852,6 +4852,9 @@ enum skb_ext_id {
+ #if IS_ENABLED(CONFIG_MCTP_FLOWS)
+ SKB_EXT_MCTP,
+ #endif
++#if IS_ENABLED(CONFIG_NET_DSA_TAG_OOB)
++ SKB_EXT_DSA_OOB,
++#endif
+ SKB_EXT_NUM, /* must be last */
+ };
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -57,6 +57,7 @@ struct tc_action;
+ #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29
+ #define DSA_TAG_PROTO_MXL862_VALUE 30
+ #define DSA_TAG_PROTO_MXL862_8021Q_VALUE 31
++#define DSA_TAG_PROTO_OOB_VALUE 32
+
+ enum dsa_tag_protocol {
+ DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
+@@ -91,6 +92,7 @@ enum dsa_tag_protocol {
+ DSA_TAG_PROTO_VSC73XX_8021Q = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE,
+ DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
+ DSA_TAG_PROTO_MXL862_8021Q = DSA_TAG_PROTO_MXL862_8021Q_VALUE,
++ DSA_TAG_PROTO_OOB = DSA_TAG_PROTO_OOB_VALUE,
+ };
+
+ struct dsa_switch;
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -65,8 +65,12 @@
+ #include <linux/kcov.h>
+ #include <linux/iov_iter.h>
+ #include <linux/if.h>
++#ifdef CONFIG_NET_DSA_TAG_OOB
++#include <linux/dsa/oob.h>
++#endif
+
+ #include <net/protocol.h>
++#include <net/dsa.h>
+ #include <net/dst.h>
+ #include <net/sock.h>
+ #include <net/checksum.h>
+@@ -5097,6 +5101,9 @@ static const u8 skb_ext_type_len[] = {
+ #if IS_ENABLED(CONFIG_MCTP_FLOWS)
+ [SKB_EXT_MCTP] = SKB_EXT_CHUNKSIZEOF(struct mctp_flow),
+ #endif
++#if IS_ENABLED(CONFIG_NET_DSA_TAG_OOB)
++ [SKB_EXT_DSA_OOB] = SKB_EXT_CHUNKSIZEOF(struct dsa_oob_tag_info),
++#endif
+ };
+
+ static __always_inline unsigned int skb_ext_total_length(void)
+--- a/net/dsa/Kconfig
++++ b/net/dsa/Kconfig
+@@ -145,6 +145,15 @@ config NET_DSA_TAG_OCELOT_8021Q
+ this mode, less TCAM resources (VCAP IS1, IS2, ES0) are available for
+ use with tc-flower.
+
++config NET_DSA_TAG_OOB
++ select SKB_EXTENSIONS
++ tristate "Tag driver for Out-of-band tagging drivers"
++ help
++ Say Y or M if you want to enable support for pairs of embedded
++ switches and host MAC drivers which perform demultiplexing and
++ packet steering to ports using out of band metadata processed
++ by the DSA master, rather than tags present in the packets.
++
+ config NET_DSA_TAG_QCA
+ tristate "Tag driver for Qualcomm Atheros QCA8K switches"
+ help
+--- a/net/dsa/Makefile
++++ b/net/dsa/Makefile
+@@ -33,6 +33,7 @@ obj-$(CONFIG_NET_DSA_TAG_MXL_862XX_8021Q
+ obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o
+ obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
+ obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o
++obj-$(CONFIG_NET_DSA_TAG_OOB) += tag_oob.o
+ obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
+ obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o
+ obj-$(CONFIG_NET_DSA_TAG_RTL8_4) += tag_rtl8_4.o
+--- /dev/null
++++ b/net/dsa/tag_oob.c
+@@ -0,0 +1,51 @@
++// SPDX-License-Identifier: GPL-2.0-only
++
++/* Copyright (c) 2022, Maxime Chevallier <maxime.chevallier@bootlin.com> */
++
++#include <linux/bitfield.h>
++#include <linux/dsa/oob.h>
++#include <linux/skbuff.h>
++
++#include "tag.h"
++
++#define OOB_NAME "oob"
++
++static struct sk_buff *oob_tag_xmit(struct sk_buff *skb,
++ struct net_device *dev)
++{
++ struct dsa_oob_tag_info *tag_info = skb_ext_add(skb, SKB_EXT_DSA_OOB);
++ struct dsa_port *dp = dsa_user_to_port(dev);
++
++ tag_info->port = dp->index;
++
++ return skb;
++}
++
++static struct sk_buff *oob_tag_rcv(struct sk_buff *skb,
++ struct net_device *dev)
++{
++ struct dsa_oob_tag_info *tag_info = skb_ext_find(skb, SKB_EXT_DSA_OOB);
++
++ if (!tag_info)
++ return NULL;
++
++ skb->dev = dsa_conduit_find_user(dev, 0, tag_info->port);
++ if (!skb->dev)
++ return NULL;
++
++ return skb;
++}
++
++static const struct dsa_device_ops oob_tag_dsa_ops = {
++ .name = OOB_NAME,
++ .proto = DSA_TAG_PROTO_OOB,
++ .xmit = oob_tag_xmit,
++ .rcv = oob_tag_rcv,
++};
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("DSA tag driver for out-of-band tagging");
++MODULE_AUTHOR("Maxime Chevallier <maxime.chevallier@bootlin.com>");
++MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OOB, OOB_NAME);
++
++module_dsa_tag_driver(oob_tag_dsa_ops);