New upstream version 18.11-rc1
[deb_dpdk.git] / drivers / net / mvpp2 / mrvl_mtr.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  * Copyright(c) 2018 Marvell International Ltd.
4  * Copyright(c) 2018 Semihalf.
5  * All rights reserved.
6  */
7
8 #include <rte_log.h>
9 #include <rte_malloc.h>
10
11 #include "mrvl_mtr.h"
12
13 /** Maximum meter rate */
14 #define MRVL_SRTCM_RFC2697_CIR_MAX 1023000
15
16 /** Invalid plcr bit */
17 #define MRVL_PLCR_BIT_INVALID -1
18
19 /**
20  * Return meter object capabilities.
21  *
22  * @param dev Pointer to the device (unused).
23  * @param cap Pointer to the meter object capabilities.
24  * @param error Pointer to the error (unused).
25  * @returns 0 always.
26  */
27 static int
28 mrvl_capabilities_get(struct rte_eth_dev *dev __rte_unused,
29                           struct rte_mtr_capabilities *cap,
30                           struct rte_mtr_error *error __rte_unused)
31 {
32         struct rte_mtr_capabilities capa = {
33                 .n_max = PP2_CLS_PLCR_NUM,
34                 .n_shared_max = PP2_CLS_PLCR_NUM,
35                 .shared_n_flows_per_mtr_max = -1,
36                 .meter_srtcm_rfc2697_n_max = PP2_CLS_PLCR_NUM,
37                 .meter_rate_max = MRVL_SRTCM_RFC2697_CIR_MAX,
38         };
39
40         memcpy(cap, &capa, sizeof(capa));
41
42         return 0;
43 }
44
45 /**
46  * Get profile using it's id.
47  *
48  * @param priv Pointer to the port's private data.
49  * @param meter_profile_id Profile id used by the meter.
50  * @returns Pointer to the profile if exists, NULL otherwise.
51  */
52 static struct mrvl_mtr_profile *
53 mrvl_mtr_profile_from_id(struct mrvl_priv *priv, uint32_t meter_profile_id)
54 {
55         struct mrvl_mtr_profile *profile = NULL;
56
57         LIST_FOREACH(profile, &priv->profiles, next)
58                 if (profile->profile_id == meter_profile_id)
59                         break;
60
61         return profile;
62 }
63
64 /**
65  * Add profile to the list of profiles.
66  *
67  * @param dev Pointer to the device.
68  * @param meter_profile_id Id of the new profile.
69  * @param profile Pointer to the profile configuration.
70  * @param error Pointer to the error.
71  * @returns 0 on success, negative value otherwise.
72  */
73 static int
74 mrvl_meter_profile_add(struct rte_eth_dev *dev, uint32_t meter_profile_id,
75                        struct rte_mtr_meter_profile *profile,
76                        struct rte_mtr_error *error)
77 {
78         struct mrvl_priv *priv = dev->data->dev_private;
79         struct mrvl_mtr_profile *prof;
80
81         if (!profile)
82                 return -rte_mtr_error_set(error, EINVAL,
83                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
84                                           NULL, NULL);
85
86         if (profile->alg != RTE_MTR_SRTCM_RFC2697)
87                 return -rte_mtr_error_set(error, EINVAL,
88                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
89                                           NULL,
90                                           "Only srTCM RFC 2697 is supported\n");
91
92         prof = mrvl_mtr_profile_from_id(priv, meter_profile_id);
93         if (prof)
94                 return -rte_mtr_error_set(error, EEXIST,
95                                           RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
96                                           NULL, "Profile id already exists\n");
97
98         prof = rte_zmalloc_socket(NULL, sizeof(*prof), 0, rte_socket_id());
99         if (!prof)
100                 return -rte_mtr_error_set(error, ENOMEM,
101                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
102                                           NULL, NULL);
103
104         prof->profile_id = meter_profile_id;
105         memcpy(&prof->profile, profile, sizeof(*profile));
106
107         LIST_INSERT_HEAD(&priv->profiles, prof, next);
108
109         return 0;
110 }
111
112 /**
113  * Remove profile from the list of profiles.
114  *
115  * @param dev Pointer to the device.
116  * @param meter_profile_id Id of the profile to remove.
117  * @param error Pointer to the error.
118  * @returns 0 on success, negative value otherwise.
119  */
120 static int
121 mrvl_meter_profile_delete(struct rte_eth_dev *dev,
122                               uint32_t meter_profile_id,
123                               struct rte_mtr_error *error)
124 {
125         struct mrvl_priv *priv = dev->data->dev_private;
126         struct mrvl_mtr_profile *profile;
127
128         profile = mrvl_mtr_profile_from_id(priv, meter_profile_id);
129         if (!profile)
130                 return -rte_mtr_error_set(error, ENODEV,
131                                           RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
132                                           NULL, "Profile id does not exist\n");
133
134         if (profile->refcnt)
135                 return -rte_mtr_error_set(error, EPERM,
136                                           RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
137                                           NULL, "Profile is used\n");
138
139         LIST_REMOVE(profile, next);
140         rte_free(profile);
141
142         return 0;
143 }
144
145 /**
146  * Get meter using it's id.
147  *
148  * @param priv Pointer to port's private data.
149  * @param mtr_id Id of the meter.
150  * @returns Pointer to the meter if exists, NULL otherwise.
151  */
152 static struct mrvl_mtr *
153 mrvl_mtr_from_id(struct mrvl_priv *priv, uint32_t mtr_id)
154 {
155         struct mrvl_mtr *mtr = NULL;
156
157         LIST_FOREACH(mtr, &priv->mtrs, next)
158                 if (mtr->mtr_id == mtr_id)
159                         break;
160
161         return mtr;
162 }
163
164 /**
165  * Reserve a policer bit in a bitmap.
166  *
167  * @param plcrs Pointer to the policers bitmap.
168  * @returns Reserved bit number on success, negative value otherwise.
169  */
170 static int
171 mrvl_reserve_plcr(uint32_t *plcrs)
172 {
173         uint32_t i, num;
174
175         num = PP2_CLS_PLCR_NUM;
176         if (num > sizeof(uint32_t) * 8) {
177                 num = sizeof(uint32_t) * 8;
178                 MRVL_LOG(WARNING, "Plcrs number was limited to 32.");
179         }
180
181         for (i = 0; i < num; i++) {
182                 uint32_t bit = BIT(i);
183
184                 if (!(*plcrs & bit)) {
185                         *plcrs |= bit;
186
187                         return i;
188                 }
189         }
190
191         return -1;
192 }
193
194 /**
195  * Enable meter object.
196  *
197  * @param dev Pointer to the device.
198  * @param mtr_id Id of the meter.
199  * @param error Pointer to the error.
200  * @returns 0 in success, negative value otherwise.
201  */
202 static int
203 mrvl_meter_enable(struct rte_eth_dev *dev, uint32_t mtr_id,
204                   struct rte_mtr_error *error)
205 {
206         struct mrvl_priv *priv = dev->data->dev_private;
207         struct mrvl_mtr *mtr = mrvl_mtr_from_id(priv, mtr_id);
208         struct pp2_cls_plcr_params params;
209         char match[MRVL_MATCH_LEN];
210         struct rte_flow *flow;
211         int ret;
212
213         if (!priv->ppio)
214                 return -rte_mtr_error_set(error, EPERM,
215                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
216                                           NULL, "Port is uninitialized\n");
217
218         if (!mtr)
219                 return -rte_mtr_error_set(error, ENODEV,
220                                           RTE_MTR_ERROR_TYPE_MTR_ID, NULL,
221                                           "Meter id does not exist\n");
222
223         if (mtr->plcr)
224                 goto skip;
225
226         mtr->plcr_bit = mrvl_reserve_plcr(&priv->used_plcrs);
227         if (mtr->plcr_bit < 0)
228                 return -rte_mtr_error_set(error, ENOSPC,
229                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
230                                           NULL,
231                                           "Failed to reserve plcr entry\n");
232
233         memset(&params, 0, sizeof(params));
234         snprintf(match, sizeof(match), "policer-%d:%d", priv->pp_id,
235                  mtr->plcr_bit);
236         params.match = match;
237         params.token_unit = PP2_CLS_PLCR_BYTES_TOKEN_UNIT;
238         params.color_mode = PP2_CLS_PLCR_COLOR_BLIND_MODE;
239         params.cir = mtr->profile->profile.srtcm_rfc2697.cir;
240         params.cbs = mtr->profile->profile.srtcm_rfc2697.cbs;
241         params.ebs = mtr->profile->profile.srtcm_rfc2697.ebs;
242
243         ret = pp2_cls_plcr_init(&params, &mtr->plcr);
244         if (ret) {
245                 priv->used_plcrs &= ~BIT(mtr->plcr_bit);
246                 mtr->plcr_bit = MRVL_PLCR_BIT_INVALID;
247
248                 return -rte_mtr_error_set(error, -ret,
249                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
250                                           NULL, "Failed to setup policer\n");
251         }
252
253         mtr->enabled = 1;
254 skip:
255         /* iterate over flows that have this mtr attached */
256         LIST_FOREACH(flow, &priv->flows, next) {
257                 if (flow->mtr != mtr)
258                         continue;
259
260                 flow->action.plcr = mtr->plcr;
261
262                 ret = pp2_cls_tbl_modify_rule(priv->cls_tbl, &flow->rule,
263                                               &flow->action);
264                 if (ret)
265                         return -rte_mtr_error_set(error, -ret,
266                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
267                                           NULL, "Failed to update cls rule\n");
268         }
269
270         return 0;
271 }
272
273 /**
274  * Disable meter object.
275  *
276  * @param dev Pointer to the device.
277  * @param mtr Id of the meter.
278  * @param error Pointer to the error.
279  * @returns 0 on success, negative value otherwise.
280  */
281 static int
282 mrvl_meter_disable(struct rte_eth_dev *dev, uint32_t mtr_id,
283                        struct rte_mtr_error *error)
284 {
285         struct mrvl_priv *priv = dev->data->dev_private;
286         struct mrvl_mtr *mtr = mrvl_mtr_from_id(priv, mtr_id);
287         struct rte_flow *flow;
288         int ret;
289
290         if (!mtr)
291                 return -rte_mtr_error_set(error, ENODEV,
292                                           RTE_MTR_ERROR_TYPE_MTR_ID, NULL,
293                                           "Meter id does not exist\n");
294
295         LIST_FOREACH(flow, &priv->flows, next) {
296                 if (flow->mtr != mtr)
297                         continue;
298
299                 flow->action.plcr = NULL;
300
301                 ret = pp2_cls_tbl_modify_rule(priv->cls_tbl, &flow->rule,
302                                               &flow->action);
303                 if (ret)
304                         return -rte_mtr_error_set(error, -ret,
305                                         RTE_MTR_ERROR_TYPE_UNSPECIFIED,
306                                         NULL, "Failed to disable meter\n");
307         }
308
309         mtr->enabled = 0;
310
311         return 0;
312 }
313
314 /**
315  * Create new meter.
316  *
317  * @param dev Pointer to the device.
318  * @param mtr_id Id of the meter.
319  * @param params Pointer to the meter parameters.
320  * @param shared Flags indicating whether meter is shared.
321  * @param error Pointer to the error.
322  * @returns 0 on success, negative value otherwise.
323  */
324 static int
325 mrvl_create(struct rte_eth_dev *dev, uint32_t mtr_id,
326             struct rte_mtr_params *params, int shared,
327             struct rte_mtr_error *error)
328 {
329         struct mrvl_priv *priv = dev->data->dev_private;
330         struct mrvl_mtr_profile *profile;
331         struct mrvl_mtr *mtr;
332
333         mtr = mrvl_mtr_from_id(priv, mtr_id);
334         if (mtr)
335                 return -rte_mtr_error_set(error, EEXIST,
336                                           RTE_MTR_ERROR_TYPE_MTR_ID, NULL,
337                                           "Meter id already exists\n");
338
339         mtr = rte_zmalloc_socket(NULL, sizeof(*mtr), 0, rte_socket_id());
340         if (!mtr)
341                 return -rte_mtr_error_set(error, ENOMEM,
342                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
343                                           NULL, NULL);
344
345         profile = mrvl_mtr_profile_from_id(priv, params->meter_profile_id);
346         if (!profile)
347                 return -rte_mtr_error_set(error, EINVAL,
348                                           RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
349                                           NULL, "Profile id does not exist\n");
350
351         mtr->shared = shared;
352         mtr->mtr_id = mtr_id;
353         mtr->plcr_bit = MRVL_PLCR_BIT_INVALID;
354         mtr->profile = profile;
355         profile->refcnt++;
356         LIST_INSERT_HEAD(&priv->mtrs, mtr, next);
357
358         if (params->meter_enable)
359                 return mrvl_meter_enable(dev, mtr_id, error);
360
361         return 0;
362 }
363
364 /**
365  * Destroy meter object.
366  *
367  * @param dev Pointer to the device.
368  * @param mtr_id Id of the meter object.
369  * @param error Pointer to the error.
370  * @returns 0 on success, negative value otherwise.
371  */
372 static int
373 mrvl_destroy(struct rte_eth_dev *dev, uint32_t mtr_id,
374                  struct rte_mtr_error *error)
375 {
376         struct mrvl_priv *priv = dev->data->dev_private;
377         struct mrvl_mtr *mtr;
378
379         if (!priv->ppio)
380                 return -rte_mtr_error_set(error, EPERM,
381                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
382                                           NULL, "Port is uninitialized\n");
383
384         mtr = mrvl_mtr_from_id(priv, mtr_id);
385         if (!mtr)
386                 return -rte_mtr_error_set(error, EEXIST,
387                                           RTE_MTR_ERROR_TYPE_MTR_ID, NULL,
388                                           "Meter id does not exist\n");
389
390         if (mtr->refcnt)
391                 return -rte_mtr_error_set(error, EPERM,
392                                           RTE_MTR_ERROR_TYPE_MTR_ID, NULL,
393                                           "Meter is used\n");
394
395         LIST_REMOVE(mtr, next);
396         mtr->profile->refcnt--;
397
398         if (mtr->plcr_bit != MRVL_PLCR_BIT_INVALID)
399                 priv->used_plcrs &= ~BIT(mtr->plcr_bit);
400
401         if (mtr->plcr)
402                 pp2_cls_plcr_deinit(mtr->plcr);
403
404         rte_free(mtr);
405
406         return 0;
407 }
408
409 /**
410  * Update profile used by the meter.
411  *
412  * @param dev Pointer to the device.
413  * @param mtr_id Id of the meter object.
414  * @param error Pointer to the error.
415  * @returns 0 on success, negative value otherwise.
416  */
417 static int
418 mrvl_meter_profile_update(struct rte_eth_dev *dev, uint32_t mtr_id,
419                           uint32_t meter_profile_id,
420                           struct rte_mtr_error *error)
421 {
422         struct mrvl_priv *priv = dev->data->dev_private;
423         struct mrvl_mtr_profile *profile;
424         struct mrvl_mtr *mtr;
425         int ret, enabled;
426
427         if (!priv->ppio)
428                 return -rte_mtr_error_set(error, EPERM,
429                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED,
430                                           NULL, "Port is uninitialized\n");
431
432         mtr = mrvl_mtr_from_id(priv, mtr_id);
433         if (!mtr)
434                 return -rte_mtr_error_set(error, EEXIST,
435                                           RTE_MTR_ERROR_TYPE_MTR_ID, NULL,
436                                           "Meter id does not exist\n");
437
438         profile = mrvl_mtr_profile_from_id(priv, meter_profile_id);
439         if (!profile)
440                 return -rte_mtr_error_set(error, EINVAL,
441                                           RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
442                                           NULL, "Profile id does not exist\n");
443
444         ret = mrvl_meter_disable(dev, mtr_id, error);
445         if (ret)
446                 return -rte_mtr_error_set(error, EPERM,
447                                           RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
448                                           NULL);
449
450         if (mtr->plcr) {
451                 enabled = 1;
452                 pp2_cls_plcr_deinit(mtr->plcr);
453                 mtr->plcr = NULL;
454         }
455
456         mtr->profile->refcnt--;
457         mtr->profile = profile;
458         profile->refcnt++;
459
460         if (enabled)
461                 return mrvl_meter_enable(dev, mtr_id, error);
462
463         return 0;
464 }
465
466 const struct rte_mtr_ops mrvl_mtr_ops = {
467         .capabilities_get = mrvl_capabilities_get,
468         .meter_profile_add = mrvl_meter_profile_add,
469         .meter_profile_delete = mrvl_meter_profile_delete,
470         .create = mrvl_create,
471         .destroy = mrvl_destroy,
472         .meter_enable = mrvl_meter_enable,
473         .meter_disable = mrvl_meter_disable,
474         .meter_profile_update = mrvl_meter_profile_update,
475 };
476
477 /**
478  * Initialize metering resources.
479  *
480  * @param dev Pointer to the device.
481  */
482 void
483 mrvl_mtr_init(struct rte_eth_dev *dev)
484 {
485         struct mrvl_priv *priv = dev->data->dev_private;
486
487         LIST_INIT(&priv->profiles);
488         LIST_INIT(&priv->mtrs);
489 }
490
491 /**
492  * Cleanup metering resources.
493  *
494  * @param dev Pointer to the device.
495  */
496 void
497 mrvl_mtr_deinit(struct rte_eth_dev *dev)
498 {
499         struct mrvl_priv *priv = dev->data->dev_private;
500         struct mrvl_mtr_profile *profile, *tmp_profile;
501         struct mrvl_mtr *mtr, *tmp_mtr;
502
503         for (mtr = LIST_FIRST(&priv->mtrs);
504              mtr && (tmp_mtr = LIST_NEXT(mtr, next), 1);
505              mtr = tmp_mtr)
506                 mrvl_destroy(dev, mtr->mtr_id, NULL);
507
508         for (profile = LIST_FIRST(&priv->profiles);
509              profile && (tmp_profile = LIST_NEXT(profile, next), 1);
510              profile = tmp_profile)
511                 mrvl_meter_profile_delete(dev, profile->profile_id, NULL);
512 }