+
+/**
+ * Find meter by index.
+ *
+ * @param priv
+ * Pointer to mlx5_priv.
+ * @param idx
+ * Meter index.
+ *
+ * @return
+ * Pointer to the meter info found on success, NULL otherwise.
+ */
+struct mlx5_flow_meter_info *
+flow_dv_meter_find_by_idx(struct mlx5_priv *priv, uint32_t idx)
+{
+ struct mlx5_aso_mtr *aso_mtr;
+
+ if (priv->sh->meter_aso_en) {
+ aso_mtr = mlx5_aso_meter_by_idx(priv, idx);
+ if (!aso_mtr)
+ return NULL;
+ return &aso_mtr->fm;
+ } else {
+ return mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_MTR], idx);
+ }
+}
+
+/**
+ * Attach meter to flow.
+ * Unidirectional Meter creation can only be done
+ * when flow direction is known, i.e. when calling meter_attach.
+ *
+ * @param [in] priv
+ * Pointer to mlx5 private data.
+ * @param[in] fm
+ * Pointer to flow meter.
+ * @param [in] attr
+ * Pointer to flow attributes.
+ * @param [out] error
+ * Pointer to error structure.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_flow_meter_attach(struct mlx5_priv *priv,
+ struct mlx5_flow_meter_info *fm,
+ const struct rte_flow_attr *attr,
+ struct rte_flow_error *error)
+{
+ int ret = 0;
+
+ if (priv->sh->meter_aso_en) {
+ struct mlx5_aso_mtr *aso_mtr;
+
+ aso_mtr = container_of(fm, struct mlx5_aso_mtr, fm);
+ if (mlx5_aso_mtr_wait(priv->sh, aso_mtr)) {
+ return rte_flow_error_set(error, ENOENT,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "Timeout in meter configuration");
+ }
+ rte_spinlock_lock(&fm->sl);
+ if (fm->shared || !fm->ref_cnt) {
+ fm->ref_cnt++;
+ } else {
+ rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "Meter cannot be shared");
+ ret = -1;
+ }
+ rte_spinlock_unlock(&fm->sl);
+ } else {
+ rte_spinlock_lock(&fm->sl);
+ if (fm->meter_action) {
+ if (fm->shared &&
+ attr->transfer == fm->transfer &&
+ attr->ingress == fm->ingress &&
+ attr->egress == fm->egress) {
+ fm->ref_cnt++;
+ } else {
+ rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ fm->shared ?
+ "Meter attr not match." :
+ "Meter cannot be shared.");
+ ret = -1;
+ }
+ } else {
+ fm->ingress = attr->ingress;
+ fm->egress = attr->egress;
+ fm->transfer = attr->transfer;
+ fm->ref_cnt = 1;
+ /* This also creates the meter object. */
+ fm->meter_action = mlx5_flow_meter_action_create(priv,
+ fm);
+ if (!fm->meter_action) {
+ fm->ref_cnt = 0;
+ fm->ingress = 0;
+ fm->egress = 0;
+ fm->transfer = 0;
+ rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "Meter action create failed.");
+ ret = -1;
+ }
+ }
+ rte_spinlock_unlock(&fm->sl);
+ }
+ return ret ? -rte_errno : 0;
+}
+
+/**
+ * Detach meter from flow.
+ *
+ * @param [in] priv
+ * Pointer to mlx5 private data.
+ * @param [in] fm
+ * Pointer to flow meter.
+ */
+void
+mlx5_flow_meter_detach(struct mlx5_priv *priv,
+ struct mlx5_flow_meter_info *fm)
+{
+#ifdef HAVE_MLX5_DR_CREATE_ACTION_FLOW_METER
+ rte_spinlock_lock(&fm->sl);
+ MLX5_ASSERT(fm->ref_cnt);
+ if (--fm->ref_cnt == 0 && !priv->sh->meter_aso_en) {
+ mlx5_glue->destroy_flow_action(fm->meter_action);
+ fm->meter_action = NULL;
+ fm->ingress = 0;
+ fm->egress = 0;
+ fm->transfer = 0;
+ }
+ rte_spinlock_unlock(&fm->sl);
+#else
+ (void)priv;
+ (void)fm;
+#endif
+}
+
+/**
+ * Flush meter with Rx queue configuration.
+ *
+ * @param[in] dev
+ * Pointer to Ethernet device.
+ */
+void
+mlx5_flow_meter_rxq_flush(struct rte_eth_dev *dev)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_flow_meter_sub_policy *sub_policy;
+ struct mlx5_flow_meter_policy *mtr_policy;
+ void *entry;
+ uint32_t i, policy_idx;
+
+ if (!priv->mtr_en)
+ return;
+ if (priv->policy_idx_tbl) {
+ MLX5_L3T_FOREACH(priv->policy_idx_tbl, i, entry) {
+ policy_idx = *(uint32_t *)entry;
+ sub_policy = mlx5_ipool_get
+ (priv->sh->ipool[MLX5_IPOOL_MTR_POLICY],
+ policy_idx);
+ if (!sub_policy || !sub_policy->main_policy)
+ continue;
+ mtr_policy = sub_policy->main_policy;
+ if (mtr_policy->is_queue || mtr_policy->is_rss)
+ mlx5_flow_destroy_sub_policy_with_rxq(dev,
+ mtr_policy);
+ }
+ }
+}
+
+/**
+ * Iterate a meter hierarchy and flush all meters and policies if possible.
+ *
+ * @param[in] dev
+ * Pointer to Ethernet device.
+ * @param[in] fm
+ * Pointer to flow meter.
+ * @param[in] mtr_idx
+ * .Meter's index
+ * @param[out] error
+ * Pointer to rte meter error structure.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_flow_meter_flush_hierarchy(struct rte_eth_dev *dev,
+ struct mlx5_flow_meter_info *fm,
+ uint32_t mtr_idx,
+ struct rte_mtr_error *error)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_flow_meter_policy *policy;
+ uint32_t policy_id;
+ struct mlx5_flow_meter_info *next_fm;
+ uint32_t next_mtr_idx;
+ struct mlx5_flow_meter_policy *next_policy = NULL;
+
+ policy = mlx5_flow_meter_policy_find(dev, fm->policy_id, NULL);
+ MLX5_ASSERT(policy);
+ while (!fm->ref_cnt && policy->is_hierarchy) {
+ policy_id = fm->policy_id;
+ next_fm = mlx5_flow_meter_find(priv,
+ policy->act_cnt[RTE_COLOR_GREEN].next_mtr_id,
+ &next_mtr_idx);
+ if (next_fm) {
+ next_policy = mlx5_flow_meter_policy_find(dev,
+ next_fm->policy_id,
+ NULL);
+ MLX5_ASSERT(next_policy);
+ }
+ if (mlx5_flow_meter_params_flush(dev, fm, mtr_idx))
+ return -rte_mtr_error_set(error, ENOTSUP,
+ RTE_MTR_ERROR_TYPE_MTR_ID,
+ NULL,
+ "Failed to flush meter.");
+ if (policy->ref_cnt)
+ break;
+ if (__mlx5_flow_meter_policy_delete(dev, policy_id,
+ policy, error, true))
+ return -rte_errno;
+ mlx5_free(policy);
+ if (!next_fm || !next_policy)
+ break;
+ fm = next_fm;
+ mtr_idx = next_mtr_idx;
+ policy = next_policy;
+ }
+ return 0;
+}
+
+/**
+ * Flush all the hierarchy meters and their policies.
+ *
+ * @param[in] dev
+ * Pointer to Ethernet device.
+ * @param[out] error
+ * Pointer to rte meter error structure.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_flow_meter_flush_all_hierarchies(struct rte_eth_dev *dev,
+ struct rte_mtr_error *error)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_flow_meter_info *fm;
+ struct mlx5_flow_meter_policy *policy;
+ struct mlx5_flow_meter_sub_policy *sub_policy;
+ struct mlx5_flow_meter_info *next_fm;
+ struct mlx5_aso_mtr *aso_mtr;
+ uint32_t mtr_idx = 0;
+ uint32_t i, policy_idx;
+ void *entry;
+
+ if (!priv->mtr_idx_tbl || !priv->policy_idx_tbl)
+ return 0;
+ MLX5_L3T_FOREACH(priv->mtr_idx_tbl, i, entry) {
+ mtr_idx = *(uint32_t *)entry;
+ if (!mtr_idx)
+ continue;
+ aso_mtr = mlx5_aso_meter_by_idx(priv, mtr_idx);
+ fm = &aso_mtr->fm;
+ if (fm->ref_cnt || fm->def_policy)
+ continue;
+ if (mlx5_flow_meter_flush_hierarchy(dev, fm, mtr_idx, error))
+ return -rte_errno;
+ }
+ MLX5_L3T_FOREACH(priv->policy_idx_tbl, i, entry) {
+ policy_idx = *(uint32_t *)entry;
+ sub_policy = mlx5_ipool_get
+ (priv->sh->ipool[MLX5_IPOOL_MTR_POLICY],
+ policy_idx);
+ if (!sub_policy)
+ return -rte_mtr_error_set(error,
+ EINVAL,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+ NULL, "Meter policy invalid.");
+ policy = sub_policy->main_policy;
+ if (!policy || !policy->is_hierarchy || policy->ref_cnt)
+ continue;
+ next_fm = mlx5_flow_meter_find(priv,
+ policy->act_cnt[RTE_COLOR_GREEN].next_mtr_id,
+ &mtr_idx);
+ if (__mlx5_flow_meter_policy_delete(dev, i, policy,
+ error, true))
+ return -rte_mtr_error_set(error,
+ EINVAL,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+ NULL, "Meter policy invalid.");
+ mlx5_free(policy);
+ if (!next_fm || next_fm->ref_cnt || next_fm->def_policy)
+ continue;
+ if (mlx5_flow_meter_flush_hierarchy(dev, next_fm,
+ mtr_idx, error))
+ return -rte_errno;
+ }
+ return 0;
+}
+/**
+ * Flush meter configuration.
+ *
+ * @param[in] dev
+ * Pointer to Ethernet device.
+ * @param[out] error
+ * Pointer to rte meter error structure.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_flow_meter_flush(struct rte_eth_dev *dev, struct rte_mtr_error *error)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct mlx5_legacy_flow_meters *fms = &priv->flow_meters;
+ struct mlx5_flow_meter_profile *fmp;
+ struct mlx5_legacy_flow_meter *legacy_fm;
+ struct mlx5_flow_meter_info *fm;
+ struct mlx5_flow_meter_sub_policy *sub_policy;
+ void *tmp;
+ uint32_t i, mtr_idx, policy_idx;
+ void *entry;
+ struct mlx5_aso_mtr *aso_mtr;
+
+ if (!priv->mtr_en)
+ return 0;
+ if (priv->sh->meter_aso_en) {
+ if (mlx5_flow_meter_flush_all_hierarchies(dev, error))
+ return -rte_errno;
+ if (priv->mtr_idx_tbl) {
+ MLX5_L3T_FOREACH(priv->mtr_idx_tbl, i, entry) {
+ mtr_idx = *(uint32_t *)entry;
+ if (mtr_idx) {
+ aso_mtr =
+ mlx5_aso_meter_by_idx(priv, mtr_idx);
+ fm = &aso_mtr->fm;
+ (void)mlx5_flow_meter_params_flush(dev,
+ fm, mtr_idx);
+ }
+ }
+ mlx5_l3t_destroy(priv->mtr_idx_tbl);
+ priv->mtr_idx_tbl = NULL;
+ }
+ } else {
+ RTE_TAILQ_FOREACH_SAFE(legacy_fm, fms, next, tmp) {
+ fm = &legacy_fm->fm;
+ if (mlx5_flow_meter_params_flush(dev, fm, 0))
+ return -rte_mtr_error_set(error, EINVAL,
+ RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
+ NULL, "MTR object meter profile invalid.");
+ }
+ }
+ if (priv->policy_idx_tbl) {
+ MLX5_L3T_FOREACH(priv->policy_idx_tbl, i, entry) {
+ policy_idx = *(uint32_t *)entry;
+ sub_policy = mlx5_ipool_get
+ (priv->sh->ipool[MLX5_IPOOL_MTR_POLICY],
+ policy_idx);
+ if (!sub_policy)
+ return -rte_mtr_error_set(error,
+ EINVAL,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+ NULL, "MTR object "
+ "meter policy invalid.");
+ if (__mlx5_flow_meter_policy_delete(dev, i,
+ sub_policy->main_policy,
+ error, true))
+ return -rte_mtr_error_set(error,
+ EINVAL,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+ NULL, "MTR object "
+ "meter policy invalid.");
+ mlx5_free(sub_policy->main_policy);
+ }
+ mlx5_l3t_destroy(priv->policy_idx_tbl);
+ priv->policy_idx_tbl = NULL;
+ }
+ if (priv->mtr_profile_tbl) {
+ MLX5_L3T_FOREACH(priv->mtr_profile_tbl, i, entry) {
+ fmp = entry;
+ if (mlx5_flow_meter_profile_delete(dev, fmp->id,
+ error))
+ return -rte_mtr_error_set(error, EINVAL,
+ RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+ NULL, "Fail to destroy "
+ "meter profile.");
+ }
+ mlx5_l3t_destroy(priv->mtr_profile_tbl);
+ priv->mtr_profile_tbl = NULL;
+ }
+ /* Delete default policy table. */
+ mlx5_flow_destroy_def_policy(dev);
+ if (priv->sh->refcnt == 1)
+ mlx5_flow_destroy_mtr_drop_tbls(dev);
+ return 0;
+}