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 /* basic constants used in application */ 74 #define NUM_QUEUES 128 75 76 #define NUM_MBUFS 64*1024 77 #define MBUF_CACHE_SIZE 64 78 #define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) 79 80 #define INVALID_PORT_ID 0xFF 81 82 /* mask of enabled ports */ 83 static uint32_t enabled_port_mask = 0; 84 85 /* number of pools (if user does not specify any, 16 by default */ 86 static enum rte_eth_nb_pools num_pools = ETH_16_POOLS; 87 88 /* empty vmdq+dcb configuration structure. Filled in programatically */ 89 static const struct rte_eth_conf vmdq_dcb_conf_default = { 90 .rxmode = { 91 .mq_mode = ETH_MQ_RX_VMDQ_DCB, 92 .split_hdr_size = 0, 93 .header_split = 0, /**< Header Split disabled */ 94 .hw_ip_checksum = 0, /**< IP checksum offload disabled */ 95 .hw_vlan_filter = 0, /**< VLAN filtering disabled */ 96 .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ 97 }, 98 .txmode = { 99 .mq_mode = ETH_MQ_TX_NONE, 100 }, 101 .rx_adv_conf = { 102 /* 103 * should be overridden separately in code with 104 * appropriate values 105 */ 106 .vmdq_dcb_conf = { 107 .nb_queue_pools = ETH_16_POOLS, 108 .enable_default_pool = 0, 109 .default_pool = 0, 110 .nb_pool_maps = 0, 111 .pool_map = {{0, 0},}, 112 .dcb_queue = {0}, 113 }, 114 }, 115 }; 116 117 static uint8_t ports[RTE_MAX_ETHPORTS]; 118 static unsigned num_ports = 0; 119 120 /* array used for printing out statistics */ 121 volatile unsigned long rxPackets[ NUM_QUEUES ] = {0}; 122 123 const uint16_t vlan_tags[] = { 124 0, 1, 2, 3, 4, 5, 6, 7, 125 8, 9, 10, 11, 12, 13, 14, 15, 126 16, 17, 18, 19, 20, 21, 22, 23, 127 24, 25, 26, 27, 28, 29, 30, 31 128 }; 129 130 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array 131 * given above, and the number of traffic classes available for use. */ 132 static inline int 133 get_eth_conf(struct rte_eth_conf *eth_conf, enum rte_eth_nb_pools num_pools) 134 { 135 struct rte_eth_vmdq_dcb_conf conf; 136 unsigned i; 137 138 if (num_pools != ETH_16_POOLS && num_pools != ETH_32_POOLS ) return -1; 139 140 conf.nb_queue_pools = num_pools; 141 conf.enable_default_pool = 0; 142 conf.default_pool = 0; /* set explicit value, even if not used */ 143 conf.nb_pool_maps = sizeof( vlan_tags )/sizeof( vlan_tags[ 0 ]); 144 for (i = 0; i < conf.nb_pool_maps; i++){ 145 conf.pool_map[i].vlan_id = vlan_tags[ i ]; 146 conf.pool_map[i].pools = 1 << (i % num_pools); 147 } 148 for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){ 149 conf.dcb_queue[i] = (uint8_t)(i % (NUM_QUEUES/num_pools)); 150 } 151 (void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf))); 152 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_dcb_conf, &conf, 153 sizeof(eth_conf->rx_adv_conf.vmdq_dcb_conf))); 154 return 0; 155 } 156 157 /* 158 * Initialises a given port using global settings and with the rx buffers 159 * coming from the mbuf_pool passed as parameter 160 */ 161 static inline int 162 port_init(uint8_t port, struct rte_mempool *mbuf_pool) 163 { 164 struct rte_eth_conf port_conf; 165 const uint16_t rxRings = ETH_VMDQ_DCB_NUM_QUEUES, 166 txRings = (uint16_t)rte_lcore_count(); 167 const uint16_t rxRingSize = 128, txRingSize = 512; 168 int retval; 169 uint16_t q; 170 171 retval = get_eth_conf(&port_conf, num_pools); 172 if (retval < 0) 173 return retval; 174 175 if (port >= rte_eth_dev_count()) return -1; 176 177 retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf); 178 if (retval != 0) 179 return retval; 180 181 for (q = 0; q < rxRings; q ++) { 182 retval = rte_eth_rx_queue_setup(port, q, rxRingSize, 183 rte_eth_dev_socket_id(port), 184 NULL, 185 mbuf_pool); 186 if (retval < 0) 187 return retval; 188 } 189 190 for (q = 0; q < txRings; q ++) { 191 retval = rte_eth_tx_queue_setup(port, q, txRingSize, 192 rte_eth_dev_socket_id(port), 193 NULL); 194 if (retval < 0) 195 return retval; 196 } 197 198 retval = rte_eth_dev_start(port); 199 if (retval < 0) 200 return retval; 201 202 struct ether_addr addr; 203 rte_eth_macaddr_get(port, &addr); 204 printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 205 " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", 206 (unsigned)port, 207 addr.addr_bytes[0], addr.addr_bytes[1], addr.addr_bytes[2], 208 addr.addr_bytes[3], addr.addr_bytes[4], addr.addr_bytes[5]); 209 210 return 0; 211 } 212 213 /* Check num_pools parameter and set it if OK*/ 214 static int 215 vmdq_parse_num_pools(const char *q_arg) 216 { 217 char *end = NULL; 218 int n; 219 220 /* parse number string */ 221 n = strtol(q_arg, &end, 10); 222 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 223 return -1; 224 if (n != 16 && n != 32) 225 return -1; 226 if (n == 16) 227 num_pools = ETH_16_POOLS; 228 else 229 num_pools = ETH_32_POOLS; 230 231 return 0; 232 } 233 234 static int 235 parse_portmask(const char *portmask) 236 { 237 char *end = NULL; 238 unsigned long pm; 239 240 /* parse hexadecimal string */ 241 pm = strtoul(portmask, &end, 16); 242 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) 243 return -1; 244 245 if (pm == 0) 246 return -1; 247 248 return pm; 249 } 250 251 /* Display usage */ 252 static void 253 vmdq_usage(const char *prgname) 254 { 255 printf("%s [EAL options] -- -p PORTMASK]\n" 256 " --nb-pools NP: number of pools (16 default, 32)\n", 257 prgname); 258 } 259 260 /* Parse the argument (num_pools) given in the command line of the application */ 261 static int 262 vmdq_parse_args(int argc, char **argv) 263 { 264 int opt; 265 int option_index; 266 unsigned i; 267 const char *prgname = argv[0]; 268 static struct option long_option[] = { 269 {"nb-pools", required_argument, NULL, 0}, 270 {NULL, 0, 0, 0} 271 }; 272 273 /* Parse command line */ 274 while ((opt = getopt_long(argc, argv, "p:",long_option,&option_index)) != EOF) { 275 switch (opt) { 276 /* portmask */ 277 case 'p': 278 enabled_port_mask = parse_portmask(optarg); 279 if (enabled_port_mask == 0) { 280 printf("invalid portmask\n"); 281 vmdq_usage(prgname); 282 return -1; 283 } 284 break; 285 case 0: 286 if (vmdq_parse_num_pools(optarg) == -1){ 287 printf("invalid number of pools\n"); 288 vmdq_usage(prgname); 289 return -1; 290 } 291 break; 292 default: 293 vmdq_usage(prgname); 294 return -1; 295 } 296 } 297 298 for(i = 0; i < RTE_MAX_ETHPORTS; i++) 299 { 300 if (enabled_port_mask & (1 << i)) 301 ports[num_ports++] = (uint8_t)i; 302 } 303 304 if (num_ports < 2 || num_ports % 2) { 305 printf("Current enabled port number is %u," 306 "but it should be even and at least 2\n",num_ports); 307 return -1; 308 } 309 310 return 0; 311 } 312 313 314 /* When we receive a HUP signal, print out our stats */ 315 static void 316 sighup_handler(int signum) 317 { 318 unsigned q; 319 for (q = 0; q < NUM_QUEUES; q ++) { 320 if (q % (NUM_QUEUES/num_pools) == 0) 321 printf("\nPool %u: ", q/(NUM_QUEUES/num_pools)); 322 printf("%lu ", rxPackets[ q ]); 323 } 324 printf("\nFinished handling signal %d\n", signum); 325 } 326 327 /* 328 * Main thread that does the work, reading from INPUT_PORT 329 * and writing to OUTPUT_PORT 330 */ 331 static __attribute__((noreturn)) int 332 lcore_main(void *arg) 333 { 334 const uintptr_t core_num = (uintptr_t)arg; 335 const unsigned num_cores = rte_lcore_count(); 336 uint16_t startQueue = (uint16_t)(core_num * (NUM_QUEUES/num_cores)); 337 uint16_t endQueue = (uint16_t)(startQueue + (NUM_QUEUES/num_cores)); 338 uint16_t q, i, p; 339 340 printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num, 341 rte_lcore_id(), startQueue, endQueue - 1); 342 343 for (;;) { 344 struct rte_mbuf *buf[32]; 345 const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]); 346 for (p = 0; p < num_ports; p++) { 347 const uint8_t src = ports[p]; 348 const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */ 349 350 if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID)) 351 continue; 352 353 for (q = startQueue; q < endQueue; q++) { 354 const uint16_t rxCount = rte_eth_rx_burst(src, 355 q, buf, buf_size); 356 if (rxCount == 0) 357 continue; 358 rxPackets[q] += rxCount; 359 360 const uint16_t txCount = rte_eth_tx_burst(dst, 361 (uint16_t)core_num, buf, rxCount); 362 if (txCount != rxCount) { 363 for (i = txCount; i < rxCount; i++) 364 rte_pktmbuf_free(buf[i]); 365 } 366 } 367 } 368 } 369 } 370 371 /* 372 * Update the global var NUM_PORTS and array PORTS according to system ports number 373 * and return valid ports number 374 */ 375 static unsigned check_ports_num(unsigned nb_ports) 376 { 377 unsigned valid_num_ports = num_ports; 378 unsigned portid; 379 380 if (num_ports > nb_ports) { 381 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n", 382 num_ports, nb_ports); 383 num_ports = nb_ports; 384 } 385 386 for (portid = 0; portid < num_ports; portid ++) { 387 if (ports[portid] >= nb_ports) { 388 printf("\nSpecified port ID(%u) exceeds max system port ID(%u)\n", 389 ports[portid], (nb_ports - 1)); 390 ports[portid] = INVALID_PORT_ID; 391 valid_num_ports --; 392 } 393 } 394 return valid_num_ports; 395 } 396 397 398 /* Main function, does initialisation and calls the per-lcore functions */ 399 int 400 main(int argc, char *argv[]) 401 { 402 unsigned cores; 403 struct rte_mempool *mbuf_pool; 404 unsigned lcore_id; 405 uintptr_t i; 406 int ret; 407 unsigned nb_ports, valid_num_ports; 408 uint8_t portid; 409 410 signal(SIGHUP, sighup_handler); 411 412 /* init EAL */ 413 ret = rte_eal_init(argc, argv); 414 if (ret < 0) 415 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); 416 argc -= ret; 417 argv += ret; 418 419 /* parse app arguments */ 420 ret = vmdq_parse_args(argc, argv); 421 if (ret < 0) 422 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n"); 423 424 cores = rte_lcore_count(); 425 if ((cores & (cores - 1)) != 0 || cores > 128) { 426 rte_exit(EXIT_FAILURE,"This program can only run on an even" 427 "number of cores(1-128)\n\n"); 428 } 429 430 nb_ports = rte_eth_dev_count(); 431 if (nb_ports > RTE_MAX_ETHPORTS) 432 nb_ports = RTE_MAX_ETHPORTS; 433 434 /* 435 * Update the global var NUM_PORTS and global array PORTS 436 * and get value of var VALID_NUM_PORTS according to system ports number 437 */ 438 valid_num_ports = check_ports_num(nb_ports); 439 440 if (valid_num_ports < 2 || valid_num_ports % 2) { 441 printf("Current valid ports number is %u\n", valid_num_ports); 442 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n"); 443 } 444 445 mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS * nb_ports, 446 MBUF_SIZE, MBUF_CACHE_SIZE, 447 sizeof(struct rte_pktmbuf_pool_private), 448 rte_pktmbuf_pool_init, NULL, 449 rte_pktmbuf_init, NULL, 450 rte_socket_id(), 0); 451 if (mbuf_pool == NULL) 452 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n"); 453 454 /* initialize all ports */ 455 for (portid = 0; portid < nb_ports; portid++) { 456 /* skip ports that are not enabled */ 457 if ((enabled_port_mask & (1 << portid)) == 0) { 458 printf("\nSkipping disabled port %d\n", portid); 459 continue; 460 } 461 if (port_init(portid, mbuf_pool) != 0) 462 rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n"); 463 } 464 465 /* call lcore_main() on every slave lcore */ 466 i = 0; 467 RTE_LCORE_FOREACH_SLAVE(lcore_id) { 468 rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id); 469 } 470 /* call on master too */ 471 (void) lcore_main((void*)i); 472 473 return 0; 474 } 475