net/mvpp2: support DSA mode
[dpdk.git] / drivers / net / mvpp2 / mrvl_qos.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2017 Marvell International Ltd.
3  * Copyright(c) 2017 Semihalf.
4  * All rights reserved.
5  */
6
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include <rte_common.h>
12 #include <rte_cfgfile.h>
13 #include <rte_log.h>
14 #include <rte_lcore.h>
15 #include <rte_malloc.h>
16 #include <rte_string_fns.h>
17
18 #include "mrvl_qos.h"
19
20 /* Parsing tokens. Defined conveniently, so that any correction is easy. */
21 #define MRVL_TOK_DEFAULT "default"
22 #define MRVL_TOK_DSA_MODE "dsa_mode"
23 #define MRVL_TOK_DSA_MODE_NONE "none"
24 #define MRVL_TOK_DSA_MODE_DSA "dsa"
25 #define MRVL_TOK_DSA_MODE_EXT_DSA "ext_dsa"
26 #define MRVL_TOK_DEFAULT_TC "default_tc"
27 #define MRVL_TOK_DSCP "dscp"
28 #define MRVL_TOK_MAPPING_PRIORITY "mapping_priority"
29 #define MRVL_TOK_IP "ip"
30 #define MRVL_TOK_IP_VLAN "ip/vlan"
31 #define MRVL_TOK_PCP "pcp"
32 #define MRVL_TOK_PORT "port"
33 #define MRVL_TOK_RXQ "rxq"
34 #define MRVL_TOK_TC "tc"
35 #define MRVL_TOK_TXQ "txq"
36 #define MRVL_TOK_VLAN "vlan"
37 #define MRVL_TOK_VLAN_IP "vlan/ip"
38
39 /* egress specific configuration tokens */
40 #define MRVL_TOK_BURST_SIZE "burst_size"
41 #define MRVL_TOK_RATE_LIMIT "rate_limit"
42 #define MRVL_TOK_RATE_LIMIT_ENABLE "rate_limit_enable"
43 #define MRVL_TOK_SCHED_MODE "sched_mode"
44 #define MRVL_TOK_SCHED_MODE_SP "sp"
45 #define MRVL_TOK_SCHED_MODE_WRR "wrr"
46 #define MRVL_TOK_WRR_WEIGHT "wrr_weight"
47
48 /* policer specific configuration tokens */
49 #define MRVL_TOK_PLCR "policer"
50 #define MRVL_TOK_PLCR_DEFAULT "default_policer"
51 #define MRVL_TOK_PLCR_UNIT "token_unit"
52 #define MRVL_TOK_PLCR_UNIT_BYTES "bytes"
53 #define MRVL_TOK_PLCR_UNIT_PACKETS "packets"
54 #define MRVL_TOK_PLCR_COLOR "color_mode"
55 #define MRVL_TOK_PLCR_COLOR_BLIND "blind"
56 #define MRVL_TOK_PLCR_COLOR_AWARE "aware"
57 #define MRVL_TOK_PLCR_CIR "cir"
58 #define MRVL_TOK_PLCR_CBS "cbs"
59 #define MRVL_TOK_PLCR_EBS "ebs"
60 #define MRVL_TOK_PLCR_DEFAULT_COLOR "default_color"
61 #define MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN "green"
62 #define MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW "yellow"
63 #define MRVL_TOK_PLCR_DEFAULT_COLOR_RED "red"
64
65 /** Number of tokens in range a-b = 2. */
66 #define MAX_RNG_TOKENS 2
67
68 /** Maximum possible value of PCP. */
69 #define MAX_PCP 7
70
71 /** Maximum possible value of DSCP. */
72 #define MAX_DSCP 63
73
74 /** Global QoS configuration. */
75 struct mrvl_qos_cfg *mrvl_qos_cfg;
76
77 /**
78  * Convert string to uint32_t with extra checks for result correctness.
79  *
80  * @param string String to convert.
81  * @param val Conversion result.
82  * @returns 0 in case of success, negative value otherwise.
83  */
84 static int
85 get_val_securely(const char *string, uint32_t *val)
86 {
87         char *endptr;
88         size_t len = strlen(string);
89
90         if (len == 0)
91                 return -1;
92
93         errno = 0;
94         *val = strtoul(string, &endptr, 0);
95         if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len)
96                 return -2;
97
98         return 0;
99 }
100
101 /**
102  * Read out-queue configuration from file.
103  *
104  * @param file Path to the configuration file.
105  * @param port Port number.
106  * @param outq Out queue number.
107  * @param cfg Pointer to the Marvell QoS configuration structure.
108  * @returns 0 in case of success, negative value otherwise.
109  */
110 static int
111 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
112                 struct mrvl_qos_cfg *cfg)
113 {
114         char sec_name[32];
115         const char *entry;
116         uint32_t val;
117
118         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
119                 MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
120
121         /* Skip non-existing */
122         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
123                 return 0;
124
125         /* Read scheduling mode */
126         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_SCHED_MODE);
127         if (entry) {
128                 if (!strncmp(entry, MRVL_TOK_SCHED_MODE_SP,
129                                         strlen(MRVL_TOK_SCHED_MODE_SP))) {
130                         cfg->port[port].outq[outq].sched_mode =
131                                 PP2_PPIO_SCHED_M_SP;
132                 } else if (!strncmp(entry, MRVL_TOK_SCHED_MODE_WRR,
133                                         strlen(MRVL_TOK_SCHED_MODE_WRR))) {
134                         cfg->port[port].outq[outq].sched_mode =
135                                 PP2_PPIO_SCHED_M_WRR;
136                 } else {
137                         MRVL_LOG(ERR, "Unknown token: %s", entry);
138                         return -1;
139                 }
140         }
141
142         /* Read wrr weight */
143         if (cfg->port[port].outq[outq].sched_mode == PP2_PPIO_SCHED_M_WRR) {
144                 entry = rte_cfgfile_get_entry(file, sec_name,
145                                 MRVL_TOK_WRR_WEIGHT);
146                 if (entry) {
147                         if (get_val_securely(entry, &val) < 0)
148                                 return -1;
149                         cfg->port[port].outq[outq].weight = val;
150                 }
151         }
152
153         /*
154          * There's no point in setting rate limiting for specific outq as
155          * global port rate limiting has priority.
156          */
157         if (cfg->port[port].rate_limit_enable) {
158                 MRVL_LOG(WARNING, "Port %d rate limiting already enabled",
159                         port);
160                 return 0;
161         }
162
163         entry = rte_cfgfile_get_entry(file, sec_name,
164                         MRVL_TOK_RATE_LIMIT_ENABLE);
165         if (entry) {
166                 if (get_val_securely(entry, &val) < 0)
167                         return -1;
168                 cfg->port[port].outq[outq].rate_limit_enable = val;
169         }
170
171         if (!cfg->port[port].outq[outq].rate_limit_enable)
172                 return 0;
173
174         /* Read CBS (in kB) */
175         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_BURST_SIZE);
176         if (entry) {
177                 if (get_val_securely(entry, &val) < 0)
178                         return -1;
179                 cfg->port[port].outq[outq].rate_limit_params.cbs = val;
180         }
181
182         /* Read CIR (in kbps) */
183         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RATE_LIMIT);
184         if (entry) {
185                 if (get_val_securely(entry, &val) < 0)
186                         return -1;
187                 cfg->port[port].outq[outq].rate_limit_params.cir = val;
188         }
189
190         return 0;
191 }
192
193 /**
194  * Gets multiple-entry values and places them in table.
195  *
196  * Entry can be anything, e.g. "1 2-3 5 6 7-9". This needs to be converted to
197  * table entries, respectively: {1, 2, 3, 5, 6, 7, 8, 9}.
198  * As all result table's elements are always 1-byte long, we
199  * won't overcomplicate the function, but we'll keep API generic,
200  * check if someone hasn't changed element size and make it simple
201  * to extend to other sizes.
202  *
203  * This function is purely utilitary, it does not print any error, only returns
204  * different error numbers.
205  *
206  * @param entry[in] Values string to parse.
207  * @param tab[out] Results table.
208  * @param elem_sz[in] Element size (in bytes).
209  * @param max_elems[in] Number of results table elements available.
210  * @param max val[in] Maximum value allowed.
211  * @returns Number of correctly parsed elements in case of success.
212  * @retval -1 Wrong element size.
213  * @retval -2 More tokens than result table allows.
214  * @retval -3 Wrong range syntax.
215  * @retval -4 Wrong range values.
216  * @retval -5 Maximum value exceeded.
217  */
218 static int
219 get_entry_values(const char *entry, uint8_t *tab,
220         size_t elem_sz, uint8_t max_elems, uint8_t max_val)
221 {
222         /* There should not be more tokens than max elements.
223          * Add 1 for error trap.
224          */
225         char *tokens[max_elems + 1];
226
227         /* Begin, End + error trap = 3. */
228         char *rng_tokens[MAX_RNG_TOKENS + 1];
229         long beg, end;
230         uint32_t token_val;
231         int nb_tokens, nb_rng_tokens;
232         int i;
233         int values = 0;
234         char val;
235         char entry_cpy[CFG_VALUE_LEN];
236
237         if (elem_sz != 1)
238                 return -1;
239
240         /* Copy the entry to safely use rte_strsplit(). */
241         strlcpy(entry_cpy, entry, RTE_DIM(entry_cpy));
242
243         /*
244          * If there are more tokens than array size, rte_strsplit will
245          * not return error, just array size.
246          */
247         nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
248                 tokens, max_elems + 1, ' ');
249
250         /* Quick check, will be refined later. */
251         if (nb_tokens > max_elems)
252                 return -2;
253
254         for (i = 0; i < nb_tokens; ++i) {
255                 if (strchr(tokens[i], '-') != NULL) {
256                         /*
257                          * Split to begin and end tokens.
258                          * We want to catch error cases too, thus we leave
259                          * option for number of tokens to be more than 2.
260                          */
261                         nb_rng_tokens = rte_strsplit(tokens[i],
262                                         strlen(tokens[i]), rng_tokens,
263                                         RTE_DIM(rng_tokens), '-');
264                         if (nb_rng_tokens != 2)
265                                 return -3;
266
267                         /* Range and sanity checks. */
268                         if (get_val_securely(rng_tokens[0], &token_val) < 0)
269                                 return -4;
270                         beg = (char)token_val;
271                         if (get_val_securely(rng_tokens[1], &token_val) < 0)
272                                 return -4;
273                         end = (char)token_val;
274                         if (beg < 0 || beg > UCHAR_MAX ||
275                                 end < 0 || end > UCHAR_MAX || end < beg)
276                                 return -4;
277
278                         for (val = beg; val <= end; ++val) {
279                                 if (val > max_val)
280                                         return -5;
281
282                                 *tab = val;
283                                 tab = RTE_PTR_ADD(tab, elem_sz);
284                                 ++values;
285                                 if (values >= max_elems)
286                                         return -2;
287                         }
288                 } else {
289                         /* Single values. */
290                         if (get_val_securely(tokens[i], &token_val) < 0)
291                                 return -5;
292                         val = (char)token_val;
293                         if (val > max_val)
294                                 return -5;
295
296                         *tab = val;
297                         tab = RTE_PTR_ADD(tab, elem_sz);
298                         ++values;
299                         if (values >= max_elems)
300                                 return -2;
301                 }
302         }
303
304         return values;
305 }
306
307 /**
308  * Parse Traffic Class'es mapping configuration.
309  *
310  * @param file Config file handle.
311  * @param port Which port to look for.
312  * @param tc Which Traffic Class to look for.
313  * @param cfg[out] Parsing results.
314  * @returns 0 in case of success, negative value otherwise.
315  */
316 static int
317 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
318                 struct mrvl_qos_cfg *cfg)
319 {
320         char sec_name[32];
321         const char *entry;
322         int n;
323
324         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
325                 MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
326
327         /* Skip non-existing */
328         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
329                 return 0;
330
331         cfg->port[port].use_global_defaults = 0;
332         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
333         if (entry) {
334                 n = get_entry_values(entry,
335                         cfg->port[port].tc[tc].inq,
336                         sizeof(cfg->port[port].tc[tc].inq[0]),
337                         RTE_DIM(cfg->port[port].tc[tc].inq),
338                         MRVL_PP2_RXQ_MAX);
339                 if (n < 0) {
340                         MRVL_LOG(ERR, "Error %d while parsing: %s",
341                                 n, entry);
342                         return n;
343                 }
344                 cfg->port[port].tc[tc].inqs = n;
345         }
346
347         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
348         if (entry) {
349                 n = get_entry_values(entry,
350                         cfg->port[port].tc[tc].pcp,
351                         sizeof(cfg->port[port].tc[tc].pcp[0]),
352                         RTE_DIM(cfg->port[port].tc[tc].pcp),
353                         MAX_PCP);
354                 if (n < 0) {
355                         MRVL_LOG(ERR, "Error %d while parsing: %s",
356                                 n, entry);
357                         return n;
358                 }
359                 cfg->port[port].tc[tc].pcps = n;
360         }
361
362         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
363         if (entry) {
364                 n = get_entry_values(entry,
365                         cfg->port[port].tc[tc].dscp,
366                         sizeof(cfg->port[port].tc[tc].dscp[0]),
367                         RTE_DIM(cfg->port[port].tc[tc].dscp),
368                         MAX_DSCP);
369                 if (n < 0) {
370                         MRVL_LOG(ERR, "Error %d while parsing: %s",
371                                 n, entry);
372                         return n;
373                 }
374                 cfg->port[port].tc[tc].dscps = n;
375         }
376
377         if (!cfg->port[port].setup_policer)
378                 return 0;
379
380         entry = rte_cfgfile_get_entry(file, sec_name,
381                         MRVL_TOK_PLCR_DEFAULT_COLOR);
382         if (entry) {
383                 if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN,
384                                 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN))) {
385                         cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_GREEN;
386                 } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW,
387                                 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW))) {
388                         cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_YELLOW;
389                 } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_RED,
390                                 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_RED))) {
391                         cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_RED;
392                 } else {
393                         MRVL_LOG(ERR, "Error while parsing: %s", entry);
394                         return -1;
395                 }
396         }
397
398         return 0;
399 }
400
401 /**
402  * Parse default port policer.
403  *
404  * @param file Config file handle.
405  * @param sec_name Section name with policer configuration
406  * @param port Port number.
407  * @param cfg[out] Parsing results.
408  * @returns 0 in case of success, negative value otherwise.
409  */
410 static int
411 parse_policer(struct rte_cfgfile *file, int port, const char *sec_name,
412                 struct mrvl_qos_cfg *cfg)
413 {
414         const char *entry;
415         uint32_t val;
416
417         /* Read policer token unit */
418         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_UNIT);
419         if (entry) {
420                 if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_BYTES,
421                                         sizeof(MRVL_TOK_PLCR_UNIT_BYTES))) {
422                         cfg->port[port].policer_params.token_unit =
423                                 PP2_CLS_PLCR_BYTES_TOKEN_UNIT;
424                 } else if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_PACKETS,
425                                         sizeof(MRVL_TOK_PLCR_UNIT_PACKETS))) {
426                         cfg->port[port].policer_params.token_unit =
427                                 PP2_CLS_PLCR_PACKETS_TOKEN_UNIT;
428                 } else {
429                         MRVL_LOG(ERR, "Unknown token: %s", entry);
430                         return -1;
431                 }
432         }
433
434         /* Read policer color mode */
435         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_COLOR);
436         if (entry) {
437                 if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_BLIND,
438                                         sizeof(MRVL_TOK_PLCR_COLOR_BLIND))) {
439                         cfg->port[port].policer_params.color_mode =
440                                 PP2_CLS_PLCR_COLOR_BLIND_MODE;
441                 } else if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_AWARE,
442                                         sizeof(MRVL_TOK_PLCR_COLOR_AWARE))) {
443                         cfg->port[port].policer_params.color_mode =
444                                 PP2_CLS_PLCR_COLOR_AWARE_MODE;
445                 } else {
446                         MRVL_LOG(ERR, "Error in parsing: %s", entry);
447                         return -1;
448                 }
449         }
450
451         /* Read policer cir */
452         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CIR);
453         if (entry) {
454                 if (get_val_securely(entry, &val) < 0)
455                         return -1;
456                 cfg->port[port].policer_params.cir = val;
457         }
458
459         /* Read policer cbs */
460         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CBS);
461         if (entry) {
462                 if (get_val_securely(entry, &val) < 0)
463                         return -1;
464                 cfg->port[port].policer_params.cbs = val;
465         }
466
467         /* Read policer ebs */
468         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_EBS);
469         if (entry) {
470                 if (get_val_securely(entry, &val) < 0)
471                         return -1;
472                 cfg->port[port].policer_params.ebs = val;
473         }
474
475         cfg->port[port].setup_policer = 1;
476
477         return 0;
478 }
479
480 /**
481  * Parse QoS configuration - rte_kvargs_process handler.
482  *
483  * Opens configuration file and parses its content.
484  *
485  * @param key Unused.
486  * @param path Path to config file.
487  * @param extra_args Pointer to configuration structure.
488  * @returns 0 in case of success, exits otherwise.
489  */
490 int
491 mrvl_get_qoscfg(const char *key __rte_unused, const char *path,
492                 void *extra_args)
493 {
494         struct mrvl_qos_cfg **cfg = extra_args;
495         struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
496         uint32_t val;
497         int n, i, ret;
498         const char *entry;
499         char sec_name[32];
500
501         if (file == NULL) {
502                 MRVL_LOG(ERR, "Cannot load configuration %s\n", path);
503                 return -1;
504         }
505
506         /* Create configuration. This is never accessed on the fast path,
507          * so we can ignore socket.
508          */
509         *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0);
510         if (*cfg == NULL) {
511                 MRVL_LOG(ERR, "Cannot allocate configuration %s\n", path);
512                 return -1;
513         }
514
515         n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
516                 sizeof(MRVL_TOK_PORT) - 1);
517
518         if (n == 0) {
519                 /* This is weird, but not bad. */
520                 MRVL_LOG(WARNING, "Empty configuration file?");
521                 return 0;
522         }
523
524         /* Use the number of ports given as vdev parameters. */
525         for (n = 0; n < (PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC); ++n) {
526                 snprintf(sec_name, sizeof(sec_name), "%s %d %s",
527                         MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT);
528
529                 /* Use global defaults, unless an override occurs */
530                 (*cfg)->port[n].use_global_defaults = 1;
531
532                 /* Skip ports non-existing in configuration. */
533                 if (rte_cfgfile_num_sections(file, sec_name,
534                                 strlen(sec_name)) <= 0) {
535                         continue;
536                 }
537
538                 entry = rte_cfgfile_get_entry(file, sec_name,
539                                 MRVL_TOK_DSA_MODE);
540                 if (entry) {
541                         if (!strncmp(entry, MRVL_TOK_DSA_MODE_NONE,
542                                 sizeof(MRVL_TOK_DSA_MODE_NONE)))
543                                 (*cfg)->port[n].eth_start_hdr =
544                                 PP2_PPIO_HDR_ETH;
545                         else if (!strncmp(entry, MRVL_TOK_DSA_MODE_DSA,
546                                 sizeof(MRVL_TOK_DSA_MODE_DSA)))
547                                 (*cfg)->port[n].eth_start_hdr =
548                                 PP2_PPIO_HDR_ETH_DSA;
549                         else if (!strncmp(entry, MRVL_TOK_DSA_MODE_EXT_DSA,
550                                 sizeof(MRVL_TOK_DSA_MODE_EXT_DSA))) {
551                                 (*cfg)->port[n].eth_start_hdr =
552                                 PP2_PPIO_HDR_ETH_EXT_DSA;
553                         } else {
554                                 MRVL_LOG(ERR,
555                                         "Error in parsing %s value (%s)!\n",
556                                         MRVL_TOK_DSA_MODE, entry);
557                                 return -1;
558                         }
559                 } else {
560                         (*cfg)->port[n].eth_start_hdr = PP2_PPIO_HDR_ETH;
561                 }
562
563                 /*
564                  * Read per-port rate limiting. Setting that will
565                  * disable per-queue rate limiting.
566                  */
567                 entry = rte_cfgfile_get_entry(file, sec_name,
568                                 MRVL_TOK_RATE_LIMIT_ENABLE);
569                 if (entry) {
570                         if (get_val_securely(entry, &val) < 0)
571                                 return -1;
572                         (*cfg)->port[n].rate_limit_enable = val;
573                 }
574
575                 if ((*cfg)->port[n].rate_limit_enable) {
576                         entry = rte_cfgfile_get_entry(file, sec_name,
577                                         MRVL_TOK_BURST_SIZE);
578                         if (entry) {
579                                 if (get_val_securely(entry, &val) < 0)
580                                         return -1;
581                                 (*cfg)->port[n].rate_limit_params.cbs = val;
582                         }
583
584                         entry = rte_cfgfile_get_entry(file, sec_name,
585                                         MRVL_TOK_RATE_LIMIT);
586                         if (entry) {
587                                 if (get_val_securely(entry, &val) < 0)
588                                         return -1;
589                                 (*cfg)->port[n].rate_limit_params.cir = val;
590                         }
591                 }
592
593                 entry = rte_cfgfile_get_entry(file, sec_name,
594                                 MRVL_TOK_MAPPING_PRIORITY);
595                 if (entry) {
596                         (*cfg)->port[n].use_global_defaults = 0;
597                         if (!strncmp(entry, MRVL_TOK_VLAN_IP,
598                                 sizeof(MRVL_TOK_VLAN_IP)))
599                                 (*cfg)->port[n].mapping_priority =
600                                         PP2_CLS_QOS_TBL_VLAN_IP_PRI;
601                         else if (!strncmp(entry, MRVL_TOK_IP_VLAN,
602                                 sizeof(MRVL_TOK_IP_VLAN)))
603                                 (*cfg)->port[n].mapping_priority =
604                                         PP2_CLS_QOS_TBL_IP_VLAN_PRI;
605                         else if (!strncmp(entry, MRVL_TOK_IP,
606                                 sizeof(MRVL_TOK_IP)))
607                                 (*cfg)->port[n].mapping_priority =
608                                         PP2_CLS_QOS_TBL_IP_PRI;
609                         else if (!strncmp(entry, MRVL_TOK_VLAN,
610                                 sizeof(MRVL_TOK_VLAN))) {
611                                 (*cfg)->port[n].mapping_priority =
612                                         PP2_CLS_QOS_TBL_VLAN_PRI;
613                         } else {
614                                 MRVL_LOG(ERR,
615                                         "Error in parsing %s value (%s)!\n",
616                                         MRVL_TOK_MAPPING_PRIORITY, entry);
617                                 return -1;
618                         }
619                 } else {
620                         (*cfg)->port[n].mapping_priority =
621                                 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
622                 }
623
624                 /* Parse policer configuration (if any) */
625                 entry = rte_cfgfile_get_entry(file, sec_name,
626                                 MRVL_TOK_PLCR_DEFAULT);
627                 if (entry) {
628                         (*cfg)->port[n].use_global_defaults = 0;
629                         if (get_val_securely(entry, &val) < 0)
630                                 return -1;
631
632                         snprintf(sec_name, sizeof(sec_name), "%s %d",
633                                         MRVL_TOK_PLCR, val);
634                         ret = parse_policer(file, n, sec_name, *cfg);
635                         if (ret)
636                                 return -1;
637                 }
638
639                 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
640                         ret = get_outq_cfg(file, n, i, *cfg);
641                         if (ret < 0) {
642                                 MRVL_LOG(ERR,
643                                         "Error %d parsing port %d outq %d!\n",
644                                         ret, n, i);
645                                 return -1;
646                         }
647                 }
648
649                 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
650                         ret = parse_tc_cfg(file, n, i, *cfg);
651                         if (ret < 0) {
652                                 MRVL_LOG(ERR,
653                                         "Error %d parsing port %d tc %d!\n",
654                                         ret, n, i);
655                                 return -1;
656                         }
657                 }
658
659                 entry = rte_cfgfile_get_entry(file, sec_name,
660                                               MRVL_TOK_DEFAULT_TC);
661                 if (entry) {
662                         if (get_val_securely(entry, &val) < 0 ||
663                             val > USHRT_MAX)
664                                 return -1;
665                         (*cfg)->port[n].default_tc = (uint8_t)val;
666                 } else {
667                         if ((*cfg)->port[n].use_global_defaults == 0) {
668                                 MRVL_LOG(ERR,
669                                          "Default Traffic Class required in "
670                                          "custom configuration!");
671                                 return -1;
672                         }
673                 }
674         }
675
676         return 0;
677 }
678
679 /**
680  * Setup Traffic Class.
681  *
682  * Fill in TC parameters in single MUSDK TC config entry.
683  * @param param TC parameters entry.
684  * @param inqs Number of MUSDK in-queues in this TC.
685  * @param bpool Bpool for this TC.
686  * @param color Default color for this TC.
687  * @returns 0 in case of success, exits otherwise.
688  */
689 static int
690 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
691         struct pp2_bpool *bpool, enum pp2_ppio_color color)
692 {
693         struct pp2_ppio_inq_params *inq_params;
694
695         param->pkt_offset = MRVL_PKT_OFFS;
696         param->pools[0][0] = bpool;
697         param->default_color = color;
698
699         inq_params = rte_zmalloc_socket("inq_params",
700                 inqs * sizeof(*inq_params),
701                 0, rte_socket_id());
702         if (!inq_params)
703                 return -ENOMEM;
704
705         param->num_in_qs = inqs;
706
707         /* Release old config if necessary. */
708         if (param->inqs_params)
709                 rte_free(param->inqs_params);
710
711         param->inqs_params = inq_params;
712
713         return 0;
714 }
715
716 /**
717  * Setup ingress policer.
718  *
719  * @param priv Port's private data.
720  * @param params Pointer to the policer's configuration.
721  * @param plcr_id Policer id.
722  * @returns 0 in case of success, negative values otherwise.
723  */
724 static int
725 setup_policer(struct mrvl_priv *priv, struct pp2_cls_plcr_params *params)
726 {
727         char match[16];
728         int ret;
729
730         /*
731          * At this point no other policers are used which means
732          * any policer can be picked up and used as a default one.
733          *
734          * Lets use 0th then.
735          */
736         sprintf(match, "policer-%d:%d\n", priv->pp_id, 0);
737         params->match = match;
738
739         ret = pp2_cls_plcr_init(params, &priv->default_policer);
740         if (ret) {
741                 MRVL_LOG(ERR, "Failed to setup %s", match);
742                 return -1;
743         }
744
745         priv->ppio_params.inqs_params.plcr = priv->default_policer;
746         priv->used_plcrs = BIT(0);
747
748         return 0;
749 }
750
751 /**
752  * Configure RX Queues in a given port.
753  *
754  * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
755  *
756  * @param priv Port's private data
757  * @param portid DPDK port ID
758  * @param max_queues Maximum number of queues to configure.
759  * @returns 0 in case of success, negative value otherwise.
760  */
761 int
762 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid,
763         uint16_t max_queues)
764 {
765         size_t i, tc;
766
767         if (mrvl_qos_cfg == NULL ||
768                 mrvl_qos_cfg->port[portid].use_global_defaults) {
769                 /*
770                  * No port configuration, use default: 1 TC, no QoS,
771                  * TC color set to green.
772                  */
773                 priv->ppio_params.inqs_params.num_tcs = 1;
774                 setup_tc(&priv->ppio_params.inqs_params.tcs_params[0],
775                         max_queues, priv->bpool, PP2_PPIO_COLOR_GREEN);
776
777                 /* Direct mapping of queues i.e. 0->0, 1->1 etc. */
778                 for (i = 0; i < max_queues; ++i) {
779                         priv->rxq_map[i].tc = 0;
780                         priv->rxq_map[i].inq = i;
781                 }
782                 return 0;
783         }
784
785         /* We need only a subset of configuration. */
786         struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid];
787
788         priv->qos_tbl_params.type = port_cfg->mapping_priority;
789
790         /*
791          * We need to reverse mapping, from tc->pcp (better from usability
792          * point of view) to pcp->tc (configurable in MUSDK).
793          * First, set all map elements to "default".
794          */
795         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
796                 priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc;
797
798         /* Then, fill in all known values. */
799         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
800                 if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) {
801                         /* Better safe than sorry. */
802                         MRVL_LOG(ERR,
803                                 "Too many PCPs configured in TC %zu!", tc);
804                         return -1;
805                 }
806                 for (i = 0; i < port_cfg->tc[tc].pcps; ++i) {
807                         priv->qos_tbl_params.pcp_cos_map[
808                           port_cfg->tc[tc].pcp[i]].tc = tc;
809                 }
810         }
811
812         /*
813          * The same logic goes with DSCP.
814          * First, set all map elements to "default".
815          */
816         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
817                 priv->qos_tbl_params.dscp_cos_map[i].tc =
818                         port_cfg->default_tc;
819
820         /* Fill in all known values. */
821         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
822                 if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) {
823                         /* Better safe than sorry. */
824                         MRVL_LOG(ERR,
825                                 "Too many DSCPs configured in TC %zu!", tc);
826                         return -1;
827                 }
828                 for (i = 0; i < port_cfg->tc[tc].dscps; ++i) {
829                         priv->qos_tbl_params.dscp_cos_map[
830                           port_cfg->tc[tc].dscp[i]].tc = tc;
831                 }
832         }
833
834         /*
835          * Surprisingly, similar logic goes with queue mapping.
836          * We need only to store qid->tc mapping,
837          * to know TC when queue is read.
838          */
839         for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
840                 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
841
842         /* Set up DPDKq->(TC,inq) mapping. */
843         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
844                 if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) {
845                         /* Overflow. */
846                         MRVL_LOG(ERR,
847                                 "Too many RX queues configured per TC %zu!",
848                                 tc);
849                         return -1;
850                 }
851                 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
852                         uint8_t idx = port_cfg->tc[tc].inq[i];
853
854                         if (idx > RTE_DIM(priv->rxq_map)) {
855                                 MRVL_LOG(ERR, "Bad queue index %d!", idx);
856                                 return -1;
857                         }
858
859                         priv->rxq_map[idx].tc = tc;
860                         priv->rxq_map[idx].inq = i;
861                 }
862         }
863
864         /*
865          * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
866          * with no gaps. Empty TC means end of processing.
867          */
868         for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
869                 if (port_cfg->tc[i].inqs == 0)
870                         break;
871                 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
872                                 port_cfg->tc[i].inqs,
873                                 priv->bpool, port_cfg->tc[i].color);
874         }
875
876         priv->ppio_params.inqs_params.num_tcs = i;
877
878         if (port_cfg->setup_policer)
879                 return setup_policer(priv, &port_cfg->policer_params);
880
881         return 0;
882 }
883
884 /**
885  * Configure TX Queues in a given port.
886  *
887  * Sets up TX queues egress scheduler and limiter.
888  *
889  * @param priv Port's private data
890  * @param portid DPDK port ID
891  * @param max_queues Maximum number of queues to configure.
892  * @returns 0 in case of success, negative value otherwise.
893  */
894 int
895 mrvl_configure_txqs(struct mrvl_priv *priv, uint16_t portid,
896                 uint16_t max_queues)
897 {
898         /* We need only a subset of configuration. */
899         struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid];
900         int i;
901
902         if (mrvl_qos_cfg == NULL)
903                 return 0;
904
905         priv->ppio_params.rate_limit_enable = port_cfg->rate_limit_enable;
906         if (port_cfg->rate_limit_enable)
907                 priv->ppio_params.rate_limit_params =
908                         port_cfg->rate_limit_params;
909
910         for (i = 0; i < max_queues; i++) {
911                 struct pp2_ppio_outq_params *params =
912                         &priv->ppio_params.outqs_params.outqs_params[i];
913
914                 params->sched_mode = port_cfg->outq[i].sched_mode;
915                 params->weight = port_cfg->outq[i].weight;
916                 params->rate_limit_enable = port_cfg->outq[i].rate_limit_enable;
917                 params->rate_limit_params = port_cfg->outq[i].rate_limit_params;
918         }
919
920         return 0;
921 }
922
923 /**
924  * Start QoS mapping.
925  *
926  * Finalize QoS table configuration and initialize it in SDK. It can be done
927  * only after port is started, so we have a valid ppio reference.
928  *
929  * @param priv Port's private (configuration) data.
930  * @returns 0 in case of success, exits otherwise.
931  */
932 int
933 mrvl_start_qos_mapping(struct mrvl_priv *priv)
934 {
935         size_t i;
936
937         if (priv->ppio == NULL) {
938                 MRVL_LOG(ERR, "ppio must not be NULL here!");
939                 return -1;
940         }
941
942         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
943                 priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio;
944
945         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
946                 priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio;
947
948         /* Initialize Classifier QoS table. */
949
950         return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);
951 }