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