kernel: support hw flow-offloading counters on newer MediaTek SoCs
authorDaniel Golle <daniel@makrotopia.org>
Wed, 2 Nov 2022 11:24:56 +0000 (11:24 +0000)
committerDaniel Golle <daniel@makrotopia.org>
Wed, 2 Nov 2022 11:41:56 +0000 (11:41 +0000)
The packet processing engine (PPE) found in newer ARM-based MediaTek
SoCs provides packet and byte counters for offloaded streams.
Import pending patch reading and using those counters.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch [new file with mode: 0644]

diff --git a/target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch b/target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch
new file mode 100644 (file)
index 0000000..815e0d2
--- /dev/null
@@ -0,0 +1,409 @@
+From patchwork Wed Nov  2 00:58:01 2022
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
+X-Patchwork-Id: 13027653
+X-Patchwork-Delegate: kuba@kernel.org
+Return-Path: <netdev-owner@kernel.org>
+Date: Wed, 2 Nov 2022 00:58:01 +0000
+From: Daniel Golle <daniel@makrotopia.org>
+To: Felix Fietkau <nbd@nbd.name>, John Crispin <john@phrozen.org>,
+        Sean Wang <sean.wang@mediatek.com>,
+        Mark Lee <Mark-MC.Lee@mediatek.com>,
+        "David S. Miller" <davem@davemloft.net>,
+        Eric Dumazet <edumazet@google.com>,
+        Jakub Kicinski <kuba@kernel.org>,
+        Paolo Abeni <pabeni@redhat.com>,
+        Matthias Brugger <matthias.bgg@gmail.com>,
+        netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+        linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org
+Subject: [PATCH v4] net: ethernet: mediatek: ppe: add support for flow
+ accounting
+Message-ID: <Y2HAmYYPd77dz+K5@makrotopia.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+The PPE units found in MT7622 and newer support packet and byte
+accounting of hw-offloaded flows. Add support for reading those
+counters as found in MediaTek's SDK[1].
+
+[1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/bc6a6a375c800dc2b80e1a325a2c732d1737df92
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+v4: declare function mtk_mib_entry_read as static
+v3: don't bother to set 'false' values in any zero-initialized struct
+    use mtk_foe_entry_ib2
+    both changes were requested by Felix Fietkau
+
+v2: fix wrong variable name in return value check spotted by Denis Kirjanov
+
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c   |   7 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h   |   1 +
+ drivers/net/ethernet/mediatek/mtk_ppe.c       | 110 +++++++++++++++++-
+ drivers/net/ethernet/mediatek/mtk_ppe.h       |  23 +++-
+ .../net/ethernet/mediatek/mtk_ppe_debugfs.c   |   9 +-
+ .../net/ethernet/mediatek/mtk_ppe_offload.c   |   7 ++
+ drivers/net/ethernet/mediatek/mtk_ppe_regs.h  |  14 +++
+ 7 files changed, 166 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4084,7 +4084,9 @@ static int mtk_probe(struct platform_dev
+                       u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400;
+                       eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr,
+-                                                 eth->soc->offload_version, i);
++                                                 eth->soc->offload_version, i,
++                                                 eth->soc->has_accounting);
++
+                       if (!eth->ppe[i]) {
+                               err = -ENOMEM;
+                               goto err_free_dev;
+@@ -4204,6 +4206,7 @@ static const struct mtk_soc_data mt7622_
+       .required_pctl = false,
+       .offload_version = 2,
+       .hash_offset = 2,
++      .has_accounting = true,
+       .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
+       .txrx = {
+               .txd_size = sizeof(struct mtk_tx_dma),
+@@ -4241,6 +4244,7 @@ static const struct mtk_soc_data mt7629_
+       .hw_features = MTK_HW_FEATURES,
+       .required_clks = MT7629_CLKS_BITMAP,
+       .required_pctl = false,
++      .has_accounting = true,
+       .txrx = {
+               .txd_size = sizeof(struct mtk_tx_dma),
+               .rxd_size = sizeof(struct mtk_rx_dma),
+@@ -4261,6 +4265,7 @@ static const struct mtk_soc_data mt7986_
+       .offload_version = 2,
+       .hash_offset = 4,
+       .foe_entry_size = sizeof(struct mtk_foe_entry),
++      .has_accounting = true,
+       .txrx = {
+               .txd_size = sizeof(struct mtk_tx_dma_v2),
+               .rxd_size = sizeof(struct mtk_rx_dma_v2),
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -983,6 +983,7 @@ struct mtk_soc_data {
+       u8              hash_offset;
+       u16             foe_entry_size;
+       netdev_features_t hw_features;
++      bool            has_accounting;
+       struct {
+               u32     txd_size;
+               u32     rxd_size;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -74,6 +74,46 @@ static int mtk_ppe_wait_busy(struct mtk_
+       return ret;
+ }
++static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe)
++{
++      int ret;
++      u32 val;
++
++      ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val,
++                               !(val & MTK_PPE_MIB_SER_CR_ST),
++                               20, MTK_PPE_WAIT_TIMEOUT_US);
++
++      if (ret)
++              dev_err(ppe->dev, "MIB table busy");
++
++      return ret;
++}
++
++static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
++{
++      u32 val, cnt_r0, cnt_r1, cnt_r2;
++      u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
++
++      val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST;
++      ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val);
++
++      if (mtk_ppe_mib_wait_busy(ppe))
++              return -ETIMEDOUT;
++
++      cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
++      cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
++      cnt_r2 = readl(ppe->base + MTK_PPE_MIB_SER_R2);
++
++      byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0);
++      byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
++      pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
++      pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
++      *bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
++      *packets = (pkt_cnt_high << 16) | pkt_cnt_low;
++
++      return 0;
++}
++
+ static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
+ {
+       ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
+@@ -444,6 +484,13 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
+               hwe->ib1 &= ~MTK_FOE_IB1_STATE;
+               hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
+               dma_wmb();
++              if (ppe->accounting) {
++                      struct mtk_foe_accounting *acct;
++
++                      acct = ppe->acct_table + entry->hash * sizeof(*acct);
++                      acct->packets = 0;
++                      acct->bytes = 0;
++              }
+       }
+       entry->hash = 0xffff;
+@@ -551,6 +598,9 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
+       wmb();
+       hwe->ib1 = entry->ib1;
++      if (ppe->accounting)
++              *mtk_foe_entry_ib2(eth, hwe) |= MTK_FOE_IB2_MIB_CNT;
++
+       dma_wmb();
+       mtk_ppe_cache_clear(ppe);
+@@ -716,14 +766,42 @@ int mtk_foe_entry_idle_time(struct mtk_p
+       return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+ }
++struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
++                                               struct mtk_foe_accounting *diff)
++{
++      struct mtk_foe_accounting *acct;
++      int size = sizeof(struct mtk_foe_accounting);
++      u64 bytes, packets;
++
++      if (!ppe->accounting)
++              return NULL;
++
++      if (mtk_mib_entry_read(ppe, index, &bytes, &packets))
++              return NULL;
++
++      acct = ppe->acct_table + index * size;
++
++      acct->bytes += bytes;
++      acct->packets += packets;
++
++      if (diff) {
++              diff->bytes = bytes;
++              diff->packets = packets;
++      }
++
++      return acct;
++}
++
+ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+-                           int version, int index)
++                           int version, int index, bool accounting)
+ {
+       const struct mtk_soc_data *soc = eth->soc;
+       struct device *dev = eth->dev;
+       struct mtk_ppe *ppe;
+       u32 foe_flow_size;
+       void *foe;
++      struct mtk_mib_entry *mib;
++      struct mtk_foe_accounting *acct;
+       ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL);
+       if (!ppe)
+@@ -738,6 +816,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+       ppe->eth = eth;
+       ppe->dev = dev;
+       ppe->version = version;
++      ppe->accounting = accounting;
+       foe = dmam_alloc_coherent(ppe->dev,
+                                 MTK_PPE_ENTRIES * soc->foe_entry_size,
+@@ -753,6 +832,25 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+       if (!ppe->foe_flow)
+               return NULL;
++      if (accounting) {
++              mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib),
++                                        &ppe->mib_phys, GFP_KERNEL);
++              if (!mib)
++                      return NULL;
++
++              memset(mib, 0, MTK_PPE_ENTRIES * sizeof(*mib));
++
++              ppe->mib_table = mib;
++
++              acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct),
++                                  GFP_KERNEL);
++
++              if (!acct)
++                      return NULL;
++
++              ppe->acct_table = acct;
++      }
++
+       mtk_ppe_debugfs_init(ppe, index);
+       return ppe;
+@@ -867,6 +965,16 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
+               ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777);
+               ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f);
+       }
++
++      if (ppe->accounting && ppe->mib_phys) {
++              ppe_w32(ppe, MTK_PPE_MIB_TB_BASE, ppe->mib_phys);
++              ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_EN,
++                      MTK_PPE_MIB_CFG_EN);
++              ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_RD_CLR,
++                      MTK_PPE_MIB_CFG_RD_CLR);
++              ppe_m32(ppe, MTK_PPE_MIB_CACHE_CTL, MTK_PPE_MIB_CACHE_CTL_EN,
++                      MTK_PPE_MIB_CFG_RD_CLR);
++      }
+ }
+ int mtk_ppe_stop(struct mtk_ppe *ppe)
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -57,6 +57,7 @@ enum {
+ #define MTK_FOE_IB2_MULTICAST         BIT(8)
+ #define MTK_FOE_IB2_WDMA_QID2         GENMASK(13, 12)
++#define MTK_FOE_IB2_MIB_CNT           BIT(15)
+ #define MTK_FOE_IB2_WDMA_DEVIDX               BIT(16)
+ #define MTK_FOE_IB2_WDMA_WINFO                BIT(17)
+@@ -284,16 +285,34 @@ struct mtk_flow_entry {
+       unsigned long cookie;
+ };
++struct mtk_mib_entry {
++      u32     byt_cnt_l;
++      u16     byt_cnt_h;
++      u32     pkt_cnt_l;
++      u8      pkt_cnt_h;
++      u8      _rsv0;
++      u32     _rsv1;
++} __packed;
++
++struct mtk_foe_accounting {
++      u64     bytes;
++      u64     packets;
++};
++
+ struct mtk_ppe {
+       struct mtk_eth *eth;
+       struct device *dev;
+       void __iomem *base;
+       int version;
+       char dirname[5];
++      bool accounting;
+       void *foe_table;
+       dma_addr_t foe_phys;
++      struct mtk_mib_entry *mib_table;
++      dma_addr_t mib_phys;
++
+       u16 foe_check_time[MTK_PPE_ENTRIES];
+       struct hlist_head *foe_flow;
+@@ -303,7 +322,7 @@ struct mtk_ppe {
+ };
+ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+-                           int version, int index);
++                           int version, int index, bool accounting);
+ void mtk_ppe_start(struct mtk_ppe *ppe);
+ int mtk_ppe_stop(struct mtk_ppe *ppe);
+@@ -354,5 +373,7 @@ int mtk_foe_entry_commit(struct mtk_ppe
+ void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+ int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index);
++struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
++                                               struct mtk_foe_accounting *diff);
+ #endif
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -82,6 +82,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file
+               struct mtk_foe_entry *entry = mtk_foe_get_entry(ppe, i);
+               struct mtk_foe_mac_info *l2;
+               struct mtk_flow_addr_info ai = {};
++              struct mtk_foe_accounting *acct;
+               unsigned char h_source[ETH_ALEN];
+               unsigned char h_dest[ETH_ALEN];
+               int type, state;
+@@ -95,6 +96,8 @@ mtk_ppe_debugfs_foe_show(struct seq_file
+               if (bind && state != MTK_FOE_STATE_BIND)
+                       continue;
++              acct = mtk_foe_entry_get_mib(ppe, i, NULL);
++
+               type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
+               seq_printf(m, "%05x %s %7s", i,
+                          mtk_foe_entry_state_str(state),
+@@ -153,9 +156,11 @@ mtk_ppe_debugfs_foe_show(struct seq_file
+               *((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
+               seq_printf(m, " eth=%pM->%pM etype=%04x"
+-                            " vlan=%d,%d ib1=%08x ib2=%08x\n",
++                            " vlan=%d,%d ib1=%08x ib2=%08x"
++                            " packets=%lld bytes=%lld\n",
+                          h_source, h_dest, ntohs(l2->etype),
+-                         l2->vlan1, l2->vlan2, entry->ib1, ib2);
++                         l2->vlan1, l2->vlan2, entry->ib1, ib2,
++                         acct->packets, acct->bytes);
+       }
+       return 0;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -491,6 +491,7 @@ static int
+ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
+ {
+       struct mtk_flow_entry *entry;
++      struct mtk_foe_accounting diff;
+       u32 idle;
+       entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
+@@ -501,6 +502,12 @@ mtk_flow_offload_stats(struct mtk_eth *e
+       idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
+       f->stats.lastused = jiffies - idle * HZ;
++      if (entry->hash != 0xFFFF) {
++              mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash, &diff);
++              f->stats.pkts += diff.packets;
++              f->stats.bytes += diff.bytes;
++      }
++
+       return 0;
+ }
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+@@ -143,6 +143,20 @@ enum {
+ #define MTK_PPE_MIB_TB_BASE                   0x338
++#define MTK_PPE_MIB_SER_CR                    0x33C
++#define MTK_PPE_MIB_SER_CR_ST                 BIT(16)
++#define MTK_PPE_MIB_SER_CR_ADDR                       GENMASK(13, 0)
++
++#define MTK_PPE_MIB_SER_R0                    0x340
++#define MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW               GENMASK(31, 0)
++
++#define MTK_PPE_MIB_SER_R1                    0x344
++#define MTK_PPE_MIB_SER_R1_PKT_CNT_LOW                GENMASK(31, 16)
++#define MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH      GENMASK(15, 0)
++
++#define MTK_PPE_MIB_SER_R2                    0x348
++#define MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH               GENMASK(23, 0)
++
+ #define MTK_PPE_MIB_CACHE_CTL                 0x350
+ #define MTK_PPE_MIB_CACHE_CTL_EN              BIT(0)
+ #define MTK_PPE_MIB_CACHE_CTL_FLUSH           BIT(2)