12ae7408fSSathya Perla /* Broadcom NetXtreme-C/E network driver.
22ae7408fSSathya Perla  *
32ae7408fSSathya Perla  * Copyright (c) 2017 Broadcom Limited
42ae7408fSSathya Perla  *
52ae7408fSSathya Perla  * This program is free software; you can redistribute it and/or modify
62ae7408fSSathya Perla  * it under the terms of the GNU General Public License as published by
72ae7408fSSathya Perla  * the Free Software Foundation.
82ae7408fSSathya Perla  */
92ae7408fSSathya Perla 
102ae7408fSSathya Perla #include <linux/netdevice.h>
112ae7408fSSathya Perla #include <linux/inetdevice.h>
122ae7408fSSathya Perla #include <linux/if_vlan.h>
132ae7408fSSathya Perla #include <net/flow_dissector.h>
142ae7408fSSathya Perla #include <net/pkt_cls.h>
152ae7408fSSathya Perla #include <net/tc_act/tc_gact.h>
162ae7408fSSathya Perla #include <net/tc_act/tc_skbedit.h>
172ae7408fSSathya Perla #include <net/tc_act/tc_mirred.h>
182ae7408fSSathya Perla #include <net/tc_act/tc_vlan.h>
1990f90624SVenkat Duvvuru #include <net/tc_act/tc_pedit.h>
208c95f773SSathya Perla #include <net/tc_act/tc_tunnel_key.h>
21627c89d0SSriharsha Basavapatna #include <net/vxlan.h>
222ae7408fSSathya Perla 
232ae7408fSSathya Perla #include "bnxt_hsi.h"
242ae7408fSSathya Perla #include "bnxt.h"
253c8c20dbSEdwin Peer #include "bnxt_hwrm.h"
262ae7408fSSathya Perla #include "bnxt_sriov.h"
272ae7408fSSathya Perla #include "bnxt_tc.h"
282ae7408fSSathya Perla #include "bnxt_vfr.h"
292ae7408fSSathya Perla 
302ae7408fSSathya Perla #define BNXT_FID_INVALID			0xffff
312ae7408fSSathya Perla #define VLAN_TCI(vid, prio)	((vid) | ((prio) << VLAN_PRIO_SHIFT))
322ae7408fSSathya Perla 
33e32d4e60SVenkat Duvvuru #define is_vlan_pcp_wildcarded(vlan_tci_mask)	\
34e32d4e60SVenkat Duvvuru 	((ntohs(vlan_tci_mask) & VLAN_PRIO_MASK) == 0x0000)
35e32d4e60SVenkat Duvvuru #define is_vlan_pcp_exactmatch(vlan_tci_mask)	\
36e32d4e60SVenkat Duvvuru 	((ntohs(vlan_tci_mask) & VLAN_PRIO_MASK) == VLAN_PRIO_MASK)
37e32d4e60SVenkat Duvvuru #define is_vlan_pcp_zero(vlan_tci)	\
38e32d4e60SVenkat Duvvuru 	((ntohs(vlan_tci) & VLAN_PRIO_MASK) == 0x0000)
39e32d4e60SVenkat Duvvuru #define is_vid_exactmatch(vlan_tci_mask)	\
40e32d4e60SVenkat Duvvuru 	((ntohs(vlan_tci_mask) & VLAN_VID_MASK) == VLAN_VID_MASK)
41e32d4e60SVenkat Duvvuru 
4290f90624SVenkat Duvvuru static bool is_wildcard(void *mask, int len);
4390f90624SVenkat Duvvuru static bool is_exactmatch(void *mask, int len);
442ae7408fSSathya Perla /* Return the dst fid of the func for flow forwarding
452ae7408fSSathya Perla  * For PFs: src_fid is the fid of the PF
462ae7408fSSathya Perla  * For VF-reps: src_fid the fid of the VF
472ae7408fSSathya Perla  */
bnxt_flow_get_dst_fid(struct bnxt * pf_bp,struct net_device * dev)482ae7408fSSathya Perla static u16 bnxt_flow_get_dst_fid(struct bnxt *pf_bp, struct net_device *dev)
492ae7408fSSathya Perla {
502ae7408fSSathya Perla 	struct bnxt *bp;
512ae7408fSSathya Perla 
522ae7408fSSathya Perla 	/* check if dev belongs to the same switch */
5352d5254aSFlorian Fainelli 	if (!netdev_port_same_parent_id(pf_bp->dev, dev)) {
549a005c38SJonathan Lemon 		netdev_info(pf_bp->dev, "dev(ifindex=%d) not on same switch\n",
552ae7408fSSathya Perla 			    dev->ifindex);
562ae7408fSSathya Perla 		return BNXT_FID_INVALID;
572ae7408fSSathya Perla 	}
582ae7408fSSathya Perla 
592ae7408fSSathya Perla 	/* Is dev a VF-rep? */
60dd4ea1daSSathya Perla 	if (bnxt_dev_is_vf_rep(dev))
612ae7408fSSathya Perla 		return bnxt_vf_rep_get_fid(dev);
622ae7408fSSathya Perla 
632ae7408fSSathya Perla 	bp = netdev_priv(dev);
642ae7408fSSathya Perla 	return bp->pf.fw_fid;
652ae7408fSSathya Perla }
662ae7408fSSathya Perla 
bnxt_tc_parse_redir(struct bnxt * bp,struct bnxt_tc_actions * actions,const struct flow_action_entry * act)672ae7408fSSathya Perla static int bnxt_tc_parse_redir(struct bnxt *bp,
682ae7408fSSathya Perla 			       struct bnxt_tc_actions *actions,
6973867881SPablo Neira Ayuso 			       const struct flow_action_entry *act)
702ae7408fSSathya Perla {
7173867881SPablo Neira Ayuso 	struct net_device *dev = act->dev;
722ae7408fSSathya Perla 
732ae7408fSSathya Perla 	if (!dev) {
749a005c38SJonathan Lemon 		netdev_info(bp->dev, "no dev in mirred action\n");
752ae7408fSSathya Perla 		return -EINVAL;
762ae7408fSSathya Perla 	}
772ae7408fSSathya Perla 
782ae7408fSSathya Perla 	actions->flags |= BNXT_TC_ACTION_FLAG_FWD;
792ae7408fSSathya Perla 	actions->dst_dev = dev;
802ae7408fSSathya Perla 	return 0;
812ae7408fSSathya Perla }
822ae7408fSSathya Perla 
bnxt_tc_parse_vlan(struct bnxt * bp,struct bnxt_tc_actions * actions,const struct flow_action_entry * act)838c6ec361SDavide Caratti static int bnxt_tc_parse_vlan(struct bnxt *bp,
842ae7408fSSathya Perla 			      struct bnxt_tc_actions *actions,
8573867881SPablo Neira Ayuso 			      const struct flow_action_entry *act)
862ae7408fSSathya Perla {
8773867881SPablo Neira Ayuso 	switch (act->id) {
8873867881SPablo Neira Ayuso 	case FLOW_ACTION_VLAN_POP:
892ae7408fSSathya Perla 		actions->flags |= BNXT_TC_ACTION_FLAG_POP_VLAN;
908c6ec361SDavide Caratti 		break;
9173867881SPablo Neira Ayuso 	case FLOW_ACTION_VLAN_PUSH:
922ae7408fSSathya Perla 		actions->flags |= BNXT_TC_ACTION_FLAG_PUSH_VLAN;
9373867881SPablo Neira Ayuso 		actions->push_vlan_tci = htons(act->vlan.vid);
9473867881SPablo Neira Ayuso 		actions->push_vlan_tpid = act->vlan.proto;
958c6ec361SDavide Caratti 		break;
968c6ec361SDavide Caratti 	default:
978c6ec361SDavide Caratti 		return -EOPNOTSUPP;
982ae7408fSSathya Perla 	}
998c6ec361SDavide Caratti 	return 0;
1002ae7408fSSathya Perla }
1012ae7408fSSathya Perla 
bnxt_tc_parse_tunnel_set(struct bnxt * bp,struct bnxt_tc_actions * actions,const struct flow_action_entry * act)1028c95f773SSathya Perla static int bnxt_tc_parse_tunnel_set(struct bnxt *bp,
1038c95f773SSathya Perla 				    struct bnxt_tc_actions *actions,
10473867881SPablo Neira Ayuso 				    const struct flow_action_entry *act)
1058c95f773SSathya Perla {
10673867881SPablo Neira Ayuso 	const struct ip_tunnel_info *tun_info = act->tunnel;
10773867881SPablo Neira Ayuso 	const struct ip_tunnel_key *tun_key = &tun_info->key;
1088c95f773SSathya Perla 
1098c95f773SSathya Perla 	if (ip_tunnel_info_af(tun_info) != AF_INET) {
1109a005c38SJonathan Lemon 		netdev_info(bp->dev, "only IPv4 tunnel-encap is supported\n");
1118c95f773SSathya Perla 		return -EOPNOTSUPP;
1128c95f773SSathya Perla 	}
1138c95f773SSathya Perla 
1148c95f773SSathya Perla 	actions->tun_encap_key = *tun_key;
1158c95f773SSathya Perla 	actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP;
1168c95f773SSathya Perla 	return 0;
1178c95f773SSathya Perla }
1188c95f773SSathya Perla 
1199b9eb518SSomnath Kotur /* Key & Mask from the stack comes unaligned in multiple iterations of 4 bytes
1209b9eb518SSomnath Kotur  * each(u32).
12190f90624SVenkat Duvvuru  * This routine consolidates such multiple unaligned values into one
12290f90624SVenkat Duvvuru  * field each for Key & Mask (for src and dst macs separately)
12390f90624SVenkat Duvvuru  * For example,
12490f90624SVenkat Duvvuru  *			Mask/Key	Offset	Iteration
12590f90624SVenkat Duvvuru  *			==========	======	=========
12690f90624SVenkat Duvvuru  *	dst mac		0xffffffff	0	1
12790f90624SVenkat Duvvuru  *	dst mac		0x0000ffff	4	2
12890f90624SVenkat Duvvuru  *
12990f90624SVenkat Duvvuru  *	src mac		0xffff0000	4	1
13090f90624SVenkat Duvvuru  *	src mac		0xffffffff	8	2
13190f90624SVenkat Duvvuru  *
13290f90624SVenkat Duvvuru  * The above combination coming from the stack will be consolidated as
13390f90624SVenkat Duvvuru  *			Mask/Key
13490f90624SVenkat Duvvuru  *			==============
13590f90624SVenkat Duvvuru  *	src mac:	0xffffffffffff
13690f90624SVenkat Duvvuru  *	dst mac:	0xffffffffffff
13790f90624SVenkat Duvvuru  */
bnxt_set_l2_key_mask(u32 part_key,u32 part_mask,u8 * actual_key,u8 * actual_mask)13890f90624SVenkat Duvvuru static void bnxt_set_l2_key_mask(u32 part_key, u32 part_mask,
13990f90624SVenkat Duvvuru 				 u8 *actual_key, u8 *actual_mask)
14090f90624SVenkat Duvvuru {
14190f90624SVenkat Duvvuru 	u32 key = get_unaligned((u32 *)actual_key);
14290f90624SVenkat Duvvuru 	u32 mask = get_unaligned((u32 *)actual_mask);
14390f90624SVenkat Duvvuru 
14490f90624SVenkat Duvvuru 	part_key &= part_mask;
14590f90624SVenkat Duvvuru 	part_key |= key & ~part_mask;
14690f90624SVenkat Duvvuru 
14790f90624SVenkat Duvvuru 	put_unaligned(mask | part_mask, (u32 *)actual_mask);
14890f90624SVenkat Duvvuru 	put_unaligned(part_key, (u32 *)actual_key);
14990f90624SVenkat Duvvuru }
15090f90624SVenkat Duvvuru 
15190f90624SVenkat Duvvuru static int
bnxt_fill_l2_rewrite_fields(struct bnxt_tc_actions * actions,u16 * eth_addr,u16 * eth_addr_mask)15290f90624SVenkat Duvvuru bnxt_fill_l2_rewrite_fields(struct bnxt_tc_actions *actions,
15390f90624SVenkat Duvvuru 			    u16 *eth_addr, u16 *eth_addr_mask)
15490f90624SVenkat Duvvuru {
15590f90624SVenkat Duvvuru 	u16 *p;
15690f90624SVenkat Duvvuru 	int j;
15790f90624SVenkat Duvvuru 
15890f90624SVenkat Duvvuru 	if (unlikely(bnxt_eth_addr_key_mask_invalid(eth_addr, eth_addr_mask)))
15990f90624SVenkat Duvvuru 		return -EINVAL;
16090f90624SVenkat Duvvuru 
16190f90624SVenkat Duvvuru 	if (!is_wildcard(&eth_addr_mask[0], ETH_ALEN)) {
16290f90624SVenkat Duvvuru 		if (!is_exactmatch(&eth_addr_mask[0], ETH_ALEN))
16390f90624SVenkat Duvvuru 			return -EINVAL;
16490f90624SVenkat Duvvuru 		/* FW expects dmac to be in u16 array format */
16590f90624SVenkat Duvvuru 		p = eth_addr;
16690f90624SVenkat Duvvuru 		for (j = 0; j < 3; j++)
16790f90624SVenkat Duvvuru 			actions->l2_rewrite_dmac[j] = cpu_to_be16(*(p + j));
16890f90624SVenkat Duvvuru 	}
16990f90624SVenkat Duvvuru 
1703128aad1SVenkat Duvvuru 	if (!is_wildcard(&eth_addr_mask[ETH_ALEN / 2], ETH_ALEN)) {
1713128aad1SVenkat Duvvuru 		if (!is_exactmatch(&eth_addr_mask[ETH_ALEN / 2], ETH_ALEN))
17290f90624SVenkat Duvvuru 			return -EINVAL;
17390f90624SVenkat Duvvuru 		/* FW expects smac to be in u16 array format */
17490f90624SVenkat Duvvuru 		p = &eth_addr[ETH_ALEN / 2];
17590f90624SVenkat Duvvuru 		for (j = 0; j < 3; j++)
17690f90624SVenkat Duvvuru 			actions->l2_rewrite_smac[j] = cpu_to_be16(*(p + j));
17790f90624SVenkat Duvvuru 	}
17890f90624SVenkat Duvvuru 
17990f90624SVenkat Duvvuru 	return 0;
18090f90624SVenkat Duvvuru }
18190f90624SVenkat Duvvuru 
18290f90624SVenkat Duvvuru static int
bnxt_tc_parse_pedit(struct bnxt * bp,struct bnxt_tc_actions * actions,struct flow_action_entry * act,int act_idx,u8 * eth_addr,u8 * eth_addr_mask)18390f90624SVenkat Duvvuru bnxt_tc_parse_pedit(struct bnxt *bp, struct bnxt_tc_actions *actions,
1849b9eb518SSomnath Kotur 		    struct flow_action_entry *act, int act_idx, u8 *eth_addr,
18590f90624SVenkat Duvvuru 		    u8 *eth_addr_mask)
18690f90624SVenkat Duvvuru {
1879b9eb518SSomnath Kotur 	size_t offset_of_ip6_daddr = offsetof(struct ipv6hdr, daddr);
1889b9eb518SSomnath Kotur 	size_t offset_of_ip6_saddr = offsetof(struct ipv6hdr, saddr);
1899b9eb518SSomnath Kotur 	u32 mask, val, offset, idx;
19090f90624SVenkat Duvvuru 	u8 htype;
19190f90624SVenkat Duvvuru 
19290f90624SVenkat Duvvuru 	offset = act->mangle.offset;
19390f90624SVenkat Duvvuru 	htype = act->mangle.htype;
1949b9eb518SSomnath Kotur 	mask = ~act->mangle.mask;
1959b9eb518SSomnath Kotur 	val = act->mangle.val;
1969b9eb518SSomnath Kotur 
19790f90624SVenkat Duvvuru 	switch (htype) {
19890f90624SVenkat Duvvuru 	case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
19990f90624SVenkat Duvvuru 		if (offset > PEDIT_OFFSET_SMAC_LAST_4_BYTES) {
20090f90624SVenkat Duvvuru 			netdev_err(bp->dev,
20190f90624SVenkat Duvvuru 				   "%s: eth_hdr: Invalid pedit field\n",
20290f90624SVenkat Duvvuru 				   __func__);
20390f90624SVenkat Duvvuru 			return -EINVAL;
20490f90624SVenkat Duvvuru 		}
20590f90624SVenkat Duvvuru 		actions->flags |= BNXT_TC_ACTION_FLAG_L2_REWRITE;
20690f90624SVenkat Duvvuru 
20790f90624SVenkat Duvvuru 		bnxt_set_l2_key_mask(val, mask, &eth_addr[offset],
20890f90624SVenkat Duvvuru 				     &eth_addr_mask[offset]);
20990f90624SVenkat Duvvuru 		break;
2109b9eb518SSomnath Kotur 	case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
2119b9eb518SSomnath Kotur 		actions->flags |= BNXT_TC_ACTION_FLAG_NAT_XLATE;
2129b9eb518SSomnath Kotur 		actions->nat.l3_is_ipv4 = true;
2139b9eb518SSomnath Kotur 		if (offset ==  offsetof(struct iphdr, saddr)) {
2149b9eb518SSomnath Kotur 			actions->nat.src_xlate = true;
2159b9eb518SSomnath Kotur 			actions->nat.l3.ipv4.saddr.s_addr = htonl(val);
2169b9eb518SSomnath Kotur 		} else if (offset ==  offsetof(struct iphdr, daddr)) {
2179b9eb518SSomnath Kotur 			actions->nat.src_xlate = false;
2189b9eb518SSomnath Kotur 			actions->nat.l3.ipv4.daddr.s_addr = htonl(val);
2199b9eb518SSomnath Kotur 		} else {
2209b9eb518SSomnath Kotur 			netdev_err(bp->dev,
2219b9eb518SSomnath Kotur 				   "%s: IPv4_hdr: Invalid pedit field\n",
2229b9eb518SSomnath Kotur 				   __func__);
2239b9eb518SSomnath Kotur 			return -EINVAL;
2249b9eb518SSomnath Kotur 		}
2259b9eb518SSomnath Kotur 
2269b9eb518SSomnath Kotur 		netdev_dbg(bp->dev, "nat.src_xlate = %d src IP: %pI4 dst ip : %pI4\n",
2279b9eb518SSomnath Kotur 			   actions->nat.src_xlate, &actions->nat.l3.ipv4.saddr,
2289b9eb518SSomnath Kotur 			   &actions->nat.l3.ipv4.daddr);
2299b9eb518SSomnath Kotur 		break;
2309b9eb518SSomnath Kotur 
2319b9eb518SSomnath Kotur 	case FLOW_ACT_MANGLE_HDR_TYPE_IP6:
2329b9eb518SSomnath Kotur 		actions->flags |= BNXT_TC_ACTION_FLAG_NAT_XLATE;
2339b9eb518SSomnath Kotur 		actions->nat.l3_is_ipv4 = false;
2349b9eb518SSomnath Kotur 		if (offset >= offsetof(struct ipv6hdr, saddr) &&
2359b9eb518SSomnath Kotur 		    offset < offset_of_ip6_daddr) {
2369b9eb518SSomnath Kotur 			/* 16 byte IPv6 address comes in 4 iterations of
2379b9eb518SSomnath Kotur 			 * 4byte chunks each
2389b9eb518SSomnath Kotur 			 */
2399b9eb518SSomnath Kotur 			actions->nat.src_xlate = true;
2409b9eb518SSomnath Kotur 			idx = (offset - offset_of_ip6_saddr) / 4;
2419b9eb518SSomnath Kotur 			/* First 4bytes will be copied to idx 0 and so on */
2429b9eb518SSomnath Kotur 			actions->nat.l3.ipv6.saddr.s6_addr32[idx] = htonl(val);
2439b9eb518SSomnath Kotur 		} else if (offset >= offset_of_ip6_daddr &&
2449b9eb518SSomnath Kotur 			   offset < offset_of_ip6_daddr + 16) {
2459b9eb518SSomnath Kotur 			actions->nat.src_xlate = false;
2469b9eb518SSomnath Kotur 			idx = (offset - offset_of_ip6_daddr) / 4;
2479b9eb518SSomnath Kotur 			actions->nat.l3.ipv6.saddr.s6_addr32[idx] = htonl(val);
2489b9eb518SSomnath Kotur 		} else {
2499b9eb518SSomnath Kotur 			netdev_err(bp->dev,
2509b9eb518SSomnath Kotur 				   "%s: IPv6_hdr: Invalid pedit field\n",
2519b9eb518SSomnath Kotur 				   __func__);
2529b9eb518SSomnath Kotur 			return -EINVAL;
2539b9eb518SSomnath Kotur 		}
2549b9eb518SSomnath Kotur 		break;
2559b9eb518SSomnath Kotur 	case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
2569b9eb518SSomnath Kotur 	case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
2579b9eb518SSomnath Kotur 		/* HW does not support L4 rewrite alone without L3
2589b9eb518SSomnath Kotur 		 * rewrite
2599b9eb518SSomnath Kotur 		 */
2609b9eb518SSomnath Kotur 		if (!(actions->flags & BNXT_TC_ACTION_FLAG_NAT_XLATE)) {
2619b9eb518SSomnath Kotur 			netdev_err(bp->dev,
2629b9eb518SSomnath Kotur 				   "Need to specify L3 rewrite as well\n");
2639b9eb518SSomnath Kotur 			return -EINVAL;
2649b9eb518SSomnath Kotur 		}
2659b9eb518SSomnath Kotur 		if (actions->nat.src_xlate)
2669b9eb518SSomnath Kotur 			actions->nat.l4.ports.sport = htons(val);
2679b9eb518SSomnath Kotur 		else
2689b9eb518SSomnath Kotur 			actions->nat.l4.ports.dport = htons(val);
2699b9eb518SSomnath Kotur 		netdev_dbg(bp->dev, "actions->nat.sport = %d dport = %d\n",
2709b9eb518SSomnath Kotur 			   actions->nat.l4.ports.sport,
2719b9eb518SSomnath Kotur 			   actions->nat.l4.ports.dport);
2729b9eb518SSomnath Kotur 		break;
27390f90624SVenkat Duvvuru 	default:
27490f90624SVenkat Duvvuru 		netdev_err(bp->dev, "%s: Unsupported pedit hdr type\n",
27590f90624SVenkat Duvvuru 			   __func__);
27690f90624SVenkat Duvvuru 		return -EINVAL;
27790f90624SVenkat Duvvuru 	}
27890f90624SVenkat Duvvuru 	return 0;
27990f90624SVenkat Duvvuru }
28090f90624SVenkat Duvvuru 
bnxt_tc_parse_actions(struct bnxt * bp,struct bnxt_tc_actions * actions,struct flow_action * flow_action,struct netlink_ext_ack * extack)2812ae7408fSSathya Perla static int bnxt_tc_parse_actions(struct bnxt *bp,
2822ae7408fSSathya Perla 				 struct bnxt_tc_actions *actions,
283319a1d19SJiri Pirko 				 struct flow_action *flow_action,
284319a1d19SJiri Pirko 				 struct netlink_ext_ack *extack)
2852ae7408fSSathya Perla {
28690f90624SVenkat Duvvuru 	/* Used to store the L2 rewrite mask for dmac (6 bytes) followed by
28790f90624SVenkat Duvvuru 	 * smac (6 bytes) if rewrite of both is specified, otherwise either
28890f90624SVenkat Duvvuru 	 * dmac or smac
28990f90624SVenkat Duvvuru 	 */
29090f90624SVenkat Duvvuru 	u16 eth_addr_mask[ETH_ALEN] = { 0 };
29190f90624SVenkat Duvvuru 	/* Used to store the L2 rewrite key for dmac (6 bytes) followed by
29290f90624SVenkat Duvvuru 	 * smac (6 bytes) if rewrite of both is specified, otherwise either
29390f90624SVenkat Duvvuru 	 * dmac or smac
29490f90624SVenkat Duvvuru 	 */
29590f90624SVenkat Duvvuru 	u16 eth_addr[ETH_ALEN] = { 0 };
29673867881SPablo Neira Ayuso 	struct flow_action_entry *act;
297244cd96aSCong Wang 	int i, rc;
2982ae7408fSSathya Perla 
29973867881SPablo Neira Ayuso 	if (!flow_action_has_entries(flow_action)) {
3009a005c38SJonathan Lemon 		netdev_info(bp->dev, "no actions\n");
3012ae7408fSSathya Perla 		return -EINVAL;
3022ae7408fSSathya Perla 	}
3032ae7408fSSathya Perla 
30453eca1f3SJakub Kicinski 	if (!flow_action_basic_hw_stats_check(flow_action, extack))
305319a1d19SJiri Pirko 		return -EOPNOTSUPP;
306319a1d19SJiri Pirko 
30773867881SPablo Neira Ayuso 	flow_action_for_each(i, act, flow_action) {
30873867881SPablo Neira Ayuso 		switch (act->id) {
30973867881SPablo Neira Ayuso 		case FLOW_ACTION_DROP:
3102ae7408fSSathya Perla 			actions->flags |= BNXT_TC_ACTION_FLAG_DROP;
3112ae7408fSSathya Perla 			return 0; /* don't bother with other actions */
31273867881SPablo Neira Ayuso 		case FLOW_ACTION_REDIRECT:
31373867881SPablo Neira Ayuso 			rc = bnxt_tc_parse_redir(bp, actions, act);
3142ae7408fSSathya Perla 			if (rc)
3152ae7408fSSathya Perla 				return rc;
31673867881SPablo Neira Ayuso 			break;
31773867881SPablo Neira Ayuso 		case FLOW_ACTION_VLAN_POP:
31873867881SPablo Neira Ayuso 		case FLOW_ACTION_VLAN_PUSH:
31973867881SPablo Neira Ayuso 		case FLOW_ACTION_VLAN_MANGLE:
32073867881SPablo Neira Ayuso 			rc = bnxt_tc_parse_vlan(bp, actions, act);
3218c6ec361SDavide Caratti 			if (rc)
3228c6ec361SDavide Caratti 				return rc;
32373867881SPablo Neira Ayuso 			break;
32473867881SPablo Neira Ayuso 		case FLOW_ACTION_TUNNEL_ENCAP:
32573867881SPablo Neira Ayuso 			rc = bnxt_tc_parse_tunnel_set(bp, actions, act);
3268c95f773SSathya Perla 			if (rc)
3278c95f773SSathya Perla 				return rc;
32873867881SPablo Neira Ayuso 			break;
32973867881SPablo Neira Ayuso 		case FLOW_ACTION_TUNNEL_DECAP:
3308c95f773SSathya Perla 			actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_DECAP;
33173867881SPablo Neira Ayuso 			break;
33290f90624SVenkat Duvvuru 		/* Packet edit: L2 rewrite, NAT, NAPT */
33390f90624SVenkat Duvvuru 		case FLOW_ACTION_MANGLE:
3349b9eb518SSomnath Kotur 			rc = bnxt_tc_parse_pedit(bp, actions, act, i,
33590f90624SVenkat Duvvuru 						 (u8 *)eth_addr,
33690f90624SVenkat Duvvuru 						 (u8 *)eth_addr_mask);
33790f90624SVenkat Duvvuru 			if (rc)
33890f90624SVenkat Duvvuru 				return rc;
33990f90624SVenkat Duvvuru 			break;
34073867881SPablo Neira Ayuso 		default:
34173867881SPablo Neira Ayuso 			break;
3428c95f773SSathya Perla 		}
3438c95f773SSathya Perla 	}
3448c95f773SSathya Perla 
34590f90624SVenkat Duvvuru 	if (actions->flags & BNXT_TC_ACTION_FLAG_L2_REWRITE) {
34690f90624SVenkat Duvvuru 		rc = bnxt_fill_l2_rewrite_fields(actions, eth_addr,
34790f90624SVenkat Duvvuru 						 eth_addr_mask);
34890f90624SVenkat Duvvuru 		if (rc)
34990f90624SVenkat Duvvuru 			return rc;
35090f90624SVenkat Duvvuru 	}
35190f90624SVenkat Duvvuru 
352e9ecc731SSathya Perla 	if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) {
353e9ecc731SSathya Perla 		if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) {
354e9ecc731SSathya Perla 			/* dst_fid is PF's fid */
355e9ecc731SSathya Perla 			actions->dst_fid = bp->pf.fw_fid;
356e9ecc731SSathya Perla 		} else {
357e9ecc731SSathya Perla 			/* find the FID from dst_dev */
358e9ecc731SSathya Perla 			actions->dst_fid =
359e9ecc731SSathya Perla 				bnxt_flow_get_dst_fid(bp, actions->dst_dev);
360e9ecc731SSathya Perla 			if (actions->dst_fid == BNXT_FID_INVALID)
3618c95f773SSathya Perla 				return -EINVAL;
3628c95f773SSathya Perla 		}
363e9ecc731SSathya Perla 	}
3648c95f773SSathya Perla 
36592425c40SDan Carpenter 	return 0;
3662ae7408fSSathya Perla }
3672ae7408fSSathya Perla 
bnxt_tc_parse_flow(struct bnxt * bp,struct flow_cls_offload * tc_flow_cmd,struct bnxt_tc_flow * flow)3682ae7408fSSathya Perla static int bnxt_tc_parse_flow(struct bnxt *bp,
369f9e30088SPablo Neira Ayuso 			      struct flow_cls_offload *tc_flow_cmd,
3702ae7408fSSathya Perla 			      struct bnxt_tc_flow *flow)
3712ae7408fSSathya Perla {
372f9e30088SPablo Neira Ayuso 	struct flow_rule *rule = flow_cls_offload_flow_rule(tc_flow_cmd);
3738f256622SPablo Neira Ayuso 	struct flow_dissector *dissector = rule->match.dissector;
3742ae7408fSSathya Perla 
3752ae7408fSSathya Perla 	/* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */
3762b3082c6SRatheesh Kannoth 	if ((dissector->used_keys & BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL)) == 0 ||
3772b3082c6SRatheesh Kannoth 	    (dissector->used_keys & BIT_ULL(FLOW_DISSECTOR_KEY_BASIC)) == 0) {
3782b3082c6SRatheesh Kannoth 		netdev_info(bp->dev, "cannot form TC key: used_keys = 0x%llx\n",
3792ae7408fSSathya Perla 			    dissector->used_keys);
3802ae7408fSSathya Perla 		return -EOPNOTSUPP;
3812ae7408fSSathya Perla 	}
3822ae7408fSSathya Perla 
3838f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
3848f256622SPablo Neira Ayuso 		struct flow_match_basic match;
3852ae7408fSSathya Perla 
3868f256622SPablo Neira Ayuso 		flow_rule_match_basic(rule, &match);
3878f256622SPablo Neira Ayuso 		flow->l2_key.ether_type = match.key->n_proto;
3888f256622SPablo Neira Ayuso 		flow->l2_mask.ether_type = match.mask->n_proto;
3892ae7408fSSathya Perla 
3908f256622SPablo Neira Ayuso 		if (match.key->n_proto == htons(ETH_P_IP) ||
3918f256622SPablo Neira Ayuso 		    match.key->n_proto == htons(ETH_P_IPV6)) {
3928f256622SPablo Neira Ayuso 			flow->l4_key.ip_proto = match.key->ip_proto;
3938f256622SPablo Neira Ayuso 			flow->l4_mask.ip_proto = match.mask->ip_proto;
3942ae7408fSSathya Perla 		}
3952ae7408fSSathya Perla 	}
3962ae7408fSSathya Perla 
3978f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
3988f256622SPablo Neira Ayuso 		struct flow_match_eth_addrs match;
3992ae7408fSSathya Perla 
4008f256622SPablo Neira Ayuso 		flow_rule_match_eth_addrs(rule, &match);
4012ae7408fSSathya Perla 		flow->flags |= BNXT_TC_FLOW_FLAGS_ETH_ADDRS;
4028f256622SPablo Neira Ayuso 		ether_addr_copy(flow->l2_key.dmac, match.key->dst);
4038f256622SPablo Neira Ayuso 		ether_addr_copy(flow->l2_mask.dmac, match.mask->dst);
4048f256622SPablo Neira Ayuso 		ether_addr_copy(flow->l2_key.smac, match.key->src);
4058f256622SPablo Neira Ayuso 		ether_addr_copy(flow->l2_mask.smac, match.mask->src);
4062ae7408fSSathya Perla 	}
4072ae7408fSSathya Perla 
4088f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
4098f256622SPablo Neira Ayuso 		struct flow_match_vlan match;
4102ae7408fSSathya Perla 
4118f256622SPablo Neira Ayuso 		flow_rule_match_vlan(rule, &match);
4122ae7408fSSathya Perla 		flow->l2_key.inner_vlan_tci =
4138f256622SPablo Neira Ayuso 			cpu_to_be16(VLAN_TCI(match.key->vlan_id,
4148f256622SPablo Neira Ayuso 					     match.key->vlan_priority));
4152ae7408fSSathya Perla 		flow->l2_mask.inner_vlan_tci =
4168f256622SPablo Neira Ayuso 			cpu_to_be16((VLAN_TCI(match.mask->vlan_id,
4178f256622SPablo Neira Ayuso 					      match.mask->vlan_priority)));
4182ae7408fSSathya Perla 		flow->l2_key.inner_vlan_tpid = htons(ETH_P_8021Q);
4192ae7408fSSathya Perla 		flow->l2_mask.inner_vlan_tpid = htons(0xffff);
4202ae7408fSSathya Perla 		flow->l2_key.num_vlans = 1;
4212ae7408fSSathya Perla 	}
4222ae7408fSSathya Perla 
4238f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
4248f256622SPablo Neira Ayuso 		struct flow_match_ipv4_addrs match;
4252ae7408fSSathya Perla 
4268f256622SPablo Neira Ayuso 		flow_rule_match_ipv4_addrs(rule, &match);
4272ae7408fSSathya Perla 		flow->flags |= BNXT_TC_FLOW_FLAGS_IPV4_ADDRS;
4288f256622SPablo Neira Ayuso 		flow->l3_key.ipv4.daddr.s_addr = match.key->dst;
4298f256622SPablo Neira Ayuso 		flow->l3_mask.ipv4.daddr.s_addr = match.mask->dst;
4308f256622SPablo Neira Ayuso 		flow->l3_key.ipv4.saddr.s_addr = match.key->src;
4318f256622SPablo Neira Ayuso 		flow->l3_mask.ipv4.saddr.s_addr = match.mask->src;
4328f256622SPablo Neira Ayuso 	} else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
4338f256622SPablo Neira Ayuso 		struct flow_match_ipv6_addrs match;
4342ae7408fSSathya Perla 
4358f256622SPablo Neira Ayuso 		flow_rule_match_ipv6_addrs(rule, &match);
4362ae7408fSSathya Perla 		flow->flags |= BNXT_TC_FLOW_FLAGS_IPV6_ADDRS;
4378f256622SPablo Neira Ayuso 		flow->l3_key.ipv6.daddr = match.key->dst;
4388f256622SPablo Neira Ayuso 		flow->l3_mask.ipv6.daddr = match.mask->dst;
4398f256622SPablo Neira Ayuso 		flow->l3_key.ipv6.saddr = match.key->src;
4408f256622SPablo Neira Ayuso 		flow->l3_mask.ipv6.saddr = match.mask->src;
4412ae7408fSSathya Perla 	}
4422ae7408fSSathya Perla 
4438f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
4448f256622SPablo Neira Ayuso 		struct flow_match_ports match;
4452ae7408fSSathya Perla 
4468f256622SPablo Neira Ayuso 		flow_rule_match_ports(rule, &match);
4472ae7408fSSathya Perla 		flow->flags |= BNXT_TC_FLOW_FLAGS_PORTS;
4488f256622SPablo Neira Ayuso 		flow->l4_key.ports.dport = match.key->dst;
4498f256622SPablo Neira Ayuso 		flow->l4_mask.ports.dport = match.mask->dst;
4508f256622SPablo Neira Ayuso 		flow->l4_key.ports.sport = match.key->src;
4518f256622SPablo Neira Ayuso 		flow->l4_mask.ports.sport = match.mask->src;
4522ae7408fSSathya Perla 	}
4532ae7408fSSathya Perla 
4548f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ICMP)) {
4558f256622SPablo Neira Ayuso 		struct flow_match_icmp match;
4562ae7408fSSathya Perla 
4578f256622SPablo Neira Ayuso 		flow_rule_match_icmp(rule, &match);
4582ae7408fSSathya Perla 		flow->flags |= BNXT_TC_FLOW_FLAGS_ICMP;
4598f256622SPablo Neira Ayuso 		flow->l4_key.icmp.type = match.key->type;
4608f256622SPablo Neira Ayuso 		flow->l4_key.icmp.code = match.key->code;
4618f256622SPablo Neira Ayuso 		flow->l4_mask.icmp.type = match.mask->type;
4628f256622SPablo Neira Ayuso 		flow->l4_mask.icmp.code = match.mask->code;
4632ae7408fSSathya Perla 	}
4642ae7408fSSathya Perla 
4658f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) {
4668f256622SPablo Neira Ayuso 		struct flow_match_ipv4_addrs match;
4678c95f773SSathya Perla 
4688f256622SPablo Neira Ayuso 		flow_rule_match_enc_ipv4_addrs(rule, &match);
4698c95f773SSathya Perla 		flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS;
4708f256622SPablo Neira Ayuso 		flow->tun_key.u.ipv4.dst = match.key->dst;
4718f256622SPablo Neira Ayuso 		flow->tun_mask.u.ipv4.dst = match.mask->dst;
4728f256622SPablo Neira Ayuso 		flow->tun_key.u.ipv4.src = match.key->src;
4738f256622SPablo Neira Ayuso 		flow->tun_mask.u.ipv4.src = match.mask->src;
4748f256622SPablo Neira Ayuso 	} else if (flow_rule_match_key(rule,
4758c95f773SSathya Perla 				      FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) {
4768c95f773SSathya Perla 		return -EOPNOTSUPP;
4778c95f773SSathya Perla 	}
4788c95f773SSathya Perla 
4798f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
4808f256622SPablo Neira Ayuso 		struct flow_match_enc_keyid match;
4818c95f773SSathya Perla 
4828f256622SPablo Neira Ayuso 		flow_rule_match_enc_keyid(rule, &match);
4838c95f773SSathya Perla 		flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ID;
4848f256622SPablo Neira Ayuso 		flow->tun_key.tun_id = key32_to_tunnel_id(match.key->keyid);
4858f256622SPablo Neira Ayuso 		flow->tun_mask.tun_id = key32_to_tunnel_id(match.mask->keyid);
4868c95f773SSathya Perla 	}
4878c95f773SSathya Perla 
4888f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS)) {
4898f256622SPablo Neira Ayuso 		struct flow_match_ports match;
4908c95f773SSathya Perla 
4918f256622SPablo Neira Ayuso 		flow_rule_match_enc_ports(rule, &match);
4928c95f773SSathya Perla 		flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_PORTS;
4938f256622SPablo Neira Ayuso 		flow->tun_key.tp_dst = match.key->dst;
4948f256622SPablo Neira Ayuso 		flow->tun_mask.tp_dst = match.mask->dst;
4958f256622SPablo Neira Ayuso 		flow->tun_key.tp_src = match.key->src;
4968f256622SPablo Neira Ayuso 		flow->tun_mask.tp_src = match.mask->src;
4978c95f773SSathya Perla 	}
4988c95f773SSathya Perla 
499319a1d19SJiri Pirko 	return bnxt_tc_parse_actions(bp, &flow->actions, &rule->action,
500319a1d19SJiri Pirko 				     tc_flow_cmd->common.extack);
5012ae7408fSSathya Perla }
5022ae7408fSSathya Perla 
bnxt_hwrm_cfa_flow_free(struct bnxt * bp,struct bnxt_tc_flow_node * flow_node)503abd43a13SVenkat Duvvuru static int bnxt_hwrm_cfa_flow_free(struct bnxt *bp,
504abd43a13SVenkat Duvvuru 				   struct bnxt_tc_flow_node *flow_node)
5052ae7408fSSathya Perla {
506bbf33d1dSEdwin Peer 	struct hwrm_cfa_flow_free_input *req;
507db1d36a2SSathya Perla 	int rc;
508db1d36a2SSathya Perla 
509bbf33d1dSEdwin Peer 	rc = hwrm_req_init(bp, req, HWRM_CFA_FLOW_FREE);
510bbf33d1dSEdwin Peer 	if (!rc) {
511abd43a13SVenkat Duvvuru 		if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE)
512bbf33d1dSEdwin Peer 			req->ext_flow_handle = flow_node->ext_flow_handle;
513abd43a13SVenkat Duvvuru 		else
514bbf33d1dSEdwin Peer 			req->flow_handle = flow_node->flow_handle;
515db1d36a2SSathya Perla 
516bbf33d1dSEdwin Peer 		rc = hwrm_req_send(bp, req);
517bbf33d1dSEdwin Peer 	}
518db1d36a2SSathya Perla 	if (rc)
5199a005c38SJonathan Lemon 		netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc);
5206ae777eaSVenkat Duvvuru 
521db1d36a2SSathya Perla 	return rc;
522db1d36a2SSathya Perla }
523db1d36a2SSathya Perla 
ipv6_mask_len(struct in6_addr * mask)524db1d36a2SSathya Perla static int ipv6_mask_len(struct in6_addr *mask)
525db1d36a2SSathya Perla {
526db1d36a2SSathya Perla 	int mask_len = 0, i;
527db1d36a2SSathya Perla 
528db1d36a2SSathya Perla 	for (i = 0; i < 4; i++)
529db1d36a2SSathya Perla 		mask_len += inet_mask_len(mask->s6_addr32[i]);
530db1d36a2SSathya Perla 
531db1d36a2SSathya Perla 	return mask_len;
532db1d36a2SSathya Perla }
533db1d36a2SSathya Perla 
is_wildcard(void * mask,int len)534db1d36a2SSathya Perla static bool is_wildcard(void *mask, int len)
535db1d36a2SSathya Perla {
536db1d36a2SSathya Perla 	const u8 *p = mask;
537db1d36a2SSathya Perla 	int i;
538db1d36a2SSathya Perla 
539db1d36a2SSathya Perla 	for (i = 0; i < len; i++) {
540db1d36a2SSathya Perla 		if (p[i] != 0)
541db1d36a2SSathya Perla 			return false;
542db1d36a2SSathya Perla 	}
543db1d36a2SSathya Perla 	return true;
5442ae7408fSSathya Perla }
5452ae7408fSSathya Perla 
is_exactmatch(void * mask,int len)546e85a9be9SAndy Gospodarek static bool is_exactmatch(void *mask, int len)
547e85a9be9SAndy Gospodarek {
548e85a9be9SAndy Gospodarek 	const u8 *p = mask;
549e85a9be9SAndy Gospodarek 	int i;
550e85a9be9SAndy Gospodarek 
551e85a9be9SAndy Gospodarek 	for (i = 0; i < len; i++)
552e85a9be9SAndy Gospodarek 		if (p[i] != 0xff)
553e85a9be9SAndy Gospodarek 			return false;
554e85a9be9SAndy Gospodarek 
555e85a9be9SAndy Gospodarek 	return true;
556e85a9be9SAndy Gospodarek }
557e85a9be9SAndy Gospodarek 
is_vlan_tci_allowed(__be16 vlan_tci_mask,__be16 vlan_tci)558e32d4e60SVenkat Duvvuru static bool is_vlan_tci_allowed(__be16  vlan_tci_mask,
559e32d4e60SVenkat Duvvuru 				__be16  vlan_tci)
560e32d4e60SVenkat Duvvuru {
561e32d4e60SVenkat Duvvuru 	/* VLAN priority must be either exactly zero or fully wildcarded and
562e32d4e60SVenkat Duvvuru 	 * VLAN id must be exact match.
563e32d4e60SVenkat Duvvuru 	 */
564e32d4e60SVenkat Duvvuru 	if (is_vid_exactmatch(vlan_tci_mask) &&
565e32d4e60SVenkat Duvvuru 	    ((is_vlan_pcp_exactmatch(vlan_tci_mask) &&
566e32d4e60SVenkat Duvvuru 	      is_vlan_pcp_zero(vlan_tci)) ||
567e32d4e60SVenkat Duvvuru 	     is_vlan_pcp_wildcarded(vlan_tci_mask)))
568e32d4e60SVenkat Duvvuru 		return true;
569e32d4e60SVenkat Duvvuru 
570e32d4e60SVenkat Duvvuru 	return false;
571e32d4e60SVenkat Duvvuru }
572e32d4e60SVenkat Duvvuru 
bits_set(void * key,int len)573e85a9be9SAndy Gospodarek static bool bits_set(void *key, int len)
574e85a9be9SAndy Gospodarek {
575e85a9be9SAndy Gospodarek 	const u8 *p = key;
576e85a9be9SAndy Gospodarek 	int i;
577e85a9be9SAndy Gospodarek 
578e85a9be9SAndy Gospodarek 	for (i = 0; i < len; i++)
579e85a9be9SAndy Gospodarek 		if (p[i] != 0)
580e85a9be9SAndy Gospodarek 			return true;
581e85a9be9SAndy Gospodarek 
582e85a9be9SAndy Gospodarek 	return false;
583e85a9be9SAndy Gospodarek }
584e85a9be9SAndy Gospodarek 
bnxt_hwrm_cfa_flow_alloc(struct bnxt * bp,struct bnxt_tc_flow * flow,__le16 ref_flow_handle,__le32 tunnel_handle,struct bnxt_tc_flow_node * flow_node)5852ae7408fSSathya Perla static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow,
5868c95f773SSathya Perla 				    __le16 ref_flow_handle,
587abd43a13SVenkat Duvvuru 				    __le32 tunnel_handle,
588abd43a13SVenkat Duvvuru 				    struct bnxt_tc_flow_node *flow_node)
5892ae7408fSSathya Perla {
590db1d36a2SSathya Perla 	struct bnxt_tc_actions *actions = &flow->actions;
591db1d36a2SSathya Perla 	struct bnxt_tc_l3_key *l3_mask = &flow->l3_mask;
592db1d36a2SSathya Perla 	struct bnxt_tc_l3_key *l3_key = &flow->l3_key;
5935c209fc8SVenkat Duvvuru 	struct hwrm_cfa_flow_alloc_output *resp;
594bbf33d1dSEdwin Peer 	struct hwrm_cfa_flow_alloc_input *req;
595db1d36a2SSathya Perla 	u16 flow_flags = 0, action_flags = 0;
596db1d36a2SSathya Perla 	int rc;
597db1d36a2SSathya Perla 
598bbf33d1dSEdwin Peer 	rc = hwrm_req_init(bp, req, HWRM_CFA_FLOW_ALLOC);
599bbf33d1dSEdwin Peer 	if (rc)
600bbf33d1dSEdwin Peer 		return rc;
601db1d36a2SSathya Perla 
602bbf33d1dSEdwin Peer 	req->src_fid = cpu_to_le16(flow->src_fid);
603bbf33d1dSEdwin Peer 	req->ref_flow_handle = ref_flow_handle;
6048c95f773SSathya Perla 
60590f90624SVenkat Duvvuru 	if (actions->flags & BNXT_TC_ACTION_FLAG_L2_REWRITE) {
606bbf33d1dSEdwin Peer 		memcpy(req->l2_rewrite_dmac, actions->l2_rewrite_dmac,
60790f90624SVenkat Duvvuru 		       ETH_ALEN);
608bbf33d1dSEdwin Peer 		memcpy(req->l2_rewrite_smac, actions->l2_rewrite_smac,
60990f90624SVenkat Duvvuru 		       ETH_ALEN);
61090f90624SVenkat Duvvuru 		action_flags |=
61190f90624SVenkat Duvvuru 			CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE;
61290f90624SVenkat Duvvuru 	}
61390f90624SVenkat Duvvuru 
6149b9eb518SSomnath Kotur 	if (actions->flags & BNXT_TC_ACTION_FLAG_NAT_XLATE) {
6159b9eb518SSomnath Kotur 		if (actions->nat.l3_is_ipv4) {
6169b9eb518SSomnath Kotur 			action_flags |=
6179b9eb518SSomnath Kotur 				CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_IPV4_ADDRESS;
6189b9eb518SSomnath Kotur 
6199b9eb518SSomnath Kotur 			if (actions->nat.src_xlate) {
6209b9eb518SSomnath Kotur 				action_flags |=
6219b9eb518SSomnath Kotur 					CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC;
6229b9eb518SSomnath Kotur 				/* L3 source rewrite */
623bbf33d1dSEdwin Peer 				req->nat_ip_address[0] =
6249b9eb518SSomnath Kotur 					actions->nat.l3.ipv4.saddr.s_addr;
6259b9eb518SSomnath Kotur 				/* L4 source port */
6269b9eb518SSomnath Kotur 				if (actions->nat.l4.ports.sport)
627bbf33d1dSEdwin Peer 					req->nat_port =
6289b9eb518SSomnath Kotur 						actions->nat.l4.ports.sport;
6299b9eb518SSomnath Kotur 			} else {
6309b9eb518SSomnath Kotur 				action_flags |=
6319b9eb518SSomnath Kotur 					CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST;
6329b9eb518SSomnath Kotur 				/* L3 destination rewrite */
633bbf33d1dSEdwin Peer 				req->nat_ip_address[0] =
6349b9eb518SSomnath Kotur 					actions->nat.l3.ipv4.daddr.s_addr;
6359b9eb518SSomnath Kotur 				/* L4 destination port */
6369b9eb518SSomnath Kotur 				if (actions->nat.l4.ports.dport)
637bbf33d1dSEdwin Peer 					req->nat_port =
6389b9eb518SSomnath Kotur 						actions->nat.l4.ports.dport;
6399b9eb518SSomnath Kotur 			}
6409b9eb518SSomnath Kotur 			netdev_dbg(bp->dev,
641bbf33d1dSEdwin Peer 				   "req->nat_ip_address: %pI4 src_xlate: %d req->nat_port: %x\n",
642bbf33d1dSEdwin Peer 				   req->nat_ip_address, actions->nat.src_xlate,
643bbf33d1dSEdwin Peer 				   req->nat_port);
6449b9eb518SSomnath Kotur 		} else {
6459b9eb518SSomnath Kotur 			if (actions->nat.src_xlate) {
6469b9eb518SSomnath Kotur 				action_flags |=
6479b9eb518SSomnath Kotur 					CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC;
6489b9eb518SSomnath Kotur 				/* L3 source rewrite */
649bbf33d1dSEdwin Peer 				memcpy(req->nat_ip_address,
6509b9eb518SSomnath Kotur 				       actions->nat.l3.ipv6.saddr.s6_addr32,
651bbf33d1dSEdwin Peer 				       sizeof(req->nat_ip_address));
6529b9eb518SSomnath Kotur 				/* L4 source port */
6539b9eb518SSomnath Kotur 				if (actions->nat.l4.ports.sport)
654bbf33d1dSEdwin Peer 					req->nat_port =
6559b9eb518SSomnath Kotur 						actions->nat.l4.ports.sport;
6569b9eb518SSomnath Kotur 			} else {
6579b9eb518SSomnath Kotur 				action_flags |=
6589b9eb518SSomnath Kotur 					CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST;
6599b9eb518SSomnath Kotur 				/* L3 destination rewrite */
660bbf33d1dSEdwin Peer 				memcpy(req->nat_ip_address,
6619b9eb518SSomnath Kotur 				       actions->nat.l3.ipv6.daddr.s6_addr32,
662bbf33d1dSEdwin Peer 				       sizeof(req->nat_ip_address));
6639b9eb518SSomnath Kotur 				/* L4 destination port */
6649b9eb518SSomnath Kotur 				if (actions->nat.l4.ports.dport)
665bbf33d1dSEdwin Peer 					req->nat_port =
6669b9eb518SSomnath Kotur 						actions->nat.l4.ports.dport;
6679b9eb518SSomnath Kotur 			}
6689b9eb518SSomnath Kotur 			netdev_dbg(bp->dev,
669bbf33d1dSEdwin Peer 				   "req->nat_ip_address: %pI6 src_xlate: %d req->nat_port: %x\n",
670bbf33d1dSEdwin Peer 				   req->nat_ip_address, actions->nat.src_xlate,
671bbf33d1dSEdwin Peer 				   req->nat_port);
6729b9eb518SSomnath Kotur 		}
6739b9eb518SSomnath Kotur 	}
6749b9eb518SSomnath Kotur 
6758c95f773SSathya Perla 	if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP ||
6768c95f773SSathya Perla 	    actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) {
677bbf33d1dSEdwin Peer 		req->tunnel_handle = tunnel_handle;
6788c95f773SSathya Perla 		flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_TUNNEL;
6798c95f773SSathya Perla 		action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL;
6808c95f773SSathya Perla 	}
6818c95f773SSathya Perla 
682bbf33d1dSEdwin Peer 	req->ethertype = flow->l2_key.ether_type;
683bbf33d1dSEdwin Peer 	req->ip_proto = flow->l4_key.ip_proto;
684db1d36a2SSathya Perla 
685db1d36a2SSathya Perla 	if (flow->flags & BNXT_TC_FLOW_FLAGS_ETH_ADDRS) {
686bbf33d1dSEdwin Peer 		memcpy(req->dmac, flow->l2_key.dmac, ETH_ALEN);
687bbf33d1dSEdwin Peer 		memcpy(req->smac, flow->l2_key.smac, ETH_ALEN);
688db1d36a2SSathya Perla 	}
689db1d36a2SSathya Perla 
690db1d36a2SSathya Perla 	if (flow->l2_key.num_vlans > 0) {
691db1d36a2SSathya Perla 		flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_ONE;
692db1d36a2SSathya Perla 		/* FW expects the inner_vlan_tci value to be set
693db1d36a2SSathya Perla 		 * in outer_vlan_tci when num_vlans is 1 (which is
694db1d36a2SSathya Perla 		 * always the case in TC.)
695db1d36a2SSathya Perla 		 */
696bbf33d1dSEdwin Peer 		req->outer_vlan_tci = flow->l2_key.inner_vlan_tci;
697db1d36a2SSathya Perla 	}
698db1d36a2SSathya Perla 
699db1d36a2SSathya Perla 	/* If all IP and L4 fields are wildcarded then this is an L2 flow */
7007deea450SSunil Challa 	if (is_wildcard(l3_mask, sizeof(*l3_mask)) &&
701db1d36a2SSathya Perla 	    is_wildcard(&flow->l4_mask, sizeof(flow->l4_mask))) {
702db1d36a2SSathya Perla 		flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_L2;
703db1d36a2SSathya Perla 	} else {
704db1d36a2SSathya Perla 		flow_flags |= flow->l2_key.ether_type == htons(ETH_P_IP) ?
705db1d36a2SSathya Perla 				CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV4 :
706db1d36a2SSathya Perla 				CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6;
707db1d36a2SSathya Perla 
708db1d36a2SSathya Perla 		if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV4_ADDRS) {
709bbf33d1dSEdwin Peer 			req->ip_dst[0] = l3_key->ipv4.daddr.s_addr;
710bbf33d1dSEdwin Peer 			req->ip_dst_mask_len =
711db1d36a2SSathya Perla 				inet_mask_len(l3_mask->ipv4.daddr.s_addr);
712bbf33d1dSEdwin Peer 			req->ip_src[0] = l3_key->ipv4.saddr.s_addr;
713bbf33d1dSEdwin Peer 			req->ip_src_mask_len =
714db1d36a2SSathya Perla 				inet_mask_len(l3_mask->ipv4.saddr.s_addr);
715db1d36a2SSathya Perla 		} else if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV6_ADDRS) {
716bbf33d1dSEdwin Peer 			memcpy(req->ip_dst, l3_key->ipv6.daddr.s6_addr32,
717bbf33d1dSEdwin Peer 			       sizeof(req->ip_dst));
718bbf33d1dSEdwin Peer 			req->ip_dst_mask_len =
719db1d36a2SSathya Perla 					ipv6_mask_len(&l3_mask->ipv6.daddr);
720bbf33d1dSEdwin Peer 			memcpy(req->ip_src, l3_key->ipv6.saddr.s6_addr32,
721bbf33d1dSEdwin Peer 			       sizeof(req->ip_src));
722bbf33d1dSEdwin Peer 			req->ip_src_mask_len =
723db1d36a2SSathya Perla 					ipv6_mask_len(&l3_mask->ipv6.saddr);
724db1d36a2SSathya Perla 		}
725db1d36a2SSathya Perla 	}
726db1d36a2SSathya Perla 
727db1d36a2SSathya Perla 	if (flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) {
728bbf33d1dSEdwin Peer 		req->l4_src_port = flow->l4_key.ports.sport;
729bbf33d1dSEdwin Peer 		req->l4_src_port_mask = flow->l4_mask.ports.sport;
730bbf33d1dSEdwin Peer 		req->l4_dst_port = flow->l4_key.ports.dport;
731bbf33d1dSEdwin Peer 		req->l4_dst_port_mask = flow->l4_mask.ports.dport;
732db1d36a2SSathya Perla 	} else if (flow->flags & BNXT_TC_FLOW_FLAGS_ICMP) {
733db1d36a2SSathya Perla 		/* l4 ports serve as type/code when ip_proto is ICMP */
734bbf33d1dSEdwin Peer 		req->l4_src_port = htons(flow->l4_key.icmp.type);
735bbf33d1dSEdwin Peer 		req->l4_src_port_mask = htons(flow->l4_mask.icmp.type);
736bbf33d1dSEdwin Peer 		req->l4_dst_port = htons(flow->l4_key.icmp.code);
737bbf33d1dSEdwin Peer 		req->l4_dst_port_mask = htons(flow->l4_mask.icmp.code);
738db1d36a2SSathya Perla 	}
739bbf33d1dSEdwin Peer 	req->flags = cpu_to_le16(flow_flags);
740db1d36a2SSathya Perla 
741db1d36a2SSathya Perla 	if (actions->flags & BNXT_TC_ACTION_FLAG_DROP) {
742db1d36a2SSathya Perla 		action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP;
743db1d36a2SSathya Perla 	} else {
744db1d36a2SSathya Perla 		if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) {
745db1d36a2SSathya Perla 			action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD;
746bbf33d1dSEdwin Peer 			req->dst_fid = cpu_to_le16(actions->dst_fid);
747db1d36a2SSathya Perla 		}
748db1d36a2SSathya Perla 		if (actions->flags & BNXT_TC_ACTION_FLAG_PUSH_VLAN) {
749db1d36a2SSathya Perla 			action_flags |=
750db1d36a2SSathya Perla 			    CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE;
751bbf33d1dSEdwin Peer 			req->l2_rewrite_vlan_tpid = actions->push_vlan_tpid;
752bbf33d1dSEdwin Peer 			req->l2_rewrite_vlan_tci = actions->push_vlan_tci;
753bbf33d1dSEdwin Peer 			memcpy(&req->l2_rewrite_dmac, &req->dmac, ETH_ALEN);
754bbf33d1dSEdwin Peer 			memcpy(&req->l2_rewrite_smac, &req->smac, ETH_ALEN);
755db1d36a2SSathya Perla 		}
756db1d36a2SSathya Perla 		if (actions->flags & BNXT_TC_ACTION_FLAG_POP_VLAN) {
757db1d36a2SSathya Perla 			action_flags |=
758db1d36a2SSathya Perla 			    CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE;
759db1d36a2SSathya Perla 			/* Rewrite config with tpid = 0 implies vlan pop */
760bbf33d1dSEdwin Peer 			req->l2_rewrite_vlan_tpid = 0;
761bbf33d1dSEdwin Peer 			memcpy(&req->l2_rewrite_dmac, &req->dmac, ETH_ALEN);
762bbf33d1dSEdwin Peer 			memcpy(&req->l2_rewrite_smac, &req->smac, ETH_ALEN);
763db1d36a2SSathya Perla 		}
764db1d36a2SSathya Perla 	}
765bbf33d1dSEdwin Peer 	req->action_flags = cpu_to_le16(action_flags);
766db1d36a2SSathya Perla 
767bbf33d1dSEdwin Peer 	resp = hwrm_req_hold(bp, req);
768bbf33d1dSEdwin Peer 	rc = hwrm_req_send_silent(bp, req);
7695c209fc8SVenkat Duvvuru 	if (!rc) {
770abd43a13SVenkat Duvvuru 		/* CFA_FLOW_ALLOC response interpretation:
771abd43a13SVenkat Duvvuru 		 *		    fw with	     fw with
772abd43a13SVenkat Duvvuru 		 *		    16-bit	     64-bit
773abd43a13SVenkat Duvvuru 		 *		    flow handle      flow handle
774abd43a13SVenkat Duvvuru 		 *		    ===========	     ===========
775abd43a13SVenkat Duvvuru 		 * flow_handle      flow handle      flow context id
776abd43a13SVenkat Duvvuru 		 * ext_flow_handle  INVALID	     flow handle
777abd43a13SVenkat Duvvuru 		 * flow_id	    INVALID	     flow counter id
778abd43a13SVenkat Duvvuru 		 */
779abd43a13SVenkat Duvvuru 		flow_node->flow_handle = resp->flow_handle;
780abd43a13SVenkat Duvvuru 		if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) {
781abd43a13SVenkat Duvvuru 			flow_node->ext_flow_handle = resp->ext_flow_handle;
782abd43a13SVenkat Duvvuru 			flow_node->flow_id = resp->flow_id;
783abd43a13SVenkat Duvvuru 		}
7845c209fc8SVenkat Duvvuru 	}
785bbf33d1dSEdwin Peer 	hwrm_req_drop(bp, req);
786db1d36a2SSathya Perla 	return rc;
7872ae7408fSSathya Perla }
7882ae7408fSSathya Perla 
hwrm_cfa_decap_filter_alloc(struct bnxt * bp,struct bnxt_tc_flow * flow,struct bnxt_tc_l2_key * l2_info,__le32 ref_decap_handle,__le32 * decap_filter_handle)7898c95f773SSathya Perla static int hwrm_cfa_decap_filter_alloc(struct bnxt *bp,
7908c95f773SSathya Perla 				       struct bnxt_tc_flow *flow,
7918c95f773SSathya Perla 				       struct bnxt_tc_l2_key *l2_info,
7928c95f773SSathya Perla 				       __le32 ref_decap_handle,
7938c95f773SSathya Perla 				       __le32 *decap_filter_handle)
7948c95f773SSathya Perla {
7955c209fc8SVenkat Duvvuru 	struct hwrm_cfa_decap_filter_alloc_output *resp;
796f484f678SSathya Perla 	struct ip_tunnel_key *tun_key = &flow->tun_key;
797bbf33d1dSEdwin Peer 	struct hwrm_cfa_decap_filter_alloc_input *req;
798f484f678SSathya Perla 	u32 enables = 0;
799f484f678SSathya Perla 	int rc;
800f484f678SSathya Perla 
801bbf33d1dSEdwin Peer 	rc = hwrm_req_init(bp, req, HWRM_CFA_DECAP_FILTER_ALLOC);
802bbf33d1dSEdwin Peer 	if (rc)
803bbf33d1dSEdwin Peer 		goto exit;
804f484f678SSathya Perla 
805bbf33d1dSEdwin Peer 	req->flags = cpu_to_le32(CFA_DECAP_FILTER_ALLOC_REQ_FLAGS_OVS_TUNNEL);
806f484f678SSathya Perla 	enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE |
807f484f678SSathya Perla 		   CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL;
808bbf33d1dSEdwin Peer 	req->tunnel_type = CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN;
809bbf33d1dSEdwin Peer 	req->ip_protocol = CFA_DECAP_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP;
810f484f678SSathya Perla 
811f484f678SSathya Perla 	if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_ID) {
812f484f678SSathya Perla 		enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_TUNNEL_ID;
813f484f678SSathya Perla 		/* tunnel_id is wrongly defined in hsi defn. as __le32 */
814bbf33d1dSEdwin Peer 		req->tunnel_id = tunnel_id_to_key32(tun_key->tun_id);
815f484f678SSathya Perla 	}
816f484f678SSathya Perla 
817f484f678SSathya Perla 	if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS) {
818c8fb7b82SSunil Challa 		enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_MACADDR;
819bbf33d1dSEdwin Peer 		ether_addr_copy(req->dst_macaddr, l2_info->dmac);
820f484f678SSathya Perla 	}
821f484f678SSathya Perla 	if (l2_info->num_vlans) {
822f484f678SSathya Perla 		enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_T_IVLAN_VID;
823bbf33d1dSEdwin Peer 		req->t_ivlan_vid = l2_info->inner_vlan_tci;
824f484f678SSathya Perla 	}
825f484f678SSathya Perla 
826f484f678SSathya Perla 	enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE;
827bbf33d1dSEdwin Peer 	req->ethertype = htons(ETH_P_IP);
828f484f678SSathya Perla 
829f484f678SSathya Perla 	if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS) {
830f484f678SSathya Perla 		enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR |
831f484f678SSathya Perla 			   CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR |
832f484f678SSathya Perla 			   CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE;
833bbf33d1dSEdwin Peer 		req->ip_addr_type =
834bbf33d1dSEdwin Peer 			CFA_DECAP_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4;
835bbf33d1dSEdwin Peer 		req->dst_ipaddr[0] = tun_key->u.ipv4.dst;
836bbf33d1dSEdwin Peer 		req->src_ipaddr[0] = tun_key->u.ipv4.src;
837f484f678SSathya Perla 	}
838f484f678SSathya Perla 
839f484f678SSathya Perla 	if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_PORTS) {
840f484f678SSathya Perla 		enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_PORT;
841bbf33d1dSEdwin Peer 		req->dst_port = tun_key->tp_dst;
842f484f678SSathya Perla 	}
843f484f678SSathya Perla 
844f484f678SSathya Perla 	/* Eventhough the decap_handle returned by hwrm_cfa_decap_filter_alloc
845f484f678SSathya Perla 	 * is defined as __le32, l2_ctxt_ref_id is defined in HSI as __le16.
846f484f678SSathya Perla 	 */
847bbf33d1dSEdwin Peer 	req->l2_ctxt_ref_id = (__force __le16)ref_decap_handle;
848bbf33d1dSEdwin Peer 	req->enables = cpu_to_le32(enables);
849f484f678SSathya Perla 
850bbf33d1dSEdwin Peer 	resp = hwrm_req_hold(bp, req);
851bbf33d1dSEdwin Peer 	rc = hwrm_req_send_silent(bp, req);
852bbf33d1dSEdwin Peer 	if (!rc)
853f484f678SSathya Perla 		*decap_filter_handle = resp->decap_filter_id;
854bbf33d1dSEdwin Peer 	hwrm_req_drop(bp, req);
855bbf33d1dSEdwin Peer exit:
856bbf33d1dSEdwin Peer 	if (rc)
8579a005c38SJonathan Lemon 		netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc);
858f484f678SSathya Perla 
859f484f678SSathya Perla 	return rc;
8608c95f773SSathya Perla }
8618c95f773SSathya Perla 
hwrm_cfa_decap_filter_free(struct bnxt * bp,__le32 decap_filter_handle)8628c95f773SSathya Perla static int hwrm_cfa_decap_filter_free(struct bnxt *bp,
8638c95f773SSathya Perla 				      __le32 decap_filter_handle)
8648c95f773SSathya Perla {
865bbf33d1dSEdwin Peer 	struct hwrm_cfa_decap_filter_free_input *req;
866f484f678SSathya Perla 	int rc;
867f484f678SSathya Perla 
868bbf33d1dSEdwin Peer 	rc = hwrm_req_init(bp, req, HWRM_CFA_DECAP_FILTER_FREE);
869bbf33d1dSEdwin Peer 	if (!rc) {
870bbf33d1dSEdwin Peer 		req->decap_filter_id = decap_filter_handle;
871bbf33d1dSEdwin Peer 		rc = hwrm_req_send(bp, req);
872bbf33d1dSEdwin Peer 	}
873f484f678SSathya Perla 	if (rc)
8749a005c38SJonathan Lemon 		netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc);
8756ae777eaSVenkat Duvvuru 
876f484f678SSathya Perla 	return rc;
8778c95f773SSathya Perla }
8788c95f773SSathya Perla 
hwrm_cfa_encap_record_alloc(struct bnxt * bp,struct ip_tunnel_key * encap_key,struct bnxt_tc_l2_key * l2_info,__le32 * encap_record_handle)8798c95f773SSathya Perla static int hwrm_cfa_encap_record_alloc(struct bnxt *bp,
8808c95f773SSathya Perla 				       struct ip_tunnel_key *encap_key,
8818c95f773SSathya Perla 				       struct bnxt_tc_l2_key *l2_info,
8828c95f773SSathya Perla 				       __le32 *encap_record_handle)
8838c95f773SSathya Perla {
8845c209fc8SVenkat Duvvuru 	struct hwrm_cfa_encap_record_alloc_output *resp;
885bbf33d1dSEdwin Peer 	struct hwrm_cfa_encap_record_alloc_input *req;
886bbf33d1dSEdwin Peer 	struct hwrm_cfa_encap_data_vxlan *encap;
887bbf33d1dSEdwin Peer 	struct hwrm_vxlan_ipv4_hdr *encap_ipv4;
888f484f678SSathya Perla 	int rc;
889f484f678SSathya Perla 
890bbf33d1dSEdwin Peer 	rc = hwrm_req_init(bp, req, HWRM_CFA_ENCAP_RECORD_ALLOC);
891bbf33d1dSEdwin Peer 	if (rc)
892bbf33d1dSEdwin Peer 		goto exit;
893f484f678SSathya Perla 
894bbf33d1dSEdwin Peer 	encap = (struct hwrm_cfa_encap_data_vxlan *)&req->encap_data;
895bbf33d1dSEdwin Peer 	req->encap_type = CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_VXLAN;
896f484f678SSathya Perla 	ether_addr_copy(encap->dst_mac_addr, l2_info->dmac);
897f484f678SSathya Perla 	ether_addr_copy(encap->src_mac_addr, l2_info->smac);
898f484f678SSathya Perla 	if (l2_info->num_vlans) {
899f484f678SSathya Perla 		encap->num_vlan_tags = l2_info->num_vlans;
900f484f678SSathya Perla 		encap->ovlan_tci = l2_info->inner_vlan_tci;
901f484f678SSathya Perla 		encap->ovlan_tpid = l2_info->inner_vlan_tpid;
902f484f678SSathya Perla 	}
903f484f678SSathya Perla 
904bbf33d1dSEdwin Peer 	encap_ipv4 = (struct hwrm_vxlan_ipv4_hdr *)encap->l3;
905f484f678SSathya Perla 	encap_ipv4->ver_hlen = 4 << VXLAN_IPV4_HDR_VER_HLEN_VERSION_SFT;
906f484f678SSathya Perla 	encap_ipv4->ver_hlen |= 5 << VXLAN_IPV4_HDR_VER_HLEN_HEADER_LENGTH_SFT;
907f484f678SSathya Perla 	encap_ipv4->ttl = encap_key->ttl;
908f484f678SSathya Perla 
909f484f678SSathya Perla 	encap_ipv4->dest_ip_addr = encap_key->u.ipv4.dst;
910f484f678SSathya Perla 	encap_ipv4->src_ip_addr = encap_key->u.ipv4.src;
911f484f678SSathya Perla 	encap_ipv4->protocol = IPPROTO_UDP;
912f484f678SSathya Perla 
913f484f678SSathya Perla 	encap->dst_port = encap_key->tp_dst;
914f484f678SSathya Perla 	encap->vni = tunnel_id_to_key32(encap_key->tun_id);
915f484f678SSathya Perla 
916bbf33d1dSEdwin Peer 	resp = hwrm_req_hold(bp, req);
917bbf33d1dSEdwin Peer 	rc = hwrm_req_send_silent(bp, req);
918bbf33d1dSEdwin Peer 	if (!rc)
919f484f678SSathya Perla 		*encap_record_handle = resp->encap_record_id;
920bbf33d1dSEdwin Peer 	hwrm_req_drop(bp, req);
921bbf33d1dSEdwin Peer exit:
922bbf33d1dSEdwin Peer 	if (rc)
9239a005c38SJonathan Lemon 		netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc);
924f484f678SSathya Perla 
925f484f678SSathya Perla 	return rc;
9268c95f773SSathya Perla }
9278c95f773SSathya Perla 
hwrm_cfa_encap_record_free(struct bnxt * bp,__le32 encap_record_handle)9288c95f773SSathya Perla static int hwrm_cfa_encap_record_free(struct bnxt *bp,
9298c95f773SSathya Perla 				      __le32 encap_record_handle)
9308c95f773SSathya Perla {
931bbf33d1dSEdwin Peer 	struct hwrm_cfa_encap_record_free_input *req;
932f484f678SSathya Perla 	int rc;
933f484f678SSathya Perla 
934bbf33d1dSEdwin Peer 	rc = hwrm_req_init(bp, req, HWRM_CFA_ENCAP_RECORD_FREE);
935bbf33d1dSEdwin Peer 	if (!rc) {
936bbf33d1dSEdwin Peer 		req->encap_record_id = encap_record_handle;
937bbf33d1dSEdwin Peer 		rc = hwrm_req_send(bp, req);
938bbf33d1dSEdwin Peer 	}
939f484f678SSathya Perla 	if (rc)
9409a005c38SJonathan Lemon 		netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc);
9416ae777eaSVenkat Duvvuru 
942f484f678SSathya Perla 	return rc;
9438c95f773SSathya Perla }
9448c95f773SSathya Perla 
bnxt_tc_put_l2_node(struct bnxt * bp,struct bnxt_tc_flow_node * flow_node)9452ae7408fSSathya Perla static int bnxt_tc_put_l2_node(struct bnxt *bp,
9462ae7408fSSathya Perla 			       struct bnxt_tc_flow_node *flow_node)
9472ae7408fSSathya Perla {
9482ae7408fSSathya Perla 	struct bnxt_tc_l2_node *l2_node = flow_node->l2_node;
949cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
9502ae7408fSSathya Perla 	int rc;
9512ae7408fSSathya Perla 
9522ae7408fSSathya Perla 	/* remove flow_node from the L2 shared flow list */
9532ae7408fSSathya Perla 	list_del(&flow_node->l2_list_node);
9542ae7408fSSathya Perla 	if (--l2_node->refcount == 0) {
9552ae7408fSSathya Perla 		rc =  rhashtable_remove_fast(&tc_info->l2_table, &l2_node->node,
9562ae7408fSSathya Perla 					     tc_info->l2_ht_params);
9572ae7408fSSathya Perla 		if (rc)
9582ae7408fSSathya Perla 			netdev_err(bp->dev,
9599a005c38SJonathan Lemon 				   "Error: %s: rhashtable_remove_fast: %d\n",
9602ae7408fSSathya Perla 				   __func__, rc);
9612ae7408fSSathya Perla 		kfree_rcu(l2_node, rcu);
9622ae7408fSSathya Perla 	}
9632ae7408fSSathya Perla 	return 0;
9642ae7408fSSathya Perla }
9652ae7408fSSathya Perla 
9662ae7408fSSathya Perla static struct bnxt_tc_l2_node *
bnxt_tc_get_l2_node(struct bnxt * bp,struct rhashtable * l2_table,struct rhashtable_params ht_params,struct bnxt_tc_l2_key * l2_key)9672ae7408fSSathya Perla bnxt_tc_get_l2_node(struct bnxt *bp, struct rhashtable *l2_table,
9682ae7408fSSathya Perla 		    struct rhashtable_params ht_params,
9692ae7408fSSathya Perla 		    struct bnxt_tc_l2_key *l2_key)
9702ae7408fSSathya Perla {
9712ae7408fSSathya Perla 	struct bnxt_tc_l2_node *l2_node;
9722ae7408fSSathya Perla 	int rc;
9732ae7408fSSathya Perla 
9742ae7408fSSathya Perla 	l2_node = rhashtable_lookup_fast(l2_table, l2_key, ht_params);
9752ae7408fSSathya Perla 	if (!l2_node) {
9762ae7408fSSathya Perla 		l2_node = kzalloc(sizeof(*l2_node), GFP_KERNEL);
9772ae7408fSSathya Perla 		if (!l2_node) {
9782ae7408fSSathya Perla 			rc = -ENOMEM;
9792ae7408fSSathya Perla 			return NULL;
9802ae7408fSSathya Perla 		}
9812ae7408fSSathya Perla 
9822ae7408fSSathya Perla 		l2_node->key = *l2_key;
9832ae7408fSSathya Perla 		rc = rhashtable_insert_fast(l2_table, &l2_node->node,
9842ae7408fSSathya Perla 					    ht_params);
9852ae7408fSSathya Perla 		if (rc) {
9868c95f773SSathya Perla 			kfree_rcu(l2_node, rcu);
9872ae7408fSSathya Perla 			netdev_err(bp->dev,
9889a005c38SJonathan Lemon 				   "Error: %s: rhashtable_insert_fast: %d\n",
9892ae7408fSSathya Perla 				   __func__, rc);
9902ae7408fSSathya Perla 			return NULL;
9912ae7408fSSathya Perla 		}
9922ae7408fSSathya Perla 		INIT_LIST_HEAD(&l2_node->common_l2_flows);
9932ae7408fSSathya Perla 	}
9942ae7408fSSathya Perla 	return l2_node;
9952ae7408fSSathya Perla }
9962ae7408fSSathya Perla 
9972ae7408fSSathya Perla /* Get the ref_flow_handle for a flow by checking if there are any other
9982ae7408fSSathya Perla  * flows that share the same L2 key as this flow.
9992ae7408fSSathya Perla  */
10002ae7408fSSathya Perla static int
bnxt_tc_get_ref_flow_handle(struct bnxt * bp,struct bnxt_tc_flow * flow,struct bnxt_tc_flow_node * flow_node,__le16 * ref_flow_handle)10012ae7408fSSathya Perla bnxt_tc_get_ref_flow_handle(struct bnxt *bp, struct bnxt_tc_flow *flow,
10022ae7408fSSathya Perla 			    struct bnxt_tc_flow_node *flow_node,
10032ae7408fSSathya Perla 			    __le16 *ref_flow_handle)
10042ae7408fSSathya Perla {
1005cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
10062ae7408fSSathya Perla 	struct bnxt_tc_flow_node *ref_flow_node;
10072ae7408fSSathya Perla 	struct bnxt_tc_l2_node *l2_node;
10082ae7408fSSathya Perla 
10092ae7408fSSathya Perla 	l2_node = bnxt_tc_get_l2_node(bp, &tc_info->l2_table,
10102ae7408fSSathya Perla 				      tc_info->l2_ht_params,
10112ae7408fSSathya Perla 				      &flow->l2_key);
10122ae7408fSSathya Perla 	if (!l2_node)
10132ae7408fSSathya Perla 		return -1;
10142ae7408fSSathya Perla 
10152ae7408fSSathya Perla 	/* If any other flow is using this l2_node, use it's flow_handle
10162ae7408fSSathya Perla 	 * as the ref_flow_handle
10172ae7408fSSathya Perla 	 */
10182ae7408fSSathya Perla 	if (l2_node->refcount > 0) {
10192ae7408fSSathya Perla 		ref_flow_node = list_first_entry(&l2_node->common_l2_flows,
10202ae7408fSSathya Perla 						 struct bnxt_tc_flow_node,
10212ae7408fSSathya Perla 						 l2_list_node);
10222ae7408fSSathya Perla 		*ref_flow_handle = ref_flow_node->flow_handle;
10232ae7408fSSathya Perla 	} else {
10242ae7408fSSathya Perla 		*ref_flow_handle = cpu_to_le16(0xffff);
10252ae7408fSSathya Perla 	}
10262ae7408fSSathya Perla 
10272ae7408fSSathya Perla 	/* Insert the l2_node into the flow_node so that subsequent flows
10282ae7408fSSathya Perla 	 * with a matching l2 key can use the flow_handle of this flow
10292ae7408fSSathya Perla 	 * as their ref_flow_handle
10302ae7408fSSathya Perla 	 */
10312ae7408fSSathya Perla 	flow_node->l2_node = l2_node;
10322ae7408fSSathya Perla 	list_add(&flow_node->l2_list_node, &l2_node->common_l2_flows);
10332ae7408fSSathya Perla 	l2_node->refcount++;
10342ae7408fSSathya Perla 	return 0;
10352ae7408fSSathya Perla }
10362ae7408fSSathya Perla 
10372ae7408fSSathya Perla /* After the flow parsing is done, this routine is used for checking
10382ae7408fSSathya Perla  * if there are any aspects of the flow that prevent it from being
10392ae7408fSSathya Perla  * offloaded.
10402ae7408fSSathya Perla  */
bnxt_tc_can_offload(struct bnxt * bp,struct bnxt_tc_flow * flow)10412ae7408fSSathya Perla static bool bnxt_tc_can_offload(struct bnxt *bp, struct bnxt_tc_flow *flow)
10422ae7408fSSathya Perla {
10432ae7408fSSathya Perla 	/* If L4 ports are specified then ip_proto must be TCP or UDP */
10442ae7408fSSathya Perla 	if ((flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) &&
10452ae7408fSSathya Perla 	    (flow->l4_key.ip_proto != IPPROTO_TCP &&
10462ae7408fSSathya Perla 	     flow->l4_key.ip_proto != IPPROTO_UDP)) {
10479a005c38SJonathan Lemon 		netdev_info(bp->dev, "Cannot offload non-TCP/UDP (%d) ports\n",
10482ae7408fSSathya Perla 			    flow->l4_key.ip_proto);
10492ae7408fSSathya Perla 		return false;
10502ae7408fSSathya Perla 	}
10512ae7408fSSathya Perla 
1052e85a9be9SAndy Gospodarek 	/* Currently source/dest MAC cannot be partial wildcard  */
1053e85a9be9SAndy Gospodarek 	if (bits_set(&flow->l2_key.smac, sizeof(flow->l2_key.smac)) &&
1054e85a9be9SAndy Gospodarek 	    !is_exactmatch(flow->l2_mask.smac, sizeof(flow->l2_mask.smac))) {
1055e85a9be9SAndy Gospodarek 		netdev_info(bp->dev, "Wildcard match unsupported for Source MAC\n");
1056e85a9be9SAndy Gospodarek 		return false;
1057e85a9be9SAndy Gospodarek 	}
1058e85a9be9SAndy Gospodarek 	if (bits_set(&flow->l2_key.dmac, sizeof(flow->l2_key.dmac)) &&
1059e85a9be9SAndy Gospodarek 	    !is_exactmatch(&flow->l2_mask.dmac, sizeof(flow->l2_mask.dmac))) {
1060e85a9be9SAndy Gospodarek 		netdev_info(bp->dev, "Wildcard match unsupported for Dest MAC\n");
1061e85a9be9SAndy Gospodarek 		return false;
1062e85a9be9SAndy Gospodarek 	}
1063e85a9be9SAndy Gospodarek 
1064e85a9be9SAndy Gospodarek 	/* Currently VLAN fields cannot be partial wildcard */
1065e85a9be9SAndy Gospodarek 	if (bits_set(&flow->l2_key.inner_vlan_tci,
1066e85a9be9SAndy Gospodarek 		     sizeof(flow->l2_key.inner_vlan_tci)) &&
1067e32d4e60SVenkat Duvvuru 	    !is_vlan_tci_allowed(flow->l2_mask.inner_vlan_tci,
1068e32d4e60SVenkat Duvvuru 				 flow->l2_key.inner_vlan_tci)) {
1069e32d4e60SVenkat Duvvuru 		netdev_info(bp->dev, "Unsupported VLAN TCI\n");
1070e85a9be9SAndy Gospodarek 		return false;
1071e85a9be9SAndy Gospodarek 	}
1072e85a9be9SAndy Gospodarek 	if (bits_set(&flow->l2_key.inner_vlan_tpid,
1073e85a9be9SAndy Gospodarek 		     sizeof(flow->l2_key.inner_vlan_tpid)) &&
1074e85a9be9SAndy Gospodarek 	    !is_exactmatch(&flow->l2_mask.inner_vlan_tpid,
1075e85a9be9SAndy Gospodarek 			   sizeof(flow->l2_mask.inner_vlan_tpid))) {
1076e85a9be9SAndy Gospodarek 		netdev_info(bp->dev, "Wildcard match unsupported for VLAN TPID\n");
1077e85a9be9SAndy Gospodarek 		return false;
1078e85a9be9SAndy Gospodarek 	}
1079e85a9be9SAndy Gospodarek 
1080e85a9be9SAndy Gospodarek 	/* Currently Ethertype must be set */
1081e85a9be9SAndy Gospodarek 	if (!is_exactmatch(&flow->l2_mask.ether_type,
1082e85a9be9SAndy Gospodarek 			   sizeof(flow->l2_mask.ether_type))) {
1083e85a9be9SAndy Gospodarek 		netdev_info(bp->dev, "Wildcard match unsupported for Ethertype\n");
1084e85a9be9SAndy Gospodarek 		return false;
1085e85a9be9SAndy Gospodarek 	}
1086e85a9be9SAndy Gospodarek 
10872ae7408fSSathya Perla 	return true;
10882ae7408fSSathya Perla }
10892ae7408fSSathya Perla 
10908c95f773SSathya Perla /* Returns the final refcount of the node on success
10918c95f773SSathya Perla  * or a -ve error code on failure
10928c95f773SSathya Perla  */
bnxt_tc_put_tunnel_node(struct bnxt * bp,struct rhashtable * tunnel_table,struct rhashtable_params * ht_params,struct bnxt_tc_tunnel_node * tunnel_node)10938c95f773SSathya Perla static int bnxt_tc_put_tunnel_node(struct bnxt *bp,
10948c95f773SSathya Perla 				   struct rhashtable *tunnel_table,
10958c95f773SSathya Perla 				   struct rhashtable_params *ht_params,
10968c95f773SSathya Perla 				   struct bnxt_tc_tunnel_node *tunnel_node)
10978c95f773SSathya Perla {
10988c95f773SSathya Perla 	int rc;
10998c95f773SSathya Perla 
11008c95f773SSathya Perla 	if (--tunnel_node->refcount == 0) {
11018c95f773SSathya Perla 		rc =  rhashtable_remove_fast(tunnel_table, &tunnel_node->node,
11028c95f773SSathya Perla 					     *ht_params);
11038c95f773SSathya Perla 		if (rc) {
11049a005c38SJonathan Lemon 			netdev_err(bp->dev, "rhashtable_remove_fast rc=%d\n", rc);
11058c95f773SSathya Perla 			rc = -1;
11068c95f773SSathya Perla 		}
11078c95f773SSathya Perla 		kfree_rcu(tunnel_node, rcu);
11088c95f773SSathya Perla 		return rc;
11098c95f773SSathya Perla 	} else {
11108c95f773SSathya Perla 		return tunnel_node->refcount;
11118c95f773SSathya Perla 	}
11128c95f773SSathya Perla }
11138c95f773SSathya Perla 
11148c95f773SSathya Perla /* Get (or add) either encap or decap tunnel node from/to the supplied
11158c95f773SSathya Perla  * hash table.
11168c95f773SSathya Perla  */
11178c95f773SSathya Perla static struct bnxt_tc_tunnel_node *
bnxt_tc_get_tunnel_node(struct bnxt * bp,struct rhashtable * tunnel_table,struct rhashtable_params * ht_params,struct ip_tunnel_key * tun_key)11188c95f773SSathya Perla bnxt_tc_get_tunnel_node(struct bnxt *bp, struct rhashtable *tunnel_table,
11198c95f773SSathya Perla 			struct rhashtable_params *ht_params,
11208c95f773SSathya Perla 			struct ip_tunnel_key *tun_key)
11218c95f773SSathya Perla {
11228c95f773SSathya Perla 	struct bnxt_tc_tunnel_node *tunnel_node;
11238c95f773SSathya Perla 	int rc;
11248c95f773SSathya Perla 
11258c95f773SSathya Perla 	tunnel_node = rhashtable_lookup_fast(tunnel_table, tun_key, *ht_params);
11268c95f773SSathya Perla 	if (!tunnel_node) {
11278c95f773SSathya Perla 		tunnel_node = kzalloc(sizeof(*tunnel_node), GFP_KERNEL);
11288c95f773SSathya Perla 		if (!tunnel_node) {
11298c95f773SSathya Perla 			rc = -ENOMEM;
11308c95f773SSathya Perla 			goto err;
11318c95f773SSathya Perla 		}
11328c95f773SSathya Perla 
11338c95f773SSathya Perla 		tunnel_node->key = *tun_key;
11348c95f773SSathya Perla 		tunnel_node->tunnel_handle = INVALID_TUNNEL_HANDLE;
11358c95f773SSathya Perla 		rc = rhashtable_insert_fast(tunnel_table, &tunnel_node->node,
11368c95f773SSathya Perla 					    *ht_params);
11378c95f773SSathya Perla 		if (rc) {
11388c95f773SSathya Perla 			kfree_rcu(tunnel_node, rcu);
11398c95f773SSathya Perla 			goto err;
11408c95f773SSathya Perla 		}
11418c95f773SSathya Perla 	}
11428c95f773SSathya Perla 	tunnel_node->refcount++;
11438c95f773SSathya Perla 	return tunnel_node;
11448c95f773SSathya Perla err:
11459a005c38SJonathan Lemon 	netdev_info(bp->dev, "error rc=%d\n", rc);
11468c95f773SSathya Perla 	return NULL;
11478c95f773SSathya Perla }
11488c95f773SSathya Perla 
bnxt_tc_get_ref_decap_handle(struct bnxt * bp,struct bnxt_tc_flow * flow,struct bnxt_tc_l2_key * l2_key,struct bnxt_tc_flow_node * flow_node,__le32 * ref_decap_handle)11498c95f773SSathya Perla static int bnxt_tc_get_ref_decap_handle(struct bnxt *bp,
11508c95f773SSathya Perla 					struct bnxt_tc_flow *flow,
11518c95f773SSathya Perla 					struct bnxt_tc_l2_key *l2_key,
11528c95f773SSathya Perla 					struct bnxt_tc_flow_node *flow_node,
11538c95f773SSathya Perla 					__le32 *ref_decap_handle)
11548c95f773SSathya Perla {
1155cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
11568c95f773SSathya Perla 	struct bnxt_tc_flow_node *ref_flow_node;
11578c95f773SSathya Perla 	struct bnxt_tc_l2_node *decap_l2_node;
11588c95f773SSathya Perla 
11598c95f773SSathya Perla 	decap_l2_node = bnxt_tc_get_l2_node(bp, &tc_info->decap_l2_table,
11608c95f773SSathya Perla 					    tc_info->decap_l2_ht_params,
11618c95f773SSathya Perla 					    l2_key);
11628c95f773SSathya Perla 	if (!decap_l2_node)
11638c95f773SSathya Perla 		return -1;
11648c95f773SSathya Perla 
11658c95f773SSathya Perla 	/* If any other flow is using this decap_l2_node, use it's decap_handle
11668c95f773SSathya Perla 	 * as the ref_decap_handle
11678c95f773SSathya Perla 	 */
11688c95f773SSathya Perla 	if (decap_l2_node->refcount > 0) {
11698c95f773SSathya Perla 		ref_flow_node =
11708c95f773SSathya Perla 			list_first_entry(&decap_l2_node->common_l2_flows,
11718c95f773SSathya Perla 					 struct bnxt_tc_flow_node,
11728c95f773SSathya Perla 					 decap_l2_list_node);
11738c95f773SSathya Perla 		*ref_decap_handle = ref_flow_node->decap_node->tunnel_handle;
11748c95f773SSathya Perla 	} else {
11758c95f773SSathya Perla 		*ref_decap_handle = INVALID_TUNNEL_HANDLE;
11768c95f773SSathya Perla 	}
11778c95f773SSathya Perla 
11788c95f773SSathya Perla 	/* Insert the l2_node into the flow_node so that subsequent flows
11798c95f773SSathya Perla 	 * with a matching decap l2 key can use the decap_filter_handle of
11808c95f773SSathya Perla 	 * this flow as their ref_decap_handle
11818c95f773SSathya Perla 	 */
11828c95f773SSathya Perla 	flow_node->decap_l2_node = decap_l2_node;
11838c95f773SSathya Perla 	list_add(&flow_node->decap_l2_list_node,
11848c95f773SSathya Perla 		 &decap_l2_node->common_l2_flows);
11858c95f773SSathya Perla 	decap_l2_node->refcount++;
11868c95f773SSathya Perla 	return 0;
11878c95f773SSathya Perla }
11888c95f773SSathya Perla 
bnxt_tc_put_decap_l2_node(struct bnxt * bp,struct bnxt_tc_flow_node * flow_node)11898c95f773SSathya Perla static void bnxt_tc_put_decap_l2_node(struct bnxt *bp,
11908c95f773SSathya Perla 				      struct bnxt_tc_flow_node *flow_node)
11918c95f773SSathya Perla {
11928c95f773SSathya Perla 	struct bnxt_tc_l2_node *decap_l2_node = flow_node->decap_l2_node;
1193cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
11948c95f773SSathya Perla 	int rc;
11958c95f773SSathya Perla 
11968c95f773SSathya Perla 	/* remove flow_node from the decap L2 sharing flow list */
11978c95f773SSathya Perla 	list_del(&flow_node->decap_l2_list_node);
11988c95f773SSathya Perla 	if (--decap_l2_node->refcount == 0) {
11998c95f773SSathya Perla 		rc =  rhashtable_remove_fast(&tc_info->decap_l2_table,
12008c95f773SSathya Perla 					     &decap_l2_node->node,
12018c95f773SSathya Perla 					     tc_info->decap_l2_ht_params);
12028c95f773SSathya Perla 		if (rc)
12039a005c38SJonathan Lemon 			netdev_err(bp->dev, "rhashtable_remove_fast rc=%d\n", rc);
12048c95f773SSathya Perla 		kfree_rcu(decap_l2_node, rcu);
12058c95f773SSathya Perla 	}
12068c95f773SSathya Perla }
12078c95f773SSathya Perla 
bnxt_tc_put_decap_handle(struct bnxt * bp,struct bnxt_tc_flow_node * flow_node)12088c95f773SSathya Perla static void bnxt_tc_put_decap_handle(struct bnxt *bp,
12098c95f773SSathya Perla 				     struct bnxt_tc_flow_node *flow_node)
12108c95f773SSathya Perla {
12118c95f773SSathya Perla 	__le32 decap_handle = flow_node->decap_node->tunnel_handle;
1212cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
12138c95f773SSathya Perla 	int rc;
12148c95f773SSathya Perla 
12158c95f773SSathya Perla 	if (flow_node->decap_l2_node)
12168c95f773SSathya Perla 		bnxt_tc_put_decap_l2_node(bp, flow_node);
12178c95f773SSathya Perla 
12188c95f773SSathya Perla 	rc = bnxt_tc_put_tunnel_node(bp, &tc_info->decap_table,
12198c95f773SSathya Perla 				     &tc_info->decap_ht_params,
12208c95f773SSathya Perla 				     flow_node->decap_node);
12218c95f773SSathya Perla 	if (!rc && decap_handle != INVALID_TUNNEL_HANDLE)
12228c95f773SSathya Perla 		hwrm_cfa_decap_filter_free(bp, decap_handle);
12238c95f773SSathya Perla }
12248c95f773SSathya Perla 
bnxt_tc_resolve_tunnel_hdrs(struct bnxt * bp,struct ip_tunnel_key * tun_key,struct bnxt_tc_l2_key * l2_info)12258c95f773SSathya Perla static int bnxt_tc_resolve_tunnel_hdrs(struct bnxt *bp,
12268c95f773SSathya Perla 				       struct ip_tunnel_key *tun_key,
1227e9ecc731SSathya Perla 				       struct bnxt_tc_l2_key *l2_info)
12288c95f773SSathya Perla {
1229952c5719SMichael Chan #ifdef CONFIG_INET
1230e9ecc731SSathya Perla 	struct net_device *real_dst_dev = bp->dev;
12318c95f773SSathya Perla 	struct flowi4 flow = { {0} };
12328c95f773SSathya Perla 	struct net_device *dst_dev;
12338c95f773SSathya Perla 	struct neighbour *nbr;
12348c95f773SSathya Perla 	struct rtable *rt;
12358c95f773SSathya Perla 	int rc;
12368c95f773SSathya Perla 
12378c95f773SSathya Perla 	flow.flowi4_proto = IPPROTO_UDP;
12388c95f773SSathya Perla 	flow.fl4_dport = tun_key->tp_dst;
12398c95f773SSathya Perla 	flow.daddr = tun_key->u.ipv4.dst;
12408c95f773SSathya Perla 
12418c95f773SSathya Perla 	rt = ip_route_output_key(dev_net(real_dst_dev), &flow);
12428c95f773SSathya Perla 	if (IS_ERR(rt)) {
12439a005c38SJonathan Lemon 		netdev_info(bp->dev, "no route to %pI4b\n", &flow.daddr);
12448c95f773SSathya Perla 		return -EOPNOTSUPP;
12458c95f773SSathya Perla 	}
12468c95f773SSathya Perla 
12478c95f773SSathya Perla 	/* The route must either point to the real_dst_dev or a dst_dev that
12488c95f773SSathya Perla 	 * uses the real_dst_dev.
12498c95f773SSathya Perla 	 */
12508c95f773SSathya Perla 	dst_dev = rt->dst.dev;
12518c95f773SSathya Perla 	if (is_vlan_dev(dst_dev)) {
1252952c5719SMichael Chan #if IS_ENABLED(CONFIG_VLAN_8021Q)
12538c95f773SSathya Perla 		struct vlan_dev_priv *vlan = vlan_dev_priv(dst_dev);
12548c95f773SSathya Perla 
12558c95f773SSathya Perla 		if (vlan->real_dev != real_dst_dev) {
12568c95f773SSathya Perla 			netdev_info(bp->dev,
12579a005c38SJonathan Lemon 				    "dst_dev(%s) doesn't use PF-if(%s)\n",
12588c95f773SSathya Perla 				    netdev_name(dst_dev),
12598c95f773SSathya Perla 				    netdev_name(real_dst_dev));
12608c95f773SSathya Perla 			rc = -EOPNOTSUPP;
12618c95f773SSathya Perla 			goto put_rt;
12628c95f773SSathya Perla 		}
12638c95f773SSathya Perla 		l2_info->inner_vlan_tci = htons(vlan->vlan_id);
12648c95f773SSathya Perla 		l2_info->inner_vlan_tpid = vlan->vlan_proto;
12658c95f773SSathya Perla 		l2_info->num_vlans = 1;
1266952c5719SMichael Chan #endif
12678c95f773SSathya Perla 	} else if (dst_dev != real_dst_dev) {
12688c95f773SSathya Perla 		netdev_info(bp->dev,
12699a005c38SJonathan Lemon 			    "dst_dev(%s) for %pI4b is not PF-if(%s)\n",
12708c95f773SSathya Perla 			    netdev_name(dst_dev), &flow.daddr,
12718c95f773SSathya Perla 			    netdev_name(real_dst_dev));
12728c95f773SSathya Perla 		rc = -EOPNOTSUPP;
12738c95f773SSathya Perla 		goto put_rt;
12748c95f773SSathya Perla 	}
12758c95f773SSathya Perla 
12768c95f773SSathya Perla 	nbr = dst_neigh_lookup(&rt->dst, &flow.daddr);
12778c95f773SSathya Perla 	if (!nbr) {
12789a005c38SJonathan Lemon 		netdev_info(bp->dev, "can't lookup neighbor for %pI4b\n",
12798c95f773SSathya Perla 			    &flow.daddr);
12808c95f773SSathya Perla 		rc = -EOPNOTSUPP;
12818c95f773SSathya Perla 		goto put_rt;
12828c95f773SSathya Perla 	}
12838c95f773SSathya Perla 
12848c95f773SSathya Perla 	tun_key->u.ipv4.src = flow.saddr;
12858c95f773SSathya Perla 	tun_key->ttl = ip4_dst_hoplimit(&rt->dst);
12868c95f773SSathya Perla 	neigh_ha_snapshot(l2_info->dmac, nbr, dst_dev);
12878c95f773SSathya Perla 	ether_addr_copy(l2_info->smac, dst_dev->dev_addr);
12888c95f773SSathya Perla 	neigh_release(nbr);
12898c95f773SSathya Perla 	ip_rt_put(rt);
12908c95f773SSathya Perla 
12918c95f773SSathya Perla 	return 0;
12928c95f773SSathya Perla put_rt:
12938c95f773SSathya Perla 	ip_rt_put(rt);
12948c95f773SSathya Perla 	return rc;
1295952c5719SMichael Chan #else
1296952c5719SMichael Chan 	return -EOPNOTSUPP;
1297952c5719SMichael Chan #endif
12988c95f773SSathya Perla }
12998c95f773SSathya Perla 
bnxt_tc_get_decap_handle(struct bnxt * bp,struct bnxt_tc_flow * flow,struct bnxt_tc_flow_node * flow_node,__le32 * decap_filter_handle)13008c95f773SSathya Perla static int bnxt_tc_get_decap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow,
13018c95f773SSathya Perla 				    struct bnxt_tc_flow_node *flow_node,
13028c95f773SSathya Perla 				    __le32 *decap_filter_handle)
13038c95f773SSathya Perla {
13048c95f773SSathya Perla 	struct ip_tunnel_key *decap_key = &flow->tun_key;
1305cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
13068c95f773SSathya Perla 	struct bnxt_tc_l2_key l2_info = { {0} };
13078c95f773SSathya Perla 	struct bnxt_tc_tunnel_node *decap_node;
13088c95f773SSathya Perla 	struct ip_tunnel_key tun_key = { 0 };
13098c95f773SSathya Perla 	struct bnxt_tc_l2_key *decap_l2_info;
13108c95f773SSathya Perla 	__le32 ref_decap_handle;
13118c95f773SSathya Perla 	int rc;
13128c95f773SSathya Perla 
13138c95f773SSathya Perla 	/* Check if there's another flow using the same tunnel decap.
13148c95f773SSathya Perla 	 * If not, add this tunnel to the table and resolve the other
1315479ca3bfSSriharsha Basavapatna 	 * tunnel header fileds. Ignore src_port in the tunnel_key,
1316479ca3bfSSriharsha Basavapatna 	 * since it is not required for decap filters.
13178c95f773SSathya Perla 	 */
1318479ca3bfSSriharsha Basavapatna 	decap_key->tp_src = 0;
13198c95f773SSathya Perla 	decap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->decap_table,
13208c95f773SSathya Perla 					     &tc_info->decap_ht_params,
13218c95f773SSathya Perla 					     decap_key);
13228c95f773SSathya Perla 	if (!decap_node)
13238c95f773SSathya Perla 		return -ENOMEM;
13248c95f773SSathya Perla 
13258c95f773SSathya Perla 	flow_node->decap_node = decap_node;
13268c95f773SSathya Perla 
13278c95f773SSathya Perla 	if (decap_node->tunnel_handle != INVALID_TUNNEL_HANDLE)
13288c95f773SSathya Perla 		goto done;
13298c95f773SSathya Perla 
13308c95f773SSathya Perla 	/* Resolve the L2 fields for tunnel decap
13318c95f773SSathya Perla 	 * Resolve the route for remote vtep (saddr) of the decap key
13328c95f773SSathya Perla 	 * Find it's next-hop mac addrs
13338c95f773SSathya Perla 	 */
13348c95f773SSathya Perla 	tun_key.u.ipv4.dst = flow->tun_key.u.ipv4.src;
13358c95f773SSathya Perla 	tun_key.tp_dst = flow->tun_key.tp_dst;
1336e9ecc731SSathya Perla 	rc = bnxt_tc_resolve_tunnel_hdrs(bp, &tun_key, &l2_info);
13378c95f773SSathya Perla 	if (rc)
13388c95f773SSathya Perla 		goto put_decap;
13398c95f773SSathya Perla 
13408c95f773SSathya Perla 	decap_l2_info = &decap_node->l2_info;
1341c8fb7b82SSunil Challa 	/* decap smac is wildcarded */
13428c95f773SSathya Perla 	ether_addr_copy(decap_l2_info->dmac, l2_info.smac);
13438c95f773SSathya Perla 	if (l2_info.num_vlans) {
13448c95f773SSathya Perla 		decap_l2_info->num_vlans = l2_info.num_vlans;
13458c95f773SSathya Perla 		decap_l2_info->inner_vlan_tpid = l2_info.inner_vlan_tpid;
13468c95f773SSathya Perla 		decap_l2_info->inner_vlan_tci = l2_info.inner_vlan_tci;
13478c95f773SSathya Perla 	}
13488c95f773SSathya Perla 	flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS;
13498c95f773SSathya Perla 
13508c95f773SSathya Perla 	/* For getting a decap_filter_handle we first need to check if
13518c95f773SSathya Perla 	 * there are any other decap flows that share the same tunnel L2
13528c95f773SSathya Perla 	 * key and if so, pass that flow's decap_filter_handle as the
13538c95f773SSathya Perla 	 * ref_decap_handle for this flow.
13548c95f773SSathya Perla 	 */
13558c95f773SSathya Perla 	rc = bnxt_tc_get_ref_decap_handle(bp, flow, decap_l2_info, flow_node,
13568c95f773SSathya Perla 					  &ref_decap_handle);
13578c95f773SSathya Perla 	if (rc)
13588c95f773SSathya Perla 		goto put_decap;
13598c95f773SSathya Perla 
13608c95f773SSathya Perla 	/* Issue the hwrm cmd to allocate a decap filter handle */
13618c95f773SSathya Perla 	rc = hwrm_cfa_decap_filter_alloc(bp, flow, decap_l2_info,
13628c95f773SSathya Perla 					 ref_decap_handle,
13638c95f773SSathya Perla 					 &decap_node->tunnel_handle);
13648c95f773SSathya Perla 	if (rc)
13658c95f773SSathya Perla 		goto put_decap_l2;
13668c95f773SSathya Perla 
13678c95f773SSathya Perla done:
13688c95f773SSathya Perla 	*decap_filter_handle = decap_node->tunnel_handle;
13698c95f773SSathya Perla 	return 0;
13708c95f773SSathya Perla 
13718c95f773SSathya Perla put_decap_l2:
13728c95f773SSathya Perla 	bnxt_tc_put_decap_l2_node(bp, flow_node);
13738c95f773SSathya Perla put_decap:
13748c95f773SSathya Perla 	bnxt_tc_put_tunnel_node(bp, &tc_info->decap_table,
13758c95f773SSathya Perla 				&tc_info->decap_ht_params,
13768c95f773SSathya Perla 				flow_node->decap_node);
13778c95f773SSathya Perla 	return rc;
13788c95f773SSathya Perla }
13798c95f773SSathya Perla 
bnxt_tc_put_encap_handle(struct bnxt * bp,struct bnxt_tc_tunnel_node * encap_node)13808c95f773SSathya Perla static void bnxt_tc_put_encap_handle(struct bnxt *bp,
13818c95f773SSathya Perla 				     struct bnxt_tc_tunnel_node *encap_node)
13828c95f773SSathya Perla {
13838c95f773SSathya Perla 	__le32 encap_handle = encap_node->tunnel_handle;
1384cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
13858c95f773SSathya Perla 	int rc;
13868c95f773SSathya Perla 
13878c95f773SSathya Perla 	rc = bnxt_tc_put_tunnel_node(bp, &tc_info->encap_table,
13888c95f773SSathya Perla 				     &tc_info->encap_ht_params, encap_node);
13898c95f773SSathya Perla 	if (!rc && encap_handle != INVALID_TUNNEL_HANDLE)
13908c95f773SSathya Perla 		hwrm_cfa_encap_record_free(bp, encap_handle);
13918c95f773SSathya Perla }
13928c95f773SSathya Perla 
13938c95f773SSathya Perla /* Lookup the tunnel encap table and check if there's an encap_handle
13948c95f773SSathya Perla  * alloc'd already.
13958c95f773SSathya Perla  * If not, query L2 info via a route lookup and issue an encap_record_alloc
13968c95f773SSathya Perla  * cmd to FW.
13978c95f773SSathya Perla  */
bnxt_tc_get_encap_handle(struct bnxt * bp,struct bnxt_tc_flow * flow,struct bnxt_tc_flow_node * flow_node,__le32 * encap_handle)13988c95f773SSathya Perla static int bnxt_tc_get_encap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow,
13998c95f773SSathya Perla 				    struct bnxt_tc_flow_node *flow_node,
14008c95f773SSathya Perla 				    __le32 *encap_handle)
14018c95f773SSathya Perla {
14028c95f773SSathya Perla 	struct ip_tunnel_key *encap_key = &flow->actions.tun_encap_key;
1403cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
14048c95f773SSathya Perla 	struct bnxt_tc_tunnel_node *encap_node;
14058c95f773SSathya Perla 	int rc;
14068c95f773SSathya Perla 
14078c95f773SSathya Perla 	/* Check if there's another flow using the same tunnel encap.
14088c95f773SSathya Perla 	 * If not, add this tunnel to the table and resolve the other
14098c95f773SSathya Perla 	 * tunnel header fileds
14108c95f773SSathya Perla 	 */
14118c95f773SSathya Perla 	encap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->encap_table,
14128c95f773SSathya Perla 					     &tc_info->encap_ht_params,
14138c95f773SSathya Perla 					     encap_key);
14148c95f773SSathya Perla 	if (!encap_node)
14158c95f773SSathya Perla 		return -ENOMEM;
14168c95f773SSathya Perla 
14178c95f773SSathya Perla 	flow_node->encap_node = encap_node;
14188c95f773SSathya Perla 
14198c95f773SSathya Perla 	if (encap_node->tunnel_handle != INVALID_TUNNEL_HANDLE)
14208c95f773SSathya Perla 		goto done;
14218c95f773SSathya Perla 
1422e9ecc731SSathya Perla 	rc = bnxt_tc_resolve_tunnel_hdrs(bp, encap_key, &encap_node->l2_info);
14238c95f773SSathya Perla 	if (rc)
14248c95f773SSathya Perla 		goto put_encap;
14258c95f773SSathya Perla 
14268c95f773SSathya Perla 	/* Allocate a new tunnel encap record */
14278c95f773SSathya Perla 	rc = hwrm_cfa_encap_record_alloc(bp, encap_key, &encap_node->l2_info,
14288c95f773SSathya Perla 					 &encap_node->tunnel_handle);
14298c95f773SSathya Perla 	if (rc)
14308c95f773SSathya Perla 		goto put_encap;
14318c95f773SSathya Perla 
14328c95f773SSathya Perla done:
14338c95f773SSathya Perla 	*encap_handle = encap_node->tunnel_handle;
14348c95f773SSathya Perla 	return 0;
14358c95f773SSathya Perla 
14368c95f773SSathya Perla put_encap:
14378c95f773SSathya Perla 	bnxt_tc_put_tunnel_node(bp, &tc_info->encap_table,
14388c95f773SSathya Perla 				&tc_info->encap_ht_params, encap_node);
14398c95f773SSathya Perla 	return rc;
14408c95f773SSathya Perla }
14418c95f773SSathya Perla 
bnxt_tc_put_tunnel_handle(struct bnxt * bp,struct bnxt_tc_flow * flow,struct bnxt_tc_flow_node * flow_node)14428c95f773SSathya Perla static void bnxt_tc_put_tunnel_handle(struct bnxt *bp,
14438c95f773SSathya Perla 				      struct bnxt_tc_flow *flow,
14448c95f773SSathya Perla 				      struct bnxt_tc_flow_node *flow_node)
14458c95f773SSathya Perla {
14468c95f773SSathya Perla 	if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP)
14478c95f773SSathya Perla 		bnxt_tc_put_decap_handle(bp, flow_node);
14488c95f773SSathya Perla 	else if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP)
14498c95f773SSathya Perla 		bnxt_tc_put_encap_handle(bp, flow_node->encap_node);
14508c95f773SSathya Perla }
14518c95f773SSathya Perla 
bnxt_tc_get_tunnel_handle(struct bnxt * bp,struct bnxt_tc_flow * flow,struct bnxt_tc_flow_node * flow_node,__le32 * tunnel_handle)14528c95f773SSathya Perla static int bnxt_tc_get_tunnel_handle(struct bnxt *bp,
14538c95f773SSathya Perla 				     struct bnxt_tc_flow *flow,
14548c95f773SSathya Perla 				     struct bnxt_tc_flow_node *flow_node,
14558c95f773SSathya Perla 				     __le32 *tunnel_handle)
14568c95f773SSathya Perla {
14578c95f773SSathya Perla 	if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP)
14588c95f773SSathya Perla 		return bnxt_tc_get_decap_handle(bp, flow, flow_node,
14598c95f773SSathya Perla 						tunnel_handle);
14608c95f773SSathya Perla 	else if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP)
14618c95f773SSathya Perla 		return bnxt_tc_get_encap_handle(bp, flow, flow_node,
14628c95f773SSathya Perla 						tunnel_handle);
14638c95f773SSathya Perla 	else
14648c95f773SSathya Perla 		return 0;
14658c95f773SSathya Perla }
__bnxt_tc_del_flow(struct bnxt * bp,struct bnxt_tc_flow_node * flow_node)14662ae7408fSSathya Perla static int __bnxt_tc_del_flow(struct bnxt *bp,
14672ae7408fSSathya Perla 			      struct bnxt_tc_flow_node *flow_node)
14682ae7408fSSathya Perla {
1469cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
14702ae7408fSSathya Perla 	int rc;
14712ae7408fSSathya Perla 
14722ae7408fSSathya Perla 	/* send HWRM cmd to free the flow-id */
1473abd43a13SVenkat Duvvuru 	bnxt_hwrm_cfa_flow_free(bp, flow_node);
14742ae7408fSSathya Perla 
14752ae7408fSSathya Perla 	mutex_lock(&tc_info->lock);
14762ae7408fSSathya Perla 
14778c95f773SSathya Perla 	/* release references to any tunnel encap/decap nodes */
14788c95f773SSathya Perla 	bnxt_tc_put_tunnel_handle(bp, &flow_node->flow, flow_node);
14798c95f773SSathya Perla 
14802ae7408fSSathya Perla 	/* release reference to l2 node */
14812ae7408fSSathya Perla 	bnxt_tc_put_l2_node(bp, flow_node);
14822ae7408fSSathya Perla 
14832ae7408fSSathya Perla 	mutex_unlock(&tc_info->lock);
14842ae7408fSSathya Perla 
14852ae7408fSSathya Perla 	rc = rhashtable_remove_fast(&tc_info->flow_table, &flow_node->node,
14862ae7408fSSathya Perla 				    tc_info->flow_ht_params);
14872ae7408fSSathya Perla 	if (rc)
14889a005c38SJonathan Lemon 		netdev_err(bp->dev, "Error: %s: rhashtable_remove_fast rc=%d\n",
14892ae7408fSSathya Perla 			   __func__, rc);
14902ae7408fSSathya Perla 
14912ae7408fSSathya Perla 	kfree_rcu(flow_node, rcu);
14922ae7408fSSathya Perla 	return 0;
14932ae7408fSSathya Perla }
14942ae7408fSSathya Perla 
bnxt_tc_set_flow_dir(struct bnxt * bp,struct bnxt_tc_flow * flow,u16 src_fid)1495abd43a13SVenkat Duvvuru static void bnxt_tc_set_flow_dir(struct bnxt *bp, struct bnxt_tc_flow *flow,
1496abd43a13SVenkat Duvvuru 				 u16 src_fid)
1497abd43a13SVenkat Duvvuru {
14989bf46566SSomnath Kotur 	flow->l2_key.dir = (bp->pf.fw_fid == src_fid) ? BNXT_DIR_RX : BNXT_DIR_TX;
1499abd43a13SVenkat Duvvuru }
1500abd43a13SVenkat Duvvuru 
bnxt_tc_set_src_fid(struct bnxt * bp,struct bnxt_tc_flow * flow,u16 src_fid)1501e9ecc731SSathya Perla static void bnxt_tc_set_src_fid(struct bnxt *bp, struct bnxt_tc_flow *flow,
1502e9ecc731SSathya Perla 				u16 src_fid)
1503e9ecc731SSathya Perla {
1504e9ecc731SSathya Perla 	if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP)
1505e9ecc731SSathya Perla 		flow->src_fid = bp->pf.fw_fid;
1506e9ecc731SSathya Perla 	else
1507e9ecc731SSathya Perla 		flow->src_fid = src_fid;
1508e9ecc731SSathya Perla }
1509e9ecc731SSathya Perla 
15102ae7408fSSathya Perla /* Add a new flow or replace an existing flow.
15112ae7408fSSathya Perla  * Notes on locking:
15122ae7408fSSathya Perla  * There are essentially two critical sections here.
15132ae7408fSSathya Perla  * 1. while adding a new flow
15142ae7408fSSathya Perla  *    a) lookup l2-key
15152ae7408fSSathya Perla  *    b) issue HWRM cmd and get flow_handle
15162ae7408fSSathya Perla  *    c) link l2-key with flow
15172ae7408fSSathya Perla  * 2. while deleting a flow
15182ae7408fSSathya Perla  *    a) unlinking l2-key from flow
15192ae7408fSSathya Perla  * A lock is needed to protect these two critical sections.
15202ae7408fSSathya Perla  *
15212ae7408fSSathya Perla  * The hash-tables are already protected by the rhashtable API.
15222ae7408fSSathya Perla  */
bnxt_tc_add_flow(struct bnxt * bp,u16 src_fid,struct flow_cls_offload * tc_flow_cmd)15232ae7408fSSathya Perla static int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid,
1524f9e30088SPablo Neira Ayuso 			    struct flow_cls_offload *tc_flow_cmd)
15252ae7408fSSathya Perla {
15262ae7408fSSathya Perla 	struct bnxt_tc_flow_node *new_node, *old_node;
1527cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
15282ae7408fSSathya Perla 	struct bnxt_tc_flow *flow;
15298c95f773SSathya Perla 	__le32 tunnel_handle = 0;
15302ae7408fSSathya Perla 	__le16 ref_flow_handle;
15312ae7408fSSathya Perla 	int rc;
15322ae7408fSSathya Perla 
15332ae7408fSSathya Perla 	/* allocate memory for the new flow and it's node */
15342ae7408fSSathya Perla 	new_node = kzalloc(sizeof(*new_node), GFP_KERNEL);
15352ae7408fSSathya Perla 	if (!new_node) {
15362ae7408fSSathya Perla 		rc = -ENOMEM;
15372ae7408fSSathya Perla 		goto done;
15382ae7408fSSathya Perla 	}
15392ae7408fSSathya Perla 	new_node->cookie = tc_flow_cmd->cookie;
15402ae7408fSSathya Perla 	flow = &new_node->flow;
15412ae7408fSSathya Perla 
15422ae7408fSSathya Perla 	rc = bnxt_tc_parse_flow(bp, tc_flow_cmd, flow);
15432ae7408fSSathya Perla 	if (rc)
15442ae7408fSSathya Perla 		goto free_node;
1545e9ecc731SSathya Perla 
1546e9ecc731SSathya Perla 	bnxt_tc_set_src_fid(bp, flow, src_fid);
1547685ec6a8SVenkat Duvvuru 	bnxt_tc_set_flow_dir(bp, flow, flow->src_fid);
1548abd43a13SVenkat Duvvuru 
15492ae7408fSSathya Perla 	if (!bnxt_tc_can_offload(bp, flow)) {
1550b2d69122SSriharsha Basavapatna 		rc = -EOPNOTSUPP;
155108f8280eSSomnath Kotur 		kfree_rcu(new_node, rcu);
155208f8280eSSomnath Kotur 		return rc;
15532ae7408fSSathya Perla 	}
15542ae7408fSSathya Perla 
15552ae7408fSSathya Perla 	/* If a flow exists with the same cookie, delete it */
15562ae7408fSSathya Perla 	old_node = rhashtable_lookup_fast(&tc_info->flow_table,
15572ae7408fSSathya Perla 					  &tc_flow_cmd->cookie,
15582ae7408fSSathya Perla 					  tc_info->flow_ht_params);
15592ae7408fSSathya Perla 	if (old_node)
15602ae7408fSSathya Perla 		__bnxt_tc_del_flow(bp, old_node);
15612ae7408fSSathya Perla 
15622ae7408fSSathya Perla 	/* Check if the L2 part of the flow has been offloaded already.
15632ae7408fSSathya Perla 	 * If so, bump up it's refcnt and get it's reference handle.
15642ae7408fSSathya Perla 	 */
15652ae7408fSSathya Perla 	mutex_lock(&tc_info->lock);
15662ae7408fSSathya Perla 	rc = bnxt_tc_get_ref_flow_handle(bp, flow, new_node, &ref_flow_handle);
15672ae7408fSSathya Perla 	if (rc)
15682ae7408fSSathya Perla 		goto unlock;
15692ae7408fSSathya Perla 
15708c95f773SSathya Perla 	/* If the flow involves tunnel encap/decap, get tunnel_handle */
15718c95f773SSathya Perla 	rc = bnxt_tc_get_tunnel_handle(bp, flow, new_node, &tunnel_handle);
15722ae7408fSSathya Perla 	if (rc)
15732ae7408fSSathya Perla 		goto put_l2;
15742ae7408fSSathya Perla 
15758c95f773SSathya Perla 	/* send HWRM cmd to alloc the flow */
15768c95f773SSathya Perla 	rc = bnxt_hwrm_cfa_flow_alloc(bp, flow, ref_flow_handle,
1577abd43a13SVenkat Duvvuru 				      tunnel_handle, new_node);
15788c95f773SSathya Perla 	if (rc)
15798c95f773SSathya Perla 		goto put_tunnel;
15808c95f773SSathya Perla 
15815a84acbeSSathya Perla 	flow->lastused = jiffies;
15825a84acbeSSathya Perla 	spin_lock_init(&flow->stats_lock);
15832ae7408fSSathya Perla 	/* add new flow to flow-table */
15842ae7408fSSathya Perla 	rc = rhashtable_insert_fast(&tc_info->flow_table, &new_node->node,
15852ae7408fSSathya Perla 				    tc_info->flow_ht_params);
15862ae7408fSSathya Perla 	if (rc)
15872ae7408fSSathya Perla 		goto hwrm_flow_free;
15882ae7408fSSathya Perla 
15892ae7408fSSathya Perla 	mutex_unlock(&tc_info->lock);
15902ae7408fSSathya Perla 	return 0;
15912ae7408fSSathya Perla 
15922ae7408fSSathya Perla hwrm_flow_free:
1593abd43a13SVenkat Duvvuru 	bnxt_hwrm_cfa_flow_free(bp, new_node);
15948c95f773SSathya Perla put_tunnel:
15958c95f773SSathya Perla 	bnxt_tc_put_tunnel_handle(bp, flow, new_node);
15962ae7408fSSathya Perla put_l2:
15972ae7408fSSathya Perla 	bnxt_tc_put_l2_node(bp, new_node);
15982ae7408fSSathya Perla unlock:
15992ae7408fSSathya Perla 	mutex_unlock(&tc_info->lock);
16002ae7408fSSathya Perla free_node:
16018c95f773SSathya Perla 	kfree_rcu(new_node, rcu);
16022ae7408fSSathya Perla done:
16039a005c38SJonathan Lemon 	netdev_err(bp->dev, "Error: %s: cookie=0x%lx error=%d\n",
16042ae7408fSSathya Perla 		   __func__, tc_flow_cmd->cookie, rc);
16052ae7408fSSathya Perla 	return rc;
16062ae7408fSSathya Perla }
16072ae7408fSSathya Perla 
bnxt_tc_del_flow(struct bnxt * bp,struct flow_cls_offload * tc_flow_cmd)16082ae7408fSSathya Perla static int bnxt_tc_del_flow(struct bnxt *bp,
1609f9e30088SPablo Neira Ayuso 			    struct flow_cls_offload *tc_flow_cmd)
16102ae7408fSSathya Perla {
1611cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
16122ae7408fSSathya Perla 	struct bnxt_tc_flow_node *flow_node;
16132ae7408fSSathya Perla 
16142ae7408fSSathya Perla 	flow_node = rhashtable_lookup_fast(&tc_info->flow_table,
16152ae7408fSSathya Perla 					   &tc_flow_cmd->cookie,
16162ae7408fSSathya Perla 					   tc_info->flow_ht_params);
1617b9ecc340SSriharsha Basavapatna 	if (!flow_node)
16182ae7408fSSathya Perla 		return -EINVAL;
16192ae7408fSSathya Perla 
16202ae7408fSSathya Perla 	return __bnxt_tc_del_flow(bp, flow_node);
16212ae7408fSSathya Perla }
16222ae7408fSSathya Perla 
bnxt_tc_get_flow_stats(struct bnxt * bp,struct flow_cls_offload * tc_flow_cmd)16232ae7408fSSathya Perla static int bnxt_tc_get_flow_stats(struct bnxt *bp,
1624f9e30088SPablo Neira Ayuso 				  struct flow_cls_offload *tc_flow_cmd)
16252ae7408fSSathya Perla {
16265a84acbeSSathya Perla 	struct bnxt_tc_flow_stats stats, *curr_stats, *prev_stats;
1627cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
1628d7bc7305SSathya Perla 	struct bnxt_tc_flow_node *flow_node;
16295a84acbeSSathya Perla 	struct bnxt_tc_flow *flow;
16305a84acbeSSathya Perla 	unsigned long lastused;
1631d7bc7305SSathya Perla 
1632d7bc7305SSathya Perla 	flow_node = rhashtable_lookup_fast(&tc_info->flow_table,
1633d7bc7305SSathya Perla 					   &tc_flow_cmd->cookie,
1634d7bc7305SSathya Perla 					   tc_info->flow_ht_params);
1635b9ecc340SSriharsha Basavapatna 	if (!flow_node)
1636d7bc7305SSathya Perla 		return -1;
1637d7bc7305SSathya Perla 
16385a84acbeSSathya Perla 	flow = &flow_node->flow;
16395a84acbeSSathya Perla 	curr_stats = &flow->stats;
16405a84acbeSSathya Perla 	prev_stats = &flow->prev_stats;
16415a84acbeSSathya Perla 
16425a84acbeSSathya Perla 	spin_lock(&flow->stats_lock);
16435a84acbeSSathya Perla 	stats.packets = curr_stats->packets - prev_stats->packets;
16445a84acbeSSathya Perla 	stats.bytes = curr_stats->bytes - prev_stats->bytes;
16455a84acbeSSathya Perla 	*prev_stats = *curr_stats;
16465a84acbeSSathya Perla 	lastused = flow->lastused;
16475a84acbeSSathya Perla 	spin_unlock(&flow->stats_lock);
16485a84acbeSSathya Perla 
16494b61d3e8SPo Liu 	flow_stats_update(&tc_flow_cmd->stats, stats.bytes, stats.packets, 0,
165093a129ebSJiri Pirko 			  lastused, FLOW_ACTION_HW_STATS_DELAYED);
16515a84acbeSSathya Perla 	return 0;
16525a84acbeSSathya Perla }
16535a84acbeSSathya Perla 
bnxt_fill_cfa_stats_req(struct bnxt * bp,struct bnxt_tc_flow_node * flow_node,__le16 * flow_handle,__le32 * flow_id)1654abd43a13SVenkat Duvvuru static void bnxt_fill_cfa_stats_req(struct bnxt *bp,
1655abd43a13SVenkat Duvvuru 				    struct bnxt_tc_flow_node *flow_node,
1656abd43a13SVenkat Duvvuru 				    __le16 *flow_handle, __le32 *flow_id)
1657abd43a13SVenkat Duvvuru {
1658abd43a13SVenkat Duvvuru 	u16 handle;
1659abd43a13SVenkat Duvvuru 
1660abd43a13SVenkat Duvvuru 	if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) {
1661abd43a13SVenkat Duvvuru 		*flow_id = flow_node->flow_id;
1662abd43a13SVenkat Duvvuru 
1663abd43a13SVenkat Duvvuru 		/* If flow_id is used to fetch flow stats then:
1664abd43a13SVenkat Duvvuru 		 * 1. lower 12 bits of flow_handle must be set to all 1s.
1665abd43a13SVenkat Duvvuru 		 * 2. 15th bit of flow_handle must specify the flow
1666abd43a13SVenkat Duvvuru 		 *    direction (TX/RX).
1667abd43a13SVenkat Duvvuru 		 */
16689bf46566SSomnath Kotur 		if (flow_node->flow.l2_key.dir == BNXT_DIR_RX)
1669abd43a13SVenkat Duvvuru 			handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_DIR_RX |
1670abd43a13SVenkat Duvvuru 				 CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK;
1671abd43a13SVenkat Duvvuru 		else
1672abd43a13SVenkat Duvvuru 			handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK;
1673abd43a13SVenkat Duvvuru 
1674abd43a13SVenkat Duvvuru 		*flow_handle = cpu_to_le16(handle);
1675abd43a13SVenkat Duvvuru 	} else {
1676abd43a13SVenkat Duvvuru 		*flow_handle = flow_node->flow_handle;
1677abd43a13SVenkat Duvvuru 	}
1678abd43a13SVenkat Duvvuru }
1679abd43a13SVenkat Duvvuru 
16805a84acbeSSathya Perla static int
bnxt_hwrm_cfa_flow_stats_get(struct bnxt * bp,int num_flows,struct bnxt_tc_stats_batch stats_batch[])16815a84acbeSSathya Perla bnxt_hwrm_cfa_flow_stats_get(struct bnxt *bp, int num_flows,
16825a84acbeSSathya Perla 			     struct bnxt_tc_stats_batch stats_batch[])
16835a84acbeSSathya Perla {
16845c209fc8SVenkat Duvvuru 	struct hwrm_cfa_flow_stats_output *resp;
1685bbf33d1dSEdwin Peer 	struct hwrm_cfa_flow_stats_input *req;
1686bbf33d1dSEdwin Peer 	__le16 *req_flow_handles;
1687bbf33d1dSEdwin Peer 	__le32 *req_flow_ids;
16885a84acbeSSathya Perla 	int rc, i;
16895a84acbeSSathya Perla 
1690bbf33d1dSEdwin Peer 	rc = hwrm_req_init(bp, req, HWRM_CFA_FLOW_STATS);
1691bbf33d1dSEdwin Peer 	if (rc)
1692bbf33d1dSEdwin Peer 		goto exit;
1693bbf33d1dSEdwin Peer 
1694bbf33d1dSEdwin Peer 	req_flow_handles = &req->flow_handle_0;
1695bbf33d1dSEdwin Peer 	req_flow_ids = &req->flow_id_0;
1696bbf33d1dSEdwin Peer 
1697bbf33d1dSEdwin Peer 	req->num_flows = cpu_to_le16(num_flows);
16985a84acbeSSathya Perla 	for (i = 0; i < num_flows; i++) {
16995a84acbeSSathya Perla 		struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node;
17005a84acbeSSathya Perla 
1701abd43a13SVenkat Duvvuru 		bnxt_fill_cfa_stats_req(bp, flow_node,
1702abd43a13SVenkat Duvvuru 					&req_flow_handles[i], &req_flow_ids[i]);
17035a84acbeSSathya Perla 	}
17045a84acbeSSathya Perla 
1705bbf33d1dSEdwin Peer 	resp = hwrm_req_hold(bp, req);
1706bbf33d1dSEdwin Peer 	rc = hwrm_req_send(bp, req);
17075a84acbeSSathya Perla 	if (!rc) {
17085c209fc8SVenkat Duvvuru 		__le64 *resp_packets;
17095c209fc8SVenkat Duvvuru 		__le64 *resp_bytes;
17105c209fc8SVenkat Duvvuru 
17115c209fc8SVenkat Duvvuru 		resp_packets = &resp->packet_0;
17125c209fc8SVenkat Duvvuru 		resp_bytes = &resp->byte_0;
17135a84acbeSSathya Perla 
17145a84acbeSSathya Perla 		for (i = 0; i < num_flows; i++) {
17155a84acbeSSathya Perla 			stats_batch[i].hw_stats.packets =
17165a84acbeSSathya Perla 						le64_to_cpu(resp_packets[i]);
17175a84acbeSSathya Perla 			stats_batch[i].hw_stats.bytes =
17185a84acbeSSathya Perla 						le64_to_cpu(resp_bytes[i]);
17195a84acbeSSathya Perla 		}
17205a84acbeSSathya Perla 	}
1721bbf33d1dSEdwin Peer 	hwrm_req_drop(bp, req);
1722bbf33d1dSEdwin Peer exit:
1723bbf33d1dSEdwin Peer 	if (rc)
1724bbf33d1dSEdwin Peer 		netdev_info(bp->dev, "error rc=%d\n", rc);
17256ae777eaSVenkat Duvvuru 
17265a84acbeSSathya Perla 	return rc;
17275a84acbeSSathya Perla }
17285a84acbeSSathya Perla 
17295a84acbeSSathya Perla /* Add val to accum while handling a possible wraparound
17305a84acbeSSathya Perla  * of val. Eventhough val is of type u64, its actual width
17315a84acbeSSathya Perla  * is denoted by mask and will wrap-around beyond that width.
17325a84acbeSSathya Perla  */
accumulate_val(u64 * accum,u64 val,u64 mask)17335a84acbeSSathya Perla static void accumulate_val(u64 *accum, u64 val, u64 mask)
17345a84acbeSSathya Perla {
17355a84acbeSSathya Perla #define low_bits(x, mask)		((x) & (mask))
17365a84acbeSSathya Perla #define high_bits(x, mask)		((x) & ~(mask))
17375a84acbeSSathya Perla 	bool wrapped = val < low_bits(*accum, mask);
17385a84acbeSSathya Perla 
17395a84acbeSSathya Perla 	*accum = high_bits(*accum, mask) + val;
17405a84acbeSSathya Perla 	if (wrapped)
17415a84acbeSSathya Perla 		*accum += (mask + 1);
17425a84acbeSSathya Perla }
17435a84acbeSSathya Perla 
17445a84acbeSSathya Perla /* The HW counters' width is much less than 64bits.
17455a84acbeSSathya Perla  * Handle possible wrap-around while updating the stat counters
17465a84acbeSSathya Perla  */
bnxt_flow_stats_accum(struct bnxt_tc_info * tc_info,struct bnxt_tc_flow_stats * acc_stats,struct bnxt_tc_flow_stats * hw_stats)17475a84acbeSSathya Perla static void bnxt_flow_stats_accum(struct bnxt_tc_info *tc_info,
17485a84acbeSSathya Perla 				  struct bnxt_tc_flow_stats *acc_stats,
17495a84acbeSSathya Perla 				  struct bnxt_tc_flow_stats *hw_stats)
17505a84acbeSSathya Perla {
17515a84acbeSSathya Perla 	accumulate_val(&acc_stats->bytes, hw_stats->bytes, tc_info->bytes_mask);
17525a84acbeSSathya Perla 	accumulate_val(&acc_stats->packets, hw_stats->packets,
17535a84acbeSSathya Perla 		       tc_info->packets_mask);
17545a84acbeSSathya Perla }
17555a84acbeSSathya Perla 
17565a84acbeSSathya Perla static int
bnxt_tc_flow_stats_batch_update(struct bnxt * bp,int num_flows,struct bnxt_tc_stats_batch stats_batch[])17575a84acbeSSathya Perla bnxt_tc_flow_stats_batch_update(struct bnxt *bp, int num_flows,
17585a84acbeSSathya Perla 				struct bnxt_tc_stats_batch stats_batch[])
17595a84acbeSSathya Perla {
1760cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
17615a84acbeSSathya Perla 	int rc, i;
17625a84acbeSSathya Perla 
17635a84acbeSSathya Perla 	rc = bnxt_hwrm_cfa_flow_stats_get(bp, num_flows, stats_batch);
1764d7bc7305SSathya Perla 	if (rc)
1765d7bc7305SSathya Perla 		return rc;
1766d7bc7305SSathya Perla 
17675a84acbeSSathya Perla 	for (i = 0; i < num_flows; i++) {
17685a84acbeSSathya Perla 		struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node;
17695a84acbeSSathya Perla 		struct bnxt_tc_flow *flow = &flow_node->flow;
17705a84acbeSSathya Perla 
17715a84acbeSSathya Perla 		spin_lock(&flow->stats_lock);
17725a84acbeSSathya Perla 		bnxt_flow_stats_accum(tc_info, &flow->stats,
17735a84acbeSSathya Perla 				      &stats_batch[i].hw_stats);
17745a84acbeSSathya Perla 		if (flow->stats.packets != flow->prev_stats.packets)
17755a84acbeSSathya Perla 			flow->lastused = jiffies;
17765a84acbeSSathya Perla 		spin_unlock(&flow->stats_lock);
17775a84acbeSSathya Perla 	}
17785a84acbeSSathya Perla 
17792ae7408fSSathya Perla 	return 0;
17802ae7408fSSathya Perla }
17812ae7408fSSathya Perla 
17825a84acbeSSathya Perla static int
bnxt_tc_flow_stats_batch_prep(struct bnxt * bp,struct bnxt_tc_stats_batch stats_batch[],int * num_flows)17835a84acbeSSathya Perla bnxt_tc_flow_stats_batch_prep(struct bnxt *bp,
17845a84acbeSSathya Perla 			      struct bnxt_tc_stats_batch stats_batch[],
17855a84acbeSSathya Perla 			      int *num_flows)
17865a84acbeSSathya Perla {
1787cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
17885a84acbeSSathya Perla 	struct rhashtable_iter *iter = &tc_info->iter;
17895a84acbeSSathya Perla 	void *flow_node;
17905a84acbeSSathya Perla 	int rc, i;
17915a84acbeSSathya Perla 
179297a6ec4aSTom Herbert 	rhashtable_walk_start(iter);
17935a84acbeSSathya Perla 
17945a84acbeSSathya Perla 	rc = 0;
17955a84acbeSSathya Perla 	for (i = 0; i < BNXT_FLOW_STATS_BATCH_MAX; i++) {
17965a84acbeSSathya Perla 		flow_node = rhashtable_walk_next(iter);
17975a84acbeSSathya Perla 		if (IS_ERR(flow_node)) {
17985a84acbeSSathya Perla 			i = 0;
17995a84acbeSSathya Perla 			if (PTR_ERR(flow_node) == -EAGAIN) {
18005a84acbeSSathya Perla 				continue;
18015a84acbeSSathya Perla 			} else {
18025a84acbeSSathya Perla 				rc = PTR_ERR(flow_node);
18035a84acbeSSathya Perla 				goto done;
18045a84acbeSSathya Perla 			}
18055a84acbeSSathya Perla 		}
18065a84acbeSSathya Perla 
18075a84acbeSSathya Perla 		/* No more flows */
18085a84acbeSSathya Perla 		if (!flow_node)
18095a84acbeSSathya Perla 			goto done;
18105a84acbeSSathya Perla 
18115a84acbeSSathya Perla 		stats_batch[i].flow_node = flow_node;
18125a84acbeSSathya Perla 	}
18135a84acbeSSathya Perla done:
18145a84acbeSSathya Perla 	rhashtable_walk_stop(iter);
18155a84acbeSSathya Perla 	*num_flows = i;
18165a84acbeSSathya Perla 	return rc;
18175a84acbeSSathya Perla }
18185a84acbeSSathya Perla 
bnxt_tc_flow_stats_work(struct bnxt * bp)18195a84acbeSSathya Perla void bnxt_tc_flow_stats_work(struct bnxt *bp)
18205a84acbeSSathya Perla {
1821cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
18225a84acbeSSathya Perla 	int num_flows, rc;
18235a84acbeSSathya Perla 
18245a84acbeSSathya Perla 	num_flows = atomic_read(&tc_info->flow_table.nelems);
18255a84acbeSSathya Perla 	if (!num_flows)
18265a84acbeSSathya Perla 		return;
18275a84acbeSSathya Perla 
18285a84acbeSSathya Perla 	rhashtable_walk_enter(&tc_info->flow_table, &tc_info->iter);
18295a84acbeSSathya Perla 
18305a84acbeSSathya Perla 	for (;;) {
18315a84acbeSSathya Perla 		rc = bnxt_tc_flow_stats_batch_prep(bp, tc_info->stats_batch,
18325a84acbeSSathya Perla 						   &num_flows);
18335a84acbeSSathya Perla 		if (rc) {
18345a84acbeSSathya Perla 			if (rc == -EAGAIN)
18355a84acbeSSathya Perla 				continue;
18365a84acbeSSathya Perla 			break;
18375a84acbeSSathya Perla 		}
18385a84acbeSSathya Perla 
18395a84acbeSSathya Perla 		if (!num_flows)
18405a84acbeSSathya Perla 			break;
18415a84acbeSSathya Perla 
18425a84acbeSSathya Perla 		bnxt_tc_flow_stats_batch_update(bp, num_flows,
18435a84acbeSSathya Perla 						tc_info->stats_batch);
18445a84acbeSSathya Perla 	}
18455a84acbeSSathya Perla 
18465a84acbeSSathya Perla 	rhashtable_walk_exit(&tc_info->iter);
18475a84acbeSSathya Perla }
18485a84acbeSSathya Perla 
bnxt_tc_setup_flower(struct bnxt * bp,u16 src_fid,struct flow_cls_offload * cls_flower)18492ae7408fSSathya Perla int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid,
1850f9e30088SPablo Neira Ayuso 			 struct flow_cls_offload *cls_flower)
18512ae7408fSSathya Perla {
18522ae7408fSSathya Perla 	switch (cls_flower->command) {
1853f9e30088SPablo Neira Ayuso 	case FLOW_CLS_REPLACE:
185483741bb0SJiri Pirko 		return bnxt_tc_add_flow(bp, src_fid, cls_flower);
1855f9e30088SPablo Neira Ayuso 	case FLOW_CLS_DESTROY:
185683741bb0SJiri Pirko 		return bnxt_tc_del_flow(bp, cls_flower);
1857f9e30088SPablo Neira Ayuso 	case FLOW_CLS_STATS:
185883741bb0SJiri Pirko 		return bnxt_tc_get_flow_stats(bp, cls_flower);
185983741bb0SJiri Pirko 	default:
186083741bb0SJiri Pirko 		return -EOPNOTSUPP;
18612ae7408fSSathya Perla 	}
18622ae7408fSSathya Perla }
18632ae7408fSSathya Perla 
bnxt_tc_setup_indr_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)1864627c89d0SSriharsha Basavapatna static int bnxt_tc_setup_indr_block_cb(enum tc_setup_type type,
1865627c89d0SSriharsha Basavapatna 				       void *type_data, void *cb_priv)
1866627c89d0SSriharsha Basavapatna {
1867627c89d0SSriharsha Basavapatna 	struct bnxt_flower_indr_block_cb_priv *priv = cb_priv;
1868627c89d0SSriharsha Basavapatna 	struct flow_cls_offload *flower = type_data;
1869627c89d0SSriharsha Basavapatna 	struct bnxt *bp = priv->bp;
1870627c89d0SSriharsha Basavapatna 
1871b0757491SSriharsha Basavapatna 	if (!tc_cls_can_offload_and_chain0(bp->dev, type_data))
1872627c89d0SSriharsha Basavapatna 		return -EOPNOTSUPP;
1873627c89d0SSriharsha Basavapatna 
1874627c89d0SSriharsha Basavapatna 	switch (type) {
1875627c89d0SSriharsha Basavapatna 	case TC_SETUP_CLSFLOWER:
1876627c89d0SSriharsha Basavapatna 		return bnxt_tc_setup_flower(bp, bp->pf.fw_fid, flower);
1877627c89d0SSriharsha Basavapatna 	default:
1878627c89d0SSriharsha Basavapatna 		return -EOPNOTSUPP;
1879627c89d0SSriharsha Basavapatna 	}
1880627c89d0SSriharsha Basavapatna }
1881627c89d0SSriharsha Basavapatna 
1882627c89d0SSriharsha Basavapatna static struct bnxt_flower_indr_block_cb_priv *
bnxt_tc_indr_block_cb_lookup(struct bnxt * bp,struct net_device * netdev)1883627c89d0SSriharsha Basavapatna bnxt_tc_indr_block_cb_lookup(struct bnxt *bp, struct net_device *netdev)
1884627c89d0SSriharsha Basavapatna {
1885627c89d0SSriharsha Basavapatna 	struct bnxt_flower_indr_block_cb_priv *cb_priv;
1886627c89d0SSriharsha Basavapatna 
1887627c89d0SSriharsha Basavapatna 	list_for_each_entry(cb_priv, &bp->tc_indr_block_list, list)
1888627c89d0SSriharsha Basavapatna 		if (cb_priv->tunnel_netdev == netdev)
1889627c89d0SSriharsha Basavapatna 			return cb_priv;
1890627c89d0SSriharsha Basavapatna 
1891627c89d0SSriharsha Basavapatna 	return NULL;
1892627c89d0SSriharsha Basavapatna }
1893627c89d0SSriharsha Basavapatna 
bnxt_tc_setup_indr_rel(void * cb_priv)1894627c89d0SSriharsha Basavapatna static void bnxt_tc_setup_indr_rel(void *cb_priv)
1895627c89d0SSriharsha Basavapatna {
1896627c89d0SSriharsha Basavapatna 	struct bnxt_flower_indr_block_cb_priv *priv = cb_priv;
1897627c89d0SSriharsha Basavapatna 
1898627c89d0SSriharsha Basavapatna 	list_del(&priv->list);
1899627c89d0SSriharsha Basavapatna 	kfree(priv);
1900627c89d0SSriharsha Basavapatna }
1901627c89d0SSriharsha Basavapatna 
bnxt_tc_setup_indr_block(struct net_device * netdev,struct Qdisc * sch,struct bnxt * bp,struct flow_block_offload * f,void * data,void (* cleanup)(struct flow_block_cb * block_cb))1902c40f4e50SPetr Machata static int bnxt_tc_setup_indr_block(struct net_device *netdev, struct Qdisc *sch, struct bnxt *bp,
190366f1939aSwenxu 				    struct flow_block_offload *f, void *data,
190466f1939aSwenxu 				    void (*cleanup)(struct flow_block_cb *block_cb))
1905627c89d0SSriharsha Basavapatna {
1906627c89d0SSriharsha Basavapatna 	struct bnxt_flower_indr_block_cb_priv *cb_priv;
1907627c89d0SSriharsha Basavapatna 	struct flow_block_cb *block_cb;
1908627c89d0SSriharsha Basavapatna 
1909627c89d0SSriharsha Basavapatna 	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
1910627c89d0SSriharsha Basavapatna 		return -EOPNOTSUPP;
1911627c89d0SSriharsha Basavapatna 
1912627c89d0SSriharsha Basavapatna 	switch (f->command) {
1913627c89d0SSriharsha Basavapatna 	case FLOW_BLOCK_BIND:
1914627c89d0SSriharsha Basavapatna 		cb_priv = kmalloc(sizeof(*cb_priv), GFP_KERNEL);
1915627c89d0SSriharsha Basavapatna 		if (!cb_priv)
1916627c89d0SSriharsha Basavapatna 			return -ENOMEM;
1917627c89d0SSriharsha Basavapatna 
1918627c89d0SSriharsha Basavapatna 		cb_priv->tunnel_netdev = netdev;
1919627c89d0SSriharsha Basavapatna 		cb_priv->bp = bp;
1920627c89d0SSriharsha Basavapatna 		list_add(&cb_priv->list, &bp->tc_indr_block_list);
1921627c89d0SSriharsha Basavapatna 
192266f1939aSwenxu 		block_cb = flow_indr_block_cb_alloc(bnxt_tc_setup_indr_block_cb,
1923627c89d0SSriharsha Basavapatna 						    cb_priv, cb_priv,
192466f1939aSwenxu 						    bnxt_tc_setup_indr_rel, f,
1925c40f4e50SPetr Machata 						    netdev, sch, data, bp, cleanup);
1926627c89d0SSriharsha Basavapatna 		if (IS_ERR(block_cb)) {
1927627c89d0SSriharsha Basavapatna 			list_del(&cb_priv->list);
1928627c89d0SSriharsha Basavapatna 			kfree(cb_priv);
1929627c89d0SSriharsha Basavapatna 			return PTR_ERR(block_cb);
1930627c89d0SSriharsha Basavapatna 		}
1931627c89d0SSriharsha Basavapatna 
1932627c89d0SSriharsha Basavapatna 		flow_block_cb_add(block_cb, f);
1933627c89d0SSriharsha Basavapatna 		list_add_tail(&block_cb->driver_list, &bnxt_block_cb_list);
1934627c89d0SSriharsha Basavapatna 		break;
1935627c89d0SSriharsha Basavapatna 	case FLOW_BLOCK_UNBIND:
1936627c89d0SSriharsha Basavapatna 		cb_priv = bnxt_tc_indr_block_cb_lookup(bp, netdev);
1937627c89d0SSriharsha Basavapatna 		if (!cb_priv)
1938627c89d0SSriharsha Basavapatna 			return -ENOENT;
1939627c89d0SSriharsha Basavapatna 
1940627c89d0SSriharsha Basavapatna 		block_cb = flow_block_cb_lookup(f->block,
1941627c89d0SSriharsha Basavapatna 						bnxt_tc_setup_indr_block_cb,
1942627c89d0SSriharsha Basavapatna 						cb_priv);
1943627c89d0SSriharsha Basavapatna 		if (!block_cb)
1944627c89d0SSriharsha Basavapatna 			return -ENOENT;
1945627c89d0SSriharsha Basavapatna 
194666f1939aSwenxu 		flow_indr_block_cb_remove(block_cb, f);
1947627c89d0SSriharsha Basavapatna 		list_del(&block_cb->driver_list);
1948627c89d0SSriharsha Basavapatna 		break;
1949627c89d0SSriharsha Basavapatna 	default:
1950627c89d0SSriharsha Basavapatna 		return -EOPNOTSUPP;
1951627c89d0SSriharsha Basavapatna 	}
1952627c89d0SSriharsha Basavapatna 	return 0;
1953627c89d0SSriharsha Basavapatna }
1954627c89d0SSriharsha Basavapatna 
bnxt_is_netdev_indr_offload(struct net_device * netdev)1955627c89d0SSriharsha Basavapatna static bool bnxt_is_netdev_indr_offload(struct net_device *netdev)
1956627c89d0SSriharsha Basavapatna {
1957627c89d0SSriharsha Basavapatna 	return netif_is_vxlan(netdev);
1958627c89d0SSriharsha Basavapatna }
1959627c89d0SSriharsha Basavapatna 
bnxt_tc_setup_indr_cb(struct net_device * netdev,struct Qdisc * sch,void * cb_priv,enum tc_setup_type type,void * type_data,void * data,void (* cleanup)(struct flow_block_cb * block_cb))1960c40f4e50SPetr Machata static int bnxt_tc_setup_indr_cb(struct net_device *netdev, struct Qdisc *sch, void *cb_priv,
196166f1939aSwenxu 				 enum tc_setup_type type, void *type_data,
196266f1939aSwenxu 				 void *data,
196366f1939aSwenxu 				 void (*cleanup)(struct flow_block_cb *block_cb))
1964627c89d0SSriharsha Basavapatna {
1965144d4c9eSBaowen Zheng 	if (!netdev || !bnxt_is_netdev_indr_offload(netdev))
1966e445e30cSPablo Neira Ayuso 		return -EOPNOTSUPP;
1967627c89d0SSriharsha Basavapatna 
1968e445e30cSPablo Neira Ayuso 	switch (type) {
1969e445e30cSPablo Neira Ayuso 	case TC_SETUP_BLOCK:
1970c40f4e50SPetr Machata 		return bnxt_tc_setup_indr_block(netdev, sch, cb_priv, type_data, data, cleanup);
1971e445e30cSPablo Neira Ayuso 	default:
1972627c89d0SSriharsha Basavapatna 		break;
1973627c89d0SSriharsha Basavapatna 	}
1974627c89d0SSriharsha Basavapatna 
1975e445e30cSPablo Neira Ayuso 	return -EOPNOTSUPP;
1976627c89d0SSriharsha Basavapatna }
1977627c89d0SSriharsha Basavapatna 
19782ae7408fSSathya Perla static const struct rhashtable_params bnxt_tc_flow_ht_params = {
19792ae7408fSSathya Perla 	.head_offset = offsetof(struct bnxt_tc_flow_node, node),
19802ae7408fSSathya Perla 	.key_offset = offsetof(struct bnxt_tc_flow_node, cookie),
19812ae7408fSSathya Perla 	.key_len = sizeof(((struct bnxt_tc_flow_node *)0)->cookie),
19822ae7408fSSathya Perla 	.automatic_shrinking = true
19832ae7408fSSathya Perla };
19842ae7408fSSathya Perla 
19852ae7408fSSathya Perla static const struct rhashtable_params bnxt_tc_l2_ht_params = {
19862ae7408fSSathya Perla 	.head_offset = offsetof(struct bnxt_tc_l2_node, node),
19872ae7408fSSathya Perla 	.key_offset = offsetof(struct bnxt_tc_l2_node, key),
19882ae7408fSSathya Perla 	.key_len = BNXT_TC_L2_KEY_LEN,
19892ae7408fSSathya Perla 	.automatic_shrinking = true
19902ae7408fSSathya Perla };
19912ae7408fSSathya Perla 
19928c95f773SSathya Perla static const struct rhashtable_params bnxt_tc_decap_l2_ht_params = {
19938c95f773SSathya Perla 	.head_offset = offsetof(struct bnxt_tc_l2_node, node),
19948c95f773SSathya Perla 	.key_offset = offsetof(struct bnxt_tc_l2_node, key),
19958c95f773SSathya Perla 	.key_len = BNXT_TC_L2_KEY_LEN,
19968c95f773SSathya Perla 	.automatic_shrinking = true
19978c95f773SSathya Perla };
19988c95f773SSathya Perla 
19998c95f773SSathya Perla static const struct rhashtable_params bnxt_tc_tunnel_ht_params = {
20008c95f773SSathya Perla 	.head_offset = offsetof(struct bnxt_tc_tunnel_node, node),
20018c95f773SSathya Perla 	.key_offset = offsetof(struct bnxt_tc_tunnel_node, key),
20028c95f773SSathya Perla 	.key_len = sizeof(struct ip_tunnel_key),
20038c95f773SSathya Perla 	.automatic_shrinking = true
20048c95f773SSathya Perla };
20058c95f773SSathya Perla 
20062ae7408fSSathya Perla /* convert counter width in bits to a mask */
20072ae7408fSSathya Perla #define mask(width)		((u64)~0 >> (64 - (width)))
20082ae7408fSSathya Perla 
bnxt_init_tc(struct bnxt * bp)20092ae7408fSSathya Perla int bnxt_init_tc(struct bnxt *bp)
20102ae7408fSSathya Perla {
2011cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info;
20122ae7408fSSathya Perla 	int rc;
20132ae7408fSSathya Perla 
201418c7015cSJakub Kicinski 	if (bp->hwrm_spec_code < 0x10803)
201518c7015cSJakub Kicinski 		return 0;
2016cd66358eSSathya Perla 
2017cd66358eSSathya Perla 	tc_info = kzalloc(sizeof(*tc_info), GFP_KERNEL);
2018cd66358eSSathya Perla 	if (!tc_info)
2019cd66358eSSathya Perla 		return -ENOMEM;
20202ae7408fSSathya Perla 	mutex_init(&tc_info->lock);
20212ae7408fSSathya Perla 
20222ae7408fSSathya Perla 	/* Counter widths are programmed by FW */
20232ae7408fSSathya Perla 	tc_info->bytes_mask = mask(36);
20242ae7408fSSathya Perla 	tc_info->packets_mask = mask(28);
20252ae7408fSSathya Perla 
20262ae7408fSSathya Perla 	tc_info->flow_ht_params = bnxt_tc_flow_ht_params;
20272ae7408fSSathya Perla 	rc = rhashtable_init(&tc_info->flow_table, &tc_info->flow_ht_params);
20282ae7408fSSathya Perla 	if (rc)
2029cd66358eSSathya Perla 		goto free_tc_info;
20302ae7408fSSathya Perla 
20312ae7408fSSathya Perla 	tc_info->l2_ht_params = bnxt_tc_l2_ht_params;
20322ae7408fSSathya Perla 	rc = rhashtable_init(&tc_info->l2_table, &tc_info->l2_ht_params);
20332ae7408fSSathya Perla 	if (rc)
20342ae7408fSSathya Perla 		goto destroy_flow_table;
20352ae7408fSSathya Perla 
20368c95f773SSathya Perla 	tc_info->decap_l2_ht_params = bnxt_tc_decap_l2_ht_params;
20378c95f773SSathya Perla 	rc = rhashtable_init(&tc_info->decap_l2_table,
20388c95f773SSathya Perla 			     &tc_info->decap_l2_ht_params);
20398c95f773SSathya Perla 	if (rc)
20408c95f773SSathya Perla 		goto destroy_l2_table;
20418c95f773SSathya Perla 
20428c95f773SSathya Perla 	tc_info->decap_ht_params = bnxt_tc_tunnel_ht_params;
20438c95f773SSathya Perla 	rc = rhashtable_init(&tc_info->decap_table,
20448c95f773SSathya Perla 			     &tc_info->decap_ht_params);
20458c95f773SSathya Perla 	if (rc)
20468c95f773SSathya Perla 		goto destroy_decap_l2_table;
20478c95f773SSathya Perla 
20488c95f773SSathya Perla 	tc_info->encap_ht_params = bnxt_tc_tunnel_ht_params;
20498c95f773SSathya Perla 	rc = rhashtable_init(&tc_info->encap_table,
20508c95f773SSathya Perla 			     &tc_info->encap_ht_params);
20518c95f773SSathya Perla 	if (rc)
20528c95f773SSathya Perla 		goto destroy_decap_table;
20538c95f773SSathya Perla 
20542ae7408fSSathya Perla 	tc_info->enabled = true;
20552ae7408fSSathya Perla 	bp->dev->hw_features |= NETIF_F_HW_TC;
20562ae7408fSSathya Perla 	bp->dev->features |= NETIF_F_HW_TC;
2057cd66358eSSathya Perla 	bp->tc_info = tc_info;
2058627c89d0SSriharsha Basavapatna 
2059627c89d0SSriharsha Basavapatna 	/* init indirect block notifications */
2060627c89d0SSriharsha Basavapatna 	INIT_LIST_HEAD(&bp->tc_indr_block_list);
2061e445e30cSPablo Neira Ayuso 
2062e445e30cSPablo Neira Ayuso 	rc = flow_indr_dev_register(bnxt_tc_setup_indr_cb, bp);
2063627c89d0SSriharsha Basavapatna 	if (!rc)
20642ae7408fSSathya Perla 		return 0;
20652ae7408fSSathya Perla 
2066627c89d0SSriharsha Basavapatna 	rhashtable_destroy(&tc_info->encap_table);
2067627c89d0SSriharsha Basavapatna 
20688c95f773SSathya Perla destroy_decap_table:
20698c95f773SSathya Perla 	rhashtable_destroy(&tc_info->decap_table);
20708c95f773SSathya Perla destroy_decap_l2_table:
20718c95f773SSathya Perla 	rhashtable_destroy(&tc_info->decap_l2_table);
20728c95f773SSathya Perla destroy_l2_table:
20738c95f773SSathya Perla 	rhashtable_destroy(&tc_info->l2_table);
20742ae7408fSSathya Perla destroy_flow_table:
20752ae7408fSSathya Perla 	rhashtable_destroy(&tc_info->flow_table);
2076cd66358eSSathya Perla free_tc_info:
2077cd66358eSSathya Perla 	kfree(tc_info);
2078*dc903ddcSDinghao Liu 	bp->tc_info = NULL;
20792ae7408fSSathya Perla 	return rc;
20802ae7408fSSathya Perla }
20812ae7408fSSathya Perla 
bnxt_shutdown_tc(struct bnxt * bp)20822ae7408fSSathya Perla void bnxt_shutdown_tc(struct bnxt *bp)
20832ae7408fSSathya Perla {
2084cd66358eSSathya Perla 	struct bnxt_tc_info *tc_info = bp->tc_info;
20852ae7408fSSathya Perla 
2086cd66358eSSathya Perla 	if (!bnxt_tc_flower_enabled(bp))
20872ae7408fSSathya Perla 		return;
20882ae7408fSSathya Perla 
2089e445e30cSPablo Neira Ayuso 	flow_indr_dev_unregister(bnxt_tc_setup_indr_cb, bp,
2090a1db2178Swenxu 				 bnxt_tc_setup_indr_rel);
20912ae7408fSSathya Perla 	rhashtable_destroy(&tc_info->flow_table);
20922ae7408fSSathya Perla 	rhashtable_destroy(&tc_info->l2_table);
20938c95f773SSathya Perla 	rhashtable_destroy(&tc_info->decap_l2_table);
20948c95f773SSathya Perla 	rhashtable_destroy(&tc_info->decap_table);
20958c95f773SSathya Perla 	rhashtable_destroy(&tc_info->encap_table);
2096cd66358eSSathya Perla 	kfree(tc_info);
2097cd66358eSSathya Perla 	bp->tc_info = NULL;
20982ae7408fSSathya Perla }
2099