5ad5e14fae012439a2e047b8819f6e9743662072
[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 static const struct rte_mtr_ops mlx5_flow_mtr_ops = {
366         .capabilities_get = mlx5_flow_mtr_cap_get,
367         .meter_profile_add = mlx5_flow_meter_profile_add,
368         .meter_profile_delete = mlx5_flow_meter_profile_delete,
369         .create = NULL,
370         .destroy = NULL,
371         .meter_enable = NULL,
372         .meter_disable = NULL,
373         .meter_profile_update = NULL,
374         .meter_dscp_table_update = NULL,
375         .policer_actions_update = NULL,
376         .stats_update = NULL,
377         .stats_read = NULL,
378 };
379
380 /**
381  * Get meter operations.
382  *
383  * @param dev
384  *   Pointer to Ethernet device structure.
385  * @param arg
386  *   Pointer to set the mtr operations.
387  *
388  * @return
389  *   Always 0.
390  */
391 int
392 mlx5_flow_meter_ops_get(struct rte_eth_dev *dev __rte_unused, void *arg)
393 {
394         *(const struct rte_mtr_ops **)arg = &mlx5_flow_mtr_ops;
395         return 0;
396 }