+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_flow_meter_sub_policy *sub_policy = NULL;
+ union mlx5_l3t_data data;
+
+ if (policy_id > MLX5_MAX_SUB_POLICY_TBL_NUM || !priv->policy_idx_tbl)
+ return NULL;
+ if (mlx5_l3t_get_entry(priv->policy_idx_tbl, policy_id, &data) ||
+ !data.dword)
+ return NULL;
+ if (policy_idx)
+ *policy_idx = data.dword;
+ sub_policy = mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_MTR_POLICY],
+ data.dword);
+ /* Remove reference taken by the mlx5_l3t_get_entry. */
+ mlx5_l3t_clear_entry(priv->policy_idx_tbl, policy_id);
+ if (sub_policy)
+ if (sub_policy->main_policy_id)
+ return sub_policy->main_policy;
+ return NULL;
+}
+
+/**
+ * Get the last meter's policy from one meter's policy in hierarchy.
+ *
+ * @param[in] dev
+ * Pointer to Ethernet device.
+ * @param[in] policy
+ * Pointer to flow meter policy.
+ *
+ * @return
+ * Pointer to the final meter's policy, or NULL when fail.
+ */
+struct mlx5_flow_meter_policy *
+mlx5_flow_meter_hierarchy_get_final_policy(struct rte_eth_dev *dev,
+ struct mlx5_flow_meter_policy *policy)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_flow_meter_info *next_fm;
+ struct mlx5_flow_meter_policy *next_policy = policy;
+
+ while (next_policy->is_hierarchy) {
+ next_fm = mlx5_flow_meter_find(priv,
+ next_policy->act_cnt[RTE_COLOR_GREEN].next_mtr_id, NULL);
+ if (!next_fm || next_fm->def_policy)
+ return NULL;
+ next_policy = mlx5_flow_meter_policy_find(dev,
+ next_fm->policy_id, NULL);
+ MLX5_ASSERT(next_policy);
+ }
+ return next_policy;
+}
+
+/**
+ * Callback to check MTR policy action validate
+ *
+ * @param[in] dev
+ * Pointer to Ethernet device.
+ * @param[in] actions
+ * Pointer to meter policy action detail.
+ * @param[out] error
+ * Pointer to the error structure.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_flow_meter_policy_validate(struct rte_eth_dev *dev,
+ struct rte_mtr_meter_policy_params *policy,
+ struct rte_mtr_error *error)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct rte_flow_attr attr = { .transfer =
+ priv->config.dv_esw_en ? 1 : 0};
+ bool is_rss = false;
+ uint8_t policy_mode;
+ uint8_t domain_bitmap;
+ int ret;
+
+ if (!priv->mtr_en || !priv->sh->meter_aso_en)
+ return -rte_mtr_error_set(error, ENOTSUP,
+ RTE_MTR_ERROR_TYPE_METER_POLICY,
+ NULL, "meter policy unsupported.");
+ ret = mlx5_flow_validate_mtr_acts(dev, policy->actions, &attr,
+ &is_rss, &domain_bitmap, &policy_mode, error);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static int
+__mlx5_flow_meter_policy_delete(struct rte_eth_dev *dev,
+ uint32_t policy_id,
+ struct mlx5_flow_meter_policy *mtr_policy,
+ struct rte_mtr_error *error,
+ bool clear_l3t)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_flow_meter_sub_policy *sub_policy;
+ uint32_t i, j;
+ uint16_t sub_policy_num;
+
+ rte_spinlock_lock(&mtr_policy->sl);
+ if (mtr_policy->ref_cnt) {
+ rte_spinlock_unlock(&mtr_policy->sl);
+ return -rte_mtr_error_set(error, EBUSY,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+ NULL,
+ "Meter policy object is being used.");
+ }
+ mlx5_flow_destroy_policy_rules(dev, mtr_policy);
+ mlx5_flow_destroy_mtr_acts(dev, mtr_policy);
+ for (i = 0; i < MLX5_MTR_DOMAIN_MAX; i++) {
+ sub_policy_num = (mtr_policy->sub_policy_num >>
+ (MLX5_MTR_SUB_POLICY_NUM_SHIFT * i)) &
+ MLX5_MTR_SUB_POLICY_NUM_MASK;
+ if (sub_policy_num) {
+ for (j = 0; j < sub_policy_num; j++) {
+ sub_policy = mtr_policy->sub_policys[i][j];
+ if (sub_policy)
+ mlx5_ipool_free
+ (priv->sh->ipool[MLX5_IPOOL_MTR_POLICY],
+ sub_policy->idx);
+ }
+ }
+ }
+ if (priv->policy_idx_tbl && clear_l3t) {
+ if (mlx5_l3t_clear_entry(priv->policy_idx_tbl, policy_id)) {
+ rte_spinlock_unlock(&mtr_policy->sl);
+ return -rte_mtr_error_set(error, ENOTSUP,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID, NULL,
+ "Fail to delete policy in index table.");
+ }
+ }
+ rte_spinlock_unlock(&mtr_policy->sl);
+ return 0;
+}
+
+/**
+ * Callback to add MTR policy.
+ *
+ * @param[in] dev
+ * Pointer to Ethernet device.
+ * @param[out] policy_id
+ * Pointer to policy id
+ * @param[in] actions
+ * Pointer to meter policy action detail.
+ * @param[out] error
+ * Pointer to the error structure.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_flow_meter_policy_add(struct rte_eth_dev *dev,
+ uint32_t policy_id,
+ struct rte_mtr_meter_policy_params *policy,
+ struct rte_mtr_error *error)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct rte_flow_attr attr = { .transfer =
+ priv->config.dv_esw_en ? 1 : 0};
+ uint32_t sub_policy_idx = 0;
+ uint32_t policy_idx = 0;
+ struct mlx5_flow_meter_policy *mtr_policy = NULL;
+ struct mlx5_flow_meter_sub_policy *sub_policy;
+ bool is_rss = false;
+ uint8_t policy_mode;
+ uint32_t i;
+ int ret;
+ uint32_t policy_size = sizeof(struct mlx5_flow_meter_policy);
+ uint16_t sub_policy_num;
+ uint8_t domain_bitmap = 0;
+ union mlx5_l3t_data data;
+ bool skip_rule = false;
+
+ if (!priv->mtr_en)
+ return -rte_mtr_error_set(error, ENOTSUP,
+ RTE_MTR_ERROR_TYPE_METER_POLICY,
+ NULL, "meter policy unsupported. ");
+ if (policy_id == MLX5_INVALID_POLICY_ID)
+ return -rte_mtr_error_set(error, ENOTSUP,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+ NULL, "policy ID is invalid. ");
+ if (policy_id == priv->sh->mtrmng->def_policy_id)
+ return -rte_mtr_error_set(error, EEXIST,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+ NULL, "default policy ID exists. ");
+ mtr_policy = mlx5_flow_meter_policy_find(dev, policy_id, &policy_idx);
+ if (mtr_policy)
+ return -rte_mtr_error_set(error, EEXIST,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+ NULL, "policy ID exists. ");
+ ret = mlx5_flow_validate_mtr_acts(dev, policy->actions, &attr,
+ &is_rss, &domain_bitmap,
+ &policy_mode, error);
+ if (ret)
+ return ret;
+ if (!domain_bitmap)
+ return -rte_mtr_error_set(error, ENOTSUP,
+ RTE_MTR_ERROR_TYPE_METER_POLICY,
+ NULL, "fail to find policy domain.");
+ if (policy_mode == MLX5_MTR_POLICY_MODE_DEF) {
+ if (priv->sh->mtrmng->def_policy_id != MLX5_INVALID_POLICY_ID)
+ return -rte_mtr_error_set(error, EEXIST,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+ NULL, "a policy with similar actions "
+ "is already configured");
+ if (mlx5_flow_create_def_policy(dev))
+ return -rte_mtr_error_set(error, ENOTSUP,
+ RTE_MTR_ERROR_TYPE_METER_POLICY,
+ NULL,
+ "fail to create non-terminated policy.");
+ priv->sh->mtrmng->def_policy_id = policy_id;
+ return 0;