13998e2a0SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 23998e2a0SBruce Richardson * Copyright(c) 2010-2016 Intel Corporation 3e64833f2SRemy Horton */ 4e64833f2SRemy Horton 5e64833f2SRemy Horton #include <stdio.h> 6e64833f2SRemy Horton #include <stdlib.h> 7e64833f2SRemy Horton #include <string.h> 8e64833f2SRemy Horton #include <stdint.h> 9e64833f2SRemy Horton #include <inttypes.h> 10e64833f2SRemy Horton #include <sys/types.h> 11e64833f2SRemy Horton #include <sys/queue.h> 12e64833f2SRemy Horton #include <netinet/in.h> 13e64833f2SRemy Horton #include <setjmp.h> 14e64833f2SRemy Horton #include <stdarg.h> 15e64833f2SRemy Horton #include <ctype.h> 16e64833f2SRemy Horton #include <errno.h> 17e64833f2SRemy Horton #include <getopt.h> 1891e89e47SRemy Horton #include <signal.h> 19e64833f2SRemy Horton 20e64833f2SRemy Horton #include <rte_common.h> 21e64833f2SRemy Horton #include <rte_log.h> 22e2366e74STomasz Kulasek #include <rte_malloc.h> 23e64833f2SRemy Horton #include <rte_memory.h> 24e64833f2SRemy Horton #include <rte_memcpy.h> 25e64833f2SRemy Horton #include <rte_eal.h> 26e64833f2SRemy Horton #include <rte_launch.h> 27e64833f2SRemy Horton #include <rte_atomic.h> 28e64833f2SRemy Horton #include <rte_cycles.h> 29e64833f2SRemy Horton #include <rte_prefetch.h> 30e64833f2SRemy Horton #include <rte_lcore.h> 31e64833f2SRemy Horton #include <rte_per_lcore.h> 32e64833f2SRemy Horton #include <rte_branch_prediction.h> 33e64833f2SRemy Horton #include <rte_interrupts.h> 34e64833f2SRemy Horton #include <rte_random.h> 35e64833f2SRemy Horton #include <rte_debug.h> 36e64833f2SRemy Horton #include <rte_ether.h> 37e64833f2SRemy Horton #include <rte_ethdev.h> 38e64833f2SRemy Horton #include <rte_mempool.h> 39e64833f2SRemy Horton #include <rte_mbuf.h> 40e64833f2SRemy Horton #include <rte_timer.h> 41e64833f2SRemy Horton #include <rte_keepalive.h> 42e64833f2SRemy Horton 437b2a704cSRemy Horton #include "shm.h" 447b2a704cSRemy Horton 45e64833f2SRemy Horton #define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1 46e64833f2SRemy Horton 47e64833f2SRemy Horton #define NB_MBUF 8192 48e64833f2SRemy Horton 49e64833f2SRemy Horton #define MAX_PKT_BURST 32 50e64833f2SRemy Horton #define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */ 51e64833f2SRemy Horton 52e64833f2SRemy Horton /* 53e64833f2SRemy Horton * Configurable number of RX/TX ring descriptors 54e64833f2SRemy Horton */ 55867a6c66SKevin Laatz #define RTE_TEST_RX_DESC_DEFAULT 1024 56867a6c66SKevin Laatz #define RTE_TEST_TX_DESC_DEFAULT 1024 57e64833f2SRemy Horton static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; 58e64833f2SRemy Horton static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; 59e64833f2SRemy Horton 60e64833f2SRemy Horton /* ethernet addresses of ports */ 616d13ea8eSOlivier Matz static struct rte_ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS]; 62e64833f2SRemy Horton 63e64833f2SRemy Horton /* mask of enabled ports */ 64e64833f2SRemy Horton static uint32_t l2fwd_enabled_port_mask; 65e64833f2SRemy Horton 66e64833f2SRemy Horton /* list of enabled ports */ 67e64833f2SRemy Horton static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS]; 68e64833f2SRemy Horton 69e64833f2SRemy Horton static unsigned int l2fwd_rx_queue_per_lcore = 1; 70e64833f2SRemy Horton 71e64833f2SRemy Horton #define MAX_RX_QUEUE_PER_LCORE 16 72e64833f2SRemy Horton #define MAX_TX_QUEUE_PER_PORT 16 73e64833f2SRemy Horton struct lcore_queue_conf { 74e64833f2SRemy Horton unsigned n_rx_port; 75e64833f2SRemy Horton unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE]; 76e64833f2SRemy Horton } __rte_cache_aligned; 77e64833f2SRemy Horton struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE]; 78e64833f2SRemy Horton 79e2366e74STomasz Kulasek struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS]; 80e2366e74STomasz Kulasek 81563e239bSShahaf Shuler static struct rte_eth_conf port_conf = { 82e64833f2SRemy Horton .rxmode = { 83e64833f2SRemy Horton .split_hdr_size = 0, 84e64833f2SRemy Horton }, 85e64833f2SRemy Horton .txmode = { 86e64833f2SRemy Horton .mq_mode = ETH_MQ_TX_NONE, 87e64833f2SRemy Horton }, 88e64833f2SRemy Horton }; 89e64833f2SRemy Horton 90e64833f2SRemy Horton struct rte_mempool *l2fwd_pktmbuf_pool = NULL; 91e64833f2SRemy Horton 92e64833f2SRemy Horton /* Per-port statistics struct */ 93e64833f2SRemy Horton struct l2fwd_port_statistics { 94e64833f2SRemy Horton uint64_t tx; 95e64833f2SRemy Horton uint64_t rx; 96e64833f2SRemy Horton uint64_t dropped; 97e64833f2SRemy Horton } __rte_cache_aligned; 98e64833f2SRemy Horton struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS]; 99e64833f2SRemy Horton 100e64833f2SRemy Horton /* A tsc-based timer responsible for triggering statistics printout */ 101e64833f2SRemy Horton #define TIMER_MILLISECOND 1 102e64833f2SRemy Horton #define MAX_TIMER_PERIOD 86400 /* 1 day max */ 103e64833f2SRemy Horton static int64_t timer_period = 10 * TIMER_MILLISECOND * 1000; /* 10 seconds */ 104e64833f2SRemy Horton static int64_t check_period = 5; /* default check cycle is 5ms */ 105e64833f2SRemy Horton 106e64833f2SRemy Horton /* Keepalive structure */ 107e64833f2SRemy Horton struct rte_keepalive *rte_global_keepalive_info; 108e64833f2SRemy Horton 10991e89e47SRemy Horton /* Termination signalling */ 11091e89e47SRemy Horton static int terminate_signal_received; 11191e89e47SRemy Horton 11291e89e47SRemy Horton /* Termination signal handler */ 11391e89e47SRemy Horton static void handle_sigterm(__rte_unused int value) 11491e89e47SRemy Horton { 11591e89e47SRemy Horton terminate_signal_received = 1; 11691e89e47SRemy Horton } 11791e89e47SRemy Horton 118e64833f2SRemy Horton /* Print out statistics on packets dropped */ 119e64833f2SRemy Horton static void 120e64833f2SRemy Horton print_stats(__attribute__((unused)) struct rte_timer *ptr_timer, 121e64833f2SRemy Horton __attribute__((unused)) void *ptr_data) 122e64833f2SRemy Horton { 123e64833f2SRemy Horton uint64_t total_packets_dropped, total_packets_tx, total_packets_rx; 124f8244c63SZhiyong Yang uint16_t portid; 125e64833f2SRemy Horton 126e64833f2SRemy Horton total_packets_dropped = 0; 127e64833f2SRemy Horton total_packets_tx = 0; 128e64833f2SRemy Horton total_packets_rx = 0; 129e64833f2SRemy Horton 130e64833f2SRemy Horton const char clr[] = { 27, '[', '2', 'J', '\0' }; 131e64833f2SRemy Horton const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' }; 132e64833f2SRemy Horton 133e64833f2SRemy Horton /* Clear screen and move to top left */ 134e64833f2SRemy Horton printf("%s%s", clr, topLeft); 135e64833f2SRemy Horton 136e64833f2SRemy Horton printf("\nPort statistics ===================================="); 137e64833f2SRemy Horton 138e64833f2SRemy Horton for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { 139e64833f2SRemy Horton /* skip disabled ports */ 140e64833f2SRemy Horton if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) 141e64833f2SRemy Horton continue; 142e64833f2SRemy Horton printf("\nStatistics for port %u ------------------------------" 143e64833f2SRemy Horton "\nPackets sent: %24"PRIu64 144e64833f2SRemy Horton "\nPackets received: %20"PRIu64 145e64833f2SRemy Horton "\nPackets dropped: %21"PRIu64, 146e64833f2SRemy Horton portid, 147e64833f2SRemy Horton port_statistics[portid].tx, 148e64833f2SRemy Horton port_statistics[portid].rx, 149e64833f2SRemy Horton port_statistics[portid].dropped); 150e64833f2SRemy Horton 151e64833f2SRemy Horton total_packets_dropped += port_statistics[portid].dropped; 152e64833f2SRemy Horton total_packets_tx += port_statistics[portid].tx; 153e64833f2SRemy Horton total_packets_rx += port_statistics[portid].rx; 154e64833f2SRemy Horton } 155e64833f2SRemy Horton printf("\nAggregate statistics ===============================" 156e64833f2SRemy Horton "\nTotal packets sent: %18"PRIu64 157e64833f2SRemy Horton "\nTotal packets received: %14"PRIu64 158e64833f2SRemy Horton "\nTotal packets dropped: %15"PRIu64, 159e64833f2SRemy Horton total_packets_tx, 160e64833f2SRemy Horton total_packets_rx, 161e64833f2SRemy Horton total_packets_dropped); 162e64833f2SRemy Horton printf("\n====================================================\n"); 163e64833f2SRemy Horton } 164e64833f2SRemy Horton 165e64833f2SRemy Horton static void 166e64833f2SRemy Horton l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid) 167e64833f2SRemy Horton { 1686d13ea8eSOlivier Matz struct rte_ether_hdr *eth; 169e64833f2SRemy Horton void *tmp; 170e2366e74STomasz Kulasek int sent; 171e64833f2SRemy Horton unsigned dst_port; 172e2366e74STomasz Kulasek struct rte_eth_dev_tx_buffer *buffer; 173e64833f2SRemy Horton 174e64833f2SRemy Horton dst_port = l2fwd_dst_ports[portid]; 1756d13ea8eSOlivier Matz eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); 176e64833f2SRemy Horton 177e64833f2SRemy Horton /* 02:00:00:00:00:xx */ 178e64833f2SRemy Horton tmp = ð->d_addr.addr_bytes[0]; 179e64833f2SRemy Horton *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40); 180e64833f2SRemy Horton 181e64833f2SRemy Horton /* src addr */ 182*538da7a1SOlivier Matz rte_ether_addr_copy(&l2fwd_ports_eth_addr[dst_port], ð->s_addr); 183e64833f2SRemy Horton 184e2366e74STomasz Kulasek buffer = tx_buffer[dst_port]; 185e2366e74STomasz Kulasek sent = rte_eth_tx_buffer(dst_port, 0, buffer, m); 186e2366e74STomasz Kulasek if (sent) 187e2366e74STomasz Kulasek port_statistics[dst_port].tx += sent; 188e64833f2SRemy Horton } 189e64833f2SRemy Horton 190e64833f2SRemy Horton /* main processing loop */ 191e64833f2SRemy Horton static void 192e64833f2SRemy Horton l2fwd_main_loop(void) 193e64833f2SRemy Horton { 194e64833f2SRemy Horton struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; 195e64833f2SRemy Horton struct rte_mbuf *m; 196e2366e74STomasz Kulasek int sent; 197e64833f2SRemy Horton unsigned lcore_id; 198e64833f2SRemy Horton uint64_t prev_tsc, diff_tsc, cur_tsc; 199e64833f2SRemy Horton unsigned i, j, portid, nb_rx; 200e64833f2SRemy Horton struct lcore_queue_conf *qconf; 201e64833f2SRemy Horton const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) 202e64833f2SRemy Horton / US_PER_S * BURST_TX_DRAIN_US; 203e2366e74STomasz Kulasek struct rte_eth_dev_tx_buffer *buffer; 204e64833f2SRemy Horton 205e64833f2SRemy Horton prev_tsc = 0; 206e64833f2SRemy Horton 207e64833f2SRemy Horton lcore_id = rte_lcore_id(); 208e64833f2SRemy Horton qconf = &lcore_queue_conf[lcore_id]; 209e64833f2SRemy Horton 210e64833f2SRemy Horton if (qconf->n_rx_port == 0) { 211e64833f2SRemy Horton RTE_LOG(INFO, L2FWD, "lcore %u has nothing to do\n", lcore_id); 212e64833f2SRemy Horton return; 213e64833f2SRemy Horton } 214e64833f2SRemy Horton 215e64833f2SRemy Horton RTE_LOG(INFO, L2FWD, "entering main loop on lcore %u\n", lcore_id); 216e64833f2SRemy Horton 217e64833f2SRemy Horton for (i = 0; i < qconf->n_rx_port; i++) { 218e64833f2SRemy Horton 219e64833f2SRemy Horton portid = qconf->rx_port_list[i]; 220e64833f2SRemy Horton RTE_LOG(INFO, L2FWD, " -- lcoreid=%u portid=%u\n", lcore_id, 221e64833f2SRemy Horton portid); 222e64833f2SRemy Horton } 223e64833f2SRemy Horton 224e64833f2SRemy Horton uint64_t tsc_initial = rte_rdtsc(); 225e64833f2SRemy Horton uint64_t tsc_lifetime = (rand()&0x07) * rte_get_tsc_hz(); 226e64833f2SRemy Horton 22791e89e47SRemy Horton while (!terminate_signal_received) { 228e64833f2SRemy Horton /* Keepalive heartbeat */ 229e64833f2SRemy Horton rte_keepalive_mark_alive(rte_global_keepalive_info); 230e64833f2SRemy Horton 231e64833f2SRemy Horton cur_tsc = rte_rdtsc(); 232e64833f2SRemy Horton 233e64833f2SRemy Horton /* 234e64833f2SRemy Horton * Die randomly within 7 secs for demo purposes if 235e64833f2SRemy Horton * keepalive enabled 236e64833f2SRemy Horton */ 237e64833f2SRemy Horton if (check_period > 0 && cur_tsc - tsc_initial > tsc_lifetime) 238e64833f2SRemy Horton break; 239e64833f2SRemy Horton 240e64833f2SRemy Horton /* 241e64833f2SRemy Horton * TX burst queue drain 242e64833f2SRemy Horton */ 243e64833f2SRemy Horton diff_tsc = cur_tsc - prev_tsc; 244e64833f2SRemy Horton if (unlikely(diff_tsc > drain_tsc)) { 245e64833f2SRemy Horton 246e2366e74STomasz Kulasek for (i = 0; i < qconf->n_rx_port; i++) { 247e2366e74STomasz Kulasek 248e2366e74STomasz Kulasek portid = l2fwd_dst_ports[qconf->rx_port_list[i]]; 249e2366e74STomasz Kulasek buffer = tx_buffer[portid]; 250e2366e74STomasz Kulasek 251e2366e74STomasz Kulasek sent = rte_eth_tx_buffer_flush(portid, 0, buffer); 252e2366e74STomasz Kulasek if (sent) 253e2366e74STomasz Kulasek port_statistics[portid].tx += sent; 254e2366e74STomasz Kulasek 255e64833f2SRemy Horton } 256e64833f2SRemy Horton 257e64833f2SRemy Horton prev_tsc = cur_tsc; 258e64833f2SRemy Horton } 259e64833f2SRemy Horton 260e64833f2SRemy Horton /* 261e64833f2SRemy Horton * Read packet from RX queues 262e64833f2SRemy Horton */ 263e64833f2SRemy Horton for (i = 0; i < qconf->n_rx_port; i++) { 264e64833f2SRemy Horton 265e64833f2SRemy Horton portid = qconf->rx_port_list[i]; 266f8244c63SZhiyong Yang nb_rx = rte_eth_rx_burst(portid, 0, 267e64833f2SRemy Horton pkts_burst, MAX_PKT_BURST); 268e64833f2SRemy Horton 269e64833f2SRemy Horton port_statistics[portid].rx += nb_rx; 270e64833f2SRemy Horton 271e64833f2SRemy Horton for (j = 0; j < nb_rx; j++) { 272e64833f2SRemy Horton m = pkts_burst[j]; 273e64833f2SRemy Horton rte_prefetch0(rte_pktmbuf_mtod(m, void *)); 274e64833f2SRemy Horton l2fwd_simple_forward(m, portid); 275e64833f2SRemy Horton } 276e64833f2SRemy Horton } 277e64833f2SRemy Horton } 278e64833f2SRemy Horton } 279e64833f2SRemy Horton 280e64833f2SRemy Horton static int 281e64833f2SRemy Horton l2fwd_launch_one_lcore(__attribute__((unused)) void *dummy) 282e64833f2SRemy Horton { 283e64833f2SRemy Horton l2fwd_main_loop(); 284e64833f2SRemy Horton return 0; 285e64833f2SRemy Horton } 286e64833f2SRemy Horton 287e64833f2SRemy Horton /* display usage */ 288e64833f2SRemy Horton static void 289e64833f2SRemy Horton l2fwd_usage(const char *prgname) 290e64833f2SRemy Horton { 291e64833f2SRemy Horton printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n" 292e64833f2SRemy Horton " -p PORTMASK: hexadecimal bitmask of ports to configure\n" 293e64833f2SRemy Horton " -q NQ: number of queue (=ports) per lcore (default is 1)\n" 294e64833f2SRemy Horton " -K PERIOD: Keepalive check period (5 default; 86400 max)\n" 295e64833f2SRemy Horton " -T PERIOD: statistics will be refreshed each PERIOD seconds (0 to disable, 10 default, 86400 maximum)\n", 296e64833f2SRemy Horton prgname); 297e64833f2SRemy Horton } 298e64833f2SRemy Horton 299e64833f2SRemy Horton static int 300e64833f2SRemy Horton l2fwd_parse_portmask(const char *portmask) 301e64833f2SRemy Horton { 302e64833f2SRemy Horton char *end = NULL; 303e64833f2SRemy Horton unsigned long pm; 304e64833f2SRemy Horton 305e64833f2SRemy Horton /* parse hexadecimal string */ 306e64833f2SRemy Horton pm = strtoul(portmask, &end, 16); 307e64833f2SRemy Horton if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) 308e64833f2SRemy Horton return -1; 309e64833f2SRemy Horton 310e64833f2SRemy Horton if (pm == 0) 311e64833f2SRemy Horton return -1; 312e64833f2SRemy Horton 313e64833f2SRemy Horton return pm; 314e64833f2SRemy Horton } 315e64833f2SRemy Horton 316e64833f2SRemy Horton static unsigned int 317e64833f2SRemy Horton l2fwd_parse_nqueue(const char *q_arg) 318e64833f2SRemy Horton { 319e64833f2SRemy Horton char *end = NULL; 320e64833f2SRemy Horton unsigned long n; 321e64833f2SRemy Horton 322e64833f2SRemy Horton /* parse hexadecimal string */ 323e64833f2SRemy Horton n = strtoul(q_arg, &end, 10); 324e64833f2SRemy Horton if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 325e64833f2SRemy Horton return 0; 326e64833f2SRemy Horton if (n == 0) 327e64833f2SRemy Horton return 0; 328e64833f2SRemy Horton if (n >= MAX_RX_QUEUE_PER_LCORE) 329e64833f2SRemy Horton return 0; 330e64833f2SRemy Horton 331e64833f2SRemy Horton return n; 332e64833f2SRemy Horton } 333e64833f2SRemy Horton 334e64833f2SRemy Horton static int 335e64833f2SRemy Horton l2fwd_parse_timer_period(const char *q_arg) 336e64833f2SRemy Horton { 337e64833f2SRemy Horton char *end = NULL; 338e64833f2SRemy Horton int n; 339e64833f2SRemy Horton 340e64833f2SRemy Horton /* parse number string */ 341e64833f2SRemy Horton n = strtol(q_arg, &end, 10); 342e64833f2SRemy Horton if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 343e64833f2SRemy Horton return -1; 344e64833f2SRemy Horton if (n >= MAX_TIMER_PERIOD) 345e64833f2SRemy Horton return -1; 346e64833f2SRemy Horton 347e64833f2SRemy Horton return n; 348e64833f2SRemy Horton } 349e64833f2SRemy Horton 350e64833f2SRemy Horton static int 351e64833f2SRemy Horton l2fwd_parse_check_period(const char *q_arg) 352e64833f2SRemy Horton { 353e64833f2SRemy Horton char *end = NULL; 354e64833f2SRemy Horton int n; 355e64833f2SRemy Horton 356e64833f2SRemy Horton /* parse number string */ 357e64833f2SRemy Horton n = strtol(q_arg, &end, 10); 358e64833f2SRemy Horton if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 359e64833f2SRemy Horton return -1; 360e64833f2SRemy Horton if (n >= MAX_TIMER_PERIOD) 361e64833f2SRemy Horton return -1; 362e64833f2SRemy Horton 363e64833f2SRemy Horton return n; 364e64833f2SRemy Horton } 365e64833f2SRemy Horton 366e64833f2SRemy Horton /* Parse the argument given in the command line of the application */ 367e64833f2SRemy Horton static int 368e64833f2SRemy Horton l2fwd_parse_args(int argc, char **argv) 369e64833f2SRemy Horton { 370e64833f2SRemy Horton int opt, ret; 371e64833f2SRemy Horton char **argvopt; 372e64833f2SRemy Horton int option_index; 373e64833f2SRemy Horton char *prgname = argv[0]; 374e64833f2SRemy Horton static struct option lgopts[] = { 375e64833f2SRemy Horton {NULL, 0, 0, 0} 376e64833f2SRemy Horton }; 377e64833f2SRemy Horton 378e64833f2SRemy Horton argvopt = argv; 379e64833f2SRemy Horton 380e64833f2SRemy Horton while ((opt = getopt_long(argc, argvopt, "p:q:T:K:", 381e64833f2SRemy Horton lgopts, &option_index)) != EOF) { 382e64833f2SRemy Horton 383e64833f2SRemy Horton switch (opt) { 384e64833f2SRemy Horton /* portmask */ 385e64833f2SRemy Horton case 'p': 386e64833f2SRemy Horton l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg); 387e64833f2SRemy Horton if (l2fwd_enabled_port_mask == 0) { 388e64833f2SRemy Horton printf("invalid portmask\n"); 389e64833f2SRemy Horton l2fwd_usage(prgname); 390e64833f2SRemy Horton return -1; 391e64833f2SRemy Horton } 392e64833f2SRemy Horton break; 393e64833f2SRemy Horton 394e64833f2SRemy Horton /* nqueue */ 395e64833f2SRemy Horton case 'q': 396e64833f2SRemy Horton l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg); 397e64833f2SRemy Horton if (l2fwd_rx_queue_per_lcore == 0) { 398e64833f2SRemy Horton printf("invalid queue number\n"); 399e64833f2SRemy Horton l2fwd_usage(prgname); 400e64833f2SRemy Horton return -1; 401e64833f2SRemy Horton } 402e64833f2SRemy Horton break; 403e64833f2SRemy Horton 404e64833f2SRemy Horton /* timer period */ 405e64833f2SRemy Horton case 'T': 406e64833f2SRemy Horton timer_period = l2fwd_parse_timer_period(optarg) 40783c27f59SRemy Horton * (int64_t)(1000 * TIMER_MILLISECOND); 408e64833f2SRemy Horton if (timer_period < 0) { 409e64833f2SRemy Horton printf("invalid timer period\n"); 410e64833f2SRemy Horton l2fwd_usage(prgname); 411e64833f2SRemy Horton return -1; 412e64833f2SRemy Horton } 413e64833f2SRemy Horton break; 414e64833f2SRemy Horton 415e64833f2SRemy Horton /* Check period */ 416e64833f2SRemy Horton case 'K': 417e64833f2SRemy Horton check_period = l2fwd_parse_check_period(optarg); 418e64833f2SRemy Horton if (check_period < 0) { 419e64833f2SRemy Horton printf("invalid check period\n"); 420e64833f2SRemy Horton l2fwd_usage(prgname); 421e64833f2SRemy Horton return -1; 422e64833f2SRemy Horton } 423e64833f2SRemy Horton break; 424e64833f2SRemy Horton 425e64833f2SRemy Horton /* long options */ 426e64833f2SRemy Horton case 0: 427e64833f2SRemy Horton l2fwd_usage(prgname); 428e64833f2SRemy Horton return -1; 429e64833f2SRemy Horton 430e64833f2SRemy Horton default: 431e64833f2SRemy Horton l2fwd_usage(prgname); 432e64833f2SRemy Horton return -1; 433e64833f2SRemy Horton } 434e64833f2SRemy Horton } 435e64833f2SRemy Horton 436e64833f2SRemy Horton if (optind >= 0) 437e64833f2SRemy Horton argv[optind-1] = prgname; 438e64833f2SRemy Horton 439e64833f2SRemy Horton ret = optind-1; 4409d5ca532SKeith Wiles optind = 1; /* reset getopt lib */ 441e64833f2SRemy Horton return ret; 442e64833f2SRemy Horton } 443e64833f2SRemy Horton 444e64833f2SRemy Horton /* Check the link status of all ports in up to 9s, and print them finally */ 445e64833f2SRemy Horton static void 4468728ccf3SThomas Monjalon check_all_ports_link_status(uint32_t port_mask) 447e64833f2SRemy Horton { 448e64833f2SRemy Horton #define CHECK_INTERVAL 100 /* 100ms */ 449e64833f2SRemy Horton #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ 450f8244c63SZhiyong Yang uint16_t portid; 451f8244c63SZhiyong Yang uint8_t count, all_ports_up, print_flag = 0; 452e64833f2SRemy Horton struct rte_eth_link link; 453e64833f2SRemy Horton 454e64833f2SRemy Horton printf("\nChecking link status"); 455e64833f2SRemy Horton fflush(stdout); 456e64833f2SRemy Horton for (count = 0; count <= MAX_CHECK_TIME; count++) { 457e64833f2SRemy Horton all_ports_up = 1; 4588728ccf3SThomas Monjalon RTE_ETH_FOREACH_DEV(portid) { 459e64833f2SRemy Horton if ((port_mask & (1 << portid)) == 0) 460e64833f2SRemy Horton continue; 461e64833f2SRemy Horton memset(&link, 0, sizeof(link)); 462e64833f2SRemy Horton rte_eth_link_get_nowait(portid, &link); 463e64833f2SRemy Horton /* print link status if flag set */ 464e64833f2SRemy Horton if (print_flag == 1) { 465e64833f2SRemy Horton if (link.link_status) 466f8244c63SZhiyong Yang printf( 467f8244c63SZhiyong Yang "Port%d Link Up. Speed %u Mbps - %s\n", 468f8244c63SZhiyong Yang portid, link.link_speed, 469e64833f2SRemy Horton (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? 470e64833f2SRemy Horton ("full-duplex") : ("half-duplex\n")); 471e64833f2SRemy Horton else 472f8244c63SZhiyong Yang printf("Port %d Link Down\n", portid); 473e64833f2SRemy Horton continue; 474e64833f2SRemy Horton } 475e64833f2SRemy Horton /* clear all_ports_up flag if any link down */ 47609419f23SThomas Monjalon if (link.link_status == ETH_LINK_DOWN) { 477e64833f2SRemy Horton all_ports_up = 0; 478e64833f2SRemy Horton break; 479e64833f2SRemy Horton } 480e64833f2SRemy Horton } 481e64833f2SRemy Horton /* after finally printing all link status, get out */ 482e64833f2SRemy Horton if (print_flag == 1) 483e64833f2SRemy Horton break; 484e64833f2SRemy Horton 485e64833f2SRemy Horton if (all_ports_up == 0) { 486e64833f2SRemy Horton printf("."); 487e64833f2SRemy Horton fflush(stdout); 488e64833f2SRemy Horton rte_delay_ms(CHECK_INTERVAL); 489e64833f2SRemy Horton } 490e64833f2SRemy Horton 491e64833f2SRemy Horton /* set the print_flag if all ports up or timeout */ 492e64833f2SRemy Horton if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) { 493e64833f2SRemy Horton print_flag = 1; 494e64833f2SRemy Horton printf("done\n"); 495e64833f2SRemy Horton } 496e64833f2SRemy Horton } 497e64833f2SRemy Horton } 498e64833f2SRemy Horton 499e64833f2SRemy Horton static void 5007b2a704cSRemy Horton dead_core(__rte_unused void *ptr_data, const int id_core) 501e64833f2SRemy Horton { 50291e89e47SRemy Horton if (terminate_signal_received) 50391e89e47SRemy Horton return; 504e64833f2SRemy Horton printf("Dead core %i - restarting..\n", id_core); 505e64833f2SRemy Horton if (rte_eal_get_lcore_state(id_core) == FINISHED) { 506e64833f2SRemy Horton rte_eal_wait_lcore(id_core); 507e64833f2SRemy Horton rte_eal_remote_launch(l2fwd_launch_one_lcore, NULL, id_core); 508e64833f2SRemy Horton } else { 509e64833f2SRemy Horton printf("..false positive!\n"); 510e64833f2SRemy Horton } 511e64833f2SRemy Horton } 512e64833f2SRemy Horton 5137b2a704cSRemy Horton static void 5147b2a704cSRemy Horton relay_core_state(void *ptr_data, const int id_core, 5157b2a704cSRemy Horton const enum rte_keepalive_state core_state, uint64_t last_alive) 5167b2a704cSRemy Horton { 5177b2a704cSRemy Horton rte_keepalive_relayed_state((struct rte_keepalive_shm *)ptr_data, 5187b2a704cSRemy Horton id_core, core_state, last_alive); 5197b2a704cSRemy Horton } 5207b2a704cSRemy Horton 521e64833f2SRemy Horton int 522e64833f2SRemy Horton main(int argc, char **argv) 523e64833f2SRemy Horton { 524e64833f2SRemy Horton struct lcore_queue_conf *qconf; 525e64833f2SRemy Horton int ret; 526f8244c63SZhiyong Yang uint16_t nb_ports; 5278728ccf3SThomas Monjalon uint16_t nb_ports_available = 0; 528f8244c63SZhiyong Yang uint16_t portid, last_port; 529e64833f2SRemy Horton unsigned lcore_id, rx_lcore_id; 530e64833f2SRemy Horton unsigned nb_ports_in_mask = 0; 53191e89e47SRemy Horton struct sigaction signal_handler; 53293543923SRemy Horton struct rte_keepalive_shm *ka_shm; 53391e89e47SRemy Horton 53491e89e47SRemy Horton memset(&signal_handler, 0, sizeof(signal_handler)); 53591e89e47SRemy Horton terminate_signal_received = 0; 53691e89e47SRemy Horton signal_handler.sa_handler = &handle_sigterm; 53791e89e47SRemy Horton if (sigaction(SIGINT, &signal_handler, NULL) == -1 || 53891e89e47SRemy Horton sigaction(SIGTERM, &signal_handler, NULL) == -1) 53991e89e47SRemy Horton rte_exit(EXIT_FAILURE, "SIGNAL\n"); 54091e89e47SRemy Horton 541e64833f2SRemy Horton 542e64833f2SRemy Horton /* init EAL */ 543e64833f2SRemy Horton ret = rte_eal_init(argc, argv); 544e64833f2SRemy Horton if (ret < 0) 545e64833f2SRemy Horton rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); 546e64833f2SRemy Horton argc -= ret; 547e64833f2SRemy Horton argv += ret; 548e64833f2SRemy Horton 549e64833f2SRemy Horton l2fwd_enabled_port_mask = 0; 550e64833f2SRemy Horton 551e64833f2SRemy Horton /* parse application arguments (after the EAL ones) */ 552e64833f2SRemy Horton ret = l2fwd_parse_args(argc, argv); 553e64833f2SRemy Horton if (ret < 0) 554e64833f2SRemy Horton rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n"); 555e64833f2SRemy Horton 556e64833f2SRemy Horton /* create the mbuf pool */ 557e64833f2SRemy Horton l2fwd_pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", NB_MBUF, 32, 558e64833f2SRemy Horton 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); 559e64833f2SRemy Horton if (l2fwd_pktmbuf_pool == NULL) 560e64833f2SRemy Horton rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n"); 561e64833f2SRemy Horton 562d9a42a69SThomas Monjalon nb_ports = rte_eth_dev_count_avail(); 563e64833f2SRemy Horton if (nb_ports == 0) 564e64833f2SRemy Horton rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); 565e64833f2SRemy Horton 566e64833f2SRemy Horton /* reset l2fwd_dst_ports */ 567e64833f2SRemy Horton for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) 568e64833f2SRemy Horton l2fwd_dst_ports[portid] = 0; 569e64833f2SRemy Horton last_port = 0; 570e64833f2SRemy Horton 571e64833f2SRemy Horton /* 572e64833f2SRemy Horton * Each logical core is assigned a dedicated TX queue on each port. 573e64833f2SRemy Horton */ 5748728ccf3SThomas Monjalon RTE_ETH_FOREACH_DEV(portid) { 575e64833f2SRemy Horton /* skip ports that are not enabled */ 576e64833f2SRemy Horton if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) 577e64833f2SRemy Horton continue; 578e64833f2SRemy Horton 579e64833f2SRemy Horton if (nb_ports_in_mask % 2) { 580e64833f2SRemy Horton l2fwd_dst_ports[portid] = last_port; 581e64833f2SRemy Horton l2fwd_dst_ports[last_port] = portid; 582e64833f2SRemy Horton } else 583e64833f2SRemy Horton last_port = portid; 584e64833f2SRemy Horton 585e64833f2SRemy Horton nb_ports_in_mask++; 586e64833f2SRemy Horton } 587e64833f2SRemy Horton if (nb_ports_in_mask % 2) { 588e64833f2SRemy Horton printf("Notice: odd number of ports in portmask.\n"); 589e64833f2SRemy Horton l2fwd_dst_ports[last_port] = last_port; 590e64833f2SRemy Horton } 591e64833f2SRemy Horton 592e64833f2SRemy Horton rx_lcore_id = 1; 593e64833f2SRemy Horton qconf = NULL; 594e64833f2SRemy Horton 595e64833f2SRemy Horton /* Initialize the port/queue configuration of each logical core */ 5968728ccf3SThomas Monjalon RTE_ETH_FOREACH_DEV(portid) { 597e64833f2SRemy Horton /* skip ports that are not enabled */ 598e64833f2SRemy Horton if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) 599e64833f2SRemy Horton continue; 600e64833f2SRemy Horton 601e64833f2SRemy Horton /* get the lcore_id for this port */ 602e64833f2SRemy Horton while (rte_lcore_is_enabled(rx_lcore_id) == 0 || 603e64833f2SRemy Horton lcore_queue_conf[rx_lcore_id].n_rx_port == 604e64833f2SRemy Horton l2fwd_rx_queue_per_lcore) { 605e64833f2SRemy Horton rx_lcore_id++; 606e64833f2SRemy Horton if (rx_lcore_id >= RTE_MAX_LCORE) 607e64833f2SRemy Horton rte_exit(EXIT_FAILURE, "Not enough cores\n"); 608e64833f2SRemy Horton } 609e64833f2SRemy Horton 610e64833f2SRemy Horton if (qconf != &lcore_queue_conf[rx_lcore_id]) 611e64833f2SRemy Horton /* Assigned a new logical core in the loop above. */ 612e64833f2SRemy Horton qconf = &lcore_queue_conf[rx_lcore_id]; 613e64833f2SRemy Horton 614e64833f2SRemy Horton qconf->rx_port_list[qconf->n_rx_port] = portid; 615e64833f2SRemy Horton qconf->n_rx_port++; 616e64833f2SRemy Horton printf("Lcore %u: RX port %u\n", 617f8244c63SZhiyong Yang rx_lcore_id, portid); 618e64833f2SRemy Horton } 619e64833f2SRemy Horton 620e64833f2SRemy Horton /* Initialise each port */ 6218728ccf3SThomas Monjalon RTE_ETH_FOREACH_DEV(portid) { 622563e239bSShahaf Shuler struct rte_eth_dev_info dev_info; 623563e239bSShahaf Shuler struct rte_eth_rxconf rxq_conf; 624563e239bSShahaf Shuler struct rte_eth_txconf txq_conf; 625563e239bSShahaf Shuler struct rte_eth_conf local_port_conf = port_conf; 626563e239bSShahaf Shuler 627e64833f2SRemy Horton /* skip ports that are not enabled */ 628e64833f2SRemy Horton if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) { 629f8244c63SZhiyong Yang printf("Skipping disabled port %u\n", portid); 630e64833f2SRemy Horton continue; 631e64833f2SRemy Horton } 6328728ccf3SThomas Monjalon nb_ports_available++; 6338728ccf3SThomas Monjalon 634e64833f2SRemy Horton /* init port */ 635f8244c63SZhiyong Yang printf("Initializing port %u... ", portid); 636e64833f2SRemy Horton fflush(stdout); 637563e239bSShahaf Shuler rte_eth_dev_info_get(portid, &dev_info); 638563e239bSShahaf Shuler if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) 639563e239bSShahaf Shuler local_port_conf.txmode.offloads |= 640563e239bSShahaf Shuler DEV_TX_OFFLOAD_MBUF_FAST_FREE; 641563e239bSShahaf Shuler ret = rte_eth_dev_configure(portid, 1, 1, &local_port_conf); 642e64833f2SRemy Horton if (ret < 0) 643e64833f2SRemy Horton rte_exit(EXIT_FAILURE, 644e64833f2SRemy Horton "Cannot configure device: err=%d, port=%u\n", 645f8244c63SZhiyong Yang ret, portid); 646e64833f2SRemy Horton 64760efb44fSRoman Zhukov ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd, 64860efb44fSRoman Zhukov &nb_txd); 64960efb44fSRoman Zhukov if (ret < 0) 65060efb44fSRoman Zhukov rte_exit(EXIT_FAILURE, 65160efb44fSRoman Zhukov "Cannot adjust number of descriptors: err=%d, port=%u\n", 652f8244c63SZhiyong Yang ret, portid); 65360efb44fSRoman Zhukov 654e64833f2SRemy Horton rte_eth_macaddr_get(portid, &l2fwd_ports_eth_addr[portid]); 655e64833f2SRemy Horton 656e64833f2SRemy Horton /* init one RX queue */ 657e64833f2SRemy Horton fflush(stdout); 658563e239bSShahaf Shuler rxq_conf = dev_info.default_rxconf; 659563e239bSShahaf Shuler rxq_conf.offloads = local_port_conf.rxmode.offloads; 660e64833f2SRemy Horton ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd, 661e64833f2SRemy Horton rte_eth_dev_socket_id(portid), 662563e239bSShahaf Shuler &rxq_conf, 663e64833f2SRemy Horton l2fwd_pktmbuf_pool); 664e64833f2SRemy Horton if (ret < 0) 665e64833f2SRemy Horton rte_exit(EXIT_FAILURE, 666e64833f2SRemy Horton "rte_eth_rx_queue_setup:err=%d, port=%u\n", 667f8244c63SZhiyong Yang ret, portid); 668e64833f2SRemy Horton 669e64833f2SRemy Horton /* init one TX queue on each port */ 670e64833f2SRemy Horton fflush(stdout); 671563e239bSShahaf Shuler txq_conf = dev_info.default_txconf; 672563e239bSShahaf Shuler txq_conf.offloads = local_port_conf.txmode.offloads; 673e64833f2SRemy Horton ret = rte_eth_tx_queue_setup(portid, 0, nb_txd, 674e64833f2SRemy Horton rte_eth_dev_socket_id(portid), 675563e239bSShahaf Shuler &txq_conf); 676e64833f2SRemy Horton if (ret < 0) 677e64833f2SRemy Horton rte_exit(EXIT_FAILURE, 678e64833f2SRemy Horton "rte_eth_tx_queue_setup:err=%d, port=%u\n", 679f8244c63SZhiyong Yang ret, portid); 680e64833f2SRemy Horton 681e2366e74STomasz Kulasek /* Initialize TX buffers */ 682e2366e74STomasz Kulasek tx_buffer[portid] = rte_zmalloc_socket("tx_buffer", 683e2366e74STomasz Kulasek RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0, 684e2366e74STomasz Kulasek rte_eth_dev_socket_id(portid)); 685e2366e74STomasz Kulasek if (tx_buffer[portid] == NULL) 686e2366e74STomasz Kulasek rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx on port %u\n", 687f8244c63SZhiyong Yang portid); 688e2366e74STomasz Kulasek 689e2366e74STomasz Kulasek rte_eth_tx_buffer_init(tx_buffer[portid], MAX_PKT_BURST); 690e2366e74STomasz Kulasek 691e2366e74STomasz Kulasek ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[portid], 692e2366e74STomasz Kulasek rte_eth_tx_buffer_count_callback, 693e2366e74STomasz Kulasek &port_statistics[portid].dropped); 694e2366e74STomasz Kulasek if (ret < 0) 695f8244c63SZhiyong Yang rte_exit(EXIT_FAILURE, 696f8244c63SZhiyong Yang "Cannot set error callback for tx buffer on port %u\n", 697f8244c63SZhiyong Yang portid); 698e2366e74STomasz Kulasek 699e64833f2SRemy Horton /* Start device */ 700e64833f2SRemy Horton ret = rte_eth_dev_start(portid); 701e64833f2SRemy Horton if (ret < 0) 702e64833f2SRemy Horton rte_exit(EXIT_FAILURE, 703e64833f2SRemy Horton "rte_eth_dev_start:err=%d, port=%u\n", 704f8244c63SZhiyong Yang ret, portid); 705e64833f2SRemy Horton 706e64833f2SRemy Horton rte_eth_promiscuous_enable(portid); 707e64833f2SRemy Horton 708e64833f2SRemy Horton printf("Port %u, MAC address: " 709e64833f2SRemy Horton "%02X:%02X:%02X:%02X:%02X:%02X\n\n", 710f8244c63SZhiyong Yang portid, 711e64833f2SRemy Horton l2fwd_ports_eth_addr[portid].addr_bytes[0], 712e64833f2SRemy Horton l2fwd_ports_eth_addr[portid].addr_bytes[1], 713e64833f2SRemy Horton l2fwd_ports_eth_addr[portid].addr_bytes[2], 714e64833f2SRemy Horton l2fwd_ports_eth_addr[portid].addr_bytes[3], 715e64833f2SRemy Horton l2fwd_ports_eth_addr[portid].addr_bytes[4], 716e64833f2SRemy Horton l2fwd_ports_eth_addr[portid].addr_bytes[5]); 717e64833f2SRemy Horton 718e64833f2SRemy Horton /* initialize port stats */ 719e64833f2SRemy Horton memset(&port_statistics, 0, sizeof(port_statistics)); 720e64833f2SRemy Horton } 721e64833f2SRemy Horton 722e64833f2SRemy Horton if (!nb_ports_available) { 723e64833f2SRemy Horton rte_exit(EXIT_FAILURE, 724e64833f2SRemy Horton "All available ports are disabled. Please set portmask.\n"); 725e64833f2SRemy Horton } 726e64833f2SRemy Horton 7278728ccf3SThomas Monjalon check_all_ports_link_status(l2fwd_enabled_port_mask); 728e64833f2SRemy Horton 729e64833f2SRemy Horton struct rte_timer hb_timer, stats_timer; 730e64833f2SRemy Horton 731e64833f2SRemy Horton rte_timer_subsystem_init(); 732e64833f2SRemy Horton rte_timer_init(&stats_timer); 733e64833f2SRemy Horton 73493543923SRemy Horton ka_shm = NULL; 735e64833f2SRemy Horton if (check_period > 0) { 7367b2a704cSRemy Horton ka_shm = rte_keepalive_shm_create(); 7377b2a704cSRemy Horton if (ka_shm == NULL) 7387b2a704cSRemy Horton rte_exit(EXIT_FAILURE, 7397b2a704cSRemy Horton "rte_keepalive_shm_create() failed"); 740e64833f2SRemy Horton rte_global_keepalive_info = 7417b2a704cSRemy Horton rte_keepalive_create(&dead_core, ka_shm); 742e64833f2SRemy Horton if (rte_global_keepalive_info == NULL) 743e64833f2SRemy Horton rte_exit(EXIT_FAILURE, "init_keep_alive() failed"); 7447b2a704cSRemy Horton rte_keepalive_register_relay_callback(rte_global_keepalive_info, 7457b2a704cSRemy Horton relay_core_state, ka_shm); 746e64833f2SRemy Horton rte_timer_init(&hb_timer); 747e64833f2SRemy Horton if (rte_timer_reset(&hb_timer, 748e64833f2SRemy Horton (check_period * rte_get_timer_hz()) / 1000, 749e64833f2SRemy Horton PERIODICAL, 750e64833f2SRemy Horton rte_lcore_id(), 751e64833f2SRemy Horton (void(*)(struct rte_timer*, void*)) 752e64833f2SRemy Horton &rte_keepalive_dispatch_pings, 753e64833f2SRemy Horton rte_global_keepalive_info 754e64833f2SRemy Horton ) != 0 ) 755e64833f2SRemy Horton rte_exit(EXIT_FAILURE, "Keepalive setup failure.\n"); 756e64833f2SRemy Horton } 757e64833f2SRemy Horton if (timer_period > 0) { 758e64833f2SRemy Horton if (rte_timer_reset(&stats_timer, 759e64833f2SRemy Horton (timer_period * rte_get_timer_hz()) / 1000, 760e64833f2SRemy Horton PERIODICAL, 761e64833f2SRemy Horton rte_lcore_id(), 762e64833f2SRemy Horton &print_stats, NULL 763e64833f2SRemy Horton ) != 0 ) 764e64833f2SRemy Horton rte_exit(EXIT_FAILURE, "Stats setup failure.\n"); 765e64833f2SRemy Horton } 766e64833f2SRemy Horton /* launch per-lcore init on every slave lcore */ 767e64833f2SRemy Horton RTE_LCORE_FOREACH_SLAVE(lcore_id) { 768e64833f2SRemy Horton struct lcore_queue_conf *qconf = &lcore_queue_conf[lcore_id]; 769e64833f2SRemy Horton 770e64833f2SRemy Horton if (qconf->n_rx_port == 0) 771e64833f2SRemy Horton RTE_LOG(INFO, L2FWD, 772e64833f2SRemy Horton "lcore %u has nothing to do\n", 773e64833f2SRemy Horton lcore_id 774e64833f2SRemy Horton ); 775e64833f2SRemy Horton else { 776e64833f2SRemy Horton rte_eal_remote_launch( 777e64833f2SRemy Horton l2fwd_launch_one_lcore, 778e64833f2SRemy Horton NULL, 779e64833f2SRemy Horton lcore_id 780e64833f2SRemy Horton ); 781e64833f2SRemy Horton rte_keepalive_register_core(rte_global_keepalive_info, 782e64833f2SRemy Horton lcore_id); 783e64833f2SRemy Horton } 784e64833f2SRemy Horton } 78591e89e47SRemy Horton while (!terminate_signal_received) { 786e64833f2SRemy Horton rte_timer_manage(); 787e64833f2SRemy Horton rte_delay_ms(5); 788e64833f2SRemy Horton } 789e64833f2SRemy Horton 790e64833f2SRemy Horton RTE_LCORE_FOREACH_SLAVE(lcore_id) { 791e64833f2SRemy Horton if (rte_eal_wait_lcore(lcore_id) < 0) 792e64833f2SRemy Horton return -1; 793e64833f2SRemy Horton } 794e64833f2SRemy Horton 79593543923SRemy Horton if (ka_shm != NULL) 79693543923SRemy Horton rte_keepalive_shm_cleanup(ka_shm); 797e64833f2SRemy Horton return 0; 798e64833f2SRemy Horton } 799