ramips: ethernet: ralink: fix offload with diff dsa tag

DSA copies the driver features to slave device, including offload
capabilites. Once a packet is sent though a DSA slave interface,
according to its features, the kernel does not calculate checksums,
expecting that the HW will fill the gaps. DSA adds the defined DSA
tag and sends the tagged packet through the master device.

Ethertype DSA tags expect the driver to calculate checksum based on the
csum_start/csum_offset. However, mtk_eth_soc does not use that info.
mtk_eth_soc checks the network header and decides if the HW can manage
that packet, unaware that mac layer now contains an extra DSA tag. When
that tag is the Mediatek CPU tag, offload will work as expected.
When it is an incompatible DSA tag (all but Mediatek) or if DSA is
stacking two incompatible DSA tags, the driver will still count on the
HW offload. In this case, packets go to the network with an incorrect
checksum.

This patch adds an extra check and disables offloading when DSA tag is
in use and it is not Mediatek. Offloading is also disabled even when DSA
tag is Mediatek but there is something else increasing the mac layer
region (possibly another stacked tag). Mediatek tag can encode 802.1Q info and
using vlan tags will still use offloading. Other mac layer protocols
that are not supported by Mediatek will disable HW offloading. PPPoE was
not tested but it is expected that it will disable offloading.

This code is heuristically trying to keep HW offload enable with DSA tags.
Anyway, in the worse case, offloading is disabled and there is an increase
in overhead, which is better than packet with wrong checksum.

Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23933
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
Luiz Angelo Daros de Luca
2026-06-29 14:54:58 +02:00
committed by Robert Marko
parent 84aafc08d7
commit 3c0a73b4d2
@@ -41,6 +41,10 @@
#include "mdio.h"
#include "ethtool.h"
#if IS_ENABLED(CONFIG_NET_DSA)
#include <net/dsa.h>
#endif
#if defined(CONFIG_SOC_MT7620)
#define DMA_FWD_REG MT7620A_GDMA1_FWD_CFG
#define MAX_RX_LENGTH 2048
@@ -786,6 +790,41 @@ err_out:
return -1;
}
#if IS_ENABLED(CONFIG_NET_DSA)
#define MTK_HDR_LEN 4
static netdev_features_t fe_features_check(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features)
{
/* No point in doing any of this if neither checksum nor GSO are
* being requested for this frame. We can rule out both by just
* checking for CHECKSUM_PARTIAL
*/
if (skb->ip_summed != CHECKSUM_PARTIAL)
return features;
/* DSA tag might break existing offload checks as offload feature flags
* are copied to slave ports and this driver does not use csum_start. */
if (netdev_uses_dsa(dev)) {
const struct dsa_device_ops *tag_ops = dev->dsa_ptr->tag_ops;
/* If tag is Mediatek, checksum should work */
if (tag_ops->proto == DSA_TAG_PROTO_MTK)
/* However, make sure that it is not stacking another
* L2 protocol, possibly a second incompatible DSA tag
* 802.1Q does not increase the mac header size because
* it is embedded inside mediatek tag */
if (skb_mac_header_len(skb) <= ETH_HLEN + MTK_HDR_LEN)
return features;
features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
}
return features;
}
#endif
static inline int fe_skb_padto(struct sk_buff *skb, struct fe_priv *priv)
{
unsigned int len;
@@ -1491,6 +1530,9 @@ static const struct net_device_ops fe_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = fe_poll_controller,
#endif
#if IS_ENABLED(CONFIG_NET_DSA)
.ndo_features_check = fe_features_check,
#endif
};
static void fe_reset_pending(struct fe_priv *priv)