net/mrvl: add net PMD skeleton
[dpdk.git] / drivers / net / mrvl / mrvl_qos.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2017 Semihalf. All rights reserved.
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above copyright
13  *       notice, this list of conditions and the following disclaimer in
14  *       the documentation and/or other materials provided with the
15  *       distribution.
16  *     * Neither the name of Semihalf nor the names of its
17  *       contributors may be used to endorse or promote products derived
18  *       from this software without specific prior written permission.
19  *
20  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <stdint.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <rte_common.h>
38 #include <rte_cfgfile.h>
39 #include <rte_log.h>
40 #include <rte_lcore.h>
41 #include <rte_malloc.h>
42 #include <rte_string_fns.h>
43
44 /* Unluckily, container_of is defined by both DPDK and MUSDK,
45  * we'll declare only one version.
46  *
47  * Note that it is not used in this PMD anyway.
48  */
49 #ifdef container_of
50 #undef container_of
51 #endif
52
53 #include "mrvl_qos.h"
54
55 /* Parsing tokens. Defined conveniently, so that any correction is easy. */
56 #define MRVL_TOK_DEFAULT "default"
57 #define MRVL_TOK_DEFAULT_TC "default_tc"
58 #define MRVL_TOK_DSCP "dscp"
59 #define MRVL_TOK_MAPPING_PRIORITY "mapping_priority"
60 #define MRVL_TOK_IP "ip"
61 #define MRVL_TOK_IP_VLAN "ip/vlan"
62 #define MRVL_TOK_PCP "pcp"
63 #define MRVL_TOK_PORT "port"
64 #define MRVL_TOK_RXQ "rxq"
65 #define MRVL_TOK_SP "SP"
66 #define MRVL_TOK_TC "tc"
67 #define MRVL_TOK_TXQ "txq"
68 #define MRVL_TOK_VLAN "vlan"
69 #define MRVL_TOK_VLAN_IP "vlan/ip"
70 #define MRVL_TOK_WEIGHT "weight"
71
72 /** Number of tokens in range a-b = 2. */
73 #define MAX_RNG_TOKENS 2
74
75 /** Maximum possible value of PCP. */
76 #define MAX_PCP 7
77
78 /** Maximum possible value of DSCP. */
79 #define MAX_DSCP 63
80
81 /** Global QoS configuration. */
82 struct mrvl_qos_cfg *mrvl_qos_cfg;
83
84 /**
85  * Convert string to uint32_t with extra checks for result correctness.
86  *
87  * @param string String to convert.
88  * @param val Conversion result.
89  * @returns 0 in case of success, negative value otherwise.
90  */
91 static int
92 get_val_securely(const char *string, uint32_t *val)
93 {
94         char *endptr;
95         size_t len = strlen(string);
96
97         if (len == 0)
98                 return -1;
99
100         *val = strtoul(string, &endptr, 0);
101         if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len)
102                 return -2;
103
104         return 0;
105 }
106
107 /**
108  * Read out-queue configuration from file.
109  *
110  * @param file Path to the configuration file.
111  * @param port Port number.
112  * @param outq Out queue number.
113  * @param cfg Pointer to the Marvell QoS configuration structure.
114  * @returns 0 in case of success, negative value otherwise.
115  */
116 static int
117 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
118                 struct mrvl_qos_cfg *cfg)
119 {
120         char sec_name[32];
121         const char *entry;
122         uint32_t val;
123
124         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
125                 MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
126
127         /* Skip non-existing */
128         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
129                 return 0;
130
131         entry = rte_cfgfile_get_entry(file, sec_name,
132                         MRVL_TOK_WEIGHT);
133         if (entry) {
134                 if (get_val_securely(entry, &val) < 0)
135                         return -1;
136                 cfg->port[port].outq[outq].weight = (uint8_t)val;
137         }
138
139         return 0;
140 }
141
142 /**
143  * Gets multiple-entry values and places them in table.
144  *
145  * Entry can be anything, e.g. "1 2-3 5 6 7-9". This needs to be converted to
146  * table entries, respectively: {1, 2, 3, 5, 6, 7, 8, 9}.
147  * As all result table's elements are always 1-byte long, we
148  * won't overcomplicate the function, but we'll keep API generic,
149  * check if someone hasn't changed element size and make it simple
150  * to extend to other sizes.
151  *
152  * This function is purely utilitary, it does not print any error, only returns
153  * different error numbers.
154  *
155  * @param entry[in] Values string to parse.
156  * @param tab[out] Results table.
157  * @param elem_sz[in] Element size (in bytes).
158  * @param max_elems[in] Number of results table elements available.
159  * @param max val[in] Maximum value allowed.
160  * @returns Number of correctly parsed elements in case of success.
161  * @retval -1 Wrong element size.
162  * @retval -2 More tokens than result table allows.
163  * @retval -3 Wrong range syntax.
164  * @retval -4 Wrong range values.
165  * @retval -5 Maximum value exceeded.
166  */
167 static int
168 get_entry_values(const char *entry, uint8_t *tab,
169         size_t elem_sz, uint8_t max_elems, uint8_t max_val)
170 {
171         /* There should not be more tokens than max elements.
172          * Add 1 for error trap.
173          */
174         char *tokens[max_elems + 1];
175
176         /* Begin, End + error trap = 3. */
177         char *rng_tokens[MAX_RNG_TOKENS + 1];
178         long beg, end;
179         uint32_t token_val;
180         int nb_tokens, nb_rng_tokens;
181         int i;
182         int values = 0;
183         char val;
184         char entry_cpy[CFG_VALUE_LEN];
185
186         if (elem_sz != 1)
187                 return -1;
188
189         /* Copy the entry to safely use rte_strsplit(). */
190         snprintf(entry_cpy, RTE_DIM(entry_cpy), "%s", entry);
191
192         /*
193          * If there are more tokens than array size, rte_strsplit will
194          * not return error, just array size.
195          */
196         nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
197                 tokens, max_elems + 1, ' ');
198
199         /* Quick check, will be refined later. */
200         if (nb_tokens > max_elems)
201                 return -2;
202
203         for (i = 0; i < nb_tokens; ++i) {
204                 if (strchr(tokens[i], '-') != NULL) {
205                         /*
206                          * Split to begin and end tokens.
207                          * We want to catch error cases too, thus we leave
208                          * option for number of tokens to be more than 2.
209                          */
210                         nb_rng_tokens = rte_strsplit(tokens[i],
211                                         strlen(tokens[i]), rng_tokens,
212                                         RTE_DIM(rng_tokens), '-');
213                         if (nb_rng_tokens != 2)
214                                 return -3;
215
216                         /* Range and sanity checks. */
217                         if (get_val_securely(rng_tokens[0], &token_val) < 0)
218                                 return -4;
219                         beg = (char)token_val;
220                         if (get_val_securely(rng_tokens[1], &token_val) < 0)
221                                 return -4;
222                         end = (char)token_val;
223                         if (beg < 0 || beg > UCHAR_MAX ||
224                                 end < 0 || end > UCHAR_MAX || end < beg)
225                                 return -4;
226
227                         for (val = beg; val <= end; ++val) {
228                                 if (val > max_val)
229                                         return -5;
230
231                                 *tab = val;
232                                 tab = RTE_PTR_ADD(tab, elem_sz);
233                                 ++values;
234                                 if (values >= max_elems)
235                                         return -2;
236                         }
237                 } else {
238                         /* Single values. */
239                         if (get_val_securely(tokens[i], &token_val) < 0)
240                                 return -5;
241                         val = (char)token_val;
242                         if (val > max_val)
243                                 return -5;
244
245                         *tab = val;
246                         tab = RTE_PTR_ADD(tab, elem_sz);
247                         ++values;
248                         if (values >= max_elems)
249                                 return -2;
250                 }
251         }
252
253         return values;
254 }
255
256 /**
257  * Parse Traffic Class'es mapping configuration.
258  *
259  * @param file Config file handle.
260  * @param port Which port to look for.
261  * @param tc Which Traffic Class to look for.
262  * @param cfg[out] Parsing results.
263  * @returns 0 in case of success, negative value otherwise.
264  */
265 static int
266 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
267                 struct mrvl_qos_cfg *cfg)
268 {
269         char sec_name[32];
270         const char *entry;
271         int n;
272
273         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
274                 MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
275
276         /* Skip non-existing */
277         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
278                 return 0;
279
280         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
281         if (entry) {
282                 n = get_entry_values(entry,
283                         cfg->port[port].tc[tc].inq,
284                         sizeof(cfg->port[port].tc[tc].inq[0]),
285                         RTE_DIM(cfg->port[port].tc[tc].inq),
286                         MRVL_PP2_RXQ_MAX);
287                 if (n < 0) {
288                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
289                                 n, entry);
290                         return n;
291                 }
292                 cfg->port[port].tc[tc].inqs = n;
293         }
294
295         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
296         if (entry) {
297                 n = get_entry_values(entry,
298                         cfg->port[port].tc[tc].pcp,
299                         sizeof(cfg->port[port].tc[tc].pcp[0]),
300                         RTE_DIM(cfg->port[port].tc[tc].pcp),
301                         MAX_PCP);
302                 if (n < 0) {
303                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
304                                 n, entry);
305                         return n;
306                 }
307                 cfg->port[port].tc[tc].pcps = n;
308         }
309
310         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
311         if (entry) {
312                 n = get_entry_values(entry,
313                         cfg->port[port].tc[tc].dscp,
314                         sizeof(cfg->port[port].tc[tc].dscp[0]),
315                         RTE_DIM(cfg->port[port].tc[tc].dscp),
316                         MAX_DSCP);
317                 if (n < 0) {
318                         RTE_LOG(ERR, PMD, "Error %d while parsing: %s\n",
319                                 n, entry);
320                         return n;
321                 }
322                 cfg->port[port].tc[tc].dscps = n;
323         }
324         return 0;
325 }
326
327 /**
328  * Parse QoS configuration - rte_kvargs_process handler.
329  *
330  * Opens configuration file and parses its content.
331  *
332  * @param key Unused.
333  * @param path Path to config file.
334  * @param extra_args Pointer to configuration structure.
335  * @returns 0 in case of success, exits otherwise.
336  */
337 int
338 mrvl_get_qoscfg(const char *key __rte_unused, const char *path,
339                 void *extra_args)
340 {
341         struct mrvl_qos_cfg **cfg = extra_args;
342         struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
343         uint32_t val;
344         int n, i, ret;
345         const char *entry;
346         char sec_name[32];
347
348         if (file == NULL)
349                 rte_exit(EXIT_FAILURE, "Cannot load configuration %s\n", path);
350
351         /* Create configuration. This is never accessed on the fast path,
352          * so we can ignore socket.
353          */
354         *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0);
355         if (*cfg == NULL)
356                 rte_exit(EXIT_FAILURE, "Cannot allocate configuration %s\n",
357                         path);
358
359         n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
360                 sizeof(MRVL_TOK_PORT) - 1);
361
362         if (n == 0) {
363                 /* This is weird, but not bad. */
364                 RTE_LOG(WARNING, PMD, "Empty configuration file?\n");
365                 return 0;
366         }
367
368         /* Use the number of ports given as vdev parameters. */
369         for (n = 0; n < mrvl_ports_nb; ++n) {
370                 snprintf(sec_name, sizeof(sec_name), "%s %d %s",
371                         MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT);
372
373                 /* Skip ports non-existing in configuration. */
374                 if (rte_cfgfile_num_sections(file, sec_name,
375                                 strlen(sec_name)) <= 0) {
376                         (*cfg)->port[n].use_global_defaults = 1;
377                         (*cfg)->port[n].mapping_priority =
378                                 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
379                         continue;
380                 }
381
382                 entry = rte_cfgfile_get_entry(file, sec_name,
383                                 MRVL_TOK_DEFAULT_TC);
384                 if (entry) {
385                         if (get_val_securely(entry, &val) < 0 ||
386                                 val > USHRT_MAX)
387                                 return -1;
388                         (*cfg)->port[n].default_tc = (uint8_t)val;
389                 } else {
390                         RTE_LOG(ERR, PMD,
391                                 "Default Traffic Class required in custom configuration!\n");
392                         return -1;
393                 }
394
395                 entry = rte_cfgfile_get_entry(file, sec_name,
396                                 MRVL_TOK_MAPPING_PRIORITY);
397                 if (entry) {
398                         if (!strncmp(entry, MRVL_TOK_VLAN_IP,
399                                 sizeof(MRVL_TOK_VLAN_IP)))
400                                 (*cfg)->port[n].mapping_priority =
401                                         PP2_CLS_QOS_TBL_VLAN_IP_PRI;
402                         else if (!strncmp(entry, MRVL_TOK_IP_VLAN,
403                                 sizeof(MRVL_TOK_IP_VLAN)))
404                                 (*cfg)->port[n].mapping_priority =
405                                         PP2_CLS_QOS_TBL_IP_VLAN_PRI;
406                         else if (!strncmp(entry, MRVL_TOK_IP,
407                                 sizeof(MRVL_TOK_IP)))
408                                 (*cfg)->port[n].mapping_priority =
409                                         PP2_CLS_QOS_TBL_IP_PRI;
410                         else if (!strncmp(entry, MRVL_TOK_VLAN,
411                                 sizeof(MRVL_TOK_VLAN)))
412                                 (*cfg)->port[n].mapping_priority =
413                                         PP2_CLS_QOS_TBL_VLAN_PRI;
414                         else
415                                 rte_exit(EXIT_FAILURE,
416                                         "Error in parsing %s value (%s)!\n",
417                                         MRVL_TOK_MAPPING_PRIORITY, entry);
418                 } else {
419                         (*cfg)->port[n].mapping_priority =
420                                 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
421                 }
422
423                 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
424                         ret = get_outq_cfg(file, n, i, *cfg);
425                         if (ret < 0)
426                                 rte_exit(EXIT_FAILURE,
427                                         "Error %d parsing port %d outq %d!\n",
428                                         ret, n, i);
429                 }
430
431                 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
432                         ret = parse_tc_cfg(file, n, i, *cfg);
433                         if (ret < 0)
434                                 rte_exit(EXIT_FAILURE,
435                                         "Error %d parsing port %d tc %d!\n",
436                                         ret, n, i);
437                 }
438         }
439
440         return 0;
441 }
442
443 /**
444  * Setup Traffic Class.
445  *
446  * Fill in TC parameters in single MUSDK TC config entry.
447  * @param param TC parameters entry.
448  * @param inqs Number of MUSDK in-queues in this TC.
449  * @param bpool Bpool for this TC.
450  * @returns 0 in case of success, exits otherwise.
451  */
452 static int
453 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
454         struct pp2_bpool *bpool)
455 {
456         struct pp2_ppio_inq_params *inq_params;
457
458         param->pkt_offset = MRVL_PKT_OFFS;
459         param->pools[0] = bpool;
460
461         inq_params = rte_zmalloc_socket("inq_params",
462                 inqs * sizeof(*inq_params),
463                 0, rte_socket_id());
464         if (!inq_params)
465                 return -ENOMEM;
466
467         param->num_in_qs = inqs;
468
469         /* Release old config if necessary. */
470         if (param->inqs_params)
471                 rte_free(param->inqs_params);
472
473         param->inqs_params = inq_params;
474
475         return 0;
476 }
477
478 /**
479  * Configure RX Queues in a given port.
480  *
481  * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
482  *
483  * @param priv Port's private data
484  * @param portid DPDK port ID
485  * @param max_queues Maximum number of queues to configure.
486  * @returns 0 in case of success, negative value otherwise.
487  */
488 int
489 mrvl_configure_rxqs(struct mrvl_priv *priv, uint8_t portid,
490         uint16_t max_queues)
491 {
492         size_t i, tc;
493
494         if (mrvl_qos_cfg == NULL ||
495                 mrvl_qos_cfg->port[portid].use_global_defaults) {
496                 /* No port configuration, use default: 1 TC, no QoS. */
497                 priv->ppio_params.inqs_params.num_tcs = 1;
498                 setup_tc(&priv->ppio_params.inqs_params.tcs_params[0],
499                         max_queues, priv->bpool);
500
501                 /* Direct mapping of queues i.e. 0->0, 1->1 etc. */
502                 for (i = 0; i < max_queues; ++i) {
503                         priv->rxq_map[i].tc = 0;
504                         priv->rxq_map[i].inq = i;
505                 }
506                 return 0;
507         }
508
509         /* We need only a subset of configuration. */
510         struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid];
511
512         priv->qos_tbl_params.type = port_cfg->mapping_priority;
513
514         /*
515          * We need to reverse mapping, from tc->pcp (better from usability
516          * point of view) to pcp->tc (configurable in MUSDK).
517          * First, set all map elements to "default".
518          */
519         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
520                 priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc;
521
522         /* Then, fill in all known values. */
523         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
524                 if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) {
525                         /* Better safe than sorry. */
526                         RTE_LOG(ERR, PMD,
527                                 "Too many PCPs configured in TC %zu!\n", tc);
528                         return -1;
529                 }
530                 for (i = 0; i < port_cfg->tc[tc].pcps; ++i) {
531                         priv->qos_tbl_params.pcp_cos_map[
532                           port_cfg->tc[tc].pcp[i]].tc = tc;
533                 }
534         }
535
536         /*
537          * The same logic goes with DSCP.
538          * First, set all map elements to "default".
539          */
540         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
541                 priv->qos_tbl_params.dscp_cos_map[i].tc =
542                         port_cfg->default_tc;
543
544         /* Fill in all known values. */
545         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
546                 if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) {
547                         /* Better safe than sorry. */
548                         RTE_LOG(ERR, PMD,
549                                 "Too many DSCPs configured in TC %zu!\n", tc);
550                         return -1;
551                 }
552                 for (i = 0; i < port_cfg->tc[tc].dscps; ++i) {
553                         priv->qos_tbl_params.dscp_cos_map[
554                           port_cfg->tc[tc].dscp[i]].tc = tc;
555                 }
556         }
557
558         /*
559          * Surprisingly, similar logic goes with queue mapping.
560          * We need only to store qid->tc mapping,
561          * to know TC when queue is read.
562          */
563         for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
564                 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
565
566         /* Set up DPDKq->(TC,inq) mapping. */
567         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
568                 if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) {
569                         /* Overflow. */
570                         RTE_LOG(ERR, PMD,
571                                 "Too many RX queues configured per TC %zu!\n",
572                                 tc);
573                         return -1;
574                 }
575                 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
576                         uint8_t idx = port_cfg->tc[tc].inq[i];
577
578                         if (idx > RTE_DIM(priv->rxq_map)) {
579                                 RTE_LOG(ERR, PMD, "Bad queue index %d!\n", idx);
580                                 return -1;
581                         }
582
583                         priv->rxq_map[idx].tc = tc;
584                         priv->rxq_map[idx].inq = i;
585                 }
586         }
587
588         /*
589          * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
590          * with no gaps. Empty TC means end of processing.
591          */
592         for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
593                 if (port_cfg->tc[i].inqs == 0)
594                         break;
595                 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
596                                 port_cfg->tc[i].inqs,
597                                 priv->bpool);
598         }
599
600         priv->ppio_params.inqs_params.num_tcs = i;
601
602         return 0;
603 }
604
605 /**
606  * Start QoS mapping.
607  *
608  * Finalize QoS table configuration and initialize it in SDK. It can be done
609  * only after port is started, so we have a valid ppio reference.
610  *
611  * @param priv Port's private (configuration) data.
612  * @returns 0 in case of success, exits otherwise.
613  */
614 int
615 mrvl_start_qos_mapping(struct mrvl_priv *priv)
616 {
617         size_t i;
618
619         if (priv->ppio == NULL) {
620                 RTE_LOG(ERR, PMD, "ppio must not be NULL here!\n");
621                 return -1;
622         }
623
624         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
625                 priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio;
626
627         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
628                 priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio;
629
630         /* Initialize Classifier QoS table. */
631
632         return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);
633 }