diff options
author | Felix Fietkau <nbd@nbd.name> | 2021-11-30 11:55:24 +0100 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2022-05-06 19:57:57 +0200 |
commit | 77e123340f0b5490905e27ddc92f0dff8ed017a5 (patch) | |
tree | 36398f65a267775cb961539a2a3ad479a6054648 /target/linux/generic/pending-5.10/701-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch | |
parent | 5ff900e0ade775062bf888b447893aefa1a37146 (diff) | |
download | upstream-77e123340f0b5490905e27ddc92f0dff8ed017a5.tar.gz upstream-77e123340f0b5490905e27ddc92f0dff8ed017a5.tar.bz2 upstream-77e123340f0b5490905e27ddc92f0dff8ed017a5.zip |
mediatek: add patches for MT7622 WED (wireless ethernet dispatch)
This series also contains other improvement for hardware flow offload support
Signed-off-by: Felix Fietkau <nbd@nbd.name>
(cherry-picked from commit 0f029b3d2b505b40aca9a24a002838ed1060f83d)
Diffstat (limited to 'target/linux/generic/pending-5.10/701-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch')
-rw-r--r-- | target/linux/generic/pending-5.10/701-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/target/linux/generic/pending-5.10/701-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch b/target/linux/generic/pending-5.10/701-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch new file mode 100644 index 0000000000..99e597a3d1 --- /dev/null +++ b/target/linux/generic/pending-5.10/701-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch @@ -0,0 +1,553 @@ +From: Felix Fietkau <nbd@nbd.name> +Date: Wed, 23 Feb 2022 10:56:34 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: support creating mac + address based offload entries + +This will be used to implement a limited form of bridge offloading. +Since the hardware does not support flow table entries with just source +and destination MAC address, the driver has to emulate it. + +The hardware automatically creates entries entries for incoming flows, even +when they are bridged instead of routed, and reports when packets for these +flows have reached the minimum PPS rate for offloading. + +After this happens, we look up the L2 flow offload entry based on the MAC +header and fill in the output routing information in the flow table. +The dynamically created per-flow entries are automatically removed when +either the hardware flowtable entry expires, is replaced, or if the offload +rule they belong to is removed + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -6,12 +6,22 @@ + #include <linux/iopoll.h> + #include <linux/etherdevice.h> + #include <linux/platform_device.h> ++#include <linux/if_ether.h> ++#include <linux/if_vlan.h> ++#include <net/dsa.h> + #include "mtk_eth_soc.h" + #include "mtk_ppe.h" + #include "mtk_ppe_regs.h" + + static DEFINE_SPINLOCK(ppe_lock); + ++static const struct rhashtable_params mtk_flow_l2_ht_params = { ++ .head_offset = offsetof(struct mtk_flow_entry, l2_node), ++ .key_offset = offsetof(struct mtk_flow_entry, data.bridge), ++ .key_len = offsetof(struct mtk_foe_bridge, key_end), ++ .automatic_shrinking = true, ++}; ++ + static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val) + { + writel(val, ppe->base + reg); +@@ -123,6 +133,9 @@ mtk_foe_entry_l2(struct mtk_foe_entry *e + { + int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); + ++ if (type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return &entry->bridge.l2; ++ + if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) + return &entry->ipv6.l2; + +@@ -134,6 +147,9 @@ mtk_foe_entry_ib2(struct mtk_foe_entry * + { + int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); + ++ if (type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return &entry->bridge.ib2; ++ + if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) + return &entry->ipv6.ib2; + +@@ -168,7 +184,12 @@ int mtk_foe_entry_prepare(struct mtk_foe + if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T) + entry->ipv6.ports = ports_pad; + +- if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) { ++ if (type == MTK_PPE_PKT_TYPE_BRIDGE) { ++ ether_addr_copy(entry->bridge.src_mac, src_mac); ++ ether_addr_copy(entry->bridge.dest_mac, dest_mac); ++ entry->bridge.ib2 = val; ++ l2 = &entry->bridge.l2; ++ } else if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) { + entry->ipv6.ib2 = val; + l2 = &entry->ipv6.l2; + } else { +@@ -372,12 +393,96 @@ mtk_flow_entry_match(struct mtk_flow_ent + } + + static void ++__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ struct hlist_head *head; ++ struct hlist_node *tmp; ++ ++ if (entry->type == MTK_FLOW_TYPE_L2) { ++ rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node, ++ 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); ++ return; ++ } ++ ++ hlist_del_init(&entry->list); ++ if (entry->hash != 0xffff) { ++ ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE; ++ ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, ++ MTK_FOE_STATE_BIND); ++ dma_wmb(); ++ } ++ entry->hash = 0xffff; ++ ++ if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW) ++ return; ++ ++ hlist_del_init(&entry->l2_data.list); ++ kfree(entry); ++} ++ ++static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1) ++{ ++ u16 timestamp; ++ u16 now; ++ ++ now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP; ++ timestamp = ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; ++ ++ if (timestamp > now) ++ return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now; ++ else ++ return now - timestamp; ++} ++ ++static void ++mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ 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) { ++ int cur_idle; ++ u32 ib1; ++ ++ hwe = &ppe->foe_table[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); ++ continue; ++ } ++ ++ cur_idle = __mtk_foe_entry_idle_time(ppe, ib1); ++ if (cur_idle >= idle) ++ continue; ++ ++ idle = cur_idle; ++ entry->data.ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; ++ entry->data.ib1 |= hwe->ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; ++ } ++} ++ ++static void + mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + struct mtk_foe_entry *hwe; + struct mtk_foe_entry foe; + + spin_lock_bh(&ppe_lock); ++ ++ if (entry->type == MTK_FLOW_TYPE_L2) { ++ mtk_flow_entry_update_l2(ppe, entry); ++ goto out; ++ } ++ + if (entry->hash == 0xffff) + goto out; + +@@ -419,21 +524,28 @@ __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); +- hlist_del_init(&entry->list); +- if (entry->hash != 0xffff) { +- ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE; +- ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, +- MTK_FOE_STATE_BIND); +- dma_wmb(); +- } +- entry->hash = 0xffff; ++ __mtk_foe_entry_clear(ppe, entry); + spin_unlock_bh(&ppe_lock); + } + ++static int ++mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ entry->type = MTK_FLOW_TYPE_L2; ++ ++ return rhashtable_insert_fast(&ppe->l2_flows, &entry->l2_node, ++ mtk_flow_l2_ht_params); ++} ++ + int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { +- u32 hash = mtk_ppe_hash_entry(&entry->data); ++ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1); ++ u32 hash; ++ ++ if (type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return mtk_foe_entry_commit_l2(ppe, entry); + ++ hash = mtk_ppe_hash_entry(&entry->data); + entry->hash = 0xffff; + spin_lock_bh(&ppe_lock); + hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]); +@@ -442,18 +554,72 @@ int mtk_foe_entry_commit(struct mtk_ppe + return 0; + } + ++static void ++mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ u16 hash) ++{ ++ struct mtk_flow_entry *flow_info; ++ struct mtk_foe_entry foe, *hwe; ++ struct mtk_foe_mac_info *l2; ++ u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP; ++ int type; ++ ++ flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end), ++ GFP_ATOMIC); ++ 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 / 2]); ++ hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); ++ ++ hwe = &ppe->foe_table[hash]; ++ memcpy(&foe, hwe, sizeof(foe)); ++ foe.ib1 &= ib1_mask; ++ foe.ib1 |= entry->data.ib1 & ~ib1_mask; ++ ++ l2 = mtk_foe_entry_l2(&foe); ++ memcpy(l2, &entry->data.bridge.l2, sizeof(*l2)); ++ ++ type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, foe.ib1); ++ if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT) ++ memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.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(&foe) = entry->data.bridge.ib2; ++ ++ __mtk_foe_entry_commit(ppe, &foe, hash); ++} ++ + void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) + { + struct hlist_head *head = &ppe->foe_flow[hash / 2]; +- struct mtk_flow_entry *entry; + struct mtk_foe_entry *hwe = &ppe->foe_table[hash]; ++ struct mtk_flow_entry *entry; ++ struct mtk_foe_bridge key = {}; ++ struct ethhdr *eh; + bool found = false; +- +- if (hlist_empty(head)) +- return; ++ u8 *tag; + + spin_lock_bh(&ppe_lock); ++ ++ if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND) ++ goto out; ++ + hlist_for_each_entry(entry, 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; ++ } ++ + if (found || !mtk_flow_entry_match(entry, hwe)) { + if (entry->hash != 0xffff) + entry->hash = 0xffff; +@@ -464,21 +630,50 @@ void __mtk_ppe_check_skb(struct mtk_ppe + __mtk_foe_entry_commit(ppe, &entry->data, hash); + found = true; + } ++ ++ if (found) ++ goto out; ++ ++ eh = eth_hdr(skb); ++ ether_addr_copy(key.dest_mac, eh->h_dest); ++ ether_addr_copy(key.src_mac, eh->h_source); ++ tag = skb->data - 2; ++ key.vlan = 0; ++ switch (skb->protocol) { ++#if IS_ENABLED(CONFIG_NET_DSA) ++ case htons(ETH_P_XDSA): ++ if (!netdev_uses_dsa(skb->dev) || ++ skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK) ++ goto out; ++ ++ tag += 4; ++ if (get_unaligned_be16(tag) != ETH_P_8021Q) ++ break; ++ ++ fallthrough; ++#endif ++ case htons(ETH_P_8021Q): ++ key.vlan = get_unaligned_be16(tag + 2) & VLAN_VID_MASK; ++ break; ++ default: ++ break; ++ } ++ ++ entry = rhashtable_lookup_fast(&ppe->l2_flows, &key, mtk_flow_l2_ht_params); ++ if (!entry) ++ goto out; ++ ++ mtk_foe_entry_commit_subflow(ppe, entry, hash); ++ ++out: + spin_unlock_bh(&ppe_lock); + } + + int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { +- u16 now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP; +- u16 timestamp; +- + mtk_flow_entry_update(ppe, entry); +- timestamp = entry->data.ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; + +- if (timestamp > now) +- return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now; +- else +- return now - timestamp; ++ return __mtk_foe_entry_idle_time(ppe, entry->data.ib1); + } + + struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, +@@ -492,6 +687,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ + if (!ppe) + return NULL; + ++ rhashtable_init(&ppe->l2_flows, &mtk_flow_l2_ht_params); ++ + /* need to allocate a separate device, since it PPE DMA access is + * not coherent. + */ +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -6,6 +6,7 @@ + + #include <linux/kernel.h> + #include <linux/bitfield.h> ++#include <linux/rhashtable.h> + + #define MTK_ETH_PPE_BASE 0xc00 + +@@ -84,19 +85,16 @@ struct mtk_foe_mac_info { + u16 src_mac_lo; + }; + ++/* software-only entry type */ + struct mtk_foe_bridge { +- u32 dest_mac_hi; +- +- u16 src_mac_lo; +- u16 dest_mac_lo; ++ u8 dest_mac[ETH_ALEN]; ++ u8 src_mac[ETH_ALEN]; ++ u16 vlan; + +- u32 src_mac_hi; ++ struct {} key_end; + + u32 ib2; + +- u32 _rsv[5]; +- +- u32 udf_tsid; + struct mtk_foe_mac_info l2; + }; + +@@ -235,13 +233,33 @@ enum { + MTK_PPE_CPU_REASON_INVALID = 0x1f, + }; + ++enum { ++ MTK_FLOW_TYPE_L4, ++ MTK_FLOW_TYPE_L2, ++ MTK_FLOW_TYPE_L2_SUBFLOW, ++}; ++ + struct mtk_flow_entry { ++ union { ++ struct hlist_node list; ++ struct { ++ struct rhash_head l2_node; ++ struct hlist_head l2_flows; ++ }; ++ }; ++ u8 type; ++ s8 wed_index; ++ u16 hash; ++ union { ++ struct mtk_foe_entry data; ++ struct { ++ struct mtk_flow_entry *base_flow; ++ struct hlist_node list; ++ struct {} end; ++ } l2_data; ++ }; + struct rhash_head node; +- struct hlist_node list; + unsigned long cookie; +- struct mtk_foe_entry data; +- u16 hash; +- s8 wed_index; + }; + + struct mtk_ppe { +@@ -256,6 +274,8 @@ struct mtk_ppe { + u16 foe_check_time[MTK_PPE_ENTRIES]; + struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2]; + ++ struct rhashtable l2_flows; ++ + void *acct_table; + }; + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -32,6 +32,8 @@ struct mtk_flow_data { + __be16 src_port; + __be16 dst_port; + ++ u16 vlan_in; ++ + struct { + u16 id; + __be16 proto; +@@ -258,9 +260,45 @@ mtk_flow_offload_replace(struct mtk_eth + return -EOPNOTSUPP; + } + ++ switch (addr_type) { ++ case 0: ++ offload_type = MTK_PPE_PKT_TYPE_BRIDGE; ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { ++ struct flow_match_eth_addrs match; ++ ++ flow_rule_match_eth_addrs(rule, &match); ++ memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN); ++ memcpy(data.eth.h_source, match.key->src, ETH_ALEN); ++ } else { ++ return -EOPNOTSUPP; ++ } ++ ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { ++ struct flow_match_vlan match; ++ ++ flow_rule_match_vlan(rule, &match); ++ ++ if (match.key->vlan_tpid != cpu_to_be16(ETH_P_8021Q)) ++ return -EOPNOTSUPP; ++ ++ data.vlan_in = match.key->vlan_id; ++ } ++ break; ++ case FLOW_DISSECTOR_KEY_IPV4_ADDRS: ++ offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT; ++ break; ++ case FLOW_DISSECTOR_KEY_IPV6_ADDRS: ++ offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ + flow_action_for_each(i, act, &rule->action) { + switch (act->id) { + case FLOW_ACTION_MANGLE: ++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return -EOPNOTSUPP; + if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH) + mtk_flow_offload_mangle_eth(act, &data.eth); + break; +@@ -292,17 +330,6 @@ mtk_flow_offload_replace(struct mtk_eth + } + } + +- switch (addr_type) { +- case FLOW_DISSECTOR_KEY_IPV4_ADDRS: +- offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT; +- break; +- case FLOW_DISSECTOR_KEY_IPV6_ADDRS: +- offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T; +- break; +- default: +- return -EOPNOTSUPP; +- } +- + if (!is_valid_ether_addr(data.eth.h_source) || + !is_valid_ether_addr(data.eth.h_dest)) + return -EINVAL; +@@ -316,10 +343,13 @@ mtk_flow_offload_replace(struct mtk_eth + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_match_ports ports; + ++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return -EOPNOTSUPP; ++ + flow_rule_match_ports(rule, &ports); + data.src_port = ports.key->src; + data.dst_port = ports.key->dst; +- } else { ++ } else if (offload_type != MTK_PPE_PKT_TYPE_BRIDGE) { + return -EOPNOTSUPP; + } + +@@ -349,6 +379,9 @@ mtk_flow_offload_replace(struct mtk_eth + if (act->id != FLOW_ACTION_MANGLE) + continue; + ++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return -EOPNOTSUPP; ++ + switch (act->mangle.htype) { + case FLOW_ACT_MANGLE_HDR_TYPE_TCP: + case FLOW_ACT_MANGLE_HDR_TYPE_UDP: +@@ -374,6 +407,9 @@ mtk_flow_offload_replace(struct mtk_eth + return err; + } + ++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) ++ foe.bridge.vlan = data.vlan_in; ++ + if (data.vlan.num == 1) { + if (data.vlan.proto != htons(ETH_P_8021Q)) + return -EOPNOTSUPP; |