4 * Copyright(c) 2017 Marvell International Ltd.
5 * Copyright(c) 2017 Semihalf.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
18 * * Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 #include <rte_common.h>
40 #include <rte_cfgfile.h>
42 #include <rte_lcore.h>
43 #include <rte_malloc.h>
44 #include <rte_string_fns.h>
46 /* Unluckily, container_of is defined by both DPDK and MUSDK,
47 * we'll declare only one version.
49 * Note that it is not used in this PMD anyway.
57 /* Parsing tokens. Defined conveniently, so that any correction is easy. */
58 #define MRVL_TOK_DEFAULT "default"
59 #define MRVL_TOK_DEFAULT_TC "default_tc"
60 #define MRVL_TOK_DSCP "dscp"
61 #define MRVL_TOK_MAPPING_PRIORITY "mapping_priority"
62 #define MRVL_TOK_IP "ip"
63 #define MRVL_TOK_IP_VLAN "ip/vlan"
64 #define MRVL_TOK_PCP "pcp"
65 #define MRVL_TOK_PORT "port"
66 #define MRVL_TOK_RXQ "rxq"
67 #define MRVL_TOK_SP "SP"
68 #define MRVL_TOK_TC "tc"
69 #define MRVL_TOK_TXQ "txq"
70 #define MRVL_TOK_VLAN "vlan"
71 #define MRVL_TOK_VLAN_IP "vlan/ip"
72 #define MRVL_TOK_WEIGHT "weight"
74 /** Number of tokens in range a-b = 2. */
75 #define MAX_RNG_TOKENS 2
77 /** Maximum possible value of PCP. */
80 /** Maximum possible value of DSCP. */
83 /** Global QoS configuration. */
84 struct mrvl_qos_cfg *mrvl_qos_cfg;
87 * Convert string to uint32_t with extra checks for result correctness.
89 * @param string String to convert.
90 * @param val Conversion result.
91 * @returns 0 in case of success, negative value otherwise.
94 get_val_securely(const char *string, uint32_t *val)
97 size_t len = strlen(string);
102 *val = strtoul(string, &endptr, 0);
103 if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len)
110 * Read out-queue configuration from file.
112 * @param file Path to the configuration file.
113 * @param port Port number.
114 * @param outq Out queue number.
115 * @param cfg Pointer to the Marvell QoS configuration structure.
116 * @returns 0 in case of success, negative value otherwise.
119 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
120 struct mrvl_qos_cfg *cfg)
126 snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
127 MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
129 /* Skip non-existing */
130 if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
133 entry = rte_cfgfile_get_entry(file, sec_name,
136 if (get_val_securely(entry, &val) < 0)
138 cfg->port[port].outq[outq].weight = (uint8_t)val;
145 * Gets multiple-entry values and places them in table.
147 * Entry can be anything, e.g. "1 2-3 5 6 7-9". This needs to be converted to
148 * table entries, respectively: {1, 2, 3, 5, 6, 7, 8, 9}.
149 * As all result table's elements are always 1-byte long, we
150 * won't overcomplicate the function, but we'll keep API generic,
151 * check if someone hasn't changed element size and make it simple
152 * to extend to other sizes.
154 * This function is purely utilitary, it does not print any error, only returns
155 * different error numbers.
157 * @param entry[in] Values string to parse.
158 * @param tab[out] Results table.
159 * @param elem_sz[in] Element size (in bytes).
160 * @param max_elems[in] Number of results table elements available.
161 * @param max val[in] Maximum value allowed.
162 * @returns Number of correctly parsed elements in case of success.
163 * @retval -1 Wrong element size.
164 * @retval -2 More tokens than result table allows.
165 * @retval -3 Wrong range syntax.
166 * @retval -4 Wrong range values.
167 * @retval -5 Maximum value exceeded.
170 get_entry_values(const char *entry, uint8_t *tab,
171 size_t elem_sz, uint8_t max_elems, uint8_t max_val)
173 /* There should not be more tokens than max elements.
174 * Add 1 for error trap.
176 char *tokens[max_elems + 1];
178 /* Begin, End + error trap = 3. */
179 char *rng_tokens[MAX_RNG_TOKENS + 1];
182 int nb_tokens, nb_rng_tokens;
186 char entry_cpy[CFG_VALUE_LEN];
191 /* Copy the entry to safely use rte_strsplit(). */
192 snprintf(entry_cpy, RTE_DIM(entry_cpy), "%s", entry);
195 * If there are more tokens than array size, rte_strsplit will
196 * not return error, just array size.
198 nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
199 tokens, max_elems + 1, ' ');
201 /* Quick check, will be refined later. */
202 if (nb_tokens > max_elems)
205 for (i = 0; i < nb_tokens; ++i) {
206 if (strchr(tokens[i], '-') != NULL) {
208 * Split to begin and end tokens.
209 * We want to catch error cases too, thus we leave
210 * option for number of tokens to be more than 2.
212 nb_rng_tokens = rte_strsplit(tokens[i],
213 strlen(tokens[i]), rng_tokens,
214 RTE_DIM(rng_tokens), '-');
215 if (nb_rng_tokens != 2)
218 /* Range and sanity checks. */
219 if (get_val_securely(rng_tokens[0], &token_val) < 0)
221 beg = (char)token_val;
222 if (get_val_securely(rng_tokens[1], &token_val) < 0)
224 end = (char)token_val;
225 if (beg < 0 || beg > UCHAR_MAX ||
226 end < 0 || end > UCHAR_MAX || end < beg)
229 for (val = beg; val <= end; ++val) {
234 tab = RTE_PTR_ADD(tab, elem_sz);
236 if (values >= max_elems)
241 if (get_val_securely(tokens[i], &token_val) < 0)
243 val = (char)token_val;
248 tab = RTE_PTR_ADD(tab, elem_sz);
250 if (values >= max_elems)
259 * Parse Traffic Class'es mapping configuration.
261 * @param file Config file handle.
262 * @param port Which port to look for.
263 * @param tc Which Traffic Class to look for.
264 * @param cfg[out] Parsing results.
265 * @returns 0 in case of success, negative value otherwise.
268 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
269 struct mrvl_qos_cfg *cfg)
275 snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
276 MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
278 /* Skip non-existing */
279 if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
282 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
284 n = get_entry_values(entry,
285 cfg->port[port].tc[tc].inq,
286 sizeof(cfg->port[port].tc[tc].inq[0]),
287 RTE_DIM(cfg->port[port].tc[tc].inq),
290 RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
294 cfg->port[port].tc[tc].inqs = n;
297 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
299 n = get_entry_values(entry,
300 cfg->port[port].tc[tc].pcp,
301 sizeof(cfg->port[port].tc[tc].pcp[0]),
302 RTE_DIM(cfg->port[port].tc[tc].pcp),
305 RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
309 cfg->port[port].tc[tc].pcps = n;
312 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
314 n = get_entry_values(entry,
315 cfg->port[port].tc[tc].dscp,
316 sizeof(cfg->port[port].tc[tc].dscp[0]),
317 RTE_DIM(cfg->port[port].tc[tc].dscp),
320 RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
324 cfg->port[port].tc[tc].dscps = n;
330 * Parse QoS configuration - rte_kvargs_process handler.
332 * Opens configuration file and parses its content.
335 * @param path Path to config file.
336 * @param extra_args Pointer to configuration structure.
337 * @returns 0 in case of success, exits otherwise.
340 mrvl_get_qoscfg(const char *key __rte_unused, const char *path,
343 struct mrvl_qos_cfg **cfg = extra_args;
344 struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
351 rte_exit(EXIT_FAILURE, "Cannot load configuration %s\n", path);
353 /* Create configuration. This is never accessed on the fast path,
354 * so we can ignore socket.
356 *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0);
358 rte_exit(EXIT_FAILURE, "Cannot allocate configuration %s\n",
361 n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
362 sizeof(MRVL_TOK_PORT) - 1);
365 /* This is weird, but not bad. */
366 RTE_LOG(WARNING, PMD, "Empty configuration file?\n");
370 /* Use the number of ports given as vdev parameters. */
371 for (n = 0; n < mrvl_ports_nb; ++n) {
372 snprintf(sec_name, sizeof(sec_name), "%s %d %s",
373 MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT);
375 /* Skip ports non-existing in configuration. */
376 if (rte_cfgfile_num_sections(file, sec_name,
377 strlen(sec_name)) <= 0) {
378 (*cfg)->port[n].use_global_defaults = 1;
379 (*cfg)->port[n].mapping_priority =
380 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
384 entry = rte_cfgfile_get_entry(file, sec_name,
385 MRVL_TOK_DEFAULT_TC);
387 if (get_val_securely(entry, &val) < 0 ||
390 (*cfg)->port[n].default_tc = (uint8_t)val;
393 "Default Traffic Class required in custom configuration!\n");
397 entry = rte_cfgfile_get_entry(file, sec_name,
398 MRVL_TOK_MAPPING_PRIORITY);
400 if (!strncmp(entry, MRVL_TOK_VLAN_IP,
401 sizeof(MRVL_TOK_VLAN_IP)))
402 (*cfg)->port[n].mapping_priority =
403 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
404 else if (!strncmp(entry, MRVL_TOK_IP_VLAN,
405 sizeof(MRVL_TOK_IP_VLAN)))
406 (*cfg)->port[n].mapping_priority =
407 PP2_CLS_QOS_TBL_IP_VLAN_PRI;
408 else if (!strncmp(entry, MRVL_TOK_IP,
409 sizeof(MRVL_TOK_IP)))
410 (*cfg)->port[n].mapping_priority =
411 PP2_CLS_QOS_TBL_IP_PRI;
412 else if (!strncmp(entry, MRVL_TOK_VLAN,
413 sizeof(MRVL_TOK_VLAN)))
414 (*cfg)->port[n].mapping_priority =
415 PP2_CLS_QOS_TBL_VLAN_PRI;
417 rte_exit(EXIT_FAILURE,
418 "Error in parsing %s value (%s)!\n",
419 MRVL_TOK_MAPPING_PRIORITY, entry);
421 (*cfg)->port[n].mapping_priority =
422 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
425 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
426 ret = get_outq_cfg(file, n, i, *cfg);
428 rte_exit(EXIT_FAILURE,
429 "Error %d parsing port %d outq %d!\n",
433 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
434 ret = parse_tc_cfg(file, n, i, *cfg);
436 rte_exit(EXIT_FAILURE,
437 "Error %d parsing port %d tc %d!\n",
446 * Setup Traffic Class.
448 * Fill in TC parameters in single MUSDK TC config entry.
449 * @param param TC parameters entry.
450 * @param inqs Number of MUSDK in-queues in this TC.
451 * @param bpool Bpool for this TC.
452 * @returns 0 in case of success, exits otherwise.
455 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
456 struct pp2_bpool *bpool)
458 struct pp2_ppio_inq_params *inq_params;
460 param->pkt_offset = MRVL_PKT_OFFS;
461 param->pools[0] = bpool;
463 inq_params = rte_zmalloc_socket("inq_params",
464 inqs * sizeof(*inq_params),
469 param->num_in_qs = inqs;
471 /* Release old config if necessary. */
472 if (param->inqs_params)
473 rte_free(param->inqs_params);
475 param->inqs_params = inq_params;
481 * Configure RX Queues in a given port.
483 * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
485 * @param priv Port's private data
486 * @param portid DPDK port ID
487 * @param max_queues Maximum number of queues to configure.
488 * @returns 0 in case of success, negative value otherwise.
491 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid,
496 if (mrvl_qos_cfg == NULL ||
497 mrvl_qos_cfg->port[portid].use_global_defaults) {
498 /* No port configuration, use default: 1 TC, no QoS. */
499 priv->ppio_params.inqs_params.num_tcs = 1;
500 setup_tc(&priv->ppio_params.inqs_params.tcs_params[0],
501 max_queues, priv->bpool);
503 /* Direct mapping of queues i.e. 0->0, 1->1 etc. */
504 for (i = 0; i < max_queues; ++i) {
505 priv->rxq_map[i].tc = 0;
506 priv->rxq_map[i].inq = i;
511 /* We need only a subset of configuration. */
512 struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid];
514 priv->qos_tbl_params.type = port_cfg->mapping_priority;
517 * We need to reverse mapping, from tc->pcp (better from usability
518 * point of view) to pcp->tc (configurable in MUSDK).
519 * First, set all map elements to "default".
521 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
522 priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc;
524 /* Then, fill in all known values. */
525 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
526 if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) {
527 /* Better safe than sorry. */
529 "Too many PCPs configured in TC %zu!\n", tc);
532 for (i = 0; i < port_cfg->tc[tc].pcps; ++i) {
533 priv->qos_tbl_params.pcp_cos_map[
534 port_cfg->tc[tc].pcp[i]].tc = tc;
539 * The same logic goes with DSCP.
540 * First, set all map elements to "default".
542 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
543 priv->qos_tbl_params.dscp_cos_map[i].tc =
544 port_cfg->default_tc;
546 /* Fill in all known values. */
547 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
548 if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) {
549 /* Better safe than sorry. */
551 "Too many DSCPs configured in TC %zu!\n", tc);
554 for (i = 0; i < port_cfg->tc[tc].dscps; ++i) {
555 priv->qos_tbl_params.dscp_cos_map[
556 port_cfg->tc[tc].dscp[i]].tc = tc;
561 * Surprisingly, similar logic goes with queue mapping.
562 * We need only to store qid->tc mapping,
563 * to know TC when queue is read.
565 for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
566 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
568 /* Set up DPDKq->(TC,inq) mapping. */
569 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
570 if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) {
573 "Too many RX queues configured per TC %zu!\n",
577 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
578 uint8_t idx = port_cfg->tc[tc].inq[i];
580 if (idx > RTE_DIM(priv->rxq_map)) {
581 RTE_LOG(ERR, PMD, "Bad queue index %d!\n", idx);
585 priv->rxq_map[idx].tc = tc;
586 priv->rxq_map[idx].inq = i;
591 * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
592 * with no gaps. Empty TC means end of processing.
594 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
595 if (port_cfg->tc[i].inqs == 0)
597 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
598 port_cfg->tc[i].inqs,
602 priv->ppio_params.inqs_params.num_tcs = i;
610 * Finalize QoS table configuration and initialize it in SDK. It can be done
611 * only after port is started, so we have a valid ppio reference.
613 * @param priv Port's private (configuration) data.
614 * @returns 0 in case of success, exits otherwise.
617 mrvl_start_qos_mapping(struct mrvl_priv *priv)
621 if (priv->ppio == NULL) {
622 RTE_LOG(ERR, PMD, "ppio must not be NULL here!\n");
626 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
627 priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio;
629 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
630 priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio;
632 /* Initialize Classifier QoS table. */
634 return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);