net/mlx5: support meter modification operations
[dpdk.git] / drivers / net / mlx5 / mlx5_flow_meter.c
1 // SPDX-License-Identifier: BSD-3-Clause
2 /*
3  * Copyright 2018 Mellanox Technologies, Ltd
4  */
5 #include <math.h>
6
7 #include <rte_malloc.h>
8 #include <rte_mtr.h>
9 #include <rte_mtr_driver.h>
10
11 #include "mlx5.h"
12 #include "mlx5_flow.h"
13
14 /**
15  * Find meter profile by id.
16  *
17  * @param priv
18  *   Pointer to mlx5_priv.
19  * @param meter_profile_id
20  *   Meter profile id.
21  *
22  * @return
23  *   Pointer to the profile found on success, NULL otherwise.
24  */
25 static struct mlx5_flow_meter_profile *
26 mlx5_flow_meter_profile_find(struct mlx5_priv *priv, uint32_t meter_profile_id)
27 {
28         struct mlx5_mtr_profiles *fmps = &priv->flow_meter_profiles;
29         struct mlx5_flow_meter_profile *fmp;
30
31         TAILQ_FOREACH(fmp, fmps, next)
32                 if (meter_profile_id == fmp->meter_profile_id)
33                         return fmp;
34         return NULL;
35 }
36
37 /**
38  * Validate the MTR profile.
39  *
40  * @param[in] dev
41  *   Pointer to Ethernet device.
42  * @param[in] meter_profile_id
43  *   Meter profile id.
44  * @param[in] profile
45  *   Pointer to meter profile detail.
46  * @param[out] error
47  *   Pointer to the error structure.
48  *
49  * @return
50  *   0 on success, a negative errno value otherwise and rte_errno is set.
51  */
52 static int
53 mlx5_flow_meter_profile_validate(struct rte_eth_dev *dev,
54                                  uint32_t meter_profile_id,
55                                  struct rte_mtr_meter_profile *profile,
56                                  struct rte_mtr_error *error)
57 {
58         struct mlx5_priv *priv = dev->data->dev_private;
59         struct mlx5_flow_meter_profile *fmp;
60
61         /* Profile must not be NULL. */
62         if (profile == NULL)
63                 return -rte_mtr_error_set(error, EINVAL,
64                                           RTE_MTR_ERROR_TYPE_METER_PROFILE,
65                                           NULL, "Meter profile is null.");
66         /* Meter profile ID must be valid. */
67         if (meter_profile_id == UINT32_MAX)
68                 return -rte_mtr_error_set(error, EINVAL,
69                                           RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
70                                           NULL, "Meter profile id not valid.");
71         /* Meter profile must not exist. */
72         fmp = mlx5_flow_meter_profile_find(priv, meter_profile_id);
73         if (fmp)
74                 return -rte_mtr_error_set(error, EEXIST,
75                                           RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
76                                           NULL,
77                                           "Meter profile already exists.");
78         if (profile->alg == RTE_MTR_SRTCM_RFC2697) {
79                 if (priv->config.hca_attr.qos.srtcm_sup) {
80                         /* Verify support for flow meter parameters. */
81                         if (profile->srtcm_rfc2697.cir > 0 &&
82                             profile->srtcm_rfc2697.cir <= MLX5_SRTCM_CIR_MAX &&
83                             profile->srtcm_rfc2697.cbs > 0 &&
84                             profile->srtcm_rfc2697.cbs <= MLX5_SRTCM_CBS_MAX &&
85                             profile->srtcm_rfc2697.ebs <= MLX5_SRTCM_EBS_MAX)
86                                 return 0;
87                         else
88                                 return -rte_mtr_error_set
89                                              (error, ENOTSUP,
90                                               RTE_MTR_ERROR_TYPE_MTR_PARAMS,
91                                               NULL,
92                                               profile->srtcm_rfc2697.ebs ?
93                                               "Metering value ebs must be 0." :
94                                               "Invalid metering parameters.");
95                 }
96         }
97         return -rte_mtr_error_set(error, ENOTSUP,
98                                   RTE_MTR_ERROR_TYPE_METER_PROFILE,
99                                   NULL, "Metering algorithm not supported.");
100 }
101
102 /**
103  * Calculate mantissa and exponent for cir.
104  *
105  * @param[in] cir
106  *   Value to be calculated.
107  * @param[out] man
108  *   Pointer to the mantissa.
109  * @param[out] exp
110  *   Pointer to the exp.
111  */
112 static void
113 mlx5_flow_meter_cir_man_exp_calc(int64_t cir, uint8_t *man, uint8_t *exp)
114 {
115         int64_t _cir;
116         int64_t delta = INT64_MAX;
117         uint8_t _man = 0;
118         uint8_t _exp = 0;
119         uint64_t m, e;
120
121         for (m = 0; m <= 0xFF; m++) { /* man width 8 bit */
122                 for (e = 0; e <= 0x1F; e++) { /* exp width 5bit */
123                         _cir = (1000000000ULL * m) >> e;
124                         if (llabs(cir - _cir) <= delta) {
125                                 delta = llabs(cir - _cir);
126                                 _man = m;
127                                 _exp = e;
128                         }
129                 }
130         }
131         *man = _man;
132         *exp = _exp;
133 }
134
135 /**
136  * Calculate mantissa and exponent for xbs.
137  *
138  * @param[in] xbs
139  *   Value to be calculated.
140  * @param[out] man
141  *   Pointer to the mantissa.
142  * @param[out] exp
143  *   Pointer to the exp.
144  */
145 static void
146 mlx5_flow_meter_xbs_man_exp_calc(uint64_t xbs, uint8_t *man, uint8_t *exp)
147 {
148         int _exp;
149         double _man;
150
151         /* Special case xbs == 0 ? both exp and matissa are 0. */
152         if (xbs == 0) {
153                 *man = 0;
154                 *exp = 0;
155                 return;
156         }
157         /* xbs = xbs_mantissa * 2^xbs_exponent */
158         _man = frexp(xbs, &_exp);
159         _man = _man * pow(2, MLX5_MAN_WIDTH);
160         _exp = _exp - MLX5_MAN_WIDTH;
161         *man = (uint8_t)ceil(_man);
162         *exp = _exp;
163 }
164
165 /**
166  * Fill the prm meter parameter.
167  *
168  * @param[in,out] fmp
169  *   Pointer to meter profie to be converted.
170  * @param[out] error
171  *   Pointer to the error structure.
172  *
173  * @return
174  *   0 on success, a negative errno value otherwise and rte_errno is set.
175  */
176 static int
177 mlx5_flow_meter_param_fill(struct mlx5_flow_meter_profile *fmp,
178                           struct rte_mtr_error *error)
179 {
180         struct mlx5_flow_meter_srtcm_rfc2697_prm *srtcm = &fmp->srtcm_prm;
181         uint8_t man, exp;
182
183         if (fmp->profile.alg != RTE_MTR_SRTCM_RFC2697)
184                 return -rte_mtr_error_set(error, ENOTSUP,
185                                 RTE_MTR_ERROR_TYPE_METER_PROFILE,
186                                 NULL, "Metering algorithm not supported.");
187          /* cbs = cbs_mantissa * 2^cbs_exponent */
188         mlx5_flow_meter_xbs_man_exp_calc(fmp->profile.srtcm_rfc2697.cbs,
189                                     &man, &exp);
190         srtcm->cbs_mantissa = man;
191         srtcm->cbs_exponent = exp;
192         /* Check if cbs mantissa is too large. */
193         if (srtcm->cbs_exponent != exp)
194                 return -rte_mtr_error_set(error, EINVAL,
195                                           RTE_MTR_ERROR_TYPE_MTR_PARAMS, NULL,
196                                           "Metering profile parameter cbs is"
197                                           " invalid.");
198         /* ebs = ebs_mantissa * 2^ebs_exponent */
199         mlx5_flow_meter_xbs_man_exp_calc(fmp->profile.srtcm_rfc2697.ebs,
200                                     &man, &exp);
201         srtcm->ebs_mantissa = man;
202         srtcm->ebs_exponent = exp;
203         /* Check if ebs mantissa is too large. */
204         if (srtcm->ebs_exponent != exp)
205                 return -rte_mtr_error_set(error, EINVAL,
206                                           RTE_MTR_ERROR_TYPE_MTR_PARAMS, NULL,
207                                           "Metering profile parameter ebs is"
208                                           " invalid.");
209         /* cir = 8G * cir_mantissa * 1/(2^cir_exponent)) Bytes/Sec */
210         mlx5_flow_meter_cir_man_exp_calc(fmp->profile.srtcm_rfc2697.cir,
211                                     &man, &exp);
212         srtcm->cir_mantissa = man;
213         srtcm->cir_exponent = exp;
214         /* Check if cir mantissa is too large. */
215         if (srtcm->cir_exponent != exp)
216                 return -rte_mtr_error_set(error, EINVAL,
217                                           RTE_MTR_ERROR_TYPE_MTR_PARAMS, NULL,
218                                           "Metering profile parameter cir is"
219                                           " invalid.");
220         return 0;
221 }
222
223 /**
224  * Callback to get MTR capabilities.
225  *
226  * @param[in] dev
227  *   Pointer to Ethernet device.
228  * @param[out] cap
229  *   Pointer to save MTR capabilities.
230  * @param[out] error
231  *   Pointer to the error structure.
232  *
233  * @return
234  *   0 on success, a negative errno value otherwise and rte_errno is set.
235  */
236 static int
237 mlx5_flow_mtr_cap_get(struct rte_eth_dev *dev,
238                  struct rte_mtr_capabilities *cap,
239                  struct rte_mtr_error *error __rte_unused)
240 {
241         struct mlx5_priv *priv = dev->data->dev_private;
242         struct mlx5_hca_qos_attr *qattr = &priv->config.hca_attr.qos;
243
244         if (!priv->mtr_en)
245                 return -rte_mtr_error_set(error, ENOTSUP,
246                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
247                                           "Meter is not support");
248         memset(cap, 0, sizeof(*cap));
249         cap->n_max = 1 << qattr->log_max_flow_meter;
250         cap->n_shared_max = cap->n_max;
251         cap->identical = 1;
252         cap->shared_identical = 1;
253         cap->shared_n_flows_per_mtr_max = 4 << 20;
254         /* 2M flows can share the same meter. */
255         cap->chaining_n_mtrs_per_flow_max = 1; /* Chaining is not supported. */
256         cap->meter_srtcm_rfc2697_n_max = qattr->srtcm_sup ? cap->n_max : 0;
257         cap->meter_rate_max = 1ULL << 40; /* 1 Tera tokens per sec. */
258         cap->policer_action_drop_supported = 1;
259         cap->stats_mask = RTE_MTR_STATS_N_BYTES_DROPPED |
260                           RTE_MTR_STATS_N_PKTS_DROPPED;
261         return 0;
262 }
263
264 /**
265  * Callback to add MTR profile.
266  *
267  * @param[in] dev
268  *   Pointer to Ethernet device.
269  * @param[in] meter_profile_id
270  *   Meter profile id.
271  * @param[in] profile
272  *   Pointer to meter profile detail.
273  * @param[out] error
274  *   Pointer to the error structure.
275  *
276  * @return
277  *   0 on success, a negative errno value otherwise and rte_errno is set.
278  */
279 static int
280 mlx5_flow_meter_profile_add(struct rte_eth_dev *dev,
281                        uint32_t meter_profile_id,
282                        struct rte_mtr_meter_profile *profile,
283                        struct rte_mtr_error *error)
284 {
285         struct mlx5_priv *priv = dev->data->dev_private;
286         struct mlx5_mtr_profiles *fmps = &priv->flow_meter_profiles;
287         struct mlx5_flow_meter_profile *fmp;
288         int ret;
289
290         if (!priv->mtr_en)
291                 return -rte_mtr_error_set(error, ENOTSUP,
292                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
293                                           "Meter is not support");
294         /* Check input params. */
295         ret = mlx5_flow_meter_profile_validate(dev, meter_profile_id,
296                                                profile, error);
297         if (ret)
298                 return ret;
299         /* Meter profile memory allocation. */
300         fmp = rte_calloc(__func__, 1, sizeof(struct mlx5_flow_meter_profile),
301                          RTE_CACHE_LINE_SIZE);
302         if (fmp == NULL)
303                 return -rte_mtr_error_set(error, ENOMEM,
304                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
305                                           NULL, "Meter profile memory "
306                                           "alloc failed.");
307         /* Fill profile info. */
308         fmp->meter_profile_id = meter_profile_id;
309         fmp->profile = *profile;
310         /* Fill the flow meter parameters for the PRM. */
311         ret = mlx5_flow_meter_param_fill(fmp, error);
312         if (ret)
313                 goto error;
314         /* Add to list. */
315         TAILQ_INSERT_TAIL(fmps, fmp, next);
316         return 0;
317 error:
318         rte_free(fmp);
319         return ret;
320 }
321
322 /**
323  * Callback to delete MTR profile.
324  *
325  * @param[in] dev
326  *   Pointer to Ethernet device.
327  * @param[in] meter_profile_id
328  *   Meter profile id.
329  * @param[out] error
330  *   Pointer to the error structure.
331  *
332  * @return
333  *   0 on success, a negative errno value otherwise and rte_errno is set.
334  */
335 static int
336 mlx5_flow_meter_profile_delete(struct rte_eth_dev *dev,
337                           uint32_t meter_profile_id,
338                           struct rte_mtr_error *error)
339 {
340         struct mlx5_priv *priv = dev->data->dev_private;
341         struct mlx5_flow_meter_profile *fmp;
342
343         if (!priv->mtr_en)
344                 return -rte_mtr_error_set(error, ENOTSUP,
345                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
346                                           "Meter is not support");
347         /* Meter profile must exist. */
348         fmp = mlx5_flow_meter_profile_find(priv, meter_profile_id);
349         if (fmp == NULL)
350                 return -rte_mtr_error_set(error, ENOENT,
351                                           RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
352                                           &meter_profile_id,
353                                           "Meter profile id invalid.");
354         /* Check profile is unused. */
355         if (fmp->ref_cnt)
356                 return -rte_mtr_error_set(error, EBUSY,
357                                           RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
358                                           NULL, "Meter profile in use.");
359         /* Remove from list. */
360         TAILQ_REMOVE(&priv->flow_meter_profiles, fmp, next);
361         rte_free(fmp);
362         return 0;
363 }
364
365 /**
366  * Convert wrong color setting action to verbose error.
367  *
368  * @param[in] action
369  *   Policy color action.
370  *
371  * @return
372  *   Verbose meter color error type.
373  */
374 static inline enum rte_mtr_error_type
375 action2error(enum rte_mtr_policer_action action)
376 {
377         switch (action) {
378         case MTR_POLICER_ACTION_COLOR_GREEN:
379                 return RTE_MTR_ERROR_TYPE_POLICER_ACTION_GREEN;
380         case MTR_POLICER_ACTION_COLOR_YELLOW:
381                 return RTE_MTR_ERROR_TYPE_POLICER_ACTION_YELLOW;
382         case MTR_POLICER_ACTION_COLOR_RED:
383                 return RTE_MTR_ERROR_TYPE_POLICER_ACTION_RED;
384         default:
385                 break;
386         }
387         return RTE_MTR_ERROR_TYPE_UNSPECIFIED;
388 }
389
390 /**
391  * Check meter validation.
392  *
393  * @param[in] priv
394  *   Pointer to mlx5 private data structure.
395  * @param[in] meter_id
396  *   Meter id.
397  * @param[in] params
398  *   Pointer to rte meter parameters.
399  * @param[out] error
400  *   Pointer to rte meter error structure.
401  *
402  * @return
403  *   0 on success, a negative errno value otherwise and rte_errno is set.
404  */
405 static int
406 mlx5_flow_meter_validate(struct mlx5_priv *priv, uint32_t meter_id,
407                          struct rte_mtr_params *params,
408                          struct rte_mtr_error *error)
409 {
410         static enum rte_mtr_policer_action
411                                 valid_recol_action[RTE_COLORS] = {
412                                                MTR_POLICER_ACTION_COLOR_GREEN,
413                                                MTR_POLICER_ACTION_COLOR_YELLOW,
414                                                MTR_POLICER_ACTION_COLOR_RED };
415         int i;
416
417         /* Meter params must not be NULL. */
418         if (params == NULL)
419                 return -rte_mtr_error_set(error, EINVAL,
420                                           RTE_MTR_ERROR_TYPE_MTR_PARAMS,
421                                           NULL, "Meter object params null.");
422         /* Previous meter color is not supported. */
423         if (params->use_prev_mtr_color)
424                 return -rte_mtr_error_set(error, ENOTSUP,
425                                           RTE_MTR_ERROR_TYPE_MTR_PARAMS,
426                                           NULL,
427                                           "Previous meter color "
428                                           "not supported.");
429         /* Validate policer settings. */
430         for (i = 0; i < RTE_COLORS; i++)
431                 if (params->action[i] != valid_recol_action[i] &&
432                     params->action[i] != MTR_POLICER_ACTION_DROP)
433                         return -rte_mtr_error_set
434                                         (error, ENOTSUP,
435                                          action2error(params->action[i]), NULL,
436                                          "Recolor action not supported.");
437         /* Validate meter id. */
438         if (mlx5_flow_meter_find(priv, meter_id))
439                 return -rte_mtr_error_set(error, EEXIST,
440                                           RTE_MTR_ERROR_TYPE_MTR_ID, NULL,
441                                           "Meter object already exists.");
442         return 0;
443 }
444
445 /**
446  * Modify the flow meter action.
447  *
448  * @param[in] priv
449  *   Pointer to mlx5 private data structure.
450  * @param[in] fm
451  *   Pointer to flow meter to be modified.
452  * @param[in] srtcm
453  *   Pointer to meter srtcm description parameter.
454  * @param[in] modify_bits
455  *   The bit in srtcm to be updated.
456  * @param[in] active_state
457  *   The state to be updated.
458  * @return
459  *   0 on success, o negative value otherwise.
460  */
461 static int
462 mlx5_flow_meter_action_modify(struct mlx5_priv *priv,
463                 struct mlx5_flow_meter *fm,
464                 const struct mlx5_flow_meter_srtcm_rfc2697_prm *srtcm,
465                 uint64_t modify_bits, uint32_t active_state)
466 {
467 #ifdef HAVE_MLX5_DR_CREATE_ACTION_FLOW_METER
468         uint32_t in[MLX5_ST_SZ_DW(flow_meter_parameters)] = { 0 };
469         uint32_t *attr;
470         struct mlx5dv_dr_flow_meter_attr mod_attr = { 0 };
471         int ret;
472
473         /* Fill command parameters. */
474         mod_attr.reg_c_index = priv->mtr_color_reg - REG_C_0;
475         mod_attr.flow_meter_parameter = in;
476         mod_attr.flow_meter_parameter_sz = fm->mfts->fmp_size;
477         if (modify_bits & MLX5_FLOW_METER_OBJ_MODIFY_FIELD_ACTIVE)
478                 mod_attr.active = !!active_state;
479         else
480                 mod_attr.active = 0;
481         attr = in;
482         if (modify_bits & MLX5_FLOW_METER_OBJ_MODIFY_FIELD_CBS) {
483                 MLX5_SET(flow_meter_parameters,
484                          attr, cbs_exponent, srtcm->cbs_exponent);
485                 MLX5_SET(flow_meter_parameters,
486                          attr, cbs_mantissa, srtcm->cbs_mantissa);
487         }
488         if (modify_bits & MLX5_FLOW_METER_OBJ_MODIFY_FIELD_CIR) {
489                 MLX5_SET(flow_meter_parameters,
490                          attr, cir_exponent, srtcm->cir_exponent);
491                 MLX5_SET(flow_meter_parameters,
492                          attr, cir_mantissa, srtcm->cir_mantissa);
493         }
494         if (modify_bits & MLX5_FLOW_METER_OBJ_MODIFY_FIELD_EBS) {
495                 MLX5_SET(flow_meter_parameters,
496                          attr, ebs_exponent, srtcm->ebs_exponent);
497                 MLX5_SET(flow_meter_parameters,
498                          attr, ebs_mantissa, srtcm->ebs_mantissa);
499         }
500         /* Apply modifications to meter only if it was created. */
501         if (fm->mfts->meter_action) {
502                 ret = mlx5_glue->dv_modify_flow_action_meter
503                                         (fm->mfts->meter_action, &mod_attr,
504                                         rte_cpu_to_be_64(modify_bits));
505                 if (ret)
506                         return ret;
507         }
508         /* Update succeedded modify meter parameters. */
509         if (modify_bits & MLX5_FLOW_METER_OBJ_MODIFY_FIELD_ACTIVE)
510                 fm->active_state = !!active_state;
511         attr = fm->mfts->fmp;
512         if (modify_bits & MLX5_FLOW_METER_OBJ_MODIFY_FIELD_CBS) {
513                 MLX5_SET(flow_meter_parameters,
514                          attr, cbs_exponent, srtcm->cbs_exponent);
515                 MLX5_SET(flow_meter_parameters,
516                          attr, cbs_mantissa, srtcm->cbs_mantissa);
517         }
518         if (modify_bits & MLX5_FLOW_METER_OBJ_MODIFY_FIELD_CIR) {
519                 MLX5_SET(flow_meter_parameters,
520                          attr, cir_exponent, srtcm->cir_exponent);
521                 MLX5_SET(flow_meter_parameters,
522                          attr, cir_mantissa, srtcm->cir_mantissa);
523         }
524         if (modify_bits & MLX5_FLOW_METER_OBJ_MODIFY_FIELD_EBS) {
525                 MLX5_SET(flow_meter_parameters,
526                          attr, ebs_exponent, srtcm->ebs_exponent);
527                 MLX5_SET(flow_meter_parameters,
528                          attr, ebs_mantissa, srtcm->ebs_mantissa);
529         }
530
531         return 0;
532 #else
533         (void)priv;
534         (void)fm;
535         (void)srtcm;
536         (void)modify_bits;
537         (void)active_state;
538         return -ENOTSUP;
539 #endif
540 }
541
542 /**
543  * Create meter rules.
544  *
545  * @param[in] dev
546  *   Pointer to Ethernet device.
547  * @param[in] meter_id
548  *   Meter id.
549  * @param[in] params
550  *   Pointer to rte meter parameters.
551  * @param[in] shared
552  *   Meter shared with other flow or not.
553  * @param[out] error
554  *   Pointer to rte meter error structure.
555  *
556  * @return
557  *   0 on success, a negative errno value otherwise and rte_errno is set.
558  */
559 static int
560 mlx5_flow_meter_create(struct rte_eth_dev *dev, uint32_t meter_id,
561                        struct rte_mtr_params *params, int shared,
562                        struct rte_mtr_error *error)
563 {
564         struct mlx5_priv *priv = dev->data->dev_private;
565         struct mlx5_flow_meters *fms = &priv->flow_meters;
566         struct mlx5_flow_meter_profile *fmp;
567         struct mlx5_flow_meter *fm;
568         const struct rte_flow_attr attr = {
569                                 .ingress = 1,
570                                 .egress = 1,
571                                 .transfer = priv->config.dv_esw_en ? 1 : 0,
572                         };
573         int ret;
574
575         if (!priv->mtr_en)
576                 return -rte_mtr_error_set(error, ENOTSUP,
577                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
578                                           "Meter is not support");
579         /* Validate the parameters. */
580         ret = mlx5_flow_meter_validate(priv, meter_id, params, error);
581         if (ret)
582                 return ret;
583         /* Meter profile must exist. */
584         fmp = mlx5_flow_meter_profile_find(priv, params->meter_profile_id);
585         if (fmp == NULL)
586                 return -rte_mtr_error_set(error, ENOENT,
587                                           RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
588                                           NULL, "Meter profile id not valid.");
589         /* Allocate the flow meter memory. */
590         fm = rte_calloc(__func__, 1,
591                         sizeof(struct mlx5_flow_meter), RTE_CACHE_LINE_SIZE);
592         if (fm == NULL)
593                 return -rte_mtr_error_set(error, ENOMEM,
594                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
595                                           "Memory alloc failed for meter.");
596         /* Fill the flow meter parameters. */
597         fm->meter_id = meter_id;
598         fm->profile = fmp;
599         fm->params = *params;
600         fm->mfts = mlx5_flow_create_mtr_tbls(dev);
601         if (!fm->mfts)
602                 goto error;
603         ret = mlx5_flow_create_policer_rules(dev, fm, &attr);
604         if (ret)
605                 goto error;
606         /* Add to the flow meter list. */
607         TAILQ_INSERT_TAIL(fms, fm, next);
608         fm->active_state = 1; /* Config meter starts as active. */
609         fm->shared = !!shared;
610         fm->profile->ref_cnt++;
611         return 0;
612 error:
613         mlx5_flow_destroy_policer_rules(dev, fm, &attr);
614         mlx5_flow_destroy_mtr_tbls(dev, fm->mfts);
615         rte_free(fm);
616         return -rte_mtr_error_set(error, -ret,
617                                   RTE_MTR_ERROR_TYPE_UNSPECIFIED,
618                                   NULL, "Failed to create devx meter.");
619 }
620
621 /**
622  * Destroy meter rules.
623  *
624  * @param[in] dev
625  *   Pointer to Ethernet device.
626  * @param[in] meter_id
627  *   Meter id.
628  * @param[out] error
629  *   Pointer to rte meter error structure.
630  *
631  * @return
632  *   0 on success, a negative errno value otherwise and rte_errno is set.
633  */
634 static int
635 mlx5_flow_meter_destroy(struct rte_eth_dev *dev, uint32_t meter_id,
636                         struct rte_mtr_error *error)
637 {
638         struct mlx5_priv *priv = dev->data->dev_private;
639         struct mlx5_flow_meters *fms = &priv->flow_meters;
640         struct mlx5_flow_meter_profile *fmp;
641         struct mlx5_flow_meter *fm;
642         const struct rte_flow_attr attr = {
643                                 .ingress = 1,
644                                 .egress = 1,
645                                 .transfer = priv->config.dv_esw_en ? 1 : 0,
646                         };
647
648         if (!priv->mtr_en)
649                 return -rte_mtr_error_set(error, ENOTSUP,
650                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
651                                           "Meter is not support");
652         /* Meter object must exist. */
653         fm = mlx5_flow_meter_find(priv, meter_id);
654         if (fm == NULL)
655                 return -rte_mtr_error_set(error, ENOENT,
656                                           RTE_MTR_ERROR_TYPE_MTR_ID,
657                                           NULL, "Meter object id not valid.");
658         /* Meter object must not have any owner. */
659         if (fm->ref_cnt > 0)
660                 return -rte_mtr_error_set(error, EBUSY,
661                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
662                                           NULL, "Meter object is being used.");
663         /* Get the meter profile. */
664         fmp = fm->profile;
665         RTE_ASSERT(fmp);
666         /* Update dependencies. */
667         fmp->ref_cnt--;
668         /* Remove from the flow meter list. */
669         TAILQ_REMOVE(fms, fm, next);
670         /* Free meter flow table */
671         mlx5_flow_destroy_policer_rules(dev, fm, &attr);
672         mlx5_flow_destroy_mtr_tbls(dev, fm->mfts);
673         rte_free(fm);
674         return 0;
675 }
676
677 /**
678  * Modify meter state.
679  *
680  * @param[in] priv
681  *   Pointer to mlx5 private data structure.
682  * @param[in] fm
683  *   Pointer to flow meter.
684  * @param[in] new_state
685  *   New state to update.
686  * @param[out] error
687  *   Pointer to rte meter error structure.
688  *
689  * @return
690  *   0 on success, a negative errno value otherwise and rte_errno is set.
691  */
692 static int
693 mlx5_flow_meter_modify_state(struct mlx5_priv *priv,
694                              struct mlx5_flow_meter *fm,
695                              uint32_t new_state,
696                              struct rte_mtr_error *error)
697 {
698         static const struct mlx5_flow_meter_srtcm_rfc2697_prm srtcm = {
699                 .cbs_exponent = 20,
700                 .cbs_mantissa = 191,
701                 .cir_exponent = 0,
702                 .cir_mantissa = 200,
703                 .ebs_exponent = 0,
704                 .ebs_mantissa = 0,
705         };
706         uint64_t modify_bits = MLX5_FLOW_METER_OBJ_MODIFY_FIELD_CBS |
707                                MLX5_FLOW_METER_OBJ_MODIFY_FIELD_CIR;
708         int ret;
709
710         if (new_state == MLX5_FLOW_METER_DISABLE)
711                 ret = mlx5_flow_meter_action_modify(priv, fm, &srtcm,
712                                                     modify_bits, 0);
713         else
714                 ret = mlx5_flow_meter_action_modify(priv, fm,
715                                                    &fm->profile->srtcm_prm,
716                                                     modify_bits, 0);
717         if (ret)
718                 return -rte_mtr_error_set(error, -ret,
719                                           RTE_MTR_ERROR_TYPE_MTR_PARAMS,
720                                           NULL,
721                                           new_state ?
722                                           "Failed to enable meter." :
723                                           "Failed to disable meter.");
724         return 0;
725 }
726
727 /**
728  * Callback to enable flow meter.
729  *
730  * @param[in] dev
731  *   Pointer to Ethernet device.
732  * @param[in] meter_id
733  *   Meter id.
734  * @param[out] error
735  *   Pointer to rte meter error structure.
736  *
737  * @return
738  *   0 on success, a negative errno value otherwise and rte_errno is set.
739  */
740 static int
741 mlx5_flow_meter_enable(struct rte_eth_dev *dev,
742                        uint32_t meter_id,
743                        struct rte_mtr_error *error)
744 {
745         struct mlx5_priv *priv = dev->data->dev_private;
746         struct mlx5_flow_meter *fm;
747         int ret;
748
749         if (!priv->mtr_en)
750                 return -rte_mtr_error_set(error, ENOTSUP,
751                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
752                                           "Meter is not support");
753         /* Meter object must exist. */
754         fm = mlx5_flow_meter_find(priv, meter_id);
755         if (fm == NULL)
756                 return -rte_mtr_error_set(error, ENOENT,
757                                           RTE_MTR_ERROR_TYPE_MTR_ID,
758                                           NULL, "Meter not found.");
759         if (fm->active_state == MLX5_FLOW_METER_ENABLE)
760                 return 0;
761         ret = mlx5_flow_meter_modify_state(priv, fm, MLX5_FLOW_METER_ENABLE,
762                                            error);
763         if (!ret)
764                 fm->active_state = MLX5_FLOW_METER_ENABLE;
765         return ret;
766 }
767
768 /**
769  * Callback to disable flow meter.
770  *
771  * @param[in] dev
772  *   Pointer to Ethernet device.
773  * @param[in] meter_id
774  *   Meter id.
775  * @param[out] error
776  *   Pointer to rte meter error structure.
777  *
778  * @return
779  *   0 on success, a negative errno value otherwise and rte_errno is set.
780  */
781 static int
782 mlx5_flow_meter_disable(struct rte_eth_dev *dev,
783                         uint32_t meter_id,
784                         struct rte_mtr_error *error)
785 {
786         struct mlx5_priv *priv = dev->data->dev_private;
787         struct mlx5_flow_meter *fm;
788         int ret;
789
790         if (!priv->mtr_en)
791                 return -rte_mtr_error_set(error, ENOTSUP,
792                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
793                                           "Meter is not support");
794         /* Meter object must exist. */
795         fm = mlx5_flow_meter_find(priv, meter_id);
796         if (fm == NULL)
797                 return -rte_mtr_error_set(error, ENOENT,
798                                           RTE_MTR_ERROR_TYPE_MTR_ID,
799                                           NULL, "Meter not found.");
800         if (fm->active_state == MLX5_FLOW_METER_DISABLE)
801                 return 0;
802         ret = mlx5_flow_meter_modify_state(priv, fm, MLX5_FLOW_METER_DISABLE,
803                                            error);
804         if (!ret)
805                 fm->active_state = MLX5_FLOW_METER_DISABLE;
806         return ret;
807 }
808
809 static const struct rte_mtr_ops mlx5_flow_mtr_ops = {
810         .capabilities_get = mlx5_flow_mtr_cap_get,
811         .meter_profile_add = mlx5_flow_meter_profile_add,
812         .meter_profile_delete = mlx5_flow_meter_profile_delete,
813         .create = mlx5_flow_meter_create,
814         .destroy = mlx5_flow_meter_destroy,
815         .meter_enable = mlx5_flow_meter_enable,
816         .meter_disable = mlx5_flow_meter_disable,
817         .meter_profile_update = NULL,
818         .meter_dscp_table_update = NULL,
819         .policer_actions_update = NULL,
820         .stats_update = NULL,
821         .stats_read = NULL,
822 };
823
824 /**
825  * Get meter operations.
826  *
827  * @param dev
828  *   Pointer to Ethernet device structure.
829  * @param arg
830  *   Pointer to set the mtr operations.
831  *
832  * @return
833  *   Always 0.
834  */
835 int
836 mlx5_flow_meter_ops_get(struct rte_eth_dev *dev __rte_unused, void *arg)
837 {
838         *(const struct rte_mtr_ops **)arg = &mlx5_flow_mtr_ops;
839         return 0;
840 }
841
842 /**
843  * Find meter by id.
844  *
845  * @param priv
846  *   Pointer to mlx5_priv.
847  * @param meter_id
848  *   Meter id.
849  *
850  * @return
851  *   Pointer to the profile found on success, NULL otherwise.
852  */
853 struct mlx5_flow_meter *
854 mlx5_flow_meter_find(struct mlx5_priv *priv, uint32_t meter_id)
855 {
856         struct mlx5_flow_meters *fms = &priv->flow_meters;
857         struct mlx5_flow_meter *fm;
858
859         TAILQ_FOREACH(fm, fms, next)
860                 if (meter_id == fm->meter_id)
861                         return fm;
862         return NULL;
863 }