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 programmatically. 8< */
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,
111 /* >8 End of empty vmdq+dcb configuration structure. */
113 /* array used for printing out statistics */
114 volatile unsigned long rxPackets[MAX_QUEUES] = {0};
116 /* Dividing up the possible user priority values. 8< */
117 const uint16_t vlan_tags[] = {
118 0, 1, 2, 3, 4, 5, 6, 7,
119 8, 9, 10, 11, 12, 13, 14, 15,
120 16, 17, 18, 19, 20, 21, 22, 23,
121 24, 25, 26, 27, 28, 29, 30, 31
124 const uint16_t num_vlans = RTE_DIM(vlan_tags);
125 /* pool mac addr template, pool mac addr is like: 52 54 00 12 port# pool# */
126 static struct rte_ether_addr pool_addr_template = {
127 .addr_bytes = {0x52, 0x54, 0x00, 0x12, 0x00, 0x00}
130 /* ethernet addresses of ports */
131 static struct rte_ether_addr vmdq_ports_eth_addr[RTE_MAX_ETHPORTS];
133 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array
134 * given above, and the number of traffic classes available for use. */
136 get_eth_conf(struct rte_eth_conf *eth_conf)
138 struct rte_eth_vmdq_dcb_conf conf;
139 struct rte_eth_vmdq_rx_conf vmdq_conf;
140 struct rte_eth_dcb_rx_conf dcb_conf;
141 struct rte_eth_vmdq_dcb_tx_conf tx_conf;
144 conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
145 vmdq_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
146 tx_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
147 conf.nb_pool_maps = num_pools;
148 vmdq_conf.nb_pool_maps = num_pools;
149 conf.enable_default_pool = 0;
150 vmdq_conf.enable_default_pool = 0;
151 conf.default_pool = 0; /* set explicit value, even if not used */
152 vmdq_conf.default_pool = 0;
154 for (i = 0; i < conf.nb_pool_maps; i++) {
155 conf.pool_map[i].vlan_id = vlan_tags[i];
156 vmdq_conf.pool_map[i].vlan_id = vlan_tags[i];
157 conf.pool_map[i].pools = 1UL << i;
158 vmdq_conf.pool_map[i].pools = 1UL << i;
160 for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){
161 conf.dcb_tc[i] = i % num_tcs;
162 dcb_conf.dcb_tc[i] = i % num_tcs;
163 tx_conf.dcb_tc[i] = i % num_tcs;
165 dcb_conf.nb_tcs = (enum rte_eth_nb_tcs)num_tcs;
166 (void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf)));
167 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_dcb_conf, &conf,
169 (void)(rte_memcpy(ð_conf->rx_adv_conf.dcb_rx_conf, &dcb_conf,
171 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_rx_conf, &vmdq_conf,
173 (void)(rte_memcpy(ð_conf->tx_adv_conf.vmdq_dcb_tx_conf, &tx_conf,
176 eth_conf->rxmode.mq_mode = ETH_MQ_RX_VMDQ_DCB_RSS;
177 eth_conf->rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IP |
184 /* >8 End of dividing up the possible user priority values. */
187 * Initialises a given port using global settings and with the rx buffers
188 * coming from the mbuf_pool passed as parameter
191 port_init(uint16_t port, struct rte_mempool *mbuf_pool)
193 struct rte_eth_dev_info dev_info;
194 struct rte_eth_conf port_conf = {0};
195 uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT;
196 uint16_t txRingSize = RTE_TEST_TX_DESC_DEFAULT;
199 uint16_t queues_per_pool;
200 uint32_t max_nb_pools;
201 struct rte_eth_txconf txq_conf;
205 * The max pool number from dev_info will be used to validate the pool
206 * number specified in cmd line
208 retval = rte_eth_dev_info_get(port, &dev_info);
210 printf("Error during getting device (port %u) info: %s\n",
211 port, strerror(-retval));
216 max_nb_pools = (uint32_t)dev_info.max_vmdq_pools;
218 * We allow to process part of VMDQ pools specified by num_pools in
221 if (num_pools > max_nb_pools) {
222 printf("num_pools %d >max_nb_pools %d\n",
223 num_pools, max_nb_pools);
228 * NIC queues are divided into pf queues and vmdq queues.
229 * There is assumption here all ports have the same configuration!
231 vmdq_queue_base = dev_info.vmdq_queue_base;
232 vmdq_pool_base = dev_info.vmdq_pool_base;
233 printf("vmdq queue base: %d pool base %d\n",
234 vmdq_queue_base, vmdq_pool_base);
235 if (vmdq_pool_base == 0) {
236 num_vmdq_queues = dev_info.max_rx_queues;
237 num_queues = dev_info.max_rx_queues;
238 if (num_tcs != num_vmdq_queues / num_pools) {
239 printf("nb_tcs %d is invalid considering with"
240 " nb_pools %d, nb_tcs * nb_pools should = %d\n",
241 num_tcs, num_pools, num_vmdq_queues);
245 queues_per_pool = dev_info.vmdq_queue_num /
246 dev_info.max_vmdq_pools;
247 if (num_tcs > queues_per_pool) {
248 printf("num_tcs %d > num of queues per pool %d\n",
249 num_tcs, queues_per_pool);
252 num_vmdq_queues = num_pools * queues_per_pool;
253 num_queues = vmdq_queue_base + num_vmdq_queues;
254 printf("Configured vmdq pool num: %u,"
255 " each vmdq pool has %u queues\n",
256 num_pools, queues_per_pool);
259 if (!rte_eth_dev_is_valid_port(port))
262 retval = get_eth_conf(&port_conf);
266 retval = rte_eth_dev_info_get(port, &dev_info);
268 printf("Error during getting device (port %u) info: %s\n",
269 port, strerror(-retval));
274 if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
275 port_conf.txmode.offloads |=
276 DEV_TX_OFFLOAD_MBUF_FAST_FREE;
278 rss_hf_tmp = port_conf.rx_adv_conf.rss_conf.rss_hf;
279 port_conf.rx_adv_conf.rss_conf.rss_hf &=
280 dev_info.flow_type_rss_offloads;
281 if (port_conf.rx_adv_conf.rss_conf.rss_hf != rss_hf_tmp) {
282 printf("Port %u modified RSS hash function based on hardware support,"
283 "requested:%#"PRIx64" configured:%#"PRIx64"\n",
286 port_conf.rx_adv_conf.rss_conf.rss_hf);
290 * Though in this example, all queues including pf queues are setup.
291 * This is because VMDQ queues doesn't always start from zero, and the
292 * PMD layer doesn't support selectively initialising part of rx/tx
295 retval = rte_eth_dev_configure(port, num_queues, num_queues, &port_conf);
299 retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &rxRingSize,
303 if (RTE_MAX(rxRingSize, txRingSize) >
304 RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT)) {
305 printf("Mbuf pool has an insufficient size for port %u.\n",
310 for (q = 0; q < num_queues; q++) {
311 retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
312 rte_eth_dev_socket_id(port),
316 printf("initialize rx queue %d failed\n", q);
321 txq_conf = dev_info.default_txconf;
322 txq_conf.offloads = port_conf.txmode.offloads;
323 for (q = 0; q < num_queues; q++) {
324 retval = rte_eth_tx_queue_setup(port, q, txRingSize,
325 rte_eth_dev_socket_id(port),
328 printf("initialize tx queue %d failed\n", q);
333 retval = rte_eth_dev_start(port);
335 printf("port %d start failed\n", port);
339 retval = rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]);
341 printf("port %d MAC address get failed: %s\n", port,
342 rte_strerror(-retval));
345 printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
346 " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
348 RTE_ETHER_ADDR_BYTES(&vmdq_ports_eth_addr[port]));
350 /* Set mac for each pool. 8< */
351 for (q = 0; q < num_pools; q++) {
352 struct rte_ether_addr mac;
354 mac = pool_addr_template;
355 mac.addr_bytes[4] = port;
356 mac.addr_bytes[5] = q;
357 printf("Port %u vmdq pool %u set mac " RTE_ETHER_ADDR_PRT_FMT "\n",
358 port, q, RTE_ETHER_ADDR_BYTES(&mac));
359 retval = rte_eth_dev_mac_addr_add(port, &mac,
362 printf("mac addr add failed at pool %d\n", q);
366 /* >8 End of set mac for each pool. */
371 /* Check num_pools parameter and set it if OK*/
373 vmdq_parse_num_pools(const char *q_arg)
378 /* parse number string */
379 n = strtol(q_arg, &end, 10);
380 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
382 if (n != 16 && n != 32)
385 num_pools = ETH_16_POOLS;
387 num_pools = ETH_32_POOLS;
392 /* Check num_tcs parameter and set it if OK*/
394 vmdq_parse_num_tcs(const char *q_arg)
399 /* parse number string */
400 n = strtol(q_arg, &end, 10);
401 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
404 if (n != 4 && n != 8)
415 parse_portmask(const char *portmask)
420 /* parse hexadecimal string */
421 pm = strtoul(portmask, &end, 16);
422 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
430 vmdq_usage(const char *prgname)
432 printf("%s [EAL options] -- -p PORTMASK]\n"
433 " --nb-pools NP: number of pools (32 default, 16)\n"
434 " --nb-tcs NP: number of TCs (4 default, 8)\n"
435 " --enable-rss: enable RSS (disabled by default)\n",
439 /* Parse the argument (num_pools) given in the command line of the application */
441 vmdq_parse_args(int argc, char **argv)
446 const char *prgname = argv[0];
447 static struct option long_option[] = {
448 {"nb-pools", required_argument, NULL, 0},
449 {"nb-tcs", required_argument, NULL, 0},
450 {"enable-rss", 0, NULL, 0},
454 /* Parse command line */
455 while ((opt = getopt_long(argc, argv, "p:", long_option,
456 &option_index)) != EOF) {
460 enabled_port_mask = parse_portmask(optarg);
461 if (enabled_port_mask == 0) {
462 printf("invalid portmask\n");
468 if (!strcmp(long_option[option_index].name, "nb-pools")) {
469 if (vmdq_parse_num_pools(optarg) == -1) {
470 printf("invalid number of pools\n");
475 if (!strcmp(long_option[option_index].name, "nb-tcs")) {
476 if (vmdq_parse_num_tcs(optarg) == -1) {
477 printf("invalid number of tcs\n");
482 if (!strcmp(long_option[option_index].name, "enable-rss"))
492 for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
493 if (enabled_port_mask & (1 << i))
494 ports[num_ports++] = (uint8_t)i;
497 if (num_ports < 2 || num_ports % 2) {
498 printf("Current enabled port number is %u,"
499 " but it should be even and at least 2\n", num_ports);
507 update_mac_address(struct rte_mbuf *m, unsigned dst_port)
509 struct rte_ether_hdr *eth;
512 eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
514 /* 02:00:00:00:00:xx */
515 tmp = ð->dst_addr.addr_bytes[0];
516 *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
519 rte_ether_addr_copy(&vmdq_ports_eth_addr[dst_port], ð->src_addr);
522 /* When we receive a HUP signal, print out our stats */
524 sighup_handler(int signum)
526 unsigned q = vmdq_queue_base;
528 for (; q < num_queues; q++) {
529 if (q % (num_vmdq_queues / num_pools) == 0)
530 printf("\nPool %u: ", (q - vmdq_queue_base) /
531 (num_vmdq_queues / num_pools));
532 printf("%lu ", rxPackets[q]);
534 printf("\nFinished handling signal %d\n", signum);
538 * Main thread that does the work, reading from INPUT_PORT
539 * and writing to OUTPUT_PORT
542 lcore_main(void *arg)
544 const uintptr_t core_num = (uintptr_t)arg;
545 const unsigned num_cores = rte_lcore_count();
546 uint16_t startQueue, endQueue;
548 const uint16_t quot = (uint16_t)(num_vmdq_queues / num_cores);
549 const uint16_t remainder = (uint16_t)(num_vmdq_queues % num_cores);
553 if (core_num < remainder) {
554 startQueue = (uint16_t)(core_num * (quot + 1));
555 endQueue = (uint16_t)(startQueue + quot + 1);
557 startQueue = (uint16_t)(core_num * quot + remainder);
558 endQueue = (uint16_t)(startQueue + quot);
561 startQueue = (uint16_t)(core_num * quot);
562 endQueue = (uint16_t)(startQueue + quot);
565 /* vmdq queue idx doesn't always start from zero.*/
566 startQueue += vmdq_queue_base;
567 endQueue += vmdq_queue_base;
568 printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num,
569 rte_lcore_id(), startQueue, endQueue - 1);
571 if (startQueue == endQueue) {
572 printf("lcore %u has nothing to do\n", (unsigned)core_num);
577 struct rte_mbuf *buf[MAX_PKT_BURST];
578 const uint16_t buf_size = RTE_DIM(buf);
579 for (p = 0; p < num_ports; p++) {
580 const uint8_t src = ports[p];
581 const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
583 if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID))
586 for (q = startQueue; q < endQueue; q++) {
587 const uint16_t rxCount = rte_eth_rx_burst(src,
590 if (unlikely(rxCount == 0))
593 rxPackets[q] += rxCount;
595 for (i = 0; i < rxCount; i++)
596 update_mac_address(buf[i], dst);
598 const uint16_t txCount = rte_eth_tx_burst(dst,
600 if (txCount != rxCount) {
601 for (i = txCount; i < rxCount; i++)
602 rte_pktmbuf_free(buf[i]);
610 * Update the global var NUM_PORTS and array PORTS according to system ports number
611 * and return valid ports number
613 static unsigned check_ports_num(unsigned nb_ports)
615 unsigned valid_num_ports = num_ports;
618 if (num_ports > nb_ports) {
619 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
620 num_ports, nb_ports);
621 num_ports = nb_ports;
624 for (portid = 0; portid < num_ports; portid++) {
625 if (!rte_eth_dev_is_valid_port(ports[portid])) {
626 printf("\nSpecified port ID(%u) is not valid\n",
628 ports[portid] = INVALID_PORT_ID;
632 return valid_num_ports;
636 /* Main function, does initialisation and calls the per-lcore functions */
638 main(int argc, char *argv[])
641 struct rte_mempool *mbuf_pool;
645 unsigned nb_ports, valid_num_ports;
648 signal(SIGHUP, sighup_handler);
651 ret = rte_eal_init(argc, argv);
653 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
657 /* parse app arguments */
658 ret = vmdq_parse_args(argc, argv);
660 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
662 cores = rte_lcore_count();
663 if ((cores & (cores - 1)) != 0 || cores > RTE_MAX_LCORE) {
664 rte_exit(EXIT_FAILURE,"This program can only run on an even"
665 " number of cores(1-%d)\n\n", RTE_MAX_LCORE);
668 nb_ports = rte_eth_dev_count_avail();
671 * Update the global var NUM_PORTS and global array PORTS
672 * and get value of var VALID_NUM_PORTS according to system ports number
674 valid_num_ports = check_ports_num(nb_ports);
676 if (valid_num_ports < 2 || valid_num_ports % 2) {
677 printf("Current valid ports number is %u\n", valid_num_ports);
678 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
681 mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
682 NUM_MBUFS_PER_PORT * nb_ports, MBUF_CACHE_SIZE,
683 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
684 if (mbuf_pool == NULL)
685 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
687 /* initialize all ports */
688 RTE_ETH_FOREACH_DEV(portid) {
689 /* skip ports that are not enabled */
690 if ((enabled_port_mask & (1 << portid)) == 0) {
691 printf("\nSkipping disabled port %d\n", portid);
694 if (port_init(portid, mbuf_pool) != 0)
695 rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
698 /* call lcore_main() on every worker lcore */
700 RTE_LCORE_FOREACH_WORKER(lcore_id) {
701 rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id);
703 /* call on main too */
704 (void) lcore_main((void*)i);
706 /* clean up the EAL */