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