net/mlx5: support ASO meter action
[dpdk.git] / drivers / net / mlx5 / mlx5_flow.c
index 1afddf7..cf93ab0 100644 (file)
@@ -4359,8 +4359,12 @@ flow_create_split_inner(struct rte_eth_dev *dev,
  *
  * @param[in] dev
  *   Pointer to Ethernet device.
+ * @param[in] flow
+ *   Parent flow structure pointer.
  * @param[in] fm
  *   Pointer to flow meter structure.
+ * @param[in] attr
+ *   Flow rule attributes.
  * @param[in] items
  *   Pattern specification (list terminated by the END pattern item).
  * @param[out] sfx_items
@@ -4371,10 +4375,6 @@ flow_create_split_inner(struct rte_eth_dev *dev,
  *   Suffix flow actions.
  * @param[out] actions_pre
  *   Prefix flow actions.
- * @param[out] pattern_sfx
- *   The pattern items for the suffix flow.
- * @param[out] tag_sfx
- *   Pointer to suffix flow tag.
  * @param[out] error
  *   Perform verbose error reporting if not NULL.
  *
@@ -4383,7 +4383,9 @@ flow_create_split_inner(struct rte_eth_dev *dev,
  */
 static uint32_t
 flow_meter_split_prep(struct rte_eth_dev *dev,
-                     struct mlx5_flow_meter *fm,
+                     struct rte_flow *flow,
+                     struct mlx5_flow_meter_info *fm,
+                     const struct rte_flow_attr *attr,
                      const struct rte_flow_item items[],
                      struct rte_flow_item sfx_items[],
                      const struct rte_flow_action actions[],
@@ -4401,6 +4403,12 @@ flow_meter_split_prep(struct rte_eth_dev *dev,
        struct mlx5_rte_flow_item_tag *tag_item_mask;
        uint32_t tag_id = 0;
        bool copy_vlan = false;
+       struct rte_flow_action *hw_mtr_action;
+       struct rte_flow_action_jump *jump_data;
+       struct rte_flow_action *action_pre_head = NULL;
+       bool mtr_first = priv->sh->meter_aso_en &&
+                       (attr->egress ||
+                       (attr->transfer && priv->representor_id != UINT16_MAX));
        uint8_t mtr_id_offset = priv->mtr_reg_share ? MLX5_MTR_COLOR_BITS : 0;
        uint8_t mtr_reg_bits = priv->mtr_reg_share ?
                                MLX5_MTR_IDLE_BITS_IN_COLOR_REG : MLX5_REG_BITS;
@@ -4409,29 +4417,39 @@ flow_meter_split_prep(struct rte_eth_dev *dev,
        uint8_t flow_id_bits = 0;
        int shift;
 
+       /* For ASO meter, meter must be before tag in TX direction. */
+       if (mtr_first) {
+               action_pre_head = actions_pre++;
+               /* Leave space for tag action. */
+               tag_action = actions_pre++;
+       }
        /* Prepare the actions for prefix and suffix flow. */
        for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
-               struct rte_flow_action **action_cur = NULL;
+               struct rte_flow_action *action_cur = NULL;
 
                switch (actions->type) {
                case RTE_FLOW_ACTION_TYPE_METER:
-                       /* Add the extra tag action first. */
-                       tag_action = actions_pre++;
-                       action_cur = &actions_pre;
+                       if (mtr_first) {
+                               action_cur = action_pre_head;
+                       } else {
+                               /* Leave space for tag action. */
+                               tag_action = actions_pre++;
+                               action_cur = actions_pre++;
+                       }
                        break;
                case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP:
                case RTE_FLOW_ACTION_TYPE_NVGRE_DECAP:
-                       action_cur = &actions_pre;
+                       action_cur = actions_pre++;
                        break;
                case RTE_FLOW_ACTION_TYPE_RAW_ENCAP:
                        raw_encap = actions->conf;
                        if (raw_encap->size < MLX5_ENCAPSULATION_DECISION_SIZE)
-                               action_cur = &actions_pre;
+                               action_cur = actions_pre++;
                        break;
                case RTE_FLOW_ACTION_TYPE_RAW_DECAP:
                        raw_decap = actions->conf;
                        if (raw_decap->size > MLX5_ENCAPSULATION_DECISION_SIZE)
-                               action_cur = &actions_pre;
+                               action_cur = actions_pre++;
                        break;
                case RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN:
                case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_VID:
@@ -4441,14 +4459,32 @@ flow_meter_split_prep(struct rte_eth_dev *dev,
                        break;
                }
                if (!action_cur)
-                       action_cur = &actions_sfx;
-               memcpy(*action_cur, actions, sizeof(struct rte_flow_action));
-               (*action_cur)++;
+                       action_cur = actions_sfx++;
+               memcpy(action_cur, actions, sizeof(struct rte_flow_action));
        }
        /* Add end action to the actions. */
        actions_sfx->type = RTE_FLOW_ACTION_TYPE_END;
-       actions_pre->type = RTE_FLOW_ACTION_TYPE_END;
-       actions_pre++;
+       if (priv->sh->meter_aso_en) {
+               /**
+                * For ASO meter, need to add an extra jump action explicitly,
+                * to jump from meter to policer table.
+                */
+               hw_mtr_action = actions_pre;
+               hw_mtr_action->type = RTE_FLOW_ACTION_TYPE_JUMP;
+               actions_pre++;
+               actions_pre->type = RTE_FLOW_ACTION_TYPE_END;
+               actions_pre++;
+               jump_data = (struct rte_flow_action_jump *)actions_pre;
+               jump_data->group = attr->transfer ?
+                               (MLX5_FLOW_TABLE_LEVEL_METER - 1) :
+                                MLX5_FLOW_TABLE_LEVEL_METER;
+               hw_mtr_action->conf = jump_data;
+               actions_pre = (struct rte_flow_action *)(jump_data + 1);
+       } else {
+               actions_pre->type = RTE_FLOW_ACTION_TYPE_END;
+               actions_pre++;
+       }
+       /* Generate meter flow_id only if support multiple flows per meter. */
        mlx5_ipool_malloc(fm->flow_ipool, &tag_id);
        if (!tag_id)
                return rte_flow_error_set(error, ENOMEM,
@@ -4504,7 +4540,7 @@ flow_meter_split_prep(struct rte_eth_dev *dev,
                                                            0, error),
                .offset = mtr_id_offset,
                .length = mtr_reg_bits,
-               .data = fm->idx,
+               .data = flow->meter,
        };
        /*
         * The color Reg bits used by flow_id are growing from
@@ -5240,9 +5276,10 @@ flow_create_split_meter(struct rte_eth_dev *dev,
        struct rte_flow_item *sfx_items = NULL;
        struct mlx5_flow *dev_flow = NULL;
        struct rte_flow_attr sfx_attr = *attr;
-       struct mlx5_flow_meter *fm = NULL;
+       struct mlx5_flow_meter_info *fm = NULL;
        bool has_mtr = false;
        uint32_t meter_id;
+       uint32_t mtr_idx = 0;
        uint32_t mtr_tag_id = 0;
        size_t act_size;
        size_t item_size;
@@ -5254,14 +5291,13 @@ flow_create_split_meter(struct rte_eth_dev *dev,
                                                    &meter_id);
        if (has_mtr) {
                if (flow->meter) {
-                       fm = mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_MTR],
-                                           flow->meter);
+                       fm = flow_dv_meter_find_by_idx(priv, flow->meter);
                        if (!fm)
                                return rte_flow_error_set(error, EINVAL,
                                                RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
                                                NULL, "Meter not found.");
                } else {
-                       fm = mlx5_flow_meter_find(priv, meter_id);
+                       fm = mlx5_flow_meter_find(priv, meter_id, &mtr_idx);
                        if (!fm)
                                return rte_flow_error_set(error, EINVAL,
                                                RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
@@ -5270,13 +5306,14 @@ flow_create_split_meter(struct rte_eth_dev *dev,
                                                     &sfx_attr, error);
                        if (ret)
                                return -rte_errno;
-                       flow->meter = fm->idx;
+                       flow->meter = mtr_idx;
                }
                wks->fm = fm;
-               /* The prefix actions: meter, decap, encap, tag, end. */
-               act_size = sizeof(struct rte_flow_action) * (actions_n + 5) +
-                          sizeof(struct mlx5_rte_flow_action_set_tag);
-               /* The suffix items: tag, vlan, port id, end. */
+               /* Prefix actions: meter, decap, encap, tag, jump, end. */
+               act_size = sizeof(struct rte_flow_action) * (actions_n + 6) +
+                          sizeof(struct mlx5_rte_flow_action_set_tag) +
+                          sizeof(struct rte_flow_action_jump);
+               /* Suffix items: tag, vlan, port id, end. */
 #define METER_SUFFIX_ITEM 4
                item_size = sizeof(struct rte_flow_item) * METER_SUFFIX_ITEM +
                            sizeof(struct mlx5_rte_flow_item_tag) * 2;
@@ -5290,9 +5327,10 @@ flow_create_split_meter(struct rte_eth_dev *dev,
                sfx_items = (struct rte_flow_item *)((char *)sfx_actions +
                             act_size);
                pre_actions = sfx_actions + actions_n;
-               mtr_tag_id = flow_meter_split_prep(dev, fm, items, sfx_items,
-                                                  actions, sfx_actions,
-                                                  pre_actions, error);
+               mtr_tag_id = flow_meter_split_prep(dev, flow, fm, &sfx_attr,
+                                                  items, sfx_items, actions,
+                                                  sfx_actions, pre_actions,
+                                                  error);
                if (!mtr_tag_id) {
                        ret = -rte_errno;
                        goto exit;
@@ -6629,7 +6667,7 @@ mlx5_flow_destroy_mtr_tbls(struct rte_eth_dev *dev,
  */
 int
 mlx5_flow_prepare_policer_rules(struct rte_eth_dev *dev,
-                              struct mlx5_flow_meter *fm,
+                              struct mlx5_flow_meter_info *fm,
                               const struct rte_flow_attr *attr)
 {
        const struct mlx5_flow_driver_ops *fops;
@@ -6651,7 +6689,7 @@ mlx5_flow_prepare_policer_rules(struct rte_eth_dev *dev,
  */
 int
 mlx5_flow_destroy_policer_rules(struct rte_eth_dev *dev,
-                               struct mlx5_flow_meter *fm,
+                               struct mlx5_flow_meter_info *fm,
                                const struct rte_flow_attr *attr)
 {
        const struct mlx5_flow_driver_ops *fops;
@@ -6660,6 +6698,44 @@ mlx5_flow_destroy_policer_rules(struct rte_eth_dev *dev,
        return fops->destroy_policer_rules(dev, fm, attr);
 }
 
+/**
+ * Allocate the needed aso flow meter id.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ *
+ * @return
+ *   Index to aso flow meter on success, NULL otherwise.
+ */
+uint32_t
+mlx5_flow_mtr_alloc(struct rte_eth_dev *dev)
+{
+       const struct mlx5_flow_driver_ops *fops;
+
+       fops = flow_get_drv_ops(MLX5_FLOW_TYPE_DV);
+       return fops->create_meter(dev);
+}
+
+/**
+ * Free the aso flow meter id.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param[in] mtr_idx
+ *  Index to aso flow meter to be free.
+ *
+ * @return
+ *   0 on success.
+ */
+void
+mlx5_flow_mtr_free(struct rte_eth_dev *dev, uint32_t mtr_idx)
+{
+       const struct mlx5_flow_driver_ops *fops;
+
+       fops = flow_get_drv_ops(MLX5_FLOW_TYPE_DV);
+       fops->free_meter(dev, mtr_idx);
+}
+
 /**
  * Allocate a counter.
  *