4 * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
35 #include <sys/queue.h>
46 #include <rte_common.h>
48 #include <rte_memory.h>
49 #include <rte_memcpy.h>
50 #include <rte_memzone.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>
62 #include <rte_random.h>
63 #include <rte_debug.h>
64 #include <rte_ether.h>
65 #include <rte_ethdev.h>
68 #include <rte_mempool.h>
70 #include <rte_memcpy.h>
72 /* basic constants used in application */
73 #define NUM_QUEUES 128
75 #define NUM_MBUFS 64*1024
76 #define MBUF_CACHE_SIZE 64
78 #define INVALID_PORT_ID 0xFF
80 /* mask of enabled ports */
81 static uint32_t enabled_port_mask = 0;
83 /* number of pools (if user does not specify any, 16 by default */
84 static enum rte_eth_nb_pools num_pools = ETH_16_POOLS;
86 /* empty vmdq+dcb configuration structure. Filled in programatically */
87 static const struct rte_eth_conf vmdq_dcb_conf_default = {
89 .mq_mode = ETH_MQ_RX_VMDQ_DCB,
91 .header_split = 0, /**< Header Split disabled */
92 .hw_ip_checksum = 0, /**< IP checksum offload disabled */
93 .hw_vlan_filter = 0, /**< VLAN filtering disabled */
94 .jumbo_frame = 0, /**< Jumbo Frame Support disabled */
97 .mq_mode = ETH_MQ_TX_NONE,
101 * should be overridden separately in code with
105 .nb_queue_pools = ETH_16_POOLS,
106 .enable_default_pool = 0,
109 .pool_map = {{0, 0},},
115 static uint8_t ports[RTE_MAX_ETHPORTS];
116 static unsigned num_ports = 0;
118 /* array used for printing out statistics */
119 volatile unsigned long rxPackets[ NUM_QUEUES ] = {0};
121 const uint16_t vlan_tags[] = {
122 0, 1, 2, 3, 4, 5, 6, 7,
123 8, 9, 10, 11, 12, 13, 14, 15,
124 16, 17, 18, 19, 20, 21, 22, 23,
125 24, 25, 26, 27, 28, 29, 30, 31
128 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array
129 * given above, and the number of traffic classes available for use. */
131 get_eth_conf(struct rte_eth_conf *eth_conf, enum rte_eth_nb_pools num_pools)
133 struct rte_eth_vmdq_dcb_conf conf;
136 if (num_pools != ETH_16_POOLS && num_pools != ETH_32_POOLS ) return -1;
138 conf.nb_queue_pools = num_pools;
139 conf.enable_default_pool = 0;
140 conf.default_pool = 0; /* set explicit value, even if not used */
141 conf.nb_pool_maps = sizeof( vlan_tags )/sizeof( vlan_tags[ 0 ]);
142 for (i = 0; i < conf.nb_pool_maps; i++){
143 conf.pool_map[i].vlan_id = vlan_tags[ i ];
144 conf.pool_map[i].pools = 1 << (i % num_pools);
146 for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){
147 conf.dcb_tc[i] = (uint8_t)(i % (NUM_QUEUES/num_pools));
149 (void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf)));
150 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_dcb_conf, &conf,
151 sizeof(eth_conf->rx_adv_conf.vmdq_dcb_conf)));
156 * Initialises a given port using global settings and with the rx buffers
157 * coming from the mbuf_pool passed as parameter
160 port_init(uint8_t port, struct rte_mempool *mbuf_pool)
162 struct rte_eth_conf port_conf;
163 const uint16_t rxRings = ETH_VMDQ_DCB_NUM_QUEUES,
164 txRings = (uint16_t)rte_lcore_count();
165 const uint16_t rxRingSize = 128, txRingSize = 512;
169 retval = get_eth_conf(&port_conf, num_pools);
173 if (port >= rte_eth_dev_count()) return -1;
175 retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf);
179 for (q = 0; q < rxRings; q ++) {
180 retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
181 rte_eth_dev_socket_id(port),
188 for (q = 0; q < txRings; q ++) {
189 retval = rte_eth_tx_queue_setup(port, q, txRingSize,
190 rte_eth_dev_socket_id(port),
196 retval = rte_eth_dev_start(port);
200 struct ether_addr addr;
201 rte_eth_macaddr_get(port, &addr);
202 printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
203 " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
205 addr.addr_bytes[0], addr.addr_bytes[1], addr.addr_bytes[2],
206 addr.addr_bytes[3], addr.addr_bytes[4], addr.addr_bytes[5]);
211 /* Check num_pools parameter and set it if OK*/
213 vmdq_parse_num_pools(const char *q_arg)
218 /* parse number string */
219 n = strtol(q_arg, &end, 10);
220 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
222 if (n != 16 && n != 32)
225 num_pools = ETH_16_POOLS;
227 num_pools = ETH_32_POOLS;
233 parse_portmask(const char *portmask)
238 /* parse hexadecimal string */
239 pm = strtoul(portmask, &end, 16);
240 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
251 vmdq_usage(const char *prgname)
253 printf("%s [EAL options] -- -p PORTMASK]\n"
254 " --nb-pools NP: number of pools (16 default, 32)\n",
258 /* Parse the argument (num_pools) given in the command line of the application */
260 vmdq_parse_args(int argc, char **argv)
265 const char *prgname = argv[0];
266 static struct option long_option[] = {
267 {"nb-pools", required_argument, NULL, 0},
271 /* Parse command line */
272 while ((opt = getopt_long(argc, argv, "p:",long_option,&option_index)) != EOF) {
276 enabled_port_mask = parse_portmask(optarg);
277 if (enabled_port_mask == 0) {
278 printf("invalid portmask\n");
284 if (vmdq_parse_num_pools(optarg) == -1){
285 printf("invalid number of pools\n");
296 for(i = 0; i < RTE_MAX_ETHPORTS; i++)
298 if (enabled_port_mask & (1 << i))
299 ports[num_ports++] = (uint8_t)i;
302 if (num_ports < 2 || num_ports % 2) {
303 printf("Current enabled port number is %u,"
304 "but it should be even and at least 2\n",num_ports);
312 /* When we receive a HUP signal, print out our stats */
314 sighup_handler(int signum)
317 for (q = 0; q < NUM_QUEUES; q ++) {
318 if (q % (NUM_QUEUES/num_pools) == 0)
319 printf("\nPool %u: ", q/(NUM_QUEUES/num_pools));
320 printf("%lu ", rxPackets[ q ]);
322 printf("\nFinished handling signal %d\n", signum);
326 * Main thread that does the work, reading from INPUT_PORT
327 * and writing to OUTPUT_PORT
329 static __attribute__((noreturn)) int
330 lcore_main(void *arg)
332 const uintptr_t core_num = (uintptr_t)arg;
333 const unsigned num_cores = rte_lcore_count();
334 uint16_t startQueue = (uint16_t)(core_num * (NUM_QUEUES/num_cores));
335 uint16_t endQueue = (uint16_t)(startQueue + (NUM_QUEUES/num_cores));
338 printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num,
339 rte_lcore_id(), startQueue, endQueue - 1);
342 struct rte_mbuf *buf[32];
343 const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]);
344 for (p = 0; p < num_ports; p++) {
345 const uint8_t src = ports[p];
346 const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
348 if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID))
351 for (q = startQueue; q < endQueue; q++) {
352 const uint16_t rxCount = rte_eth_rx_burst(src,
356 rxPackets[q] += rxCount;
358 const uint16_t txCount = rte_eth_tx_burst(dst,
359 (uint16_t)core_num, buf, rxCount);
360 if (txCount != rxCount) {
361 for (i = txCount; i < rxCount; i++)
362 rte_pktmbuf_free(buf[i]);
370 * Update the global var NUM_PORTS and array PORTS according to system ports number
371 * and return valid ports number
373 static unsigned check_ports_num(unsigned nb_ports)
375 unsigned valid_num_ports = num_ports;
378 if (num_ports > nb_ports) {
379 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
380 num_ports, nb_ports);
381 num_ports = nb_ports;
384 for (portid = 0; portid < num_ports; portid ++) {
385 if (ports[portid] >= nb_ports) {
386 printf("\nSpecified port ID(%u) exceeds max system port ID(%u)\n",
387 ports[portid], (nb_ports - 1));
388 ports[portid] = INVALID_PORT_ID;
392 return valid_num_ports;
396 /* Main function, does initialisation and calls the per-lcore functions */
398 main(int argc, char *argv[])
401 struct rte_mempool *mbuf_pool;
405 unsigned nb_ports, valid_num_ports;
408 signal(SIGHUP, sighup_handler);
411 ret = rte_eal_init(argc, argv);
413 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
417 /* parse app arguments */
418 ret = vmdq_parse_args(argc, argv);
420 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
422 cores = rte_lcore_count();
423 if ((cores & (cores - 1)) != 0 || cores > 128) {
424 rte_exit(EXIT_FAILURE,"This program can only run on an even"
425 "number of cores(1-128)\n\n");
428 nb_ports = rte_eth_dev_count();
429 if (nb_ports > RTE_MAX_ETHPORTS)
430 nb_ports = RTE_MAX_ETHPORTS;
433 * Update the global var NUM_PORTS and global array PORTS
434 * and get value of var VALID_NUM_PORTS according to system ports number
436 valid_num_ports = check_ports_num(nb_ports);
438 if (valid_num_ports < 2 || valid_num_ports % 2) {
439 printf("Current valid ports number is %u\n", valid_num_ports);
440 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
443 mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
444 MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
445 if (mbuf_pool == NULL)
446 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
448 /* initialize all ports */
449 for (portid = 0; portid < nb_ports; portid++) {
450 /* skip ports that are not enabled */
451 if ((enabled_port_mask & (1 << portid)) == 0) {
452 printf("\nSkipping disabled port %d\n", portid);
455 if (port_init(portid, mbuf_pool) != 0)
456 rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
459 /* call lcore_main() on every slave lcore */
461 RTE_LCORE_FOREACH_SLAVE(lcore_id) {
462 rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id);
464 /* call on master too */
465 (void) lcore_main((void*)i);