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