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