xref: /dpdk/examples/vmdq/main.c (revision cb056611a8ed9ab9024f3b91bf26e97255194514)
13998e2a0SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
23998e2a0SBruce Richardson  * Copyright(c) 2010-2014 Intel Corporation
36bb97df5SIntel  */
46bb97df5SIntel 
56bb97df5SIntel #include <stdint.h>
66bb97df5SIntel #include <sys/queue.h>
76bb97df5SIntel #include <stdlib.h>
86bb97df5SIntel #include <string.h>
96bb97df5SIntel #include <stdio.h>
106bb97df5SIntel #include <assert.h>
116bb97df5SIntel #include <errno.h>
126bb97df5SIntel #include <signal.h>
136bb97df5SIntel #include <stdarg.h>
146bb97df5SIntel #include <inttypes.h>
156bb97df5SIntel #include <getopt.h>
166bb97df5SIntel 
176bb97df5SIntel #include <rte_common.h>
186bb97df5SIntel #include <rte_log.h>
196bb97df5SIntel #include <rte_memory.h>
206bb97df5SIntel #include <rte_memcpy.h>
216bb97df5SIntel #include <rte_eal.h>
226bb97df5SIntel #include <rte_launch.h>
236bb97df5SIntel #include <rte_atomic.h>
246bb97df5SIntel #include <rte_cycles.h>
256bb97df5SIntel #include <rte_prefetch.h>
266bb97df5SIntel #include <rte_lcore.h>
276bb97df5SIntel #include <rte_per_lcore.h>
286bb97df5SIntel #include <rte_branch_prediction.h>
296bb97df5SIntel #include <rte_interrupts.h>
306bb97df5SIntel #include <rte_random.h>
316bb97df5SIntel #include <rte_debug.h>
326bb97df5SIntel #include <rte_ether.h>
336bb97df5SIntel #include <rte_ethdev.h>
346bb97df5SIntel #include <rte_mempool.h>
356bb97df5SIntel #include <rte_mbuf.h>
366bb97df5SIntel 
37e4363e81SXutao Sun #define MAX_QUEUES 1024
386bb97df5SIntel /*
39e4363e81SXutao Sun  * 1024 queues require to meet the needs of a large number of vmdq_pools.
40e4363e81SXutao Sun  * (RX/TX_queue_nb * RX/TX_ring_descriptors_nb) per port.
416bb97df5SIntel  */
42e4363e81SXutao Sun #define NUM_MBUFS_PER_PORT (MAX_QUEUES * RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, \
43e4363e81SXutao Sun 						RTE_TEST_TX_DESC_DEFAULT))
446bb97df5SIntel #define MBUF_CACHE_SIZE 64
456bb97df5SIntel 
466bb97df5SIntel #define MAX_PKT_BURST 32
476bb97df5SIntel 
486bb97df5SIntel /*
496bb97df5SIntel  * Configurable number of RX/TX ring descriptors
506bb97df5SIntel  */
51867a6c66SKevin Laatz #define RTE_TEST_RX_DESC_DEFAULT 1024
52867a6c66SKevin Laatz #define RTE_TEST_TX_DESC_DEFAULT 1024
536bb97df5SIntel 
546bb97df5SIntel #define INVALID_PORT_ID 0xFF
556bb97df5SIntel 
566bb97df5SIntel /* mask of enabled ports */
57b30eb1d2SHuawei Xie static uint32_t enabled_port_mask;
586bb97df5SIntel 
596bb97df5SIntel /* number of pools (if user does not specify any, 8 by default */
606bb97df5SIntel static uint32_t num_queues = 8;
616bb97df5SIntel static uint32_t num_pools = 8;
628f5b4af7SJunyu Jiang static uint8_t rss_enable;
636bb97df5SIntel 
646bb97df5SIntel /* empty vmdq configuration structure. Filled in programatically */
656bb97df5SIntel static const struct rte_eth_conf vmdq_conf_default = {
666bb97df5SIntel 	.rxmode = {
676bb97df5SIntel 		.mq_mode        = ETH_MQ_RX_VMDQ_ONLY,
686bb97df5SIntel 		.split_hdr_size = 0,
696bb97df5SIntel 	},
706bb97df5SIntel 
716bb97df5SIntel 	.txmode = {
726bb97df5SIntel 		.mq_mode = ETH_MQ_TX_NONE,
736bb97df5SIntel 	},
746bb97df5SIntel 	.rx_adv_conf = {
756bb97df5SIntel 		/*
766bb97df5SIntel 		 * should be overridden separately in code with
776bb97df5SIntel 		 * appropriate values
786bb97df5SIntel 		 */
796bb97df5SIntel 		.vmdq_rx_conf = {
806bb97df5SIntel 			.nb_queue_pools = ETH_8_POOLS,
816bb97df5SIntel 			.enable_default_pool = 0,
826bb97df5SIntel 			.default_pool = 0,
836bb97df5SIntel 			.nb_pool_maps = 0,
846bb97df5SIntel 			.pool_map = {{0, 0},},
856bb97df5SIntel 		},
866bb97df5SIntel 	},
876bb97df5SIntel };
886bb97df5SIntel 
896bb97df5SIntel static unsigned lcore_ids[RTE_MAX_LCORE];
9047523597SZhiyong Yang static uint16_t ports[RTE_MAX_ETHPORTS];
91b30eb1d2SHuawei Xie static unsigned num_ports; /**< The number of ports specified in command line */
926bb97df5SIntel 
936bb97df5SIntel /* array used for printing out statistics */
946bb97df5SIntel volatile unsigned long rxPackets[MAX_QUEUES] = {0};
956bb97df5SIntel 
966bb97df5SIntel const uint16_t vlan_tags[] = {
976bb97df5SIntel 	0,  1,  2,  3,  4,  5,  6,  7,
986bb97df5SIntel 	8,  9, 10, 11,	12, 13, 14, 15,
996bb97df5SIntel 	16, 17, 18, 19, 20, 21, 22, 23,
1006bb97df5SIntel 	24, 25, 26, 27, 28, 29, 30, 31,
1016bb97df5SIntel 	32, 33, 34, 35, 36, 37, 38, 39,
1026bb97df5SIntel 	40, 41, 42, 43, 44, 45, 46, 47,
1036bb97df5SIntel 	48, 49, 50, 51, 52, 53, 54, 55,
1046bb97df5SIntel 	56, 57, 58, 59, 60, 61, 62, 63,
1056bb97df5SIntel };
1062a13a5a0SHuawei Xie const uint16_t num_vlans = RTE_DIM(vlan_tags);
1072a13a5a0SHuawei Xie static uint16_t num_pf_queues,  num_vmdq_queues;
1082a13a5a0SHuawei Xie static uint16_t vmdq_pool_base, vmdq_queue_base;
1092a13a5a0SHuawei Xie /* pool mac addr template, pool mac addr is like: 52 54 00 12 port# pool# */
1106d13ea8eSOlivier Matz static struct rte_ether_addr pool_addr_template = {
1112a13a5a0SHuawei Xie 	.addr_bytes = {0x52, 0x54, 0x00, 0x12, 0x00, 0x00}
1122a13a5a0SHuawei Xie };
1136bb97df5SIntel 
1146bb97df5SIntel /* ethernet addresses of ports */
1156d13ea8eSOlivier Matz static struct rte_ether_addr vmdq_ports_eth_addr[RTE_MAX_ETHPORTS];
1166bb97df5SIntel 
1176bb97df5SIntel #define MAX_QUEUE_NUM_10G 128
1186bb97df5SIntel #define MAX_QUEUE_NUM_1G 8
1196bb97df5SIntel #define MAX_POOL_MAP_NUM_10G 64
1206bb97df5SIntel #define MAX_POOL_MAP_NUM_1G 32
1216bb97df5SIntel #define MAX_POOL_NUM_10G 64
1226bb97df5SIntel #define MAX_POOL_NUM_1G 8
123b30eb1d2SHuawei Xie /*
124b30eb1d2SHuawei Xie  * Builds up the correct configuration for vmdq based on the vlan tags array
125b30eb1d2SHuawei Xie  * given above, and determine the queue number and pool map number according to
126b30eb1d2SHuawei Xie  * valid pool number
127b30eb1d2SHuawei Xie  */
1286bb97df5SIntel static inline int
1296bb97df5SIntel get_eth_conf(struct rte_eth_conf *eth_conf, uint32_t num_pools)
1306bb97df5SIntel {
1316bb97df5SIntel 	struct rte_eth_vmdq_rx_conf conf;
1326bb97df5SIntel 	unsigned i;
1336bb97df5SIntel 
1346bb97df5SIntel 	conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
1352a13a5a0SHuawei Xie 	conf.nb_pool_maps = num_pools;
1366bb97df5SIntel 	conf.enable_default_pool = 0;
1376bb97df5SIntel 	conf.default_pool = 0; /* set explicit value, even if not used */
1386bb97df5SIntel 
1396bb97df5SIntel 	for (i = 0; i < conf.nb_pool_maps; i++) {
1406bb97df5SIntel 		conf.pool_map[i].vlan_id = vlan_tags[i];
1416bb97df5SIntel 		conf.pool_map[i].pools = (1UL << (i % num_pools));
1426bb97df5SIntel 	}
1436bb97df5SIntel 
1446bb97df5SIntel 	(void)(rte_memcpy(eth_conf, &vmdq_conf_default, sizeof(*eth_conf)));
1456bb97df5SIntel 	(void)(rte_memcpy(&eth_conf->rx_adv_conf.vmdq_rx_conf, &conf,
1466bb97df5SIntel 		   sizeof(eth_conf->rx_adv_conf.vmdq_rx_conf)));
1478f5b4af7SJunyu Jiang 	if (rss_enable) {
1488f5b4af7SJunyu Jiang 		eth_conf->rxmode.mq_mode = ETH_MQ_RX_VMDQ_RSS;
1498f5b4af7SJunyu Jiang 		eth_conf->rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IP |
1508f5b4af7SJunyu Jiang 							ETH_RSS_UDP |
1518f5b4af7SJunyu Jiang 							ETH_RSS_TCP |
1528f5b4af7SJunyu Jiang 							ETH_RSS_SCTP;
1538f5b4af7SJunyu Jiang 	}
1546bb97df5SIntel 	return 0;
1556bb97df5SIntel }
1566bb97df5SIntel 
1576bb97df5SIntel /*
1586bb97df5SIntel  * Initialises a given port using global settings and with the rx buffers
1596bb97df5SIntel  * coming from the mbuf_pool passed as parameter
1606bb97df5SIntel  */
1616bb97df5SIntel static inline int
16247523597SZhiyong Yang port_init(uint16_t port, struct rte_mempool *mbuf_pool)
1636bb97df5SIntel {
1646bb97df5SIntel 	struct rte_eth_dev_info dev_info;
16581f7ecd9SPablo de Lara 	struct rte_eth_rxconf *rxconf;
1668dd0befeSShahaf Shuler 	struct rte_eth_txconf *txconf;
1676bb97df5SIntel 	struct rte_eth_conf port_conf;
1682a13a5a0SHuawei Xie 	uint16_t rxRings, txRings;
16960efb44fSRoman Zhukov 	uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT;
17060efb44fSRoman Zhukov 	uint16_t txRingSize = RTE_TEST_TX_DESC_DEFAULT;
1716bb97df5SIntel 	int retval;
1726bb97df5SIntel 	uint16_t q;
1732a13a5a0SHuawei Xie 	uint16_t queues_per_pool;
1746bb97df5SIntel 	uint32_t max_nb_pools;
1758f5b4af7SJunyu Jiang 	uint64_t rss_hf_tmp;
1766bb97df5SIntel 
177b30eb1d2SHuawei Xie 	/*
178b30eb1d2SHuawei Xie 	 * The max pool number from dev_info will be used to validate the pool
179b30eb1d2SHuawei Xie 	 * number specified in cmd line
180b30eb1d2SHuawei Xie 	 */
181089e5ed7SIvan Ilchenko 	retval = rte_eth_dev_info_get(port, &dev_info);
182089e5ed7SIvan Ilchenko 	if (retval != 0) {
183089e5ed7SIvan Ilchenko 		printf("Error during getting device (port %u) info: %s\n",
184089e5ed7SIvan Ilchenko 				port, strerror(-retval));
185089e5ed7SIvan Ilchenko 		return retval;
186089e5ed7SIvan Ilchenko 	}
187089e5ed7SIvan Ilchenko 
1886bb97df5SIntel 	max_nb_pools = (uint32_t)dev_info.max_vmdq_pools;
1892a13a5a0SHuawei Xie 	/*
1902a13a5a0SHuawei Xie 	 * We allow to process part of VMDQ pools specified by num_pools in
1912a13a5a0SHuawei Xie 	 * command line.
1922a13a5a0SHuawei Xie 	 */
1932a13a5a0SHuawei Xie 	if (num_pools > max_nb_pools) {
1942a13a5a0SHuawei Xie 		printf("num_pools %d >max_nb_pools %d\n",
1952a13a5a0SHuawei Xie 			num_pools, max_nb_pools);
1962a13a5a0SHuawei Xie 		return -1;
1972a13a5a0SHuawei Xie 	}
1982a13a5a0SHuawei Xie 	retval = get_eth_conf(&port_conf, max_nb_pools);
1996bb97df5SIntel 	if (retval < 0)
2006bb97df5SIntel 		return retval;
2016bb97df5SIntel 
2022a13a5a0SHuawei Xie 	/*
2032a13a5a0SHuawei Xie 	 * NIC queues are divided into pf queues and vmdq queues.
2042a13a5a0SHuawei Xie 	 */
2052a13a5a0SHuawei Xie 	/* There is assumption here all ports have the same configuration! */
2062a13a5a0SHuawei Xie 	num_pf_queues = dev_info.max_rx_queues - dev_info.vmdq_queue_num;
2072a13a5a0SHuawei Xie 	queues_per_pool = dev_info.vmdq_queue_num / dev_info.max_vmdq_pools;
2082a13a5a0SHuawei Xie 	num_vmdq_queues = num_pools * queues_per_pool;
2092a13a5a0SHuawei Xie 	num_queues = num_pf_queues + num_vmdq_queues;
2102a13a5a0SHuawei Xie 	vmdq_queue_base = dev_info.vmdq_queue_base;
2112a13a5a0SHuawei Xie 	vmdq_pool_base  = dev_info.vmdq_pool_base;
2126bb97df5SIntel 
2132a13a5a0SHuawei Xie 	printf("pf queue num: %u, configured vmdq pool num: %u,"
2142a13a5a0SHuawei Xie 		" each vmdq pool has %u queues\n",
2152a13a5a0SHuawei Xie 		num_pf_queues, num_pools, queues_per_pool);
2162a13a5a0SHuawei Xie 	printf("vmdq queue base: %d pool base %d\n",
2172a13a5a0SHuawei Xie 		vmdq_queue_base, vmdq_pool_base);
218a9dbe180SThomas Monjalon 	if (!rte_eth_dev_is_valid_port(port))
219b30eb1d2SHuawei Xie 		return -1;
2206bb97df5SIntel 
2218f5b4af7SJunyu Jiang 	rss_hf_tmp = port_conf.rx_adv_conf.rss_conf.rss_hf;
2228f5b4af7SJunyu Jiang 	port_conf.rx_adv_conf.rss_conf.rss_hf &=
2238f5b4af7SJunyu Jiang 		dev_info.flow_type_rss_offloads;
2248f5b4af7SJunyu Jiang 	if (port_conf.rx_adv_conf.rss_conf.rss_hf != rss_hf_tmp) {
2258f5b4af7SJunyu Jiang 		printf("Port %u modified RSS hash function based on hardware support,"
2268f5b4af7SJunyu Jiang 			"requested:%#"PRIx64" configured:%#"PRIx64"\n",
2278f5b4af7SJunyu Jiang 			port,
2288f5b4af7SJunyu Jiang 			rss_hf_tmp,
2298f5b4af7SJunyu Jiang 			port_conf.rx_adv_conf.rss_conf.rss_hf);
2308f5b4af7SJunyu Jiang 	}
2318f5b4af7SJunyu Jiang 
2322a13a5a0SHuawei Xie 	/*
2332a13a5a0SHuawei Xie 	 * Though in this example, we only receive packets from the first queue
2342a13a5a0SHuawei Xie 	 * of each pool and send packets through first rte_lcore_count() tx
2352a13a5a0SHuawei Xie 	 * queues of vmdq queues, all queues including pf queues are setup.
2362a13a5a0SHuawei Xie 	 * This is because VMDQ queues doesn't always start from zero, and the
2372a13a5a0SHuawei Xie 	 * PMD layer doesn't support selectively initialising part of rx/tx
2382a13a5a0SHuawei Xie 	 * queues.
2392a13a5a0SHuawei Xie 	 */
2402a13a5a0SHuawei Xie 	rxRings = (uint16_t)dev_info.max_rx_queues;
2412a13a5a0SHuawei Xie 	txRings = (uint16_t)dev_info.max_tx_queues;
2428dd0befeSShahaf Shuler 
243089e5ed7SIvan Ilchenko 	retval = rte_eth_dev_info_get(port, &dev_info);
244089e5ed7SIvan Ilchenko 	if (retval != 0) {
245089e5ed7SIvan Ilchenko 		printf("Error during getting device (port %u) info: %s\n",
246089e5ed7SIvan Ilchenko 				port, strerror(-retval));
247089e5ed7SIvan Ilchenko 		return retval;
248089e5ed7SIvan Ilchenko 	}
249089e5ed7SIvan Ilchenko 
2508dd0befeSShahaf Shuler 	if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
2518dd0befeSShahaf Shuler 		port_conf.txmode.offloads |=
2528dd0befeSShahaf Shuler 			DEV_TX_OFFLOAD_MBUF_FAST_FREE;
2536bb97df5SIntel 	retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf);
2546bb97df5SIntel 	if (retval != 0)
2556bb97df5SIntel 		return retval;
2566bb97df5SIntel 
25760efb44fSRoman Zhukov 	retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &rxRingSize,
25860efb44fSRoman Zhukov 				&txRingSize);
25960efb44fSRoman Zhukov 	if (retval != 0)
26060efb44fSRoman Zhukov 		return retval;
26160efb44fSRoman Zhukov 	if (RTE_MAX(rxRingSize, txRingSize) > RTE_MAX(RTE_TEST_RX_DESC_DEFAULT,
26260efb44fSRoman Zhukov 			RTE_TEST_TX_DESC_DEFAULT)) {
26360efb44fSRoman Zhukov 		printf("Mbuf pool has an insufficient size for port %u.\n",
26460efb44fSRoman Zhukov 			port);
26560efb44fSRoman Zhukov 		return -1;
26660efb44fSRoman Zhukov 	}
26760efb44fSRoman Zhukov 
26881f7ecd9SPablo de Lara 	rxconf = &dev_info.default_rxconf;
26981f7ecd9SPablo de Lara 	rxconf->rx_drop_en = 1;
2708dd0befeSShahaf Shuler 	txconf = &dev_info.default_txconf;
2718dd0befeSShahaf Shuler 	txconf->offloads = port_conf.txmode.offloads;
2726bb97df5SIntel 	for (q = 0; q < rxRings; q++) {
2736bb97df5SIntel 		retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
27481f7ecd9SPablo de Lara 					rte_eth_dev_socket_id(port),
27581f7ecd9SPablo de Lara 					rxconf,
2766bb97df5SIntel 					mbuf_pool);
2772a13a5a0SHuawei Xie 		if (retval < 0) {
2782a13a5a0SHuawei Xie 			printf("initialise rx queue %d failed\n", q);
2796bb97df5SIntel 			return retval;
2806bb97df5SIntel 		}
2812a13a5a0SHuawei Xie 	}
2826bb97df5SIntel 
2836bb97df5SIntel 	for (q = 0; q < txRings; q++) {
2846bb97df5SIntel 		retval = rte_eth_tx_queue_setup(port, q, txRingSize,
28581f7ecd9SPablo de Lara 					rte_eth_dev_socket_id(port),
2868dd0befeSShahaf Shuler 					txconf);
2872a13a5a0SHuawei Xie 		if (retval < 0) {
2882a13a5a0SHuawei Xie 			printf("initialise tx queue %d failed\n", q);
2896bb97df5SIntel 			return retval;
2906bb97df5SIntel 		}
2912a13a5a0SHuawei Xie 	}
2926bb97df5SIntel 
2936bb97df5SIntel 	retval  = rte_eth_dev_start(port);
2942a13a5a0SHuawei Xie 	if (retval < 0) {
2952a13a5a0SHuawei Xie 		printf("port %d start failed\n", port);
2966bb97df5SIntel 		return retval;
2972a13a5a0SHuawei Xie 	}
2986bb97df5SIntel 
29970febdcfSIgor Romanov 	retval = rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]);
30070febdcfSIgor Romanov 	if (retval < 0) {
30170febdcfSIgor Romanov 		printf("port %d MAC address get failed: %s\n", port,
30270febdcfSIgor Romanov 		       rte_strerror(-retval));
30370febdcfSIgor Romanov 		return retval;
30470febdcfSIgor Romanov 	}
3056bb97df5SIntel 	printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
3066bb97df5SIntel 			" %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
3076bb97df5SIntel 			(unsigned)port,
3086bb97df5SIntel 			vmdq_ports_eth_addr[port].addr_bytes[0],
3096bb97df5SIntel 			vmdq_ports_eth_addr[port].addr_bytes[1],
3106bb97df5SIntel 			vmdq_ports_eth_addr[port].addr_bytes[2],
3116bb97df5SIntel 			vmdq_ports_eth_addr[port].addr_bytes[3],
3126bb97df5SIntel 			vmdq_ports_eth_addr[port].addr_bytes[4],
3136bb97df5SIntel 			vmdq_ports_eth_addr[port].addr_bytes[5]);
3146bb97df5SIntel 
3152a13a5a0SHuawei Xie 	/*
3162a13a5a0SHuawei Xie 	 * Set mac for each pool.
3172a13a5a0SHuawei Xie 	 * There is no default mac for the pools in i40.
3182a13a5a0SHuawei Xie 	 * Removes this after i40e fixes this issue.
3192a13a5a0SHuawei Xie 	 */
3202a13a5a0SHuawei Xie 	for (q = 0; q < num_pools; q++) {
3216d13ea8eSOlivier Matz 		struct rte_ether_addr mac;
3222a13a5a0SHuawei Xie 		mac = pool_addr_template;
3232a13a5a0SHuawei Xie 		mac.addr_bytes[4] = port;
3242a13a5a0SHuawei Xie 		mac.addr_bytes[5] = q;
3252a13a5a0SHuawei Xie 		printf("Port %u vmdq pool %u set mac %02x:%02x:%02x:%02x:%02x:%02x\n",
3262a13a5a0SHuawei Xie 			port, q,
3272a13a5a0SHuawei Xie 			mac.addr_bytes[0], mac.addr_bytes[1],
3282a13a5a0SHuawei Xie 			mac.addr_bytes[2], mac.addr_bytes[3],
3292a13a5a0SHuawei Xie 			mac.addr_bytes[4], mac.addr_bytes[5]);
3302a13a5a0SHuawei Xie 		retval = rte_eth_dev_mac_addr_add(port, &mac,
3312a13a5a0SHuawei Xie 				q + vmdq_pool_base);
3322a13a5a0SHuawei Xie 		if (retval) {
3332a13a5a0SHuawei Xie 			printf("mac addr add failed at pool %d\n", q);
3342a13a5a0SHuawei Xie 			return retval;
3352a13a5a0SHuawei Xie 		}
3362a13a5a0SHuawei Xie 	}
3372a13a5a0SHuawei Xie 
3386bb97df5SIntel 	return 0;
3396bb97df5SIntel }
3406bb97df5SIntel 
3416bb97df5SIntel /* Check num_pools parameter and set it if OK*/
3426bb97df5SIntel static int
3436bb97df5SIntel vmdq_parse_num_pools(const char *q_arg)
3446bb97df5SIntel {
3456bb97df5SIntel 	char *end = NULL;
3466bb97df5SIntel 	int n;
3476bb97df5SIntel 
3486bb97df5SIntel 	/* parse number string */
3496bb97df5SIntel 	n = strtol(q_arg, &end, 10);
3506bb97df5SIntel 	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
3516bb97df5SIntel 		return -1;
3526bb97df5SIntel 
3532a13a5a0SHuawei Xie 	if (num_pools > num_vlans) {
3542a13a5a0SHuawei Xie 		printf("num_pools %d > num_vlans %d\n", num_pools, num_vlans);
3552a13a5a0SHuawei Xie 		return -1;
3562a13a5a0SHuawei Xie 	}
3572a13a5a0SHuawei Xie 
3586bb97df5SIntel 	num_pools = n;
3596bb97df5SIntel 
3606bb97df5SIntel 	return 0;
3616bb97df5SIntel }
3626bb97df5SIntel 
3636bb97df5SIntel 
3646bb97df5SIntel static int
3656bb97df5SIntel parse_portmask(const char *portmask)
3666bb97df5SIntel {
3676bb97df5SIntel 	char *end = NULL;
3686bb97df5SIntel 	unsigned long pm;
3696bb97df5SIntel 
3706bb97df5SIntel 	/* parse hexadecimal string */
3716bb97df5SIntel 	pm = strtoul(portmask, &end, 16);
3726bb97df5SIntel 	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
373ce6b8c31SSarosh Arif 		return 0;
3746bb97df5SIntel 
3756bb97df5SIntel 	return pm;
3766bb97df5SIntel }
3776bb97df5SIntel 
3786bb97df5SIntel /* Display usage */
3796bb97df5SIntel static void
3806bb97df5SIntel vmdq_usage(const char *prgname)
3816bb97df5SIntel {
3826bb97df5SIntel 	printf("%s [EAL options] -- -p PORTMASK]\n"
3838f5b4af7SJunyu Jiang 	"  --nb-pools NP: number of pools\n"
3848f5b4af7SJunyu Jiang 	"  --enable-rss: enable RSS (disabled by default)\n",
3856bb97df5SIntel 	       prgname);
3866bb97df5SIntel }
3876bb97df5SIntel 
3886bb97df5SIntel /*  Parse the argument (num_pools) given in the command line of the application */
3896bb97df5SIntel static int
3906bb97df5SIntel vmdq_parse_args(int argc, char **argv)
3916bb97df5SIntel {
3926bb97df5SIntel 	int opt;
3936bb97df5SIntel 	int option_index;
3946bb97df5SIntel 	unsigned i;
3956bb97df5SIntel 	const char *prgname = argv[0];
3966bb97df5SIntel 	static struct option long_option[] = {
3976bb97df5SIntel 		{"nb-pools", required_argument, NULL, 0},
3988f5b4af7SJunyu Jiang 		{"enable-rss", 0, NULL, 0},
3996bb97df5SIntel 		{NULL, 0, 0, 0}
4006bb97df5SIntel 	};
4016bb97df5SIntel 
4026bb97df5SIntel 	/* Parse command line */
403b30eb1d2SHuawei Xie 	while ((opt = getopt_long(argc, argv, "p:", long_option,
404b30eb1d2SHuawei Xie 		&option_index)) != EOF) {
4056bb97df5SIntel 		switch (opt) {
4066bb97df5SIntel 		/* portmask */
4076bb97df5SIntel 		case 'p':
4086bb97df5SIntel 			enabled_port_mask = parse_portmask(optarg);
4096bb97df5SIntel 			if (enabled_port_mask == 0) {
4106bb97df5SIntel 				printf("invalid portmask\n");
4116bb97df5SIntel 				vmdq_usage(prgname);
4126bb97df5SIntel 				return -1;
4136bb97df5SIntel 			}
4146bb97df5SIntel 			break;
4156bb97df5SIntel 		case 0:
4168f5b4af7SJunyu Jiang 			if (!strcmp(long_option[option_index].name,
4178f5b4af7SJunyu Jiang 			    "nb-pools")) {
4186bb97df5SIntel 				if (vmdq_parse_num_pools(optarg) == -1) {
4196bb97df5SIntel 					printf("invalid number of pools\n");
4206bb97df5SIntel 					vmdq_usage(prgname);
4216bb97df5SIntel 					return -1;
4226bb97df5SIntel 				}
4238f5b4af7SJunyu Jiang 			}
4248f5b4af7SJunyu Jiang 
4258f5b4af7SJunyu Jiang 			if (!strcmp(long_option[option_index].name,
4268f5b4af7SJunyu Jiang 			    "enable-rss"))
4278f5b4af7SJunyu Jiang 				rss_enable = 1;
4286bb97df5SIntel 			break;
4296bb97df5SIntel 
4306bb97df5SIntel 		default:
4316bb97df5SIntel 			vmdq_usage(prgname);
4326bb97df5SIntel 			return -1;
4336bb97df5SIntel 		}
4346bb97df5SIntel 	}
4356bb97df5SIntel 
4366bb97df5SIntel 	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
4376bb97df5SIntel 		if (enabled_port_mask & (1 << i))
4386bb97df5SIntel 			ports[num_ports++] = (uint8_t)i;
4396bb97df5SIntel 	}
4406bb97df5SIntel 
4416bb97df5SIntel 	if (num_ports < 2 || num_ports % 2) {
4426bb97df5SIntel 		printf("Current enabled port number is %u,"
4436bb97df5SIntel 			"but it should be even and at least 2\n", num_ports);
4446bb97df5SIntel 		return -1;
4456bb97df5SIntel 	}
4466bb97df5SIntel 
4476bb97df5SIntel 	return 0;
4486bb97df5SIntel }
4496bb97df5SIntel 
4506bb97df5SIntel static void
4516bb97df5SIntel update_mac_address(struct rte_mbuf *m, unsigned dst_port)
4526bb97df5SIntel {
4536d13ea8eSOlivier Matz 	struct rte_ether_hdr *eth;
4546bb97df5SIntel 	void *tmp;
4556bb97df5SIntel 
4566d13ea8eSOlivier Matz 	eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
4576bb97df5SIntel 
4586bb97df5SIntel 	/* 02:00:00:00:00:xx */
4596bb97df5SIntel 	tmp = &eth->d_addr.addr_bytes[0];
4606bb97df5SIntel 	*((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
4616bb97df5SIntel 
4626bb97df5SIntel 	/* src addr */
463538da7a1SOlivier Matz 	rte_ether_addr_copy(&vmdq_ports_eth_addr[dst_port], &eth->s_addr);
4646bb97df5SIntel }
4656bb97df5SIntel 
4666bb97df5SIntel /* When we receive a HUP signal, print out our stats */
4676bb97df5SIntel static void
4686bb97df5SIntel sighup_handler(int signum)
4696bb97df5SIntel {
47070c37e32SJunyu Jiang 	unsigned int q = vmdq_queue_base;
47170c37e32SJunyu Jiang 	for (; q < num_queues; q++) {
47270c37e32SJunyu Jiang 		if ((q - vmdq_queue_base) % (num_vmdq_queues / num_pools) == 0)
47370c37e32SJunyu Jiang 			printf("\nPool %u: ", (q - vmdq_queue_base) /
47470c37e32SJunyu Jiang 			       (num_vmdq_queues / num_pools));
4756bb97df5SIntel 		printf("%lu ", rxPackets[q]);
4766bb97df5SIntel 	}
4776bb97df5SIntel 	printf("\nFinished handling signal %d\n", signum);
4786bb97df5SIntel }
4796bb97df5SIntel 
4806bb97df5SIntel /*
4816bb97df5SIntel  * Main thread that does the work, reading from INPUT_PORT
4826bb97df5SIntel  * and writing to OUTPUT_PORT
4836bb97df5SIntel  */
48413c4ebd6SBruce Richardson static int
485f2fc83b4SThomas Monjalon lcore_main(__rte_unused void *dummy)
4866bb97df5SIntel {
4876bb97df5SIntel 	const uint16_t lcore_id = (uint16_t)rte_lcore_id();
4886bb97df5SIntel 	const uint16_t num_cores = (uint16_t)rte_lcore_count();
4896bb97df5SIntel 	uint16_t core_id = 0;
4906bb97df5SIntel 	uint16_t startQueue, endQueue;
4916bb97df5SIntel 	uint16_t q, i, p;
4922a13a5a0SHuawei Xie 	const uint16_t remainder = (uint16_t)(num_vmdq_queues % num_cores);
4936bb97df5SIntel 
4946bb97df5SIntel 	for (i = 0; i < num_cores; i++)
4956bb97df5SIntel 		if (lcore_ids[i] == lcore_id) {
4966bb97df5SIntel 			core_id = i;
4976bb97df5SIntel 			break;
4986bb97df5SIntel 		}
4996bb97df5SIntel 
5006bb97df5SIntel 	if (remainder != 0) {
5016bb97df5SIntel 		if (core_id < remainder) {
5022a13a5a0SHuawei Xie 			startQueue = (uint16_t)(core_id *
5032a13a5a0SHuawei Xie 					(num_vmdq_queues / num_cores + 1));
5042a13a5a0SHuawei Xie 			endQueue = (uint16_t)(startQueue +
5052a13a5a0SHuawei Xie 					(num_vmdq_queues / num_cores) + 1);
5066bb97df5SIntel 		} else {
5072a13a5a0SHuawei Xie 			startQueue = (uint16_t)(core_id *
5082a13a5a0SHuawei Xie 					(num_vmdq_queues / num_cores) +
5092a13a5a0SHuawei Xie 					remainder);
5102a13a5a0SHuawei Xie 			endQueue = (uint16_t)(startQueue +
5112a13a5a0SHuawei Xie 					(num_vmdq_queues / num_cores));
5126bb97df5SIntel 		}
5136bb97df5SIntel 	} else {
5142a13a5a0SHuawei Xie 		startQueue = (uint16_t)(core_id *
5152a13a5a0SHuawei Xie 				(num_vmdq_queues / num_cores));
5162a13a5a0SHuawei Xie 		endQueue = (uint16_t)(startQueue +
5172a13a5a0SHuawei Xie 				(num_vmdq_queues / num_cores));
5186bb97df5SIntel 	}
5196bb97df5SIntel 
5202a13a5a0SHuawei Xie 	/* vmdq queue idx doesn't always start from zero.*/
5212a13a5a0SHuawei Xie 	startQueue += vmdq_queue_base;
5222a13a5a0SHuawei Xie 	endQueue   += vmdq_queue_base;
5236bb97df5SIntel 	printf("core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_id,
5246bb97df5SIntel 		(unsigned)lcore_id, startQueue, endQueue - 1);
5256bb97df5SIntel 
52613c4ebd6SBruce Richardson 	if (startQueue == endQueue) {
52713c4ebd6SBruce Richardson 		printf("lcore %u has nothing to do\n", lcore_id);
528b30eb1d2SHuawei Xie 		return 0;
52913c4ebd6SBruce Richardson 	}
53013c4ebd6SBruce Richardson 
5316bb97df5SIntel 	for (;;) {
5326bb97df5SIntel 		struct rte_mbuf *buf[MAX_PKT_BURST];
5337efe28bdSPavan Nikhilesh 		const uint16_t buf_size = RTE_DIM(buf);
5346bb97df5SIntel 
5356bb97df5SIntel 		for (p = 0; p < num_ports; p++) {
5366bb97df5SIntel 			const uint8_t sport = ports[p];
537b30eb1d2SHuawei Xie 			/* 0 <-> 1, 2 <-> 3 etc */
538b30eb1d2SHuawei Xie 			const uint8_t dport = ports[p ^ 1];
5396bb97df5SIntel 			if ((sport == INVALID_PORT_ID) || (dport == INVALID_PORT_ID))
5406bb97df5SIntel 				continue;
5416bb97df5SIntel 
5426bb97df5SIntel 			for (q = startQueue; q < endQueue; q++) {
5436bb97df5SIntel 				const uint16_t rxCount = rte_eth_rx_burst(sport,
5446bb97df5SIntel 					q, buf, buf_size);
5456bb97df5SIntel 
5466bb97df5SIntel 				if (unlikely(rxCount == 0))
5476bb97df5SIntel 					continue;
5486bb97df5SIntel 
5496bb97df5SIntel 				rxPackets[q] += rxCount;
5506bb97df5SIntel 
5516bb97df5SIntel 				for (i = 0; i < rxCount; i++)
5526bb97df5SIntel 					update_mac_address(buf[i], dport);
5536bb97df5SIntel 
5546bb97df5SIntel 				const uint16_t txCount = rte_eth_tx_burst(dport,
5552a13a5a0SHuawei Xie 					vmdq_queue_base + core_id,
5562a13a5a0SHuawei Xie 					buf,
5572a13a5a0SHuawei Xie 					rxCount);
5586bb97df5SIntel 
5596bb97df5SIntel 				if (txCount != rxCount) {
5606bb97df5SIntel 					for (i = txCount; i < rxCount; i++)
5616bb97df5SIntel 						rte_pktmbuf_free(buf[i]);
5626bb97df5SIntel 				}
5636bb97df5SIntel 			}
5646bb97df5SIntel 		}
5656bb97df5SIntel 	}
5666bb97df5SIntel }
5676bb97df5SIntel 
5686bb97df5SIntel /*
5696bb97df5SIntel  * Update the global var NUM_PORTS and array PORTS according to system ports number
5706bb97df5SIntel  * and return valid ports number
5716bb97df5SIntel  */
5726bb97df5SIntel static unsigned check_ports_num(unsigned nb_ports)
5736bb97df5SIntel {
5746bb97df5SIntel 	unsigned valid_num_ports = num_ports;
5756bb97df5SIntel 	unsigned portid;
5766bb97df5SIntel 
5776bb97df5SIntel 	if (num_ports > nb_ports) {
5786bb97df5SIntel 		printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
5796bb97df5SIntel 			num_ports, nb_ports);
5806bb97df5SIntel 		num_ports = nb_ports;
5816bb97df5SIntel 	}
5826bb97df5SIntel 
5836bb97df5SIntel 	for (portid = 0; portid < num_ports; portid++) {
584a9dbe180SThomas Monjalon 		if (!rte_eth_dev_is_valid_port(ports[portid])) {
585a9dbe180SThomas Monjalon 			printf("\nSpecified port ID(%u) is not valid\n",
586a9dbe180SThomas Monjalon 				ports[portid]);
5876bb97df5SIntel 			ports[portid] = INVALID_PORT_ID;
5886bb97df5SIntel 			valid_num_ports--;
5896bb97df5SIntel 		}
5906bb97df5SIntel 	}
5916bb97df5SIntel 	return valid_num_ports;
5926bb97df5SIntel }
5936bb97df5SIntel 
5946bb97df5SIntel /* Main function, does initialisation and calls the per-lcore functions */
5956bb97df5SIntel int
59698a16481SDavid Marchand main(int argc, char *argv[])
5976bb97df5SIntel {
5986bb97df5SIntel 	struct rte_mempool *mbuf_pool;
5996bb97df5SIntel 	unsigned lcore_id, core_id = 0;
6006bb97df5SIntel 	int ret;
6016bb97df5SIntel 	unsigned nb_ports, valid_num_ports;
60247523597SZhiyong Yang 	uint16_t portid;
6036bb97df5SIntel 
6046bb97df5SIntel 	signal(SIGHUP, sighup_handler);
6056bb97df5SIntel 
6066bb97df5SIntel 	/* init EAL */
6076bb97df5SIntel 	ret = rte_eal_init(argc, argv);
6086bb97df5SIntel 	if (ret < 0)
6096bb97df5SIntel 		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
6106bb97df5SIntel 	argc -= ret;
6116bb97df5SIntel 	argv += ret;
6126bb97df5SIntel 
6136bb97df5SIntel 	/* parse app arguments */
6146bb97df5SIntel 	ret = vmdq_parse_args(argc, argv);
6156bb97df5SIntel 	if (ret < 0)
6166bb97df5SIntel 		rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
6176bb97df5SIntel 
6186bb97df5SIntel 	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++)
6196bb97df5SIntel 		if (rte_lcore_is_enabled(lcore_id))
6206bb97df5SIntel 			lcore_ids[core_id++] = lcore_id;
6216bb97df5SIntel 
6226bb97df5SIntel 	if (rte_lcore_count() > RTE_MAX_LCORE)
6236bb97df5SIntel 		rte_exit(EXIT_FAILURE, "Not enough cores\n");
6246bb97df5SIntel 
625d9a42a69SThomas Monjalon 	nb_ports = rte_eth_dev_count_avail();
6266bb97df5SIntel 
6276bb97df5SIntel 	/*
6286bb97df5SIntel 	 * Update the global var NUM_PORTS and global array PORTS
6296bb97df5SIntel 	 * and get value of var VALID_NUM_PORTS according to system ports number
6306bb97df5SIntel 	 */
6316bb97df5SIntel 	valid_num_ports = check_ports_num(nb_ports);
6326bb97df5SIntel 
6336bb97df5SIntel 	if (valid_num_ports < 2 || valid_num_ports % 2) {
6346bb97df5SIntel 		printf("Current valid ports number is %u\n", valid_num_ports);
6356bb97df5SIntel 		rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
6366bb97df5SIntel 	}
6376bb97df5SIntel 
638ea0c20eaSOlivier Matz 	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
639ea0c20eaSOlivier Matz 		NUM_MBUFS_PER_PORT * nb_ports, MBUF_CACHE_SIZE,
640824cb29cSKonstantin Ananyev 		0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
6416bb97df5SIntel 	if (mbuf_pool == NULL)
6426bb97df5SIntel 		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
6436bb97df5SIntel 
6446bb97df5SIntel 	/* initialize all ports */
6458728ccf3SThomas Monjalon 	RTE_ETH_FOREACH_DEV(portid) {
6466bb97df5SIntel 		/* skip ports that are not enabled */
6476bb97df5SIntel 		if ((enabled_port_mask & (1 << portid)) == 0) {
6486bb97df5SIntel 			printf("\nSkipping disabled port %d\n", portid);
6496bb97df5SIntel 			continue;
6506bb97df5SIntel 		}
6516bb97df5SIntel 		if (port_init(portid, mbuf_pool) != 0)
6526bb97df5SIntel 			rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
6536bb97df5SIntel 	}
6546bb97df5SIntel 
6556bb97df5SIntel 	/* call lcore_main() on every lcore */
656*cb056611SStephen Hemminger 	rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MAIN);
657*cb056611SStephen Hemminger 	RTE_LCORE_FOREACH_WORKER(lcore_id) {
6586bb97df5SIntel 		if (rte_eal_wait_lcore(lcore_id) < 0)
6596bb97df5SIntel 			return -1;
6606bb97df5SIntel 	}
6616bb97df5SIntel 
6626bb97df5SIntel 	return 0;
6636bb97df5SIntel }
664