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