diff mbox

[1/2] example: echo: add new L2 echo example application

Message ID 1466073070-20148-1-git-send-email-matias.elo@nokia.com
State New
Headers show

Commit Message

Elo, Matias (Nokia - FI/Espoo) June 16, 2016, 10:31 a.m. UTC
Added new L2 echo example application which works in the
same manner as standard L3 ping utility. The application has
two modes transmitter and responder.

In transmitter mode the application sends echo packets from
selected interface and waits for responses. When a response
is received RTT measurement is printed.

In responder mode the application merely resends received
echo packets back to the transmitter.

Signed-off-by: Matias Elo <matias.elo@nokia.com>
---
 example/Makefile.am      |   2 +-
 example/echo/.gitignore  |   1 +
 example/echo/Makefile.am |  10 +
 example/echo/odp_echo.c  | 528 +++++++++++++++++++++++++++++++++++++++++++++++
 example/m4/configure.m4  |   3 +-
 5 files changed, 542 insertions(+), 2 deletions(-)
 create mode 100644 example/echo/.gitignore
 create mode 100644 example/echo/Makefile.am
 create mode 100644 example/echo/odp_echo.c

Comments

Maxim Uvarov June 16, 2016, 10:37 a.m. UTC | #1
Matias, you also missed make check test for this. I think it's should be 
easy to do with vethX.

Maxim.

