/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2017 Marvell International Ltd. * Copyright(c) 2017 Semihalf. * All rights reserved. */ #include #include #include #include #include #include #include #include #include /* Unluckily, container_of is defined by both DPDK and MUSDK, * we'll declare only one version. * * Note that it is not used in this PMD anyway. */ #ifdef container_of #undef container_of #endif #include "mrvl_qos.h" /* Parsing tokens. Defined conveniently, so that any correction is easy. */ #define MRVL_TOK_DEFAULT "default" #define MRVL_TOK_DEFAULT_TC "default_tc" #define MRVL_TOK_DSCP "dscp" #define MRVL_TOK_MAPPING_PRIORITY "mapping_priority" #define MRVL_TOK_IP "ip" #define MRVL_TOK_IP_VLAN "ip/vlan" #define MRVL_TOK_PCP "pcp" #define MRVL_TOK_PORT "port" #define MRVL_TOK_RXQ "rxq" #define MRVL_TOK_TC "tc" #define MRVL_TOK_TXQ "txq" #define MRVL_TOK_VLAN "vlan" #define MRVL_TOK_VLAN_IP "vlan/ip" /* egress specific configuration tokens */ #define MRVL_TOK_BURST_SIZE "burst_size" #define MRVL_TOK_RATE_LIMIT "rate_limit" #define MRVL_TOK_RATE_LIMIT_ENABLE "rate_limit_enable" #define MRVL_TOK_SCHED_MODE "sched_mode" #define MRVL_TOK_SCHED_MODE_SP "sp" #define MRVL_TOK_SCHED_MODE_WRR "wrr" #define MRVL_TOK_WRR_WEIGHT "wrr_weight" /* policer specific configuration tokens */ #define MRVL_TOK_PLCR_ENABLE "policer_enable" #define MRVL_TOK_PLCR_UNIT "token_unit" #define MRVL_TOK_PLCR_UNIT_BYTES "bytes" #define MRVL_TOK_PLCR_UNIT_PACKETS "packets" #define MRVL_TOK_PLCR_COLOR "color_mode" #define MRVL_TOK_PLCR_COLOR_BLIND "blind" #define MRVL_TOK_PLCR_COLOR_AWARE "aware" #define MRVL_TOK_PLCR_CIR "cir" #define MRVL_TOK_PLCR_CBS "cbs" #define MRVL_TOK_PLCR_EBS "ebs" #define MRVL_TOK_PLCR_DEFAULT_COLOR "default_color" #define MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN "green" #define MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW "yellow" #define MRVL_TOK_PLCR_DEFAULT_COLOR_RED "red" /** Number of tokens in range a-b = 2. */ #define MAX_RNG_TOKENS 2 /** Maximum possible value of PCP. */ #define MAX_PCP 7 /** Maximum possible value of DSCP. */ #define MAX_DSCP 63 /** Global QoS configuration. */ struct mrvl_qos_cfg *mrvl_qos_cfg; /** * Convert string to uint32_t with extra checks for result correctness. * * @param string String to convert. * @param val Conversion result. * @returns 0 in case of success, negative value otherwise. */ static int get_val_securely(const char *string, uint32_t *val) { char *endptr; size_t len = strlen(string); if (len == 0) return -1; errno = 0; *val = strtoul(string, &endptr, 0); if (errno != 0 || RTE_PTR_DIFF(endptr, string) != len) return -2; return 0; } /** * Read out-queue configuration from file. * * @param file Path to the configuration file. * @param port Port number. * @param outq Out queue number. * @param cfg Pointer to the Marvell QoS configuration structure. * @returns 0 in case of success, negative value otherwise. */ static int get_outq_cfg(struct rte_cfgfile *file, int port, int outq, struct mrvl_qos_cfg *cfg) { char sec_name[32]; const char *entry; uint32_t val; snprintf(sec_name, sizeof(sec_name), "%s %d %s %d", MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq); /* Skip non-existing */ if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0) return 0; /* Read scheduling mode */ entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_SCHED_MODE); if (entry) { if (!strncmp(entry, MRVL_TOK_SCHED_MODE_SP, strlen(MRVL_TOK_SCHED_MODE_SP))) { cfg->port[port].outq[outq].sched_mode = PP2_PPIO_SCHED_M_SP; } else if (!strncmp(entry, MRVL_TOK_SCHED_MODE_WRR, strlen(MRVL_TOK_SCHED_MODE_WRR))) { cfg->port[port].outq[outq].sched_mode = PP2_PPIO_SCHED_M_WRR; } else { MRVL_LOG(ERR, "Unknown token: %s", entry); return -1; } } /* Read wrr weight */ if (cfg->port[port].outq[outq].sched_mode == PP2_PPIO_SCHED_M_WRR) { entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_WRR_WEIGHT); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; cfg->port[port].outq[outq].weight = val; } } /* * There's no point in setting rate limiting for specific outq as * global port rate limiting has priority. */ if (cfg->port[port].rate_limit_enable) { MRVL_LOG(WARNING, "Port %d rate limiting already enabled", port); return 0; } entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RATE_LIMIT_ENABLE); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; cfg->port[port].outq[outq].rate_limit_enable = val; } if (!cfg->port[port].outq[outq].rate_limit_enable) return 0; /* Read CBS (in kB) */ entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_BURST_SIZE); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; cfg->port[port].outq[outq].rate_limit_params.cbs = val; } /* Read CIR (in kbps) */ entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RATE_LIMIT); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; cfg->port[port].outq[outq].rate_limit_params.cir = val; } return 0; } /** * Gets multiple-entry values and places them in table. * * Entry can be anything, e.g. "1 2-3 5 6 7-9". This needs to be converted to * table entries, respectively: {1, 2, 3, 5, 6, 7, 8, 9}. * As all result table's elements are always 1-byte long, we * won't overcomplicate the function, but we'll keep API generic, * check if someone hasn't changed element size and make it simple * to extend to other sizes. * * This function is purely utilitary, it does not print any error, only returns * different error numbers. * * @param entry[in] Values string to parse. * @param tab[out] Results table. * @param elem_sz[in] Element size (in bytes). * @param max_elems[in] Number of results table elements available. * @param max val[in] Maximum value allowed. * @returns Number of correctly parsed elements in case of success. * @retval -1 Wrong element size. * @retval -2 More tokens than result table allows. * @retval -3 Wrong range syntax. * @retval -4 Wrong range values. * @retval -5 Maximum value exceeded. */ static int get_entry_values(const char *entry, uint8_t *tab, size_t elem_sz, uint8_t max_elems, uint8_t max_val) { /* There should not be more tokens than max elements. * Add 1 for error trap. */ char *tokens[max_elems + 1]; /* Begin, End + error trap = 3. */ char *rng_tokens[MAX_RNG_TOKENS + 1]; long beg, end; uint32_t token_val; int nb_tokens, nb_rng_tokens; int i; int values = 0; char val; char entry_cpy[CFG_VALUE_LEN]; if (elem_sz != 1) return -1; /* Copy the entry to safely use rte_strsplit(). */ strlcpy(entry_cpy, entry, RTE_DIM(entry_cpy)); /* * If there are more tokens than array size, rte_strsplit will * not return error, just array size. */ nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy), tokens, max_elems + 1, ' '); /* Quick check, will be refined later. */ if (nb_tokens > max_elems) return -2; for (i = 0; i < nb_tokens; ++i) { if (strchr(tokens[i], '-') != NULL) { /* * Split to begin and end tokens. * We want to catch error cases too, thus we leave * option for number of tokens to be more than 2. */ nb_rng_tokens = rte_strsplit(tokens[i], strlen(tokens[i]), rng_tokens, RTE_DIM(rng_tokens), '-'); if (nb_rng_tokens != 2) return -3; /* Range and sanity checks. */ if (get_val_securely(rng_tokens[0], &token_val) < 0) return -4; beg = (char)token_val; if (get_val_securely(rng_tokens[1], &token_val) < 0) return -4; end = (char)token_val; if (beg < 0 || beg > UCHAR_MAX || end < 0 || end > UCHAR_MAX || end < beg) return -4; for (val = beg; val <= end; ++val) { if (val > max_val) return -5; *tab = val; tab = RTE_PTR_ADD(tab, elem_sz); ++values; if (values >= max_elems) return -2; } } else { /* Single values. */ if (get_val_securely(tokens[i], &token_val) < 0) return -5; val = (char)token_val; if (val > max_val) return -5; *tab = val; tab = RTE_PTR_ADD(tab, elem_sz); ++values; if (values >= max_elems) return -2; } } return values; } /** * Parse Traffic Class'es mapping configuration. * * @param file Config file handle. * @param port Which port to look for. * @param tc Which Traffic Class to look for. * @param cfg[out] Parsing results. * @returns 0 in case of success, negative value otherwise. */ static int parse_tc_cfg(struct rte_cfgfile *file, int port, int tc, struct mrvl_qos_cfg *cfg) { char sec_name[32]; const char *entry; int n; snprintf(sec_name, sizeof(sec_name), "%s %d %s %d", MRVL_TOK_PORT, port, MRVL_TOK_TC, tc); /* Skip non-existing */ if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0) return 0; entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ); if (entry) { n = get_entry_values(entry, cfg->port[port].tc[tc].inq, sizeof(cfg->port[port].tc[tc].inq[0]), RTE_DIM(cfg->port[port].tc[tc].inq), MRVL_PP2_RXQ_MAX); if (n < 0) { MRVL_LOG(ERR, "Error %d while parsing: %s", n, entry); return n; } cfg->port[port].tc[tc].inqs = n; } entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP); if (entry) { n = get_entry_values(entry, cfg->port[port].tc[tc].pcp, sizeof(cfg->port[port].tc[tc].pcp[0]), RTE_DIM(cfg->port[port].tc[tc].pcp), MAX_PCP); if (n < 0) { MRVL_LOG(ERR, "Error %d while parsing: %s", n, entry); return n; } cfg->port[port].tc[tc].pcps = n; } entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP); if (entry) { n = get_entry_values(entry, cfg->port[port].tc[tc].dscp, sizeof(cfg->port[port].tc[tc].dscp[0]), RTE_DIM(cfg->port[port].tc[tc].dscp), MAX_DSCP); if (n < 0) { MRVL_LOG(ERR, "Error %d while parsing: %s", n, entry); return n; } cfg->port[port].tc[tc].dscps = n; } entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_DEFAULT_COLOR); if (entry) { if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN, sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN))) { cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_GREEN; } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW, sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW))) { cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_YELLOW; } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_RED, sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_RED))) { cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_RED; } else { MRVL_LOG(ERR, "Error while parsing: %s", entry); return -1; } } return 0; } /** * Parse QoS configuration - rte_kvargs_process handler. * * Opens configuration file and parses its content. * * @param key Unused. * @param path Path to config file. * @param extra_args Pointer to configuration structure. * @returns 0 in case of success, exits otherwise. */ int mrvl_get_qoscfg(const char *key __rte_unused, const char *path, void *extra_args) { struct mrvl_qos_cfg **cfg = extra_args; struct rte_cfgfile *file = rte_cfgfile_load(path, 0); uint32_t val; int n, i, ret; const char *entry; char sec_name[32]; if (file == NULL) rte_exit(EXIT_FAILURE, "Cannot load configuration %s\n", path); /* Create configuration. This is never accessed on the fast path, * so we can ignore socket. */ *cfg = rte_zmalloc("mrvl_qos_cfg", sizeof(struct mrvl_qos_cfg), 0); if (*cfg == NULL) rte_exit(EXIT_FAILURE, "Cannot allocate configuration %s\n", path); n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT, sizeof(MRVL_TOK_PORT) - 1); if (n == 0) { /* This is weird, but not bad. */ MRVL_LOG(WARNING, "Empty configuration file?"); return 0; } /* Use the number of ports given as vdev parameters. */ for (n = 0; n < (PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC); ++n) { snprintf(sec_name, sizeof(sec_name), "%s %d %s", MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT); /* Skip ports non-existing in configuration. */ if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0) { (*cfg)->port[n].use_global_defaults = 1; (*cfg)->port[n].mapping_priority = PP2_CLS_QOS_TBL_VLAN_IP_PRI; continue; } entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DEFAULT_TC); if (entry) { if (get_val_securely(entry, &val) < 0 || val > USHRT_MAX) return -1; (*cfg)->port[n].default_tc = (uint8_t)val; } else { MRVL_LOG(ERR, "Default Traffic Class required in custom configuration!"); return -1; } entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_ENABLE); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; (*cfg)->port[n].policer_enable = val; } if ((*cfg)->port[n].policer_enable) { enum pp2_cls_plcr_token_unit unit; /* Read policer token unit */ entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_UNIT); if (entry) { if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_BYTES, sizeof(MRVL_TOK_PLCR_UNIT_BYTES))) { unit = PP2_CLS_PLCR_BYTES_TOKEN_UNIT; } else if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_PACKETS, sizeof(MRVL_TOK_PLCR_UNIT_PACKETS))) { unit = PP2_CLS_PLCR_PACKETS_TOKEN_UNIT; } else { MRVL_LOG(ERR, "Unknown token: %s", entry); return -1; } (*cfg)->port[n].policer_params.token_unit = unit; } /* Read policer color mode */ entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_COLOR); if (entry) { enum pp2_cls_plcr_color_mode mode; if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_BLIND, sizeof(MRVL_TOK_PLCR_COLOR_BLIND))) { mode = PP2_CLS_PLCR_COLOR_BLIND_MODE; } else if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_AWARE, sizeof(MRVL_TOK_PLCR_COLOR_AWARE))) { mode = PP2_CLS_PLCR_COLOR_AWARE_MODE; } else { MRVL_LOG(ERR, "Error in parsing: %s", entry); return -1; } (*cfg)->port[n].policer_params.color_mode = mode; } /* Read policer cir */ entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CIR); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; (*cfg)->port[n].policer_params.cir = val; } /* Read policer cbs */ entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CBS); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; (*cfg)->port[n].policer_params.cbs = val; } /* Read policer ebs */ entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_EBS); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; (*cfg)->port[n].policer_params.ebs = val; } } /* * Read per-port rate limiting. Setting that will * disable per-queue rate limiting. */ entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RATE_LIMIT_ENABLE); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; (*cfg)->port[n].rate_limit_enable = val; } if ((*cfg)->port[n].rate_limit_enable) { entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_BURST_SIZE); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; (*cfg)->port[n].rate_limit_params.cbs = val; } entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RATE_LIMIT); if (entry) { if (get_val_securely(entry, &val) < 0) return -1; (*cfg)->port[n].rate_limit_params.cir = val; } } entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_MAPPING_PRIORITY); if (entry) { if (!strncmp(entry, MRVL_TOK_VLAN_IP, sizeof(MRVL_TOK_VLAN_IP))) (*cfg)->port[n].mapping_priority = PP2_CLS_QOS_TBL_VLAN_IP_PRI; else if (!strncmp(entry, MRVL_TOK_IP_VLAN, sizeof(MRVL_TOK_IP_VLAN))) (*cfg)->port[n].mapping_priority = PP2_CLS_QOS_TBL_IP_VLAN_PRI; else if (!strncmp(entry, MRVL_TOK_IP, sizeof(MRVL_TOK_IP))) (*cfg)->port[n].mapping_priority = PP2_CLS_QOS_TBL_IP_PRI; else if (!strncmp(entry, MRVL_TOK_VLAN, sizeof(MRVL_TOK_VLAN))) (*cfg)->port[n].mapping_priority = PP2_CLS_QOS_TBL_VLAN_PRI; else rte_exit(EXIT_FAILURE, "Error in parsing %s value (%s)!\n", MRVL_TOK_MAPPING_PRIORITY, entry); } else { (*cfg)->port[n].mapping_priority = PP2_CLS_QOS_TBL_VLAN_IP_PRI; } for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) { ret = get_outq_cfg(file, n, i, *cfg); if (ret < 0) rte_exit(EXIT_FAILURE, "Error %d parsing port %d outq %d!\n", ret, n, i); } for (i = 0; i < MRVL_PP2_TC_MAX; ++i) { ret = parse_tc_cfg(file, n, i, *cfg); if (ret < 0) rte_exit(EXIT_FAILURE, "Error %d parsing port %d tc %d!\n", ret, n, i); } } return 0; } /** * Setup Traffic Class. * * Fill in TC parameters in single MUSDK TC config entry. * @param param TC parameters entry. * @param inqs Number of MUSDK in-queues in this TC. * @param bpool Bpool for this TC. * @param color Default color for this TC. * @returns 0 in case of success, exits otherwise. */ static int setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs, struct pp2_bpool *bpool, enum pp2_ppio_color color) { struct pp2_ppio_inq_params *inq_params; param->pkt_offset = MRVL_PKT_OFFS; param->pools[0] = bpool; param->default_color = color; inq_params = rte_zmalloc_socket("inq_params", inqs * sizeof(*inq_params), 0, rte_socket_id()); if (!inq_params) return -ENOMEM; param->num_in_qs = inqs; /* Release old config if necessary. */ if (param->inqs_params) rte_free(param->inqs_params); param->inqs_params = inq_params; return 0; } /** * Setup ingress policer. * * @param priv Port's private data. * @param params Pointer to the policer's configuration. * @returns 0 in case of success, negative values otherwise. */ static int setup_policer(struct mrvl_priv *priv, struct pp2_cls_plcr_params *params) { char match[16]; int ret; snprintf(match, sizeof(match), "policer-%d:%d\n", priv->pp_id, priv->ppio_id); params->match = match; ret = pp2_cls_plcr_init(params, &priv->policer); if (ret) { MRVL_LOG(ERR, "Failed to setup %s", match); return -1; } priv->ppio_params.inqs_params.plcr = priv->policer; return 0; } /** * Configure RX Queues in a given port. * * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping. * * @param priv Port's private data * @param portid DPDK port ID * @param max_queues Maximum number of queues to configure. * @returns 0 in case of success, negative value otherwise. */ int mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid, uint16_t max_queues) { size_t i, tc; if (mrvl_qos_cfg == NULL || mrvl_qos_cfg->port[portid].use_global_defaults) { /* * No port configuration, use default: 1 TC, no QoS, * TC color set to green. */ priv->ppio_params.inqs_params.num_tcs = 1; setup_tc(&priv->ppio_params.inqs_params.tcs_params[0], max_queues, priv->bpool, PP2_PPIO_COLOR_GREEN); /* Direct mapping of queues i.e. 0->0, 1->1 etc. */ for (i = 0; i < max_queues; ++i) { priv->rxq_map[i].tc = 0; priv->rxq_map[i].inq = i; } return 0; } /* We need only a subset of configuration. */ struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid]; priv->qos_tbl_params.type = port_cfg->mapping_priority; /* * We need to reverse mapping, from tc->pcp (better from usability * point of view) to pcp->tc (configurable in MUSDK). * First, set all map elements to "default". */ for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i) priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc; /* Then, fill in all known values. */ for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) { if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) { /* Better safe than sorry. */ MRVL_LOG(ERR, "Too many PCPs configured in TC %zu!", tc); return -1; } for (i = 0; i < port_cfg->tc[tc].pcps; ++i) { priv->qos_tbl_params.pcp_cos_map[ port_cfg->tc[tc].pcp[i]].tc = tc; } } /* * The same logic goes with DSCP. * First, set all map elements to "default". */ for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i) priv->qos_tbl_params.dscp_cos_map[i].tc = port_cfg->default_tc; /* Fill in all known values. */ for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) { if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) { /* Better safe than sorry. */ MRVL_LOG(ERR, "Too many DSCPs configured in TC %zu!", tc); return -1; } for (i = 0; i < port_cfg->tc[tc].dscps; ++i) { priv->qos_tbl_params.dscp_cos_map[ port_cfg->tc[tc].dscp[i]].tc = tc; } } /* * Surprisingly, similar logic goes with queue mapping. * We need only to store qid->tc mapping, * to know TC when queue is read. */ for (i = 0; i < RTE_DIM(priv->rxq_map); ++i) priv->rxq_map[i].tc = MRVL_UNKNOWN_TC; /* Set up DPDKq->(TC,inq) mapping. */ for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) { if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) { /* Overflow. */ MRVL_LOG(ERR, "Too many RX queues configured per TC %zu!", tc); return -1; } for (i = 0; i < port_cfg->tc[tc].inqs; ++i) { uint8_t idx = port_cfg->tc[tc].inq[i]; if (idx > RTE_DIM(priv->rxq_map)) { MRVL_LOG(ERR, "Bad queue index %d!", idx); return -1; } priv->rxq_map[idx].tc = tc; priv->rxq_map[idx].inq = i; } } /* * Set up TC configuration. TCs need to be sequenced: 0, 1, 2 * with no gaps. Empty TC means end of processing. */ for (i = 0; i < MRVL_PP2_TC_MAX; ++i) { if (port_cfg->tc[i].inqs == 0) break; setup_tc(&priv->ppio_params.inqs_params.tcs_params[i], port_cfg->tc[i].inqs, priv->bpool, port_cfg->tc[i].color); } priv->ppio_params.inqs_params.num_tcs = i; if (port_cfg->policer_enable) return setup_policer(priv, &port_cfg->policer_params); return 0; } /** * Configure TX Queues in a given port. * * Sets up TX queues egress scheduler and limiter. * * @param priv Port's private data * @param portid DPDK port ID * @param max_queues Maximum number of queues to configure. * @returns 0 in case of success, negative value otherwise. */ int mrvl_configure_txqs(struct mrvl_priv *priv, uint16_t portid, uint16_t max_queues) { /* We need only a subset of configuration. */ struct port_cfg *port_cfg = &mrvl_qos_cfg->port[portid]; int i; if (mrvl_qos_cfg == NULL) return 0; priv->ppio_params.rate_limit_enable = port_cfg->rate_limit_enable; if (port_cfg->rate_limit_enable) priv->ppio_params.rate_limit_params = port_cfg->rate_limit_params; for (i = 0; i < max_queues; i++) { struct pp2_ppio_outq_params *params = &priv->ppio_params.outqs_params.outqs_params[i]; params->sched_mode = port_cfg->outq[i].sched_mode; params->weight = port_cfg->outq[i].weight; params->rate_limit_enable = port_cfg->outq[i].rate_limit_enable; params->rate_limit_params = port_cfg->outq[i].rate_limit_params; } return 0; } /** * Start QoS mapping. * * Finalize QoS table configuration and initialize it in SDK. It can be done * only after port is started, so we have a valid ppio reference. * * @param priv Port's private (configuration) data. * @returns 0 in case of success, exits otherwise. */ int mrvl_start_qos_mapping(struct mrvl_priv *priv) { size_t i; if (priv->ppio == NULL) { MRVL_LOG(ERR, "ppio must not be NULL here!"); return -1; } for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i) priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio; for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i) priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio; /* Initialize Classifier QoS table. */ return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl); }