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 return -1; 108 109 for (i = 0; i < n_tokens; i++) 110 opt_vals[i] = (uint32_t)atol(tokens[i]); 111 112 free(string); 113 114 return n_tokens; 115 } 116 117 static int 118 app_parse_ring_conf(const char *conf_str) 119 { 120 int ret; 121 uint32_t vals[3]; 122 123 ret = app_parse_opt_vals(conf_str, ',', 3, vals); 124 if (ret != 3) 125 return ret; 126 127 ring_conf.rx_size = vals[0]; 128 ring_conf.ring_size = vals[1]; 129 ring_conf.tx_size = vals[2]; 130 131 return 0; 132 } 133 134 static int 135 app_parse_rth_conf(const char *conf_str) 136 { 137 int ret; 138 uint32_t vals[3]; 139 140 ret = app_parse_opt_vals(conf_str, ',', 3, vals); 141 if (ret != 3) 142 return ret; 143 144 rx_thresh.pthresh = (uint8_t)vals[0]; 145 rx_thresh.hthresh = (uint8_t)vals[1]; 146 rx_thresh.wthresh = (uint8_t)vals[2]; 147 148 return 0; 149 } 150 151 static int 152 app_parse_tth_conf(const char *conf_str) 153 { 154 int ret; 155 uint32_t vals[3]; 156 157 ret = app_parse_opt_vals(conf_str, ',', 3, vals); 158 if (ret != 3) 159 return ret; 160 161 tx_thresh.pthresh = (uint8_t)vals[0]; 162 tx_thresh.hthresh = (uint8_t)vals[1]; 163 tx_thresh.wthresh = (uint8_t)vals[2]; 164 165 return 0; 166 } 167 168 static int 169 app_parse_flow_conf(const char *conf_str) 170 { 171 int ret; 172 uint32_t vals[5]; 173 struct flow_conf *pconf; 174 uint64_t mask; 175 176 memset(vals, 0, sizeof(vals)); 177 ret = app_parse_opt_vals(conf_str, ',', 6, vals); 178 if (ret < 4 || ret > 5) 179 return ret; 180 181 pconf = &qos_conf[nb_pfc]; 182 183 pconf->rx_port = vals[0]; 184 pconf->tx_port = vals[1]; 185 pconf->rx_core = (uint8_t)vals[2]; 186 pconf->wt_core = (uint8_t)vals[3]; 187 if (ret == 5) 188 pconf->tx_core = (uint8_t)vals[4]; 189 else 190 pconf->tx_core = pconf->wt_core; 191 192 if (pconf->rx_core == pconf->wt_core) { 193 RTE_LOG(ERR, APP, "pfc %u: rx thread and worker thread cannot share same core\n", nb_pfc); 194 return -1; 195 } 196 197 if (pconf->rx_port >= RTE_MAX_ETHPORTS) { 198 RTE_LOG(ERR, APP, "pfc %u: invalid rx port %"PRIu16" index\n", 199 nb_pfc, pconf->rx_port); 200 return -1; 201 } 202 if (pconf->tx_port >= RTE_MAX_ETHPORTS) { 203 RTE_LOG(ERR, APP, "pfc %u: invalid tx port %"PRIu16" index\n", 204 nb_pfc, pconf->tx_port); 205 return -1; 206 } 207 208 mask = 1lu << pconf->rx_port; 209 if (app_used_rx_port_mask & mask) { 210 RTE_LOG(ERR, APP, "pfc %u: rx port %"PRIu16" is used already\n", 211 nb_pfc, pconf->rx_port); 212 return -1; 213 } 214 app_used_rx_port_mask |= mask; 215 app_used_port_mask |= mask; 216 217 mask = 1lu << pconf->tx_port; 218 if (app_used_tx_port_mask & mask) { 219 RTE_LOG(ERR, APP, "pfc %u: port %"PRIu16" is used already\n", 220 nb_pfc, pconf->tx_port); 221 return -1; 222 } 223 app_used_tx_port_mask |= mask; 224 app_used_port_mask |= mask; 225 226 nb_pfc++; 227 228 return 0; 229 } 230 231 static int 232 app_parse_burst_conf(const char *conf_str) 233 { 234 int ret; 235 uint32_t vals[4]; 236 237 ret = app_parse_opt_vals(conf_str, ',', 4, vals); 238 if (ret != 4) 239 return ret; 240 241 burst_conf.rx_burst = (uint16_t)vals[0]; 242 burst_conf.ring_burst = (uint16_t)vals[1]; 243 burst_conf.qos_dequeue = (uint16_t)vals[2]; 244 burst_conf.tx_burst = (uint16_t)vals[3]; 245 246 return 0; 247 } 248 249 enum { 250 #define OPT_PFC "pfc" 251 OPT_PFC_NUM = 256, 252 #define OPT_MNC "mnc" 253 OPT_MNC_NUM, 254 #define OPT_RSZ "rsz" 255 OPT_RSZ_NUM, 256 #define OPT_BSZ "bsz" 257 OPT_BSZ_NUM, 258 #define OPT_MSZ "msz" 259 OPT_MSZ_NUM, 260 #define OPT_RTH "rth" 261 OPT_RTH_NUM, 262 #define OPT_TTH "tth" 263 OPT_TTH_NUM, 264 #define OPT_CFG "cfg" 265 OPT_CFG_NUM, 266 }; 267 268 /* 269 * Parses the argument given in the command line of the application, 270 * calculates mask for used cores and initializes EAL with calculated core mask 271 */ 272 int 273 app_parse_args(int argc, char **argv) 274 { 275 int opt, ret; 276 int option_index; 277 char *prgname = argv[0]; 278 uint32_t i; 279 280 static struct option lgopts[] = { 281 {OPT_PFC, 1, NULL, OPT_PFC_NUM}, 282 {OPT_MNC, 1, NULL, OPT_MNC_NUM}, 283 {OPT_RSZ, 1, NULL, OPT_RSZ_NUM}, 284 {OPT_BSZ, 1, NULL, OPT_BSZ_NUM}, 285 {OPT_MSZ, 1, NULL, OPT_MSZ_NUM}, 286 {OPT_RTH, 1, NULL, OPT_RTH_NUM}, 287 {OPT_TTH, 1, NULL, OPT_TTH_NUM}, 288 {OPT_CFG, 1, NULL, OPT_CFG_NUM}, 289 {NULL, 0, 0, 0 } 290 }; 291 292 /* initialize EAL first */ 293 ret = rte_eal_init(argc, argv); 294 if (ret < 0) 295 return -1; 296 297 argc -= ret; 298 argv += ret; 299 300 /* set en_US locale to print big numbers with ',' */ 301 setlocale(LC_NUMERIC, "en_US.utf-8"); 302 303 while ((opt = getopt_long(argc, argv, "i", 304 lgopts, &option_index)) != EOF) { 305 306 switch (opt) { 307 case 'i': 308 printf("Interactive-mode selected\n"); 309 interactive = 1; 310 break; 311 /* long options */ 312 313 case OPT_PFC_NUM: 314 ret = app_parse_flow_conf(optarg); 315 if (ret) { 316 RTE_LOG(ERR, APP, "Invalid pipe configuration %s\n", 317 optarg); 318 return -1; 319 } 320 break; 321 322 case OPT_MNC_NUM: 323 app_main_core = (uint32_t)atoi(optarg); 324 break; 325 326 case OPT_RSZ_NUM: 327 ret = app_parse_ring_conf(optarg); 328 if (ret) { 329 RTE_LOG(ERR, APP, "Invalid ring configuration %s\n", 330 optarg); 331 return -1; 332 } 333 break; 334 335 case OPT_BSZ_NUM: 336 ret = app_parse_burst_conf(optarg); 337 if (ret) { 338 RTE_LOG(ERR, APP, "Invalid burst configuration %s\n", 339 optarg); 340 return -1; 341 } 342 break; 343 344 case OPT_MSZ_NUM: 345 mp_size = atoi(optarg); 346 if (mp_size <= 0) { 347 RTE_LOG(ERR, APP, "Invalid mempool size %s\n", 348 optarg); 349 return -1; 350 } 351 break; 352 353 case OPT_RTH_NUM: 354 ret = app_parse_rth_conf(optarg); 355 if (ret) { 356 RTE_LOG(ERR, APP, "Invalid RX threshold configuration %s\n", 357 optarg); 358 return -1; 359 } 360 break; 361 362 case OPT_TTH_NUM: 363 ret = app_parse_tth_conf(optarg); 364 if (ret) { 365 RTE_LOG(ERR, APP, "Invalid TX threshold configuration %s\n", 366 optarg); 367 return -1; 368 } 369 break; 370 371 case OPT_CFG_NUM: 372 cfg_profile = optarg; 373 break; 374 375 default: 376 app_usage(prgname); 377 return -1; 378 } 379 } 380 381 if (nb_pfc == 0) { 382 RTE_LOG(ERR, APP, "Packet flow not configured!\n"); 383 app_usage(prgname); 384 return -1; 385 } 386 387 /* sanity check for cores assignment */ 388 for(i = 0; i < nb_pfc; i++) { 389 if (qos_conf[i].rx_core >= RTE_MAX_LCORE) { 390 RTE_LOG(ERR, APP, "pfc %u: invalid RX lcore index %u\n", i + 1, 391 qos_conf[i].rx_core); 392 return -1; 393 } 394 if (qos_conf[i].wt_core >= RTE_MAX_LCORE) { 395 RTE_LOG(ERR, APP, "pfc %u: invalid WT lcore index %u\n", i + 1, 396 qos_conf[i].wt_core); 397 return -1; 398 } 399 uint32_t rx_sock = rte_lcore_to_socket_id(qos_conf[i].rx_core); 400 uint32_t wt_sock = rte_lcore_to_socket_id(qos_conf[i].wt_core); 401 if (rx_sock != wt_sock) { 402 RTE_LOG(ERR, APP, "pfc %u: RX and WT must be on the same socket\n", i + 1); 403 return -1; 404 } 405 app_numa_mask |= 1 << rte_lcore_to_socket_id(qos_conf[i].rx_core); 406 } 407 408 return 0; 409 } 410