SoC-e switches exchange frames with the conduit using an 8-byte SDSA header inserted between the source MAC address and the original EtherType. The header identifies itself with EtherType 0xdcdc and carries the frame direction and a 10-bit source or destination port. Add support for both frame types FROM_CPU and TO_CPU. SDSA VLAN metadata generation could not be tested on the available switch configuration yet. Leave these fields clear on transmit and reject receive headers which mark the metadata as valid. Signed-off-by: Vasilij Strassheim --- include/net/dsa.h | 2 + net/dsa/Kconfig | 6 +++ net/dsa/Makefile | 1 + net/dsa/tag_sdsa.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+) diff --git a/include/net/dsa.h b/include/net/dsa.h index 7507d632e7c6..cabe9f94788a 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -61,6 +61,7 @@ struct tc_action; #define DSA_TAG_PROTO_NETC_VALUE 33 #define DSA_TAG_PROTO_KSZ8463_VALUE 34 #define DSA_TAG_PROTO_MT7628_VALUE 35 +#define DSA_TAG_PROTO_SDSA_VALUE 36 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, @@ -99,6 +100,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE, DSA_TAG_PROTO_KSZ8463 = DSA_TAG_PROTO_KSZ8463_VALUE, DSA_TAG_PROTO_MT7628 = DSA_TAG_PROTO_MT7628_VALUE, + DSA_TAG_PROTO_SDSA = DSA_TAG_PROTO_SDSA_VALUE, }; struct dsa_switch; diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 23b4b74004ed..0032ccd3badf 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -188,6 +188,12 @@ config NET_DSA_TAG_RZN1_A5PSW Renesas RZ/N1 embedded switch that uses an 8 byte tag located after destination MAC address. +config NET_DSA_TAG_SDSA + tristate "Tag driver for SoC-e switches using EtherType SDSA headers" + help + Say Y or M if you want to enable support for tagging frames for the + SoC-e switches. + config NET_DSA_TAG_LAN9303 tristate "Tag driver for SMSC/Microchip LAN9303 family of switches" help diff --git a/net/dsa/Makefile b/net/dsa/Makefile index d15bcf5c68f0..2f31ed0e4626 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -39,6 +39,7 @@ 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 obj-$(CONFIG_NET_DSA_TAG_RZN1_A5PSW) += tag_rzn1_a5psw.o +obj-$(CONFIG_NET_DSA_TAG_SDSA) += tag_sdsa.o obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o obj-$(CONFIG_NET_DSA_TAG_VSC73XX_8021Q) += tag_vsc73xx_8021q.o diff --git a/net/dsa/tag_sdsa.c b/net/dsa/tag_sdsa.c new file mode 100644 index 000000000000..83bccd65b1b1 --- /dev/null +++ b/net/dsa/tag_sdsa.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020-2026 System on Chip engineering, S.L. + * Copyright (c) 2026 Linutronix GmbH + * Author: Vasilij Strassheim + */ + +#include +#include +#include + +#include "tag.h" + +#define SDSA_HLEN 8 + +#define SDSA_NAME "sdsa" +#define ETH_P_SDSA 0xDCDC + +/* SDSA tag byte layout (after the 12-byte MAC header): + * Bytes 0-1: SDSA EtherType (0xDCDC) + * Bytes 2-3: Reserved + * Byte 4: Frame type (bits 7-6), VLAN-info bit (bit 5), port[9:5] (bits 4-0) + * Byte 5: Port[4:0] (bits 7-3) + * Bytes 6-7: PCP (bits 7-5) / CFI (bit 4) / VID (bits 3-0 + byte 7), only + * meaningful when the VLAN-info bit is set. + */ +#define SDSA_TAG_FRAME_TYPE_MASK GENMASK(7, 6) +#define SDSA_TAG_VLAN_BIT BIT(5) +#define SDSA_TAG_PORT_HI_MASK GENMASK(4, 0) +#define SDSA_TAG_PORT_LO_MASK GENMASK(7, 3) +#define SDSA_TAG_PORT_HI_SHIFT 5 +#define SDSA_FRAME_TYPE_TO_CPU 0 +#define SDSA_FRAME_TYPE_FROM_CPU 1 + +struct sdsa_tag { + __be16 ethertype; + __be16 reserved; + u8 frame_type_port_hi; + u8 port_lo; + __be16 vlan; +}; + +static struct sk_buff *sdsa_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct dsa_port *dp = dsa_user_to_port(dev); + struct sdsa_tag *tag; + + BUILD_BUG_ON(sizeof(*tag) != SDSA_HLEN); + + skb_push(skb, SDSA_HLEN); + dsa_alloc_etype_header(skb, SDSA_HLEN); + + /* Construct the FROM_CPU DSA tag. */ + tag = dsa_etype_header_pos_tx(skb); + tag->ethertype = cpu_to_be16(ETH_P_SDSA); + tag->reserved = 0; + tag->frame_type_port_hi = + FIELD_PREP(SDSA_TAG_FRAME_TYPE_MASK, SDSA_FRAME_TYPE_FROM_CPU) | + FIELD_PREP(SDSA_TAG_PORT_HI_MASK, + dp->index >> SDSA_TAG_PORT_HI_SHIFT); + tag->port_lo = FIELD_PREP(SDSA_TAG_PORT_LO_MASK, dp->index); + tag->vlan = 0; + + return skb; +} + +static struct sk_buff *sdsa_rcv(struct sk_buff *skb, struct net_device *dev) +{ + struct sdsa_tag *tag; + int source_port; + u8 frame_type; + + if (unlikely(!pskb_may_pull(skb, SDSA_HLEN))) + goto out_drop; + + tag = dsa_etype_header_pos_rx(skb); + if (unlikely(tag->ethertype != cpu_to_be16(ETH_P_SDSA))) + goto out_drop; + + /* Check that the frame type is TO_CPU. */ + frame_type = FIELD_GET(SDSA_TAG_FRAME_TYPE_MASK, + tag->frame_type_port_hi); + if (frame_type != SDSA_FRAME_TYPE_TO_CPU) + goto out_drop; + + /* SDSA VLAN information is not supported. */ + if (tag->frame_type_port_hi & SDSA_TAG_VLAN_BIT) + goto out_drop; + + /* Determine the source port from the two port fields. */ + source_port = FIELD_GET(SDSA_TAG_PORT_HI_MASK, + tag->frame_type_port_hi) << + SDSA_TAG_PORT_HI_SHIFT; + source_port |= FIELD_GET(SDSA_TAG_PORT_LO_MASK, tag->port_lo); + + skb->dev = dsa_conduit_find_user(dev, 0, source_port); + if (!skb->dev) + goto out_drop; + + skb_pull_rcsum(skb, SDSA_HLEN); + dsa_strip_etype_header(skb, SDSA_HLEN); + + dsa_default_offload_fwd_mark(skb); + return skb; + +out_drop: + kfree_skb(skb); + return NULL; +} + +static const struct dsa_device_ops sdsa_netdev_ops = { + .name = SDSA_NAME, + .proto = DSA_TAG_PROTO_SDSA, + .xmit = sdsa_xmit, + .rcv = sdsa_rcv, + .needed_headroom = SDSA_HLEN, +}; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DSA tag driver for SoC-e SDSA protocol"); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SDSA, SDSA_NAME); + +module_dsa_tag_driver(sdsa_netdev_ops); -- 2.39.5