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