xref: /f-stack/dpdk/examples/vmdq/main.c (revision 2d9fd380)
1d30ea906Sjfb8856606 /* SPDX-License-Identifier: BSD-3-Clause
2d30ea906Sjfb8856606  * Copyright(c) 2010-2014 Intel Corporation
3a9643ea8Slogwang  */
4a9643ea8Slogwang 
5a9643ea8Slogwang #include <stdint.h>
6a9643ea8Slogwang #include <sys/queue.h>
7a9643ea8Slogwang #include <stdlib.h>
8a9643ea8Slogwang #include <string.h>
9a9643ea8Slogwang #include <stdio.h>
10a9643ea8Slogwang #include <assert.h>
11a9643ea8Slogwang #include <errno.h>
12a9643ea8Slogwang #include <signal.h>
13a9643ea8Slogwang #include <stdarg.h>
14a9643ea8Slogwang #include <inttypes.h>
15a9643ea8Slogwang #include <getopt.h>
16a9643ea8Slogwang 
17a9643ea8Slogwang #include <rte_common.h>
18a9643ea8Slogwang #include <rte_log.h>
19a9643ea8Slogwang #include <rte_memory.h>
20a9643ea8Slogwang #include <rte_memcpy.h>
21a9643ea8Slogwang #include <rte_eal.h>
22a9643ea8Slogwang #include <rte_launch.h>
23a9643ea8Slogwang #include <rte_atomic.h>
24a9643ea8Slogwang #include <rte_cycles.h>
25a9643ea8Slogwang #include <rte_prefetch.h>
26a9643ea8Slogwang #include <rte_lcore.h>
27a9643ea8Slogwang #include <rte_per_lcore.h>
28a9643ea8Slogwang #include <rte_branch_prediction.h>
29a9643ea8Slogwang #include <rte_interrupts.h>
30a9643ea8Slogwang #include <rte_random.h>
31a9643ea8Slogwang #include <rte_debug.h>
32a9643ea8Slogwang #include <rte_ether.h>
33a9643ea8Slogwang #include <rte_ethdev.h>
34a9643ea8Slogwang #include <rte_mempool.h>
35a9643ea8Slogwang #include <rte_mbuf.h>
36a9643ea8Slogwang 
37a9643ea8Slogwang #define MAX_QUEUES 1024
38a9643ea8Slogwang /*
39a9643ea8Slogwang  * 1024 queues require to meet the needs of a large number of vmdq_pools.
40a9643ea8Slogwang  * (RX/TX_queue_nb * RX/TX_ring_descriptors_nb) per port.
41a9643ea8Slogwang  */
42a9643ea8Slogwang #define NUM_MBUFS_PER_PORT (MAX_QUEUES * RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, \
43a9643ea8Slogwang 						RTE_TEST_TX_DESC_DEFAULT))
44a9643ea8Slogwang #define MBUF_CACHE_SIZE 64
45a9643ea8Slogwang 
46a9643ea8Slogwang #define MAX_PKT_BURST 32
47a9643ea8Slogwang 
48a9643ea8Slogwang /*
49a9643ea8Slogwang  * Configurable number of RX/TX ring descriptors
50a9643ea8Slogwang  */
51d30ea906Sjfb8856606 #define RTE_TEST_RX_DESC_DEFAULT 1024
52d30ea906Sjfb8856606 #define RTE_TEST_TX_DESC_DEFAULT 1024
53a9643ea8Slogwang 
54a9643ea8Slogwang #define INVALID_PORT_ID 0xFF
55a9643ea8Slogwang 
56a9643ea8Slogwang /* mask of enabled ports */
57a9643ea8Slogwang static uint32_t enabled_port_mask;
58a9643ea8Slogwang 
59a9643ea8Slogwang /* number of pools (if user does not specify any, 8 by default */
60a9643ea8Slogwang static uint32_t num_queues = 8;
61a9643ea8Slogwang static uint32_t num_pools = 8;
620c6bd470Sfengbojiang static uint8_t rss_enable;
63a9643ea8Slogwang 
64a9643ea8Slogwang /* empty vmdq configuration structure. Filled in programatically */
65a9643ea8Slogwang static const struct rte_eth_conf vmdq_conf_default = {
66a9643ea8Slogwang 	.rxmode = {
67a9643ea8Slogwang 		.mq_mode        = ETH_MQ_RX_VMDQ_ONLY,
68a9643ea8Slogwang 		.split_hdr_size = 0,
69a9643ea8Slogwang 	},
70a9643ea8Slogwang 
71a9643ea8Slogwang 	.txmode = {
72a9643ea8Slogwang 		.mq_mode = ETH_MQ_TX_NONE,
73a9643ea8Slogwang 	},
74a9643ea8Slogwang 	.rx_adv_conf = {
75a9643ea8Slogwang 		/*
76a9643ea8Slogwang 		 * should be overridden separately in code with
77a9643ea8Slogwang 		 * appropriate values
78a9643ea8Slogwang 		 */
79a9643ea8Slogwang 		.vmdq_rx_conf = {
80a9643ea8Slogwang 			.nb_queue_pools = ETH_8_POOLS,
81a9643ea8Slogwang 			.enable_default_pool = 0,
82a9643ea8Slogwang 			.default_pool = 0,
83a9643ea8Slogwang 			.nb_pool_maps = 0,
84a9643ea8Slogwang 			.pool_map = {{0, 0},},
85a9643ea8Slogwang 		},
86a9643ea8Slogwang 	},
87a9643ea8Slogwang };
88a9643ea8Slogwang 
89a9643ea8Slogwang static unsigned lcore_ids[RTE_MAX_LCORE];
902bfe3f2eSlogwang static uint16_t ports[RTE_MAX_ETHPORTS];
91a9643ea8Slogwang static unsigned num_ports; /**< The number of ports specified in command line */
92a9643ea8Slogwang 
93a9643ea8Slogwang /* array used for printing out statistics */
94a9643ea8Slogwang volatile unsigned long rxPackets[MAX_QUEUES] = {0};
95a9643ea8Slogwang 
96a9643ea8Slogwang const uint16_t vlan_tags[] = {
97a9643ea8Slogwang 	0,  1,  2,  3,  4,  5,  6,  7,
98a9643ea8Slogwang 	8,  9, 10, 11,	12, 13, 14, 15,
99a9643ea8Slogwang 	16, 17, 18, 19, 20, 21, 22, 23,
100a9643ea8Slogwang 	24, 25, 26, 27, 28, 29, 30, 31,
101a9643ea8Slogwang 	32, 33, 34, 35, 36, 37, 38, 39,
102a9643ea8Slogwang 	40, 41, 42, 43, 44, 45, 46, 47,
103a9643ea8Slogwang 	48, 49, 50, 51, 52, 53, 54, 55,
104a9643ea8Slogwang 	56, 57, 58, 59, 60, 61, 62, 63,
105a9643ea8Slogwang };
106a9643ea8Slogwang const uint16_t num_vlans = RTE_DIM(vlan_tags);
107a9643ea8Slogwang static uint16_t num_pf_queues,  num_vmdq_queues;
108a9643ea8Slogwang static uint16_t vmdq_pool_base, vmdq_queue_base;
109a9643ea8Slogwang /* pool mac addr template, pool mac addr is like: 52 54 00 12 port# pool# */
1104418919fSjohnjiang static struct rte_ether_addr pool_addr_template = {
111a9643ea8Slogwang 	.addr_bytes = {0x52, 0x54, 0x00, 0x12, 0x00, 0x00}
112a9643ea8Slogwang };
113a9643ea8Slogwang 
114a9643ea8Slogwang /* ethernet addresses of ports */
1154418919fSjohnjiang static struct rte_ether_addr vmdq_ports_eth_addr[RTE_MAX_ETHPORTS];
116a9643ea8Slogwang 
117a9643ea8Slogwang #define MAX_QUEUE_NUM_10G 128
118a9643ea8Slogwang #define MAX_QUEUE_NUM_1G 8
119a9643ea8Slogwang #define MAX_POOL_MAP_NUM_10G 64
120a9643ea8Slogwang #define MAX_POOL_MAP_NUM_1G 32
121a9643ea8Slogwang #define MAX_POOL_NUM_10G 64
122a9643ea8Slogwang #define MAX_POOL_NUM_1G 8
123a9643ea8Slogwang /*
124a9643ea8Slogwang  * Builds up the correct configuration for vmdq based on the vlan tags array
125a9643ea8Slogwang  * given above, and determine the queue number and pool map number according to
126a9643ea8Slogwang  * valid pool number
127a9643ea8Slogwang  */
128a9643ea8Slogwang static inline int
get_eth_conf(struct rte_eth_conf * eth_conf,uint32_t num_pools)129a9643ea8Slogwang get_eth_conf(struct rte_eth_conf *eth_conf, uint32_t num_pools)
130a9643ea8Slogwang {
131a9643ea8Slogwang 	struct rte_eth_vmdq_rx_conf conf;
132a9643ea8Slogwang 	unsigned i;
133a9643ea8Slogwang 
134a9643ea8Slogwang 	conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
135a9643ea8Slogwang 	conf.nb_pool_maps = num_pools;
136a9643ea8Slogwang 	conf.enable_default_pool = 0;
137a9643ea8Slogwang 	conf.default_pool = 0; /* set explicit value, even if not used */
138a9643ea8Slogwang 
139a9643ea8Slogwang 	for (i = 0; i < conf.nb_pool_maps; i++) {
140a9643ea8Slogwang 		conf.pool_map[i].vlan_id = vlan_tags[i];
141a9643ea8Slogwang 		conf.pool_map[i].pools = (1UL << (i % num_pools));
142a9643ea8Slogwang 	}
143a9643ea8Slogwang 
144a9643ea8Slogwang 	(void)(rte_memcpy(eth_conf, &vmdq_conf_default, sizeof(*eth_conf)));
145a9643ea8Slogwang 	(void)(rte_memcpy(&eth_conf->rx_adv_conf.vmdq_rx_conf, &conf,
146a9643ea8Slogwang 		   sizeof(eth_conf->rx_adv_conf.vmdq_rx_conf)));
1470c6bd470Sfengbojiang 	if (rss_enable) {
1480c6bd470Sfengbojiang 		eth_conf->rxmode.mq_mode = ETH_MQ_RX_VMDQ_RSS;
1490c6bd470Sfengbojiang 		eth_conf->rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IP |
1500c6bd470Sfengbojiang 							ETH_RSS_UDP |
1510c6bd470Sfengbojiang 							ETH_RSS_TCP |
1520c6bd470Sfengbojiang 							ETH_RSS_SCTP;
1530c6bd470Sfengbojiang 	}
154a9643ea8Slogwang 	return 0;
155a9643ea8Slogwang }
156a9643ea8Slogwang 
157a9643ea8Slogwang /*
158a9643ea8Slogwang  * Initialises a given port using global settings and with the rx buffers
159a9643ea8Slogwang  * coming from the mbuf_pool passed as parameter
160a9643ea8Slogwang  */
161a9643ea8Slogwang static inline int
port_init(uint16_t port,struct rte_mempool * mbuf_pool)1622bfe3f2eSlogwang port_init(uint16_t port, struct rte_mempool *mbuf_pool)
163a9643ea8Slogwang {
164a9643ea8Slogwang 	struct rte_eth_dev_info dev_info;
165a9643ea8Slogwang 	struct rte_eth_rxconf *rxconf;
166d30ea906Sjfb8856606 	struct rte_eth_txconf *txconf;
167a9643ea8Slogwang 	struct rte_eth_conf port_conf;
168a9643ea8Slogwang 	uint16_t rxRings, txRings;
1692bfe3f2eSlogwang 	uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT;
1702bfe3f2eSlogwang 	uint16_t txRingSize = RTE_TEST_TX_DESC_DEFAULT;
171a9643ea8Slogwang 	int retval;
172a9643ea8Slogwang 	uint16_t q;
173a9643ea8Slogwang 	uint16_t queues_per_pool;
174a9643ea8Slogwang 	uint32_t max_nb_pools;
1750c6bd470Sfengbojiang 	uint64_t rss_hf_tmp;
176a9643ea8Slogwang 
177a9643ea8Slogwang 	/*
178a9643ea8Slogwang 	 * The max pool number from dev_info will be used to validate the pool
179a9643ea8Slogwang 	 * number specified in cmd line
180a9643ea8Slogwang 	 */
1814418919fSjohnjiang 	retval = rte_eth_dev_info_get(port, &dev_info);
1824418919fSjohnjiang 	if (retval != 0) {
1834418919fSjohnjiang 		printf("Error during getting device (port %u) info: %s\n",
1844418919fSjohnjiang 				port, strerror(-retval));
1854418919fSjohnjiang 		return retval;
1864418919fSjohnjiang 	}
1874418919fSjohnjiang 
188a9643ea8Slogwang 	max_nb_pools = (uint32_t)dev_info.max_vmdq_pools;
189a9643ea8Slogwang 	/*
190a9643ea8Slogwang 	 * We allow to process part of VMDQ pools specified by num_pools in
191a9643ea8Slogwang 	 * command line.
192a9643ea8Slogwang 	 */
193a9643ea8Slogwang 	if (num_pools > max_nb_pools) {
194a9643ea8Slogwang 		printf("num_pools %d >max_nb_pools %d\n",
195a9643ea8Slogwang 			num_pools, max_nb_pools);
196a9643ea8Slogwang 		return -1;
197a9643ea8Slogwang 	}
198a9643ea8Slogwang 	retval = get_eth_conf(&port_conf, max_nb_pools);
199a9643ea8Slogwang 	if (retval < 0)
200a9643ea8Slogwang 		return retval;
201a9643ea8Slogwang 
202a9643ea8Slogwang 	/*
203a9643ea8Slogwang 	 * NIC queues are divided into pf queues and vmdq queues.
204a9643ea8Slogwang 	 */
205a9643ea8Slogwang 	/* There is assumption here all ports have the same configuration! */
206a9643ea8Slogwang 	num_pf_queues = dev_info.max_rx_queues - dev_info.vmdq_queue_num;
207a9643ea8Slogwang 	queues_per_pool = dev_info.vmdq_queue_num / dev_info.max_vmdq_pools;
208a9643ea8Slogwang 	num_vmdq_queues = num_pools * queues_per_pool;
209a9643ea8Slogwang 	num_queues = num_pf_queues + num_vmdq_queues;
210a9643ea8Slogwang 	vmdq_queue_base = dev_info.vmdq_queue_base;
211a9643ea8Slogwang 	vmdq_pool_base  = dev_info.vmdq_pool_base;
212a9643ea8Slogwang 
213a9643ea8Slogwang 	printf("pf queue num: %u, configured vmdq pool num: %u,"
214a9643ea8Slogwang 		" each vmdq pool has %u queues\n",
215a9643ea8Slogwang 		num_pf_queues, num_pools, queues_per_pool);
216a9643ea8Slogwang 	printf("vmdq queue base: %d pool base %d\n",
217a9643ea8Slogwang 		vmdq_queue_base, vmdq_pool_base);
218d30ea906Sjfb8856606 	if (!rte_eth_dev_is_valid_port(port))
219a9643ea8Slogwang 		return -1;
220a9643ea8Slogwang 
2210c6bd470Sfengbojiang 	rss_hf_tmp = port_conf.rx_adv_conf.rss_conf.rss_hf;
2220c6bd470Sfengbojiang 	port_conf.rx_adv_conf.rss_conf.rss_hf &=
2230c6bd470Sfengbojiang 		dev_info.flow_type_rss_offloads;
2240c6bd470Sfengbojiang 	if (port_conf.rx_adv_conf.rss_conf.rss_hf != rss_hf_tmp) {
2250c6bd470Sfengbojiang 		printf("Port %u modified RSS hash function based on hardware support,"
2260c6bd470Sfengbojiang 			"requested:%#"PRIx64" configured:%#"PRIx64"\n",
2270c6bd470Sfengbojiang 			port,
2280c6bd470Sfengbojiang 			rss_hf_tmp,
2290c6bd470Sfengbojiang 			port_conf.rx_adv_conf.rss_conf.rss_hf);
2300c6bd470Sfengbojiang 	}
2310c6bd470Sfengbojiang 
232a9643ea8Slogwang 	/*
233a9643ea8Slogwang 	 * Though in this example, we only receive packets from the first queue
234a9643ea8Slogwang 	 * of each pool and send packets through first rte_lcore_count() tx
235a9643ea8Slogwang 	 * queues of vmdq queues, all queues including pf queues are setup.
236a9643ea8Slogwang 	 * This is because VMDQ queues doesn't always start from zero, and the
237a9643ea8Slogwang 	 * PMD layer doesn't support selectively initialising part of rx/tx
238a9643ea8Slogwang 	 * queues.
239a9643ea8Slogwang 	 */
240a9643ea8Slogwang 	rxRings = (uint16_t)dev_info.max_rx_queues;
241a9643ea8Slogwang 	txRings = (uint16_t)dev_info.max_tx_queues;
242d30ea906Sjfb8856606 
2434418919fSjohnjiang 	retval = rte_eth_dev_info_get(port, &dev_info);
2444418919fSjohnjiang 	if (retval != 0) {
2454418919fSjohnjiang 		printf("Error during getting device (port %u) info: %s\n",
2464418919fSjohnjiang 				port, strerror(-retval));
2474418919fSjohnjiang 		return retval;
2484418919fSjohnjiang 	}
2494418919fSjohnjiang 
250d30ea906Sjfb8856606 	if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
251d30ea906Sjfb8856606 		port_conf.txmode.offloads |=
252d30ea906Sjfb8856606 			DEV_TX_OFFLOAD_MBUF_FAST_FREE;
253a9643ea8Slogwang 	retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf);
254a9643ea8Slogwang 	if (retval != 0)
255a9643ea8Slogwang 		return retval;
256a9643ea8Slogwang 
2572bfe3f2eSlogwang 	retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &rxRingSize,
2582bfe3f2eSlogwang 				&txRingSize);
2592bfe3f2eSlogwang 	if (retval != 0)
2602bfe3f2eSlogwang 		return retval;
2612bfe3f2eSlogwang 	if (RTE_MAX(rxRingSize, txRingSize) > RTE_MAX(RTE_TEST_RX_DESC_DEFAULT,
2622bfe3f2eSlogwang 			RTE_TEST_TX_DESC_DEFAULT)) {
2632bfe3f2eSlogwang 		printf("Mbuf pool has an insufficient size for port %u.\n",
2642bfe3f2eSlogwang 			port);
2652bfe3f2eSlogwang 		return -1;
2662bfe3f2eSlogwang 	}
2672bfe3f2eSlogwang 
268a9643ea8Slogwang 	rxconf = &dev_info.default_rxconf;
269a9643ea8Slogwang 	rxconf->rx_drop_en = 1;
270d30ea906Sjfb8856606 	txconf = &dev_info.default_txconf;
271d30ea906Sjfb8856606 	txconf->offloads = port_conf.txmode.offloads;
272a9643ea8Slogwang 	for (q = 0; q < rxRings; q++) {
273a9643ea8Slogwang 		retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
274a9643ea8Slogwang 					rte_eth_dev_socket_id(port),
275a9643ea8Slogwang 					rxconf,
276a9643ea8Slogwang 					mbuf_pool);
277a9643ea8Slogwang 		if (retval < 0) {
278a9643ea8Slogwang 			printf("initialise rx queue %d failed\n", q);
279a9643ea8Slogwang 			return retval;
280a9643ea8Slogwang 		}
281a9643ea8Slogwang 	}
282a9643ea8Slogwang 
283a9643ea8Slogwang 	for (q = 0; q < txRings; q++) {
284a9643ea8Slogwang 		retval = rte_eth_tx_queue_setup(port, q, txRingSize,
285a9643ea8Slogwang 					rte_eth_dev_socket_id(port),
286d30ea906Sjfb8856606 					txconf);
287a9643ea8Slogwang 		if (retval < 0) {
288a9643ea8Slogwang 			printf("initialise tx queue %d failed\n", q);
289a9643ea8Slogwang 			return retval;
290a9643ea8Slogwang 		}
291a9643ea8Slogwang 	}
292a9643ea8Slogwang 
293a9643ea8Slogwang 	retval  = rte_eth_dev_start(port);
294a9643ea8Slogwang 	if (retval < 0) {
295a9643ea8Slogwang 		printf("port %d start failed\n", port);
296a9643ea8Slogwang 		return retval;
297a9643ea8Slogwang 	}
298a9643ea8Slogwang 
2994418919fSjohnjiang 	retval = rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]);
3004418919fSjohnjiang 	if (retval < 0) {
3014418919fSjohnjiang 		printf("port %d MAC address get failed: %s\n", port,
3024418919fSjohnjiang 		       rte_strerror(-retval));
3034418919fSjohnjiang 		return retval;
3044418919fSjohnjiang 	}
305a9643ea8Slogwang 	printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
306a9643ea8Slogwang 			" %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
307a9643ea8Slogwang 			(unsigned)port,
308a9643ea8Slogwang 			vmdq_ports_eth_addr[port].addr_bytes[0],
309a9643ea8Slogwang 			vmdq_ports_eth_addr[port].addr_bytes[1],
310a9643ea8Slogwang 			vmdq_ports_eth_addr[port].addr_bytes[2],
311a9643ea8Slogwang 			vmdq_ports_eth_addr[port].addr_bytes[3],
312a9643ea8Slogwang 			vmdq_ports_eth_addr[port].addr_bytes[4],
313a9643ea8Slogwang 			vmdq_ports_eth_addr[port].addr_bytes[5]);
314a9643ea8Slogwang 
315a9643ea8Slogwang 	/*
316a9643ea8Slogwang 	 * Set mac for each pool.
317a9643ea8Slogwang 	 * There is no default mac for the pools in i40.
318a9643ea8Slogwang 	 * Removes this after i40e fixes this issue.
319a9643ea8Slogwang 	 */
320a9643ea8Slogwang 	for (q = 0; q < num_pools; q++) {
3214418919fSjohnjiang 		struct rte_ether_addr mac;
322a9643ea8Slogwang 		mac = pool_addr_template;
323a9643ea8Slogwang 		mac.addr_bytes[4] = port;
324a9643ea8Slogwang 		mac.addr_bytes[5] = q;
325a9643ea8Slogwang 		printf("Port %u vmdq pool %u set mac %02x:%02x:%02x:%02x:%02x:%02x\n",
326a9643ea8Slogwang 			port, q,
327a9643ea8Slogwang 			mac.addr_bytes[0], mac.addr_bytes[1],
328a9643ea8Slogwang 			mac.addr_bytes[2], mac.addr_bytes[3],
329a9643ea8Slogwang 			mac.addr_bytes[4], mac.addr_bytes[5]);
330a9643ea8Slogwang 		retval = rte_eth_dev_mac_addr_add(port, &mac,
331a9643ea8Slogwang 				q + vmdq_pool_base);
332a9643ea8Slogwang 		if (retval) {
333a9643ea8Slogwang 			printf("mac addr add failed at pool %d\n", q);
334a9643ea8Slogwang 			return retval;
335a9643ea8Slogwang 		}
336a9643ea8Slogwang 	}
337a9643ea8Slogwang 
338a9643ea8Slogwang 	return 0;
339a9643ea8Slogwang }
340a9643ea8Slogwang 
341a9643ea8Slogwang /* Check num_pools parameter and set it if OK*/
342a9643ea8Slogwang static int
vmdq_parse_num_pools(const char * q_arg)343a9643ea8Slogwang vmdq_parse_num_pools(const char *q_arg)
344a9643ea8Slogwang {
345a9643ea8Slogwang 	char *end = NULL;
346a9643ea8Slogwang 	int n;
347a9643ea8Slogwang 
348a9643ea8Slogwang 	/* parse number string */
349a9643ea8Slogwang 	n = strtol(q_arg, &end, 10);
350a9643ea8Slogwang 	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
351a9643ea8Slogwang 		return -1;
352a9643ea8Slogwang 
353a9643ea8Slogwang 	if (num_pools > num_vlans) {
354a9643ea8Slogwang 		printf("num_pools %d > num_vlans %d\n", num_pools, num_vlans);
355a9643ea8Slogwang 		return -1;
356a9643ea8Slogwang 	}
357a9643ea8Slogwang 
358a9643ea8Slogwang 	num_pools = n;
359a9643ea8Slogwang 
360a9643ea8Slogwang 	return 0;
361a9643ea8Slogwang }
362a9643ea8Slogwang 
363a9643ea8Slogwang 
364a9643ea8Slogwang static int
parse_portmask(const char * portmask)365a9643ea8Slogwang parse_portmask(const char *portmask)
366a9643ea8Slogwang {
367a9643ea8Slogwang 	char *end = NULL;
368a9643ea8Slogwang 	unsigned long pm;
369a9643ea8Slogwang 
370a9643ea8Slogwang 	/* parse hexadecimal string */
371a9643ea8Slogwang 	pm = strtoul(portmask, &end, 16);
372a9643ea8Slogwang 	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
373*2d9fd380Sjfb8856606 		return 0;
374a9643ea8Slogwang 
375a9643ea8Slogwang 	return pm;
376a9643ea8Slogwang }
377a9643ea8Slogwang 
378a9643ea8Slogwang /* Display usage */
379a9643ea8Slogwang static void
vmdq_usage(const char * prgname)380a9643ea8Slogwang vmdq_usage(const char *prgname)
381a9643ea8Slogwang {
382a9643ea8Slogwang 	printf("%s [EAL options] -- -p PORTMASK]\n"
3830c6bd470Sfengbojiang 	"  --nb-pools NP: number of pools\n"
3840c6bd470Sfengbojiang 	"  --enable-rss: enable RSS (disabled by default)\n",
385a9643ea8Slogwang 	       prgname);
386a9643ea8Slogwang }
387a9643ea8Slogwang 
388a9643ea8Slogwang /*  Parse the argument (num_pools) given in the command line of the application */
389a9643ea8Slogwang static int
vmdq_parse_args(int argc,char ** argv)390a9643ea8Slogwang vmdq_parse_args(int argc, char **argv)
391a9643ea8Slogwang {
392a9643ea8Slogwang 	int opt;
393a9643ea8Slogwang 	int option_index;
394a9643ea8Slogwang 	unsigned i;
395a9643ea8Slogwang 	const char *prgname = argv[0];
396a9643ea8Slogwang 	static struct option long_option[] = {
397a9643ea8Slogwang 		{"nb-pools", required_argument, NULL, 0},
3980c6bd470Sfengbojiang 		{"enable-rss", 0, NULL, 0},
399a9643ea8Slogwang 		{NULL, 0, 0, 0}
400a9643ea8Slogwang 	};
401a9643ea8Slogwang 
402a9643ea8Slogwang 	/* Parse command line */
403a9643ea8Slogwang 	while ((opt = getopt_long(argc, argv, "p:", long_option,
404a9643ea8Slogwang 		&option_index)) != EOF) {
405a9643ea8Slogwang 		switch (opt) {
406a9643ea8Slogwang 		/* portmask */
407a9643ea8Slogwang 		case 'p':
408a9643ea8Slogwang 			enabled_port_mask = parse_portmask(optarg);
409a9643ea8Slogwang 			if (enabled_port_mask == 0) {
410a9643ea8Slogwang 				printf("invalid portmask\n");
411a9643ea8Slogwang 				vmdq_usage(prgname);
412a9643ea8Slogwang 				return -1;
413a9643ea8Slogwang 			}
414a9643ea8Slogwang 			break;
415a9643ea8Slogwang 		case 0:
4160c6bd470Sfengbojiang 			if (!strcmp(long_option[option_index].name,
4170c6bd470Sfengbojiang 			    "nb-pools")) {
418a9643ea8Slogwang 				if (vmdq_parse_num_pools(optarg) == -1) {
419a9643ea8Slogwang 					printf("invalid number of pools\n");
420a9643ea8Slogwang 					vmdq_usage(prgname);
421a9643ea8Slogwang 					return -1;
422a9643ea8Slogwang 				}
4230c6bd470Sfengbojiang 			}
4240c6bd470Sfengbojiang 
4250c6bd470Sfengbojiang 			if (!strcmp(long_option[option_index].name,
4260c6bd470Sfengbojiang 			    "enable-rss"))
4270c6bd470Sfengbojiang 				rss_enable = 1;
428a9643ea8Slogwang 			break;
429a9643ea8Slogwang 
430a9643ea8Slogwang 		default:
431a9643ea8Slogwang 			vmdq_usage(prgname);
432a9643ea8Slogwang 			return -1;
433a9643ea8Slogwang 		}
434a9643ea8Slogwang 	}
435a9643ea8Slogwang 
436a9643ea8Slogwang 	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
437a9643ea8Slogwang 		if (enabled_port_mask & (1 << i))
438a9643ea8Slogwang 			ports[num_ports++] = (uint8_t)i;
439a9643ea8Slogwang 	}
440a9643ea8Slogwang 
441a9643ea8Slogwang 	if (num_ports < 2 || num_ports % 2) {
442a9643ea8Slogwang 		printf("Current enabled port number is %u,"
443a9643ea8Slogwang 			"but it should be even and at least 2\n", num_ports);
444a9643ea8Slogwang 		return -1;
445a9643ea8Slogwang 	}
446a9643ea8Slogwang 
447a9643ea8Slogwang 	return 0;
448a9643ea8Slogwang }
449a9643ea8Slogwang 
450a9643ea8Slogwang static void
update_mac_address(struct rte_mbuf * m,unsigned dst_port)451a9643ea8Slogwang update_mac_address(struct rte_mbuf *m, unsigned dst_port)
452a9643ea8Slogwang {
4534418919fSjohnjiang 	struct rte_ether_hdr *eth;
454a9643ea8Slogwang 	void *tmp;
455a9643ea8Slogwang 
4564418919fSjohnjiang 	eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
457a9643ea8Slogwang 
458a9643ea8Slogwang 	/* 02:00:00:00:00:xx */
459a9643ea8Slogwang 	tmp = &eth->d_addr.addr_bytes[0];
460a9643ea8Slogwang 	*((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
461a9643ea8Slogwang 
462a9643ea8Slogwang 	/* src addr */
4634418919fSjohnjiang 	rte_ether_addr_copy(&vmdq_ports_eth_addr[dst_port], &eth->s_addr);
464a9643ea8Slogwang }
465a9643ea8Slogwang 
466a9643ea8Slogwang /* When we receive a HUP signal, print out our stats */
467a9643ea8Slogwang static void
sighup_handler(int signum)468a9643ea8Slogwang sighup_handler(int signum)
469a9643ea8Slogwang {
4700c6bd470Sfengbojiang 	unsigned int q = vmdq_queue_base;
4710c6bd470Sfengbojiang 	for (; q < num_queues; q++) {
4720c6bd470Sfengbojiang 		if ((q - vmdq_queue_base) % (num_vmdq_queues / num_pools) == 0)
4730c6bd470Sfengbojiang 			printf("\nPool %u: ", (q - vmdq_queue_base) /
4740c6bd470Sfengbojiang 			       (num_vmdq_queues / num_pools));
475a9643ea8Slogwang 		printf("%lu ", rxPackets[q]);
476a9643ea8Slogwang 	}
477a9643ea8Slogwang 	printf("\nFinished handling signal %d\n", signum);
478a9643ea8Slogwang }
479a9643ea8Slogwang 
480a9643ea8Slogwang /*
481a9643ea8Slogwang  * Main thread that does the work, reading from INPUT_PORT
482a9643ea8Slogwang  * and writing to OUTPUT_PORT
483a9643ea8Slogwang  */
484a9643ea8Slogwang static int
lcore_main(__rte_unused void * dummy)485*2d9fd380Sjfb8856606 lcore_main(__rte_unused void *dummy)
486a9643ea8Slogwang {
487a9643ea8Slogwang 	const uint16_t lcore_id = (uint16_t)rte_lcore_id();
488a9643ea8Slogwang 	const uint16_t num_cores = (uint16_t)rte_lcore_count();
489a9643ea8Slogwang 	uint16_t core_id = 0;
490a9643ea8Slogwang 	uint16_t startQueue, endQueue;
491a9643ea8Slogwang 	uint16_t q, i, p;
492a9643ea8Slogwang 	const uint16_t remainder = (uint16_t)(num_vmdq_queues % num_cores);
493a9643ea8Slogwang 
494a9643ea8Slogwang 	for (i = 0; i < num_cores; i++)
495a9643ea8Slogwang 		if (lcore_ids[i] == lcore_id) {
496a9643ea8Slogwang 			core_id = i;
497a9643ea8Slogwang 			break;
498a9643ea8Slogwang 		}
499a9643ea8Slogwang 
500a9643ea8Slogwang 	if (remainder != 0) {
501a9643ea8Slogwang 		if (core_id < remainder) {
502a9643ea8Slogwang 			startQueue = (uint16_t)(core_id *
503a9643ea8Slogwang 					(num_vmdq_queues / num_cores + 1));
504a9643ea8Slogwang 			endQueue = (uint16_t)(startQueue +
505a9643ea8Slogwang 					(num_vmdq_queues / num_cores) + 1);
506a9643ea8Slogwang 		} else {
507a9643ea8Slogwang 			startQueue = (uint16_t)(core_id *
508a9643ea8Slogwang 					(num_vmdq_queues / num_cores) +
509a9643ea8Slogwang 					remainder);
510a9643ea8Slogwang 			endQueue = (uint16_t)(startQueue +
511a9643ea8Slogwang 					(num_vmdq_queues / num_cores));
512a9643ea8Slogwang 		}
513a9643ea8Slogwang 	} else {
514a9643ea8Slogwang 		startQueue = (uint16_t)(core_id *
515a9643ea8Slogwang 				(num_vmdq_queues / num_cores));
516a9643ea8Slogwang 		endQueue = (uint16_t)(startQueue +
517a9643ea8Slogwang 				(num_vmdq_queues / num_cores));
518a9643ea8Slogwang 	}
519a9643ea8Slogwang 
520a9643ea8Slogwang 	/* vmdq queue idx doesn't always start from zero.*/
521a9643ea8Slogwang 	startQueue += vmdq_queue_base;
522a9643ea8Slogwang 	endQueue   += vmdq_queue_base;
523a9643ea8Slogwang 	printf("core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_id,
524a9643ea8Slogwang 		(unsigned)lcore_id, startQueue, endQueue - 1);
525a9643ea8Slogwang 
526a9643ea8Slogwang 	if (startQueue == endQueue) {
527a9643ea8Slogwang 		printf("lcore %u has nothing to do\n", lcore_id);
528a9643ea8Slogwang 		return 0;
529a9643ea8Slogwang 	}
530a9643ea8Slogwang 
531a9643ea8Slogwang 	for (;;) {
532a9643ea8Slogwang 		struct rte_mbuf *buf[MAX_PKT_BURST];
533*2d9fd380Sjfb8856606 		const uint16_t buf_size = RTE_DIM(buf);
534a9643ea8Slogwang 
535a9643ea8Slogwang 		for (p = 0; p < num_ports; p++) {
536a9643ea8Slogwang 			const uint8_t sport = ports[p];
537a9643ea8Slogwang 			/* 0 <-> 1, 2 <-> 3 etc */
538a9643ea8Slogwang 			const uint8_t dport = ports[p ^ 1];
539a9643ea8Slogwang 			if ((sport == INVALID_PORT_ID) || (dport == INVALID_PORT_ID))
540a9643ea8Slogwang 				continue;
541a9643ea8Slogwang 
542a9643ea8Slogwang 			for (q = startQueue; q < endQueue; q++) {
543a9643ea8Slogwang 				const uint16_t rxCount = rte_eth_rx_burst(sport,
544a9643ea8Slogwang 					q, buf, buf_size);
545a9643ea8Slogwang 
546a9643ea8Slogwang 				if (unlikely(rxCount == 0))
547a9643ea8Slogwang 					continue;
548a9643ea8Slogwang 
549a9643ea8Slogwang 				rxPackets[q] += rxCount;
550a9643ea8Slogwang 
551a9643ea8Slogwang 				for (i = 0; i < rxCount; i++)
552a9643ea8Slogwang 					update_mac_address(buf[i], dport);
553a9643ea8Slogwang 
554a9643ea8Slogwang 				const uint16_t txCount = rte_eth_tx_burst(dport,
555a9643ea8Slogwang 					vmdq_queue_base + core_id,
556a9643ea8Slogwang 					buf,
557a9643ea8Slogwang 					rxCount);
558a9643ea8Slogwang 
559a9643ea8Slogwang 				if (txCount != rxCount) {
560a9643ea8Slogwang 					for (i = txCount; i < rxCount; i++)
561a9643ea8Slogwang 						rte_pktmbuf_free(buf[i]);
562a9643ea8Slogwang 				}
563a9643ea8Slogwang 			}
564a9643ea8Slogwang 		}
565a9643ea8Slogwang 	}
566a9643ea8Slogwang }
567a9643ea8Slogwang 
568a9643ea8Slogwang /*
569a9643ea8Slogwang  * Update the global var NUM_PORTS and array PORTS according to system ports number
570a9643ea8Slogwang  * and return valid ports number
571a9643ea8Slogwang  */
check_ports_num(unsigned nb_ports)572a9643ea8Slogwang static unsigned check_ports_num(unsigned nb_ports)
573a9643ea8Slogwang {
574a9643ea8Slogwang 	unsigned valid_num_ports = num_ports;
575a9643ea8Slogwang 	unsigned portid;
576a9643ea8Slogwang 
577a9643ea8Slogwang 	if (num_ports > nb_ports) {
578a9643ea8Slogwang 		printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
579a9643ea8Slogwang 			num_ports, nb_ports);
580a9643ea8Slogwang 		num_ports = nb_ports;
581a9643ea8Slogwang 	}
582a9643ea8Slogwang 
583a9643ea8Slogwang 	for (portid = 0; portid < num_ports; portid++) {
584d30ea906Sjfb8856606 		if (!rte_eth_dev_is_valid_port(ports[portid])) {
585d30ea906Sjfb8856606 			printf("\nSpecified port ID(%u) is not valid\n",
586d30ea906Sjfb8856606 				ports[portid]);
587a9643ea8Slogwang 			ports[portid] = INVALID_PORT_ID;
588a9643ea8Slogwang 			valid_num_ports--;
589a9643ea8Slogwang 		}
590a9643ea8Slogwang 	}
591a9643ea8Slogwang 	return valid_num_ports;
592a9643ea8Slogwang }
593a9643ea8Slogwang 
594a9643ea8Slogwang /* Main function, does initialisation and calls the per-lcore functions */
595a9643ea8Slogwang int
main(int argc,char * argv[])596a9643ea8Slogwang main(int argc, char *argv[])
597a9643ea8Slogwang {
598a9643ea8Slogwang 	struct rte_mempool *mbuf_pool;
599a9643ea8Slogwang 	unsigned lcore_id, core_id = 0;
600a9643ea8Slogwang 	int ret;
601a9643ea8Slogwang 	unsigned nb_ports, valid_num_ports;
6022bfe3f2eSlogwang 	uint16_t portid;
603a9643ea8Slogwang 
604a9643ea8Slogwang 	signal(SIGHUP, sighup_handler);
605a9643ea8Slogwang 
606a9643ea8Slogwang 	/* init EAL */
607a9643ea8Slogwang 	ret = rte_eal_init(argc, argv);
608a9643ea8Slogwang 	if (ret < 0)
609a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
610a9643ea8Slogwang 	argc -= ret;
611a9643ea8Slogwang 	argv += ret;
612a9643ea8Slogwang 
613a9643ea8Slogwang 	/* parse app arguments */
614a9643ea8Slogwang 	ret = vmdq_parse_args(argc, argv);
615a9643ea8Slogwang 	if (ret < 0)
616a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
617a9643ea8Slogwang 
618a9643ea8Slogwang 	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++)
619a9643ea8Slogwang 		if (rte_lcore_is_enabled(lcore_id))
620a9643ea8Slogwang 			lcore_ids[core_id++] = lcore_id;
621a9643ea8Slogwang 
622a9643ea8Slogwang 	if (rte_lcore_count() > RTE_MAX_LCORE)
623a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Not enough cores\n");
624a9643ea8Slogwang 
625d30ea906Sjfb8856606 	nb_ports = rte_eth_dev_count_avail();
626a9643ea8Slogwang 
627a9643ea8Slogwang 	/*
628a9643ea8Slogwang 	 * Update the global var NUM_PORTS and global array PORTS
629a9643ea8Slogwang 	 * and get value of var VALID_NUM_PORTS according to system ports number
630a9643ea8Slogwang 	 */
631a9643ea8Slogwang 	valid_num_ports = check_ports_num(nb_ports);
632a9643ea8Slogwang 
633a9643ea8Slogwang 	if (valid_num_ports < 2 || valid_num_ports % 2) {
634a9643ea8Slogwang 		printf("Current valid ports number is %u\n", valid_num_ports);
635a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
636a9643ea8Slogwang 	}
637a9643ea8Slogwang 
638a9643ea8Slogwang 	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
639a9643ea8Slogwang 		NUM_MBUFS_PER_PORT * nb_ports, MBUF_CACHE_SIZE,
640a9643ea8Slogwang 		0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
641a9643ea8Slogwang 	if (mbuf_pool == NULL)
642a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
643a9643ea8Slogwang 
644a9643ea8Slogwang 	/* initialize all ports */
645d30ea906Sjfb8856606 	RTE_ETH_FOREACH_DEV(portid) {
646a9643ea8Slogwang 		/* skip ports that are not enabled */
647a9643ea8Slogwang 		if ((enabled_port_mask & (1 << portid)) == 0) {
648a9643ea8Slogwang 			printf("\nSkipping disabled port %d\n", portid);
649a9643ea8Slogwang 			continue;
650a9643ea8Slogwang 		}
651a9643ea8Slogwang 		if (port_init(portid, mbuf_pool) != 0)
652a9643ea8Slogwang 			rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
653a9643ea8Slogwang 	}
654a9643ea8Slogwang 
655a9643ea8Slogwang 	/* call lcore_main() on every lcore */
656*2d9fd380Sjfb8856606 	rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MAIN);
657*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(lcore_id) {
658a9643ea8Slogwang 		if (rte_eal_wait_lcore(lcore_id) < 0)
659a9643ea8Slogwang 			return -1;
660a9643ea8Slogwang 	}
661a9643ea8Slogwang 
662a9643ea8Slogwang 	return 0;
663a9643ea8Slogwang }
664