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 128
53 #define RTE_TEST_TX_DESC_DEFAULT 512
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,
74 .header_split = 0, /**< Header Split disabled */
75 .hw_ip_checksum = 0, /**< IP checksum offload disabled */
76 .hw_vlan_filter = 0, /**< VLAN filtering disabled */
77 .jumbo_frame = 0, /**< Jumbo Frame Support disabled */
80 .mq_mode = ETH_MQ_TX_VMDQ_DCB,
83 * should be overridden separately in code with
88 .nb_queue_pools = ETH_32_POOLS,
89 .enable_default_pool = 0,
92 .pool_map = {{0, 0},},
97 /** Traffic class each UP mapped to. */
101 .nb_queue_pools = ETH_32_POOLS,
102 .enable_default_pool = 0,
105 .pool_map = {{0, 0},},
109 .vmdq_dcb_tx_conf = {
110 .nb_queue_pools = ETH_32_POOLS,
116 /* array used for printing out statistics */
117 volatile unsigned long rxPackets[MAX_QUEUES] = {0};
119 const uint16_t vlan_tags[] = {
120 0, 1, 2, 3, 4, 5, 6, 7,
121 8, 9, 10, 11, 12, 13, 14, 15,
122 16, 17, 18, 19, 20, 21, 22, 23,
123 24, 25, 26, 27, 28, 29, 30, 31
126 const uint16_t num_vlans = RTE_DIM(vlan_tags);
127 /* pool mac addr template, pool mac addr is like: 52 54 00 12 port# pool# */
128 static struct ether_addr pool_addr_template = {
129 .addr_bytes = {0x52, 0x54, 0x00, 0x12, 0x00, 0x00}
132 /* ethernet addresses of ports */
133 static struct ether_addr vmdq_ports_eth_addr[RTE_MAX_ETHPORTS];
135 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array
136 * given above, and the number of traffic classes available for use. */
138 get_eth_conf(struct rte_eth_conf *eth_conf)
140 struct rte_eth_vmdq_dcb_conf conf;
141 struct rte_eth_vmdq_rx_conf vmdq_conf;
142 struct rte_eth_dcb_rx_conf dcb_conf;
143 struct rte_eth_vmdq_dcb_tx_conf tx_conf;
146 conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
147 vmdq_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
148 tx_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
149 conf.nb_pool_maps = num_pools;
150 vmdq_conf.nb_pool_maps = num_pools;
151 conf.enable_default_pool = 0;
152 vmdq_conf.enable_default_pool = 0;
153 conf.default_pool = 0; /* set explicit value, even if not used */
154 vmdq_conf.default_pool = 0;
156 for (i = 0; i < conf.nb_pool_maps; i++) {
157 conf.pool_map[i].vlan_id = vlan_tags[i];
158 vmdq_conf.pool_map[i].vlan_id = vlan_tags[i];
159 conf.pool_map[i].pools = 1UL << i;
160 vmdq_conf.pool_map[i].pools = 1UL << i;
162 for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){
163 conf.dcb_tc[i] = i % num_tcs;
164 dcb_conf.dcb_tc[i] = i % num_tcs;
165 tx_conf.dcb_tc[i] = i % num_tcs;
167 dcb_conf.nb_tcs = (enum rte_eth_nb_tcs)num_tcs;
168 (void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf)));
169 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_dcb_conf, &conf,
171 (void)(rte_memcpy(ð_conf->rx_adv_conf.dcb_rx_conf, &dcb_conf,
173 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_rx_conf, &vmdq_conf,
175 (void)(rte_memcpy(ð_conf->tx_adv_conf.vmdq_dcb_tx_conf, &tx_conf,
178 eth_conf->rxmode.mq_mode = ETH_MQ_RX_VMDQ_DCB_RSS;
179 eth_conf->rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IP |
188 * Initialises a given port using global settings and with the rx buffers
189 * coming from the mbuf_pool passed as parameter
192 port_init(uint16_t port, struct rte_mempool *mbuf_pool)
194 struct rte_eth_dev_info dev_info;
195 struct rte_eth_conf port_conf = {0};
196 uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT;
197 uint16_t txRingSize = RTE_TEST_TX_DESC_DEFAULT;
200 uint16_t queues_per_pool;
201 uint32_t max_nb_pools;
204 * The max pool number from dev_info will be used to validate the pool
205 * number specified in cmd line
207 rte_eth_dev_info_get(port, &dev_info);
208 max_nb_pools = (uint32_t)dev_info.max_vmdq_pools;
210 * We allow to process part of VMDQ pools specified by num_pools in
213 if (num_pools > max_nb_pools) {
214 printf("num_pools %d >max_nb_pools %d\n",
215 num_pools, max_nb_pools);
220 * NIC queues are divided into pf queues and vmdq queues.
221 * There is assumption here all ports have the same configuration!
223 vmdq_queue_base = dev_info.vmdq_queue_base;
224 vmdq_pool_base = dev_info.vmdq_pool_base;
225 printf("vmdq queue base: %d pool base %d\n",
226 vmdq_queue_base, vmdq_pool_base);
227 if (vmdq_pool_base == 0) {
228 num_vmdq_queues = dev_info.max_rx_queues;
229 num_queues = dev_info.max_rx_queues;
230 if (num_tcs != num_vmdq_queues / num_pools) {
231 printf("nb_tcs %d is invalid considering with"
232 " nb_pools %d, nb_tcs * nb_pools should = %d\n",
233 num_tcs, num_pools, num_vmdq_queues);
237 queues_per_pool = dev_info.vmdq_queue_num /
238 dev_info.max_vmdq_pools;
239 if (num_tcs > queues_per_pool) {
240 printf("num_tcs %d > num of queues per pool %d\n",
241 num_tcs, queues_per_pool);
244 num_vmdq_queues = num_pools * queues_per_pool;
245 num_queues = vmdq_queue_base + num_vmdq_queues;
246 printf("Configured vmdq pool num: %u,"
247 " each vmdq pool has %u queues\n",
248 num_pools, queues_per_pool);
251 if (port >= rte_eth_dev_count())
254 retval = get_eth_conf(&port_conf);
259 * Though in this example, all queues including pf queues are setup.
260 * This is because VMDQ queues doesn't always start from zero, and the
261 * PMD layer doesn't support selectively initialising part of rx/tx
264 retval = rte_eth_dev_configure(port, num_queues, num_queues, &port_conf);
268 retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &rxRingSize,
272 if (RTE_MAX(rxRingSize, txRingSize) >
273 RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT)) {
274 printf("Mbuf pool has an insufficient size for port %u.\n",
279 for (q = 0; q < num_queues; q++) {
280 retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
281 rte_eth_dev_socket_id(port),
285 printf("initialize rx queue %d failed\n", q);
290 for (q = 0; q < num_queues; q++) {
291 retval = rte_eth_tx_queue_setup(port, q, txRingSize,
292 rte_eth_dev_socket_id(port),
295 printf("initialize tx queue %d failed\n", q);
300 retval = rte_eth_dev_start(port);
302 printf("port %d start failed\n", port);
306 rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]);
307 printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
308 " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
310 vmdq_ports_eth_addr[port].addr_bytes[0],
311 vmdq_ports_eth_addr[port].addr_bytes[1],
312 vmdq_ports_eth_addr[port].addr_bytes[2],
313 vmdq_ports_eth_addr[port].addr_bytes[3],
314 vmdq_ports_eth_addr[port].addr_bytes[4],
315 vmdq_ports_eth_addr[port].addr_bytes[5]);
317 /* Set mac for each pool.*/
318 for (q = 0; q < num_pools; q++) {
319 struct ether_addr mac;
321 mac = pool_addr_template;
322 mac.addr_bytes[4] = port;
323 mac.addr_bytes[5] = q;
324 printf("Port %u vmdq pool %u set mac %02x:%02x:%02x:%02x:%02x:%02x\n",
326 mac.addr_bytes[0], mac.addr_bytes[1],
327 mac.addr_bytes[2], mac.addr_bytes[3],
328 mac.addr_bytes[4], mac.addr_bytes[5]);
329 retval = rte_eth_dev_mac_addr_add(port, &mac,
332 printf("mac addr add failed at pool %d\n", q);
340 /* Check num_pools parameter and set it if OK*/
342 vmdq_parse_num_pools(const char *q_arg)
347 /* parse number string */
348 n = strtol(q_arg, &end, 10);
349 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
351 if (n != 16 && n != 32)
354 num_pools = ETH_16_POOLS;
356 num_pools = ETH_32_POOLS;
361 /* Check num_tcs parameter and set it if OK*/
363 vmdq_parse_num_tcs(const char *q_arg)
368 /* parse number string */
369 n = strtol(q_arg, &end, 10);
370 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
373 if (n != 4 && n != 8)
384 parse_portmask(const char *portmask)
389 /* parse hexadecimal string */
390 pm = strtoul(portmask, &end, 16);
391 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
402 vmdq_usage(const char *prgname)
404 printf("%s [EAL options] -- -p PORTMASK]\n"
405 " --nb-pools NP: number of pools (32 default, 16)\n"
406 " --nb-tcs NP: number of TCs (4 default, 8)\n"
407 " --enable-rss: enable RSS (disabled by default)\n",
411 /* Parse the argument (num_pools) given in the command line of the application */
413 vmdq_parse_args(int argc, char **argv)
418 const char *prgname = argv[0];
419 static struct option long_option[] = {
420 {"nb-pools", required_argument, NULL, 0},
421 {"nb-tcs", required_argument, NULL, 0},
422 {"enable-rss", 0, NULL, 0},
426 /* Parse command line */
427 while ((opt = getopt_long(argc, argv, "p:", long_option,
428 &option_index)) != EOF) {
432 enabled_port_mask = parse_portmask(optarg);
433 if (enabled_port_mask == 0) {
434 printf("invalid portmask\n");
440 if (!strcmp(long_option[option_index].name, "nb-pools")) {
441 if (vmdq_parse_num_pools(optarg) == -1) {
442 printf("invalid number of pools\n");
447 if (!strcmp(long_option[option_index].name, "nb-tcs")) {
448 if (vmdq_parse_num_tcs(optarg) == -1) {
449 printf("invalid number of tcs\n");
454 if (!strcmp(long_option[option_index].name, "enable-rss"))
464 for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
465 if (enabled_port_mask & (1 << i))
466 ports[num_ports++] = (uint8_t)i;
469 if (num_ports < 2 || num_ports % 2) {
470 printf("Current enabled port number is %u,"
471 " but it should be even and at least 2\n", num_ports);
479 update_mac_address(struct rte_mbuf *m, unsigned dst_port)
481 struct ether_hdr *eth;
484 eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
486 /* 02:00:00:00:00:xx */
487 tmp = ð->d_addr.addr_bytes[0];
488 *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
491 ether_addr_copy(&vmdq_ports_eth_addr[dst_port], ð->s_addr);
494 /* When we receive a HUP signal, print out our stats */
496 sighup_handler(int signum)
498 unsigned q = vmdq_queue_base;
500 for (; q < num_queues; q++) {
501 if (q % (num_vmdq_queues / num_pools) == 0)
502 printf("\nPool %u: ", (q - vmdq_queue_base) /
503 (num_vmdq_queues / num_pools));
504 printf("%lu ", rxPackets[q]);
506 printf("\nFinished handling signal %d\n", signum);
510 * Main thread that does the work, reading from INPUT_PORT
511 * and writing to OUTPUT_PORT
514 lcore_main(void *arg)
516 const uintptr_t core_num = (uintptr_t)arg;
517 const unsigned num_cores = rte_lcore_count();
518 uint16_t startQueue, endQueue;
520 const uint16_t quot = (uint16_t)(num_vmdq_queues / num_cores);
521 const uint16_t remainder = (uint16_t)(num_vmdq_queues % num_cores);
525 if (core_num < remainder) {
526 startQueue = (uint16_t)(core_num * (quot + 1));
527 endQueue = (uint16_t)(startQueue + quot + 1);
529 startQueue = (uint16_t)(core_num * quot + remainder);
530 endQueue = (uint16_t)(startQueue + quot);
533 startQueue = (uint16_t)(core_num * quot);
534 endQueue = (uint16_t)(startQueue + quot);
537 /* vmdq queue idx doesn't always start from zero.*/
538 startQueue += vmdq_queue_base;
539 endQueue += vmdq_queue_base;
540 printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num,
541 rte_lcore_id(), startQueue, endQueue - 1);
543 if (startQueue == endQueue) {
544 printf("lcore %u has nothing to do\n", (unsigned)core_num);
549 struct rte_mbuf *buf[MAX_PKT_BURST];
550 const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]);
551 for (p = 0; p < num_ports; p++) {
552 const uint8_t src = ports[p];
553 const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
555 if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID))
558 for (q = startQueue; q < endQueue; q++) {
559 const uint16_t rxCount = rte_eth_rx_burst(src,
562 if (unlikely(rxCount == 0))
565 rxPackets[q] += rxCount;
567 for (i = 0; i < rxCount; i++)
568 update_mac_address(buf[i], dst);
570 const uint16_t txCount = rte_eth_tx_burst(dst,
572 if (txCount != rxCount) {
573 for (i = txCount; i < rxCount; i++)
574 rte_pktmbuf_free(buf[i]);
582 * Update the global var NUM_PORTS and array PORTS according to system ports number
583 * and return valid ports number
585 static unsigned check_ports_num(unsigned nb_ports)
587 unsigned valid_num_ports = num_ports;
590 if (num_ports > nb_ports) {
591 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
592 num_ports, nb_ports);
593 num_ports = nb_ports;
596 for (portid = 0; portid < num_ports; portid++) {
597 if (ports[portid] >= nb_ports) {
598 printf("\nSpecified port ID(%u) exceeds max system port ID(%u)\n",
599 ports[portid], (nb_ports - 1));
600 ports[portid] = INVALID_PORT_ID;
604 return valid_num_ports;
608 /* Main function, does initialisation and calls the per-lcore functions */
610 main(int argc, char *argv[])
613 struct rte_mempool *mbuf_pool;
617 unsigned nb_ports, valid_num_ports;
620 signal(SIGHUP, sighup_handler);
623 ret = rte_eal_init(argc, argv);
625 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
629 /* parse app arguments */
630 ret = vmdq_parse_args(argc, argv);
632 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
634 cores = rte_lcore_count();
635 if ((cores & (cores - 1)) != 0 || cores > RTE_MAX_LCORE) {
636 rte_exit(EXIT_FAILURE,"This program can only run on an even"
637 " number of cores(1-%d)\n\n", RTE_MAX_LCORE);
640 nb_ports = rte_eth_dev_count();
643 * Update the global var NUM_PORTS and global array PORTS
644 * and get value of var VALID_NUM_PORTS according to system ports number
646 valid_num_ports = check_ports_num(nb_ports);
648 if (valid_num_ports < 2 || valid_num_ports % 2) {
649 printf("Current valid ports number is %u\n", valid_num_ports);
650 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
653 mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
654 NUM_MBUFS_PER_PORT * nb_ports, MBUF_CACHE_SIZE,
655 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
656 if (mbuf_pool == NULL)
657 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
659 /* initialize all ports */
660 for (portid = 0; portid < nb_ports; portid++) {
661 /* skip ports that are not enabled */
662 if ((enabled_port_mask & (1 << portid)) == 0) {
663 printf("\nSkipping disabled port %d\n", portid);
666 if (port_init(portid, mbuf_pool) != 0)
667 rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
670 /* call lcore_main() on every slave lcore */
672 RTE_LCORE_FOREACH_SLAVE(lcore_id) {
673 rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id);
675 /* call on master too */
676 (void) lcore_main((void*)i);