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 static inline int str_is(const char *str, const char *is) 85 { 86 return strcmp(str, is) == 0; 87 } 88 89 /* returns core mask used by DPDK */ 90 static uint64_t 91 app_eal_core_mask(void) 92 { 93 uint64_t cm = 0; 94 uint32_t i; 95 96 for (i = 0; i < APP_MAX_LCORE; i++) { 97 if (rte_lcore_has_role(i, ROLE_RTE)) 98 cm |= (1ULL << i); 99 } 100 101 cm |= (1ULL << rte_get_main_lcore()); 102 103 return cm; 104 } 105 106 107 /* returns total number of cores presented in a system */ 108 static uint32_t 109 app_cpu_core_count(void) 110 { 111 int i, len; 112 char path[PATH_MAX]; 113 uint32_t ncores = 0; 114 115 for (i = 0; i < APP_MAX_LCORE; i++) { 116 len = snprintf(path, sizeof(path), SYS_CPU_DIR, i); 117 if (len <= 0 || (unsigned)len >= sizeof(path)) 118 continue; 119 120 if (access(path, F_OK) == 0) 121 ncores++; 122 } 123 124 return ncores; 125 } 126 127 /* returns: 128 number of values parsed 129 -1 in case of error 130 */ 131 static int 132 app_parse_opt_vals(const char *conf_str, char separator, uint32_t n_vals, uint32_t *opt_vals) 133 { 134 char *string; 135 int i, n_tokens; 136 char *tokens[MAX_OPT_VALUES]; 137 138 if (conf_str == NULL || opt_vals == NULL || n_vals == 0 || n_vals > MAX_OPT_VALUES) 139 return -1; 140 141 /* duplicate configuration string before splitting it to tokens */ 142 string = strdup(conf_str); 143 if (string == NULL) 144 return -1; 145 146 n_tokens = rte_strsplit(string, strnlen(string, 32), tokens, n_vals, separator); 147 148 if (n_tokens > MAX_OPT_VALUES) 149 return -1; 150 151 for (i = 0; i < n_tokens; i++) 152 opt_vals[i] = (uint32_t)atol(tokens[i]); 153 154 free(string); 155 156 return n_tokens; 157 } 158 159 static int 160 app_parse_ring_conf(const char *conf_str) 161 { 162 int ret; 163 uint32_t vals[3]; 164 165 ret = app_parse_opt_vals(conf_str, ',', 3, vals); 166 if (ret != 3) 167 return ret; 168 169 ring_conf.rx_size = vals[0]; 170 ring_conf.ring_size = vals[1]; 171 ring_conf.tx_size = vals[2]; 172 173 return 0; 174 } 175 176 static int 177 app_parse_rth_conf(const char *conf_str) 178 { 179 int ret; 180 uint32_t vals[3]; 181 182 ret = app_parse_opt_vals(conf_str, ',', 3, vals); 183 if (ret != 3) 184 return ret; 185 186 rx_thresh.pthresh = (uint8_t)vals[0]; 187 rx_thresh.hthresh = (uint8_t)vals[1]; 188 rx_thresh.wthresh = (uint8_t)vals[2]; 189 190 return 0; 191 } 192 193 static int 194 app_parse_tth_conf(const char *conf_str) 195 { 196 int ret; 197 uint32_t vals[3]; 198 199 ret = app_parse_opt_vals(conf_str, ',', 3, vals); 200 if (ret != 3) 201 return ret; 202 203 tx_thresh.pthresh = (uint8_t)vals[0]; 204 tx_thresh.hthresh = (uint8_t)vals[1]; 205 tx_thresh.wthresh = (uint8_t)vals[2]; 206 207 return 0; 208 } 209 210 static int 211 app_parse_flow_conf(const char *conf_str) 212 { 213 int ret; 214 uint32_t vals[5]; 215 struct flow_conf *pconf; 216 uint64_t mask; 217 218 memset(vals, 0, sizeof(vals)); 219 ret = app_parse_opt_vals(conf_str, ',', 6, vals); 220 if (ret < 4 || ret > 5) 221 return ret; 222 223 pconf = &qos_conf[nb_pfc]; 224 225 pconf->rx_port = vals[0]; 226 pconf->tx_port = vals[1]; 227 pconf->rx_core = (uint8_t)vals[2]; 228 pconf->wt_core = (uint8_t)vals[3]; 229 if (ret == 5) 230 pconf->tx_core = (uint8_t)vals[4]; 231 else 232 pconf->tx_core = pconf->wt_core; 233 234 if (pconf->rx_core == pconf->wt_core) { 235 RTE_LOG(ERR, APP, "pfc %u: rx thread and worker thread cannot share same core\n", nb_pfc); 236 return -1; 237 } 238 239 if (pconf->rx_port >= RTE_MAX_ETHPORTS) { 240 RTE_LOG(ERR, APP, "pfc %u: invalid rx port %"PRIu16" index\n", 241 nb_pfc, pconf->rx_port); 242 return -1; 243 } 244 if (pconf->tx_port >= RTE_MAX_ETHPORTS) { 245 RTE_LOG(ERR, APP, "pfc %u: invalid tx port %"PRIu16" index\n", 246 nb_pfc, pconf->tx_port); 247 return -1; 248 } 249 250 mask = 1lu << pconf->rx_port; 251 if (app_used_rx_port_mask & mask) { 252 RTE_LOG(ERR, APP, "pfc %u: rx port %"PRIu16" is used already\n", 253 nb_pfc, pconf->rx_port); 254 return -1; 255 } 256 app_used_rx_port_mask |= mask; 257 app_used_port_mask |= mask; 258 259 mask = 1lu << pconf->tx_port; 260 if (app_used_tx_port_mask & mask) { 261 RTE_LOG(ERR, APP, "pfc %u: port %"PRIu16" is used already\n", 262 nb_pfc, pconf->tx_port); 263 return -1; 264 } 265 app_used_tx_port_mask |= mask; 266 app_used_port_mask |= mask; 267 268 mask = 1lu << pconf->rx_core; 269 app_used_core_mask |= mask; 270 271 mask = 1lu << pconf->wt_core; 272 app_used_core_mask |= mask; 273 274 mask = 1lu << pconf->tx_core; 275 app_used_core_mask |= mask; 276 277 nb_pfc++; 278 279 return 0; 280 } 281 282 static int 283 app_parse_burst_conf(const char *conf_str) 284 { 285 int ret; 286 uint32_t vals[4]; 287 288 ret = app_parse_opt_vals(conf_str, ',', 4, vals); 289 if (ret != 4) 290 return ret; 291 292 burst_conf.rx_burst = (uint16_t)vals[0]; 293 burst_conf.ring_burst = (uint16_t)vals[1]; 294 burst_conf.qos_dequeue = (uint16_t)vals[2]; 295 burst_conf.tx_burst = (uint16_t)vals[3]; 296 297 return 0; 298 } 299 300 /* 301 * Parses the argument given in the command line of the application, 302 * calculates mask for used cores and initializes EAL with calculated core mask 303 */ 304 int 305 app_parse_args(int argc, char **argv) 306 { 307 int opt, ret; 308 int option_index; 309 const char *optname; 310 char *prgname = argv[0]; 311 uint32_t i, nb_lcores; 312 313 static struct option lgopts[] = { 314 { "pfc", 1, 0, 0 }, 315 { "mnc", 1, 0, 0 }, 316 { "rsz", 1, 0, 0 }, 317 { "bsz", 1, 0, 0 }, 318 { "msz", 1, 0, 0 }, 319 { "rth", 1, 0, 0 }, 320 { "tth", 1, 0, 0 }, 321 { "cfg", 1, 0, 0 }, 322 { NULL, 0, 0, 0 } 323 }; 324 325 /* initialize EAL first */ 326 ret = rte_eal_init(argc, argv); 327 if (ret < 0) 328 return -1; 329 330 argc -= ret; 331 argv += ret; 332 333 /* set en_US locale to print big numbers with ',' */ 334 setlocale(LC_NUMERIC, "en_US.utf-8"); 335 336 while ((opt = getopt_long(argc, argv, "i", 337 lgopts, &option_index)) != EOF) { 338 339 switch (opt) { 340 case 'i': 341 printf("Interactive-mode selected\n"); 342 interactive = 1; 343 break; 344 /* long options */ 345 case 0: 346 optname = lgopts[option_index].name; 347 if (str_is(optname, "pfc")) { 348 ret = app_parse_flow_conf(optarg); 349 if (ret) { 350 RTE_LOG(ERR, APP, "Invalid pipe configuration %s\n", optarg); 351 return -1; 352 } 353 break; 354 } 355 if (str_is(optname, "mnc")) { 356 app_main_core = (uint32_t)atoi(optarg); 357 break; 358 } 359 if (str_is(optname, "rsz")) { 360 ret = app_parse_ring_conf(optarg); 361 if (ret) { 362 RTE_LOG(ERR, APP, "Invalid ring configuration %s\n", optarg); 363 return -1; 364 } 365 break; 366 } 367 if (str_is(optname, "bsz")) { 368 ret = app_parse_burst_conf(optarg); 369 if (ret) { 370 RTE_LOG(ERR, APP, "Invalid burst configuration %s\n", optarg); 371 return -1; 372 } 373 break; 374 } 375 if (str_is(optname, "msz")) { 376 mp_size = atoi(optarg); 377 if (mp_size <= 0) { 378 RTE_LOG(ERR, APP, "Invalid mempool size %s\n", optarg); 379 return -1; 380 } 381 break; 382 } 383 if (str_is(optname, "rth")) { 384 ret = app_parse_rth_conf(optarg); 385 if (ret) { 386 RTE_LOG(ERR, APP, "Invalid RX threshold configuration %s\n", optarg); 387 return -1; 388 } 389 break; 390 } 391 if (str_is(optname, "tth")) { 392 ret = app_parse_tth_conf(optarg); 393 if (ret) { 394 RTE_LOG(ERR, APP, "Invalid TX threshold configuration %s\n", optarg); 395 return -1; 396 } 397 break; 398 } 399 if (str_is(optname, "cfg")) { 400 cfg_profile = optarg; 401 break; 402 } 403 break; 404 405 default: 406 app_usage(prgname); 407 return -1; 408 } 409 } 410 411 /* check main core index validity */ 412 for (i = 0; i <= app_main_core; i++) { 413 if (app_used_core_mask & (1u << app_main_core)) { 414 RTE_LOG(ERR, APP, "Main core index is not configured properly\n"); 415 app_usage(prgname); 416 return -1; 417 } 418 } 419 app_used_core_mask |= 1u << app_main_core; 420 421 if ((app_used_core_mask != app_eal_core_mask()) || 422 (app_main_core != rte_get_main_lcore())) { 423 RTE_LOG(ERR, APP, "EAL core mask not configured properly, must be %" PRIx64 424 " instead of %" PRIx64 "\n" , app_used_core_mask, app_eal_core_mask()); 425 return -1; 426 } 427 428 if (nb_pfc == 0) { 429 RTE_LOG(ERR, APP, "Packet flow not configured!\n"); 430 app_usage(prgname); 431 return -1; 432 } 433 434 /* sanity check for cores assignment */ 435 nb_lcores = app_cpu_core_count(); 436 437 for(i = 0; i < nb_pfc; i++) { 438 if (qos_conf[i].rx_core >= nb_lcores) { 439 RTE_LOG(ERR, APP, "pfc %u: invalid RX lcore index %u\n", i + 1, 440 qos_conf[i].rx_core); 441 return -1; 442 } 443 if (qos_conf[i].wt_core >= nb_lcores) { 444 RTE_LOG(ERR, APP, "pfc %u: invalid WT lcore index %u\n", i + 1, 445 qos_conf[i].wt_core); 446 return -1; 447 } 448 uint32_t rx_sock = rte_lcore_to_socket_id(qos_conf[i].rx_core); 449 uint32_t wt_sock = rte_lcore_to_socket_id(qos_conf[i].wt_core); 450 if (rx_sock != wt_sock) { 451 RTE_LOG(ERR, APP, "pfc %u: RX and WT must be on the same socket\n", i + 1); 452 return -1; 453 } 454 app_numa_mask |= 1 << rte_lcore_to_socket_id(qos_conf[i].rx_core); 455 } 456 457 return 0; 458 } 459