162306a36Sopenharmony_ci/* Broadcom NetXtreme-C/E network driver.
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * Copyright (c) 2017 Broadcom Limited
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
662306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by
762306a36Sopenharmony_ci * the Free Software Foundation.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/netdevice.h>
1162306a36Sopenharmony_ci#include <linux/inetdevice.h>
1262306a36Sopenharmony_ci#include <linux/if_vlan.h>
1362306a36Sopenharmony_ci#include <net/flow_dissector.h>
1462306a36Sopenharmony_ci#include <net/pkt_cls.h>
1562306a36Sopenharmony_ci#include <net/tc_act/tc_gact.h>
1662306a36Sopenharmony_ci#include <net/tc_act/tc_skbedit.h>
1762306a36Sopenharmony_ci#include <net/tc_act/tc_mirred.h>
1862306a36Sopenharmony_ci#include <net/tc_act/tc_vlan.h>
1962306a36Sopenharmony_ci#include <net/tc_act/tc_pedit.h>
2062306a36Sopenharmony_ci#include <net/tc_act/tc_tunnel_key.h>
2162306a36Sopenharmony_ci#include <net/vxlan.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "bnxt_hsi.h"
2462306a36Sopenharmony_ci#include "bnxt.h"
2562306a36Sopenharmony_ci#include "bnxt_hwrm.h"
2662306a36Sopenharmony_ci#include "bnxt_sriov.h"
2762306a36Sopenharmony_ci#include "bnxt_tc.h"
2862306a36Sopenharmony_ci#include "bnxt_vfr.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define BNXT_FID_INVALID			0xffff
3162306a36Sopenharmony_ci#define VLAN_TCI(vid, prio)	((vid) | ((prio) << VLAN_PRIO_SHIFT))
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define is_vlan_pcp_wildcarded(vlan_tci_mask)	\
3462306a36Sopenharmony_ci	((ntohs(vlan_tci_mask) & VLAN_PRIO_MASK) == 0x0000)
3562306a36Sopenharmony_ci#define is_vlan_pcp_exactmatch(vlan_tci_mask)	\
3662306a36Sopenharmony_ci	((ntohs(vlan_tci_mask) & VLAN_PRIO_MASK) == VLAN_PRIO_MASK)
3762306a36Sopenharmony_ci#define is_vlan_pcp_zero(vlan_tci)	\
3862306a36Sopenharmony_ci	((ntohs(vlan_tci) & VLAN_PRIO_MASK) == 0x0000)
3962306a36Sopenharmony_ci#define is_vid_exactmatch(vlan_tci_mask)	\
4062306a36Sopenharmony_ci	((ntohs(vlan_tci_mask) & VLAN_VID_MASK) == VLAN_VID_MASK)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic bool is_wildcard(void *mask, int len);
4362306a36Sopenharmony_cistatic bool is_exactmatch(void *mask, int len);
4462306a36Sopenharmony_ci/* Return the dst fid of the func for flow forwarding
4562306a36Sopenharmony_ci * For PFs: src_fid is the fid of the PF
4662306a36Sopenharmony_ci * For VF-reps: src_fid the fid of the VF
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic u16 bnxt_flow_get_dst_fid(struct bnxt *pf_bp, struct net_device *dev)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct bnxt *bp;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* check if dev belongs to the same switch */
5362306a36Sopenharmony_ci	if (!netdev_port_same_parent_id(pf_bp->dev, dev)) {
5462306a36Sopenharmony_ci		netdev_info(pf_bp->dev, "dev(ifindex=%d) not on same switch\n",
5562306a36Sopenharmony_ci			    dev->ifindex);
5662306a36Sopenharmony_ci		return BNXT_FID_INVALID;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* Is dev a VF-rep? */
6062306a36Sopenharmony_ci	if (bnxt_dev_is_vf_rep(dev))
6162306a36Sopenharmony_ci		return bnxt_vf_rep_get_fid(dev);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	bp = netdev_priv(dev);
6462306a36Sopenharmony_ci	return bp->pf.fw_fid;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int bnxt_tc_parse_redir(struct bnxt *bp,
6862306a36Sopenharmony_ci			       struct bnxt_tc_actions *actions,
6962306a36Sopenharmony_ci			       const struct flow_action_entry *act)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct net_device *dev = act->dev;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (!dev) {
7462306a36Sopenharmony_ci		netdev_info(bp->dev, "no dev in mirred action\n");
7562306a36Sopenharmony_ci		return -EINVAL;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	actions->flags |= BNXT_TC_ACTION_FLAG_FWD;
7962306a36Sopenharmony_ci	actions->dst_dev = dev;
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int bnxt_tc_parse_vlan(struct bnxt *bp,
8462306a36Sopenharmony_ci			      struct bnxt_tc_actions *actions,
8562306a36Sopenharmony_ci			      const struct flow_action_entry *act)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	switch (act->id) {
8862306a36Sopenharmony_ci	case FLOW_ACTION_VLAN_POP:
8962306a36Sopenharmony_ci		actions->flags |= BNXT_TC_ACTION_FLAG_POP_VLAN;
9062306a36Sopenharmony_ci		break;
9162306a36Sopenharmony_ci	case FLOW_ACTION_VLAN_PUSH:
9262306a36Sopenharmony_ci		actions->flags |= BNXT_TC_ACTION_FLAG_PUSH_VLAN;
9362306a36Sopenharmony_ci		actions->push_vlan_tci = htons(act->vlan.vid);
9462306a36Sopenharmony_ci		actions->push_vlan_tpid = act->vlan.proto;
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci	default:
9762306a36Sopenharmony_ci		return -EOPNOTSUPP;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int bnxt_tc_parse_tunnel_set(struct bnxt *bp,
10362306a36Sopenharmony_ci				    struct bnxt_tc_actions *actions,
10462306a36Sopenharmony_ci				    const struct flow_action_entry *act)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	const struct ip_tunnel_info *tun_info = act->tunnel;
10762306a36Sopenharmony_ci	const struct ip_tunnel_key *tun_key = &tun_info->key;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (ip_tunnel_info_af(tun_info) != AF_INET) {
11062306a36Sopenharmony_ci		netdev_info(bp->dev, "only IPv4 tunnel-encap is supported\n");
11162306a36Sopenharmony_ci		return -EOPNOTSUPP;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	actions->tun_encap_key = *tun_key;
11562306a36Sopenharmony_ci	actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP;
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/* Key & Mask from the stack comes unaligned in multiple iterations of 4 bytes
12062306a36Sopenharmony_ci * each(u32).
12162306a36Sopenharmony_ci * This routine consolidates such multiple unaligned values into one
12262306a36Sopenharmony_ci * field each for Key & Mask (for src and dst macs separately)
12362306a36Sopenharmony_ci * For example,
12462306a36Sopenharmony_ci *			Mask/Key	Offset	Iteration
12562306a36Sopenharmony_ci *			==========	======	=========
12662306a36Sopenharmony_ci *	dst mac		0xffffffff	0	1
12762306a36Sopenharmony_ci *	dst mac		0x0000ffff	4	2
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci *	src mac		0xffff0000	4	1
13062306a36Sopenharmony_ci *	src mac		0xffffffff	8	2
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * The above combination coming from the stack will be consolidated as
13362306a36Sopenharmony_ci *			Mask/Key
13462306a36Sopenharmony_ci *			==============
13562306a36Sopenharmony_ci *	src mac:	0xffffffffffff
13662306a36Sopenharmony_ci *	dst mac:	0xffffffffffff
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistatic void bnxt_set_l2_key_mask(u32 part_key, u32 part_mask,
13962306a36Sopenharmony_ci				 u8 *actual_key, u8 *actual_mask)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	u32 key = get_unaligned((u32 *)actual_key);
14262306a36Sopenharmony_ci	u32 mask = get_unaligned((u32 *)actual_mask);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	part_key &= part_mask;
14562306a36Sopenharmony_ci	part_key |= key & ~part_mask;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	put_unaligned(mask | part_mask, (u32 *)actual_mask);
14862306a36Sopenharmony_ci	put_unaligned(part_key, (u32 *)actual_key);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int
15262306a36Sopenharmony_cibnxt_fill_l2_rewrite_fields(struct bnxt_tc_actions *actions,
15362306a36Sopenharmony_ci			    u16 *eth_addr, u16 *eth_addr_mask)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	u16 *p;
15662306a36Sopenharmony_ci	int j;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (unlikely(bnxt_eth_addr_key_mask_invalid(eth_addr, eth_addr_mask)))
15962306a36Sopenharmony_ci		return -EINVAL;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (!is_wildcard(&eth_addr_mask[0], ETH_ALEN)) {
16262306a36Sopenharmony_ci		if (!is_exactmatch(&eth_addr_mask[0], ETH_ALEN))
16362306a36Sopenharmony_ci			return -EINVAL;
16462306a36Sopenharmony_ci		/* FW expects dmac to be in u16 array format */
16562306a36Sopenharmony_ci		p = eth_addr;
16662306a36Sopenharmony_ci		for (j = 0; j < 3; j++)
16762306a36Sopenharmony_ci			actions->l2_rewrite_dmac[j] = cpu_to_be16(*(p + j));
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (!is_wildcard(&eth_addr_mask[ETH_ALEN / 2], ETH_ALEN)) {
17162306a36Sopenharmony_ci		if (!is_exactmatch(&eth_addr_mask[ETH_ALEN / 2], ETH_ALEN))
17262306a36Sopenharmony_ci			return -EINVAL;
17362306a36Sopenharmony_ci		/* FW expects smac to be in u16 array format */
17462306a36Sopenharmony_ci		p = &eth_addr[ETH_ALEN / 2];
17562306a36Sopenharmony_ci		for (j = 0; j < 3; j++)
17662306a36Sopenharmony_ci			actions->l2_rewrite_smac[j] = cpu_to_be16(*(p + j));
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return 0;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic int
18362306a36Sopenharmony_cibnxt_tc_parse_pedit(struct bnxt *bp, struct bnxt_tc_actions *actions,
18462306a36Sopenharmony_ci		    struct flow_action_entry *act, int act_idx, u8 *eth_addr,
18562306a36Sopenharmony_ci		    u8 *eth_addr_mask)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	size_t offset_of_ip6_daddr = offsetof(struct ipv6hdr, daddr);
18862306a36Sopenharmony_ci	size_t offset_of_ip6_saddr = offsetof(struct ipv6hdr, saddr);
18962306a36Sopenharmony_ci	u32 mask, val, offset, idx;
19062306a36Sopenharmony_ci	u8 htype;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	offset = act->mangle.offset;
19362306a36Sopenharmony_ci	htype = act->mangle.htype;
19462306a36Sopenharmony_ci	mask = ~act->mangle.mask;
19562306a36Sopenharmony_ci	val = act->mangle.val;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	switch (htype) {
19862306a36Sopenharmony_ci	case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
19962306a36Sopenharmony_ci		if (offset > PEDIT_OFFSET_SMAC_LAST_4_BYTES) {
20062306a36Sopenharmony_ci			netdev_err(bp->dev,
20162306a36Sopenharmony_ci				   "%s: eth_hdr: Invalid pedit field\n",
20262306a36Sopenharmony_ci				   __func__);
20362306a36Sopenharmony_ci			return -EINVAL;
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci		actions->flags |= BNXT_TC_ACTION_FLAG_L2_REWRITE;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		bnxt_set_l2_key_mask(val, mask, &eth_addr[offset],
20862306a36Sopenharmony_ci				     &eth_addr_mask[offset]);
20962306a36Sopenharmony_ci		break;
21062306a36Sopenharmony_ci	case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
21162306a36Sopenharmony_ci		actions->flags |= BNXT_TC_ACTION_FLAG_NAT_XLATE;
21262306a36Sopenharmony_ci		actions->nat.l3_is_ipv4 = true;
21362306a36Sopenharmony_ci		if (offset ==  offsetof(struct iphdr, saddr)) {
21462306a36Sopenharmony_ci			actions->nat.src_xlate = true;
21562306a36Sopenharmony_ci			actions->nat.l3.ipv4.saddr.s_addr = htonl(val);
21662306a36Sopenharmony_ci		} else if (offset ==  offsetof(struct iphdr, daddr)) {
21762306a36Sopenharmony_ci			actions->nat.src_xlate = false;
21862306a36Sopenharmony_ci			actions->nat.l3.ipv4.daddr.s_addr = htonl(val);
21962306a36Sopenharmony_ci		} else {
22062306a36Sopenharmony_ci			netdev_err(bp->dev,
22162306a36Sopenharmony_ci				   "%s: IPv4_hdr: Invalid pedit field\n",
22262306a36Sopenharmony_ci				   __func__);
22362306a36Sopenharmony_ci			return -EINVAL;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		netdev_dbg(bp->dev, "nat.src_xlate = %d src IP: %pI4 dst ip : %pI4\n",
22762306a36Sopenharmony_ci			   actions->nat.src_xlate, &actions->nat.l3.ipv4.saddr,
22862306a36Sopenharmony_ci			   &actions->nat.l3.ipv4.daddr);
22962306a36Sopenharmony_ci		break;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	case FLOW_ACT_MANGLE_HDR_TYPE_IP6:
23262306a36Sopenharmony_ci		actions->flags |= BNXT_TC_ACTION_FLAG_NAT_XLATE;
23362306a36Sopenharmony_ci		actions->nat.l3_is_ipv4 = false;
23462306a36Sopenharmony_ci		if (offset >= offsetof(struct ipv6hdr, saddr) &&
23562306a36Sopenharmony_ci		    offset < offset_of_ip6_daddr) {
23662306a36Sopenharmony_ci			/* 16 byte IPv6 address comes in 4 iterations of
23762306a36Sopenharmony_ci			 * 4byte chunks each
23862306a36Sopenharmony_ci			 */
23962306a36Sopenharmony_ci			actions->nat.src_xlate = true;
24062306a36Sopenharmony_ci			idx = (offset - offset_of_ip6_saddr) / 4;
24162306a36Sopenharmony_ci			/* First 4bytes will be copied to idx 0 and so on */
24262306a36Sopenharmony_ci			actions->nat.l3.ipv6.saddr.s6_addr32[idx] = htonl(val);
24362306a36Sopenharmony_ci		} else if (offset >= offset_of_ip6_daddr &&
24462306a36Sopenharmony_ci			   offset < offset_of_ip6_daddr + 16) {
24562306a36Sopenharmony_ci			actions->nat.src_xlate = false;
24662306a36Sopenharmony_ci			idx = (offset - offset_of_ip6_daddr) / 4;
24762306a36Sopenharmony_ci			actions->nat.l3.ipv6.saddr.s6_addr32[idx] = htonl(val);
24862306a36Sopenharmony_ci		} else {
24962306a36Sopenharmony_ci			netdev_err(bp->dev,
25062306a36Sopenharmony_ci				   "%s: IPv6_hdr: Invalid pedit field\n",
25162306a36Sopenharmony_ci				   __func__);
25262306a36Sopenharmony_ci			return -EINVAL;
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci		break;
25562306a36Sopenharmony_ci	case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
25662306a36Sopenharmony_ci	case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
25762306a36Sopenharmony_ci		/* HW does not support L4 rewrite alone without L3
25862306a36Sopenharmony_ci		 * rewrite
25962306a36Sopenharmony_ci		 */
26062306a36Sopenharmony_ci		if (!(actions->flags & BNXT_TC_ACTION_FLAG_NAT_XLATE)) {
26162306a36Sopenharmony_ci			netdev_err(bp->dev,
26262306a36Sopenharmony_ci				   "Need to specify L3 rewrite as well\n");
26362306a36Sopenharmony_ci			return -EINVAL;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci		if (actions->nat.src_xlate)
26662306a36Sopenharmony_ci			actions->nat.l4.ports.sport = htons(val);
26762306a36Sopenharmony_ci		else
26862306a36Sopenharmony_ci			actions->nat.l4.ports.dport = htons(val);
26962306a36Sopenharmony_ci		netdev_dbg(bp->dev, "actions->nat.sport = %d dport = %d\n",
27062306a36Sopenharmony_ci			   actions->nat.l4.ports.sport,
27162306a36Sopenharmony_ci			   actions->nat.l4.ports.dport);
27262306a36Sopenharmony_ci		break;
27362306a36Sopenharmony_ci	default:
27462306a36Sopenharmony_ci		netdev_err(bp->dev, "%s: Unsupported pedit hdr type\n",
27562306a36Sopenharmony_ci			   __func__);
27662306a36Sopenharmony_ci		return -EINVAL;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	return 0;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic int bnxt_tc_parse_actions(struct bnxt *bp,
28262306a36Sopenharmony_ci				 struct bnxt_tc_actions *actions,
28362306a36Sopenharmony_ci				 struct flow_action *flow_action,
28462306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	/* Used to store the L2 rewrite mask for dmac (6 bytes) followed by
28762306a36Sopenharmony_ci	 * smac (6 bytes) if rewrite of both is specified, otherwise either
28862306a36Sopenharmony_ci	 * dmac or smac
28962306a36Sopenharmony_ci	 */
29062306a36Sopenharmony_ci	u16 eth_addr_mask[ETH_ALEN] = { 0 };
29162306a36Sopenharmony_ci	/* Used to store the L2 rewrite key for dmac (6 bytes) followed by
29262306a36Sopenharmony_ci	 * smac (6 bytes) if rewrite of both is specified, otherwise either
29362306a36Sopenharmony_ci	 * dmac or smac
29462306a36Sopenharmony_ci	 */
29562306a36Sopenharmony_ci	u16 eth_addr[ETH_ALEN] = { 0 };
29662306a36Sopenharmony_ci	struct flow_action_entry *act;
29762306a36Sopenharmony_ci	int i, rc;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (!flow_action_has_entries(flow_action)) {
30062306a36Sopenharmony_ci		netdev_info(bp->dev, "no actions\n");
30162306a36Sopenharmony_ci		return -EINVAL;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (!flow_action_basic_hw_stats_check(flow_action, extack))
30562306a36Sopenharmony_ci		return -EOPNOTSUPP;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	flow_action_for_each(i, act, flow_action) {
30862306a36Sopenharmony_ci		switch (act->id) {
30962306a36Sopenharmony_ci		case FLOW_ACTION_DROP:
31062306a36Sopenharmony_ci			actions->flags |= BNXT_TC_ACTION_FLAG_DROP;
31162306a36Sopenharmony_ci			return 0; /* don't bother with other actions */
31262306a36Sopenharmony_ci		case FLOW_ACTION_REDIRECT:
31362306a36Sopenharmony_ci			rc = bnxt_tc_parse_redir(bp, actions, act);
31462306a36Sopenharmony_ci			if (rc)
31562306a36Sopenharmony_ci				return rc;
31662306a36Sopenharmony_ci			break;
31762306a36Sopenharmony_ci		case FLOW_ACTION_VLAN_POP:
31862306a36Sopenharmony_ci		case FLOW_ACTION_VLAN_PUSH:
31962306a36Sopenharmony_ci		case FLOW_ACTION_VLAN_MANGLE:
32062306a36Sopenharmony_ci			rc = bnxt_tc_parse_vlan(bp, actions, act);
32162306a36Sopenharmony_ci			if (rc)
32262306a36Sopenharmony_ci				return rc;
32362306a36Sopenharmony_ci			break;
32462306a36Sopenharmony_ci		case FLOW_ACTION_TUNNEL_ENCAP:
32562306a36Sopenharmony_ci			rc = bnxt_tc_parse_tunnel_set(bp, actions, act);
32662306a36Sopenharmony_ci			if (rc)
32762306a36Sopenharmony_ci				return rc;
32862306a36Sopenharmony_ci			break;
32962306a36Sopenharmony_ci		case FLOW_ACTION_TUNNEL_DECAP:
33062306a36Sopenharmony_ci			actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_DECAP;
33162306a36Sopenharmony_ci			break;
33262306a36Sopenharmony_ci		/* Packet edit: L2 rewrite, NAT, NAPT */
33362306a36Sopenharmony_ci		case FLOW_ACTION_MANGLE:
33462306a36Sopenharmony_ci			rc = bnxt_tc_parse_pedit(bp, actions, act, i,
33562306a36Sopenharmony_ci						 (u8 *)eth_addr,
33662306a36Sopenharmony_ci						 (u8 *)eth_addr_mask);
33762306a36Sopenharmony_ci			if (rc)
33862306a36Sopenharmony_ci				return rc;
33962306a36Sopenharmony_ci			break;
34062306a36Sopenharmony_ci		default:
34162306a36Sopenharmony_ci			break;
34262306a36Sopenharmony_ci		}
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (actions->flags & BNXT_TC_ACTION_FLAG_L2_REWRITE) {
34662306a36Sopenharmony_ci		rc = bnxt_fill_l2_rewrite_fields(actions, eth_addr,
34762306a36Sopenharmony_ci						 eth_addr_mask);
34862306a36Sopenharmony_ci		if (rc)
34962306a36Sopenharmony_ci			return rc;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) {
35362306a36Sopenharmony_ci		if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) {
35462306a36Sopenharmony_ci			/* dst_fid is PF's fid */
35562306a36Sopenharmony_ci			actions->dst_fid = bp->pf.fw_fid;
35662306a36Sopenharmony_ci		} else {
35762306a36Sopenharmony_ci			/* find the FID from dst_dev */
35862306a36Sopenharmony_ci			actions->dst_fid =
35962306a36Sopenharmony_ci				bnxt_flow_get_dst_fid(bp, actions->dst_dev);
36062306a36Sopenharmony_ci			if (actions->dst_fid == BNXT_FID_INVALID)
36162306a36Sopenharmony_ci				return -EINVAL;
36262306a36Sopenharmony_ci		}
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return 0;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic int bnxt_tc_parse_flow(struct bnxt *bp,
36962306a36Sopenharmony_ci			      struct flow_cls_offload *tc_flow_cmd,
37062306a36Sopenharmony_ci			      struct bnxt_tc_flow *flow)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct flow_rule *rule = flow_cls_offload_flow_rule(tc_flow_cmd);
37362306a36Sopenharmony_ci	struct flow_dissector *dissector = rule->match.dissector;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */
37662306a36Sopenharmony_ci	if ((dissector->used_keys & BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL)) == 0 ||
37762306a36Sopenharmony_ci	    (dissector->used_keys & BIT_ULL(FLOW_DISSECTOR_KEY_BASIC)) == 0) {
37862306a36Sopenharmony_ci		netdev_info(bp->dev, "cannot form TC key: used_keys = 0x%llx\n",
37962306a36Sopenharmony_ci			    dissector->used_keys);
38062306a36Sopenharmony_ci		return -EOPNOTSUPP;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
38462306a36Sopenharmony_ci		struct flow_match_basic match;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		flow_rule_match_basic(rule, &match);
38762306a36Sopenharmony_ci		flow->l2_key.ether_type = match.key->n_proto;
38862306a36Sopenharmony_ci		flow->l2_mask.ether_type = match.mask->n_proto;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		if (match.key->n_proto == htons(ETH_P_IP) ||
39162306a36Sopenharmony_ci		    match.key->n_proto == htons(ETH_P_IPV6)) {
39262306a36Sopenharmony_ci			flow->l4_key.ip_proto = match.key->ip_proto;
39362306a36Sopenharmony_ci			flow->l4_mask.ip_proto = match.mask->ip_proto;
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
39862306a36Sopenharmony_ci		struct flow_match_eth_addrs match;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		flow_rule_match_eth_addrs(rule, &match);
40162306a36Sopenharmony_ci		flow->flags |= BNXT_TC_FLOW_FLAGS_ETH_ADDRS;
40262306a36Sopenharmony_ci		ether_addr_copy(flow->l2_key.dmac, match.key->dst);
40362306a36Sopenharmony_ci		ether_addr_copy(flow->l2_mask.dmac, match.mask->dst);
40462306a36Sopenharmony_ci		ether_addr_copy(flow->l2_key.smac, match.key->src);
40562306a36Sopenharmony_ci		ether_addr_copy(flow->l2_mask.smac, match.mask->src);
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
40962306a36Sopenharmony_ci		struct flow_match_vlan match;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		flow_rule_match_vlan(rule, &match);
41262306a36Sopenharmony_ci		flow->l2_key.inner_vlan_tci =
41362306a36Sopenharmony_ci			cpu_to_be16(VLAN_TCI(match.key->vlan_id,
41462306a36Sopenharmony_ci					     match.key->vlan_priority));
41562306a36Sopenharmony_ci		flow->l2_mask.inner_vlan_tci =
41662306a36Sopenharmony_ci			cpu_to_be16((VLAN_TCI(match.mask->vlan_id,
41762306a36Sopenharmony_ci					      match.mask->vlan_priority)));
41862306a36Sopenharmony_ci		flow->l2_key.inner_vlan_tpid = htons(ETH_P_8021Q);
41962306a36Sopenharmony_ci		flow->l2_mask.inner_vlan_tpid = htons(0xffff);
42062306a36Sopenharmony_ci		flow->l2_key.num_vlans = 1;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
42462306a36Sopenharmony_ci		struct flow_match_ipv4_addrs match;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		flow_rule_match_ipv4_addrs(rule, &match);
42762306a36Sopenharmony_ci		flow->flags |= BNXT_TC_FLOW_FLAGS_IPV4_ADDRS;
42862306a36Sopenharmony_ci		flow->l3_key.ipv4.daddr.s_addr = match.key->dst;
42962306a36Sopenharmony_ci		flow->l3_mask.ipv4.daddr.s_addr = match.mask->dst;
43062306a36Sopenharmony_ci		flow->l3_key.ipv4.saddr.s_addr = match.key->src;
43162306a36Sopenharmony_ci		flow->l3_mask.ipv4.saddr.s_addr = match.mask->src;
43262306a36Sopenharmony_ci	} else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
43362306a36Sopenharmony_ci		struct flow_match_ipv6_addrs match;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		flow_rule_match_ipv6_addrs(rule, &match);
43662306a36Sopenharmony_ci		flow->flags |= BNXT_TC_FLOW_FLAGS_IPV6_ADDRS;
43762306a36Sopenharmony_ci		flow->l3_key.ipv6.daddr = match.key->dst;
43862306a36Sopenharmony_ci		flow->l3_mask.ipv6.daddr = match.mask->dst;
43962306a36Sopenharmony_ci		flow->l3_key.ipv6.saddr = match.key->src;
44062306a36Sopenharmony_ci		flow->l3_mask.ipv6.saddr = match.mask->src;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
44462306a36Sopenharmony_ci		struct flow_match_ports match;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		flow_rule_match_ports(rule, &match);
44762306a36Sopenharmony_ci		flow->flags |= BNXT_TC_FLOW_FLAGS_PORTS;
44862306a36Sopenharmony_ci		flow->l4_key.ports.dport = match.key->dst;
44962306a36Sopenharmony_ci		flow->l4_mask.ports.dport = match.mask->dst;
45062306a36Sopenharmony_ci		flow->l4_key.ports.sport = match.key->src;
45162306a36Sopenharmony_ci		flow->l4_mask.ports.sport = match.mask->src;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ICMP)) {
45562306a36Sopenharmony_ci		struct flow_match_icmp match;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		flow_rule_match_icmp(rule, &match);
45862306a36Sopenharmony_ci		flow->flags |= BNXT_TC_FLOW_FLAGS_ICMP;
45962306a36Sopenharmony_ci		flow->l4_key.icmp.type = match.key->type;
46062306a36Sopenharmony_ci		flow->l4_key.icmp.code = match.key->code;
46162306a36Sopenharmony_ci		flow->l4_mask.icmp.type = match.mask->type;
46262306a36Sopenharmony_ci		flow->l4_mask.icmp.code = match.mask->code;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) {
46662306a36Sopenharmony_ci		struct flow_match_ipv4_addrs match;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		flow_rule_match_enc_ipv4_addrs(rule, &match);
46962306a36Sopenharmony_ci		flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS;
47062306a36Sopenharmony_ci		flow->tun_key.u.ipv4.dst = match.key->dst;
47162306a36Sopenharmony_ci		flow->tun_mask.u.ipv4.dst = match.mask->dst;
47262306a36Sopenharmony_ci		flow->tun_key.u.ipv4.src = match.key->src;
47362306a36Sopenharmony_ci		flow->tun_mask.u.ipv4.src = match.mask->src;
47462306a36Sopenharmony_ci	} else if (flow_rule_match_key(rule,
47562306a36Sopenharmony_ci				      FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) {
47662306a36Sopenharmony_ci		return -EOPNOTSUPP;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
48062306a36Sopenharmony_ci		struct flow_match_enc_keyid match;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		flow_rule_match_enc_keyid(rule, &match);
48362306a36Sopenharmony_ci		flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ID;
48462306a36Sopenharmony_ci		flow->tun_key.tun_id = key32_to_tunnel_id(match.key->keyid);
48562306a36Sopenharmony_ci		flow->tun_mask.tun_id = key32_to_tunnel_id(match.mask->keyid);
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS)) {
48962306a36Sopenharmony_ci		struct flow_match_ports match;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		flow_rule_match_enc_ports(rule, &match);
49262306a36Sopenharmony_ci		flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_PORTS;
49362306a36Sopenharmony_ci		flow->tun_key.tp_dst = match.key->dst;
49462306a36Sopenharmony_ci		flow->tun_mask.tp_dst = match.mask->dst;
49562306a36Sopenharmony_ci		flow->tun_key.tp_src = match.key->src;
49662306a36Sopenharmony_ci		flow->tun_mask.tp_src = match.mask->src;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return bnxt_tc_parse_actions(bp, &flow->actions, &rule->action,
50062306a36Sopenharmony_ci				     tc_flow_cmd->common.extack);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic int bnxt_hwrm_cfa_flow_free(struct bnxt *bp,
50462306a36Sopenharmony_ci				   struct bnxt_tc_flow_node *flow_node)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct hwrm_cfa_flow_free_input *req;
50762306a36Sopenharmony_ci	int rc;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	rc = hwrm_req_init(bp, req, HWRM_CFA_FLOW_FREE);
51062306a36Sopenharmony_ci	if (!rc) {
51162306a36Sopenharmony_ci		if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE)
51262306a36Sopenharmony_ci			req->ext_flow_handle = flow_node->ext_flow_handle;
51362306a36Sopenharmony_ci		else
51462306a36Sopenharmony_ci			req->flow_handle = flow_node->flow_handle;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		rc = hwrm_req_send(bp, req);
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci	if (rc)
51962306a36Sopenharmony_ci		netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return rc;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int ipv6_mask_len(struct in6_addr *mask)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	int mask_len = 0, i;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
52962306a36Sopenharmony_ci		mask_len += inet_mask_len(mask->s6_addr32[i]);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	return mask_len;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic bool is_wildcard(void *mask, int len)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	const u8 *p = mask;
53762306a36Sopenharmony_ci	int i;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
54062306a36Sopenharmony_ci		if (p[i] != 0)
54162306a36Sopenharmony_ci			return false;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci	return true;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic bool is_exactmatch(void *mask, int len)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	const u8 *p = mask;
54962306a36Sopenharmony_ci	int i;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	for (i = 0; i < len; i++)
55262306a36Sopenharmony_ci		if (p[i] != 0xff)
55362306a36Sopenharmony_ci			return false;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return true;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic bool is_vlan_tci_allowed(__be16  vlan_tci_mask,
55962306a36Sopenharmony_ci				__be16  vlan_tci)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	/* VLAN priority must be either exactly zero or fully wildcarded and
56262306a36Sopenharmony_ci	 * VLAN id must be exact match.
56362306a36Sopenharmony_ci	 */
56462306a36Sopenharmony_ci	if (is_vid_exactmatch(vlan_tci_mask) &&
56562306a36Sopenharmony_ci	    ((is_vlan_pcp_exactmatch(vlan_tci_mask) &&
56662306a36Sopenharmony_ci	      is_vlan_pcp_zero(vlan_tci)) ||
56762306a36Sopenharmony_ci	     is_vlan_pcp_wildcarded(vlan_tci_mask)))
56862306a36Sopenharmony_ci		return true;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return false;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic bool bits_set(void *key, int len)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	const u8 *p = key;
57662306a36Sopenharmony_ci	int i;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	for (i = 0; i < len; i++)
57962306a36Sopenharmony_ci		if (p[i] != 0)
58062306a36Sopenharmony_ci			return true;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	return false;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow,
58662306a36Sopenharmony_ci				    __le16 ref_flow_handle,
58762306a36Sopenharmony_ci				    __le32 tunnel_handle,
58862306a36Sopenharmony_ci				    struct bnxt_tc_flow_node *flow_node)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct bnxt_tc_actions *actions = &flow->actions;
59162306a36Sopenharmony_ci	struct bnxt_tc_l3_key *l3_mask = &flow->l3_mask;
59262306a36Sopenharmony_ci	struct bnxt_tc_l3_key *l3_key = &flow->l3_key;
59362306a36Sopenharmony_ci	struct hwrm_cfa_flow_alloc_output *resp;
59462306a36Sopenharmony_ci	struct hwrm_cfa_flow_alloc_input *req;
59562306a36Sopenharmony_ci	u16 flow_flags = 0, action_flags = 0;
59662306a36Sopenharmony_ci	int rc;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	rc = hwrm_req_init(bp, req, HWRM_CFA_FLOW_ALLOC);
59962306a36Sopenharmony_ci	if (rc)
60062306a36Sopenharmony_ci		return rc;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	req->src_fid = cpu_to_le16(flow->src_fid);
60362306a36Sopenharmony_ci	req->ref_flow_handle = ref_flow_handle;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (actions->flags & BNXT_TC_ACTION_FLAG_L2_REWRITE) {
60662306a36Sopenharmony_ci		memcpy(req->l2_rewrite_dmac, actions->l2_rewrite_dmac,
60762306a36Sopenharmony_ci		       ETH_ALEN);
60862306a36Sopenharmony_ci		memcpy(req->l2_rewrite_smac, actions->l2_rewrite_smac,
60962306a36Sopenharmony_ci		       ETH_ALEN);
61062306a36Sopenharmony_ci		action_flags |=
61162306a36Sopenharmony_ci			CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (actions->flags & BNXT_TC_ACTION_FLAG_NAT_XLATE) {
61562306a36Sopenharmony_ci		if (actions->nat.l3_is_ipv4) {
61662306a36Sopenharmony_ci			action_flags |=
61762306a36Sopenharmony_ci				CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_IPV4_ADDRESS;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci			if (actions->nat.src_xlate) {
62062306a36Sopenharmony_ci				action_flags |=
62162306a36Sopenharmony_ci					CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC;
62262306a36Sopenharmony_ci				/* L3 source rewrite */
62362306a36Sopenharmony_ci				req->nat_ip_address[0] =
62462306a36Sopenharmony_ci					actions->nat.l3.ipv4.saddr.s_addr;
62562306a36Sopenharmony_ci				/* L4 source port */
62662306a36Sopenharmony_ci				if (actions->nat.l4.ports.sport)
62762306a36Sopenharmony_ci					req->nat_port =
62862306a36Sopenharmony_ci						actions->nat.l4.ports.sport;
62962306a36Sopenharmony_ci			} else {
63062306a36Sopenharmony_ci				action_flags |=
63162306a36Sopenharmony_ci					CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST;
63262306a36Sopenharmony_ci				/* L3 destination rewrite */
63362306a36Sopenharmony_ci				req->nat_ip_address[0] =
63462306a36Sopenharmony_ci					actions->nat.l3.ipv4.daddr.s_addr;
63562306a36Sopenharmony_ci				/* L4 destination port */
63662306a36Sopenharmony_ci				if (actions->nat.l4.ports.dport)
63762306a36Sopenharmony_ci					req->nat_port =
63862306a36Sopenharmony_ci						actions->nat.l4.ports.dport;
63962306a36Sopenharmony_ci			}
64062306a36Sopenharmony_ci			netdev_dbg(bp->dev,
64162306a36Sopenharmony_ci				   "req->nat_ip_address: %pI4 src_xlate: %d req->nat_port: %x\n",
64262306a36Sopenharmony_ci				   req->nat_ip_address, actions->nat.src_xlate,
64362306a36Sopenharmony_ci				   req->nat_port);
64462306a36Sopenharmony_ci		} else {
64562306a36Sopenharmony_ci			if (actions->nat.src_xlate) {
64662306a36Sopenharmony_ci				action_flags |=
64762306a36Sopenharmony_ci					CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC;
64862306a36Sopenharmony_ci				/* L3 source rewrite */
64962306a36Sopenharmony_ci				memcpy(req->nat_ip_address,
65062306a36Sopenharmony_ci				       actions->nat.l3.ipv6.saddr.s6_addr32,
65162306a36Sopenharmony_ci				       sizeof(req->nat_ip_address));
65262306a36Sopenharmony_ci				/* L4 source port */
65362306a36Sopenharmony_ci				if (actions->nat.l4.ports.sport)
65462306a36Sopenharmony_ci					req->nat_port =
65562306a36Sopenharmony_ci						actions->nat.l4.ports.sport;
65662306a36Sopenharmony_ci			} else {
65762306a36Sopenharmony_ci				action_flags |=
65862306a36Sopenharmony_ci					CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST;
65962306a36Sopenharmony_ci				/* L3 destination rewrite */
66062306a36Sopenharmony_ci				memcpy(req->nat_ip_address,
66162306a36Sopenharmony_ci				       actions->nat.l3.ipv6.daddr.s6_addr32,
66262306a36Sopenharmony_ci				       sizeof(req->nat_ip_address));
66362306a36Sopenharmony_ci				/* L4 destination port */
66462306a36Sopenharmony_ci				if (actions->nat.l4.ports.dport)
66562306a36Sopenharmony_ci					req->nat_port =
66662306a36Sopenharmony_ci						actions->nat.l4.ports.dport;
66762306a36Sopenharmony_ci			}
66862306a36Sopenharmony_ci			netdev_dbg(bp->dev,
66962306a36Sopenharmony_ci				   "req->nat_ip_address: %pI6 src_xlate: %d req->nat_port: %x\n",
67062306a36Sopenharmony_ci				   req->nat_ip_address, actions->nat.src_xlate,
67162306a36Sopenharmony_ci				   req->nat_port);
67262306a36Sopenharmony_ci		}
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP ||
67662306a36Sopenharmony_ci	    actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) {
67762306a36Sopenharmony_ci		req->tunnel_handle = tunnel_handle;
67862306a36Sopenharmony_ci		flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_TUNNEL;
67962306a36Sopenharmony_ci		action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	req->ethertype = flow->l2_key.ether_type;
68362306a36Sopenharmony_ci	req->ip_proto = flow->l4_key.ip_proto;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (flow->flags & BNXT_TC_FLOW_FLAGS_ETH_ADDRS) {
68662306a36Sopenharmony_ci		memcpy(req->dmac, flow->l2_key.dmac, ETH_ALEN);
68762306a36Sopenharmony_ci		memcpy(req->smac, flow->l2_key.smac, ETH_ALEN);
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (flow->l2_key.num_vlans > 0) {
69162306a36Sopenharmony_ci		flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_ONE;
69262306a36Sopenharmony_ci		/* FW expects the inner_vlan_tci value to be set
69362306a36Sopenharmony_ci		 * in outer_vlan_tci when num_vlans is 1 (which is
69462306a36Sopenharmony_ci		 * always the case in TC.)
69562306a36Sopenharmony_ci		 */
69662306a36Sopenharmony_ci		req->outer_vlan_tci = flow->l2_key.inner_vlan_tci;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* If all IP and L4 fields are wildcarded then this is an L2 flow */
70062306a36Sopenharmony_ci	if (is_wildcard(l3_mask, sizeof(*l3_mask)) &&
70162306a36Sopenharmony_ci	    is_wildcard(&flow->l4_mask, sizeof(flow->l4_mask))) {
70262306a36Sopenharmony_ci		flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_L2;
70362306a36Sopenharmony_ci	} else {
70462306a36Sopenharmony_ci		flow_flags |= flow->l2_key.ether_type == htons(ETH_P_IP) ?
70562306a36Sopenharmony_ci				CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV4 :
70662306a36Sopenharmony_ci				CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci		if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV4_ADDRS) {
70962306a36Sopenharmony_ci			req->ip_dst[0] = l3_key->ipv4.daddr.s_addr;
71062306a36Sopenharmony_ci			req->ip_dst_mask_len =
71162306a36Sopenharmony_ci				inet_mask_len(l3_mask->ipv4.daddr.s_addr);
71262306a36Sopenharmony_ci			req->ip_src[0] = l3_key->ipv4.saddr.s_addr;
71362306a36Sopenharmony_ci			req->ip_src_mask_len =
71462306a36Sopenharmony_ci				inet_mask_len(l3_mask->ipv4.saddr.s_addr);
71562306a36Sopenharmony_ci		} else if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV6_ADDRS) {
71662306a36Sopenharmony_ci			memcpy(req->ip_dst, l3_key->ipv6.daddr.s6_addr32,
71762306a36Sopenharmony_ci			       sizeof(req->ip_dst));
71862306a36Sopenharmony_ci			req->ip_dst_mask_len =
71962306a36Sopenharmony_ci					ipv6_mask_len(&l3_mask->ipv6.daddr);
72062306a36Sopenharmony_ci			memcpy(req->ip_src, l3_key->ipv6.saddr.s6_addr32,
72162306a36Sopenharmony_ci			       sizeof(req->ip_src));
72262306a36Sopenharmony_ci			req->ip_src_mask_len =
72362306a36Sopenharmony_ci					ipv6_mask_len(&l3_mask->ipv6.saddr);
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) {
72862306a36Sopenharmony_ci		req->l4_src_port = flow->l4_key.ports.sport;
72962306a36Sopenharmony_ci		req->l4_src_port_mask = flow->l4_mask.ports.sport;
73062306a36Sopenharmony_ci		req->l4_dst_port = flow->l4_key.ports.dport;
73162306a36Sopenharmony_ci		req->l4_dst_port_mask = flow->l4_mask.ports.dport;
73262306a36Sopenharmony_ci	} else if (flow->flags & BNXT_TC_FLOW_FLAGS_ICMP) {
73362306a36Sopenharmony_ci		/* l4 ports serve as type/code when ip_proto is ICMP */
73462306a36Sopenharmony_ci		req->l4_src_port = htons(flow->l4_key.icmp.type);
73562306a36Sopenharmony_ci		req->l4_src_port_mask = htons(flow->l4_mask.icmp.type);
73662306a36Sopenharmony_ci		req->l4_dst_port = htons(flow->l4_key.icmp.code);
73762306a36Sopenharmony_ci		req->l4_dst_port_mask = htons(flow->l4_mask.icmp.code);
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci	req->flags = cpu_to_le16(flow_flags);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (actions->flags & BNXT_TC_ACTION_FLAG_DROP) {
74262306a36Sopenharmony_ci		action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP;
74362306a36Sopenharmony_ci	} else {
74462306a36Sopenharmony_ci		if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) {
74562306a36Sopenharmony_ci			action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD;
74662306a36Sopenharmony_ci			req->dst_fid = cpu_to_le16(actions->dst_fid);
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci		if (actions->flags & BNXT_TC_ACTION_FLAG_PUSH_VLAN) {
74962306a36Sopenharmony_ci			action_flags |=
75062306a36Sopenharmony_ci			    CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE;
75162306a36Sopenharmony_ci			req->l2_rewrite_vlan_tpid = actions->push_vlan_tpid;
75262306a36Sopenharmony_ci			req->l2_rewrite_vlan_tci = actions->push_vlan_tci;
75362306a36Sopenharmony_ci			memcpy(&req->l2_rewrite_dmac, &req->dmac, ETH_ALEN);
75462306a36Sopenharmony_ci			memcpy(&req->l2_rewrite_smac, &req->smac, ETH_ALEN);
75562306a36Sopenharmony_ci		}
75662306a36Sopenharmony_ci		if (actions->flags & BNXT_TC_ACTION_FLAG_POP_VLAN) {
75762306a36Sopenharmony_ci			action_flags |=
75862306a36Sopenharmony_ci			    CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE;
75962306a36Sopenharmony_ci			/* Rewrite config with tpid = 0 implies vlan pop */
76062306a36Sopenharmony_ci			req->l2_rewrite_vlan_tpid = 0;
76162306a36Sopenharmony_ci			memcpy(&req->l2_rewrite_dmac, &req->dmac, ETH_ALEN);
76262306a36Sopenharmony_ci			memcpy(&req->l2_rewrite_smac, &req->smac, ETH_ALEN);
76362306a36Sopenharmony_ci		}
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci	req->action_flags = cpu_to_le16(action_flags);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	resp = hwrm_req_hold(bp, req);
76862306a36Sopenharmony_ci	rc = hwrm_req_send_silent(bp, req);
76962306a36Sopenharmony_ci	if (!rc) {
77062306a36Sopenharmony_ci		/* CFA_FLOW_ALLOC response interpretation:
77162306a36Sopenharmony_ci		 *		    fw with	     fw with
77262306a36Sopenharmony_ci		 *		    16-bit	     64-bit
77362306a36Sopenharmony_ci		 *		    flow handle      flow handle
77462306a36Sopenharmony_ci		 *		    ===========	     ===========
77562306a36Sopenharmony_ci		 * flow_handle      flow handle      flow context id
77662306a36Sopenharmony_ci		 * ext_flow_handle  INVALID	     flow handle
77762306a36Sopenharmony_ci		 * flow_id	    INVALID	     flow counter id
77862306a36Sopenharmony_ci		 */
77962306a36Sopenharmony_ci		flow_node->flow_handle = resp->flow_handle;
78062306a36Sopenharmony_ci		if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) {
78162306a36Sopenharmony_ci			flow_node->ext_flow_handle = resp->ext_flow_handle;
78262306a36Sopenharmony_ci			flow_node->flow_id = resp->flow_id;
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci	hwrm_req_drop(bp, req);
78662306a36Sopenharmony_ci	return rc;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic int hwrm_cfa_decap_filter_alloc(struct bnxt *bp,
79062306a36Sopenharmony_ci				       struct bnxt_tc_flow *flow,
79162306a36Sopenharmony_ci				       struct bnxt_tc_l2_key *l2_info,
79262306a36Sopenharmony_ci				       __le32 ref_decap_handle,
79362306a36Sopenharmony_ci				       __le32 *decap_filter_handle)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	struct hwrm_cfa_decap_filter_alloc_output *resp;
79662306a36Sopenharmony_ci	struct ip_tunnel_key *tun_key = &flow->tun_key;
79762306a36Sopenharmony_ci	struct hwrm_cfa_decap_filter_alloc_input *req;
79862306a36Sopenharmony_ci	u32 enables = 0;
79962306a36Sopenharmony_ci	int rc;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	rc = hwrm_req_init(bp, req, HWRM_CFA_DECAP_FILTER_ALLOC);
80262306a36Sopenharmony_ci	if (rc)
80362306a36Sopenharmony_ci		goto exit;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	req->flags = cpu_to_le32(CFA_DECAP_FILTER_ALLOC_REQ_FLAGS_OVS_TUNNEL);
80662306a36Sopenharmony_ci	enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE |
80762306a36Sopenharmony_ci		   CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL;
80862306a36Sopenharmony_ci	req->tunnel_type = CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN;
80962306a36Sopenharmony_ci	req->ip_protocol = CFA_DECAP_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_ID) {
81262306a36Sopenharmony_ci		enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_TUNNEL_ID;
81362306a36Sopenharmony_ci		/* tunnel_id is wrongly defined in hsi defn. as __le32 */
81462306a36Sopenharmony_ci		req->tunnel_id = tunnel_id_to_key32(tun_key->tun_id);
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS) {
81862306a36Sopenharmony_ci		enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_MACADDR;
81962306a36Sopenharmony_ci		ether_addr_copy(req->dst_macaddr, l2_info->dmac);
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci	if (l2_info->num_vlans) {
82262306a36Sopenharmony_ci		enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_T_IVLAN_VID;
82362306a36Sopenharmony_ci		req->t_ivlan_vid = l2_info->inner_vlan_tci;
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE;
82762306a36Sopenharmony_ci	req->ethertype = htons(ETH_P_IP);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS) {
83062306a36Sopenharmony_ci		enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR |
83162306a36Sopenharmony_ci			   CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR |
83262306a36Sopenharmony_ci			   CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE;
83362306a36Sopenharmony_ci		req->ip_addr_type =
83462306a36Sopenharmony_ci			CFA_DECAP_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4;
83562306a36Sopenharmony_ci		req->dst_ipaddr[0] = tun_key->u.ipv4.dst;
83662306a36Sopenharmony_ci		req->src_ipaddr[0] = tun_key->u.ipv4.src;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_PORTS) {
84062306a36Sopenharmony_ci		enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_PORT;
84162306a36Sopenharmony_ci		req->dst_port = tun_key->tp_dst;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/* Eventhough the decap_handle returned by hwrm_cfa_decap_filter_alloc
84562306a36Sopenharmony_ci	 * is defined as __le32, l2_ctxt_ref_id is defined in HSI as __le16.
84662306a36Sopenharmony_ci	 */
84762306a36Sopenharmony_ci	req->l2_ctxt_ref_id = (__force __le16)ref_decap_handle;
84862306a36Sopenharmony_ci	req->enables = cpu_to_le32(enables);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	resp = hwrm_req_hold(bp, req);
85162306a36Sopenharmony_ci	rc = hwrm_req_send_silent(bp, req);
85262306a36Sopenharmony_ci	if (!rc)
85362306a36Sopenharmony_ci		*decap_filter_handle = resp->decap_filter_id;
85462306a36Sopenharmony_ci	hwrm_req_drop(bp, req);
85562306a36Sopenharmony_ciexit:
85662306a36Sopenharmony_ci	if (rc)
85762306a36Sopenharmony_ci		netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	return rc;
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic int hwrm_cfa_decap_filter_free(struct bnxt *bp,
86362306a36Sopenharmony_ci				      __le32 decap_filter_handle)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct hwrm_cfa_decap_filter_free_input *req;
86662306a36Sopenharmony_ci	int rc;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	rc = hwrm_req_init(bp, req, HWRM_CFA_DECAP_FILTER_FREE);
86962306a36Sopenharmony_ci	if (!rc) {
87062306a36Sopenharmony_ci		req->decap_filter_id = decap_filter_handle;
87162306a36Sopenharmony_ci		rc = hwrm_req_send(bp, req);
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci	if (rc)
87462306a36Sopenharmony_ci		netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	return rc;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic int hwrm_cfa_encap_record_alloc(struct bnxt *bp,
88062306a36Sopenharmony_ci				       struct ip_tunnel_key *encap_key,
88162306a36Sopenharmony_ci				       struct bnxt_tc_l2_key *l2_info,
88262306a36Sopenharmony_ci				       __le32 *encap_record_handle)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct hwrm_cfa_encap_record_alloc_output *resp;
88562306a36Sopenharmony_ci	struct hwrm_cfa_encap_record_alloc_input *req;
88662306a36Sopenharmony_ci	struct hwrm_cfa_encap_data_vxlan *encap;
88762306a36Sopenharmony_ci	struct hwrm_vxlan_ipv4_hdr *encap_ipv4;
88862306a36Sopenharmony_ci	int rc;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	rc = hwrm_req_init(bp, req, HWRM_CFA_ENCAP_RECORD_ALLOC);
89162306a36Sopenharmony_ci	if (rc)
89262306a36Sopenharmony_ci		goto exit;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	encap = (struct hwrm_cfa_encap_data_vxlan *)&req->encap_data;
89562306a36Sopenharmony_ci	req->encap_type = CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_VXLAN;
89662306a36Sopenharmony_ci	ether_addr_copy(encap->dst_mac_addr, l2_info->dmac);
89762306a36Sopenharmony_ci	ether_addr_copy(encap->src_mac_addr, l2_info->smac);
89862306a36Sopenharmony_ci	if (l2_info->num_vlans) {
89962306a36Sopenharmony_ci		encap->num_vlan_tags = l2_info->num_vlans;
90062306a36Sopenharmony_ci		encap->ovlan_tci = l2_info->inner_vlan_tci;
90162306a36Sopenharmony_ci		encap->ovlan_tpid = l2_info->inner_vlan_tpid;
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	encap_ipv4 = (struct hwrm_vxlan_ipv4_hdr *)encap->l3;
90562306a36Sopenharmony_ci	encap_ipv4->ver_hlen = 4 << VXLAN_IPV4_HDR_VER_HLEN_VERSION_SFT;
90662306a36Sopenharmony_ci	encap_ipv4->ver_hlen |= 5 << VXLAN_IPV4_HDR_VER_HLEN_HEADER_LENGTH_SFT;
90762306a36Sopenharmony_ci	encap_ipv4->ttl = encap_key->ttl;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	encap_ipv4->dest_ip_addr = encap_key->u.ipv4.dst;
91062306a36Sopenharmony_ci	encap_ipv4->src_ip_addr = encap_key->u.ipv4.src;
91162306a36Sopenharmony_ci	encap_ipv4->protocol = IPPROTO_UDP;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	encap->dst_port = encap_key->tp_dst;
91462306a36Sopenharmony_ci	encap->vni = tunnel_id_to_key32(encap_key->tun_id);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	resp = hwrm_req_hold(bp, req);
91762306a36Sopenharmony_ci	rc = hwrm_req_send_silent(bp, req);
91862306a36Sopenharmony_ci	if (!rc)
91962306a36Sopenharmony_ci		*encap_record_handle = resp->encap_record_id;
92062306a36Sopenharmony_ci	hwrm_req_drop(bp, req);
92162306a36Sopenharmony_ciexit:
92262306a36Sopenharmony_ci	if (rc)
92362306a36Sopenharmony_ci		netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	return rc;
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic int hwrm_cfa_encap_record_free(struct bnxt *bp,
92962306a36Sopenharmony_ci				      __le32 encap_record_handle)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	struct hwrm_cfa_encap_record_free_input *req;
93262306a36Sopenharmony_ci	int rc;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	rc = hwrm_req_init(bp, req, HWRM_CFA_ENCAP_RECORD_FREE);
93562306a36Sopenharmony_ci	if (!rc) {
93662306a36Sopenharmony_ci		req->encap_record_id = encap_record_handle;
93762306a36Sopenharmony_ci		rc = hwrm_req_send(bp, req);
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci	if (rc)
94062306a36Sopenharmony_ci		netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	return rc;
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic int bnxt_tc_put_l2_node(struct bnxt *bp,
94662306a36Sopenharmony_ci			       struct bnxt_tc_flow_node *flow_node)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct bnxt_tc_l2_node *l2_node = flow_node->l2_node;
94962306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
95062306a36Sopenharmony_ci	int rc;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/* remove flow_node from the L2 shared flow list */
95362306a36Sopenharmony_ci	list_del(&flow_node->l2_list_node);
95462306a36Sopenharmony_ci	if (--l2_node->refcount == 0) {
95562306a36Sopenharmony_ci		rc =  rhashtable_remove_fast(&tc_info->l2_table, &l2_node->node,
95662306a36Sopenharmony_ci					     tc_info->l2_ht_params);
95762306a36Sopenharmony_ci		if (rc)
95862306a36Sopenharmony_ci			netdev_err(bp->dev,
95962306a36Sopenharmony_ci				   "Error: %s: rhashtable_remove_fast: %d\n",
96062306a36Sopenharmony_ci				   __func__, rc);
96162306a36Sopenharmony_ci		kfree_rcu(l2_node, rcu);
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci	return 0;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_cistatic struct bnxt_tc_l2_node *
96762306a36Sopenharmony_cibnxt_tc_get_l2_node(struct bnxt *bp, struct rhashtable *l2_table,
96862306a36Sopenharmony_ci		    struct rhashtable_params ht_params,
96962306a36Sopenharmony_ci		    struct bnxt_tc_l2_key *l2_key)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	struct bnxt_tc_l2_node *l2_node;
97262306a36Sopenharmony_ci	int rc;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	l2_node = rhashtable_lookup_fast(l2_table, l2_key, ht_params);
97562306a36Sopenharmony_ci	if (!l2_node) {
97662306a36Sopenharmony_ci		l2_node = kzalloc(sizeof(*l2_node), GFP_KERNEL);
97762306a36Sopenharmony_ci		if (!l2_node) {
97862306a36Sopenharmony_ci			rc = -ENOMEM;
97962306a36Sopenharmony_ci			return NULL;
98062306a36Sopenharmony_ci		}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		l2_node->key = *l2_key;
98362306a36Sopenharmony_ci		rc = rhashtable_insert_fast(l2_table, &l2_node->node,
98462306a36Sopenharmony_ci					    ht_params);
98562306a36Sopenharmony_ci		if (rc) {
98662306a36Sopenharmony_ci			kfree_rcu(l2_node, rcu);
98762306a36Sopenharmony_ci			netdev_err(bp->dev,
98862306a36Sopenharmony_ci				   "Error: %s: rhashtable_insert_fast: %d\n",
98962306a36Sopenharmony_ci				   __func__, rc);
99062306a36Sopenharmony_ci			return NULL;
99162306a36Sopenharmony_ci		}
99262306a36Sopenharmony_ci		INIT_LIST_HEAD(&l2_node->common_l2_flows);
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci	return l2_node;
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci/* Get the ref_flow_handle for a flow by checking if there are any other
99862306a36Sopenharmony_ci * flows that share the same L2 key as this flow.
99962306a36Sopenharmony_ci */
100062306a36Sopenharmony_cistatic int
100162306a36Sopenharmony_cibnxt_tc_get_ref_flow_handle(struct bnxt *bp, struct bnxt_tc_flow *flow,
100262306a36Sopenharmony_ci			    struct bnxt_tc_flow_node *flow_node,
100362306a36Sopenharmony_ci			    __le16 *ref_flow_handle)
100462306a36Sopenharmony_ci{
100562306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
100662306a36Sopenharmony_ci	struct bnxt_tc_flow_node *ref_flow_node;
100762306a36Sopenharmony_ci	struct bnxt_tc_l2_node *l2_node;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	l2_node = bnxt_tc_get_l2_node(bp, &tc_info->l2_table,
101062306a36Sopenharmony_ci				      tc_info->l2_ht_params,
101162306a36Sopenharmony_ci				      &flow->l2_key);
101262306a36Sopenharmony_ci	if (!l2_node)
101362306a36Sopenharmony_ci		return -1;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	/* If any other flow is using this l2_node, use it's flow_handle
101662306a36Sopenharmony_ci	 * as the ref_flow_handle
101762306a36Sopenharmony_ci	 */
101862306a36Sopenharmony_ci	if (l2_node->refcount > 0) {
101962306a36Sopenharmony_ci		ref_flow_node = list_first_entry(&l2_node->common_l2_flows,
102062306a36Sopenharmony_ci						 struct bnxt_tc_flow_node,
102162306a36Sopenharmony_ci						 l2_list_node);
102262306a36Sopenharmony_ci		*ref_flow_handle = ref_flow_node->flow_handle;
102362306a36Sopenharmony_ci	} else {
102462306a36Sopenharmony_ci		*ref_flow_handle = cpu_to_le16(0xffff);
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	/* Insert the l2_node into the flow_node so that subsequent flows
102862306a36Sopenharmony_ci	 * with a matching l2 key can use the flow_handle of this flow
102962306a36Sopenharmony_ci	 * as their ref_flow_handle
103062306a36Sopenharmony_ci	 */
103162306a36Sopenharmony_ci	flow_node->l2_node = l2_node;
103262306a36Sopenharmony_ci	list_add(&flow_node->l2_list_node, &l2_node->common_l2_flows);
103362306a36Sopenharmony_ci	l2_node->refcount++;
103462306a36Sopenharmony_ci	return 0;
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci/* After the flow parsing is done, this routine is used for checking
103862306a36Sopenharmony_ci * if there are any aspects of the flow that prevent it from being
103962306a36Sopenharmony_ci * offloaded.
104062306a36Sopenharmony_ci */
104162306a36Sopenharmony_cistatic bool bnxt_tc_can_offload(struct bnxt *bp, struct bnxt_tc_flow *flow)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	/* If L4 ports are specified then ip_proto must be TCP or UDP */
104462306a36Sopenharmony_ci	if ((flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) &&
104562306a36Sopenharmony_ci	    (flow->l4_key.ip_proto != IPPROTO_TCP &&
104662306a36Sopenharmony_ci	     flow->l4_key.ip_proto != IPPROTO_UDP)) {
104762306a36Sopenharmony_ci		netdev_info(bp->dev, "Cannot offload non-TCP/UDP (%d) ports\n",
104862306a36Sopenharmony_ci			    flow->l4_key.ip_proto);
104962306a36Sopenharmony_ci		return false;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/* Currently source/dest MAC cannot be partial wildcard  */
105362306a36Sopenharmony_ci	if (bits_set(&flow->l2_key.smac, sizeof(flow->l2_key.smac)) &&
105462306a36Sopenharmony_ci	    !is_exactmatch(flow->l2_mask.smac, sizeof(flow->l2_mask.smac))) {
105562306a36Sopenharmony_ci		netdev_info(bp->dev, "Wildcard match unsupported for Source MAC\n");
105662306a36Sopenharmony_ci		return false;
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci	if (bits_set(&flow->l2_key.dmac, sizeof(flow->l2_key.dmac)) &&
105962306a36Sopenharmony_ci	    !is_exactmatch(&flow->l2_mask.dmac, sizeof(flow->l2_mask.dmac))) {
106062306a36Sopenharmony_ci		netdev_info(bp->dev, "Wildcard match unsupported for Dest MAC\n");
106162306a36Sopenharmony_ci		return false;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	/* Currently VLAN fields cannot be partial wildcard */
106562306a36Sopenharmony_ci	if (bits_set(&flow->l2_key.inner_vlan_tci,
106662306a36Sopenharmony_ci		     sizeof(flow->l2_key.inner_vlan_tci)) &&
106762306a36Sopenharmony_ci	    !is_vlan_tci_allowed(flow->l2_mask.inner_vlan_tci,
106862306a36Sopenharmony_ci				 flow->l2_key.inner_vlan_tci)) {
106962306a36Sopenharmony_ci		netdev_info(bp->dev, "Unsupported VLAN TCI\n");
107062306a36Sopenharmony_ci		return false;
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci	if (bits_set(&flow->l2_key.inner_vlan_tpid,
107362306a36Sopenharmony_ci		     sizeof(flow->l2_key.inner_vlan_tpid)) &&
107462306a36Sopenharmony_ci	    !is_exactmatch(&flow->l2_mask.inner_vlan_tpid,
107562306a36Sopenharmony_ci			   sizeof(flow->l2_mask.inner_vlan_tpid))) {
107662306a36Sopenharmony_ci		netdev_info(bp->dev, "Wildcard match unsupported for VLAN TPID\n");
107762306a36Sopenharmony_ci		return false;
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/* Currently Ethertype must be set */
108162306a36Sopenharmony_ci	if (!is_exactmatch(&flow->l2_mask.ether_type,
108262306a36Sopenharmony_ci			   sizeof(flow->l2_mask.ether_type))) {
108362306a36Sopenharmony_ci		netdev_info(bp->dev, "Wildcard match unsupported for Ethertype\n");
108462306a36Sopenharmony_ci		return false;
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	return true;
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci/* Returns the final refcount of the node on success
109162306a36Sopenharmony_ci * or a -ve error code on failure
109262306a36Sopenharmony_ci */
109362306a36Sopenharmony_cistatic int bnxt_tc_put_tunnel_node(struct bnxt *bp,
109462306a36Sopenharmony_ci				   struct rhashtable *tunnel_table,
109562306a36Sopenharmony_ci				   struct rhashtable_params *ht_params,
109662306a36Sopenharmony_ci				   struct bnxt_tc_tunnel_node *tunnel_node)
109762306a36Sopenharmony_ci{
109862306a36Sopenharmony_ci	int rc;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	if (--tunnel_node->refcount == 0) {
110162306a36Sopenharmony_ci		rc =  rhashtable_remove_fast(tunnel_table, &tunnel_node->node,
110262306a36Sopenharmony_ci					     *ht_params);
110362306a36Sopenharmony_ci		if (rc) {
110462306a36Sopenharmony_ci			netdev_err(bp->dev, "rhashtable_remove_fast rc=%d\n", rc);
110562306a36Sopenharmony_ci			rc = -1;
110662306a36Sopenharmony_ci		}
110762306a36Sopenharmony_ci		kfree_rcu(tunnel_node, rcu);
110862306a36Sopenharmony_ci		return rc;
110962306a36Sopenharmony_ci	} else {
111062306a36Sopenharmony_ci		return tunnel_node->refcount;
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci/* Get (or add) either encap or decap tunnel node from/to the supplied
111562306a36Sopenharmony_ci * hash table.
111662306a36Sopenharmony_ci */
111762306a36Sopenharmony_cistatic struct bnxt_tc_tunnel_node *
111862306a36Sopenharmony_cibnxt_tc_get_tunnel_node(struct bnxt *bp, struct rhashtable *tunnel_table,
111962306a36Sopenharmony_ci			struct rhashtable_params *ht_params,
112062306a36Sopenharmony_ci			struct ip_tunnel_key *tun_key)
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	struct bnxt_tc_tunnel_node *tunnel_node;
112362306a36Sopenharmony_ci	int rc;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	tunnel_node = rhashtable_lookup_fast(tunnel_table, tun_key, *ht_params);
112662306a36Sopenharmony_ci	if (!tunnel_node) {
112762306a36Sopenharmony_ci		tunnel_node = kzalloc(sizeof(*tunnel_node), GFP_KERNEL);
112862306a36Sopenharmony_ci		if (!tunnel_node) {
112962306a36Sopenharmony_ci			rc = -ENOMEM;
113062306a36Sopenharmony_ci			goto err;
113162306a36Sopenharmony_ci		}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		tunnel_node->key = *tun_key;
113462306a36Sopenharmony_ci		tunnel_node->tunnel_handle = INVALID_TUNNEL_HANDLE;
113562306a36Sopenharmony_ci		rc = rhashtable_insert_fast(tunnel_table, &tunnel_node->node,
113662306a36Sopenharmony_ci					    *ht_params);
113762306a36Sopenharmony_ci		if (rc) {
113862306a36Sopenharmony_ci			kfree_rcu(tunnel_node, rcu);
113962306a36Sopenharmony_ci			goto err;
114062306a36Sopenharmony_ci		}
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci	tunnel_node->refcount++;
114362306a36Sopenharmony_ci	return tunnel_node;
114462306a36Sopenharmony_cierr:
114562306a36Sopenharmony_ci	netdev_info(bp->dev, "error rc=%d\n", rc);
114662306a36Sopenharmony_ci	return NULL;
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic int bnxt_tc_get_ref_decap_handle(struct bnxt *bp,
115062306a36Sopenharmony_ci					struct bnxt_tc_flow *flow,
115162306a36Sopenharmony_ci					struct bnxt_tc_l2_key *l2_key,
115262306a36Sopenharmony_ci					struct bnxt_tc_flow_node *flow_node,
115362306a36Sopenharmony_ci					__le32 *ref_decap_handle)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
115662306a36Sopenharmony_ci	struct bnxt_tc_flow_node *ref_flow_node;
115762306a36Sopenharmony_ci	struct bnxt_tc_l2_node *decap_l2_node;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	decap_l2_node = bnxt_tc_get_l2_node(bp, &tc_info->decap_l2_table,
116062306a36Sopenharmony_ci					    tc_info->decap_l2_ht_params,
116162306a36Sopenharmony_ci					    l2_key);
116262306a36Sopenharmony_ci	if (!decap_l2_node)
116362306a36Sopenharmony_ci		return -1;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	/* If any other flow is using this decap_l2_node, use it's decap_handle
116662306a36Sopenharmony_ci	 * as the ref_decap_handle
116762306a36Sopenharmony_ci	 */
116862306a36Sopenharmony_ci	if (decap_l2_node->refcount > 0) {
116962306a36Sopenharmony_ci		ref_flow_node =
117062306a36Sopenharmony_ci			list_first_entry(&decap_l2_node->common_l2_flows,
117162306a36Sopenharmony_ci					 struct bnxt_tc_flow_node,
117262306a36Sopenharmony_ci					 decap_l2_list_node);
117362306a36Sopenharmony_ci		*ref_decap_handle = ref_flow_node->decap_node->tunnel_handle;
117462306a36Sopenharmony_ci	} else {
117562306a36Sopenharmony_ci		*ref_decap_handle = INVALID_TUNNEL_HANDLE;
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	/* Insert the l2_node into the flow_node so that subsequent flows
117962306a36Sopenharmony_ci	 * with a matching decap l2 key can use the decap_filter_handle of
118062306a36Sopenharmony_ci	 * this flow as their ref_decap_handle
118162306a36Sopenharmony_ci	 */
118262306a36Sopenharmony_ci	flow_node->decap_l2_node = decap_l2_node;
118362306a36Sopenharmony_ci	list_add(&flow_node->decap_l2_list_node,
118462306a36Sopenharmony_ci		 &decap_l2_node->common_l2_flows);
118562306a36Sopenharmony_ci	decap_l2_node->refcount++;
118662306a36Sopenharmony_ci	return 0;
118762306a36Sopenharmony_ci}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_cistatic void bnxt_tc_put_decap_l2_node(struct bnxt *bp,
119062306a36Sopenharmony_ci				      struct bnxt_tc_flow_node *flow_node)
119162306a36Sopenharmony_ci{
119262306a36Sopenharmony_ci	struct bnxt_tc_l2_node *decap_l2_node = flow_node->decap_l2_node;
119362306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
119462306a36Sopenharmony_ci	int rc;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	/* remove flow_node from the decap L2 sharing flow list */
119762306a36Sopenharmony_ci	list_del(&flow_node->decap_l2_list_node);
119862306a36Sopenharmony_ci	if (--decap_l2_node->refcount == 0) {
119962306a36Sopenharmony_ci		rc =  rhashtable_remove_fast(&tc_info->decap_l2_table,
120062306a36Sopenharmony_ci					     &decap_l2_node->node,
120162306a36Sopenharmony_ci					     tc_info->decap_l2_ht_params);
120262306a36Sopenharmony_ci		if (rc)
120362306a36Sopenharmony_ci			netdev_err(bp->dev, "rhashtable_remove_fast rc=%d\n", rc);
120462306a36Sopenharmony_ci		kfree_rcu(decap_l2_node, rcu);
120562306a36Sopenharmony_ci	}
120662306a36Sopenharmony_ci}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_cistatic void bnxt_tc_put_decap_handle(struct bnxt *bp,
120962306a36Sopenharmony_ci				     struct bnxt_tc_flow_node *flow_node)
121062306a36Sopenharmony_ci{
121162306a36Sopenharmony_ci	__le32 decap_handle = flow_node->decap_node->tunnel_handle;
121262306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
121362306a36Sopenharmony_ci	int rc;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	if (flow_node->decap_l2_node)
121662306a36Sopenharmony_ci		bnxt_tc_put_decap_l2_node(bp, flow_node);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	rc = bnxt_tc_put_tunnel_node(bp, &tc_info->decap_table,
121962306a36Sopenharmony_ci				     &tc_info->decap_ht_params,
122062306a36Sopenharmony_ci				     flow_node->decap_node);
122162306a36Sopenharmony_ci	if (!rc && decap_handle != INVALID_TUNNEL_HANDLE)
122262306a36Sopenharmony_ci		hwrm_cfa_decap_filter_free(bp, decap_handle);
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cistatic int bnxt_tc_resolve_tunnel_hdrs(struct bnxt *bp,
122662306a36Sopenharmony_ci				       struct ip_tunnel_key *tun_key,
122762306a36Sopenharmony_ci				       struct bnxt_tc_l2_key *l2_info)
122862306a36Sopenharmony_ci{
122962306a36Sopenharmony_ci#ifdef CONFIG_INET
123062306a36Sopenharmony_ci	struct net_device *real_dst_dev = bp->dev;
123162306a36Sopenharmony_ci	struct flowi4 flow = { {0} };
123262306a36Sopenharmony_ci	struct net_device *dst_dev;
123362306a36Sopenharmony_ci	struct neighbour *nbr;
123462306a36Sopenharmony_ci	struct rtable *rt;
123562306a36Sopenharmony_ci	int rc;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	flow.flowi4_proto = IPPROTO_UDP;
123862306a36Sopenharmony_ci	flow.fl4_dport = tun_key->tp_dst;
123962306a36Sopenharmony_ci	flow.daddr = tun_key->u.ipv4.dst;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	rt = ip_route_output_key(dev_net(real_dst_dev), &flow);
124262306a36Sopenharmony_ci	if (IS_ERR(rt)) {
124362306a36Sopenharmony_ci		netdev_info(bp->dev, "no route to %pI4b\n", &flow.daddr);
124462306a36Sopenharmony_ci		return -EOPNOTSUPP;
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	/* The route must either point to the real_dst_dev or a dst_dev that
124862306a36Sopenharmony_ci	 * uses the real_dst_dev.
124962306a36Sopenharmony_ci	 */
125062306a36Sopenharmony_ci	dst_dev = rt->dst.dev;
125162306a36Sopenharmony_ci	if (is_vlan_dev(dst_dev)) {
125262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_VLAN_8021Q)
125362306a36Sopenharmony_ci		struct vlan_dev_priv *vlan = vlan_dev_priv(dst_dev);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci		if (vlan->real_dev != real_dst_dev) {
125662306a36Sopenharmony_ci			netdev_info(bp->dev,
125762306a36Sopenharmony_ci				    "dst_dev(%s) doesn't use PF-if(%s)\n",
125862306a36Sopenharmony_ci				    netdev_name(dst_dev),
125962306a36Sopenharmony_ci				    netdev_name(real_dst_dev));
126062306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
126162306a36Sopenharmony_ci			goto put_rt;
126262306a36Sopenharmony_ci		}
126362306a36Sopenharmony_ci		l2_info->inner_vlan_tci = htons(vlan->vlan_id);
126462306a36Sopenharmony_ci		l2_info->inner_vlan_tpid = vlan->vlan_proto;
126562306a36Sopenharmony_ci		l2_info->num_vlans = 1;
126662306a36Sopenharmony_ci#endif
126762306a36Sopenharmony_ci	} else if (dst_dev != real_dst_dev) {
126862306a36Sopenharmony_ci		netdev_info(bp->dev,
126962306a36Sopenharmony_ci			    "dst_dev(%s) for %pI4b is not PF-if(%s)\n",
127062306a36Sopenharmony_ci			    netdev_name(dst_dev), &flow.daddr,
127162306a36Sopenharmony_ci			    netdev_name(real_dst_dev));
127262306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
127362306a36Sopenharmony_ci		goto put_rt;
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	nbr = dst_neigh_lookup(&rt->dst, &flow.daddr);
127762306a36Sopenharmony_ci	if (!nbr) {
127862306a36Sopenharmony_ci		netdev_info(bp->dev, "can't lookup neighbor for %pI4b\n",
127962306a36Sopenharmony_ci			    &flow.daddr);
128062306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
128162306a36Sopenharmony_ci		goto put_rt;
128262306a36Sopenharmony_ci	}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	tun_key->u.ipv4.src = flow.saddr;
128562306a36Sopenharmony_ci	tun_key->ttl = ip4_dst_hoplimit(&rt->dst);
128662306a36Sopenharmony_ci	neigh_ha_snapshot(l2_info->dmac, nbr, dst_dev);
128762306a36Sopenharmony_ci	ether_addr_copy(l2_info->smac, dst_dev->dev_addr);
128862306a36Sopenharmony_ci	neigh_release(nbr);
128962306a36Sopenharmony_ci	ip_rt_put(rt);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	return 0;
129262306a36Sopenharmony_ciput_rt:
129362306a36Sopenharmony_ci	ip_rt_put(rt);
129462306a36Sopenharmony_ci	return rc;
129562306a36Sopenharmony_ci#else
129662306a36Sopenharmony_ci	return -EOPNOTSUPP;
129762306a36Sopenharmony_ci#endif
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_cistatic int bnxt_tc_get_decap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow,
130162306a36Sopenharmony_ci				    struct bnxt_tc_flow_node *flow_node,
130262306a36Sopenharmony_ci				    __le32 *decap_filter_handle)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	struct ip_tunnel_key *decap_key = &flow->tun_key;
130562306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
130662306a36Sopenharmony_ci	struct bnxt_tc_l2_key l2_info = { {0} };
130762306a36Sopenharmony_ci	struct bnxt_tc_tunnel_node *decap_node;
130862306a36Sopenharmony_ci	struct ip_tunnel_key tun_key = { 0 };
130962306a36Sopenharmony_ci	struct bnxt_tc_l2_key *decap_l2_info;
131062306a36Sopenharmony_ci	__le32 ref_decap_handle;
131162306a36Sopenharmony_ci	int rc;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	/* Check if there's another flow using the same tunnel decap.
131462306a36Sopenharmony_ci	 * If not, add this tunnel to the table and resolve the other
131562306a36Sopenharmony_ci	 * tunnel header fileds. Ignore src_port in the tunnel_key,
131662306a36Sopenharmony_ci	 * since it is not required for decap filters.
131762306a36Sopenharmony_ci	 */
131862306a36Sopenharmony_ci	decap_key->tp_src = 0;
131962306a36Sopenharmony_ci	decap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->decap_table,
132062306a36Sopenharmony_ci					     &tc_info->decap_ht_params,
132162306a36Sopenharmony_ci					     decap_key);
132262306a36Sopenharmony_ci	if (!decap_node)
132362306a36Sopenharmony_ci		return -ENOMEM;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	flow_node->decap_node = decap_node;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	if (decap_node->tunnel_handle != INVALID_TUNNEL_HANDLE)
132862306a36Sopenharmony_ci		goto done;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/* Resolve the L2 fields for tunnel decap
133162306a36Sopenharmony_ci	 * Resolve the route for remote vtep (saddr) of the decap key
133262306a36Sopenharmony_ci	 * Find it's next-hop mac addrs
133362306a36Sopenharmony_ci	 */
133462306a36Sopenharmony_ci	tun_key.u.ipv4.dst = flow->tun_key.u.ipv4.src;
133562306a36Sopenharmony_ci	tun_key.tp_dst = flow->tun_key.tp_dst;
133662306a36Sopenharmony_ci	rc = bnxt_tc_resolve_tunnel_hdrs(bp, &tun_key, &l2_info);
133762306a36Sopenharmony_ci	if (rc)
133862306a36Sopenharmony_ci		goto put_decap;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	decap_l2_info = &decap_node->l2_info;
134162306a36Sopenharmony_ci	/* decap smac is wildcarded */
134262306a36Sopenharmony_ci	ether_addr_copy(decap_l2_info->dmac, l2_info.smac);
134362306a36Sopenharmony_ci	if (l2_info.num_vlans) {
134462306a36Sopenharmony_ci		decap_l2_info->num_vlans = l2_info.num_vlans;
134562306a36Sopenharmony_ci		decap_l2_info->inner_vlan_tpid = l2_info.inner_vlan_tpid;
134662306a36Sopenharmony_ci		decap_l2_info->inner_vlan_tci = l2_info.inner_vlan_tci;
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci	flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	/* For getting a decap_filter_handle we first need to check if
135162306a36Sopenharmony_ci	 * there are any other decap flows that share the same tunnel L2
135262306a36Sopenharmony_ci	 * key and if so, pass that flow's decap_filter_handle as the
135362306a36Sopenharmony_ci	 * ref_decap_handle for this flow.
135462306a36Sopenharmony_ci	 */
135562306a36Sopenharmony_ci	rc = bnxt_tc_get_ref_decap_handle(bp, flow, decap_l2_info, flow_node,
135662306a36Sopenharmony_ci					  &ref_decap_handle);
135762306a36Sopenharmony_ci	if (rc)
135862306a36Sopenharmony_ci		goto put_decap;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	/* Issue the hwrm cmd to allocate a decap filter handle */
136162306a36Sopenharmony_ci	rc = hwrm_cfa_decap_filter_alloc(bp, flow, decap_l2_info,
136262306a36Sopenharmony_ci					 ref_decap_handle,
136362306a36Sopenharmony_ci					 &decap_node->tunnel_handle);
136462306a36Sopenharmony_ci	if (rc)
136562306a36Sopenharmony_ci		goto put_decap_l2;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cidone:
136862306a36Sopenharmony_ci	*decap_filter_handle = decap_node->tunnel_handle;
136962306a36Sopenharmony_ci	return 0;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ciput_decap_l2:
137262306a36Sopenharmony_ci	bnxt_tc_put_decap_l2_node(bp, flow_node);
137362306a36Sopenharmony_ciput_decap:
137462306a36Sopenharmony_ci	bnxt_tc_put_tunnel_node(bp, &tc_info->decap_table,
137562306a36Sopenharmony_ci				&tc_info->decap_ht_params,
137662306a36Sopenharmony_ci				flow_node->decap_node);
137762306a36Sopenharmony_ci	return rc;
137862306a36Sopenharmony_ci}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_cistatic void bnxt_tc_put_encap_handle(struct bnxt *bp,
138162306a36Sopenharmony_ci				     struct bnxt_tc_tunnel_node *encap_node)
138262306a36Sopenharmony_ci{
138362306a36Sopenharmony_ci	__le32 encap_handle = encap_node->tunnel_handle;
138462306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
138562306a36Sopenharmony_ci	int rc;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	rc = bnxt_tc_put_tunnel_node(bp, &tc_info->encap_table,
138862306a36Sopenharmony_ci				     &tc_info->encap_ht_params, encap_node);
138962306a36Sopenharmony_ci	if (!rc && encap_handle != INVALID_TUNNEL_HANDLE)
139062306a36Sopenharmony_ci		hwrm_cfa_encap_record_free(bp, encap_handle);
139162306a36Sopenharmony_ci}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci/* Lookup the tunnel encap table and check if there's an encap_handle
139462306a36Sopenharmony_ci * alloc'd already.
139562306a36Sopenharmony_ci * If not, query L2 info via a route lookup and issue an encap_record_alloc
139662306a36Sopenharmony_ci * cmd to FW.
139762306a36Sopenharmony_ci */
139862306a36Sopenharmony_cistatic int bnxt_tc_get_encap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow,
139962306a36Sopenharmony_ci				    struct bnxt_tc_flow_node *flow_node,
140062306a36Sopenharmony_ci				    __le32 *encap_handle)
140162306a36Sopenharmony_ci{
140262306a36Sopenharmony_ci	struct ip_tunnel_key *encap_key = &flow->actions.tun_encap_key;
140362306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
140462306a36Sopenharmony_ci	struct bnxt_tc_tunnel_node *encap_node;
140562306a36Sopenharmony_ci	int rc;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	/* Check if there's another flow using the same tunnel encap.
140862306a36Sopenharmony_ci	 * If not, add this tunnel to the table and resolve the other
140962306a36Sopenharmony_ci	 * tunnel header fileds
141062306a36Sopenharmony_ci	 */
141162306a36Sopenharmony_ci	encap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->encap_table,
141262306a36Sopenharmony_ci					     &tc_info->encap_ht_params,
141362306a36Sopenharmony_ci					     encap_key);
141462306a36Sopenharmony_ci	if (!encap_node)
141562306a36Sopenharmony_ci		return -ENOMEM;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	flow_node->encap_node = encap_node;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	if (encap_node->tunnel_handle != INVALID_TUNNEL_HANDLE)
142062306a36Sopenharmony_ci		goto done;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	rc = bnxt_tc_resolve_tunnel_hdrs(bp, encap_key, &encap_node->l2_info);
142362306a36Sopenharmony_ci	if (rc)
142462306a36Sopenharmony_ci		goto put_encap;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	/* Allocate a new tunnel encap record */
142762306a36Sopenharmony_ci	rc = hwrm_cfa_encap_record_alloc(bp, encap_key, &encap_node->l2_info,
142862306a36Sopenharmony_ci					 &encap_node->tunnel_handle);
142962306a36Sopenharmony_ci	if (rc)
143062306a36Sopenharmony_ci		goto put_encap;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_cidone:
143362306a36Sopenharmony_ci	*encap_handle = encap_node->tunnel_handle;
143462306a36Sopenharmony_ci	return 0;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ciput_encap:
143762306a36Sopenharmony_ci	bnxt_tc_put_tunnel_node(bp, &tc_info->encap_table,
143862306a36Sopenharmony_ci				&tc_info->encap_ht_params, encap_node);
143962306a36Sopenharmony_ci	return rc;
144062306a36Sopenharmony_ci}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_cistatic void bnxt_tc_put_tunnel_handle(struct bnxt *bp,
144362306a36Sopenharmony_ci				      struct bnxt_tc_flow *flow,
144462306a36Sopenharmony_ci				      struct bnxt_tc_flow_node *flow_node)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP)
144762306a36Sopenharmony_ci		bnxt_tc_put_decap_handle(bp, flow_node);
144862306a36Sopenharmony_ci	else if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP)
144962306a36Sopenharmony_ci		bnxt_tc_put_encap_handle(bp, flow_node->encap_node);
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_cistatic int bnxt_tc_get_tunnel_handle(struct bnxt *bp,
145362306a36Sopenharmony_ci				     struct bnxt_tc_flow *flow,
145462306a36Sopenharmony_ci				     struct bnxt_tc_flow_node *flow_node,
145562306a36Sopenharmony_ci				     __le32 *tunnel_handle)
145662306a36Sopenharmony_ci{
145762306a36Sopenharmony_ci	if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP)
145862306a36Sopenharmony_ci		return bnxt_tc_get_decap_handle(bp, flow, flow_node,
145962306a36Sopenharmony_ci						tunnel_handle);
146062306a36Sopenharmony_ci	else if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP)
146162306a36Sopenharmony_ci		return bnxt_tc_get_encap_handle(bp, flow, flow_node,
146262306a36Sopenharmony_ci						tunnel_handle);
146362306a36Sopenharmony_ci	else
146462306a36Sopenharmony_ci		return 0;
146562306a36Sopenharmony_ci}
146662306a36Sopenharmony_cistatic int __bnxt_tc_del_flow(struct bnxt *bp,
146762306a36Sopenharmony_ci			      struct bnxt_tc_flow_node *flow_node)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
147062306a36Sopenharmony_ci	int rc;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	/* send HWRM cmd to free the flow-id */
147362306a36Sopenharmony_ci	bnxt_hwrm_cfa_flow_free(bp, flow_node);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	mutex_lock(&tc_info->lock);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	/* release references to any tunnel encap/decap nodes */
147862306a36Sopenharmony_ci	bnxt_tc_put_tunnel_handle(bp, &flow_node->flow, flow_node);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	/* release reference to l2 node */
148162306a36Sopenharmony_ci	bnxt_tc_put_l2_node(bp, flow_node);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	mutex_unlock(&tc_info->lock);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	rc = rhashtable_remove_fast(&tc_info->flow_table, &flow_node->node,
148662306a36Sopenharmony_ci				    tc_info->flow_ht_params);
148762306a36Sopenharmony_ci	if (rc)
148862306a36Sopenharmony_ci		netdev_err(bp->dev, "Error: %s: rhashtable_remove_fast rc=%d\n",
148962306a36Sopenharmony_ci			   __func__, rc);
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	kfree_rcu(flow_node, rcu);
149262306a36Sopenharmony_ci	return 0;
149362306a36Sopenharmony_ci}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_cistatic void bnxt_tc_set_flow_dir(struct bnxt *bp, struct bnxt_tc_flow *flow,
149662306a36Sopenharmony_ci				 u16 src_fid)
149762306a36Sopenharmony_ci{
149862306a36Sopenharmony_ci	flow->l2_key.dir = (bp->pf.fw_fid == src_fid) ? BNXT_DIR_RX : BNXT_DIR_TX;
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cistatic void bnxt_tc_set_src_fid(struct bnxt *bp, struct bnxt_tc_flow *flow,
150262306a36Sopenharmony_ci				u16 src_fid)
150362306a36Sopenharmony_ci{
150462306a36Sopenharmony_ci	if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP)
150562306a36Sopenharmony_ci		flow->src_fid = bp->pf.fw_fid;
150662306a36Sopenharmony_ci	else
150762306a36Sopenharmony_ci		flow->src_fid = src_fid;
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci/* Add a new flow or replace an existing flow.
151162306a36Sopenharmony_ci * Notes on locking:
151262306a36Sopenharmony_ci * There are essentially two critical sections here.
151362306a36Sopenharmony_ci * 1. while adding a new flow
151462306a36Sopenharmony_ci *    a) lookup l2-key
151562306a36Sopenharmony_ci *    b) issue HWRM cmd and get flow_handle
151662306a36Sopenharmony_ci *    c) link l2-key with flow
151762306a36Sopenharmony_ci * 2. while deleting a flow
151862306a36Sopenharmony_ci *    a) unlinking l2-key from flow
151962306a36Sopenharmony_ci * A lock is needed to protect these two critical sections.
152062306a36Sopenharmony_ci *
152162306a36Sopenharmony_ci * The hash-tables are already protected by the rhashtable API.
152262306a36Sopenharmony_ci */
152362306a36Sopenharmony_cistatic int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid,
152462306a36Sopenharmony_ci			    struct flow_cls_offload *tc_flow_cmd)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	struct bnxt_tc_flow_node *new_node, *old_node;
152762306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
152862306a36Sopenharmony_ci	struct bnxt_tc_flow *flow;
152962306a36Sopenharmony_ci	__le32 tunnel_handle = 0;
153062306a36Sopenharmony_ci	__le16 ref_flow_handle;
153162306a36Sopenharmony_ci	int rc;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	/* allocate memory for the new flow and it's node */
153462306a36Sopenharmony_ci	new_node = kzalloc(sizeof(*new_node), GFP_KERNEL);
153562306a36Sopenharmony_ci	if (!new_node) {
153662306a36Sopenharmony_ci		rc = -ENOMEM;
153762306a36Sopenharmony_ci		goto done;
153862306a36Sopenharmony_ci	}
153962306a36Sopenharmony_ci	new_node->cookie = tc_flow_cmd->cookie;
154062306a36Sopenharmony_ci	flow = &new_node->flow;
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	rc = bnxt_tc_parse_flow(bp, tc_flow_cmd, flow);
154362306a36Sopenharmony_ci	if (rc)
154462306a36Sopenharmony_ci		goto free_node;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	bnxt_tc_set_src_fid(bp, flow, src_fid);
154762306a36Sopenharmony_ci	bnxt_tc_set_flow_dir(bp, flow, flow->src_fid);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	if (!bnxt_tc_can_offload(bp, flow)) {
155062306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
155162306a36Sopenharmony_ci		kfree_rcu(new_node, rcu);
155262306a36Sopenharmony_ci		return rc;
155362306a36Sopenharmony_ci	}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	/* If a flow exists with the same cookie, delete it */
155662306a36Sopenharmony_ci	old_node = rhashtable_lookup_fast(&tc_info->flow_table,
155762306a36Sopenharmony_ci					  &tc_flow_cmd->cookie,
155862306a36Sopenharmony_ci					  tc_info->flow_ht_params);
155962306a36Sopenharmony_ci	if (old_node)
156062306a36Sopenharmony_ci		__bnxt_tc_del_flow(bp, old_node);
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	/* Check if the L2 part of the flow has been offloaded already.
156362306a36Sopenharmony_ci	 * If so, bump up it's refcnt and get it's reference handle.
156462306a36Sopenharmony_ci	 */
156562306a36Sopenharmony_ci	mutex_lock(&tc_info->lock);
156662306a36Sopenharmony_ci	rc = bnxt_tc_get_ref_flow_handle(bp, flow, new_node, &ref_flow_handle);
156762306a36Sopenharmony_ci	if (rc)
156862306a36Sopenharmony_ci		goto unlock;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	/* If the flow involves tunnel encap/decap, get tunnel_handle */
157162306a36Sopenharmony_ci	rc = bnxt_tc_get_tunnel_handle(bp, flow, new_node, &tunnel_handle);
157262306a36Sopenharmony_ci	if (rc)
157362306a36Sopenharmony_ci		goto put_l2;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	/* send HWRM cmd to alloc the flow */
157662306a36Sopenharmony_ci	rc = bnxt_hwrm_cfa_flow_alloc(bp, flow, ref_flow_handle,
157762306a36Sopenharmony_ci				      tunnel_handle, new_node);
157862306a36Sopenharmony_ci	if (rc)
157962306a36Sopenharmony_ci		goto put_tunnel;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	flow->lastused = jiffies;
158262306a36Sopenharmony_ci	spin_lock_init(&flow->stats_lock);
158362306a36Sopenharmony_ci	/* add new flow to flow-table */
158462306a36Sopenharmony_ci	rc = rhashtable_insert_fast(&tc_info->flow_table, &new_node->node,
158562306a36Sopenharmony_ci				    tc_info->flow_ht_params);
158662306a36Sopenharmony_ci	if (rc)
158762306a36Sopenharmony_ci		goto hwrm_flow_free;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	mutex_unlock(&tc_info->lock);
159062306a36Sopenharmony_ci	return 0;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_cihwrm_flow_free:
159362306a36Sopenharmony_ci	bnxt_hwrm_cfa_flow_free(bp, new_node);
159462306a36Sopenharmony_ciput_tunnel:
159562306a36Sopenharmony_ci	bnxt_tc_put_tunnel_handle(bp, flow, new_node);
159662306a36Sopenharmony_ciput_l2:
159762306a36Sopenharmony_ci	bnxt_tc_put_l2_node(bp, new_node);
159862306a36Sopenharmony_ciunlock:
159962306a36Sopenharmony_ci	mutex_unlock(&tc_info->lock);
160062306a36Sopenharmony_cifree_node:
160162306a36Sopenharmony_ci	kfree_rcu(new_node, rcu);
160262306a36Sopenharmony_cidone:
160362306a36Sopenharmony_ci	netdev_err(bp->dev, "Error: %s: cookie=0x%lx error=%d\n",
160462306a36Sopenharmony_ci		   __func__, tc_flow_cmd->cookie, rc);
160562306a36Sopenharmony_ci	return rc;
160662306a36Sopenharmony_ci}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic int bnxt_tc_del_flow(struct bnxt *bp,
160962306a36Sopenharmony_ci			    struct flow_cls_offload *tc_flow_cmd)
161062306a36Sopenharmony_ci{
161162306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
161262306a36Sopenharmony_ci	struct bnxt_tc_flow_node *flow_node;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	flow_node = rhashtable_lookup_fast(&tc_info->flow_table,
161562306a36Sopenharmony_ci					   &tc_flow_cmd->cookie,
161662306a36Sopenharmony_ci					   tc_info->flow_ht_params);
161762306a36Sopenharmony_ci	if (!flow_node)
161862306a36Sopenharmony_ci		return -EINVAL;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	return __bnxt_tc_del_flow(bp, flow_node);
162162306a36Sopenharmony_ci}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_cistatic int bnxt_tc_get_flow_stats(struct bnxt *bp,
162462306a36Sopenharmony_ci				  struct flow_cls_offload *tc_flow_cmd)
162562306a36Sopenharmony_ci{
162662306a36Sopenharmony_ci	struct bnxt_tc_flow_stats stats, *curr_stats, *prev_stats;
162762306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
162862306a36Sopenharmony_ci	struct bnxt_tc_flow_node *flow_node;
162962306a36Sopenharmony_ci	struct bnxt_tc_flow *flow;
163062306a36Sopenharmony_ci	unsigned long lastused;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	flow_node = rhashtable_lookup_fast(&tc_info->flow_table,
163362306a36Sopenharmony_ci					   &tc_flow_cmd->cookie,
163462306a36Sopenharmony_ci					   tc_info->flow_ht_params);
163562306a36Sopenharmony_ci	if (!flow_node)
163662306a36Sopenharmony_ci		return -1;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	flow = &flow_node->flow;
163962306a36Sopenharmony_ci	curr_stats = &flow->stats;
164062306a36Sopenharmony_ci	prev_stats = &flow->prev_stats;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	spin_lock(&flow->stats_lock);
164362306a36Sopenharmony_ci	stats.packets = curr_stats->packets - prev_stats->packets;
164462306a36Sopenharmony_ci	stats.bytes = curr_stats->bytes - prev_stats->bytes;
164562306a36Sopenharmony_ci	*prev_stats = *curr_stats;
164662306a36Sopenharmony_ci	lastused = flow->lastused;
164762306a36Sopenharmony_ci	spin_unlock(&flow->stats_lock);
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	flow_stats_update(&tc_flow_cmd->stats, stats.bytes, stats.packets, 0,
165062306a36Sopenharmony_ci			  lastused, FLOW_ACTION_HW_STATS_DELAYED);
165162306a36Sopenharmony_ci	return 0;
165262306a36Sopenharmony_ci}
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_cistatic void bnxt_fill_cfa_stats_req(struct bnxt *bp,
165562306a36Sopenharmony_ci				    struct bnxt_tc_flow_node *flow_node,
165662306a36Sopenharmony_ci				    __le16 *flow_handle, __le32 *flow_id)
165762306a36Sopenharmony_ci{
165862306a36Sopenharmony_ci	u16 handle;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) {
166162306a36Sopenharmony_ci		*flow_id = flow_node->flow_id;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci		/* If flow_id is used to fetch flow stats then:
166462306a36Sopenharmony_ci		 * 1. lower 12 bits of flow_handle must be set to all 1s.
166562306a36Sopenharmony_ci		 * 2. 15th bit of flow_handle must specify the flow
166662306a36Sopenharmony_ci		 *    direction (TX/RX).
166762306a36Sopenharmony_ci		 */
166862306a36Sopenharmony_ci		if (flow_node->flow.l2_key.dir == BNXT_DIR_RX)
166962306a36Sopenharmony_ci			handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_DIR_RX |
167062306a36Sopenharmony_ci				 CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK;
167162306a36Sopenharmony_ci		else
167262306a36Sopenharmony_ci			handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci		*flow_handle = cpu_to_le16(handle);
167562306a36Sopenharmony_ci	} else {
167662306a36Sopenharmony_ci		*flow_handle = flow_node->flow_handle;
167762306a36Sopenharmony_ci	}
167862306a36Sopenharmony_ci}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_cistatic int
168162306a36Sopenharmony_cibnxt_hwrm_cfa_flow_stats_get(struct bnxt *bp, int num_flows,
168262306a36Sopenharmony_ci			     struct bnxt_tc_stats_batch stats_batch[])
168362306a36Sopenharmony_ci{
168462306a36Sopenharmony_ci	struct hwrm_cfa_flow_stats_output *resp;
168562306a36Sopenharmony_ci	struct hwrm_cfa_flow_stats_input *req;
168662306a36Sopenharmony_ci	__le16 *req_flow_handles;
168762306a36Sopenharmony_ci	__le32 *req_flow_ids;
168862306a36Sopenharmony_ci	int rc, i;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	rc = hwrm_req_init(bp, req, HWRM_CFA_FLOW_STATS);
169162306a36Sopenharmony_ci	if (rc)
169262306a36Sopenharmony_ci		goto exit;
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	req_flow_handles = &req->flow_handle_0;
169562306a36Sopenharmony_ci	req_flow_ids = &req->flow_id_0;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	req->num_flows = cpu_to_le16(num_flows);
169862306a36Sopenharmony_ci	for (i = 0; i < num_flows; i++) {
169962306a36Sopenharmony_ci		struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node;
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci		bnxt_fill_cfa_stats_req(bp, flow_node,
170262306a36Sopenharmony_ci					&req_flow_handles[i], &req_flow_ids[i]);
170362306a36Sopenharmony_ci	}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	resp = hwrm_req_hold(bp, req);
170662306a36Sopenharmony_ci	rc = hwrm_req_send(bp, req);
170762306a36Sopenharmony_ci	if (!rc) {
170862306a36Sopenharmony_ci		__le64 *resp_packets;
170962306a36Sopenharmony_ci		__le64 *resp_bytes;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci		resp_packets = &resp->packet_0;
171262306a36Sopenharmony_ci		resp_bytes = &resp->byte_0;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci		for (i = 0; i < num_flows; i++) {
171562306a36Sopenharmony_ci			stats_batch[i].hw_stats.packets =
171662306a36Sopenharmony_ci						le64_to_cpu(resp_packets[i]);
171762306a36Sopenharmony_ci			stats_batch[i].hw_stats.bytes =
171862306a36Sopenharmony_ci						le64_to_cpu(resp_bytes[i]);
171962306a36Sopenharmony_ci		}
172062306a36Sopenharmony_ci	}
172162306a36Sopenharmony_ci	hwrm_req_drop(bp, req);
172262306a36Sopenharmony_ciexit:
172362306a36Sopenharmony_ci	if (rc)
172462306a36Sopenharmony_ci		netdev_info(bp->dev, "error rc=%d\n", rc);
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	return rc;
172762306a36Sopenharmony_ci}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci/* Add val to accum while handling a possible wraparound
173062306a36Sopenharmony_ci * of val. Eventhough val is of type u64, its actual width
173162306a36Sopenharmony_ci * is denoted by mask and will wrap-around beyond that width.
173262306a36Sopenharmony_ci */
173362306a36Sopenharmony_cistatic void accumulate_val(u64 *accum, u64 val, u64 mask)
173462306a36Sopenharmony_ci{
173562306a36Sopenharmony_ci#define low_bits(x, mask)		((x) & (mask))
173662306a36Sopenharmony_ci#define high_bits(x, mask)		((x) & ~(mask))
173762306a36Sopenharmony_ci	bool wrapped = val < low_bits(*accum, mask);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	*accum = high_bits(*accum, mask) + val;
174062306a36Sopenharmony_ci	if (wrapped)
174162306a36Sopenharmony_ci		*accum += (mask + 1);
174262306a36Sopenharmony_ci}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci/* The HW counters' width is much less than 64bits.
174562306a36Sopenharmony_ci * Handle possible wrap-around while updating the stat counters
174662306a36Sopenharmony_ci */
174762306a36Sopenharmony_cistatic void bnxt_flow_stats_accum(struct bnxt_tc_info *tc_info,
174862306a36Sopenharmony_ci				  struct bnxt_tc_flow_stats *acc_stats,
174962306a36Sopenharmony_ci				  struct bnxt_tc_flow_stats *hw_stats)
175062306a36Sopenharmony_ci{
175162306a36Sopenharmony_ci	accumulate_val(&acc_stats->bytes, hw_stats->bytes, tc_info->bytes_mask);
175262306a36Sopenharmony_ci	accumulate_val(&acc_stats->packets, hw_stats->packets,
175362306a36Sopenharmony_ci		       tc_info->packets_mask);
175462306a36Sopenharmony_ci}
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_cistatic int
175762306a36Sopenharmony_cibnxt_tc_flow_stats_batch_update(struct bnxt *bp, int num_flows,
175862306a36Sopenharmony_ci				struct bnxt_tc_stats_batch stats_batch[])
175962306a36Sopenharmony_ci{
176062306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
176162306a36Sopenharmony_ci	int rc, i;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	rc = bnxt_hwrm_cfa_flow_stats_get(bp, num_flows, stats_batch);
176462306a36Sopenharmony_ci	if (rc)
176562306a36Sopenharmony_ci		return rc;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	for (i = 0; i < num_flows; i++) {
176862306a36Sopenharmony_ci		struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node;
176962306a36Sopenharmony_ci		struct bnxt_tc_flow *flow = &flow_node->flow;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci		spin_lock(&flow->stats_lock);
177262306a36Sopenharmony_ci		bnxt_flow_stats_accum(tc_info, &flow->stats,
177362306a36Sopenharmony_ci				      &stats_batch[i].hw_stats);
177462306a36Sopenharmony_ci		if (flow->stats.packets != flow->prev_stats.packets)
177562306a36Sopenharmony_ci			flow->lastused = jiffies;
177662306a36Sopenharmony_ci		spin_unlock(&flow->stats_lock);
177762306a36Sopenharmony_ci	}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	return 0;
178062306a36Sopenharmony_ci}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_cistatic int
178362306a36Sopenharmony_cibnxt_tc_flow_stats_batch_prep(struct bnxt *bp,
178462306a36Sopenharmony_ci			      struct bnxt_tc_stats_batch stats_batch[],
178562306a36Sopenharmony_ci			      int *num_flows)
178662306a36Sopenharmony_ci{
178762306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
178862306a36Sopenharmony_ci	struct rhashtable_iter *iter = &tc_info->iter;
178962306a36Sopenharmony_ci	void *flow_node;
179062306a36Sopenharmony_ci	int rc, i;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	rhashtable_walk_start(iter);
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	rc = 0;
179562306a36Sopenharmony_ci	for (i = 0; i < BNXT_FLOW_STATS_BATCH_MAX; i++) {
179662306a36Sopenharmony_ci		flow_node = rhashtable_walk_next(iter);
179762306a36Sopenharmony_ci		if (IS_ERR(flow_node)) {
179862306a36Sopenharmony_ci			i = 0;
179962306a36Sopenharmony_ci			if (PTR_ERR(flow_node) == -EAGAIN) {
180062306a36Sopenharmony_ci				continue;
180162306a36Sopenharmony_ci			} else {
180262306a36Sopenharmony_ci				rc = PTR_ERR(flow_node);
180362306a36Sopenharmony_ci				goto done;
180462306a36Sopenharmony_ci			}
180562306a36Sopenharmony_ci		}
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci		/* No more flows */
180862306a36Sopenharmony_ci		if (!flow_node)
180962306a36Sopenharmony_ci			goto done;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci		stats_batch[i].flow_node = flow_node;
181262306a36Sopenharmony_ci	}
181362306a36Sopenharmony_cidone:
181462306a36Sopenharmony_ci	rhashtable_walk_stop(iter);
181562306a36Sopenharmony_ci	*num_flows = i;
181662306a36Sopenharmony_ci	return rc;
181762306a36Sopenharmony_ci}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_civoid bnxt_tc_flow_stats_work(struct bnxt *bp)
182062306a36Sopenharmony_ci{
182162306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
182262306a36Sopenharmony_ci	int num_flows, rc;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	num_flows = atomic_read(&tc_info->flow_table.nelems);
182562306a36Sopenharmony_ci	if (!num_flows)
182662306a36Sopenharmony_ci		return;
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	rhashtable_walk_enter(&tc_info->flow_table, &tc_info->iter);
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	for (;;) {
183162306a36Sopenharmony_ci		rc = bnxt_tc_flow_stats_batch_prep(bp, tc_info->stats_batch,
183262306a36Sopenharmony_ci						   &num_flows);
183362306a36Sopenharmony_ci		if (rc) {
183462306a36Sopenharmony_ci			if (rc == -EAGAIN)
183562306a36Sopenharmony_ci				continue;
183662306a36Sopenharmony_ci			break;
183762306a36Sopenharmony_ci		}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci		if (!num_flows)
184062306a36Sopenharmony_ci			break;
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci		bnxt_tc_flow_stats_batch_update(bp, num_flows,
184362306a36Sopenharmony_ci						tc_info->stats_batch);
184462306a36Sopenharmony_ci	}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	rhashtable_walk_exit(&tc_info->iter);
184762306a36Sopenharmony_ci}
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ciint bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid,
185062306a36Sopenharmony_ci			 struct flow_cls_offload *cls_flower)
185162306a36Sopenharmony_ci{
185262306a36Sopenharmony_ci	switch (cls_flower->command) {
185362306a36Sopenharmony_ci	case FLOW_CLS_REPLACE:
185462306a36Sopenharmony_ci		return bnxt_tc_add_flow(bp, src_fid, cls_flower);
185562306a36Sopenharmony_ci	case FLOW_CLS_DESTROY:
185662306a36Sopenharmony_ci		return bnxt_tc_del_flow(bp, cls_flower);
185762306a36Sopenharmony_ci	case FLOW_CLS_STATS:
185862306a36Sopenharmony_ci		return bnxt_tc_get_flow_stats(bp, cls_flower);
185962306a36Sopenharmony_ci	default:
186062306a36Sopenharmony_ci		return -EOPNOTSUPP;
186162306a36Sopenharmony_ci	}
186262306a36Sopenharmony_ci}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_cistatic int bnxt_tc_setup_indr_block_cb(enum tc_setup_type type,
186562306a36Sopenharmony_ci				       void *type_data, void *cb_priv)
186662306a36Sopenharmony_ci{
186762306a36Sopenharmony_ci	struct bnxt_flower_indr_block_cb_priv *priv = cb_priv;
186862306a36Sopenharmony_ci	struct flow_cls_offload *flower = type_data;
186962306a36Sopenharmony_ci	struct bnxt *bp = priv->bp;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	if (!tc_cls_can_offload_and_chain0(bp->dev, type_data))
187262306a36Sopenharmony_ci		return -EOPNOTSUPP;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	switch (type) {
187562306a36Sopenharmony_ci	case TC_SETUP_CLSFLOWER:
187662306a36Sopenharmony_ci		return bnxt_tc_setup_flower(bp, bp->pf.fw_fid, flower);
187762306a36Sopenharmony_ci	default:
187862306a36Sopenharmony_ci		return -EOPNOTSUPP;
187962306a36Sopenharmony_ci	}
188062306a36Sopenharmony_ci}
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_cistatic struct bnxt_flower_indr_block_cb_priv *
188362306a36Sopenharmony_cibnxt_tc_indr_block_cb_lookup(struct bnxt *bp, struct net_device *netdev)
188462306a36Sopenharmony_ci{
188562306a36Sopenharmony_ci	struct bnxt_flower_indr_block_cb_priv *cb_priv;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	list_for_each_entry(cb_priv, &bp->tc_indr_block_list, list)
188862306a36Sopenharmony_ci		if (cb_priv->tunnel_netdev == netdev)
188962306a36Sopenharmony_ci			return cb_priv;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	return NULL;
189262306a36Sopenharmony_ci}
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_cistatic void bnxt_tc_setup_indr_rel(void *cb_priv)
189562306a36Sopenharmony_ci{
189662306a36Sopenharmony_ci	struct bnxt_flower_indr_block_cb_priv *priv = cb_priv;
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	list_del(&priv->list);
189962306a36Sopenharmony_ci	kfree(priv);
190062306a36Sopenharmony_ci}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_cistatic int bnxt_tc_setup_indr_block(struct net_device *netdev, struct Qdisc *sch, struct bnxt *bp,
190362306a36Sopenharmony_ci				    struct flow_block_offload *f, void *data,
190462306a36Sopenharmony_ci				    void (*cleanup)(struct flow_block_cb *block_cb))
190562306a36Sopenharmony_ci{
190662306a36Sopenharmony_ci	struct bnxt_flower_indr_block_cb_priv *cb_priv;
190762306a36Sopenharmony_ci	struct flow_block_cb *block_cb;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
191062306a36Sopenharmony_ci		return -EOPNOTSUPP;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	switch (f->command) {
191362306a36Sopenharmony_ci	case FLOW_BLOCK_BIND:
191462306a36Sopenharmony_ci		cb_priv = kmalloc(sizeof(*cb_priv), GFP_KERNEL);
191562306a36Sopenharmony_ci		if (!cb_priv)
191662306a36Sopenharmony_ci			return -ENOMEM;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci		cb_priv->tunnel_netdev = netdev;
191962306a36Sopenharmony_ci		cb_priv->bp = bp;
192062306a36Sopenharmony_ci		list_add(&cb_priv->list, &bp->tc_indr_block_list);
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci		block_cb = flow_indr_block_cb_alloc(bnxt_tc_setup_indr_block_cb,
192362306a36Sopenharmony_ci						    cb_priv, cb_priv,
192462306a36Sopenharmony_ci						    bnxt_tc_setup_indr_rel, f,
192562306a36Sopenharmony_ci						    netdev, sch, data, bp, cleanup);
192662306a36Sopenharmony_ci		if (IS_ERR(block_cb)) {
192762306a36Sopenharmony_ci			list_del(&cb_priv->list);
192862306a36Sopenharmony_ci			kfree(cb_priv);
192962306a36Sopenharmony_ci			return PTR_ERR(block_cb);
193062306a36Sopenharmony_ci		}
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci		flow_block_cb_add(block_cb, f);
193362306a36Sopenharmony_ci		list_add_tail(&block_cb->driver_list, &bnxt_block_cb_list);
193462306a36Sopenharmony_ci		break;
193562306a36Sopenharmony_ci	case FLOW_BLOCK_UNBIND:
193662306a36Sopenharmony_ci		cb_priv = bnxt_tc_indr_block_cb_lookup(bp, netdev);
193762306a36Sopenharmony_ci		if (!cb_priv)
193862306a36Sopenharmony_ci			return -ENOENT;
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci		block_cb = flow_block_cb_lookup(f->block,
194162306a36Sopenharmony_ci						bnxt_tc_setup_indr_block_cb,
194262306a36Sopenharmony_ci						cb_priv);
194362306a36Sopenharmony_ci		if (!block_cb)
194462306a36Sopenharmony_ci			return -ENOENT;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci		flow_indr_block_cb_remove(block_cb, f);
194762306a36Sopenharmony_ci		list_del(&block_cb->driver_list);
194862306a36Sopenharmony_ci		break;
194962306a36Sopenharmony_ci	default:
195062306a36Sopenharmony_ci		return -EOPNOTSUPP;
195162306a36Sopenharmony_ci	}
195262306a36Sopenharmony_ci	return 0;
195362306a36Sopenharmony_ci}
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_cistatic bool bnxt_is_netdev_indr_offload(struct net_device *netdev)
195662306a36Sopenharmony_ci{
195762306a36Sopenharmony_ci	return netif_is_vxlan(netdev);
195862306a36Sopenharmony_ci}
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_cistatic int bnxt_tc_setup_indr_cb(struct net_device *netdev, struct Qdisc *sch, void *cb_priv,
196162306a36Sopenharmony_ci				 enum tc_setup_type type, void *type_data,
196262306a36Sopenharmony_ci				 void *data,
196362306a36Sopenharmony_ci				 void (*cleanup)(struct flow_block_cb *block_cb))
196462306a36Sopenharmony_ci{
196562306a36Sopenharmony_ci	if (!netdev || !bnxt_is_netdev_indr_offload(netdev))
196662306a36Sopenharmony_ci		return -EOPNOTSUPP;
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	switch (type) {
196962306a36Sopenharmony_ci	case TC_SETUP_BLOCK:
197062306a36Sopenharmony_ci		return bnxt_tc_setup_indr_block(netdev, sch, cb_priv, type_data, data, cleanup);
197162306a36Sopenharmony_ci	default:
197262306a36Sopenharmony_ci		break;
197362306a36Sopenharmony_ci	}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	return -EOPNOTSUPP;
197662306a36Sopenharmony_ci}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_cistatic const struct rhashtable_params bnxt_tc_flow_ht_params = {
197962306a36Sopenharmony_ci	.head_offset = offsetof(struct bnxt_tc_flow_node, node),
198062306a36Sopenharmony_ci	.key_offset = offsetof(struct bnxt_tc_flow_node, cookie),
198162306a36Sopenharmony_ci	.key_len = sizeof(((struct bnxt_tc_flow_node *)0)->cookie),
198262306a36Sopenharmony_ci	.automatic_shrinking = true
198362306a36Sopenharmony_ci};
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_cistatic const struct rhashtable_params bnxt_tc_l2_ht_params = {
198662306a36Sopenharmony_ci	.head_offset = offsetof(struct bnxt_tc_l2_node, node),
198762306a36Sopenharmony_ci	.key_offset = offsetof(struct bnxt_tc_l2_node, key),
198862306a36Sopenharmony_ci	.key_len = BNXT_TC_L2_KEY_LEN,
198962306a36Sopenharmony_ci	.automatic_shrinking = true
199062306a36Sopenharmony_ci};
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_cistatic const struct rhashtable_params bnxt_tc_decap_l2_ht_params = {
199362306a36Sopenharmony_ci	.head_offset = offsetof(struct bnxt_tc_l2_node, node),
199462306a36Sopenharmony_ci	.key_offset = offsetof(struct bnxt_tc_l2_node, key),
199562306a36Sopenharmony_ci	.key_len = BNXT_TC_L2_KEY_LEN,
199662306a36Sopenharmony_ci	.automatic_shrinking = true
199762306a36Sopenharmony_ci};
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_cistatic const struct rhashtable_params bnxt_tc_tunnel_ht_params = {
200062306a36Sopenharmony_ci	.head_offset = offsetof(struct bnxt_tc_tunnel_node, node),
200162306a36Sopenharmony_ci	.key_offset = offsetof(struct bnxt_tc_tunnel_node, key),
200262306a36Sopenharmony_ci	.key_len = sizeof(struct ip_tunnel_key),
200362306a36Sopenharmony_ci	.automatic_shrinking = true
200462306a36Sopenharmony_ci};
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci/* convert counter width in bits to a mask */
200762306a36Sopenharmony_ci#define mask(width)		((u64)~0 >> (64 - (width)))
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ciint bnxt_init_tc(struct bnxt *bp)
201062306a36Sopenharmony_ci{
201162306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info;
201262306a36Sopenharmony_ci	int rc;
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	if (bp->hwrm_spec_code < 0x10803)
201562306a36Sopenharmony_ci		return 0;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	tc_info = kzalloc(sizeof(*tc_info), GFP_KERNEL);
201862306a36Sopenharmony_ci	if (!tc_info)
201962306a36Sopenharmony_ci		return -ENOMEM;
202062306a36Sopenharmony_ci	mutex_init(&tc_info->lock);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	/* Counter widths are programmed by FW */
202362306a36Sopenharmony_ci	tc_info->bytes_mask = mask(36);
202462306a36Sopenharmony_ci	tc_info->packets_mask = mask(28);
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	tc_info->flow_ht_params = bnxt_tc_flow_ht_params;
202762306a36Sopenharmony_ci	rc = rhashtable_init(&tc_info->flow_table, &tc_info->flow_ht_params);
202862306a36Sopenharmony_ci	if (rc)
202962306a36Sopenharmony_ci		goto free_tc_info;
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	tc_info->l2_ht_params = bnxt_tc_l2_ht_params;
203262306a36Sopenharmony_ci	rc = rhashtable_init(&tc_info->l2_table, &tc_info->l2_ht_params);
203362306a36Sopenharmony_ci	if (rc)
203462306a36Sopenharmony_ci		goto destroy_flow_table;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	tc_info->decap_l2_ht_params = bnxt_tc_decap_l2_ht_params;
203762306a36Sopenharmony_ci	rc = rhashtable_init(&tc_info->decap_l2_table,
203862306a36Sopenharmony_ci			     &tc_info->decap_l2_ht_params);
203962306a36Sopenharmony_ci	if (rc)
204062306a36Sopenharmony_ci		goto destroy_l2_table;
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	tc_info->decap_ht_params = bnxt_tc_tunnel_ht_params;
204362306a36Sopenharmony_ci	rc = rhashtable_init(&tc_info->decap_table,
204462306a36Sopenharmony_ci			     &tc_info->decap_ht_params);
204562306a36Sopenharmony_ci	if (rc)
204662306a36Sopenharmony_ci		goto destroy_decap_l2_table;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	tc_info->encap_ht_params = bnxt_tc_tunnel_ht_params;
204962306a36Sopenharmony_ci	rc = rhashtable_init(&tc_info->encap_table,
205062306a36Sopenharmony_ci			     &tc_info->encap_ht_params);
205162306a36Sopenharmony_ci	if (rc)
205262306a36Sopenharmony_ci		goto destroy_decap_table;
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	tc_info->enabled = true;
205562306a36Sopenharmony_ci	bp->dev->hw_features |= NETIF_F_HW_TC;
205662306a36Sopenharmony_ci	bp->dev->features |= NETIF_F_HW_TC;
205762306a36Sopenharmony_ci	bp->tc_info = tc_info;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	/* init indirect block notifications */
206062306a36Sopenharmony_ci	INIT_LIST_HEAD(&bp->tc_indr_block_list);
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	rc = flow_indr_dev_register(bnxt_tc_setup_indr_cb, bp);
206362306a36Sopenharmony_ci	if (!rc)
206462306a36Sopenharmony_ci		return 0;
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	rhashtable_destroy(&tc_info->encap_table);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_cidestroy_decap_table:
206962306a36Sopenharmony_ci	rhashtable_destroy(&tc_info->decap_table);
207062306a36Sopenharmony_cidestroy_decap_l2_table:
207162306a36Sopenharmony_ci	rhashtable_destroy(&tc_info->decap_l2_table);
207262306a36Sopenharmony_cidestroy_l2_table:
207362306a36Sopenharmony_ci	rhashtable_destroy(&tc_info->l2_table);
207462306a36Sopenharmony_cidestroy_flow_table:
207562306a36Sopenharmony_ci	rhashtable_destroy(&tc_info->flow_table);
207662306a36Sopenharmony_cifree_tc_info:
207762306a36Sopenharmony_ci	kfree(tc_info);
207862306a36Sopenharmony_ci	bp->tc_info = NULL;
207962306a36Sopenharmony_ci	return rc;
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_civoid bnxt_shutdown_tc(struct bnxt *bp)
208362306a36Sopenharmony_ci{
208462306a36Sopenharmony_ci	struct bnxt_tc_info *tc_info = bp->tc_info;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	if (!bnxt_tc_flower_enabled(bp))
208762306a36Sopenharmony_ci		return;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	flow_indr_dev_unregister(bnxt_tc_setup_indr_cb, bp,
209062306a36Sopenharmony_ci				 bnxt_tc_setup_indr_rel);
209162306a36Sopenharmony_ci	rhashtable_destroy(&tc_info->flow_table);
209262306a36Sopenharmony_ci	rhashtable_destroy(&tc_info->l2_table);
209362306a36Sopenharmony_ci	rhashtable_destroy(&tc_info->decap_l2_table);
209462306a36Sopenharmony_ci	rhashtable_destroy(&tc_info->decap_table);
209562306a36Sopenharmony_ci	rhashtable_destroy(&tc_info->encap_table);
209662306a36Sopenharmony_ci	kfree(tc_info);
209762306a36Sopenharmony_ci	bp->tc_info = NULL;
209862306a36Sopenharmony_ci}
2099