1148beb61SHenry Tieman // SPDX-License-Identifier: GPL-2.0
2148beb61SHenry Tieman /* Copyright (C) 2018-2020, Intel Corporation. */
3148beb61SHenry Tieman 
4148beb61SHenry Tieman /* flow director ethtool support for ice */
5148beb61SHenry Tieman 
6148beb61SHenry Tieman #include "ice.h"
7148beb61SHenry Tieman #include "ice_lib.h"
840319796SKiran Patil #include "ice_fdir.h"
9148beb61SHenry Tieman #include "ice_flow.h"
10148beb61SHenry Tieman 
11165d80d6SHenry Tieman static struct in6_addr full_ipv6_addr_mask = {
12165d80d6SHenry Tieman 	.in6_u = {
13165d80d6SHenry Tieman 		.u6_addr8 = {
14165d80d6SHenry Tieman 			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
15165d80d6SHenry Tieman 			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
16165d80d6SHenry Tieman 		}
17165d80d6SHenry Tieman 	}
18165d80d6SHenry Tieman };
19165d80d6SHenry Tieman 
20165d80d6SHenry Tieman static struct in6_addr zero_ipv6_addr_mask = {
21165d80d6SHenry Tieman 	.in6_u = {
22165d80d6SHenry Tieman 		.u6_addr8 = {
23165d80d6SHenry Tieman 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
24165d80d6SHenry Tieman 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25165d80d6SHenry Tieman 		}
26165d80d6SHenry Tieman 	}
27165d80d6SHenry Tieman };
28165d80d6SHenry Tieman 
29148beb61SHenry Tieman /* calls to ice_flow_add_prof require the number of segments in the array
30148beb61SHenry Tieman  * for segs_cnt. In this code that is one more than the index.
31148beb61SHenry Tieman  */
32148beb61SHenry Tieman #define TNL_SEG_CNT(_TNL_) ((_TNL_) + 1)
33148beb61SHenry Tieman 
34148beb61SHenry Tieman /**
354ab95646SHenry Tieman  * ice_fltr_to_ethtool_flow - convert filter type values to ethtool
364ab95646SHenry Tieman  * flow type values
374ab95646SHenry Tieman  * @flow: filter type to be converted
384ab95646SHenry Tieman  *
394ab95646SHenry Tieman  * Returns the corresponding ethtool flow type.
404ab95646SHenry Tieman  */
ice_fltr_to_ethtool_flow(enum ice_fltr_ptype flow)414ab95646SHenry Tieman static int ice_fltr_to_ethtool_flow(enum ice_fltr_ptype flow)
424ab95646SHenry Tieman {
434ab95646SHenry Tieman 	switch (flow) {
444ab95646SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV4_TCP:
454ab95646SHenry Tieman 		return TCP_V4_FLOW;
464ab95646SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV4_UDP:
474ab95646SHenry Tieman 		return UDP_V4_FLOW;
484ab95646SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV4_SCTP:
494ab95646SHenry Tieman 		return SCTP_V4_FLOW;
504ab95646SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV4_OTHER:
514ab95646SHenry Tieman 		return IPV4_USER_FLOW;
52165d80d6SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV6_TCP:
53165d80d6SHenry Tieman 		return TCP_V6_FLOW;
54165d80d6SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV6_UDP:
55165d80d6SHenry Tieman 		return UDP_V6_FLOW;
56165d80d6SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV6_SCTP:
57165d80d6SHenry Tieman 		return SCTP_V6_FLOW;
58165d80d6SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV6_OTHER:
59165d80d6SHenry Tieman 		return IPV6_USER_FLOW;
604ab95646SHenry Tieman 	default:
614ab95646SHenry Tieman 		/* 0 is undefined ethtool flow */
624ab95646SHenry Tieman 		return 0;
634ab95646SHenry Tieman 	}
644ab95646SHenry Tieman }
654ab95646SHenry Tieman 
664ab95646SHenry Tieman /**
674ab95646SHenry Tieman  * ice_ethtool_flow_to_fltr - convert ethtool flow type to filter enum
684ab95646SHenry Tieman  * @eth: Ethtool flow type to be converted
694ab95646SHenry Tieman  *
704ab95646SHenry Tieman  * Returns flow enum
714ab95646SHenry Tieman  */
ice_ethtool_flow_to_fltr(int eth)724ab95646SHenry Tieman static enum ice_fltr_ptype ice_ethtool_flow_to_fltr(int eth)
734ab95646SHenry Tieman {
744ab95646SHenry Tieman 	switch (eth) {
754ab95646SHenry Tieman 	case TCP_V4_FLOW:
764ab95646SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV4_TCP;
774ab95646SHenry Tieman 	case UDP_V4_FLOW:
784ab95646SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV4_UDP;
794ab95646SHenry Tieman 	case SCTP_V4_FLOW:
804ab95646SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV4_SCTP;
814ab95646SHenry Tieman 	case IPV4_USER_FLOW:
824ab95646SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV4_OTHER;
83165d80d6SHenry Tieman 	case TCP_V6_FLOW:
84165d80d6SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV6_TCP;
85165d80d6SHenry Tieman 	case UDP_V6_FLOW:
86165d80d6SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV6_UDP;
87165d80d6SHenry Tieman 	case SCTP_V6_FLOW:
88165d80d6SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV6_SCTP;
89165d80d6SHenry Tieman 	case IPV6_USER_FLOW:
90165d80d6SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV6_OTHER;
914ab95646SHenry Tieman 	default:
924ab95646SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_NONE;
934ab95646SHenry Tieman 	}
944ab95646SHenry Tieman }
954ab95646SHenry Tieman 
964ab95646SHenry Tieman /**
972c57ffcbSHenry Tieman  * ice_is_mask_valid - check mask field set
982c57ffcbSHenry Tieman  * @mask: full mask to check
992c57ffcbSHenry Tieman  * @field: field for which mask should be valid
1002c57ffcbSHenry Tieman  *
1012c57ffcbSHenry Tieman  * If the mask is fully set return true. If it is not valid for field return
1022c57ffcbSHenry Tieman  * false.
1032c57ffcbSHenry Tieman  */
ice_is_mask_valid(u64 mask,u64 field)1042c57ffcbSHenry Tieman static bool ice_is_mask_valid(u64 mask, u64 field)
1052c57ffcbSHenry Tieman {
1062c57ffcbSHenry Tieman 	return (mask & field) == field;
1072c57ffcbSHenry Tieman }
1082c57ffcbSHenry Tieman 
1092c57ffcbSHenry Tieman /**
1104ab95646SHenry Tieman  * ice_get_ethtool_fdir_entry - fill ethtool structure with fdir filter data
1114ab95646SHenry Tieman  * @hw: hardware structure that contains filter list
1124ab95646SHenry Tieman  * @cmd: ethtool command data structure to receive the filter data
1134ab95646SHenry Tieman  *
1144ab95646SHenry Tieman  * Returns 0 on success and -EINVAL on failure
1154ab95646SHenry Tieman  */
ice_get_ethtool_fdir_entry(struct ice_hw * hw,struct ethtool_rxnfc * cmd)1164ab95646SHenry Tieman int ice_get_ethtool_fdir_entry(struct ice_hw *hw, struct ethtool_rxnfc *cmd)
1174ab95646SHenry Tieman {
1184ab95646SHenry Tieman 	struct ethtool_rx_flow_spec *fsp;
1194ab95646SHenry Tieman 	struct ice_fdir_fltr *rule;
1204ab95646SHenry Tieman 	int ret = 0;
1214ab95646SHenry Tieman 	u16 idx;
1224ab95646SHenry Tieman 
1234ab95646SHenry Tieman 	fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
1244ab95646SHenry Tieman 
1254ab95646SHenry Tieman 	mutex_lock(&hw->fdir_fltr_lock);
1264ab95646SHenry Tieman 
1274ab95646SHenry Tieman 	rule = ice_fdir_find_fltr_by_idx(hw, fsp->location);
1284ab95646SHenry Tieman 
1294ab95646SHenry Tieman 	if (!rule || fsp->location != rule->fltr_id) {
1304ab95646SHenry Tieman 		ret = -EINVAL;
1314ab95646SHenry Tieman 		goto release_lock;
1324ab95646SHenry Tieman 	}
1334ab95646SHenry Tieman 
1344ab95646SHenry Tieman 	fsp->flow_type = ice_fltr_to_ethtool_flow(rule->flow_type);
1354ab95646SHenry Tieman 
1364ab95646SHenry Tieman 	memset(&fsp->m_u, 0, sizeof(fsp->m_u));
1374ab95646SHenry Tieman 	memset(&fsp->m_ext, 0, sizeof(fsp->m_ext));
1384ab95646SHenry Tieman 
1394ab95646SHenry Tieman 	switch (fsp->flow_type) {
1404ab95646SHenry Tieman 	case IPV4_USER_FLOW:
1414ab95646SHenry Tieman 		fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
1424ab95646SHenry Tieman 		fsp->h_u.usr_ip4_spec.proto = 0;
143165d80d6SHenry Tieman 		fsp->h_u.usr_ip4_spec.l4_4_bytes = rule->ip.v4.l4_header;
144165d80d6SHenry Tieman 		fsp->h_u.usr_ip4_spec.tos = rule->ip.v4.tos;
145165d80d6SHenry Tieman 		fsp->h_u.usr_ip4_spec.ip4src = rule->ip.v4.src_ip;
146165d80d6SHenry Tieman 		fsp->h_u.usr_ip4_spec.ip4dst = rule->ip.v4.dst_ip;
147165d80d6SHenry Tieman 		fsp->m_u.usr_ip4_spec.ip4src = rule->mask.v4.src_ip;
148165d80d6SHenry Tieman 		fsp->m_u.usr_ip4_spec.ip4dst = rule->mask.v4.dst_ip;
1494ab95646SHenry Tieman 		fsp->m_u.usr_ip4_spec.ip_ver = 0xFF;
1504ab95646SHenry Tieman 		fsp->m_u.usr_ip4_spec.proto = 0;
151165d80d6SHenry Tieman 		fsp->m_u.usr_ip4_spec.l4_4_bytes = rule->mask.v4.l4_header;
152165d80d6SHenry Tieman 		fsp->m_u.usr_ip4_spec.tos = rule->mask.v4.tos;
1534ab95646SHenry Tieman 		break;
1544ab95646SHenry Tieman 	case TCP_V4_FLOW:
1554ab95646SHenry Tieman 	case UDP_V4_FLOW:
1564ab95646SHenry Tieman 	case SCTP_V4_FLOW:
157165d80d6SHenry Tieman 		fsp->h_u.tcp_ip4_spec.psrc = rule->ip.v4.src_port;
158165d80d6SHenry Tieman 		fsp->h_u.tcp_ip4_spec.pdst = rule->ip.v4.dst_port;
159165d80d6SHenry Tieman 		fsp->h_u.tcp_ip4_spec.ip4src = rule->ip.v4.src_ip;
160165d80d6SHenry Tieman 		fsp->h_u.tcp_ip4_spec.ip4dst = rule->ip.v4.dst_ip;
161165d80d6SHenry Tieman 		fsp->m_u.tcp_ip4_spec.psrc = rule->mask.v4.src_port;
162165d80d6SHenry Tieman 		fsp->m_u.tcp_ip4_spec.pdst = rule->mask.v4.dst_port;
163165d80d6SHenry Tieman 		fsp->m_u.tcp_ip4_spec.ip4src = rule->mask.v4.src_ip;
164165d80d6SHenry Tieman 		fsp->m_u.tcp_ip4_spec.ip4dst = rule->mask.v4.dst_ip;
165165d80d6SHenry Tieman 		break;
166165d80d6SHenry Tieman 	case IPV6_USER_FLOW:
167165d80d6SHenry Tieman 		fsp->h_u.usr_ip6_spec.l4_4_bytes = rule->ip.v6.l4_header;
168165d80d6SHenry Tieman 		fsp->h_u.usr_ip6_spec.tclass = rule->ip.v6.tc;
169165d80d6SHenry Tieman 		fsp->h_u.usr_ip6_spec.l4_proto = rule->ip.v6.proto;
170165d80d6SHenry Tieman 		memcpy(fsp->h_u.tcp_ip6_spec.ip6src, rule->ip.v6.src_ip,
171165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
172165d80d6SHenry Tieman 		memcpy(fsp->h_u.tcp_ip6_spec.ip6dst, rule->ip.v6.dst_ip,
173165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
174165d80d6SHenry Tieman 		memcpy(fsp->m_u.tcp_ip6_spec.ip6src, rule->mask.v6.src_ip,
175165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
176165d80d6SHenry Tieman 		memcpy(fsp->m_u.tcp_ip6_spec.ip6dst, rule->mask.v6.dst_ip,
177165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
178165d80d6SHenry Tieman 		fsp->m_u.usr_ip6_spec.l4_4_bytes = rule->mask.v6.l4_header;
179165d80d6SHenry Tieman 		fsp->m_u.usr_ip6_spec.tclass = rule->mask.v6.tc;
180165d80d6SHenry Tieman 		fsp->m_u.usr_ip6_spec.l4_proto = rule->mask.v6.proto;
181165d80d6SHenry Tieman 		break;
182165d80d6SHenry Tieman 	case TCP_V6_FLOW:
183165d80d6SHenry Tieman 	case UDP_V6_FLOW:
184165d80d6SHenry Tieman 	case SCTP_V6_FLOW:
185165d80d6SHenry Tieman 		memcpy(fsp->h_u.tcp_ip6_spec.ip6src, rule->ip.v6.src_ip,
186165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
187165d80d6SHenry Tieman 		memcpy(fsp->h_u.tcp_ip6_spec.ip6dst, rule->ip.v6.dst_ip,
188165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
189165d80d6SHenry Tieman 		fsp->h_u.tcp_ip6_spec.psrc = rule->ip.v6.src_port;
190165d80d6SHenry Tieman 		fsp->h_u.tcp_ip6_spec.pdst = rule->ip.v6.dst_port;
191165d80d6SHenry Tieman 		memcpy(fsp->m_u.tcp_ip6_spec.ip6src,
192165d80d6SHenry Tieman 		       rule->mask.v6.src_ip,
193165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
194165d80d6SHenry Tieman 		memcpy(fsp->m_u.tcp_ip6_spec.ip6dst,
195165d80d6SHenry Tieman 		       rule->mask.v6.dst_ip,
196165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
197165d80d6SHenry Tieman 		fsp->m_u.tcp_ip6_spec.psrc = rule->mask.v6.src_port;
198165d80d6SHenry Tieman 		fsp->m_u.tcp_ip6_spec.pdst = rule->mask.v6.dst_port;
199165d80d6SHenry Tieman 		fsp->h_u.tcp_ip6_spec.tclass = rule->ip.v6.tc;
200165d80d6SHenry Tieman 		fsp->m_u.tcp_ip6_spec.tclass = rule->mask.v6.tc;
2014ab95646SHenry Tieman 		break;
2024ab95646SHenry Tieman 	default:
2034ab95646SHenry Tieman 		break;
2044ab95646SHenry Tieman 	}
2054ab95646SHenry Tieman 
2064ab95646SHenry Tieman 	if (rule->dest_ctl == ICE_FLTR_PRGM_DESC_DEST_DROP_PKT)
2074ab95646SHenry Tieman 		fsp->ring_cookie = RX_CLS_FLOW_DISC;
2084ab95646SHenry Tieman 	else
20940319796SKiran Patil 		fsp->ring_cookie = rule->orig_q_index;
2104ab95646SHenry Tieman 
2114ab95646SHenry Tieman 	idx = ice_ethtool_flow_to_fltr(fsp->flow_type);
2124ab95646SHenry Tieman 	if (idx == ICE_FLTR_PTYPE_NONF_NONE) {
2134ab95646SHenry Tieman 		dev_err(ice_hw_to_dev(hw), "Missing input index for flow_type %d\n",
2144ab95646SHenry Tieman 			rule->flow_type);
2154ab95646SHenry Tieman 		ret = -EINVAL;
2164ab95646SHenry Tieman 	}
2174ab95646SHenry Tieman 
2184ab95646SHenry Tieman release_lock:
2194ab95646SHenry Tieman 	mutex_unlock(&hw->fdir_fltr_lock);
2204ab95646SHenry Tieman 	return ret;
2214ab95646SHenry Tieman }
2224ab95646SHenry Tieman 
2234ab95646SHenry Tieman /**
2244ab95646SHenry Tieman  * ice_get_fdir_fltr_ids - fill buffer with filter IDs of active filters
2254ab95646SHenry Tieman  * @hw: hardware structure containing the filter list
2264ab95646SHenry Tieman  * @cmd: ethtool command data structure
2274ab95646SHenry Tieman  * @rule_locs: ethtool array passed in from OS to receive filter IDs
2284ab95646SHenry Tieman  *
2294ab95646SHenry Tieman  * Returns 0 as expected for success by ethtool
2304ab95646SHenry Tieman  */
2314ab95646SHenry Tieman int
ice_get_fdir_fltr_ids(struct ice_hw * hw,struct ethtool_rxnfc * cmd,u32 * rule_locs)2324ab95646SHenry Tieman ice_get_fdir_fltr_ids(struct ice_hw *hw, struct ethtool_rxnfc *cmd,
2334ab95646SHenry Tieman 		      u32 *rule_locs)
2344ab95646SHenry Tieman {
2354ab95646SHenry Tieman 	struct ice_fdir_fltr *f_rule;
2364ab95646SHenry Tieman 	unsigned int cnt = 0;
2374ab95646SHenry Tieman 	int val = 0;
2384ab95646SHenry Tieman 
2394ab95646SHenry Tieman 	/* report total rule count */
2404ab95646SHenry Tieman 	cmd->data = ice_get_fdir_cnt_all(hw);
2414ab95646SHenry Tieman 
2424ab95646SHenry Tieman 	mutex_lock(&hw->fdir_fltr_lock);
2434ab95646SHenry Tieman 
2444ab95646SHenry Tieman 	list_for_each_entry(f_rule, &hw->fdir_list_head, fltr_node) {
2454ab95646SHenry Tieman 		if (cnt == cmd->rule_cnt) {
2464ab95646SHenry Tieman 			val = -EMSGSIZE;
2474ab95646SHenry Tieman 			goto release_lock;
2484ab95646SHenry Tieman 		}
2494ab95646SHenry Tieman 		rule_locs[cnt] = f_rule->fltr_id;
2504ab95646SHenry Tieman 		cnt++;
2514ab95646SHenry Tieman 	}
2524ab95646SHenry Tieman 
2534ab95646SHenry Tieman release_lock:
2544ab95646SHenry Tieman 	mutex_unlock(&hw->fdir_fltr_lock);
2554ab95646SHenry Tieman 	if (!val)
2564ab95646SHenry Tieman 		cmd->rule_cnt = cnt;
2574ab95646SHenry Tieman 	return val;
2584ab95646SHenry Tieman }
2594ab95646SHenry Tieman 
2604ab95646SHenry Tieman /**
26140319796SKiran Patil  * ice_fdir_remap_entries - update the FDir entries in profile
26240319796SKiran Patil  * @prof: FDir structure pointer
26340319796SKiran Patil  * @tun: tunneled or non-tunneled packet
26440319796SKiran Patil  * @idx: FDir entry index
26540319796SKiran Patil  */
26640319796SKiran Patil static void
ice_fdir_remap_entries(struct ice_fd_hw_prof * prof,int tun,int idx)26740319796SKiran Patil ice_fdir_remap_entries(struct ice_fd_hw_prof *prof, int tun, int idx)
26840319796SKiran Patil {
26940319796SKiran Patil 	if (idx != prof->cnt && tun < ICE_FD_HW_SEG_MAX) {
27040319796SKiran Patil 		int i;
27140319796SKiran Patil 
27240319796SKiran Patil 		for (i = idx; i < (prof->cnt - 1); i++) {
27340319796SKiran Patil 			u64 old_entry_h;
27440319796SKiran Patil 
27540319796SKiran Patil 			old_entry_h = prof->entry_h[i + 1][tun];
27640319796SKiran Patil 			prof->entry_h[i][tun] = old_entry_h;
27740319796SKiran Patil 			prof->vsi_h[i] = prof->vsi_h[i + 1];
27840319796SKiran Patil 		}
27940319796SKiran Patil 
28040319796SKiran Patil 		prof->entry_h[i][tun] = 0;
28140319796SKiran Patil 		prof->vsi_h[i] = 0;
28240319796SKiran Patil 	}
28340319796SKiran Patil }
28440319796SKiran Patil 
28540319796SKiran Patil /**
28640319796SKiran Patil  * ice_fdir_rem_adq_chnl - remove an ADQ channel from HW filter rules
28740319796SKiran Patil  * @hw: hardware structure containing filter list
28840319796SKiran Patil  * @vsi_idx: VSI handle
28940319796SKiran Patil  */
ice_fdir_rem_adq_chnl(struct ice_hw * hw,u16 vsi_idx)29040319796SKiran Patil void ice_fdir_rem_adq_chnl(struct ice_hw *hw, u16 vsi_idx)
29140319796SKiran Patil {
29240319796SKiran Patil 	int status, flow;
29340319796SKiran Patil 
29440319796SKiran Patil 	if (!hw->fdir_prof)
29540319796SKiran Patil 		return;
29640319796SKiran Patil 
29740319796SKiran Patil 	for (flow = 0; flow < ICE_FLTR_PTYPE_MAX; flow++) {
29840319796SKiran Patil 		struct ice_fd_hw_prof *prof = hw->fdir_prof[flow];
29940319796SKiran Patil 		int tun, i;
30040319796SKiran Patil 
30140319796SKiran Patil 		if (!prof || !prof->cnt)
30240319796SKiran Patil 			continue;
30340319796SKiran Patil 
30440319796SKiran Patil 		for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
30540319796SKiran Patil 			u64 prof_id;
30640319796SKiran Patil 
30740319796SKiran Patil 			prof_id = flow + tun * ICE_FLTR_PTYPE_MAX;
30840319796SKiran Patil 
30940319796SKiran Patil 			for (i = 0; i < prof->cnt; i++) {
31040319796SKiran Patil 				if (prof->vsi_h[i] != vsi_idx)
31140319796SKiran Patil 					continue;
31240319796SKiran Patil 
31340319796SKiran Patil 				prof->entry_h[i][tun] = 0;
31440319796SKiran Patil 				prof->vsi_h[i] = 0;
31540319796SKiran Patil 				break;
31640319796SKiran Patil 			}
31740319796SKiran Patil 
31840319796SKiran Patil 			/* after clearing FDir entries update the remaining */
31940319796SKiran Patil 			ice_fdir_remap_entries(prof, tun, i);
32040319796SKiran Patil 
32140319796SKiran Patil 			/* find flow profile corresponding to prof_id and clear
32240319796SKiran Patil 			 * vsi_idx from bitmap.
32340319796SKiran Patil 			 */
32440319796SKiran Patil 			status = ice_flow_rem_vsi_prof(hw, vsi_idx, prof_id);
32540319796SKiran Patil 			if (status) {
32640319796SKiran Patil 				dev_err(ice_hw_to_dev(hw), "ice_flow_rem_vsi_prof() failed status=%d\n",
32740319796SKiran Patil 					status);
32840319796SKiran Patil 			}
32940319796SKiran Patil 		}
33040319796SKiran Patil 		prof->cnt--;
33140319796SKiran Patil 	}
33240319796SKiran Patil }
33340319796SKiran Patil 
33440319796SKiran Patil /**
335148beb61SHenry Tieman  * ice_fdir_get_hw_prof - return the ice_fd_hw_proc associated with a flow
336148beb61SHenry Tieman  * @hw: hardware structure containing the filter list
337148beb61SHenry Tieman  * @blk: hardware block
338148beb61SHenry Tieman  * @flow: FDir flow type to release
339148beb61SHenry Tieman  */
340148beb61SHenry Tieman static struct ice_fd_hw_prof *
ice_fdir_get_hw_prof(struct ice_hw * hw,enum ice_block blk,int flow)341148beb61SHenry Tieman ice_fdir_get_hw_prof(struct ice_hw *hw, enum ice_block blk, int flow)
342148beb61SHenry Tieman {
343148beb61SHenry Tieman 	if (blk == ICE_BLK_FD && hw->fdir_prof)
344148beb61SHenry Tieman 		return hw->fdir_prof[flow];
345148beb61SHenry Tieman 
346148beb61SHenry Tieman 	return NULL;
347148beb61SHenry Tieman }
348148beb61SHenry Tieman 
349148beb61SHenry Tieman /**
350148beb61SHenry Tieman  * ice_fdir_erase_flow_from_hw - remove a flow from the HW profile tables
351148beb61SHenry Tieman  * @hw: hardware structure containing the filter list
352148beb61SHenry Tieman  * @blk: hardware block
353148beb61SHenry Tieman  * @flow: FDir flow type to release
354148beb61SHenry Tieman  */
355148beb61SHenry Tieman static void
ice_fdir_erase_flow_from_hw(struct ice_hw * hw,enum ice_block blk,int flow)356148beb61SHenry Tieman ice_fdir_erase_flow_from_hw(struct ice_hw *hw, enum ice_block blk, int flow)
357148beb61SHenry Tieman {
358148beb61SHenry Tieman 	struct ice_fd_hw_prof *prof = ice_fdir_get_hw_prof(hw, blk, flow);
359148beb61SHenry Tieman 	int tun;
360148beb61SHenry Tieman 
361148beb61SHenry Tieman 	if (!prof)
362148beb61SHenry Tieman 		return;
363148beb61SHenry Tieman 
364148beb61SHenry Tieman 	for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
365148beb61SHenry Tieman 		u64 prof_id;
366148beb61SHenry Tieman 		int j;
367148beb61SHenry Tieman 
368148beb61SHenry Tieman 		prof_id = flow + tun * ICE_FLTR_PTYPE_MAX;
369148beb61SHenry Tieman 		for (j = 0; j < prof->cnt; j++) {
370148beb61SHenry Tieman 			u16 vsi_num;
371148beb61SHenry Tieman 
372148beb61SHenry Tieman 			if (!prof->entry_h[j][tun] || !prof->vsi_h[j])
373148beb61SHenry Tieman 				continue;
374148beb61SHenry Tieman 			vsi_num = ice_get_hw_vsi_num(hw, prof->vsi_h[j]);
375148beb61SHenry Tieman 			ice_rem_prof_id_flow(hw, blk, vsi_num, prof_id);
376148beb61SHenry Tieman 			ice_flow_rem_entry(hw, blk, prof->entry_h[j][tun]);
377148beb61SHenry Tieman 			prof->entry_h[j][tun] = 0;
378148beb61SHenry Tieman 		}
379148beb61SHenry Tieman 		ice_flow_rem_prof(hw, blk, prof_id);
380148beb61SHenry Tieman 	}
381148beb61SHenry Tieman }
382148beb61SHenry Tieman 
383148beb61SHenry Tieman /**
384148beb61SHenry Tieman  * ice_fdir_rem_flow - release the ice_flow structures for a filter type
385148beb61SHenry Tieman  * @hw: hardware structure containing the filter list
386148beb61SHenry Tieman  * @blk: hardware block
387148beb61SHenry Tieman  * @flow_type: FDir flow type to release
388148beb61SHenry Tieman  */
389148beb61SHenry Tieman static void
ice_fdir_rem_flow(struct ice_hw * hw,enum ice_block blk,enum ice_fltr_ptype flow_type)390148beb61SHenry Tieman ice_fdir_rem_flow(struct ice_hw *hw, enum ice_block blk,
391148beb61SHenry Tieman 		  enum ice_fltr_ptype flow_type)
392148beb61SHenry Tieman {
393148beb61SHenry Tieman 	int flow = (int)flow_type & ~FLOW_EXT;
394148beb61SHenry Tieman 	struct ice_fd_hw_prof *prof;
395148beb61SHenry Tieman 	int tun, i;
396148beb61SHenry Tieman 
397148beb61SHenry Tieman 	prof = ice_fdir_get_hw_prof(hw, blk, flow);
398148beb61SHenry Tieman 	if (!prof)
399148beb61SHenry Tieman 		return;
400148beb61SHenry Tieman 
401148beb61SHenry Tieman 	ice_fdir_erase_flow_from_hw(hw, blk, flow);
402148beb61SHenry Tieman 	for (i = 0; i < prof->cnt; i++)
403148beb61SHenry Tieman 		prof->vsi_h[i] = 0;
404148beb61SHenry Tieman 	for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
405148beb61SHenry Tieman 		if (!prof->fdir_seg[tun])
406148beb61SHenry Tieman 			continue;
407148beb61SHenry Tieman 		devm_kfree(ice_hw_to_dev(hw), prof->fdir_seg[tun]);
408148beb61SHenry Tieman 		prof->fdir_seg[tun] = NULL;
409148beb61SHenry Tieman 	}
410148beb61SHenry Tieman 	prof->cnt = 0;
411148beb61SHenry Tieman }
412148beb61SHenry Tieman 
413148beb61SHenry Tieman /**
414148beb61SHenry Tieman  * ice_fdir_release_flows - release all flows in use for later replay
415148beb61SHenry Tieman  * @hw: pointer to HW instance
416148beb61SHenry Tieman  */
ice_fdir_release_flows(struct ice_hw * hw)417148beb61SHenry Tieman void ice_fdir_release_flows(struct ice_hw *hw)
418148beb61SHenry Tieman {
419148beb61SHenry Tieman 	int flow;
420148beb61SHenry Tieman 
421148beb61SHenry Tieman 	/* release Flow Director HW table entries */
422148beb61SHenry Tieman 	for (flow = 0; flow < ICE_FLTR_PTYPE_MAX; flow++)
423148beb61SHenry Tieman 		ice_fdir_erase_flow_from_hw(hw, ICE_BLK_FD, flow);
424148beb61SHenry Tieman }
425148beb61SHenry Tieman 
426148beb61SHenry Tieman /**
42783af0039SHenry Tieman  * ice_fdir_replay_flows - replay HW Flow Director filter info
42883af0039SHenry Tieman  * @hw: pointer to HW instance
42983af0039SHenry Tieman  */
ice_fdir_replay_flows(struct ice_hw * hw)43083af0039SHenry Tieman void ice_fdir_replay_flows(struct ice_hw *hw)
43183af0039SHenry Tieman {
43283af0039SHenry Tieman 	int flow;
43383af0039SHenry Tieman 
43483af0039SHenry Tieman 	for (flow = 0; flow < ICE_FLTR_PTYPE_MAX; flow++) {
43583af0039SHenry Tieman 		int tun;
43683af0039SHenry Tieman 
43783af0039SHenry Tieman 		if (!hw->fdir_prof[flow] || !hw->fdir_prof[flow]->cnt)
43883af0039SHenry Tieman 			continue;
43983af0039SHenry Tieman 		for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
44083af0039SHenry Tieman 			struct ice_flow_prof *hw_prof;
44183af0039SHenry Tieman 			struct ice_fd_hw_prof *prof;
44283af0039SHenry Tieman 			u64 prof_id;
44383af0039SHenry Tieman 			int j;
44483af0039SHenry Tieman 
44583af0039SHenry Tieman 			prof = hw->fdir_prof[flow];
44683af0039SHenry Tieman 			prof_id = flow + tun * ICE_FLTR_PTYPE_MAX;
44783af0039SHenry Tieman 			ice_flow_add_prof(hw, ICE_BLK_FD, ICE_FLOW_RX, prof_id,
44883af0039SHenry Tieman 					  prof->fdir_seg[tun], TNL_SEG_CNT(tun),
44983af0039SHenry Tieman 					  &hw_prof);
45083af0039SHenry Tieman 			for (j = 0; j < prof->cnt; j++) {
45183af0039SHenry Tieman 				enum ice_flow_priority prio;
45283af0039SHenry Tieman 				u64 entry_h = 0;
45383af0039SHenry Tieman 				int err;
45483af0039SHenry Tieman 
45583af0039SHenry Tieman 				prio = ICE_FLOW_PRIO_NORMAL;
45683af0039SHenry Tieman 				err = ice_flow_add_entry(hw, ICE_BLK_FD,
45783af0039SHenry Tieman 							 prof_id,
45883af0039SHenry Tieman 							 prof->vsi_h[0],
45983af0039SHenry Tieman 							 prof->vsi_h[j],
46083af0039SHenry Tieman 							 prio, prof->fdir_seg,
46183af0039SHenry Tieman 							 &entry_h);
46283af0039SHenry Tieman 				if (err) {
46383af0039SHenry Tieman 					dev_err(ice_hw_to_dev(hw), "Could not replay Flow Director, flow type %d\n",
46483af0039SHenry Tieman 						flow);
46583af0039SHenry Tieman 					continue;
46683af0039SHenry Tieman 				}
46783af0039SHenry Tieman 				prof->entry_h[j][tun] = entry_h;
46883af0039SHenry Tieman 			}
46983af0039SHenry Tieman 		}
47083af0039SHenry Tieman 	}
47183af0039SHenry Tieman }
47283af0039SHenry Tieman 
47383af0039SHenry Tieman /**
4742c57ffcbSHenry Tieman  * ice_parse_rx_flow_user_data - deconstruct user-defined data
4752c57ffcbSHenry Tieman  * @fsp: pointer to ethtool Rx flow specification
4762c57ffcbSHenry Tieman  * @data: pointer to userdef data structure for storage
4772c57ffcbSHenry Tieman  *
4782c57ffcbSHenry Tieman  * Returns 0 on success, negative error value on failure
4792c57ffcbSHenry Tieman  */
4802c57ffcbSHenry Tieman static int
ice_parse_rx_flow_user_data(struct ethtool_rx_flow_spec * fsp,struct ice_rx_flow_userdef * data)4812c57ffcbSHenry Tieman ice_parse_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp,
4822c57ffcbSHenry Tieman 			    struct ice_rx_flow_userdef *data)
4832c57ffcbSHenry Tieman {
4842c57ffcbSHenry Tieman 	u64 value, mask;
4852c57ffcbSHenry Tieman 
4862c57ffcbSHenry Tieman 	memset(data, 0, sizeof(*data));
4872c57ffcbSHenry Tieman 	if (!(fsp->flow_type & FLOW_EXT))
4882c57ffcbSHenry Tieman 		return 0;
4892c57ffcbSHenry Tieman 
4902c57ffcbSHenry Tieman 	value = be64_to_cpu(*((__force __be64 *)fsp->h_ext.data));
4912c57ffcbSHenry Tieman 	mask = be64_to_cpu(*((__force __be64 *)fsp->m_ext.data));
4922c57ffcbSHenry Tieman 	if (!mask)
4932c57ffcbSHenry Tieman 		return 0;
4942c57ffcbSHenry Tieman 
4952c57ffcbSHenry Tieman #define ICE_USERDEF_FLEX_WORD_M	GENMASK_ULL(15, 0)
4962c57ffcbSHenry Tieman #define ICE_USERDEF_FLEX_OFFS_S	16
4972c57ffcbSHenry Tieman #define ICE_USERDEF_FLEX_OFFS_M	GENMASK_ULL(31, ICE_USERDEF_FLEX_OFFS_S)
4982c57ffcbSHenry Tieman #define ICE_USERDEF_FLEX_FLTR_M	GENMASK_ULL(31, 0)
4992c57ffcbSHenry Tieman 
5002c57ffcbSHenry Tieman 	/* 0x1fe is the maximum value for offsets stored in the internal
5012c57ffcbSHenry Tieman 	 * filtering tables.
5022c57ffcbSHenry Tieman 	 */
5032c57ffcbSHenry Tieman #define ICE_USERDEF_FLEX_MAX_OFFS_VAL 0x1fe
5042c57ffcbSHenry Tieman 
5052c57ffcbSHenry Tieman 	if (!ice_is_mask_valid(mask, ICE_USERDEF_FLEX_FLTR_M) ||
5062c57ffcbSHenry Tieman 	    value > ICE_USERDEF_FLEX_FLTR_M)
5072c57ffcbSHenry Tieman 		return -EINVAL;
5082c57ffcbSHenry Tieman 
5092c57ffcbSHenry Tieman 	data->flex_word = value & ICE_USERDEF_FLEX_WORD_M;
5102c57ffcbSHenry Tieman 	data->flex_offset = (value & ICE_USERDEF_FLEX_OFFS_M) >>
5112c57ffcbSHenry Tieman 			     ICE_USERDEF_FLEX_OFFS_S;
5122c57ffcbSHenry Tieman 	if (data->flex_offset > ICE_USERDEF_FLEX_MAX_OFFS_VAL)
5132c57ffcbSHenry Tieman 		return -EINVAL;
5142c57ffcbSHenry Tieman 
5152c57ffcbSHenry Tieman 	data->flex_fltr = true;
5162c57ffcbSHenry Tieman 
5172c57ffcbSHenry Tieman 	return 0;
5182c57ffcbSHenry Tieman }
5192c57ffcbSHenry Tieman 
5202c57ffcbSHenry Tieman /**
521cac2a27cSHenry Tieman  * ice_fdir_num_avail_fltr - return the number of unused flow director filters
522cac2a27cSHenry Tieman  * @hw: pointer to hardware structure
523cac2a27cSHenry Tieman  * @vsi: software VSI structure
524cac2a27cSHenry Tieman  *
525cac2a27cSHenry Tieman  * There are 2 filter pools: guaranteed and best effort(shared). Each VSI can
526cac2a27cSHenry Tieman  * use filters from either pool. The guaranteed pool is divided between VSIs.
527cac2a27cSHenry Tieman  * The best effort filter pool is common to all VSIs and is a device shared
528cac2a27cSHenry Tieman  * resource pool. The number of filters available to this VSI is the sum of
529cac2a27cSHenry Tieman  * the VSIs guaranteed filter pool and the global available best effort
530cac2a27cSHenry Tieman  * filter pool.
531cac2a27cSHenry Tieman  *
532cac2a27cSHenry Tieman  * Returns the number of available flow director filters to this VSI
533cac2a27cSHenry Tieman  */
ice_fdir_num_avail_fltr(struct ice_hw * hw,struct ice_vsi * vsi)534cac2a27cSHenry Tieman static int ice_fdir_num_avail_fltr(struct ice_hw *hw, struct ice_vsi *vsi)
535cac2a27cSHenry Tieman {
536cac2a27cSHenry Tieman 	u16 vsi_num = ice_get_hw_vsi_num(hw, vsi->idx);
537cac2a27cSHenry Tieman 	u16 num_guar;
538cac2a27cSHenry Tieman 	u16 num_be;
539cac2a27cSHenry Tieman 
540cac2a27cSHenry Tieman 	/* total guaranteed filters assigned to this VSI */
541cac2a27cSHenry Tieman 	num_guar = vsi->num_gfltr;
542cac2a27cSHenry Tieman 
543cac2a27cSHenry Tieman 	/* minus the guaranteed filters programed by this VSI */
544cac2a27cSHenry Tieman 	num_guar -= (rd32(hw, VSIQF_FD_CNT(vsi_num)) &
545cac2a27cSHenry Tieman 		     VSIQF_FD_CNT_FD_GCNT_M) >> VSIQF_FD_CNT_FD_GCNT_S;
546cac2a27cSHenry Tieman 
547cac2a27cSHenry Tieman 	/* total global best effort filters */
548cac2a27cSHenry Tieman 	num_be = hw->func_caps.fd_fltr_best_effort;
549cac2a27cSHenry Tieman 
550cac2a27cSHenry Tieman 	/* minus the global best effort filters programmed */
551cac2a27cSHenry Tieman 	num_be -= (rd32(hw, GLQF_FD_CNT) & GLQF_FD_CNT_FD_BCNT_M) >>
552cac2a27cSHenry Tieman 		   GLQF_FD_CNT_FD_BCNT_S;
553cac2a27cSHenry Tieman 
554cac2a27cSHenry Tieman 	return num_guar + num_be;
555cac2a27cSHenry Tieman }
556cac2a27cSHenry Tieman 
557cac2a27cSHenry Tieman /**
558148beb61SHenry Tieman  * ice_fdir_alloc_flow_prof - allocate FDir flow profile structure(s)
559148beb61SHenry Tieman  * @hw: HW structure containing the FDir flow profile structure(s)
560148beb61SHenry Tieman  * @flow: flow type to allocate the flow profile for
561148beb61SHenry Tieman  *
562148beb61SHenry Tieman  * Allocate the fdir_prof and fdir_prof[flow] if not already created. Return 0
563148beb61SHenry Tieman  * on success and negative on error.
564148beb61SHenry Tieman  */
565148beb61SHenry Tieman static int
ice_fdir_alloc_flow_prof(struct ice_hw * hw,enum ice_fltr_ptype flow)566148beb61SHenry Tieman ice_fdir_alloc_flow_prof(struct ice_hw *hw, enum ice_fltr_ptype flow)
567148beb61SHenry Tieman {
568148beb61SHenry Tieman 	if (!hw)
569148beb61SHenry Tieman 		return -EINVAL;
570148beb61SHenry Tieman 
571148beb61SHenry Tieman 	if (!hw->fdir_prof) {
572148beb61SHenry Tieman 		hw->fdir_prof = devm_kcalloc(ice_hw_to_dev(hw),
573148beb61SHenry Tieman 					     ICE_FLTR_PTYPE_MAX,
574148beb61SHenry Tieman 					     sizeof(*hw->fdir_prof),
575148beb61SHenry Tieman 					     GFP_KERNEL);
576148beb61SHenry Tieman 		if (!hw->fdir_prof)
577148beb61SHenry Tieman 			return -ENOMEM;
578148beb61SHenry Tieman 	}
579148beb61SHenry Tieman 
580148beb61SHenry Tieman 	if (!hw->fdir_prof[flow]) {
581148beb61SHenry Tieman 		hw->fdir_prof[flow] = devm_kzalloc(ice_hw_to_dev(hw),
582148beb61SHenry Tieman 						   sizeof(**hw->fdir_prof),
583148beb61SHenry Tieman 						   GFP_KERNEL);
584148beb61SHenry Tieman 		if (!hw->fdir_prof[flow])
585148beb61SHenry Tieman 			return -ENOMEM;
586148beb61SHenry Tieman 	}
587148beb61SHenry Tieman 
588148beb61SHenry Tieman 	return 0;
589148beb61SHenry Tieman }
590148beb61SHenry Tieman 
591148beb61SHenry Tieman /**
59240319796SKiran Patil  * ice_fdir_prof_vsi_idx - find or insert a vsi_idx in structure
59340319796SKiran Patil  * @prof: pointer to flow director HW profile
59440319796SKiran Patil  * @vsi_idx: vsi_idx to locate
59540319796SKiran Patil  *
59640319796SKiran Patil  * return the index of the vsi_idx. if vsi_idx is not found insert it
59740319796SKiran Patil  * into the vsi_h table.
59840319796SKiran Patil  */
59940319796SKiran Patil static u16
ice_fdir_prof_vsi_idx(struct ice_fd_hw_prof * prof,int vsi_idx)60040319796SKiran Patil ice_fdir_prof_vsi_idx(struct ice_fd_hw_prof *prof, int vsi_idx)
60140319796SKiran Patil {
60240319796SKiran Patil 	u16 idx = 0;
60340319796SKiran Patil 
60440319796SKiran Patil 	for (idx = 0; idx < prof->cnt; idx++)
60540319796SKiran Patil 		if (prof->vsi_h[idx] == vsi_idx)
60640319796SKiran Patil 			return idx;
60740319796SKiran Patil 
60840319796SKiran Patil 	if (idx == prof->cnt)
60940319796SKiran Patil 		prof->vsi_h[prof->cnt++] = vsi_idx;
61040319796SKiran Patil 	return idx;
61140319796SKiran Patil }
61240319796SKiran Patil 
61340319796SKiran Patil /**
614148beb61SHenry Tieman  * ice_fdir_set_hw_fltr_rule - Configure HW tables to generate a FDir rule
615148beb61SHenry Tieman  * @pf: pointer to the PF structure
616148beb61SHenry Tieman  * @seg: protocol header description pointer
617148beb61SHenry Tieman  * @flow: filter enum
618148beb61SHenry Tieman  * @tun: FDir segment to program
619148beb61SHenry Tieman  */
620148beb61SHenry Tieman static int
ice_fdir_set_hw_fltr_rule(struct ice_pf * pf,struct ice_flow_seg_info * seg,enum ice_fltr_ptype flow,enum ice_fd_hw_seg tun)621148beb61SHenry Tieman ice_fdir_set_hw_fltr_rule(struct ice_pf *pf, struct ice_flow_seg_info *seg,
622148beb61SHenry Tieman 			  enum ice_fltr_ptype flow, enum ice_fd_hw_seg tun)
623148beb61SHenry Tieman {
624148beb61SHenry Tieman 	struct device *dev = ice_pf_to_dev(pf);
625148beb61SHenry Tieman 	struct ice_vsi *main_vsi, *ctrl_vsi;
626148beb61SHenry Tieman 	struct ice_flow_seg_info *old_seg;
627148beb61SHenry Tieman 	struct ice_flow_prof *prof = NULL;
628148beb61SHenry Tieman 	struct ice_fd_hw_prof *hw_prof;
629148beb61SHenry Tieman 	struct ice_hw *hw = &pf->hw;
630148beb61SHenry Tieman 	u64 entry1_h = 0;
631148beb61SHenry Tieman 	u64 entry2_h = 0;
63240319796SKiran Patil 	bool del_last;
633148beb61SHenry Tieman 	u64 prof_id;
634148beb61SHenry Tieman 	int err;
63540319796SKiran Patil 	int idx;
636148beb61SHenry Tieman 
637148beb61SHenry Tieman 	main_vsi = ice_get_main_vsi(pf);
638148beb61SHenry Tieman 	if (!main_vsi)
639148beb61SHenry Tieman 		return -EINVAL;
640148beb61SHenry Tieman 
641148beb61SHenry Tieman 	ctrl_vsi = ice_get_ctrl_vsi(pf);
642148beb61SHenry Tieman 	if (!ctrl_vsi)
643148beb61SHenry Tieman 		return -EINVAL;
644148beb61SHenry Tieman 
645148beb61SHenry Tieman 	err = ice_fdir_alloc_flow_prof(hw, flow);
646148beb61SHenry Tieman 	if (err)
647148beb61SHenry Tieman 		return err;
648148beb61SHenry Tieman 
649148beb61SHenry Tieman 	hw_prof = hw->fdir_prof[flow];
650148beb61SHenry Tieman 	old_seg = hw_prof->fdir_seg[tun];
651148beb61SHenry Tieman 	if (old_seg) {
652148beb61SHenry Tieman 		/* This flow_type already has a changed input set.
653148beb61SHenry Tieman 		 * If it matches the requested input set then we are
654148beb61SHenry Tieman 		 * done. Or, if it's different then it's an error.
655148beb61SHenry Tieman 		 */
656148beb61SHenry Tieman 		if (!memcmp(old_seg, seg, sizeof(*seg)))
657148beb61SHenry Tieman 			return -EEXIST;
658148beb61SHenry Tieman 
659cac2a27cSHenry Tieman 		/* if there are FDir filters using this flow,
660cac2a27cSHenry Tieman 		 * then return error.
661cac2a27cSHenry Tieman 		 */
662cac2a27cSHenry Tieman 		if (hw->fdir_fltr_cnt[flow]) {
663cac2a27cSHenry Tieman 			dev_err(dev, "Failed to add filter.  Flow director filters on each port must have the same input set.\n");
664cac2a27cSHenry Tieman 			return -EINVAL;
665cac2a27cSHenry Tieman 		}
666cac2a27cSHenry Tieman 
66728bf2672SBrett Creeley 		if (ice_is_arfs_using_perfect_flow(hw, flow)) {
66828bf2672SBrett Creeley 			dev_err(dev, "aRFS using perfect flow type %d, cannot change input set\n",
66928bf2672SBrett Creeley 				flow);
67028bf2672SBrett Creeley 			return -EINVAL;
67128bf2672SBrett Creeley 		}
67228bf2672SBrett Creeley 
673148beb61SHenry Tieman 		/* remove HW filter definition */
674148beb61SHenry Tieman 		ice_fdir_rem_flow(hw, ICE_BLK_FD, flow);
675148beb61SHenry Tieman 	}
676148beb61SHenry Tieman 
677148beb61SHenry Tieman 	/* Adding a profile, but there is only one header supported.
678148beb61SHenry Tieman 	 * That is the final parameters are 1 header (segment), no
679148beb61SHenry Tieman 	 * actions (NULL) and zero actions 0.
680148beb61SHenry Tieman 	 */
681148beb61SHenry Tieman 	prof_id = flow + tun * ICE_FLTR_PTYPE_MAX;
6822ccc1c1cSTony Nguyen 	err = ice_flow_add_prof(hw, ICE_BLK_FD, ICE_FLOW_RX, prof_id, seg,
683148beb61SHenry Tieman 				TNL_SEG_CNT(tun), &prof);
6842ccc1c1cSTony Nguyen 	if (err)
6852ccc1c1cSTony Nguyen 		return err;
6862ccc1c1cSTony Nguyen 	err = ice_flow_add_entry(hw, ICE_BLK_FD, prof_id, main_vsi->idx,
687148beb61SHenry Tieman 				 main_vsi->idx, ICE_FLOW_PRIO_NORMAL,
688148beb61SHenry Tieman 				 seg, &entry1_h);
6892ccc1c1cSTony Nguyen 	if (err)
690148beb61SHenry Tieman 		goto err_prof;
6912ccc1c1cSTony Nguyen 	err = ice_flow_add_entry(hw, ICE_BLK_FD, prof_id, main_vsi->idx,
692148beb61SHenry Tieman 				 ctrl_vsi->idx, ICE_FLOW_PRIO_NORMAL,
693148beb61SHenry Tieman 				 seg, &entry2_h);
6942ccc1c1cSTony Nguyen 	if (err)
695148beb61SHenry Tieman 		goto err_entry;
696148beb61SHenry Tieman 
697148beb61SHenry Tieman 	hw_prof->fdir_seg[tun] = seg;
698148beb61SHenry Tieman 	hw_prof->entry_h[0][tun] = entry1_h;
699148beb61SHenry Tieman 	hw_prof->entry_h[1][tun] = entry2_h;
700148beb61SHenry Tieman 	hw_prof->vsi_h[0] = main_vsi->idx;
701148beb61SHenry Tieman 	hw_prof->vsi_h[1] = ctrl_vsi->idx;
702148beb61SHenry Tieman 	if (!hw_prof->cnt)
703148beb61SHenry Tieman 		hw_prof->cnt = 2;
704148beb61SHenry Tieman 
70540319796SKiran Patil 	for (idx = 1; idx < ICE_CHNL_MAX_TC; idx++) {
70640319796SKiran Patil 		u16 vsi_idx;
70740319796SKiran Patil 		u16 vsi_h;
70840319796SKiran Patil 
70940319796SKiran Patil 		if (!ice_is_adq_active(pf) || !main_vsi->tc_map_vsi[idx])
71040319796SKiran Patil 			continue;
71140319796SKiran Patil 
71240319796SKiran Patil 		entry1_h = 0;
71340319796SKiran Patil 		vsi_h = main_vsi->tc_map_vsi[idx]->idx;
71440319796SKiran Patil 		err = ice_flow_add_entry(hw, ICE_BLK_FD, prof_id,
71540319796SKiran Patil 					 main_vsi->idx, vsi_h,
71640319796SKiran Patil 					 ICE_FLOW_PRIO_NORMAL, seg,
71740319796SKiran Patil 					 &entry1_h);
71840319796SKiran Patil 		if (err) {
71940319796SKiran Patil 			dev_err(dev, "Could not add Channel VSI %d to flow group\n",
72040319796SKiran Patil 				idx);
72140319796SKiran Patil 			goto err_unroll;
72240319796SKiran Patil 		}
72340319796SKiran Patil 
72440319796SKiran Patil 		vsi_idx = ice_fdir_prof_vsi_idx(hw_prof,
72540319796SKiran Patil 						main_vsi->tc_map_vsi[idx]->idx);
72640319796SKiran Patil 		hw_prof->entry_h[vsi_idx][tun] = entry1_h;
72740319796SKiran Patil 	}
72840319796SKiran Patil 
729148beb61SHenry Tieman 	return 0;
730148beb61SHenry Tieman 
73140319796SKiran Patil err_unroll:
73240319796SKiran Patil 	entry1_h = 0;
73340319796SKiran Patil 	hw_prof->fdir_seg[tun] = NULL;
73440319796SKiran Patil 
73540319796SKiran Patil 	/* The variable del_last will be used to determine when to clean up
73640319796SKiran Patil 	 * the VSI group data. The VSI data is not needed if there are no
73740319796SKiran Patil 	 * segments.
73840319796SKiran Patil 	 */
73940319796SKiran Patil 	del_last = true;
74040319796SKiran Patil 	for (idx = 0; idx < ICE_FD_HW_SEG_MAX; idx++)
74140319796SKiran Patil 		if (hw_prof->fdir_seg[idx]) {
74240319796SKiran Patil 			del_last = false;
74340319796SKiran Patil 			break;
74440319796SKiran Patil 		}
74540319796SKiran Patil 
74640319796SKiran Patil 	for (idx = 0; idx < hw_prof->cnt; idx++) {
74740319796SKiran Patil 		u16 vsi_num = ice_get_hw_vsi_num(hw, hw_prof->vsi_h[idx]);
74840319796SKiran Patil 
74940319796SKiran Patil 		if (!hw_prof->entry_h[idx][tun])
75040319796SKiran Patil 			continue;
75140319796SKiran Patil 		ice_rem_prof_id_flow(hw, ICE_BLK_FD, vsi_num, prof_id);
75240319796SKiran Patil 		ice_flow_rem_entry(hw, ICE_BLK_FD, hw_prof->entry_h[idx][tun]);
75340319796SKiran Patil 		hw_prof->entry_h[idx][tun] = 0;
75440319796SKiran Patil 		if (del_last)
75540319796SKiran Patil 			hw_prof->vsi_h[idx] = 0;
75640319796SKiran Patil 	}
75740319796SKiran Patil 	if (del_last)
75840319796SKiran Patil 		hw_prof->cnt = 0;
759148beb61SHenry Tieman err_entry:
760148beb61SHenry Tieman 	ice_rem_prof_id_flow(hw, ICE_BLK_FD,
761148beb61SHenry Tieman 			     ice_get_hw_vsi_num(hw, main_vsi->idx), prof_id);
762148beb61SHenry Tieman 	ice_flow_rem_entry(hw, ICE_BLK_FD, entry1_h);
763148beb61SHenry Tieman err_prof:
764148beb61SHenry Tieman 	ice_flow_rem_prof(hw, ICE_BLK_FD, prof_id);
765148beb61SHenry Tieman 	dev_err(dev, "Failed to add filter.  Flow director filters on each port must have the same input set.\n");
766148beb61SHenry Tieman 
767148beb61SHenry Tieman 	return err;
768148beb61SHenry Tieman }
769148beb61SHenry Tieman 
770148beb61SHenry Tieman /**
771148beb61SHenry Tieman  * ice_set_init_fdir_seg
772148beb61SHenry Tieman  * @seg: flow segment for programming
773165d80d6SHenry Tieman  * @l3_proto: ICE_FLOW_SEG_HDR_IPV4 or ICE_FLOW_SEG_HDR_IPV6
774148beb61SHenry Tieman  * @l4_proto: ICE_FLOW_SEG_HDR_TCP or ICE_FLOW_SEG_HDR_UDP
775148beb61SHenry Tieman  *
776148beb61SHenry Tieman  * Set the configuration for perfect filters to the provided flow segment for
777148beb61SHenry Tieman  * programming the HW filter. This is to be called only when initializing
778148beb61SHenry Tieman  * filters as this function it assumes no filters exist.
779148beb61SHenry Tieman  */
780148beb61SHenry Tieman static int
ice_set_init_fdir_seg(struct ice_flow_seg_info * seg,enum ice_flow_seg_hdr l3_proto,enum ice_flow_seg_hdr l4_proto)781148beb61SHenry Tieman ice_set_init_fdir_seg(struct ice_flow_seg_info *seg,
782165d80d6SHenry Tieman 		      enum ice_flow_seg_hdr l3_proto,
783148beb61SHenry Tieman 		      enum ice_flow_seg_hdr l4_proto)
784148beb61SHenry Tieman {
785165d80d6SHenry Tieman 	enum ice_flow_field src_addr, dst_addr, src_port, dst_port;
786148beb61SHenry Tieman 
787148beb61SHenry Tieman 	if (!seg)
788148beb61SHenry Tieman 		return -EINVAL;
789148beb61SHenry Tieman 
790165d80d6SHenry Tieman 	if (l3_proto == ICE_FLOW_SEG_HDR_IPV4) {
791165d80d6SHenry Tieman 		src_addr = ICE_FLOW_FIELD_IDX_IPV4_SA;
792165d80d6SHenry Tieman 		dst_addr = ICE_FLOW_FIELD_IDX_IPV4_DA;
793165d80d6SHenry Tieman 	} else if (l3_proto == ICE_FLOW_SEG_HDR_IPV6) {
794165d80d6SHenry Tieman 		src_addr = ICE_FLOW_FIELD_IDX_IPV6_SA;
795165d80d6SHenry Tieman 		dst_addr = ICE_FLOW_FIELD_IDX_IPV6_DA;
796165d80d6SHenry Tieman 	} else {
797165d80d6SHenry Tieman 		return -EINVAL;
798165d80d6SHenry Tieman 	}
799165d80d6SHenry Tieman 
800148beb61SHenry Tieman 	if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
801148beb61SHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
802148beb61SHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
803148beb61SHenry Tieman 	} else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
804148beb61SHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
805148beb61SHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
806148beb61SHenry Tieman 	} else {
807148beb61SHenry Tieman 		return -EINVAL;
808148beb61SHenry Tieman 	}
809148beb61SHenry Tieman 
810165d80d6SHenry Tieman 	ICE_FLOW_SET_HDRS(seg, l3_proto | l4_proto);
811148beb61SHenry Tieman 
812148beb61SHenry Tieman 	/* IP source address */
813165d80d6SHenry Tieman 	ice_flow_set_fld(seg, src_addr, ICE_FLOW_FLD_OFF_INVAL,
814165d80d6SHenry Tieman 			 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
815148beb61SHenry Tieman 
816148beb61SHenry Tieman 	/* IP destination address */
817165d80d6SHenry Tieman 	ice_flow_set_fld(seg, dst_addr, ICE_FLOW_FLD_OFF_INVAL,
818165d80d6SHenry Tieman 			 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
819148beb61SHenry Tieman 
820148beb61SHenry Tieman 	/* Layer 4 source port */
821148beb61SHenry Tieman 	ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
822148beb61SHenry Tieman 			 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
823148beb61SHenry Tieman 
824148beb61SHenry Tieman 	/* Layer 4 destination port */
825148beb61SHenry Tieman 	ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
826148beb61SHenry Tieman 			 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
827148beb61SHenry Tieman 
828148beb61SHenry Tieman 	return 0;
829148beb61SHenry Tieman }
830148beb61SHenry Tieman 
831148beb61SHenry Tieman /**
832148beb61SHenry Tieman  * ice_create_init_fdir_rule
833148beb61SHenry Tieman  * @pf: PF structure
834148beb61SHenry Tieman  * @flow: filter enum
835148beb61SHenry Tieman  *
836148beb61SHenry Tieman  * Return error value or 0 on success.
837148beb61SHenry Tieman  */
838148beb61SHenry Tieman static int
ice_create_init_fdir_rule(struct ice_pf * pf,enum ice_fltr_ptype flow)839148beb61SHenry Tieman ice_create_init_fdir_rule(struct ice_pf *pf, enum ice_fltr_ptype flow)
840148beb61SHenry Tieman {
841148beb61SHenry Tieman 	struct ice_flow_seg_info *seg, *tun_seg;
842148beb61SHenry Tieman 	struct device *dev = ice_pf_to_dev(pf);
843148beb61SHenry Tieman 	struct ice_hw *hw = &pf->hw;
844148beb61SHenry Tieman 	int ret;
845148beb61SHenry Tieman 
846148beb61SHenry Tieman 	/* if there is already a filter rule for kind return -EINVAL */
847148beb61SHenry Tieman 	if (hw->fdir_prof && hw->fdir_prof[flow] &&
848148beb61SHenry Tieman 	    hw->fdir_prof[flow]->fdir_seg[0])
849148beb61SHenry Tieman 		return -EINVAL;
850148beb61SHenry Tieman 
851148beb61SHenry Tieman 	seg = devm_kzalloc(dev, sizeof(*seg), GFP_KERNEL);
852148beb61SHenry Tieman 	if (!seg)
853148beb61SHenry Tieman 		return -ENOMEM;
854148beb61SHenry Tieman 
855a4da4913SChristophe JAILLET 	tun_seg = devm_kcalloc(dev, ICE_FD_HW_SEG_MAX, sizeof(*tun_seg),
856148beb61SHenry Tieman 			       GFP_KERNEL);
857148beb61SHenry Tieman 	if (!tun_seg) {
858148beb61SHenry Tieman 		devm_kfree(dev, seg);
859148beb61SHenry Tieman 		return -ENOMEM;
860148beb61SHenry Tieman 	}
861148beb61SHenry Tieman 
862148beb61SHenry Tieman 	if (flow == ICE_FLTR_PTYPE_NONF_IPV4_TCP)
863165d80d6SHenry Tieman 		ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV4,
864165d80d6SHenry Tieman 					    ICE_FLOW_SEG_HDR_TCP);
865148beb61SHenry Tieman 	else if (flow == ICE_FLTR_PTYPE_NONF_IPV4_UDP)
866165d80d6SHenry Tieman 		ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV4,
867165d80d6SHenry Tieman 					    ICE_FLOW_SEG_HDR_UDP);
868165d80d6SHenry Tieman 	else if (flow == ICE_FLTR_PTYPE_NONF_IPV6_TCP)
869165d80d6SHenry Tieman 		ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV6,
870165d80d6SHenry Tieman 					    ICE_FLOW_SEG_HDR_TCP);
871165d80d6SHenry Tieman 	else if (flow == ICE_FLTR_PTYPE_NONF_IPV6_UDP)
872165d80d6SHenry Tieman 		ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV6,
873165d80d6SHenry Tieman 					    ICE_FLOW_SEG_HDR_UDP);
874148beb61SHenry Tieman 	else
875148beb61SHenry Tieman 		ret = -EINVAL;
876148beb61SHenry Tieman 	if (ret)
877148beb61SHenry Tieman 		goto err_exit;
878148beb61SHenry Tieman 
879148beb61SHenry Tieman 	/* add filter for outer headers */
880148beb61SHenry Tieman 	ret = ice_fdir_set_hw_fltr_rule(pf, seg, flow, ICE_FD_HW_SEG_NON_TUN);
881148beb61SHenry Tieman 	if (ret)
882148beb61SHenry Tieman 		/* could not write filter, free memory */
883148beb61SHenry Tieman 		goto err_exit;
884148beb61SHenry Tieman 
885148beb61SHenry Tieman 	/* make tunneled filter HW entries if possible */
886148beb61SHenry Tieman 	memcpy(&tun_seg[1], seg, sizeof(*seg));
887148beb61SHenry Tieman 	ret = ice_fdir_set_hw_fltr_rule(pf, tun_seg, flow, ICE_FD_HW_SEG_TUN);
888148beb61SHenry Tieman 	if (ret)
889148beb61SHenry Tieman 		/* could not write tunnel filter, but outer header filter
890148beb61SHenry Tieman 		 * exists
891148beb61SHenry Tieman 		 */
892148beb61SHenry Tieman 		devm_kfree(dev, tun_seg);
893148beb61SHenry Tieman 
894148beb61SHenry Tieman 	set_bit(flow, hw->fdir_perfect_fltr);
895148beb61SHenry Tieman 	return ret;
896148beb61SHenry Tieman err_exit:
897148beb61SHenry Tieman 	devm_kfree(dev, tun_seg);
898148beb61SHenry Tieman 	devm_kfree(dev, seg);
899148beb61SHenry Tieman 
900148beb61SHenry Tieman 	return -EOPNOTSUPP;
901148beb61SHenry Tieman }
902148beb61SHenry Tieman 
903148beb61SHenry Tieman /**
904cac2a27cSHenry Tieman  * ice_set_fdir_ip4_seg
905cac2a27cSHenry Tieman  * @seg: flow segment for programming
906cac2a27cSHenry Tieman  * @tcp_ip4_spec: mask data from ethtool
907cac2a27cSHenry Tieman  * @l4_proto: Layer 4 protocol to program
908cac2a27cSHenry Tieman  * @perfect_fltr: only valid on success; returns true if perfect filter,
909cac2a27cSHenry Tieman  *		  false if not
910cac2a27cSHenry Tieman  *
911cac2a27cSHenry Tieman  * Set the mask data into the flow segment to be used to program HW
912cac2a27cSHenry Tieman  * table based on provided L4 protocol for IPv4
913cac2a27cSHenry Tieman  */
914cac2a27cSHenry Tieman static int
ice_set_fdir_ip4_seg(struct ice_flow_seg_info * seg,struct ethtool_tcpip4_spec * tcp_ip4_spec,enum ice_flow_seg_hdr l4_proto,bool * perfect_fltr)915cac2a27cSHenry Tieman ice_set_fdir_ip4_seg(struct ice_flow_seg_info *seg,
916cac2a27cSHenry Tieman 		     struct ethtool_tcpip4_spec *tcp_ip4_spec,
917cac2a27cSHenry Tieman 		     enum ice_flow_seg_hdr l4_proto, bool *perfect_fltr)
918cac2a27cSHenry Tieman {
919cac2a27cSHenry Tieman 	enum ice_flow_field src_port, dst_port;
920cac2a27cSHenry Tieman 
921cac2a27cSHenry Tieman 	/* make sure we don't have any empty rule */
922cac2a27cSHenry Tieman 	if (!tcp_ip4_spec->psrc && !tcp_ip4_spec->ip4src &&
923cac2a27cSHenry Tieman 	    !tcp_ip4_spec->pdst && !tcp_ip4_spec->ip4dst)
924cac2a27cSHenry Tieman 		return -EINVAL;
925cac2a27cSHenry Tieman 
926cac2a27cSHenry Tieman 	/* filtering on TOS not supported */
927cac2a27cSHenry Tieman 	if (tcp_ip4_spec->tos)
928cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
929cac2a27cSHenry Tieman 
930cac2a27cSHenry Tieman 	if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
931cac2a27cSHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
932cac2a27cSHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
933cac2a27cSHenry Tieman 	} else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
934cac2a27cSHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
935cac2a27cSHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
936cac2a27cSHenry Tieman 	} else if (l4_proto == ICE_FLOW_SEG_HDR_SCTP) {
937cac2a27cSHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT;
938cac2a27cSHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_SCTP_DST_PORT;
939cac2a27cSHenry Tieman 	} else {
940cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
941cac2a27cSHenry Tieman 	}
942cac2a27cSHenry Tieman 
943cac2a27cSHenry Tieman 	*perfect_fltr = true;
944cac2a27cSHenry Tieman 	ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4 | l4_proto);
945cac2a27cSHenry Tieman 
946cac2a27cSHenry Tieman 	/* IP source address */
947cac2a27cSHenry Tieman 	if (tcp_ip4_spec->ip4src == htonl(0xFFFFFFFF))
948cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_SA,
949cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
950cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
951cac2a27cSHenry Tieman 	else if (!tcp_ip4_spec->ip4src)
952cac2a27cSHenry Tieman 		*perfect_fltr = false;
953cac2a27cSHenry Tieman 	else
954cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
955cac2a27cSHenry Tieman 
956cac2a27cSHenry Tieman 	/* IP destination address */
957cac2a27cSHenry Tieman 	if (tcp_ip4_spec->ip4dst == htonl(0xFFFFFFFF))
958cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_DA,
959cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
960cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
961cac2a27cSHenry Tieman 	else if (!tcp_ip4_spec->ip4dst)
962cac2a27cSHenry Tieman 		*perfect_fltr = false;
963cac2a27cSHenry Tieman 	else
964cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
965cac2a27cSHenry Tieman 
966cac2a27cSHenry Tieman 	/* Layer 4 source port */
967cac2a27cSHenry Tieman 	if (tcp_ip4_spec->psrc == htons(0xFFFF))
968cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
969cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
970cac2a27cSHenry Tieman 				 false);
971cac2a27cSHenry Tieman 	else if (!tcp_ip4_spec->psrc)
972cac2a27cSHenry Tieman 		*perfect_fltr = false;
973cac2a27cSHenry Tieman 	else
974cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
975cac2a27cSHenry Tieman 
976cac2a27cSHenry Tieman 	/* Layer 4 destination port */
977cac2a27cSHenry Tieman 	if (tcp_ip4_spec->pdst == htons(0xFFFF))
978cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
979cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
980cac2a27cSHenry Tieman 				 false);
981cac2a27cSHenry Tieman 	else if (!tcp_ip4_spec->pdst)
982cac2a27cSHenry Tieman 		*perfect_fltr = false;
983cac2a27cSHenry Tieman 	else
984cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
985cac2a27cSHenry Tieman 
986cac2a27cSHenry Tieman 	return 0;
987cac2a27cSHenry Tieman }
988cac2a27cSHenry Tieman 
989cac2a27cSHenry Tieman /**
990cac2a27cSHenry Tieman  * ice_set_fdir_ip4_usr_seg
991cac2a27cSHenry Tieman  * @seg: flow segment for programming
992cac2a27cSHenry Tieman  * @usr_ip4_spec: ethtool userdef packet offset
993cac2a27cSHenry Tieman  * @perfect_fltr: only valid on success; returns true if perfect filter,
994cac2a27cSHenry Tieman  *		  false if not
995cac2a27cSHenry Tieman  *
996cac2a27cSHenry Tieman  * Set the offset data into the flow segment to be used to program HW
997cac2a27cSHenry Tieman  * table for IPv4
998cac2a27cSHenry Tieman  */
999cac2a27cSHenry Tieman static int
ice_set_fdir_ip4_usr_seg(struct ice_flow_seg_info * seg,struct ethtool_usrip4_spec * usr_ip4_spec,bool * perfect_fltr)1000cac2a27cSHenry Tieman ice_set_fdir_ip4_usr_seg(struct ice_flow_seg_info *seg,
1001cac2a27cSHenry Tieman 			 struct ethtool_usrip4_spec *usr_ip4_spec,
1002cac2a27cSHenry Tieman 			 bool *perfect_fltr)
1003cac2a27cSHenry Tieman {
1004cac2a27cSHenry Tieman 	/* first 4 bytes of Layer 4 header */
1005cac2a27cSHenry Tieman 	if (usr_ip4_spec->l4_4_bytes)
1006cac2a27cSHenry Tieman 		return -EINVAL;
1007cac2a27cSHenry Tieman 	if (usr_ip4_spec->tos)
1008cac2a27cSHenry Tieman 		return -EINVAL;
1009cac2a27cSHenry Tieman 	if (usr_ip4_spec->ip_ver)
1010cac2a27cSHenry Tieman 		return -EINVAL;
1011cac2a27cSHenry Tieman 	/* Filtering on Layer 4 protocol not supported */
1012cac2a27cSHenry Tieman 	if (usr_ip4_spec->proto)
1013cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
1014cac2a27cSHenry Tieman 	/* empty rules are not valid */
1015cac2a27cSHenry Tieman 	if (!usr_ip4_spec->ip4src && !usr_ip4_spec->ip4dst)
1016cac2a27cSHenry Tieman 		return -EINVAL;
1017cac2a27cSHenry Tieman 
1018cac2a27cSHenry Tieman 	*perfect_fltr = true;
1019cac2a27cSHenry Tieman 	ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4);
1020cac2a27cSHenry Tieman 
1021cac2a27cSHenry Tieman 	/* IP source address */
1022cac2a27cSHenry Tieman 	if (usr_ip4_spec->ip4src == htonl(0xFFFFFFFF))
1023cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_SA,
1024cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
1025cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
1026cac2a27cSHenry Tieman 	else if (!usr_ip4_spec->ip4src)
1027cac2a27cSHenry Tieman 		*perfect_fltr = false;
1028cac2a27cSHenry Tieman 	else
1029cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
1030cac2a27cSHenry Tieman 
1031cac2a27cSHenry Tieman 	/* IP destination address */
1032cac2a27cSHenry Tieman 	if (usr_ip4_spec->ip4dst == htonl(0xFFFFFFFF))
1033cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_DA,
1034cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
1035cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
1036cac2a27cSHenry Tieman 	else if (!usr_ip4_spec->ip4dst)
1037cac2a27cSHenry Tieman 		*perfect_fltr = false;
1038cac2a27cSHenry Tieman 	else
1039cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
1040cac2a27cSHenry Tieman 
1041cac2a27cSHenry Tieman 	return 0;
1042cac2a27cSHenry Tieman }
1043cac2a27cSHenry Tieman 
1044cac2a27cSHenry Tieman /**
1045165d80d6SHenry Tieman  * ice_set_fdir_ip6_seg
1046165d80d6SHenry Tieman  * @seg: flow segment for programming
1047165d80d6SHenry Tieman  * @tcp_ip6_spec: mask data from ethtool
1048165d80d6SHenry Tieman  * @l4_proto: Layer 4 protocol to program
1049165d80d6SHenry Tieman  * @perfect_fltr: only valid on success; returns true if perfect filter,
1050165d80d6SHenry Tieman  *		  false if not
1051165d80d6SHenry Tieman  *
1052165d80d6SHenry Tieman  * Set the mask data into the flow segment to be used to program HW
1053165d80d6SHenry Tieman  * table based on provided L4 protocol for IPv6
1054165d80d6SHenry Tieman  */
1055165d80d6SHenry Tieman static int
ice_set_fdir_ip6_seg(struct ice_flow_seg_info * seg,struct ethtool_tcpip6_spec * tcp_ip6_spec,enum ice_flow_seg_hdr l4_proto,bool * perfect_fltr)1056165d80d6SHenry Tieman ice_set_fdir_ip6_seg(struct ice_flow_seg_info *seg,
1057165d80d6SHenry Tieman 		     struct ethtool_tcpip6_spec *tcp_ip6_spec,
1058165d80d6SHenry Tieman 		     enum ice_flow_seg_hdr l4_proto, bool *perfect_fltr)
1059165d80d6SHenry Tieman {
1060165d80d6SHenry Tieman 	enum ice_flow_field src_port, dst_port;
1061165d80d6SHenry Tieman 
1062165d80d6SHenry Tieman 	/* make sure we don't have any empty rule */
1063165d80d6SHenry Tieman 	if (!memcmp(tcp_ip6_spec->ip6src, &zero_ipv6_addr_mask,
1064165d80d6SHenry Tieman 		    sizeof(struct in6_addr)) &&
1065165d80d6SHenry Tieman 	    !memcmp(tcp_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
1066165d80d6SHenry Tieman 		    sizeof(struct in6_addr)) &&
1067165d80d6SHenry Tieman 	    !tcp_ip6_spec->psrc && !tcp_ip6_spec->pdst)
1068165d80d6SHenry Tieman 		return -EINVAL;
1069165d80d6SHenry Tieman 
1070165d80d6SHenry Tieman 	/* filtering on TC not supported */
1071165d80d6SHenry Tieman 	if (tcp_ip6_spec->tclass)
1072165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1073165d80d6SHenry Tieman 
1074165d80d6SHenry Tieman 	if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
1075165d80d6SHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
1076165d80d6SHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
1077165d80d6SHenry Tieman 	} else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
1078165d80d6SHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
1079165d80d6SHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
1080165d80d6SHenry Tieman 	} else if (l4_proto == ICE_FLOW_SEG_HDR_SCTP) {
1081165d80d6SHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT;
1082165d80d6SHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_SCTP_DST_PORT;
1083165d80d6SHenry Tieman 	} else {
1084165d80d6SHenry Tieman 		return -EINVAL;
1085165d80d6SHenry Tieman 	}
1086165d80d6SHenry Tieman 
1087165d80d6SHenry Tieman 	*perfect_fltr = true;
1088165d80d6SHenry Tieman 	ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV6 | l4_proto);
1089165d80d6SHenry Tieman 
1090165d80d6SHenry Tieman 	if (!memcmp(tcp_ip6_spec->ip6src, &full_ipv6_addr_mask,
1091165d80d6SHenry Tieman 		    sizeof(struct in6_addr)))
1092165d80d6SHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_SA,
1093165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
1094165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
1095165d80d6SHenry Tieman 	else if (!memcmp(tcp_ip6_spec->ip6src, &zero_ipv6_addr_mask,
1096165d80d6SHenry Tieman 			 sizeof(struct in6_addr)))
1097165d80d6SHenry Tieman 		*perfect_fltr = false;
1098165d80d6SHenry Tieman 	else
1099165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1100165d80d6SHenry Tieman 
1101165d80d6SHenry Tieman 	if (!memcmp(tcp_ip6_spec->ip6dst, &full_ipv6_addr_mask,
1102165d80d6SHenry Tieman 		    sizeof(struct in6_addr)))
1103165d80d6SHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_DA,
1104165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
1105165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
1106165d80d6SHenry Tieman 	else if (!memcmp(tcp_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
1107165d80d6SHenry Tieman 			 sizeof(struct in6_addr)))
1108165d80d6SHenry Tieman 		*perfect_fltr = false;
1109165d80d6SHenry Tieman 	else
1110165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1111165d80d6SHenry Tieman 
1112165d80d6SHenry Tieman 	/* Layer 4 source port */
1113165d80d6SHenry Tieman 	if (tcp_ip6_spec->psrc == htons(0xFFFF))
1114165d80d6SHenry Tieman 		ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
1115165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
1116165d80d6SHenry Tieman 				 false);
1117165d80d6SHenry Tieman 	else if (!tcp_ip6_spec->psrc)
1118165d80d6SHenry Tieman 		*perfect_fltr = false;
1119165d80d6SHenry Tieman 	else
1120165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1121165d80d6SHenry Tieman 
1122165d80d6SHenry Tieman 	/* Layer 4 destination port */
1123165d80d6SHenry Tieman 	if (tcp_ip6_spec->pdst == htons(0xFFFF))
1124165d80d6SHenry Tieman 		ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
1125165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
1126165d80d6SHenry Tieman 				 false);
1127165d80d6SHenry Tieman 	else if (!tcp_ip6_spec->pdst)
1128165d80d6SHenry Tieman 		*perfect_fltr = false;
1129165d80d6SHenry Tieman 	else
1130165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1131165d80d6SHenry Tieman 
1132165d80d6SHenry Tieman 	return 0;
1133165d80d6SHenry Tieman }
1134165d80d6SHenry Tieman 
1135165d80d6SHenry Tieman /**
1136165d80d6SHenry Tieman  * ice_set_fdir_ip6_usr_seg
1137165d80d6SHenry Tieman  * @seg: flow segment for programming
1138165d80d6SHenry Tieman  * @usr_ip6_spec: ethtool userdef packet offset
1139165d80d6SHenry Tieman  * @perfect_fltr: only valid on success; returns true if perfect filter,
1140165d80d6SHenry Tieman  *		  false if not
1141165d80d6SHenry Tieman  *
1142165d80d6SHenry Tieman  * Set the offset data into the flow segment to be used to program HW
1143165d80d6SHenry Tieman  * table for IPv6
1144165d80d6SHenry Tieman  */
1145165d80d6SHenry Tieman static int
ice_set_fdir_ip6_usr_seg(struct ice_flow_seg_info * seg,struct ethtool_usrip6_spec * usr_ip6_spec,bool * perfect_fltr)1146165d80d6SHenry Tieman ice_set_fdir_ip6_usr_seg(struct ice_flow_seg_info *seg,
1147165d80d6SHenry Tieman 			 struct ethtool_usrip6_spec *usr_ip6_spec,
1148165d80d6SHenry Tieman 			 bool *perfect_fltr)
1149165d80d6SHenry Tieman {
1150165d80d6SHenry Tieman 	/* filtering on Layer 4 bytes not supported */
1151165d80d6SHenry Tieman 	if (usr_ip6_spec->l4_4_bytes)
1152165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1153165d80d6SHenry Tieman 	/* filtering on TC not supported */
1154165d80d6SHenry Tieman 	if (usr_ip6_spec->tclass)
1155165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1156165d80d6SHenry Tieman 	/* filtering on Layer 4 protocol not supported */
1157165d80d6SHenry Tieman 	if (usr_ip6_spec->l4_proto)
1158165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1159165d80d6SHenry Tieman 	/* empty rules are not valid */
1160165d80d6SHenry Tieman 	if (!memcmp(usr_ip6_spec->ip6src, &zero_ipv6_addr_mask,
1161165d80d6SHenry Tieman 		    sizeof(struct in6_addr)) &&
1162165d80d6SHenry Tieman 	    !memcmp(usr_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
1163165d80d6SHenry Tieman 		    sizeof(struct in6_addr)))
1164165d80d6SHenry Tieman 		return -EINVAL;
1165165d80d6SHenry Tieman 
1166165d80d6SHenry Tieman 	*perfect_fltr = true;
1167165d80d6SHenry Tieman 	ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV6);
1168165d80d6SHenry Tieman 
1169165d80d6SHenry Tieman 	if (!memcmp(usr_ip6_spec->ip6src, &full_ipv6_addr_mask,
1170165d80d6SHenry Tieman 		    sizeof(struct in6_addr)))
1171165d80d6SHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_SA,
1172165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
1173165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
1174165d80d6SHenry Tieman 	else if (!memcmp(usr_ip6_spec->ip6src, &zero_ipv6_addr_mask,
1175165d80d6SHenry Tieman 			 sizeof(struct in6_addr)))
1176165d80d6SHenry Tieman 		*perfect_fltr = false;
1177165d80d6SHenry Tieman 	else
1178165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1179165d80d6SHenry Tieman 
1180165d80d6SHenry Tieman 	if (!memcmp(usr_ip6_spec->ip6dst, &full_ipv6_addr_mask,
1181165d80d6SHenry Tieman 		    sizeof(struct in6_addr)))
1182165d80d6SHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_DA,
1183165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
1184165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
1185165d80d6SHenry Tieman 	else if (!memcmp(usr_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
1186165d80d6SHenry Tieman 			 sizeof(struct in6_addr)))
1187165d80d6SHenry Tieman 		*perfect_fltr = false;
1188165d80d6SHenry Tieman 	else
1189165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1190165d80d6SHenry Tieman 
1191165d80d6SHenry Tieman 	return 0;
1192165d80d6SHenry Tieman }
1193165d80d6SHenry Tieman 
1194165d80d6SHenry Tieman /**
1195cac2a27cSHenry Tieman  * ice_cfg_fdir_xtrct_seq - Configure extraction sequence for the given filter
1196cac2a27cSHenry Tieman  * @pf: PF structure
1197cac2a27cSHenry Tieman  * @fsp: pointer to ethtool Rx flow specification
11982c57ffcbSHenry Tieman  * @user: user defined data from flow specification
1199cac2a27cSHenry Tieman  *
1200cac2a27cSHenry Tieman  * Returns 0 on success.
1201cac2a27cSHenry Tieman  */
1202cac2a27cSHenry Tieman static int
ice_cfg_fdir_xtrct_seq(struct ice_pf * pf,struct ethtool_rx_flow_spec * fsp,struct ice_rx_flow_userdef * user)12032c57ffcbSHenry Tieman ice_cfg_fdir_xtrct_seq(struct ice_pf *pf, struct ethtool_rx_flow_spec *fsp,
12042c57ffcbSHenry Tieman 		       struct ice_rx_flow_userdef *user)
1205cac2a27cSHenry Tieman {
1206cac2a27cSHenry Tieman 	struct ice_flow_seg_info *seg, *tun_seg;
1207cac2a27cSHenry Tieman 	struct device *dev = ice_pf_to_dev(pf);
1208cac2a27cSHenry Tieman 	enum ice_fltr_ptype fltr_idx;
1209cac2a27cSHenry Tieman 	struct ice_hw *hw = &pf->hw;
1210cac2a27cSHenry Tieman 	bool perfect_filter;
1211cac2a27cSHenry Tieman 	int ret;
1212cac2a27cSHenry Tieman 
1213cac2a27cSHenry Tieman 	seg = devm_kzalloc(dev, sizeof(*seg), GFP_KERNEL);
1214cac2a27cSHenry Tieman 	if (!seg)
1215cac2a27cSHenry Tieman 		return -ENOMEM;
1216cac2a27cSHenry Tieman 
1217a4da4913SChristophe JAILLET 	tun_seg = devm_kcalloc(dev, ICE_FD_HW_SEG_MAX, sizeof(*tun_seg),
1218cac2a27cSHenry Tieman 			       GFP_KERNEL);
1219cac2a27cSHenry Tieman 	if (!tun_seg) {
1220cac2a27cSHenry Tieman 		devm_kfree(dev, seg);
1221cac2a27cSHenry Tieman 		return -ENOMEM;
1222cac2a27cSHenry Tieman 	}
1223cac2a27cSHenry Tieman 
1224cac2a27cSHenry Tieman 	switch (fsp->flow_type & ~FLOW_EXT) {
1225cac2a27cSHenry Tieman 	case TCP_V4_FLOW:
1226cac2a27cSHenry Tieman 		ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
1227cac2a27cSHenry Tieman 					   ICE_FLOW_SEG_HDR_TCP,
1228cac2a27cSHenry Tieman 					   &perfect_filter);
1229cac2a27cSHenry Tieman 		break;
1230cac2a27cSHenry Tieman 	case UDP_V4_FLOW:
1231cac2a27cSHenry Tieman 		ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
1232cac2a27cSHenry Tieman 					   ICE_FLOW_SEG_HDR_UDP,
1233cac2a27cSHenry Tieman 					   &perfect_filter);
1234cac2a27cSHenry Tieman 		break;
1235cac2a27cSHenry Tieman 	case SCTP_V4_FLOW:
1236cac2a27cSHenry Tieman 		ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
1237cac2a27cSHenry Tieman 					   ICE_FLOW_SEG_HDR_SCTP,
1238cac2a27cSHenry Tieman 					   &perfect_filter);
1239cac2a27cSHenry Tieman 		break;
1240cac2a27cSHenry Tieman 	case IPV4_USER_FLOW:
1241cac2a27cSHenry Tieman 		ret = ice_set_fdir_ip4_usr_seg(seg, &fsp->m_u.usr_ip4_spec,
1242cac2a27cSHenry Tieman 					       &perfect_filter);
1243cac2a27cSHenry Tieman 		break;
1244165d80d6SHenry Tieman 	case TCP_V6_FLOW:
1245165d80d6SHenry Tieman 		ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
1246165d80d6SHenry Tieman 					   ICE_FLOW_SEG_HDR_TCP,
1247165d80d6SHenry Tieman 					   &perfect_filter);
1248165d80d6SHenry Tieman 		break;
1249165d80d6SHenry Tieman 	case UDP_V6_FLOW:
1250165d80d6SHenry Tieman 		ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
1251165d80d6SHenry Tieman 					   ICE_FLOW_SEG_HDR_UDP,
1252165d80d6SHenry Tieman 					   &perfect_filter);
1253165d80d6SHenry Tieman 		break;
1254165d80d6SHenry Tieman 	case SCTP_V6_FLOW:
1255165d80d6SHenry Tieman 		ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
1256165d80d6SHenry Tieman 					   ICE_FLOW_SEG_HDR_SCTP,
1257165d80d6SHenry Tieman 					   &perfect_filter);
1258165d80d6SHenry Tieman 		break;
1259165d80d6SHenry Tieman 	case IPV6_USER_FLOW:
1260165d80d6SHenry Tieman 		ret = ice_set_fdir_ip6_usr_seg(seg, &fsp->m_u.usr_ip6_spec,
1261165d80d6SHenry Tieman 					       &perfect_filter);
1262165d80d6SHenry Tieman 		break;
1263cac2a27cSHenry Tieman 	default:
1264cac2a27cSHenry Tieman 		ret = -EINVAL;
1265cac2a27cSHenry Tieman 	}
1266cac2a27cSHenry Tieman 	if (ret)
1267cac2a27cSHenry Tieman 		goto err_exit;
1268cac2a27cSHenry Tieman 
1269cac2a27cSHenry Tieman 	/* tunnel segments are shifted up one. */
1270cac2a27cSHenry Tieman 	memcpy(&tun_seg[1], seg, sizeof(*seg));
1271cac2a27cSHenry Tieman 
12722c57ffcbSHenry Tieman 	if (user && user->flex_fltr) {
12732c57ffcbSHenry Tieman 		perfect_filter = false;
12742c57ffcbSHenry Tieman 		ice_flow_add_fld_raw(seg, user->flex_offset,
12752c57ffcbSHenry Tieman 				     ICE_FLTR_PRGM_FLEX_WORD_SIZE,
12762c57ffcbSHenry Tieman 				     ICE_FLOW_FLD_OFF_INVAL,
12772c57ffcbSHenry Tieman 				     ICE_FLOW_FLD_OFF_INVAL);
12782c57ffcbSHenry Tieman 		ice_flow_add_fld_raw(&tun_seg[1], user->flex_offset,
12792c57ffcbSHenry Tieman 				     ICE_FLTR_PRGM_FLEX_WORD_SIZE,
12802c57ffcbSHenry Tieman 				     ICE_FLOW_FLD_OFF_INVAL,
12812c57ffcbSHenry Tieman 				     ICE_FLOW_FLD_OFF_INVAL);
12822c57ffcbSHenry Tieman 	}
12832c57ffcbSHenry Tieman 
1284cac2a27cSHenry Tieman 	fltr_idx = ice_ethtool_flow_to_fltr(fsp->flow_type & ~FLOW_EXT);
1285*a3336056SJedrzej Jagielski 
1286*a3336056SJedrzej Jagielski 	assign_bit(fltr_idx, hw->fdir_perfect_fltr, perfect_filter);
1287*a3336056SJedrzej Jagielski 
1288*a3336056SJedrzej Jagielski 	/* add filter for outer headers */
1289cac2a27cSHenry Tieman 	ret = ice_fdir_set_hw_fltr_rule(pf, seg, fltr_idx,
1290cac2a27cSHenry Tieman 					ICE_FD_HW_SEG_NON_TUN);
1291*a3336056SJedrzej Jagielski 	if (ret == -EEXIST) {
1292*a3336056SJedrzej Jagielski 		/* Rule already exists, free memory and count as success */
1293*a3336056SJedrzej Jagielski 		ret = 0;
1294*a3336056SJedrzej Jagielski 		goto err_exit;
1295*a3336056SJedrzej Jagielski 	} else if (ret) {
1296cac2a27cSHenry Tieman 		/* could not write filter, free memory */
1297cac2a27cSHenry Tieman 		goto err_exit;
1298*a3336056SJedrzej Jagielski 	}
1299cac2a27cSHenry Tieman 
1300cac2a27cSHenry Tieman 	/* make tunneled filter HW entries if possible */
1301cac2a27cSHenry Tieman 	memcpy(&tun_seg[1], seg, sizeof(*seg));
1302cac2a27cSHenry Tieman 	ret = ice_fdir_set_hw_fltr_rule(pf, tun_seg, fltr_idx,
1303cac2a27cSHenry Tieman 					ICE_FD_HW_SEG_TUN);
1304cac2a27cSHenry Tieman 	if (ret == -EEXIST) {
1305cac2a27cSHenry Tieman 		/* Rule already exists, free memory and count as success */
1306cac2a27cSHenry Tieman 		devm_kfree(dev, tun_seg);
1307cac2a27cSHenry Tieman 		ret = 0;
1308cac2a27cSHenry Tieman 	} else if (ret) {
1309cac2a27cSHenry Tieman 		/* could not write tunnel filter, but outer filter exists */
1310cac2a27cSHenry Tieman 		devm_kfree(dev, tun_seg);
1311cac2a27cSHenry Tieman 	}
1312cac2a27cSHenry Tieman 
1313cac2a27cSHenry Tieman 	return ret;
1314cac2a27cSHenry Tieman 
1315cac2a27cSHenry Tieman err_exit:
1316cac2a27cSHenry Tieman 	devm_kfree(dev, tun_seg);
1317cac2a27cSHenry Tieman 	devm_kfree(dev, seg);
1318cac2a27cSHenry Tieman 
1319*a3336056SJedrzej Jagielski 	return ret;
1320cac2a27cSHenry Tieman }
1321cac2a27cSHenry Tieman 
1322cac2a27cSHenry Tieman /**
132340319796SKiran Patil  * ice_update_per_q_fltr
132440319796SKiran Patil  * @vsi: ptr to VSI
132540319796SKiran Patil  * @q_index: queue index
132640319796SKiran Patil  * @inc: true to increment or false to decrement per queue filter count
132740319796SKiran Patil  *
132840319796SKiran Patil  * This function is used to keep track of per queue sideband filters
132940319796SKiran Patil  */
ice_update_per_q_fltr(struct ice_vsi * vsi,u32 q_index,bool inc)133040319796SKiran Patil static void ice_update_per_q_fltr(struct ice_vsi *vsi, u32 q_index, bool inc)
133140319796SKiran Patil {
133240319796SKiran Patil 	struct ice_rx_ring *rx_ring;
133340319796SKiran Patil 
133440319796SKiran Patil 	if (!vsi->num_rxq || q_index >= vsi->num_rxq)
133540319796SKiran Patil 		return;
133640319796SKiran Patil 
133740319796SKiran Patil 	rx_ring = vsi->rx_rings[q_index];
133840319796SKiran Patil 	if (!rx_ring || !rx_ring->ch)
133940319796SKiran Patil 		return;
134040319796SKiran Patil 
134140319796SKiran Patil 	if (inc)
134240319796SKiran Patil 		atomic_inc(&rx_ring->ch->num_sb_fltr);
134340319796SKiran Patil 	else
134440319796SKiran Patil 		atomic_dec_if_positive(&rx_ring->ch->num_sb_fltr);
134540319796SKiran Patil }
134640319796SKiran Patil 
134740319796SKiran Patil /**
1348cac2a27cSHenry Tieman  * ice_fdir_write_fltr - send a flow director filter to the hardware
1349cac2a27cSHenry Tieman  * @pf: PF data structure
1350cac2a27cSHenry Tieman  * @input: filter structure
1351cac2a27cSHenry Tieman  * @add: true adds filter and false removed filter
1352cac2a27cSHenry Tieman  * @is_tun: true adds inner filter on tunnel and false outer headers
1353cac2a27cSHenry Tieman  *
1354cac2a27cSHenry Tieman  * returns 0 on success and negative value on error
1355cac2a27cSHenry Tieman  */
135628bf2672SBrett Creeley int
ice_fdir_write_fltr(struct ice_pf * pf,struct ice_fdir_fltr * input,bool add,bool is_tun)1357cac2a27cSHenry Tieman ice_fdir_write_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input, bool add,
1358cac2a27cSHenry Tieman 		    bool is_tun)
1359cac2a27cSHenry Tieman {
1360cac2a27cSHenry Tieman 	struct device *dev = ice_pf_to_dev(pf);
1361cac2a27cSHenry Tieman 	struct ice_hw *hw = &pf->hw;
1362cac2a27cSHenry Tieman 	struct ice_fltr_desc desc;
1363cac2a27cSHenry Tieman 	struct ice_vsi *ctrl_vsi;
1364cac2a27cSHenry Tieman 	u8 *pkt, *frag_pkt;
1365cac2a27cSHenry Tieman 	bool has_frag;
1366cac2a27cSHenry Tieman 	int err;
1367cac2a27cSHenry Tieman 
1368cac2a27cSHenry Tieman 	ctrl_vsi = ice_get_ctrl_vsi(pf);
1369cac2a27cSHenry Tieman 	if (!ctrl_vsi)
1370cac2a27cSHenry Tieman 		return -EINVAL;
1371cac2a27cSHenry Tieman 
1372cac2a27cSHenry Tieman 	pkt = devm_kzalloc(dev, ICE_FDIR_MAX_RAW_PKT_SIZE, GFP_KERNEL);
1373cac2a27cSHenry Tieman 	if (!pkt)
1374cac2a27cSHenry Tieman 		return -ENOMEM;
1375cac2a27cSHenry Tieman 	frag_pkt = devm_kzalloc(dev, ICE_FDIR_MAX_RAW_PKT_SIZE, GFP_KERNEL);
1376cac2a27cSHenry Tieman 	if (!frag_pkt) {
1377cac2a27cSHenry Tieman 		err = -ENOMEM;
1378cac2a27cSHenry Tieman 		goto err_free;
1379cac2a27cSHenry Tieman 	}
1380cac2a27cSHenry Tieman 
1381cac2a27cSHenry Tieman 	ice_fdir_get_prgm_desc(hw, input, &desc, add);
13822ccc1c1cSTony Nguyen 	err = ice_fdir_get_gen_prgm_pkt(hw, input, pkt, false, is_tun);
13832ccc1c1cSTony Nguyen 	if (err)
1384cac2a27cSHenry Tieman 		goto err_free_all;
1385cac2a27cSHenry Tieman 	err = ice_prgm_fdir_fltr(ctrl_vsi, &desc, pkt);
1386cac2a27cSHenry Tieman 	if (err)
1387cac2a27cSHenry Tieman 		goto err_free_all;
1388cac2a27cSHenry Tieman 
1389cac2a27cSHenry Tieman 	/* repeat for fragment packet */
1390cac2a27cSHenry Tieman 	has_frag = ice_fdir_has_frag(input->flow_type);
1391cac2a27cSHenry Tieman 	if (has_frag) {
1392cac2a27cSHenry Tieman 		/* does not return error */
1393cac2a27cSHenry Tieman 		ice_fdir_get_prgm_desc(hw, input, &desc, add);
13942ccc1c1cSTony Nguyen 		err = ice_fdir_get_gen_prgm_pkt(hw, input, frag_pkt, true,
1395cac2a27cSHenry Tieman 						is_tun);
13962ccc1c1cSTony Nguyen 		if (err)
1397cac2a27cSHenry Tieman 			goto err_frag;
1398cac2a27cSHenry Tieman 		err = ice_prgm_fdir_fltr(ctrl_vsi, &desc, frag_pkt);
1399cac2a27cSHenry Tieman 		if (err)
1400cac2a27cSHenry Tieman 			goto err_frag;
1401cac2a27cSHenry Tieman 	} else {
1402cac2a27cSHenry Tieman 		devm_kfree(dev, frag_pkt);
1403cac2a27cSHenry Tieman 	}
1404cac2a27cSHenry Tieman 
1405cac2a27cSHenry Tieman 	return 0;
1406cac2a27cSHenry Tieman 
1407cac2a27cSHenry Tieman err_free_all:
1408cac2a27cSHenry Tieman 	devm_kfree(dev, frag_pkt);
1409cac2a27cSHenry Tieman err_free:
1410cac2a27cSHenry Tieman 	devm_kfree(dev, pkt);
1411cac2a27cSHenry Tieman 	return err;
1412cac2a27cSHenry Tieman 
1413cac2a27cSHenry Tieman err_frag:
1414cac2a27cSHenry Tieman 	devm_kfree(dev, frag_pkt);
1415cac2a27cSHenry Tieman 	return err;
1416cac2a27cSHenry Tieman }
1417cac2a27cSHenry Tieman 
1418cac2a27cSHenry Tieman /**
1419cac2a27cSHenry Tieman  * ice_fdir_write_all_fltr - send a flow director filter to the hardware
1420cac2a27cSHenry Tieman  * @pf: PF data structure
1421cac2a27cSHenry Tieman  * @input: filter structure
1422cac2a27cSHenry Tieman  * @add: true adds filter and false removed filter
1423cac2a27cSHenry Tieman  *
1424cac2a27cSHenry Tieman  * returns 0 on success and negative value on error
1425cac2a27cSHenry Tieman  */
1426cac2a27cSHenry Tieman static int
ice_fdir_write_all_fltr(struct ice_pf * pf,struct ice_fdir_fltr * input,bool add)1427cac2a27cSHenry Tieman ice_fdir_write_all_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input,
1428cac2a27cSHenry Tieman 			bool add)
1429cac2a27cSHenry Tieman {
1430cac2a27cSHenry Tieman 	u16 port_num;
1431cac2a27cSHenry Tieman 	int tun;
1432cac2a27cSHenry Tieman 
1433cac2a27cSHenry Tieman 	for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
1434cac2a27cSHenry Tieman 		bool is_tun = tun == ICE_FD_HW_SEG_TUN;
1435cac2a27cSHenry Tieman 		int err;
1436cac2a27cSHenry Tieman 
1437de6acd1cSMichal Swiatkowski 		if (is_tun && !ice_get_open_tunnel_port(&pf->hw, &port_num, TNL_ALL))
1438cac2a27cSHenry Tieman 			continue;
1439cac2a27cSHenry Tieman 		err = ice_fdir_write_fltr(pf, input, add, is_tun);
1440cac2a27cSHenry Tieman 		if (err)
1441cac2a27cSHenry Tieman 			return err;
1442cac2a27cSHenry Tieman 	}
1443cac2a27cSHenry Tieman 	return 0;
1444cac2a27cSHenry Tieman }
1445cac2a27cSHenry Tieman 
1446cac2a27cSHenry Tieman /**
144783af0039SHenry Tieman  * ice_fdir_replay_fltrs - replay filters from the HW filter list
144883af0039SHenry Tieman  * @pf: board private structure
144983af0039SHenry Tieman  */
ice_fdir_replay_fltrs(struct ice_pf * pf)145083af0039SHenry Tieman void ice_fdir_replay_fltrs(struct ice_pf *pf)
145183af0039SHenry Tieman {
145283af0039SHenry Tieman 	struct ice_fdir_fltr *f_rule;
145383af0039SHenry Tieman 	struct ice_hw *hw = &pf->hw;
145483af0039SHenry Tieman 
145583af0039SHenry Tieman 	list_for_each_entry(f_rule, &hw->fdir_list_head, fltr_node) {
145683af0039SHenry Tieman 		int err = ice_fdir_write_all_fltr(pf, f_rule, true);
145783af0039SHenry Tieman 
145883af0039SHenry Tieman 		if (err)
145983af0039SHenry Tieman 			dev_dbg(ice_pf_to_dev(pf), "Flow Director error %d, could not reprogram filter %d\n",
146083af0039SHenry Tieman 				err, f_rule->fltr_id);
146183af0039SHenry Tieman 	}
146283af0039SHenry Tieman }
146383af0039SHenry Tieman 
146483af0039SHenry Tieman /**
1465148beb61SHenry Tieman  * ice_fdir_create_dflt_rules - create default perfect filters
1466148beb61SHenry Tieman  * @pf: PF data structure
1467148beb61SHenry Tieman  *
1468148beb61SHenry Tieman  * Returns 0 for success or error.
1469148beb61SHenry Tieman  */
ice_fdir_create_dflt_rules(struct ice_pf * pf)1470148beb61SHenry Tieman int ice_fdir_create_dflt_rules(struct ice_pf *pf)
1471148beb61SHenry Tieman {
1472148beb61SHenry Tieman 	int err;
1473148beb61SHenry Tieman 
1474148beb61SHenry Tieman 	/* Create perfect TCP and UDP rules in hardware. */
1475148beb61SHenry Tieman 	err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV4_TCP);
1476148beb61SHenry Tieman 	if (err)
1477148beb61SHenry Tieman 		return err;
1478148beb61SHenry Tieman 
1479148beb61SHenry Tieman 	err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV4_UDP);
1480165d80d6SHenry Tieman 	if (err)
1481165d80d6SHenry Tieman 		return err;
1482165d80d6SHenry Tieman 
1483165d80d6SHenry Tieman 	err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV6_TCP);
1484165d80d6SHenry Tieman 	if (err)
1485165d80d6SHenry Tieman 		return err;
1486165d80d6SHenry Tieman 
1487165d80d6SHenry Tieman 	err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV6_UDP);
1488148beb61SHenry Tieman 
1489148beb61SHenry Tieman 	return err;
1490148beb61SHenry Tieman }
1491148beb61SHenry Tieman 
1492148beb61SHenry Tieman /**
149340319796SKiran Patil  * ice_fdir_del_all_fltrs - Delete all flow director filters
149440319796SKiran Patil  * @vsi: the VSI being changed
149540319796SKiran Patil  *
149640319796SKiran Patil  * This function needs to be called while holding hw->fdir_fltr_lock
149740319796SKiran Patil  */
ice_fdir_del_all_fltrs(struct ice_vsi * vsi)149840319796SKiran Patil void ice_fdir_del_all_fltrs(struct ice_vsi *vsi)
149940319796SKiran Patil {
150040319796SKiran Patil 	struct ice_fdir_fltr *f_rule, *tmp;
150140319796SKiran Patil 	struct ice_pf *pf = vsi->back;
150240319796SKiran Patil 	struct ice_hw *hw = &pf->hw;
150340319796SKiran Patil 
150440319796SKiran Patil 	list_for_each_entry_safe(f_rule, tmp, &hw->fdir_list_head, fltr_node) {
150540319796SKiran Patil 		ice_fdir_write_all_fltr(pf, f_rule, false);
150640319796SKiran Patil 		ice_fdir_update_cntrs(hw, f_rule->flow_type, false);
150740319796SKiran Patil 		list_del(&f_rule->fltr_node);
150840319796SKiran Patil 		devm_kfree(ice_pf_to_dev(pf), f_rule);
150940319796SKiran Patil 	}
151040319796SKiran Patil }
151140319796SKiran Patil 
151240319796SKiran Patil /**
1513148beb61SHenry Tieman  * ice_vsi_manage_fdir - turn on/off flow director
1514148beb61SHenry Tieman  * @vsi: the VSI being changed
1515148beb61SHenry Tieman  * @ena: boolean value indicating if this is an enable or disable request
1516148beb61SHenry Tieman  */
ice_vsi_manage_fdir(struct ice_vsi * vsi,bool ena)1517148beb61SHenry Tieman void ice_vsi_manage_fdir(struct ice_vsi *vsi, bool ena)
1518148beb61SHenry Tieman {
1519148beb61SHenry Tieman 	struct ice_pf *pf = vsi->back;
1520148beb61SHenry Tieman 	struct ice_hw *hw = &pf->hw;
1521148beb61SHenry Tieman 	enum ice_fltr_ptype flow;
1522148beb61SHenry Tieman 
1523148beb61SHenry Tieman 	if (ena) {
1524148beb61SHenry Tieman 		set_bit(ICE_FLAG_FD_ENA, pf->flags);
1525148beb61SHenry Tieman 		ice_fdir_create_dflt_rules(pf);
1526148beb61SHenry Tieman 		return;
1527148beb61SHenry Tieman 	}
1528148beb61SHenry Tieman 
1529148beb61SHenry Tieman 	mutex_lock(&hw->fdir_fltr_lock);
1530148beb61SHenry Tieman 	if (!test_and_clear_bit(ICE_FLAG_FD_ENA, pf->flags))
1531148beb61SHenry Tieman 		goto release_lock;
153240319796SKiran Patil 
153340319796SKiran Patil 	ice_fdir_del_all_fltrs(vsi);
1534148beb61SHenry Tieman 
1535148beb61SHenry Tieman 	if (hw->fdir_prof)
1536148beb61SHenry Tieman 		for (flow = ICE_FLTR_PTYPE_NONF_NONE; flow < ICE_FLTR_PTYPE_MAX;
1537148beb61SHenry Tieman 		     flow++)
1538148beb61SHenry Tieman 			if (hw->fdir_prof[flow])
1539148beb61SHenry Tieman 				ice_fdir_rem_flow(hw, ICE_BLK_FD, flow);
1540148beb61SHenry Tieman 
1541148beb61SHenry Tieman release_lock:
1542148beb61SHenry Tieman 	mutex_unlock(&hw->fdir_fltr_lock);
1543148beb61SHenry Tieman }
1544cac2a27cSHenry Tieman 
1545cac2a27cSHenry Tieman /**
1546d5329be9SHenry Tieman  * ice_fdir_do_rem_flow - delete flow and possibly add perfect flow
1547d5329be9SHenry Tieman  * @pf: PF structure
1548d5329be9SHenry Tieman  * @flow_type: FDir flow type to release
1549d5329be9SHenry Tieman  */
1550d5329be9SHenry Tieman static void
ice_fdir_do_rem_flow(struct ice_pf * pf,enum ice_fltr_ptype flow_type)1551d5329be9SHenry Tieman ice_fdir_do_rem_flow(struct ice_pf *pf, enum ice_fltr_ptype flow_type)
1552d5329be9SHenry Tieman {
1553d5329be9SHenry Tieman 	struct ice_hw *hw = &pf->hw;
1554d5329be9SHenry Tieman 	bool need_perfect = false;
1555d5329be9SHenry Tieman 
1556d5329be9SHenry Tieman 	if (flow_type == ICE_FLTR_PTYPE_NONF_IPV4_TCP ||
1557d5329be9SHenry Tieman 	    flow_type == ICE_FLTR_PTYPE_NONF_IPV4_UDP ||
1558d5329be9SHenry Tieman 	    flow_type == ICE_FLTR_PTYPE_NONF_IPV6_TCP ||
1559d5329be9SHenry Tieman 	    flow_type == ICE_FLTR_PTYPE_NONF_IPV6_UDP)
1560d5329be9SHenry Tieman 		need_perfect = true;
1561d5329be9SHenry Tieman 
1562d5329be9SHenry Tieman 	if (need_perfect && test_bit(flow_type, hw->fdir_perfect_fltr))
1563d5329be9SHenry Tieman 		return;
1564d5329be9SHenry Tieman 
1565d5329be9SHenry Tieman 	ice_fdir_rem_flow(hw, ICE_BLK_FD, flow_type);
1566d5329be9SHenry Tieman 	if (need_perfect)
1567d5329be9SHenry Tieman 		ice_create_init_fdir_rule(pf, flow_type);
1568d5329be9SHenry Tieman }
1569d5329be9SHenry Tieman 
1570d5329be9SHenry Tieman /**
1571cac2a27cSHenry Tieman  * ice_fdir_update_list_entry - add or delete a filter from the filter list
1572cac2a27cSHenry Tieman  * @pf: PF structure
1573cac2a27cSHenry Tieman  * @input: filter structure
1574cac2a27cSHenry Tieman  * @fltr_idx: ethtool index of filter to modify
1575cac2a27cSHenry Tieman  *
1576cac2a27cSHenry Tieman  * returns 0 on success and negative on errors
1577cac2a27cSHenry Tieman  */
1578cac2a27cSHenry Tieman static int
ice_fdir_update_list_entry(struct ice_pf * pf,struct ice_fdir_fltr * input,int fltr_idx)1579cac2a27cSHenry Tieman ice_fdir_update_list_entry(struct ice_pf *pf, struct ice_fdir_fltr *input,
1580cac2a27cSHenry Tieman 			   int fltr_idx)
1581cac2a27cSHenry Tieman {
1582cac2a27cSHenry Tieman 	struct ice_fdir_fltr *old_fltr;
1583cac2a27cSHenry Tieman 	struct ice_hw *hw = &pf->hw;
158440319796SKiran Patil 	struct ice_vsi *vsi;
1585cac2a27cSHenry Tieman 	int err = -ENOENT;
1586cac2a27cSHenry Tieman 
1587cac2a27cSHenry Tieman 	/* Do not update filters during reset */
1588cac2a27cSHenry Tieman 	if (ice_is_reset_in_progress(pf->state))
1589cac2a27cSHenry Tieman 		return -EBUSY;
1590cac2a27cSHenry Tieman 
159140319796SKiran Patil 	vsi = ice_get_main_vsi(pf);
159240319796SKiran Patil 	if (!vsi)
159340319796SKiran Patil 		return -EINVAL;
159440319796SKiran Patil 
1595cac2a27cSHenry Tieman 	old_fltr = ice_fdir_find_fltr_by_idx(hw, fltr_idx);
1596cac2a27cSHenry Tieman 	if (old_fltr) {
1597cac2a27cSHenry Tieman 		err = ice_fdir_write_all_fltr(pf, old_fltr, false);
1598cac2a27cSHenry Tieman 		if (err)
1599cac2a27cSHenry Tieman 			return err;
1600cac2a27cSHenry Tieman 		ice_fdir_update_cntrs(hw, old_fltr->flow_type, false);
160140319796SKiran Patil 		/* update sb-filters count, specific to ring->channel */
160240319796SKiran Patil 		ice_update_per_q_fltr(vsi, old_fltr->orig_q_index, false);
1603cac2a27cSHenry Tieman 		if (!input && !hw->fdir_fltr_cnt[old_fltr->flow_type])
1604cac2a27cSHenry Tieman 			/* we just deleted the last filter of flow_type so we
1605cac2a27cSHenry Tieman 			 * should also delete the HW filter info.
1606cac2a27cSHenry Tieman 			 */
1607d5329be9SHenry Tieman 			ice_fdir_do_rem_flow(pf, old_fltr->flow_type);
1608cac2a27cSHenry Tieman 		list_del(&old_fltr->fltr_node);
1609cac2a27cSHenry Tieman 		devm_kfree(ice_hw_to_dev(hw), old_fltr);
1610cac2a27cSHenry Tieman 	}
1611cac2a27cSHenry Tieman 	if (!input)
1612cac2a27cSHenry Tieman 		return err;
1613cac2a27cSHenry Tieman 	ice_fdir_list_add_fltr(hw, input);
161440319796SKiran Patil 	/* update sb-filters count, specific to ring->channel */
161540319796SKiran Patil 	ice_update_per_q_fltr(vsi, input->orig_q_index, true);
1616cac2a27cSHenry Tieman 	ice_fdir_update_cntrs(hw, input->flow_type, true);
1617cac2a27cSHenry Tieman 	return 0;
1618cac2a27cSHenry Tieman }
1619cac2a27cSHenry Tieman 
1620cac2a27cSHenry Tieman /**
1621cac2a27cSHenry Tieman  * ice_del_fdir_ethtool - delete Flow Director filter
1622cac2a27cSHenry Tieman  * @vsi: pointer to target VSI
1623cac2a27cSHenry Tieman  * @cmd: command to add or delete Flow Director filter
1624cac2a27cSHenry Tieman  *
1625cac2a27cSHenry Tieman  * Returns 0 on success and negative values for failure
1626cac2a27cSHenry Tieman  */
ice_del_fdir_ethtool(struct ice_vsi * vsi,struct ethtool_rxnfc * cmd)1627cac2a27cSHenry Tieman int ice_del_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
1628cac2a27cSHenry Tieman {
1629cac2a27cSHenry Tieman 	struct ethtool_rx_flow_spec *fsp =
1630cac2a27cSHenry Tieman 		(struct ethtool_rx_flow_spec *)&cmd->fs;
1631cac2a27cSHenry Tieman 	struct ice_pf *pf = vsi->back;
1632cac2a27cSHenry Tieman 	struct ice_hw *hw = &pf->hw;
1633cac2a27cSHenry Tieman 	int val;
1634cac2a27cSHenry Tieman 
1635cac2a27cSHenry Tieman 	if (!test_bit(ICE_FLAG_FD_ENA, pf->flags))
1636cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
1637cac2a27cSHenry Tieman 
1638cac2a27cSHenry Tieman 	/* Do not delete filters during reset */
1639cac2a27cSHenry Tieman 	if (ice_is_reset_in_progress(pf->state)) {
1640cac2a27cSHenry Tieman 		dev_err(ice_pf_to_dev(pf), "Device is resetting - deleting Flow Director filters not supported during reset\n");
1641cac2a27cSHenry Tieman 		return -EBUSY;
1642cac2a27cSHenry Tieman 	}
1643cac2a27cSHenry Tieman 
16447e408e07SAnirudh Venkataramanan 	if (test_bit(ICE_FD_FLUSH_REQ, pf->state))
1645cac2a27cSHenry Tieman 		return -EBUSY;
1646cac2a27cSHenry Tieman 
1647cac2a27cSHenry Tieman 	mutex_lock(&hw->fdir_fltr_lock);
1648cac2a27cSHenry Tieman 	val = ice_fdir_update_list_entry(pf, NULL, fsp->location);
1649cac2a27cSHenry Tieman 	mutex_unlock(&hw->fdir_fltr_lock);
1650cac2a27cSHenry Tieman 
1651cac2a27cSHenry Tieman 	return val;
1652cac2a27cSHenry Tieman }
1653cac2a27cSHenry Tieman 
1654cac2a27cSHenry Tieman /**
165540319796SKiran Patil  * ice_update_ring_dest_vsi - update dest ring and dest VSI
165640319796SKiran Patil  * @vsi: pointer to target VSI
165740319796SKiran Patil  * @dest_vsi: ptr to dest VSI index
165840319796SKiran Patil  * @ring: ptr to dest ring
165940319796SKiran Patil  *
166040319796SKiran Patil  * This function updates destination VSI and queue if user specifies
166140319796SKiran Patil  * target queue which falls in channel's (aka ADQ) queue region
166240319796SKiran Patil  */
166340319796SKiran Patil static void
ice_update_ring_dest_vsi(struct ice_vsi * vsi,u16 * dest_vsi,u32 * ring)166440319796SKiran Patil ice_update_ring_dest_vsi(struct ice_vsi *vsi, u16 *dest_vsi, u32 *ring)
166540319796SKiran Patil {
166640319796SKiran Patil 	struct ice_channel *ch;
166740319796SKiran Patil 
166840319796SKiran Patil 	list_for_each_entry(ch, &vsi->ch_list, list) {
166940319796SKiran Patil 		if (!ch->ch_vsi)
167040319796SKiran Patil 			continue;
167140319796SKiran Patil 
167240319796SKiran Patil 		/* make sure to locate corresponding channel based on "queue"
167340319796SKiran Patil 		 * specified
167440319796SKiran Patil 		 */
167540319796SKiran Patil 		if ((*ring < ch->base_q) ||
167640319796SKiran Patil 		    (*ring >= (ch->base_q + ch->num_rxq)))
167740319796SKiran Patil 			continue;
167840319796SKiran Patil 
167940319796SKiran Patil 		/* update the dest_vsi based on channel */
168040319796SKiran Patil 		*dest_vsi = ch->ch_vsi->idx;
168140319796SKiran Patil 
168240319796SKiran Patil 		/* update the "ring" to be correct based on channel */
168340319796SKiran Patil 		*ring -= ch->base_q;
168440319796SKiran Patil 	}
168540319796SKiran Patil }
168640319796SKiran Patil 
168740319796SKiran Patil /**
1688cac2a27cSHenry Tieman  * ice_set_fdir_input_set - Set the input set for Flow Director
1689cac2a27cSHenry Tieman  * @vsi: pointer to target VSI
1690cac2a27cSHenry Tieman  * @fsp: pointer to ethtool Rx flow specification
1691cac2a27cSHenry Tieman  * @input: filter structure
1692cac2a27cSHenry Tieman  */
1693cac2a27cSHenry Tieman static int
ice_set_fdir_input_set(struct ice_vsi * vsi,struct ethtool_rx_flow_spec * fsp,struct ice_fdir_fltr * input)1694cac2a27cSHenry Tieman ice_set_fdir_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
1695cac2a27cSHenry Tieman 		       struct ice_fdir_fltr *input)
1696cac2a27cSHenry Tieman {
1697cac2a27cSHenry Tieman 	u16 dest_vsi, q_index = 0;
169840319796SKiran Patil 	u16 orig_q_index = 0;
1699cac2a27cSHenry Tieman 	struct ice_pf *pf;
1700cac2a27cSHenry Tieman 	struct ice_hw *hw;
1701cac2a27cSHenry Tieman 	int flow_type;
1702cac2a27cSHenry Tieman 	u8 dest_ctl;
1703cac2a27cSHenry Tieman 
1704cac2a27cSHenry Tieman 	if (!vsi || !fsp || !input)
1705cac2a27cSHenry Tieman 		return -EINVAL;
1706cac2a27cSHenry Tieman 
1707cac2a27cSHenry Tieman 	pf = vsi->back;
1708cac2a27cSHenry Tieman 	hw = &pf->hw;
1709cac2a27cSHenry Tieman 
1710cac2a27cSHenry Tieman 	dest_vsi = vsi->idx;
1711cac2a27cSHenry Tieman 	if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
1712cac2a27cSHenry Tieman 		dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DROP_PKT;
1713cac2a27cSHenry Tieman 	} else {
1714cac2a27cSHenry Tieman 		u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie);
1715cac2a27cSHenry Tieman 		u8 vf = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie);
1716cac2a27cSHenry Tieman 
1717cac2a27cSHenry Tieman 		if (vf) {
1718cac2a27cSHenry Tieman 			dev_err(ice_pf_to_dev(pf), "Failed to add filter. Flow director filters are not supported on VF queues.\n");
1719cac2a27cSHenry Tieman 			return -EINVAL;
1720cac2a27cSHenry Tieman 		}
1721cac2a27cSHenry Tieman 
1722cac2a27cSHenry Tieman 		if (ring >= vsi->num_rxq)
1723cac2a27cSHenry Tieman 			return -EINVAL;
1724cac2a27cSHenry Tieman 
172540319796SKiran Patil 		orig_q_index = ring;
172640319796SKiran Patil 		ice_update_ring_dest_vsi(vsi, &dest_vsi, &ring);
1727cac2a27cSHenry Tieman 		dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DIRECT_PKT_QINDEX;
1728cac2a27cSHenry Tieman 		q_index = ring;
1729cac2a27cSHenry Tieman 	}
1730cac2a27cSHenry Tieman 
1731cac2a27cSHenry Tieman 	input->fltr_id = fsp->location;
1732cac2a27cSHenry Tieman 	input->q_index = q_index;
1733cac2a27cSHenry Tieman 	flow_type = fsp->flow_type & ~FLOW_EXT;
1734cac2a27cSHenry Tieman 
173540319796SKiran Patil 	/* Record the original queue index as specified by user.
173640319796SKiran Patil 	 * with channel configuration 'q_index' becomes relative
173740319796SKiran Patil 	 * to TC (channel).
173840319796SKiran Patil 	 */
173940319796SKiran Patil 	input->orig_q_index = orig_q_index;
1740cac2a27cSHenry Tieman 	input->dest_vsi = dest_vsi;
1741cac2a27cSHenry Tieman 	input->dest_ctl = dest_ctl;
1742cac2a27cSHenry Tieman 	input->fltr_status = ICE_FLTR_PRGM_DESC_FD_STATUS_FD_ID;
1743cac2a27cSHenry Tieman 	input->cnt_index = ICE_FD_SB_STAT_IDX(hw->fd_ctr_base);
1744cac2a27cSHenry Tieman 	input->flow_type = ice_ethtool_flow_to_fltr(flow_type);
1745cac2a27cSHenry Tieman 
1746cac2a27cSHenry Tieman 	if (fsp->flow_type & FLOW_EXT) {
1747cac2a27cSHenry Tieman 		memcpy(input->ext_data.usr_def, fsp->h_ext.data,
1748cac2a27cSHenry Tieman 		       sizeof(input->ext_data.usr_def));
1749cac2a27cSHenry Tieman 		input->ext_data.vlan_type = fsp->h_ext.vlan_etype;
1750cac2a27cSHenry Tieman 		input->ext_data.vlan_tag = fsp->h_ext.vlan_tci;
1751cac2a27cSHenry Tieman 		memcpy(input->ext_mask.usr_def, fsp->m_ext.data,
1752cac2a27cSHenry Tieman 		       sizeof(input->ext_mask.usr_def));
1753cac2a27cSHenry Tieman 		input->ext_mask.vlan_type = fsp->m_ext.vlan_etype;
1754cac2a27cSHenry Tieman 		input->ext_mask.vlan_tag = fsp->m_ext.vlan_tci;
1755cac2a27cSHenry Tieman 	}
1756cac2a27cSHenry Tieman 
1757cac2a27cSHenry Tieman 	switch (flow_type) {
1758cac2a27cSHenry Tieman 	case TCP_V4_FLOW:
1759cac2a27cSHenry Tieman 	case UDP_V4_FLOW:
1760cac2a27cSHenry Tieman 	case SCTP_V4_FLOW:
1761165d80d6SHenry Tieman 		input->ip.v4.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
1762165d80d6SHenry Tieman 		input->ip.v4.src_port = fsp->h_u.tcp_ip4_spec.psrc;
1763165d80d6SHenry Tieman 		input->ip.v4.dst_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
1764165d80d6SHenry Tieman 		input->ip.v4.src_ip = fsp->h_u.tcp_ip4_spec.ip4src;
1765165d80d6SHenry Tieman 		input->mask.v4.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
1766165d80d6SHenry Tieman 		input->mask.v4.src_port = fsp->m_u.tcp_ip4_spec.psrc;
1767165d80d6SHenry Tieman 		input->mask.v4.dst_ip = fsp->m_u.tcp_ip4_spec.ip4dst;
1768165d80d6SHenry Tieman 		input->mask.v4.src_ip = fsp->m_u.tcp_ip4_spec.ip4src;
1769cac2a27cSHenry Tieman 		break;
1770cac2a27cSHenry Tieman 	case IPV4_USER_FLOW:
1771165d80d6SHenry Tieman 		input->ip.v4.dst_ip = fsp->h_u.usr_ip4_spec.ip4dst;
1772165d80d6SHenry Tieman 		input->ip.v4.src_ip = fsp->h_u.usr_ip4_spec.ip4src;
1773165d80d6SHenry Tieman 		input->ip.v4.l4_header = fsp->h_u.usr_ip4_spec.l4_4_bytes;
1774165d80d6SHenry Tieman 		input->ip.v4.proto = fsp->h_u.usr_ip4_spec.proto;
1775165d80d6SHenry Tieman 		input->ip.v4.ip_ver = fsp->h_u.usr_ip4_spec.ip_ver;
1776165d80d6SHenry Tieman 		input->ip.v4.tos = fsp->h_u.usr_ip4_spec.tos;
1777165d80d6SHenry Tieman 		input->mask.v4.dst_ip = fsp->m_u.usr_ip4_spec.ip4dst;
1778165d80d6SHenry Tieman 		input->mask.v4.src_ip = fsp->m_u.usr_ip4_spec.ip4src;
1779165d80d6SHenry Tieman 		input->mask.v4.l4_header = fsp->m_u.usr_ip4_spec.l4_4_bytes;
1780165d80d6SHenry Tieman 		input->mask.v4.proto = fsp->m_u.usr_ip4_spec.proto;
1781165d80d6SHenry Tieman 		input->mask.v4.ip_ver = fsp->m_u.usr_ip4_spec.ip_ver;
1782165d80d6SHenry Tieman 		input->mask.v4.tos = fsp->m_u.usr_ip4_spec.tos;
1783165d80d6SHenry Tieman 		break;
1784165d80d6SHenry Tieman 	case TCP_V6_FLOW:
1785165d80d6SHenry Tieman 	case UDP_V6_FLOW:
1786165d80d6SHenry Tieman 	case SCTP_V6_FLOW:
1787165d80d6SHenry Tieman 		memcpy(input->ip.v6.dst_ip, fsp->h_u.usr_ip6_spec.ip6dst,
1788165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1789165d80d6SHenry Tieman 		memcpy(input->ip.v6.src_ip, fsp->h_u.usr_ip6_spec.ip6src,
1790165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1791165d80d6SHenry Tieman 		input->ip.v6.dst_port = fsp->h_u.tcp_ip6_spec.pdst;
1792165d80d6SHenry Tieman 		input->ip.v6.src_port = fsp->h_u.tcp_ip6_spec.psrc;
1793165d80d6SHenry Tieman 		input->ip.v6.tc = fsp->h_u.tcp_ip6_spec.tclass;
1794165d80d6SHenry Tieman 		memcpy(input->mask.v6.dst_ip, fsp->m_u.tcp_ip6_spec.ip6dst,
1795165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1796165d80d6SHenry Tieman 		memcpy(input->mask.v6.src_ip, fsp->m_u.tcp_ip6_spec.ip6src,
1797165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1798165d80d6SHenry Tieman 		input->mask.v6.dst_port = fsp->m_u.tcp_ip6_spec.pdst;
1799165d80d6SHenry Tieman 		input->mask.v6.src_port = fsp->m_u.tcp_ip6_spec.psrc;
1800165d80d6SHenry Tieman 		input->mask.v6.tc = fsp->m_u.tcp_ip6_spec.tclass;
1801165d80d6SHenry Tieman 		break;
1802165d80d6SHenry Tieman 	case IPV6_USER_FLOW:
1803165d80d6SHenry Tieman 		memcpy(input->ip.v6.dst_ip, fsp->h_u.usr_ip6_spec.ip6dst,
1804165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1805165d80d6SHenry Tieman 		memcpy(input->ip.v6.src_ip, fsp->h_u.usr_ip6_spec.ip6src,
1806165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1807165d80d6SHenry Tieman 		input->ip.v6.l4_header = fsp->h_u.usr_ip6_spec.l4_4_bytes;
1808165d80d6SHenry Tieman 		input->ip.v6.tc = fsp->h_u.usr_ip6_spec.tclass;
180929e2d9ebSHenry Tieman 
181029e2d9ebSHenry Tieman 		/* if no protocol requested, use IPPROTO_NONE */
181129e2d9ebSHenry Tieman 		if (!fsp->m_u.usr_ip6_spec.l4_proto)
181229e2d9ebSHenry Tieman 			input->ip.v6.proto = IPPROTO_NONE;
181329e2d9ebSHenry Tieman 		else
1814165d80d6SHenry Tieman 			input->ip.v6.proto = fsp->h_u.usr_ip6_spec.l4_proto;
181529e2d9ebSHenry Tieman 
1816165d80d6SHenry Tieman 		memcpy(input->mask.v6.dst_ip, fsp->m_u.usr_ip6_spec.ip6dst,
1817165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1818165d80d6SHenry Tieman 		memcpy(input->mask.v6.src_ip, fsp->m_u.usr_ip6_spec.ip6src,
1819165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1820165d80d6SHenry Tieman 		input->mask.v6.l4_header = fsp->m_u.usr_ip6_spec.l4_4_bytes;
1821165d80d6SHenry Tieman 		input->mask.v6.tc = fsp->m_u.usr_ip6_spec.tclass;
1822165d80d6SHenry Tieman 		input->mask.v6.proto = fsp->m_u.usr_ip6_spec.l4_proto;
1823cac2a27cSHenry Tieman 		break;
1824cac2a27cSHenry Tieman 	default:
1825cac2a27cSHenry Tieman 		/* not doing un-parsed flow types */
1826cac2a27cSHenry Tieman 		return -EINVAL;
1827cac2a27cSHenry Tieman 	}
1828cac2a27cSHenry Tieman 
1829cac2a27cSHenry Tieman 	return 0;
1830cac2a27cSHenry Tieman }
1831cac2a27cSHenry Tieman 
1832cac2a27cSHenry Tieman /**
1833cac2a27cSHenry Tieman  * ice_add_fdir_ethtool - Add/Remove Flow Director filter
1834cac2a27cSHenry Tieman  * @vsi: pointer to target VSI
1835cac2a27cSHenry Tieman  * @cmd: command to add or delete Flow Director filter
1836cac2a27cSHenry Tieman  *
1837cac2a27cSHenry Tieman  * Returns 0 on success and negative values for failure
1838cac2a27cSHenry Tieman  */
ice_add_fdir_ethtool(struct ice_vsi * vsi,struct ethtool_rxnfc * cmd)1839cac2a27cSHenry Tieman int ice_add_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
1840cac2a27cSHenry Tieman {
18412c57ffcbSHenry Tieman 	struct ice_rx_flow_userdef userdata;
1842cac2a27cSHenry Tieman 	struct ethtool_rx_flow_spec *fsp;
1843cac2a27cSHenry Tieman 	struct ice_fdir_fltr *input;
1844cac2a27cSHenry Tieman 	struct device *dev;
1845cac2a27cSHenry Tieman 	struct ice_pf *pf;
1846cac2a27cSHenry Tieman 	struct ice_hw *hw;
1847cac2a27cSHenry Tieman 	int fltrs_needed;
1848cac2a27cSHenry Tieman 	u16 tunnel_port;
1849cac2a27cSHenry Tieman 	int ret;
1850cac2a27cSHenry Tieman 
1851cac2a27cSHenry Tieman 	if (!vsi)
1852cac2a27cSHenry Tieman 		return -EINVAL;
1853cac2a27cSHenry Tieman 
1854cac2a27cSHenry Tieman 	pf = vsi->back;
1855cac2a27cSHenry Tieman 	hw = &pf->hw;
1856cac2a27cSHenry Tieman 	dev = ice_pf_to_dev(pf);
1857cac2a27cSHenry Tieman 
1858cac2a27cSHenry Tieman 	if (!test_bit(ICE_FLAG_FD_ENA, pf->flags))
1859cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
1860cac2a27cSHenry Tieman 
1861cac2a27cSHenry Tieman 	/* Do not program filters during reset */
1862cac2a27cSHenry Tieman 	if (ice_is_reset_in_progress(pf->state)) {
1863cac2a27cSHenry Tieman 		dev_err(dev, "Device is resetting - adding Flow Director filters not supported during reset\n");
1864cac2a27cSHenry Tieman 		return -EBUSY;
1865cac2a27cSHenry Tieman 	}
1866cac2a27cSHenry Tieman 
1867cac2a27cSHenry Tieman 	fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
1868cac2a27cSHenry Tieman 
18692c57ffcbSHenry Tieman 	if (ice_parse_rx_flow_user_data(fsp, &userdata))
18702c57ffcbSHenry Tieman 		return -EINVAL;
18712c57ffcbSHenry Tieman 
1872cac2a27cSHenry Tieman 	if (fsp->flow_type & FLOW_MAC_EXT)
1873cac2a27cSHenry Tieman 		return -EINVAL;
1874cac2a27cSHenry Tieman 
18752c57ffcbSHenry Tieman 	ret = ice_cfg_fdir_xtrct_seq(pf, fsp, &userdata);
1876cac2a27cSHenry Tieman 	if (ret)
1877cac2a27cSHenry Tieman 		return ret;
1878cac2a27cSHenry Tieman 
1879cac2a27cSHenry Tieman 	if (fsp->location >= ice_get_fdir_cnt_all(hw)) {
1880cac2a27cSHenry Tieman 		dev_err(dev, "Failed to add filter.  The maximum number of flow director filters has been reached.\n");
1881cac2a27cSHenry Tieman 		return -ENOSPC;
1882cac2a27cSHenry Tieman 	}
1883cac2a27cSHenry Tieman 
1884cac2a27cSHenry Tieman 	/* return error if not an update and no available filters */
1885de6acd1cSMichal Swiatkowski 	fltrs_needed = ice_get_open_tunnel_port(hw, &tunnel_port, TNL_ALL) ? 2 : 1;
1886cac2a27cSHenry Tieman 	if (!ice_fdir_find_fltr_by_idx(hw, fsp->location) &&
1887cac2a27cSHenry Tieman 	    ice_fdir_num_avail_fltr(hw, pf->vsi[vsi->idx]) < fltrs_needed) {
1888cac2a27cSHenry Tieman 		dev_err(dev, "Failed to add filter.  The maximum number of flow director filters has been reached.\n");
1889cac2a27cSHenry Tieman 		return -ENOSPC;
1890cac2a27cSHenry Tieman 	}
1891cac2a27cSHenry Tieman 
1892cac2a27cSHenry Tieman 	input = devm_kzalloc(dev, sizeof(*input), GFP_KERNEL);
1893cac2a27cSHenry Tieman 	if (!input)
1894cac2a27cSHenry Tieman 		return -ENOMEM;
1895cac2a27cSHenry Tieman 
1896cac2a27cSHenry Tieman 	ret = ice_set_fdir_input_set(vsi, fsp, input);
1897cac2a27cSHenry Tieman 	if (ret)
1898cac2a27cSHenry Tieman 		goto free_input;
1899cac2a27cSHenry Tieman 
1900cac2a27cSHenry Tieman 	mutex_lock(&hw->fdir_fltr_lock);
1901cac2a27cSHenry Tieman 	if (ice_fdir_is_dup_fltr(hw, input)) {
1902cac2a27cSHenry Tieman 		ret = -EINVAL;
1903cac2a27cSHenry Tieman 		goto release_lock;
1904cac2a27cSHenry Tieman 	}
1905cac2a27cSHenry Tieman 
19062c57ffcbSHenry Tieman 	if (userdata.flex_fltr) {
19072c57ffcbSHenry Tieman 		input->flex_fltr = true;
19082c57ffcbSHenry Tieman 		input->flex_word = cpu_to_be16(userdata.flex_word);
19092c57ffcbSHenry Tieman 		input->flex_offset = userdata.flex_offset;
19102c57ffcbSHenry Tieman 	}
19112c57ffcbSHenry Tieman 
19121f7ea1cdSQi Zhang 	input->cnt_ena = ICE_FXD_FLTR_QW0_STAT_ENA_PKTS;
19131f7ea1cdSQi Zhang 	input->fdid_prio = ICE_FXD_FLTR_QW1_FDID_PRI_THREE;
19141f7ea1cdSQi Zhang 	input->comp_report = ICE_FXD_FLTR_QW0_COMP_REPORT_SW_FAIL;
19151f7ea1cdSQi Zhang 
1916cac2a27cSHenry Tieman 	/* input struct is added to the HW filter list */
1917*a3336056SJedrzej Jagielski 	ret = ice_fdir_update_list_entry(pf, input, fsp->location);
1918*a3336056SJedrzej Jagielski 	if (ret)
1919*a3336056SJedrzej Jagielski 		goto release_lock;
1920cac2a27cSHenry Tieman 
1921cac2a27cSHenry Tieman 	ret = ice_fdir_write_all_fltr(pf, input, true);
1922cac2a27cSHenry Tieman 	if (ret)
1923cac2a27cSHenry Tieman 		goto remove_sw_rule;
1924cac2a27cSHenry Tieman 
1925cac2a27cSHenry Tieman 	goto release_lock;
1926cac2a27cSHenry Tieman 
1927cac2a27cSHenry Tieman remove_sw_rule:
1928cac2a27cSHenry Tieman 	ice_fdir_update_cntrs(hw, input->flow_type, false);
192940319796SKiran Patil 	/* update sb-filters count, specific to ring->channel */
193040319796SKiran Patil 	ice_update_per_q_fltr(vsi, input->orig_q_index, false);
1931cac2a27cSHenry Tieman 	list_del(&input->fltr_node);
1932cac2a27cSHenry Tieman release_lock:
1933cac2a27cSHenry Tieman 	mutex_unlock(&hw->fdir_fltr_lock);
1934cac2a27cSHenry Tieman free_input:
1935cac2a27cSHenry Tieman 	if (ret)
1936cac2a27cSHenry Tieman 		devm_kfree(dev, input);
1937cac2a27cSHenry Tieman 
1938cac2a27cSHenry Tieman 	return ret;
1939cac2a27cSHenry Tieman }
1940