From 75801c22f53136462cdc687b34d655bf9dc8445f Mon Sep 17 00:00:00 2001 From: Mihai Ordean Date: Sat, 1 Aug 2026 00:00:00 +0000 Subject: [PATCH] net: ethernet: mtk_eth_soc: allow TX checksum offload with a tag_8021q DSA tagger mtk_fix_features() clears NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM on any conduit whose DSA tag protocol is not DSA_TAG_PROTO_MTK. The test is a proxy for "framing the GDM transmit checksum engine cannot parse": the engine is programmed with a single descriptor bit and no header offsets, so it has to find the L3 and L4 headers itself, and a tagger that pushes an opaque header in front of them hides them from it. The proxy is too coarse. DSA_TAG_PROTO_MXL862_8021Q pushes nothing opaque - mxl862_8021q_xmit() calls dsa_8021q_xmit(), which inserts a real 802.1Q header with vlan_insert_tag(), and declares needed_headroom = VLAN_HLEN. What reaches the GDM is an ordinary VLAN tagged Ethernet frame, and the parser handles those already: the same hardware inserts a VLAN tag on its own via TX_DMA_INS_VLAN and TX_DMA_INS_VLAN_V2, which it could not place correctly if it could not locate the tag. On a BPI-R4 Pro the effect is large. The MxL86252C conduit is the only egress path for the 10G port, so every TCP segment leaving that port is checksummed by the CPU. Single stream iperf3 over a direct 10G link between two MT7988A boards: R4 Pro -> R4, checksum denied by this test 3.16 Gbit/s R4 -> R4 Pro, checksum offloaded 4.64 Gbit/s R4 -> R4 Pro, checksum disabled by hand 3.09 Gbit/s R4 Pro -> R4, with this patch 4.65 Gbit/s The third row is the control: with the offload removed from the direction that had it, both directions agree to within 2%, so the whole difference is this test and not the extra DSA hop. The fourth is the result. Correctness was checked on the receiver over 5.42 GB, roughly 3.9M segments carrying a hardware computed checksum behind an inserted 802.1Q tag: zero TCP retransmissions and TcpInCsumErrors unchanged at zero. A misparsed header offset would produce a wrong checksum, which the receiver discards and the sender retransmits, so neither counter could have stayed at zero had the engine failed to skip the tag. Replace non_mtk_uses_dsa(), which had no other callers, with a predicate named for the property actually being tested, and let a tag_8021q based tagger keep the offload. Signed-off-by: Mihai Ordean --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 30 +++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index d1bcc8afefa7..cef807b304a1 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -1627,11 +1627,31 @@ static bool mtk_uses_dsa(struct net_device *dev) #endif } -static bool non_mtk_uses_dsa(struct net_device *dev) +/* The GDM transmit checksum engine is programmed with a single descriptor bit + * (TX_DMA_CHKSUM / TX_DMA_CHKSUM_V2) and no header offsets, so it locates the + * L3 and L4 headers by parsing the frame itself. A DSA tagger that prepends + * an opaque header hides those headers from the parser, and checksum offload + * has to be refused on a conduit using such a tag. + * + * A tag_8021q based tagger hides nothing: dsa_8021q_xmit() inserts a real + * 802.1Q header with vlan_insert_tag(), so what reaches the GDM is an ordinary + * VLAN tagged Ethernet frame. The parser handles those - the same hardware + * inserts VLAN tags itself via TX_DMA_INS_VLAN(_V2), which it could not place + * correctly against a parser unable to find the tag. + */ +static bool mtk_dsa_tag_hides_l3_hdr(struct net_device *dev) { #if IS_ENABLED(CONFIG_NET_DSA) - return netdev_uses_dsa(dev) && - dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK; + if (!netdev_uses_dsa(dev)) + return false; + + switch (dev->dsa_ptr->tag_ops->proto) { + case DSA_TAG_PROTO_MTK: + case DSA_TAG_PROTO_MXL862_8021Q: + return false; + default: + return true; + } #else return false; #endif @@ -3582,11 +3602,11 @@ static netdev_features_t mtk_fix_features(struct net_device *dev, } if ((features & NETIF_F_IP_CSUM) && - non_mtk_uses_dsa(dev)) + mtk_dsa_tag_hides_l3_hdr(dev)) features &= ~NETIF_F_IP_CSUM; if ((features & NETIF_F_IPV6_CSUM) && - non_mtk_uses_dsa(dev)) + mtk_dsa_tag_hides_l3_hdr(dev)) features &= ~NETIF_F_IPV6_CSUM; return features;