net/bnxt: fix duplicate filter pattern creation error
[dpdk.git] / drivers / net / bnxt / bnxt_filter.c
index 029a4eb..22cfbd3 100644 (file)
@@ -301,7 +301,8 @@ bnxt_filter_type_check(const struct rte_flow_item pattern[],
 }
 
 static int
-bnxt_validate_and_parse_flow_type(const struct rte_flow_item pattern[],
+bnxt_validate_and_parse_flow_type(struct bnxt *bp,
+                                 const struct rte_flow_item pattern[],
                                  struct rte_flow_error *error,
                                  struct bnxt_filter_info *filter)
 {
@@ -318,11 +319,14 @@ bnxt_validate_and_parse_flow_type(const struct rte_flow_item pattern[],
        const struct rte_flow_item_vxlan *vxlan_mask;
        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;
        bool vni_masked = 0;
        bool tni_masked = 0;
+       uint32_t vf = 0;
        int use_ntuple;
        uint32_t en = 0;
+       int dflt_vnic;
 
        use_ntuple = bnxt_filter_type_check(pattern, error);
        RTE_LOG(DEBUG, PMD, "Use NTUPLE %d\n", use_ntuple);
@@ -687,6 +691,40 @@ bnxt_validate_and_parse_flow_type(const struct rte_flow_item pattern[],
                                 CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE;
                        }
                        break;
+               case RTE_FLOW_ITEM_TYPE_VF:
+                       vf_spec = (const struct rte_flow_item_vf *)item->spec;
+                       vf = vf_spec->id;
+                       if (!BNXT_PF(bp)) {
+                               rte_flow_error_set(error, EINVAL,
+                                          RTE_FLOW_ERROR_TYPE_ITEM,
+                                          item,
+                                          "Configuring on a VF!");
+                               return -rte_errno;
+                       }
+
+                       if (vf >= bp->pdev->max_vfs) {
+                               rte_flow_error_set(error, EINVAL,
+                                          RTE_FLOW_ERROR_TYPE_ITEM,
+                                          item,
+                                          "Incorrect VF id!");
+                               return -rte_errno;
+                       }
+
+                       filter->mirror_vnic_id =
+                       dflt_vnic = bnxt_hwrm_func_qcfg_vf_dflt_vnic_id(bp, vf);
+                       if (dflt_vnic < 0) {
+                               /* This simply indicates there's no driver
+                                * loaded. This is not an error.
+                                */
+                               rte_flow_error_set(error, EINVAL,
+                                          RTE_FLOW_ERROR_TYPE_ITEM,
+                                          item,
+                                          "Unable to get default VNIC for VF");
+                               return -rte_errno;
+                       }
+                       filter->mirror_vnic_id = dflt_vnic;
+                       en |= NTUPLE_FLTR_ALLOC_INPUT_EN_MIRROR_VNIC_ID;
+                       break;
                default:
                        break;
                }
@@ -755,6 +793,8 @@ bnxt_get_l2_filter(struct bnxt *bp, struct bnxt_filter_info *nf,
        //This flow needs DST MAC which is not same as port/l2
        RTE_LOG(DEBUG, PMD, "Create L2 filter for DST MAC\n");
        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 |
                        L2_FILTER_ALLOC_INPUT_EN_L2_ADDR_MASK;
@@ -766,7 +806,6 @@ bnxt_get_l2_filter(struct bnxt *bp, struct bnxt_filter_info *nf,
                bnxt_free_filter(bp, filter1);
                return NULL;
        }
-       STAILQ_INSERT_TAIL(&vnic->filter, filter1, next);
        return filter1;
 }
 
@@ -781,8 +820,11 @@ bnxt_validate_and_parse_flow(struct rte_eth_dev *dev,
        const struct rte_flow_action *act = nxt_non_void_action(actions);
        struct bnxt *bp = (struct bnxt *)dev->data->dev_private;
        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;
+       uint32_t vf = 0;
+       int dflt_vnic;
        int rc;
 
        if (bp->eth_dev->data->dev_conf.rxmode.mq_mode & ETH_MQ_RX_RSS) {
@@ -794,7 +836,7 @@ bnxt_validate_and_parse_flow(struct rte_eth_dev *dev,
                goto ret;
        }
 
-       rc = bnxt_validate_and_parse_flow_type(pattern, error, filter);
+       rc = bnxt_validate_and_parse_flow_type(bp, pattern, error, filter);
        if (rc != 0)
                goto ret;
 
@@ -860,6 +902,52 @@ bnxt_validate_and_parse_flow(struct rte_eth_dev *dev,
                filter->fw_l2_filter_id = filter1->fw_l2_filter_id;
                filter->flags = HWRM_CFA_NTUPLE_FILTER_ALLOC_INPUT_FLAGS_METER;
                break;
+       case RTE_FLOW_ACTION_TYPE_VF:
+               act_vf = (const struct rte_flow_action_vf *)act->conf;
+               vf = act_vf->id;
+               if (!BNXT_PF(bp)) {
+                       rte_flow_error_set(error, EINVAL,
+                                  RTE_FLOW_ERROR_TYPE_ACTION,
+                                  act,
+                                  "Configuring on a VF!");
+                       rc = -rte_errno;
+                       goto ret;
+               }
+
+               if (vf >= bp->pdev->max_vfs) {
+                       rte_flow_error_set(error, EINVAL,
+                                  RTE_FLOW_ERROR_TYPE_ACTION,
+                                  act,
+                                  "Incorrect VF id!");
+                       rc = -rte_errno;
+                       goto ret;
+               }
+
+               filter->mirror_vnic_id =
+               dflt_vnic = bnxt_hwrm_func_qcfg_vf_dflt_vnic_id(bp, vf);
+               if (dflt_vnic < 0) {
+                       /* This simply indicates there's no driver loaded.
+                        * This is not an error.
+                        */
+                       rte_flow_error_set(error, EINVAL,
+                                  RTE_FLOW_ERROR_TYPE_ACTION,
+                                  act,
+                                  "Unable to get default VNIC for VF");
+                       rc = -rte_errno;
+                       goto ret;
+               }
+               filter->mirror_vnic_id = dflt_vnic;
+               filter->enables |= NTUPLE_FLTR_ALLOC_INPUT_EN_MIRROR_VNIC_ID;
+
+               vnic0 = STAILQ_FIRST(&bp->ff_pool[0]);
+               filter1 = bnxt_get_l2_filter(bp, filter, vnic0);
+               if (filter1 == NULL) {
+                       rc = -ENOSPC;
+                       goto ret;
+               }
+               filter->fw_l2_filter_id = filter1->fw_l2_filter_id;
+               break;
+
        default:
                rte_flow_error_set(error, EINVAL,
                                   RTE_FLOW_ERROR_TYPE_ACTION, act,
@@ -868,6 +956,11 @@ bnxt_validate_and_parse_flow(struct rte_eth_dev *dev,
                goto ret;
        }
 
+       if (filter1) {
+               bnxt_free_filter(bp, filter1);
+               filter1->fw_l2_filter_id = -1;
+       }
+
        act = nxt_non_void_action(++act);
        if (act->type != RTE_FLOW_ACTION_TYPE_END) {
                rte_flow_error_set(error, EINVAL,
@@ -952,8 +1045,23 @@ bnxt_match_filter(struct bnxt *bp, struct bnxt_filter_info *nf)
                            !memcmp(mf->dst_ipaddr, nf->dst_ipaddr,
                                    sizeof(nf->dst_ipaddr)) &&
                            !memcmp(mf->dst_ipaddr_mask, nf->dst_ipaddr_mask,
-                                   sizeof(nf->dst_ipaddr_mask)))
-                               return -EEXIST;
+                                   sizeof(nf->dst_ipaddr_mask))) {
+                               if (mf->dst_id == nf->dst_id)
+                                       return -EEXIST;
+                               /* Same Flow, Different queue
+                                * Clear the old ntuple filter
+                                */
+                               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_free_filter(bp, mf);
+                               flow->filter = nf;
+                               return -EXDEV;
+                       }
                }
        }
        return 0;
@@ -969,6 +1077,7 @@ bnxt_flow_create(struct rte_eth_dev *dev,
        struct bnxt *bp = (struct bnxt *)dev->data->dev_private;
        struct bnxt_filter_info *filter;
        struct bnxt_vnic_info *vnic = NULL;
+       bool update_flow = false;
        struct rte_flow *flow;
        unsigned int i;
        int ret = 0;
@@ -999,9 +1108,17 @@ bnxt_flow_create(struct rte_eth_dev *dev,
                goto free_filter;
 
        ret = bnxt_match_filter(bp, filter);
-       if (ret != 0) {
+       if (ret == -EEXIST) {
                RTE_LOG(DEBUG, PMD, "Flow already exists.\n");
+               /* Clear the filter that was created as part of
+                * validate_and_parse_flow() above
+                */
+               bnxt_hwrm_clear_l2_filter(bp, filter);
                goto free_filter;
+       } else if (ret == -EXDEV) {
+               RTE_LOG(DEBUG, PMD, "Flow with same pattern exists");
+               RTE_LOG(DEBUG, PMD, "Updating with different destination\n");
+               update_flow = true;
        }
 
        if (filter->filter_type == HWRM_CFA_EM_FILTER) {
@@ -1024,22 +1141,29 @@ bnxt_flow_create(struct rte_eth_dev *dev,
        if (!ret) {
                flow->filter = filter;
                flow->vnic = vnic;
+               if (update_flow) {
+                       ret = -EXDEV;
+                       goto free_flow;
+               }
                RTE_LOG(ERR, PMD, "Successfully created flow.\n");
                STAILQ_INSERT_TAIL(&vnic->flow_list, flow, next);
                return flow;
        }
 free_filter:
-       filter->fw_l2_filter_id = -1;
        bnxt_free_filter(bp, filter);
 free_flow:
        if (ret == -EEXIST)
                rte_flow_error_set(error, ret,
                                   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,
+                                  "Flow with pattern exists, updating destination queue");
        else
                rte_flow_error_set(error, -ret,
                                   RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
-                          "Failed to create flow.");
+                                  "Failed to create flow.");
        rte_free(flow);
        flow = NULL;
        return flow;
@@ -1063,6 +1187,7 @@ bnxt_flow_destroy(struct rte_eth_dev *dev,
        if (filter->filter_type == HWRM_CFA_NTUPLE_FILTER)
                ret = bnxt_hwrm_clear_ntuple_filter(bp, filter);
 
+       bnxt_hwrm_clear_l2_filter(bp, filter);
        if (!ret) {
                STAILQ_REMOVE(&vnic->flow_list, flow, rte_flow, next);
                rte_free(flow);