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