1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2017 Marvell International Ltd.
3 * Copyright(c) 2017 Semihalf.
11 #include <rte_common.h>
12 #include <rte_cfgfile.h>
14 #include <rte_lcore.h>
15 #include <rte_malloc.h>
16 #include <rte_string_fns.h>
18 /* Unluckily, container_of is defined by both DPDK and MUSDK,
19 * we'll declare only one version.
21 * Note that it is not used in this PMD anyway.
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"
46 /* policer specific configuration tokens */
47 #define MRVL_TOK_PLCR_ENABLE "policer_enable"
48 #define MRVL_TOK_PLCR_UNIT "token_unit"
49 #define MRVL_TOK_PLCR_UNIT_BYTES "bytes"
50 #define MRVL_TOK_PLCR_UNIT_PACKETS "packets"
51 #define MRVL_TOK_PLCR_COLOR "color_mode"
52 #define MRVL_TOK_PLCR_COLOR_BLIND "blind"
53 #define MRVL_TOK_PLCR_COLOR_AWARE "aware"
54 #define MRVL_TOK_PLCR_CIR "cir"
55 #define MRVL_TOK_PLCR_CBS "cbs"
56 #define MRVL_TOK_PLCR_EBS "ebs"
57 #define MRVL_TOK_PLCR_DEFAULT_COLOR "default_color"
58 #define MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN "green"
59 #define MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW "yellow"
60 #define MRVL_TOK_PLCR_DEFAULT_COLOR_RED "red"
62 /** Number of tokens in range a-b = 2. */
63 #define MAX_RNG_TOKENS 2
65 /** Maximum possible value of PCP. */
68 /** Maximum possible value of DSCP. */
71 /** Global QoS configuration. */
72 struct mrvl_qos_cfg *mrvl_qos_cfg;
75 * Convert string to uint32_t with extra checks for result correctness.
77 * @param string String to convert.
78 * @param val Conversion result.
79 * @returns 0 in case of success, negative value otherwise.
82 get_val_securely(const char *string, uint32_t *val)
85 size_t len = strlen(string);
91 *val = strtoul(string, &endptr, 0);
92 if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len)
99 * Read out-queue configuration from file.
101 * @param file Path to the configuration file.
102 * @param port Port number.
103 * @param outq Out queue number.
104 * @param cfg Pointer to the Marvell QoS configuration structure.
105 * @returns 0 in case of success, negative value otherwise.
108 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
109 struct mrvl_qos_cfg *cfg)
115 snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
116 MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
118 /* Skip non-existing */
119 if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
122 entry = rte_cfgfile_get_entry(file, sec_name,
125 if (get_val_securely(entry, &val) < 0)
127 cfg->port[port].outq[outq].weight = (uint8_t)val;
134 * Gets multiple-entry values and places them in table.
136 * Entry can be anything, e.g. "1 2-3 5 6 7-9". This needs to be converted to
137 * table entries, respectively: {1, 2, 3, 5, 6, 7, 8, 9}.
138 * As all result table's elements are always 1-byte long, we
139 * won't overcomplicate the function, but we'll keep API generic,
140 * check if someone hasn't changed element size and make it simple
141 * to extend to other sizes.
143 * This function is purely utilitary, it does not print any error, only returns
144 * different error numbers.
146 * @param entry[in] Values string to parse.
147 * @param tab[out] Results table.
148 * @param elem_sz[in] Element size (in bytes).
149 * @param max_elems[in] Number of results table elements available.
150 * @param max val[in] Maximum value allowed.
151 * @returns Number of correctly parsed elements in case of success.
152 * @retval -1 Wrong element size.
153 * @retval -2 More tokens than result table allows.
154 * @retval -3 Wrong range syntax.
155 * @retval -4 Wrong range values.
156 * @retval -5 Maximum value exceeded.
159 get_entry_values(const char *entry, uint8_t *tab,
160 size_t elem_sz, uint8_t max_elems, uint8_t max_val)
162 /* There should not be more tokens than max elements.
163 * Add 1 for error trap.
165 char *tokens[max_elems + 1];
167 /* Begin, End + error trap = 3. */
168 char *rng_tokens[MAX_RNG_TOKENS + 1];
171 int nb_tokens, nb_rng_tokens;
175 char entry_cpy[CFG_VALUE_LEN];
180 /* Copy the entry to safely use rte_strsplit(). */
181 snprintf(entry_cpy, RTE_DIM(entry_cpy), "%s", entry);
184 * If there are more tokens than array size, rte_strsplit will
185 * not return error, just array size.
187 nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
188 tokens, max_elems + 1, ' ');
190 /* Quick check, will be refined later. */
191 if (nb_tokens > max_elems)
194 for (i = 0; i < nb_tokens; ++i) {
195 if (strchr(tokens[i], '-') != NULL) {
197 * Split to begin and end tokens.
198 * We want to catch error cases too, thus we leave
199 * option for number of tokens to be more than 2.
201 nb_rng_tokens = rte_strsplit(tokens[i],
202 strlen(tokens[i]), rng_tokens,
203 RTE_DIM(rng_tokens), '-');
204 if (nb_rng_tokens != 2)
207 /* Range and sanity checks. */
208 if (get_val_securely(rng_tokens[0], &token_val) < 0)
210 beg = (char)token_val;
211 if (get_val_securely(rng_tokens[1], &token_val) < 0)
213 end = (char)token_val;
214 if (beg < 0 || beg > UCHAR_MAX ||
215 end < 0 || end > UCHAR_MAX || end < beg)
218 for (val = beg; val <= end; ++val) {
223 tab = RTE_PTR_ADD(tab, elem_sz);
225 if (values >= max_elems)
230 if (get_val_securely(tokens[i], &token_val) < 0)
232 val = (char)token_val;
237 tab = RTE_PTR_ADD(tab, elem_sz);
239 if (values >= max_elems)
248 * Parse Traffic Class'es mapping configuration.
250 * @param file Config file handle.
251 * @param port Which port to look for.
252 * @param tc Which Traffic Class to look for.
253 * @param cfg[out] Parsing results.
254 * @returns 0 in case of success, negative value otherwise.
257 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
258 struct mrvl_qos_cfg *cfg)
264 snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
265 MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
267 /* Skip non-existing */
268 if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
271 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
273 n = get_entry_values(entry,
274 cfg->port[port].tc[tc].inq,
275 sizeof(cfg->port[port].tc[tc].inq[0]),
276 RTE_DIM(cfg->port[port].tc[tc].inq),
279 RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
283 cfg->port[port].tc[tc].inqs = n;
286 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
288 n = get_entry_values(entry,
289 cfg->port[port].tc[tc].pcp,
290 sizeof(cfg->port[port].tc[tc].pcp[0]),
291 RTE_DIM(cfg->port[port].tc[tc].pcp),
294 RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
298 cfg->port[port].tc[tc].pcps = n;
301 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
303 n = get_entry_values(entry,
304 cfg->port[port].tc[tc].dscp,
305 sizeof(cfg->port[port].tc[tc].dscp[0]),
306 RTE_DIM(cfg->port[port].tc[tc].dscp),
309 RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
313 cfg->port[port].tc[tc].dscps = n;
316 entry = rte_cfgfile_get_entry(file, sec_name,
317 MRVL_TOK_PLCR_DEFAULT_COLOR);
319 if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN,
320 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN))) {
321 cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_GREEN;
322 } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW,
323 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW))) {
324 cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_YELLOW;
325 } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_RED,
326 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_RED))) {
327 cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_RED;
329 RTE_LOG(ERR, PMD, "Error while parsing: %s\n", entry);
338 * Parse QoS configuration - rte_kvargs_process handler.
340 * Opens configuration file and parses its content.
343 * @param path Path to config file.
344 * @param extra_args Pointer to configuration structure.
345 * @returns 0 in case of success, exits otherwise.
348 mrvl_get_qoscfg(const char *key __rte_unused, const char *path,
351 struct mrvl_qos_cfg **cfg = extra_args;
352 struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
359 rte_exit(EXIT_FAILURE, "Cannot load configuration %s\n", path);
361 /* Create configuration. This is never accessed on the fast path,
362 * so we can ignore socket.
364 *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0);
366 rte_exit(EXIT_FAILURE, "Cannot allocate configuration %s\n",
369 n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
370 sizeof(MRVL_TOK_PORT) - 1);
373 /* This is weird, but not bad. */
374 RTE_LOG(WARNING, PMD, "Empty configuration file?\n");
378 /* Use the number of ports given as vdev parameters. */
379 for (n = 0; n < (PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC); ++n) {
380 snprintf(sec_name, sizeof(sec_name), "%s %d %s",
381 MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT);
383 /* Skip ports non-existing in configuration. */
384 if (rte_cfgfile_num_sections(file, sec_name,
385 strlen(sec_name)) <= 0) {
386 (*cfg)->port[n].use_global_defaults = 1;
387 (*cfg)->port[n].mapping_priority =
388 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
392 entry = rte_cfgfile_get_entry(file, sec_name,
393 MRVL_TOK_DEFAULT_TC);
395 if (get_val_securely(entry, &val) < 0 ||
398 (*cfg)->port[n].default_tc = (uint8_t)val;
401 "Default Traffic Class required in custom configuration!\n");
405 entry = rte_cfgfile_get_entry(file, sec_name,
406 MRVL_TOK_PLCR_ENABLE);
408 if (get_val_securely(entry, &val) < 0)
410 (*cfg)->port[n].policer_enable = val;
413 if ((*cfg)->port[n].policer_enable) {
414 enum pp2_cls_plcr_token_unit unit;
416 /* Read policer token unit */
417 entry = rte_cfgfile_get_entry(file, sec_name,
420 if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_BYTES,
421 sizeof(MRVL_TOK_PLCR_UNIT_BYTES))) {
422 unit = PP2_CLS_PLCR_BYTES_TOKEN_UNIT;
423 } else if (!strncmp(entry,
424 MRVL_TOK_PLCR_UNIT_PACKETS,
425 sizeof(MRVL_TOK_PLCR_UNIT_PACKETS))) {
426 unit = PP2_CLS_PLCR_PACKETS_TOKEN_UNIT;
428 RTE_LOG(ERR, PMD, "Unknown token: %s\n",
432 (*cfg)->port[n].policer_params.token_unit =
436 /* Read policer color mode */
437 entry = rte_cfgfile_get_entry(file, sec_name,
438 MRVL_TOK_PLCR_COLOR);
440 enum pp2_cls_plcr_color_mode mode;
442 if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_BLIND,
443 sizeof(MRVL_TOK_PLCR_COLOR_BLIND))) {
444 mode = PP2_CLS_PLCR_COLOR_BLIND_MODE;
445 } else if (!strncmp(entry,
446 MRVL_TOK_PLCR_COLOR_AWARE,
447 sizeof(MRVL_TOK_PLCR_COLOR_AWARE))) {
448 mode = PP2_CLS_PLCR_COLOR_AWARE_MODE;
451 "Error in parsing: %s\n",
455 (*cfg)->port[n].policer_params.color_mode =
459 /* Read policer cir */
460 entry = rte_cfgfile_get_entry(file, sec_name,
463 if (get_val_securely(entry, &val) < 0)
465 (*cfg)->port[n].policer_params.cir = val;
468 /* Read policer cbs */
469 entry = rte_cfgfile_get_entry(file, sec_name,
472 if (get_val_securely(entry, &val) < 0)
474 (*cfg)->port[n].policer_params.cbs = val;
477 /* Read policer ebs */
478 entry = rte_cfgfile_get_entry(file, sec_name,
481 if (get_val_securely(entry, &val) < 0)
483 (*cfg)->port[n].policer_params.ebs = val;
487 entry = rte_cfgfile_get_entry(file, sec_name,
488 MRVL_TOK_MAPPING_PRIORITY);
490 if (!strncmp(entry, MRVL_TOK_VLAN_IP,
491 sizeof(MRVL_TOK_VLAN_IP)))
492 (*cfg)->port[n].mapping_priority =
493 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
494 else if (!strncmp(entry, MRVL_TOK_IP_VLAN,
495 sizeof(MRVL_TOK_IP_VLAN)))
496 (*cfg)->port[n].mapping_priority =
497 PP2_CLS_QOS_TBL_IP_VLAN_PRI;
498 else if (!strncmp(entry, MRVL_TOK_IP,
499 sizeof(MRVL_TOK_IP)))
500 (*cfg)->port[n].mapping_priority =
501 PP2_CLS_QOS_TBL_IP_PRI;
502 else if (!strncmp(entry, MRVL_TOK_VLAN,
503 sizeof(MRVL_TOK_VLAN)))
504 (*cfg)->port[n].mapping_priority =
505 PP2_CLS_QOS_TBL_VLAN_PRI;
507 rte_exit(EXIT_FAILURE,
508 "Error in parsing %s value (%s)!\n",
509 MRVL_TOK_MAPPING_PRIORITY, entry);
511 (*cfg)->port[n].mapping_priority =
512 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
515 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
516 ret = get_outq_cfg(file, n, i, *cfg);
518 rte_exit(EXIT_FAILURE,
519 "Error %d parsing port %d outq %d!\n",
523 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
524 ret = parse_tc_cfg(file, n, i, *cfg);
526 rte_exit(EXIT_FAILURE,
527 "Error %d parsing port %d tc %d!\n",
536 * Setup Traffic Class.
538 * Fill in TC parameters in single MUSDK TC config entry.
539 * @param param TC parameters entry.
540 * @param inqs Number of MUSDK in-queues in this TC.
541 * @param bpool Bpool for this TC.
542 * @param color Default color for this TC.
543 * @returns 0 in case of success, exits otherwise.
546 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
547 struct pp2_bpool *bpool, enum pp2_ppio_color color)
549 struct pp2_ppio_inq_params *inq_params;
551 param->pkt_offset = MRVL_PKT_OFFS;
552 param->pools[0] = bpool;
553 param->default_color = color;
555 inq_params = rte_zmalloc_socket("inq_params",
556 inqs * sizeof(*inq_params),
561 param->num_in_qs = inqs;
563 /* Release old config if necessary. */
564 if (param->inqs_params)
565 rte_free(param->inqs_params);
567 param->inqs_params = inq_params;
573 * Setup ingress policer.
575 * @param priv Port's private data.
576 * @param params Pointer to the policer's configuration.
577 * @returns 0 in case of success, negative values otherwise.
580 setup_policer(struct mrvl_priv *priv, struct pp2_cls_plcr_params *params)
585 snprintf(match, sizeof(match), "policer-%d:%d\n",
586 priv->pp_id, priv->ppio_id);
587 params->match = match;
589 ret = pp2_cls_plcr_init(params, &priv->policer);
591 RTE_LOG(ERR, PMD, "Failed to setup %s\n", match);
595 priv->ppio_params.inqs_params.plcr = priv->policer;
601 * Configure RX Queues in a given port.
603 * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
605 * @param priv Port's private data
606 * @param portid DPDK port ID
607 * @param max_queues Maximum number of queues to configure.
608 * @returns 0 in case of success, negative value otherwise.
611 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid,
616 if (mrvl_qos_cfg == NULL ||
617 mrvl_qos_cfg->port[portid].use_global_defaults) {
619 * No port configuration, use default: 1 TC, no QoS,
620 * TC color set to green.
622 priv->ppio_params.inqs_params.num_tcs = 1;
623 setup_tc(&priv->ppio_params.inqs_params.tcs_params[0],
624 max_queues, priv->bpool, PP2_PPIO_COLOR_GREEN);
626 /* Direct mapping of queues i.e. 0->0, 1->1 etc. */
627 for (i = 0; i < max_queues; ++i) {
628 priv->rxq_map[i].tc = 0;
629 priv->rxq_map[i].inq = i;
634 /* We need only a subset of configuration. */
635 struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid];
637 priv->qos_tbl_params.type = port_cfg->mapping_priority;
640 * We need to reverse mapping, from tc->pcp (better from usability
641 * point of view) to pcp->tc (configurable in MUSDK).
642 * First, set all map elements to "default".
644 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
645 priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc;
647 /* Then, fill in all known values. */
648 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
649 if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) {
650 /* Better safe than sorry. */
652 "Too many PCPs configured in TC %zu!\n", tc);
655 for (i = 0; i < port_cfg->tc[tc].pcps; ++i) {
656 priv->qos_tbl_params.pcp_cos_map[
657 port_cfg->tc[tc].pcp[i]].tc = tc;
662 * The same logic goes with DSCP.
663 * First, set all map elements to "default".
665 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
666 priv->qos_tbl_params.dscp_cos_map[i].tc =
667 port_cfg->default_tc;
669 /* Fill in all known values. */
670 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
671 if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) {
672 /* Better safe than sorry. */
674 "Too many DSCPs configured in TC %zu!\n", tc);
677 for (i = 0; i < port_cfg->tc[tc].dscps; ++i) {
678 priv->qos_tbl_params.dscp_cos_map[
679 port_cfg->tc[tc].dscp[i]].tc = tc;
684 * Surprisingly, similar logic goes with queue mapping.
685 * We need only to store qid->tc mapping,
686 * to know TC when queue is read.
688 for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
689 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
691 /* Set up DPDKq->(TC,inq) mapping. */
692 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
693 if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) {
696 "Too many RX queues configured per TC %zu!\n",
700 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
701 uint8_t idx = port_cfg->tc[tc].inq[i];
703 if (idx > RTE_DIM(priv->rxq_map)) {
704 RTE_LOG(ERR, PMD, "Bad queue index %d!\n", idx);
708 priv->rxq_map[idx].tc = tc;
709 priv->rxq_map[idx].inq = i;
714 * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
715 * with no gaps. Empty TC means end of processing.
717 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
718 if (port_cfg->tc[i].inqs == 0)
720 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
721 port_cfg->tc[i].inqs,
722 priv->bpool, port_cfg->tc[i].color);
725 priv->ppio_params.inqs_params.num_tcs = i;
727 if (port_cfg->policer_enable)
728 return setup_policer(priv, &port_cfg->policer_params);
736 * Finalize QoS table configuration and initialize it in SDK. It can be done
737 * only after port is started, so we have a valid ppio reference.
739 * @param priv Port's private (configuration) data.
740 * @returns 0 in case of success, exits otherwise.
743 mrvl_start_qos_mapping(struct mrvl_priv *priv)
747 if (priv->ppio == NULL) {
748 RTE_LOG(ERR, PMD, "ppio must not be NULL here!\n");
752 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
753 priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio;
755 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
756 priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio;
758 /* Initialize Classifier QoS table. */
760 return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);