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