d206dc6dad0efe0c20371ed04d04a0625d792cc8
[dpdk.git] / drivers / net / mrvl / 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 /* Unluckily, container_of is defined by both DPDK and MUSDK,
19  * we'll declare only one version.
20  *
21  * Note that it is not used in this PMD anyway.
22  */
23 #ifdef container_of
24 #undef container_of
25 #endif
26
27 #include "mrvl_qos.h"
28
29 /* Parsing tokens. Defined conveniently, so that any correction is easy. */
30 #define MRVL_TOK_DEFAULT "default"
31 #define MRVL_TOK_DEFAULT_TC "default_tc"
32 #define MRVL_TOK_DSCP "dscp"
33 #define MRVL_TOK_MAPPING_PRIORITY "mapping_priority"
34 #define MRVL_TOK_IP "ip"
35 #define MRVL_TOK_IP_VLAN "ip/vlan"
36 #define MRVL_TOK_PCP "pcp"
37 #define MRVL_TOK_PORT "port"
38 #define MRVL_TOK_RXQ "rxq"
39 #define MRVL_TOK_SP "SP"
40 #define MRVL_TOK_TC "tc"
41 #define MRVL_TOK_TXQ "txq"
42 #define MRVL_TOK_VLAN "vlan"
43 #define MRVL_TOK_VLAN_IP "vlan/ip"
44 #define MRVL_TOK_WEIGHT "weight"
45
46 /** Number of tokens in range a-b = 2. */
47 #define MAX_RNG_TOKENS 2
48
49 /** Maximum possible value of PCP. */
50 #define MAX_PCP 7
51
52 /** Maximum possible value of DSCP. */
53 #define MAX_DSCP 63
54
55 /** Global QoS configuration. */
56 struct mrvl_qos_cfg *mrvl_qos_cfg;
57
58 /**
59  * Convert string to uint32_t with extra checks for result correctness.
60  *
61  * @param string String to convert.
62  * @param val Conversion result.
63  * @returns 0 in case of success, negative value otherwise.
64  */
65 static int
66 get_val_securely(const char *string, uint32_t *val)
67 {
68         char *endptr;
69         size_t len = strlen(string);
70
71         if (len == 0)
72                 return -1;
73
74         errno = 0;
75         *val = strtoul(string, &endptr, 0);
76         if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len)
77                 return -2;
78
79         return 0;
80 }
81
82 /**
83  * Read out-queue configuration from file.
84  *
85  * @param file Path to the configuration file.
86  * @param port Port number.
87  * @param outq Out queue number.
88  * @param cfg Pointer to the Marvell QoS configuration structure.
89  * @returns 0 in case of success, negative value otherwise.
90  */
91 static int
92 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
93                 struct mrvl_qos_cfg *cfg)
94 {
95         char sec_name[32];
96         const char *entry;
97         uint32_t val;
98
99         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
100                 MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
101
102         /* Skip non-existing */
103         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
104                 return 0;
105
106         entry = rte_cfgfile_get_entry(file, sec_name,
107                         MRVL_TOK_WEIGHT);
108         if (entry) {
109                 if (get_val_securely(entry, &val) < 0)
110                         return -1;
111                 cfg->port[port].outq[outq].weight = (uint8_t)val;
112         }
113
114         return 0;
115 }
116
117 /**
118  * Gets multiple-entry values and places them in table.
119  *
120  * Entry can be anything, e.g. "1 2-3 5 6 7-9". This needs to be converted to
121  * table entries, respectively: {1, 2, 3, 5, 6, 7, 8, 9}.
122  * As all result table's elements are always 1-byte long, we
123  * won't overcomplicate the function, but we'll keep API generic,
124  * check if someone hasn't changed element size and make it simple
125  * to extend to other sizes.
126  *
127  * This function is purely utilitary, it does not print any error, only returns
128  * different error numbers.
129  *
130  * @param entry[in] Values string to parse.
131  * @param tab[out] Results table.
132  * @param elem_sz[in] Element size (in bytes).
133  * @param max_elems[in] Number of results table elements available.
134  * @param max val[in] Maximum value allowed.
135  * @returns Number of correctly parsed elements in case of success.
136  * @retval -1 Wrong element size.
137  * @retval -2 More tokens than result table allows.
138  * @retval -3 Wrong range syntax.
139  * @retval -4 Wrong range values.
140  * @retval -5 Maximum value exceeded.
141  */
142 static int
143 get_entry_values(const char *entry, uint8_t *tab,
144         size_t elem_sz, uint8_t max_elems, uint8_t max_val)
145 {
146         /* There should not be more tokens than max elements.
147          * Add 1 for error trap.
148          */
149         char *tokens[max_elems + 1];
150
151         /* Begin, End + error trap = 3. */
152         char *rng_tokens[MAX_RNG_TOKENS + 1];
153         long beg, end;
154         uint32_t token_val;
155         int nb_tokens, nb_rng_tokens;
156         int i;
157         int values = 0;
158         char val;
159         char entry_cpy[CFG_VALUE_LEN];
160
161         if (elem_sz != 1)
162                 return -1;
163
164         /* Copy the entry to safely use rte_strsplit(). */
165         snprintf(entry_cpy, RTE_DIM(entry_cpy), "%s", entry);
166
167         /*
168          * If there are more tokens than array size, rte_strsplit will
169          * not return error, just array size.
170          */
171         nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
172                 tokens, max_elems + 1, ' ');
173
174         /* Quick check, will be refined later. */
175         if (nb_tokens > max_elems)
176                 return -2;
177
178         for (i = 0; i < nb_tokens; ++i) {
179                 if (strchr(tokens[i], '-') != NULL) {
180                         /*
181                          * Split to begin and end tokens.
182                          * We want to catch error cases too, thus we leave
183                          * option for number of tokens to be more than 2.
184                          */
185                         nb_rng_tokens = rte_strsplit(tokens[i],
186                                         strlen(tokens[i]), rng_tokens,
187                                         RTE_DIM(rng_tokens), '-');
188                         if (nb_rng_tokens != 2)
189                                 return -3;
190
191                         /* Range and sanity checks. */
192                         if (get_val_securely(rng_tokens[0], &token_val) < 0)
193                                 return -4;
194                         beg = (char)token_val;
195                         if (get_val_securely(rng_tokens[1], &token_val) < 0)
196                                 return -4;
197                         end = (char)token_val;
198                         if (beg < 0 || beg > UCHAR_MAX ||
199                                 end < 0 || end > UCHAR_MAX || end < beg)
200                                 return -4;
201
202                         for (val = beg; val <= end; ++val) {
203                                 if (val > max_val)
204                                         return -5;
205
206                                 *tab = val;
207                                 tab = RTE_PTR_ADD(tab, elem_sz);
208                                 ++values;
209                                 if (values >= max_elems)
210                                         return -2;
211                         }
212                 } else {
213                         /* Single values. */
214                         if (get_val_securely(tokens[i], &token_val) < 0)
215                                 return -5;
216                         val = (char)token_val;
217                         if (val > max_val)
218                                 return -5;
219
220                         *tab = val;
221                         tab = RTE_PTR_ADD(tab, elem_sz);
222                         ++values;
223                         if (values >= max_elems)
224                                 return -2;
225                 }
226         }
227
228         return values;
229 }
230
231 /**
232  * Parse Traffic Class'es mapping configuration.
233  *
234  * @param file Config file handle.
235  * @param port Which port to look for.
236  * @param tc Which Traffic Class to look for.
237  * @param cfg[out] Parsing results.
238  * @returns 0 in case of success, negative value otherwise.
239  */
240 static int
241 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
242                 struct mrvl_qos_cfg *cfg)
243 {
244         char sec_name[32];
245         const char *entry;
246         int n;
247
248         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
249                 MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
250
251         /* Skip non-existing */
252         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
253                 return 0;
254
255         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
256         if (entry) {
257                 n = get_entry_values(entry,
258                         cfg->port[port].tc[tc].inq,
259                         sizeof(cfg->port[port].tc[tc].inq[0]),
260                         RTE_DIM(cfg->port[port].tc[tc].inq),
261                         MRVL_PP2_RXQ_MAX);
262                 if (n < 0) {
263                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
264                                 n, entry);
265                         return n;
266                 }
267                 cfg->port[port].tc[tc].inqs = n;
268         }
269
270         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
271         if (entry) {
272                 n = get_entry_values(entry,
273                         cfg->port[port].tc[tc].pcp,
274                         sizeof(cfg->port[port].tc[tc].pcp[0]),
275                         RTE_DIM(cfg->port[port].tc[tc].pcp),
276                         MAX_PCP);
277                 if (n < 0) {
278                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
279                                 n, entry);
280                         return n;
281                 }
282                 cfg->port[port].tc[tc].pcps = n;
283         }
284
285         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
286         if (entry) {
287                 n = get_entry_values(entry,
288                         cfg->port[port].tc[tc].dscp,
289                         sizeof(cfg->port[port].tc[tc].dscp[0]),
290                         RTE_DIM(cfg->port[port].tc[tc].dscp),
291                         MAX_DSCP);
292                 if (n < 0) {
293                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
294                                 n, entry);
295                         return n;
296                 }
297                 cfg->port[port].tc[tc].dscps = n;
298         }
299         return 0;
300 }
301
302 /**
303  * Parse QoS configuration - rte_kvargs_process handler.
304  *
305  * Opens configuration file and parses its content.
306  *
307  * @param key Unused.
308  * @param path Path to config file.
309  * @param extra_args Pointer to configuration structure.
310  * @returns 0 in case of success, exits otherwise.
311  */
312 int
313 mrvl_get_qoscfg(const char *key __rte_unused, const char *path,
314                 void *extra_args)
315 {
316         struct mrvl_qos_cfg **cfg = extra_args;
317         struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
318         uint32_t val;
319         int n, i, ret;
320         const char *entry;
321         char sec_name[32];
322
323         if (file == NULL)
324                 rte_exit(EXIT_FAILURE, "Cannot load configuration %s\n", path);
325
326         /* Create configuration. This is never accessed on the fast path,
327          * so we can ignore socket.
328          */
329         *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0);
330         if (*cfg == NULL)
331                 rte_exit(EXIT_FAILURE, "Cannot allocate configuration %s\n",
332                         path);
333
334         n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
335                 sizeof(MRVL_TOK_PORT) - 1);
336
337         if (n == 0) {
338                 /* This is weird, but not bad. */
339                 RTE_LOG(WARNING, PMD, "Empty configuration file?\n");
340                 return 0;
341         }
342
343         /* Use the number of ports given as vdev parameters. */
344         for (n = 0; n < (PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC); ++n) {
345                 snprintf(sec_name, sizeof(sec_name), "%s %d %s",
346                         MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT);
347
348                 /* Skip ports non-existing in configuration. */
349                 if (rte_cfgfile_num_sections(file, sec_name,
350                                 strlen(sec_name)) <= 0) {
351                         (*cfg)->port[n].use_global_defaults = 1;
352                         (*cfg)->port[n].mapping_priority =
353                                 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
354                         continue;
355                 }
356
357                 entry = rte_cfgfile_get_entry(file, sec_name,
358                                 MRVL_TOK_DEFAULT_TC);
359                 if (entry) {
360                         if (get_val_securely(entry, &val) < 0 ||
361                                 val > USHRT_MAX)
362                                 return -1;
363                         (*cfg)->port[n].default_tc = (uint8_t)val;
364                 } else {
365                         RTE_LOG(ERR, PMD,
366                                 "Default Traffic Class required in custom configuration!\n");
367                         return -1;
368                 }
369
370                 entry = rte_cfgfile_get_entry(file, sec_name,
371                                 MRVL_TOK_MAPPING_PRIORITY);
372                 if (entry) {
373                         if (!strncmp(entry, MRVL_TOK_VLAN_IP,
374                                 sizeof(MRVL_TOK_VLAN_IP)))
375                                 (*cfg)->port[n].mapping_priority =
376                                         PP2_CLS_QOS_TBL_VLAN_IP_PRI;
377                         else if (!strncmp(entry, MRVL_TOK_IP_VLAN,
378                                 sizeof(MRVL_TOK_IP_VLAN)))
379                                 (*cfg)->port[n].mapping_priority =
380                                         PP2_CLS_QOS_TBL_IP_VLAN_PRI;
381                         else if (!strncmp(entry, MRVL_TOK_IP,
382                                 sizeof(MRVL_TOK_IP)))
383                                 (*cfg)->port[n].mapping_priority =
384                                         PP2_CLS_QOS_TBL_IP_PRI;
385                         else if (!strncmp(entry, MRVL_TOK_VLAN,
386                                 sizeof(MRVL_TOK_VLAN)))
387                                 (*cfg)->port[n].mapping_priority =
388                                         PP2_CLS_QOS_TBL_VLAN_PRI;
389                         else
390                                 rte_exit(EXIT_FAILURE,
391                                         "Error in parsing %s value (%s)!\n",
392                                         MRVL_TOK_MAPPING_PRIORITY, entry);
393                 } else {
394                         (*cfg)->port[n].mapping_priority =
395                                 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
396                 }
397
398                 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
399                         ret = get_outq_cfg(file, n, i, *cfg);
400                         if (ret < 0)
401                                 rte_exit(EXIT_FAILURE,
402                                         "Error %d parsing port %d outq %d!\n",
403                                         ret, n, i);
404                 }
405
406                 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
407                         ret = parse_tc_cfg(file, n, i, *cfg);
408                         if (ret < 0)
409                                 rte_exit(EXIT_FAILURE,
410                                         "Error %d parsing port %d tc %d!\n",
411                                         ret, n, i);
412                 }
413         }
414
415         return 0;
416 }
417
418 /**
419  * Setup Traffic Class.
420  *
421  * Fill in TC parameters in single MUSDK TC config entry.
422  * @param param TC parameters entry.
423  * @param inqs Number of MUSDK in-queues in this TC.
424  * @param bpool Bpool for this TC.
425  * @returns 0 in case of success, exits otherwise.
426  */
427 static int
428 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
429         struct pp2_bpool *bpool)
430 {
431         struct pp2_ppio_inq_params *inq_params;
432
433         param->pkt_offset = MRVL_PKT_OFFS;
434         param->pools[0] = bpool;
435
436         inq_params = rte_zmalloc_socket("inq_params",
437                 inqs * sizeof(*inq_params),
438                 0, rte_socket_id());
439         if (!inq_params)
440                 return -ENOMEM;
441
442         param->num_in_qs = inqs;
443
444         /* Release old config if necessary. */
445         if (param->inqs_params)
446                 rte_free(param->inqs_params);
447
448         param->inqs_params = inq_params;
449
450         return 0;
451 }
452
453 /**
454  * Configure RX Queues in a given port.
455  *
456  * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
457  *
458  * @param priv Port's private data
459  * @param portid DPDK port ID
460  * @param max_queues Maximum number of queues to configure.
461  * @returns 0 in case of success, negative value otherwise.
462  */
463 int
464 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid,
465         uint16_t max_queues)
466 {
467         size_t i, tc;
468
469         if (mrvl_qos_cfg == NULL ||
470                 mrvl_qos_cfg->port[portid].use_global_defaults) {
471                 /* No port configuration, use default: 1 TC, no QoS. */
472                 priv->ppio_params.inqs_params.num_tcs = 1;
473                 setup_tc(&priv->ppio_params.inqs_params.tcs_params[0],
474                         max_queues, priv->bpool);
475
476                 /* Direct mapping of queues i.e. 0->0, 1->1 etc. */
477                 for (i = 0; i < max_queues; ++i) {
478                         priv->rxq_map[i].tc = 0;
479                         priv->rxq_map[i].inq = i;
480                 }
481                 return 0;
482         }
483
484         /* We need only a subset of configuration. */
485         struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid];
486
487         priv->qos_tbl_params.type = port_cfg->mapping_priority;
488
489         /*
490          * We need to reverse mapping, from tc->pcp (better from usability
491          * point of view) to pcp->tc (configurable in MUSDK).
492          * First, set all map elements to "default".
493          */
494         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
495                 priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc;
496
497         /* Then, fill in all known values. */
498         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
499                 if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) {
500                         /* Better safe than sorry. */
501                         RTE_LOG(ERR, PMD,
502                                 "Too many PCPs configured in TC %zu!\n", tc);
503                         return -1;
504                 }
505                 for (i = 0; i < port_cfg->tc[tc].pcps; ++i) {
506                         priv->qos_tbl_params.pcp_cos_map[
507                           port_cfg->tc[tc].pcp[i]].tc = tc;
508                 }
509         }
510
511         /*
512          * The same logic goes with DSCP.
513          * First, set all map elements to "default".
514          */
515         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
516                 priv->qos_tbl_params.dscp_cos_map[i].tc =
517                         port_cfg->default_tc;
518
519         /* Fill in all known values. */
520         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
521                 if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) {
522                         /* Better safe than sorry. */
523                         RTE_LOG(ERR, PMD,
524                                 "Too many DSCPs configured in TC %zu!\n", tc);
525                         return -1;
526                 }
527                 for (i = 0; i < port_cfg->tc[tc].dscps; ++i) {
528                         priv->qos_tbl_params.dscp_cos_map[
529                           port_cfg->tc[tc].dscp[i]].tc = tc;
530                 }
531         }
532
533         /*
534          * Surprisingly, similar logic goes with queue mapping.
535          * We need only to store qid->tc mapping,
536          * to know TC when queue is read.
537          */
538         for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
539                 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
540
541         /* Set up DPDKq->(TC,inq) mapping. */
542         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
543                 if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) {
544                         /* Overflow. */
545                         RTE_LOG(ERR, PMD,
546                                 "Too many RX queues configured per TC %zu!\n",
547                                 tc);
548                         return -1;
549                 }
550                 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
551                         uint8_t idx = port_cfg->tc[tc].inq[i];
552
553                         if (idx > RTE_DIM(priv->rxq_map)) {
554                                 RTE_LOG(ERR, PMD, "Bad queue index %d!\n", idx);
555                                 return -1;
556                         }
557
558                         priv->rxq_map[idx].tc = tc;
559                         priv->rxq_map[idx].inq = i;
560                 }
561         }
562
563         /*
564          * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
565          * with no gaps. Empty TC means end of processing.
566          */
567         for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
568                 if (port_cfg->tc[i].inqs == 0)
569                         break;
570                 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
571                                 port_cfg->tc[i].inqs,
572                                 priv->bpool);
573         }
574
575         priv->ppio_params.inqs_params.num_tcs = i;
576
577         return 0;
578 }
579
580 /**
581  * Start QoS mapping.
582  *
583  * Finalize QoS table configuration and initialize it in SDK. It can be done
584  * only after port is started, so we have a valid ppio reference.
585  *
586  * @param priv Port's private (configuration) data.
587  * @returns 0 in case of success, exits otherwise.
588  */
589 int
590 mrvl_start_qos_mapping(struct mrvl_priv *priv)
591 {
592         size_t i;
593
594         if (priv->ppio == NULL) {
595                 RTE_LOG(ERR, PMD, "ppio must not be NULL here!\n");
596                 return -1;
597         }
598
599         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
600                 priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio;
601
602         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
603                 priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio;
604
605         /* Initialize Classifier QoS table. */
606
607         return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);
608 }