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(&eth_addr_mask[0], ETH_ALEN)) {
1618c2ecf20Sopenharmony_ci		if (!is_exactmatch(&eth_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(&eth_addr_mask[ETH_ALEN / 2], ETH_ALEN)) {
1708c2ecf20Sopenharmony_ci		if (!is_exactmatch(&eth_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 = &eth_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, &eth_addr[offset],
2078c2ecf20Sopenharmony_ci				     &eth_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