#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <netinet/in.h>
#include <setjmp.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#define RTE_LOGTYPE_LSI RTE_LOGTYPE_USER1
#define NB_MBUF   8192
#define MAX_PKT_BURST 32
#define BURST_TX_DRAIN_US 100 
#define RTE_TEST_RX_DESC_DEFAULT 1024
#define RTE_TEST_TX_DESC_DEFAULT 1024
static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
static struct ether_addr lsi_ports_eth_addr[RTE_MAX_ETHPORTS];
 
static uint32_t lsi_enabled_port_mask = 0;
static unsigned int lsi_rx_queue_per_lcore = 1;
static unsigned lsi_dst_ports[RTE_MAX_ETHPORTS] = {0};
#define MAX_PKT_BURST 32
#define MAX_RX_QUEUE_PER_LCORE 16
#define MAX_TX_QUEUE_PER_PORT 16
struct lcore_queue_conf {
    unsigned n_rx_port;
    unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE];
    unsigned tx_queue_id;
struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE];
    },
    .txmode = {
    },
    .intr_conf = {
        .lsc = 1, 
    },
};
struct lsi_port_statistics {
    uint64_t tx;
    uint64_t rx;
    uint64_t dropped;
struct lsi_port_statistics port_statistics[RTE_MAX_ETHPORTS];
#define TIMER_MILLISECOND 2000000ULL 
#define MAX_TIMER_PERIOD 86400 
static int64_t timer_period = 10 * TIMER_MILLISECOND * 1000; 
static void
print_stats(void)
{
    uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
    uint16_t portid;
    total_packets_dropped = 0;
    total_packets_tx = 0;
    total_packets_rx = 0;
    const char clr[] = { 27, '[', '2', 'J', '\0' };
    const char topLeft[] = { 27, '[', '1', ';', '1', 'H','\0' };
        
    printf("%s%s", clr, topLeft);
    printf("\nPort statistics ====================================");
    for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
        
        if ((lsi_enabled_port_mask & (1 << portid)) == 0)
            continue;
        memset(&link, 0, sizeof(link));
        printf("\nStatistics for port %u ------------------------------"
               "\nLink status: %25s"
               "\nLink speed: %26u"
               "\nLink duplex: %25s"
               "\nPackets sent: %24"PRIu64
               "\nPackets received: %20"PRIu64
               "\nPackets dropped: %21"PRIu64,
               portid,
               (link.link_status ? "Link up" : "Link down"),
               (unsigned)link.link_speed,
                    "full-duplex" : "half-duplex"),
               port_statistics[portid].tx,
               port_statistics[portid].rx,
               port_statistics[portid].dropped);
        total_packets_dropped += port_statistics[portid].dropped;
        total_packets_tx += port_statistics[portid].tx;
        total_packets_rx += port_statistics[portid].rx;
    }
    printf("\nAggregate statistics ==============================="
           "\nTotal packets sent: %18"PRIu64
           "\nTotal packets received: %14"PRIu64
           "\nTotal packets dropped: %15"PRIu64,
           total_packets_tx,
           total_packets_rx,
           total_packets_dropped);
    printf("\n====================================================\n");
    fflush(stdout);
}
static void
lsi_simple_forward(
struct rte_mbuf *m, 
unsigned portid)
{
    void *tmp;
    unsigned dst_port = lsi_dst_ports[portid];
    int sent;
    
    *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
    
    buffer = tx_buffer[dst_port];
    if (sent)
        port_statistics[dst_port].tx += sent;
}
static void
lsi_main_loop(void)
{
    struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
 
    unsigned lcore_id;
    unsigned sent;
    uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc;
    unsigned i, j, portid, nb_rx;
    struct lcore_queue_conf *qconf;
    const uint64_t drain_tsc = (
rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S *
 
            BURST_TX_DRAIN_US;
    prev_tsc = 0;
    timer_tsc = 0;
    qconf = &lcore_queue_conf[lcore_id];
    if (qconf->n_rx_port == 0) {
        RTE_LOG(INFO, LSI, 
"lcore %u has nothing to do\n", lcore_id);
 
        return;
    }
    RTE_LOG(INFO, LSI, 
"entering main loop on lcore %u\n", lcore_id);
 
    for (i = 0; i < qconf->n_rx_port; i++) {
        portid = qconf->rx_port_list[i];
        RTE_LOG(INFO, LSI, 
" -- lcoreid=%u portid=%u\n", lcore_id,
 
            portid);
    }
    while (1) {
        cur_tsc = rte_rdtsc();
        
        diff_tsc = cur_tsc - prev_tsc;
            for (i = 0; i < qconf->n_rx_port; i++) {
                portid = lsi_dst_ports[qconf->rx_port_list[i]];
                buffer = tx_buffer[portid];
                if (sent)
                    port_statistics[portid].tx += sent;
            }
            
            if (timer_period > 0) {
                
                timer_tsc += diff_tsc;
                
                if (
unlikely(timer_tsc >= (uint64_t) timer_period)) {
 
                    
                        print_stats();
                        
                        timer_tsc = 0;
                    }
                }
            }
            prev_tsc = cur_tsc;
        }
        
        for (i = 0; i < qconf->n_rx_port; i++) {
            portid = qconf->rx_port_list[i];
                         pkts_burst, MAX_PKT_BURST);
            port_statistics[portid].rx += nb_rx;
            for (j = 0; j < nb_rx; j++) {
                m = pkts_burst[j];
                lsi_simple_forward(m, portid);
            }
        }
    }
}
static int
lsi_launch_one_lcore(__attribute__((unused)) void *dummy)
{
    lsi_main_loop();
    return 0;
}
static void
lsi_usage(const char *prgname)
{
    printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
        "  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
        "  -q NQ: number of queue (=ports) per lcore (default is 1)\n"
        "  -T PERIOD: statistics will be refreshed each PERIOD seconds (0 to disable, 10 default, 86400 maximum)\n",
            prgname);
}
static int
lsi_parse_portmask(const char *portmask)
{
    char *end = NULL;
    unsigned long pm;
    
    pm = strtoul(portmask, &end, 16);
    if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
        return -1;
    if (pm == 0)
        return -1;
    return pm;
}
static unsigned int
lsi_parse_nqueue(const char *q_arg)
{
    char *end = NULL;
    unsigned long n;
    
    n = strtoul(q_arg, &end, 10);
    if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
        return 0;
    if (n == 0)
        return 0;
    if (n >= MAX_RX_QUEUE_PER_LCORE)
        return 0;
    return n;
}
static int
lsi_parse_timer_period(const char *q_arg)
{
    char *end = NULL;
    int n;
    
    n = strtol(q_arg, &end, 10);
    if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
        return -1;
    if (n >= MAX_TIMER_PERIOD)
        return -1;
    return n;
}
static int
lsi_parse_args(int argc, char **argv)
{
    int opt, ret;
    char **argvopt;
    int option_index;
    char *prgname = argv[0];
    static struct option lgopts[] = {
        {NULL, 0, 0, 0}
    };
    argvopt = argv;
    while ((opt = getopt_long(argc, argvopt, "p:q:T:",
                  lgopts, &option_index)) != EOF) {
        switch (opt) {
        
        case 'p':
            lsi_enabled_port_mask = lsi_parse_portmask(optarg);
            if (lsi_enabled_port_mask == 0) {
                printf("invalid portmask\n");
                lsi_usage(prgname);
                return -1;
            }
            break;
        
        case 'q':
            lsi_rx_queue_per_lcore = lsi_parse_nqueue(optarg);
            if (lsi_rx_queue_per_lcore == 0) {
                printf("invalid queue number\n");
                lsi_usage(prgname);
                return -1;
            }
            break;
        
        case 'T':
            timer_period = lsi_parse_timer_period(optarg) * 1000 * TIMER_MILLISECOND;
            if (timer_period < 0) {
                printf("invalid timer period\n");
                lsi_usage(prgname);
                return -1;
            }
            break;
        
        case 0:
            lsi_usage(prgname);
            return -1;
        default:
            lsi_usage(prgname);
            return -1;
        }
    }
    if (optind >= 0)
        argv[optind-1] = prgname;
    ret = optind-1;
    optind = 1; 
    return ret;
}
static int
            void *ret_param)
{
    printf("\n\nIn registered callback...\n");
    if (link.link_status) {
        printf("Port %d Link Up - speed %u Mbps - %s\n\n",
                port_id, (unsigned)link.link_speed,
                ("full-duplex") : ("half-duplex"));
    } else
        printf("Port %d Link Down\n\n", port_id);
    return 0;
}
static void
check_all_ports_link_status(uint16_t port_num, uint32_t port_mask)
{
#define CHECK_INTERVAL 100 
#define MAX_CHECK_TIME 90 
    uint8_t count, all_ports_up, print_flag = 0;
    uint16_t portid;
    printf("\nChecking link status");
    fflush(stdout);
    for (count = 0; count <= MAX_CHECK_TIME; count++) {
        all_ports_up = 1;
        for (portid = 0; portid < port_num; portid++) {
            if ((port_mask & (1 << portid)) == 0)
                continue;
            memset(&link, 0, sizeof(link));
            
            if (print_flag == 1) {
                if (link.link_status)
                    printf(
                    "Port%d Link Up. Speed %u Mbps - %s\n",
                        portid, link.link_speed,
                    ("full-duplex") : ("half-duplex"));
                else
                    printf("Port %d Link Down\n", portid);
                continue;
            }
            
                all_ports_up = 0;
                break;
            }
        }
        
        if (print_flag == 1)
            break;
        if (all_ports_up == 0) {
            printf(".");
            fflush(stdout);
        }
        
        if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
            print_flag = 1;
            printf("done\n");
        }
    }
}
int
main(int argc, char **argv)
{
    struct lcore_queue_conf *qconf;
    int ret;
    uint16_t nb_ports;
    uint16_t portid, portid_last = 0;
    unsigned lcore_id, rx_lcore_id;
    unsigned nb_ports_in_mask = 0;
    
    if (ret < 0)
        rte_exit(EXIT_FAILURE, 
"rte_eal_init failed");
 
    argc -= ret;
    argv += ret;
    
    ret = lsi_parse_args(argc, argv);
    if (ret < 0)
        rte_exit(EXIT_FAILURE, 
"Invalid arguments");
 
    
    lsi_pktmbuf_pool =
    if (lsi_pktmbuf_pool == NULL)
    if (nb_ports == 0)
    
    for (portid = 0; portid < nb_ports; portid++) {
        
        if ((lsi_enabled_port_mask & (1 << portid)) == 0)
            continue;
        
        if (nb_ports_in_mask % 2) {
            lsi_dst_ports[portid] = portid_last;
            lsi_dst_ports[portid_last] = portid;
        }
        else
            portid_last = portid;
        nb_ports_in_mask++;
    }
    if (nb_ports_in_mask < 2 || nb_ports_in_mask % 2)
        rte_exit(EXIT_FAILURE, 
"Current enabled port number is %u, " 
                "but it should be even and at least 2\n",
                nb_ports_in_mask);
    rx_lcore_id = 0;
    qconf = &lcore_queue_conf[rx_lcore_id];
    
    for (portid = 0; portid < nb_ports; portid++) {
        
        if ((lsi_enabled_port_mask & (1 << portid)) == 0)
            continue;
        
               lcore_queue_conf[rx_lcore_id].n_rx_port ==
               lsi_rx_queue_per_lcore) {
            rx_lcore_id++;
            if (rx_lcore_id >= RTE_MAX_LCORE)
                rte_exit(EXIT_FAILURE, 
"Not enough cores\n");
 
        }
        if (qconf != &lcore_queue_conf[rx_lcore_id])
            
            qconf = &lcore_queue_conf[rx_lcore_id];
        qconf->rx_port_list[qconf->n_rx_port] = portid;
        qconf->n_rx_port++;
        printf("Lcore %u: RX port %u\n",rx_lcore_id, (unsigned) portid);
    }
    
    for (portid = 0; portid < nb_ports; portid++) {
        
        if ((lsi_enabled_port_mask & (1 << portid)) == 0) {
            printf("Skipping disabled port %u\n", (unsigned) portid);
            continue;
        }
        
        printf("Initializing port %u... ", (unsigned) portid);
        fflush(stdout);
        if (ret < 0)
            rte_exit(EXIT_FAILURE, 
"Cannot configure device: err=%d, port=%u\n",
 
                  ret, (unsigned) portid);
                               &nb_txd);
        if (ret < 0)
                 "rte_eth_dev_adjust_nb_rx_tx_desc: err=%d, port=%u\n",
                 ret, (unsigned) portid);
        
                    &lsi_ports_eth_addr[portid]);
        
        fflush(stdout);
        rxq_conf = dev_info.default_rxconf;
                         &rxq_conf,
                         lsi_pktmbuf_pool);
        if (ret < 0)
            rte_exit(EXIT_FAILURE, 
"rte_eth_rx_queue_setup: err=%d, port=%u\n",
 
                  ret, (unsigned) portid);
        
        fflush(stdout);
        txq_conf = dev_info.default_txconf;
                &txq_conf);
        if (ret < 0)
            rte_exit(EXIT_FAILURE, 
"rte_eth_tx_queue_setup: err=%d,port=%u\n",
 
                  ret, (unsigned) portid);
        
        if (tx_buffer[portid] == NULL)
            rte_exit(EXIT_FAILURE, 
"Cannot allocate buffer for tx on port %u\n",
 
                    (unsigned) portid);
                &port_statistics[portid].dropped);
        if (ret < 0)
            rte_exit(EXIT_FAILURE, 
"Cannot set error callback for " 
                    "tx buffer on port %u\n", (unsigned) portid);
        
        if (ret < 0)
            rte_exit(EXIT_FAILURE, 
"rte_eth_dev_start: err=%d, port=%u\n",
 
                  ret, (unsigned) portid);
        printf("done:\n");
        printf("Port %u, MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n\n",
                (unsigned) portid,
                lsi_ports_eth_addr[portid].addr_bytes[0],
                lsi_ports_eth_addr[portid].addr_bytes[1],
                lsi_ports_eth_addr[portid].addr_bytes[2],
                lsi_ports_eth_addr[portid].addr_bytes[3],
                lsi_ports_eth_addr[portid].addr_bytes[4],
                lsi_ports_eth_addr[portid].addr_bytes[5]);
        
        memset(&port_statistics, 0, sizeof(port_statistics));
    }
    check_all_ports_link_status(nb_ports, lsi_enabled_port_mask);
    
            return -1;
    }
    return 0;
}