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