54e682a36ff82364e86e591a6da9703ec7e95db7
[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         *val = strtoul(string, &endptr, 0);
103         if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len)
104                 return -2;
105
106         return 0;
107 }
108
109 /**
110  * Read out-queue configuration from file.
111  *
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.
117  */
118 static int
119 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
120                 struct mrvl_qos_cfg *cfg)
121 {
122         char sec_name[32];
123         const char *entry;
124         uint32_t val;
125
126         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
127                 MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
128
129         /* Skip non-existing */
130         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
131                 return 0;
132
133         entry = rte_cfgfile_get_entry(file, sec_name,
134                         MRVL_TOK_WEIGHT);
135         if (entry) {
136                 if (get_val_securely(entry, &val) < 0)
137                         return -1;
138                 cfg->port[port].outq[outq].weight = (uint8_t)val;
139         }
140
141         return 0;
142 }
143
144 /**
145  * Gets multiple-entry values and places them in table.
146  *
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.
153  *
154  * This function is purely utilitary, it does not print any error, only returns
155  * different error numbers.
156  *
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.
168  */
169 static int
170 get_entry_values(const char *entry, uint8_t *tab,
171         size_t elem_sz, uint8_t max_elems, uint8_t max_val)
172 {
173         /* There should not be more tokens than max elements.
174          * Add 1 for error trap.
175          */
176         char *tokens[max_elems + 1];
177
178         /* Begin, End + error trap = 3. */
179         char *rng_tokens[MAX_RNG_TOKENS + 1];
180         long beg, end;
181         uint32_t token_val;
182         int nb_tokens, nb_rng_tokens;
183         int i;
184         int values = 0;
185         char val;
186         char entry_cpy[CFG_VALUE_LEN];
187
188         if (elem_sz != 1)
189                 return -1;
190
191         /* Copy the entry to safely use rte_strsplit(). */
192         snprintf(entry_cpy, RTE_DIM(entry_cpy), "%s", entry);
193
194         /*
195          * If there are more tokens than array size, rte_strsplit will
196          * not return error, just array size.
197          */
198         nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
199                 tokens, max_elems + 1, ' ');
200
201         /* Quick check, will be refined later. */
202         if (nb_tokens > max_elems)
203                 return -2;
204
205         for (i = 0; i < nb_tokens; ++i) {
206                 if (strchr(tokens[i], '-') != NULL) {
207                         /*
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.
211                          */
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)
216                                 return -3;
217
218                         /* Range and sanity checks. */
219                         if (get_val_securely(rng_tokens[0], &token_val) < 0)
220                                 return -4;
221                         beg = (char)token_val;
222                         if (get_val_securely(rng_tokens[1], &token_val) < 0)
223                                 return -4;
224                         end = (char)token_val;
225                         if (beg < 0 || beg > UCHAR_MAX ||
226                                 end < 0 || end > UCHAR_MAX || end < beg)
227                                 return -4;
228
229                         for (val = beg; val <= end; ++val) {
230                                 if (val > max_val)
231                                         return -5;
232
233                                 *tab = val;
234                                 tab = RTE_PTR_ADD(tab, elem_sz);
235                                 ++values;
236                                 if (values >= max_elems)
237                                         return -2;
238                         }
239                 } else {
240                         /* Single values. */
241                         if (get_val_securely(tokens[i], &token_val) < 0)
242                                 return -5;
243                         val = (char)token_val;
244                         if (val > max_val)
245                                 return -5;
246
247                         *tab = val;
248                         tab = RTE_PTR_ADD(tab, elem_sz);
249                         ++values;
250                         if (values >= max_elems)
251                                 return -2;
252                 }
253         }
254
255         return values;
256 }
257
258 /**
259  * Parse Traffic Class'es mapping configuration.
260  *
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.
266  */
267 static int
268 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
269                 struct mrvl_qos_cfg *cfg)
270 {
271         char sec_name[32];
272         const char *entry;
273         int n;
274
275         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
276                 MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
277
278         /* Skip non-existing */
279         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
280                 return 0;
281
282         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
283         if (entry) {
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),
288                         MRVL_PP2_RXQ_MAX);
289                 if (n < 0) {
290                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
291                                 n, entry);
292                         return n;
293                 }
294                 cfg->port[port].tc[tc].inqs = n;
295         }
296
297         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
298         if (entry) {
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),
303                         MAX_PCP);
304                 if (n < 0) {
305                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
306                                 n, entry);
307                         return n;
308                 }
309                 cfg->port[port].tc[tc].pcps = n;
310         }
311
312         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
313         if (entry) {
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),
318                         MAX_DSCP);
319                 if (n < 0) {
320                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
321                                 n, entry);
322                         return n;
323                 }
324                 cfg->port[port].tc[tc].dscps = n;
325         }
326         return 0;
327 }
328
329 /**
330  * Parse QoS configuration - rte_kvargs_process handler.
331  *
332  * Opens configuration file and parses its content.
333  *
334  * @param key Unused.
335  * @param path Path to config file.
336  * @param extra_args Pointer to configuration structure.
337  * @returns 0 in case of success, exits otherwise.
338  */
339 int
340 mrvl_get_qoscfg(const char *key __rte_unused, const char *path,
341                 void *extra_args)
342 {
343         struct mrvl_qos_cfg **cfg = extra_args;
344         struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
345         uint32_t val;
346         int n, i, ret;
347         const char *entry;
348         char sec_name[32];
349
350         if (file == NULL)
351                 rte_exit(EXIT_FAILURE, "Cannot load configuration %s\n", path);
352
353         /* Create configuration. This is never accessed on the fast path,
354          * so we can ignore socket.
355          */
356         *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0);
357         if (*cfg == NULL)
358                 rte_exit(EXIT_FAILURE, "Cannot allocate configuration %s\n",
359                         path);
360
361         n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
362                 sizeof(MRVL_TOK_PORT) - 1);
363
364         if (n == 0) {
365                 /* This is weird, but not bad. */
366                 RTE_LOG(WARNING, PMD, "Empty configuration file?\n");
367                 return 0;
368         }
369
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);
374
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;
381                         continue;
382                 }
383
384                 entry = rte_cfgfile_get_entry(file, sec_name,
385                                 MRVL_TOK_DEFAULT_TC);
386                 if (entry) {
387                         if (get_val_securely(entry, &val) < 0 ||
388                                 val > USHRT_MAX)
389                                 return -1;
390                         (*cfg)->port[n].default_tc = (uint8_t)val;
391                 } else {
392                         RTE_LOG(ERR, PMD,
393                                 "Default Traffic Class required in custom configuration!\n");
394                         return -1;
395                 }
396
397                 entry = rte_cfgfile_get_entry(file, sec_name,
398                                 MRVL_TOK_MAPPING_PRIORITY);
399                 if (entry) {
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;
416                         else
417                                 rte_exit(EXIT_FAILURE,
418                                         "Error in parsing %s value (%s)!\n",
419                                         MRVL_TOK_MAPPING_PRIORITY, entry);
420                 } else {
421                         (*cfg)->port[n].mapping_priority =
422                                 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
423                 }
424
425                 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
426                         ret = get_outq_cfg(file, n, i, *cfg);
427                         if (ret < 0)
428                                 rte_exit(EXIT_FAILURE,
429                                         "Error %d parsing port %d outq %d!\n",
430                                         ret, n, i);
431                 }
432
433                 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
434                         ret = parse_tc_cfg(file, n, i, *cfg);
435                         if (ret < 0)
436                                 rte_exit(EXIT_FAILURE,
437                                         "Error %d parsing port %d tc %d!\n",
438                                         ret, n, i);
439                 }
440         }
441
442         return 0;
443 }
444
445 /**
446  * Setup Traffic Class.
447  *
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.
453  */
454 static int
455 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
456         struct pp2_bpool *bpool)
457 {
458         struct pp2_ppio_inq_params *inq_params;
459
460         param->pkt_offset = MRVL_PKT_OFFS;
461         param->pools[0] = bpool;
462
463         inq_params = rte_zmalloc_socket("inq_params",
464                 inqs * sizeof(*inq_params),
465                 0, rte_socket_id());
466         if (!inq_params)
467                 return -ENOMEM;
468
469         param->num_in_qs = inqs;
470
471         /* Release old config if necessary. */
472         if (param->inqs_params)
473                 rte_free(param->inqs_params);
474
475         param->inqs_params = inq_params;
476
477         return 0;
478 }
479
480 /**
481  * Configure RX Queues in a given port.
482  *
483  * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
484  *
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.
489  */
490 int
491 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid,
492         uint16_t max_queues)
493 {
494         size_t i, tc;
495
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);
502
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;
507                 }
508                 return 0;
509         }
510
511         /* We need only a subset of configuration. */
512         struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid];
513
514         priv->qos_tbl_params.type = port_cfg->mapping_priority;
515
516         /*
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".
520          */
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;
523
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. */
528                         RTE_LOG(ERR, PMD,
529                                 "Too many PCPs configured in TC %zu!\n", tc);
530                         return -1;
531                 }
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;
535                 }
536         }
537
538         /*
539          * The same logic goes with DSCP.
540          * First, set all map elements to "default".
541          */
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;
545
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. */
550                         RTE_LOG(ERR, PMD,
551                                 "Too many DSCPs configured in TC %zu!\n", tc);
552                         return -1;
553                 }
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;
557                 }
558         }
559
560         /*
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.
564          */
565         for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
566                 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
567
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)) {
571                         /* Overflow. */
572                         RTE_LOG(ERR, PMD,
573                                 "Too many RX queues configured per TC %zu!\n",
574                                 tc);
575                         return -1;
576                 }
577                 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
578                         uint8_t idx = port_cfg->tc[tc].inq[i];
579
580                         if (idx > RTE_DIM(priv->rxq_map)) {
581                                 RTE_LOG(ERR, PMD, "Bad queue index %d!\n", idx);
582                                 return -1;
583                         }
584
585                         priv->rxq_map[idx].tc = tc;
586                         priv->rxq_map[idx].inq = i;
587                 }
588         }
589
590         /*
591          * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
592          * with no gaps. Empty TC means end of processing.
593          */
594         for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
595                 if (port_cfg->tc[i].inqs == 0)
596                         break;
597                 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
598                                 port_cfg->tc[i].inqs,
599                                 priv->bpool);
600         }
601
602         priv->ppio_params.inqs_params.num_tcs = i;
603
604         return 0;
605 }
606
607 /**
608  * Start QoS mapping.
609  *
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.
612  *
613  * @param priv Port's private (configuration) data.
614  * @returns 0 in case of success, exits otherwise.
615  */
616 int
617 mrvl_start_qos_mapping(struct mrvl_priv *priv)
618 {
619         size_t i;
620
621         if (priv->ppio == NULL) {
622                 RTE_LOG(ERR, PMD, "ppio must not be NULL here!\n");
623                 return -1;
624         }
625
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;
628
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;
631
632         /* Initialize Classifier QoS table. */
633
634         return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);
635 }