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