[PATCH net-next v5 5/9] net: dsa: tag_ksz: Share code for KSZ8795 and KSZ9893 xmit operations

From: Bastien Curutchet (Schneider Electric)

Date: Thu Feb 26 2026 - 12:07:27 EST


KSZ8795 and KSZ9893 have very similar tag handling in the xmit path,
leading to code duplication.

There are only two differences between the two ksz*_xmit():
- the KSZ8795 doesn't handle priorities between frames
- ksz8795_xmit() directly returns the SKB instead of calling
ksz_defer_xmit(). Yet, ksz_defer_xmit() also returns directly the SKB
if no clone is present inside the SKB. Clones are only created by the KSZ
driver when the PTP feature is enabled. Since KSZ8795 doesn't support
PTP, returning the SKB directly or ksz_defer_xmit() is the same.

The upcoming support for the KSZ8463 also requires a similar xmit().

Move KSZ8795 operations below the definition of ksz_defer_xmit().

Gather the common code from ksz8795_xmit() and ksz9893_xmit() into a new
ksz_common_xmit() function that takes three input arguments:
- do_tstamp to tell whether ksz_xmit_timestamp() should be called
- prio to give the priority tag (if any)
- override_mask to give the location of the override bit (if any)

Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@xxxxxxxxxxx>
---
net/dsa/tag_ksz.c | 161 +++++++++++++++++++++++++++---------------------------
1 file changed, 80 insertions(+), 81 deletions(-)

diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index d2475c3bbb7d227bb42b0368914275b00fb2784a..6ec9bcb324a953d988ef665a177566159e27027f 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -99,68 +99,6 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb,
return skb;
}

-/*
- * For Ingress (Host -> KSZ8795), 1 byte is added before FCS.
- * ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag(1byte)|FCS(4bytes)
- * ---------------------------------------------------------------------------
- * tag : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
- *
- * For Egress (KSZ8795 -> Host), 1 byte is added before FCS.
- * ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
- * ---------------------------------------------------------------------------
- * tag0 : zero-based value represents port
- * (eg, 0x0=port1, 0x2=port3, 0x3=port4)
- */
-
-#define KSZ8795_TAIL_TAG_EG_PORT_M GENMASK(1, 0)
-#define KSZ8795_TAIL_TAG_OVERRIDE BIT(6)
-#define KSZ8795_TAIL_TAG_LOOKUP BIT(7)
-
-static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct ethhdr *hdr;
- u8 *tag;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb))
- return NULL;
-
- /* Tag encoding */
- tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
- hdr = skb_eth_hdr(skb);
-
- *tag = dsa_xmit_port_mask(skb, dev);
- if (is_link_local_ether_addr(hdr->h_dest))
- *tag |= KSZ8795_TAIL_TAG_OVERRIDE;
-
- return skb;
-}
-
-static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
-{
- u8 *tag;
-
- if (skb_linearize(skb))
- return NULL;
-
- tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
-
- return ksz_common_rcv(skb, dev, tag[0] & KSZ8795_TAIL_TAG_EG_PORT_M,
- KSZ_EGRESS_TAG_LEN);
-}
-
-static const struct dsa_device_ops ksz8795_netdev_ops = {
- .name = KSZ8795_NAME,
- .proto = DSA_TAG_PROTO_KSZ8795,
- .xmit = ksz8795_xmit,
- .rcv = ksz8795_rcv,
- .needed_tailroom = KSZ_INGRESS_TAG_LEN,
-};
-
-DSA_TAG_DRIVER(ksz8795_netdev_ops);
-MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
-
/*
* For Ingress (Host -> KSZ9477), 2/6 bytes are added before FCS.
* ---------------------------------------------------------------------------
@@ -274,6 +212,35 @@ static struct sk_buff *ksz_defer_xmit(struct dsa_port *dp, struct sk_buff *skb)
return NULL;
}

+static struct sk_buff *ksz_common_xmit(struct sk_buff *skb,
+ struct net_device *dev,
+ bool do_tstamp,
+ u8 prio,
+ u8 override_mask)
+{
+ struct dsa_port *dp = dsa_user_to_port(dev);
+ struct ethhdr *hdr;
+ u8 *tag;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb))
+ return NULL;
+
+ /* Tag encoding */
+ if (do_tstamp)
+ ksz_xmit_timestamp(dp, skb);
+
+ tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
+ hdr = skb_eth_hdr(skb);
+
+ *tag = dsa_xmit_port_mask(skb, dev);
+ *tag |= prio;
+
+ if (is_link_local_ether_addr(hdr->h_dest))
+ *tag |= override_mask;
+
+ return ksz_defer_xmit(dp, skb);
+}
+
static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@@ -339,35 +306,67 @@ static const struct dsa_device_ops ksz9477_netdev_ops = {
DSA_TAG_DRIVER(ksz9477_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9477, KSZ9477_NAME);

-#define KSZ9893_TAIL_TAG_PRIO GENMASK(4, 3)
-#define KSZ9893_TAIL_TAG_OVERRIDE BIT(5)
-#define KSZ9893_TAIL_TAG_LOOKUP BIT(6)
+/*
+ * For Ingress (Host -> KSZ8795), 1 byte is added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
+ *
+ * For Egress (KSZ8795 -> Host), 1 byte is added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag0 : zero-based value represents port
+ * (eg, 0x0=port1, 0x2=port3, 0x3=port4)
+ */

-static struct sk_buff *ksz9893_xmit(struct sk_buff *skb,
- struct net_device *dev)
+#define KSZ8795_TAIL_TAG_EG_PORT_M GENMASK(1, 0)
+#define KSZ8795_TAIL_TAG_OVERRIDE BIT(6)
+#define KSZ8795_TAIL_TAG_LOOKUP BIT(7)
+
+static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ return ksz_common_xmit(skb, dev, false, 0, KSZ8795_TAIL_TAG_OVERRIDE);
+}
+
+static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
{
- u16 queue_mapping = skb_get_queue_mapping(skb);
- u8 prio = netdev_txq_to_tc(dev, queue_mapping);
- struct dsa_port *dp = dsa_user_to_port(dev);
- struct ethhdr *hdr;
u8 *tag;

- if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb))
+ if (skb_linearize(skb))
return NULL;

