X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=drivers%2Fnet%2Fbnxt%2Fbnxt_flow.c;h=19bc66a5a20c96505e35c68e2c639071fb129f5e;hb=5ff00cf988ab6f2888239d090b7e982c6af8c6c7;hp=af86be63b8be1f038c68687a2a3c405d683ab3e5;hpb=d24610f7bfdaa4eb12ca1aed04d60a73b8ea7745;p=dpdk.git diff --git a/drivers/net/bnxt/bnxt_flow.c b/drivers/net/bnxt/bnxt_flow.c index af86be63b8..19bc66a5a2 100644 --- a/drivers/net/bnxt/bnxt_flow.c +++ b/drivers/net/bnxt/bnxt_flow.c @@ -10,14 +10,16 @@ #include #include #include +#include +#include #include "bnxt.h" #include "bnxt_filter.h" #include "bnxt_hwrm.h" #include "bnxt_ring.h" #include "bnxt_rxq.h" +#include "bnxt_rxr.h" #include "bnxt_vnic.h" -#include "bnxt_util.h" #include "hsi_struct_def_dpdk.h" static int @@ -78,19 +80,22 @@ bnxt_flow_non_void_action(const struct rte_flow_action *cur) static int bnxt_filter_type_check(const struct rte_flow_item pattern[], - struct rte_flow_error *error __rte_unused) + struct rte_flow_error *error) { const struct rte_flow_item *item = bnxt_flow_non_void_item(pattern); int use_ntuple = 1; + bool has_vlan = 0; while (item->type != RTE_FLOW_ITEM_TYPE_END) { switch (item->type) { + case RTE_FLOW_ITEM_TYPE_ANY: case RTE_FLOW_ITEM_TYPE_ETH: - use_ntuple = 1; + use_ntuple = 0; break; case RTE_FLOW_ITEM_TYPE_VLAN: use_ntuple = 0; + has_vlan = 1; break; case RTE_FLOW_ITEM_TYPE_IPV4: case RTE_FLOW_ITEM_TYPE_IPV6: @@ -98,28 +103,25 @@ bnxt_filter_type_check(const struct rte_flow_item pattern[], case RTE_FLOW_ITEM_TYPE_UDP: /* FALLTHROUGH */ /* need ntuple match, reset exact match */ - if (!use_ntuple) { - PMD_DRV_LOG(ERR, - "VLAN flow cannot use NTUPLE filter\n"); - rte_flow_error_set - (error, - EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, - "Cannot use VLAN with NTUPLE"); - return -rte_errno; - } use_ntuple |= 1; break; - case RTE_FLOW_ITEM_TYPE_ANY: - use_ntuple = 0; - break; default: PMD_DRV_LOG(DEBUG, "Unknown Flow type\n"); use_ntuple |= 0; } item++; } + + if (has_vlan && use_ntuple) { + PMD_DRV_LOG(ERR, + "VLAN flow cannot use NTUPLE filter\n"); + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Cannot use VLAN with NTUPLE"); + return -rte_errno; + } + return use_ntuple; } @@ -137,6 +139,7 @@ bnxt_validate_and_parse_flow_type(struct bnxt *bp, const struct rte_flow_item_tcp *tcp_spec, *tcp_mask; const struct rte_flow_item_udp *udp_spec, *udp_mask; const struct rte_flow_item_eth *eth_spec, *eth_mask; + const struct rte_ether_addr *dst, *src; const struct rte_flow_item_nvgre *nvgre_spec; const struct rte_flow_item_nvgre *nvgre_mask; const struct rte_flow_item_gre *gre_spec; @@ -146,22 +149,23 @@ bnxt_validate_and_parse_flow_type(struct bnxt *bp, uint8_t vni_mask[] = {0xFF, 0xFF, 0xFF}; uint8_t tni_mask[] = {0xFF, 0xFF, 0xFF}; const struct rte_flow_item_vf *vf_spec; - uint32_t tenant_id_be = 0; + uint32_t tenant_id_be = 0, valid_flags = 0; bool vni_masked = 0; bool tni_masked = 0; + uint32_t en_ethertype; + uint8_t inner = 0; uint32_t vf = 0; - int use_ntuple; uint32_t en = 0; - uint32_t en_ethertype; + int use_ntuple; int dflt_vnic; use_ntuple = bnxt_filter_type_check(pattern, error); - PMD_DRV_LOG(DEBUG, "Use NTUPLE %d\n", use_ntuple); if (use_ntuple < 0) return use_ntuple; + PMD_DRV_LOG(DEBUG, "Use NTUPLE %d\n", use_ntuple); filter->filter_type = use_ntuple ? - HWRM_CFA_NTUPLE_FILTER : HWRM_CFA_EM_FILTER; + HWRM_CFA_NTUPLE_FILTER : HWRM_CFA_L2_FILTER; en_ethertype = use_ntuple ? NTUPLE_FLTR_ALLOC_INPUT_EN_ETHERTYPE : EM_FLOW_ALLOC_INPUT_EN_ETHERTYPE; @@ -177,6 +181,12 @@ bnxt_validate_and_parse_flow_type(struct bnxt *bp, } switch (item->type) { + case RTE_FLOW_ITEM_TYPE_ANY: + inner = + ((const struct rte_flow_item_any *)item->spec)->num > 3; + if (inner) + PMD_DRV_LOG(DEBUG, "Parse inner header\n"); + break; case RTE_FLOW_ITEM_TYPE_ETH: if (!item->spec || !item->mask) break; @@ -212,19 +222,49 @@ bnxt_validate_and_parse_flow_type(struct bnxt *bp, } if (rte_is_broadcast_ether_addr(ð_mask->dst)) { + dst = ð_spec->dst; + if (!rte_is_valid_assigned_ether_addr(dst)) { + rte_flow_error_set(error, + EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "DMAC is invalid"); + PMD_DRV_LOG(ERR, + "DMAC is invalid!\n"); + return -rte_errno; + } rte_memcpy(filter->dst_macaddr, - ð_spec->dst, 6); + ð_spec->dst, RTE_ETHER_ADDR_LEN); en |= use_ntuple ? NTUPLE_FLTR_ALLOC_INPUT_EN_DST_MACADDR : EM_FLOW_ALLOC_INPUT_EN_DST_MACADDR; + valid_flags |= inner ? + BNXT_FLOW_L2_INNER_DST_VALID_FLAG : + BNXT_FLOW_L2_DST_VALID_FLAG; + filter->priority = attr->priority; + PMD_DRV_LOG(DEBUG, + "Creating a priority flow\n"); } - if (rte_is_broadcast_ether_addr(ð_mask->src)) { + src = ð_spec->src; + if (!rte_is_valid_assigned_ether_addr(src)) { + rte_flow_error_set(error, + EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "SMAC is invalid"); + PMD_DRV_LOG(ERR, + "SMAC is invalid!\n"); + return -rte_errno; + } rte_memcpy(filter->src_macaddr, - ð_spec->src, 6); + ð_spec->src, RTE_ETHER_ADDR_LEN); en |= use_ntuple ? NTUPLE_FLTR_ALLOC_INPUT_EN_SRC_MACADDR : EM_FLOW_ALLOC_INPUT_EN_SRC_MACADDR; + valid_flags |= inner ? + BNXT_FLOW_L2_INNER_SRC_VALID_FLAG : + BNXT_FLOW_L2_SRC_VALID_FLAG; } /* * else { * PMD_DRV_LOG(ERR, "Handle this condition\n"); @@ -235,6 +275,8 @@ bnxt_validate_and_parse_flow_type(struct bnxt *bp, rte_be_to_cpu_16(eth_spec->type); en |= en_ethertype; } + if (inner) + valid_flags |= BNXT_FLOW_PARSE_INNER_FLAG; break; case RTE_FLOW_ITEM_TYPE_VLAN: @@ -669,6 +711,7 @@ bnxt_validate_and_parse_flow_type(struct bnxt *bp, item++; } filter->enables = en; + filter->valid_flags = valid_flags; return 0; } @@ -698,44 +741,129 @@ bnxt_flow_parse_attr(const struct rte_flow_attr *attr, return -rte_errno; } - /* Not supported */ - if (attr->priority) { - rte_flow_error_set(error, - EINVAL, - RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, - attr, - "No support for priority."); - return -rte_errno; - } return 0; } -struct bnxt_filter_info * -bnxt_get_l2_filter(struct bnxt *bp, struct bnxt_filter_info *nf, - struct bnxt_vnic_info *vnic) +static struct bnxt_filter_info * +bnxt_find_matching_l2_filter(struct bnxt *bp, struct bnxt_filter_info *nf) { - struct bnxt_filter_info *filter1, *f0; + struct bnxt_filter_info *mf, *f0; struct bnxt_vnic_info *vnic0; - int rc; + int i; - vnic0 = &bp->vnic_info[0]; + vnic0 = BNXT_GET_DEFAULT_VNIC(bp); f0 = STAILQ_FIRST(&vnic0->filter); /* This flow has same DST MAC as the port/l2 filter. */ if (memcmp(f0->l2_addr, nf->dst_macaddr, RTE_ETHER_ADDR_LEN) == 0) return f0; - /* This flow needs DST MAC which is not same as port/l2 */ - PMD_DRV_LOG(DEBUG, "Create L2 filter for DST MAC\n"); + for (i = bp->max_vnics - 1; i >= 0; i--) { + struct bnxt_vnic_info *vnic = &bp->vnic_info[i]; + + if (vnic->fw_vnic_id == INVALID_VNIC_ID) + continue; + + STAILQ_FOREACH(mf, &vnic->filter, next) { + + if (mf->matching_l2_fltr_ptr) + continue; + + if (mf->ethertype == nf->ethertype && + mf->l2_ovlan == nf->l2_ovlan && + mf->l2_ovlan_mask == nf->l2_ovlan_mask && + mf->l2_ivlan == nf->l2_ivlan && + mf->l2_ivlan_mask == nf->l2_ivlan_mask && + !memcmp(mf->src_macaddr, nf->src_macaddr, + RTE_ETHER_ADDR_LEN) && + !memcmp(mf->dst_macaddr, nf->dst_macaddr, + RTE_ETHER_ADDR_LEN)) + return mf; + } + } + return NULL; +} + +static struct bnxt_filter_info * +bnxt_create_l2_filter(struct bnxt *bp, struct bnxt_filter_info *nf, + struct bnxt_vnic_info *vnic) +{ + struct bnxt_filter_info *filter1; + int rc; + + /* Alloc new L2 filter. + * This flow needs MAC filter which does not match any existing + * L2 filters. + */ filter1 = bnxt_get_unused_filter(bp); if (filter1 == NULL) return NULL; - filter1->flags = HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_PATH_RX; - filter1->enables = HWRM_CFA_L2_FILTER_ALLOC_INPUT_ENABLES_L2_ADDR | + memcpy(filter1, nf, sizeof(*filter1)); + + filter1->flags = HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_XDP_DISABLE; + filter1->flags |= HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_PATH_RX; + if (nf->valid_flags & BNXT_FLOW_L2_SRC_VALID_FLAG || + nf->valid_flags & BNXT_FLOW_L2_DST_VALID_FLAG) { + filter1->flags |= + HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_OUTERMOST; + PMD_DRV_LOG(DEBUG, "Create Outer filter\n"); + } + + if (nf->filter_type == HWRM_CFA_L2_FILTER && + (nf->valid_flags & BNXT_FLOW_L2_SRC_VALID_FLAG || + nf->valid_flags & BNXT_FLOW_L2_INNER_SRC_VALID_FLAG)) { + PMD_DRV_LOG(DEBUG, "Create L2 filter for SRC MAC\n"); + filter1->flags |= + HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_SOURCE_VALID; + memcpy(filter1->l2_addr, nf->src_macaddr, RTE_ETHER_ADDR_LEN); + } else { + PMD_DRV_LOG(DEBUG, "Create L2 filter for DST MAC\n"); + memcpy(filter1->l2_addr, nf->dst_macaddr, RTE_ETHER_ADDR_LEN); + } + + if (nf->priority && + (nf->valid_flags & BNXT_FLOW_L2_DST_VALID_FLAG || + nf->valid_flags & BNXT_FLOW_L2_INNER_DST_VALID_FLAG)) { + /* Tell the FW where to place the filter in the table. */ + if (nf->priority > 65535) { + filter1->pri_hint = + HWRM_CFA_L2_FILTER_ALLOC_INPUT_PRI_HINT_BELOW_FILTER; + /* This will place the filter in TCAM */ + filter1->l2_filter_id_hint = (uint64_t)-1; + } + } + + if (nf->valid_flags & (BNXT_FLOW_L2_DST_VALID_FLAG | + BNXT_FLOW_L2_SRC_VALID_FLAG | + BNXT_FLOW_L2_INNER_SRC_VALID_FLAG | + BNXT_FLOW_L2_INNER_DST_VALID_FLAG)) { + filter1->enables = + HWRM_CFA_L2_FILTER_ALLOC_INPUT_ENABLES_L2_ADDR | L2_FILTER_ALLOC_INPUT_EN_L2_ADDR_MASK; - memcpy(filter1->l2_addr, nf->dst_macaddr, RTE_ETHER_ADDR_LEN); - memset(filter1->l2_addr_mask, 0xff, RTE_ETHER_ADDR_LEN); + memset(filter1->l2_addr_mask, 0xff, RTE_ETHER_ADDR_LEN); + } + + if (nf->valid_flags & BNXT_FLOW_L2_DROP_FLAG) { + filter1->flags |= + HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_DROP; + if (nf->ethertype == RTE_ETHER_TYPE_IPV4) { + /* Num VLANs for drop filter will/should be 0. + * If the req is memset to 0, then the count will + * be automatically set to 0. + */ + if (nf->valid_flags & BNXT_FLOW_PARSE_INNER_FLAG) { + filter1->enables |= + L2_FILTER_ALLOC_INPUT_EN_T_NUM_VLANS; + } else { + filter1->enables |= + L2_FILTER_ALLOC_INPUT_EN_NUM_VLANS; + filter1->flags |= + HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_OUTERMOST; + } + } + } + rc = bnxt_hwrm_set_l2_filter(bp, vnic->fw_vnic_id, filter1); if (rc) { @@ -745,6 +873,27 @@ bnxt_get_l2_filter(struct bnxt *bp, struct bnxt_filter_info *nf, return filter1; } +struct bnxt_filter_info * +bnxt_get_l2_filter(struct bnxt *bp, struct bnxt_filter_info *nf, + struct bnxt_vnic_info *vnic) +{ + struct bnxt_filter_info *l2_filter = NULL; + + l2_filter = bnxt_find_matching_l2_filter(bp, nf); + if (l2_filter) { + l2_filter->l2_ref_cnt++; + } else { + l2_filter = bnxt_create_l2_filter(bp, nf, vnic); + if (l2_filter) { + STAILQ_INSERT_TAIL(&vnic->filter, l2_filter, next); + l2_filter->vnic = vnic; + } + } + nf->matching_l2_fltr_ptr = l2_filter; + + return l2_filter; +} + static int bnxt_vnic_prep(struct bnxt *bp, struct bnxt_vnic_info *vnic) { struct rte_eth_conf *dev_conf = &bp->eth_dev->data->dev_conf; @@ -789,6 +938,70 @@ ret: return rc; } +static int match_vnic_rss_cfg(struct bnxt *bp, + struct bnxt_vnic_info *vnic, + const struct rte_flow_action_rss *rss) +{ + unsigned int match = 0, i; + + if (vnic->rx_queue_cnt != rss->queue_num) + return -EINVAL; + + for (i = 0; i < rss->queue_num; i++) { + if (!bp->rx_queues[rss->queue[i]]->vnic->rx_queue_cnt && + !bp->rx_queues[rss->queue[i]]->rx_started) + return -EINVAL; + } + + for (i = 0; i < vnic->rx_queue_cnt; i++) { + int j; + + for (j = 0; j < vnic->rx_queue_cnt; j++) { + if (bp->grp_info[rss->queue[i]].fw_grp_id == + vnic->fw_grp_ids[j]) + match++; + } + } + + if (match != vnic->rx_queue_cnt) { + PMD_DRV_LOG(ERR, + "VNIC queue count %d vs queues matched %d\n", + match, vnic->rx_queue_cnt); + return -EINVAL; + } + + return 0; +} + +static void +bnxt_update_filter_flags_en(struct bnxt_filter_info *filter, + struct bnxt_filter_info *filter1, + int use_ntuple) +{ + if (!use_ntuple && + !(filter->valid_flags & + ~(BNXT_FLOW_L2_DST_VALID_FLAG | + BNXT_FLOW_L2_SRC_VALID_FLAG | + BNXT_FLOW_L2_INNER_SRC_VALID_FLAG | + BNXT_FLOW_L2_INNER_DST_VALID_FLAG | + BNXT_FLOW_L2_DROP_FLAG | + BNXT_FLOW_PARSE_INNER_FLAG))) { + filter->flags = filter1->flags; + filter->enables = filter1->enables; + filter->filter_type = HWRM_CFA_L2_FILTER; + memcpy(filter->l2_addr, filter1->l2_addr, RTE_ETHER_ADDR_LEN); + memset(filter->l2_addr_mask, 0xff, RTE_ETHER_ADDR_LEN); + filter->pri_hint = filter1->pri_hint; + filter->l2_filter_id_hint = filter1->l2_filter_id_hint; + } + filter->fw_l2_filter_id = filter1->fw_l2_filter_id; + filter->l2_ref_cnt = filter1->l2_ref_cnt; + filter->flow_id = filter1->flow_id; + PMD_DRV_LOG(DEBUG, + "l2_filter: %p fw_l2_filter_id %" PRIx64 " l2_ref_cnt %u\n", + filter1, filter->fw_l2_filter_id, filter->l2_ref_cnt); +} + static int bnxt_validate_and_parse_flow(struct rte_eth_dev *dev, const struct rte_flow_item pattern[], @@ -801,14 +1014,16 @@ bnxt_validate_and_parse_flow(struct rte_eth_dev *dev, bnxt_flow_non_void_action(actions); struct bnxt *bp = dev->data->dev_private; struct rte_eth_conf *dev_conf = &bp->eth_dev->data->dev_conf; + struct bnxt_vnic_info *vnic = NULL, *vnic0 = NULL; const struct rte_flow_action_queue *act_q; const struct rte_flow_action_vf *act_vf; - struct bnxt_vnic_info *vnic, *vnic0; - struct bnxt_filter_info *filter1; + struct bnxt_filter_info *filter1 = NULL; + const struct rte_flow_action_rss *rss; struct bnxt_rx_queue *rxq = NULL; int dflt_vnic, vnic_id; - uint32_t vf = 0; - int rc; + unsigned int rss_idx; + uint32_t vf = 0, i; + int rc, use_ntuple; rc = bnxt_validate_and_parse_flow_type(bp, attr, pattern, error, filter); @@ -823,6 +1038,9 @@ bnxt_validate_and_parse_flow(struct rte_eth_dev *dev, if (filter->filter_type == HWRM_CFA_EM_FILTER) filter->flags = HWRM_CFA_EM_FLOW_ALLOC_INPUT_FLAGS_PATH_RX; + use_ntuple = bnxt_filter_type_check(pattern, error); + +start: switch (act->type) { case RTE_FLOW_ACTION_TYPE_QUEUE: /* Allow this flow. Redirect to a VNIC. */ @@ -844,16 +1062,9 @@ bnxt_validate_and_parse_flow(struct rte_eth_dev *dev, vnic_id = act_q->index; } + BNXT_VALID_VNIC_OR_RET(bp, vnic_id); + vnic = &bp->vnic_info[vnic_id]; - if (vnic == NULL) { - rte_flow_error_set(error, - EINVAL, - RTE_FLOW_ERROR_TYPE_ACTION, - act, - "No matching VNIC found."); - rc = -rte_errno; - goto ret; - } if (vnic->rx_queue_cnt) { if (vnic->start_grp_id != act_q->index) { PMD_DRV_LOG(ERR, @@ -871,13 +1082,11 @@ bnxt_validate_and_parse_flow(struct rte_eth_dev *dev, rxq = bp->rx_queues[act_q->index]; - if (!(dev_conf->rxmode.mq_mode & ETH_MQ_RX_RSS) && rxq) + if (!(dev_conf->rxmode.mq_mode & ETH_MQ_RX_RSS) && rxq && + vnic->fw_vnic_id != INVALID_HW_RING_ID) goto use_vnic; - if (!rxq || - bp->vnic_info[0].fw_grp_ids[act_q->index] != - INVALID_HW_RING_ID || - !rxq->rx_deferred_start) { + if (!rxq) { PMD_DRV_LOG(ERR, "Queue invalid or used with other VNIC\n"); rte_flow_error_set(error, @@ -889,8 +1098,8 @@ bnxt_validate_and_parse_flow(struct rte_eth_dev *dev, goto ret; } -use_vnic: rxq->vnic = vnic; + rxq->rx_started = 1; vnic->rx_queue_cnt++; vnic->start_grp_id = act_q->index; vnic->end_grp_id = act_q->index; @@ -899,51 +1108,88 @@ use_vnic: PMD_DRV_LOG(DEBUG, "VNIC found\n"); rc = bnxt_vnic_prep(bp, vnic); - if (rc) + if (rc) { + rte_flow_error_set(error, + EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "VNIC prep fail"); + rc = -rte_errno; goto ret; + } PMD_DRV_LOG(DEBUG, "vnic[%d] = %p vnic->fw_grp_ids = %p\n", act_q->index, vnic, vnic->fw_grp_ids); +use_vnic: vnic->ff_pool_idx = vnic_id; PMD_DRV_LOG(DEBUG, "Setting vnic ff_idx %d\n", vnic->ff_pool_idx); filter->dst_id = vnic->fw_vnic_id; - filter1 = bnxt_get_l2_filter(bp, filter, vnic); + + /* For ntuple filter, create the L2 filter with default VNIC. + * The user specified redirect queue will be set while creating + * the ntuple filter in hardware. + */ + vnic0 = BNXT_GET_DEFAULT_VNIC(bp); + if (use_ntuple) + filter1 = bnxt_get_l2_filter(bp, filter, vnic0); + else + filter1 = bnxt_get_l2_filter(bp, filter, vnic); if (filter1 == NULL) { - rc = -ENOSPC; + rte_flow_error_set(error, + ENOSPC, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "Filter not available"); + rc = -rte_errno; goto ret; } - filter->fw_l2_filter_id = filter1->fw_l2_filter_id; - PMD_DRV_LOG(DEBUG, "VNIC found\n"); + PMD_DRV_LOG(DEBUG, "new fltr: %p l2fltr: %p l2_ref_cnt: %d\n", + filter, filter1, filter1->l2_ref_cnt); + bnxt_update_filter_flags_en(filter, filter1, use_ntuple); break; case RTE_FLOW_ACTION_TYPE_DROP: vnic0 = &bp->vnic_info[0]; + filter->dst_id = vnic0->fw_vnic_id; + filter->valid_flags |= BNXT_FLOW_L2_DROP_FLAG; filter1 = bnxt_get_l2_filter(bp, filter, vnic0); if (filter1 == NULL) { - rc = -ENOSPC; + rte_flow_error_set(error, + ENOSPC, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "Filter not available"); + rc = -rte_errno; goto ret; } - filter->fw_l2_filter_id = filter1->fw_l2_filter_id; if (filter->filter_type == HWRM_CFA_EM_FILTER) filter->flags = HWRM_CFA_EM_FLOW_ALLOC_INPUT_FLAGS_DROP; - else + else if (filter->filter_type == HWRM_CFA_NTUPLE_FILTER) filter->flags = HWRM_CFA_NTUPLE_FILTER_ALLOC_INPUT_FLAGS_DROP; + + bnxt_update_filter_flags_en(filter, filter1, use_ntuple); break; case RTE_FLOW_ACTION_TYPE_COUNT: vnic0 = &bp->vnic_info[0]; filter1 = bnxt_get_l2_filter(bp, filter, vnic0); if (filter1 == NULL) { - rc = -ENOSPC; + rte_flow_error_set(error, + ENOSPC, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "New filter not available"); + rc = -rte_errno; goto ret; } filter->fw_l2_filter_id = filter1->fw_l2_filter_id; + filter->flow_id = filter1->flow_id; filter->flags = HWRM_CFA_NTUPLE_FILTER_ALLOC_INPUT_FLAGS_METER; break; case RTE_FLOW_ACTION_TYPE_VF: @@ -1002,13 +1248,184 @@ use_vnic: vnic0 = &bp->vnic_info[0]; filter1 = bnxt_get_l2_filter(bp, filter, vnic0); if (filter1 == NULL) { - rc = -ENOSPC; + rte_flow_error_set(error, + ENOSPC, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "New filter not available"); + rc = -rte_errno; goto ret; } filter->fw_l2_filter_id = filter1->fw_l2_filter_id; + filter->flow_id = filter1->flow_id; break; + case RTE_FLOW_ACTION_TYPE_RSS: + rss = (const struct rte_flow_action_rss *)act->conf; + + vnic_id = attr->group; + + BNXT_VALID_VNIC_OR_RET(bp, vnic_id); + vnic = &bp->vnic_info[vnic_id]; + + /* Check if requested RSS config matches RSS config of VNIC + * only if it is not a fresh VNIC configuration. + * Otherwise the existing VNIC configuration can be used. + */ + if (vnic->rx_queue_cnt) { + rc = match_vnic_rss_cfg(bp, vnic, rss); + if (rc) { + PMD_DRV_LOG(ERR, + "VNIC and RSS config mismatch\n"); + rte_flow_error_set(error, + EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "VNIC and RSS cfg mismatch"); + rc = -rte_errno; + goto ret; + } + goto vnic_found; + } + + for (i = 0; i < rss->queue_num; i++) { + PMD_DRV_LOG(DEBUG, "RSS action Queue %d\n", + rss->queue[i]); + + if (!rss->queue[i] || + rss->queue[i] >= bp->rx_nr_rings || + !bp->rx_queues[rss->queue[i]]) { + rte_flow_error_set(error, + EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "Invalid queue ID for RSS"); + rc = -rte_errno; + goto ret; + } + rxq = bp->rx_queues[rss->queue[i]]; + + if (bp->vnic_info[0].fw_grp_ids[rss->queue[i]] != + INVALID_HW_RING_ID) { + PMD_DRV_LOG(ERR, + "queue active with other VNIC\n"); + rte_flow_error_set(error, + EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "Invalid queue ID for RSS"); + rc = -rte_errno; + goto ret; + } + rxq->vnic = vnic; + rxq->rx_started = 1; + vnic->rx_queue_cnt++; + } + + vnic->start_grp_id = rss->queue[0]; + vnic->end_grp_id = rss->queue[rss->queue_num - 1]; + vnic->func_default = 0; //This is not a default VNIC. + + rc = bnxt_vnic_prep(bp, vnic); + if (rc) { + rte_flow_error_set(error, + EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "VNIC prep fail"); + rc = -rte_errno; + goto ret; + } + + PMD_DRV_LOG(DEBUG, + "vnic[%d] = %p vnic->fw_grp_ids = %p\n", + vnic_id, vnic, vnic->fw_grp_ids); + + vnic->ff_pool_idx = vnic_id; + PMD_DRV_LOG(DEBUG, + "Setting vnic ff_pool_idx %d\n", vnic->ff_pool_idx); + + /* This can be done only after vnic_grp_alloc is done. */ + for (i = 0; i < vnic->rx_queue_cnt; i++) { + vnic->fw_grp_ids[i] = + bp->grp_info[rss->queue[i]].fw_grp_id; + /* Make sure vnic0 does not use these rings. */ + bp->vnic_info[0].fw_grp_ids[rss->queue[i]] = + INVALID_HW_RING_ID; + } + + for (rss_idx = 0; rss_idx < HW_HASH_INDEX_SIZE; ) { + for (i = 0; i < vnic->rx_queue_cnt; i++) + vnic->rss_table[rss_idx++] = + vnic->fw_grp_ids[i]; + } + + /* Configure RSS only if the queue count is > 1 */ + if (vnic->rx_queue_cnt > 1) { + vnic->hash_type = + bnxt_rte_to_hwrm_hash_types(rss->types); + + if (!rss->key_len) { + /* If hash key has not been specified, + * use random hash key. + */ + prandom_bytes(vnic->rss_hash_key, + HW_HASH_KEY_SIZE); + } else { + if (rss->key_len > HW_HASH_KEY_SIZE) + memcpy(vnic->rss_hash_key, + rss->key, + HW_HASH_KEY_SIZE); + else + memcpy(vnic->rss_hash_key, + rss->key, + rss->key_len); + } + bnxt_hwrm_vnic_rss_cfg(bp, vnic); + } else { + PMD_DRV_LOG(DEBUG, "No RSS config required\n"); + } + +vnic_found: + filter->dst_id = vnic->fw_vnic_id; + filter1 = bnxt_get_l2_filter(bp, filter, vnic); + if (filter1 == NULL) { + rte_flow_error_set(error, + ENOSPC, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "New filter not available"); + rc = -rte_errno; + goto ret; + } + + PMD_DRV_LOG(DEBUG, "L2 filter created\n"); + bnxt_update_filter_flags_en(filter, filter1, use_ntuple); + break; + case RTE_FLOW_ACTION_TYPE_MARK: + if (bp->mark_table == NULL) { + rte_flow_error_set(error, + ENOMEM, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "Mark table not allocated."); + rc = -rte_errno; + goto ret; + } + + if (bp->flags & BNXT_FLAG_RX_VECTOR_PKT_MODE) { + PMD_DRV_LOG(DEBUG, + "Disabling vector processing for mark\n"); + bp->eth_dev->rx_pkt_burst = bnxt_recv_pkts; + bp->flags &= ~BNXT_FLAG_RX_VECTOR_PKT_MODE; + } + + filter->valid_flags |= BNXT_FLOW_MARK_FLAG; + filter->mark = ((const struct rte_flow_action_mark *) + act->conf)->id; + PMD_DRV_LOG(DEBUG, "Mark the flow %d\n", filter->mark); + break; default: rte_flow_error_set(error, EINVAL, @@ -1019,23 +1436,46 @@ use_vnic: goto ret; } +done: + act = bnxt_flow_non_void_action(++act); + while (act->type != RTE_FLOW_ACTION_TYPE_END) + goto start; + + return rc; +ret: + if (filter1) { + bnxt_hwrm_clear_l2_filter(bp, filter1); bnxt_free_filter(bp, filter1); - filter1->fw_l2_filter_id = -1; } -done: - act = bnxt_flow_non_void_action(++act); - if (act->type != RTE_FLOW_ACTION_TYPE_END) { - rte_flow_error_set(error, - EINVAL, - RTE_FLOW_ERROR_TYPE_ACTION, - act, - "Invalid action."); - rc = -rte_errno; - goto ret; + + if (rte_errno) { + if (vnic && STAILQ_EMPTY(&vnic->filter)) + vnic->rx_queue_cnt = 0; + + if (rxq && !vnic->rx_queue_cnt) + rxq->vnic = &bp->vnic_info[0]; } -ret: - return rc; + return -rte_errno; +} + +static +struct bnxt_vnic_info *find_matching_vnic(struct bnxt *bp, + struct bnxt_filter_info *filter) +{ + struct bnxt_vnic_info *vnic = NULL; + unsigned int i; + + for (i = 0; i < bp->max_vnics; i++) { + vnic = &bp->vnic_info[i]; + if (vnic->fw_vnic_id != INVALID_VNIC_ID && + filter->dst_id == vnic->fw_vnic_id) { + PMD_DRV_LOG(DEBUG, "Found matching VNIC Id %d\n", + vnic->ff_pool_idx); + return vnic; + } + } + return NULL; } static int @@ -1046,28 +1486,80 @@ bnxt_flow_validate(struct rte_eth_dev *dev, struct rte_flow_error *error) { struct bnxt *bp = dev->data->dev_private; + struct bnxt_vnic_info *vnic = NULL; struct bnxt_filter_info *filter; int ret = 0; + bnxt_acquire_flow_lock(bp); ret = bnxt_flow_args_validate(attr, pattern, actions, error); - if (ret != 0) + if (ret != 0) { + bnxt_release_flow_lock(bp); return ret; + } filter = bnxt_get_unused_filter(bp); if (filter == NULL) { PMD_DRV_LOG(ERR, "Not enough resources for a new flow.\n"); + bnxt_release_flow_lock(bp); return -ENOMEM; } ret = bnxt_validate_and_parse_flow(dev, pattern, actions, attr, error, filter); + if (ret) + goto exit; + + vnic = find_matching_vnic(bp, filter); + if (vnic) { + if (STAILQ_EMPTY(&vnic->filter)) { + rte_free(vnic->fw_grp_ids); + bnxt_hwrm_vnic_ctx_free(bp, vnic); + bnxt_hwrm_vnic_free(bp, vnic); + vnic->rx_queue_cnt = 0; + PMD_DRV_LOG(DEBUG, "Free VNIC\n"); + } + } + + if (filter->filter_type == HWRM_CFA_EM_FILTER) + bnxt_hwrm_clear_em_filter(bp, filter); + else if (filter->filter_type == HWRM_CFA_NTUPLE_FILTER) + bnxt_hwrm_clear_ntuple_filter(bp, filter); + else + bnxt_hwrm_clear_l2_filter(bp, filter); + +exit: /* No need to hold on to this filter if we are just validating flow */ - filter->fw_l2_filter_id = UINT64_MAX; bnxt_free_filter(bp, filter); + bnxt_release_flow_lock(bp); return ret; } +static void +bnxt_update_filter(struct bnxt *bp, struct bnxt_filter_info *old_filter, + struct bnxt_filter_info *new_filter) +{ + /* Clear the new L2 filter that was created in the previous step in + * bnxt_validate_and_parse_flow. For L2 filters, we will use the new + * filter which points to the new destination queue and so we clear + * the previous L2 filter. For ntuple filters, we are going to reuse + * the old L2 filter and create new NTUPLE filter with this new + * destination queue subsequently during bnxt_flow_create. So we + * decrement the ref cnt of the L2 filter that would've been bumped + * up previously in bnxt_validate_and_parse_flow as the old n-tuple + * filter that was referencing it will be deleted now. + */ + bnxt_hwrm_clear_l2_filter(bp, old_filter); + if (new_filter->filter_type == HWRM_CFA_L2_FILTER) { + bnxt_hwrm_set_l2_filter(bp, new_filter->dst_id, new_filter); + } else { + if (new_filter->filter_type == HWRM_CFA_EM_FILTER) + bnxt_hwrm_clear_em_filter(bp, old_filter); + if (new_filter->filter_type == HWRM_CFA_NTUPLE_FILTER) + bnxt_hwrm_clear_ntuple_filter(bp, old_filter); + } +} + static int bnxt_match_filter(struct bnxt *bp, struct bnxt_filter_info *nf) { @@ -1075,7 +1567,7 @@ bnxt_match_filter(struct bnxt *bp, struct bnxt_filter_info *nf) struct rte_flow *flow; int i; - for (i = bp->max_vnics; i >= 0; i--) { + for (i = bp->max_vnics - 1; i >= 0; i--) { struct bnxt_vnic_info *vnic = &bp->vnic_info[i]; if (vnic->fw_vnic_id == INVALID_VNIC_ID) @@ -1117,24 +1609,13 @@ bnxt_match_filter(struct bnxt *bp, struct bnxt_filter_info *nf) sizeof(nf->dst_ipaddr_mask))) { if (mf->dst_id == nf->dst_id) return -EEXIST; - /* Clear the new L2 filter that was created - * earlier in bnxt_validate_and_parse_flow. - */ - bnxt_hwrm_clear_l2_filter(bp, nf); - /* - * Same Flow, Different queue - * Clear the old ntuple filter - * Reuse the matching L2 filter - * ID for the new filter - */ - nf->fw_l2_filter_id = mf->fw_l2_filter_id; - if (nf->filter_type == HWRM_CFA_EM_FILTER) - bnxt_hwrm_clear_em_filter(bp, mf); - if (nf->filter_type == HWRM_CFA_NTUPLE_FILTER) - bnxt_hwrm_clear_ntuple_filter(bp, mf); /* Free the old filter, update flow * with new filter */ + bnxt_update_filter(bp, mf, nf); + STAILQ_REMOVE(&vnic->filter, mf, + bnxt_filter_info, next); + STAILQ_INSERT_TAIL(&vnic->filter, nf, next); bnxt_free_filter(bp, mf); flow->filter = nf; return -EXDEV; @@ -1144,6 +1625,51 @@ bnxt_match_filter(struct bnxt *bp, struct bnxt_filter_info *nf) return 0; } +static void +bnxt_setup_flow_counter(struct bnxt *bp) +{ + if (bp->fw_cap & BNXT_FW_CAP_ADV_FLOW_COUNTERS && + !(bp->flags & BNXT_FLAG_FC_THREAD) && BNXT_FLOW_XSTATS_EN(bp)) { + rte_eal_alarm_set(US_PER_S * BNXT_FC_TIMER, + bnxt_flow_cnt_alarm_cb, + (void *)bp); + bp->flags |= BNXT_FLAG_FC_THREAD; + } +} + +void bnxt_flow_cnt_alarm_cb(void *arg) +{ + int rc = 0; + struct bnxt *bp = arg; + + if (!bp->flow_stat->rx_fc_out_tbl.va) { + PMD_DRV_LOG(ERR, "bp->flow_stat->rx_fc_out_tbl.va is NULL?\n"); + bnxt_cancel_fc_thread(bp); + return; + } + + if (!bp->flow_stat->flow_count) { + bnxt_cancel_fc_thread(bp); + return; + } + + if (!bp->eth_dev->data->dev_started) { + bnxt_cancel_fc_thread(bp); + return; + } + + rc = bnxt_flow_stats_req(bp); + if (rc) { + PMD_DRV_LOG(ERR, "Flow stat alarm not rescheduled.\n"); + return; + } + + rte_eal_alarm_set(US_PER_S * BNXT_FC_TIMER, + bnxt_flow_cnt_alarm_cb, + (void *)bp); +} + + static struct rte_flow * bnxt_flow_create(struct rte_eth_dev *dev, const struct rte_flow_attr *attr, @@ -1156,9 +1682,8 @@ bnxt_flow_create(struct rte_eth_dev *dev, struct bnxt_filter_info *filter; bool update_flow = false; struct rte_flow *flow; - unsigned int i; int ret = 0; - uint32_t tun_type; + uint32_t tun_type, flow_id; if (BNXT_VF(bp) && !BNXT_VF_IS_TRUSTED(bp)) { rte_flow_error_set(error, EINVAL, @@ -1167,6 +1692,15 @@ bnxt_flow_create(struct rte_eth_dev *dev, return NULL; } + if (!dev->data->dev_started) { + rte_flow_error_set(error, + EINVAL, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, + "Device must be started"); + return NULL; + } + flow = rte_zmalloc("bnxt_flow", sizeof(struct rte_flow), 0); if (!flow) { rte_flow_error_set(error, ENOMEM, @@ -1175,6 +1709,7 @@ bnxt_flow_create(struct rte_eth_dev *dev, return flow; } + bnxt_acquire_flow_lock(bp); ret = bnxt_flow_args_validate(attr, pattern, actions, error); if (ret != 0) { PMD_DRV_LOG(ERR, "Not a validate flow.\n"); @@ -1183,7 +1718,9 @@ bnxt_flow_create(struct rte_eth_dev *dev, filter = bnxt_get_unused_filter(bp); if (filter == NULL) { - PMD_DRV_LOG(ERR, "Not enough resources for a new flow.\n"); + rte_flow_error_set(error, ENOSPC, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Not enough resources for a new flow"); goto free_flow; } @@ -1257,46 +1794,45 @@ bnxt_flow_create(struct rte_eth_dev *dev, ret = bnxt_hwrm_set_ntuple_filter(bp, filter->dst_id, filter); } - for (i = 0; i < bp->max_vnics; i++) { - vnic = &bp->vnic_info[i]; - if (vnic->fw_vnic_id != INVALID_VNIC_ID && - filter->dst_id == vnic->fw_vnic_id) { - PMD_DRV_LOG(ERR, "Found matching VNIC Id %d\n", - vnic->ff_pool_idx); - break; - } - } + vnic = find_matching_vnic(bp, filter); done: - if (!ret) { - flow->filter = filter; - flow->vnic = vnic; - /* VNIC is set only in case of queue or RSS action */ - if (vnic) { - /* - * RxQ0 is not used for flow filters. - */ - - if (update_flow) { - ret = -EXDEV; - goto free_flow; - } - STAILQ_INSERT_TAIL(&vnic->filter, filter, next); - } - PMD_DRV_LOG(ERR, "Successfully created flow.\n"); - STAILQ_INSERT_TAIL(&vnic->flow_list, flow, next); - return flow; - } - if (!ret) { + if (!ret || update_flow) { flow->filter = filter; flow->vnic = vnic; if (update_flow) { ret = -EXDEV; goto free_flow; } - PMD_DRV_LOG(ERR, "Successfully created flow.\n"); + + STAILQ_INSERT_TAIL(&vnic->filter, filter, next); + PMD_DRV_LOG(DEBUG, "Successfully created flow.\n"); STAILQ_INSERT_TAIL(&vnic->flow_list, flow, next); + if (filter->valid_flags & BNXT_FLOW_MARK_FLAG) { + PMD_DRV_LOG(DEBUG, + "Mark action: mark id 0x%x, flow id 0x%x\n", + filter->mark, filter->flow_id); + + /* TCAM and EM should be 16-bit only. + * Other modes not supported. + */ + flow_id = filter->flow_id & BNXT_FLOW_ID_MASK; + if (bp->mark_table[flow_id].valid) { + PMD_DRV_LOG(ERR, + "Entry for Mark id 0x%x occupied" + " flow id 0x%x\n", + filter->mark, filter->flow_id); + goto free_filter; + } + bp->mark_table[flow_id].valid = true; + bp->mark_table[flow_id].mark_id = filter->mark; + } + if (BNXT_FLOW_XSTATS_EN(bp)) + bp->flow_stat->flow_count++; + bnxt_release_flow_lock(bp); + bnxt_setup_flow_counter(bp); return flow; } + free_filter: bnxt_free_filter(bp, filter); free_flow: @@ -1305,8 +1841,8 @@ free_flow: RTE_FLOW_ERROR_TYPE_HANDLE, NULL, "Matching Flow exists."); else if (ret == -EXDEV) - rte_flow_error_set(error, ret, - RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + rte_flow_error_set(error, 0, + RTE_FLOW_ERROR_TYPE_NONE, NULL, "Flow with pattern exists, updating destination queue"); else if (!rte_errno) rte_flow_error_set(error, -ret, @@ -1314,6 +1850,7 @@ free_flow: "Failed to create flow."); rte_free(flow); flow = NULL; + bnxt_release_flow_lock(bp); return flow; } @@ -1359,25 +1896,21 @@ static int bnxt_handle_tunnel_redirect_destroy(struct bnxt *bp, } static int -bnxt_flow_destroy(struct rte_eth_dev *dev, - struct rte_flow *flow, - struct rte_flow_error *error) +_bnxt_flow_destroy(struct bnxt *bp, + struct rte_flow *flow, + struct rte_flow_error *error) { - struct bnxt *bp = dev->data->dev_private; - struct bnxt_filter_info *filter = flow->filter; - struct bnxt_vnic_info *vnic = flow->vnic; + struct bnxt_filter_info *filter; + struct bnxt_vnic_info *vnic; int ret = 0; + uint32_t flow_id; - if (!filter) { - ret = -EINVAL; - goto done; - } + filter = flow->filter; + vnic = flow->vnic; if (filter->filter_type == HWRM_CFA_TUNNEL_REDIRECT_FILTER && filter->enables == filter->tunnel_type) { - ret = bnxt_handle_tunnel_redirect_destroy(bp, - filter, - error); + ret = bnxt_handle_tunnel_redirect_destroy(bp, filter, error); if (!ret) goto done; else @@ -1387,18 +1920,50 @@ bnxt_flow_destroy(struct rte_eth_dev *dev, ret = bnxt_match_filter(bp, filter); if (ret == 0) PMD_DRV_LOG(ERR, "Could not find matching flow\n"); + + if (filter->valid_flags & BNXT_FLOW_MARK_FLAG) { + flow_id = filter->flow_id & BNXT_FLOW_ID_MASK; + memset(&bp->mark_table[flow_id], 0, + sizeof(bp->mark_table[flow_id])); + filter->flow_id = 0; + } + if (filter->filter_type == HWRM_CFA_EM_FILTER) ret = bnxt_hwrm_clear_em_filter(bp, filter); if (filter->filter_type == HWRM_CFA_NTUPLE_FILTER) ret = bnxt_hwrm_clear_ntuple_filter(bp, filter); - else - ret = bnxt_hwrm_clear_l2_filter(bp, filter); + ret = bnxt_hwrm_clear_l2_filter(bp, filter); done: if (!ret) { + /* If it is a L2 drop filter, when the filter is created, + * the FW updates the BC/MC records. + * Once this filter is removed, issue the set_rx_mask command + * to reset the BC/MC records in the HW to the settings + * before the drop counter is created. + */ + if (filter->valid_flags & BNXT_FLOW_L2_DROP_FLAG) + bnxt_set_rx_mask_no_vlan(bp, &bp->vnic_info[0]); + + STAILQ_REMOVE(&vnic->filter, filter, bnxt_filter_info, next); bnxt_free_filter(bp, filter); STAILQ_REMOVE(&vnic->flow_list, flow, rte_flow, next); rte_free(flow); + if (BNXT_FLOW_XSTATS_EN(bp)) + bp->flow_stat->flow_count--; + + /* If this was the last flow associated with this vnic, + * switch the queue back to RSS pool. + */ + if (vnic && !vnic->func_default && + STAILQ_EMPTY(&vnic->flow_list)) { + rte_free(vnic->fw_grp_ids); + if (vnic->rx_queue_cnt > 1) + bnxt_hwrm_vnic_ctx_free(bp, vnic); + + bnxt_hwrm_vnic_free(bp, vnic); + vnic->rx_queue_cnt = 0; + } } else { rte_flow_error_set(error, -ret, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, @@ -1408,6 +1973,42 @@ done: return ret; } +static int +bnxt_flow_destroy(struct rte_eth_dev *dev, + struct rte_flow *flow, + struct rte_flow_error *error) +{ + struct bnxt *bp = dev->data->dev_private; + int ret = 0; + + bnxt_acquire_flow_lock(bp); + if (!flow) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Invalid flow: failed to destroy flow."); + bnxt_release_flow_lock(bp); + return -EINVAL; + } + + if (!flow->filter) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Invalid flow: failed to destroy flow."); + bnxt_release_flow_lock(bp); + return -EINVAL; + } + ret = _bnxt_flow_destroy(bp, flow, error); + bnxt_release_flow_lock(bp); + + return ret; +} + +void bnxt_cancel_fc_thread(struct bnxt *bp) +{ + bp->flags &= ~BNXT_FLAG_FC_THREAD; + rte_eal_alarm_cancel(bnxt_flow_cnt_alarm_cb, (void *)bp); +} + static int bnxt_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error) { @@ -1417,46 +2018,27 @@ bnxt_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error) unsigned int i; int ret = 0; - for (i = 0; i < bp->nr_vnics; i++) { + bnxt_acquire_flow_lock(bp); + for (i = 0; i < bp->max_vnics; i++) { vnic = &bp->vnic_info[i]; - STAILQ_FOREACH(flow, &vnic->flow_list, next) { - struct bnxt_filter_info *filter = flow->filter; - - if (filter->filter_type == - HWRM_CFA_TUNNEL_REDIRECT_FILTER && - filter->enables == filter->tunnel_type) { - ret = - bnxt_handle_tunnel_redirect_destroy(bp, - filter, - error); - if (!ret) - goto done; - else - return ret; - } + if (vnic && vnic->fw_vnic_id == INVALID_VNIC_ID) + continue; - if (filter->filter_type == HWRM_CFA_EM_FILTER) - ret = bnxt_hwrm_clear_em_filter(bp, filter); - if (filter->filter_type == HWRM_CFA_NTUPLE_FILTER) - ret = bnxt_hwrm_clear_ntuple_filter(bp, filter); + while (!STAILQ_EMPTY(&vnic->flow_list)) { + flow = STAILQ_FIRST(&vnic->flow_list); - if (ret) { - rte_flow_error_set - (error, - -ret, - RTE_FLOW_ERROR_TYPE_HANDLE, - NULL, - "Failed to flush flow in HW."); - return -rte_errno; - } -done: - bnxt_free_filter(bp, filter); - STAILQ_REMOVE(&vnic->flow_list, flow, - rte_flow, next); - rte_free(flow); + if (!flow->filter) + continue; + + ret = _bnxt_flow_destroy(bp, flow, error); + if (ret) + break; } } + bnxt_cancel_fc_thread(bp); + bnxt_release_flow_lock(bp); + return ret; }