diff mbox

[1/2,v6] example: introducing l3fwd

Message ID 1468394986-4951-1-git-send-email-forrest.shi@linaro.org
State New
Headers show

Commit Message

Forrest Shi July 13, 2016, 7:29 a.m. UTC
From: Xuelin Shi <forrest.shi@linaro.org>


multi-thread, multi-queues and bi-directional forwarding.

support (port, queue, thread) arguments in cmdline which specify how
the threads handle which rx queue at which port, if no this argument,
default specification used.

both hash and lpm based lookup methods are supported, default lpm.

Signed-off-by: Xuelin Shi <forrest.shi@linaro.org>

---
 example/Makefile.am           |    2 +-
 example/l3fwd/.gitignore      |    4 +
 example/l3fwd/Makefile.am     |   18 +
 example/l3fwd/odp_l3fwd.c     | 1072 +++++++++++++++++++++++++++++++++++++++++
 example/l3fwd/odp_l3fwd_db.c  |  408 ++++++++++++++++
 example/l3fwd/odp_l3fwd_db.h  |  130 +++++
 example/l3fwd/odp_l3fwd_lpm.c |  224 +++++++++
 example/l3fwd/odp_l3fwd_lpm.h |   20 +
 example/m4/configure.m4       |    1 +
 9 files changed, 1878 insertions(+), 1 deletion(-)
 create mode 100644 example/l3fwd/.gitignore
 create mode 100644 example/l3fwd/Makefile.am
 create mode 100644 example/l3fwd/odp_l3fwd.c
 create mode 100644 example/l3fwd/odp_l3fwd_db.c
 create mode 100644 example/l3fwd/odp_l3fwd_db.h
 create mode 100644 example/l3fwd/odp_l3fwd_lpm.c
 create mode 100644 example/l3fwd/odp_l3fwd_lpm.h

-- 
2.1.0.27.g96db324

Comments

Maxim Uvarov July 13, 2016, 9:10 a.m. UTC | #1
On 07/13/16 10:29, forrest.shi@linaro.org wrote:
> From: Xuelin Shi <forrest.shi@linaro.org>

>

> multi-thread, multi-queues and bi-directional forwarding.

>

> support (port, queue, thread) arguments in cmdline which specify how

> the threads handle which rx queue at which port, if no this argument,

> default specification used.

>

> both hash and lpm based lookup methods are supported, default lpm.

>

> Signed-off-by: Xuelin Shi <forrest.shi@linaro.org>

> ---

>   example/Makefile.am           |    2 +-

>   example/l3fwd/.gitignore      |    4 +

>   example/l3fwd/Makefile.am     |   18 +

>   example/l3fwd/odp_l3fwd.c     | 1072 +++++++++++++++++++++++++++++++++++++++++

>   example/l3fwd/odp_l3fwd_db.c  |  408 ++++++++++++++++

>   example/l3fwd/odp_l3fwd_db.h  |  130 +++++

>   example/l3fwd/odp_l3fwd_lpm.c |  224 +++++++++

>   example/l3fwd/odp_l3fwd_lpm.h |   20 +

>   example/m4/configure.m4       |    1 +

>   9 files changed, 1878 insertions(+), 1 deletion(-)

>   create mode 100644 example/l3fwd/.gitignore

>   create mode 100644 example/l3fwd/Makefile.am

>   create mode 100644 example/l3fwd/odp_l3fwd.c

>   create mode 100644 example/l3fwd/odp_l3fwd_db.c

>   create mode 100644 example/l3fwd/odp_l3fwd_db.h

>   create mode 100644 example/l3fwd/odp_l3fwd_lpm.c

>   create mode 100644 example/l3fwd/odp_l3fwd_lpm.h

>

> diff --git a/example/Makefile.am b/example/Makefile.am

> index 37542af..1f1b62e 100644

> --- a/example/Makefile.am

> +++ b/example/Makefile.am

> @@ -1 +1 @@

> -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt l2fwd_simple switch hello

> +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt l2fwd_simple switch hello l3fwd


Hello Forest, you should use one git send-email command for both 
patches. So 2 patches will be in the same email thread,
and not as 2 separate patches.

alphabetical order here is required. And in next update please place all 
entries vertically, newer patches will be simpler.


> diff --git a/example/l3fwd/.gitignore b/example/l3fwd/.gitignore

> new file mode 100644

> index 0000000..4a25ae8

> --- /dev/null

> +++ b/example/l3fwd/.gitignore

> @@ -0,0 +1,4 @@

> +odp_l3fwd

> +Makefile

> +Makefile.in

> +*.o

last 3 entries are not needed due to we already have it in common .git 
ignore file.

> diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am

> new file mode 100644

> index 0000000..5092aa7

> --- /dev/null

> +++ b/example/l3fwd/Makefile.am

> @@ -0,0 +1,18 @@

> +include $(top_srcdir)/example/Makefile.inc

> +

> +bin_PROGRAMS = odp_l3fwd$(EXEEXT)

> +odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static

> +odp_l3fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example -I${top_srcdir}/test

> +

> +noinst_HEADERS = \

> +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \

> +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_lpm.h \

> +		  $(top_srcdir)/example/example_debug.h

> +

> +dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c odp_l3fwd_lpm.c

> +

> +if test_example

> +TESTS = odp_l3fwd_run.sh

> +endif

> +

> +EXTRA_DIST = odp_l3fwd_run.sh

> diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c

> new file mode 100644

> index 0000000..a10ca76

> --- /dev/null

> +++ b/example/l3fwd/odp_l3fwd.c

> @@ -0,0 +1,1072 @@

> +/* Copyright (c) 2016, Linaro Limited

> + * All rights reserved.

> + *

> + * SPDX-License-Identifier:     BSD-3-Clause

> + */

> +

> +#include <stdlib.h>

> +#include <stdio.h>

> +#include <errno.h>

> +#include <getopt.h>

> +#include <unistd.h>

> +#include <inttypes.h>

> +

> +#include <test_debug.h>

> +

> +#include <odp_api.h>

> +#include <odp/helper/linux.h>

> +#include <odp/helper/eth.h>

> +#include <odp/helper/ip.h>

> +#include <odp/helper/udp.h>

> +#include <odp/helper/tcp.h>

> +

> +#include "odp_l3fwd_db.h"

> +#include "odp_l3fwd_lpm.h"

> +

> +#define POOL_NUM_PKT	8192

> +#define POOL_SEG_LEN	1856

> +#define MAX_PKT_BURST	32

> +

> +#define MAX_NB_WORKER	32

> +#define MAX_NB_PKTIO	32

> +#define MAX_NB_QUEUE	32

> +#define MAX_NB_QCONFS	1024

> +#define MAX_NB_ROUTE	32

> +

> +#define INVALID_ID	(-1)

> +#define PRINT_INTERVAL	10	/* interval seconds of printing stats */

> +

> +/** Get rid of path in filename - only for unix-type paths using '/' */

> +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \

> +			    strrchr((file_name), '/') + 1 : (file_name))

> +

> +struct l3fwd_pktio_s {

> +	odp_pktio_t pktio;

> +	odph_ethaddr_t mac_addr;

> +	odp_pktin_queue_t ifin[MAX_NB_QUEUE];

> +	odp_pktout_queue_t ifout[MAX_NB_QUEUE];

> +	int nb_rxq;

> +	int nb_txq;

> +	int rxq_idx;

> +	int txq_idx;

> +};

> +

> +struct l3fwd_qconf_s {

> +	uint8_t if_idx;		/* port index */

> +	uint8_t rxq_idx;	/* recv queue index in a port */

> +	uint8_t core_idx;	/* this core should handle traffic */

> +};

> +

> +struct thread_arg_s {

> +	uint64_t packets;

> +	uint64_t rx_drops;

> +	uint64_t tx_drops;

> +	struct {

> +		int used;

> +		int rxq[MAX_NB_QUEUE];

> +		int txq[MAX_NB_QUEUE];

> +	} pktio[MAX_NB_PKTIO];

> +	int thr_idx;

> +	int nb_pktio;

> +};

> +

> +typedef struct {

> +	char *if_names[MAX_NB_PKTIO];

> +	int if_count;

> +	char *route_str[MAX_NB_ROUTE];

> +	int worker_count;

> +	struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS];

> +	int qconf_count;

> +	uint32_t duration; /* seconds to run */

> +	uint8_t hash_mode; /* 1:hash, 0:lpm */

> +	uint8_t dest_mac_changed[MAX_NB_PKTIO]; /* 1: dest mac from cmdline */

> +} app_args_t;

> +

> +struct {

> +	app_args_t		cmd_args;

> +	struct l3fwd_pktio_s	l3fwd_pktios[MAX_NB_PKTIO];

> +	odph_odpthread_t	l3fwd_workers[MAX_NB_WORKER];

> +	struct thread_arg_s	worker_args[MAX_NB_WORKER];

> +	odph_ethaddr_t		eth_dest_mac[MAX_NB_PKTIO];

> +

> +	/* forward func, hash or lpm */

> +	int (*fwd_func)(odp_packet_t pkt, int sif);

> +} global;

> +

> +/** Global barrier to synchronize main and workers */

> +static odp_barrier_t barrier;

> +static int exit_threads;	/**< Break workers loop if set to 1 */

> +

> +static void print_usage(char *progname);

if you place print_usage() above main there is no need for declaration. The
same for other functions like print_info(). Not critical but you can 
just remove
that lines and make example shorter.

> +static void print_qconf_table(app_args_t *args);

> +static void print_info(char *progname, app_args_t *args);

> +static int print_speed_stats(int num_workers, int duration, int timeout);

> +static void parse_cmdline_args(int argc, char *argv[], app_args_t *args);

> +static int parse_config(char *cfg_str, app_args_t *args);

> +static void setup_worker_qconf(app_args_t *args);

> +static void setup_fwd_db(void);

> +static int find_port_id_by_name(char *name, app_args_t *args);

> +static int split_string(char *str, int stringlen,

> +			char **tokens, int maxtokens, char delim);

> +

> +static int create_pktio(const char *name, odp_pool_t pool,

> +			struct l3fwd_pktio_s *fwd_pktio)

> +{

> +	odp_pktio_param_t pktio_param;

> +	odp_pktio_t pktio;

> +	odp_pktio_capability_t capa;

> +	int rc;

> +

> +	odp_pktio_param_init(&pktio_param);

> +

> +	pktio = odp_pktio_open(name, pool, &pktio_param);

> +	if (pktio == ODP_PKTIO_INVALID) {

> +		printf("Failed to open %s\n", name);

> +		return -1;

> +	}

> +	fwd_pktio->pktio = pktio;

> +

> +	rc = odp_pktio_capability(pktio, &capa);

> +	if (rc) {

> +		printf("Error: pktio %s: unable to read capabilities!\n",

> +		       name);

> +

> +		return -1;

> +	}

> +

> +	fwd_pktio->nb_rxq = (int)capa.max_input_queues;

> +	fwd_pktio->nb_txq = (int)capa.max_output_queues;

> +

> +	if (fwd_pktio->nb_rxq > MAX_NB_QUEUE)

> +		fwd_pktio->nb_rxq = MAX_NB_QUEUE;

> +

> +	if (fwd_pktio->nb_txq > MAX_NB_QUEUE)

> +		fwd_pktio->nb_txq = MAX_NB_QUEUE;

> +

> +	return 0;

> +}

> +

> +static void setup_fwd_db(void)

> +{

> +	fwd_db_entry_t *entry;

> +	int if_idx;

> +	app_args_t *args;

> +

> +	args = &global.cmd_args;

> +	if (args->hash_mode)

> +		init_fwd_hash_cache();

> +	else

> +		fib_tbl_init();

> +

> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> +		if_idx = entry->oif_id;

> +		if (!args->hash_mode)

> +			fib_tbl_insert(entry->subnet.addr, if_idx,

> +				       entry->subnet.depth);

> +		if (args->dest_mac_changed[if_idx])

> +			global.eth_dest_mac[if_idx] = entry->dst_mac;

> +		else

> +			entry->dst_mac = global.eth_dest_mac[if_idx];

> +	}

> +}

> +

> +static int l3fwd_pkt_hash(odp_packet_t pkt, int sif)

> +{

> +	fwd_db_entry_t *entry;

> +	ipv4_tuple5_t key;

> +	odph_ethhdr_t *eth;

> +	odph_udphdr_t  *udp;

> +	odph_ipv4hdr_t *ip;

> +	uint32_t len;

> +	int dif;

> +

> +	ip = odp_packet_l3_ptr(pkt, &len);

> +	key.dst_ip = odp_be_to_cpu_32(ip->dst_addr);

> +	key.src_ip = odp_be_to_cpu_32(ip->src_addr);

> +	key.proto = ip->proto;

> +

> +	if (odp_packet_has_udp(pkt) ||

> +	    odp_packet_has_tcp(pkt)) {

> +		/* UDP or TCP*/

> +		void *ptr = odp_packet_l4_ptr(pkt, NULL);

> +

> +		udp = (odph_udphdr_t *)ptr;

> +		key.src_port = odp_be_to_cpu_16(udp->src_port);

> +		key.dst_port = odp_be_to_cpu_16(udp->dst_port);

> +	} else {

> +		key.src_port = 0;

> +		key.dst_port = 0;

> +	}

> +

> +	entry = find_fwd_db_entry(&key);

> +	ip->ttl--;

odp_packet_l3_ptr() can return NULL. And you will have null pointer 
deference here for non IP packet.
> +	ip->chksum = odph_ipv4_csum_update(pkt);

> +	eth = odp_packet_l2_ptr(pkt, NULL);

same here eth might be NULL. Better to optimize checks  ifs with 
odp_unlikely().
> +	if (entry) {

> +		eth->src = entry->src_mac;

> +		eth->dst = entry->dst_mac;

> +		dif = entry->oif_id;

> +	} else {

> +		/* no route, send by src port */

> +		eth->dst = eth->src;

> +		dif = sif;

> +	}

> +

> +	return dif;

> +}

> +

> +static int l3fwd_pkt_lpm(odp_packet_t pkt, int sif)

> +{

> +	odph_ipv4hdr_t *ip;

> +	odph_ethhdr_t *eth;

> +	uint32_t len;

> +	int dif;

> +	int ret;

> +

> +	ip = odp_packet_l3_ptr(pkt, &len);

NULL
> +	ip->ttl--;

> +	ip->chksum = odph_ipv4_csum_update(pkt);

> +	eth = odp_packet_l2_ptr(pkt, NULL);

NULL
> +

> +	/* network byte order maybe different from host */

> +	ret = fib_tbl_lookup(odp_be_to_cpu_32(ip->dst_addr), &dif);

> +	if (ret)

> +		dif = sif;

> +

> +	eth->dst = global.eth_dest_mac[dif];

> +	eth->src = global.l3fwd_pktios[dif].mac_addr;

> +

> +	return dif;

> +}

> +

> +/**

> + * Drop packets which input parsing marked as containing errors.

> + *

> + * Frees packets with error and modifies pkt_tbl[] to only contain packets with

> + * no detected errors.

> + *

> + * @param pkt_tbl  Array of packets

> + * @param num      Number of packets in pkt_tbl[]

> + *

> + * @return Number of packets dropped

> + */

> +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)

> +{

> +	odp_packet_t pkt;

> +	unsigned dropped = 0;

> +	unsigned i, j;

> +

> +	for (i = 0, j = 0; i < num; ++i) {

> +		pkt = pkt_tbl[i];

> +

> +		if (odp_unlikely(odp_packet_has_error(pkt) ||

> +				 !odp_packet_has_ipv4(pkt))) {

> +			odp_packet_free(pkt);

> +			dropped++;

> +		} else if (odp_unlikely(i != j++)) {

> +			pkt_tbl[j - 1] = pkt;

> +		}

> +	}

> +

> +	return dropped;

> +}

> +

> +static void l3fwd_one_queue(uint32_t sif, uint8_t rxq_id, void *thr_arg)

> +{

> +	struct l3fwd_pktio_s *port;

> +	odp_packet_t *tbl;

> +	odp_pktout_queue_t outq;

> +	odp_packet_t pkt_tbl[MAX_PKT_BURST];

> +	struct thread_arg_s *arg;

> +	uint8_t txq_id;

> +	int pkts, drop, sent;

> +	int dif, dst_port;

> +	int i;

> +

> +	arg = thr_arg;

> +	port = &global.l3fwd_pktios[sif];

> +	pkts = odp_pktin_recv(port->ifin[rxq_id], pkt_tbl, MAX_PKT_BURST);

> +	if (pkts <= 0)

> +		return;

> +	arg->packets += pkts;

> +	drop = drop_err_pkts(pkt_tbl, pkts);

> +	pkts -= drop;

> +	arg->rx_drops += drop;


Increment one value from number of threads might be not good thing to do 
due to 1) performance 2) atomic operations break.
It's better if each thread has it's own counters and control threads 
summaries values.

> +

> +	dif = global.fwd_func(pkt_tbl[0], sif);

> +	tbl = &pkt_tbl[0];

> +	while (pkts) {

> +		dst_port = dif;

> +		for (i = 1; i < pkts; i++) {

> +			dif = global.fwd_func(tbl[i], sif);

> +			if (dif != dst_port)

> +				break;

> +		}

> +		txq_id = arg->pktio[dst_port].txq[rxq_id];

> +		outq = global.l3fwd_pktios[dst_port].ifout[txq_id];

> +		sent = odp_pktout_send(outq, tbl, i);

> +		if (odp_unlikely(sent < i)) {

> +			sent = sent < 0 ? 0 : sent;

> +			odp_packet_free_multi(&tbl[sent], i - sent);

> +			arg->tx_drops += i - sent;

> +		}

> +

> +		if (i < pkts)

> +			tbl += i;

> +

> +		pkts -= i;

> +	}

> +}

> +

> +static int run_worker(void *arg)

> +{

> +	int if_idx, rxq_idx;

> +	struct thread_arg_s *thr_arg = arg;

> +	struct l3fwd_pktio_s *port;

> +

> +	odp_barrier_wait(&barrier);

> +

> +	while (!exit_threads) {

> +		for (if_idx = 0; if_idx < thr_arg->nb_pktio; if_idx++) {

> +			if (!thr_arg->pktio[if_idx].used ||

> +			    thr_arg->thr_idx == INVALID_ID)

> +				continue;

> +

> +			port = &global.l3fwd_pktios[if_idx];

> +			for (rxq_idx = 0; rxq_idx < port->rxq_idx; rxq_idx++)

> +				l3fwd_one_queue(if_idx, rxq_idx, arg);

> +		}

> +	}

> +

> +	/* Make sure that latest stat writes are visible to other threads */

> +	odp_mb_full();

> +

> +	return 0;

> +}

> +

> +static int find_port_id_by_name(char *name, app_args_t *args)

> +{

> +	int i;

> +

> +	if (!name)

> +		return -1;

> +

> +	for (i = 0; i < args->if_count; i++) {

> +		if (!strcmp(name, args->if_names[i]))

> +			return i;

> +	}

> +

> +	return -1;

> +}

> +

> +int main(int argc, char **argv)

> +{

> +	odph_odpthread_t thread_tbl[MAX_NB_WORKER];

> +	odp_pool_t pool;

> +	odp_pool_param_t params;

> +	odp_instance_t instance;

> +	odph_odpthread_params_t thr_params;

> +	odp_cpumask_t cpumask;

> +	int cpu, i, j, nb_worker;

> +	uint8_t mac[ODPH_ETHADDR_LEN];

> +	app_args_t *args;

> +	struct thread_arg_s *thr_arg;

> +	char *oif;

> +	int oid;

> +

> +	if (odp_init_global(&instance, NULL, NULL)) {

> +		printf("Error: ODP global init failed.\n");

> +		exit(1);

> +	}

> +

> +	if (odp_init_local(instance, ODP_THREAD_CONTROL)) {

> +		printf("Error: ODP local init failed.\n");

> +		exit(1);

> +	}

> +

> +	/* Clear global argument and initialize the dest mac as 2:0:0:0:0:x */

> +	memset(&global, 0, sizeof(global));

> +	mac[0] = 2;

> +	for (i = 0; i < MAX_NB_PKTIO; i++) {

> +		mac[ODPH_ETHADDR_LEN - 1] = (uint8_t)i;

> +		memcpy(global.eth_dest_mac[i].addr, mac, ODPH_ETHADDR_LEN);

> +	}

> +

> +	/* Initialize the thread arguments */

> +	for (i = 0; i < MAX_NB_WORKER; i++) {

> +		thr_arg = &global.worker_args[i];

> +		for (j = 0; j < MAX_NB_PKTIO; j++) {

> +			thr_arg->thr_idx = INVALID_ID;

> +			memset(thr_arg->pktio[j].rxq, INVALID_ID,

> +			       sizeof(thr_arg->pktio[j].rxq));

> +		}

> +	}

> +

> +	/* Parse cmdline arguments */

> +	args = &global.cmd_args;

> +	parse_cmdline_args(argc, argv, args);

> +

> +	/* Init l3fwd table */

> +	init_fwd_db();

> +

> +	/* Add route into table */

> +	for (i = 0; i < MAX_NB_ROUTE; i++) {

> +		if (args->route_str[i]) {

> +			oif = NULL;

> +			create_fwd_db_entry(args->route_str[i], &oif);

> +			oid = find_port_id_by_name(oif, args);

> +			if (oid != -1)

> +				args->dest_mac_changed[oid] = 1;

> +		}

> +	}

> +

> +	print_info(NO_PATH(argv[0]), args);

> +

> +	/* Create packet pool */

> +	odp_pool_param_init(&params);

> +	params.pkt.seg_len = POOL_SEG_LEN;

> +	params.pkt.len     = POOL_SEG_LEN;

> +	params.pkt.num     = POOL_NUM_PKT;

> +	params.type        = ODP_POOL_PACKET;

> +

> +	pool = odp_pool_create("packet pool", &params);

> +

> +	if (pool == ODP_POOL_INVALID) {

> +		printf("Error: packet pool create failed.\n");

> +		exit(1);

> +	}

> +

> +	/* Resolve fwd db*/

> +	for (i = 0; i < args->if_count; i++) {

> +		struct l3fwd_pktio_s *port;

> +		char *if_name;

> +

> +		if_name = args->if_names[i];

> +		port = &global.l3fwd_pktios[i];

> +		if (create_pktio(if_name, pool, port)) {

> +			printf("Error: create pktio %s\n", if_name);

> +			exit(1);

> +		}

> +		odp_pktio_mac_addr(port->pktio, mac, ODPH_ETHADDR_LEN);

> +		resolve_fwd_db(if_name, i, mac);

> +		memcpy(port->mac_addr.addr, mac, ODPH_ETHADDR_LEN);

> +	}

> +

> +	setup_fwd_db();

> +	dump_fwd_db();

> +

> +	/* Dicide available workers */

> +	nb_worker = MAX_NB_WORKER;

> +	if (args->worker_count)

> +		nb_worker = args->worker_count;

> +	nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker);

> +	args->worker_count = nb_worker;

> +

> +	/* Setup rx and tx queues for each port */

> +	setup_worker_qconf(args);

> +	print_qconf_table(args);

> +

> +	/* Decide ip lookup method */

> +	if (args->hash_mode)

> +		global.fwd_func = l3fwd_pkt_hash;

> +	else

> +		global.fwd_func = l3fwd_pkt_lpm;

> +

> +	/* Start all the available ports */

> +	for (i = 0; i < args->if_count; i++) {

> +		struct l3fwd_pktio_s *port;

> +		char *if_name;

> +		char buf[32];

> +

> +		if_name = args->if_names[i];

> +		port = &global.l3fwd_pktios[i];

> +		/* start pktio */

> +		if (odp_pktio_start(port->pktio)) {

> +			printf("unable to start pktio: %s\n", if_name);

> +			exit(1);

> +		}

> +

> +		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",

> +			port->mac_addr.addr[0],

> +			port->mac_addr.addr[1],

> +			port->mac_addr.addr[2],

> +			port->mac_addr.addr[3],

> +			port->mac_addr.addr[4],

> +			port->mac_addr.addr[5]);

> +		printf("start pktio: %s, mac %s\n", if_name, buf);

> +	}

> +

> +	odp_barrier_init(&barrier, nb_worker + 1);

> +

> +	memset(&thr_params, 0, sizeof(thr_params));

> +	thr_params.start    = run_worker;

> +	thr_params.thr_type = ODP_THREAD_WORKER;

> +	thr_params.instance = instance;

> +

> +	memset(thread_tbl, 0, sizeof(thread_tbl));

> +	cpu = odp_cpumask_first(&cpumask);

> +	for (i = 0; i < nb_worker; i++) {

> +		struct thread_arg_s *arg;

> +		odp_cpumask_t thr_mask;

> +

> +		arg = &global.worker_args[i];

> +		arg->nb_pktio = args->if_count;

> +		odp_cpumask_zero(&thr_mask);

> +		odp_cpumask_set(&thr_mask, cpu);

> +		thr_params.arg = arg;

> +		odph_odpthreads_create(&thread_tbl[i], &thr_mask,

> +				       &thr_params);

> +		cpu = odp_cpumask_next(&cpumask, cpu);

> +	}

> +

> +	if (args->duration) {

> +		print_speed_stats(nb_worker, args->duration, PRINT_INTERVAL);

> +		exit_threads = 1;

> +	}

> +

> +	/* wait for other threads to join */

> +	for (i = 0; i < nb_worker; i++)

> +		odph_odpthreads_join(&thread_tbl[i]);

> +


main should end if odp_destroy_global(). And to add this to 'make check' 
requires probably to add some logic when we think that test passed and 
when we thing that test failed.
I think you copied print_speed_stats() from l2fwd,  there return code 
returns if app passed or not.

> +	return 0;

> +}

> +

> +static void print_usage(char *progname)

> +{

> +	printf("\n"

> +	       "ODP L3 forwarding application.\n"

> +	       "\n"

> +	       "Usage: %s OPTIONS\n"

> +	       "  E.g. %s -i eth0,eth1 -r 1.1.1.0/24,eth0 -r 2.2.2.0/24,eth1\n"

> +	       " In the above example,\n"

> +	       " eth0 will send pkts to eth1 and vice versa\n"

> +	       "\n"

> +	       "Mandatory OPTIONS:\n"

> +	       "  -i, --interface eth interfaces (comma-separated, no spaces)\n"

> +	       "  -r, --route SubNet,Intf[,NextHopMAC]\n"

> +	       "	NextHopMAC can be optional\n"

> +	       "\n"

> +	       "Optional OPTIONS:\n"

> +	       "  -s, --style [lpm|hash], ip lookup method\n"

> +	       "	optional, default as lpm\n"

> +	       "  -d, --duration Seconds to run and print stats\n"

> +	       "	optional, default as 0, run forever\n"

> +	       "  -t, --thread Number of threads to do forwarding\n"

> +	       "	optional, default as availbe worker cpu count\n"

> +	       "  -q, --queue  Configure rx queue(s) for port\n"

> +	       "	optional, format: [(port, queue, thread),...]\n"

> +	       "	for example: -q '(0, 0, 1),(1,0,2)'\n"

> +	       "  -h, --help   Display help and exit.\n\n"

> +	       "\n", NO_PATH(progname), NO_PATH(progname)

> +	    );

> +}

> +

> +static void parse_cmdline_args(int argc, char *argv[], app_args_t *args)

> +{

> +	int opt;

> +	int long_index;

> +	char *token, *local;

> +	size_t len, route_index = 0;

> +	int i, mem_failure = 0;

> +

> +	static struct option longopts[] = {

> +		{"interface", required_argument, NULL, 'i'},	/* return 'i' */

> +		{"route", required_argument, NULL, 'r'},	/* return 'r' */

> +		{"style", optional_argument, NULL, 's'},	/* return 's' */

> +		{"duration", optional_argument, NULL, 'd'},	/* return 'd' */

> +		{"thread", optional_argument, NULL, 't'},	/* return 't' */

> +		{"queue", optional_argument, NULL, 'q'},	/* return 'q' */

> +		{"help", no_argument, NULL, 'h'},		/* return 'h' */

> +		{NULL, 0, NULL, 0}

> +	};

> +

> +	while (1) {

> +		opt = getopt_long(argc, argv, "+s:t:d:i:r:q:h",

> +				  longopts, &long_index);

> +

> +		if (opt == -1)

> +			break;	/* No more options */

> +

> +		switch (opt) {

> +		/* parse ip lookup method */

> +		case 's':

> +			if (!strcmp(optarg, "hash"))

> +				args->hash_mode = 1;

> +			break;

> +		/* parse number of worker threads to be run*/

> +		case 't':

> +			i = odp_cpu_count();

> +			args->worker_count = atoi(optarg);

> +			if (args->worker_count > i) {

> +				printf("Too many threads,"

> +				       "truncate to cpu count: %d\n", i);

> +				args->worker_count = i;

> +			}

> +

> +			break;

> +

> +		/* parse seconds to run */

> +		case 'd':

> +			args->duration = atoi(optarg);

> +			break;

> +

> +		/* parse packet-io interface names */

> +		case 'i':

> +			len = strlen(optarg);

> +			if (len == 0) {

> +				print_usage(argv[0]);

> +				exit(EXIT_FAILURE);

> +			}

> +			len += 1;	/* add room for '\0' */

> +

> +			local = malloc(len);

> +			if (!local) {

> +				print_usage(argv[0]);

> +				exit(EXIT_FAILURE);

> +			}

> +

> +			/* count the number of tokens separated by ',' */

> +			strcpy(local, optarg);

> +			for (token = strtok(local, ","), i = 0;

> +			     token != NULL;

> +			     token = strtok(NULL, ","), i++)

> +				;

> +

> +			if (i == 0) {

> +				print_usage(argv[0]);

> +				exit(EXIT_FAILURE);

> +			} else if (i > MAX_NB_PKTIO) {

> +				printf("too many ports specified, "

> +				       "truncated to %d", MAX_NB_PKTIO);

> +			}

> +			args->if_count = i;

> +

> +			/* store the if names (reset names string) */

> +			strcpy(local, optarg);

> +			for (token = strtok(local, ","), i = 0;

> +			     token != NULL; token = strtok(NULL, ","), i++) {

> +				args->if_names[i] = token;

> +			}

> +			break;

> +

> +		/*Configure Route in forwarding database*/

> +		case 'r':

> +			if (route_index >= MAX_NB_ROUTE) {

> +				printf("No more routes can be added\n");

> +				break;

> +			}

> +			local = calloc(1, strlen(optarg) + 1);

> +			if (!local) {

> +				mem_failure = 1;

> +				break;

> +			}

> +			memcpy(local, optarg, strlen(optarg));

> +			local[strlen(optarg)] = '\0';

> +			args->route_str[route_index++] = local;

> +			break;

> +

> +		case 'h':

> +			print_usage(argv[0]);

> +			exit(EXIT_SUCCESS);

> +			break;

> +

> +		case 'q':

> +			parse_config(optarg, args);

> +			break;

> +

> +		default:

> +			break;

> +		}

> +	}

> +

> +	/* checking arguments */

> +	if (args->if_count == 0) {

> +		printf("\nNo option -i specified.\n");

> +		goto out;

> +	}

> +

> +	if (args->route_str[0] == NULL) {

> +		printf("\nNo option -r specified.\n");

> +		goto out;

> +	}

> +

> +	if (mem_failure == 1) {

> +		printf("\nAllocate memory failure.\n");

> +		goto out;

> +	}

> +	optind = 1;		/* reset 'extern optind' from the getopt lib */

> +	return;

> +

> +out:

> +	print_usage(argv[0]);

> +	exit(EXIT_FAILURE);

> +}

> +

> +/* split string into tokens */

> +int split_string(char *str, int stringlen,

> +		 char **tokens, int maxtokens, char delim)

> +{

> +	int i, tok = 0;

> +	int tokstart = 1; /* first token is right at start of string */

> +

> +	if (str == NULL || tokens == NULL)

> +		goto einval_error;

> +

> +	for (i = 0; i < stringlen; i++) {

> +		if (str[i] == '\0' || tok >= maxtokens)

> +			break;

> +		if (tokstart) {

> +			tokstart = 0;

> +			tokens[tok++] = &str[i];

> +		}

> +		if (str[i] == delim) {

> +			str[i] = '\0';

> +			tokstart = 1;

> +		}

> +	}

> +	return tok;

> +

> +einval_error:

> +	errno = EINVAL;

> +	return -1;

> +}

> +

> +static int parse_config(char *cfg_str, app_args_t *args)

> +{

> +	char s[256];

> +	const char *p, *p0 = cfg_str;

> +	char *end;

> +	enum fieldnames {

> +		FLD_PORT = 0,

> +		FLD_QUEUE,

> +		FLD_LCORE,

> +		FLD_LAST

> +	};

> +	unsigned long int_fld[FLD_LAST];

> +	char *str_fld[FLD_LAST];

> +	int i;

> +	unsigned size;

> +	int nb_qconfs = 0;

> +	struct l3fwd_qconf_s *qconf_array = &args->qconf_config[0];

> +

> +	p = strchr(p0, '(');

> +	while (p != NULL) {

> +		++p;

> +		p0 = strchr(p, ')');

> +		if (p0 == NULL)

> +			return -1;

> +

> +		size = p0 - p;

> +		if (size >= sizeof(s))

> +			return -1;

> +

> +		snprintf(s, sizeof(s), "%.*s", size, p);

> +		i = split_string(s, sizeof(s), str_fld, FLD_LAST, ',');

> +		if (i != FLD_LAST)

> +			return -1;

> +		for (i = 0; i < FLD_LAST; i++) {

> +			errno = 0;

> +			int_fld[i] = strtoul(str_fld[i], &end, 0);

> +			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)

> +				return -1;

> +		}

> +		if (nb_qconfs >= MAX_NB_QCONFS) {

> +			printf("exceeded max number of queue params: %hu\n",

> +			       nb_qconfs);

> +			return -1;

> +		}

> +		qconf_array[nb_qconfs].if_idx = (uint8_t)int_fld[FLD_PORT];

> +		qconf_array[nb_qconfs].rxq_idx = (uint8_t)int_fld[FLD_QUEUE];

> +		qconf_array[nb_qconfs].core_idx = (uint8_t)int_fld[FLD_LCORE];

> +		++nb_qconfs;

> +

> +		p = strchr(p0, '(');

> +	}

> +	args->qconf_count = nb_qconfs;

> +

> +	return 0;

> +}

> +

> +static void print_info(char *progname, app_args_t *args)

> +{

> +	int i;

> +

> +	printf("\n"

> +	       "ODP system info\n"

> +	       "---------------\n"

> +	       "ODP API version: %s\n"

> +	       "ODP impl name:	 %s\n"

> +	       "CPU model:       %s\n"

> +	       "CPU freq (hz):   %" PRIu64 "\n"

> +	       "Cache line size: %i\n"

> +	       "CPU count:       %i\n"

> +	       "\n",

> +	       odp_version_api_str(), odp_version_impl_name(),

> +	       odp_cpu_model_str(), odp_cpu_hz_max(),

> +	       odp_sys_cache_line_size(), odp_cpu_count());

> +

> +	printf("Running ODP appl: \"%s\"\n"

> +	       "-----------------\n"

> +	       "IP Lookup:	 %s\n"

> +	       "IF Count:        %i\n"

> +	       "Using IFs:      ",

> +	       progname,

> +	       args->hash_mode ? "hash" : "lpm",

> +	       args->if_count);

> +

> +	for (i = 0; i < args->if_count; ++i)

> +		printf(" %s", args->if_names[i]);

> +

> +	printf("\n\n");

> +	fflush(NULL);

> +}

> +

> +/**

> + * Setup rx and tx queues, distribute them among threads.

> + * Try to have one tx queue for each rx queue, if not vailable,

> + * shared tx queue is used.

> + *

> + * If no q argument, the queues are distribute among threads as default.

> + * The thread take one rx queue of a port one time as round-robin order.

> + */

> +static void setup_worker_qconf(app_args_t *args)

> +{

> +	int nb_worker, if_count;

> +	int i, j, rxq_idx, txq_idx;

> +	struct thread_arg_s *arg;

> +	struct l3fwd_pktio_s *port;

> +	uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_QUEUE];

> +

> +	nb_worker = args->worker_count;

> +	if_count = args->if_count;

> +

> +	/* distribute queues among threads */

> +	if (!args->qconf_count) {

> +		if (nb_worker > if_count) {

> +			for (i = 0; i < nb_worker; i++) {

> +				arg = &global.worker_args[i];

> +				arg->thr_idx = i;

> +				j = i % if_count;

> +				port = &global.l3fwd_pktios[j];

> +				if (port->rxq_idx < port->nb_rxq) {

> +					rxq_idx = port->rxq_idx;

> +					arg->pktio[j].rxq[rxq_idx] = rxq_idx;

> +					port->rxq_idx++;

> +					txq_idx = port->txq_idx;

> +					arg->pktio[j].txq[txq_idx] = txq_idx;

> +					port->txq_idx++;

> +					arg->pktio[j].used = 1;

> +				}

> +			}

> +		} else {

> +			for (i = 0; i < if_count; i++) {

> +				j = i % nb_worker;

> +				arg = &global.worker_args[j];

> +				arg->thr_idx = j;

> +				port = &global.l3fwd_pktios[i];

> +				if (port->rxq_idx < port->nb_rxq) {

> +					rxq_idx = port->rxq_idx;

> +					arg->pktio[i].rxq[rxq_idx] = rxq_idx;

> +					port->rxq_idx++;

> +					txq_idx = port->txq_idx;

> +					arg->pktio[i].txq[txq_idx] = txq_idx;

> +					port->txq_idx++;

> +					arg->pktio[i].used = 1;

> +				}

> +			}

> +		}

> +	}

> +

> +	/* specified q argument, distribute queues among threads as it says */

> +	memset(queue_mask, 0, sizeof(queue_mask));

> +	for (i = 0; i < args->qconf_count; i++) {

> +		struct l3fwd_qconf_s *q;

> +

> +		q = &args->qconf_config[i];

> +		if (q->core_idx >= nb_worker || q->if_idx >= if_count)

> +			LOG_ABORT("Error queue (%d, %d, %d), max port: "

> +				  "%d, max core: %d\n", q->if_idx, q->rxq_idx,

> +				  q->core_idx, args->if_count - 1,

> +				  args->worker_count - 1);

> +

> +		/* check if one queue is configured twice or more */

> +		if (queue_mask[q->if_idx][q->rxq_idx])

> +			LOG_ABORT("Error queue (%d, %d, %d), reconfig queue\n",

> +				  q->if_idx, q->rxq_idx, q->core_idx);

> +		queue_mask[q->if_idx][q->rxq_idx] = 1;

> +

> +		port = &global.l3fwd_pktios[q->if_idx];

> +		if (port->rxq_idx < q->rxq_idx)

> +			LOG_ABORT("Error queue (%d, %d, %d), queue should be"

> +				  " in sequence and start from 0, queue %d\n",

> +				  q->if_idx, q->rxq_idx, q->core_idx,

> +				  q->rxq_idx);

> +

> +		if (q->rxq_idx > port->nb_rxq) {

> +			LOG_ABORT("Error queue (%d, %d, %d), max queue %d\n",

> +				  q->if_idx, q->rxq_idx, q->core_idx,

> +				  port->nb_rxq - 1);

> +		}

> +		port->rxq_idx = q->rxq_idx + 1;

> +		port->txq_idx = q->rxq_idx + 1;

> +

> +		/* put the queue into worker_args */

> +		arg = &global.worker_args[q->core_idx];

> +		arg->pktio[q->if_idx].rxq[q->rxq_idx] = q->rxq_idx;

> +		arg->pktio[q->if_idx].txq[q->rxq_idx] = q->rxq_idx;

> +		arg->pktio[q->if_idx].used = 1;

> +		arg->thr_idx = q->core_idx;

> +	}

> +

> +	/* config and initialize rx and tx queues. */

> +	for (i = 0; i < if_count; i++) {

> +		odp_pktin_queue_param_t in_queue_param;

> +		odp_pktout_queue_param_t out_queue_param;

> +		struct odp_pktin_queue_t *inq;

> +		struct odp_pktout_queue_t *outq;

> +		const char *name;

> +		int rc, nb_rxq, nb_txq;

> +

> +		port = &global.l3fwd_pktios[i];

> +		name = args->if_names[i];

> +		odp_pktin_queue_param_init(&in_queue_param);

> +		odp_pktout_queue_param_init(&out_queue_param);

> +

> +		in_queue_param.op_mode = ODP_PKTIO_OP_MT;

> +		out_queue_param.op_mode = ODP_PKTIO_OP_MT;

> +

> +		in_queue_param.num_queues = port->rxq_idx;

> +		if (port->rxq_idx > port->nb_rxq) {

> +			in_queue_param.num_queues = port->nb_rxq;

> +			in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;

> +		}

> +

> +		/* enable flow hashing for multi-input queues */

> +		if (in_queue_param.num_queues > 1) {

> +			in_queue_param.hash_enable = 1;

> +			in_queue_param.hash_proto.proto.ipv4_tcp = 1;

> +			in_queue_param.hash_proto.proto.ipv4_udp = 1;

> +		}

> +

> +		if (odp_pktin_queue_config(port->pktio, &in_queue_param))

> +			LOG_ABORT("Fail to config input queue for %s\n", name);

> +

> +		out_queue_param.num_queues = port->txq_idx;

> +		if (port->txq_idx > port->nb_txq) {

> +			out_queue_param.num_queues = port->nb_txq;

> +			out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;

> +		}

> +		if (odp_pktout_queue_config(port->pktio, &out_queue_param))

> +			LOG_ABORT("Fail to config output queue for %s\n", name);

> +

> +		inq = port->ifin;

> +		nb_rxq = in_queue_param.num_queues;

> +		if (odp_pktin_queue(port->pktio, inq, nb_rxq) != nb_rxq)

> +			LOG_ABORT("Fail to set pktin queue for %s\n", name);

> +

> +		if (port->rxq_idx > port->nb_rxq) {

> +			for (rc = port->nb_rxq; rc < port->rxq_idx; rc++)

> +				inq[rc] = inq[rc % port->nb_rxq];

> +		}

> +

> +		outq = port->ifout;

> +		nb_txq = out_queue_param.num_queues;

> +		if (odp_pktout_queue(port->pktio, outq, nb_txq) != nb_txq)

> +			LOG_ABORT("Fail to set pktout queue for %s\n", name);

> +

> +		if (port->txq_idx > port->nb_txq) {

> +			for (rc = port->nb_txq; rc < port->txq_idx; rc++)

> +				outq[rc] = outq[rc % port->nb_txq];

> +		}

> +	}

> +}

> +

> +static void print_qconf_table(app_args_t *args)

> +{

> +	int i, j, k, qid;

> +	char buf[32];

> +	struct thread_arg_s *thr_arg;

> +

> +	printf("Rx queue table\n"

> +	       "-----------------\n"

> +	       "%-16s%-16s%-16s\n",

> +	       "port/id", "queue", "thread");

> +

> +	for (i = 0; i < args->worker_count; i++) {

> +		thr_arg = &global.worker_args[i];

> +		for (j = 0; j < args->if_count; j++) {

> +			if (!thr_arg->pktio[j].used)

> +				continue;

> +

> +			sprintf(buf, "%s/%d", args->if_names[j], j);

> +			for (k = 0; k < MAX_NB_QUEUE; k++) {

> +				qid = thr_arg->pktio[j].rxq[k];

> +				if (qid != INVALID_ID)

> +					printf("%-16s%-16d%-16d\n", buf, qid,

> +					       thr_arg->thr_idx);

> +			}

> +		}

> +	}

> +	printf("\n");

> +	fflush(NULL);

> +}

> +

> +/**

> + *  Print statistics

> + *

> + * @param num_workers Number of worker threads

> + * @param duration Number of seconds to loop in

> + * @param timeout Number of seconds for stats calculation

> + *

> + */

> +static int print_speed_stats(int num_workers, int duration, int timeout)

> +{

> +	uint64_t pkts = 0;

> +	uint64_t pkts_prev = 0;

> +	uint64_t pps;

> +	uint64_t rx_drops, tx_drops;

> +	uint64_t maximum_pps = 0;

> +	int i;

> +	int elapsed = 0;

> +	int stats_enabled = 1;

> +	int loop_forever = (duration == 0);

> +

> +	if (timeout <= 0) {

> +		stats_enabled = 0;

> +		timeout = 1;

> +	}

> +	/* Wait for all threads to be ready*/

> +	odp_barrier_wait(&barrier);

> +

> +	do {

> +		pkts = 0;

> +		rx_drops = 0;

> +		tx_drops = 0;

> +		sleep(timeout);

> +

> +		for (i = 0; i < num_workers; i++) {

> +			pkts += global.worker_args[i].packets;

> +			rx_drops += global.worker_args[i].rx_drops;

> +			tx_drops += global.worker_args[i].tx_drops;

> +		}

> +		if (stats_enabled) {

> +			pps = (pkts - pkts_prev) / timeout;

> +			if (pps > maximum_pps)

> +				maximum_pps = pps;

> +			printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,

> +			       maximum_pps);

> +

> +			printf(" %" PRIu64 " rx drops, %" PRIu64 " tx drops\n",

> +			       rx_drops, tx_drops);

> +

> +			pkts_prev = pkts;

> +		}

> +		elapsed += timeout;

> +	} while (loop_forever || (elapsed < duration));

> +

> +	if (stats_enabled)

> +		printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",

> +		       maximum_pps);

> +

> +	return pkts > 100 ? 0 : -1;

> +}

> diff --git a/example/l3fwd/odp_l3fwd_db.c b/example/l3fwd/odp_l3fwd_db.c

> new file mode 100644

> index 0000000..93e32f0

> --- /dev/null

> +++ b/example/l3fwd/odp_l3fwd_db.c

> @@ -0,0 +1,408 @@

> +/* Copyright (c) 2016, Linaro Limited

> + * All rights reserved.

> + *

> + * SPDX-License-Identifier:     BSD-3-Clause

> + */

> +

> +#ifndef _GNU_SOURCE

> +#define _GNU_SOURCE

> +#endif

> +

> +#include <stdlib.h>

> +#include <string.h>

> +

> +#include <example_debug.h>

> +#include <odp_api.h>

> +#include <odp_l3fwd_db.h>

> +

> +/** Jenkins hash support.

> +  *

> +  * Copyright (C) 2006 Bob Jenkins (bob_jenkins@burtleburtle.net)

> +  *

> +  * http://burtleburtle.net/bob/hash/

> +  *

> +  * These are the credits from Bob's sources:

> +  *

> +  * lookup3.c, by Bob Jenkins, May 2006, Public Domain.

> +  *

> +  * These are functions for producing 32-bit hashes for hash table lookup.

> +  * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()

> +  * are externally useful functions.  Routines to test the hash are included

> +  * if SELF_TEST is defined.  You can use this free for any purpose.  It's in

> +  * the public domain.  It has no warranty.

> +  *

> +  * $FreeBSD$

> +  */

> +#define JHASH_GOLDEN_RATIO	0x9e3779b9

> +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))

> +#define FWD_BJ3_MIX(a, b, c) \

> +{ \

> +	a -= c; a ^= rot(c, 4); c += b; \

> +	b -= a; b ^= rot(a, 6); a += c; \

> +	c -= b; c ^= rot(b, 8); b += a; \

> +	a -= c; a ^= rot(c, 16); c += b; \

> +	b -= a; b ^= rot(a, 19); a += c; \

> +	c -= b; c ^= rot(b, 4); b += a; \

> +}

> +

> +/**

> + * Compute hash value from a flow

> + */

> +static inline

> +uint64_t l3fwd_calc_hash(ipv4_tuple5_t *key)

> +{

> +	uint64_t l4_ports = 0;

> +	uint32_t dst_ip, src_ip;

> +

> +	src_ip = key->src_ip;

> +	dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO;

> +	FWD_BJ3_MIX(src_ip, dst_ip, l4_ports);

> +

> +	return l4_ports;

> +}

> +

> +/**

> + * Parse text string representing an IPv4 address or subnet

> + *

> + * String is of the format "XXX.XXX.XXX.XXX(/W)" where

> + * "XXX" is decimal value and "/W" is optional subnet length

> + *

> + * @param ipaddress  Pointer to IP address/subnet string to convert

> + * @param addr       Pointer to return IPv4 address, host endianness

> + * @param depth      Pointer to subnet bit width

> + * @return 0 if successful else -1

> + */

> +static inline

> +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *depth)

> +{

> +	int b[4];

> +	int qualifier = 32;

> +	int converted;

> +	uint32_t addr_le;

> +

> +	if (strchr(ipaddress, '/')) {

> +		converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",

> +				   &b[3], &b[2], &b[1], &b[0],

> +				   &qualifier);

> +		if (5 != converted)

> +			return -1;

> +	} else {

> +		converted = sscanf(ipaddress, "%d.%d.%d.%d",

> +				   &b[3], &b[2], &b[1], &b[0]);

> +		if (4 != converted)

> +			return -1;

> +	}

> +

> +	if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))

> +		return -1;

> +	if (!qualifier || (qualifier > 32))

> +		return -1;

> +

> +	addr_le = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;

> +	*addr = odp_le_to_cpu_32(addr_le);

> +	*depth = qualifier;

> +

> +	return 0;

> +}

> +

> +/**

> + * Generate text string representing IPv4 range/subnet, output

> + * in "XXX.XXX.XXX.XXX/W" format

> + *

> + * @param b     Pointer to buffer to store string

> + * @param range Pointer to IPv4 address range

> + *

> + * @return Pointer to supplied buffer

> + */

> +static inline

> +char *ipv4_subnet_str(char *b, ip_addr_range_t *range)

> +{

> +	sprintf(b, "%d.%d.%d.%d/%d",

> +		0xFF & ((range->addr) >> 24),

> +		0xFF & ((range->addr) >> 16),

> +		0xFF & ((range->addr) >>  8),

> +		0xFF & ((range->addr) >>  0),

> +		range->depth);

> +	return b;

> +}

> +

> +/**

> + * Generate text string representing MAC address

> + *

> + * @param b     Pointer to buffer to store string

> + * @param mac   Pointer to MAC address

> + *

> + * @return Pointer to supplied buffer

> + */

> +static inline

> +char *mac_addr_str(char *b, odph_ethaddr_t *mac)

> +{

> +	uint8_t *byte;

> +

> +	byte = mac->addr;

> +	sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",

> +		byte[0], byte[1], byte[2], byte[3], byte[4], byte[5]);

> +	return b;

> +}

> +

> +/**

> + * Flow cache table entry

> + */

> +typedef struct flow_entry_s {

> +	ipv4_tuple5_t key;		/**< match key */

> +	struct flow_entry_s *next;      /**< next entry on the list */

> +	fwd_db_entry_t *fwd_entry;	/**< entry info in db */

> +} flow_entry_t;

> +

> +/**

> + * Flow cache table bucket

> + */

> +typedef struct flow_bucket_s {

> +	odp_spinlock_t		lock;	/**< Bucket lock*/

> +	flow_entry_t		*next;	/**< Pointer to first flow entry in bucket*/

> +} flow_bucket_t;

> +

> +/**

> + * Flow hash table, fast lookup cache

> + */

> +typedef struct flow_table_s {

> +	flow_bucket_t *bucket;

> +	uint32_t count;

> +} flow_table_t;

> +

> +static flow_table_t fwd_lookup_cache;

> +

> +void init_fwd_hash_cache(void)

> +{

> +	odp_shm_t		hash_shm;

> +	flow_bucket_t		*bucket;

> +	uint32_t		bucket_count;

> +	uint32_t		i;

> +

> +	bucket_count = FWD_DEF_BUCKET_COUNT;

> +

> +	/*Reserve memory for Routing hash table*/

> +	hash_shm = odp_shm_reserve("route_table",

> +				   sizeof(flow_bucket_t) * bucket_count,

> +				   ODP_CACHE_LINE_SIZE, 0);

> +

> +	bucket = odp_shm_addr(hash_shm);

> +	if (!bucket) {

> +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> +		exit(-1);

> +	}

> +

> +	fwd_lookup_cache.bucket = bucket;

> +	fwd_lookup_cache.count = bucket_count;

> +

> +	/*Initialize Locks*/

> +	for (i = 0; i < bucket_count; i++) {

> +		bucket = &fwd_lookup_cache.bucket[i];

> +		odp_spinlock_init(&bucket->lock);

> +		bucket->next = NULL;

> +	}

> +}

> +

> +static inline

> +int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow)

> +{

> +	if (key->src_ip == flow->key.src_ip &&

> +	    key->dst_ip == flow->key.dst_ip &&

> +	    key->src_port == flow->key.src_port &&

> +	    key->dst_port == flow->key.dst_port &&

> +	    key->proto == flow->key.proto)

> +		return 1;

> +

> +	return 0;

> +}

> +

> +static inline

> +flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t *bucket)

> +{

> +	flow_entry_t *rst;

> +

> +	for (rst = bucket->next; rst != NULL; rst = rst->next) {

> +		if (match_key_flow(key, rst))

> +			break;

> +	}

> +

> +	return rst;

> +}

> +

> +static inline

> +flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key,

> +			       flow_bucket_t *bucket,

> +			       fwd_db_entry_t *entry)

> +{

> +	flow_entry_t *flow;

> +

> +	flow = lookup_fwd_cache(key, bucket);

> +	if (flow)

> +		return flow;

> +

> +	flow = malloc(sizeof(flow_entry_t));

> +	flow->key = *key;

> +	flow->fwd_entry = entry;

> +

> +	odp_spinlock_lock(&bucket->lock);

> +	if (!bucket->next) {

> +		bucket->next = flow;

> +	} else {

> +		flow->next = bucket->next;

> +		bucket->next = flow;

> +	}

> +	odp_spinlock_unlock(&bucket->lock);

> +

> +	return flow;

> +}

> +

> +/** Global pointer to fwd db */

> +fwd_db_t *fwd_db;

> +

> +void init_fwd_db(void)

> +{

> +	odp_shm_t shm;

> +

> +	shm = odp_shm_reserve("shm_fwd_db",

> +			      sizeof(fwd_db_t),

> +			      ODP_CACHE_LINE_SIZE,

> +			      0);

> +

> +	fwd_db = odp_shm_addr(shm);

> +

> +	if (fwd_db == NULL) {

> +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> +		exit(EXIT_FAILURE);

> +	}

> +	memset(fwd_db, 0, sizeof(*fwd_db));

> +}

> +

> +int create_fwd_db_entry(char *input, char **oif)

> +{

> +	int pos = 0;

> +	char *local;

> +	char *str;

> +	char *save;

> +	char *token;

> +	fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];

> +

> +	/* Verify we haven't run out of space */

> +	if (MAX_DB <= fwd_db->index)

> +		return -1;

> +

> +	/* Make a local copy */

> +	local = malloc(strlen(input) + 1);

> +	if (NULL == local)

> +		return -1;

> +	strcpy(local, input);

> +

> +	/* Setup for using "strtok_r" to search input string */

> +	str = local;

> +	save = NULL;

> +

> +	/* Parse tokens separated by ':' */

> +	while (NULL != (token = strtok_r(str, ",", &save))) {

> +		str = NULL;  /* reset str for subsequent strtok_r calls */

> +

> +		/* Parse token based on its position */

> +		switch (pos) {

> +		case 0:

> +			parse_ipv4_string(token,

> +					  &entry->subnet.addr,

> +					  &entry->subnet.depth);

> +			break;

> +		case 1:

> +			strncpy(entry->oif, token, OIF_LEN - 1);

> +			entry->oif[OIF_LEN - 1] = 0;

> +			break;

> +		case 2:

> +			odph_eth_addr_parse(&entry->dst_mac, token);

> +			*oif = entry->oif;

> +			break;

> +

> +		default:

> +			printf("ERROR: extra token \"%s\" at position %d\n",

> +			       token, pos);

> +			break;

> +		}

> +

> +		/* Advance to next position */

> +		pos++;

> +	}

> +

> +	/* Add route to the list */

> +	fwd_db->index++;

> +	entry->next = fwd_db->list;

> +	fwd_db->list = entry;

> +

> +	free(local);

> +	return 0;

> +}

> +

> +void resolve_fwd_db(char *intf, int portid, uint8_t *mac)

> +{

> +	fwd_db_entry_t *entry;

> +

> +	/* Walk the list and attempt to set output and MAC */

> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> +		if (strcmp(intf, entry->oif))

> +			continue;

> +

> +		entry->oif_id = portid;

> +		memcpy(entry->src_mac.addr, mac, ODPH_ETHADDR_LEN);

> +	}

> +}

> +

> +void dump_fwd_db_entry(fwd_db_entry_t *entry)

> +{

> +	char subnet_str[MAX_STRING];

> +	char mac_str[MAX_STRING];

> +

> +	mac_addr_str(mac_str, &entry->dst_mac);

> +	printf("%-16s%-16s%-16s\n",

> +	       ipv4_subnet_str(subnet_str, &entry->subnet),

> +	       entry->oif, mac_str);

> +}

> +

> +void dump_fwd_db(void)

> +{

> +	fwd_db_entry_t *entry;

> +

> +	printf("Routing table\n"

> +	       "-----------------\n"

> +	       "%-16s%-16s%-16s\n",

> +	       "subnet", "next_hop", "dest_mac");

> +

> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> +		dump_fwd_db_entry(entry);

> +

> +	printf("\n");

> +}

> +

> +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key)

> +{

> +	fwd_db_entry_t *entry;

> +	flow_entry_t *flow;

> +	flow_bucket_t *bucket;

> +	uint64_t hash;

> +

> +	/* first find in cache */

> +	hash = l3fwd_calc_hash(key);

> +	hash &= fwd_lookup_cache.count - 1;

> +	bucket = &fwd_lookup_cache.bucket[hash];

> +	flow = lookup_fwd_cache(key, bucket);

> +	if (flow)

> +		return flow->fwd_entry;

> +

> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> +		uint32_t mask;

> +

> +		mask = ((1u << entry->subnet.depth) - 1) <<

> +			(32 - entry->subnet.depth);

> +

> +		if (entry->subnet.addr == (key->dst_ip & mask))

> +			break;

> +	}

> +

> +	return entry;

> +}

> diff --git a/example/l3fwd/odp_l3fwd_db.h b/example/l3fwd/odp_l3fwd_db.h

> new file mode 100644

> index 0000000..840946a

> --- /dev/null

> +++ b/example/l3fwd/odp_l3fwd_db.h

> @@ -0,0 +1,130 @@

> +/* Copyright (c) 2016, Linaro Limited

> + * All rights reserved.

> + *

> + * SPDX-License-Identifier:     BSD-3-Clause

> + */

> +

> +#ifndef ODP_L3FWD_DB_H_

> +#define ODP_L3FWD_DB_H_

> +

> +#ifdef __cplusplus

> +extern "C" {

> +#endif

> +

> +#include <odp_api.h>

> +#include <odp/helper/eth.h>

> +

> +#define OIF_LEN 32

> +#define MAX_DB  32

> +#define MAX_STRING  32

> +

> +/**

> + * Default number of flows

> + */

> +#define FWD_DEF_FLOW_COUNT		100000

> +

> +/**

> + * Default Hash bucket number

> + */

> +#define FWD_DEF_BUCKET_COUNT	(FWD_DEF_FLOW_COUNT / 8)

> +

> +/**

> + * IP address range (subnet)

> + */

> +typedef struct ip_addr_range_s {

> +	uint32_t  addr;     /**< IP address, host endianness */

> +	uint32_t  depth;    /**< subnet bit width */

> +} ip_addr_range_t;

> +

> +/**

> + * TCP/UDP flow

> + */

> +typedef struct ipv4_tuple5_s {

> +	uint32_t src_ip;

> +	uint32_t dst_ip;

> +	uint16_t src_port;

> +	uint16_t dst_port;

> +	uint8_t  proto;

> +} ipv4_tuple5_t;

> +

> +/**

> + * Forwarding data base entry

> + */

> +typedef struct fwd_db_entry_s {

> +	struct fwd_db_entry_s *next;          /**< Next entry on list */

> +	char                    oif[OIF_LEN]; /**< Output interface name */

> +	int			oif_id;	      /**< Output interface idx */

> +	odph_ethaddr_t		src_mac;      /**< Output source MAC */

> +	odph_ethaddr_t		dst_mac;      /**< Output destination MAC */

> +	ip_addr_range_t		subnet;       /**< Subnet for this router */

> +} fwd_db_entry_t;

> +

> +/**

> + * Forwarding data base

> + */

> +typedef struct fwd_db_s {

> +	uint32_t          index;          /**< Next available entry */

> +	fwd_db_entry_t   *list;           /**< List of active routes */

> +	fwd_db_entry_t    array[MAX_DB];  /**< Entry storage */

> +} fwd_db_t;

> +

> +/** Global pointer to fwd db */

> +extern fwd_db_t *fwd_db;

> +

> +/**

> + * Initialize FWD DB

> + */

> +void init_fwd_db(void);

> +

> +/**

> + * Initialize forward lookup cache based on hash

> + */

> +void init_fwd_hash_cache(void);

> +

> +/**

> + * Create a forwarding database entry

> + *

> + * String is of the format "SubNet,Intf,NextHopMAC"

> + *

> + * @param input  Pointer to string describing route

> + * @param oif  Pointer to out interface name, as a return value

> + *

> + * @return 0 if successful else -1

> + */

> +int create_fwd_db_entry(char *input, char **oif);

> +

> +/**

> + * Scan FWD DB entries and resolve output queue and source MAC address

> + *

> + * @param intf   Interface name string

> + * @param portid Output queue for packet transmit

> + * @param mac    MAC address of this interface

> + */

> +void resolve_fwd_db(char *intf, int portid, uint8_t *mac);

> +

> +/**

> + * Display one forwarding database entry

> + *

> + * @param entry  Pointer to entry to display

> + */

> +void dump_fwd_db_entry(fwd_db_entry_t *entry);

> +

> +/**

> + * Display the forwarding database

> + */

> +void dump_fwd_db(void);

> +

> +/**

> + * Find a matching forwarding database entry

> + *

> + * @param key  ipv4 tuple

> + *

> + * @return pointer to forwarding DB entry else NULL

> + */

> +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key);

> +

> +#ifdef __cplusplus

> +}

> +#endif

> +

> +#endif

> diff --git a/example/l3fwd/odp_l3fwd_lpm.c b/example/l3fwd/odp_l3fwd_lpm.c

> new file mode 100644

> index 0000000..1b3bfcf

> --- /dev/null

> +++ b/example/l3fwd/odp_l3fwd_lpm.c

> @@ -0,0 +1,224 @@

> +/* Copyright (c) 2016, Linaro Limited

> + * All rights reserved.

> + *

> + * SPDX-License-Identifier:     BSD-3-Clause

> + */

> +#ifndef _GNU_SOURCE

> +#define _GNU_SOURCE

> +#endif

> +

> +#include <stdio.h>

> +#include <stdlib.h>

> +

> +#include <example_debug.h>

> +#include <odp_api.h>

> +

> +#include <odp_l3fwd_lpm.h>

> +

> +/**

> + * This is a simple implementation of lpm based on patricia tree.

> + *

> + * Tradeoff exists between memory consumption and lookup time.

> + * Currently it prefers 5 levels: {16, 4, 4, 4, 4}, could be 3

> + * levels: {16, 8, 8} by defining FIB_NEXT_STRIDE as 8. Other

> + * levels are also possible.

> + *

> + * the ip here is host endian, when doing init or lookup, the

> + * caller should do endianness conversion if needed.

> + */

> +

> +#define FIB_IP_WIDTH 32

> +#define FIB_FIRST_STRIDE 16

> +#define FIB_NEXT_STRIDE 4

> +#define FIB_NEXT_SIZE (1 << FIB_NEXT_STRIDE)

> +#define FIB_SUB_COUNT 16384

> +#define DEPTH_TO_MASK(depth) ((1 << (depth)) - 1)

> +

> +typedef struct fib_node_s {

> +	union {

> +		uint32_t next_hop;

> +		struct fib_node_s *next; /* next level table */

> +	};

> +	uint8_t valid	:1; /* 1, this node has a valid next hop */

> +	uint8_t end	:1; /* 0, next points to the extended table */

> +	uint8_t depth	:6; /* bit length of subnet mask */

> +} fib_node_t;

> +

> +typedef struct fib_sub_tbl_s {

> +	fib_node_t *fib_nodes;

> +	uint32_t fib_count;

> +	uint32_t fib_idx;

> +} fib_sub_tbl_t;

> +

> +static fib_node_t fib_rt_tbl[1 << FIB_FIRST_STRIDE];

> +static fib_sub_tbl_t fib_lpm_cache;

> +

> +static inline fib_node_t *fib_alloc_sub(void)

> +{

> +	fib_node_t *sub_tbl = NULL;

> +	uint32_t i, nb_entry;

> +

> +	/* extend to next level */

> +	if (fib_lpm_cache.fib_idx < fib_lpm_cache.fib_count) {

> +		nb_entry = (fib_lpm_cache.fib_idx + 1) * FIB_NEXT_SIZE;

> +		sub_tbl = &fib_lpm_cache.fib_nodes[nb_entry];

> +		fib_lpm_cache.fib_idx++;

> +		for (i = 0; i < nb_entry; i++) {

> +			sub_tbl[i].valid = 0;

> +			sub_tbl[i].end = 1;

> +		}

> +	}

> +

> +	return sub_tbl;

> +}

> +

> +static void fib_update_node(fib_node_t *fe, int port, int depth)

> +{

> +	fib_node_t *p;

> +	int i;

> +

> +	if (fe->end) {

> +		if (!fe->valid) {

> +			fe->depth = depth;

> +			fe->next_hop = port;

> +			fe->valid = 1;

> +		} else if (fe->depth <= depth) {

> +			fe->next_hop = port;

> +			fe->depth = depth;

> +		}

> +

> +		return;

> +	}

> +

> +	for (i = 0; i < FIB_NEXT_SIZE; i++) {

> +		p = &fe->next[i];

> +		if (p->end)

> +			fib_update_node(p, port, depth);

> +	}

> +}

> +

> +static void fib_insert_node(fib_node_t *fe, uint32_t ip, uint32_t next_hop,

> +			    int ip_width, int eat_bits, int depth)

> +{

> +	int i;

> +	uint32_t idx, port;

> +	fib_node_t *p;

> +

> +	if (fe->end) {

> +		port = fe->next_hop;

> +		p = fib_alloc_sub();

> +		if (!p)

> +			return;

> +

> +		fe->next = p;

> +		fe->end = 0;

> +		if (fe->valid) {

> +			for (i = 0; i < FIB_NEXT_SIZE; i++) {

> +				p = &fe->next[i];

> +				p->next_hop = port;

> +				p->depth = fe->depth;

> +			}

> +		}

> +	}

> +	if (depth - eat_bits <= FIB_NEXT_STRIDE) {

> +		ip_width -= depth - eat_bits;

> +		idx = ip >> ip_width;

> +		ip &= DEPTH_TO_MASK(ip_width);

> +		p = &fe->next[idx];

> +		fib_update_node(p, next_hop, depth);

> +	} else {

> +		ip_width -= FIB_NEXT_STRIDE;

> +		idx = ip >> ip_width;

> +		p = &fe->next[idx];

> +		ip &= DEPTH_TO_MASK(ip_width);

> +		eat_bits += FIB_NEXT_STRIDE;

> +		fib_insert_node(p, ip, next_hop, ip_width, eat_bits, depth);

> +	}

> +}

> +

> +void fib_tbl_init(void)

> +{

> +	int i;

> +	fib_node_t *fe;

> +	uint32_t size;

> +	odp_shm_t lpm_shm;

> +

> +	for (i = 0; i < (1 << FIB_FIRST_STRIDE); i++) {

> +		fe = &fib_rt_tbl[i];

> +		fe->valid = 0;

> +		fe->end = 1;

> +		fe->depth = 0;

> +		fe->next_hop = 0;

> +	}

> +

> +	size = FIB_NEXT_SIZE * FIB_SUB_COUNT;

> +	/*Reserve memory for Routing hash table*/

> +	lpm_shm = odp_shm_reserve("fib_lpm_sub", size, ODP_CACHE_LINE_SIZE, 0);

> +	fe = odp_shm_addr(lpm_shm);

> +	if (!fe) {

> +		EXAMPLE_ERR("Error: shared mem alloc failed for lpm cache.\n");

> +		exit(-1);

> +	}

> +

> +	fib_lpm_cache.fib_nodes = fe;

> +	fib_lpm_cache.fib_count = FIB_SUB_COUNT;

> +	fib_lpm_cache.fib_idx = 0;

> +}

> +

> +void fib_tbl_insert(uint32_t ip, int port, int depth)

> +{

> +	fib_node_t *fe, *p;

> +	uint32_t idx;

> +	int i, j;

> +	int nb_bits;

> +

> +	nb_bits = FIB_FIRST_STRIDE;

> +	idx = ip >> nb_bits;

> +	fe = &fib_rt_tbl[idx];

> +	if (depth <= nb_bits) {

> +		if (fe->end) {

> +			fe->next_hop = port;

> +			fe->depth = depth;

> +			fe->valid = 1;

> +			return;

> +		}

> +

> +		for (i = 0; i < FIB_NEXT_SIZE; i++) {

> +			p = &fe->next[i];

> +			if (p->end)

> +				fib_update_node(p, port, depth);

> +			else

> +				for (j = 0; j < FIB_NEXT_SIZE; j++)

> +					fib_update_node(&p->next[j], port,

> +							depth);

> +		}

> +

> +		return;

> +	}

> +

> +	/* need to check sub table */

> +	ip &= DEPTH_TO_MASK(FIB_IP_WIDTH - nb_bits);

> +	fib_insert_node(fe, ip, port, FIB_IP_WIDTH - nb_bits, nb_bits, depth);

> +}

> +

> +int fib_tbl_lookup(uint32_t ip, int *port)

> +{

> +	fib_node_t *fe;

> +	uint32_t idx;

> +	int nb_bits;

> +

> +	nb_bits = FIB_IP_WIDTH - FIB_FIRST_STRIDE;

> +	idx = ip >> nb_bits;

> +	fe = &fib_rt_tbl[idx];

> +

> +	ip &= DEPTH_TO_MASK(nb_bits);

> +	while (!fe->end) {

> +		nb_bits -= FIB_NEXT_STRIDE;

> +		idx = ip >> nb_bits;

> +		fe = &fe->next[idx];

> +		ip &= DEPTH_TO_MASK(nb_bits);

> +	}

> +	*port = fe->next_hop;

> +

> +	return fe->valid ? 0 : -1;

> +}

> diff --git a/example/l3fwd/odp_l3fwd_lpm.h b/example/l3fwd/odp_l3fwd_lpm.h

> new file mode 100644

> index 0000000..8f78e39

> --- /dev/null

> +++ b/example/l3fwd/odp_l3fwd_lpm.h

> @@ -0,0 +1,20 @@

> +/* Copyright (c) 2016, Linaro Limited

> + * All rights reserved.

> + *

> + * SPDX-License-Identifier:     BSD-3-Clause

> + */

> +

> +#ifndef ODP_L3FWD_LPM_H_

> +#define ODP_L3FWD_LPM_H_

it has to be _ODP_EXAMPLE_L3FWD_LPM_H

i.e. define should be started with "_" meaning that it's not odp api.
(I know that in some places we use _ODP_ in other places we use ODP_ for 
that
case. But that is wrong. We should not overlap with api name space.).

> +

> +#ifdef __cplusplus

> +extern "C" {

> +#endif

> +void fib_tbl_init(void);

> +void fib_tbl_insert(uint32_t ip, int port, int depth);

> +int fib_tbl_lookup(uint32_t ip, int *port);

> +#ifdef __cplusplus

> +}

> +#endif

> +

> +#endif

> diff --git a/example/m4/configure.m4 b/example/m4/configure.m4

> index bbda38f..78ef396 100644

> --- a/example/m4/configure.m4

> +++ b/example/m4/configure.m4

> @@ -19,5 +19,6 @@ AC_CONFIG_FILES([example/classifier/Makefile

>   		 example/timer/Makefile

>   		 example/traffic_mgmt/Makefile

>   		 example/l2fwd_simple/Makefile

> +		 example/l3fwd/Makefile

>   		 example/switch/Makefile

>   		 example/hello/Makefile])


On thought is what we need with l2fwd. We places it under 
test/performance/ but it's actual example app.
Not sure that it's clear for new users. Definitely l2fwd and l3fwd have 
to be at the same level. I'm thinking
about placing both to test/performance/ and create symlink with some 
README to example/. But I'm not sure
about that, we need to discuss.

Maxim.
Forrest Shi July 15, 2016, 8:22 a.m. UTC | #2
Hi Maxim,

The patch is length.  I extracted the comments I have some question as below,
Others will be fixed.

 Increment one value from number of threads might be not good thing to do
 due to 1) performance 2) atomic operations break.
 It's better if each thread has it's own counters and control threads
 summaries values.
==========================================================
Not catch your idea. Here each thread has its own counters and is totaled
before main exit.


 main should end if odp_destroy_global(). And to add this to 'make check'
 requires probably to add some logic when we think that test passed and
 when we thing that test failed.
 I think you copied print_speed_stats() from l2fwd,  there return code
 returns if app passed or not.
==========================================================
The testing PASS depends on the print_speed_stats() output that is used by
test shell script.
Here return value is not taken  as PASS or FAIL.


On thought is what we need with l2fwd. We places it under
 test/performance/ but it's actual example app.
 Not sure that it's clear for new users. Definitely l2fwd and l3fwd have
 to be at the same level. I'm thinking
 about placing both to test/performance/ and create symlink with some
 README to example/. But I'm not sure
 about that, we need to discuss.
==========================================================
In my opinion, don't need to put app in performance. 
From literal meaning, test should handle testing things, no new app.

Thanks,
Forrest

> -----Original Message-----

> From: lng-odp [mailto:lng-odp-bounces@lists.linaro.org] On Behalf Of Maxim

> Uvarov

> Sent: Wednesday, July 13, 2016 17:10

> To: lng-odp@lists.linaro.org

> Subject: Re: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

> 

> On 07/13/16 10:29, forrest.shi@linaro.org wrote:

> > From: Xuelin Shi <forrest.shi@linaro.org>

> >

> > multi-thread, multi-queues and bi-directional forwarding.

> >

> > support (port, queue, thread) arguments in cmdline which specify how

> > the threads handle which rx queue at which port, if no this argument,

> > default specification used.

> >

> > both hash and lpm based lookup methods are supported, default lpm.

> >

> > Signed-off-by: Xuelin Shi <forrest.shi@linaro.org>

> > ---

> >   example/Makefile.am           |    2 +-

> >   example/l3fwd/.gitignore      |    4 +

> >   example/l3fwd/Makefile.am     |   18 +

> >   example/l3fwd/odp_l3fwd.c     | 1072

> +++++++++++++++++++++++++++++++++++++++++

> >   example/l3fwd/odp_l3fwd_db.c  |  408 ++++++++++++++++

> >   example/l3fwd/odp_l3fwd_db.h  |  130 +++++

> >   example/l3fwd/odp_l3fwd_lpm.c |  224 +++++++++

> >   example/l3fwd/odp_l3fwd_lpm.h |   20 +

> >   example/m4/configure.m4       |    1 +

> >   9 files changed, 1878 insertions(+), 1 deletion(-)

> >   create mode 100644 example/l3fwd/.gitignore

> >   create mode 100644 example/l3fwd/Makefile.am

> >   create mode 100644 example/l3fwd/odp_l3fwd.c

> >   create mode 100644 example/l3fwd/odp_l3fwd_db.c

> >   create mode 100644 example/l3fwd/odp_l3fwd_db.h

> >   create mode 100644 example/l3fwd/odp_l3fwd_lpm.c

> >   create mode 100644 example/l3fwd/odp_l3fwd_lpm.h

> >

> > diff --git a/example/Makefile.am b/example/Makefile.am

> > index 37542af..1f1b62e 100644

> > --- a/example/Makefile.am

> > +++ b/example/Makefile.am

> > @@ -1 +1 @@

> > -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

> l2fwd_simple switch hello

> > +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

> l2fwd_simple switch hello l3fwd

> 

> Hello Forest, you should use one git send-email command for both

> patches. So 2 patches will be in the same email thread,

> and not as 2 separate patches.

> 

> alphabetical order here is required. And in next update please place all

> entries vertically, newer patches will be simpler.

> 


OK, will be fixed.

> 

> > diff --git a/example/l3fwd/.gitignore b/example/l3fwd/.gitignore

> > new file mode 100644

> > index 0000000..4a25ae8

> > --- /dev/null

> > +++ b/example/l3fwd/.gitignore

> > @@ -0,0 +1,4 @@

> > +odp_l3fwd

> > +Makefile

> > +Makefile.in

> > +*.o

> last 3 entries are not needed due to we already have it in common .git

> ignore file.

> 

> > diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am

> > new file mode 100644

> > index 0000000..5092aa7

> > --- /dev/null

> > +++ b/example/l3fwd/Makefile.am

> > @@ -0,0 +1,18 @@

> > +include $(top_srcdir)/example/Makefile.inc

> > +

> > +bin_PROGRAMS = odp_l3fwd$(EXEEXT)

> > +odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static

> > +odp_l3fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example -

> I${top_srcdir}/test

> > +

> > +noinst_HEADERS = \

> > +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \

> > +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_lpm.h \

> > +		  $(top_srcdir)/example/example_debug.h

> > +

> > +dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c odp_l3fwd_lpm.c

> > +

> > +if test_example

> > +TESTS = odp_l3fwd_run.sh

> > +endif

> > +

> > +EXTRA_DIST = odp_l3fwd_run.sh

> > diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c

> > new file mode 100644

> > index 0000000..a10ca76

> > --- /dev/null

> > +++ b/example/l3fwd/odp_l3fwd.c

> > @@ -0,0 +1,1072 @@

> > +/* Copyright (c) 2016, Linaro Limited

> > + * All rights reserved.

> > + *

> > + * SPDX-License-Identifier:     BSD-3-Clause

> > + */

> > +

> > +#include <stdlib.h>

> > +#include <stdio.h>

> > +#include <errno.h>

> > +#include <getopt.h>

> > +#include <unistd.h>

> > +#include <inttypes.h>

> > +

> > +#include <test_debug.h>

> > +

> > +#include <odp_api.h>

> > +#include <odp/helper/linux.h>

> > +#include <odp/helper/eth.h>

> > +#include <odp/helper/ip.h>

> > +#include <odp/helper/udp.h>

> > +#include <odp/helper/tcp.h>

> > +

> > +#include "odp_l3fwd_db.h"

> > +#include "odp_l3fwd_lpm.h"

> > +

> > +#define POOL_NUM_PKT	8192

> > +#define POOL_SEG_LEN	1856

> > +#define MAX_PKT_BURST	32

> > +

> > +#define MAX_NB_WORKER	32

> > +#define MAX_NB_PKTIO	32

> > +#define MAX_NB_QUEUE	32

> > +#define MAX_NB_QCONFS	1024

> > +#define MAX_NB_ROUTE	32

> > +

> > +#define INVALID_ID	(-1)

> > +#define PRINT_INTERVAL	10	/* interval seconds of printing stats

*/
> > +

> > +/** Get rid of path in filename - only for unix-type paths using '/' */

> > +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \

> > +			    strrchr((file_name), '/') + 1 : (file_name))

> > +

> > +struct l3fwd_pktio_s {

> > +	odp_pktio_t pktio;

> > +	odph_ethaddr_t mac_addr;

> > +	odp_pktin_queue_t ifin[MAX_NB_QUEUE];

> > +	odp_pktout_queue_t ifout[MAX_NB_QUEUE];

> > +	int nb_rxq;

> > +	int nb_txq;

> > +	int rxq_idx;

> > +	int txq_idx;

> > +};

> > +

> > +struct l3fwd_qconf_s {

> > +	uint8_t if_idx;		/* port index */

> > +	uint8_t rxq_idx;	/* recv queue index in a port */

> > +	uint8_t core_idx;	/* this core should handle traffic */

> > +};

> > +

> > +struct thread_arg_s {

> > +	uint64_t packets;

> > +	uint64_t rx_drops;

> > +	uint64_t tx_drops;

> > +	struct {

> > +		int used;

> > +		int rxq[MAX_NB_QUEUE];

> > +		int txq[MAX_NB_QUEUE];

> > +	} pktio[MAX_NB_PKTIO];

> > +	int thr_idx;

> > +	int nb_pktio;

> > +};

> > +

> > +typedef struct {

> > +	char *if_names[MAX_NB_PKTIO];

> > +	int if_count;

> > +	char *route_str[MAX_NB_ROUTE];

> > +	int worker_count;

> > +	struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS];

> > +	int qconf_count;

> > +	uint32_t duration; /* seconds to run */

> > +	uint8_t hash_mode; /* 1:hash, 0:lpm */

> > +	uint8_t dest_mac_changed[MAX_NB_PKTIO]; /* 1: dest mac from

> cmdline */

> > +} app_args_t;

> > +

> > +struct {

> > +	app_args_t		cmd_args;

> > +	struct l3fwd_pktio_s	l3fwd_pktios[MAX_NB_PKTIO];

> > +	odph_odpthread_t	l3fwd_workers[MAX_NB_WORKER];

> > +	struct thread_arg_s	worker_args[MAX_NB_WORKER];

> > +	odph_ethaddr_t		eth_dest_mac[MAX_NB_PKTIO];

> > +

> > +	/* forward func, hash or lpm */

> > +	int (*fwd_func)(odp_packet_t pkt, int sif);

> > +} global;

> > +

> > +/** Global barrier to synchronize main and workers */

> > +static odp_barrier_t barrier;

> > +static int exit_threads;	/**< Break workers loop if set to 1 */

> > +

> > +static void print_usage(char *progname);

> if you place print_usage() above main there is no need for declaration. The

> same for other functions like print_info(). Not critical but you can

> just remove

> that lines and make example shorter.

> 


Will be fixed.

> > +static void print_qconf_table(app_args_t *args);

> > +static void print_info(char *progname, app_args_t *args);

> > +static int print_speed_stats(int num_workers, int duration, int timeout);

> > +static void parse_cmdline_args(int argc, char *argv[], app_args_t *args);

> > +static int parse_config(char *cfg_str, app_args_t *args);

> > +static void setup_worker_qconf(app_args_t *args);

> > +static void setup_fwd_db(void);

> > +static int find_port_id_by_name(char *name, app_args_t *args);

> > +static int split_string(char *str, int stringlen,

> > +			char **tokens, int maxtokens, char delim);

> > +

> > +static int create_pktio(const char *name, odp_pool_t pool,

> > +			struct l3fwd_pktio_s *fwd_pktio)

> > +{

> > +	odp_pktio_param_t pktio_param;

> > +	odp_pktio_t pktio;

> > +	odp_pktio_capability_t capa;

> > +	int rc;

> > +

> > +	odp_pktio_param_init(&pktio_param);

> > +

> > +	pktio = odp_pktio_open(name, pool, &pktio_param);

> > +	if (pktio == ODP_PKTIO_INVALID) {

> > +		printf("Failed to open %s\n", name);

> > +		return -1;

> > +	}

> > +	fwd_pktio->pktio = pktio;

> > +

> > +	rc = odp_pktio_capability(pktio, &capa);

> > +	if (rc) {

> > +		printf("Error: pktio %s: unable to read capabilities!\n",

> > +		       name);

> > +

> > +		return -1;

> > +	}

> > +

> > +	fwd_pktio->nb_rxq = (int)capa.max_input_queues;

> > +	fwd_pktio->nb_txq = (int)capa.max_output_queues;

> > +

> > +	if (fwd_pktio->nb_rxq > MAX_NB_QUEUE)

> > +		fwd_pktio->nb_rxq = MAX_NB_QUEUE;

> > +

> > +	if (fwd_pktio->nb_txq > MAX_NB_QUEUE)

> > +		fwd_pktio->nb_txq = MAX_NB_QUEUE;

> > +

> > +	return 0;

> > +}

> > +

> > +static void setup_fwd_db(void)

> > +{

> > +	fwd_db_entry_t *entry;

> > +	int if_idx;

> > +	app_args_t *args;

> > +

> > +	args = &global.cmd_args;

> > +	if (args->hash_mode)

> > +		init_fwd_hash_cache();

> > +	else

> > +		fib_tbl_init();

> > +

> > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> > +		if_idx = entry->oif_id;

> > +		if (!args->hash_mode)

> > +			fib_tbl_insert(entry->subnet.addr, if_idx,

> > +				       entry->subnet.depth);

> > +		if (args->dest_mac_changed[if_idx])

> > +			global.eth_dest_mac[if_idx] = entry->dst_mac;

> > +		else

> > +			entry->dst_mac = global.eth_dest_mac[if_idx];

> > +	}

> > +}

> > +

> > +static int l3fwd_pkt_hash(odp_packet_t pkt, int sif)

> > +{

> > +	fwd_db_entry_t *entry;

> > +	ipv4_tuple5_t key;

> > +	odph_ethhdr_t *eth;

> > +	odph_udphdr_t  *udp;

> > +	odph_ipv4hdr_t *ip;

> > +	uint32_t len;

> > +	int dif;

> > +

> > +	ip = odp_packet_l3_ptr(pkt, &len);

> > +	key.dst_ip = odp_be_to_cpu_32(ip->dst_addr);

> > +	key.src_ip = odp_be_to_cpu_32(ip->src_addr);

> > +	key.proto = ip->proto;

> > +

> > +	if (odp_packet_has_udp(pkt) ||

> > +	    odp_packet_has_tcp(pkt)) {

> > +		/* UDP or TCP*/

> > +		void *ptr = odp_packet_l4_ptr(pkt, NULL);

> > +

> > +		udp = (odph_udphdr_t *)ptr;

> > +		key.src_port = odp_be_to_cpu_16(udp->src_port);

> > +		key.dst_port = odp_be_to_cpu_16(udp->dst_port);

> > +	} else {

> > +		key.src_port = 0;

> > +		key.dst_port = 0;

> > +	}

> > +

> > +	entry = find_fwd_db_entry(&key);

> > +	ip->ttl--;

> odp_packet_l3_ptr() can return NULL. And you will have null pointer

> deference here for non IP packet.

> > +	ip->chksum = odph_ipv4_csum_update(pkt);

> > +	eth = odp_packet_l2_ptr(pkt, NULL);

> same here eth might be NULL. Better to optimize checks  ifs with

> odp_unlikely().


Will be fixed.

> > +	if (entry) {

> > +		eth->src = entry->src_mac;

> > +		eth->dst = entry->dst_mac;

> > +		dif = entry->oif_id;

> > +	} else {

> > +		/* no route, send by src port */

> > +		eth->dst = eth->src;

> > +		dif = sif;

> > +	}

> > +

> > +	return dif;

> > +}

> > +

> > +static int l3fwd_pkt_lpm(odp_packet_t pkt, int sif)

> > +{

> > +	odph_ipv4hdr_t *ip;

> > +	odph_ethhdr_t *eth;

> > +	uint32_t len;

> > +	int dif;

> > +	int ret;

> > +

> > +	ip = odp_packet_l3_ptr(pkt, &len);

> NULL

> > +	ip->ttl--;

> > +	ip->chksum = odph_ipv4_csum_update(pkt);

> > +	eth = odp_packet_l2_ptr(pkt, NULL);

> NULL

> > +

> > +	/* network byte order maybe different from host */

> > +	ret = fib_tbl_lookup(odp_be_to_cpu_32(ip->dst_addr), &dif);

> > +	if (ret)

> > +		dif = sif;

> > +

> > +	eth->dst = global.eth_dest_mac[dif];

> > +	eth->src = global.l3fwd_pktios[dif].mac_addr;

> > +

> > +	return dif;

> > +}

> > +

> > +/**

> > + * Drop packets which input parsing marked as containing errors.

> > + *

> > + * Frees packets with error and modifies pkt_tbl[] to only contain

packets
> with

> > + * no detected errors.

> > + *

> > + * @param pkt_tbl  Array of packets

> > + * @param num      Number of packets in pkt_tbl[]

> > + *

> > + * @return Number of packets dropped

> > + */

> > +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)

> > +{

> > +	odp_packet_t pkt;

> > +	unsigned dropped = 0;

> > +	unsigned i, j;

> > +

> > +	for (i = 0, j = 0; i < num; ++i) {

> > +		pkt = pkt_tbl[i];

> > +

> > +		if (odp_unlikely(odp_packet_has_error(pkt) ||

> > +				 !odp_packet_has_ipv4(pkt))) {

> > +			odp_packet_free(pkt);

> > +			dropped++;

> > +		} else if (odp_unlikely(i != j++)) {

> > +			pkt_tbl[j - 1] = pkt;

> > +		}

> > +	}

> > +

> > +	return dropped;

> > +}

> > +

> > +static void l3fwd_one_queue(uint32_t sif, uint8_t rxq_id, void *thr_arg)

> > +{

> > +	struct l3fwd_pktio_s *port;

> > +	odp_packet_t *tbl;

> > +	odp_pktout_queue_t outq;

> > +	odp_packet_t pkt_tbl[MAX_PKT_BURST];

> > +	struct thread_arg_s *arg;

> > +	uint8_t txq_id;

> > +	int pkts, drop, sent;

> > +	int dif, dst_port;

> > +	int i;

> > +

> > +	arg = thr_arg;

> > +	port = &global.l3fwd_pktios[sif];

> > +	pkts = odp_pktin_recv(port->ifin[rxq_id], pkt_tbl, MAX_PKT_BURST);

> > +	if (pkts <= 0)

> > +		return;

> > +	arg->packets += pkts;

> > +	drop = drop_err_pkts(pkt_tbl, pkts);

> > +	pkts -= drop;

> > +	arg->rx_drops += drop;

> 

> Increment one value from number of threads might be not good thing to do

> due to 1) performance 2) atomic operations break.

> It's better if each thread has it's own counters and control threads

> summaries values.


Not catch your idea. Here each thread has its own counters. 
It will be totaled before exit.

> 

> > +

> > +	dif = global.fwd_func(pkt_tbl[0], sif);

> > +	tbl = &pkt_tbl[0];

> > +	while (pkts) {

> > +		dst_port = dif;

> > +		for (i = 1; i < pkts; i++) {

> > +			dif = global.fwd_func(tbl[i], sif);

> > +			if (dif != dst_port)

> > +				break;

> > +		}

> > +		txq_id = arg->pktio[dst_port].txq[rxq_id];

> > +		outq = global.l3fwd_pktios[dst_port].ifout[txq_id];

> > +		sent = odp_pktout_send(outq, tbl, i);

> > +		if (odp_unlikely(sent < i)) {

> > +			sent = sent < 0 ? 0 : sent;

> > +			odp_packet_free_multi(&tbl[sent], i - sent);

> > +			arg->tx_drops += i - sent;

> > +		}

> > +

> > +		if (i < pkts)

> > +			tbl += i;

> > +

> > +		pkts -= i;

> > +	}

> > +}

> > +

> > +static int run_worker(void *arg)

> > +{

> > +	int if_idx, rxq_idx;

> > +	struct thread_arg_s *thr_arg = arg;

> > +	struct l3fwd_pktio_s *port;

> > +

> > +	odp_barrier_wait(&barrier);

> > +

> > +	while (!exit_threads) {

> > +		for (if_idx = 0; if_idx < thr_arg->nb_pktio; if_idx++) {

> > +			if (!thr_arg->pktio[if_idx].used ||

> > +			    thr_arg->thr_idx == INVALID_ID)

> > +				continue;

> > +

> > +			port = &global.l3fwd_pktios[if_idx];

> > +			for (rxq_idx = 0; rxq_idx < port->rxq_idx; rxq_idx++)

> > +				l3fwd_one_queue(if_idx, rxq_idx, arg);

> > +		}

> > +	}

> > +

> > +	/* Make sure that latest stat writes are visible to other threads */

> > +	odp_mb_full();

> > +

> > +	return 0;

> > +}

> > +

> > +static int find_port_id_by_name(char *name, app_args_t *args)

> > +{

> > +	int i;

> > +

> > +	if (!name)

> > +		return -1;

> > +

> > +	for (i = 0; i < args->if_count; i++) {

> > +		if (!strcmp(name, args->if_names[i]))

> > +			return i;

> > +	}

> > +

> > +	return -1;

> > +}

> > +

> > +int main(int argc, char **argv)

> > +{

> > +	odph_odpthread_t thread_tbl[MAX_NB_WORKER];

> > +	odp_pool_t pool;

> > +	odp_pool_param_t params;

> > +	odp_instance_t instance;

> > +	odph_odpthread_params_t thr_params;

> > +	odp_cpumask_t cpumask;

> > +	int cpu, i, j, nb_worker;

> > +	uint8_t mac[ODPH_ETHADDR_LEN];

> > +	app_args_t *args;

> > +	struct thread_arg_s *thr_arg;

> > +	char *oif;

> > +	int oid;

> > +

> > +	if (odp_init_global(&instance, NULL, NULL)) {

> > +		printf("Error: ODP global init failed.\n");

> > +		exit(1);

> > +	}

> > +

> > +	if (odp_init_local(instance, ODP_THREAD_CONTROL)) {

> > +		printf("Error: ODP local init failed.\n");

> > +		exit(1);

> > +	}

> > +

> > +	/* Clear global argument and initialize the dest mac as 2:0:0:0:0:x */

> > +	memset(&global, 0, sizeof(global));

> > +	mac[0] = 2;

> > +	for (i = 0; i < MAX_NB_PKTIO; i++) {

> > +		mac[ODPH_ETHADDR_LEN - 1] = (uint8_t)i;

> > +		memcpy(global.eth_dest_mac[i].addr, mac,

> ODPH_ETHADDR_LEN);

> > +	}

> > +

> > +	/* Initialize the thread arguments */

> > +	for (i = 0; i < MAX_NB_WORKER; i++) {

> > +		thr_arg = &global.worker_args[i];

> > +		for (j = 0; j < MAX_NB_PKTIO; j++) {

> > +			thr_arg->thr_idx = INVALID_ID;

> > +			memset(thr_arg->pktio[j].rxq, INVALID_ID,

> > +			       sizeof(thr_arg->pktio[j].rxq));

> > +		}

> > +	}

> > +

> > +	/* Parse cmdline arguments */

> > +	args = &global.cmd_args;

> > +	parse_cmdline_args(argc, argv, args);

> > +

> > +	/* Init l3fwd table */

> > +	init_fwd_db();

> > +

> > +	/* Add route into table */

> > +	for (i = 0; i < MAX_NB_ROUTE; i++) {

> > +		if (args->route_str[i]) {

> > +			oif = NULL;

> > +			create_fwd_db_entry(args->route_str[i], &oif);

> > +			oid = find_port_id_by_name(oif, args);

> > +			if (oid != -1)

> > +				args->dest_mac_changed[oid] = 1;

> > +		}

> > +	}

> > +

> > +	print_info(NO_PATH(argv[0]), args);

> > +

> > +	/* Create packet pool */

> > +	odp_pool_param_init(&params);

> > +	params.pkt.seg_len = POOL_SEG_LEN;

> > +	params.pkt.len     = POOL_SEG_LEN;

> > +	params.pkt.num     = POOL_NUM_PKT;

> > +	params.type        = ODP_POOL_PACKET;

> > +

> > +	pool = odp_pool_create("packet pool", &params);

> > +

> > +	if (pool == ODP_POOL_INVALID) {

> > +		printf("Error: packet pool create failed.\n");

> > +		exit(1);

> > +	}

> > +

> > +	/* Resolve fwd db*/

> > +	for (i = 0; i < args->if_count; i++) {

> > +		struct l3fwd_pktio_s *port;

> > +		char *if_name;

> > +

> > +		if_name = args->if_names[i];

> > +		port = &global.l3fwd_pktios[i];

> > +		if (create_pktio(if_name, pool, port)) {

> > +			printf("Error: create pktio %s\n", if_name);

> > +			exit(1);

> > +		}

> > +		odp_pktio_mac_addr(port->pktio, mac, ODPH_ETHADDR_LEN);

> > +		resolve_fwd_db(if_name, i, mac);

> > +		memcpy(port->mac_addr.addr, mac, ODPH_ETHADDR_LEN);

> > +	}

> > +

> > +	setup_fwd_db();

> > +	dump_fwd_db();

> > +

> > +	/* Dicide available workers */

> > +	nb_worker = MAX_NB_WORKER;

> > +	if (args->worker_count)

> > +		nb_worker = args->worker_count;

> > +	nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker);

> > +	args->worker_count = nb_worker;

> > +

> > +	/* Setup rx and tx queues for each port */

> > +	setup_worker_qconf(args);

> > +	print_qconf_table(args);

> > +

> > +	/* Decide ip lookup method */

> > +	if (args->hash_mode)

> > +		global.fwd_func = l3fwd_pkt_hash;

> > +	else

> > +		global.fwd_func = l3fwd_pkt_lpm;

> > +

> > +	/* Start all the available ports */

> > +	for (i = 0; i < args->if_count; i++) {

> > +		struct l3fwd_pktio_s *port;

> > +		char *if_name;

> > +		char buf[32];

> > +

> > +		if_name = args->if_names[i];

> > +		port = &global.l3fwd_pktios[i];

> > +		/* start pktio */

> > +		if (odp_pktio_start(port->pktio)) {

> > +			printf("unable to start pktio: %s\n", if_name);

> > +			exit(1);

> > +		}

> > +

> > +		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",

> > +			port->mac_addr.addr[0],

> > +			port->mac_addr.addr[1],

> > +			port->mac_addr.addr[2],

> > +			port->mac_addr.addr[3],

> > +			port->mac_addr.addr[4],

> > +			port->mac_addr.addr[5]);

> > +		printf("start pktio: %s, mac %s\n", if_name, buf);

> > +	}

> > +

> > +	odp_barrier_init(&barrier, nb_worker + 1);

> > +

> > +	memset(&thr_params, 0, sizeof(thr_params));

> > +	thr_params.start    = run_worker;

> > +	thr_params.thr_type = ODP_THREAD_WORKER;

> > +	thr_params.instance = instance;

> > +

> > +	memset(thread_tbl, 0, sizeof(thread_tbl));

> > +	cpu = odp_cpumask_first(&cpumask);

> > +	for (i = 0; i < nb_worker; i++) {

> > +		struct thread_arg_s *arg;

> > +		odp_cpumask_t thr_mask;

> > +

> > +		arg = &global.worker_args[i];

> > +		arg->nb_pktio = args->if_count;

> > +		odp_cpumask_zero(&thr_mask);

> > +		odp_cpumask_set(&thr_mask, cpu);

> > +		thr_params.arg = arg;

> > +		odph_odpthreads_create(&thread_tbl[i], &thr_mask,

> > +				       &thr_params);

> > +		cpu = odp_cpumask_next(&cpumask, cpu);

> > +	}

> > +

> > +	if (args->duration) {

> > +		print_speed_stats(nb_worker, args->duration,

> PRINT_INTERVAL);

> > +		exit_threads = 1;

> > +	}

> > +

> > +	/* wait for other threads to join */

> > +	for (i = 0; i < nb_worker; i++)

> > +		odph_odpthreads_join(&thread_tbl[i]);

> > +

> 

> main should end if odp_destroy_global(). And to add this to 'make check'

> requires probably to add some logic when we think that test passed and

> when we thing that test failed.

> I think you copied print_speed_stats() from l2fwd,  there return code

> returns if app passed or not.

> 


The testing PASS depends on the print_speed_stats() output in the log file.
Here return value is not taken  as PASS or FAIL.

> > +	return 0;

> > +}

> > +

> > +static void print_usage(char *progname)

> > +{

> > +	printf("\n"

> > +	       "ODP L3 forwarding application.\n"

> > +	       "\n"

> > +	       "Usage: %s OPTIONS\n"

> > +	       "  E.g. %s -i eth0,eth1 -r 1.1.1.0/24,eth0 -r

2.2.2.0/24,eth1\n"
> > +	       " In the above example,\n"

> > +	       " eth0 will send pkts to eth1 and vice versa\n"

> > +	       "\n"

> > +	       "Mandatory OPTIONS:\n"

> > +	       "  -i, --interface eth interfaces (comma-separated, no

spaces)\n"
> > +	       "  -r, --route SubNet,Intf[,NextHopMAC]\n"

> > +	       "	NextHopMAC can be optional\n"

> > +	       "\n"

> > +	       "Optional OPTIONS:\n"

> > +	       "  -s, --style [lpm|hash], ip lookup method\n"

> > +	       "	optional, default as lpm\n"

> > +	       "  -d, --duration Seconds to run and print stats\n"

> > +	       "	optional, default as 0, run forever\n"

> > +	       "  -t, --thread Number of threads to do forwarding\n"

> > +	       "	optional, default as availbe worker cpu count\n"

> > +	       "  -q, --queue  Configure rx queue(s) for port\n"

> > +	       "	optional, format: [(port, queue, thread),...]\n"

> > +	       "	for example: -q '(0, 0, 1),(1,0,2)'\n"

> > +	       "  -h, --help   Display help and exit.\n\n"

> > +	       "\n", NO_PATH(progname), NO_PATH(progname)

> > +	    );

> > +}

> > +

> > +static void parse_cmdline_args(int argc, char *argv[], app_args_t *args)

> > +{

> > +	int opt;

> > +	int long_index;

> > +	char *token, *local;

> > +	size_t len, route_index = 0;

> > +	int i, mem_failure = 0;

> > +

> > +	static struct option longopts[] = {

> > +		{"interface", required_argument, NULL, 'i'},	/* return 'i'

*/
> > +		{"route", required_argument, NULL, 'r'},	/* return 'r'

*/
> > +		{"style", optional_argument, NULL, 's'},	/* return 's'

*/
> > +		{"duration", optional_argument, NULL, 'd'},	/* return 'd'

*/
> > +		{"thread", optional_argument, NULL, 't'},	/* return 't'

*/
> > +		{"queue", optional_argument, NULL, 'q'},	/* return 'q'

*/
> > +		{"help", no_argument, NULL, 'h'},		/* return 'h'

*/
> > +		{NULL, 0, NULL, 0}

> > +	};

> > +

> > +	while (1) {

> > +		opt = getopt_long(argc, argv, "+s:t:d:i:r:q:h",

> > +				  longopts, &long_index);

> > +

> > +		if (opt == -1)

> > +			break;	/* No more options */

> > +

> > +		switch (opt) {

> > +		/* parse ip lookup method */

> > +		case 's':

> > +			if (!strcmp(optarg, "hash"))

> > +				args->hash_mode = 1;

> > +			break;

> > +		/* parse number of worker threads to be run*/

> > +		case 't':

> > +			i = odp_cpu_count();

> > +			args->worker_count = atoi(optarg);

> > +			if (args->worker_count > i) {

> > +				printf("Too many threads,"

> > +				       "truncate to cpu count: %d\n", i);

> > +				args->worker_count = i;

> > +			}

> > +

> > +			break;

> > +

> > +		/* parse seconds to run */

> > +		case 'd':

> > +			args->duration = atoi(optarg);

> > +			break;

> > +

> > +		/* parse packet-io interface names */

> > +		case 'i':

> > +			len = strlen(optarg);

> > +			if (len == 0) {

> > +				print_usage(argv[0]);

> > +				exit(EXIT_FAILURE);

> > +			}

> > +			len += 1;	/* add room for '\0' */

> > +

> > +			local = malloc(len);

> > +			if (!local) {

> > +				print_usage(argv[0]);

> > +				exit(EXIT_FAILURE);

> > +			}

> > +

> > +			/* count the number of tokens separated by ',' */

> > +			strcpy(local, optarg);

> > +			for (token = strtok(local, ","), i = 0;

> > +			     token != NULL;

> > +			     token = strtok(NULL, ","), i++)

> > +				;

> > +

> > +			if (i == 0) {

> > +				print_usage(argv[0]);

> > +				exit(EXIT_FAILURE);

> > +			} else if (i > MAX_NB_PKTIO) {

> > +				printf("too many ports specified, "

> > +				       "truncated to %d", MAX_NB_PKTIO);

> > +			}

> > +			args->if_count = i;

> > +

> > +			/* store the if names (reset names string) */

> > +			strcpy(local, optarg);

> > +			for (token = strtok(local, ","), i = 0;

> > +			     token != NULL; token = strtok(NULL, ","), i++) {

> > +				args->if_names[i] = token;

> > +			}

> > +			break;

> > +

> > +		/*Configure Route in forwarding database*/

> > +		case 'r':

> > +			if (route_index >= MAX_NB_ROUTE) {

> > +				printf("No more routes can be added\n");

> > +				break;

> > +			}

> > +			local = calloc(1, strlen(optarg) + 1);

> > +			if (!local) {

> > +				mem_failure = 1;

> > +				break;

> > +			}

> > +			memcpy(local, optarg, strlen(optarg));

> > +			local[strlen(optarg)] = '\0';

> > +			args->route_str[route_index++] = local;

> > +			break;

> > +

> > +		case 'h':

> > +			print_usage(argv[0]);

> > +			exit(EXIT_SUCCESS);

> > +			break;

> > +

> > +		case 'q':

> > +			parse_config(optarg, args);

> > +			break;

> > +

> > +		default:

> > +			break;

> > +		}

> > +	}

> > +

> > +	/* checking arguments */

> > +	if (args->if_count == 0) {

> > +		printf("\nNo option -i specified.\n");

> > +		goto out;

> > +	}

> > +

> > +	if (args->route_str[0] == NULL) {

> > +		printf("\nNo option -r specified.\n");

> > +		goto out;

> > +	}

> > +

> > +	if (mem_failure == 1) {

> > +		printf("\nAllocate memory failure.\n");

> > +		goto out;

> > +	}

> > +	optind = 1;		/* reset 'extern optind' from the getopt lib

*/
> > +	return;

> > +

> > +out:

> > +	print_usage(argv[0]);

> > +	exit(EXIT_FAILURE);

> > +}

> > +

> > +/* split string into tokens */

> > +int split_string(char *str, int stringlen,

> > +		 char **tokens, int maxtokens, char delim)

> > +{

> > +	int i, tok = 0;

> > +	int tokstart = 1; /* first token is right at start of string */

> > +

> > +	if (str == NULL || tokens == NULL)

> > +		goto einval_error;

> > +

> > +	for (i = 0; i < stringlen; i++) {

> > +		if (str[i] == '\0' || tok >= maxtokens)

> > +			break;

> > +		if (tokstart) {

> > +			tokstart = 0;

> > +			tokens[tok++] = &str[i];

> > +		}

> > +		if (str[i] == delim) {

> > +			str[i] = '\0';

> > +			tokstart = 1;

> > +		}

> > +	}

> > +	return tok;

> > +

> > +einval_error:

> > +	errno = EINVAL;

> > +	return -1;

> > +}

> > +

> > +static int parse_config(char *cfg_str, app_args_t *args)

> > +{

> > +	char s[256];

> > +	const char *p, *p0 = cfg_str;

> > +	char *end;

> > +	enum fieldnames {

> > +		FLD_PORT = 0,

> > +		FLD_QUEUE,

> > +		FLD_LCORE,

> > +		FLD_LAST

> > +	};

> > +	unsigned long int_fld[FLD_LAST];

> > +	char *str_fld[FLD_LAST];

> > +	int i;

> > +	unsigned size;

> > +	int nb_qconfs = 0;

> > +	struct l3fwd_qconf_s *qconf_array = &args->qconf_config[0];

> > +

> > +	p = strchr(p0, '(');

> > +	while (p != NULL) {

> > +		++p;

> > +		p0 = strchr(p, ')');

> > +		if (p0 == NULL)

> > +			return -1;

> > +

> > +		size = p0 - p;

> > +		if (size >= sizeof(s))

> > +			return -1;

> > +

> > +		snprintf(s, sizeof(s), "%.*s", size, p);

> > +		i = split_string(s, sizeof(s), str_fld, FLD_LAST, ',');

> > +		if (i != FLD_LAST)

> > +			return -1;

> > +		for (i = 0; i < FLD_LAST; i++) {

> > +			errno = 0;

> > +			int_fld[i] = strtoul(str_fld[i], &end, 0);

> > +			if (errno != 0 || end == str_fld[i] || int_fld[i] >

255)
> > +				return -1;

> > +		}

> > +		if (nb_qconfs >= MAX_NB_QCONFS) {

> > +			printf("exceeded max number of queue

> params: %hu\n",

> > +			       nb_qconfs);

> > +			return -1;

> > +		}

> > +		qconf_array[nb_qconfs].if_idx = (uint8_t)int_fld[FLD_PORT];

> > +		qconf_array[nb_qconfs].rxq_idx =

> (uint8_t)int_fld[FLD_QUEUE];

> > +		qconf_array[nb_qconfs].core_idx =

> (uint8_t)int_fld[FLD_LCORE];

> > +		++nb_qconfs;

> > +

> > +		p = strchr(p0, '(');

> > +	}

> > +	args->qconf_count = nb_qconfs;

> > +

> > +	return 0;

> > +}

> > +

> > +static void print_info(char *progname, app_args_t *args)

> > +{

> > +	int i;

> > +

> > +	printf("\n"

> > +	       "ODP system info\n"

> > +	       "---------------\n"

> > +	       "ODP API version: %s\n"

> > +	       "ODP impl name:	 %s\n"

> > +	       "CPU model:       %s\n"

> > +	       "CPU freq (hz):   %" PRIu64 "\n"

> > +	       "Cache line size: %i\n"

> > +	       "CPU count:       %i\n"

> > +	       "\n",

> > +	       odp_version_api_str(), odp_version_impl_name(),

> > +	       odp_cpu_model_str(), odp_cpu_hz_max(),

> > +	       odp_sys_cache_line_size(), odp_cpu_count());

> > +

> > +	printf("Running ODP appl: \"%s\"\n"

> > +	       "-----------------\n"

> > +	       "IP Lookup:	 %s\n"

> > +	       "IF Count:        %i\n"

> > +	       "Using IFs:      ",

> > +	       progname,

> > +	       args->hash_mode ? "hash" : "lpm",

> > +	       args->if_count);

> > +

> > +	for (i = 0; i < args->if_count; ++i)

> > +		printf(" %s", args->if_names[i]);

> > +

> > +	printf("\n\n");

> > +	fflush(NULL);

> > +}

> > +

> > +/**

> > + * Setup rx and tx queues, distribute them among threads.

> > + * Try to have one tx queue for each rx queue, if not vailable,

> > + * shared tx queue is used.

> > + *

> > + * If no q argument, the queues are distribute among threads as default.

> > + * The thread take one rx queue of a port one time as round-robin order.

> > + */

> > +static void setup_worker_qconf(app_args_t *args)

> > +{

> > +	int nb_worker, if_count;

> > +	int i, j, rxq_idx, txq_idx;

> > +	struct thread_arg_s *arg;

> > +	struct l3fwd_pktio_s *port;

> > +	uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_QUEUE];

> > +

> > +	nb_worker = args->worker_count;

> > +	if_count = args->if_count;

> > +

> > +	/* distribute queues among threads */

> > +	if (!args->qconf_count) {

> > +		if (nb_worker > if_count) {

> > +			for (i = 0; i < nb_worker; i++) {

> > +				arg = &global.worker_args[i];

> > +				arg->thr_idx = i;

> > +				j = i % if_count;

> > +				port = &global.l3fwd_pktios[j];

> > +				if (port->rxq_idx < port->nb_rxq) {

> > +					rxq_idx = port->rxq_idx;

> > +					arg->pktio[j].rxq[rxq_idx] = rxq_idx;

> > +					port->rxq_idx++;

> > +					txq_idx = port->txq_idx;

> > +					arg->pktio[j].txq[txq_idx] = txq_idx;

> > +					port->txq_idx++;

> > +					arg->pktio[j].used = 1;

> > +				}

> > +			}

> > +		} else {

> > +			for (i = 0; i < if_count; i++) {

> > +				j = i % nb_worker;

> > +				arg = &global.worker_args[j];

> > +				arg->thr_idx = j;

> > +				port = &global.l3fwd_pktios[i];

> > +				if (port->rxq_idx < port->nb_rxq) {

> > +					rxq_idx = port->rxq_idx;

> > +					arg->pktio[i].rxq[rxq_idx] = rxq_idx;

> > +					port->rxq_idx++;

> > +					txq_idx = port->txq_idx;

> > +					arg->pktio[i].txq[txq_idx] = txq_idx;

> > +					port->txq_idx++;

> > +					arg->pktio[i].used = 1;

> > +				}

> > +			}

> > +		}

> > +	}

> > +

> > +	/* specified q argument, distribute queues among threads as it says */

> > +	memset(queue_mask, 0, sizeof(queue_mask));

> > +	for (i = 0; i < args->qconf_count; i++) {

> > +		struct l3fwd_qconf_s *q;

> > +

> > +		q = &args->qconf_config[i];

> > +		if (q->core_idx >= nb_worker || q->if_idx >= if_count)

> > +			LOG_ABORT("Error queue (%d, %d, %d), max port: "

> > +				  "%d, max core: %d\n", q->if_idx, q->rxq_idx,

> > +				  q->core_idx, args->if_count - 1,

> > +				  args->worker_count - 1);

> > +

> > +		/* check if one queue is configured twice or more */

> > +		if (queue_mask[q->if_idx][q->rxq_idx])

> > +			LOG_ABORT("Error queue (%d, %d, %d), reconfig

> queue\n",

> > +				  q->if_idx, q->rxq_idx, q->core_idx);

> > +		queue_mask[q->if_idx][q->rxq_idx] = 1;

> > +

> > +		port = &global.l3fwd_pktios[q->if_idx];

> > +		if (port->rxq_idx < q->rxq_idx)

> > +			LOG_ABORT("Error queue (%d, %d, %d), queue should

> be"

> > +				  " in sequence and start from 0, queue %d\n",

> > +				  q->if_idx, q->rxq_idx, q->core_idx,

> > +				  q->rxq_idx);

> > +

> > +		if (q->rxq_idx > port->nb_rxq) {

> > +			LOG_ABORT("Error queue (%d, %d, %d), max

> queue %d\n",

> > +				  q->if_idx, q->rxq_idx, q->core_idx,

> > +				  port->nb_rxq - 1);

> > +		}

> > +		port->rxq_idx = q->rxq_idx + 1;

> > +		port->txq_idx = q->rxq_idx + 1;

> > +

> > +		/* put the queue into worker_args */

> > +		arg = &global.worker_args[q->core_idx];

> > +		arg->pktio[q->if_idx].rxq[q->rxq_idx] = q->rxq_idx;

> > +		arg->pktio[q->if_idx].txq[q->rxq_idx] = q->rxq_idx;

> > +		arg->pktio[q->if_idx].used = 1;

> > +		arg->thr_idx = q->core_idx;

> > +	}

> > +

> > +	/* config and initialize rx and tx queues. */

> > +	for (i = 0; i < if_count; i++) {

> > +		odp_pktin_queue_param_t in_queue_param;

> > +		odp_pktout_queue_param_t out_queue_param;

> > +		struct odp_pktin_queue_t *inq;

> > +		struct odp_pktout_queue_t *outq;

> > +		const char *name;

> > +		int rc, nb_rxq, nb_txq;

> > +

> > +		port = &global.l3fwd_pktios[i];

> > +		name = args->if_names[i];

> > +		odp_pktin_queue_param_init(&in_queue_param);

> > +		odp_pktout_queue_param_init(&out_queue_param);

> > +

> > +		in_queue_param.op_mode = ODP_PKTIO_OP_MT;

> > +		out_queue_param.op_mode = ODP_PKTIO_OP_MT;

> > +

> > +		in_queue_param.num_queues = port->rxq_idx;

> > +		if (port->rxq_idx > port->nb_rxq) {

> > +			in_queue_param.num_queues = port->nb_rxq;

> > +			in_queue_param.op_mode =

> ODP_PKTIO_OP_MT_UNSAFE;

> > +		}

> > +

> > +		/* enable flow hashing for multi-input queues */

> > +		if (in_queue_param.num_queues > 1) {

> > +			in_queue_param.hash_enable = 1;

> > +			in_queue_param.hash_proto.proto.ipv4_tcp = 1;

> > +			in_queue_param.hash_proto.proto.ipv4_udp = 1;

> > +		}

> > +

> > +		if (odp_pktin_queue_config(port->pktio, &in_queue_param))

> > +			LOG_ABORT("Fail to config input queue for %s\n",

> name);

> > +

> > +		out_queue_param.num_queues = port->txq_idx;

> > +		if (port->txq_idx > port->nb_txq) {

> > +			out_queue_param.num_queues = port->nb_txq;

> > +			out_queue_param.op_mode =

> ODP_PKTIO_OP_MT_UNSAFE;

> > +		}

> > +		if (odp_pktout_queue_config(port->pktio,

> &out_queue_param))

> > +			LOG_ABORT("Fail to config output queue for %s\n",

> name);

> > +

> > +		inq = port->ifin;

> > +		nb_rxq = in_queue_param.num_queues;

> > +		if (odp_pktin_queue(port->pktio, inq, nb_rxq) != nb_rxq)

> > +			LOG_ABORT("Fail to set pktin queue for %s\n", name);

> > +

> > +		if (port->rxq_idx > port->nb_rxq) {

> > +			for (rc = port->nb_rxq; rc < port->rxq_idx; rc++)

> > +				inq[rc] = inq[rc % port->nb_rxq];

> > +		}

> > +

> > +		outq = port->ifout;

> > +		nb_txq = out_queue_param.num_queues;

> > +		if (odp_pktout_queue(port->pktio, outq, nb_txq) != nb_txq)

> > +			LOG_ABORT("Fail to set pktout queue for %s\n",

> name);

> > +

> > +		if (port->txq_idx > port->nb_txq) {

> > +			for (rc = port->nb_txq; rc < port->txq_idx; rc++)

> > +				outq[rc] = outq[rc % port->nb_txq];

> > +		}

> > +	}

> > +}

> > +

> > +static void print_qconf_table(app_args_t *args)

> > +{

> > +	int i, j, k, qid;

> > +	char buf[32];

> > +	struct thread_arg_s *thr_arg;

> > +

> > +	printf("Rx queue table\n"

> > +	       "-----------------\n"

> > +	       "%-16s%-16s%-16s\n",

> > +	       "port/id", "queue", "thread");

> > +

> > +	for (i = 0; i < args->worker_count; i++) {

> > +		thr_arg = &global.worker_args[i];

> > +		for (j = 0; j < args->if_count; j++) {

> > +			if (!thr_arg->pktio[j].used)

> > +				continue;

> > +

> > +			sprintf(buf, "%s/%d", args->if_names[j], j);

> > +			for (k = 0; k < MAX_NB_QUEUE; k++) {

> > +				qid = thr_arg->pktio[j].rxq[k];

> > +				if (qid != INVALID_ID)

> > +					printf("%-16s%-16d%-16d\n", buf, qid,

> > +					       thr_arg->thr_idx);

> > +			}

> > +		}

> > +	}

> > +	printf("\n");

> > +	fflush(NULL);

> > +}

> > +

> > +/**

> > + *  Print statistics

> > + *

> > + * @param num_workers Number of worker threads

> > + * @param duration Number of seconds to loop in

> > + * @param timeout Number of seconds for stats calculation

> > + *

> > + */

> > +static int print_speed_stats(int num_workers, int duration, int timeout)

> > +{

> > +	uint64_t pkts = 0;

> > +	uint64_t pkts_prev = 0;

> > +	uint64_t pps;

> > +	uint64_t rx_drops, tx_drops;

> > +	uint64_t maximum_pps = 0;

> > +	int i;

> > +	int elapsed = 0;

> > +	int stats_enabled = 1;

> > +	int loop_forever = (duration == 0);

> > +

> > +	if (timeout <= 0) {

> > +		stats_enabled = 0;

> > +		timeout = 1;

> > +	}

> > +	/* Wait for all threads to be ready*/

> > +	odp_barrier_wait(&barrier);

> > +

> > +	do {

> > +		pkts = 0;

> > +		rx_drops = 0;

> > +		tx_drops = 0;

> > +		sleep(timeout);

> > +

> > +		for (i = 0; i < num_workers; i++) {

> > +			pkts += global.worker_args[i].packets;

> > +			rx_drops += global.worker_args[i].rx_drops;

> > +			tx_drops += global.worker_args[i].tx_drops;

> > +		}

> > +		if (stats_enabled) {

> > +			pps = (pkts - pkts_prev) / timeout;

> > +			if (pps > maximum_pps)

> > +				maximum_pps = pps;

> > +			printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,

> > +			       maximum_pps);

> > +

> > +			printf(" %" PRIu64 " rx drops, %" PRIu64 " tx

drops\n",
> > +			       rx_drops, tx_drops);

> > +

> > +			pkts_prev = pkts;

> > +		}

> > +		elapsed += timeout;

> > +	} while (loop_forever || (elapsed < duration));

> > +

> > +	if (stats_enabled)

> > +		printf("TEST RESULT: %" PRIu64 " maximum packets per

> second.\n",

> > +		       maximum_pps);

> > +

> > +	return pkts > 100 ? 0 : -1;

> > +}

> > diff --git a/example/l3fwd/odp_l3fwd_db.c

> b/example/l3fwd/odp_l3fwd_db.c

> > new file mode 100644

> > index 0000000..93e32f0

> > --- /dev/null

> > +++ b/example/l3fwd/odp_l3fwd_db.c

> > @@ -0,0 +1,408 @@

> > +/* Copyright (c) 2016, Linaro Limited

> > + * All rights reserved.

> > + *

> > + * SPDX-License-Identifier:     BSD-3-Clause

> > + */

> > +

> > +#ifndef _GNU_SOURCE

> > +#define _GNU_SOURCE

> > +#endif

> > +

> > +#include <stdlib.h>

> > +#include <string.h>

> > +

> > +#include <example_debug.h>

> > +#include <odp_api.h>

> > +#include <odp_l3fwd_db.h>

> > +

> > +/** Jenkins hash support.

> > +  *

> > +  * Copyright (C) 2006 Bob Jenkins (bob_jenkins@burtleburtle.net)

> > +  *

> > +  * http://burtleburtle.net/bob/hash/

> > +  *

> > +  * These are the credits from Bob's sources:

> > +  *

> > +  * lookup3.c, by Bob Jenkins, May 2006, Public Domain.

> > +  *

> > +  * These are functions for producing 32-bit hashes for hash table

lookup.
> > +  * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and

final()
> > +  * are externally useful functions.  Routines to test the hash are

included
> > +  * if SELF_TEST is defined.  You can use this free for any purpose.

It's in
> > +  * the public domain.  It has no warranty.

> > +  *

> > +  * $FreeBSD$

> > +  */

> > +#define JHASH_GOLDEN_RATIO	0x9e3779b9

> > +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))

> > +#define FWD_BJ3_MIX(a, b, c) \

> > +{ \

> > +	a -= c; a ^= rot(c, 4); c += b; \

> > +	b -= a; b ^= rot(a, 6); a += c; \

> > +	c -= b; c ^= rot(b, 8); b += a; \

> > +	a -= c; a ^= rot(c, 16); c += b; \

> > +	b -= a; b ^= rot(a, 19); a += c; \

> > +	c -= b; c ^= rot(b, 4); b += a; \

> > +}

> > +

> > +/**

> > + * Compute hash value from a flow

> > + */

> > +static inline

> > +uint64_t l3fwd_calc_hash(ipv4_tuple5_t *key)

> > +{

> > +	uint64_t l4_ports = 0;

> > +	uint32_t dst_ip, src_ip;

> > +

> > +	src_ip = key->src_ip;

> > +	dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO;

> > +	FWD_BJ3_MIX(src_ip, dst_ip, l4_ports);

> > +

> > +	return l4_ports;

> > +}

> > +

> > +/**

> > + * Parse text string representing an IPv4 address or subnet

> > + *

> > + * String is of the format "XXX.XXX.XXX.XXX(/W)" where

> > + * "XXX" is decimal value and "/W" is optional subnet length

> > + *

> > + * @param ipaddress  Pointer to IP address/subnet string to convert

> > + * @param addr       Pointer to return IPv4 address, host endianness

> > + * @param depth      Pointer to subnet bit width

> > + * @return 0 if successful else -1

> > + */

> > +static inline

> > +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *depth)

> > +{

> > +	int b[4];

> > +	int qualifier = 32;

> > +	int converted;

> > +	uint32_t addr_le;

> > +

> > +	if (strchr(ipaddress, '/')) {

> > +		converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",

> > +				   &b[3], &b[2], &b[1], &b[0],

> > +				   &qualifier);

> > +		if (5 != converted)

> > +			return -1;

> > +	} else {

> > +		converted = sscanf(ipaddress, "%d.%d.%d.%d",

> > +				   &b[3], &b[2], &b[1], &b[0]);

> > +		if (4 != converted)

> > +			return -1;

> > +	}

> > +

> > +	if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))

> > +		return -1;

> > +	if (!qualifier || (qualifier > 32))

> > +		return -1;

> > +

> > +	addr_le = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;

> > +	*addr = odp_le_to_cpu_32(addr_le);

> > +	*depth = qualifier;

> > +

> > +	return 0;

> > +}

> > +

> > +/**

> > + * Generate text string representing IPv4 range/subnet, output

> > + * in "XXX.XXX.XXX.XXX/W" format

> > + *

> > + * @param b     Pointer to buffer to store string

> > + * @param range Pointer to IPv4 address range

> > + *

> > + * @return Pointer to supplied buffer

> > + */

> > +static inline

> > +char *ipv4_subnet_str(char *b, ip_addr_range_t *range)

> > +{

> > +	sprintf(b, "%d.%d.%d.%d/%d",

> > +		0xFF & ((range->addr) >> 24),

> > +		0xFF & ((range->addr) >> 16),

> > +		0xFF & ((range->addr) >>  8),

> > +		0xFF & ((range->addr) >>  0),

> > +		range->depth);

> > +	return b;

> > +}

> > +

> > +/**

> > + * Generate text string representing MAC address

> > + *

> > + * @param b     Pointer to buffer to store string

> > + * @param mac   Pointer to MAC address

> > + *

> > + * @return Pointer to supplied buffer

> > + */

> > +static inline

> > +char *mac_addr_str(char *b, odph_ethaddr_t *mac)

> > +{

> > +	uint8_t *byte;

> > +

> > +	byte = mac->addr;

> > +	sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",

> > +		byte[0], byte[1], byte[2], byte[3], byte[4], byte[5]);

> > +	return b;

> > +}

> > +

> > +/**

> > + * Flow cache table entry

> > + */

> > +typedef struct flow_entry_s {

> > +	ipv4_tuple5_t key;		/**< match key */

> > +	struct flow_entry_s *next;      /**< next entry on the list */

> > +	fwd_db_entry_t *fwd_entry;	/**< entry info in db */

> > +} flow_entry_t;

> > +

> > +/**

> > + * Flow cache table bucket

> > + */

> > +typedef struct flow_bucket_s {

> > +	odp_spinlock_t		lock;	/**< Bucket lock*/

> > +	flow_entry_t		*next;	/**< Pointer to first flow entry in

> bucket*/

> > +} flow_bucket_t;

> > +

> > +/**

> > + * Flow hash table, fast lookup cache

> > + */

> > +typedef struct flow_table_s {

> > +	flow_bucket_t *bucket;

> > +	uint32_t count;

> > +} flow_table_t;

> > +

> > +static flow_table_t fwd_lookup_cache;

> > +

> > +void init_fwd_hash_cache(void)

> > +{

> > +	odp_shm_t		hash_shm;

> > +	flow_bucket_t		*bucket;

> > +	uint32_t		bucket_count;

> > +	uint32_t		i;

> > +

> > +	bucket_count = FWD_DEF_BUCKET_COUNT;

> > +

> > +	/*Reserve memory for Routing hash table*/

> > +	hash_shm = odp_shm_reserve("route_table",

> > +				   sizeof(flow_bucket_t) * bucket_count,

> > +				   ODP_CACHE_LINE_SIZE, 0);

> > +

> > +	bucket = odp_shm_addr(hash_shm);

> > +	if (!bucket) {

> > +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> > +		exit(-1);

> > +	}

> > +

> > +	fwd_lookup_cache.bucket = bucket;

> > +	fwd_lookup_cache.count = bucket_count;

> > +

> > +	/*Initialize Locks*/

> > +	for (i = 0; i < bucket_count; i++) {

> > +		bucket = &fwd_lookup_cache.bucket[i];

> > +		odp_spinlock_init(&bucket->lock);

> > +		bucket->next = NULL;

> > +	}

> > +}

> > +

> > +static inline

> > +int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow)

> > +{

> > +	if (key->src_ip == flow->key.src_ip &&

> > +	    key->dst_ip == flow->key.dst_ip &&

> > +	    key->src_port == flow->key.src_port &&

> > +	    key->dst_port == flow->key.dst_port &&

> > +	    key->proto == flow->key.proto)

> > +		return 1;

> > +

> > +	return 0;

> > +}

> > +

> > +static inline

> > +flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t

> *bucket)

> > +{

> > +	flow_entry_t *rst;

> > +

> > +	for (rst = bucket->next; rst != NULL; rst = rst->next) {

> > +		if (match_key_flow(key, rst))

> > +			break;

> > +	}

> > +

> > +	return rst;

> > +}

> > +

> > +static inline

> > +flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key,

> > +			       flow_bucket_t *bucket,

> > +			       fwd_db_entry_t *entry)

> > +{

> > +	flow_entry_t *flow;

> > +

> > +	flow = lookup_fwd_cache(key, bucket);

> > +	if (flow)

> > +		return flow;

> > +

> > +	flow = malloc(sizeof(flow_entry_t));

> > +	flow->key = *key;

> > +	flow->fwd_entry = entry;

> > +

> > +	odp_spinlock_lock(&bucket->lock);

> > +	if (!bucket->next) {

> > +		bucket->next = flow;

> > +	} else {

> > +		flow->next = bucket->next;

> > +		bucket->next = flow;

> > +	}

> > +	odp_spinlock_unlock(&bucket->lock);

> > +

> > +	return flow;

> > +}

> > +

> > +/** Global pointer to fwd db */

> > +fwd_db_t *fwd_db;

> > +

> > +void init_fwd_db(void)

> > +{

> > +	odp_shm_t shm;

> > +

> > +	shm = odp_shm_reserve("shm_fwd_db",

> > +			      sizeof(fwd_db_t),

> > +			      ODP_CACHE_LINE_SIZE,

> > +			      0);

> > +

> > +	fwd_db = odp_shm_addr(shm);

> > +

> > +	if (fwd_db == NULL) {

> > +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> > +		exit(EXIT_FAILURE);

> > +	}

> > +	memset(fwd_db, 0, sizeof(*fwd_db));

> > +}

> > +

> > +int create_fwd_db_entry(char *input, char **oif)

> > +{

> > +	int pos = 0;

> > +	char *local;

> > +	char *str;

> > +	char *save;

> > +	char *token;

> > +	fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];

> > +

> > +	/* Verify we haven't run out of space */

> > +	if (MAX_DB <= fwd_db->index)

> > +		return -1;

> > +

> > +	/* Make a local copy */

> > +	local = malloc(strlen(input) + 1);

> > +	if (NULL == local)

> > +		return -1;

> > +	strcpy(local, input);

> > +

> > +	/* Setup for using "strtok_r" to search input string */

> > +	str = local;

> > +	save = NULL;

> > +

> > +	/* Parse tokens separated by ':' */

> > +	while (NULL != (token = strtok_r(str, ",", &save))) {

> > +		str = NULL;  /* reset str for subsequent strtok_r calls */

> > +

> > +		/* Parse token based on its position */

> > +		switch (pos) {

> > +		case 0:

> > +			parse_ipv4_string(token,

> > +					  &entry->subnet.addr,

> > +					  &entry->subnet.depth);

> > +			break;

> > +		case 1:

> > +			strncpy(entry->oif, token, OIF_LEN - 1);

> > +			entry->oif[OIF_LEN - 1] = 0;

> > +			break;

> > +		case 2:

> > +			odph_eth_addr_parse(&entry->dst_mac, token);

> > +			*oif = entry->oif;

> > +			break;

> > +

> > +		default:

> > +			printf("ERROR: extra token \"%s\" at position %d\n",

> > +			       token, pos);

> > +			break;

> > +		}

> > +

> > +		/* Advance to next position */

> > +		pos++;

> > +	}

> > +

> > +	/* Add route to the list */

> > +	fwd_db->index++;

> > +	entry->next = fwd_db->list;

> > +	fwd_db->list = entry;

> > +

> > +	free(local);

> > +	return 0;

> > +}

> > +

> > +void resolve_fwd_db(char *intf, int portid, uint8_t *mac)

> > +{

> > +	fwd_db_entry_t *entry;

> > +

> > +	/* Walk the list and attempt to set output and MAC */

> > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> > +		if (strcmp(intf, entry->oif))

> > +			continue;

> > +

> > +		entry->oif_id = portid;

> > +		memcpy(entry->src_mac.addr, mac, ODPH_ETHADDR_LEN);

> > +	}

> > +}

> > +

> > +void dump_fwd_db_entry(fwd_db_entry_t *entry)

> > +{

> > +	char subnet_str[MAX_STRING];

> > +	char mac_str[MAX_STRING];

> > +

> > +	mac_addr_str(mac_str, &entry->dst_mac);

> > +	printf("%-16s%-16s%-16s\n",

> > +	       ipv4_subnet_str(subnet_str, &entry->subnet),

> > +	       entry->oif, mac_str);

> > +}

> > +

> > +void dump_fwd_db(void)

> > +{

> > +	fwd_db_entry_t *entry;

> > +

> > +	printf("Routing table\n"

> > +	       "-----------------\n"

> > +	       "%-16s%-16s%-16s\n",

> > +	       "subnet", "next_hop", "dest_mac");

> > +

> > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> > +		dump_fwd_db_entry(entry);

> > +

> > +	printf("\n");

> > +}

> > +

> > +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key)

> > +{

> > +	fwd_db_entry_t *entry;

> > +	flow_entry_t *flow;

> > +	flow_bucket_t *bucket;

> > +	uint64_t hash;

> > +

> > +	/* first find in cache */

> > +	hash = l3fwd_calc_hash(key);

> > +	hash &= fwd_lookup_cache.count - 1;

> > +	bucket = &fwd_lookup_cache.bucket[hash];

> > +	flow = lookup_fwd_cache(key, bucket);

> > +	if (flow)

> > +		return flow->fwd_entry;

> > +

> > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> > +		uint32_t mask;

> > +

> > +		mask = ((1u << entry->subnet.depth) - 1) <<

> > +			(32 - entry->subnet.depth);

> > +

> > +		if (entry->subnet.addr == (key->dst_ip & mask))

> > +			break;

> > +	}

> > +

> > +	return entry;

> > +}

> > diff --git a/example/l3fwd/odp_l3fwd_db.h

> b/example/l3fwd/odp_l3fwd_db.h

> > new file mode 100644

> > index 0000000..840946a

> > --- /dev/null

> > +++ b/example/l3fwd/odp_l3fwd_db.h

> > @@ -0,0 +1,130 @@

> > +/* Copyright (c) 2016, Linaro Limited

> > + * All rights reserved.

> > + *

> > + * SPDX-License-Identifier:     BSD-3-Clause

> > + */

> > +

> > +#ifndef ODP_L3FWD_DB_H_

> > +#define ODP_L3FWD_DB_H_

> > +

> > +#ifdef __cplusplus

> > +extern "C" {

> > +#endif

> > +

> > +#include <odp_api.h>

> > +#include <odp/helper/eth.h>

> > +

> > +#define OIF_LEN 32

> > +#define MAX_DB  32

> > +#define MAX_STRING  32

> > +

> > +/**

> > + * Default number of flows

> > + */

> > +#define FWD_DEF_FLOW_COUNT		100000

> > +

> > +/**

> > + * Default Hash bucket number

> > + */

> > +#define FWD_DEF_BUCKET_COUNT	(FWD_DEF_FLOW_COUNT / 8)

> > +

> > +/**

> > + * IP address range (subnet)

> > + */

> > +typedef struct ip_addr_range_s {

> > +	uint32_t  addr;     /**< IP address, host endianness */

> > +	uint32_t  depth;    /**< subnet bit width */

> > +} ip_addr_range_t;

> > +

> > +/**

> > + * TCP/UDP flow

> > + */

> > +typedef struct ipv4_tuple5_s {

> > +	uint32_t src_ip;

> > +	uint32_t dst_ip;

> > +	uint16_t src_port;

> > +	uint16_t dst_port;

> > +	uint8_t  proto;

> > +} ipv4_tuple5_t;

> > +

> > +/**

> > + * Forwarding data base entry

> > + */

> > +typedef struct fwd_db_entry_s {

> > +	struct fwd_db_entry_s *next;          /**< Next entry on list */

> > +	char                    oif[OIF_LEN]; /**< Output interface name */

> > +	int			oif_id;	      /**< Output interface idx */

> > +	odph_ethaddr_t		src_mac;      /**< Output source MAC */

> > +	odph_ethaddr_t		dst_mac;      /**< Output destination

> MAC */

> > +	ip_addr_range_t		subnet;       /**< Subnet for this router

> */

> > +} fwd_db_entry_t;

> > +

> > +/**

> > + * Forwarding data base

> > + */

> > +typedef struct fwd_db_s {

> > +	uint32_t          index;          /**< Next available entry */

> > +	fwd_db_entry_t   *list;           /**< List of active routes */

> > +	fwd_db_entry_t    array[MAX_DB];  /**< Entry storage */

> > +} fwd_db_t;

> > +

> > +/** Global pointer to fwd db */

> > +extern fwd_db_t *fwd_db;

> > +

> > +/**

> > + * Initialize FWD DB

> > + */

> > +void init_fwd_db(void);

> > +

> > +/**

> > + * Initialize forward lookup cache based on hash

> > + */

> > +void init_fwd_hash_cache(void);

> > +

> > +/**

> > + * Create a forwarding database entry

> > + *

> > + * String is of the format "SubNet,Intf,NextHopMAC"

> > + *

> > + * @param input  Pointer to string describing route

> > + * @param oif  Pointer to out interface name, as a return value

> > + *

> > + * @return 0 if successful else -1

> > + */

> > +int create_fwd_db_entry(char *input, char **oif);

> > +

> > +/**

> > + * Scan FWD DB entries and resolve output queue and source MAC address

> > + *

> > + * @param intf   Interface name string

> > + * @param portid Output queue for packet transmit

> > + * @param mac    MAC address of this interface

> > + */

> > +void resolve_fwd_db(char *intf, int portid, uint8_t *mac);

> > +

> > +/**

> > + * Display one forwarding database entry

> > + *

> > + * @param entry  Pointer to entry to display

> > + */

> > +void dump_fwd_db_entry(fwd_db_entry_t *entry);

> > +

> > +/**

> > + * Display the forwarding database

> > + */

> > +void dump_fwd_db(void);

> > +

> > +/**

> > + * Find a matching forwarding database entry

> > + *

> > + * @param key  ipv4 tuple

> > + *

> > + * @return pointer to forwarding DB entry else NULL

> > + */

> > +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key);

> > +

> > +#ifdef __cplusplus

> > +}

> > +#endif

> > +

> > +#endif

> > diff --git a/example/l3fwd/odp_l3fwd_lpm.c

> b/example/l3fwd/odp_l3fwd_lpm.c

> > new file mode 100644

> > index 0000000..1b3bfcf

> > --- /dev/null

> > +++ b/example/l3fwd/odp_l3fwd_lpm.c

> > @@ -0,0 +1,224 @@

> > +/* Copyright (c) 2016, Linaro Limited

> > + * All rights reserved.

> > + *

> > + * SPDX-License-Identifier:     BSD-3-Clause

> > + */

> > +#ifndef _GNU_SOURCE

> > +#define _GNU_SOURCE

> > +#endif

> > +

> > +#include <stdio.h>

> > +#include <stdlib.h>

> > +

> > +#include <example_debug.h>

> > +#include <odp_api.h>

> > +

> > +#include <odp_l3fwd_lpm.h>

> > +

> > +/**

> > + * This is a simple implementation of lpm based on patricia tree.

> > + *

> > + * Tradeoff exists between memory consumption and lookup time.

> > + * Currently it prefers 5 levels: {16, 4, 4, 4, 4}, could be 3

> > + * levels: {16, 8, 8} by defining FIB_NEXT_STRIDE as 8. Other

> > + * levels are also possible.

> > + *

> > + * the ip here is host endian, when doing init or lookup, the

> > + * caller should do endianness conversion if needed.

> > + */

> > +

> > +#define FIB_IP_WIDTH 32

> > +#define FIB_FIRST_STRIDE 16

> > +#define FIB_NEXT_STRIDE 4

> > +#define FIB_NEXT_SIZE (1 << FIB_NEXT_STRIDE)

> > +#define FIB_SUB_COUNT 16384

> > +#define DEPTH_TO_MASK(depth) ((1 << (depth)) - 1)

> > +

> > +typedef struct fib_node_s {

> > +	union {

> > +		uint32_t next_hop;

> > +		struct fib_node_s *next; /* next level table */

> > +	};

> > +	uint8_t valid	:1; /* 1, this node has a valid next hop */

> > +	uint8_t end	:1; /* 0, next points to the extended table */

> > +	uint8_t depth	:6; /* bit length of subnet mask */

> > +} fib_node_t;

> > +

> > +typedef struct fib_sub_tbl_s {

> > +	fib_node_t *fib_nodes;

> > +	uint32_t fib_count;

> > +	uint32_t fib_idx;

> > +} fib_sub_tbl_t;

> > +

> > +static fib_node_t fib_rt_tbl[1 << FIB_FIRST_STRIDE];

> > +static fib_sub_tbl_t fib_lpm_cache;

> > +

> > +static inline fib_node_t *fib_alloc_sub(void)

> > +{

> > +	fib_node_t *sub_tbl = NULL;

> > +	uint32_t i, nb_entry;

> > +

> > +	/* extend to next level */

> > +	if (fib_lpm_cache.fib_idx < fib_lpm_cache.fib_count) {

> > +		nb_entry = (fib_lpm_cache.fib_idx + 1) * FIB_NEXT_SIZE;

> > +		sub_tbl = &fib_lpm_cache.fib_nodes[nb_entry];

> > +		fib_lpm_cache.fib_idx++;

> > +		for (i = 0; i < nb_entry; i++) {

> > +			sub_tbl[i].valid = 0;

> > +			sub_tbl[i].end = 1;

> > +		}

> > +	}

> > +

> > +	return sub_tbl;

> > +}

> > +

> > +static void fib_update_node(fib_node_t *fe, int port, int depth)

> > +{

> > +	fib_node_t *p;

> > +	int i;

> > +

> > +	if (fe->end) {

> > +		if (!fe->valid) {

> > +			fe->depth = depth;

> > +			fe->next_hop = port;

> > +			fe->valid = 1;

> > +		} else if (fe->depth <= depth) {

> > +			fe->next_hop = port;

> > +			fe->depth = depth;

> > +		}

> > +

> > +		return;

> > +	}

> > +

> > +	for (i = 0; i < FIB_NEXT_SIZE; i++) {

> > +		p = &fe->next[i];

> > +		if (p->end)

> > +			fib_update_node(p, port, depth);

> > +	}

> > +}

> > +

> > +static void fib_insert_node(fib_node_t *fe, uint32_t ip, uint32_t

next_hop,
> > +			    int ip_width, int eat_bits, int depth)

> > +{

> > +	int i;

> > +	uint32_t idx, port;

> > +	fib_node_t *p;

> > +

> > +	if (fe->end) {

> > +		port = fe->next_hop;

> > +		p = fib_alloc_sub();

> > +		if (!p)

> > +			return;

> > +

> > +		fe->next = p;

> > +		fe->end = 0;

> > +		if (fe->valid) {

> > +			for (i = 0; i < FIB_NEXT_SIZE; i++) {

> > +				p = &fe->next[i];

> > +				p->next_hop = port;

> > +				p->depth = fe->depth;

> > +			}

> > +		}

> > +	}

> > +	if (depth - eat_bits <= FIB_NEXT_STRIDE) {

> > +		ip_width -= depth - eat_bits;

> > +		idx = ip >> ip_width;

> > +		ip &= DEPTH_TO_MASK(ip_width);

> > +		p = &fe->next[idx];

> > +		fib_update_node(p, next_hop, depth);

> > +	} else {

> > +		ip_width -= FIB_NEXT_STRIDE;

> > +		idx = ip >> ip_width;

> > +		p = &fe->next[idx];

> > +		ip &= DEPTH_TO_MASK(ip_width);

> > +		eat_bits += FIB_NEXT_STRIDE;

> > +		fib_insert_node(p, ip, next_hop, ip_width, eat_bits, depth);

> > +	}

> > +}

> > +

> > +void fib_tbl_init(void)

> > +{

> > +	int i;

> > +	fib_node_t *fe;

> > +	uint32_t size;

> > +	odp_shm_t lpm_shm;

> > +

> > +	for (i = 0; i < (1 << FIB_FIRST_STRIDE); i++) {

> > +		fe = &fib_rt_tbl[i];

> > +		fe->valid = 0;

> > +		fe->end = 1;

> > +		fe->depth = 0;

> > +		fe->next_hop = 0;

> > +	}

> > +

> > +	size = FIB_NEXT_SIZE * FIB_SUB_COUNT;

> > +	/*Reserve memory for Routing hash table*/

> > +	lpm_shm = odp_shm_reserve("fib_lpm_sub", size,

> ODP_CACHE_LINE_SIZE, 0);

> > +	fe = odp_shm_addr(lpm_shm);

> > +	if (!fe) {

> > +		EXAMPLE_ERR("Error: shared mem alloc failed for lpm

> cache.\n");

> > +		exit(-1);

> > +	}

> > +

> > +	fib_lpm_cache.fib_nodes = fe;

> > +	fib_lpm_cache.fib_count = FIB_SUB_COUNT;

> > +	fib_lpm_cache.fib_idx = 0;

> > +}

> > +

> > +void fib_tbl_insert(uint32_t ip, int port, int depth)

> > +{

> > +	fib_node_t *fe, *p;

> > +	uint32_t idx;

> > +	int i, j;

> > +	int nb_bits;

> > +

> > +	nb_bits = FIB_FIRST_STRIDE;

> > +	idx = ip >> nb_bits;

> > +	fe = &fib_rt_tbl[idx];

> > +	if (depth <= nb_bits) {

> > +		if (fe->end) {

> > +			fe->next_hop = port;

> > +			fe->depth = depth;

> > +			fe->valid = 1;

> > +			return;

> > +		}

> > +

> > +		for (i = 0; i < FIB_NEXT_SIZE; i++) {

> > +			p = &fe->next[i];

> > +			if (p->end)

> > +				fib_update_node(p, port, depth);

> > +			else

> > +				for (j = 0; j < FIB_NEXT_SIZE; j++)

> > +					fib_update_node(&p->next[j], port,

> > +							depth);

> > +		}

> > +

> > +		return;

> > +	}

> > +

> > +	/* need to check sub table */

> > +	ip &= DEPTH_TO_MASK(FIB_IP_WIDTH - nb_bits);

> > +	fib_insert_node(fe, ip, port, FIB_IP_WIDTH - nb_bits, nb_bits, depth);

> > +}

> > +

> > +int fib_tbl_lookup(uint32_t ip, int *port)

> > +{

> > +	fib_node_t *fe;

> > +	uint32_t idx;

> > +	int nb_bits;

> > +

> > +	nb_bits = FIB_IP_WIDTH - FIB_FIRST_STRIDE;

> > +	idx = ip >> nb_bits;

> > +	fe = &fib_rt_tbl[idx];

> > +

> > +	ip &= DEPTH_TO_MASK(nb_bits);

> > +	while (!fe->end) {

> > +		nb_bits -= FIB_NEXT_STRIDE;

> > +		idx = ip >> nb_bits;

> > +		fe = &fe->next[idx];

> > +		ip &= DEPTH_TO_MASK(nb_bits);

> > +	}

> > +	*port = fe->next_hop;

> > +

> > +	return fe->valid ? 0 : -1;

> > +}

> > diff --git a/example/l3fwd/odp_l3fwd_lpm.h

> b/example/l3fwd/odp_l3fwd_lpm.h

> > new file mode 100644

> > index 0000000..8f78e39

> > --- /dev/null

> > +++ b/example/l3fwd/odp_l3fwd_lpm.h

> > @@ -0,0 +1,20 @@

> > +/* Copyright (c) 2016, Linaro Limited

> > + * All rights reserved.

> > + *

> > + * SPDX-License-Identifier:     BSD-3-Clause

> > + */

> > +

> > +#ifndef ODP_L3FWD_LPM_H_

> > +#define ODP_L3FWD_LPM_H_

> it has to be _ODP_EXAMPLE_L3FWD_LPM_H

> 

> i.e. define should be started with "_" meaning that it's not odp api.

> (I know that in some places we use _ODP_ in other places we use ODP_ for

> that

> case. But that is wrong. We should not overlap with api name space.).

> 

> > +

> > +#ifdef __cplusplus

> > +extern "C" {

> > +#endif

> > +void fib_tbl_init(void);

> > +void fib_tbl_insert(uint32_t ip, int port, int depth);

> > +int fib_tbl_lookup(uint32_t ip, int *port);

> > +#ifdef __cplusplus

> > +}

> > +#endif

> > +

> > +#endif

> > diff --git a/example/m4/configure.m4 b/example/m4/configure.m4

> > index bbda38f..78ef396 100644

> > --- a/example/m4/configure.m4

> > +++ b/example/m4/configure.m4

> > @@ -19,5 +19,6 @@ AC_CONFIG_FILES([example/classifier/Makefile

> >   		 example/timer/Makefile

> >   		 example/traffic_mgmt/Makefile

> >   		 example/l2fwd_simple/Makefile

> > +		 example/l3fwd/Makefile

> >   		 example/switch/Makefile

> >   		 example/hello/Makefile])

> 

> On thought is what we need with l2fwd. We places it under

> test/performance/ but it's actual example app.

> Not sure that it's clear for new users. Definitely l2fwd and l3fwd have

> to be at the same level. I'm thinking

> about placing both to test/performance/ and create symlink with some

> README to example/. But I'm not sure

> about that, we need to discuss.


Yes.  In my opinion, we do not need a test/performance directory. 
If want to benchmarking automatically, we can build all the apps but run only
interested one.

> Maxim.

>
Forrest Shi July 25, 2016, 8:05 a.m. UTC | #3
Hi Maxim,

Any comments?

Thanks,
Forrest

> -----Original Message-----

> From: forrest.shi [mailto:forrest.shi@linaro.org]

> Sent: Friday, July 15, 2016 16:22

> To: 'Maxim Uvarov' <maxim.uvarov@linaro.org>

> Cc: lng-odp@lists.linaro.org

> Subject: RE: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

> 

> Hi Maxim,

> 

> The patch is length.  I extracted the comments I have some question as

below,
> Others will be fixed.

> 

>  Increment one value from number of threads might be not good thing to do

>  due to 1) performance 2) atomic operations break.

>  It's better if each thread has it's own counters and control threads

>  summaries values.

> ==========================================================

> Not catch your idea. Here each thread has its own counters and is totaled

> before main exit.

> 

> 

>  main should end if odp_destroy_global(). And to add this to 'make check'

>  requires probably to add some logic when we think that test passed and

>  when we thing that test failed.

>  I think you copied print_speed_stats() from l2fwd,  there return code

>  returns if app passed or not.

> ==========================================================

> The testing PASS depends on the print_speed_stats() output that is used by

> test shell script.

> Here return value is not taken  as PASS or FAIL.

> 

> 

> On thought is what we need with l2fwd. We places it under

>  test/performance/ but it's actual example app.

>  Not sure that it's clear for new users. Definitely l2fwd and l3fwd have

>  to be at the same level. I'm thinking

>  about placing both to test/performance/ and create symlink with some

>  README to example/. But I'm not sure

>  about that, we need to discuss.

> ==========================================================

> In my opinion, don't need to put app in performance.

> From literal meaning, test should handle testing things, no new app.

> 

> Thanks,

> Forrest

> 

> > -----Original Message-----

> > From: lng-odp [mailto:lng-odp-bounces@lists.linaro.org] On Behalf Of Maxim

> > Uvarov

> > Sent: Wednesday, July 13, 2016 17:10

> > To: lng-odp@lists.linaro.org

> > Subject: Re: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

> >

> > On 07/13/16 10:29, forrest.shi@linaro.org wrote:

> > > From: Xuelin Shi <forrest.shi@linaro.org>

> > >

> > > multi-thread, multi-queues and bi-directional forwarding.

> > >

> > > support (port, queue, thread) arguments in cmdline which specify how

> > > the threads handle which rx queue at which port, if no this argument,

> > > default specification used.

> > >

> > > both hash and lpm based lookup methods are supported, default lpm.

> > >

> > > Signed-off-by: Xuelin Shi <forrest.shi@linaro.org>

> > > ---

> > >   example/Makefile.am           |    2 +-

> > >   example/l3fwd/.gitignore      |    4 +

> > >   example/l3fwd/Makefile.am     |   18 +

> > >   example/l3fwd/odp_l3fwd.c     | 1072

> > +++++++++++++++++++++++++++++++++++++++++

> > >   example/l3fwd/odp_l3fwd_db.c  |  408 ++++++++++++++++

> > >   example/l3fwd/odp_l3fwd_db.h  |  130 +++++

> > >   example/l3fwd/odp_l3fwd_lpm.c |  224 +++++++++

> > >   example/l3fwd/odp_l3fwd_lpm.h |   20 +

> > >   example/m4/configure.m4       |    1 +

> > >   9 files changed, 1878 insertions(+), 1 deletion(-)

> > >   create mode 100644 example/l3fwd/.gitignore

> > >   create mode 100644 example/l3fwd/Makefile.am

> > >   create mode 100644 example/l3fwd/odp_l3fwd.c

> > >   create mode 100644 example/l3fwd/odp_l3fwd_db.c

> > >   create mode 100644 example/l3fwd/odp_l3fwd_db.h

> > >   create mode 100644 example/l3fwd/odp_l3fwd_lpm.c

> > >   create mode 100644 example/l3fwd/odp_l3fwd_lpm.h

> > >

> > > diff --git a/example/Makefile.am b/example/Makefile.am

> > > index 37542af..1f1b62e 100644

> > > --- a/example/Makefile.am

> > > +++ b/example/Makefile.am

> > > @@ -1 +1 @@

> > > -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

> > l2fwd_simple switch hello

> > > +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

> > l2fwd_simple switch hello l3fwd

> >

> > Hello Forest, you should use one git send-email command for both

> > patches. So 2 patches will be in the same email thread,

> > and not as 2 separate patches.

> >

> > alphabetical order here is required. And in next update please place all

> > entries vertically, newer patches will be simpler.

> >

> 

> OK, will be fixed.

> 

> >

> > > diff --git a/example/l3fwd/.gitignore b/example/l3fwd/.gitignore

> > > new file mode 100644

> > > index 0000000..4a25ae8

> > > --- /dev/null

> > > +++ b/example/l3fwd/.gitignore

> > > @@ -0,0 +1,4 @@

> > > +odp_l3fwd

> > > +Makefile

> > > +Makefile.in

> > > +*.o

> > last 3 entries are not needed due to we already have it in common .git

> > ignore file.

> >

> > > diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am

> > > new file mode 100644

> > > index 0000000..5092aa7

> > > --- /dev/null

> > > +++ b/example/l3fwd/Makefile.am

> > > @@ -0,0 +1,18 @@

> > > +include $(top_srcdir)/example/Makefile.inc

> > > +

> > > +bin_PROGRAMS = odp_l3fwd$(EXEEXT)

> > > +odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static

> > > +odp_l3fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example -

> > I${top_srcdir}/test

> > > +

> > > +noinst_HEADERS = \

> > > +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \

> > > +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_lpm.h \

> > > +		  $(top_srcdir)/example/example_debug.h

> > > +

> > > +dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c

> odp_l3fwd_lpm.c

> > > +

> > > +if test_example

> > > +TESTS = odp_l3fwd_run.sh

> > > +endif

> > > +

> > > +EXTRA_DIST = odp_l3fwd_run.sh

> > > diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c

> > > new file mode 100644

> > > index 0000000..a10ca76

> > > --- /dev/null

> > > +++ b/example/l3fwd/odp_l3fwd.c

> > > @@ -0,0 +1,1072 @@

> > > +/* Copyright (c) 2016, Linaro Limited

> > > + * All rights reserved.

> > > + *

> > > + * SPDX-License-Identifier:     BSD-3-Clause

> > > + */

> > > +

> > > +#include <stdlib.h>

> > > +#include <stdio.h>

> > > +#include <errno.h>

> > > +#include <getopt.h>

> > > +#include <unistd.h>

> > > +#include <inttypes.h>

> > > +

> > > +#include <test_debug.h>

> > > +

> > > +#include <odp_api.h>

> > > +#include <odp/helper/linux.h>

> > > +#include <odp/helper/eth.h>

> > > +#include <odp/helper/ip.h>

> > > +#include <odp/helper/udp.h>

> > > +#include <odp/helper/tcp.h>

> > > +

> > > +#include "odp_l3fwd_db.h"

> > > +#include "odp_l3fwd_lpm.h"

> > > +

> > > +#define POOL_NUM_PKT	8192

> > > +#define POOL_SEG_LEN	1856

> > > +#define MAX_PKT_BURST	32

> > > +

> > > +#define MAX_NB_WORKER	32

> > > +#define MAX_NB_PKTIO	32

> > > +#define MAX_NB_QUEUE	32

> > > +#define MAX_NB_QCONFS	1024

> > > +#define MAX_NB_ROUTE	32

> > > +

> > > +#define INVALID_ID	(-1)

> > > +#define PRINT_INTERVAL	10	/* interval seconds of printing stats

> */

> > > +

> > > +/** Get rid of path in filename - only for unix-type paths using '/' */

> > > +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \

> > > +			    strrchr((file_name), '/') + 1 : (file_name))

> > > +

> > > +struct l3fwd_pktio_s {

> > > +	odp_pktio_t pktio;

> > > +	odph_ethaddr_t mac_addr;

> > > +	odp_pktin_queue_t ifin[MAX_NB_QUEUE];

> > > +	odp_pktout_queue_t ifout[MAX_NB_QUEUE];

> > > +	int nb_rxq;

> > > +	int nb_txq;

> > > +	int rxq_idx;

> > > +	int txq_idx;

> > > +};

> > > +

> > > +struct l3fwd_qconf_s {

> > > +	uint8_t if_idx;		/* port index */

> > > +	uint8_t rxq_idx;	/* recv queue index in a port */

> > > +	uint8_t core_idx;	/* this core should handle traffic */

> > > +};

> > > +

> > > +struct thread_arg_s {

> > > +	uint64_t packets;

> > > +	uint64_t rx_drops;

> > > +	uint64_t tx_drops;

> > > +	struct {

> > > +		int used;

> > > +		int rxq[MAX_NB_QUEUE];

> > > +		int txq[MAX_NB_QUEUE];

> > > +	} pktio[MAX_NB_PKTIO];

> > > +	int thr_idx;

> > > +	int nb_pktio;

> > > +};

> > > +

> > > +typedef struct {

> > > +	char *if_names[MAX_NB_PKTIO];

> > > +	int if_count;

> > > +	char *route_str[MAX_NB_ROUTE];

> > > +	int worker_count;

> > > +	struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS];

> > > +	int qconf_count;

> > > +	uint32_t duration; /* seconds to run */

> > > +	uint8_t hash_mode; /* 1:hash, 0:lpm */

> > > +	uint8_t dest_mac_changed[MAX_NB_PKTIO]; /* 1: dest mac from

> > cmdline */

> > > +} app_args_t;

> > > +

> > > +struct {

> > > +	app_args_t		cmd_args;

> > > +	struct l3fwd_pktio_s	l3fwd_pktios[MAX_NB_PKTIO];

> > > +	odph_odpthread_t	l3fwd_workers[MAX_NB_WORKER];

> > > +	struct thread_arg_s	worker_args[MAX_NB_WORKER];

> > > +	odph_ethaddr_t		eth_dest_mac[MAX_NB_PKTIO];

> > > +

> > > +	/* forward func, hash or lpm */

> > > +	int (*fwd_func)(odp_packet_t pkt, int sif);

> > > +} global;

> > > +

> > > +/** Global barrier to synchronize main and workers */

> > > +static odp_barrier_t barrier;

> > > +static int exit_threads;	/**< Break workers loop if set to 1 */

> > > +

> > > +static void print_usage(char *progname);

> > if you place print_usage() above main there is no need for declaration.

The
> > same for other functions like print_info(). Not critical but you can

> > just remove

> > that lines and make example shorter.

> >

> 

> Will be fixed.

> 

> > > +static void print_qconf_table(app_args_t *args);

> > > +static void print_info(char *progname, app_args_t *args);

> > > +static int print_speed_stats(int num_workers, int duration, int

timeout);
> > > +static void parse_cmdline_args(int argc, char *argv[], app_args_t

*args);
> > > +static int parse_config(char *cfg_str, app_args_t *args);

> > > +static void setup_worker_qconf(app_args_t *args);

> > > +static void setup_fwd_db(void);

> > > +static int find_port_id_by_name(char *name, app_args_t *args);

> > > +static int split_string(char *str, int stringlen,

> > > +			char **tokens, int maxtokens, char delim);

> > > +

> > > +static int create_pktio(const char *name, odp_pool_t pool,

> > > +			struct l3fwd_pktio_s *fwd_pktio)

> > > +{

> > > +	odp_pktio_param_t pktio_param;

> > > +	odp_pktio_t pktio;

> > > +	odp_pktio_capability_t capa;

> > > +	int rc;

> > > +

> > > +	odp_pktio_param_init(&pktio_param);

> > > +

> > > +	pktio = odp_pktio_open(name, pool, &pktio_param);

> > > +	if (pktio == ODP_PKTIO_INVALID) {

> > > +		printf("Failed to open %s\n", name);

> > > +		return -1;

> > > +	}

> > > +	fwd_pktio->pktio = pktio;

> > > +

> > > +	rc = odp_pktio_capability(pktio, &capa);

> > > +	if (rc) {

> > > +		printf("Error: pktio %s: unable to read capabilities!\n",

> > > +		       name);

> > > +

> > > +		return -1;

> > > +	}

> > > +

> > > +	fwd_pktio->nb_rxq = (int)capa.max_input_queues;

> > > +	fwd_pktio->nb_txq = (int)capa.max_output_queues;

> > > +

> > > +	if (fwd_pktio->nb_rxq > MAX_NB_QUEUE)

> > > +		fwd_pktio->nb_rxq = MAX_NB_QUEUE;

> > > +

> > > +	if (fwd_pktio->nb_txq > MAX_NB_QUEUE)

> > > +		fwd_pktio->nb_txq = MAX_NB_QUEUE;

> > > +

> > > +	return 0;

> > > +}

> > > +

> > > +static void setup_fwd_db(void)

> > > +{

> > > +	fwd_db_entry_t *entry;

> > > +	int if_idx;

> > > +	app_args_t *args;

> > > +

> > > +	args = &global.cmd_args;

> > > +	if (args->hash_mode)

> > > +		init_fwd_hash_cache();

> > > +	else

> > > +		fib_tbl_init();

> > > +

> > > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> > > +		if_idx = entry->oif_id;

> > > +		if (!args->hash_mode)

> > > +			fib_tbl_insert(entry->subnet.addr, if_idx,

> > > +				       entry->subnet.depth);

> > > +		if (args->dest_mac_changed[if_idx])

> > > +			global.eth_dest_mac[if_idx] = entry->dst_mac;

> > > +		else

> > > +			entry->dst_mac = global.eth_dest_mac[if_idx];

> > > +	}

> > > +}

> > > +

> > > +static int l3fwd_pkt_hash(odp_packet_t pkt, int sif)

> > > +{

> > > +	fwd_db_entry_t *entry;

> > > +	ipv4_tuple5_t key;

> > > +	odph_ethhdr_t *eth;

> > > +	odph_udphdr_t  *udp;

> > > +	odph_ipv4hdr_t *ip;

> > > +	uint32_t len;

> > > +	int dif;

> > > +

> > > +	ip = odp_packet_l3_ptr(pkt, &len);

> > > +	key.dst_ip = odp_be_to_cpu_32(ip->dst_addr);

> > > +	key.src_ip = odp_be_to_cpu_32(ip->src_addr);

> > > +	key.proto = ip->proto;

> > > +

> > > +	if (odp_packet_has_udp(pkt) ||

> > > +	    odp_packet_has_tcp(pkt)) {

> > > +		/* UDP or TCP*/

> > > +		void *ptr = odp_packet_l4_ptr(pkt, NULL);

> > > +

> > > +		udp = (odph_udphdr_t *)ptr;

> > > +		key.src_port = odp_be_to_cpu_16(udp->src_port);

> > > +		key.dst_port = odp_be_to_cpu_16(udp->dst_port);

> > > +	} else {

> > > +		key.src_port = 0;

> > > +		key.dst_port = 0;

> > > +	}

> > > +

> > > +	entry = find_fwd_db_entry(&key);

> > > +	ip->ttl--;

> > odp_packet_l3_ptr() can return NULL. And you will have null pointer

> > deference here for non IP packet.

> > > +	ip->chksum = odph_ipv4_csum_update(pkt);

> > > +	eth = odp_packet_l2_ptr(pkt, NULL);

> > same here eth might be NULL. Better to optimize checks  ifs with

> > odp_unlikely().

> 

> Will be fixed.

> 

> > > +	if (entry) {

> > > +		eth->src = entry->src_mac;

> > > +		eth->dst = entry->dst_mac;

> > > +		dif = entry->oif_id;

> > > +	} else {

> > > +		/* no route, send by src port */

> > > +		eth->dst = eth->src;

> > > +		dif = sif;

> > > +	}

> > > +

> > > +	return dif;

> > > +}

> > > +

> > > +static int l3fwd_pkt_lpm(odp_packet_t pkt, int sif)

> > > +{

> > > +	odph_ipv4hdr_t *ip;

> > > +	odph_ethhdr_t *eth;

> > > +	uint32_t len;

> > > +	int dif;

> > > +	int ret;

> > > +

> > > +	ip = odp_packet_l3_ptr(pkt, &len);

> > NULL

> > > +	ip->ttl--;

> > > +	ip->chksum = odph_ipv4_csum_update(pkt);

> > > +	eth = odp_packet_l2_ptr(pkt, NULL);

> > NULL

> > > +

> > > +	/* network byte order maybe different from host */

> > > +	ret = fib_tbl_lookup(odp_be_to_cpu_32(ip->dst_addr), &dif);

> > > +	if (ret)

> > > +		dif = sif;

> > > +

> > > +	eth->dst = global.eth_dest_mac[dif];

> > > +	eth->src = global.l3fwd_pktios[dif].mac_addr;

> > > +

> > > +	return dif;

> > > +}

> > > +

> > > +/**

> > > + * Drop packets which input parsing marked as containing errors.

> > > + *

> > > + * Frees packets with error and modifies pkt_tbl[] to only contain

> packets

> > with

> > > + * no detected errors.

> > > + *

> > > + * @param pkt_tbl  Array of packets

> > > + * @param num      Number of packets in pkt_tbl[]

> > > + *

> > > + * @return Number of packets dropped

> > > + */

> > > +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)

> > > +{

> > > +	odp_packet_t pkt;

> > > +	unsigned dropped = 0;

> > > +	unsigned i, j;

> > > +

> > > +	for (i = 0, j = 0; i < num; ++i) {

> > > +		pkt = pkt_tbl[i];

> > > +

> > > +		if (odp_unlikely(odp_packet_has_error(pkt) ||

> > > +				 !odp_packet_has_ipv4(pkt))) {

> > > +			odp_packet_free(pkt);

> > > +			dropped++;

> > > +		} else if (odp_unlikely(i != j++)) {

> > > +			pkt_tbl[j - 1] = pkt;

> > > +		}

> > > +	}

> > > +

> > > +	return dropped;

> > > +}

> > > +

> > > +static void l3fwd_one_queue(uint32_t sif, uint8_t rxq_id, void

*thr_arg)
> > > +{

> > > +	struct l3fwd_pktio_s *port;

> > > +	odp_packet_t *tbl;

> > > +	odp_pktout_queue_t outq;

> > > +	odp_packet_t pkt_tbl[MAX_PKT_BURST];

> > > +	struct thread_arg_s *arg;

> > > +	uint8_t txq_id;

> > > +	int pkts, drop, sent;

> > > +	int dif, dst_port;

> > > +	int i;

> > > +

> > > +	arg = thr_arg;

> > > +	port = &global.l3fwd_pktios[sif];

> > > +	pkts = odp_pktin_recv(port->ifin[rxq_id], pkt_tbl, MAX_PKT_BURST);

> > > +	if (pkts <= 0)

> > > +		return;

> > > +	arg->packets += pkts;

> > > +	drop = drop_err_pkts(pkt_tbl, pkts);

> > > +	pkts -= drop;

> > > +	arg->rx_drops += drop;

> >

> > Increment one value from number of threads might be not good thing to do

> > due to 1) performance 2) atomic operations break.

> > It's better if each thread has it's own counters and control threads

> > summaries values.

> 

> Not catch your idea. Here each thread has its own counters.

> It will be totaled before exit.

> 

> >

> > > +

> > > +	dif = global.fwd_func(pkt_tbl[0], sif);

> > > +	tbl = &pkt_tbl[0];

> > > +	while (pkts) {

> > > +		dst_port = dif;

> > > +		for (i = 1; i < pkts; i++) {

> > > +			dif = global.fwd_func(tbl[i], sif);

> > > +			if (dif != dst_port)

> > > +				break;

> > > +		}

> > > +		txq_id = arg->pktio[dst_port].txq[rxq_id];

> > > +		outq = global.l3fwd_pktios[dst_port].ifout[txq_id];

> > > +		sent = odp_pktout_send(outq, tbl, i);

> > > +		if (odp_unlikely(sent < i)) {

> > > +			sent = sent < 0 ? 0 : sent;

> > > +			odp_packet_free_multi(&tbl[sent], i - sent);

> > > +			arg->tx_drops += i - sent;

> > > +		}

> > > +

> > > +		if (i < pkts)

> > > +			tbl += i;

> > > +

> > > +		pkts -= i;

> > > +	}

> > > +}

> > > +

> > > +static int run_worker(void *arg)

> > > +{

> > > +	int if_idx, rxq_idx;

> > > +	struct thread_arg_s *thr_arg = arg;

> > > +	struct l3fwd_pktio_s *port;

> > > +

> > > +	odp_barrier_wait(&barrier);

> > > +

> > > +	while (!exit_threads) {

> > > +		for (if_idx = 0; if_idx < thr_arg->nb_pktio; if_idx++) {

> > > +			if (!thr_arg->pktio[if_idx].used ||

> > > +			    thr_arg->thr_idx == INVALID_ID)

> > > +				continue;

> > > +

> > > +			port = &global.l3fwd_pktios[if_idx];

> > > +			for (rxq_idx = 0; rxq_idx < port->rxq_idx; rxq_idx++)

> > > +				l3fwd_one_queue(if_idx, rxq_idx, arg);

> > > +		}

> > > +	}

> > > +

> > > +	/* Make sure that latest stat writes are visible to other threads */

> > > +	odp_mb_full();

> > > +

> > > +	return 0;

> > > +}

> > > +

> > > +static int find_port_id_by_name(char *name, app_args_t *args)

> > > +{

> > > +	int i;

> > > +

> > > +	if (!name)

> > > +		return -1;

> > > +

> > > +	for (i = 0; i < args->if_count; i++) {

> > > +		if (!strcmp(name, args->if_names[i]))

> > > +			return i;

> > > +	}

> > > +

> > > +	return -1;

> > > +}

> > > +

> > > +int main(int argc, char **argv)

> > > +{

> > > +	odph_odpthread_t thread_tbl[MAX_NB_WORKER];

> > > +	odp_pool_t pool;

> > > +	odp_pool_param_t params;

> > > +	odp_instance_t instance;

> > > +	odph_odpthread_params_t thr_params;

> > > +	odp_cpumask_t cpumask;

> > > +	int cpu, i, j, nb_worker;

> > > +	uint8_t mac[ODPH_ETHADDR_LEN];

> > > +	app_args_t *args;

> > > +	struct thread_arg_s *thr_arg;

> > > +	char *oif;

> > > +	int oid;

> > > +

> > > +	if (odp_init_global(&instance, NULL, NULL)) {

> > > +		printf("Error: ODP global init failed.\n");

> > > +		exit(1);

> > > +	}

> > > +

> > > +	if (odp_init_local(instance, ODP_THREAD_CONTROL)) {

> > > +		printf("Error: ODP local init failed.\n");

> > > +		exit(1);

> > > +	}

> > > +

> > > +	/* Clear global argument and initialize the dest mac as 2:0:0:0:0:x */

> > > +	memset(&global, 0, sizeof(global));

> > > +	mac[0] = 2;

> > > +	for (i = 0; i < MAX_NB_PKTIO; i++) {

> > > +		mac[ODPH_ETHADDR_LEN - 1] = (uint8_t)i;

> > > +		memcpy(global.eth_dest_mac[i].addr, mac,

> > ODPH_ETHADDR_LEN);

> > > +	}

> > > +

> > > +	/* Initialize the thread arguments */

> > > +	for (i = 0; i < MAX_NB_WORKER; i++) {

> > > +		thr_arg = &global.worker_args[i];

> > > +		for (j = 0; j < MAX_NB_PKTIO; j++) {

> > > +			thr_arg->thr_idx = INVALID_ID;

> > > +			memset(thr_arg->pktio[j].rxq, INVALID_ID,

> > > +			       sizeof(thr_arg->pktio[j].rxq));

> > > +		}

> > > +	}

> > > +

> > > +	/* Parse cmdline arguments */

> > > +	args = &global.cmd_args;

> > > +	parse_cmdline_args(argc, argv, args);

> > > +

> > > +	/* Init l3fwd table */

> > > +	init_fwd_db();

> > > +

> > > +	/* Add route into table */

> > > +	for (i = 0; i < MAX_NB_ROUTE; i++) {

> > > +		if (args->route_str[i]) {

> > > +			oif = NULL;

> > > +			create_fwd_db_entry(args->route_str[i], &oif);

> > > +			oid = find_port_id_by_name(oif, args);

> > > +			if (oid != -1)

> > > +				args->dest_mac_changed[oid] = 1;

> > > +		}

> > > +	}

> > > +

> > > +	print_info(NO_PATH(argv[0]), args);

> > > +

> > > +	/* Create packet pool */

> > > +	odp_pool_param_init(&params);

> > > +	params.pkt.seg_len = POOL_SEG_LEN;

> > > +	params.pkt.len     = POOL_SEG_LEN;

> > > +	params.pkt.num     = POOL_NUM_PKT;

> > > +	params.type        = ODP_POOL_PACKET;

> > > +

> > > +	pool = odp_pool_create("packet pool", &params);

> > > +

> > > +	if (pool == ODP_POOL_INVALID) {

> > > +		printf("Error: packet pool create failed.\n");

> > > +		exit(1);

> > > +	}

> > > +

> > > +	/* Resolve fwd db*/

> > > +	for (i = 0; i < args->if_count; i++) {

> > > +		struct l3fwd_pktio_s *port;

> > > +		char *if_name;

> > > +

> > > +		if_name = args->if_names[i];

> > > +		port = &global.l3fwd_pktios[i];

> > > +		if (create_pktio(if_name, pool, port)) {

> > > +			printf("Error: create pktio %s\n", if_name);

> > > +			exit(1);

> > > +		}

> > > +		odp_pktio_mac_addr(port->pktio, mac, ODPH_ETHADDR_LEN);

> > > +		resolve_fwd_db(if_name, i, mac);

> > > +		memcpy(port->mac_addr.addr, mac, ODPH_ETHADDR_LEN);

> > > +	}

> > > +

> > > +	setup_fwd_db();

> > > +	dump_fwd_db();

> > > +

> > > +	/* Dicide available workers */

> > > +	nb_worker = MAX_NB_WORKER;

> > > +	if (args->worker_count)

> > > +		nb_worker = args->worker_count;

> > > +	nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker);

> > > +	args->worker_count = nb_worker;

> > > +

> > > +	/* Setup rx and tx queues for each port */

> > > +	setup_worker_qconf(args);

> > > +	print_qconf_table(args);

> > > +

> > > +	/* Decide ip lookup method */

> > > +	if (args->hash_mode)

> > > +		global.fwd_func = l3fwd_pkt_hash;

> > > +	else

> > > +		global.fwd_func = l3fwd_pkt_lpm;

> > > +

> > > +	/* Start all the available ports */

> > > +	for (i = 0; i < args->if_count; i++) {

> > > +		struct l3fwd_pktio_s *port;

> > > +		char *if_name;

> > > +		char buf[32];

> > > +

> > > +		if_name = args->if_names[i];

> > > +		port = &global.l3fwd_pktios[i];

> > > +		/* start pktio */

> > > +		if (odp_pktio_start(port->pktio)) {

> > > +			printf("unable to start pktio: %s\n", if_name);

> > > +			exit(1);

> > > +		}

> > > +

> > > +		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",

> > > +			port->mac_addr.addr[0],

> > > +			port->mac_addr.addr[1],

> > > +			port->mac_addr.addr[2],

> > > +			port->mac_addr.addr[3],

> > > +			port->mac_addr.addr[4],

> > > +			port->mac_addr.addr[5]);

> > > +		printf("start pktio: %s, mac %s\n", if_name, buf);

> > > +	}

> > > +

> > > +	odp_barrier_init(&barrier, nb_worker + 1);

> > > +

> > > +	memset(&thr_params, 0, sizeof(thr_params));

> > > +	thr_params.start    = run_worker;

> > > +	thr_params.thr_type = ODP_THREAD_WORKER;

> > > +	thr_params.instance = instance;

> > > +

> > > +	memset(thread_tbl, 0, sizeof(thread_tbl));

> > > +	cpu = odp_cpumask_first(&cpumask);

> > > +	for (i = 0; i < nb_worker; i++) {

> > > +		struct thread_arg_s *arg;

> > > +		odp_cpumask_t thr_mask;

> > > +

> > > +		arg = &global.worker_args[i];

> > > +		arg->nb_pktio = args->if_count;

> > > +		odp_cpumask_zero(&thr_mask);

> > > +		odp_cpumask_set(&thr_mask, cpu);

> > > +		thr_params.arg = arg;

> > > +		odph_odpthreads_create(&thread_tbl[i], &thr_mask,

> > > +				       &thr_params);

> > > +		cpu = odp_cpumask_next(&cpumask, cpu);

> > > +	}

> > > +

> > > +	if (args->duration) {

> > > +		print_speed_stats(nb_worker, args->duration,

> > PRINT_INTERVAL);

> > > +		exit_threads = 1;

> > > +	}

> > > +

> > > +	/* wait for other threads to join */

> > > +	for (i = 0; i < nb_worker; i++)

> > > +		odph_odpthreads_join(&thread_tbl[i]);

> > > +

> >

> > main should end if odp_destroy_global(). And to add this to 'make check'

> > requires probably to add some logic when we think that test passed and

> > when we thing that test failed.

> > I think you copied print_speed_stats() from l2fwd,  there return code

> > returns if app passed or not.

> >

> 

> The testing PASS depends on the print_speed_stats() output in the log file.

> Here return value is not taken  as PASS or FAIL.

> 

> > > +	return 0;

> > > +}

> > > +

> > > +static void print_usage(char *progname)

> > > +{

> > > +	printf("\n"

> > > +	       "ODP L3 forwarding application.\n"

> > > +	       "\n"

> > > +	       "Usage: %s OPTIONS\n"

> > > +	       "  E.g. %s -i eth0,eth1 -r 1.1.1.0/24,eth0 -r

> 2.2.2.0/24,eth1\n"

> > > +	       " In the above example,\n"

> > > +	       " eth0 will send pkts to eth1 and vice versa\n"

> > > +	       "\n"

> > > +	       "Mandatory OPTIONS:\n"

> > > +	       "  -i, --interface eth interfaces (comma-separated, no

> spaces)\n"

> > > +	       "  -r, --route SubNet,Intf[,NextHopMAC]\n"

> > > +	       "	NextHopMAC can be optional\n"

> > > +	       "\n"

> > > +	       "Optional OPTIONS:\n"

> > > +	       "  -s, --style [lpm|hash], ip lookup method\n"

> > > +	       "	optional, default as lpm\n"

> > > +	       "  -d, --duration Seconds to run and print stats\n"

> > > +	       "	optional, default as 0, run forever\n"

> > > +	       "  -t, --thread Number of threads to do forwarding\n"

> > > +	       "	optional, default as availbe worker cpu count\n"

> > > +	       "  -q, --queue  Configure rx queue(s) for port\n"

> > > +	       "	optional, format: [(port, queue, thread),...]\n"

> > > +	       "	for example: -q '(0, 0, 1),(1,0,2)'\n"

> > > +	       "  -h, --help   Display help and exit.\n\n"

> > > +	       "\n", NO_PATH(progname), NO_PATH(progname)

> > > +	    );

> > > +}

> > > +

> > > +static void parse_cmdline_args(int argc, char *argv[], app_args_t

*args)
> > > +{

> > > +	int opt;

> > > +	int long_index;

> > > +	char *token, *local;

> > > +	size_t len, route_index = 0;

> > > +	int i, mem_failure = 0;

> > > +

> > > +	static struct option longopts[] = {

> > > +		{"interface", required_argument, NULL, 'i'},	/* return 'i'

> */

> > > +		{"route", required_argument, NULL, 'r'},	/* return 'r'

> */

> > > +		{"style", optional_argument, NULL, 's'},	/* return 's'

> */

> > > +		{"duration", optional_argument, NULL, 'd'},	/* return 'd'

> */

> > > +		{"thread", optional_argument, NULL, 't'},	/* return 't'

> */

> > > +		{"queue", optional_argument, NULL, 'q'},	/* return 'q'

> */

> > > +		{"help", no_argument, NULL, 'h'},		/* return 'h'

> */

> > > +		{NULL, 0, NULL, 0}

> > > +	};

> > > +

> > > +	while (1) {

> > > +		opt = getopt_long(argc, argv, "+s:t:d:i:r:q:h",

> > > +				  longopts, &long_index);

> > > +

> > > +		if (opt == -1)

> > > +			break;	/* No more options */

> > > +

> > > +		switch (opt) {

> > > +		/* parse ip lookup method */

> > > +		case 's':

> > > +			if (!strcmp(optarg, "hash"))

> > > +				args->hash_mode = 1;

> > > +			break;

> > > +		/* parse number of worker threads to be run*/

> > > +		case 't':

> > > +			i = odp_cpu_count();

> > > +			args->worker_count = atoi(optarg);

> > > +			if (args->worker_count > i) {

> > > +				printf("Too many threads,"

> > > +				       "truncate to cpu count: %d\n", i);

> > > +				args->worker_count = i;

> > > +			}

> > > +

> > > +			break;

> > > +

> > > +		/* parse seconds to run */

> > > +		case 'd':

> > > +			args->duration = atoi(optarg);

> > > +			break;

> > > +

> > > +		/* parse packet-io interface names */

> > > +		case 'i':

> > > +			len = strlen(optarg);

> > > +			if (len == 0) {

> > > +				print_usage(argv[0]);

> > > +				exit(EXIT_FAILURE);

> > > +			}

> > > +			len += 1;	/* add room for '\0' */

> > > +

> > > +			local = malloc(len);

> > > +			if (!local) {

> > > +				print_usage(argv[0]);

> > > +				exit(EXIT_FAILURE);

> > > +			}

> > > +

> > > +			/* count the number of tokens separated by ',' */

> > > +			strcpy(local, optarg);

> > > +			for (token = strtok(local, ","), i = 0;

> > > +			     token != NULL;

> > > +			     token = strtok(NULL, ","), i++)

> > > +				;

> > > +

> > > +			if (i == 0) {

> > > +				print_usage(argv[0]);

> > > +				exit(EXIT_FAILURE);

> > > +			} else if (i > MAX_NB_PKTIO) {

> > > +				printf("too many ports specified, "

> > > +				       "truncated to %d", MAX_NB_PKTIO);

> > > +			}

> > > +			args->if_count = i;

> > > +

> > > +			/* store the if names (reset names string) */

> > > +			strcpy(local, optarg);

> > > +			for (token = strtok(local, ","), i = 0;

> > > +			     token != NULL; token = strtok(NULL, ","), i++) {

> > > +				args->if_names[i] = token;

> > > +			}

> > > +			break;

> > > +

> > > +		/*Configure Route in forwarding database*/

> > > +		case 'r':

> > > +			if (route_index >= MAX_NB_ROUTE) {

> > > +				printf("No more routes can be added\n");

> > > +				break;

> > > +			}

> > > +			local = calloc(1, strlen(optarg) + 1);

> > > +			if (!local) {

> > > +				mem_failure = 1;

> > > +				break;

> > > +			}

> > > +			memcpy(local, optarg, strlen(optarg));

> > > +			local[strlen(optarg)] = '\0';

> > > +			args->route_str[route_index++] = local;

> > > +			break;

> > > +

> > > +		case 'h':

> > > +			print_usage(argv[0]);

> > > +			exit(EXIT_SUCCESS);

> > > +			break;

> > > +

> > > +		case 'q':

> > > +			parse_config(optarg, args);

> > > +			break;

> > > +

> > > +		default:

> > > +			break;

> > > +		}

> > > +	}

> > > +

> > > +	/* checking arguments */

> > > +	if (args->if_count == 0) {

> > > +		printf("\nNo option -i specified.\n");

> > > +		goto out;

> > > +	}

> > > +

> > > +	if (args->route_str[0] == NULL) {

> > > +		printf("\nNo option -r specified.\n");

> > > +		goto out;

> > > +	}

> > > +

> > > +	if (mem_failure == 1) {

> > > +		printf("\nAllocate memory failure.\n");

> > > +		goto out;

> > > +	}

> > > +	optind = 1;		/* reset 'extern optind' from the getopt lib

> */

> > > +	return;

> > > +

> > > +out:

> > > +	print_usage(argv[0]);

> > > +	exit(EXIT_FAILURE);

> > > +}

> > > +

> > > +/* split string into tokens */

> > > +int split_string(char *str, int stringlen,

> > > +		 char **tokens, int maxtokens, char delim)

> > > +{

> > > +	int i, tok = 0;

> > > +	int tokstart = 1; /* first token is right at start of string */

> > > +

> > > +	if (str == NULL || tokens == NULL)

> > > +		goto einval_error;

> > > +

> > > +	for (i = 0; i < stringlen; i++) {

> > > +		if (str[i] == '\0' || tok >= maxtokens)

> > > +			break;

> > > +		if (tokstart) {

> > > +			tokstart = 0;

> > > +			tokens[tok++] = &str[i];

> > > +		}

> > > +		if (str[i] == delim) {

> > > +			str[i] = '\0';

> > > +			tokstart = 1;

> > > +		}

> > > +	}

> > > +	return tok;

> > > +

> > > +einval_error:

> > > +	errno = EINVAL;

> > > +	return -1;

> > > +}

> > > +

> > > +static int parse_config(char *cfg_str, app_args_t *args)

> > > +{

> > > +	char s[256];

> > > +	const char *p, *p0 = cfg_str;

> > > +	char *end;

> > > +	enum fieldnames {

> > > +		FLD_PORT = 0,

> > > +		FLD_QUEUE,

> > > +		FLD_LCORE,

> > > +		FLD_LAST

> > > +	};

> > > +	unsigned long int_fld[FLD_LAST];

> > > +	char *str_fld[FLD_LAST];

> > > +	int i;

> > > +	unsigned size;

> > > +	int nb_qconfs = 0;

> > > +	struct l3fwd_qconf_s *qconf_array = &args->qconf_config[0];

> > > +

> > > +	p = strchr(p0, '(');

> > > +	while (p != NULL) {

> > > +		++p;

> > > +		p0 = strchr(p, ')');

> > > +		if (p0 == NULL)

> > > +			return -1;

> > > +

> > > +		size = p0 - p;

> > > +		if (size >= sizeof(s))

> > > +			return -1;

> > > +

> > > +		snprintf(s, sizeof(s), "%.*s", size, p);

> > > +		i = split_string(s, sizeof(s), str_fld, FLD_LAST, ',');

> > > +		if (i != FLD_LAST)

> > > +			return -1;

> > > +		for (i = 0; i < FLD_LAST; i++) {

> > > +			errno = 0;

> > > +			int_fld[i] = strtoul(str_fld[i], &end, 0);

> > > +			if (errno != 0 || end == str_fld[i] || int_fld[i] >

> 255)

> > > +				return -1;

> > > +		}

> > > +		if (nb_qconfs >= MAX_NB_QCONFS) {

> > > +			printf("exceeded max number of queue

> > params: %hu\n",

> > > +			       nb_qconfs);

> > > +			return -1;

> > > +		}

> > > +		qconf_array[nb_qconfs].if_idx = (uint8_t)int_fld[FLD_PORT];

> > > +		qconf_array[nb_qconfs].rxq_idx =

> > (uint8_t)int_fld[FLD_QUEUE];

> > > +		qconf_array[nb_qconfs].core_idx =

> > (uint8_t)int_fld[FLD_LCORE];

> > > +		++nb_qconfs;

> > > +

> > > +		p = strchr(p0, '(');

> > > +	}

> > > +	args->qconf_count = nb_qconfs;

> > > +

> > > +	return 0;

> > > +}

> > > +

> > > +static void print_info(char *progname, app_args_t *args)

> > > +{

> > > +	int i;

> > > +

> > > +	printf("\n"

> > > +	       "ODP system info\n"

> > > +	       "---------------\n"

> > > +	       "ODP API version: %s\n"

> > > +	       "ODP impl name:	 %s\n"

> > > +	       "CPU model:       %s\n"

> > > +	       "CPU freq (hz):   %" PRIu64 "\n"

> > > +	       "Cache line size: %i\n"

> > > +	       "CPU count:       %i\n"

> > > +	       "\n",

> > > +	       odp_version_api_str(), odp_version_impl_name(),

> > > +	       odp_cpu_model_str(), odp_cpu_hz_max(),

> > > +	       odp_sys_cache_line_size(), odp_cpu_count());

> > > +

> > > +	printf("Running ODP appl: \"%s\"\n"

> > > +	       "-----------------\n"

> > > +	       "IP Lookup:	 %s\n"

> > > +	       "IF Count:        %i\n"

> > > +	       "Using IFs:      ",

> > > +	       progname,

> > > +	       args->hash_mode ? "hash" : "lpm",

> > > +	       args->if_count);

> > > +

> > > +	for (i = 0; i < args->if_count; ++i)

> > > +		printf(" %s", args->if_names[i]);

> > > +

> > > +	printf("\n\n");

> > > +	fflush(NULL);

> > > +}

> > > +

> > > +/**

> > > + * Setup rx and tx queues, distribute them among threads.

> > > + * Try to have one tx queue for each rx queue, if not vailable,

> > > + * shared tx queue is used.

> > > + *

> > > + * If no q argument, the queues are distribute among threads as

default.
> > > + * The thread take one rx queue of a port one time as round-robin

order.
> > > + */

> > > +static void setup_worker_qconf(app_args_t *args)

> > > +{

> > > +	int nb_worker, if_count;

> > > +	int i, j, rxq_idx, txq_idx;

> > > +	struct thread_arg_s *arg;

> > > +	struct l3fwd_pktio_s *port;

> > > +	uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_QUEUE];

> > > +

> > > +	nb_worker = args->worker_count;

> > > +	if_count = args->if_count;

> > > +

> > > +	/* distribute queues among threads */

> > > +	if (!args->qconf_count) {

> > > +		if (nb_worker > if_count) {

> > > +			for (i = 0; i < nb_worker; i++) {

> > > +				arg = &global.worker_args[i];

> > > +				arg->thr_idx = i;

> > > +				j = i % if_count;

> > > +				port = &global.l3fwd_pktios[j];

> > > +				if (port->rxq_idx < port->nb_rxq) {

> > > +					rxq_idx = port->rxq_idx;

> > > +					arg->pktio[j].rxq[rxq_idx] = rxq_idx;

> > > +					port->rxq_idx++;

> > > +					txq_idx = port->txq_idx;

> > > +					arg->pktio[j].txq[txq_idx] = txq_idx;

> > > +					port->txq_idx++;

> > > +					arg->pktio[j].used = 1;

> > > +				}

> > > +			}

> > > +		} else {

> > > +			for (i = 0; i < if_count; i++) {

> > > +				j = i % nb_worker;

> > > +				arg = &global.worker_args[j];

> > > +				arg->thr_idx = j;

> > > +				port = &global.l3fwd_pktios[i];

> > > +				if (port->rxq_idx < port->nb_rxq) {

> > > +					rxq_idx = port->rxq_idx;

> > > +					arg->pktio[i].rxq[rxq_idx] = rxq_idx;

> > > +					port->rxq_idx++;

> > > +					txq_idx = port->txq_idx;

> > > +					arg->pktio[i].txq[txq_idx] = txq_idx;

> > > +					port->txq_idx++;

> > > +					arg->pktio[i].used = 1;

> > > +				}

> > > +			}

> > > +		}

> > > +	}

> > > +

> > > +	/* specified q argument, distribute queues among threads as it says */

> > > +	memset(queue_mask, 0, sizeof(queue_mask));

> > > +	for (i = 0; i < args->qconf_count; i++) {

> > > +		struct l3fwd_qconf_s *q;

> > > +

> > > +		q = &args->qconf_config[i];

> > > +		if (q->core_idx >= nb_worker || q->if_idx >= if_count)

> > > +			LOG_ABORT("Error queue (%d, %d, %d), max port: "

> > > +				  "%d, max core: %d\n", q->if_idx, q->rxq_idx,

> > > +				  q->core_idx, args->if_count - 1,

> > > +				  args->worker_count - 1);

> > > +

> > > +		/* check if one queue is configured twice or more */

> > > +		if (queue_mask[q->if_idx][q->rxq_idx])

> > > +			LOG_ABORT("Error queue (%d, %d, %d), reconfig

> > queue\n",

> > > +				  q->if_idx, q->rxq_idx, q->core_idx);

> > > +		queue_mask[q->if_idx][q->rxq_idx] = 1;

> > > +

> > > +		port = &global.l3fwd_pktios[q->if_idx];

> > > +		if (port->rxq_idx < q->rxq_idx)

> > > +			LOG_ABORT("Error queue (%d, %d, %d), queue should

> > be"

> > > +				  " in sequence and start from 0, queue %d\n",

> > > +				  q->if_idx, q->rxq_idx, q->core_idx,

> > > +				  q->rxq_idx);

> > > +

> > > +		if (q->rxq_idx > port->nb_rxq) {

> > > +			LOG_ABORT("Error queue (%d, %d, %d), max

> > queue %d\n",

> > > +				  q->if_idx, q->rxq_idx, q->core_idx,

> > > +				  port->nb_rxq - 1);

> > > +		}

> > > +		port->rxq_idx = q->rxq_idx + 1;

> > > +		port->txq_idx = q->rxq_idx + 1;

> > > +

> > > +		/* put the queue into worker_args */

> > > +		arg = &global.worker_args[q->core_idx];

> > > +		arg->pktio[q->if_idx].rxq[q->rxq_idx] = q->rxq_idx;

> > > +		arg->pktio[q->if_idx].txq[q->rxq_idx] = q->rxq_idx;

> > > +		arg->pktio[q->if_idx].used = 1;

> > > +		arg->thr_idx = q->core_idx;

> > > +	}

> > > +

> > > +	/* config and initialize rx and tx queues. */

> > > +	for (i = 0; i < if_count; i++) {

> > > +		odp_pktin_queue_param_t in_queue_param;

> > > +		odp_pktout_queue_param_t out_queue_param;

> > > +		struct odp_pktin_queue_t *inq;

> > > +		struct odp_pktout_queue_t *outq;

> > > +		const char *name;

> > > +		int rc, nb_rxq, nb_txq;

> > > +

> > > +		port = &global.l3fwd_pktios[i];

> > > +		name = args->if_names[i];

> > > +		odp_pktin_queue_param_init(&in_queue_param);

> > > +		odp_pktout_queue_param_init(&out_queue_param);

> > > +

> > > +		in_queue_param.op_mode = ODP_PKTIO_OP_MT;

> > > +		out_queue_param.op_mode = ODP_PKTIO_OP_MT;

> > > +

> > > +		in_queue_param.num_queues = port->rxq_idx;

> > > +		if (port->rxq_idx > port->nb_rxq) {

> > > +			in_queue_param.num_queues = port->nb_rxq;

> > > +			in_queue_param.op_mode =

> > ODP_PKTIO_OP_MT_UNSAFE;

> > > +		}

> > > +

> > > +		/* enable flow hashing for multi-input queues */

> > > +		if (in_queue_param.num_queues > 1) {

> > > +			in_queue_param.hash_enable = 1;

> > > +			in_queue_param.hash_proto.proto.ipv4_tcp = 1;

> > > +			in_queue_param.hash_proto.proto.ipv4_udp = 1;

> > > +		}

> > > +

> > > +		if (odp_pktin_queue_config(port->pktio, &in_queue_param))

> > > +			LOG_ABORT("Fail to config input queue for %s\n",

> > name);

> > > +

> > > +		out_queue_param.num_queues = port->txq_idx;

> > > +		if (port->txq_idx > port->nb_txq) {

> > > +			out_queue_param.num_queues = port->nb_txq;

> > > +			out_queue_param.op_mode =

> > ODP_PKTIO_OP_MT_UNSAFE;

> > > +		}

> > > +		if (odp_pktout_queue_config(port->pktio,

> > &out_queue_param))

> > > +			LOG_ABORT("Fail to config output queue for %s\n",

> > name);

> > > +

> > > +		inq = port->ifin;

> > > +		nb_rxq = in_queue_param.num_queues;

> > > +		if (odp_pktin_queue(port->pktio, inq, nb_rxq) != nb_rxq)

> > > +			LOG_ABORT("Fail to set pktin queue for %s\n", name);

> > > +

> > > +		if (port->rxq_idx > port->nb_rxq) {

> > > +			for (rc = port->nb_rxq; rc < port->rxq_idx; rc++)

> > > +				inq[rc] = inq[rc % port->nb_rxq];

> > > +		}

> > > +

> > > +		outq = port->ifout;

> > > +		nb_txq = out_queue_param.num_queues;

> > > +		if (odp_pktout_queue(port->pktio, outq, nb_txq) != nb_txq)

> > > +			LOG_ABORT("Fail to set pktout queue for %s\n",

> > name);

> > > +

> > > +		if (port->txq_idx > port->nb_txq) {

> > > +			for (rc = port->nb_txq; rc < port->txq_idx; rc++)

> > > +				outq[rc] = outq[rc % port->nb_txq];

> > > +		}

> > > +	}

> > > +}

> > > +

> > > +static void print_qconf_table(app_args_t *args)

> > > +{

> > > +	int i, j, k, qid;

> > > +	char buf[32];

> > > +	struct thread_arg_s *thr_arg;

> > > +

> > > +	printf("Rx queue table\n"

> > > +	       "-----------------\n"

> > > +	       "%-16s%-16s%-16s\n",

> > > +	       "port/id", "queue", "thread");

> > > +

> > > +	for (i = 0; i < args->worker_count; i++) {

> > > +		thr_arg = &global.worker_args[i];

> > > +		for (j = 0; j < args->if_count; j++) {

> > > +			if (!thr_arg->pktio[j].used)

> > > +				continue;

> > > +

> > > +			sprintf(buf, "%s/%d", args->if_names[j], j);

> > > +			for (k = 0; k < MAX_NB_QUEUE; k++) {

> > > +				qid = thr_arg->pktio[j].rxq[k];

> > > +				if (qid != INVALID_ID)

> > > +					printf("%-16s%-16d%-16d\n", buf, qid,

> > > +					       thr_arg->thr_idx);

> > > +			}

> > > +		}

> > > +	}

> > > +	printf("\n");

> > > +	fflush(NULL);

> > > +}

> > > +

> > > +/**

> > > + *  Print statistics

> > > + *

> > > + * @param num_workers Number of worker threads

> > > + * @param duration Number of seconds to loop in

> > > + * @param timeout Number of seconds for stats calculation

> > > + *

> > > + */

> > > +static int print_speed_stats(int num_workers, int duration, int

timeout)
> > > +{

> > > +	uint64_t pkts = 0;

> > > +	uint64_t pkts_prev = 0;

> > > +	uint64_t pps;

> > > +	uint64_t rx_drops, tx_drops;

> > > +	uint64_t maximum_pps = 0;

> > > +	int i;

> > > +	int elapsed = 0;

> > > +	int stats_enabled = 1;

> > > +	int loop_forever = (duration == 0);

> > > +

> > > +	if (timeout <= 0) {

> > > +		stats_enabled = 0;

> > > +		timeout = 1;

> > > +	}

> > > +	/* Wait for all threads to be ready*/

> > > +	odp_barrier_wait(&barrier);

> > > +

> > > +	do {

> > > +		pkts = 0;

> > > +		rx_drops = 0;

> > > +		tx_drops = 0;

> > > +		sleep(timeout);

> > > +

> > > +		for (i = 0; i < num_workers; i++) {

> > > +			pkts += global.worker_args[i].packets;

> > > +			rx_drops += global.worker_args[i].rx_drops;

> > > +			tx_drops += global.worker_args[i].tx_drops;

> > > +		}

> > > +		if (stats_enabled) {

> > > +			pps = (pkts - pkts_prev) / timeout;

> > > +			if (pps > maximum_pps)

> > > +				maximum_pps = pps;

> > > +			printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,

> > > +			       maximum_pps);

> > > +

> > > +			printf(" %" PRIu64 " rx drops, %" PRIu64 " tx

> drops\n",

> > > +			       rx_drops, tx_drops);

> > > +

> > > +			pkts_prev = pkts;

> > > +		}

> > > +		elapsed += timeout;

> > > +	} while (loop_forever || (elapsed < duration));

> > > +

> > > +	if (stats_enabled)

> > > +		printf("TEST RESULT: %" PRIu64 " maximum packets per

> > second.\n",

> > > +		       maximum_pps);

> > > +

> > > +	return pkts > 100 ? 0 : -1;

> > > +}

> > > diff --git a/example/l3fwd/odp_l3fwd_db.c

> > b/example/l3fwd/odp_l3fwd_db.c

> > > new file mode 100644

> > > index 0000000..93e32f0

> > > --- /dev/null

> > > +++ b/example/l3fwd/odp_l3fwd_db.c

> > > @@ -0,0 +1,408 @@

> > > +/* Copyright (c) 2016, Linaro Limited

> > > + * All rights reserved.

> > > + *

> > > + * SPDX-License-Identifier:     BSD-3-Clause

> > > + */

> > > +

> > > +#ifndef _GNU_SOURCE

> > > +#define _GNU_SOURCE

> > > +#endif

> > > +

> > > +#include <stdlib.h>

> > > +#include <string.h>

> > > +

> > > +#include <example_debug.h>

> > > +#include <odp_api.h>

> > > +#include <odp_l3fwd_db.h>

> > > +

> > > +/** Jenkins hash support.

> > > +  *

> > > +  * Copyright (C) 2006 Bob Jenkins (bob_jenkins@burtleburtle.net)

> > > +  *

> > > +  * http://burtleburtle.net/bob/hash/

> > > +  *

> > > +  * These are the credits from Bob's sources:

> > > +  *

> > > +  * lookup3.c, by Bob Jenkins, May 2006, Public Domain.

> > > +  *

> > > +  * These are functions for producing 32-bit hashes for hash table

> lookup.

> > > +  * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and

> final()

> > > +  * are externally useful functions.  Routines to test the hash are

> included

> > > +  * if SELF_TEST is defined.  You can use this free for any purpose.

> It's in

> > > +  * the public domain.  It has no warranty.

> > > +  *

> > > +  * $FreeBSD$

> > > +  */

> > > +#define JHASH_GOLDEN_RATIO	0x9e3779b9

> > > +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))

> > > +#define FWD_BJ3_MIX(a, b, c) \

> > > +{ \

> > > +	a -= c; a ^= rot(c, 4); c += b; \

> > > +	b -= a; b ^= rot(a, 6); a += c; \

> > > +	c -= b; c ^= rot(b, 8); b += a; \

> > > +	a -= c; a ^= rot(c, 16); c += b; \

> > > +	b -= a; b ^= rot(a, 19); a += c; \

> > > +	c -= b; c ^= rot(b, 4); b += a; \

> > > +}

> > > +

> > > +/**

> > > + * Compute hash value from a flow

> > > + */

> > > +static inline

> > > +uint64_t l3fwd_calc_hash(ipv4_tuple5_t *key)

> > > +{

> > > +	uint64_t l4_ports = 0;

> > > +	uint32_t dst_ip, src_ip;

> > > +

> > > +	src_ip = key->src_ip;

> > > +	dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO;

> > > +	FWD_BJ3_MIX(src_ip, dst_ip, l4_ports);

> > > +

> > > +	return l4_ports;

> > > +}

> > > +

> > > +/**

> > > + * Parse text string representing an IPv4 address or subnet

> > > + *

> > > + * String is of the format "XXX.XXX.XXX.XXX(/W)" where

> > > + * "XXX" is decimal value and "/W" is optional subnet length

> > > + *

> > > + * @param ipaddress  Pointer to IP address/subnet string to convert

> > > + * @param addr       Pointer to return IPv4 address, host endianness

> > > + * @param depth      Pointer to subnet bit width

> > > + * @return 0 if successful else -1

> > > + */

> > > +static inline

> > > +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *depth)

> > > +{

> > > +	int b[4];

> > > +	int qualifier = 32;

> > > +	int converted;

> > > +	uint32_t addr_le;

> > > +

> > > +	if (strchr(ipaddress, '/')) {

> > > +		converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",

> > > +				   &b[3], &b[2], &b[1], &b[0],

> > > +				   &qualifier);

> > > +		if (5 != converted)

> > > +			return -1;

> > > +	} else {

> > > +		converted = sscanf(ipaddress, "%d.%d.%d.%d",

> > > +				   &b[3], &b[2], &b[1], &b[0]);

> > > +		if (4 != converted)

> > > +			return -1;

> > > +	}

> > > +

> > > +	if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))

> > > +		return -1;

> > > +	if (!qualifier || (qualifier > 32))

> > > +		return -1;

> > > +

> > > +	addr_le = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;

> > > +	*addr = odp_le_to_cpu_32(addr_le);

> > > +	*depth = qualifier;

> > > +

> > > +	return 0;

> > > +}

> > > +

> > > +/**

> > > + * Generate text string representing IPv4 range/subnet, output

> > > + * in "XXX.XXX.XXX.XXX/W" format

> > > + *

> > > + * @param b     Pointer to buffer to store string

> > > + * @param range Pointer to IPv4 address range

> > > + *

> > > + * @return Pointer to supplied buffer

> > > + */

> > > +static inline

> > > +char *ipv4_subnet_str(char *b, ip_addr_range_t *range)

> > > +{

> > > +	sprintf(b, "%d.%d.%d.%d/%d",

> > > +		0xFF & ((range->addr) >> 24),

> > > +		0xFF & ((range->addr) >> 16),

> > > +		0xFF & ((range->addr) >>  8),

> > > +		0xFF & ((range->addr) >>  0),

> > > +		range->depth);

> > > +	return b;

> > > +}

> > > +

> > > +/**

> > > + * Generate text string representing MAC address

> > > + *

> > > + * @param b     Pointer to buffer to store string

> > > + * @param mac   Pointer to MAC address

> > > + *

> > > + * @return Pointer to supplied buffer

> > > + */

> > > +static inline

> > > +char *mac_addr_str(char *b, odph_ethaddr_t *mac)

> > > +{

> > > +	uint8_t *byte;

> > > +

> > > +	byte = mac->addr;

> > > +	sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",

> > > +		byte[0], byte[1], byte[2], byte[3], byte[4], byte[5]);

> > > +	return b;

> > > +}

> > > +

> > > +/**

> > > + * Flow cache table entry

> > > + */

> > > +typedef struct flow_entry_s {

> > > +	ipv4_tuple5_t key;		/**< match key */

> > > +	struct flow_entry_s *next;      /**< next entry on the list */

> > > +	fwd_db_entry_t *fwd_entry;	/**< entry info in db */

> > > +} flow_entry_t;

> > > +

> > > +/**

> > > + * Flow cache table bucket

> > > + */

> > > +typedef struct flow_bucket_s {

> > > +	odp_spinlock_t		lock;	/**< Bucket lock*/

> > > +	flow_entry_t		*next;	/**< Pointer to first flow entry in

> > bucket*/

> > > +} flow_bucket_t;

> > > +

> > > +/**

> > > + * Flow hash table, fast lookup cache

> > > + */

> > > +typedef struct flow_table_s {

> > > +	flow_bucket_t *bucket;

> > > +	uint32_t count;

> > > +} flow_table_t;

> > > +

> > > +static flow_table_t fwd_lookup_cache;

> > > +

> > > +void init_fwd_hash_cache(void)

> > > +{

> > > +	odp_shm_t		hash_shm;

> > > +	flow_bucket_t		*bucket;

> > > +	uint32_t		bucket_count;

> > > +	uint32_t		i;

> > > +

> > > +	bucket_count = FWD_DEF_BUCKET_COUNT;

> > > +

> > > +	/*Reserve memory for Routing hash table*/

> > > +	hash_shm = odp_shm_reserve("route_table",

> > > +				   sizeof(flow_bucket_t) * bucket_count,

> > > +				   ODP_CACHE_LINE_SIZE, 0);

> > > +

> > > +	bucket = odp_shm_addr(hash_shm);

> > > +	if (!bucket) {

> > > +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> > > +		exit(-1);

> > > +	}

> > > +

> > > +	fwd_lookup_cache.bucket = bucket;

> > > +	fwd_lookup_cache.count = bucket_count;

> > > +

> > > +	/*Initialize Locks*/

> > > +	for (i = 0; i < bucket_count; i++) {

> > > +		bucket = &fwd_lookup_cache.bucket[i];

> > > +		odp_spinlock_init(&bucket->lock);

> > > +		bucket->next = NULL;

> > > +	}

> > > +}

> > > +

> > > +static inline

> > > +int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow)

> > > +{

> > > +	if (key->src_ip == flow->key.src_ip &&

> > > +	    key->dst_ip == flow->key.dst_ip &&

> > > +	    key->src_port == flow->key.src_port &&

> > > +	    key->dst_port == flow->key.dst_port &&

> > > +	    key->proto == flow->key.proto)

> > > +		return 1;

> > > +

> > > +	return 0;

> > > +}

> > > +

> > > +static inline

> > > +flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t

> > *bucket)

> > > +{

> > > +	flow_entry_t *rst;

> > > +

> > > +	for (rst = bucket->next; rst != NULL; rst = rst->next) {

> > > +		if (match_key_flow(key, rst))

> > > +			break;

> > > +	}

> > > +

> > > +	return rst;

> > > +}

> > > +

> > > +static inline

> > > +flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key,

> > > +			       flow_bucket_t *bucket,

> > > +			       fwd_db_entry_t *entry)

> > > +{

> > > +	flow_entry_t *flow;

> > > +

> > > +	flow = lookup_fwd_cache(key, bucket);

> > > +	if (flow)

> > > +		return flow;

> > > +

> > > +	flow = malloc(sizeof(flow_entry_t));

> > > +	flow->key = *key;

> > > +	flow->fwd_entry = entry;

> > > +

> > > +	odp_spinlock_lock(&bucket->lock);

> > > +	if (!bucket->next) {

> > > +		bucket->next = flow;

> > > +	} else {

> > > +		flow->next = bucket->next;

> > > +		bucket->next = flow;

> > > +	}

> > > +	odp_spinlock_unlock(&bucket->lock);

> > > +

> > > +	return flow;

> > > +}

> > > +

> > > +/** Global pointer to fwd db */

> > > +fwd_db_t *fwd_db;

> > > +

> > > +void init_fwd_db(void)

> > > +{

> > > +	odp_shm_t shm;

> > > +

> > > +	shm = odp_shm_reserve("shm_fwd_db",

> > > +			      sizeof(fwd_db_t),

> > > +			      ODP_CACHE_LINE_SIZE,

> > > +			      0);

> > > +

> > > +	fwd_db = odp_shm_addr(shm);

> > > +

> > > +	if (fwd_db == NULL) {

> > > +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> > > +		exit(EXIT_FAILURE);

> > > +	}

> > > +	memset(fwd_db, 0, sizeof(*fwd_db));

> > > +}

> > > +

> > > +int create_fwd_db_entry(char *input, char **oif)

> > > +{

> > > +	int pos = 0;

> > > +	char *local;

> > > +	char *str;

> > > +	char *save;

> > > +	char *token;

> > > +	fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];

> > > +

> > > +	/* Verify we haven't run out of space */

> > > +	if (MAX_DB <= fwd_db->index)

> > > +		return -1;

> > > +

> > > +	/* Make a local copy */

> > > +	local = malloc(strlen(input) + 1);

> > > +	if (NULL == local)

> > > +		return -1;

> > > +	strcpy(local, input);

> > > +

> > > +	/* Setup for using "strtok_r" to search input string */

> > > +	str = local;

> > > +	save = NULL;

> > > +

> > > +	/* Parse tokens separated by ':' */

> > > +	while (NULL != (token = strtok_r(str, ",", &save))) {

> > > +		str = NULL;  /* reset str for subsequent strtok_r calls */

> > > +

> > > +		/* Parse token based on its position */

> > > +		switch (pos) {

> > > +		case 0:

> > > +			parse_ipv4_string(token,

> > > +					  &entry->subnet.addr,

> > > +					  &entry->subnet.depth);

> > > +			break;

> > > +		case 1:

> > > +			strncpy(entry->oif, token, OIF_LEN - 1);

> > > +			entry->oif[OIF_LEN - 1] = 0;

> > > +			break;

> > > +		case 2:

> > > +			odph_eth_addr_parse(&entry->dst_mac, token);

> > > +			*oif = entry->oif;

> > > +			break;

> > > +

> > > +		default:

> > > +			printf("ERROR: extra token \"%s\" at position %d\n",

> > > +			       token, pos);

> > > +			break;

> > > +		}

> > > +

> > > +		/* Advance to next position */

> > > +		pos++;

> > > +	}

> > > +

> > > +	/* Add route to the list */

> > > +	fwd_db->index++;

> > > +	entry->next = fwd_db->list;

> > > +	fwd_db->list = entry;

> > > +

> > > +	free(local);

> > > +	return 0;

> > > +}

> > > +

> > > +void resolve_fwd_db(char *intf, int portid, uint8_t *mac)

> > > +{

> > > +	fwd_db_entry_t *entry;

> > > +

> > > +	/* Walk the list and attempt to set output and MAC */

> > > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> > > +		if (strcmp(intf, entry->oif))

> > > +			continue;

> > > +

> > > +		entry->oif_id = portid;

> > > +		memcpy(entry->src_mac.addr, mac, ODPH_ETHADDR_LEN);

> > > +	}

> > > +}

> > > +

> > > +void dump_fwd_db_entry(fwd_db_entry_t *entry)

> > > +{

> > > +	char subnet_str[MAX_STRING];

> > > +	char mac_str[MAX_STRING];

> > > +

> > > +	mac_addr_str(mac_str, &entry->dst_mac);

> > > +	printf("%-16s%-16s%-16s\n",

> > > +	       ipv4_subnet_str(subnet_str, &entry->subnet),

> > > +	       entry->oif, mac_str);

> > > +}

> > > +

> > > +void dump_fwd_db(void)

> > > +{

> > > +	fwd_db_entry_t *entry;

> > > +

> > > +	printf("Routing table\n"

> > > +	       "-----------------\n"

> > > +	       "%-16s%-16s%-16s\n",

> > > +	       "subnet", "next_hop", "dest_mac");

> > > +

> > > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> > > +		dump_fwd_db_entry(entry);

> > > +

> > > +	printf("\n");

> > > +}

> > > +

> > > +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key)

> > > +{

> > > +	fwd_db_entry_t *entry;

> > > +	flow_entry_t *flow;

> > > +	flow_bucket_t *bucket;

> > > +	uint64_t hash;

> > > +

> > > +	/* first find in cache */

> > > +	hash = l3fwd_calc_hash(key);

> > > +	hash &= fwd_lookup_cache.count - 1;

> > > +	bucket = &fwd_lookup_cache.bucket[hash];

> > > +	flow = lookup_fwd_cache(key, bucket);

> > > +	if (flow)

> > > +		return flow->fwd_entry;

> > > +

> > > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> > > +		uint32_t mask;

> > > +

> > > +		mask = ((1u << entry->subnet.depth) - 1) <<

> > > +			(32 - entry->subnet.depth);

> > > +

> > > +		if (entry->subnet.addr == (key->dst_ip & mask))

> > > +			break;

> > > +	}

> > > +

> > > +	return entry;

> > > +}

> > > diff --git a/example/l3fwd/odp_l3fwd_db.h

> > b/example/l3fwd/odp_l3fwd_db.h

> > > new file mode 100644

> > > index 0000000..840946a

> > > --- /dev/null

> > > +++ b/example/l3fwd/odp_l3fwd_db.h

> > > @@ -0,0 +1,130 @@

> > > +/* Copyright (c) 2016, Linaro Limited

> > > + * All rights reserved.

> > > + *

> > > + * SPDX-License-Identifier:     BSD-3-Clause

> > > + */

> > > +

> > > +#ifndef ODP_L3FWD_DB_H_

> > > +#define ODP_L3FWD_DB_H_

> > > +

> > > +#ifdef __cplusplus

> > > +extern "C" {

> > > +#endif

> > > +

> > > +#include <odp_api.h>

> > > +#include <odp/helper/eth.h>

> > > +

> > > +#define OIF_LEN 32

> > > +#define MAX_DB  32

> > > +#define MAX_STRING  32

> > > +

> > > +/**

> > > + * Default number of flows

> > > + */

> > > +#define FWD_DEF_FLOW_COUNT		100000

> > > +

> > > +/**

> > > + * Default Hash bucket number

> > > + */

> > > +#define FWD_DEF_BUCKET_COUNT	(FWD_DEF_FLOW_COUNT / 8)

> > > +

> > > +/**

> > > + * IP address range (subnet)

> > > + */

> > > +typedef struct ip_addr_range_s {

> > > +	uint32_t  addr;     /**< IP address, host endianness */

> > > +	uint32_t  depth;    /**< subnet bit width */

> > > +} ip_addr_range_t;

> > > +

> > > +/**

> > > + * TCP/UDP flow

> > > + */

> > > +typedef struct ipv4_tuple5_s {

> > > +	uint32_t src_ip;

> > > +	uint32_t dst_ip;

> > > +	uint16_t src_port;

> > > +	uint16_t dst_port;

> > > +	uint8_t  proto;

> > > +} ipv4_tuple5_t;

> > > +

> > > +/**

> > > + * Forwarding data base entry

> > > + */

> > > +typedef struct fwd_db_entry_s {

> > > +	struct fwd_db_entry_s *next;          /**< Next entry on list */

> > > +	char                    oif[OIF_LEN]; /**< Output interface name */

> > > +	int			oif_id;	      /**< Output interface idx */

> > > +	odph_ethaddr_t		src_mac;      /**< Output source MAC */

> > > +	odph_ethaddr_t		dst_mac;      /**< Output destination

> > MAC */

> > > +	ip_addr_range_t		subnet;       /**< Subnet for this router

> > */

> > > +} fwd_db_entry_t;

> > > +

> > > +/**

> > > + * Forwarding data base

> > > + */

> > > +typedef struct fwd_db_s {

> > > +	uint32_t          index;          /**< Next available entry */

> > > +	fwd_db_entry_t   *list;           /**< List of active routes */

> > > +	fwd_db_entry_t    array[MAX_DB];  /**< Entry storage */

> > > +} fwd_db_t;

> > > +

> > > +/** Global pointer to fwd db */

> > > +extern fwd_db_t *fwd_db;

> > > +

> > > +/**

> > > + * Initialize FWD DB

> > > + */

> > > +void init_fwd_db(void);

> > > +

> > > +/**

> > > + * Initialize forward lookup cache based on hash

> > > + */

> > > +void init_fwd_hash_cache(void);

> > > +

> > > +/**

> > > + * Create a forwarding database entry

> > > + *

> > > + * String is of the format "SubNet,Intf,NextHopMAC"

> > > + *

> > > + * @param input  Pointer to string describing route

> > > + * @param oif  Pointer to out interface name, as a return value

> > > + *

> > > + * @return 0 if successful else -1

> > > + */

> > > +int create_fwd_db_entry(char *input, char **oif);

> > > +

> > > +/**

> > > + * Scan FWD DB entries and resolve output queue and source MAC

> address

> > > + *

> > > + * @param intf   Interface name string

> > > + * @param portid Output queue for packet transmit

> > > + * @param mac    MAC address of this interface

> > > + */

> > > +void resolve_fwd_db(char *intf, int portid, uint8_t *mac);

> > > +

> > > +/**

> > > + * Display one forwarding database entry

> > > + *

> > > + * @param entry  Pointer to entry to display

> > > + */

> > > +void dump_fwd_db_entry(fwd_db_entry_t *entry);

> > > +

> > > +/**

> > > + * Display the forwarding database

> > > + */

> > > +void dump_fwd_db(void);

> > > +

> > > +/**

> > > + * Find a matching forwarding database entry

> > > + *

> > > + * @param key  ipv4 tuple

> > > + *

> > > + * @return pointer to forwarding DB entry else NULL

> > > + */

> > > +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key);

> > > +

> > > +#ifdef __cplusplus

> > > +}

> > > +#endif

> > > +

> > > +#endif

> > > diff --git a/example/l3fwd/odp_l3fwd_lpm.c

> > b/example/l3fwd/odp_l3fwd_lpm.c

> > > new file mode 100644

> > > index 0000000..1b3bfcf

> > > --- /dev/null

> > > +++ b/example/l3fwd/odp_l3fwd_lpm.c

> > > @@ -0,0 +1,224 @@

> > > +/* Copyright (c) 2016, Linaro Limited

> > > + * All rights reserved.

> > > + *

> > > + * SPDX-License-Identifier:     BSD-3-Clause

> > > + */

> > > +#ifndef _GNU_SOURCE

> > > +#define _GNU_SOURCE

> > > +#endif

> > > +

> > > +#include <stdio.h>

> > > +#include <stdlib.h>

> > > +

> > > +#include <example_debug.h>

> > > +#include <odp_api.h>

> > > +

> > > +#include <odp_l3fwd_lpm.h>

> > > +

> > > +/**

> > > + * This is a simple implementation of lpm based on patricia tree.

> > > + *

> > > + * Tradeoff exists between memory consumption and lookup time.

> > > + * Currently it prefers 5 levels: {16, 4, 4, 4, 4}, could be 3

> > > + * levels: {16, 8, 8} by defining FIB_NEXT_STRIDE as 8. Other

> > > + * levels are also possible.

> > > + *

> > > + * the ip here is host endian, when doing init or lookup, the

> > > + * caller should do endianness conversion if needed.

> > > + */

> > > +

> > > +#define FIB_IP_WIDTH 32

> > > +#define FIB_FIRST_STRIDE 16

> > > +#define FIB_NEXT_STRIDE 4

> > > +#define FIB_NEXT_SIZE (1 << FIB_NEXT_STRIDE)

> > > +#define FIB_SUB_COUNT 16384

> > > +#define DEPTH_TO_MASK(depth) ((1 << (depth)) - 1)

> > > +

> > > +typedef struct fib_node_s {

> > > +	union {

> > > +		uint32_t next_hop;

> > > +		struct fib_node_s *next; /* next level table */

> > > +	};

> > > +	uint8_t valid	:1; /* 1, this node has a valid next hop */

> > > +	uint8_t end	:1; /* 0, next points to the extended table */

> > > +	uint8_t depth	:6; /* bit length of subnet mask */

> > > +} fib_node_t;

> > > +

> > > +typedef struct fib_sub_tbl_s {

> > > +	fib_node_t *fib_nodes;

> > > +	uint32_t fib_count;

> > > +	uint32_t fib_idx;

> > > +} fib_sub_tbl_t;

> > > +

> > > +static fib_node_t fib_rt_tbl[1 << FIB_FIRST_STRIDE];

> > > +static fib_sub_tbl_t fib_lpm_cache;

> > > +

> > > +static inline fib_node_t *fib_alloc_sub(void)

> > > +{

> > > +	fib_node_t *sub_tbl = NULL;

> > > +	uint32_t i, nb_entry;

> > > +

> > > +	/* extend to next level */

> > > +	if (fib_lpm_cache.fib_idx < fib_lpm_cache.fib_count) {

> > > +		nb_entry = (fib_lpm_cache.fib_idx + 1) * FIB_NEXT_SIZE;

> > > +		sub_tbl = &fib_lpm_cache.fib_nodes[nb_entry];

> > > +		fib_lpm_cache.fib_idx++;

> > > +		for (i = 0; i < nb_entry; i++) {

> > > +			sub_tbl[i].valid = 0;

> > > +			sub_tbl[i].end = 1;

> > > +		}

> > > +	}

> > > +

> > > +	return sub_tbl;

> > > +}

> > > +

> > > +static void fib_update_node(fib_node_t *fe, int port, int depth)

> > > +{

> > > +	fib_node_t *p;

> > > +	int i;

> > > +

> > > +	if (fe->end) {

> > > +		if (!fe->valid) {

> > > +			fe->depth = depth;

> > > +			fe->next_hop = port;

> > > +			fe->valid = 1;

> > > +		} else if (fe->depth <= depth) {

> > > +			fe->next_hop = port;

> > > +			fe->depth = depth;

> > > +		}

> > > +

> > > +		return;

> > > +	}

> > > +

> > > +	for (i = 0; i < FIB_NEXT_SIZE; i++) {

> > > +		p = &fe->next[i];

> > > +		if (p->end)

> > > +			fib_update_node(p, port, depth);

> > > +	}

> > > +}

> > > +

> > > +static void fib_insert_node(fib_node_t *fe, uint32_t ip, uint32_t

> next_hop,

> > > +			    int ip_width, int eat_bits, int depth)

> > > +{

> > > +	int i;

> > > +	uint32_t idx, port;

> > > +	fib_node_t *p;

> > > +

> > > +	if (fe->end) {

> > > +		port = fe->next_hop;

> > > +		p = fib_alloc_sub();

> > > +		if (!p)

> > > +			return;

> > > +

> > > +		fe->next = p;

> > > +		fe->end = 0;

> > > +		if (fe->valid) {

> > > +			for (i = 0; i < FIB_NEXT_SIZE; i++) {

> > > +				p = &fe->next[i];

> > > +				p->next_hop = port;

> > > +				p->depth = fe->depth;

> > > +			}

> > > +		}

> > > +	}

> > > +	if (depth - eat_bits <= FIB_NEXT_STRIDE) {

> > > +		ip_width -= depth - eat_bits;

> > > +		idx = ip >> ip_width;

> > > +		ip &= DEPTH_TO_MASK(ip_width);

> > > +		p = &fe->next[idx];

> > > +		fib_update_node(p, next_hop, depth);

> > > +	} else {

> > > +		ip_width -= FIB_NEXT_STRIDE;

> > > +		idx = ip >> ip_width;

> > > +		p = &fe->next[idx];

> > > +		ip &= DEPTH_TO_MASK(ip_width);

> > > +		eat_bits += FIB_NEXT_STRIDE;

> > > +		fib_insert_node(p, ip, next_hop, ip_width, eat_bits, depth);

> > > +	}

> > > +}

> > > +

> > > +void fib_tbl_init(void)

> > > +{

> > > +	int i;

> > > +	fib_node_t *fe;

> > > +	uint32_t size;

> > > +	odp_shm_t lpm_shm;

> > > +

> > > +	for (i = 0; i < (1 << FIB_FIRST_STRIDE); i++) {

> > > +		fe = &fib_rt_tbl[i];

> > > +		fe->valid = 0;

> > > +		fe->end = 1;

> > > +		fe->depth = 0;

> > > +		fe->next_hop = 0;

> > > +	}

> > > +

> > > +	size = FIB_NEXT_SIZE * FIB_SUB_COUNT;

> > > +	/*Reserve memory for Routing hash table*/

> > > +	lpm_shm = odp_shm_reserve("fib_lpm_sub", size,

> > ODP_CACHE_LINE_SIZE, 0);

> > > +	fe = odp_shm_addr(lpm_shm);

> > > +	if (!fe) {

> > > +		EXAMPLE_ERR("Error: shared mem alloc failed for lpm

> > cache.\n");

> > > +		exit(-1);

> > > +	}

> > > +

> > > +	fib_lpm_cache.fib_nodes = fe;

> > > +	fib_lpm_cache.fib_count = FIB_SUB_COUNT;

> > > +	fib_lpm_cache.fib_idx = 0;

> > > +}

> > > +

> > > +void fib_tbl_insert(uint32_t ip, int port, int depth)

> > > +{

> > > +	fib_node_t *fe, *p;

> > > +	uint32_t idx;

> > > +	int i, j;

> > > +	int nb_bits;

> > > +

> > > +	nb_bits = FIB_FIRST_STRIDE;

> > > +	idx = ip >> nb_bits;

> > > +	fe = &fib_rt_tbl[idx];

> > > +	if (depth <= nb_bits) {

> > > +		if (fe->end) {

> > > +			fe->next_hop = port;

> > > +			fe->depth = depth;

> > > +			fe->valid = 1;

> > > +			return;

> > > +		}

> > > +

> > > +		for (i = 0; i < FIB_NEXT_SIZE; i++) {

> > > +			p = &fe->next[i];

> > > +			if (p->end)

> > > +				fib_update_node(p, port, depth);

> > > +			else

> > > +				for (j = 0; j < FIB_NEXT_SIZE; j++)

> > > +					fib_update_node(&p->next[j], port,

> > > +							depth);

> > > +		}

> > > +

> > > +		return;

> > > +	}

> > > +

> > > +	/* need to check sub table */

> > > +	ip &= DEPTH_TO_MASK(FIB_IP_WIDTH - nb_bits);

> > > +	fib_insert_node(fe, ip, port, FIB_IP_WIDTH - nb_bits, nb_bits, depth);

> > > +}

> > > +

> > > +int fib_tbl_lookup(uint32_t ip, int *port)

> > > +{

> > > +	fib_node_t *fe;

> > > +	uint32_t idx;

> > > +	int nb_bits;

> > > +

> > > +	nb_bits = FIB_IP_WIDTH - FIB_FIRST_STRIDE;

> > > +	idx = ip >> nb_bits;

> > > +	fe = &fib_rt_tbl[idx];

> > > +

> > > +	ip &= DEPTH_TO_MASK(nb_bits);

> > > +	while (!fe->end) {

> > > +		nb_bits -= FIB_NEXT_STRIDE;

> > > +		idx = ip >> nb_bits;

> > > +		fe = &fe->next[idx];

> > > +		ip &= DEPTH_TO_MASK(nb_bits);

> > > +	}

> > > +	*port = fe->next_hop;

> > > +

> > > +	return fe->valid ? 0 : -1;

> > > +}

> > > diff --git a/example/l3fwd/odp_l3fwd_lpm.h

> > b/example/l3fwd/odp_l3fwd_lpm.h

> > > new file mode 100644

> > > index 0000000..8f78e39

> > > --- /dev/null

> > > +++ b/example/l3fwd/odp_l3fwd_lpm.h

> > > @@ -0,0 +1,20 @@

> > > +/* Copyright (c) 2016, Linaro Limited

> > > + * All rights reserved.

> > > + *

> > > + * SPDX-License-Identifier:     BSD-3-Clause

> > > + */

> > > +

> > > +#ifndef ODP_L3FWD_LPM_H_

> > > +#define ODP_L3FWD_LPM_H_

> > it has to be _ODP_EXAMPLE_L3FWD_LPM_H

> >

> > i.e. define should be started with "_" meaning that it's not odp api.

> > (I know that in some places we use _ODP_ in other places we use ODP_ for

> > that

> > case. But that is wrong. We should not overlap with api name space.).

> >

> > > +

> > > +#ifdef __cplusplus

> > > +extern "C" {

> > > +#endif

> > > +void fib_tbl_init(void);

> > > +void fib_tbl_insert(uint32_t ip, int port, int depth);

> > > +int fib_tbl_lookup(uint32_t ip, int *port);

> > > +#ifdef __cplusplus

> > > +}

> > > +#endif

> > > +

> > > +#endif

> > > diff --git a/example/m4/configure.m4 b/example/m4/configure.m4

> > > index bbda38f..78ef396 100644

> > > --- a/example/m4/configure.m4

> > > +++ b/example/m4/configure.m4

> > > @@ -19,5 +19,6 @@ AC_CONFIG_FILES([example/classifier/Makefile

> > >   		 example/timer/Makefile

> > >   		 example/traffic_mgmt/Makefile

> > >   		 example/l2fwd_simple/Makefile

> > > +		 example/l3fwd/Makefile

> > >   		 example/switch/Makefile

> > >   		 example/hello/Makefile])

> >

> > On thought is what we need with l2fwd. We places it under

> > test/performance/ but it's actual example app.

> > Not sure that it's clear for new users. Definitely l2fwd and l3fwd have

> > to be at the same level. I'm thinking

> > about placing both to test/performance/ and create symlink with some

> > README to example/. But I'm not sure

> > about that, we need to discuss.

> 

> Yes.  In my opinion, we do not need a test/performance directory.

> If want to benchmarking automatically, we can build all the apps but run

only
> interested one.

> 

> > Maxim.

> >

>
Forrest Shi July 25, 2016, 8:06 a.m. UTC | #4
Hi Matias,

Do you have any further comment about this version?

Thanks,
Forrest

> -----Original Message-----

> From: forrest.shi@linaro.org [mailto:forrest.shi@linaro.org]

> Sent: Wednesday, July 13, 2016 15:30

> To: matias.elo@nokia-bell-labs.com

> Cc: lng-odp@lists.linaro.org; Xuelin Shi <forrest.shi@linaro.org>

> Subject: [lng-odp][PATCH 1/2 v6] example: introducing l3fwd

> 

> From: Xuelin Shi <forrest.shi@linaro.org>

> 

> multi-thread, multi-queues and bi-directional forwarding.

> 

> support (port, queue, thread) arguments in cmdline which specify how the

> threads handle which rx queue at which port, if no this argument, default

> specification used.

> 

> both hash and lpm based lookup methods are supported, default lpm.

> 

> Signed-off-by: Xuelin Shi <forrest.shi@linaro.org>

> ---

>  example/Makefile.am           |    2 +-

>  example/l3fwd/.gitignore      |    4 +

>  example/l3fwd/Makefile.am     |   18 +

>  example/l3fwd/odp_l3fwd.c     | 1072

> +++++++++++++++++++++++++++++++++++++++++

>  example/l3fwd/odp_l3fwd_db.c  |  408 ++++++++++++++++

> example/l3fwd/odp_l3fwd_db.h  |  130 +++++

> example/l3fwd/odp_l3fwd_lpm.c |  224 +++++++++

>  example/l3fwd/odp_l3fwd_lpm.h |   20 +

>  example/m4/configure.m4       |    1 +

>  9 files changed, 1878 insertions(+), 1 deletion(-)  create mode 100644

> example/l3fwd/.gitignore  create mode 100644 example/l3fwd/Makefile.am

> create mode 100644 example/l3fwd/odp_l3fwd.c  create mode 100644

> example/l3fwd/odp_l3fwd_db.c  create mode 100644

> example/l3fwd/odp_l3fwd_db.h  create mode 100644

> example/l3fwd/odp_l3fwd_lpm.c  create mode 100644

> example/l3fwd/odp_l3fwd_lpm.h

> 

> diff --git a/example/Makefile.am b/example/Makefile.am index

> 37542af..1f1b62e 100644

> --- a/example/Makefile.am

> +++ b/example/Makefile.am

> @@ -1 +1 @@

> -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

> l2fwd_simple switch hello

> +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

> +l2fwd_simple switch hello l3fwd

> diff --git a/example/l3fwd/.gitignore b/example/l3fwd/.gitignore new file

> mode 100644 index 0000000..4a25ae8

> --- /dev/null

> +++ b/example/l3fwd/.gitignore

> @@ -0,0 +1,4 @@

> +odp_l3fwd

> +Makefile

> +Makefile.in

> +*.o

> diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am new

> file mode 100644 index 0000000..5092aa7

> --- /dev/null

> +++ b/example/l3fwd/Makefile.am

> @@ -0,0 +1,18 @@

> +include $(top_srcdir)/example/Makefile.inc

> +

> +bin_PROGRAMS = odp_l3fwd$(EXEEXT)

> +odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static odp_l3fwd_CFLAGS =

> +$(AM_CFLAGS) -I${top_srcdir}/example -I${top_srcdir}/test

> +

> +noinst_HEADERS = \

> +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \

> +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_lpm.h \

> +		  $(top_srcdir)/example/example_debug.h

> +

> +dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c odp_l3fwd_lpm.c

> +

> +if test_example

> +TESTS = odp_l3fwd_run.sh

> +endif

> +

> +EXTRA_DIST = odp_l3fwd_run.sh

> diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c new

> file mode 100644 index 0000000..a10ca76

> --- /dev/null

> +++ b/example/l3fwd/odp_l3fwd.c

> @@ -0,0 +1,1072 @@

> +/* Copyright (c) 2016, Linaro Limited

> + * All rights reserved.

> + *

> + * SPDX-License-Identifier:     BSD-3-Clause

> + */

> +

> +#include <stdlib.h>

> +#include <stdio.h>

> +#include <errno.h>

> +#include <getopt.h>

> +#include <unistd.h>

> +#include <inttypes.h>

> +

> +#include <test_debug.h>

> +

> +#include <odp_api.h>

> +#include <odp/helper/linux.h>

> +#include <odp/helper/eth.h>

> +#include <odp/helper/ip.h>

> +#include <odp/helper/udp.h>

> +#include <odp/helper/tcp.h>

> +

> +#include "odp_l3fwd_db.h"

> +#include "odp_l3fwd_lpm.h"

> +

> +#define POOL_NUM_PKT	8192

> +#define POOL_SEG_LEN	1856

> +#define MAX_PKT_BURST	32

> +

> +#define MAX_NB_WORKER	32

> +#define MAX_NB_PKTIO	32

> +#define MAX_NB_QUEUE	32

> +#define MAX_NB_QCONFS	1024

> +#define MAX_NB_ROUTE	32

> +

> +#define INVALID_ID	(-1)

> +#define PRINT_INTERVAL	10	/* interval seconds of printing stats

*/
> +

> +/** Get rid of path in filename - only for unix-type paths using '/' */

> +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \

> +			    strrchr((file_name), '/') + 1 : (file_name))

> +

> +struct l3fwd_pktio_s {

> +	odp_pktio_t pktio;

> +	odph_ethaddr_t mac_addr;

> +	odp_pktin_queue_t ifin[MAX_NB_QUEUE];

> +	odp_pktout_queue_t ifout[MAX_NB_QUEUE];

> +	int nb_rxq;

> +	int nb_txq;

> +	int rxq_idx;

> +	int txq_idx;

> +};

> +

> +struct l3fwd_qconf_s {

> +	uint8_t if_idx;		/* port index */

> +	uint8_t rxq_idx;	/* recv queue index in a port */

> +	uint8_t core_idx;	/* this core should handle traffic */

> +};

> +

> +struct thread_arg_s {

> +	uint64_t packets;

> +	uint64_t rx_drops;

> +	uint64_t tx_drops;

> +	struct {

> +		int used;

> +		int rxq[MAX_NB_QUEUE];

> +		int txq[MAX_NB_QUEUE];

> +	} pktio[MAX_NB_PKTIO];

> +	int thr_idx;

> +	int nb_pktio;

> +};

> +

> +typedef struct {

> +	char *if_names[MAX_NB_PKTIO];

> +	int if_count;

> +	char *route_str[MAX_NB_ROUTE];

> +	int worker_count;

> +	struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS];

> +	int qconf_count;

> +	uint32_t duration; /* seconds to run */

> +	uint8_t hash_mode; /* 1:hash, 0:lpm */

> +	uint8_t dest_mac_changed[MAX_NB_PKTIO]; /* 1: dest mac from

> cmdline */

> +} app_args_t;

> +

> +struct {

> +	app_args_t		cmd_args;

> +	struct l3fwd_pktio_s	l3fwd_pktios[MAX_NB_PKTIO];

> +	odph_odpthread_t	l3fwd_workers[MAX_NB_WORKER];

> +	struct thread_arg_s	worker_args[MAX_NB_WORKER];

> +	odph_ethaddr_t		eth_dest_mac[MAX_NB_PKTIO];

> +

> +	/* forward func, hash or lpm */

> +	int (*fwd_func)(odp_packet_t pkt, int sif); } global;

> +

> +/** Global barrier to synchronize main and workers */ static

> +odp_barrier_t barrier;

> +static int exit_threads;	/**< Break workers loop if set to 1 */

> +

> +static void print_usage(char *progname); static void

> +print_qconf_table(app_args_t *args); static void print_info(char

> +*progname, app_args_t *args); static int print_speed_stats(int

> +num_workers, int duration, int timeout); static void

> +parse_cmdline_args(int argc, char *argv[], app_args_t *args); static

> +int parse_config(char *cfg_str, app_args_t *args); static void

> +setup_worker_qconf(app_args_t *args); static void setup_fwd_db(void);

> +static int find_port_id_by_name(char *name, app_args_t *args); static

> +int split_string(char *str, int stringlen,

> +			char **tokens, int maxtokens, char delim);

> +

> +static int create_pktio(const char *name, odp_pool_t pool,

> +			struct l3fwd_pktio_s *fwd_pktio)

> +{

> +	odp_pktio_param_t pktio_param;

> +	odp_pktio_t pktio;

> +	odp_pktio_capability_t capa;

> +	int rc;

> +

> +	odp_pktio_param_init(&pktio_param);

> +

> +	pktio = odp_pktio_open(name, pool, &pktio_param);

> +	if (pktio == ODP_PKTIO_INVALID) {

> +		printf("Failed to open %s\n", name);

> +		return -1;

> +	}

> +	fwd_pktio->pktio = pktio;

> +

> +	rc = odp_pktio_capability(pktio, &capa);

> +	if (rc) {

> +		printf("Error: pktio %s: unable to read capabilities!\n",

> +		       name);

> +

> +		return -1;

> +	}

> +

> +	fwd_pktio->nb_rxq = (int)capa.max_input_queues;

> +	fwd_pktio->nb_txq = (int)capa.max_output_queues;

> +

> +	if (fwd_pktio->nb_rxq > MAX_NB_QUEUE)

> +		fwd_pktio->nb_rxq = MAX_NB_QUEUE;

> +

> +	if (fwd_pktio->nb_txq > MAX_NB_QUEUE)

> +		fwd_pktio->nb_txq = MAX_NB_QUEUE;

> +

> +	return 0;

> +}

> +

> +static void setup_fwd_db(void)

> +{

> +	fwd_db_entry_t *entry;

> +	int if_idx;

> +	app_args_t *args;

> +

> +	args = &global.cmd_args;

> +	if (args->hash_mode)

> +		init_fwd_hash_cache();

> +	else

> +		fib_tbl_init();

> +

> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> +		if_idx = entry->oif_id;

> +		if (!args->hash_mode)

> +			fib_tbl_insert(entry->subnet.addr, if_idx,

> +				       entry->subnet.depth);

> +		if (args->dest_mac_changed[if_idx])

> +			global.eth_dest_mac[if_idx] = entry->dst_mac;

> +		else

> +			entry->dst_mac = global.eth_dest_mac[if_idx];

> +	}

> +}

> +

> +static int l3fwd_pkt_hash(odp_packet_t pkt, int sif) {

> +	fwd_db_entry_t *entry;

> +	ipv4_tuple5_t key;

> +	odph_ethhdr_t *eth;

> +	odph_udphdr_t  *udp;

> +	odph_ipv4hdr_t *ip;

> +	uint32_t len;

> +	int dif;

> +

> +	ip = odp_packet_l3_ptr(pkt, &len);

> +	key.dst_ip = odp_be_to_cpu_32(ip->dst_addr);

> +	key.src_ip = odp_be_to_cpu_32(ip->src_addr);

> +	key.proto = ip->proto;

> +

> +	if (odp_packet_has_udp(pkt) ||

> +	    odp_packet_has_tcp(pkt)) {

> +		/* UDP or TCP*/

> +		void *ptr = odp_packet_l4_ptr(pkt, NULL);

> +

> +		udp = (odph_udphdr_t *)ptr;

> +		key.src_port = odp_be_to_cpu_16(udp->src_port);

> +		key.dst_port = odp_be_to_cpu_16(udp->dst_port);

> +	} else {

> +		key.src_port = 0;

> +		key.dst_port = 0;

> +	}

> +

> +	entry = find_fwd_db_entry(&key);

> +	ip->ttl--;

> +	ip->chksum = odph_ipv4_csum_update(pkt);

> +	eth = odp_packet_l2_ptr(pkt, NULL);

> +	if (entry) {

> +		eth->src = entry->src_mac;

> +		eth->dst = entry->dst_mac;

> +		dif = entry->oif_id;

> +	} else {

> +		/* no route, send by src port */

> +		eth->dst = eth->src;

> +		dif = sif;

> +	}

> +

> +	return dif;

> +}

> +

> +static int l3fwd_pkt_lpm(odp_packet_t pkt, int sif) {

> +	odph_ipv4hdr_t *ip;

> +	odph_ethhdr_t *eth;

> +	uint32_t len;

> +	int dif;

> +	int ret;

> +

> +	ip = odp_packet_l3_ptr(pkt, &len);

> +	ip->ttl--;

> +	ip->chksum = odph_ipv4_csum_update(pkt);

> +	eth = odp_packet_l2_ptr(pkt, NULL);

> +

> +	/* network byte order maybe different from host */

> +	ret = fib_tbl_lookup(odp_be_to_cpu_32(ip->dst_addr), &dif);

> +	if (ret)

> +		dif = sif;

> +

> +	eth->dst = global.eth_dest_mac[dif];

> +	eth->src = global.l3fwd_pktios[dif].mac_addr;

> +

> +	return dif;

> +}

> +

> +/**

> + * Drop packets which input parsing marked as containing errors.

> + *

> + * Frees packets with error and modifies pkt_tbl[] to only contain

> +packets with

> + * no detected errors.

> + *

> + * @param pkt_tbl  Array of packets

> + * @param num      Number of packets in pkt_tbl[]

> + *

> + * @return Number of packets dropped

> + */

> +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num) {

> +	odp_packet_t pkt;

> +	unsigned dropped = 0;

> +	unsigned i, j;

> +

> +	for (i = 0, j = 0; i < num; ++i) {

> +		pkt = pkt_tbl[i];

> +

> +		if (odp_unlikely(odp_packet_has_error(pkt) ||

> +				 !odp_packet_has_ipv4(pkt))) {

> +			odp_packet_free(pkt);

> +			dropped++;

> +		} else if (odp_unlikely(i != j++)) {

> +			pkt_tbl[j - 1] = pkt;

> +		}

> +	}

> +

> +	return dropped;

> +}

> +

> +static void l3fwd_one_queue(uint32_t sif, uint8_t rxq_id, void

> +*thr_arg) {

> +	struct l3fwd_pktio_s *port;

> +	odp_packet_t *tbl;

> +	odp_pktout_queue_t outq;

> +	odp_packet_t pkt_tbl[MAX_PKT_BURST];

> +	struct thread_arg_s *arg;

> +	uint8_t txq_id;

> +	int pkts, drop, sent;

> +	int dif, dst_port;

> +	int i;

> +

> +	arg = thr_arg;

> +	port = &global.l3fwd_pktios[sif];

> +	pkts = odp_pktin_recv(port->ifin[rxq_id], pkt_tbl, MAX_PKT_BURST);

> +	if (pkts <= 0)

> +		return;

> +	arg->packets += pkts;

> +	drop = drop_err_pkts(pkt_tbl, pkts);

> +	pkts -= drop;

> +	arg->rx_drops += drop;

> +

> +	dif = global.fwd_func(pkt_tbl[0], sif);

> +	tbl = &pkt_tbl[0];

> +	while (pkts) {

> +		dst_port = dif;

> +		for (i = 1; i < pkts; i++) {

> +			dif = global.fwd_func(tbl[i], sif);

> +			if (dif != dst_port)

> +				break;

> +		}

> +		txq_id = arg->pktio[dst_port].txq[rxq_id];

> +		outq = global.l3fwd_pktios[dst_port].ifout[txq_id];

> +		sent = odp_pktout_send(outq, tbl, i);

> +		if (odp_unlikely(sent < i)) {

> +			sent = sent < 0 ? 0 : sent;

> +			odp_packet_free_multi(&tbl[sent], i - sent);

> +			arg->tx_drops += i - sent;

> +		}

> +

> +		if (i < pkts)

> +			tbl += i;

> +

> +		pkts -= i;

> +	}

> +}

> +

> +static int run_worker(void *arg)

> +{

> +	int if_idx, rxq_idx;

> +	struct thread_arg_s *thr_arg = arg;

> +	struct l3fwd_pktio_s *port;

> +

> +	odp_barrier_wait(&barrier);

> +

> +	while (!exit_threads) {

> +		for (if_idx = 0; if_idx < thr_arg->nb_pktio; if_idx++) {

> +			if (!thr_arg->pktio[if_idx].used ||

> +			    thr_arg->thr_idx == INVALID_ID)

> +				continue;

> +

> +			port = &global.l3fwd_pktios[if_idx];

> +			for (rxq_idx = 0; rxq_idx < port->rxq_idx; rxq_idx++)

> +				l3fwd_one_queue(if_idx, rxq_idx, arg);

> +		}

> +	}

> +

> +	/* Make sure that latest stat writes are visible to other threads */

> +	odp_mb_full();

> +

> +	return 0;

> +}

> +

> +static int find_port_id_by_name(char *name, app_args_t *args) {

> +	int i;

> +

> +	if (!name)

> +		return -1;

> +

> +	for (i = 0; i < args->if_count; i++) {

> +		if (!strcmp(name, args->if_names[i]))

> +			return i;

> +	}

> +

> +	return -1;

> +}

> +

> +int main(int argc, char **argv)

> +{

> +	odph_odpthread_t thread_tbl[MAX_NB_WORKER];

> +	odp_pool_t pool;

> +	odp_pool_param_t params;

> +	odp_instance_t instance;

> +	odph_odpthread_params_t thr_params;

> +	odp_cpumask_t cpumask;

> +	int cpu, i, j, nb_worker;

> +	uint8_t mac[ODPH_ETHADDR_LEN];

> +	app_args_t *args;

> +	struct thread_arg_s *thr_arg;

> +	char *oif;

> +	int oid;

> +

> +	if (odp_init_global(&instance, NULL, NULL)) {

> +		printf("Error: ODP global init failed.\n");

> +		exit(1);

> +	}

> +

> +	if (odp_init_local(instance, ODP_THREAD_CONTROL)) {

> +		printf("Error: ODP local init failed.\n");

> +		exit(1);

> +	}

> +

> +	/* Clear global argument and initialize the dest mac as 2:0:0:0:0:x */

> +	memset(&global, 0, sizeof(global));

> +	mac[0] = 2;

> +	for (i = 0; i < MAX_NB_PKTIO; i++) {

> +		mac[ODPH_ETHADDR_LEN - 1] = (uint8_t)i;

> +		memcpy(global.eth_dest_mac[i].addr, mac,

> ODPH_ETHADDR_LEN);

> +	}

> +

> +	/* Initialize the thread arguments */

> +	for (i = 0; i < MAX_NB_WORKER; i++) {

> +		thr_arg = &global.worker_args[i];

> +		for (j = 0; j < MAX_NB_PKTIO; j++) {

> +			thr_arg->thr_idx = INVALID_ID;

> +			memset(thr_arg->pktio[j].rxq, INVALID_ID,

> +			       sizeof(thr_arg->pktio[j].rxq));

> +		}

> +	}

> +

> +	/* Parse cmdline arguments */

> +	args = &global.cmd_args;

> +	parse_cmdline_args(argc, argv, args);

> +

> +	/* Init l3fwd table */

> +	init_fwd_db();

> +

> +	/* Add route into table */

> +	for (i = 0; i < MAX_NB_ROUTE; i++) {

> +		if (args->route_str[i]) {

> +			oif = NULL;

> +			create_fwd_db_entry(args->route_str[i], &oif);

> +			oid = find_port_id_by_name(oif, args);

> +			if (oid != -1)

> +				args->dest_mac_changed[oid] = 1;

> +		}

> +	}

> +

> +	print_info(NO_PATH(argv[0]), args);

> +

> +	/* Create packet pool */

> +	odp_pool_param_init(&params);

> +	params.pkt.seg_len = POOL_SEG_LEN;

> +	params.pkt.len     = POOL_SEG_LEN;

> +	params.pkt.num     = POOL_NUM_PKT;

> +	params.type        = ODP_POOL_PACKET;

> +

> +	pool = odp_pool_create("packet pool", &params);

> +

> +	if (pool == ODP_POOL_INVALID) {

> +		printf("Error: packet pool create failed.\n");

> +		exit(1);

> +	}

> +

> +	/* Resolve fwd db*/

> +	for (i = 0; i < args->if_count; i++) {

> +		struct l3fwd_pktio_s *port;

> +		char *if_name;

> +

> +		if_name = args->if_names[i];

> +		port = &global.l3fwd_pktios[i];

> +		if (create_pktio(if_name, pool, port)) {

> +			printf("Error: create pktio %s\n", if_name);

> +			exit(1);

> +		}

> +		odp_pktio_mac_addr(port->pktio, mac, ODPH_ETHADDR_LEN);

> +		resolve_fwd_db(if_name, i, mac);

> +		memcpy(port->mac_addr.addr, mac, ODPH_ETHADDR_LEN);

> +	}

> +

> +	setup_fwd_db();

> +	dump_fwd_db();

> +

> +	/* Dicide available workers */

> +	nb_worker = MAX_NB_WORKER;

> +	if (args->worker_count)

> +		nb_worker = args->worker_count;

> +	nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker);

> +	args->worker_count = nb_worker;

> +

> +	/* Setup rx and tx queues for each port */

> +	setup_worker_qconf(args);

> +	print_qconf_table(args);

> +

> +	/* Decide ip lookup method */

> +	if (args->hash_mode)

> +		global.fwd_func = l3fwd_pkt_hash;

> +	else

> +		global.fwd_func = l3fwd_pkt_lpm;

> +

> +	/* Start all the available ports */

> +	for (i = 0; i < args->if_count; i++) {

> +		struct l3fwd_pktio_s *port;

> +		char *if_name;

> +		char buf[32];

> +

> +		if_name = args->if_names[i];

> +		port = &global.l3fwd_pktios[i];

> +		/* start pktio */

> +		if (odp_pktio_start(port->pktio)) {

> +			printf("unable to start pktio: %s\n", if_name);

> +			exit(1);

> +		}

> +

> +		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",

> +			port->mac_addr.addr[0],

> +			port->mac_addr.addr[1],

> +			port->mac_addr.addr[2],

> +			port->mac_addr.addr[3],

> +			port->mac_addr.addr[4],

> +			port->mac_addr.addr[5]);

> +		printf("start pktio: %s, mac %s\n", if_name, buf);

> +	}

> +

> +	odp_barrier_init(&barrier, nb_worker + 1);

> +

> +	memset(&thr_params, 0, sizeof(thr_params));

> +	thr_params.start    = run_worker;

> +	thr_params.thr_type = ODP_THREAD_WORKER;

> +	thr_params.instance = instance;

> +

> +	memset(thread_tbl, 0, sizeof(thread_tbl));

> +	cpu = odp_cpumask_first(&cpumask);

> +	for (i = 0; i < nb_worker; i++) {

> +		struct thread_arg_s *arg;

> +		odp_cpumask_t thr_mask;

> +

> +		arg = &global.worker_args[i];

> +		arg->nb_pktio = args->if_count;

> +		odp_cpumask_zero(&thr_mask);

> +		odp_cpumask_set(&thr_mask, cpu);

> +		thr_params.arg = arg;

> +		odph_odpthreads_create(&thread_tbl[i], &thr_mask,

> +				       &thr_params);

> +		cpu = odp_cpumask_next(&cpumask, cpu);

> +	}

> +

> +	if (args->duration) {

> +		print_speed_stats(nb_worker, args->duration,

> PRINT_INTERVAL);

> +		exit_threads = 1;

> +	}

> +

> +	/* wait for other threads to join */

> +	for (i = 0; i < nb_worker; i++)

> +		odph_odpthreads_join(&thread_tbl[i]);

> +

> +	return 0;

> +}

> +

> +static void print_usage(char *progname) {

> +	printf("\n"

> +	       "ODP L3 forwarding application.\n"

> +	       "\n"

> +	       "Usage: %s OPTIONS\n"

> +	       "  E.g. %s -i eth0,eth1 -r 1.1.1.0/24,eth0 -r

2.2.2.0/24,eth1\n"
> +	       " In the above example,\n"

> +	       " eth0 will send pkts to eth1 and vice versa\n"

> +	       "\n"

> +	       "Mandatory OPTIONS:\n"

> +	       "  -i, --interface eth interfaces (comma-separated, no

spaces)\n"
> +	       "  -r, --route SubNet,Intf[,NextHopMAC]\n"

> +	       "	NextHopMAC can be optional\n"

> +	       "\n"

> +	       "Optional OPTIONS:\n"

> +	       "  -s, --style [lpm|hash], ip lookup method\n"

> +	       "	optional, default as lpm\n"

> +	       "  -d, --duration Seconds to run and print stats\n"

> +	       "	optional, default as 0, run forever\n"

> +	       "  -t, --thread Number of threads to do forwarding\n"

> +	       "	optional, default as availbe worker cpu count\n"

> +	       "  -q, --queue  Configure rx queue(s) for port\n"

> +	       "	optional, format: [(port, queue, thread),...]\n"

> +	       "	for example: -q '(0, 0, 1),(1,0,2)'\n"

> +	       "  -h, --help   Display help and exit.\n\n"

> +	       "\n", NO_PATH(progname), NO_PATH(progname)

> +	    );

> +}

> +

> +static void parse_cmdline_args(int argc, char *argv[], app_args_t

> +*args) {

> +	int opt;

> +	int long_index;

> +	char *token, *local;

> +	size_t len, route_index = 0;

> +	int i, mem_failure = 0;

> +

> +	static struct option longopts[] = {

> +		{"interface", required_argument, NULL, 'i'},	/* return 'i'

*/
> +		{"route", required_argument, NULL, 'r'},	/* return 'r'

*/
> +		{"style", optional_argument, NULL, 's'},	/* return 's'

*/
> +		{"duration", optional_argument, NULL, 'd'},	/* return 'd'

*/
> +		{"thread", optional_argument, NULL, 't'},	/* return 't'

*/
> +		{"queue", optional_argument, NULL, 'q'},	/* return 'q'

*/
> +		{"help", no_argument, NULL, 'h'},		/* return 'h'

*/
> +		{NULL, 0, NULL, 0}

> +	};

> +

> +	while (1) {

> +		opt = getopt_long(argc, argv, "+s:t:d:i:r:q:h",

> +				  longopts, &long_index);

> +

> +		if (opt == -1)

> +			break;	/* No more options */

> +

> +		switch (opt) {

> +		/* parse ip lookup method */

> +		case 's':

> +			if (!strcmp(optarg, "hash"))

> +				args->hash_mode = 1;

> +			break;

> +		/* parse number of worker threads to be run*/

> +		case 't':

> +			i = odp_cpu_count();

> +			args->worker_count = atoi(optarg);

> +			if (args->worker_count > i) {

> +				printf("Too many threads,"

> +				       "truncate to cpu count: %d\n", i);

> +				args->worker_count = i;

> +			}

> +

> +			break;

> +

> +		/* parse seconds to run */

> +		case 'd':

> +			args->duration = atoi(optarg);

> +			break;

> +

> +		/* parse packet-io interface names */

> +		case 'i':

> +			len = strlen(optarg);

> +			if (len == 0) {

> +				print_usage(argv[0]);

> +				exit(EXIT_FAILURE);

> +			}

> +			len += 1;	/* add room for '\0' */

> +

> +			local = malloc(len);

> +			if (!local) {

> +				print_usage(argv[0]);

> +				exit(EXIT_FAILURE);

> +			}

> +

> +			/* count the number of tokens separated by ',' */

> +			strcpy(local, optarg);

> +			for (token = strtok(local, ","), i = 0;

> +			     token != NULL;

> +			     token = strtok(NULL, ","), i++)

> +				;

> +

> +			if (i == 0) {

> +				print_usage(argv[0]);

> +				exit(EXIT_FAILURE);

> +			} else if (i > MAX_NB_PKTIO) {

> +				printf("too many ports specified, "

> +				       "truncated to %d", MAX_NB_PKTIO);

> +			}

> +			args->if_count = i;

> +

> +			/* store the if names (reset names string) */

> +			strcpy(local, optarg);

> +			for (token = strtok(local, ","), i = 0;

> +			     token != NULL; token = strtok(NULL, ","), i++) {

> +				args->if_names[i] = token;

> +			}

> +			break;

> +

> +		/*Configure Route in forwarding database*/

> +		case 'r':

> +			if (route_index >= MAX_NB_ROUTE) {

> +				printf("No more routes can be added\n");

> +				break;

> +			}

> +			local = calloc(1, strlen(optarg) + 1);

> +			if (!local) {

> +				mem_failure = 1;

> +				break;

> +			}

> +			memcpy(local, optarg, strlen(optarg));

> +			local[strlen(optarg)] = '\0';

> +			args->route_str[route_index++] = local;

> +			break;

> +

> +		case 'h':

> +			print_usage(argv[0]);

> +			exit(EXIT_SUCCESS);

> +			break;

> +

> +		case 'q':

> +			parse_config(optarg, args);

> +			break;

> +

> +		default:

> +			break;

> +		}

> +	}

> +

> +	/* checking arguments */

> +	if (args->if_count == 0) {

> +		printf("\nNo option -i specified.\n");

> +		goto out;

> +	}

> +

> +	if (args->route_str[0] == NULL) {

> +		printf("\nNo option -r specified.\n");

> +		goto out;

> +	}

> +

> +	if (mem_failure == 1) {

> +		printf("\nAllocate memory failure.\n");

> +		goto out;

> +	}

> +	optind = 1;		/* reset 'extern optind' from the getopt lib

*/
> +	return;

> +

> +out:

> +	print_usage(argv[0]);

> +	exit(EXIT_FAILURE);

> +}

> +

> +/* split string into tokens */

> +int split_string(char *str, int stringlen,

> +		 char **tokens, int maxtokens, char delim) {

> +	int i, tok = 0;

> +	int tokstart = 1; /* first token is right at start of string */

> +

> +	if (str == NULL || tokens == NULL)

> +		goto einval_error;

> +

> +	for (i = 0; i < stringlen; i++) {

> +		if (str[i] == '\0' || tok >= maxtokens)

> +			break;

> +		if (tokstart) {

> +			tokstart = 0;

> +			tokens[tok++] = &str[i];

> +		}

> +		if (str[i] == delim) {

> +			str[i] = '\0';

> +			tokstart = 1;

> +		}

> +	}

> +	return tok;

> +

> +einval_error:

> +	errno = EINVAL;

> +	return -1;

> +}

> +

> +static int parse_config(char *cfg_str, app_args_t *args) {

> +	char s[256];

> +	const char *p, *p0 = cfg_str;

> +	char *end;

> +	enum fieldnames {

> +		FLD_PORT = 0,

> +		FLD_QUEUE,

> +		FLD_LCORE,

> +		FLD_LAST

> +	};

> +	unsigned long int_fld[FLD_LAST];

> +	char *str_fld[FLD_LAST];

> +	int i;

> +	unsigned size;

> +	int nb_qconfs = 0;

> +	struct l3fwd_qconf_s *qconf_array = &args->qconf_config[0];

> +

> +	p = strchr(p0, '(');

> +	while (p != NULL) {

> +		++p;

> +		p0 = strchr(p, ')');

> +		if (p0 == NULL)

> +			return -1;

> +

> +		size = p0 - p;

> +		if (size >= sizeof(s))

> +			return -1;

> +

> +		snprintf(s, sizeof(s), "%.*s", size, p);

> +		i = split_string(s, sizeof(s), str_fld, FLD_LAST, ',');

> +		if (i != FLD_LAST)

> +			return -1;

> +		for (i = 0; i < FLD_LAST; i++) {

> +			errno = 0;

> +			int_fld[i] = strtoul(str_fld[i], &end, 0);

> +			if (errno != 0 || end == str_fld[i] || int_fld[i] >

255)
> +				return -1;

> +		}

> +		if (nb_qconfs >= MAX_NB_QCONFS) {

> +			printf("exceeded max number of queue

> params: %hu\n",

> +			       nb_qconfs);

> +			return -1;

> +		}

> +		qconf_array[nb_qconfs].if_idx = (uint8_t)int_fld[FLD_PORT];

> +		qconf_array[nb_qconfs].rxq_idx =

> (uint8_t)int_fld[FLD_QUEUE];

> +		qconf_array[nb_qconfs].core_idx =

> (uint8_t)int_fld[FLD_LCORE];

> +		++nb_qconfs;

> +

> +		p = strchr(p0, '(');

> +	}

> +	args->qconf_count = nb_qconfs;

> +

> +	return 0;

> +}

> +

> +static void print_info(char *progname, app_args_t *args) {

> +	int i;

> +

> +	printf("\n"

> +	       "ODP system info\n"

> +	       "---------------\n"

> +	       "ODP API version: %s\n"

> +	       "ODP impl name:	 %s\n"

> +	       "CPU model:       %s\n"

> +	       "CPU freq (hz):   %" PRIu64 "\n"

> +	       "Cache line size: %i\n"

> +	       "CPU count:       %i\n"

> +	       "\n",

> +	       odp_version_api_str(), odp_version_impl_name(),

> +	       odp_cpu_model_str(), odp_cpu_hz_max(),

> +	       odp_sys_cache_line_size(), odp_cpu_count());

> +

> +	printf("Running ODP appl: \"%s\"\n"

> +	       "-----------------\n"

> +	       "IP Lookup:	 %s\n"

> +	       "IF Count:        %i\n"

> +	       "Using IFs:      ",

> +	       progname,

> +	       args->hash_mode ? "hash" : "lpm",

> +	       args->if_count);

> +

> +	for (i = 0; i < args->if_count; ++i)

> +		printf(" %s", args->if_names[i]);

> +

> +	printf("\n\n");

> +	fflush(NULL);

> +}

> +

> +/**

> + * Setup rx and tx queues, distribute them among threads.

> + * Try to have one tx queue for each rx queue, if not vailable,

> + * shared tx queue is used.

> + *

> + * If no q argument, the queues are distribute among threads as default.

> + * The thread take one rx queue of a port one time as round-robin order.

> + */

> +static void setup_worker_qconf(app_args_t *args) {

> +	int nb_worker, if_count;

> +	int i, j, rxq_idx, txq_idx;

> +	struct thread_arg_s *arg;

> +	struct l3fwd_pktio_s *port;

> +	uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_QUEUE];

> +

> +	nb_worker = args->worker_count;

> +	if_count = args->if_count;

> +

> +	/* distribute queues among threads */

> +	if (!args->qconf_count) {

> +		if (nb_worker > if_count) {

> +			for (i = 0; i < nb_worker; i++) {

> +				arg = &global.worker_args[i];

> +				arg->thr_idx = i;

> +				j = i % if_count;

> +				port = &global.l3fwd_pktios[j];

> +				if (port->rxq_idx < port->nb_rxq) {

> +					rxq_idx = port->rxq_idx;

> +					arg->pktio[j].rxq[rxq_idx] = rxq_idx;

> +					port->rxq_idx++;

> +					txq_idx = port->txq_idx;

> +					arg->pktio[j].txq[txq_idx] = txq_idx;

> +					port->txq_idx++;

> +					arg->pktio[j].used = 1;

> +				}

> +			}

> +		} else {

> +			for (i = 0; i < if_count; i++) {

> +				j = i % nb_worker;

> +				arg = &global.worker_args[j];

> +				arg->thr_idx = j;

> +				port = &global.l3fwd_pktios[i];

> +				if (port->rxq_idx < port->nb_rxq) {

> +					rxq_idx = port->rxq_idx;

> +					arg->pktio[i].rxq[rxq_idx] = rxq_idx;

> +					port->rxq_idx++;

> +					txq_idx = port->txq_idx;

> +					arg->pktio[i].txq[txq_idx] = txq_idx;

> +					port->txq_idx++;

> +					arg->pktio[i].used = 1;

> +				}

> +			}

> +		}

> +	}

> +

> +	/* specified q argument, distribute queues among threads as it says */

> +	memset(queue_mask, 0, sizeof(queue_mask));

> +	for (i = 0; i < args->qconf_count; i++) {

> +		struct l3fwd_qconf_s *q;

> +

> +		q = &args->qconf_config[i];

> +		if (q->core_idx >= nb_worker || q->if_idx >= if_count)

> +			LOG_ABORT("Error queue (%d, %d, %d), max port: "

> +				  "%d, max core: %d\n", q->if_idx, q->rxq_idx,

> +				  q->core_idx, args->if_count - 1,

> +				  args->worker_count - 1);

> +

> +		/* check if one queue is configured twice or more */

> +		if (queue_mask[q->if_idx][q->rxq_idx])

> +			LOG_ABORT("Error queue (%d, %d, %d), reconfig

> queue\n",

> +				  q->if_idx, q->rxq_idx, q->core_idx);

> +		queue_mask[q->if_idx][q->rxq_idx] = 1;

> +

> +		port = &global.l3fwd_pktios[q->if_idx];

> +		if (port->rxq_idx < q->rxq_idx)

> +			LOG_ABORT("Error queue (%d, %d, %d), queue should

> be"

> +				  " in sequence and start from 0, queue %d\n",

> +				  q->if_idx, q->rxq_idx, q->core_idx,

> +				  q->rxq_idx);

> +

> +		if (q->rxq_idx > port->nb_rxq) {

> +			LOG_ABORT("Error queue (%d, %d, %d), max

> queue %d\n",

> +				  q->if_idx, q->rxq_idx, q->core_idx,

> +				  port->nb_rxq - 1);

> +		}

> +		port->rxq_idx = q->rxq_idx + 1;

> +		port->txq_idx = q->rxq_idx + 1;

> +

> +		/* put the queue into worker_args */

> +		arg = &global.worker_args[q->core_idx];

> +		arg->pktio[q->if_idx].rxq[q->rxq_idx] = q->rxq_idx;

> +		arg->pktio[q->if_idx].txq[q->rxq_idx] = q->rxq_idx;

> +		arg->pktio[q->if_idx].used = 1;

> +		arg->thr_idx = q->core_idx;

> +	}

> +

> +	/* config and initialize rx and tx queues. */

> +	for (i = 0; i < if_count; i++) {

> +		odp_pktin_queue_param_t in_queue_param;

> +		odp_pktout_queue_param_t out_queue_param;

> +		struct odp_pktin_queue_t *inq;

> +		struct odp_pktout_queue_t *outq;

> +		const char *name;

> +		int rc, nb_rxq, nb_txq;

> +

> +		port = &global.l3fwd_pktios[i];

> +		name = args->if_names[i];

> +		odp_pktin_queue_param_init(&in_queue_param);

> +		odp_pktout_queue_param_init(&out_queue_param);

> +

> +		in_queue_param.op_mode = ODP_PKTIO_OP_MT;

> +		out_queue_param.op_mode = ODP_PKTIO_OP_MT;

> +

> +		in_queue_param.num_queues = port->rxq_idx;

> +		if (port->rxq_idx > port->nb_rxq) {

> +			in_queue_param.num_queues = port->nb_rxq;

> +			in_queue_param.op_mode =

> ODP_PKTIO_OP_MT_UNSAFE;

> +		}

> +

> +		/* enable flow hashing for multi-input queues */

> +		if (in_queue_param.num_queues > 1) {

> +			in_queue_param.hash_enable = 1;

> +			in_queue_param.hash_proto.proto.ipv4_tcp = 1;

> +			in_queue_param.hash_proto.proto.ipv4_udp = 1;

> +		}

> +

> +		if (odp_pktin_queue_config(port->pktio, &in_queue_param))

> +			LOG_ABORT("Fail to config input queue for %s\n",

> name);

> +

> +		out_queue_param.num_queues = port->txq_idx;

> +		if (port->txq_idx > port->nb_txq) {

> +			out_queue_param.num_queues = port->nb_txq;

> +			out_queue_param.op_mode =

> ODP_PKTIO_OP_MT_UNSAFE;

> +		}

> +		if (odp_pktout_queue_config(port->pktio,

> &out_queue_param))

> +			LOG_ABORT("Fail to config output queue for %s\n",

> name);

> +

> +		inq = port->ifin;

> +		nb_rxq = in_queue_param.num_queues;

> +		if (odp_pktin_queue(port->pktio, inq, nb_rxq) != nb_rxq)

> +			LOG_ABORT("Fail to set pktin queue for %s\n", name);

> +

> +		if (port->rxq_idx > port->nb_rxq) {

> +			for (rc = port->nb_rxq; rc < port->rxq_idx; rc++)

> +				inq[rc] = inq[rc % port->nb_rxq];

> +		}

> +

> +		outq = port->ifout;

> +		nb_txq = out_queue_param.num_queues;

> +		if (odp_pktout_queue(port->pktio, outq, nb_txq) != nb_txq)

> +			LOG_ABORT("Fail to set pktout queue for %s\n",

> name);

> +

> +		if (port->txq_idx > port->nb_txq) {

> +			for (rc = port->nb_txq; rc < port->txq_idx; rc++)

> +				outq[rc] = outq[rc % port->nb_txq];

> +		}

> +	}

> +}

> +

> +static void print_qconf_table(app_args_t *args) {

> +	int i, j, k, qid;

> +	char buf[32];

> +	struct thread_arg_s *thr_arg;

> +

> +	printf("Rx queue table\n"

> +	       "-----------------\n"

> +	       "%-16s%-16s%-16s\n",

> +	       "port/id", "queue", "thread");

> +

> +	for (i = 0; i < args->worker_count; i++) {

> +		thr_arg = &global.worker_args[i];

> +		for (j = 0; j < args->if_count; j++) {

> +			if (!thr_arg->pktio[j].used)

> +				continue;

> +

> +			sprintf(buf, "%s/%d", args->if_names[j], j);

> +			for (k = 0; k < MAX_NB_QUEUE; k++) {

> +				qid = thr_arg->pktio[j].rxq[k];

> +				if (qid != INVALID_ID)

> +					printf("%-16s%-16d%-16d\n", buf, qid,

> +					       thr_arg->thr_idx);

> +			}

> +		}

> +	}

> +	printf("\n");

> +	fflush(NULL);

> +}

> +

> +/**

> + *  Print statistics

> + *

> + * @param num_workers Number of worker threads

> + * @param duration Number of seconds to loop in

> + * @param timeout Number of seconds for stats calculation

> + *

> + */

> +static int print_speed_stats(int num_workers, int duration, int

> +timeout) {

> +	uint64_t pkts = 0;

> +	uint64_t pkts_prev = 0;

> +	uint64_t pps;

> +	uint64_t rx_drops, tx_drops;

> +	uint64_t maximum_pps = 0;

> +	int i;

> +	int elapsed = 0;

> +	int stats_enabled = 1;

> +	int loop_forever = (duration == 0);

> +

> +	if (timeout <= 0) {

> +		stats_enabled = 0;

> +		timeout = 1;

> +	}

> +	/* Wait for all threads to be ready*/

> +	odp_barrier_wait(&barrier);

> +

> +	do {

> +		pkts = 0;

> +		rx_drops = 0;

> +		tx_drops = 0;

> +		sleep(timeout);

> +

> +		for (i = 0; i < num_workers; i++) {

> +			pkts += global.worker_args[i].packets;

> +			rx_drops += global.worker_args[i].rx_drops;

> +			tx_drops += global.worker_args[i].tx_drops;

> +		}

> +		if (stats_enabled) {

> +			pps = (pkts - pkts_prev) / timeout;

> +			if (pps > maximum_pps)

> +				maximum_pps = pps;

> +			printf("%" PRIu64 " pps, %" PRIu64 " max pps, ",  pps,

> +			       maximum_pps);

> +

> +			printf(" %" PRIu64 " rx drops, %" PRIu64 " tx

drops\n",
> +			       rx_drops, tx_drops);

> +

> +			pkts_prev = pkts;

> +		}

> +		elapsed += timeout;

> +	} while (loop_forever || (elapsed < duration));

> +

> +	if (stats_enabled)

> +		printf("TEST RESULT: %" PRIu64 " maximum packets per

> second.\n",

> +		       maximum_pps);

> +

> +	return pkts > 100 ? 0 : -1;

> +}

> diff --git a/example/l3fwd/odp_l3fwd_db.c b/example/l3fwd/odp_l3fwd_db.c

> new file mode 100644 index 0000000..93e32f0

> --- /dev/null

> +++ b/example/l3fwd/odp_l3fwd_db.c

> @@ -0,0 +1,408 @@

> +/* Copyright (c) 2016, Linaro Limited

> + * All rights reserved.

> + *

> + * SPDX-License-Identifier:     BSD-3-Clause

> + */

> +

> +#ifndef _GNU_SOURCE

> +#define _GNU_SOURCE

> +#endif

> +

> +#include <stdlib.h>

> +#include <string.h>

> +

> +#include <example_debug.h>

> +#include <odp_api.h>

> +#include <odp_l3fwd_db.h>

> +

> +/** Jenkins hash support.

> +  *

> +  * Copyright (C) 2006 Bob Jenkins (bob_jenkins@burtleburtle.net)

> +  *

> +  * http://burtleburtle.net/bob/hash/

> +  *

> +  * These are the credits from Bob's sources:

> +  *

> +  * lookup3.c, by Bob Jenkins, May 2006, Public Domain.

> +  *

> +  * These are functions for producing 32-bit hashes for hash table lookup.

> +  * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and

> +final()

> +  * are externally useful functions.  Routines to test the hash are

> +included

> +  * if SELF_TEST is defined.  You can use this free for any purpose.

> +It's in

> +  * the public domain.  It has no warranty.

> +  *

> +  * $FreeBSD$

> +  */

> +#define JHASH_GOLDEN_RATIO	0x9e3779b9

> +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) #define

> +FWD_BJ3_MIX(a, b, c) \ { \

> +	a -= c; a ^= rot(c, 4); c += b; \

> +	b -= a; b ^= rot(a, 6); a += c; \

> +	c -= b; c ^= rot(b, 8); b += a; \

> +	a -= c; a ^= rot(c, 16); c += b; \

> +	b -= a; b ^= rot(a, 19); a += c; \

> +	c -= b; c ^= rot(b, 4); b += a; \

> +}

> +

> +/**

> + * Compute hash value from a flow

> + */

> +static inline

> +uint64_t l3fwd_calc_hash(ipv4_tuple5_t *key) {

> +	uint64_t l4_ports = 0;

> +	uint32_t dst_ip, src_ip;

> +

> +	src_ip = key->src_ip;

> +	dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO;

> +	FWD_BJ3_MIX(src_ip, dst_ip, l4_ports);

> +

> +	return l4_ports;

> +}

> +

> +/**

> + * Parse text string representing an IPv4 address or subnet

> + *

> + * String is of the format "XXX.XXX.XXX.XXX(/W)" where

> + * "XXX" is decimal value and "/W" is optional subnet length

> + *

> + * @param ipaddress  Pointer to IP address/subnet string to convert

> + * @param addr       Pointer to return IPv4 address, host endianness

> + * @param depth      Pointer to subnet bit width

> + * @return 0 if successful else -1

> + */

> +static inline

> +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *depth)

> +{

> +	int b[4];

> +	int qualifier = 32;

> +	int converted;

> +	uint32_t addr_le;

> +

> +	if (strchr(ipaddress, '/')) {

> +		converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",

> +				   &b[3], &b[2], &b[1], &b[0],

> +				   &qualifier);

> +		if (5 != converted)

> +			return -1;

> +	} else {

> +		converted = sscanf(ipaddress, "%d.%d.%d.%d",

> +				   &b[3], &b[2], &b[1], &b[0]);

> +		if (4 != converted)

> +			return -1;

> +	}

> +

> +	if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))

> +		return -1;

> +	if (!qualifier || (qualifier > 32))

> +		return -1;

> +

> +	addr_le = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;

> +	*addr = odp_le_to_cpu_32(addr_le);

> +	*depth = qualifier;

> +

> +	return 0;

> +}

> +

> +/**

> + * Generate text string representing IPv4 range/subnet, output

> + * in "XXX.XXX.XXX.XXX/W" format

> + *

> + * @param b     Pointer to buffer to store string

> + * @param range Pointer to IPv4 address range

> + *

> + * @return Pointer to supplied buffer

> + */

> +static inline

> +char *ipv4_subnet_str(char *b, ip_addr_range_t *range) {

> +	sprintf(b, "%d.%d.%d.%d/%d",

> +		0xFF & ((range->addr) >> 24),

> +		0xFF & ((range->addr) >> 16),

> +		0xFF & ((range->addr) >>  8),

> +		0xFF & ((range->addr) >>  0),

> +		range->depth);

> +	return b;

> +}

> +

> +/**

> + * Generate text string representing MAC address

> + *

> + * @param b     Pointer to buffer to store string

> + * @param mac   Pointer to MAC address

> + *

> + * @return Pointer to supplied buffer

> + */

> +static inline

> +char *mac_addr_str(char *b, odph_ethaddr_t *mac) {

> +	uint8_t *byte;

> +

> +	byte = mac->addr;

> +	sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",

> +		byte[0], byte[1], byte[2], byte[3], byte[4], byte[5]);

> +	return b;

> +}

> +

> +/**

> + * Flow cache table entry

> + */

> +typedef struct flow_entry_s {

> +	ipv4_tuple5_t key;		/**< match key */

> +	struct flow_entry_s *next;      /**< next entry on the list */

> +	fwd_db_entry_t *fwd_entry;	/**< entry info in db */

> +} flow_entry_t;

> +

> +/**

> + * Flow cache table bucket

> + */

> +typedef struct flow_bucket_s {

> +	odp_spinlock_t		lock;	/**< Bucket lock*/

> +	flow_entry_t		*next;	/**< Pointer to first flow entry in

> bucket*/

> +} flow_bucket_t;

> +

> +/**

> + * Flow hash table, fast lookup cache

> + */

> +typedef struct flow_table_s {

> +	flow_bucket_t *bucket;

> +	uint32_t count;

> +} flow_table_t;

> +

> +static flow_table_t fwd_lookup_cache;

> +

> +void init_fwd_hash_cache(void)

> +{

> +	odp_shm_t		hash_shm;

> +	flow_bucket_t		*bucket;

> +	uint32_t		bucket_count;

> +	uint32_t		i;

> +

> +	bucket_count = FWD_DEF_BUCKET_COUNT;

> +

> +	/*Reserve memory for Routing hash table*/

> +	hash_shm = odp_shm_reserve("route_table",

> +				   sizeof(flow_bucket_t) * bucket_count,

> +				   ODP_CACHE_LINE_SIZE, 0);

> +

> +	bucket = odp_shm_addr(hash_shm);

> +	if (!bucket) {

> +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> +		exit(-1);

> +	}

> +

> +	fwd_lookup_cache.bucket = bucket;

> +	fwd_lookup_cache.count = bucket_count;

> +

> +	/*Initialize Locks*/

> +	for (i = 0; i < bucket_count; i++) {

> +		bucket = &fwd_lookup_cache.bucket[i];

> +		odp_spinlock_init(&bucket->lock);

> +		bucket->next = NULL;

> +	}

> +}

> +

> +static inline

> +int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow) {

> +	if (key->src_ip == flow->key.src_ip &&

> +	    key->dst_ip == flow->key.dst_ip &&

> +	    key->src_port == flow->key.src_port &&

> +	    key->dst_port == flow->key.dst_port &&

> +	    key->proto == flow->key.proto)

> +		return 1;

> +

> +	return 0;

> +}

> +

> +static inline

> +flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t

> +*bucket) {

> +	flow_entry_t *rst;

> +

> +	for (rst = bucket->next; rst != NULL; rst = rst->next) {

> +		if (match_key_flow(key, rst))

> +			break;

> +	}

> +

> +	return rst;

> +}

> +

> +static inline

> +flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key,

> +			       flow_bucket_t *bucket,

> +			       fwd_db_entry_t *entry)

> +{

> +	flow_entry_t *flow;

> +

> +	flow = lookup_fwd_cache(key, bucket);

> +	if (flow)

> +		return flow;

> +

> +	flow = malloc(sizeof(flow_entry_t));

> +	flow->key = *key;

> +	flow->fwd_entry = entry;

> +

> +	odp_spinlock_lock(&bucket->lock);

> +	if (!bucket->next) {

> +		bucket->next = flow;

> +	} else {

> +		flow->next = bucket->next;

> +		bucket->next = flow;

> +	}

> +	odp_spinlock_unlock(&bucket->lock);

> +

> +	return flow;

> +}

> +

> +/** Global pointer to fwd db */

> +fwd_db_t *fwd_db;

> +

> +void init_fwd_db(void)

> +{

> +	odp_shm_t shm;

> +

> +	shm = odp_shm_reserve("shm_fwd_db",

> +			      sizeof(fwd_db_t),

> +			      ODP_CACHE_LINE_SIZE,

> +			      0);

> +

> +	fwd_db = odp_shm_addr(shm);

> +

> +	if (fwd_db == NULL) {

> +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> +		exit(EXIT_FAILURE);

> +	}

> +	memset(fwd_db, 0, sizeof(*fwd_db));

> +}

> +

> +int create_fwd_db_entry(char *input, char **oif) {

> +	int pos = 0;

> +	char *local;

> +	char *str;

> +	char *save;

> +	char *token;

> +	fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];

> +

> +	/* Verify we haven't run out of space */

> +	if (MAX_DB <= fwd_db->index)

> +		return -1;

> +

> +	/* Make a local copy */

> +	local = malloc(strlen(input) + 1);

> +	if (NULL == local)

> +		return -1;

> +	strcpy(local, input);

> +

> +	/* Setup for using "strtok_r" to search input string */

> +	str = local;

> +	save = NULL;

> +

> +	/* Parse tokens separated by ':' */

> +	while (NULL != (token = strtok_r(str, ",", &save))) {

> +		str = NULL;  /* reset str for subsequent strtok_r calls */

> +

> +		/* Parse token based on its position */

> +		switch (pos) {

> +		case 0:

> +			parse_ipv4_string(token,

> +					  &entry->subnet.addr,

> +					  &entry->subnet.depth);

> +			break;

> +		case 1:

> +			strncpy(entry->oif, token, OIF_LEN - 1);

> +			entry->oif[OIF_LEN - 1] = 0;

> +			break;

> +		case 2:

> +			odph_eth_addr_parse(&entry->dst_mac, token);

> +			*oif = entry->oif;

> +			break;

> +

> +		default:

> +			printf("ERROR: extra token \"%s\" at position %d\n",

> +			       token, pos);

> +			break;

> +		}

> +

> +		/* Advance to next position */

> +		pos++;

> +	}

> +

> +	/* Add route to the list */

> +	fwd_db->index++;

> +	entry->next = fwd_db->list;

> +	fwd_db->list = entry;

> +

> +	free(local);

> +	return 0;

> +}

> +

> +void resolve_fwd_db(char *intf, int portid, uint8_t *mac) {

> +	fwd_db_entry_t *entry;

> +

> +	/* Walk the list and attempt to set output and MAC */

> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> +		if (strcmp(intf, entry->oif))

> +			continue;

> +

> +		entry->oif_id = portid;

> +		memcpy(entry->src_mac.addr, mac, ODPH_ETHADDR_LEN);

> +	}

> +}

> +

> +void dump_fwd_db_entry(fwd_db_entry_t *entry) {

> +	char subnet_str[MAX_STRING];

> +	char mac_str[MAX_STRING];

> +

> +	mac_addr_str(mac_str, &entry->dst_mac);

> +	printf("%-16s%-16s%-16s\n",

> +	       ipv4_subnet_str(subnet_str, &entry->subnet),

> +	       entry->oif, mac_str);

> +}

> +

> +void dump_fwd_db(void)

> +{

> +	fwd_db_entry_t *entry;

> +

> +	printf("Routing table\n"

> +	       "-----------------\n"

> +	       "%-16s%-16s%-16s\n",

> +	       "subnet", "next_hop", "dest_mac");

> +

> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> +		dump_fwd_db_entry(entry);

> +

> +	printf("\n");

> +}

> +

> +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key) {

> +	fwd_db_entry_t *entry;

> +	flow_entry_t *flow;

> +	flow_bucket_t *bucket;

> +	uint64_t hash;

> +

> +	/* first find in cache */

> +	hash = l3fwd_calc_hash(key);

> +	hash &= fwd_lookup_cache.count - 1;

> +	bucket = &fwd_lookup_cache.bucket[hash];

> +	flow = lookup_fwd_cache(key, bucket);

> +	if (flow)

> +		return flow->fwd_entry;

> +

> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next) {

> +		uint32_t mask;

> +

> +		mask = ((1u << entry->subnet.depth) - 1) <<

> +			(32 - entry->subnet.depth);

> +

> +		if (entry->subnet.addr == (key->dst_ip & mask))

> +			break;

> +	}

> +

> +	return entry;

> +}

> diff --git a/example/l3fwd/odp_l3fwd_db.h

> b/example/l3fwd/odp_l3fwd_db.h new file mode 100644 index

> 0000000..840946a

> --- /dev/null

> +++ b/example/l3fwd/odp_l3fwd_db.h

> @@ -0,0 +1,130 @@

> +/* Copyright (c) 2016, Linaro Limited

> + * All rights reserved.

> + *

> + * SPDX-License-Identifier:     BSD-3-Clause

> + */

> +

> +#ifndef ODP_L3FWD_DB_H_

> +#define ODP_L3FWD_DB_H_

> +

> +#ifdef __cplusplus

> +extern "C" {

> +#endif

> +

> +#include <odp_api.h>

> +#include <odp/helper/eth.h>

> +

> +#define OIF_LEN 32

> +#define MAX_DB  32

> +#define MAX_STRING  32

> +

> +/**

> + * Default number of flows

> + */

> +#define FWD_DEF_FLOW_COUNT		100000

> +

> +/**

> + * Default Hash bucket number

> + */

> +#define FWD_DEF_BUCKET_COUNT	(FWD_DEF_FLOW_COUNT / 8)

> +

> +/**

> + * IP address range (subnet)

> + */

> +typedef struct ip_addr_range_s {

> +	uint32_t  addr;     /**< IP address, host endianness */

> +	uint32_t  depth;    /**< subnet bit width */

> +} ip_addr_range_t;

> +

> +/**

> + * TCP/UDP flow

> + */

> +typedef struct ipv4_tuple5_s {

> +	uint32_t src_ip;

> +	uint32_t dst_ip;

> +	uint16_t src_port;

> +	uint16_t dst_port;

> +	uint8_t  proto;

> +} ipv4_tuple5_t;

> +

> +/**

> + * Forwarding data base entry

> + */

> +typedef struct fwd_db_entry_s {

> +	struct fwd_db_entry_s *next;          /**< Next entry on list */

> +	char                    oif[OIF_LEN]; /**< Output interface name */

> +	int			oif_id;	      /**< Output interface idx */

> +	odph_ethaddr_t		src_mac;      /**< Output source MAC */

> +	odph_ethaddr_t		dst_mac;      /**< Output destination

> MAC */

> +	ip_addr_range_t		subnet;       /**< Subnet for this router

> */

> +} fwd_db_entry_t;

> +

> +/**

> + * Forwarding data base

> + */

> +typedef struct fwd_db_s {

> +	uint32_t          index;          /**< Next available entry */

> +	fwd_db_entry_t   *list;           /**< List of active routes */

> +	fwd_db_entry_t    array[MAX_DB];  /**< Entry storage */

> +} fwd_db_t;

> +

> +/** Global pointer to fwd db */

> +extern fwd_db_t *fwd_db;

> +

> +/**

> + * Initialize FWD DB

> + */

> +void init_fwd_db(void);

> +

> +/**

> + * Initialize forward lookup cache based on hash  */ void

> +init_fwd_hash_cache(void);

> +

> +/**

> + * Create a forwarding database entry

> + *

> + * String is of the format "SubNet,Intf,NextHopMAC"

> + *

> + * @param input  Pointer to string describing route

> + * @param oif  Pointer to out interface name, as a return value

> + *

> + * @return 0 if successful else -1

> + */

> +int create_fwd_db_entry(char *input, char **oif);

> +

> +/**

> + * Scan FWD DB entries and resolve output queue and source MAC address

> + *

> + * @param intf   Interface name string

> + * @param portid Output queue for packet transmit

> + * @param mac    MAC address of this interface

> + */

> +void resolve_fwd_db(char *intf, int portid, uint8_t *mac);

> +

> +/**

> + * Display one forwarding database entry

> + *

> + * @param entry  Pointer to entry to display  */ void

> +dump_fwd_db_entry(fwd_db_entry_t *entry);

> +

> +/**

> + * Display the forwarding database

> + */

> +void dump_fwd_db(void);

> +

> +/**

> + * Find a matching forwarding database entry

> + *

> + * @param key  ipv4 tuple

> + *

> + * @return pointer to forwarding DB entry else NULL  */ fwd_db_entry_t

> +*find_fwd_db_entry(ipv4_tuple5_t *key);

> +

> +#ifdef __cplusplus

> +}

> +#endif

> +

> +#endif

> diff --git a/example/l3fwd/odp_l3fwd_lpm.c

> b/example/l3fwd/odp_l3fwd_lpm.c new file mode 100644 index

> 0000000..1b3bfcf

> --- /dev/null

> +++ b/example/l3fwd/odp_l3fwd_lpm.c

> @@ -0,0 +1,224 @@

> +/* Copyright (c) 2016, Linaro Limited

> + * All rights reserved.

> + *

> + * SPDX-License-Identifier:     BSD-3-Clause

> + */

> +#ifndef _GNU_SOURCE

> +#define _GNU_SOURCE

> +#endif

> +

> +#include <stdio.h>

> +#include <stdlib.h>

> +

> +#include <example_debug.h>

> +#include <odp_api.h>

> +

> +#include <odp_l3fwd_lpm.h>

> +

> +/**

> + * This is a simple implementation of lpm based on patricia tree.

> + *

> + * Tradeoff exists between memory consumption and lookup time.

> + * Currently it prefers 5 levels: {16, 4, 4, 4, 4}, could be 3

> + * levels: {16, 8, 8} by defining FIB_NEXT_STRIDE as 8. Other

> + * levels are also possible.

> + *

> + * the ip here is host endian, when doing init or lookup, the

> + * caller should do endianness conversion if needed.

> + */

> +

> +#define FIB_IP_WIDTH 32

> +#define FIB_FIRST_STRIDE 16

> +#define FIB_NEXT_STRIDE 4

> +#define FIB_NEXT_SIZE (1 << FIB_NEXT_STRIDE) #define FIB_SUB_COUNT

> +16384 #define DEPTH_TO_MASK(depth) ((1 << (depth)) - 1)

> +

> +typedef struct fib_node_s {

> +	union {

> +		uint32_t next_hop;

> +		struct fib_node_s *next; /* next level table */

> +	};

> +	uint8_t valid	:1; /* 1, this node has a valid next hop */

> +	uint8_t end	:1; /* 0, next points to the extended table */

> +	uint8_t depth	:6; /* bit length of subnet mask */

> +} fib_node_t;

> +

> +typedef struct fib_sub_tbl_s {

> +	fib_node_t *fib_nodes;

> +	uint32_t fib_count;

> +	uint32_t fib_idx;

> +} fib_sub_tbl_t;

> +

> +static fib_node_t fib_rt_tbl[1 << FIB_FIRST_STRIDE]; static

> +fib_sub_tbl_t fib_lpm_cache;

> +

> +static inline fib_node_t *fib_alloc_sub(void) {

> +	fib_node_t *sub_tbl = NULL;

> +	uint32_t i, nb_entry;

> +

> +	/* extend to next level */

> +	if (fib_lpm_cache.fib_idx < fib_lpm_cache.fib_count) {

> +		nb_entry = (fib_lpm_cache.fib_idx + 1) * FIB_NEXT_SIZE;

> +		sub_tbl = &fib_lpm_cache.fib_nodes[nb_entry];

> +		fib_lpm_cache.fib_idx++;

> +		for (i = 0; i < nb_entry; i++) {

> +			sub_tbl[i].valid = 0;

> +			sub_tbl[i].end = 1;

> +		}

> +	}

> +

> +	return sub_tbl;

> +}

> +

> +static void fib_update_node(fib_node_t *fe, int port, int depth) {

> +	fib_node_t *p;

> +	int i;

> +

> +	if (fe->end) {

> +		if (!fe->valid) {

> +			fe->depth = depth;

> +			fe->next_hop = port;

> +			fe->valid = 1;

> +		} else if (fe->depth <= depth) {

> +			fe->next_hop = port;

> +			fe->depth = depth;

> +		}

> +

> +		return;

> +	}

> +

> +	for (i = 0; i < FIB_NEXT_SIZE; i++) {

> +		p = &fe->next[i];

> +		if (p->end)

> +			fib_update_node(p, port, depth);

> +	}

> +}

> +

> +static void fib_insert_node(fib_node_t *fe, uint32_t ip, uint32_t next_hop,

> +			    int ip_width, int eat_bits, int depth) {

> +	int i;

> +	uint32_t idx, port;

> +	fib_node_t *p;

> +

> +	if (fe->end) {

> +		port = fe->next_hop;

> +		p = fib_alloc_sub();

> +		if (!p)

> +			return;

> +

> +		fe->next = p;

> +		fe->end = 0;

> +		if (fe->valid) {

> +			for (i = 0; i < FIB_NEXT_SIZE; i++) {

> +				p = &fe->next[i];

> +				p->next_hop = port;

> +				p->depth = fe->depth;

> +			}

> +		}

> +	}

> +	if (depth - eat_bits <= FIB_NEXT_STRIDE) {

> +		ip_width -= depth - eat_bits;

> +		idx = ip >> ip_width;

> +		ip &= DEPTH_TO_MASK(ip_width);

> +		p = &fe->next[idx];

> +		fib_update_node(p, next_hop, depth);

> +	} else {

> +		ip_width -= FIB_NEXT_STRIDE;

> +		idx = ip >> ip_width;

> +		p = &fe->next[idx];

> +		ip &= DEPTH_TO_MASK(ip_width);

> +		eat_bits += FIB_NEXT_STRIDE;

> +		fib_insert_node(p, ip, next_hop, ip_width, eat_bits, depth);

> +	}

> +}

> +

> +void fib_tbl_init(void)

> +{

> +	int i;

> +	fib_node_t *fe;

> +	uint32_t size;

> +	odp_shm_t lpm_shm;

> +

> +	for (i = 0; i < (1 << FIB_FIRST_STRIDE); i++) {

> +		fe = &fib_rt_tbl[i];

> +		fe->valid = 0;

> +		fe->end = 1;

> +		fe->depth = 0;

> +		fe->next_hop = 0;

> +	}

> +

> +	size = FIB_NEXT_SIZE * FIB_SUB_COUNT;

> +	/*Reserve memory for Routing hash table*/

> +	lpm_shm = odp_shm_reserve("fib_lpm_sub", size,

> ODP_CACHE_LINE_SIZE, 0);

> +	fe = odp_shm_addr(lpm_shm);

> +	if (!fe) {

> +		EXAMPLE_ERR("Error: shared mem alloc failed for lpm

> cache.\n");

> +		exit(-1);

> +	}

> +

> +	fib_lpm_cache.fib_nodes = fe;

> +	fib_lpm_cache.fib_count = FIB_SUB_COUNT;

> +	fib_lpm_cache.fib_idx = 0;

> +}

> +

> +void fib_tbl_insert(uint32_t ip, int port, int depth) {

> +	fib_node_t *fe, *p;

> +	uint32_t idx;

> +	int i, j;

> +	int nb_bits;

> +

> +	nb_bits = FIB_FIRST_STRIDE;

> +	idx = ip >> nb_bits;

> +	fe = &fib_rt_tbl[idx];

> +	if (depth <= nb_bits) {

> +		if (fe->end) {

> +			fe->next_hop = port;

> +			fe->depth = depth;

> +			fe->valid = 1;

> +			return;

> +		}

> +

> +		for (i = 0; i < FIB_NEXT_SIZE; i++) {

> +			p = &fe->next[i];

> +			if (p->end)

> +				fib_update_node(p, port, depth);

> +			else

> +				for (j = 0; j < FIB_NEXT_SIZE; j++)

> +					fib_update_node(&p->next[j], port,

> +							depth);

> +		}

> +

> +		return;

> +	}

> +

> +	/* need to check sub table */

> +	ip &= DEPTH_TO_MASK(FIB_IP_WIDTH - nb_bits);

> +	fib_insert_node(fe, ip, port, FIB_IP_WIDTH - nb_bits, nb_bits, depth);

> +}

> +

> +int fib_tbl_lookup(uint32_t ip, int *port) {

> +	fib_node_t *fe;

> +	uint32_t idx;

> +	int nb_bits;

> +

> +	nb_bits = FIB_IP_WIDTH - FIB_FIRST_STRIDE;

> +	idx = ip >> nb_bits;

> +	fe = &fib_rt_tbl[idx];

> +

> +	ip &= DEPTH_TO_MASK(nb_bits);

> +	while (!fe->end) {

> +		nb_bits -= FIB_NEXT_STRIDE;

> +		idx = ip >> nb_bits;

> +		fe = &fe->next[idx];

> +		ip &= DEPTH_TO_MASK(nb_bits);

> +	}

> +	*port = fe->next_hop;

> +

> +	return fe->valid ? 0 : -1;

> +}

> diff --git a/example/l3fwd/odp_l3fwd_lpm.h

> b/example/l3fwd/odp_l3fwd_lpm.h new file mode 100644 index

> 0000000..8f78e39

> --- /dev/null

> +++ b/example/l3fwd/odp_l3fwd_lpm.h

> @@ -0,0 +1,20 @@

> +/* Copyright (c) 2016, Linaro Limited

> + * All rights reserved.

> + *

> + * SPDX-License-Identifier:     BSD-3-Clause

> + */

> +

> +#ifndef ODP_L3FWD_LPM_H_

> +#define ODP_L3FWD_LPM_H_

> +

> +#ifdef __cplusplus

> +extern "C" {

> +#endif

> +void fib_tbl_init(void);

> +void fib_tbl_insert(uint32_t ip, int port, int depth); int

> +fib_tbl_lookup(uint32_t ip, int *port); #ifdef __cplusplus } #endif

> +

> +#endif

> diff --git a/example/m4/configure.m4 b/example/m4/configure.m4 index

> bbda38f..78ef396 100644

> --- a/example/m4/configure.m4

> +++ b/example/m4/configure.m4

> @@ -19,5 +19,6 @@ AC_CONFIG_FILES([example/classifier/Makefile

>  		 example/timer/Makefile

>  		 example/traffic_mgmt/Makefile

>  		 example/l2fwd_simple/Makefile

> +		 example/l3fwd/Makefile

>  		 example/switch/Makefile

>  		 example/hello/Makefile])

> --

> 2.1.0.27.g96db324
Forrest Shi July 25, 2016, 8:55 a.m. UTC | #5
Hi Maxim,

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

Thanks,
Forrest

> -----Original Message-----

> From: forrest.shi [mailto:forrest.shi@linaro.org]

> Sent: Monday, July 25, 2016 16:06

> To: 'Maxim Uvarov' <maxim.uvarov@linaro.org>

> Cc: lng-odp@lists.linaro.org

> Subject: RE: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

> 

> Hi Maxim,

> 

> Any comments?

> 

> Thanks,

> Forrest

> 

> > -----Original Message-----

> > From: forrest.shi [mailto:forrest.shi@linaro.org]

> > Sent: Friday, July 15, 2016 16:22

> > To: 'Maxim Uvarov' <maxim.uvarov@linaro.org>

> > Cc: lng-odp@lists.linaro.org

> > Subject: RE: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

> >

> > Hi Maxim,

> >

> > The patch is length.  I extracted the comments I have some question as

> below,

> > Others will be fixed.

> >

> >  Increment one value from number of threads might be not good thing to do

> >  due to 1) performance 2) atomic operations break.

> >  It's better if each thread has it's own counters and control threads

> >  summaries values.

> > ==========================================================

> > Not catch your idea. Here each thread has its own counters and is totaled

> > before main exit.

> >

> >

> >  main should end if odp_destroy_global(). And to add this to 'make check'

> >  requires probably to add some logic when we think that test passed and

> >  when we thing that test failed.

> >  I think you copied print_speed_stats() from l2fwd,  there return code

> >  returns if app passed or not.

> > ==========================================================

> > The testing PASS depends on the print_speed_stats() output that is used by

> > test shell script.

> > Here return value is not taken  as PASS or FAIL.

> >

> >

> > On thought is what we need with l2fwd. We places it under

> >  test/performance/ but it's actual example app.

> >  Not sure that it's clear for new users. Definitely l2fwd and l3fwd have

> >  to be at the same level. I'm thinking

> >  about placing both to test/performance/ and create symlink with some

> >  README to example/. But I'm not sure

> >  about that, we need to discuss.

> > ==========================================================

> > In my opinion, don't need to put app in performance.

> > From literal meaning, test should handle testing things, no new app.

> >

> > Thanks,

> > Forrest

> >

> > > -----Original Message-----

> > > From: lng-odp [mailto:lng-odp-bounces@lists.linaro.org] On Behalf Of

> Maxim

> > > Uvarov

> > > Sent: Wednesday, July 13, 2016 17:10

> > > To: lng-odp@lists.linaro.org

> > > Subject: Re: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

> > >

> > > On 07/13/16 10:29, forrest.shi@linaro.org wrote:

> > > > From: Xuelin Shi <forrest.shi@linaro.org>

> > > >

> > > > multi-thread, multi-queues and bi-directional forwarding.

> > > >

> > > > support (port, queue, thread) arguments in cmdline which specify how

> > > > the threads handle which rx queue at which port, if no this argument,

> > > > default specification used.

> > > >

> > > > both hash and lpm based lookup methods are supported, default lpm.

> > > >

> > > > Signed-off-by: Xuelin Shi <forrest.shi@linaro.org>

> > > > ---

> > > >   example/Makefile.am           |    2 +-

> > > >   example/l3fwd/.gitignore      |    4 +

> > > >   example/l3fwd/Makefile.am     |   18 +

> > > >   example/l3fwd/odp_l3fwd.c     | 1072

> > > +++++++++++++++++++++++++++++++++++++++++

> > > >   example/l3fwd/odp_l3fwd_db.c  |  408 ++++++++++++++++

> > > >   example/l3fwd/odp_l3fwd_db.h  |  130 +++++

> > > >   example/l3fwd/odp_l3fwd_lpm.c |  224 +++++++++

> > > >   example/l3fwd/odp_l3fwd_lpm.h |   20 +

> > > >   example/m4/configure.m4       |    1 +

> > > >   9 files changed, 1878 insertions(+), 1 deletion(-)

> > > >   create mode 100644 example/l3fwd/.gitignore

> > > >   create mode 100644 example/l3fwd/Makefile.am

> > > >   create mode 100644 example/l3fwd/odp_l3fwd.c

> > > >   create mode 100644 example/l3fwd/odp_l3fwd_db.c

> > > >   create mode 100644 example/l3fwd/odp_l3fwd_db.h

> > > >   create mode 100644 example/l3fwd/odp_l3fwd_lpm.c

> > > >   create mode 100644 example/l3fwd/odp_l3fwd_lpm.h

> > > >

> > > > diff --git a/example/Makefile.am b/example/Makefile.am

> > > > index 37542af..1f1b62e 100644

> > > > --- a/example/Makefile.am

> > > > +++ b/example/Makefile.am

> > > > @@ -1 +1 @@

> > > > -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

> > > l2fwd_simple switch hello

> > > > +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

> > > l2fwd_simple switch hello l3fwd

> > >

> > > Hello Forest, you should use one git send-email command for both

> > > patches. So 2 patches will be in the same email thread,

> > > and not as 2 separate patches.

> > >

> > > alphabetical order here is required. And in next update please place all

> > > entries vertically, newer patches will be simpler.

> > >

> >

> > OK, will be fixed.

> >

> > >

> > > > diff --git a/example/l3fwd/.gitignore b/example/l3fwd/.gitignore

> > > > new file mode 100644

> > > > index 0000000..4a25ae8

> > > > --- /dev/null

> > > > +++ b/example/l3fwd/.gitignore

> > > > @@ -0,0 +1,4 @@

> > > > +odp_l3fwd

> > > > +Makefile

> > > > +Makefile.in

> > > > +*.o

> > > last 3 entries are not needed due to we already have it in common .git

> > > ignore file.

> > >

> > > > diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am

> > > > new file mode 100644

> > > > index 0000000..5092aa7

> > > > --- /dev/null

> > > > +++ b/example/l3fwd/Makefile.am

> > > > @@ -0,0 +1,18 @@

> > > > +include $(top_srcdir)/example/Makefile.inc

> > > > +

> > > > +bin_PROGRAMS = odp_l3fwd$(EXEEXT)

> > > > +odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static

> > > > +odp_l3fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example -

> > > I${top_srcdir}/test

> > > > +

> > > > +noinst_HEADERS = \

> > > > +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \

> > > > +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_lpm.h \

> > > > +		  $(top_srcdir)/example/example_debug.h

> > > > +

> > > > +dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c

> > odp_l3fwd_lpm.c

> > > > +

> > > > +if test_example

> > > > +TESTS = odp_l3fwd_run.sh

> > > > +endif

> > > > +

> > > > +EXTRA_DIST = odp_l3fwd_run.sh

> > > > diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c

> > > > new file mode 100644

> > > > index 0000000..a10ca76

> > > > --- /dev/null

> > > > +++ b/example/l3fwd/odp_l3fwd.c

> > > > @@ -0,0 +1,1072 @@

> > > > +/* Copyright (c) 2016, Linaro Limited

> > > > + * All rights reserved.

> > > > + *

> > > > + * SPDX-License-Identifier:     BSD-3-Clause

> > > > + */

> > > > +

> > > > +#include <stdlib.h>

> > > > +#include <stdio.h>

> > > > +#include <errno.h>

> > > > +#include <getopt.h>

> > > > +#include <unistd.h>

> > > > +#include <inttypes.h>

> > > > +

> > > > +#include <test_debug.h>

> > > > +

> > > > +#include <odp_api.h>

> > > > +#include <odp/helper/linux.h>

> > > > +#include <odp/helper/eth.h>

> > > > +#include <odp/helper/ip.h>

> > > > +#include <odp/helper/udp.h>

> > > > +#include <odp/helper/tcp.h>

> > > > +

> > > > +#include "odp_l3fwd_db.h"

> > > > +#include "odp_l3fwd_lpm.h"

> > > > +

> > > > +#define POOL_NUM_PKT	8192

> > > > +#define POOL_SEG_LEN	1856

> > > > +#define MAX_PKT_BURST	32

> > > > +

> > > > +#define MAX_NB_WORKER	32

> > > > +#define MAX_NB_PKTIO	32

> > > > +#define MAX_NB_QUEUE	32

> > > > +#define MAX_NB_QCONFS	1024

> > > > +#define MAX_NB_ROUTE	32

> > > > +

> > > > +#define INVALID_ID	(-1)

> > > > +#define PRINT_INTERVAL	10	/* interval seconds of printing

> stats

> > */

> > > > +

> > > > +/** Get rid of path in filename - only for unix-type paths using '/'

*/
> > > > +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \

> > > > +			    strrchr((file_name), '/') + 1 :

(file_name))
> > > > +

> > > > +struct l3fwd_pktio_s {

> > > > +	odp_pktio_t pktio;

> > > > +	odph_ethaddr_t mac_addr;

> > > > +	odp_pktin_queue_t ifin[MAX_NB_QUEUE];

> > > > +	odp_pktout_queue_t ifout[MAX_NB_QUEUE];

> > > > +	int nb_rxq;

> > > > +	int nb_txq;

> > > > +	int rxq_idx;

> > > > +	int txq_idx;

> > > > +};

> > > > +

> > > > +struct l3fwd_qconf_s {

> > > > +	uint8_t if_idx;		/* port index */

> > > > +	uint8_t rxq_idx;	/* recv queue index in a port */

> > > > +	uint8_t core_idx;	/* this core should handle traffic */

> > > > +};

> > > > +

> > > > +struct thread_arg_s {

> > > > +	uint64_t packets;

> > > > +	uint64_t rx_drops;

> > > > +	uint64_t tx_drops;

> > > > +	struct {

> > > > +		int used;

> > > > +		int rxq[MAX_NB_QUEUE];

> > > > +		int txq[MAX_NB_QUEUE];

> > > > +	} pktio[MAX_NB_PKTIO];

> > > > +	int thr_idx;

> > > > +	int nb_pktio;

> > > > +};

> > > > +

> > > > +typedef struct {

> > > > +	char *if_names[MAX_NB_PKTIO];

> > > > +	int if_count;

> > > > +	char *route_str[MAX_NB_ROUTE];

> > > > +	int worker_count;

> > > > +	struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS];

> > > > +	int qconf_count;

> > > > +	uint32_t duration; /* seconds to run */

> > > > +	uint8_t hash_mode; /* 1:hash, 0:lpm */

> > > > +	uint8_t dest_mac_changed[MAX_NB_PKTIO]; /* 1: dest mac from

> > > cmdline */

> > > > +} app_args_t;

> > > > +

> > > > +struct {

> > > > +	app_args_t		cmd_args;

> > > > +	struct l3fwd_pktio_s	l3fwd_pktios[MAX_NB_PKTIO];

> > > > +	odph_odpthread_t	l3fwd_workers[MAX_NB_WORKER];

> > > > +	struct thread_arg_s	worker_args[MAX_NB_WORKER];

> > > > +	odph_ethaddr_t		eth_dest_mac[MAX_NB_PKTIO];

> > > > +

> > > > +	/* forward func, hash or lpm */

> > > > +	int (*fwd_func)(odp_packet_t pkt, int sif);

> > > > +} global;

> > > > +

> > > > +/** Global barrier to synchronize main and workers */

> > > > +static odp_barrier_t barrier;

> > > > +static int exit_threads;	/**< Break workers loop if set to 1 */

> > > > +

> > > > +static void print_usage(char *progname);

> > > if you place print_usage() above main there is no need for declaration.

> The

> > > same for other functions like print_info(). Not critical but you can

> > > just remove

> > > that lines and make example shorter.

> > >

> >

> > Will be fixed.

> >

> > > > +static void print_qconf_table(app_args_t *args);

> > > > +static void print_info(char *progname, app_args_t *args);

> > > > +static int print_speed_stats(int num_workers, int duration, int

> timeout);

> > > > +static void parse_cmdline_args(int argc, char *argv[], app_args_t

> *args);

> > > > +static int parse_config(char *cfg_str, app_args_t *args);

> > > > +static void setup_worker_qconf(app_args_t *args);

> > > > +static void setup_fwd_db(void);

> > > > +static int find_port_id_by_name(char *name, app_args_t *args);

> > > > +static int split_string(char *str, int stringlen,

> > > > +			char **tokens, int maxtokens, char delim);

> > > > +

> > > > +static int create_pktio(const char *name, odp_pool_t pool,

> > > > +			struct l3fwd_pktio_s *fwd_pktio)

> > > > +{

> > > > +	odp_pktio_param_t pktio_param;

> > > > +	odp_pktio_t pktio;

> > > > +	odp_pktio_capability_t capa;

> > > > +	int rc;

> > > > +

> > > > +	odp_pktio_param_init(&pktio_param);

> > > > +

> > > > +	pktio = odp_pktio_open(name, pool, &pktio_param);

> > > > +	if (pktio == ODP_PKTIO_INVALID) {

> > > > +		printf("Failed to open %s\n", name);

> > > > +		return -1;

> > > > +	}

> > > > +	fwd_pktio->pktio = pktio;

> > > > +

> > > > +	rc = odp_pktio_capability(pktio, &capa);

> > > > +	if (rc) {

> > > > +		printf("Error: pktio %s: unable to read

capabilities!\n",
> > > > +		       name);

> > > > +

> > > > +		return -1;

> > > > +	}

> > > > +

> > > > +	fwd_pktio->nb_rxq = (int)capa.max_input_queues;

> > > > +	fwd_pktio->nb_txq = (int)capa.max_output_queues;

> > > > +

> > > > +	if (fwd_pktio->nb_rxq > MAX_NB_QUEUE)

> > > > +		fwd_pktio->nb_rxq = MAX_NB_QUEUE;

> > > > +

> > > > +	if (fwd_pktio->nb_txq > MAX_NB_QUEUE)

> > > > +		fwd_pktio->nb_txq = MAX_NB_QUEUE;

> > > > +

> > > > +	return 0;

> > > > +}

> > > > +

> > > > +static void setup_fwd_db(void)

> > > > +{

> > > > +	fwd_db_entry_t *entry;

> > > > +	int if_idx;

> > > > +	app_args_t *args;

> > > > +

> > > > +	args = &global.cmd_args;

> > > > +	if (args->hash_mode)

> > > > +		init_fwd_hash_cache();

> > > > +	else

> > > > +		fib_tbl_init();

> > > > +

> > > > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

{
> > > > +		if_idx = entry->oif_id;

> > > > +		if (!args->hash_mode)

> > > > +			fib_tbl_insert(entry->subnet.addr, if_idx,

> > > > +				       entry->subnet.depth);

> > > > +		if (args->dest_mac_changed[if_idx])

> > > > +			global.eth_dest_mac[if_idx] = entry->dst_mac;

> > > > +		else

> > > > +			entry->dst_mac = global.eth_dest_mac[if_idx];

> > > > +	}

> > > > +}

> > > > +

> > > > +static int l3fwd_pkt_hash(odp_packet_t pkt, int sif)

> > > > +{

> > > > +	fwd_db_entry_t *entry;

> > > > +	ipv4_tuple5_t key;

> > > > +	odph_ethhdr_t *eth;

> > > > +	odph_udphdr_t  *udp;

> > > > +	odph_ipv4hdr_t *ip;

> > > > +	uint32_t len;

> > > > +	int dif;

> > > > +

> > > > +	ip = odp_packet_l3_ptr(pkt, &len);

> > > > +	key.dst_ip = odp_be_to_cpu_32(ip->dst_addr);

> > > > +	key.src_ip = odp_be_to_cpu_32(ip->src_addr);

> > > > +	key.proto = ip->proto;

> > > > +

> > > > +	if (odp_packet_has_udp(pkt) ||

> > > > +	    odp_packet_has_tcp(pkt)) {

> > > > +		/* UDP or TCP*/

> > > > +		void *ptr = odp_packet_l4_ptr(pkt, NULL);

> > > > +

> > > > +		udp = (odph_udphdr_t *)ptr;

> > > > +		key.src_port = odp_be_to_cpu_16(udp->src_port);

> > > > +		key.dst_port = odp_be_to_cpu_16(udp->dst_port);

> > > > +	} else {

> > > > +		key.src_port = 0;

> > > > +		key.dst_port = 0;

> > > > +	}

> > > > +

> > > > +	entry = find_fwd_db_entry(&key);

> > > > +	ip->ttl--;

> > > odp_packet_l3_ptr() can return NULL. And you will have null pointer

> > > deference here for non IP packet.

> > > > +	ip->chksum = odph_ipv4_csum_update(pkt);

> > > > +	eth = odp_packet_l2_ptr(pkt, NULL);

> > > same here eth might be NULL. Better to optimize checks  ifs with

> > > odp_unlikely().

> >

> > Will be fixed.

> >

> > > > +	if (entry) {

> > > > +		eth->src = entry->src_mac;

> > > > +		eth->dst = entry->dst_mac;

> > > > +		dif = entry->oif_id;

> > > > +	} else {

> > > > +		/* no route, send by src port */

> > > > +		eth->dst = eth->src;

> > > > +		dif = sif;

> > > > +	}

> > > > +

> > > > +	return dif;

> > > > +}

> > > > +

> > > > +static int l3fwd_pkt_lpm(odp_packet_t pkt, int sif)

> > > > +{

> > > > +	odph_ipv4hdr_t *ip;

> > > > +	odph_ethhdr_t *eth;

> > > > +	uint32_t len;

> > > > +	int dif;

> > > > +	int ret;

> > > > +

> > > > +	ip = odp_packet_l3_ptr(pkt, &len);

> > > NULL

> > > > +	ip->ttl--;

> > > > +	ip->chksum = odph_ipv4_csum_update(pkt);

> > > > +	eth = odp_packet_l2_ptr(pkt, NULL);

> > > NULL

> > > > +

> > > > +	/* network byte order maybe different from host */

> > > > +	ret = fib_tbl_lookup(odp_be_to_cpu_32(ip->dst_addr), &dif);

> > > > +	if (ret)

> > > > +		dif = sif;

> > > > +

> > > > +	eth->dst = global.eth_dest_mac[dif];

> > > > +	eth->src = global.l3fwd_pktios[dif].mac_addr;

> > > > +

> > > > +	return dif;

> > > > +}

> > > > +

> > > > +/**

> > > > + * Drop packets which input parsing marked as containing errors.

> > > > + *

> > > > + * Frees packets with error and modifies pkt_tbl[] to only contain

> > packets

> > > with

> > > > + * no detected errors.

> > > > + *

> > > > + * @param pkt_tbl  Array of packets

> > > > + * @param num      Number of packets in pkt_tbl[]

> > > > + *

> > > > + * @return Number of packets dropped

> > > > + */

> > > > +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)

> > > > +{

> > > > +	odp_packet_t pkt;

> > > > +	unsigned dropped = 0;

> > > > +	unsigned i, j;

> > > > +

> > > > +	for (i = 0, j = 0; i < num; ++i) {

> > > > +		pkt = pkt_tbl[i];

> > > > +

> > > > +		if (odp_unlikely(odp_packet_has_error(pkt) ||

> > > > +				 !odp_packet_has_ipv4(pkt))) {

> > > > +			odp_packet_free(pkt);

> > > > +			dropped++;

> > > > +		} else if (odp_unlikely(i != j++)) {

> > > > +			pkt_tbl[j - 1] = pkt;

> > > > +		}

> > > > +	}

> > > > +

> > > > +	return dropped;

> > > > +}

> > > > +

> > > > +static void l3fwd_one_queue(uint32_t sif, uint8_t rxq_id, void

> *thr_arg)

> > > > +{

> > > > +	struct l3fwd_pktio_s *port;

> > > > +	odp_packet_t *tbl;

> > > > +	odp_pktout_queue_t outq;

> > > > +	odp_packet_t pkt_tbl[MAX_PKT_BURST];

> > > > +	struct thread_arg_s *arg;

> > > > +	uint8_t txq_id;

> > > > +	int pkts, drop, sent;

> > > > +	int dif, dst_port;

> > > > +	int i;

> > > > +

> > > > +	arg = thr_arg;

> > > > +	port = &global.l3fwd_pktios[sif];

> > > > +	pkts = odp_pktin_recv(port->ifin[rxq_id], pkt_tbl,

MAX_PKT_BURST);
> > > > +	if (pkts <= 0)

> > > > +		return;

> > > > +	arg->packets += pkts;

> > > > +	drop = drop_err_pkts(pkt_tbl, pkts);

> > > > +	pkts -= drop;

> > > > +	arg->rx_drops += drop;

> > >

> > > Increment one value from number of threads might be not good thing to

> do

> > > due to 1) performance 2) atomic operations break.

> > > It's better if each thread has it's own counters and control threads

> > > summaries values.

> >

> > Not catch your idea. Here each thread has its own counters.

> > It will be totaled before exit.

> >

> > >

> > > > +

> > > > +	dif = global.fwd_func(pkt_tbl[0], sif);

> > > > +	tbl = &pkt_tbl[0];

> > > > +	while (pkts) {

> > > > +		dst_port = dif;

> > > > +		for (i = 1; i < pkts; i++) {

> > > > +			dif = global.fwd_func(tbl[i], sif);

> > > > +			if (dif != dst_port)

> > > > +				break;

> > > > +		}

> > > > +		txq_id = arg->pktio[dst_port].txq[rxq_id];

> > > > +		outq = global.l3fwd_pktios[dst_port].ifout[txq_id];

> > > > +		sent = odp_pktout_send(outq, tbl, i);

> > > > +		if (odp_unlikely(sent < i)) {

> > > > +			sent = sent < 0 ? 0 : sent;

> > > > +			odp_packet_free_multi(&tbl[sent], i - sent);

> > > > +			arg->tx_drops += i - sent;

> > > > +		}

> > > > +

> > > > +		if (i < pkts)

> > > > +			tbl += i;

> > > > +

> > > > +		pkts -= i;

> > > > +	}

> > > > +}

> > > > +

> > > > +static int run_worker(void *arg)

> > > > +{

> > > > +	int if_idx, rxq_idx;

> > > > +	struct thread_arg_s *thr_arg = arg;

> > > > +	struct l3fwd_pktio_s *port;

> > > > +

> > > > +	odp_barrier_wait(&barrier);

> > > > +

> > > > +	while (!exit_threads) {

> > > > +		for (if_idx = 0; if_idx < thr_arg->nb_pktio; if_idx++)

{
> > > > +			if (!thr_arg->pktio[if_idx].used ||

> > > > +			    thr_arg->thr_idx == INVALID_ID)

> > > > +				continue;

> > > > +

> > > > +			port = &global.l3fwd_pktios[if_idx];

> > > > +			for (rxq_idx = 0; rxq_idx < port->rxq_idx;

rxq_idx++)
> > > > +				l3fwd_one_queue(if_idx, rxq_idx, arg);

> > > > +		}

> > > > +	}

> > > > +

> > > > +	/* Make sure that latest stat writes are visible to other

threads */
> > > > +	odp_mb_full();

> > > > +

> > > > +	return 0;

> > > > +}

> > > > +

> > > > +static int find_port_id_by_name(char *name, app_args_t *args)

> > > > +{

> > > > +	int i;

> > > > +

> > > > +	if (!name)

> > > > +		return -1;

> > > > +

> > > > +	for (i = 0; i < args->if_count; i++) {

> > > > +		if (!strcmp(name, args->if_names[i]))

> > > > +			return i;

> > > > +	}

> > > > +

> > > > +	return -1;

> > > > +}

> > > > +

> > > > +int main(int argc, char **argv)

> > > > +{

> > > > +	odph_odpthread_t thread_tbl[MAX_NB_WORKER];

> > > > +	odp_pool_t pool;

> > > > +	odp_pool_param_t params;

> > > > +	odp_instance_t instance;

> > > > +	odph_odpthread_params_t thr_params;

> > > > +	odp_cpumask_t cpumask;

> > > > +	int cpu, i, j, nb_worker;

> > > > +	uint8_t mac[ODPH_ETHADDR_LEN];

> > > > +	app_args_t *args;

> > > > +	struct thread_arg_s *thr_arg;

> > > > +	char *oif;

> > > > +	int oid;

> > > > +

> > > > +	if (odp_init_global(&instance, NULL, NULL)) {

> > > > +		printf("Error: ODP global init failed.\n");

> > > > +		exit(1);

> > > > +	}

> > > > +

> > > > +	if (odp_init_local(instance, ODP_THREAD_CONTROL)) {

> > > > +		printf("Error: ODP local init failed.\n");

> > > > +		exit(1);

> > > > +	}

> > > > +

> > > > +	/* Clear global argument and initialize the dest mac as

2:0:0:0:0:x */
> > > > +	memset(&global, 0, sizeof(global));

> > > > +	mac[0] = 2;

> > > > +	for (i = 0; i < MAX_NB_PKTIO; i++) {

> > > > +		mac[ODPH_ETHADDR_LEN - 1] = (uint8_t)i;

> > > > +		memcpy(global.eth_dest_mac[i].addr, mac,

> > > ODPH_ETHADDR_LEN);

> > > > +	}

> > > > +

> > > > +	/* Initialize the thread arguments */

> > > > +	for (i = 0; i < MAX_NB_WORKER; i++) {

> > > > +		thr_arg = &global.worker_args[i];

> > > > +		for (j = 0; j < MAX_NB_PKTIO; j++) {

> > > > +			thr_arg->thr_idx = INVALID_ID;

> > > > +			memset(thr_arg->pktio[j].rxq, INVALID_ID,

> > > > +			       sizeof(thr_arg->pktio[j].rxq));

> > > > +		}

> > > > +	}

> > > > +

> > > > +	/* Parse cmdline arguments */

> > > > +	args = &global.cmd_args;

> > > > +	parse_cmdline_args(argc, argv, args);

> > > > +

> > > > +	/* Init l3fwd table */

> > > > +	init_fwd_db();

> > > > +

> > > > +	/* Add route into table */

> > > > +	for (i = 0; i < MAX_NB_ROUTE; i++) {

> > > > +		if (args->route_str[i]) {

> > > > +			oif = NULL;

> > > > +			create_fwd_db_entry(args->route_str[i], &oif);

> > > > +			oid = find_port_id_by_name(oif, args);

> > > > +			if (oid != -1)

> > > > +				args->dest_mac_changed[oid] = 1;

> > > > +		}

> > > > +	}

> > > > +

> > > > +	print_info(NO_PATH(argv[0]), args);

> > > > +

> > > > +	/* Create packet pool */

> > > > +	odp_pool_param_init(&params);

> > > > +	params.pkt.seg_len = POOL_SEG_LEN;

> > > > +	params.pkt.len     = POOL_SEG_LEN;

> > > > +	params.pkt.num     = POOL_NUM_PKT;

> > > > +	params.type        = ODP_POOL_PACKET;

> > > > +

> > > > +	pool = odp_pool_create("packet pool", &params);

> > > > +

> > > > +	if (pool == ODP_POOL_INVALID) {

> > > > +		printf("Error: packet pool create failed.\n");

> > > > +		exit(1);

> > > > +	}

> > > > +

> > > > +	/* Resolve fwd db*/

> > > > +	for (i = 0; i < args->if_count; i++) {

> > > > +		struct l3fwd_pktio_s *port;

> > > > +		char *if_name;

> > > > +

> > > > +		if_name = args->if_names[i];

> > > > +		port = &global.l3fwd_pktios[i];

> > > > +		if (create_pktio(if_name, pool, port)) {

> > > > +			printf("Error: create pktio %s\n", if_name);

> > > > +			exit(1);

> > > > +		}

> > > > +		odp_pktio_mac_addr(port->pktio, mac,

ODPH_ETHADDR_LEN);
> > > > +		resolve_fwd_db(if_name, i, mac);

> > > > +		memcpy(port->mac_addr.addr, mac, ODPH_ETHADDR_LEN);

> > > > +	}

> > > > +

> > > > +	setup_fwd_db();

> > > > +	dump_fwd_db();

> > > > +

> > > > +	/* Dicide available workers */

> > > > +	nb_worker = MAX_NB_WORKER;

> > > > +	if (args->worker_count)

> > > > +		nb_worker = args->worker_count;

> > > > +	nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker);

> > > > +	args->worker_count = nb_worker;

> > > > +

> > > > +	/* Setup rx and tx queues for each port */

> > > > +	setup_worker_qconf(args);

> > > > +	print_qconf_table(args);

> > > > +

> > > > +	/* Decide ip lookup method */

> > > > +	if (args->hash_mode)

> > > > +		global.fwd_func = l3fwd_pkt_hash;

> > > > +	else

> > > > +		global.fwd_func = l3fwd_pkt_lpm;

> > > > +

> > > > +	/* Start all the available ports */

> > > > +	for (i = 0; i < args->if_count; i++) {

> > > > +		struct l3fwd_pktio_s *port;

> > > > +		char *if_name;

> > > > +		char buf[32];

> > > > +

> > > > +		if_name = args->if_names[i];

> > > > +		port = &global.l3fwd_pktios[i];

> > > > +		/* start pktio */

> > > > +		if (odp_pktio_start(port->pktio)) {

> > > > +			printf("unable to start pktio: %s\n",

if_name);
> > > > +			exit(1);

> > > > +		}

> > > > +

> > > > +		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",

> > > > +			port->mac_addr.addr[0],

> > > > +			port->mac_addr.addr[1],

> > > > +			port->mac_addr.addr[2],

> > > > +			port->mac_addr.addr[3],

> > > > +			port->mac_addr.addr[4],

> > > > +			port->mac_addr.addr[5]);

> > > > +		printf("start pktio: %s, mac %s\n", if_name, buf);

> > > > +	}

> > > > +

> > > > +	odp_barrier_init(&barrier, nb_worker + 1);

> > > > +

> > > > +	memset(&thr_params, 0, sizeof(thr_params));

> > > > +	thr_params.start    = run_worker;

> > > > +	thr_params.thr_type = ODP_THREAD_WORKER;

> > > > +	thr_params.instance = instance;

> > > > +

> > > > +	memset(thread_tbl, 0, sizeof(thread_tbl));

> > > > +	cpu = odp_cpumask_first(&cpumask);

> > > > +	for (i = 0; i < nb_worker; i++) {

> > > > +		struct thread_arg_s *arg;

> > > > +		odp_cpumask_t thr_mask;

> > > > +

> > > > +		arg = &global.worker_args[i];

> > > > +		arg->nb_pktio = args->if_count;

> > > > +		odp_cpumask_zero(&thr_mask);

> > > > +		odp_cpumask_set(&thr_mask, cpu);

> > > > +		thr_params.arg = arg;

> > > > +		odph_odpthreads_create(&thread_tbl[i], &thr_mask,

> > > > +				       &thr_params);

> > > > +		cpu = odp_cpumask_next(&cpumask, cpu);

> > > > +	}

> > > > +

> > > > +	if (args->duration) {

> > > > +		print_speed_stats(nb_worker, args->duration,

> > > PRINT_INTERVAL);

> > > > +		exit_threads = 1;

> > > > +	}

> > > > +

> > > > +	/* wait for other threads to join */

> > > > +	for (i = 0; i < nb_worker; i++)

> > > > +		odph_odpthreads_join(&thread_tbl[i]);

> > > > +

> > >

> > > main should end if odp_destroy_global(). And to add this to 'make check'

> > > requires probably to add some logic when we think that test passed and

> > > when we thing that test failed.

> > > I think you copied print_speed_stats() from l2fwd,  there return code

> > > returns if app passed or not.

> > >

> >

> > The testing PASS depends on the print_speed_stats() output in the log

file.
> > Here return value is not taken  as PASS or FAIL.

> >

> > > > +	return 0;

> > > > +}

> > > > +

> > > > +static void print_usage(char *progname)

> > > > +{

> > > > +	printf("\n"

> > > > +	       "ODP L3 forwarding application.\n"

> > > > +	       "\n"

> > > > +	       "Usage: %s OPTIONS\n"

> > > > +	       "  E.g. %s -i eth0,eth1 -r 1.1.1.0/24,eth0 -r

> > 2.2.2.0/24,eth1\n"

> > > > +	       " In the above example,\n"

> > > > +	       " eth0 will send pkts to eth1 and vice versa\n"

> > > > +	       "\n"

> > > > +	       "Mandatory OPTIONS:\n"

> > > > +	       "  -i, --interface eth interfaces (comma-separated, no

> > spaces)\n"

> > > > +	       "  -r, --route SubNet,Intf[,NextHopMAC]\n"

> > > > +	       "	NextHopMAC can be optional\n"

> > > > +	       "\n"

> > > > +	       "Optional OPTIONS:\n"

> > > > +	       "  -s, --style [lpm|hash], ip lookup method\n"

> > > > +	       "	optional, default as lpm\n"

> > > > +	       "  -d, --duration Seconds to run and print stats\n"

> > > > +	       "	optional, default as 0, run forever\n"

> > > > +	       "  -t, --thread Number of threads to do forwarding\n"

> > > > +	       "	optional, default as availbe worker cpu

count\n"
> > > > +	       "  -q, --queue  Configure rx queue(s) for port\n"

> > > > +	       "	optional, format: [(port, queue,

thread),...]\n"
> > > > +	       "	for example: -q '(0, 0, 1),(1,0,2)'\n"

> > > > +	       "  -h, --help   Display help and exit.\n\n"

> > > > +	       "\n", NO_PATH(progname), NO_PATH(progname)

> > > > +	    );

> > > > +}

> > > > +

> > > > +static void parse_cmdline_args(int argc, char *argv[], app_args_t

> *args)

> > > > +{

> > > > +	int opt;

> > > > +	int long_index;

> > > > +	char *token, *local;

> > > > +	size_t len, route_index = 0;

> > > > +	int i, mem_failure = 0;

> > > > +

> > > > +	static struct option longopts[] = {

> > > > +		{"interface", required_argument, NULL, 'i'},	/*

return 'i'
> > */

> > > > +		{"route", required_argument, NULL, 'r'},	/*

return 'r'
> > */

> > > > +		{"style", optional_argument, NULL, 's'},	/*

return 's'
> > */

> > > > +		{"duration", optional_argument, NULL, 'd'},	/*

return 'd'
> > */

> > > > +		{"thread", optional_argument, NULL, 't'},	/*

return 't'
> > */

> > > > +		{"queue", optional_argument, NULL, 'q'},	/*

return 'q'
> > */

> > > > +		{"help", no_argument, NULL, 'h'},		/*

return 'h'
> > */

> > > > +		{NULL, 0, NULL, 0}

> > > > +	};

> > > > +

> > > > +	while (1) {

> > > > +		opt = getopt_long(argc, argv, "+s:t:d:i:r:q:h",

> > > > +				  longopts, &long_index);

> > > > +

> > > > +		if (opt == -1)

> > > > +			break;	/* No more options */

> > > > +

> > > > +		switch (opt) {

> > > > +		/* parse ip lookup method */

> > > > +		case 's':

> > > > +			if (!strcmp(optarg, "hash"))

> > > > +				args->hash_mode = 1;

> > > > +			break;

> > > > +		/* parse number of worker threads to be run*/

> > > > +		case 't':

> > > > +			i = odp_cpu_count();

> > > > +			args->worker_count = atoi(optarg);

> > > > +			if (args->worker_count > i) {

> > > > +				printf("Too many threads,"

> > > > +				       "truncate to cpu count: %d\n",

i);
> > > > +				args->worker_count = i;

> > > > +			}

> > > > +

> > > > +			break;

> > > > +

> > > > +		/* parse seconds to run */

> > > > +		case 'd':

> > > > +			args->duration = atoi(optarg);

> > > > +			break;

> > > > +

> > > > +		/* parse packet-io interface names */

> > > > +		case 'i':

> > > > +			len = strlen(optarg);

> > > > +			if (len == 0) {

> > > > +				print_usage(argv[0]);

> > > > +				exit(EXIT_FAILURE);

> > > > +			}

> > > > +			len += 1;	/* add room for '\0' */

> > > > +

> > > > +			local = malloc(len);

> > > > +			if (!local) {

> > > > +				print_usage(argv[0]);

> > > > +				exit(EXIT_FAILURE);

> > > > +			}

> > > > +

> > > > +			/* count the number of tokens separated by ','

*/
> > > > +			strcpy(local, optarg);

> > > > +			for (token = strtok(local, ","), i = 0;

> > > > +			     token != NULL;

> > > > +			     token = strtok(NULL, ","), i++)

> > > > +				;

> > > > +

> > > > +			if (i == 0) {

> > > > +				print_usage(argv[0]);

> > > > +				exit(EXIT_FAILURE);

> > > > +			} else if (i > MAX_NB_PKTIO) {

> > > > +				printf("too many ports specified, "

> > > > +				       "truncated to %d",

MAX_NB_PKTIO);
> > > > +			}

> > > > +			args->if_count = i;

> > > > +

> > > > +			/* store the if names (reset names string) */

> > > > +			strcpy(local, optarg);

> > > > +			for (token = strtok(local, ","), i = 0;

> > > > +			     token != NULL; token = strtok(NULL, ","),

i++) {
> > > > +				args->if_names[i] = token;

> > > > +			}

> > > > +			break;

> > > > +

> > > > +		/*Configure Route in forwarding database*/

> > > > +		case 'r':

> > > > +			if (route_index >= MAX_NB_ROUTE) {

> > > > +				printf("No more routes can be

added\n");
> > > > +				break;

> > > > +			}

> > > > +			local = calloc(1, strlen(optarg) + 1);

> > > > +			if (!local) {

> > > > +				mem_failure = 1;

> > > > +				break;

> > > > +			}

> > > > +			memcpy(local, optarg, strlen(optarg));

> > > > +			local[strlen(optarg)] = '\0';

> > > > +			args->route_str[route_index++] = local;

> > > > +			break;

> > > > +

> > > > +		case 'h':

> > > > +			print_usage(argv[0]);

> > > > +			exit(EXIT_SUCCESS);

> > > > +			break;

> > > > +

> > > > +		case 'q':

> > > > +			parse_config(optarg, args);

> > > > +			break;

> > > > +

> > > > +		default:

> > > > +			break;

> > > > +		}

> > > > +	}

> > > > +

> > > > +	/* checking arguments */

> > > > +	if (args->if_count == 0) {

> > > > +		printf("\nNo option -i specified.\n");

> > > > +		goto out;

> > > > +	}

> > > > +

> > > > +	if (args->route_str[0] == NULL) {

> > > > +		printf("\nNo option -r specified.\n");

> > > > +		goto out;

> > > > +	}

> > > > +

> > > > +	if (mem_failure == 1) {

> > > > +		printf("\nAllocate memory failure.\n");

> > > > +		goto out;

> > > > +	}

> > > > +	optind = 1;		/* reset 'extern optind' from the

getopt lib
> > */

> > > > +	return;

> > > > +

> > > > +out:

> > > > +	print_usage(argv[0]);

> > > > +	exit(EXIT_FAILURE);

> > > > +}

> > > > +

> > > > +/* split string into tokens */

> > > > +int split_string(char *str, int stringlen,

> > > > +		 char **tokens, int maxtokens, char delim)

> > > > +{

> > > > +	int i, tok = 0;

> > > > +	int tokstart = 1; /* first token is right at start of string

*/
> > > > +

> > > > +	if (str == NULL || tokens == NULL)

> > > > +		goto einval_error;

> > > > +

> > > > +	for (i = 0; i < stringlen; i++) {

> > > > +		if (str[i] == '\0' || tok >= maxtokens)

> > > > +			break;

> > > > +		if (tokstart) {

> > > > +			tokstart = 0;

> > > > +			tokens[tok++] = &str[i];

> > > > +		}

> > > > +		if (str[i] == delim) {

> > > > +			str[i] = '\0';

> > > > +			tokstart = 1;

> > > > +		}

> > > > +	}

> > > > +	return tok;

> > > > +

> > > > +einval_error:

> > > > +	errno = EINVAL;

> > > > +	return -1;

> > > > +}

> > > > +

> > > > +static int parse_config(char *cfg_str, app_args_t *args)

> > > > +{

> > > > +	char s[256];

> > > > +	const char *p, *p0 = cfg_str;

> > > > +	char *end;

> > > > +	enum fieldnames {

> > > > +		FLD_PORT = 0,

> > > > +		FLD_QUEUE,

> > > > +		FLD_LCORE,

> > > > +		FLD_LAST

> > > > +	};

> > > > +	unsigned long int_fld[FLD_LAST];

> > > > +	char *str_fld[FLD_LAST];

> > > > +	int i;

> > > > +	unsigned size;

> > > > +	int nb_qconfs = 0;

> > > > +	struct l3fwd_qconf_s *qconf_array = &args->qconf_config[0];

> > > > +

> > > > +	p = strchr(p0, '(');

> > > > +	while (p != NULL) {

> > > > +		++p;

> > > > +		p0 = strchr(p, ')');

> > > > +		if (p0 == NULL)

> > > > +			return -1;

> > > > +

> > > > +		size = p0 - p;

> > > > +		if (size >= sizeof(s))

> > > > +			return -1;

> > > > +

> > > > +		snprintf(s, sizeof(s), "%.*s", size, p);

> > > > +		i = split_string(s, sizeof(s), str_fld, FLD_LAST,

',');
> > > > +		if (i != FLD_LAST)

> > > > +			return -1;

> > > > +		for (i = 0; i < FLD_LAST; i++) {

> > > > +			errno = 0;

> > > > +			int_fld[i] = strtoul(str_fld[i], &end, 0);

> > > > +			if (errno != 0 || end == str_fld[i] ||

int_fld[i] >
> > 255)

> > > > +				return -1;

> > > > +		}

> > > > +		if (nb_qconfs >= MAX_NB_QCONFS) {

> > > > +			printf("exceeded max number of queue

> > > params: %hu\n",

> > > > +			       nb_qconfs);

> > > > +			return -1;

> > > > +		}

> > > > +		qconf_array[nb_qconfs].if_idx =

(uint8_t)int_fld[FLD_PORT];
> > > > +		qconf_array[nb_qconfs].rxq_idx =

> > > (uint8_t)int_fld[FLD_QUEUE];

> > > > +		qconf_array[nb_qconfs].core_idx =

> > > (uint8_t)int_fld[FLD_LCORE];

> > > > +		++nb_qconfs;

> > > > +

> > > > +		p = strchr(p0, '(');

> > > > +	}

> > > > +	args->qconf_count = nb_qconfs;

> > > > +

> > > > +	return 0;

> > > > +}

> > > > +

> > > > +static void print_info(char *progname, app_args_t *args)

> > > > +{

> > > > +	int i;

> > > > +

> > > > +	printf("\n"

> > > > +	       "ODP system info\n"

> > > > +	       "---------------\n"

> > > > +	       "ODP API version: %s\n"

> > > > +	       "ODP impl name:	 %s\n"

> > > > +	       "CPU model:       %s\n"

> > > > +	       "CPU freq (hz):   %" PRIu64 "\n"

> > > > +	       "Cache line size: %i\n"

> > > > +	       "CPU count:       %i\n"

> > > > +	       "\n",

> > > > +	       odp_version_api_str(), odp_version_impl_name(),

> > > > +	       odp_cpu_model_str(), odp_cpu_hz_max(),

> > > > +	       odp_sys_cache_line_size(), odp_cpu_count());

> > > > +

> > > > +	printf("Running ODP appl: \"%s\"\n"

> > > > +	       "-----------------\n"

> > > > +	       "IP Lookup:	 %s\n"

> > > > +	       "IF Count:        %i\n"

> > > > +	       "Using IFs:      ",

> > > > +	       progname,

> > > > +	       args->hash_mode ? "hash" : "lpm",

> > > > +	       args->if_count);

> > > > +

> > > > +	for (i = 0; i < args->if_count; ++i)

> > > > +		printf(" %s", args->if_names[i]);

> > > > +

> > > > +	printf("\n\n");

> > > > +	fflush(NULL);

> > > > +}

> > > > +

> > > > +/**

> > > > + * Setup rx and tx queues, distribute them among threads.

> > > > + * Try to have one tx queue for each rx queue, if not vailable,

> > > > + * shared tx queue is used.

> > > > + *

> > > > + * If no q argument, the queues are distribute among threads as

> default.

> > > > + * The thread take one rx queue of a port one time as round-robin

> order.

> > > > + */

> > > > +static void setup_worker_qconf(app_args_t *args)

> > > > +{

> > > > +	int nb_worker, if_count;

> > > > +	int i, j, rxq_idx, txq_idx;

> > > > +	struct thread_arg_s *arg;

> > > > +	struct l3fwd_pktio_s *port;

> > > > +	uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_QUEUE];

> > > > +

> > > > +	nb_worker = args->worker_count;

> > > > +	if_count = args->if_count;

> > > > +

> > > > +	/* distribute queues among threads */

> > > > +	if (!args->qconf_count) {

> > > > +		if (nb_worker > if_count) {

> > > > +			for (i = 0; i < nb_worker; i++) {

> > > > +				arg = &global.worker_args[i];

> > > > +				arg->thr_idx = i;

> > > > +				j = i % if_count;

> > > > +				port = &global.l3fwd_pktios[j];

> > > > +				if (port->rxq_idx < port->nb_rxq) {

> > > > +					rxq_idx = port->rxq_idx;

> > > > +					arg->pktio[j].rxq[rxq_idx] =

rxq_idx;
> > > > +					port->rxq_idx++;

> > > > +					txq_idx = port->txq_idx;

> > > > +					arg->pktio[j].txq[txq_idx] =

txq_idx;
> > > > +					port->txq_idx++;

> > > > +					arg->pktio[j].used = 1;

> > > > +				}

> > > > +			}

> > > > +		} else {

> > > > +			for (i = 0; i < if_count; i++) {

> > > > +				j = i % nb_worker;

> > > > +				arg = &global.worker_args[j];

> > > > +				arg->thr_idx = j;

> > > > +				port = &global.l3fwd_pktios[i];

> > > > +				if (port->rxq_idx < port->nb_rxq) {

> > > > +					rxq_idx = port->rxq_idx;

> > > > +					arg->pktio[i].rxq[rxq_idx] =

rxq_idx;
> > > > +					port->rxq_idx++;

> > > > +					txq_idx = port->txq_idx;

> > > > +					arg->pktio[i].txq[txq_idx] =

txq_idx;
> > > > +					port->txq_idx++;

> > > > +					arg->pktio[i].used = 1;

> > > > +				}

> > > > +			}

> > > > +		}

> > > > +	}

> > > > +

> > > > +	/* specified q argument, distribute queues among threads as it

says */
> > > > +	memset(queue_mask, 0, sizeof(queue_mask));

> > > > +	for (i = 0; i < args->qconf_count; i++) {

> > > > +		struct l3fwd_qconf_s *q;

> > > > +

> > > > +		q = &args->qconf_config[i];

> > > > +		if (q->core_idx >= nb_worker || q->if_idx >= if_count)

> > > > +			LOG_ABORT("Error queue (%d, %d, %d), max port:

"
> > > > +				  "%d, max core: %d\n", q->if_idx,

q->rxq_idx,
> > > > +				  q->core_idx, args->if_count - 1,

> > > > +				  args->worker_count - 1);

> > > > +

> > > > +		/* check if one queue is configured twice or more */

> > > > +		if (queue_mask[q->if_idx][q->rxq_idx])

> > > > +			LOG_ABORT("Error queue (%d, %d, %d), reconfig

> > > queue\n",

> > > > +				  q->if_idx, q->rxq_idx, q->core_idx);

> > > > +		queue_mask[q->if_idx][q->rxq_idx] = 1;

> > > > +

> > > > +		port = &global.l3fwd_pktios[q->if_idx];

> > > > +		if (port->rxq_idx < q->rxq_idx)

> > > > +			LOG_ABORT("Error queue (%d, %d, %d), queue

should
> > > be"

> > > > +				  " in sequence and start from 0,

queue %d\n",
> > > > +				  q->if_idx, q->rxq_idx, q->core_idx,

> > > > +				  q->rxq_idx);

> > > > +

> > > > +		if (q->rxq_idx > port->nb_rxq) {

> > > > +			LOG_ABORT("Error queue (%d, %d, %d), max

> > > queue %d\n",

> > > > +				  q->if_idx, q->rxq_idx, q->core_idx,

> > > > +				  port->nb_rxq - 1);

> > > > +		}

> > > > +		port->rxq_idx = q->rxq_idx + 1;

> > > > +		port->txq_idx = q->rxq_idx + 1;

> > > > +

> > > > +		/* put the queue into worker_args */

> > > > +		arg = &global.worker_args[q->core_idx];

> > > > +		arg->pktio[q->if_idx].rxq[q->rxq_idx] = q->rxq_idx;

> > > > +		arg->pktio[q->if_idx].txq[q->rxq_idx] = q->rxq_idx;

> > > > +		arg->pktio[q->if_idx].used = 1;

> > > > +		arg->thr_idx = q->core_idx;

> > > > +	}

> > > > +

> > > > +	/* config and initialize rx and tx queues. */

> > > > +	for (i = 0; i < if_count; i++) {

> > > > +		odp_pktin_queue_param_t in_queue_param;

> > > > +		odp_pktout_queue_param_t out_queue_param;

> > > > +		struct odp_pktin_queue_t *inq;

> > > > +		struct odp_pktout_queue_t *outq;

> > > > +		const char *name;

> > > > +		int rc, nb_rxq, nb_txq;

> > > > +

> > > > +		port = &global.l3fwd_pktios[i];

> > > > +		name = args->if_names[i];

> > > > +		odp_pktin_queue_param_init(&in_queue_param);

> > > > +		odp_pktout_queue_param_init(&out_queue_param);

> > > > +

> > > > +		in_queue_param.op_mode = ODP_PKTIO_OP_MT;

> > > > +		out_queue_param.op_mode = ODP_PKTIO_OP_MT;

> > > > +

> > > > +		in_queue_param.num_queues = port->rxq_idx;

> > > > +		if (port->rxq_idx > port->nb_rxq) {

> > > > +			in_queue_param.num_queues = port->nb_rxq;

> > > > +			in_queue_param.op_mode =

> > > ODP_PKTIO_OP_MT_UNSAFE;

> > > > +		}

> > > > +

> > > > +		/* enable flow hashing for multi-input queues */

> > > > +		if (in_queue_param.num_queues > 1) {

> > > > +			in_queue_param.hash_enable = 1;

> > > > +			in_queue_param.hash_proto.proto.ipv4_tcp = 1;

> > > > +			in_queue_param.hash_proto.proto.ipv4_udp = 1;

> > > > +		}

> > > > +

> > > > +		if (odp_pktin_queue_config(port->pktio,

&in_queue_param))
> > > > +			LOG_ABORT("Fail to config input queue for

%s\n",
> > > name);

> > > > +

> > > > +		out_queue_param.num_queues = port->txq_idx;

> > > > +		if (port->txq_idx > port->nb_txq) {

> > > > +			out_queue_param.num_queues = port->nb_txq;

> > > > +			out_queue_param.op_mode =

> > > ODP_PKTIO_OP_MT_UNSAFE;

> > > > +		}

> > > > +		if (odp_pktout_queue_config(port->pktio,

> > > &out_queue_param))

> > > > +			LOG_ABORT("Fail to config output queue for

%s\n",
> > > name);

> > > > +

> > > > +		inq = port->ifin;

> > > > +		nb_rxq = in_queue_param.num_queues;

> > > > +		if (odp_pktin_queue(port->pktio, inq, nb_rxq) !=

nb_rxq)
> > > > +			LOG_ABORT("Fail to set pktin queue for %s\n",

name);
> > > > +

> > > > +		if (port->rxq_idx > port->nb_rxq) {

> > > > +			for (rc = port->nb_rxq; rc < port->rxq_idx;

rc++)
> > > > +				inq[rc] = inq[rc % port->nb_rxq];

> > > > +		}

> > > > +

> > > > +		outq = port->ifout;

> > > > +		nb_txq = out_queue_param.num_queues;

> > > > +		if (odp_pktout_queue(port->pktio, outq, nb_txq) !=

nb_txq)
> > > > +			LOG_ABORT("Fail to set pktout queue for %s\n",

> > > name);

> > > > +

> > > > +		if (port->txq_idx > port->nb_txq) {

> > > > +			for (rc = port->nb_txq; rc < port->txq_idx;

rc++)
> > > > +				outq[rc] = outq[rc % port->nb_txq];

> > > > +		}

> > > > +	}

> > > > +}

> > > > +

> > > > +static void print_qconf_table(app_args_t *args)

> > > > +{

> > > > +	int i, j, k, qid;

> > > > +	char buf[32];

> > > > +	struct thread_arg_s *thr_arg;

> > > > +

> > > > +	printf("Rx queue table\n"

> > > > +	       "-----------------\n"

> > > > +	       "%-16s%-16s%-16s\n",

> > > > +	       "port/id", "queue", "thread");

> > > > +

> > > > +	for (i = 0; i < args->worker_count; i++) {

> > > > +		thr_arg = &global.worker_args[i];

> > > > +		for (j = 0; j < args->if_count; j++) {

> > > > +			if (!thr_arg->pktio[j].used)

> > > > +				continue;

> > > > +

> > > > +			sprintf(buf, "%s/%d", args->if_names[j], j);

> > > > +			for (k = 0; k < MAX_NB_QUEUE; k++) {

> > > > +				qid = thr_arg->pktio[j].rxq[k];

> > > > +				if (qid != INVALID_ID)

> > > > +					printf("%-16s%-16d%-16d\n",

buf, qid,
> > > > +					       thr_arg->thr_idx);

> > > > +			}

> > > > +		}

> > > > +	}

> > > > +	printf("\n");

> > > > +	fflush(NULL);

> > > > +}

> > > > +

> > > > +/**

> > > > + *  Print statistics

> > > > + *

> > > > + * @param num_workers Number of worker threads

> > > > + * @param duration Number of seconds to loop in

> > > > + * @param timeout Number of seconds for stats calculation

> > > > + *

> > > > + */

> > > > +static int print_speed_stats(int num_workers, int duration, int

> timeout)

> > > > +{

> > > > +	uint64_t pkts = 0;

> > > > +	uint64_t pkts_prev = 0;

> > > > +	uint64_t pps;

> > > > +	uint64_t rx_drops, tx_drops;

> > > > +	uint64_t maximum_pps = 0;

> > > > +	int i;

> > > > +	int elapsed = 0;

> > > > +	int stats_enabled = 1;

> > > > +	int loop_forever = (duration == 0);

> > > > +

> > > > +	if (timeout <= 0) {

> > > > +		stats_enabled = 0;

> > > > +		timeout = 1;

> > > > +	}

> > > > +	/* Wait for all threads to be ready*/

> > > > +	odp_barrier_wait(&barrier);

> > > > +

> > > > +	do {

> > > > +		pkts = 0;

> > > > +		rx_drops = 0;

> > > > +		tx_drops = 0;

> > > > +		sleep(timeout);

> > > > +

> > > > +		for (i = 0; i < num_workers; i++) {

> > > > +			pkts += global.worker_args[i].packets;

> > > > +			rx_drops += global.worker_args[i].rx_drops;

> > > > +			tx_drops += global.worker_args[i].tx_drops;

> > > > +		}

> > > > +		if (stats_enabled) {

> > > > +			pps = (pkts - pkts_prev) / timeout;

> > > > +			if (pps > maximum_pps)

> > > > +				maximum_pps = pps;

> > > > +			printf("%" PRIu64 " pps, %" PRIu64 " max pps,

",  pps,
> > > > +			       maximum_pps);

> > > > +

> > > > +			printf(" %" PRIu64 " rx drops, %" PRIu64 " tx

> > drops\n",

> > > > +			       rx_drops, tx_drops);

> > > > +

> > > > +			pkts_prev = pkts;

> > > > +		}

> > > > +		elapsed += timeout;

> > > > +	} while (loop_forever || (elapsed < duration));

> > > > +

> > > > +	if (stats_enabled)

> > > > +		printf("TEST RESULT: %" PRIu64 " maximum packets per

> > > second.\n",

> > > > +		       maximum_pps);

> > > > +

> > > > +	return pkts > 100 ? 0 : -1;

> > > > +}

> > > > diff --git a/example/l3fwd/odp_l3fwd_db.c

> > > b/example/l3fwd/odp_l3fwd_db.c

> > > > new file mode 100644

> > > > index 0000000..93e32f0

> > > > --- /dev/null

> > > > +++ b/example/l3fwd/odp_l3fwd_db.c

> > > > @@ -0,0 +1,408 @@

> > > > +/* Copyright (c) 2016, Linaro Limited

> > > > + * All rights reserved.

> > > > + *

> > > > + * SPDX-License-Identifier:     BSD-3-Clause

> > > > + */

> > > > +

> > > > +#ifndef _GNU_SOURCE

> > > > +#define _GNU_SOURCE

> > > > +#endif

> > > > +

> > > > +#include <stdlib.h>

> > > > +#include <string.h>

> > > > +

> > > > +#include <example_debug.h>

> > > > +#include <odp_api.h>

> > > > +#include <odp_l3fwd_db.h>

> > > > +

> > > > +/** Jenkins hash support.

> > > > +  *

> > > > +  * Copyright (C) 2006 Bob Jenkins (bob_jenkins@burtleburtle.net)

> > > > +  *

> > > > +  * http://burtleburtle.net/bob/hash/

> > > > +  *

> > > > +  * These are the credits from Bob's sources:

> > > > +  *

> > > > +  * lookup3.c, by Bob Jenkins, May 2006, Public Domain.

> > > > +  *

> > > > +  * These are functions for producing 32-bit hashes for hash table

> > lookup.

> > > > +  * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and

> > final()

> > > > +  * are externally useful functions.  Routines to test the hash are

> > included

> > > > +  * if SELF_TEST is defined.  You can use this free for any purpose.

> > It's in

> > > > +  * the public domain.  It has no warranty.

> > > > +  *

> > > > +  * $FreeBSD$

> > > > +  */

> > > > +#define JHASH_GOLDEN_RATIO	0x9e3779b9

> > > > +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))

> > > > +#define FWD_BJ3_MIX(a, b, c) \

> > > > +{ \

> > > > +	a -= c; a ^= rot(c, 4); c += b; \

> > > > +	b -= a; b ^= rot(a, 6); a += c; \

> > > > +	c -= b; c ^= rot(b, 8); b += a; \

> > > > +	a -= c; a ^= rot(c, 16); c += b; \

> > > > +	b -= a; b ^= rot(a, 19); a += c; \

> > > > +	c -= b; c ^= rot(b, 4); b += a; \

> > > > +}

> > > > +

> > > > +/**

> > > > + * Compute hash value from a flow

> > > > + */

> > > > +static inline

> > > > +uint64_t l3fwd_calc_hash(ipv4_tuple5_t *key)

> > > > +{

> > > > +	uint64_t l4_ports = 0;

> > > > +	uint32_t dst_ip, src_ip;

> > > > +

> > > > +	src_ip = key->src_ip;

> > > > +	dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO;

> > > > +	FWD_BJ3_MIX(src_ip, dst_ip, l4_ports);

> > > > +

> > > > +	return l4_ports;

> > > > +}

> > > > +

> > > > +/**

> > > > + * Parse text string representing an IPv4 address or subnet

> > > > + *

> > > > + * String is of the format "XXX.XXX.XXX.XXX(/W)" where

> > > > + * "XXX" is decimal value and "/W" is optional subnet length

> > > > + *

> > > > + * @param ipaddress  Pointer to IP address/subnet string to convert

> > > > + * @param addr       Pointer to return IPv4 address, host endianness

> > > > + * @param depth      Pointer to subnet bit width

> > > > + * @return 0 if successful else -1

> > > > + */

> > > > +static inline

> > > > +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t

*depth)
> > > > +{

> > > > +	int b[4];

> > > > +	int qualifier = 32;

> > > > +	int converted;

> > > > +	uint32_t addr_le;

> > > > +

> > > > +	if (strchr(ipaddress, '/')) {

> > > > +		converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",

> > > > +				   &b[3], &b[2], &b[1], &b[0],

> > > > +				   &qualifier);

> > > > +		if (5 != converted)

> > > > +			return -1;

> > > > +	} else {

> > > > +		converted = sscanf(ipaddress, "%d.%d.%d.%d",

> > > > +				   &b[3], &b[2], &b[1], &b[0]);

> > > > +		if (4 != converted)

> > > > +			return -1;

> > > > +	}

> > > > +

> > > > +	if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] >

255))
> > > > +		return -1;

> > > > +	if (!qualifier || (qualifier > 32))

> > > > +		return -1;

> > > > +

> > > > +	addr_le = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;

> > > > +	*addr = odp_le_to_cpu_32(addr_le);

> > > > +	*depth = qualifier;

> > > > +

> > > > +	return 0;

> > > > +}

> > > > +

> > > > +/**

> > > > + * Generate text string representing IPv4 range/subnet, output

> > > > + * in "XXX.XXX.XXX.XXX/W" format

> > > > + *

> > > > + * @param b     Pointer to buffer to store string

> > > > + * @param range Pointer to IPv4 address range

> > > > + *

> > > > + * @return Pointer to supplied buffer

> > > > + */

> > > > +static inline

> > > > +char *ipv4_subnet_str(char *b, ip_addr_range_t *range)

> > > > +{

> > > > +	sprintf(b, "%d.%d.%d.%d/%d",

> > > > +		0xFF & ((range->addr) >> 24),

> > > > +		0xFF & ((range->addr) >> 16),

> > > > +		0xFF & ((range->addr) >>  8),

> > > > +		0xFF & ((range->addr) >>  0),

> > > > +		range->depth);

> > > > +	return b;

> > > > +}

> > > > +

> > > > +/**

> > > > + * Generate text string representing MAC address

> > > > + *

> > > > + * @param b     Pointer to buffer to store string

> > > > + * @param mac   Pointer to MAC address

> > > > + *

> > > > + * @return Pointer to supplied buffer

> > > > + */

> > > > +static inline

> > > > +char *mac_addr_str(char *b, odph_ethaddr_t *mac)

> > > > +{

> > > > +	uint8_t *byte;

> > > > +

> > > > +	byte = mac->addr;

> > > > +	sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",

> > > > +		byte[0], byte[1], byte[2], byte[3], byte[4], byte[5]);

> > > > +	return b;

> > > > +}

> > > > +

> > > > +/**

> > > > + * Flow cache table entry

> > > > + */

> > > > +typedef struct flow_entry_s {

> > > > +	ipv4_tuple5_t key;		/**< match key */

> > > > +	struct flow_entry_s *next;      /**< next entry on the list */

> > > > +	fwd_db_entry_t *fwd_entry;	/**< entry info in db */

> > > > +} flow_entry_t;

> > > > +

> > > > +/**

> > > > + * Flow cache table bucket

> > > > + */

> > > > +typedef struct flow_bucket_s {

> > > > +	odp_spinlock_t		lock;	/**< Bucket lock*/

> > > > +	flow_entry_t		*next;	/**< Pointer to first flow

entry in
> > > bucket*/

> > > > +} flow_bucket_t;

> > > > +

> > > > +/**

> > > > + * Flow hash table, fast lookup cache

> > > > + */

> > > > +typedef struct flow_table_s {

> > > > +	flow_bucket_t *bucket;

> > > > +	uint32_t count;

> > > > +} flow_table_t;

> > > > +

> > > > +static flow_table_t fwd_lookup_cache;

> > > > +

> > > > +void init_fwd_hash_cache(void)

> > > > +{

> > > > +	odp_shm_t		hash_shm;

> > > > +	flow_bucket_t		*bucket;

> > > > +	uint32_t		bucket_count;

> > > > +	uint32_t		i;

> > > > +

> > > > +	bucket_count = FWD_DEF_BUCKET_COUNT;

> > > > +

> > > > +	/*Reserve memory for Routing hash table*/

> > > > +	hash_shm = odp_shm_reserve("route_table",

> > > > +				   sizeof(flow_bucket_t) *

bucket_count,
> > > > +				   ODP_CACHE_LINE_SIZE, 0);

> > > > +

> > > > +	bucket = odp_shm_addr(hash_shm);

> > > > +	if (!bucket) {

> > > > +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> > > > +		exit(-1);

> > > > +	}

> > > > +

> > > > +	fwd_lookup_cache.bucket = bucket;

> > > > +	fwd_lookup_cache.count = bucket_count;

> > > > +

> > > > +	/*Initialize Locks*/

> > > > +	for (i = 0; i < bucket_count; i++) {

> > > > +		bucket = &fwd_lookup_cache.bucket[i];

> > > > +		odp_spinlock_init(&bucket->lock);

> > > > +		bucket->next = NULL;

> > > > +	}

> > > > +}

> > > > +

> > > > +static inline

> > > > +int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow)

> > > > +{

> > > > +	if (key->src_ip == flow->key.src_ip &&

> > > > +	    key->dst_ip == flow->key.dst_ip &&

> > > > +	    key->src_port == flow->key.src_port &&

> > > > +	    key->dst_port == flow->key.dst_port &&

> > > > +	    key->proto == flow->key.proto)

> > > > +		return 1;

> > > > +

> > > > +	return 0;

> > > > +}

> > > > +

> > > > +static inline

> > > > +flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t

> > > *bucket)

> > > > +{

> > > > +	flow_entry_t *rst;

> > > > +

> > > > +	for (rst = bucket->next; rst != NULL; rst = rst->next) {

> > > > +		if (match_key_flow(key, rst))

> > > > +			break;

> > > > +	}

> > > > +

> > > > +	return rst;

> > > > +}

> > > > +

> > > > +static inline

> > > > +flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key,

> > > > +			       flow_bucket_t *bucket,

> > > > +			       fwd_db_entry_t *entry)

> > > > +{

> > > > +	flow_entry_t *flow;

> > > > +

> > > > +	flow = lookup_fwd_cache(key, bucket);

> > > > +	if (flow)

> > > > +		return flow;

> > > > +

> > > > +	flow = malloc(sizeof(flow_entry_t));

> > > > +	flow->key = *key;

> > > > +	flow->fwd_entry = entry;

> > > > +

> > > > +	odp_spinlock_lock(&bucket->lock);

> > > > +	if (!bucket->next) {

> > > > +		bucket->next = flow;

> > > > +	} else {

> > > > +		flow->next = bucket->next;

> > > > +		bucket->next = flow;

> > > > +	}

> > > > +	odp_spinlock_unlock(&bucket->lock);

> > > > +

> > > > +	return flow;

> > > > +}

> > > > +

> > > > +/** Global pointer to fwd db */

> > > > +fwd_db_t *fwd_db;

> > > > +

> > > > +void init_fwd_db(void)

> > > > +{

> > > > +	odp_shm_t shm;

> > > > +

> > > > +	shm = odp_shm_reserve("shm_fwd_db",

> > > > +			      sizeof(fwd_db_t),

> > > > +			      ODP_CACHE_LINE_SIZE,

> > > > +			      0);

> > > > +

> > > > +	fwd_db = odp_shm_addr(shm);

> > > > +

> > > > +	if (fwd_db == NULL) {

> > > > +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> > > > +		exit(EXIT_FAILURE);

> > > > +	}

> > > > +	memset(fwd_db, 0, sizeof(*fwd_db));

> > > > +}

> > > > +

> > > > +int create_fwd_db_entry(char *input, char **oif)

> > > > +{

> > > > +	int pos = 0;

> > > > +	char *local;

> > > > +	char *str;

> > > > +	char *save;

> > > > +	char *token;

> > > > +	fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];

> > > > +

> > > > +	/* Verify we haven't run out of space */

> > > > +	if (MAX_DB <= fwd_db->index)

> > > > +		return -1;

> > > > +

> > > > +	/* Make a local copy */

> > > > +	local = malloc(strlen(input) + 1);

> > > > +	if (NULL == local)

> > > > +		return -1;

> > > > +	strcpy(local, input);

> > > > +

> > > > +	/* Setup for using "strtok_r" to search input string */

> > > > +	str = local;

> > > > +	save = NULL;

> > > > +

> > > > +	/* Parse tokens separated by ':' */

> > > > +	while (NULL != (token = strtok_r(str, ",", &save))) {

> > > > +		str = NULL;  /* reset str for subsequent strtok_r

calls */
> > > > +

> > > > +		/* Parse token based on its position */

> > > > +		switch (pos) {

> > > > +		case 0:

> > > > +			parse_ipv4_string(token,

> > > > +					  &entry->subnet.addr,

> > > > +					  &entry->subnet.depth);

> > > > +			break;

> > > > +		case 1:

> > > > +			strncpy(entry->oif, token, OIF_LEN - 1);

> > > > +			entry->oif[OIF_LEN - 1] = 0;

> > > > +			break;

> > > > +		case 2:

> > > > +			odph_eth_addr_parse(&entry->dst_mac, token);

> > > > +			*oif = entry->oif;

> > > > +			break;

> > > > +

> > > > +		default:

> > > > +			printf("ERROR: extra token \"%s\" at position

%d\n",
> > > > +			       token, pos);

> > > > +			break;

> > > > +		}

> > > > +

> > > > +		/* Advance to next position */

> > > > +		pos++;

> > > > +	}

> > > > +

> > > > +	/* Add route to the list */

> > > > +	fwd_db->index++;

> > > > +	entry->next = fwd_db->list;

> > > > +	fwd_db->list = entry;

> > > > +

> > > > +	free(local);

> > > > +	return 0;

> > > > +}

> > > > +

> > > > +void resolve_fwd_db(char *intf, int portid, uint8_t *mac)

> > > > +{

> > > > +	fwd_db_entry_t *entry;

> > > > +

> > > > +	/* Walk the list and attempt to set output and MAC */

> > > > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

{
> > > > +		if (strcmp(intf, entry->oif))

> > > > +			continue;

> > > > +

> > > > +		entry->oif_id = portid;

> > > > +		memcpy(entry->src_mac.addr, mac, ODPH_ETHADDR_LEN);

> > > > +	}

> > > > +}

> > > > +

> > > > +void dump_fwd_db_entry(fwd_db_entry_t *entry)

> > > > +{

> > > > +	char subnet_str[MAX_STRING];

> > > > +	char mac_str[MAX_STRING];

> > > > +

> > > > +	mac_addr_str(mac_str, &entry->dst_mac);

> > > > +	printf("%-16s%-16s%-16s\n",

> > > > +	       ipv4_subnet_str(subnet_str, &entry->subnet),

> > > > +	       entry->oif, mac_str);

> > > > +}

> > > > +

> > > > +void dump_fwd_db(void)

> > > > +{

> > > > +	fwd_db_entry_t *entry;

> > > > +

> > > > +	printf("Routing table\n"

> > > > +	       "-----------------\n"

> > > > +	       "%-16s%-16s%-16s\n",

> > > > +	       "subnet", "next_hop", "dest_mac");

> > > > +

> > > > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> > > > +		dump_fwd_db_entry(entry);

> > > > +

> > > > +	printf("\n");

> > > > +}

> > > > +

> > > > +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key)

> > > > +{

> > > > +	fwd_db_entry_t *entry;

> > > > +	flow_entry_t *flow;

> > > > +	flow_bucket_t *bucket;

> > > > +	uint64_t hash;

> > > > +

> > > > +	/* first find in cache */

> > > > +	hash = l3fwd_calc_hash(key);

> > > > +	hash &= fwd_lookup_cache.count - 1;

> > > > +	bucket = &fwd_lookup_cache.bucket[hash];

> > > > +	flow = lookup_fwd_cache(key, bucket);

> > > > +	if (flow)

> > > > +		return flow->fwd_entry;

> > > > +

> > > > +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

{
> > > > +		uint32_t mask;

> > > > +

> > > > +		mask = ((1u << entry->subnet.depth) - 1) <<

> > > > +			(32 - entry->subnet.depth);

> > > > +

> > > > +		if (entry->subnet.addr == (key->dst_ip & mask))

> > > > +			break;

> > > > +	}

> > > > +

> > > > +	return entry;

> > > > +}

> > > > diff --git a/example/l3fwd/odp_l3fwd_db.h

> > > b/example/l3fwd/odp_l3fwd_db.h

> > > > new file mode 100644

> > > > index 0000000..840946a

> > > > --- /dev/null

> > > > +++ b/example/l3fwd/odp_l3fwd_db.h

> > > > @@ -0,0 +1,130 @@

> > > > +/* Copyright (c) 2016, Linaro Limited

> > > > + * All rights reserved.

> > > > + *

> > > > + * SPDX-License-Identifier:     BSD-3-Clause

> > > > + */

> > > > +

> > > > +#ifndef ODP_L3FWD_DB_H_

> > > > +#define ODP_L3FWD_DB_H_

> > > > +

> > > > +#ifdef __cplusplus

> > > > +extern "C" {

> > > > +#endif

> > > > +

> > > > +#include <odp_api.h>

> > > > +#include <odp/helper/eth.h>

> > > > +

> > > > +#define OIF_LEN 32

> > > > +#define MAX_DB  32

> > > > +#define MAX_STRING  32

> > > > +

> > > > +/**

> > > > + * Default number of flows

> > > > + */

> > > > +#define FWD_DEF_FLOW_COUNT		100000

> > > > +

> > > > +/**

> > > > + * Default Hash bucket number

> > > > + */

> > > > +#define FWD_DEF_BUCKET_COUNT	(FWD_DEF_FLOW_COUNT / 8)

> > > > +

> > > > +/**

> > > > + * IP address range (subnet)

> > > > + */

> > > > +typedef struct ip_addr_range_s {

> > > > +	uint32_t  addr;     /**< IP address, host endianness */

> > > > +	uint32_t  depth;    /**< subnet bit width */

> > > > +} ip_addr_range_t;

> > > > +

> > > > +/**

> > > > + * TCP/UDP flow

> > > > + */

> > > > +typedef struct ipv4_tuple5_s {

> > > > +	uint32_t src_ip;

> > > > +	uint32_t dst_ip;

> > > > +	uint16_t src_port;

> > > > +	uint16_t dst_port;

> > > > +	uint8_t  proto;

> > > > +} ipv4_tuple5_t;

> > > > +

> > > > +/**

> > > > + * Forwarding data base entry

> > > > + */

> > > > +typedef struct fwd_db_entry_s {

> > > > +	struct fwd_db_entry_s *next;          /**< Next entry on list

*/
> > > > +	char                    oif[OIF_LEN]; /**< Output interface

name */
> > > > +	int			oif_id;	      /**< Output interface

idx */
> > > > +	odph_ethaddr_t		src_mac;      /**< Output source MAC

*/
> > > > +	odph_ethaddr_t		dst_mac;      /**< Output destination

> > > MAC */

> > > > +	ip_addr_range_t		subnet;       /**< Subnet for this

router
> > > */

> > > > +} fwd_db_entry_t;

> > > > +

> > > > +/**

> > > > + * Forwarding data base

> > > > + */

> > > > +typedef struct fwd_db_s {

> > > > +	uint32_t          index;          /**< Next available entry */

> > > > +	fwd_db_entry_t   *list;           /**< List of active routes

*/
> > > > +	fwd_db_entry_t    array[MAX_DB];  /**< Entry storage */

> > > > +} fwd_db_t;

> > > > +

> > > > +/** Global pointer to fwd db */

> > > > +extern fwd_db_t *fwd_db;

> > > > +

> > > > +/**

> > > > + * Initialize FWD DB

> > > > + */

> > > > +void init_fwd_db(void);

> > > > +

> > > > +/**

> > > > + * Initialize forward lookup cache based on hash

> > > > + */

> > > > +void init_fwd_hash_cache(void);

> > > > +

> > > > +/**

> > > > + * Create a forwarding database entry

> > > > + *

> > > > + * String is of the format "SubNet,Intf,NextHopMAC"

> > > > + *

> > > > + * @param input  Pointer to string describing route

> > > > + * @param oif  Pointer to out interface name, as a return value

> > > > + *

> > > > + * @return 0 if successful else -1

> > > > + */

> > > > +int create_fwd_db_entry(char *input, char **oif);

> > > > +

> > > > +/**

> > > > + * Scan FWD DB entries and resolve output queue and source MAC

> > address

> > > > + *

> > > > + * @param intf   Interface name string

> > > > + * @param portid Output queue for packet transmit

> > > > + * @param mac    MAC address of this interface

> > > > + */

> > > > +void resolve_fwd_db(char *intf, int portid, uint8_t *mac);

> > > > +

> > > > +/**

> > > > + * Display one forwarding database entry

> > > > + *

> > > > + * @param entry  Pointer to entry to display

> > > > + */

> > > > +void dump_fwd_db_entry(fwd_db_entry_t *entry);

> > > > +

> > > > +/**

> > > > + * Display the forwarding database

> > > > + */

> > > > +void dump_fwd_db(void);

> > > > +

> > > > +/**

> > > > + * Find a matching forwarding database entry

> > > > + *

> > > > + * @param key  ipv4 tuple

> > > > + *

> > > > + * @return pointer to forwarding DB entry else NULL

> > > > + */

> > > > +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key);

> > > > +

> > > > +#ifdef __cplusplus

> > > > +}

> > > > +#endif

> > > > +

> > > > +#endif

> > > > diff --git a/example/l3fwd/odp_l3fwd_lpm.c

> > > b/example/l3fwd/odp_l3fwd_lpm.c

> > > > new file mode 100644

> > > > index 0000000..1b3bfcf

> > > > --- /dev/null

> > > > +++ b/example/l3fwd/odp_l3fwd_lpm.c

> > > > @@ -0,0 +1,224 @@

> > > > +/* Copyright (c) 2016, Linaro Limited

> > > > + * All rights reserved.

> > > > + *

> > > > + * SPDX-License-Identifier:     BSD-3-Clause

> > > > + */

> > > > +#ifndef _GNU_SOURCE

> > > > +#define _GNU_SOURCE

> > > > +#endif

> > > > +

> > > > +#include <stdio.h>

> > > > +#include <stdlib.h>

> > > > +

> > > > +#include <example_debug.h>

> > > > +#include <odp_api.h>

> > > > +

> > > > +#include <odp_l3fwd_lpm.h>

> > > > +

> > > > +/**

> > > > + * This is a simple implementation of lpm based on patricia tree.

> > > > + *

> > > > + * Tradeoff exists between memory consumption and lookup time.

> > > > + * Currently it prefers 5 levels: {16, 4, 4, 4, 4}, could be 3

> > > > + * levels: {16, 8, 8} by defining FIB_NEXT_STRIDE as 8. Other

> > > > + * levels are also possible.

> > > > + *

> > > > + * the ip here is host endian, when doing init or lookup, the

> > > > + * caller should do endianness conversion if needed.

> > > > + */

> > > > +

> > > > +#define FIB_IP_WIDTH 32

> > > > +#define FIB_FIRST_STRIDE 16

> > > > +#define FIB_NEXT_STRIDE 4

> > > > +#define FIB_NEXT_SIZE (1 << FIB_NEXT_STRIDE)

> > > > +#define FIB_SUB_COUNT 16384

> > > > +#define DEPTH_TO_MASK(depth) ((1 << (depth)) - 1)

> > > > +

> > > > +typedef struct fib_node_s {

> > > > +	union {

> > > > +		uint32_t next_hop;

> > > > +		struct fib_node_s *next; /* next level table */

> > > > +	};

> > > > +	uint8_t valid	:1; /* 1, this node has a valid next hop */

> > > > +	uint8_t end	:1; /* 0, next points to the extended table */

> > > > +	uint8_t depth	:6; /* bit length of subnet mask */

> > > > +} fib_node_t;

> > > > +

> > > > +typedef struct fib_sub_tbl_s {

> > > > +	fib_node_t *fib_nodes;

> > > > +	uint32_t fib_count;

> > > > +	uint32_t fib_idx;

> > > > +} fib_sub_tbl_t;

> > > > +

> > > > +static fib_node_t fib_rt_tbl[1 << FIB_FIRST_STRIDE];

> > > > +static fib_sub_tbl_t fib_lpm_cache;

> > > > +

> > > > +static inline fib_node_t *fib_alloc_sub(void)

> > > > +{

> > > > +	fib_node_t *sub_tbl = NULL;

> > > > +	uint32_t i, nb_entry;

> > > > +

> > > > +	/* extend to next level */

> > > > +	if (fib_lpm_cache.fib_idx < fib_lpm_cache.fib_count) {

> > > > +		nb_entry = (fib_lpm_cache.fib_idx + 1) *

FIB_NEXT_SIZE;
> > > > +		sub_tbl = &fib_lpm_cache.fib_nodes[nb_entry];

> > > > +		fib_lpm_cache.fib_idx++;

> > > > +		for (i = 0; i < nb_entry; i++) {

> > > > +			sub_tbl[i].valid = 0;

> > > > +			sub_tbl[i].end = 1;

> > > > +		}

> > > > +	}

> > > > +

> > > > +	return sub_tbl;

> > > > +}

> > > > +

> > > > +static void fib_update_node(fib_node_t *fe, int port, int depth)

> > > > +{

> > > > +	fib_node_t *p;

> > > > +	int i;

> > > > +

> > > > +	if (fe->end) {

> > > > +		if (!fe->valid) {

> > > > +			fe->depth = depth;

> > > > +			fe->next_hop = port;

> > > > +			fe->valid = 1;

> > > > +		} else if (fe->depth <= depth) {

> > > > +			fe->next_hop = port;

> > > > +			fe->depth = depth;

> > > > +		}

> > > > +

> > > > +		return;

> > > > +	}

> > > > +

> > > > +	for (i = 0; i < FIB_NEXT_SIZE; i++) {

> > > > +		p = &fe->next[i];

> > > > +		if (p->end)

> > > > +			fib_update_node(p, port, depth);

> > > > +	}

> > > > +}

> > > > +

> > > > +static void fib_insert_node(fib_node_t *fe, uint32_t ip, uint32_t

> > next_hop,

> > > > +			    int ip_width, int eat_bits, int depth)

> > > > +{

> > > > +	int i;

> > > > +	uint32_t idx, port;

> > > > +	fib_node_t *p;

> > > > +

> > > > +	if (fe->end) {

> > > > +		port = fe->next_hop;

> > > > +		p = fib_alloc_sub();

> > > > +		if (!p)

> > > > +			return;

> > > > +

> > > > +		fe->next = p;

> > > > +		fe->end = 0;

> > > > +		if (fe->valid) {

> > > > +			for (i = 0; i < FIB_NEXT_SIZE; i++) {

> > > > +				p = &fe->next[i];

> > > > +				p->next_hop = port;

> > > > +				p->depth = fe->depth;

> > > > +			}

> > > > +		}

> > > > +	}

> > > > +	if (depth - eat_bits <= FIB_NEXT_STRIDE) {

> > > > +		ip_width -= depth - eat_bits;

> > > > +		idx = ip >> ip_width;

> > > > +		ip &= DEPTH_TO_MASK(ip_width);

> > > > +		p = &fe->next[idx];

> > > > +		fib_update_node(p, next_hop, depth);

> > > > +	} else {

> > > > +		ip_width -= FIB_NEXT_STRIDE;

> > > > +		idx = ip >> ip_width;

> > > > +		p = &fe->next[idx];

> > > > +		ip &= DEPTH_TO_MASK(ip_width);

> > > > +		eat_bits += FIB_NEXT_STRIDE;

> > > > +		fib_insert_node(p, ip, next_hop, ip_width, eat_bits,

depth);
> > > > +	}

> > > > +}

> > > > +

> > > > +void fib_tbl_init(void)

> > > > +{

> > > > +	int i;

> > > > +	fib_node_t *fe;

> > > > +	uint32_t size;

> > > > +	odp_shm_t lpm_shm;

> > > > +

> > > > +	for (i = 0; i < (1 << FIB_FIRST_STRIDE); i++) {

> > > > +		fe = &fib_rt_tbl[i];

> > > > +		fe->valid = 0;

> > > > +		fe->end = 1;

> > > > +		fe->depth = 0;

> > > > +		fe->next_hop = 0;

> > > > +	}

> > > > +

> > > > +	size = FIB_NEXT_SIZE * FIB_SUB_COUNT;

> > > > +	/*Reserve memory for Routing hash table*/

> > > > +	lpm_shm = odp_shm_reserve("fib_lpm_sub", size,

> > > ODP_CACHE_LINE_SIZE, 0);

> > > > +	fe = odp_shm_addr(lpm_shm);

> > > > +	if (!fe) {

> > > > +		EXAMPLE_ERR("Error: shared mem alloc failed for lpm

> > > cache.\n");

> > > > +		exit(-1);

> > > > +	}

> > > > +

> > > > +	fib_lpm_cache.fib_nodes = fe;

> > > > +	fib_lpm_cache.fib_count = FIB_SUB_COUNT;

> > > > +	fib_lpm_cache.fib_idx = 0;

> > > > +}

> > > > +

> > > > +void fib_tbl_insert(uint32_t ip, int port, int depth)

> > > > +{

> > > > +	fib_node_t *fe, *p;

> > > > +	uint32_t idx;

> > > > +	int i, j;

> > > > +	int nb_bits;

> > > > +

> > > > +	nb_bits = FIB_FIRST_STRIDE;

> > > > +	idx = ip >> nb_bits;

> > > > +	fe = &fib_rt_tbl[idx];

> > > > +	if (depth <= nb_bits) {

> > > > +		if (fe->end) {

> > > > +			fe->next_hop = port;

> > > > +			fe->depth = depth;

> > > > +			fe->valid = 1;

> > > > +			return;

> > > > +		}

> > > > +

> > > > +		for (i = 0; i < FIB_NEXT_SIZE; i++) {

> > > > +			p = &fe->next[i];

> > > > +			if (p->end)

> > > > +				fib_update_node(p, port, depth);

> > > > +			else

> > > > +				for (j = 0; j < FIB_NEXT_SIZE; j++)

> > > > +					fib_update_node(&p->next[j],

port,
> > > > +							depth);

> > > > +		}

> > > > +

> > > > +		return;

> > > > +	}

> > > > +

> > > > +	/* need to check sub table */

> > > > +	ip &= DEPTH_TO_MASK(FIB_IP_WIDTH - nb_bits);

> > > > +	fib_insert_node(fe, ip, port, FIB_IP_WIDTH - nb_bits, nb_bits,

depth);
> > > > +}

> > > > +

> > > > +int fib_tbl_lookup(uint32_t ip, int *port)

> > > > +{

> > > > +	fib_node_t *fe;

> > > > +	uint32_t idx;

> > > > +	int nb_bits;

> > > > +

> > > > +	nb_bits = FIB_IP_WIDTH - FIB_FIRST_STRIDE;

> > > > +	idx = ip >> nb_bits;

> > > > +	fe = &fib_rt_tbl[idx];

> > > > +

> > > > +	ip &= DEPTH_TO_MASK(nb_bits);

> > > > +	while (!fe->end) {

> > > > +		nb_bits -= FIB_NEXT_STRIDE;

> > > > +		idx = ip >> nb_bits;

> > > > +		fe = &fe->next[idx];

> > > > +		ip &= DEPTH_TO_MASK(nb_bits);

> > > > +	}

> > > > +	*port = fe->next_hop;

> > > > +

> > > > +	return fe->valid ? 0 : -1;

> > > > +}

> > > > diff --git a/example/l3fwd/odp_l3fwd_lpm.h

> > > b/example/l3fwd/odp_l3fwd_lpm.h

> > > > new file mode 100644

> > > > index 0000000..8f78e39

> > > > --- /dev/null

> > > > +++ b/example/l3fwd/odp_l3fwd_lpm.h

> > > > @@ -0,0 +1,20 @@

> > > > +/* Copyright (c) 2016, Linaro Limited

> > > > + * All rights reserved.

> > > > + *

> > > > + * SPDX-License-Identifier:     BSD-3-Clause

> > > > + */

> > > > +

> > > > +#ifndef ODP_L3FWD_LPM_H_

> > > > +#define ODP_L3FWD_LPM_H_

> > > it has to be _ODP_EXAMPLE_L3FWD_LPM_H

> > >

> > > i.e. define should be started with "_" meaning that it's not odp api.

> > > (I know that in some places we use _ODP_ in other places we use ODP_ for

> > > that

> > > case. But that is wrong. We should not overlap with api name space.).

> > >

> > > > +

> > > > +#ifdef __cplusplus

> > > > +extern "C" {

> > > > +#endif

> > > > +void fib_tbl_init(void);

> > > > +void fib_tbl_insert(uint32_t ip, int port, int depth);

> > > > +int fib_tbl_lookup(uint32_t ip, int *port);

> > > > +#ifdef __cplusplus

> > > > +}

> > > > +#endif

> > > > +

> > > > +#endif

> > > > diff --git a/example/m4/configure.m4 b/example/m4/configure.m4

> > > > index bbda38f..78ef396 100644

> > > > --- a/example/m4/configure.m4

> > > > +++ b/example/m4/configure.m4

> > > > @@ -19,5 +19,6 @@ AC_CONFIG_FILES([example/classifier/Makefile

> > > >   		 example/timer/Makefile

> > > >   		 example/traffic_mgmt/Makefile

> > > >   		 example/l2fwd_simple/Makefile

> > > > +		 example/l3fwd/Makefile

> > > >   		 example/switch/Makefile

> > > >   		 example/hello/Makefile])

> > >

> > > On thought is what we need with l2fwd. We places it under

> > > test/performance/ but it's actual example app.

> > > Not sure that it's clear for new users. Definitely l2fwd and l3fwd have

> > > to be at the same level. I'm thinking

> > > about placing both to test/performance/ and create symlink with some

> > > README to example/. But I'm not sure

> > > about that, we need to discuss.

> >

> > Yes.  In my opinion, we do not need a test/performance directory.

> > If want to benchmarking automatically, we can build all the apps but run

> only

> > interested one.

> >

> > > Maxim.

> > >

> >

>
Maxim Uvarov July 25, 2016, 12:44 p.m. UTC | #6
On 07/25/16 11:55, forrest.shi wrote:
> Hi Maxim,

>

> odp_packet_l3_ptr() can return NULL. And you will have null pointer

> deference here for non IP packet.

> +	ip->chksum = odph_ipv4_csum_update(pkt);

> +	eth = odp_packet_l2_ptr(pkt, NULL);

> same here eth might be NULL. Better to optimize checks  ifs with

> odp_unlikely().

> =====================================================

> Before called this function, drop_err_pkts(...) has filtered bad packet first.

> So looks like no necessary re-check l2/l3 pointer is NULL.

>

> Thanks,

> Forrest

ah, ok, if you already dropped bad packets that it's fine.

Maxim.


>

>> -----Original Message-----

>> From: forrest.shi [mailto:forrest.shi@linaro.org]

>> Sent: Monday, July 25, 2016 16:06

>> To: 'Maxim Uvarov' <maxim.uvarov@linaro.org>

>> Cc: lng-odp@lists.linaro.org

>> Subject: RE: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

>>

>> Hi Maxim,

>>

>> Any comments?

>>

>> Thanks,

>> Forrest

>>

>>> -----Original Message-----

>>> From: forrest.shi [mailto:forrest.shi@linaro.org]

>>> Sent: Friday, July 15, 2016 16:22

>>> To: 'Maxim Uvarov' <maxim.uvarov@linaro.org>

>>> Cc: lng-odp@lists.linaro.org

>>> Subject: RE: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

>>>

>>> Hi Maxim,

>>>

>>> The patch is length.  I extracted the comments I have some question as

>> below,

>>> Others will be fixed.

>>>

>>>   Increment one value from number of threads might be not good thing to do

>>>   due to 1) performance 2) atomic operations break.

>>>   It's better if each thread has it's own counters and control threads

>>>   summaries values.

>>> ==========================================================

>>> Not catch your idea. Here each thread has its own counters and is totaled

>>> before main exit.

>>>

>>>

>>>   main should end if odp_destroy_global(). And to add this to 'make check'

>>>   requires probably to add some logic when we think that test passed and

>>>   when we thing that test failed.

>>>   I think you copied print_speed_stats() from l2fwd,  there return code

>>>   returns if app passed or not.

>>> ==========================================================

>>> The testing PASS depends on the print_speed_stats() output that is used by

>>> test shell script.

>>> Here return value is not taken  as PASS or FAIL.

>>>

>>>

>>> On thought is what we need with l2fwd. We places it under

>>>   test/performance/ but it's actual example app.

>>>   Not sure that it's clear for new users. Definitely l2fwd and l3fwd have

>>>   to be at the same level. I'm thinking

>>>   about placing both to test/performance/ and create symlink with some

>>>   README to example/. But I'm not sure

>>>   about that, we need to discuss.

>>> ==========================================================

>>> In my opinion, don't need to put app in performance.

>>>  From literal meaning, test should handle testing things, no new app.

>>>

>>> Thanks,

>>> Forrest

>>>

>>>> -----Original Message-----

>>>> From: lng-odp [mailto:lng-odp-bounces@lists.linaro.org] On Behalf Of

>> Maxim

>>>> Uvarov

>>>> Sent: Wednesday, July 13, 2016 17:10

>>>> To: lng-odp@lists.linaro.org

>>>> Subject: Re: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

>>>>

>>>> On 07/13/16 10:29, forrest.shi@linaro.org wrote:

>>>>> From: Xuelin Shi <forrest.shi@linaro.org>

>>>>>

>>>>> multi-thread, multi-queues and bi-directional forwarding.

>>>>>

>>>>> support (port, queue, thread) arguments in cmdline which specify how

>>>>> the threads handle which rx queue at which port, if no this argument,

>>>>> default specification used.

>>>>>

>>>>> both hash and lpm based lookup methods are supported, default lpm.

>>>>>

>>>>> Signed-off-by: Xuelin Shi <forrest.shi@linaro.org>

>>>>> ---

>>>>>    example/Makefile.am           |    2 +-

>>>>>    example/l3fwd/.gitignore      |    4 +

>>>>>    example/l3fwd/Makefile.am     |   18 +

>>>>>    example/l3fwd/odp_l3fwd.c     | 1072

>>>> +++++++++++++++++++++++++++++++++++++++++

>>>>>    example/l3fwd/odp_l3fwd_db.c  |  408 ++++++++++++++++

>>>>>    example/l3fwd/odp_l3fwd_db.h  |  130 +++++

>>>>>    example/l3fwd/odp_l3fwd_lpm.c |  224 +++++++++

>>>>>    example/l3fwd/odp_l3fwd_lpm.h |   20 +

>>>>>    example/m4/configure.m4       |    1 +

>>>>>    9 files changed, 1878 insertions(+), 1 deletion(-)

>>>>>    create mode 100644 example/l3fwd/.gitignore

>>>>>    create mode 100644 example/l3fwd/Makefile.am

>>>>>    create mode 100644 example/l3fwd/odp_l3fwd.c

>>>>>    create mode 100644 example/l3fwd/odp_l3fwd_db.c

>>>>>    create mode 100644 example/l3fwd/odp_l3fwd_db.h

>>>>>    create mode 100644 example/l3fwd/odp_l3fwd_lpm.c

>>>>>    create mode 100644 example/l3fwd/odp_l3fwd_lpm.h

>>>>>

>>>>> diff --git a/example/Makefile.am b/example/Makefile.am

>>>>> index 37542af..1f1b62e 100644

>>>>> --- a/example/Makefile.am

>>>>> +++ b/example/Makefile.am

>>>>> @@ -1 +1 @@

>>>>> -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

>>>> l2fwd_simple switch hello

>>>>> +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

>>>> l2fwd_simple switch hello l3fwd

>>>>

>>>> Hello Forest, you should use one git send-email command for both

>>>> patches. So 2 patches will be in the same email thread,

>>>> and not as 2 separate patches.

>>>>

>>>> alphabetical order here is required. And in next update please place all

>>>> entries vertically, newer patches will be simpler.

>>>>

>>> OK, will be fixed.

>>>

>>>>> diff --git a/example/l3fwd/.gitignore b/example/l3fwd/.gitignore

>>>>> new file mode 100644

>>>>> index 0000000..4a25ae8

>>>>> --- /dev/null

>>>>> +++ b/example/l3fwd/.gitignore

>>>>> @@ -0,0 +1,4 @@

>>>>> +odp_l3fwd

>>>>> +Makefile

>>>>> +Makefile.in

>>>>> +*.o

>>>> last 3 entries are not needed due to we already have it in common .git

>>>> ignore file.

>>>>

>>>>> diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am

>>>>> new file mode 100644

>>>>> index 0000000..5092aa7

>>>>> --- /dev/null

>>>>> +++ b/example/l3fwd/Makefile.am

>>>>> @@ -0,0 +1,18 @@

>>>>> +include $(top_srcdir)/example/Makefile.inc

>>>>> +

>>>>> +bin_PROGRAMS = odp_l3fwd$(EXEEXT)

>>>>> +odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static

>>>>> +odp_l3fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example -

>>>> I${top_srcdir}/test

>>>>> +

>>>>> +noinst_HEADERS = \

>>>>> +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \

>>>>> +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_lpm.h \

>>>>> +		  $(top_srcdir)/example/example_debug.h

>>>>> +

>>>>> +dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c

>>> odp_l3fwd_lpm.c

>>>>> +

>>>>> +if test_example

>>>>> +TESTS = odp_l3fwd_run.sh

>>>>> +endif

>>>>> +

>>>>> +EXTRA_DIST = odp_l3fwd_run.sh

>>>>> diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c

>>>>> new file mode 100644

>>>>> index 0000000..a10ca76

>>>>> --- /dev/null

>>>>> +++ b/example/l3fwd/odp_l3fwd.c

>>>>> @@ -0,0 +1,1072 @@

>>>>> +/* Copyright (c) 2016, Linaro Limited

>>>>> + * All rights reserved.

>>>>> + *

>>>>> + * SPDX-License-Identifier:     BSD-3-Clause

>>>>> + */

>>>>> +

>>>>> +#include <stdlib.h>

>>>>> +#include <stdio.h>

>>>>> +#include <errno.h>

>>>>> +#include <getopt.h>

>>>>> +#include <unistd.h>

>>>>> +#include <inttypes.h>

>>>>> +

>>>>> +#include <test_debug.h>

>>>>> +

>>>>> +#include <odp_api.h>

>>>>> +#include <odp/helper/linux.h>

>>>>> +#include <odp/helper/eth.h>

>>>>> +#include <odp/helper/ip.h>

>>>>> +#include <odp/helper/udp.h>

>>>>> +#include <odp/helper/tcp.h>

>>>>> +

>>>>> +#include "odp_l3fwd_db.h"

>>>>> +#include "odp_l3fwd_lpm.h"

>>>>> +

>>>>> +#define POOL_NUM_PKT	8192

>>>>> +#define POOL_SEG_LEN	1856

>>>>> +#define MAX_PKT_BURST	32

>>>>> +

>>>>> +#define MAX_NB_WORKER	32

>>>>> +#define MAX_NB_PKTIO	32

>>>>> +#define MAX_NB_QUEUE	32

>>>>> +#define MAX_NB_QCONFS	1024

>>>>> +#define MAX_NB_ROUTE	32

>>>>> +

>>>>> +#define INVALID_ID	(-1)

>>>>> +#define PRINT_INTERVAL	10	/* interval seconds of printing

>> stats

>>> */

>>>>> +

>>>>> +/** Get rid of path in filename - only for unix-type paths using '/'

> */

>>>>> +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \

>>>>> +			    strrchr((file_name), '/') + 1 :

> (file_name))

>>>>> +

>>>>> +struct l3fwd_pktio_s {

>>>>> +	odp_pktio_t pktio;

>>>>> +	odph_ethaddr_t mac_addr;

>>>>> +	odp_pktin_queue_t ifin[MAX_NB_QUEUE];

>>>>> +	odp_pktout_queue_t ifout[MAX_NB_QUEUE];

>>>>> +	int nb_rxq;

>>>>> +	int nb_txq;

>>>>> +	int rxq_idx;

>>>>> +	int txq_idx;

>>>>> +};

>>>>> +

>>>>> +struct l3fwd_qconf_s {

>>>>> +	uint8_t if_idx;		/* port index */

>>>>> +	uint8_t rxq_idx;	/* recv queue index in a port */

>>>>> +	uint8_t core_idx;	/* this core should handle traffic */

>>>>> +};

>>>>> +

>>>>> +struct thread_arg_s {

>>>>> +	uint64_t packets;

>>>>> +	uint64_t rx_drops;

>>>>> +	uint64_t tx_drops;

>>>>> +	struct {

>>>>> +		int used;

>>>>> +		int rxq[MAX_NB_QUEUE];

>>>>> +		int txq[MAX_NB_QUEUE];

>>>>> +	} pktio[MAX_NB_PKTIO];

>>>>> +	int thr_idx;

>>>>> +	int nb_pktio;

>>>>> +};

>>>>> +

>>>>> +typedef struct {

>>>>> +	char *if_names[MAX_NB_PKTIO];

>>>>> +	int if_count;

>>>>> +	char *route_str[MAX_NB_ROUTE];

>>>>> +	int worker_count;

>>>>> +	struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS];

>>>>> +	int qconf_count;

>>>>> +	uint32_t duration; /* seconds to run */

>>>>> +	uint8_t hash_mode; /* 1:hash, 0:lpm */

>>>>> +	uint8_t dest_mac_changed[MAX_NB_PKTIO]; /* 1: dest mac from

>>>> cmdline */

>>>>> +} app_args_t;

>>>>> +

>>>>> +struct {

>>>>> +	app_args_t		cmd_args;

>>>>> +	struct l3fwd_pktio_s	l3fwd_pktios[MAX_NB_PKTIO];

>>>>> +	odph_odpthread_t	l3fwd_workers[MAX_NB_WORKER];

>>>>> +	struct thread_arg_s	worker_args[MAX_NB_WORKER];

>>>>> +	odph_ethaddr_t		eth_dest_mac[MAX_NB_PKTIO];

>>>>> +

>>>>> +	/* forward func, hash or lpm */

>>>>> +	int (*fwd_func)(odp_packet_t pkt, int sif);

>>>>> +} global;

>>>>> +

>>>>> +/** Global barrier to synchronize main and workers */

>>>>> +static odp_barrier_t barrier;

>>>>> +static int exit_threads;	/**< Break workers loop if set to 1 */

>>>>> +

>>>>> +static void print_usage(char *progname);

>>>> if you place print_usage() above main there is no need for declaration.

>> The

>>>> same for other functions like print_info(). Not critical but you can

>>>> just remove

>>>> that lines and make example shorter.

>>>>

>>> Will be fixed.

>>>

>>>>> +static void print_qconf_table(app_args_t *args);

>>>>> +static void print_info(char *progname, app_args_t *args);

>>>>> +static int print_speed_stats(int num_workers, int duration, int

>> timeout);

>>>>> +static void parse_cmdline_args(int argc, char *argv[], app_args_t

>> *args);

>>>>> +static int parse_config(char *cfg_str, app_args_t *args);

>>>>> +static void setup_worker_qconf(app_args_t *args);

>>>>> +static void setup_fwd_db(void);

>>>>> +static int find_port_id_by_name(char *name, app_args_t *args);

>>>>> +static int split_string(char *str, int stringlen,

>>>>> +			char **tokens, int maxtokens, char delim);

>>>>> +

>>>>> +static int create_pktio(const char *name, odp_pool_t pool,

>>>>> +			struct l3fwd_pktio_s *fwd_pktio)

>>>>> +{

>>>>> +	odp_pktio_param_t pktio_param;

>>>>> +	odp_pktio_t pktio;

>>>>> +	odp_pktio_capability_t capa;

>>>>> +	int rc;

>>>>> +

>>>>> +	odp_pktio_param_init(&pktio_param);

>>>>> +

>>>>> +	pktio = odp_pktio_open(name, pool, &pktio_param);

>>>>> +	if (pktio == ODP_PKTIO_INVALID) {

>>>>> +		printf("Failed to open %s\n", name);

>>>>> +		return -1;

>>>>> +	}

>>>>> +	fwd_pktio->pktio = pktio;

>>>>> +

>>>>> +	rc = odp_pktio_capability(pktio, &capa);

>>>>> +	if (rc) {

>>>>> +		printf("Error: pktio %s: unable to read

> capabilities!\n",

>>>>> +		       name);

>>>>> +

>>>>> +		return -1;

>>>>> +	}

>>>>> +

>>>>> +	fwd_pktio->nb_rxq = (int)capa.max_input_queues;

>>>>> +	fwd_pktio->nb_txq = (int)capa.max_output_queues;

>>>>> +

>>>>> +	if (fwd_pktio->nb_rxq > MAX_NB_QUEUE)

>>>>> +		fwd_pktio->nb_rxq = MAX_NB_QUEUE;

>>>>> +

>>>>> +	if (fwd_pktio->nb_txq > MAX_NB_QUEUE)

>>>>> +		fwd_pktio->nb_txq = MAX_NB_QUEUE;

>>>>> +

>>>>> +	return 0;

>>>>> +}

>>>>> +

>>>>> +static void setup_fwd_db(void)

>>>>> +{

>>>>> +	fwd_db_entry_t *entry;

>>>>> +	int if_idx;

>>>>> +	app_args_t *args;

>>>>> +

>>>>> +	args = &global.cmd_args;

>>>>> +	if (args->hash_mode)

>>>>> +		init_fwd_hash_cache();

>>>>> +	else

>>>>> +		fib_tbl_init();

>>>>> +

>>>>> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> {

>>>>> +		if_idx = entry->oif_id;

>>>>> +		if (!args->hash_mode)

>>>>> +			fib_tbl_insert(entry->subnet.addr, if_idx,

>>>>> +				       entry->subnet.depth);

>>>>> +		if (args->dest_mac_changed[if_idx])

>>>>> +			global.eth_dest_mac[if_idx] = entry->dst_mac;

>>>>> +		else

>>>>> +			entry->dst_mac = global.eth_dest_mac[if_idx];

>>>>> +	}

>>>>> +}

>>>>> +

>>>>> +static int l3fwd_pkt_hash(odp_packet_t pkt, int sif)

>>>>> +{

>>>>> +	fwd_db_entry_t *entry;

>>>>> +	ipv4_tuple5_t key;

>>>>> +	odph_ethhdr_t *eth;

>>>>> +	odph_udphdr_t  *udp;

>>>>> +	odph_ipv4hdr_t *ip;

>>>>> +	uint32_t len;

>>>>> +	int dif;

>>>>> +

>>>>> +	ip = odp_packet_l3_ptr(pkt, &len);

>>>>> +	key.dst_ip = odp_be_to_cpu_32(ip->dst_addr);

>>>>> +	key.src_ip = odp_be_to_cpu_32(ip->src_addr);

>>>>> +	key.proto = ip->proto;

>>>>> +

>>>>> +	if (odp_packet_has_udp(pkt) ||

>>>>> +	    odp_packet_has_tcp(pkt)) {

>>>>> +		/* UDP or TCP*/

>>>>> +		void *ptr = odp_packet_l4_ptr(pkt, NULL);

>>>>> +

>>>>> +		udp = (odph_udphdr_t *)ptr;

>>>>> +		key.src_port = odp_be_to_cpu_16(udp->src_port);

>>>>> +		key.dst_port = odp_be_to_cpu_16(udp->dst_port);

>>>>> +	} else {

>>>>> +		key.src_port = 0;

>>>>> +		key.dst_port = 0;

>>>>> +	}

>>>>> +

>>>>> +	entry = find_fwd_db_entry(&key);

>>>>> +	ip->ttl--;

>>>> odp_packet_l3_ptr() can return NULL. And you will have null pointer

>>>> deference here for non IP packet.

>>>>> +	ip->chksum = odph_ipv4_csum_update(pkt);

>>>>> +	eth = odp_packet_l2_ptr(pkt, NULL);

>>>> same here eth might be NULL. Better to optimize checks  ifs with

>>>> odp_unlikely().

>>> Will be fixed.

>>>

>>>>> +	if (entry) {

>>>>> +		eth->src = entry->src_mac;

>>>>> +		eth->dst = entry->dst_mac;

>>>>> +		dif = entry->oif_id;

>>>>> +	} else {

>>>>> +		/* no route, send by src port */

>>>>> +		eth->dst = eth->src;

>>>>> +		dif = sif;

>>>>> +	}

>>>>> +

>>>>> +	return dif;

>>>>> +}

>>>>> +

>>>>> +static int l3fwd_pkt_lpm(odp_packet_t pkt, int sif)

>>>>> +{

>>>>> +	odph_ipv4hdr_t *ip;

>>>>> +	odph_ethhdr_t *eth;

>>>>> +	uint32_t len;

>>>>> +	int dif;

>>>>> +	int ret;

>>>>> +

>>>>> +	ip = odp_packet_l3_ptr(pkt, &len);

>>>> NULL

>>>>> +	ip->ttl--;

>>>>> +	ip->chksum = odph_ipv4_csum_update(pkt);

>>>>> +	eth = odp_packet_l2_ptr(pkt, NULL);

>>>> NULL

>>>>> +

>>>>> +	/* network byte order maybe different from host */

>>>>> +	ret = fib_tbl_lookup(odp_be_to_cpu_32(ip->dst_addr), &dif);

>>>>> +	if (ret)

>>>>> +		dif = sif;

>>>>> +

>>>>> +	eth->dst = global.eth_dest_mac[dif];

>>>>> +	eth->src = global.l3fwd_pktios[dif].mac_addr;

>>>>> +

>>>>> +	return dif;

>>>>> +}

>>>>> +

>>>>> +/**

>>>>> + * Drop packets which input parsing marked as containing errors.

>>>>> + *

>>>>> + * Frees packets with error and modifies pkt_tbl[] to only contain

>>> packets

>>>> with

>>>>> + * no detected errors.

>>>>> + *

>>>>> + * @param pkt_tbl  Array of packets

>>>>> + * @param num      Number of packets in pkt_tbl[]

>>>>> + *

>>>>> + * @return Number of packets dropped

>>>>> + */

>>>>> +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)

>>>>> +{

>>>>> +	odp_packet_t pkt;

>>>>> +	unsigned dropped = 0;

>>>>> +	unsigned i, j;

>>>>> +

>>>>> +	for (i = 0, j = 0; i < num; ++i) {

>>>>> +		pkt = pkt_tbl[i];

>>>>> +

>>>>> +		if (odp_unlikely(odp_packet_has_error(pkt) ||

>>>>> +				 !odp_packet_has_ipv4(pkt))) {

>>>>> +			odp_packet_free(pkt);

>>>>> +			dropped++;

>>>>> +		} else if (odp_unlikely(i != j++)) {

>>>>> +			pkt_tbl[j - 1] = pkt;

>>>>> +		}

>>>>> +	}

>>>>> +

>>>>> +	return dropped;

>>>>> +}

>>>>> +

>>>>> +static void l3fwd_one_queue(uint32_t sif, uint8_t rxq_id, void

>> *thr_arg)

>>>>> +{

>>>>> +	struct l3fwd_pktio_s *port;

>>>>> +	odp_packet_t *tbl;

>>>>> +	odp_pktout_queue_t outq;

>>>>> +	odp_packet_t pkt_tbl[MAX_PKT_BURST];

>>>>> +	struct thread_arg_s *arg;

>>>>> +	uint8_t txq_id;

>>>>> +	int pkts, drop, sent;

>>>>> +	int dif, dst_port;

>>>>> +	int i;

>>>>> +

>>>>> +	arg = thr_arg;

>>>>> +	port = &global.l3fwd_pktios[sif];

>>>>> +	pkts = odp_pktin_recv(port->ifin[rxq_id], pkt_tbl,

> MAX_PKT_BURST);

>>>>> +	if (pkts <= 0)

>>>>> +		return;

>>>>> +	arg->packets += pkts;

>>>>> +	drop = drop_err_pkts(pkt_tbl, pkts);

>>>>> +	pkts -= drop;

>>>>> +	arg->rx_drops += drop;

>>>> Increment one value from number of threads might be not good thing to

>> do

>>>> due to 1) performance 2) atomic operations break.

>>>> It's better if each thread has it's own counters and control threads

>>>> summaries values.

>>> Not catch your idea. Here each thread has its own counters.

>>> It will be totaled before exit.

>>>

>>>>> +

>>>>> +	dif = global.fwd_func(pkt_tbl[0], sif);

>>>>> +	tbl = &pkt_tbl[0];

>>>>> +	while (pkts) {

>>>>> +		dst_port = dif;

>>>>> +		for (i = 1; i < pkts; i++) {

>>>>> +			dif = global.fwd_func(tbl[i], sif);

>>>>> +			if (dif != dst_port)

>>>>> +				break;

>>>>> +		}

>>>>> +		txq_id = arg->pktio[dst_port].txq[rxq_id];

>>>>> +		outq = global.l3fwd_pktios[dst_port].ifout[txq_id];

>>>>> +		sent = odp_pktout_send(outq, tbl, i);

>>>>> +		if (odp_unlikely(sent < i)) {

>>>>> +			sent = sent < 0 ? 0 : sent;

>>>>> +			odp_packet_free_multi(&tbl[sent], i - sent);

>>>>> +			arg->tx_drops += i - sent;

>>>>> +		}

>>>>> +

>>>>> +		if (i < pkts)

>>>>> +			tbl += i;

>>>>> +

>>>>> +		pkts -= i;

>>>>> +	}

>>>>> +}

>>>>> +

>>>>> +static int run_worker(void *arg)

>>>>> +{

>>>>> +	int if_idx, rxq_idx;

>>>>> +	struct thread_arg_s *thr_arg = arg;

>>>>> +	struct l3fwd_pktio_s *port;

>>>>> +

>>>>> +	odp_barrier_wait(&barrier);

>>>>> +

>>>>> +	while (!exit_threads) {

>>>>> +		for (if_idx = 0; if_idx < thr_arg->nb_pktio; if_idx++)

> {

>>>>> +			if (!thr_arg->pktio[if_idx].used ||

>>>>> +			    thr_arg->thr_idx == INVALID_ID)

>>>>> +				continue;

>>>>> +

>>>>> +			port = &global.l3fwd_pktios[if_idx];

>>>>> +			for (rxq_idx = 0; rxq_idx < port->rxq_idx;

> rxq_idx++)

>>>>> +				l3fwd_one_queue(if_idx, rxq_idx, arg);

>>>>> +		}

>>>>> +	}

>>>>> +

>>>>> +	/* Make sure that latest stat writes are visible to other

> threads */

>>>>> +	odp_mb_full();

>>>>> +

>>>>> +	return 0;

>>>>> +}

>>>>> +

>>>>> +static int find_port_id_by_name(char *name, app_args_t *args)

>>>>> +{

>>>>> +	int i;

>>>>> +

>>>>> +	if (!name)

>>>>> +		return -1;

>>>>> +

>>>>> +	for (i = 0; i < args->if_count; i++) {

>>>>> +		if (!strcmp(name, args->if_names[i]))

>>>>> +			return i;

>>>>> +	}

>>>>> +

>>>>> +	return -1;

>>>>> +}

>>>>> +

>>>>> +int main(int argc, char **argv)

>>>>> +{

>>>>> +	odph_odpthread_t thread_tbl[MAX_NB_WORKER];

>>>>> +	odp_pool_t pool;

>>>>> +	odp_pool_param_t params;

>>>>> +	odp_instance_t instance;

>>>>> +	odph_odpthread_params_t thr_params;

>>>>> +	odp_cpumask_t cpumask;

>>>>> +	int cpu, i, j, nb_worker;

>>>>> +	uint8_t mac[ODPH_ETHADDR_LEN];

>>>>> +	app_args_t *args;

>>>>> +	struct thread_arg_s *thr_arg;

>>>>> +	char *oif;

>>>>> +	int oid;

>>>>> +

>>>>> +	if (odp_init_global(&instance, NULL, NULL)) {

>>>>> +		printf("Error: ODP global init failed.\n");

>>>>> +		exit(1);

>>>>> +	}

>>>>> +

>>>>> +	if (odp_init_local(instance, ODP_THREAD_CONTROL)) {

>>>>> +		printf("Error: ODP local init failed.\n");

>>>>> +		exit(1);

>>>>> +	}

>>>>> +

>>>>> +	/* Clear global argument and initialize the dest mac as

> 2:0:0:0:0:x */

>>>>> +	memset(&global, 0, sizeof(global));

>>>>> +	mac[0] = 2;

>>>>> +	for (i = 0; i < MAX_NB_PKTIO; i++) {

>>>>> +		mac[ODPH_ETHADDR_LEN - 1] = (uint8_t)i;

>>>>> +		memcpy(global.eth_dest_mac[i].addr, mac,

>>>> ODPH_ETHADDR_LEN);

>>>>> +	}

>>>>> +

>>>>> +	/* Initialize the thread arguments */

>>>>> +	for (i = 0; i < MAX_NB_WORKER; i++) {

>>>>> +		thr_arg = &global.worker_args[i];

>>>>> +		for (j = 0; j < MAX_NB_PKTIO; j++) {

>>>>> +			thr_arg->thr_idx = INVALID_ID;

>>>>> +			memset(thr_arg->pktio[j].rxq, INVALID_ID,

>>>>> +			       sizeof(thr_arg->pktio[j].rxq));

>>>>> +		}

>>>>> +	}

>>>>> +

>>>>> +	/* Parse cmdline arguments */

>>>>> +	args = &global.cmd_args;

>>>>> +	parse_cmdline_args(argc, argv, args);

>>>>> +

>>>>> +	/* Init l3fwd table */

>>>>> +	init_fwd_db();

>>>>> +

>>>>> +	/* Add route into table */

>>>>> +	for (i = 0; i < MAX_NB_ROUTE; i++) {

>>>>> +		if (args->route_str[i]) {

>>>>> +			oif = NULL;

>>>>> +			create_fwd_db_entry(args->route_str[i], &oif);

>>>>> +			oid = find_port_id_by_name(oif, args);

>>>>> +			if (oid != -1)

>>>>> +				args->dest_mac_changed[oid] = 1;

>>>>> +		}

>>>>> +	}

>>>>> +

>>>>> +	print_info(NO_PATH(argv[0]), args);

>>>>> +

>>>>> +	/* Create packet pool */

>>>>> +	odp_pool_param_init(&params);

>>>>> +	params.pkt.seg_len = POOL_SEG_LEN;

>>>>> +	params.pkt.len     = POOL_SEG_LEN;

>>>>> +	params.pkt.num     = POOL_NUM_PKT;

>>>>> +	params.type        = ODP_POOL_PACKET;

>>>>> +

>>>>> +	pool = odp_pool_create("packet pool", &params);

>>>>> +

>>>>> +	if (pool == ODP_POOL_INVALID) {

>>>>> +		printf("Error: packet pool create failed.\n");

>>>>> +		exit(1);

>>>>> +	}

>>>>> +

>>>>> +	/* Resolve fwd db*/

>>>>> +	for (i = 0; i < args->if_count; i++) {

>>>>> +		struct l3fwd_pktio_s *port;

>>>>> +		char *if_name;

>>>>> +

>>>>> +		if_name = args->if_names[i];

>>>>> +		port = &global.l3fwd_pktios[i];

>>>>> +		if (create_pktio(if_name, pool, port)) {

>>>>> +			printf("Error: create pktio %s\n", if_name);

>>>>> +			exit(1);

>>>>> +		}

>>>>> +		odp_pktio_mac_addr(port->pktio, mac,

> ODPH_ETHADDR_LEN);

>>>>> +		resolve_fwd_db(if_name, i, mac);

>>>>> +		memcpy(port->mac_addr.addr, mac, ODPH_ETHADDR_LEN);

>>>>> +	}

>>>>> +

>>>>> +	setup_fwd_db();

>>>>> +	dump_fwd_db();

>>>>> +

>>>>> +	/* Dicide available workers */

>>>>> +	nb_worker = MAX_NB_WORKER;

>>>>> +	if (args->worker_count)

>>>>> +		nb_worker = args->worker_count;

>>>>> +	nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker);

>>>>> +	args->worker_count = nb_worker;

>>>>> +

>>>>> +	/* Setup rx and tx queues for each port */

>>>>> +	setup_worker_qconf(args);

>>>>> +	print_qconf_table(args);

>>>>> +

>>>>> +	/* Decide ip lookup method */

>>>>> +	if (args->hash_mode)

>>>>> +		global.fwd_func = l3fwd_pkt_hash;

>>>>> +	else

>>>>> +		global.fwd_func = l3fwd_pkt_lpm;

>>>>> +

>>>>> +	/* Start all the available ports */

>>>>> +	for (i = 0; i < args->if_count; i++) {

>>>>> +		struct l3fwd_pktio_s *port;

>>>>> +		char *if_name;

>>>>> +		char buf[32];

>>>>> +

>>>>> +		if_name = args->if_names[i];

>>>>> +		port = &global.l3fwd_pktios[i];

>>>>> +		/* start pktio */

>>>>> +		if (odp_pktio_start(port->pktio)) {

>>>>> +			printf("unable to start pktio: %s\n",

> if_name);

>>>>> +			exit(1);

>>>>> +		}

>>>>> +

>>>>> +		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",

>>>>> +			port->mac_addr.addr[0],

>>>>> +			port->mac_addr.addr[1],

>>>>> +			port->mac_addr.addr[2],

>>>>> +			port->mac_addr.addr[3],

>>>>> +			port->mac_addr.addr[4],

>>>>> +			port->mac_addr.addr[5]);

>>>>> +		printf("start pktio: %s, mac %s\n", if_name, buf);

>>>>> +	}

>>>>> +

>>>>> +	odp_barrier_init(&barrier, nb_worker + 1);

>>>>> +

>>>>> +	memset(&thr_params, 0, sizeof(thr_params));

>>>>> +	thr_params.start    = run_worker;

>>>>> +	thr_params.thr_type = ODP_THREAD_WORKER;

>>>>> +	thr_params.instance = instance;

>>>>> +

>>>>> +	memset(thread_tbl, 0, sizeof(thread_tbl));

>>>>> +	cpu = odp_cpumask_first(&cpumask);

>>>>> +	for (i = 0; i < nb_worker; i++) {

>>>>> +		struct thread_arg_s *arg;

>>>>> +		odp_cpumask_t thr_mask;

>>>>> +

>>>>> +		arg = &global.worker_args[i];

>>>>> +		arg->nb_pktio = args->if_count;

>>>>> +		odp_cpumask_zero(&thr_mask);

>>>>> +		odp_cpumask_set(&thr_mask, cpu);

>>>>> +		thr_params.arg = arg;

>>>>> +		odph_odpthreads_create(&thread_tbl[i], &thr_mask,

>>>>> +				       &thr_params);

>>>>> +		cpu = odp_cpumask_next(&cpumask, cpu);

>>>>> +	}

>>>>> +

>>>>> +	if (args->duration) {

>>>>> +		print_speed_stats(nb_worker, args->duration,

>>>> PRINT_INTERVAL);

>>>>> +		exit_threads = 1;

>>>>> +	}

>>>>> +

>>>>> +	/* wait for other threads to join */

>>>>> +	for (i = 0; i < nb_worker; i++)

>>>>> +		odph_odpthreads_join(&thread_tbl[i]);

>>>>> +

>>>> main should end if odp_destroy_global(). And to add this to 'make check'

>>>> requires probably to add some logic when we think that test passed and

>>>> when we thing that test failed.

>>>> I think you copied print_speed_stats() from l2fwd,  there return code

>>>> returns if app passed or not.

>>>>

>>> The testing PASS depends on the print_speed_stats() output in the log

> file.

>>> Here return value is not taken  as PASS or FAIL.

>>>

>>>>> +	return 0;

>>>>> +}

>>>>> +

>>>>> +static void print_usage(char *progname)

>>>>> +{

>>>>> +	printf("\n"

>>>>> +	       "ODP L3 forwarding application.\n"

>>>>> +	       "\n"

>>>>> +	       "Usage: %s OPTIONS\n"

>>>>> +	       "  E.g. %s -i eth0,eth1 -r 1.1.1.0/24,eth0 -r

>>> 2.2.2.0/24,eth1\n"

>>>>> +	       " In the above example,\n"

>>>>> +	       " eth0 will send pkts to eth1 and vice versa\n"

>>>>> +	       "\n"

>>>>> +	       "Mandatory OPTIONS:\n"

>>>>> +	       "  -i, --interface eth interfaces (comma-separated, no

>>> spaces)\n"

>>>>> +	       "  -r, --route SubNet,Intf[,NextHopMAC]\n"

>>>>> +	       "	NextHopMAC can be optional\n"

>>>>> +	       "\n"

>>>>> +	       "Optional OPTIONS:\n"

>>>>> +	       "  -s, --style [lpm|hash], ip lookup method\n"

>>>>> +	       "	optional, default as lpm\n"

>>>>> +	       "  -d, --duration Seconds to run and print stats\n"

>>>>> +	       "	optional, default as 0, run forever\n"

>>>>> +	       "  -t, --thread Number of threads to do forwarding\n"

>>>>> +	       "	optional, default as availbe worker cpu

> count\n"

>>>>> +	       "  -q, --queue  Configure rx queue(s) for port\n"

>>>>> +	       "	optional, format: [(port, queue,

> thread),...]\n"

>>>>> +	       "	for example: -q '(0, 0, 1),(1,0,2)'\n"

>>>>> +	       "  -h, --help   Display help and exit.\n\n"

>>>>> +	       "\n", NO_PATH(progname), NO_PATH(progname)

>>>>> +	    );

>>>>> +}

>>>>> +

>>>>> +static void parse_cmdline_args(int argc, char *argv[], app_args_t

>> *args)

>>>>> +{

>>>>> +	int opt;

>>>>> +	int long_index;

>>>>> +	char *token, *local;

>>>>> +	size_t len, route_index = 0;

>>>>> +	int i, mem_failure = 0;

>>>>> +

>>>>> +	static struct option longopts[] = {

>>>>> +		{"interface", required_argument, NULL, 'i'},	/*

> return 'i'

>>> */

>>>>> +		{"route", required_argument, NULL, 'r'},	/*

> return 'r'

>>> */

>>>>> +		{"style", optional_argument, NULL, 's'},	/*

> return 's'

>>> */

>>>>> +		{"duration", optional_argument, NULL, 'd'},	/*

> return 'd'

>>> */

>>>>> +		{"thread", optional_argument, NULL, 't'},	/*

> return 't'

>>> */

>>>>> +		{"queue", optional_argument, NULL, 'q'},	/*

> return 'q'

>>> */

>>>>> +		{"help", no_argument, NULL, 'h'},		/*

> return 'h'

>>> */

>>>>> +		{NULL, 0, NULL, 0}

>>>>> +	};

>>>>> +

>>>>> +	while (1) {

>>>>> +		opt = getopt_long(argc, argv, "+s:t:d:i:r:q:h",

>>>>> +				  longopts, &long_index);

>>>>> +

>>>>> +		if (opt == -1)

>>>>> +			break;	/* No more options */

>>>>> +

>>>>> +		switch (opt) {

>>>>> +		/* parse ip lookup method */

>>>>> +		case 's':

>>>>> +			if (!strcmp(optarg, "hash"))

>>>>> +				args->hash_mode = 1;

>>>>> +			break;

>>>>> +		/* parse number of worker threads to be run*/

>>>>> +		case 't':

>>>>> +			i = odp_cpu_count();

>>>>> +			args->worker_count = atoi(optarg);

>>>>> +			if (args->worker_count > i) {

>>>>> +				printf("Too many threads,"

>>>>> +				       "truncate to cpu count: %d\n",

> i);

>>>>> +				args->worker_count = i;

>>>>> +			}

>>>>> +

>>>>> +			break;

>>>>> +

>>>>> +		/* parse seconds to run */

>>>>> +		case 'd':

>>>>> +			args->duration = atoi(optarg);

>>>>> +			break;

>>>>> +

>>>>> +		/* parse packet-io interface names */

>>>>> +		case 'i':

>>>>> +			len = strlen(optarg);

>>>>> +			if (len == 0) {

>>>>> +				print_usage(argv[0]);

>>>>> +				exit(EXIT_FAILURE);

>>>>> +			}

>>>>> +			len += 1;	/* add room for '\0' */

>>>>> +

>>>>> +			local = malloc(len);

>>>>> +			if (!local) {

>>>>> +				print_usage(argv[0]);

>>>>> +				exit(EXIT_FAILURE);

>>>>> +			}

>>>>> +

>>>>> +			/* count the number of tokens separated by ','

> */

>>>>> +			strcpy(local, optarg);

>>>>> +			for (token = strtok(local, ","), i = 0;

>>>>> +			     token != NULL;

>>>>> +			     token = strtok(NULL, ","), i++)

>>>>> +				;

>>>>> +

>>>>> +			if (i == 0) {

>>>>> +				print_usage(argv[0]);

>>>>> +				exit(EXIT_FAILURE);

>>>>> +			} else if (i > MAX_NB_PKTIO) {

>>>>> +				printf("too many ports specified, "

>>>>> +				       "truncated to %d",

> MAX_NB_PKTIO);

>>>>> +			}

>>>>> +			args->if_count = i;

>>>>> +

>>>>> +			/* store the if names (reset names string) */

>>>>> +			strcpy(local, optarg);

>>>>> +			for (token = strtok(local, ","), i = 0;

>>>>> +			     token != NULL; token = strtok(NULL, ","),

> i++) {

>>>>> +				args->if_names[i] = token;

>>>>> +			}

>>>>> +			break;

>>>>> +

>>>>> +		/*Configure Route in forwarding database*/

>>>>> +		case 'r':

>>>>> +			if (route_index >= MAX_NB_ROUTE) {

>>>>> +				printf("No more routes can be

> added\n");

>>>>> +				break;

>>>>> +			}

>>>>> +			local = calloc(1, strlen(optarg) + 1);

>>>>> +			if (!local) {

>>>>> +				mem_failure = 1;

>>>>> +				break;

>>>>> +			}

>>>>> +			memcpy(local, optarg, strlen(optarg));

>>>>> +			local[strlen(optarg)] = '\0';

>>>>> +			args->route_str[route_index++] = local;

>>>>> +			break;

>>>>> +

>>>>> +		case 'h':

>>>>> +			print_usage(argv[0]);

>>>>> +			exit(EXIT_SUCCESS);

>>>>> +			break;

>>>>> +

>>>>> +		case 'q':

>>>>> +			parse_config(optarg, args);

>>>>> +			break;

>>>>> +

>>>>> +		default:

>>>>> +			break;

>>>>> +		}

>>>>> +	}

>>>>> +

>>>>> +	/* checking arguments */

>>>>> +	if (args->if_count == 0) {

>>>>> +		printf("\nNo option -i specified.\n");

>>>>> +		goto out;

>>>>> +	}

>>>>> +

>>>>> +	if (args->route_str[0] == NULL) {

>>>>> +		printf("\nNo option -r specified.\n");

>>>>> +		goto out;

>>>>> +	}

>>>>> +

>>>>> +	if (mem_failure == 1) {

>>>>> +		printf("\nAllocate memory failure.\n");

>>>>> +		goto out;

>>>>> +	}

>>>>> +	optind = 1;		/* reset 'extern optind' from the

> getopt lib

>>> */

>>>>> +	return;

>>>>> +

>>>>> +out:

>>>>> +	print_usage(argv[0]);

>>>>> +	exit(EXIT_FAILURE);

>>>>> +}

>>>>> +

>>>>> +/* split string into tokens */

>>>>> +int split_string(char *str, int stringlen,

>>>>> +		 char **tokens, int maxtokens, char delim)

>>>>> +{

>>>>> +	int i, tok = 0;

>>>>> +	int tokstart = 1; /* first token is right at start of string

> */

>>>>> +

>>>>> +	if (str == NULL || tokens == NULL)

>>>>> +		goto einval_error;

>>>>> +

>>>>> +	for (i = 0; i < stringlen; i++) {

>>>>> +		if (str[i] == '\0' || tok >= maxtokens)

>>>>> +			break;

>>>>> +		if (tokstart) {

>>>>> +			tokstart = 0;

>>>>> +			tokens[tok++] = &str[i];

>>>>> +		}

>>>>> +		if (str[i] == delim) {

>>>>> +			str[i] = '\0';

>>>>> +			tokstart = 1;

>>>>> +		}

>>>>> +	}

>>>>> +	return tok;

>>>>> +

>>>>> +einval_error:

>>>>> +	errno = EINVAL;

>>>>> +	return -1;

>>>>> +}

>>>>> +

>>>>> +static int parse_config(char *cfg_str, app_args_t *args)

>>>>> +{

>>>>> +	char s[256];

>>>>> +	const char *p, *p0 = cfg_str;

>>>>> +	char *end;

>>>>> +	enum fieldnames {

>>>>> +		FLD_PORT = 0,

>>>>> +		FLD_QUEUE,

>>>>> +		FLD_LCORE,

>>>>> +		FLD_LAST

>>>>> +	};

>>>>> +	unsigned long int_fld[FLD_LAST];

>>>>> +	char *str_fld[FLD_LAST];

>>>>> +	int i;

>>>>> +	unsigned size;

>>>>> +	int nb_qconfs = 0;

>>>>> +	struct l3fwd_qconf_s *qconf_array = &args->qconf_config[0];

>>>>> +

>>>>> +	p = strchr(p0, '(');

>>>>> +	while (p != NULL) {

>>>>> +		++p;

>>>>> +		p0 = strchr(p, ')');

>>>>> +		if (p0 == NULL)

>>>>> +			return -1;

>>>>> +

>>>>> +		size = p0 - p;

>>>>> +		if (size >= sizeof(s))

>>>>> +			return -1;

>>>>> +

>>>>> +		snprintf(s, sizeof(s), "%.*s", size, p);

>>>>> +		i = split_string(s, sizeof(s), str_fld, FLD_LAST,

> ',');

>>>>> +		if (i != FLD_LAST)

>>>>> +			return -1;

>>>>> +		for (i = 0; i < FLD_LAST; i++) {

>>>>> +			errno = 0;

>>>>> +			int_fld[i] = strtoul(str_fld[i], &end, 0);

>>>>> +			if (errno != 0 || end == str_fld[i] ||

> int_fld[i] >

>>> 255)

>>>>> +				return -1;

>>>>> +		}

>>>>> +		if (nb_qconfs >= MAX_NB_QCONFS) {

>>>>> +			printf("exceeded max number of queue

>>>> params: %hu\n",

>>>>> +			       nb_qconfs);

>>>>> +			return -1;

>>>>> +		}

>>>>> +		qconf_array[nb_qconfs].if_idx =

> (uint8_t)int_fld[FLD_PORT];

>>>>> +		qconf_array[nb_qconfs].rxq_idx =

>>>> (uint8_t)int_fld[FLD_QUEUE];

>>>>> +		qconf_array[nb_qconfs].core_idx =

>>>> (uint8_t)int_fld[FLD_LCORE];

>>>>> +		++nb_qconfs;

>>>>> +

>>>>> +		p = strchr(p0, '(');

>>>>> +	}

>>>>> +	args->qconf_count = nb_qconfs;

>>>>> +

>>>>> +	return 0;

>>>>> +}

>>>>> +

>>>>> +static void print_info(char *progname, app_args_t *args)

>>>>> +{

>>>>> +	int i;

>>>>> +

>>>>> +	printf("\n"

>>>>> +	       "ODP system info\n"

>>>>> +	       "---------------\n"

>>>>> +	       "ODP API version: %s\n"

>>>>> +	       "ODP impl name:	 %s\n"

>>>>> +	       "CPU model:       %s\n"

>>>>> +	       "CPU freq (hz):   %" PRIu64 "\n"

>>>>> +	       "Cache line size: %i\n"

>>>>> +	       "CPU count:       %i\n"

>>>>> +	       "\n",

>>>>> +	       odp_version_api_str(), odp_version_impl_name(),

>>>>> +	       odp_cpu_model_str(), odp_cpu_hz_max(),

>>>>> +	       odp_sys_cache_line_size(), odp_cpu_count());

>>>>> +

>>>>> +	printf("Running ODP appl: \"%s\"\n"

>>>>> +	       "-----------------\n"

>>>>> +	       "IP Lookup:	 %s\n"

>>>>> +	       "IF Count:        %i\n"

>>>>> +	       "Using IFs:      ",

>>>>> +	       progname,

>>>>> +	       args->hash_mode ? "hash" : "lpm",

>>>>> +	       args->if_count);

>>>>> +

>>>>> +	for (i = 0; i < args->if_count; ++i)

>>>>> +		printf(" %s", args->if_names[i]);

>>>>> +

>>>>> +	printf("\n\n");

>>>>> +	fflush(NULL);

>>>>> +}

>>>>> +

>>>>> +/**

>>>>> + * Setup rx and tx queues, distribute them among threads.

>>>>> + * Try to have one tx queue for each rx queue, if not vailable,

>>>>> + * shared tx queue is used.

>>>>> + *

>>>>> + * If no q argument, the queues are distribute among threads as

>> default.

>>>>> + * The thread take one rx queue of a port one time as round-robin

>> order.

>>>>> + */

>>>>> +static void setup_worker_qconf(app_args_t *args)

>>>>> +{

>>>>> +	int nb_worker, if_count;

>>>>> +	int i, j, rxq_idx, txq_idx;

>>>>> +	struct thread_arg_s *arg;

>>>>> +	struct l3fwd_pktio_s *port;

>>>>> +	uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_QUEUE];

>>>>> +

>>>>> +	nb_worker = args->worker_count;

>>>>> +	if_count = args->if_count;

>>>>> +

>>>>> +	/* distribute queues among threads */

>>>>> +	if (!args->qconf_count) {

>>>>> +		if (nb_worker > if_count) {

>>>>> +			for (i = 0; i < nb_worker; i++) {

>>>>> +				arg = &global.worker_args[i];

>>>>> +				arg->thr_idx = i;

>>>>> +				j = i % if_count;

>>>>> +				port = &global.l3fwd_pktios[j];

>>>>> +				if (port->rxq_idx < port->nb_rxq) {

>>>>> +					rxq_idx = port->rxq_idx;

>>>>> +					arg->pktio[j].rxq[rxq_idx] =

> rxq_idx;

>>>>> +					port->rxq_idx++;

>>>>> +					txq_idx = port->txq_idx;

>>>>> +					arg->pktio[j].txq[txq_idx] =

> txq_idx;

>>>>> +					port->txq_idx++;

>>>>> +					arg->pktio[j].used = 1;

>>>>> +				}

>>>>> +			}

>>>>> +		} else {

>>>>> +			for (i = 0; i < if_count; i++) {

>>>>> +				j = i % nb_worker;

>>>>> +				arg = &global.worker_args[j];

>>>>> +				arg->thr_idx = j;

>>>>> +				port = &global.l3fwd_pktios[i];

>>>>> +				if (port->rxq_idx < port->nb_rxq) {

>>>>> +					rxq_idx = port->rxq_idx;

>>>>> +					arg->pktio[i].rxq[rxq_idx] =

> rxq_idx;

>>>>> +					port->rxq_idx++;

>>>>> +					txq_idx = port->txq_idx;

>>>>> +					arg->pktio[i].txq[txq_idx] =

> txq_idx;

>>>>> +					port->txq_idx++;

>>>>> +					arg->pktio[i].used = 1;

>>>>> +				}

>>>>> +			}

>>>>> +		}

>>>>> +	}

>>>>> +

>>>>> +	/* specified q argument, distribute queues among threads as it

> says */

>>>>> +	memset(queue_mask, 0, sizeof(queue_mask));

>>>>> +	for (i = 0; i < args->qconf_count; i++) {

>>>>> +		struct l3fwd_qconf_s *q;

>>>>> +

>>>>> +		q = &args->qconf_config[i];

>>>>> +		if (q->core_idx >= nb_worker || q->if_idx >= if_count)

>>>>> +			LOG_ABORT("Error queue (%d, %d, %d), max port:

> "

>>>>> +				  "%d, max core: %d\n", q->if_idx,

> q->rxq_idx,

>>>>> +				  q->core_idx, args->if_count - 1,

>>>>> +				  args->worker_count - 1);

>>>>> +

>>>>> +		/* check if one queue is configured twice or more */

>>>>> +		if (queue_mask[q->if_idx][q->rxq_idx])

>>>>> +			LOG_ABORT("Error queue (%d, %d, %d), reconfig

>>>> queue\n",

>>>>> +				  q->if_idx, q->rxq_idx, q->core_idx);

>>>>> +		queue_mask[q->if_idx][q->rxq_idx] = 1;

>>>>> +

>>>>> +		port = &global.l3fwd_pktios[q->if_idx];

>>>>> +		if (port->rxq_idx < q->rxq_idx)

>>>>> +			LOG_ABORT("Error queue (%d, %d, %d), queue

> should

>>>> be"

>>>>> +				  " in sequence and start from 0,

> queue %d\n",

>>>>> +				  q->if_idx, q->rxq_idx, q->core_idx,

>>>>> +				  q->rxq_idx);

>>>>> +

>>>>> +		if (q->rxq_idx > port->nb_rxq) {

>>>>> +			LOG_ABORT("Error queue (%d, %d, %d), max

>>>> queue %d\n",

>>>>> +				  q->if_idx, q->rxq_idx, q->core_idx,

>>>>> +				  port->nb_rxq - 1);

>>>>> +		}

>>>>> +		port->rxq_idx = q->rxq_idx + 1;

>>>>> +		port->txq_idx = q->rxq_idx + 1;

>>>>> +

>>>>> +		/* put the queue into worker_args */

>>>>> +		arg = &global.worker_args[q->core_idx];

>>>>> +		arg->pktio[q->if_idx].rxq[q->rxq_idx] = q->rxq_idx;

>>>>> +		arg->pktio[q->if_idx].txq[q->rxq_idx] = q->rxq_idx;

>>>>> +		arg->pktio[q->if_idx].used = 1;

>>>>> +		arg->thr_idx = q->core_idx;

>>>>> +	}

>>>>> +

>>>>> +	/* config and initialize rx and tx queues. */

>>>>> +	for (i = 0; i < if_count; i++) {

>>>>> +		odp_pktin_queue_param_t in_queue_param;

>>>>> +		odp_pktout_queue_param_t out_queue_param;

>>>>> +		struct odp_pktin_queue_t *inq;

>>>>> +		struct odp_pktout_queue_t *outq;

>>>>> +		const char *name;

>>>>> +		int rc, nb_rxq, nb_txq;

>>>>> +

>>>>> +		port = &global.l3fwd_pktios[i];

>>>>> +		name = args->if_names[i];

>>>>> +		odp_pktin_queue_param_init(&in_queue_param);

>>>>> +		odp_pktout_queue_param_init(&out_queue_param);

>>>>> +

>>>>> +		in_queue_param.op_mode = ODP_PKTIO_OP_MT;

>>>>> +		out_queue_param.op_mode = ODP_PKTIO_OP_MT;

>>>>> +

>>>>> +		in_queue_param.num_queues = port->rxq_idx;

>>>>> +		if (port->rxq_idx > port->nb_rxq) {

>>>>> +			in_queue_param.num_queues = port->nb_rxq;

>>>>> +			in_queue_param.op_mode =

>>>> ODP_PKTIO_OP_MT_UNSAFE;

>>>>> +		}

>>>>> +

>>>>> +		/* enable flow hashing for multi-input queues */

>>>>> +		if (in_queue_param.num_queues > 1) {

>>>>> +			in_queue_param.hash_enable = 1;

>>>>> +			in_queue_param.hash_proto.proto.ipv4_tcp = 1;

>>>>> +			in_queue_param.hash_proto.proto.ipv4_udp = 1;

>>>>> +		}

>>>>> +

>>>>> +		if (odp_pktin_queue_config(port->pktio,

> &in_queue_param))

>>>>> +			LOG_ABORT("Fail to config input queue for

> %s\n",

>>>> name);

>>>>> +

>>>>> +		out_queue_param.num_queues = port->txq_idx;

>>>>> +		if (port->txq_idx > port->nb_txq) {

>>>>> +			out_queue_param.num_queues = port->nb_txq;

>>>>> +			out_queue_param.op_mode =

>>>> ODP_PKTIO_OP_MT_UNSAFE;

>>>>> +		}

>>>>> +		if (odp_pktout_queue_config(port->pktio,

>>>> &out_queue_param))

>>>>> +			LOG_ABORT("Fail to config output queue for

> %s\n",

>>>> name);

>>>>> +

>>>>> +		inq = port->ifin;

>>>>> +		nb_rxq = in_queue_param.num_queues;

>>>>> +		if (odp_pktin_queue(port->pktio, inq, nb_rxq) !=

> nb_rxq)

>>>>> +			LOG_ABORT("Fail to set pktin queue for %s\n",

> name);

>>>>> +

>>>>> +		if (port->rxq_idx > port->nb_rxq) {

>>>>> +			for (rc = port->nb_rxq; rc < port->rxq_idx;

> rc++)

>>>>> +				inq[rc] = inq[rc % port->nb_rxq];

>>>>> +		}

>>>>> +

>>>>> +		outq = port->ifout;

>>>>> +		nb_txq = out_queue_param.num_queues;

>>>>> +		if (odp_pktout_queue(port->pktio, outq, nb_txq) !=

> nb_txq)

>>>>> +			LOG_ABORT("Fail to set pktout queue for %s\n",

>>>> name);

>>>>> +

>>>>> +		if (port->txq_idx > port->nb_txq) {

>>>>> +			for (rc = port->nb_txq; rc < port->txq_idx;

> rc++)

>>>>> +				outq[rc] = outq[rc % port->nb_txq];

>>>>> +		}

>>>>> +	}

>>>>> +}

>>>>> +

>>>>> +static void print_qconf_table(app_args_t *args)

>>>>> +{

>>>>> +	int i, j, k, qid;

>>>>> +	char buf[32];

>>>>> +	struct thread_arg_s *thr_arg;

>>>>> +

>>>>> +	printf("Rx queue table\n"

>>>>> +	       "-----------------\n"

>>>>> +	       "%-16s%-16s%-16s\n",

>>>>> +	       "port/id", "queue", "thread");

>>>>> +

>>>>> +	for (i = 0; i < args->worker_count; i++) {

>>>>> +		thr_arg = &global.worker_args[i];

>>>>> +		for (j = 0; j < args->if_count; j++) {

>>>>> +			if (!thr_arg->pktio[j].used)

>>>>> +				continue;

>>>>> +

>>>>> +			sprintf(buf, "%s/%d", args->if_names[j], j);

>>>>> +			for (k = 0; k < MAX_NB_QUEUE; k++) {

>>>>> +				qid = thr_arg->pktio[j].rxq[k];

>>>>> +				if (qid != INVALID_ID)

>>>>> +					printf("%-16s%-16d%-16d\n",

> buf, qid,

>>>>> +					       thr_arg->thr_idx);

>>>>> +			}

>>>>> +		}

>>>>> +	}

>>>>> +	printf("\n");

>>>>> +	fflush(NULL);

>>>>> +}

>>>>> +

>>>>> +/**

>>>>> + *  Print statistics

>>>>> + *

>>>>> + * @param num_workers Number of worker threads

>>>>> + * @param duration Number of seconds to loop in

>>>>> + * @param timeout Number of seconds for stats calculation

>>>>> + *

>>>>> + */

>>>>> +static int print_speed_stats(int num_workers, int duration, int

>> timeout)

>>>>> +{

>>>>> +	uint64_t pkts = 0;

>>>>> +	uint64_t pkts_prev = 0;

>>>>> +	uint64_t pps;

>>>>> +	uint64_t rx_drops, tx_drops;

>>>>> +	uint64_t maximum_pps = 0;

>>>>> +	int i;

>>>>> +	int elapsed = 0;

>>>>> +	int stats_enabled = 1;

>>>>> +	int loop_forever = (duration == 0);

>>>>> +

>>>>> +	if (timeout <= 0) {

>>>>> +		stats_enabled = 0;

>>>>> +		timeout = 1;

>>>>> +	}

>>>>> +	/* Wait for all threads to be ready*/

>>>>> +	odp_barrier_wait(&barrier);

>>>>> +

>>>>> +	do {

>>>>> +		pkts = 0;

>>>>> +		rx_drops = 0;

>>>>> +		tx_drops = 0;

>>>>> +		sleep(timeout);

>>>>> +

>>>>> +		for (i = 0; i < num_workers; i++) {

>>>>> +			pkts += global.worker_args[i].packets;

>>>>> +			rx_drops += global.worker_args[i].rx_drops;

>>>>> +			tx_drops += global.worker_args[i].tx_drops;

>>>>> +		}

>>>>> +		if (stats_enabled) {

>>>>> +			pps = (pkts - pkts_prev) / timeout;

>>>>> +			if (pps > maximum_pps)

>>>>> +				maximum_pps = pps;

>>>>> +			printf("%" PRIu64 " pps, %" PRIu64 " max pps,

> ",  pps,

>>>>> +			       maximum_pps);

>>>>> +

>>>>> +			printf(" %" PRIu64 " rx drops, %" PRIu64 " tx

>>> drops\n",

>>>>> +			       rx_drops, tx_drops);

>>>>> +

>>>>> +			pkts_prev = pkts;

>>>>> +		}

>>>>> +		elapsed += timeout;

>>>>> +	} while (loop_forever || (elapsed < duration));

>>>>> +

>>>>> +	if (stats_enabled)

>>>>> +		printf("TEST RESULT: %" PRIu64 " maximum packets per

>>>> second.\n",

>>>>> +		       maximum_pps);

>>>>> +

>>>>> +	return pkts > 100 ? 0 : -1;

>>>>> +}

>>>>> diff --git a/example/l3fwd/odp_l3fwd_db.c

>>>> b/example/l3fwd/odp_l3fwd_db.c

>>>>> new file mode 100644

>>>>> index 0000000..93e32f0

>>>>> --- /dev/null

>>>>> +++ b/example/l3fwd/odp_l3fwd_db.c

>>>>> @@ -0,0 +1,408 @@

>>>>> +/* Copyright (c) 2016, Linaro Limited

>>>>> + * All rights reserved.

>>>>> + *

>>>>> + * SPDX-License-Identifier:     BSD-3-Clause

>>>>> + */

>>>>> +

>>>>> +#ifndef _GNU_SOURCE

>>>>> +#define _GNU_SOURCE

>>>>> +#endif

>>>>> +

>>>>> +#include <stdlib.h>

>>>>> +#include <string.h>

>>>>> +

>>>>> +#include <example_debug.h>

>>>>> +#include <odp_api.h>

>>>>> +#include <odp_l3fwd_db.h>

>>>>> +

>>>>> +/** Jenkins hash support.

>>>>> +  *

>>>>> +  * Copyright (C) 2006 Bob Jenkins (bob_jenkins@burtleburtle.net)

>>>>> +  *

>>>>> +  * http://burtleburtle.net/bob/hash/

>>>>> +  *

>>>>> +  * These are the credits from Bob's sources:

>>>>> +  *

>>>>> +  * lookup3.c, by Bob Jenkins, May 2006, Public Domain.

>>>>> +  *

>>>>> +  * These are functions for producing 32-bit hashes for hash table

>>> lookup.

>>>>> +  * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and

>>> final()

>>>>> +  * are externally useful functions.  Routines to test the hash are

>>> included

>>>>> +  * if SELF_TEST is defined.  You can use this free for any purpose.

>>> It's in

>>>>> +  * the public domain.  It has no warranty.

>>>>> +  *

>>>>> +  * $FreeBSD$

>>>>> +  */

>>>>> +#define JHASH_GOLDEN_RATIO	0x9e3779b9

>>>>> +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))

>>>>> +#define FWD_BJ3_MIX(a, b, c) \

>>>>> +{ \

>>>>> +	a -= c; a ^= rot(c, 4); c += b; \

>>>>> +	b -= a; b ^= rot(a, 6); a += c; \

>>>>> +	c -= b; c ^= rot(b, 8); b += a; \

>>>>> +	a -= c; a ^= rot(c, 16); c += b; \

>>>>> +	b -= a; b ^= rot(a, 19); a += c; \

>>>>> +	c -= b; c ^= rot(b, 4); b += a; \

>>>>> +}

>>>>> +

>>>>> +/**

>>>>> + * Compute hash value from a flow

>>>>> + */

>>>>> +static inline

>>>>> +uint64_t l3fwd_calc_hash(ipv4_tuple5_t *key)

>>>>> +{

>>>>> +	uint64_t l4_ports = 0;

>>>>> +	uint32_t dst_ip, src_ip;

>>>>> +

>>>>> +	src_ip = key->src_ip;

>>>>> +	dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO;

>>>>> +	FWD_BJ3_MIX(src_ip, dst_ip, l4_ports);

>>>>> +

>>>>> +	return l4_ports;

>>>>> +}

>>>>> +

>>>>> +/**

>>>>> + * Parse text string representing an IPv4 address or subnet

>>>>> + *

>>>>> + * String is of the format "XXX.XXX.XXX.XXX(/W)" where

>>>>> + * "XXX" is decimal value and "/W" is optional subnet length

>>>>> + *

>>>>> + * @param ipaddress  Pointer to IP address/subnet string to convert

>>>>> + * @param addr       Pointer to return IPv4 address, host endianness

>>>>> + * @param depth      Pointer to subnet bit width

>>>>> + * @return 0 if successful else -1

>>>>> + */

>>>>> +static inline

>>>>> +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t

> *depth)

>>>>> +{

>>>>> +	int b[4];

>>>>> +	int qualifier = 32;

>>>>> +	int converted;

>>>>> +	uint32_t addr_le;

>>>>> +

>>>>> +	if (strchr(ipaddress, '/')) {

>>>>> +		converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",

>>>>> +				   &b[3], &b[2], &b[1], &b[0],

>>>>> +				   &qualifier);

>>>>> +		if (5 != converted)

>>>>> +			return -1;

>>>>> +	} else {

>>>>> +		converted = sscanf(ipaddress, "%d.%d.%d.%d",

>>>>> +				   &b[3], &b[2], &b[1], &b[0]);

>>>>> +		if (4 != converted)

>>>>> +			return -1;

>>>>> +	}

>>>>> +

>>>>> +	if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] >

> 255))

>>>>> +		return -1;

>>>>> +	if (!qualifier || (qualifier > 32))

>>>>> +		return -1;

>>>>> +

>>>>> +	addr_le = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;

>>>>> +	*addr = odp_le_to_cpu_32(addr_le);

>>>>> +	*depth = qualifier;

>>>>> +

>>>>> +	return 0;

>>>>> +}

>>>>> +

>>>>> +/**

>>>>> + * Generate text string representing IPv4 range/subnet, output

>>>>> + * in "XXX.XXX.XXX.XXX/W" format

>>>>> + *

>>>>> + * @param b     Pointer to buffer to store string

>>>>> + * @param range Pointer to IPv4 address range

>>>>> + *

>>>>> + * @return Pointer to supplied buffer

>>>>> + */

>>>>> +static inline

>>>>> +char *ipv4_subnet_str(char *b, ip_addr_range_t *range)

>>>>> +{

>>>>> +	sprintf(b, "%d.%d.%d.%d/%d",

>>>>> +		0xFF & ((range->addr) >> 24),

>>>>> +		0xFF & ((range->addr) >> 16),

>>>>> +		0xFF & ((range->addr) >>  8),

>>>>> +		0xFF & ((range->addr) >>  0),

>>>>> +		range->depth);

>>>>> +	return b;

>>>>> +}

>>>>> +

>>>>> +/**

>>>>> + * Generate text string representing MAC address

>>>>> + *

>>>>> + * @param b     Pointer to buffer to store string

>>>>> + * @param mac   Pointer to MAC address

>>>>> + *

>>>>> + * @return Pointer to supplied buffer

>>>>> + */

>>>>> +static inline

>>>>> +char *mac_addr_str(char *b, odph_ethaddr_t *mac)

>>>>> +{

>>>>> +	uint8_t *byte;

>>>>> +

>>>>> +	byte = mac->addr;

>>>>> +	sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",

>>>>> +		byte[0], byte[1], byte[2], byte[3], byte[4], byte[5]);

>>>>> +	return b;

>>>>> +}

>>>>> +

>>>>> +/**

>>>>> + * Flow cache table entry

>>>>> + */

>>>>> +typedef struct flow_entry_s {

>>>>> +	ipv4_tuple5_t key;		/**< match key */

>>>>> +	struct flow_entry_s *next;      /**< next entry on the list */

>>>>> +	fwd_db_entry_t *fwd_entry;	/**< entry info in db */

>>>>> +} flow_entry_t;

>>>>> +

>>>>> +/**

>>>>> + * Flow cache table bucket

>>>>> + */

>>>>> +typedef struct flow_bucket_s {

>>>>> +	odp_spinlock_t		lock;	/**< Bucket lock*/

>>>>> +	flow_entry_t		*next;	/**< Pointer to first flow

> entry in

>>>> bucket*/

>>>>> +} flow_bucket_t;

>>>>> +

>>>>> +/**

>>>>> + * Flow hash table, fast lookup cache

>>>>> + */

>>>>> +typedef struct flow_table_s {

>>>>> +	flow_bucket_t *bucket;

>>>>> +	uint32_t count;

>>>>> +} flow_table_t;

>>>>> +

>>>>> +static flow_table_t fwd_lookup_cache;

>>>>> +

>>>>> +void init_fwd_hash_cache(void)

>>>>> +{

>>>>> +	odp_shm_t		hash_shm;

>>>>> +	flow_bucket_t		*bucket;

>>>>> +	uint32_t		bucket_count;

>>>>> +	uint32_t		i;

>>>>> +

>>>>> +	bucket_count = FWD_DEF_BUCKET_COUNT;

>>>>> +

>>>>> +	/*Reserve memory for Routing hash table*/

>>>>> +	hash_shm = odp_shm_reserve("route_table",

>>>>> +				   sizeof(flow_bucket_t) *

> bucket_count,

>>>>> +				   ODP_CACHE_LINE_SIZE, 0);

>>>>> +

>>>>> +	bucket = odp_shm_addr(hash_shm);

>>>>> +	if (!bucket) {

>>>>> +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

>>>>> +		exit(-1);

>>>>> +	}

>>>>> +

>>>>> +	fwd_lookup_cache.bucket = bucket;

>>>>> +	fwd_lookup_cache.count = bucket_count;

>>>>> +

>>>>> +	/*Initialize Locks*/

>>>>> +	for (i = 0; i < bucket_count; i++) {

>>>>> +		bucket = &fwd_lookup_cache.bucket[i];

>>>>> +		odp_spinlock_init(&bucket->lock);

>>>>> +		bucket->next = NULL;

>>>>> +	}

>>>>> +}

>>>>> +

>>>>> +static inline

>>>>> +int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow)

>>>>> +{

>>>>> +	if (key->src_ip == flow->key.src_ip &&

>>>>> +	    key->dst_ip == flow->key.dst_ip &&

>>>>> +	    key->src_port == flow->key.src_port &&

>>>>> +	    key->dst_port == flow->key.dst_port &&

>>>>> +	    key->proto == flow->key.proto)

>>>>> +		return 1;

>>>>> +

>>>>> +	return 0;

>>>>> +}

>>>>> +

>>>>> +static inline

>>>>> +flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t

>>>> *bucket)

>>>>> +{

>>>>> +	flow_entry_t *rst;

>>>>> +

>>>>> +	for (rst = bucket->next; rst != NULL; rst = rst->next) {

>>>>> +		if (match_key_flow(key, rst))

>>>>> +			break;

>>>>> +	}

>>>>> +

>>>>> +	return rst;

>>>>> +}

>>>>> +

>>>>> +static inline

>>>>> +flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key,

>>>>> +			       flow_bucket_t *bucket,

>>>>> +			       fwd_db_entry_t *entry)

>>>>> +{

>>>>> +	flow_entry_t *flow;

>>>>> +

>>>>> +	flow = lookup_fwd_cache(key, bucket);

>>>>> +	if (flow)

>>>>> +		return flow;

>>>>> +

>>>>> +	flow = malloc(sizeof(flow_entry_t));

>>>>> +	flow->key = *key;

>>>>> +	flow->fwd_entry = entry;

>>>>> +

>>>>> +	odp_spinlock_lock(&bucket->lock);

>>>>> +	if (!bucket->next) {

>>>>> +		bucket->next = flow;

>>>>> +	} else {

>>>>> +		flow->next = bucket->next;

>>>>> +		bucket->next = flow;

>>>>> +	}

>>>>> +	odp_spinlock_unlock(&bucket->lock);

>>>>> +

>>>>> +	return flow;

>>>>> +}

>>>>> +

>>>>> +/** Global pointer to fwd db */

>>>>> +fwd_db_t *fwd_db;

>>>>> +

>>>>> +void init_fwd_db(void)

>>>>> +{

>>>>> +	odp_shm_t shm;

>>>>> +

>>>>> +	shm = odp_shm_reserve("shm_fwd_db",

>>>>> +			      sizeof(fwd_db_t),

>>>>> +			      ODP_CACHE_LINE_SIZE,

>>>>> +			      0);

>>>>> +

>>>>> +	fwd_db = odp_shm_addr(shm);

>>>>> +

>>>>> +	if (fwd_db == NULL) {

>>>>> +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

>>>>> +		exit(EXIT_FAILURE);

>>>>> +	}

>>>>> +	memset(fwd_db, 0, sizeof(*fwd_db));

>>>>> +}

>>>>> +

>>>>> +int create_fwd_db_entry(char *input, char **oif)

>>>>> +{

>>>>> +	int pos = 0;

>>>>> +	char *local;

>>>>> +	char *str;

>>>>> +	char *save;

>>>>> +	char *token;

>>>>> +	fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];

>>>>> +

>>>>> +	/* Verify we haven't run out of space */

>>>>> +	if (MAX_DB <= fwd_db->index)

>>>>> +		return -1;

>>>>> +

>>>>> +	/* Make a local copy */

>>>>> +	local = malloc(strlen(input) + 1);

>>>>> +	if (NULL == local)

>>>>> +		return -1;

>>>>> +	strcpy(local, input);

>>>>> +

>>>>> +	/* Setup for using "strtok_r" to search input string */

>>>>> +	str = local;

>>>>> +	save = NULL;

>>>>> +

>>>>> +	/* Parse tokens separated by ':' */

>>>>> +	while (NULL != (token = strtok_r(str, ",", &save))) {

>>>>> +		str = NULL;  /* reset str for subsequent strtok_r

> calls */

>>>>> +

>>>>> +		/* Parse token based on its position */

>>>>> +		switch (pos) {

>>>>> +		case 0:

>>>>> +			parse_ipv4_string(token,

>>>>> +					  &entry->subnet.addr,

>>>>> +					  &entry->subnet.depth);

>>>>> +			break;

>>>>> +		case 1:

>>>>> +			strncpy(entry->oif, token, OIF_LEN - 1);

>>>>> +			entry->oif[OIF_LEN - 1] = 0;

>>>>> +			break;

>>>>> +		case 2:

>>>>> +			odph_eth_addr_parse(&entry->dst_mac, token);

>>>>> +			*oif = entry->oif;

>>>>> +			break;

>>>>> +

>>>>> +		default:

>>>>> +			printf("ERROR: extra token \"%s\" at position

> %d\n",

>>>>> +			       token, pos);

>>>>> +			break;

>>>>> +		}

>>>>> +

>>>>> +		/* Advance to next position */

>>>>> +		pos++;

>>>>> +	}

>>>>> +

>>>>> +	/* Add route to the list */

>>>>> +	fwd_db->index++;

>>>>> +	entry->next = fwd_db->list;

>>>>> +	fwd_db->list = entry;

>>>>> +

>>>>> +	free(local);

>>>>> +	return 0;

>>>>> +}

>>>>> +

>>>>> +void resolve_fwd_db(char *intf, int portid, uint8_t *mac)

>>>>> +{

>>>>> +	fwd_db_entry_t *entry;

>>>>> +

>>>>> +	/* Walk the list and attempt to set output and MAC */

>>>>> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> {

>>>>> +		if (strcmp(intf, entry->oif))

>>>>> +			continue;

>>>>> +

>>>>> +		entry->oif_id = portid;

>>>>> +		memcpy(entry->src_mac.addr, mac, ODPH_ETHADDR_LEN);

>>>>> +	}

>>>>> +}

>>>>> +

>>>>> +void dump_fwd_db_entry(fwd_db_entry_t *entry)

>>>>> +{

>>>>> +	char subnet_str[MAX_STRING];

>>>>> +	char mac_str[MAX_STRING];

>>>>> +

>>>>> +	mac_addr_str(mac_str, &entry->dst_mac);

>>>>> +	printf("%-16s%-16s%-16s\n",

>>>>> +	       ipv4_subnet_str(subnet_str, &entry->subnet),

>>>>> +	       entry->oif, mac_str);

>>>>> +}

>>>>> +

>>>>> +void dump_fwd_db(void)

>>>>> +{

>>>>> +	fwd_db_entry_t *entry;

>>>>> +

>>>>> +	printf("Routing table\n"

>>>>> +	       "-----------------\n"

>>>>> +	       "%-16s%-16s%-16s\n",

>>>>> +	       "subnet", "next_hop", "dest_mac");

>>>>> +

>>>>> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

>>>>> +		dump_fwd_db_entry(entry);

>>>>> +

>>>>> +	printf("\n");

>>>>> +}

>>>>> +

>>>>> +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key)

>>>>> +{

>>>>> +	fwd_db_entry_t *entry;

>>>>> +	flow_entry_t *flow;

>>>>> +	flow_bucket_t *bucket;

>>>>> +	uint64_t hash;

>>>>> +

>>>>> +	/* first find in cache */

>>>>> +	hash = l3fwd_calc_hash(key);

>>>>> +	hash &= fwd_lookup_cache.count - 1;

>>>>> +	bucket = &fwd_lookup_cache.bucket[hash];

>>>>> +	flow = lookup_fwd_cache(key, bucket);

>>>>> +	if (flow)

>>>>> +		return flow->fwd_entry;

>>>>> +

>>>>> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> {

>>>>> +		uint32_t mask;

>>>>> +

>>>>> +		mask = ((1u << entry->subnet.depth) - 1) <<

>>>>> +			(32 - entry->subnet.depth);

>>>>> +

>>>>> +		if (entry->subnet.addr == (key->dst_ip & mask))

>>>>> +			break;

>>>>> +	}

>>>>> +

>>>>> +	return entry;

>>>>> +}

>>>>> diff --git a/example/l3fwd/odp_l3fwd_db.h

>>>> b/example/l3fwd/odp_l3fwd_db.h

>>>>> new file mode 100644

>>>>> index 0000000..840946a

>>>>> --- /dev/null

>>>>> +++ b/example/l3fwd/odp_l3fwd_db.h

>>>>> @@ -0,0 +1,130 @@

>>>>> +/* Copyright (c) 2016, Linaro Limited

>>>>> + * All rights reserved.

>>>>> + *

>>>>> + * SPDX-License-Identifier:     BSD-3-Clause

>>>>> + */

>>>>> +

>>>>> +#ifndef ODP_L3FWD_DB_H_

>>>>> +#define ODP_L3FWD_DB_H_

>>>>> +

>>>>> +#ifdef __cplusplus

>>>>> +extern "C" {

>>>>> +#endif

>>>>> +

>>>>> +#include <odp_api.h>

>>>>> +#include <odp/helper/eth.h>

>>>>> +

>>>>> +#define OIF_LEN 32

>>>>> +#define MAX_DB  32

>>>>> +#define MAX_STRING  32

>>>>> +

>>>>> +/**

>>>>> + * Default number of flows

>>>>> + */

>>>>> +#define FWD_DEF_FLOW_COUNT		100000

>>>>> +

>>>>> +/**

>>>>> + * Default Hash bucket number

>>>>> + */

>>>>> +#define FWD_DEF_BUCKET_COUNT	(FWD_DEF_FLOW_COUNT / 8)

>>>>> +

>>>>> +/**

>>>>> + * IP address range (subnet)

>>>>> + */

>>>>> +typedef struct ip_addr_range_s {

>>>>> +	uint32_t  addr;     /**< IP address, host endianness */

>>>>> +	uint32_t  depth;    /**< subnet bit width */

>>>>> +} ip_addr_range_t;

>>>>> +

>>>>> +/**

>>>>> + * TCP/UDP flow

>>>>> + */

>>>>> +typedef struct ipv4_tuple5_s {

>>>>> +	uint32_t src_ip;

>>>>> +	uint32_t dst_ip;

>>>>> +	uint16_t src_port;

>>>>> +	uint16_t dst_port;

>>>>> +	uint8_t  proto;

>>>>> +} ipv4_tuple5_t;

>>>>> +

>>>>> +/**

>>>>> + * Forwarding data base entry

>>>>> + */

>>>>> +typedef struct fwd_db_entry_s {

>>>>> +	struct fwd_db_entry_s *next;          /**< Next entry on list

> */

>>>>> +	char                    oif[OIF_LEN]; /**< Output interface

> name */

>>>>> +	int			oif_id;	      /**< Output interface

> idx */

>>>>> +	odph_ethaddr_t		src_mac;      /**< Output source MAC

> */

>>>>> +	odph_ethaddr_t		dst_mac;      /**< Output destination

>>>> MAC */

>>>>> +	ip_addr_range_t		subnet;       /**< Subnet for this

> router

>>>> */

>>>>> +} fwd_db_entry_t;

>>>>> +

>>>>> +/**

>>>>> + * Forwarding data base

>>>>> + */

>>>>> +typedef struct fwd_db_s {

>>>>> +	uint32_t          index;          /**< Next available entry */

>>>>> +	fwd_db_entry_t   *list;           /**< List of active routes

> */

>>>>> +	fwd_db_entry_t    array[MAX_DB];  /**< Entry storage */

>>>>> +} fwd_db_t;

>>>>> +

>>>>> +/** Global pointer to fwd db */

>>>>> +extern fwd_db_t *fwd_db;

>>>>> +

>>>>> +/**

>>>>> + * Initialize FWD DB

>>>>> + */

>>>>> +void init_fwd_db(void);

>>>>> +

>>>>> +/**

>>>>> + * Initialize forward lookup cache based on hash

>>>>> + */

>>>>> +void init_fwd_hash_cache(void);

>>>>> +

>>>>> +/**

>>>>> + * Create a forwarding database entry

>>>>> + *

>>>>> + * String is of the format "SubNet,Intf,NextHopMAC"

>>>>> + *

>>>>> + * @param input  Pointer to string describing route

>>>>> + * @param oif  Pointer to out interface name, as a return value

>>>>> + *

>>>>> + * @return 0 if successful else -1

>>>>> + */

>>>>> +int create_fwd_db_entry(char *input, char **oif);

>>>>> +

>>>>> +/**

>>>>> + * Scan FWD DB entries and resolve output queue and source MAC

>>> address

>>>>> + *

>>>>> + * @param intf   Interface name string

>>>>> + * @param portid Output queue for packet transmit

>>>>> + * @param mac    MAC address of this interface

>>>>> + */

>>>>> +void resolve_fwd_db(char *intf, int portid, uint8_t *mac);

>>>>> +

>>>>> +/**

>>>>> + * Display one forwarding database entry

>>>>> + *

>>>>> + * @param entry  Pointer to entry to display

>>>>> + */

>>>>> +void dump_fwd_db_entry(fwd_db_entry_t *entry);

>>>>> +

>>>>> +/**

>>>>> + * Display the forwarding database

>>>>> + */

>>>>> +void dump_fwd_db(void);

>>>>> +

>>>>> +/**

>>>>> + * Find a matching forwarding database entry

>>>>> + *

>>>>> + * @param key  ipv4 tuple

>>>>> + *

>>>>> + * @return pointer to forwarding DB entry else NULL

>>>>> + */

>>>>> +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key);

>>>>> +

>>>>> +#ifdef __cplusplus

>>>>> +}

>>>>> +#endif

>>>>> +

>>>>> +#endif

>>>>> diff --git a/example/l3fwd/odp_l3fwd_lpm.c

>>>> b/example/l3fwd/odp_l3fwd_lpm.c

>>>>> new file mode 100644

>>>>> index 0000000..1b3bfcf

>>>>> --- /dev/null

>>>>> +++ b/example/l3fwd/odp_l3fwd_lpm.c

>>>>> @@ -0,0 +1,224 @@

>>>>> +/* Copyright (c) 2016, Linaro Limited

>>>>> + * All rights reserved.

>>>>> + *

>>>>> + * SPDX-License-Identifier:     BSD-3-Clause

>>>>> + */

>>>>> +#ifndef _GNU_SOURCE

>>>>> +#define _GNU_SOURCE

>>>>> +#endif

>>>>> +

>>>>> +#include <stdio.h>

>>>>> +#include <stdlib.h>

>>>>> +

>>>>> +#include <example_debug.h>

>>>>> +#include <odp_api.h>

>>>>> +

>>>>> +#include <odp_l3fwd_lpm.h>

>>>>> +

>>>>> +/**

>>>>> + * This is a simple implementation of lpm based on patricia tree.

>>>>> + *

>>>>> + * Tradeoff exists between memory consumption and lookup time.

>>>>> + * Currently it prefers 5 levels: {16, 4, 4, 4, 4}, could be 3

>>>>> + * levels: {16, 8, 8} by defining FIB_NEXT_STRIDE as 8. Other

>>>>> + * levels are also possible.

>>>>> + *

>>>>> + * the ip here is host endian, when doing init or lookup, the

>>>>> + * caller should do endianness conversion if needed.

>>>>> + */

>>>>> +

>>>>> +#define FIB_IP_WIDTH 32

>>>>> +#define FIB_FIRST_STRIDE 16

>>>>> +#define FIB_NEXT_STRIDE 4

>>>>> +#define FIB_NEXT_SIZE (1 << FIB_NEXT_STRIDE)

>>>>> +#define FIB_SUB_COUNT 16384

>>>>> +#define DEPTH_TO_MASK(depth) ((1 << (depth)) - 1)

>>>>> +

>>>>> +typedef struct fib_node_s {

>>>>> +	union {

>>>>> +		uint32_t next_hop;

>>>>> +		struct fib_node_s *next; /* next level table */

>>>>> +	};

>>>>> +	uint8_t valid	:1; /* 1, this node has a valid next hop */

>>>>> +	uint8_t end	:1; /* 0, next points to the extended table */

>>>>> +	uint8_t depth	:6; /* bit length of subnet mask */

>>>>> +} fib_node_t;

>>>>> +

>>>>> +typedef struct fib_sub_tbl_s {

>>>>> +	fib_node_t *fib_nodes;

>>>>> +	uint32_t fib_count;

>>>>> +	uint32_t fib_idx;

>>>>> +} fib_sub_tbl_t;

>>>>> +

>>>>> +static fib_node_t fib_rt_tbl[1 << FIB_FIRST_STRIDE];

>>>>> +static fib_sub_tbl_t fib_lpm_cache;

>>>>> +

>>>>> +static inline fib_node_t *fib_alloc_sub(void)

>>>>> +{

>>>>> +	fib_node_t *sub_tbl = NULL;

>>>>> +	uint32_t i, nb_entry;

>>>>> +

>>>>> +	/* extend to next level */

>>>>> +	if (fib_lpm_cache.fib_idx < fib_lpm_cache.fib_count) {

>>>>> +		nb_entry = (fib_lpm_cache.fib_idx + 1) *

> FIB_NEXT_SIZE;

>>>>> +		sub_tbl = &fib_lpm_cache.fib_nodes[nb_entry];

>>>>> +		fib_lpm_cache.fib_idx++;

>>>>> +		for (i = 0; i < nb_entry; i++) {

>>>>> +			sub_tbl[i].valid = 0;

>>>>> +			sub_tbl[i].end = 1;

>>>>> +		}

>>>>> +	}

>>>>> +

>>>>> +	return sub_tbl;

>>>>> +}

>>>>> +

>>>>> +static void fib_update_node(fib_node_t *fe, int port, int depth)

>>>>> +{

>>>>> +	fib_node_t *p;

>>>>> +	int i;

>>>>> +

>>>>> +	if (fe->end) {

>>>>> +		if (!fe->valid) {

>>>>> +			fe->depth = depth;

>>>>> +			fe->next_hop = port;

>>>>> +			fe->valid = 1;

>>>>> +		} else if (fe->depth <= depth) {

>>>>> +			fe->next_hop = port;

>>>>> +			fe->depth = depth;

>>>>> +		}

>>>>> +

>>>>> +		return;

>>>>> +	}

>>>>> +

>>>>> +	for (i = 0; i < FIB_NEXT_SIZE; i++) {

>>>>> +		p = &fe->next[i];

>>>>> +		if (p->end)

>>>>> +			fib_update_node(p, port, depth);

>>>>> +	}

>>>>> +}

>>>>> +

>>>>> +static void fib_insert_node(fib_node_t *fe, uint32_t ip, uint32_t

>>> next_hop,

>>>>> +			    int ip_width, int eat_bits, int depth)

>>>>> +{

>>>>> +	int i;

>>>>> +	uint32_t idx, port;

>>>>> +	fib_node_t *p;

>>>>> +

>>>>> +	if (fe->end) {

>>>>> +		port = fe->next_hop;

>>>>> +		p = fib_alloc_sub();

>>>>> +		if (!p)

>>>>> +			return;

>>>>> +

>>>>> +		fe->next = p;

>>>>> +		fe->end = 0;

>>>>> +		if (fe->valid) {

>>>>> +			for (i = 0; i < FIB_NEXT_SIZE; i++) {

>>>>> +				p = &fe->next[i];

>>>>> +				p->next_hop = port;

>>>>> +				p->depth = fe->depth;

>>>>> +			}

>>>>> +		}

>>>>> +	}

>>>>> +	if (depth - eat_bits <= FIB_NEXT_STRIDE) {

>>>>> +		ip_width -= depth - eat_bits;

>>>>> +		idx = ip >> ip_width;

>>>>> +		ip &= DEPTH_TO_MASK(ip_width);

>>>>> +		p = &fe->next[idx];

>>>>> +		fib_update_node(p, next_hop, depth);

>>>>> +	} else {

>>>>> +		ip_width -= FIB_NEXT_STRIDE;

>>>>> +		idx = ip >> ip_width;

>>>>> +		p = &fe->next[idx];

>>>>> +		ip &= DEPTH_TO_MASK(ip_width);

>>>>> +		eat_bits += FIB_NEXT_STRIDE;

>>>>> +		fib_insert_node(p, ip, next_hop, ip_width, eat_bits,

> depth);

>>>>> +	}

>>>>> +}

>>>>> +

>>>>> +void fib_tbl_init(void)

>>>>> +{

>>>>> +	int i;

>>>>> +	fib_node_t *fe;

>>>>> +	uint32_t size;

>>>>> +	odp_shm_t lpm_shm;

>>>>> +

>>>>> +	for (i = 0; i < (1 << FIB_FIRST_STRIDE); i++) {

>>>>> +		fe = &fib_rt_tbl[i];

>>>>> +		fe->valid = 0;

>>>>> +		fe->end = 1;

>>>>> +		fe->depth = 0;

>>>>> +		fe->next_hop = 0;

>>>>> +	}

>>>>> +

>>>>> +	size = FIB_NEXT_SIZE * FIB_SUB_COUNT;

>>>>> +	/*Reserve memory for Routing hash table*/

>>>>> +	lpm_shm = odp_shm_reserve("fib_lpm_sub", size,

>>>> ODP_CACHE_LINE_SIZE, 0);

>>>>> +	fe = odp_shm_addr(lpm_shm);

>>>>> +	if (!fe) {

>>>>> +		EXAMPLE_ERR("Error: shared mem alloc failed for lpm

>>>> cache.\n");

>>>>> +		exit(-1);

>>>>> +	}

>>>>> +

>>>>> +	fib_lpm_cache.fib_nodes = fe;

>>>>> +	fib_lpm_cache.fib_count = FIB_SUB_COUNT;

>>>>> +	fib_lpm_cache.fib_idx = 0;

>>>>> +}

>>>>> +

>>>>> +void fib_tbl_insert(uint32_t ip, int port, int depth)

>>>>> +{

>>>>> +	fib_node_t *fe, *p;

>>>>> +	uint32_t idx;

>>>>> +	int i, j;

>>>>> +	int nb_bits;

>>>>> +

>>>>> +	nb_bits = FIB_FIRST_STRIDE;

>>>>> +	idx = ip >> nb_bits;

>>>>> +	fe = &fib_rt_tbl[idx];

>>>>> +	if (depth <= nb_bits) {

>>>>> +		if (fe->end) {

>>>>> +			fe->next_hop = port;

>>>>> +			fe->depth = depth;

>>>>> +			fe->valid = 1;

>>>>> +			return;

>>>>> +		}

>>>>> +

>>>>> +		for (i = 0; i < FIB_NEXT_SIZE; i++) {

>>>>> +			p = &fe->next[i];

>>>>> +			if (p->end)

>>>>> +				fib_update_node(p, port, depth);

>>>>> +			else

>>>>> +				for (j = 0; j < FIB_NEXT_SIZE; j++)

>>>>> +					fib_update_node(&p->next[j],

> port,

>>>>> +							depth);

>>>>> +		}

>>>>> +

>>>>> +		return;

>>>>> +	}

>>>>> +

>>>>> +	/* need to check sub table */

>>>>> +	ip &= DEPTH_TO_MASK(FIB_IP_WIDTH - nb_bits);

>>>>> +	fib_insert_node(fe, ip, port, FIB_IP_WIDTH - nb_bits, nb_bits,

> depth);

>>>>> +}

>>>>> +

>>>>> +int fib_tbl_lookup(uint32_t ip, int *port)

>>>>> +{

>>>>> +	fib_node_t *fe;

>>>>> +	uint32_t idx;

>>>>> +	int nb_bits;

>>>>> +

>>>>> +	nb_bits = FIB_IP_WIDTH - FIB_FIRST_STRIDE;

>>>>> +	idx = ip >> nb_bits;

>>>>> +	fe = &fib_rt_tbl[idx];

>>>>> +

>>>>> +	ip &= DEPTH_TO_MASK(nb_bits);

>>>>> +	while (!fe->end) {

>>>>> +		nb_bits -= FIB_NEXT_STRIDE;

>>>>> +		idx = ip >> nb_bits;

>>>>> +		fe = &fe->next[idx];

>>>>> +		ip &= DEPTH_TO_MASK(nb_bits);

>>>>> +	}

>>>>> +	*port = fe->next_hop;

>>>>> +

>>>>> +	return fe->valid ? 0 : -1;

>>>>> +}

>>>>> diff --git a/example/l3fwd/odp_l3fwd_lpm.h

>>>> b/example/l3fwd/odp_l3fwd_lpm.h

>>>>> new file mode 100644

>>>>> index 0000000..8f78e39

>>>>> --- /dev/null

>>>>> +++ b/example/l3fwd/odp_l3fwd_lpm.h

>>>>> @@ -0,0 +1,20 @@

>>>>> +/* Copyright (c) 2016, Linaro Limited

>>>>> + * All rights reserved.

>>>>> + *

>>>>> + * SPDX-License-Identifier:     BSD-3-Clause

>>>>> + */

>>>>> +

>>>>> +#ifndef ODP_L3FWD_LPM_H_

>>>>> +#define ODP_L3FWD_LPM_H_

>>>> it has to be _ODP_EXAMPLE_L3FWD_LPM_H

>>>>

>>>> i.e. define should be started with "_" meaning that it's not odp api.

>>>> (I know that in some places we use _ODP_ in other places we use ODP_ for

>>>> that

>>>> case. But that is wrong. We should not overlap with api name space.).

>>>>

>>>>> +

>>>>> +#ifdef __cplusplus

>>>>> +extern "C" {

>>>>> +#endif

>>>>> +void fib_tbl_init(void);

>>>>> +void fib_tbl_insert(uint32_t ip, int port, int depth);

>>>>> +int fib_tbl_lookup(uint32_t ip, int *port);

>>>>> +#ifdef __cplusplus

>>>>> +}

>>>>> +#endif

>>>>> +

>>>>> +#endif

>>>>> diff --git a/example/m4/configure.m4 b/example/m4/configure.m4

>>>>> index bbda38f..78ef396 100644

>>>>> --- a/example/m4/configure.m4

>>>>> +++ b/example/m4/configure.m4

>>>>> @@ -19,5 +19,6 @@ AC_CONFIG_FILES([example/classifier/Makefile

>>>>>    		 example/timer/Makefile

>>>>>    		 example/traffic_mgmt/Makefile

>>>>>    		 example/l2fwd_simple/Makefile

>>>>> +		 example/l3fwd/Makefile

>>>>>    		 example/switch/Makefile

>>>>>    		 example/hello/Makefile])

>>>> On thought is what we need with l2fwd. We places it under

>>>> test/performance/ but it's actual example app.

>>>> Not sure that it's clear for new users. Definitely l2fwd and l3fwd have

>>>> to be at the same level. I'm thinking

>>>> about placing both to test/performance/ and create symlink with some

>>>> README to example/. But I'm not sure

>>>> about that, we need to discuss.

>>> Yes.  In my opinion, we do not need a test/performance directory.

>>> If want to benchmarking automatically, we can build all the apps but run

>> only

>>> interested one.

>>>

>>>> Maxim.

>>>>

>
Forrest Shi July 26, 2016, 8:04 a.m. UTC | #7
Hi Maxim,

What's your idea bout below comment?

Increment one value from number of threads might be not good thing to do
due to 1) performance 2) atomic operations break.
It's better if each thread has it's own counters and control threads
summaries values.
=====================================================
The values are already per-thread one copy and stored in thread arguments.
Each thread has its own thread argument.

Thanks,
Forrest

> -----Original Message-----

> From: Maxim Uvarov [mailto:maxim.uvarov@linaro.org]

> Sent: Monday, July 25, 2016 20:45

> To: forrest.shi <forrest.shi@linaro.org>

> Cc: lng-odp@lists.linaro.org

> Subject: Re: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

> 

> On 07/25/16 11:55, forrest.shi wrote:

> > Hi Maxim,

> >

> > odp_packet_l3_ptr() can return NULL. And you will have null pointer

> > deference here for non IP packet.

> > +	ip->chksum = odph_ipv4_csum_update(pkt);

> > +	eth = odp_packet_l2_ptr(pkt, NULL);

> > same here eth might be NULL. Better to optimize checks  ifs with

> > odp_unlikely().

> > =====================================================

> > Before called this function, drop_err_pkts(...) has filtered bad packet

first.
> > So looks like no necessary re-check l2/l3 pointer is NULL.

> >

> > Thanks,

> > Forrest

> ah, ok, if you already dropped bad packets that it's fine.

> 

> Maxim.

> 

> 

> >

> >> -----Original Message-----

> >> From: forrest.shi [mailto:forrest.shi@linaro.org]

> >> Sent: Monday, July 25, 2016 16:06

> >> To: 'Maxim Uvarov' <maxim.uvarov@linaro.org>

> >> Cc: lng-odp@lists.linaro.org

> >> Subject: RE: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

> >>

> >> Hi Maxim,

> >>

> >> Any comments?

> >>

> >> Thanks,

> >> Forrest

> >>

> >>> -----Original Message-----

> >>> From: forrest.shi [mailto:forrest.shi@linaro.org]

> >>> Sent: Friday, July 15, 2016 16:22

> >>> To: 'Maxim Uvarov' <maxim.uvarov@linaro.org>

> >>> Cc: lng-odp@lists.linaro.org

> >>> Subject: RE: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

> >>>

> >>> Hi Maxim,

> >>>

> >>> The patch is length.  I extracted the comments I have some question as

> >> below,

> >>> Others will be fixed.

> >>>

> >>>   Increment one value from number of threads might be not good thing to

> do

> >>>   due to 1) performance 2) atomic operations break.

> >>>   It's better if each thread has it's own counters and control threads

> >>>   summaries values.

> >>>

> ==========================================================

> >>> Not catch your idea. Here each thread has its own counters and is

totaled
> >>> before main exit.

> >>>

> >>>

> >>>   main should end if odp_destroy_global(). And to add this to 'make

check'
> >>>   requires probably to add some logic when we think that test passed and

> >>>   when we thing that test failed.

> >>>   I think you copied print_speed_stats() from l2fwd,  there return code

> >>>   returns if app passed or not.

> >>>

> ==========================================================

> >>> The testing PASS depends on the print_speed_stats() output that is used

> by

> >>> test shell script.

> >>> Here return value is not taken  as PASS or FAIL.

> >>>

> >>>

> >>> On thought is what we need with l2fwd. We places it under

> >>>   test/performance/ but it's actual example app.

> >>>   Not sure that it's clear for new users. Definitely l2fwd and l3fwd

have
> >>>   to be at the same level. I'm thinking

> >>>   about placing both to test/performance/ and create symlink with some

> >>>   README to example/. But I'm not sure

> >>>   about that, we need to discuss.

> >>>

> ==========================================================

> >>> In my opinion, don't need to put app in performance.

> >>>  From literal meaning, test should handle testing things, no new app.

> >>>

> >>> Thanks,

> >>> Forrest

> >>>

> >>>> -----Original Message-----

> >>>> From: lng-odp [mailto:lng-odp-bounces@lists.linaro.org] On Behalf Of

> >> Maxim

> >>>> Uvarov

> >>>> Sent: Wednesday, July 13, 2016 17:10

> >>>> To: lng-odp@lists.linaro.org

> >>>> Subject: Re: [lng-odp] [PATCH 1/2 v6] example: introducing l3fwd

> >>>>

> >>>> On 07/13/16 10:29, forrest.shi@linaro.org wrote:

> >>>>> From: Xuelin Shi <forrest.shi@linaro.org>

> >>>>>

> >>>>> multi-thread, multi-queues and bi-directional forwarding.

> >>>>>

> >>>>> support (port, queue, thread) arguments in cmdline which specify how

> >>>>> the threads handle which rx queue at which port, if no this argument,

> >>>>> default specification used.

> >>>>>

> >>>>> both hash and lpm based lookup methods are supported, default lpm.

> >>>>>

> >>>>> Signed-off-by: Xuelin Shi <forrest.shi@linaro.org>

> >>>>> ---

> >>>>>    example/Makefile.am           |    2 +-

> >>>>>    example/l3fwd/.gitignore      |    4 +

> >>>>>    example/l3fwd/Makefile.am     |   18 +

> >>>>>    example/l3fwd/odp_l3fwd.c     | 1072

> >>>> +++++++++++++++++++++++++++++++++++++++++

> >>>>>    example/l3fwd/odp_l3fwd_db.c  |  408 ++++++++++++++++

> >>>>>    example/l3fwd/odp_l3fwd_db.h  |  130 +++++

> >>>>>    example/l3fwd/odp_l3fwd_lpm.c |  224 +++++++++

> >>>>>    example/l3fwd/odp_l3fwd_lpm.h |   20 +

> >>>>>    example/m4/configure.m4       |    1 +

> >>>>>    9 files changed, 1878 insertions(+), 1 deletion(-)

> >>>>>    create mode 100644 example/l3fwd/.gitignore

> >>>>>    create mode 100644 example/l3fwd/Makefile.am

> >>>>>    create mode 100644 example/l3fwd/odp_l3fwd.c

> >>>>>    create mode 100644 example/l3fwd/odp_l3fwd_db.c

> >>>>>    create mode 100644 example/l3fwd/odp_l3fwd_db.h

> >>>>>    create mode 100644 example/l3fwd/odp_l3fwd_lpm.c

> >>>>>    create mode 100644 example/l3fwd/odp_l3fwd_lpm.h

> >>>>>

> >>>>> diff --git a/example/Makefile.am b/example/Makefile.am

> >>>>> index 37542af..1f1b62e 100644

> >>>>> --- a/example/Makefile.am

> >>>>> +++ b/example/Makefile.am

> >>>>> @@ -1 +1 @@

> >>>>> -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

> >>>> l2fwd_simple switch hello

> >>>>> +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt

> >>>> l2fwd_simple switch hello l3fwd

> >>>>

> >>>> Hello Forest, you should use one git send-email command for both

> >>>> patches. So 2 patches will be in the same email thread,

> >>>> and not as 2 separate patches.

> >>>>

> >>>> alphabetical order here is required. And in next update please place

all
> >>>> entries vertically, newer patches will be simpler.

> >>>>

> >>> OK, will be fixed.

> >>>

> >>>>> diff --git a/example/l3fwd/.gitignore b/example/l3fwd/.gitignore

> >>>>> new file mode 100644

> >>>>> index 0000000..4a25ae8

> >>>>> --- /dev/null

> >>>>> +++ b/example/l3fwd/.gitignore

> >>>>> @@ -0,0 +1,4 @@

> >>>>> +odp_l3fwd

> >>>>> +Makefile

> >>>>> +Makefile.in

> >>>>> +*.o

> >>>> last 3 entries are not needed due to we already have it in common .git

> >>>> ignore file.

> >>>>

> >>>>> diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am

> >>>>> new file mode 100644

> >>>>> index 0000000..5092aa7

> >>>>> --- /dev/null

> >>>>> +++ b/example/l3fwd/Makefile.am

> >>>>> @@ -0,0 +1,18 @@

> >>>>> +include $(top_srcdir)/example/Makefile.inc

> >>>>> +

> >>>>> +bin_PROGRAMS = odp_l3fwd$(EXEEXT)

> >>>>> +odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static

> >>>>> +odp_l3fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example -

> >>>> I${top_srcdir}/test

> >>>>> +

> >>>>> +noinst_HEADERS = \

> >>>>> +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \

> >>>>> +		  $(top_srcdir)/example/l3fwd/odp_l3fwd_lpm.h \

> >>>>> +		  $(top_srcdir)/example/example_debug.h

> >>>>> +

> >>>>> +dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c

> >>> odp_l3fwd_lpm.c

> >>>>> +

> >>>>> +if test_example

> >>>>> +TESTS = odp_l3fwd_run.sh

> >>>>> +endif

> >>>>> +

> >>>>> +EXTRA_DIST = odp_l3fwd_run.sh

> >>>>> diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c

> >>>>> new file mode 100644

> >>>>> index 0000000..a10ca76

> >>>>> --- /dev/null

> >>>>> +++ b/example/l3fwd/odp_l3fwd.c

> >>>>> @@ -0,0 +1,1072 @@

> >>>>> +/* Copyright (c) 2016, Linaro Limited

> >>>>> + * All rights reserved.

> >>>>> + *

> >>>>> + * SPDX-License-Identifier:     BSD-3-Clause

> >>>>> + */

> >>>>> +

> >>>>> +#include <stdlib.h>

> >>>>> +#include <stdio.h>

> >>>>> +#include <errno.h>

> >>>>> +#include <getopt.h>

> >>>>> +#include <unistd.h>

> >>>>> +#include <inttypes.h>

> >>>>> +

> >>>>> +#include <test_debug.h>

> >>>>> +

> >>>>> +#include <odp_api.h>

> >>>>> +#include <odp/helper/linux.h>

> >>>>> +#include <odp/helper/eth.h>

> >>>>> +#include <odp/helper/ip.h>

> >>>>> +#include <odp/helper/udp.h>

> >>>>> +#include <odp/helper/tcp.h>

> >>>>> +

> >>>>> +#include "odp_l3fwd_db.h"

> >>>>> +#include "odp_l3fwd_lpm.h"

> >>>>> +

> >>>>> +#define POOL_NUM_PKT	8192

> >>>>> +#define POOL_SEG_LEN	1856

> >>>>> +#define MAX_PKT_BURST	32

> >>>>> +

> >>>>> +#define MAX_NB_WORKER	32

> >>>>> +#define MAX_NB_PKTIO	32

> >>>>> +#define MAX_NB_QUEUE	32

> >>>>> +#define MAX_NB_QCONFS	1024

> >>>>> +#define MAX_NB_ROUTE	32

> >>>>> +

> >>>>> +#define INVALID_ID	(-1)

> >>>>> +#define PRINT_INTERVAL	10	/* interval seconds of printing

> >> stats

> >>> */

> >>>>> +

> >>>>> +/** Get rid of path in filename - only for unix-type paths using '/'

> > */

> >>>>> +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \

> >>>>> +			    strrchr((file_name), '/') + 1 :

> > (file_name))

> >>>>> +

> >>>>> +struct l3fwd_pktio_s {

> >>>>> +	odp_pktio_t pktio;

> >>>>> +	odph_ethaddr_t mac_addr;

> >>>>> +	odp_pktin_queue_t ifin[MAX_NB_QUEUE];

> >>>>> +	odp_pktout_queue_t ifout[MAX_NB_QUEUE];

> >>>>> +	int nb_rxq;

> >>>>> +	int nb_txq;

> >>>>> +	int rxq_idx;

> >>>>> +	int txq_idx;

> >>>>> +};

> >>>>> +

> >>>>> +struct l3fwd_qconf_s {

> >>>>> +	uint8_t if_idx;		/* port index */

> >>>>> +	uint8_t rxq_idx;	/* recv queue index in a port */

> >>>>> +	uint8_t core_idx;	/* this core should handle traffic */

> >>>>> +};

> >>>>> +

> >>>>> +struct thread_arg_s {

> >>>>> +	uint64_t packets;

> >>>>> +	uint64_t rx_drops;

> >>>>> +	uint64_t tx_drops;

> >>>>> +	struct {

> >>>>> +		int used;

> >>>>> +		int rxq[MAX_NB_QUEUE];

> >>>>> +		int txq[MAX_NB_QUEUE];

> >>>>> +	} pktio[MAX_NB_PKTIO];

> >>>>> +	int thr_idx;

> >>>>> +	int nb_pktio;

> >>>>> +};

> >>>>> +

> >>>>> +typedef struct {

> >>>>> +	char *if_names[MAX_NB_PKTIO];

> >>>>> +	int if_count;

> >>>>> +	char *route_str[MAX_NB_ROUTE];

> >>>>> +	int worker_count;

> >>>>> +	struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS];

> >>>>> +	int qconf_count;

> >>>>> +	uint32_t duration; /* seconds to run */

> >>>>> +	uint8_t hash_mode; /* 1:hash, 0:lpm */

> >>>>> +	uint8_t dest_mac_changed[MAX_NB_PKTIO]; /* 1: dest mac

> from

> >>>> cmdline */

> >>>>> +} app_args_t;

> >>>>> +

> >>>>> +struct {

> >>>>> +	app_args_t		cmd_args;

> >>>>> +	struct l3fwd_pktio_s	l3fwd_pktios[MAX_NB_PKTIO];

> >>>>> +	odph_odpthread_t	l3fwd_workers[MAX_NB_WORKER];

> >>>>> +	struct thread_arg_s	worker_args[MAX_NB_WORKER];

> >>>>> +	odph_ethaddr_t

> 	eth_dest_mac[MAX_NB_PKTIO];

> >>>>> +

> >>>>> +	/* forward func, hash or lpm */

> >>>>> +	int (*fwd_func)(odp_packet_t pkt, int sif);

> >>>>> +} global;

> >>>>> +

> >>>>> +/** Global barrier to synchronize main and workers */

> >>>>> +static odp_barrier_t barrier;

> >>>>> +static int exit_threads;	/**< Break workers loop if set to 1 */

> >>>>> +

> >>>>> +static void print_usage(char *progname);

> >>>> if you place print_usage() above main there is no need for declaration.

> >> The

> >>>> same for other functions like print_info(). Not critical but you can

> >>>> just remove

> >>>> that lines and make example shorter.

> >>>>

> >>> Will be fixed.

> >>>

> >>>>> +static void print_qconf_table(app_args_t *args);

> >>>>> +static void print_info(char *progname, app_args_t *args);

> >>>>> +static int print_speed_stats(int num_workers, int duration, int

> >> timeout);

> >>>>> +static void parse_cmdline_args(int argc, char *argv[], app_args_t

> >> *args);

> >>>>> +static int parse_config(char *cfg_str, app_args_t *args);

> >>>>> +static void setup_worker_qconf(app_args_t *args);

> >>>>> +static void setup_fwd_db(void);

> >>>>> +static int find_port_id_by_name(char *name, app_args_t *args);

> >>>>> +static int split_string(char *str, int stringlen,

> >>>>> +			char **tokens, int maxtokens, char delim);

> >>>>> +

> >>>>> +static int create_pktio(const char *name, odp_pool_t pool,

> >>>>> +			struct l3fwd_pktio_s *fwd_pktio)

> >>>>> +{

> >>>>> +	odp_pktio_param_t pktio_param;

> >>>>> +	odp_pktio_t pktio;

> >>>>> +	odp_pktio_capability_t capa;

> >>>>> +	int rc;

> >>>>> +

> >>>>> +	odp_pktio_param_init(&pktio_param);

> >>>>> +

> >>>>> +	pktio = odp_pktio_open(name, pool, &pktio_param);

> >>>>> +	if (pktio == ODP_PKTIO_INVALID) {

> >>>>> +		printf("Failed to open %s\n", name);

> >>>>> +		return -1;

> >>>>> +	}

> >>>>> +	fwd_pktio->pktio = pktio;

> >>>>> +

> >>>>> +	rc = odp_pktio_capability(pktio, &capa);

> >>>>> +	if (rc) {

> >>>>> +		printf("Error: pktio %s: unable to read

> > capabilities!\n",

> >>>>> +		       name);

> >>>>> +

> >>>>> +		return -1;

> >>>>> +	}

> >>>>> +

> >>>>> +	fwd_pktio->nb_rxq = (int)capa.max_input_queues;

> >>>>> +	fwd_pktio->nb_txq = (int)capa.max_output_queues;

> >>>>> +

> >>>>> +	if (fwd_pktio->nb_rxq > MAX_NB_QUEUE)

> >>>>> +		fwd_pktio->nb_rxq = MAX_NB_QUEUE;

> >>>>> +

> >>>>> +	if (fwd_pktio->nb_txq > MAX_NB_QUEUE)

> >>>>> +		fwd_pktio->nb_txq = MAX_NB_QUEUE;

> >>>>> +

> >>>>> +	return 0;

> >>>>> +}

> >>>>> +

> >>>>> +static void setup_fwd_db(void)

> >>>>> +{

> >>>>> +	fwd_db_entry_t *entry;

> >>>>> +	int if_idx;

> >>>>> +	app_args_t *args;

> >>>>> +

> >>>>> +	args = &global.cmd_args;

> >>>>> +	if (args->hash_mode)

> >>>>> +		init_fwd_hash_cache();

> >>>>> +	else

> >>>>> +		fib_tbl_init();

> >>>>> +

> >>>>> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> > {

> >>>>> +		if_idx = entry->oif_id;

> >>>>> +		if (!args->hash_mode)

> >>>>> +			fib_tbl_insert(entry->subnet.addr, if_idx,

> >>>>> +				       entry->subnet.depth);

> >>>>> +		if (args->dest_mac_changed[if_idx])

> >>>>> +			global.eth_dest_mac[if_idx] = entry->dst_mac;

> >>>>> +		else

> >>>>> +			entry->dst_mac = global.eth_dest_mac[if_idx];

> >>>>> +	}

> >>>>> +}

> >>>>> +

> >>>>> +static int l3fwd_pkt_hash(odp_packet_t pkt, int sif)

> >>>>> +{

> >>>>> +	fwd_db_entry_t *entry;

> >>>>> +	ipv4_tuple5_t key;

> >>>>> +	odph_ethhdr_t *eth;

> >>>>> +	odph_udphdr_t  *udp;

> >>>>> +	odph_ipv4hdr_t *ip;

> >>>>> +	uint32_t len;

> >>>>> +	int dif;

> >>>>> +

> >>>>> +	ip = odp_packet_l3_ptr(pkt, &len);

> >>>>> +	key.dst_ip = odp_be_to_cpu_32(ip->dst_addr);

> >>>>> +	key.src_ip = odp_be_to_cpu_32(ip->src_addr);

> >>>>> +	key.proto = ip->proto;

> >>>>> +

> >>>>> +	if (odp_packet_has_udp(pkt) ||

> >>>>> +	    odp_packet_has_tcp(pkt)) {

> >>>>> +		/* UDP or TCP*/

> >>>>> +		void *ptr = odp_packet_l4_ptr(pkt, NULL);

> >>>>> +

> >>>>> +		udp = (odph_udphdr_t *)ptr;

> >>>>> +		key.src_port = odp_be_to_cpu_16(udp->src_port);

> >>>>> +		key.dst_port = odp_be_to_cpu_16(udp->dst_port);

> >>>>> +	} else {

> >>>>> +		key.src_port = 0;

> >>>>> +		key.dst_port = 0;

> >>>>> +	}

> >>>>> +

> >>>>> +	entry = find_fwd_db_entry(&key);

> >>>>> +	ip->ttl--;

> >>>> odp_packet_l3_ptr() can return NULL. And you will have null pointer

> >>>> deference here for non IP packet.

> >>>>> +	ip->chksum = odph_ipv4_csum_update(pkt);

> >>>>> +	eth = odp_packet_l2_ptr(pkt, NULL);

> >>>> same here eth might be NULL. Better to optimize checks  ifs with

> >>>> odp_unlikely().

> >>> Will be fixed.

> >>>

> >>>>> +	if (entry) {

> >>>>> +		eth->src = entry->src_mac;

> >>>>> +		eth->dst = entry->dst_mac;

> >>>>> +		dif = entry->oif_id;

> >>>>> +	} else {

> >>>>> +		/* no route, send by src port */

> >>>>> +		eth->dst = eth->src;

> >>>>> +		dif = sif;

> >>>>> +	}

> >>>>> +

> >>>>> +	return dif;

> >>>>> +}

> >>>>> +

> >>>>> +static int l3fwd_pkt_lpm(odp_packet_t pkt, int sif)

> >>>>> +{

> >>>>> +	odph_ipv4hdr_t *ip;

> >>>>> +	odph_ethhdr_t *eth;

> >>>>> +	uint32_t len;

> >>>>> +	int dif;

> >>>>> +	int ret;

> >>>>> +

> >>>>> +	ip = odp_packet_l3_ptr(pkt, &len);

> >>>> NULL

> >>>>> +	ip->ttl--;

> >>>>> +	ip->chksum = odph_ipv4_csum_update(pkt);

> >>>>> +	eth = odp_packet_l2_ptr(pkt, NULL);

> >>>> NULL

> >>>>> +

> >>>>> +	/* network byte order maybe different from host */

> >>>>> +	ret = fib_tbl_lookup(odp_be_to_cpu_32(ip->dst_addr), &dif);

> >>>>> +	if (ret)

> >>>>> +		dif = sif;

> >>>>> +

> >>>>> +	eth->dst = global.eth_dest_mac[dif];

> >>>>> +	eth->src = global.l3fwd_pktios[dif].mac_addr;

> >>>>> +

> >>>>> +	return dif;

> >>>>> +}

> >>>>> +

> >>>>> +/**

> >>>>> + * Drop packets which input parsing marked as containing errors.

> >>>>> + *

> >>>>> + * Frees packets with error and modifies pkt_tbl[] to only contain

> >>> packets

> >>>> with

> >>>>> + * no detected errors.

> >>>>> + *

> >>>>> + * @param pkt_tbl  Array of packets

> >>>>> + * @param num      Number of packets in pkt_tbl[]

> >>>>> + *

> >>>>> + * @return Number of packets dropped

> >>>>> + */

> >>>>> +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)

> >>>>> +{

> >>>>> +	odp_packet_t pkt;

> >>>>> +	unsigned dropped = 0;

> >>>>> +	unsigned i, j;

> >>>>> +

> >>>>> +	for (i = 0, j = 0; i < num; ++i) {

> >>>>> +		pkt = pkt_tbl[i];

> >>>>> +

> >>>>> +		if (odp_unlikely(odp_packet_has_error(pkt) ||

> >>>>> +				 !odp_packet_has_ipv4(pkt))) {

> >>>>> +			odp_packet_free(pkt);

> >>>>> +			dropped++;

> >>>>> +		} else if (odp_unlikely(i != j++)) {

> >>>>> +			pkt_tbl[j - 1] = pkt;

> >>>>> +		}

> >>>>> +	}

> >>>>> +

> >>>>> +	return dropped;

> >>>>> +}

> >>>>> +

> >>>>> +static void l3fwd_one_queue(uint32_t sif, uint8_t rxq_id, void

> >> *thr_arg)

> >>>>> +{

> >>>>> +	struct l3fwd_pktio_s *port;

> >>>>> +	odp_packet_t *tbl;

> >>>>> +	odp_pktout_queue_t outq;

> >>>>> +	odp_packet_t pkt_tbl[MAX_PKT_BURST];

> >>>>> +	struct thread_arg_s *arg;

> >>>>> +	uint8_t txq_id;

> >>>>> +	int pkts, drop, sent;

> >>>>> +	int dif, dst_port;

> >>>>> +	int i;

> >>>>> +

> >>>>> +	arg = thr_arg;

> >>>>> +	port = &global.l3fwd_pktios[sif];

> >>>>> +	pkts = odp_pktin_recv(port->ifin[rxq_id], pkt_tbl,

> > MAX_PKT_BURST);

> >>>>> +	if (pkts <= 0)

> >>>>> +		return;

> >>>>> +	arg->packets += pkts;

> >>>>> +	drop = drop_err_pkts(pkt_tbl, pkts);

> >>>>> +	pkts -= drop;

> >>>>> +	arg->rx_drops += drop;

> >>>> Increment one value from number of threads might be not good thing to

> >> do

> >>>> due to 1) performance 2) atomic operations break.

> >>>> It's better if each thread has it's own counters and control threads

> >>>> summaries values.

> >>> Not catch your idea. Here each thread has its own counters.

> >>> It will be totaled before exit.

> >>>

> >>>>> +

> >>>>> +	dif = global.fwd_func(pkt_tbl[0], sif);

> >>>>> +	tbl = &pkt_tbl[0];

> >>>>> +	while (pkts) {

> >>>>> +		dst_port = dif;

> >>>>> +		for (i = 1; i < pkts; i++) {

> >>>>> +			dif = global.fwd_func(tbl[i], sif);

> >>>>> +			if (dif != dst_port)

> >>>>> +				break;

> >>>>> +		}

> >>>>> +		txq_id = arg->pktio[dst_port].txq[rxq_id];

> >>>>> +		outq = global.l3fwd_pktios[dst_port].ifout[txq_id];

> >>>>> +		sent = odp_pktout_send(outq, tbl, i);

> >>>>> +		if (odp_unlikely(sent < i)) {

> >>>>> +			sent = sent < 0 ? 0 : sent;

> >>>>> +			odp_packet_free_multi(&tbl[sent], i - sent);

> >>>>> +			arg->tx_drops += i - sent;

> >>>>> +		}

> >>>>> +

> >>>>> +		if (i < pkts)

> >>>>> +			tbl += i;

> >>>>> +

> >>>>> +		pkts -= i;

> >>>>> +	}

> >>>>> +}

> >>>>> +

> >>>>> +static int run_worker(void *arg)

> >>>>> +{

> >>>>> +	int if_idx, rxq_idx;

> >>>>> +	struct thread_arg_s *thr_arg = arg;

> >>>>> +	struct l3fwd_pktio_s *port;

> >>>>> +

> >>>>> +	odp_barrier_wait(&barrier);

> >>>>> +

> >>>>> +	while (!exit_threads) {

> >>>>> +		for (if_idx = 0; if_idx < thr_arg->nb_pktio; if_idx++)

> > {

> >>>>> +			if (!thr_arg->pktio[if_idx].used ||

> >>>>> +			    thr_arg->thr_idx == INVALID_ID)

> >>>>> +				continue;

> >>>>> +

> >>>>> +			port = &global.l3fwd_pktios[if_idx];

> >>>>> +			for (rxq_idx = 0; rxq_idx < port->rxq_idx;

> > rxq_idx++)

> >>>>> +				l3fwd_one_queue(if_idx, rxq_idx, arg);

> >>>>> +		}

> >>>>> +	}

> >>>>> +

> >>>>> +	/* Make sure that latest stat writes are visible to other

> > threads */

> >>>>> +	odp_mb_full();

> >>>>> +

> >>>>> +	return 0;

> >>>>> +}

> >>>>> +

> >>>>> +static int find_port_id_by_name(char *name, app_args_t *args)

> >>>>> +{

> >>>>> +	int i;

> >>>>> +

> >>>>> +	if (!name)

> >>>>> +		return -1;

> >>>>> +

> >>>>> +	for (i = 0; i < args->if_count; i++) {

> >>>>> +		if (!strcmp(name, args->if_names[i]))

> >>>>> +			return i;

> >>>>> +	}

> >>>>> +

> >>>>> +	return -1;

> >>>>> +}

> >>>>> +

> >>>>> +int main(int argc, char **argv)

> >>>>> +{

> >>>>> +	odph_odpthread_t thread_tbl[MAX_NB_WORKER];

> >>>>> +	odp_pool_t pool;

> >>>>> +	odp_pool_param_t params;

> >>>>> +	odp_instance_t instance;

> >>>>> +	odph_odpthread_params_t thr_params;

> >>>>> +	odp_cpumask_t cpumask;

> >>>>> +	int cpu, i, j, nb_worker;

> >>>>> +	uint8_t mac[ODPH_ETHADDR_LEN];

> >>>>> +	app_args_t *args;

> >>>>> +	struct thread_arg_s *thr_arg;

> >>>>> +	char *oif;

> >>>>> +	int oid;

> >>>>> +

> >>>>> +	if (odp_init_global(&instance, NULL, NULL)) {

> >>>>> +		printf("Error: ODP global init failed.\n");

> >>>>> +		exit(1);

> >>>>> +	}

> >>>>> +

> >>>>> +	if (odp_init_local(instance, ODP_THREAD_CONTROL)) {

> >>>>> +		printf("Error: ODP local init failed.\n");

> >>>>> +		exit(1);

> >>>>> +	}

> >>>>> +

> >>>>> +	/* Clear global argument and initialize the dest mac as

> > 2:0:0:0:0:x */

> >>>>> +	memset(&global, 0, sizeof(global));

> >>>>> +	mac[0] = 2;

> >>>>> +	for (i = 0; i < MAX_NB_PKTIO; i++) {

> >>>>> +		mac[ODPH_ETHADDR_LEN - 1] = (uint8_t)i;

> >>>>> +		memcpy(global.eth_dest_mac[i].addr, mac,

> >>>> ODPH_ETHADDR_LEN);

> >>>>> +	}

> >>>>> +

> >>>>> +	/* Initialize the thread arguments */

> >>>>> +	for (i = 0; i < MAX_NB_WORKER; i++) {

> >>>>> +		thr_arg = &global.worker_args[i];

> >>>>> +		for (j = 0; j < MAX_NB_PKTIO; j++) {

> >>>>> +			thr_arg->thr_idx = INVALID_ID;

> >>>>> +			memset(thr_arg->pktio[j].rxq, INVALID_ID,

> >>>>> +			       sizeof(thr_arg->pktio[j].rxq));

> >>>>> +		}

> >>>>> +	}

> >>>>> +

> >>>>> +	/* Parse cmdline arguments */

> >>>>> +	args = &global.cmd_args;

> >>>>> +	parse_cmdline_args(argc, argv, args);

> >>>>> +

> >>>>> +	/* Init l3fwd table */

> >>>>> +	init_fwd_db();

> >>>>> +

> >>>>> +	/* Add route into table */

> >>>>> +	for (i = 0; i < MAX_NB_ROUTE; i++) {

> >>>>> +		if (args->route_str[i]) {

> >>>>> +			oif = NULL;

> >>>>> +			create_fwd_db_entry(args->route_str[i], &oif);

> >>>>> +			oid = find_port_id_by_name(oif, args);

> >>>>> +			if (oid != -1)

> >>>>> +				args->dest_mac_changed[oid] = 1;

> >>>>> +		}

> >>>>> +	}

> >>>>> +

> >>>>> +	print_info(NO_PATH(argv[0]), args);

> >>>>> +

> >>>>> +	/* Create packet pool */

> >>>>> +	odp_pool_param_init(&params);

> >>>>> +	params.pkt.seg_len = POOL_SEG_LEN;

> >>>>> +	params.pkt.len     = POOL_SEG_LEN;

> >>>>> +	params.pkt.num     = POOL_NUM_PKT;

> >>>>> +	params.type        = ODP_POOL_PACKET;

> >>>>> +

> >>>>> +	pool = odp_pool_create("packet pool", &params);

> >>>>> +

> >>>>> +	if (pool == ODP_POOL_INVALID) {

> >>>>> +		printf("Error: packet pool create failed.\n");

> >>>>> +		exit(1);

> >>>>> +	}

> >>>>> +

> >>>>> +	/* Resolve fwd db*/

> >>>>> +	for (i = 0; i < args->if_count; i++) {

> >>>>> +		struct l3fwd_pktio_s *port;

> >>>>> +		char *if_name;

> >>>>> +

> >>>>> +		if_name = args->if_names[i];

> >>>>> +		port = &global.l3fwd_pktios[i];

> >>>>> +		if (create_pktio(if_name, pool, port)) {

> >>>>> +			printf("Error: create pktio %s\n", if_name);

> >>>>> +			exit(1);

> >>>>> +		}

> >>>>> +		odp_pktio_mac_addr(port->pktio, mac,

> > ODPH_ETHADDR_LEN);

> >>>>> +		resolve_fwd_db(if_name, i, mac);

> >>>>> +		memcpy(port->mac_addr.addr, mac,

> ODPH_ETHADDR_LEN);

> >>>>> +	}

> >>>>> +

> >>>>> +	setup_fwd_db();

> >>>>> +	dump_fwd_db();

> >>>>> +

> >>>>> +	/* Dicide available workers */

> >>>>> +	nb_worker = MAX_NB_WORKER;

> >>>>> +	if (args->worker_count)

> >>>>> +		nb_worker = args->worker_count;

> >>>>> +	nb_worker = odp_cpumask_default_worker(&cpumask,

> nb_worker);

> >>>>> +	args->worker_count = nb_worker;

> >>>>> +

> >>>>> +	/* Setup rx and tx queues for each port */

> >>>>> +	setup_worker_qconf(args);

> >>>>> +	print_qconf_table(args);

> >>>>> +

> >>>>> +	/* Decide ip lookup method */

> >>>>> +	if (args->hash_mode)

> >>>>> +		global.fwd_func = l3fwd_pkt_hash;

> >>>>> +	else

> >>>>> +		global.fwd_func = l3fwd_pkt_lpm;

> >>>>> +

> >>>>> +	/* Start all the available ports */

> >>>>> +	for (i = 0; i < args->if_count; i++) {

> >>>>> +		struct l3fwd_pktio_s *port;

> >>>>> +		char *if_name;

> >>>>> +		char buf[32];

> >>>>> +

> >>>>> +		if_name = args->if_names[i];

> >>>>> +		port = &global.l3fwd_pktios[i];

> >>>>> +		/* start pktio */

> >>>>> +		if (odp_pktio_start(port->pktio)) {

> >>>>> +			printf("unable to start pktio: %s\n",

> > if_name);

> >>>>> +			exit(1);

> >>>>> +		}

> >>>>> +

> >>>>> +		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",

> >>>>> +			port->mac_addr.addr[0],

> >>>>> +			port->mac_addr.addr[1],

> >>>>> +			port->mac_addr.addr[2],

> >>>>> +			port->mac_addr.addr[3],

> >>>>> +			port->mac_addr.addr[4],

> >>>>> +			port->mac_addr.addr[5]);

> >>>>> +		printf("start pktio: %s, mac %s\n", if_name, buf);

> >>>>> +	}

> >>>>> +

> >>>>> +	odp_barrier_init(&barrier, nb_worker + 1);

> >>>>> +

> >>>>> +	memset(&thr_params, 0, sizeof(thr_params));

> >>>>> +	thr_params.start    = run_worker;

> >>>>> +	thr_params.thr_type = ODP_THREAD_WORKER;

> >>>>> +	thr_params.instance = instance;

> >>>>> +

> >>>>> +	memset(thread_tbl, 0, sizeof(thread_tbl));

> >>>>> +	cpu = odp_cpumask_first(&cpumask);

> >>>>> +	for (i = 0; i < nb_worker; i++) {

> >>>>> +		struct thread_arg_s *arg;

> >>>>> +		odp_cpumask_t thr_mask;

> >>>>> +

> >>>>> +		arg = &global.worker_args[i];

> >>>>> +		arg->nb_pktio = args->if_count;

> >>>>> +		odp_cpumask_zero(&thr_mask);

> >>>>> +		odp_cpumask_set(&thr_mask, cpu);

> >>>>> +		thr_params.arg = arg;

> >>>>> +		odph_odpthreads_create(&thread_tbl[i], &thr_mask,

> >>>>> +				       &thr_params);

> >>>>> +		cpu = odp_cpumask_next(&cpumask, cpu);

> >>>>> +	}

> >>>>> +

> >>>>> +	if (args->duration) {

> >>>>> +		print_speed_stats(nb_worker, args->duration,

> >>>> PRINT_INTERVAL);

> >>>>> +		exit_threads = 1;

> >>>>> +	}

> >>>>> +

> >>>>> +	/* wait for other threads to join */

> >>>>> +	for (i = 0; i < nb_worker; i++)

> >>>>> +		odph_odpthreads_join(&thread_tbl[i]);

> >>>>> +

> >>>> main should end if odp_destroy_global(). And to add this to 'make

check'
> >>>> requires probably to add some logic when we think that test passed and

> >>>> when we thing that test failed.

> >>>> I think you copied print_speed_stats() from l2fwd,  there return code

> >>>> returns if app passed or not.

> >>>>

> >>> The testing PASS depends on the print_speed_stats() output in the log

> > file.

> >>> Here return value is not taken  as PASS or FAIL.

> >>>

> >>>>> +	return 0;

> >>>>> +}

> >>>>> +

> >>>>> +static void print_usage(char *progname)

> >>>>> +{

> >>>>> +	printf("\n"

> >>>>> +	       "ODP L3 forwarding application.\n"

> >>>>> +	       "\n"

> >>>>> +	       "Usage: %s OPTIONS\n"

> >>>>> +	       "  E.g. %s -i eth0,eth1 -r 1.1.1.0/24,eth0 -r

> >>> 2.2.2.0/24,eth1\n"

> >>>>> +	       " In the above example,\n"

> >>>>> +	       " eth0 will send pkts to eth1 and vice versa\n"

> >>>>> +	       "\n"

> >>>>> +	       "Mandatory OPTIONS:\n"

> >>>>> +	       "  -i, --interface eth interfaces (comma-separated, no

> >>> spaces)\n"

> >>>>> +	       "  -r, --route SubNet,Intf[,NextHopMAC]\n"

> >>>>> +	       "	NextHopMAC can be optional\n"

> >>>>> +	       "\n"

> >>>>> +	       "Optional OPTIONS:\n"

> >>>>> +	       "  -s, --style [lpm|hash], ip lookup method\n"

> >>>>> +	       "	optional, default as lpm\n"

> >>>>> +	       "  -d, --duration Seconds to run and print stats\n"

> >>>>> +	       "	optional, default as 0, run forever\n"

> >>>>> +	       "  -t, --thread Number of threads to do forwarding\n"

> >>>>> +	       "	optional, default as availbe worker cpu

> > count\n"

> >>>>> +	       "  -q, --queue  Configure rx queue(s) for port\n"

> >>>>> +	       "	optional, format: [(port, queue,

> > thread),...]\n"

> >>>>> +	       "	for example: -q '(0, 0, 1),(1,0,2)'\n"

> >>>>> +	       "  -h, --help   Display help and exit.\n\n"

> >>>>> +	       "\n", NO_PATH(progname), NO_PATH(progname)

> >>>>> +	    );

> >>>>> +}

> >>>>> +

> >>>>> +static void parse_cmdline_args(int argc, char *argv[], app_args_t

> >> *args)

> >>>>> +{

> >>>>> +	int opt;

> >>>>> +	int long_index;

> >>>>> +	char *token, *local;

> >>>>> +	size_t len, route_index = 0;

> >>>>> +	int i, mem_failure = 0;

> >>>>> +

> >>>>> +	static struct option longopts[] = {

> >>>>> +		{"interface", required_argument, NULL, 'i'},	/*

> > return 'i'

> >>> */

> >>>>> +		{"route", required_argument, NULL, 'r'},	/*

> > return 'r'

> >>> */

> >>>>> +		{"style", optional_argument, NULL, 's'},	/*

> > return 's'

> >>> */

> >>>>> +		{"duration", optional_argument, NULL, 'd'},	/*

> > return 'd'

> >>> */

> >>>>> +		{"thread", optional_argument, NULL, 't'},	/*

> > return 't'

> >>> */

> >>>>> +		{"queue", optional_argument, NULL, 'q'},	/*

> > return 'q'

> >>> */

> >>>>> +		{"help", no_argument, NULL, 'h'},		/*

> > return 'h'

> >>> */

> >>>>> +		{NULL, 0, NULL, 0}

> >>>>> +	};

> >>>>> +

> >>>>> +	while (1) {

> >>>>> +		opt = getopt_long(argc, argv, "+s:t:d:i:r:q:h",

> >>>>> +				  longopts, &long_index);

> >>>>> +

> >>>>> +		if (opt == -1)

> >>>>> +			break;	/* No more options */

> >>>>> +

> >>>>> +		switch (opt) {

> >>>>> +		/* parse ip lookup method */

> >>>>> +		case 's':

> >>>>> +			if (!strcmp(optarg, "hash"))

> >>>>> +				args->hash_mode = 1;

> >>>>> +			break;

> >>>>> +		/* parse number of worker threads to be run*/

> >>>>> +		case 't':

> >>>>> +			i = odp_cpu_count();

> >>>>> +			args->worker_count = atoi(optarg);

> >>>>> +			if (args->worker_count > i) {

> >>>>> +				printf("Too many threads,"

> >>>>> +				       "truncate to cpu count: %d\n",

> > i);

> >>>>> +				args->worker_count = i;

> >>>>> +			}

> >>>>> +

> >>>>> +			break;

> >>>>> +

> >>>>> +		/* parse seconds to run */

> >>>>> +		case 'd':

> >>>>> +			args->duration = atoi(optarg);

> >>>>> +			break;

> >>>>> +

> >>>>> +		/* parse packet-io interface names */

> >>>>> +		case 'i':

> >>>>> +			len = strlen(optarg);

> >>>>> +			if (len == 0) {

> >>>>> +				print_usage(argv[0]);

> >>>>> +				exit(EXIT_FAILURE);

> >>>>> +			}

> >>>>> +			len += 1;	/* add room for '\0' */

> >>>>> +

> >>>>> +			local = malloc(len);

> >>>>> +			if (!local) {

> >>>>> +				print_usage(argv[0]);

> >>>>> +				exit(EXIT_FAILURE);

> >>>>> +			}

> >>>>> +

> >>>>> +			/* count the number of tokens separated by ','

> > */

> >>>>> +			strcpy(local, optarg);

> >>>>> +			for (token = strtok(local, ","), i = 0;

> >>>>> +			     token != NULL;

> >>>>> +			     token = strtok(NULL, ","), i++)

> >>>>> +				;

> >>>>> +

> >>>>> +			if (i == 0) {

> >>>>> +				print_usage(argv[0]);

> >>>>> +				exit(EXIT_FAILURE);

> >>>>> +			} else if (i > MAX_NB_PKTIO) {

> >>>>> +				printf("too many ports specified, "

> >>>>> +				       "truncated to %d",

> > MAX_NB_PKTIO);

> >>>>> +			}

> >>>>> +			args->if_count = i;

> >>>>> +

> >>>>> +			/* store the if names (reset names string) */

> >>>>> +			strcpy(local, optarg);

> >>>>> +			for (token = strtok(local, ","), i = 0;

> >>>>> +			     token != NULL; token = strtok(NULL, ","),

> > i++) {

> >>>>> +				args->if_names[i] = token;

> >>>>> +			}

> >>>>> +			break;

> >>>>> +

> >>>>> +		/*Configure Route in forwarding database*/

> >>>>> +		case 'r':

> >>>>> +			if (route_index >= MAX_NB_ROUTE) {

> >>>>> +				printf("No more routes can be

> > added\n");

> >>>>> +				break;

> >>>>> +			}

> >>>>> +			local = calloc(1, strlen(optarg) + 1);

> >>>>> +			if (!local) {

> >>>>> +				mem_failure = 1;

> >>>>> +				break;

> >>>>> +			}

> >>>>> +			memcpy(local, optarg, strlen(optarg));

> >>>>> +			local[strlen(optarg)] = '\0';

> >>>>> +			args->route_str[route_index++] = local;

> >>>>> +			break;

> >>>>> +

> >>>>> +		case 'h':

> >>>>> +			print_usage(argv[0]);

> >>>>> +			exit(EXIT_SUCCESS);

> >>>>> +			break;

> >>>>> +

> >>>>> +		case 'q':

> >>>>> +			parse_config(optarg, args);

> >>>>> +			break;

> >>>>> +

> >>>>> +		default:

> >>>>> +			break;

> >>>>> +		}

> >>>>> +	}

> >>>>> +

> >>>>> +	/* checking arguments */

> >>>>> +	if (args->if_count == 0) {

> >>>>> +		printf("\nNo option -i specified.\n");

> >>>>> +		goto out;

> >>>>> +	}

> >>>>> +

> >>>>> +	if (args->route_str[0] == NULL) {

> >>>>> +		printf("\nNo option -r specified.\n");

> >>>>> +		goto out;

> >>>>> +	}

> >>>>> +

> >>>>> +	if (mem_failure == 1) {

> >>>>> +		printf("\nAllocate memory failure.\n");

> >>>>> +		goto out;

> >>>>> +	}

> >>>>> +	optind = 1;		/* reset 'extern optind' from the

> > getopt lib

> >>> */

> >>>>> +	return;

> >>>>> +

> >>>>> +out:

> >>>>> +	print_usage(argv[0]);

> >>>>> +	exit(EXIT_FAILURE);

> >>>>> +}

> >>>>> +

> >>>>> +/* split string into tokens */

> >>>>> +int split_string(char *str, int stringlen,

> >>>>> +		 char **tokens, int maxtokens, char delim)

> >>>>> +{

> >>>>> +	int i, tok = 0;

> >>>>> +	int tokstart = 1; /* first token is right at start of string

> > */

> >>>>> +

> >>>>> +	if (str == NULL || tokens == NULL)

> >>>>> +		goto einval_error;

> >>>>> +

> >>>>> +	for (i = 0; i < stringlen; i++) {

> >>>>> +		if (str[i] == '\0' || tok >= maxtokens)

> >>>>> +			break;

> >>>>> +		if (tokstart) {

> >>>>> +			tokstart = 0;

> >>>>> +			tokens[tok++] = &str[i];

> >>>>> +		}

> >>>>> +		if (str[i] == delim) {

> >>>>> +			str[i] = '\0';

> >>>>> +			tokstart = 1;

> >>>>> +		}

> >>>>> +	}

> >>>>> +	return tok;

> >>>>> +

> >>>>> +einval_error:

> >>>>> +	errno = EINVAL;

> >>>>> +	return -1;

> >>>>> +}

> >>>>> +

> >>>>> +static int parse_config(char *cfg_str, app_args_t *args)

> >>>>> +{

> >>>>> +	char s[256];

> >>>>> +	const char *p, *p0 = cfg_str;

> >>>>> +	char *end;

> >>>>> +	enum fieldnames {

> >>>>> +		FLD_PORT = 0,

> >>>>> +		FLD_QUEUE,

> >>>>> +		FLD_LCORE,

> >>>>> +		FLD_LAST

> >>>>> +	};

> >>>>> +	unsigned long int_fld[FLD_LAST];

> >>>>> +	char *str_fld[FLD_LAST];

> >>>>> +	int i;

> >>>>> +	unsigned size;

> >>>>> +	int nb_qconfs = 0;

> >>>>> +	struct l3fwd_qconf_s *qconf_array = &args->qconf_config[0];

> >>>>> +

> >>>>> +	p = strchr(p0, '(');

> >>>>> +	while (p != NULL) {

> >>>>> +		++p;

> >>>>> +		p0 = strchr(p, ')');

> >>>>> +		if (p0 == NULL)

> >>>>> +			return -1;

> >>>>> +

> >>>>> +		size = p0 - p;

> >>>>> +		if (size >= sizeof(s))

> >>>>> +			return -1;

> >>>>> +

> >>>>> +		snprintf(s, sizeof(s), "%.*s", size, p);

> >>>>> +		i = split_string(s, sizeof(s), str_fld, FLD_LAST,

> > ',');

> >>>>> +		if (i != FLD_LAST)

> >>>>> +			return -1;

> >>>>> +		for (i = 0; i < FLD_LAST; i++) {

> >>>>> +			errno = 0;

> >>>>> +			int_fld[i] = strtoul(str_fld[i], &end, 0);

> >>>>> +			if (errno != 0 || end == str_fld[i] ||

> > int_fld[i] >

> >>> 255)

> >>>>> +				return -1;

> >>>>> +		}

> >>>>> +		if (nb_qconfs >= MAX_NB_QCONFS) {

> >>>>> +			printf("exceeded max number of queue

> >>>> params: %hu\n",

> >>>>> +			       nb_qconfs);

> >>>>> +			return -1;

> >>>>> +		}

> >>>>> +		qconf_array[nb_qconfs].if_idx =

> > (uint8_t)int_fld[FLD_PORT];

> >>>>> +		qconf_array[nb_qconfs].rxq_idx =

> >>>> (uint8_t)int_fld[FLD_QUEUE];

> >>>>> +		qconf_array[nb_qconfs].core_idx =

> >>>> (uint8_t)int_fld[FLD_LCORE];

> >>>>> +		++nb_qconfs;

> >>>>> +

> >>>>> +		p = strchr(p0, '(');

> >>>>> +	}

> >>>>> +	args->qconf_count = nb_qconfs;

> >>>>> +

> >>>>> +	return 0;

> >>>>> +}

> >>>>> +

> >>>>> +static void print_info(char *progname, app_args_t *args)

> >>>>> +{

> >>>>> +	int i;

> >>>>> +

> >>>>> +	printf("\n"

> >>>>> +	       "ODP system info\n"

> >>>>> +	       "---------------\n"

> >>>>> +	       "ODP API version: %s\n"

> >>>>> +	       "ODP impl name:	 %s\n"

> >>>>> +	       "CPU model:       %s\n"

> >>>>> +	       "CPU freq (hz):   %" PRIu64 "\n"

> >>>>> +	       "Cache line size: %i\n"

> >>>>> +	       "CPU count:       %i\n"

> >>>>> +	       "\n",

> >>>>> +	       odp_version_api_str(), odp_version_impl_name(),

> >>>>> +	       odp_cpu_model_str(), odp_cpu_hz_max(),

> >>>>> +	       odp_sys_cache_line_size(), odp_cpu_count());

> >>>>> +

> >>>>> +	printf("Running ODP appl: \"%s\"\n"

> >>>>> +	       "-----------------\n"

> >>>>> +	       "IP Lookup:	 %s\n"

> >>>>> +	       "IF Count:        %i\n"

> >>>>> +	       "Using IFs:      ",

> >>>>> +	       progname,

> >>>>> +	       args->hash_mode ? "hash" : "lpm",

> >>>>> +	       args->if_count);

> >>>>> +

> >>>>> +	for (i = 0; i < args->if_count; ++i)

> >>>>> +		printf(" %s", args->if_names[i]);

> >>>>> +

> >>>>> +	printf("\n\n");

> >>>>> +	fflush(NULL);

> >>>>> +}

> >>>>> +

> >>>>> +/**

> >>>>> + * Setup rx and tx queues, distribute them among threads.

> >>>>> + * Try to have one tx queue for each rx queue, if not vailable,

> >>>>> + * shared tx queue is used.

> >>>>> + *

> >>>>> + * If no q argument, the queues are distribute among threads as

> >> default.

> >>>>> + * The thread take one rx queue of a port one time as round-robin

> >> order.

> >>>>> + */

> >>>>> +static void setup_worker_qconf(app_args_t *args)

> >>>>> +{

> >>>>> +	int nb_worker, if_count;

> >>>>> +	int i, j, rxq_idx, txq_idx;

> >>>>> +	struct thread_arg_s *arg;

> >>>>> +	struct l3fwd_pktio_s *port;

> >>>>> +	uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_QUEUE];

> >>>>> +

> >>>>> +	nb_worker = args->worker_count;

> >>>>> +	if_count = args->if_count;

> >>>>> +

> >>>>> +	/* distribute queues among threads */

> >>>>> +	if (!args->qconf_count) {

> >>>>> +		if (nb_worker > if_count) {

> >>>>> +			for (i = 0; i < nb_worker; i++) {

> >>>>> +				arg = &global.worker_args[i];

> >>>>> +				arg->thr_idx = i;

> >>>>> +				j = i % if_count;

> >>>>> +				port = &global.l3fwd_pktios[j];

> >>>>> +				if (port->rxq_idx < port->nb_rxq) {

> >>>>> +					rxq_idx = port->rxq_idx;

> >>>>> +					arg->pktio[j].rxq[rxq_idx] =

> > rxq_idx;

> >>>>> +					port->rxq_idx++;

> >>>>> +					txq_idx = port->txq_idx;

> >>>>> +					arg->pktio[j].txq[txq_idx] =

> > txq_idx;

> >>>>> +					port->txq_idx++;

> >>>>> +					arg->pktio[j].used = 1;

> >>>>> +				}

> >>>>> +			}

> >>>>> +		} else {

> >>>>> +			for (i = 0; i < if_count; i++) {

> >>>>> +				j = i % nb_worker;

> >>>>> +				arg = &global.worker_args[j];

> >>>>> +				arg->thr_idx = j;

> >>>>> +				port = &global.l3fwd_pktios[i];

> >>>>> +				if (port->rxq_idx < port->nb_rxq) {

> >>>>> +					rxq_idx = port->rxq_idx;

> >>>>> +					arg->pktio[i].rxq[rxq_idx] =

> > rxq_idx;

> >>>>> +					port->rxq_idx++;

> >>>>> +					txq_idx = port->txq_idx;

> >>>>> +					arg->pktio[i].txq[txq_idx] =

> > txq_idx;

> >>>>> +					port->txq_idx++;

> >>>>> +					arg->pktio[i].used = 1;

> >>>>> +				}

> >>>>> +			}

> >>>>> +		}

> >>>>> +	}

> >>>>> +

> >>>>> +	/* specified q argument, distribute queues among threads as it

> > says */

> >>>>> +	memset(queue_mask, 0, sizeof(queue_mask));

> >>>>> +	for (i = 0; i < args->qconf_count; i++) {

> >>>>> +		struct l3fwd_qconf_s *q;

> >>>>> +

> >>>>> +		q = &args->qconf_config[i];

> >>>>> +		if (q->core_idx >= nb_worker || q->if_idx >= if_count)

> >>>>> +			LOG_ABORT("Error queue (%d, %d, %d), max

> port:

> > "

> >>>>> +				  "%d, max core: %d\n", q->if_idx,

> > q->rxq_idx,

> >>>>> +				  q->core_idx, args->if_count - 1,

> >>>>> +				  args->worker_count - 1);

> >>>>> +

> >>>>> +		/* check if one queue is configured twice or more */

> >>>>> +		if (queue_mask[q->if_idx][q->rxq_idx])

> >>>>> +			LOG_ABORT("Error queue (%d, %d, %d),

> reconfig

> >>>> queue\n",

> >>>>> +				  q->if_idx, q->rxq_idx, q->core_idx);

> >>>>> +		queue_mask[q->if_idx][q->rxq_idx] = 1;

> >>>>> +

> >>>>> +		port = &global.l3fwd_pktios[q->if_idx];

> >>>>> +		if (port->rxq_idx < q->rxq_idx)

> >>>>> +			LOG_ABORT("Error queue (%d, %d, %d), queue

> > should

> >>>> be"

> >>>>> +				  " in sequence and start from 0,

> > queue %d\n",

> >>>>> +				  q->if_idx, q->rxq_idx, q->core_idx,

> >>>>> +				  q->rxq_idx);

> >>>>> +

> >>>>> +		if (q->rxq_idx > port->nb_rxq) {

> >>>>> +			LOG_ABORT("Error queue (%d, %d, %d), max

> >>>> queue %d\n",

> >>>>> +				  q->if_idx, q->rxq_idx, q->core_idx,

> >>>>> +				  port->nb_rxq - 1);

> >>>>> +		}

> >>>>> +		port->rxq_idx = q->rxq_idx + 1;

> >>>>> +		port->txq_idx = q->rxq_idx + 1;

> >>>>> +

> >>>>> +		/* put the queue into worker_args */

> >>>>> +		arg = &global.worker_args[q->core_idx];

> >>>>> +		arg->pktio[q->if_idx].rxq[q->rxq_idx] = q->rxq_idx;

> >>>>> +		arg->pktio[q->if_idx].txq[q->rxq_idx] = q->rxq_idx;

> >>>>> +		arg->pktio[q->if_idx].used = 1;

> >>>>> +		arg->thr_idx = q->core_idx;

> >>>>> +	}

> >>>>> +

> >>>>> +	/* config and initialize rx and tx queues. */

> >>>>> +	for (i = 0; i < if_count; i++) {

> >>>>> +		odp_pktin_queue_param_t in_queue_param;

> >>>>> +		odp_pktout_queue_param_t out_queue_param;

> >>>>> +		struct odp_pktin_queue_t *inq;

> >>>>> +		struct odp_pktout_queue_t *outq;

> >>>>> +		const char *name;

> >>>>> +		int rc, nb_rxq, nb_txq;

> >>>>> +

> >>>>> +		port = &global.l3fwd_pktios[i];

> >>>>> +		name = args->if_names[i];

> >>>>> +		odp_pktin_queue_param_init(&in_queue_param);

> >>>>> +		odp_pktout_queue_param_init(&out_queue_param);

> >>>>> +

> >>>>> +		in_queue_param.op_mode = ODP_PKTIO_OP_MT;

> >>>>> +		out_queue_param.op_mode = ODP_PKTIO_OP_MT;

> >>>>> +

> >>>>> +		in_queue_param.num_queues = port->rxq_idx;

> >>>>> +		if (port->rxq_idx > port->nb_rxq) {

> >>>>> +			in_queue_param.num_queues = port->nb_rxq;

> >>>>> +			in_queue_param.op_mode =

> >>>> ODP_PKTIO_OP_MT_UNSAFE;

> >>>>> +		}

> >>>>> +

> >>>>> +		/* enable flow hashing for multi-input queues */

> >>>>> +		if (in_queue_param.num_queues > 1) {

> >>>>> +			in_queue_param.hash_enable = 1;

> >>>>> +			in_queue_param.hash_proto.proto.ipv4_tcp =

> 1;

> >>>>> +			in_queue_param.hash_proto.proto.ipv4_udp =

> 1;

> >>>>> +		}

> >>>>> +

> >>>>> +		if (odp_pktin_queue_config(port->pktio,

> > &in_queue_param))

> >>>>> +			LOG_ABORT("Fail to config input queue for

> > %s\n",

> >>>> name);

> >>>>> +

> >>>>> +		out_queue_param.num_queues = port->txq_idx;

> >>>>> +		if (port->txq_idx > port->nb_txq) {

> >>>>> +			out_queue_param.num_queues = port-

> >nb_txq;

> >>>>> +			out_queue_param.op_mode =

> >>>> ODP_PKTIO_OP_MT_UNSAFE;

> >>>>> +		}

> >>>>> +		if (odp_pktout_queue_config(port->pktio,

> >>>> &out_queue_param))

> >>>>> +			LOG_ABORT("Fail to config output queue for

> > %s\n",

> >>>> name);

> >>>>> +

> >>>>> +		inq = port->ifin;

> >>>>> +		nb_rxq = in_queue_param.num_queues;

> >>>>> +		if (odp_pktin_queue(port->pktio, inq, nb_rxq) !=

> > nb_rxq)

> >>>>> +			LOG_ABORT("Fail to set pktin queue for %s\n",

> > name);

> >>>>> +

> >>>>> +		if (port->rxq_idx > port->nb_rxq) {

> >>>>> +			for (rc = port->nb_rxq; rc < port->rxq_idx;

> > rc++)

> >>>>> +				inq[rc] = inq[rc % port->nb_rxq];

> >>>>> +		}

> >>>>> +

> >>>>> +		outq = port->ifout;

> >>>>> +		nb_txq = out_queue_param.num_queues;

> >>>>> +		if (odp_pktout_queue(port->pktio, outq, nb_txq) !=

> > nb_txq)

> >>>>> +			LOG_ABORT("Fail to set pktout queue

> for %s\n",

> >>>> name);

> >>>>> +

> >>>>> +		if (port->txq_idx > port->nb_txq) {

> >>>>> +			for (rc = port->nb_txq; rc < port->txq_idx;

> > rc++)

> >>>>> +				outq[rc] = outq[rc % port->nb_txq];

> >>>>> +		}

> >>>>> +	}

> >>>>> +}

> >>>>> +

> >>>>> +static void print_qconf_table(app_args_t *args)

> >>>>> +{

> >>>>> +	int i, j, k, qid;

> >>>>> +	char buf[32];

> >>>>> +	struct thread_arg_s *thr_arg;

> >>>>> +

> >>>>> +	printf("Rx queue table\n"

> >>>>> +	       "-----------------\n"

> >>>>> +	       "%-16s%-16s%-16s\n",

> >>>>> +	       "port/id", "queue", "thread");

> >>>>> +

> >>>>> +	for (i = 0; i < args->worker_count; i++) {

> >>>>> +		thr_arg = &global.worker_args[i];

> >>>>> +		for (j = 0; j < args->if_count; j++) {

> >>>>> +			if (!thr_arg->pktio[j].used)

> >>>>> +				continue;

> >>>>> +

> >>>>> +			sprintf(buf, "%s/%d", args->if_names[j], j);

> >>>>> +			for (k = 0; k < MAX_NB_QUEUE; k++) {

> >>>>> +				qid = thr_arg->pktio[j].rxq[k];

> >>>>> +				if (qid != INVALID_ID)

> >>>>> +					printf("%-16s%-16d%-16d\n",

> > buf, qid,

> >>>>> +					       thr_arg->thr_idx);

> >>>>> +			}

> >>>>> +		}

> >>>>> +	}

> >>>>> +	printf("\n");

> >>>>> +	fflush(NULL);

> >>>>> +}

> >>>>> +

> >>>>> +/**

> >>>>> + *  Print statistics

> >>>>> + *

> >>>>> + * @param num_workers Number of worker threads

> >>>>> + * @param duration Number of seconds to loop in

> >>>>> + * @param timeout Number of seconds for stats calculation

> >>>>> + *

> >>>>> + */

> >>>>> +static int print_speed_stats(int num_workers, int duration, int

> >> timeout)

> >>>>> +{

> >>>>> +	uint64_t pkts = 0;

> >>>>> +	uint64_t pkts_prev = 0;

> >>>>> +	uint64_t pps;

> >>>>> +	uint64_t rx_drops, tx_drops;

> >>>>> +	uint64_t maximum_pps = 0;

> >>>>> +	int i;

> >>>>> +	int elapsed = 0;

> >>>>> +	int stats_enabled = 1;

> >>>>> +	int loop_forever = (duration == 0);

> >>>>> +

> >>>>> +	if (timeout <= 0) {

> >>>>> +		stats_enabled = 0;

> >>>>> +		timeout = 1;

> >>>>> +	}

> >>>>> +	/* Wait for all threads to be ready*/

> >>>>> +	odp_barrier_wait(&barrier);

> >>>>> +

> >>>>> +	do {

> >>>>> +		pkts = 0;

> >>>>> +		rx_drops = 0;

> >>>>> +		tx_drops = 0;

> >>>>> +		sleep(timeout);

> >>>>> +

> >>>>> +		for (i = 0; i < num_workers; i++) {

> >>>>> +			pkts += global.worker_args[i].packets;

> >>>>> +			rx_drops += global.worker_args[i].rx_drops;

> >>>>> +			tx_drops += global.worker_args[i].tx_drops;

> >>>>> +		}

> >>>>> +		if (stats_enabled) {

> >>>>> +			pps = (pkts - pkts_prev) / timeout;

> >>>>> +			if (pps > maximum_pps)

> >>>>> +				maximum_pps = pps;

> >>>>> +			printf("%" PRIu64 " pps, %" PRIu64 " max pps,

> > ",  pps,

> >>>>> +			       maximum_pps);

> >>>>> +

> >>>>> +			printf(" %" PRIu64 " rx drops, %" PRIu64 " tx

> >>> drops\n",

> >>>>> +			       rx_drops, tx_drops);

> >>>>> +

> >>>>> +			pkts_prev = pkts;

> >>>>> +		}

> >>>>> +		elapsed += timeout;

> >>>>> +	} while (loop_forever || (elapsed < duration));

> >>>>> +

> >>>>> +	if (stats_enabled)

> >>>>> +		printf("TEST RESULT: %" PRIu64 " maximum packets per

> >>>> second.\n",

> >>>>> +		       maximum_pps);

> >>>>> +

> >>>>> +	return pkts > 100 ? 0 : -1;

> >>>>> +}

> >>>>> diff --git a/example/l3fwd/odp_l3fwd_db.c

> >>>> b/example/l3fwd/odp_l3fwd_db.c

> >>>>> new file mode 100644

> >>>>> index 0000000..93e32f0

> >>>>> --- /dev/null

> >>>>> +++ b/example/l3fwd/odp_l3fwd_db.c

> >>>>> @@ -0,0 +1,408 @@

> >>>>> +/* Copyright (c) 2016, Linaro Limited

> >>>>> + * All rights reserved.

> >>>>> + *

> >>>>> + * SPDX-License-Identifier:     BSD-3-Clause

> >>>>> + */

> >>>>> +

> >>>>> +#ifndef _GNU_SOURCE

> >>>>> +#define _GNU_SOURCE

> >>>>> +#endif

> >>>>> +

> >>>>> +#include <stdlib.h>

> >>>>> +#include <string.h>

> >>>>> +

> >>>>> +#include <example_debug.h>

> >>>>> +#include <odp_api.h>

> >>>>> +#include <odp_l3fwd_db.h>

> >>>>> +

> >>>>> +/** Jenkins hash support.

> >>>>> +  *

> >>>>> +  * Copyright (C) 2006 Bob Jenkins (bob_jenkins@burtleburtle.net)

> >>>>> +  *

> >>>>> +  * http://burtleburtle.net/bob/hash/

> >>>>> +  *

> >>>>> +  * These are the credits from Bob's sources:

> >>>>> +  *

> >>>>> +  * lookup3.c, by Bob Jenkins, May 2006, Public Domain.

> >>>>> +  *

> >>>>> +  * These are functions for producing 32-bit hashes for hash table

> >>> lookup.

> >>>>> +  * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and

> >>> final()

> >>>>> +  * are externally useful functions.  Routines to test the hash are

> >>> included

> >>>>> +  * if SELF_TEST is defined.  You can use this free for any purpose.

> >>> It's in

> >>>>> +  * the public domain.  It has no warranty.

> >>>>> +  *

> >>>>> +  * $FreeBSD$

> >>>>> +  */

> >>>>> +#define JHASH_GOLDEN_RATIO	0x9e3779b9

> >>>>> +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))

> >>>>> +#define FWD_BJ3_MIX(a, b, c) \

> >>>>> +{ \

> >>>>> +	a -= c; a ^= rot(c, 4); c += b; \

> >>>>> +	b -= a; b ^= rot(a, 6); a += c; \

> >>>>> +	c -= b; c ^= rot(b, 8); b += a; \

> >>>>> +	a -= c; a ^= rot(c, 16); c += b; \

> >>>>> +	b -= a; b ^= rot(a, 19); a += c; \

> >>>>> +	c -= b; c ^= rot(b, 4); b += a; \

> >>>>> +}

> >>>>> +

> >>>>> +/**

> >>>>> + * Compute hash value from a flow

> >>>>> + */

> >>>>> +static inline

> >>>>> +uint64_t l3fwd_calc_hash(ipv4_tuple5_t *key)

> >>>>> +{

> >>>>> +	uint64_t l4_ports = 0;

> >>>>> +	uint32_t dst_ip, src_ip;

> >>>>> +

> >>>>> +	src_ip = key->src_ip;

> >>>>> +	dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO;

> >>>>> +	FWD_BJ3_MIX(src_ip, dst_ip, l4_ports);

> >>>>> +

> >>>>> +	return l4_ports;

> >>>>> +}

> >>>>> +

> >>>>> +/**

> >>>>> + * Parse text string representing an IPv4 address or subnet

> >>>>> + *

> >>>>> + * String is of the format "XXX.XXX.XXX.XXX(/W)" where

> >>>>> + * "XXX" is decimal value and "/W" is optional subnet length

> >>>>> + *

> >>>>> + * @param ipaddress  Pointer to IP address/subnet string to convert

> >>>>> + * @param addr       Pointer to return IPv4 address, host endianness

> >>>>> + * @param depth      Pointer to subnet bit width

> >>>>> + * @return 0 if successful else -1

> >>>>> + */

> >>>>> +static inline

> >>>>> +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t

> > *depth)

> >>>>> +{

> >>>>> +	int b[4];

> >>>>> +	int qualifier = 32;

> >>>>> +	int converted;

> >>>>> +	uint32_t addr_le;

> >>>>> +

> >>>>> +	if (strchr(ipaddress, '/')) {

> >>>>> +		converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",

> >>>>> +				   &b[3], &b[2], &b[1], &b[0],

> >>>>> +				   &qualifier);

> >>>>> +		if (5 != converted)

> >>>>> +			return -1;

> >>>>> +	} else {

> >>>>> +		converted = sscanf(ipaddress, "%d.%d.%d.%d",

> >>>>> +				   &b[3], &b[2], &b[1], &b[0]);

> >>>>> +		if (4 != converted)

> >>>>> +			return -1;

> >>>>> +	}

> >>>>> +

> >>>>> +	if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] >

> > 255))

> >>>>> +		return -1;

> >>>>> +	if (!qualifier || (qualifier > 32))

> >>>>> +		return -1;

> >>>>> +

> >>>>> +	addr_le = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;

> >>>>> +	*addr = odp_le_to_cpu_32(addr_le);

> >>>>> +	*depth = qualifier;

> >>>>> +

> >>>>> +	return 0;

> >>>>> +}

> >>>>> +

> >>>>> +/**

> >>>>> + * Generate text string representing IPv4 range/subnet, output

> >>>>> + * in "XXX.XXX.XXX.XXX/W" format

> >>>>> + *

> >>>>> + * @param b     Pointer to buffer to store string

> >>>>> + * @param range Pointer to IPv4 address range

> >>>>> + *

> >>>>> + * @return Pointer to supplied buffer

> >>>>> + */

> >>>>> +static inline

> >>>>> +char *ipv4_subnet_str(char *b, ip_addr_range_t *range)

> >>>>> +{

> >>>>> +	sprintf(b, "%d.%d.%d.%d/%d",

> >>>>> +		0xFF & ((range->addr) >> 24),

> >>>>> +		0xFF & ((range->addr) >> 16),

> >>>>> +		0xFF & ((range->addr) >>  8),

> >>>>> +		0xFF & ((range->addr) >>  0),

> >>>>> +		range->depth);

> >>>>> +	return b;

> >>>>> +}

> >>>>> +

> >>>>> +/**

> >>>>> + * Generate text string representing MAC address

> >>>>> + *

> >>>>> + * @param b     Pointer to buffer to store string

> >>>>> + * @param mac   Pointer to MAC address

> >>>>> + *

> >>>>> + * @return Pointer to supplied buffer

> >>>>> + */

> >>>>> +static inline

> >>>>> +char *mac_addr_str(char *b, odph_ethaddr_t *mac)

> >>>>> +{

> >>>>> +	uint8_t *byte;

> >>>>> +

> >>>>> +	byte = mac->addr;

> >>>>> +	sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",

> >>>>> +		byte[0], byte[1], byte[2], byte[3], byte[4], byte[5]);

> >>>>> +	return b;

> >>>>> +}

> >>>>> +

> >>>>> +/**

> >>>>> + * Flow cache table entry

> >>>>> + */

> >>>>> +typedef struct flow_entry_s {

> >>>>> +	ipv4_tuple5_t key;		/**< match key */

> >>>>> +	struct flow_entry_s *next;      /**< next entry on the list */

> >>>>> +	fwd_db_entry_t *fwd_entry;	/**< entry info in db */

> >>>>> +} flow_entry_t;

> >>>>> +

> >>>>> +/**

> >>>>> + * Flow cache table bucket

> >>>>> + */

> >>>>> +typedef struct flow_bucket_s {

> >>>>> +	odp_spinlock_t		lock;	/**< Bucket lock*/

> >>>>> +	flow_entry_t		*next;	/**< Pointer to first flow

> > entry in

> >>>> bucket*/

> >>>>> +} flow_bucket_t;

> >>>>> +

> >>>>> +/**

> >>>>> + * Flow hash table, fast lookup cache

> >>>>> + */

> >>>>> +typedef struct flow_table_s {

> >>>>> +	flow_bucket_t *bucket;

> >>>>> +	uint32_t count;

> >>>>> +} flow_table_t;

> >>>>> +

> >>>>> +static flow_table_t fwd_lookup_cache;

> >>>>> +

> >>>>> +void init_fwd_hash_cache(void)

> >>>>> +{

> >>>>> +	odp_shm_t		hash_shm;

> >>>>> +	flow_bucket_t		*bucket;

> >>>>> +	uint32_t		bucket_count;

> >>>>> +	uint32_t		i;

> >>>>> +

> >>>>> +	bucket_count = FWD_DEF_BUCKET_COUNT;

> >>>>> +

> >>>>> +	/*Reserve memory for Routing hash table*/

> >>>>> +	hash_shm = odp_shm_reserve("route_table",

> >>>>> +				   sizeof(flow_bucket_t) *

> > bucket_count,

> >>>>> +				   ODP_CACHE_LINE_SIZE, 0);

> >>>>> +

> >>>>> +	bucket = odp_shm_addr(hash_shm);

> >>>>> +	if (!bucket) {

> >>>>> +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> >>>>> +		exit(-1);

> >>>>> +	}

> >>>>> +

> >>>>> +	fwd_lookup_cache.bucket = bucket;

> >>>>> +	fwd_lookup_cache.count = bucket_count;

> >>>>> +

> >>>>> +	/*Initialize Locks*/

> >>>>> +	for (i = 0; i < bucket_count; i++) {

> >>>>> +		bucket = &fwd_lookup_cache.bucket[i];

> >>>>> +		odp_spinlock_init(&bucket->lock);

> >>>>> +		bucket->next = NULL;

> >>>>> +	}

> >>>>> +}

> >>>>> +

> >>>>> +static inline

> >>>>> +int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow)

> >>>>> +{

> >>>>> +	if (key->src_ip == flow->key.src_ip &&

> >>>>> +	    key->dst_ip == flow->key.dst_ip &&

> >>>>> +	    key->src_port == flow->key.src_port &&

> >>>>> +	    key->dst_port == flow->key.dst_port &&

> >>>>> +	    key->proto == flow->key.proto)

> >>>>> +		return 1;

> >>>>> +

> >>>>> +	return 0;

> >>>>> +}

> >>>>> +

> >>>>> +static inline

> >>>>> +flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t

> >>>> *bucket)

> >>>>> +{

> >>>>> +	flow_entry_t *rst;

> >>>>> +

> >>>>> +	for (rst = bucket->next; rst != NULL; rst = rst->next) {

> >>>>> +		if (match_key_flow(key, rst))

> >>>>> +			break;

> >>>>> +	}

> >>>>> +

> >>>>> +	return rst;

> >>>>> +}

> >>>>> +

> >>>>> +static inline

> >>>>> +flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key,

> >>>>> +			       flow_bucket_t *bucket,

> >>>>> +			       fwd_db_entry_t *entry)

> >>>>> +{

> >>>>> +	flow_entry_t *flow;

> >>>>> +

> >>>>> +	flow = lookup_fwd_cache(key, bucket);

> >>>>> +	if (flow)

> >>>>> +		return flow;

> >>>>> +

> >>>>> +	flow = malloc(sizeof(flow_entry_t));

> >>>>> +	flow->key = *key;

> >>>>> +	flow->fwd_entry = entry;

> >>>>> +

> >>>>> +	odp_spinlock_lock(&bucket->lock);

> >>>>> +	if (!bucket->next) {

> >>>>> +		bucket->next = flow;

> >>>>> +	} else {

> >>>>> +		flow->next = bucket->next;

> >>>>> +		bucket->next = flow;

> >>>>> +	}

> >>>>> +	odp_spinlock_unlock(&bucket->lock);

> >>>>> +

> >>>>> +	return flow;

> >>>>> +}

> >>>>> +

> >>>>> +/** Global pointer to fwd db */

> >>>>> +fwd_db_t *fwd_db;

> >>>>> +

> >>>>> +void init_fwd_db(void)

> >>>>> +{

> >>>>> +	odp_shm_t shm;

> >>>>> +

> >>>>> +	shm = odp_shm_reserve("shm_fwd_db",

> >>>>> +			      sizeof(fwd_db_t),

> >>>>> +			      ODP_CACHE_LINE_SIZE,

> >>>>> +			      0);

> >>>>> +

> >>>>> +	fwd_db = odp_shm_addr(shm);

> >>>>> +

> >>>>> +	if (fwd_db == NULL) {

> >>>>> +		EXAMPLE_ERR("Error: shared mem alloc failed.\n");

> >>>>> +		exit(EXIT_FAILURE);

> >>>>> +	}

> >>>>> +	memset(fwd_db, 0, sizeof(*fwd_db));

> >>>>> +}

> >>>>> +

> >>>>> +int create_fwd_db_entry(char *input, char **oif)

> >>>>> +{

> >>>>> +	int pos = 0;

> >>>>> +	char *local;

> >>>>> +	char *str;

> >>>>> +	char *save;

> >>>>> +	char *token;

> >>>>> +	fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];

> >>>>> +

> >>>>> +	/* Verify we haven't run out of space */

> >>>>> +	if (MAX_DB <= fwd_db->index)

> >>>>> +		return -1;

> >>>>> +

> >>>>> +	/* Make a local copy */

> >>>>> +	local = malloc(strlen(input) + 1);

> >>>>> +	if (NULL == local)

> >>>>> +		return -1;

> >>>>> +	strcpy(local, input);

> >>>>> +

> >>>>> +	/* Setup for using "strtok_r" to search input string */

> >>>>> +	str = local;

> >>>>> +	save = NULL;

> >>>>> +

> >>>>> +	/* Parse tokens separated by ':' */

> >>>>> +	while (NULL != (token = strtok_r(str, ",", &save))) {

> >>>>> +		str = NULL;  /* reset str for subsequent strtok_r

> > calls */

> >>>>> +

> >>>>> +		/* Parse token based on its position */

> >>>>> +		switch (pos) {

> >>>>> +		case 0:

> >>>>> +			parse_ipv4_string(token,

> >>>>> +					  &entry->subnet.addr,

> >>>>> +					  &entry->subnet.depth);

> >>>>> +			break;

> >>>>> +		case 1:

> >>>>> +			strncpy(entry->oif, token, OIF_LEN - 1);

> >>>>> +			entry->oif[OIF_LEN - 1] = 0;

> >>>>> +			break;

> >>>>> +		case 2:

> >>>>> +			odph_eth_addr_parse(&entry->dst_mac,

> token);

> >>>>> +			*oif = entry->oif;

> >>>>> +			break;

> >>>>> +

> >>>>> +		default:

> >>>>> +			printf("ERROR: extra token \"%s\" at position

> > %d\n",

> >>>>> +			       token, pos);

> >>>>> +			break;

> >>>>> +		}

> >>>>> +

> >>>>> +		/* Advance to next position */

> >>>>> +		pos++;

> >>>>> +	}

> >>>>> +

> >>>>> +	/* Add route to the list */

> >>>>> +	fwd_db->index++;

> >>>>> +	entry->next = fwd_db->list;

> >>>>> +	fwd_db->list = entry;

> >>>>> +

> >>>>> +	free(local);

> >>>>> +	return 0;

> >>>>> +}

> >>>>> +

> >>>>> +void resolve_fwd_db(char *intf, int portid, uint8_t *mac)

> >>>>> +{

> >>>>> +	fwd_db_entry_t *entry;

> >>>>> +

> >>>>> +	/* Walk the list and attempt to set output and MAC */

> >>>>> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> > {

> >>>>> +		if (strcmp(intf, entry->oif))

> >>>>> +			continue;

> >>>>> +

> >>>>> +		entry->oif_id = portid;

> >>>>> +		memcpy(entry->src_mac.addr, mac,

> ODPH_ETHADDR_LEN);

> >>>>> +	}

> >>>>> +}

> >>>>> +

> >>>>> +void dump_fwd_db_entry(fwd_db_entry_t *entry)

> >>>>> +{

> >>>>> +	char subnet_str[MAX_STRING];

> >>>>> +	char mac_str[MAX_STRING];

> >>>>> +

> >>>>> +	mac_addr_str(mac_str, &entry->dst_mac);

> >>>>> +	printf("%-16s%-16s%-16s\n",

> >>>>> +	       ipv4_subnet_str(subnet_str, &entry->subnet),

> >>>>> +	       entry->oif, mac_str);

> >>>>> +}

> >>>>> +

> >>>>> +void dump_fwd_db(void)

> >>>>> +{

> >>>>> +	fwd_db_entry_t *entry;

> >>>>> +

> >>>>> +	printf("Routing table\n"

> >>>>> +	       "-----------------\n"

> >>>>> +	       "%-16s%-16s%-16s\n",

> >>>>> +	       "subnet", "next_hop", "dest_mac");

> >>>>> +

> >>>>> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> >>>>> +		dump_fwd_db_entry(entry);

> >>>>> +

> >>>>> +	printf("\n");

> >>>>> +}

> >>>>> +

> >>>>> +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key)

> >>>>> +{

> >>>>> +	fwd_db_entry_t *entry;

> >>>>> +	flow_entry_t *flow;

> >>>>> +	flow_bucket_t *bucket;

> >>>>> +	uint64_t hash;

> >>>>> +

> >>>>> +	/* first find in cache */

> >>>>> +	hash = l3fwd_calc_hash(key);

> >>>>> +	hash &= fwd_lookup_cache.count - 1;

> >>>>> +	bucket = &fwd_lookup_cache.bucket[hash];

> >>>>> +	flow = lookup_fwd_cache(key, bucket);

> >>>>> +	if (flow)

> >>>>> +		return flow->fwd_entry;

> >>>>> +

> >>>>> +	for (entry = fwd_db->list; NULL != entry; entry = entry->next)

> > {

> >>>>> +		uint32_t mask;

> >>>>> +

> >>>>> +		mask = ((1u << entry->subnet.depth) - 1) <<

> >>>>> +			(32 - entry->subnet.depth);

> >>>>> +

> >>>>> +		if (entry->subnet.addr == (key->dst_ip & mask))

> >>>>> +			break;

> >>>>> +	}

> >>>>> +

> >>>>> +	return entry;

> >>>>> +}

> >>>>> diff --git a/example/l3fwd/odp_l3fwd_db.h

> >>>> b/example/l3fwd/odp_l3fwd_db.h

> >>>>> new file mode 100644

> >>>>> index 0000000..840946a

> >>>>> --- /dev/null

> >>>>> +++ b/example/l3fwd/odp_l3fwd_db.h

> >>>>> @@ -0,0 +1,130 @@

> >>>>> +/* Copyright (c) 2016, Linaro Limited

> >>>>> + * All rights reserved.

> >>>>> + *

> >>>>> + * SPDX-License-Identifier:     BSD-3-Clause

> >>>>> + */

> >>>>> +

> >>>>> +#ifndef ODP_L3FWD_DB_H_

> >>>>> +#define ODP_L3FWD_DB_H_

> >>>>> +

> >>>>> +#ifdef __cplusplus

> >>>>> +extern "C" {

> >>>>> +#endif

> >>>>> +

> >>>>> +#include <odp_api.h>

> >>>>> +#include <odp/helper/eth.h>

> >>>>> +

> >>>>> +#define OIF_LEN 32

> >>>>> +#define MAX_DB  32

> >>>>> +#define MAX_STRING  32

> >>>>> +

> >>>>> +/**

> >>>>> + * Default number of flows

> >>>>> + */

> >>>>> +#define FWD_DEF_FLOW_COUNT		100000

> >>>>> +

> >>>>> +/**

> >>>>> + * Default Hash bucket number

> >>>>> + */

> >>>>> +#define FWD_DEF_BUCKET_COUNT	(FWD_DEF_FLOW_COUNT / 8)

> >>>>> +

> >>>>> +/**

> >>>>> + * IP address range (subnet)

> >>>>> + */

> >>>>> +typedef struct ip_addr_range_s {

> >>>>> +	uint32_t  addr;     /**< IP address, host endianness */

> >>>>> +	uint32_t  depth;    /**< subnet bit width */

> >>>>> +} ip_addr_range_t;

> >>>>> +

> >>>>> +/**

> >>>>> + * TCP/UDP flow

> >>>>> + */

> >>>>> +typedef struct ipv4_tuple5_s {

> >>>>> +	uint32_t src_ip;

> >>>>> +	uint32_t dst_ip;

> >>>>> +	uint16_t src_port;

> >>>>> +	uint16_t dst_port;

> >>>>> +	uint8_t  proto;

> >>>>> +} ipv4_tuple5_t;

> >>>>> +

> >>>>> +/**

> >>>>> + * Forwarding data base entry

> >>>>> + */

> >>>>> +typedef struct fwd_db_entry_s {

> >>>>> +	struct fwd_db_entry_s *next;          /**< Next entry on list

> > */

> >>>>> +	char                    oif[OIF_LEN]; /**< Output interface

> > name */

> >>>>> +	int			oif_id;	      /**< Output interface

> > idx */

> >>>>> +	odph_ethaddr_t		src_mac;      /**< Output source

> MAC

> > */

> >>>>> +	odph_ethaddr_t		dst_mac;      /**< Output

> destination

> >>>> MAC */

> >>>>> +	ip_addr_range_t		subnet;       /**< Subnet for this

> > router

> >>>> */

> >>>>> +} fwd_db_entry_t;

> >>>>> +

> >>>>> +/**

> >>>>> + * Forwarding data base

> >>>>> + */

> >>>>> +typedef struct fwd_db_s {

> >>>>> +	uint32_t          index;          /**< Next available entry */

> >>>>> +	fwd_db_entry_t   *list;           /**< List of active routes

> > */

> >>>>> +	fwd_db_entry_t    array[MAX_DB];  /**< Entry storage */

> >>>>> +} fwd_db_t;

> >>>>> +

> >>>>> +/** Global pointer to fwd db */

> >>>>> +extern fwd_db_t *fwd_db;

> >>>>> +

> >>>>> +/**

> >>>>> + * Initialize FWD DB

> >>>>> + */

> >>>>> +void init_fwd_db(void);

> >>>>> +

> >>>>> +/**

> >>>>> + * Initialize forward lookup cache based on hash

> >>>>> + */

> >>>>> +void init_fwd_hash_cache(void);

> >>>>> +

> >>>>> +/**

> >>>>> + * Create a forwarding database entry

> >>>>> + *

> >>>>> + * String is of the format "SubNet,Intf,NextHopMAC"

> >>>>> + *

> >>>>> + * @param input  Pointer to string describing route

> >>>>> + * @param oif  Pointer to out interface name, as a return value

> >>>>> + *

> >>>>> + * @return 0 if successful else -1

> >>>>> + */

> >>>>> +int create_fwd_db_entry(char *input, char **oif);

> >>>>> +

> >>>>> +/**

> >>>>> + * Scan FWD DB entries and resolve output queue and source MAC

> >>> address

> >>>>> + *

> >>>>> + * @param intf   Interface name string

> >>>>> + * @param portid Output queue for packet transmit

> >>>>> + * @param mac    MAC address of this interface

> >>>>> + */

> >>>>> +void resolve_fwd_db(char *intf, int portid, uint8_t *mac);

> >>>>> +

> >>>>> +/**

> >>>>> + * Display one forwarding database entry

> >>>>> + *

> >>>>> + * @param entry  Pointer to entry to display

> >>>>> + */

> >>>>> +void dump_fwd_db_entry(fwd_db_entry_t *entry);

> >>>>> +

> >>>>> +/**

> >>>>> + * Display the forwarding database

> >>>>> + */

> >>>>> +void dump_fwd_db(void);

> >>>>> +

> >>>>> +/**

> >>>>> + * Find a matching forwarding database entry

> >>>>> + *

> >>>>> + * @param key  ipv4 tuple

> >>>>> + *

> >>>>> + * @return pointer to forwarding DB entry else NULL

> >>>>> + */

> >>>>> +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key);

> >>>>> +

> >>>>> +#ifdef __cplusplus

> >>>>> +}

> >>>>> +#endif

> >>>>> +

> >>>>> +#endif

> >>>>> diff --git a/example/l3fwd/odp_l3fwd_lpm.c

> >>>> b/example/l3fwd/odp_l3fwd_lpm.c

> >>>>> new file mode 100644

> >>>>> index 0000000..1b3bfcf

> >>>>> --- /dev/null

> >>>>> +++ b/example/l3fwd/odp_l3fwd_lpm.c

> >>>>> @@ -0,0 +1,224 @@

> >>>>> +/* Copyright (c) 2016, Linaro Limited

> >>>>> + * All rights reserved.

> >>>>> + *

> >>>>> + * SPDX-License-Identifier:     BSD-3-Clause

> >>>>> + */

> >>>>> +#ifndef _GNU_SOURCE

> >>>>> +#define _GNU_SOURCE

> >>>>> +#endif

> >>>>> +

> >>>>> +#include <stdio.h>

> >>>>> +#include <stdlib.h>

> >>>>> +

> >>>>> +#include <example_debug.h>

> >>>>> +#include <odp_api.h>

> >>>>> +

> >>>>> +#include <odp_l3fwd_lpm.h>

> >>>>> +

> >>>>> +/**

> >>>>> + * This is a simple implementation of lpm based on patricia tree.

> >>>>> + *

> >>>>> + * Tradeoff exists between memory consumption and lookup time.

> >>>>> + * Currently it prefers 5 levels: {16, 4, 4, 4, 4}, could be 3

> >>>>> + * levels: {16, 8, 8} by defining FIB_NEXT_STRIDE as 8. Other

> >>>>> + * levels are also possible.

> >>>>> + *

> >>>>> + * the ip here is host endian, when doing init or lookup, the

> >>>>> + * caller should do endianness conversion if needed.

> >>>>> + */

> >>>>> +

> >>>>> +#define FIB_IP_WIDTH 32

> >>>>> +#define FIB_FIRST_STRIDE 16

> >>>>> +#define FIB_NEXT_STRIDE 4

> >>>>> +#define FIB_NEXT_SIZE (1 << FIB_NEXT_STRIDE)

> >>>>> +#define FIB_SUB_COUNT 16384

> >>>>> +#define DEPTH_TO_MASK(depth) ((1 << (depth)) - 1)

> >>>>> +

> >>>>> +typedef struct fib_node_s {

> >>>>> +	union {

> >>>>> +		uint32_t next_hop;

> >>>>> +		struct fib_node_s *next; /* next level table */

> >>>>> +	};

> >>>>> +	uint8_t valid	:1; /* 1, this node has a valid next hop */

> >>>>> +	uint8_t end	:1; /* 0, next points to the extended table */

> >>>>> +	uint8_t depth	:6; /* bit length of subnet mask */

> >>>>> +} fib_node_t;

> >>>>> +

> >>>>> +typedef struct fib_sub_tbl_s {

> >>>>> +	fib_node_t *fib_nodes;

> >>>>> +	uint32_t fib_count;

> >>>>> +	uint32_t fib_idx;

> >>>>> +} fib_sub_tbl_t;

> >>>>> +

> >>>>> +static fib_node_t fib_rt_tbl[1 << FIB_FIRST_STRIDE];

> >>>>> +static fib_sub_tbl_t fib_lpm_cache;

> >>>>> +

> >>>>> +static inline fib_node_t *fib_alloc_sub(void)

> >>>>> +{

> >>>>> +	fib_node_t *sub_tbl = NULL;

> >>>>> +	uint32_t i, nb_entry;

> >>>>> +

> >>>>> +	/* extend to next level */

> >>>>> +	if (fib_lpm_cache.fib_idx < fib_lpm_cache.fib_count) {

> >>>>> +		nb_entry = (fib_lpm_cache.fib_idx + 1) *

> > FIB_NEXT_SIZE;

> >>>>> +		sub_tbl = &fib_lpm_cache.fib_nodes[nb_entry];

> >>>>> +		fib_lpm_cache.fib_idx++;

> >>>>> +		for (i = 0; i < nb_entry; i++) {

> >>>>> +			sub_tbl[i].valid = 0;

> >>>>> +			sub_tbl[i].end = 1;

> >>>>> +		}

> >>>>> +	}

> >>>>> +

> >>>>> +	return sub_tbl;

> >>>>> +}

> >>>>> +

> >>>>> +static void fib_update_node(fib_node_t *fe, int port, int depth)

> >>>>> +{

> >>>>> +	fib_node_t *p;

> >>>>> +	int i;

> >>>>> +

> >>>>> +	if (fe->end) {

> >>>>> +		if (!fe->valid) {

> >>>>> +			fe->depth = depth;

> >>>>> +			fe->next_hop = port;

> >>>>> +			fe->valid = 1;

> >>>>> +		} else if (fe->depth <= depth) {

> >>>>> +			fe->next_hop = port;

> >>>>> +			fe->depth = depth;

> >>>>> +		}

> >>>>> +

> >>>>> +		return;

> >>>>> +	}

> >>>>> +

> >>>>> +	for (i = 0; i < FIB_NEXT_SIZE; i++) {

> >>>>> +		p = &fe->next[i];

> >>>>> +		if (p->end)

> >>>>> +			fib_update_node(p, port, depth);

> >>>>> +	}

> >>>>> +}

> >>>>> +

> >>>>> +static void fib_insert_node(fib_node_t *fe, uint32_t ip, uint32_t

> >>> next_hop,

> >>>>> +			    int ip_width, int eat_bits, int depth)

> >>>>> +{

> >>>>> +	int i;

> >>>>> +	uint32_t idx, port;

> >>>>> +	fib_node_t *p;

> >>>>> +

> >>>>> +	if (fe->end) {

> >>>>> +		port = fe->next_hop;

> >>>>> +		p = fib_alloc_sub();

> >>>>> +		if (!p)

> >>>>> +			return;

> >>>>> +

> >>>>> +		fe->next = p;

> >>>>> +		fe->end = 0;

> >>>>> +		if (fe->valid) {

> >>>>> +			for (i = 0; i < FIB_NEXT_SIZE; i++) {

> >>>>> +				p = &fe->next[i];

> >>>>> +				p->next_hop = port;

> >>>>> +				p->depth = fe->depth;

> >>>>> +			}

> >>>>> +		}

> >>>>> +	}

> >>>>> +	if (depth - eat_bits <= FIB_NEXT_STRIDE) {

> >>>>> +		ip_width -= depth - eat_bits;

> >>>>> +		idx = ip >> ip_width;

> >>>>> +		ip &= DEPTH_TO_MASK(ip_width);

> >>>>> +		p = &fe->next[idx];

> >>>>> +		fib_update_node(p, next_hop, depth);

> >>>>> +	} else {

> >>>>> +		ip_width -= FIB_NEXT_STRIDE;

> >>>>> +		idx = ip >> ip_width;

> >>>>> +		p = &fe->next[idx];

> >>>>> +		ip &= DEPTH_TO_MASK(ip_width);

> >>>>> +		eat_bits += FIB_NEXT_STRIDE;

> >>>>> +		fib_insert_node(p, ip, next_hop, ip_width, eat_bits,

> > depth);

> >>>>> +	}

> >>>>> +}

> >>>>> +

> >>>>> +void fib_tbl_init(void)

> >>>>> +{

> >>>>> +	int i;

> >>>>> +	fib_node_t *fe;

> >>>>> +	uint32_t size;

> >>>>> +	odp_shm_t lpm_shm;

> >>>>> +

> >>>>> +	for (i = 0; i < (1 << FIB_FIRST_STRIDE); i++) {

> >>>>> +		fe = &fib_rt_tbl[i];

> >>>>> +		fe->valid = 0;

> >>>>> +		fe->end = 1;

> >>>>> +		fe->depth = 0;

> >>>>> +		fe->next_hop = 0;

> >>>>> +	}

> >>>>> +

> >>>>> +	size = FIB_NEXT_SIZE * FIB_SUB_COUNT;

> >>>>> +	/*Reserve memory for Routing hash table*/

> >>>>> +	lpm_shm = odp_shm_reserve("fib_lpm_sub", size,

> >>>> ODP_CACHE_LINE_SIZE, 0);

> >>>>> +	fe = odp_shm_addr(lpm_shm);

> >>>>> +	if (!fe) {

> >>>>> +		EXAMPLE_ERR("Error: shared mem alloc failed for lpm

> >>>> cache.\n");

> >>>>> +		exit(-1);

> >>>>> +	}

> >>>>> +

> >>>>> +	fib_lpm_cache.fib_nodes = fe;

> >>>>> +	fib_lpm_cache.fib_count = FIB_SUB_COUNT;

> >>>>> +	fib_lpm_cache.fib_idx = 0;

> >>>>> +}

> >>>>> +

> >>>>> +void fib_tbl_insert(uint32_t ip, int port, int depth)

> >>>>> +{

> >>>>> +	fib_node_t *fe, *p;

> >>>>> +	uint32_t idx;

> >>>>> +	int i, j;

> >>>>> +	int nb_bits;

> >>>>> +

> >>>>> +	nb_bits = FIB_FIRST_STRIDE;

> >>>>> +	idx = ip >> nb_bits;

> >>>>> +	fe = &fib_rt_tbl[idx];

> >>>>> +	if (depth <= nb_bits) {

> >>>>> +		if (fe->end) {

> >>>>> +			fe->next_hop = port;

> >>>>> +			fe->depth = depth;

> >>>>> +			fe->valid = 1;

> >>>>> +			return;

> >>>>> +		}

> >>>>> +

> >>>>> +		for (i = 0; i < FIB_NEXT_SIZE; i++) {

> >>>>> +			p = &fe->next[i];

> >>>>> +			if (p->end)

> >>>>> +				fib_update_node(p, port, depth);

> >>>>> +			else

> >>>>> +				for (j = 0; j < FIB_NEXT_SIZE; j++)

> >>>>> +					fib_update_node(&p->next[j],

> > port,

> >>>>> +							depth);

> >>>>> +		}

> >>>>> +

> >>>>> +		return;

> >>>>> +	}

> >>>>> +

> >>>>> +	/* need to check sub table */

> >>>>> +	ip &= DEPTH_TO_MASK(FIB_IP_WIDTH - nb_bits);

> >>>>> +	fib_insert_node(fe, ip, port, FIB_IP_WIDTH - nb_bits, nb_bits,

> > depth);

> >>>>> +}

> >>>>> +

> >>>>> +int fib_tbl_lookup(uint32_t ip, int *port)

> >>>>> +{

> >>>>> +	fib_node_t *fe;

> >>>>> +	uint32_t idx;

> >>>>> +	int nb_bits;

> >>>>> +

> >>>>> +	nb_bits = FIB_IP_WIDTH - FIB_FIRST_STRIDE;

> >>>>> +	idx = ip >> nb_bits;

> >>>>> +	fe = &fib_rt_tbl[idx];

> >>>>> +

> >>>>> +	ip &= DEPTH_TO_MASK(nb_bits);

> >>>>> +	while (!fe->end) {

> >>>>> +		nb_bits -= FIB_NEXT_STRIDE;

> >>>>> +		idx = ip >> nb_bits;

> >>>>> +		fe = &fe->next[idx];

> >>>>> +		ip &= DEPTH_TO_MASK(nb_bits);

> >>>>> +	}

> >>>>> +	*port = fe->next_hop;

> >>>>> +

> >>>>> +	return fe->valid ? 0 : -1;

> >>>>> +}

> >>>>> diff --git a/example/l3fwd/odp_l3fwd_lpm.h

> >>>> b/example/l3fwd/odp_l3fwd_lpm.h

> >>>>> new file mode 100644

> >>>>> index 0000000..8f78e39

> >>>>> --- /dev/null

> >>>>> +++ b/example/l3fwd/odp_l3fwd_lpm.h

> >>>>> @@ -0,0 +1,20 @@

> >>>>> +/* Copyright (c) 2016, Linaro Limited

> >>>>> + * All rights reserved.

> >>>>> + *

> >>>>> + * SPDX-License-Identifier:     BSD-3-Clause

> >>>>> + */

> >>>>> +

> >>>>> +#ifndef ODP_L3FWD_LPM_H_

> >>>>> +#define ODP_L3FWD_LPM_H_

> >>>> it has to be _ODP_EXAMPLE_L3FWD_LPM_H

> >>>>

> >>>> i.e. define should be started with "_" meaning that it's not odp api.

> >>>> (I know that in some places we use _ODP_ in other places we use ODP_

> for

> >>>> that

> >>>> case. But that is wrong. We should not overlap with api name space.).

> >>>>

> >>>>> +

> >>>>> +#ifdef __cplusplus

> >>>>> +extern "C" {

> >>>>> +#endif

> >>>>> +void fib_tbl_init(void);

> >>>>> +void fib_tbl_insert(uint32_t ip, int port, int depth);

> >>>>> +int fib_tbl_lookup(uint32_t ip, int *port);

> >>>>> +#ifdef __cplusplus

> >>>>> +}

> >>>>> +#endif

> >>>>> +

> >>>>> +#endif

> >>>>> diff --git a/example/m4/configure.m4 b/example/m4/configure.m4

> >>>>> index bbda38f..78ef396 100644

> >>>>> --- a/example/m4/configure.m4

> >>>>> +++ b/example/m4/configure.m4

> >>>>> @@ -19,5 +19,6 @@ AC_CONFIG_FILES([example/classifier/Makefile

> >>>>>    		 example/timer/Makefile

> >>>>>    		 example/traffic_mgmt/Makefile

> >>>>>    		 example/l2fwd_simple/Makefile

> >>>>> +		 example/l3fwd/Makefile

> >>>>>    		 example/switch/Makefile

> >>>>>    		 example/hello/Makefile])

> >>>> On thought is what we need with l2fwd. We places it under

> >>>> test/performance/ but it's actual example app.

> >>>> Not sure that it's clear for new users. Definitely l2fwd and l3fwd have

> >>>> to be at the same level. I'm thinking

> >>>> about placing both to test/performance/ and create symlink with some

> >>>> README to example/. But I'm not sure

> >>>> about that, we need to discuss.

> >>> Yes.  In my opinion, we do not need a test/performance directory.

> >>> If want to benchmarking automatically, we can build all the apps but run

> >> only

> >>> interested one.

> >>>

> >>>> Maxim.

> >>>>

> >
diff mbox

Patch

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