xref: /dpdk/app/test-pmd/testpmd.c (revision c87988187fdb35cfc7ee1624159ee273e587ac9d)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2012 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34 
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <signal.h>
39 #include <string.h>
40 #include <time.h>
41 #include <fcntl.h>
42 #include <sys/types.h>
43 #include <errno.h>
44 
45 #include <sys/queue.h>
46 #include <sys/stat.h>
47 
48 #include <stdint.h>
49 #include <unistd.h>
50 #include <inttypes.h>
51 
52 #include <rte_common.h>
53 #include <rte_byteorder.h>
54 #include <rte_log.h>
55 #include <rte_debug.h>
56 #include <rte_cycles.h>
57 #include <rte_memory.h>
58 #include <rte_memcpy.h>
59 #include <rte_memzone.h>
60 #include <rte_launch.h>
61 #include <rte_tailq.h>
62 #include <rte_eal.h>
63 #include <rte_per_lcore.h>
64 #include <rte_lcore.h>
65 #include <rte_atomic.h>
66 #include <rte_branch_prediction.h>
67 #include <rte_ring.h>
68 #include <rte_mempool.h>
69 #include <rte_malloc.h>
70 #include <rte_mbuf.h>
71 #include <rte_interrupts.h>
72 #include <rte_pci.h>
73 #include <rte_ether.h>
74 #include <rte_ethdev.h>
75 #include <rte_string_fns.h>
76 
77 #include "testpmd.h"
78 
79 uint16_t verbose_level = 0; /**< Silent by default. */
80 
81 /* use master core for command line ? */
82 uint8_t interactive = 0;
83 
84 /*
85  * NUMA support configuration.
86  * When set, the NUMA support attempts to dispatch the allocation of the
87  * RX and TX memory rings, and of the DMA memory buffers (mbufs) for the
88  * probed ports among the CPU sockets 0 and 1.
89  * Otherwise, all memory is allocated from CPU socket 0.
90  */
91 uint8_t numa_support = 0; /**< No numa support by default */
92 
93 /*
94  * Record the Ethernet address of peer target ports to which packets are
95  * forwarded.
96  * Must be instanciated with the ethernet addresses of peer traffic generator
97  * ports.
98  */
99 struct ether_addr peer_eth_addrs[RTE_MAX_ETHPORTS];
100 portid_t nb_peer_eth_addrs = 0;
101 
102 /*
103  * Probed Target Environment.
104  */
105 struct rte_port *ports;	       /**< For all probed ethernet ports. */
106 portid_t nb_ports;             /**< Number of probed ethernet ports. */
107 struct fwd_lcore **fwd_lcores; /**< For all probed logical cores. */
108 lcoreid_t nb_lcores;           /**< Number of probed logical cores. */
109 
110 /*
111  * Test Forwarding Configuration.
112  *    nb_fwd_lcores <= nb_cfg_lcores <= nb_lcores
113  *    nb_fwd_ports  <= nb_cfg_ports  <= nb_ports
114  */
115 lcoreid_t nb_cfg_lcores; /**< Number of configured logical cores. */
116 lcoreid_t nb_fwd_lcores; /**< Number of forwarding logical cores. */
117 portid_t  nb_cfg_ports;  /**< Number of configured ports. */
118 portid_t  nb_fwd_ports;  /**< Number of forwarding ports. */
119 
120 unsigned int fwd_lcores_cpuids[RTE_MAX_LCORE]; /**< CPU ids configuration. */
121 portid_t fwd_ports_ids[RTE_MAX_ETHPORTS];      /**< Port ids configuration. */
122 
123 struct fwd_stream **fwd_streams; /**< For each RX queue of each port. */
124 streamid_t nb_fwd_streams;       /**< Is equal to (nb_ports * nb_rxq). */
125 
126 /*
127  * Forwarding engines.
128  */
129 struct fwd_engine * fwd_engines[] = {
130 	&io_fwd_engine,
131 	&mac_fwd_engine,
132 	&rx_only_engine,
133 	&tx_only_engine,
134 	&csum_fwd_engine,
135 #ifdef RTE_LIBRTE_IEEE1588
136 	&ieee1588_fwd_engine,
137 #endif
138 	NULL,
139 };
140 
141 struct fwd_config cur_fwd_config;
142 struct fwd_engine *cur_fwd_eng = &io_fwd_engine; /**< IO mode by default. */
143 
144 uint16_t mbuf_data_size = DEFAULT_MBUF_DATA_SIZE; /**< Mbuf data space size. */
145 uint32_t param_total_num_mbufs = 0;  /**< number of mbufs in all pools - if
146                                       * specified on command-line. */
147 
148 /*
149  * Configuration of packet segments used by the "txonly" processing engine.
150  */
151 uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; /**< TXONLY packet length. */
152 uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = {
153 	TXONLY_DEF_PACKET_LEN,
154 };
155 uint8_t  tx_pkt_nb_segs = 1; /**< Number of segments in TXONLY packets */
156 
157 uint16_t nb_pkt_per_burst = DEF_PKT_BURST; /**< Number of packets per burst. */
158 uint16_t mb_mempool_cache = DEF_PKT_BURST; /**< Size of mbuf mempool cache. */
159 
160 /*
161  * Ethernet Ports Configuration.
162  */
163 int promiscuous_on = 1; /**< Ports set in promiscuous mode by default. */
164 
165 /*
166  * Configurable number of RX/TX queues.
167  */
168 queueid_t nb_rxq = 1; /**< Number of RX queues per port. */
169 queueid_t nb_txq = 1; /**< Number of TX queues per port. */
170 
171 /*
172  * Configurable number of RX/TX ring descriptors.
173  */
174 #define RTE_TEST_RX_DESC_DEFAULT 128
175 #define RTE_TEST_TX_DESC_DEFAULT 512
176 uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; /**< Number of RX descriptors. */
177 uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; /**< Number of TX descriptors. */
178 
179 /*
180  * Configurable values of RX and TX ring threshold registers.
181  */
182 #define RX_PTHRESH 8 /**< Default value of RX prefetch threshold register. */
183 #define RX_HTHRESH 8 /**< Default value of RX host threshold register. */
184 #define RX_WTHRESH 4 /**< Default value of RX write-back threshold register. */
185 
186 #define TX_PTHRESH 36 /**< Default value of TX prefetch threshold register. */
187 #define TX_HTHRESH 0 /**< Default value of TX host threshold register. */
188 #define TX_WTHRESH 0 /**< Default value of TX write-back threshold register. */
189 
190 struct rte_eth_thresh rx_thresh = {
191 	.pthresh = RX_PTHRESH,
192 	.hthresh = RX_HTHRESH,
193 	.wthresh = RX_WTHRESH,
194 };
195 
196 struct rte_eth_thresh tx_thresh = {
197 	.pthresh = TX_PTHRESH,
198 	.hthresh = TX_HTHRESH,
199 	.wthresh = TX_WTHRESH,
200 };
201 
202 /*
203  * Configurable value of RX free threshold.
204  */
205 uint16_t rx_free_thresh = 0; /* Immediately free RX descriptors by default. */
206 
207 /*
208  * Configurable value of TX free threshold.
209  */
210 uint16_t tx_free_thresh = 0; /* Use default values. */
211 
212 /*
213  * Configurable value of TX RS bit threshold.
214  */
215 uint16_t tx_rs_thresh = 0; /* Use default values. */
216 
217 /*
218  * Receive Side Scaling (RSS) configuration.
219  */
220 uint16_t rss_hf = ETH_RSS_IPV4 | ETH_RSS_IPV6; /* RSS IP by default. */
221 
222 /*
223  * Port topology configuration
224  */
225 uint16_t port_topology = PORT_TOPOLOGY_PAIRED; /* Ports are paired by default */
226 
227 /*
228  * Ethernet device configuration.
229  */
230 struct rte_eth_rxmode rx_mode = {
231 	.max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */
232 	.split_hdr_size = 0,
233 	.header_split   = 0, /**< Header Split disabled. */
234 	.hw_ip_checksum = 0, /**< IP checksum offload disabled. */
235 	.hw_vlan_filter = 1, /**< VLAN filtering enabled. */
236 	.hw_vlan_strip  = 1, /**< VLAN strip enabled. */
237 	.hw_vlan_extend = 0, /**< Extended VLAN disabled. */
238 	.jumbo_frame    = 0, /**< Jumbo Frame Support disabled. */
239 	.hw_strip_crc   = 0, /**< CRC stripping by hardware disabled. */
240 };
241 
242 struct rte_fdir_conf fdir_conf = {
243 	.mode = RTE_FDIR_MODE_NONE,
244 	.pballoc = RTE_FDIR_PBALLOC_64K,
245 	.status = RTE_FDIR_REPORT_STATUS,
246 	.flexbytes_offset = 0x6,
247 	.drop_queue = 127,
248 };
249 
250 static volatile int test_done = 1; /* stop packet forwarding when set to 1. */
251 
252 struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS];
253 struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS];
254 
255 struct queue_stats_mappings *tx_queue_stats_mappings = tx_queue_stats_mappings_array;
256 struct queue_stats_mappings *rx_queue_stats_mappings = rx_queue_stats_mappings_array;
257 
258 uint16_t nb_tx_queue_stats_mappings = 0;
259 uint16_t nb_rx_queue_stats_mappings = 0;
260 
261 /* Forward function declarations */
262 static void map_port_queue_stats_mapping_registers(uint8_t pi, struct rte_port *port);
263 
264 /*
265  * Setup default configuration.
266  */
267 static void
268 set_default_fwd_lcores_config(void)
269 {
270 	unsigned int i;
271 	unsigned int nb_lc;
272 
273 	nb_lc = 0;
274 	for (i = 0; i < RTE_MAX_LCORE; i++) {
275 		if (! rte_lcore_is_enabled(i))
276 			continue;
277 		if (i == rte_get_master_lcore())
278 			continue;
279 		fwd_lcores_cpuids[nb_lc++] = i;
280 	}
281 	nb_lcores = (lcoreid_t) nb_lc;
282 	nb_cfg_lcores = nb_lcores;
283 	nb_fwd_lcores = 1;
284 }
285 
286 static void
287 set_def_peer_eth_addrs(void)
288 {
289 	portid_t i;
290 
291 	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
292 		peer_eth_addrs[i].addr_bytes[0] = ETHER_LOCAL_ADMIN_ADDR;
293 		peer_eth_addrs[i].addr_bytes[5] = i;
294 	}
295 }
296 
297 static void
298 set_default_fwd_ports_config(void)
299 {
300 	portid_t pt_id;
301 
302 	for (pt_id = 0; pt_id < nb_ports; pt_id++)
303 		fwd_ports_ids[pt_id] = pt_id;
304 
305 	nb_cfg_ports = nb_ports;
306 	nb_fwd_ports = nb_ports;
307 }
308 
309 void
310 set_def_fwd_config(void)
311 {
312 	set_default_fwd_lcores_config();
313 	set_def_peer_eth_addrs();
314 	set_default_fwd_ports_config();
315 }
316 
317 /*
318  * Configuration initialisation done once at init time.
319  */
320 struct mbuf_ctor_arg {
321 	uint16_t seg_buf_offset; /**< offset of data in data segment of mbuf. */
322 	uint16_t seg_buf_size;   /**< size of data segment in mbuf. */
323 };
324 
325 struct mbuf_pool_ctor_arg {
326 	uint16_t seg_buf_size; /**< size of data segment in mbuf. */
327 };
328 
329 static void
330 testpmd_mbuf_ctor(struct rte_mempool *mp,
331 		  void *opaque_arg,
332 		  void *raw_mbuf,
333 		  __attribute__((unused)) unsigned i)
334 {
335 	struct mbuf_ctor_arg *mb_ctor_arg;
336 	struct rte_mbuf    *mb;
337 
338 	mb_ctor_arg = (struct mbuf_ctor_arg *) opaque_arg;
339 	mb = (struct rte_mbuf *) raw_mbuf;
340 
341 	mb->pool         = mp;
342 	mb->buf_addr     = (void *) ((char *)mb + mb_ctor_arg->seg_buf_offset);
343 	mb->buf_physaddr = (uint64_t) (rte_mempool_virt2phy(mp, mb) +
344 			mb_ctor_arg->seg_buf_offset);
345 	mb->buf_len      = mb_ctor_arg->seg_buf_size;
346 	mb->type         = RTE_MBUF_PKT;
347 	mb->ol_flags     = 0;
348 	mb->pkt.data     = (char *) mb->buf_addr + RTE_PKTMBUF_HEADROOM;
349 	mb->pkt.nb_segs  = 1;
350 	mb->pkt.vlan_macip.data = 0;
351 	mb->pkt.hash.rss = 0;
352 }
353 
354 static void
355 testpmd_mbuf_pool_ctor(struct rte_mempool *mp,
356 		       void *opaque_arg)
357 {
358 	struct mbuf_pool_ctor_arg      *mbp_ctor_arg;
359 	struct rte_pktmbuf_pool_private *mbp_priv;
360 
361 	if (mp->private_data_size < sizeof(struct rte_pktmbuf_pool_private)) {
362 		printf("%s(%s) private_data_size %d < %d\n",
363 		       __func__, mp->name, (int) mp->private_data_size,
364 		       (int) sizeof(struct rte_pktmbuf_pool_private));
365 		return;
366 	}
367 	mbp_ctor_arg = (struct mbuf_pool_ctor_arg *) opaque_arg;
368 	mbp_priv = (struct rte_pktmbuf_pool_private *)
369 		((char *)mp + sizeof(struct rte_mempool));
370 	mbp_priv->mbuf_data_room_size = mbp_ctor_arg->seg_buf_size;
371 }
372 
373 static void
374 mbuf_pool_create(uint16_t mbuf_seg_size, unsigned nb_mbuf,
375 		 unsigned int socket_id)
376 {
377 	char pool_name[RTE_MEMPOOL_NAMESIZE];
378 	struct rte_mempool *rte_mp;
379 	struct mbuf_pool_ctor_arg mbp_ctor_arg;
380 	struct mbuf_ctor_arg mb_ctor_arg;
381 	uint32_t mb_size;
382 
383 	mbp_ctor_arg.seg_buf_size = (uint16_t) (RTE_PKTMBUF_HEADROOM +
384 						mbuf_seg_size);
385 	mb_ctor_arg.seg_buf_offset =
386 		(uint16_t) CACHE_LINE_ROUNDUP(sizeof(struct rte_mbuf));
387 	mb_ctor_arg.seg_buf_size = mbp_ctor_arg.seg_buf_size;
388 	mb_size = mb_ctor_arg.seg_buf_offset + mb_ctor_arg.seg_buf_size;
389 	mbuf_poolname_build(socket_id, pool_name, sizeof(pool_name));
390 	rte_mp = rte_mempool_create(pool_name, nb_mbuf, (unsigned) mb_size,
391 				    (unsigned) mb_mempool_cache,
392 				    sizeof(struct rte_pktmbuf_pool_private),
393 				    testpmd_mbuf_pool_ctor, &mbp_ctor_arg,
394 				    testpmd_mbuf_ctor, &mb_ctor_arg,
395 				    socket_id, 0);
396 	if (rte_mp == NULL) {
397 		rte_exit(EXIT_FAILURE, "Creation of mbuf pool for socket %u failed\n",
398 		       socket_id);
399 	}
400 }
401 
402 static void
403 init_config(void)
404 {
405 	struct rte_port *port;
406 	struct rte_mempool *mbp;
407 	unsigned int nb_mbuf_per_pool;
408 	streamid_t sm_id;
409 	lcoreid_t  lc_id;
410 	portid_t   pt_id;
411 
412 	/* Configuration of logical cores. */
413 	fwd_lcores = rte_zmalloc("testpmd: fwd_lcores",
414 				sizeof(struct fwd_lcore *) * nb_lcores,
415 				CACHE_LINE_SIZE);
416 	if (fwd_lcores == NULL) {
417 		rte_exit(EXIT_FAILURE, "rte_zmalloc(%d (struct fwd_lcore *)) failed\n",
418 		       nb_lcores);
419 	}
420 	for (lc_id = 0; lc_id < nb_lcores; lc_id++) {
421 		fwd_lcores[lc_id] = rte_zmalloc("testpmd: struct fwd_lcore",
422 					       sizeof(struct fwd_lcore),
423 					       CACHE_LINE_SIZE);
424 		if (fwd_lcores[lc_id] == NULL) {
425 			rte_exit(EXIT_FAILURE, "rte_zmalloc(struct fwd_lcore) failed\n");
426 		}
427 		fwd_lcores[lc_id]->cpuid_idx = lc_id;
428 	}
429 
430 	/*
431 	 * Create pools of mbuf.
432 	 * If NUMA support is disabled, create a single pool of mbuf in
433 	 * socket 0 memory.
434 	 * Otherwise, create a pool of mbuf in the memory of sockets 0 and 1.
435 	 *
436 	 * Use the maximum value of nb_rxd and nb_txd here, then nb_rxd and
437 	 * nb_txd can be configured at run time.
438 	 */
439 	if (param_total_num_mbufs)
440 		nb_mbuf_per_pool = param_total_num_mbufs;
441 	else {
442 		nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + (nb_lcores * mb_mempool_cache)
443 				+ RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST;
444 		nb_mbuf_per_pool = (nb_mbuf_per_pool * nb_ports);
445 	}
446 	if (numa_support) {
447 		nb_mbuf_per_pool /= 2;
448 		mbuf_pool_create(mbuf_data_size, nb_mbuf_per_pool, 0);
449 		mbuf_pool_create(mbuf_data_size, nb_mbuf_per_pool, 1);
450 	} else {
451 		mbuf_pool_create(mbuf_data_size, nb_mbuf_per_pool, 0);
452 	}
453 
454 	/*
455 	 * Records which Mbuf pool to use by each logical core, if needed.
456 	 */
457 	for (lc_id = 0; lc_id < nb_lcores; lc_id++) {
458 		mbp = mbuf_pool_find(rte_lcore_to_socket_id(lc_id));
459 		if (mbp == NULL)
460 			mbp = mbuf_pool_find(0);
461 		fwd_lcores[lc_id]->mbp = mbp;
462 	}
463 
464 	/* Configuration of Ethernet ports. */
465 	ports = rte_zmalloc("testpmd: ports",
466 			    sizeof(struct rte_port) * nb_ports,
467 			    CACHE_LINE_SIZE);
468 	if (ports == NULL) {
469 		rte_exit(EXIT_FAILURE, "rte_zmalloc(%d struct rte_port) failed\n",
470 		       nb_ports);
471 	}
472 	port = ports;
473 	for (pt_id = 0; pt_id < nb_ports; pt_id++, port++) {
474 		rte_eth_dev_info_get(pt_id, &port->dev_info);
475 		if (nb_rxq > port->dev_info.max_rx_queues) {
476 			rte_exit(EXIT_FAILURE, "Port %d: max RX queues %d < nb_rxq %d\n",
477 			       (int) pt_id,
478 			       (int) port->dev_info.max_rx_queues,
479 			       (int) nb_rxq);
480 		}
481 		if (nb_txq > port->dev_info.max_tx_queues) {
482 			rte_exit(EXIT_FAILURE, "Port %d: max TX queues %d < nb_txq %d\n",
483 			       (int) pt_id,
484 			       (int) port->dev_info.max_tx_queues,
485 			       (int) nb_txq);
486 		}
487 
488 		if (numa_support)
489 			port->socket_id = (pt_id < (nb_ports >> 1)) ? 0 : 1;
490 		else
491 			port->socket_id = 0;
492 	}
493 
494 	/* Configuration of packet forwarding streams. */
495 	nb_fwd_streams = (streamid_t) (nb_ports * nb_rxq);
496 	fwd_streams = rte_zmalloc("testpmd: fwd_streams",
497 				  sizeof(struct fwd_stream *) * nb_fwd_streams,
498 				  CACHE_LINE_SIZE);
499 	if (fwd_streams == NULL) {
500 		rte_exit(EXIT_FAILURE, "rte_zmalloc(%d (struct fwd_stream *)) failed\n",
501 		       nb_fwd_streams);
502 	}
503 	for (sm_id = 0; sm_id < nb_fwd_streams; sm_id++) {
504 		fwd_streams[sm_id] = rte_zmalloc("testpmd: struct fwd_stream",
505 						 sizeof(struct fwd_stream),
506 						 CACHE_LINE_SIZE);
507 		if (fwd_streams[sm_id] == NULL) {
508 			rte_exit(EXIT_FAILURE, "rte_zmalloc(struct fwd_stream) failed\n");
509 		}
510 	}
511 }
512 
513 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
514 static void
515 pkt_burst_stats_display(const char *rx_tx, struct pkt_burst_stats *pbs)
516 {
517 	unsigned int total_burst;
518 	unsigned int nb_burst;
519 	unsigned int burst_stats[3];
520 	uint16_t pktnb_stats[3];
521 	uint16_t nb_pkt;
522 	int burst_percent[3];
523 
524 	/*
525 	 * First compute the total number of packet bursts and the
526 	 * two highest numbers of bursts of the same number of packets.
527 	 */
528 	total_burst = 0;
529 	burst_stats[0] = burst_stats[1] = burst_stats[2] = 0;
530 	pktnb_stats[0] = pktnb_stats[1] = pktnb_stats[2] = 0;
531 	for (nb_pkt = 0; nb_pkt < MAX_PKT_BURST; nb_pkt++) {
532 		nb_burst = pbs->pkt_burst_spread[nb_pkt];
533 		if (nb_burst == 0)
534 			continue;
535 		total_burst += nb_burst;
536 		if (nb_burst > burst_stats[0]) {
537 			burst_stats[1] = burst_stats[0];
538 			pktnb_stats[1] = pktnb_stats[0];
539 			burst_stats[0] = nb_burst;
540 			pktnb_stats[0] = nb_pkt;
541 		}
542 	}
543 	if (total_burst == 0)
544 		return;
545 	burst_percent[0] = (burst_stats[0] * 100) / total_burst;
546 	printf("  %s-bursts : %u [%d%% of %d pkts", rx_tx, total_burst,
547 	       burst_percent[0], (int) pktnb_stats[0]);
548 	if (burst_stats[0] == total_burst) {
549 		printf("]\n");
550 		return;
551 	}
552 	if (burst_stats[0] + burst_stats[1] == total_burst) {
553 		printf(" + %d%% of %d pkts]\n",
554 		       100 - burst_percent[0], pktnb_stats[1]);
555 		return;
556 	}
557 	burst_percent[1] = (burst_stats[1] * 100) / total_burst;
558 	burst_percent[2] = 100 - (burst_percent[0] + burst_percent[1]);
559 	if ((burst_percent[1] == 0) || (burst_percent[2] == 0)) {
560 		printf(" + %d%% of others]\n", 100 - burst_percent[0]);
561 		return;
562 	}
563 	printf(" + %d%% of %d pkts + %d%% of others]\n",
564 	       burst_percent[1], (int) pktnb_stats[1], burst_percent[2]);
565 }
566 #endif /* RTE_TEST_PMD_RECORD_BURST_STATS */
567 
568 static void
569 fwd_port_stats_display(portid_t port_id, struct rte_eth_stats *stats)
570 {
571 	struct rte_port *port;
572 
573 	static const char *fwd_stats_border = "----------------------";
574 
575 	port = &ports[port_id];
576 	printf("\n  %s Forward statistics for port %-2d %s\n",
577 		fwd_stats_border, port_id, fwd_stats_border);
578 	printf("  RX-packets: %-14"PRIu64" RX-dropped: %-14"PRIu64"RX-total: "
579 	       "%-"PRIu64"\n",
580 	       stats->ipackets, stats->ierrors,
581 	       (uint64_t) (stats->ipackets + stats->ierrors));
582 
583 	if (cur_fwd_eng == &csum_fwd_engine)
584 		printf("  Bad-ipcsum: %-14"PRIu64" Bad-l4csum: %-14"PRIu64" \n",
585 				port->rx_bad_ip_csum, port->rx_bad_l4_csum);
586 
587 	printf("  TX-packets: %-14"PRIu64" TX-dropped: %-14"PRIu64"TX-total: "
588 	       "%-"PRIu64"\n",
589 	       stats->opackets, port->tx_dropped,
590 	       (uint64_t) (stats->opackets + port->tx_dropped));
591 
592 	if (stats->rx_nombuf > 0)
593 		printf("  RX-nombufs: %-14"PRIu64"\n", stats->rx_nombuf);
594 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
595 	if (port->rx_stream)
596 		pkt_burst_stats_display("RX", &port->rx_stream->rx_burst_stats);
597 	if (port->tx_stream)
598 		pkt_burst_stats_display("TX", &port->tx_stream->tx_burst_stats);
599 #endif
600 	/* stats fdir */
601 	if (fdir_conf.mode != RTE_FDIR_MODE_NONE)
602 		printf("  Fdirmiss: %-14"PRIu64"   Fdirmatch: %-14"PRIu64"\n",
603 		       stats->fdirmiss,
604 		       stats->fdirmatch);
605 
606 	printf("  %s--------------------------------%s\n",
607 	       fwd_stats_border, fwd_stats_border);
608 }
609 
610 static void
611 fwd_stream_stats_display(streamid_t stream_id)
612 {
613 	struct fwd_stream *fs;
614 	static const char *fwd_top_stats_border = "-------";
615 
616 	fs = fwd_streams[stream_id];
617 	if ((fs->rx_packets == 0) && (fs->tx_packets == 0) &&
618 	    (fs->fwd_dropped == 0))
619 		return;
620 	printf("\n  %s Forward Stats for RX Port=%2d/Queue=%2d -> "
621 	       "TX Port=%2d/Queue=%2d %s\n",
622 	       fwd_top_stats_border, fs->rx_port, fs->rx_queue,
623 	       fs->tx_port, fs->tx_queue, fwd_top_stats_border);
624 	printf("  RX-packets: %-14u TX-packets: %-14u TX-dropped: %-14u",
625 	       fs->rx_packets, fs->tx_packets, fs->fwd_dropped);
626 
627 	/* if checksum mode */
628 	if (cur_fwd_eng == &csum_fwd_engine) {
629 	       printf("  RX- bad IP checksum: %-14u  Rx- bad L4 checksum: %-14u\n",
630 	       fs->rx_bad_ip_csum, fs->rx_bad_l4_csum);
631 	}
632 
633 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
634 	pkt_burst_stats_display("RX", &fs->rx_burst_stats);
635 	pkt_burst_stats_display("TX", &fs->tx_burst_stats);
636 #endif
637 }
638 
639 static void
640 flush_all_rx_queues(void)
641 {
642 	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
643 	portid_t  rxp;
644 	queueid_t rxq;
645 	uint16_t  nb_rx;
646 	uint16_t  i;
647 	uint8_t   j;
648 
649 	for (j = 0; j < 2; j++) {
650 		for (rxp = 0; rxp < nb_ports; rxp++) {
651 			for (rxq = 0; rxq < nb_rxq; rxq++) {
652 				do {
653 					nb_rx = rte_eth_rx_burst(rxp, rxq,
654 								 pkts_burst,
655 								 MAX_PKT_BURST);
656 					for (i = 0; i < nb_rx; i++)
657 						rte_pktmbuf_free(pkts_burst[i]);
658 				} while (nb_rx > 0);
659 			}
660 		}
661 		rte_delay_ms(10); /* wait 10 milli-seconds before retrying */
662 	}
663 }
664 
665 static void
666 run_pkt_fwd_on_lcore(struct fwd_lcore *fc, packet_fwd_t pkt_fwd)
667 {
668 	struct fwd_stream **fsm;
669 	streamid_t nb_fs;
670 	streamid_t sm_id;
671 
672 	fsm = &fwd_streams[fc->stream_idx];
673 	nb_fs = fc->stream_nb;
674 	do {
675 		for (sm_id = 0; sm_id < nb_fs; sm_id++)
676 			(*pkt_fwd)(fsm[sm_id]);
677 	} while (! fc->stopped);
678 }
679 
680 static int
681 start_pkt_forward_on_core(void *fwd_arg)
682 {
683 	run_pkt_fwd_on_lcore((struct fwd_lcore *) fwd_arg,
684 			     cur_fwd_config.fwd_eng->packet_fwd);
685 	return 0;
686 }
687 
688 /*
689  * Run the TXONLY packet forwarding engine to send a single burst of packets.
690  * Used to start communication flows in network loopback test configurations.
691  */
692 static int
693 run_one_txonly_burst_on_core(void *fwd_arg)
694 {
695 	struct fwd_lcore *fwd_lc;
696 	struct fwd_lcore tmp_lcore;
697 
698 	fwd_lc = (struct fwd_lcore *) fwd_arg;
699 	tmp_lcore = *fwd_lc;
700 	tmp_lcore.stopped = 1;
701 	run_pkt_fwd_on_lcore(&tmp_lcore, tx_only_engine.packet_fwd);
702 	return 0;
703 }
704 
705 /*
706  * Launch packet forwarding:
707  *     - Setup per-port forwarding context.
708  *     - launch logical cores with their forwarding configuration.
709  */
710 static void
711 launch_packet_forwarding(lcore_function_t *pkt_fwd_on_lcore)
712 {
713 	port_fwd_begin_t port_fwd_begin;
714 	unsigned int i;
715 	unsigned int lc_id;
716 	int diag;
717 
718 	port_fwd_begin = cur_fwd_config.fwd_eng->port_fwd_begin;
719 	if (port_fwd_begin != NULL) {
720 		for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++)
721 			(*port_fwd_begin)(fwd_ports_ids[i]);
722 	}
723 	for (i = 0; i < cur_fwd_config.nb_fwd_lcores; i++) {
724 		lc_id = fwd_lcores_cpuids[i];
725 		if ((interactive == 0) || (lc_id != rte_lcore_id())) {
726 			fwd_lcores[i]->stopped = 0;
727 			diag = rte_eal_remote_launch(pkt_fwd_on_lcore,
728 						     fwd_lcores[i], lc_id);
729 			if (diag != 0)
730 				printf("launch lcore %u failed - diag=%d\n",
731 				       lc_id, diag);
732 		}
733 	}
734 }
735 
736 /*
737  * Launch packet forwarding configuration.
738  */
739 void
740 start_packet_forwarding(int with_tx_first)
741 {
742 	port_fwd_begin_t port_fwd_begin;
743 	port_fwd_end_t  port_fwd_end;
744 	struct rte_port *port;
745 	unsigned int i;
746 	portid_t   pt_id;
747 	streamid_t sm_id;
748 
749 	if (test_done == 0) {
750 		printf("Packet forwarding already started\n");
751 		return;
752 	}
753 	test_done = 0;
754 	flush_all_rx_queues();
755 	fwd_config_setup();
756 	rxtx_config_display();
757 
758 	for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++) {
759 		pt_id = fwd_ports_ids[i];
760 		port = &ports[pt_id];
761 		rte_eth_stats_get(pt_id, &port->stats);
762 		port->tx_dropped = 0;
763 	}
764 	for (sm_id = 0; sm_id < cur_fwd_config.nb_fwd_streams; sm_id++) {
765 		fwd_streams[sm_id]->rx_packets = 0;
766 		fwd_streams[sm_id]->tx_packets = 0;
767 		fwd_streams[sm_id]->fwd_dropped = 0;
768 		fwd_streams[sm_id]->rx_bad_ip_csum = 0;
769 		fwd_streams[sm_id]->rx_bad_l4_csum = 0;
770 
771 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
772 		memset(&fwd_streams[sm_id]->rx_burst_stats, 0,
773 		       sizeof(fwd_streams[sm_id]->rx_burst_stats));
774 		memset(&fwd_streams[sm_id]->tx_burst_stats, 0,
775 		       sizeof(fwd_streams[sm_id]->tx_burst_stats));
776 #endif
777 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
778 		fwd_streams[sm_id]->core_cycles = 0;
779 #endif
780 	}
781 	if (with_tx_first) {
782 		port_fwd_begin = tx_only_engine.port_fwd_begin;
783 		if (port_fwd_begin != NULL) {
784 			for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++)
785 				(*port_fwd_begin)(fwd_ports_ids[i]);
786 		}
787 		launch_packet_forwarding(run_one_txonly_burst_on_core);
788 		rte_eal_mp_wait_lcore();
789 		port_fwd_end = tx_only_engine.port_fwd_end;
790 		if (port_fwd_end != NULL) {
791 			for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++)
792 				(*port_fwd_end)(fwd_ports_ids[i]);
793 		}
794 	}
795 	launch_packet_forwarding(start_pkt_forward_on_core);
796 }
797 
798 void
799 stop_packet_forwarding(void)
800 {
801 	struct rte_eth_stats stats;
802 	struct rte_port *port;
803 	port_fwd_end_t  port_fwd_end;
804 	int i;
805 	portid_t   pt_id;
806 	streamid_t sm_id;
807 	lcoreid_t  lc_id;
808 	uint64_t total_recv;
809 	uint64_t total_xmit;
810 	uint64_t total_rx_dropped;
811 	uint64_t total_tx_dropped;
812 	uint64_t total_rx_nombuf;
813 	uint64_t tx_dropped;
814 	uint64_t rx_bad_ip_csum;
815 	uint64_t rx_bad_l4_csum;
816 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
817 	uint64_t fwd_cycles;
818 #endif
819 	static const char *acc_stats_border = "+++++++++++++++";
820 
821 	if (test_done) {
822 		printf("Packet forwarding not started\n");
823 		return;
824 	}
825 	printf("Telling cores to stop...");
826 	for (lc_id = 0; lc_id < cur_fwd_config.nb_fwd_lcores; lc_id++)
827 		fwd_lcores[lc_id]->stopped = 1;
828 	printf("\nWaiting for lcores to finish...\n");
829 	rte_eal_mp_wait_lcore();
830 	port_fwd_end = cur_fwd_config.fwd_eng->port_fwd_end;
831 	if (port_fwd_end != NULL) {
832 		for (i = 0; i < cur_fwd_config.nb_fwd_ports; i++) {
833 			pt_id = fwd_ports_ids[i];
834 			(*port_fwd_end)(pt_id);
835 		}
836 	}
837 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
838 	fwd_cycles = 0;
839 #endif
840 	for (sm_id = 0; sm_id < cur_fwd_config.nb_fwd_streams; sm_id++) {
841 		if (cur_fwd_config.nb_fwd_streams >
842 		    cur_fwd_config.nb_fwd_ports) {
843 			fwd_stream_stats_display(sm_id);
844 			ports[fwd_streams[sm_id]->tx_port].tx_stream = NULL;
845 			ports[fwd_streams[sm_id]->rx_port].rx_stream = NULL;
846 		} else {
847 			ports[fwd_streams[sm_id]->tx_port].tx_stream =
848 				fwd_streams[sm_id];
849 			ports[fwd_streams[sm_id]->rx_port].rx_stream =
850 				fwd_streams[sm_id];
851 		}
852 		tx_dropped = ports[fwd_streams[sm_id]->tx_port].tx_dropped;
853 		tx_dropped = (uint64_t) (tx_dropped +
854 					 fwd_streams[sm_id]->fwd_dropped);
855 		ports[fwd_streams[sm_id]->tx_port].tx_dropped = tx_dropped;
856 
857 		rx_bad_ip_csum = ports[fwd_streams[sm_id]->rx_port].rx_bad_ip_csum;
858 		rx_bad_ip_csum = (uint64_t) (rx_bad_ip_csum +
859 					 fwd_streams[sm_id]->rx_bad_ip_csum);
860 		ports[fwd_streams[sm_id]->rx_port].rx_bad_ip_csum = rx_bad_ip_csum;
861 
862 		rx_bad_l4_csum = ports[fwd_streams[sm_id]->rx_port].rx_bad_l4_csum;
863 		rx_bad_l4_csum = (uint64_t) (rx_bad_l4_csum +
864 					 fwd_streams[sm_id]->rx_bad_l4_csum);
865 		ports[fwd_streams[sm_id]->rx_port].rx_bad_l4_csum = rx_bad_l4_csum;
866 
867 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
868 		fwd_cycles = (uint64_t) (fwd_cycles +
869 					 fwd_streams[sm_id]->core_cycles);
870 #endif
871 	}
872 	total_recv = 0;
873 	total_xmit = 0;
874 	total_rx_dropped = 0;
875 	total_tx_dropped = 0;
876 	total_rx_nombuf  = 0;
877 	for (i = 0; i < ((cur_fwd_config.nb_fwd_ports + 1) & ~0x1); i++) {
878 		pt_id = fwd_ports_ids[i];
879 
880 		port = &ports[pt_id];
881 		rte_eth_stats_get(pt_id, &stats);
882 		stats.ipackets -= port->stats.ipackets;
883 		port->stats.ipackets = 0;
884 		stats.opackets -= port->stats.opackets;
885 		port->stats.opackets = 0;
886 		stats.ibytes   -= port->stats.ibytes;
887 		port->stats.ibytes = 0;
888 		stats.obytes   -= port->stats.obytes;
889 		port->stats.obytes = 0;
890 		stats.ierrors  -= port->stats.ierrors;
891 		port->stats.ierrors = 0;
892 		stats.oerrors  -= port->stats.oerrors;
893 		port->stats.oerrors = 0;
894 		stats.rx_nombuf -= port->stats.rx_nombuf;
895 		port->stats.rx_nombuf = 0;
896 		stats.fdirmatch -= port->stats.fdirmatch;
897 		port->stats.rx_nombuf = 0;
898 		stats.fdirmiss -= port->stats.fdirmiss;
899 		port->stats.rx_nombuf = 0;
900 
901 		total_recv += stats.ipackets;
902 		total_xmit += stats.opackets;
903 		total_rx_dropped += stats.ierrors;
904 		total_tx_dropped += port->tx_dropped;
905 		total_rx_nombuf  += stats.rx_nombuf;
906 
907 		fwd_port_stats_display(pt_id, &stats);
908 	}
909 	printf("\n  %s Accumulated forward statistics for all ports"
910 	       "%s\n",
911 	       acc_stats_border, acc_stats_border);
912 	printf("  RX-packets: %-14"PRIu64" RX-dropped: %-14"PRIu64"RX-total: "
913 	       "%-"PRIu64"\n"
914 	       "  TX-packets: %-14"PRIu64" TX-dropped: %-14"PRIu64"TX-total: "
915 	       "%-"PRIu64"\n",
916 	       total_recv, total_rx_dropped, total_recv + total_rx_dropped,
917 	       total_xmit, total_tx_dropped, total_xmit + total_tx_dropped);
918 	if (total_rx_nombuf > 0)
919 		printf("  RX-nombufs: %-14"PRIu64"\n", total_rx_nombuf);
920 	printf("  %s++++++++++++++++++++++++++++++++++++++++++++++"
921 	       "%s\n",
922 	       acc_stats_border, acc_stats_border);
923 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
924 	if (total_recv > 0)
925 		printf("\n  CPU cycles/packet=%u (total cycles="
926 		       "%"PRIu64" / total RX packets=%"PRIu64")\n",
927 		       (unsigned int)(fwd_cycles / total_recv),
928 		       fwd_cycles, total_recv);
929 #endif
930 	printf("\nDone.\n");
931 	test_done = 1;
932 }
933 
934 void
935 pmd_test_exit(void)
936 {
937 	portid_t pt_id;
938 
939 	for (pt_id = 0; pt_id < nb_ports; pt_id++) {
940 		printf("Stopping port %d...", pt_id);
941 		fflush(stdout);
942 		rte_eth_dev_close(pt_id);
943 		printf("done\n");
944 	}
945 	printf("bye...\n");
946 }
947 
948 typedef void (*cmd_func_t)(void);
949 struct pmd_test_command {
950 	const char *cmd_name;
951 	cmd_func_t cmd_func;
952 };
953 
954 #define PMD_TEST_CMD_NB (sizeof(pmd_test_menu) / sizeof(pmd_test_menu[0]))
955 
956 static void
957 fatal_init_error(const char *func_name, uint8_t port_id, int diag)
958 {
959 	rte_panic("%s(port_id=%d) failed - diag=%d\n",
960 		  func_name, port_id, diag);
961 }
962 
963 static void
964 init_ports(void)
965 {
966 	struct rte_eth_link   link;
967 	struct rte_eth_conf   port_conf = {
968 		.intr_conf = {
969 			.lsc = 0,
970 		},
971 	};
972 	struct rte_eth_rxconf rx_conf;
973 	struct rte_eth_txconf tx_conf;
974 	struct rte_port *port;
975 	unsigned int sock_id;
976 	portid_t  pi;
977 	queueid_t qi;
978 	int diag;
979 
980 	port_conf.rxmode = rx_mode;
981 	port_conf.fdir_conf = fdir_conf;
982 
983 	if (nb_rxq > 0) { /* configure RSS */
984 		port_conf.rx_adv_conf.rss_conf.rss_key = NULL;
985 		/* use default hash key */
986 		port_conf.rx_adv_conf.rss_conf.rss_hf = rss_hf;
987 	} else
988 		port_conf.rx_adv_conf.rss_conf.rss_hf = 0;
989 	rx_conf.rx_thresh = rx_thresh;
990 	rx_conf.rx_free_thresh = rx_free_thresh;
991 	tx_conf.tx_thresh = tx_thresh;
992 	tx_conf.tx_rs_thresh = tx_rs_thresh;
993 	tx_conf.tx_free_thresh = tx_free_thresh;
994 
995 	for (pi = 0; pi < nb_ports; pi++) {
996 		port = &ports[pi];
997 		memcpy(&port->dev_conf, &port_conf, sizeof(port_conf));
998 		sock_id = port->socket_id;
999 		printf("Initializing port %d... ", pi);
1000 		fflush(stdout);
1001 		diag = rte_eth_dev_configure(pi, nb_rxq, nb_txq, &port_conf);
1002 		if (diag != 0) {
1003 			fatal_init_error("rte_eth_dev_configure", pi, diag);
1004 			/* NOT REACHED */
1005 		}
1006 		rte_eth_macaddr_get(pi, &port->eth_addr);
1007 		for (qi = 0; qi < nb_txq; qi++) {
1008 			diag = rte_eth_tx_queue_setup(pi, qi, nb_txd,
1009 						      sock_id,
1010 						      &tx_conf);
1011 			if (diag != 0) {
1012 				fatal_init_error("rte_eth_tx_queue_setup",
1013 						 pi, diag);
1014 				/* NOT REACHED */
1015 			}
1016 		}
1017 		for (qi = 0; qi < nb_rxq; qi++) {
1018 			diag = rte_eth_rx_queue_setup(pi, qi, nb_rxd, sock_id,
1019 						      &rx_conf,
1020 						      mbuf_pool_find(sock_id));
1021 			if (diag != 0) {
1022 				fatal_init_error("rte_eth_rx_queue_setup",
1023 						 pi , diag);
1024 				/* NOT REACHED */
1025 			}
1026 		}
1027 
1028 		/* Start device */
1029 		diag = rte_eth_dev_start(pi);
1030 		if (diag != 0) {
1031 			fatal_init_error("rte_eth_dev_start", pi, diag);
1032 			/* NOT REACHED */
1033 		}
1034 		printf("done: ");
1035 		rte_eth_link_get(pi, &link);
1036 		if (link.link_status) {
1037 			printf(" Link Up - speed %u Mbps - %s\n",
1038 			       (unsigned) link.link_speed,
1039 			       (link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
1040 			       ("full-duplex") : ("half-duplex\n"));
1041 		} else {
1042 			printf(" Link Down\n");
1043 		}
1044 
1045 		/*
1046 		 * If enabled, put device in promiscuous mode.
1047 		 * This allows the PMD test in IO forwarding mode to forward
1048 		 * packets to itself through 2 cross-connected  ports of the
1049 		 * target machine.
1050 		 */
1051 		if (promiscuous_on)
1052 			rte_eth_promiscuous_enable(pi);
1053 	}
1054 }
1055 
1056 #ifdef RTE_EXEC_ENV_BAREMETAL
1057 #define main _main
1058 #endif
1059 
1060 int
1061 main(int argc, char** argv)
1062 {
1063 	int  diag;
1064 
1065 	diag = rte_eal_init(argc, argv);
1066 	if (diag < 0)
1067 		rte_panic("Cannot init EAL\n");
1068 
1069 	if (rte_pmd_init_all())
1070 		rte_panic("Cannot init PMD\n");
1071 
1072 	if (rte_eal_pci_probe())
1073 		rte_panic("Cannot probe PCI\n");
1074 
1075 	nb_ports = (portid_t) rte_eth_dev_count();
1076 	if (nb_ports == 0)
1077 		rte_exit(EXIT_FAILURE, "No probed ethernet devices - check that "
1078 			  "CONFIG_RTE_LIBRTE_IGB_PMD=y and that "
1079 			  "CONFIG_RTE_LIBRTE_EM_PMD=y and that "
1080 			  "CONFIG_RTE_LIBRTE_IXGBE_PMD=y in your "
1081 			  "configuration file\n");
1082 
1083 	set_def_fwd_config();
1084 	if (nb_lcores == 0)
1085 		rte_panic("Empty set of forwarding logical cores - check the "
1086 			  "core mask supplied in the command parameters\n");
1087 
1088 	argc -= diag;
1089 	argv += diag;
1090 	if (argc > 1)
1091 		launch_args_parse(argc, argv);
1092 
1093 	if (nb_rxq > nb_txq)
1094 		printf("Warning: nb_rxq=%d enables RSS configuration, "
1095 		       "but nb_txq=%d will prevent to fully test it.\n",
1096 		       nb_rxq, nb_txq);
1097 
1098 	init_config();
1099 
1100 	init_ports();
1101 
1102 	if (interactive == 1)
1103 		prompt();
1104 	else {
1105 		char c;
1106 		int rc;
1107 
1108 		printf("No commandline core given, start packet forwarding\n");
1109 		start_packet_forwarding(0);
1110 		printf("Press enter to exit\n");
1111 		rc = read(0, &c, 1);
1112 		if (rc < 0)
1113 			return 1;
1114 	}
1115 
1116 	return 0;
1117 }
1118