net: ethernet: mtk_eth_soc: enable hardware DSA untagging
authorFelix Fietkau <nbd@nbd.name>
Mon, 14 Nov 2022 12:42:14 +0000 (13:42 +0100)
committerJakub Kicinski <kuba@kernel.org>
Wed, 16 Nov 2022 04:22:08 +0000 (20:22 -0800)
- pass the tag to DSA via metadata dst
- disabled on 7986 for now, since it's not working yet
- disabled if a MAC is enabled that does not use DSA

This improves performance by bypassing the DSA tag driver and avoiding extra
skb data mangling

Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mediatek/mtk_eth_soc.c
drivers/net/ethernet/mediatek/mtk_eth_soc.h

index a118c71..3e9f553 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/jhash.h>
 #include <linux/bitfield.h>
 #include <net/dsa.h>
+#include <net/dst_metadata.h>
 
 #include "mtk_eth_soc.h"
 #include "mtk_wed.h"
@@ -1939,13 +1940,19 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
                                __vlan_hwaccel_put_tag(skb, htons(RX_DMA_VPID(trxd.rxd3)),
                                                       RX_DMA_VID(trxd.rxd3));
                        }
+               }
+
+               /* When using VLAN untagging in combination with DSA, the
+                * hardware treats the MTK special tag as a VLAN and untags it.
+                */
+               if (skb_vlan_tag_present(skb) && netdev_uses_dsa(netdev)) {
+                       unsigned int port = ntohs(skb->vlan_proto) & GENMASK(2, 0);
 
-                       /* If the device is attached to a dsa switch, the special
-                        * tag inserted in VLAN field by hw switch can * be offloaded
-                        * by RX HW VLAN offload. Clear vlan info.
-                        */
-                       if (netdev_uses_dsa(netdev))
-                               __vlan_hwaccel_clear_tag(skb);
+                       if (port < ARRAY_SIZE(eth->dsa_meta) &&
+                           eth->dsa_meta[port])
+                               skb_dst_set_noref(skb, &eth->dsa_meta[port]->dst);
+
+                       __vlan_hwaccel_clear_tag(skb);
                }
 
                skb_record_rx_queue(skb, 0);
@@ -2990,11 +2997,46 @@ static void mtk_gdm_config(struct mtk_eth *eth, u32 config)
        mtk_w32(eth, 0, MTK_RST_GL);
 }
 
+
+static bool mtk_uses_dsa(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;
+#else
+       return false;
+#endif
+}
+
 static int mtk_open(struct net_device *dev)
 {
        struct mtk_mac *mac = netdev_priv(dev);
        struct mtk_eth *eth = mac->hw;
-       int err;
+       int i, err;
+
+       if (mtk_uses_dsa(dev) && !eth->prog) {
+               for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
+                       struct metadata_dst *md_dst = eth->dsa_meta[i];
+
+                       if (md_dst)
+                               continue;
+
+                       md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX,
+                                                   GFP_KERNEL);
+                       if (!md_dst)
+                               return -ENOMEM;
+
+                       md_dst->u.port_info.port_id = i;
+                       eth->dsa_meta[i] = md_dst;
+               }
+       } else {
+               /* Hardware special tag parsing needs to be disabled if at least
+                * one MAC does not use DSA.
+                */
+               u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
+               val &= ~MTK_CDMP_STAG_EN;
+               mtk_w32(eth, val, MTK_CDMP_IG_CTRL);
+       }
 
        err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0);
        if (err) {
@@ -3321,6 +3363,10 @@ static int mtk_hw_init(struct mtk_eth *eth)
         */
        val = mtk_r32(eth, MTK_CDMQ_IG_CTRL);
        mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL);
+       if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+               val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
+               mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL);
+       }
 
        /* Enable RX VLan Offloading */
        mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
@@ -3538,6 +3584,12 @@ static int mtk_free_dev(struct mtk_eth *eth)
                free_netdev(eth->netdev[i]);
        }
 
+       for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
+               if (!eth->dsa_meta[i])
+                       break;
+               metadata_dst_free(eth->dsa_meta[i]);
+       }
+
        return 0;
 }
 
index 589f27d..a572416 100644 (file)
@@ -22,6 +22,9 @@
 #include <linux/bpf_trace.h>
 #include "mtk_ppe.h"
 
+#define MTK_MAX_DSA_PORTS      7
+#define MTK_DSA_PORT_MASK      GENMASK(2, 0)
+
 #define MTK_QDMA_PAGE_SIZE     2048
 #define MTK_MAX_RX_LENGTH      1536
 #define MTK_MAX_RX_LENGTH_2K   2048
@@ -91,6 +94,9 @@
 #define MTK_CDMQ_IG_CTRL       0x1400
 #define MTK_CDMQ_STAG_EN       BIT(0)
 
+/* CDMQ Exgress Control Register */
+#define MTK_CDMQ_EG_CTRL       0x1404
+
 /* CDMP Ingress Control Register */
 #define MTK_CDMP_IG_CTRL       0x400
 #define MTK_CDMP_STAG_EN       BIT(0)
@@ -1121,6 +1127,8 @@ struct mtk_eth {
 
        int                             ip_align;
 
+       struct metadata_dst             *dsa_meta[MTK_MAX_DSA_PORTS];
+
        struct mtk_ppe                  *ppe[2];
        struct rhashtable               flow_table;