| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright (C) 2018-2023, Intel Corporation. */
- /* flow director ethtool support for ice */
- #include "ice.h"
- #include "ice_lib.h"
- #include "ice_fdir.h"
- #include "ice_flow.h"
- static struct in6_addr full_ipv6_addr_mask = {
- .in6_u = {
- .u6_addr8 = {
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- }
- }
- };
- static struct in6_addr zero_ipv6_addr_mask = {
- .in6_u = {
- .u6_addr8 = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- }
- }
- };
- /* calls to ice_flow_add_prof require the number of segments in the array
- * for segs_cnt. In this code that is one more than the index.
- */
- #define TNL_SEG_CNT(_TNL_) ((_TNL_) + 1)
- /**
- * ice_fltr_to_ethtool_flow - convert filter type values to ethtool
- * flow type values
- * @flow: filter type to be converted
- *
- * Returns the corresponding ethtool flow type.
- */
- static int ice_fltr_to_ethtool_flow(enum ice_fltr_ptype flow)
- {
- switch (flow) {
- case ICE_FLTR_PTYPE_NONF_ETH:
- return ETHER_FLOW;
- case ICE_FLTR_PTYPE_NONF_IPV4_TCP:
- return TCP_V4_FLOW;
- case ICE_FLTR_PTYPE_NONF_IPV4_UDP:
- return UDP_V4_FLOW;
- case ICE_FLTR_PTYPE_NONF_IPV4_SCTP:
- return SCTP_V4_FLOW;
- case ICE_FLTR_PTYPE_NONF_IPV4_OTHER:
- return IPV4_USER_FLOW;
- case ICE_FLTR_PTYPE_NONF_IPV6_TCP:
- return TCP_V6_FLOW;
- case ICE_FLTR_PTYPE_NONF_IPV6_UDP:
- return UDP_V6_FLOW;
- case ICE_FLTR_PTYPE_NONF_IPV6_SCTP:
- return SCTP_V6_FLOW;
- case ICE_FLTR_PTYPE_NONF_IPV6_OTHER:
- return IPV6_USER_FLOW;
- default:
- /* 0 is undefined ethtool flow */
- return 0;
- }
- }
- /**
- * ice_ethtool_flow_to_fltr - convert ethtool flow type to filter enum
- * @eth: Ethtool flow type to be converted
- *
- * Returns flow enum
- */
- static enum ice_fltr_ptype ice_ethtool_flow_to_fltr(int eth)
- {
- switch (eth) {
- case ETHER_FLOW:
- return ICE_FLTR_PTYPE_NONF_ETH;
- case TCP_V4_FLOW:
- return ICE_FLTR_PTYPE_NONF_IPV4_TCP;
- case UDP_V4_FLOW:
- return ICE_FLTR_PTYPE_NONF_IPV4_UDP;
- case SCTP_V4_FLOW:
- return ICE_FLTR_PTYPE_NONF_IPV4_SCTP;
- case IPV4_USER_FLOW:
- return ICE_FLTR_PTYPE_NONF_IPV4_OTHER;
- case TCP_V6_FLOW:
- return ICE_FLTR_PTYPE_NONF_IPV6_TCP;
- case UDP_V6_FLOW:
- return ICE_FLTR_PTYPE_NONF_IPV6_UDP;
- case SCTP_V6_FLOW:
- return ICE_FLTR_PTYPE_NONF_IPV6_SCTP;
- case IPV6_USER_FLOW:
- return ICE_FLTR_PTYPE_NONF_IPV6_OTHER;
- default:
- return ICE_FLTR_PTYPE_NONF_NONE;
- }
- }
- /**
- * ice_is_mask_valid - check mask field set
- * @mask: full mask to check
- * @field: field for which mask should be valid
- *
- * If the mask is fully set return true. If it is not valid for field return
- * false.
- */
- static bool ice_is_mask_valid(u64 mask, u64 field)
- {
- return (mask & field) == field;
- }
- /**
- * ice_get_ethtool_fdir_entry - fill ethtool structure with fdir filter data
- * @hw: hardware structure that contains filter list
- * @cmd: ethtool command data structure to receive the filter data
- *
- * Returns 0 on success and -EINVAL on failure
- */
- int ice_get_ethtool_fdir_entry(struct ice_hw *hw, struct ethtool_rxnfc *cmd)
- {
- struct ethtool_rx_flow_spec *fsp;
- struct ice_fdir_fltr *rule;
- int ret = 0;
- u16 idx;
- fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
- mutex_lock(&hw->fdir_fltr_lock);
- rule = ice_fdir_find_fltr_by_idx(hw, fsp->location);
- if (!rule || fsp->location != rule->fltr_id) {
- ret = -EINVAL;
- goto release_lock;
- }
- fsp->flow_type = ice_fltr_to_ethtool_flow(rule->flow_type);
- memset(&fsp->m_u, 0, sizeof(fsp->m_u));
- memset(&fsp->m_ext, 0, sizeof(fsp->m_ext));
- switch (fsp->flow_type) {
- case ETHER_FLOW:
- fsp->h_u.ether_spec = rule->eth;
- fsp->m_u.ether_spec = rule->eth_mask;
- break;
- case IPV4_USER_FLOW:
- fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
- fsp->h_u.usr_ip4_spec.proto = 0;
- fsp->h_u.usr_ip4_spec.l4_4_bytes = rule->ip.v4.l4_header;
- fsp->h_u.usr_ip4_spec.tos = rule->ip.v4.tos;
- fsp->h_u.usr_ip4_spec.ip4src = rule->ip.v4.src_ip;
- fsp->h_u.usr_ip4_spec.ip4dst = rule->ip.v4.dst_ip;
- fsp->m_u.usr_ip4_spec.ip4src = rule->mask.v4.src_ip;
- fsp->m_u.usr_ip4_spec.ip4dst = rule->mask.v4.dst_ip;
- fsp->m_u.usr_ip4_spec.ip_ver = 0xFF;
- fsp->m_u.usr_ip4_spec.proto = 0;
- fsp->m_u.usr_ip4_spec.l4_4_bytes = rule->mask.v4.l4_header;
- fsp->m_u.usr_ip4_spec.tos = rule->mask.v4.tos;
- break;
- case TCP_V4_FLOW:
- case UDP_V4_FLOW:
- case SCTP_V4_FLOW:
- fsp->h_u.tcp_ip4_spec.psrc = rule->ip.v4.src_port;
- fsp->h_u.tcp_ip4_spec.pdst = rule->ip.v4.dst_port;
- fsp->h_u.tcp_ip4_spec.ip4src = rule->ip.v4.src_ip;
- fsp->h_u.tcp_ip4_spec.ip4dst = rule->ip.v4.dst_ip;
- fsp->m_u.tcp_ip4_spec.psrc = rule->mask.v4.src_port;
- fsp->m_u.tcp_ip4_spec.pdst = rule->mask.v4.dst_port;
- fsp->m_u.tcp_ip4_spec.ip4src = rule->mask.v4.src_ip;
- fsp->m_u.tcp_ip4_spec.ip4dst = rule->mask.v4.dst_ip;
- break;
- case IPV6_USER_FLOW:
- fsp->h_u.usr_ip6_spec.l4_4_bytes = rule->ip.v6.l4_header;
- fsp->h_u.usr_ip6_spec.tclass = rule->ip.v6.tc;
- fsp->h_u.usr_ip6_spec.l4_proto = rule->ip.v6.proto;
- memcpy(fsp->h_u.tcp_ip6_spec.ip6src, rule->ip.v6.src_ip,
- sizeof(struct in6_addr));
- memcpy(fsp->h_u.tcp_ip6_spec.ip6dst, rule->ip.v6.dst_ip,
- sizeof(struct in6_addr));
- memcpy(fsp->m_u.tcp_ip6_spec.ip6src, rule->mask.v6.src_ip,
- sizeof(struct in6_addr));
- memcpy(fsp->m_u.tcp_ip6_spec.ip6dst, rule->mask.v6.dst_ip,
- sizeof(struct in6_addr));
- fsp->m_u.usr_ip6_spec.l4_4_bytes = rule->mask.v6.l4_header;
- fsp->m_u.usr_ip6_spec.tclass = rule->mask.v6.tc;
- fsp->m_u.usr_ip6_spec.l4_proto = rule->mask.v6.proto;
- break;
- case TCP_V6_FLOW:
- case UDP_V6_FLOW:
- case SCTP_V6_FLOW:
- memcpy(fsp->h_u.tcp_ip6_spec.ip6src, rule->ip.v6.src_ip,
- sizeof(struct in6_addr));
- memcpy(fsp->h_u.tcp_ip6_spec.ip6dst, rule->ip.v6.dst_ip,
- sizeof(struct in6_addr));
- fsp->h_u.tcp_ip6_spec.psrc = rule->ip.v6.src_port;
- fsp->h_u.tcp_ip6_spec.pdst = rule->ip.v6.dst_port;
- memcpy(fsp->m_u.tcp_ip6_spec.ip6src,
- rule->mask.v6.src_ip,
- sizeof(struct in6_addr));
- memcpy(fsp->m_u.tcp_ip6_spec.ip6dst,
- rule->mask.v6.dst_ip,
- sizeof(struct in6_addr));
- fsp->m_u.tcp_ip6_spec.psrc = rule->mask.v6.src_port;
- fsp->m_u.tcp_ip6_spec.pdst = rule->mask.v6.dst_port;
- fsp->h_u.tcp_ip6_spec.tclass = rule->ip.v6.tc;
- fsp->m_u.tcp_ip6_spec.tclass = rule->mask.v6.tc;
- break;
- default:
- break;
- }
- if (rule->dest_ctl == ICE_FLTR_PRGM_DESC_DEST_DROP_PKT)
- fsp->ring_cookie = RX_CLS_FLOW_DISC;
- else
- fsp->ring_cookie = rule->orig_q_index;
- idx = ice_ethtool_flow_to_fltr(fsp->flow_type);
- if (idx == ICE_FLTR_PTYPE_NONF_NONE) {
- dev_err(ice_hw_to_dev(hw), "Missing input index for flow_type %d\n",
- rule->flow_type);
- ret = -EINVAL;
- }
- release_lock:
- mutex_unlock(&hw->fdir_fltr_lock);
- return ret;
- }
- /**
- * ice_get_fdir_fltr_ids - fill buffer with filter IDs of active filters
- * @hw: hardware structure containing the filter list
- * @cmd: ethtool command data structure
- * @rule_locs: ethtool array passed in from OS to receive filter IDs
- *
- * Returns 0 as expected for success by ethtool
- */
- int
- ice_get_fdir_fltr_ids(struct ice_hw *hw, struct ethtool_rxnfc *cmd,
- u32 *rule_locs)
- {
- struct ice_fdir_fltr *f_rule;
- unsigned int cnt = 0;
- int val = 0;
- /* report total rule count */
- cmd->data = ice_get_fdir_cnt_all(hw);
- mutex_lock(&hw->fdir_fltr_lock);
- list_for_each_entry(f_rule, &hw->fdir_list_head, fltr_node) {
- if (cnt == cmd->rule_cnt) {
- val = -EMSGSIZE;
- goto release_lock;
- }
- rule_locs[cnt] = f_rule->fltr_id;
- cnt++;
- }
- release_lock:
- mutex_unlock(&hw->fdir_fltr_lock);
- if (!val)
- cmd->rule_cnt = cnt;
- return val;
- }
- /**
- * ice_fdir_remap_entries - update the FDir entries in profile
- * @prof: FDir structure pointer
- * @tun: tunneled or non-tunneled packet
- * @idx: FDir entry index
- */
- static void
- ice_fdir_remap_entries(struct ice_fd_hw_prof *prof, int tun, int idx)
- {
- if (idx != prof->cnt && tun < ICE_FD_HW_SEG_MAX) {
- int i;
- for (i = idx; i < (prof->cnt - 1); i++) {
- u64 old_entry_h;
- old_entry_h = prof->entry_h[i + 1][tun];
- prof->entry_h[i][tun] = old_entry_h;
- prof->vsi_h[i] = prof->vsi_h[i + 1];
- }
- prof->entry_h[i][tun] = 0;
- prof->vsi_h[i] = 0;
- }
- }
- /**
- * ice_fdir_rem_adq_chnl - remove an ADQ channel from HW filter rules
- * @hw: hardware structure containing filter list
- * @vsi_idx: VSI handle
- */
- void ice_fdir_rem_adq_chnl(struct ice_hw *hw, u16 vsi_idx)
- {
- int status, flow;
- if (!hw->fdir_prof)
- return;
- for (flow = 0; flow < ICE_FLTR_PTYPE_MAX; flow++) {
- struct ice_fd_hw_prof *prof = hw->fdir_prof[flow];
- int tun, i;
- if (!prof || !prof->cnt)
- continue;
- for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
- u64 prof_id = prof->prof_id[tun];
- for (i = 0; i < prof->cnt; i++) {
- if (prof->vsi_h[i] != vsi_idx)
- continue;
- prof->entry_h[i][tun] = 0;
- prof->vsi_h[i] = 0;
- break;
- }
- /* after clearing FDir entries update the remaining */
- ice_fdir_remap_entries(prof, tun, i);
- /* find flow profile corresponding to prof_id and clear
- * vsi_idx from bitmap.
- */
- status = ice_flow_rem_vsi_prof(hw, vsi_idx, prof_id);
- if (status) {
- dev_err(ice_hw_to_dev(hw), "ice_flow_rem_vsi_prof() failed status=%d\n",
- status);
- }
- }
- prof->cnt--;
- }
- }
- /**
- * ice_fdir_get_hw_prof - return the ice_fd_hw_proc associated with a flow
- * @hw: hardware structure containing the filter list
- * @blk: hardware block
- * @flow: FDir flow type to release
- */
- static struct ice_fd_hw_prof *
- ice_fdir_get_hw_prof(struct ice_hw *hw, enum ice_block blk, int flow)
- {
- if (blk == ICE_BLK_FD && hw->fdir_prof)
- return hw->fdir_prof[flow];
- return NULL;
- }
- /**
- * ice_fdir_erase_flow_from_hw - remove a flow from the HW profile tables
- * @hw: hardware structure containing the filter list
- * @blk: hardware block
- * @flow: FDir flow type to release
- */
- static void
- ice_fdir_erase_flow_from_hw(struct ice_hw *hw, enum ice_block blk, int flow)
- {
- struct ice_fd_hw_prof *prof = ice_fdir_get_hw_prof(hw, blk, flow);
- int tun;
- if (!prof)
- return;
- for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
- u64 prof_id = prof->prof_id[tun];
- int j;
- for (j = 0; j < prof->cnt; j++) {
- u16 vsi_num;
- if (!prof->entry_h[j][tun] || !prof->vsi_h[j])
- continue;
- vsi_num = ice_get_hw_vsi_num(hw, prof->vsi_h[j]);
- ice_rem_prof_id_flow(hw, blk, vsi_num, prof_id);
- ice_flow_rem_entry(hw, blk, prof->entry_h[j][tun]);
- prof->entry_h[j][tun] = 0;
- }
- ice_flow_rem_prof(hw, blk, prof_id);
- }
- }
- /**
- * ice_fdir_rem_flow - release the ice_flow structures for a filter type
- * @hw: hardware structure containing the filter list
- * @blk: hardware block
- * @flow_type: FDir flow type to release
- */
- static void
- ice_fdir_rem_flow(struct ice_hw *hw, enum ice_block blk,
- enum ice_fltr_ptype flow_type)
- {
- int flow = (int)flow_type & ~FLOW_EXT;
- struct ice_fd_hw_prof *prof;
- int tun, i;
- prof = ice_fdir_get_hw_prof(hw, blk, flow);
- if (!prof)
- return;
- ice_fdir_erase_flow_from_hw(hw, blk, flow);
- for (i = 0; i < prof->cnt; i++)
- prof->vsi_h[i] = 0;
- for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
- if (!prof->fdir_seg[tun])
- continue;
- devm_kfree(ice_hw_to_dev(hw), prof->fdir_seg[tun]);
- prof->fdir_seg[tun] = NULL;
- }
- prof->cnt = 0;
- }
- /**
- * ice_fdir_release_flows - release all flows in use for later replay
- * @hw: pointer to HW instance
- */
- void ice_fdir_release_flows(struct ice_hw *hw)
- {
- int flow;
- /* release Flow Director HW table entries */
- for (flow = 0; flow < ICE_FLTR_PTYPE_MAX; flow++)
- ice_fdir_erase_flow_from_hw(hw, ICE_BLK_FD, flow);
- }
- /**
- * ice_fdir_replay_flows - replay HW Flow Director filter info
- * @hw: pointer to HW instance
- */
- void ice_fdir_replay_flows(struct ice_hw *hw)
- {
- int flow;
- for (flow = 0; flow < ICE_FLTR_PTYPE_MAX; flow++) {
- int tun;
- if (!hw->fdir_prof[flow] || !hw->fdir_prof[flow]->cnt)
- continue;
- for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
- struct ice_flow_prof *hw_prof;
- struct ice_fd_hw_prof *prof;
- int j;
- prof = hw->fdir_prof[flow];
- ice_flow_add_prof(hw, ICE_BLK_FD, ICE_FLOW_RX,
- prof->fdir_seg[tun], TNL_SEG_CNT(tun),
- false, &hw_prof);
- for (j = 0; j < prof->cnt; j++) {
- enum ice_flow_priority prio;
- u64 entry_h = 0;
- int err;
- prio = ICE_FLOW_PRIO_NORMAL;
- err = ice_flow_add_entry(hw, ICE_BLK_FD,
- hw_prof->id,
- prof->vsi_h[0],
- prof->vsi_h[j],
- prio, prof->fdir_seg,
- &entry_h);
- if (err) {
- dev_err(ice_hw_to_dev(hw), "Could not replay Flow Director, flow type %d\n",
- flow);
- continue;
- }
- prof->prof_id[tun] = hw_prof->id;
- prof->entry_h[j][tun] = entry_h;
- }
- }
- }
- }
- /**
- * ice_parse_rx_flow_user_data - deconstruct user-defined data
- * @fsp: pointer to ethtool Rx flow specification
- * @data: pointer to userdef data structure for storage
- *
- * Returns 0 on success, negative error value on failure
- */
- static int
- ice_parse_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp,
- struct ice_rx_flow_userdef *data)
- {
- u64 value, mask;
- memset(data, 0, sizeof(*data));
- if (!(fsp->flow_type & FLOW_EXT))
- return 0;
- value = be64_to_cpu(*((__force __be64 *)fsp->h_ext.data));
- mask = be64_to_cpu(*((__force __be64 *)fsp->m_ext.data));
- if (!mask)
- return 0;
- #define ICE_USERDEF_FLEX_WORD_M GENMASK_ULL(15, 0)
- #define ICE_USERDEF_FLEX_OFFS_S 16
- #define ICE_USERDEF_FLEX_OFFS_M GENMASK_ULL(31, ICE_USERDEF_FLEX_OFFS_S)
- #define ICE_USERDEF_FLEX_FLTR_M GENMASK_ULL(31, 0)
- /* 0x1fe is the maximum value for offsets stored in the internal
- * filtering tables.
- */
- #define ICE_USERDEF_FLEX_MAX_OFFS_VAL 0x1fe
- if (!ice_is_mask_valid(mask, ICE_USERDEF_FLEX_FLTR_M) ||
- value > ICE_USERDEF_FLEX_FLTR_M)
- return -EINVAL;
- data->flex_word = value & ICE_USERDEF_FLEX_WORD_M;
- data->flex_offset = FIELD_GET(ICE_USERDEF_FLEX_OFFS_M, value);
- if (data->flex_offset > ICE_USERDEF_FLEX_MAX_OFFS_VAL)
- return -EINVAL;
- data->flex_fltr = true;
- return 0;
- }
- /**
- * ice_fdir_num_avail_fltr - return the number of unused flow director filters
- * @hw: pointer to hardware structure
- * @vsi: software VSI structure
- *
- * There are 2 filter pools: guaranteed and best effort(shared). Each VSI can
- * use filters from either pool. The guaranteed pool is divided between VSIs.
- * The best effort filter pool is common to all VSIs and is a device shared
- * resource pool. The number of filters available to this VSI is the sum of
- * the VSIs guaranteed filter pool and the global available best effort
- * filter pool.
- *
- * Returns the number of available flow director filters to this VSI
- */
- int ice_fdir_num_avail_fltr(struct ice_hw *hw, struct ice_vsi *vsi)
- {
- u16 vsi_num = ice_get_hw_vsi_num(hw, vsi->idx);
- u16 num_guar;
- u16 num_be;
- /* total guaranteed filters assigned to this VSI */
- num_guar = vsi->num_gfltr;
- /* total global best effort filters */
- num_be = hw->func_caps.fd_fltr_best_effort;
- /* Subtract the number of programmed filters from the global values */
- switch (hw->mac_type) {
- case ICE_MAC_E830:
- num_guar -= FIELD_GET(E830_VSIQF_FD_CNT_FD_GCNT_M,
- rd32(hw, VSIQF_FD_CNT(vsi_num)));
- num_be -= FIELD_GET(E830_GLQF_FD_CNT_FD_BCNT_M,
- rd32(hw, GLQF_FD_CNT));
- break;
- case ICE_MAC_E810:
- default:
- num_guar -= FIELD_GET(E800_VSIQF_FD_CNT_FD_GCNT_M,
- rd32(hw, VSIQF_FD_CNT(vsi_num)));
- num_be -= FIELD_GET(E800_GLQF_FD_CNT_FD_BCNT_M,
- rd32(hw, GLQF_FD_CNT));
- }
- return num_guar + num_be;
- }
- /**
- * ice_fdir_alloc_flow_prof - allocate FDir flow profile structure(s)
- * @hw: HW structure containing the FDir flow profile structure(s)
- * @flow: flow type to allocate the flow profile for
- *
- * Allocate the fdir_prof and fdir_prof[flow] if not already created. Return 0
- * on success and negative on error.
- */
- static int
- ice_fdir_alloc_flow_prof(struct ice_hw *hw, enum ice_fltr_ptype flow)
- {
- if (!hw)
- return -EINVAL;
- if (!hw->fdir_prof) {
- hw->fdir_prof = devm_kcalloc(ice_hw_to_dev(hw),
- ICE_FLTR_PTYPE_MAX,
- sizeof(*hw->fdir_prof),
- GFP_KERNEL);
- if (!hw->fdir_prof)
- return -ENOMEM;
- }
- if (!hw->fdir_prof[flow]) {
- hw->fdir_prof[flow] = devm_kzalloc(ice_hw_to_dev(hw),
- sizeof(**hw->fdir_prof),
- GFP_KERNEL);
- if (!hw->fdir_prof[flow])
- return -ENOMEM;
- }
- return 0;
- }
- /**
- * ice_fdir_prof_vsi_idx - find or insert a vsi_idx in structure
- * @prof: pointer to flow director HW profile
- * @vsi_idx: vsi_idx to locate
- *
- * return the index of the vsi_idx. if vsi_idx is not found insert it
- * into the vsi_h table.
- */
- static u16
- ice_fdir_prof_vsi_idx(struct ice_fd_hw_prof *prof, int vsi_idx)
- {
- u16 idx = 0;
- for (idx = 0; idx < prof->cnt; idx++)
- if (prof->vsi_h[idx] == vsi_idx)
- return idx;
- if (idx == prof->cnt)
- prof->vsi_h[prof->cnt++] = vsi_idx;
- return idx;
- }
- /**
- * ice_fdir_set_hw_fltr_rule - Configure HW tables to generate a FDir rule
- * @pf: pointer to the PF structure
- * @seg: protocol header description pointer
- * @flow: filter enum
- * @tun: FDir segment to program
- */
- static int
- ice_fdir_set_hw_fltr_rule(struct ice_pf *pf, struct ice_flow_seg_info *seg,
- enum ice_fltr_ptype flow, enum ice_fd_hw_seg tun)
- {
- struct device *dev = ice_pf_to_dev(pf);
- struct ice_vsi *main_vsi, *ctrl_vsi;
- struct ice_flow_seg_info *old_seg;
- struct ice_flow_prof *prof = NULL;
- struct ice_fd_hw_prof *hw_prof;
- struct ice_hw *hw = &pf->hw;
- u64 entry1_h = 0;
- u64 entry2_h = 0;
- bool del_last;
- int err;
- int idx;
- main_vsi = ice_get_main_vsi(pf);
- if (!main_vsi)
- return -EINVAL;
- ctrl_vsi = ice_get_ctrl_vsi(pf);
- if (!ctrl_vsi)
- return -EINVAL;
- err = ice_fdir_alloc_flow_prof(hw, flow);
- if (err)
- return err;
- hw_prof = hw->fdir_prof[flow];
- old_seg = hw_prof->fdir_seg[tun];
- if (old_seg) {
- /* This flow_type already has a changed input set.
- * If it matches the requested input set then we are
- * done. Or, if it's different then it's an error.
- */
- if (!memcmp(old_seg, seg, sizeof(*seg)))
- return -EEXIST;
- /* if there are FDir filters using this flow,
- * then return error.
- */
- if (hw->fdir_fltr_cnt[flow]) {
- dev_err(dev, "Failed to add filter. Flow director filters on each port must have the same input set.\n");
- return -EINVAL;
- }
- if (ice_is_arfs_using_perfect_flow(hw, flow)) {
- dev_err(dev, "aRFS using perfect flow type %d, cannot change input set\n",
- flow);
- return -EINVAL;
- }
- /* remove HW filter definition */
- ice_fdir_rem_flow(hw, ICE_BLK_FD, flow);
- }
- /* Adding a profile, but there is only one header supported.
- * That is the final parameters are 1 header (segment), no
- * actions (NULL) and zero actions 0.
- */
- err = ice_flow_add_prof(hw, ICE_BLK_FD, ICE_FLOW_RX, seg,
- TNL_SEG_CNT(tun), false, &prof);
- if (err)
- return err;
- err = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id, main_vsi->idx,
- main_vsi->idx, ICE_FLOW_PRIO_NORMAL,
- seg, &entry1_h);
- if (err)
- goto err_prof;
- err = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id, main_vsi->idx,
- ctrl_vsi->idx, ICE_FLOW_PRIO_NORMAL,
- seg, &entry2_h);
- if (err)
- goto err_entry;
- hw_prof->fdir_seg[tun] = seg;
- hw_prof->prof_id[tun] = prof->id;
- hw_prof->entry_h[0][tun] = entry1_h;
- hw_prof->entry_h[1][tun] = entry2_h;
- hw_prof->vsi_h[0] = main_vsi->idx;
- hw_prof->vsi_h[1] = ctrl_vsi->idx;
- if (!hw_prof->cnt)
- hw_prof->cnt = 2;
- for (idx = 1; idx < ICE_CHNL_MAX_TC; idx++) {
- u16 vsi_idx;
- u16 vsi_h;
- if (!ice_is_adq_active(pf) || !main_vsi->tc_map_vsi[idx])
- continue;
- entry1_h = 0;
- vsi_h = main_vsi->tc_map_vsi[idx]->idx;
- err = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id,
- main_vsi->idx, vsi_h,
- ICE_FLOW_PRIO_NORMAL, seg,
- &entry1_h);
- if (err) {
- dev_err(dev, "Could not add Channel VSI %d to flow group\n",
- idx);
- goto err_unroll;
- }
- vsi_idx = ice_fdir_prof_vsi_idx(hw_prof,
- main_vsi->tc_map_vsi[idx]->idx);
- hw_prof->entry_h[vsi_idx][tun] = entry1_h;
- }
- return 0;
- err_unroll:
- entry1_h = 0;
- hw_prof->fdir_seg[tun] = NULL;
- /* The variable del_last will be used to determine when to clean up
- * the VSI group data. The VSI data is not needed if there are no
- * segments.
- */
- del_last = true;
- for (idx = 0; idx < ICE_FD_HW_SEG_MAX; idx++)
- if (hw_prof->fdir_seg[idx]) {
- del_last = false;
- break;
- }
- for (idx = 0; idx < hw_prof->cnt; idx++) {
- u16 vsi_num = ice_get_hw_vsi_num(hw, hw_prof->vsi_h[idx]);
- if (!hw_prof->entry_h[idx][tun])
- continue;
- ice_rem_prof_id_flow(hw, ICE_BLK_FD, vsi_num, prof->id);
- ice_flow_rem_entry(hw, ICE_BLK_FD, hw_prof->entry_h[idx][tun]);
- hw_prof->entry_h[idx][tun] = 0;
- if (del_last)
- hw_prof->vsi_h[idx] = 0;
- }
- if (del_last)
- hw_prof->cnt = 0;
- err_entry:
- ice_rem_prof_id_flow(hw, ICE_BLK_FD,
- ice_get_hw_vsi_num(hw, main_vsi->idx), prof->id);
- ice_flow_rem_entry(hw, ICE_BLK_FD, entry1_h);
- err_prof:
- ice_flow_rem_prof(hw, ICE_BLK_FD, prof->id);
- dev_err(dev, "Failed to add filter. Flow director filters on each port must have the same input set.\n");
- return err;
- }
- /**
- * ice_set_init_fdir_seg
- * @seg: flow segment for programming
- * @l3_proto: ICE_FLOW_SEG_HDR_IPV4 or ICE_FLOW_SEG_HDR_IPV6
- * @l4_proto: ICE_FLOW_SEG_HDR_TCP or ICE_FLOW_SEG_HDR_UDP
- *
- * Set the configuration for perfect filters to the provided flow segment for
- * programming the HW filter. This is to be called only when initializing
- * filters as this function it assumes no filters exist.
- */
- static int
- ice_set_init_fdir_seg(struct ice_flow_seg_info *seg,
- enum ice_flow_seg_hdr l3_proto,
- enum ice_flow_seg_hdr l4_proto)
- {
- enum ice_flow_field src_addr, dst_addr, src_port, dst_port;
- if (!seg)
- return -EINVAL;
- if (l3_proto == ICE_FLOW_SEG_HDR_IPV4) {
- src_addr = ICE_FLOW_FIELD_IDX_IPV4_SA;
- dst_addr = ICE_FLOW_FIELD_IDX_IPV4_DA;
- } else if (l3_proto == ICE_FLOW_SEG_HDR_IPV6) {
- src_addr = ICE_FLOW_FIELD_IDX_IPV6_SA;
- dst_addr = ICE_FLOW_FIELD_IDX_IPV6_DA;
- } else {
- return -EINVAL;
- }
- if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
- src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
- } else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
- src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
- } else {
- return -EINVAL;
- }
- ICE_FLOW_SET_HDRS(seg, l3_proto | l4_proto);
- /* IP source address */
- ice_flow_set_fld(seg, src_addr, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
- /* IP destination address */
- ice_flow_set_fld(seg, dst_addr, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
- /* Layer 4 source port */
- ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
- /* Layer 4 destination port */
- ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL, false);
- return 0;
- }
- /**
- * ice_create_init_fdir_rule
- * @pf: PF structure
- * @flow: filter enum
- *
- * Return error value or 0 on success.
- */
- static int
- ice_create_init_fdir_rule(struct ice_pf *pf, enum ice_fltr_ptype flow)
- {
- struct ice_flow_seg_info *seg, *tun_seg;
- struct device *dev = ice_pf_to_dev(pf);
- struct ice_hw *hw = &pf->hw;
- int ret;
- /* if there is already a filter rule for kind return -EINVAL */
- if (hw->fdir_prof && hw->fdir_prof[flow] &&
- hw->fdir_prof[flow]->fdir_seg[0])
- return -EINVAL;
- seg = devm_kzalloc(dev, sizeof(*seg), GFP_KERNEL);
- if (!seg)
- return -ENOMEM;
- tun_seg = devm_kcalloc(dev, ICE_FD_HW_SEG_MAX, sizeof(*tun_seg),
- GFP_KERNEL);
- if (!tun_seg) {
- devm_kfree(dev, seg);
- return -ENOMEM;
- }
- if (flow == ICE_FLTR_PTYPE_NONF_IPV4_TCP)
- ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV4,
- ICE_FLOW_SEG_HDR_TCP);
- else if (flow == ICE_FLTR_PTYPE_NONF_IPV4_UDP)
- ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV4,
- ICE_FLOW_SEG_HDR_UDP);
- else if (flow == ICE_FLTR_PTYPE_NONF_IPV6_TCP)
- ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV6,
- ICE_FLOW_SEG_HDR_TCP);
- else if (flow == ICE_FLTR_PTYPE_NONF_IPV6_UDP)
- ret = ice_set_init_fdir_seg(seg, ICE_FLOW_SEG_HDR_IPV6,
- ICE_FLOW_SEG_HDR_UDP);
- else
- ret = -EINVAL;
- if (ret)
- goto err_exit;
- /* add filter for outer headers */
- ret = ice_fdir_set_hw_fltr_rule(pf, seg, flow, ICE_FD_HW_SEG_NON_TUN);
- if (ret)
- /* could not write filter, free memory */
- goto err_exit;
- /* make tunneled filter HW entries if possible */
- memcpy(&tun_seg[1], seg, sizeof(*seg));
- ret = ice_fdir_set_hw_fltr_rule(pf, tun_seg, flow, ICE_FD_HW_SEG_TUN);
- if (ret)
- /* could not write tunnel filter, but outer header filter
- * exists
- */
- devm_kfree(dev, tun_seg);
- set_bit(flow, hw->fdir_perfect_fltr);
- return ret;
- err_exit:
- devm_kfree(dev, tun_seg);
- devm_kfree(dev, seg);
- return -EOPNOTSUPP;
- }
- /**
- * ice_set_fdir_ip4_seg
- * @seg: flow segment for programming
- * @tcp_ip4_spec: mask data from ethtool
- * @l4_proto: Layer 4 protocol to program
- * @perfect_fltr: only valid on success; returns true if perfect filter,
- * false if not
- *
- * Set the mask data into the flow segment to be used to program HW
- * table based on provided L4 protocol for IPv4
- */
- static int
- ice_set_fdir_ip4_seg(struct ice_flow_seg_info *seg,
- struct ethtool_tcpip4_spec *tcp_ip4_spec,
- enum ice_flow_seg_hdr l4_proto, bool *perfect_fltr)
- {
- enum ice_flow_field src_port, dst_port;
- /* make sure we don't have any empty rule */
- if (!tcp_ip4_spec->psrc && !tcp_ip4_spec->ip4src &&
- !tcp_ip4_spec->pdst && !tcp_ip4_spec->ip4dst)
- return -EINVAL;
- /* filtering on TOS not supported */
- if (tcp_ip4_spec->tos)
- return -EOPNOTSUPP;
- if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
- src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
- } else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
- src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
- } else if (l4_proto == ICE_FLOW_SEG_HDR_SCTP) {
- src_port = ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_SCTP_DST_PORT;
- } else {
- return -EOPNOTSUPP;
- }
- *perfect_fltr = true;
- ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4 | l4_proto);
- /* IP source address */
- if (tcp_ip4_spec->ip4src == htonl(0xFFFFFFFF))
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_SA,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- else if (!tcp_ip4_spec->ip4src)
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- /* IP destination address */
- if (tcp_ip4_spec->ip4dst == htonl(0xFFFFFFFF))
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_DA,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- else if (!tcp_ip4_spec->ip4dst)
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- /* Layer 4 source port */
- if (tcp_ip4_spec->psrc == htons(0xFFFF))
- ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- false);
- else if (!tcp_ip4_spec->psrc)
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- /* Layer 4 destination port */
- if (tcp_ip4_spec->pdst == htons(0xFFFF))
- ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- false);
- else if (!tcp_ip4_spec->pdst)
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- return 0;
- }
- /**
- * ice_set_fdir_ip4_usr_seg
- * @seg: flow segment for programming
- * @usr_ip4_spec: ethtool userdef packet offset
- * @perfect_fltr: only valid on success; returns true if perfect filter,
- * false if not
- *
- * Set the offset data into the flow segment to be used to program HW
- * table for IPv4
- */
- static int
- ice_set_fdir_ip4_usr_seg(struct ice_flow_seg_info *seg,
- struct ethtool_usrip4_spec *usr_ip4_spec,
- bool *perfect_fltr)
- {
- /* first 4 bytes of Layer 4 header */
- if (usr_ip4_spec->l4_4_bytes)
- return -EINVAL;
- if (usr_ip4_spec->tos)
- return -EINVAL;
- if (usr_ip4_spec->ip_ver)
- return -EINVAL;
- /* Filtering on Layer 4 protocol not supported */
- if (usr_ip4_spec->proto)
- return -EOPNOTSUPP;
- /* empty rules are not valid */
- if (!usr_ip4_spec->ip4src && !usr_ip4_spec->ip4dst)
- return -EINVAL;
- *perfect_fltr = true;
- ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4);
- /* IP source address */
- if (usr_ip4_spec->ip4src == htonl(0xFFFFFFFF))
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_SA,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- else if (!usr_ip4_spec->ip4src)
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- /* IP destination address */
- if (usr_ip4_spec->ip4dst == htonl(0xFFFFFFFF))
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_DA,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- else if (!usr_ip4_spec->ip4dst)
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- return 0;
- }
- /**
- * ice_set_fdir_ip6_seg
- * @seg: flow segment for programming
- * @tcp_ip6_spec: mask data from ethtool
- * @l4_proto: Layer 4 protocol to program
- * @perfect_fltr: only valid on success; returns true if perfect filter,
- * false if not
- *
- * Set the mask data into the flow segment to be used to program HW
- * table based on provided L4 protocol for IPv6
- */
- static int
- ice_set_fdir_ip6_seg(struct ice_flow_seg_info *seg,
- struct ethtool_tcpip6_spec *tcp_ip6_spec,
- enum ice_flow_seg_hdr l4_proto, bool *perfect_fltr)
- {
- enum ice_flow_field src_port, dst_port;
- /* make sure we don't have any empty rule */
- if (!memcmp(tcp_ip6_spec->ip6src, &zero_ipv6_addr_mask,
- sizeof(struct in6_addr)) &&
- !memcmp(tcp_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
- sizeof(struct in6_addr)) &&
- !tcp_ip6_spec->psrc && !tcp_ip6_spec->pdst)
- return -EINVAL;
- /* filtering on TC not supported */
- if (tcp_ip6_spec->tclass)
- return -EOPNOTSUPP;
- if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
- src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
- } else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
- src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
- } else if (l4_proto == ICE_FLOW_SEG_HDR_SCTP) {
- src_port = ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_SCTP_DST_PORT;
- } else {
- return -EINVAL;
- }
- *perfect_fltr = true;
- ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV6 | l4_proto);
- if (!memcmp(tcp_ip6_spec->ip6src, &full_ipv6_addr_mask,
- sizeof(struct in6_addr)))
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_SA,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- else if (!memcmp(tcp_ip6_spec->ip6src, &zero_ipv6_addr_mask,
- sizeof(struct in6_addr)))
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- if (!memcmp(tcp_ip6_spec->ip6dst, &full_ipv6_addr_mask,
- sizeof(struct in6_addr)))
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_DA,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- else if (!memcmp(tcp_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
- sizeof(struct in6_addr)))
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- /* Layer 4 source port */
- if (tcp_ip6_spec->psrc == htons(0xFFFF))
- ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- false);
- else if (!tcp_ip6_spec->psrc)
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- /* Layer 4 destination port */
- if (tcp_ip6_spec->pdst == htons(0xFFFF))
- ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- false);
- else if (!tcp_ip6_spec->pdst)
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- return 0;
- }
- /**
- * ice_set_fdir_ip6_usr_seg
- * @seg: flow segment for programming
- * @usr_ip6_spec: ethtool userdef packet offset
- * @perfect_fltr: only valid on success; returns true if perfect filter,
- * false if not
- *
- * Set the offset data into the flow segment to be used to program HW
- * table for IPv6
- */
- static int
- ice_set_fdir_ip6_usr_seg(struct ice_flow_seg_info *seg,
- struct ethtool_usrip6_spec *usr_ip6_spec,
- bool *perfect_fltr)
- {
- /* filtering on Layer 4 bytes not supported */
- if (usr_ip6_spec->l4_4_bytes)
- return -EOPNOTSUPP;
- /* filtering on TC not supported */
- if (usr_ip6_spec->tclass)
- return -EOPNOTSUPP;
- /* filtering on Layer 4 protocol not supported */
- if (usr_ip6_spec->l4_proto)
- return -EOPNOTSUPP;
- /* empty rules are not valid */
- if (!memcmp(usr_ip6_spec->ip6src, &zero_ipv6_addr_mask,
- sizeof(struct in6_addr)) &&
- !memcmp(usr_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
- sizeof(struct in6_addr)))
- return -EINVAL;
- *perfect_fltr = true;
- ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV6);
- if (!memcmp(usr_ip6_spec->ip6src, &full_ipv6_addr_mask,
- sizeof(struct in6_addr)))
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_SA,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- else if (!memcmp(usr_ip6_spec->ip6src, &zero_ipv6_addr_mask,
- sizeof(struct in6_addr)))
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- if (!memcmp(usr_ip6_spec->ip6dst, &full_ipv6_addr_mask,
- sizeof(struct in6_addr)))
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV6_DA,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- else if (!memcmp(usr_ip6_spec->ip6dst, &zero_ipv6_addr_mask,
- sizeof(struct in6_addr)))
- *perfect_fltr = false;
- else
- return -EOPNOTSUPP;
- return 0;
- }
- /**
- * ice_fdir_vlan_valid - validate VLAN data for Flow Director rule
- * @dev: network interface device structure
- * @fsp: pointer to ethtool Rx flow specification
- *
- * Return: true if vlan data is valid, false otherwise
- */
- static bool ice_fdir_vlan_valid(struct device *dev,
- struct ethtool_rx_flow_spec *fsp)
- {
- if (fsp->m_ext.vlan_etype && !eth_type_vlan(fsp->h_ext.vlan_etype))
- return false;
- if (fsp->m_ext.vlan_tci && ntohs(fsp->h_ext.vlan_tci) >= VLAN_N_VID)
- return false;
- /* proto and vlan must have vlan-etype defined */
- if (fsp->m_u.ether_spec.h_proto && fsp->m_ext.vlan_tci &&
- !fsp->m_ext.vlan_etype) {
- dev_warn(dev, "Filter with proto and vlan require also vlan-etype");
- return false;
- }
- return true;
- }
- /**
- * ice_set_ether_flow_seg - set address and protocol segments for ether flow
- * @dev: network interface device structure
- * @seg: flow segment for programming
- * @eth_spec: mask data from ethtool
- *
- * Return: 0 on success and errno in case of error.
- */
- static int ice_set_ether_flow_seg(struct device *dev,
- struct ice_flow_seg_info *seg,
- struct ethhdr *eth_spec)
- {
- ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_ETH);
- /* empty rules are not valid */
- if (is_zero_ether_addr(eth_spec->h_source) &&
- is_zero_ether_addr(eth_spec->h_dest) &&
- !eth_spec->h_proto)
- return -EINVAL;
- /* Ethertype */
- if (eth_spec->h_proto == htons(0xFFFF)) {
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_ETH_TYPE,
- ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- } else if (eth_spec->h_proto) {
- dev_warn(dev, "Only 0x0000 or 0xffff proto mask is allowed for flow-type ether");
- return -EOPNOTSUPP;
- }
- /* Source MAC address */
- if (is_broadcast_ether_addr(eth_spec->h_source))
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_ETH_SA,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- else if (!is_zero_ether_addr(eth_spec->h_source))
- goto err_mask;
- /* Destination MAC address */
- if (is_broadcast_ether_addr(eth_spec->h_dest))
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_ETH_DA,
- ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- else if (!is_zero_ether_addr(eth_spec->h_dest))
- goto err_mask;
- return 0;
- err_mask:
- dev_warn(dev, "Only 00:00:00:00:00:00 or ff:ff:ff:ff:ff:ff MAC address mask is allowed for flow-type ether");
- return -EOPNOTSUPP;
- }
- /**
- * ice_set_fdir_vlan_seg - set vlan segments for ether flow
- * @seg: flow segment for programming
- * @ext_masks: masks for additional RX flow fields
- *
- * Return: 0 on success and errno in case of error.
- */
- static int
- ice_set_fdir_vlan_seg(struct ice_flow_seg_info *seg,
- struct ethtool_flow_ext *ext_masks)
- {
- ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_VLAN);
- if (ext_masks->vlan_etype) {
- if (ext_masks->vlan_etype != htons(0xFFFF))
- return -EOPNOTSUPP;
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_S_VLAN,
- ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- }
- if (ext_masks->vlan_tci) {
- if (ext_masks->vlan_tci != htons(0xFFFF))
- return -EOPNOTSUPP;
- ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_C_VLAN,
- ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL, false);
- }
- return 0;
- }
- /**
- * ice_cfg_fdir_xtrct_seq - Configure extraction sequence for the given filter
- * @pf: PF structure
- * @fsp: pointer to ethtool Rx flow specification
- * @user: user defined data from flow specification
- *
- * Returns 0 on success.
- */
- static int
- ice_cfg_fdir_xtrct_seq(struct ice_pf *pf, struct ethtool_rx_flow_spec *fsp,
- struct ice_rx_flow_userdef *user)
- {
- struct ice_flow_seg_info *seg, *tun_seg;
- struct device *dev = ice_pf_to_dev(pf);
- enum ice_fltr_ptype fltr_idx;
- struct ice_hw *hw = &pf->hw;
- bool perfect_filter = false;
- int ret;
- seg = devm_kzalloc(dev, sizeof(*seg), GFP_KERNEL);
- if (!seg)
- return -ENOMEM;
- tun_seg = devm_kcalloc(dev, ICE_FD_HW_SEG_MAX, sizeof(*tun_seg),
- GFP_KERNEL);
- if (!tun_seg) {
- devm_kfree(dev, seg);
- return -ENOMEM;
- }
- switch (fsp->flow_type & ~FLOW_EXT) {
- case TCP_V4_FLOW:
- ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
- ICE_FLOW_SEG_HDR_TCP,
- &perfect_filter);
- break;
- case UDP_V4_FLOW:
- ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
- ICE_FLOW_SEG_HDR_UDP,
- &perfect_filter);
- break;
- case SCTP_V4_FLOW:
- ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
- ICE_FLOW_SEG_HDR_SCTP,
- &perfect_filter);
- break;
- case IPV4_USER_FLOW:
- ret = ice_set_fdir_ip4_usr_seg(seg, &fsp->m_u.usr_ip4_spec,
- &perfect_filter);
- break;
- case TCP_V6_FLOW:
- ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
- ICE_FLOW_SEG_HDR_TCP,
- &perfect_filter);
- break;
- case UDP_V6_FLOW:
- ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
- ICE_FLOW_SEG_HDR_UDP,
- &perfect_filter);
- break;
- case SCTP_V6_FLOW:
- ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
- ICE_FLOW_SEG_HDR_SCTP,
- &perfect_filter);
- break;
- case IPV6_USER_FLOW:
- ret = ice_set_fdir_ip6_usr_seg(seg, &fsp->m_u.usr_ip6_spec,
- &perfect_filter);
- break;
- case ETHER_FLOW:
- ret = ice_set_ether_flow_seg(dev, seg, &fsp->m_u.ether_spec);
- if (!ret && (fsp->m_ext.vlan_etype || fsp->m_ext.vlan_tci)) {
- if (!ice_fdir_vlan_valid(dev, fsp)) {
- ret = -EINVAL;
- break;
- }
- ret = ice_set_fdir_vlan_seg(seg, &fsp->m_ext);
- }
- break;
- default:
- ret = -EINVAL;
- }
- if (ret)
- goto err_exit;
- /* tunnel segments are shifted up one. */
- memcpy(&tun_seg[1], seg, sizeof(*seg));
- if (user && user->flex_fltr) {
- perfect_filter = false;
- ice_flow_add_fld_raw(seg, user->flex_offset,
- ICE_FLTR_PRGM_FLEX_WORD_SIZE,
- ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL);
- ice_flow_add_fld_raw(&tun_seg[1], user->flex_offset,
- ICE_FLTR_PRGM_FLEX_WORD_SIZE,
- ICE_FLOW_FLD_OFF_INVAL,
- ICE_FLOW_FLD_OFF_INVAL);
- }
- fltr_idx = ice_ethtool_flow_to_fltr(fsp->flow_type & ~FLOW_EXT);
- assign_bit(fltr_idx, hw->fdir_perfect_fltr, perfect_filter);
- /* add filter for outer headers */
- ret = ice_fdir_set_hw_fltr_rule(pf, seg, fltr_idx,
- ICE_FD_HW_SEG_NON_TUN);
- if (ret == -EEXIST) {
- /* Rule already exists, free memory and count as success */
- ret = 0;
- goto err_exit;
- } else if (ret) {
- /* could not write filter, free memory */
- goto err_exit;
- }
- /* make tunneled filter HW entries if possible */
- memcpy(&tun_seg[1], seg, sizeof(*seg));
- ret = ice_fdir_set_hw_fltr_rule(pf, tun_seg, fltr_idx,
- ICE_FD_HW_SEG_TUN);
- if (ret == -EEXIST) {
- /* Rule already exists, free memory and count as success */
- devm_kfree(dev, tun_seg);
- ret = 0;
- } else if (ret) {
- /* could not write tunnel filter, but outer filter exists */
- devm_kfree(dev, tun_seg);
- }
- return ret;
- err_exit:
- devm_kfree(dev, tun_seg);
- devm_kfree(dev, seg);
- return ret;
- }
- /**
- * ice_update_per_q_fltr
- * @vsi: ptr to VSI
- * @q_index: queue index
- * @inc: true to increment or false to decrement per queue filter count
- *
- * This function is used to keep track of per queue sideband filters
- */
- static void ice_update_per_q_fltr(struct ice_vsi *vsi, u32 q_index, bool inc)
- {
- struct ice_rx_ring *rx_ring;
- if (!vsi->num_rxq || q_index >= vsi->num_rxq)
- return;
- rx_ring = vsi->rx_rings[q_index];
- if (!rx_ring || !rx_ring->ch)
- return;
- if (inc)
- atomic_inc(&rx_ring->ch->num_sb_fltr);
- else
- atomic_dec_if_positive(&rx_ring->ch->num_sb_fltr);
- }
- /**
- * ice_fdir_write_fltr - send a flow director filter to the hardware
- * @pf: PF data structure
- * @input: filter structure
- * @add: true adds filter and false removed filter
- * @is_tun: true adds inner filter on tunnel and false outer headers
- *
- * returns 0 on success and negative value on error
- */
- int
- ice_fdir_write_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input, bool add,
- bool is_tun)
- {
- struct device *dev = ice_pf_to_dev(pf);
- struct ice_hw *hw = &pf->hw;
- struct ice_fltr_desc desc;
- struct ice_vsi *ctrl_vsi;
- u8 *pkt, *frag_pkt;
- bool has_frag;
- int err;
- ctrl_vsi = ice_get_ctrl_vsi(pf);
- if (!ctrl_vsi)
- return -EINVAL;
- pkt = devm_kzalloc(dev, ICE_FDIR_MAX_RAW_PKT_SIZE, GFP_KERNEL);
- if (!pkt)
- return -ENOMEM;
- frag_pkt = devm_kzalloc(dev, ICE_FDIR_MAX_RAW_PKT_SIZE, GFP_KERNEL);
- if (!frag_pkt) {
- err = -ENOMEM;
- goto err_free;
- }
- ice_fdir_get_prgm_desc(hw, input, &desc, add);
- err = ice_fdir_get_gen_prgm_pkt(hw, input, pkt, false, is_tun);
- if (err)
- goto err_free_all;
- err = ice_prgm_fdir_fltr(ctrl_vsi, &desc, pkt);
- if (err)
- goto err_free_all;
- /* repeat for fragment packet */
- has_frag = ice_fdir_has_frag(input->flow_type);
- if (has_frag) {
- /* does not return error */
- ice_fdir_get_prgm_desc(hw, input, &desc, add);
- err = ice_fdir_get_gen_prgm_pkt(hw, input, frag_pkt, true,
- is_tun);
- if (err)
- goto err_frag;
- err = ice_prgm_fdir_fltr(ctrl_vsi, &desc, frag_pkt);
- if (err)
- goto err_frag;
- } else {
- devm_kfree(dev, frag_pkt);
- }
- return 0;
- err_free_all:
- devm_kfree(dev, frag_pkt);
- err_free:
- devm_kfree(dev, pkt);
- return err;
- err_frag:
- devm_kfree(dev, frag_pkt);
- return err;
- }
- /**
- * ice_fdir_write_all_fltr - send a flow director filter to the hardware
- * @pf: PF data structure
- * @input: filter structure
- * @add: true adds filter and false removed filter
- *
- * returns 0 on success and negative value on error
- */
- static int
- ice_fdir_write_all_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input,
- bool add)
- {
- u16 port_num;
- int tun;
- for (tun = 0; tun < ICE_FD_HW_SEG_MAX; tun++) {
- bool is_tun = tun == ICE_FD_HW_SEG_TUN;
- int err;
- if (is_tun && !ice_get_open_tunnel_port(&pf->hw, &port_num, TNL_ALL))
- continue;
- err = ice_fdir_write_fltr(pf, input, add, is_tun);
- if (err)
- return err;
- }
- return 0;
- }
- /**
- * ice_fdir_replay_fltrs - replay filters from the HW filter list
- * @pf: board private structure
- */
- void ice_fdir_replay_fltrs(struct ice_pf *pf)
- {
- struct ice_fdir_fltr *f_rule;
- struct ice_hw *hw = &pf->hw;
- list_for_each_entry(f_rule, &hw->fdir_list_head, fltr_node) {
- int err = ice_fdir_write_all_fltr(pf, f_rule, true);
- if (err)
- dev_dbg(ice_pf_to_dev(pf), "Flow Director error %d, could not reprogram filter %d\n",
- err, f_rule->fltr_id);
- }
- }
- /**
- * ice_fdir_create_dflt_rules - create default perfect filters
- * @pf: PF data structure
- *
- * Returns 0 for success or error.
- */
- int ice_fdir_create_dflt_rules(struct ice_pf *pf)
- {
- int err;
- /* Create perfect TCP and UDP rules in hardware. */
- err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV4_TCP);
- if (err)
- return err;
- err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV4_UDP);
- if (err)
- return err;
- err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV6_TCP);
- if (err)
- return err;
- err = ice_create_init_fdir_rule(pf, ICE_FLTR_PTYPE_NONF_IPV6_UDP);
- return err;
- }
- /**
- * ice_fdir_del_all_fltrs - Delete all flow director filters
- * @vsi: the VSI being changed
- *
- * This function needs to be called while holding hw->fdir_fltr_lock
- */
- void ice_fdir_del_all_fltrs(struct ice_vsi *vsi)
- {
- struct ice_fdir_fltr *f_rule, *tmp;
- struct ice_pf *pf = vsi->back;
- struct ice_hw *hw = &pf->hw;
- list_for_each_entry_safe(f_rule, tmp, &hw->fdir_list_head, fltr_node) {
- ice_fdir_write_all_fltr(pf, f_rule, false);
- ice_fdir_update_cntrs(hw, f_rule->flow_type, false);
- list_del(&f_rule->fltr_node);
- devm_kfree(ice_pf_to_dev(pf), f_rule);
- }
- }
- /**
- * ice_vsi_manage_fdir - turn on/off flow director
- * @vsi: the VSI being changed
- * @ena: boolean value indicating if this is an enable or disable request
- */
- void ice_vsi_manage_fdir(struct ice_vsi *vsi, bool ena)
- {
- struct ice_pf *pf = vsi->back;
- struct ice_hw *hw = &pf->hw;
- enum ice_fltr_ptype flow;
- if (ena) {
- set_bit(ICE_FLAG_FD_ENA, pf->flags);
- ice_fdir_create_dflt_rules(pf);
- return;
- }
- mutex_lock(&hw->fdir_fltr_lock);
- if (!test_and_clear_bit(ICE_FLAG_FD_ENA, pf->flags))
- goto release_lock;
- ice_fdir_del_all_fltrs(vsi);
- if (hw->fdir_prof)
- for (flow = ICE_FLTR_PTYPE_NONF_NONE; flow < ICE_FLTR_PTYPE_MAX;
- flow++)
- if (hw->fdir_prof[flow])
- ice_fdir_rem_flow(hw, ICE_BLK_FD, flow);
- release_lock:
- mutex_unlock(&hw->fdir_fltr_lock);
- }
- /**
- * ice_fdir_do_rem_flow - delete flow and possibly add perfect flow
- * @pf: PF structure
- * @flow_type: FDir flow type to release
- */
- static void
- ice_fdir_do_rem_flow(struct ice_pf *pf, enum ice_fltr_ptype flow_type)
- {
- struct ice_hw *hw = &pf->hw;
- bool need_perfect = false;
- if (flow_type == ICE_FLTR_PTYPE_NONF_IPV4_TCP ||
- flow_type == ICE_FLTR_PTYPE_NONF_IPV4_UDP ||
- flow_type == ICE_FLTR_PTYPE_NONF_IPV6_TCP ||
- flow_type == ICE_FLTR_PTYPE_NONF_IPV6_UDP)
- need_perfect = true;
- if (need_perfect && test_bit(flow_type, hw->fdir_perfect_fltr))
- return;
- ice_fdir_rem_flow(hw, ICE_BLK_FD, flow_type);
- if (need_perfect)
- ice_create_init_fdir_rule(pf, flow_type);
- }
- /**
- * ice_fdir_update_list_entry - add or delete a filter from the filter list
- * @pf: PF structure
- * @input: filter structure
- * @fltr_idx: ethtool index of filter to modify
- *
- * returns 0 on success and negative on errors
- */
- static int
- ice_fdir_update_list_entry(struct ice_pf *pf, struct ice_fdir_fltr *input,
- int fltr_idx)
- {
- struct ice_fdir_fltr *old_fltr;
- struct ice_hw *hw = &pf->hw;
- struct ice_vsi *vsi;
- int err = -ENOENT;
- /* Do not update filters during reset */
- if (ice_is_reset_in_progress(pf->state))
- return -EBUSY;
- vsi = ice_get_main_vsi(pf);
- if (!vsi)
- return -EINVAL;
- old_fltr = ice_fdir_find_fltr_by_idx(hw, fltr_idx);
- if (old_fltr) {
- err = ice_fdir_write_all_fltr(pf, old_fltr, false);
- if (err)
- return err;
- ice_fdir_update_cntrs(hw, old_fltr->flow_type, false);
- /* update sb-filters count, specific to ring->channel */
- ice_update_per_q_fltr(vsi, old_fltr->orig_q_index, false);
- if (!input && !hw->fdir_fltr_cnt[old_fltr->flow_type])
- /* we just deleted the last filter of flow_type so we
- * should also delete the HW filter info.
- */
- ice_fdir_do_rem_flow(pf, old_fltr->flow_type);
- list_del(&old_fltr->fltr_node);
- devm_kfree(ice_hw_to_dev(hw), old_fltr);
- }
- if (!input)
- return err;
- ice_fdir_list_add_fltr(hw, input);
- /* update sb-filters count, specific to ring->channel */
- ice_update_per_q_fltr(vsi, input->orig_q_index, true);
- ice_fdir_update_cntrs(hw, input->flow_type, true);
- return 0;
- }
- /**
- * ice_del_fdir_ethtool - delete Flow Director filter
- * @vsi: pointer to target VSI
- * @cmd: command to add or delete Flow Director filter
- *
- * Returns 0 on success and negative values for failure
- */
- int ice_del_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
- {
- struct ethtool_rx_flow_spec *fsp =
- (struct ethtool_rx_flow_spec *)&cmd->fs;
- struct ice_pf *pf = vsi->back;
- struct ice_hw *hw = &pf->hw;
- int val;
- if (!test_bit(ICE_FLAG_FD_ENA, pf->flags))
- return -EOPNOTSUPP;
- /* Do not delete filters during reset */
- if (ice_is_reset_in_progress(pf->state)) {
- dev_err(ice_pf_to_dev(pf), "Device is resetting - deleting Flow Director filters not supported during reset\n");
- return -EBUSY;
- }
- if (test_bit(ICE_FD_FLUSH_REQ, pf->state))
- return -EBUSY;
- mutex_lock(&hw->fdir_fltr_lock);
- val = ice_fdir_update_list_entry(pf, NULL, fsp->location);
- mutex_unlock(&hw->fdir_fltr_lock);
- return val;
- }
- /**
- * ice_update_ring_dest_vsi - update dest ring and dest VSI
- * @vsi: pointer to target VSI
- * @dest_vsi: ptr to dest VSI index
- * @ring: ptr to dest ring
- *
- * This function updates destination VSI and queue if user specifies
- * target queue which falls in channel's (aka ADQ) queue region
- */
- static void
- ice_update_ring_dest_vsi(struct ice_vsi *vsi, u16 *dest_vsi, u32 *ring)
- {
- struct ice_channel *ch;
- list_for_each_entry(ch, &vsi->ch_list, list) {
- if (!ch->ch_vsi)
- continue;
- /* make sure to locate corresponding channel based on "queue"
- * specified
- */
- if ((*ring < ch->base_q) ||
- (*ring >= (ch->base_q + ch->num_rxq)))
- continue;
- /* update the dest_vsi based on channel */
- *dest_vsi = ch->ch_vsi->idx;
- /* update the "ring" to be correct based on channel */
- *ring -= ch->base_q;
- }
- }
- /**
- * ice_set_fdir_input_set - Set the input set for Flow Director
- * @vsi: pointer to target VSI
- * @fsp: pointer to ethtool Rx flow specification
- * @input: filter structure
- */
- static int
- ice_set_fdir_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
- struct ice_fdir_fltr *input)
- {
- s16 q_index = ICE_FDIR_NO_QUEUE_IDX;
- u16 orig_q_index = 0;
- struct ice_pf *pf;
- struct ice_hw *hw;
- int flow_type;
- u16 dest_vsi;
- u8 dest_ctl;
- if (!vsi || !fsp || !input)
- return -EINVAL;
- pf = vsi->back;
- hw = &pf->hw;
- dest_vsi = vsi->idx;
- if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
- dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DROP_PKT;
- } else {
- u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie);
- u8 vf = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie);
- if (vf) {
- dev_err(ice_pf_to_dev(pf), "Failed to add filter. Flow director filters are not supported on VF queues.\n");
- return -EINVAL;
- }
- if (ring >= vsi->num_rxq)
- return -EINVAL;
- orig_q_index = ring;
- ice_update_ring_dest_vsi(vsi, &dest_vsi, &ring);
- dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DIRECT_PKT_QINDEX;
- q_index = ring;
- }
- input->fltr_id = fsp->location;
- input->q_index = q_index;
- flow_type = fsp->flow_type & ~FLOW_EXT;
- /* Record the original queue index as specified by user.
- * with channel configuration 'q_index' becomes relative
- * to TC (channel).
- */
- input->orig_q_index = orig_q_index;
- input->dest_vsi = dest_vsi;
- input->dest_ctl = dest_ctl;
- input->fltr_status = ICE_FLTR_PRGM_DESC_FD_STATUS_FD_ID;
- input->cnt_index = ICE_FD_SB_STAT_IDX(hw->fd_ctr_base);
- input->flow_type = ice_ethtool_flow_to_fltr(flow_type);
- if (fsp->flow_type & FLOW_EXT) {
- memcpy(input->ext_data.usr_def, fsp->h_ext.data,
- sizeof(input->ext_data.usr_def));
- input->ext_data.vlan_type = fsp->h_ext.vlan_etype;
- input->ext_data.vlan_tag = fsp->h_ext.vlan_tci;
- memcpy(input->ext_mask.usr_def, fsp->m_ext.data,
- sizeof(input->ext_mask.usr_def));
- input->ext_mask.vlan_type = fsp->m_ext.vlan_etype;
- input->ext_mask.vlan_tag = fsp->m_ext.vlan_tci;
- }
- switch (flow_type) {
- case TCP_V4_FLOW:
- case UDP_V4_FLOW:
- case SCTP_V4_FLOW:
- input->ip.v4.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
- input->ip.v4.src_port = fsp->h_u.tcp_ip4_spec.psrc;
- input->ip.v4.dst_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
- input->ip.v4.src_ip = fsp->h_u.tcp_ip4_spec.ip4src;
- input->mask.v4.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
- input->mask.v4.src_port = fsp->m_u.tcp_ip4_spec.psrc;
- input->mask.v4.dst_ip = fsp->m_u.tcp_ip4_spec.ip4dst;
- input->mask.v4.src_ip = fsp->m_u.tcp_ip4_spec.ip4src;
- break;
- case IPV4_USER_FLOW:
- input->ip.v4.dst_ip = fsp->h_u.usr_ip4_spec.ip4dst;
- input->ip.v4.src_ip = fsp->h_u.usr_ip4_spec.ip4src;
- input->ip.v4.l4_header = fsp->h_u.usr_ip4_spec.l4_4_bytes;
- input->ip.v4.proto = fsp->h_u.usr_ip4_spec.proto;
- input->ip.v4.ip_ver = fsp->h_u.usr_ip4_spec.ip_ver;
- input->ip.v4.tos = fsp->h_u.usr_ip4_spec.tos;
- input->mask.v4.dst_ip = fsp->m_u.usr_ip4_spec.ip4dst;
- input->mask.v4.src_ip = fsp->m_u.usr_ip4_spec.ip4src;
- input->mask.v4.l4_header = fsp->m_u.usr_ip4_spec.l4_4_bytes;
- input->mask.v4.proto = fsp->m_u.usr_ip4_spec.proto;
- input->mask.v4.ip_ver = fsp->m_u.usr_ip4_spec.ip_ver;
- input->mask.v4.tos = fsp->m_u.usr_ip4_spec.tos;
- break;
- case TCP_V6_FLOW:
- case UDP_V6_FLOW:
- case SCTP_V6_FLOW:
- memcpy(input->ip.v6.dst_ip, fsp->h_u.usr_ip6_spec.ip6dst,
- sizeof(struct in6_addr));
- memcpy(input->ip.v6.src_ip, fsp->h_u.usr_ip6_spec.ip6src,
- sizeof(struct in6_addr));
- input->ip.v6.dst_port = fsp->h_u.tcp_ip6_spec.pdst;
- input->ip.v6.src_port = fsp->h_u.tcp_ip6_spec.psrc;
- input->ip.v6.tc = fsp->h_u.tcp_ip6_spec.tclass;
- memcpy(input->mask.v6.dst_ip, fsp->m_u.tcp_ip6_spec.ip6dst,
- sizeof(struct in6_addr));
- memcpy(input->mask.v6.src_ip, fsp->m_u.tcp_ip6_spec.ip6src,
- sizeof(struct in6_addr));
- input->mask.v6.dst_port = fsp->m_u.tcp_ip6_spec.pdst;
- input->mask.v6.src_port = fsp->m_u.tcp_ip6_spec.psrc;
- input->mask.v6.tc = fsp->m_u.tcp_ip6_spec.tclass;
- break;
- case IPV6_USER_FLOW:
- memcpy(input->ip.v6.dst_ip, fsp->h_u.usr_ip6_spec.ip6dst,
- sizeof(struct in6_addr));
- memcpy(input->ip.v6.src_ip, fsp->h_u.usr_ip6_spec.ip6src,
- sizeof(struct in6_addr));
- input->ip.v6.l4_header = fsp->h_u.usr_ip6_spec.l4_4_bytes;
- input->ip.v6.tc = fsp->h_u.usr_ip6_spec.tclass;
- /* if no protocol requested, use IPPROTO_NONE */
- if (!fsp->m_u.usr_ip6_spec.l4_proto)
- input->ip.v6.proto = IPPROTO_NONE;
- else
- input->ip.v6.proto = fsp->h_u.usr_ip6_spec.l4_proto;
- memcpy(input->mask.v6.dst_ip, fsp->m_u.usr_ip6_spec.ip6dst,
- sizeof(struct in6_addr));
- memcpy(input->mask.v6.src_ip, fsp->m_u.usr_ip6_spec.ip6src,
- sizeof(struct in6_addr));
- input->mask.v6.l4_header = fsp->m_u.usr_ip6_spec.l4_4_bytes;
- input->mask.v6.tc = fsp->m_u.usr_ip6_spec.tclass;
- input->mask.v6.proto = fsp->m_u.usr_ip6_spec.l4_proto;
- break;
- case ETHER_FLOW:
- input->eth = fsp->h_u.ether_spec;
- input->eth_mask = fsp->m_u.ether_spec;
- break;
- default:
- /* not doing un-parsed flow types */
- return -EINVAL;
- }
- return 0;
- }
- /**
- * ice_add_fdir_ethtool - Add/Remove Flow Director filter
- * @vsi: pointer to target VSI
- * @cmd: command to add or delete Flow Director filter
- *
- * Returns 0 on success and negative values for failure
- */
- int ice_add_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
- {
- struct ice_rx_flow_userdef userdata;
- struct ethtool_rx_flow_spec *fsp;
- struct ice_fdir_fltr *input;
- struct device *dev;
- struct ice_pf *pf;
- struct ice_hw *hw;
- int fltrs_needed;
- u32 max_location;
- u16 tunnel_port;
- int ret;
- if (!vsi)
- return -EINVAL;
- pf = vsi->back;
- hw = &pf->hw;
- dev = ice_pf_to_dev(pf);
- if (!test_bit(ICE_FLAG_FD_ENA, pf->flags))
- return -EOPNOTSUPP;
- /* Do not program filters during reset */
- if (ice_is_reset_in_progress(pf->state)) {
- dev_err(dev, "Device is resetting - adding Flow Director filters not supported during reset\n");
- return -EBUSY;
- }
- fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
- if (ice_parse_rx_flow_user_data(fsp, &userdata))
- return -EINVAL;
- if (fsp->flow_type & FLOW_MAC_EXT)
- return -EINVAL;
- ret = ice_cfg_fdir_xtrct_seq(pf, fsp, &userdata);
- if (ret)
- return ret;
- max_location = ice_get_fdir_cnt_all(hw);
- if (fsp->location >= max_location) {
- dev_err(dev, "Failed to add filter. The number of ntuple filters or provided location exceed max %d.\n",
- max_location);
- return -ENOSPC;
- }
- /* return error if not an update and no available filters */
- fltrs_needed = ice_get_open_tunnel_port(hw, &tunnel_port, TNL_ALL) ? 2 : 1;
- if (!ice_fdir_find_fltr_by_idx(hw, fsp->location) &&
- ice_fdir_num_avail_fltr(hw, pf->vsi[vsi->idx]) < fltrs_needed) {
- dev_err(dev, "Failed to add filter. The maximum number of flow director filters has been reached.\n");
- return -ENOSPC;
- }
- input = devm_kzalloc(dev, sizeof(*input), GFP_KERNEL);
- if (!input)
- return -ENOMEM;
- ret = ice_set_fdir_input_set(vsi, fsp, input);
- if (ret)
- goto free_input;
- mutex_lock(&hw->fdir_fltr_lock);
- if (ice_fdir_is_dup_fltr(hw, input)) {
- ret = -EINVAL;
- goto release_lock;
- }
- if (userdata.flex_fltr) {
- input->flex_fltr = true;
- input->flex_word = cpu_to_be16(userdata.flex_word);
- input->flex_offset = userdata.flex_offset;
- }
- input->cnt_ena = ICE_FXD_FLTR_QW0_STAT_ENA_PKTS;
- input->fdid_prio = ICE_FXD_FLTR_QW1_FDID_PRI_THREE;
- input->comp_report = ICE_FXD_FLTR_QW0_COMP_REPORT_SW_FAIL;
- /* input struct is added to the HW filter list */
- ret = ice_fdir_update_list_entry(pf, input, fsp->location);
- if (ret)
- goto release_lock;
- ret = ice_fdir_write_all_fltr(pf, input, true);
- if (ret)
- goto remove_sw_rule;
- goto release_lock;
- remove_sw_rule:
- ice_fdir_update_cntrs(hw, input->flow_type, false);
- /* update sb-filters count, specific to ring->channel */
- ice_update_per_q_fltr(vsi, input->orig_q_index, false);
- list_del(&input->fltr_node);
- release_lock:
- mutex_unlock(&hw->fdir_fltr_lock);
- free_input:
- if (ret)
- devm_kfree(dev, input);
- return ret;
- }
|