1 /*- 2 * BSD LICENSE 3 * 4 * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <stdint.h> 35 #include <sys/queue.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <stdio.h> 39 #include <assert.h> 40 #include <errno.h> 41 #include <signal.h> 42 #include <stdarg.h> 43 #include <inttypes.h> 44 #include <getopt.h> 45 46 #include <rte_common.h> 47 #include <rte_log.h> 48 #include <rte_memory.h> 49 #include <rte_memcpy.h> 50 #include <rte_memzone.h> 51 #include <rte_tailq.h> 52 #include <rte_eal.h> 53 #include <rte_per_lcore.h> 54 #include <rte_launch.h> 55 #include <rte_atomic.h> 56 #include <rte_cycles.h> 57 #include <rte_prefetch.h> 58 #include <rte_lcore.h> 59 #include <rte_per_lcore.h> 60 #include <rte_branch_prediction.h> 61 #include <rte_interrupts.h> 62 #include <rte_pci.h> 63 #include <rte_random.h> 64 #include <rte_debug.h> 65 #include <rte_ether.h> 66 #include <rte_ethdev.h> 67 #include <rte_ring.h> 68 #include <rte_log.h> 69 #include <rte_mempool.h> 70 #include <rte_mbuf.h> 71 #include <rte_memcpy.h> 72 73 #include "main.h" 74 75 /* basic constants used in application */ 76 #define NUM_QUEUES 128 77 78 #define NUM_MBUFS 64*1024 79 #define MBUF_CACHE_SIZE 64 80 #define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) 81 82 #define INVALID_PORT_ID 0xFF 83 84 /* mask of enabled ports */ 85 static uint32_t enabled_port_mask = 0; 86 87 /* number of pools (if user does not specify any, 16 by default */ 88 static enum rte_eth_nb_pools num_pools = ETH_16_POOLS; 89 90 /* empty vmdq+dcb configuration structure. Filled in programatically */ 91 static const struct rte_eth_conf vmdq_dcb_conf_default = { 92 .rxmode = { 93 .mq_mode = ETH_MQ_RX_VMDQ_DCB, 94 .split_hdr_size = 0, 95 .header_split = 0, /**< Header Split disabled */ 96 .hw_ip_checksum = 0, /**< IP checksum offload disabled */ 97 .hw_vlan_filter = 0, /**< VLAN filtering disabled */ 98 .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ 99 }, 100 .txmode = { 101 .mq_mode = ETH_MQ_TX_NONE, 102 }, 103 .rx_adv_conf = { 104 /* 105 * should be overridden separately in code with 106 * appropriate values 107 */ 108 .vmdq_dcb_conf = { 109 .nb_queue_pools = ETH_16_POOLS, 110 .enable_default_pool = 0, 111 .default_pool = 0, 112 .nb_pool_maps = 0, 113 .pool_map = {{0, 0},}, 114 .dcb_queue = {0}, 115 }, 116 }, 117 }; 118 119 static uint8_t ports[RTE_MAX_ETHPORTS]; 120 static unsigned num_ports = 0; 121 122 /* array used for printing out statistics */ 123 volatile unsigned long rxPackets[ NUM_QUEUES ] = {0}; 124 125 const uint16_t vlan_tags[] = { 126 0, 1, 2, 3, 4, 5, 6, 7, 127 8, 9, 10, 11, 12, 13, 14, 15, 128 16, 17, 18, 19, 20, 21, 22, 23, 129 24, 25, 26, 27, 28, 29, 30, 31 130 }; 131 132 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array 133 * given above, and the number of traffic classes available for use. */ 134 static inline int 135 get_eth_conf(struct rte_eth_conf *eth_conf, enum rte_eth_nb_pools num_pools) 136 { 137 struct rte_eth_vmdq_dcb_conf conf; 138 unsigned i; 139 140 if (num_pools != ETH_16_POOLS && num_pools != ETH_32_POOLS ) return -1; 141 142 conf.nb_queue_pools = num_pools; 143 conf.enable_default_pool = 0; 144 conf.default_pool = 0; /* set explicit value, even if not used */ 145 conf.nb_pool_maps = sizeof( vlan_tags )/sizeof( vlan_tags[ 0 ]); 146 for (i = 0; i < conf.nb_pool_maps; i++){ 147 conf.pool_map[i].vlan_id = vlan_tags[ i ]; 148 conf.pool_map[i].pools = 1 << (i % num_pools); 149 } 150 for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){ 151 conf.dcb_queue[i] = (uint8_t)(i % (NUM_QUEUES/num_pools)); 152 } 153 (void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf))); 154 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_dcb_conf, &conf, 155 sizeof(eth_conf->rx_adv_conf.vmdq_dcb_conf))); 156 return 0; 157 } 158 159 /* 160 * Initialises a given port using global settings and with the rx buffers 161 * coming from the mbuf_pool passed as parameter 162 */ 163 static inline int 164 port_init(uint8_t port, struct rte_mempool *mbuf_pool) 165 { 166 struct rte_eth_conf port_conf; 167 const uint16_t rxRings = ETH_VMDQ_DCB_NUM_QUEUES, 168 txRings = (uint16_t)rte_lcore_count(); 169 const uint16_t rxRingSize = 128, txRingSize = 512; 170 int retval; 171 uint16_t q; 172 173 retval = get_eth_conf(&port_conf, num_pools); 174 if (retval < 0) 175 return retval; 176 177 if (port >= rte_eth_dev_count()) return -1; 178 179 retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf); 180 if (retval != 0) 181 return retval; 182 183 for (q = 0; q < rxRings; q ++) { 184 retval = rte_eth_rx_queue_setup(port, q, rxRingSize, 185 rte_eth_dev_socket_id(port), 186 NULL, 187 mbuf_pool); 188 if (retval < 0) 189 return retval; 190 } 191 192 for (q = 0; q < txRings; q ++) { 193 retval = rte_eth_tx_queue_setup(port, q, txRingSize, 194 rte_eth_dev_socket_id(port), 195 NULL); 196 if (retval < 0) 197 return retval; 198 } 199 200 retval = rte_eth_dev_start(port); 201 if (retval < 0) 202 return retval; 203 204 struct ether_addr addr; 205 rte_eth_macaddr_get(port, &addr); 206 printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 207 " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", 208 (unsigned)port, 209 addr.addr_bytes[0], addr.addr_bytes[1], addr.addr_bytes[2], 210 addr.addr_bytes[3], addr.addr_bytes[4], addr.addr_bytes[5]); 211 212 return 0; 213 } 214 215 /* Check num_pools parameter and set it if OK*/ 216 static int 217 vmdq_parse_num_pools(const char *q_arg) 218 { 219 char *end = NULL; 220 int n; 221 222 /* parse number string */ 223 n = strtol(q_arg, &end, 10); 224 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 225 return -1; 226 if (n != 16 && n != 32) 227 return -1; 228 if (n == 16) 229 num_pools = ETH_16_POOLS; 230 else 231 num_pools = ETH_32_POOLS; 232 233 return 0; 234 } 235 236 static int 237 parse_portmask(const char *portmask) 238 { 239 char *end = NULL; 240 unsigned long pm; 241 242 /* parse hexadecimal string */ 243 pm = strtoul(portmask, &end, 16); 244 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) 245 return -1; 246 247 if (pm == 0) 248 return -1; 249 250 return pm; 251 } 252 253 /* Display usage */ 254 static void 255 vmdq_usage(const char *prgname) 256 { 257 printf("%s [EAL options] -- -p PORTMASK]\n" 258 " --nb-pools NP: number of pools (16 default, 32)\n", 259 prgname); 260 } 261 262 /* Parse the argument (num_pools) given in the command line of the application */ 263 static int 264 vmdq_parse_args(int argc, char **argv) 265 { 266 int opt; 267 int option_index; 268 unsigned i; 269 const char *prgname = argv[0]; 270 static struct option long_option[] = { 271 {"nb-pools", required_argument, NULL, 0}, 272 {NULL, 0, 0, 0} 273 }; 274 275 /* Parse command line */ 276 while ((opt = getopt_long(argc, argv, "p:",long_option,&option_index)) != EOF) { 277 switch (opt) { 278 /* portmask */ 279 case 'p': 280 enabled_port_mask = parse_portmask(optarg); 281 if (enabled_port_mask == 0) { 282 printf("invalid portmask\n"); 283 vmdq_usage(prgname); 284 return -1; 285 } 286 break; 287 case 0: 288 if (vmdq_parse_num_pools(optarg) == -1){ 289 printf("invalid number of pools\n"); 290 vmdq_usage(prgname); 291 return -1; 292 } 293 break; 294 default: 295 vmdq_usage(prgname); 296 return -1; 297 } 298 } 299 300 for(i = 0; i < RTE_MAX_ETHPORTS; i++) 301 { 302 if (enabled_port_mask & (1 << i)) 303 ports[num_ports++] = (uint8_t)i; 304 } 305 306 if (num_ports < 2 || num_ports % 2) { 307 printf("Current enabled port number is %u," 308 "but it should be even and at least 2\n",num_ports); 309 return -1; 310 } 311 312 return 0; 313 } 314 315 316 #ifndef RTE_EXEC_ENV_BAREMETAL 317 /* When we receive a HUP signal, print out our stats */ 318 static void 319 sighup_handler(int signum) 320 { 321 unsigned q; 322 for (q = 0; q < NUM_QUEUES; q ++) { 323 if (q % (NUM_QUEUES/num_pools) == 0) 324 printf("\nPool %u: ", q/(NUM_QUEUES/num_pools)); 325 printf("%lu ", rxPackets[ q ]); 326 } 327 printf("\nFinished handling signal %d\n", signum); 328 } 329 #endif 330 331 /* 332 * Main thread that does the work, reading from INPUT_PORT 333 * and writing to OUTPUT_PORT 334 */ 335 static __attribute__((noreturn)) int 336 lcore_main(void *arg) 337 { 338 const uintptr_t core_num = (uintptr_t)arg; 339 const unsigned num_cores = rte_lcore_count(); 340 uint16_t startQueue = (uint16_t)(core_num * (NUM_QUEUES/num_cores)); 341 uint16_t endQueue = (uint16_t)(startQueue + (NUM_QUEUES/num_cores)); 342 uint16_t q, i, p; 343 344 printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num, 345 rte_lcore_id(), startQueue, endQueue - 1); 346 347 for (;;) { 348 struct rte_mbuf *buf[32]; 349 const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]); 350 for (p = 0; p < num_ports; p++) { 351 const uint8_t src = ports[p]; 352 const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */ 353 354 if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID)) 355 continue; 356 357 for (q = startQueue; q < endQueue; q++) { 358 const uint16_t rxCount = rte_eth_rx_burst(src, 359 q, buf, buf_size); 360 if (rxCount == 0) 361 continue; 362 rxPackets[q] += rxCount; 363 364 const uint16_t txCount = rte_eth_tx_burst(dst, 365 (uint16_t)core_num, buf, rxCount); 366 if (txCount != rxCount) { 367 for (i = txCount; i < rxCount; i++) 368 rte_pktmbuf_free(buf[i]); 369 } 370 } 371 } 372 } 373 } 374 375 /* 376 * Update the global var NUM_PORTS and array PORTS according to system ports number 377 * and return valid ports number 378 */ 379 static unsigned check_ports_num(unsigned nb_ports) 380 { 381 unsigned valid_num_ports = num_ports; 382 unsigned portid; 383 384 if (num_ports > nb_ports) { 385 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n", 386 num_ports, nb_ports); 387 num_ports = nb_ports; 388 } 389 390 for (portid = 0; portid < num_ports; portid ++) { 391 if (ports[portid] >= nb_ports) { 392 printf("\nSpecified port ID(%u) exceeds max system port ID(%u)\n", 393 ports[portid], (nb_ports - 1)); 394 ports[portid] = INVALID_PORT_ID; 395 valid_num_ports --; 396 } 397 } 398 return valid_num_ports; 399 } 400 401 402 /* Main function, does initialisation and calls the per-lcore functions */ 403 int 404 MAIN(int argc, char *argv[]) 405 { 406 unsigned cores; 407 struct rte_mempool *mbuf_pool; 408 unsigned lcore_id; 409 uintptr_t i; 410 int ret; 411 unsigned nb_ports, valid_num_ports; 412 uint8_t portid; 413 414 #ifndef RTE_EXEC_ENV_BAREMETAL 415 signal(SIGHUP, sighup_handler); 416 #endif 417 418 /* init EAL */ 419 ret = rte_eal_init(argc, argv); 420 if (ret < 0) 421 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); 422 argc -= ret; 423 argv += ret; 424 425 /* parse app arguments */ 426 ret = vmdq_parse_args(argc, argv); 427 if (ret < 0) 428 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n"); 429 430 cores = rte_lcore_count(); 431 if ((cores & (cores - 1)) != 0 || cores > 128) { 432 rte_exit(EXIT_FAILURE,"This program can only run on an even" 433 "number of cores(1-128)\n\n"); 434 } 435 436 nb_ports = rte_eth_dev_count(); 437 if (nb_ports > RTE_MAX_ETHPORTS) 438 nb_ports = RTE_MAX_ETHPORTS; 439 440 /* 441 * Update the global var NUM_PORTS and global array PORTS 442 * and get value of var VALID_NUM_PORTS according to system ports number 443 */ 444 valid_num_ports = check_ports_num(nb_ports); 445 446 if (valid_num_ports < 2 || valid_num_ports % 2) { 447 printf("Current valid ports number is %u\n", valid_num_ports); 448 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n"); 449 } 450 451 mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS * nb_ports, 452 MBUF_SIZE, MBUF_CACHE_SIZE, 453 sizeof(struct rte_pktmbuf_pool_private), 454 rte_pktmbuf_pool_init, NULL, 455 rte_pktmbuf_init, NULL, 456 rte_socket_id(), 0); 457 if (mbuf_pool == NULL) 458 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n"); 459 460 /* initialize all ports */ 461 for (portid = 0; portid < nb_ports; portid++) { 462 /* skip ports that are not enabled */ 463 if ((enabled_port_mask & (1 << portid)) == 0) { 464 printf("\nSkipping disabled port %d\n", portid); 465 continue; 466 } 467 if (port_init(portid, mbuf_pool) != 0) 468 rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n"); 469 } 470 471 /* call lcore_main() on every slave lcore */ 472 i = 0; 473 RTE_LCORE_FOREACH_SLAVE(lcore_id) { 474 rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id); 475 } 476 /* call on master too */ 477 (void) lcore_main((void*)i); 478 479 return 0; 480 } 481