a12daff4d1
Instead of using getopt_long return value, strcmp was used to compare the input parameters with the struct option array. This patch get rid of all those strcmp by directly binding each longopt with an int enum. This is to improve readability and consistency in all examples. Bugzilla ID: 238 Reported-by: David Marchand <david.marchand@redhat.com> Signed-off-by: Ibtisam Tariq <ibtisam.tariq@emumba.com> Reviewed-by: David Marchand <david.marchand@redhat.com>
476 lines
12 KiB
C
476 lines
12 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2010-2014 Intel Corporation
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <locale.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include <getopt.h>
|
|
|
|
#include <rte_log.h>
|
|
#include <rte_eal.h>
|
|
#include <rte_lcore.h>
|
|
#include <rte_string_fns.h>
|
|
|
|
#include "main.h"
|
|
|
|
#define APP_NAME "qos_sched"
|
|
#define MAX_OPT_VALUES 8
|
|
#define SYS_CPU_DIR "/sys/devices/system/cpu/cpu%u/topology/"
|
|
|
|
static uint32_t app_main_core = 1;
|
|
static uint32_t app_numa_mask;
|
|
static uint64_t app_used_core_mask = 0;
|
|
static uint64_t app_used_port_mask = 0;
|
|
static uint64_t app_used_rx_port_mask = 0;
|
|
static uint64_t app_used_tx_port_mask = 0;
|
|
|
|
|
|
static const char usage[] =
|
|
" \n"
|
|
" %s <APP PARAMS> \n"
|
|
" \n"
|
|
"Application mandatory parameters: \n"
|
|
" --pfc \"RX PORT, TX PORT, RX LCORE, WT LCORE\" : Packet flow configuration \n"
|
|
" multiple pfc can be configured in command line \n"
|
|
" \n"
|
|
"Application optional parameters: \n"
|
|
" -i : run in interactive mode (default value is %u) \n"
|
|
" --mnc I : main core index (default value is %u) \n"
|
|
" --rsz \"A, B, C\" : Ring sizes \n"
|
|
" A = Size (in number of buffer descriptors) of each of the NIC RX \n"
|
|
" rings read by the I/O RX lcores (default value is %u) \n"
|
|
" B = Size (in number of elements) of each of the SW rings used by the\n"
|
|
" I/O RX lcores to send packets to worker lcores (default value is\n"
|
|
" %u) \n"
|
|
" C = Size (in number of buffer descriptors) of each of the NIC TX \n"
|
|
" rings written by worker lcores (default value is %u) \n"
|
|
" --bsz \"A, B, C, D\": Burst sizes \n"
|
|
" A = I/O RX lcore read burst size from NIC RX (default value is %u) \n"
|
|
" B = I/O RX lcore write burst size to output SW rings, \n"
|
|
" Worker lcore read burst size from input SW rings, \n"
|
|
" QoS enqueue size (default value is %u) \n"
|
|
" C = QoS dequeue size (default value is %u) \n"
|
|
" D = Worker lcore write burst size to NIC TX (default value is %u) \n"
|
|
" --msz M : Mempool size (in number of mbufs) for each pfc (default %u) \n"
|
|
" --rth \"A, B, C\" : RX queue threshold parameters \n"
|
|
" A = RX prefetch threshold (default value is %u) \n"
|
|
" B = RX host threshold (default value is %u) \n"
|
|
" C = RX write-back threshold (default value is %u) \n"
|
|
" --tth \"A, B, C\" : TX queue threshold parameters \n"
|
|
" A = TX prefetch threshold (default value is %u) \n"
|
|
" B = TX host threshold (default value is %u) \n"
|
|
" C = TX write-back threshold (default value is %u) \n"
|
|
" --cfg FILE : profile configuration to load \n"
|
|
;
|
|
|
|
/* display usage */
|
|
static void
|
|
app_usage(const char *prgname)
|
|
{
|
|
printf(usage, prgname, APP_INTERACTIVE_DEFAULT, app_main_core,
|
|
APP_RX_DESC_DEFAULT, APP_RING_SIZE, APP_TX_DESC_DEFAULT,
|
|
MAX_PKT_RX_BURST, PKT_ENQUEUE, PKT_DEQUEUE,
|
|
MAX_PKT_TX_BURST, NB_MBUF,
|
|
RX_PTHRESH, RX_HTHRESH, RX_WTHRESH,
|
|
TX_PTHRESH, TX_HTHRESH, TX_WTHRESH
|
|
);
|
|
}
|
|
|
|
|
|
/* returns core mask used by DPDK */
|
|
static uint64_t
|
|
app_eal_core_mask(void)
|
|
{
|
|
uint64_t cm = 0;
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < APP_MAX_LCORE; i++) {
|
|
if (rte_lcore_has_role(i, ROLE_RTE))
|
|
cm |= (1ULL << i);
|
|
}
|
|
|
|
cm |= (1ULL << rte_get_main_lcore());
|
|
|
|
return cm;
|
|
}
|
|
|
|
|
|
/* returns total number of cores presented in a system */
|
|
static uint32_t
|
|
app_cpu_core_count(void)
|
|
{
|
|
int i, len;
|
|
char path[PATH_MAX];
|
|
uint32_t ncores = 0;
|
|
|
|
for (i = 0; i < APP_MAX_LCORE; i++) {
|
|
len = snprintf(path, sizeof(path), SYS_CPU_DIR, i);
|
|
if (len <= 0 || (unsigned)len >= sizeof(path))
|
|
continue;
|
|
|
|
if (access(path, F_OK) == 0)
|
|
ncores++;
|
|
}
|
|
|
|
return ncores;
|
|
}
|
|
|
|
/* returns:
|
|
number of values parsed
|
|
-1 in case of error
|
|
*/
|
|
static int
|
|
app_parse_opt_vals(const char *conf_str, char separator, uint32_t n_vals, uint32_t *opt_vals)
|
|
{
|
|
char *string;
|
|
int i, n_tokens;
|
|
char *tokens[MAX_OPT_VALUES];
|
|
|
|
if (conf_str == NULL || opt_vals == NULL || n_vals == 0 || n_vals > MAX_OPT_VALUES)
|
|
return -1;
|
|
|
|
/* duplicate configuration string before splitting it to tokens */
|
|
string = strdup(conf_str);
|
|
if (string == NULL)
|
|
return -1;
|
|
|
|
n_tokens = rte_strsplit(string, strnlen(string, 32), tokens, n_vals, separator);
|
|
|
|
if (n_tokens > MAX_OPT_VALUES)
|
|
return -1;
|
|
|
|
for (i = 0; i < n_tokens; i++)
|
|
opt_vals[i] = (uint32_t)atol(tokens[i]);
|
|
|
|
free(string);
|
|
|
|
return n_tokens;
|
|
}
|
|
|
|
static int
|
|
app_parse_ring_conf(const char *conf_str)
|
|
{
|
|
int ret;
|
|
uint32_t vals[3];
|
|
|
|
ret = app_parse_opt_vals(conf_str, ',', 3, vals);
|
|
if (ret != 3)
|
|
return ret;
|
|
|
|
ring_conf.rx_size = vals[0];
|
|
ring_conf.ring_size = vals[1];
|
|
ring_conf.tx_size = vals[2];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
app_parse_rth_conf(const char *conf_str)
|
|
{
|
|
int ret;
|
|
uint32_t vals[3];
|
|
|
|
ret = app_parse_opt_vals(conf_str, ',', 3, vals);
|
|
if (ret != 3)
|
|
return ret;
|
|
|
|
rx_thresh.pthresh = (uint8_t)vals[0];
|
|
rx_thresh.hthresh = (uint8_t)vals[1];
|
|
rx_thresh.wthresh = (uint8_t)vals[2];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
app_parse_tth_conf(const char *conf_str)
|
|
{
|
|
int ret;
|
|
uint32_t vals[3];
|
|
|
|
ret = app_parse_opt_vals(conf_str, ',', 3, vals);
|
|
if (ret != 3)
|
|
return ret;
|
|
|
|
tx_thresh.pthresh = (uint8_t)vals[0];
|
|
tx_thresh.hthresh = (uint8_t)vals[1];
|
|
tx_thresh.wthresh = (uint8_t)vals[2];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
app_parse_flow_conf(const char *conf_str)
|
|
{
|
|
int ret;
|
|
uint32_t vals[5];
|
|
struct flow_conf *pconf;
|
|
uint64_t mask;
|
|
|
|
memset(vals, 0, sizeof(vals));
|
|
ret = app_parse_opt_vals(conf_str, ',', 6, vals);
|
|
if (ret < 4 || ret > 5)
|
|
return ret;
|
|
|
|
pconf = &qos_conf[nb_pfc];
|
|
|
|
pconf->rx_port = vals[0];
|
|
pconf->tx_port = vals[1];
|
|
pconf->rx_core = (uint8_t)vals[2];
|
|
pconf->wt_core = (uint8_t)vals[3];
|
|
if (ret == 5)
|
|
pconf->tx_core = (uint8_t)vals[4];
|
|
else
|
|
pconf->tx_core = pconf->wt_core;
|
|
|
|
if (pconf->rx_core == pconf->wt_core) {
|
|
RTE_LOG(ERR, APP, "pfc %u: rx thread and worker thread cannot share same core\n", nb_pfc);
|
|
return -1;
|
|
}
|
|
|
|
if (pconf->rx_port >= RTE_MAX_ETHPORTS) {
|
|
RTE_LOG(ERR, APP, "pfc %u: invalid rx port %"PRIu16" index\n",
|
|
nb_pfc, pconf->rx_port);
|
|
return -1;
|
|
}
|
|
if (pconf->tx_port >= RTE_MAX_ETHPORTS) {
|
|
RTE_LOG(ERR, APP, "pfc %u: invalid tx port %"PRIu16" index\n",
|
|
nb_pfc, pconf->tx_port);
|
|
return -1;
|
|
}
|
|
|
|
mask = 1lu << pconf->rx_port;
|
|
if (app_used_rx_port_mask & mask) {
|
|
RTE_LOG(ERR, APP, "pfc %u: rx port %"PRIu16" is used already\n",
|
|
nb_pfc, pconf->rx_port);
|
|
return -1;
|
|
}
|
|
app_used_rx_port_mask |= mask;
|
|
app_used_port_mask |= mask;
|
|
|
|
mask = 1lu << pconf->tx_port;
|
|
if (app_used_tx_port_mask & mask) {
|
|
RTE_LOG(ERR, APP, "pfc %u: port %"PRIu16" is used already\n",
|
|
nb_pfc, pconf->tx_port);
|
|
return -1;
|
|
}
|
|
app_used_tx_port_mask |= mask;
|
|
app_used_port_mask |= mask;
|
|
|
|
mask = 1lu << pconf->rx_core;
|
|
app_used_core_mask |= mask;
|
|
|
|
mask = 1lu << pconf->wt_core;
|
|
app_used_core_mask |= mask;
|
|
|
|
mask = 1lu << pconf->tx_core;
|
|
app_used_core_mask |= mask;
|
|
|
|
nb_pfc++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
app_parse_burst_conf(const char *conf_str)
|
|
{
|
|
int ret;
|
|
uint32_t vals[4];
|
|
|
|
ret = app_parse_opt_vals(conf_str, ',', 4, vals);
|
|
if (ret != 4)
|
|
return ret;
|
|
|
|
burst_conf.rx_burst = (uint16_t)vals[0];
|
|
burst_conf.ring_burst = (uint16_t)vals[1];
|
|
burst_conf.qos_dequeue = (uint16_t)vals[2];
|
|
burst_conf.tx_burst = (uint16_t)vals[3];
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum {
|
|
#define OPT_PFC "pfc"
|
|
OPT_PFC_NUM = 256,
|
|
#define OPT_MNC "mnc"
|
|
OPT_MNC_NUM,
|
|
#define OPT_RSZ "rsz"
|
|
OPT_RSZ_NUM,
|
|
#define OPT_BSZ "bsz"
|
|
OPT_BSZ_NUM,
|
|
#define OPT_MSZ "msz"
|
|
OPT_MSZ_NUM,
|
|
#define OPT_RTH "rth"
|
|
OPT_RTH_NUM,
|
|
#define OPT_TTH "tth"
|
|
OPT_TTH_NUM,
|
|
#define OPT_CFG "cfg"
|
|
OPT_CFG_NUM,
|
|
};
|
|
|
|
/*
|
|
* Parses the argument given in the command line of the application,
|
|
* calculates mask for used cores and initializes EAL with calculated core mask
|
|
*/
|
|
int
|
|
app_parse_args(int argc, char **argv)
|
|
{
|
|
int opt, ret;
|
|
int option_index;
|
|
char *prgname = argv[0];
|
|
uint32_t i, nb_lcores;
|
|
|
|
static struct option lgopts[] = {
|
|
{OPT_PFC, 1, NULL, OPT_PFC_NUM},
|
|
{OPT_MNC, 1, NULL, OPT_MNC_NUM},
|
|
{OPT_RSZ, 1, NULL, OPT_RSZ_NUM},
|
|
{OPT_BSZ, 1, NULL, OPT_BSZ_NUM},
|
|
{OPT_MSZ, 1, NULL, OPT_MSZ_NUM},
|
|
{OPT_RTH, 1, NULL, OPT_RTH_NUM},
|
|
{OPT_TTH, 1, NULL, OPT_TTH_NUM},
|
|
{OPT_CFG, 1, NULL, OPT_CFG_NUM},
|
|
{NULL, 0, 0, 0 }
|
|
};
|
|
|
|
/* initialize EAL first */
|
|
ret = rte_eal_init(argc, argv);
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
argc -= ret;
|
|
argv += ret;
|
|
|
|
/* set en_US locale to print big numbers with ',' */
|
|
setlocale(LC_NUMERIC, "en_US.utf-8");
|
|
|
|
while ((opt = getopt_long(argc, argv, "i",
|
|
lgopts, &option_index)) != EOF) {
|
|
|
|
switch (opt) {
|
|
case 'i':
|
|
printf("Interactive-mode selected\n");
|
|
interactive = 1;
|
|
break;
|
|
/* long options */
|
|
|
|
case OPT_PFC_NUM:
|
|
ret = app_parse_flow_conf(optarg);
|
|
if (ret) {
|
|
RTE_LOG(ERR, APP, "Invalid pipe configuration %s\n",
|
|
optarg);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case OPT_MNC_NUM:
|
|
app_main_core = (uint32_t)atoi(optarg);
|
|
break;
|
|
|
|
case OPT_RSZ_NUM:
|
|
ret = app_parse_ring_conf(optarg);
|
|
if (ret) {
|
|
RTE_LOG(ERR, APP, "Invalid ring configuration %s\n",
|
|
optarg);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case OPT_BSZ_NUM:
|
|
ret = app_parse_burst_conf(optarg);
|
|
if (ret) {
|
|
RTE_LOG(ERR, APP, "Invalid burst configuration %s\n",
|
|
optarg);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case OPT_MSZ_NUM:
|
|
mp_size = atoi(optarg);
|
|
if (mp_size <= 0) {
|
|
RTE_LOG(ERR, APP, "Invalid mempool size %s\n",
|
|
optarg);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case OPT_RTH_NUM:
|
|
ret = app_parse_rth_conf(optarg);
|
|
if (ret) {
|
|
RTE_LOG(ERR, APP, "Invalid RX threshold configuration %s\n",
|
|
optarg);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case OPT_TTH_NUM:
|
|
ret = app_parse_tth_conf(optarg);
|
|
if (ret) {
|
|
RTE_LOG(ERR, APP, "Invalid TX threshold configuration %s\n",
|
|
optarg);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case OPT_CFG_NUM:
|
|
cfg_profile = optarg;
|
|
break;
|
|
|
|
default:
|
|
app_usage(prgname);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* check main core index validity */
|
|
for (i = 0; i <= app_main_core; i++) {
|
|
if (app_used_core_mask & (1u << app_main_core)) {
|
|
RTE_LOG(ERR, APP, "Main core index is not configured properly\n");
|
|
app_usage(prgname);
|
|
return -1;
|
|
}
|
|
}
|
|
app_used_core_mask |= 1u << app_main_core;
|
|
|
|
if ((app_used_core_mask != app_eal_core_mask()) ||
|
|
(app_main_core != rte_get_main_lcore())) {
|
|
RTE_LOG(ERR, APP, "EAL core mask not configured properly, must be %" PRIx64
|
|
" instead of %" PRIx64 "\n" , app_used_core_mask, app_eal_core_mask());
|
|
return -1;
|
|
}
|
|
|
|
if (nb_pfc == 0) {
|
|
RTE_LOG(ERR, APP, "Packet flow not configured!\n");
|
|
app_usage(prgname);
|
|
return -1;
|
|
}
|
|
|
|
/* sanity check for cores assignment */
|
|
nb_lcores = app_cpu_core_count();
|
|
|
|
for(i = 0; i < nb_pfc; i++) {
|
|
if (qos_conf[i].rx_core >= nb_lcores) {
|
|
RTE_LOG(ERR, APP, "pfc %u: invalid RX lcore index %u\n", i + 1,
|
|
qos_conf[i].rx_core);
|
|
return -1;
|
|
}
|
|
if (qos_conf[i].wt_core >= nb_lcores) {
|
|
RTE_LOG(ERR, APP, "pfc %u: invalid WT lcore index %u\n", i + 1,
|
|
qos_conf[i].wt_core);
|
|
return -1;
|
|
}
|
|
uint32_t rx_sock = rte_lcore_to_socket_id(qos_conf[i].rx_core);
|
|
uint32_t wt_sock = rte_lcore_to_socket_id(qos_conf[i].wt_core);
|
|
if (rx_sock != wt_sock) {
|
|
RTE_LOG(ERR, APP, "pfc %u: RX and WT must be on the same socket\n", i + 1);
|
|
return -1;
|
|
}
|
|
app_numa_mask |= 1 << rte_lcore_to_socket_id(qos_conf[i].rx_core);
|
|
}
|
|
|
|
return 0;
|
|
}
|