diff mbox

[PATCHv13,3/5] validation: move l2fwd from example to performance tests

Message ID 1428517336-29626-4-git-send-email-maxim.uvarov@linaro.org
State New
Headers show

Commit Message

Maxim Uvarov April 8, 2015, 6:22 p.m. UTC
Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org>
---
 configure.ac                 |   1 -
 example/Makefile.am          |   2 +-
 example/l2fwd/.gitignore     |   1 -
 example/l2fwd/Makefile.am    |  10 -
 example/l2fwd/odp_l2fwd.c    | 685 -------------------------------------------
 test/performance/.gitignore  |   3 +-
 test/performance/Makefile.am |   4 +-
 test/performance/odp_l2fwd.c | 685 +++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 690 insertions(+), 701 deletions(-)
 delete mode 100644 example/l2fwd/.gitignore
 delete mode 100644 example/l2fwd/Makefile.am
 delete mode 100644 example/l2fwd/odp_l2fwd.c
 create mode 100644 test/performance/odp_l2fwd.c

Comments

Christophe Milard April 9, 2015, 8:18 a.m. UTC | #1
Hi Maxim,

I am not fully following what this patch does -I appologize for this- but I
am a bit worried about the usage of scripts and other environment specific
usage in the tests.
I am trying to restructure the test/validation tree to enventually get one
single directory per ODP API module and run all tests using CUnit (as main
entry point).
The idea being to eventually have a single executable including all tests
(but where command line options could select specific tests or suites).

If we go for this approach, all pre and post test setup should be done
through the suite initialisation and terminaison functions.

I get a bit worried when I see Cunit beeing call by scripts etc...
I guess we should maybe have a HO with you Mike Anders and me to try to
converge...

Christophe.


On 8 April 2015 at 20:22, Maxim Uvarov <maxim.uvarov@linaro.org> wrote:

> Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org>
> ---
>  configure.ac                 |   1 -
>  example/Makefile.am          |   2 +-
>  example/l2fwd/.gitignore     |   1 -
>  example/l2fwd/Makefile.am    |  10 -
>  example/l2fwd/odp_l2fwd.c    | 685
> -------------------------------------------
>  test/performance/.gitignore  |   3 +-
>  test/performance/Makefile.am |   4 +-
>  test/performance/odp_l2fwd.c | 685
> +++++++++++++++++++++++++++++++++++++++++++
>  8 files changed, 690 insertions(+), 701 deletions(-)
>  delete mode 100644 example/l2fwd/.gitignore
>  delete mode 100644 example/l2fwd/Makefile.am
>  delete mode 100644 example/l2fwd/odp_l2fwd.c
>  create mode 100644 test/performance/odp_l2fwd.c
>
> diff --git a/configure.ac b/configure.ac
> index 91083b9..f47ac66 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -264,7 +264,6 @@ AC_CONFIG_FILES([Makefile
>                  example/Makefile
>                  example/generator/Makefile
>                  example/ipsec/Makefile
> -                example/l2fwd/Makefile
>                  example/packet/Makefile
>                  example/timer/Makefile
>                  pkgconfig/libodp.pc
> diff --git a/example/Makefile.am b/example/Makefile.am
> index 3021571..6bb4f5c 100644
> --- a/example/Makefile.am
> +++ b/example/Makefile.am
> @@ -1 +1 @@
> -SUBDIRS = generator ipsec l2fwd packet timer
> +SUBDIRS = generator ipsec packet timer
> diff --git a/example/l2fwd/.gitignore b/example/l2fwd/.gitignore
> deleted file mode 100644
> index 8563319..0000000
> --- a/example/l2fwd/.gitignore
> +++ /dev/null
> @@ -1 +0,0 @@
> -odp_l2fwd
> diff --git a/example/l2fwd/Makefile.am b/example/l2fwd/Makefile.am
> deleted file mode 100644
> index feced2a..0000000
> --- a/example/l2fwd/Makefile.am
> +++ /dev/null
> @@ -1,10 +0,0 @@
> -include $(top_srcdir)/example/Makefile.inc
> -
> -bin_PROGRAMS = odp_l2fwd
> -odp_l2fwd_LDFLAGS = $(AM_LDFLAGS) -static
> -odp_l2fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
> -
> -noinst_HEADERS = \
> -                 $(top_srcdir)/example/example_debug.h
> -
> -dist_odp_l2fwd_SOURCES = odp_l2fwd.c
> diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c
> deleted file mode 100644
> index 4a49008..0000000
> --- a/example/l2fwd/odp_l2fwd.c
> +++ /dev/null
> @@ -1,685 +0,0 @@
> -/* Copyright (c) 2014, Linaro Limited
> - * All rights reserved.
> - *
> - * SPDX-License-Identifier:     BSD-3-Clause
> - */
> -
> -/**
> - * @file
> - *
> - * @example odp_l2fwd.c  ODP basic forwarding application
> - */
> -
> -/** enable strtok */
> -#define _POSIX_C_SOURCE 200112L
> -
> -#include <stdlib.h>
> -#include <getopt.h>
> -#include <unistd.h>
> -#include <errno.h>
> -
> -#include <example_debug.h>
> -
> -#include <odp.h>
> -#include <odp/helper/linux.h>
> -#include <odp/helper/eth.h>
> -#include <odp/helper/ip.h>
> -
> -/** @def MAX_WORKERS
> - * @brief Maximum number of worker threads
> - */
> -#define MAX_WORKERS            32
> -
> -/** @def SHM_PKT_POOL_SIZE
> - * @brief Size of the shared memory block
> - */
> -#define SHM_PKT_POOL_SIZE      (512*2048)
> -
> -/** @def SHM_PKT_POOL_BUF_SIZE
> - * @brief Buffer size of the packet pool buffer
> - */
> -#define SHM_PKT_POOL_BUF_SIZE  1856
> -
> -/** @def MAX_PKT_BURST
> - * @brief Maximum number of packet bursts
> - */
> -#define MAX_PKT_BURST          16
> -
> -/** @def APPL_MODE_PKT_BURST
> - * @brief The application will handle pakcets in bursts
> - */
> -#define APPL_MODE_PKT_BURST    0
> -
> -/** @def APPL_MODE_PKT_QUEUE
> - * @brief The application will handle packets in queues
> - */
> -#define APPL_MODE_PKT_QUEUE    1
> -
> -/** @def PRINT_APPL_MODE(x)
> - * @brief Macro to print the current status of how the application handles
> - * packets.
> - */
> -#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
> -
> -/** 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))
> -/**
> - * Parsed command line application arguments
> - */
> -typedef struct {
> -       int cpu_count;
> -       int if_count;           /**< Number of interfaces to be used */
> -       char **if_names;        /**< Array of pointers to interface names
> */
> -       int mode;               /**< Packet IO mode */
> -       int time;               /**< Time in seconds to run. */
> -       int accuracy;           /**< Number of seconds to get and print
> statistics */
> -       char *if_str;           /**< Storage for interface names */
> -} appl_args_t;
> -
> -static int exit_threads;       /**< Break workers loop if set to 1 */
> -
> -/**
> - * Statistics
> - */
> -typedef struct {
> -       uint64_t packets;       /**< Number of forwarded packets. */
> -       uint64_t drops;         /**< Number of dropped packets. */
> -} stats_t;
> -
> -/**
> - * Thread specific arguments
> - */
> -typedef struct {
> -       int src_idx;            /**< Source interface identifier */
> -       stats_t **stats;        /**< Per thread packet stats */
> -} thread_args_t;
> -
> -/**
> - * Grouping of all global data
> - */
> -typedef struct {
> -       /** Application (parsed) arguments */
> -       appl_args_t appl;
> -       /** Thread specific arguments */
> -       thread_args_t thread[MAX_WORKERS];
> -       /** Table of pktio handles */
> -       odp_pktio_t pktios[ODP_CONFIG_PKTIO_ENTRIES];
> -} args_t;
> -
> -/** Global pointer to args */
> -static args_t *gbl_args;
> -
> -/* helper funcs */
> -static inline odp_queue_t lookup_dest_q(odp_packet_t pkt);
> -static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
> -static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
> -static void print_info(char *progname, appl_args_t *appl_args);
> -static void usage(char *progname);
> -
> -/**
> - * Packet IO worker thread using ODP queues
> - *
> - * @param arg  thread arguments of type 'thread_args_t *'
> - */
> -static void *pktio_queue_thread(void *arg)
> -{
> -       int thr;
> -       odp_queue_t outq_def;
> -       odp_packet_t pkt;
> -       odp_event_t ev;
> -       thread_args_t *thr_args = arg;
> -
> -       stats_t *stats = calloc(1, sizeof(stats_t));
> -       *thr_args->stats = stats;
> -
> -       thr = odp_thread_id();
> -
> -       printf("[%02i] QUEUE mode\n", thr);
> -
> -       /* Loop packets */
> -       while (!exit_threads) {
> -               /* Use schedule to get buf from any input queue */
> -               ev  = odp_schedule(NULL, ODP_SCHED_WAIT);
> -               pkt = odp_packet_from_event(ev);
> -
> -               /* Drop packets with errors */
> -               if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
> -                       stats->drops += 1;
> -                       continue;
> -               }
> -
> -               outq_def = lookup_dest_q(pkt);
> -
> -               /* Enqueue the packet for output */
> -               odp_queue_enq(outq_def, ev);
> -
> -               stats->packets += 1;
> -       }
> -
> -       free(stats);
> -       return NULL;
> -}
> -
> -/**
> - * Lookup the destination pktio for a given packet
> - */
> -static inline odp_queue_t lookup_dest_q(odp_packet_t pkt)
> -{
> -       int i, src_idx, dst_idx;
> -       odp_pktio_t pktio_src, pktio_dst;
> -
> -       pktio_src = odp_packet_input(pkt);
> -
> -       for (src_idx = -1, i = 0; gbl_args->pktios[i] !=
> ODP_PKTIO_INVALID; ++i)
> -               if (gbl_args->pktios[i] == pktio_src)
> -                       src_idx = i;
> -
> -       if (src_idx == -1)
> -               EXAMPLE_ABORT("Failed to determine pktio input\n");
> -
> -       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
> -       pktio_dst = gbl_args->pktios[dst_idx];
> -
> -       return odp_pktio_outq_getdef(pktio_dst);
> -}
> -
> -/**
> - * Packet IO worker thread using bursts from/to IO resources
> - *
> - * @param arg  thread arguments of type 'thread_args_t *'
> - */
> -static void *pktio_ifburst_thread(void *arg)
> -{
> -       int thr;
> -       thread_args_t *thr_args;
> -       int pkts, pkts_ok;
> -       odp_packet_t pkt_tbl[MAX_PKT_BURST];
> -       int src_idx, dst_idx;
> -       odp_pktio_t pktio_src, pktio_dst;
> -
> -       thr = odp_thread_id();
> -       thr_args = arg;
> -
> -       stats_t *stats = calloc(1, sizeof(stats_t));
> -       *thr_args->stats = stats;
> -
> -       src_idx = thr_args->src_idx;
> -       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
> -       pktio_src = gbl_args->pktios[src_idx];
> -       pktio_dst = gbl_args->pktios[dst_idx];
> -
> -       printf("[%02i] srcif:%s dstif:%s spktio:%02" PRIu64
> -              " dpktio:%02" PRIu64 " BURST mode\n",
> -              thr,
> -              gbl_args->appl.if_names[src_idx],
> -              gbl_args->appl.if_names[dst_idx],
> -              odp_pktio_to_u64(pktio_src), odp_pktio_to_u64(pktio_dst));
> -
> -       /* Loop packets */
> -       while (!exit_threads) {
> -               pkts = odp_pktio_recv(pktio_src, pkt_tbl, MAX_PKT_BURST);
> -               if (pkts <= 0)
> -                       continue;
> -
> -               /* Drop packets with errors */
> -               pkts_ok = drop_err_pkts(pkt_tbl, pkts);
> -               if (pkts_ok > 0)
> -                       odp_pktio_send(pktio_dst, pkt_tbl, pkts_ok);
> -
> -               if (odp_unlikely(pkts_ok != pkts))
> -                       stats->drops += pkts - pkts_ok;
> -
> -               if (pkts_ok == 0)
> -                       continue;
> -
> -               stats->packets += pkts_ok;
> -       }
> -
> -       free(stats);
> -       return NULL;
> -}
> -
> -/**
> - * Create a pktio handle, optionally associating a default input queue.
> - *
> - * @param dev Name of device to open
> - * @param pool Pool to associate with device for packet RX/TX
> - * @param mode Packet processing mode for this device (BURST or QUEUE)
> - *
> - * @return The handle of the created pktio object.
> - * @retval ODP_PKTIO_INVALID if the create fails.
> - */
> -static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool,
> -                               int mode)
> -{
> -       char inq_name[ODP_QUEUE_NAME_LEN];
> -       odp_queue_param_t qparam;
> -       odp_queue_t inq_def;
> -       odp_pktio_t pktio;
> -       int ret;
> -
> -       pktio = odp_pktio_open(dev, pool);
> -       if (pktio == ODP_PKTIO_INVALID) {
> -               EXAMPLE_ERR("Error: failed to open %s\n", dev);
> -               return ODP_PKTIO_INVALID;
> -       }
> -
> -       printf("created pktio %" PRIu64 " (%s)\n",
> -              odp_pktio_to_u64(pktio), dev);
> -
> -       /* no further setup needed for burst mode */
> -       if (mode == APPL_MODE_PKT_BURST)
> -               return pktio;
> -
> -       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
> -       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
> -       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
> -       snprintf(inq_name, sizeof(inq_name), "%" PRIu64 "-pktio_inq_def",
> -                odp_pktio_to_u64(pktio));
> -       inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
> -
> -       inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN,
> &qparam);
> -       if (inq_def == ODP_QUEUE_INVALID) {
> -               EXAMPLE_ERR("Error: pktio queue creation failed\n");
> -               return ODP_PKTIO_INVALID;
> -       }
> -
> -       ret = odp_pktio_inq_setdef(pktio, inq_def);
> -       if (ret != 0) {
> -               EXAMPLE_ERR("Error: default input-Q setup\n");
> -               return ODP_PKTIO_INVALID;
> -       }
> -
> -       return pktio;
> -}
> -
> -/**
> - *  Print statistics
> - *
> - * @param num_workers Number of worker threads
> - * @param thr_stats Pointer to stats storage
> - * @param duration Number of seconds to loop in
> - * @param timeout Number of seconds for stats calculation
> - *
> - */
> -static void print_speed_stats(int num_workers, stats_t **thr_stats,
> -                             int duration, int timeout)
> -{
> -       uint64_t pkts, pkts_prev = 0, pps, drops, maximum_pps = 0;
> -       int i, elapsed = 0;
> -       int loop_forever = (duration == 0);
> -
> -       do {
> -               pkts = 0;
> -               drops = 0;
> -
> -               sleep(timeout);
> -
> -               for (i = 0; i < num_workers; i++) {
> -                       pkts += thr_stats[i]->packets;
> -                       drops += thr_stats[i]->drops;
> -               }
> -               pps = (pkts - pkts_prev) / timeout;
> -               if (pps > maximum_pps)
> -                       maximum_pps = pps;
> -               printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,
> -                      maximum_pps);
> -
> -               printf(" %" PRIu64 " total drops\n", drops);
> -
> -               elapsed += timeout;
> -               pkts_prev = pkts;
> -       } while (loop_forever || (elapsed < duration));
> -
> -       printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
> -              maximum_pps);
> -       return;
> -}
> -
> -/**
> - * ODP L2 forwarding main function
> - */
> -int main(int argc, char *argv[])
> -{
> -       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
> -       odp_pool_t pool;
> -       int i;
> -       int cpu;
> -       int num_workers;
> -       odp_shm_t shm;
> -       odp_cpumask_t cpumask;
> -       char cpumaskstr[ODP_CPUMASK_STR_SIZE];
> -       odp_pool_param_t params;
> -
> -       /* Init ODP before calling anything else */
> -       if (odp_init_global(NULL, NULL)) {
> -               EXAMPLE_ERR("Error: ODP global init failed.\n");
> -               exit(EXIT_FAILURE);
> -       }
> -
> -       /* Init this thread */
> -       if (odp_init_local()) {
> -               EXAMPLE_ERR("Error: ODP local init failed.\n");
> -               exit(EXIT_FAILURE);
> -       }
> -
> -       /* Reserve memory for args from shared mem */
> -       shm = odp_shm_reserve("shm_args", sizeof(args_t),
> -                             ODP_CACHE_LINE_SIZE, 0);
> -       gbl_args = odp_shm_addr(shm);
> -
> -       if (gbl_args == NULL) {
> -               EXAMPLE_ERR("Error: shared mem alloc failed.\n");
> -               exit(EXIT_FAILURE);
> -       }
> -       memset(gbl_args, 0, sizeof(*gbl_args));
> -
> -       /* Parse and store the application arguments */
> -       parse_args(argc, argv, &gbl_args->appl);
> -
> -       /* Print both system and application information */
> -       print_info(NO_PATH(argv[0]), &gbl_args->appl);
> -
> -       /* Default to system CPU count unless user specified */
> -       num_workers = MAX_WORKERS;
> -       if (gbl_args->appl.cpu_count)
> -               num_workers = gbl_args->appl.cpu_count;
> -
> -       /*
> -        * By default CPU #0 runs Linux kernel background tasks.
> -        * Start mapping thread from CPU #1
> -        */
> -       num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
> -       (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
> -
> -       printf("num worker threads: %i\n", num_workers);
> -       printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));
> -       printf("cpu mask:           %s\n", cpumaskstr);
> -
> -       if (num_workers < gbl_args->appl.if_count) {
> -               EXAMPLE_ERR("Error: CPU count %d less than interface
> count\n",
> -                           num_workers);
> -               exit(EXIT_FAILURE);
> -       }
> -       if (gbl_args->appl.if_count % 2 != 0) {
> -               EXAMPLE_ERR("Error: interface count %d is odd in fwd
> appl.\n",
> -                           gbl_args->appl.if_count);
> -               exit(EXIT_FAILURE);
> -       }
> -
> -       /* Create packet pool */
> -       memset(&params, 0, sizeof(params));
> -       params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
> -       params.pkt.len     = SHM_PKT_POOL_BUF_SIZE;
> -       params.pkt.num     = SHM_PKT_POOL_SIZE/SHM_PKT_POOL_BUF_SIZE;
> -       params.type        = ODP_POOL_PACKET;
> -
> -       pool = odp_pool_create("packet pool", ODP_SHM_NULL, &params);
> -
> -       if (pool == ODP_POOL_INVALID) {
> -               EXAMPLE_ERR("Error: packet pool create failed.\n");
> -               exit(EXIT_FAILURE);
> -       }
> -       odp_pool_print(pool);
> -
> -       for (i = 0; i < gbl_args->appl.if_count; ++i) {
> -               gbl_args->pktios[i] =
> create_pktio(gbl_args->appl.if_names[i],
> -                                                  pool,
> gbl_args->appl.mode);
> -               if (gbl_args->pktios[i] == ODP_PKTIO_INVALID)
> -                       exit(EXIT_FAILURE);
> -       }
> -       gbl_args->pktios[i] = ODP_PKTIO_INVALID;
> -
> -       memset(thread_tbl, 0, sizeof(thread_tbl));
> -
> -       stats_t **stats = calloc(1, sizeof(stats_t) * num_workers);
> -
> -       /* Create worker threads */
> -       cpu = odp_cpumask_first(&cpumask);
> -       for (i = 0; i < num_workers; ++i) {
> -               odp_cpumask_t thd_mask;
> -               void *(*thr_run_func) (void *);
> -
> -               if (gbl_args->appl.mode == APPL_MODE_PKT_BURST)
> -                       thr_run_func = pktio_ifburst_thread;
> -               else /* APPL_MODE_PKT_QUEUE */
> -                       thr_run_func = pktio_queue_thread;
> -
> -               gbl_args->thread[i].src_idx = i % gbl_args->appl.if_count;
> -               gbl_args->thread[i].stats = &stats[i];
> -
> -               odp_cpumask_zero(&thd_mask);
> -               odp_cpumask_set(&thd_mask, cpu);
> -               odph_linux_pthread_create(&thread_tbl[i], &thd_mask,
> -                                         thr_run_func,
> -                                         &gbl_args->thread[i]);
> -               cpu = odp_cpumask_next(&cpumask, cpu);
> -       }
> -
> -       print_speed_stats(num_workers, stats, gbl_args->appl.time,
> -                         gbl_args->appl.accuracy);
> -       free(stats);
> -       exit_threads = 1;
> -
> -       /* Master thread waits for other threads to exit */
> -       odph_linux_pthread_join(thread_tbl, num_workers);
> -
> -       free(gbl_args->appl.if_names);
> -       free(gbl_args->appl.if_str);
> -       printf("Exit\n\n");
> -
> -       return 0;
> -}
> -
> -/**
> - * 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 packet
> - * @param len      Length of pkt_tbl[]
> - *
> - * @return Number of packets with no detected error
> - */
> -static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
> -{
> -       odp_packet_t pkt;
> -       unsigned pkt_cnt = len;
> -       unsigned i, j;
> -
> -       for (i = 0, j = 0; i < len; ++i) {
> -               pkt = pkt_tbl[i];
> -
> -               if (odp_unlikely(odp_packet_has_error(pkt))) {
> -                       odp_packet_free(pkt); /* Drop */
> -                       pkt_cnt--;
> -               } else if (odp_unlikely(i != j++)) {
> -                       pkt_tbl[j-1] = pkt;
> -               }
> -       }
> -
> -       return pkt_cnt;
> -}
> -
> -/**
> - * Parse and store the command line arguments
> - *
> - * @param argc       argument count
> - * @param argv[]     argument vector
> - * @param appl_args  Store application arguments here
> - */
> -static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
> -{
> -       int opt;
> -       int long_index;
> -       char *token;
> -       size_t len;
> -       int i;
> -       static struct option longopts[] = {
> -               {"count", required_argument, NULL, 'c'},
> -               {"time", required_argument, NULL, 't'},
> -               {"accuracy", required_argument, NULL, 'a'},
> -               {"interface", required_argument, NULL, 'i'},    /* return
> 'i' */
> -               {"mode", required_argument, NULL, 'm'},         /* return
> 'm' */
> -               {"help", no_argument, NULL, 'h'},               /* return
> 'h' */
> -               {NULL, 0, NULL, 0}
> -       };
> -
> -       appl_args->time = 0; /* loop forever if time to run is 0 */
> -       appl_args->accuracy = 1; /* get and print pps stats second */
> -       appl_args->mode = -1; /* Invalid, must be changed by parsing */
> -
> -       while (1) {
> -               opt = getopt_long(argc, argv, "+c:+t:+a:i:m:h",
> -                                 longopts, &long_index);
> -
> -               if (opt == -1)
> -                       break;  /* No more options */
> -
> -               switch (opt) {
> -               case 'c':
> -                       appl_args->cpu_count = atoi(optarg);
> -                       break;
> -               case 't':
> -                       appl_args->time = atoi(optarg);
> -                       break;
> -               case 'a':
> -                       appl_args->accuracy = atoi(optarg);
> -                       break;
> -                       /* parse packet-io interface names */
> -               case 'i':
> -                       len = strlen(optarg);
> -                       if (len == 0) {
> -                               usage(argv[0]);
> -                               exit(EXIT_FAILURE);
> -                       }
> -                       len += 1;       /* add room for '\0' */
> -
> -                       appl_args->if_str = malloc(len);
> -                       if (appl_args->if_str == NULL) {
> -                               usage(argv[0]);
> -                               exit(EXIT_FAILURE);
> -                       }
> -
> -                       /* count the number of tokens separated by ',' */
> -                       strcpy(appl_args->if_str, optarg);
> -                       for (token = strtok(appl_args->if_str, ","), i = 0;
> -                            token != NULL;
> -                            token = strtok(NULL, ","), i++)
> -                               ;
> -
> -                       appl_args->if_count = i;
> -
> -                       if (appl_args->if_count == 0) {
> -                               usage(argv[0]);
> -                               exit(EXIT_FAILURE);
> -                       }
> -
> -                       /* allocate storage for the if names */
> -                       appl_args->if_names =
> -                           calloc(appl_args->if_count, sizeof(char *));
> -
> -                       /* store the if names (reset names string) */
> -                       strcpy(appl_args->if_str, optarg);
> -                       for (token = strtok(appl_args->if_str, ","), i = 0;
> -                            token != NULL; token = strtok(NULL, ","),
> i++) {
> -                               appl_args->if_names[i] = token;
> -                       }
> -                       break;
> -
> -               case 'm':
> -                       i = atoi(optarg);
> -                       if (i == 0)
> -                               appl_args->mode = APPL_MODE_PKT_BURST;
> -                       else
> -                               appl_args->mode = APPL_MODE_PKT_QUEUE;
> -                       break;
> -
> -               case 'h':
> -                       usage(argv[0]);
> -                       exit(EXIT_SUCCESS);
> -                       break;
> -
> -               default:
> -                       break;
> -               }
> -       }
> -
> -       if (appl_args->if_count == 0 || appl_args->mode == -1) {
> -               usage(argv[0]);
> -               exit(EXIT_FAILURE);
> -       }
> -
> -       optind = 1;             /* reset 'extern optind' from the getopt
> lib */
> -}
> -
> -/**
> - * Print system and application info
> - */
> -static void print_info(char *progname, appl_args_t *appl_args)
> -{
> -       int i;
> -
> -       printf("\n"
> -              "ODP system info\n"
> -              "---------------\n"
> -              "ODP API version: %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_sys_cpu_model_str(),
> odp_sys_cpu_hz(),
> -              odp_sys_cache_line_size(), odp_cpu_count());
> -
> -       printf("Running ODP appl: \"%s\"\n"
> -              "-----------------\n"
> -              "IF-count:        %i\n"
> -              "Using IFs:      ",
> -              progname, appl_args->if_count);
> -       for (i = 0; i < appl_args->if_count; ++i)
> -               printf(" %s", appl_args->if_names[i]);
> -       printf("\n"
> -              "Mode:            ");
> -       if (appl_args->mode == APPL_MODE_PKT_BURST)
> -               PRINT_APPL_MODE(APPL_MODE_PKT_BURST);
> -       else
> -               PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE);
> -       printf("\n\n");
> -       fflush(NULL);
> -}
> -
> -/**
> - * Prinf usage information
> - */
> -static void usage(char *progname)
> -{
> -       printf("\n"
> -              "OpenDataPlane L2 forwarding application.\n"
> -              "\n"
> -              "Usage: %s OPTIONS\n"
> -              "  E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
> -              " In the above example,\n"
> -              " eth0 will send pkts to eth1 and vice versa\n"
> -              " eth2 will send pkts to eth3 and vice versa\n"
> -              "\n"
> -              "Mandatory OPTIONS:\n"
> -              "  -i, --interface Eth interfaces (comma-separated, no
> spaces)\n"
> -              "  -m, --mode      0: Burst send&receive packets (no
> queues)\n"
> -              "                  1: Send&receive packets through ODP
> queues.\n"
> -              "\n"
> -              "Optional OPTIONS\n"
> -              "  -c, --count <number> CPU count.\n"
> -              "  -t, --time  <number> Time in seconds to run.\n"
> -              "  -a, --accuracy <number> Time in seconds get print
> statistics\n"
> -              "                          (default is 1 second).\n"
> -              "  -h, --help           Display help and exit.\n\n"
> -              " environment variables: ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
> -              "                        ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
> -              "                        ODP_PKTIO_DISABLE_SOCKET_BASIC\n"
> -              " can be used to advanced pkt I/O selection for
> linux-generic\n"
> -              "\n", NO_PATH(progname), NO_PATH(progname)
> -           );
> -}
> diff --git a/test/performance/.gitignore b/test/performance/.gitignore
> index 1bdb90d..d41d1e1 100644
> --- a/test/performance/.gitignore
> +++ b/test/performance/.gitignore
> @@ -1,4 +1,5 @@
>  *.log
>  *.trs
> -odp_scheduling
>  odp_atomic
> +odp_l2fwd
> +odp_scheduling
> diff --git a/test/performance/Makefile.am b/test/performance/Makefile.am
> index b0f7457..b501584 100644
> --- a/test/performance/Makefile.am
> +++ b/test/performance/Makefile.am
> @@ -1,10 +1,10 @@
>  include $(top_srcdir)/test/Makefile.inc
>
> -TESTS_ENVIRONMENT = TEST_DIR=${builddir}
> +TESTS_ENVIRONMENT = TEST_DIR=${builddir} ODP_PLATFORM=${with_platform}
>
>  EXECUTABLES = odp_atomic
>
> -COMPILE_ONLY = odp_scheduling
> +COMPILE_ONLY = odp_scheduling odp_l2fwd
>
>  TESTSCRIPTS = odp_scheduling_run
>
> diff --git a/test/performance/odp_l2fwd.c b/test/performance/odp_l2fwd.c
> new file mode 100644
> index 0000000..5d4b833
> --- /dev/null
> +++ b/test/performance/odp_l2fwd.c
> @@ -0,0 +1,685 @@
> +/* Copyright (c) 2014, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +/**
> + * @file
> + *
> + * @example odp_l2fwd.c  ODP basic forwarding application
> + */
> +
> +/** enable strtok */
> +#define _POSIX_C_SOURCE 200112L
> +
> +#include <stdlib.h>
> +#include <getopt.h>
> +#include <unistd.h>
> +#include <errno.h>
> +
> +#include <test_debug.h>
> +
> +#include <odp.h>
> +#include <odp/helper/linux.h>
> +#include <odp/helper/eth.h>
> +#include <odp/helper/ip.h>
> +
> +/** @def MAX_WORKERS
> + * @brief Maximum number of worker threads
> + */
> +#define MAX_WORKERS            32
> +
> +/** @def SHM_PKT_POOL_SIZE
> + * @brief Size of the shared memory block
> + */
> +#define SHM_PKT_POOL_SIZE      (512*2048)
> +
> +/** @def SHM_PKT_POOL_BUF_SIZE
> + * @brief Buffer size of the packet pool buffer
> + */
> +#define SHM_PKT_POOL_BUF_SIZE  1856
> +
> +/** @def MAX_PKT_BURST
> + * @brief Maximum number of packet bursts
> + */
> +#define MAX_PKT_BURST          16
> +
> +/** @def APPL_MODE_PKT_BURST
> + * @brief The application will handle pakcets in bursts
> + */
> +#define APPL_MODE_PKT_BURST    0
> +
> +/** @def APPL_MODE_PKT_QUEUE
> + * @brief The application will handle packets in queues
> + */
> +#define APPL_MODE_PKT_QUEUE    1
> +
> +/** @def PRINT_APPL_MODE(x)
> + * @brief Macro to print the current status of how the application handles
> + * packets.
> + */
> +#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
> +
> +/** 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))
> +/**
> + * Parsed command line application arguments
> + */
> +typedef struct {
> +       int cpu_count;
> +       int if_count;           /**< Number of interfaces to be used */
> +       char **if_names;        /**< Array of pointers to interface names
> */
> +       int mode;               /**< Packet IO mode */
> +       int time;               /**< Time in seconds to run. */
> +       int accuracy;           /**< Number of seconds to get and print
> statistics */
> +       char *if_str;           /**< Storage for interface names */
> +} appl_args_t;
> +
> +static int exit_threads;       /**< Break workers loop if set to 1 */
> +
> +/**
> + * Statistics
> + */
> +typedef struct {
> +       uint64_t packets;       /**< Number of forwarded packets. */
> +       uint64_t drops;         /**< Number of dropped packets. */
> +} stats_t;
> +
> +/**
> + * Thread specific arguments
> + */
> +typedef struct {
> +       int src_idx;            /**< Source interface identifier */
> +       stats_t **stats;        /**< Per thread packet stats */
> +} thread_args_t;
> +
> +/**
> + * Grouping of all global data
> + */
> +typedef struct {
> +       /** Application (parsed) arguments */
> +       appl_args_t appl;
> +       /** Thread specific arguments */
> +       thread_args_t thread[MAX_WORKERS];
> +       /** Table of pktio handles */
> +       odp_pktio_t pktios[ODP_CONFIG_PKTIO_ENTRIES];
> +} args_t;
> +
> +/** Global pointer to args */
> +static args_t *gbl_args;
> +
> +/* helper funcs */
> +static inline odp_queue_t lookup_dest_q(odp_packet_t pkt);
> +static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
> +static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
> +static void print_info(char *progname, appl_args_t *appl_args);
> +static void usage(char *progname);
> +
> +/**
> + * Packet IO worker thread using ODP queues
> + *
> + * @param arg  thread arguments of type 'thread_args_t *'
> + */
> +static void *pktio_queue_thread(void *arg)
> +{
> +       int thr;
> +       odp_queue_t outq_def;
> +       odp_packet_t pkt;
> +       odp_event_t ev;
> +       thread_args_t *thr_args = arg;
> +
> +       stats_t *stats = calloc(1, sizeof(stats_t));
> +       *thr_args->stats = stats;
> +
> +       thr = odp_thread_id();
> +
> +       printf("[%02i] QUEUE mode\n", thr);
> +
> +       /* Loop packets */
> +       while (!exit_threads) {
> +               /* Use schedule to get buf from any input queue */
> +               ev  = odp_schedule(NULL, ODP_SCHED_WAIT);
> +               pkt = odp_packet_from_event(ev);
> +
> +               /* Drop packets with errors */
> +               if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
> +                       stats->drops += 1;
> +                       continue;
> +               }
> +
> +               outq_def = lookup_dest_q(pkt);
> +
> +               /* Enqueue the packet for output */
> +               odp_queue_enq(outq_def, ev);
> +
> +               stats->packets += 1;
> +       }
> +
> +       free(stats);
> +       return NULL;
> +}
> +
> +/**
> + * Lookup the destination pktio for a given packet
> + */
> +static inline odp_queue_t lookup_dest_q(odp_packet_t pkt)
> +{
> +       int i, src_idx, dst_idx;
> +       odp_pktio_t pktio_src, pktio_dst;
> +
> +       pktio_src = odp_packet_input(pkt);
> +
> +       for (src_idx = -1, i = 0; gbl_args->pktios[i] !=
> ODP_PKTIO_INVALID; ++i)
> +               if (gbl_args->pktios[i] == pktio_src)
> +                       src_idx = i;
> +
> +       if (src_idx == -1)
> +               LOG_ABORT("Failed to determine pktio input\n");
> +
> +       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
> +       pktio_dst = gbl_args->pktios[dst_idx];
> +
> +       return odp_pktio_outq_getdef(pktio_dst);
> +}
> +
> +/**
> + * Packet IO worker thread using bursts from/to IO resources
> + *
> + * @param arg  thread arguments of type 'thread_args_t *'
> + */
> +static void *pktio_ifburst_thread(void *arg)
> +{
> +       int thr;
> +       thread_args_t *thr_args;
> +       int pkts, pkts_ok;
> +       odp_packet_t pkt_tbl[MAX_PKT_BURST];
> +       int src_idx, dst_idx;
> +       odp_pktio_t pktio_src, pktio_dst;
> +
> +       thr = odp_thread_id();
> +       thr_args = arg;
> +
> +       stats_t *stats = calloc(1, sizeof(stats_t));
> +       *thr_args->stats = stats;
> +
> +       src_idx = thr_args->src_idx;
> +       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
> +       pktio_src = gbl_args->pktios[src_idx];
> +       pktio_dst = gbl_args->pktios[dst_idx];
> +
> +       printf("[%02i] srcif:%s dstif:%s spktio:%02" PRIu64
> +              " dpktio:%02" PRIu64 " BURST mode\n",
> +              thr,
> +              gbl_args->appl.if_names[src_idx],
> +              gbl_args->appl.if_names[dst_idx],
> +              odp_pktio_to_u64(pktio_src), odp_pktio_to_u64(pktio_dst));
> +
> +       /* Loop packets */
> +       while (!exit_threads) {
> +               pkts = odp_pktio_recv(pktio_src, pkt_tbl, MAX_PKT_BURST);
> +               if (pkts <= 0)
> +                       continue;
> +
> +               /* Drop packets with errors */
> +               pkts_ok = drop_err_pkts(pkt_tbl, pkts);
> +               if (pkts_ok > 0)
> +                       odp_pktio_send(pktio_dst, pkt_tbl, pkts_ok);
> +
> +               if (odp_unlikely(pkts_ok != pkts))
> +                       stats->drops += pkts - pkts_ok;
> +
> +               if (pkts_ok == 0)
> +                       continue;
> +
> +               stats->packets += pkts_ok;
> +       }
> +
> +       free(stats);
> +       return NULL;
> +}
> +
> +/**
> + * Create a pktio handle, optionally associating a default input queue.
> + *
> + * @param dev Name of device to open
> + * @param pool Pool to associate with device for packet RX/TX
> + * @param mode Packet processing mode for this device (BURST or QUEUE)
> + *
> + * @return The handle of the created pktio object.
> + * @retval ODP_PKTIO_INVALID if the create fails.
> + */
> +static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool,
> +                               int mode)
> +{
> +       char inq_name[ODP_QUEUE_NAME_LEN];
> +       odp_queue_param_t qparam;
> +       odp_queue_t inq_def;
> +       odp_pktio_t pktio;
> +       int ret;
> +
> +       pktio = odp_pktio_open(dev, pool);
> +       if (pktio == ODP_PKTIO_INVALID) {
> +               LOG_ERR("Error: failed to open %s\n", dev);
> +               return ODP_PKTIO_INVALID;
> +       }
> +
> +       printf("created pktio %" PRIu64 " (%s)\n",
> +              odp_pktio_to_u64(pktio), dev);
> +
> +       /* no further setup needed for burst mode */
> +       if (mode == APPL_MODE_PKT_BURST)
> +               return pktio;
> +
> +       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
> +       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
> +       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
> +       snprintf(inq_name, sizeof(inq_name), "%" PRIu64 "-pktio_inq_def",
> +                odp_pktio_to_u64(pktio));
> +       inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
> +
> +       inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN,
> &qparam);
> +       if (inq_def == ODP_QUEUE_INVALID) {
> +               LOG_ERR("Error: pktio queue creation failed\n");
> +               return ODP_PKTIO_INVALID;
> +       }
> +
> +       ret = odp_pktio_inq_setdef(pktio, inq_def);
> +       if (ret != 0) {
> +               LOG_ERR("Error: default input-Q setup\n");
> +               return ODP_PKTIO_INVALID;
> +       }
> +
> +       return pktio;
> +}
> +
> +/**
> + *  Print statistics
> + *
> + * @param num_workers Number of worker threads
> + * @param thr_stats Pointer to stats storage
> + * @param duration Number of seconds to loop in
> + * @param timeout Number of seconds for stats calculation
> + *
> + */
> +static void print_speed_stats(int num_workers, stats_t **thr_stats,
> +                             int duration, int timeout)
> +{
> +       uint64_t pkts, pkts_prev = 0, pps, drops, maximum_pps = 0;
> +       int i, elapsed = 0;
> +       int loop_forever = (duration == 0);
> +
> +       do {
> +               pkts = 0;
> +               drops = 0;
> +
> +               sleep(timeout);
> +
> +               for (i = 0; i < num_workers; i++) {
> +                       pkts += thr_stats[i]->packets;
> +                       drops += thr_stats[i]->drops;
> +               }
> +               pps = (pkts - pkts_prev) / timeout;
> +               if (pps > maximum_pps)
> +                       maximum_pps = pps;
> +               printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,
> +                      maximum_pps);
> +
> +               printf(" %" PRIu64 " total drops\n", drops);
> +
> +               elapsed += timeout;
> +               pkts_prev = pkts;
> +       } while (loop_forever || (elapsed < duration));
> +
> +       printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
> +              maximum_pps);
> +       return;
> +}
> +
> +/**
> + * ODP L2 forwarding main function
> + */
> +int main(int argc, char *argv[])
> +{
> +       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
> +       odp_pool_t pool;
> +       int i;
> +       int cpu;
> +       int num_workers;
> +       odp_shm_t shm;
> +       odp_cpumask_t cpumask;
> +       char cpumaskstr[ODP_CPUMASK_STR_SIZE];
> +       odp_pool_param_t params;
> +
> +       /* Init ODP before calling anything else */
> +       if (odp_init_global(NULL, NULL)) {
> +               LOG_ERR("Error: ODP global init failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Init this thread */
> +       if (odp_init_local()) {
> +               LOG_ERR("Error: ODP local init failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Reserve memory for args from shared mem */
> +       shm = odp_shm_reserve("shm_args", sizeof(args_t),
> +                             ODP_CACHE_LINE_SIZE, 0);
> +       gbl_args = odp_shm_addr(shm);
> +
> +       if (gbl_args == NULL) {
> +               LOG_ERR("Error: shared mem alloc failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       memset(gbl_args, 0, sizeof(*gbl_args));
> +
> +       /* Parse and store the application arguments */
> +       parse_args(argc, argv, &gbl_args->appl);
> +
> +       /* Print both system and application information */
> +       print_info(NO_PATH(argv[0]), &gbl_args->appl);
> +
> +       /* Default to system CPU count unless user specified */
> +       num_workers = MAX_WORKERS;
> +       if (gbl_args->appl.cpu_count)
> +               num_workers = gbl_args->appl.cpu_count;
> +
> +       /*
> +        * By default CPU #0 runs Linux kernel background tasks.
> +        * Start mapping thread from CPU #1
> +        */
> +       num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
> +       (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
> +
> +       printf("num worker threads: %i\n", num_workers);
> +       printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));
> +       printf("cpu mask:           %s\n", cpumaskstr);
> +
> +       if (num_workers < gbl_args->appl.if_count) {
> +               LOG_ERR("Error: CPU count %d less than interface count\n",
> +                       num_workers);
> +               exit(EXIT_FAILURE);
> +       }
> +       if (gbl_args->appl.if_count % 2 != 0) {
> +               LOG_ERR("Error: interface count %d is odd in fwd appl.\n",
> +                       gbl_args->appl.if_count);
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Create packet pool */
> +       memset(&params, 0, sizeof(params));
> +       params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
> +       params.pkt.len     = SHM_PKT_POOL_BUF_SIZE;
> +       params.pkt.num     = SHM_PKT_POOL_SIZE/SHM_PKT_POOL_BUF_SIZE;
> +       params.type        = ODP_POOL_PACKET;
> +
> +       pool = odp_pool_create("packet pool", ODP_SHM_NULL, &params);
> +
> +       if (pool == ODP_POOL_INVALID) {
> +               LOG_ERR("Error: packet pool create failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       odp_pool_print(pool);
> +
> +       for (i = 0; i < gbl_args->appl.if_count; ++i) {
> +               gbl_args->pktios[i] =
> create_pktio(gbl_args->appl.if_names[i],
> +                                                  pool,
> gbl_args->appl.mode);
> +               if (gbl_args->pktios[i] == ODP_PKTIO_INVALID)
> +                       exit(EXIT_FAILURE);
> +       }
> +       gbl_args->pktios[i] = ODP_PKTIO_INVALID;
> +
> +       memset(thread_tbl, 0, sizeof(thread_tbl));
> +
> +       stats_t **stats = calloc(1, sizeof(stats_t) * num_workers);
> +
> +       /* Create worker threads */
> +       cpu = odp_cpumask_first(&cpumask);
> +       for (i = 0; i < num_workers; ++i) {
> +               odp_cpumask_t thd_mask;
> +               void *(*thr_run_func) (void *);
> +
> +               if (gbl_args->appl.mode == APPL_MODE_PKT_BURST)
> +                       thr_run_func = pktio_ifburst_thread;
> +               else /* APPL_MODE_PKT_QUEUE */
> +                       thr_run_func = pktio_queue_thread;
> +
> +               gbl_args->thread[i].src_idx = i % gbl_args->appl.if_count;
> +               gbl_args->thread[i].stats = &stats[i];
> +
> +               odp_cpumask_zero(&thd_mask);
> +               odp_cpumask_set(&thd_mask, cpu);
> +               odph_linux_pthread_create(&thread_tbl[i], &thd_mask,
> +                                         thr_run_func,
> +                                         &gbl_args->thread[i]);
> +               cpu = odp_cpumask_next(&cpumask, cpu);
> +       }
> +
> +       print_speed_stats(num_workers, stats, gbl_args->appl.time,
> +                         gbl_args->appl.accuracy);
> +       free(stats);
> +       exit_threads = 1;
> +
> +       /* Master thread waits for other threads to exit */
> +       odph_linux_pthread_join(thread_tbl, num_workers);
> +
> +       free(gbl_args->appl.if_names);
> +       free(gbl_args->appl.if_str);
> +       printf("Exit\n\n");
> +
> +       return 0;
> +}
> +
> +/**
> + * 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 packet
> + * @param len      Length of pkt_tbl[]
> + *
> + * @return Number of packets with no detected error
> + */
> +static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
> +{
> +       odp_packet_t pkt;
> +       unsigned pkt_cnt = len;
> +       unsigned i, j;
> +
> +       for (i = 0, j = 0; i < len; ++i) {
> +               pkt = pkt_tbl[i];
> +
> +               if (odp_unlikely(odp_packet_has_error(pkt))) {
> +                       odp_packet_free(pkt); /* Drop */
> +                       pkt_cnt--;
> +               } else if (odp_unlikely(i != j++)) {
> +                       pkt_tbl[j-1] = pkt;
> +               }
> +       }
> +
> +       return pkt_cnt;
> +}
> +
> +/**
> + * Parse and store the command line arguments
> + *
> + * @param argc       argument count
> + * @param argv[]     argument vector
> + * @param appl_args  Store application arguments here
> + */
> +static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
> +{
> +       int opt;
> +       int long_index;
> +       char *token;
> +       size_t len;
> +       int i;
> +       static struct option longopts[] = {
> +               {"count", required_argument, NULL, 'c'},
> +               {"time", required_argument, NULL, 't'},
> +               {"accuracy", required_argument, NULL, 'a'},
> +               {"interface", required_argument, NULL, 'i'},    /* return
> 'i' */
> +               {"mode", required_argument, NULL, 'm'},         /* return
> 'm' */
> +               {"help", no_argument, NULL, 'h'},               /* return
> 'h' */
> +               {NULL, 0, NULL, 0}
> +       };
> +
> +       appl_args->time = 0; /* loop forever if time to run is 0 */
> +       appl_args->accuracy = 1; /* get and print pps stats second */
> +       appl_args->mode = -1; /* Invalid, must be changed by parsing */
> +
> +       while (1) {
> +               opt = getopt_long(argc, argv, "+c:+t:+a:i:m:h",
> +                                 longopts, &long_index);
> +
> +               if (opt == -1)
> +                       break;  /* No more options */
> +
> +               switch (opt) {
> +               case 'c':
> +                       appl_args->cpu_count = atoi(optarg);
> +                       break;
> +               case 't':
> +                       appl_args->time = atoi(optarg);
> +                       break;
> +               case 'a':
> +                       appl_args->accuracy = atoi(optarg);
> +                       break;
> +                       /* parse packet-io interface names */
> +               case 'i':
> +                       len = strlen(optarg);
> +                       if (len == 0) {
> +                               usage(argv[0]);
> +                               exit(EXIT_FAILURE);
> +                       }
> +                       len += 1;       /* add room for '\0' */
> +
> +                       appl_args->if_str = malloc(len);
> +                       if (appl_args->if_str == NULL) {
> +                               usage(argv[0]);
> +                               exit(EXIT_FAILURE);
> +                       }
> +
> +                       /* count the number of tokens separated by ',' */
> +                       strcpy(appl_args->if_str, optarg);
> +                       for (token = strtok(appl_args->if_str, ","), i = 0;
> +                            token != NULL;
> +                            token = strtok(NULL, ","), i++)
> +                               ;
> +
> +                       appl_args->if_count = i;
> +
> +                       if (appl_args->if_count == 0) {
> +                               usage(argv[0]);
> +                               exit(EXIT_FAILURE);
> +                       }
> +
> +                       /* allocate storage for the if names */
> +                       appl_args->if_names =
> +                           calloc(appl_args->if_count, sizeof(char *));
> +
> +                       /* store the if names (reset names string) */
> +                       strcpy(appl_args->if_str, optarg);
> +                       for (token = strtok(appl_args->if_str, ","), i = 0;
> +                            token != NULL; token = strtok(NULL, ","),
> i++) {
> +                               appl_args->if_names[i] = token;
> +                       }
> +                       break;
> +
> +               case 'm':
> +                       i = atoi(optarg);
> +                       if (i == 0)
> +                               appl_args->mode = APPL_MODE_PKT_BURST;
> +                       else
> +                               appl_args->mode = APPL_MODE_PKT_QUEUE;
> +                       break;
> +
> +               case 'h':
> +                       usage(argv[0]);
> +                       exit(EXIT_SUCCESS);
> +                       break;
> +
> +               default:
> +                       break;
> +               }
> +       }
> +
> +       if (appl_args->if_count == 0 || appl_args->mode == -1) {
> +               usage(argv[0]);
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       optind = 1;             /* reset 'extern optind' from the getopt
> lib */
> +}
> +
> +/**
> + * Print system and application info
> + */
> +static void print_info(char *progname, appl_args_t *appl_args)
> +{
> +       int i;
> +
> +       printf("\n"
> +              "ODP system info\n"
> +              "---------------\n"
> +              "ODP API version: %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_sys_cpu_model_str(),
> odp_sys_cpu_hz(),
> +              odp_sys_cache_line_size(), odp_cpu_count());
> +
> +       printf("Running ODP appl: \"%s\"\n"
> +              "-----------------\n"
> +              "IF-count:        %i\n"
> +              "Using IFs:      ",
> +              progname, appl_args->if_count);
> +       for (i = 0; i < appl_args->if_count; ++i)
> +               printf(" %s", appl_args->if_names[i]);
> +       printf("\n"
> +              "Mode:            ");
> +       if (appl_args->mode == APPL_MODE_PKT_BURST)
> +               PRINT_APPL_MODE(APPL_MODE_PKT_BURST);
> +       else
> +               PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE);
> +       printf("\n\n");
> +       fflush(NULL);
> +}
> +
> +/**
> + * Prinf usage information
> + */
> +static void usage(char *progname)
> +{
> +       printf("\n"
> +              "OpenDataPlane L2 forwarding application.\n"
> +              "\n"
> +              "Usage: %s OPTIONS\n"
> +              "  E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
> +              " In the above example,\n"
> +              " eth0 will send pkts to eth1 and vice versa\n"
> +              " eth2 will send pkts to eth3 and vice versa\n"
> +              "\n"
> +              "Mandatory OPTIONS:\n"
> +              "  -i, --interface Eth interfaces (comma-separated, no
> spaces)\n"
> +              "  -m, --mode      0: Burst send&receive packets (no
> queues)\n"
> +              "                  1: Send&receive packets through ODP
> queues.\n"
> +              "\n"
> +              "Optional OPTIONS\n"
> +              "  -c, --count <number> CPU count.\n"
> +              "  -t, --time  <number> Time in seconds to run.\n"
> +              "  -a, --accuracy <number> Time in seconds get print
> statistics\n"
> +              "                          (default is 1 second).\n"
> +              "  -h, --help           Display help and exit.\n\n"
> +              " environment variables: ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
> +              "                        ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
> +              "                        ODP_PKTIO_DISABLE_SOCKET_BASIC\n"
> +              " can be used to advanced pkt I/O selection for
> linux-generic\n"
> +              "\n", NO_PATH(progname), NO_PATH(progname)
> +           );
> +}
> --
> 1.9.1
>
> _______________________________________________
> lng-odp mailing list
> lng-odp@lists.linaro.org
> https://lists.linaro.org/mailman/listinfo/lng-odp
>
Maxim Uvarov April 9, 2015, 11:29 a.m. UTC | #2
On 04/09/15 11:18, Christophe Milard wrote:
> Hi Maxim,
>
> I am not fully following what this patch does -I appologize for this- 
> but I am a bit worried about the usage of scripts and other 
> environment specific usage in the tests.
> I am trying to restructure the test/validation tree to enventually get 
> one single directory per ODP API module and run all tests using CUnit 
> (as main entry point).
> The idea being to eventually have a single executable including all 
> tests (but where command line options could select specific tests or 
> suites).
>
> If we go for this approach, all pre and post test setup should be done 
> through the suite initialisation and terminaison functions.
>
> I get a bit worried when I see Cunit beeing call by scripts etc...
> I guess we should maybe have a HO with you Mike Anders and me to try 
> to converge...
>
> Christophe.

For pktio tests for example we need some environment to set up to run 
that tests. I want also run all our examples in some automatic manner to 
make sure that they still work. We wanted to keep only simple examples 
in example directory and move more complex apps to tests. But here 
odp_l2fwd should be standalone app and should not be linked to 
validation suite.

Maxim.


>
>
> On 8 April 2015 at 20:22, Maxim Uvarov <maxim.uvarov@linaro.org 
> <mailto:maxim.uvarov@linaro.org>> wrote:
>
>     Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org
>     <mailto:maxim.uvarov@linaro.org>>
>     ---
>     configure.ac <http://configure.ac>                 |   1 -
>      example/Makefile.am          |   2 +-
>      example/l2fwd/.gitignore     |   1 -
>      example/l2fwd/Makefile.am    |  10 -
>      example/l2fwd/odp_l2fwd.c    | 685
>     -------------------------------------------
>      test/performance/.gitignore  |   3 +-
>      test/performance/Makefile.am |   4 +-
>      test/performance/odp_l2fwd.c | 685
>     +++++++++++++++++++++++++++++++++++++++++++
>      8 files changed, 690 insertions(+), 701 deletions(-)
>      delete mode 100644 example/l2fwd/.gitignore
>      delete mode 100644 example/l2fwd/Makefile.am
>      delete mode 100644 example/l2fwd/odp_l2fwd.c
>      create mode 100644 test/performance/odp_l2fwd.c
>
>     diff --git a/configure.ac <http://configure.ac> b/configure.ac
>     <http://configure.ac>
>     index 91083b9..f47ac66 100644
>     --- a/configure.ac <http://configure.ac>
>     +++ b/configure.ac <http://configure.ac>
>     @@ -264,7 +264,6 @@ AC_CONFIG_FILES([Makefile
>                      example/Makefile
>                      example/generator/Makefile
>                      example/ipsec/Makefile
>     -                example/l2fwd/Makefile
>                      example/packet/Makefile
>                      example/timer/Makefile
>                      pkgconfig/libodp.pc
>     diff --git a/example/Makefile.am b/example/Makefile.am
>     index 3021571..6bb4f5c 100644
>     --- a/example/Makefile.am
>     +++ b/example/Makefile.am
>     @@ -1 +1 @@
>     -SUBDIRS = generator ipsec l2fwd packet timer
>     +SUBDIRS = generator ipsec packet timer
>     diff --git a/example/l2fwd/.gitignore b/example/l2fwd/.gitignore
>     deleted file mode 100644
>     index 8563319..0000000
>     --- a/example/l2fwd/.gitignore
>     +++ /dev/null
>     @@ -1 +0,0 @@
>     -odp_l2fwd
>     diff --git a/example/l2fwd/Makefile.am b/example/l2fwd/Makefile.am
>     deleted file mode 100644
>     index feced2a..0000000
>     --- a/example/l2fwd/Makefile.am
>     +++ /dev/null
>     @@ -1,10 +0,0 @@
>     -include $(top_srcdir)/example/Makefile.inc
>     -
>     -bin_PROGRAMS = odp_l2fwd
>     -odp_l2fwd_LDFLAGS = $(AM_LDFLAGS) -static
>     -odp_l2fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
>     -
>     -noinst_HEADERS = \
>     -                 $(top_srcdir)/example/example_debug.h
>     -
>     -dist_odp_l2fwd_SOURCES = odp_l2fwd.c
>     diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c
>     deleted file mode 100644
>     index 4a49008..0000000
>     --- a/example/l2fwd/odp_l2fwd.c
>     +++ /dev/null
>     @@ -1,685 +0,0 @@
>     -/* Copyright (c) 2014, Linaro Limited
>     - * All rights reserved.
>     - *
>     - * SPDX-License-Identifier:     BSD-3-Clause
>     - */
>     -
>     -/**
>     - * @file
>     - *
>     - * @example odp_l2fwd.c  ODP basic forwarding application
>     - */
>     -
>     -/** enable strtok */
>     -#define _POSIX_C_SOURCE 200112L
>     -
>     -#include <stdlib.h>
>     -#include <getopt.h>
>     -#include <unistd.h>
>     -#include <errno.h>
>     -
>     -#include <example_debug.h>
>     -
>     -#include <odp.h>
>     -#include <odp/helper/linux.h>
>     -#include <odp/helper/eth.h>
>     -#include <odp/helper/ip.h>
>     -
>     -/** @def MAX_WORKERS
>     - * @brief Maximum number of worker threads
>     - */
>     -#define MAX_WORKERS            32
>     -
>     -/** @def SHM_PKT_POOL_SIZE
>     - * @brief Size of the shared memory block
>     - */
>     -#define SHM_PKT_POOL_SIZE      (512*2048)
>     -
>     -/** @def SHM_PKT_POOL_BUF_SIZE
>     - * @brief Buffer size of the packet pool buffer
>     - */
>     -#define SHM_PKT_POOL_BUF_SIZE  1856
>     -
>     -/** @def MAX_PKT_BURST
>     - * @brief Maximum number of packet bursts
>     - */
>     -#define MAX_PKT_BURST          16
>     -
>     -/** @def APPL_MODE_PKT_BURST
>     - * @brief The application will handle pakcets in bursts
>     - */
>     -#define APPL_MODE_PKT_BURST    0
>     -
>     -/** @def APPL_MODE_PKT_QUEUE
>     - * @brief The application will handle packets in queues
>     - */
>     -#define APPL_MODE_PKT_QUEUE    1
>     -
>     -/** @def PRINT_APPL_MODE(x)
>     - * @brief Macro to print the current status of how the
>     application handles
>     - * packets.
>     - */
>     -#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
>     -
>     -/** 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))
>     -/**
>     - * Parsed command line application arguments
>     - */
>     -typedef struct {
>     -       int cpu_count;
>     -       int if_count;           /**< Number of interfaces to be
>     used */
>     -       char **if_names;        /**< Array of pointers to
>     interface names */
>     -       int mode;               /**< Packet IO mode */
>     -       int time;               /**< Time in seconds to run. */
>     -       int accuracy;           /**< Number of seconds to get and
>     print statistics */
>     -       char *if_str;           /**< Storage for interface names */
>     -} appl_args_t;
>     -
>     -static int exit_threads;       /**< Break workers loop if set to 1 */
>     -
>     -/**
>     - * Statistics
>     - */
>     -typedef struct {
>     -       uint64_t packets;       /**< Number of forwarded packets. */
>     -       uint64_t drops;         /**< Number of dropped packets. */
>     -} stats_t;
>     -
>     -/**
>     - * Thread specific arguments
>     - */
>     -typedef struct {
>     -       int src_idx;            /**< Source interface identifier */
>     -       stats_t **stats;        /**< Per thread packet stats */
>     -} thread_args_t;
>     -
>     -/**
>     - * Grouping of all global data
>     - */
>     -typedef struct {
>     -       /** Application (parsed) arguments */
>     -       appl_args_t appl;
>     -       /** Thread specific arguments */
>     -       thread_args_t thread[MAX_WORKERS];
>     -       /** Table of pktio handles */
>     -       odp_pktio_t pktios[ODP_CONFIG_PKTIO_ENTRIES];
>     -} args_t;
>     -
>     -/** Global pointer to args */
>     -static args_t *gbl_args;
>     -
>     -/* helper funcs */
>     -static inline odp_queue_t lookup_dest_q(odp_packet_t pkt);
>     -static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
>     -static void parse_args(int argc, char *argv[], appl_args_t
>     *appl_args);
>     -static void print_info(char *progname, appl_args_t *appl_args);
>     -static void usage(char *progname);
>     -
>     -/**
>     - * Packet IO worker thread using ODP queues
>     - *
>     - * @param arg  thread arguments of type 'thread_args_t *'
>     - */
>     -static void *pktio_queue_thread(void *arg)
>     -{
>     -       int thr;
>     -       odp_queue_t outq_def;
>     -       odp_packet_t pkt;
>     -       odp_event_t ev;
>     -       thread_args_t *thr_args = arg;
>     -
>     -       stats_t *stats = calloc(1, sizeof(stats_t));
>     -       *thr_args->stats = stats;
>     -
>     -       thr = odp_thread_id();
>     -
>     -       printf("[%02i] QUEUE mode\n", thr);
>     -
>     -       /* Loop packets */
>     -       while (!exit_threads) {
>     -               /* Use schedule to get buf from any input queue */
>     -               ev  = odp_schedule(NULL, ODP_SCHED_WAIT);
>     -               pkt = odp_packet_from_event(ev);
>     -
>     -               /* Drop packets with errors */
>     -               if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
>     -                       stats->drops += 1;
>     -                       continue;
>     -               }
>     -
>     -               outq_def = lookup_dest_q(pkt);
>     -
>     -               /* Enqueue the packet for output */
>     -               odp_queue_enq(outq_def, ev);
>     -
>     -               stats->packets += 1;
>     -       }
>     -
>     -       free(stats);
>     -       return NULL;
>     -}
>     -
>     -/**
>     - * Lookup the destination pktio for a given packet
>     - */
>     -static inline odp_queue_t lookup_dest_q(odp_packet_t pkt)
>     -{
>     -       int i, src_idx, dst_idx;
>     -       odp_pktio_t pktio_src, pktio_dst;
>     -
>     -       pktio_src = odp_packet_input(pkt);
>     -
>     -       for (src_idx = -1, i = 0; gbl_args->pktios[i] !=
>     ODP_PKTIO_INVALID; ++i)
>     -               if (gbl_args->pktios[i] == pktio_src)
>     -                       src_idx = i;
>     -
>     -       if (src_idx == -1)
>     -               EXAMPLE_ABORT("Failed to determine pktio input\n");
>     -
>     -       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
>     -       pktio_dst = gbl_args->pktios[dst_idx];
>     -
>     -       return odp_pktio_outq_getdef(pktio_dst);
>     -}
>     -
>     -/**
>     - * Packet IO worker thread using bursts from/to IO resources
>     - *
>     - * @param arg  thread arguments of type 'thread_args_t *'
>     - */
>     -static void *pktio_ifburst_thread(void *arg)
>     -{
>     -       int thr;
>     -       thread_args_t *thr_args;
>     -       int pkts, pkts_ok;
>     -       odp_packet_t pkt_tbl[MAX_PKT_BURST];
>     -       int src_idx, dst_idx;
>     -       odp_pktio_t pktio_src, pktio_dst;
>     -
>     -       thr = odp_thread_id();
>     -       thr_args = arg;
>     -
>     -       stats_t *stats = calloc(1, sizeof(stats_t));
>     -       *thr_args->stats = stats;
>     -
>     -       src_idx = thr_args->src_idx;
>     -       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
>     -       pktio_src = gbl_args->pktios[src_idx];
>     -       pktio_dst = gbl_args->pktios[dst_idx];
>     -
>     -       printf("[%02i] srcif:%s dstif:%s spktio:%02" PRIu64
>     -              " dpktio:%02" PRIu64 " BURST mode\n",
>     -              thr,
>     -              gbl_args->appl.if_names[src_idx],
>     -              gbl_args->appl.if_names[dst_idx],
>     -              odp_pktio_to_u64(pktio_src),
>     odp_pktio_to_u64(pktio_dst));
>     -
>     -       /* Loop packets */
>     -       while (!exit_threads) {
>     -               pkts = odp_pktio_recv(pktio_src, pkt_tbl,
>     MAX_PKT_BURST);
>     -               if (pkts <= 0)
>     -                       continue;
>     -
>     -               /* Drop packets with errors */
>     -               pkts_ok = drop_err_pkts(pkt_tbl, pkts);
>     -               if (pkts_ok > 0)
>     -                       odp_pktio_send(pktio_dst, pkt_tbl, pkts_ok);
>     -
>     -               if (odp_unlikely(pkts_ok != pkts))
>     -                       stats->drops += pkts - pkts_ok;
>     -
>     -               if (pkts_ok == 0)
>     -                       continue;
>     -
>     -               stats->packets += pkts_ok;
>     -       }
>     -
>     -       free(stats);
>     -       return NULL;
>     -}
>     -
>     -/**
>     - * Create a pktio handle, optionally associating a default input
>     queue.
>     - *
>     - * @param dev Name of device to open
>     - * @param pool Pool to associate with device for packet RX/TX
>     - * @param mode Packet processing mode for this device (BURST or
>     QUEUE)
>     - *
>     - * @return The handle of the created pktio object.
>     - * @retval ODP_PKTIO_INVALID if the create fails.
>     - */
>     -static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool,
>     -                               int mode)
>     -{
>     -       char inq_name[ODP_QUEUE_NAME_LEN];
>     -       odp_queue_param_t qparam;
>     -       odp_queue_t inq_def;
>     -       odp_pktio_t pktio;
>     -       int ret;
>     -
>     -       pktio = odp_pktio_open(dev, pool);
>     -       if (pktio == ODP_PKTIO_INVALID) {
>     -               EXAMPLE_ERR("Error: failed to open %s\n", dev);
>     -               return ODP_PKTIO_INVALID;
>     -       }
>     -
>     -       printf("created pktio %" PRIu64 " (%s)\n",
>     -              odp_pktio_to_u64(pktio), dev);
>     -
>     -       /* no further setup needed for burst mode */
>     -       if (mode == APPL_MODE_PKT_BURST)
>     -               return pktio;
>     -
>     -       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
>     -       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
>     -       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
>     -       snprintf(inq_name, sizeof(inq_name), "%" PRIu64
>     "-pktio_inq_def",
>     -                odp_pktio_to_u64(pktio));
>     -       inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
>     -
>     -       inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN,
>     &qparam);
>     -       if (inq_def == ODP_QUEUE_INVALID) {
>     -               EXAMPLE_ERR("Error: pktio queue creation failed\n");
>     -               return ODP_PKTIO_INVALID;
>     -       }
>     -
>     -       ret = odp_pktio_inq_setdef(pktio, inq_def);
>     -       if (ret != 0) {
>     -               EXAMPLE_ERR("Error: default input-Q setup\n");
>     -               return ODP_PKTIO_INVALID;
>     -       }
>     -
>     -       return pktio;
>     -}
>     -
>     -/**
>     - *  Print statistics
>     - *
>     - * @param num_workers Number of worker threads
>     - * @param thr_stats Pointer to stats storage
>     - * @param duration Number of seconds to loop in
>     - * @param timeout Number of seconds for stats calculation
>     - *
>     - */
>     -static void print_speed_stats(int num_workers, stats_t **thr_stats,
>     -                             int duration, int timeout)
>     -{
>     -       uint64_t pkts, pkts_prev = 0, pps, drops, maximum_pps = 0;
>     -       int i, elapsed = 0;
>     -       int loop_forever = (duration == 0);
>     -
>     -       do {
>     -               pkts = 0;
>     -               drops = 0;
>     -
>     -               sleep(timeout);
>     -
>     -               for (i = 0; i < num_workers; i++) {
>     -                       pkts += thr_stats[i]->packets;
>     -                       drops += thr_stats[i]->drops;
>     -               }
>     -               pps = (pkts - pkts_prev) / timeout;
>     -               if (pps > maximum_pps)
>     -                       maximum_pps = pps;
>     -               printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,
>     -                      maximum_pps);
>     -
>     -               printf(" %" PRIu64 " total drops\n", drops);
>     -
>     -               elapsed += timeout;
>     -               pkts_prev = pkts;
>     -       } while (loop_forever || (elapsed < duration));
>     -
>     -       printf("TEST RESULT: %" PRIu64 " maximum packets per
>     second.\n",
>     -              maximum_pps);
>     -       return;
>     -}
>     -
>     -/**
>     - * ODP L2 forwarding main function
>     - */
>     -int main(int argc, char *argv[])
>     -{
>     -       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>     -       odp_pool_t pool;
>     -       int i;
>     -       int cpu;
>     -       int num_workers;
>     -       odp_shm_t shm;
>     -       odp_cpumask_t cpumask;
>     -       char cpumaskstr[ODP_CPUMASK_STR_SIZE];
>     -       odp_pool_param_t params;
>     -
>     -       /* Init ODP before calling anything else */
>     -       if (odp_init_global(NULL, NULL)) {
>     -               EXAMPLE_ERR("Error: ODP global init failed.\n");
>     -               exit(EXIT_FAILURE);
>     -       }
>     -
>     -       /* Init this thread */
>     -       if (odp_init_local()) {
>     -               EXAMPLE_ERR("Error: ODP local init failed.\n");
>     -               exit(EXIT_FAILURE);
>     -       }
>     -
>     -       /* Reserve memory for args from shared mem */
>     -       shm = odp_shm_reserve("shm_args", sizeof(args_t),
>     -                             ODP_CACHE_LINE_SIZE, 0);
>     -       gbl_args = odp_shm_addr(shm);
>     -
>     -       if (gbl_args == NULL) {
>     -               EXAMPLE_ERR("Error: shared mem alloc failed.\n");
>     -               exit(EXIT_FAILURE);
>     -       }
>     -       memset(gbl_args, 0, sizeof(*gbl_args));
>     -
>     -       /* Parse and store the application arguments */
>     -       parse_args(argc, argv, &gbl_args->appl);
>     -
>     -       /* Print both system and application information */
>     -       print_info(NO_PATH(argv[0]), &gbl_args->appl);
>     -
>     -       /* Default to system CPU count unless user specified */
>     -       num_workers = MAX_WORKERS;
>     -       if (gbl_args->appl.cpu_count)
>     -               num_workers = gbl_args->appl.cpu_count;
>     -
>     -       /*
>     -        * By default CPU #0 runs Linux kernel background tasks.
>     -        * Start mapping thread from CPU #1
>     -        */
>     -       num_workers = odph_linux_cpumask_default(&cpumask,
>     num_workers);
>     -       (void)odp_cpumask_to_str(&cpumask, cpumaskstr,
>     sizeof(cpumaskstr));
>     -
>     -       printf("num worker threads: %i\n", num_workers);
>     -       printf("first CPU:          %i\n",
>     odp_cpumask_first(&cpumask));
>     -       printf("cpu mask:           %s\n", cpumaskstr);
>     -
>     -       if (num_workers < gbl_args->appl.if_count) {
>     -               EXAMPLE_ERR("Error: CPU count %d less than
>     interface count\n",
>     -                           num_workers);
>     -               exit(EXIT_FAILURE);
>     -       }
>     -       if (gbl_args->appl.if_count % 2 != 0) {
>     -               EXAMPLE_ERR("Error: interface count %d is odd in
>     fwd appl.\n",
>     -                           gbl_args->appl.if_count);
>     -               exit(EXIT_FAILURE);
>     -       }
>     -
>     -       /* Create packet pool */
>     -       memset(&params, 0, sizeof(params));
>     -       params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
>     -       params.pkt.len     = SHM_PKT_POOL_BUF_SIZE;
>     -       params.pkt.num     = SHM_PKT_POOL_SIZE/SHM_PKT_POOL_BUF_SIZE;
>     -       params.type        = ODP_POOL_PACKET;
>     -
>     -       pool = odp_pool_create("packet pool", ODP_SHM_NULL, &params);
>     -
>     -       if (pool == ODP_POOL_INVALID) {
>     -               EXAMPLE_ERR("Error: packet pool create failed.\n");
>     -               exit(EXIT_FAILURE);
>     -       }
>     -       odp_pool_print(pool);
>     -
>     -       for (i = 0; i < gbl_args->appl.if_count; ++i) {
>     -               gbl_args->pktios[i] =
>     create_pktio(gbl_args->appl.if_names[i],
>     -                                                  pool,
>     gbl_args->appl.mode);
>     -               if (gbl_args->pktios[i] == ODP_PKTIO_INVALID)
>     -                       exit(EXIT_FAILURE);
>     -       }
>     -       gbl_args->pktios[i] = ODP_PKTIO_INVALID;
>     -
>     -       memset(thread_tbl, 0, sizeof(thread_tbl));
>     -
>     -       stats_t **stats = calloc(1, sizeof(stats_t) * num_workers);
>     -
>     -       /* Create worker threads */
>     -       cpu = odp_cpumask_first(&cpumask);
>     -       for (i = 0; i < num_workers; ++i) {
>     -               odp_cpumask_t thd_mask;
>     -               void *(*thr_run_func) (void *);
>     -
>     -               if (gbl_args->appl.mode == APPL_MODE_PKT_BURST)
>     -                       thr_run_func = pktio_ifburst_thread;
>     -               else /* APPL_MODE_PKT_QUEUE */
>     -                       thr_run_func = pktio_queue_thread;
>     -
>     -               gbl_args->thread[i].src_idx = i %
>     gbl_args->appl.if_count;
>     -               gbl_args->thread[i].stats = &stats[i];
>     -
>     -               odp_cpumask_zero(&thd_mask);
>     -               odp_cpumask_set(&thd_mask, cpu);
>     -  odph_linux_pthread_create(&thread_tbl[i], &thd_mask,
>     -                                         thr_run_func,
>     -  &gbl_args->thread[i]);
>     -               cpu = odp_cpumask_next(&cpumask, cpu);
>     -       }
>     -
>     -       print_speed_stats(num_workers, stats, gbl_args->appl.time,
>     -                         gbl_args->appl.accuracy);
>     -       free(stats);
>     -       exit_threads = 1;
>     -
>     -       /* Master thread waits for other threads to exit */
>     -       odph_linux_pthread_join(thread_tbl, num_workers);
>     -
>     -       free(gbl_args->appl.if_names);
>     -       free(gbl_args->appl.if_str);
>     -       printf("Exit\n\n");
>     -
>     -       return 0;
>     -}
>     -
>     -/**
>     - * 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 packet
>     - * @param len      Length of pkt_tbl[]
>     - *
>     - * @return Number of packets with no detected error
>     - */
>     -static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
>     -{
>     -       odp_packet_t pkt;
>     -       unsigned pkt_cnt = len;
>     -       unsigned i, j;
>     -
>     -       for (i = 0, j = 0; i < len; ++i) {
>     -               pkt = pkt_tbl[i];
>     -
>     -               if (odp_unlikely(odp_packet_has_error(pkt))) {
>     -                       odp_packet_free(pkt); /* Drop */
>     -                       pkt_cnt--;
>     -               } else if (odp_unlikely(i != j++)) {
>     -                       pkt_tbl[j-1] = pkt;
>     -               }
>     -       }
>     -
>     -       return pkt_cnt;
>     -}
>     -
>     -/**
>     - * Parse and store the command line arguments
>     - *
>     - * @param argc       argument count
>     - * @param argv[]     argument vector
>     - * @param appl_args  Store application arguments here
>     - */
>     -static void parse_args(int argc, char *argv[], appl_args_t
>     *appl_args)
>     -{
>     -       int opt;
>     -       int long_index;
>     -       char *token;
>     -       size_t len;
>     -       int i;
>     -       static struct option longopts[] = {
>     -               {"count", required_argument, NULL, 'c'},
>     -               {"time", required_argument, NULL, 't'},
>     -               {"accuracy", required_argument, NULL, 'a'},
>     -               {"interface", required_argument, NULL, 'i'},    /*
>     return 'i' */
>     -               {"mode", required_argument, NULL, 'm'},        /*
>     return 'm' */
>     -               {"help", no_argument, NULL, 'h'},        /* return
>     'h' */
>     -               {NULL, 0, NULL, 0}
>     -       };
>     -
>     -       appl_args->time = 0; /* loop forever if time to run is 0 */
>     -       appl_args->accuracy = 1; /* get and print pps stats second */
>     -       appl_args->mode = -1; /* Invalid, must be changed by
>     parsing */
>     -
>     -       while (1) {
>     -               opt = getopt_long(argc, argv, "+c:+t:+a:i:m:h",
>     -                                 longopts, &long_index);
>     -
>     -               if (opt == -1)
>     -                       break;  /* No more options */
>     -
>     -               switch (opt) {
>     -               case 'c':
>     -                       appl_args->cpu_count = atoi(optarg);
>     -                       break;
>     -               case 't':
>     -                       appl_args->time = atoi(optarg);
>     -                       break;
>     -               case 'a':
>     -                       appl_args->accuracy = atoi(optarg);
>     -                       break;
>     -                       /* parse packet-io interface names */
>     -               case 'i':
>     -                       len = strlen(optarg);
>     -                       if (len == 0) {
>     -                               usage(argv[0]);
>     -                               exit(EXIT_FAILURE);
>     -                       }
>     -                       len += 1;       /* add room for '\0' */
>     -
>     -                       appl_args->if_str = malloc(len);
>     -                       if (appl_args->if_str == NULL) {
>     -                               usage(argv[0]);
>     -                               exit(EXIT_FAILURE);
>     -                       }
>     -
>     -                       /* count the number of tokens separated by
>     ',' */
>     -                       strcpy(appl_args->if_str, optarg);
>     -                       for (token = strtok(appl_args->if_str,
>     ","), i = 0;
>     -                            token != NULL;
>     -                            token = strtok(NULL, ","), i++)
>     -                               ;
>     -
>     -                       appl_args->if_count = i;
>     -
>     -                       if (appl_args->if_count == 0) {
>     -                               usage(argv[0]);
>     -                               exit(EXIT_FAILURE);
>     -                       }
>     -
>     -                       /* allocate storage for the if names */
>     -                       appl_args->if_names =
>     -  calloc(appl_args->if_count, sizeof(char *));
>     -
>     -                       /* store the if names (reset names string) */
>     -                       strcpy(appl_args->if_str, optarg);
>     -                       for (token = strtok(appl_args->if_str,
>     ","), i = 0;
>     -                            token != NULL; token = strtok(NULL,
>     ","), i++) {
>     -  appl_args->if_names[i] = token;
>     -                       }
>     -                       break;
>     -
>     -               case 'm':
>     -                       i = atoi(optarg);
>     -                       if (i == 0)
>     -                               appl_args->mode = APPL_MODE_PKT_BURST;
>     -                       else
>     -                               appl_args->mode = APPL_MODE_PKT_QUEUE;
>     -                       break;
>     -
>     -               case 'h':
>     -                       usage(argv[0]);
>     -                       exit(EXIT_SUCCESS);
>     -                       break;
>     -
>     -               default:
>     -                       break;
>     -               }
>     -       }
>     -
>     -       if (appl_args->if_count == 0 || appl_args->mode == -1) {
>     -               usage(argv[0]);
>     -               exit(EXIT_FAILURE);
>     -       }
>     -
>     -       optind = 1;             /* reset 'extern optind' from the
>     getopt lib */
>     -}
>     -
>     -/**
>     - * Print system and application info
>     - */
>     -static void print_info(char *progname, appl_args_t *appl_args)
>     -{
>     -       int i;
>     -
>     -       printf("\n"
>     -              "ODP system info\n"
>     -              "---------------\n"
>     -              "ODP API version: %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_sys_cpu_model_str(),
>     odp_sys_cpu_hz(),
>     -              odp_sys_cache_line_size(), odp_cpu_count());
>     -
>     -       printf("Running ODP appl: \"%s\"\n"
>     -              "-----------------\n"
>     -              "IF-count:        %i\n"
>     -              "Using IFs:      ",
>     -              progname, appl_args->if_count);
>     -       for (i = 0; i < appl_args->if_count; ++i)
>     -               printf(" %s", appl_args->if_names[i]);
>     -       printf("\n"
>     -              "Mode:            ");
>     -       if (appl_args->mode == APPL_MODE_PKT_BURST)
>     -               PRINT_APPL_MODE(APPL_MODE_PKT_BURST);
>     -       else
>     -               PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE);
>     -       printf("\n\n");
>     -       fflush(NULL);
>     -}
>     -
>     -/**
>     - * Prinf usage information
>     - */
>     -static void usage(char *progname)
>     -{
>     -       printf("\n"
>     -              "OpenDataPlane L2 forwarding application.\n"
>     -              "\n"
>     -              "Usage: %s OPTIONS\n"
>     -              "  E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
>     -              " In the above example,\n"
>     -              " eth0 will send pkts to eth1 and vice versa\n"
>     -              " eth2 will send pkts to eth3 and vice versa\n"
>     -              "\n"
>     -              "Mandatory OPTIONS:\n"
>     -              "  -i, --interface Eth interfaces (comma-separated,
>     no spaces)\n"
>     -              "  -m, --mode      0: Burst send&receive packets
>     (no queues)\n"
>     -              "                  1: Send&receive packets through
>     ODP queues.\n"
>     -              "\n"
>     -              "Optional OPTIONS\n"
>     -              "  -c, --count <number> CPU count.\n"
>     -              "  -t, --time  <number> Time in seconds to run.\n"
>     -              "  -a, --accuracy <number> Time in seconds get
>     print statistics\n"
>     -              "                          (default is 1 second).\n"
>     -              "  -h, --help           Display help and exit.\n\n"
>     -              " environment variables:
>     ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
>     -              " ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
>     -              " ODP_PKTIO_DISABLE_SOCKET_BASIC\n"
>     -              " can be used to advanced pkt I/O selection for
>     linux-generic\n"
>     -              "\n", NO_PATH(progname), NO_PATH(progname)
>     -           );
>     -}
>     diff --git a/test/performance/.gitignore b/test/performance/.gitignore
>     index 1bdb90d..d41d1e1 100644
>     --- a/test/performance/.gitignore
>     +++ b/test/performance/.gitignore
>     @@ -1,4 +1,5 @@
>      *.log
>      *.trs
>     -odp_scheduling
>      odp_atomic
>     +odp_l2fwd
>     +odp_scheduling
>     diff --git a/test/performance/Makefile.am
>     b/test/performance/Makefile.am
>     index b0f7457..b501584 100644
>     --- a/test/performance/Makefile.am
>     +++ b/test/performance/Makefile.am
>     @@ -1,10 +1,10 @@
>      include $(top_srcdir)/test/Makefile.inc
>
>     -TESTS_ENVIRONMENT = TEST_DIR=${builddir}
>     +TESTS_ENVIRONMENT = TEST_DIR=${builddir}
>     ODP_PLATFORM=${with_platform}
>
>      EXECUTABLES = odp_atomic
>
>     -COMPILE_ONLY = odp_scheduling
>     +COMPILE_ONLY = odp_scheduling odp_l2fwd
>
>      TESTSCRIPTS = odp_scheduling_run
>
>     diff --git a/test/performance/odp_l2fwd.c
>     b/test/performance/odp_l2fwd.c
>     new file mode 100644
>     index 0000000..5d4b833
>     --- /dev/null
>     +++ b/test/performance/odp_l2fwd.c
>     @@ -0,0 +1,685 @@
>     +/* Copyright (c) 2014, Linaro Limited
>     + * All rights reserved.
>     + *
>     + * SPDX-License-Identifier:     BSD-3-Clause
>     + */
>     +
>     +/**
>     + * @file
>     + *
>     + * @example odp_l2fwd.c  ODP basic forwarding application
>     + */
>     +
>     +/** enable strtok */
>     +#define _POSIX_C_SOURCE 200112L
>     +
>     +#include <stdlib.h>
>     +#include <getopt.h>
>     +#include <unistd.h>
>     +#include <errno.h>
>     +
>     +#include <test_debug.h>
>     +
>     +#include <odp.h>
>     +#include <odp/helper/linux.h>
>     +#include <odp/helper/eth.h>
>     +#include <odp/helper/ip.h>
>     +
>     +/** @def MAX_WORKERS
>     + * @brief Maximum number of worker threads
>     + */
>     +#define MAX_WORKERS            32
>     +
>     +/** @def SHM_PKT_POOL_SIZE
>     + * @brief Size of the shared memory block
>     + */
>     +#define SHM_PKT_POOL_SIZE      (512*2048)
>     +
>     +/** @def SHM_PKT_POOL_BUF_SIZE
>     + * @brief Buffer size of the packet pool buffer
>     + */
>     +#define SHM_PKT_POOL_BUF_SIZE  1856
>     +
>     +/** @def MAX_PKT_BURST
>     + * @brief Maximum number of packet bursts
>     + */
>     +#define MAX_PKT_BURST          16
>     +
>     +/** @def APPL_MODE_PKT_BURST
>     + * @brief The application will handle pakcets in bursts
>     + */
>     +#define APPL_MODE_PKT_BURST    0
>     +
>     +/** @def APPL_MODE_PKT_QUEUE
>     + * @brief The application will handle packets in queues
>     + */
>     +#define APPL_MODE_PKT_QUEUE    1
>     +
>     +/** @def PRINT_APPL_MODE(x)
>     + * @brief Macro to print the current status of how the
>     application handles
>     + * packets.
>     + */
>     +#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
>     +
>     +/** 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))
>     +/**
>     + * Parsed command line application arguments
>     + */
>     +typedef struct {
>     +       int cpu_count;
>     +       int if_count;           /**< Number of interfaces to be
>     used */
>     +       char **if_names;        /**< Array of pointers to
>     interface names */
>     +       int mode;               /**< Packet IO mode */
>     +       int time;               /**< Time in seconds to run. */
>     +       int accuracy;           /**< Number of seconds to get and
>     print statistics */
>     +       char *if_str;           /**< Storage for interface names */
>     +} appl_args_t;
>     +
>     +static int exit_threads;       /**< Break workers loop if set to 1 */
>     +
>     +/**
>     + * Statistics
>     + */
>     +typedef struct {
>     +       uint64_t packets;       /**< Number of forwarded packets. */
>     +       uint64_t drops;         /**< Number of dropped packets. */
>     +} stats_t;
>     +
>     +/**
>     + * Thread specific arguments
>     + */
>     +typedef struct {
>     +       int src_idx;            /**< Source interface identifier */
>     +       stats_t **stats;        /**< Per thread packet stats */
>     +} thread_args_t;
>     +
>     +/**
>     + * Grouping of all global data
>     + */
>     +typedef struct {
>     +       /** Application (parsed) arguments */
>     +       appl_args_t appl;
>     +       /** Thread specific arguments */
>     +       thread_args_t thread[MAX_WORKERS];
>     +       /** Table of pktio handles */
>     +       odp_pktio_t pktios[ODP_CONFIG_PKTIO_ENTRIES];
>     +} args_t;
>     +
>     +/** Global pointer to args */
>     +static args_t *gbl_args;
>     +
>     +/* helper funcs */
>     +static inline odp_queue_t lookup_dest_q(odp_packet_t pkt);
>     +static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
>     +static void parse_args(int argc, char *argv[], appl_args_t
>     *appl_args);
>     +static void print_info(char *progname, appl_args_t *appl_args);
>     +static void usage(char *progname);
>     +
>     +/**
>     + * Packet IO worker thread using ODP queues
>     + *
>     + * @param arg  thread arguments of type 'thread_args_t *'
>     + */
>     +static void *pktio_queue_thread(void *arg)
>     +{
>     +       int thr;
>     +       odp_queue_t outq_def;
>     +       odp_packet_t pkt;
>     +       odp_event_t ev;
>     +       thread_args_t *thr_args = arg;
>     +
>     +       stats_t *stats = calloc(1, sizeof(stats_t));
>     +       *thr_args->stats = stats;
>     +
>     +       thr = odp_thread_id();
>     +
>     +       printf("[%02i] QUEUE mode\n", thr);
>     +
>     +       /* Loop packets */
>     +       while (!exit_threads) {
>     +               /* Use schedule to get buf from any input queue */
>     +               ev  = odp_schedule(NULL, ODP_SCHED_WAIT);
>     +               pkt = odp_packet_from_event(ev);
>     +
>     +               /* Drop packets with errors */
>     +               if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
>     +                       stats->drops += 1;
>     +                       continue;
>     +               }
>     +
>     +               outq_def = lookup_dest_q(pkt);
>     +
>     +               /* Enqueue the packet for output */
>     +               odp_queue_enq(outq_def, ev);
>     +
>     +               stats->packets += 1;
>     +       }
>     +
>     +       free(stats);
>     +       return NULL;
>     +}
>     +
>     +/**
>     + * Lookup the destination pktio for a given packet
>     + */
>     +static inline odp_queue_t lookup_dest_q(odp_packet_t pkt)
>     +{
>     +       int i, src_idx, dst_idx;
>     +       odp_pktio_t pktio_src, pktio_dst;
>     +
>     +       pktio_src = odp_packet_input(pkt);
>     +
>     +       for (src_idx = -1, i = 0; gbl_args->pktios[i] !=
>     ODP_PKTIO_INVALID; ++i)
>     +               if (gbl_args->pktios[i] == pktio_src)
>     +                       src_idx = i;
>     +
>     +       if (src_idx == -1)
>     +               LOG_ABORT("Failed to determine pktio input\n");
>     +
>     +       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
>     +       pktio_dst = gbl_args->pktios[dst_idx];
>     +
>     +       return odp_pktio_outq_getdef(pktio_dst);
>     +}
>     +
>     +/**
>     + * Packet IO worker thread using bursts from/to IO resources
>     + *
>     + * @param arg  thread arguments of type 'thread_args_t *'
>     + */
>     +static void *pktio_ifburst_thread(void *arg)
>     +{
>     +       int thr;
>     +       thread_args_t *thr_args;
>     +       int pkts, pkts_ok;
>     +       odp_packet_t pkt_tbl[MAX_PKT_BURST];
>     +       int src_idx, dst_idx;
>     +       odp_pktio_t pktio_src, pktio_dst;
>     +
>     +       thr = odp_thread_id();
>     +       thr_args = arg;
>     +
>     +       stats_t *stats = calloc(1, sizeof(stats_t));
>     +       *thr_args->stats = stats;
>     +
>     +       src_idx = thr_args->src_idx;
>     +       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
>     +       pktio_src = gbl_args->pktios[src_idx];
>     +       pktio_dst = gbl_args->pktios[dst_idx];
>     +
>     +       printf("[%02i] srcif:%s dstif:%s spktio:%02" PRIu64
>     +              " dpktio:%02" PRIu64 " BURST mode\n",
>     +              thr,
>     +              gbl_args->appl.if_names[src_idx],
>     +              gbl_args->appl.if_names[dst_idx],
>     +              odp_pktio_to_u64(pktio_src),
>     odp_pktio_to_u64(pktio_dst));
>     +
>     +       /* Loop packets */
>     +       while (!exit_threads) {
>     +               pkts = odp_pktio_recv(pktio_src, pkt_tbl,
>     MAX_PKT_BURST);
>     +               if (pkts <= 0)
>     +                       continue;
>     +
>     +               /* Drop packets with errors */
>     +               pkts_ok = drop_err_pkts(pkt_tbl, pkts);
>     +               if (pkts_ok > 0)
>     +                       odp_pktio_send(pktio_dst, pkt_tbl, pkts_ok);
>     +
>     +               if (odp_unlikely(pkts_ok != pkts))
>     +                       stats->drops += pkts - pkts_ok;
>     +
>     +               if (pkts_ok == 0)
>     +                       continue;
>     +
>     +               stats->packets += pkts_ok;
>     +       }
>     +
>     +       free(stats);
>     +       return NULL;
>     +}
>     +
>     +/**
>     + * Create a pktio handle, optionally associating a default input
>     queue.
>     + *
>     + * @param dev Name of device to open
>     + * @param pool Pool to associate with device for packet RX/TX
>     + * @param mode Packet processing mode for this device (BURST or
>     QUEUE)
>     + *
>     + * @return The handle of the created pktio object.
>     + * @retval ODP_PKTIO_INVALID if the create fails.
>     + */
>     +static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool,
>     +                               int mode)
>     +{
>     +       char inq_name[ODP_QUEUE_NAME_LEN];
>     +       odp_queue_param_t qparam;
>     +       odp_queue_t inq_def;
>     +       odp_pktio_t pktio;
>     +       int ret;
>     +
>     +       pktio = odp_pktio_open(dev, pool);
>     +       if (pktio == ODP_PKTIO_INVALID) {
>     +               LOG_ERR("Error: failed to open %s\n", dev);
>     +               return ODP_PKTIO_INVALID;
>     +       }
>     +
>     +       printf("created pktio %" PRIu64 " (%s)\n",
>     +              odp_pktio_to_u64(pktio), dev);
>     +
>     +       /* no further setup needed for burst mode */
>     +       if (mode == APPL_MODE_PKT_BURST)
>     +               return pktio;
>     +
>     +       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
>     +       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
>     +       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
>     +       snprintf(inq_name, sizeof(inq_name), "%" PRIu64
>     "-pktio_inq_def",
>     +                odp_pktio_to_u64(pktio));
>     +       inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
>     +
>     +       inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN,
>     &qparam);
>     +       if (inq_def == ODP_QUEUE_INVALID) {
>     +               LOG_ERR("Error: pktio queue creation failed\n");
>     +               return ODP_PKTIO_INVALID;
>     +       }
>     +
>     +       ret = odp_pktio_inq_setdef(pktio, inq_def);
>     +       if (ret != 0) {
>     +               LOG_ERR("Error: default input-Q setup\n");
>     +               return ODP_PKTIO_INVALID;
>     +       }
>     +
>     +       return pktio;
>     +}
>     +
>     +/**
>     + *  Print statistics
>     + *
>     + * @param num_workers Number of worker threads
>     + * @param thr_stats Pointer to stats storage
>     + * @param duration Number of seconds to loop in
>     + * @param timeout Number of seconds for stats calculation
>     + *
>     + */
>     +static void print_speed_stats(int num_workers, stats_t **thr_stats,
>     +                             int duration, int timeout)
>     +{
>     +       uint64_t pkts, pkts_prev = 0, pps, drops, maximum_pps = 0;
>     +       int i, elapsed = 0;
>     +       int loop_forever = (duration == 0);
>     +
>     +       do {
>     +               pkts = 0;
>     +               drops = 0;
>     +
>     +               sleep(timeout);
>     +
>     +               for (i = 0; i < num_workers; i++) {
>     +                       pkts += thr_stats[i]->packets;
>     +                       drops += thr_stats[i]->drops;
>     +               }
>     +               pps = (pkts - pkts_prev) / timeout;
>     +               if (pps > maximum_pps)
>     +                       maximum_pps = pps;
>     +               printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,
>     +                      maximum_pps);
>     +
>     +               printf(" %" PRIu64 " total drops\n", drops);
>     +
>     +               elapsed += timeout;
>     +               pkts_prev = pkts;
>     +       } while (loop_forever || (elapsed < duration));
>     +
>     +       printf("TEST RESULT: %" PRIu64 " maximum packets per
>     second.\n",
>     +              maximum_pps);
>     +       return;
>     +}
>     +
>     +/**
>     + * ODP L2 forwarding main function
>     + */
>     +int main(int argc, char *argv[])
>     +{
>     +       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>     +       odp_pool_t pool;
>     +       int i;
>     +       int cpu;
>     +       int num_workers;
>     +       odp_shm_t shm;
>     +       odp_cpumask_t cpumask;
>     +       char cpumaskstr[ODP_CPUMASK_STR_SIZE];
>     +       odp_pool_param_t params;
>     +
>     +       /* Init ODP before calling anything else */
>     +       if (odp_init_global(NULL, NULL)) {
>     +               LOG_ERR("Error: ODP global init failed.\n");
>     +               exit(EXIT_FAILURE);
>     +       }
>     +
>     +       /* Init this thread */
>     +       if (odp_init_local()) {
>     +               LOG_ERR("Error: ODP local init failed.\n");
>     +               exit(EXIT_FAILURE);
>     +       }
>     +
>     +       /* Reserve memory for args from shared mem */
>     +       shm = odp_shm_reserve("shm_args", sizeof(args_t),
>     +                             ODP_CACHE_LINE_SIZE, 0);
>     +       gbl_args = odp_shm_addr(shm);
>     +
>     +       if (gbl_args == NULL) {
>     +               LOG_ERR("Error: shared mem alloc failed.\n");
>     +               exit(EXIT_FAILURE);
>     +       }
>     +       memset(gbl_args, 0, sizeof(*gbl_args));
>     +
>     +       /* Parse and store the application arguments */
>     +       parse_args(argc, argv, &gbl_args->appl);
>     +
>     +       /* Print both system and application information */
>     +       print_info(NO_PATH(argv[0]), &gbl_args->appl);
>     +
>     +       /* Default to system CPU count unless user specified */
>     +       num_workers = MAX_WORKERS;
>     +       if (gbl_args->appl.cpu_count)
>     +               num_workers = gbl_args->appl.cpu_count;
>     +
>     +       /*
>     +        * By default CPU #0 runs Linux kernel background tasks.
>     +        * Start mapping thread from CPU #1
>     +        */
>     +       num_workers = odph_linux_cpumask_default(&cpumask,
>     num_workers);
>     +       (void)odp_cpumask_to_str(&cpumask, cpumaskstr,
>     sizeof(cpumaskstr));
>     +
>     +       printf("num worker threads: %i\n", num_workers);
>     +       printf("first CPU:          %i\n",
>     odp_cpumask_first(&cpumask));
>     +       printf("cpu mask:           %s\n", cpumaskstr);
>     +
>     +       if (num_workers < gbl_args->appl.if_count) {
>     +               LOG_ERR("Error: CPU count %d less than interface
>     count\n",
>     +                       num_workers);
>     +               exit(EXIT_FAILURE);
>     +       }
>     +       if (gbl_args->appl.if_count % 2 != 0) {
>     +               LOG_ERR("Error: interface count %d is odd in fwd
>     appl.\n",
>     +                       gbl_args->appl.if_count);
>     +               exit(EXIT_FAILURE);
>     +       }
>     +
>     +       /* Create packet pool */
>     +       memset(&params, 0, sizeof(params));
>     +       params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
>     +       params.pkt.len     = SHM_PKT_POOL_BUF_SIZE;
>     +       params.pkt.num     = SHM_PKT_POOL_SIZE/SHM_PKT_POOL_BUF_SIZE;
>     +       params.type        = ODP_POOL_PACKET;
>     +
>     +       pool = odp_pool_create("packet pool", ODP_SHM_NULL, &params);
>     +
>     +       if (pool == ODP_POOL_INVALID) {
>     +               LOG_ERR("Error: packet pool create failed.\n");
>     +               exit(EXIT_FAILURE);
>     +       }
>     +       odp_pool_print(pool);
>     +
>     +       for (i = 0; i < gbl_args->appl.if_count; ++i) {
>     +               gbl_args->pktios[i] =
>     create_pktio(gbl_args->appl.if_names[i],
>     +                                                  pool,
>     gbl_args->appl.mode);
>     +               if (gbl_args->pktios[i] == ODP_PKTIO_INVALID)
>     +                       exit(EXIT_FAILURE);
>     +       }
>     +       gbl_args->pktios[i] = ODP_PKTIO_INVALID;
>     +
>     +       memset(thread_tbl, 0, sizeof(thread_tbl));
>     +
>     +       stats_t **stats = calloc(1, sizeof(stats_t) * num_workers);
>     +
>     +       /* Create worker threads */
>     +       cpu = odp_cpumask_first(&cpumask);
>     +       for (i = 0; i < num_workers; ++i) {
>     +               odp_cpumask_t thd_mask;
>     +               void *(*thr_run_func) (void *);
>     +
>     +               if (gbl_args->appl.mode == APPL_MODE_PKT_BURST)
>     +                       thr_run_func = pktio_ifburst_thread;
>     +               else /* APPL_MODE_PKT_QUEUE */
>     +                       thr_run_func = pktio_queue_thread;
>     +
>     +               gbl_args->thread[i].src_idx = i %
>     gbl_args->appl.if_count;
>     +               gbl_args->thread[i].stats = &stats[i];
>     +
>     +               odp_cpumask_zero(&thd_mask);
>     +               odp_cpumask_set(&thd_mask, cpu);
>     +  odph_linux_pthread_create(&thread_tbl[i], &thd_mask,
>     +                                         thr_run_func,
>     +  &gbl_args->thread[i]);
>     +               cpu = odp_cpumask_next(&cpumask, cpu);
>     +       }
>     +
>     +       print_speed_stats(num_workers, stats, gbl_args->appl.time,
>     +                         gbl_args->appl.accuracy);
>     +       free(stats);
>     +       exit_threads = 1;
>     +
>     +       /* Master thread waits for other threads to exit */
>     +       odph_linux_pthread_join(thread_tbl, num_workers);
>     +
>     +       free(gbl_args->appl.if_names);
>     +       free(gbl_args->appl.if_str);
>     +       printf("Exit\n\n");
>     +
>     +       return 0;
>     +}
>     +
>     +/**
>     + * 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 packet
>     + * @param len      Length of pkt_tbl[]
>     + *
>     + * @return Number of packets with no detected error
>     + */
>     +static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
>     +{
>     +       odp_packet_t pkt;
>     +       unsigned pkt_cnt = len;
>     +       unsigned i, j;
>     +
>     +       for (i = 0, j = 0; i < len; ++i) {
>     +               pkt = pkt_tbl[i];
>     +
>     +               if (odp_unlikely(odp_packet_has_error(pkt))) {
>     +                       odp_packet_free(pkt); /* Drop */
>     +                       pkt_cnt--;
>     +               } else if (odp_unlikely(i != j++)) {
>     +                       pkt_tbl[j-1] = pkt;
>     +               }
>     +       }
>     +
>     +       return pkt_cnt;
>     +}
>     +
>     +/**
>     + * Parse and store the command line arguments
>     + *
>     + * @param argc       argument count
>     + * @param argv[]     argument vector
>     + * @param appl_args  Store application arguments here
>     + */
>     +static void parse_args(int argc, char *argv[], appl_args_t
>     *appl_args)
>     +{
>     +       int opt;
>     +       int long_index;
>     +       char *token;
>     +       size_t len;
>     +       int i;
>     +       static struct option longopts[] = {
>     +               {"count", required_argument, NULL, 'c'},
>     +               {"time", required_argument, NULL, 't'},
>     +               {"accuracy", required_argument, NULL, 'a'},
>     +               {"interface", required_argument, NULL, 'i'},    /*
>     return 'i' */
>     +               {"mode", required_argument, NULL, 'm'},        /*
>     return 'm' */
>     +               {"help", no_argument, NULL, 'h'},        /* return
>     'h' */
>     +               {NULL, 0, NULL, 0}
>     +       };
>     +
>     +       appl_args->time = 0; /* loop forever if time to run is 0 */
>     +       appl_args->accuracy = 1; /* get and print pps stats second */
>     +       appl_args->mode = -1; /* Invalid, must be changed by
>     parsing */
>     +
>     +       while (1) {
>     +               opt = getopt_long(argc, argv, "+c:+t:+a:i:m:h",
>     +                                 longopts, &long_index);
>     +
>     +               if (opt == -1)
>     +                       break;  /* No more options */
>     +
>     +               switch (opt) {
>     +               case 'c':
>     +                       appl_args->cpu_count = atoi(optarg);
>     +                       break;
>     +               case 't':
>     +                       appl_args->time = atoi(optarg);
>     +                       break;
>     +               case 'a':
>     +                       appl_args->accuracy = atoi(optarg);
>     +                       break;
>     +                       /* parse packet-io interface names */
>     +               case 'i':
>     +                       len = strlen(optarg);
>     +                       if (len == 0) {
>     +                               usage(argv[0]);
>     +                               exit(EXIT_FAILURE);
>     +                       }
>     +                       len += 1;       /* add room for '\0' */
>     +
>     +                       appl_args->if_str = malloc(len);
>     +                       if (appl_args->if_str == NULL) {
>     +                               usage(argv[0]);
>     +                               exit(EXIT_FAILURE);
>     +                       }
>     +
>     +                       /* count the number of tokens separated by
>     ',' */
>     +                       strcpy(appl_args->if_str, optarg);
>     +                       for (token = strtok(appl_args->if_str,
>     ","), i = 0;
>     +                            token != NULL;
>     +                            token = strtok(NULL, ","), i++)
>     +                               ;
>     +
>     +                       appl_args->if_count = i;
>     +
>     +                       if (appl_args->if_count == 0) {
>     +                               usage(argv[0]);
>     +                               exit(EXIT_FAILURE);
>     +                       }
>     +
>     +                       /* allocate storage for the if names */
>     +                       appl_args->if_names =
>     +  calloc(appl_args->if_count, sizeof(char *));
>     +
>     +                       /* store the if names (reset names string) */
>     +                       strcpy(appl_args->if_str, optarg);
>     +                       for (token = strtok(appl_args->if_str,
>     ","), i = 0;
>     +                            token != NULL; token = strtok(NULL,
>     ","), i++) {
>     +  appl_args->if_names[i] = token;
>     +                       }
>     +                       break;
>     +
>     +               case 'm':
>     +                       i = atoi(optarg);
>     +                       if (i == 0)
>     +                               appl_args->mode = APPL_MODE_PKT_BURST;
>     +                       else
>     +                               appl_args->mode = APPL_MODE_PKT_QUEUE;
>     +                       break;
>     +
>     +               case 'h':
>     +                       usage(argv[0]);
>     +                       exit(EXIT_SUCCESS);
>     +                       break;
>     +
>     +               default:
>     +                       break;
>     +               }
>     +       }
>     +
>     +       if (appl_args->if_count == 0 || appl_args->mode == -1) {
>     +               usage(argv[0]);
>     +               exit(EXIT_FAILURE);
>     +       }
>     +
>     +       optind = 1;             /* reset 'extern optind' from the
>     getopt lib */
>     +}
>     +
>     +/**
>     + * Print system and application info
>     + */
>     +static void print_info(char *progname, appl_args_t *appl_args)
>     +{
>     +       int i;
>     +
>     +       printf("\n"
>     +              "ODP system info\n"
>     +              "---------------\n"
>     +              "ODP API version: %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_sys_cpu_model_str(),
>     odp_sys_cpu_hz(),
>     +              odp_sys_cache_line_size(), odp_cpu_count());
>     +
>     +       printf("Running ODP appl: \"%s\"\n"
>     +              "-----------------\n"
>     +              "IF-count:        %i\n"
>     +              "Using IFs:      ",
>     +              progname, appl_args->if_count);
>     +       for (i = 0; i < appl_args->if_count; ++i)
>     +               printf(" %s", appl_args->if_names[i]);
>     +       printf("\n"
>     +              "Mode:            ");
>     +       if (appl_args->mode == APPL_MODE_PKT_BURST)
>     +               PRINT_APPL_MODE(APPL_MODE_PKT_BURST);
>     +       else
>     +               PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE);
>     +       printf("\n\n");
>     +       fflush(NULL);
>     +}
>     +
>     +/**
>     + * Prinf usage information
>     + */
>     +static void usage(char *progname)
>     +{
>     +       printf("\n"
>     +              "OpenDataPlane L2 forwarding application.\n"
>     +              "\n"
>     +              "Usage: %s OPTIONS\n"
>     +              "  E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
>     +              " In the above example,\n"
>     +              " eth0 will send pkts to eth1 and vice versa\n"
>     +              " eth2 will send pkts to eth3 and vice versa\n"
>     +              "\n"
>     +              "Mandatory OPTIONS:\n"
>     +              "  -i, --interface Eth interfaces (comma-separated,
>     no spaces)\n"
>     +              "  -m, --mode      0: Burst send&receive packets
>     (no queues)\n"
>     +              "                  1: Send&receive packets through
>     ODP queues.\n"
>     +              "\n"
>     +              "Optional OPTIONS\n"
>     +              "  -c, --count <number> CPU count.\n"
>     +              "  -t, --time  <number> Time in seconds to run.\n"
>     +              "  -a, --accuracy <number> Time in seconds get
>     print statistics\n"
>     +              "                          (default is 1 second).\n"
>     +              "  -h, --help           Display help and exit.\n\n"
>     +              " environment variables:
>     ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
>     +              " ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
>     +              " ODP_PKTIO_DISABLE_SOCKET_BASIC\n"
>     +              " can be used to advanced pkt I/O selection for
>     linux-generic\n"
>     +              "\n", NO_PATH(progname), NO_PATH(progname)
>     +           );
>     +}
>     --
>     1.9.1
>
>     _______________________________________________
>     lng-odp mailing list
>     lng-odp@lists.linaro.org <mailto:lng-odp@lists.linaro.org>
>     https://lists.linaro.org/mailman/listinfo/lng-odp
>
>
Mike Holmes April 9, 2015, 1:10 p.m. UTC | #3
On 9 April 2015 at 07:29, Maxim Uvarov <maxim.uvarov@linaro.org> wrote:

