xref: /dpdk/drivers/net/mvpp2/mrvl_qos.c (revision 8df71650e9fdc6346f09b7a57e86cded7b553152)
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
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
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
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
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
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", 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", 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", 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", 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", 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", 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", 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", 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'",
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", 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", 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", 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'",
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", 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", 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", 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
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", 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", 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",
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)",
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!", 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)!",
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)!",
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!",
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!",
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)!",
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)!",
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
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
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", 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
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
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
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