1*2bfe3f2eSlogwang /*-
2*2bfe3f2eSlogwang  *   BSD LICENSE
3*2bfe3f2eSlogwang  *
4*2bfe3f2eSlogwang  *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
5*2bfe3f2eSlogwang  *   All rights reserved.
6*2bfe3f2eSlogwang  *
7*2bfe3f2eSlogwang  *   Redistribution and use in source and binary forms, with or without
8*2bfe3f2eSlogwang  *   modification, are permitted provided that the following conditions
9*2bfe3f2eSlogwang  *   are met:
10*2bfe3f2eSlogwang  *
11*2bfe3f2eSlogwang  *     * Redistributions of source code must retain the above copyright
12*2bfe3f2eSlogwang  *       notice, this list of conditions and the following disclaimer.
13*2bfe3f2eSlogwang  *     * Redistributions in binary form must reproduce the above copyright
14*2bfe3f2eSlogwang  *       notice, this list of conditions and the following disclaimer in
15*2bfe3f2eSlogwang  *       the documentation and/or other materials provided with the
16*2bfe3f2eSlogwang  *       distribution.
17*2bfe3f2eSlogwang  *     * Neither the name of Intel Corporation nor the names of its
18*2bfe3f2eSlogwang  *       contributors may be used to endorse or promote products derived
19*2bfe3f2eSlogwang  *       from this software without specific prior written permission.
20*2bfe3f2eSlogwang  *
21*2bfe3f2eSlogwang  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*2bfe3f2eSlogwang  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*2bfe3f2eSlogwang  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24*2bfe3f2eSlogwang  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25*2bfe3f2eSlogwang  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26*2bfe3f2eSlogwang  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27*2bfe3f2eSlogwang  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28*2bfe3f2eSlogwang  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29*2bfe3f2eSlogwang  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30*2bfe3f2eSlogwang  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31*2bfe3f2eSlogwang  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*2bfe3f2eSlogwang  */
33*2bfe3f2eSlogwang 
34*2bfe3f2eSlogwang #include <stdint.h>
35*2bfe3f2eSlogwang #include <stdio.h>
36*2bfe3f2eSlogwang #include <string.h>
37*2bfe3f2eSlogwang #include <sys/queue.h>
38*2bfe3f2eSlogwang #include <errno.h>
39*2bfe3f2eSlogwang #include <stdarg.h>
40*2bfe3f2eSlogwang #include <inttypes.h>
41*2bfe3f2eSlogwang 
42*2bfe3f2eSlogwang #include <rte_common.h>
43*2bfe3f2eSlogwang #include <rte_memory.h>
44*2bfe3f2eSlogwang #include <rte_memzone.h>
45*2bfe3f2eSlogwang #include <rte_eal.h>
46*2bfe3f2eSlogwang #include <rte_byteorder.h>
47*2bfe3f2eSlogwang #include <rte_atomic.h>
48*2bfe3f2eSlogwang #include <rte_launch.h>
49*2bfe3f2eSlogwang #include <rte_per_lcore.h>
50*2bfe3f2eSlogwang #include <rte_lcore.h>
51*2bfe3f2eSlogwang #include <rte_branch_prediction.h>
52*2bfe3f2eSlogwang #include <rte_debug.h>
53*2bfe3f2eSlogwang #include <rte_ring.h>
54*2bfe3f2eSlogwang #include <rte_log.h>
55*2bfe3f2eSlogwang #include <rte_mempool.h>
56*2bfe3f2eSlogwang #include <rte_memcpy.h>
57*2bfe3f2eSlogwang #include <rte_mbuf.h>
58*2bfe3f2eSlogwang #include <rte_interrupts.h>
59*2bfe3f2eSlogwang #include <rte_ether.h>
60*2bfe3f2eSlogwang #include <rte_ethdev.h>
61*2bfe3f2eSlogwang #include <rte_malloc.h>
62*2bfe3f2eSlogwang #include <rte_string_fns.h>
63*2bfe3f2eSlogwang #include <rte_cycles.h>
64*2bfe3f2eSlogwang #include <rte_efd.h>
65*2bfe3f2eSlogwang #include <rte_hash.h>
66*2bfe3f2eSlogwang 
67*2bfe3f2eSlogwang #include "common.h"
68*2bfe3f2eSlogwang #include "args.h"
69*2bfe3f2eSlogwang #include "init.h"
70*2bfe3f2eSlogwang 
71*2bfe3f2eSlogwang #define MBUFS_PER_NODE 1536
72*2bfe3f2eSlogwang #define MBUFS_PER_PORT 1536
73*2bfe3f2eSlogwang #define MBUF_CACHE_SIZE 512
74*2bfe3f2eSlogwang 
75*2bfe3f2eSlogwang #define RTE_MP_RX_DESC_DEFAULT 512
76*2bfe3f2eSlogwang #define RTE_MP_TX_DESC_DEFAULT 512
77*2bfe3f2eSlogwang #define NODE_QUEUE_RINGSIZE 128
78*2bfe3f2eSlogwang 
79*2bfe3f2eSlogwang #define NO_FLAGS 0
80*2bfe3f2eSlogwang 
81*2bfe3f2eSlogwang /* The mbuf pool for packet rx */
82*2bfe3f2eSlogwang struct rte_mempool *pktmbuf_pool;
83*2bfe3f2eSlogwang 
84*2bfe3f2eSlogwang /* array of info/queues for nodes */
85*2bfe3f2eSlogwang struct node *nodes;
86*2bfe3f2eSlogwang 
87*2bfe3f2eSlogwang /* EFD table */
88*2bfe3f2eSlogwang struct rte_efd_table *efd_table;
89*2bfe3f2eSlogwang 
90*2bfe3f2eSlogwang /* Shared info between server and nodes */
91*2bfe3f2eSlogwang struct shared_info *info;
92*2bfe3f2eSlogwang 
93*2bfe3f2eSlogwang /**
94*2bfe3f2eSlogwang  * Initialise the mbuf pool for packet reception for the NIC, and any other
95*2bfe3f2eSlogwang  * buffer pools needed by the app - currently none.
96*2bfe3f2eSlogwang  */
97*2bfe3f2eSlogwang static int
98*2bfe3f2eSlogwang init_mbuf_pools(void)
99*2bfe3f2eSlogwang {
100*2bfe3f2eSlogwang 	const unsigned int num_mbufs = (num_nodes * MBUFS_PER_NODE) +
101*2bfe3f2eSlogwang 			(info->num_ports * MBUFS_PER_PORT);
102*2bfe3f2eSlogwang 
103*2bfe3f2eSlogwang 	/*
104*2bfe3f2eSlogwang 	 * Don't pass single-producer/single-consumer flags to mbuf create as it
105*2bfe3f2eSlogwang 	 * seems faster to use a cache instead
106*2bfe3f2eSlogwang 	 */
107*2bfe3f2eSlogwang 	printf("Creating mbuf pool '%s' [%u mbufs] ...\n",
108*2bfe3f2eSlogwang 			PKTMBUF_POOL_NAME, num_mbufs);
109*2bfe3f2eSlogwang 	pktmbuf_pool = rte_pktmbuf_pool_create(PKTMBUF_POOL_NAME, num_mbufs,
110*2bfe3f2eSlogwang 		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
111*2bfe3f2eSlogwang 
112*2bfe3f2eSlogwang 	return pktmbuf_pool == NULL; /* 0  on success */
113*2bfe3f2eSlogwang }
114*2bfe3f2eSlogwang 
115*2bfe3f2eSlogwang /**
116*2bfe3f2eSlogwang  * Initialise an individual port:
117*2bfe3f2eSlogwang  * - configure number of rx and tx rings
118*2bfe3f2eSlogwang  * - set up each rx ring, to pull from the main mbuf pool
119*2bfe3f2eSlogwang  * - set up each tx ring
120*2bfe3f2eSlogwang  * - start the port and report its status to stdout
121*2bfe3f2eSlogwang  */
122*2bfe3f2eSlogwang static int
123*2bfe3f2eSlogwang init_port(uint16_t port_num)
124*2bfe3f2eSlogwang {
125*2bfe3f2eSlogwang 	/* for port configuration all features are off by default */
126*2bfe3f2eSlogwang 	const struct rte_eth_conf port_conf = {
127*2bfe3f2eSlogwang 		.rxmode = {
128*2bfe3f2eSlogwang 			.mq_mode = ETH_MQ_RX_RSS
129*2bfe3f2eSlogwang 		}
130*2bfe3f2eSlogwang 	};
131*2bfe3f2eSlogwang 	const uint16_t rx_rings = 1, tx_rings = num_nodes;
132*2bfe3f2eSlogwang 	uint16_t rx_ring_size = RTE_MP_RX_DESC_DEFAULT;
133*2bfe3f2eSlogwang 	uint16_t tx_ring_size = RTE_MP_TX_DESC_DEFAULT;
134*2bfe3f2eSlogwang 
135*2bfe3f2eSlogwang 	uint16_t q;
136*2bfe3f2eSlogwang 	int retval;
137*2bfe3f2eSlogwang 
138*2bfe3f2eSlogwang 	printf("Port %u init ... ", port_num);
139*2bfe3f2eSlogwang 	fflush(stdout);
140*2bfe3f2eSlogwang 
141*2bfe3f2eSlogwang 	/*
142*2bfe3f2eSlogwang 	 * Standard DPDK port initialisation - config port, then set up
143*2bfe3f2eSlogwang 	 * rx and tx rings.
144*2bfe3f2eSlogwang 	 */
145*2bfe3f2eSlogwang 	retval = rte_eth_dev_configure(port_num, rx_rings, tx_rings, &port_conf);
146*2bfe3f2eSlogwang 	if (retval != 0)
147*2bfe3f2eSlogwang 		return retval;
148*2bfe3f2eSlogwang 
149*2bfe3f2eSlogwang 	retval = rte_eth_dev_adjust_nb_rx_tx_desc(port_num, &rx_ring_size,
150*2bfe3f2eSlogwang 			&tx_ring_size);
151*2bfe3f2eSlogwang 	if (retval != 0)
152*2bfe3f2eSlogwang 		return retval;
153*2bfe3f2eSlogwang 
154*2bfe3f2eSlogwang 	for (q = 0; q < rx_rings; q++) {
155*2bfe3f2eSlogwang 		retval = rte_eth_rx_queue_setup(port_num, q, rx_ring_size,
156*2bfe3f2eSlogwang 				rte_eth_dev_socket_id(port_num),
157*2bfe3f2eSlogwang 				NULL, pktmbuf_pool);
158*2bfe3f2eSlogwang 		if (retval < 0)
159*2bfe3f2eSlogwang 			return retval;
160*2bfe3f2eSlogwang 	}
161*2bfe3f2eSlogwang 
162*2bfe3f2eSlogwang 	for (q = 0; q < tx_rings; q++) {
163*2bfe3f2eSlogwang 		retval = rte_eth_tx_queue_setup(port_num, q, tx_ring_size,
164*2bfe3f2eSlogwang 				rte_eth_dev_socket_id(port_num),
165*2bfe3f2eSlogwang 				NULL);
166*2bfe3f2eSlogwang 		if (retval < 0)
167*2bfe3f2eSlogwang 			return retval;
168*2bfe3f2eSlogwang 	}
169*2bfe3f2eSlogwang 
170*2bfe3f2eSlogwang 	rte_eth_promiscuous_enable(port_num);
171*2bfe3f2eSlogwang 
172*2bfe3f2eSlogwang 	retval = rte_eth_dev_start(port_num);
173*2bfe3f2eSlogwang 	if (retval < 0)
174*2bfe3f2eSlogwang 		return retval;
175*2bfe3f2eSlogwang 
176*2bfe3f2eSlogwang 	printf("done:\n");
177*2bfe3f2eSlogwang 
178*2bfe3f2eSlogwang 	return 0;
179*2bfe3f2eSlogwang }
180*2bfe3f2eSlogwang 
181*2bfe3f2eSlogwang /**
182*2bfe3f2eSlogwang  * Set up the DPDK rings which will be used to pass packets, via
183*2bfe3f2eSlogwang  * pointers, between the multi-process server and node processes.
184*2bfe3f2eSlogwang  * Each node needs one RX queue.
185*2bfe3f2eSlogwang  */
186*2bfe3f2eSlogwang static int
187*2bfe3f2eSlogwang init_shm_rings(void)
188*2bfe3f2eSlogwang {
189*2bfe3f2eSlogwang 	unsigned int i;
190*2bfe3f2eSlogwang 	unsigned int socket_id;
191*2bfe3f2eSlogwang 	const char *q_name;
192*2bfe3f2eSlogwang 	const unsigned int ringsize = NODE_QUEUE_RINGSIZE;
193*2bfe3f2eSlogwang 
194*2bfe3f2eSlogwang 	nodes = rte_malloc("node details",
195*2bfe3f2eSlogwang 		sizeof(*nodes) * num_nodes, 0);
196*2bfe3f2eSlogwang 	if (nodes == NULL)
197*2bfe3f2eSlogwang 		rte_exit(EXIT_FAILURE, "Cannot allocate memory for "
198*2bfe3f2eSlogwang 				"node program details\n");
199*2bfe3f2eSlogwang 
200*2bfe3f2eSlogwang 	for (i = 0; i < num_nodes; i++) {
201*2bfe3f2eSlogwang 		/* Create an RX queue for each node */
202*2bfe3f2eSlogwang 		socket_id = rte_socket_id();
203*2bfe3f2eSlogwang 		q_name = get_rx_queue_name(i);
204*2bfe3f2eSlogwang 		nodes[i].rx_q = rte_ring_create(q_name,
205*2bfe3f2eSlogwang 				ringsize, socket_id,
206*2bfe3f2eSlogwang 				RING_F_SP_ENQ | RING_F_SC_DEQ);
207*2bfe3f2eSlogwang 		if (nodes[i].rx_q == NULL)
208*2bfe3f2eSlogwang 			rte_exit(EXIT_FAILURE, "Cannot create rx ring queue "
209*2bfe3f2eSlogwang 					"for node %u\n", i);
210*2bfe3f2eSlogwang 	}
211*2bfe3f2eSlogwang 	return 0;
212*2bfe3f2eSlogwang }
213*2bfe3f2eSlogwang 
214*2bfe3f2eSlogwang /*
215*2bfe3f2eSlogwang  * Create EFD table which will contain all the flows
216*2bfe3f2eSlogwang  * that will be distributed among the nodes
217*2bfe3f2eSlogwang  */
218*2bfe3f2eSlogwang static void
219*2bfe3f2eSlogwang create_efd_table(void)
220*2bfe3f2eSlogwang {
221*2bfe3f2eSlogwang 	uint8_t socket_id = rte_socket_id();
222*2bfe3f2eSlogwang 
223*2bfe3f2eSlogwang 	/* create table */
224*2bfe3f2eSlogwang 	efd_table = rte_efd_create("flow table", num_flows * 2, sizeof(uint32_t),
225*2bfe3f2eSlogwang 			1 << socket_id,	socket_id);
226*2bfe3f2eSlogwang 
227*2bfe3f2eSlogwang 	if (efd_table == NULL)
228*2bfe3f2eSlogwang 		rte_exit(EXIT_FAILURE, "Problem creating the flow table\n");
229*2bfe3f2eSlogwang }
230*2bfe3f2eSlogwang 
231*2bfe3f2eSlogwang static void
232*2bfe3f2eSlogwang populate_efd_table(void)
233*2bfe3f2eSlogwang {
234*2bfe3f2eSlogwang 	unsigned int i;
235*2bfe3f2eSlogwang 	int32_t ret;
236*2bfe3f2eSlogwang 	uint32_t ip_dst;
237*2bfe3f2eSlogwang 	uint8_t socket_id = rte_socket_id();
238*2bfe3f2eSlogwang 	uint64_t node_id;
239*2bfe3f2eSlogwang 
240*2bfe3f2eSlogwang 	/* Add flows in table */
241*2bfe3f2eSlogwang 	for (i = 0; i < num_flows; i++) {
242*2bfe3f2eSlogwang 		node_id = i % num_nodes;
243*2bfe3f2eSlogwang 
244*2bfe3f2eSlogwang 		ip_dst = rte_cpu_to_be_32(i);
245*2bfe3f2eSlogwang 		ret = rte_efd_update(efd_table, socket_id,
246*2bfe3f2eSlogwang 				(void *)&ip_dst, (efd_value_t)node_id);
247*2bfe3f2eSlogwang 		if (ret < 0)
248*2bfe3f2eSlogwang 			rte_exit(EXIT_FAILURE, "Unable to add entry %u in "
249*2bfe3f2eSlogwang 					"EFD table\n", i);
250*2bfe3f2eSlogwang 	}
251*2bfe3f2eSlogwang 
252*2bfe3f2eSlogwang 	printf("EFD table: Adding 0x%x keys\n", num_flows);
253*2bfe3f2eSlogwang }
254*2bfe3f2eSlogwang 
255*2bfe3f2eSlogwang /* Check the link status of all ports in up to 9s, and print them finally */
256*2bfe3f2eSlogwang static void
257*2bfe3f2eSlogwang check_all_ports_link_status(uint16_t port_num, uint32_t port_mask)
258*2bfe3f2eSlogwang {
259*2bfe3f2eSlogwang #define CHECK_INTERVAL 100 /* 100ms */
260*2bfe3f2eSlogwang #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
261*2bfe3f2eSlogwang 	uint8_t count, all_ports_up, print_flag = 0;
262*2bfe3f2eSlogwang 	uint16_t portid;
263*2bfe3f2eSlogwang 	struct rte_eth_link link;
264*2bfe3f2eSlogwang 
265*2bfe3f2eSlogwang 	printf("\nChecking link status");
266*2bfe3f2eSlogwang 	fflush(stdout);
267*2bfe3f2eSlogwang 	for (count = 0; count <= MAX_CHECK_TIME; count++) {
268*2bfe3f2eSlogwang 		all_ports_up = 1;
269*2bfe3f2eSlogwang 		for (portid = 0; portid < port_num; portid++) {
270*2bfe3f2eSlogwang 			if ((port_mask & (1 << info->id[portid])) == 0)
271*2bfe3f2eSlogwang 				continue;
272*2bfe3f2eSlogwang 			memset(&link, 0, sizeof(link));
273*2bfe3f2eSlogwang 			rte_eth_link_get_nowait(info->id[portid], &link);
274*2bfe3f2eSlogwang 			/* print link status if flag set */
275*2bfe3f2eSlogwang 			if (print_flag == 1) {
276*2bfe3f2eSlogwang 				if (link.link_status)
277*2bfe3f2eSlogwang 					printf(
278*2bfe3f2eSlogwang 					"Port%d Link Up. Speed %u Mbps - %s\n",
279*2bfe3f2eSlogwang 						info->id[portid],
280*2bfe3f2eSlogwang 						link.link_speed,
281*2bfe3f2eSlogwang 				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
282*2bfe3f2eSlogwang 					("full-duplex") : ("half-duplex\n"));
283*2bfe3f2eSlogwang 				else
284*2bfe3f2eSlogwang 					printf("Port %d Link Down\n",
285*2bfe3f2eSlogwang 						info->id[portid]);
286*2bfe3f2eSlogwang 				continue;
287*2bfe3f2eSlogwang 			}
288*2bfe3f2eSlogwang 			/* clear all_ports_up flag if any link down */
289*2bfe3f2eSlogwang 			if (link.link_status == ETH_LINK_DOWN) {
290*2bfe3f2eSlogwang 				all_ports_up = 0;
291*2bfe3f2eSlogwang 				break;
292*2bfe3f2eSlogwang 			}
293*2bfe3f2eSlogwang 		}
294*2bfe3f2eSlogwang 		/* after finally printing all link status, get out */
295*2bfe3f2eSlogwang 		if (print_flag == 1)
296*2bfe3f2eSlogwang 			break;
297*2bfe3f2eSlogwang 
298*2bfe3f2eSlogwang 		if (all_ports_up == 0) {
299*2bfe3f2eSlogwang 			printf(".");
300*2bfe3f2eSlogwang 			fflush(stdout);
301*2bfe3f2eSlogwang 			rte_delay_ms(CHECK_INTERVAL);
302*2bfe3f2eSlogwang 		}
303*2bfe3f2eSlogwang 
304*2bfe3f2eSlogwang 		/* set the print_flag if all ports up or timeout */
305*2bfe3f2eSlogwang 		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
306*2bfe3f2eSlogwang 			print_flag = 1;
307*2bfe3f2eSlogwang 			printf("done\n");
308*2bfe3f2eSlogwang 		}
309*2bfe3f2eSlogwang 	}
310*2bfe3f2eSlogwang }
311*2bfe3f2eSlogwang 
312*2bfe3f2eSlogwang /**
313*2bfe3f2eSlogwang  * Main init function for the multi-process server app,
314*2bfe3f2eSlogwang  * calls subfunctions to do each stage of the initialisation.
315*2bfe3f2eSlogwang  */
316*2bfe3f2eSlogwang int
317*2bfe3f2eSlogwang init(int argc, char *argv[])
318*2bfe3f2eSlogwang {
319*2bfe3f2eSlogwang 	int retval;
320*2bfe3f2eSlogwang 	const struct rte_memzone *mz;
321*2bfe3f2eSlogwang 	uint8_t i, total_ports;
322*2bfe3f2eSlogwang 
323*2bfe3f2eSlogwang 	/* init EAL, parsing EAL args */
324*2bfe3f2eSlogwang 	retval = rte_eal_init(argc, argv);
325*2bfe3f2eSlogwang 	if (retval < 0)
326*2bfe3f2eSlogwang 		return -1;
327*2bfe3f2eSlogwang 	argc -= retval;
328*2bfe3f2eSlogwang 	argv += retval;
329*2bfe3f2eSlogwang 
330*2bfe3f2eSlogwang 	/* get total number of ports */
331*2bfe3f2eSlogwang 	total_ports = rte_eth_dev_count();
332*2bfe3f2eSlogwang 
333*2bfe3f2eSlogwang 	/* set up array for port data */
334*2bfe3f2eSlogwang 	mz = rte_memzone_reserve(MZ_SHARED_INFO, sizeof(*info),
335*2bfe3f2eSlogwang 				rte_socket_id(), NO_FLAGS);
336*2bfe3f2eSlogwang 	if (mz == NULL)
337*2bfe3f2eSlogwang 		rte_exit(EXIT_FAILURE, "Cannot reserve memory zone "
338*2bfe3f2eSlogwang 				"for port information\n");
339*2bfe3f2eSlogwang 	memset(mz->addr, 0, sizeof(*info));
340*2bfe3f2eSlogwang 	info = mz->addr;
341*2bfe3f2eSlogwang 
342*2bfe3f2eSlogwang 	/* parse additional, application arguments */
343*2bfe3f2eSlogwang 	retval = parse_app_args(total_ports, argc, argv);
344*2bfe3f2eSlogwang 	if (retval != 0)
345*2bfe3f2eSlogwang 		return -1;
346*2bfe3f2eSlogwang 
347*2bfe3f2eSlogwang 	/* initialise mbuf pools */
348*2bfe3f2eSlogwang 	retval = init_mbuf_pools();
349*2bfe3f2eSlogwang 	if (retval != 0)
350*2bfe3f2eSlogwang 		rte_exit(EXIT_FAILURE, "Cannot create needed mbuf pools\n");
351*2bfe3f2eSlogwang 
352*2bfe3f2eSlogwang 	/* now initialise the ports we will use */
353*2bfe3f2eSlogwang 	for (i = 0; i < info->num_ports; i++) {
354*2bfe3f2eSlogwang 		retval = init_port(info->id[i]);
355*2bfe3f2eSlogwang 		if (retval != 0)
356*2bfe3f2eSlogwang 			rte_exit(EXIT_FAILURE, "Cannot initialise port %u\n",
357*2bfe3f2eSlogwang 					(unsigned int) i);
358*2bfe3f2eSlogwang 	}
359*2bfe3f2eSlogwang 
360*2bfe3f2eSlogwang 	check_all_ports_link_status(info->num_ports, (~0x0));
361*2bfe3f2eSlogwang 
362*2bfe3f2eSlogwang 	/* initialise the node queues/rings for inter-eu comms */
363*2bfe3f2eSlogwang 	init_shm_rings();
364*2bfe3f2eSlogwang 
365*2bfe3f2eSlogwang 	/* Create the EFD table */
366*2bfe3f2eSlogwang 	create_efd_table();
367*2bfe3f2eSlogwang 
368*2bfe3f2eSlogwang 	/* Populate the EFD table */
369*2bfe3f2eSlogwang 	populate_efd_table();
370*2bfe3f2eSlogwang 
371*2bfe3f2eSlogwang 	/* Share the total number of nodes */
372*2bfe3f2eSlogwang 	info->num_nodes = num_nodes;
373*2bfe3f2eSlogwang 
374*2bfe3f2eSlogwang 	/* Share the total number of flows */
375*2bfe3f2eSlogwang 	info->num_flows = num_flows;
376*2bfe3f2eSlogwang 	return 0;
377*2bfe3f2eSlogwang }
378