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