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