> On 04/09/15 11:18, Christophe Milard wrote:
>
>> Hi Maxim,
>>
>> I am not fully following what this patch does -I appologize for this- but
>> I am a bit worried about the usage of scripts and other environment
>> specific usage in the tests.
>> I am trying to restructure the test/validation tree to enventually get
>> one single directory per ODP API module and run all tests using CUnit (as
>> main entry point).
>> The idea being to eventually have a single executable including all tests
>> (but where command line options could select specific tests or suites).
>>
>> If we go for this approach, all pre and post test setup should be done
>> through the suite initialisation and terminaison functions.
>>
>> I get a bit worried when I see Cunit beeing call by scripts etc...
>> I guess we should maybe have a HO with you Mike Anders and me to try to
>> converge...
>>
>> Christophe.
>>
>
> For pktio tests for example we need some environment to set up to run that
> tests. I want also run all our examples in some automatic manner to make
> sure that they still work. We wanted to keep only simple examples in
> example directory and move more complex apps to tests. But here odp_l2fwd
> should be standalone app and should not be linked to validation suite.
>
>
I agree with both perspectives, I wonder if configuring the env via C calls
rather than calls to bash might be more portable and support Christophes
goals without removing the functionality you describe Maxim.

I am currently adding ./configure support for the example dir so that
whatever remains there can be run to prove it works, and we still need to
move some of it out - like ipsec, l2fwd is close.

