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