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);
103 *val = strtoul(string, &endptr, 0);
104 if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len)
111 * Read out-queue configuration from file.
113 * @param file Path to the configuration file.
114 * @param port Port number.
115 * @param outq Out queue number.
116 * @param cfg Pointer to the Marvell QoS configuration structure.
117 * @returns 0 in case of success, negative value otherwise.
120 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
121 struct mrvl_qos_cfg *cfg)
127 snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
128 MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
130 /* Skip non-existing */
131 if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
134 entry = rte_cfgfile_get_entry(file, sec_name,
137 if (get_val_securely(entry, &val) < 0)
139 cfg->port[port].outq[outq].weight = (uint8_t)val;
146 * Gets multiple-entry values and places them in table.
148 * Entry can be anything, e.g. "1 2-3 5 6 7-9". This needs to be converted to
149 * table entries, respectively: {1, 2, 3, 5, 6, 7, 8, 9}.
150 * As all result table's elements are always 1-byte long, we
151 * won't overcomplicate the function, but we'll keep API generic,
152 * check if someone hasn't changed element size and make it simple
153 * to extend to other sizes.
155 * This function is purely utilitary, it does not print any error, only returns
156 * different error numbers.
158 * @param entry[in] Values string to parse.
159 * @param tab[out] Results table.
160 * @param elem_sz[in] Element size (in bytes).
161 * @param max_elems[in] Number of results table elements available.
162 * @param max val[in] Maximum value allowed.
163 * @returns Number of correctly parsed elements in case of success.
164 * @retval -1 Wrong element size.
165 * @retval -2 More tokens than result table allows.
166 * @retval -3 Wrong range syntax.
167 * @retval -4 Wrong range values.
168 * @retval -5 Maximum value exceeded.
171 get_entry_values(const char *entry, uint8_t *tab,
172 size_t elem_sz, uint8_t max_elems, uint8_t max_val)
174 /* There should not be more tokens than max elements.
175 * Add 1 for error trap.
177 char *tokens[max_elems + 1];
179 /* Begin, End + error trap = 3. */
180 char *rng_tokens[MAX_RNG_TOKENS + 1];
183 int nb_tokens, nb_rng_tokens;
187 char entry_cpy[CFG_VALUE_LEN];
192 /* Copy the entry to safely use rte_strsplit(). */
193 snprintf(entry_cpy, RTE_DIM(entry_cpy), "%s", entry);
196 * If there are more tokens than array size, rte_strsplit will
197 * not return error, just array size.
199 nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
200 tokens, max_elems + 1, ' ');
202 /* Quick check, will be refined later. */
203 if (nb_tokens > max_elems)
206 for (i = 0; i < nb_tokens; ++i) {
207 if (strchr(tokens[i], '-') != NULL) {
209 * Split to begin and end tokens.
210 * We want to catch error cases too, thus we leave
211 * option for number of tokens to be more than 2.
213 nb_rng_tokens = rte_strsplit(tokens[i],
214 strlen(tokens[i]), rng_tokens,
215 RTE_DIM(rng_tokens), '-');
216 if (nb_rng_tokens != 2)
219 /* Range and sanity checks. */
220 if (get_val_securely(rng_tokens[0], &token_val) < 0)
222 beg = (char)token_val;
223 if (get_val_securely(rng_tokens[1], &token_val) < 0)
225 end = (char)token_val;
226 if (beg < 0 || beg > UCHAR_MAX ||
227 end < 0 || end > UCHAR_MAX || end < beg)
230 for (val = beg; val <= end; ++val) {
235 tab = RTE_PTR_ADD(tab, elem_sz);
237 if (values >= max_elems)
242 if (get_val_securely(tokens[i], &token_val) < 0)
244 val = (char)token_val;
249 tab = RTE_PTR_ADD(tab, elem_sz);
251 if (values >= max_elems)
260 * Parse Traffic Class'es mapping configuration.
262 * @param file Config file handle.
263 * @param port Which port to look for.
264 * @param tc Which Traffic Class to look for.
265 * @param cfg[out] Parsing results.
266 * @returns 0 in case of success, negative value otherwise.
269 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
270 struct mrvl_qos_cfg *cfg)
276 snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
277 MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
279 /* Skip non-existing */
280 if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
283 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
285 n = get_entry_values(entry,
286 cfg->port[port].tc[tc].inq,
287 sizeof(cfg->port[port].tc[tc].inq[0]),
288 RTE_DIM(cfg->port[port].tc[tc].inq),
291 RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
295 cfg->port[port].tc[tc].inqs = n;
298 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
300 n = get_entry_values(entry,
301 cfg->port[port].tc[tc].pcp,
302 sizeof(cfg->port[port].tc[tc].pcp[0]),
303 RTE_DIM(cfg->port[port].tc[tc].pcp),
306 RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
310 cfg->port[port].tc[tc].pcps = n;
313 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
315 n = get_entry_values(entry,
316 cfg->port[port].tc[tc].dscp,
317 sizeof(cfg->port[port].tc[tc].dscp[0]),
318 RTE_DIM(cfg->port[port].tc[tc].dscp),
321 RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
325 cfg->port[port].tc[tc].dscps = n;
331 * Parse QoS configuration - rte_kvargs_process handler.
333 * Opens configuration file and parses its content.
336 * @param path Path to config file.
337 * @param extra_args Pointer to configuration structure.
338 * @returns 0 in case of success, exits otherwise.
341 mrvl_get_qoscfg(const char *key __rte_unused, const char *path,
344 struct mrvl_qos_cfg **cfg = extra_args;
345 struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
352 rte_exit(EXIT_FAILURE, "Cannot load configuration %s\n", path);
354 /* Create configuration. This is never accessed on the fast path,
355 * so we can ignore socket.
357 *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0);
359 rte_exit(EXIT_FAILURE, "Cannot allocate configuration %s\n",
362 n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
363 sizeof(MRVL_TOK_PORT) - 1);
366 /* This is weird, but not bad. */
367 RTE_LOG(WARNING, PMD, "Empty configuration file?\n");
371 /* Use the number of ports given as vdev parameters. */
372 for (n = 0; n < mrvl_ports_nb; ++n) {
373 snprintf(sec_name, sizeof(sec_name), "%s %d %s",
374 MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT);
376 /* Skip ports non-existing in configuration. */
377 if (rte_cfgfile_num_sections(file, sec_name,
378 strlen(sec_name)) <= 0) {
379 (*cfg)->port[n].use_global_defaults = 1;
380 (*cfg)->port[n].mapping_priority =
381 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
385 entry = rte_cfgfile_get_entry(file, sec_name,
386 MRVL_TOK_DEFAULT_TC);
388 if (get_val_securely(entry, &val) < 0 ||
391 (*cfg)->port[n].default_tc = (uint8_t)val;
394 "Default Traffic Class required in custom configuration!\n");
398 entry = rte_cfgfile_get_entry(file, sec_name,
399 MRVL_TOK_MAPPING_PRIORITY);
401 if (!strncmp(entry, MRVL_TOK_VLAN_IP,
402 sizeof(MRVL_TOK_VLAN_IP)))
403 (*cfg)->port[n].mapping_priority =
404 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
405 else if (!strncmp(entry, MRVL_TOK_IP_VLAN,
406 sizeof(MRVL_TOK_IP_VLAN)))
407 (*cfg)->port[n].mapping_priority =
408 PP2_CLS_QOS_TBL_IP_VLAN_PRI;
409 else if (!strncmp(entry, MRVL_TOK_IP,
410 sizeof(MRVL_TOK_IP)))
411 (*cfg)->port[n].mapping_priority =
412 PP2_CLS_QOS_TBL_IP_PRI;
413 else if (!strncmp(entry, MRVL_TOK_VLAN,
414 sizeof(MRVL_TOK_VLAN)))
415 (*cfg)->port[n].mapping_priority =
416 PP2_CLS_QOS_TBL_VLAN_PRI;
418 rte_exit(EXIT_FAILURE,
419 "Error in parsing %s value (%s)!\n",
420 MRVL_TOK_MAPPING_PRIORITY, entry);
422 (*cfg)->port[n].mapping_priority =
423 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
426 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
427 ret = get_outq_cfg(file, n, i, *cfg);
429 rte_exit(EXIT_FAILURE,
430 "Error %d parsing port %d outq %d!\n",
434 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
435 ret = parse_tc_cfg(file, n, i, *cfg);
437 rte_exit(EXIT_FAILURE,
438 "Error %d parsing port %d tc %d!\n",
447 * Setup Traffic Class.
449 * Fill in TC parameters in single MUSDK TC config entry.
450 * @param param TC parameters entry.
451 * @param inqs Number of MUSDK in-queues in this TC.
452 * @param bpool Bpool for this TC.
453 * @returns 0 in case of success, exits otherwise.
456 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
457 struct pp2_bpool *bpool)
459 struct pp2_ppio_inq_params *inq_params;
461 param->pkt_offset = MRVL_PKT_OFFS;
462 param->pools[0] = bpool;
464 inq_params = rte_zmalloc_socket("inq_params",
465 inqs * sizeof(*inq_params),
470 param->num_in_qs = inqs;
472 /* Release old config if necessary. */
473 if (param->inqs_params)
474 rte_free(param->inqs_params);
476 param->inqs_params = inq_params;
482 * Configure RX Queues in a given port.
484 * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
486 * @param priv Port's private data
487 * @param portid DPDK port ID
488 * @param max_queues Maximum number of queues to configure.
489 * @returns 0 in case of success, negative value otherwise.
492 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid,
497 if (mrvl_qos_cfg == NULL ||
498 mrvl_qos_cfg->port[portid].use_global_defaults) {
499 /* No port configuration, use default: 1 TC, no QoS. */
500 priv->ppio_params.inqs_params.num_tcs = 1;
501 setup_tc(&priv->ppio_params.inqs_params.tcs_params[0],
502 max_queues, priv->bpool);
504 /* Direct mapping of queues i.e. 0->0, 1->1 etc. */
505 for (i = 0; i < max_queues; ++i) {
506 priv->rxq_map[i].tc = 0;
507 priv->rxq_map[i].inq = i;
512 /* We need only a subset of configuration. */
513 struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid];
515 priv->qos_tbl_params.type = port_cfg->mapping_priority;
518 * We need to reverse mapping, from tc->pcp (better from usability
519 * point of view) to pcp->tc (configurable in MUSDK).
520 * First, set all map elements to "default".
522 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
523 priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc;
525 /* Then, fill in all known values. */
526 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
527 if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) {
528 /* Better safe than sorry. */
530 "Too many PCPs configured in TC %zu!\n", tc);
533 for (i = 0; i < port_cfg->tc[tc].pcps; ++i) {
534 priv->qos_tbl_params.pcp_cos_map[
535 port_cfg->tc[tc].pcp[i]].tc = tc;
540 * The same logic goes with DSCP.
541 * First, set all map elements to "default".
543 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
544 priv->qos_tbl_params.dscp_cos_map[i].tc =
545 port_cfg->default_tc;
547 /* Fill in all known values. */
548 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
549 if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) {
550 /* Better safe than sorry. */
552 "Too many DSCPs configured in TC %zu!\n", tc);
555 for (i = 0; i < port_cfg->tc[tc].dscps; ++i) {
556 priv->qos_tbl_params.dscp_cos_map[
557 port_cfg->tc[tc].dscp[i]].tc = tc;
562 * Surprisingly, similar logic goes with queue mapping.
563 * We need only to store qid->tc mapping,
564 * to know TC when queue is read.
566 for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
567 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
569 /* Set up DPDKq->(TC,inq) mapping. */
570 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
571 if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) {
574 "Too many RX queues configured per TC %zu!\n",
578 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
579 uint8_t idx = port_cfg->tc[tc].inq[i];
581 if (idx > RTE_DIM(priv->rxq_map)) {
582 RTE_LOG(ERR, PMD, "Bad queue index %d!\n", idx);
586 priv->rxq_map[idx].tc = tc;
587 priv->rxq_map[idx].inq = i;
592 * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
593 * with no gaps. Empty TC means end of processing.
595 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
596 if (port_cfg->tc[i].inqs == 0)
598 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
599 port_cfg->tc[i].inqs,
603 priv->ppio_params.inqs_params.num_tcs = i;
611 * Finalize QoS table configuration and initialize it in SDK. It can be done
612 * only after port is started, so we have a valid ppio reference.
614 * @param priv Port's private (configuration) data.
615 * @returns 0 in case of success, exits otherwise.
618 mrvl_start_qos_mapping(struct mrvl_priv *priv)
622 if (priv->ppio == NULL) {
623 RTE_LOG(ERR, PMD, "ppio must not be NULL here!\n");
627 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
628 priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio;
630 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
631 priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio;
633 /* Initialize Classifier QoS table. */
635 return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);