Merge Official Source

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
Tianling Shen 2023-03-24 15:49:09 +08:00
commit a55043bb91
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
17 changed files with 1198 additions and 107 deletions

View File

@ -0,0 +1,149 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 20 Mar 2023 14:28:08 +0100
Subject: [PATCH] wifi: mac80211: add support for letting drivers register tc
offload support
On newer MediaTek SoCs (e.g. MT7986), WLAN->WLAN or WLAN->Ethernet flows can
be offloaded by the SoC. In order to support that, the .ndo_setup_tc op is
needed.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -4196,6 +4196,10 @@ struct ieee80211_prep_tx_info {
* Note that a sta can also be inserted or removed with valid links,
* i.e. passed to @sta_add/@sta_state with sta->valid_links not zero.
* In fact, cannot change from having valid_links and not having them.
+ * @net_setup_tc: Called from .ndo_setup_tc in order to prepare hardware
+ * flow offloading for flows originating from the vif.
+ * Note that the driver must not assume that the vif driver_data is valid
+ * at this point, since the callback can be called during netdev teardown.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -4551,6 +4555,11 @@ struct ieee80211_ops {
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
u16 old_links, u16 new_links);
+ int (*net_setup_tc)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct net_device *dev,
+ enum tc_setup_type type,
+ void *type_data);
};
/**
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1470,6 +1470,23 @@ static inline int drv_net_fill_forward_p
return ret;
}
+static inline int drv_net_setup_tc(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct net_device *dev,
+ enum tc_setup_type type, void *type_data)
+{
+ int ret = -EOPNOTSUPP;
+
+ sdata = get_bss_sdata(sdata);
+ trace_drv_net_setup_tc(local, sdata, type);
+ if (local->ops->net_setup_tc)
+ ret = local->ops->net_setup_tc(&local->hw, &sdata->vif, dev,
+ type, type_data);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
int drv_change_vif_links(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
u16 old_links, u16 new_links,
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1935,7 +1935,8 @@ void ieee80211_color_change_finalize_wor
/* interface handling */
#define MAC80211_SUPPORTED_FEATURES_TX (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_HIGHDMA | NETIF_F_GSO_SOFTWARE)
+ NETIF_F_HIGHDMA | NETIF_F_GSO_SOFTWARE | \
+ NETIF_F_HW_TC)
#define MAC80211_SUPPORTED_FEATURES_RX (NETIF_F_RXCSUM)
#define MAC80211_SUPPORTED_FEATURES (MAC80211_SUPPORTED_FEATURES_TX | \
MAC80211_SUPPORTED_FEATURES_RX)
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -813,6 +813,21 @@ ieee80211_get_stats64(struct net_device
dev_fetch_sw_netstats(stats, dev->tstats);
}
+static int ieee80211_netdev_setup_tc(struct net_device *dev,
+ enum tc_setup_type type, void *type_data)
+{
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_local *local;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ local = sdata->local;
+
+ if (!local->ops->net_setup_tc)
+ return -EOPNOTSUPP;
+
+ return drv_net_setup_tc(local, sdata, dev, type, type_data);
+}
+
static const struct net_device_ops ieee80211_dataif_ops = {
.ndo_open = ieee80211_open,
.ndo_stop = ieee80211_stop,
@@ -821,6 +836,7 @@ static const struct net_device_ops ieee8
.ndo_set_rx_mode = ieee80211_set_multicast_list,
.ndo_set_mac_address = ieee80211_change_mac,
.ndo_get_stats64 = ieee80211_get_stats64,
+ .ndo_setup_tc = ieee80211_netdev_setup_tc,
};
static u16 ieee80211_monitor_select_queue(struct net_device *dev,
@@ -929,6 +945,7 @@ static const struct net_device_ops ieee8
.ndo_set_mac_address = ieee80211_change_mac,
.ndo_get_stats64 = ieee80211_get_stats64,
.ndo_fill_forward_path = ieee80211_netdev_fill_forward_path,
+ .ndo_setup_tc = ieee80211_netdev_setup_tc,
};
static bool ieee80211_iftype_supports_hdr_offload(enum nl80211_iftype iftype)
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -2478,6 +2478,31 @@ DEFINE_EVENT(sta_event, drv_net_fill_for
TP_ARGS(local, sdata, sta)
);
+TRACE_EVENT(drv_net_setup_tc,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ u8 type),
+
+ TP_ARGS(local, sdata, type),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __field(u8, type)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ __entry->type = type;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " type:%d\n",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->type
+ )
+);
+
TRACE_EVENT(drv_change_vif_links,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,

View File

@ -18,7 +18,7 @@
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1645,6 +1645,7 @@ enum ieee80211_smps_mode {
@@ -1671,6 +1671,7 @@ enum ieee80211_smps_mode {
*
* @power_level: requested transmit power (in dBm), backward compatibility
* value only that is set to the minimum of all interfaces
@ -26,7 +26,7 @@
*
* @chandef: the channel definition to tune to
* @radar_enabled: whether radar detection is enabled
@@ -1665,6 +1666,7 @@ enum ieee80211_smps_mode {
@@ -1691,6 +1692,7 @@ enum ieee80211_smps_mode {
struct ieee80211_conf {
u32 flags;
int power_level, dynamic_ps_timeout;
@ -57,7 +57,7 @@
__NL80211_ATTR_AFTER_LAST,
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2998,6 +2998,19 @@ static int ieee80211_get_tx_power(struct
@@ -3028,6 +3028,19 @@ static int ieee80211_get_tx_power(struct
return 0;
}
@ -77,7 +77,7 @@
static void ieee80211_rfkill_poll(struct wiphy *wiphy)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
@@ -4881,6 +4894,7 @@ const struct cfg80211_ops mac80211_confi
@@ -4911,6 +4924,7 @@ const struct cfg80211_ops mac80211_confi
.set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,

View File

@ -12,9 +12,9 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=$(PROJECT_GIT)/project/odhcpd.git
PKG_MIRROR_HASH:=a27ac6f006e6591487a2425adab0bf4c9975785e7d49120408d4e5c4ddb00d56
PKG_SOURCE_DATE:=2023-03-22
PKG_SOURCE_VERSION:=7c0f603abc1481ddabcdeae0eaaf72d5535ecefa
PKG_MIRROR_HASH:=f95c2edfa6756e2a6522cd9d3e0560bfc1cbc2aed9e0c3763c5f6081b153eb49
PKG_SOURCE_DATE:=2023-03-24
PKG_SOURCE_VERSION:=29c934d7ab98ca0b5da0e3757b885a1d3c19a2f4
PKG_MAINTAINER:=Hans Dedecker <dedeckeh@gmail.com>
PKG_LICENSE:=GPL-2.0

View File

@ -1,10 +1,11 @@
--- a/include/linux/soc/mediatek/mtk_wed.h
+++ b/include/linux/soc/mediatek/mtk_wed.h
@@ -5,21 +5,76 @@
@@ -5,21 +5,77 @@
#include <linux/rcupdate.h>
#include <linux/regmap.h>
#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
#define MTK_WED_TX_QUEUES 2
+#define MTK_WED_RX_QUEUES 2
@ -77,7 +78,7 @@
struct mtk_wed_device {
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
const struct mtk_wed_ops *ops;
@@ -28,30 +83,76 @@ struct mtk_wed_device {
@@ -28,30 +84,76 @@ struct mtk_wed_device {
bool init_done, running;
int wdma_idx;
int irq;
@ -156,7 +157,7 @@
} wlan;
#endif
};
@@ -59,10 +160,16 @@ struct mtk_wed_device {
@@ -59,10 +161,16 @@ struct mtk_wed_device {
struct mtk_wed_ops {
int (*attach)(struct mtk_wed_device *dev);
int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring,
@ -174,7 +175,16 @@
void (*stop)(struct mtk_wed_device *dev);
void (*start)(struct mtk_wed_device *dev, u32 irq_mask);
@@ -97,12 +204,22 @@ mtk_wed_device_attach(struct mtk_wed_dev
@@ -73,6 +181,8 @@ struct mtk_wed_ops {
u32 (*irq_get)(struct mtk_wed_device *dev, u32 mask);
void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask);
+ int (*setup_tc)(struct mtk_wed_device *wed, struct net_device *dev,
+ enum tc_setup_type type, void *type_data);
};
extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
@@ -97,12 +207,22 @@ mtk_wed_device_attach(struct mtk_wed_dev
return ret;
}
@ -199,7 +209,7 @@
#define mtk_wed_device_txfree_ring_setup(_dev, _regs) \
(_dev)->ops->txfree_ring_setup(_dev, _regs)
#define mtk_wed_device_reg_read(_dev, _reg) \
@@ -113,6 +230,14 @@ mtk_wed_device_attach(struct mtk_wed_dev
@@ -113,6 +233,16 @@ mtk_wed_device_attach(struct mtk_wed_dev
(_dev)->ops->irq_get(_dev, _mask)
#define mtk_wed_device_irq_set_mask(_dev, _mask) \
(_dev)->ops->irq_set_mask(_dev, _mask)
@ -211,10 +221,12 @@
+ (_dev)->ops->msg_update(_dev, _id, _msg, _len)
+#define mtk_wed_device_stop(_dev) (_dev)->ops->stop(_dev)
+#define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev)
+#define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) \
+ (_dev)->ops->setup_tc(_dev, _netdev, _type, _type_data)
#else
static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
{
@@ -120,12 +245,17 @@ static inline bool mtk_wed_device_active
@@ -120,12 +250,18 @@ static inline bool mtk_wed_device_active
}
#define mtk_wed_device_detach(_dev) do {} while (0)
#define mtk_wed_device_start(_dev, _mask) do {} while (0)
@ -230,6 +242,7 @@
+#define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV
+#define mtk_wed_device_stop(_dev) do {} while (0)
+#define mtk_wed_device_dma_reset(_dev) do {} while (0)
+#define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) -EOPNOTSUPP
#endif
#endif

View File

@ -53,18 +53,18 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -4586,7 +4586,9 @@ static int mtk_probe(struct platform_dev
@@ -4585,8 +4585,8 @@ static int mtk_probe(struct platform_dev
for (i = 0; i < num_ppe; i++) {
u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400;
eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr,
- 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);
+ eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr, i);
+
if (!eth->ppe[i]) {
err = -ENOMEM;
goto err_free_dev;
@@ -4711,6 +4713,7 @@ static const struct mtk_soc_data mt7622_
@@ -4711,6 +4711,7 @@ static const struct mtk_soc_data mt7622_
.required_pctl = false,
.offload_version = 2,
.hash_offset = 2,
@ -72,7 +72,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
.foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
.txrx = {
.txd_size = sizeof(struct mtk_tx_dma),
@@ -4748,6 +4751,7 @@ static const struct mtk_soc_data mt7629_
@@ -4748,6 +4749,7 @@ static const struct mtk_soc_data mt7629_
.hw_features = MTK_HW_FEATURES,
.required_clks = MT7629_CLKS_BITMAP,
.required_pctl = false,
@ -80,7 +80,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
.txrx = {
.txd_size = sizeof(struct mtk_tx_dma),
.rxd_size = sizeof(struct mtk_rx_dma),
@@ -4768,6 +4772,7 @@ static const struct mtk_soc_data mt7986_
@@ -4768,6 +4770,7 @@ static const struct mtk_soc_data mt7986_
.offload_version = 2,
.hash_offset = 4,
.foe_entry_size = sizeof(struct mtk_foe_entry),
@ -90,7 +90,16 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
.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
@@ -1042,6 +1042,7 @@ struct mtk_soc_data {
@@ -1025,6 +1025,8 @@ struct mtk_reg_map {
* the extra setup for those pins used by GMAC.
* @hash_offset Flow table hash offset.
* @foe_entry_size Foe table entry size.
+ * @has_accounting Bool indicating support for accounting of
+ * offloaded flows.
* @txd_size Tx DMA descriptor size.
* @rxd_size Rx DMA descriptor size.
* @rx_irq_done_mask Rx irq done register mask.
@@ -1042,6 +1044,7 @@ struct mtk_soc_data {
u8 hash_offset;
u16 foe_entry_size;
netdev_features_t hw_features;
@ -100,7 +109,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
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_
@@ -74,6 +74,48 @@ static int mtk_ppe_wait_busy(struct mtk_
return ret;
}
@ -121,14 +130,16 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
+
+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;
+ u32 val, cnt_r0, cnt_r1, cnt_r2;
+ int ret;
+
+ 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;
+ ret = mtk_ppe_mib_wait_busy(ppe);
+ if (ret)
+ return ret;
+
+ cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
+ cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
@ -147,7 +158,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
{
ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
@@ -464,6 +504,13 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
@@ -464,6 +506,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();
@ -161,7 +172,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
}
entry->hash = 0xffff;
@@ -571,6 +618,9 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
@@ -571,6 +620,9 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
wmb();
hwe->ib1 = entry->ib1;
@ -171,10 +182,12 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
dma_wmb();
mtk_ppe_cache_clear(ppe);
@@ -762,14 +812,42 @@ int mtk_ppe_prepare_reset(struct mtk_ppe
@@ -762,11 +814,39 @@ int mtk_ppe_prepare_reset(struct mtk_ppe
return mtk_ppe_wait_busy(ppe);
}
-struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
- int version, int index)
+struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
+ struct mtk_foe_accounting *diff)
+{
@ -201,29 +214,27 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
+ 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)
+struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
{
+ bool accounting = eth->soc->has_accounting;
const struct mtk_soc_data *soc = eth->soc;
+ struct mtk_foe_accounting *acct;
struct device *dev = eth->dev;
+ struct mtk_mib_entry *mib;
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)
@@ -784,6 +862,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
@@ -783,7 +863,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
ppe->base = base;
ppe->eth = eth;
ppe->dev = dev;
ppe->version = version;
- ppe->version = version;
+ ppe->version = eth->soc->offload_version;
+ ppe->accounting = accounting;
foe = dmam_alloc_coherent(ppe->dev,
MTK_PPE_ENTRIES * soc->foe_entry_size,
@@ -799,6 +878,25 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
@@ -799,6 +880,23 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
if (!ppe->foe_flow)
return NULL;
@ -233,8 +244,6 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
+ 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),
@ -311,16 +320,17 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
u16 foe_check_time[MTK_PPE_ENTRIES];
struct hlist_head *foe_flow;
@@ -304,7 +323,7 @@ struct mtk_ppe {
@@ -303,8 +322,7 @@ struct mtk_ppe {
void *acct_table;
};
struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
-struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
- int version, int index);
+ int version, int index, bool accounting);
+struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index);
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);
@@ -358,5 +377,7 @@ int mtk_foe_entry_commit(struct mtk_ppe
@@ -358,5 +376,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);
@ -353,7 +363,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
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",
+ " packets=%llu bytes=%llu\n",
h_source, h_dest, ntohs(l2->etype),
- l2->vlan1, l2->vlan2, entry->ib1, ib2);
+ l2->vlan1, l2->vlan2, entry->ib1, ib2,
@ -371,12 +381,13 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov
u32 idle;
entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
@@ -507,6 +508,12 @@ mtk_flow_offload_stats(struct mtk_eth *e
@@ -507,6 +508,13 @@ 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);
+ 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;
+ }

View File

@ -20,7 +20,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
#include <net/dsa.h>
#include "mtk_eth_soc.h"
#include "mtk_ppe.h"
@@ -755,7 +756,9 @@ void __mtk_ppe_check_skb(struct mtk_ppe
@@ -757,7 +758,9 @@ void __mtk_ppe_check_skb(struct mtk_ppe
skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK)
goto out;

View File

@ -14,43 +14,15 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -561,6 +561,7 @@ mtk_eth_setup_tc_block(struct net_device
struct mtk_eth *eth = mac->hw;
static LIST_HEAD(block_cb_list);
struct flow_block_cb *block_cb;
+ bool register_block = false;
flow_setup_cb_t *cb;
@@ -584,6 +584,7 @@ mtk_eth_setup_tc_block(struct net_device
if (IS_ERR(block_cb))
return PTR_ERR(block_cb);
if (!eth->soc->offload_version)
@@ -575,23 +576,27 @@ mtk_eth_setup_tc_block(struct net_device
switch (f->command) {
case FLOW_BLOCK_BIND:
block_cb = flow_block_cb_lookup(f->block, cb, dev);
- if (block_cb) {
- flow_block_cb_incref(block_cb);
- return 0;
+ if (!block_cb) {
+ block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
+ if (IS_ERR(block_cb))
+ return PTR_ERR(block_cb);
+
+ register_block = true;
}
- block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
- if (IS_ERR(block_cb))
- return PTR_ERR(block_cb);
- flow_block_cb_add(block_cb, f);
- list_add_tail(&block_cb->driver_list, &block_cb_list);
+ flow_block_cb_incref(block_cb);
+
+ if (register_block) {
+ flow_block_cb_add(block_cb, f);
+ list_add_tail(&block_cb->driver_list, &block_cb_list);
+ }
flow_block_cb_add(block_cb, f);
list_add_tail(&block_cb->driver_list, &block_cb_list);
return 0;
case FLOW_BLOCK_UNBIND:
block_cb = flow_block_cb_lookup(f->block, cb, dev);
@@ -592,7 +593,7 @@ mtk_eth_setup_tc_block(struct net_device
if (!block_cb)
return -ENOENT;

View File

@ -0,0 +1,266 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 20 Mar 2023 11:44:30 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: add code for offloading flows
from wlan devices
WED version 2 (on MT7986 and later) can offload flows originating from wireless
devices. In order to make that work, ndo_setup_tc needs to be implemented on
the netdevs. This adds the required code to offload flows coming in from WED,
while keeping track of the incoming wed index used for selecting the correct
PPE device.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -1319,6 +1319,9 @@ int mtk_gmac_rgmii_path_setup(struct mtk
int mtk_eth_offload_init(struct mtk_eth *eth);
int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data);
+int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
+ int ppe_index);
+void mtk_flow_offload_cleanup(struct mtk_eth *eth, struct list_head *list);
void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -235,7 +235,8 @@ out:
}
static int
-mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
+ int ppe_index)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(f);
struct flow_action_entry *act;
@@ -452,6 +453,7 @@ mtk_flow_offload_replace(struct mtk_eth
entry->cookie = f->cookie;
memcpy(&entry->data, &foe, sizeof(entry->data));
entry->wed_index = wed_index;
+ entry->ppe_index = ppe_index;
err = mtk_foe_entry_commit(eth->ppe[entry->ppe_index], entry);
if (err < 0)
@@ -520,25 +522,15 @@ mtk_flow_offload_stats(struct mtk_eth *e
static DEFINE_MUTEX(mtk_flow_offload_mutex);
-static int
-mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
+int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
+ int ppe_index)
{
- struct flow_cls_offload *cls = type_data;
- struct net_device *dev = cb_priv;
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
int err;
- if (!tc_can_offload(dev))
- return -EOPNOTSUPP;
-
- if (type != TC_SETUP_CLSFLOWER)
- return -EOPNOTSUPP;
-
mutex_lock(&mtk_flow_offload_mutex);
switch (cls->command) {
case FLOW_CLS_REPLACE:
- err = mtk_flow_offload_replace(eth, cls);
+ err = mtk_flow_offload_replace(eth, cls, ppe_index);
break;
case FLOW_CLS_DESTROY:
err = mtk_flow_offload_destroy(eth, cls);
@@ -556,6 +548,23 @@ mtk_eth_setup_tc_block_cb(enum tc_setup_
}
static int
+mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
+{
+ struct flow_cls_offload *cls = type_data;
+ struct net_device *dev = cb_priv;
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ if (!tc_can_offload(dev))
+ return -EOPNOTSUPP;
+
+ if (type != TC_SETUP_CLSFLOWER)
+ return -EOPNOTSUPP;
+
+ return mtk_flow_offload_cmd(eth, cls, 0);
+}
+
+static int
mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
{
struct mtk_mac *mac = netdev_priv(dev);
--- a/drivers/net/ethernet/mediatek/mtk_wed.c
+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
@@ -13,6 +13,8 @@
#include <linux/mfd/syscon.h>
#include <linux/debugfs.h>
#include <linux/soc/mediatek/mtk_wed.h>
+#include <net/flow_offload.h>
+#include <net/pkt_cls.h>
#include "mtk_eth_soc.h"
#include "mtk_wed_regs.h"
#include "mtk_wed.h"
@@ -41,6 +43,11 @@
static struct mtk_wed_hw *hw_list[2];
static DEFINE_MUTEX(hw_lock);
+struct mtk_wed_flow_block_priv {
+ struct mtk_wed_hw *hw;
+ struct net_device *dev;
+};
+
static void
wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val)
{
@@ -1752,6 +1759,99 @@ out:
mutex_unlock(&hw_lock);
}
+static int
+mtk_wed_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
+{
+ struct mtk_wed_flow_block_priv *priv = cb_priv;
+ struct flow_cls_offload *cls = type_data;
+ struct mtk_wed_hw *hw = priv->hw;
+
+ if (!tc_can_offload(priv->dev))
+ return -EOPNOTSUPP;
+
+ if (type != TC_SETUP_CLSFLOWER)
+ return -EOPNOTSUPP;
+
+ return mtk_flow_offload_cmd(hw->eth, cls, hw->index);
+}
+
+static int
+mtk_wed_setup_tc_block(struct mtk_wed_hw *hw, struct net_device *dev,
+ struct flow_block_offload *f)
+{
+ struct mtk_wed_flow_block_priv *priv;
+ static LIST_HEAD(block_cb_list);
+ struct flow_block_cb *block_cb;
+ struct mtk_eth *eth = hw->eth;
+ flow_setup_cb_t *cb;
+
+ if (!eth->soc->offload_version)
+ return -EOPNOTSUPP;
+
+ if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+ return -EOPNOTSUPP;
+
+ cb = mtk_wed_setup_tc_block_cb;
+ f->driver_block_list = &block_cb_list;
+
+ switch (f->command) {
+ case FLOW_BLOCK_BIND:
+ block_cb = flow_block_cb_lookup(f->block, cb, dev);
+ if (block_cb) {
+ flow_block_cb_incref(block_cb);
+ return 0;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->hw = hw;
+ priv->dev = dev;
+ block_cb = flow_block_cb_alloc(cb, dev, priv, NULL);
+ if (IS_ERR(block_cb)) {
+ kfree(priv);
+ return PTR_ERR(block_cb);
+ }
+
+ flow_block_cb_incref(block_cb);
+ flow_block_cb_add(block_cb, f);
+ list_add_tail(&block_cb->driver_list, &block_cb_list);
+ return 0;
+ case FLOW_BLOCK_UNBIND:
+ block_cb = flow_block_cb_lookup(f->block, cb, dev);
+ if (!block_cb)
+ return -ENOENT;
+
+ if (!flow_block_cb_decref(block_cb)) {
+ flow_block_cb_remove(block_cb, f);
+ list_del(&block_cb->driver_list);
+ kfree(block_cb->cb_priv);
+ }
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int
+mtk_wed_setup_tc(struct mtk_wed_device *wed, struct net_device *dev,
+ enum tc_setup_type type, void *type_data)
+{
+ struct mtk_wed_hw *hw = wed->hw;
+
+ if (hw->version < 2)
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case TC_SETUP_BLOCK:
+ case TC_SETUP_FT:
+ return mtk_wed_setup_tc_block(hw, dev, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
void __iomem *wdma, phys_addr_t wdma_phy,
int index)
@@ -1771,6 +1871,7 @@ void mtk_wed_add_hw(struct device_node *
.irq_set_mask = mtk_wed_irq_set_mask,
.detach = mtk_wed_detach,
.ppe_check = mtk_wed_ppe_check,
+ .setup_tc = mtk_wed_setup_tc,
};
struct device_node *eth_np = eth->dev->of_node;
struct platform_device *pdev;
--- a/include/linux/soc/mediatek/mtk_wed.h
+++ b/include/linux/soc/mediatek/mtk_wed.h
@@ -6,6 +6,7 @@
#include <linux/regmap.h>
#include <linux/pci.h>
#include <linux/skbuff.h>
+#include <linux/netdevice.h>
#define MTK_WED_TX_QUEUES 2
#define MTK_WED_RX_QUEUES 2
@@ -180,6 +181,8 @@ struct mtk_wed_ops {
u32 (*irq_get)(struct mtk_wed_device *dev, u32 mask);
void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask);
+ int (*setup_tc)(struct mtk_wed_device *wed, struct net_device *dev,
+ enum tc_setup_type type, void *type_data);
};
extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
@@ -238,6 +241,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_devic
(_dev)->ops->msg_update(_dev, _id, _msg, _len)
#define mtk_wed_device_stop(_dev) (_dev)->ops->stop(_dev)
#define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev)
+#define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) \
+ (_dev)->ops->setup_tc(_dev, _netdev, _type, _type_data)
#else
static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
{
@@ -256,6 +261,7 @@ static inline bool mtk_wed_device_active
#define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV
#define mtk_wed_device_stop(_dev) do {} while (0)
#define mtk_wed_device_dma_reset(_dev) do {} while (0)
+#define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) -EOPNOTSUPP
#endif
#endif

View File

@ -0,0 +1,37 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 20 Mar 2023 15:37:55 +0100
Subject: [PATCH] net: ethernet: mediatek: mtk_ppe: prefer newly added l2
flows over existing ones
When a device is roaming between interfaces and a new flow entry is created,
we should assume that its output device is more up to date than whatever
entry existed already.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -639,10 +639,20 @@ void mtk_foe_entry_clear(struct mtk_ppe
static int
mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
{
+ struct mtk_flow_entry *prev;
+
entry->type = MTK_FLOW_TYPE_L2;
- return rhashtable_insert_fast(&ppe->l2_flows, &entry->l2_node,
- mtk_flow_l2_ht_params);
+ prev = rhashtable_lookup_get_insert_fast(&ppe->l2_flows, &entry->l2_node,
+ mtk_flow_l2_ht_params);
+ if (likely(!prev))
+ return 0;
+
+ if (IS_ERR(prev))
+ return PTR_ERR(prev);
+
+ return rhashtable_replace_fast(&ppe->l2_flows, &prev->l2_node,
+ &entry->l2_node, mtk_flow_l2_ht_params);
}
int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)

View File

@ -0,0 +1,314 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Thu, 23 Mar 2023 10:24:11 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: improve keeping track of
offloaded flows
Unify tracking of L2 and L3 flows. Use the generic list field in struct
mtk_foe_entry for tracking L2 subflows. Preparation for improving
flow accounting support.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -466,26 +466,30 @@ int mtk_foe_entry_set_queue(struct mtk_e
return 0;
}
+static int
+mtk_flow_entry_match_len(struct mtk_eth *eth, struct mtk_foe_entry *entry)
+{
+ int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
+
+ if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
+ return offsetof(struct mtk_foe_entry, ipv6._rsv);
+ else
+ return offsetof(struct mtk_foe_entry, ipv4.ib2);
+}
+
static bool
mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry,
- struct mtk_foe_entry *data)
+ struct mtk_foe_entry *data, int len)
{
- int type, len;
-
if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP)
return false;
- type = mtk_get_ib1_pkt_type(eth, entry->data.ib1);
- if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
- len = offsetof(struct mtk_foe_entry, ipv6._rsv);
- else
- len = offsetof(struct mtk_foe_entry, ipv4.ib2);
-
return !memcmp(&entry->data.data, &data->data, len - 4);
}
static void
-__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
+ bool set_state)
{
struct hlist_head *head;
struct hlist_node *tmp;
@@ -495,13 +499,12 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
mtk_flow_l2_ht_params);
head = &entry->l2_flows;
- hlist_for_each_entry_safe(entry, tmp, head, l2_data.list)
- __mtk_foe_entry_clear(ppe, entry);
+ hlist_for_each_entry_safe(entry, tmp, head, list)
+ __mtk_foe_entry_clear(ppe, entry, set_state);
return;
}
- hlist_del_init(&entry->list);
- if (entry->hash != 0xffff) {
+ if (entry->hash != 0xffff && set_state) {
struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash);
hwe->ib1 &= ~MTK_FOE_IB1_STATE;
@@ -520,7 +523,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW)
return;
- hlist_del_init(&entry->l2_data.list);
+ hlist_del_init(&entry->list);
kfree(entry);
}
@@ -536,66 +539,55 @@ static int __mtk_foe_entry_idle_time(str
return now - timestamp;
}
+static bool
+mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+{
+ struct mtk_foe_entry foe = {};
+ struct mtk_foe_entry *hwe;
+ u16 hash = entry->hash;
+ int len;
+
+ if (hash == 0xffff)
+ return false;
+
+ hwe = mtk_foe_get_entry(ppe, hash);
+ len = mtk_flow_entry_match_len(ppe->eth, &entry->data);
+ memcpy(&foe, hwe, len);
+
+ if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) ||
+ FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND)
+ return false;
+
+ entry->data.ib1 = foe.ib1;
+
+ return true;
+}
+
static void
mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
{
u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
struct mtk_flow_entry *cur;
- struct mtk_foe_entry *hwe;
struct hlist_node *tmp;
int idle;
idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
- hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) {
+ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, list) {
int cur_idle;
- u32 ib1;
-
- hwe = mtk_foe_get_entry(ppe, cur->hash);
- ib1 = READ_ONCE(hwe->ib1);
- if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) {
- cur->hash = 0xffff;
- __mtk_foe_entry_clear(ppe, cur);
+ if (!mtk_flow_entry_update(ppe, cur)) {
+ __mtk_foe_entry_clear(ppe, entry, false);
continue;
}
- cur_idle = __mtk_foe_entry_idle_time(ppe, ib1);
+ cur_idle = __mtk_foe_entry_idle_time(ppe, cur->data.ib1);
if (cur_idle >= idle)
continue;
idle = cur_idle;
entry->data.ib1 &= ~ib1_ts_mask;
- entry->data.ib1 |= hwe->ib1 & ib1_ts_mask;
- }
-}
-
-static void
-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
-{
- struct mtk_foe_entry foe = {};
- struct mtk_foe_entry *hwe;
-
- spin_lock_bh(&ppe_lock);
-
- if (entry->type == MTK_FLOW_TYPE_L2) {
- mtk_flow_entry_update_l2(ppe, entry);
- goto out;
+ entry->data.ib1 |= cur->data.ib1 & ib1_ts_mask;
}
-
- if (entry->hash == 0xffff)
- goto out;
-
- hwe = mtk_foe_get_entry(ppe, entry->hash);
- memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size);
- if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) {
- entry->hash = 0xffff;
- goto out;
- }
-
- entry->data.ib1 = foe.ib1;
-
-out:
- spin_unlock_bh(&ppe_lock);
}
static void
@@ -632,7 +624,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
{
spin_lock_bh(&ppe_lock);
- __mtk_foe_entry_clear(ppe, entry);
+ __mtk_foe_entry_clear(ppe, entry, true);
+ hlist_del_init(&entry->list);
spin_unlock_bh(&ppe_lock);
}
@@ -679,8 +672,8 @@ mtk_foe_entry_commit_subflow(struct mtk_
{
const struct mtk_soc_data *soc = ppe->eth->soc;
struct mtk_flow_entry *flow_info;
- struct mtk_foe_entry foe = {}, *hwe;
struct mtk_foe_mac_info *l2;
+ struct mtk_foe_entry *hwe;
u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP;
int type;
@@ -688,30 +681,30 @@ mtk_foe_entry_commit_subflow(struct mtk_
if (!flow_info)
return;
- flow_info->l2_data.base_flow = entry;
flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW;
flow_info->hash = hash;
hlist_add_head(&flow_info->list,
&ppe->foe_flow[hash / soc->hash_offset]);
- hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
+ hlist_add_head(&flow_info->list, &entry->l2_flows);
hwe = mtk_foe_get_entry(ppe, hash);
- memcpy(&foe, hwe, soc->foe_entry_size);
- foe.ib1 &= ib1_mask;
- foe.ib1 |= entry->data.ib1 & ~ib1_mask;
+ memcpy(&flow_info->data, hwe, soc->foe_entry_size);
+ flow_info->data.ib1 &= ib1_mask;
+ flow_info->data.ib1 |= entry->data.ib1 & ~ib1_mask;
- l2 = mtk_foe_entry_l2(ppe->eth, &foe);
+ l2 = mtk_foe_entry_l2(ppe->eth, &flow_info->data);
memcpy(l2, &entry->data.bridge.l2, sizeof(*l2));
- type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1);
+ type = mtk_get_ib1_pkt_type(ppe->eth, flow_info->data.ib1);
if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT)
- memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new));
+ memcpy(&flow_info->data.ipv4.new, &flow_info->data.ipv4.orig,
+ sizeof(flow_info->data.ipv4.new));
else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP)
l2->etype = ETH_P_IPV6;
- *mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2;
+ *mtk_foe_entry_ib2(ppe->eth, &flow_info->data) = entry->data.bridge.ib2;
- __mtk_foe_entry_commit(ppe, &foe, hash);
+ __mtk_foe_entry_commit(ppe, &flow_info->data, hash);
}
void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
@@ -721,9 +714,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe
struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash);
struct mtk_flow_entry *entry;
struct mtk_foe_bridge key = {};
+ struct mtk_foe_entry foe = {};
struct hlist_node *n;
struct ethhdr *eh;
bool found = false;
+ int entry_len;
u8 *tag;
spin_lock_bh(&ppe_lock);
@@ -731,20 +726,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe
if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND)
goto out;
- hlist_for_each_entry_safe(entry, n, head, list) {
- if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
- if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
- MTK_FOE_STATE_BIND))
- continue;
-
- entry->hash = 0xffff;
- __mtk_foe_entry_clear(ppe, entry);
- continue;
- }
+ entry_len = mtk_flow_entry_match_len(ppe->eth, hwe);
+ memcpy(&foe, hwe, entry_len);
- if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) {
+ hlist_for_each_entry_safe(entry, n, head, list) {
+ if (found ||
+ !mtk_flow_entry_match(ppe->eth, entry, &foe, entry_len)) {
if (entry->hash != 0xffff)
- entry->hash = 0xffff;
+ __mtk_foe_entry_clear(ppe, entry, false);
continue;
}
@@ -795,9 +784,17 @@ out:
int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
{
- mtk_flow_entry_update(ppe, entry);
+ int idle;
+
+ spin_lock_bh(&ppe_lock);
+ if (entry->type == MTK_FLOW_TYPE_L2)
+ mtk_flow_entry_update_l2(ppe, entry);
+ else
+ mtk_flow_entry_update(ppe, entry);
+ idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+ spin_unlock_bh(&ppe_lock);
- return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+ return idle;
}
int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
@@ -275,13 +275,7 @@ struct mtk_flow_entry {
s8 wed_index;
u8 ppe_index;
u16 hash;
- union {
- struct mtk_foe_entry data;
- struct {
- struct mtk_flow_entry *base_flow;
- struct hlist_node list;
- } l2_data;
- };
+ struct mtk_foe_entry data;
struct rhash_head node;
unsigned long cookie;
};

View File

@ -0,0 +1,320 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Thu, 23 Mar 2023 11:05:22 +0100
Subject: [PATCH] net: ethernet: mediatek: fix ppe flow accounting for L2
flows
For L2 flows, the packet/byte counters should report the sum of the
counters of their subflows, both current and expired.
In order to make this work, change the way that accounting data is tracked.
Reset counters when a flow enters bind. Once it expires (or enters unbind),
store the last counter value in struct mtk_flow_entry.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -80,9 +80,9 @@ static int mtk_ppe_mib_wait_busy(struct
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);
+ ret = readl_poll_timeout_atomic(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");
@@ -90,18 +90,32 @@ static int mtk_ppe_mib_wait_busy(struct
return ret;
}
-static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
+static inline struct mtk_foe_accounting *
+mtk_ppe_acct_data(struct mtk_ppe *ppe, u16 index)
+{
+ if (!ppe->acct_table)
+ return NULL;
+
+ return ppe->acct_table + index * sizeof(struct mtk_foe_accounting);
+}
+
+struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index)
{
u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
u32 val, cnt_r0, cnt_r1, cnt_r2;
+ struct mtk_foe_accounting *acct;
int ret;
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);
+ acct = mtk_ppe_acct_data(ppe, index);
+ if (!acct)
+ return NULL;
+
ret = mtk_ppe_mib_wait_busy(ppe);
if (ret)
- return ret;
+ return acct;
cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
@@ -111,10 +125,11 @@ static int mtk_mib_entry_read(struct mtk
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;
+ acct->bytes += ((u64)byte_cnt_high << 32) | byte_cnt_low;
+ acct->packets += (pkt_cnt_high << 16) | pkt_cnt_low;
+
+ return acct;
}
static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
@@ -510,13 +525,6 @@ __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;
@@ -540,8 +548,10 @@ static int __mtk_foe_entry_idle_time(str
}
static bool
-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
+ u64 *packets, u64 *bytes)
{
+ struct mtk_foe_accounting *acct;
struct mtk_foe_entry foe = {};
struct mtk_foe_entry *hwe;
u16 hash = entry->hash;
@@ -555,16 +565,29 @@ mtk_flow_entry_update(struct mtk_ppe *pp
memcpy(&foe, hwe, len);
if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) ||
- FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND)
+ FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) {
+ acct = mtk_ppe_acct_data(ppe, hash);
+ if (acct) {
+ entry->packets += acct->packets;
+ entry->bytes += acct->bytes;
+ }
+
return false;
+ }
entry->data.ib1 = foe.ib1;
+ acct = mtk_ppe_mib_entry_read(ppe, hash);
+ if (acct) {
+ *packets += acct->packets;
+ *bytes += acct->bytes;
+ }
return true;
}
static void
-mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
+ u64 *packets, u64 *bytes)
{
u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
struct mtk_flow_entry *cur;
@@ -575,7 +598,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe
hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, list) {
int cur_idle;
- if (!mtk_flow_entry_update(ppe, cur)) {
+ if (!mtk_flow_entry_update(ppe, cur, packets, bytes)) {
+ entry->packets += cur->packets;
+ entry->bytes += cur->bytes;
__mtk_foe_entry_clear(ppe, entry, false);
continue;
}
@@ -590,10 +615,31 @@ mtk_flow_entry_update_l2(struct mtk_ppe
}
}
+void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
+ int *idle, u64 *packets, u64 *bytes)
+{
+ *packets = 0;
+ *bytes = 0;
+
+ spin_lock_bh(&ppe_lock);
+
+ if (entry->type == MTK_FLOW_TYPE_L2)
+ mtk_flow_entry_update_l2(ppe, entry, packets, bytes);
+ else
+ mtk_flow_entry_update(ppe, entry, packets, bytes);
+
+ *packets += entry->packets;
+ *bytes += entry->bytes;
+ *idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+
+ spin_unlock_bh(&ppe_lock);
+}
+
static void
__mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
u16 hash)
{
+ struct mtk_foe_accounting *acct;
struct mtk_eth *eth = ppe->eth;
u16 timestamp = mtk_eth_timestamp(eth);
struct mtk_foe_entry *hwe;
@@ -618,6 +664,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
dma_wmb();
+ acct = mtk_ppe_mib_entry_read(ppe, hash);
+ if (acct) {
+ acct->packets = 0;
+ acct->bytes = 0;
+ }
+
mtk_ppe_cache_clear(ppe);
}
@@ -782,21 +834,6 @@ out:
spin_unlock_bh(&ppe_lock);
}
-int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
-{
- int idle;
-
- spin_lock_bh(&ppe_lock);
- if (entry->type == MTK_FLOW_TYPE_L2)
- mtk_flow_entry_update_l2(ppe, entry);
- else
- mtk_flow_entry_update(ppe, entry);
- idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
- spin_unlock_bh(&ppe_lock);
-
- return idle;
-}
-
int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
{
if (!ppe)
@@ -824,32 +861,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe
return mtk_ppe_wait_busy(ppe);
}
-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 index)
{
bool accounting = eth->soc->has_accounting;
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
@@ -278,6 +278,8 @@ struct mtk_flow_entry {
struct mtk_foe_entry data;
struct rhash_head node;
unsigned long cookie;
+ u64 packets;
+ u64 bytes;
};
struct mtk_mib_entry {
@@ -320,6 +322,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
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);
+struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index);
void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
@@ -368,9 +371,8 @@ int mtk_foe_entry_set_queue(struct mtk_e
unsigned int queue);
int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
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);
+void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
+ int *idle, u64 *packets, u64 *bytes);
#endif
--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
@@ -96,7 +96,7 @@ 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);
+ acct = mtk_ppe_mib_entry_read(ppe, i);
type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
seq_printf(m, "%05x %s %7s", i,
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -499,24 +499,17 @@ 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;
+ int idle;
entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
mtk_flow_ht_params);
if (!entry)
return -ENOENT;
- idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
+ mtk_foe_entry_get_stats(eth->ppe[entry->ppe_index], entry, &idle,
+ &f->stats.pkts, &f->stats.bytes);
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;
}

