123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- From: Felix Fietkau <[email protected]>
- 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 <[email protected]>
- ---
- --- 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
- @@ -31,6 +31,8 @@ struct mtk_flow_data {
- __be16 src_port;
- __be16 dst_port;
-
- + u16 vlan_in;
- +
- struct {
- u16 id;
- __be16 proto;
- @@ -257,9 +259,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;
- @@ -291,17 +329,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;
- @@ -315,10 +342,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;
- }
-
- @@ -348,6 +378,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:
- @@ -373,6 +406,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;
|