On 06/16/16 13:31, Matias Elo wrote:
> Added new L2 echo example application which works in the
> same manner as standard L3 ping utility. The application has
> two modes transmitter and responder.
>
> In transmitter mode the application sends echo packets from
> selected interface and waits for responses. When a response
> is received RTT measurement is printed.
>
> In responder mode the application merely resends received
> echo packets back to the transmitter.
>
> Signed-off-by: Matias Elo <matias.elo@nokia.com>
> ---
>   example/Makefile.am      |   2 +-
>   example/echo/.gitignore  |   1 +
>   example/echo/Makefile.am |  10 +
>   example/echo/odp_echo.c  | 528 +++++++++++++++++++++++++++++++++++++++++++++++
>   example/m4/configure.m4  |   3 +-
>   5 files changed, 542 insertions(+), 2 deletions(-)
>   create mode 100644 example/echo/.gitignore
>   create mode 100644 example/echo/Makefile.am
>   create mode 100644 example/echo/odp_echo.c
>
> diff --git a/example/Makefile.am b/example/Makefile.am
> index 7f82c4d..ab37f21 100644
> --- a/example/Makefile.am
> +++ b/example/Makefile.am
> @@ -1 +1 @@
> -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt l2fwd_simple switch
> +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt l2fwd_simple switch echo
> diff --git a/example/echo/.gitignore b/example/echo/.gitignore
> new file mode 100644
> index 0000000..b1e5eb5
> --- /dev/null
> +++ b/example/echo/.gitignore
> @@ -0,0 +1 @@
> +odp_echo
> diff --git a/example/echo/Makefile.am b/example/echo/Makefile.am
> new file mode 100644
> index 0000000..323522f
> --- /dev/null
> +++ b/example/echo/Makefile.am
> @@ -0,0 +1,10 @@
> +include $(top_srcdir)/example/Makefile.inc
> +
> +bin_PROGRAMS = odp_echo$(EXEEXT)
> +odp_echo_LDFLAGS = $(AM_LDFLAGS) -static
> +odp_echo_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
> +
> +noinst_HEADERS = \
> +		  $(top_srcdir)/example/example_debug.h
> +
> +dist_odp_echo_SOURCES = odp_echo.c
> diff --git a/example/echo/odp_echo.c b/example/echo/odp_echo.c
> new file mode 100644
> index 0000000..cc966e1
> --- /dev/null
> +++ b/example/echo/odp_echo.c
> @@ -0,0 +1,528 @@
> +/* Copyright (c) 2016, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <getopt.h>
> +
> +#include <odp_api.h>
> +#include <odp/helper/linux.h>
> +#include <odp/helper/eth.h>
> +#include <odp/helper/ip.h>
> +
> +#define POOL_NUM_PKT 256
> +#define POOL_SEG_LEN 1856
> +
> +#define MAX_WORKERS 2
> +
> +#define ECHO_PACKET_LEN 64
> +#define ECHO_MAGIC 0x92749451
> +#define ECHO_TX_INTERVAL ODP_TIME_SEC_IN_NS
> +
> +/**
> + * Application mode
> + */
> +typedef enum appl_mode_t {
> +	TRANSMIT,
> +	RESPOND
> +} appl_mode_t;
> +
> +/** Get rid of path in filename - only for unix-type paths using '/' */
> +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
> +			    strrchr((file_name), '/') + 1 : (file_name))
> +
> +/**
> + * Parsed command line application arguments
> + */
> +typedef struct {
> +	char *if_str;		/**< Storage for interface name */
> +	odph_ethaddr_t dst;	/**< Destination MAC address */
> +	appl_mode_t mode;	/**< Application mode */
> +	uint64_t wait;		/**< Wait new packets */
> +	int burst_size;		/**< Packet burst size */
> +} appl_args_t;
> +
> +/**
> + * Global application data
> + */
> +struct {
> +	appl_args_t appl;	   /**< Parsed application arguments */
> +	odp_pktio_t iface;	   /**< Pktio handle */
> +	odph_ethaddr_t src;	   /**< Source MAC address */
> +	odp_pktin_queue_t pktin;   /**< Packet input queue */
> +	odp_pktout_queue_t pktout; /**< packet output queue */
> +	odp_pool_t pool;	   /**< Packet pool */
> +} global;
> +
> +/**
> + * Echo packet header
> + */
> +typedef struct ODP_PACKED {
> +	uint32_t llc;		/**< LLC header */
> +	uint32_t magic;		/**< Magic number for verifying echo packets */
> +	uint32_t seq;		/**< Sequence number */
> +	uint64_t ts;		/**< Timestamp (ns) */
> +} echo_hdr_t;
> +
> +/**
> + * Create a pktio handle
> + *
> + * @param dev         Device name
> + * @param pool        Associated Packet Pool
> + * @param pktin[out]  Packet input queue
> + * @param pktout[out] Packet output queue
> + *
> + * @return The handle of the created pktio object.
> + */
> +static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
> +				odp_pktin_queue_t *pktin,
> +				odp_pktout_queue_t *pktout)
> +{
> +	odp_pktio_param_t pktio_param;
> +	odp_pktin_queue_param_t in_queue_param;
> +	odp_pktout_queue_param_t out_queue_param;
> +	odp_pktio_t pktio;
> +
> +	odp_pktio_param_init(&pktio_param);
> +
> +	pktio = odp_pktio_open(name, pool, &pktio_param);
> +	if (pktio == ODP_PKTIO_INVALID) {
> +		printf("Error: failed to open %s\n", name);
> +		exit(1);
> +	}
> +	/* Configure one input queue */
> +	odp_pktin_queue_param_init(&in_queue_param);
> +	in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
> +	in_queue_param.num_queues = 1;
> +	if (odp_pktin_queue_config(pktio, &in_queue_param)) {
> +		printf("Error: failed to config input queue for %s\n", name);
> +		exit(1);
> +	}
> +	/* Configure one output queue */
> +	odp_pktout_queue_param_init(&out_queue_param);
> +	out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
> +	out_queue_param.num_queues = 1;
> +	if (odp_pktout_queue_config(pktio, &out_queue_param)) {
> +		printf("Error: failed to config output queue for %s\n", name);
> +		exit(1);
> +	}
> +	/* Save input and output queue handles */
> +	if (odp_pktin_queue(pktio, pktin, 1) != 1) {
> +		printf("Error: pktin queue query failed for %s\n", name);
> +		exit(1);
> +	}
> +	if (odp_pktout_queue(pktio, pktout, 1) != 1) {
> +		printf("Error: pktout queue query failed for %s\n", name);
> +		exit(1);
> +	}
> +	return pktio;
> +}
> +
> +/**
> + * Initialize echo packet headers
> + *
> + * @param pkt  Packet handle
> + *
> + * @retval 0 on success
> + * @retval <0 on failure
> + */
> +static uint32_t echo_packet_init(odp_packet_t pkt)
> +{
> +	odph_ethhdr_t *eth;
> +	echo_hdr_t echo;
> +	static uint32_t seq_num;
> +
> +	/* Ethernet headers */
> +	eth = odp_packet_data(pkt);
> +	eth->src = global.src;
> +	eth->dst = global.appl.dst;
> +	eth->type = odp_cpu_to_be_16(odp_packet_len(pkt) - ODPH_ETHHDR_LEN);
> +
> +	/* Echo headers */
> +	echo.llc = 0; /* NULL DSAP, SSAP and control fields */
> +	echo.magic = ECHO_MAGIC;
> +	echo.seq = seq_num++;
> +	echo.ts = odp_time_to_ns(odp_time_global());
> +
> +	if (odp_packet_copy_from_mem(pkt, ODPH_ETHHDR_LEN, sizeof(echo),
> +				     &echo) != 0) {
> +		printf("Error: copying echo header failed\n");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +/**
> + * Echo header start pointer
> + *
> + * @param pkt  Packet handle
> + *
> + * @return  Echo header start pointer
> + * @retval  NULL packet does not contain a valid echo header
> + */
> +static echo_hdr_t *echo_hdr(odp_packet_t pkt)
> +{
> +	odph_ethhdr_t *eth;
> +	echo_hdr_t *echo;
> +
> +	eth = odp_packet_l2_ptr(pkt, NULL);
> +	echo = (echo_hdr_t *)(eth + 1);
> +
> +	if (echo->magic != ECHO_MAGIC)
> +		return NULL;
> +
> +	return echo;
> +}
> +
> +/**
> + * Echo packet responder thread
> + */
> +static int run_responder(void *arg ODP_UNUSED)
> +{
> +	odp_packet_t pkt_tbl[global.appl.burst_size];
> +	odp_packet_t tx_tbl[global.appl.burst_size];
> +	int pkts, sent, tx_drops, i;
> +
> +	printf("\n*** Echo responder started ***\n\n");
> +
> +	for (;;) {
> +		int num_rx = 0;
> +
> +		pkts = odp_pktin_recv_tmo(global.pktin, pkt_tbl,
> +					  global.appl.burst_size,
> +					  global.appl.wait);
> +
> +		if (odp_unlikely(pkts <= 0))
> +			continue;
> +		for (i = 0; i < pkts; i++) {
> +			odp_packet_t pkt = pkt_tbl[i];
> +			odph_ethhdr_t *eth;
> +
> +			if (odp_unlikely(!odp_packet_has_eth(pkt))) {
> +				odp_packet_free(pkt);
> +				continue;
> +			}
> +			eth = odp_packet_l2_ptr(pkt, NULL);
> +
> +			if (echo_hdr(pkt) == NULL) {
> +				odp_packet_free(pkt);
> +				continue;
> +			}
> +
> +			/* Swap MAC addresses */
> +			eth->dst = eth->src;
> +			eth->src = global.src;
> +
> +			tx_tbl[num_rx++] = pkt;
> +		}
> +		sent = odp_pktout_send(global.pktout, tx_tbl, num_rx);
> +		if (odp_unlikely(sent < 0))
> +			sent = 0;
> +		tx_drops = num_rx - sent;
> +		if (odp_unlikely(tx_drops))
> +			odp_packet_free_multi(&tx_tbl[sent], tx_drops);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * Echo packet transmitter thread
> + */
> +static int run_transmitter(void *arg ODP_UNUSED)
> +{
> +	for (;;) {
> +		odp_packet_t pkt;
> +		int sent;
> +
> +		pkt = odp_packet_alloc(global.pool, ECHO_PACKET_LEN);
> +		if (pkt == ODP_PACKET_INVALID)
> +			continue;
> +
> +		if (echo_packet_init(pkt)) {
> +			odp_packet_free(pkt);
> +			continue;
> +		}
> +
> +		sent = odp_pktout_send(global.pktout, &pkt, 1);
> +		if (odp_unlikely(sent != 1))
> +			odp_packet_free(pkt);
> +
> +		odp_time_wait_ns(ECHO_TX_INTERVAL);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * Echo packet receiver thread
> + */
> +static int run_receiver(void *arg ODP_UNUSED)
> +{
> +	odp_packet_t pkt_tbl[global.appl.burst_size];
> +	int pkts, i;
> +
> +	for (;;) {
> +		uint64_t ts;
> +
> +		pkts = odp_pktin_recv_tmo(global.pktin, pkt_tbl,
> +					  global.appl.burst_size,
> +					  global.appl.wait);
> +
> +		if (odp_unlikely(pkts <= 0))
> +			continue;
> +
> +		ts = odp_time_to_ns(odp_time_global());
> +
> +		for (i = 0; i < pkts; i++) {
> +			odp_packet_t pkt = pkt_tbl[i];
> +			odph_ethhdr_t *eth;
> +			odph_ethaddr_t *src;
> +			echo_hdr_t *echo;
> +			double delay;
> +
> +			if (odp_unlikely(!odp_packet_has_eth(pkt)))
> +				continue;
> +			eth = odp_packet_l2_ptr(pkt, NULL);
> +
> +			echo = echo_hdr(pkt);
> +			if (!echo)
> +				continue;
> +
> +			delay = (double)(ts - echo->ts) / ODP_TIME_MSEC_IN_NS;
> +			src = &eth->src;
> +
> +			printf("%" PRIu32 " bytes from "
> +			       "%02x:%02x:%02x:%02x:%02x:%02x: seq=%" PRIu32 " "
> +			       "time=%0.3f ms\n", odp_packet_len(pkt),
> +			       src->addr[0], src->addr[1], src->addr[2],
> +			       src->addr[3], src->addr[4], src->addr[5],
> +			       echo->seq, delay);
> +		}
> +		odp_packet_free_multi(pkt_tbl, pkts);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * Print usage information
> + */
> +static void usage(char *progname)
> +{
> +	printf("\n"
> +	       "OpenDataPlane echo example application.\n"
> +	       "\n"
> +	       "Usage: %s OPTIONS\n"
> +	       "  E.g. host1$ %s -m 0 -i eth0 -d 02:01:02:03:04:05\n"
> +	       "       host2$ %s -m 1 -i eth1\n\n"
> +	       " In the above example,\n"
> +	       " host1 sends echo packets from eth0 to the MAC address of\n"
> +	       " host2's eth1 and waits for responses. Host2 waits for echo\n"
> +	       " packets from eth1 and resends them back to the sender.\n"
> +	       "\n"
> +	       "Mandatory OPTIONS:\n"
> +	       "  -i, --interface   Eth interface\n"
> +	       "  -d, --destination Destination MAC address (required in transmitter mode)\n"
> +	       "\n"
> +	       "Optional OPTIONS:\n"
> +	       "  -m, --mode        0: Transmitter (default)\n"
> +	       "                    1: Responder\n"
> +	       "  -n, --no-wait     Busy wait new packets\n"
> +	       "  -b, --burst       Receive burst size\n"
> +	       "  -h, --help        Display help and exit.\n\n"
> +	       "\n", NO_PATH(progname), NO_PATH(progname), NO_PATH(progname));
> +}
> +
> +/**
> + * Parse and store the command line arguments
> + *
> + * @param argc       argument count
> + * @param argv[]     argument vector
> + * @param appl_args  Store application arguments here
> + */
> +static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
> +{
> +	int opt;
> +	int long_index;
> +	size_t len;
> +	int i;
> +	odp_bool_t mac_set = 0;
> +	static const char *shortopts =  "+m:i:d:b:nh";
> +	static const struct option longopts[] = {
> +		{"mode", required_argument, NULL, 'm'},
> +		{"interface", required_argument, NULL, 'i'},
> +		{"destination", required_argument, NULL, 'd'},
> +		{"burst", required_argument, NULL, 'b'},
> +		{"no-wait", no_argument, NULL, 'n'},
> +		{"help", no_argument, NULL, 'h'},
> +		{NULL, 0, NULL, 0}
> +	};
> +
> +	appl_args->if_str = NULL;
> +	appl_args->burst_size = 1;
> +	appl_args->wait = ODP_PKTIN_WAIT;
> +
> +	/* let helper collect its own arguments (e.g. --odph_proc) */
> +	odph_parse_options(argc, argv, shortopts, longopts);
> +
> +	opterr = 0; /* do not issue errors on helper options */
> +
> +	while (1) {
> +		opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
> +
> +		if (opt == -1)
> +			break;	/* No more options */
> +
> +		switch (opt) {
> +		case 'm':
> +			i = atoi(optarg);
> +			if (i == 1)
> +				appl_args->mode = RESPOND;
> +			else
> +				appl_args->mode = TRANSMIT;
> +			break;
> +		case 'i':
> +			len = strlen(optarg);
> +			if (len == 0) {
> +				usage(argv[0]);
> +				exit(EXIT_FAILURE);
> +			}
> +			len += 1;	/* add room for '\0' */
> +
> +			appl_args->if_str = malloc(len);
> +			if (appl_args->if_str == NULL) {
> +				usage(argv[0]);
> +				exit(EXIT_FAILURE);
> +			}
> +			strcpy(appl_args->if_str, optarg);
> +			break;
> +		case 'd':
> +			if (odph_eth_addr_parse(&appl_args->dst, optarg)) {
> +				usage(argv[0]);
> +				exit(EXIT_FAILURE);
> +			}
> +			mac_set = 1;
> +
> +			break;
> +		case 'b':
> +			i = atoi(optarg);
> +			if (i < 1)
> +				appl_args->burst_size = 1;
> +			else
> +				appl_args->burst_size = i;
> +			break;
> +		case 'n':
> +			appl_args->wait = ODP_PKTIN_NO_WAIT;
> +			break;
> +		case 'h':
> +			usage(argv[0]);
> +			exit(EXIT_SUCCESS);
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	if (appl_args->if_str == NULL ||
> +	    (appl_args->mode == TRANSMIT && !mac_set)) {
> +		usage(argv[0]);
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	optind = 1;		/* reset 'extern optind' from the getopt lib */
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	odph_odpthread_t thread_tbl[MAX_WORKERS];
> +	odp_pool_param_t params;
> +	odp_cpumask_t cpumask;
> +	odp_instance_t instance;
> +	int i;
> +	int cpu;
> +	int num_workers;
> +	int req_workers;
> +
> +	/* Initialize ODP */
> +	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);
> +	}
> +
> +	/* Parse and store the application arguments */
> +	parse_args(argc, argv, &global.appl);
> +
> +	/* 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;
> +
> +	global.pool = odp_pool_create("packet pool", &params);
> +
> +	if (global.pool == ODP_POOL_INVALID) {
> +		printf("Error: packet pool create failed.\n");
> +		exit(1);
> +	}
> +
> +	/* Create and configure pktio device */
> +	global.iface = create_pktio(global.appl.if_str, global.pool,
> +				    &global.pktin, &global.pktout);
> +
> +	/* Save interface ethernet address */
> +	if (odp_pktio_mac_addr(global.iface, &global.src,
> +			       ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
> +		printf("Error: interface ethernet address unknown\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (odp_pktio_start(global.iface)) {
> +		printf("Error: unable to start input interface\n");
> +		exit(1);
> +	}
> +
> +	/* Create and bind worker threads */
> +	req_workers = global.appl.mode == TRANSMIT ? 2 : 1;
> +	num_workers = odp_cpumask_default_worker(&cpumask, MAX_WORKERS);
> +
> +	if (num_workers < req_workers) {
> +		printf("Error: not enough workers (%d available, %d required)\n",
> +		       num_workers, req_workers);
> +		exit(1);
> +	}
> +
> +	cpu = odp_cpumask_first(&cpumask);
> +	for (i = 0; i < req_workers; i++) {
> +		odp_cpumask_t thd_mask;
> +		odph_odpthread_params_t thr_params;
> +
> +		memset(&thr_params, 0, sizeof(thr_params));
> +
> +		if (global.appl.mode == RESPOND)
> +			thr_params.start = run_responder;
> +		else
> +			thr_params.start = i % 2 ? run_transmitter :
> +					run_receiver;
> +
> +		thr_params.arg      = NULL;
> +		thr_params.thr_type = ODP_THREAD_WORKER;
> +		thr_params.instance = instance;
> +
> +		odp_cpumask_zero(&thd_mask);
> +		odp_cpumask_set(&thd_mask, cpu);
> +		odph_odpthreads_create(&thread_tbl[i], &thd_mask,
> +				       &thr_params);
> +		cpu = odp_cpumask_next(&cpumask, cpu);
> +	}
> +
> +	odph_odpthreads_join(thread_tbl);
> +
> +	return 0;
> +}
> diff --git a/example/m4/configure.m4 b/example/m4/configure.m4
> index 9731d81..d54ea72 100644
> --- a/example/m4/configure.m4
> +++ b/example/m4/configure.m4
> @@ -19,4 +19,5 @@ AC_CONFIG_FILES([example/classifier/Makefile
>   		 example/timer/Makefile
>   		 example/traffic_mgmt/Makefile
>   		 example/l2fwd_simple/Makefile
> -		 example/switch/Makefile])
> +		 example/switch/Makefile
> +		 example/echo/Makefile])
Maxim Uvarov June 16, 2016, 11:12 a.m. UTC | #2
On 06/16/16 13:31, Matias Elo wrote:
> Added new L2 echo example application which works in the
> same manner as standard L3 ping utility. The application has
> two modes transmitter and responder.
>
> In transmitter mode the application sends echo packets from
> selected interface and waits for responses. When a response
> is received RTT measurement is printed.
>
> In responder mode the application merely resends received
> echo packets back to the transmitter.
>
> Signed-off-by: Matias Elo <matias.elo@nokia.com>
> ---
>   example/Makefile.am      |   2 +-
>   example/echo/.gitignore  |   1 +
>   example/echo/Makefile.am |  10 +
>   example/echo/odp_echo.c  | 528 +++++++++++++++++++++++++++++++++++++++++++++++
>   example/m4/configure.m4  |   3 +-
>   5 files changed, 542 insertions(+), 2 deletions(-)
>   create mode 100644 example/echo/.gitignore
>   create mode 100644 example/echo/Makefile.am
>   create mode 100644 example/echo/odp_echo.c
>
> diff --git a/example/Makefile.am b/example/Makefile.am
> index 7f82c4d..ab37f21 100644
> --- a/example/Makefile.am
> +++ b/example/Makefile.am
> @@ -1 +1 @@
> -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt l2fwd_simple switch
> +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt l2fwd_simple switch echo

sort in alphabetic order

> diff --git a/example/echo/.gitignore b/example/echo/.gitignore
> new file mode 100644
> index 0000000..b1e5eb5
> --- /dev/null
> +++ b/example/echo/.gitignore
> @@ -0,0 +1 @@
> +odp_echo
> diff --git a/example/echo/Makefile.am b/example/echo/Makefile.am
> new file mode 100644
> index 0000000..323522f
> --- /dev/null
> +++ b/example/echo/Makefile.am
> @@ -0,0 +1,10 @@
> +include $(top_srcdir)/example/Makefile.inc
> +
> +bin_PROGRAMS = odp_echo$(EXEEXT)
> +odp_echo_LDFLAGS = $(AM_LDFLAGS) -static
> +odp_echo_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
> +
> +noinst_HEADERS = \
> +		  $(top_srcdir)/example/example_debug.h
> +
> +dist_odp_echo_SOURCES = odp_echo.c
> diff --git a/example/echo/odp_echo.c b/example/echo/odp_echo.c
> new file mode 100644
> index 0000000..cc966e1
> --- /dev/null
> +++ b/example/echo/odp_echo.c
> @@ -0,0 +1,528 @@
> +/* Copyright (c) 2016, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <getopt.h>
> +
> +#include <odp_api.h>
> +#include <odp/helper/linux.h>
> +#include <odp/helper/eth.h>
> +#include <odp/helper/ip.h>
> +
> +#define POOL_NUM_PKT 256
> +#define POOL_SEG_LEN 1856
> +
> +#define MAX_WORKERS 2
> +
> +#define ECHO_PACKET_LEN 64
> +#define ECHO_MAGIC 0x92749451
> +#define ECHO_TX_INTERVAL ODP_TIME_SEC_IN_NS
> +
> +/**
> + * Application mode
> + */
> +typedef enum appl_mode_t {
> +	TRANSMIT,
> +	RESPOND
> +} appl_mode_t;
> +
> +/** Get rid of path in filename - only for unix-type paths using '/' */
> +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
> +			    strrchr((file_name), '/') + 1 : (file_name))
> +
> +/**
> + * Parsed command line application arguments
> + */
> +typedef struct {
> +	char *if_str;		/**< Storage for interface name */
> +	odph_ethaddr_t dst;	/**< Destination MAC address */
> +	appl_mode_t mode;	/**< Application mode */
> +	uint64_t wait;		/**< Wait new packets */
> +	int burst_size;		/**< Packet burst size */
> +} appl_args_t;
> +
> +/**
> + * Global application data
> + */
> +struct {
> +	appl_args_t appl;	   /**< Parsed application arguments */
> +	odp_pktio_t iface;	   /**< Pktio handle */
> +	odph_ethaddr_t src;	   /**< Source MAC address */
> +	odp_pktin_queue_t pktin;   /**< Packet input queue */
> +	odp_pktout_queue_t pktout; /**< packet output queue */
> +	odp_pool_t pool;	   /**< Packet pool */
> +} global;
> +
> +/**
> + * Echo packet header
> + */
> +typedef struct ODP_PACKED {
> +	uint32_t llc;		/**< LLC header */
> +	uint32_t magic;		/**< Magic number for verifying echo packets */
> +	uint32_t seq;		/**< Sequence number */
> +	uint64_t ts;		/**< Timestamp (ns) */
> +} echo_hdr_t;
> +
> +/**
> + * Create a pktio handle
> + *
> + * @param dev         Device name
> + * @param pool        Associated Packet Pool
> + * @param pktin[out]  Packet input queue
> + * @param pktout[out] Packet output queue
> + *
> + * @return The handle of the created pktio object.
> + */
> +static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
> +				odp_pktin_queue_t *pktin,
> +				odp_pktout_queue_t *pktout)
> +{
> +	odp_pktio_param_t pktio_param;
> +	odp_pktin_queue_param_t in_queue_param;
> +	odp_pktout_queue_param_t out_queue_param;
> +	odp_pktio_t pktio;
> +
> +	odp_pktio_param_init(&pktio_param);
> +
> +	pktio = odp_pktio_open(name, pool, &pktio_param);
> +	if (pktio == ODP_PKTIO_INVALID) {
> +		printf("Error: failed to open %s\n", name);
> +		exit(1);
> +	}
> +	/* Configure one input queue */
> +	odp_pktin_queue_param_init(&in_queue_param);
> +	in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
> +	in_queue_param.num_queues = 1;
> +	if (odp_pktin_queue_config(pktio, &in_queue_param)) {
> +		printf("Error: failed to config input queue for %s\n", name);
> +		exit(1);
> +	}

Interesting how tools like Coverity interpret exit without clean 
resource destroy
on errors. I usually use EXAMPLE_ABORT() which aloows me to  safe 2 
lines of code.


> +	/* Configure one output queue */
> +	odp_pktout_queue_param_init(&out_queue_param);
> +	out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
> +	out_queue_param.num_queues = 1;
> +	if (odp_pktout_queue_config(pktio, &out_queue_param)) {
> +		printf("Error: failed to config output queue for %s\n", name);
> +		exit(1);
> +	}
> +	/* Save input and output queue handles */
> +	if (odp_pktin_queue(pktio, pktin, 1) != 1) {
> +		printf("Error: pktin queue query failed for %s\n", name);
> +		exit(1);
> +	}
> +	if (odp_pktout_queue(pktio, pktout, 1) != 1) {
> +		printf("Error: pktout queue query failed for %s\n", name);
> +		exit(1);
> +	}
> +	return pktio;
> +}
> +
> +/**
> + * Initialize echo packet headers
> + *
> + * @param pkt  Packet handle
> + *
> + * @retval 0 on success
> + * @retval <0 on failure
> + */
> +static uint32_t echo_packet_init(odp_packet_t pkt)
> +{
> +	odph_ethhdr_t *eth;
> +	echo_hdr_t echo;
> +	static uint32_t seq_num;
> +
> +	/* Ethernet headers */
> +	eth = odp_packet_data(pkt);
> +	eth->src = global.src;
> +	eth->dst = global.appl.dst;
> +	eth->type = odp_cpu_to_be_16(odp_packet_len(pkt) - ODPH_ETHHDR_LEN);
> +
> +	/* Echo headers */
> +	echo.llc = 0; /* NULL DSAP, SSAP and control fields */
> +	echo.magic = ECHO_MAGIC;
> +	echo.seq = seq_num++;
> +	echo.ts = odp_time_to_ns(odp_time_global());
> +
> +	if (odp_packet_copy_from_mem(pkt, ODPH_ETHHDR_LEN, sizeof(echo),
> +				     &echo) != 0) {
> +		printf("Error: copying echo header failed\n");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +/**
> + * Echo header start pointer
> + *
> + * @param pkt  Packet handle
> + *
> + * @return  Echo header start pointer
> + * @retval  NULL packet does not contain a valid echo header
> + */
> +static echo_hdr_t *echo_hdr(odp_packet_t pkt)
> +{
> +	odph_ethhdr_t *eth;
> +	echo_hdr_t *echo;
> +
> +	eth = odp_packet_l2_ptr(pkt, NULL);
> +	echo = (echo_hdr_t *)(eth + 1);
> +
> +	if (echo->magic != ECHO_MAGIC)
> +		return NULL;
> +
> +	return echo;
> +}
> +
> +/**
> + * Echo packet responder thread
> + */
> +static int run_responder(void *arg ODP_UNUSED)
> +{
> +	odp_packet_t pkt_tbl[global.appl.burst_size];
> +	odp_packet_t tx_tbl[global.appl.burst_size];
> +	int pkts, sent, tx_drops, i;
> +
> +	printf("\n*** Echo responder started ***\n\n");
> +
> +	for (;;) {
> +		int num_rx = 0;

not clear why this declared here and all other variable declared above. 
To make code 1 line shorter?

> +
> +		pkts = odp_pktin_recv_tmo(global.pktin, pkt_tbl,
> +					  global.appl.burst_size,
> +					  global.appl.wait);
> +
> +		if (odp_unlikely(pkts <= 0))
> +			continue;
> +		for (i = 0; i < pkts; i++) {
> +			odp_packet_t pkt = pkt_tbl[i];
> +			odph_ethhdr_t *eth;
> +
> +			if (odp_unlikely(!odp_packet_has_eth(pkt))) {
> +				odp_packet_free(pkt);
> +				continue;
> +			}
> +			eth = odp_packet_l2_ptr(pkt, NULL);
> +
> +			if (echo_hdr(pkt) == NULL) {
> +				odp_packet_free(pkt);
> +				continue;
> +			}
> +
> +			/* Swap MAC addresses */
> +			eth->dst = eth->src;
> +			eth->src = global.src;
> +
> +			tx_tbl[num_rx++] = pkt;
> +		}
> +		sent = odp_pktout_send(global.pktout, tx_tbl, num_rx);
> +		if (odp_unlikely(sent < 0))
> +			sent = 0;
> +		tx_drops = num_rx - sent;
> +		if (odp_unlikely(tx_drops))
> +			odp_packet_free_multi(&tx_tbl[sent], tx_drops);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * Echo packet transmitter thread
> + */
> +static int run_transmitter(void *arg ODP_UNUSED)
> +{
> +	for (;;) {
> +		odp_packet_t pkt;
> +		int sent;
> +
> +		pkt = odp_packet_alloc(global.pool, ECHO_PACKET_LEN);
> +		if (pkt == ODP_PACKET_INVALID)
> +			continue;
> +
> +		if (echo_packet_init(pkt)) {
> +			odp_packet_free(pkt);
> +			continue;
> +		}
> +
> +		sent = odp_pktout_send(global.pktout, &pkt, 1);
> +		if (odp_unlikely(sent != 1))
> +			odp_packet_free(pkt);
> +
> +		odp_time_wait_ns(ECHO_TX_INTERVAL);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * Echo packet receiver thread
> + */
> +static int run_receiver(void *arg ODP_UNUSED)
> +{
> +	odp_packet_t pkt_tbl[global.appl.burst_size];
> +	int pkts, i;
> +
> +	for (;;) {
> +		uint64_t ts;
> +
> +		pkts = odp_pktin_recv_tmo(global.pktin, pkt_tbl,
> +					  global.appl.burst_size,
> +					  global.appl.wait);
> +
> +		if (odp_unlikely(pkts <= 0))
> +			continue;
> +
> +		ts = odp_time_to_ns(odp_time_global());
> +
> +		for (i = 0; i < pkts; i++) {
> +			odp_packet_t pkt = pkt_tbl[i];
> +			odph_ethhdr_t *eth;
> +			odph_ethaddr_t *src;
> +			echo_hdr_t *echo;
> +			double delay;
> +
> +			if (odp_unlikely(!odp_packet_has_eth(pkt)))
> +				continue;
> +			eth = odp_packet_l2_ptr(pkt, NULL);
> +
> +			echo = echo_hdr(pkt);
> +			if (!echo)
> +				continue;
> +
> +			delay = (double)(ts - echo->ts) / ODP_TIME_MSEC_IN_NS;
> +			src = &eth->src;
> +
> +			printf("%" PRIu32 " bytes from "
> +			       "%02x:%02x:%02x:%02x:%02x:%02x: seq=%" PRIu32 " "
> +			       "time=%0.3f ms\n", odp_packet_len(pkt),
> +			       src->addr[0], src->addr[1], src->addr[2],
> +			       src->addr[3], src->addr[4], src->addr[5],
> +			       echo->seq, delay);
> +		}
> +		odp_packet_free_multi(pkt_tbl, pkts);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * Print usage information
> + */
> +static void usage(char *progname)
> +{
> +	printf("\n"
> +	       "OpenDataPlane echo example application.\n"
> +	       "\n"
> +	       "Usage: %s OPTIONS\n"
> +	       "  E.g. host1$ %s -m 0 -i eth0 -d 02:01:02:03:04:05\n"
> +	       "       host2$ %s -m 1 -i eth1\n\n"
> +	       " In the above example,\n"
> +	       " host1 sends echo packets from eth0 to the MAC address of\n"
> +	       " host2's eth1 and waits for responses. Host2 waits for echo\n"
> +	       " packets from eth1 and resends them back to the sender.\n"
> +	       "\n"
> +	       "Mandatory OPTIONS:\n"
> +	       "  -i, --interface   Eth interface\n"
> +	       "  -d, --destination Destination MAC address (required in transmitter mode)\n"
> +	       "\n"
> +	       "Optional OPTIONS:\n"
> +	       "  -m, --mode        0: Transmitter (default)\n"
> +	       "                    1: Responder\n"
> +	       "  -n, --no-wait     Busy wait new packets\n"
> +	       "  -b, --burst       Receive burst size\n"
> +	       "  -h, --help        Display help and exit.\n\n"
> +	       "\n", NO_PATH(progname), NO_PATH(progname), NO_PATH(progname));
> +}
> +
> +/**
> + * Parse and store the command line arguments
> + *
> + * @param argc       argument count
> + * @param argv[]     argument vector
> + * @param appl_args  Store application arguments here
> + */
> +static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
> +{
> +	int opt;
> +	int long_index;
> +	size_t len;
> +	int i;
> +	odp_bool_t mac_set = 0;
> +	static const char *shortopts =  "+m:i:d:b:nh";
> +	static const struct option longopts[] = {
> +		{"mode", required_argument, NULL, 'm'},
> +		{"interface", required_argument, NULL, 'i'},
> +		{"destination", required_argument, NULL, 'd'},
> +		{"burst", required_argument, NULL, 'b'},
> +		{"no-wait", no_argument, NULL, 'n'},
> +		{"help", no_argument, NULL, 'h'},
> +		{NULL, 0, NULL, 0}
> +	};
> +
> +	appl_args->if_str = NULL;
> +	appl_args->burst_size = 1;
> +	appl_args->wait = ODP_PKTIN_WAIT;
> +
> +	/* let helper collect its own arguments (e.g. --odph_proc) */
> +	odph_parse_options(argc, argv, shortopts, longopts);
> +
> +	opterr = 0; /* do not issue errors on helper options */
> +
> +	while (1) {
> +		opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
> +
> +		if (opt == -1)
> +			break;	/* No more options */
> +
> +		switch (opt) {
> +		case 'm':
> +			i = atoi(optarg);
> +			if (i == 1)
> +				appl_args->mode = RESPOND;
> +			else
> +				appl_args->mode = TRANSMIT;
> +			break;
> +		case 'i':
> +			len = strlen(optarg);
> +			if (len == 0) {
> +				usage(argv[0]);
> +				exit(EXIT_FAILURE);
> +			}
> +			len += 1;	/* add room for '\0' */
> +
> +			appl_args->if_str = malloc(len);
> +			if (appl_args->if_str == NULL) {
> +				usage(argv[0]);
> +				exit(EXIT_FAILURE);
> +			}
> +			strcpy(appl_args->if_str, optarg);
> +			break;
> +		case 'd':
> +			if (odph_eth_addr_parse(&appl_args->dst, optarg)) {
> +				usage(argv[0]);
> +				exit(EXIT_FAILURE);
> +			}
> +			mac_set = 1;
> +
> +			break;
> +		case 'b':
> +			i = atoi(optarg);
> +			if (i < 1)
> +				appl_args->burst_size = 1;
> +			else
> +				appl_args->burst_size = i;
> +			break;
> +		case 'n':
> +			appl_args->wait = ODP_PKTIN_NO_WAIT;
> +			break;
> +		case 'h':
> +			usage(argv[0]);
> +			exit(EXIT_SUCCESS);
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	if (appl_args->if_str == NULL ||
> +	    (appl_args->mode == TRANSMIT && !mac_set)) {
> +		usage(argv[0]);
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	optind = 1;		/* reset 'extern optind' from the getopt lib */
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	odph_odpthread_t thread_tbl[MAX_WORKERS];
> +	odp_pool_param_t params;
> +	odp_cpumask_t cpumask;
> +	odp_instance_t instance;
> +	int i;
> +	int cpu;
> +	int num_workers;
> +	int req_workers;
> +
> +	/* Initialize ODP */
> +	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);
> +	}
> +
> +	/* Parse and store the application arguments */
> +	parse_args(argc, argv, &global.appl);
> +
> +	/* 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;
> +
> +	global.pool = odp_pool_create("packet pool", &params);
> +
> +	if (global.pool == ODP_POOL_INVALID) {
> +		printf("Error: packet pool create failed.\n");
> +		exit(1);
> +	}
> +
> +	/* Create and configure pktio device */
> +	global.iface = create_pktio(global.appl.if_str, global.pool,
> +				    &global.pktin, &global.pktout);
> +
> +	/* Save interface ethernet address */
> +	if (odp_pktio_mac_addr(global.iface, &global.src,
> +			       ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
> +		printf("Error: interface ethernet address unknown\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (odp_pktio_start(global.iface)) {
> +		printf("Error: unable to start input interface\n");
> +		exit(1);
> +	}
> +
> +	/* Create and bind worker threads */
> +	req_workers = global.appl.mode == TRANSMIT ? 2 : 1;
> +	num_workers = odp_cpumask_default_worker(&cpumask, MAX_WORKERS);
> +
> +	if (num_workers < req_workers) {
> +		printf("Error: not enough workers (%d available, %d required)\n",
> +		       num_workers, req_workers);
> +		exit(1);
> +	}
> +
> +	cpu = odp_cpumask_first(&cpumask);
> +	for (i = 0; i < req_workers; i++) {
> +		odp_cpumask_t thd_mask;
> +		odph_odpthread_params_t thr_params;
> +
> +		memset(&thr_params, 0, sizeof(thr_params));
> +
> +		if (global.appl.mode == RESPOND)
> +			thr_params.start = run_responder;
> +		else
> +			thr_params.start = i % 2 ? run_transmitter :
> +					run_receiver;
> +
> +		thr_params.arg      = NULL;
> +		thr_params.thr_type = ODP_THREAD_WORKER;
> +		thr_params.instance = instance;
> +
> +		odp_cpumask_zero(&thd_mask);
> +		odp_cpumask_set(&thd_mask, cpu);
> +		odph_odpthreads_create(&thread_tbl[i], &thd_mask,
> +				       &thr_params);
> +		cpu = odp_cpumask_next(&cpumask, cpu);
> +	}
> +
> +	odph_odpthreads_join(thread_tbl);

when you will add this to make check you will need clean termination 
path. There should be some condition to exit,
like number of packets.

> +
> +	return 0;
> +}
> diff --git a/example/m4/configure.m4 b/example/m4/configure.m4
> index 9731d81..d54ea72 100644
> --- a/example/m4/configure.m4
> +++ b/example/m4/configure.m4
> @@ -19,4 +19,5 @@ AC_CONFIG_FILES([example/classifier/Makefile
>   		 example/timer/Makefile
>   		 example/traffic_mgmt/Makefile
>   		 example/l2fwd_simple/Makefile
> -		 example/switch/Makefile])
> +		 example/switch/Makefile
> +		 example/echo/Makefile])
alphabetical order.

Maxim.
Maxim Uvarov June 16, 2016, 11:28 a.m. UTC | #3
On 06/16/16 14:19, Elo, Matias (Nokia - FI/Espoo) wrote:
> Hi Maxim,
>
> I wrote this application for an ODP programming tutorial in the OPNFV design summit next week. For sure I'll add the validation test in a following revision, but for now I would be thankful for any feedback regarding the application code.
>
> -Matias
I did not find anything wrong in code.  Only one thing where I'm not 
sure is if setting ips works for both le/be or we need byte swap:

+	eth = odp_packet_data(pkt);
+	eth->src = global.src;
+	eth->dst = global.appl.dst;


Maxim.


>> -----Original Message-----
>> From: lng-odp [mailto:lng-odp-bounces@lists.linaro.org] On Behalf Of Maxim
>> Uvarov
>> Sent: Thursday, June 16, 2016 1:38 PM
>> To: lng-odp@lists.linaro.org
>> Subject: Re: [lng-odp] [PATCH 1/2] example: echo: add new L2 echo example
>> application
>>
>> Matias, you also missed make check test for this. I think it's should be
>> easy to do with vethX.
>>
>> Maxim.
>>
>> On 06/16/16 13:31, Matias Elo wrote:
>>> Added new L2 echo example application which works in the
>>> same manner as standard L3 ping utility. The application has
>>> two modes transmitter and responder.
>>>
>>> In transmitter mode the application sends echo packets from
>>> selected interface and waits for responses. When a response
>>> is received RTT measurement is printed.
>>>
>>> In responder mode the application merely resends received
>>> echo packets back to the transmitter.
>>>
>>> Signed-off-by: Matias Elo <matias.elo@nokia.com>
>>> ---
>>>    example/Makefile.am      |   2 +-
>>>    example/echo/.gitignore  |   1 +
>>>    example/echo/Makefile.am |  10 +
>>>    example/echo/odp_echo.c  | 528
>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>    example/m4/configure.m4  |   3 +-
>>>    5 files changed, 542 insertions(+), 2 deletions(-)
>>>    create mode 100644 example/echo/.gitignore
>>>    create mode 100644 example/echo/Makefile.am
>>>    create mode 100644 example/echo/odp_echo.c
>>>
>>> diff --git a/example/Makefile.am b/example/Makefile.am
>>> index 7f82c4d..ab37f21 100644
>>> --- a/example/Makefile.am
>>> +++ b/example/Makefile.am
>>> @@ -1 +1 @@
>>> -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt
>> l2fwd_simple switch
>>> +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt
>> l2fwd_simple switch echo
>>> diff --git a/example/echo/.gitignore b/example/echo/.gitignore
>>> new file mode 100644
>>> index 0000000..b1e5eb5
>>> --- /dev/null
>>> +++ b/example/echo/.gitignore
>>> @@ -0,0 +1 @@
>>> +odp_echo
>>> diff --git a/example/echo/Makefile.am b/example/echo/Makefile.am
>>> new file mode 100644
>>> index 0000000..323522f
>>> --- /dev/null
>>> +++ b/example/echo/Makefile.am
>>> @@ -0,0 +1,10 @@
>>> +include $(top_srcdir)/example/Makefile.inc
>>> +
>>> +bin_PROGRAMS = odp_echo$(EXEEXT)
>>> +odp_echo_LDFLAGS = $(AM_LDFLAGS) -static
>>> +odp_echo_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
>>> +
>>> +noinst_HEADERS = \
>>> +		  $(top_srcdir)/example/example_debug.h
>>> +
>>> +dist_odp_echo_SOURCES = odp_echo.c
>>> diff --git a/example/echo/odp_echo.c b/example/echo/odp_echo.c
>>> new file mode 100644
>>> index 0000000..cc966e1
>>> --- /dev/null
>>> +++ b/example/echo/odp_echo.c
>>> @@ -0,0 +1,528 @@
>>> +/* Copyright (c) 2016, Linaro Limited
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:     BSD-3-Clause
>>> + */
>>> +
>>> +#include <stdlib.h>
>>> +#include <stdio.h>
>>> +#include <getopt.h>
>>> +
>>> +#include <odp_api.h>
>>> +#include <odp/helper/linux.h>
>>> +#include <odp/helper/eth.h>
>>> +#include <odp/helper/ip.h>
>>> +
>>> +#define POOL_NUM_PKT 256
>>> +#define POOL_SEG_LEN 1856
>>> +
>>> +#define MAX_WORKERS 2
>>> +
>>> +#define ECHO_PACKET_LEN 64
>>> +#define ECHO_MAGIC 0x92749451
>>> +#define ECHO_TX_INTERVAL ODP_TIME_SEC_IN_NS
>>> +
>>> +/**
>>> + * Application mode
>>> + */
>>> +typedef enum appl_mode_t {
>>> +	TRANSMIT,
>>> +	RESPOND
>>> +} appl_mode_t;
>>> +
>>> +/** Get rid of path in filename - only for unix-type paths using '/' */
>>> +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
>>> +			    strrchr((file_name), '/') + 1 : (file_name))
>>> +
>>> +/**
>>> + * Parsed command line application arguments
>>> + */
>>> +typedef struct {
>>> +	char *if_str;		/**< Storage for interface name */
>>> +	odph_ethaddr_t dst;	/**< Destination MAC address */
>>> +	appl_mode_t mode;	/**< Application mode */
>>> +	uint64_t wait;		/**< Wait new packets */
>>> +	int burst_size;		/**< Packet burst size */
>>> +} appl_args_t;
>>> +
>>> +/**
>>> + * Global application data
>>> + */
>>> +struct {
>>> +	appl_args_t appl;	   /**< Parsed application arguments */
>>> +	odp_pktio_t iface;	   /**< Pktio handle */
>>> +	odph_ethaddr_t src;	   /**< Source MAC address */
>>> +	odp_pktin_queue_t pktin;   /**< Packet input queue */
>>> +	odp_pktout_queue_t pktout; /**< packet output queue */
>>> +	odp_pool_t pool;	   /**< Packet pool */
>>> +} global;
>>> +
>>> +/**
>>> + * Echo packet header
>>> + */
>>> +typedef struct ODP_PACKED {
>>> +	uint32_t llc;		/**< LLC header */
>>> +	uint32_t magic;		/**< Magic number for verifying echo packets */
>>> +	uint32_t seq;		/**< Sequence number */
>>> +	uint64_t ts;		/**< Timestamp (ns) */
>>> +} echo_hdr_t;
>>> +
>>> +/**
>>> + * Create a pktio handle
>>> + *
>>> + * @param dev         Device name
>>> + * @param pool        Associated Packet Pool
>>> + * @param pktin[out]  Packet input queue
>>> + * @param pktout[out] Packet output queue
>>> + *
>>> + * @return The handle of the created pktio object.
>>> + */
>>> +static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
>>> +				odp_pktin_queue_t *pktin,
>>> +				odp_pktout_queue_t *pktout)
>>> +{
>>> +	odp_pktio_param_t pktio_param;
>>> +	odp_pktin_queue_param_t in_queue_param;
>>> +	odp_pktout_queue_param_t out_queue_param;
>>> +	odp_pktio_t pktio;
>>> +
>>> +	odp_pktio_param_init(&pktio_param);
>>> +
>>> +	pktio = odp_pktio_open(name, pool, &pktio_param);
>>> +	if (pktio == ODP_PKTIO_INVALID) {
>>> +		printf("Error: failed to open %s\n", name);
>>> +		exit(1);
>>> +	}
>>> +	/* Configure one input queue */
>>> +	odp_pktin_queue_param_init(&in_queue_param);
>>> +	in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
>>> +	in_queue_param.num_queues = 1;
>>> +	if (odp_pktin_queue_config(pktio, &in_queue_param)) {
>>> +		printf("Error: failed to config input queue for %s\n", name);
>>> +		exit(1);
>>> +	}
>>> +	/* Configure one output queue */
>>> +	odp_pktout_queue_param_init(&out_queue_param);
>>> +	out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
>>> +	out_queue_param.num_queues = 1;
>>> +	if (odp_pktout_queue_config(pktio, &out_queue_param)) {
>>> +		printf("Error: failed to config output queue for %s\n", name);
>>> +		exit(1);
>>> +	}
>>> +	/* Save input and output queue handles */
>>> +	if (odp_pktin_queue(pktio, pktin, 1) != 1) {
>>> +		printf("Error: pktin queue query failed for %s\n", name);
>>> +		exit(1);
>>> +	}
>>> +	if (odp_pktout_queue(pktio, pktout, 1) != 1) {
>>> +		printf("Error: pktout queue query failed for %s\n", name);
>>> +		exit(1);
>>> +	}
>>> +	return pktio;
>>> +}
>>> +
>>> +/**
>>> + * Initialize echo packet headers
>>> + *
>>> + * @param pkt  Packet handle
>>> + *
>>> + * @retval 0 on success
>>> + * @retval <0 on failure
>>> + */
>>> +static uint32_t echo_packet_init(odp_packet_t pkt)
>>> +{
>>> +	odph_ethhdr_t *eth;
>>> +	echo_hdr_t echo;
>>> +	static uint32_t seq_num;
>>> +
>>> +	/* Ethernet headers */
>>> +	eth = odp_packet_data(pkt);
>>> +	eth->src = global.src;
>>> +	eth->dst = global.appl.dst;
>>> +	eth->type = odp_cpu_to_be_16(odp_packet_len(pkt) -
>> ODPH_ETHHDR_LEN);
>>> +
>>> +	/* Echo headers */
>>> +	echo.llc = 0; /* NULL DSAP, SSAP and control fields */
>>> +	echo.magic = ECHO_MAGIC;
>>> +	echo.seq = seq_num++;
>>> +	echo.ts = odp_time_to_ns(odp_time_global());
>>> +
>>> +	if (odp_packet_copy_from_mem(pkt, ODPH_ETHHDR_LEN, sizeof(echo),
>>> +				     &echo) != 0) {
>>> +		printf("Error: copying echo header failed\n");
>>> +		return -1;
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * Echo header start pointer
>>> + *
>>> + * @param pkt  Packet handle
>>> + *
>>> + * @return  Echo header start pointer
>>> + * @retval  NULL packet does not contain a valid echo header
>>> + */
>>> +static echo_hdr_t *echo_hdr(odp_packet_t pkt)
>>> +{
>>> +	odph_ethhdr_t *eth;
>>> +	echo_hdr_t *echo;
>>> +
>>> +	eth = odp_packet_l2_ptr(pkt, NULL);
>>> +	echo = (echo_hdr_t *)(eth + 1);
>>> +
>>> +	if (echo->magic != ECHO_MAGIC)
>>> +		return NULL;
>>> +
>>> +	return echo;
>>> +}
>>> +
>>> +/**
>>> + * Echo packet responder thread
>>> + */
>>> +static int run_responder(void *arg ODP_UNUSED)
>>> +{
>>> +	odp_packet_t pkt_tbl[global.appl.burst_size];
>>> +	odp_packet_t tx_tbl[global.appl.burst_size];
>>> +	int pkts, sent, tx_drops, i;
>>> +
>>> +	printf("\n*** Echo responder started ***\n\n");
>>> +
>>> +	for (;;) {
>>> +		int num_rx = 0;
>>> +
>>> +		pkts = odp_pktin_recv_tmo(global.pktin, pkt_tbl,
>>> +					  global.appl.burst_size,
>>> +					  global.appl.wait);
>>> +
>>> +		if (odp_unlikely(pkts <= 0))
>>> +			continue;
>>> +		for (i = 0; i < pkts; i++) {
>>> +			odp_packet_t pkt = pkt_tbl[i];
>>> +			odph_ethhdr_t *eth;
>>> +
>>> +			if (odp_unlikely(!odp_packet_has_eth(pkt))) {
>>> +				odp_packet_free(pkt);
>>> +				continue;
>>> +			}
>>> +			eth = odp_packet_l2_ptr(pkt, NULL);
>>> +
>>> +			if (echo_hdr(pkt) == NULL) {
>>> +				odp_packet_free(pkt);
>>> +				continue;
>>> +			}
>>> +
>>> +			/* Swap MAC addresses */
>>> +			eth->dst = eth->src;
>>> +			eth->src = global.src;
>>> +
>>> +			tx_tbl[num_rx++] = pkt;
>>> +		}
>>> +		sent = odp_pktout_send(global.pktout, tx_tbl, num_rx);
>>> +		if (odp_unlikely(sent < 0))
>>> +			sent = 0;
>>> +		tx_drops = num_rx - sent;
>>> +		if (odp_unlikely(tx_drops))
>>> +			odp_packet_free_multi(&tx_tbl[sent], tx_drops);
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * Echo packet transmitter thread
>>> + */
>>> +static int run_transmitter(void *arg ODP_UNUSED)
>>> +{
>>> +	for (;;) {
>>> +		odp_packet_t pkt;
>>> +		int sent;
>>> +
>>> +		pkt = odp_packet_alloc(global.pool, ECHO_PACKET_LEN);
>>> +		if (pkt == ODP_PACKET_INVALID)
>>> +			continue;
>>> +
>>> +		if (echo_packet_init(pkt)) {
>>> +			odp_packet_free(pkt);
>>> +			continue;
>>> +		}
>>> +
>>> +		sent = odp_pktout_send(global.pktout, &pkt, 1);
>>> +		if (odp_unlikely(sent != 1))
>>> +			odp_packet_free(pkt);
>>> +
>>> +		odp_time_wait_ns(ECHO_TX_INTERVAL);
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * Echo packet receiver thread
>>> + */
>>> +static int run_receiver(void *arg ODP_UNUSED)
>>> +{
>>> +	odp_packet_t pkt_tbl[global.appl.burst_size];
>>> +	int pkts, i;
>>> +
>>> +	for (;;) {
>>> +		uint64_t ts;
>>> +
>>> +		pkts = odp_pktin_recv_tmo(global.pktin, pkt_tbl,
>>> +					  global.appl.burst_size,
>>> +					  global.appl.wait);
>>> +
>>> +		if (odp_unlikely(pkts <= 0))
>>> +			continue;
>>> +
>>> +		ts = odp_time_to_ns(odp_time_global());
>>> +
>>> +		for (i = 0; i < pkts; i++) {
>>> +			odp_packet_t pkt = pkt_tbl[i];
>>> +			odph_ethhdr_t *eth;
>>> +			odph_ethaddr_t *src;
>>> +			echo_hdr_t *echo;
>>> +			double delay;
>>> +
>>> +			if (odp_unlikely(!odp_packet_has_eth(pkt)))
>>> +				continue;
>>> +			eth = odp_packet_l2_ptr(pkt, NULL);
>>> +
>>> +			echo = echo_hdr(pkt);
>>> +			if (!echo)
>>> +				continue;
>>> +
>>> +			delay = (double)(ts - echo->ts) /
>> ODP_TIME_MSEC_IN_NS;
>>> +			src = &eth->src;
>>> +
>>> +			printf("%" PRIu32 " bytes from "
>>> +			       "%02x:%02x:%02x:%02x:%02x:%02x: seq=%" PRIu32 "
>> "
>>> +			       "time=%0.3f ms\n", odp_packet_len(pkt),
>>> +			       src->addr[0], src->addr[1], src->addr[2],
>>> +			       src->addr[3], src->addr[4], src->addr[5],
>>> +			       echo->seq, delay);
>>> +		}
>>> +		odp_packet_free_multi(pkt_tbl, pkts);
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * Print usage information
>>> + */
>>> +static void usage(char *progname)
>>> +{
>>> +	printf("\n"
>>> +	       "OpenDataPlane echo example application.\n"
>>> +	       "\n"
>>> +	       "Usage: %s OPTIONS\n"
>>> +	       "  E.g. host1$ %s -m 0 -i eth0 -d 02:01:02:03:04:05\n"
>>> +	       "       host2$ %s -m 1 -i eth1\n\n"
>>> +	       " In the above example,\n"
>>> +	       " host1 sends echo packets from eth0 to the MAC address of\n"
>>> +	       " host2's eth1 and waits for responses. Host2 waits for echo\n"
>>> +	       " packets from eth1 and resends them back to the sender.\n"
>>> +	       "\n"
>>> +	       "Mandatory OPTIONS:\n"
>>> +	       "  -i, --interface   Eth interface\n"
>>> +	       "  -d, --destination Destination MAC address (required in transmitter
>> mode)\n"
>>> +	       "\n"
>>> +	       "Optional OPTIONS:\n"
>>> +	       "  -m, --mode        0: Transmitter (default)\n"
>>> +	       "                    1: Responder\n"
>>> +	       "  -n, --no-wait     Busy wait new packets\n"
>>> +	       "  -b, --burst       Receive burst size\n"
>>> +	       "  -h, --help        Display help and exit.\n\n"
>>> +	       "\n", NO_PATH(progname), NO_PATH(progname),
>> NO_PATH(progname));
>>> +}
>>> +
>>> +/**
>>> + * Parse and store the command line arguments
>>> + *
>>> + * @param argc       argument count
>>> + * @param argv[]     argument vector
>>> + * @param appl_args  Store application arguments here
>>> + */
>>> +static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
>>> +{
>>> +	int opt;
>>> +	int long_index;
>>> +	size_t len;
>>> +	int i;
>>> +	odp_bool_t mac_set = 0;
>>> +	static const char *shortopts =  "+m:i:d:b:nh";
>>> +	static const struct option longopts[] = {
>>> +		{"mode", required_argument, NULL, 'm'},
>>> +		{"interface", required_argument, NULL, 'i'},
>>> +		{"destination", required_argument, NULL, 'd'},
>>> +		{"burst", required_argument, NULL, 'b'},
>>> +		{"no-wait", no_argument, NULL, 'n'},
>>> +		{"help", no_argument, NULL, 'h'},
>>> +		{NULL, 0, NULL, 0}
>>> +	};
>>> +
>>> +	appl_args->if_str = NULL;
>>> +	appl_args->burst_size = 1;
>>> +	appl_args->wait = ODP_PKTIN_WAIT;
>>> +
>>> +	/* let helper collect its own arguments (e.g. --odph_proc) */
>>> +	odph_parse_options(argc, argv, shortopts, longopts);
>>> +
>>> +	opterr = 0; /* do not issue errors on helper options */
>>> +
>>> +	while (1) {
>>> +		opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
>>> +
>>> +		if (opt == -1)
>>> +			break;	/* No more options */
>>> +
>>> +		switch (opt) {
>>> +		case 'm':
>>> +			i = atoi(optarg);
>>> +			if (i == 1)
>>> +				appl_args->mode = RESPOND;
>>> +			else
>>> +				appl_args->mode = TRANSMIT;
>>> +			break;
>>> +		case 'i':
>>> +			len = strlen(optarg);
>>> +			if (len == 0) {
>>> +				usage(argv[0]);
>>> +				exit(EXIT_FAILURE);
>>> +			}
>>> +			len += 1;	/* add room for '\0' */
>>> +
>>> +			appl_args->if_str = malloc(len);
>>> +			if (appl_args->if_str == NULL) {
>>> +				usage(argv[0]);
>>> +				exit(EXIT_FAILURE);
>>> +			}
>>> +			strcpy(appl_args->if_str, optarg);
>>> +			break;
>>> +		case 'd':
>>> +			if (odph_eth_addr_parse(&appl_args->dst, optarg)) {
>>> +				usage(argv[0]);
>>> +				exit(EXIT_FAILURE);
>>> +			}
>>> +			mac_set = 1;
>>> +
>>> +			break;
>>> +		case 'b':
>>> +			i = atoi(optarg);
>>> +			if (i < 1)
>>> +				appl_args->burst_size = 1;
>>> +			else
>>> +				appl_args->burst_size = i;
>>> +			break;
>>> +		case 'n':
>>> +			appl_args->wait = ODP_PKTIN_NO_WAIT;
>>> +			break;
>>> +		case 'h':
>>> +			usage(argv[0]);
>>> +			exit(EXIT_SUCCESS);
>>> +			break;
>>> +		default:
>>> +			break;
>>> +		}
>>> +	}
>>> +
>>> +	if (appl_args->if_str == NULL ||
>>> +	    (appl_args->mode == TRANSMIT && !mac_set)) {
>>> +		usage(argv[0]);
>>> +		exit(EXIT_FAILURE);
>>> +	}
>>> +
>>> +	optind = 1;		/* reset 'extern optind' from the getopt lib */
>>> +}
>>> +
>>> +int main(int argc, char **argv)
>>> +{
>>> +	odph_odpthread_t thread_tbl[MAX_WORKERS];
>>> +	odp_pool_param_t params;
>>> +	odp_cpumask_t cpumask;
>>> +	odp_instance_t instance;
>>> +	int i;
>>> +	int cpu;
>>> +	int num_workers;
>>> +	int req_workers;
>>> +
>>> +	/* Initialize ODP */
>>> +	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);
>>> +	}
>>> +
>>> +	/* Parse and store the application arguments */
>>> +	parse_args(argc, argv, &global.appl);
>>> +
>>> +	/* 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;
>>> +
>>> +	global.pool = odp_pool_create("packet pool", &params);
>>> +
>>> +	if (global.pool == ODP_POOL_INVALID) {
>>> +		printf("Error: packet pool create failed.\n");
>>> +		exit(1);
>>> +	}
>>> +
>>> +	/* Create and configure pktio device */
>>> +	global.iface = create_pktio(global.appl.if_str, global.pool,
>>> +				    &global.pktin, &global.pktout);
>>> +
>>> +	/* Save interface ethernet address */
>>> +	if (odp_pktio_mac_addr(global.iface, &global.src,
>>> +			       ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
>>> +		printf("Error: interface ethernet address unknown\n");
>>> +		exit(EXIT_FAILURE);
>>> +	}
>>> +
>>> +	if (odp_pktio_start(global.iface)) {
>>> +		printf("Error: unable to start input interface\n");
>>> +		exit(1);
>>> +	}
>>> +
>>> +	/* Create and bind worker threads */
>>> +	req_workers = global.appl.mode == TRANSMIT ? 2 : 1;
>>> +	num_workers = odp_cpumask_default_worker(&cpumask,
>> MAX_WORKERS);
>>> +
>>> +	if (num_workers < req_workers) {
>>> +		printf("Error: not enough workers (%d available, %d required)\n",
>>> +		       num_workers, req_workers);
>>> +		exit(1);
>>> +	}
>>> +
>>> +	cpu = odp_cpumask_first(&cpumask);
>>> +	for (i = 0; i < req_workers; i++) {
>>> +		odp_cpumask_t thd_mask;
>>> +		odph_odpthread_params_t thr_params;
>>> +
>>> +		memset(&thr_params, 0, sizeof(thr_params));
>>> +
>>> +		if (global.appl.mode == RESPOND)
>>> +			thr_params.start = run_responder;
>>> +		else
>>> +			thr_params.start = i % 2 ? run_transmitter :
>>> +					run_receiver;
>>> +
>>> +		thr_params.arg      = NULL;
>>> +		thr_params.thr_type = ODP_THREAD_WORKER;
>>> +		thr_params.instance = instance;
>>> +
>>> +		odp_cpumask_zero(&thd_mask);
>>> +		odp_cpumask_set(&thd_mask, cpu);
>>> +		odph_odpthreads_create(&thread_tbl[i], &thd_mask,
>>> +				       &thr_params);
>>> +		cpu = odp_cpumask_next(&cpumask, cpu);
>>> +	}
>>> +
>>> +	odph_odpthreads_join(thread_tbl);
>>> +
>>> +	return 0;
>>> +}
>>> diff --git a/example/m4/configure.m4 b/example/m4/configure.m4
>>> index 9731d81..d54ea72 100644
>>> --- a/example/m4/configure.m4
>>> +++ b/example/m4/configure.m4
>>> @@ -19,4 +19,5 @@ AC_CONFIG_FILES([example/classifier/Makefile
>>>    		 example/timer/Makefile
>>>    		 example/traffic_mgmt/Makefile
>>>    		 example/l2fwd_simple/Makefile
>>> -		 example/switch/Makefile])
>>> +		 example/switch/Makefile
>>> +		 example/echo/Makefile])
>> _______________________________________________
>> lng-odp mailing list
>> lng-odp@lists.linaro.org
>> https://lists.linaro.org/mailman/listinfo/lng-odp
diff mbox

Patch

diff --git a/example/Makefile.am b/example/Makefile.am
index 7f82c4d..ab37f21 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -1 +1 @@ 
-SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt l2fwd_simple switch
+SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt l2fwd_simple switch echo
diff --git a/example/echo/.gitignore b/example/echo/.gitignore
new file mode 100644
index 0000000..b1e5eb5
--- /dev/null
+++ b/example/echo/.gitignore
@@ -0,0 +1 @@ 
+odp_echo
diff --git a/example/echo/Makefile.am b/example/echo/Makefile.am
new file mode 100644
index 0000000..323522f
--- /dev/null
+++ b/example/echo/Makefile.am
@@ -0,0 +1,10 @@ 
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_echo$(EXEEXT)
+odp_echo_LDFLAGS = $(AM_LDFLAGS) -static
+odp_echo_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+
+noinst_HEADERS = \
+		  $(top_srcdir)/example/example_debug.h
+
+dist_odp_echo_SOURCES = odp_echo.c
diff --git a/example/echo/odp_echo.c b/example/echo/odp_echo.c
new file mode 100644
index 0000000..cc966e1
--- /dev/null
+++ b/example/echo/odp_echo.c
@@ -0,0 +1,528 @@ 
+/* Copyright (c) 2016, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/linux.h>
+#include <odp/helper/eth.h>
+#include <odp/helper/ip.h>
+
+#define POOL_NUM_PKT 256
+#define POOL_SEG_LEN 1856
+
+#define MAX_WORKERS 2
+
+#define ECHO_PACKET_LEN 64
+#define ECHO_MAGIC 0x92749451
+#define ECHO_TX_INTERVAL ODP_TIME_SEC_IN_NS
+
+/**
+ * Application mode
+ */
+typedef enum appl_mode_t {
+	TRANSMIT,
+	RESPOND
+} appl_mode_t;
+
+/** Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+			    strrchr((file_name), '/') + 1 : (file_name))
+
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+	char *if_str;		/**< Storage for interface name */
+	odph_ethaddr_t dst;	/**< Destination MAC address */
+	appl_mode_t mode;	/**< Application mode */
+	uint64_t wait;		/**< Wait new packets */
+	int burst_size;		/**< Packet burst size */
+} appl_args_t;
+
+/**
+ * Global application data
+ */
+struct {
+	appl_args_t appl;	   /**< Parsed application arguments */
+	odp_pktio_t iface;	   /**< Pktio handle */
+	odph_ethaddr_t src;	   /**< Source MAC address */
+	odp_pktin_queue_t pktin;   /**< Packet input queue */
+	odp_pktout_queue_t pktout; /**< packet output queue */
+	odp_pool_t pool;	   /**< Packet pool */
+} global;
+
+/**
+ * Echo packet header
+ */
+typedef struct ODP_PACKED {
+	uint32_t llc;		/**< LLC header */
+	uint32_t magic;		/**< Magic number for verifying echo packets */
+	uint32_t seq;		/**< Sequence number */
+	uint64_t ts;		/**< Timestamp (ns) */
+} echo_hdr_t;
+
+/**
+ * Create a pktio handle
+ *
+ * @param dev         Device name
+ * @param pool        Associated Packet Pool
+ * @param pktin[out]  Packet input queue
+ * @param pktout[out] Packet output queue
+ *
+ * @return The handle of the created pktio object.
+ */
+static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
+				odp_pktin_queue_t *pktin,
+				odp_pktout_queue_t *pktout)
+{
+	odp_pktio_param_t pktio_param;
+	odp_pktin_queue_param_t in_queue_param;
+	odp_pktout_queue_param_t out_queue_param;
+	odp_pktio_t pktio;
+
+	odp_pktio_param_init(&pktio_param);
+
+	pktio = odp_pktio_open(name, pool, &pktio_param);
+	if (pktio == ODP_PKTIO_INVALID) {
+		printf("Error: failed to open %s\n", name);
+		exit(1);
+	}
+	/* Configure one input queue */
+	odp_pktin_queue_param_init(&in_queue_param);
+	in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+	in_queue_param.num_queues = 1;
+	if (odp_pktin_queue_config(pktio, &in_queue_param)) {
+		printf("Error: failed to config input queue for %s\n", name);
+		exit(1);
+	}
+	/* Configure one output queue */
+	odp_pktout_queue_param_init(&out_queue_param);
+	out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+	out_queue_param.num_queues = 1;
+	if (odp_pktout_queue_config(pktio, &out_queue_param)) {
+		printf("Error: failed to config output queue for %s\n", name);
+		exit(1);
+	}
+	/* Save input and output queue handles */
+	if (odp_pktin_queue(pktio, pktin, 1) != 1) {
+		printf("Error: pktin queue query failed for %s\n", name);
+		exit(1);
+	}
+	if (odp_pktout_queue(pktio, pktout, 1) != 1) {
+		printf("Error: pktout queue query failed for %s\n", name);
+		exit(1);
+	}
+	return pktio;
+}
+
+/**
+ * Initialize echo packet headers
+ *
+ * @param pkt  Packet handle
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+static uint32_t echo_packet_init(odp_packet_t pkt)
+{
+	odph_ethhdr_t *eth;
+	echo_hdr_t echo;
+	static uint32_t seq_num;
+
+	/* Ethernet headers */
+	eth = odp_packet_data(pkt);
+	eth->src = global.src;
+	eth->dst = global.appl.dst;
+	eth->type = odp_cpu_to_be_16(odp_packet_len(pkt) - ODPH_ETHHDR_LEN);
+
+	/* Echo headers */
+	echo.llc = 0; /* NULL DSAP, SSAP and control fields */
+	echo.magic = ECHO_MAGIC;
+	echo.seq = seq_num++;
+	echo.ts = odp_time_to_ns(odp_time_global());
+
+	if (odp_packet_copy_from_mem(pkt, ODPH_ETHHDR_LEN, sizeof(echo),
+				     &echo) != 0) {
+		printf("Error: copying echo header failed\n");
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ * Echo header start pointer
+ *
+ * @param pkt  Packet handle
+ *
+ * @return  Echo header start pointer
+ * @retval  NULL packet does not contain a valid echo header
+ */
+static echo_hdr_t *echo_hdr(odp_packet_t pkt)
+{
+	odph_ethhdr_t *eth;
+	echo_hdr_t *echo;
+
+	eth = odp_packet_l2_ptr(pkt, NULL);
+	echo = (echo_hdr_t *)(eth + 1);
+
+	if (echo->magic != ECHO_MAGIC)
+		return NULL;
+
+	return echo;
+}
+
+/**
+ * Echo packet responder thread
+ */
+static int run_responder(void *arg ODP_UNUSED)
+{
+	odp_packet_t pkt_tbl[global.appl.burst_size];
+	odp_packet_t tx_tbl[global.appl.burst_size];
+	int pkts, sent, tx_drops, i;
+
+	printf("\n*** Echo responder started ***\n\n");
+
+	for (;;) {
+		int num_rx = 0;
+
+		pkts = odp_pktin_recv_tmo(global.pktin, pkt_tbl,
+					  global.appl.burst_size,
+					  global.appl.wait);
+
+		if (odp_unlikely(pkts <= 0))
+			continue;
+		for (i = 0; i < pkts; i++) {
+			odp_packet_t pkt = pkt_tbl[i];
+			odph_ethhdr_t *eth;
+
+			if (odp_unlikely(!odp_packet_has_eth(pkt))) {
+				odp_packet_free(pkt);
+				continue;
+			}
+			eth = odp_packet_l2_ptr(pkt, NULL);
+
+			if (echo_hdr(pkt) == NULL) {
+				odp_packet_free(pkt);
+				continue;
+			}
+
+			/* Swap MAC addresses */
+			eth->dst = eth->src;
+			eth->src = global.src;
+
+			tx_tbl[num_rx++] = pkt;
+		}
+		sent = odp_pktout_send(global.pktout, tx_tbl, num_rx);
+		if (odp_unlikely(sent < 0))
+			sent = 0;
+		tx_drops = num_rx - sent;
+		if (odp_unlikely(tx_drops))
+			odp_packet_free_multi(&tx_tbl[sent], tx_drops);
+	}
+	return 0;
+}
+
+/**
+ * Echo packet transmitter thread
+ */
+static int run_transmitter(void *arg ODP_UNUSED)
+{
+	for (;;) {
+		odp_packet_t pkt;
+		int sent;
+
+		pkt = odp_packet_alloc(global.pool, ECHO_PACKET_LEN);
+		if (pkt == ODP_PACKET_INVALID)
+			continue;
+
+		if (echo_packet_init(pkt)) {
+			odp_packet_free(pkt);
+			continue;
+		}
+
+		sent = odp_pktout_send(global.pktout, &pkt, 1);
+		if (odp_unlikely(sent != 1))
+			odp_packet_free(pkt);
+
+		odp_time_wait_ns(ECHO_TX_INTERVAL);
+	}
+	return 0;
+}
+
+/**
+ * Echo packet receiver thread
+ */
+static int run_receiver(void *arg ODP_UNUSED)
+{
+	odp_packet_t pkt_tbl[global.appl.burst_size];
+	int pkts, i;
+
+	for (;;) {
+		uint64_t ts;
+
+		pkts = odp_pktin_recv_tmo(global.pktin, pkt_tbl,
+					  global.appl.burst_size,
+					  global.appl.wait);
+
+		if (odp_unlikely(pkts <= 0))
+			continue;
+
+		ts = odp_time_to_ns(odp_time_global());
+
+		for (i = 0; i < pkts; i++) {
+			odp_packet_t pkt = pkt_tbl[i];
+			odph_ethhdr_t *eth;
+			odph_ethaddr_t *src;
+			echo_hdr_t *echo;
+			double delay;
+
+			if (odp_unlikely(!odp_packet_has_eth(pkt)))
+				continue;
+			eth = odp_packet_l2_ptr(pkt, NULL);
+
+			echo = echo_hdr(pkt);
+			if (!echo)
+				continue;
+
+			delay = (double)(ts - echo->ts) / ODP_TIME_MSEC_IN_NS;
+			src = &eth->src;
+
+			printf("%" PRIu32 " bytes from "
+			       "%02x:%02x:%02x:%02x:%02x:%02x: seq=%" PRIu32 " "
+			       "time=%0.3f ms\n", odp_packet_len(pkt),
+			       src->addr[0], src->addr[1], src->addr[2],
+			       src->addr[3], src->addr[4], src->addr[5],
+			       echo->seq, delay);
+		}
+		odp_packet_free_multi(pkt_tbl, pkts);
+	}
+	return 0;
+}
+
+/**
+ * Print usage information
+ */
+static void usage(char *progname)
+{
+	printf("\n"
+	       "OpenDataPlane echo example application.\n"
+	       "\n"
+	       "Usage: %s OPTIONS\n"
+	       "  E.g. host1$ %s -m 0 -i eth0 -d 02:01:02:03:04:05\n"
+	       "       host2$ %s -m 1 -i eth1\n\n"
+	       " In the above example,\n"
+	       " host1 sends echo packets from eth0 to the MAC address of\n"
+	       " host2's eth1 and waits for responses. Host2 waits for echo\n"
+	       " packets from eth1 and resends them back to the sender.\n"
+	       "\n"
+	       "Mandatory OPTIONS:\n"
+	       "  -i, --interface   Eth interface\n"
+	       "  -d, --destination Destination MAC address (required in transmitter mode)\n"
+	       "\n"
+	       "Optional OPTIONS:\n"
+	       "  -m, --mode        0: Transmitter (default)\n"
+	       "                    1: Responder\n"
+	       "  -n, --no-wait     Busy wait new packets\n"
+	       "  -b, --burst       Receive burst size\n"
+	       "  -h, --help        Display help and exit.\n\n"
+	       "\n", NO_PATH(progname), NO_PATH(progname), NO_PATH(progname));
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc       argument count
+ * @param argv[]     argument vector
+ * @param appl_args  Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+	int opt;
+	int long_index;
+	size_t len;
+	int i;
+	odp_bool_t mac_set = 0;
+	static const char *shortopts =  "+m:i:d:b:nh";
+	static const struct option longopts[] = {
+		{"mode", required_argument, NULL, 'm'},
+		{"interface", required_argument, NULL, 'i'},
+		{"destination", required_argument, NULL, 'd'},
+		{"burst", required_argument, NULL, 'b'},
+		{"no-wait", no_argument, NULL, 'n'},
+		{"help", no_argument, NULL, 'h'},
+		{NULL, 0, NULL, 0}
+	};
+
+	appl_args->if_str = NULL;
+	appl_args->burst_size = 1;
+	appl_args->wait = ODP_PKTIN_WAIT;
+
+	/* let helper collect its own arguments (e.g. --odph_proc) */
+	odph_parse_options(argc, argv, shortopts, longopts);
+
+	opterr = 0; /* do not issue errors on helper options */
+
+	while (1) {
+		opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+		if (opt == -1)
+			break;	/* No more options */
+
+		switch (opt) {
+		case 'm':
+			i = atoi(optarg);
+			if (i == 1)
+				appl_args->mode = RESPOND;
+			else
+				appl_args->mode = TRANSMIT;
+			break;
+		case 'i':
+			len = strlen(optarg);
+			if (len == 0) {
+				usage(argv[0]);
+				exit(EXIT_FAILURE);
+			}
+			len += 1;	/* add room for '\0' */
+
+			appl_args->if_str = malloc(len);
+			if (appl_args->if_str == NULL) {
+				usage(argv[0]);
+				exit(EXIT_FAILURE);
+			}
+			strcpy(appl_args->if_str, optarg);
+			break;
+		case 'd':
+			if (odph_eth_addr_parse(&appl_args->dst, optarg)) {
+				usage(argv[0]);
+				exit(EXIT_FAILURE);
+			}
+			mac_set = 1;
+
+			break;
+		case 'b':
+			i = atoi(optarg);
+			if (i < 1)
+				appl_args->burst_size = 1;
+			else
+				appl_args->burst_size = i;
+			break;
+		case 'n':
+			appl_args->wait = ODP_PKTIN_NO_WAIT;
+			break;
+		case 'h':
+			usage(argv[0]);
+			exit(EXIT_SUCCESS);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (appl_args->if_str == NULL ||
+	    (appl_args->mode == TRANSMIT && !mac_set)) {
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	optind = 1;		/* reset 'extern optind' from the getopt lib */
+}
+
+int main(int argc, char **argv)
+{
+	odph_odpthread_t thread_tbl[MAX_WORKERS];
+	odp_pool_param_t params;
+	odp_cpumask_t cpumask;
+	odp_instance_t instance;
+	int i;
+	int cpu;
+	int num_workers;
+	int req_workers;
+
+	/* Initialize ODP */
+	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);
+	}
+
+	/* Parse and store the application arguments */
+	parse_args(argc, argv, &global.appl);
+
+	/* 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;
+
+	global.pool = odp_pool_create("packet pool", &params);
+
+	if (global.pool == ODP_POOL_INVALID) {
+		printf("Error: packet pool create failed.\n");
+		exit(1);
+	}
+
+	/* Create and configure pktio device */
+	global.iface = create_pktio(global.appl.if_str, global.pool,
+				    &global.pktin, &global.pktout);
+
+	/* Save interface ethernet address */
+	if (odp_pktio_mac_addr(global.iface, &global.src,
+			       ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
+		printf("Error: interface ethernet address unknown\n");
+		exit(EXIT_FAILURE);
+	}
+
+	if (odp_pktio_start(global.iface)) {
+		printf("Error: unable to start input interface\n");
+		exit(1);
+	}
+
+	/* Create and bind worker threads */
+	req_workers = global.appl.mode == TRANSMIT ? 2 : 1;
+	num_workers = odp_cpumask_default_worker(&cpumask, MAX_WORKERS);
+
+	if (num_workers < req_workers) {
+		printf("Error: not enough workers (%d available, %d required)\n",
+		       num_workers, req_workers);
+		exit(1);
+	}
+
+	cpu = odp_cpumask_first(&cpumask);
+	for (i = 0; i < req_workers; i++) {
+		odp_cpumask_t thd_mask;
+		odph_odpthread_params_t thr_params;
+
+		memset(&thr_params, 0, sizeof(thr_params));
+
+		if (global.appl.mode == RESPOND)
+			thr_params.start = run_responder;
+		else
+			thr_params.start = i % 2 ? run_transmitter :
+					run_receiver;
+
+		thr_params.arg      = NULL;
+		thr_params.thr_type = ODP_THREAD_WORKER;
+		thr_params.instance = instance;
+
+		odp_cpumask_zero(&thd_mask);
+		odp_cpumask_set(&thd_mask, cpu);
+		odph_odpthreads_create(&thread_tbl[i], &thd_mask,
+				       &thr_params);
+		cpu = odp_cpumask_next(&cpumask, cpu);
+	}
+
+	odph_odpthreads_join(thread_tbl);
+
+	return 0;
+}
diff --git a/example/m4/configure.m4 b/example/m4/configure.m4
index 9731d81..d54ea72 100644
--- a/example/m4/configure.m4
+++ b/example/m4/configure.m4
@@ -19,4 +19,5 @@  AC_CONFIG_FILES([example/classifier/Makefile
 		 example/timer/Makefile
 		 example/traffic_mgmt/Makefile
 		 example/l2fwd_simple/Makefile
-		 example/switch/Makefile])
+		 example/switch/Makefile
+		 example/echo/Makefile])