162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/if_ether.h> 762306a36Sopenharmony_ci#include <linux/rhashtable.h> 862306a36Sopenharmony_ci#include <linux/ip.h> 962306a36Sopenharmony_ci#include <linux/ipv6.h> 1062306a36Sopenharmony_ci#include <net/flow_offload.h> 1162306a36Sopenharmony_ci#include <net/pkt_cls.h> 1262306a36Sopenharmony_ci#include <net/dsa.h> 1362306a36Sopenharmony_ci#include "mtk_eth_soc.h" 1462306a36Sopenharmony_ci#include "mtk_wed.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct mtk_flow_data { 1762306a36Sopenharmony_ci struct ethhdr eth; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci union { 2062306a36Sopenharmony_ci struct { 2162306a36Sopenharmony_ci __be32 src_addr; 2262306a36Sopenharmony_ci __be32 dst_addr; 2362306a36Sopenharmony_ci } v4; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci struct { 2662306a36Sopenharmony_ci struct in6_addr src_addr; 2762306a36Sopenharmony_ci struct in6_addr dst_addr; 2862306a36Sopenharmony_ci } v6; 2962306a36Sopenharmony_ci }; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci __be16 src_port; 3262306a36Sopenharmony_ci __be16 dst_port; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci u16 vlan_in; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci struct { 3762306a36Sopenharmony_ci u16 id; 3862306a36Sopenharmony_ci __be16 proto; 3962306a36Sopenharmony_ci u8 num; 4062306a36Sopenharmony_ci } vlan; 4162306a36Sopenharmony_ci struct { 4262306a36Sopenharmony_ci u16 sid; 4362306a36Sopenharmony_ci u8 num; 4462306a36Sopenharmony_ci } pppoe; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic const struct rhashtable_params mtk_flow_ht_params = { 4862306a36Sopenharmony_ci .head_offset = offsetof(struct mtk_flow_entry, node), 4962306a36Sopenharmony_ci .key_offset = offsetof(struct mtk_flow_entry, cookie), 5062306a36Sopenharmony_ci .key_len = sizeof(unsigned long), 5162306a36Sopenharmony_ci .automatic_shrinking = true, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic int 5562306a36Sopenharmony_cimtk_flow_set_ipv4_addr(struct mtk_eth *eth, struct mtk_foe_entry *foe, 5662306a36Sopenharmony_ci struct mtk_flow_data *data, bool egress) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci return mtk_foe_entry_set_ipv4_tuple(eth, foe, egress, 5962306a36Sopenharmony_ci data->v4.src_addr, data->src_port, 6062306a36Sopenharmony_ci data->v4.dst_addr, data->dst_port); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int 6462306a36Sopenharmony_cimtk_flow_set_ipv6_addr(struct mtk_eth *eth, struct mtk_foe_entry *foe, 6562306a36Sopenharmony_ci struct mtk_flow_data *data) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return mtk_foe_entry_set_ipv6_tuple(eth, foe, 6862306a36Sopenharmony_ci data->v6.src_addr.s6_addr32, data->src_port, 6962306a36Sopenharmony_ci data->v6.dst_addr.s6_addr32, data->dst_port); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void 7362306a36Sopenharmony_cimtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci void *dest = eth + act->mangle.offset; 7662306a36Sopenharmony_ci const void *src = &act->mangle.val; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (act->mangle.offset > 8) 7962306a36Sopenharmony_ci return; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (act->mangle.mask == 0xffff) { 8262306a36Sopenharmony_ci src += 2; 8362306a36Sopenharmony_ci dest += 2; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci memcpy(dest, src, act->mangle.mask ? 2 : 4); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int 9062306a36Sopenharmony_cimtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct net_device_path_stack stack; 9362306a36Sopenharmony_ci struct net_device_path *path; 9462306a36Sopenharmony_ci int err; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (!dev) 9762306a36Sopenharmony_ci return -ENODEV; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED)) 10062306a36Sopenharmony_ci return -1; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci err = dev_fill_forward_path(dev, addr, &stack); 10362306a36Sopenharmony_ci if (err) 10462306a36Sopenharmony_ci return err; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci path = &stack.path[stack.num_paths - 1]; 10762306a36Sopenharmony_ci if (path->type != DEV_PATH_MTK_WDMA) 10862306a36Sopenharmony_ci return -1; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci info->wdma_idx = path->mtk_wdma.wdma_idx; 11162306a36Sopenharmony_ci info->queue = path->mtk_wdma.queue; 11262306a36Sopenharmony_ci info->bss = path->mtk_wdma.bss; 11362306a36Sopenharmony_ci info->wcid = path->mtk_wdma.wcid; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int 12062306a36Sopenharmony_cimtk_flow_mangle_ports(const struct flow_action_entry *act, 12162306a36Sopenharmony_ci struct mtk_flow_data *data) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci u32 val = ntohl(act->mangle.val); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci switch (act->mangle.offset) { 12662306a36Sopenharmony_ci case 0: 12762306a36Sopenharmony_ci if (act->mangle.mask == ~htonl(0xffff)) 12862306a36Sopenharmony_ci data->dst_port = cpu_to_be16(val); 12962306a36Sopenharmony_ci else 13062306a36Sopenharmony_ci data->src_port = cpu_to_be16(val >> 16); 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case 2: 13362306a36Sopenharmony_ci data->dst_port = cpu_to_be16(val); 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci default: 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int 14362306a36Sopenharmony_cimtk_flow_mangle_ipv4(const struct flow_action_entry *act, 14462306a36Sopenharmony_ci struct mtk_flow_data *data) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci __be32 *dest; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci switch (act->mangle.offset) { 14962306a36Sopenharmony_ci case offsetof(struct iphdr, saddr): 15062306a36Sopenharmony_ci dest = &data->v4.src_addr; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci case offsetof(struct iphdr, daddr): 15362306a36Sopenharmony_ci dest = &data->v4.dst_addr; 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci default: 15662306a36Sopenharmony_ci return -EINVAL; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci memcpy(dest, &act->mangle.val, sizeof(u32)); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int 16562306a36Sopenharmony_cimtk_flow_get_dsa_port(struct net_device **dev) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_DSA) 16862306a36Sopenharmony_ci struct dsa_port *dp; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci dp = dsa_port_from_netdev(*dev); 17162306a36Sopenharmony_ci if (IS_ERR(dp)) 17262306a36Sopenharmony_ci return -ENODEV; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (dp->cpu_dp->tag_ops->proto != DSA_TAG_PROTO_MTK) 17562306a36Sopenharmony_ci return -ENODEV; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci *dev = dsa_port_to_master(dp); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return dp->index; 18062306a36Sopenharmony_ci#else 18162306a36Sopenharmony_ci return -ENODEV; 18262306a36Sopenharmony_ci#endif 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int 18662306a36Sopenharmony_cimtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, 18762306a36Sopenharmony_ci struct net_device *dev, const u8 *dest_mac, 18862306a36Sopenharmony_ci int *wed_index) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct mtk_wdma_info info = {}; 19162306a36Sopenharmony_ci int pse_port, dsa_port, queue; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) { 19462306a36Sopenharmony_ci mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue, 19562306a36Sopenharmony_ci info.bss, info.wcid); 19662306a36Sopenharmony_ci if (mtk_is_netsys_v2_or_greater(eth)) { 19762306a36Sopenharmony_ci switch (info.wdma_idx) { 19862306a36Sopenharmony_ci case 0: 19962306a36Sopenharmony_ci pse_port = 8; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci case 1: 20262306a36Sopenharmony_ci pse_port = 9; 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci default: 20562306a36Sopenharmony_ci return -EINVAL; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci } else { 20862306a36Sopenharmony_ci pse_port = 3; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci *wed_index = info.wdma_idx; 21162306a36Sopenharmony_ci goto out; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci dsa_port = mtk_flow_get_dsa_port(&dev); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (dev == eth->netdev[0]) 21762306a36Sopenharmony_ci pse_port = PSE_GDM1_PORT; 21862306a36Sopenharmony_ci else if (dev == eth->netdev[1]) 21962306a36Sopenharmony_ci pse_port = PSE_GDM2_PORT; 22062306a36Sopenharmony_ci else if (dev == eth->netdev[2]) 22162306a36Sopenharmony_ci pse_port = PSE_GDM3_PORT; 22262306a36Sopenharmony_ci else 22362306a36Sopenharmony_ci return -EOPNOTSUPP; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (dsa_port >= 0) { 22662306a36Sopenharmony_ci mtk_foe_entry_set_dsa(eth, foe, dsa_port); 22762306a36Sopenharmony_ci queue = 3 + dsa_port; 22862306a36Sopenharmony_ci } else { 22962306a36Sopenharmony_ci queue = pse_port - 1; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci mtk_foe_entry_set_queue(eth, foe, queue); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ciout: 23462306a36Sopenharmony_ci mtk_foe_entry_set_pse_port(eth, foe, pse_port); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int 24062306a36Sopenharmony_cimtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f, 24162306a36Sopenharmony_ci int ppe_index) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct flow_rule *rule = flow_cls_offload_flow_rule(f); 24462306a36Sopenharmony_ci struct flow_action_entry *act; 24562306a36Sopenharmony_ci struct mtk_flow_data data = {}; 24662306a36Sopenharmony_ci struct mtk_foe_entry foe; 24762306a36Sopenharmony_ci struct net_device *odev = NULL; 24862306a36Sopenharmony_ci struct mtk_flow_entry *entry; 24962306a36Sopenharmony_ci int offload_type = 0; 25062306a36Sopenharmony_ci int wed_index = -1; 25162306a36Sopenharmony_ci u16 addr_type = 0; 25262306a36Sopenharmony_ci u8 l4proto = 0; 25362306a36Sopenharmony_ci int err = 0; 25462306a36Sopenharmony_ci int i; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (rhashtable_lookup(ð->flow_table, &f->cookie, mtk_flow_ht_params)) 25762306a36Sopenharmony_ci return -EEXIST; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) { 26062306a36Sopenharmony_ci struct flow_match_meta match; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci flow_rule_match_meta(rule, &match); 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci return -EOPNOTSUPP; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { 26862306a36Sopenharmony_ci struct flow_match_control match; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci flow_rule_match_control(rule, &match); 27162306a36Sopenharmony_ci addr_type = match.key->addr_type; 27262306a36Sopenharmony_ci } else { 27362306a36Sopenharmony_ci return -EOPNOTSUPP; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { 27762306a36Sopenharmony_ci struct flow_match_basic match; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci flow_rule_match_basic(rule, &match); 28062306a36Sopenharmony_ci l4proto = match.key->ip_proto; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci return -EOPNOTSUPP; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci switch (addr_type) { 28662306a36Sopenharmony_ci case 0: 28762306a36Sopenharmony_ci offload_type = MTK_PPE_PKT_TYPE_BRIDGE; 28862306a36Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 28962306a36Sopenharmony_ci struct flow_match_eth_addrs match; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci flow_rule_match_eth_addrs(rule, &match); 29262306a36Sopenharmony_ci memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN); 29362306a36Sopenharmony_ci memcpy(data.eth.h_source, match.key->src, ETH_ALEN); 29462306a36Sopenharmony_ci } else { 29562306a36Sopenharmony_ci return -EOPNOTSUPP; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { 29962306a36Sopenharmony_ci struct flow_match_vlan match; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci flow_rule_match_vlan(rule, &match); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (match.key->vlan_tpid != cpu_to_be16(ETH_P_8021Q)) 30462306a36Sopenharmony_ci return -EOPNOTSUPP; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci data.vlan_in = match.key->vlan_id; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci case FLOW_DISSECTOR_KEY_IPV4_ADDRS: 31062306a36Sopenharmony_ci offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT; 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci case FLOW_DISSECTOR_KEY_IPV6_ADDRS: 31362306a36Sopenharmony_ci offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T; 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci default: 31662306a36Sopenharmony_ci return -EOPNOTSUPP; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci flow_action_for_each(i, act, &rule->action) { 32062306a36Sopenharmony_ci switch (act->id) { 32162306a36Sopenharmony_ci case FLOW_ACTION_MANGLE: 32262306a36Sopenharmony_ci if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) 32362306a36Sopenharmony_ci return -EOPNOTSUPP; 32462306a36Sopenharmony_ci if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH) 32562306a36Sopenharmony_ci mtk_flow_offload_mangle_eth(act, &data.eth); 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci case FLOW_ACTION_REDIRECT: 32862306a36Sopenharmony_ci odev = act->dev; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci case FLOW_ACTION_CSUM: 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci case FLOW_ACTION_VLAN_PUSH: 33362306a36Sopenharmony_ci if (data.vlan.num == 1 || 33462306a36Sopenharmony_ci act->vlan.proto != htons(ETH_P_8021Q)) 33562306a36Sopenharmony_ci return -EOPNOTSUPP; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci data.vlan.id = act->vlan.vid; 33862306a36Sopenharmony_ci data.vlan.proto = act->vlan.proto; 33962306a36Sopenharmony_ci data.vlan.num++; 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case FLOW_ACTION_VLAN_POP: 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci case FLOW_ACTION_PPPOE_PUSH: 34462306a36Sopenharmony_ci if (data.pppoe.num == 1) 34562306a36Sopenharmony_ci return -EOPNOTSUPP; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci data.pppoe.sid = act->pppoe.sid; 34862306a36Sopenharmony_ci data.pppoe.num++; 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci default: 35162306a36Sopenharmony_ci return -EOPNOTSUPP; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!is_valid_ether_addr(data.eth.h_source) || 35662306a36Sopenharmony_ci !is_valid_ether_addr(data.eth.h_dest)) 35762306a36Sopenharmony_ci return -EINVAL; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci err = mtk_foe_entry_prepare(eth, &foe, offload_type, l4proto, 0, 36062306a36Sopenharmony_ci data.eth.h_source, data.eth.h_dest); 36162306a36Sopenharmony_ci if (err) 36262306a36Sopenharmony_ci return err; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { 36562306a36Sopenharmony_ci struct flow_match_ports ports; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) 36862306a36Sopenharmony_ci return -EOPNOTSUPP; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci flow_rule_match_ports(rule, &ports); 37162306a36Sopenharmony_ci data.src_port = ports.key->src; 37262306a36Sopenharmony_ci data.dst_port = ports.key->dst; 37362306a36Sopenharmony_ci } else if (offload_type != MTK_PPE_PKT_TYPE_BRIDGE) { 37462306a36Sopenharmony_ci return -EOPNOTSUPP; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 37862306a36Sopenharmony_ci struct flow_match_ipv4_addrs addrs; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci flow_rule_match_ipv4_addrs(rule, &addrs); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci data.v4.src_addr = addrs.key->src; 38362306a36Sopenharmony_ci data.v4.dst_addr = addrs.key->dst; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci mtk_flow_set_ipv4_addr(eth, &foe, &data, false); 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { 38962306a36Sopenharmony_ci struct flow_match_ipv6_addrs addrs; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci flow_rule_match_ipv6_addrs(rule, &addrs); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci data.v6.src_addr = addrs.key->src; 39462306a36Sopenharmony_ci data.v6.dst_addr = addrs.key->dst; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci mtk_flow_set_ipv6_addr(eth, &foe, &data); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci flow_action_for_each(i, act, &rule->action) { 40062306a36Sopenharmony_ci if (act->id != FLOW_ACTION_MANGLE) 40162306a36Sopenharmony_ci continue; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) 40462306a36Sopenharmony_ci return -EOPNOTSUPP; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci switch (act->mangle.htype) { 40762306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_TCP: 40862306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_UDP: 40962306a36Sopenharmony_ci err = mtk_flow_mangle_ports(act, &data); 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_IP4: 41262306a36Sopenharmony_ci err = mtk_flow_mangle_ipv4(act, &data); 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_ETH: 41562306a36Sopenharmony_ci /* handled earlier */ 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci default: 41862306a36Sopenharmony_ci return -EOPNOTSUPP; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (err) 42262306a36Sopenharmony_ci return err; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 42662306a36Sopenharmony_ci err = mtk_flow_set_ipv4_addr(eth, &foe, &data, true); 42762306a36Sopenharmony_ci if (err) 42862306a36Sopenharmony_ci return err; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) 43262306a36Sopenharmony_ci foe.bridge.vlan = data.vlan_in; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (data.vlan.num == 1) { 43562306a36Sopenharmony_ci if (data.vlan.proto != htons(ETH_P_8021Q)) 43662306a36Sopenharmony_ci return -EOPNOTSUPP; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci mtk_foe_entry_set_vlan(eth, &foe, data.vlan.id); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci if (data.pppoe.num == 1) 44162306a36Sopenharmony_ci mtk_foe_entry_set_pppoe(eth, &foe, data.pppoe.sid); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest, 44462306a36Sopenharmony_ci &wed_index); 44562306a36Sopenharmony_ci if (err) 44662306a36Sopenharmony_ci return err; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (wed_index >= 0 && (err = mtk_wed_flow_add(wed_index)) < 0) 44962306a36Sopenharmony_ci return err; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 45262306a36Sopenharmony_ci if (!entry) 45362306a36Sopenharmony_ci return -ENOMEM; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci entry->cookie = f->cookie; 45662306a36Sopenharmony_ci memcpy(&entry->data, &foe, sizeof(entry->data)); 45762306a36Sopenharmony_ci entry->wed_index = wed_index; 45862306a36Sopenharmony_ci entry->ppe_index = ppe_index; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci err = mtk_foe_entry_commit(eth->ppe[entry->ppe_index], entry); 46162306a36Sopenharmony_ci if (err < 0) 46262306a36Sopenharmony_ci goto free; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci err = rhashtable_insert_fast(ð->flow_table, &entry->node, 46562306a36Sopenharmony_ci mtk_flow_ht_params); 46662306a36Sopenharmony_ci if (err < 0) 46762306a36Sopenharmony_ci goto clear; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ciclear: 47262306a36Sopenharmony_ci mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry); 47362306a36Sopenharmony_cifree: 47462306a36Sopenharmony_ci kfree(entry); 47562306a36Sopenharmony_ci if (wed_index >= 0) 47662306a36Sopenharmony_ci mtk_wed_flow_remove(wed_index); 47762306a36Sopenharmony_ci return err; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic int 48162306a36Sopenharmony_cimtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct mtk_flow_entry *entry; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci entry = rhashtable_lookup(ð->flow_table, &f->cookie, 48662306a36Sopenharmony_ci mtk_flow_ht_params); 48762306a36Sopenharmony_ci if (!entry) 48862306a36Sopenharmony_ci return -ENOENT; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry); 49162306a36Sopenharmony_ci rhashtable_remove_fast(ð->flow_table, &entry->node, 49262306a36Sopenharmony_ci mtk_flow_ht_params); 49362306a36Sopenharmony_ci if (entry->wed_index >= 0) 49462306a36Sopenharmony_ci mtk_wed_flow_remove(entry->wed_index); 49562306a36Sopenharmony_ci kfree(entry); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int 50162306a36Sopenharmony_cimtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct mtk_flow_entry *entry; 50462306a36Sopenharmony_ci struct mtk_foe_accounting diff; 50562306a36Sopenharmony_ci u32 idle; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci entry = rhashtable_lookup(ð->flow_table, &f->cookie, 50862306a36Sopenharmony_ci mtk_flow_ht_params); 50962306a36Sopenharmony_ci if (!entry) 51062306a36Sopenharmony_ci return -ENOENT; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry); 51362306a36Sopenharmony_ci f->stats.lastused = jiffies - idle * HZ; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (entry->hash != 0xFFFF && 51662306a36Sopenharmony_ci mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash, 51762306a36Sopenharmony_ci &diff)) { 51862306a36Sopenharmony_ci f->stats.pkts += diff.packets; 51962306a36Sopenharmony_ci f->stats.bytes += diff.bytes; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic DEFINE_MUTEX(mtk_flow_offload_mutex); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ciint mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls, 52862306a36Sopenharmony_ci int ppe_index) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci int err; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci mutex_lock(&mtk_flow_offload_mutex); 53362306a36Sopenharmony_ci switch (cls->command) { 53462306a36Sopenharmony_ci case FLOW_CLS_REPLACE: 53562306a36Sopenharmony_ci err = mtk_flow_offload_replace(eth, cls, ppe_index); 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci case FLOW_CLS_DESTROY: 53862306a36Sopenharmony_ci err = mtk_flow_offload_destroy(eth, cls); 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci case FLOW_CLS_STATS: 54162306a36Sopenharmony_ci err = mtk_flow_offload_stats(eth, cls); 54262306a36Sopenharmony_ci break; 54362306a36Sopenharmony_ci default: 54462306a36Sopenharmony_ci err = -EOPNOTSUPP; 54562306a36Sopenharmony_ci break; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci mutex_unlock(&mtk_flow_offload_mutex); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return err; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int 55362306a36Sopenharmony_cimtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct flow_cls_offload *cls = type_data; 55662306a36Sopenharmony_ci struct net_device *dev = cb_priv; 55762306a36Sopenharmony_ci struct mtk_mac *mac; 55862306a36Sopenharmony_ci struct mtk_eth *eth; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci mac = netdev_priv(dev); 56162306a36Sopenharmony_ci eth = mac->hw; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (!tc_can_offload(dev)) 56462306a36Sopenharmony_ci return -EOPNOTSUPP; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (type != TC_SETUP_CLSFLOWER) 56762306a36Sopenharmony_ci return -EOPNOTSUPP; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return mtk_flow_offload_cmd(eth, cls, 0); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int 57362306a36Sopenharmony_cimtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct mtk_mac *mac = netdev_priv(dev); 57662306a36Sopenharmony_ci struct mtk_eth *eth = mac->hw; 57762306a36Sopenharmony_ci static LIST_HEAD(block_cb_list); 57862306a36Sopenharmony_ci struct flow_block_cb *block_cb; 57962306a36Sopenharmony_ci flow_setup_cb_t *cb; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (!eth->soc->offload_version) 58262306a36Sopenharmony_ci return -EOPNOTSUPP; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 58562306a36Sopenharmony_ci return -EOPNOTSUPP; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci cb = mtk_eth_setup_tc_block_cb; 58862306a36Sopenharmony_ci f->driver_block_list = &block_cb_list; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci switch (f->command) { 59162306a36Sopenharmony_ci case FLOW_BLOCK_BIND: 59262306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, cb, dev); 59362306a36Sopenharmony_ci if (block_cb) { 59462306a36Sopenharmony_ci flow_block_cb_incref(block_cb); 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci block_cb = flow_block_cb_alloc(cb, dev, dev, NULL); 59862306a36Sopenharmony_ci if (IS_ERR(block_cb)) 59962306a36Sopenharmony_ci return PTR_ERR(block_cb); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci flow_block_cb_incref(block_cb); 60262306a36Sopenharmony_ci flow_block_cb_add(block_cb, f); 60362306a36Sopenharmony_ci list_add_tail(&block_cb->driver_list, &block_cb_list); 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci case FLOW_BLOCK_UNBIND: 60662306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, cb, dev); 60762306a36Sopenharmony_ci if (!block_cb) 60862306a36Sopenharmony_ci return -ENOENT; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (!flow_block_cb_decref(block_cb)) { 61162306a36Sopenharmony_ci flow_block_cb_remove(block_cb, f); 61262306a36Sopenharmony_ci list_del(&block_cb->driver_list); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci default: 61662306a36Sopenharmony_ci return -EOPNOTSUPP; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ciint mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, 62162306a36Sopenharmony_ci void *type_data) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci switch (type) { 62462306a36Sopenharmony_ci case TC_SETUP_BLOCK: 62562306a36Sopenharmony_ci case TC_SETUP_FT: 62662306a36Sopenharmony_ci return mtk_eth_setup_tc_block(dev, type_data); 62762306a36Sopenharmony_ci default: 62862306a36Sopenharmony_ci return -EOPNOTSUPP; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ciint mtk_eth_offload_init(struct mtk_eth *eth) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci return rhashtable_init(ð->flow_table, &mtk_flow_ht_params); 63562306a36Sopenharmony_ci} 636