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