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"
8148beb61SHenry Tieman #include "ice_flow.h"
9148beb61SHenry Tieman 
10165d80d6SHenry Tieman static struct in6_addr full_ipv6_addr_mask = {
11165d80d6SHenry Tieman 	.in6_u = {
12165d80d6SHenry Tieman 		.u6_addr8 = {
13165d80d6SHenry Tieman 			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
14165d80d6SHenry Tieman 			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
15165d80d6SHenry Tieman 		}
16165d80d6SHenry Tieman 	}
17165d80d6SHenry Tieman };
18165d80d6SHenry Tieman 
19165d80d6SHenry Tieman static struct in6_addr zero_ipv6_addr_mask = {
20165d80d6SHenry Tieman 	.in6_u = {
21165d80d6SHenry Tieman 		.u6_addr8 = {
22165d80d6SHenry Tieman 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
23165d80d6SHenry Tieman 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
24165d80d6SHenry Tieman 		}
25165d80d6SHenry Tieman 	}
26165d80d6SHenry Tieman };
27165d80d6SHenry Tieman 
28148beb61SHenry Tieman /* calls to ice_flow_add_prof require the number of segments in the array
29148beb61SHenry Tieman  * for segs_cnt. In this code that is one more than the index.
30148beb61SHenry Tieman  */
31148beb61SHenry Tieman #define TNL_SEG_CNT(_TNL_) ((_TNL_) + 1)
32148beb61SHenry Tieman 
33148beb61SHenry Tieman /**
344ab95646SHenry Tieman  * ice_fltr_to_ethtool_flow - convert filter type values to ethtool
354ab95646SHenry Tieman  * flow type values
364ab95646SHenry Tieman  * @flow: filter type to be converted
374ab95646SHenry Tieman  *
384ab95646SHenry Tieman  * Returns the corresponding ethtool flow type.
394ab95646SHenry Tieman  */
404ab95646SHenry Tieman static int ice_fltr_to_ethtool_flow(enum ice_fltr_ptype flow)
414ab95646SHenry Tieman {
424ab95646SHenry Tieman 	switch (flow) {
434ab95646SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV4_TCP:
444ab95646SHenry Tieman 		return TCP_V4_FLOW;
454ab95646SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV4_UDP:
464ab95646SHenry Tieman 		return UDP_V4_FLOW;
474ab95646SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV4_SCTP:
484ab95646SHenry Tieman 		return SCTP_V4_FLOW;
494ab95646SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV4_OTHER:
504ab95646SHenry Tieman 		return IPV4_USER_FLOW;
51165d80d6SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV6_TCP:
52165d80d6SHenry Tieman 		return TCP_V6_FLOW;
53165d80d6SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV6_UDP:
54165d80d6SHenry Tieman 		return UDP_V6_FLOW;
55165d80d6SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV6_SCTP:
56165d80d6SHenry Tieman 		return SCTP_V6_FLOW;
57165d80d6SHenry Tieman 	case ICE_FLTR_PTYPE_NONF_IPV6_OTHER:
58165d80d6SHenry Tieman 		return IPV6_USER_FLOW;
594ab95646SHenry Tieman 	default:
604ab95646SHenry Tieman 		/* 0 is undefined ethtool flow */
614ab95646SHenry Tieman 		return 0;
624ab95646SHenry Tieman 	}
634ab95646SHenry Tieman }
644ab95646SHenry Tieman 
654ab95646SHenry Tieman /**
664ab95646SHenry Tieman  * ice_ethtool_flow_to_fltr - convert ethtool flow type to filter enum
674ab95646SHenry Tieman  * @eth: Ethtool flow type to be converted
684ab95646SHenry Tieman  *
694ab95646SHenry Tieman  * Returns flow enum
704ab95646SHenry Tieman  */
714ab95646SHenry Tieman static enum ice_fltr_ptype ice_ethtool_flow_to_fltr(int eth)
724ab95646SHenry Tieman {
734ab95646SHenry Tieman 	switch (eth) {
744ab95646SHenry Tieman 	case TCP_V4_FLOW:
754ab95646SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV4_TCP;
764ab95646SHenry Tieman 	case UDP_V4_FLOW:
774ab95646SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV4_UDP;
784ab95646SHenry Tieman 	case SCTP_V4_FLOW:
794ab95646SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV4_SCTP;
804ab95646SHenry Tieman 	case IPV4_USER_FLOW:
814ab95646SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV4_OTHER;
82165d80d6SHenry Tieman 	case TCP_V6_FLOW:
83165d80d6SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV6_TCP;
84165d80d6SHenry Tieman 	case UDP_V6_FLOW:
85165d80d6SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV6_UDP;
86165d80d6SHenry Tieman 	case SCTP_V6_FLOW:
87165d80d6SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV6_SCTP;
88165d80d6SHenry Tieman 	case IPV6_USER_FLOW:
89165d80d6SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_IPV6_OTHER;
904ab95646SHenry Tieman 	default:
914ab95646SHenry Tieman 		return ICE_FLTR_PTYPE_NONF_NONE;
924ab95646SHenry Tieman 	}
934ab95646SHenry Tieman }
944ab95646SHenry Tieman 
954ab95646SHenry Tieman /**
962c57ffcbSHenry Tieman  * ice_is_mask_valid - check mask field set
972c57ffcbSHenry Tieman  * @mask: full mask to check
982c57ffcbSHenry Tieman  * @field: field for which mask should be valid
992c57ffcbSHenry Tieman  *
1002c57ffcbSHenry Tieman  * If the mask is fully set return true. If it is not valid for field return
1012c57ffcbSHenry Tieman  * false.
1022c57ffcbSHenry Tieman  */
1032c57ffcbSHenry Tieman static bool ice_is_mask_valid(u64 mask, u64 field)
1042c57ffcbSHenry Tieman {
1052c57ffcbSHenry Tieman 	return (mask & field) == field;
1062c57ffcbSHenry Tieman }
1072c57ffcbSHenry Tieman 
1082c57ffcbSHenry Tieman /**
1094ab95646SHenry Tieman  * ice_get_ethtool_fdir_entry - fill ethtool structure with fdir filter data
1104ab95646SHenry Tieman  * @hw: hardware structure that contains filter list
1114ab95646SHenry Tieman  * @cmd: ethtool command data structure to receive the filter data
1124ab95646SHenry Tieman  *
1134ab95646SHenry Tieman  * Returns 0 on success and -EINVAL on failure
1144ab95646SHenry Tieman  */
1154ab95646SHenry Tieman int ice_get_ethtool_fdir_entry(struct ice_hw *hw, struct ethtool_rxnfc *cmd)
1164ab95646SHenry Tieman {
1174ab95646SHenry Tieman 	struct ethtool_rx_flow_spec *fsp;
1184ab95646SHenry Tieman 	struct ice_fdir_fltr *rule;
1194ab95646SHenry Tieman 	int ret = 0;
1204ab95646SHenry Tieman 	u16 idx;
1214ab95646SHenry Tieman 
1224ab95646SHenry Tieman 	fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
1234ab95646SHenry Tieman 
1244ab95646SHenry Tieman 	mutex_lock(&hw->fdir_fltr_lock);
1254ab95646SHenry Tieman 
1264ab95646SHenry Tieman 	rule = ice_fdir_find_fltr_by_idx(hw, fsp->location);
1274ab95646SHenry Tieman 
1284ab95646SHenry Tieman 	if (!rule || fsp->location != rule->fltr_id) {
1294ab95646SHenry Tieman 		ret = -EINVAL;
1304ab95646SHenry Tieman 		goto release_lock;
1314ab95646SHenry Tieman 	}
1324ab95646SHenry Tieman 
1334ab95646SHenry Tieman 	fsp->flow_type = ice_fltr_to_ethtool_flow(rule->flow_type);
1344ab95646SHenry Tieman 
1354ab95646SHenry Tieman 	memset(&fsp->m_u, 0, sizeof(fsp->m_u));
1364ab95646SHenry Tieman 	memset(&fsp->m_ext, 0, sizeof(fsp->m_ext));
1374ab95646SHenry Tieman 
1384ab95646SHenry Tieman 	switch (fsp->flow_type) {
1394ab95646SHenry Tieman 	case IPV4_USER_FLOW:
1404ab95646SHenry Tieman 		fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
1414ab95646SHenry Tieman 		fsp->h_u.usr_ip4_spec.proto = 0;
142165d80d6SHenry Tieman 		fsp->h_u.usr_ip4_spec.l4_4_bytes = rule->ip.v4.l4_header;
143165d80d6SHenry Tieman 		fsp->h_u.usr_ip4_spec.tos = rule->ip.v4.tos;
144165d80d6SHenry Tieman 		fsp->h_u.usr_ip4_spec.ip4src = rule->ip.v4.src_ip;
145165d80d6SHenry Tieman 		fsp->h_u.usr_ip4_spec.ip4dst = rule->ip.v4.dst_ip;
146165d80d6SHenry Tieman 		fsp->m_u.usr_ip4_spec.ip4src = rule->mask.v4.src_ip;
147165d80d6SHenry Tieman 		fsp->m_u.usr_ip4_spec.ip4dst = rule->mask.v4.dst_ip;
1484ab95646SHenry Tieman 		fsp->m_u.usr_ip4_spec.ip_ver = 0xFF;
1494ab95646SHenry Tieman 		fsp->m_u.usr_ip4_spec.proto = 0;
150165d80d6SHenry Tieman 		fsp->m_u.usr_ip4_spec.l4_4_bytes = rule->mask.v4.l4_header;
151165d80d6SHenry Tieman 		fsp->m_u.usr_ip4_spec.tos = rule->mask.v4.tos;
1524ab95646SHenry Tieman 		break;
1534ab95646SHenry Tieman 	case TCP_V4_FLOW:
1544ab95646SHenry Tieman 	case UDP_V4_FLOW:
1554ab95646SHenry Tieman 	case SCTP_V4_FLOW:
156165d80d6SHenry Tieman 		fsp->h_u.tcp_ip4_spec.psrc = rule->ip.v4.src_port;
157165d80d6SHenry Tieman 		fsp->h_u.tcp_ip4_spec.pdst = rule->ip.v4.dst_port;
158165d80d6SHenry Tieman 		fsp->h_u.tcp_ip4_spec.ip4src = rule->ip.v4.src_ip;
159165d80d6SHenry Tieman 		fsp->h_u.tcp_ip4_spec.ip4dst = rule->ip.v4.dst_ip;
160165d80d6SHenry Tieman 		fsp->m_u.tcp_ip4_spec.psrc = rule->mask.v4.src_port;
161165d80d6SHenry Tieman 		fsp->m_u.tcp_ip4_spec.pdst = rule->mask.v4.dst_port;
162165d80d6SHenry Tieman 		fsp->m_u.tcp_ip4_spec.ip4src = rule->mask.v4.src_ip;
163165d80d6SHenry Tieman 		fsp->m_u.tcp_ip4_spec.ip4dst = rule->mask.v4.dst_ip;
164165d80d6SHenry Tieman 		break;
165165d80d6SHenry Tieman 	case IPV6_USER_FLOW:
166165d80d6SHenry Tieman 		fsp->h_u.usr_ip6_spec.l4_4_bytes = rule->ip.v6.l4_header;
167165d80d6SHenry Tieman 		fsp->h_u.usr_ip6_spec.tclass = rule->ip.v6.tc;
168165d80d6SHenry Tieman 		fsp->h_u.usr_ip6_spec.l4_proto = rule->ip.v6.proto;
169165d80d6SHenry Tieman 		memcpy(fsp->h_u.tcp_ip6_spec.ip6src, rule->ip.v6.src_ip,
170165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
171165d80d6SHenry Tieman 		memcpy(fsp->h_u.tcp_ip6_spec.ip6dst, rule->ip.v6.dst_ip,
172165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
173165d80d6SHenry Tieman 		memcpy(fsp->m_u.tcp_ip6_spec.ip6src, rule->mask.v6.src_ip,
174165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
175165d80d6SHenry Tieman 		memcpy(fsp->m_u.tcp_ip6_spec.ip6dst, rule->mask.v6.dst_ip,
176165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
177165d80d6SHenry Tieman 		fsp->m_u.usr_ip6_spec.l4_4_bytes = rule->mask.v6.l4_header;
178165d80d6SHenry Tieman 		fsp->m_u.usr_ip6_spec.tclass = rule->mask.v6.tc;
179165d80d6SHenry Tieman 		fsp->m_u.usr_ip6_spec.l4_proto = rule->mask.v6.proto;
180165d80d6SHenry Tieman 		break;
181165d80d6SHenry Tieman 	case TCP_V6_FLOW:
182165d80d6SHenry Tieman 	case UDP_V6_FLOW:
183165d80d6SHenry Tieman 	case SCTP_V6_FLOW:
184165d80d6SHenry Tieman 		memcpy(fsp->h_u.tcp_ip6_spec.ip6src, rule->ip.v6.src_ip,
185165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
186165d80d6SHenry Tieman 		memcpy(fsp->h_u.tcp_ip6_spec.ip6dst, rule->ip.v6.dst_ip,
187165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
188165d80d6SHenry Tieman 		fsp->h_u.tcp_ip6_spec.psrc = rule->ip.v6.src_port;
189165d80d6SHenry Tieman 		fsp->h_u.tcp_ip6_spec.pdst = rule->ip.v6.dst_port;
190165d80d6SHenry Tieman 		memcpy(fsp->m_u.tcp_ip6_spec.ip6src,
191165d80d6SHenry Tieman 		       rule->mask.v6.src_ip,
192165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
193165d80d6SHenry Tieman 		memcpy(fsp->m_u.tcp_ip6_spec.ip6dst,
194165d80d6SHenry Tieman 		       rule->mask.v6.dst_ip,
195165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
196165d80d6SHenry Tieman 		fsp->m_u.tcp_ip6_spec.psrc = rule->mask.v6.src_port;
197165d80d6SHenry Tieman 		fsp->m_u.tcp_ip6_spec.pdst = rule->mask.v6.dst_port;
198165d80d6SHenry Tieman 		fsp->h_u.tcp_ip6_spec.tclass = rule->ip.v6.tc;
199165d80d6SHenry Tieman 		fsp->m_u.tcp_ip6_spec.tclass = rule->mask.v6.tc;
2004ab95646SHenry Tieman 		break;
2014ab95646SHenry Tieman 	default:
2024ab95646SHenry Tieman 		break;
2034ab95646SHenry Tieman 	}
2044ab95646SHenry Tieman 
2054ab95646SHenry Tieman 	if (rule->dest_ctl == ICE_FLTR_PRGM_DESC_DEST_DROP_PKT)
2064ab95646SHenry Tieman 		fsp->ring_cookie = RX_CLS_FLOW_DISC;
2074ab95646SHenry Tieman 	else
2084ab95646SHenry Tieman 		fsp->ring_cookie = rule->q_index;
2094ab95646SHenry Tieman 
2104ab95646SHenry Tieman 	idx = ice_ethtool_flow_to_fltr(fsp->flow_type);
2114ab95646SHenry Tieman 	if (idx == ICE_FLTR_PTYPE_NONF_NONE) {
2124ab95646SHenry Tieman 		dev_err(ice_hw_to_dev(hw), "Missing input index for flow_type %d\n",
2134ab95646SHenry Tieman 			rule->flow_type);
2144ab95646SHenry Tieman 		ret = -EINVAL;
2154ab95646SHenry Tieman 	}
2164ab95646SHenry Tieman 
2174ab95646SHenry Tieman release_lock:
2184ab95646SHenry Tieman 	mutex_unlock(&hw->fdir_fltr_lock);
2194ab95646SHenry Tieman 	return ret;
2204ab95646SHenry Tieman }
2214ab95646SHenry Tieman 
2224ab95646SHenry Tieman /**
2234ab95646SHenry Tieman  * ice_get_fdir_fltr_ids - fill buffer with filter IDs of active filters
2244ab95646SHenry Tieman  * @hw: hardware structure containing the filter list
2254ab95646SHenry Tieman  * @cmd: ethtool command data structure
2264ab95646SHenry Tieman  * @rule_locs: ethtool array passed in from OS to receive filter IDs
2274ab95646SHenry Tieman  *
2284ab95646SHenry Tieman  * Returns 0 as expected for success by ethtool
2294ab95646SHenry Tieman  */
2304ab95646SHenry Tieman int
2314ab95646SHenry Tieman ice_get_fdir_fltr_ids(struct ice_hw *hw, struct ethtool_rxnfc *cmd,
2324ab95646SHenry Tieman 		      u32 *rule_locs)
2334ab95646SHenry Tieman {
2344ab95646SHenry Tieman 	struct ice_fdir_fltr *f_rule;
2354ab95646SHenry Tieman 	unsigned int cnt = 0;
2364ab95646SHenry Tieman 	int val = 0;
2374ab95646SHenry Tieman 
2384ab95646SHenry Tieman 	/* report total rule count */
2394ab95646SHenry Tieman 	cmd->data = ice_get_fdir_cnt_all(hw);
2404ab95646SHenry Tieman 
2414ab95646SHenry Tieman 	mutex_lock(&hw->fdir_fltr_lock);
2424ab95646SHenry Tieman 
2434ab95646SHenry Tieman 	list_for_each_entry(f_rule, &hw->fdir_list_head, fltr_node) {
2444ab95646SHenry Tieman 		if (cnt == cmd->rule_cnt) {
2454ab95646SHenry Tieman 			val = -EMSGSIZE;
2464ab95646SHenry Tieman 			goto release_lock;
2474ab95646SHenry Tieman 		}
2484ab95646SHenry Tieman 		rule_locs[cnt] = f_rule->fltr_id;
2494ab95646SHenry Tieman 		cnt++;
2504ab95646SHenry Tieman 	}
2514ab95646SHenry Tieman 
2524ab95646SHenry Tieman release_lock:
2534ab95646SHenry Tieman 	mutex_unlock(&hw->fdir_fltr_lock);
2544ab95646SHenry Tieman 	if (!val)
2554ab95646SHenry Tieman 		cmd->rule_cnt = cnt;
2564ab95646SHenry Tieman 	return val;
2574ab95646SHenry Tieman }
2584ab95646SHenry Tieman 
2594ab95646SHenry Tieman /**
260148beb61SHenry Tieman  * ice_fdir_get_hw_prof - return the ice_fd_hw_proc associated with a flow
261148beb61SHenry Tieman  * @hw: hardware structure containing the filter list
262148beb61SHenry Tieman  * @blk: hardware block
263148beb61SHenry Tieman  * @flow: FDir flow type to release
264148beb61SHenry Tieman  */
265148beb61SHenry Tieman static struct ice_fd_hw_prof *
266148beb61SHenry Tieman ice_fdir_get_hw_prof(struct ice_hw *hw, enum ice_block blk, int flow)
267148beb61SHenry Tieman {
268148beb61SHenry Tieman 	if (blk == ICE_BLK_FD && hw->fdir_prof)
269148beb61SHenry Tieman 		return hw->fdir_prof[flow];
270148beb61SHenry Tieman 
271148beb61SHenry Tieman 	return NULL;
272148beb61SHenry Tieman }
273148beb61SHenry Tieman 
274148beb61SHenry Tieman /**
275148beb61SHenry Tieman  * ice_fdir_erase_flow_from_hw - remove a flow from the HW profile tables
276148beb61SHenry Tieman  * @hw: hardware structure containing the filter list
277148beb61SHenry Tieman  * @blk: hardware block
278148beb61SHenry Tieman  * @flow: FDir flow type to release
279148beb61SHenry Tieman  */
280148beb61SHenry Tieman static void
281148beb61SHenry Tieman ice_fdir_erase_flow_from_hw(struct ice_hw *hw, enum ice_block blk, int flow)
282148beb61SHenry Tieman {
283148beb61SHenry Tieman 	struct ice_fd_hw_prof *prof = ice_fdir_get_hw_prof(hw, blk, flow);
284148beb61SHenry Tieman 	int tun;
285148beb61SHenry Tieman 
286148beb61SHenry Tieman 	if (!prof)
287148beb61SHenry Tieman 		return;
288148beb61SHenry Tieman 
289148beb61SHenry Tieman 	for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
290148beb61SHenry Tieman 		u64 prof_id;
291148beb61SHenry Tieman 		int j;
292148beb61SHenry Tieman 
293148beb61SHenry Tieman 		prof_id = flow + tun * ICE_FLTR_PTYPE_MAX;
294148beb61SHenry Tieman 		for (j = 0; j < prof->cnt; j++) {
295148beb61SHenry Tieman 			u16 vsi_num;
296148beb61SHenry Tieman 
297148beb61SHenry Tieman 			if (!prof->entry_h[j][tun] || !prof->vsi_h[j])
298148beb61SHenry Tieman 				continue;
299148beb61SHenry Tieman 			vsi_num = ice_get_hw_vsi_num(hw, prof->vsi_h[j]);
300148beb61SHenry Tieman 			ice_rem_prof_id_flow(hw, blk, vsi_num, prof_id);
301148beb61SHenry Tieman 			ice_flow_rem_entry(hw, blk, prof->entry_h[j][tun]);
302148beb61SHenry Tieman 			prof->entry_h[j][tun] = 0;
303148beb61SHenry Tieman 		}
304148beb61SHenry Tieman 		ice_flow_rem_prof(hw, blk, prof_id);
305148beb61SHenry Tieman 	}
306148beb61SHenry Tieman }
307148beb61SHenry Tieman 
308148beb61SHenry Tieman /**
309148beb61SHenry Tieman  * ice_fdir_rem_flow - release the ice_flow structures for a filter type
310148beb61SHenry Tieman  * @hw: hardware structure containing the filter list
311148beb61SHenry Tieman  * @blk: hardware block
312148beb61SHenry Tieman  * @flow_type: FDir flow type to release
313148beb61SHenry Tieman  */
314148beb61SHenry Tieman static void
315148beb61SHenry Tieman ice_fdir_rem_flow(struct ice_hw *hw, enum ice_block blk,
316148beb61SHenry Tieman 		  enum ice_fltr_ptype flow_type)
317148beb61SHenry Tieman {
318148beb61SHenry Tieman 	int flow = (int)flow_type & ~FLOW_EXT;
319148beb61SHenry Tieman 	struct ice_fd_hw_prof *prof;
320148beb61SHenry Tieman 	int tun, i;
321148beb61SHenry Tieman 
322148beb61SHenry Tieman 	prof = ice_fdir_get_hw_prof(hw, blk, flow);
323148beb61SHenry Tieman 	if (!prof)
324148beb61SHenry Tieman 		return;
325148beb61SHenry Tieman 
326148beb61SHenry Tieman 	ice_fdir_erase_flow_from_hw(hw, blk, flow);
327148beb61SHenry Tieman 	for (i = 0; i < prof->cnt; i++)
328148beb61SHenry Tieman 		prof->vsi_h[i] = 0;
329148beb61SHenry Tieman 	for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
330148beb61SHenry Tieman 		if (!prof->fdir_seg[tun])
331148beb61SHenry Tieman 			continue;
332148beb61SHenry Tieman 		devm_kfree(ice_hw_to_dev(hw), prof->fdir_seg[tun]);
333148beb61SHenry Tieman 		prof->fdir_seg[tun] = NULL;
334148beb61SHenry Tieman 	}
335148beb61SHenry Tieman 	prof->cnt = 0;
336148beb61SHenry Tieman }
337148beb61SHenry Tieman 
338148beb61SHenry Tieman /**
339148beb61SHenry Tieman  * ice_fdir_release_flows - release all flows in use for later replay
340148beb61SHenry Tieman  * @hw: pointer to HW instance
341148beb61SHenry Tieman  */
342148beb61SHenry Tieman void ice_fdir_release_flows(struct ice_hw *hw)
343148beb61SHenry Tieman {
344148beb61SHenry Tieman 	int flow;
345148beb61SHenry Tieman 
346148beb61SHenry Tieman 	/* release Flow Director HW table entries */
347148beb61SHenry Tieman 	for (flow = 0; flow < ICE_FLTR_PTYPE_MAX; flow++)
348148beb61SHenry Tieman 		ice_fdir_erase_flow_from_hw(hw, ICE_BLK_FD, flow);
349148beb61SHenry Tieman }
350148beb61SHenry Tieman 
351148beb61SHenry Tieman /**
35283af0039SHenry Tieman  * ice_fdir_replay_flows - replay HW Flow Director filter info
35383af0039SHenry Tieman  * @hw: pointer to HW instance
35483af0039SHenry Tieman  */
35583af0039SHenry Tieman void ice_fdir_replay_flows(struct ice_hw *hw)
35683af0039SHenry Tieman {
35783af0039SHenry Tieman 	int flow;
35883af0039SHenry Tieman 
35983af0039SHenry Tieman 	for (flow = 0; flow < ICE_FLTR_PTYPE_MAX; flow++) {
36083af0039SHenry Tieman 		int tun;
36183af0039SHenry Tieman 
36283af0039SHenry Tieman 		if (!hw->fdir_prof[flow] || !hw->fdir_prof[flow]->cnt)
36383af0039SHenry Tieman 			continue;
36483af0039SHenry Tieman 		for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
36583af0039SHenry Tieman 			struct ice_flow_prof *hw_prof;
36683af0039SHenry Tieman 			struct ice_fd_hw_prof *prof;
36783af0039SHenry Tieman 			u64 prof_id;
36883af0039SHenry Tieman 			int j;
36983af0039SHenry Tieman 
37083af0039SHenry Tieman 			prof = hw->fdir_prof[flow];
37183af0039SHenry Tieman 			prof_id = flow + tun * ICE_FLTR_PTYPE_MAX;
37283af0039SHenry Tieman 			ice_flow_add_prof(hw, ICE_BLK_FD, ICE_FLOW_RX, prof_id,
37383af0039SHenry Tieman 					  prof->fdir_seg[tun], TNL_SEG_CNT(tun),
37483af0039SHenry Tieman 					  &hw_prof);
37583af0039SHenry Tieman 			for (j = 0; j < prof->cnt; j++) {
37683af0039SHenry Tieman 				enum ice_flow_priority prio;
37783af0039SHenry Tieman 				u64 entry_h = 0;
37883af0039SHenry Tieman 				int err;
37983af0039SHenry Tieman 
38083af0039SHenry Tieman 				prio = ICE_FLOW_PRIO_NORMAL;
38183af0039SHenry Tieman 				err = ice_flow_add_entry(hw, ICE_BLK_FD,
38283af0039SHenry Tieman 							 prof_id,
38383af0039SHenry Tieman 							 prof->vsi_h[0],
38483af0039SHenry Tieman 							 prof->vsi_h[j],
38583af0039SHenry Tieman 							 prio, prof->fdir_seg,
38683af0039SHenry Tieman 							 &entry_h);
38783af0039SHenry Tieman 				if (err) {
38883af0039SHenry Tieman 					dev_err(ice_hw_to_dev(hw), "Could not replay Flow Director, flow type %d\n",
38983af0039SHenry Tieman 						flow);
39083af0039SHenry Tieman 					continue;
39183af0039SHenry Tieman 				}
39283af0039SHenry Tieman 				prof->entry_h[j][tun] = entry_h;
39383af0039SHenry Tieman 			}
39483af0039SHenry Tieman 		}
39583af0039SHenry Tieman 	}
39683af0039SHenry Tieman }
39783af0039SHenry Tieman 
39883af0039SHenry Tieman /**
3992c57ffcbSHenry Tieman  * ice_parse_rx_flow_user_data - deconstruct user-defined data
4002c57ffcbSHenry Tieman  * @fsp: pointer to ethtool Rx flow specification
4012c57ffcbSHenry Tieman  * @data: pointer to userdef data structure for storage
4022c57ffcbSHenry Tieman  *
4032c57ffcbSHenry Tieman  * Returns 0 on success, negative error value on failure
4042c57ffcbSHenry Tieman  */
4052c57ffcbSHenry Tieman static int
4062c57ffcbSHenry Tieman ice_parse_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp,
4072c57ffcbSHenry Tieman 			    struct ice_rx_flow_userdef *data)
4082c57ffcbSHenry Tieman {
4092c57ffcbSHenry Tieman 	u64 value, mask;
4102c57ffcbSHenry Tieman 
4112c57ffcbSHenry Tieman 	memset(data, 0, sizeof(*data));
4122c57ffcbSHenry Tieman 	if (!(fsp->flow_type & FLOW_EXT))
4132c57ffcbSHenry Tieman 		return 0;
4142c57ffcbSHenry Tieman 
4152c57ffcbSHenry Tieman 	value = be64_to_cpu(*((__force __be64 *)fsp->h_ext.data));
4162c57ffcbSHenry Tieman 	mask = be64_to_cpu(*((__force __be64 *)fsp->m_ext.data));
4172c57ffcbSHenry Tieman 	if (!mask)
4182c57ffcbSHenry Tieman 		return 0;
4192c57ffcbSHenry Tieman 
4202c57ffcbSHenry Tieman #define ICE_USERDEF_FLEX_WORD_M	GENMASK_ULL(15, 0)
4212c57ffcbSHenry Tieman #define ICE_USERDEF_FLEX_OFFS_S	16
4222c57ffcbSHenry Tieman #define ICE_USERDEF_FLEX_OFFS_M	GENMASK_ULL(31, ICE_USERDEF_FLEX_OFFS_S)
4232c57ffcbSHenry Tieman #define ICE_USERDEF_FLEX_FLTR_M	GENMASK_ULL(31, 0)
4242c57ffcbSHenry Tieman 
4252c57ffcbSHenry Tieman 	/* 0x1fe is the maximum value for offsets stored in the internal
4262c57ffcbSHenry Tieman 	 * filtering tables.
4272c57ffcbSHenry Tieman 	 */
4282c57ffcbSHenry Tieman #define ICE_USERDEF_FLEX_MAX_OFFS_VAL 0x1fe
4292c57ffcbSHenry Tieman 
4302c57ffcbSHenry Tieman 	if (!ice_is_mask_valid(mask, ICE_USERDEF_FLEX_FLTR_M) ||
4312c57ffcbSHenry Tieman 	    value > ICE_USERDEF_FLEX_FLTR_M)
4322c57ffcbSHenry Tieman 		return -EINVAL;
4332c57ffcbSHenry Tieman 
4342c57ffcbSHenry Tieman 	data->flex_word = value & ICE_USERDEF_FLEX_WORD_M;
4352c57ffcbSHenry Tieman 	data->flex_offset = (value & ICE_USERDEF_FLEX_OFFS_M) >>
4362c57ffcbSHenry Tieman 			     ICE_USERDEF_FLEX_OFFS_S;
4372c57ffcbSHenry Tieman 	if (data->flex_offset > ICE_USERDEF_FLEX_MAX_OFFS_VAL)
4382c57ffcbSHenry Tieman 		return -EINVAL;
4392c57ffcbSHenry Tieman 
4402c57ffcbSHenry Tieman 	data->flex_fltr = true;
4412c57ffcbSHenry Tieman 
4422c57ffcbSHenry Tieman 	return 0;
4432c57ffcbSHenry Tieman }
4442c57ffcbSHenry Tieman 
4452c57ffcbSHenry Tieman /**
446cac2a27cSHenry Tieman  * ice_fdir_num_avail_fltr - return the number of unused flow director filters
447cac2a27cSHenry Tieman  * @hw: pointer to hardware structure
448cac2a27cSHenry Tieman  * @vsi: software VSI structure
449cac2a27cSHenry Tieman  *
450cac2a27cSHenry Tieman  * There are 2 filter pools: guaranteed and best effort(shared). Each VSI can
451cac2a27cSHenry Tieman  * use filters from either pool. The guaranteed pool is divided between VSIs.
452cac2a27cSHenry Tieman  * The best effort filter pool is common to all VSIs and is a device shared
453cac2a27cSHenry Tieman  * resource pool. The number of filters available to this VSI is the sum of
454cac2a27cSHenry Tieman  * the VSIs guaranteed filter pool and the global available best effort
455cac2a27cSHenry Tieman  * filter pool.
456cac2a27cSHenry Tieman  *
457cac2a27cSHenry Tieman  * Returns the number of available flow director filters to this VSI
458cac2a27cSHenry Tieman  */
459cac2a27cSHenry Tieman static int ice_fdir_num_avail_fltr(struct ice_hw *hw, struct ice_vsi *vsi)
460cac2a27cSHenry Tieman {
461cac2a27cSHenry Tieman 	u16 vsi_num = ice_get_hw_vsi_num(hw, vsi->idx);
462cac2a27cSHenry Tieman 	u16 num_guar;
463cac2a27cSHenry Tieman 	u16 num_be;
464cac2a27cSHenry Tieman 
465cac2a27cSHenry Tieman 	/* total guaranteed filters assigned to this VSI */
466cac2a27cSHenry Tieman 	num_guar = vsi->num_gfltr;
467cac2a27cSHenry Tieman 
468cac2a27cSHenry Tieman 	/* minus the guaranteed filters programed by this VSI */
469cac2a27cSHenry Tieman 	num_guar -= (rd32(hw, VSIQF_FD_CNT(vsi_num)) &
470cac2a27cSHenry Tieman 		     VSIQF_FD_CNT_FD_GCNT_M) >> VSIQF_FD_CNT_FD_GCNT_S;
471cac2a27cSHenry Tieman 
472cac2a27cSHenry Tieman 	/* total global best effort filters */
473cac2a27cSHenry Tieman 	num_be = hw->func_caps.fd_fltr_best_effort;
474cac2a27cSHenry Tieman 
475cac2a27cSHenry Tieman 	/* minus the global best effort filters programmed */
476cac2a27cSHenry Tieman 	num_be -= (rd32(hw, GLQF_FD_CNT) & GLQF_FD_CNT_FD_BCNT_M) >>
477cac2a27cSHenry Tieman 		   GLQF_FD_CNT_FD_BCNT_S;
478cac2a27cSHenry Tieman 
479cac2a27cSHenry Tieman 	return num_guar + num_be;
480cac2a27cSHenry Tieman }
481cac2a27cSHenry Tieman 
482cac2a27cSHenry Tieman /**
483148beb61SHenry Tieman  * ice_fdir_alloc_flow_prof - allocate FDir flow profile structure(s)
484148beb61SHenry Tieman  * @hw: HW structure containing the FDir flow profile structure(s)
485148beb61SHenry Tieman  * @flow: flow type to allocate the flow profile for
486148beb61SHenry Tieman  *
487148beb61SHenry Tieman  * Allocate the fdir_prof and fdir_prof[flow] if not already created. Return 0
488148beb61SHenry Tieman  * on success and negative on error.
489148beb61SHenry Tieman  */
490148beb61SHenry Tieman static int
491148beb61SHenry Tieman ice_fdir_alloc_flow_prof(struct ice_hw *hw, enum ice_fltr_ptype flow)
492148beb61SHenry Tieman {
493148beb61SHenry Tieman 	if (!hw)
494148beb61SHenry Tieman 		return -EINVAL;
495148beb61SHenry Tieman 
496148beb61SHenry Tieman 	if (!hw->fdir_prof) {
497148beb61SHenry Tieman 		hw->fdir_prof = devm_kcalloc(ice_hw_to_dev(hw),
498148beb61SHenry Tieman 					     ICE_FLTR_PTYPE_MAX,
499148beb61SHenry Tieman 					     sizeof(*hw->fdir_prof),
500148beb61SHenry Tieman 					     GFP_KERNEL);
501148beb61SHenry Tieman 		if (!hw->fdir_prof)
502148beb61SHenry Tieman 			return -ENOMEM;
503148beb61SHenry Tieman 	}
504148beb61SHenry Tieman 
505148beb61SHenry Tieman 	if (!hw->fdir_prof[flow]) {
506148beb61SHenry Tieman 		hw->fdir_prof[flow] = devm_kzalloc(ice_hw_to_dev(hw),
507148beb61SHenry Tieman 						   sizeof(**hw->fdir_prof),
508148beb61SHenry Tieman 						   GFP_KERNEL);
509148beb61SHenry Tieman 		if (!hw->fdir_prof[flow])
510148beb61SHenry Tieman 			return -ENOMEM;
511148beb61SHenry Tieman 	}
512148beb61SHenry Tieman 
513148beb61SHenry Tieman 	return 0;
514148beb61SHenry Tieman }
515148beb61SHenry Tieman 
516148beb61SHenry Tieman /**
517148beb61SHenry Tieman  * ice_fdir_set_hw_fltr_rule - Configure HW tables to generate a FDir rule
518148beb61SHenry Tieman  * @pf: pointer to the PF structure
519148beb61SHenry Tieman  * @seg: protocol header description pointer
520148beb61SHenry Tieman  * @flow: filter enum
521148beb61SHenry Tieman  * @tun: FDir segment to program
522148beb61SHenry Tieman  */
523148beb61SHenry Tieman static int
524148beb61SHenry Tieman ice_fdir_set_hw_fltr_rule(struct ice_pf *pf, struct ice_flow_seg_info *seg,
525148beb61SHenry Tieman 			  enum ice_fltr_ptype flow, enum ice_fd_hw_seg tun)
526148beb61SHenry Tieman {
527148beb61SHenry Tieman 	struct device *dev = ice_pf_to_dev(pf);
528148beb61SHenry Tieman 	struct ice_vsi *main_vsi, *ctrl_vsi;
529148beb61SHenry Tieman 	struct ice_flow_seg_info *old_seg;
530148beb61SHenry Tieman 	struct ice_flow_prof *prof = NULL;
531148beb61SHenry Tieman 	struct ice_fd_hw_prof *hw_prof;
532148beb61SHenry Tieman 	struct ice_hw *hw = &pf->hw;
533148beb61SHenry Tieman 	enum ice_status status;
534148beb61SHenry Tieman 	u64 entry1_h = 0;
535148beb61SHenry Tieman 	u64 entry2_h = 0;
536148beb61SHenry Tieman 	u64 prof_id;
537148beb61SHenry Tieman 	int err;
538148beb61SHenry Tieman 
539148beb61SHenry Tieman 	main_vsi = ice_get_main_vsi(pf);
540148beb61SHenry Tieman 	if (!main_vsi)
541148beb61SHenry Tieman 		return -EINVAL;
542148beb61SHenry Tieman 
543148beb61SHenry Tieman 	ctrl_vsi = ice_get_ctrl_vsi(pf);
544148beb61SHenry Tieman 	if (!ctrl_vsi)
545148beb61SHenry Tieman 		return -EINVAL;
546148beb61SHenry Tieman 
547148beb61SHenry Tieman 	err = ice_fdir_alloc_flow_prof(hw, flow);
548148beb61SHenry Tieman 	if (err)
549148beb61SHenry Tieman 		return err;
550148beb61SHenry Tieman 
551148beb61SHenry Tieman 	hw_prof = hw->fdir_prof[flow];
552148beb61SHenry Tieman 	old_seg = hw_prof->fdir_seg[tun];
553148beb61SHenry Tieman 	if (old_seg) {
554148beb61SHenry Tieman 		/* This flow_type already has a changed input set.
555148beb61SHenry Tieman 		 * If it matches the requested input set then we are
556148beb61SHenry Tieman 		 * done. Or, if it's different then it's an error.
557148beb61SHenry Tieman 		 */
558148beb61SHenry Tieman 		if (!memcmp(old_seg, seg, sizeof(*seg)))
559148beb61SHenry Tieman 			return -EEXIST;
560148beb61SHenry Tieman 
561cac2a27cSHenry Tieman 		/* if there are FDir filters using this flow,
562cac2a27cSHenry Tieman 		 * then return error.
563cac2a27cSHenry Tieman 		 */
564cac2a27cSHenry Tieman 		if (hw->fdir_fltr_cnt[flow]) {
565cac2a27cSHenry Tieman 			dev_err(dev, "Failed to add filter.  Flow director filters on each port must have the same input set.\n");
566cac2a27cSHenry Tieman 			return -EINVAL;
567cac2a27cSHenry Tieman 		}
568cac2a27cSHenry Tieman 
56928bf2672SBrett Creeley 		if (ice_is_arfs_using_perfect_flow(hw, flow)) {
57028bf2672SBrett Creeley 			dev_err(dev, "aRFS using perfect flow type %d, cannot change input set\n",
57128bf2672SBrett Creeley 				flow);
57228bf2672SBrett Creeley 			return -EINVAL;
57328bf2672SBrett Creeley 		}
57428bf2672SBrett Creeley 
575148beb61SHenry Tieman 		/* remove HW filter definition */
576148beb61SHenry Tieman 		ice_fdir_rem_flow(hw, ICE_BLK_FD, flow);
577148beb61SHenry Tieman 	}
578148beb61SHenry Tieman 
579148beb61SHenry Tieman 	/* Adding a profile, but there is only one header supported.
580148beb61SHenry Tieman 	 * That is the final parameters are 1 header (segment), no
581148beb61SHenry Tieman 	 * actions (NULL) and zero actions 0.
582148beb61SHenry Tieman 	 */
583148beb61SHenry Tieman 	prof_id = flow + tun * ICE_FLTR_PTYPE_MAX;
584148beb61SHenry Tieman 	status = ice_flow_add_prof(hw, ICE_BLK_FD, ICE_FLOW_RX, prof_id, seg,
585148beb61SHenry Tieman 				   TNL_SEG_CNT(tun), &prof);
586148beb61SHenry Tieman 	if (status)
587148beb61SHenry Tieman 		return ice_status_to_errno(status);
588148beb61SHenry Tieman 	status = ice_flow_add_entry(hw, ICE_BLK_FD, prof_id, main_vsi->idx,
589148beb61SHenry Tieman 				    main_vsi->idx, ICE_FLOW_PRIO_NORMAL,
590148beb61SHenry Tieman 				    seg, &entry1_h);
591148beb61SHenry Tieman 	if (status) {
592148beb61SHenry Tieman 		err = ice_status_to_errno(status);
593148beb61SHenry Tieman 		goto err_prof;
594148beb61SHenry Tieman 	}
595148beb61SHenry Tieman 	status = ice_flow_add_entry(hw, ICE_BLK_FD, prof_id, main_vsi->idx,
596148beb61SHenry Tieman 				    ctrl_vsi->idx, ICE_FLOW_PRIO_NORMAL,
597148beb61SHenry Tieman 				    seg, &entry2_h);
598148beb61SHenry Tieman 	if (status) {
599148beb61SHenry Tieman 		err = ice_status_to_errno(status);
600148beb61SHenry Tieman 		goto err_entry;
601148beb61SHenry Tieman 	}
602148beb61SHenry Tieman 
603148beb61SHenry Tieman 	hw_prof->fdir_seg[tun] = seg;
604148beb61SHenry Tieman 	hw_prof->entry_h[0][tun] = entry1_h;
605148beb61SHenry Tieman 	hw_prof->entry_h[1][tun] = entry2_h;
606148beb61SHenry Tieman 	hw_prof->vsi_h[0] = main_vsi->idx;
607148beb61SHenry Tieman 	hw_prof->vsi_h[1] = ctrl_vsi->idx;
608148beb61SHenry Tieman 	if (!hw_prof->cnt)
609148beb61SHenry Tieman 		hw_prof->cnt = 2;
610148beb61SHenry Tieman 
611148beb61SHenry Tieman 	return 0;
612148beb61SHenry Tieman 
613148beb61SHenry Tieman err_entry:
614148beb61SHenry Tieman 	ice_rem_prof_id_flow(hw, ICE_BLK_FD,
615148beb61SHenry Tieman 			     ice_get_hw_vsi_num(hw, main_vsi->idx), prof_id);
616148beb61SHenry Tieman 	ice_flow_rem_entry(hw, ICE_BLK_FD, entry1_h);
617148beb61SHenry Tieman err_prof:
618148beb61SHenry Tieman 	ice_flow_rem_prof(hw, ICE_BLK_FD, prof_id);
619148beb61SHenry Tieman 	dev_err(dev, "Failed to add filter.  Flow director filters on each port must have the same input set.\n");
620148beb61SHenry Tieman 
621148beb61SHenry Tieman 	return err;
622148beb61SHenry Tieman }
623148beb61SHenry Tieman 
624148beb61SHenry Tieman /**
625148beb61SHenry Tieman  * ice_set_init_fdir_seg
626148beb61SHenry Tieman  * @seg: flow segment for programming
627165d80d6SHenry Tieman  * @l3_proto: ICE_FLOW_SEG_HDR_IPV4 or ICE_FLOW_SEG_HDR_IPV6
628148beb61SHenry Tieman  * @l4_proto: ICE_FLOW_SEG_HDR_TCP or ICE_FLOW_SEG_HDR_UDP
629148beb61SHenry Tieman  *
630148beb61SHenry Tieman  * Set the configuration for perfect filters to the provided flow segment for
631148beb61SHenry Tieman  * programming the HW filter. This is to be called only when initializing
632148beb61SHenry Tieman  * filters as this function it assumes no filters exist.
633148beb61SHenry Tieman  */
634148beb61SHenry Tieman static int
635148beb61SHenry Tieman ice_set_init_fdir_seg(struct ice_flow_seg_info *seg,
636165d80d6SHenry Tieman 		      enum ice_flow_seg_hdr l3_proto,
637148beb61SHenry Tieman 		      enum ice_flow_seg_hdr l4_proto)
638148beb61SHenry Tieman {
639165d80d6SHenry Tieman 	enum ice_flow_field src_addr, dst_addr, src_port, dst_port;
640148beb61SHenry Tieman 
641148beb61SHenry Tieman 	if (!seg)
642148beb61SHenry Tieman 		return -EINVAL;
643148beb61SHenry Tieman 
644165d80d6SHenry Tieman 	if (l3_proto == ICE_FLOW_SEG_HDR_IPV4) {
645165d80d6SHenry Tieman 		src_addr = ICE_FLOW_FIELD_IDX_IPV4_SA;
646165d80d6SHenry Tieman 		dst_addr = ICE_FLOW_FIELD_IDX_IPV4_DA;
647165d80d6SHenry Tieman 	} else if (l3_proto == ICE_FLOW_SEG_HDR_IPV6) {
648165d80d6SHenry Tieman 		src_addr = ICE_FLOW_FIELD_IDX_IPV6_SA;
649165d80d6SHenry Tieman 		dst_addr = ICE_FLOW_FIELD_IDX_IPV6_DA;
650165d80d6SHenry Tieman 	} else {
651165d80d6SHenry Tieman 		return -EINVAL;
652165d80d6SHenry Tieman 	}
653165d80d6SHenry Tieman 
654148beb61SHenry Tieman 	if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
655148beb61SHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
656148beb61SHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
657148beb61SHenry Tieman 	} else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
658148beb61SHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
659148beb61SHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
660148beb61SHenry Tieman 	} else {
661148beb61SHenry Tieman 		return -EINVAL;
662148beb61SHenry Tieman 	}
663148beb61SHenry Tieman 
664165d80d6SHenry Tieman 	ICE_FLOW_SET_HDRS(seg, l3_proto | l4_proto);
665148beb61SHenry Tieman 
666148beb61SHenry Tieman 	/* IP source address */
667165d80d6SHenry Tieman 	ice_flow_set_fld(seg, src_addr, ICE_FLOW_FLD_OFF_INVAL,
668165d80d6SHenry Tieman 			 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
669148beb61SHenry Tieman 
670148beb61SHenry Tieman 	/* IP destination address */
671165d80d6SHenry Tieman 	ice_flow_set_fld(seg, dst_addr, ICE_FLOW_FLD_OFF_INVAL,
672165d80d6SHenry Tieman 			 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
673148beb61SHenry Tieman 
674148beb61SHenry Tieman 	/* Layer 4 source port */
675148beb61SHenry Tieman 	ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
676148beb61SHenry Tieman 			 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
677148beb61SHenry Tieman 
678148beb61SHenry Tieman 	/* Layer 4 destination port */
679148beb61SHenry Tieman 	ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
680148beb61SHenry Tieman 			 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
681148beb61SHenry Tieman 
682148beb61SHenry Tieman 	return 0;
683148beb61SHenry Tieman }
684148beb61SHenry Tieman 
685148beb61SHenry Tieman /**
686148beb61SHenry Tieman  * ice_create_init_fdir_rule
687148beb61SHenry Tieman  * @pf: PF structure
688148beb61SHenry Tieman  * @flow: filter enum
689148beb61SHenry Tieman  *
690148beb61SHenry Tieman  * Return error value or 0 on success.
691148beb61SHenry Tieman  */
692148beb61SHenry Tieman static int
693148beb61SHenry Tieman ice_create_init_fdir_rule(struct ice_pf *pf, enum ice_fltr_ptype flow)
694148beb61SHenry Tieman {
695148beb61SHenry Tieman 	struct ice_flow_seg_info *seg, *tun_seg;
696148beb61SHenry Tieman 	struct device *dev = ice_pf_to_dev(pf);
697148beb61SHenry Tieman 	struct ice_hw *hw = &pf->hw;
698148beb61SHenry Tieman 	int ret;
699148beb61SHenry Tieman 
700148beb61SHenry Tieman 	/* if there is already a filter rule for kind return -EINVAL */
701148beb61SHenry Tieman 	if (hw->fdir_prof && hw->fdir_prof[flow] &&
702148beb61SHenry Tieman 	    hw->fdir_prof[flow]->fdir_seg[0])
703148beb61SHenry Tieman 		return -EINVAL;
704148beb61SHenry Tieman 
705148beb61SHenry Tieman 	seg = devm_kzalloc(dev, sizeof(*seg), GFP_KERNEL);
706148beb61SHenry Tieman 	if (!seg)
707148beb61SHenry Tieman 		return -ENOMEM;
708148beb61SHenry Tieman 
709148beb61SHenry Tieman 	tun_seg = devm_kzalloc(dev, sizeof(*seg) * ICE_FD_HW_SEG_MAX,
710148beb61SHenry Tieman 			       GFP_KERNEL);
711148beb61SHenry Tieman 	if (!tun_seg) {
712148beb61SHenry Tieman 		devm_kfree(dev, seg);
713148beb61SHenry Tieman 		return -ENOMEM;
714148beb61SHenry Tieman 	}
715148beb61SHenry Tieman 
716148beb61SHenry Tieman 	if (flow == ICE_FLTR_PTYPE_NONF_IPV4_TCP)
717165d80d6SHenry Tieman 		ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV4,
718165d80d6SHenry Tieman 					    ICE_FLOW_SEG_HDR_TCP);
719148beb61SHenry Tieman 	else if (flow == ICE_FLTR_PTYPE_NONF_IPV4_UDP)
720165d80d6SHenry Tieman 		ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV4,
721165d80d6SHenry Tieman 					    ICE_FLOW_SEG_HDR_UDP);
722165d80d6SHenry Tieman 	else if (flow == ICE_FLTR_PTYPE_NONF_IPV6_TCP)
723165d80d6SHenry Tieman 		ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV6,
724165d80d6SHenry Tieman 					    ICE_FLOW_SEG_HDR_TCP);
725165d80d6SHenry Tieman 	else if (flow == ICE_FLTR_PTYPE_NONF_IPV6_UDP)
726165d80d6SHenry Tieman 		ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV6,
727165d80d6SHenry Tieman 					    ICE_FLOW_SEG_HDR_UDP);
728148beb61SHenry Tieman 	else
729148beb61SHenry Tieman 		ret = -EINVAL;
730148beb61SHenry Tieman 	if (ret)
731148beb61SHenry Tieman 		goto err_exit;
732148beb61SHenry Tieman 
733148beb61SHenry Tieman 	/* add filter for outer headers */
734148beb61SHenry Tieman 	ret = ice_fdir_set_hw_fltr_rule(pf, seg, flow, ICE_FD_HW_SEG_NON_TUN);
735148beb61SHenry Tieman 	if (ret)
736148beb61SHenry Tieman 		/* could not write filter, free memory */
737148beb61SHenry Tieman 		goto err_exit;
738148beb61SHenry Tieman 
739148beb61SHenry Tieman 	/* make tunneled filter HW entries if possible */
740148beb61SHenry Tieman 	memcpy(&tun_seg[1], seg, sizeof(*seg));
741148beb61SHenry Tieman 	ret = ice_fdir_set_hw_fltr_rule(pf, tun_seg, flow, ICE_FD_HW_SEG_TUN);
742148beb61SHenry Tieman 	if (ret)
743148beb61SHenry Tieman 		/* could not write tunnel filter, but outer header filter
744148beb61SHenry Tieman 		 * exists
745148beb61SHenry Tieman 		 */
746148beb61SHenry Tieman 		devm_kfree(dev, tun_seg);
747148beb61SHenry Tieman 
748148beb61SHenry Tieman 	set_bit(flow, hw->fdir_perfect_fltr);
749148beb61SHenry Tieman 	return ret;
750148beb61SHenry Tieman err_exit:
751148beb61SHenry Tieman 	devm_kfree(dev, tun_seg);
752148beb61SHenry Tieman 	devm_kfree(dev, seg);
753148beb61SHenry Tieman 
754148beb61SHenry Tieman 	return -EOPNOTSUPP;
755148beb61SHenry Tieman }
756148beb61SHenry Tieman 
757148beb61SHenry Tieman /**
758cac2a27cSHenry Tieman  * ice_set_fdir_ip4_seg
759cac2a27cSHenry Tieman  * @seg: flow segment for programming
760cac2a27cSHenry Tieman  * @tcp_ip4_spec: mask data from ethtool
761cac2a27cSHenry Tieman  * @l4_proto: Layer 4 protocol to program
762cac2a27cSHenry Tieman  * @perfect_fltr: only valid on success; returns true if perfect filter,
763cac2a27cSHenry Tieman  *		  false if not
764cac2a27cSHenry Tieman  *
765cac2a27cSHenry Tieman  * Set the mask data into the flow segment to be used to program HW
766cac2a27cSHenry Tieman  * table based on provided L4 protocol for IPv4
767cac2a27cSHenry Tieman  */
768cac2a27cSHenry Tieman static int
769cac2a27cSHenry Tieman ice_set_fdir_ip4_seg(struct ice_flow_seg_info *seg,
770cac2a27cSHenry Tieman 		     struct ethtool_tcpip4_spec *tcp_ip4_spec,
771cac2a27cSHenry Tieman 		     enum ice_flow_seg_hdr l4_proto, bool *perfect_fltr)
772cac2a27cSHenry Tieman {
773cac2a27cSHenry Tieman 	enum ice_flow_field src_port, dst_port;
774cac2a27cSHenry Tieman 
775cac2a27cSHenry Tieman 	/* make sure we don't have any empty rule */
776cac2a27cSHenry Tieman 	if (!tcp_ip4_spec->psrc && !tcp_ip4_spec->ip4src &&
777cac2a27cSHenry Tieman 	    !tcp_ip4_spec->pdst && !tcp_ip4_spec->ip4dst)
778cac2a27cSHenry Tieman 		return -EINVAL;
779cac2a27cSHenry Tieman 
780cac2a27cSHenry Tieman 	/* filtering on TOS not supported */
781cac2a27cSHenry Tieman 	if (tcp_ip4_spec->tos)
782cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
783cac2a27cSHenry Tieman 
784cac2a27cSHenry Tieman 	if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
785cac2a27cSHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
786cac2a27cSHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
787cac2a27cSHenry Tieman 	} else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
788cac2a27cSHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
789cac2a27cSHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
790cac2a27cSHenry Tieman 	} else if (l4_proto == ICE_FLOW_SEG_HDR_SCTP) {
791cac2a27cSHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT;
792cac2a27cSHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_SCTP_DST_PORT;
793cac2a27cSHenry Tieman 	} else {
794cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
795cac2a27cSHenry Tieman 	}
796cac2a27cSHenry Tieman 
797cac2a27cSHenry Tieman 	*perfect_fltr = true;
798cac2a27cSHenry Tieman 	ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4 | l4_proto);
799cac2a27cSHenry Tieman 
800cac2a27cSHenry Tieman 	/* IP source address */
801cac2a27cSHenry Tieman 	if (tcp_ip4_spec->ip4src == htonl(0xFFFFFFFF))
802cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_SA,
803cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
804cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
805cac2a27cSHenry Tieman 	else if (!tcp_ip4_spec->ip4src)
806cac2a27cSHenry Tieman 		*perfect_fltr = false;
807cac2a27cSHenry Tieman 	else
808cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
809cac2a27cSHenry Tieman 
810cac2a27cSHenry Tieman 	/* IP destination address */
811cac2a27cSHenry Tieman 	if (tcp_ip4_spec->ip4dst == htonl(0xFFFFFFFF))
812cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_DA,
813cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
814cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
815cac2a27cSHenry Tieman 	else if (!tcp_ip4_spec->ip4dst)
816cac2a27cSHenry Tieman 		*perfect_fltr = false;
817cac2a27cSHenry Tieman 	else
818cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
819cac2a27cSHenry Tieman 
820cac2a27cSHenry Tieman 	/* Layer 4 source port */
821cac2a27cSHenry Tieman 	if (tcp_ip4_spec->psrc == htons(0xFFFF))
822cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
823cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
824cac2a27cSHenry Tieman 				 false);
825cac2a27cSHenry Tieman 	else if (!tcp_ip4_spec->psrc)
826cac2a27cSHenry Tieman 		*perfect_fltr = false;
827cac2a27cSHenry Tieman 	else
828cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
829cac2a27cSHenry Tieman 
830cac2a27cSHenry Tieman 	/* Layer 4 destination port */
831cac2a27cSHenry Tieman 	if (tcp_ip4_spec->pdst == htons(0xFFFF))
832cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
833cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
834cac2a27cSHenry Tieman 				 false);
835cac2a27cSHenry Tieman 	else if (!tcp_ip4_spec->pdst)
836cac2a27cSHenry Tieman 		*perfect_fltr = false;
837cac2a27cSHenry Tieman 	else
838cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
839cac2a27cSHenry Tieman 
840cac2a27cSHenry Tieman 	return 0;
841cac2a27cSHenry Tieman }
842cac2a27cSHenry Tieman 
843cac2a27cSHenry Tieman /**
844cac2a27cSHenry Tieman  * ice_set_fdir_ip4_usr_seg
845cac2a27cSHenry Tieman  * @seg: flow segment for programming
846cac2a27cSHenry Tieman  * @usr_ip4_spec: ethtool userdef packet offset
847cac2a27cSHenry Tieman  * @perfect_fltr: only valid on success; returns true if perfect filter,
848cac2a27cSHenry Tieman  *		  false if not
849cac2a27cSHenry Tieman  *
850cac2a27cSHenry Tieman  * Set the offset data into the flow segment to be used to program HW
851cac2a27cSHenry Tieman  * table for IPv4
852cac2a27cSHenry Tieman  */
853cac2a27cSHenry Tieman static int
854cac2a27cSHenry Tieman ice_set_fdir_ip4_usr_seg(struct ice_flow_seg_info *seg,
855cac2a27cSHenry Tieman 			 struct ethtool_usrip4_spec *usr_ip4_spec,
856cac2a27cSHenry Tieman 			 bool *perfect_fltr)
857cac2a27cSHenry Tieman {
858cac2a27cSHenry Tieman 	/* first 4 bytes of Layer 4 header */
859cac2a27cSHenry Tieman 	if (usr_ip4_spec->l4_4_bytes)
860cac2a27cSHenry Tieman 		return -EINVAL;
861cac2a27cSHenry Tieman 	if (usr_ip4_spec->tos)
862cac2a27cSHenry Tieman 		return -EINVAL;
863cac2a27cSHenry Tieman 	if (usr_ip4_spec->ip_ver)
864cac2a27cSHenry Tieman 		return -EINVAL;
865cac2a27cSHenry Tieman 	/* Filtering on Layer 4 protocol not supported */
866cac2a27cSHenry Tieman 	if (usr_ip4_spec->proto)
867cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
868cac2a27cSHenry Tieman 	/* empty rules are not valid */
869cac2a27cSHenry Tieman 	if (!usr_ip4_spec->ip4src && !usr_ip4_spec->ip4dst)
870cac2a27cSHenry Tieman 		return -EINVAL;
871cac2a27cSHenry Tieman 
872cac2a27cSHenry Tieman 	*perfect_fltr = true;
873cac2a27cSHenry Tieman 	ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4);
874cac2a27cSHenry Tieman 
875cac2a27cSHenry Tieman 	/* IP source address */
876cac2a27cSHenry Tieman 	if (usr_ip4_spec->ip4src == htonl(0xFFFFFFFF))
877cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_SA,
878cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
879cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
880cac2a27cSHenry Tieman 	else if (!usr_ip4_spec->ip4src)
881cac2a27cSHenry Tieman 		*perfect_fltr = false;
882cac2a27cSHenry Tieman 	else
883cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
884cac2a27cSHenry Tieman 
885cac2a27cSHenry Tieman 	/* IP destination address */
886cac2a27cSHenry Tieman 	if (usr_ip4_spec->ip4dst == htonl(0xFFFFFFFF))
887cac2a27cSHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_DA,
888cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
889cac2a27cSHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
890cac2a27cSHenry Tieman 	else if (!usr_ip4_spec->ip4dst)
891cac2a27cSHenry Tieman 		*perfect_fltr = false;
892cac2a27cSHenry Tieman 	else
893cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
894cac2a27cSHenry Tieman 
895cac2a27cSHenry Tieman 	return 0;
896cac2a27cSHenry Tieman }
897cac2a27cSHenry Tieman 
898cac2a27cSHenry Tieman /**
899165d80d6SHenry Tieman  * ice_set_fdir_ip6_seg
900165d80d6SHenry Tieman  * @seg: flow segment for programming
901165d80d6SHenry Tieman  * @tcp_ip6_spec: mask data from ethtool
902165d80d6SHenry Tieman  * @l4_proto: Layer 4 protocol to program
903165d80d6SHenry Tieman  * @perfect_fltr: only valid on success; returns true if perfect filter,
904165d80d6SHenry Tieman  *		  false if not
905165d80d6SHenry Tieman  *
906165d80d6SHenry Tieman  * Set the mask data into the flow segment to be used to program HW
907165d80d6SHenry Tieman  * table based on provided L4 protocol for IPv6
908165d80d6SHenry Tieman  */
909165d80d6SHenry Tieman static int
910165d80d6SHenry Tieman ice_set_fdir_ip6_seg(struct ice_flow_seg_info *seg,
911165d80d6SHenry Tieman 		     struct ethtool_tcpip6_spec *tcp_ip6_spec,
912165d80d6SHenry Tieman 		     enum ice_flow_seg_hdr l4_proto, bool *perfect_fltr)
913165d80d6SHenry Tieman {
914165d80d6SHenry Tieman 	enum ice_flow_field src_port, dst_port;
915165d80d6SHenry Tieman 
916165d80d6SHenry Tieman 	/* make sure we don't have any empty rule */
917165d80d6SHenry Tieman 	if (!memcmp(tcp_ip6_spec->ip6src, &zero_ipv6_addr_mask,
918165d80d6SHenry Tieman 		    sizeof(struct in6_addr)) &&
919165d80d6SHenry Tieman 	    !memcmp(tcp_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
920165d80d6SHenry Tieman 		    sizeof(struct in6_addr)) &&
921165d80d6SHenry Tieman 	    !tcp_ip6_spec->psrc && !tcp_ip6_spec->pdst)
922165d80d6SHenry Tieman 		return -EINVAL;
923165d80d6SHenry Tieman 
924165d80d6SHenry Tieman 	/* filtering on TC not supported */
925165d80d6SHenry Tieman 	if (tcp_ip6_spec->tclass)
926165d80d6SHenry Tieman 		return -EOPNOTSUPP;
927165d80d6SHenry Tieman 
928165d80d6SHenry Tieman 	if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
929165d80d6SHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
930165d80d6SHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
931165d80d6SHenry Tieman 	} else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
932165d80d6SHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
933165d80d6SHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
934165d80d6SHenry Tieman 	} else if (l4_proto == ICE_FLOW_SEG_HDR_SCTP) {
935165d80d6SHenry Tieman 		src_port = ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT;
936165d80d6SHenry Tieman 		dst_port = ICE_FLOW_FIELD_IDX_SCTP_DST_PORT;
937165d80d6SHenry Tieman 	} else {
938165d80d6SHenry Tieman 		return -EINVAL;
939165d80d6SHenry Tieman 	}
940165d80d6SHenry Tieman 
941165d80d6SHenry Tieman 	*perfect_fltr = true;
942165d80d6SHenry Tieman 	ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV6 | l4_proto);
943165d80d6SHenry Tieman 
944165d80d6SHenry Tieman 	if (!memcmp(tcp_ip6_spec->ip6src, &full_ipv6_addr_mask,
945165d80d6SHenry Tieman 		    sizeof(struct in6_addr)))
946165d80d6SHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_SA,
947165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
948165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
949165d80d6SHenry Tieman 	else if (!memcmp(tcp_ip6_spec->ip6src, &zero_ipv6_addr_mask,
950165d80d6SHenry Tieman 			 sizeof(struct in6_addr)))
951165d80d6SHenry Tieman 		*perfect_fltr = false;
952165d80d6SHenry Tieman 	else
953165d80d6SHenry Tieman 		return -EOPNOTSUPP;
954165d80d6SHenry Tieman 
955165d80d6SHenry Tieman 	if (!memcmp(tcp_ip6_spec->ip6dst, &full_ipv6_addr_mask,
956165d80d6SHenry Tieman 		    sizeof(struct in6_addr)))
957165d80d6SHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_DA,
958165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
959165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
960165d80d6SHenry Tieman 	else if (!memcmp(tcp_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
961165d80d6SHenry Tieman 			 sizeof(struct in6_addr)))
962165d80d6SHenry Tieman 		*perfect_fltr = false;
963165d80d6SHenry Tieman 	else
964165d80d6SHenry Tieman 		return -EOPNOTSUPP;
965165d80d6SHenry Tieman 
966165d80d6SHenry Tieman 	/* Layer 4 source port */
967165d80d6SHenry Tieman 	if (tcp_ip6_spec->psrc == htons(0xFFFF))
968165d80d6SHenry Tieman 		ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
969165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
970165d80d6SHenry Tieman 				 false);
971165d80d6SHenry Tieman 	else if (!tcp_ip6_spec->psrc)
972165d80d6SHenry Tieman 		*perfect_fltr = false;
973165d80d6SHenry Tieman 	else
974165d80d6SHenry Tieman 		return -EOPNOTSUPP;
975165d80d6SHenry Tieman 
976165d80d6SHenry Tieman 	/* Layer 4 destination port */
977165d80d6SHenry Tieman 	if (tcp_ip6_spec->pdst == htons(0xFFFF))
978165d80d6SHenry Tieman 		ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
979165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
980165d80d6SHenry Tieman 				 false);
981165d80d6SHenry Tieman 	else if (!tcp_ip6_spec->pdst)
982165d80d6SHenry Tieman 		*perfect_fltr = false;
983165d80d6SHenry Tieman 	else
984165d80d6SHenry Tieman 		return -EOPNOTSUPP;
985165d80d6SHenry Tieman 
986165d80d6SHenry Tieman 	return 0;
987165d80d6SHenry Tieman }
988165d80d6SHenry Tieman 
989165d80d6SHenry Tieman /**
990165d80d6SHenry Tieman  * ice_set_fdir_ip6_usr_seg
991165d80d6SHenry Tieman  * @seg: flow segment for programming
992165d80d6SHenry Tieman  * @usr_ip6_spec: ethtool userdef packet offset
993165d80d6SHenry Tieman  * @perfect_fltr: only valid on success; returns true if perfect filter,
994165d80d6SHenry Tieman  *		  false if not
995165d80d6SHenry Tieman  *
996165d80d6SHenry Tieman  * Set the offset data into the flow segment to be used to program HW
997165d80d6SHenry Tieman  * table for IPv6
998165d80d6SHenry Tieman  */
999165d80d6SHenry Tieman static int
1000165d80d6SHenry Tieman ice_set_fdir_ip6_usr_seg(struct ice_flow_seg_info *seg,
1001165d80d6SHenry Tieman 			 struct ethtool_usrip6_spec *usr_ip6_spec,
1002165d80d6SHenry Tieman 			 bool *perfect_fltr)
1003165d80d6SHenry Tieman {
1004165d80d6SHenry Tieman 	/* filtering on Layer 4 bytes not supported */
1005165d80d6SHenry Tieman 	if (usr_ip6_spec->l4_4_bytes)
1006165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1007165d80d6SHenry Tieman 	/* filtering on TC not supported */
1008165d80d6SHenry Tieman 	if (usr_ip6_spec->tclass)
1009165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1010165d80d6SHenry Tieman 	/* filtering on Layer 4 protocol not supported */
1011165d80d6SHenry Tieman 	if (usr_ip6_spec->l4_proto)
1012165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1013165d80d6SHenry Tieman 	/* empty rules are not valid */
1014165d80d6SHenry Tieman 	if (!memcmp(usr_ip6_spec->ip6src, &zero_ipv6_addr_mask,
1015165d80d6SHenry Tieman 		    sizeof(struct in6_addr)) &&
1016165d80d6SHenry Tieman 	    !memcmp(usr_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
1017165d80d6SHenry Tieman 		    sizeof(struct in6_addr)))
1018165d80d6SHenry Tieman 		return -EINVAL;
1019165d80d6SHenry Tieman 
1020165d80d6SHenry Tieman 	*perfect_fltr = true;
1021165d80d6SHenry Tieman 	ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV6);
1022165d80d6SHenry Tieman 
1023165d80d6SHenry Tieman 	if (!memcmp(usr_ip6_spec->ip6src, &full_ipv6_addr_mask,
1024165d80d6SHenry Tieman 		    sizeof(struct in6_addr)))
1025165d80d6SHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_SA,
1026165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
1027165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
1028165d80d6SHenry Tieman 	else if (!memcmp(usr_ip6_spec->ip6src, &zero_ipv6_addr_mask,
1029165d80d6SHenry Tieman 			 sizeof(struct in6_addr)))
1030165d80d6SHenry Tieman 		*perfect_fltr = false;
1031165d80d6SHenry Tieman 	else
1032165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1033165d80d6SHenry Tieman 
1034165d80d6SHenry Tieman 	if (!memcmp(usr_ip6_spec->ip6dst, &full_ipv6_addr_mask,
1035165d80d6SHenry Tieman 		    sizeof(struct in6_addr)))
1036165d80d6SHenry Tieman 		ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_DA,
1037165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
1038165d80d6SHenry Tieman 				 ICE_FLOW_FLD_OFF_INVAL, false);
1039165d80d6SHenry Tieman 	else if (!memcmp(usr_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
1040165d80d6SHenry Tieman 			 sizeof(struct in6_addr)))
1041165d80d6SHenry Tieman 		*perfect_fltr = false;
1042165d80d6SHenry Tieman 	else
1043165d80d6SHenry Tieman 		return -EOPNOTSUPP;
1044165d80d6SHenry Tieman 
1045165d80d6SHenry Tieman 	return 0;
1046165d80d6SHenry Tieman }
1047165d80d6SHenry Tieman 
1048165d80d6SHenry Tieman /**
1049cac2a27cSHenry Tieman  * ice_cfg_fdir_xtrct_seq - Configure extraction sequence for the given filter
1050cac2a27cSHenry Tieman  * @pf: PF structure
1051cac2a27cSHenry Tieman  * @fsp: pointer to ethtool Rx flow specification
10522c57ffcbSHenry Tieman  * @user: user defined data from flow specification
1053cac2a27cSHenry Tieman  *
1054cac2a27cSHenry Tieman  * Returns 0 on success.
1055cac2a27cSHenry Tieman  */
1056cac2a27cSHenry Tieman static int
10572c57ffcbSHenry Tieman ice_cfg_fdir_xtrct_seq(struct ice_pf *pf, struct ethtool_rx_flow_spec *fsp,
10582c57ffcbSHenry Tieman 		       struct ice_rx_flow_userdef *user)
1059cac2a27cSHenry Tieman {
1060cac2a27cSHenry Tieman 	struct ice_flow_seg_info *seg, *tun_seg;
1061cac2a27cSHenry Tieman 	struct device *dev = ice_pf_to_dev(pf);
1062cac2a27cSHenry Tieman 	enum ice_fltr_ptype fltr_idx;
1063cac2a27cSHenry Tieman 	struct ice_hw *hw = &pf->hw;
1064cac2a27cSHenry Tieman 	bool perfect_filter;
1065cac2a27cSHenry Tieman 	int ret;
1066cac2a27cSHenry Tieman 
1067cac2a27cSHenry Tieman 	seg = devm_kzalloc(dev, sizeof(*seg), GFP_KERNEL);
1068cac2a27cSHenry Tieman 	if (!seg)
1069cac2a27cSHenry Tieman 		return -ENOMEM;
1070cac2a27cSHenry Tieman 
1071cac2a27cSHenry Tieman 	tun_seg = devm_kzalloc(dev, sizeof(*seg) * ICE_FD_HW_SEG_MAX,
1072cac2a27cSHenry Tieman 			       GFP_KERNEL);
1073cac2a27cSHenry Tieman 	if (!tun_seg) {
1074cac2a27cSHenry Tieman 		devm_kfree(dev, seg);
1075cac2a27cSHenry Tieman 		return -ENOMEM;
1076cac2a27cSHenry Tieman 	}
1077cac2a27cSHenry Tieman 
1078cac2a27cSHenry Tieman 	switch (fsp->flow_type & ~FLOW_EXT) {
1079cac2a27cSHenry Tieman 	case TCP_V4_FLOW:
1080cac2a27cSHenry Tieman 		ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
1081cac2a27cSHenry Tieman 					   ICE_FLOW_SEG_HDR_TCP,
1082cac2a27cSHenry Tieman 					   &perfect_filter);
1083cac2a27cSHenry Tieman 		break;
1084cac2a27cSHenry Tieman 	case UDP_V4_FLOW:
1085cac2a27cSHenry Tieman 		ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
1086cac2a27cSHenry Tieman 					   ICE_FLOW_SEG_HDR_UDP,
1087cac2a27cSHenry Tieman 					   &perfect_filter);
1088cac2a27cSHenry Tieman 		break;
1089cac2a27cSHenry Tieman 	case SCTP_V4_FLOW:
1090cac2a27cSHenry Tieman 		ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
1091cac2a27cSHenry Tieman 					   ICE_FLOW_SEG_HDR_SCTP,
1092cac2a27cSHenry Tieman 					   &perfect_filter);
1093cac2a27cSHenry Tieman 		break;
1094cac2a27cSHenry Tieman 	case IPV4_USER_FLOW:
1095cac2a27cSHenry Tieman 		ret = ice_set_fdir_ip4_usr_seg(seg, &fsp->m_u.usr_ip4_spec,
1096cac2a27cSHenry Tieman 					       &perfect_filter);
1097cac2a27cSHenry Tieman 		break;
1098165d80d6SHenry Tieman 	case TCP_V6_FLOW:
1099165d80d6SHenry Tieman 		ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
1100165d80d6SHenry Tieman 					   ICE_FLOW_SEG_HDR_TCP,
1101165d80d6SHenry Tieman 					   &perfect_filter);
1102165d80d6SHenry Tieman 		break;
1103165d80d6SHenry Tieman 	case UDP_V6_FLOW:
1104165d80d6SHenry Tieman 		ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
1105165d80d6SHenry Tieman 					   ICE_FLOW_SEG_HDR_UDP,
1106165d80d6SHenry Tieman 					   &perfect_filter);
1107165d80d6SHenry Tieman 		break;
1108165d80d6SHenry Tieman 	case SCTP_V6_FLOW:
1109165d80d6SHenry Tieman 		ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
1110165d80d6SHenry Tieman 					   ICE_FLOW_SEG_HDR_SCTP,
1111165d80d6SHenry Tieman 					   &perfect_filter);
1112165d80d6SHenry Tieman 		break;
1113165d80d6SHenry Tieman 	case IPV6_USER_FLOW:
1114165d80d6SHenry Tieman 		ret = ice_set_fdir_ip6_usr_seg(seg, &fsp->m_u.usr_ip6_spec,
1115165d80d6SHenry Tieman 					       &perfect_filter);
1116165d80d6SHenry Tieman 		break;
1117cac2a27cSHenry Tieman 	default:
1118cac2a27cSHenry Tieman 		ret = -EINVAL;
1119cac2a27cSHenry Tieman 	}
1120cac2a27cSHenry Tieman 	if (ret)
1121cac2a27cSHenry Tieman 		goto err_exit;
1122cac2a27cSHenry Tieman 
1123cac2a27cSHenry Tieman 	/* tunnel segments are shifted up one. */
1124cac2a27cSHenry Tieman 	memcpy(&tun_seg[1], seg, sizeof(*seg));
1125cac2a27cSHenry Tieman 
11262c57ffcbSHenry Tieman 	if (user && user->flex_fltr) {
11272c57ffcbSHenry Tieman 		perfect_filter = false;
11282c57ffcbSHenry Tieman 		ice_flow_add_fld_raw(seg, user->flex_offset,
11292c57ffcbSHenry Tieman 				     ICE_FLTR_PRGM_FLEX_WORD_SIZE,
11302c57ffcbSHenry Tieman 				     ICE_FLOW_FLD_OFF_INVAL,
11312c57ffcbSHenry Tieman 				     ICE_FLOW_FLD_OFF_INVAL);
11322c57ffcbSHenry Tieman 		ice_flow_add_fld_raw(&tun_seg[1], user->flex_offset,
11332c57ffcbSHenry Tieman 				     ICE_FLTR_PRGM_FLEX_WORD_SIZE,
11342c57ffcbSHenry Tieman 				     ICE_FLOW_FLD_OFF_INVAL,
11352c57ffcbSHenry Tieman 				     ICE_FLOW_FLD_OFF_INVAL);
11362c57ffcbSHenry Tieman 	}
11372c57ffcbSHenry Tieman 
1138cac2a27cSHenry Tieman 	/* add filter for outer headers */
1139cac2a27cSHenry Tieman 	fltr_idx = ice_ethtool_flow_to_fltr(fsp->flow_type & ~FLOW_EXT);
1140cac2a27cSHenry Tieman 	ret = ice_fdir_set_hw_fltr_rule(pf, seg, fltr_idx,
1141cac2a27cSHenry Tieman 					ICE_FD_HW_SEG_NON_TUN);
1142cac2a27cSHenry Tieman 	if (ret == -EEXIST)
1143cac2a27cSHenry Tieman 		/* Rule already exists, free memory and continue */
1144cac2a27cSHenry Tieman 		devm_kfree(dev, seg);
1145cac2a27cSHenry Tieman 	else if (ret)
1146cac2a27cSHenry Tieman 		/* could not write filter, free memory */
1147cac2a27cSHenry Tieman 		goto err_exit;
1148cac2a27cSHenry Tieman 
1149cac2a27cSHenry Tieman 	/* make tunneled filter HW entries if possible */
1150cac2a27cSHenry Tieman 	memcpy(&tun_seg[1], seg, sizeof(*seg));
1151cac2a27cSHenry Tieman 	ret = ice_fdir_set_hw_fltr_rule(pf, tun_seg, fltr_idx,
1152cac2a27cSHenry Tieman 					ICE_FD_HW_SEG_TUN);
1153cac2a27cSHenry Tieman 	if (ret == -EEXIST) {
1154cac2a27cSHenry Tieman 		/* Rule already exists, free memory and count as success */
1155cac2a27cSHenry Tieman 		devm_kfree(dev, tun_seg);
1156cac2a27cSHenry Tieman 		ret = 0;
1157cac2a27cSHenry Tieman 	} else if (ret) {
1158cac2a27cSHenry Tieman 		/* could not write tunnel filter, but outer filter exists */
1159cac2a27cSHenry Tieman 		devm_kfree(dev, tun_seg);
1160cac2a27cSHenry Tieman 	}
1161cac2a27cSHenry Tieman 
1162cac2a27cSHenry Tieman 	if (perfect_filter)
1163cac2a27cSHenry Tieman 		set_bit(fltr_idx, hw->fdir_perfect_fltr);
1164cac2a27cSHenry Tieman 	else
1165cac2a27cSHenry Tieman 		clear_bit(fltr_idx, hw->fdir_perfect_fltr);
1166cac2a27cSHenry Tieman 
1167cac2a27cSHenry Tieman 	return ret;
1168cac2a27cSHenry Tieman 
1169cac2a27cSHenry Tieman err_exit:
1170cac2a27cSHenry Tieman 	devm_kfree(dev, tun_seg);
1171cac2a27cSHenry Tieman 	devm_kfree(dev, seg);
1172cac2a27cSHenry Tieman 
1173cac2a27cSHenry Tieman 	return -EOPNOTSUPP;
1174cac2a27cSHenry Tieman }
1175cac2a27cSHenry Tieman 
1176cac2a27cSHenry Tieman /**
1177cac2a27cSHenry Tieman  * ice_fdir_write_fltr - send a flow director filter to the hardware
1178cac2a27cSHenry Tieman  * @pf: PF data structure
1179cac2a27cSHenry Tieman  * @input: filter structure
1180cac2a27cSHenry Tieman  * @add: true adds filter and false removed filter
1181cac2a27cSHenry Tieman  * @is_tun: true adds inner filter on tunnel and false outer headers
1182cac2a27cSHenry Tieman  *
1183cac2a27cSHenry Tieman  * returns 0 on success and negative value on error
1184cac2a27cSHenry Tieman  */
118528bf2672SBrett Creeley int
1186cac2a27cSHenry Tieman ice_fdir_write_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input, bool add,
1187cac2a27cSHenry Tieman 		    bool is_tun)
1188cac2a27cSHenry Tieman {
1189cac2a27cSHenry Tieman 	struct device *dev = ice_pf_to_dev(pf);
1190cac2a27cSHenry Tieman 	struct ice_hw *hw = &pf->hw;
1191cac2a27cSHenry Tieman 	struct ice_fltr_desc desc;
1192cac2a27cSHenry Tieman 	struct ice_vsi *ctrl_vsi;
1193cac2a27cSHenry Tieman 	enum ice_status status;
1194cac2a27cSHenry Tieman 	u8 *pkt, *frag_pkt;
1195cac2a27cSHenry Tieman 	bool has_frag;
1196cac2a27cSHenry Tieman 	int err;
1197cac2a27cSHenry Tieman 
1198cac2a27cSHenry Tieman 	ctrl_vsi = ice_get_ctrl_vsi(pf);
1199cac2a27cSHenry Tieman 	if (!ctrl_vsi)
1200cac2a27cSHenry Tieman 		return -EINVAL;
1201cac2a27cSHenry Tieman 
1202cac2a27cSHenry Tieman 	pkt = devm_kzalloc(dev, ICE_FDIR_MAX_RAW_PKT_SIZE, GFP_KERNEL);
1203cac2a27cSHenry Tieman 	if (!pkt)
1204cac2a27cSHenry Tieman 		return -ENOMEM;
1205cac2a27cSHenry Tieman 	frag_pkt = devm_kzalloc(dev, ICE_FDIR_MAX_RAW_PKT_SIZE, GFP_KERNEL);
1206cac2a27cSHenry Tieman 	if (!frag_pkt) {
1207cac2a27cSHenry Tieman 		err = -ENOMEM;
1208cac2a27cSHenry Tieman 		goto err_free;
1209cac2a27cSHenry Tieman 	}
1210cac2a27cSHenry Tieman 
1211cac2a27cSHenry Tieman 	ice_fdir_get_prgm_desc(hw, input, &desc, add);
1212cac2a27cSHenry Tieman 	status = ice_fdir_get_gen_prgm_pkt(hw, input, pkt, false, is_tun);
1213cac2a27cSHenry Tieman 	if (status) {
1214cac2a27cSHenry Tieman 		err = ice_status_to_errno(status);
1215cac2a27cSHenry Tieman 		goto err_free_all;
1216cac2a27cSHenry Tieman 	}
1217cac2a27cSHenry Tieman 	err = ice_prgm_fdir_fltr(ctrl_vsi, &desc, pkt);
1218cac2a27cSHenry Tieman 	if (err)
1219cac2a27cSHenry Tieman 		goto err_free_all;
1220cac2a27cSHenry Tieman 
1221cac2a27cSHenry Tieman 	/* repeat for fragment packet */
1222cac2a27cSHenry Tieman 	has_frag = ice_fdir_has_frag(input->flow_type);
1223cac2a27cSHenry Tieman 	if (has_frag) {
1224cac2a27cSHenry Tieman 		/* does not return error */
1225cac2a27cSHenry Tieman 		ice_fdir_get_prgm_desc(hw, input, &desc, add);
1226cac2a27cSHenry Tieman 		status = ice_fdir_get_gen_prgm_pkt(hw, input, frag_pkt, true,
1227cac2a27cSHenry Tieman 						   is_tun);
1228cac2a27cSHenry Tieman 		if (status) {
1229cac2a27cSHenry Tieman 			err = ice_status_to_errno(status);
1230cac2a27cSHenry Tieman 			goto err_frag;
1231cac2a27cSHenry Tieman 		}
1232cac2a27cSHenry Tieman 		err = ice_prgm_fdir_fltr(ctrl_vsi, &desc, frag_pkt);
1233cac2a27cSHenry Tieman 		if (err)
1234cac2a27cSHenry Tieman 			goto err_frag;
1235cac2a27cSHenry Tieman 	} else {
1236cac2a27cSHenry Tieman 		devm_kfree(dev, frag_pkt);
1237cac2a27cSHenry Tieman 	}
1238cac2a27cSHenry Tieman 
1239cac2a27cSHenry Tieman 	return 0;
1240cac2a27cSHenry Tieman 
1241cac2a27cSHenry Tieman err_free_all:
1242cac2a27cSHenry Tieman 	devm_kfree(dev, frag_pkt);
1243cac2a27cSHenry Tieman err_free:
1244cac2a27cSHenry Tieman 	devm_kfree(dev, pkt);
1245cac2a27cSHenry Tieman 	return err;
1246cac2a27cSHenry Tieman 
1247cac2a27cSHenry Tieman err_frag:
1248cac2a27cSHenry Tieman 	devm_kfree(dev, frag_pkt);
1249cac2a27cSHenry Tieman 	return err;
1250cac2a27cSHenry Tieman }
1251cac2a27cSHenry Tieman 
1252cac2a27cSHenry Tieman /**
1253cac2a27cSHenry Tieman  * ice_fdir_write_all_fltr - send a flow director filter to the hardware
1254cac2a27cSHenry Tieman  * @pf: PF data structure
1255cac2a27cSHenry Tieman  * @input: filter structure
1256cac2a27cSHenry Tieman  * @add: true adds filter and false removed filter
1257cac2a27cSHenry Tieman  *
1258cac2a27cSHenry Tieman  * returns 0 on success and negative value on error
1259cac2a27cSHenry Tieman  */
1260cac2a27cSHenry Tieman static int
1261cac2a27cSHenry Tieman ice_fdir_write_all_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input,
1262cac2a27cSHenry Tieman 			bool add)
1263cac2a27cSHenry Tieman {
1264cac2a27cSHenry Tieman 	u16 port_num;
1265cac2a27cSHenry Tieman 	int tun;
1266cac2a27cSHenry Tieman 
1267cac2a27cSHenry Tieman 	for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
1268cac2a27cSHenry Tieman 		bool is_tun = tun == ICE_FD_HW_SEG_TUN;
1269cac2a27cSHenry Tieman 		int err;
1270cac2a27cSHenry Tieman 
1271cac2a27cSHenry Tieman 		if (is_tun && !ice_get_open_tunnel_port(&pf->hw, TNL_ALL,
1272cac2a27cSHenry Tieman 							&port_num))
1273cac2a27cSHenry Tieman 			continue;
1274cac2a27cSHenry Tieman 		err = ice_fdir_write_fltr(pf, input, add, is_tun);
1275cac2a27cSHenry Tieman 		if (err)
1276cac2a27cSHenry Tieman 			return err;
1277cac2a27cSHenry Tieman 	}
1278cac2a27cSHenry Tieman 	return 0;
1279cac2a27cSHenry Tieman }
1280cac2a27cSHenry Tieman 
1281cac2a27cSHenry Tieman /**
128283af0039SHenry Tieman  * ice_fdir_replay_fltrs - replay filters from the HW filter list
128383af0039SHenry Tieman  * @pf: board private structure
128483af0039SHenry Tieman  */
128583af0039SHenry Tieman void ice_fdir_replay_fltrs(struct ice_pf *pf)
128683af0039SHenry Tieman {
128783af0039SHenry Tieman 	struct ice_fdir_fltr *f_rule;
128883af0039SHenry Tieman 	struct ice_hw *hw = &pf->hw;
128983af0039SHenry Tieman 
129083af0039SHenry Tieman 	list_for_each_entry(f_rule, &hw->fdir_list_head, fltr_node) {
129183af0039SHenry Tieman 		int err = ice_fdir_write_all_fltr(pf, f_rule, true);
129283af0039SHenry Tieman 
129383af0039SHenry Tieman 		if (err)
129483af0039SHenry Tieman 			dev_dbg(ice_pf_to_dev(pf), "Flow Director error %d, could not reprogram filter %d\n",
129583af0039SHenry Tieman 				err, f_rule->fltr_id);
129683af0039SHenry Tieman 	}
129783af0039SHenry Tieman }
129883af0039SHenry Tieman 
129983af0039SHenry Tieman /**
1300148beb61SHenry Tieman  * ice_fdir_create_dflt_rules - create default perfect filters
1301148beb61SHenry Tieman  * @pf: PF data structure
1302148beb61SHenry Tieman  *
1303148beb61SHenry Tieman  * Returns 0 for success or error.
1304148beb61SHenry Tieman  */
1305148beb61SHenry Tieman int ice_fdir_create_dflt_rules(struct ice_pf *pf)
1306148beb61SHenry Tieman {
1307148beb61SHenry Tieman 	int err;
1308148beb61SHenry Tieman 
1309148beb61SHenry Tieman 	/* Create perfect TCP and UDP rules in hardware. */
1310148beb61SHenry Tieman 	err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV4_TCP);
1311148beb61SHenry Tieman 	if (err)
1312148beb61SHenry Tieman 		return err;
1313148beb61SHenry Tieman 
1314148beb61SHenry Tieman 	err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV4_UDP);
1315165d80d6SHenry Tieman 	if (err)
1316165d80d6SHenry Tieman 		return err;
1317165d80d6SHenry Tieman 
1318165d80d6SHenry Tieman 	err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV6_TCP);
1319165d80d6SHenry Tieman 	if (err)
1320165d80d6SHenry Tieman 		return err;
1321165d80d6SHenry Tieman 
1322165d80d6SHenry Tieman 	err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV6_UDP);
1323148beb61SHenry Tieman 
1324148beb61SHenry Tieman 	return err;
1325148beb61SHenry Tieman }
1326148beb61SHenry Tieman 
1327148beb61SHenry Tieman /**
1328148beb61SHenry Tieman  * ice_vsi_manage_fdir - turn on/off flow director
1329148beb61SHenry Tieman  * @vsi: the VSI being changed
1330148beb61SHenry Tieman  * @ena: boolean value indicating if this is an enable or disable request
1331148beb61SHenry Tieman  */
1332148beb61SHenry Tieman void ice_vsi_manage_fdir(struct ice_vsi *vsi, bool ena)
1333148beb61SHenry Tieman {
1334cac2a27cSHenry Tieman 	struct ice_fdir_fltr *f_rule, *tmp;
1335148beb61SHenry Tieman 	struct ice_pf *pf = vsi->back;
1336148beb61SHenry Tieman 	struct ice_hw *hw = &pf->hw;
1337148beb61SHenry Tieman 	enum ice_fltr_ptype flow;
1338148beb61SHenry Tieman 
1339148beb61SHenry Tieman 	if (ena) {
1340148beb61SHenry Tieman 		set_bit(ICE_FLAG_FD_ENA, pf->flags);
1341148beb61SHenry Tieman 		ice_fdir_create_dflt_rules(pf);
1342148beb61SHenry Tieman 		return;
1343148beb61SHenry Tieman 	}
1344148beb61SHenry Tieman 
1345148beb61SHenry Tieman 	mutex_lock(&hw->fdir_fltr_lock);
1346148beb61SHenry Tieman 	if (!test_and_clear_bit(ICE_FLAG_FD_ENA, pf->flags))
1347148beb61SHenry Tieman 		goto release_lock;
1348cac2a27cSHenry Tieman 	list_for_each_entry_safe(f_rule, tmp, &hw->fdir_list_head, fltr_node) {
1349cac2a27cSHenry Tieman 		/* ignore return value */
1350cac2a27cSHenry Tieman 		ice_fdir_write_all_fltr(pf, f_rule, false);
1351cac2a27cSHenry Tieman 		ice_fdir_update_cntrs(hw, f_rule->flow_type, false);
1352cac2a27cSHenry Tieman 		list_del(&f_rule->fltr_node);
1353cac2a27cSHenry Tieman 		devm_kfree(ice_hw_to_dev(hw), f_rule);
1354cac2a27cSHenry Tieman 	}
1355148beb61SHenry Tieman 
1356148beb61SHenry Tieman 	if (hw->fdir_prof)
1357148beb61SHenry Tieman 		for (flow = ICE_FLTR_PTYPE_NONF_NONE; flow < ICE_FLTR_PTYPE_MAX;
1358148beb61SHenry Tieman 		     flow++)
1359148beb61SHenry Tieman 			if (hw->fdir_prof[flow])
1360148beb61SHenry Tieman 				ice_fdir_rem_flow(hw, ICE_BLK_FD, flow);
1361148beb61SHenry Tieman 
1362148beb61SHenry Tieman release_lock:
1363148beb61SHenry Tieman 	mutex_unlock(&hw->fdir_fltr_lock);
1364148beb61SHenry Tieman }
1365cac2a27cSHenry Tieman 
1366cac2a27cSHenry Tieman /**
1367d5329be9SHenry Tieman  * ice_fdir_do_rem_flow - delete flow and possibly add perfect flow
1368d5329be9SHenry Tieman  * @pf: PF structure
1369d5329be9SHenry Tieman  * @flow_type: FDir flow type to release
1370d5329be9SHenry Tieman  */
1371d5329be9SHenry Tieman static void
1372d5329be9SHenry Tieman ice_fdir_do_rem_flow(struct ice_pf *pf, enum ice_fltr_ptype flow_type)
1373d5329be9SHenry Tieman {
1374d5329be9SHenry Tieman 	struct ice_hw *hw = &pf->hw;
1375d5329be9SHenry Tieman 	bool need_perfect = false;
1376d5329be9SHenry Tieman 
1377d5329be9SHenry Tieman 	if (flow_type == ICE_FLTR_PTYPE_NONF_IPV4_TCP ||
1378d5329be9SHenry Tieman 	    flow_type == ICE_FLTR_PTYPE_NONF_IPV4_UDP ||
1379d5329be9SHenry Tieman 	    flow_type == ICE_FLTR_PTYPE_NONF_IPV6_TCP ||
1380d5329be9SHenry Tieman 	    flow_type == ICE_FLTR_PTYPE_NONF_IPV6_UDP)
1381d5329be9SHenry Tieman 		need_perfect = true;
1382d5329be9SHenry Tieman 
1383d5329be9SHenry Tieman 	if (need_perfect && test_bit(flow_type, hw->fdir_perfect_fltr))
1384d5329be9SHenry Tieman 		return;
1385d5329be9SHenry Tieman 
1386d5329be9SHenry Tieman 	ice_fdir_rem_flow(hw, ICE_BLK_FD, flow_type);
1387d5329be9SHenry Tieman 	if (need_perfect)
1388d5329be9SHenry Tieman 		ice_create_init_fdir_rule(pf, flow_type);
1389d5329be9SHenry Tieman }
1390d5329be9SHenry Tieman 
1391d5329be9SHenry Tieman /**
1392cac2a27cSHenry Tieman  * ice_fdir_update_list_entry - add or delete a filter from the filter list
1393cac2a27cSHenry Tieman  * @pf: PF structure
1394cac2a27cSHenry Tieman  * @input: filter structure
1395cac2a27cSHenry Tieman  * @fltr_idx: ethtool index of filter to modify
1396cac2a27cSHenry Tieman  *
1397cac2a27cSHenry Tieman  * returns 0 on success and negative on errors
1398cac2a27cSHenry Tieman  */
1399cac2a27cSHenry Tieman static int
1400cac2a27cSHenry Tieman ice_fdir_update_list_entry(struct ice_pf *pf, struct ice_fdir_fltr *input,
1401cac2a27cSHenry Tieman 			   int fltr_idx)
1402cac2a27cSHenry Tieman {
1403cac2a27cSHenry Tieman 	struct ice_fdir_fltr *old_fltr;
1404cac2a27cSHenry Tieman 	struct ice_hw *hw = &pf->hw;
1405cac2a27cSHenry Tieman 	int err = -ENOENT;
1406cac2a27cSHenry Tieman 
1407cac2a27cSHenry Tieman 	/* Do not update filters during reset */
1408cac2a27cSHenry Tieman 	if (ice_is_reset_in_progress(pf->state))
1409cac2a27cSHenry Tieman 		return -EBUSY;
1410cac2a27cSHenry Tieman 
1411cac2a27cSHenry Tieman 	old_fltr = ice_fdir_find_fltr_by_idx(hw, fltr_idx);
1412cac2a27cSHenry Tieman 	if (old_fltr) {
1413cac2a27cSHenry Tieman 		err = ice_fdir_write_all_fltr(pf, old_fltr, false);
1414cac2a27cSHenry Tieman 		if (err)
1415cac2a27cSHenry Tieman 			return err;
1416cac2a27cSHenry Tieman 		ice_fdir_update_cntrs(hw, old_fltr->flow_type, false);
1417cac2a27cSHenry Tieman 		if (!input && !hw->fdir_fltr_cnt[old_fltr->flow_type])
1418cac2a27cSHenry Tieman 			/* we just deleted the last filter of flow_type so we
1419cac2a27cSHenry Tieman 			 * should also delete the HW filter info.
1420cac2a27cSHenry Tieman 			 */
1421d5329be9SHenry Tieman 			ice_fdir_do_rem_flow(pf, old_fltr->flow_type);
1422cac2a27cSHenry Tieman 		list_del(&old_fltr->fltr_node);
1423cac2a27cSHenry Tieman 		devm_kfree(ice_hw_to_dev(hw), old_fltr);
1424cac2a27cSHenry Tieman 	}
1425cac2a27cSHenry Tieman 	if (!input)
1426cac2a27cSHenry Tieman 		return err;
1427cac2a27cSHenry Tieman 	ice_fdir_list_add_fltr(hw, input);
1428cac2a27cSHenry Tieman 	ice_fdir_update_cntrs(hw, input->flow_type, true);
1429cac2a27cSHenry Tieman 	return 0;
1430cac2a27cSHenry Tieman }
1431cac2a27cSHenry Tieman 
1432cac2a27cSHenry Tieman /**
1433cac2a27cSHenry Tieman  * ice_del_fdir_ethtool - delete Flow Director filter
1434cac2a27cSHenry Tieman  * @vsi: pointer to target VSI
1435cac2a27cSHenry Tieman  * @cmd: command to add or delete Flow Director filter
1436cac2a27cSHenry Tieman  *
1437cac2a27cSHenry Tieman  * Returns 0 on success and negative values for failure
1438cac2a27cSHenry Tieman  */
1439cac2a27cSHenry Tieman int ice_del_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
1440cac2a27cSHenry Tieman {
1441cac2a27cSHenry Tieman 	struct ethtool_rx_flow_spec *fsp =
1442cac2a27cSHenry Tieman 		(struct ethtool_rx_flow_spec *)&cmd->fs;
1443cac2a27cSHenry Tieman 	struct ice_pf *pf = vsi->back;
1444cac2a27cSHenry Tieman 	struct ice_hw *hw = &pf->hw;
1445cac2a27cSHenry Tieman 	int val;
1446cac2a27cSHenry Tieman 
1447cac2a27cSHenry Tieman 	if (!test_bit(ICE_FLAG_FD_ENA, pf->flags))
1448cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
1449cac2a27cSHenry Tieman 
1450cac2a27cSHenry Tieman 	/* Do not delete filters during reset */
1451cac2a27cSHenry Tieman 	if (ice_is_reset_in_progress(pf->state)) {
1452cac2a27cSHenry Tieman 		dev_err(ice_pf_to_dev(pf), "Device is resetting - deleting Flow Director filters not supported during reset\n");
1453cac2a27cSHenry Tieman 		return -EBUSY;
1454cac2a27cSHenry Tieman 	}
1455cac2a27cSHenry Tieman 
1456cac2a27cSHenry Tieman 	if (test_bit(__ICE_FD_FLUSH_REQ, pf->state))
1457cac2a27cSHenry Tieman 		return -EBUSY;
1458cac2a27cSHenry Tieman 
1459cac2a27cSHenry Tieman 	mutex_lock(&hw->fdir_fltr_lock);
1460cac2a27cSHenry Tieman 	val = ice_fdir_update_list_entry(pf, NULL, fsp->location);
1461cac2a27cSHenry Tieman 	mutex_unlock(&hw->fdir_fltr_lock);
1462cac2a27cSHenry Tieman 
1463cac2a27cSHenry Tieman 	return val;
1464cac2a27cSHenry Tieman }
1465cac2a27cSHenry Tieman 
1466cac2a27cSHenry Tieman /**
1467cac2a27cSHenry Tieman  * ice_set_fdir_input_set - Set the input set for Flow Director
1468cac2a27cSHenry Tieman  * @vsi: pointer to target VSI
1469cac2a27cSHenry Tieman  * @fsp: pointer to ethtool Rx flow specification
1470cac2a27cSHenry Tieman  * @input: filter structure
1471cac2a27cSHenry Tieman  */
1472cac2a27cSHenry Tieman static int
1473cac2a27cSHenry Tieman ice_set_fdir_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
1474cac2a27cSHenry Tieman 		       struct ice_fdir_fltr *input)
1475cac2a27cSHenry Tieman {
1476cac2a27cSHenry Tieman 	u16 dest_vsi, q_index = 0;
1477cac2a27cSHenry Tieman 	struct ice_pf *pf;
1478cac2a27cSHenry Tieman 	struct ice_hw *hw;
1479cac2a27cSHenry Tieman 	int flow_type;
1480cac2a27cSHenry Tieman 	u8 dest_ctl;
1481cac2a27cSHenry Tieman 
1482cac2a27cSHenry Tieman 	if (!vsi || !fsp || !input)
1483cac2a27cSHenry Tieman 		return -EINVAL;
1484cac2a27cSHenry Tieman 
1485cac2a27cSHenry Tieman 	pf = vsi->back;
1486cac2a27cSHenry Tieman 	hw = &pf->hw;
1487cac2a27cSHenry Tieman 
1488cac2a27cSHenry Tieman 	dest_vsi = vsi->idx;
1489cac2a27cSHenry Tieman 	if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
1490cac2a27cSHenry Tieman 		dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DROP_PKT;
1491cac2a27cSHenry Tieman 	} else {
1492cac2a27cSHenry Tieman 		u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie);
1493cac2a27cSHenry Tieman 		u8 vf = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie);
1494cac2a27cSHenry Tieman 
1495cac2a27cSHenry Tieman 		if (vf) {
1496cac2a27cSHenry Tieman 			dev_err(ice_pf_to_dev(pf), "Failed to add filter. Flow director filters are not supported on VF queues.\n");
1497cac2a27cSHenry Tieman 			return -EINVAL;
1498cac2a27cSHenry Tieman 		}
1499cac2a27cSHenry Tieman 
1500cac2a27cSHenry Tieman 		if (ring >= vsi->num_rxq)
1501cac2a27cSHenry Tieman 			return -EINVAL;
1502cac2a27cSHenry Tieman 
1503cac2a27cSHenry Tieman 		dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DIRECT_PKT_QINDEX;
1504cac2a27cSHenry Tieman 		q_index = ring;
1505cac2a27cSHenry Tieman 	}
1506cac2a27cSHenry Tieman 
1507cac2a27cSHenry Tieman 	input->fltr_id = fsp->location;
1508cac2a27cSHenry Tieman 	input->q_index = q_index;
1509cac2a27cSHenry Tieman 	flow_type = fsp->flow_type & ~FLOW_EXT;
1510cac2a27cSHenry Tieman 
1511cac2a27cSHenry Tieman 	input->dest_vsi = dest_vsi;
1512cac2a27cSHenry Tieman 	input->dest_ctl = dest_ctl;
1513cac2a27cSHenry Tieman 	input->fltr_status = ICE_FLTR_PRGM_DESC_FD_STATUS_FD_ID;
1514cac2a27cSHenry Tieman 	input->cnt_index = ICE_FD_SB_STAT_IDX(hw->fd_ctr_base);
1515cac2a27cSHenry Tieman 	input->flow_type = ice_ethtool_flow_to_fltr(flow_type);
1516cac2a27cSHenry Tieman 
1517cac2a27cSHenry Tieman 	if (fsp->flow_type & FLOW_EXT) {
1518cac2a27cSHenry Tieman 		memcpy(input->ext_data.usr_def, fsp->h_ext.data,
1519cac2a27cSHenry Tieman 		       sizeof(input->ext_data.usr_def));
1520cac2a27cSHenry Tieman 		input->ext_data.vlan_type = fsp->h_ext.vlan_etype;
1521cac2a27cSHenry Tieman 		input->ext_data.vlan_tag = fsp->h_ext.vlan_tci;
1522cac2a27cSHenry Tieman 		memcpy(input->ext_mask.usr_def, fsp->m_ext.data,
1523cac2a27cSHenry Tieman 		       sizeof(input->ext_mask.usr_def));
1524cac2a27cSHenry Tieman 		input->ext_mask.vlan_type = fsp->m_ext.vlan_etype;
1525cac2a27cSHenry Tieman 		input->ext_mask.vlan_tag = fsp->m_ext.vlan_tci;
1526cac2a27cSHenry Tieman 	}
1527cac2a27cSHenry Tieman 
1528cac2a27cSHenry Tieman 	switch (flow_type) {
1529cac2a27cSHenry Tieman 	case TCP_V4_FLOW:
1530cac2a27cSHenry Tieman 	case UDP_V4_FLOW:
1531cac2a27cSHenry Tieman 	case SCTP_V4_FLOW:
1532165d80d6SHenry Tieman 		input->ip.v4.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
1533165d80d6SHenry Tieman 		input->ip.v4.src_port = fsp->h_u.tcp_ip4_spec.psrc;
1534165d80d6SHenry Tieman 		input->ip.v4.dst_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
1535165d80d6SHenry Tieman 		input->ip.v4.src_ip = fsp->h_u.tcp_ip4_spec.ip4src;
1536165d80d6SHenry Tieman 		input->mask.v4.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
1537165d80d6SHenry Tieman 		input->mask.v4.src_port = fsp->m_u.tcp_ip4_spec.psrc;
1538165d80d6SHenry Tieman 		input->mask.v4.dst_ip = fsp->m_u.tcp_ip4_spec.ip4dst;
1539165d80d6SHenry Tieman 		input->mask.v4.src_ip = fsp->m_u.tcp_ip4_spec.ip4src;
1540cac2a27cSHenry Tieman 		break;
1541cac2a27cSHenry Tieman 	case IPV4_USER_FLOW:
1542165d80d6SHenry Tieman 		input->ip.v4.dst_ip = fsp->h_u.usr_ip4_spec.ip4dst;
1543165d80d6SHenry Tieman 		input->ip.v4.src_ip = fsp->h_u.usr_ip4_spec.ip4src;
1544165d80d6SHenry Tieman 		input->ip.v4.l4_header = fsp->h_u.usr_ip4_spec.l4_4_bytes;
1545165d80d6SHenry Tieman 		input->ip.v4.proto = fsp->h_u.usr_ip4_spec.proto;
1546165d80d6SHenry Tieman 		input->ip.v4.ip_ver = fsp->h_u.usr_ip4_spec.ip_ver;
1547165d80d6SHenry Tieman 		input->ip.v4.tos = fsp->h_u.usr_ip4_spec.tos;
1548165d80d6SHenry Tieman 		input->mask.v4.dst_ip = fsp->m_u.usr_ip4_spec.ip4dst;
1549165d80d6SHenry Tieman 		input->mask.v4.src_ip = fsp->m_u.usr_ip4_spec.ip4src;
1550165d80d6SHenry Tieman 		input->mask.v4.l4_header = fsp->m_u.usr_ip4_spec.l4_4_bytes;
1551165d80d6SHenry Tieman 		input->mask.v4.proto = fsp->m_u.usr_ip4_spec.proto;
1552165d80d6SHenry Tieman 		input->mask.v4.ip_ver = fsp->m_u.usr_ip4_spec.ip_ver;
1553165d80d6SHenry Tieman 		input->mask.v4.tos = fsp->m_u.usr_ip4_spec.tos;
1554165d80d6SHenry Tieman 		break;
1555165d80d6SHenry Tieman 	case TCP_V6_FLOW:
1556165d80d6SHenry Tieman 	case UDP_V6_FLOW:
1557165d80d6SHenry Tieman 	case SCTP_V6_FLOW:
1558165d80d6SHenry Tieman 		memcpy(input->ip.v6.dst_ip, fsp->h_u.usr_ip6_spec.ip6dst,
1559165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1560165d80d6SHenry Tieman 		memcpy(input->ip.v6.src_ip, fsp->h_u.usr_ip6_spec.ip6src,
1561165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1562165d80d6SHenry Tieman 		input->ip.v6.dst_port = fsp->h_u.tcp_ip6_spec.pdst;
1563165d80d6SHenry Tieman 		input->ip.v6.src_port = fsp->h_u.tcp_ip6_spec.psrc;
1564165d80d6SHenry Tieman 		input->ip.v6.tc = fsp->h_u.tcp_ip6_spec.tclass;
1565165d80d6SHenry Tieman 		memcpy(input->mask.v6.dst_ip, fsp->m_u.tcp_ip6_spec.ip6dst,
1566165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1567165d80d6SHenry Tieman 		memcpy(input->mask.v6.src_ip, fsp->m_u.tcp_ip6_spec.ip6src,
1568165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1569165d80d6SHenry Tieman 		input->mask.v6.dst_port = fsp->m_u.tcp_ip6_spec.pdst;
1570165d80d6SHenry Tieman 		input->mask.v6.src_port = fsp->m_u.tcp_ip6_spec.psrc;
1571165d80d6SHenry Tieman 		input->mask.v6.tc = fsp->m_u.tcp_ip6_spec.tclass;
1572165d80d6SHenry Tieman 		break;
1573165d80d6SHenry Tieman 	case IPV6_USER_FLOW:
1574165d80d6SHenry Tieman 		memcpy(input->ip.v6.dst_ip, fsp->h_u.usr_ip6_spec.ip6dst,
1575165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1576165d80d6SHenry Tieman 		memcpy(input->ip.v6.src_ip, fsp->h_u.usr_ip6_spec.ip6src,
1577165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1578165d80d6SHenry Tieman 		input->ip.v6.l4_header = fsp->h_u.usr_ip6_spec.l4_4_bytes;
1579165d80d6SHenry Tieman 		input->ip.v6.tc = fsp->h_u.usr_ip6_spec.tclass;
1580165d80d6SHenry Tieman 		input->ip.v6.proto = fsp->h_u.usr_ip6_spec.l4_proto;
1581165d80d6SHenry Tieman 		memcpy(input->mask.v6.dst_ip, fsp->m_u.usr_ip6_spec.ip6dst,
1582165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1583165d80d6SHenry Tieman 		memcpy(input->mask.v6.src_ip, fsp->m_u.usr_ip6_spec.ip6src,
1584165d80d6SHenry Tieman 		       sizeof(struct in6_addr));
1585165d80d6SHenry Tieman 		input->mask.v6.l4_header = fsp->m_u.usr_ip6_spec.l4_4_bytes;
1586165d80d6SHenry Tieman 		input->mask.v6.tc = fsp->m_u.usr_ip6_spec.tclass;
1587165d80d6SHenry Tieman 		input->mask.v6.proto = fsp->m_u.usr_ip6_spec.l4_proto;
1588cac2a27cSHenry Tieman 		break;
1589cac2a27cSHenry Tieman 	default:
1590cac2a27cSHenry Tieman 		/* not doing un-parsed flow types */
1591cac2a27cSHenry Tieman 		return -EINVAL;
1592cac2a27cSHenry Tieman 	}
1593cac2a27cSHenry Tieman 
1594cac2a27cSHenry Tieman 	return 0;
1595cac2a27cSHenry Tieman }
1596cac2a27cSHenry Tieman 
1597cac2a27cSHenry Tieman /**
1598cac2a27cSHenry Tieman  * ice_add_fdir_ethtool - Add/Remove Flow Director filter
1599cac2a27cSHenry Tieman  * @vsi: pointer to target VSI
1600cac2a27cSHenry Tieman  * @cmd: command to add or delete Flow Director filter
1601cac2a27cSHenry Tieman  *
1602cac2a27cSHenry Tieman  * Returns 0 on success and negative values for failure
1603cac2a27cSHenry Tieman  */
1604cac2a27cSHenry Tieman int ice_add_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
1605cac2a27cSHenry Tieman {
16062c57ffcbSHenry Tieman 	struct ice_rx_flow_userdef userdata;
1607cac2a27cSHenry Tieman 	struct ethtool_rx_flow_spec *fsp;
1608cac2a27cSHenry Tieman 	struct ice_fdir_fltr *input;
1609cac2a27cSHenry Tieman 	struct device *dev;
1610cac2a27cSHenry Tieman 	struct ice_pf *pf;
1611cac2a27cSHenry Tieman 	struct ice_hw *hw;
1612cac2a27cSHenry Tieman 	int fltrs_needed;
1613cac2a27cSHenry Tieman 	u16 tunnel_port;
1614cac2a27cSHenry Tieman 	int ret;
1615cac2a27cSHenry Tieman 
1616cac2a27cSHenry Tieman 	if (!vsi)
1617cac2a27cSHenry Tieman 		return -EINVAL;
1618cac2a27cSHenry Tieman 
1619cac2a27cSHenry Tieman 	pf = vsi->back;
1620cac2a27cSHenry Tieman 	hw = &pf->hw;
1621cac2a27cSHenry Tieman 	dev = ice_pf_to_dev(pf);
1622cac2a27cSHenry Tieman 
1623cac2a27cSHenry Tieman 	if (!test_bit(ICE_FLAG_FD_ENA, pf->flags))
1624cac2a27cSHenry Tieman 		return -EOPNOTSUPP;
1625cac2a27cSHenry Tieman 
1626cac2a27cSHenry Tieman 	/* Do not program filters during reset */
1627cac2a27cSHenry Tieman 	if (ice_is_reset_in_progress(pf->state)) {
1628cac2a27cSHenry Tieman 		dev_err(dev, "Device is resetting - adding Flow Director filters not supported during reset\n");
1629cac2a27cSHenry Tieman 		return -EBUSY;
1630cac2a27cSHenry Tieman 	}
1631cac2a27cSHenry Tieman 
1632cac2a27cSHenry Tieman 	fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
1633cac2a27cSHenry Tieman 
16342c57ffcbSHenry Tieman 	if (ice_parse_rx_flow_user_data(fsp, &userdata))
16352c57ffcbSHenry Tieman 		return -EINVAL;
16362c57ffcbSHenry Tieman 
1637cac2a27cSHenry Tieman 	if (fsp->flow_type & FLOW_MAC_EXT)
1638cac2a27cSHenry Tieman 		return -EINVAL;
1639cac2a27cSHenry Tieman 
16402c57ffcbSHenry Tieman 	ret = ice_cfg_fdir_xtrct_seq(pf, fsp, &userdata);
1641cac2a27cSHenry Tieman 	if (ret)
1642cac2a27cSHenry Tieman 		return ret;
1643cac2a27cSHenry Tieman 
1644cac2a27cSHenry Tieman 	if (fsp->location >= ice_get_fdir_cnt_all(hw)) {
1645cac2a27cSHenry Tieman 		dev_err(dev, "Failed to add filter.  The maximum number of flow director filters has been reached.\n");
1646cac2a27cSHenry Tieman 		return -ENOSPC;
1647cac2a27cSHenry Tieman 	}
1648cac2a27cSHenry Tieman 
1649cac2a27cSHenry Tieman 	/* return error if not an update and no available filters */
1650cac2a27cSHenry Tieman 	fltrs_needed = ice_get_open_tunnel_port(hw, TNL_ALL, &tunnel_port) ?
1651cac2a27cSHenry Tieman 		2 : 1;
1652cac2a27cSHenry Tieman 	if (!ice_fdir_find_fltr_by_idx(hw, fsp->location) &&
1653cac2a27cSHenry Tieman 	    ice_fdir_num_avail_fltr(hw, pf->vsi[vsi->idx]) < fltrs_needed) {
1654cac2a27cSHenry Tieman 		dev_err(dev, "Failed to add filter.  The maximum number of flow director filters has been reached.\n");
1655cac2a27cSHenry Tieman 		return -ENOSPC;
1656cac2a27cSHenry Tieman 	}
1657cac2a27cSHenry Tieman 
1658cac2a27cSHenry Tieman 	input = devm_kzalloc(dev, sizeof(*input), GFP_KERNEL);
1659cac2a27cSHenry Tieman 	if (!input)
1660cac2a27cSHenry Tieman 		return -ENOMEM;
1661cac2a27cSHenry Tieman 
1662cac2a27cSHenry Tieman 	ret = ice_set_fdir_input_set(vsi, fsp, input);
1663cac2a27cSHenry Tieman 	if (ret)
1664cac2a27cSHenry Tieman 		goto free_input;
1665cac2a27cSHenry Tieman 
1666cac2a27cSHenry Tieman 	mutex_lock(&hw->fdir_fltr_lock);
1667cac2a27cSHenry Tieman 	if (ice_fdir_is_dup_fltr(hw, input)) {
1668cac2a27cSHenry Tieman 		ret = -EINVAL;
1669cac2a27cSHenry Tieman 		goto release_lock;
1670cac2a27cSHenry Tieman 	}
1671cac2a27cSHenry Tieman 
16722c57ffcbSHenry Tieman 	if (userdata.flex_fltr) {
16732c57ffcbSHenry Tieman 		input->flex_fltr = true;
16742c57ffcbSHenry Tieman 		input->flex_word = cpu_to_be16(userdata.flex_word);
16752c57ffcbSHenry Tieman 		input->flex_offset = userdata.flex_offset;
16762c57ffcbSHenry Tieman 	}
16772c57ffcbSHenry Tieman 
1678cac2a27cSHenry Tieman 	/* input struct is added to the HW filter list */
1679cac2a27cSHenry Tieman 	ice_fdir_update_list_entry(pf, input, fsp->location);
1680cac2a27cSHenry Tieman 
1681cac2a27cSHenry Tieman 	ret = ice_fdir_write_all_fltr(pf, input, true);
1682cac2a27cSHenry Tieman 	if (ret)
1683cac2a27cSHenry Tieman 		goto remove_sw_rule;
1684cac2a27cSHenry Tieman 
1685cac2a27cSHenry Tieman 	goto release_lock;
1686cac2a27cSHenry Tieman 
1687cac2a27cSHenry Tieman remove_sw_rule:
1688cac2a27cSHenry Tieman 	ice_fdir_update_cntrs(hw, input->flow_type, false);
1689cac2a27cSHenry Tieman 	list_del(&input->fltr_node);
1690cac2a27cSHenry Tieman release_lock:
1691cac2a27cSHenry Tieman 	mutex_unlock(&hw->fdir_fltr_lock);
1692cac2a27cSHenry Tieman free_input:
1693cac2a27cSHenry Tieman 	if (ret)
1694cac2a27cSHenry Tieman 		devm_kfree(dev, input);
1695cac2a27cSHenry Tieman 
1696cac2a27cSHenry Tieman 	return ret;
1697cac2a27cSHenry Tieman }
1698