Hi Maxim,

odp_packet_l3_ptr() can return NULL. And you will have null pointer
deference here for non IP packet.
+       ip->chksum = odph_ipv4_csum_update(pkt);
+       eth = odp_packet_l2_ptr(pkt, NULL);
same here eth might be NULL. Better to optimize checks  ifs with
odp_unlikely().
=====================================================
Before called this function, drop_err_pkts(...) has filtered bad packet first.
So looks like no necessary re-check l2/l3 pointer is NULL.

Thanks,
Forrest

> -----Original Message-----
> From: forrest.shi [mailto:[email protected]]
> Sent: Monday, July 25, 2016 16:06
> To: 'Maxim Uvarov' <[email protected]>
> Cc: [email protected]
> Subject: RE: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd
> 
> Hi Maxim,
> 
> Any comments?
> 
> Thanks,
> Forrest
> 
> > -----Original Message-----
> > From: forrest.shi [mailto:[email protected]]
> > Sent: Friday, July 15, 2016 16:22
> > To: 'Maxim Uvarov' <[email protected]>
> > Cc: [email protected]
> > Subject: RE: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd
> >
> > Hi Maxim,
> >
> > The patch is length.  I extracted the comments I have some question as
> below,
> > Others will be fixed.
> >
> >  Increment one value from number of threads might be not good thing to do
> >  due to 1) performance 2) atomic operations break.
> >  It's better if each thread has it's own counters and control threads
> >  summaries values.
> > ==========================================================
> > Not catch your idea. Here each thread has its own counters and is totaled
> > before main exit.
> >
> >
> >  main should end if odp_destroy_global(). And to add this to 'make check'
> >  requires probably to add some logic when we think that test passed and
> >  when we thing that test failed.
> >  I think you copied print_speed_stats() from l2fwd,  there return code
> >  returns if app passed or not.
> > ==========================================================
> > The testing PASS depends on the print_speed_stats() output that is used by
> > test shell script.
> > Here return value is not taken  as PASS or FAIL.
> >
> >
> > On thought is what we need with l2fwd. We places it under
> >  test/performance/ but it's actual example app.
> >  Not sure that it's clear for new users. Definitely l2fwd and l3fwd have
> >  to be at the same level. I'm thinking
> >  about placing both to test/performance/ and create symlink with some
> >  README to example/. But I'm not sure
> >  about that, we need to discuss.
> > ==========================================================
> > In my opinion, don't need to put app in performance.
> > From literal meaning, test should handle testing things, no new app.
> >
> > Thanks,
> > Forrest
> >
> > > -----Original Message-----
> > > From: lng-odp [mailto:[email protected]] On Behalf Of
> Maxim
> > > Uvarov
> > > Sent: Wednesday, July 13, 2016 17:10
> > > To: [email protected]
> > > Subject: Re: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd
> > >
> > > On 07/13/16 10:29, [email protected] wrote:
> > > > 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
> > >
> > > Hello Forest, you should use one git send-email command for both
> > > patches. So 2 patches will be in the same email thread,
> > > and not as 2 separate patches.
> > >
> > > alphabetical order here is required. And in next update please place all
> > > entries vertically, newer patches will be simpler.
> > >
> >
> > OK, will be fixed.
> >
> > >
> > > > 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
> > > last 3 entries are not needed due to we already have it in common .git
> > > ignore file.
> > >
> > > > 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);
> > > if you place print_usage() above main there is no need for declaration.
> The
> > > same for other functions like print_info(). Not critical but you can
> > > just remove
> > > that lines and make example shorter.
> > >
> >
> > Will be fixed.
> >
> > > > +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--;
> > > odp_packet_l3_ptr() can return NULL. And you will have null pointer
> > > deference here for non IP packet.
> > > > +       ip->chksum = odph_ipv4_csum_update(pkt);
> > > > +       eth = odp_packet_l2_ptr(pkt, NULL);
> > > same here eth might be NULL. Better to optimize checks  ifs with
> > > odp_unlikely().
> >
> > Will be fixed.
> >
> > > > +       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);
> > > NULL
> > > > +       ip->ttl--;
> > > > +       ip->chksum = odph_ipv4_csum_update(pkt);
> > > > +       eth = odp_packet_l2_ptr(pkt, NULL);
> > > 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;
> > >
> > > Increment one value from number of threads might be not good thing to
> do
> > > due to 1) performance 2) atomic operations break.
> > > It's better if each thread has it's own counters and control threads
> > > summaries values.
> >
> > Not catch your idea. Here each thread has its own counters.
> > It will be totaled before exit.
> >
> > >
> > > > +
> > > > +       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]);
> > > > +
> > >
> > > main should end if odp_destroy_global(). And to add this to 'make check'
> > > requires probably to add some logic when we think that test passed and
> > > when we thing that test failed.
> > > I think you copied print_speed_stats() from l2fwd,  there return code
> > > returns if app passed or not.
> > >
> >
> > The testing PASS depends on the print_speed_stats() output in the log
file.
> > Here return value is not taken  as PASS or FAIL.
> >
> > > > +       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_
> > > it has to be _ODP_EXAMPLE_L3FWD_LPM_H
> > >
> > > i.e. define should be started with "_" meaning that it's not odp api.
> > > (I know that in some places we use _ODP_ in other places we use ODP_ for
> > > that
> > > case. But that is wrong. We should not overlap with api name space.).
> > >
> > > > +
> > > > +#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])
> > >
> > > On thought is what we need with l2fwd. We places it under
> > > test/performance/ but it's actual example app.
> > > Not sure that it's clear for new users. Definitely l2fwd and l3fwd have
> > > to be at the same level. I'm thinking
> > > about placing both to test/performance/ and create symlink with some
> > > README to example/. But I'm not sure
> > > about that, we need to discuss.
> >
> > Yes.  In my opinion, we do not need a test/performance directory.
> > If want to benchmarking automatically, we can build all the apps but run
> only
> > interested one.
> >
> > > Maxim.
> > >
> >
> 


Reply via email to