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_DEFAULT_TC "default_tc" 23 #define MRVL_TOK_DSCP "dscp" 24 #define MRVL_TOK_MAPPING_PRIORITY "mapping_priority" 25 #define MRVL_TOK_IP "ip" 26 #define MRVL_TOK_IP_VLAN "ip/vlan" 27 #define MRVL_TOK_PCP "pcp" 28 #define MRVL_TOK_PORT "port" 29 #define MRVL_TOK_RXQ "rxq" 30 #define MRVL_TOK_TC "tc" 31 #define MRVL_TOK_TXQ "txq" 32 #define MRVL_TOK_VLAN "vlan" 33 #define MRVL_TOK_VLAN_IP "vlan/ip" 34 35 /* egress specific configuration tokens */ 36 #define MRVL_TOK_BURST_SIZE "burst_size" 37 #define MRVL_TOK_RATE_LIMIT "rate_limit" 38 #define MRVL_TOK_RATE_LIMIT_ENABLE "rate_limit_enable" 39 #define MRVL_TOK_SCHED_MODE "sched_mode" 40 #define MRVL_TOK_SCHED_MODE_SP "sp" 41 #define MRVL_TOK_SCHED_MODE_WRR "wrr" 42 #define MRVL_TOK_WRR_WEIGHT "wrr_weight" 43 44 /* policer specific configuration tokens */ 45 #define MRVL_TOK_PLCR "policer" 46 #define MRVL_TOK_PLCR_DEFAULT "default_policer" 47 #define MRVL_TOK_PLCR_UNIT "token_unit" 48 #define MRVL_TOK_PLCR_UNIT_BYTES "bytes" 49 #define MRVL_TOK_PLCR_UNIT_PACKETS "packets" 50 #define MRVL_TOK_PLCR_COLOR "color_mode" 51 #define MRVL_TOK_PLCR_COLOR_BLIND "blind" 52 #define MRVL_TOK_PLCR_COLOR_AWARE "aware" 53 #define MRVL_TOK_PLCR_CIR "cir" 54 #define MRVL_TOK_PLCR_CBS "cbs" 55 #define MRVL_TOK_PLCR_EBS "ebs" 56 #define MRVL_TOK_PLCR_DEFAULT_COLOR "default_color" 57 #define MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN "green" 58 #define MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW "yellow" 59 #define MRVL_TOK_PLCR_DEFAULT_COLOR_RED "red" 60 61 /** Number of tokens in range a-b = 2. */ 62 #define MAX_RNG_TOKENS 2 63 64 /** Maximum possible value of PCP. */ 65 #define MAX_PCP 7 66 67 /** Maximum possible value of DSCP. */ 68 #define MAX_DSCP 63 69 70 /** Global QoS configuration. */ 71 struct mrvl_qos_cfg *mrvl_qos_cfg; 72 73 /** 74 * Convert string to uint32_t with extra checks for result correctness. 75 * 76 * @param string String to convert. 77 * @param val Conversion result. 78 * @returns 0 in case of success, negative value otherwise. 79 */ 80 static int 81 get_val_securely(const char *string, uint32_t *val) 82 { 83 char *endptr; 84 size_t len = strlen(string); 85 86 if (len == 0) 87 return -1; 88 89 errno = 0; 90 *val = strtoul(string, &endptr, 0); 91 if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len) 92 return -2; 93 94 return 0; 95 } 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 QoS 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_qos_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 Class'es 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_qos_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 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ); 328 if (entry) { 329 n = get_entry_values(entry, 330 cfg->port[port].tc[tc].inq, 331 sizeof(cfg->port[port].tc[tc].inq[0]), 332 RTE_DIM(cfg->port[port].tc[tc].inq), 333 MRVL_PP2_RXQ_MAX); 334 if (n < 0) { 335 MRVL_LOG(ERR, "Error %d while parsing: %s", 336 n, entry); 337 return n; 338 } 339 cfg->port[port].tc[tc].inqs = n; 340 } 341 342 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP); 343 if (entry) { 344 n = get_entry_values(entry, 345 cfg->port[port].tc[tc].pcp, 346 sizeof(cfg->port[port].tc[tc].pcp[0]), 347 RTE_DIM(cfg->port[port].tc[tc].pcp), 348 MAX_PCP); 349 if (n < 0) { 350 MRVL_LOG(ERR, "Error %d while parsing: %s", 351 n, entry); 352 return n; 353 } 354 cfg->port[port].tc[tc].pcps = n; 355 } 356 357 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP); 358 if (entry) { 359 n = get_entry_values(entry, 360 cfg->port[port].tc[tc].dscp, 361 sizeof(cfg->port[port].tc[tc].dscp[0]), 362 RTE_DIM(cfg->port[port].tc[tc].dscp), 363 MAX_DSCP); 364 if (n < 0) { 365 MRVL_LOG(ERR, "Error %d while parsing: %s", 366 n, entry); 367 return n; 368 } 369 cfg->port[port].tc[tc].dscps = n; 370 } 371 372 if (!cfg->port[port].setup_policer) 373 return 0; 374 375 entry = rte_cfgfile_get_entry(file, sec_name, 376 MRVL_TOK_PLCR_DEFAULT_COLOR); 377 if (entry) { 378 if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN, 379 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN))) { 380 cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_GREEN; 381 } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW, 382 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW))) { 383 cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_YELLOW; 384 } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_RED, 385 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_RED))) { 386 cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_RED; 387 } else { 388 MRVL_LOG(ERR, "Error while parsing: %s", entry); 389 return -1; 390 } 391 } 392 393 return 0; 394 } 395 396 /** 397 * Parse default port policer. 398 * 399 * @param file Config file handle. 400 * @param sec_name Section name with policer configuration 401 * @param port Port number. 402 * @param cfg[out] Parsing results. 403 * @returns 0 in case of success, negative value otherwise. 404 */ 405 static int 406 parse_policer(struct rte_cfgfile *file, int port, const char *sec_name, 407 struct mrvl_qos_cfg *cfg) 408 { 409 const char *entry; 410 uint32_t val; 411 412 /* Read policer token unit */ 413 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_UNIT); 414 if (entry) { 415 if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_BYTES, 416 sizeof(MRVL_TOK_PLCR_UNIT_BYTES))) { 417 cfg->port[port].policer_params.token_unit = 418 PP2_CLS_PLCR_BYTES_TOKEN_UNIT; 419 } else if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_PACKETS, 420 sizeof(MRVL_TOK_PLCR_UNIT_PACKETS))) { 421 cfg->port[port].policer_params.token_unit = 422 PP2_CLS_PLCR_PACKETS_TOKEN_UNIT; 423 } else { 424 RTE_LOG(ERR, PMD, "Unknown token: %s\n", entry); 425 return -1; 426 } 427 } 428 429 /* Read policer color mode */ 430 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_COLOR); 431 if (entry) { 432 if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_BLIND, 433 sizeof(MRVL_TOK_PLCR_COLOR_BLIND))) { 434 cfg->port[port].policer_params.color_mode = 435 PP2_CLS_PLCR_COLOR_BLIND_MODE; 436 } else if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_AWARE, 437 sizeof(MRVL_TOK_PLCR_COLOR_AWARE))) { 438 cfg->port[port].policer_params.color_mode = 439 PP2_CLS_PLCR_COLOR_AWARE_MODE; 440 } else { 441 RTE_LOG(ERR, PMD, "Error in parsing: %s\n", entry); 442 return -1; 443 } 444 } 445 446 /* Read policer cir */ 447 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CIR); 448 if (entry) { 449 if (get_val_securely(entry, &val) < 0) 450 return -1; 451 cfg->port[port].policer_params.cir = val; 452 } 453 454 /* Read policer cbs */ 455 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CBS); 456 if (entry) { 457 if (get_val_securely(entry, &val) < 0) 458 return -1; 459 cfg->port[port].policer_params.cbs = val; 460 } 461 462 /* Read policer ebs */ 463 entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_EBS); 464 if (entry) { 465 if (get_val_securely(entry, &val) < 0) 466 return -1; 467 cfg->port[port].policer_params.ebs = val; 468 } 469 470 cfg->port[port].setup_policer = 1; 471 472 return 0; 473 } 474 475 /** 476 * Parse QoS configuration - rte_kvargs_process handler. 477 * 478 * Opens configuration file and parses its content. 479 * 480 * @param key Unused. 481 * @param path Path to config file. 482 * @param extra_args Pointer to configuration structure. 483 * @returns 0 in case of success, exits otherwise. 484 */ 485 int 486 mrvl_get_qoscfg(const char *key __rte_unused, const char *path, 487 void *extra_args) 488 { 489 struct mrvl_qos_cfg **cfg = extra_args; 490 struct rte_cfgfile *file = rte_cfgfile_load(path, 0); 491 uint32_t val; 492 int n, i, ret; 493 const char *entry; 494 char sec_name[32]; 495 496 if (file == NULL) 497 rte_exit(EXIT_FAILURE, "Cannot load configuration %s\n", path); 498 499 /* Create configuration. This is never accessed on the fast path, 500 * so we can ignore socket. 501 */ 502 *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0); 503 if (*cfg == NULL) 504 rte_exit(EXIT_FAILURE, "Cannot allocate configuration %s\n", 505 path); 506 507 n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT, 508 sizeof(MRVL_TOK_PORT) - 1); 509 510 if (n == 0) { 511 /* This is weird, but not bad. */ 512 MRVL_LOG(WARNING, "Empty configuration file?"); 513 return 0; 514 } 515 516 /* Use the number of ports given as vdev parameters. */ 517 for (n = 0; n < (PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC); ++n) { 518 snprintf(sec_name, sizeof(sec_name), "%s %d %s", 519 MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT); 520 521 /* Skip ports non-existing in configuration. */ 522 if (rte_cfgfile_num_sections(file, sec_name, 523 strlen(sec_name)) <= 0) { 524 (*cfg)->port[n].use_global_defaults = 1; 525 (*cfg)->port[n].mapping_priority = 526 PP2_CLS_QOS_TBL_VLAN_IP_PRI; 527 continue; 528 } 529 530 entry = rte_cfgfile_get_entry(file, sec_name, 531 MRVL_TOK_DEFAULT_TC); 532 if (entry) { 533 if (get_val_securely(entry, &val) < 0 || 534 val > USHRT_MAX) 535 return -1; 536 (*cfg)->port[n].default_tc = (uint8_t)val; 537 } else { 538 MRVL_LOG(ERR, 539 "Default Traffic Class required in custom configuration!"); 540 return -1; 541 } 542 543 /* 544 * Read per-port rate limiting. Setting that will 545 * disable per-queue rate limiting. 546 */ 547 entry = rte_cfgfile_get_entry(file, sec_name, 548 MRVL_TOK_RATE_LIMIT_ENABLE); 549 if (entry) { 550 if (get_val_securely(entry, &val) < 0) 551 return -1; 552 (*cfg)->port[n].rate_limit_enable = val; 553 } 554 555 if ((*cfg)->port[n].rate_limit_enable) { 556 entry = rte_cfgfile_get_entry(file, sec_name, 557 MRVL_TOK_BURST_SIZE); 558 if (entry) { 559 if (get_val_securely(entry, &val) < 0) 560 return -1; 561 (*cfg)->port[n].rate_limit_params.cbs = val; 562 } 563 564 entry = rte_cfgfile_get_entry(file, sec_name, 565 MRVL_TOK_RATE_LIMIT); 566 if (entry) { 567 if (get_val_securely(entry, &val) < 0) 568 return -1; 569 (*cfg)->port[n].rate_limit_params.cir = val; 570 } 571 } 572 573 entry = rte_cfgfile_get_entry(file, sec_name, 574 MRVL_TOK_MAPPING_PRIORITY); 575 if (entry) { 576 if (!strncmp(entry, MRVL_TOK_VLAN_IP, 577 sizeof(MRVL_TOK_VLAN_IP))) 578 (*cfg)->port[n].mapping_priority = 579 PP2_CLS_QOS_TBL_VLAN_IP_PRI; 580 else if (!strncmp(entry, MRVL_TOK_IP_VLAN, 581 sizeof(MRVL_TOK_IP_VLAN))) 582 (*cfg)->port[n].mapping_priority = 583 PP2_CLS_QOS_TBL_IP_VLAN_PRI; 584 else if (!strncmp(entry, MRVL_TOK_IP, 585 sizeof(MRVL_TOK_IP))) 586 (*cfg)->port[n].mapping_priority = 587 PP2_CLS_QOS_TBL_IP_PRI; 588 else if (!strncmp(entry, MRVL_TOK_VLAN, 589 sizeof(MRVL_TOK_VLAN))) 590 (*cfg)->port[n].mapping_priority = 591 PP2_CLS_QOS_TBL_VLAN_PRI; 592 else 593 rte_exit(EXIT_FAILURE, 594 "Error in parsing %s value (%s)!\n", 595 MRVL_TOK_MAPPING_PRIORITY, entry); 596 } else { 597 (*cfg)->port[n].mapping_priority = 598 PP2_CLS_QOS_TBL_VLAN_IP_PRI; 599 } 600 601 /* Parse policer configuration (if any) */ 602 entry = rte_cfgfile_get_entry(file, sec_name, 603 MRVL_TOK_PLCR_DEFAULT); 604 if (entry) { 605 if (get_val_securely(entry, &val) < 0) 606 return -1; 607 608 snprintf(sec_name, sizeof(sec_name), "%s %d", 609 MRVL_TOK_PLCR, val); 610 ret = parse_policer(file, n, sec_name, *cfg); 611 if (ret) 612 return -1; 613 } 614 615 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) { 616 ret = get_outq_cfg(file, n, i, *cfg); 617 if (ret < 0) 618 rte_exit(EXIT_FAILURE, 619 "Error %d parsing port %d outq %d!\n", 620 ret, n, i); 621 } 622 623 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) { 624 ret = parse_tc_cfg(file, n, i, *cfg); 625 if (ret < 0) 626 rte_exit(EXIT_FAILURE, 627 "Error %d parsing port %d tc %d!\n", 628 ret, n, i); 629 } 630 } 631 632 return 0; 633 } 634 635 /** 636 * Setup Traffic Class. 637 * 638 * Fill in TC parameters in single MUSDK TC config entry. 639 * @param param TC parameters entry. 640 * @param inqs Number of MUSDK in-queues in this TC. 641 * @param bpool Bpool for this TC. 642 * @param color Default color for this TC. 643 * @returns 0 in case of success, exits otherwise. 644 */ 645 static int 646 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs, 647 struct pp2_bpool *bpool, enum pp2_ppio_color color) 648 { 649 struct pp2_ppio_inq_params *inq_params; 650 651 param->pkt_offset = MRVL_PKT_OFFS; 652 param->pools[0] = bpool; 653 param->default_color = color; 654 655 inq_params = rte_zmalloc_socket("inq_params", 656 inqs * sizeof(*inq_params), 657 0, rte_socket_id()); 658 if (!inq_params) 659 return -ENOMEM; 660 661 param->num_in_qs = inqs; 662 663 /* Release old config if necessary. */ 664 if (param->inqs_params) 665 rte_free(param->inqs_params); 666 667 param->inqs_params = inq_params; 668 669 return 0; 670 } 671 672 /** 673 * Setup ingress policer. 674 * 675 * @param priv Port's private data. 676 * @param params Pointer to the policer's configuration. 677 * @param plcr_id Policer id. 678 * @returns 0 in case of success, negative values otherwise. 679 */ 680 static int 681 setup_policer(struct mrvl_priv *priv, struct pp2_cls_plcr_params *params) 682 { 683 char match[16]; 684 int ret; 685 686 /* 687 * At this point no other policers are used which means 688 * any policer can be picked up and used as a default one. 689 * 690 * Lets use 0th then. 691 */ 692 sprintf(match, "policer-%d:%d\n", priv->pp_id, 0); 693 params->match = match; 694 695 ret = pp2_cls_plcr_init(params, &priv->default_policer); 696 if (ret) { 697 MRVL_LOG(ERR, "Failed to setup %s", match); 698 return -1; 699 } 700 701 priv->ppio_params.inqs_params.plcr = priv->default_policer; 702 priv->used_plcrs = BIT(0); 703 704 return 0; 705 } 706 707 /** 708 * Configure RX Queues in a given port. 709 * 710 * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping. 711 * 712 * @param priv Port's private data 713 * @param portid DPDK port ID 714 * @param max_queues Maximum number of queues to configure. 715 * @returns 0 in case of success, negative value otherwise. 716 */ 717 int 718 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid, 719 uint16_t max_queues) 720 { 721 size_t i, tc; 722 723 if (mrvl_qos_cfg == NULL || 724 mrvl_qos_cfg->port[portid].use_global_defaults) { 725 /* 726 * No port configuration, use default: 1 TC, no QoS, 727 * TC color set to green. 728 */ 729 priv->ppio_params.inqs_params.num_tcs = 1; 730 setup_tc(&priv->ppio_params.inqs_params.tcs_params[0], 731 max_queues, priv->bpool, PP2_PPIO_COLOR_GREEN); 732 733 /* Direct mapping of queues i.e. 0->0, 1->1 etc. */ 734 for (i = 0; i < max_queues; ++i) { 735 priv->rxq_map[i].tc = 0; 736 priv->rxq_map[i].inq = i; 737 } 738 return 0; 739 } 740 741 /* We need only a subset of configuration. */ 742 struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid]; 743 744 priv->qos_tbl_params.type = port_cfg->mapping_priority; 745 746 /* 747 * We need to reverse mapping, from tc->pcp (better from usability 748 * point of view) to pcp->tc (configurable in MUSDK). 749 * First, set all map elements to "default". 750 */ 751 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i) 752 priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc; 753 754 /* Then, fill in all known values. */ 755 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) { 756 if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) { 757 /* Better safe than sorry. */ 758 MRVL_LOG(ERR, 759 "Too many PCPs configured in TC %zu!", tc); 760 return -1; 761 } 762 for (i = 0; i < port_cfg->tc[tc].pcps; ++i) { 763 priv->qos_tbl_params.pcp_cos_map[ 764 port_cfg->tc[tc].pcp[i]].tc = tc; 765 } 766 } 767 768 /* 769 * The same logic goes with DSCP. 770 * First, set all map elements to "default". 771 */ 772 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i) 773 priv->qos_tbl_params.dscp_cos_map[i].tc = 774 port_cfg->default_tc; 775 776 /* Fill in all known values. */ 777 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) { 778 if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) { 779 /* Better safe than sorry. */ 780 MRVL_LOG(ERR, 781 "Too many DSCPs configured in TC %zu!", tc); 782 return -1; 783 } 784 for (i = 0; i < port_cfg->tc[tc].dscps; ++i) { 785 priv->qos_tbl_params.dscp_cos_map[ 786 port_cfg->tc[tc].dscp[i]].tc = tc; 787 } 788 } 789 790 /* 791 * Surprisingly, similar logic goes with queue mapping. 792 * We need only to store qid->tc mapping, 793 * to know TC when queue is read. 794 */ 795 for (i = 0; i < RTE_DIM(priv->rxq_map); ++i) 796 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC; 797 798 /* Set up DPDKq->(TC,inq) mapping. */ 799 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) { 800 if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) { 801 /* Overflow. */ 802 MRVL_LOG(ERR, 803 "Too many RX queues configured per TC %zu!", 804 tc); 805 return -1; 806 } 807 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) { 808 uint8_t idx = port_cfg->tc[tc].inq[i]; 809 810 if (idx > RTE_DIM(priv->rxq_map)) { 811 MRVL_LOG(ERR, "Bad queue index %d!", idx); 812 return -1; 813 } 814 815 priv->rxq_map[idx].tc = tc; 816 priv->rxq_map[idx].inq = i; 817 } 818 } 819 820 /* 821 * Set up TC configuration. TCs need to be sequenced: 0, 1, 2 822 * with no gaps. Empty TC means end of processing. 823 */ 824 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) { 825 if (port_cfg->tc[i].inqs == 0) 826 break; 827 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i], 828 port_cfg->tc[i].inqs, 829 priv->bpool, port_cfg->tc[i].color); 830 } 831 832 priv->ppio_params.inqs_params.num_tcs = i; 833 834 if (port_cfg->setup_policer) 835 return setup_policer(priv, &port_cfg->policer_params); 836 837 return 0; 838 } 839 840 /** 841 * Configure TX Queues in a given port. 842 * 843 * Sets up TX queues egress scheduler and limiter. 844 * 845 * @param priv Port's private data 846 * @param portid DPDK port ID 847 * @param max_queues Maximum number of queues to configure. 848 * @returns 0 in case of success, negative value otherwise. 849 */ 850 int 851 mrvl_configure_txqs(struct mrvl_priv *priv, uint16_t portid, 852 uint16_t max_queues) 853 { 854 /* We need only a subset of configuration. */ 855 struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid]; 856 int i; 857 858 if (mrvl_qos_cfg == NULL) 859 return 0; 860 861 priv->ppio_params.rate_limit_enable = port_cfg->rate_limit_enable; 862 if (port_cfg->rate_limit_enable) 863 priv->ppio_params.rate_limit_params = 864 port_cfg->rate_limit_params; 865 866 for (i = 0; i < max_queues; i++) { 867 struct pp2_ppio_outq_params *params = 868 &priv->ppio_params.outqs_params.outqs_params[i]; 869 870 params->sched_mode = port_cfg->outq[i].sched_mode; 871 params->weight = port_cfg->outq[i].weight; 872 params->rate_limit_enable = port_cfg->outq[i].rate_limit_enable; 873 params->rate_limit_params = port_cfg->outq[i].rate_limit_params; 874 } 875 876 return 0; 877 } 878 879 /** 880 * Start QoS mapping. 881 * 882 * Finalize QoS table configuration and initialize it in SDK. It can be done 883 * only after port is started, so we have a valid ppio reference. 884 * 885 * @param priv Port's private (configuration) data. 886 * @returns 0 in case of success, exits otherwise. 887 */ 888 int 889 mrvl_start_qos_mapping(struct mrvl_priv *priv) 890 { 891 size_t i; 892 893 if (priv->ppio == NULL) { 894 MRVL_LOG(ERR, "ppio must not be NULL here!"); 895 return -1; 896 } 897 898 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i) 899 priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio; 900 901 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i) 902 priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio; 903 904 /* Initialize Classifier QoS table. */ 905 906 return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl); 907 } 908