]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: stmmac: fix TSO support when some channels have TBS available
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Wed, 1 Apr 2026 07:21:24 +0000 (08:21 +0100)
committerJakub Kicinski <kuba@kernel.org>
Thu, 2 Apr 2026 18:28:18 +0000 (11:28 -0700)
According to the STM32MP25xx manual, which is dwmac v5.3, TBS (time
based scheduling) is not permitted for channels which have hardware
TSO enabled. Intel's commit 5e6038b88a57 ("net: stmmac: fix TSO and
TBS feature enabling during driver open") concurs with this, but it
is incomplete.

This commit avoids enabling TSO support on the channels which have
TBS available, which, as far as the hardware is concerned, means we
do not set the TSE bit in the DMA channel's transmit control register.

However, the net device's features apply to all queues(channels), which
means these channels may still be handed TSO skbs to transmit, and the
driver will pass them to stmmac_tso_xmit(). This will generate the
descriptors for TSO, even though the channel has the TSE bit clear.

Fix this by checking whether the queue(channel) has TBS available,
and if it does, fall back to software GSO support.

Fixes: 5e6038b88a57 ("net: stmmac: fix TSO and TBS feature enabling during driver open")
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Link: https://patch.msgid.link/E1w7ptE-0000000Easz-28tv@rmk-PC.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

index f76ed64fb9e6ee2b1cf3ff5196cef818b048c755..729072622fcbce3c2de55c3d813993e0f82aaa2e 100644 (file)
@@ -3619,6 +3619,17 @@ static void stmmac_safety_feat_configuration(struct stmmac_priv *priv)
        }
 }
 
+/* STM32MP25xx (dwmac v5.3) states "Do not enable time-based scheduling for
+ * channels on which the TSO feature is enabled." If we have a skb for a
+ * channel which has TBS enabled, fall back to software GSO.
+ */
+static bool stmmac_tso_channel_permitted(struct stmmac_priv *priv,
+                                        unsigned int chan)
+{
+       /* TSO and TBS cannot co-exist */
+       return !(priv->dma_conf.tx_queue[chan].tbs & STMMAC_TBS_AVAIL);
+}
+
 /**
  * stmmac_hw_setup - setup mac in a usable state.
  *  @dev : pointer to the device structure.
@@ -3707,10 +3718,7 @@ static int stmmac_hw_setup(struct net_device *dev)
        /* Enable TSO */
        if (priv->dma_cap.tsoen && priv->plat->flags & STMMAC_FLAG_TSO_EN) {
                for (chan = 0; chan < tx_cnt; chan++) {
-                       struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
-
-                       /* TSO and TBS cannot co-exist */
-                       if (tx_q->tbs & STMMAC_TBS_AVAIL)
+                       if (!stmmac_tso_channel_permitted(priv, chan))
                                continue;
 
                        stmmac_enable_tso(priv, priv->ioaddr, 1, chan);
@@ -4919,6 +4927,21 @@ max_sdu_err:
        return NETDEV_TX_OK;
 }
 
+static netdev_features_t stmmac_features_check(struct sk_buff *skb,
+                                              struct net_device *dev,
+                                              netdev_features_t features)
+{
+       u16 queue;
+
+       if (skb_is_gso(skb)) {
+               queue = skb_get_queue_mapping(skb);
+               if (!stmmac_tso_channel_permitted(netdev_priv(dev), queue))
+                       features &= ~NETIF_F_GSO_MASK;
+       }
+
+       return vlan_features_check(skb, features);
+}
+
 static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
 {
        struct vlan_ethhdr *veth = skb_vlan_eth_hdr(skb);
@@ -7208,6 +7231,7 @@ static void stmmac_get_stats64(struct net_device *dev, struct rtnl_link_stats64
 static const struct net_device_ops stmmac_netdev_ops = {
        .ndo_open = stmmac_open,
        .ndo_start_xmit = stmmac_xmit,
+       .ndo_features_check = stmmac_features_check,
        .ndo_stop = stmmac_release,
        .ndo_change_mtu = stmmac_change_mtu,
        .ndo_fix_features = stmmac_fix_features,