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 vmdq_ports_eth_addr[port].addr_bytes[0],
349 vmdq_ports_eth_addr[port].addr_bytes[1],
350 vmdq_ports_eth_addr[port].addr_bytes[2],
351 vmdq_ports_eth_addr[port].addr_bytes[3],
352 vmdq_ports_eth_addr[port].addr_bytes[4],
353 vmdq_ports_eth_addr[port].addr_bytes[5]);
355 /* Set mac for each pool. 8< */
356 for (q = 0; q < num_pools; q++) {
357 struct rte_ether_addr mac;
359 mac = pool_addr_template;
360 mac.addr_bytes[4] = port;
361 mac.addr_bytes[5] = q;
362 printf("Port %u vmdq pool %u set mac " RTE_ETHER_ADDR_PRT_FMT "\n",
364 mac.addr_bytes[0], mac.addr_bytes[1],
365 mac.addr_bytes[2], mac.addr_bytes[3],
366 mac.addr_bytes[4], mac.addr_bytes[5]);
367 retval = rte_eth_dev_mac_addr_add(port, &mac,
370 printf("mac addr add failed at pool %d\n", q);
374 /* >8 End of set mac for each pool. */
379 /* Check num_pools parameter and set it if OK*/
381 vmdq_parse_num_pools(const char *q_arg)
386 /* parse number string */
387 n = strtol(q_arg, &end, 10);
388 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
390 if (n != 16 && n != 32)
393 num_pools = ETH_16_POOLS;
395 num_pools = ETH_32_POOLS;
400 /* Check num_tcs parameter and set it if OK*/
402 vmdq_parse_num_tcs(const char *q_arg)
407 /* parse number string */
408 n = strtol(q_arg, &end, 10);
409 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
412 if (n != 4 && n != 8)
423 parse_portmask(const char *portmask)
428 /* parse hexadecimal string */
429 pm = strtoul(portmask, &end, 16);
430 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
438 vmdq_usage(const char *prgname)
440 printf("%s [EAL options] -- -p PORTMASK]\n"
441 " --nb-pools NP: number of pools (32 default, 16)\n"
442 " --nb-tcs NP: number of TCs (4 default, 8)\n"
443 " --enable-rss: enable RSS (disabled by default)\n",
447 /* Parse the argument (num_pools) given in the command line of the application */
449 vmdq_parse_args(int argc, char **argv)
454 const char *prgname = argv[0];
455 static struct option long_option[] = {
456 {"nb-pools", required_argument, NULL, 0},
457 {"nb-tcs", required_argument, NULL, 0},
458 {"enable-rss", 0, NULL, 0},
462 /* Parse command line */
463 while ((opt = getopt_long(argc, argv, "p:", long_option,
464 &option_index)) != EOF) {
468 enabled_port_mask = parse_portmask(optarg);
469 if (enabled_port_mask == 0) {
470 printf("invalid portmask\n");
476 if (!strcmp(long_option[option_index].name, "nb-pools")) {
477 if (vmdq_parse_num_pools(optarg) == -1) {
478 printf("invalid number of pools\n");
483 if (!strcmp(long_option[option_index].name, "nb-tcs")) {
484 if (vmdq_parse_num_tcs(optarg) == -1) {
485 printf("invalid number of tcs\n");
490 if (!strcmp(long_option[option_index].name, "enable-rss"))
500 for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
501 if (enabled_port_mask & (1 << i))
502 ports[num_ports++] = (uint8_t)i;
505 if (num_ports < 2 || num_ports % 2) {
506 printf("Current enabled port number is %u,"
507 " but it should be even and at least 2\n", num_ports);
515 update_mac_address(struct rte_mbuf *m, unsigned dst_port)
517 struct rte_ether_hdr *eth;
520 eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
522 /* 02:00:00:00:00:xx */
523 tmp = ð->d_addr.addr_bytes[0];
524 *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
527 rte_ether_addr_copy(&vmdq_ports_eth_addr[dst_port], ð->s_addr);
530 /* When we receive a HUP signal, print out our stats */
532 sighup_handler(int signum)
534 unsigned q = vmdq_queue_base;
536 for (; q < num_queues; q++) {
537 if (q % (num_vmdq_queues / num_pools) == 0)
538 printf("\nPool %u: ", (q - vmdq_queue_base) /
539 (num_vmdq_queues / num_pools));
540 printf("%lu ", rxPackets[q]);
542 printf("\nFinished handling signal %d\n", signum);
546 * Main thread that does the work, reading from INPUT_PORT
547 * and writing to OUTPUT_PORT
550 lcore_main(void *arg)
552 const uintptr_t core_num = (uintptr_t)arg;
553 const unsigned num_cores = rte_lcore_count();
554 uint16_t startQueue, endQueue;
556 const uint16_t quot = (uint16_t)(num_vmdq_queues / num_cores);
557 const uint16_t remainder = (uint16_t)(num_vmdq_queues % num_cores);
561 if (core_num < remainder) {
562 startQueue = (uint16_t)(core_num * (quot + 1));
563 endQueue = (uint16_t)(startQueue + quot + 1);
565 startQueue = (uint16_t)(core_num * quot + remainder);
566 endQueue = (uint16_t)(startQueue + quot);
569 startQueue = (uint16_t)(core_num * quot);
570 endQueue = (uint16_t)(startQueue + quot);
573 /* vmdq queue idx doesn't always start from zero.*/
574 startQueue += vmdq_queue_base;
575 endQueue += vmdq_queue_base;
576 printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num,
577 rte_lcore_id(), startQueue, endQueue - 1);
579 if (startQueue == endQueue) {
580 printf("lcore %u has nothing to do\n", (unsigned)core_num);
585 struct rte_mbuf *buf[MAX_PKT_BURST];
586 const uint16_t buf_size = RTE_DIM(buf);
587 for (p = 0; p < num_ports; p++) {
588 const uint8_t src = ports[p];
589 const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
591 if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID))
594 for (q = startQueue; q < endQueue; q++) {
595 const uint16_t rxCount = rte_eth_rx_burst(src,
598 if (unlikely(rxCount == 0))
601 rxPackets[q] += rxCount;
603 for (i = 0; i < rxCount; i++)
604 update_mac_address(buf[i], dst);
606 const uint16_t txCount = rte_eth_tx_burst(dst,
608 if (txCount != rxCount) {
609 for (i = txCount; i < rxCount; i++)
610 rte_pktmbuf_free(buf[i]);
618 * Update the global var NUM_PORTS and array PORTS according to system ports number
619 * and return valid ports number
621 static unsigned check_ports_num(unsigned nb_ports)
623 unsigned valid_num_ports = num_ports;
626 if (num_ports > nb_ports) {
627 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
628 num_ports, nb_ports);
629 num_ports = nb_ports;
632 for (portid = 0; portid < num_ports; portid++) {
633 if (!rte_eth_dev_is_valid_port(ports[portid])) {
634 printf("\nSpecified port ID(%u) is not valid\n",
636 ports[portid] = INVALID_PORT_ID;
640 return valid_num_ports;
644 /* Main function, does initialisation and calls the per-lcore functions */
646 main(int argc, char *argv[])
649 struct rte_mempool *mbuf_pool;
653 unsigned nb_ports, valid_num_ports;
656 signal(SIGHUP, sighup_handler);
659 ret = rte_eal_init(argc, argv);
661 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
665 /* parse app arguments */
666 ret = vmdq_parse_args(argc, argv);
668 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
670 cores = rte_lcore_count();
671 if ((cores & (cores - 1)) != 0 || cores > RTE_MAX_LCORE) {
672 rte_exit(EXIT_FAILURE,"This program can only run on an even"
673 " number of cores(1-%d)\n\n", RTE_MAX_LCORE);
676 nb_ports = rte_eth_dev_count_avail();
679 * Update the global var NUM_PORTS and global array PORTS
680 * and get value of var VALID_NUM_PORTS according to system ports number
682 valid_num_ports = check_ports_num(nb_ports);
684 if (valid_num_ports < 2 || valid_num_ports % 2) {
685 printf("Current valid ports number is %u\n", valid_num_ports);
686 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
689 mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
690 NUM_MBUFS_PER_PORT * nb_ports, MBUF_CACHE_SIZE,
691 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
692 if (mbuf_pool == NULL)
693 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
695 /* initialize all ports */
696 RTE_ETH_FOREACH_DEV(portid) {
697 /* skip ports that are not enabled */
698 if ((enabled_port_mask & (1 << portid)) == 0) {
699 printf("\nSkipping disabled port %d\n", portid);
702 if (port_init(portid, mbuf_pool) != 0)
703 rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
706 /* call lcore_main() on every worker lcore */
708 RTE_LCORE_FOREACH_WORKER(lcore_id) {
709 rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id);
711 /* call on main too */
712 (void) lcore_main((void*)i);
714 /* clean up the EAL */