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 .ignore_offload_bitfield = 1,
77 .mq_mode = ETH_MQ_TX_VMDQ_DCB,
80 * should be overridden separately in code with
85 .nb_queue_pools = ETH_32_POOLS,
86 .enable_default_pool = 0,
89 .pool_map = {{0, 0},},
94 /** Traffic class each UP mapped to. */
98 .nb_queue_pools = ETH_32_POOLS,
99 .enable_default_pool = 0,
102 .pool_map = {{0, 0},},
106 .vmdq_dcb_tx_conf = {
107 .nb_queue_pools = ETH_32_POOLS,
113 /* array used for printing out statistics */
114 volatile unsigned long rxPackets[MAX_QUEUES] = {0};
116 const uint16_t vlan_tags[] = {
117 0, 1, 2, 3, 4, 5, 6, 7,
118 8, 9, 10, 11, 12, 13, 14, 15,
119 16, 17, 18, 19, 20, 21, 22, 23,
120 24, 25, 26, 27, 28, 29, 30, 31
123 const uint16_t num_vlans = RTE_DIM(vlan_tags);
124 /* pool mac addr template, pool mac addr is like: 52 54 00 12 port# pool# */
125 static struct ether_addr pool_addr_template = {
126 .addr_bytes = {0x52, 0x54, 0x00, 0x12, 0x00, 0x00}
129 /* ethernet addresses of ports */
130 static struct ether_addr vmdq_ports_eth_addr[RTE_MAX_ETHPORTS];
132 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array
133 * given above, and the number of traffic classes available for use. */
135 get_eth_conf(struct rte_eth_conf *eth_conf)
137 struct rte_eth_vmdq_dcb_conf conf;
138 struct rte_eth_vmdq_rx_conf vmdq_conf;
139 struct rte_eth_dcb_rx_conf dcb_conf;
140 struct rte_eth_vmdq_dcb_tx_conf tx_conf;
143 conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
144 vmdq_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
145 tx_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
146 conf.nb_pool_maps = num_pools;
147 vmdq_conf.nb_pool_maps = num_pools;
148 conf.enable_default_pool = 0;
149 vmdq_conf.enable_default_pool = 0;
150 conf.default_pool = 0; /* set explicit value, even if not used */
151 vmdq_conf.default_pool = 0;
153 for (i = 0; i < conf.nb_pool_maps; i++) {
154 conf.pool_map[i].vlan_id = vlan_tags[i];
155 vmdq_conf.pool_map[i].vlan_id = vlan_tags[i];
156 conf.pool_map[i].pools = 1UL << i;
157 vmdq_conf.pool_map[i].pools = 1UL << i;
159 for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){
160 conf.dcb_tc[i] = i % num_tcs;
161 dcb_conf.dcb_tc[i] = i % num_tcs;
162 tx_conf.dcb_tc[i] = i % num_tcs;
164 dcb_conf.nb_tcs = (enum rte_eth_nb_tcs)num_tcs;
165 (void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf)));
166 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_dcb_conf, &conf,
168 (void)(rte_memcpy(ð_conf->rx_adv_conf.dcb_rx_conf, &dcb_conf,
170 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_rx_conf, &vmdq_conf,
172 (void)(rte_memcpy(ð_conf->tx_adv_conf.vmdq_dcb_tx_conf, &tx_conf,
175 eth_conf->rxmode.mq_mode = ETH_MQ_RX_VMDQ_DCB_RSS;
176 eth_conf->rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IP |
185 * Initialises a given port using global settings and with the rx buffers
186 * coming from the mbuf_pool passed as parameter
189 port_init(uint16_t port, struct rte_mempool *mbuf_pool)
191 struct rte_eth_dev_info dev_info;
192 struct rte_eth_conf port_conf = {0};
193 uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT;
194 uint16_t txRingSize = RTE_TEST_TX_DESC_DEFAULT;
197 uint16_t queues_per_pool;
198 uint32_t max_nb_pools;
199 struct rte_eth_txconf txq_conf;
202 * The max pool number from dev_info will be used to validate the pool
203 * number specified in cmd line
205 rte_eth_dev_info_get(port, &dev_info);
206 max_nb_pools = (uint32_t)dev_info.max_vmdq_pools;
208 * We allow to process part of VMDQ pools specified by num_pools in
211 if (num_pools > max_nb_pools) {
212 printf("num_pools %d >max_nb_pools %d\n",
213 num_pools, max_nb_pools);
218 * NIC queues are divided into pf queues and vmdq queues.
219 * There is assumption here all ports have the same configuration!
221 vmdq_queue_base = dev_info.vmdq_queue_base;
222 vmdq_pool_base = dev_info.vmdq_pool_base;
223 printf("vmdq queue base: %d pool base %d\n",
224 vmdq_queue_base, vmdq_pool_base);
225 if (vmdq_pool_base == 0) {
226 num_vmdq_queues = dev_info.max_rx_queues;
227 num_queues = dev_info.max_rx_queues;
228 if (num_tcs != num_vmdq_queues / num_pools) {
229 printf("nb_tcs %d is invalid considering with"
230 " nb_pools %d, nb_tcs * nb_pools should = %d\n",
231 num_tcs, num_pools, num_vmdq_queues);
235 queues_per_pool = dev_info.vmdq_queue_num /
236 dev_info.max_vmdq_pools;
237 if (num_tcs > queues_per_pool) {
238 printf("num_tcs %d > num of queues per pool %d\n",
239 num_tcs, queues_per_pool);
242 num_vmdq_queues = num_pools * queues_per_pool;
243 num_queues = vmdq_queue_base + num_vmdq_queues;
244 printf("Configured vmdq pool num: %u,"
245 " each vmdq pool has %u queues\n",
246 num_pools, queues_per_pool);
249 if (port >= rte_eth_dev_count())
252 retval = get_eth_conf(&port_conf);
256 rte_eth_dev_info_get(port, &dev_info);
257 if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
258 port_conf.txmode.offloads |=
259 DEV_TX_OFFLOAD_MBUF_FAST_FREE;
261 * Though in this example, all queues including pf queues are setup.
262 * This is because VMDQ queues doesn't always start from zero, and the
263 * PMD layer doesn't support selectively initialising part of rx/tx
266 retval = rte_eth_dev_configure(port, num_queues, num_queues, &port_conf);
270 retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &rxRingSize,
274 if (RTE_MAX(rxRingSize, txRingSize) >
275 RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT)) {
276 printf("Mbuf pool has an insufficient size for port %u.\n",
281 for (q = 0; q < num_queues; q++) {
282 retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
283 rte_eth_dev_socket_id(port),
287 printf("initialize rx queue %d failed\n", q);
292 txq_conf = dev_info.default_txconf;
293 txq_conf.txq_flags = ETH_TXQ_FLAGS_IGNORE;
294 txq_conf.offloads = port_conf.txmode.offloads;
295 for (q = 0; q < num_queues; q++) {
296 retval = rte_eth_tx_queue_setup(port, q, txRingSize,
297 rte_eth_dev_socket_id(port),
300 printf("initialize tx queue %d failed\n", q);
305 retval = rte_eth_dev_start(port);
307 printf("port %d start failed\n", port);
311 rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]);
312 printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
313 " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
315 vmdq_ports_eth_addr[port].addr_bytes[0],
316 vmdq_ports_eth_addr[port].addr_bytes[1],
317 vmdq_ports_eth_addr[port].addr_bytes[2],
318 vmdq_ports_eth_addr[port].addr_bytes[3],
319 vmdq_ports_eth_addr[port].addr_bytes[4],
320 vmdq_ports_eth_addr[port].addr_bytes[5]);
322 /* Set mac for each pool.*/
323 for (q = 0; q < num_pools; q++) {
324 struct ether_addr mac;
326 mac = pool_addr_template;
327 mac.addr_bytes[4] = port;
328 mac.addr_bytes[5] = q;
329 printf("Port %u vmdq pool %u set mac %02x:%02x:%02x:%02x:%02x:%02x\n",
331 mac.addr_bytes[0], mac.addr_bytes[1],
332 mac.addr_bytes[2], mac.addr_bytes[3],
333 mac.addr_bytes[4], mac.addr_bytes[5]);
334 retval = rte_eth_dev_mac_addr_add(port, &mac,
337 printf("mac addr add failed at pool %d\n", q);
345 /* Check num_pools parameter and set it if OK*/
347 vmdq_parse_num_pools(const char *q_arg)
352 /* parse number string */
353 n = strtol(q_arg, &end, 10);
354 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
356 if (n != 16 && n != 32)
359 num_pools = ETH_16_POOLS;
361 num_pools = ETH_32_POOLS;
366 /* Check num_tcs parameter and set it if OK*/
368 vmdq_parse_num_tcs(const char *q_arg)
373 /* parse number string */
374 n = strtol(q_arg, &end, 10);
375 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
378 if (n != 4 && n != 8)
389 parse_portmask(const char *portmask)
394 /* parse hexadecimal string */
395 pm = strtoul(portmask, &end, 16);
396 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
407 vmdq_usage(const char *prgname)
409 printf("%s [EAL options] -- -p PORTMASK]\n"
410 " --nb-pools NP: number of pools (32 default, 16)\n"
411 " --nb-tcs NP: number of TCs (4 default, 8)\n"
412 " --enable-rss: enable RSS (disabled by default)\n",
416 /* Parse the argument (num_pools) given in the command line of the application */
418 vmdq_parse_args(int argc, char **argv)
423 const char *prgname = argv[0];
424 static struct option long_option[] = {
425 {"nb-pools", required_argument, NULL, 0},
426 {"nb-tcs", required_argument, NULL, 0},
427 {"enable-rss", 0, NULL, 0},
431 /* Parse command line */
432 while ((opt = getopt_long(argc, argv, "p:", long_option,
433 &option_index)) != EOF) {
437 enabled_port_mask = parse_portmask(optarg);
438 if (enabled_port_mask == 0) {
439 printf("invalid portmask\n");
445 if (!strcmp(long_option[option_index].name, "nb-pools")) {
446 if (vmdq_parse_num_pools(optarg) == -1) {
447 printf("invalid number of pools\n");
452 if (!strcmp(long_option[option_index].name, "nb-tcs")) {
453 if (vmdq_parse_num_tcs(optarg) == -1) {
454 printf("invalid number of tcs\n");
459 if (!strcmp(long_option[option_index].name, "enable-rss"))
469 for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
470 if (enabled_port_mask & (1 << i))
471 ports[num_ports++] = (uint8_t)i;
474 if (num_ports < 2 || num_ports % 2) {
475 printf("Current enabled port number is %u,"
476 " but it should be even and at least 2\n", num_ports);
484 update_mac_address(struct rte_mbuf *m, unsigned dst_port)
486 struct ether_hdr *eth;
489 eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
491 /* 02:00:00:00:00:xx */
492 tmp = ð->d_addr.addr_bytes[0];
493 *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
496 ether_addr_copy(&vmdq_ports_eth_addr[dst_port], ð->s_addr);
499 /* When we receive a HUP signal, print out our stats */
501 sighup_handler(int signum)
503 unsigned q = vmdq_queue_base;
505 for (; q < num_queues; q++) {
506 if (q % (num_vmdq_queues / num_pools) == 0)
507 printf("\nPool %u: ", (q - vmdq_queue_base) /
508 (num_vmdq_queues / num_pools));
509 printf("%lu ", rxPackets[q]);
511 printf("\nFinished handling signal %d\n", signum);
515 * Main thread that does the work, reading from INPUT_PORT
516 * and writing to OUTPUT_PORT
519 lcore_main(void *arg)
521 const uintptr_t core_num = (uintptr_t)arg;
522 const unsigned num_cores = rte_lcore_count();
523 uint16_t startQueue, endQueue;
525 const uint16_t quot = (uint16_t)(num_vmdq_queues / num_cores);
526 const uint16_t remainder = (uint16_t)(num_vmdq_queues % num_cores);
530 if (core_num < remainder) {
531 startQueue = (uint16_t)(core_num * (quot + 1));
532 endQueue = (uint16_t)(startQueue + quot + 1);
534 startQueue = (uint16_t)(core_num * quot + remainder);
535 endQueue = (uint16_t)(startQueue + quot);
538 startQueue = (uint16_t)(core_num * quot);
539 endQueue = (uint16_t)(startQueue + quot);
542 /* vmdq queue idx doesn't always start from zero.*/
543 startQueue += vmdq_queue_base;
544 endQueue += vmdq_queue_base;
545 printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num,
546 rte_lcore_id(), startQueue, endQueue - 1);
548 if (startQueue == endQueue) {
549 printf("lcore %u has nothing to do\n", (unsigned)core_num);
554 struct rte_mbuf *buf[MAX_PKT_BURST];
555 const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]);
556 for (p = 0; p < num_ports; p++) {
557 const uint8_t src = ports[p];
558 const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
560 if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID))
563 for (q = startQueue; q < endQueue; q++) {
564 const uint16_t rxCount = rte_eth_rx_burst(src,
567 if (unlikely(rxCount == 0))
570 rxPackets[q] += rxCount;
572 for (i = 0; i < rxCount; i++)
573 update_mac_address(buf[i], dst);
575 const uint16_t txCount = rte_eth_tx_burst(dst,
577 if (txCount != rxCount) {
578 for (i = txCount; i < rxCount; i++)
579 rte_pktmbuf_free(buf[i]);
587 * Update the global var NUM_PORTS and array PORTS according to system ports number
588 * and return valid ports number
590 static unsigned check_ports_num(unsigned nb_ports)
592 unsigned valid_num_ports = num_ports;
595 if (num_ports > nb_ports) {
596 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
597 num_ports, nb_ports);
598 num_ports = nb_ports;
601 for (portid = 0; portid < num_ports; portid++) {
602 if (ports[portid] >= nb_ports) {
603 printf("\nSpecified port ID(%u) exceeds max system port ID(%u)\n",
604 ports[portid], (nb_ports - 1));
605 ports[portid] = INVALID_PORT_ID;
609 return valid_num_ports;
613 /* Main function, does initialisation and calls the per-lcore functions */
615 main(int argc, char *argv[])
618 struct rte_mempool *mbuf_pool;
622 unsigned nb_ports, valid_num_ports;
625 signal(SIGHUP, sighup_handler);
628 ret = rte_eal_init(argc, argv);
630 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
634 /* parse app arguments */
635 ret = vmdq_parse_args(argc, argv);
637 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
639 cores = rte_lcore_count();
640 if ((cores & (cores - 1)) != 0 || cores > RTE_MAX_LCORE) {
641 rte_exit(EXIT_FAILURE,"This program can only run on an even"
642 " number of cores(1-%d)\n\n", RTE_MAX_LCORE);
645 nb_ports = rte_eth_dev_count();
648 * Update the global var NUM_PORTS and global array PORTS
649 * and get value of var VALID_NUM_PORTS according to system ports number
651 valid_num_ports = check_ports_num(nb_ports);
653 if (valid_num_ports < 2 || valid_num_ports % 2) {
654 printf("Current valid ports number is %u\n", valid_num_ports);
655 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
658 mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
659 NUM_MBUFS_PER_PORT * nb_ports, MBUF_CACHE_SIZE,
660 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
661 if (mbuf_pool == NULL)
662 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
664 /* initialize all ports */
665 for (portid = 0; portid < nb_ports; portid++) {
666 /* skip ports that are not enabled */
667 if ((enabled_port_mask & (1 << portid)) == 0) {
668 printf("\nSkipping disabled port %d\n", portid);
671 if (port_init(portid, mbuf_pool) != 0)
672 rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
675 /* call lcore_main() on every slave lcore */
677 RTE_LCORE_FOREACH_SLAVE(lcore_id) {
678 rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id);
680 /* call on master too */
681 (void) lcore_main((void*)i);