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