]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: ethernet: mtk_eth_soc: initialize PPE per-tag-layer MTU registers
authorDaniel Golle <daniel@makrotopia.org>
Fri, 10 Apr 2026 02:57:52 +0000 (03:57 +0100)
committerJakub Kicinski <kuba@kernel.org>
Sun, 12 Apr 2026 22:22:58 +0000 (15:22 -0700)
The PPE enforces output frame size limits via per-tag-layer VLAN_MTU
registers that the driver never initializes. The hardware defaults do
not account for PPPoE overhead, causing the PPE to punt encapsulated
frames back to the CPU instead of forwarding them.

Initialize the registers at PPE start and on MTU changes using the
maximum GMAC MTU. This is a conservative approximation -- the actual
per-PPE requirement depends on egress path, but using the global
maximum ensures the limits are never too small.

Fixes: ba37b7caf1ed2 ("net: ethernet: mtk_eth_soc: add support for initializing the PPE")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Link: https://patch.msgid.link/ec995ab8ce8be423267a1cc093147a74d2eb9d82.1775789829.git.daniel@makrotopia.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mediatek/mtk_eth_soc.c
drivers/net/ethernet/mediatek/mtk_ppe.c
drivers/net/ethernet/mediatek/mtk_ppe.h

index ddc321a02fdae03d4602d0b9053b97c4788f15ac..796f79088f3662ef765c5cb397f703dd2339da6c 100644 (file)
@@ -3566,12 +3566,23 @@ found:
        return NOTIFY_DONE;
 }
 
+static int mtk_max_gmac_mtu(struct mtk_eth *eth)
+{
+       int i, max_mtu = ETH_DATA_LEN;
+
+       for (i = 0; i < ARRAY_SIZE(eth->netdev); i++)
+               if (eth->netdev[i] && eth->netdev[i]->mtu > max_mtu)
+                       max_mtu = eth->netdev[i]->mtu;
+
+       return max_mtu;
+}
+
 static int mtk_open(struct net_device *dev)
 {
        struct mtk_mac *mac = netdev_priv(dev);
        struct mtk_eth *eth = mac->hw;
        struct mtk_mac *target_mac;
-       int i, err, ppe_num;
+       int i, err, ppe_num, mtu;
 
        ppe_num = eth->soc->ppe_num;
 
@@ -3618,6 +3629,10 @@ static int mtk_open(struct net_device *dev)
                        mtk_gdm_config(eth, target_mac->id, gdm_config);
                }
 
+               mtu = mtk_max_gmac_mtu(eth);
+               for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
+                       mtk_ppe_update_mtu(eth->ppe[i], mtu);
+
                napi_enable(&eth->tx_napi);
                napi_enable(&eth->rx_napi);
                mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
@@ -4311,6 +4326,7 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu)
        int length = new_mtu + MTK_RX_ETH_HLEN;
        struct mtk_mac *mac = netdev_priv(dev);
        struct mtk_eth *eth = mac->hw;
+       int max_mtu, i;
 
        if (rcu_access_pointer(eth->prog) &&
            length > MTK_PP_MAX_BUF_SIZE) {
@@ -4321,6 +4337,10 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu)
        mtk_set_mcr_max_rx(mac, length);
        WRITE_ONCE(dev->mtu, new_mtu);
 
+       max_mtu = mtk_max_gmac_mtu(eth);
+       for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
+               mtk_ppe_update_mtu(eth->ppe[i], max_mtu);
+
        return 0;
 }
 
index 75f7728fc796236d9cc9702a6d0e98812e8d9b06..18279e2a7022ef058d0cf870e35338979c4e38e5 100644 (file)
@@ -973,6 +973,36 @@ static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
        }
 }
 
+void mtk_ppe_update_mtu(struct mtk_ppe *ppe, int mtu)
+{
+       int base;
+       u32 val;
+
+       if (!ppe)
+               return;
+
+       /* The PPE checks output frame size against per-tag-layer MTU limits,
+        * treating PPPoE and DSA tags just like 802.1Q VLAN tags. The Linux
+        * device MTU already accounts for PPPoE (PPPOE_SES_HLEN) and DSA tag
+        * overhead, but 802.1Q VLAN tags are handled transparently without
+        * being reflected by the lower device MTU being increased by 4.
+        * Use the maximum MTU across all GMAC interfaces so that PPE output
+        * frame limits are sufficiently high regardless of which port a flow
+        * egresses through.
+        */
+       base = ETH_HLEN + mtu;
+
+       val = FIELD_PREP(MTK_PPE_VLAN_MTU0_NONE, base) |
+             FIELD_PREP(MTK_PPE_VLAN_MTU0_1TAG, base + VLAN_HLEN);
+       ppe_w32(ppe, MTK_PPE_VLAN_MTU0, val);
+
+       val = FIELD_PREP(MTK_PPE_VLAN_MTU1_2TAG,
+                        base + 2 * VLAN_HLEN) |
+             FIELD_PREP(MTK_PPE_VLAN_MTU1_3TAG,
+                        base + 3 * VLAN_HLEN);
+       ppe_w32(ppe, MTK_PPE_VLAN_MTU1, val);
+}
+
 void mtk_ppe_start(struct mtk_ppe *ppe)
 {
        u32 val;
index 223f709e2704f8b4d06095d2c8192914033059c6..ba85e39a155bf92cde6790a358d69bb80f61dbfc 100644 (file)
@@ -346,6 +346,7 @@ struct mtk_ppe {
 struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index);
 
 void mtk_ppe_deinit(struct mtk_eth *eth);
+void mtk_ppe_update_mtu(struct mtk_ppe *ppe, int mtu);
 void mtk_ppe_start(struct mtk_ppe *ppe);
 int mtk_ppe_stop(struct mtk_ppe *ppe);
 int mtk_ppe_prepare_reset(struct mtk_ppe *ppe);