162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (C) 2018-2020, Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "ice.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/** 762306a36Sopenharmony_ci * ice_is_arfs_active - helper to check is aRFS is active 862306a36Sopenharmony_ci * @vsi: VSI to check 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_cistatic bool ice_is_arfs_active(struct ice_vsi *vsi) 1162306a36Sopenharmony_ci{ 1262306a36Sopenharmony_ci return !!vsi->arfs_fltr_list; 1362306a36Sopenharmony_ci} 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/** 1662306a36Sopenharmony_ci * ice_is_arfs_using_perfect_flow - check if aRFS has active perfect filters 1762306a36Sopenharmony_ci * @hw: pointer to the HW structure 1862306a36Sopenharmony_ci * @flow_type: flow type as Flow Director understands it 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Flow Director will query this function to see if aRFS is currently using 2162306a36Sopenharmony_ci * the specified flow_type for perfect (4-tuple) filters. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cibool 2462306a36Sopenharmony_ciice_is_arfs_using_perfect_flow(struct ice_hw *hw, enum ice_fltr_ptype flow_type) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct ice_arfs_active_fltr_cntrs *arfs_fltr_cntrs; 2762306a36Sopenharmony_ci struct ice_pf *pf = hw->back; 2862306a36Sopenharmony_ci struct ice_vsi *vsi; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci vsi = ice_get_main_vsi(pf); 3162306a36Sopenharmony_ci if (!vsi) 3262306a36Sopenharmony_ci return false; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci arfs_fltr_cntrs = vsi->arfs_fltr_cntrs; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* active counters can be updated by multiple CPUs */ 3762306a36Sopenharmony_ci smp_mb__before_atomic(); 3862306a36Sopenharmony_ci switch (flow_type) { 3962306a36Sopenharmony_ci case ICE_FLTR_PTYPE_NONF_IPV4_UDP: 4062306a36Sopenharmony_ci return atomic_read(&arfs_fltr_cntrs->active_udpv4_cnt) > 0; 4162306a36Sopenharmony_ci case ICE_FLTR_PTYPE_NONF_IPV6_UDP: 4262306a36Sopenharmony_ci return atomic_read(&arfs_fltr_cntrs->active_udpv6_cnt) > 0; 4362306a36Sopenharmony_ci case ICE_FLTR_PTYPE_NONF_IPV4_TCP: 4462306a36Sopenharmony_ci return atomic_read(&arfs_fltr_cntrs->active_tcpv4_cnt) > 0; 4562306a36Sopenharmony_ci case ICE_FLTR_PTYPE_NONF_IPV6_TCP: 4662306a36Sopenharmony_ci return atomic_read(&arfs_fltr_cntrs->active_tcpv6_cnt) > 0; 4762306a36Sopenharmony_ci default: 4862306a36Sopenharmony_ci return false; 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * ice_arfs_update_active_fltr_cntrs - update active filter counters for aRFS 5462306a36Sopenharmony_ci * @vsi: VSI that aRFS is active on 5562306a36Sopenharmony_ci * @entry: aRFS entry used to change counters 5662306a36Sopenharmony_ci * @add: true to increment counter, false to decrement 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic void 5962306a36Sopenharmony_ciice_arfs_update_active_fltr_cntrs(struct ice_vsi *vsi, 6062306a36Sopenharmony_ci struct ice_arfs_entry *entry, bool add) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct ice_arfs_active_fltr_cntrs *fltr_cntrs = vsi->arfs_fltr_cntrs; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci switch (entry->fltr_info.flow_type) { 6562306a36Sopenharmony_ci case ICE_FLTR_PTYPE_NONF_IPV4_TCP: 6662306a36Sopenharmony_ci if (add) 6762306a36Sopenharmony_ci atomic_inc(&fltr_cntrs->active_tcpv4_cnt); 6862306a36Sopenharmony_ci else 6962306a36Sopenharmony_ci atomic_dec(&fltr_cntrs->active_tcpv4_cnt); 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci case ICE_FLTR_PTYPE_NONF_IPV6_TCP: 7262306a36Sopenharmony_ci if (add) 7362306a36Sopenharmony_ci atomic_inc(&fltr_cntrs->active_tcpv6_cnt); 7462306a36Sopenharmony_ci else 7562306a36Sopenharmony_ci atomic_dec(&fltr_cntrs->active_tcpv6_cnt); 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci case ICE_FLTR_PTYPE_NONF_IPV4_UDP: 7862306a36Sopenharmony_ci if (add) 7962306a36Sopenharmony_ci atomic_inc(&fltr_cntrs->active_udpv4_cnt); 8062306a36Sopenharmony_ci else 8162306a36Sopenharmony_ci atomic_dec(&fltr_cntrs->active_udpv4_cnt); 8262306a36Sopenharmony_ci break; 8362306a36Sopenharmony_ci case ICE_FLTR_PTYPE_NONF_IPV6_UDP: 8462306a36Sopenharmony_ci if (add) 8562306a36Sopenharmony_ci atomic_inc(&fltr_cntrs->active_udpv6_cnt); 8662306a36Sopenharmony_ci else 8762306a36Sopenharmony_ci atomic_dec(&fltr_cntrs->active_udpv6_cnt); 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci default: 9062306a36Sopenharmony_ci dev_err(ice_pf_to_dev(vsi->back), "aRFS: Failed to update filter counters, invalid filter type %d\n", 9162306a36Sopenharmony_ci entry->fltr_info.flow_type); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/** 9662306a36Sopenharmony_ci * ice_arfs_del_flow_rules - delete the rules passed in from HW 9762306a36Sopenharmony_ci * @vsi: VSI for the flow rules that need to be deleted 9862306a36Sopenharmony_ci * @del_list_head: head of the list of ice_arfs_entry(s) for rule deletion 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * Loop through the delete list passed in and remove the rules from HW. After 10162306a36Sopenharmony_ci * each rule is deleted, disconnect and free the ice_arfs_entry because it is no 10262306a36Sopenharmony_ci * longer being referenced by the aRFS hash table. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cistatic void 10562306a36Sopenharmony_ciice_arfs_del_flow_rules(struct ice_vsi *vsi, struct hlist_head *del_list_head) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct ice_arfs_entry *e; 10862306a36Sopenharmony_ci struct hlist_node *n; 10962306a36Sopenharmony_ci struct device *dev; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci dev = ice_pf_to_dev(vsi->back); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci hlist_for_each_entry_safe(e, n, del_list_head, list_entry) { 11462306a36Sopenharmony_ci int result; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci result = ice_fdir_write_fltr(vsi->back, &e->fltr_info, false, 11762306a36Sopenharmony_ci false); 11862306a36Sopenharmony_ci if (!result) 11962306a36Sopenharmony_ci ice_arfs_update_active_fltr_cntrs(vsi, e, false); 12062306a36Sopenharmony_ci else 12162306a36Sopenharmony_ci dev_dbg(dev, "Unable to delete aRFS entry, err %d fltr_state %d fltr_id %d flow_id %d Q %d\n", 12262306a36Sopenharmony_ci result, e->fltr_state, e->fltr_info.fltr_id, 12362306a36Sopenharmony_ci e->flow_id, e->fltr_info.q_index); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* The aRFS hash table is no longer referencing this entry */ 12662306a36Sopenharmony_ci hlist_del(&e->list_entry); 12762306a36Sopenharmony_ci devm_kfree(dev, e); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/** 13262306a36Sopenharmony_ci * ice_arfs_add_flow_rules - add the rules passed in from HW 13362306a36Sopenharmony_ci * @vsi: VSI for the flow rules that need to be added 13462306a36Sopenharmony_ci * @add_list_head: head of the list of ice_arfs_entry_ptr(s) for rule addition 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * Loop through the add list passed in and remove the rules from HW. After each 13762306a36Sopenharmony_ci * rule is added, disconnect and free the ice_arfs_entry_ptr node. Don't free 13862306a36Sopenharmony_ci * the ice_arfs_entry(s) because they are still being referenced in the aRFS 13962306a36Sopenharmony_ci * hash table. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic void 14262306a36Sopenharmony_ciice_arfs_add_flow_rules(struct ice_vsi *vsi, struct hlist_head *add_list_head) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct ice_arfs_entry_ptr *ep; 14562306a36Sopenharmony_ci struct hlist_node *n; 14662306a36Sopenharmony_ci struct device *dev; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci dev = ice_pf_to_dev(vsi->back); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci hlist_for_each_entry_safe(ep, n, add_list_head, list_entry) { 15162306a36Sopenharmony_ci int result; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci result = ice_fdir_write_fltr(vsi->back, 15462306a36Sopenharmony_ci &ep->arfs_entry->fltr_info, true, 15562306a36Sopenharmony_ci false); 15662306a36Sopenharmony_ci if (!result) 15762306a36Sopenharmony_ci ice_arfs_update_active_fltr_cntrs(vsi, ep->arfs_entry, 15862306a36Sopenharmony_ci true); 15962306a36Sopenharmony_ci else 16062306a36Sopenharmony_ci dev_dbg(dev, "Unable to add aRFS entry, err %d fltr_state %d fltr_id %d flow_id %d Q %d\n", 16162306a36Sopenharmony_ci result, ep->arfs_entry->fltr_state, 16262306a36Sopenharmony_ci ep->arfs_entry->fltr_info.fltr_id, 16362306a36Sopenharmony_ci ep->arfs_entry->flow_id, 16462306a36Sopenharmony_ci ep->arfs_entry->fltr_info.q_index); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci hlist_del(&ep->list_entry); 16762306a36Sopenharmony_ci devm_kfree(dev, ep); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/** 17262306a36Sopenharmony_ci * ice_arfs_is_flow_expired - check if the aRFS entry has expired 17362306a36Sopenharmony_ci * @vsi: VSI containing the aRFS entry 17462306a36Sopenharmony_ci * @arfs_entry: aRFS entry that's being checked for expiration 17562306a36Sopenharmony_ci * 17662306a36Sopenharmony_ci * Return true if the flow has expired, else false. This function should be used 17762306a36Sopenharmony_ci * to determine whether or not an aRFS entry should be removed from the hardware 17862306a36Sopenharmony_ci * and software structures. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_cistatic bool 18162306a36Sopenharmony_ciice_arfs_is_flow_expired(struct ice_vsi *vsi, struct ice_arfs_entry *arfs_entry) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci#define ICE_ARFS_TIME_DELTA_EXPIRATION msecs_to_jiffies(5000) 18462306a36Sopenharmony_ci if (rps_may_expire_flow(vsi->netdev, arfs_entry->fltr_info.q_index, 18562306a36Sopenharmony_ci arfs_entry->flow_id, 18662306a36Sopenharmony_ci arfs_entry->fltr_info.fltr_id)) 18762306a36Sopenharmony_ci return true; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* expiration timer only used for UDP filters */ 19062306a36Sopenharmony_ci if (arfs_entry->fltr_info.flow_type != ICE_FLTR_PTYPE_NONF_IPV4_UDP && 19162306a36Sopenharmony_ci arfs_entry->fltr_info.flow_type != ICE_FLTR_PTYPE_NONF_IPV6_UDP) 19262306a36Sopenharmony_ci return false; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return time_in_range64(arfs_entry->time_activated + 19562306a36Sopenharmony_ci ICE_ARFS_TIME_DELTA_EXPIRATION, 19662306a36Sopenharmony_ci arfs_entry->time_activated, get_jiffies_64()); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/** 20062306a36Sopenharmony_ci * ice_arfs_update_flow_rules - add/delete aRFS rules in HW 20162306a36Sopenharmony_ci * @vsi: the VSI to be forwarded to 20262306a36Sopenharmony_ci * @idx: index into the table of aRFS filter lists. Obtained from skb->hash 20362306a36Sopenharmony_ci * @add_list: list to populate with filters to be added to Flow Director 20462306a36Sopenharmony_ci * @del_list: list to populate with filters to be deleted from Flow Director 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * Iterate over the hlist at the index given in the aRFS hash table and 20762306a36Sopenharmony_ci * determine if there are any aRFS entries that need to be either added or 20862306a36Sopenharmony_ci * deleted in the HW. If the aRFS entry is marked as ICE_ARFS_INACTIVE the 20962306a36Sopenharmony_ci * filter needs to be added to HW, else if it's marked as ICE_ARFS_ACTIVE and 21062306a36Sopenharmony_ci * the flow has expired delete the filter from HW. The caller of this function 21162306a36Sopenharmony_ci * is expected to add/delete rules on the add_list/del_list respectively. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistatic void 21462306a36Sopenharmony_ciice_arfs_update_flow_rules(struct ice_vsi *vsi, u16 idx, 21562306a36Sopenharmony_ci struct hlist_head *add_list, 21662306a36Sopenharmony_ci struct hlist_head *del_list) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct ice_arfs_entry *e; 21962306a36Sopenharmony_ci struct hlist_node *n; 22062306a36Sopenharmony_ci struct device *dev; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci dev = ice_pf_to_dev(vsi->back); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* go through the aRFS hlist at this idx and check for needed updates */ 22562306a36Sopenharmony_ci hlist_for_each_entry_safe(e, n, &vsi->arfs_fltr_list[idx], list_entry) 22662306a36Sopenharmony_ci /* check if filter needs to be added to HW */ 22762306a36Sopenharmony_ci if (e->fltr_state == ICE_ARFS_INACTIVE) { 22862306a36Sopenharmony_ci enum ice_fltr_ptype flow_type = e->fltr_info.flow_type; 22962306a36Sopenharmony_ci struct ice_arfs_entry_ptr *ep = 23062306a36Sopenharmony_ci devm_kzalloc(dev, sizeof(*ep), GFP_ATOMIC); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (!ep) 23362306a36Sopenharmony_ci continue; 23462306a36Sopenharmony_ci INIT_HLIST_NODE(&ep->list_entry); 23562306a36Sopenharmony_ci /* reference aRFS entry to add HW filter */ 23662306a36Sopenharmony_ci ep->arfs_entry = e; 23762306a36Sopenharmony_ci hlist_add_head(&ep->list_entry, add_list); 23862306a36Sopenharmony_ci e->fltr_state = ICE_ARFS_ACTIVE; 23962306a36Sopenharmony_ci /* expiration timer only used for UDP flows */ 24062306a36Sopenharmony_ci if (flow_type == ICE_FLTR_PTYPE_NONF_IPV4_UDP || 24162306a36Sopenharmony_ci flow_type == ICE_FLTR_PTYPE_NONF_IPV6_UDP) 24262306a36Sopenharmony_ci e->time_activated = get_jiffies_64(); 24362306a36Sopenharmony_ci } else if (e->fltr_state == ICE_ARFS_ACTIVE) { 24462306a36Sopenharmony_ci /* check if filter needs to be removed from HW */ 24562306a36Sopenharmony_ci if (ice_arfs_is_flow_expired(vsi, e)) { 24662306a36Sopenharmony_ci /* remove aRFS entry from hash table for delete 24762306a36Sopenharmony_ci * and to prevent referencing it the next time 24862306a36Sopenharmony_ci * through this hlist index 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci hlist_del(&e->list_entry); 25162306a36Sopenharmony_ci e->fltr_state = ICE_ARFS_TODEL; 25262306a36Sopenharmony_ci /* save reference to aRFS entry for delete */ 25362306a36Sopenharmony_ci hlist_add_head(&e->list_entry, del_list); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/** 25962306a36Sopenharmony_ci * ice_sync_arfs_fltrs - update all aRFS filters 26062306a36Sopenharmony_ci * @pf: board private structure 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_civoid ice_sync_arfs_fltrs(struct ice_pf *pf) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci HLIST_HEAD(tmp_del_list); 26562306a36Sopenharmony_ci HLIST_HEAD(tmp_add_list); 26662306a36Sopenharmony_ci struct ice_vsi *pf_vsi; 26762306a36Sopenharmony_ci unsigned int i; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci pf_vsi = ice_get_main_vsi(pf); 27062306a36Sopenharmony_ci if (!pf_vsi) 27162306a36Sopenharmony_ci return; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (!ice_is_arfs_active(pf_vsi)) 27462306a36Sopenharmony_ci return; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci spin_lock_bh(&pf_vsi->arfs_lock); 27762306a36Sopenharmony_ci /* Once we process aRFS for the PF VSI get out */ 27862306a36Sopenharmony_ci for (i = 0; i < ICE_MAX_ARFS_LIST; i++) 27962306a36Sopenharmony_ci ice_arfs_update_flow_rules(pf_vsi, i, &tmp_add_list, 28062306a36Sopenharmony_ci &tmp_del_list); 28162306a36Sopenharmony_ci spin_unlock_bh(&pf_vsi->arfs_lock); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* use list of ice_arfs_entry(s) for delete */ 28462306a36Sopenharmony_ci ice_arfs_del_flow_rules(pf_vsi, &tmp_del_list); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* use list of ice_arfs_entry_ptr(s) for add */ 28762306a36Sopenharmony_ci ice_arfs_add_flow_rules(pf_vsi, &tmp_add_list); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/** 29162306a36Sopenharmony_ci * ice_arfs_build_entry - builds an aRFS entry based on input 29262306a36Sopenharmony_ci * @vsi: destination VSI for this flow 29362306a36Sopenharmony_ci * @fk: flow dissector keys for creating the tuple 29462306a36Sopenharmony_ci * @rxq_idx: Rx queue to steer this flow to 29562306a36Sopenharmony_ci * @flow_id: passed down from the stack and saved for flow expiration 29662306a36Sopenharmony_ci * 29762306a36Sopenharmony_ci * returns an aRFS entry on success and NULL on failure 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_cistatic struct ice_arfs_entry * 30062306a36Sopenharmony_ciice_arfs_build_entry(struct ice_vsi *vsi, const struct flow_keys *fk, 30162306a36Sopenharmony_ci u16 rxq_idx, u32 flow_id) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct ice_arfs_entry *arfs_entry; 30462306a36Sopenharmony_ci struct ice_fdir_fltr *fltr_info; 30562306a36Sopenharmony_ci u8 ip_proto; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci arfs_entry = devm_kzalloc(ice_pf_to_dev(vsi->back), 30862306a36Sopenharmony_ci sizeof(*arfs_entry), 30962306a36Sopenharmony_ci GFP_ATOMIC | __GFP_NOWARN); 31062306a36Sopenharmony_ci if (!arfs_entry) 31162306a36Sopenharmony_ci return NULL; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci fltr_info = &arfs_entry->fltr_info; 31462306a36Sopenharmony_ci fltr_info->q_index = rxq_idx; 31562306a36Sopenharmony_ci fltr_info->dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DIRECT_PKT_QINDEX; 31662306a36Sopenharmony_ci fltr_info->dest_vsi = vsi->idx; 31762306a36Sopenharmony_ci ip_proto = fk->basic.ip_proto; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (fk->basic.n_proto == htons(ETH_P_IP)) { 32062306a36Sopenharmony_ci fltr_info->ip.v4.proto = ip_proto; 32162306a36Sopenharmony_ci fltr_info->flow_type = (ip_proto == IPPROTO_TCP) ? 32262306a36Sopenharmony_ci ICE_FLTR_PTYPE_NONF_IPV4_TCP : 32362306a36Sopenharmony_ci ICE_FLTR_PTYPE_NONF_IPV4_UDP; 32462306a36Sopenharmony_ci fltr_info->ip.v4.src_ip = fk->addrs.v4addrs.src; 32562306a36Sopenharmony_ci fltr_info->ip.v4.dst_ip = fk->addrs.v4addrs.dst; 32662306a36Sopenharmony_ci fltr_info->ip.v4.src_port = fk->ports.src; 32762306a36Sopenharmony_ci fltr_info->ip.v4.dst_port = fk->ports.dst; 32862306a36Sopenharmony_ci } else { /* ETH_P_IPV6 */ 32962306a36Sopenharmony_ci fltr_info->ip.v6.proto = ip_proto; 33062306a36Sopenharmony_ci fltr_info->flow_type = (ip_proto == IPPROTO_TCP) ? 33162306a36Sopenharmony_ci ICE_FLTR_PTYPE_NONF_IPV6_TCP : 33262306a36Sopenharmony_ci ICE_FLTR_PTYPE_NONF_IPV6_UDP; 33362306a36Sopenharmony_ci memcpy(&fltr_info->ip.v6.src_ip, &fk->addrs.v6addrs.src, 33462306a36Sopenharmony_ci sizeof(struct in6_addr)); 33562306a36Sopenharmony_ci memcpy(&fltr_info->ip.v6.dst_ip, &fk->addrs.v6addrs.dst, 33662306a36Sopenharmony_ci sizeof(struct in6_addr)); 33762306a36Sopenharmony_ci fltr_info->ip.v6.src_port = fk->ports.src; 33862306a36Sopenharmony_ci fltr_info->ip.v6.dst_port = fk->ports.dst; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci arfs_entry->flow_id = flow_id; 34262306a36Sopenharmony_ci fltr_info->fltr_id = 34362306a36Sopenharmony_ci atomic_inc_return(vsi->arfs_last_fltr_id) % RPS_NO_FILTER; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return arfs_entry; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/** 34962306a36Sopenharmony_ci * ice_arfs_is_perfect_flow_set - Check to see if perfect flow is set 35062306a36Sopenharmony_ci * @hw: pointer to HW structure 35162306a36Sopenharmony_ci * @l3_proto: ETH_P_IP or ETH_P_IPV6 in network order 35262306a36Sopenharmony_ci * @l4_proto: IPPROTO_UDP or IPPROTO_TCP 35362306a36Sopenharmony_ci * 35462306a36Sopenharmony_ci * We only support perfect (4-tuple) filters for aRFS. This function allows aRFS 35562306a36Sopenharmony_ci * to check if perfect (4-tuple) flow rules are currently in place by Flow 35662306a36Sopenharmony_ci * Director. 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_cistatic bool 35962306a36Sopenharmony_ciice_arfs_is_perfect_flow_set(struct ice_hw *hw, __be16 l3_proto, u8 l4_proto) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci unsigned long *perfect_fltr = hw->fdir_perfect_fltr; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* advanced Flow Director disabled, perfect filters always supported */ 36462306a36Sopenharmony_ci if (!perfect_fltr) 36562306a36Sopenharmony_ci return true; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (l3_proto == htons(ETH_P_IP) && l4_proto == IPPROTO_UDP) 36862306a36Sopenharmony_ci return test_bit(ICE_FLTR_PTYPE_NONF_IPV4_UDP, perfect_fltr); 36962306a36Sopenharmony_ci else if (l3_proto == htons(ETH_P_IP) && l4_proto == IPPROTO_TCP) 37062306a36Sopenharmony_ci return test_bit(ICE_FLTR_PTYPE_NONF_IPV4_TCP, perfect_fltr); 37162306a36Sopenharmony_ci else if (l3_proto == htons(ETH_P_IPV6) && l4_proto == IPPROTO_UDP) 37262306a36Sopenharmony_ci return test_bit(ICE_FLTR_PTYPE_NONF_IPV6_UDP, perfect_fltr); 37362306a36Sopenharmony_ci else if (l3_proto == htons(ETH_P_IPV6) && l4_proto == IPPROTO_TCP) 37462306a36Sopenharmony_ci return test_bit(ICE_FLTR_PTYPE_NONF_IPV6_TCP, perfect_fltr); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return false; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/** 38062306a36Sopenharmony_ci * ice_rx_flow_steer - steer the Rx flow to where application is being run 38162306a36Sopenharmony_ci * @netdev: ptr to the netdev being adjusted 38262306a36Sopenharmony_ci * @skb: buffer with required header information 38362306a36Sopenharmony_ci * @rxq_idx: queue to which the flow needs to move 38462306a36Sopenharmony_ci * @flow_id: flow identifier provided by the netdev 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * Based on the skb, rxq_idx, and flow_id passed in add/update an entry in the 38762306a36Sopenharmony_ci * aRFS hash table. Iterate over one of the hlists in the aRFS hash table and 38862306a36Sopenharmony_ci * if the flow_id already exists in the hash table but the rxq_idx has changed 38962306a36Sopenharmony_ci * mark the entry as ICE_ARFS_INACTIVE so it can get updated in HW, else 39062306a36Sopenharmony_ci * if the entry is marked as ICE_ARFS_TODEL delete it from the aRFS hash table. 39162306a36Sopenharmony_ci * If neither of the previous conditions are true then add a new entry in the 39262306a36Sopenharmony_ci * aRFS hash table, which gets set to ICE_ARFS_INACTIVE by default so it can be 39362306a36Sopenharmony_ci * added to HW. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ciint 39662306a36Sopenharmony_ciice_rx_flow_steer(struct net_device *netdev, const struct sk_buff *skb, 39762306a36Sopenharmony_ci u16 rxq_idx, u32 flow_id) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct ice_netdev_priv *np = netdev_priv(netdev); 40062306a36Sopenharmony_ci struct ice_arfs_entry *arfs_entry; 40162306a36Sopenharmony_ci struct ice_vsi *vsi = np->vsi; 40262306a36Sopenharmony_ci struct flow_keys fk; 40362306a36Sopenharmony_ci struct ice_pf *pf; 40462306a36Sopenharmony_ci __be16 n_proto; 40562306a36Sopenharmony_ci u8 ip_proto; 40662306a36Sopenharmony_ci u16 idx; 40762306a36Sopenharmony_ci int ret; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* failed to allocate memory for aRFS so don't crash */ 41062306a36Sopenharmony_ci if (unlikely(!vsi->arfs_fltr_list)) 41162306a36Sopenharmony_ci return -ENODEV; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci pf = vsi->back; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (skb->encapsulation) 41662306a36Sopenharmony_ci return -EPROTONOSUPPORT; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (!skb_flow_dissect_flow_keys(skb, &fk, 0)) 41962306a36Sopenharmony_ci return -EPROTONOSUPPORT; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci n_proto = fk.basic.n_proto; 42262306a36Sopenharmony_ci /* Support only IPV4 and IPV6 */ 42362306a36Sopenharmony_ci if ((n_proto == htons(ETH_P_IP) && !ip_is_fragment(ip_hdr(skb))) || 42462306a36Sopenharmony_ci n_proto == htons(ETH_P_IPV6)) 42562306a36Sopenharmony_ci ip_proto = fk.basic.ip_proto; 42662306a36Sopenharmony_ci else 42762306a36Sopenharmony_ci return -EPROTONOSUPPORT; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Support only TCP and UDP */ 43062306a36Sopenharmony_ci if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) 43162306a36Sopenharmony_ci return -EPROTONOSUPPORT; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* only support 4-tuple filters for aRFS */ 43462306a36Sopenharmony_ci if (!ice_arfs_is_perfect_flow_set(&pf->hw, n_proto, ip_proto)) 43562306a36Sopenharmony_ci return -EOPNOTSUPP; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* choose the aRFS list bucket based on skb hash */ 43862306a36Sopenharmony_ci idx = skb_get_hash_raw(skb) & ICE_ARFS_LST_MASK; 43962306a36Sopenharmony_ci /* search for entry in the bucket */ 44062306a36Sopenharmony_ci spin_lock_bh(&vsi->arfs_lock); 44162306a36Sopenharmony_ci hlist_for_each_entry(arfs_entry, &vsi->arfs_fltr_list[idx], 44262306a36Sopenharmony_ci list_entry) { 44362306a36Sopenharmony_ci struct ice_fdir_fltr *fltr_info; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* keep searching for the already existing arfs_entry flow */ 44662306a36Sopenharmony_ci if (arfs_entry->flow_id != flow_id) 44762306a36Sopenharmony_ci continue; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci fltr_info = &arfs_entry->fltr_info; 45062306a36Sopenharmony_ci ret = fltr_info->fltr_id; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (fltr_info->q_index == rxq_idx || 45362306a36Sopenharmony_ci arfs_entry->fltr_state != ICE_ARFS_ACTIVE) 45462306a36Sopenharmony_ci goto out; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* update the queue to forward to on an already existing flow */ 45762306a36Sopenharmony_ci fltr_info->q_index = rxq_idx; 45862306a36Sopenharmony_ci arfs_entry->fltr_state = ICE_ARFS_INACTIVE; 45962306a36Sopenharmony_ci ice_arfs_update_active_fltr_cntrs(vsi, arfs_entry, false); 46062306a36Sopenharmony_ci goto out_schedule_service_task; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci arfs_entry = ice_arfs_build_entry(vsi, &fk, rxq_idx, flow_id); 46462306a36Sopenharmony_ci if (!arfs_entry) { 46562306a36Sopenharmony_ci ret = -ENOMEM; 46662306a36Sopenharmony_ci goto out; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ret = arfs_entry->fltr_info.fltr_id; 47062306a36Sopenharmony_ci INIT_HLIST_NODE(&arfs_entry->list_entry); 47162306a36Sopenharmony_ci hlist_add_head(&arfs_entry->list_entry, &vsi->arfs_fltr_list[idx]); 47262306a36Sopenharmony_ciout_schedule_service_task: 47362306a36Sopenharmony_ci ice_service_task_schedule(pf); 47462306a36Sopenharmony_ciout: 47562306a36Sopenharmony_ci spin_unlock_bh(&vsi->arfs_lock); 47662306a36Sopenharmony_ci return ret; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/** 48062306a36Sopenharmony_ci * ice_init_arfs_cntrs - initialize aRFS counter values 48162306a36Sopenharmony_ci * @vsi: VSI that aRFS counters need to be initialized on 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_cistatic int ice_init_arfs_cntrs(struct ice_vsi *vsi) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci if (!vsi || vsi->type != ICE_VSI_PF) 48662306a36Sopenharmony_ci return -EINVAL; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci vsi->arfs_fltr_cntrs = kzalloc(sizeof(*vsi->arfs_fltr_cntrs), 48962306a36Sopenharmony_ci GFP_KERNEL); 49062306a36Sopenharmony_ci if (!vsi->arfs_fltr_cntrs) 49162306a36Sopenharmony_ci return -ENOMEM; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci vsi->arfs_last_fltr_id = kzalloc(sizeof(*vsi->arfs_last_fltr_id), 49462306a36Sopenharmony_ci GFP_KERNEL); 49562306a36Sopenharmony_ci if (!vsi->arfs_last_fltr_id) { 49662306a36Sopenharmony_ci kfree(vsi->arfs_fltr_cntrs); 49762306a36Sopenharmony_ci vsi->arfs_fltr_cntrs = NULL; 49862306a36Sopenharmony_ci return -ENOMEM; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return 0; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/** 50562306a36Sopenharmony_ci * ice_init_arfs - initialize aRFS resources 50662306a36Sopenharmony_ci * @vsi: the VSI to be forwarded to 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_civoid ice_init_arfs(struct ice_vsi *vsi) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct hlist_head *arfs_fltr_list; 51162306a36Sopenharmony_ci unsigned int i; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (!vsi || vsi->type != ICE_VSI_PF) 51462306a36Sopenharmony_ci return; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci arfs_fltr_list = kcalloc(ICE_MAX_ARFS_LIST, sizeof(*arfs_fltr_list), 51762306a36Sopenharmony_ci GFP_KERNEL); 51862306a36Sopenharmony_ci if (!arfs_fltr_list) 51962306a36Sopenharmony_ci return; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (ice_init_arfs_cntrs(vsi)) 52262306a36Sopenharmony_ci goto free_arfs_fltr_list; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci for (i = 0; i < ICE_MAX_ARFS_LIST; i++) 52562306a36Sopenharmony_ci INIT_HLIST_HEAD(&arfs_fltr_list[i]); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci spin_lock_init(&vsi->arfs_lock); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci vsi->arfs_fltr_list = arfs_fltr_list; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cifree_arfs_fltr_list: 53462306a36Sopenharmony_ci kfree(arfs_fltr_list); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci/** 53862306a36Sopenharmony_ci * ice_clear_arfs - clear the aRFS hash table and any memory used for aRFS 53962306a36Sopenharmony_ci * @vsi: the VSI to be forwarded to 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_civoid ice_clear_arfs(struct ice_vsi *vsi) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct device *dev; 54462306a36Sopenharmony_ci unsigned int i; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (!vsi || vsi->type != ICE_VSI_PF || !vsi->back || 54762306a36Sopenharmony_ci !vsi->arfs_fltr_list) 54862306a36Sopenharmony_ci return; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci dev = ice_pf_to_dev(vsi->back); 55162306a36Sopenharmony_ci for (i = 0; i < ICE_MAX_ARFS_LIST; i++) { 55262306a36Sopenharmony_ci struct ice_arfs_entry *r; 55362306a36Sopenharmony_ci struct hlist_node *n; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci spin_lock_bh(&vsi->arfs_lock); 55662306a36Sopenharmony_ci hlist_for_each_entry_safe(r, n, &vsi->arfs_fltr_list[i], 55762306a36Sopenharmony_ci list_entry) { 55862306a36Sopenharmony_ci hlist_del(&r->list_entry); 55962306a36Sopenharmony_ci devm_kfree(dev, r); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci spin_unlock_bh(&vsi->arfs_lock); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci kfree(vsi->arfs_fltr_list); 56562306a36Sopenharmony_ci vsi->arfs_fltr_list = NULL; 56662306a36Sopenharmony_ci kfree(vsi->arfs_last_fltr_id); 56762306a36Sopenharmony_ci vsi->arfs_last_fltr_id = NULL; 56862306a36Sopenharmony_ci kfree(vsi->arfs_fltr_cntrs); 56962306a36Sopenharmony_ci vsi->arfs_fltr_cntrs = NULL; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/** 57362306a36Sopenharmony_ci * ice_free_cpu_rx_rmap - free setup CPU reverse map 57462306a36Sopenharmony_ci * @vsi: the VSI to be forwarded to 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_civoid ice_free_cpu_rx_rmap(struct ice_vsi *vsi) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct net_device *netdev; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!vsi || vsi->type != ICE_VSI_PF) 58162306a36Sopenharmony_ci return; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci netdev = vsi->netdev; 58462306a36Sopenharmony_ci if (!netdev || !netdev->rx_cpu_rmap) 58562306a36Sopenharmony_ci return; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci free_irq_cpu_rmap(netdev->rx_cpu_rmap); 58862306a36Sopenharmony_ci netdev->rx_cpu_rmap = NULL; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/** 59262306a36Sopenharmony_ci * ice_set_cpu_rx_rmap - setup CPU reverse map for each queue 59362306a36Sopenharmony_ci * @vsi: the VSI to be forwarded to 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_ciint ice_set_cpu_rx_rmap(struct ice_vsi *vsi) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct net_device *netdev; 59862306a36Sopenharmony_ci struct ice_pf *pf; 59962306a36Sopenharmony_ci int i; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (!vsi || vsi->type != ICE_VSI_PF) 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci pf = vsi->back; 60562306a36Sopenharmony_ci netdev = vsi->netdev; 60662306a36Sopenharmony_ci if (!pf || !netdev || !vsi->num_q_vectors) 60762306a36Sopenharmony_ci return -EINVAL; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci netdev_dbg(netdev, "Setup CPU RMAP: vsi type 0x%x, ifname %s, q_vectors %d\n", 61062306a36Sopenharmony_ci vsi->type, netdev->name, vsi->num_q_vectors); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci netdev->rx_cpu_rmap = alloc_irq_cpu_rmap(vsi->num_q_vectors); 61362306a36Sopenharmony_ci if (unlikely(!netdev->rx_cpu_rmap)) 61462306a36Sopenharmony_ci return -EINVAL; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci ice_for_each_q_vector(vsi, i) 61762306a36Sopenharmony_ci if (irq_cpu_rmap_add(netdev->rx_cpu_rmap, 61862306a36Sopenharmony_ci vsi->q_vectors[i]->irq.virq)) { 61962306a36Sopenharmony_ci ice_free_cpu_rx_rmap(vsi); 62062306a36Sopenharmony_ci return -EINVAL; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci return 0; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci/** 62762306a36Sopenharmony_ci * ice_remove_arfs - remove/clear all aRFS resources 62862306a36Sopenharmony_ci * @pf: device private structure 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_civoid ice_remove_arfs(struct ice_pf *pf) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct ice_vsi *pf_vsi; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci pf_vsi = ice_get_main_vsi(pf); 63562306a36Sopenharmony_ci if (!pf_vsi) 63662306a36Sopenharmony_ci return; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci ice_clear_arfs(pf_vsi); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci/** 64262306a36Sopenharmony_ci * ice_rebuild_arfs - remove/clear all aRFS resources and rebuild after reset 64362306a36Sopenharmony_ci * @pf: device private structure 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_civoid ice_rebuild_arfs(struct ice_pf *pf) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct ice_vsi *pf_vsi; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci pf_vsi = ice_get_main_vsi(pf); 65062306a36Sopenharmony_ci if (!pf_vsi) 65162306a36Sopenharmony_ci return; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ice_remove_arfs(pf); 65462306a36Sopenharmony_ci ice_init_arfs(pf_vsi); 65562306a36Sopenharmony_ci} 656