18c2ecf20Sopenharmony_ci/* Broadcom NetXtreme-C/E network driver. 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * Copyright (c) 2017 Broadcom Limited 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 68c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 78c2ecf20Sopenharmony_ci * the Free Software Foundation. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 118c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 138c2ecf20Sopenharmony_ci#include <net/flow_dissector.h> 148c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 158c2ecf20Sopenharmony_ci#include <net/tc_act/tc_gact.h> 168c2ecf20Sopenharmony_ci#include <net/tc_act/tc_skbedit.h> 178c2ecf20Sopenharmony_ci#include <net/tc_act/tc_mirred.h> 188c2ecf20Sopenharmony_ci#include <net/tc_act/tc_vlan.h> 198c2ecf20Sopenharmony_ci#include <net/tc_act/tc_pedit.h> 208c2ecf20Sopenharmony_ci#include <net/tc_act/tc_tunnel_key.h> 218c2ecf20Sopenharmony_ci#include <net/vxlan.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "bnxt_hsi.h" 248c2ecf20Sopenharmony_ci#include "bnxt.h" 258c2ecf20Sopenharmony_ci#include "bnxt_sriov.h" 268c2ecf20Sopenharmony_ci#include "bnxt_tc.h" 278c2ecf20Sopenharmony_ci#include "bnxt_vfr.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define BNXT_FID_INVALID 0xffff 308c2ecf20Sopenharmony_ci#define VLAN_TCI(vid, prio) ((vid) | ((prio) << VLAN_PRIO_SHIFT)) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define is_vlan_pcp_wildcarded(vlan_tci_mask) \ 338c2ecf20Sopenharmony_ci ((ntohs(vlan_tci_mask) & VLAN_PRIO_MASK) == 0x0000) 348c2ecf20Sopenharmony_ci#define is_vlan_pcp_exactmatch(vlan_tci_mask) \ 358c2ecf20Sopenharmony_ci ((ntohs(vlan_tci_mask) & VLAN_PRIO_MASK) == VLAN_PRIO_MASK) 368c2ecf20Sopenharmony_ci#define is_vlan_pcp_zero(vlan_tci) \ 378c2ecf20Sopenharmony_ci ((ntohs(vlan_tci) & VLAN_PRIO_MASK) == 0x0000) 388c2ecf20Sopenharmony_ci#define is_vid_exactmatch(vlan_tci_mask) \ 398c2ecf20Sopenharmony_ci ((ntohs(vlan_tci_mask) & VLAN_VID_MASK) == VLAN_VID_MASK) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic bool is_wildcard(void *mask, int len); 428c2ecf20Sopenharmony_cistatic bool is_exactmatch(void *mask, int len); 438c2ecf20Sopenharmony_ci/* Return the dst fid of the func for flow forwarding 448c2ecf20Sopenharmony_ci * For PFs: src_fid is the fid of the PF 458c2ecf20Sopenharmony_ci * For VF-reps: src_fid the fid of the VF 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_cistatic u16 bnxt_flow_get_dst_fid(struct bnxt *pf_bp, struct net_device *dev) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct bnxt *bp; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* check if dev belongs to the same switch */ 528c2ecf20Sopenharmony_ci if (!netdev_port_same_parent_id(pf_bp->dev, dev)) { 538c2ecf20Sopenharmony_ci netdev_info(pf_bp->dev, "dev(ifindex=%d) not on same switch\n", 548c2ecf20Sopenharmony_ci dev->ifindex); 558c2ecf20Sopenharmony_ci return BNXT_FID_INVALID; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Is dev a VF-rep? */ 598c2ecf20Sopenharmony_ci if (bnxt_dev_is_vf_rep(dev)) 608c2ecf20Sopenharmony_ci return bnxt_vf_rep_get_fid(dev); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci bp = netdev_priv(dev); 638c2ecf20Sopenharmony_ci return bp->pf.fw_fid; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int bnxt_tc_parse_redir(struct bnxt *bp, 678c2ecf20Sopenharmony_ci struct bnxt_tc_actions *actions, 688c2ecf20Sopenharmony_ci const struct flow_action_entry *act) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct net_device *dev = act->dev; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (!dev) { 738c2ecf20Sopenharmony_ci netdev_info(bp->dev, "no dev in mirred action\n"); 748c2ecf20Sopenharmony_ci return -EINVAL; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci actions->flags |= BNXT_TC_ACTION_FLAG_FWD; 788c2ecf20Sopenharmony_ci actions->dst_dev = dev; 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int bnxt_tc_parse_vlan(struct bnxt *bp, 838c2ecf20Sopenharmony_ci struct bnxt_tc_actions *actions, 848c2ecf20Sopenharmony_ci const struct flow_action_entry *act) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci switch (act->id) { 878c2ecf20Sopenharmony_ci case FLOW_ACTION_VLAN_POP: 888c2ecf20Sopenharmony_ci actions->flags |= BNXT_TC_ACTION_FLAG_POP_VLAN; 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci case FLOW_ACTION_VLAN_PUSH: 918c2ecf20Sopenharmony_ci actions->flags |= BNXT_TC_ACTION_FLAG_PUSH_VLAN; 928c2ecf20Sopenharmony_ci actions->push_vlan_tci = htons(act->vlan.vid); 938c2ecf20Sopenharmony_ci actions->push_vlan_tpid = act->vlan.proto; 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci default: 968c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int bnxt_tc_parse_tunnel_set(struct bnxt *bp, 1028c2ecf20Sopenharmony_ci struct bnxt_tc_actions *actions, 1038c2ecf20Sopenharmony_ci const struct flow_action_entry *act) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci const struct ip_tunnel_info *tun_info = act->tunnel; 1068c2ecf20Sopenharmony_ci const struct ip_tunnel_key *tun_key = &tun_info->key; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (ip_tunnel_info_af(tun_info) != AF_INET) { 1098c2ecf20Sopenharmony_ci netdev_info(bp->dev, "only IPv4 tunnel-encap is supported\n"); 1108c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci actions->tun_encap_key = *tun_key; 1148c2ecf20Sopenharmony_ci actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP; 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* Key & Mask from the stack comes unaligned in multiple iterations of 4 bytes 1198c2ecf20Sopenharmony_ci * each(u32). 1208c2ecf20Sopenharmony_ci * This routine consolidates such multiple unaligned values into one 1218c2ecf20Sopenharmony_ci * field each for Key & Mask (for src and dst macs separately) 1228c2ecf20Sopenharmony_ci * For example, 1238c2ecf20Sopenharmony_ci * Mask/Key Offset Iteration 1248c2ecf20Sopenharmony_ci * ========== ====== ========= 1258c2ecf20Sopenharmony_ci * dst mac 0xffffffff 0 1 1268c2ecf20Sopenharmony_ci * dst mac 0x0000ffff 4 2 1278c2ecf20Sopenharmony_ci * 1288c2ecf20Sopenharmony_ci * src mac 0xffff0000 4 1 1298c2ecf20Sopenharmony_ci * src mac 0xffffffff 8 2 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * The above combination coming from the stack will be consolidated as 1328c2ecf20Sopenharmony_ci * Mask/Key 1338c2ecf20Sopenharmony_ci * ============== 1348c2ecf20Sopenharmony_ci * src mac: 0xffffffffffff 1358c2ecf20Sopenharmony_ci * dst mac: 0xffffffffffff 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_cistatic void bnxt_set_l2_key_mask(u32 part_key, u32 part_mask, 1388c2ecf20Sopenharmony_ci u8 *actual_key, u8 *actual_mask) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci u32 key = get_unaligned((u32 *)actual_key); 1418c2ecf20Sopenharmony_ci u32 mask = get_unaligned((u32 *)actual_mask); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci part_key &= part_mask; 1448c2ecf20Sopenharmony_ci part_key |= key & ~part_mask; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci put_unaligned(mask | part_mask, (u32 *)actual_mask); 1478c2ecf20Sopenharmony_ci put_unaligned(part_key, (u32 *)actual_key); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int 1518c2ecf20Sopenharmony_cibnxt_fill_l2_rewrite_fields(struct bnxt_tc_actions *actions, 1528c2ecf20Sopenharmony_ci u16 *eth_addr, u16 *eth_addr_mask) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci u16 *p; 1558c2ecf20Sopenharmony_ci int j; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (unlikely(bnxt_eth_addr_key_mask_invalid(eth_addr, eth_addr_mask))) 1588c2ecf20Sopenharmony_ci return -EINVAL; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!is_wildcard(ð_addr_mask[0], ETH_ALEN)) { 1618c2ecf20Sopenharmony_ci if (!is_exactmatch(ð_addr_mask[0], ETH_ALEN)) 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci /* FW expects dmac to be in u16 array format */ 1648c2ecf20Sopenharmony_ci p = eth_addr; 1658c2ecf20Sopenharmony_ci for (j = 0; j < 3; j++) 1668c2ecf20Sopenharmony_ci actions->l2_rewrite_dmac[j] = cpu_to_be16(*(p + j)); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!is_wildcard(ð_addr_mask[ETH_ALEN / 2], ETH_ALEN)) { 1708c2ecf20Sopenharmony_ci if (!is_exactmatch(ð_addr_mask[ETH_ALEN / 2], ETH_ALEN)) 1718c2ecf20Sopenharmony_ci return -EINVAL; 1728c2ecf20Sopenharmony_ci /* FW expects smac to be in u16 array format */ 1738c2ecf20Sopenharmony_ci p = ð_addr[ETH_ALEN / 2]; 1748c2ecf20Sopenharmony_ci for (j = 0; j < 3; j++) 1758c2ecf20Sopenharmony_ci actions->l2_rewrite_smac[j] = cpu_to_be16(*(p + j)); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int 1828c2ecf20Sopenharmony_cibnxt_tc_parse_pedit(struct bnxt *bp, struct bnxt_tc_actions *actions, 1838c2ecf20Sopenharmony_ci struct flow_action_entry *act, int act_idx, u8 *eth_addr, 1848c2ecf20Sopenharmony_ci u8 *eth_addr_mask) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci size_t offset_of_ip6_daddr = offsetof(struct ipv6hdr, daddr); 1878c2ecf20Sopenharmony_ci size_t offset_of_ip6_saddr = offsetof(struct ipv6hdr, saddr); 1888c2ecf20Sopenharmony_ci u32 mask, val, offset, idx; 1898c2ecf20Sopenharmony_ci u8 htype; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci offset = act->mangle.offset; 1928c2ecf20Sopenharmony_ci htype = act->mangle.htype; 1938c2ecf20Sopenharmony_ci mask = ~act->mangle.mask; 1948c2ecf20Sopenharmony_ci val = act->mangle.val; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci switch (htype) { 1978c2ecf20Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_ETH: 1988c2ecf20Sopenharmony_ci if (offset > PEDIT_OFFSET_SMAC_LAST_4_BYTES) { 1998c2ecf20Sopenharmony_ci netdev_err(bp->dev, 2008c2ecf20Sopenharmony_ci "%s: eth_hdr: Invalid pedit field\n", 2018c2ecf20Sopenharmony_ci __func__); 2028c2ecf20Sopenharmony_ci return -EINVAL; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci actions->flags |= BNXT_TC_ACTION_FLAG_L2_REWRITE; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci bnxt_set_l2_key_mask(val, mask, ð_addr[offset], 2078c2ecf20Sopenharmony_ci ð_addr_mask[offset]); 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_IP4: 2108c2ecf20Sopenharmony_ci actions->flags |= BNXT_TC_ACTION_FLAG_NAT_XLATE; 2118c2ecf20Sopenharmony_ci actions->nat.l3_is_ipv4 = true; 2128c2ecf20Sopenharmony_ci if (offset == offsetof(struct iphdr, saddr)) { 2138c2ecf20Sopenharmony_ci actions->nat.src_xlate = true; 2148c2ecf20Sopenharmony_ci actions->nat.l3.ipv4.saddr.s_addr = htonl(val); 2158c2ecf20Sopenharmony_ci } else if (offset == offsetof(struct iphdr, daddr)) { 2168c2ecf20Sopenharmony_ci actions->nat.src_xlate = false; 2178c2ecf20Sopenharmony_ci actions->nat.l3.ipv4.daddr.s_addr = htonl(val); 2188c2ecf20Sopenharmony_ci } else { 2198c2ecf20Sopenharmony_ci netdev_err(bp->dev, 2208c2ecf20Sopenharmony_ci "%s: IPv4_hdr: Invalid pedit field\n", 2218c2ecf20Sopenharmony_ci __func__); 2228c2ecf20Sopenharmony_ci return -EINVAL; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci netdev_dbg(bp->dev, "nat.src_xlate = %d src IP: %pI4 dst ip : %pI4\n", 2268c2ecf20Sopenharmony_ci actions->nat.src_xlate, &actions->nat.l3.ipv4.saddr, 2278c2ecf20Sopenharmony_ci &actions->nat.l3.ipv4.daddr); 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_IP6: 2318c2ecf20Sopenharmony_ci actions->flags |= BNXT_TC_ACTION_FLAG_NAT_XLATE; 2328c2ecf20Sopenharmony_ci actions->nat.l3_is_ipv4 = false; 2338c2ecf20Sopenharmony_ci if (offset >= offsetof(struct ipv6hdr, saddr) && 2348c2ecf20Sopenharmony_ci offset < offset_of_ip6_daddr) { 2358c2ecf20Sopenharmony_ci /* 16 byte IPv6 address comes in 4 iterations of 2368c2ecf20Sopenharmony_ci * 4byte chunks each 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci actions->nat.src_xlate = true; 2398c2ecf20Sopenharmony_ci idx = (offset - offset_of_ip6_saddr) / 4; 2408c2ecf20Sopenharmony_ci /* First 4bytes will be copied to idx 0 and so on */ 2418c2ecf20Sopenharmony_ci actions->nat.l3.ipv6.saddr.s6_addr32[idx] = htonl(val); 2428c2ecf20Sopenharmony_ci } else if (offset >= offset_of_ip6_daddr && 2438c2ecf20Sopenharmony_ci offset < offset_of_ip6_daddr + 16) { 2448c2ecf20Sopenharmony_ci actions->nat.src_xlate = false; 2458c2ecf20Sopenharmony_ci idx = (offset - offset_of_ip6_daddr) / 4; 2468c2ecf20Sopenharmony_ci actions->nat.l3.ipv6.saddr.s6_addr32[idx] = htonl(val); 2478c2ecf20Sopenharmony_ci } else { 2488c2ecf20Sopenharmony_ci netdev_err(bp->dev, 2498c2ecf20Sopenharmony_ci "%s: IPv6_hdr: Invalid pedit field\n", 2508c2ecf20Sopenharmony_ci __func__); 2518c2ecf20Sopenharmony_ci return -EINVAL; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_TCP: 2558c2ecf20Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_UDP: 2568c2ecf20Sopenharmony_ci /* HW does not support L4 rewrite alone without L3 2578c2ecf20Sopenharmony_ci * rewrite 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci if (!(actions->flags & BNXT_TC_ACTION_FLAG_NAT_XLATE)) { 2608c2ecf20Sopenharmony_ci netdev_err(bp->dev, 2618c2ecf20Sopenharmony_ci "Need to specify L3 rewrite as well\n"); 2628c2ecf20Sopenharmony_ci return -EINVAL; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci if (actions->nat.src_xlate) 2658c2ecf20Sopenharmony_ci actions->nat.l4.ports.sport = htons(val); 2668c2ecf20Sopenharmony_ci else 2678c2ecf20Sopenharmony_ci actions->nat.l4.ports.dport = htons(val); 2688c2ecf20Sopenharmony_ci netdev_dbg(bp->dev, "actions->nat.sport = %d dport = %d\n", 2698c2ecf20Sopenharmony_ci actions->nat.l4.ports.sport, 2708c2ecf20Sopenharmony_ci actions->nat.l4.ports.dport); 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci default: 2738c2ecf20Sopenharmony_ci netdev_err(bp->dev, "%s: Unsupported pedit hdr type\n", 2748c2ecf20Sopenharmony_ci __func__); 2758c2ecf20Sopenharmony_ci return -EINVAL; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int bnxt_tc_parse_actions(struct bnxt *bp, 2818c2ecf20Sopenharmony_ci struct bnxt_tc_actions *actions, 2828c2ecf20Sopenharmony_ci struct flow_action *flow_action, 2838c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci /* Used to store the L2 rewrite mask for dmac (6 bytes) followed by 2868c2ecf20Sopenharmony_ci * smac (6 bytes) if rewrite of both is specified, otherwise either 2878c2ecf20Sopenharmony_ci * dmac or smac 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci u16 eth_addr_mask[ETH_ALEN] = { 0 }; 2908c2ecf20Sopenharmony_ci /* Used to store the L2 rewrite key for dmac (6 bytes) followed by 2918c2ecf20Sopenharmony_ci * smac (6 bytes) if rewrite of both is specified, otherwise either 2928c2ecf20Sopenharmony_ci * dmac or smac 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci u16 eth_addr[ETH_ALEN] = { 0 }; 2958c2ecf20Sopenharmony_ci struct flow_action_entry *act; 2968c2ecf20Sopenharmony_ci int i, rc; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (!flow_action_has_entries(flow_action)) { 2998c2ecf20Sopenharmony_ci netdev_info(bp->dev, "no actions\n"); 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (!flow_action_basic_hw_stats_check(flow_action, extack)) 3048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci flow_action_for_each(i, act, flow_action) { 3078c2ecf20Sopenharmony_ci switch (act->id) { 3088c2ecf20Sopenharmony_ci case FLOW_ACTION_DROP: 3098c2ecf20Sopenharmony_ci actions->flags |= BNXT_TC_ACTION_FLAG_DROP; 3108c2ecf20Sopenharmony_ci return 0; /* don't bother with other actions */ 3118c2ecf20Sopenharmony_ci case FLOW_ACTION_REDIRECT: 3128c2ecf20Sopenharmony_ci rc = bnxt_tc_parse_redir(bp, actions, act); 3138c2ecf20Sopenharmony_ci if (rc) 3148c2ecf20Sopenharmony_ci return rc; 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci case FLOW_ACTION_VLAN_POP: 3178c2ecf20Sopenharmony_ci case FLOW_ACTION_VLAN_PUSH: 3188c2ecf20Sopenharmony_ci case FLOW_ACTION_VLAN_MANGLE: 3198c2ecf20Sopenharmony_ci rc = bnxt_tc_parse_vlan(bp, actions, act); 3208c2ecf20Sopenharmony_ci if (rc) 3218c2ecf20Sopenharmony_ci return rc; 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci case FLOW_ACTION_TUNNEL_ENCAP: 3248c2ecf20Sopenharmony_ci rc = bnxt_tc_parse_tunnel_set(bp, actions, act); 3258c2ecf20Sopenharmony_ci if (rc) 3268c2ecf20Sopenharmony_ci return rc; 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci case FLOW_ACTION_TUNNEL_DECAP: 3298c2ecf20Sopenharmony_ci actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_DECAP; 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci /* Packet edit: L2 rewrite, NAT, NAPT */ 3328c2ecf20Sopenharmony_ci case FLOW_ACTION_MANGLE: 3338c2ecf20Sopenharmony_ci rc = bnxt_tc_parse_pedit(bp, actions, act, i, 3348c2ecf20Sopenharmony_ci (u8 *)eth_addr, 3358c2ecf20Sopenharmony_ci (u8 *)eth_addr_mask); 3368c2ecf20Sopenharmony_ci if (rc) 3378c2ecf20Sopenharmony_ci return rc; 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci default: 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (actions->flags & BNXT_TC_ACTION_FLAG_L2_REWRITE) { 3458c2ecf20Sopenharmony_ci rc = bnxt_fill_l2_rewrite_fields(actions, eth_addr, 3468c2ecf20Sopenharmony_ci eth_addr_mask); 3478c2ecf20Sopenharmony_ci if (rc) 3488c2ecf20Sopenharmony_ci return rc; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) { 3528c2ecf20Sopenharmony_ci if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) { 3538c2ecf20Sopenharmony_ci /* dst_fid is PF's fid */ 3548c2ecf20Sopenharmony_ci actions->dst_fid = bp->pf.fw_fid; 3558c2ecf20Sopenharmony_ci } else { 3568c2ecf20Sopenharmony_ci /* find the FID from dst_dev */ 3578c2ecf20Sopenharmony_ci actions->dst_fid = 3588c2ecf20Sopenharmony_ci bnxt_flow_get_dst_fid(bp, actions->dst_dev); 3598c2ecf20Sopenharmony_ci if (actions->dst_fid == BNXT_FID_INVALID) 3608c2ecf20Sopenharmony_ci return -EINVAL; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int bnxt_tc_parse_flow(struct bnxt *bp, 3688c2ecf20Sopenharmony_ci struct flow_cls_offload *tc_flow_cmd, 3698c2ecf20Sopenharmony_ci struct bnxt_tc_flow *flow) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct flow_rule *rule = flow_cls_offload_flow_rule(tc_flow_cmd); 3728c2ecf20Sopenharmony_ci struct flow_dissector *dissector = rule->match.dissector; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */ 3758c2ecf20Sopenharmony_ci if ((dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL)) == 0 || 3768c2ecf20Sopenharmony_ci (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_BASIC)) == 0) { 3778c2ecf20Sopenharmony_ci netdev_info(bp->dev, "cannot form TC key: used_keys = 0x%x\n", 3788c2ecf20Sopenharmony_ci dissector->used_keys); 3798c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { 3838c2ecf20Sopenharmony_ci struct flow_match_basic match; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci flow_rule_match_basic(rule, &match); 3868c2ecf20Sopenharmony_ci flow->l2_key.ether_type = match.key->n_proto; 3878c2ecf20Sopenharmony_ci flow->l2_mask.ether_type = match.mask->n_proto; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (match.key->n_proto == htons(ETH_P_IP) || 3908c2ecf20Sopenharmony_ci match.key->n_proto == htons(ETH_P_IPV6)) { 3918c2ecf20Sopenharmony_ci flow->l4_key.ip_proto = match.key->ip_proto; 3928c2ecf20Sopenharmony_ci flow->l4_mask.ip_proto = match.mask->ip_proto; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 3978c2ecf20Sopenharmony_ci struct flow_match_eth_addrs match; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci flow_rule_match_eth_addrs(rule, &match); 4008c2ecf20Sopenharmony_ci flow->flags |= BNXT_TC_FLOW_FLAGS_ETH_ADDRS; 4018c2ecf20Sopenharmony_ci ether_addr_copy(flow->l2_key.dmac, match.key->dst); 4028c2ecf20Sopenharmony_ci ether_addr_copy(flow->l2_mask.dmac, match.mask->dst); 4038c2ecf20Sopenharmony_ci ether_addr_copy(flow->l2_key.smac, match.key->src); 4048c2ecf20Sopenharmony_ci ether_addr_copy(flow->l2_mask.smac, match.mask->src); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { 4088c2ecf20Sopenharmony_ci struct flow_match_vlan match; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci flow_rule_match_vlan(rule, &match); 4118c2ecf20Sopenharmony_ci flow->l2_key.inner_vlan_tci = 4128c2ecf20Sopenharmony_ci cpu_to_be16(VLAN_TCI(match.key->vlan_id, 4138c2ecf20Sopenharmony_ci match.key->vlan_priority)); 4148c2ecf20Sopenharmony_ci flow->l2_mask.inner_vlan_tci = 4158c2ecf20Sopenharmony_ci cpu_to_be16((VLAN_TCI(match.mask->vlan_id, 4168c2ecf20Sopenharmony_ci match.mask->vlan_priority))); 4178c2ecf20Sopenharmony_ci flow->l2_key.inner_vlan_tpid = htons(ETH_P_8021Q); 4188c2ecf20Sopenharmony_ci flow->l2_mask.inner_vlan_tpid = htons(0xffff); 4198c2ecf20Sopenharmony_ci flow->l2_key.num_vlans = 1; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { 4238c2ecf20Sopenharmony_ci struct flow_match_ipv4_addrs match; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci flow_rule_match_ipv4_addrs(rule, &match); 4268c2ecf20Sopenharmony_ci flow->flags |= BNXT_TC_FLOW_FLAGS_IPV4_ADDRS; 4278c2ecf20Sopenharmony_ci flow->l3_key.ipv4.daddr.s_addr = match.key->dst; 4288c2ecf20Sopenharmony_ci flow->l3_mask.ipv4.daddr.s_addr = match.mask->dst; 4298c2ecf20Sopenharmony_ci flow->l3_key.ipv4.saddr.s_addr = match.key->src; 4308c2ecf20Sopenharmony_ci flow->l3_mask.ipv4.saddr.s_addr = match.mask->src; 4318c2ecf20Sopenharmony_ci } else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { 4328c2ecf20Sopenharmony_ci struct flow_match_ipv6_addrs match; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci flow_rule_match_ipv6_addrs(rule, &match); 4358c2ecf20Sopenharmony_ci flow->flags |= BNXT_TC_FLOW_FLAGS_IPV6_ADDRS; 4368c2ecf20Sopenharmony_ci flow->l3_key.ipv6.daddr = match.key->dst; 4378c2ecf20Sopenharmony_ci flow->l3_mask.ipv6.daddr = match.mask->dst; 4388c2ecf20Sopenharmony_ci flow->l3_key.ipv6.saddr = match.key->src; 4398c2ecf20Sopenharmony_ci flow->l3_mask.ipv6.saddr = match.mask->src; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { 4438c2ecf20Sopenharmony_ci struct flow_match_ports match; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci flow_rule_match_ports(rule, &match); 4468c2ecf20Sopenharmony_ci flow->flags |= BNXT_TC_FLOW_FLAGS_PORTS; 4478c2ecf20Sopenharmony_ci flow->l4_key.ports.dport = match.key->dst; 4488c2ecf20Sopenharmony_ci flow->l4_mask.ports.dport = match.mask->dst; 4498c2ecf20Sopenharmony_ci flow->l4_key.ports.sport = match.key->src; 4508c2ecf20Sopenharmony_ci flow->l4_mask.ports.sport = match.mask->src; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ICMP)) { 4548c2ecf20Sopenharmony_ci struct flow_match_icmp match; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci flow_rule_match_icmp(rule, &match); 4578c2ecf20Sopenharmony_ci flow->flags |= BNXT_TC_FLOW_FLAGS_ICMP; 4588c2ecf20Sopenharmony_ci flow->l4_key.icmp.type = match.key->type; 4598c2ecf20Sopenharmony_ci flow->l4_key.icmp.code = match.key->code; 4608c2ecf20Sopenharmony_ci flow->l4_mask.icmp.type = match.mask->type; 4618c2ecf20Sopenharmony_ci flow->l4_mask.icmp.code = match.mask->code; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) { 4658c2ecf20Sopenharmony_ci struct flow_match_ipv4_addrs match; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci flow_rule_match_enc_ipv4_addrs(rule, &match); 4688c2ecf20Sopenharmony_ci flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS; 4698c2ecf20Sopenharmony_ci flow->tun_key.u.ipv4.dst = match.key->dst; 4708c2ecf20Sopenharmony_ci flow->tun_mask.u.ipv4.dst = match.mask->dst; 4718c2ecf20Sopenharmony_ci flow->tun_key.u.ipv4.src = match.key->src; 4728c2ecf20Sopenharmony_ci flow->tun_mask.u.ipv4.src = match.mask->src; 4738c2ecf20Sopenharmony_ci } else if (flow_rule_match_key(rule, 4748c2ecf20Sopenharmony_ci FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) { 4758c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) { 4798c2ecf20Sopenharmony_ci struct flow_match_enc_keyid match; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci flow_rule_match_enc_keyid(rule, &match); 4828c2ecf20Sopenharmony_ci flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ID; 4838c2ecf20Sopenharmony_ci flow->tun_key.tun_id = key32_to_tunnel_id(match.key->keyid); 4848c2ecf20Sopenharmony_ci flow->tun_mask.tun_id = key32_to_tunnel_id(match.mask->keyid); 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS)) { 4888c2ecf20Sopenharmony_ci struct flow_match_ports match; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci flow_rule_match_enc_ports(rule, &match); 4918c2ecf20Sopenharmony_ci flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_PORTS; 4928c2ecf20Sopenharmony_ci flow->tun_key.tp_dst = match.key->dst; 4938c2ecf20Sopenharmony_ci flow->tun_mask.tp_dst = match.mask->dst; 4948c2ecf20Sopenharmony_ci flow->tun_key.tp_src = match.key->src; 4958c2ecf20Sopenharmony_ci flow->tun_mask.tp_src = match.mask->src; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return bnxt_tc_parse_actions(bp, &flow->actions, &rule->action, 4998c2ecf20Sopenharmony_ci tc_flow_cmd->common.extack); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic int bnxt_hwrm_cfa_flow_free(struct bnxt *bp, 5038c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct hwrm_cfa_flow_free_input req = { 0 }; 5068c2ecf20Sopenharmony_ci int rc; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_FREE, -1, -1); 5098c2ecf20Sopenharmony_ci if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) 5108c2ecf20Sopenharmony_ci req.ext_flow_handle = flow_node->ext_flow_handle; 5118c2ecf20Sopenharmony_ci else 5128c2ecf20Sopenharmony_ci req.flow_handle = flow_node->flow_handle; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 5158c2ecf20Sopenharmony_ci if (rc) 5168c2ecf20Sopenharmony_ci netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return rc; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int ipv6_mask_len(struct in6_addr *mask) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci int mask_len = 0, i; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 5268c2ecf20Sopenharmony_ci mask_len += inet_mask_len(mask->s6_addr32[i]); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return mask_len; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic bool is_wildcard(void *mask, int len) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci const u8 *p = mask; 5348c2ecf20Sopenharmony_ci int i; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 5378c2ecf20Sopenharmony_ci if (p[i] != 0) 5388c2ecf20Sopenharmony_ci return false; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci return true; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic bool is_exactmatch(void *mask, int len) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci const u8 *p = mask; 5468c2ecf20Sopenharmony_ci int i; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 5498c2ecf20Sopenharmony_ci if (p[i] != 0xff) 5508c2ecf20Sopenharmony_ci return false; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return true; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic bool is_vlan_tci_allowed(__be16 vlan_tci_mask, 5568c2ecf20Sopenharmony_ci __be16 vlan_tci) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci /* VLAN priority must be either exactly zero or fully wildcarded and 5598c2ecf20Sopenharmony_ci * VLAN id must be exact match. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_ci if (is_vid_exactmatch(vlan_tci_mask) && 5628c2ecf20Sopenharmony_ci ((is_vlan_pcp_exactmatch(vlan_tci_mask) && 5638c2ecf20Sopenharmony_ci is_vlan_pcp_zero(vlan_tci)) || 5648c2ecf20Sopenharmony_ci is_vlan_pcp_wildcarded(vlan_tci_mask))) 5658c2ecf20Sopenharmony_ci return true; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci return false; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic bool bits_set(void *key, int len) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci const u8 *p = key; 5738c2ecf20Sopenharmony_ci int i; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 5768c2ecf20Sopenharmony_ci if (p[i] != 0) 5778c2ecf20Sopenharmony_ci return true; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci return false; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow, 5838c2ecf20Sopenharmony_ci __le16 ref_flow_handle, 5848c2ecf20Sopenharmony_ci __le32 tunnel_handle, 5858c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct bnxt_tc_actions *actions = &flow->actions; 5888c2ecf20Sopenharmony_ci struct bnxt_tc_l3_key *l3_mask = &flow->l3_mask; 5898c2ecf20Sopenharmony_ci struct bnxt_tc_l3_key *l3_key = &flow->l3_key; 5908c2ecf20Sopenharmony_ci struct hwrm_cfa_flow_alloc_input req = { 0 }; 5918c2ecf20Sopenharmony_ci struct hwrm_cfa_flow_alloc_output *resp; 5928c2ecf20Sopenharmony_ci u16 flow_flags = 0, action_flags = 0; 5938c2ecf20Sopenharmony_ci int rc; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_ALLOC, -1, -1); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci req.src_fid = cpu_to_le16(flow->src_fid); 5988c2ecf20Sopenharmony_ci req.ref_flow_handle = ref_flow_handle; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (actions->flags & BNXT_TC_ACTION_FLAG_L2_REWRITE) { 6018c2ecf20Sopenharmony_ci memcpy(req.l2_rewrite_dmac, actions->l2_rewrite_dmac, 6028c2ecf20Sopenharmony_ci ETH_ALEN); 6038c2ecf20Sopenharmony_ci memcpy(req.l2_rewrite_smac, actions->l2_rewrite_smac, 6048c2ecf20Sopenharmony_ci ETH_ALEN); 6058c2ecf20Sopenharmony_ci action_flags |= 6068c2ecf20Sopenharmony_ci CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (actions->flags & BNXT_TC_ACTION_FLAG_NAT_XLATE) { 6108c2ecf20Sopenharmony_ci if (actions->nat.l3_is_ipv4) { 6118c2ecf20Sopenharmony_ci action_flags |= 6128c2ecf20Sopenharmony_ci CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_IPV4_ADDRESS; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (actions->nat.src_xlate) { 6158c2ecf20Sopenharmony_ci action_flags |= 6168c2ecf20Sopenharmony_ci CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC; 6178c2ecf20Sopenharmony_ci /* L3 source rewrite */ 6188c2ecf20Sopenharmony_ci req.nat_ip_address[0] = 6198c2ecf20Sopenharmony_ci actions->nat.l3.ipv4.saddr.s_addr; 6208c2ecf20Sopenharmony_ci /* L4 source port */ 6218c2ecf20Sopenharmony_ci if (actions->nat.l4.ports.sport) 6228c2ecf20Sopenharmony_ci req.nat_port = 6238c2ecf20Sopenharmony_ci actions->nat.l4.ports.sport; 6248c2ecf20Sopenharmony_ci } else { 6258c2ecf20Sopenharmony_ci action_flags |= 6268c2ecf20Sopenharmony_ci CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST; 6278c2ecf20Sopenharmony_ci /* L3 destination rewrite */ 6288c2ecf20Sopenharmony_ci req.nat_ip_address[0] = 6298c2ecf20Sopenharmony_ci actions->nat.l3.ipv4.daddr.s_addr; 6308c2ecf20Sopenharmony_ci /* L4 destination port */ 6318c2ecf20Sopenharmony_ci if (actions->nat.l4.ports.dport) 6328c2ecf20Sopenharmony_ci req.nat_port = 6338c2ecf20Sopenharmony_ci actions->nat.l4.ports.dport; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci netdev_dbg(bp->dev, 6368c2ecf20Sopenharmony_ci "req.nat_ip_address: %pI4 src_xlate: %d req.nat_port: %x\n", 6378c2ecf20Sopenharmony_ci req.nat_ip_address, actions->nat.src_xlate, 6388c2ecf20Sopenharmony_ci req.nat_port); 6398c2ecf20Sopenharmony_ci } else { 6408c2ecf20Sopenharmony_ci if (actions->nat.src_xlate) { 6418c2ecf20Sopenharmony_ci action_flags |= 6428c2ecf20Sopenharmony_ci CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC; 6438c2ecf20Sopenharmony_ci /* L3 source rewrite */ 6448c2ecf20Sopenharmony_ci memcpy(req.nat_ip_address, 6458c2ecf20Sopenharmony_ci actions->nat.l3.ipv6.saddr.s6_addr32, 6468c2ecf20Sopenharmony_ci sizeof(req.nat_ip_address)); 6478c2ecf20Sopenharmony_ci /* L4 source port */ 6488c2ecf20Sopenharmony_ci if (actions->nat.l4.ports.sport) 6498c2ecf20Sopenharmony_ci req.nat_port = 6508c2ecf20Sopenharmony_ci actions->nat.l4.ports.sport; 6518c2ecf20Sopenharmony_ci } else { 6528c2ecf20Sopenharmony_ci action_flags |= 6538c2ecf20Sopenharmony_ci CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST; 6548c2ecf20Sopenharmony_ci /* L3 destination rewrite */ 6558c2ecf20Sopenharmony_ci memcpy(req.nat_ip_address, 6568c2ecf20Sopenharmony_ci actions->nat.l3.ipv6.daddr.s6_addr32, 6578c2ecf20Sopenharmony_ci sizeof(req.nat_ip_address)); 6588c2ecf20Sopenharmony_ci /* L4 destination port */ 6598c2ecf20Sopenharmony_ci if (actions->nat.l4.ports.dport) 6608c2ecf20Sopenharmony_ci req.nat_port = 6618c2ecf20Sopenharmony_ci actions->nat.l4.ports.dport; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci netdev_dbg(bp->dev, 6648c2ecf20Sopenharmony_ci "req.nat_ip_address: %pI6 src_xlate: %d req.nat_port: %x\n", 6658c2ecf20Sopenharmony_ci req.nat_ip_address, actions->nat.src_xlate, 6668c2ecf20Sopenharmony_ci req.nat_port); 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP || 6718c2ecf20Sopenharmony_ci actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) { 6728c2ecf20Sopenharmony_ci req.tunnel_handle = tunnel_handle; 6738c2ecf20Sopenharmony_ci flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_TUNNEL; 6748c2ecf20Sopenharmony_ci action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci req.ethertype = flow->l2_key.ether_type; 6788c2ecf20Sopenharmony_ci req.ip_proto = flow->l4_key.ip_proto; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (flow->flags & BNXT_TC_FLOW_FLAGS_ETH_ADDRS) { 6818c2ecf20Sopenharmony_ci memcpy(req.dmac, flow->l2_key.dmac, ETH_ALEN); 6828c2ecf20Sopenharmony_ci memcpy(req.smac, flow->l2_key.smac, ETH_ALEN); 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (flow->l2_key.num_vlans > 0) { 6868c2ecf20Sopenharmony_ci flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_ONE; 6878c2ecf20Sopenharmony_ci /* FW expects the inner_vlan_tci value to be set 6888c2ecf20Sopenharmony_ci * in outer_vlan_tci when num_vlans is 1 (which is 6898c2ecf20Sopenharmony_ci * always the case in TC.) 6908c2ecf20Sopenharmony_ci */ 6918c2ecf20Sopenharmony_ci req.outer_vlan_tci = flow->l2_key.inner_vlan_tci; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* If all IP and L4 fields are wildcarded then this is an L2 flow */ 6958c2ecf20Sopenharmony_ci if (is_wildcard(l3_mask, sizeof(*l3_mask)) && 6968c2ecf20Sopenharmony_ci is_wildcard(&flow->l4_mask, sizeof(flow->l4_mask))) { 6978c2ecf20Sopenharmony_ci flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_L2; 6988c2ecf20Sopenharmony_ci } else { 6998c2ecf20Sopenharmony_ci flow_flags |= flow->l2_key.ether_type == htons(ETH_P_IP) ? 7008c2ecf20Sopenharmony_ci CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV4 : 7018c2ecf20Sopenharmony_ci CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV4_ADDRS) { 7048c2ecf20Sopenharmony_ci req.ip_dst[0] = l3_key->ipv4.daddr.s_addr; 7058c2ecf20Sopenharmony_ci req.ip_dst_mask_len = 7068c2ecf20Sopenharmony_ci inet_mask_len(l3_mask->ipv4.daddr.s_addr); 7078c2ecf20Sopenharmony_ci req.ip_src[0] = l3_key->ipv4.saddr.s_addr; 7088c2ecf20Sopenharmony_ci req.ip_src_mask_len = 7098c2ecf20Sopenharmony_ci inet_mask_len(l3_mask->ipv4.saddr.s_addr); 7108c2ecf20Sopenharmony_ci } else if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV6_ADDRS) { 7118c2ecf20Sopenharmony_ci memcpy(req.ip_dst, l3_key->ipv6.daddr.s6_addr32, 7128c2ecf20Sopenharmony_ci sizeof(req.ip_dst)); 7138c2ecf20Sopenharmony_ci req.ip_dst_mask_len = 7148c2ecf20Sopenharmony_ci ipv6_mask_len(&l3_mask->ipv6.daddr); 7158c2ecf20Sopenharmony_ci memcpy(req.ip_src, l3_key->ipv6.saddr.s6_addr32, 7168c2ecf20Sopenharmony_ci sizeof(req.ip_src)); 7178c2ecf20Sopenharmony_ci req.ip_src_mask_len = 7188c2ecf20Sopenharmony_ci ipv6_mask_len(&l3_mask->ipv6.saddr); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) { 7238c2ecf20Sopenharmony_ci req.l4_src_port = flow->l4_key.ports.sport; 7248c2ecf20Sopenharmony_ci req.l4_src_port_mask = flow->l4_mask.ports.sport; 7258c2ecf20Sopenharmony_ci req.l4_dst_port = flow->l4_key.ports.dport; 7268c2ecf20Sopenharmony_ci req.l4_dst_port_mask = flow->l4_mask.ports.dport; 7278c2ecf20Sopenharmony_ci } else if (flow->flags & BNXT_TC_FLOW_FLAGS_ICMP) { 7288c2ecf20Sopenharmony_ci /* l4 ports serve as type/code when ip_proto is ICMP */ 7298c2ecf20Sopenharmony_ci req.l4_src_port = htons(flow->l4_key.icmp.type); 7308c2ecf20Sopenharmony_ci req.l4_src_port_mask = htons(flow->l4_mask.icmp.type); 7318c2ecf20Sopenharmony_ci req.l4_dst_port = htons(flow->l4_key.icmp.code); 7328c2ecf20Sopenharmony_ci req.l4_dst_port_mask = htons(flow->l4_mask.icmp.code); 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci req.flags = cpu_to_le16(flow_flags); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (actions->flags & BNXT_TC_ACTION_FLAG_DROP) { 7378c2ecf20Sopenharmony_ci action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP; 7388c2ecf20Sopenharmony_ci } else { 7398c2ecf20Sopenharmony_ci if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) { 7408c2ecf20Sopenharmony_ci action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD; 7418c2ecf20Sopenharmony_ci req.dst_fid = cpu_to_le16(actions->dst_fid); 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci if (actions->flags & BNXT_TC_ACTION_FLAG_PUSH_VLAN) { 7448c2ecf20Sopenharmony_ci action_flags |= 7458c2ecf20Sopenharmony_ci CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; 7468c2ecf20Sopenharmony_ci req.l2_rewrite_vlan_tpid = actions->push_vlan_tpid; 7478c2ecf20Sopenharmony_ci req.l2_rewrite_vlan_tci = actions->push_vlan_tci; 7488c2ecf20Sopenharmony_ci memcpy(&req.l2_rewrite_dmac, &req.dmac, ETH_ALEN); 7498c2ecf20Sopenharmony_ci memcpy(&req.l2_rewrite_smac, &req.smac, ETH_ALEN); 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci if (actions->flags & BNXT_TC_ACTION_FLAG_POP_VLAN) { 7528c2ecf20Sopenharmony_ci action_flags |= 7538c2ecf20Sopenharmony_ci CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; 7548c2ecf20Sopenharmony_ci /* Rewrite config with tpid = 0 implies vlan pop */ 7558c2ecf20Sopenharmony_ci req.l2_rewrite_vlan_tpid = 0; 7568c2ecf20Sopenharmony_ci memcpy(&req.l2_rewrite_dmac, &req.dmac, ETH_ALEN); 7578c2ecf20Sopenharmony_ci memcpy(&req.l2_rewrite_smac, &req.smac, ETH_ALEN); 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci req.action_flags = cpu_to_le16(action_flags); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci mutex_lock(&bp->hwrm_cmd_lock); 7638c2ecf20Sopenharmony_ci rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 7648c2ecf20Sopenharmony_ci if (!rc) { 7658c2ecf20Sopenharmony_ci resp = bnxt_get_hwrm_resp_addr(bp, &req); 7668c2ecf20Sopenharmony_ci /* CFA_FLOW_ALLOC response interpretation: 7678c2ecf20Sopenharmony_ci * fw with fw with 7688c2ecf20Sopenharmony_ci * 16-bit 64-bit 7698c2ecf20Sopenharmony_ci * flow handle flow handle 7708c2ecf20Sopenharmony_ci * =========== =========== 7718c2ecf20Sopenharmony_ci * flow_handle flow handle flow context id 7728c2ecf20Sopenharmony_ci * ext_flow_handle INVALID flow handle 7738c2ecf20Sopenharmony_ci * flow_id INVALID flow counter id 7748c2ecf20Sopenharmony_ci */ 7758c2ecf20Sopenharmony_ci flow_node->flow_handle = resp->flow_handle; 7768c2ecf20Sopenharmony_ci if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) { 7778c2ecf20Sopenharmony_ci flow_node->ext_flow_handle = resp->ext_flow_handle; 7788c2ecf20Sopenharmony_ci flow_node->flow_id = resp->flow_id; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci mutex_unlock(&bp->hwrm_cmd_lock); 7828c2ecf20Sopenharmony_ci return rc; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic int hwrm_cfa_decap_filter_alloc(struct bnxt *bp, 7868c2ecf20Sopenharmony_ci struct bnxt_tc_flow *flow, 7878c2ecf20Sopenharmony_ci struct bnxt_tc_l2_key *l2_info, 7888c2ecf20Sopenharmony_ci __le32 ref_decap_handle, 7898c2ecf20Sopenharmony_ci __le32 *decap_filter_handle) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct hwrm_cfa_decap_filter_alloc_input req = { 0 }; 7928c2ecf20Sopenharmony_ci struct hwrm_cfa_decap_filter_alloc_output *resp; 7938c2ecf20Sopenharmony_ci struct ip_tunnel_key *tun_key = &flow->tun_key; 7948c2ecf20Sopenharmony_ci u32 enables = 0; 7958c2ecf20Sopenharmony_ci int rc; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_DECAP_FILTER_ALLOC, -1, -1); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci req.flags = cpu_to_le32(CFA_DECAP_FILTER_ALLOC_REQ_FLAGS_OVS_TUNNEL); 8008c2ecf20Sopenharmony_ci enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE | 8018c2ecf20Sopenharmony_ci CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL; 8028c2ecf20Sopenharmony_ci req.tunnel_type = CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN; 8038c2ecf20Sopenharmony_ci req.ip_protocol = CFA_DECAP_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_ID) { 8068c2ecf20Sopenharmony_ci enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_TUNNEL_ID; 8078c2ecf20Sopenharmony_ci /* tunnel_id is wrongly defined in hsi defn. as __le32 */ 8088c2ecf20Sopenharmony_ci req.tunnel_id = tunnel_id_to_key32(tun_key->tun_id); 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS) { 8128c2ecf20Sopenharmony_ci enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_MACADDR; 8138c2ecf20Sopenharmony_ci ether_addr_copy(req.dst_macaddr, l2_info->dmac); 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci if (l2_info->num_vlans) { 8168c2ecf20Sopenharmony_ci enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_T_IVLAN_VID; 8178c2ecf20Sopenharmony_ci req.t_ivlan_vid = l2_info->inner_vlan_tci; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE; 8218c2ecf20Sopenharmony_ci req.ethertype = htons(ETH_P_IP); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS) { 8248c2ecf20Sopenharmony_ci enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR | 8258c2ecf20Sopenharmony_ci CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR | 8268c2ecf20Sopenharmony_ci CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE; 8278c2ecf20Sopenharmony_ci req.ip_addr_type = CFA_DECAP_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4; 8288c2ecf20Sopenharmony_ci req.dst_ipaddr[0] = tun_key->u.ipv4.dst; 8298c2ecf20Sopenharmony_ci req.src_ipaddr[0] = tun_key->u.ipv4.src; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_PORTS) { 8338c2ecf20Sopenharmony_ci enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_PORT; 8348c2ecf20Sopenharmony_ci req.dst_port = tun_key->tp_dst; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci /* Eventhough the decap_handle returned by hwrm_cfa_decap_filter_alloc 8388c2ecf20Sopenharmony_ci * is defined as __le32, l2_ctxt_ref_id is defined in HSI as __le16. 8398c2ecf20Sopenharmony_ci */ 8408c2ecf20Sopenharmony_ci req.l2_ctxt_ref_id = (__force __le16)ref_decap_handle; 8418c2ecf20Sopenharmony_ci req.enables = cpu_to_le32(enables); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci mutex_lock(&bp->hwrm_cmd_lock); 8448c2ecf20Sopenharmony_ci rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 8458c2ecf20Sopenharmony_ci if (!rc) { 8468c2ecf20Sopenharmony_ci resp = bnxt_get_hwrm_resp_addr(bp, &req); 8478c2ecf20Sopenharmony_ci *decap_filter_handle = resp->decap_filter_id; 8488c2ecf20Sopenharmony_ci } else { 8498c2ecf20Sopenharmony_ci netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc); 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci mutex_unlock(&bp->hwrm_cmd_lock); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci return rc; 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic int hwrm_cfa_decap_filter_free(struct bnxt *bp, 8578c2ecf20Sopenharmony_ci __le32 decap_filter_handle) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci struct hwrm_cfa_decap_filter_free_input req = { 0 }; 8608c2ecf20Sopenharmony_ci int rc; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_DECAP_FILTER_FREE, -1, -1); 8638c2ecf20Sopenharmony_ci req.decap_filter_id = decap_filter_handle; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 8668c2ecf20Sopenharmony_ci if (rc) 8678c2ecf20Sopenharmony_ci netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci return rc; 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic int hwrm_cfa_encap_record_alloc(struct bnxt *bp, 8738c2ecf20Sopenharmony_ci struct ip_tunnel_key *encap_key, 8748c2ecf20Sopenharmony_ci struct bnxt_tc_l2_key *l2_info, 8758c2ecf20Sopenharmony_ci __le32 *encap_record_handle) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci struct hwrm_cfa_encap_record_alloc_input req = { 0 }; 8788c2ecf20Sopenharmony_ci struct hwrm_cfa_encap_record_alloc_output *resp; 8798c2ecf20Sopenharmony_ci struct hwrm_cfa_encap_data_vxlan *encap = 8808c2ecf20Sopenharmony_ci (struct hwrm_cfa_encap_data_vxlan *)&req.encap_data; 8818c2ecf20Sopenharmony_ci struct hwrm_vxlan_ipv4_hdr *encap_ipv4 = 8828c2ecf20Sopenharmony_ci (struct hwrm_vxlan_ipv4_hdr *)encap->l3; 8838c2ecf20Sopenharmony_ci int rc; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_ENCAP_RECORD_ALLOC, -1, -1); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci req.encap_type = CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_VXLAN; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci ether_addr_copy(encap->dst_mac_addr, l2_info->dmac); 8908c2ecf20Sopenharmony_ci ether_addr_copy(encap->src_mac_addr, l2_info->smac); 8918c2ecf20Sopenharmony_ci if (l2_info->num_vlans) { 8928c2ecf20Sopenharmony_ci encap->num_vlan_tags = l2_info->num_vlans; 8938c2ecf20Sopenharmony_ci encap->ovlan_tci = l2_info->inner_vlan_tci; 8948c2ecf20Sopenharmony_ci encap->ovlan_tpid = l2_info->inner_vlan_tpid; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci encap_ipv4->ver_hlen = 4 << VXLAN_IPV4_HDR_VER_HLEN_VERSION_SFT; 8988c2ecf20Sopenharmony_ci encap_ipv4->ver_hlen |= 5 << VXLAN_IPV4_HDR_VER_HLEN_HEADER_LENGTH_SFT; 8998c2ecf20Sopenharmony_ci encap_ipv4->ttl = encap_key->ttl; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci encap_ipv4->dest_ip_addr = encap_key->u.ipv4.dst; 9028c2ecf20Sopenharmony_ci encap_ipv4->src_ip_addr = encap_key->u.ipv4.src; 9038c2ecf20Sopenharmony_ci encap_ipv4->protocol = IPPROTO_UDP; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci encap->dst_port = encap_key->tp_dst; 9068c2ecf20Sopenharmony_ci encap->vni = tunnel_id_to_key32(encap_key->tun_id); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci mutex_lock(&bp->hwrm_cmd_lock); 9098c2ecf20Sopenharmony_ci rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 9108c2ecf20Sopenharmony_ci if (!rc) { 9118c2ecf20Sopenharmony_ci resp = bnxt_get_hwrm_resp_addr(bp, &req); 9128c2ecf20Sopenharmony_ci *encap_record_handle = resp->encap_record_id; 9138c2ecf20Sopenharmony_ci } else { 9148c2ecf20Sopenharmony_ci netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc); 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci mutex_unlock(&bp->hwrm_cmd_lock); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci return rc; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic int hwrm_cfa_encap_record_free(struct bnxt *bp, 9228c2ecf20Sopenharmony_ci __le32 encap_record_handle) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci struct hwrm_cfa_encap_record_free_input req = { 0 }; 9258c2ecf20Sopenharmony_ci int rc; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_ENCAP_RECORD_FREE, -1, -1); 9288c2ecf20Sopenharmony_ci req.encap_record_id = encap_record_handle; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 9318c2ecf20Sopenharmony_ci if (rc) 9328c2ecf20Sopenharmony_ci netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci return rc; 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic int bnxt_tc_put_l2_node(struct bnxt *bp, 9388c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct bnxt_tc_l2_node *l2_node = flow_node->l2_node; 9418c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 9428c2ecf20Sopenharmony_ci int rc; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* remove flow_node from the L2 shared flow list */ 9458c2ecf20Sopenharmony_ci list_del(&flow_node->l2_list_node); 9468c2ecf20Sopenharmony_ci if (--l2_node->refcount == 0) { 9478c2ecf20Sopenharmony_ci rc = rhashtable_remove_fast(&tc_info->l2_table, &l2_node->node, 9488c2ecf20Sopenharmony_ci tc_info->l2_ht_params); 9498c2ecf20Sopenharmony_ci if (rc) 9508c2ecf20Sopenharmony_ci netdev_err(bp->dev, 9518c2ecf20Sopenharmony_ci "Error: %s: rhashtable_remove_fast: %d\n", 9528c2ecf20Sopenharmony_ci __func__, rc); 9538c2ecf20Sopenharmony_ci kfree_rcu(l2_node, rcu); 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci return 0; 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cistatic struct bnxt_tc_l2_node * 9598c2ecf20Sopenharmony_cibnxt_tc_get_l2_node(struct bnxt *bp, struct rhashtable *l2_table, 9608c2ecf20Sopenharmony_ci struct rhashtable_params ht_params, 9618c2ecf20Sopenharmony_ci struct bnxt_tc_l2_key *l2_key) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci struct bnxt_tc_l2_node *l2_node; 9648c2ecf20Sopenharmony_ci int rc; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci l2_node = rhashtable_lookup_fast(l2_table, l2_key, ht_params); 9678c2ecf20Sopenharmony_ci if (!l2_node) { 9688c2ecf20Sopenharmony_ci l2_node = kzalloc(sizeof(*l2_node), GFP_KERNEL); 9698c2ecf20Sopenharmony_ci if (!l2_node) { 9708c2ecf20Sopenharmony_ci rc = -ENOMEM; 9718c2ecf20Sopenharmony_ci return NULL; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci l2_node->key = *l2_key; 9758c2ecf20Sopenharmony_ci rc = rhashtable_insert_fast(l2_table, &l2_node->node, 9768c2ecf20Sopenharmony_ci ht_params); 9778c2ecf20Sopenharmony_ci if (rc) { 9788c2ecf20Sopenharmony_ci kfree_rcu(l2_node, rcu); 9798c2ecf20Sopenharmony_ci netdev_err(bp->dev, 9808c2ecf20Sopenharmony_ci "Error: %s: rhashtable_insert_fast: %d\n", 9818c2ecf20Sopenharmony_ci __func__, rc); 9828c2ecf20Sopenharmony_ci return NULL; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&l2_node->common_l2_flows); 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci return l2_node; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci/* Get the ref_flow_handle for a flow by checking if there are any other 9908c2ecf20Sopenharmony_ci * flows that share the same L2 key as this flow. 9918c2ecf20Sopenharmony_ci */ 9928c2ecf20Sopenharmony_cistatic int 9938c2ecf20Sopenharmony_cibnxt_tc_get_ref_flow_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, 9948c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node, 9958c2ecf20Sopenharmony_ci __le16 *ref_flow_handle) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 9988c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *ref_flow_node; 9998c2ecf20Sopenharmony_ci struct bnxt_tc_l2_node *l2_node; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci l2_node = bnxt_tc_get_l2_node(bp, &tc_info->l2_table, 10028c2ecf20Sopenharmony_ci tc_info->l2_ht_params, 10038c2ecf20Sopenharmony_ci &flow->l2_key); 10048c2ecf20Sopenharmony_ci if (!l2_node) 10058c2ecf20Sopenharmony_ci return -1; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* If any other flow is using this l2_node, use it's flow_handle 10088c2ecf20Sopenharmony_ci * as the ref_flow_handle 10098c2ecf20Sopenharmony_ci */ 10108c2ecf20Sopenharmony_ci if (l2_node->refcount > 0) { 10118c2ecf20Sopenharmony_ci ref_flow_node = list_first_entry(&l2_node->common_l2_flows, 10128c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node, 10138c2ecf20Sopenharmony_ci l2_list_node); 10148c2ecf20Sopenharmony_ci *ref_flow_handle = ref_flow_node->flow_handle; 10158c2ecf20Sopenharmony_ci } else { 10168c2ecf20Sopenharmony_ci *ref_flow_handle = cpu_to_le16(0xffff); 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* Insert the l2_node into the flow_node so that subsequent flows 10208c2ecf20Sopenharmony_ci * with a matching l2 key can use the flow_handle of this flow 10218c2ecf20Sopenharmony_ci * as their ref_flow_handle 10228c2ecf20Sopenharmony_ci */ 10238c2ecf20Sopenharmony_ci flow_node->l2_node = l2_node; 10248c2ecf20Sopenharmony_ci list_add(&flow_node->l2_list_node, &l2_node->common_l2_flows); 10258c2ecf20Sopenharmony_ci l2_node->refcount++; 10268c2ecf20Sopenharmony_ci return 0; 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci/* After the flow parsing is done, this routine is used for checking 10308c2ecf20Sopenharmony_ci * if there are any aspects of the flow that prevent it from being 10318c2ecf20Sopenharmony_ci * offloaded. 10328c2ecf20Sopenharmony_ci */ 10338c2ecf20Sopenharmony_cistatic bool bnxt_tc_can_offload(struct bnxt *bp, struct bnxt_tc_flow *flow) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci /* If L4 ports are specified then ip_proto must be TCP or UDP */ 10368c2ecf20Sopenharmony_ci if ((flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) && 10378c2ecf20Sopenharmony_ci (flow->l4_key.ip_proto != IPPROTO_TCP && 10388c2ecf20Sopenharmony_ci flow->l4_key.ip_proto != IPPROTO_UDP)) { 10398c2ecf20Sopenharmony_ci netdev_info(bp->dev, "Cannot offload non-TCP/UDP (%d) ports\n", 10408c2ecf20Sopenharmony_ci flow->l4_key.ip_proto); 10418c2ecf20Sopenharmony_ci return false; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci /* Currently source/dest MAC cannot be partial wildcard */ 10458c2ecf20Sopenharmony_ci if (bits_set(&flow->l2_key.smac, sizeof(flow->l2_key.smac)) && 10468c2ecf20Sopenharmony_ci !is_exactmatch(flow->l2_mask.smac, sizeof(flow->l2_mask.smac))) { 10478c2ecf20Sopenharmony_ci netdev_info(bp->dev, "Wildcard match unsupported for Source MAC\n"); 10488c2ecf20Sopenharmony_ci return false; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci if (bits_set(&flow->l2_key.dmac, sizeof(flow->l2_key.dmac)) && 10518c2ecf20Sopenharmony_ci !is_exactmatch(&flow->l2_mask.dmac, sizeof(flow->l2_mask.dmac))) { 10528c2ecf20Sopenharmony_ci netdev_info(bp->dev, "Wildcard match unsupported for Dest MAC\n"); 10538c2ecf20Sopenharmony_ci return false; 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci /* Currently VLAN fields cannot be partial wildcard */ 10578c2ecf20Sopenharmony_ci if (bits_set(&flow->l2_key.inner_vlan_tci, 10588c2ecf20Sopenharmony_ci sizeof(flow->l2_key.inner_vlan_tci)) && 10598c2ecf20Sopenharmony_ci !is_vlan_tci_allowed(flow->l2_mask.inner_vlan_tci, 10608c2ecf20Sopenharmony_ci flow->l2_key.inner_vlan_tci)) { 10618c2ecf20Sopenharmony_ci netdev_info(bp->dev, "Unsupported VLAN TCI\n"); 10628c2ecf20Sopenharmony_ci return false; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci if (bits_set(&flow->l2_key.inner_vlan_tpid, 10658c2ecf20Sopenharmony_ci sizeof(flow->l2_key.inner_vlan_tpid)) && 10668c2ecf20Sopenharmony_ci !is_exactmatch(&flow->l2_mask.inner_vlan_tpid, 10678c2ecf20Sopenharmony_ci sizeof(flow->l2_mask.inner_vlan_tpid))) { 10688c2ecf20Sopenharmony_ci netdev_info(bp->dev, "Wildcard match unsupported for VLAN TPID\n"); 10698c2ecf20Sopenharmony_ci return false; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* Currently Ethertype must be set */ 10738c2ecf20Sopenharmony_ci if (!is_exactmatch(&flow->l2_mask.ether_type, 10748c2ecf20Sopenharmony_ci sizeof(flow->l2_mask.ether_type))) { 10758c2ecf20Sopenharmony_ci netdev_info(bp->dev, "Wildcard match unsupported for Ethertype\n"); 10768c2ecf20Sopenharmony_ci return false; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci return true; 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci/* Returns the final refcount of the node on success 10838c2ecf20Sopenharmony_ci * or a -ve error code on failure 10848c2ecf20Sopenharmony_ci */ 10858c2ecf20Sopenharmony_cistatic int bnxt_tc_put_tunnel_node(struct bnxt *bp, 10868c2ecf20Sopenharmony_ci struct rhashtable *tunnel_table, 10878c2ecf20Sopenharmony_ci struct rhashtable_params *ht_params, 10888c2ecf20Sopenharmony_ci struct bnxt_tc_tunnel_node *tunnel_node) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci int rc; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (--tunnel_node->refcount == 0) { 10938c2ecf20Sopenharmony_ci rc = rhashtable_remove_fast(tunnel_table, &tunnel_node->node, 10948c2ecf20Sopenharmony_ci *ht_params); 10958c2ecf20Sopenharmony_ci if (rc) { 10968c2ecf20Sopenharmony_ci netdev_err(bp->dev, "rhashtable_remove_fast rc=%d\n", rc); 10978c2ecf20Sopenharmony_ci rc = -1; 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci kfree_rcu(tunnel_node, rcu); 11008c2ecf20Sopenharmony_ci return rc; 11018c2ecf20Sopenharmony_ci } else { 11028c2ecf20Sopenharmony_ci return tunnel_node->refcount; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci/* Get (or add) either encap or decap tunnel node from/to the supplied 11078c2ecf20Sopenharmony_ci * hash table. 11088c2ecf20Sopenharmony_ci */ 11098c2ecf20Sopenharmony_cistatic struct bnxt_tc_tunnel_node * 11108c2ecf20Sopenharmony_cibnxt_tc_get_tunnel_node(struct bnxt *bp, struct rhashtable *tunnel_table, 11118c2ecf20Sopenharmony_ci struct rhashtable_params *ht_params, 11128c2ecf20Sopenharmony_ci struct ip_tunnel_key *tun_key) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci struct bnxt_tc_tunnel_node *tunnel_node; 11158c2ecf20Sopenharmony_ci int rc; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci tunnel_node = rhashtable_lookup_fast(tunnel_table, tun_key, *ht_params); 11188c2ecf20Sopenharmony_ci if (!tunnel_node) { 11198c2ecf20Sopenharmony_ci tunnel_node = kzalloc(sizeof(*tunnel_node), GFP_KERNEL); 11208c2ecf20Sopenharmony_ci if (!tunnel_node) { 11218c2ecf20Sopenharmony_ci rc = -ENOMEM; 11228c2ecf20Sopenharmony_ci goto err; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci tunnel_node->key = *tun_key; 11268c2ecf20Sopenharmony_ci tunnel_node->tunnel_handle = INVALID_TUNNEL_HANDLE; 11278c2ecf20Sopenharmony_ci rc = rhashtable_insert_fast(tunnel_table, &tunnel_node->node, 11288c2ecf20Sopenharmony_ci *ht_params); 11298c2ecf20Sopenharmony_ci if (rc) { 11308c2ecf20Sopenharmony_ci kfree_rcu(tunnel_node, rcu); 11318c2ecf20Sopenharmony_ci goto err; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci tunnel_node->refcount++; 11358c2ecf20Sopenharmony_ci return tunnel_node; 11368c2ecf20Sopenharmony_cierr: 11378c2ecf20Sopenharmony_ci netdev_info(bp->dev, "error rc=%d\n", rc); 11388c2ecf20Sopenharmony_ci return NULL; 11398c2ecf20Sopenharmony_ci} 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_cistatic int bnxt_tc_get_ref_decap_handle(struct bnxt *bp, 11428c2ecf20Sopenharmony_ci struct bnxt_tc_flow *flow, 11438c2ecf20Sopenharmony_ci struct bnxt_tc_l2_key *l2_key, 11448c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node, 11458c2ecf20Sopenharmony_ci __le32 *ref_decap_handle) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 11488c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *ref_flow_node; 11498c2ecf20Sopenharmony_ci struct bnxt_tc_l2_node *decap_l2_node; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci decap_l2_node = bnxt_tc_get_l2_node(bp, &tc_info->decap_l2_table, 11528c2ecf20Sopenharmony_ci tc_info->decap_l2_ht_params, 11538c2ecf20Sopenharmony_ci l2_key); 11548c2ecf20Sopenharmony_ci if (!decap_l2_node) 11558c2ecf20Sopenharmony_ci return -1; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci /* If any other flow is using this decap_l2_node, use it's decap_handle 11588c2ecf20Sopenharmony_ci * as the ref_decap_handle 11598c2ecf20Sopenharmony_ci */ 11608c2ecf20Sopenharmony_ci if (decap_l2_node->refcount > 0) { 11618c2ecf20Sopenharmony_ci ref_flow_node = 11628c2ecf20Sopenharmony_ci list_first_entry(&decap_l2_node->common_l2_flows, 11638c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node, 11648c2ecf20Sopenharmony_ci decap_l2_list_node); 11658c2ecf20Sopenharmony_ci *ref_decap_handle = ref_flow_node->decap_node->tunnel_handle; 11668c2ecf20Sopenharmony_ci } else { 11678c2ecf20Sopenharmony_ci *ref_decap_handle = INVALID_TUNNEL_HANDLE; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* Insert the l2_node into the flow_node so that subsequent flows 11718c2ecf20Sopenharmony_ci * with a matching decap l2 key can use the decap_filter_handle of 11728c2ecf20Sopenharmony_ci * this flow as their ref_decap_handle 11738c2ecf20Sopenharmony_ci */ 11748c2ecf20Sopenharmony_ci flow_node->decap_l2_node = decap_l2_node; 11758c2ecf20Sopenharmony_ci list_add(&flow_node->decap_l2_list_node, 11768c2ecf20Sopenharmony_ci &decap_l2_node->common_l2_flows); 11778c2ecf20Sopenharmony_ci decap_l2_node->refcount++; 11788c2ecf20Sopenharmony_ci return 0; 11798c2ecf20Sopenharmony_ci} 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_cistatic void bnxt_tc_put_decap_l2_node(struct bnxt *bp, 11828c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci struct bnxt_tc_l2_node *decap_l2_node = flow_node->decap_l2_node; 11858c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 11868c2ecf20Sopenharmony_ci int rc; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci /* remove flow_node from the decap L2 sharing flow list */ 11898c2ecf20Sopenharmony_ci list_del(&flow_node->decap_l2_list_node); 11908c2ecf20Sopenharmony_ci if (--decap_l2_node->refcount == 0) { 11918c2ecf20Sopenharmony_ci rc = rhashtable_remove_fast(&tc_info->decap_l2_table, 11928c2ecf20Sopenharmony_ci &decap_l2_node->node, 11938c2ecf20Sopenharmony_ci tc_info->decap_l2_ht_params); 11948c2ecf20Sopenharmony_ci if (rc) 11958c2ecf20Sopenharmony_ci netdev_err(bp->dev, "rhashtable_remove_fast rc=%d\n", rc); 11968c2ecf20Sopenharmony_ci kfree_rcu(decap_l2_node, rcu); 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_cistatic void bnxt_tc_put_decap_handle(struct bnxt *bp, 12018c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci __le32 decap_handle = flow_node->decap_node->tunnel_handle; 12048c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 12058c2ecf20Sopenharmony_ci int rc; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci if (flow_node->decap_l2_node) 12088c2ecf20Sopenharmony_ci bnxt_tc_put_decap_l2_node(bp, flow_node); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci rc = bnxt_tc_put_tunnel_node(bp, &tc_info->decap_table, 12118c2ecf20Sopenharmony_ci &tc_info->decap_ht_params, 12128c2ecf20Sopenharmony_ci flow_node->decap_node); 12138c2ecf20Sopenharmony_ci if (!rc && decap_handle != INVALID_TUNNEL_HANDLE) 12148c2ecf20Sopenharmony_ci hwrm_cfa_decap_filter_free(bp, decap_handle); 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic int bnxt_tc_resolve_tunnel_hdrs(struct bnxt *bp, 12188c2ecf20Sopenharmony_ci struct ip_tunnel_key *tun_key, 12198c2ecf20Sopenharmony_ci struct bnxt_tc_l2_key *l2_info) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci#ifdef CONFIG_INET 12228c2ecf20Sopenharmony_ci struct net_device *real_dst_dev = bp->dev; 12238c2ecf20Sopenharmony_ci struct flowi4 flow = { {0} }; 12248c2ecf20Sopenharmony_ci struct net_device *dst_dev; 12258c2ecf20Sopenharmony_ci struct neighbour *nbr; 12268c2ecf20Sopenharmony_ci struct rtable *rt; 12278c2ecf20Sopenharmony_ci int rc; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci flow.flowi4_proto = IPPROTO_UDP; 12308c2ecf20Sopenharmony_ci flow.fl4_dport = tun_key->tp_dst; 12318c2ecf20Sopenharmony_ci flow.daddr = tun_key->u.ipv4.dst; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci rt = ip_route_output_key(dev_net(real_dst_dev), &flow); 12348c2ecf20Sopenharmony_ci if (IS_ERR(rt)) { 12358c2ecf20Sopenharmony_ci netdev_info(bp->dev, "no route to %pI4b\n", &flow.daddr); 12368c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci /* The route must either point to the real_dst_dev or a dst_dev that 12408c2ecf20Sopenharmony_ci * uses the real_dst_dev. 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_ci dst_dev = rt->dst.dev; 12438c2ecf20Sopenharmony_ci if (is_vlan_dev(dst_dev)) { 12448c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VLAN_8021Q) 12458c2ecf20Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(dst_dev); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (vlan->real_dev != real_dst_dev) { 12488c2ecf20Sopenharmony_ci netdev_info(bp->dev, 12498c2ecf20Sopenharmony_ci "dst_dev(%s) doesn't use PF-if(%s)\n", 12508c2ecf20Sopenharmony_ci netdev_name(dst_dev), 12518c2ecf20Sopenharmony_ci netdev_name(real_dst_dev)); 12528c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 12538c2ecf20Sopenharmony_ci goto put_rt; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci l2_info->inner_vlan_tci = htons(vlan->vlan_id); 12568c2ecf20Sopenharmony_ci l2_info->inner_vlan_tpid = vlan->vlan_proto; 12578c2ecf20Sopenharmony_ci l2_info->num_vlans = 1; 12588c2ecf20Sopenharmony_ci#endif 12598c2ecf20Sopenharmony_ci } else if (dst_dev != real_dst_dev) { 12608c2ecf20Sopenharmony_ci netdev_info(bp->dev, 12618c2ecf20Sopenharmony_ci "dst_dev(%s) for %pI4b is not PF-if(%s)\n", 12628c2ecf20Sopenharmony_ci netdev_name(dst_dev), &flow.daddr, 12638c2ecf20Sopenharmony_ci netdev_name(real_dst_dev)); 12648c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 12658c2ecf20Sopenharmony_ci goto put_rt; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci nbr = dst_neigh_lookup(&rt->dst, &flow.daddr); 12698c2ecf20Sopenharmony_ci if (!nbr) { 12708c2ecf20Sopenharmony_ci netdev_info(bp->dev, "can't lookup neighbor for %pI4b\n", 12718c2ecf20Sopenharmony_ci &flow.daddr); 12728c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 12738c2ecf20Sopenharmony_ci goto put_rt; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci tun_key->u.ipv4.src = flow.saddr; 12778c2ecf20Sopenharmony_ci tun_key->ttl = ip4_dst_hoplimit(&rt->dst); 12788c2ecf20Sopenharmony_ci neigh_ha_snapshot(l2_info->dmac, nbr, dst_dev); 12798c2ecf20Sopenharmony_ci ether_addr_copy(l2_info->smac, dst_dev->dev_addr); 12808c2ecf20Sopenharmony_ci neigh_release(nbr); 12818c2ecf20Sopenharmony_ci ip_rt_put(rt); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci return 0; 12848c2ecf20Sopenharmony_ciput_rt: 12858c2ecf20Sopenharmony_ci ip_rt_put(rt); 12868c2ecf20Sopenharmony_ci return rc; 12878c2ecf20Sopenharmony_ci#else 12888c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12898c2ecf20Sopenharmony_ci#endif 12908c2ecf20Sopenharmony_ci} 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_cistatic int bnxt_tc_get_decap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, 12938c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node, 12948c2ecf20Sopenharmony_ci __le32 *decap_filter_handle) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci struct ip_tunnel_key *decap_key = &flow->tun_key; 12978c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 12988c2ecf20Sopenharmony_ci struct bnxt_tc_l2_key l2_info = { {0} }; 12998c2ecf20Sopenharmony_ci struct bnxt_tc_tunnel_node *decap_node; 13008c2ecf20Sopenharmony_ci struct ip_tunnel_key tun_key = { 0 }; 13018c2ecf20Sopenharmony_ci struct bnxt_tc_l2_key *decap_l2_info; 13028c2ecf20Sopenharmony_ci __le32 ref_decap_handle; 13038c2ecf20Sopenharmony_ci int rc; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci /* Check if there's another flow using the same tunnel decap. 13068c2ecf20Sopenharmony_ci * If not, add this tunnel to the table and resolve the other 13078c2ecf20Sopenharmony_ci * tunnel header fileds. Ignore src_port in the tunnel_key, 13088c2ecf20Sopenharmony_ci * since it is not required for decap filters. 13098c2ecf20Sopenharmony_ci */ 13108c2ecf20Sopenharmony_ci decap_key->tp_src = 0; 13118c2ecf20Sopenharmony_ci decap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->decap_table, 13128c2ecf20Sopenharmony_ci &tc_info->decap_ht_params, 13138c2ecf20Sopenharmony_ci decap_key); 13148c2ecf20Sopenharmony_ci if (!decap_node) 13158c2ecf20Sopenharmony_ci return -ENOMEM; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci flow_node->decap_node = decap_node; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (decap_node->tunnel_handle != INVALID_TUNNEL_HANDLE) 13208c2ecf20Sopenharmony_ci goto done; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* Resolve the L2 fields for tunnel decap 13238c2ecf20Sopenharmony_ci * Resolve the route for remote vtep (saddr) of the decap key 13248c2ecf20Sopenharmony_ci * Find it's next-hop mac addrs 13258c2ecf20Sopenharmony_ci */ 13268c2ecf20Sopenharmony_ci tun_key.u.ipv4.dst = flow->tun_key.u.ipv4.src; 13278c2ecf20Sopenharmony_ci tun_key.tp_dst = flow->tun_key.tp_dst; 13288c2ecf20Sopenharmony_ci rc = bnxt_tc_resolve_tunnel_hdrs(bp, &tun_key, &l2_info); 13298c2ecf20Sopenharmony_ci if (rc) 13308c2ecf20Sopenharmony_ci goto put_decap; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci decap_l2_info = &decap_node->l2_info; 13338c2ecf20Sopenharmony_ci /* decap smac is wildcarded */ 13348c2ecf20Sopenharmony_ci ether_addr_copy(decap_l2_info->dmac, l2_info.smac); 13358c2ecf20Sopenharmony_ci if (l2_info.num_vlans) { 13368c2ecf20Sopenharmony_ci decap_l2_info->num_vlans = l2_info.num_vlans; 13378c2ecf20Sopenharmony_ci decap_l2_info->inner_vlan_tpid = l2_info.inner_vlan_tpid; 13388c2ecf20Sopenharmony_ci decap_l2_info->inner_vlan_tci = l2_info.inner_vlan_tci; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci /* For getting a decap_filter_handle we first need to check if 13438c2ecf20Sopenharmony_ci * there are any other decap flows that share the same tunnel L2 13448c2ecf20Sopenharmony_ci * key and if so, pass that flow's decap_filter_handle as the 13458c2ecf20Sopenharmony_ci * ref_decap_handle for this flow. 13468c2ecf20Sopenharmony_ci */ 13478c2ecf20Sopenharmony_ci rc = bnxt_tc_get_ref_decap_handle(bp, flow, decap_l2_info, flow_node, 13488c2ecf20Sopenharmony_ci &ref_decap_handle); 13498c2ecf20Sopenharmony_ci if (rc) 13508c2ecf20Sopenharmony_ci goto put_decap; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci /* Issue the hwrm cmd to allocate a decap filter handle */ 13538c2ecf20Sopenharmony_ci rc = hwrm_cfa_decap_filter_alloc(bp, flow, decap_l2_info, 13548c2ecf20Sopenharmony_ci ref_decap_handle, 13558c2ecf20Sopenharmony_ci &decap_node->tunnel_handle); 13568c2ecf20Sopenharmony_ci if (rc) 13578c2ecf20Sopenharmony_ci goto put_decap_l2; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cidone: 13608c2ecf20Sopenharmony_ci *decap_filter_handle = decap_node->tunnel_handle; 13618c2ecf20Sopenharmony_ci return 0; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ciput_decap_l2: 13648c2ecf20Sopenharmony_ci bnxt_tc_put_decap_l2_node(bp, flow_node); 13658c2ecf20Sopenharmony_ciput_decap: 13668c2ecf20Sopenharmony_ci bnxt_tc_put_tunnel_node(bp, &tc_info->decap_table, 13678c2ecf20Sopenharmony_ci &tc_info->decap_ht_params, 13688c2ecf20Sopenharmony_ci flow_node->decap_node); 13698c2ecf20Sopenharmony_ci return rc; 13708c2ecf20Sopenharmony_ci} 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_cistatic void bnxt_tc_put_encap_handle(struct bnxt *bp, 13738c2ecf20Sopenharmony_ci struct bnxt_tc_tunnel_node *encap_node) 13748c2ecf20Sopenharmony_ci{ 13758c2ecf20Sopenharmony_ci __le32 encap_handle = encap_node->tunnel_handle; 13768c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 13778c2ecf20Sopenharmony_ci int rc; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci rc = bnxt_tc_put_tunnel_node(bp, &tc_info->encap_table, 13808c2ecf20Sopenharmony_ci &tc_info->encap_ht_params, encap_node); 13818c2ecf20Sopenharmony_ci if (!rc && encap_handle != INVALID_TUNNEL_HANDLE) 13828c2ecf20Sopenharmony_ci hwrm_cfa_encap_record_free(bp, encap_handle); 13838c2ecf20Sopenharmony_ci} 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci/* Lookup the tunnel encap table and check if there's an encap_handle 13868c2ecf20Sopenharmony_ci * alloc'd already. 13878c2ecf20Sopenharmony_ci * If not, query L2 info via a route lookup and issue an encap_record_alloc 13888c2ecf20Sopenharmony_ci * cmd to FW. 13898c2ecf20Sopenharmony_ci */ 13908c2ecf20Sopenharmony_cistatic int bnxt_tc_get_encap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, 13918c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node, 13928c2ecf20Sopenharmony_ci __le32 *encap_handle) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci struct ip_tunnel_key *encap_key = &flow->actions.tun_encap_key; 13958c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 13968c2ecf20Sopenharmony_ci struct bnxt_tc_tunnel_node *encap_node; 13978c2ecf20Sopenharmony_ci int rc; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci /* Check if there's another flow using the same tunnel encap. 14008c2ecf20Sopenharmony_ci * If not, add this tunnel to the table and resolve the other 14018c2ecf20Sopenharmony_ci * tunnel header fileds 14028c2ecf20Sopenharmony_ci */ 14038c2ecf20Sopenharmony_ci encap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->encap_table, 14048c2ecf20Sopenharmony_ci &tc_info->encap_ht_params, 14058c2ecf20Sopenharmony_ci encap_key); 14068c2ecf20Sopenharmony_ci if (!encap_node) 14078c2ecf20Sopenharmony_ci return -ENOMEM; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci flow_node->encap_node = encap_node; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci if (encap_node->tunnel_handle != INVALID_TUNNEL_HANDLE) 14128c2ecf20Sopenharmony_ci goto done; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci rc = bnxt_tc_resolve_tunnel_hdrs(bp, encap_key, &encap_node->l2_info); 14158c2ecf20Sopenharmony_ci if (rc) 14168c2ecf20Sopenharmony_ci goto put_encap; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* Allocate a new tunnel encap record */ 14198c2ecf20Sopenharmony_ci rc = hwrm_cfa_encap_record_alloc(bp, encap_key, &encap_node->l2_info, 14208c2ecf20Sopenharmony_ci &encap_node->tunnel_handle); 14218c2ecf20Sopenharmony_ci if (rc) 14228c2ecf20Sopenharmony_ci goto put_encap; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_cidone: 14258c2ecf20Sopenharmony_ci *encap_handle = encap_node->tunnel_handle; 14268c2ecf20Sopenharmony_ci return 0; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ciput_encap: 14298c2ecf20Sopenharmony_ci bnxt_tc_put_tunnel_node(bp, &tc_info->encap_table, 14308c2ecf20Sopenharmony_ci &tc_info->encap_ht_params, encap_node); 14318c2ecf20Sopenharmony_ci return rc; 14328c2ecf20Sopenharmony_ci} 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_cistatic void bnxt_tc_put_tunnel_handle(struct bnxt *bp, 14358c2ecf20Sopenharmony_ci struct bnxt_tc_flow *flow, 14368c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node) 14378c2ecf20Sopenharmony_ci{ 14388c2ecf20Sopenharmony_ci if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP) 14398c2ecf20Sopenharmony_ci bnxt_tc_put_decap_handle(bp, flow_node); 14408c2ecf20Sopenharmony_ci else if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) 14418c2ecf20Sopenharmony_ci bnxt_tc_put_encap_handle(bp, flow_node->encap_node); 14428c2ecf20Sopenharmony_ci} 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_cistatic int bnxt_tc_get_tunnel_handle(struct bnxt *bp, 14458c2ecf20Sopenharmony_ci struct bnxt_tc_flow *flow, 14468c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node, 14478c2ecf20Sopenharmony_ci __le32 *tunnel_handle) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP) 14508c2ecf20Sopenharmony_ci return bnxt_tc_get_decap_handle(bp, flow, flow_node, 14518c2ecf20Sopenharmony_ci tunnel_handle); 14528c2ecf20Sopenharmony_ci else if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) 14538c2ecf20Sopenharmony_ci return bnxt_tc_get_encap_handle(bp, flow, flow_node, 14548c2ecf20Sopenharmony_ci tunnel_handle); 14558c2ecf20Sopenharmony_ci else 14568c2ecf20Sopenharmony_ci return 0; 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_cistatic int __bnxt_tc_del_flow(struct bnxt *bp, 14598c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node) 14608c2ecf20Sopenharmony_ci{ 14618c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 14628c2ecf20Sopenharmony_ci int rc; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci /* send HWRM cmd to free the flow-id */ 14658c2ecf20Sopenharmony_ci bnxt_hwrm_cfa_flow_free(bp, flow_node); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci mutex_lock(&tc_info->lock); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci /* release references to any tunnel encap/decap nodes */ 14708c2ecf20Sopenharmony_ci bnxt_tc_put_tunnel_handle(bp, &flow_node->flow, flow_node); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci /* release reference to l2 node */ 14738c2ecf20Sopenharmony_ci bnxt_tc_put_l2_node(bp, flow_node); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci mutex_unlock(&tc_info->lock); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci rc = rhashtable_remove_fast(&tc_info->flow_table, &flow_node->node, 14788c2ecf20Sopenharmony_ci tc_info->flow_ht_params); 14798c2ecf20Sopenharmony_ci if (rc) 14808c2ecf20Sopenharmony_ci netdev_err(bp->dev, "Error: %s: rhashtable_remove_fast rc=%d\n", 14818c2ecf20Sopenharmony_ci __func__, rc); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci kfree_rcu(flow_node, rcu); 14848c2ecf20Sopenharmony_ci return 0; 14858c2ecf20Sopenharmony_ci} 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_cistatic void bnxt_tc_set_flow_dir(struct bnxt *bp, struct bnxt_tc_flow *flow, 14888c2ecf20Sopenharmony_ci u16 src_fid) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci flow->l2_key.dir = (bp->pf.fw_fid == src_fid) ? BNXT_DIR_RX : BNXT_DIR_TX; 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_cistatic void bnxt_tc_set_src_fid(struct bnxt *bp, struct bnxt_tc_flow *flow, 14948c2ecf20Sopenharmony_ci u16 src_fid) 14958c2ecf20Sopenharmony_ci{ 14968c2ecf20Sopenharmony_ci if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP) 14978c2ecf20Sopenharmony_ci flow->src_fid = bp->pf.fw_fid; 14988c2ecf20Sopenharmony_ci else 14998c2ecf20Sopenharmony_ci flow->src_fid = src_fid; 15008c2ecf20Sopenharmony_ci} 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci/* Add a new flow or replace an existing flow. 15038c2ecf20Sopenharmony_ci * Notes on locking: 15048c2ecf20Sopenharmony_ci * There are essentially two critical sections here. 15058c2ecf20Sopenharmony_ci * 1. while adding a new flow 15068c2ecf20Sopenharmony_ci * a) lookup l2-key 15078c2ecf20Sopenharmony_ci * b) issue HWRM cmd and get flow_handle 15088c2ecf20Sopenharmony_ci * c) link l2-key with flow 15098c2ecf20Sopenharmony_ci * 2. while deleting a flow 15108c2ecf20Sopenharmony_ci * a) unlinking l2-key from flow 15118c2ecf20Sopenharmony_ci * A lock is needed to protect these two critical sections. 15128c2ecf20Sopenharmony_ci * 15138c2ecf20Sopenharmony_ci * The hash-tables are already protected by the rhashtable API. 15148c2ecf20Sopenharmony_ci */ 15158c2ecf20Sopenharmony_cistatic int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid, 15168c2ecf20Sopenharmony_ci struct flow_cls_offload *tc_flow_cmd) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *new_node, *old_node; 15198c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 15208c2ecf20Sopenharmony_ci struct bnxt_tc_flow *flow; 15218c2ecf20Sopenharmony_ci __le32 tunnel_handle = 0; 15228c2ecf20Sopenharmony_ci __le16 ref_flow_handle; 15238c2ecf20Sopenharmony_ci int rc; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci /* allocate memory for the new flow and it's node */ 15268c2ecf20Sopenharmony_ci new_node = kzalloc(sizeof(*new_node), GFP_KERNEL); 15278c2ecf20Sopenharmony_ci if (!new_node) { 15288c2ecf20Sopenharmony_ci rc = -ENOMEM; 15298c2ecf20Sopenharmony_ci goto done; 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci new_node->cookie = tc_flow_cmd->cookie; 15328c2ecf20Sopenharmony_ci flow = &new_node->flow; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci rc = bnxt_tc_parse_flow(bp, tc_flow_cmd, flow); 15358c2ecf20Sopenharmony_ci if (rc) 15368c2ecf20Sopenharmony_ci goto free_node; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci bnxt_tc_set_src_fid(bp, flow, src_fid); 15398c2ecf20Sopenharmony_ci bnxt_tc_set_flow_dir(bp, flow, flow->src_fid); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci if (!bnxt_tc_can_offload(bp, flow)) { 15428c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 15438c2ecf20Sopenharmony_ci kfree_rcu(new_node, rcu); 15448c2ecf20Sopenharmony_ci return rc; 15458c2ecf20Sopenharmony_ci } 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci /* If a flow exists with the same cookie, delete it */ 15488c2ecf20Sopenharmony_ci old_node = rhashtable_lookup_fast(&tc_info->flow_table, 15498c2ecf20Sopenharmony_ci &tc_flow_cmd->cookie, 15508c2ecf20Sopenharmony_ci tc_info->flow_ht_params); 15518c2ecf20Sopenharmony_ci if (old_node) 15528c2ecf20Sopenharmony_ci __bnxt_tc_del_flow(bp, old_node); 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci /* Check if the L2 part of the flow has been offloaded already. 15558c2ecf20Sopenharmony_ci * If so, bump up it's refcnt and get it's reference handle. 15568c2ecf20Sopenharmony_ci */ 15578c2ecf20Sopenharmony_ci mutex_lock(&tc_info->lock); 15588c2ecf20Sopenharmony_ci rc = bnxt_tc_get_ref_flow_handle(bp, flow, new_node, &ref_flow_handle); 15598c2ecf20Sopenharmony_ci if (rc) 15608c2ecf20Sopenharmony_ci goto unlock; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci /* If the flow involves tunnel encap/decap, get tunnel_handle */ 15638c2ecf20Sopenharmony_ci rc = bnxt_tc_get_tunnel_handle(bp, flow, new_node, &tunnel_handle); 15648c2ecf20Sopenharmony_ci if (rc) 15658c2ecf20Sopenharmony_ci goto put_l2; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci /* send HWRM cmd to alloc the flow */ 15688c2ecf20Sopenharmony_ci rc = bnxt_hwrm_cfa_flow_alloc(bp, flow, ref_flow_handle, 15698c2ecf20Sopenharmony_ci tunnel_handle, new_node); 15708c2ecf20Sopenharmony_ci if (rc) 15718c2ecf20Sopenharmony_ci goto put_tunnel; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci flow->lastused = jiffies; 15748c2ecf20Sopenharmony_ci spin_lock_init(&flow->stats_lock); 15758c2ecf20Sopenharmony_ci /* add new flow to flow-table */ 15768c2ecf20Sopenharmony_ci rc = rhashtable_insert_fast(&tc_info->flow_table, &new_node->node, 15778c2ecf20Sopenharmony_ci tc_info->flow_ht_params); 15788c2ecf20Sopenharmony_ci if (rc) 15798c2ecf20Sopenharmony_ci goto hwrm_flow_free; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci mutex_unlock(&tc_info->lock); 15828c2ecf20Sopenharmony_ci return 0; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_cihwrm_flow_free: 15858c2ecf20Sopenharmony_ci bnxt_hwrm_cfa_flow_free(bp, new_node); 15868c2ecf20Sopenharmony_ciput_tunnel: 15878c2ecf20Sopenharmony_ci bnxt_tc_put_tunnel_handle(bp, flow, new_node); 15888c2ecf20Sopenharmony_ciput_l2: 15898c2ecf20Sopenharmony_ci bnxt_tc_put_l2_node(bp, new_node); 15908c2ecf20Sopenharmony_ciunlock: 15918c2ecf20Sopenharmony_ci mutex_unlock(&tc_info->lock); 15928c2ecf20Sopenharmony_cifree_node: 15938c2ecf20Sopenharmony_ci kfree_rcu(new_node, rcu); 15948c2ecf20Sopenharmony_cidone: 15958c2ecf20Sopenharmony_ci netdev_err(bp->dev, "Error: %s: cookie=0x%lx error=%d\n", 15968c2ecf20Sopenharmony_ci __func__, tc_flow_cmd->cookie, rc); 15978c2ecf20Sopenharmony_ci return rc; 15988c2ecf20Sopenharmony_ci} 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_cistatic int bnxt_tc_del_flow(struct bnxt *bp, 16018c2ecf20Sopenharmony_ci struct flow_cls_offload *tc_flow_cmd) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 16048c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci flow_node = rhashtable_lookup_fast(&tc_info->flow_table, 16078c2ecf20Sopenharmony_ci &tc_flow_cmd->cookie, 16088c2ecf20Sopenharmony_ci tc_info->flow_ht_params); 16098c2ecf20Sopenharmony_ci if (!flow_node) 16108c2ecf20Sopenharmony_ci return -EINVAL; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci return __bnxt_tc_del_flow(bp, flow_node); 16138c2ecf20Sopenharmony_ci} 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_cistatic int bnxt_tc_get_flow_stats(struct bnxt *bp, 16168c2ecf20Sopenharmony_ci struct flow_cls_offload *tc_flow_cmd) 16178c2ecf20Sopenharmony_ci{ 16188c2ecf20Sopenharmony_ci struct bnxt_tc_flow_stats stats, *curr_stats, *prev_stats; 16198c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 16208c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node; 16218c2ecf20Sopenharmony_ci struct bnxt_tc_flow *flow; 16228c2ecf20Sopenharmony_ci unsigned long lastused; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci flow_node = rhashtable_lookup_fast(&tc_info->flow_table, 16258c2ecf20Sopenharmony_ci &tc_flow_cmd->cookie, 16268c2ecf20Sopenharmony_ci tc_info->flow_ht_params); 16278c2ecf20Sopenharmony_ci if (!flow_node) 16288c2ecf20Sopenharmony_ci return -1; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci flow = &flow_node->flow; 16318c2ecf20Sopenharmony_ci curr_stats = &flow->stats; 16328c2ecf20Sopenharmony_ci prev_stats = &flow->prev_stats; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci spin_lock(&flow->stats_lock); 16358c2ecf20Sopenharmony_ci stats.packets = curr_stats->packets - prev_stats->packets; 16368c2ecf20Sopenharmony_ci stats.bytes = curr_stats->bytes - prev_stats->bytes; 16378c2ecf20Sopenharmony_ci *prev_stats = *curr_stats; 16388c2ecf20Sopenharmony_ci lastused = flow->lastused; 16398c2ecf20Sopenharmony_ci spin_unlock(&flow->stats_lock); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci flow_stats_update(&tc_flow_cmd->stats, stats.bytes, stats.packets, 0, 16428c2ecf20Sopenharmony_ci lastused, FLOW_ACTION_HW_STATS_DELAYED); 16438c2ecf20Sopenharmony_ci return 0; 16448c2ecf20Sopenharmony_ci} 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_cistatic void bnxt_fill_cfa_stats_req(struct bnxt *bp, 16478c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node, 16488c2ecf20Sopenharmony_ci __le16 *flow_handle, __le32 *flow_id) 16498c2ecf20Sopenharmony_ci{ 16508c2ecf20Sopenharmony_ci u16 handle; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) { 16538c2ecf20Sopenharmony_ci *flow_id = flow_node->flow_id; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci /* If flow_id is used to fetch flow stats then: 16568c2ecf20Sopenharmony_ci * 1. lower 12 bits of flow_handle must be set to all 1s. 16578c2ecf20Sopenharmony_ci * 2. 15th bit of flow_handle must specify the flow 16588c2ecf20Sopenharmony_ci * direction (TX/RX). 16598c2ecf20Sopenharmony_ci */ 16608c2ecf20Sopenharmony_ci if (flow_node->flow.l2_key.dir == BNXT_DIR_RX) 16618c2ecf20Sopenharmony_ci handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_DIR_RX | 16628c2ecf20Sopenharmony_ci CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK; 16638c2ecf20Sopenharmony_ci else 16648c2ecf20Sopenharmony_ci handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci *flow_handle = cpu_to_le16(handle); 16678c2ecf20Sopenharmony_ci } else { 16688c2ecf20Sopenharmony_ci *flow_handle = flow_node->flow_handle; 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cistatic int 16738c2ecf20Sopenharmony_cibnxt_hwrm_cfa_flow_stats_get(struct bnxt *bp, int num_flows, 16748c2ecf20Sopenharmony_ci struct bnxt_tc_stats_batch stats_batch[]) 16758c2ecf20Sopenharmony_ci{ 16768c2ecf20Sopenharmony_ci struct hwrm_cfa_flow_stats_input req = { 0 }; 16778c2ecf20Sopenharmony_ci struct hwrm_cfa_flow_stats_output *resp; 16788c2ecf20Sopenharmony_ci __le16 *req_flow_handles = &req.flow_handle_0; 16798c2ecf20Sopenharmony_ci __le32 *req_flow_ids = &req.flow_id_0; 16808c2ecf20Sopenharmony_ci int rc, i; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_STATS, -1, -1); 16838c2ecf20Sopenharmony_ci req.num_flows = cpu_to_le16(num_flows); 16848c2ecf20Sopenharmony_ci for (i = 0; i < num_flows; i++) { 16858c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci bnxt_fill_cfa_stats_req(bp, flow_node, 16888c2ecf20Sopenharmony_ci &req_flow_handles[i], &req_flow_ids[i]); 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci mutex_lock(&bp->hwrm_cmd_lock); 16928c2ecf20Sopenharmony_ci rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 16938c2ecf20Sopenharmony_ci if (!rc) { 16948c2ecf20Sopenharmony_ci __le64 *resp_packets; 16958c2ecf20Sopenharmony_ci __le64 *resp_bytes; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci resp = bnxt_get_hwrm_resp_addr(bp, &req); 16988c2ecf20Sopenharmony_ci resp_packets = &resp->packet_0; 16998c2ecf20Sopenharmony_ci resp_bytes = &resp->byte_0; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci for (i = 0; i < num_flows; i++) { 17028c2ecf20Sopenharmony_ci stats_batch[i].hw_stats.packets = 17038c2ecf20Sopenharmony_ci le64_to_cpu(resp_packets[i]); 17048c2ecf20Sopenharmony_ci stats_batch[i].hw_stats.bytes = 17058c2ecf20Sopenharmony_ci le64_to_cpu(resp_bytes[i]); 17068c2ecf20Sopenharmony_ci } 17078c2ecf20Sopenharmony_ci } else { 17088c2ecf20Sopenharmony_ci netdev_info(bp->dev, "error rc=%d\n", rc); 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci mutex_unlock(&bp->hwrm_cmd_lock); 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci return rc; 17138c2ecf20Sopenharmony_ci} 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci/* Add val to accum while handling a possible wraparound 17168c2ecf20Sopenharmony_ci * of val. Eventhough val is of type u64, its actual width 17178c2ecf20Sopenharmony_ci * is denoted by mask and will wrap-around beyond that width. 17188c2ecf20Sopenharmony_ci */ 17198c2ecf20Sopenharmony_cistatic void accumulate_val(u64 *accum, u64 val, u64 mask) 17208c2ecf20Sopenharmony_ci{ 17218c2ecf20Sopenharmony_ci#define low_bits(x, mask) ((x) & (mask)) 17228c2ecf20Sopenharmony_ci#define high_bits(x, mask) ((x) & ~(mask)) 17238c2ecf20Sopenharmony_ci bool wrapped = val < low_bits(*accum, mask); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci *accum = high_bits(*accum, mask) + val; 17268c2ecf20Sopenharmony_ci if (wrapped) 17278c2ecf20Sopenharmony_ci *accum += (mask + 1); 17288c2ecf20Sopenharmony_ci} 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci/* The HW counters' width is much less than 64bits. 17318c2ecf20Sopenharmony_ci * Handle possible wrap-around while updating the stat counters 17328c2ecf20Sopenharmony_ci */ 17338c2ecf20Sopenharmony_cistatic void bnxt_flow_stats_accum(struct bnxt_tc_info *tc_info, 17348c2ecf20Sopenharmony_ci struct bnxt_tc_flow_stats *acc_stats, 17358c2ecf20Sopenharmony_ci struct bnxt_tc_flow_stats *hw_stats) 17368c2ecf20Sopenharmony_ci{ 17378c2ecf20Sopenharmony_ci accumulate_val(&acc_stats->bytes, hw_stats->bytes, tc_info->bytes_mask); 17388c2ecf20Sopenharmony_ci accumulate_val(&acc_stats->packets, hw_stats->packets, 17398c2ecf20Sopenharmony_ci tc_info->packets_mask); 17408c2ecf20Sopenharmony_ci} 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_cistatic int 17438c2ecf20Sopenharmony_cibnxt_tc_flow_stats_batch_update(struct bnxt *bp, int num_flows, 17448c2ecf20Sopenharmony_ci struct bnxt_tc_stats_batch stats_batch[]) 17458c2ecf20Sopenharmony_ci{ 17468c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 17478c2ecf20Sopenharmony_ci int rc, i; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci rc = bnxt_hwrm_cfa_flow_stats_get(bp, num_flows, stats_batch); 17508c2ecf20Sopenharmony_ci if (rc) 17518c2ecf20Sopenharmony_ci return rc; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci for (i = 0; i < num_flows; i++) { 17548c2ecf20Sopenharmony_ci struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node; 17558c2ecf20Sopenharmony_ci struct bnxt_tc_flow *flow = &flow_node->flow; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci spin_lock(&flow->stats_lock); 17588c2ecf20Sopenharmony_ci bnxt_flow_stats_accum(tc_info, &flow->stats, 17598c2ecf20Sopenharmony_ci &stats_batch[i].hw_stats); 17608c2ecf20Sopenharmony_ci if (flow->stats.packets != flow->prev_stats.packets) 17618c2ecf20Sopenharmony_ci flow->lastused = jiffies; 17628c2ecf20Sopenharmony_ci spin_unlock(&flow->stats_lock); 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci return 0; 17668c2ecf20Sopenharmony_ci} 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_cistatic int 17698c2ecf20Sopenharmony_cibnxt_tc_flow_stats_batch_prep(struct bnxt *bp, 17708c2ecf20Sopenharmony_ci struct bnxt_tc_stats_batch stats_batch[], 17718c2ecf20Sopenharmony_ci int *num_flows) 17728c2ecf20Sopenharmony_ci{ 17738c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 17748c2ecf20Sopenharmony_ci struct rhashtable_iter *iter = &tc_info->iter; 17758c2ecf20Sopenharmony_ci void *flow_node; 17768c2ecf20Sopenharmony_ci int rc, i; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci rhashtable_walk_start(iter); 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci rc = 0; 17818c2ecf20Sopenharmony_ci for (i = 0; i < BNXT_FLOW_STATS_BATCH_MAX; i++) { 17828c2ecf20Sopenharmony_ci flow_node = rhashtable_walk_next(iter); 17838c2ecf20Sopenharmony_ci if (IS_ERR(flow_node)) { 17848c2ecf20Sopenharmony_ci i = 0; 17858c2ecf20Sopenharmony_ci if (PTR_ERR(flow_node) == -EAGAIN) { 17868c2ecf20Sopenharmony_ci continue; 17878c2ecf20Sopenharmony_ci } else { 17888c2ecf20Sopenharmony_ci rc = PTR_ERR(flow_node); 17898c2ecf20Sopenharmony_ci goto done; 17908c2ecf20Sopenharmony_ci } 17918c2ecf20Sopenharmony_ci } 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci /* No more flows */ 17948c2ecf20Sopenharmony_ci if (!flow_node) 17958c2ecf20Sopenharmony_ci goto done; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci stats_batch[i].flow_node = flow_node; 17988c2ecf20Sopenharmony_ci } 17998c2ecf20Sopenharmony_cidone: 18008c2ecf20Sopenharmony_ci rhashtable_walk_stop(iter); 18018c2ecf20Sopenharmony_ci *num_flows = i; 18028c2ecf20Sopenharmony_ci return rc; 18038c2ecf20Sopenharmony_ci} 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_civoid bnxt_tc_flow_stats_work(struct bnxt *bp) 18068c2ecf20Sopenharmony_ci{ 18078c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 18088c2ecf20Sopenharmony_ci int num_flows, rc; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci num_flows = atomic_read(&tc_info->flow_table.nelems); 18118c2ecf20Sopenharmony_ci if (!num_flows) 18128c2ecf20Sopenharmony_ci return; 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci rhashtable_walk_enter(&tc_info->flow_table, &tc_info->iter); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci for (;;) { 18178c2ecf20Sopenharmony_ci rc = bnxt_tc_flow_stats_batch_prep(bp, tc_info->stats_batch, 18188c2ecf20Sopenharmony_ci &num_flows); 18198c2ecf20Sopenharmony_ci if (rc) { 18208c2ecf20Sopenharmony_ci if (rc == -EAGAIN) 18218c2ecf20Sopenharmony_ci continue; 18228c2ecf20Sopenharmony_ci break; 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci if (!num_flows) 18268c2ecf20Sopenharmony_ci break; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci bnxt_tc_flow_stats_batch_update(bp, num_flows, 18298c2ecf20Sopenharmony_ci tc_info->stats_batch); 18308c2ecf20Sopenharmony_ci } 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci rhashtable_walk_exit(&tc_info->iter); 18338c2ecf20Sopenharmony_ci} 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ciint bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid, 18368c2ecf20Sopenharmony_ci struct flow_cls_offload *cls_flower) 18378c2ecf20Sopenharmony_ci{ 18388c2ecf20Sopenharmony_ci switch (cls_flower->command) { 18398c2ecf20Sopenharmony_ci case FLOW_CLS_REPLACE: 18408c2ecf20Sopenharmony_ci return bnxt_tc_add_flow(bp, src_fid, cls_flower); 18418c2ecf20Sopenharmony_ci case FLOW_CLS_DESTROY: 18428c2ecf20Sopenharmony_ci return bnxt_tc_del_flow(bp, cls_flower); 18438c2ecf20Sopenharmony_ci case FLOW_CLS_STATS: 18448c2ecf20Sopenharmony_ci return bnxt_tc_get_flow_stats(bp, cls_flower); 18458c2ecf20Sopenharmony_ci default: 18468c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_cistatic int bnxt_tc_setup_indr_block_cb(enum tc_setup_type type, 18518c2ecf20Sopenharmony_ci void *type_data, void *cb_priv) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci struct bnxt_flower_indr_block_cb_priv *priv = cb_priv; 18548c2ecf20Sopenharmony_ci struct flow_cls_offload *flower = type_data; 18558c2ecf20Sopenharmony_ci struct bnxt *bp = priv->bp; 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci if (!tc_cls_can_offload_and_chain0(bp->dev, type_data)) 18588c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci switch (type) { 18618c2ecf20Sopenharmony_ci case TC_SETUP_CLSFLOWER: 18628c2ecf20Sopenharmony_ci return bnxt_tc_setup_flower(bp, bp->pf.fw_fid, flower); 18638c2ecf20Sopenharmony_ci default: 18648c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_cistatic struct bnxt_flower_indr_block_cb_priv * 18698c2ecf20Sopenharmony_cibnxt_tc_indr_block_cb_lookup(struct bnxt *bp, struct net_device *netdev) 18708c2ecf20Sopenharmony_ci{ 18718c2ecf20Sopenharmony_ci struct bnxt_flower_indr_block_cb_priv *cb_priv; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci list_for_each_entry(cb_priv, &bp->tc_indr_block_list, list) 18748c2ecf20Sopenharmony_ci if (cb_priv->tunnel_netdev == netdev) 18758c2ecf20Sopenharmony_ci return cb_priv; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci return NULL; 18788c2ecf20Sopenharmony_ci} 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_cistatic void bnxt_tc_setup_indr_rel(void *cb_priv) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci struct bnxt_flower_indr_block_cb_priv *priv = cb_priv; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci list_del(&priv->list); 18858c2ecf20Sopenharmony_ci kfree(priv); 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_cistatic int bnxt_tc_setup_indr_block(struct net_device *netdev, struct Qdisc *sch, struct bnxt *bp, 18898c2ecf20Sopenharmony_ci struct flow_block_offload *f, void *data, 18908c2ecf20Sopenharmony_ci void (*cleanup)(struct flow_block_cb *block_cb)) 18918c2ecf20Sopenharmony_ci{ 18928c2ecf20Sopenharmony_ci struct bnxt_flower_indr_block_cb_priv *cb_priv; 18938c2ecf20Sopenharmony_ci struct flow_block_cb *block_cb; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 18968c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci switch (f->command) { 18998c2ecf20Sopenharmony_ci case FLOW_BLOCK_BIND: 19008c2ecf20Sopenharmony_ci cb_priv = kmalloc(sizeof(*cb_priv), GFP_KERNEL); 19018c2ecf20Sopenharmony_ci if (!cb_priv) 19028c2ecf20Sopenharmony_ci return -ENOMEM; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci cb_priv->tunnel_netdev = netdev; 19058c2ecf20Sopenharmony_ci cb_priv->bp = bp; 19068c2ecf20Sopenharmony_ci list_add(&cb_priv->list, &bp->tc_indr_block_list); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci block_cb = flow_indr_block_cb_alloc(bnxt_tc_setup_indr_block_cb, 19098c2ecf20Sopenharmony_ci cb_priv, cb_priv, 19108c2ecf20Sopenharmony_ci bnxt_tc_setup_indr_rel, f, 19118c2ecf20Sopenharmony_ci netdev, sch, data, bp, cleanup); 19128c2ecf20Sopenharmony_ci if (IS_ERR(block_cb)) { 19138c2ecf20Sopenharmony_ci list_del(&cb_priv->list); 19148c2ecf20Sopenharmony_ci kfree(cb_priv); 19158c2ecf20Sopenharmony_ci return PTR_ERR(block_cb); 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci flow_block_cb_add(block_cb, f); 19198c2ecf20Sopenharmony_ci list_add_tail(&block_cb->driver_list, &bnxt_block_cb_list); 19208c2ecf20Sopenharmony_ci break; 19218c2ecf20Sopenharmony_ci case FLOW_BLOCK_UNBIND: 19228c2ecf20Sopenharmony_ci cb_priv = bnxt_tc_indr_block_cb_lookup(bp, netdev); 19238c2ecf20Sopenharmony_ci if (!cb_priv) 19248c2ecf20Sopenharmony_ci return -ENOENT; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, 19278c2ecf20Sopenharmony_ci bnxt_tc_setup_indr_block_cb, 19288c2ecf20Sopenharmony_ci cb_priv); 19298c2ecf20Sopenharmony_ci if (!block_cb) 19308c2ecf20Sopenharmony_ci return -ENOENT; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci flow_indr_block_cb_remove(block_cb, f); 19338c2ecf20Sopenharmony_ci list_del(&block_cb->driver_list); 19348c2ecf20Sopenharmony_ci break; 19358c2ecf20Sopenharmony_ci default: 19368c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 19378c2ecf20Sopenharmony_ci } 19388c2ecf20Sopenharmony_ci return 0; 19398c2ecf20Sopenharmony_ci} 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_cistatic bool bnxt_is_netdev_indr_offload(struct net_device *netdev) 19428c2ecf20Sopenharmony_ci{ 19438c2ecf20Sopenharmony_ci return netif_is_vxlan(netdev); 19448c2ecf20Sopenharmony_ci} 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_cistatic int bnxt_tc_setup_indr_cb(struct net_device *netdev, struct Qdisc *sch, void *cb_priv, 19478c2ecf20Sopenharmony_ci enum tc_setup_type type, void *type_data, 19488c2ecf20Sopenharmony_ci void *data, 19498c2ecf20Sopenharmony_ci void (*cleanup)(struct flow_block_cb *block_cb)) 19508c2ecf20Sopenharmony_ci{ 19518c2ecf20Sopenharmony_ci if (!bnxt_is_netdev_indr_offload(netdev)) 19528c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci switch (type) { 19558c2ecf20Sopenharmony_ci case TC_SETUP_BLOCK: 19568c2ecf20Sopenharmony_ci return bnxt_tc_setup_indr_block(netdev, sch, cb_priv, type_data, data, cleanup); 19578c2ecf20Sopenharmony_ci default: 19588c2ecf20Sopenharmony_ci break; 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 19628c2ecf20Sopenharmony_ci} 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_cistatic const struct rhashtable_params bnxt_tc_flow_ht_params = { 19658c2ecf20Sopenharmony_ci .head_offset = offsetof(struct bnxt_tc_flow_node, node), 19668c2ecf20Sopenharmony_ci .key_offset = offsetof(struct bnxt_tc_flow_node, cookie), 19678c2ecf20Sopenharmony_ci .key_len = sizeof(((struct bnxt_tc_flow_node *)0)->cookie), 19688c2ecf20Sopenharmony_ci .automatic_shrinking = true 19698c2ecf20Sopenharmony_ci}; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_cistatic const struct rhashtable_params bnxt_tc_l2_ht_params = { 19728c2ecf20Sopenharmony_ci .head_offset = offsetof(struct bnxt_tc_l2_node, node), 19738c2ecf20Sopenharmony_ci .key_offset = offsetof(struct bnxt_tc_l2_node, key), 19748c2ecf20Sopenharmony_ci .key_len = BNXT_TC_L2_KEY_LEN, 19758c2ecf20Sopenharmony_ci .automatic_shrinking = true 19768c2ecf20Sopenharmony_ci}; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_cistatic const struct rhashtable_params bnxt_tc_decap_l2_ht_params = { 19798c2ecf20Sopenharmony_ci .head_offset = offsetof(struct bnxt_tc_l2_node, node), 19808c2ecf20Sopenharmony_ci .key_offset = offsetof(struct bnxt_tc_l2_node, key), 19818c2ecf20Sopenharmony_ci .key_len = BNXT_TC_L2_KEY_LEN, 19828c2ecf20Sopenharmony_ci .automatic_shrinking = true 19838c2ecf20Sopenharmony_ci}; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_cistatic const struct rhashtable_params bnxt_tc_tunnel_ht_params = { 19868c2ecf20Sopenharmony_ci .head_offset = offsetof(struct bnxt_tc_tunnel_node, node), 19878c2ecf20Sopenharmony_ci .key_offset = offsetof(struct bnxt_tc_tunnel_node, key), 19888c2ecf20Sopenharmony_ci .key_len = sizeof(struct ip_tunnel_key), 19898c2ecf20Sopenharmony_ci .automatic_shrinking = true 19908c2ecf20Sopenharmony_ci}; 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci/* convert counter width in bits to a mask */ 19938c2ecf20Sopenharmony_ci#define mask(width) ((u64)~0 >> (64 - (width))) 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ciint bnxt_init_tc(struct bnxt *bp) 19968c2ecf20Sopenharmony_ci{ 19978c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info; 19988c2ecf20Sopenharmony_ci int rc; 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci if (bp->hwrm_spec_code < 0x10803) 20018c2ecf20Sopenharmony_ci return 0; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci tc_info = kzalloc(sizeof(*tc_info), GFP_KERNEL); 20048c2ecf20Sopenharmony_ci if (!tc_info) 20058c2ecf20Sopenharmony_ci return -ENOMEM; 20068c2ecf20Sopenharmony_ci mutex_init(&tc_info->lock); 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci /* Counter widths are programmed by FW */ 20098c2ecf20Sopenharmony_ci tc_info->bytes_mask = mask(36); 20108c2ecf20Sopenharmony_ci tc_info->packets_mask = mask(28); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci tc_info->flow_ht_params = bnxt_tc_flow_ht_params; 20138c2ecf20Sopenharmony_ci rc = rhashtable_init(&tc_info->flow_table, &tc_info->flow_ht_params); 20148c2ecf20Sopenharmony_ci if (rc) 20158c2ecf20Sopenharmony_ci goto free_tc_info; 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci tc_info->l2_ht_params = bnxt_tc_l2_ht_params; 20188c2ecf20Sopenharmony_ci rc = rhashtable_init(&tc_info->l2_table, &tc_info->l2_ht_params); 20198c2ecf20Sopenharmony_ci if (rc) 20208c2ecf20Sopenharmony_ci goto destroy_flow_table; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci tc_info->decap_l2_ht_params = bnxt_tc_decap_l2_ht_params; 20238c2ecf20Sopenharmony_ci rc = rhashtable_init(&tc_info->decap_l2_table, 20248c2ecf20Sopenharmony_ci &tc_info->decap_l2_ht_params); 20258c2ecf20Sopenharmony_ci if (rc) 20268c2ecf20Sopenharmony_ci goto destroy_l2_table; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci tc_info->decap_ht_params = bnxt_tc_tunnel_ht_params; 20298c2ecf20Sopenharmony_ci rc = rhashtable_init(&tc_info->decap_table, 20308c2ecf20Sopenharmony_ci &tc_info->decap_ht_params); 20318c2ecf20Sopenharmony_ci if (rc) 20328c2ecf20Sopenharmony_ci goto destroy_decap_l2_table; 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci tc_info->encap_ht_params = bnxt_tc_tunnel_ht_params; 20358c2ecf20Sopenharmony_ci rc = rhashtable_init(&tc_info->encap_table, 20368c2ecf20Sopenharmony_ci &tc_info->encap_ht_params); 20378c2ecf20Sopenharmony_ci if (rc) 20388c2ecf20Sopenharmony_ci goto destroy_decap_table; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci tc_info->enabled = true; 20418c2ecf20Sopenharmony_ci bp->dev->hw_features |= NETIF_F_HW_TC; 20428c2ecf20Sopenharmony_ci bp->dev->features |= NETIF_F_HW_TC; 20438c2ecf20Sopenharmony_ci bp->tc_info = tc_info; 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci /* init indirect block notifications */ 20468c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bp->tc_indr_block_list); 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci rc = flow_indr_dev_register(bnxt_tc_setup_indr_cb, bp); 20498c2ecf20Sopenharmony_ci if (!rc) 20508c2ecf20Sopenharmony_ci return 0; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci rhashtable_destroy(&tc_info->encap_table); 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_cidestroy_decap_table: 20558c2ecf20Sopenharmony_ci rhashtable_destroy(&tc_info->decap_table); 20568c2ecf20Sopenharmony_cidestroy_decap_l2_table: 20578c2ecf20Sopenharmony_ci rhashtable_destroy(&tc_info->decap_l2_table); 20588c2ecf20Sopenharmony_cidestroy_l2_table: 20598c2ecf20Sopenharmony_ci rhashtable_destroy(&tc_info->l2_table); 20608c2ecf20Sopenharmony_cidestroy_flow_table: 20618c2ecf20Sopenharmony_ci rhashtable_destroy(&tc_info->flow_table); 20628c2ecf20Sopenharmony_cifree_tc_info: 20638c2ecf20Sopenharmony_ci kfree(tc_info); 20648c2ecf20Sopenharmony_ci bp->tc_info = NULL; 20658c2ecf20Sopenharmony_ci return rc; 20668c2ecf20Sopenharmony_ci} 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_civoid bnxt_shutdown_tc(struct bnxt *bp) 20698c2ecf20Sopenharmony_ci{ 20708c2ecf20Sopenharmony_ci struct bnxt_tc_info *tc_info = bp->tc_info; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci if (!bnxt_tc_flower_enabled(bp)) 20738c2ecf20Sopenharmony_ci return; 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci flow_indr_dev_unregister(bnxt_tc_setup_indr_cb, bp, 20768c2ecf20Sopenharmony_ci bnxt_tc_setup_indr_rel); 20778c2ecf20Sopenharmony_ci rhashtable_destroy(&tc_info->flow_table); 20788c2ecf20Sopenharmony_ci rhashtable_destroy(&tc_info->l2_table); 20798c2ecf20Sopenharmony_ci rhashtable_destroy(&tc_info->decap_l2_table); 20808c2ecf20Sopenharmony_ci rhashtable_destroy(&tc_info->decap_table); 20818c2ecf20Sopenharmony_ci rhashtable_destroy(&tc_info->encap_table); 20828c2ecf20Sopenharmony_ci kfree(tc_info); 20838c2ecf20Sopenharmony_ci bp->tc_info = NULL; 20848c2ecf20Sopenharmony_ci} 2085