aboutsummaryrefslogtreecommitdiff
path: root/example
diff options
context:
space:
mode:
authorMike Holmes <mike.holmes@linaro.org>2014-07-22 17:02:31 -0400
committerMaxim Uvarov <maxim.uvarov@linaro.org>2014-07-25 10:17:42 +0400
commit49dc95b655059df7e5fbd9b319c19b2863fdb500 (patch)
tree1e8b861fcafaa4f6f0d72658b781f5fc1134bc3b /example
parent16e10e1e354e8c771bbd4da1a07f23e8983eea1c (diff)
Move ODP examples from under tests
The expectations of example code vs testing code is significantly different. This difference is more easily managed if tests and examples have their own root directory especially as the number of validation tests is increased for the ODP 1.0 release. For example test cases may be significantly more complex than a good example might be expected to be. In addition tests are not expected to have Doxygen documentation and tests will not be expected to be coherent, they will test isolated APIs and to do so may perform illegal combinations of calls making them poor examples. Signed-off-by: Mike Holmes <mike.holmes@linaro.org> Signed-off-by: Anders Roxell <anders.roxell@linaro.org> Reviewed-and-Tested-by: Ciprian Barbu <ciprian.barbu@linaro.org>
Diffstat (limited to 'example')
-rw-r--r--example/Makefile.am1
-rw-r--r--example/Makefile.inc10
-rw-r--r--example/README3
-rw-r--r--example/generator/Makefile.am5
-rw-r--r--example/generator/odp_generator.c923
-rw-r--r--example/l2fwd/Makefile.am5
-rw-r--r--example/l2fwd/odp_l2fwd.c637
-rw-r--r--example/odp_example/Makefile.am5
-rw-r--r--example/odp_example/odp_example.c1062
-rw-r--r--example/packet/Makefile.am5
-rw-r--r--example/packet/odp_pktio.c635
-rw-r--r--example/packet_netmap/Makefile.am7
-rw-r--r--example/packet_netmap/odp_pktio_netmap.c557
-rw-r--r--example/timer/Makefile.am5
-rw-r--r--example/timer/odp_timer_test.c329
15 files changed, 4189 insertions, 0 deletions
diff --git a/example/Makefile.am b/example/Makefile.am
new file mode 100644
index 000000000..01a330578
--- /dev/null
+++ b/example/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = generator l2fwd odp_example packet packet_netmap timer
diff --git a/example/Makefile.inc b/example/Makefile.inc
new file mode 100644
index 000000000..b549001fc
--- /dev/null
+++ b/example/Makefile.inc
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.inc
+LIB = $(top_builddir)/lib
+LDADD = $(LIB)/libodp.la
+AM_CFLAGS += \
+ -I$(srcdir) \
+ -I$(top_srcdir)/platform/@with_platform@/include/api \
+ -I$(top_srcdir)/platform/linux-generic/include/api \
+ -I$(top_srcdir)/include
+
+AM_LDFLAGS += -L$(LIB)
diff --git a/example/README b/example/README
new file mode 100644
index 000000000..3f8596981
--- /dev/null
+++ b/example/README
@@ -0,0 +1,3 @@
+Files in this directory are intended to be part of the ODP documentation.
+They should not introduce any Doxygen warnings or errors and will be
+part of the generated documentation.
diff --git a/example/generator/Makefile.am b/example/generator/Makefile.am
new file mode 100644
index 000000000..d3bd5bfb7
--- /dev/null
+++ b/example/generator/Makefile.am
@@ -0,0 +1,5 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_generator
+
+dist_odp_generator_SOURCES = odp_generator.c
diff --git a/example/generator/odp_generator.c b/example/generator/odp_generator.c
new file mode 100644
index 000000000..e4a72fad8
--- /dev/null
+++ b/example/generator/odp_generator.c
@@ -0,0 +1,923 @@
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * @example odp_generator.c ODP loopback demo application
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <odp.h>
+#include <odp_packet_io.h>
+#include <helper/odp_linux.h>
+#include <helper/odp_packet_helper.h>
+#include <helper/odp_eth.h>
+#include <helper/odp_ip.h>
+#include <helper/odp_udp.h>
+#include <helper/odp_icmp.h>
+
+#define MAX_WORKERS 32 /**< max number of works */
+#define SHM_PKT_POOL_SIZE (512*2048) /**< pkt pool size */
+#define SHM_PKT_POOL_BUF_SIZE 1856 /**< pkt pool buf size */
+
+#define APPL_MODE_UDP 0 /**< UDP mode */
+#define APPL_MODE_PING 1 /**< ping mode */
+#define APPL_MODE_RCV 2 /**< receive mode */
+
+/** print appl mode */
+#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
+
+/** Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+ int core_count; /**< system core count */
+ int if_count; /**< Number of interfaces to be used */
+ char **if_names; /**< Array of pointers to interface names */
+ odp_buffer_pool_t pool; /**< Buffer pool for packet IO */
+ odp_ethaddr_t srcmac; /**< src mac addr */
+ odp_ethaddr_t dstmac; /**< dest mac addr */
+ unsigned int srcip; /**< src ip addr */
+ unsigned int dstip; /**< dest ip addr */
+ int mode; /**< work mode */
+ int number; /**< packets number to be sent */
+ int payload; /**< data len */
+ int timeout; /**< wait time */
+ int interval; /**< wait interval ms between sending each packet */
+} appl_args_t;
+
+/**
+ * counters
+*/
+static struct {
+ odp_atomic_u64_t seq; /**< ip seq to be send */
+ odp_atomic_u64_t ip; /**< ip packets */
+ odp_atomic_u64_t udp; /**< udp packets */
+ odp_atomic_u64_t icmp; /**< icmp packets */
+} counters;
+
+/** * Thread specific arguments
+ */
+typedef struct {
+ char *pktio_dev; /**< Interface name to use */
+ odp_buffer_pool_t pool; /**< Buffer pool for packet IO */
+ int mode; /**< Thread mode */
+} thread_args_t;
+
+/**
+ * Grouping of both parsed CL args and thread specific args - alloc together
+ */
+typedef struct {
+ /** Application (parsed) arguments */
+ appl_args_t appl;
+ /** Thread specific arguments */
+ thread_args_t thread[MAX_WORKERS];
+} args_t;
+
+/** Global pointer to args */
+static args_t *args;
+
+/* helper funcs */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
+static void print_info(char *progname, appl_args_t *appl_args);
+static void usage(char *progname);
+static int scan_ip(char *buf, unsigned int *paddr);
+static int scan_mac(char *in, odp_ethaddr_t *des);
+static void tv_sub(struct timeval *recvtime, struct timeval *sendtime);
+
+/**
+ * Scan ip
+ * Parse ip address.
+ *
+ * @param buf ip address string xxx.xxx.xxx.xx
+ * @param paddr ip address for odp_packet
+ * @return 1 success, 0 failed
+*/
+static int scan_ip(char *buf, unsigned int *paddr)
+{
+ int part1, part2, part3, part4;
+ char tail = 0;
+ int field;
+
+ if (buf == NULL)
+ return 0;
+
+ field = sscanf(buf, "%d . %d . %d . %d %c",
+ &part1, &part2, &part3, &part4, &tail);
+
+ if (field < 4 || field > 5) {
+ printf("expect 4 field,get %d/n", field);
+ return 0;
+ }
+
+ if (tail != 0) {
+ printf("ip address mixed with non number/n");
+ return 0;
+ }
+
+ if ((part1 >= 0 && part1 <= 255) && (part2 >= 0 && part2 <= 255) &&
+ (part3 >= 0 && part3 <= 255) && (part4 >= 0 && part4 <= 255)) {
+ if (paddr)
+ *paddr = part1 << 24 | part2 << 16 | part3 << 8 | part4;
+ return 1;
+ } else {
+ printf("not good ip %d:%d:%d:%d/n", part1, part2, part3, part4);
+ }
+
+ return 0;
+}
+
+/**
+ * Scan mac addr form string
+ *
+ * @param in mac string
+ * @param des mac for odp_packet
+ * @return 1 success, 0 failed
+ */
+static int scan_mac(char *in, odp_ethaddr_t *des)
+{
+ int field;
+ int i;
+ unsigned int mac[7];
+
+ field = sscanf(in, "%2x:%2x:%2x:%2x:%2x:%2x",
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
+
+ for (i = 0; i < 6; i++)
+ des->addr[i] = mac[i];
+
+ if (field != 6)
+ return 0;
+ return 1;
+}
+
+/**
+ * set up an udp packet
+ *
+ * @param obuf packet buffer
+*/
+static void pack_udp_pkt(odp_buffer_t obuf)
+{
+ char *buf;
+ int max;
+ odp_packet_t pkt;
+ odp_ethhdr_t *eth;
+ odp_ipv4hdr_t *ip;
+ odp_udphdr_t *udp;
+ unsigned short seq;
+
+ buf = odp_buffer_addr(obuf);
+ if (buf == NULL)
+ return;
+ max = odp_buffer_size(obuf);
+ if (max <= 0)
+ return;
+
+ pkt = odp_packet_from_buffer(obuf);
+ /* ether */
+ odp_packet_set_l2_offset(pkt, 0);
+ eth = (odp_ethhdr_t *)buf;
+ memcpy((char *)eth->src.addr, args->appl.srcmac.addr, ODP_ETHADDR_LEN);
+ memcpy((char *)eth->dst.addr, args->appl.dstmac.addr, ODP_ETHADDR_LEN);
+ eth->type = odp_cpu_to_be_16(ODP_ETHTYPE_IPV4);
+ /* ip */
+ odp_packet_set_l3_offset(pkt, ODP_ETHHDR_LEN);
+ ip = (odp_ipv4hdr_t *)(buf + ODP_ETHHDR_LEN);
+ ip->dst_addr = odp_cpu_to_be_32(args->appl.dstip);
+ ip->src_addr = odp_cpu_to_be_32(args->appl.srcip);
+ ip->ver_ihl = ODP_IPV4 << 4 | ODP_IPV4HDR_IHL_MIN;
+ ip->tot_len = odp_cpu_to_be_16(args->appl.payload + ODP_UDPHDR_LEN +
+ ODP_IPV4HDR_LEN);
+ ip->proto = ODP_IPPROTO_UDP;
+ seq = odp_atomic_fetch_add_u64(&counters.seq, 1) % 0xFFFF;
+ ip->id = odp_cpu_to_be_16(seq);
+ ip->chksum = 0;
+ odp_ipv4_csum_update(pkt);
+ /* udp */
+ odp_packet_set_l4_offset(pkt, ODP_ETHHDR_LEN + ODP_IPV4HDR_LEN);
+ udp = (odp_udphdr_t *)(buf + ODP_ETHHDR_LEN + ODP_IPV4HDR_LEN);
+ udp->src_port = 0;
+ udp->dst_port = 0;
+ udp->length = odp_cpu_to_be_16(args->appl.payload + ODP_UDPHDR_LEN);
+ udp->chksum = 0;
+ udp->chksum = odp_cpu_to_be_16(odp_ipv4_udp_chksum(pkt));
+ odp_packet_set_len(pkt, args->appl.payload + ODP_UDPHDR_LEN +
+ ODP_IPV4HDR_LEN + ODP_ETHHDR_LEN);
+}
+
+/**
+ * Set up an icmp packet
+ *
+ * @param obuf packet buffer
+*/
+static void pack_icmp_pkt(odp_buffer_t obuf)
+{
+ char *buf;
+ int max;
+ odp_packet_t pkt;
+ odp_ethhdr_t *eth;
+ odp_ipv4hdr_t *ip;
+ odp_icmphdr_t *icmp;
+ struct timeval tval;
+ uint8_t *tval_d;
+ unsigned short seq;
+
+ buf = odp_buffer_addr(obuf);
+ if (buf == NULL)
+ return;
+ max = odp_buffer_size(obuf);
+ if (max <= 0)
+ return;
+
+ args->appl.payload = 56;
+ pkt = odp_packet_from_buffer(obuf);
+ /* ether */
+ odp_packet_set_l2_offset(pkt, 0);
+ eth = (odp_ethhdr_t *)buf;
+ memcpy((char *)eth->src.addr, args->appl.srcmac.addr, ODP_ETHADDR_LEN);
+ memcpy((char *)eth->dst.addr, args->appl.dstmac.addr, ODP_ETHADDR_LEN);
+ eth->type = odp_cpu_to_be_16(ODP_ETHTYPE_IPV4);
+ /* ip */
+ odp_packet_set_l3_offset(pkt, ODP_ETHHDR_LEN);
+ ip = (odp_ipv4hdr_t *)(buf + ODP_ETHHDR_LEN);
+ ip->dst_addr = odp_cpu_to_be_32(args->appl.dstip);
+ ip->src_addr = odp_cpu_to_be_32(args->appl.srcip);
+ ip->ver_ihl = ODP_IPV4 << 4 | ODP_IPV4HDR_IHL_MIN;
+ ip->tot_len = odp_cpu_to_be_16(args->appl.payload + ODP_ICMPHDR_LEN +
+ ODP_IPV4HDR_LEN);
+ ip->proto = ODP_IPPROTO_ICMP;
+ seq = odp_atomic_fetch_add_u64(&counters.seq, 1) % 0xffff;
+ ip->id = odp_cpu_to_be_16(seq);
+ ip->chksum = 0;
+ odp_ipv4_csum_update(pkt);
+ /* icmp */
+ icmp = (odp_icmphdr_t *)(buf + ODP_ETHHDR_LEN + ODP_IPV4HDR_LEN);
+ icmp->type = ICMP_ECHO;
+ icmp->code = 0;
+ icmp->un.echo.id = 0;
+ icmp->un.echo.sequence = ip->id;
+ tval_d = (uint8_t *)(buf + ODP_ETHHDR_LEN + ODP_IPV4HDR_LEN +
+ ODP_ICMPHDR_LEN);
+ /* TODO This should be changed to use an
+ * ODP timer API once one exists. */
+ gettimeofday(&tval, NULL);
+ memcpy(tval_d, &tval, sizeof(struct timeval));
+ icmp->chksum = 0;
+ icmp->chksum = odp_chksum(icmp, args->appl.payload +
+ ODP_ICMPHDR_LEN);
+
+ odp_packet_set_len(pkt, args->appl.payload + ODP_ICMPHDR_LEN +
+ ODP_IPV4HDR_LEN + ODP_ETHHDR_LEN);
+}
+
+/**
+ * Packet IO loopback worker thread using ODP queues
+ *
+ * @param arg thread arguments of type 'thread_args_t *'
+ */
+
+static void *gen_send_thread(void *arg)
+{
+ int thr;
+ odp_pktio_t pktio;
+ thread_args_t *thr_args;
+ odp_queue_t outq_def;
+ odp_pktio_params_t params;
+ socket_params_t *sock_params = &params.sock_params;
+
+ odp_buffer_t buf;
+
+ thr = odp_thread_id();
+ thr_args = arg;
+
+ /* Open a packet IO instance for this thread */
+ sock_params->type = 1;
+ pktio = odp_pktio_open(thr_args->pktio_dev, thr_args->pool, &params);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODP_ERR(" [%02i] Error: pktio create failed\n", thr);
+ return NULL;
+ }
+
+ outq_def = odp_pktio_outq_getdef(pktio);
+ if (outq_def == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%02i] Error: def output-Q query\n", thr);
+ return NULL;
+ }
+
+ printf(" [%02i] created mode: SEND\n", thr);
+ for (;;) {
+ int err;
+ buf = odp_buffer_alloc(thr_args->pool);
+ if (!odp_buffer_is_valid(buf)) {
+ ODP_ERR(" [%2i] alloc_single failed\n", thr);
+ return NULL;
+ }
+
+ if (args->appl.mode == APPL_MODE_UDP)
+ pack_udp_pkt(buf);
+ else if (args->appl.mode == APPL_MODE_PING)
+ pack_icmp_pkt(buf);
+
+ err = odp_queue_enq(outq_def, buf);
+ if (err != 0) {
+ ODP_ERR(" [%02i] send pkt err!\n", thr);
+ return NULL;
+ }
+
+ if (args->appl.interval != 0) {
+ printf(" [%02i] send pkt no:%ju seq %ju\n",
+ thr, counters.seq, counters.seq%0xffff);
+ /* TODO use odp timer */
+ usleep(args->appl.interval * 1000);
+ } else {
+ /* TODO maybe need a rating control */
+ /* flood mode use '\r' instead of '\n' */
+ printf(" [%02i] send pkt no:%ju seq %ju\r",
+ thr, counters.seq, counters.seq%0xffff);
+ }
+ if (args->appl.number != -1 && counters.seq
+ >= (unsigned int)args->appl.number) {
+ break;
+ }
+ }
+
+ /* receive number of reply pks until timeout */
+ if (args->appl.mode == APPL_MODE_PING && args->appl.number > 0) {
+ while (args->appl.timeout >= 0) {
+ if (counters.icmp >= (unsigned int)args->appl.number)
+ break;
+ /* TODO use odp timer */
+ sleep(1);
+ args->appl.timeout--;
+ }
+ }
+
+ /* print info */
+ if (args->appl.mode == APPL_MODE_UDP) {
+ printf(" [%02i] total send:%ju\n", thr, counters.seq);
+ } else if (args->appl.mode == APPL_MODE_PING) {
+ printf(" [%02i] total send:%ju,total receiver %ju\n",
+ thr, counters.seq, counters.icmp);
+ }
+ return arg;
+}
+
+/**
+ * Print odp packets
+ *
+ * @param thr worker id
+ * @param pkt_tbl packets to be print
+ * @param len packet number
+ */
+static void print_pkts(int thr, odp_packet_t pkt_tbl[], unsigned len)
+{
+ odp_packet_t pkt;
+ char *buf;
+ odp_ipv4hdr_t *ip;
+ odp_udphdr_t *udp;
+ odp_icmphdr_t *icmp;
+ struct timeval tvrecv;
+ struct timeval tvsend;
+ double rtt;
+ unsigned i;
+ size_t offset;
+ char msg[1024];
+ int rlen;
+ for (i = 0; i < len; ++i) {
+ pkt = pkt_tbl[i];
+ rlen = 0;
+
+ /* only ip pkts */
+ if (!odp_packet_inflag_ipv4(pkt))
+ continue;
+
+ odp_atomic_inc_u64(&counters.ip);
+ rlen += sprintf(msg, "receive Packet proto:IP ");
+ buf = odp_buffer_addr(odp_buffer_from_packet(pkt));
+ ip = (odp_ipv4hdr_t *)(buf + odp_packet_l3_offset(pkt));
+ rlen += sprintf(msg + rlen, "id %d ",
+ odp_be_to_cpu_16(ip->id));
+ offset = odp_packet_l4_offset(pkt);
+
+ /* udp */
+ if (ip->proto == ODP_IPPROTO_UDP) {
+ odp_atomic_inc_u64(&counters.udp);
+ udp = (odp_udphdr_t *)(buf + offset);
+ rlen += sprintf(msg + rlen, "UDP payload %d ",
+ odp_be_to_cpu_16(udp->length) -
+ ODP_UDPHDR_LEN);
+ }
+
+ /* icmp */
+ if (ip->proto == ODP_IPPROTO_ICMP) {
+ icmp = (odp_icmphdr_t *)(buf + offset);
+ /* echo reply */
+ if (icmp->type == ICMP_ECHOREPLY) {
+ odp_atomic_inc_u64(&counters.icmp);
+ memcpy(&tvsend, buf + offset + ODP_ICMPHDR_LEN,
+ sizeof(struct timeval));
+ /* TODO This should be changed to use an
+ * ODP timer API once one exists. */
+ gettimeofday(&tvrecv, NULL);
+ tv_sub(&tvrecv, &tvsend);
+ rtt = tvrecv.tv_sec*1000 + tvrecv.tv_usec/1000;
+ rlen += sprintf(msg + rlen,
+ "ICMP Echo Reply seq %d time %.1f ",
+ odp_be_to_cpu_16(icmp->un.echo.sequence)
+ , rtt);
+ } else if (icmp->type == ICMP_ECHO) {
+ rlen += sprintf(msg + rlen,
+ "Icmp Echo Request");
+ }
+ }
+
+ msg[rlen] = '\0';
+ printf(" [%02i] %s\n", thr, msg);
+ }
+}
+
+/**
+ * Main receive funtion
+ *
+ * @param arg thread arguments of type 'thread_args_t *'
+ */
+static void *gen_recv_thread(void *arg)
+{
+ int thr;
+ odp_pktio_t pktio;
+ thread_args_t *thr_args;
+ odp_queue_t inq_def;
+ odp_pktio_params_t params;
+ char inq_name[ODP_QUEUE_NAME_LEN];
+ odp_queue_param_t qparam;
+ socket_params_t *sock_params = &params.sock_params;
+
+ odp_packet_t pkt;
+ odp_buffer_t buf;
+
+ thr = odp_thread_id();
+ thr_args = arg;
+
+ /* Open a packet IO instance for this thread */
+ sock_params->type = 1;
+ pktio = odp_pktio_open(thr_args->pktio_dev, thr_args->pool, &params);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODP_ERR(" [%02i] Error: pktio create failed\n", thr);
+ return NULL;
+ }
+
+ int ret;
+ qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+ snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def", (int)pktio);
+ inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
+ inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam);
+ if (inq_def == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%02i] Error: pktio queue creation failed\n", thr);
+ return NULL;
+ }
+
+ ret = odp_pktio_inq_setdef(pktio, inq_def);
+ if (ret != 0) {
+ ODP_ERR(" [%02i] Error: default input-Q setup\n", thr);
+ return NULL;
+ }
+
+ printf(" [%02i] created mode: RECEIVE\n", thr);
+ for (;;) {
+ /* Use schedule to get buf from any input queue */
+ buf = odp_schedule(NULL, ODP_SCHED_WAIT);
+
+ pkt = odp_packet_from_buffer(buf);
+ /* Drop packets with errors */
+ if (odp_unlikely(odp_packet_error(pkt))) {
+ odp_packet_free(pkt);
+ continue;
+ }
+
+ print_pkts(thr, &pkt, 1);
+
+ odp_packet_free(pkt);
+ }
+
+ return arg;
+}
+/**
+ * ODP packet example main function
+ */
+int main(int argc, char *argv[])
+{
+ odp_linux_pthread_t thread_tbl[MAX_WORKERS];
+ odp_buffer_pool_t pool;
+ int thr_id;
+ int num_workers;
+ void *pool_base;
+ int i;
+ int first_core;
+ int core_count;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global()) {
+ ODP_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* init counters */
+ odp_atomic_init_u64(&counters.seq);
+ odp_atomic_init_u64(&counters.ip);
+ odp_atomic_init_u64(&counters.udp);
+ odp_atomic_init_u64(&counters.icmp);
+
+ /* Reserve memory for args from shared mem */
+ args = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE);
+ if (args == NULL) {
+ ODP_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(args, 0, sizeof(*args));
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &args->appl);
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), &args->appl);
+
+ core_count = odp_sys_core_count();
+ num_workers = core_count;
+
+ if (args->appl.core_count)
+ num_workers = args->appl.core_count;
+
+ if (num_workers > MAX_WORKERS)
+ num_workers = MAX_WORKERS;
+
+ /* ping mode need two worker */
+ if (args->appl.mode == APPL_MODE_PING)
+ num_workers = 2;
+
+ printf("Num worker threads: %i\n", num_workers);
+
+ /*
+ * By default core #0 runs Linux kernel background tasks.
+ * Start mapping thread from core #1
+ */
+ first_core = 1;
+
+ if (core_count == 1)
+ first_core = 0;
+
+ printf("First core: %i\n\n", first_core);
+
+ /* Init this thread */
+ thr_id = odp_thread_create(0);
+ odp_init_local(thr_id);
+
+ /* Create packet pool */
+ pool_base = odp_shm_reserve("shm_packet_pool",
+ SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE);
+ if (pool_base == NULL) {
+ ODP_ERR("Error: packet pool mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ pool = odp_buffer_pool_create("packet_pool", pool_base,
+ SHM_PKT_POOL_SIZE,
+ SHM_PKT_POOL_BUF_SIZE,
+ ODP_CACHE_LINE_SIZE,
+ ODP_BUFFER_TYPE_PACKET);
+ if (pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ odp_buffer_pool_print(pool);
+
+ /* Create and init worker threads */
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+
+ if (args->appl.mode == APPL_MODE_PING) {
+ args->thread[1].pktio_dev = args->appl.if_names[0];
+ args->thread[1].pool = pool;
+ args->thread[1].mode = args->appl.mode;
+ odp_linux_pthread_create(thread_tbl, 1, 0,
+ gen_recv_thread, &args->thread[1]);
+
+ args->thread[0].pktio_dev = args->appl.if_names[0];
+ args->thread[0].pool = pool;
+ args->thread[0].mode = args->appl.mode;
+ odp_linux_pthread_create(thread_tbl, 1, 0,
+ gen_send_thread, &args->thread[0]);
+
+ /* only wait send thread to join */
+ num_workers = 1;
+ } else {
+ for (i = 0; i < num_workers; ++i) {
+ void *(*thr_run_func) (void *);
+ int core;
+ int if_idx;
+
+ core = (first_core + i) % core_count;
+
+ if_idx = i % args->appl.if_count;
+
+ args->thread[i].pktio_dev = args->appl.if_names[if_idx];
+ args->thread[i].pool = pool;
+ args->thread[i].mode = args->appl.mode;
+
+ if (args->appl.mode == APPL_MODE_UDP) {
+ thr_run_func = gen_send_thread;
+ } else if (args->appl.mode == APPL_MODE_RCV) {
+ thr_run_func = gen_recv_thread;
+ } else {
+ ODP_ERR("ERR MODE\n");
+ exit(EXIT_FAILURE);
+ }
+ /*
+ * Create threads one-by-one instead of all-at-once,
+ * because each thread might get different arguments.
+ * Calls odp_thread_create(cpu) for each thread
+ */
+ odp_linux_pthread_create(thread_tbl, 1,
+ core, thr_run_func,
+ &args->thread[i]);
+ }
+ }
+
+ /* Master thread waits for other threads to exit */
+ odp_linux_pthread_join(thread_tbl, num_workers);
+ printf("Exit\n\n");
+
+ return 0;
+}
+
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ char *names, *str, *token, *save;
+ size_t len;
+ int i;
+ static struct option longopts[] = {
+ {"interface", required_argument, NULL, 'I'},
+ {"workers", required_argument, NULL, 'w'},
+ {"srcmac", required_argument, NULL, 'a'},
+ {"dstmac", required_argument, NULL, 'b'},
+ {"srcip", required_argument, NULL, 'c'},
+ {"dstip", required_argument, NULL, 'd'},
+ {"packetsize", required_argument, NULL, 's'},
+ {"mode", required_argument, NULL, 'm'},
+ {"count", required_argument, NULL, 'n'},
+ {"timeout", required_argument, NULL, 't'},
+ {"interval", required_argument, NULL, 'i'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ appl_args->mode = -1; /* Invalid, must be changed by parsing */
+ appl_args->number = -1;
+ appl_args->payload = 56;
+ appl_args->timeout = -1;
+
+ while (1) {
+ opt = getopt_long(argc, argv, "+I:a:b:c:d:s:i:m:n:t:w:h",
+ longopts, &long_index);
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'w':
+ appl_args->core_count = atoi(optarg);
+ break;
+ /* parse packet-io interface names */
+ case 'I':
+ len = strlen(optarg);
+ if (len == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ names = malloc(len);
+ if (names == NULL) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* count the number of tokens separated by ',' */
+ strcpy(names, optarg);
+ for (str = names, i = 0;; str = NULL, i++) {
+ token = strtok_r(str, ",", &save);
+ if (token == NULL)
+ break;
+ }
+ appl_args->if_count = i;
+
+ if (appl_args->if_count == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* allocate storage for the if names */
+ appl_args->if_names =
+ calloc(appl_args->if_count, sizeof(char *));
+
+ /* store the if names (reset names string) */
+ strcpy(names, optarg);
+ for (str = names, i = 0;; str = NULL, i++) {
+ token = strtok_r(str, ",", &save);
+ if (token == NULL)
+ break;
+ appl_args->if_names[i] = token;
+ }
+ break;
+
+ case 'm':
+ if (optarg[0] == 'u') {
+ appl_args->mode = APPL_MODE_UDP;
+ } else if (optarg[0] == 'p') {
+ appl_args->mode = APPL_MODE_PING;
+ } else if (optarg[0] == 'r') {
+ appl_args->mode = APPL_MODE_RCV;
+ } else {
+ ODP_ERR("wrong mode!\n");
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'a':
+ if (scan_mac(optarg, &appl_args->srcmac) != 1) {
+ ODP_ERR("wrong src mac:%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'b':
+ if (scan_mac(optarg, &appl_args->dstmac) != 1) {
+ ODP_ERR("wrong dst mac:%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'c':
+ if (scan_ip(optarg, &appl_args->srcip) != 1) {
+ ODP_ERR("wrong src ip:%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'd':
+ if (scan_ip(optarg, &appl_args->dstip) != 1) {
+ ODP_ERR("wrong dst ip:%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 's':
+ appl_args->payload = atoi(optarg);
+ break;
+
+ case 'n':
+ appl_args->number = atoi(optarg);
+ break;
+
+ case 't':
+ appl_args->timeout = atoi(optarg);
+ break;
+
+ case 'i':
+ appl_args->interval = atoi(optarg);
+ if (appl_args->interval <= 200 && geteuid() != 0) {
+ ODP_ERR("should be root user\n");
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (appl_args->if_count == 0 || appl_args->mode == -1) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args)
+{
+ int i;
+
+ printf("\n"
+ "ODP system info\n"
+ "---------------\n"
+ "ODP API version: %s\n"
+ "CPU model: %s\n"
+ "CPU freq (hz): %"PRIu64"\n"
+ "Cache line size: %i\n"
+ "Core count: %i\n"
+ "\n",
+ odp_version_api_str(), odp_sys_cpu_model_str(), odp_sys_cpu_hz(),
+ odp_sys_cache_line_size(), odp_sys_core_count());
+
+ printf("Running ODP appl: \"%s\"\n"
+ "-----------------\n"
+ "IF-count: %i\n"
+ "Using IFs: ",
+ progname, appl_args->if_count);
+ for (i = 0; i < appl_args->if_count; ++i)
+ printf(" %s", appl_args->if_names[i]);
+ printf("\n"
+ "Mode: ");
+ if (appl_args->mode == 0)
+ PRINT_APPL_MODE(0);
+ else
+ PRINT_APPL_MODE(0);
+ printf("\n\n");
+ fflush(NULL);
+}
+
+/**
+ * Prinf usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -I eth1 -r\n"
+ "\n"
+ "OpenDataPlane example application.\n"
+ "\n"
+ " Work mode:\n"
+ " 1.send udp packets\n"
+ " odp_generator -I eth0 --srcmac fe:0f:97:c9:e0:44 --dstmac 32:cb:9b:27:2f:1a --srcip 192.168.0.1 --dstip 192.168.0.2 -m u\n"
+ " 2.receive udp packets\n"
+ " odp_generator -I eth0 -m r\n"
+ " 3.work likes ping\n"
+ " odp_generator -I eth0 --srcmac fe:0f:97:c9:e0:44 --dstmac 32:cb:9b:27:2f:1a --srcip 192.168.0.1 --dstip 192.168.0.2 -m p\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -I, --interface Eth interfaces (comma-separated, no spaces)\n"
+ " -a, --srcmac src mac address\n"
+ " -b, --dstmac dst mac address\n"
+ " -c, --srcip src ip address\n"
+ " -d, --dstip dst ip address\n"
+ " -s, --packetsize payload length of the packets\n"
+ " -m, --mode work mode: send udp(u), receive(r), send icmp(p)\n"
+ " -n, --count the number of packets to be send\n"
+ " -t, --timeout only for ping mode, wait ICMP reply timeout seconds\n"
+ " -i, --interval wait interval ms between sending each packet\n"
+ " default is 1000ms. 0 for flood mode\n"
+ "\n"
+ "Optional OPTIONS\n"
+ " -h, --help Display help and exit.\n"
+ "\n", NO_PATH(progname), NO_PATH(progname)
+ );
+}
+/**
+ * calc time period
+ *
+ *@param recvtime start time
+ *@param sendtime end time
+*/
+static void tv_sub(struct timeval *recvtime, struct timeval *sendtime)
+{
+ long sec = recvtime->tv_sec - sendtime->tv_sec;
+ long usec = recvtime->tv_usec - sendtime->tv_usec;
+ if (usec >= 0) {
+ recvtime->tv_sec = sec;
+ recvtime->tv_usec = usec;
+ } else {
+ recvtime->tv_sec = sec - 1;
+ recvtime->tv_usec = -usec;
+ }
+}
diff --git a/example/l2fwd/Makefile.am b/example/l2fwd/Makefile.am
new file mode 100644
index 000000000..9b5a7ef04
--- /dev/null
+++ b/example/l2fwd/Makefile.am
@@ -0,0 +1,5 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_l2fwd
+
+dist_odp_l2fwd_SOURCES = odp_l2fwd.c
diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c
new file mode 100644
index 000000000..e331ff262
--- /dev/null
+++ b/example/l2fwd/odp_l2fwd.c
@@ -0,0 +1,637 @@
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * @example odp_l2fwd.c ODP basic forwarding application
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <odp.h>
+#include <helper/odp_linux.h>
+#include <helper/odp_packet_helper.h>
+#include <helper/odp_eth.h>
+#include <helper/odp_ip.h>
+
+#define MAX_WORKERS 32
+#define SHM_PKT_POOL_SIZE (512*2048)
+#define SHM_PKT_POOL_BUF_SIZE 1856
+#define MAX_PKT_BURST 16
+
+#define APPL_MODE_PKT_BURST 0
+#define APPL_MODE_PKT_QUEUE 1
+
+#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
+
+/** Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+ int core_count;
+ int if_count; /**< Number of interfaces to be used */
+ char **if_names; /**< Array of pointers to interface names */
+ int mode; /**< Packet IO mode */
+ int type; /**< Packet IO type */
+ int fanout; /**< Packet IO fanout */
+ odp_buffer_pool_t pool; /**< Buffer pool for packet IO */
+} appl_args_t;
+
+/**
+ * Thread specific arguments
+ */
+typedef struct {
+ char *srcif; /**< Source Interface */
+ char *dstif; /**< Dest Interface */
+ odp_buffer_pool_t pool; /**< Buffer pool for packet IO */
+ odp_pktio_t srcpktio; /**< Source pktio handle */
+ odp_pktio_t dstpktio; /**< Destination pktio handle */
+ int mode; /**< Thread mode */
+ int type; /**< Thread i/o type */
+ int fanout; /**< Thread i/o fanout */
+} thread_args_t;
+
+/**
+ * Grouping of both parsed CL args and thread specific args - alloc together
+ */
+typedef struct {
+ /** Application (parsed) arguments */
+ appl_args_t appl;
+ /** Thread specific arguments */
+ thread_args_t thread[MAX_WORKERS];
+} args_t;
+
+/** Global pointer to args */
+static args_t *gbl_args;
+static int num_workers;
+
+/* helper funcs */
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
+static void print_info(char *progname, appl_args_t *appl_args);
+static void usage(char *progname);
+
+/**
+ * Burst mode: pktio for each thread will be created with either same or
+ * different params
+ *
+ * @param arg thread arguments of type 'thread_args_t *'
+ * @param pool is the packet pool from where buffers should be taken
+ */
+static odp_pktio_t burst_mode_init_params(void *arg, odp_buffer_pool_t pool)
+{
+ thread_args_t *args;
+ odp_pktio_params_t params;
+ socket_params_t *sock_params = &params.sock_params;
+ odp_pktio_t pktio;
+
+ args = arg;
+ /* Open a packet IO instance for this thread */
+ sock_params->type = args->type;
+ sock_params->fanout = args->fanout;
+ pktio = odp_pktio_open(args->srcif, pool, &params);
+ if (pktio == ODP_PKTIO_INVALID)
+ ODP_ERR(" Error: pktio create failed");
+
+ return pktio;
+}
+
+/**
+ * Queue mode: pktio for each thread will be created with either same or
+ * different params. Queues are created and attached to the pktio.
+ *
+ * @param arg thread arguments of type 'thread_args_t *'
+ * @param pool is the packet pool from where buffers should be taken
+ */
+static odp_pktio_t queue_mode_init_params(void *arg, odp_buffer_pool_t pool)
+{
+ char inq_name[ODP_QUEUE_NAME_LEN];
+ odp_queue_param_t qparam;
+ odp_queue_t inq_def;
+ int ret;
+ odp_pktio_t pktio = ODP_PKTIO_INVALID;
+
+ pktio = burst_mode_init_params(arg, pool);
+ if (pktio == ODP_PKTIO_INVALID)
+ return pktio;
+ /*
+ * Create and set the default INPUT queue associated with the 'pktio'
+ * resource
+ */
+ qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+ snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def", (int)pktio);
+ inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
+
+ inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam);
+ if (inq_def == ODP_QUEUE_INVALID) {
+ ODP_ERR(" Error: pktio queue creation failed");
+ return ODP_PKTIO_INVALID;
+ }
+
+ ret = odp_pktio_inq_setdef(pktio, inq_def);
+ if (ret != 0) {
+ ODP_ERR(" Error: default input-Q setup");
+ return ODP_PKTIO_INVALID;
+ }
+
+ return pktio;
+}
+
+/**
+ * Packet IO worker thread using ODP queues
+ *
+ * @param arg thread arguments of type 'thread_args_t *'
+ */
+static void *pktio_queue_thread(void *arg)
+{
+ int thr, i;
+ thread_args_t *thr_args;
+ char dstpktio[MAX_WORKERS+1];
+ odp_queue_t outq_def;
+ odp_packet_t pkt;
+ odp_buffer_t buf;
+ unsigned long pkt_cnt = 0;
+ unsigned long err_cnt = 0;
+
+ thr = odp_thread_id();
+ thr_args = arg;
+
+ if (thr_args->srcpktio == 0 || thr_args->dstpktio == 0) {
+ ODP_ERR("Invalid srcpktio:%d dstpktio:%d\n",
+ thr_args->srcpktio, thr_args->dstpktio);
+ return NULL;
+ }
+ printf("[%02i] srcif:%s dstif:%s spktio:%02i dpktio:%02i QUEUE mode\n",
+ thr, thr_args->srcif, thr_args->dstif, thr_args->srcpktio,
+ thr_args->dstpktio);
+
+ /* Populate an array of destination pktio's in all threads as the
+ * scheduler can take packets from any input queue
+ */
+ for (i = 0; i < num_workers; i++)
+ dstpktio[i+1] = gbl_args->thread[i].dstpktio;
+
+ /* Loop packets */
+ for (;;) {
+ odp_pktio_t pktio_tmp;
+
+ /* Use schedule to get buf from any input queue */
+ buf = odp_schedule(NULL, ODP_SCHED_WAIT);
+
+ pkt = odp_packet_from_buffer(buf);
+ /* Drop packets with errors */
+ if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
+ ODP_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt);
+ continue;
+ }
+
+ pktio_tmp = odp_pktio_get_input(pkt);
+ outq_def = odp_pktio_outq_getdef(dstpktio[pktio_tmp]);
+ if (outq_def == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%02i] Error: def output-Q query\n", thr);
+ return NULL;
+ }
+
+ /* Enqueue the packet for output */
+ odp_queue_enq(outq_def, buf);
+
+ /* Print packet counts every once in a while */
+ if (odp_unlikely(pkt_cnt++ % 100000 == 0)) {
+ printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
+ fflush(NULL);
+ }
+ }
+
+/* unreachable */
+}
+
+/**
+ * Packet IO worker thread using bursts from/to IO resources
+ *
+ * @param arg thread arguments of type 'thread_args_t *'
+ */
+static void *pktio_ifburst_thread(void *arg)
+{
+ int thr;
+ thread_args_t *thr_args;
+ int pkts, pkts_ok;
+ odp_packet_t pkt_tbl[MAX_PKT_BURST];
+ unsigned long pkt_cnt = 0;
+ unsigned long err_cnt = 0;
+ unsigned long tmp = 0;
+
+ thr = odp_thread_id();
+ thr_args = arg;
+
+ if (thr_args->srcpktio == 0 || thr_args->dstpktio == 0) {
+ ODP_ERR("Invalid srcpktio:%d dstpktio:%d\n",
+ thr_args->srcpktio, thr_args->dstpktio);
+ return NULL;
+ }
+ printf("[%02i] srcif:%s dstif:%s spktio:%02i dpktio:%02i BURST mode\n",
+ thr, thr_args->srcif, thr_args->dstif, thr_args->srcpktio,
+ thr_args->dstpktio);
+
+ /* Loop packets */
+ for (;;) {
+ pkts = odp_pktio_recv(thr_args->srcpktio, pkt_tbl,
+ MAX_PKT_BURST);
+ if (pkts > 0) {
+ /* Drop packets with errors */
+ pkts_ok = drop_err_pkts(pkt_tbl, pkts);
+ if (pkts_ok > 0)
+ odp_pktio_send(thr_args->dstpktio, pkt_tbl,
+ pkts_ok);
+ if (odp_unlikely(pkts_ok != pkts))
+ ODP_ERR("Dropped frames:%u - err_cnt:%lu\n",
+ pkts-pkts_ok, ++err_cnt);
+
+ /* Print packet counts every once in a while */
+ tmp += pkts_ok;
+ if (odp_unlikely((tmp >= 100000) || /* OR first print:*/
+ ((pkt_cnt == 0) && ((tmp-1) < MAX_PKT_BURST)))) {
+ pkt_cnt += tmp;
+ printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
+ fflush(NULL);
+ tmp = 0;
+ }
+ }
+ }
+
+/* unreachable */
+}
+
+/**
+ * ODP L2 forwarding main function
+ */
+int main(int argc, char *argv[])
+{
+ odp_linux_pthread_t thread_tbl[MAX_WORKERS];
+ odp_buffer_pool_t pool;
+ int thr_id;
+ void *pool_base;
+ int i;
+ int first_core;
+ int core_count;
+ odp_pktio_t pktio;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global()) {
+ ODP_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ gbl_args = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE);
+ if (gbl_args == NULL) {
+ ODP_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(gbl_args, 0, sizeof(*gbl_args));
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &gbl_args->appl);
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), &gbl_args->appl);
+
+ core_count = odp_sys_core_count();
+ num_workers = core_count;
+
+ if (gbl_args->appl.core_count)
+ num_workers = gbl_args->appl.core_count;
+
+ if (num_workers > MAX_WORKERS)
+ num_workers = MAX_WORKERS;
+
+ printf("Num worker threads: %i\n", num_workers);
+
+ if (num_workers < gbl_args->appl.if_count) {
+ ODP_ERR("Error: core count %d is less than interface count\n",
+ num_workers);
+ exit(EXIT_FAILURE);
+ }
+ if (gbl_args->appl.if_count % 2 != 0) {
+ ODP_ERR("Error: interface count %d is odd in fwd appl.\n",
+ gbl_args->appl.if_count);
+ exit(EXIT_FAILURE);
+ }
+ /*
+ * By default core #0 runs Linux kernel background tasks.
+ * Start mapping thread from core #1
+ */
+ first_core = 1;
+
+ if (core_count == 1)
+ first_core = 0;
+
+ printf("First core: %i\n\n", first_core);
+
+ /* Init this thread */
+ thr_id = odp_thread_create(0);
+ odp_init_local(thr_id);
+
+ /* Create packet pool */
+ pool_base = odp_shm_reserve("shm_packet_pool",
+ SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE);
+ if (pool_base == NULL) {
+ ODP_ERR("Error: packet pool mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ pool = odp_buffer_pool_create("packet_pool", pool_base,
+ SHM_PKT_POOL_SIZE,
+ SHM_PKT_POOL_BUF_SIZE,
+ ODP_CACHE_LINE_SIZE,
+ ODP_BUFFER_TYPE_PACKET);
+ if (pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ odp_buffer_pool_print(pool);
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ /* initialize threads params */
+ for (i = 0; i < num_workers; ++i) {
+ int if_idx;
+
+ if_idx = i % gbl_args->appl.if_count;
+
+ gbl_args->thread[i].srcif = gbl_args->appl.if_names[if_idx];
+ if (if_idx % 2 == 0)
+ gbl_args->thread[i].dstif = gbl_args->appl.if_names[if_idx+1];
+ else
+ gbl_args->thread[i].dstif = gbl_args->appl.if_names[if_idx-1];
+ gbl_args->thread[i].pool = pool;
+ gbl_args->thread[i].mode = gbl_args->appl.mode;
+ gbl_args->thread[i].type = gbl_args->appl.type;
+ gbl_args->thread[i].fanout = gbl_args->appl.fanout;
+
+ if (gbl_args->appl.mode == APPL_MODE_PKT_BURST) {
+ pktio = burst_mode_init_params(&gbl_args->thread[i], pool);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODP_ERR(" for thread:%02i\n", i);
+ exit(EXIT_FAILURE);
+ }
+ } else { /* APPL_MODE_PKT_QUEUE */
+ pktio = queue_mode_init_params(&gbl_args->thread[i], pool);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODP_ERR(" for thread:%02i\n", i);
+ exit(EXIT_FAILURE);
+ }
+ }
+ gbl_args->thread[i].srcpktio = pktio;
+ }
+ for (i = 0; i < num_workers; ++i) {
+ if (i % 2 == 0)
+ gbl_args->thread[i].dstpktio = gbl_args->thread[i+1].srcpktio;
+ else
+ gbl_args->thread[i].dstpktio = gbl_args->thread[i-1].srcpktio;
+ }
+ /* Create worker threads */
+ for (i = 0; i < num_workers; ++i) {
+ void *(*thr_run_func) (void *);
+ int core;
+
+ core = (first_core + i) % core_count;
+
+ if (gbl_args->appl.mode == APPL_MODE_PKT_BURST)
+ thr_run_func = pktio_ifburst_thread;
+ else /* APPL_MODE_PKT_QUEUE */
+ thr_run_func = pktio_queue_thread;
+ odp_linux_pthread_create(thread_tbl, 1, core, thr_run_func,
+ &gbl_args->thread[i]);
+ }
+
+ /* Master thread waits for other threads to exit */
+ odp_linux_pthread_join(thread_tbl, num_workers);
+
+ printf("Exit\n\n");
+
+ return 0;
+}
+
+/**
+ * Drop packets which input parsing marked as containing errors.
+ *
+ * Frees packets with error and modifies pkt_tbl[] to only contain packets with
+ * no detected errors.
+ *
+ * @param pkt_tbl Array of packet
+ * @param len Length of pkt_tbl[]
+ *
+ * @return Number of packets with no detected error
+ */
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
+{
+ odp_packet_t pkt;
+ unsigned pkt_cnt = len;
+ unsigned i, j;
+
+ for (i = 0, j = 0; i < len; ++i) {
+ pkt = pkt_tbl[i];
+
+ if (odp_unlikely(odp_packet_error(pkt))) {
+ odp_packet_free(pkt); /* Drop */
+ pkt_cnt--;
+ } else if (odp_unlikely(i != j++)) {
+ pkt_tbl[j-1] = pkt;
+ }
+ }
+
+ return pkt_cnt;
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ char *names, *str, *token, *save;
+ size_t len;
+ int i;
+ static struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"interface", required_argument, NULL, 'i'}, /* return 'i' */
+ {"mode", required_argument, NULL, 'm'}, /* return 'm' */
+ {"help", no_argument, NULL, 'h'}, /* return 'h' */
+ {NULL, 0, NULL, 0}
+ };
+
+ appl_args->mode = -1; /* Invalid, must be changed by parsing */
+ appl_args->type = 3; /* 3: ODP_PKTIO_TYPE_SOCKET_MMAP */
+ appl_args->fanout = 1; /* turn off fanout by default for mmap */
+
+ while (1) {
+ opt = getopt_long(argc, argv, "+c:i:m:t:f:h",
+ longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ appl_args->core_count = atoi(optarg);
+ break;
+ /* parse packet-io interface names */
+ case 'i':
+ len = strlen(optarg);
+ if (len == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ names = malloc(len);
+ if (names == NULL) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* count the number of tokens separated by ',' */
+ strcpy(names, optarg);
+ for (str = names, i = 0;; str = NULL, i++) {
+ token = strtok_r(str, ",", &save);
+ if (token == NULL)
+ break;
+ }
+ appl_args->if_count = i;
+
+ if (appl_args->if_count == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* allocate storage for the if names */
+ appl_args->if_names =
+ calloc(appl_args->if_count, sizeof(char *));
+
+ /* store the if names (reset names string) */
+ strcpy(names, optarg);
+ for (str = names, i = 0;; str = NULL, i++) {
+ token = strtok_r(str, ",", &save);
+ if (token == NULL)
+ break;
+ appl_args->if_names[i] = token;
+ }
+ break;
+
+ case 'm':
+ i = atoi(optarg);
+ if (i == 0)
+ appl_args->mode = APPL_MODE_PKT_BURST;
+ else
+ appl_args->mode = APPL_MODE_PKT_QUEUE;
+ break;
+
+ case 't':
+ appl_args->type = atoi(optarg);
+ break;
+
+ case 'f':
+ appl_args->fanout = atoi(optarg);
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (appl_args->if_count == 0 || appl_args->mode == -1) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args)
+{
+ int i;
+
+ printf("\n"
+ "ODP system info\n"
+ "---------------\n"
+ "ODP API version: %s\n"
+ "CPU model: %s\n"
+ "CPU freq (hz): %"PRIu64"\n"
+ "Cache line size: %i\n"
+ "Core count: %i\n"
+ "\n",
+ odp_version_api_str(), odp_sys_cpu_model_str(), odp_sys_cpu_hz(),
+ odp_sys_cache_line_size(), odp_sys_core_count());
+
+ printf("Running ODP appl: \"%s\"\n"
+ "-----------------\n"
+ "IF-count: %i\n"
+ "Using IFs: ",
+ progname, appl_args->if_count);
+ for (i = 0; i < appl_args->if_count; ++i)
+ printf(" %s", appl_args->if_names[i]);
+ printf("\n"
+ "Mode: ");
+ if (appl_args->mode == APPL_MODE_PKT_BURST)
+ PRINT_APPL_MODE(APPL_MODE_PKT_BURST);
+ else
+ PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE);
+ printf("\n\n");
+ fflush(NULL);
+}
+
+/**
+ * Prinf usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "OpenDataPlane L2 forwarding application.\n"
+ "\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
+ " In the above example,\n"
+ " eth0 will send pkts to eth1 and vice versa\n"
+ " eth2 will send pkts to eth3 and vice versa\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -i, --interface Eth interfaces (comma-separated, no spaces)\n"
+ " -m, --mode 0: Burst send&receive packets (no queues)\n"
+ " 1: Send&receive packets through ODP queues.\n"
+ " -t, --type 1: ODP_PKTIO_TYPE_SOCKET_BASIC\n"
+ " 2: ODP_PKTIO_TYPE_SOCKET_MMSG\n"
+ " 3: ODP_PKTIO_TYPE_SOCKET_MMAP\n"
+ " 4: ODP_PKTIO_TYPE_NETMAP\n"
+ " Default: 3: ODP_PKTIO_TYPE_SOCKET_MMAP\n"
+ " -f, --fanout 0: off 1: on (Default 1: on)\n"
+ "\n"
+ "Optional OPTIONS\n"
+ " -c, --count <number> Core count.\n"
+ " -h, --help Display help and exit.\n\n"
+ "\n", NO_PATH(progname), NO_PATH(progname)
+ );
+}
diff --git a/example/odp_example/Makefile.am b/example/odp_example/Makefile.am
new file mode 100644
index 000000000..c1b4ed327
--- /dev/null
+++ b/example/odp_example/Makefile.am
@@ -0,0 +1,5 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_example
+
+dist_odp_example_SOURCES = odp_example.c
diff --git a/example/odp_example/odp_example.c b/example/odp_example/odp_example.c
new file mode 100644
index 000000000..be9609356
--- /dev/null
+++ b/example/odp_example/odp_example.c
@@ -0,0 +1,1062 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * @example odp_example.c ODP example application
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+/* ODP main header */
+#include <odp.h>
+
+/* ODP helper for Linux apps */
+#include <helper/odp_linux.h>
+
+/* Needs librt*/
+#include <time.h>
+
+/* GNU lib C */
+#include <getopt.h>
+
+
+#define MAX_WORKERS 32 /**< Max worker threads */
+#define MSG_POOL_SIZE (4*1024*1024) /**< Message pool size */
+#define MAX_ALLOCS 35 /**< Alloc burst size */
+#define QUEUES_PER_PRIO 64 /**< Queue per priority */
+#define QUEUE_ROUNDS (512*1024) /**< Queue test rounds */
+#define ALLOC_ROUNDS (1024*1024) /**< Alloc test rounds */
+#define MULTI_BUFS_MAX 4 /**< Buffer burst size */
+#define TEST_SEC 2 /**< Time test duration in sec */
+
+/** Dummy message */
+typedef struct {
+ int msg_id; /**< Message ID */
+ int seq; /**< Sequence number */
+} test_message_t;
+
+#define MSG_HELLO 1 /**< Hello */
+#define MSG_ACK 2 /**< Ack */
+
+/** Test arguments */
+typedef struct {
+ int core_count; /**< Core count*/
+} test_args_t;
+
+
+/** @private Barrier for test synchronisation */
+static odp_barrier_t test_barrier;
+
+
+/**
+ * @internal Clear all scheduled queues. Retry to be sure that all
+ * buffers have been scheduled.
+ */
+static void clear_sched_queues(void)
+{
+ odp_buffer_t buf;
+
+ while (1) {
+ buf = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+ if (buf == ODP_BUFFER_INVALID)
+ break;
+
+ odp_buffer_free(buf);
+ }
+}
+
+
+static int create_queue(int thr, odp_buffer_pool_t msg_pool, int prio)
+{
+ char name[] = "sched_XX_00";
+ odp_buffer_t buf;
+ odp_queue_t queue;
+
+ buf = odp_buffer_alloc(msg_pool);
+
+ if (!odp_buffer_is_valid(buf)) {
+ ODP_ERR(" [%i] msg_pool alloc failed\n", thr);
+ return -1;
+ }
+
+ name[6] = '0' + prio/10;
+ name[7] = '0' + prio - 10*(prio/10);
+
+ queue = odp_queue_lookup(name);
+
+ if (queue == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%i] Queue %s lookup failed.\n", thr, name);
+ return -1;
+ }
+
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int create_queues(int thr, odp_buffer_pool_t msg_pool, int prio)
+{
+ char name[] = "sched_XX_YY";
+ odp_buffer_t buf;
+ odp_queue_t queue;
+ int i;
+
+ name[6] = '0' + prio/10;
+ name[7] = '0' + prio - 10*(prio/10);
+
+ /* Alloc and enqueue a buffer per queue */
+ for (i = 0; i < QUEUES_PER_PRIO; i++) {
+ name[9] = '0' + i/10;
+ name[10] = '0' + i - 10*(i/10);
+
+ queue = odp_queue_lookup(name);
+
+ if (queue == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%i] Queue %s lookup failed.\n", thr, name);
+ return -1;
+ }
+
+ buf = odp_buffer_alloc(msg_pool);
+
+ if (!odp_buffer_is_valid(buf)) {
+ ODP_ERR(" [%i] msg_pool alloc failed\n", thr);
+ return -1;
+ }
+
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * @internal Test single buffer alloc and free
+ *
+ * @param thr Thread
+ * @param pool Buffer pool
+ *
+ * @return 0 if successful
+ */
+static int test_alloc_single(int thr, odp_buffer_pool_t pool)
+{
+ int i;
+ odp_buffer_t temp_buf;
+ uint64_t t1, t2, cycles, ns;
+
+ t1 = odp_time_get_cycles();
+
+ for (i = 0; i < ALLOC_ROUNDS; i++) {
+ temp_buf = odp_buffer_alloc(pool);
+
+ if (!odp_buffer_is_valid(temp_buf)) {
+ ODP_ERR(" [%i] alloc_single failed\n", thr);
+ return -1;
+ }
+
+ odp_buffer_free(temp_buf);
+ }
+
+ t2 = odp_time_get_cycles();
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns = odp_time_cycles_to_ns(cycles);
+
+ printf(" [%i] alloc_sng alloc+free %"PRIu64" cycles, %"PRIu64" ns\n",
+ thr, cycles/ALLOC_ROUNDS, ns/ALLOC_ROUNDS);
+
+ return 0;
+}
+
+/**
+ * @internal Test multiple buffers alloc and free
+ *
+ * @param thr Thread
+ * @param pool Buffer pool
+ *
+ * @return 0 if successful
+ */
+static int test_alloc_multi(int thr, odp_buffer_pool_t pool)
+{
+ int i, j;
+ odp_buffer_t temp_buf[MAX_ALLOCS];
+ uint64_t t1, t2, cycles, ns;
+
+ t1 = odp_time_get_cycles();
+
+ for (i = 0; i < ALLOC_ROUNDS; i++) {
+ for (j = 0; j < MAX_ALLOCS; j++) {
+ temp_buf[j] = odp_buffer_alloc(pool);
+
+ if (!odp_buffer_is_valid(temp_buf[j])) {
+ ODP_ERR(" [%i] alloc_multi failed\n", thr);
+ return -1;
+ }
+ }
+
+ for (; j > 0; j--)
+ odp_buffer_free(temp_buf[j-1]);
+ }
+
+ t2 = odp_time_get_cycles();
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns = odp_time_cycles_to_ns(cycles);
+
+ printf(" [%i] alloc_multi alloc+free %"PRIu64" cycles, %"PRIu64" ns\n",
+ thr, cycles/(ALLOC_ROUNDS*MAX_ALLOCS),
+ ns/(ALLOC_ROUNDS*MAX_ALLOCS));
+
+ return 0;
+}
+
+/**
+ * @internal Test queue polling
+ *
+ * Enqueue to and dequeue to/from a single shared queue.
+ *
+ * @param thr Thread
+ * @param msg_pool Buffer pool
+ *
+ * @return 0 if successful
+ */
+static int test_poll_queue(int thr, odp_buffer_pool_t msg_pool)
+{
+ odp_buffer_t buf;
+ test_message_t *t_msg;
+ odp_queue_t queue;
+ uint64_t t1, t2, cycles, ns;
+ int i;
+
+ /* Alloc test message */
+ buf = odp_buffer_alloc(msg_pool);
+
+ if (!odp_buffer_is_valid(buf)) {
+ ODP_ERR(" [%i] msg_pool alloc failed\n", thr);
+ return -1;
+ }
+
+ /* odp_buffer_print(buf); */
+
+ t_msg = odp_buffer_addr(buf);
+ t_msg->msg_id = MSG_HELLO;
+ t_msg->seq = 0;
+
+ queue = odp_queue_lookup("poll_queue");
+
+ if (queue == ODP_QUEUE_INVALID) {
+ printf(" [%i] Queue lookup failed.\n", thr);
+ return -1;
+ }
+
+ t1 = odp_time_get_cycles();
+
+ for (i = 0; i < QUEUE_ROUNDS; i++) {
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+
+ buf = odp_queue_deq(queue);
+
+ if (!odp_buffer_is_valid(buf)) {
+ ODP_ERR(" [%i] Queue empty.\n", thr);
+ return -1;
+ }
+ }
+
+ t2 = odp_time_get_cycles();
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns = odp_time_cycles_to_ns(cycles);
+
+ printf(" [%i] poll_queue enq+deq %"PRIu64" cycles, %"PRIu64" ns\n",
+ thr, cycles/QUEUE_ROUNDS, ns/QUEUE_ROUNDS);
+
+ odp_buffer_free(buf);
+ return 0;
+}
+
+/**
+ * @internal Test scheduling of a single queue - with odp_schedule_one()
+ *
+ * Enqueue a buffer to the shared queue. Schedule and enqueue the received
+ * buffer back into the queue.
+ *
+ * @param str Test case name string
+ * @param thr Thread
+ * @param msg_pool Buffer pool
+ * @param prio Priority
+ *
+ * @return 0 if successful
+ */
+static int test_schedule_one_single(const char *str, int thr,
+ odp_buffer_pool_t msg_pool, int prio)
+{
+ odp_buffer_t buf;
+ odp_queue_t queue;
+ uint64_t t1, t2, cycles, ns;
+ uint32_t i;
+ uint32_t tot = 0;
+
+ if (create_queue(thr, msg_pool, prio))
+ return -1;
+
+ t1 = odp_time_get_cycles();
+
+ for (i = 0; i < QUEUE_ROUNDS; i++) {
+ buf = odp_schedule_one(&queue, ODP_SCHED_WAIT);
+
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC)
+ odp_schedule_release_atomic();
+
+ t2 = odp_time_get_cycles();
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns = odp_time_cycles_to_ns(cycles);
+ tot = i;
+
+ odp_barrier_sync(&test_barrier);
+ clear_sched_queues();
+
+ if (tot) {
+ cycles = cycles/tot;
+ ns = ns/tot;
+ } else {
+ cycles = 0;
+ ns = 0;
+ }
+
+ printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n",
+ thr, str, cycles, ns);
+
+ return 0;
+}
+
+/**
+ * @internal Test scheduling of multiple queues - with odp_schedule_one()
+ *
+ * Enqueue a buffer to each queue. Schedule and enqueue the received
+ * buffer back into the queue it came from.
+ *
+ * @param str Test case name string
+ * @param thr Thread
+ * @param msg_pool Buffer pool
+ * @param prio Priority
+ *
+ * @return 0 if successful
+ */
+static int test_schedule_one_many(const char *str, int thr,
+ odp_buffer_pool_t msg_pool, int prio)
+{
+ odp_buffer_t buf;
+ odp_queue_t queue;
+ uint64_t t1 = 0;
+ uint64_t t2 = 0;
+ uint64_t cycles, ns;
+ uint32_t i;
+ uint32_t tot = 0;
+
+ if (create_queues(thr, msg_pool, prio))
+ return -1;
+
+ /* Start sched-enq loop */
+ t1 = odp_time_get_cycles();
+
+ for (i = 0; i < QUEUE_ROUNDS; i++) {
+ buf = odp_schedule_one(&queue, ODP_SCHED_WAIT);
+
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC)
+ odp_schedule_release_atomic();
+
+ t2 = odp_time_get_cycles();
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns = odp_time_cycles_to_ns(cycles);
+ tot = i;
+
+ odp_barrier_sync(&test_barrier);
+ clear_sched_queues();
+
+ if (tot) {
+ cycles = cycles/tot;
+ ns = ns/tot;
+ } else {
+ cycles = 0;
+ ns = 0;
+ }
+
+ printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n",
+ thr, str, cycles, ns);
+
+ return 0;
+}
+
+/**
+ * @internal Test scheduling of a single queue - with odp_schedule()
+ *
+ * Enqueue a buffer to the shared queue. Schedule and enqueue the received
+ * buffer back into the queue.
+ *
+ * @param str Test case name string
+ * @param thr Thread
+ * @param msg_pool Buffer pool
+ * @param prio Priority
+ *
+ * @return 0 if successful
+ */
+static int test_schedule_single(const char *str, int thr,
+ odp_buffer_pool_t msg_pool, int prio)
+{
+ odp_buffer_t buf;
+ odp_queue_t queue;
+ uint64_t t1, t2, cycles, ns;
+ uint32_t i;
+ uint32_t tot = 0;
+
+ if (create_queue(thr, msg_pool, prio))
+ return -1;
+
+ t1 = odp_time_get_cycles();
+
+ for (i = 0; i < QUEUE_ROUNDS; i++) {
+ buf = odp_schedule(&queue, ODP_SCHED_WAIT);
+
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ /* Clear possible locally stored buffers */
+ odp_schedule_pause();
+
+ tot = i;
+
+ while (1) {
+ buf = odp_schedule(&queue, ODP_SCHED_NO_WAIT);
+
+ if (buf == ODP_BUFFER_INVALID)
+ break;
+
+ tot++;
+
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ odp_schedule_resume();
+
+ t2 = odp_time_get_cycles();
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns = odp_time_cycles_to_ns(cycles);
+
+ odp_barrier_sync(&test_barrier);
+ clear_sched_queues();
+
+ if (tot) {
+ cycles = cycles/tot;
+ ns = ns/tot;
+ } else {
+ cycles = 0;
+ ns = 0;
+ }
+
+ printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n",
+ thr, str, cycles, ns);
+
+ return 0;
+}
+
+
+/**
+ * @internal Test scheduling of multiple queues - with odp_schedule()
+ *
+ * Enqueue a buffer to each queue. Schedule and enqueue the received
+ * buffer back into the queue it came from.
+ *
+ * @param str Test case name string
+ * @param thr Thread
+ * @param msg_pool Buffer pool
+ * @param prio Priority
+ *
+ * @return 0 if successful
+ */
+static int test_schedule_many(const char *str, int thr,
+ odp_buffer_pool_t msg_pool, int prio)
+{
+ odp_buffer_t buf;
+ odp_queue_t queue;
+ uint64_t t1 = 0;
+ uint64_t t2 = 0;
+ uint64_t cycles, ns;
+ uint32_t i;
+ uint32_t tot = 0;
+
+ if (create_queues(thr, msg_pool, prio))
+ return -1;
+
+ /* Start sched-enq loop */
+ t1 = odp_time_get_cycles();
+
+ for (i = 0; i < QUEUE_ROUNDS; i++) {
+ buf = odp_schedule(&queue, ODP_SCHED_WAIT);
+
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ /* Clear possible locally stored buffers */
+ odp_schedule_pause();
+
+ tot = i;
+
+ while (1) {
+ buf = odp_schedule(&queue, ODP_SCHED_NO_WAIT);
+
+ if (buf == ODP_BUFFER_INVALID)
+ break;
+
+ tot++;
+
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ odp_schedule_resume();
+
+ t2 = odp_time_get_cycles();
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns = odp_time_cycles_to_ns(cycles);
+
+ odp_barrier_sync(&test_barrier);
+ clear_sched_queues();
+
+ if (tot) {
+ cycles = cycles/tot;
+ ns = ns/tot;
+ } else {
+ cycles = 0;
+ ns = 0;
+ }
+
+ printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n",
+ thr, str, cycles, ns);
+
+ return 0;
+}
+
+/**
+ * @internal Test scheduling of multiple queues with multi_sched and multi_enq
+ *
+ * @param str Test case name string
+ * @param thr Thread
+ * @param msg_pool Buffer pool
+ * @param prio Priority
+ *
+ * @return 0 if successful
+ */
+static int test_schedule_multi(const char *str, int thr,
+ odp_buffer_pool_t msg_pool, int prio)
+{
+ odp_buffer_t buf[MULTI_BUFS_MAX];
+ odp_queue_t queue;
+ uint64_t t1 = 0;
+ uint64_t t2 = 0;
+ uint64_t cycles, ns;
+ int i, j;
+ int num;
+ uint32_t tot = 0;
+ char name[] = "sched_XX_YY";
+
+ name[6] = '0' + prio/10;
+ name[7] = '0' + prio - 10*(prio/10);
+
+ /* Alloc and enqueue a buffer per queue */
+ for (i = 0; i < QUEUES_PER_PRIO; i++) {
+ name[9] = '0' + i/10;
+ name[10] = '0' + i - 10*(i/10);
+
+ queue = odp_queue_lookup(name);
+
+ if (queue == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%i] Queue %s lookup failed.\n", thr, name);
+ return -1;
+ }
+
+ for (j = 0; j < MULTI_BUFS_MAX; j++) {
+ buf[j] = odp_buffer_alloc(msg_pool);
+
+ if (!odp_buffer_is_valid(buf[j])) {
+ ODP_ERR(" [%i] msg_pool alloc failed\n", thr);
+ return -1;
+ }
+ }
+
+ if (odp_queue_enq_multi(queue, buf, MULTI_BUFS_MAX)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ /* Start sched-enq loop */
+ t1 = odp_time_get_cycles();
+
+ for (i = 0; i < QUEUE_ROUNDS; i++) {
+ num = odp_schedule_multi(&queue, ODP_SCHED_WAIT, buf,
+ MULTI_BUFS_MAX);
+
+ tot += num;
+
+ if (odp_queue_enq_multi(queue, buf, num)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ /* Clear possible locally stored buffers */
+ odp_schedule_pause();
+
+ while (1) {
+ num = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT, buf,
+ MULTI_BUFS_MAX);
+
+ if (num == 0)
+ break;
+
+ tot += num;
+
+ if (odp_queue_enq_multi(queue, buf, num)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ odp_schedule_resume();
+
+
+ t2 = odp_time_get_cycles();
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns = odp_time_cycles_to_ns(cycles);
+
+ odp_barrier_sync(&test_barrier);
+ clear_sched_queues();
+
+ if (tot) {
+ cycles = cycles/tot;
+ ns = ns/tot;
+ } else {
+ cycles = 0;
+ ns = 0;
+ }
+
+ printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n",
+ thr, str, cycles, ns);
+
+ return 0;
+}
+
+/**
+ * @internal Worker thread
+ *
+ * @param arg Arguments
+ *
+ * @return NULL on failure
+ */
+static void *run_thread(void *arg)
+{
+ int thr;
+ odp_buffer_pool_t msg_pool;
+
+ thr = odp_thread_id();
+
+ printf("Thread %i starts on core %i\n", thr, odp_thread_core());
+
+ /*
+ * Test barriers back-to-back
+ */
+ odp_barrier_sync(&test_barrier);
+ odp_barrier_sync(&test_barrier);
+ odp_barrier_sync(&test_barrier);
+ odp_barrier_sync(&test_barrier);
+
+ /*
+ * Find the buffer pool
+ */
+ msg_pool = odp_buffer_pool_lookup("msg_pool");
+
+ if (msg_pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR(" [%i] msg_pool not found\n", thr);
+ return NULL;
+ }
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_alloc_single(thr, msg_pool))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_alloc_multi(thr, msg_pool))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_poll_queue(thr, msg_pool))
+ return NULL;
+
+ /* Low prio */
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_one_single("sched_one_s_lo", thr, msg_pool,
+ ODP_SCHED_PRIO_LOWEST))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_single("sched_____s_lo", thr, msg_pool,
+ ODP_SCHED_PRIO_LOWEST))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_one_many("sched_one_m_lo", thr, msg_pool,
+ ODP_SCHED_PRIO_LOWEST))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_many("sched_____m_lo", thr, msg_pool,
+ ODP_SCHED_PRIO_LOWEST))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_multi("sched_multi_lo", thr, msg_pool,
+ ODP_SCHED_PRIO_LOWEST))
+ return NULL;
+
+ /* High prio */
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_one_single("sched_one_s_hi", thr, msg_pool,
+ ODP_SCHED_PRIO_HIGHEST))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_single("sched_____s_hi", thr, msg_pool,
+ ODP_SCHED_PRIO_HIGHEST))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_one_many("sched_one_m_hi", thr, msg_pool,
+ ODP_SCHED_PRIO_HIGHEST))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_many("sched_____m_hi", thr, msg_pool,
+ ODP_SCHED_PRIO_HIGHEST))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_multi("sched_multi_hi", thr, msg_pool,
+ ODP_SCHED_PRIO_HIGHEST))
+ return NULL;
+
+
+ printf("Thread %i exits\n", thr);
+ fflush(NULL);
+ return arg;
+}
+
+/**
+ * @internal Test cycle counter accuracy
+ */
+static void test_time(void)
+{
+ struct timespec tp1, tp2;
+ uint64_t t1, t2;
+ uint64_t ns1, ns2, cycles;
+ double err;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tp2)) {
+ ODP_ERR("clock_gettime failed.\n");
+ return;
+ }
+
+ printf("\nTime accuracy test (%i sec)\n", TEST_SEC);
+
+ do {
+ if (clock_gettime(CLOCK_MONOTONIC, &tp1)) {
+ ODP_ERR("clock_gettime failed.\n");
+ return;
+ }
+
+ } while (tp1.tv_sec == tp2.tv_sec);
+
+ t1 = odp_time_get_cycles();
+
+ do {
+ if (clock_gettime(CLOCK_MONOTONIC, &tp2)) {
+ ODP_ERR("clock_gettime failed.\n");
+ return;
+ }
+
+ } while ((tp2.tv_sec - tp1.tv_sec) < TEST_SEC);
+
+ t2 = odp_time_get_cycles();
+
+ ns1 = (tp2.tv_sec - tp1.tv_sec)*1000000000;
+
+ if (tp2.tv_nsec > tp1.tv_nsec)
+ ns1 += tp2.tv_nsec - tp1.tv_nsec;
+ else
+ ns1 -= tp1.tv_nsec - tp2.tv_nsec;
+
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns2 = odp_time_cycles_to_ns(cycles);
+
+ err = ((double)(ns2) - (double)ns1) / (double)ns1;
+
+ printf("clock_gettime %"PRIu64" ns\n", ns1);
+ printf("odp_time_get_cycles %"PRIu64" cycles\n", cycles);
+ printf("odp_time_cycles_to_ns %"PRIu64" ns\n", ns2);
+ printf("odp get cycle error %f%%\n", err*100.0);
+
+ printf("\n");
+}
+
+/**
+ * @internal Print help
+ */
+static void print_usage(void)
+{
+ printf("\n\nUsage: ./odp_example [options]\n");
+ printf("Options:\n");
+ printf(" -c, --count <number> core count, core IDs start from 1\n");
+ printf(" -h, --help this help\n");
+ printf("\n\n");
+}
+
+/**
+ * @internal Parse arguments
+ *
+ * @param argc Argument count
+ * @param argv Argument vector
+ * @param args Test arguments
+ */
+static void parse_args(int argc, char *argv[], test_args_t *args)
+{
+ int opt;
+ int long_index;
+
+ static struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ while (1) {
+ opt = getopt_long(argc, argv, "+c:h", longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ args->core_count = atoi(optarg);
+ break;
+
+ case 'h':
+ print_usage();
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+/**
+ * Test main function
+ */
+int main(int argc, char *argv[])
+{
+ odp_linux_pthread_t thread_tbl[MAX_WORKERS];
+ test_args_t args;
+ int thr_id;
+ int num_workers;
+ odp_buffer_pool_t pool;
+ void *pool_base;
+ odp_queue_t queue;
+ int i, j;
+ int prios;
+ int first_core;
+
+ printf("\nODP example starts\n");
+
+ memset(&args, 0, sizeof(args));
+ parse_args(argc, argv, &args);
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+
+ if (odp_init_global()) {
+ printf("ODP global init failed.\n");
+ return -1;
+ }
+
+ printf("\n");
+ printf("ODP system info\n");
+ printf("---------------\n");
+ printf("ODP API version: %s\n", odp_version_api_str());
+ printf("CPU model: %s\n", odp_sys_cpu_model_str());
+ printf("CPU freq (hz): %"PRIu64"\n", odp_sys_cpu_hz());
+ printf("Cache line size: %i\n", odp_sys_cache_line_size());
+ printf("Max core count: %i\n", odp_sys_core_count());
+
+ printf("\n");
+
+ /* A worker thread per core */
+ num_workers = odp_sys_core_count();
+
+ if (args.core_count)
+ num_workers = args.core_count;
+
+ /* force to max core count */
+ if (num_workers > MAX_WORKERS)
+ num_workers = MAX_WORKERS;
+
+ printf("num worker threads: %i\n", num_workers);
+
+ /*
+ * By default core #0 runs Linux kernel background tasks.
+ * Start mapping thread from core #1
+ */
+ first_core = 1;
+
+ if (odp_sys_core_count() == 1)
+ first_core = 0;
+
+ printf("first core: %i\n", first_core);
+
+ /*
+ * Init this thread. It makes also ODP calls when
+ * setting up resources for worker threads.
+ */
+ thr_id = odp_thread_create(0);
+ odp_init_local(thr_id);
+
+ /* Test cycle count accuracy */
+ test_time();
+
+ /*
+ * Create message pool
+ */
+ pool_base = odp_shm_reserve("msg_pool",
+ MSG_POOL_SIZE, ODP_CACHE_LINE_SIZE);
+
+ pool = odp_buffer_pool_create("msg_pool", pool_base, MSG_POOL_SIZE,
+ sizeof(test_message_t),
+ ODP_CACHE_LINE_SIZE, ODP_BUFFER_TYPE_RAW);
+
+ if (pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR("Pool create failed.\n");
+ return -1;
+ }
+
+ /* odp_buffer_pool_print(pool); */
+
+ /*
+ * Create a queue for direct poll test
+ */
+ queue = odp_queue_create("poll_queue", ODP_QUEUE_TYPE_POLL, NULL);
+
+ if (queue == ODP_QUEUE_INVALID) {
+ ODP_ERR("Poll queue create failed.\n");
+ return -1;
+ }
+
+ /*
+ * Create queues for schedule test. QUEUES_PER_PRIO per priority.
+ */
+ prios = odp_schedule_num_prio();
+
+ for (i = 0; i < prios; i++) {
+ if (i != ODP_SCHED_PRIO_HIGHEST &&
+ i != ODP_SCHED_PRIO_LOWEST)
+ continue;
+
+ odp_queue_param_t param;
+ char name[] = "sched_XX_YY";
+
+ name[6] = '0' + i/10;
+ name[7] = '0' + i - 10*(i/10);
+
+ param.sched.prio = i;
+ param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ param.sched.group = ODP_SCHED_GROUP_DEFAULT;
+
+ for (j = 0; j < QUEUES_PER_PRIO; j++) {
+ name[9] = '0' + j/10;
+ name[10] = '0' + j - 10*(j/10);
+
+ queue = odp_queue_create(name, ODP_QUEUE_TYPE_SCHED,
+ &param);
+
+ if (queue == ODP_QUEUE_INVALID) {
+ ODP_ERR("Schedule queue create failed.\n");
+ return -1;
+ }
+ }
+ }
+
+ odp_shm_print_all();
+
+ /* Barrier to sync test case execution */
+ odp_barrier_init_count(&test_barrier, num_workers);
+
+ /* Create and launch worker threads */
+ odp_linux_pthread_create(thread_tbl, num_workers, first_core,
+ run_thread, NULL);
+
+ /* Wait for worker threads to exit */
+ odp_linux_pthread_join(thread_tbl, num_workers);
+
+ printf("ODP example complete\n\n");
+
+ return 0;
+}
diff --git a/example/packet/Makefile.am b/example/packet/Makefile.am
new file mode 100644
index 000000000..2d778ddd6
--- /dev/null
+++ b/example/packet/Makefile.am
@@ -0,0 +1,5 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_pktio
+
+dist_odp_pktio_SOURCES = odp_pktio.c
diff --git a/example/packet/odp_pktio.c b/example/packet/odp_pktio.c
new file mode 100644
index 000000000..edf8cfd6f
--- /dev/null
+++ b/example/packet/odp_pktio.c
@@ -0,0 +1,635 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * @example odp_pktio.c ODP basic packet IO loopback test application
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <odp.h>
+#include <helper/odp_linux.h>
+#include <helper/odp_packet_helper.h>
+#include <helper/odp_eth.h>
+#include <helper/odp_ip.h>
+
+#define MAX_WORKERS 32
+#define SHM_PKT_POOL_SIZE (512*2048)
+#define SHM_PKT_POOL_BUF_SIZE 1856
+#define MAX_PKT_BURST 16
+
+#define APPL_MODE_PKT_BURST 0
+#define APPL_MODE_PKT_QUEUE 1
+
+#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
+
+/** Get rid of path in filename - only for unix-type paths using '/' */
+#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
+ strrchr((file_name), '/') + 1 : (file_name))
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+ int core_count;
+ int if_count; /**< Number of interfaces to be used */
+ char **if_names; /**< Array of pointers to interface names */
+ int mode; /**< Packet IO mode */
+ int type; /**< Packet IO type */
+ int fanout; /**< Packet IO fanout */
+ odp_buffer_pool_t pool; /**< Buffer pool for packet IO */
+} appl_args_t;
+
+/**
+ * Thread specific arguments
+ */
+typedef struct {
+ char *pktio_dev; /**< Interface name to use */
+ odp_buffer_pool_t pool; /**< Buffer pool for packet IO */
+ int mode; /**< Thread mode */
+ int type; /**< Thread i/o type */
+ int fanout; /**< Thread i/o fanout */
+} thread_args_t;
+
+/**
+ * Grouping of both parsed CL args and thread specific args - alloc together
+ */
+typedef struct {
+ /** Application (parsed) arguments */
+ appl_args_t appl;
+ /** Thread specific arguments */
+ thread_args_t thread[MAX_WORKERS];
+} args_t;
+
+/** Global pointer to args */
+static args_t *args;
+
+/* helper funcs */
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
+static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len);
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
+static void print_info(char *progname, appl_args_t *appl_args);
+static void usage(char *progname);
+
+/**
+ * Packet IO loopback worker thread using ODP queues
+ *
+ * @param arg thread arguments of type 'thread_args_t *'
+ */
+static void *pktio_queue_thread(void *arg)
+{
+ int thr;
+ odp_buffer_pool_t pkt_pool;
+ odp_pktio_t pktio;
+ thread_args_t *thr_args;
+ odp_queue_t outq_def;
+ odp_queue_t inq_def;
+ char inq_name[ODP_QUEUE_NAME_LEN];
+ odp_queue_param_t qparam;
+ odp_packet_t pkt;
+ odp_buffer_t buf;
+ int ret;
+ unsigned long pkt_cnt = 0;
+ unsigned long err_cnt = 0;
+ odp_pktio_params_t params;
+ socket_params_t *sock_params = &params.sock_params;
+
+ thr = odp_thread_id();
+ thr_args = arg;
+
+ printf("Pktio thread [%02i] starts, pktio_dev:%s\n", thr,
+ thr_args->pktio_dev);
+
+ /* Lookup the packet pool */
+ pkt_pool = odp_buffer_pool_lookup("packet_pool");
+ if (pkt_pool == ODP_BUFFER_POOL_INVALID || pkt_pool != thr_args->pool) {
+ ODP_ERR(" [%02i] Error: pkt_pool not found\n", thr);
+ return NULL;
+ }
+
+ /* Open a packet IO instance for this thread */
+ sock_params->type = thr_args->type;
+ sock_params->fanout = thr_args->fanout;
+ pktio = odp_pktio_open(thr_args->pktio_dev, pkt_pool, &params);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODP_ERR(" [%02i] Error: pktio create failed\n", thr);
+ return NULL;
+ }
+
+ /*
+ * Create and set the default INPUT queue associated with the 'pktio'
+ * resource
+ */
+ qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+ snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def", (int)pktio);
+ inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
+
+ inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam);
+ if (inq_def == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%02i] Error: pktio queue creation failed\n", thr);
+ return NULL;
+ }
+
+ ret = odp_pktio_inq_setdef(pktio, inq_def);
+ if (ret != 0) {
+ ODP_ERR(" [%02i] Error: default input-Q setup\n", thr);
+ return NULL;
+ }
+
+ printf(" [%02i] created pktio:%02i, queue mode (ATOMIC queues)\n"
+ " default pktio%02i-INPUT queue:%u\n",
+ thr, pktio, pktio, inq_def);
+
+ /* Loop packets */
+ for (;;) {
+ odp_pktio_t pktio_tmp;
+
+#if 1
+ /* Use schedule to get buf from any input queue */
+ buf = odp_schedule(NULL, ODP_SCHED_WAIT);
+#else
+ /* Always dequeue from the same input queue */
+ buf = odp_queue_deq(inq_def);
+ if (!odp_buffer_is_valid(buf))
+ continue;
+#endif
+
+ pkt = odp_packet_from_buffer(buf);
+
+ /* Drop packets with errors */
+ if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
+ ODP_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt);
+ continue;
+ }
+
+ pktio_tmp = odp_pktio_get_input(pkt);
+ outq_def = odp_pktio_outq_getdef(pktio_tmp);
+
+ if (outq_def == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%02i] Error: def output-Q query\n", thr);
+ return NULL;
+ }
+
+ /* Swap Eth MACs and possibly IP-addrs before sending back */
+ swap_pkt_addrs(&pkt, 1);
+
+ /* Enqueue the packet for output */
+ odp_queue_enq(outq_def, buf);
+
+ /* Print packet counts every once in a while */
+ if (odp_unlikely(pkt_cnt++ % 100000 == 0)) {
+ printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
+ fflush(NULL);
+ }
+ }
+
+/* unreachable */
+}
+
+/**
+ * Packet IO loopback worker thread using bursts from/to IO resources
+ *
+ * @param arg thread arguments of type 'thread_args_t *'
+ */
+static void *pktio_ifburst_thread(void *arg)
+{
+ int thr;
+ odp_buffer_pool_t pkt_pool;
+ odp_pktio_t pktio;
+ thread_args_t *thr_args;
+ int pkts, pkts_ok;
+ odp_packet_t pkt_tbl[MAX_PKT_BURST];
+ unsigned long pkt_cnt = 0;
+ unsigned long err_cnt = 0;
+ unsigned long tmp = 0;
+ odp_pktio_params_t params;
+ socket_params_t *sock_params = &params.sock_params;
+
+ thr = odp_thread_id();
+ thr_args = arg;
+
+ printf("Pktio thread [%02i] starts, pktio_dev:%s\n", thr,
+ thr_args->pktio_dev);
+
+ /* Lookup the packet pool */
+ pkt_pool = odp_buffer_pool_lookup("packet_pool");
+ if (pkt_pool == ODP_BUFFER_POOL_INVALID || pkt_pool != thr_args->pool) {
+ ODP_ERR(" [%02i] Error: pkt_pool not found\n", thr);
+ return NULL;
+ }
+
+ /* Open a packet IO instance for this thread */
+ sock_params->type = thr_args->type;
+ sock_params->fanout = thr_args->fanout;
+ pktio = odp_pktio_open(thr_args->pktio_dev, pkt_pool, &params);
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODP_ERR(" [%02i] Error: pktio create failed.\n", thr);
+ return NULL;
+ }
+
+ printf(" [%02i] created pktio:%02i, burst mode\n",
+ thr, pktio);
+
+ /* Loop packets */
+ for (;;) {
+ pkts = odp_pktio_recv(pktio, pkt_tbl, MAX_PKT_BURST);
+ if (pkts > 0) {
+ /* Drop packets with errors */
+ pkts_ok = drop_err_pkts(pkt_tbl, pkts);
+ if (pkts_ok > 0) {
+ /* Swap Eth MACs and IP-addrs */
+ swap_pkt_addrs(pkt_tbl, pkts_ok);
+ odp_pktio_send(pktio, pkt_tbl, pkts_ok);
+ }
+
+ if (odp_unlikely(pkts_ok != pkts))
+ ODP_ERR("Dropped frames:%u - err_cnt:%lu\n",
+ pkts-pkts_ok, ++err_cnt);
+
+ /* Print packet counts every once in a while */
+ tmp += pkts_ok;
+ if (odp_unlikely((tmp >= 100000) || /* OR first print:*/
+ ((pkt_cnt == 0) && ((tmp-1) < MAX_PKT_BURST)))) {
+ pkt_cnt += tmp;
+ printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
+ fflush(NULL);
+ tmp = 0;
+ }
+ }
+ }
+
+/* unreachable */
+}
+
+/**
+ * ODP packet example main function
+ */
+int main(int argc, char *argv[])
+{
+ odp_linux_pthread_t thread_tbl[MAX_WORKERS];
+ odp_buffer_pool_t pool;
+ int thr_id;
+ int num_workers;
+ void *pool_base;
+ int i;
+ int first_core;
+ int core_count;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global()) {
+ ODP_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ args = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE);
+ if (args == NULL) {
+ ODP_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(args, 0, sizeof(*args));
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &args->appl);
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), &args->appl);
+
+ core_count = odp_sys_core_count();
+ num_workers = core_count;
+
+ if (args->appl.core_count)
+ num_workers = args->appl.core_count;
+
+ if (num_workers > MAX_WORKERS)
+ num_workers = MAX_WORKERS;
+
+ printf("Num worker threads: %i\n", num_workers);
+
+ /*
+ * By default core #0 runs Linux kernel background tasks.
+ * Start mapping thread from core #1
+ */
+ first_core = 1;
+
+ if (core_count == 1)
+ first_core = 0;
+
+ printf("First core: %i\n\n", first_core);
+
+ /* Init this thread */
+ thr_id = odp_thread_create(0);
+ odp_init_local(thr_id);
+
+ /* Create packet pool */
+ pool_base = odp_shm_reserve("shm_packet_pool",
+ SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE);
+ if (pool_base == NULL) {
+ ODP_ERR("Error: packet pool mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ pool = odp_buffer_pool_create("packet_pool", pool_base,
+ SHM_PKT_POOL_SIZE,
+ SHM_PKT_POOL_BUF_SIZE,
+ ODP_CACHE_LINE_SIZE,
+ ODP_BUFFER_TYPE_PACKET);
+ if (pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ odp_buffer_pool_print(pool);
+
+ /* Create and init worker threads */
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ for (i = 0; i < num_workers; ++i) {
+ void *(*thr_run_func) (void *);
+ int core;
+ int if_idx;
+
+ core = (first_core + i) % core_count;
+
+ if_idx = i % args->appl.if_count;
+
+ args->thread[i].pktio_dev = args->appl.if_names[if_idx];
+ args->thread[i].pool = pool;
+ args->thread[i].mode = args->appl.mode;
+ args->thread[i].type = args->appl.type;
+ args->thread[i].fanout = args->appl.fanout;
+
+ if (args->appl.mode == APPL_MODE_PKT_BURST)
+ thr_run_func = pktio_ifburst_thread;
+ else /* APPL_MODE_PKT_QUEUE */
+ thr_run_func = pktio_queue_thread;
+ /*
+ * Create threads one-by-one instead of all-at-once,
+ * because each thread might get different arguments.
+ * Calls odp_thread_create(cpu) for each thread
+ */
+ odp_linux_pthread_create(thread_tbl, 1, core, thr_run_func,
+ &args->thread[i]);
+ }
+
+ /* Master thread waits for other threads to exit */
+ odp_linux_pthread_join(thread_tbl, num_workers);
+
+ printf("Exit\n\n");
+
+ return 0;
+}
+
+/**
+ * Drop packets which input parsing marked as containing errors.
+ *
+ * Frees packets with error and modifies pkt_tbl[] to only contain packets with
+ * no detected errors.
+ *
+ * @param pkt_tbl Array of packet
+ * @param len Length of pkt_tbl[]
+ *
+ * @return Number of packets with no detected error
+ */
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
+{
+ odp_packet_t pkt;
+ unsigned pkt_cnt = len;
+ unsigned i, j;
+
+ for (i = 0, j = 0; i < len; ++i) {
+ pkt = pkt_tbl[i];
+
+ if (odp_unlikely(odp_packet_error(pkt))) {
+ odp_packet_free(pkt); /* Drop */
+ pkt_cnt--;
+ } else if (odp_unlikely(i != j++)) {
+ pkt_tbl[j-1] = pkt;
+ }
+ }
+
+ return pkt_cnt;
+}
+
+/**
+ * Swap eth src<->dst and IP src<->dst addresses
+ *
+ * @param pkt_tbl Array of packets
+ * @param len Length of pkt_tbl[]
+ */
+
+static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len)
+{
+ odp_packet_t pkt;
+ odp_ethhdr_t *eth;
+ odp_ethaddr_t tmp_addr;
+ odp_ipv4hdr_t *ip;
+ uint32be_t ip_tmp_addr; /* tmp ip addr */
+ unsigned i;
+
+ for (i = 0; i < len; ++i) {
+ pkt = pkt_tbl[i];
+ if (odp_packet_inflag_eth(pkt)) {
+ eth = (odp_ethhdr_t *)odp_packet_l2(pkt);
+
+ tmp_addr = eth->dst;
+ eth->dst = eth->src;
+ eth->src = tmp_addr;
+
+ if (odp_packet_inflag_ipv4(pkt)) {
+ /* IPv4 */
+ ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
+
+ ip_tmp_addr = ip->src_addr;
+ ip->src_addr = ip->dst_addr;
+ ip->dst_addr = ip_tmp_addr;
+ }
+ }
+ }
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ char *names, *str, *token, *save;
+ size_t len;
+ int i;
+ static struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"interface", required_argument, NULL, 'i'}, /* return 'i' */
+ {"mode", required_argument, NULL, 'm'}, /* return 'm' */
+ {"help", no_argument, NULL, 'h'}, /* return 'h' */
+ {NULL, 0, NULL, 0}
+ };
+
+ appl_args->mode = -1; /* Invalid, must be changed by parsing */
+ appl_args->type = 3; /* 3: ODP_PKTIO_TYPE_SOCKET_MMAP */
+ appl_args->fanout = 1; /* turn off fanout by default for mmap */
+
+ while (1) {
+ opt = getopt_long(argc, argv, "+c:i:m:t:f:h",
+ longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ appl_args->core_count = atoi(optarg);
+ break;
+ /* parse packet-io interface names */
+ case 'i':
+ len = strlen(optarg);
+ if (len == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ names = malloc(len);
+ if (names == NULL) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* count the number of tokens separated by ',' */
+ strcpy(names, optarg);
+ for (str = names, i = 0;; str = NULL, i++) {
+ token = strtok_r(str, ",", &save);
+ if (token == NULL)
+ break;
+ }
+ appl_args->if_count = i;
+
+ if (appl_args->if_count == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* allocate storage for the if names */
+ appl_args->if_names =
+ calloc(appl_args->if_count, sizeof(char *));
+
+ /* store the if names (reset names string) */
+ strcpy(names, optarg);
+ for (str = names, i = 0;; str = NULL, i++) {
+ token = strtok_r(str, ",", &save);
+ if (token == NULL)
+ break;
+ appl_args->if_names[i] = token;
+ }
+ break;
+
+ case 'm':
+ i = atoi(optarg);
+ if (i == 0)
+ appl_args->mode = APPL_MODE_PKT_BURST;
+ else
+ appl_args->mode = APPL_MODE_PKT_QUEUE;
+ break;
+
+ case 't':
+ appl_args->type = atoi(optarg);
+ break;
+
+ case 'f':
+ appl_args->fanout = atoi(optarg);
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (appl_args->if_count == 0 || appl_args->mode == -1) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args)
+{
+ int i;
+
+ printf("\n"
+ "ODP system info\n"
+ "---------------\n"
+ "ODP API version: %s\n"
+ "CPU model: %s\n"
+ "CPU freq (hz): %"PRIu64"\n"
+ "Cache line size: %i\n"
+ "Core count: %i\n"
+ "\n",
+ odp_version_api_str(), odp_sys_cpu_model_str(), odp_sys_cpu_hz(),
+ odp_sys_cache_line_size(), odp_sys_core_count());
+
+ printf("Running ODP appl: \"%s\"\n"
+ "-----------------\n"
+ "IF-count: %i\n"
+ "Using IFs: ",
+ progname, appl_args->if_count);
+ for (i = 0; i < appl_args->if_count; ++i)
+ printf(" %s", appl_args->if_names[i]);
+ printf("\n"
+ "Mode: ");
+ if (appl_args->mode == APPL_MODE_PKT_BURST)
+ PRINT_APPL_MODE(APPL_MODE_PKT_BURST);
+ else
+ PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE);
+ printf("\n\n");
+ fflush(NULL);
+}
+
+/**
+ * Prinf usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -i eth1,eth2,eth3 -m 0\n"
+ "\n"
+ "OpenDataPlane example application.\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -i, --interface Eth interfaces (comma-separated, no spaces)\n"
+ " -m, --mode 0: Burst send&receive packets (no queues)\n"
+ " 1: Send&receive packets through ODP queues.\n"
+ " -t, --type 1: ODP_PKTIO_TYPE_SOCKET_BASIC\n"
+ " 2: ODP_PKTIO_TYPE_SOCKET_MMSG\n"
+ " 3: ODP_PKTIO_TYPE_SOCKET_MMAP\n"
+ " 4: ODP_PKTIO_TYPE_NETMAP\n"
+ " Default: 3: ODP_PKTIO_TYPE_SOCKET_MMAP\n"
+ " -f, --fanout 0: off 1: on (Default 1: on)\n"
+ "\n"
+ "Optional OPTIONS\n"
+ " -c, --count <number> Core count.\n"
+ " -h, --help Display help and exit.\n"
+ "\n", NO_PATH(progname), NO_PATH(progname)
+ );
+}
diff --git a/example/packet_netmap/Makefile.am b/example/packet_netmap/Makefile.am
new file mode 100644
index 000000000..6db0f98d5
--- /dev/null
+++ b/example/packet_netmap/Makefile.am
@@ -0,0 +1,7 @@
+include $(top_srcdir)/example/Makefile.inc
+
+if ODP_NETMAP_ENABLED
+bin_PROGRAMS = odp_pktio_netmap
+endif
+
+dist_odp_pktio_netmap_SOURCES = odp_pktio_netmap.c
diff --git a/example/packet_netmap/odp_pktio_netmap.c b/example/packet_netmap/odp_pktio_netmap.c
new file mode 100644
index 000000000..7d33b19b5
--- /dev/null
+++ b/example/packet_netmap/odp_pktio_netmap.c
@@ -0,0 +1,557 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * ODP basic packet IO loopback test application
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <arpa/inet.h>
+
+#include <odp.h>
+#include <helper/odp_linux.h>
+#include <helper/odp_packet_helper.h>
+#include <helper/odp_eth.h>
+#include <helper/odp_ip.h>
+#include <helper/odp_packet_helper.h>
+
+#include <odp_pktio_netmap.h>
+
+#define MAX_WORKERS 32
+#define MAX_IFS 16
+#define SHM_PKT_POOL_SIZE (512*2048)
+#define SHM_PKT_POOL_BUF_SIZE 1856
+#define MAX_PKT_BURST 16
+
+#define PKTIO_MODE_SOCK 0
+#define PKTIO_MODE_NETMAP 1
+
+/** 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))
+
+/**
+ * Interface parameters obatained from app arguments
+ */
+typedef struct {
+ char if_name[32];
+ int pktio_mode; /**< Socket mode or netmap mode */
+} if_info_t;
+
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+ int if_count; /**< Number of interfaces to be used */
+ if_info_t *ifs; /**< Array of interface config options */
+ odp_buffer_pool_t pool; /**< Buffer pool for packet IO */
+} appl_args_t;
+
+/**
+ * Thread specific arguments
+ * In this netmap example, there is a thread polling a network interface
+ * and another thread polling the ring that is used by the software stack
+ * to send packets to the same network interface. Each of the two threads
+ * needs to know which is the output queue corresponding to the other thread
+ * to be able to pass packets between the stack and the nic. This queue is
+ * defined by bridge_q below.
+ */
+typedef struct {
+ odp_pktio_t pktio;
+ odp_buffer_pool_t pool; /**< Buffer pool for packet IO */
+ char *pktio_dev; /**< Interface name to use */
+ int netmap_mode; /**< Either poll the hardware rings or the
+ rings associated with the host stack */
+ odp_queue_t bridge_q; /**< Connect the network stack with the NIC */
+} pktio_info_t;
+
+/**
+ * Grouping of both parsed CL args and thread specific args - alloc together
+ */
+typedef struct {
+ /** Application (parsed) arguments */
+ appl_args_t appl;
+ /** pktio entries: one for SW ring and one for HW ring */
+ pktio_info_t pktios[2 * MAX_IFS];
+ /** TODO: find a way to associate private data with pktios */
+ /** Lookup table: find pktio_info_t based on pktio id */
+ pktio_info_t *pktio_lt[ODP_CONFIG_PKTIO_ENTRIES];
+} args_t;
+
+/** Global pointer to args */
+static args_t *args;
+
+/* helper funcs */
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
+static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len);
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
+static void print_info(char *progname, appl_args_t *appl_args);
+static void usage(char *progname);
+
+/**
+ * Packet IO loopback worker thread using ODP queues
+ *
+ * @param arg thread arguments of type 'thread_args_t *'
+ */
+static void *pktio_queue_thread(void *arg)
+{
+ int thr;
+ odp_buffer_pool_t pkt_pool;
+ odp_packet_t pkt;
+ odp_buffer_t buf;
+ unsigned long pkt_cnt = 0;
+ unsigned long err_cnt = 0;
+
+ (void)arg;
+
+ thr = odp_thread_id();
+ printf("Pktio thread [%02i] starts\n", thr);
+
+ /* Lookup the packet pool */
+ pkt_pool = odp_buffer_pool_lookup("packet_pool");
+ if (pkt_pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR(" [%02i] Error: pkt_pool not found\n", thr);
+ return NULL;
+ }
+
+ /* Loop packets */
+ for (;;) {
+ odp_pktio_t pktio_tmp;
+ odp_queue_t outq_def;
+ pktio_info_t *pktio_info;
+
+ /* Use schedule to get buf from any input queue */
+ buf = odp_schedule(NULL, ODP_SCHED_WAIT);
+
+ pkt = odp_packet_from_buffer(buf);
+
+ /* Drop packets with errors */
+ if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
+ ODP_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt);
+ continue;
+ }
+
+ pktio_tmp = odp_pktio_get_input(pkt);
+ if (pktio_tmp == ODP_PKTIO_INVALID) {
+ ODP_ERR("[%02i] Error: invalid pktio\n", thr);
+ return NULL;
+ }
+
+ outq_def = odp_pktio_outq_getdef(pktio_tmp);
+
+ if (outq_def == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%02i] Error: def output-Q query\n",
+ thr);
+ return NULL;
+ }
+
+ /* Lookup the thread associated with the entry */
+ pktio_info = args->pktio_lt[pktio_tmp];
+
+ /* Send back packets arrived on physical interface */
+ if (pktio_info->netmap_mode == ODP_NETMAP_MODE_HW) {
+ odp_packet_t pkt_copy;
+
+ pkt_copy = odp_packet_alloc(pkt_pool);
+
+ if (odp_packet_copy(pkt_copy, pkt) != 0) {
+ ODP_ERR("Packet copy failed!\n");
+ odp_packet_free(pkt_copy);
+ } else {
+ swap_pkt_addrs(&pkt_copy, 1);
+ odp_queue_enq(outq_def,
+ odp_buffer_from_packet(pkt_copy));
+ }
+ }
+
+ odp_queue_enq(pktio_info->bridge_q, buf);
+
+ /* Print packet counts every once in a while */
+ if (odp_unlikely(pkt_cnt++ % 100000 == 0)) {
+ printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
+ fflush(NULL);
+ }
+ }
+
+/* unreachable */
+}
+
+/**
+ * ODP packet example main function
+ */
+int main(int argc, char *argv[])
+{
+ odp_linux_pthread_t thread_tbl[MAX_WORKERS];
+ odp_buffer_pool_t pool;
+ int thr_id;
+ int num_workers;
+ void *pool_base;
+ int i;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global()) {
+ ODP_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ args = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE);
+ if (args == NULL) {
+ ODP_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(args, 0, sizeof(*args));
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &args->appl);
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), &args->appl);
+
+ num_workers = odp_sys_core_count();
+ if (num_workers > MAX_WORKERS)
+ num_workers = MAX_WORKERS;
+
+ /* Init this thread */
+ thr_id = odp_thread_create(0);
+ odp_init_local(thr_id);
+
+ /* Create packet pool */
+ pool_base = odp_shm_reserve("shm_packet_pool",
+ SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE);
+ if (pool_base == NULL) {
+ ODP_ERR("Error: packet pool mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ pool = odp_buffer_pool_create("packet_pool", pool_base,
+ SHM_PKT_POOL_SIZE,
+ SHM_PKT_POOL_BUF_SIZE,
+ ODP_CACHE_LINE_SIZE,
+ ODP_BUFFER_TYPE_PACKET);
+ if (pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ odp_buffer_pool_print(pool);
+
+ for (i = 0; i < 2 * args->appl.if_count; ++i) {
+ odp_pktio_params_t params;
+ netmap_params_t *nm_params = &params.nm_params;
+ char inq_name[ODP_QUEUE_NAME_LEN];
+ odp_queue_t inq_def;
+ odp_queue_param_t qparam;
+ odp_pktio_t pktio;
+ int ret;
+
+ /* Create a pktio polling the hardware rings and one that polls
+ * the software ring associated with the physical interface
+ */
+
+ args->pktios[i].pktio_dev = args->appl.ifs[i / 2].if_name;
+ memset(nm_params, 0, sizeof(*nm_params));
+ nm_params->type = ODP_PKTIO_TYPE_NETMAP;
+ if (i % 2) {
+ nm_params->netmap_mode = ODP_NETMAP_MODE_SW;
+ nm_params->ringid = 0;
+ } else {
+ nm_params->netmap_mode = ODP_NETMAP_MODE_HW;
+ nm_params->ringid = 0;
+ }
+ pktio = odp_pktio_open(args->pktios[i].pktio_dev,
+ pool, &params);
+ /* Open a packet IO instance for this thread */
+ if (pktio == ODP_PKTIO_INVALID) {
+ ODP_ERR(" [%02i] Err: pktio create\n", i);
+ return -1;
+ }
+
+ args->pktios[i].pktio = pktio;
+ args->pktios[i].pool = pool;
+ args->pktios[i].netmap_mode = nm_params->netmap_mode;
+ /* Save pktio_info in the lookup table */
+ args->pktio_lt[pktio] = &args->pktios[i];
+ /*
+ * Create and set the default INPUT queue associated with the
+ * 'pktio' resource
+ */
+ qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
+ qparam.sched.sync = ODP_SCHED_SYNC_NONE;
+ qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+ snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def",
+ (int)pktio);
+ inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
+
+ inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN,
+ &qparam);
+ if (inq_def == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%02i] Err: pktio q create\n", i);
+ return -1;
+ }
+
+ ret = odp_pktio_inq_setdef(pktio, inq_def);
+ if (ret != 0) {
+ ODP_ERR(" [%02i] Err: default input-Q setup\n"
+ , i);
+ return -1;
+ }
+
+ printf(" [%02i] created pktio:%02i, queue mode\n"
+ " default pktio%02i-INPUT queue:%u\n",
+ i, pktio, pktio, inq_def);
+
+ /* Prepare for bridging: set bridge_q queue ids */
+ if (i % 2) {
+ odp_pktio_t pktio_bridge;
+ odp_queue_t outq_def;
+
+ pktio_bridge = args->pktios[i-1].pktio;
+ outq_def = odp_pktio_outq_getdef(pktio_bridge);
+ args->pktios[i].bridge_q = outq_def;
+
+ pktio_bridge = args->pktios[i].pktio;
+ outq_def = odp_pktio_outq_getdef(pktio_bridge);
+ args->pktios[i-1].bridge_q = outq_def;
+ }
+ }
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ for (i = 0; i < num_workers; ++i) {
+
+ /*
+ * Create threads one-by-one instead of all-at-once,
+ * because each thread might get different arguments
+ */
+ odp_linux_pthread_create(thread_tbl, 1, i, pktio_queue_thread,
+ NULL);
+ }
+
+ /* Master thread waits for other threads to exit */
+ odp_linux_pthread_join(thread_tbl, num_workers);
+
+ printf("Exit\n\n");
+
+ return 0;
+}
+
+/**
+ * Drop packets which input parsing marked as containing errors.
+ *
+ * Frees packets with error and modifies pkt_tbl[] to only contain packets with
+ * no detected errors.
+ *
+ * @param pkt_tbl Array of packet
+ * @param len Length of pkt_tbl[]
+ *
+ * @return Number of packets with no detected error
+ */
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
+{
+ odp_packet_t pkt;
+ unsigned pkt_cnt = len;
+ unsigned i, j;
+
+ for (i = 0, j = 0; i < len; ++i) {
+ pkt = pkt_tbl[i];
+
+ if (odp_unlikely(odp_packet_error(pkt))) {
+ odp_packet_free(pkt); /* Drop */
+ pkt_cnt--;
+ } else if (odp_unlikely(i != j++)) {
+ pkt_tbl[j-1] = pkt;
+ }
+ }
+
+ return pkt_cnt;
+}
+
+/**
+ * Swap eth src<->dst and IP src<->dst addresses
+ *
+ * @param pkt_tbl Array of packets
+ * @param len Length of pkt_tbl[]
+ */
+static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len)
+{
+ odp_packet_t pkt;
+ odp_ethhdr_t *eth;
+ odp_ethaddr_t tmp_addr;
+ odp_ipv4hdr_t *ip;
+ uint32be_t ip_tmp_addr; /* tmp ip addr */
+ unsigned i;
+
+ for (i = 0; i < len; ++i) {
+ pkt = pkt_tbl[i];
+ if (odp_packet_inflag_eth(pkt)) {
+ eth = (odp_ethhdr_t *)odp_packet_l2(pkt);
+
+ tmp_addr = eth->dst;
+ eth->dst = eth->src;
+ eth->src = tmp_addr;
+
+ if (odp_packet_inflag_ipv4(pkt)) {
+ /* IPv4 */
+ ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
+
+ ip_tmp_addr = ip->src_addr;
+ ip->src_addr = ip->dst_addr;
+ ip->dst_addr = ip_tmp_addr;
+ }
+ }
+ }
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ char *names, *str, *token, *save;
+ size_t len;
+ int i;
+ static struct option longopts[] = {
+ {"interface", required_argument, NULL, 'i'}, /* return 'i' */
+ {"help", no_argument, NULL, 'h'}, /* return 'h' */
+ {NULL, 0, NULL, 0}
+ };
+
+ while (1) {
+ opt = getopt_long(argc, argv, "+i:h", longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ /* parse packet-io interface names */
+ case 'i':
+ len = strlen(optarg);
+ if (len == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ names = malloc(len);
+ if (names == NULL) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* count the number of tokens separated by ',' */
+ strcpy(names, optarg);
+ for (str = names, i = 0;; str = NULL, i++) {
+ token = strtok_r(str, ",", &save);
+ if (token == NULL)
+ break;
+ }
+ appl_args->if_count = i;
+
+ if (appl_args->if_count == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* allocate storage for the if names */
+ appl_args->ifs =
+ calloc(appl_args->if_count, sizeof(if_info_t));
+
+ /* store the if names (reset names string) */
+ strcpy(names, optarg);
+ for (str = names, i = 0;; str = NULL, i++) {
+ token = strtok_r(str, ",", &save);
+ if (token == NULL)
+ break;
+ strncpy(appl_args->ifs[i].if_name, token,
+ sizeof(appl_args->ifs[i].if_name));
+ appl_args->ifs[i].pktio_mode =
+ PKTIO_MODE_NETMAP;
+ }
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (appl_args->if_count == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args)
+{
+ int i;
+
+ printf("\n"
+ "ODP system info\n"
+ "---------------\n"
+ "ODP API version: %s\n"
+ "CPU model: %s\n"
+ "CPU freq (hz): %"PRIu64"\n"
+ "Cache line size: %i\n"
+ "Core count: %i\n"
+ "\n",
+ odp_version_api_str(), odp_sys_cpu_model_str(), odp_sys_cpu_hz(),
+ odp_sys_cache_line_size(), odp_sys_core_count()
+ );
+ printf("Running ODP appl: \"%s\"\n"
+ "-----------------\n"
+ "IF-count: %i\n"
+ "Using IFs: ",
+ progname, appl_args->if_count);
+ for (i = 0; i < appl_args->if_count; ++i)
+ printf(" %s", appl_args->ifs[i].if_name);
+ printf("\n"
+ "Mode: ");
+ printf("\n\n");
+ fflush(NULL);
+}
+
+/**
+ * Prinf usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -i eth1,eth2,eth3\n"
+ "\n"
+ "OpenDataPlane example application.\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -i, --interface Eth interfaces (comma-separated, no spaces)\n"
+ "\n"
+ "Optional OPTIONS\n"
+ " -h, --help Display help and exit.\n"
+ "\n", NO_PATH(progname), NO_PATH(progname)
+ );
+}
diff --git a/example/timer/Makefile.am b/example/timer/Makefile.am
new file mode 100644
index 000000000..09253a6f0
--- /dev/null
+++ b/example/timer/Makefile.am
@@ -0,0 +1,5 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_timer_test
+
+dist_odp_timer_test_SOURCES = odp_timer_test.c
diff --git a/example/timer/odp_timer_test.c b/example/timer/odp_timer_test.c
new file mode 100644
index 000000000..dbe0e5bed
--- /dev/null
+++ b/example/timer/odp_timer_test.c
@@ -0,0 +1,329 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * @example odp_timer_test.c ODP timer example application
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+/* ODP main header */
+#include <odp.h>
+
+/* ODP helper for Linux apps */
+#include <helper/odp_linux.h>
+
+/* GNU lib C */
+#include <getopt.h>
+
+
+#define MAX_WORKERS 32 /**< Max worker threads */
+#define MSG_POOL_SIZE (4*1024*1024) /**< Message pool size */
+
+/** Test arguments */
+typedef struct {
+ int core_count; /**< Core count*/
+} test_args_t;
+
+
+/** @private Barrier for test synchronisation */
+static odp_barrier_t test_barrier;
+
+/** @private Timer handle*/
+static odp_timer_t test_timer;
+
+
+/** @private test timeout */
+static void test_timeouts(int thr)
+{
+ uint64_t tick;
+ odp_queue_t queue;
+ odp_buffer_t buf;
+ int num = 10;
+
+ ODP_DBG(" [%i] test_timeouts\n", thr);
+
+ queue = odp_queue_lookup("timer_queue");
+
+ tick = odp_timer_current_tick(test_timer);
+
+ tick += 100;
+
+ odp_timer_absolute_tmo(test_timer, tick,
+ queue, ODP_BUFFER_INVALID);
+
+ ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick);
+
+ while (1) {
+ odp_timeout_t tmo;
+
+ buf = odp_schedule_one(&queue, ODP_SCHED_WAIT);
+
+ tmo = odp_timeout_from_buffer(buf);
+ tick = odp_timeout_tick(tmo);
+
+ ODP_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, tick);
+
+ odp_buffer_free(buf);
+
+ num--;
+
+ if (num == 0)
+ break;
+
+ tick += 100;
+
+ odp_timer_absolute_tmo(test_timer, tick,
+ queue, ODP_BUFFER_INVALID);
+ }
+
+ if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC)
+ odp_schedule_release_atomic();
+}
+
+
+/**
+ * @internal Worker thread
+ *
+ * @param arg Arguments
+ *
+ * @return NULL on failure
+ */
+static void *run_thread(void *arg)
+{
+ int thr;
+ odp_buffer_pool_t msg_pool;
+
+ thr = odp_thread_id();
+
+ printf("Thread %i starts on core %i\n", thr, odp_thread_core());
+
+ /*
+ * Test barriers back-to-back
+ */
+ odp_barrier_sync(&test_barrier);
+ odp_barrier_sync(&test_barrier);
+ odp_barrier_sync(&test_barrier);
+ odp_barrier_sync(&test_barrier);
+
+ /*
+ * Find the buffer pool
+ */
+ msg_pool = odp_buffer_pool_lookup("msg_pool");
+
+ if (msg_pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR(" [%i] msg_pool not found\n", thr);
+ return NULL;
+ }
+
+ odp_barrier_sync(&test_barrier);
+
+ test_timeouts(thr);
+
+
+ printf("Thread %i exits\n", thr);
+ fflush(NULL);
+ return arg;
+}
+
+
+/**
+ * @internal Print help
+ */
+static void print_usage(void)
+{
+ printf("\n\nUsage: ./odp_example [options]\n");
+ printf("Options:\n");
+ printf(" -c, --count <number> core count, core IDs start from 1\n");
+ printf(" -h, --help this help\n");
+ printf("\n\n");
+}
+
+
+/**
+ * @internal Parse arguments
+ *
+ * @param argc Argument count
+ * @param argv Argument vector
+ * @param args Test arguments
+ */
+static void parse_args(int argc, char *argv[], test_args_t *args)
+{
+ int opt;
+ int long_index;
+
+ static struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ while (1) {
+ opt = getopt_long(argc, argv, "+c:h", longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ args->core_count = atoi(optarg);
+ break;
+
+ case 'h':
+ print_usage();
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+/**
+ * Test main function
+ */
+int main(int argc, char *argv[])
+{
+ odp_linux_pthread_t thread_tbl[MAX_WORKERS];
+ test_args_t args;
+ int thr_id;
+ int num_workers;
+ odp_buffer_pool_t pool;
+ void *pool_base;
+ odp_queue_t queue;
+ int first_core;
+ uint64_t cycles, ns;
+ odp_queue_param_t param;
+
+ printf("\nODP example starts\n");
+
+ memset(&args, 0, sizeof(args));
+ parse_args(argc, argv, &args);
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+
+ if (odp_init_global()) {
+ printf("ODP global init failed.\n");
+ return -1;
+ }
+
+ printf("\n");
+ printf("ODP system info\n");
+ printf("---------------\n");
+ printf("ODP API version: %s\n", odp_version_api_str());
+ printf("CPU model: %s\n", odp_sys_cpu_model_str());
+ printf("CPU freq (hz): %"PRIu64"\n", odp_sys_cpu_hz());
+ printf("Cache line size: %i\n", odp_sys_cache_line_size());
+ printf("Max core count: %i\n", odp_sys_core_count());
+
+ printf("\n");
+
+ /* A worker thread per core */
+ num_workers = odp_sys_core_count();
+
+ if (args.core_count)
+ num_workers = args.core_count;
+
+ /* force to max core count */
+ if (num_workers > MAX_WORKERS)
+ num_workers = MAX_WORKERS;
+
+ printf("num worker threads: %i\n", num_workers);
+
+ /*
+ * By default core #0 runs Linux kernel background tasks.
+ * Start mapping thread from core #1
+ */
+ first_core = 1;
+
+ if (odp_sys_core_count() == 1)
+ first_core = 0;
+
+ printf("first core: %i\n", first_core);
+
+ /*
+ * Init this thread. It makes also ODP calls when
+ * setting up resources for worker threads.
+ */
+ thr_id = odp_thread_create(0);
+ odp_init_local(thr_id);
+
+ /*
+ * Create message pool
+ */
+ pool_base = odp_shm_reserve("msg_pool",
+ MSG_POOL_SIZE, ODP_CACHE_LINE_SIZE);
+
+ pool = odp_buffer_pool_create("msg_pool", pool_base, MSG_POOL_SIZE,
+ 0,
+ ODP_CACHE_LINE_SIZE,
+ ODP_BUFFER_TYPE_TIMEOUT);
+
+ if (pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR("Pool create failed.\n");
+ return -1;
+ }
+
+ /*
+ * Create a queue for timer test
+ */
+ memset(&param, 0, sizeof(param));
+ param.sched.prio = ODP_SCHED_PRIO_DEFAULT;
+ param.sched.sync = ODP_SCHED_SYNC_NONE;
+ param.sched.group = ODP_SCHED_GROUP_DEFAULT;
+
+ queue = odp_queue_create("timer_queue", ODP_QUEUE_TYPE_SCHED, &param);
+
+ if (queue == ODP_QUEUE_INVALID) {
+ ODP_ERR("Timer queue create failed.\n");
+ return -1;
+ }
+
+ test_timer = odp_timer_create("test_timer", pool,
+ 1000000, 1000000, 1000000000000UL);
+
+
+ odp_shm_print_all();
+
+ printf("CPU freq %"PRIu64" hz\n", odp_sys_cpu_hz());
+ printf("Cycles vs nanoseconds:\n");
+ ns = 0;
+ cycles = odp_time_ns_to_cycles(ns);
+
+ printf(" %12"PRIu64" ns -> %12"PRIu64" cycles\n", ns, cycles);
+ printf(" %12"PRIu64" cycles -> %12"PRIu64" ns\n", cycles,
+ odp_time_cycles_to_ns(cycles));
+
+ for (ns = 1; ns <= 100000000000UL; ns *= 10) {
+ cycles = odp_time_ns_to_cycles(ns);
+
+ printf(" %12"PRIu64" ns -> %12"PRIu64" cycles\n", ns,
+ cycles);
+ printf(" %12"PRIu64" cycles -> %12"PRIu64" ns\n", cycles,
+ odp_time_cycles_to_ns(cycles));
+ }
+
+ printf("\n");
+
+ /* Barrier to sync test case execution */
+ odp_barrier_init_count(&test_barrier, num_workers);
+
+ /* Create and launch worker threads */
+ odp_linux_pthread_create(thread_tbl, num_workers, first_core,
+ run_thread, NULL);
+
+ /* Wait for worker threads to exit */
+ odp_linux_pthread_join(thread_tbl, num_workers);
+
+ printf("ODP timer test complete\n\n");
+
+ return 0;
+}