New upstream version 17.11.1
[deb_dpdk.git] / drivers / net / mrvl / mrvl_qos.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2017 Marvell International Ltd.
5  *   Copyright(c) 2017 Semihalf.
6  *   All rights reserved.
7  *
8  *   Redistribution and use in source and binary forms, with or without
9  *   modification, are permitted provided that the following conditions
10  *   are met:
11  *
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
17  *       distribution.
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.
21  *
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.
33  */
34
35 #include <stdint.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include <rte_common.h>
40 #include <rte_cfgfile.h>
41 #include <rte_log.h>
42 #include <rte_lcore.h>
43 #include <rte_malloc.h>
44 #include <rte_string_fns.h>
45
46 /* Unluckily, container_of is defined by both DPDK and MUSDK,
47  * we'll declare only one version.
48  *
49  * Note that it is not used in this PMD anyway.
50  */
51 #ifdef container_of
52 #undef container_of
53 #endif
54
55 #include "mrvl_qos.h"
56
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"
73
74 /** Number of tokens in range a-b = 2. */
75 #define MAX_RNG_TOKENS 2
76
77 /** Maximum possible value of PCP. */
78 #define MAX_PCP 7
79
80 /** Maximum possible value of DSCP. */
81 #define MAX_DSCP 63
82
83 /** Global QoS configuration. */
84 struct mrvl_qos_cfg *mrvl_qos_cfg;
85
86 /**
87  * Convert string to uint32_t with extra checks for result correctness.
88  *
89  * @param string String to convert.
90  * @param val Conversion result.
91  * @returns 0 in case of success, negative value otherwise.
92  */
93 static int
94 get_val_securely(const char *string, uint32_t *val)
95 {
96         char *endptr;
97         size_t len = strlen(string);
98
99         if (len == 0)
100                 return -1;
101
102         errno = 0;
103         *val = strtoul(string, &endptr, 0);
104         if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len)
105                 return -2;
106
107         return 0;
108 }
109
110 /**
111  * Read out-queue configuration from file.
112  *
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.
118  */
119 static int
120 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
121                 struct mrvl_qos_cfg *cfg)
122 {
123         char sec_name[32];
124         const char *entry;
125         uint32_t val;
126
127         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
128                 MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
129
130         /* Skip non-existing */
131         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
132                 return 0;
133
134         entry = rte_cfgfile_get_entry(file, sec_name,
135                         MRVL_TOK_WEIGHT);
136         if (entry) {
137                 if (get_val_securely(entry, &val) < 0)
138                         return -1;
139                 cfg->port[port].outq[outq].weight = (uint8_t)val;
140         }
141
142         return 0;
143 }
144
145 /**
146  * Gets multiple-entry values and places them in table.
147  *
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.
154  *
155  * This function is purely utilitary, it does not print any error, only returns
156  * different error numbers.
157  *
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.
169  */
170 static int
171 get_entry_values(const char *entry, uint8_t *tab,
172         size_t elem_sz, uint8_t max_elems, uint8_t max_val)
173 {
174         /* There should not be more tokens than max elements.
175          * Add 1 for error trap.
176          */
177         char *tokens[max_elems + 1];
178
179         /* Begin, End + error trap = 3. */
180         char *rng_tokens[MAX_RNG_TOKENS + 1];
181         long beg, end;
182         uint32_t token_val;
183         int nb_tokens, nb_rng_tokens;
184         int i;
185         int values = 0;
186         char val;
187         char entry_cpy[CFG_VALUE_LEN];
188
189         if (elem_sz != 1)
190                 return -1;
191
192         /* Copy the entry to safely use rte_strsplit(). */
193         snprintf(entry_cpy, RTE_DIM(entry_cpy), "%s", entry);
194
195         /*
196          * If there are more tokens than array size, rte_strsplit will
197          * not return error, just array size.
198          */
199         nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
200                 tokens, max_elems + 1, ' ');
201
202         /* Quick check, will be refined later. */
203         if (nb_tokens > max_elems)
204                 return -2;
205
206         for (i = 0; i < nb_tokens; ++i) {
207                 if (strchr(tokens[i], '-') != NULL) {
208                         /*
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.
212                          */
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)
217                                 return -3;
218
219                         /* Range and sanity checks. */
220                         if (get_val_securely(rng_tokens[0], &token_val) < 0)
221                                 return -4;
222                         beg = (char)token_val;
223                         if (get_val_securely(rng_tokens[1], &token_val) < 0)
224                                 return -4;
225                         end = (char)token_val;
226                         if (beg < 0 || beg > UCHAR_MAX ||
227                                 end < 0 || end > UCHAR_MAX || end < beg)
228                                 return -4;
229
230                         for (val = beg; val <= end; ++val) {
231                                 if (val > max_val)
232                                         return -5;
233
234                                 *tab = val;
235                                 tab = RTE_PTR_ADD(tab, elem_sz);
236                                 ++values;
237                                 if (values >= max_elems)
238                                         return -2;
239                         }
240                 } else {
241                         /* Single values. */
242                         if (get_val_securely(tokens[i], &token_val) < 0)
243                                 return -5;
244                         val = (char)token_val;
245                         if (val > max_val)
246                                 return -5;
247
248                         *tab = val;
249                         tab = RTE_PTR_ADD(tab, elem_sz);
250                         ++values;
251                         if (values >= max_elems)
252                                 return -2;
253                 }
254         }
255
256         return values;
257 }
258
259 /**
260  * Parse Traffic Class'es mapping configuration.
261  *
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.
267  */
268 static int
269 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
270                 struct mrvl_qos_cfg *cfg)
271 {
272         char sec_name[32];
273         const char *entry;
274         int n;
275
276         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
277                 MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
278
279         /* Skip non-existing */
280         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
281                 return 0;
282
283         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
284         if (entry) {
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),
289                         MRVL_PP2_RXQ_MAX);
290                 if (n < 0) {
291                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
292                                 n, entry);
293                         return n;
294                 }
295                 cfg->port[port].tc[tc].inqs = n;
296         }
297
298         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
299         if (entry) {
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),
304                         MAX_PCP);
305                 if (n < 0) {
306                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
307                                 n, entry);
308                         return n;
309                 }
310                 cfg->port[port].tc[tc].pcps = n;
311         }
312
313         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
314         if (entry) {
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),
319                         MAX_DSCP);
320                 if (n < 0) {
321                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
322                                 n, entry);
323                         return n;
324                 }
325                 cfg->port[port].tc[tc].dscps = n;
326         }
327         return 0;
328 }
329
330 /**
331  * Parse QoS configuration - rte_kvargs_process handler.
332  *
333  * Opens configuration file and parses its content.
334  *
335  * @param key Unused.
336  * @param path Path to config file.
337  * @param extra_args Pointer to configuration structure.
338  * @returns 0 in case of success, exits otherwise.
339  */
340 int
341 mrvl_get_qoscfg(const char *key __rte_unused, const char *path,
342                 void *extra_args)
343 {
344         struct mrvl_qos_cfg **cfg = extra_args;
345         struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
346         uint32_t val;
347         int n, i, ret;
348         const char *entry;
349         char sec_name[32];
350
351         if (file == NULL)
352                 rte_exit(EXIT_FAILURE, "Cannot load configuration %s\n", path);
353
354         /* Create configuration. This is never accessed on the fast path,
355          * so we can ignore socket.
356          */
357         *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0);
358         if (*cfg == NULL)
359                 rte_exit(EXIT_FAILURE, "Cannot allocate configuration %s\n",
360                         path);
361
362         n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
363                 sizeof(MRVL_TOK_PORT) - 1);
364
365         if (n == 0) {
366                 /* This is weird, but not bad. */
367                 RTE_LOG(WARNING, PMD, "Empty configuration file?\n");
368                 return 0;
369         }
370
371         /* Use the number of ports given as vdev parameters. */
372         for (n = 0; n < (PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC); ++n) {
373                 snprintf(sec_name, sizeof(sec_name), "%s %d %s",
374                         MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT);
375
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;
382                         continue;
383                 }
384
385                 entry = rte_cfgfile_get_entry(file, sec_name,
386                                 MRVL_TOK_DEFAULT_TC);
387                 if (entry) {
388                         if (get_val_securely(entry, &val) < 0 ||
389                                 val > USHRT_MAX)
390                                 return -1;
391                         (*cfg)->port[n].default_tc = (uint8_t)val;
392                 } else {
393                         RTE_LOG(ERR, PMD,
394                                 "Default Traffic Class required in custom configuration!\n");
395                         return -1;
396                 }
397
398                 entry = rte_cfgfile_get_entry(file, sec_name,
399                                 MRVL_TOK_MAPPING_PRIORITY);
400                 if (entry) {
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;
417                         else
418                                 rte_exit(EXIT_FAILURE,
419                                         "Error in parsing %s value (%s)!\n",
420                                         MRVL_TOK_MAPPING_PRIORITY, entry);
421                 } else {
422                         (*cfg)->port[n].mapping_priority =
423                                 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
424                 }
425
426                 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
427                         ret = get_outq_cfg(file, n, i, *cfg);
428                         if (ret < 0)
429                                 rte_exit(EXIT_FAILURE,
430                                         "Error %d parsing port %d outq %d!\n",
431                                         ret, n, i);
432                 }
433
434                 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
435                         ret = parse_tc_cfg(file, n, i, *cfg);
436                         if (ret < 0)
437                                 rte_exit(EXIT_FAILURE,
438                                         "Error %d parsing port %d tc %d!\n",
439                                         ret, n, i);
440                 }
441         }
442
443         return 0;
444 }
445
446 /**
447  * Setup Traffic Class.
448  *
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.
454  */
455 static int
456 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
457         struct pp2_bpool *bpool)
458 {
459         struct pp2_ppio_inq_params *inq_params;
460
461         param->pkt_offset = MRVL_PKT_OFFS;
462         param->pools[0] = bpool;
463
464         inq_params = rte_zmalloc_socket("inq_params",
465                 inqs * sizeof(*inq_params),
466                 0, rte_socket_id());
467         if (!inq_params)
468                 return -ENOMEM;
469
470         param->num_in_qs = inqs;
471
472         /* Release old config if necessary. */
473         if (param->inqs_params)
474                 rte_free(param->inqs_params);
475
476         param->inqs_params = inq_params;
477
478         return 0;
479 }
480
481 /**
482  * Configure RX Queues in a given port.
483  *
484  * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
485  *
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.
490  */
491 int
492 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid,
493         uint16_t max_queues)
494 {
495         size_t i, tc;
496
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);
503
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;
508                 }
509                 return 0;
510         }
511
512         /* We need only a subset of configuration. */
513         struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid];
514
515         priv->qos_tbl_params.type = port_cfg->mapping_priority;
516
517         /*
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".
521          */
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;
524
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. */
529                         RTE_LOG(ERR, PMD,
530                                 "Too many PCPs configured in TC %zu!\n", tc);
531                         return -1;
532                 }
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;
536                 }
537         }
538
539         /*
540          * The same logic goes with DSCP.
541          * First, set all map elements to "default".
542          */
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;
546
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. */
551                         RTE_LOG(ERR, PMD,
552                                 "Too many DSCPs configured in TC %zu!\n", tc);
553                         return -1;
554                 }
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;
558                 }
559         }
560
561         /*
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.
565          */
566         for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
567                 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
568
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)) {
572                         /* Overflow. */
573                         RTE_LOG(ERR, PMD,
574                                 "Too many RX queues configured per TC %zu!\n",
575                                 tc);
576                         return -1;
577                 }
578                 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
579                         uint8_t idx = port_cfg->tc[tc].inq[i];
580
581                         if (idx > RTE_DIM(priv->rxq_map)) {
582                                 RTE_LOG(ERR, PMD, "Bad queue index %d!\n", idx);
583                                 return -1;
584                         }
585
586                         priv->rxq_map[idx].tc = tc;
587                         priv->rxq_map[idx].inq = i;
588                 }
589         }
590
591         /*
592          * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
593          * with no gaps. Empty TC means end of processing.
594          */
595         for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
596                 if (port_cfg->tc[i].inqs == 0)
597                         break;
598                 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
599                                 port_cfg->tc[i].inqs,
600                                 priv->bpool);
601         }
602
603         priv->ppio_params.inqs_params.num_tcs = i;
604
605         return 0;
606 }
607
608 /**
609  * Start QoS mapping.
610  *
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.
613  *
614  * @param priv Port's private (configuration) data.
615  * @returns 0 in case of success, exits otherwise.
616  */
617 int
618 mrvl_start_qos_mapping(struct mrvl_priv *priv)
619 {
620         size_t i;
621
622         if (priv->ppio == NULL) {
623                 RTE_LOG(ERR, PMD, "ppio must not be NULL here!\n");
624                 return -1;
625         }
626
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;
629
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;
632
633         /* Initialize Classifier QoS table. */
634
635         return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);
636 }