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