- /* Tag encoding */
- ksz_xmit_timestamp(dp, skb);
+ tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;

- tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
- hdr = skb_eth_hdr(skb);
+ return ksz_common_rcv(skb, dev, tag[0] & KSZ8795_TAIL_TAG_EG_PORT_M,
+ KSZ_EGRESS_TAG_LEN);
+}

- *tag = dsa_xmit_port_mask(skb, dev);
- *tag |= FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio);
+static const struct dsa_device_ops ksz8795_netdev_ops = {
+ .name = KSZ8795_NAME,
+ .proto = DSA_TAG_PROTO_KSZ8795,
+ .xmit = ksz8795_xmit,
+ .rcv = ksz8795_rcv,
+ .needed_tailroom = KSZ_INGRESS_TAG_LEN,
+};

- if (is_link_local_ether_addr(hdr->h_dest))
- *tag |= KSZ9893_TAIL_TAG_OVERRIDE;
+DSA_TAG_DRIVER(ksz8795_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);

- return ksz_defer_xmit(dp, skb);
+#define KSZ9893_TAIL_TAG_PRIO GENMASK(4, 3)
+#define KSZ9893_TAIL_TAG_OVERRIDE BIT(5)
+#define KSZ9893_TAIL_TAG_LOOKUP BIT(6)
+
+static struct sk_buff *ksz9893_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ u16 queue_mapping = skb_get_queue_mapping(skb);
+ u8 prio = netdev_txq_to_tc(dev, queue_mapping);
+
+ return ksz_common_xmit(skb, dev, true,
+ FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio),
+ KSZ9893_TAIL_TAG_OVERRIDE);
}

static const struct dsa_device_ops ksz9893_netdev_ops = {

--
2.53.0