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