Hi Forrest,

I'm currently on vacation. I'll be back at the office on August 8th. 

-Matias

> On 25 Jul 2016, at 10:06, forrest.shi <[email protected]> wrote:
> 
> Hi Matias,
> 
> Do you have any further comment about this version?
> 
> Thanks,
> Forrest
> 
>> -----Original Message-----
>> From: [email protected] [mailto:[email protected]]
>> Sent: Wednesday, July 13, 2016 15:30
>> To: [email protected]
>> Cc: [email protected]; Xuelin Shi <[email protected]>
>> Subject: [lng-odp][PATCH 1/2 v6] example: introducing l3fwd
>> 
>> From: Xuelin Shi <[email protected]>
>> 
>> multi-thread, multi-queues and bi-directional forwarding.
>> 
>> support (port, queue, thread) arguments in cmdline which specify how the
>> threads handle which rx queue at which port, if no this argument, default
>> specification used.
>> 
>> both hash and lpm based lookup methods are supported, default lpm.
>> 
>> Signed-off-by: Xuelin Shi <[email protected]>
>> ---
>> example/Makefile.am           |    2 +-
>> example/l3fwd/.gitignore      |    4 +
>> example/l3fwd/Makefile.am     |   18 +
>> example/l3fwd/odp_l3fwd.c     | 1072
>> +++++++++++++++++++++++++++++++++++++++++
>> example/l3fwd/odp_l3fwd_db.c  |  408 ++++++++++++++++
>> example/l3fwd/odp_l3fwd_db.h  |  130 +++++
>> example/l3fwd/odp_l3fwd_lpm.c |  224 +++++++++
>> example/l3fwd/odp_l3fwd_lpm.h |   20 +
>> example/m4/configure.m4       |    1 +
>> 9 files changed, 1878 insertions(+), 1 deletion(-)  create mode 100644
>> example/l3fwd/.gitignore  create mode 100644 example/l3fwd/Makefile.am
>> create mode 100644 example/l3fwd/odp_l3fwd.c  create mode 100644
>> example/l3fwd/odp_l3fwd_db.c  create mode 100644
>> example/l3fwd/odp_l3fwd_db.h  create mode 100644
>> example/l3fwd/odp_l3fwd_lpm.c  create mode 100644
>> example/l3fwd/odp_l3fwd_lpm.h
>> 
>> diff --git a/example/Makefile.am b/example/Makefile.am index
>> 37542af..1f1b62e 100644
>> --- a/example/Makefile.am
>> +++ b/example/Makefile.am
>> @@ -1 +1 @@
>> -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt
>> l2fwd_simple switch hello
>> +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt
>> +l2fwd_simple switch hello l3fwd
>> diff --git a/example/l3fwd/.gitignore b/example/l3fwd/.gitignore new file
>> mode 100644 index 0000000..4a25ae8
>> --- /dev/null
>> +++ b/example/l3fwd/.gitignore
>> @@ -0,0 +1,4 @@
>> +odp_l3fwd
>> +Makefile
>> +Makefile.in
>> +*.o
>> diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am new
>> file mode 100644 index 0000000..5092aa7
>> --- /dev/null
>> +++ b/example/l3fwd/Makefile.am
>> @@ -0,0 +1,18 @@
>> +include $(top_srcdir)/example/Makefile.inc
>> +
>> +bin_PROGRAMS = odp_l3fwd$(EXEEXT)
>> +odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static odp_l3fwd_CFLAGS =
>> +$(AM_CFLAGS) -I${top_srcdir}/example -I${top_srcdir}/test
>> +
>> +noinst_HEADERS = \
>> +          $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \
>> +          $(top_srcdir)/example/l3fwd/odp_l3fwd_lpm.h \
>> +          $(top_srcdir)/example/example_debug.h
>> +
>> +dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c odp_l3fwd_lpm.c
>> +
>> +if test_example
>> +TESTS = odp_l3fwd_run.sh
>> +endif
>> +
>> +EXTRA_DIST = odp_l3fwd_run.sh
>> diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c new
>> file mode 100644 index 0000000..a10ca76
>> --- /dev/null
>> +++ b/example/l3fwd/odp_l3fwd.c
>> @@ -0,0 +1,1072 @@
>> +/* Copyright (c) 2016, Linaro Limited
>> + * All rights reserved.
>> + *
>> + * SPDX-License-Identifier:     BSD-3-Clause
>> + */
>> +
>> +#include <stdlib.h>
>> +#include <stdio.h>
>> +#include <errno.h>
>> +#include <getopt.h>
>> +#include <unistd.h>
>> +#include <inttypes.h>
>> +
>> +#include <test_debug.h>
>> +
>> +#include <odp_api.h>
>> +#include <odp/helper/linux.h>
>> +#include <odp/helper/eth.h>
>> +#include <odp/helper/ip.h>
>> +#include <odp/helper/udp.h>
>> +#include <odp/helper/tcp.h>
>> +
>> +#include "odp_l3fwd_db.h"
>> +#include "odp_l3fwd_lpm.h"
>> +
>> +#define POOL_NUM_PKT    8192
>> +#define POOL_SEG_LEN    1856
>> +#define MAX_PKT_BURST    32
>> +
>> +#define MAX_NB_WORKER    32
>> +#define MAX_NB_PKTIO    32
>> +#define MAX_NB_QUEUE    32
>> +#define MAX_NB_QCONFS    1024
>> +#define MAX_NB_ROUTE    32
>> +
>> +#define INVALID_ID    (-1)
>> +#define PRINT_INTERVAL    10    /* interval seconds of printing stats
> */
>> +
>> +/** Get rid of path in filename - only for unix-type paths using '/' */
>> +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
>> +                strrchr((file_name), '/') + 1 : (file_name))
>> +
>> +struct l3fwd_pktio_s {
>> +    odp_pktio_t pktio;
>> +    odph_ethaddr_t mac_addr;
>> +    odp_pktin_queue_t ifin[MAX_NB_QUEUE];
>> +    odp_pktout_queue_t ifout[MAX_NB_QUEUE];
>> +    int nb_rxq;
>> +    int nb_txq;
>> +    int rxq_idx;
>> +    int txq_idx;
>> +};
>> +
>> +struct l3fwd_qconf_s {
>> +    uint8_t if_idx;        /* port index */
>> +    uint8_t rxq_idx;    /* recv queue index in a port */
>> +    uint8_t core_idx;    /* this core should handle traffic */
>> +};
>> +
>> +struct thread_arg_s {
>> +    uint64_t packets;
>> +    uint64_t rx_drops;
>> +    uint64_t tx_drops;
>> +    struct {
>> +        int used;
>> +        int rxq[MAX_NB_QUEUE];
>> +        int txq[MAX_NB_QUEUE];
>> +    } pktio[MAX_NB_PKTIO];
>> +    int thr_idx;
>> +    int nb_pktio;
>> +};
>> +
>> +typedef struct {
>> +    char *if_names[MAX_NB_PKTIO];
>> +    int if_count;
>> +    char *route_str[MAX_NB_ROUTE];
>> +    int worker_count;
>> +    struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS];
>> +    int qconf_count;
>> +    uint32_t duration; /* seconds to run */
>> +    uint8_t hash_mode; /* 1:hash, 0:lpm */
>> +    uint8_t dest_mac_changed[MAX_NB_PKTIO]; /* 1: dest mac from
>> cmdline */
>> +} app_args_t;
>> +
>> +struct {
>> +    app_args_t        cmd_args;
>> +    struct l3fwd_pktio_s    l3fwd_pktios[MAX_NB_PKTIO];
>> +    odph_odpthread_t    l3fwd_workers[MAX_NB_WORKER];
>> +    struct thread_arg_s    worker_args[MAX_NB_WORKER];
>> +    odph_ethaddr_t        eth_dest_mac[MAX_NB_PKTIO];
>> +
>> +    /* forward func, hash or lpm */
>> +    int (*fwd_func)(odp_packet_t pkt, int sif); } global;
>> +
>> +/** Global barrier to synchronize main and workers */ static
>> +odp_barrier_t barrier;
>> +static int exit_threads;    /**< Break workers loop if set to 1 */
>> +
>> +static void print_usage(char *progname); static void
>> +print_qconf_table(app_args_t *args); static void print_info(char
>> +*progname, app_args_t *args); static int print_speed_stats(int
>> +num_workers, int duration, int timeout); static void
>> +parse_cmdline_args(int argc, char *argv[], app_args_t *args); static
>> +int parse_config(char *cfg_str, app_args_t *args); static void
>> +setup_worker_qconf(app_args_t *args); static void setup_fwd_db(void);
>> +static int find_port_id_by_name(char *name, app_args_t *args); static
>> +int split_string(char *str, int stringlen,
>> +            char **tokens, int maxtokens, char delim);
>> +
>> +static int create_pktio(const char *name, odp_pool_t pool,
>> +            struct l3fwd_pktio_s *fwd_pktio)
>> +{
>> +    odp_pktio_param_t pktio_param;
>> +    odp_pktio_t pktio;
>> +    odp_pktio_capability_t capa;
>> +    int rc;
>> +
>> +    odp_pktio_param_init(&pktio_param);
>> +
>> +    pktio = odp_pktio_open(name, pool, &pktio_param);
>> +    if (pktio == ODP_PKTIO_INVALID) {
>> +        printf("Failed to open %s\n", name);
>> +        return -1;
>> +    }
>> +    fwd_pktio->pktio = pktio;
>> +
>> +    rc = odp_pktio_capability(pktio, &capa);
>> +    if (rc) {
>> +        printf("Error: pktio %s: unable to read capabilities!\n",
>> +               name);
>> +
>> +        return -1;
>> +    }
>> +
>> +    fwd_pktio->nb_rxq = (int)capa.max_input_queues;
>> +    fwd_pktio->nb_txq = (int)capa.max_output_queues;
>> +
>> +    if (fwd_pktio->nb_rxq > MAX_NB_QUEUE)
>> +        fwd_pktio->nb_rxq = MAX_NB_QUEUE;
>> +
>> +    if (fwd_pktio->nb_txq > MAX_NB_QUEUE)
>> +        fwd_pktio->nb_txq = MAX_NB_QUEUE;
>> +
>> +    return 0;
>> +}
>> +
>> +static void setup_fwd_db(void)
>> +{
>> +    fwd_db_entry_t *entry;
>> +    int if_idx;
>> +    app_args_t *args;
>> +
>> +    args = &global.cmd_args;
>> +    if (args->hash_mode)
>> +        init_fwd_hash_cache();
>> +    else
>> +        fib_tbl_init();
>> +
>> +    for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
>> +        if_idx = entry->oif_id;
>> +        if (!args->hash_mode)
>> +            fib_tbl_insert(entry->subnet.addr, if_idx,
>> +                       entry->subnet.depth);
>> +        if (args->dest_mac_changed[if_idx])
>> +            global.eth_dest_mac[if_idx] = entry->dst_mac;
>> +        else
>> +            entry->dst_mac = global.eth_dest_mac[if_idx];
>> +    }
>> +}
>> +
>> +static int l3fwd_pkt_hash(odp_packet_t pkt, int sif) {
>> +    fwd_db_entry_t *entry;
>> +    ipv4_tuple5_t key;
>> +    odph_ethhdr_t *eth;
>> +    odph_udphdr_t  *udp;
>> +    odph_ipv4hdr_t *ip;
>> +    uint32_t len;
>> +    int dif;
>> +
>> +    ip = odp_packet_l3_ptr(pkt, &len);
>> +    key.dst_ip = odp_be_to_cpu_32(ip->dst_addr);
>> +    key.src_ip = odp_be_to_cpu_32(ip->src_addr);
>> +    key.proto = ip->proto;
>> +
>> +    if (odp_packet_has_udp(pkt) ||
>> +        odp_packet_has_tcp(pkt)) {
>> +        /* UDP or TCP*/
>> +        void *ptr = odp_packet_l4_ptr(pkt, NULL);
>> +
>> +        udp = (odph_udphdr_t *)ptr;
>> +        key.src_port = odp_be_to_cpu_16(udp->src_port);
>> +        key.dst_port = odp_be_to_cpu_16(udp->dst_port);
>> +    } else {
>> +        key.src_port = 0;
>> +        key.dst_port = 0;
>> +    }
>> +
>> +    entry = find_fwd_db_entry(&key);
>> +    ip->ttl--;
>> +    ip->chksum = odph_ipv4_csum_update(pkt);
>> +    eth = odp_packet_l2_ptr(pkt, NULL);
>> +    if (entry) {
>> +        eth->src = entry->src_mac;
>> +        eth->dst = entry->dst_mac;
>> +        dif = entry->oif_id;
>> +    } else {
>> +        /* no route, send by src port */
>> +        eth->dst = eth->src;
>> +        dif = sif;
>> +    }
>> +
>> +    return dif;
>> +}
>> +
>> +static int l3fwd_pkt_lpm(odp_packet_t pkt, int sif) {
>> +    odph_ipv4hdr_t *ip;
>> +    odph_ethhdr_t *eth;
>> +    uint32_t len;
>> +    int dif;
>> +    int ret;
>> +
>> +    ip = odp_packet_l3_ptr(pkt, &len);
>> +    ip->ttl--;
>> +    ip->chksum = odph_ipv4_csum_update(pkt);
>> +    eth = odp_packet_l2_ptr(pkt, NULL);
>> +
>> +    /* network byte order maybe different from host */
>> +    ret = fib_tbl_lookup(odp_be_to_cpu_32(ip->dst_addr), &dif);
>> +    if (ret)
>> +        dif = sif;
>> +
>> +    eth->dst = global.eth_dest_mac[dif];
>> +    eth->src = global.l3fwd_pktios[dif].mac_addr;
>> +
>> +    return dif;
>> +}
>> +
>> +/**
>> + * Drop packets which input parsing marked as containing errors.
>> + *
>> + * Frees packets with error and modifies pkt_tbl[] to only contain
>> +packets with
>> + * no detected errors.
>> + *
>> + * @param pkt_tbl  Array of packets
>> + * @param num      Number of packets in pkt_tbl[]
>> + *
>> + * @return Number of packets dropped
>> + */
>> +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num) {
>> +    odp_packet_t pkt;
>> +    unsigned dropped = 0;
>> +    unsigned i, j;
>> +
>> +    for (i = 0, j = 0; i < num; ++i) {
>> +        pkt = pkt_tbl[i];
>> +
>> +        if (odp_unlikely(odp_packet_has_error(pkt) ||
>> +                 !odp_packet_has_ipv4(pkt))) {
>> +            odp_packet_free(pkt);
>> +            dropped++;
>> +        } else if (odp_unlikely(i != j++)) {
>> +            pkt_tbl[j - 1] = pkt;
>> +        }
>> +    }
>> +
>> +    return dropped;
>> +}
>> +
>> +static void l3fwd_one_queue(uint32_t sif, uint8_t rxq_id, void
>> +*thr_arg) {
>> +    struct l3fwd_pktio_s *port;
>> +    odp_packet_t *tbl;
>> +    odp_pktout_queue_t outq;
>> +    odp_packet_t pkt_tbl[MAX_PKT_BURST];
>> +    struct thread_arg_s *arg;
>> +    uint8_t txq_id;
>> +    int pkts, drop, sent;
>> +    int dif, dst_port;
>> +    int i;
>> +
>> +    arg = thr_arg;
>> +    port = &global.l3fwd_pktios[sif];
>> +    pkts = odp_pktin_recv(port->ifin[rxq_id], pkt_tbl, MAX_PKT_BURST);
>> +    if (pkts <= 0)
>> +        return;
>> +    arg->packets += pkts;
>> +    drop = drop_err_pkts(pkt_tbl, pkts);
>> +    pkts -= drop;
>> +    arg->rx_drops += drop;
>> +
>> +    dif = global.fwd_func(pkt_tbl[0], sif);
>> +    tbl = &pkt_tbl[0];
>> +    while (pkts) {
>> +        dst_port = dif;
>> +        for (i = 1; i < pkts; i++) {
>> +            dif = global.fwd_func(tbl[i], sif);
>> +            if (dif != dst_port)
>> +                break;
>> +        }
>> +        txq_id = arg->pktio[dst_port].txq[rxq_id];
>> +        outq = global.l3fwd_pktios[dst_port].ifout[txq_id];
>> +        sent = odp_pktout_send(outq, tbl, i);
>> +        if (odp_unlikely(sent < i)) {
>> +            sent = sent < 0 ? 0 : sent;
>> +            odp_packet_free_multi(&tbl[sent], i - sent);
>> +            arg->tx_drops += i - sent;
>> +        }
>> +
>> +        if (i < pkts)
>> +            tbl += i;
>> +
>> +        pkts -= i;
>> +    }
>> +}
>> +
>> +static int run_worker(void *arg)
>> +{
>> +    int if_idx, rxq_idx;
>> +    struct thread_arg_s *thr_arg = arg;
>> +    struct l3fwd_pktio_s *port;
>> +
>> +    odp_barrier_wait(&barrier);
>> +
>> +    while (!exit_threads) {
>> +        for (if_idx = 0; if_idx < thr_arg->nb_pktio; if_idx++) {
>> +            if (!thr_arg->pktio[if_idx].used ||
>> +                thr_arg->thr_idx == INVALID_ID)
>> +                continue;
>> +
>> +            port = &global.l3fwd_pktios[if_idx];
>> +            for (rxq_idx = 0; rxq_idx < port->rxq_idx; rxq_idx++)
>> +                l3fwd_one_queue(if_idx, rxq_idx, arg);
>> +        }
>> +    }
>> +
>> +    /* Make sure that latest stat writes are visible to other threads */
>> +    odp_mb_full();
>> +
>> +    return 0;
>> +}
>> +
>> +static int find_port_id_by_name(char *name, app_args_t *args) {
>> +    int i;
>> +
>> +    if (!name)
>> +        return -1;
>> +
>> +    for (i = 0; i < args->if_count; i++) {
>> +        if (!strcmp(name, args->if_names[i]))
>> +            return i;
>> +    }
>> +
>> +    return -1;
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> +    odph_odpthread_t thread_tbl[MAX_NB_WORKER];
>> +    odp_pool_t pool;
>> +    odp_pool_param_t params;
>> +    odp_instance_t instance;
>> +    odph_odpthread_params_t thr_params;
>> +    odp_cpumask_t cpumask;
>> +    int cpu, i, j, nb_worker;
>> +    uint8_t mac[ODPH_ETHADDR_LEN];
>> +    app_args_t *args;
>> +    struct thread_arg_s *thr_arg;
>> +    char *oif;
>> +    int oid;
>> +
>> +    if (odp_init_global(&instance, NULL, NULL)) {
>> +        printf("Error: ODP global init failed.\n");
>> +        exit(1);
>> +    }
>> +
>> +    if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
>> +        printf("Error: ODP local init failed.\n");
>> +        exit(1);
>> +    }
>> +
>> +    /* Clear global argument and initialize the dest mac as 2:0:0:0:0:x */
>> +    memset(&global, 0, sizeof(global));
>> +    mac[0] = 2;
>> +    for (i = 0; i < MAX_NB_PKTIO; i++) {
>> +        mac[ODPH_ETHADDR_LEN - 1] = (uint8_t)i;
>> +        memcpy(global.eth_dest_mac[i].addr, mac,
>> ODPH_ETHADDR_LEN);
>> +    }
>> +
>> +    /* Initialize the thread arguments */
>> +    for (i = 0; i < MAX_NB_WORKER; i++) {
>> +        thr_arg = &global.worker_args[i];
>> +        for (j = 0; j < MAX_NB_PKTIO; j++) {
>> +            thr_arg->thr_idx = INVALID_ID;
>> +            memset(thr_arg->pktio[j].rxq, INVALID_ID,
>> +                   sizeof(thr_arg->pktio[j].rxq));
>> +        }
>> +    }
>> +
>> +    /* Parse cmdline arguments */
>> +    args = &global.cmd_args;
>> +    parse_cmdline_args(argc, argv, args);
>> +
>> +    /* Init l3fwd table */
>> +    init_fwd_db();
>> +
>> +    /* Add route into table */
>> +    for (i = 0; i < MAX_NB_ROUTE; i++) {
>> +        if (args->route_str[i]) {
>> +            oif = NULL;
>> +            create_fwd_db_entry(args->route_str[i], &oif);
>> +            oid = find_port_id_by_name(oif, args);
>> +            if (oid != -1)
>> +                args->dest_mac_changed[oid] = 1;
>> +        }
>> +    }
>> +
>> +    print_info(NO_PATH(argv[0]), args);
>> +
>> +    /* Create packet pool */
>> +    odp_pool_param_init(&params);
>> +    params.pkt.seg_len = POOL_SEG_LEN;
>> +    params.pkt.len     = POOL_SEG_LEN;
>> +    params.pkt.num     = POOL_NUM_PKT;
>> +    params.type        = ODP_POOL_PACKET;
>> +
>> +    pool = odp_pool_create("packet pool", &params);
>> +
>> +    if (pool == ODP_POOL_INVALID) {
>> +        printf("Error: packet pool create failed.\n");
>> +        exit(1);
>> +    }
>> +
>> +    /* Resolve fwd db*/
>> +    for (i = 0; i < args->if_count; i++) {
>> +        struct l3fwd_pktio_s *port;
>> +        char *if_name;
>> +
>> +        if_name = args->if_names[i];
>> +        port = &global.l3fwd_pktios[i];
>> +        if (create_pktio(if_name, pool, port)) {
>> +            printf("Error: create pktio %s\n", if_name);
>> +            exit(1);
>> +        }
>> +        odp_pktio_mac_addr(port->pktio, mac, ODPH_ETHADDR_LEN);
>> +        resolve_fwd_db(if_name, i, mac);
>> +        memcpy(port->mac_addr.addr, mac, ODPH_ETHADDR_LEN);
>> +    }
>> +
>> +    setup_fwd_db();
>> +    dump_fwd_db();
>> +
>> +    /* Dicide available workers */
>> +    nb_worker = MAX_NB_WORKER;
>> +    if (args->worker_count)
>> +        nb_worker = args->worker_count;
>> +    nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker);
>> +    args->worker_count = nb_worker;
>> +
>> +    /* Setup rx and tx queues for each port */
>> +    setup_worker_qconf(args);
>> +    print_qconf_table(args);
>> +
>> +    /* Decide ip lookup method */
>> +    if (args->hash_mode)
>> +        global.fwd_func = l3fwd_pkt_hash;
>> +    else
>> +        global.fwd_func = l3fwd_pkt_lpm;
>> +
>> +    /* Start all the available ports */
>> +    for (i = 0; i < args->if_count; i++) {
>> +        struct l3fwd_pktio_s *port;
>> +        char *if_name;
>> +        char buf[32];
>> +
>> +        if_name = args->if_names[i];
>> +        port = &global.l3fwd_pktios[i];
>> +        /* start pktio */
>> +        if (odp_pktio_start(port->pktio)) {
>> +            printf("unable to start pktio: %s\n", if_name);
>> +            exit(1);
>> +        }
>> +
>> +        sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
>> +            port->mac_addr.addr[0],
>> +            port->mac_addr.addr[1],
>> +            port->mac_addr.addr[2],
>> +            port->mac_addr.addr[3],
>> +            port->mac_addr.addr[4],
>> +            port->mac_addr.addr[5]);
>> +        printf("start pktio: %s, mac %s\n", if_name, buf);
>> +    }
>> +
>> +    odp_barrier_init(&barrier, nb_worker + 1);
>> +
>> +    memset(&thr_params, 0, sizeof(thr_params));
>> +    thr_params.start    = run_worker;
>> +    thr_params.thr_type = ODP_THREAD_WORKER;
>> +    thr_params.instance = instance;
>> +
>> +    memset(thread_tbl, 0, sizeof(thread_tbl));
>> +    cpu = odp_cpumask_first(&cpumask);
>> +    for (i = 0; i < nb_worker; i++) {
>> +        struct thread_arg_s *arg;
>> +        odp_cpumask_t thr_mask;
>> +
>> +        arg = &global.worker_args[i];
>> +        arg->nb_pktio = args->if_count;
>> +        odp_cpumask_zero(&thr_mask);
>> +        odp_cpumask_set(&thr_mask, cpu);
>> +        thr_params.arg = arg;
>> +        odph_odpthreads_create(&thread_tbl[i], &thr_mask,
>> +                       &thr_params);
>> +        cpu = odp_cpumask_next(&cpumask, cpu);
>> +    }
>> +
>> +    if (args->duration) {
>> +        print_speed_stats(nb_worker, args->duration,
>> PRINT_INTERVAL);
>> +        exit_threads = 1;
>> +    }
>> +
>> +    /* wait for other threads to join */
>> +    for (i = 0; i < nb_worker; i++)
>> +        odph_odpthreads_join(&thread_tbl[i]);
>> +
>> +    return 0;
>> +}
>> +
>> +static void print_usage(char *progname) {
>> +    printf("\n"
>> +           "ODP L3 forwarding application.\n"
>> +           "\n"
>> +           "Usage: %s OPTIONS\n"
>> +           "  E.g. %s -i eth0,eth1 -r 1.1.1.0/24,eth0 -r
> 2.2.2.0/24,eth1\n"
>> +           " In the above example,\n"
>> +           " eth0 will send pkts to eth1 and vice versa\n"
>> +           "\n"
>> +           "Mandatory OPTIONS:\n"
>> +           "  -i, --interface eth interfaces (comma-separated, no
> spaces)\n"
>> +           "  -r, --route SubNet,Intf[,NextHopMAC]\n"
>> +           "    NextHopMAC can be optional\n"
>> +           "\n"
>> +           "Optional OPTIONS:\n"
>> +           "  -s, --style [lpm|hash], ip lookup method\n"
>> +           "    optional, default as lpm\n"
>> +           "  -d, --duration Seconds to run and print stats\n"
>> +           "    optional, default as 0, run forever\n"
>> +           "  -t, --thread Number of threads to do forwarding\n"
>> +           "    optional, default as availbe worker cpu count\n"
>> +           "  -q, --queue  Configure rx queue(s) for port\n"
>> +           "    optional, format: [(port, queue, thread),...]\n"
>> +           "    for example: -q '(0, 0, 1),(1,0,2)'\n"
>> +           "  -h, --help   Display help and exit.\n\n"
>> +           "\n", NO_PATH(progname), NO_PATH(progname)
>> +        );
>> +}
>> +
>> +static void parse_cmdline_args(int argc, char *argv[], app_args_t
>> +*args) {
>> +    int opt;
>> +    int long_index;
>> +    char *token, *local;
>> +    size_t len, route_index = 0;
>> +    int i, mem_failure = 0;
>> +
>> +    static struct option longopts[] = {
>> +        {"interface", required_argument, NULL, 'i'},    /* return 'i'
> */
>> +        {"route", required_argument, NULL, 'r'},    /* return 'r'
> */
>> +        {"style", optional_argument, NULL, 's'},    /* return 's'
> */
>> +        {"duration", optional_argument, NULL, 'd'},    /* return 'd'
> */
>> +        {"thread", optional_argument, NULL, 't'},    /* return 't'
> */
>> +        {"queue", optional_argument, NULL, 'q'},    /* return 'q'
> */
>> +        {"help", no_argument, NULL, 'h'},        /* return 'h'
> */
>> +        {NULL, 0, NULL, 0}
>> +    };
>> +
>> +    while (1) {
>> +        opt = getopt_long(argc, argv, "+s:t:d:i:r:q:h",
>> +                  longopts, &long_index);
>> +
>> +        if (opt == -1)
>> +            break;    /* No more options */
>> +
>> +        switch (opt) {
>> +        /* parse ip lookup method */
>> +        case 's':
>> +            if (!strcmp(optarg, "hash"))
>> +                args->hash_mode = 1;
>> +            break;
>> +        /* parse number of worker threads to be run*/
>> +        case 't':
>> +            i = odp_cpu_count();
>> +            args->worker_count = atoi(optarg);
>> +            if (args->worker_count > i) {
>> +                printf("Too many threads,"
>> +                       "truncate to cpu count: %d\n", i);
>> +                args->worker_count = i;
>> +            }
>> +
>> +            break;
>> +
>> +        /* parse seconds to run */
>> +        case 'd':
>> +            args->duration = atoi(optarg);
>> +            break;
>> +
>> +        /* parse packet-io interface names */
>> +        case 'i':
>> +            len = strlen(optarg);
>> +            if (len == 0) {
>> +                print_usage(argv[0]);
>> +                exit(EXIT_FAILURE);
>> +            }
>> +            len += 1;    /* add room for '\0' */
>> +
>> +            local = malloc(len);
>> +            if (!local) {
>> +                print_usage(argv[0]);
>> +                exit(EXIT_FAILURE);
>> +            }
>> +
>> +            /* count the number of tokens separated by ',' */
>> +            strcpy(local, optarg);
>> +            for (token = strtok(local, ","), i = 0;
>> +                 token != NULL;
>> +                 token = strtok(NULL, ","), i++)
>> +                ;
>> +
>> +            if (i == 0) {
>> +                print_usage(argv[0]);
>> +                exit(EXIT_FAILURE);
>> +            } else if (i > MAX_NB_PKTIO) {
>> +                printf("too many ports specified, "
>> +                       "truncated to %d", MAX_NB_PKTIO);
>> +            }
>> +            args->if_count = i;
>> +
>> +            /* store the if names (reset names string) */
>> +            strcpy(local, optarg);
>> +            for (token = strtok(local, ","), i = 0;
>> +                 token != NULL; token = strtok(NULL, ","), i++) {
>> +                args->if_names[i] = token;
>> +            }
>> +            break;
>> +
>> +        /*Configure Route in forwarding database*/
>> +        case 'r':
>> +            if (route_index >= MAX_NB_ROUTE) {
>> +                printf("No more routes can be added\n");
>> +                break;
>> +            }
>> +            local = calloc(1, strlen(optarg) + 1);
>> +            if (!local) {
>> +                mem_failure = 1;
>> +                break;
>> +            }
>> +            memcpy(local, optarg, strlen(optarg));
>> +            local[strlen(optarg)] = '\0';
>> +            args->route_str[route_index++] = local;
>> +            break;
>> +
>> +        case 'h':
>> +            print_usage(argv[0]);
>> +            exit(EXIT_SUCCESS);
>> +            break;
>> +
>> +        case 'q':
>> +            parse_config(optarg, args);
>> +            break;
>> +
>> +        default:
>> +            break;
>> +        }
>> +    }
>> +
>> +    /* checking arguments */
>> +    if (args->if_count == 0) {
>> +        printf("\nNo option -i specified.\n");
>> +        goto out;
>> +    }
>> +
>> +    if (args->route_str[0] == NULL) {
>> +        printf("\nNo option -r specified.\n");
>> +        goto out;
>> +    }
>> +
>> +    if (mem_failure == 1) {
>> +        printf("\nAllocate memory failure.\n");
>> +        goto out;
>> +    }
>> +    optind = 1;        /* reset 'extern optind' from the getopt lib
> */
>> +    return;
>> +
>> +out:
>> +    print_usage(argv[0]);
>> +    exit(EXIT_FAILURE);
>> +}
>> +
>> +/* split string into tokens */
>> +int split_string(char *str, int stringlen,
>> +         char **tokens, int maxtokens, char delim) {
>> +    int i, tok = 0;
>> +    int tokstart = 1; /* first token is right at start of string */
>> +
>> +    if (str == NULL || tokens == NULL)
>> +        goto einval_error;
>> +
>> +    for (i = 0; i < stringlen; i++) {
>> +        if (str[i] == '\0' || tok >= maxtokens)
>> +            break;
>> +        if (tokstart) {
>> +            tokstart = 0;
>> +            tokens[tok++] = &str[i];
>> +        }
>> +        if (str[i] == delim) {
>> +            str[i] = '\0';
>> +            tokstart = 1;
>> +        }
>> +    }
>> +    return tok;
>> +
>> +einval_error:
>> +    errno = EINVAL;
>> +    return -1;
>> +}
>> +
>> +static int parse_config(char *cfg_str, app_args_t *args) {
>> +    char s[256];
>> +    const char *p, *p0 = cfg_str;
>> +    char *end;
>> +    enum fieldnames {
>> +        FLD_PORT = 0,
>> +        FLD_QUEUE,
>> +        FLD_LCORE,
>> +        FLD_LAST
>> +    };
>> +    unsigned long int_fld[FLD_LAST];
>> +    char *str_fld[FLD_LAST];
>> +    int i;
>> +    unsigned size;
>> +    int nb_qconfs = 0;
>> +    struct l3fwd_qconf_s *qconf_array = &args->qconf_config[0];
>> +
>> +    p = strchr(p0, '(');
>> +    while (p != NULL) {
>> +        ++p;
>> +        p0 = strchr(p, ')');
>> +        if (p0 == NULL)
>> +            return -1;
>> +
>> +        size = p0 - p;
>> +        if (size >= sizeof(s))
>> +            return -1;
>> +
>> +        snprintf(s, sizeof(s), "%.*s", size, p);
>> +        i = split_string(s, sizeof(s), str_fld, FLD_LAST, ',');
>> +        if (i != FLD_LAST)
>> +            return -1;
>> +        for (i = 0; i < FLD_LAST; i++) {
>> +            errno = 0;
>> +            int_fld[i] = strtoul(str_fld[i], &end, 0);
>> +            if (errno != 0 || end == str_fld[i] || int_fld[i] >
> 255)
>> +                return -1;
>> +        }
>> +        if (nb_qconfs >= MAX_NB_QCONFS) {
>> +            printf("exceeded max number of queue
>> params: %hu\n",
>> +                   nb_qconfs);
>> +            return -1;
>> +        }
>> +        qconf_array[nb_qconfs].if_idx = (uint8_t)int_fld[FLD_PORT];
>> +        qconf_array[nb_qconfs].rxq_idx =
>> (uint8_t)int_fld[FLD_QUEUE];
>> +        qconf_array[nb_qconfs].core_idx =
>> (uint8_t)int_fld[FLD_LCORE];
>> +        ++nb_qconfs;
>> +
>> +        p = strchr(p0, '(');
>> +    }
>> +    args->qconf_count = nb_qconfs;
>> +
>> +    return 0;
>> +}
>> +
>> +static void print_info(char *progname, app_args_t *args) {
>> +    int i;
>> +
>> +    printf("\n"
>> +           "ODP system info\n"
>> +           "---------------\n"
>> +           "ODP API version: %s\n"
>> +           "ODP impl name:     %s\n"
>> +           "CPU model:       %s\n"
>> +           "CPU freq (hz):   %" PRIu64 "\n"
>> +           "Cache line size: %i\n"
>> +           "CPU count:       %i\n"
>> +           "\n",
>> +           odp_version_api_str(), odp_version_impl_name(),
>> +           odp_cpu_model_str(), odp_cpu_hz_max(),
>> +           odp_sys_cache_line_size(), odp_cpu_count());
>> +
>> +    printf("Running ODP appl: \"%s\"\n"
>> +           "-----------------\n"
>> +           "IP Lookup:     %s\n"
>> +           "IF Count:        %i\n"
>> +           "Using IFs:      ",
>> +           progname,
>> +           args->hash_mode ? "hash" : "lpm",
>> +           args->if_count);
>> +
>> +    for (i = 0; i < args->if_count; ++i)
>> +        printf(" %s", args->if_names[i]);
>> +
>> +    printf("\n\n");
>> +    fflush(NULL);
>> +}
>> +
>> +/**
>> + * Setup rx and tx queues, distribute them among threads.
>> + * Try to have one tx queue for each rx queue, if not vailable,
>> + * shared tx queue is used.
>> + *
>> + * If no q argument, the queues are distribute among threads as default.
>> + * The thread take one rx queue of a port one time as round-robin order.
>> + */
>> +static void setup_worker_qconf(app_args_t *args) {
>> +    int nb_worker, if_count;
>> +    int i, j, rxq_idx, txq_idx;
>> +    struct thread_arg_s *arg;
>> +    struct l3fwd_pktio_s *port;
>> +    uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_QUEUE];
>> +
>> +    nb_worker = args->worker_count;
>> +    if_count = args->if_count;
>> +
>> +    /* distribute queues among threads */
>> +    if (!args->qconf_count) {
>> +        if (nb_worker > if_count) {
>> +            for (i = 0; i < nb_worker; i++) {
>> +                arg = &global.worker_args[i];
>> +                arg->thr_idx = i;
>> +                j = i % if_count;
>> +                port = &global.l3fwd_pktios[j];
>> +                if (port->rxq_idx < port->nb_rxq) {
>> +                    rxq_idx = port->rxq_idx;
>> +                    arg->pktio[j].rxq[rxq_idx] = rxq_idx;
>> +                    port->rxq_idx++;
>> +                    txq_idx = port->txq_idx;
>> +                    arg->pktio[j].txq[txq_idx] = txq_idx;
>> +                    port->txq_idx++;
>> +                    arg->pktio[j].used = 1;
>> +                }
>> +            }
>> +        } else {
>> +            for (i = 0; i < if_count; i++) {
>> +                j = i % nb_worker;
>> +                arg = &global.worker_args[j];
>> +                arg->thr_idx = j;
>> +                port = &global.l3fwd_pktios[i];
>> +                if (port->rxq_idx < port->nb_rxq) {
>> +                    rxq_idx = port->rxq_idx;
>> +                    arg->pktio[i].rxq[rxq_idx] = rxq_idx;
>> +                    port->rxq_idx++;
>> +                    txq_idx = port->txq_idx;
>> +                    arg->pktio[i].txq[txq_idx] = txq_idx;
>> +                    port->txq_idx++;
>> +                    arg->pktio[i].used = 1;
>> +                }
>> +            }
>> +        }
>> +    }
>> +
>> +    /* specified q argument, distribute queues among threads as it says */
>> +    memset(queue_mask, 0, sizeof(queue_mask));
>> +    for (i = 0; i < args->qconf_count; i++) {
>> +        struct l3fwd_qconf_s *q;
>> +
>> +        q = &args->qconf_config[i];
>> +        if (q->core_idx >= nb_worker || q->if_idx >= if_count)
>> +            LOG_ABORT("Error queue (%d, %d, %d), max port: "
>> +                  "%d, max core: %d\n", q->if_idx, q->rxq_idx,
>> +                  q->core_idx, args->if_count - 1,
>> +                  args->worker_count - 1);
>> +
>> +        /* check if one queue is configured twice or more */
>> +        if (queue_mask[q->if_idx][q->rxq_idx])
>> +            LOG_ABORT("Error queue (%d, %d, %d), reconfig
>> queue\n",
>> +                  q->if_idx, q->rxq_idx, q->core_idx);
>> +        queue_mask[q->if_idx][q->rxq_idx] = 1;
>> +
>> +        port = &global.l3fwd_pktios[q->if_idx];
>> +        if (port->rxq_idx < q->rxq_idx)
>> +            LOG_ABORT("Error queue (%d, %d, %d), queue should
>> be"
>> +                  " in sequence and start from 0, queue %d\n",
>> +                  q->if_idx, q->rxq_idx, q->core_idx,
>> +                  q->rxq_idx);
>> +
>> +        if (q->rxq_idx > port->nb_rxq) {
>> +            LOG_ABORT("Error queue (%d, %d, %d), max
>> queue %d\n",
>> +                  q->if_idx, q->rxq_idx, q->core_idx,
>> +                  port->nb_rxq - 1);
>> +        }
>> +        port->rxq_idx = q->rxq_idx + 1;
>> +        port->txq_idx = q->rxq_idx + 1;
>> +
>> +        /* put the queue into worker_args */
>> +        arg = &global.worker_args[q->core_idx];
>> +        arg->pktio[q->if_idx].rxq[q->rxq_idx] = q->rxq_idx;
>> +        arg->pktio[q->if_idx].txq[q->rxq_idx] = q->rxq_idx;
>> +        arg->pktio[q->if_idx].used = 1;
>> +        arg->thr_idx = q->core_idx;
>> +    }
>> +
>> +    /* config and initialize rx and tx queues. */
>> +    for (i = 0; i < if_count; i++) {
>> +        odp_pktin_queue_param_t in_queue_param;
>> +        odp_pktout_queue_param_t out_queue_param;
>> +        struct odp_pktin_queue_t *inq;
>> +        struct odp_pktout_queue_t *outq;
>> +        const char *name;
>> +        int rc, nb_rxq, nb_txq;
>> +
>> +        port = &global.l3fwd_pktios[i];
>> +        name = args->if_names[i];
>> +        odp_pktin_queue_param_init(&in_queue_param);
>> +        odp_pktout_queue_param_init(&out_queue_param);
>> +
>> +        in_queue_param.op_mode = ODP_PKTIO_OP_MT;
>> +        out_queue_param.op_mode = ODP_PKTIO_OP_MT;
>> +
>> +        in_queue_param.num_queues = port->rxq_idx;
>> +        if (port->rxq_idx > port->nb_rxq) {
>> +            in_queue_param.num_queues = port->nb_rxq;
>> +            in_queue_param.op_mode =
>> ODP_PKTIO_OP_MT_UNSAFE;
>> +        }
>> +
>> +        /* enable flow hashing for multi-input queues */
>> +        if (in_queue_param.num_queues > 1) {
>> +            in_queue_param.hash_enable = 1;
>> +            in_queue_param.hash_proto.proto.ipv4_tcp = 1;
>> +            in_queue_param.hash_proto.proto.ipv4_udp = 1;
>> +        }
>> +
>> +        if (odp_pktin_queue_config(port->pktio, &in_queue_param))
>> +            LOG_ABORT("Fail to config input queue for %s\n",
>> name);
>> +
>> +        out_queue_param.num_queues = port->txq_idx;
>> +        if (port->txq_idx > port->nb_txq) {
>> +            out_queue_param.num_queues = port->nb_txq;
>> +            out_queue_param.op_mode =
>> ODP_PKTIO_OP_MT_UNSAFE;
>> +        }
>> +        if (odp_pktout_queue_config(port->pktio,
>> &out_queue_param))
>> +            LOG_ABORT("Fail to config output queue for %s\n",
>> name);
>> +
>> +        inq = port->ifin;
>> +        nb_rxq = in_queue_param.num_queues;
>> +        if (odp_pktin_queue(port->pktio, inq, nb_rxq) != nb_rxq)
>> +            LOG_ABORT("Fail to set pktin queue for %s\n", name);
>> +
>> +        if (port->rxq_idx > port->nb_rxq) {
>> +            for (rc = port->nb_rxq; rc < port->rxq_idx; rc++)
>> +                inq[rc] = inq[rc % port->nb_rxq];
>> +        }
>> +
>> +        outq = port->ifout;
>> +        nb_txq = out_queue_param.num_queues;
>> +        if (odp_pktout_queue(port->pktio, outq, nb_txq) != nb_txq)
>> +            LOG_ABORT("Fail to set pktout queue for %s\n",
>> name);
>> +
>> +        if (port->txq_idx > port->nb_txq) {
>> +            for (rc = port->nb_txq; rc < port->txq_idx; rc++)
>> +                outq[rc] = outq[rc % port->nb_txq];
>> +        }
>> +    }
>> +}
>> +
>> +static void print_qconf_table(app_args_t *args) {
>> +    int i, j, k, qid;
>> +    char buf[32];
>> +    struct thread_arg_s *thr_arg;
>> +
>> +    printf("Rx queue table\n"
>> +           "-----------------\n"
>> +           "%-16s%-16s%-16s\n",
>> +           "port/id", "queue", "thread");
>> +
>> +    for (i = 0; i < args->worker_count; i++) {
>> +        thr_arg = &global.worker_args[i];
>> +        for (j = 0; j < args->if_count; j++) {
>> +            if (!thr_arg->pktio[j].used)
>> +                continue;
>> +
>> +            sprintf(buf, "%s/%d", args->if_names[j], j);
>> +            for (k = 0; k < MAX_NB_QUEUE; k++) {
>> +                qid = thr_arg->pktio[j].rxq[k];
>> +                if (qid != INVALID_ID)
>> +                    printf("%-16s%-16d%-16d\n", buf, qid,
>> +                           thr_arg->thr_idx);
>> +            }
>> +        }
>> +    }
>> +    printf("\n");
>> +    fflush(NULL);
>> +}
>> +
>> +/**
>> + *  Print statistics
>> + *
>> + * @param num_workers Number of worker threads
>> + * @param duration Number of seconds to loop in
>> + * @param timeout Number of seconds for stats calculation
>> + *
>> + */
>> +static int print_speed_stats(int num_workers, int duration, int
>> +timeout) {
>> +    uint64_t pkts = 0;
>> +    uint64_t pkts_prev = 0;
>> +    uint64_t pps;
>> +    uint64_t rx_drops, tx_drops;
>> +    uint64_t maximum_pps = 0;
>> +    int i;
>> +    int elapsed = 0;
>> +    int stats_enabled = 1;
>> +    int loop_forever = (duration == 0);
>> +
>> +    if (timeout <= 0) {
>> +        stats_enabled = 0;
>> +        timeout = 1;
>> +    }
>> +    /* Wait for all threads to be ready*/
>> +    odp_barrier_wait(&barrier);
>> +
>> +    do {
>> +        pkts = 0;
>> +        rx_drops = 0;
>> +        tx_drops = 0;
>> +        sleep(timeout);
>> +
>> +        for (i = 0; i < num_workers; i++) {
>> +            pkts += global.worker_args[i].packets;
>> +            rx_drops += global.worker_args[i].rx_drops;
>> +            tx_drops += global.worker_args[i].tx_drops;
>> +        }
>> +        if (stats_enabled) {
>> +            pps = (pkts - pkts_prev) / timeout;
>> +            if (pps > maximum_pps)
>> +                maximum_pps = pps;
>> +            printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,
>> +                   maximum_pps);
>> +
>> +            printf(" %" PRIu64 " rx drops, %" PRIu64 " tx
> drops\n",
>> +                   rx_drops, tx_drops);
>> +
>> +            pkts_prev = pkts;
>> +        }
>> +        elapsed += timeout;
>> +    } while (loop_forever || (elapsed < duration));
>> +
>> +    if (stats_enabled)
>> +        printf("TEST RESULT: %" PRIu64 " maximum packets per
>> second.\n",
>> +               maximum_pps);
>> +
>> +    return pkts > 100 ? 0 : -1;
>> +}
>> diff --git a/example/l3fwd/odp_l3fwd_db.c b/example/l3fwd/odp_l3fwd_db.c
>> new file mode 100644 index 0000000..93e32f0
>> --- /dev/null
>> +++ b/example/l3fwd/odp_l3fwd_db.c
>> @@ -0,0 +1,408 @@
>> +/* Copyright (c) 2016, Linaro Limited
>> + * All rights reserved.
>> + *
>> + * SPDX-License-Identifier:     BSD-3-Clause
>> + */
>> +
>> +#ifndef _GNU_SOURCE
>> +#define _GNU_SOURCE
>> +#endif
>> +
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +#include <example_debug.h>
>> +#include <odp_api.h>
>> +#include <odp_l3fwd_db.h>
>> +
>> +/** Jenkins hash support.
>> +  *
>> +  * Copyright (C) 2006 Bob Jenkins ([email protected])
>> +  *
>> +  * http://burtleburtle.net/bob/hash/
>> +  *
>> +  * These are the credits from Bob's sources:
>> +  *
>> +  * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
>> +  *
>> +  * These are functions for producing 32-bit hashes for hash table lookup.
>> +  * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and
>> +final()
>> +  * are externally useful functions.  Routines to test the hash are
>> +included
>> +  * if SELF_TEST is defined.  You can use this free for any purpose.
>> +It's in
>> +  * the public domain.  It has no warranty.
>> +  *
>> +  * $FreeBSD$
>> +  */
>> +#define JHASH_GOLDEN_RATIO    0x9e3779b9
>> +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) #define
>> +FWD_BJ3_MIX(a, b, c) \ { \
>> +    a -= c; a ^= rot(c, 4); c += b; \
>> +    b -= a; b ^= rot(a, 6); a += c; \
>> +    c -= b; c ^= rot(b, 8); b += a; \
>> +    a -= c; a ^= rot(c, 16); c += b; \
>> +    b -= a; b ^= rot(a, 19); a += c; \
>> +    c -= b; c ^= rot(b, 4); b += a; \
>> +}
>> +
>> +/**
>> + * Compute hash value from a flow
>> + */
>> +static inline
>> +uint64_t l3fwd_calc_hash(ipv4_tuple5_t *key) {
>> +    uint64_t l4_ports = 0;
>> +    uint32_t dst_ip, src_ip;
>> +
>> +    src_ip = key->src_ip;
>> +    dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO;
>> +    FWD_BJ3_MIX(src_ip, dst_ip, l4_ports);
>> +
>> +    return l4_ports;
>> +}
>> +
>> +/**
>> + * Parse text string representing an IPv4 address or subnet
>> + *
>> + * String is of the format "XXX.XXX.XXX.XXX(/W)" where
>> + * "XXX" is decimal value and "/W" is optional subnet length
>> + *
>> + * @param ipaddress  Pointer to IP address/subnet string to convert
>> + * @param addr       Pointer to return IPv4 address, host endianness
>> + * @param depth      Pointer to subnet bit width
>> + * @return 0 if successful else -1
>> + */
>> +static inline
>> +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *depth)
>> +{
>> +    int b[4];
>> +    int qualifier = 32;
>> +    int converted;
>> +    uint32_t addr_le;
>> +
>> +    if (strchr(ipaddress, '/')) {
>> +        converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",
>> +                   &b[3], &b[2], &b[1], &b[0],
>> +                   &qualifier);
>> +        if (5 != converted)
>> +            return -1;
>> +    } else {
>> +        converted = sscanf(ipaddress, "%d.%d.%d.%d",
>> +                   &b[3], &b[2], &b[1], &b[0]);
>> +        if (4 != converted)
>> +            return -1;
>> +    }
>> +
>> +    if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))
>> +        return -1;
>> +    if (!qualifier || (qualifier > 32))
>> +        return -1;
>> +
>> +    addr_le = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
>> +    *addr = odp_le_to_cpu_32(addr_le);
>> +    *depth = qualifier;
>> +
>> +    return 0;
>> +}
>> +
>> +/**
>> + * Generate text string representing IPv4 range/subnet, output
>> + * in "XXX.XXX.XXX.XXX/W" format
>> + *
>> + * @param b     Pointer to buffer to store string
>> + * @param range Pointer to IPv4 address range
>> + *
>> + * @return Pointer to supplied buffer
>> + */
>> +static inline
>> +char *ipv4_subnet_str(char *b, ip_addr_range_t *range) {
>> +    sprintf(b, "%d.%d.%d.%d/%d",
>> +        0xFF & ((range->addr) >> 24),
>> +        0xFF & ((range->addr) >> 16),
>> +        0xFF & ((range->addr) >>  8),
>> +        0xFF & ((range->addr) >>  0),
>> +        range->depth);
>> +    return b;
>> +}
>> +
>> +/**
>> + * Generate text string representing MAC address
>> + *
>> + * @param b     Pointer to buffer to store string
>> + * @param mac   Pointer to MAC address
>> + *
>> + * @return Pointer to supplied buffer
>> + */
>> +static inline
>> +char *mac_addr_str(char *b, odph_ethaddr_t *mac) {
>> +    uint8_t *byte;
>> +
>> +    byte = mac->addr;
>> +    sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",
>> +        byte[0], byte[1], byte[2], byte[3], byte[4], byte[5]);
>> +    return b;
>> +}
>> +
>> +/**
>> + * Flow cache table entry
>> + */
>> +typedef struct flow_entry_s {
>> +    ipv4_tuple5_t key;        /**< match key */
>> +    struct flow_entry_s *next;      /**< next entry on the list */
>> +    fwd_db_entry_t *fwd_entry;    /**< entry info in db */
>> +} flow_entry_t;
>> +
>> +/**
>> + * Flow cache table bucket
>> + */
>> +typedef struct flow_bucket_s {
>> +    odp_spinlock_t        lock;    /**< Bucket lock*/
>> +    flow_entry_t        *next;    /**< Pointer to first flow entry in
>> bucket*/
>> +} flow_bucket_t;
>> +
>> +/**
>> + * Flow hash table, fast lookup cache
>> + */
>> +typedef struct flow_table_s {
>> +    flow_bucket_t *bucket;
>> +    uint32_t count;
>> +} flow_table_t;
>> +
>> +static flow_table_t fwd_lookup_cache;
>> +
>> +void init_fwd_hash_cache(void)
>> +{
>> +    odp_shm_t        hash_shm;
>> +    flow_bucket_t        *bucket;
>> +    uint32_t        bucket_count;
>> +    uint32_t        i;
>> +
>> +    bucket_count = FWD_DEF_BUCKET_COUNT;
>> +
>> +    /*Reserve memory for Routing hash table*/
>> +    hash_shm = odp_shm_reserve("route_table",
>> +                   sizeof(flow_bucket_t) * bucket_count,
>> +                   ODP_CACHE_LINE_SIZE, 0);
>> +
>> +    bucket = odp_shm_addr(hash_shm);
>> +    if (!bucket) {
>> +        EXAMPLE_ERR("Error: shared mem alloc failed.\n");
>> +        exit(-1);
>> +    }
>> +
>> +    fwd_lookup_cache.bucket = bucket;
>> +    fwd_lookup_cache.count = bucket_count;
>> +
>> +    /*Initialize Locks*/
>> +    for (i = 0; i < bucket_count; i++) {
>> +        bucket = &fwd_lookup_cache.bucket[i];
>> +        odp_spinlock_init(&bucket->lock);
>> +        bucket->next = NULL;
>> +    }
>> +}
>> +
>> +static inline
>> +int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow) {
>> +    if (key->src_ip == flow->key.src_ip &&
>> +        key->dst_ip == flow->key.dst_ip &&
>> +        key->src_port == flow->key.src_port &&
>> +        key->dst_port == flow->key.dst_port &&
>> +        key->proto == flow->key.proto)
>> +        return 1;
>> +
>> +    return 0;
>> +}
>> +
>> +static inline
>> +flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t
>> +*bucket) {
>> +    flow_entry_t *rst;
>> +
>> +    for (rst = bucket->next; rst != NULL; rst = rst->next) {
>> +        if (match_key_flow(key, rst))
>> +            break;
>> +    }
>> +
>> +    return rst;
>> +}
>> +
>> +static inline
>> +flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key,
>> +                   flow_bucket_t *bucket,
>> +                   fwd_db_entry_t *entry)
>> +{
>> +    flow_entry_t *flow;
>> +
>> +    flow = lookup_fwd_cache(key, bucket);
>> +    if (flow)
>> +        return flow;
>> +
>> +    flow = malloc(sizeof(flow_entry_t));
>> +    flow->key = *key;
>> +    flow->fwd_entry = entry;
>> +
>> +    odp_spinlock_lock(&bucket->lock);
>> +    if (!bucket->next) {
>> +        bucket->next = flow;
>> +    } else {
>> +        flow->next = bucket->next;
>> +        bucket->next = flow;
>> +    }
>> +    odp_spinlock_unlock(&bucket->lock);
>> +
>> +    return flow;
>> +}
>> +
>> +/** Global pointer to fwd db */
>> +fwd_db_t *fwd_db;
>> +
>> +void init_fwd_db(void)
>> +{
>> +    odp_shm_t shm;
>> +
>> +    shm = odp_shm_reserve("shm_fwd_db",
>> +                  sizeof(fwd_db_t),
>> +                  ODP_CACHE_LINE_SIZE,
>> +                  0);
>> +
>> +    fwd_db = odp_shm_addr(shm);
>> +
>> +    if (fwd_db == NULL) {
>> +        EXAMPLE_ERR("Error: shared mem alloc failed.\n");
>> +        exit(EXIT_FAILURE);
>> +    }
>> +    memset(fwd_db, 0, sizeof(*fwd_db));
>> +}
>> +
>> +int create_fwd_db_entry(char *input, char **oif) {
>> +    int pos = 0;
>> +    char *local;
>> +    char *str;
>> +    char *save;
>> +    char *token;
>> +    fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];
>> +
>> +    /* Verify we haven't run out of space */
>> +    if (MAX_DB <= fwd_db->index)
>> +        return -1;
>> +
>> +    /* Make a local copy */
>> +    local = malloc(strlen(input) + 1);
>> +    if (NULL == local)
>> +        return -1;
>> +    strcpy(local, input);
>> +
>> +    /* Setup for using "strtok_r" to search input string */
>> +    str = local;
>> +    save = NULL;
>> +
>> +    /* Parse tokens separated by ':' */
>> +    while (NULL != (token = strtok_r(str, ",", &save))) {
>> +        str = NULL;  /* reset str for subsequent strtok_r calls */
>> +
>> +        /* Parse token based on its position */
>> +        switch (pos) {
>> +        case 0:
>> +            parse_ipv4_string(token,
>> +                      &entry->subnet.addr,
>> +                      &entry->subnet.depth);
>> +            break;
>> +        case 1:
>> +            strncpy(entry->oif, token, OIF_LEN - 1);
>> +            entry->oif[OIF_LEN - 1] = 0;
>> +            break;
>> +        case 2:
>> +            odph_eth_addr_parse(&entry->dst_mac, token);
>> +            *oif = entry->oif;
>> +            break;
>> +
>> +        default:
>> +            printf("ERROR: extra token \"%s\" at position %d\n",
>> +                   token, pos);
>> +            break;
>> +        }
>> +
>> +        /* Advance to next position */
>> +        pos++;
>> +    }
>> +
>> +    /* Add route to the list */
>> +    fwd_db->index++;
>> +    entry->next = fwd_db->list;
>> +    fwd_db->list = entry;
>> +
>> +    free(local);
>> +    return 0;
>> +}
>> +
>> +void resolve_fwd_db(char *intf, int portid, uint8_t *mac) {
>> +    fwd_db_entry_t *entry;
>> +
>> +    /* Walk the list and attempt to set output and MAC */
>> +    for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
>> +        if (strcmp(intf, entry->oif))
>> +            continue;
>> +
>> +        entry->oif_id = portid;
>> +        memcpy(entry->src_mac.addr, mac, ODPH_ETHADDR_LEN);
>> +    }
>> +}
>> +
>> +void dump_fwd_db_entry(fwd_db_entry_t *entry) {
>> +    char subnet_str[MAX_STRING];
>> +    char mac_str[MAX_STRING];
>> +
>> +    mac_addr_str(mac_str, &entry->dst_mac);
>> +    printf("%-16s%-16s%-16s\n",
>> +           ipv4_subnet_str(subnet_str, &entry->subnet),
>> +           entry->oif, mac_str);
>> +}
>> +
>> +void dump_fwd_db(void)
>> +{
>> +    fwd_db_entry_t *entry;
>> +
>> +    printf("Routing table\n"
>> +           "-----------------\n"
>> +           "%-16s%-16s%-16s\n",
>> +           "subnet", "next_hop", "dest_mac");
>> +
>> +    for (entry = fwd_db->list; NULL != entry; entry = entry->next)
>> +        dump_fwd_db_entry(entry);
>> +
>> +    printf("\n");
>> +}
>> +
>> +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key) {
>> +    fwd_db_entry_t *entry;
>> +    flow_entry_t *flow;
>> +    flow_bucket_t *bucket;
>> +    uint64_t hash;
>> +
>> +    /* first find in cache */
>> +    hash = l3fwd_calc_hash(key);
>> +    hash &= fwd_lookup_cache.count - 1;
>> +    bucket = &fwd_lookup_cache.bucket[hash];
>> +    flow = lookup_fwd_cache(key, bucket);
>> +    if (flow)
>> +        return flow->fwd_entry;
>> +
>> +    for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
>> +        uint32_t mask;
>> +
>> +        mask = ((1u << entry->subnet.depth) - 1) <<
>> +            (32 - entry->subnet.depth);
>> +
>> +        if (entry->subnet.addr == (key->dst_ip & mask))
>> +            break;
>> +    }
>> +
>> +    return entry;
>> +}
>> diff --git a/example/l3fwd/odp_l3fwd_db.h
>> b/example/l3fwd/odp_l3fwd_db.h new file mode 100644 index
>> 0000000..840946a
>> --- /dev/null
>> +++ b/example/l3fwd/odp_l3fwd_db.h
>> @@ -0,0 +1,130 @@
>> +/* Copyright (c) 2016, Linaro Limited
>> + * All rights reserved.
>> + *
>> + * SPDX-License-Identifier:     BSD-3-Clause
>> + */
>> +
>> +#ifndef ODP_L3FWD_DB_H_
>> +#define ODP_L3FWD_DB_H_
>> +
>> +#ifdef __cplusplus
>> +extern "C" {
>> +#endif
>> +
>> +#include <odp_api.h>
>> +#include <odp/helper/eth.h>
>> +
>> +#define OIF_LEN 32
>> +#define MAX_DB  32
>> +#define MAX_STRING  32
>> +
>> +/**
>> + * Default number of flows
>> + */
>> +#define FWD_DEF_FLOW_COUNT        100000
>> +
>> +/**
>> + * Default Hash bucket number
>> + */
>> +#define FWD_DEF_BUCKET_COUNT    (FWD_DEF_FLOW_COUNT / 8)
>> +
>> +/**
>> + * IP address range (subnet)
>> + */
>> +typedef struct ip_addr_range_s {
>> +    uint32_t  addr;     /**< IP address, host endianness */
>> +    uint32_t  depth;    /**< subnet bit width */
>> +} ip_addr_range_t;
>> +
>> +/**
>> + * TCP/UDP flow
>> + */
>> +typedef struct ipv4_tuple5_s {
>> +    uint32_t src_ip;
>> +    uint32_t dst_ip;
>> +    uint16_t src_port;
>> +    uint16_t dst_port;
>> +    uint8_t  proto;
>> +} ipv4_tuple5_t;
>> +
>> +/**
>> + * Forwarding data base entry
>> + */
>> +typedef struct fwd_db_entry_s {
>> +    struct fwd_db_entry_s *next;          /**< Next entry on list */
>> +    char                    oif[OIF_LEN]; /**< Output interface name */
>> +    int            oif_id;          /**< Output interface idx */
>> +    odph_ethaddr_t        src_mac;      /**< Output source MAC */
>> +    odph_ethaddr_t        dst_mac;      /**< Output destination
>> MAC */
>> +    ip_addr_range_t        subnet;       /**< Subnet for this router
>> */
>> +} fwd_db_entry_t;
>> +
>> +/**
>> + * Forwarding data base
>> + */
>> +typedef struct fwd_db_s {
>> +    uint32_t          index;          /**< Next available entry */
>> +    fwd_db_entry_t   *list;           /**< List of active routes */
>> +    fwd_db_entry_t    array[MAX_DB];  /**< Entry storage */
>> +} fwd_db_t;
>> +
>> +/** Global pointer to fwd db */
>> +extern fwd_db_t *fwd_db;
>> +
>> +/**
>> + * Initialize FWD DB
>> + */
>> +void init_fwd_db(void);
>> +
>> +/**
>> + * Initialize forward lookup cache based on hash  */ void
>> +init_fwd_hash_cache(void);
>> +
>> +/**
>> + * Create a forwarding database entry
>> + *
>> + * String is of the format "SubNet,Intf,NextHopMAC"
>> + *
>> + * @param input  Pointer to string describing route
>> + * @param oif  Pointer to out interface name, as a return value
>> + *
>> + * @return 0 if successful else -1
>> + */
>> +int create_fwd_db_entry(char *input, char **oif);
>> +
>> +/**
>> + * Scan FWD DB entries and resolve output queue and source MAC address
>> + *
>> + * @param intf   Interface name string
>> + * @param portid Output queue for packet transmit
>> + * @param mac    MAC address of this interface
>> + */
>> +void resolve_fwd_db(char *intf, int portid, uint8_t *mac);
>> +
>> +/**
>> + * Display one forwarding database entry
>> + *
>> + * @param entry  Pointer to entry to display  */ void
>> +dump_fwd_db_entry(fwd_db_entry_t *entry);
>> +
>> +/**
>> + * Display the forwarding database
>> + */
>> +void dump_fwd_db(void);
>> +
>> +/**
>> + * Find a matching forwarding database entry
>> + *
>> + * @param key  ipv4 tuple
>> + *
>> + * @return pointer to forwarding DB entry else NULL  */ fwd_db_entry_t
>> +*find_fwd_db_entry(ipv4_tuple5_t *key);
>> +
>> +#ifdef __cplusplus
>> +}
>> +#endif
>> +
>> +#endif
>> diff --git a/example/l3fwd/odp_l3fwd_lpm.c
>> b/example/l3fwd/odp_l3fwd_lpm.c new file mode 100644 index
>> 0000000..1b3bfcf
>> --- /dev/null
>> +++ b/example/l3fwd/odp_l3fwd_lpm.c
>> @@ -0,0 +1,224 @@
>> +/* Copyright (c) 2016, Linaro Limited
>> + * All rights reserved.
>> + *
>> + * SPDX-License-Identifier:     BSD-3-Clause
>> + */
>> +#ifndef _GNU_SOURCE
>> +#define _GNU_SOURCE
>> +#endif
>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +
>> +#include <example_debug.h>
>> +#include <odp_api.h>
>> +
>> +#include <odp_l3fwd_lpm.h>
>> +
>> +/**
>> + * This is a simple implementation of lpm based on patricia tree.
>> + *
>> + * Tradeoff exists between memory consumption and lookup time.
>> + * Currently it prefers 5 levels: {16, 4, 4, 4, 4}, could be 3
>> + * levels: {16, 8, 8} by defining FIB_NEXT_STRIDE as 8. Other
>> + * levels are also possible.
>> + *
>> + * the ip here is host endian, when doing init or lookup, the
>> + * caller should do endianness conversion if needed.
>> + */
>> +
>> +#define FIB_IP_WIDTH 32
>> +#define FIB_FIRST_STRIDE 16
>> +#define FIB_NEXT_STRIDE 4
>> +#define FIB_NEXT_SIZE (1 << FIB_NEXT_STRIDE) #define FIB_SUB_COUNT
>> +16384 #define DEPTH_TO_MASK(depth) ((1 << (depth)) - 1)
>> +
>> +typedef struct fib_node_s {
>> +    union {
>> +        uint32_t next_hop;
>> +        struct fib_node_s *next; /* next level table */
>> +    };
>> +    uint8_t valid    :1; /* 1, this node has a valid next hop */
>> +    uint8_t end    :1; /* 0, next points to the extended table */
>> +    uint8_t depth    :6; /* bit length of subnet mask */
>> +} fib_node_t;
>> +
>> +typedef struct fib_sub_tbl_s {
>> +    fib_node_t *fib_nodes;
>> +    uint32_t fib_count;
>> +    uint32_t fib_idx;
>> +} fib_sub_tbl_t;
>> +
>> +static fib_node_t fib_rt_tbl[1 << FIB_FIRST_STRIDE]; static
>> +fib_sub_tbl_t fib_lpm_cache;
>> +
>> +static inline fib_node_t *fib_alloc_sub(void) {
>> +    fib_node_t *sub_tbl = NULL;
>> +    uint32_t i, nb_entry;
>> +
>> +    /* extend to next level */
>> +    if (fib_lpm_cache.fib_idx < fib_lpm_cache.fib_count) {
>> +        nb_entry = (fib_lpm_cache.fib_idx + 1) * FIB_NEXT_SIZE;
>> +        sub_tbl = &fib_lpm_cache.fib_nodes[nb_entry];
>> +        fib_lpm_cache.fib_idx++;
>> +        for (i = 0; i < nb_entry; i++) {
>> +            sub_tbl[i].valid = 0;
>> +            sub_tbl[i].end = 1;
>> +        }
>> +    }
>> +
>> +    return sub_tbl;
>> +}
>> +
>> +static void fib_update_node(fib_node_t *fe, int port, int depth) {
>> +    fib_node_t *p;
>> +    int i;
>> +
>> +    if (fe->end) {
>> +        if (!fe->valid) {
>> +            fe->depth = depth;
>> +            fe->next_hop = port;
>> +            fe->valid = 1;
>> +        } else if (fe->depth <= depth) {
>> +            fe->next_hop = port;
>> +            fe->depth = depth;
>> +        }
>> +
>> +        return;
>> +    }
>> +
>> +    for (i = 0; i < FIB_NEXT_SIZE; i++) {
>> +        p = &fe->next[i];
>> +        if (p->end)
>> +            fib_update_node(p, port, depth);
>> +    }
>> +}
>> +
>> +static void fib_insert_node(fib_node_t *fe, uint32_t ip, uint32_t next_hop,
>> +                int ip_width, int eat_bits, int depth) {
>> +    int i;
>> +    uint32_t idx, port;
>> +    fib_node_t *p;
>> +
>> +    if (fe->end) {
>> +        port = fe->next_hop;
>> +        p = fib_alloc_sub();
>> +        if (!p)
>> +            return;
>> +
>> +        fe->next = p;
>> +        fe->end = 0;
>> +        if (fe->valid) {
>> +            for (i = 0; i < FIB_NEXT_SIZE; i++) {
>> +                p = &fe->next[i];
>> +                p->next_hop = port;
>> +                p->depth = fe->depth;
>> +            }
>> +        }
>> +    }
>> +    if (depth - eat_bits <= FIB_NEXT_STRIDE) {
>> +        ip_width -= depth - eat_bits;
>> +        idx = ip >> ip_width;
>> +        ip &= DEPTH_TO_MASK(ip_width);
>> +        p = &fe->next[idx];
>> +        fib_update_node(p, next_hop, depth);
>> +    } else {
>> +        ip_width -= FIB_NEXT_STRIDE;
>> +        idx = ip >> ip_width;
>> +        p = &fe->next[idx];
>> +        ip &= DEPTH_TO_MASK(ip_width);
>> +        eat_bits += FIB_NEXT_STRIDE;
>> +        fib_insert_node(p, ip, next_hop, ip_width, eat_bits, depth);
>> +    }
>> +}
>> +
>> +void fib_tbl_init(void)
>> +{
>> +    int i;
>> +    fib_node_t *fe;
>> +    uint32_t size;
>> +    odp_shm_t lpm_shm;
>> +
>> +    for (i = 0; i < (1 << FIB_FIRST_STRIDE); i++) {
>> +        fe = &fib_rt_tbl[i];
>> +        fe->valid = 0;
>> +        fe->end = 1;
>> +        fe->depth = 0;
>> +        fe->next_hop = 0;
>> +    }
>> +
>> +    size = FIB_NEXT_SIZE * FIB_SUB_COUNT;
>> +    /*Reserve memory for Routing hash table*/
>> +    lpm_shm = odp_shm_reserve("fib_lpm_sub", size,
>> ODP_CACHE_LINE_SIZE, 0);
>> +    fe = odp_shm_addr(lpm_shm);
>> +    if (!fe) {
>> +        EXAMPLE_ERR("Error: shared mem alloc failed for lpm
>> cache.\n");
>> +        exit(-1);
>> +    }
>> +
>> +    fib_lpm_cache.fib_nodes = fe;
>> +    fib_lpm_cache.fib_count = FIB_SUB_COUNT;
>> +    fib_lpm_cache.fib_idx = 0;
>> +}
>> +
>> +void fib_tbl_insert(uint32_t ip, int port, int depth) {
>> +    fib_node_t *fe, *p;
>> +    uint32_t idx;
>> +    int i, j;
>> +    int nb_bits;
>> +
>> +    nb_bits = FIB_FIRST_STRIDE;
>> +    idx = ip >> nb_bits;
>> +    fe = &fib_rt_tbl[idx];
>> +    if (depth <= nb_bits) {
>> +        if (fe->end) {
>> +            fe->next_hop = port;
>> +            fe->depth = depth;
>> +            fe->valid = 1;
>> +            return;
>> +        }
>> +
>> +        for (i = 0; i < FIB_NEXT_SIZE; i++) {
>> +            p = &fe->next[i];
>> +            if (p->end)
>> +                fib_update_node(p, port, depth);
>> +            else
>> +                for (j = 0; j < FIB_NEXT_SIZE; j++)
>> +                    fib_update_node(&p->next[j], port,
>> +                            depth);
>> +        }
>> +
>> +        return;
>> +    }
>> +
>> +    /* need to check sub table */
>> +    ip &= DEPTH_TO_MASK(FIB_IP_WIDTH - nb_bits);
>> +    fib_insert_node(fe, ip, port, FIB_IP_WIDTH - nb_bits, nb_bits, depth);
>> +}
>> +
>> +int fib_tbl_lookup(uint32_t ip, int *port) {
>> +    fib_node_t *fe;
>> +    uint32_t idx;
>> +    int nb_bits;
>> +
>> +    nb_bits = FIB_IP_WIDTH - FIB_FIRST_STRIDE;
>> +    idx = ip >> nb_bits;
>> +    fe = &fib_rt_tbl[idx];
>> +
>> +    ip &= DEPTH_TO_MASK(nb_bits);
>> +    while (!fe->end) {
>> +        nb_bits -= FIB_NEXT_STRIDE;
>> +        idx = ip >> nb_bits;
>> +        fe = &fe->next[idx];
>> +        ip &= DEPTH_TO_MASK(nb_bits);
>> +    }
>> +    *port = fe->next_hop;
>> +
>> +    return fe->valid ? 0 : -1;
>> +}
>> diff --git a/example/l3fwd/odp_l3fwd_lpm.h
>> b/example/l3fwd/odp_l3fwd_lpm.h new file mode 100644 index
>> 0000000..8f78e39
>> --- /dev/null
>> +++ b/example/l3fwd/odp_l3fwd_lpm.h
>> @@ -0,0 +1,20 @@
>> +/* Copyright (c) 2016, Linaro Limited
>> + * All rights reserved.
>> + *
>> + * SPDX-License-Identifier:     BSD-3-Clause
>> + */
>> +
>> +#ifndef ODP_L3FWD_LPM_H_
>> +#define ODP_L3FWD_LPM_H_
>> +
>> +#ifdef __cplusplus
>> +extern "C" {
>> +#endif
>> +void fib_tbl_init(void);
>> +void fib_tbl_insert(uint32_t ip, int port, int depth); int
>> +fib_tbl_lookup(uint32_t ip, int *port); #ifdef __cplusplus } #endif
>> +
>> +#endif
>> diff --git a/example/m4/configure.m4 b/example/m4/configure.m4 index
>> bbda38f..78ef396 100644
>> --- a/example/m4/configure.m4
>> +++ b/example/m4/configure.m4
>> @@ -19,5 +19,6 @@ AC_CONFIG_FILES([example/classifier/Makefile
>>         example/timer/Makefile
>>         example/traffic_mgmt/Makefile
>>         example/l2fwd_simple/Makefile
>> +         example/l3fwd/Makefile
>>         example/switch/Makefile
>>         example/hello/Makefile])
>> --
>> 2.1.0.27.g96db324
> 
> 

Reply via email to