xref: /dpdk/drivers/net/mvpp2/mrvl_qos.c (revision d7eb4fb210d65eac30f1c1f420959d9dae62f514)
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_DSA_MODE_NONE "none"
24 #define MRVL_TOK_DSA_MODE_DSA "dsa"
25 #define MRVL_TOK_DSA_MODE_EXT_DSA "ext_dsa"
26 #define MRVL_TOK_DEFAULT_TC "default_tc"
27 #define MRVL_TOK_DSCP "dscp"
28 #define MRVL_TOK_MAPPING_PRIORITY "mapping_priority"
29 #define MRVL_TOK_IP "ip"
30 #define MRVL_TOK_IP_VLAN "ip/vlan"
31 #define MRVL_TOK_PCP "pcp"
32 #define MRVL_TOK_PORT "port"
33 #define MRVL_TOK_RXQ "rxq"
34 #define MRVL_TOK_TC "tc"
35 #define MRVL_TOK_TXQ "txq"
36 #define MRVL_TOK_VLAN "vlan"
37 #define MRVL_TOK_VLAN_IP "vlan/ip"
38 
39 /* egress specific configuration tokens */
40 #define MRVL_TOK_BURST_SIZE "burst_size"
41 #define MRVL_TOK_RATE_LIMIT "rate_limit"
42 #define MRVL_TOK_RATE_LIMIT_ENABLE "rate_limit_enable"
43 #define MRVL_TOK_SCHED_MODE "sched_mode"
44 #define MRVL_TOK_SCHED_MODE_SP "sp"
45 #define MRVL_TOK_SCHED_MODE_WRR "wrr"
46 #define MRVL_TOK_WRR_WEIGHT "wrr_weight"
47 
48 /* policer specific configuration tokens */
49 #define MRVL_TOK_PLCR "policer"
50 #define MRVL_TOK_PLCR_DEFAULT "default_policer"
51 #define MRVL_TOK_PLCR_UNIT "token_unit"
52 #define MRVL_TOK_PLCR_UNIT_BYTES "bytes"
53 #define MRVL_TOK_PLCR_UNIT_PACKETS "packets"
54 #define MRVL_TOK_PLCR_COLOR "color_mode"
55 #define MRVL_TOK_PLCR_COLOR_BLIND "blind"
56 #define MRVL_TOK_PLCR_COLOR_AWARE "aware"
57 #define MRVL_TOK_PLCR_CIR "cir"
58 #define MRVL_TOK_PLCR_CBS "cbs"
59 #define MRVL_TOK_PLCR_EBS "ebs"
60 #define MRVL_TOK_PLCR_DEFAULT_COLOR "default_color"
61 #define MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN "green"
62 #define MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW "yellow"
63 #define MRVL_TOK_PLCR_DEFAULT_COLOR_RED "red"
64 
65 /** Number of tokens in range a-b = 2. */
66 #define MAX_RNG_TOKENS 2
67 
68 /** Maximum possible value of PCP. */
69 #define MAX_PCP 7
70 
71 /** Maximum possible value of DSCP. */
72 #define MAX_DSCP 63
73 
74 /** Global configuration. */
75 struct mrvl_cfg *mrvl_cfg;
76 
77 /**
78  * Convert string to uint32_t with extra checks for result correctness.
79  *
80  * @param string String to convert.
81  * @param val Conversion result.
82  * @returns 0 in case of success, negative value otherwise.
83  */
84 static int
85 get_val_securely(const char *string, uint32_t *val)
86 {
87 	char *endptr;
88 	size_t len = strlen(string);
89 
90 	if (len == 0)
91 		return -1;
92 
93 	errno = 0;
94 	*val = strtoul(string, &endptr, 0);
95 	if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len)
96 		return -2;
97 
98 	return 0;
99 }
100 
101 /**
102  * Read out-queue configuration from file.
103  *
104  * @param file Path to the configuration file.
105  * @param port Port number.
106  * @param outq Out queue number.
107  * @param cfg Pointer to the Marvell configuration structure.
108  * @returns 0 in case of success, negative value otherwise.
109  */
110 static int
111 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
112 		struct mrvl_cfg *cfg)
113 {
114 	char sec_name[32];
115 	const char *entry;
116 	uint32_t val;
117 
118 	snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
119 		MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
120 
121 	/* Skip non-existing */
122 	if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
123 		return 0;
124 
125 	/* Read scheduling mode */
126 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_SCHED_MODE);
127 	if (entry) {
128 		if (!strncmp(entry, MRVL_TOK_SCHED_MODE_SP,
129 					strlen(MRVL_TOK_SCHED_MODE_SP))) {
130 			cfg->port[port].outq[outq].sched_mode =
131 				PP2_PPIO_SCHED_M_SP;
132 		} else if (!strncmp(entry, MRVL_TOK_SCHED_MODE_WRR,
133 					strlen(MRVL_TOK_SCHED_MODE_WRR))) {
134 			cfg->port[port].outq[outq].sched_mode =
135 				PP2_PPIO_SCHED_M_WRR;
136 		} else {
137 			MRVL_LOG(ERR, "Unknown token: %s", entry);
138 			return -1;
139 		}
140 	}
141 
142 	/* Read wrr weight */
143 	if (cfg->port[port].outq[outq].sched_mode == PP2_PPIO_SCHED_M_WRR) {
144 		entry = rte_cfgfile_get_entry(file, sec_name,
145 				MRVL_TOK_WRR_WEIGHT);
146 		if (entry) {
147 			if (get_val_securely(entry, &val) < 0)
148 				return -1;
149 			cfg->port[port].outq[outq].weight = val;
150 		}
151 	}
152 
153 	/*
154 	 * There's no point in setting rate limiting for specific outq as
155 	 * global port rate limiting has priority.
156 	 */
157 	if (cfg->port[port].rate_limit_enable) {
158 		MRVL_LOG(WARNING, "Port %d rate limiting already enabled",
159 			port);
160 		return 0;
161 	}
162 
163 	entry = rte_cfgfile_get_entry(file, sec_name,
164 			MRVL_TOK_RATE_LIMIT_ENABLE);
165 	if (entry) {
166 		if (get_val_securely(entry, &val) < 0)
167 			return -1;
168 		cfg->port[port].outq[outq].rate_limit_enable = val;
169 	}
170 
171 	if (!cfg->port[port].outq[outq].rate_limit_enable)
172 		return 0;
173 
174 	/* Read CBS (in kB) */
175 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_BURST_SIZE);
176 	if (entry) {
177 		if (get_val_securely(entry, &val) < 0)
178 			return -1;
179 		cfg->port[port].outq[outq].rate_limit_params.cbs = val;
180 	}
181 
182 	/* Read CIR (in kbps) */
183 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RATE_LIMIT);
184 	if (entry) {
185 		if (get_val_securely(entry, &val) < 0)
186 			return -1;
187 		cfg->port[port].outq[outq].rate_limit_params.cir = val;
188 	}
189 
190 	return 0;
191 }
192 
193 /**
194  * Gets multiple-entry values and places them in table.
195  *
196  * Entry can be anything, e.g. "1 2-3 5 6 7-9". This needs to be converted to
197  * table entries, respectively: {1, 2, 3, 5, 6, 7, 8, 9}.
198  * As all result table's elements are always 1-byte long, we
199  * won't overcomplicate the function, but we'll keep API generic,
200  * check if someone hasn't changed element size and make it simple
201  * to extend to other sizes.
202  *
203  * This function is purely utilitary, it does not print any error, only returns
204  * different error numbers.
205  *
206  * @param entry[in] Values string to parse.
207  * @param tab[out] Results table.
208  * @param elem_sz[in] Element size (in bytes).
209  * @param max_elems[in] Number of results table elements available.
210  * @param max val[in] Maximum value allowed.
211  * @returns Number of correctly parsed elements in case of success.
212  * @retval -1 Wrong element size.
213  * @retval -2 More tokens than result table allows.
214  * @retval -3 Wrong range syntax.
215  * @retval -4 Wrong range values.
216  * @retval -5 Maximum value exceeded.
217  */
218 static int
219 get_entry_values(const char *entry, uint8_t *tab,
220 	size_t elem_sz, uint8_t max_elems, uint8_t max_val)
221 {
222 	/* There should not be more tokens than max elements.
223 	 * Add 1 for error trap.
224 	 */
225 	char *tokens[max_elems + 1];
226 
227 	/* Begin, End + error trap = 3. */
228 	char *rng_tokens[MAX_RNG_TOKENS + 1];
229 	long beg, end;
230 	uint32_t token_val;
231 	int nb_tokens, nb_rng_tokens;
232 	int i;
233 	int values = 0;
234 	char val;
235 	char entry_cpy[CFG_VALUE_LEN];
236 
237 	if (elem_sz != 1)
238 		return -1;
239 
240 	/* Copy the entry to safely use rte_strsplit(). */
241 	strlcpy(entry_cpy, entry, RTE_DIM(entry_cpy));
242 
243 	/*
244 	 * If there are more tokens than array size, rte_strsplit will
245 	 * not return error, just array size.
246 	 */
247 	nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
248 		tokens, max_elems + 1, ' ');
249 
250 	/* Quick check, will be refined later. */
251 	if (nb_tokens > max_elems)
252 		return -2;
253 
254 	for (i = 0; i < nb_tokens; ++i) {
255 		if (strchr(tokens[i], '-') != NULL) {
256 			/*
257 			 * Split to begin and end tokens.
258 			 * We want to catch error cases too, thus we leave
259 			 * option for number of tokens to be more than 2.
260 			 */
261 			nb_rng_tokens = rte_strsplit(tokens[i],
262 					strlen(tokens[i]), rng_tokens,
263 					RTE_DIM(rng_tokens), '-');
264 			if (nb_rng_tokens != 2)
265 				return -3;
266 
267 			/* Range and sanity checks. */
268 			if (get_val_securely(rng_tokens[0], &token_val) < 0)
269 				return -4;
270 			beg = (char)token_val;
271 			if (get_val_securely(rng_tokens[1], &token_val) < 0)
272 				return -4;
273 			end = (char)token_val;
274 			if (beg < 0 || beg > UCHAR_MAX ||
275 				end < 0 || end > UCHAR_MAX || end < beg)
276 				return -4;
277 
278 			for (val = beg; val <= end; ++val) {
279 				if (val > max_val)
280 					return -5;
281 
282 				*tab = val;
283 				tab = RTE_PTR_ADD(tab, elem_sz);
284 				++values;
285 				if (values >= max_elems)
286 					return -2;
287 			}
288 		} else {
289 			/* Single values. */
290 			if (get_val_securely(tokens[i], &token_val) < 0)
291 				return -5;
292 			val = (char)token_val;
293 			if (val > max_val)
294 				return -5;
295 
296 			*tab = val;
297 			tab = RTE_PTR_ADD(tab, elem_sz);
298 			++values;
299 			if (values >= max_elems)
300 				return -2;
301 		}
302 	}
303 
304 	return values;
305 }
306 
307 /**
308  * Parse Traffic Class'es mapping configuration.
309  *
310  * @param file Config file handle.
311  * @param port Which port to look for.
312  * @param tc Which Traffic Class to look for.
313  * @param cfg[out] Parsing results.
314  * @returns 0 in case of success, negative value otherwise.
315  */
316 static int
317 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
318 		struct mrvl_cfg *cfg)
319 {
320 	char sec_name[32];
321 	const char *entry;
322 	int n;
323 
324 	snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
325 		MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
326 
327 	/* Skip non-existing */
328 	if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
329 		return 0;
330 
331 	cfg->port[port].use_global_defaults = 0;
332 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
333 	if (entry) {
334 		n = get_entry_values(entry,
335 			cfg->port[port].tc[tc].inq,
336 			sizeof(cfg->port[port].tc[tc].inq[0]),
337 			RTE_DIM(cfg->port[port].tc[tc].inq),
338 			MRVL_PP2_RXQ_MAX);
339 		if (n < 0) {
340 			MRVL_LOG(ERR, "Error %d while parsing: %s",
341 				n, entry);
342 			return n;
343 		}
344 		cfg->port[port].tc[tc].inqs = n;
345 	}
346 
347 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
348 	if (entry) {
349 		n = get_entry_values(entry,
350 			cfg->port[port].tc[tc].pcp,
351 			sizeof(cfg->port[port].tc[tc].pcp[0]),
352 			RTE_DIM(cfg->port[port].tc[tc].pcp),
353 			MAX_PCP);
354 		if (n < 0) {
355 			MRVL_LOG(ERR, "Error %d while parsing: %s",
356 				n, entry);
357 			return n;
358 		}
359 		cfg->port[port].tc[tc].pcps = n;
360 	}
361 
362 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
363 	if (entry) {
364 		n = get_entry_values(entry,
365 			cfg->port[port].tc[tc].dscp,
366 			sizeof(cfg->port[port].tc[tc].dscp[0]),
367 			RTE_DIM(cfg->port[port].tc[tc].dscp),
368 			MAX_DSCP);
369 		if (n < 0) {
370 			MRVL_LOG(ERR, "Error %d while parsing: %s",
371 				n, entry);
372 			return n;
373 		}
374 		cfg->port[port].tc[tc].dscps = n;
375 	}
376 
377 	if (!cfg->port[port].setup_policer)
378 		return 0;
379 
380 	entry = rte_cfgfile_get_entry(file, sec_name,
381 			MRVL_TOK_PLCR_DEFAULT_COLOR);
382 	if (entry) {
383 		if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN,
384 				sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN))) {
385 			cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_GREEN;
386 		} else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW,
387 				sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW))) {
388 			cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_YELLOW;
389 		} else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_RED,
390 				sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_RED))) {
391 			cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_RED;
392 		} else {
393 			MRVL_LOG(ERR, "Error while parsing: %s", entry);
394 			return -1;
395 		}
396 	}
397 
398 	return 0;
399 }
400 
401 /**
402  * Parse default port policer.
403  *
404  * @param file Config file handle.
405  * @param sec_name Section name with policer configuration
406  * @param port Port number.
407  * @param cfg[out] Parsing results.
408  * @returns 0 in case of success, negative value otherwise.
409  */
410 static int
411 parse_policer(struct rte_cfgfile *file, int port, const char *sec_name,
412 		struct mrvl_cfg *cfg)
413 {
414 	const char *entry;
415 	uint32_t val;
416 
417 	/* Read policer token unit */
418 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_UNIT);
419 	if (entry) {
420 		if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_BYTES,
421 					sizeof(MRVL_TOK_PLCR_UNIT_BYTES))) {
422 			cfg->port[port].policer_params.token_unit =
423 				PP2_CLS_PLCR_BYTES_TOKEN_UNIT;
424 		} else if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_PACKETS,
425 					sizeof(MRVL_TOK_PLCR_UNIT_PACKETS))) {
426 			cfg->port[port].policer_params.token_unit =
427 				PP2_CLS_PLCR_PACKETS_TOKEN_UNIT;
428 		} else {
429 			MRVL_LOG(ERR, "Unknown token: %s", entry);
430 			return -1;
431 		}
432 	}
433 
434 	/* Read policer color mode */
435 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_COLOR);
436 	if (entry) {
437 		if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_BLIND,
438 					sizeof(MRVL_TOK_PLCR_COLOR_BLIND))) {
439 			cfg->port[port].policer_params.color_mode =
440 				PP2_CLS_PLCR_COLOR_BLIND_MODE;
441 		} else if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_AWARE,
442 					sizeof(MRVL_TOK_PLCR_COLOR_AWARE))) {
443 			cfg->port[port].policer_params.color_mode =
444 				PP2_CLS_PLCR_COLOR_AWARE_MODE;
445 		} else {
446 			MRVL_LOG(ERR, "Error in parsing: %s", entry);
447 			return -1;
448 		}
449 	}
450 
451 	/* Read policer cir */
452 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CIR);
453 	if (entry) {
454 		if (get_val_securely(entry, &val) < 0)
455 			return -1;
456 		cfg->port[port].policer_params.cir = val;
457 	}
458 
459 	/* Read policer cbs */
460 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CBS);
461 	if (entry) {
462 		if (get_val_securely(entry, &val) < 0)
463 			return -1;
464 		cfg->port[port].policer_params.cbs = val;
465 	}
466 
467 	/* Read policer ebs */
468 	entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_EBS);
469 	if (entry) {
470 		if (get_val_securely(entry, &val) < 0)
471 			return -1;
472 		cfg->port[port].policer_params.ebs = val;
473 	}
474 
475 	cfg->port[port].setup_policer = 1;
476 
477 	return 0;
478 }
479 
480 /**
481  * Parse configuration - rte_kvargs_process handler.
482  *
483  * Opens configuration file and parses its content.
484  *
485  * @param key Unused.
486  * @param path Path to config file.
487  * @param extra_args Pointer to configuration structure.
488  * @returns 0 in case of success, exits otherwise.
489  */
490 int
491 mrvl_get_cfg(const char *key __rte_unused, const char *path, void *extra_args)
492 {
493 	struct mrvl_cfg **cfg = extra_args;
494 	struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
495 	uint32_t val;
496 	int n, i, ret;
497 	const char *entry;
498 	char sec_name[32];
499 
500 	if (file == NULL) {
501 		MRVL_LOG(ERR, "Cannot load configuration %s\n", path);
502 		return -1;
503 	}
504 
505 	/* Create configuration. This is never accessed on the fast path,
506 	 * so we can ignore socket.
507 	 */
508 	*cfg = rte_zmalloc("mrvl_cfg", sizeof(struct mrvl_cfg), 0);
509 	if (*cfg == NULL) {
510 		MRVL_LOG(ERR, "Cannot allocate configuration %s\n", path);
511 		return -1;
512 	}
513 
514 	n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
515 		sizeof(MRVL_TOK_PORT) - 1);
516 
517 	if (n == 0) {
518 		/* This is weird, but not bad. */
519 		MRVL_LOG(WARNING, "Empty configuration file?");
520 		return 0;
521 	}
522 
523 	/* Use the number of ports given as vdev parameters. */
524 	for (n = 0; n < (PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC); ++n) {
525 		snprintf(sec_name, sizeof(sec_name), "%s %d %s",
526 			MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT);
527 
528 		/* Use global defaults, unless an override occurs */
529 		(*cfg)->port[n].use_global_defaults = 1;
530 
531 		/* Skip ports non-existing in configuration. */
532 		if (rte_cfgfile_num_sections(file, sec_name,
533 				strlen(sec_name)) <= 0) {
534 			continue;
535 		}
536 
537 		entry = rte_cfgfile_get_entry(file, sec_name,
538 				MRVL_TOK_DSA_MODE);
539 		if (entry) {
540 			if (!strncmp(entry, MRVL_TOK_DSA_MODE_NONE,
541 				sizeof(MRVL_TOK_DSA_MODE_NONE)))
542 				(*cfg)->port[n].eth_start_hdr =
543 				PP2_PPIO_HDR_ETH;
544 			else if (!strncmp(entry, MRVL_TOK_DSA_MODE_DSA,
545 				sizeof(MRVL_TOK_DSA_MODE_DSA)))
546 				(*cfg)->port[n].eth_start_hdr =
547 				PP2_PPIO_HDR_ETH_DSA;
548 			else if (!strncmp(entry, MRVL_TOK_DSA_MODE_EXT_DSA,
549 				sizeof(MRVL_TOK_DSA_MODE_EXT_DSA))) {
550 				(*cfg)->port[n].eth_start_hdr =
551 				PP2_PPIO_HDR_ETH_EXT_DSA;
552 			} else {
553 				MRVL_LOG(ERR,
554 					"Error in parsing %s value (%s)!\n",
555 					MRVL_TOK_DSA_MODE, entry);
556 				return -1;
557 			}
558 		} else {
559 			(*cfg)->port[n].eth_start_hdr = PP2_PPIO_HDR_ETH;
560 		}
561 
562 		/*
563 		 * Read per-port rate limiting. Setting that will
564 		 * disable per-queue rate limiting.
565 		 */
566 		entry = rte_cfgfile_get_entry(file, sec_name,
567 				MRVL_TOK_RATE_LIMIT_ENABLE);
568 		if (entry) {
569 			if (get_val_securely(entry, &val) < 0)
570 				return -1;
571 			(*cfg)->port[n].rate_limit_enable = val;
572 		}
573 
574 		if ((*cfg)->port[n].rate_limit_enable) {
575 			entry = rte_cfgfile_get_entry(file, sec_name,
576 					MRVL_TOK_BURST_SIZE);
577 			if (entry) {
578 				if (get_val_securely(entry, &val) < 0)
579 					return -1;
580 				(*cfg)->port[n].rate_limit_params.cbs = val;
581 			}
582 
583 			entry = rte_cfgfile_get_entry(file, sec_name,
584 					MRVL_TOK_RATE_LIMIT);
585 			if (entry) {
586 				if (get_val_securely(entry, &val) < 0)
587 					return -1;
588 				(*cfg)->port[n].rate_limit_params.cir = val;
589 			}
590 		}
591 
592 		entry = rte_cfgfile_get_entry(file, sec_name,
593 				MRVL_TOK_MAPPING_PRIORITY);
594 		if (entry) {
595 			(*cfg)->port[n].use_global_defaults = 0;
596 			if (!strncmp(entry, MRVL_TOK_VLAN_IP,
597 				sizeof(MRVL_TOK_VLAN_IP)))
598 				(*cfg)->port[n].mapping_priority =
599 					PP2_CLS_QOS_TBL_VLAN_IP_PRI;
600 			else if (!strncmp(entry, MRVL_TOK_IP_VLAN,
601 				sizeof(MRVL_TOK_IP_VLAN)))
602 				(*cfg)->port[n].mapping_priority =
603 					PP2_CLS_QOS_TBL_IP_VLAN_PRI;
604 			else if (!strncmp(entry, MRVL_TOK_IP,
605 				sizeof(MRVL_TOK_IP)))
606 				(*cfg)->port[n].mapping_priority =
607 					PP2_CLS_QOS_TBL_IP_PRI;
608 			else if (!strncmp(entry, MRVL_TOK_VLAN,
609 				sizeof(MRVL_TOK_VLAN))) {
610 				(*cfg)->port[n].mapping_priority =
611 					PP2_CLS_QOS_TBL_VLAN_PRI;
612 			} else {
613 				MRVL_LOG(ERR,
614 					"Error in parsing %s value (%s)!\n",
615 					MRVL_TOK_MAPPING_PRIORITY, entry);
616 				return -1;
617 			}
618 		} else {
619 			(*cfg)->port[n].mapping_priority =
620 				PP2_CLS_QOS_TBL_VLAN_IP_PRI;
621 		}
622 
623 		/* Parse policer configuration (if any) */
624 		entry = rte_cfgfile_get_entry(file, sec_name,
625 				MRVL_TOK_PLCR_DEFAULT);
626 		if (entry) {
627 			(*cfg)->port[n].use_global_defaults = 0;
628 			if (get_val_securely(entry, &val) < 0)
629 				return -1;
630 
631 			snprintf(sec_name, sizeof(sec_name), "%s %d",
632 					MRVL_TOK_PLCR, val);
633 			ret = parse_policer(file, n, sec_name, *cfg);
634 			if (ret)
635 				return -1;
636 		}
637 
638 		for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
639 			ret = get_outq_cfg(file, n, i, *cfg);
640 			if (ret < 0) {
641 				MRVL_LOG(ERR,
642 					"Error %d parsing port %d outq %d!\n",
643 					ret, n, i);
644 				return -1;
645 			}
646 		}
647 
648 		for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
649 			ret = parse_tc_cfg(file, n, i, *cfg);
650 			if (ret < 0) {
651 				MRVL_LOG(ERR,
652 					"Error %d parsing port %d tc %d!\n",
653 					ret, n, i);
654 				return -1;
655 			}
656 		}
657 
658 		entry = rte_cfgfile_get_entry(file, sec_name,
659 					      MRVL_TOK_DEFAULT_TC);
660 		if (entry) {
661 			if (get_val_securely(entry, &val) < 0 ||
662 			    val > USHRT_MAX)
663 				return -1;
664 			(*cfg)->port[n].default_tc = (uint8_t)val;
665 		} else {
666 			if ((*cfg)->port[n].use_global_defaults == 0) {
667 				MRVL_LOG(ERR,
668 					 "Default Traffic Class required in "
669 					 "custom configuration!");
670 				return -1;
671 			}
672 		}
673 	}
674 
675 	return 0;
676 }
677 
678 /**
679  * Setup Traffic Class.
680  *
681  * Fill in TC parameters in single MUSDK TC config entry.
682  * @param param TC parameters entry.
683  * @param inqs Number of MUSDK in-queues in this TC.
684  * @param bpool Bpool for this TC.
685  * @param color Default color for this TC.
686  * @returns 0 in case of success, exits otherwise.
687  */
688 static int
689 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
690 	struct pp2_bpool *bpool, enum pp2_ppio_color color)
691 {
692 	struct pp2_ppio_inq_params *inq_params;
693 
694 	param->pkt_offset = MRVL_PKT_OFFS;
695 	param->pools[0][0] = bpool;
696 	param->default_color = color;
697 
698 	inq_params = rte_zmalloc_socket("inq_params",
699 		inqs * sizeof(*inq_params),
700 		0, rte_socket_id());
701 	if (!inq_params)
702 		return -ENOMEM;
703 
704 	param->num_in_qs = inqs;
705 
706 	/* Release old config if necessary. */
707 	if (param->inqs_params)
708 		rte_free(param->inqs_params);
709 
710 	param->inqs_params = inq_params;
711 
712 	return 0;
713 }
714 
715 /**
716  * Setup ingress policer.
717  *
718  * @param priv Port's private data.
719  * @param params Pointer to the policer's configuration.
720  * @param plcr_id Policer id.
721  * @returns 0 in case of success, negative values otherwise.
722  */
723 static int
724 setup_policer(struct mrvl_priv *priv, struct pp2_cls_plcr_params *params)
725 {
726 	char match[16];
727 	int ret;
728 
729 	/*
730 	 * At this point no other policers are used which means
731 	 * any policer can be picked up and used as a default one.
732 	 *
733 	 * Lets use 0th then.
734 	 */
735 	sprintf(match, "policer-%d:%d\n", priv->pp_id, 0);
736 	params->match = match;
737 
738 	ret = pp2_cls_plcr_init(params, &priv->default_policer);
739 	if (ret) {
740 		MRVL_LOG(ERR, "Failed to setup %s", match);
741 		return -1;
742 	}
743 
744 	priv->ppio_params.inqs_params.plcr = priv->default_policer;
745 	priv->used_plcrs = BIT(0);
746 
747 	return 0;
748 }
749 
750 /**
751  * Configure RX Queues in a given port.
752  *
753  * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
754  *
755  * @param priv Port's private data
756  * @param portid DPDK port ID
757  * @param max_queues Maximum number of queues to configure.
758  * @returns 0 in case of success, negative value otherwise.
759  */
760 int
761 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid,
762 	uint16_t max_queues)
763 {
764 	size_t i, tc;
765 
766 	if (mrvl_cfg == NULL ||
767 		mrvl_cfg->port[portid].use_global_defaults) {
768 		/*
769 		 * No port configuration, use default: 1 TC, no QoS,
770 		 * TC color set to green.
771 		 */
772 		priv->ppio_params.inqs_params.num_tcs = 1;
773 		setup_tc(&priv->ppio_params.inqs_params.tcs_params[0],
774 			max_queues, priv->bpool, PP2_PPIO_COLOR_GREEN);
775 
776 		/* Direct mapping of queues i.e. 0->0, 1->1 etc. */
777 		for (i = 0; i < max_queues; ++i) {
778 			priv->rxq_map[i].tc = 0;
779 			priv->rxq_map[i].inq = i;
780 		}
781 		return 0;
782 	}
783 
784 	/* We need only a subset of configuration. */
785 	struct port_cfg *port_cfg = &mrvl_cfg->port[portid];
786 
787 	priv->qos_tbl_params.type = port_cfg->mapping_priority;
788 
789 	/*
790 	 * We need to reverse mapping, from tc->pcp (better from usability
791 	 * point of view) to pcp->tc (configurable in MUSDK).
792 	 * First, set all map elements to "default".
793 	 */
794 	for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
795 		priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc;
796 
797 	/* Then, fill in all known values. */
798 	for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
799 		if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) {
800 			/* Better safe than sorry. */
801 			MRVL_LOG(ERR,
802 				"Too many PCPs configured in TC %zu!", tc);
803 			return -1;
804 		}
805 		for (i = 0; i < port_cfg->tc[tc].pcps; ++i) {
806 			priv->qos_tbl_params.pcp_cos_map[
807 			  port_cfg->tc[tc].pcp[i]].tc = tc;
808 		}
809 	}
810 
811 	/*
812 	 * The same logic goes with DSCP.
813 	 * First, set all map elements to "default".
814 	 */
815 	for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
816 		priv->qos_tbl_params.dscp_cos_map[i].tc =
817 			port_cfg->default_tc;
818 
819 	/* Fill in all known values. */
820 	for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
821 		if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) {
822 			/* Better safe than sorry. */
823 			MRVL_LOG(ERR,
824 				"Too many DSCPs configured in TC %zu!", tc);
825 			return -1;
826 		}
827 		for (i = 0; i < port_cfg->tc[tc].dscps; ++i) {
828 			priv->qos_tbl_params.dscp_cos_map[
829 			  port_cfg->tc[tc].dscp[i]].tc = tc;
830 		}
831 	}
832 
833 	/*
834 	 * Surprisingly, similar logic goes with queue mapping.
835 	 * We need only to store qid->tc mapping,
836 	 * to know TC when queue is read.
837 	 */
838 	for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
839 		priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
840 
841 	/* Set up DPDKq->(TC,inq) mapping. */
842 	for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
843 		if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) {
844 			/* Overflow. */
845 			MRVL_LOG(ERR,
846 				"Too many RX queues configured per TC %zu!",
847 				tc);
848 			return -1;
849 		}
850 		for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
851 			uint8_t idx = port_cfg->tc[tc].inq[i];
852 
853 			if (idx > RTE_DIM(priv->rxq_map)) {
854 				MRVL_LOG(ERR, "Bad queue index %d!", idx);
855 				return -1;
856 			}
857 
858 			priv->rxq_map[idx].tc = tc;
859 			priv->rxq_map[idx].inq = i;
860 		}
861 	}
862 
863 	/*
864 	 * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
865 	 * with no gaps. Empty TC means end of processing.
866 	 */
867 	for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
868 		if (port_cfg->tc[i].inqs == 0)
869 			break;
870 		setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
871 				port_cfg->tc[i].inqs,
872 				priv->bpool, port_cfg->tc[i].color);
873 	}
874 
875 	priv->ppio_params.inqs_params.num_tcs = i;
876 
877 	if (port_cfg->setup_policer)
878 		return setup_policer(priv, &port_cfg->policer_params);
879 
880 	return 0;
881 }
882 
883 /**
884  * Configure TX Queues in a given port.
885  *
886  * Sets up TX queues egress scheduler and limiter.
887  *
888  * @param priv Port's private data
889  * @param portid DPDK port ID
890  * @param max_queues Maximum number of queues to configure.
891  * @returns 0 in case of success, negative value otherwise.
892  */
893 int
894 mrvl_configure_txqs(struct mrvl_priv *priv, uint16_t portid,
895 		uint16_t max_queues)
896 {
897 	/* We need only a subset of configuration. */
898 	struct port_cfg *port_cfg = &mrvl_cfg->port[portid];
899 	int i;
900 
901 	if (mrvl_cfg == NULL)
902 		return 0;
903 
904 	priv->ppio_params.rate_limit_enable = port_cfg->rate_limit_enable;
905 	if (port_cfg->rate_limit_enable)
906 		priv->ppio_params.rate_limit_params =
907 			port_cfg->rate_limit_params;
908 
909 	for (i = 0; i < max_queues; i++) {
910 		struct pp2_ppio_outq_params *params =
911 			&priv->ppio_params.outqs_params.outqs_params[i];
912 
913 		params->sched_mode = port_cfg->outq[i].sched_mode;
914 		params->weight = port_cfg->outq[i].weight;
915 		params->rate_limit_enable = port_cfg->outq[i].rate_limit_enable;
916 		params->rate_limit_params = port_cfg->outq[i].rate_limit_params;
917 	}
918 
919 	return 0;
920 }
921 
922 /**
923  * Start QoS mapping.
924  *
925  * Finalize QoS table configuration and initialize it in SDK. It can be done
926  * only after port is started, so we have a valid ppio reference.
927  *
928  * @param priv Port's private (configuration) data.
929  * @returns 0 in case of success, exits otherwise.
930  */
931 int
932 mrvl_start_qos_mapping(struct mrvl_priv *priv)
933 {
934 	size_t i;
935 
936 	if (priv->ppio == NULL) {
937 		MRVL_LOG(ERR, "ppio must not be NULL here!");
938 		return -1;
939 	}
940 
941 	for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
942 		priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio;
943 
944 	for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
945 		priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio;
946 
947 	/* Initialize Classifier QoS table. */
948 
949 	return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);
950 }
951