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 cfg->port[port].use_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_qos_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 QoS configuration - rte_kvargs_process handler. 478 * 479 * Opens configuration file and parses its content. 480 * 481 * @param key Unused. 482 * @param path Path to config file. 483 * @param extra_args Pointer to configuration structure. 484 * @returns 0 in case of success, exits otherwise. 485 */ 486 int 487 mrvl_get_qoscfg(const char *key __rte_unused, const char *path, 488 void *extra_args) 489 { 490 struct mrvl_qos_cfg **cfg = extra_args; 491 struct rte_cfgfile *file = rte_cfgfile_load(path, 0); 492 uint32_t val; 493 int n, i, ret; 494 const char *entry; 495 char sec_name[32]; 496 497 if (file == NULL) 498 rte_exit(EXIT_FAILURE, "Cannot load configuration %s\n", path); 499 500 /* Create configuration. This is never accessed on the fast path, 501 * so we can ignore socket. 502 */ 503 *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0); 504 if (*cfg == NULL) 505 rte_exit(EXIT_FAILURE, "Cannot allocate configuration %s\n", 506 path); 507 508 n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT, 509 sizeof(MRVL_TOK_PORT) - 1); 510 511 if (n == 0) { 512 /* This is weird, but not bad. */ 513 MRVL_LOG(WARNING, "Empty configuration file?"); 514 return 0; 515 } 516 517 /* Use the number of ports given as vdev parameters. */ 518 for (n = 0; n < (PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC); ++n) { 519 snprintf(sec_name, sizeof(sec_name), "%s %d %s", 520 MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT); 521 522 /* Use global defaults, unless an override occurs */ 523 (*cfg)->port[n].use_global_defaults = 1; 524 525 /* Skip ports non-existing in configuration. */ 526 if (rte_cfgfile_num_sections(file, sec_name, 527 strlen(sec_name)) <= 0) { 528 continue; 529 } 530 531 /* 532 * Read per-port rate limiting. Setting that will 533 * disable per-queue rate limiting. 534 */ 535 entry = rte_cfgfile_get_entry(file, sec_name, 536 MRVL_TOK_RATE_LIMIT_ENABLE); 537 if (entry) { 538 if (get_val_securely(entry, &val) < 0) 539 return -1; 540 (*cfg)->port[n].rate_limit_enable = val; 541 } 542 543 if ((*cfg)->port[n].rate_limit_enable) { 544 entry = rte_cfgfile_get_entry(file, sec_name, 545 MRVL_TOK_BURST_SIZE); 546 if (entry) { 547 if (get_val_securely(entry, &val) < 0) 548 return -1; 549 (*cfg)->port[n].rate_limit_params.cbs = val; 550 } 551 552 entry = rte_cfgfile_get_entry(file, sec_name, 553 MRVL_TOK_RATE_LIMIT); 554 if (entry) { 555 if (get_val_securely(entry, &val) < 0) 556 return -1; 557 (*cfg)->port[n].rate_limit_params.cir = val; 558 } 559 } 560 561 entry = rte_cfgfile_get_entry(file, sec_name, 562 MRVL_TOK_MAPPING_PRIORITY); 563 if (entry) { 564 (*cfg)->port[n].use_global_defaults = 0; 565 if (!strncmp(entry, MRVL_TOK_VLAN_IP, 566 sizeof(MRVL_TOK_VLAN_IP))) 567 (*cfg)->port[n].mapping_priority = 568 PP2_CLS_QOS_TBL_VLAN_IP_PRI; 569 else if (!strncmp(entry, MRVL_TOK_IP_VLAN, 570 sizeof(MRVL_TOK_IP_VLAN))) 571 (*cfg)->port[n].mapping_priority = 572 PP2_CLS_QOS_TBL_IP_VLAN_PRI; 573 else if (!strncmp(entry, MRVL_TOK_IP, 574 sizeof(MRVL_TOK_IP))) 575 (*cfg)->port[n].mapping_priority = 576 PP2_CLS_QOS_TBL_IP_PRI; 577 else if (!strncmp(entry, MRVL_TOK_VLAN, 578 sizeof(MRVL_TOK_VLAN))) 579 (*cfg)->port[n].mapping_priority = 580 PP2_CLS_QOS_TBL_VLAN_PRI; 581 else 582 rte_exit(EXIT_FAILURE, 583 "Error in parsing %s value (%s)!\n", 584 MRVL_TOK_MAPPING_PRIORITY, entry); 585 } else { 586 (*cfg)->port[n].mapping_priority = 587 PP2_CLS_QOS_TBL_VLAN_IP_PRI; 588 } 589 590 /* Parse policer configuration (if any) */ 591 entry = rte_cfgfile_get_entry(file, sec_name, 592 MRVL_TOK_PLCR_DEFAULT); 593 if (entry) { 594 (*cfg)->port[n].use_global_defaults = 0; 595 if (get_val_securely(entry, &val) < 0) 596 return -1; 597 598 snprintf(sec_name, sizeof(sec_name), "%s %d", 599 MRVL_TOK_PLCR, val); 600 ret = parse_policer(file, n, sec_name, *cfg); 601 if (ret) 602 return -1; 603 } 604 605 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) { 606 ret = get_outq_cfg(file, n, i, *cfg); 607 if (ret < 0) 608 rte_exit(EXIT_FAILURE, 609 "Error %d parsing port %d outq %d!\n", 610 ret, n, i); 611 } 612 613 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) { 614 ret = parse_tc_cfg(file, n, i, *cfg); 615 if (ret < 0) 616 rte_exit(EXIT_FAILURE, 617 "Error %d parsing port %d tc %d!\n", 618 ret, n, i); 619 } 620 621 entry = rte_cfgfile_get_entry(file, sec_name, 622 MRVL_TOK_DEFAULT_TC); 623 if (entry) { 624 if (get_val_securely(entry, &val) < 0 || 625 val > USHRT_MAX) 626 return -1; 627 (*cfg)->port[n].default_tc = (uint8_t)val; 628 } else { 629 if ((*cfg)->port[n].use_global_defaults == 0) { 630 MRVL_LOG(ERR, 631 "Default Traffic Class required in custom configuration!"); 632 return -1; 633 } 634 } 635 } 636 637 return 0; 638 } 639 640 /** 641 * Setup Traffic Class. 642 * 643 * Fill in TC parameters in single MUSDK TC config entry. 644 * @param param TC parameters entry. 645 * @param inqs Number of MUSDK in-queues in this TC. 646 * @param bpool Bpool for this TC. 647 * @param color Default color for this TC. 648 * @returns 0 in case of success, exits otherwise. 649 */ 650 static int 651 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs, 652 struct pp2_bpool *bpool, enum pp2_ppio_color color) 653 { 654 struct pp2_ppio_inq_params *inq_params; 655 656 param->pkt_offset = MRVL_PKT_OFFS; 657 param->pools[0][0] = bpool; 658 param->default_color = color; 659 660 inq_params = rte_zmalloc_socket("inq_params", 661 inqs * sizeof(*inq_params), 662 0, rte_socket_id()); 663 if (!inq_params) 664 return -ENOMEM; 665 666 param->num_in_qs = inqs; 667 668 /* Release old config if necessary. */ 669 if (param->inqs_params) 670 rte_free(param->inqs_params); 671 672 param->inqs_params = inq_params; 673 674 return 0; 675 } 676 677 /** 678 * Setup ingress policer. 679 * 680 * @param priv Port's private data. 681 * @param params Pointer to the policer's configuration. 682 * @param plcr_id Policer id. 683 * @returns 0 in case of success, negative values otherwise. 684 */ 685 static int 686 setup_policer(struct mrvl_priv *priv, struct pp2_cls_plcr_params *params) 687 { 688 char match[16]; 689 int ret; 690 691 /* 692 * At this point no other policers are used which means 693 * any policer can be picked up and used as a default one. 694 * 695 * Lets use 0th then. 696 */ 697 sprintf(match, "policer-%d:%d\n", priv->pp_id, 0); 698 params->match = match; 699 700 ret = pp2_cls_plcr_init(params, &priv->default_policer); 701 if (ret) { 702 MRVL_LOG(ERR, "Failed to setup %s", match); 703 return -1; 704 } 705 706 priv->ppio_params.inqs_params.plcr = priv->default_policer; 707 priv->used_plcrs = BIT(0); 708 709 return 0; 710 } 711 712 /** 713 * Configure RX Queues in a given port. 714 * 715 * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping. 716 * 717 * @param priv Port's private data 718 * @param portid DPDK port ID 719 * @param max_queues Maximum number of queues to configure. 720 * @returns 0 in case of success, negative value otherwise. 721 */ 722 int 723 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid, 724 uint16_t max_queues) 725 { 726 size_t i, tc; 727 728 if (mrvl_qos_cfg == NULL || 729 mrvl_qos_cfg->port[portid].use_global_defaults) { 730 /* 731 * No port configuration, use default: 1 TC, no QoS, 732 * TC color set to green. 733 */ 734 priv->ppio_params.inqs_params.num_tcs = 1; 735 setup_tc(&priv->ppio_params.inqs_params.tcs_params[0], 736 max_queues, priv->bpool, PP2_PPIO_COLOR_GREEN); 737 738 /* Direct mapping of queues i.e. 0->0, 1->1 etc. */ 739 for (i = 0; i < max_queues; ++i) { 740 priv->rxq_map[i].tc = 0; 741 priv->rxq_map[i].inq = i; 742 } 743 return 0; 744 } 745 746 /* We need only a subset of configuration. */ 747 struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid]; 748 749 priv->qos_tbl_params.type = port_cfg->mapping_priority; 750 751 /* 752 * We need to reverse mapping, from tc->pcp (better from usability 753 * point of view) to pcp->tc (configurable in MUSDK). 754 * First, set all map elements to "default". 755 */ 756 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i) 757 priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc; 758 759 /* Then, fill in all known values. */ 760 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) { 761 if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) { 762 /* Better safe than sorry. */ 763 MRVL_LOG(ERR, 764 "Too many PCPs configured in TC %zu!", tc); 765 return -1; 766 } 767 for (i = 0; i < port_cfg->tc[tc].pcps; ++i) { 768 priv->qos_tbl_params.pcp_cos_map[ 769 port_cfg->tc[tc].pcp[i]].tc = tc; 770 } 771 } 772 773 /* 774 * The same logic goes with DSCP. 775 * First, set all map elements to "default". 776 */ 777 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i) 778 priv->qos_tbl_params.dscp_cos_map[i].tc = 779 port_cfg->default_tc; 780 781 /* Fill in all known values. */ 782 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) { 783 if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) { 784 /* Better safe than sorry. */ 785 MRVL_LOG(ERR, 786 "Too many DSCPs configured in TC %zu!", tc); 787 return -1; 788 } 789 for (i = 0; i < port_cfg->tc[tc].dscps; ++i) { 790 priv->qos_tbl_params.dscp_cos_map[ 791 port_cfg->tc[tc].dscp[i]].tc = tc; 792 } 793 } 794 795 /* 796 * Surprisingly, similar logic goes with queue mapping. 797 * We need only to store qid->tc mapping, 798 * to know TC when queue is read. 799 */ 800 for (i = 0; i < RTE_DIM(priv->rxq_map); ++i) 801 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC; 802 803 /* Set up DPDKq->(TC,inq) mapping. */ 804 for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) { 805 if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) { 806 /* Overflow. */ 807 MRVL_LOG(ERR, 808 "Too many RX queues configured per TC %zu!", 809 tc); 810 return -1; 811 } 812 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) { 813 uint8_t idx = port_cfg->tc[tc].inq[i]; 814 815 if (idx > RTE_DIM(priv->rxq_map)) { 816 MRVL_LOG(ERR, "Bad queue index %d!", idx); 817 return -1; 818 } 819 820 priv->rxq_map[idx].tc = tc; 821 priv->rxq_map[idx].inq = i; 822 } 823 } 824 825 /* 826 * Set up TC configuration. TCs need to be sequenced: 0, 1, 2 827 * with no gaps. Empty TC means end of processing. 828 */ 829 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) { 830 if (port_cfg->tc[i].inqs == 0) 831 break; 832 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i], 833 port_cfg->tc[i].inqs, 834 priv->bpool, port_cfg->tc[i].color); 835 } 836 837 priv->ppio_params.inqs_params.num_tcs = i; 838 839 if (port_cfg->setup_policer) 840 return setup_policer(priv, &port_cfg->policer_params); 841 842 return 0; 843 } 844 845 /** 846 * Configure TX Queues in a given port. 847 * 848 * Sets up TX queues egress scheduler and limiter. 849 * 850 * @param priv Port's private data 851 * @param portid DPDK port ID 852 * @param max_queues Maximum number of queues to configure. 853 * @returns 0 in case of success, negative value otherwise. 854 */ 855 int 856 mrvl_configure_txqs(struct mrvl_priv *priv, uint16_t portid, 857 uint16_t max_queues) 858 { 859 /* We need only a subset of configuration. */ 860 struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid]; 861 int i; 862 863 if (mrvl_qos_cfg == NULL) 864 return 0; 865 866 priv->ppio_params.rate_limit_enable = port_cfg->rate_limit_enable; 867 if (port_cfg->rate_limit_enable) 868 priv->ppio_params.rate_limit_params = 869 port_cfg->rate_limit_params; 870 871 for (i = 0; i < max_queues; i++) { 872 struct pp2_ppio_outq_params *params = 873 &priv->ppio_params.outqs_params.outqs_params[i]; 874 875 params->sched_mode = port_cfg->outq[i].sched_mode; 876 params->weight = port_cfg->outq[i].weight; 877 params->rate_limit_enable = port_cfg->outq[i].rate_limit_enable; 878 params->rate_limit_params = port_cfg->outq[i].rate_limit_params; 879 } 880 881 return 0; 882 } 883 884 /** 885 * Start QoS mapping. 886 * 887 * Finalize QoS table configuration and initialize it in SDK. It can be done 888 * only after port is started, so we have a valid ppio reference. 889 * 890 * @param priv Port's private (configuration) data. 891 * @returns 0 in case of success, exits otherwise. 892 */ 893 int 894 mrvl_start_qos_mapping(struct mrvl_priv *priv) 895 { 896 size_t i; 897 898 if (priv->ppio == NULL) { 899 MRVL_LOG(ERR, "ppio must not be NULL here!"); 900 return -1; 901 } 902 903 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i) 904 priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio; 905 906 for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i) 907 priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio; 908 909 /* Initialize Classifier QoS table. */ 910 911 return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl); 912 } 913