1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation
17 #include <rte_common.h>
19 #include <rte_memory.h>
20 #include <rte_memcpy.h>
22 #include <rte_launch.h>
23 #include <rte_atomic.h>
24 #include <rte_cycles.h>
25 #include <rte_prefetch.h>
26 #include <rte_lcore.h>
27 #include <rte_per_lcore.h>
28 #include <rte_branch_prediction.h>
29 #include <rte_interrupts.h>
30 #include <rte_random.h>
31 #include <rte_debug.h>
32 #include <rte_ether.h>
33 #include <rte_ethdev.h>
34 #include <rte_mempool.h>
37 /* basic constants used in application */
38 #define MAX_QUEUES 1024
40 * 1024 queues require to meet the needs of a large number of vmdq_pools.
41 * (RX/TX_queue_nb * RX/TX_ring_descriptors_nb) per port.
43 #define NUM_MBUFS_PER_PORT (MAX_QUEUES * RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, \
44 RTE_TEST_TX_DESC_DEFAULT))
45 #define MBUF_CACHE_SIZE 64
47 #define MAX_PKT_BURST 32
50 * Configurable number of RX/TX ring descriptors
52 #define RTE_TEST_RX_DESC_DEFAULT 1024
53 #define RTE_TEST_TX_DESC_DEFAULT 1024
55 #define INVALID_PORT_ID 0xFF
57 /* mask of enabled ports */
58 static uint32_t enabled_port_mask;
59 static uint16_t ports[RTE_MAX_ETHPORTS];
60 static unsigned num_ports;
62 /* number of pools (if user does not specify any, 32 by default */
63 static enum rte_eth_nb_pools num_pools = ETH_32_POOLS;
64 static enum rte_eth_nb_tcs num_tcs = ETH_4_TCS;
65 static uint16_t num_queues, num_vmdq_queues;
66 static uint16_t vmdq_pool_base, vmdq_queue_base;
67 static uint8_t rss_enable;
69 /* empty vmdq+dcb configuration structure. Filled in programatically */
70 static const struct rte_eth_conf vmdq_dcb_conf_default = {
72 .mq_mode = ETH_MQ_RX_VMDQ_DCB,
76 .mq_mode = ETH_MQ_TX_VMDQ_DCB,
79 * should be overridden separately in code with
84 .nb_queue_pools = ETH_32_POOLS,
85 .enable_default_pool = 0,
88 .pool_map = {{0, 0},},
93 /** Traffic class each UP mapped to. */
97 .nb_queue_pools = ETH_32_POOLS,
98 .enable_default_pool = 0,
101 .pool_map = {{0, 0},},
105 .vmdq_dcb_tx_conf = {
106 .nb_queue_pools = ETH_32_POOLS,
112 /* array used for printing out statistics */
113 volatile unsigned long rxPackets[MAX_QUEUES] = {0};
115 const uint16_t vlan_tags[] = {
116 0, 1, 2, 3, 4, 5, 6, 7,
117 8, 9, 10, 11, 12, 13, 14, 15,
118 16, 17, 18, 19, 20, 21, 22, 23,
119 24, 25, 26, 27, 28, 29, 30, 31
122 const uint16_t num_vlans = RTE_DIM(vlan_tags);
123 /* pool mac addr template, pool mac addr is like: 52 54 00 12 port# pool# */
124 static struct ether_addr pool_addr_template = {
125 .addr_bytes = {0x52, 0x54, 0x00, 0x12, 0x00, 0x00}
128 /* ethernet addresses of ports */
129 static struct ether_addr vmdq_ports_eth_addr[RTE_MAX_ETHPORTS];
131 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array
132 * given above, and the number of traffic classes available for use. */
134 get_eth_conf(struct rte_eth_conf *eth_conf)
136 struct rte_eth_vmdq_dcb_conf conf;
137 struct rte_eth_vmdq_rx_conf vmdq_conf;
138 struct rte_eth_dcb_rx_conf dcb_conf;
139 struct rte_eth_vmdq_dcb_tx_conf tx_conf;
142 conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
143 vmdq_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
144 tx_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
145 conf.nb_pool_maps = num_pools;
146 vmdq_conf.nb_pool_maps = num_pools;
147 conf.enable_default_pool = 0;
148 vmdq_conf.enable_default_pool = 0;
149 conf.default_pool = 0; /* set explicit value, even if not used */
150 vmdq_conf.default_pool = 0;
152 for (i = 0; i < conf.nb_pool_maps; i++) {
153 conf.pool_map[i].vlan_id = vlan_tags[i];
154 vmdq_conf.pool_map[i].vlan_id = vlan_tags[i];
155 conf.pool_map[i].pools = 1UL << i;
156 vmdq_conf.pool_map[i].pools = 1UL << i;
158 for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){
159 conf.dcb_tc[i] = i % num_tcs;
160 dcb_conf.dcb_tc[i] = i % num_tcs;
161 tx_conf.dcb_tc[i] = i % num_tcs;
163 dcb_conf.nb_tcs = (enum rte_eth_nb_tcs)num_tcs;
164 (void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf)));
165 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_dcb_conf, &conf,
167 (void)(rte_memcpy(ð_conf->rx_adv_conf.dcb_rx_conf, &dcb_conf,
169 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_rx_conf, &vmdq_conf,
171 (void)(rte_memcpy(ð_conf->tx_adv_conf.vmdq_dcb_tx_conf, &tx_conf,
174 eth_conf->rxmode.mq_mode = ETH_MQ_RX_VMDQ_DCB_RSS;
175 eth_conf->rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IP |
184 * Initialises a given port using global settings and with the rx buffers
185 * coming from the mbuf_pool passed as parameter
188 port_init(uint16_t port, struct rte_mempool *mbuf_pool)
190 struct rte_eth_dev_info dev_info;
191 struct rte_eth_conf port_conf = {0};
192 uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT;
193 uint16_t txRingSize = RTE_TEST_TX_DESC_DEFAULT;
196 uint16_t queues_per_pool;
197 uint32_t max_nb_pools;
198 struct rte_eth_txconf txq_conf;
201 * The max pool number from dev_info will be used to validate the pool
202 * number specified in cmd line
204 rte_eth_dev_info_get(port, &dev_info);
205 max_nb_pools = (uint32_t)dev_info.max_vmdq_pools;
207 * We allow to process part of VMDQ pools specified by num_pools in
210 if (num_pools > max_nb_pools) {
211 printf("num_pools %d >max_nb_pools %d\n",
212 num_pools, max_nb_pools);
217 * NIC queues are divided into pf queues and vmdq queues.
218 * There is assumption here all ports have the same configuration!
220 vmdq_queue_base = dev_info.vmdq_queue_base;
221 vmdq_pool_base = dev_info.vmdq_pool_base;
222 printf("vmdq queue base: %d pool base %d\n",
223 vmdq_queue_base, vmdq_pool_base);
224 if (vmdq_pool_base == 0) {
225 num_vmdq_queues = dev_info.max_rx_queues;
226 num_queues = dev_info.max_rx_queues;
227 if (num_tcs != num_vmdq_queues / num_pools) {
228 printf("nb_tcs %d is invalid considering with"
229 " nb_pools %d, nb_tcs * nb_pools should = %d\n",
230 num_tcs, num_pools, num_vmdq_queues);
234 queues_per_pool = dev_info.vmdq_queue_num /
235 dev_info.max_vmdq_pools;
236 if (num_tcs > queues_per_pool) {
237 printf("num_tcs %d > num of queues per pool %d\n",
238 num_tcs, queues_per_pool);
241 num_vmdq_queues = num_pools * queues_per_pool;
242 num_queues = vmdq_queue_base + num_vmdq_queues;
243 printf("Configured vmdq pool num: %u,"
244 " each vmdq pool has %u queues\n",
245 num_pools, queues_per_pool);
248 if (!rte_eth_dev_is_valid_port(port))
251 retval = get_eth_conf(&port_conf);
255 rte_eth_dev_info_get(port, &dev_info);
256 if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
257 port_conf.txmode.offloads |=
258 DEV_TX_OFFLOAD_MBUF_FAST_FREE;
260 * Though in this example, all queues including pf queues are setup.
261 * This is because VMDQ queues doesn't always start from zero, and the
262 * PMD layer doesn't support selectively initialising part of rx/tx
265 retval = rte_eth_dev_configure(port, num_queues, num_queues, &port_conf);
269 retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &rxRingSize,
273 if (RTE_MAX(rxRingSize, txRingSize) >
274 RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT)) {
275 printf("Mbuf pool has an insufficient size for port %u.\n",
280 for (q = 0; q < num_queues; q++) {
281 retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
282 rte_eth_dev_socket_id(port),
286 printf("initialize rx queue %d failed\n", q);
291 txq_conf = dev_info.default_txconf;
292 txq_conf.offloads = port_conf.txmode.offloads;
293 for (q = 0; q < num_queues; q++) {
294 retval = rte_eth_tx_queue_setup(port, q, txRingSize,
295 rte_eth_dev_socket_id(port),
298 printf("initialize tx queue %d failed\n", q);
303 retval = rte_eth_dev_start(port);
305 printf("port %d start failed\n", port);
309 rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]);
310 printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
311 " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
313 vmdq_ports_eth_addr[port].addr_bytes[0],
314 vmdq_ports_eth_addr[port].addr_bytes[1],
315 vmdq_ports_eth_addr[port].addr_bytes[2],
316 vmdq_ports_eth_addr[port].addr_bytes[3],
317 vmdq_ports_eth_addr[port].addr_bytes[4],
318 vmdq_ports_eth_addr[port].addr_bytes[5]);
320 /* Set mac for each pool.*/
321 for (q = 0; q < num_pools; q++) {
322 struct ether_addr mac;
324 mac = pool_addr_template;
325 mac.addr_bytes[4] = port;
326 mac.addr_bytes[5] = q;
327 printf("Port %u vmdq pool %u set mac %02x:%02x:%02x:%02x:%02x:%02x\n",
329 mac.addr_bytes[0], mac.addr_bytes[1],
330 mac.addr_bytes[2], mac.addr_bytes[3],
331 mac.addr_bytes[4], mac.addr_bytes[5]);
332 retval = rte_eth_dev_mac_addr_add(port, &mac,
335 printf("mac addr add failed at pool %d\n", q);
343 /* Check num_pools parameter and set it if OK*/
345 vmdq_parse_num_pools(const char *q_arg)
350 /* parse number string */
351 n = strtol(q_arg, &end, 10);
352 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
354 if (n != 16 && n != 32)
357 num_pools = ETH_16_POOLS;
359 num_pools = ETH_32_POOLS;
364 /* Check num_tcs parameter and set it if OK*/
366 vmdq_parse_num_tcs(const char *q_arg)
371 /* parse number string */
372 n = strtol(q_arg, &end, 10);
373 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
376 if (n != 4 && n != 8)
387 parse_portmask(const char *portmask)
392 /* parse hexadecimal string */
393 pm = strtoul(portmask, &end, 16);
394 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
405 vmdq_usage(const char *prgname)
407 printf("%s [EAL options] -- -p PORTMASK]\n"
408 " --nb-pools NP: number of pools (32 default, 16)\n"
409 " --nb-tcs NP: number of TCs (4 default, 8)\n"
410 " --enable-rss: enable RSS (disabled by default)\n",
414 /* Parse the argument (num_pools) given in the command line of the application */
416 vmdq_parse_args(int argc, char **argv)
421 const char *prgname = argv[0];
422 static struct option long_option[] = {
423 {"nb-pools", required_argument, NULL, 0},
424 {"nb-tcs", required_argument, NULL, 0},
425 {"enable-rss", 0, NULL, 0},
429 /* Parse command line */
430 while ((opt = getopt_long(argc, argv, "p:", long_option,
431 &option_index)) != EOF) {
435 enabled_port_mask = parse_portmask(optarg);
436 if (enabled_port_mask == 0) {
437 printf("invalid portmask\n");
443 if (!strcmp(long_option[option_index].name, "nb-pools")) {
444 if (vmdq_parse_num_pools(optarg) == -1) {
445 printf("invalid number of pools\n");
450 if (!strcmp(long_option[option_index].name, "nb-tcs")) {
451 if (vmdq_parse_num_tcs(optarg) == -1) {
452 printf("invalid number of tcs\n");
457 if (!strcmp(long_option[option_index].name, "enable-rss"))
467 for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
468 if (enabled_port_mask & (1 << i))
469 ports[num_ports++] = (uint8_t)i;
472 if (num_ports < 2 || num_ports % 2) {
473 printf("Current enabled port number is %u,"
474 " but it should be even and at least 2\n", num_ports);
482 update_mac_address(struct rte_mbuf *m, unsigned dst_port)
484 struct ether_hdr *eth;
487 eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
489 /* 02:00:00:00:00:xx */
490 tmp = ð->d_addr.addr_bytes[0];
491 *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
494 ether_addr_copy(&vmdq_ports_eth_addr[dst_port], ð->s_addr);
497 /* When we receive a HUP signal, print out our stats */
499 sighup_handler(int signum)
501 unsigned q = vmdq_queue_base;
503 for (; q < num_queues; q++) {
504 if (q % (num_vmdq_queues / num_pools) == 0)
505 printf("\nPool %u: ", (q - vmdq_queue_base) /
506 (num_vmdq_queues / num_pools));
507 printf("%lu ", rxPackets[q]);
509 printf("\nFinished handling signal %d\n", signum);
513 * Main thread that does the work, reading from INPUT_PORT
514 * and writing to OUTPUT_PORT
517 lcore_main(void *arg)
519 const uintptr_t core_num = (uintptr_t)arg;
520 const unsigned num_cores = rte_lcore_count();
521 uint16_t startQueue, endQueue;
523 const uint16_t quot = (uint16_t)(num_vmdq_queues / num_cores);
524 const uint16_t remainder = (uint16_t)(num_vmdq_queues % num_cores);
528 if (core_num < remainder) {
529 startQueue = (uint16_t)(core_num * (quot + 1));
530 endQueue = (uint16_t)(startQueue + quot + 1);
532 startQueue = (uint16_t)(core_num * quot + remainder);
533 endQueue = (uint16_t)(startQueue + quot);
536 startQueue = (uint16_t)(core_num * quot);
537 endQueue = (uint16_t)(startQueue + quot);
540 /* vmdq queue idx doesn't always start from zero.*/
541 startQueue += vmdq_queue_base;
542 endQueue += vmdq_queue_base;
543 printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num,
544 rte_lcore_id(), startQueue, endQueue - 1);
546 if (startQueue == endQueue) {
547 printf("lcore %u has nothing to do\n", (unsigned)core_num);
552 struct rte_mbuf *buf[MAX_PKT_BURST];
553 const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]);
554 for (p = 0; p < num_ports; p++) {
555 const uint8_t src = ports[p];
556 const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
558 if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID))
561 for (q = startQueue; q < endQueue; q++) {
562 const uint16_t rxCount = rte_eth_rx_burst(src,
565 if (unlikely(rxCount == 0))
568 rxPackets[q] += rxCount;
570 for (i = 0; i < rxCount; i++)
571 update_mac_address(buf[i], dst);
573 const uint16_t txCount = rte_eth_tx_burst(dst,
575 if (txCount != rxCount) {
576 for (i = txCount; i < rxCount; i++)
577 rte_pktmbuf_free(buf[i]);
585 * Update the global var NUM_PORTS and array PORTS according to system ports number
586 * and return valid ports number
588 static unsigned check_ports_num(unsigned nb_ports)
590 unsigned valid_num_ports = num_ports;
593 if (num_ports > nb_ports) {
594 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
595 num_ports, nb_ports);
596 num_ports = nb_ports;
599 for (portid = 0; portid < num_ports; portid++) {
600 if (!rte_eth_dev_is_valid_port(ports[portid])) {
601 printf("\nSpecified port ID(%u) is not valid\n",
603 ports[portid] = INVALID_PORT_ID;
607 return valid_num_ports;
611 /* Main function, does initialisation and calls the per-lcore functions */
613 main(int argc, char *argv[])
616 struct rte_mempool *mbuf_pool;
620 unsigned nb_ports, valid_num_ports;
623 signal(SIGHUP, sighup_handler);
626 ret = rte_eal_init(argc, argv);
628 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
632 /* parse app arguments */
633 ret = vmdq_parse_args(argc, argv);
635 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
637 cores = rte_lcore_count();
638 if ((cores & (cores - 1)) != 0 || cores > RTE_MAX_LCORE) {
639 rte_exit(EXIT_FAILURE,"This program can only run on an even"
640 " number of cores(1-%d)\n\n", RTE_MAX_LCORE);
643 nb_ports = rte_eth_dev_count_avail();
646 * Update the global var NUM_PORTS and global array PORTS
647 * and get value of var VALID_NUM_PORTS according to system ports number
649 valid_num_ports = check_ports_num(nb_ports);
651 if (valid_num_ports < 2 || valid_num_ports % 2) {
652 printf("Current valid ports number is %u\n", valid_num_ports);
653 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
656 mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
657 NUM_MBUFS_PER_PORT * nb_ports, MBUF_CACHE_SIZE,
658 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
659 if (mbuf_pool == NULL)
660 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
662 /* initialize all ports */
663 RTE_ETH_FOREACH_DEV(portid) {
664 /* skip ports that are not enabled */
665 if ((enabled_port_mask & (1 << portid)) == 0) {
666 printf("\nSkipping disabled port %d\n", portid);
669 if (port_init(portid, mbuf_pool) != 0)
670 rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
673 /* call lcore_main() on every slave lcore */
675 RTE_LCORE_FOREACH_SLAVE(lcore_id) {
676 rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id);
678 /* call on master too */
679 (void) lcore_main((void*)i);