xref: /f-stack/dpdk/examples/distributor/main.c (revision 2d9fd380)
1d30ea906Sjfb8856606 /* SPDX-License-Identifier: BSD-3-Clause
2d30ea906Sjfb8856606  * Copyright(c) 2010-2017 Intel Corporation
3a9643ea8Slogwang  */
4a9643ea8Slogwang 
5a9643ea8Slogwang #include <stdint.h>
6a9643ea8Slogwang #include <inttypes.h>
7a9643ea8Slogwang #include <unistd.h>
8a9643ea8Slogwang #include <signal.h>
9a9643ea8Slogwang #include <getopt.h>
10a9643ea8Slogwang 
11a9643ea8Slogwang #include <rte_eal.h>
12a9643ea8Slogwang #include <rte_ethdev.h>
13a9643ea8Slogwang #include <rte_cycles.h>
14a9643ea8Slogwang #include <rte_malloc.h>
15a9643ea8Slogwang #include <rte_debug.h>
16a9643ea8Slogwang #include <rte_prefetch.h>
17a9643ea8Slogwang #include <rte_distributor.h>
182bfe3f2eSlogwang #include <rte_pause.h>
194418919fSjohnjiang #include <rte_power.h>
20a9643ea8Slogwang 
21d30ea906Sjfb8856606 #define RX_RING_SIZE 1024
22d30ea906Sjfb8856606 #define TX_RING_SIZE 1024
23a9643ea8Slogwang #define NUM_MBUFS ((64*1024)-1)
242bfe3f2eSlogwang #define MBUF_CACHE_SIZE 128
252bfe3f2eSlogwang #define BURST_SIZE 64
262bfe3f2eSlogwang #define SCHED_RX_RING_SZ 8192
272bfe3f2eSlogwang #define SCHED_TX_RING_SZ 65536
282bfe3f2eSlogwang #define BURST_SIZE_TX 32
29a9643ea8Slogwang 
30a9643ea8Slogwang #define RTE_LOGTYPE_DISTRAPP RTE_LOGTYPE_USER1
31a9643ea8Slogwang 
322bfe3f2eSlogwang #define ANSI_COLOR_RED     "\x1b[31m"
332bfe3f2eSlogwang #define ANSI_COLOR_RESET   "\x1b[0m"
342bfe3f2eSlogwang 
35a9643ea8Slogwang /* mask of enabled ports */
36a9643ea8Slogwang static uint32_t enabled_port_mask;
37a9643ea8Slogwang volatile uint8_t quit_signal;
38a9643ea8Slogwang volatile uint8_t quit_signal_rx;
392bfe3f2eSlogwang volatile uint8_t quit_signal_dist;
402bfe3f2eSlogwang volatile uint8_t quit_signal_work;
414418919fSjohnjiang unsigned int power_lib_initialised;
42a9643ea8Slogwang 
43a9643ea8Slogwang static volatile struct app_stats {
44a9643ea8Slogwang 	struct {
45a9643ea8Slogwang 		uint64_t rx_pkts;
46a9643ea8Slogwang 		uint64_t returned_pkts;
47a9643ea8Slogwang 		uint64_t enqueued_pkts;
482bfe3f2eSlogwang 		uint64_t enqdrop_pkts;
49a9643ea8Slogwang 	} rx __rte_cache_aligned;
502bfe3f2eSlogwang 	int pad1 __rte_cache_aligned;
512bfe3f2eSlogwang 
522bfe3f2eSlogwang 	struct {
532bfe3f2eSlogwang 		uint64_t in_pkts;
542bfe3f2eSlogwang 		uint64_t ret_pkts;
552bfe3f2eSlogwang 		uint64_t sent_pkts;
562bfe3f2eSlogwang 		uint64_t enqdrop_pkts;
572bfe3f2eSlogwang 	} dist __rte_cache_aligned;
582bfe3f2eSlogwang 	int pad2 __rte_cache_aligned;
59a9643ea8Slogwang 
60a9643ea8Slogwang 	struct {
61a9643ea8Slogwang 		uint64_t dequeue_pkts;
62a9643ea8Slogwang 		uint64_t tx_pkts;
632bfe3f2eSlogwang 		uint64_t enqdrop_pkts;
64a9643ea8Slogwang 	} tx __rte_cache_aligned;
652bfe3f2eSlogwang 	int pad3 __rte_cache_aligned;
662bfe3f2eSlogwang 
672bfe3f2eSlogwang 	uint64_t worker_pkts[64] __rte_cache_aligned;
682bfe3f2eSlogwang 
692bfe3f2eSlogwang 	int pad4 __rte_cache_aligned;
702bfe3f2eSlogwang 
712bfe3f2eSlogwang 	uint64_t worker_bursts[64][8] __rte_cache_aligned;
722bfe3f2eSlogwang 
732bfe3f2eSlogwang 	int pad5 __rte_cache_aligned;
742bfe3f2eSlogwang 
752bfe3f2eSlogwang 	uint64_t port_rx_pkts[64] __rte_cache_aligned;
762bfe3f2eSlogwang 	uint64_t port_tx_pkts[64] __rte_cache_aligned;
77a9643ea8Slogwang } app_stats;
78a9643ea8Slogwang 
792bfe3f2eSlogwang struct app_stats prev_app_stats;
802bfe3f2eSlogwang 
81a9643ea8Slogwang static const struct rte_eth_conf port_conf_default = {
82a9643ea8Slogwang 	.rxmode = {
83a9643ea8Slogwang 		.mq_mode = ETH_MQ_RX_RSS,
844418919fSjohnjiang 		.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
85a9643ea8Slogwang 	},
86a9643ea8Slogwang 	.txmode = {
87a9643ea8Slogwang 		.mq_mode = ETH_MQ_TX_NONE,
88a9643ea8Slogwang 	},
89a9643ea8Slogwang 	.rx_adv_conf = {
90a9643ea8Slogwang 		.rss_conf = {
91a9643ea8Slogwang 			.rss_hf = ETH_RSS_IP | ETH_RSS_UDP |
92a9643ea8Slogwang 				ETH_RSS_TCP | ETH_RSS_SCTP,
93a9643ea8Slogwang 		}
94a9643ea8Slogwang 	},
95a9643ea8Slogwang };
96a9643ea8Slogwang 
97a9643ea8Slogwang struct output_buffer {
98a9643ea8Slogwang 	unsigned count;
99a9643ea8Slogwang 	struct rte_mbuf *mbufs[BURST_SIZE];
100a9643ea8Slogwang };
101a9643ea8Slogwang 
1022bfe3f2eSlogwang static void print_stats(void);
1032bfe3f2eSlogwang 
104a9643ea8Slogwang /*
105a9643ea8Slogwang  * Initialises a given port using global settings and with the rx buffers
106a9643ea8Slogwang  * coming from the mbuf_pool passed as parameter
107a9643ea8Slogwang  */
108a9643ea8Slogwang static inline int
port_init(uint16_t port,struct rte_mempool * mbuf_pool)1092bfe3f2eSlogwang port_init(uint16_t port, struct rte_mempool *mbuf_pool)
110a9643ea8Slogwang {
111a9643ea8Slogwang 	struct rte_eth_conf port_conf = port_conf_default;
112a9643ea8Slogwang 	const uint16_t rxRings = 1, txRings = rte_lcore_count() - 1;
113a9643ea8Slogwang 	int retval;
114a9643ea8Slogwang 	uint16_t q;
1152bfe3f2eSlogwang 	uint16_t nb_rxd = RX_RING_SIZE;
1162bfe3f2eSlogwang 	uint16_t nb_txd = TX_RING_SIZE;
117d30ea906Sjfb8856606 	struct rte_eth_dev_info dev_info;
118d30ea906Sjfb8856606 	struct rte_eth_txconf txconf;
119a9643ea8Slogwang 
120d30ea906Sjfb8856606 	if (!rte_eth_dev_is_valid_port(port))
121a9643ea8Slogwang 		return -1;
122a9643ea8Slogwang 
1234418919fSjohnjiang 	retval = rte_eth_dev_info_get(port, &dev_info);
1244418919fSjohnjiang 	if (retval != 0) {
1254418919fSjohnjiang 		printf("Error during getting device (port %u) info: %s\n",
1264418919fSjohnjiang 				port, strerror(-retval));
1274418919fSjohnjiang 		return retval;
1284418919fSjohnjiang 	}
1294418919fSjohnjiang 
130d30ea906Sjfb8856606 	if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
131d30ea906Sjfb8856606 		port_conf.txmode.offloads |=
132d30ea906Sjfb8856606 			DEV_TX_OFFLOAD_MBUF_FAST_FREE;
133d30ea906Sjfb8856606 
134d30ea906Sjfb8856606 	port_conf.rx_adv_conf.rss_conf.rss_hf &=
135d30ea906Sjfb8856606 		dev_info.flow_type_rss_offloads;
136d30ea906Sjfb8856606 	if (port_conf.rx_adv_conf.rss_conf.rss_hf !=
137d30ea906Sjfb8856606 			port_conf_default.rx_adv_conf.rss_conf.rss_hf) {
138d30ea906Sjfb8856606 		printf("Port %u modified RSS hash function based on hardware support,"
139d30ea906Sjfb8856606 			"requested:%#"PRIx64" configured:%#"PRIx64"\n",
140d30ea906Sjfb8856606 			port,
141d30ea906Sjfb8856606 			port_conf_default.rx_adv_conf.rss_conf.rss_hf,
142d30ea906Sjfb8856606 			port_conf.rx_adv_conf.rss_conf.rss_hf);
143d30ea906Sjfb8856606 	}
144d30ea906Sjfb8856606 
145a9643ea8Slogwang 	retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf);
146a9643ea8Slogwang 	if (retval != 0)
147a9643ea8Slogwang 		return retval;
148a9643ea8Slogwang 
1492bfe3f2eSlogwang 	retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
1502bfe3f2eSlogwang 	if (retval != 0)
1512bfe3f2eSlogwang 		return retval;
1522bfe3f2eSlogwang 
153a9643ea8Slogwang 	for (q = 0; q < rxRings; q++) {
1542bfe3f2eSlogwang 		retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
155a9643ea8Slogwang 						rte_eth_dev_socket_id(port),
156a9643ea8Slogwang 						NULL, mbuf_pool);
157a9643ea8Slogwang 		if (retval < 0)
158a9643ea8Slogwang 			return retval;
159a9643ea8Slogwang 	}
160a9643ea8Slogwang 
161d30ea906Sjfb8856606 	txconf = dev_info.default_txconf;
162d30ea906Sjfb8856606 	txconf.offloads = port_conf.txmode.offloads;
163a9643ea8Slogwang 	for (q = 0; q < txRings; q++) {
1642bfe3f2eSlogwang 		retval = rte_eth_tx_queue_setup(port, q, nb_txd,
165a9643ea8Slogwang 						rte_eth_dev_socket_id(port),
166d30ea906Sjfb8856606 						&txconf);
167a9643ea8Slogwang 		if (retval < 0)
168a9643ea8Slogwang 			return retval;
169a9643ea8Slogwang 	}
170a9643ea8Slogwang 
171a9643ea8Slogwang 	retval = rte_eth_dev_start(port);
172a9643ea8Slogwang 	if (retval < 0)
173a9643ea8Slogwang 		return retval;
174a9643ea8Slogwang 
175a9643ea8Slogwang 	struct rte_eth_link link;
1764418919fSjohnjiang 	do {
1774418919fSjohnjiang 		retval = rte_eth_link_get_nowait(port, &link);
1784418919fSjohnjiang 		if (retval < 0) {
1794418919fSjohnjiang 			printf("Failed link get (port %u): %s\n",
1804418919fSjohnjiang 				port, rte_strerror(-retval));
1814418919fSjohnjiang 			return retval;
1824418919fSjohnjiang 		} else if (link.link_status)
1834418919fSjohnjiang 			break;
1844418919fSjohnjiang 
1852bfe3f2eSlogwang 		printf("Waiting for Link up on port %"PRIu16"\n", port);
186a9643ea8Slogwang 		sleep(1);
1874418919fSjohnjiang 	} while (!link.link_status);
188a9643ea8Slogwang 
189a9643ea8Slogwang 	if (!link.link_status) {
1902bfe3f2eSlogwang 		printf("Link down on port %"PRIu16"\n", port);
191a9643ea8Slogwang 		return 0;
192a9643ea8Slogwang 	}
193a9643ea8Slogwang 
1944418919fSjohnjiang 	struct rte_ether_addr addr;
1954418919fSjohnjiang 	retval = rte_eth_macaddr_get(port, &addr);
1964418919fSjohnjiang 	if (retval < 0) {
1974418919fSjohnjiang 		printf("Failed to get MAC address (port %u): %s\n",
1984418919fSjohnjiang 				port, rte_strerror(-retval));
1994418919fSjohnjiang 		return retval;
2004418919fSjohnjiang 	}
2014418919fSjohnjiang 
202a9643ea8Slogwang 	printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
203a9643ea8Slogwang 			" %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
2042bfe3f2eSlogwang 			port,
205a9643ea8Slogwang 			addr.addr_bytes[0], addr.addr_bytes[1],
206a9643ea8Slogwang 			addr.addr_bytes[2], addr.addr_bytes[3],
207a9643ea8Slogwang 			addr.addr_bytes[4], addr.addr_bytes[5]);
208a9643ea8Slogwang 
2094418919fSjohnjiang 	retval = rte_eth_promiscuous_enable(port);
2104418919fSjohnjiang 	if (retval != 0)
2114418919fSjohnjiang 		return retval;
212a9643ea8Slogwang 
213a9643ea8Slogwang 	return 0;
214a9643ea8Slogwang }
215a9643ea8Slogwang 
216a9643ea8Slogwang struct lcore_params {
217a9643ea8Slogwang 	unsigned worker_id;
218a9643ea8Slogwang 	struct rte_distributor *d;
2192bfe3f2eSlogwang 	struct rte_ring *rx_dist_ring;
2202bfe3f2eSlogwang 	struct rte_ring *dist_tx_ring;
221a9643ea8Slogwang 	struct rte_mempool *mem_pool;
222a9643ea8Slogwang };
223a9643ea8Slogwang 
224a9643ea8Slogwang static int
lcore_rx(struct lcore_params * p)225a9643ea8Slogwang lcore_rx(struct lcore_params *p)
226a9643ea8Slogwang {
227d30ea906Sjfb8856606 	const uint16_t nb_ports = rte_eth_dev_count_avail();
228a9643ea8Slogwang 	const int socket_id = rte_socket_id();
2292bfe3f2eSlogwang 	uint16_t port;
2302bfe3f2eSlogwang 	struct rte_mbuf *bufs[BURST_SIZE*2];
231a9643ea8Slogwang 
232d30ea906Sjfb8856606 	RTE_ETH_FOREACH_DEV(port) {
233a9643ea8Slogwang 		/* skip ports that are not enabled */
234a9643ea8Slogwang 		if ((enabled_port_mask & (1 << port)) == 0)
235a9643ea8Slogwang 			continue;
236a9643ea8Slogwang 
237a9643ea8Slogwang 		if (rte_eth_dev_socket_id(port) > 0 &&
238a9643ea8Slogwang 				rte_eth_dev_socket_id(port) != socket_id)
239a9643ea8Slogwang 			printf("WARNING, port %u is on remote NUMA node to "
240a9643ea8Slogwang 					"RX thread.\n\tPerformance will not "
241a9643ea8Slogwang 					"be optimal.\n", port);
242a9643ea8Slogwang 	}
243a9643ea8Slogwang 
244a9643ea8Slogwang 	printf("\nCore %u doing packet RX.\n", rte_lcore_id());
245a9643ea8Slogwang 	port = 0;
246a9643ea8Slogwang 	while (!quit_signal_rx) {
247a9643ea8Slogwang 
248a9643ea8Slogwang 		/* skip ports that are not enabled */
249a9643ea8Slogwang 		if ((enabled_port_mask & (1 << port)) == 0) {
250a9643ea8Slogwang 			if (++port == nb_ports)
251a9643ea8Slogwang 				port = 0;
252a9643ea8Slogwang 			continue;
253a9643ea8Slogwang 		}
254a9643ea8Slogwang 		const uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs,
255a9643ea8Slogwang 				BURST_SIZE);
256a9643ea8Slogwang 		if (unlikely(nb_rx == 0)) {
257a9643ea8Slogwang 			if (++port == nb_ports)
258a9643ea8Slogwang 				port = 0;
259a9643ea8Slogwang 			continue;
260a9643ea8Slogwang 		}
261a9643ea8Slogwang 		app_stats.rx.rx_pkts += nb_rx;
262a9643ea8Slogwang 
2632bfe3f2eSlogwang /*
2642bfe3f2eSlogwang  * You can run the distributor on the rx core with this code. Returned
2652bfe3f2eSlogwang  * packets are then send straight to the tx core.
2662bfe3f2eSlogwang  */
2672bfe3f2eSlogwang #if 0
268a9643ea8Slogwang 	rte_distributor_process(d, bufs, nb_rx);
2692bfe3f2eSlogwang 	const uint16_t nb_ret = rte_distributor_returned_pktsd,
270a9643ea8Slogwang 			bufs, BURST_SIZE*2);
2712bfe3f2eSlogwang 
272a9643ea8Slogwang 		app_stats.rx.returned_pkts += nb_ret;
273a9643ea8Slogwang 		if (unlikely(nb_ret == 0)) {
274a9643ea8Slogwang 			if (++port == nb_ports)
275a9643ea8Slogwang 				port = 0;
276a9643ea8Slogwang 			continue;
277a9643ea8Slogwang 		}
278a9643ea8Slogwang 
2792bfe3f2eSlogwang 		struct rte_ring *tx_ring = p->dist_tx_ring;
2802bfe3f2eSlogwang 		uint16_t sent = rte_ring_enqueue_burst(tx_ring,
2812bfe3f2eSlogwang 				(void *)bufs, nb_ret, NULL);
2822bfe3f2eSlogwang #else
2832bfe3f2eSlogwang 		uint16_t nb_ret = nb_rx;
2842bfe3f2eSlogwang 		/*
2852bfe3f2eSlogwang 		 * Swap the following two lines if you want the rx traffic
2862bfe3f2eSlogwang 		 * to go directly to tx, no distribution.
2872bfe3f2eSlogwang 		 */
2882bfe3f2eSlogwang 		struct rte_ring *out_ring = p->rx_dist_ring;
2892bfe3f2eSlogwang 		/* struct rte_ring *out_ring = p->dist_tx_ring; */
2902bfe3f2eSlogwang 
2912bfe3f2eSlogwang 		uint16_t sent = rte_ring_enqueue_burst(out_ring,
2922bfe3f2eSlogwang 				(void *)bufs, nb_ret, NULL);
2932bfe3f2eSlogwang #endif
2942bfe3f2eSlogwang 
295a9643ea8Slogwang 		app_stats.rx.enqueued_pkts += sent;
296a9643ea8Slogwang 		if (unlikely(sent < nb_ret)) {
2972bfe3f2eSlogwang 			app_stats.rx.enqdrop_pkts +=  nb_ret - sent;
2982bfe3f2eSlogwang 			RTE_LOG_DP(DEBUG, DISTRAPP,
299a9643ea8Slogwang 				"%s:Packet loss due to full ring\n", __func__);
300a9643ea8Slogwang 			while (sent < nb_ret)
301a9643ea8Slogwang 				rte_pktmbuf_free(bufs[sent++]);
302a9643ea8Slogwang 		}
303a9643ea8Slogwang 		if (++port == nb_ports)
304a9643ea8Slogwang 			port = 0;
305a9643ea8Slogwang 	}
3064418919fSjohnjiang 	if (power_lib_initialised)
3074418919fSjohnjiang 		rte_power_exit(rte_lcore_id());
308a9643ea8Slogwang 	/* set worker & tx threads quit flag */
3092bfe3f2eSlogwang 	printf("\nCore %u exiting rx task.\n", rte_lcore_id());
310a9643ea8Slogwang 	quit_signal = 1;
311a9643ea8Slogwang 	return 0;
312a9643ea8Slogwang }
313a9643ea8Slogwang 
314a9643ea8Slogwang static inline void
flush_one_port(struct output_buffer * outbuf,uint8_t outp)315a9643ea8Slogwang flush_one_port(struct output_buffer *outbuf, uint8_t outp)
316a9643ea8Slogwang {
3172bfe3f2eSlogwang 	unsigned int nb_tx = rte_eth_tx_burst(outp, 0,
3182bfe3f2eSlogwang 			outbuf->mbufs, outbuf->count);
3192bfe3f2eSlogwang 	app_stats.tx.tx_pkts += outbuf->count;
320a9643ea8Slogwang 
321a9643ea8Slogwang 	if (unlikely(nb_tx < outbuf->count)) {
3222bfe3f2eSlogwang 		app_stats.tx.enqdrop_pkts +=  outbuf->count - nb_tx;
323a9643ea8Slogwang 		do {
324a9643ea8Slogwang 			rte_pktmbuf_free(outbuf->mbufs[nb_tx]);
325a9643ea8Slogwang 		} while (++nb_tx < outbuf->count);
326a9643ea8Slogwang 	}
327a9643ea8Slogwang 	outbuf->count = 0;
328a9643ea8Slogwang }
329a9643ea8Slogwang 
330a9643ea8Slogwang static inline void
flush_all_ports(struct output_buffer * tx_buffers)331d30ea906Sjfb8856606 flush_all_ports(struct output_buffer *tx_buffers)
332a9643ea8Slogwang {
3332bfe3f2eSlogwang 	uint16_t outp;
3342bfe3f2eSlogwang 
335d30ea906Sjfb8856606 	RTE_ETH_FOREACH_DEV(outp) {
336a9643ea8Slogwang 		/* skip ports that are not enabled */
337a9643ea8Slogwang 		if ((enabled_port_mask & (1 << outp)) == 0)
338a9643ea8Slogwang 			continue;
339a9643ea8Slogwang 
340a9643ea8Slogwang 		if (tx_buffers[outp].count == 0)
341a9643ea8Slogwang 			continue;
342a9643ea8Slogwang 
343a9643ea8Slogwang 		flush_one_port(&tx_buffers[outp], outp);
344a9643ea8Slogwang 	}
345a9643ea8Slogwang }
346a9643ea8Slogwang 
3472bfe3f2eSlogwang 
3482bfe3f2eSlogwang 
3492bfe3f2eSlogwang static int
lcore_distributor(struct lcore_params * p)3502bfe3f2eSlogwang lcore_distributor(struct lcore_params *p)
3512bfe3f2eSlogwang {
3522bfe3f2eSlogwang 	struct rte_ring *in_r = p->rx_dist_ring;
3532bfe3f2eSlogwang 	struct rte_ring *out_r = p->dist_tx_ring;
3542bfe3f2eSlogwang 	struct rte_mbuf *bufs[BURST_SIZE * 4];
3552bfe3f2eSlogwang 	struct rte_distributor *d = p->d;
3562bfe3f2eSlogwang 
3572bfe3f2eSlogwang 	printf("\nCore %u acting as distributor core.\n", rte_lcore_id());
3582bfe3f2eSlogwang 	while (!quit_signal_dist) {
3592bfe3f2eSlogwang 		const uint16_t nb_rx = rte_ring_dequeue_burst(in_r,
3602bfe3f2eSlogwang 				(void *)bufs, BURST_SIZE*1, NULL);
3612bfe3f2eSlogwang 		if (nb_rx) {
3622bfe3f2eSlogwang 			app_stats.dist.in_pkts += nb_rx;
3632bfe3f2eSlogwang 
3642bfe3f2eSlogwang 			/* Distribute the packets */
3652bfe3f2eSlogwang 			rte_distributor_process(d, bufs, nb_rx);
3662bfe3f2eSlogwang 			/* Handle Returns */
3672bfe3f2eSlogwang 			const uint16_t nb_ret =
3682bfe3f2eSlogwang 				rte_distributor_returned_pkts(d,
3692bfe3f2eSlogwang 					bufs, BURST_SIZE*2);
3702bfe3f2eSlogwang 
3712bfe3f2eSlogwang 			if (unlikely(nb_ret == 0))
3722bfe3f2eSlogwang 				continue;
3732bfe3f2eSlogwang 			app_stats.dist.ret_pkts += nb_ret;
3742bfe3f2eSlogwang 
3752bfe3f2eSlogwang 			uint16_t sent = rte_ring_enqueue_burst(out_r,
3762bfe3f2eSlogwang 					(void *)bufs, nb_ret, NULL);
3772bfe3f2eSlogwang 			app_stats.dist.sent_pkts += sent;
3782bfe3f2eSlogwang 			if (unlikely(sent < nb_ret)) {
3792bfe3f2eSlogwang 				app_stats.dist.enqdrop_pkts += nb_ret - sent;
3802bfe3f2eSlogwang 				RTE_LOG(DEBUG, DISTRAPP,
3812bfe3f2eSlogwang 					"%s:Packet loss due to full out ring\n",
3822bfe3f2eSlogwang 					__func__);
3832bfe3f2eSlogwang 				while (sent < nb_ret)
3842bfe3f2eSlogwang 					rte_pktmbuf_free(bufs[sent++]);
3852bfe3f2eSlogwang 			}
3862bfe3f2eSlogwang 		}
3872bfe3f2eSlogwang 	}
3882bfe3f2eSlogwang 	printf("\nCore %u exiting distributor task.\n", rte_lcore_id());
3892bfe3f2eSlogwang 	quit_signal_work = 1;
3904418919fSjohnjiang 	if (power_lib_initialised)
3914418919fSjohnjiang 		rte_power_exit(rte_lcore_id());
3922bfe3f2eSlogwang 	rte_distributor_flush(d);
3932bfe3f2eSlogwang 	/* Unblock any returns so workers can exit */
3942bfe3f2eSlogwang 	rte_distributor_clear_returns(d);
3952bfe3f2eSlogwang 	quit_signal_rx = 1;
3962bfe3f2eSlogwang 	return 0;
3972bfe3f2eSlogwang }
3982bfe3f2eSlogwang 
3992bfe3f2eSlogwang 
400a9643ea8Slogwang static int
lcore_tx(struct rte_ring * in_r)401a9643ea8Slogwang lcore_tx(struct rte_ring *in_r)
402a9643ea8Slogwang {
403a9643ea8Slogwang 	static struct output_buffer tx_buffers[RTE_MAX_ETHPORTS];
404a9643ea8Slogwang 	const int socket_id = rte_socket_id();
4052bfe3f2eSlogwang 	uint16_t port;
406a9643ea8Slogwang 
407d30ea906Sjfb8856606 	RTE_ETH_FOREACH_DEV(port) {
408a9643ea8Slogwang 		/* skip ports that are not enabled */
409a9643ea8Slogwang 		if ((enabled_port_mask & (1 << port)) == 0)
410a9643ea8Slogwang 			continue;
411a9643ea8Slogwang 
412a9643ea8Slogwang 		if (rte_eth_dev_socket_id(port) > 0 &&
413a9643ea8Slogwang 				rte_eth_dev_socket_id(port) != socket_id)
414a9643ea8Slogwang 			printf("WARNING, port %u is on remote NUMA node to "
415a9643ea8Slogwang 					"TX thread.\n\tPerformance will not "
416a9643ea8Slogwang 					"be optimal.\n", port);
417a9643ea8Slogwang 	}
418a9643ea8Slogwang 
419a9643ea8Slogwang 	printf("\nCore %u doing packet TX.\n", rte_lcore_id());
420a9643ea8Slogwang 	while (!quit_signal) {
421a9643ea8Slogwang 
422d30ea906Sjfb8856606 		RTE_ETH_FOREACH_DEV(port) {
423a9643ea8Slogwang 			/* skip ports that are not enabled */
424a9643ea8Slogwang 			if ((enabled_port_mask & (1 << port)) == 0)
425a9643ea8Slogwang 				continue;
426a9643ea8Slogwang 
4272bfe3f2eSlogwang 			struct rte_mbuf *bufs[BURST_SIZE_TX];
428a9643ea8Slogwang 			const uint16_t nb_rx = rte_ring_dequeue_burst(in_r,
4292bfe3f2eSlogwang 					(void *)bufs, BURST_SIZE_TX, NULL);
430a9643ea8Slogwang 			app_stats.tx.dequeue_pkts += nb_rx;
431a9643ea8Slogwang 
432a9643ea8Slogwang 			/* if we get no traffic, flush anything we have */
433a9643ea8Slogwang 			if (unlikely(nb_rx == 0)) {
434d30ea906Sjfb8856606 				flush_all_ports(tx_buffers);
435a9643ea8Slogwang 				continue;
436a9643ea8Slogwang 			}
437a9643ea8Slogwang 
438a9643ea8Slogwang 			/* for traffic we receive, queue it up for transmit */
439a9643ea8Slogwang 			uint16_t i;
440a9643ea8Slogwang 			rte_prefetch_non_temporal((void *)bufs[0]);
441a9643ea8Slogwang 			rte_prefetch_non_temporal((void *)bufs[1]);
442a9643ea8Slogwang 			rte_prefetch_non_temporal((void *)bufs[2]);
443a9643ea8Slogwang 			for (i = 0; i < nb_rx; i++) {
444a9643ea8Slogwang 				struct output_buffer *outbuf;
445a9643ea8Slogwang 				uint8_t outp;
446a9643ea8Slogwang 				rte_prefetch_non_temporal((void *)bufs[i + 3]);
447a9643ea8Slogwang 				/*
448a9643ea8Slogwang 				 * workers should update in_port to hold the
449a9643ea8Slogwang 				 * output port value
450a9643ea8Slogwang 				 */
451a9643ea8Slogwang 				outp = bufs[i]->port;
452a9643ea8Slogwang 				/* skip ports that are not enabled */
453a9643ea8Slogwang 				if ((enabled_port_mask & (1 << outp)) == 0)
454a9643ea8Slogwang 					continue;
455a9643ea8Slogwang 
456a9643ea8Slogwang 				outbuf = &tx_buffers[outp];
457a9643ea8Slogwang 				outbuf->mbufs[outbuf->count++] = bufs[i];
4582bfe3f2eSlogwang 				if (outbuf->count == BURST_SIZE_TX)
459a9643ea8Slogwang 					flush_one_port(outbuf, outp);
460a9643ea8Slogwang 			}
461a9643ea8Slogwang 		}
462a9643ea8Slogwang 	}
4634418919fSjohnjiang 	if (power_lib_initialised)
4644418919fSjohnjiang 		rte_power_exit(rte_lcore_id());
4652bfe3f2eSlogwang 	printf("\nCore %u exiting tx task.\n", rte_lcore_id());
466a9643ea8Slogwang 	return 0;
467a9643ea8Slogwang }
468a9643ea8Slogwang 
469a9643ea8Slogwang static void
int_handler(int sig_num)470a9643ea8Slogwang int_handler(int sig_num)
471a9643ea8Slogwang {
472a9643ea8Slogwang 	printf("Exiting on signal %d\n", sig_num);
473a9643ea8Slogwang 	/* set quit flag for rx thread to exit */
4742bfe3f2eSlogwang 	quit_signal_dist = 1;
475a9643ea8Slogwang }
476a9643ea8Slogwang 
477a9643ea8Slogwang static void
print_stats(void)478a9643ea8Slogwang print_stats(void)
479a9643ea8Slogwang {
480a9643ea8Slogwang 	struct rte_eth_stats eth_stats;
4812bfe3f2eSlogwang 	unsigned int i, j;
4822bfe3f2eSlogwang 	const unsigned int num_workers = rte_lcore_count() - 4;
483a9643ea8Slogwang 
484d30ea906Sjfb8856606 	RTE_ETH_FOREACH_DEV(i) {
485a9643ea8Slogwang 		rte_eth_stats_get(i, &eth_stats);
4862bfe3f2eSlogwang 		app_stats.port_rx_pkts[i] = eth_stats.ipackets;
4872bfe3f2eSlogwang 		app_stats.port_tx_pkts[i] = eth_stats.opackets;
4882bfe3f2eSlogwang 	}
4892bfe3f2eSlogwang 
4902bfe3f2eSlogwang 	printf("\n\nRX Thread:\n");
491d30ea906Sjfb8856606 	RTE_ETH_FOREACH_DEV(i) {
4922bfe3f2eSlogwang 		printf("Port %u Pktsin : %5.2f\n", i,
4932bfe3f2eSlogwang 				(app_stats.port_rx_pkts[i] -
4942bfe3f2eSlogwang 				prev_app_stats.port_rx_pkts[i])/1000000.0);
4952bfe3f2eSlogwang 		prev_app_stats.port_rx_pkts[i] = app_stats.port_rx_pkts[i];
4962bfe3f2eSlogwang 	}
4972bfe3f2eSlogwang 	printf(" - Received:    %5.2f\n",
4982bfe3f2eSlogwang 			(app_stats.rx.rx_pkts -
4992bfe3f2eSlogwang 			prev_app_stats.rx.rx_pkts)/1000000.0);
5002bfe3f2eSlogwang 	printf(" - Returned:    %5.2f\n",
5012bfe3f2eSlogwang 			(app_stats.rx.returned_pkts -
5022bfe3f2eSlogwang 			prev_app_stats.rx.returned_pkts)/1000000.0);
5032bfe3f2eSlogwang 	printf(" - Enqueued:    %5.2f\n",
5042bfe3f2eSlogwang 			(app_stats.rx.enqueued_pkts -
5052bfe3f2eSlogwang 			prev_app_stats.rx.enqueued_pkts)/1000000.0);
5062bfe3f2eSlogwang 	printf(" - Dropped:     %s%5.2f%s\n", ANSI_COLOR_RED,
5072bfe3f2eSlogwang 			(app_stats.rx.enqdrop_pkts -
5082bfe3f2eSlogwang 			prev_app_stats.rx.enqdrop_pkts)/1000000.0,
5092bfe3f2eSlogwang 			ANSI_COLOR_RESET);
5102bfe3f2eSlogwang 
5112bfe3f2eSlogwang 	printf("Distributor thread:\n");
5122bfe3f2eSlogwang 	printf(" - In:          %5.2f\n",
5132bfe3f2eSlogwang 			(app_stats.dist.in_pkts -
5142bfe3f2eSlogwang 			prev_app_stats.dist.in_pkts)/1000000.0);
5152bfe3f2eSlogwang 	printf(" - Returned:    %5.2f\n",
5162bfe3f2eSlogwang 			(app_stats.dist.ret_pkts -
5172bfe3f2eSlogwang 			prev_app_stats.dist.ret_pkts)/1000000.0);
5182bfe3f2eSlogwang 	printf(" - Sent:        %5.2f\n",
5192bfe3f2eSlogwang 			(app_stats.dist.sent_pkts -
5202bfe3f2eSlogwang 			prev_app_stats.dist.sent_pkts)/1000000.0);
5212bfe3f2eSlogwang 	printf(" - Dropped      %s%5.2f%s\n", ANSI_COLOR_RED,
5222bfe3f2eSlogwang 			(app_stats.dist.enqdrop_pkts -
5232bfe3f2eSlogwang 			prev_app_stats.dist.enqdrop_pkts)/1000000.0,
5242bfe3f2eSlogwang 			ANSI_COLOR_RESET);
5252bfe3f2eSlogwang 
5262bfe3f2eSlogwang 	printf("TX thread:\n");
5272bfe3f2eSlogwang 	printf(" - Dequeued:    %5.2f\n",
5282bfe3f2eSlogwang 			(app_stats.tx.dequeue_pkts -
5292bfe3f2eSlogwang 			prev_app_stats.tx.dequeue_pkts)/1000000.0);
530d30ea906Sjfb8856606 	RTE_ETH_FOREACH_DEV(i) {
5312bfe3f2eSlogwang 		printf("Port %u Pktsout: %5.2f\n",
5322bfe3f2eSlogwang 				i, (app_stats.port_tx_pkts[i] -
5332bfe3f2eSlogwang 				prev_app_stats.port_tx_pkts[i])/1000000.0);
5342bfe3f2eSlogwang 		prev_app_stats.port_tx_pkts[i] = app_stats.port_tx_pkts[i];
5352bfe3f2eSlogwang 	}
5362bfe3f2eSlogwang 	printf(" - Transmitted: %5.2f\n",
5372bfe3f2eSlogwang 			(app_stats.tx.tx_pkts -
5382bfe3f2eSlogwang 			prev_app_stats.tx.tx_pkts)/1000000.0);
5392bfe3f2eSlogwang 	printf(" - Dropped:     %s%5.2f%s\n", ANSI_COLOR_RED,
5402bfe3f2eSlogwang 			(app_stats.tx.enqdrop_pkts -
5412bfe3f2eSlogwang 			prev_app_stats.tx.enqdrop_pkts)/1000000.0,
5422bfe3f2eSlogwang 			ANSI_COLOR_RESET);
5432bfe3f2eSlogwang 
5442bfe3f2eSlogwang 	prev_app_stats.rx.rx_pkts = app_stats.rx.rx_pkts;
5452bfe3f2eSlogwang 	prev_app_stats.rx.returned_pkts = app_stats.rx.returned_pkts;
5462bfe3f2eSlogwang 	prev_app_stats.rx.enqueued_pkts = app_stats.rx.enqueued_pkts;
5472bfe3f2eSlogwang 	prev_app_stats.rx.enqdrop_pkts = app_stats.rx.enqdrop_pkts;
5482bfe3f2eSlogwang 	prev_app_stats.dist.in_pkts = app_stats.dist.in_pkts;
5492bfe3f2eSlogwang 	prev_app_stats.dist.ret_pkts = app_stats.dist.ret_pkts;
5502bfe3f2eSlogwang 	prev_app_stats.dist.sent_pkts = app_stats.dist.sent_pkts;
5512bfe3f2eSlogwang 	prev_app_stats.dist.enqdrop_pkts = app_stats.dist.enqdrop_pkts;
5522bfe3f2eSlogwang 	prev_app_stats.tx.dequeue_pkts = app_stats.tx.dequeue_pkts;
5532bfe3f2eSlogwang 	prev_app_stats.tx.tx_pkts = app_stats.tx.tx_pkts;
5542bfe3f2eSlogwang 	prev_app_stats.tx.enqdrop_pkts = app_stats.tx.enqdrop_pkts;
5552bfe3f2eSlogwang 
5562bfe3f2eSlogwang 	for (i = 0; i < num_workers; i++) {
5572bfe3f2eSlogwang 		printf("Worker %02u Pkts: %5.2f. Bursts(1-8): ", i,
5582bfe3f2eSlogwang 				(app_stats.worker_pkts[i] -
5592bfe3f2eSlogwang 				prev_app_stats.worker_pkts[i])/1000000.0);
5602bfe3f2eSlogwang 		for (j = 0; j < 8; j++) {
5612bfe3f2eSlogwang 			printf("%"PRIu64" ", app_stats.worker_bursts[i][j]);
5622bfe3f2eSlogwang 			app_stats.worker_bursts[i][j] = 0;
5632bfe3f2eSlogwang 		}
5642bfe3f2eSlogwang 		printf("\n");
5652bfe3f2eSlogwang 		prev_app_stats.worker_pkts[i] = app_stats.worker_pkts[i];
566a9643ea8Slogwang 	}
567a9643ea8Slogwang }
568a9643ea8Slogwang 
569a9643ea8Slogwang static int
lcore_worker(struct lcore_params * p)570a9643ea8Slogwang lcore_worker(struct lcore_params *p)
571a9643ea8Slogwang {
572a9643ea8Slogwang 	struct rte_distributor *d = p->d;
573a9643ea8Slogwang 	const unsigned id = p->worker_id;
5742bfe3f2eSlogwang 	unsigned int num = 0;
5752bfe3f2eSlogwang 	unsigned int i;
5762bfe3f2eSlogwang 
577a9643ea8Slogwang 	/*
578a9643ea8Slogwang 	 * for single port, xor_val will be zero so we won't modify the output
579a9643ea8Slogwang 	 * port, otherwise we send traffic from 0 to 1, 2 to 3, and vice versa
580a9643ea8Slogwang 	 */
581d30ea906Sjfb8856606 	const unsigned xor_val = (rte_eth_dev_count_avail() > 1);
5822bfe3f2eSlogwang 	struct rte_mbuf *buf[8] __rte_cache_aligned;
5832bfe3f2eSlogwang 
5842bfe3f2eSlogwang 	for (i = 0; i < 8; i++)
5852bfe3f2eSlogwang 		buf[i] = NULL;
5862bfe3f2eSlogwang 
5872bfe3f2eSlogwang 	app_stats.worker_pkts[p->worker_id] = 1;
588a9643ea8Slogwang 
589a9643ea8Slogwang 	printf("\nCore %u acting as worker core.\n", rte_lcore_id());
5902bfe3f2eSlogwang 	while (!quit_signal_work) {
5912bfe3f2eSlogwang 		num = rte_distributor_get_pkt(d, id, buf, buf, num);
5922bfe3f2eSlogwang 		/* Do a little bit of work for each packet */
5932bfe3f2eSlogwang 		for (i = 0; i < num; i++) {
5942bfe3f2eSlogwang 			uint64_t t = rte_rdtsc()+100;
5952bfe3f2eSlogwang 
5962bfe3f2eSlogwang 			while (rte_rdtsc() < t)
5972bfe3f2eSlogwang 				rte_pause();
5982bfe3f2eSlogwang 			buf[i]->port ^= xor_val;
5992bfe3f2eSlogwang 		}
6002bfe3f2eSlogwang 
6012bfe3f2eSlogwang 		app_stats.worker_pkts[p->worker_id] += num;
6022bfe3f2eSlogwang 		if (num > 0)
6032bfe3f2eSlogwang 			app_stats.worker_bursts[p->worker_id][num-1]++;
604a9643ea8Slogwang 	}
6054418919fSjohnjiang 	if (power_lib_initialised)
6064418919fSjohnjiang 		rte_power_exit(rte_lcore_id());
6074418919fSjohnjiang 	rte_free(p);
608a9643ea8Slogwang 	return 0;
609a9643ea8Slogwang }
610a9643ea8Slogwang 
6114418919fSjohnjiang static int
init_power_library(void)6124418919fSjohnjiang init_power_library(void)
6134418919fSjohnjiang {
6144418919fSjohnjiang 	int ret = 0, lcore_id;
615*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(lcore_id) {
6164418919fSjohnjiang 		/* init power management library */
6174418919fSjohnjiang 		ret = rte_power_init(lcore_id);
6184418919fSjohnjiang 		if (ret) {
6194418919fSjohnjiang 			RTE_LOG(ERR, POWER,
6204418919fSjohnjiang 				"Library initialization failed on core %u\n",
6214418919fSjohnjiang 				lcore_id);
6224418919fSjohnjiang 			/*
6234418919fSjohnjiang 			 * Return on first failure, we'll fall back
6244418919fSjohnjiang 			 * to non-power operation
6254418919fSjohnjiang 			 */
6264418919fSjohnjiang 			return ret;
6274418919fSjohnjiang 		}
6284418919fSjohnjiang 	}
6294418919fSjohnjiang 	return ret;
6304418919fSjohnjiang }
6314418919fSjohnjiang 
632a9643ea8Slogwang /* display usage */
633a9643ea8Slogwang static void
print_usage(const char * prgname)634a9643ea8Slogwang print_usage(const char *prgname)
635a9643ea8Slogwang {
636a9643ea8Slogwang 	printf("%s [EAL options] -- -p PORTMASK\n"
637a9643ea8Slogwang 			"  -p PORTMASK: hexadecimal bitmask of ports to configure\n",
638a9643ea8Slogwang 			prgname);
639a9643ea8Slogwang }
640a9643ea8Slogwang 
641a9643ea8Slogwang static int
parse_portmask(const char * portmask)642a9643ea8Slogwang parse_portmask(const char *portmask)
643a9643ea8Slogwang {
644a9643ea8Slogwang 	char *end = NULL;
645a9643ea8Slogwang 	unsigned long pm;
646a9643ea8Slogwang 
647a9643ea8Slogwang 	/* parse hexadecimal string */
648a9643ea8Slogwang 	pm = strtoul(portmask, &end, 16);
649a9643ea8Slogwang 	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
650*2d9fd380Sjfb8856606 		return 0;
651a9643ea8Slogwang 
652a9643ea8Slogwang 	return pm;
653a9643ea8Slogwang }
654a9643ea8Slogwang 
655a9643ea8Slogwang /* Parse the argument given in the command line of the application */
656a9643ea8Slogwang static int
parse_args(int argc,char ** argv)657a9643ea8Slogwang parse_args(int argc, char **argv)
658a9643ea8Slogwang {
659a9643ea8Slogwang 	int opt;
660a9643ea8Slogwang 	char **argvopt;
661a9643ea8Slogwang 	int option_index;
662a9643ea8Slogwang 	char *prgname = argv[0];
663a9643ea8Slogwang 	static struct option lgopts[] = {
664a9643ea8Slogwang 		{NULL, 0, 0, 0}
665a9643ea8Slogwang 	};
666a9643ea8Slogwang 
667a9643ea8Slogwang 	argvopt = argv;
668a9643ea8Slogwang 
669a9643ea8Slogwang 	while ((opt = getopt_long(argc, argvopt, "p:",
670a9643ea8Slogwang 			lgopts, &option_index)) != EOF) {
671a9643ea8Slogwang 
672a9643ea8Slogwang 		switch (opt) {
673a9643ea8Slogwang 		/* portmask */
674a9643ea8Slogwang 		case 'p':
675a9643ea8Slogwang 			enabled_port_mask = parse_portmask(optarg);
676a9643ea8Slogwang 			if (enabled_port_mask == 0) {
677a9643ea8Slogwang 				printf("invalid portmask\n");
678a9643ea8Slogwang 				print_usage(prgname);
679a9643ea8Slogwang 				return -1;
680a9643ea8Slogwang 			}
681a9643ea8Slogwang 			break;
682a9643ea8Slogwang 
683a9643ea8Slogwang 		default:
684a9643ea8Slogwang 			print_usage(prgname);
685a9643ea8Slogwang 			return -1;
686a9643ea8Slogwang 		}
687a9643ea8Slogwang 	}
688a9643ea8Slogwang 
689a9643ea8Slogwang 	if (optind <= 1) {
690a9643ea8Slogwang 		print_usage(prgname);
691a9643ea8Slogwang 		return -1;
692a9643ea8Slogwang 	}
693a9643ea8Slogwang 
694a9643ea8Slogwang 	argv[optind-1] = prgname;
695a9643ea8Slogwang 
6962bfe3f2eSlogwang 	optind = 1; /* reset getopt lib */
697a9643ea8Slogwang 	return 0;
698a9643ea8Slogwang }
699a9643ea8Slogwang 
700a9643ea8Slogwang /* Main function, does initialization and calls the per-lcore functions */
701a9643ea8Slogwang int
main(int argc,char * argv[])702a9643ea8Slogwang main(int argc, char *argv[])
703a9643ea8Slogwang {
704a9643ea8Slogwang 	struct rte_mempool *mbuf_pool;
705a9643ea8Slogwang 	struct rte_distributor *d;
7062bfe3f2eSlogwang 	struct rte_ring *dist_tx_ring;
7072bfe3f2eSlogwang 	struct rte_ring *rx_dist_ring;
7084418919fSjohnjiang 	struct rte_power_core_capabilities lcore_cap;
7094418919fSjohnjiang 	unsigned int lcore_id, worker_id = 0;
7104418919fSjohnjiang 	int distr_core_id = -1, rx_core_id = -1, tx_core_id = -1;
711a9643ea8Slogwang 	unsigned nb_ports;
7122bfe3f2eSlogwang 	uint16_t portid;
7132bfe3f2eSlogwang 	uint16_t nb_ports_available;
7142bfe3f2eSlogwang 	uint64_t t, freq;
715a9643ea8Slogwang 
716a9643ea8Slogwang 	/* catch ctrl-c so we can print on exit */
717a9643ea8Slogwang 	signal(SIGINT, int_handler);
718a9643ea8Slogwang 
719a9643ea8Slogwang 	/* init EAL */
720a9643ea8Slogwang 	int ret = rte_eal_init(argc, argv);
721a9643ea8Slogwang 	if (ret < 0)
722a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
723a9643ea8Slogwang 	argc -= ret;
724a9643ea8Slogwang 	argv += ret;
725a9643ea8Slogwang 
726a9643ea8Slogwang 	/* parse application arguments (after the EAL ones) */
727a9643ea8Slogwang 	ret = parse_args(argc, argv);
728a9643ea8Slogwang 	if (ret < 0)
729a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Invalid distributor parameters\n");
730a9643ea8Slogwang 
7312bfe3f2eSlogwang 	if (rte_lcore_count() < 5)
732a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Error, This application needs at "
7332bfe3f2eSlogwang 				"least 5 logical cores to run:\n"
7342bfe3f2eSlogwang 				"1 lcore for stats (can be core 0)\n"
7352bfe3f2eSlogwang 				"1 lcore for packet RX\n"
7362bfe3f2eSlogwang 				"1 lcore for distribution\n"
737a9643ea8Slogwang 				"1 lcore for packet TX\n"
738a9643ea8Slogwang 				"and at least 1 lcore for worker threads\n");
739a9643ea8Slogwang 
7404418919fSjohnjiang 	if (init_power_library() == 0)
7414418919fSjohnjiang 		power_lib_initialised = 1;
7424418919fSjohnjiang 
743d30ea906Sjfb8856606 	nb_ports = rte_eth_dev_count_avail();
744a9643ea8Slogwang 	if (nb_ports == 0)
745a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Error: no ethernet ports detected\n");
746a9643ea8Slogwang 	if (nb_ports != 1 && (nb_ports & 1))
747a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Error: number of ports must be even, except "
748a9643ea8Slogwang 				"when using a single port\n");
749a9643ea8Slogwang 
750a9643ea8Slogwang 	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
751a9643ea8Slogwang 		NUM_MBUFS * nb_ports, MBUF_CACHE_SIZE, 0,
752a9643ea8Slogwang 		RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
753a9643ea8Slogwang 	if (mbuf_pool == NULL)
754a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
755a9643ea8Slogwang 	nb_ports_available = nb_ports;
756a9643ea8Slogwang 
757a9643ea8Slogwang 	/* initialize all ports */
758d30ea906Sjfb8856606 	RTE_ETH_FOREACH_DEV(portid) {
759a9643ea8Slogwang 		/* skip ports that are not enabled */
760a9643ea8Slogwang 		if ((enabled_port_mask & (1 << portid)) == 0) {
761a9643ea8Slogwang 			printf("\nSkipping disabled port %d\n", portid);
762a9643ea8Slogwang 			nb_ports_available--;
763a9643ea8Slogwang 			continue;
764a9643ea8Slogwang 		}
765a9643ea8Slogwang 		/* init port */
7662bfe3f2eSlogwang 		printf("Initializing port %u... done\n", portid);
767a9643ea8Slogwang 
768a9643ea8Slogwang 		if (port_init(portid, mbuf_pool) != 0)
7692bfe3f2eSlogwang 			rte_exit(EXIT_FAILURE, "Cannot initialize port %u\n",
770a9643ea8Slogwang 					portid);
771a9643ea8Slogwang 	}
772a9643ea8Slogwang 
773a9643ea8Slogwang 	if (!nb_ports_available) {
774a9643ea8Slogwang 		rte_exit(EXIT_FAILURE,
775a9643ea8Slogwang 				"All available ports are disabled. Please set portmask.\n");
776a9643ea8Slogwang 	}
777a9643ea8Slogwang 
778a9643ea8Slogwang 	d = rte_distributor_create("PKT_DIST", rte_socket_id(),
7792bfe3f2eSlogwang 			rte_lcore_count() - 4,
7802bfe3f2eSlogwang 			RTE_DIST_ALG_BURST);
781a9643ea8Slogwang 	if (d == NULL)
782a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Cannot create distributor\n");
783a9643ea8Slogwang 
784a9643ea8Slogwang 	/*
7852bfe3f2eSlogwang 	 * scheduler ring is read by the transmitter core, and written to
7862bfe3f2eSlogwang 	 * by scheduler core
787a9643ea8Slogwang 	 */
7882bfe3f2eSlogwang 	dist_tx_ring = rte_ring_create("Output_ring", SCHED_TX_RING_SZ,
7892bfe3f2eSlogwang 			rte_socket_id(), RING_F_SC_DEQ | RING_F_SP_ENQ);
7902bfe3f2eSlogwang 	if (dist_tx_ring == NULL)
7912bfe3f2eSlogwang 		rte_exit(EXIT_FAILURE, "Cannot create output ring\n");
7922bfe3f2eSlogwang 
7932bfe3f2eSlogwang 	rx_dist_ring = rte_ring_create("Input_ring", SCHED_RX_RING_SZ,
7942bfe3f2eSlogwang 			rte_socket_id(), RING_F_SC_DEQ | RING_F_SP_ENQ);
7952bfe3f2eSlogwang 	if (rx_dist_ring == NULL)
796a9643ea8Slogwang 		rte_exit(EXIT_FAILURE, "Cannot create output ring\n");
797a9643ea8Slogwang 
7984418919fSjohnjiang 	if (power_lib_initialised) {
7994418919fSjohnjiang 		/*
8004418919fSjohnjiang 		 * Here we'll pre-assign lcore ids to the rx, tx and
8014418919fSjohnjiang 		 * distributor workloads if there's higher frequency
8024418919fSjohnjiang 		 * on those cores e.g. if Turbo Boost is enabled.
8034418919fSjohnjiang 		 * It's also worth mentioning that it will assign cores in a
8044418919fSjohnjiang 		 * specific order, so that if there's less than three
8054418919fSjohnjiang 		 * available, the higher frequency cores will go to the
8064418919fSjohnjiang 		 * distributor first, then rx, then tx.
8074418919fSjohnjiang 		 */
808*2d9fd380Sjfb8856606 		RTE_LCORE_FOREACH_WORKER(lcore_id) {
8094418919fSjohnjiang 
8104418919fSjohnjiang 			rte_power_get_capabilities(lcore_id, &lcore_cap);
8114418919fSjohnjiang 
8124418919fSjohnjiang 			if (lcore_cap.priority != 1)
8134418919fSjohnjiang 				continue;
8144418919fSjohnjiang 
8154418919fSjohnjiang 			if (distr_core_id < 0) {
8164418919fSjohnjiang 				distr_core_id = lcore_id;
8174418919fSjohnjiang 				printf("Distributor on priority core %d\n",
8182bfe3f2eSlogwang 					lcore_id);
8194418919fSjohnjiang 				continue;
8204418919fSjohnjiang 			}
8214418919fSjohnjiang 			if (rx_core_id < 0) {
8224418919fSjohnjiang 				rx_core_id = lcore_id;
8234418919fSjohnjiang 				printf("Rx on priority core %d\n",
8244418919fSjohnjiang 					lcore_id);
8254418919fSjohnjiang 				continue;
8264418919fSjohnjiang 			}
8274418919fSjohnjiang 			if (tx_core_id < 0) {
8284418919fSjohnjiang 				tx_core_id = lcore_id;
8294418919fSjohnjiang 				printf("Tx on priority core %d\n",
8304418919fSjohnjiang 					lcore_id);
8314418919fSjohnjiang 				continue;
8324418919fSjohnjiang 			}
8334418919fSjohnjiang 		}
8344418919fSjohnjiang 	}
8354418919fSjohnjiang 
8364418919fSjohnjiang 	/*
8374418919fSjohnjiang 	 * If there's any of the key workloads left without an lcore_id
8384418919fSjohnjiang 	 * after the high performing core assignment above, pre-assign
8394418919fSjohnjiang 	 * them here.
8404418919fSjohnjiang 	 */
841*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(lcore_id) {
8424418919fSjohnjiang 		if (lcore_id == (unsigned int)distr_core_id ||
8434418919fSjohnjiang 				lcore_id == (unsigned int)rx_core_id ||
8444418919fSjohnjiang 				lcore_id == (unsigned int)tx_core_id)
8454418919fSjohnjiang 			continue;
8464418919fSjohnjiang 		if (distr_core_id < 0) {
8474418919fSjohnjiang 			distr_core_id = lcore_id;
8484418919fSjohnjiang 			printf("Distributor on core %d\n", lcore_id);
8494418919fSjohnjiang 			continue;
8504418919fSjohnjiang 		}
8514418919fSjohnjiang 		if (rx_core_id < 0) {
8524418919fSjohnjiang 			rx_core_id = lcore_id;
8534418919fSjohnjiang 			printf("Rx on core %d\n", lcore_id);
8544418919fSjohnjiang 			continue;
8554418919fSjohnjiang 		}
8564418919fSjohnjiang 		if (tx_core_id < 0) {
8574418919fSjohnjiang 			tx_core_id = lcore_id;
8584418919fSjohnjiang 			printf("Tx on core %d\n", lcore_id);
8594418919fSjohnjiang 			continue;
8604418919fSjohnjiang 		}
8614418919fSjohnjiang 	}
8624418919fSjohnjiang 
8634418919fSjohnjiang 	printf(" tx id %d, dist id %d, rx id %d\n",
8644418919fSjohnjiang 			tx_core_id,
8654418919fSjohnjiang 			distr_core_id,
8664418919fSjohnjiang 			rx_core_id);
8674418919fSjohnjiang 
8684418919fSjohnjiang 	/*
8694418919fSjohnjiang 	 * Kick off all the worker threads first, avoiding the pre-assigned
8704418919fSjohnjiang 	 * lcore_ids for tx, rx and distributor workloads.
8714418919fSjohnjiang 	 */
872*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(lcore_id) {
8734418919fSjohnjiang 		if (lcore_id == (unsigned int)distr_core_id ||
8744418919fSjohnjiang 				lcore_id == (unsigned int)rx_core_id ||
8754418919fSjohnjiang 				lcore_id == (unsigned int)tx_core_id)
8764418919fSjohnjiang 			continue;
8774418919fSjohnjiang 		printf("Starting thread %d as worker, lcore_id %d\n",
8782bfe3f2eSlogwang 				worker_id, lcore_id);
8792bfe3f2eSlogwang 		struct lcore_params *p =
8802bfe3f2eSlogwang 			rte_malloc(NULL, sizeof(*p), 0);
8812bfe3f2eSlogwang 		if (!p)
8822bfe3f2eSlogwang 			rte_panic("malloc failure\n");
8834418919fSjohnjiang 		*p = (struct lcore_params){worker_id++, d, rx_dist_ring,
8842bfe3f2eSlogwang 			dist_tx_ring, mbuf_pool};
885a9643ea8Slogwang 
886a9643ea8Slogwang 		rte_eal_remote_launch((lcore_function_t *)lcore_worker,
887a9643ea8Slogwang 				p, lcore_id);
888a9643ea8Slogwang 	}
8894418919fSjohnjiang 
8904418919fSjohnjiang 	/* Start tx core */
8914418919fSjohnjiang 	rte_eal_remote_launch((lcore_function_t *)lcore_tx,
8924418919fSjohnjiang 			dist_tx_ring, tx_core_id);
8934418919fSjohnjiang 
8944418919fSjohnjiang 	/* Start distributor core */
8954418919fSjohnjiang 	struct lcore_params *pd =
8964418919fSjohnjiang 		rte_malloc(NULL, sizeof(*pd), 0);
8974418919fSjohnjiang 	if (!pd)
8984418919fSjohnjiang 		rte_panic("malloc failure\n");
8994418919fSjohnjiang 	*pd = (struct lcore_params){worker_id++, d,
9004418919fSjohnjiang 		rx_dist_ring, dist_tx_ring, mbuf_pool};
9014418919fSjohnjiang 	rte_eal_remote_launch(
9024418919fSjohnjiang 			(lcore_function_t *)lcore_distributor,
9034418919fSjohnjiang 			pd, distr_core_id);
9044418919fSjohnjiang 
9054418919fSjohnjiang 	/* Start rx core */
9064418919fSjohnjiang 	struct lcore_params *pr =
9074418919fSjohnjiang 		rte_malloc(NULL, sizeof(*pr), 0);
9084418919fSjohnjiang 	if (!pr)
9094418919fSjohnjiang 		rte_panic("malloc failure\n");
9104418919fSjohnjiang 	*pr = (struct lcore_params){worker_id++, d, rx_dist_ring,
9114418919fSjohnjiang 		dist_tx_ring, mbuf_pool};
9124418919fSjohnjiang 	rte_eal_remote_launch((lcore_function_t *)lcore_rx,
9134418919fSjohnjiang 			pr, rx_core_id);
914a9643ea8Slogwang 
9152bfe3f2eSlogwang 	freq = rte_get_timer_hz();
9162bfe3f2eSlogwang 	t = rte_rdtsc() + freq;
9172bfe3f2eSlogwang 	while (!quit_signal_dist) {
9182bfe3f2eSlogwang 		if (t < rte_rdtsc()) {
9192bfe3f2eSlogwang 			print_stats();
9202bfe3f2eSlogwang 			t = rte_rdtsc() + freq;
9212bfe3f2eSlogwang 		}
9222bfe3f2eSlogwang 		usleep(1000);
9232bfe3f2eSlogwang 	}
924a9643ea8Slogwang 
925*2d9fd380Sjfb8856606 	RTE_LCORE_FOREACH_WORKER(lcore_id) {
926a9643ea8Slogwang 		if (rte_eal_wait_lcore(lcore_id) < 0)
927a9643ea8Slogwang 			return -1;
928a9643ea8Slogwang 	}
929a9643ea8Slogwang 
930a9643ea8Slogwang 	print_stats();
9314418919fSjohnjiang 
9324418919fSjohnjiang 	rte_free(pd);
9334418919fSjohnjiang 	rte_free(pr);
9344418919fSjohnjiang 
935a9643ea8Slogwang 	return 0;
936a9643ea8Slogwang }
937