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