1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <stdlib.h> 8 #include <stdint.h> 9 #include <locale.h> 10 #include <unistd.h> 11 #include <limits.h> 12 #include <getopt.h> 13 14 #include <rte_log.h> 15 #include <rte_eal.h> 16 #include <rte_lcore.h> 17 #include <rte_string_fns.h> 18 19 #include "main.h" 20 21 #define APP_NAME "qos_sched" 22 #define MAX_OPT_VALUES 8 23 #define SYS_CPU_DIR "/sys/devices/system/cpu/cpu%u/topology/" 24 25 static uint32_t app_main_core = 1; 26 static uint32_t app_numa_mask; 27 static uint64_t app_used_port_mask = 0; 28 static uint64_t app_used_rx_port_mask = 0; 29 static uint64_t app_used_tx_port_mask = 0; 30 31 32 static const char usage[] = 33 " \n" 34 " %s <APP PARAMS> \n" 35 " \n" 36 "Application mandatory parameters: \n" 37 " --pfc \"RX PORT, TX PORT, RX LCORE, WT LCORE\" : Packet flow configuration \n" 38 " multiple pfc can be configured in command line \n" 39 " \n" 40 "Application optional parameters: \n" 41 " -i : run in interactive mode (default value is %u) \n" 42 " --mnc I : main core index (default value is %u) \n" 43 " --rsz \"A, B, C\" : Ring sizes \n" 44 " A = Size (in number of buffer descriptors) of each of the NIC RX \n" 45 " rings read by the I/O RX lcores (default value is %u) \n" 46 " B = Size (in number of elements) of each of the SW rings used by the\n" 47 " I/O RX lcores to send packets to worker lcores (default value is\n" 48 " %u) \n" 49 " C = Size (in number of buffer descriptors) of each of the NIC TX \n" 50 " rings written by worker lcores (default value is %u) \n" 51 " --bsz \"A, B, C, D\": Burst sizes \n" 52 " A = I/O RX lcore read burst size from NIC RX (default value is %u) \n" 53 " B = I/O RX lcore write burst size to output SW rings, \n" 54 " Worker lcore read burst size from input SW rings, \n" 55 " QoS enqueue size (default value is %u) \n" 56 " C = QoS dequeue size (default value is %u) \n" 57 " D = Worker lcore write burst size to NIC TX (default value is %u) \n" 58 " --msz M : Mempool size (in number of mbufs) for each pfc (default %u) \n" 59 " --rth \"A, B, C\" : RX queue threshold parameters \n" 60 " A = RX prefetch threshold (default value is %u) \n" 61 " B = RX host threshold (default value is %u) \n" 62 " C = RX write-back threshold (default value is %u) \n" 63 " --tth \"A, B, C\" : TX queue threshold parameters \n" 64 " A = TX prefetch threshold (default value is %u) \n" 65 " B = TX host threshold (default value is %u) \n" 66 " C = TX write-back threshold (default value is %u) \n" 67 " --cfg FILE : profile configuration to load \n" 68 ; 69 70 /* display usage */ 71 static void 72 app_usage(const char *prgname) 73 { 74 printf(usage, prgname, APP_INTERACTIVE_DEFAULT, app_main_core, 75 APP_RX_DESC_DEFAULT, APP_RING_SIZE, APP_TX_DESC_DEFAULT, 76 MAX_PKT_RX_BURST, PKT_ENQUEUE, PKT_DEQUEUE, 77 MAX_PKT_TX_BURST, NB_MBUF, 78 RX_PTHRESH, RX_HTHRESH, RX_WTHRESH, 79 TX_PTHRESH, TX_HTHRESH, TX_WTHRESH 80 ); 81 } 82 83 84 85 /* returns: 86 number of values parsed 87 -1 in case of error 88 */ 89 static int 90 app_parse_opt_vals(const char *conf_str, char separator, uint32_t n_vals, uint32_t *opt_vals) 91 { 92 char *string; 93 int i, n_tokens; 94 char *tokens[MAX_OPT_VALUES]; 95 96 if (conf_str == NULL || opt_vals == NULL || n_vals == 0 || n_vals > MAX_OPT_VALUES) 97 return -1; 98 99 /* duplicate configuration string before splitting it to tokens */ 100 string = strdup(conf_str); 101 if (string == NULL) 102 return -1; 103 104 n_tokens = rte_strsplit(string, strnlen(string, 32), tokens, n_vals, separator); 105 106 if (n_tokens > MAX_OPT_VALUES) { 107 free(string); 108 return -1; 109 } 110 111 for (i = 0; i < n_tokens; i++) 112 opt_vals[i] = (uint32_t)atol(tokens[i]); 113 114 free(string); 115 116 return n_tokens; 117 } 118 119 static int 120 app_parse_ring_conf(const char *conf_str) 121 { 122 int ret; 123 uint32_t vals[3]; 124 125 ret = app_parse_opt_vals(conf_str, ',', 3, vals); 126 if (ret != 3) 127 return ret; 128 129 ring_conf.rx_size = vals[0]; 130 ring_conf.ring_size = vals[1]; 131 ring_conf.tx_size = vals[2]; 132 133 return 0; 134 } 135 136 static int 137 app_parse_rth_conf(const char *conf_str) 138 { 139 int ret; 140 uint32_t vals[3]; 141 142 ret = app_parse_opt_vals(conf_str, ',', 3, vals); 143 if (ret != 3) 144 return ret; 145 146 rx_thresh.pthresh = (uint8_t)vals[0]; 147 rx_thresh.hthresh = (uint8_t)vals[1]; 148 rx_thresh.wthresh = (uint8_t)vals[2]; 149 150 return 0; 151 } 152 153 static int 154 app_parse_tth_conf(const char *conf_str) 155 { 156 int ret; 157 uint32_t vals[3]; 158 159 ret = app_parse_opt_vals(conf_str, ',', 3, vals); 160 if (ret != 3) 161 return ret; 162 163 tx_thresh.pthresh = (uint8_t)vals[0]; 164 tx_thresh.hthresh = (uint8_t)vals[1]; 165 tx_thresh.wthresh = (uint8_t)vals[2]; 166 167 return 0; 168 } 169 170 static int 171 app_parse_flow_conf(const char *conf_str) 172 { 173 int ret; 174 uint32_t vals[5]; 175 struct flow_conf *pconf; 176 uint64_t mask; 177 178 memset(vals, 0, sizeof(vals)); 179 ret = app_parse_opt_vals(conf_str, ',', 6, vals); 180 if (ret < 4 || ret > 5) 181 return ret; 182 183 pconf = &qos_conf[nb_pfc]; 184 185 pconf->rx_port = vals[0]; 186 pconf->tx_port = vals[1]; 187 pconf->rx_core = vals[2]; 188 pconf->wt_core = vals[3]; 189 if (ret == 5) 190 pconf->tx_core = vals[4]; 191 else 192 pconf->tx_core = pconf->wt_core; 193 194 if (pconf->rx_core == pconf->wt_core) { 195 RTE_LOG(ERR, APP, "pfc %u: rx thread and worker thread cannot share same core\n", nb_pfc); 196 return -1; 197 } 198 199 if (pconf->rx_port >= RTE_MAX_ETHPORTS) { 200 RTE_LOG(ERR, APP, "pfc %u: invalid rx port %"PRIu16" index\n", 201 nb_pfc, pconf->rx_port); 202 return -1; 203 } 204 if (pconf->tx_port >= RTE_MAX_ETHPORTS) { 205 RTE_LOG(ERR, APP, "pfc %u: invalid tx port %"PRIu16" index\n", 206 nb_pfc, pconf->tx_port); 207 return -1; 208 } 209 210 mask = 1lu << pconf->rx_port; 211 if (app_used_rx_port_mask & mask) { 212 RTE_LOG(ERR, APP, "pfc %u: rx port %"PRIu16" is used already\n", 213 nb_pfc, pconf->rx_port); 214 return -1; 215 } 216 app_used_rx_port_mask |= mask; 217 app_used_port_mask |= mask; 218 219 mask = 1lu << pconf->tx_port; 220 if (app_used_tx_port_mask & mask) { 221 RTE_LOG(ERR, APP, "pfc %u: port %"PRIu16" is used already\n", 222 nb_pfc, pconf->tx_port); 223 return -1; 224 } 225 app_used_tx_port_mask |= mask; 226 app_used_port_mask |= mask; 227 228 nb_pfc++; 229 230 return 0; 231 } 232 233 static int 234 app_parse_burst_conf(const char *conf_str) 235 { 236 int ret; 237 uint32_t vals[4]; 238 239 ret = app_parse_opt_vals(conf_str, ',', 4, vals); 240 if (ret != 4) 241 return ret; 242 243 burst_conf.rx_burst = (uint16_t)vals[0]; 244 burst_conf.ring_burst = (uint16_t)vals[1]; 245 burst_conf.qos_dequeue = (uint16_t)vals[2]; 246 burst_conf.tx_burst = (uint16_t)vals[3]; 247 248 return 0; 249 } 250 251 enum { 252 #define OPT_PFC "pfc" 253 OPT_PFC_NUM = 256, 254 #define OPT_MNC "mnc" 255 OPT_MNC_NUM, 256 #define OPT_RSZ "rsz" 257 OPT_RSZ_NUM, 258 #define OPT_BSZ "bsz" 259 OPT_BSZ_NUM, 260 #define OPT_MSZ "msz" 261 OPT_MSZ_NUM, 262 #define OPT_RTH "rth" 263 OPT_RTH_NUM, 264 #define OPT_TTH "tth" 265 OPT_TTH_NUM, 266 #define OPT_CFG "cfg" 267 OPT_CFG_NUM, 268 }; 269 270 /* 271 * Parses the argument given in the command line of the application, 272 * calculates mask for used cores and initializes EAL with calculated core mask 273 */ 274 int 275 app_parse_args(int argc, char **argv) 276 { 277 int opt, ret; 278 int option_index; 279 char *prgname = argv[0]; 280 uint32_t i; 281 282 static struct option lgopts[] = { 283 {OPT_PFC, 1, NULL, OPT_PFC_NUM}, 284 {OPT_MNC, 1, NULL, OPT_MNC_NUM}, 285 {OPT_RSZ, 1, NULL, OPT_RSZ_NUM}, 286 {OPT_BSZ, 1, NULL, OPT_BSZ_NUM}, 287 {OPT_MSZ, 1, NULL, OPT_MSZ_NUM}, 288 {OPT_RTH, 1, NULL, OPT_RTH_NUM}, 289 {OPT_TTH, 1, NULL, OPT_TTH_NUM}, 290 {OPT_CFG, 1, NULL, OPT_CFG_NUM}, 291 {NULL, 0, 0, 0 } 292 }; 293 294 /* initialize EAL first */ 295 ret = rte_eal_init(argc, argv); 296 if (ret < 0) 297 return -1; 298 299 argc -= ret; 300 argv += ret; 301 302 /* set en_US locale to print big numbers with ',' */ 303 setlocale(LC_NUMERIC, "en_US.utf-8"); 304 305 while ((opt = getopt_long(argc, argv, "i", 306 lgopts, &option_index)) != EOF) { 307 308 switch (opt) { 309 case 'i': 310 printf("Interactive-mode selected\n"); 311 interactive = 1; 312 break; 313 /* long options */ 314 315 case OPT_PFC_NUM: 316 ret = app_parse_flow_conf(optarg); 317 if (ret) { 318 RTE_LOG(ERR, APP, "Invalid pipe configuration %s\n", 319 optarg); 320 return -1; 321 } 322 break; 323 324 case OPT_MNC_NUM: 325 app_main_core = (uint32_t)atoi(optarg); 326 break; 327 328 case OPT_RSZ_NUM: 329 ret = app_parse_ring_conf(optarg); 330 if (ret) { 331 RTE_LOG(ERR, APP, "Invalid ring configuration %s\n", 332 optarg); 333 return -1; 334 } 335 break; 336 337 case OPT_BSZ_NUM: 338 ret = app_parse_burst_conf(optarg); 339 if (ret) { 340 RTE_LOG(ERR, APP, "Invalid burst configuration %s\n", 341 optarg); 342 return -1; 343 } 344 break; 345 346 case OPT_MSZ_NUM: 347 mp_size = atoi(optarg); 348 if (mp_size <= 0) { 349 RTE_LOG(ERR, APP, "Invalid mempool size %s\n", 350 optarg); 351 return -1; 352 } 353 break; 354 355 case OPT_RTH_NUM: 356 ret = app_parse_rth_conf(optarg); 357 if (ret) { 358 RTE_LOG(ERR, APP, "Invalid RX threshold configuration %s\n", 359 optarg); 360 return -1; 361 } 362 break; 363 364 case OPT_TTH_NUM: 365 ret = app_parse_tth_conf(optarg); 366 if (ret) { 367 RTE_LOG(ERR, APP, "Invalid TX threshold configuration %s\n", 368 optarg); 369 return -1; 370 } 371 break; 372 373 case OPT_CFG_NUM: 374 cfg_profile = optarg; 375 break; 376 377 default: 378 app_usage(prgname); 379 return -1; 380 } 381 } 382 383 if (nb_pfc == 0) { 384 RTE_LOG(ERR, APP, "Packet flow not configured!\n"); 385 app_usage(prgname); 386 return -1; 387 } 388 389 /* sanity check for cores assignment */ 390 for(i = 0; i < nb_pfc; i++) { 391 if (qos_conf[i].rx_core >= RTE_MAX_LCORE) { 392 RTE_LOG(ERR, APP, "pfc %u: invalid RX lcore index %u\n", i + 1, 393 qos_conf[i].rx_core); 394 return -1; 395 } 396 if (qos_conf[i].wt_core >= RTE_MAX_LCORE) { 397 RTE_LOG(ERR, APP, "pfc %u: invalid WT lcore index %u\n", i + 1, 398 qos_conf[i].wt_core); 399 return -1; 400 } 401 uint32_t rx_sock = rte_lcore_to_socket_id(qos_conf[i].rx_core); 402 uint32_t wt_sock = rte_lcore_to_socket_id(qos_conf[i].wt_core); 403 if (rx_sock != wt_sock) { 404 RTE_LOG(ERR, APP, "pfc %u: RX and WT must be on the same socket\n", i + 1); 405 return -1; 406 } 407 app_numa_mask |= 1 << rte_lcore_to_socket_id(qos_conf[i].rx_core); 408 } 409 410 return 0; 411 } 412