1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2017 Marvell International Ltd.
3 * Copyright(c) 2017 Semihalf.
4 * All rights reserved.
5 */
6
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include <rte_common.h>
12 #include <rte_cfgfile.h>
13 #include <rte_log.h>
14 #include <rte_lcore.h>
15 #include <rte_malloc.h>
16 #include <rte_string_fns.h>
17
18 #include "mrvl_qos.h"
19
20 /* Parsing tokens. Defined conveniently, so that any correction is easy. */
21 #define MRVL_TOK_DEFAULT "default"
22 #define MRVL_TOK_DSA_MODE "dsa_mode"
23 #define MRVL_TOK_START_HDR "start_hdr"
24 #define MRVL_TOK_START_HDR_NONE "none"
25 #define MRVL_TOK_START_HDR_DSA "dsa"
26 #define MRVL_TOK_START_HDR_CUSTOM "custom"
27 #define MRVL_TOK_START_HDR_EXT_DSA "ext_dsa"
28 #define MRVL_TOK_DEFAULT_TC "default_tc"
29 #define MRVL_TOK_DSCP "dscp"
30 #define MRVL_TOK_MAPPING_PRIORITY "mapping_priority"
31 #define MRVL_TOK_IP "ip"
32 #define MRVL_TOK_IP_VLAN "ip/vlan"
33 #define MRVL_TOK_PCP "pcp"
34 #define MRVL_TOK_PORT "port"
35 #define MRVL_TOK_RXQ "rxq"
36 #define MRVL_TOK_TC "tc"
37 #define MRVL_TOK_TXQ "txq"
38 #define MRVL_TOK_VLAN "vlan"
39 #define MRVL_TOK_VLAN_IP "vlan/ip"
40 #define MRVL_TOK_PARSER_UDF "parser udf"
41
42 /* egress specific configuration tokens */
43 #define MRVL_TOK_BURST_SIZE "burst_size"
44 #define MRVL_TOK_RATE_LIMIT "rate_limit"
45 #define MRVL_TOK_RATE_LIMIT_ENABLE "rate_limit_enable"
46 #define MRVL_TOK_SCHED_MODE "sched_mode"
47 #define MRVL_TOK_SCHED_MODE_SP "sp"
48 #define MRVL_TOK_SCHED_MODE_WRR "wrr"
49 #define MRVL_TOK_WRR_WEIGHT "wrr_weight"
50
51 /* policer specific configuration tokens */
52 #define MRVL_TOK_PLCR "policer"
53 #define MRVL_TOK_PLCR_DEFAULT "default_policer"
54 #define MRVL_TOK_PLCR_UNIT "token_unit"
55 #define MRVL_TOK_PLCR_UNIT_BYTES "bytes"
56 #define MRVL_TOK_PLCR_UNIT_PACKETS "packets"
57 #define MRVL_TOK_PLCR_COLOR "color_mode"
58 #define MRVL_TOK_PLCR_COLOR_BLIND "blind"
59 #define MRVL_TOK_PLCR_COLOR_AWARE "aware"
60 #define MRVL_TOK_PLCR_CIR "cir"
61 #define MRVL_TOK_PLCR_CBS "cbs"
62 #define MRVL_TOK_PLCR_EBS "ebs"
63 #define MRVL_TOK_PLCR_DEFAULT_COLOR "default_color"
64 #define MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN "green"
65 #define MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW "yellow"
66 #define MRVL_TOK_PLCR_DEFAULT_COLOR_RED "red"
67
68 /* parser udf specific configuration tokens */
69 #define MRVL_TOK_PARSER_UDF_PROTO "proto"
70 #define MRVL_TOK_PARSER_UDF_FIELD "field"
71 #define MRVL_TOK_PARSER_UDF_KEY "key"
72 #define MRVL_TOK_PARSER_UDF_MASK "mask"
73 #define MRVL_TOK_PARSER_UDF_OFFSET "offset"
74 #define MRVL_TOK_PARSER_UDF_PROTO_ETH "eth"
75 #define MRVL_TOK_PARSER_UDF_FIELD_ETH_TYPE "type"
76 #define MRVL_TOK_PARSER_UDF_PROTO_UDP "udp"
77 #define MRVL_TOK_PARSER_UDF_FIELD_UDP_DPORT "dport"
78
79 /* parser forward bad frames tokens */
80 #define MRVL_TOK_FWD_BAD_FRAMES "forward_bad_frames"
81
82 /* parse fill bpool buffers tokens */
83 #define MRVL_TOK_FILL_BPOOL_BUFFS "fill_bpool_buffs"
84
85 /** Number of tokens in range a-b = 2. */
86 #define MAX_RNG_TOKENS 2
87
88 /** Maximum possible value of PCP. */
89 #define MAX_PCP 7
90
91 /** Maximum possible value of DSCP. */
92 #define MAX_DSCP 63
93
94 /** Global configuration. */
95 struct mrvl_cfg *mrvl_cfg;
96
97 /**
98 * Read out-queue configuration from file.
99 *
100 * @param file Path to the configuration file.
101 * @param port Port number.
102 * @param outq Out queue number.
103 * @param cfg Pointer to the Marvell configuration structure.
104 * @returns 0 in case of success, negative value otherwise.
105 */
106 static int
get_outq_cfg(struct rte_cfgfile * file,int port,int outq,struct mrvl_cfg * cfg)107 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
108 struct mrvl_cfg *cfg)
109 {
110 char sec_name[32];
111 const char *entry;
112 uint32_t val;
113
114 snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
115 MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
116
117 /* Skip non-existing */
118 if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
119 return 0;
120
121 /* Read scheduling mode */
122 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_SCHED_MODE);
123 if (entry) {
124 if (!strncmp(entry, MRVL_TOK_SCHED_MODE_SP,
125 strlen(MRVL_TOK_SCHED_MODE_SP))) {
126 cfg->port[port].outq[outq].sched_mode =
127 PP2_PPIO_SCHED_M_SP;
128 } else if (!strncmp(entry, MRVL_TOK_SCHED_MODE_WRR,
129 strlen(MRVL_TOK_SCHED_MODE_WRR))) {
130 cfg->port[port].outq[outq].sched_mode =
131 PP2_PPIO_SCHED_M_WRR;
132 } else {
133 MRVL_LOG(ERR, "Unknown token: %s", entry);
134 return -1;
135 }
136 }
137
138 /* Read wrr weight */
139 if (cfg->port[port].outq[outq].sched_mode == PP2_PPIO_SCHED_M_WRR) {
140 entry = rte_cfgfile_get_entry(file, sec_name,
141 MRVL_TOK_WRR_WEIGHT);
142 if (entry) {
143 if (get_val_securely(entry, &val) < 0)
144 return -1;
145 cfg->port[port].outq[outq].weight = val;
146 }
147 }
148
149 /*
150 * There's no point in setting rate limiting for specific outq as
151 * global port rate limiting has priority.
152 */
153 if (cfg->port[port].rate_limit_enable) {
154 MRVL_LOG(WARNING, "Port %d rate limiting already enabled",
155 port);
156 return 0;
157 }
158
159 entry = rte_cfgfile_get_entry(file, sec_name,
160 MRVL_TOK_RATE_LIMIT_ENABLE);
161 if (entry) {
162 if (get_val_securely(entry, &val) < 0)
163 return -1;
164 cfg->port[port].outq[outq].rate_limit_enable = val;
165 }
166
167 if (!cfg->port[port].outq[outq].rate_limit_enable)
168 return 0;
169
170 /* Read CBS (in kB) */
171 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_BURST_SIZE);
172 if (entry) {
173 if (get_val_securely(entry, &val) < 0)
174 return -1;
175 cfg->port[port].outq[outq].rate_limit_params.cbs = val;
176 }
177
178 /* Read CIR (in kbps) */
179 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RATE_LIMIT);
180 if (entry) {
181 if (get_val_securely(entry, &val) < 0)
182 return -1;
183 cfg->port[port].outq[outq].rate_limit_params.cir = val;
184 }
185
186 return 0;
187 }
188
189 /**
190 * Gets multiple-entry values and places them in table.
191 *
192 * Entry can be anything, e.g. "1 2-3 5 6 7-9". This needs to be converted to
193 * table entries, respectively: {1, 2, 3, 5, 6, 7, 8, 9}.
194 * As all result table's elements are always 1-byte long, we
195 * won't overcomplicate the function, but we'll keep API generic,
196 * check if someone hasn't changed element size and make it simple
197 * to extend to other sizes.
198 *
199 * This function is purely utilitary, it does not print any error, only returns
200 * different error numbers.
201 *
202 * @param entry[in] Values string to parse.
203 * @param tab[out] Results table.
204 * @param elem_sz[in] Element size (in bytes).
205 * @param max_elems[in] Number of results table elements available.
206 * @param max val[in] Maximum value allowed.
207 * @returns Number of correctly parsed elements in case of success.
208 * @retval -1 Wrong element size.
209 * @retval -2 More tokens than result table allows.
210 * @retval -3 Wrong range syntax.
211 * @retval -4 Wrong range values.
212 * @retval -5 Maximum value exceeded.
213 */
214 static int
get_entry_values(const char * entry,uint8_t * tab,size_t elem_sz,uint8_t max_elems,uint8_t max_val)215 get_entry_values(const char *entry, uint8_t *tab,
216 size_t elem_sz, uint8_t max_elems, uint8_t max_val)
217 {
218 /* There should not be more tokens than max elements.
219 * Add 1 for error trap.
220 */
221 char *tokens[max_elems + 1];
222
223 /* Begin, End + error trap = 3. */
224 char *rng_tokens[MAX_RNG_TOKENS + 1];
225 long beg, end;
226 uint32_t token_val;
227 int nb_tokens, nb_rng_tokens;
228 int i;
229 int values = 0;
230 char val;
231 char entry_cpy[CFG_VALUE_LEN];
232
233 if (elem_sz != 1)
234 return -1;
235
236 /* Copy the entry to safely use rte_strsplit(). */
237 strlcpy(entry_cpy, entry, RTE_DIM(entry_cpy));
238
239 /*
240 * If there are more tokens than array size, rte_strsplit will
241 * not return error, just array size.
242 */
243 nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
244 tokens, max_elems + 1, ' ');
245
246 /* Quick check, will be refined later. */
247 if (nb_tokens > max_elems)
248 return -2;
249
250 for (i = 0; i < nb_tokens; ++i) {
251 if (strchr(tokens[i], '-') != NULL) {
252 /*
253 * Split to begin and end tokens.
254 * We want to catch error cases too, thus we leave
255 * option for number of tokens to be more than 2.
256 */
257 nb_rng_tokens = rte_strsplit(tokens[i],
258 strlen(tokens[i]), rng_tokens,
259 RTE_DIM(rng_tokens), '-');
260 if (nb_rng_tokens != 2)
261 return -3;
262
263 /* Range and sanity checks. */
264 if (get_val_securely(rng_tokens[0], &token_val) < 0)
265 return -4;
266 beg = (char)token_val;
267 if (get_val_securely(rng_tokens[1], &token_val) < 0)
268 return -4;
269 end = (char)token_val;
270 if (beg < 0 || beg > UCHAR_MAX ||
271 end < 0 || end > UCHAR_MAX || end < beg)
272 return -4;
273
274 for (val = beg; val <= end; ++val) {
275 if (val > max_val)
276 return -5;
277
278 *tab = val;
279 tab = RTE_PTR_ADD(tab, elem_sz);
280 ++values;
281 if (values >= max_elems)
282 return -2;
283 }
284 } else {
285 /* Single values. */
286 if (get_val_securely(tokens[i], &token_val) < 0)
287 return -5;
288 val = (char)token_val;
289 if (val > max_val)
290 return -5;
291
292 *tab = val;
293 tab = RTE_PTR_ADD(tab, elem_sz);
294 ++values;
295 if (values >= max_elems)
296 return -2;
297 }
298 }
299
300 return values;
301 }
302
303 /**
304 * Parse Traffic Classes mapping configuration.
305 *
306 * @param file Config file handle.
307 * @param port Which port to look for.
308 * @param tc Which Traffic Class to look for.
309 * @param cfg[out] Parsing results.
310 * @returns 0 in case of success, negative value otherwise.
311 */
312 static int
parse_tc_cfg(struct rte_cfgfile * file,int port,int tc,struct mrvl_cfg * cfg)313 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
314 struct mrvl_cfg *cfg)
315 {
316 char sec_name[32];
317 const char *entry;
318 int n;
319
320 snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
321 MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
322
323 /* Skip non-existing */
324 if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
325 return 0;
326
327 cfg->port[port].use_qos_global_defaults = 0;
328 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
329 if (entry) {
330 n = get_entry_values(entry,
331 cfg->port[port].tc[tc].inq,
332 sizeof(cfg->port[port].tc[tc].inq[0]),
333 RTE_DIM(cfg->port[port].tc[tc].inq),
334 MRVL_PP2_RXQ_MAX);
335 if (n < 0) {
336 MRVL_LOG(ERR, "Error %d while parsing: %s",
337 n, entry);
338 return n;
339 }
340 cfg->port[port].tc[tc].inqs = n;
341 }
342
343 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
344 if (entry) {
345 n = get_entry_values(entry,
346 cfg->port[port].tc[tc].pcp,
347 sizeof(cfg->port[port].tc[tc].pcp[0]),
348 RTE_DIM(cfg->port[port].tc[tc].pcp),
349 MAX_PCP);
350 if (n < 0) {
351 MRVL_LOG(ERR, "Error %d while parsing: %s",
352 n, entry);
353 return n;
354 }
355 cfg->port[port].tc[tc].pcps = n;
356 }
357
358 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
359 if (entry) {
360 n = get_entry_values(entry,
361 cfg->port[port].tc[tc].dscp,
362 sizeof(cfg->port[port].tc[tc].dscp[0]),
363 RTE_DIM(cfg->port[port].tc[tc].dscp),
364 MAX_DSCP);
365 if (n < 0) {
366 MRVL_LOG(ERR, "Error %d while parsing: %s",
367 n, entry);
368 return n;
369 }
370 cfg->port[port].tc[tc].dscps = n;
371 }
372
373 if (!cfg->port[port].setup_policer)
374 return 0;
375
376 entry = rte_cfgfile_get_entry(file, sec_name,
377 MRVL_TOK_PLCR_DEFAULT_COLOR);
378 if (entry) {
379 if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN,
380 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN))) {
381 cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_GREEN;
382 } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW,
383 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW))) {
384 cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_YELLOW;
385 } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_RED,
386 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_RED))) {
387 cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_RED;
388 } else {
389 MRVL_LOG(ERR, "Error while parsing: %s", entry);
390 return -1;
391 }
392 }
393
394 return 0;
395 }
396
397 /**
398 * Parse default port policer.
399 *
400 * @param file Config file handle.
401 * @param sec_name Section name with policer configuration
402 * @param port Port number.
403 * @param cfg[out] Parsing results.
404 * @returns 0 in case of success, negative value otherwise.
405 */
406 static int
parse_policer(struct rte_cfgfile * file,int port,const char * sec_name,struct mrvl_cfg * cfg)407 parse_policer(struct rte_cfgfile *file, int port, const char *sec_name,
408 struct mrvl_cfg *cfg)
409 {
410 const char *entry;
411 uint32_t val;
412
413 /* Read policer token unit */
414 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_UNIT);
415 if (entry) {
416 if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_BYTES,
417 sizeof(MRVL_TOK_PLCR_UNIT_BYTES))) {
418 cfg->port[port].policer_params.token_unit =
419 PP2_CLS_PLCR_BYTES_TOKEN_UNIT;
420 } else if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_PACKETS,
421 sizeof(MRVL_TOK_PLCR_UNIT_PACKETS))) {
422 cfg->port[port].policer_params.token_unit =
423 PP2_CLS_PLCR_PACKETS_TOKEN_UNIT;
424 } else {
425 MRVL_LOG(ERR, "Unknown token: %s", entry);
426 return -1;
427 }
428 }
429
430 /* Read policer color mode */
431 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_COLOR);
432 if (entry) {
433 if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_BLIND,
434 sizeof(MRVL_TOK_PLCR_COLOR_BLIND))) {
435 cfg->port[port].policer_params.color_mode =
436 PP2_CLS_PLCR_COLOR_BLIND_MODE;
437 } else if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_AWARE,
438 sizeof(MRVL_TOK_PLCR_COLOR_AWARE))) {
439 cfg->port[port].policer_params.color_mode =
440 PP2_CLS_PLCR_COLOR_AWARE_MODE;
441 } else {
442 MRVL_LOG(ERR, "Error in parsing: %s", entry);
443 return -1;
444 }
445 }
446
447 /* Read policer cir */
448 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CIR);
449 if (entry) {
450 if (get_val_securely(entry, &val) < 0)
451 return -1;
452 cfg->port[port].policer_params.cir = val;
453 }
454
455 /* Read policer cbs */
456 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CBS);
457 if (entry) {
458 if (get_val_securely(entry, &val) < 0)
459 return -1;
460 cfg->port[port].policer_params.cbs = val;
461 }
462
463 /* Read policer ebs */
464 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_EBS);
465 if (entry) {
466 if (get_val_securely(entry, &val) < 0)
467 return -1;
468 cfg->port[port].policer_params.ebs = val;
469 }
470
471 cfg->port[port].setup_policer = 1;
472
473 return 0;
474 }
475
476 /**
477 * Parse parser udf.
478 *
479 * @param file Config file handle.
480 * @param sec_name section name
481 * @param udf udf index
482 * @param cfg[out] Parsing results.
483 * @returns 0 in case of success, negative value otherwise.
484 */
485 static int
parse_udf(struct rte_cfgfile * file,const char * sec_name,int udf,struct mrvl_cfg * cfg)486 parse_udf(struct rte_cfgfile *file, const char *sec_name, int udf,
487 struct mrvl_cfg *cfg)
488 {
489 struct pp2_parse_udf_params *udf_params;
490 const char *entry, *entry_field;
491 uint32_t val, i;
492 uint8_t field_size;
493 char malloc_name[32], tmp_arr[3];
494 /* field len in chars equal to '0x' + rest of data */
495 #define FIELD_LEN_IN_CHARS(field_size) (uint32_t)(2 + (field_size) * 2)
496
497 udf_params = &cfg->pp2_cfg.prs_udfs.udfs[udf];
498
499 /* Read 'proto' field */
500 entry = rte_cfgfile_get_entry(file, sec_name,
501 MRVL_TOK_PARSER_UDF_PROTO);
502 if (!entry) {
503 MRVL_LOG(ERR, "UDF[%d]: '%s' field must be set\n", udf,
504 MRVL_TOK_PARSER_UDF_PROTO);
505 return -1;
506 }
507
508 /* Read 'field' field */
509 entry_field = rte_cfgfile_get_entry(file, sec_name,
510 MRVL_TOK_PARSER_UDF_FIELD);
511 if (!entry_field) {
512 MRVL_LOG(ERR, "UDF[%d]: '%s' field must be set\n", udf,
513 MRVL_TOK_PARSER_UDF_FIELD);
514 return -1;
515 }
516
517 if (!strncmp(entry, MRVL_TOK_PARSER_UDF_PROTO_ETH,
518 sizeof(MRVL_TOK_PARSER_UDF_PROTO_ETH))) {
519 udf_params->match_proto = MV_NET_PROTO_ETH;
520 if (!strncmp(entry_field, MRVL_TOK_PARSER_UDF_FIELD_ETH_TYPE,
521 sizeof(MRVL_TOK_PARSER_UDF_FIELD_ETH_TYPE))) {
522 udf_params->match_field.eth = MV_NET_ETH_F_TYPE;
523 field_size = 2;
524 } else {
525 MRVL_LOG(ERR, "UDF[%d]: mismatch between '%s' proto "
526 "and '%s' field\n", udf,
527 MRVL_TOK_PARSER_UDF_PROTO_ETH,
528 entry_field);
529 return -1;
530 }
531 } else if (!strncmp(entry, MRVL_TOK_PARSER_UDF_PROTO_UDP,
532 sizeof(MRVL_TOK_PARSER_UDF_PROTO_UDP))) {
533 udf_params->match_proto = MV_NET_PROTO_UDP;
534 if (!strncmp(entry_field, MRVL_TOK_PARSER_UDF_FIELD_UDP_DPORT,
535 sizeof(MRVL_TOK_PARSER_UDF_FIELD_UDP_DPORT))) {
536 udf_params->match_field.udp = MV_NET_UDP_F_DP;
537 field_size = 2;
538 } else {
539 MRVL_LOG(ERR, "UDF[%d]: mismatch between '%s' proto "
540 "and '%s' field\n", udf,
541 MRVL_TOK_PARSER_UDF_PROTO_UDP,
542 entry_field);
543 return -1;
544 }
545 } else {
546 MRVL_LOG(ERR, "UDF[%d]: Unsupported '%s' proto\n", udf, entry);
547 return -1;
548 }
549
550 snprintf(malloc_name, sizeof(malloc_name), "mrvl_udf_%d_key", udf);
551 udf_params->match_key = rte_zmalloc(malloc_name, field_size, 0);
552 if (udf_params->match_key == NULL) {
553 MRVL_LOG(ERR, "Cannot allocate udf %d key\n", udf);
554 return -1;
555 }
556 snprintf(malloc_name, sizeof(malloc_name), "mrvl_udf_%d_mask", udf);
557 udf_params->match_mask = rte_zmalloc(malloc_name, field_size, 0);
558 if (udf_params->match_mask == NULL) {
559 MRVL_LOG(ERR, "Cannot allocate udf %d mask\n", udf);
560 return -1;
561 }
562
563 /* Read 'key' field */
564 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PARSER_UDF_KEY);
565 if (!entry) {
566 MRVL_LOG(ERR, "UDF[%d]: '%s' field must be set\n", udf,
567 MRVL_TOK_PARSER_UDF_KEY);
568 return -1;
569 }
570
571 if (strncmp(entry, "0x", 2) != 0) {
572 MRVL_LOG(ERR, "UDF[%d]: '%s' field must start with '0x'\n",
573 udf, MRVL_TOK_PARSER_UDF_KEY);
574 return -EINVAL;
575 }
576
577 if (strlen(entry) != FIELD_LEN_IN_CHARS(field_size)) {
578 MRVL_LOG(ERR, "UDF[%d]: '%s' field's len must be %d\n", udf,
579 MRVL_TOK_PARSER_UDF_KEY,
580 FIELD_LEN_IN_CHARS(field_size));
581 return -EINVAL;
582 }
583
584 entry += 2; /* skip the '0x' */
585 for (i = 0; i < field_size; i++) {
586 strncpy(tmp_arr, entry, 2);
587 tmp_arr[2] = '\0';
588 if (get_val_securely8(tmp_arr, 16,
589 &udf_params->match_key[i]) < 0) {
590 MRVL_LOG(ERR, "UDF[%d]: '%s' field's value is not in "
591 "hex format\n", udf, MRVL_TOK_PARSER_UDF_KEY);
592 return -EINVAL;
593 }
594 entry += 2;
595 }
596
597 /* Read 'mask' field */
598 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PARSER_UDF_MASK);
599 if (!entry) {
600 MRVL_LOG(ERR, "UDF[%d]: '%s' field must be set\n", udf,
601 MRVL_TOK_PARSER_UDF_MASK);
602 return -1;
603 }
604 if (strncmp(entry, "0x", 2) != 0) {
605 MRVL_LOG(ERR, "UDF[%d]: '%s' field must start with '0x'\n",
606 udf, MRVL_TOK_PARSER_UDF_MASK);
607 return -EINVAL;
608 }
609
610 if (strlen(entry) != FIELD_LEN_IN_CHARS(field_size)) {
611 MRVL_LOG(ERR, "UDF[%d]: '%s' field's len must be %d\n", udf,
612 MRVL_TOK_PARSER_UDF_MASK,
613 FIELD_LEN_IN_CHARS(field_size));
614 return -EINVAL;
615 }
616
617 entry += 2; /* skip the '0x' */
618 for (i = 0; i < field_size; i++) {
619 strncpy(tmp_arr, entry, 2);
620 tmp_arr[2] = '\0';
621 if (get_val_securely8(tmp_arr, 16,
622 &udf_params->match_mask[i]) < 0) {
623 MRVL_LOG(ERR, "UDF[%d]: '%s' field's value is not in "
624 "hex format\n", udf, MRVL_TOK_PARSER_UDF_MASK);
625 return -EINVAL;
626 }
627 entry += 2;
628 }
629
630 /* Read offset */
631 entry = rte_cfgfile_get_entry(file, sec_name,
632 MRVL_TOK_PARSER_UDF_OFFSET);
633 if (!entry) {
634 MRVL_LOG(ERR, "UDF[%d]: '%s' field must be set\n", udf,
635 MRVL_TOK_PARSER_UDF_OFFSET);
636 return -1;
637 }
638 if (get_val_securely(entry, &val) < 0)
639 return -1;
640 udf_params->offset = val;
641
642 return 0;
643 }
644
645 /**
646 * Parse configuration - rte_kvargs_process handler.
647 *
648 * Opens configuration file and parses its content.
649 *
650 * @param key Unused.
651 * @param path Path to config file.
652 * @param extra_args Pointer to configuration structure.
653 * @returns 0 in case of success, exits otherwise.
654 */
655 int
mrvl_get_cfg(const char * key __rte_unused,const char * path,void * extra_args)656 mrvl_get_cfg(const char *key __rte_unused, const char *path, void *extra_args)
657 {
658 struct mrvl_cfg **cfg = extra_args;
659 struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
660 uint32_t val;
661 int n, i, ret;
662 const char *entry;
663 char sec_name[32];
664
665 if (file == NULL) {
666 MRVL_LOG(ERR, "Cannot load configuration %s\n", path);
667 return -1;
668 }
669
670 /* Create configuration. This is never accessed on the fast path,
671 * so we can ignore socket.
672 */
673 *cfg = rte_zmalloc("mrvl_cfg", sizeof(struct mrvl_cfg), 0);
674 if (*cfg == NULL) {
675 MRVL_LOG(ERR, "Cannot allocate configuration %s\n", path);
676 return -1;
677 }
678
679 /* PP2 configuration */
680 n = rte_cfgfile_num_sections(file, MRVL_TOK_PARSER_UDF,
681 sizeof(MRVL_TOK_PARSER_UDF) - 1);
682
683 if (n && n > PP2_MAX_UDFS_SUPPORTED) {
684 MRVL_LOG(ERR, "found %d udf sections, but only %d are supported\n",
685 n, PP2_MAX_UDFS_SUPPORTED);
686 return -1;
687 }
688 (*cfg)->pp2_cfg.prs_udfs.num_udfs = n;
689 for (i = 0; i < n; i++) {
690 snprintf(sec_name, sizeof(sec_name), "%s %d",
691 MRVL_TOK_PARSER_UDF, i);
692
693 /* udf sections must be sequential. */
694 if (rte_cfgfile_num_sections(file, sec_name,
695 strlen(sec_name)) <= 0) {
696 MRVL_LOG(ERR, "udf sections must be sequential (0 - %d)\n",
697 PP2_MAX_UDFS_SUPPORTED - 1);
698 return -1;
699 }
700
701 ret = parse_udf(file, sec_name, i, *cfg);
702 if (ret) {
703 MRVL_LOG(ERR, "Error in parsing %s!\n", sec_name);
704 return -1;
705 }
706 }
707
708 /* PP2 Ports configuration */
709 n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
710 sizeof(MRVL_TOK_PORT) - 1);
711
712 if (n == 0) {
713 /* This is weird, but not bad. */
714 MRVL_LOG(WARNING, "Empty configuration file?");
715 return 0;
716 }
717
718 /* Use the number of ports given as vdev parameters. */
719 for (n = 0; n < (PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC); ++n) {
720 snprintf(sec_name, sizeof(sec_name), "%s %d %s",
721 MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT);
722
723 /* Use global defaults, unless an override occurs */
724 (*cfg)->port[n].use_qos_global_defaults = 1;
725
726 /* Set non-zero defaults before the decision to continue to next
727 * port or to parse the port section in config file
728 */
729 (*cfg)->port[n].fill_bpool_buffs = MRVL_BURST_SIZE;
730
731 /* Skip ports non-existing in configuration. */
732 if (rte_cfgfile_num_sections(file, sec_name,
733 strlen(sec_name)) <= 0) {
734 continue;
735 }
736
737 /* MRVL_TOK_START_HDR replaces MRVL_TOK_DSA_MODE parameter.
738 * MRVL_TOK_DSA_MODE will be supported for backward
739 * compatibility.
740 */
741 entry = rte_cfgfile_get_entry(file, sec_name,
742 MRVL_TOK_START_HDR);
743 /* if start_hsr is missing, check if dsa_mode exist instead */
744 if (entry == NULL)
745 entry = rte_cfgfile_get_entry(file, sec_name,
746 MRVL_TOK_DSA_MODE);
747 if (entry) {
748 if (!strncmp(entry, MRVL_TOK_START_HDR_NONE,
749 sizeof(MRVL_TOK_START_HDR_NONE)))
750 (*cfg)->port[n].eth_start_hdr =
751 PP2_PPIO_HDR_ETH;
752 else if (!strncmp(entry, MRVL_TOK_START_HDR_DSA,
753 sizeof(MRVL_TOK_START_HDR_DSA)))
754 (*cfg)->port[n].eth_start_hdr =
755 PP2_PPIO_HDR_ETH_DSA;
756 else if (!strncmp(entry, MRVL_TOK_START_HDR_CUSTOM,
757 sizeof(MRVL_TOK_START_HDR_CUSTOM)))
758 (*cfg)->port[n].eth_start_hdr =
759 PP2_PPIO_HDR_ETH_CUSTOM;
760 else if (!strncmp(entry, MRVL_TOK_START_HDR_EXT_DSA,
761 sizeof(MRVL_TOK_START_HDR_EXT_DSA))) {
762 (*cfg)->port[n].eth_start_hdr =
763 PP2_PPIO_HDR_ETH_EXT_DSA;
764 } else {
765 MRVL_LOG(ERR,
766 "Error in parsing %s value (%s)!\n",
767 MRVL_TOK_START_HDR, entry);
768 return -1;
769 }
770 } else {
771 (*cfg)->port[n].eth_start_hdr = PP2_PPIO_HDR_ETH;
772 }
773
774 /*
775 * Read per-port rate limiting. Setting that will
776 * disable per-queue rate limiting.
777 */
778 entry = rte_cfgfile_get_entry(file, sec_name,
779 MRVL_TOK_RATE_LIMIT_ENABLE);
780 if (entry) {
781 if (get_val_securely(entry, &val) < 0)
782 return -1;
783 (*cfg)->port[n].rate_limit_enable = val;
784 }
785
786 if ((*cfg)->port[n].rate_limit_enable) {
787 entry = rte_cfgfile_get_entry(file, sec_name,
788 MRVL_TOK_BURST_SIZE);
789 if (entry) {
790 if (get_val_securely(entry, &val) < 0)
791 return -1;
792 (*cfg)->port[n].rate_limit_params.cbs = val;
793 }
794
795 entry = rte_cfgfile_get_entry(file, sec_name,
796 MRVL_TOK_RATE_LIMIT);
797 if (entry) {
798 if (get_val_securely(entry, &val) < 0)
799 return -1;
800 (*cfg)->port[n].rate_limit_params.cir = val;
801 }
802 }
803
804 entry = rte_cfgfile_get_entry(file, sec_name,
805 MRVL_TOK_MAPPING_PRIORITY);
806 if (entry) {
807 (*cfg)->port[n].use_qos_global_defaults = 0;
808 if (!strncmp(entry, MRVL_TOK_VLAN_IP,
809 sizeof(MRVL_TOK_VLAN_IP)))
810 (*cfg)->port[n].mapping_priority =
811 PP2_CLS_QOS_TBL_VLAN_IP_PRI;
812 else if (!strncmp(entry, MRVL_TOK_IP_VLAN,
813 sizeof(MRVL_TOK_IP_VLAN)))
814 (*cfg)->port[n].mapping_priority =
815 PP2_CLS_QOS_TBL_IP_VLAN_PRI;
816 else if (!strncmp(entry, MRVL_TOK_IP,
817 sizeof(MRVL_TOK_IP)))
818 (*cfg)->port[n].mapping_priority =
819 PP2_CLS_QOS_TBL_IP_PRI;
820 else if (!strncmp(entry, MRVL_TOK_VLAN,
821 sizeof(MRVL_TOK_VLAN))) {
822 (*cfg)->port[n].mapping_priority =
823 PP2_CLS_QOS_TBL_VLAN_PRI;
824 } else {
825 MRVL_LOG(ERR,
826 "Error in parsing %s value (%s)!\n",
827 MRVL_TOK_MAPPING_PRIORITY, entry);
828 return -1;
829 }
830 } else {
831 (*cfg)->port[n].mapping_priority =
832 PP2_CLS_QOS_TBL_NONE;
833 }
834
835 /* Parse policer configuration (if any) */
836 entry = rte_cfgfile_get_entry(file, sec_name,
837 MRVL_TOK_PLCR_DEFAULT);
838 if (entry) {
839 (*cfg)->port[n].use_qos_global_defaults = 0;
840 if (get_val_securely(entry, &val) < 0)
841 return -1;
842
843 snprintf(sec_name, sizeof(sec_name), "%s %d",
844 MRVL_TOK_PLCR, val);
845 ret = parse_policer(file, n, sec_name, *cfg);
846 if (ret)
847 return -1;
848 }
849
850 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
851 ret = get_outq_cfg(file, n, i, *cfg);
852 if (ret < 0) {
853 MRVL_LOG(ERR,
854 "Error %d parsing port %d outq %d!\n",
855 ret, n, i);
856 return -1;
857 }
858 }
859
860 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
861 ret = parse_tc_cfg(file, n, i, *cfg);
862 if (ret < 0) {
863 MRVL_LOG(ERR,
864 "Error %d parsing port %d tc %d!\n",
865 ret, n, i);
866 return -1;
867 }
868 }
869
870 entry = rte_cfgfile_get_entry(file, sec_name,
871 MRVL_TOK_DEFAULT_TC);
872 if (entry) {
873 if (get_val_securely(entry, &val) < 0 ||
874 val > USHRT_MAX)
875 return -1;
876 (*cfg)->port[n].default_tc = (uint8_t)val;
877 } else {
878 if ((*cfg)->port[n].use_qos_global_defaults == 0) {
879 MRVL_LOG(ERR,
880 "Default Traffic Class required in "
881 "custom configuration!");
882 return -1;
883 }
884 }
885
886 /* Parse forward bad frames option */
887 entry = rte_cfgfile_get_entry(file, sec_name,
888 MRVL_TOK_FWD_BAD_FRAMES);
889 if (entry) {
890 if (get_val_securely(entry, &val) < 0) {
891 MRVL_LOG(ERR,
892 "Error in parsing %s value (%s)!\n",
893 MRVL_TOK_FWD_BAD_FRAMES, entry);
894 return -1;
895 }
896 (*cfg)->port[n].forward_bad_frames = (uint8_t)val;
897 } else {
898 (*cfg)->port[n].forward_bad_frames = 0;
899 }
900
901 /* Parse fill bpool buffs option */
902 entry = rte_cfgfile_get_entry(file, sec_name,
903 MRVL_TOK_FILL_BPOOL_BUFFS);
904 if (entry) {
905 if (get_val_securely(entry, &val) < 0) {
906 MRVL_LOG(ERR,
907 "Error in parsing %s value (%s)!\n",
908 MRVL_TOK_FILL_BPOOL_BUFFS, entry);
909 return -1;
910 }
911 (*cfg)->port[n].fill_bpool_buffs = val;
912 }
913 }
914
915 return 0;
916 }
917
918 /**
919 * Setup Traffic Class.
920 *
921 * Fill in TC parameters in single MUSDK TC config entry.
922 * @param param TC parameters entry.
923 * @param inqs Number of MUSDK in-queues in this TC.
924 * @param bpool Bpool for this TC.
925 * @param color Default color for this TC.
926 * @returns 0 in case of success, exits otherwise.
927 */
928 static int
setup_tc(struct pp2_ppio_tc_params * param,uint8_t inqs,struct pp2_bpool * bpool,enum pp2_ppio_color color)929 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
930 struct pp2_bpool *bpool, enum pp2_ppio_color color)
931 {
932 struct pp2_ppio_inq_params *inq_params;
933
934 param->pkt_offset = MRVL_PKT_OFFS;
935 param->pools[0][0] = bpool;
936 param->pools[0][1] = dummy_pool[bpool->pp2_id];
937 param->default_color = color;
938
939 inq_params = rte_zmalloc_socket("inq_params",
940 inqs * sizeof(*inq_params),
941 0, rte_socket_id());
942 if (!inq_params)
943 return -ENOMEM;
944
945 param->num_in_qs = inqs;
946
947 /* Release old config if necessary. */
948 rte_free(param->inqs_params);
949
950 param->inqs_params = inq_params;
951
952 return 0;
953 }
954
955 /**
956 * Setup ingress policer.
957 *
958 * @param priv Port's private data.
959 * @param params Pointer to the policer's configuration.
960 * @param plcr_id Policer id.
961 * @returns 0 in case of success, negative values otherwise.
962 */
963 static int
setup_policer(struct mrvl_priv * priv,struct pp2_cls_plcr_params * params)964 setup_policer(struct mrvl_priv *priv, struct pp2_cls_plcr_params *params)
965 {
966 char match[16];
967 int ret;
968
969 /*
970 * At this point no other policers are used which means
971 * any policer can be picked up and used as a default one.
972 *
973 * Lets use 0th then.
974 */
975 sprintf(match, "policer-%d:%d\n", priv->pp_id, 0);
976 params->match = match;
977
978 ret = pp2_cls_plcr_init(params, &priv->default_policer);
979 if (ret) {
980 MRVL_LOG(ERR, "Failed to setup %s", match);
981 return -1;
982 }
983
984 priv->ppio_params.inqs_params.plcr = priv->default_policer;
985 priv->used_plcrs = BIT(0);
986
987 return 0;
988 }
989
990 /**
991 * Configure RX Queues in a given port.
992 *
993 * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
994 *
995 * @param priv Port's private data
996 * @param portid DPDK port ID
997 * @param max_queues Maximum number of queues to configure.
998 * @returns 0 in case of success, negative value otherwise.
999 */
1000 int
mrvl_configure_rxqs(struct mrvl_priv * priv,uint16_t portid,uint16_t max_queues)1001 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid,
1002 uint16_t max_queues)
1003 {
1004 size_t i, tc;
1005
1006 if (mrvl_cfg == NULL ||
1007 mrvl_cfg->port[portid].use_qos_global_defaults) {
1008 /*
1009 * No port configuration, use default: 1 TC, no QoS,
1010 * TC color set to green.
1011 */
1012 priv->ppio_params.inqs_params.num_tcs = 1;
1013 setup_tc(&priv->ppio_params.inqs_params.tcs_params[0],
1014 max_queues, priv->bpool, PP2_PPIO_COLOR_GREEN);
1015
1016 /* Direct mapping of queues i.e. 0->0, 1->1 etc. */
1017 for (i = 0; i < max_queues; ++i) {
1018 priv->rxq_map[i].tc = 0;
1019 priv->rxq_map[i].inq = i;
1020 }
1021 return 0;
1022 }
1023
1024 /* We need only a subset of configuration. */
1025 struct port_cfg *port_cfg = &mrvl_cfg->port[portid];
1026
1027 priv->qos_tbl_params.type = port_cfg->mapping_priority;
1028
1029 /*
1030 * We need to reverse mapping, from tc->pcp (better from usability
1031 * point of view) to pcp->tc (configurable in MUSDK).
1032 * First, set all map elements to "default".
1033 */
1034 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
1035 priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc;
1036
1037 /* Then, fill in all known values. */
1038 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
1039 if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) {
1040 /* Better safe than sorry. */
1041 MRVL_LOG(ERR,
1042 "Too many PCPs configured in TC %zu!", tc);
1043 return -1;
1044 }
1045 for (i = 0; i < port_cfg->tc[tc].pcps; ++i) {
1046 priv->qos_tbl_params.pcp_cos_map[
1047 port_cfg->tc[tc].pcp[i]].tc = tc;
1048 }
1049 }
1050
1051 /*
1052 * The same logic goes with DSCP.
1053 * First, set all map elements to "default".
1054 */
1055 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
1056 priv->qos_tbl_params.dscp_cos_map[i].tc =
1057 port_cfg->default_tc;
1058
1059 /* Fill in all known values. */
1060 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
1061 if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) {
1062 /* Better safe than sorry. */
1063 MRVL_LOG(ERR,
1064 "Too many DSCPs configured in TC %zu!", tc);
1065 return -1;
1066 }
1067 for (i = 0; i < port_cfg->tc[tc].dscps; ++i) {
1068 priv->qos_tbl_params.dscp_cos_map[
1069 port_cfg->tc[tc].dscp[i]].tc = tc;
1070 }
1071 }
1072
1073 /*
1074 * Surprisingly, similar logic goes with queue mapping.
1075 * We need only to store qid->tc mapping,
1076 * to know TC when queue is read.
1077 */
1078 for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
1079 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
1080
1081 /* Set up DPDKq->(TC,inq) mapping. */
1082 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
1083 if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) {
1084 /* Overflow. */
1085 MRVL_LOG(ERR,
1086 "Too many RX queues configured per TC %zu!",
1087 tc);
1088 return -1;
1089 }
1090 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
1091 uint8_t idx = port_cfg->tc[tc].inq[i];
1092
1093 if (idx > RTE_DIM(priv->rxq_map)) {
1094 MRVL_LOG(ERR, "Bad queue index %d!", idx);
1095 return -1;
1096 }
1097
1098 priv->rxq_map[idx].tc = tc;
1099 priv->rxq_map[idx].inq = i;
1100 }
1101 }
1102
1103 /*
1104 * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
1105 * with no gaps. Empty TC means end of processing.
1106 */
1107 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
1108 if (port_cfg->tc[i].inqs == 0)
1109 break;
1110 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
1111 port_cfg->tc[i].inqs,
1112 priv->bpool, port_cfg->tc[i].color);
1113 }
1114
1115 priv->ppio_params.inqs_params.num_tcs = i;
1116
1117 if (port_cfg->setup_policer)
1118 return setup_policer(priv, &port_cfg->policer_params);
1119
1120 return 0;
1121 }
1122
1123 /**
1124 * Configure TX Queues in a given port.
1125 *
1126 * Sets up TX queues egress scheduler and limiter.
1127 *
1128 * @param priv Port's private data
1129 * @param portid DPDK port ID
1130 * @param max_queues Maximum number of queues to configure.
1131 * @returns 0 in case of success, negative value otherwise.
1132 */
1133 int
mrvl_configure_txqs(struct mrvl_priv * priv,uint16_t portid,uint16_t max_queues)1134 mrvl_configure_txqs(struct mrvl_priv *priv, uint16_t portid,
1135 uint16_t max_queues)
1136 {
1137 /* We need only a subset of configuration. */
1138 struct port_cfg *port_cfg = &mrvl_cfg->port[portid];
1139 int i;
1140
1141 if (mrvl_cfg == NULL)
1142 return 0;
1143
1144 priv->ppio_params.rate_limit_enable = port_cfg->rate_limit_enable;
1145 if (port_cfg->rate_limit_enable)
1146 priv->ppio_params.rate_limit_params =
1147 port_cfg->rate_limit_params;
1148
1149 for (i = 0; i < max_queues; i++) {
1150 struct pp2_ppio_outq_params *params =
1151 &priv->ppio_params.outqs_params.outqs_params[i];
1152
1153 params->sched_mode = port_cfg->outq[i].sched_mode;
1154 params->weight = port_cfg->outq[i].weight;
1155 params->rate_limit_enable = port_cfg->outq[i].rate_limit_enable;
1156 params->rate_limit_params = port_cfg->outq[i].rate_limit_params;
1157 }
1158
1159 return 0;
1160 }
1161
1162 /**
1163 * Start QoS mapping.
1164 *
1165 * Finalize QoS table configuration and initialize it in SDK. It can be done
1166 * only after port is started, so we have a valid ppio reference.
1167 *
1168 * @param priv Port's private (configuration) data.
1169 * @returns 0 in case of success, exits otherwise.
1170 */
1171 int
mrvl_start_qos_mapping(struct mrvl_priv * priv)1172 mrvl_start_qos_mapping(struct mrvl_priv *priv)
1173 {
1174 size_t i;
1175
1176 if (priv->qos_tbl_params.type == PP2_CLS_QOS_TBL_NONE)
1177 return 0;
1178
1179 if (priv->ppio == NULL) {
1180 MRVL_LOG(ERR, "ppio must not be NULL here!");
1181 return -1;
1182 }
1183
1184 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
1185 priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio;
1186
1187 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
1188 priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio;
1189
1190 /* Initialize Classifier QoS table. */
1191
1192 return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);
1193 }
1194