If you have a HO to talk about it CC me.


> Maxim.
>
>
>
>>
>> On 8 April 2015 at 20:22, Maxim Uvarov <maxim.uvarov@linaro.org <mailto:
>> maxim.uvarov@linaro.org>> wrote:
>>
>>     Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org
>>     <mailto:maxim.uvarov@linaro.org>>
>>     ---
>>     configure.ac <http://configure.ac>                 |   1 -
>>      example/Makefile.am          |   2 +-
>>      example/l2fwd/.gitignore     |   1 -
>>      example/l2fwd/Makefile.am    |  10 -
>>      example/l2fwd/odp_l2fwd.c    | 685
>>     -------------------------------------------
>>      test/performance/.gitignore  |   3 +-
>>      test/performance/Makefile.am |   4 +-
>>      test/performance/odp_l2fwd.c | 685
>>     +++++++++++++++++++++++++++++++++++++++++++
>>      8 files changed, 690 insertions(+), 701 deletions(-)
>>      delete mode 100644 example/l2fwd/.gitignore
>>      delete mode 100644 example/l2fwd/Makefile.am
>>      delete mode 100644 example/l2fwd/odp_l2fwd.c
>>      create mode 100644 test/performance/odp_l2fwd.c
>>
>>     diff --git a/configure.ac <http://configure.ac> b/configure.ac
>>     <http://configure.ac>
>>     index 91083b9..f47ac66 100644
>>     --- a/configure.ac <http://configure.ac>
>>     +++ b/configure.ac <http://configure.ac>
>>     @@ -264,7 +264,6 @@ AC_CONFIG_FILES([Makefile
>>                      example/Makefile
>>                      example/generator/Makefile
>>                      example/ipsec/Makefile
>>     -                example/l2fwd/Makefile
>>                      example/packet/Makefile
>>                      example/timer/Makefile
>>                      pkgconfig/libodp.pc
>>     diff --git a/example/Makefile.am b/example/Makefile.am
>>     index 3021571..6bb4f5c 100644
>>     --- a/example/Makefile.am
>>     +++ b/example/Makefile.am
>>     @@ -1 +1 @@
>>     -SUBDIRS = generator ipsec l2fwd packet timer
>>     +SUBDIRS = generator ipsec packet timer
>>     diff --git a/example/l2fwd/.gitignore b/example/l2fwd/.gitignore
>>     deleted file mode 100644
>>     index 8563319..0000000
>>     --- a/example/l2fwd/.gitignore
>>     +++ /dev/null
>>     @@ -1 +0,0 @@
>>     -odp_l2fwd
>>     diff --git a/example/l2fwd/Makefile.am b/example/l2fwd/Makefile.am
>>     deleted file mode 100644
>>     index feced2a..0000000
>>     --- a/example/l2fwd/Makefile.am
>>     +++ /dev/null
>>     @@ -1,10 +0,0 @@
>>     -include $(top_srcdir)/example/Makefile.inc
>>     -
>>     -bin_PROGRAMS = odp_l2fwd
>>     -odp_l2fwd_LDFLAGS = $(AM_LDFLAGS) -static
>>     -odp_l2fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
>>     -
>>     -noinst_HEADERS = \
>>     -                 $(top_srcdir)/example/example_debug.h
>>     -
>>     -dist_odp_l2fwd_SOURCES = odp_l2fwd.c
>>     diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c
>>     deleted file mode 100644
>>     index 4a49008..0000000
>>     --- a/example/l2fwd/odp_l2fwd.c
>>     +++ /dev/null
>>     @@ -1,685 +0,0 @@
>>     -/* Copyright (c) 2014, Linaro Limited
>>     - * All rights reserved.
>>     - *
>>     - * SPDX-License-Identifier:     BSD-3-Clause
>>     - */
>>     -
>>     -/**
>>     - * @file
>>     - *
>>     - * @example odp_l2fwd.c  ODP basic forwarding application
>>     - */
>>     -
>>     -/** enable strtok */
>>     -#define _POSIX_C_SOURCE 200112L
>>     -
>>     -#include <stdlib.h>
>>     -#include <getopt.h>
>>     -#include <unistd.h>
>>     -#include <errno.h>
>>     -
>>     -#include <example_debug.h>
>>     -
>>     -#include <odp.h>
>>     -#include <odp/helper/linux.h>
>>     -#include <odp/helper/eth.h>
>>     -#include <odp/helper/ip.h>
>>     -
>>     -/** @def MAX_WORKERS
>>     - * @brief Maximum number of worker threads
>>     - */
>>     -#define MAX_WORKERS            32
>>     -
>>     -/** @def SHM_PKT_POOL_SIZE
>>     - * @brief Size of the shared memory block
>>     - */
>>     -#define SHM_PKT_POOL_SIZE      (512*2048)
>>     -
>>     -/** @def SHM_PKT_POOL_BUF_SIZE
>>     - * @brief Buffer size of the packet pool buffer
>>     - */
>>     -#define SHM_PKT_POOL_BUF_SIZE  1856
>>     -
>>     -/** @def MAX_PKT_BURST
>>     - * @brief Maximum number of packet bursts
>>     - */
>>     -#define MAX_PKT_BURST          16
>>     -
>>     -/** @def APPL_MODE_PKT_BURST
>>     - * @brief The application will handle pakcets in bursts
>>     - */
>>     -#define APPL_MODE_PKT_BURST    0
>>     -
>>     -/** @def APPL_MODE_PKT_QUEUE
>>     - * @brief The application will handle packets in queues
>>     - */
>>     -#define APPL_MODE_PKT_QUEUE    1
>>     -
>>     -/** @def PRINT_APPL_MODE(x)
>>     - * @brief Macro to print the current status of how the
>>     application handles
>>     - * packets.
>>     - */
>>     -#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
>>     -
>>     -/** 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))
>>     -/**
>>     - * Parsed command line application arguments
>>     - */
>>     -typedef struct {
>>     -       int cpu_count;
>>     -       int if_count;           /**< Number of interfaces to be
>>     used */
>>     -       char **if_names;        /**< Array of pointers to
>>     interface names */
>>     -       int mode;               /**< Packet IO mode */
>>     -       int time;               /**< Time in seconds to run. */
>>     -       int accuracy;           /**< Number of seconds to get and
>>     print statistics */
>>     -       char *if_str;           /**< Storage for interface names */
>>     -} appl_args_t;
>>     -
>>     -static int exit_threads;       /**< Break workers loop if set to 1 */
>>     -
>>     -/**
>>     - * Statistics
>>     - */
>>     -typedef struct {
>>     -       uint64_t packets;       /**< Number of forwarded packets. */
>>     -       uint64_t drops;         /**< Number of dropped packets. */
>>     -} stats_t;
>>     -
>>     -/**
>>     - * Thread specific arguments
>>     - */
>>     -typedef struct {
>>     -       int src_idx;            /**< Source interface identifier */
>>     -       stats_t **stats;        /**< Per thread packet stats */
>>     -} thread_args_t;
>>     -
>>     -/**
>>     - * Grouping of all global data
>>     - */
>>     -typedef struct {
>>     -       /** Application (parsed) arguments */
>>     -       appl_args_t appl;
>>     -       /** Thread specific arguments */
>>     -       thread_args_t thread[MAX_WORKERS];
>>     -       /** Table of pktio handles */
>>     -       odp_pktio_t pktios[ODP_CONFIG_PKTIO_ENTRIES];
>>     -} args_t;
>>     -
>>     -/** Global pointer to args */
>>     -static args_t *gbl_args;
>>     -
>>     -/* helper funcs */
>>     -static inline odp_queue_t lookup_dest_q(odp_packet_t pkt);
>>     -static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
>>     -static void parse_args(int argc, char *argv[], appl_args_t
>>     *appl_args);
>>     -static void print_info(char *progname, appl_args_t *appl_args);
>>     -static void usage(char *progname);
>>     -
>>     -/**
>>     - * Packet IO worker thread using ODP queues
>>     - *
>>     - * @param arg  thread arguments of type 'thread_args_t *'
>>     - */
>>     -static void *pktio_queue_thread(void *arg)
>>     -{
>>     -       int thr;
>>     -       odp_queue_t outq_def;
>>     -       odp_packet_t pkt;
>>     -       odp_event_t ev;
>>     -       thread_args_t *thr_args = arg;
>>     -
>>     -       stats_t *stats = calloc(1, sizeof(stats_t));
>>     -       *thr_args->stats = stats;
>>     -
>>     -       thr = odp_thread_id();
>>     -
>>     -       printf("[%02i] QUEUE mode\n", thr);
>>     -
>>     -       /* Loop packets */
>>     -       while (!exit_threads) {
>>     -               /* Use schedule to get buf from any input queue */
>>     -               ev  = odp_schedule(NULL, ODP_SCHED_WAIT);
>>     -               pkt = odp_packet_from_event(ev);
>>     -
>>     -               /* Drop packets with errors */
>>     -               if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
>>     -                       stats->drops += 1;
>>     -                       continue;
>>     -               }
>>     -
>>     -               outq_def = lookup_dest_q(pkt);
>>     -
>>     -               /* Enqueue the packet for output */
>>     -               odp_queue_enq(outq_def, ev);
>>     -
>>     -               stats->packets += 1;
>>     -       }
>>     -
>>     -       free(stats);
>>     -       return NULL;
>>     -}
>>     -
>>     -/**
>>     - * Lookup the destination pktio for a given packet
>>     - */
>>     -static inline odp_queue_t lookup_dest_q(odp_packet_t pkt)
>>     -{
>>     -       int i, src_idx, dst_idx;
>>     -       odp_pktio_t pktio_src, pktio_dst;
>>     -
>>     -       pktio_src = odp_packet_input(pkt);
>>     -
>>     -       for (src_idx = -1, i = 0; gbl_args->pktios[i] !=
>>     ODP_PKTIO_INVALID; ++i)
>>     -               if (gbl_args->pktios[i] == pktio_src)
>>     -                       src_idx = i;
>>     -
>>     -       if (src_idx == -1)
>>     -               EXAMPLE_ABORT("Failed to determine pktio input\n");
>>     -
>>     -       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
>>     -       pktio_dst = gbl_args->pktios[dst_idx];
>>     -
>>     -       return odp_pktio_outq_getdef(pktio_dst);
>>     -}
>>     -
>>     -/**
>>     - * Packet IO worker thread using bursts from/to IO resources
>>     - *
>>     - * @param arg  thread arguments of type 'thread_args_t *'
>>     - */
>>     -static void *pktio_ifburst_thread(void *arg)
>>     -{
>>     -       int thr;
>>     -       thread_args_t *thr_args;
>>     -       int pkts, pkts_ok;
>>     -       odp_packet_t pkt_tbl[MAX_PKT_BURST];
>>     -       int src_idx, dst_idx;
>>     -       odp_pktio_t pktio_src, pktio_dst;
>>     -
>>     -       thr = odp_thread_id();
>>     -       thr_args = arg;
>>     -
>>     -       stats_t *stats = calloc(1, sizeof(stats_t));
>>     -       *thr_args->stats = stats;
>>     -
>>     -       src_idx = thr_args->src_idx;
>>     -       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
>>     -       pktio_src = gbl_args->pktios[src_idx];
>>     -       pktio_dst = gbl_args->pktios[dst_idx];
>>     -
>>     -       printf("[%02i] srcif:%s dstif:%s spktio:%02" PRIu64
>>     -              " dpktio:%02" PRIu64 " BURST mode\n",
>>     -              thr,
>>     -              gbl_args->appl.if_names[src_idx],
>>     -              gbl_args->appl.if_names[dst_idx],
>>     -              odp_pktio_to_u64(pktio_src),
>>     odp_pktio_to_u64(pktio_dst));
>>     -
>>     -       /* Loop packets */
>>     -       while (!exit_threads) {
>>     -               pkts = odp_pktio_recv(pktio_src, pkt_tbl,
>>     MAX_PKT_BURST);
>>     -               if (pkts <= 0)
>>     -                       continue;
>>     -
>>     -               /* Drop packets with errors */
>>     -               pkts_ok = drop_err_pkts(pkt_tbl, pkts);
>>     -               if (pkts_ok > 0)
>>     -                       odp_pktio_send(pktio_dst, pkt_tbl, pkts_ok);
>>     -
>>     -               if (odp_unlikely(pkts_ok != pkts))
>>     -                       stats->drops += pkts - pkts_ok;
>>     -
>>     -               if (pkts_ok == 0)
>>     -                       continue;
>>     -
>>     -               stats->packets += pkts_ok;
>>     -       }
>>     -
>>     -       free(stats);
>>     -       return NULL;
>>     -}
>>     -
>>     -/**
>>     - * Create a pktio handle, optionally associating a default input
>>     queue.
>>     - *
>>     - * @param dev Name of device to open
>>     - * @param pool Pool to associate with device for packet RX/TX
>>     - * @param mode Packet processing mode for this device (BURST or
>>     QUEUE)
>>     - *
>>     - * @return The handle of the created pktio object.
>>     - * @retval ODP_PKTIO_INVALID if the create fails.
>>     - */
>>     -static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool,
>>     -                               int mode)
>>     -{
>>     -       char inq_name[ODP_QUEUE_NAME_LEN];
>>     -       odp_queue_param_t qparam;
>>     -       odp_queue_t inq_def;
>>     -       odp_pktio_t pktio;
>>     -       int ret;
>>     -
>>     -       pktio = odp_pktio_open(dev, pool);
>>     -       if (pktio == ODP_PKTIO_INVALID) {
>>     -               EXAMPLE_ERR("Error: failed to open %s\n", dev);
>>     -               return ODP_PKTIO_INVALID;
>>     -       }
>>     -
>>     -       printf("created pktio %" PRIu64 " (%s)\n",
>>     -              odp_pktio_to_u64(pktio), dev);
>>     -
>>     -       /* no further setup needed for burst mode */
>>     -       if (mode == APPL_MODE_PKT_BURST)
>>     -               return pktio;
>>     -
>>     -       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
>>     -       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
>>     -       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
>>     -       snprintf(inq_name, sizeof(inq_name), "%" PRIu64
>>     "-pktio_inq_def",
>>     -                odp_pktio_to_u64(pktio));
>>     -       inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
>>     -
>>     -       inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN,
>>     &qparam);
>>     -       if (inq_def == ODP_QUEUE_INVALID) {
>>     -               EXAMPLE_ERR("Error: pktio queue creation failed\n");
>>     -               return ODP_PKTIO_INVALID;
>>     -       }
>>     -
>>     -       ret = odp_pktio_inq_setdef(pktio, inq_def);
>>     -       if (ret != 0) {
>>     -               EXAMPLE_ERR("Error: default input-Q setup\n");
>>     -               return ODP_PKTIO_INVALID;
>>     -       }
>>     -
>>     -       return pktio;
>>     -}
>>     -
>>     -/**
>>     - *  Print statistics
>>     - *
>>     - * @param num_workers Number of worker threads
>>     - * @param thr_stats Pointer to stats storage
>>     - * @param duration Number of seconds to loop in
>>     - * @param timeout Number of seconds for stats calculation
>>     - *
>>     - */
>>     -static void print_speed_stats(int num_workers, stats_t **thr_stats,
>>     -                             int duration, int timeout)
>>     -{
>>     -       uint64_t pkts, pkts_prev = 0, pps, drops, maximum_pps = 0;
>>     -       int i, elapsed = 0;
>>     -       int loop_forever = (duration == 0);
>>     -
>>     -       do {
>>     -               pkts = 0;
>>     -               drops = 0;
>>     -
>>     -               sleep(timeout);
>>     -
>>     -               for (i = 0; i < num_workers; i++) {
>>     -                       pkts += thr_stats[i]->packets;
>>     -                       drops += thr_stats[i]->drops;
>>     -               }
>>     -               pps = (pkts - pkts_prev) / timeout;
>>     -               if (pps > maximum_pps)
>>     -                       maximum_pps = pps;
>>     -               printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,
>>     -                      maximum_pps);
>>     -
>>     -               printf(" %" PRIu64 " total drops\n", drops);
>>     -
>>     -               elapsed += timeout;
>>     -               pkts_prev = pkts;
>>     -       } while (loop_forever || (elapsed < duration));
>>     -
>>     -       printf("TEST RESULT: %" PRIu64 " maximum packets per
>>     second.\n",
>>     -              maximum_pps);
>>     -       return;
>>     -}
>>     -
>>     -/**
>>     - * ODP L2 forwarding main function
>>     - */
>>     -int main(int argc, char *argv[])
>>     -{
>>     -       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>>     -       odp_pool_t pool;
>>     -       int i;
>>     -       int cpu;
>>     -       int num_workers;
>>     -       odp_shm_t shm;
>>     -       odp_cpumask_t cpumask;
>>     -       char cpumaskstr[ODP_CPUMASK_STR_SIZE];
>>     -       odp_pool_param_t params;
>>     -
>>     -       /* Init ODP before calling anything else */
>>     -       if (odp_init_global(NULL, NULL)) {
>>     -               EXAMPLE_ERR("Error: ODP global init failed.\n");
>>     -               exit(EXIT_FAILURE);
>>     -       }
>>     -
>>     -       /* Init this thread */
>>     -       if (odp_init_local()) {
>>     -               EXAMPLE_ERR("Error: ODP local init failed.\n");
>>     -               exit(EXIT_FAILURE);
>>     -       }
>>     -
>>     -       /* Reserve memory for args from shared mem */
>>     -       shm = odp_shm_reserve("shm_args", sizeof(args_t),
>>     -                             ODP_CACHE_LINE_SIZE, 0);
>>     -       gbl_args = odp_shm_addr(shm);
>>     -
>>     -       if (gbl_args == NULL) {
>>     -               EXAMPLE_ERR("Error: shared mem alloc failed.\n");
>>     -               exit(EXIT_FAILURE);
>>     -       }
>>     -       memset(gbl_args, 0, sizeof(*gbl_args));
>>     -
>>     -       /* Parse and store the application arguments */
>>     -       parse_args(argc, argv, &gbl_args->appl);
>>     -
>>     -       /* Print both system and application information */
>>     -       print_info(NO_PATH(argv[0]), &gbl_args->appl);
>>     -
>>     -       /* Default to system CPU count unless user specified */
>>     -       num_workers = MAX_WORKERS;
>>     -       if (gbl_args->appl.cpu_count)
>>     -               num_workers = gbl_args->appl.cpu_count;
>>     -
>>     -       /*
>>     -        * By default CPU #0 runs Linux kernel background tasks.
>>     -        * Start mapping thread from CPU #1
>>     -        */
>>     -       num_workers = odph_linux_cpumask_default(&cpumask,
>>     num_workers);
>>     -       (void)odp_cpumask_to_str(&cpumask, cpumaskstr,
>>     sizeof(cpumaskstr));
>>     -
>>     -       printf("num worker threads: %i\n", num_workers);
>>     -       printf("first CPU:          %i\n",
>>     odp_cpumask_first(&cpumask));
>>     -       printf("cpu mask:           %s\n", cpumaskstr);
>>     -
>>     -       if (num_workers < gbl_args->appl.if_count) {
>>     -               EXAMPLE_ERR("Error: CPU count %d less than
>>     interface count\n",
>>     -                           num_workers);
>>     -               exit(EXIT_FAILURE);
>>     -       }
>>     -       if (gbl_args->appl.if_count % 2 != 0) {
>>     -               EXAMPLE_ERR("Error: interface count %d is odd in
>>     fwd appl.\n",
>>     -                           gbl_args->appl.if_count);
>>     -               exit(EXIT_FAILURE);
>>     -       }
>>     -
>>     -       /* Create packet pool */
>>     -       memset(&params, 0, sizeof(params));
>>     -       params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
>>     -       params.pkt.len     = SHM_PKT_POOL_BUF_SIZE;
>>     -       params.pkt.num     = SHM_PKT_POOL_SIZE/SHM_PKT_POOL_BUF_SIZE;
>>     -       params.type        = ODP_POOL_PACKET;
>>     -
>>     -       pool = odp_pool_create("packet pool", ODP_SHM_NULL, &params);
>>     -
>>     -       if (pool == ODP_POOL_INVALID) {
>>     -               EXAMPLE_ERR("Error: packet pool create failed.\n");
>>     -               exit(EXIT_FAILURE);
>>     -       }
>>     -       odp_pool_print(pool);
>>     -
>>     -       for (i = 0; i < gbl_args->appl.if_count; ++i) {
>>     -               gbl_args->pktios[i] =
>>     create_pktio(gbl_args->appl.if_names[i],
>>     -                                                  pool,
>>     gbl_args->appl.mode);
>>     -               if (gbl_args->pktios[i] == ODP_PKTIO_INVALID)
>>     -                       exit(EXIT_FAILURE);
>>     -       }
>>     -       gbl_args->pktios[i] = ODP_PKTIO_INVALID;
>>     -
>>     -       memset(thread_tbl, 0, sizeof(thread_tbl));
>>     -
>>     -       stats_t **stats = calloc(1, sizeof(stats_t) * num_workers);
>>     -
>>     -       /* Create worker threads */
>>     -       cpu = odp_cpumask_first(&cpumask);
>>     -       for (i = 0; i < num_workers; ++i) {
>>     -               odp_cpumask_t thd_mask;
>>     -               void *(*thr_run_func) (void *);
>>     -
>>     -               if (gbl_args->appl.mode == APPL_MODE_PKT_BURST)
>>     -                       thr_run_func = pktio_ifburst_thread;
>>     -               else /* APPL_MODE_PKT_QUEUE */
>>     -                       thr_run_func = pktio_queue_thread;
>>     -
>>     -               gbl_args->thread[i].src_idx = i %
>>     gbl_args->appl.if_count;
>>     -               gbl_args->thread[i].stats = &stats[i];
>>     -
>>     -               odp_cpumask_zero(&thd_mask);
>>     -               odp_cpumask_set(&thd_mask, cpu);
>>     -  odph_linux_pthread_create(&thread_tbl[i], &thd_mask,
>>     -                                         thr_run_func,
>>     -  &gbl_args->thread[i]);
>>     -               cpu = odp_cpumask_next(&cpumask, cpu);
>>     -       }
>>     -
>>     -       print_speed_stats(num_workers, stats, gbl_args->appl.time,
>>     -                         gbl_args->appl.accuracy);
>>     -       free(stats);
>>     -       exit_threads = 1;
>>     -
>>     -       /* Master thread waits for other threads to exit */
>>     -       odph_linux_pthread_join(thread_tbl, num_workers);
>>     -
>>     -       free(gbl_args->appl.if_names);
>>     -       free(gbl_args->appl.if_str);
>>     -       printf("Exit\n\n");
>>     -
>>     -       return 0;
>>     -}
>>     -
>>     -/**
>>     - * 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 packet
>>     - * @param len      Length of pkt_tbl[]
>>     - *
>>     - * @return Number of packets with no detected error
>>     - */
>>     -static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
>>     -{
>>     -       odp_packet_t pkt;
>>     -       unsigned pkt_cnt = len;
>>     -       unsigned i, j;
>>     -
>>     -       for (i = 0, j = 0; i < len; ++i) {
>>     -               pkt = pkt_tbl[i];
>>     -
>>     -               if (odp_unlikely(odp_packet_has_error(pkt))) {
>>     -                       odp_packet_free(pkt); /* Drop */
>>     -                       pkt_cnt--;
>>     -               } else if (odp_unlikely(i != j++)) {
>>     -                       pkt_tbl[j-1] = pkt;
>>     -               }
>>     -       }
>>     -
>>     -       return pkt_cnt;
>>     -}
>>     -
>>     -/**
>>     - * Parse and store the command line arguments
>>     - *
>>     - * @param argc       argument count
>>     - * @param argv[]     argument vector
>>     - * @param appl_args  Store application arguments here
>>     - */
>>     -static void parse_args(int argc, char *argv[], appl_args_t
>>     *appl_args)
>>     -{
>>     -       int opt;
>>     -       int long_index;
>>     -       char *token;
>>     -       size_t len;
>>     -       int i;
>>     -       static struct option longopts[] = {
>>     -               {"count", required_argument, NULL, 'c'},
>>     -               {"time", required_argument, NULL, 't'},
>>     -               {"accuracy", required_argument, NULL, 'a'},
>>     -               {"interface", required_argument, NULL, 'i'},    /*
>>     return 'i' */
>>     -               {"mode", required_argument, NULL, 'm'},        /*
>>     return 'm' */
>>     -               {"help", no_argument, NULL, 'h'},        /* return
>>     'h' */
>>     -               {NULL, 0, NULL, 0}
>>     -       };
>>     -
>>     -       appl_args->time = 0; /* loop forever if time to run is 0 */
>>     -       appl_args->accuracy = 1; /* get and print pps stats second */
>>     -       appl_args->mode = -1; /* Invalid, must be changed by
>>     parsing */
>>     -
>>     -       while (1) {
>>     -               opt = getopt_long(argc, argv, "+c:+t:+a:i:m:h",
>>     -                                 longopts, &long_index);
>>     -
>>     -               if (opt == -1)
>>     -                       break;  /* No more options */
>>     -
>>     -               switch (opt) {
>>     -               case 'c':
>>     -                       appl_args->cpu_count = atoi(optarg);
>>     -                       break;
>>     -               case 't':
>>     -                       appl_args->time = atoi(optarg);
>>     -                       break;
>>     -               case 'a':
>>     -                       appl_args->accuracy = atoi(optarg);
>>     -                       break;
>>     -                       /* parse packet-io interface names */
>>     -               case 'i':
>>     -                       len = strlen(optarg);
>>     -                       if (len == 0) {
>>     -                               usage(argv[0]);
>>     -                               exit(EXIT_FAILURE);
>>     -                       }
>>     -                       len += 1;       /* add room for '\0' */
>>     -
>>     -                       appl_args->if_str = malloc(len);
>>     -                       if (appl_args->if_str == NULL) {
>>     -                               usage(argv[0]);
>>     -                               exit(EXIT_FAILURE);
>>     -                       }
>>     -
>>     -                       /* count the number of tokens separated by
>>     ',' */
>>     -                       strcpy(appl_args->if_str, optarg);
>>     -                       for (token = strtok(appl_args->if_str,
>>     ","), i = 0;
>>     -                            token != NULL;
>>     -                            token = strtok(NULL, ","), i++)
>>     -                               ;
>>     -
>>     -                       appl_args->if_count = i;
>>     -
>>     -                       if (appl_args->if_count == 0) {
>>     -                               usage(argv[0]);
>>     -                               exit(EXIT_FAILURE);
>>     -                       }
>>     -
>>     -                       /* allocate storage for the if names */
>>     -                       appl_args->if_names =
>>     -  calloc(appl_args->if_count, sizeof(char *));
>>     -
>>     -                       /* store the if names (reset names string) */
>>     -                       strcpy(appl_args->if_str, optarg);
>>     -                       for (token = strtok(appl_args->if_str,
>>     ","), i = 0;
>>     -                            token != NULL; token = strtok(NULL,
>>     ","), i++) {
>>     -  appl_args->if_names[i] = token;
>>     -                       }
>>     -                       break;
>>     -
>>     -               case 'm':
>>     -                       i = atoi(optarg);
>>     -                       if (i == 0)
>>     -                               appl_args->mode = APPL_MODE_PKT_BURST;
>>     -                       else
>>     -                               appl_args->mode = APPL_MODE_PKT_QUEUE;
>>     -                       break;
>>     -
>>     -               case 'h':
>>     -                       usage(argv[0]);
>>     -                       exit(EXIT_SUCCESS);
>>     -                       break;
>>     -
>>     -               default:
>>     -                       break;
>>     -               }
>>     -       }
>>     -
>>     -       if (appl_args->if_count == 0 || appl_args->mode == -1) {
>>     -               usage(argv[0]);
>>     -               exit(EXIT_FAILURE);
>>     -       }
>>     -
>>     -       optind = 1;             /* reset 'extern optind' from the
>>     getopt lib */
>>     -}
>>     -
>>     -/**
>>     - * Print system and application info
>>     - */
>>     -static void print_info(char *progname, appl_args_t *appl_args)
>>     -{
>>     -       int i;
>>     -
>>     -       printf("\n"
>>     -              "ODP system info\n"
>>     -              "---------------\n"
>>     -              "ODP API version: %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_sys_cpu_model_str(),
>>     odp_sys_cpu_hz(),
>>     -              odp_sys_cache_line_size(), odp_cpu_count());
>>     -
>>     -       printf("Running ODP appl: \"%s\"\n"
>>     -              "-----------------\n"
>>     -              "IF-count:        %i\n"
>>     -              "Using IFs:      ",
>>     -              progname, appl_args->if_count);
>>     -       for (i = 0; i < appl_args->if_count; ++i)
>>     -               printf(" %s", appl_args->if_names[i]);
>>     -       printf("\n"
>>     -              "Mode:            ");
>>     -       if (appl_args->mode == APPL_MODE_PKT_BURST)
>>     -               PRINT_APPL_MODE(APPL_MODE_PKT_BURST);
>>     -       else
>>     -               PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE);
>>     -       printf("\n\n");
>>     -       fflush(NULL);
>>     -}
>>     -
>>     -/**
>>     - * Prinf usage information
>>     - */
>>     -static void usage(char *progname)
>>     -{
>>     -       printf("\n"
>>     -              "OpenDataPlane L2 forwarding application.\n"
>>     -              "\n"
>>     -              "Usage: %s OPTIONS\n"
>>     -              "  E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
>>     -              " In the above example,\n"
>>     -              " eth0 will send pkts to eth1 and vice versa\n"
>>     -              " eth2 will send pkts to eth3 and vice versa\n"
>>     -              "\n"
>>     -              "Mandatory OPTIONS:\n"
>>     -              "  -i, --interface Eth interfaces (comma-separated,
>>     no spaces)\n"
>>     -              "  -m, --mode      0: Burst send&receive packets
>>     (no queues)\n"
>>     -              "                  1: Send&receive packets through
>>     ODP queues.\n"
>>     -              "\n"
>>     -              "Optional OPTIONS\n"
>>     -              "  -c, --count <number> CPU count.\n"
>>     -              "  -t, --time  <number> Time in seconds to run.\n"
>>     -              "  -a, --accuracy <number> Time in seconds get
>>     print statistics\n"
>>     -              "                          (default is 1 second).\n"
>>     -              "  -h, --help           Display help and exit.\n\n"
>>     -              " environment variables:
>>     ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
>>     -              " ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
>>     -              " ODP_PKTIO_DISABLE_SOCKET_BASIC\n"
>>     -              " can be used to advanced pkt I/O selection for
>>     linux-generic\n"
>>     -              "\n", NO_PATH(progname), NO_PATH(progname)
>>     -           );
>>     -}
>>     diff --git a/test/performance/.gitignore b/test/performance/.gitignore
>>     index 1bdb90d..d41d1e1 100644
>>     --- a/test/performance/.gitignore
>>     +++ b/test/performance/.gitignore
>>     @@ -1,4 +1,5 @@
>>      *.log
>>      *.trs
>>     -odp_scheduling
>>      odp_atomic
>>     +odp_l2fwd
>>     +odp_scheduling
>>     diff --git a/test/performance/Makefile.am
>>     b/test/performance/Makefile.am
>>     index b0f7457..b501584 100644
>>     --- a/test/performance/Makefile.am
>>     +++ b/test/performance/Makefile.am
>>     @@ -1,10 +1,10 @@
>>      include $(top_srcdir)/test/Makefile.inc
>>
>>     -TESTS_ENVIRONMENT = TEST_DIR=${builddir}
>>     +TESTS_ENVIRONMENT = TEST_DIR=${builddir}
>>     ODP_PLATFORM=${with_platform}
>>
>>      EXECUTABLES = odp_atomic
>>
>>     -COMPILE_ONLY = odp_scheduling
>>     +COMPILE_ONLY = odp_scheduling odp_l2fwd
>>
>>      TESTSCRIPTS = odp_scheduling_run
>>
>>     diff --git a/test/performance/odp_l2fwd.c
>>     b/test/performance/odp_l2fwd.c
>>     new file mode 100644
>>     index 0000000..5d4b833
>>     --- /dev/null
>>     +++ b/test/performance/odp_l2fwd.c
>>     @@ -0,0 +1,685 @@
>>     +/* Copyright (c) 2014, Linaro Limited
>>     + * All rights reserved.
>>     + *
>>     + * SPDX-License-Identifier:     BSD-3-Clause
>>     + */
>>     +
>>     +/**
>>     + * @file
>>     + *
>>     + * @example odp_l2fwd.c  ODP basic forwarding application
>>     + */
>>     +
>>     +/** enable strtok */
>>     +#define _POSIX_C_SOURCE 200112L
>>     +
>>     +#include <stdlib.h>
>>     +#include <getopt.h>
>>     +#include <unistd.h>
>>     +#include <errno.h>
>>     +
>>     +#include <test_debug.h>
>>     +
>>     +#include <odp.h>
>>     +#include <odp/helper/linux.h>
>>     +#include <odp/helper/eth.h>
>>     +#include <odp/helper/ip.h>
>>     +
>>     +/** @def MAX_WORKERS
>>     + * @brief Maximum number of worker threads
>>     + */
>>     +#define MAX_WORKERS            32
>>     +
>>     +/** @def SHM_PKT_POOL_SIZE
>>     + * @brief Size of the shared memory block
>>     + */
>>     +#define SHM_PKT_POOL_SIZE      (512*2048)
>>     +
>>     +/** @def SHM_PKT_POOL_BUF_SIZE
>>     + * @brief Buffer size of the packet pool buffer
>>     + */
>>     +#define SHM_PKT_POOL_BUF_SIZE  1856
>>     +
>>     +/** @def MAX_PKT_BURST
>>     + * @brief Maximum number of packet bursts
>>     + */
>>     +#define MAX_PKT_BURST          16
>>     +
>>     +/** @def APPL_MODE_PKT_BURST
>>     + * @brief The application will handle pakcets in bursts
>>     + */
>>     +#define APPL_MODE_PKT_BURST    0
>>     +
>>     +/** @def APPL_MODE_PKT_QUEUE
>>     + * @brief The application will handle packets in queues
>>     + */
>>     +#define APPL_MODE_PKT_QUEUE    1
>>     +
>>     +/** @def PRINT_APPL_MODE(x)
>>     + * @brief Macro to print the current status of how the
>>     application handles
>>     + * packets.
>>     + */
>>     +#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
>>     +
>>     +/** 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))
>>     +/**
>>     + * Parsed command line application arguments
>>     + */
>>     +typedef struct {
>>     +       int cpu_count;
>>     +       int if_count;           /**< Number of interfaces to be
>>     used */
>>     +       char **if_names;        /**< Array of pointers to
>>     interface names */
>>     +       int mode;               /**< Packet IO mode */
>>     +       int time;               /**< Time in seconds to run. */
>>     +       int accuracy;           /**< Number of seconds to get and
>>     print statistics */
>>     +       char *if_str;           /**< Storage for interface names */
>>     +} appl_args_t;
>>     +
>>     +static int exit_threads;       /**< Break workers loop if set to 1 */
>>     +
>>     +/**
>>     + * Statistics
>>     + */
>>     +typedef struct {
>>     +       uint64_t packets;       /**< Number of forwarded packets. */
>>     +       uint64_t drops;         /**< Number of dropped packets. */
>>     +} stats_t;
>>     +
>>     +/**
>>     + * Thread specific arguments
>>     + */
>>     +typedef struct {
>>     +       int src_idx;            /**< Source interface identifier */
>>     +       stats_t **stats;        /**< Per thread packet stats */
>>     +} thread_args_t;
>>     +
>>     +/**
>>     + * Grouping of all global data
>>     + */
>>     +typedef struct {
>>     +       /** Application (parsed) arguments */
>>     +       appl_args_t appl;
>>     +       /** Thread specific arguments */
>>     +       thread_args_t thread[MAX_WORKERS];
>>     +       /** Table of pktio handles */
>>     +       odp_pktio_t pktios[ODP_CONFIG_PKTIO_ENTRIES];
>>     +} args_t;
>>     +
>>     +/** Global pointer to args */
>>     +static args_t *gbl_args;
>>     +
>>     +/* helper funcs */
>>     +static inline odp_queue_t lookup_dest_q(odp_packet_t pkt);
>>     +static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
>>     +static void parse_args(int argc, char *argv[], appl_args_t
>>     *appl_args);
>>     +static void print_info(char *progname, appl_args_t *appl_args);
>>     +static void usage(char *progname);
>>     +
>>     +/**
>>     + * Packet IO worker thread using ODP queues
>>     + *
>>     + * @param arg  thread arguments of type 'thread_args_t *'
>>     + */
>>     +static void *pktio_queue_thread(void *arg)
>>     +{
>>     +       int thr;
>>     +       odp_queue_t outq_def;
>>     +       odp_packet_t pkt;
>>     +       odp_event_t ev;
>>     +       thread_args_t *thr_args = arg;
>>     +
>>     +       stats_t *stats = calloc(1, sizeof(stats_t));
>>     +       *thr_args->stats = stats;
>>     +
>>     +       thr = odp_thread_id();
>>     +
>>     +       printf("[%02i] QUEUE mode\n", thr);
>>     +
>>     +       /* Loop packets */
>>     +       while (!exit_threads) {
>>     +               /* Use schedule to get buf from any input queue */
>>     +               ev  = odp_schedule(NULL, ODP_SCHED_WAIT);
>>     +               pkt = odp_packet_from_event(ev);
>>     +
>>     +               /* Drop packets with errors */
>>     +               if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
>>     +                       stats->drops += 1;
>>     +                       continue;
>>     +               }
>>     +
>>     +               outq_def = lookup_dest_q(pkt);
>>     +
>>     +               /* Enqueue the packet for output */
>>     +               odp_queue_enq(outq_def, ev);
>>     +
>>     +               stats->packets += 1;
>>     +       }
>>     +
>>     +       free(stats);
>>     +       return NULL;
>>     +}
>>     +
>>     +/**
>>     + * Lookup the destination pktio for a given packet
>>     + */
>>     +static inline odp_queue_t lookup_dest_q(odp_packet_t pkt)
>>     +{
>>     +       int i, src_idx, dst_idx;
>>     +       odp_pktio_t pktio_src, pktio_dst;
>>     +
>>     +       pktio_src = odp_packet_input(pkt);
>>     +
>>     +       for (src_idx = -1, i = 0; gbl_args->pktios[i] !=
>>     ODP_PKTIO_INVALID; ++i)
>>     +               if (gbl_args->pktios[i] == pktio_src)
>>     +                       src_idx = i;
>>     +
>>     +       if (src_idx == -1)
>>     +               LOG_ABORT("Failed to determine pktio input\n");
>>     +
>>     +       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
>>     +       pktio_dst = gbl_args->pktios[dst_idx];
>>     +
>>     +       return odp_pktio_outq_getdef(pktio_dst);
>>     +}
>>     +
>>     +/**
>>     + * Packet IO worker thread using bursts from/to IO resources
>>     + *
>>     + * @param arg  thread arguments of type 'thread_args_t *'
>>     + */
>>     +static void *pktio_ifburst_thread(void *arg)
>>     +{
>>     +       int thr;
>>     +       thread_args_t *thr_args;
>>     +       int pkts, pkts_ok;
>>     +       odp_packet_t pkt_tbl[MAX_PKT_BURST];
>>     +       int src_idx, dst_idx;
>>     +       odp_pktio_t pktio_src, pktio_dst;
>>     +
>>     +       thr = odp_thread_id();
>>     +       thr_args = arg;
>>     +
>>     +       stats_t *stats = calloc(1, sizeof(stats_t));
>>     +       *thr_args->stats = stats;
>>     +
>>     +       src_idx = thr_args->src_idx;
>>     +       dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
>>     +       pktio_src = gbl_args->pktios[src_idx];
>>     +       pktio_dst = gbl_args->pktios[dst_idx];
>>     +
>>     +       printf("[%02i] srcif:%s dstif:%s spktio:%02" PRIu64
>>     +              " dpktio:%02" PRIu64 " BURST mode\n",
>>     +              thr,
>>     +              gbl_args->appl.if_names[src_idx],
>>     +              gbl_args->appl.if_names[dst_idx],
>>     +              odp_pktio_to_u64(pktio_src),
>>     odp_pktio_to_u64(pktio_dst));
>>     +
>>     +       /* Loop packets */
>>     +       while (!exit_threads) {
>>     +               pkts = odp_pktio_recv(pktio_src, pkt_tbl,
>>     MAX_PKT_BURST);
>>     +               if (pkts <= 0)
>>     +                       continue;
>>     +
>>     +               /* Drop packets with errors */
>>     +               pkts_ok = drop_err_pkts(pkt_tbl, pkts);
>>     +               if (pkts_ok > 0)
>>     +                       odp_pktio_send(pktio_dst, pkt_tbl, pkts_ok);
>>     +
>>     +               if (odp_unlikely(pkts_ok != pkts))
>>     +                       stats->drops += pkts - pkts_ok;
>>     +
>>     +               if (pkts_ok == 0)
>>     +                       continue;
>>     +
>>     +               stats->packets += pkts_ok;
>>     +       }
>>     +
>>     +       free(stats);
>>     +       return NULL;
>>     +}
>>     +
>>     +/**
>>     + * Create a pktio handle, optionally associating a default input
>>     queue.
>>     + *
>>     + * @param dev Name of device to open
>>     + * @param pool Pool to associate with device for packet RX/TX
>>     + * @param mode Packet processing mode for this device (BURST or
>>     QUEUE)
>>     + *
>>     + * @return The handle of the created pktio object.
>>     + * @retval ODP_PKTIO_INVALID if the create fails.
>>     + */
>>     +static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool,
>>     +                               int mode)
>>     +{
>>     +       char inq_name[ODP_QUEUE_NAME_LEN];
>>     +       odp_queue_param_t qparam;
>>     +       odp_queue_t inq_def;
>>     +       odp_pktio_t pktio;
>>     +       int ret;
>>     +
>>     +       pktio = odp_pktio_open(dev, pool);
>>     +       if (pktio == ODP_PKTIO_INVALID) {
>>     +               LOG_ERR("Error: failed to open %s\n", dev);
>>     +               return ODP_PKTIO_INVALID;
>>     +       }
>>     +
>>     +       printf("created pktio %" PRIu64 " (%s)\n",
>>     +              odp_pktio_to_u64(pktio), dev);
>>     +
>>     +       /* no further setup needed for burst mode */
>>     +       if (mode == APPL_MODE_PKT_BURST)
>>     +               return pktio;
>>     +
>>     +       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
>>     +       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
>>     +       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
>>     +       snprintf(inq_name, sizeof(inq_name), "%" PRIu64
>>     "-pktio_inq_def",
>>     +                odp_pktio_to_u64(pktio));
>>     +       inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
>>     +
>>     +       inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN,
>>     &qparam);
>>     +       if (inq_def == ODP_QUEUE_INVALID) {
>>     +               LOG_ERR("Error: pktio queue creation failed\n");
>>     +               return ODP_PKTIO_INVALID;
>>     +       }
>>     +
>>     +       ret = odp_pktio_inq_setdef(pktio, inq_def);
>>     +       if (ret != 0) {
>>     +               LOG_ERR("Error: default input-Q setup\n");
>>     +               return ODP_PKTIO_INVALID;
>>     +       }
>>     +
>>     +       return pktio;
>>     +}
>>     +
>>     +/**
>>     + *  Print statistics
>>     + *
>>     + * @param num_workers Number of worker threads
>>     + * @param thr_stats Pointer to stats storage
>>     + * @param duration Number of seconds to loop in
>>     + * @param timeout Number of seconds for stats calculation
>>     + *
>>     + */
>>     +static void print_speed_stats(int num_workers, stats_t **thr_stats,
>>     +                             int duration, int timeout)
>>     +{
>>     +       uint64_t pkts, pkts_prev = 0, pps, drops, maximum_pps = 0;
>>     +       int i, elapsed = 0;
>>     +       int loop_forever = (duration == 0);
>>     +
>>     +       do {
>>     +               pkts = 0;
>>     +               drops = 0;
>>     +
>>     +               sleep(timeout);
>>     +
>>     +               for (i = 0; i < num_workers; i++) {
>>     +                       pkts += thr_stats[i]->packets;
>>     +                       drops += thr_stats[i]->drops;
>>     +               }
>>     +               pps = (pkts - pkts_prev) / timeout;
>>     +               if (pps > maximum_pps)
>>     +                       maximum_pps = pps;
>>     +               printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,
>>     +                      maximum_pps);
>>     +
>>     +               printf(" %" PRIu64 " total drops\n", drops);
>>     +
>>     +               elapsed += timeout;
>>     +               pkts_prev = pkts;
>>     +       } while (loop_forever || (elapsed < duration));
>>     +
>>     +       printf("TEST RESULT: %" PRIu64 " maximum packets per
>>     second.\n",
>>     +              maximum_pps);
>>     +       return;
>>     +}
>>     +
>>     +/**
>>     + * ODP L2 forwarding main function
>>     + */
>>     +int main(int argc, char *argv[])
>>     +{
>>     +       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>>     +       odp_pool_t pool;
>>     +       int i;
>>     +       int cpu;
>>     +       int num_workers;
>>     +       odp_shm_t shm;
>>     +       odp_cpumask_t cpumask;
>>     +       char cpumaskstr[ODP_CPUMASK_STR_SIZE];
>>     +       odp_pool_param_t params;
>>     +
>>     +       /* Init ODP before calling anything else */
>>     +       if (odp_init_global(NULL, NULL)) {
>>     +               LOG_ERR("Error: ODP global init failed.\n");
>>     +               exit(EXIT_FAILURE);
>>     +       }
>>     +
>>     +       /* Init this thread */
>>     +       if (odp_init_local()) {
>>     +               LOG_ERR("Error: ODP local init failed.\n");
>>     +               exit(EXIT_FAILURE);
>>     +       }
>>     +
>>     +       /* Reserve memory for args from shared mem */
>>     +       shm = odp_shm_reserve("shm_args", sizeof(args_t),
>>     +                             ODP_CACHE_LINE_SIZE, 0);
>>     +       gbl_args = odp_shm_addr(shm);
>>     +
>>     +       if (gbl_args == NULL) {
>>     +               LOG_ERR("Error: shared mem alloc failed.\n");
>>     +               exit(EXIT_FAILURE);
>>     +       }
>>     +       memset(gbl_args, 0, sizeof(*gbl_args));
>>     +
>>     +       /* Parse and store the application arguments */
>>     +       parse_args(argc, argv, &gbl_args->appl);
>>     +
>>     +       /* Print both system and application information */
>>     +       print_info(NO_PATH(argv[0]), &gbl_args->appl);
>>     +
>>     +       /* Default to system CPU count unless user specified */
>>     +       num_workers = MAX_WORKERS;
>>     +       if (gbl_args->appl.cpu_count)
>>     +               num_workers = gbl_args->appl.cpu_count;
>>     +
>>     +       /*
>>     +        * By default CPU #0 runs Linux kernel background tasks.
>>     +        * Start mapping thread from CPU #1
>>     +        */
>>     +       num_workers = odph_linux_cpumask_default(&cpumask,
>>     num_workers);
>>     +       (void)odp_cpumask_to_str(&cpumask, cpumaskstr,
>>     sizeof(cpumaskstr));
>>     +
>>     +       printf("num worker threads: %i\n", num_workers);
>>     +       printf("first CPU:          %i\n",
>>     odp_cpumask_first(&cpumask));
>>     +       printf("cpu mask:           %s\n", cpumaskstr);
>>     +
>>     +       if (num_workers < gbl_args->appl.if_count) {
>>     +               LOG_ERR("Error: CPU count %d less than interface
>>     count\n",
>>     +                       num_workers);
>>     +               exit(EXIT_FAILURE);
>>     +       }
>>     +       if (gbl_args->appl.if_count % 2 != 0) {
>>     +               LOG_ERR("Error: interface count %d is odd in fwd
>>     appl.\n",
>>     +                       gbl_args->appl.if_count);
>>     +               exit(EXIT_FAILURE);
>>     +       }
>>     +
>>     +       /* Create packet pool */
>>     +       memset(&params, 0, sizeof(params));
>>     +       params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
>>     +       params.pkt.len     = SHM_PKT_POOL_BUF_SIZE;
>>     +       params.pkt.num     = SHM_PKT_POOL_SIZE/SHM_PKT_POOL_BUF_SIZE;
>>     +       params.type        = ODP_POOL_PACKET;
>>     +
>>     +       pool = odp_pool_create("packet pool", ODP_SHM_NULL, &params);
>>     +
>>     +       if (pool == ODP_POOL_INVALID) {
>>     +               LOG_ERR("Error: packet pool create failed.\n");
>>     +               exit(EXIT_FAILURE);
>>     +       }
>>     +       odp_pool_print(pool);
>>     +
>>     +       for (i = 0; i < gbl_args->appl.if_count; ++i) {
>>     +               gbl_args->pktios[i] =
>>     create_pktio(gbl_args->appl.if_names[i],
>>     +                                                  pool,
>>     gbl_args->appl.mode);
>>     +               if (gbl_args->pktios[i] == ODP_PKTIO_INVALID)
>>     +                       exit(EXIT_FAILURE);
>>     +       }
>>     +       gbl_args->pktios[i] = ODP_PKTIO_INVALID;
>>     +
>>     +       memset(thread_tbl, 0, sizeof(thread_tbl));
>>     +
>>     +       stats_t **stats = calloc(1, sizeof(stats_t) * num_workers);
>>     +
>>     +       /* Create worker threads */
>>     +       cpu = odp_cpumask_first(&cpumask);
>>     +       for (i = 0; i < num_workers; ++i) {
>>     +               odp_cpumask_t thd_mask;
>>     +               void *(*thr_run_func) (void *);
>>     +
>>     +               if (gbl_args->appl.mode == APPL_MODE_PKT_BURST)
>>     +                       thr_run_func = pktio_ifburst_thread;
>>     +               else /* APPL_MODE_PKT_QUEUE */
>>     +                       thr_run_func = pktio_queue_thread;
>>     +
>>     +               gbl_args->thread[i].src_idx = i %
>>     gbl_args->appl.if_count;
>>     +               gbl_args->thread[i].stats = &stats[i];
>>     +
>>     +               odp_cpumask_zero(&thd_mask);
>>     +               odp_cpumask_set(&thd_mask, cpu);
>>     +  odph_linux_pthread_create(&thread_tbl[i], &thd_mask,
>>     +                                         thr_run_func,
>>     +  &gbl_args->thread[i]);
>>     +               cpu = odp_cpumask_next(&cpumask, cpu);
>>     +       }
>>     +
>>     +       print_speed_stats(num_workers, stats, gbl_args->appl.time,
>>     +                         gbl_args->appl.accuracy);
>>     +       free(stats);
>>     +       exit_threads = 1;
>>     +
>>     +       /* Master thread waits for other threads to exit */
>>     +       odph_linux_pthread_join(thread_tbl, num_workers);
>>     +
>>     +       free(gbl_args->appl.if_names);
>>     +       free(gbl_args->appl.if_str);
>>     +       printf("Exit\n\n");
>>     +
>>     +       return 0;
>>     +}
>>     +
>>     +/**
>>     + * 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 packet
>>     + * @param len      Length of pkt_tbl[]
>>     + *
>>     + * @return Number of packets with no detected error
>>     + */
>>     +static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
>>     +{
>>     +       odp_packet_t pkt;
>>     +       unsigned pkt_cnt = len;
>>     +       unsigned i, j;
>>     +
>>     +       for (i = 0, j = 0; i < len; ++i) {
>>     +               pkt = pkt_tbl[i];
>>     +
>>     +               if (odp_unlikely(odp_packet_has_error(pkt))) {
>>     +                       odp_packet_free(pkt); /* Drop */
>>     +                       pkt_cnt--;
>>     +               } else if (odp_unlikely(i != j++)) {
>>     +                       pkt_tbl[j-1] = pkt;
>>     +               }
>>     +       }
>>     +
>>     +       return pkt_cnt;
>>     +}
>>     +
>>     +/**
>>     + * Parse and store the command line arguments
>>     + *
>>     + * @param argc       argument count
>>     + * @param argv[]     argument vector
>>     + * @param appl_args  Store application arguments here
>>     + */
>>     +static void parse_args(int argc, char *argv[], appl_args_t
>>     *appl_args)
>>     +{
>>     +       int opt;
>>     +       int long_index;
>>     +       char *token;
>>     +       size_t len;
>>     +       int i;
>>     +       static struct option longopts[] = {
>>     +               {"count", required_argument, NULL, 'c'},
>>     +               {"time", required_argument, NULL, 't'},
>>     +               {"accuracy", required_argument, NULL, 'a'},
>>     +               {"interface", required_argument, NULL, 'i'},    /*
>>     return 'i' */
>>     +               {"mode", required_argument, NULL, 'm'},        /*
>>     return 'm' */
>>     +               {"help", no_argument, NULL, 'h'},        /* return
>>     'h' */
>>     +               {NULL, 0, NULL, 0}
>>     +       };
>>     +
>>     +       appl_args->time = 0; /* loop forever if time to run is 0 */
>>     +       appl_args->accuracy = 1; /* get and print pps stats second */
>>     +       appl_args->mode = -1; /* Invalid, must be changed by
>>     parsing */
>>     +
>>     +       while (1) {
>>     +               opt = getopt_long(argc, argv, "+c:+t:+a:i:m:h",
>>     +                                 longopts, &long_index);
>>     +
>>     +               if (opt == -1)
>>     +                       break;  /* No more options */
>>     +
>>     +               switch (opt) {
>>     +               case 'c':
>>     +                       appl_args->cpu_count = atoi(optarg);
>>     +                       break;
>>     +               case 't':
>>     +                       appl_args->time = atoi(optarg);
>>     +                       break;
>>     +               case 'a':
>>     +                       appl_args->accuracy = atoi(optarg);
>>     +                       break;
>>     +                       /* parse packet-io interface names */
>>     +               case 'i':
>>     +                       len = strlen(optarg);
>>     +                       if (len == 0) {
>>     +                               usage(argv[0]);
>>     +                               exit(EXIT_FAILURE);
>>     +                       }
>>     +                       len += 1;       /* add room for '\0' */
>>     +
>>     +                       appl_args->if_str = malloc(len);
>>     +                       if (appl_args->if_str == NULL) {
>>     +                               usage(argv[0]);
>>     +                               exit(EXIT_FAILURE);
>>     +                       }
>>     +
>>     +                       /* count the number of tokens separated by
>>     ',' */
>>     +                       strcpy(appl_args->if_str, optarg);
>>     +                       for (token = strtok(appl_args->if_str,
>>     ","), i = 0;
>>     +                            token != NULL;
>>     +                            token = strtok(NULL, ","), i++)
>>     +                               ;
>>     +
>>     +                       appl_args->if_count = i;
>>     +
>>     +                       if (appl_args->if_count == 0) {
>>     +                               usage(argv[0]);
>>     +                               exit(EXIT_FAILURE);
>>     +                       }
>
>
diff mbox

Patch

diff --git a/configure.ac b/configure.ac
index 91083b9..f47ac66 100644
--- a/configure.ac
+++ b/configure.ac
@@ -264,7 +264,6 @@  AC_CONFIG_FILES([Makefile
 		 example/Makefile
 		 example/generator/Makefile
 		 example/ipsec/Makefile
-		 example/l2fwd/Makefile
 		 example/packet/Makefile
 		 example/timer/Makefile
 		 pkgconfig/libodp.pc
diff --git a/example/Makefile.am b/example/Makefile.am
index 3021571..6bb4f5c 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -1 +1 @@ 
-SUBDIRS = generator ipsec l2fwd packet timer
+SUBDIRS = generator ipsec packet timer
diff --git a/example/l2fwd/.gitignore b/example/l2fwd/.gitignore
deleted file mode 100644
index 8563319..0000000
--- a/example/l2fwd/.gitignore
+++ /dev/null
@@ -1 +0,0 @@ 
-odp_l2fwd
diff --git a/example/l2fwd/Makefile.am b/example/l2fwd/Makefile.am
deleted file mode 100644
index feced2a..0000000
--- a/example/l2fwd/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@ 
-include $(top_srcdir)/example/Makefile.inc
-
-bin_PROGRAMS = odp_l2fwd
-odp_l2fwd_LDFLAGS = $(AM_LDFLAGS) -static
-odp_l2fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
-
-noinst_HEADERS = \
-		  $(top_srcdir)/example/example_debug.h
-
-dist_odp_l2fwd_SOURCES = odp_l2fwd.c
diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c
deleted file mode 100644
index 4a49008..0000000
--- a/example/l2fwd/odp_l2fwd.c
+++ /dev/null
@@ -1,685 +0,0 @@ 
-/* Copyright (c) 2014, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier:     BSD-3-Clause
- */
-
-/**
- * @file
- *
- * @example odp_l2fwd.c  ODP basic forwarding application
- */
-
-/** enable strtok */
-#define _POSIX_C_SOURCE 200112L
-
-#include <stdlib.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <example_debug.h>
-
-#include <odp.h>
-#include <odp/helper/linux.h>
-#include <odp/helper/eth.h>
-#include <odp/helper/ip.h>
-
-/** @def MAX_WORKERS
- * @brief Maximum number of worker threads
- */
-#define MAX_WORKERS            32
-
-/** @def SHM_PKT_POOL_SIZE
- * @brief Size of the shared memory block
- */
-#define SHM_PKT_POOL_SIZE      (512*2048)
-
-/** @def SHM_PKT_POOL_BUF_SIZE
- * @brief Buffer size of the packet pool buffer
- */
-#define SHM_PKT_POOL_BUF_SIZE  1856
-
-/** @def MAX_PKT_BURST
- * @brief Maximum number of packet bursts
- */
-#define MAX_PKT_BURST          16
-
-/** @def APPL_MODE_PKT_BURST
- * @brief The application will handle pakcets in bursts
- */
-#define APPL_MODE_PKT_BURST    0
-
-/** @def APPL_MODE_PKT_QUEUE
- * @brief The application will handle packets in queues
- */
-#define APPL_MODE_PKT_QUEUE    1
-
-/** @def PRINT_APPL_MODE(x)
- * @brief Macro to print the current status of how the application handles
- * packets.
- */
-#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
-
-/** 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))
-/**
- * Parsed command line application arguments
- */
-typedef struct {
-	int cpu_count;
-	int if_count;		/**< Number of interfaces to be used */
-	char **if_names;	/**< Array of pointers to interface names */
-	int mode;		/**< Packet IO mode */
-	int time;		/**< Time in seconds to run. */
-	int accuracy;		/**< Number of seconds to get and print statistics */
-	char *if_str;		/**< Storage for interface names */
-} appl_args_t;
-
-static int exit_threads;	/**< Break workers loop if set to 1 */
-
-/**
- * Statistics
- */
-typedef struct {
-	uint64_t packets;	/**< Number of forwarded packets. */
-	uint64_t drops;		/**< Number of dropped packets. */
-} stats_t;
-
-/**
- * Thread specific arguments
- */
-typedef struct {
-	int src_idx;            /**< Source interface identifier */
-	stats_t **stats;	/**< Per thread packet stats */
-} thread_args_t;
-
-/**
- * Grouping of all global data
- */
-typedef struct {
-	/** Application (parsed) arguments */
-	appl_args_t appl;
-	/** Thread specific arguments */
-	thread_args_t thread[MAX_WORKERS];
-	/** Table of pktio handles */
-	odp_pktio_t pktios[ODP_CONFIG_PKTIO_ENTRIES];
-} args_t;
-
-/** Global pointer to args */
-static args_t *gbl_args;
-
-/* helper funcs */
-static inline odp_queue_t lookup_dest_q(odp_packet_t pkt);
-static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
-static void print_info(char *progname, appl_args_t *appl_args);
-static void usage(char *progname);
-
-/**
- * Packet IO worker thread using ODP queues
- *
- * @param arg  thread arguments of type 'thread_args_t *'
- */
-static void *pktio_queue_thread(void *arg)
-{
-	int thr;
-	odp_queue_t outq_def;
-	odp_packet_t pkt;
-	odp_event_t ev;
-	thread_args_t *thr_args = arg;
-
-	stats_t *stats = calloc(1, sizeof(stats_t));
-	*thr_args->stats = stats;
-
-	thr = odp_thread_id();
-
-	printf("[%02i] QUEUE mode\n", thr);
-
-	/* Loop packets */
-	while (!exit_threads) {
-		/* Use schedule to get buf from any input queue */
-		ev  = odp_schedule(NULL, ODP_SCHED_WAIT);
-		pkt = odp_packet_from_event(ev);
-
-		/* Drop packets with errors */
-		if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
-			stats->drops += 1;
-			continue;
-		}
-
-		outq_def = lookup_dest_q(pkt);
-
-		/* Enqueue the packet for output */
-		odp_queue_enq(outq_def, ev);
-
-		stats->packets += 1;
-	}
-
-	free(stats);
-	return NULL;
-}
-
-/**
- * Lookup the destination pktio for a given packet
- */
-static inline odp_queue_t lookup_dest_q(odp_packet_t pkt)
-{
-	int i, src_idx, dst_idx;
-	odp_pktio_t pktio_src, pktio_dst;
-
-	pktio_src = odp_packet_input(pkt);
-
-	for (src_idx = -1, i = 0; gbl_args->pktios[i] != ODP_PKTIO_INVALID; ++i)
-		if (gbl_args->pktios[i] == pktio_src)
-			src_idx = i;
-
-	if (src_idx == -1)
-		EXAMPLE_ABORT("Failed to determine pktio input\n");
-
-	dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
-	pktio_dst = gbl_args->pktios[dst_idx];
-
-	return odp_pktio_outq_getdef(pktio_dst);
-}
-
-/**
- * Packet IO worker thread using bursts from/to IO resources
- *
- * @param arg  thread arguments of type 'thread_args_t *'
- */
-static void *pktio_ifburst_thread(void *arg)
-{
-	int thr;
-	thread_args_t *thr_args;
-	int pkts, pkts_ok;
-	odp_packet_t pkt_tbl[MAX_PKT_BURST];
-	int src_idx, dst_idx;
-	odp_pktio_t pktio_src, pktio_dst;
-
-	thr = odp_thread_id();
-	thr_args = arg;
-
-	stats_t *stats = calloc(1, sizeof(stats_t));
-	*thr_args->stats = stats;
-
-	src_idx = thr_args->src_idx;
-	dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
-	pktio_src = gbl_args->pktios[src_idx];
-	pktio_dst = gbl_args->pktios[dst_idx];
-
-	printf("[%02i] srcif:%s dstif:%s spktio:%02" PRIu64
-	       " dpktio:%02" PRIu64 " BURST mode\n",
-	       thr,
-	       gbl_args->appl.if_names[src_idx],
-	       gbl_args->appl.if_names[dst_idx],
-	       odp_pktio_to_u64(pktio_src), odp_pktio_to_u64(pktio_dst));
-
-	/* Loop packets */
-	while (!exit_threads) {
-		pkts = odp_pktio_recv(pktio_src, pkt_tbl, MAX_PKT_BURST);
-		if (pkts <= 0)
-			continue;
-
-		/* Drop packets with errors */
-		pkts_ok = drop_err_pkts(pkt_tbl, pkts);
-		if (pkts_ok > 0)
-			odp_pktio_send(pktio_dst, pkt_tbl, pkts_ok);
-
-		if (odp_unlikely(pkts_ok != pkts))
-			stats->drops += pkts - pkts_ok;
-
-		if (pkts_ok == 0)
-			continue;
-
-		stats->packets += pkts_ok;
-	}
-
-	free(stats);
-	return NULL;
-}
-
-/**
- * Create a pktio handle, optionally associating a default input queue.
- *
- * @param dev Name of device to open
- * @param pool Pool to associate with device for packet RX/TX
- * @param mode Packet processing mode for this device (BURST or QUEUE)
- *
- * @return The handle of the created pktio object.
- * @retval ODP_PKTIO_INVALID if the create fails.
- */
-static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool,
-				int mode)
-{
-	char inq_name[ODP_QUEUE_NAME_LEN];
-	odp_queue_param_t qparam;
-	odp_queue_t inq_def;
-	odp_pktio_t pktio;
-	int ret;
-
-	pktio = odp_pktio_open(dev, pool);
-	if (pktio == ODP_PKTIO_INVALID) {
-		EXAMPLE_ERR("Error: failed to open %s\n", dev);
-		return ODP_PKTIO_INVALID;
-	}
-
-	printf("created pktio %" PRIu64 " (%s)\n",
-	       odp_pktio_to_u64(pktio), dev);
-
-	/* no further setup needed for burst mode */
-	if (mode == APPL_MODE_PKT_BURST)
-		return pktio;
-
-	qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
-	qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
-	qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
-	snprintf(inq_name, sizeof(inq_name), "%" PRIu64 "-pktio_inq_def",
-		 odp_pktio_to_u64(pktio));
-	inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
-
-	inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam);
-	if (inq_def == ODP_QUEUE_INVALID) {
-		EXAMPLE_ERR("Error: pktio queue creation failed\n");
-		return ODP_PKTIO_INVALID;
-	}
-
-	ret = odp_pktio_inq_setdef(pktio, inq_def);
-	if (ret != 0) {
-		EXAMPLE_ERR("Error: default input-Q setup\n");
-		return ODP_PKTIO_INVALID;
-	}
-
-	return pktio;
-}
-
-/**
- *  Print statistics
- *
- * @param num_workers Number of worker threads
- * @param thr_stats Pointer to stats storage
- * @param duration Number of seconds to loop in
- * @param timeout Number of seconds for stats calculation
- *
- */
-static void print_speed_stats(int num_workers, stats_t **thr_stats,
-			      int duration, int timeout)
-{
-	uint64_t pkts, pkts_prev = 0, pps, drops, maximum_pps = 0;
-	int i, elapsed = 0;
-	int loop_forever = (duration == 0);
-
-	do {
-		pkts = 0;
-		drops = 0;
-
-		sleep(timeout);
-
-		for (i = 0; i < num_workers; i++) {
-			pkts += thr_stats[i]->packets;
-			drops += thr_stats[i]->drops;
-		}
-		pps = (pkts - pkts_prev) / timeout;
-		if (pps > maximum_pps)
-			maximum_pps = pps;
-		printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,
-		       maximum_pps);
-
-		printf(" %" PRIu64 " total drops\n", drops);
-
-		elapsed += timeout;
-		pkts_prev = pkts;
-	} while (loop_forever || (elapsed < duration));
-
-	printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
-	       maximum_pps);
-	return;
-}
-
-/**
- * ODP L2 forwarding main function
- */
-int main(int argc, char *argv[])
-{
-	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
-	odp_pool_t pool;
-	int i;
-	int cpu;
-	int num_workers;
-	odp_shm_t shm;
-	odp_cpumask_t cpumask;
-	char cpumaskstr[ODP_CPUMASK_STR_SIZE];
-	odp_pool_param_t params;
-
-	/* Init ODP before calling anything else */
-	if (odp_init_global(NULL, NULL)) {
-		EXAMPLE_ERR("Error: ODP global init failed.\n");
-		exit(EXIT_FAILURE);
-	}
-
-	/* Init this thread */
-	if (odp_init_local()) {
-		EXAMPLE_ERR("Error: ODP local init failed.\n");
-		exit(EXIT_FAILURE);
-	}
-
-	/* Reserve memory for args from shared mem */
-	shm = odp_shm_reserve("shm_args", sizeof(args_t),
-			      ODP_CACHE_LINE_SIZE, 0);
-	gbl_args = odp_shm_addr(shm);
-
-	if (gbl_args == NULL) {
-		EXAMPLE_ERR("Error: shared mem alloc failed.\n");
-		exit(EXIT_FAILURE);
-	}
-	memset(gbl_args, 0, sizeof(*gbl_args));
-
-	/* Parse and store the application arguments */
-	parse_args(argc, argv, &gbl_args->appl);
-
-	/* Print both system and application information */
-	print_info(NO_PATH(argv[0]), &gbl_args->appl);
-
-	/* Default to system CPU count unless user specified */
-	num_workers = MAX_WORKERS;
-	if (gbl_args->appl.cpu_count)
-		num_workers = gbl_args->appl.cpu_count;
-
-	/*
-	 * By default CPU #0 runs Linux kernel background tasks.
-	 * Start mapping thread from CPU #1
-	 */
-	num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
-	(void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
-
-	printf("num worker threads: %i\n", num_workers);
-	printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));
-	printf("cpu mask:           %s\n", cpumaskstr);
-
-	if (num_workers < gbl_args->appl.if_count) {
-		EXAMPLE_ERR("Error: CPU count %d less than interface count\n",
-			    num_workers);
-		exit(EXIT_FAILURE);
-	}
-	if (gbl_args->appl.if_count % 2 != 0) {
-		EXAMPLE_ERR("Error: interface count %d is odd in fwd appl.\n",
-			    gbl_args->appl.if_count);
-		exit(EXIT_FAILURE);
-	}
-
-	/* Create packet pool */
-	memset(&params, 0, sizeof(params));
-	params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
-	params.pkt.len     = SHM_PKT_POOL_BUF_SIZE;
-	params.pkt.num     = SHM_PKT_POOL_SIZE/SHM_PKT_POOL_BUF_SIZE;
-	params.type        = ODP_POOL_PACKET;
-
-	pool = odp_pool_create("packet pool", ODP_SHM_NULL, &params);
-
-	if (pool == ODP_POOL_INVALID) {
-		EXAMPLE_ERR("Error: packet pool create failed.\n");
-		exit(EXIT_FAILURE);
-	}
-	odp_pool_print(pool);
-
-	for (i = 0; i < gbl_args->appl.if_count; ++i) {
-		gbl_args->pktios[i] = create_pktio(gbl_args->appl.if_names[i],
-						   pool, gbl_args->appl.mode);
-		if (gbl_args->pktios[i] == ODP_PKTIO_INVALID)
-			exit(EXIT_FAILURE);
-	}
-	gbl_args->pktios[i] = ODP_PKTIO_INVALID;
-
-	memset(thread_tbl, 0, sizeof(thread_tbl));
-
-	stats_t **stats = calloc(1, sizeof(stats_t) * num_workers);
-
-	/* Create worker threads */
-	cpu = odp_cpumask_first(&cpumask);
-	for (i = 0; i < num_workers; ++i) {
-		odp_cpumask_t thd_mask;
-		void *(*thr_run_func) (void *);
-
-		if (gbl_args->appl.mode == APPL_MODE_PKT_BURST)
-			thr_run_func = pktio_ifburst_thread;
-		else /* APPL_MODE_PKT_QUEUE */
-			thr_run_func = pktio_queue_thread;
-
-		gbl_args->thread[i].src_idx = i % gbl_args->appl.if_count;
-		gbl_args->thread[i].stats = &stats[i];
-
-		odp_cpumask_zero(&thd_mask);
-		odp_cpumask_set(&thd_mask, cpu);
-		odph_linux_pthread_create(&thread_tbl[i], &thd_mask,
-					  thr_run_func,
-					  &gbl_args->thread[i]);
-		cpu = odp_cpumask_next(&cpumask, cpu);
-	}
-
-	print_speed_stats(num_workers, stats, gbl_args->appl.time,
-			  gbl_args->appl.accuracy);
-	free(stats);
-	exit_threads = 1;
-
-	/* Master thread waits for other threads to exit */
-	odph_linux_pthread_join(thread_tbl, num_workers);
-
-	free(gbl_args->appl.if_names);
-	free(gbl_args->appl.if_str);
-	printf("Exit\n\n");
-
-	return 0;
-}
-
-/**
- * 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 packet
- * @param len      Length of pkt_tbl[]
- *
- * @return Number of packets with no detected error
- */
-static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
-{
-	odp_packet_t pkt;
-	unsigned pkt_cnt = len;
-	unsigned i, j;
-
-	for (i = 0, j = 0; i < len; ++i) {
-		pkt = pkt_tbl[i];
-
-		if (odp_unlikely(odp_packet_has_error(pkt))) {
-			odp_packet_free(pkt); /* Drop */
-			pkt_cnt--;
-		} else if (odp_unlikely(i != j++)) {
-			pkt_tbl[j-1] = pkt;
-		}
-	}
-
-	return pkt_cnt;
-}
-
-/**
- * Parse and store the command line arguments
- *
- * @param argc       argument count
- * @param argv[]     argument vector
- * @param appl_args  Store application arguments here
- */
-static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
-{
-	int opt;
-	int long_index;
-	char *token;
-	size_t len;
-	int i;
-	static struct option longopts[] = {
-		{"count", required_argument, NULL, 'c'},
-		{"time", required_argument, NULL, 't'},
-		{"accuracy", required_argument, NULL, 'a'},
-		{"interface", required_argument, NULL, 'i'},	/* return 'i' */
-		{"mode", required_argument, NULL, 'm'},		/* return 'm' */
-		{"help", no_argument, NULL, 'h'},		/* return 'h' */
-		{NULL, 0, NULL, 0}
-	};
-
-	appl_args->time = 0; /* loop forever if time to run is 0 */
-	appl_args->accuracy = 1; /* get and print pps stats second */
-	appl_args->mode = -1; /* Invalid, must be changed by parsing */
-
-	while (1) {
-		opt = getopt_long(argc, argv, "+c:+t:+a:i:m:h",
-				  longopts, &long_index);
-
-		if (opt == -1)
-			break;	/* No more options */
-
-		switch (opt) {
-		case 'c':
-			appl_args->cpu_count = atoi(optarg);
-			break;
-		case 't':
-			appl_args->time = atoi(optarg);
-			break;
-		case 'a':
-			appl_args->accuracy = atoi(optarg);
-			break;
-			/* parse packet-io interface names */
-		case 'i':
-			len = strlen(optarg);
-			if (len == 0) {
-				usage(argv[0]);
-				exit(EXIT_FAILURE);
-			}
-			len += 1;	/* add room for '\0' */
-
-			appl_args->if_str = malloc(len);
-			if (appl_args->if_str == NULL) {
-				usage(argv[0]);
-				exit(EXIT_FAILURE);
-			}
-
-			/* count the number of tokens separated by ',' */
-			strcpy(appl_args->if_str, optarg);
-			for (token = strtok(appl_args->if_str, ","), i = 0;
-			     token != NULL;
-			     token = strtok(NULL, ","), i++)
-				;
-
-			appl_args->if_count = i;
-
-			if (appl_args->if_count == 0) {
-				usage(argv[0]);
-				exit(EXIT_FAILURE);
-			}
-
-			/* allocate storage for the if names */
-			appl_args->if_names =
-			    calloc(appl_args->if_count, sizeof(char *));
-
-			/* store the if names (reset names string) */
-			strcpy(appl_args->if_str, optarg);
-			for (token = strtok(appl_args->if_str, ","), i = 0;
-			     token != NULL; token = strtok(NULL, ","), i++) {
-				appl_args->if_names[i] = token;
-			}
-			break;
-
-		case 'm':
-			i = atoi(optarg);
-			if (i == 0)
-				appl_args->mode = APPL_MODE_PKT_BURST;
-			else
-				appl_args->mode = APPL_MODE_PKT_QUEUE;
-			break;
-
-		case 'h':
-			usage(argv[0]);
-			exit(EXIT_SUCCESS);
-			break;
-
-		default:
-			break;
-		}
-	}
-
-	if (appl_args->if_count == 0 || appl_args->mode == -1) {
-		usage(argv[0]);
-		exit(EXIT_FAILURE);
-	}
-
-	optind = 1;		/* reset 'extern optind' from the getopt lib */
-}
-
-/**
- * Print system and application info
- */
-static void print_info(char *progname, appl_args_t *appl_args)
-{
-	int i;
-
-	printf("\n"
-	       "ODP system info\n"
-	       "---------------\n"
-	       "ODP API version: %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_sys_cpu_model_str(), odp_sys_cpu_hz(),
-	       odp_sys_cache_line_size(), odp_cpu_count());
-
-	printf("Running ODP appl: \"%s\"\n"
-	       "-----------------\n"
-	       "IF-count:        %i\n"
-	       "Using IFs:      ",
-	       progname, appl_args->if_count);
-	for (i = 0; i < appl_args->if_count; ++i)
-		printf(" %s", appl_args->if_names[i]);
-	printf("\n"
-	       "Mode:            ");
-	if (appl_args->mode == APPL_MODE_PKT_BURST)
-		PRINT_APPL_MODE(APPL_MODE_PKT_BURST);
-	else
-		PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE);
-	printf("\n\n");
-	fflush(NULL);
-}
-
-/**
- * Prinf usage information
- */
-static void usage(char *progname)
-{
-	printf("\n"
-	       "OpenDataPlane L2 forwarding application.\n"
-	       "\n"
-	       "Usage: %s OPTIONS\n"
-	       "  E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
-	       " In the above example,\n"
-	       " eth0 will send pkts to eth1 and vice versa\n"
-	       " eth2 will send pkts to eth3 and vice versa\n"
-	       "\n"
-	       "Mandatory OPTIONS:\n"
-	       "  -i, --interface Eth interfaces (comma-separated, no spaces)\n"
-	       "  -m, --mode      0: Burst send&receive packets (no queues)\n"
-	       "                  1: Send&receive packets through ODP queues.\n"
-	       "\n"
-	       "Optional OPTIONS\n"
-	       "  -c, --count <number> CPU count.\n"
-	       "  -t, --time  <number> Time in seconds to run.\n"
-	       "  -a, --accuracy <number> Time in seconds get print statistics\n"
-	       "                          (default is 1 second).\n"
-	       "  -h, --help           Display help and exit.\n\n"
-	       " environment variables: ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
-	       "                        ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
-	       "                        ODP_PKTIO_DISABLE_SOCKET_BASIC\n"
-	       " can be used to advanced pkt I/O selection for linux-generic\n"
-	       "\n", NO_PATH(progname), NO_PATH(progname)
-	    );
-}
diff --git a/test/performance/.gitignore b/test/performance/.gitignore
index 1bdb90d..d41d1e1 100644
--- a/test/performance/.gitignore
+++ b/test/performance/.gitignore
@@ -1,4 +1,5 @@ 
 *.log
 *.trs