View File

@ -0,0 +1,21 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Thu, 23 Mar 2023 11:19:14 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: add missing ppe cache flush when
deleting a flow
The cache needs to be flushed to ensure that the hardware stops offloading
the flow immediately.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -525,6 +525,7 @@ __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();
+ mtk_ppe_cache_clear(ppe);
}
entry->hash = 0xffff;

View File

@ -17,7 +17,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -932,12 +932,26 @@ struct clk *clk_register_mux_table(struc
@@ -955,12 +955,26 @@ struct clk *clk_register_mux_table(struc
__clk_hw_register_mux((dev), NULL, (name), (num_parents), NULL, NULL, \
(parent_data), (flags), (reg), (shift), \
BIT((width)) - 1, (clk_mux_flags), NULL, (lock))

View File

@ -21,8 +21,6 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
drivers/net/ethernet/mediatek/mtk_wed_wo.h | 3 +-
2 files changed, 30 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
index 6624f6d6abdd..797c3b412ab6 100644
--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
@@ -18,12 +18,23 @@
@ -51,7 +49,7 @@ index 6624f6d6abdd..797c3b412ab6 100644
}
static struct sk_buff *
@@ -316,10 +327,21 @@ mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo)
@@ -316,10 +327,21 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
return ret;
}
@ -77,8 +75,6 @@ index 6624f6d6abdd..797c3b412ab6 100644
/* set dummy cr */
wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL,
diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.h b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
index dbcf42ce9173..c03071203cc0 100644
--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
@@ -227,7 +227,8 @@ struct mtk_wed_wo_queue {
@ -91,6 +87,3 @@ index dbcf42ce9173..c03071203cc0 100644
struct mtk_wed_wo_queue q_tx;
struct mtk_wed_wo_queue q_rx;
--
2.39.2

View File

@ -18,15 +18,12 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 55 ++++++++++++++++++---
1 file changed, 49 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
index 797c3b412ab6..976946a3653a 100644
--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
@@ -299,6 +299,52 @@ mtk_wed_mcu_run_firmware(struct mtk_wed_wo *wo, const struct firmware *fw,
return -EINVAL;
@@ -300,6 +300,52 @@ next:
}
+static int
static int
+mtk_wed_mcu_load_memory_regions(struct mtk_wed_wo *wo,
+ struct mtk_wed_wo_memory_region *region)
+{
@ -72,10 +69,11 @@ index 797c3b412ab6..976946a3653a 100644
+ &region[MTK_WED_WO_REGION_ILM]);
+}
+
static int
+static int
mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo)
{
@@ -320,12 +366,9 @@ mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo)
static struct mtk_wed_wo_memory_region mem_region[] = {
@@ -320,12 +366,9 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
u32 val, boot_cr;
int ret, i;
@ -91,6 +89,3 @@ index 797c3b412ab6..976946a3653a 100644
wo->boot_regmap = syscon_regmap_lookup_by_phandle(wo->hw->node,
"mediatek,wo-cpuboot");
--
2.39.2

View File

@ -22,7 +22,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/mediatek/mtk_wed.c
+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
@@ -806,6 +806,24 @@ mtk_wed_rro_alloc(struct mtk_wed_device
@@ -813,6 +813,24 @@ mtk_wed_rro_alloc(struct mtk_wed_device
struct device_node *np;
int index;
@ -47,7 +47,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
index = of_property_match_string(dev->hw->node, "memory-region-names",
"wo-dlm");
if (index < 0)
@@ -822,6 +840,7 @@ mtk_wed_rro_alloc(struct mtk_wed_device
@@ -829,6 +847,7 @@ mtk_wed_rro_alloc(struct mtk_wed_device
return -ENODEV;
dev->rro.miod_phys = rmem->base;

View File

@ -16,7 +16,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -272,7 +272,6 @@ image-$(CONFIG_PPC_CHRP) += zImage.chrp
@@ -273,7 +273,6 @@ image-$(CONFIG_PPC_CHRP) += zImage.chrp
image-$(CONFIG_PPC_EFIKA) += zImage.chrp
image-$(CONFIG_PPC_PMAC) += zImage.pmac
image-$(CONFIG_PPC_HOLLY) += dtbImage.holly
@ -24,7 +24,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
image-$(CONFIG_EPAPR_BOOT) += zImage.epapr
#
@@ -404,15 +403,6 @@ $(obj)/dtbImage.%: vmlinux $(wrapperbits
@@ -406,15 +405,6 @@ $(obj)/dtbImage.%: vmlinux $(wrapperbits
$(obj)/vmlinux.strip: vmlinux
$(STRIP) -s -R .comment $< -o $@