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