-odp_scheduling
 odp_atomic
+odp_l2fwd
+odp_scheduling
diff --git a/test/performance/Makefile.am b/test/performance/Makefile.am
index b0f7457..b501584 100644
--- a/test/performance/Makefile.am
+++ b/test/performance/Makefile.am
@@ -1,10 +1,10 @@ 
 include $(top_srcdir)/test/Makefile.inc
 
-TESTS_ENVIRONMENT = TEST_DIR=${builddir}
+TESTS_ENVIRONMENT = TEST_DIR=${builddir} ODP_PLATFORM=${with_platform}
 
 EXECUTABLES = odp_atomic
 
-COMPILE_ONLY = odp_scheduling
+COMPILE_ONLY = odp_scheduling odp_l2fwd
 
 TESTSCRIPTS = odp_scheduling_run
 
diff --git a/test/performance/odp_l2fwd.c b/test/performance/odp_l2fwd.c
new file mode 100644
index 0000000..5d4b833
--- /dev/null
+++ b/test/performance/odp_l2fwd.c
@@ -0,0 +1,685 @@ 
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * @example odp_l2fwd.c  ODP basic forwarding application
+ */
+
+/** enable strtok */
+#define _POSIX_C_SOURCE 200112L
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <test_debug.h>
+
+#include <odp.h>
+#include <odp/helper/linux.h>
+#include <odp/helper/eth.h>
+#include <odp/helper/ip.h>
+
+/** @def MAX_WORKERS
+ * @brief Maximum number of worker threads
+ */
+#define MAX_WORKERS            32
+
+/** @def SHM_PKT_POOL_SIZE
+ * @brief Size of the shared memory block
+ */
+#define SHM_PKT_POOL_SIZE      (512*2048)
+
+/** @def SHM_PKT_POOL_BUF_SIZE
+ * @brief Buffer size of the packet pool buffer
+ */
+#define SHM_PKT_POOL_BUF_SIZE  1856
+
+/** @def MAX_PKT_BURST
+ * @brief Maximum number of packet bursts
+ */
+#define MAX_PKT_BURST          16
+
+/** @def APPL_MODE_PKT_BURST
+ * @brief The application will handle pakcets in bursts
+ */
+#define APPL_MODE_PKT_BURST    0
+
+/** @def APPL_MODE_PKT_QUEUE
+ * @brief The application will handle packets in queues
+ */
+#define APPL_MODE_PKT_QUEUE    1
+
+/** @def PRINT_APPL_MODE(x)
+ * @brief Macro to print the current status of how the application handles
+ * packets.
+ */
+#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
+
+/** 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))
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+	int cpu_count;
+	int if_count;		/**< Number of interfaces to be used */
+	char **if_names;	/**< Array of pointers to interface names */
+	int mode;		/**< Packet IO mode */
+	int time;		/**< Time in seconds to run. */
+	int accuracy;		/**< Number of seconds to get and print statistics */
+	char *if_str;		/**< Storage for interface names */
+} appl_args_t;
+
+static int exit_threads;	/**< Break workers loop if set to 1 */
+
+/**
+ * Statistics
+ */
+typedef struct {
+	uint64_t packets;	/**< Number of forwarded packets. */
+	uint64_t drops;		/**< Number of dropped packets. */
+} stats_t;
+
+/**
+ * Thread specific arguments
+ */
+typedef struct {
+	int src_idx;            /**< Source interface identifier */
+	stats_t **stats;	/**< Per thread packet stats */
+} thread_args_t;
+
+/**
+ * Grouping of all global data
+ */
+typedef struct {
+	/** Application (parsed) arguments */
+	appl_args_t appl;
+	/** Thread specific arguments */
+	thread_args_t thread[MAX_WORKERS];
+	/** Table of pktio handles */
+	odp_pktio_t pktios[ODP_CONFIG_PKTIO_ENTRIES];
+} args_t;
+
+/** Global pointer to args */
+static args_t *gbl_args;
+
+/* helper funcs */
+static inline odp_queue_t lookup_dest_q(odp_packet_t pkt);
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
+static void print_info(char *progname, appl_args_t *appl_args);
+static void usage(char *progname);
+
+/**
+ * Packet IO worker thread using ODP queues
+ *
+ * @param arg  thread arguments of type 'thread_args_t *'
+ */
+static void *pktio_queue_thread(void *arg)
+{
+	int thr;
+	odp_queue_t outq_def;
+	odp_packet_t pkt;
+	odp_event_t ev;
+	thread_args_t *thr_args = arg;
+
+	stats_t *stats = calloc(1, sizeof(stats_t));
+	*thr_args->stats = stats;
+
+	thr = odp_thread_id();
+
+	printf("[%02i] QUEUE mode\n", thr);
+
+	/* Loop packets */
+	while (!exit_threads) {
+		/* Use schedule to get buf from any input queue */
+		ev  = odp_schedule(NULL, ODP_SCHED_WAIT);
+		pkt = odp_packet_from_event(ev);
+
+		/* Drop packets with errors */
+		if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
+			stats->drops += 1;
+			continue;
+		}
+
+		outq_def = lookup_dest_q(pkt);
+
+		/* Enqueue the packet for output */
+		odp_queue_enq(outq_def, ev);
+
+		stats->packets += 1;
+	}
+
+	free(stats);
+	return NULL;
+}
+
+/**
+ * Lookup the destination pktio for a given packet
+ */
+static inline odp_queue_t lookup_dest_q(odp_packet_t pkt)
+{
+	int i, src_idx, dst_idx;
+	odp_pktio_t pktio_src, pktio_dst;
+
+	pktio_src = odp_packet_input(pkt);
+
+	for (src_idx = -1, i = 0; gbl_args->pktios[i] != ODP_PKTIO_INVALID; ++i)
+		if (gbl_args->pktios[i] == pktio_src)
+			src_idx = i;
+
+	if (src_idx == -1)
+		LOG_ABORT("Failed to determine pktio input\n");
+
+	dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
+	pktio_dst = gbl_args->pktios[dst_idx];
+
+	return odp_pktio_outq_getdef(pktio_dst);
+}
+
+/**
+ * Packet IO worker thread using bursts from/to IO resources
+ *
+ * @param arg  thread arguments of type 'thread_args_t *'
+ */
+static void *pktio_ifburst_thread(void *arg)
+{
+	int thr;
+	thread_args_t *thr_args;
+	int pkts, pkts_ok;
+	odp_packet_t pkt_tbl[MAX_PKT_BURST];
+	int src_idx, dst_idx;
+	odp_pktio_t pktio_src, pktio_dst;
+
+	thr = odp_thread_id();
+	thr_args = arg;
+
+	stats_t *stats = calloc(1, sizeof(stats_t));
+	*thr_args->stats = stats;
+
+	src_idx = thr_args->src_idx;
+	dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
+	pktio_src = gbl_args->pktios[src_idx];
+	pktio_dst = gbl_args->pktios[dst_idx];
+
+	printf("[%02i] srcif:%s dstif:%s spktio:%02" PRIu64
+	       " dpktio:%02" PRIu64 " BURST mode\n",
+	       thr,
+	       gbl_args->appl.if_names[src_idx],
+	       gbl_args->appl.if_names[dst_idx],
+	       odp_pktio_to_u64(pktio_src), odp_pktio_to_u64(pktio_dst));
+
+	/* Loop packets */
+	while (!exit_threads) {
+		pkts = odp_pktio_recv(pktio_src, pkt_tbl, MAX_PKT_BURST);
+		if (pkts <= 0)
+			continue;
+
+		/* Drop packets with errors */
+		pkts_ok = drop_err_pkts(pkt_tbl, pkts);
+		if (pkts_ok > 0)
+			odp_pktio_send(pktio_dst, pkt_tbl, pkts_ok);
+
+		if (odp_unlikely(pkts_ok != pkts))
+			stats->drops += pkts - pkts_ok;
+
+		if (pkts_ok == 0)
+			continue;
+
+		stats->packets += pkts_ok;
+	}
+
+	free(stats);
+	return NULL;
+}
+
+/**
+ * Create a pktio handle, optionally associating a default input queue.
+ *
+ * @param dev Name of device to open
+ * @param pool Pool to associate with device for packet RX/TX
+ * @param mode Packet processing mode for this device (BURST or QUEUE)
+ *
+ * @return The handle of the created pktio object.
+ * @retval ODP_PKTIO_INVALID if the create fails.
+ */
+static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool,
+				int mode)
+{
+	char inq_name[ODP_QUEUE_NAME_LEN];
+	odp_queue_param_t qparam;
+	odp_queue_t inq_def;
+	odp_pktio_t pktio;
+	int ret;
+
+	pktio = odp_pktio_open(dev, pool);
+	if (pktio == ODP_PKTIO_INVALID) {
+		LOG_ERR("Error: failed to open %s\n", dev);
+		return ODP_PKTIO_INVALID;
+	}
+
+	printf("created pktio %" PRIu64 " (%s)\n",
+	       odp_pktio_to_u64(pktio), dev);
+
+	/* no further setup needed for burst mode */
+	if (mode == APPL_MODE_PKT_BURST)
+		return pktio;
+
+	qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
+	qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
+	qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+	snprintf(inq_name, sizeof(inq_name), "%" PRIu64 "-pktio_inq_def",
+		 odp_pktio_to_u64(pktio));
+	inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
+
+	inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam);
+	if (inq_def == ODP_QUEUE_INVALID) {
+		LOG_ERR("Error: pktio queue creation failed\n");
+		return ODP_PKTIO_INVALID;
+	}
+
+	ret = odp_pktio_inq_setdef(pktio, inq_def);
+	if (ret != 0) {
+		LOG_ERR("Error: default input-Q setup\n");
+		return ODP_PKTIO_INVALID;
+	}
+
+	return pktio;
+}
+
+/**
+ *  Print statistics
+ *
+ * @param num_workers Number of worker threads
+ * @param thr_stats Pointer to stats storage
+ * @param duration Number of seconds to loop in
+ * @param timeout Number of seconds for stats calculation
+ *
+ */
+static void print_speed_stats(int num_workers, stats_t **thr_stats,
+			      int duration, int timeout)
+{
+	uint64_t pkts, pkts_prev = 0, pps, drops, maximum_pps = 0;
+	int i, elapsed = 0;
+	int loop_forever = (duration == 0);
+
+	do {
+		pkts = 0;
+		drops = 0;
+
+		sleep(timeout);
+
+		for (i = 0; i < num_workers; i++) {
+			pkts += thr_stats[i]->packets;
+			drops += thr_stats[i]->drops;
+		}
+		pps = (pkts - pkts_prev) / timeout;
+		if (pps > maximum_pps)
+			maximum_pps = pps;
+		printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,
+		       maximum_pps);
+
+		printf(" %" PRIu64 " total drops\n", drops);
+
+		elapsed += timeout;
+		pkts_prev = pkts;
+	} while (loop_forever || (elapsed < duration));
+
+	printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
+	       maximum_pps);
+	return;
+}
+
+/**
+ * ODP L2 forwarding main function
+ */
+int main(int argc, char *argv[])
+{
+	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
+	odp_pool_t pool;
+	int i;
+	int cpu;
+	int num_workers;
+	odp_shm_t shm;
+	odp_cpumask_t cpumask;
+	char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+	odp_pool_param_t params;
+
+	/* Init ODP before calling anything else */
+	if (odp_init_global(NULL, NULL)) {
+		LOG_ERR("Error: ODP global init failed.\n");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Init this thread */
+	if (odp_init_local()) {
+		LOG_ERR("Error: ODP local init failed.\n");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Reserve memory for args from shared mem */
+	shm = odp_shm_reserve("shm_args", sizeof(args_t),
+			      ODP_CACHE_LINE_SIZE, 0);
+	gbl_args = odp_shm_addr(shm);
+
+	if (gbl_args == NULL) {
+		LOG_ERR("Error: shared mem alloc failed.\n");
+		exit(EXIT_FAILURE);
+	}
+	memset(gbl_args, 0, sizeof(*gbl_args));
+
+	/* Parse and store the application arguments */
+	parse_args(argc, argv, &gbl_args->appl);
+
+	/* Print both system and application information */
+	print_info(NO_PATH(argv[0]), &gbl_args->appl);
+
+	/* Default to system CPU count unless user specified */
+	num_workers = MAX_WORKERS;
+	if (gbl_args->appl.cpu_count)
+		num_workers = gbl_args->appl.cpu_count;
+
+	/*
+	 * By default CPU #0 runs Linux kernel background tasks.
+	 * Start mapping thread from CPU #1
+	 */
+	num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
+	(void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+	printf("num worker threads: %i\n", num_workers);
+	printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));
+	printf("cpu mask:           %s\n", cpumaskstr);
+
+	if (num_workers < gbl_args->appl.if_count) {
+		LOG_ERR("Error: CPU count %d less than interface count\n",
+			num_workers);
+		exit(EXIT_FAILURE);
+	}
+	if (gbl_args->appl.if_count % 2 != 0) {
+		LOG_ERR("Error: interface count %d is odd in fwd appl.\n",
+			gbl_args->appl.if_count);
+		exit(EXIT_FAILURE);
+	}
+
+	/* Create packet pool */
+	memset(&params, 0, sizeof(params));
+	params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
+	params.pkt.len     = SHM_PKT_POOL_BUF_SIZE;
+	params.pkt.num     = SHM_PKT_POOL_SIZE/SHM_PKT_POOL_BUF_SIZE;
+	params.type        = ODP_POOL_PACKET;
+
+	pool = odp_pool_create("packet pool", ODP_SHM_NULL, &params);
+
+	if (pool == ODP_POOL_INVALID) {
+		LOG_ERR("Error: packet pool create failed.\n");
+		exit(EXIT_FAILURE);
+	}
+	odp_pool_print(pool);
+
+	for (i = 0; i < gbl_args->appl.if_count; ++i) {
+		gbl_args->pktios[i] = create_pktio(gbl_args->appl.if_names[i],
+						   pool, gbl_args->appl.mode);
+		if (gbl_args->pktios[i] == ODP_PKTIO_INVALID)
+			exit(EXIT_FAILURE);
+	}
+	gbl_args->pktios[i] = ODP_PKTIO_INVALID;
+
+	memset(thread_tbl, 0, sizeof(thread_tbl));
+
+	stats_t **stats = calloc(1, sizeof(stats_t) * num_workers);
+
+	/* Create worker threads */
+	cpu = odp_cpumask_first(&cpumask);
+	for (i = 0; i < num_workers; ++i) {
+		odp_cpumask_t thd_mask;
+		void *(*thr_run_func) (void *);
+
+		if (gbl_args->appl.mode == APPL_MODE_PKT_BURST)
+			thr_run_func = pktio_ifburst_thread;
+		else /* APPL_MODE_PKT_QUEUE */
+			thr_run_func = pktio_queue_thread;
+
+		gbl_args->thread[i].src_idx = i % gbl_args->appl.if_count;
+		gbl_args->thread[i].stats = &stats[i];
+
+		odp_cpumask_zero(&thd_mask);
+		odp_cpumask_set(&thd_mask, cpu);
+		odph_linux_pthread_create(&thread_tbl[i], &thd_mask,
+					  thr_run_func,
+					  &gbl_args->thread[i]);
+		cpu = odp_cpumask_next(&cpumask, cpu);
+	}
+
+	print_speed_stats(num_workers, stats, gbl_args->appl.time,
+			  gbl_args->appl.accuracy);
+	free(stats);
+	exit_threads = 1;
+
+	/* Master thread waits for other threads to exit */
+	odph_linux_pthread_join(thread_tbl, num_workers);
+
+	free(gbl_args->appl.if_names);
+	free(gbl_args->appl.if_str);
+	printf("Exit\n\n");
+
+	return 0;
+}
+
+/**
+ * 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 packet
+ * @param len      Length of pkt_tbl[]
+ *
+ * @return Number of packets with no detected error
+ */
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
+{
+	odp_packet_t pkt;
+	unsigned pkt_cnt = len;
+	unsigned i, j;
+
+	for (i = 0, j = 0; i < len; ++i) {
+		pkt = pkt_tbl[i];
+
+		if (odp_unlikely(odp_packet_has_error(pkt))) {
+			odp_packet_free(pkt); /* Drop */
+			pkt_cnt--;
+		} else if (odp_unlikely(i != j++)) {
+			pkt_tbl[j-1] = pkt;
+		}
+	}
+
+	return pkt_cnt;
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc       argument count
+ * @param argv[]     argument vector
+ * @param appl_args  Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+	int opt;
+	int long_index;
+	char *token;
+	size_t len;
+	int i;
+	static struct option longopts[] = {
+		{"count", required_argument, NULL, 'c'},
+		{"time", required_argument, NULL, 't'},
+		{"accuracy", required_argument, NULL, 'a'},
+		{"interface", required_argument, NULL, 'i'},	/* return 'i' */
+		{"mode", required_argument, NULL, 'm'},		/* return 'm' */
+		{"help", no_argument, NULL, 'h'},		/* return 'h' */
+		{NULL, 0, NULL, 0}
+	};
+
+	appl_args->time = 0; /* loop forever if time to run is 0 */
+	appl_args->accuracy = 1; /* get and print pps stats second */
+	appl_args->mode = -1; /* Invalid, must be changed by parsing */
+
+	while (1) {
+		opt = getopt_long(argc, argv, "+c:+t:+a:i:m:h",
+				  longopts, &long_index);
+
+		if (opt == -1)
+			break;	/* No more options */
+
+		switch (opt) {
+		case 'c':
+			appl_args->cpu_count = atoi(optarg);
+			break;
+		case 't':
+			appl_args->time = atoi(optarg);
+			break;
+		case 'a':
+			appl_args->accuracy = atoi(optarg);
+			break;
+			/* parse packet-io interface names */
+		case 'i':
+			len = strlen(optarg);
+			if (len == 0) {
+				usage(argv[0]);
+				exit(EXIT_FAILURE);
+			}
+			len += 1;	/* add room for '\0' */
+
+			appl_args->if_str = malloc(len);
+			if (appl_args->if_str == NULL) {
+				usage(argv[0]);
+				exit(EXIT_FAILURE);
+			}
+
+			/* count the number of tokens separated by ',' */
+			strcpy(appl_args->if_str, optarg);
+			for (token = strtok(appl_args->if_str, ","), i = 0;
+			     token != NULL;
+			     token = strtok(NULL, ","), i++)
+				;
+
+			appl_args->if_count = i;
+
+			if (appl_args->if_count == 0) {
+				usage(argv[0]);
+				exit(EXIT_FAILURE);
+			}
+
+			/* allocate storage for the if names */
+			appl_args->if_names =
+			    calloc(appl_args->if_count, sizeof(char *));
+
+			/* store the if names (reset names string) */
+			strcpy(appl_args->if_str, optarg);
+			for (token = strtok(appl_args->if_str, ","), i = 0;
+			     token != NULL; token = strtok(NULL, ","), i++) {
+				appl_args->if_names[i] = token;
+			}
+			break;
+
+		case 'm':
+			i = atoi(optarg);
+			if (i == 0)
+				appl_args->mode = APPL_MODE_PKT_BURST;
+			else
+				appl_args->mode = APPL_MODE_PKT_QUEUE;
+			break;
+
+		case 'h':
+			usage(argv[0]);
+			exit(EXIT_SUCCESS);
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (appl_args->if_count == 0 || appl_args->mode == -1) {
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	optind = 1;		/* reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args)
+{
+	int i;
+
+	printf("\n"
+	       "ODP system info\n"
+	       "---------------\n"
+	       "ODP API version: %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_sys_cpu_model_str(), odp_sys_cpu_hz(),
+	       odp_sys_cache_line_size(), odp_cpu_count());
+
+	printf("Running ODP appl: \"%s\"\n"
+	       "-----------------\n"
+	       "IF-count:        %i\n"
+	       "Using IFs:      ",
+	       progname, appl_args->if_count);
+	for (i = 0; i < appl_args->if_count; ++i)
+		printf(" %s", appl_args->if_names[i]);
+	printf("\n"
+	       "Mode:            ");
+	if (appl_args->mode == APPL_MODE_PKT_BURST)
+		PRINT_APPL_MODE(APPL_MODE_PKT_BURST);
+	else
+		PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE);
+	printf("\n\n");
+	fflush(NULL);
+}
+
+/**
+ * Prinf usage information
+ */
+static void usage(char *progname)
+{
+	printf("\n"
+	       "OpenDataPlane L2 forwarding application.\n"
+	       "\n"
+	       "Usage: %s OPTIONS\n"
+	       "  E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
+	       " In the above example,\n"
+	       " eth0 will send pkts to eth1 and vice versa\n"
+	       " eth2 will send pkts to eth3 and vice versa\n"
+	       "\n"
+	       "Mandatory OPTIONS:\n"
+	       "  -i, --interface Eth interfaces (comma-separated, no spaces)\n"
+	       "  -m, --mode      0: Burst send&receive packets (no queues)\n"
+	       "                  1: Send&receive packets through ODP queues.\n"
+	       "\n"
+	       "Optional OPTIONS\n"
+	       "  -c, --count <number> CPU count.\n"
+	       "  -t, --time  <number> Time in seconds to run.\n"
+	       "  -a, --accuracy <number> Time in seconds get print statistics\n"
+	       "                          (default is 1 second).\n"
+	       "  -h, --help           Display help and exit.\n\n"
+	       " environment variables: ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
+	       "                        ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
+	       "                        ODP_PKTIO_DISABLE_SOCKET_BASIC\n"
+	       " can be used to advanced pkt I/O selection for linux-generic\n"
+	       "\n", NO_PATH(progname), NO_PATH(progname)
+	    );
+}