Add new scheduling latency benchmark application. The application
measures delays (avg, min, max) for high and low priority event.

The application's command line arguments enable configuring:
- Number of processing threads
- Number of high/low priority queues
- Number of high/low priority events
- Scheduled queue type (PARALLEL, ATOMIC, ORDERED)

Signed-off-by: Matias Elo <[email protected]>
---
 test/common_plat/performance/.gitignore          |   1 +
 test/common_plat/performance/Makefile.am         |   4 +
 test/common_plat/performance/odp_sched_latency.c | 768 +++++++++++++++++++++++
 3 files changed, 773 insertions(+)
 create mode 100644 test/common_plat/performance/odp_sched_latency.c

diff --git a/test/common_plat/performance/.gitignore 
b/test/common_plat/performance/.gitignore
index edcc832..1527d25 100644
--- a/test/common_plat/performance/.gitignore
+++ b/test/common_plat/performance/.gitignore
@@ -4,4 +4,5 @@ odp_atomic
 odp_crypto
 odp_l2fwd
 odp_pktio_perf
+odp_sched_latency
 odp_scheduling
diff --git a/test/common_plat/performance/Makefile.am 
b/test/common_plat/performance/Makefile.am
index d23bb3e..f5dd8dd 100644
--- a/test/common_plat/performance/Makefile.am
+++ b/test/common_plat/performance/Makefile.am
@@ -5,6 +5,7 @@ TESTS_ENVIRONMENT += TEST_DIR=${builddir}
 EXECUTABLES = odp_crypto$(EXEEXT) odp_pktio_perf$(EXEEXT)
 
 COMPILE_ONLY = odp_l2fwd$(EXEEXT) \
+              odp_sched_latency$(EXEEXT) \
               odp_scheduling$(EXEEXT)
 
 TESTSCRIPTS = odp_l2fwd_run.sh \
@@ -20,6 +21,8 @@ bin_PROGRAMS = $(EXECUTABLES) $(COMPILE_ONLY)
 
 odp_crypto_LDFLAGS = $(AM_LDFLAGS) -static
 odp_crypto_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
+odp_sched_latency_LDFLAGS = $(AM_LDFLAGS) -static
+odp_sched_latency_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
 odp_scheduling_LDFLAGS = $(AM_LDFLAGS) -static
 odp_scheduling_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test
 
@@ -27,6 +30,7 @@ noinst_HEADERS = \
                  $(top_srcdir)/test/test_debug.h
 
 dist_odp_crypto_SOURCES = odp_crypto.c
+dist_odp_sched_latency_SOURCES = odp_sched_latency.c
 dist_odp_scheduling_SOURCES = odp_scheduling.c
 dist_odp_pktio_perf_SOURCES = odp_pktio_perf.c
 
diff --git a/test/common_plat/performance/odp_sched_latency.c 
b/test/common_plat/performance/odp_sched_latency.c
new file mode 100644
index 0000000..d8e5d82
--- /dev/null
+++ b/test/common_plat/performance/odp_sched_latency.c
@@ -0,0 +1,768 @@
+/* Copyright (c) 2016, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * @example odp_sched_latency.c  ODP scheduling latency benchmark application
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <test_debug.h>
+
+/* ODP main header */
+#include <odp_api.h>
+
+/* ODP helper for Linux apps */
+#include <odp/helper/linux.h>
+
+/* GNU lib C */
+#include <getopt.h>
+
+#define MAX_WORKERS      64            /**< Maximum number of worker threads */
+#define MAX_QUEUES       4096          /**< Maximum number of queues */
+#define EVENT_POOL_SIZE          (1024 * 1024) /**< Event pool size */
+#define TEST_ROUNDS (4 * 1024 * 1024)  /**< Test rounds for each thread */
+#define MAIN_THREAD       1 /**< Thread ID performing maintenance tasks */
+
+/* Default values for command line arguments */
+#define SAMPLE_EVENT_PER_PRIO    0 /**< Allocate a separate sample event for
+                                        each priority */
+#define HI_PRIO_EVENTS           0 /**< Number of high priority events */
+#define LO_PRIO_EVENTS          32 /**< Number of low priority events */
+#define HI_PRIO_QUEUES          16 /**< Number of high priority queues */
+#define LO_PRIO_QUEUES          64 /**< Number of low priority queues */
+
+#define EVENTS_PER_HI_PRIO_QUEUE 0  /**< Alloc HI_PRIO_QUEUES x HI_PRIO_EVENTS
+                                        events */
+#define EVENTS_PER_LO_PRIO_QUEUE 1  /**< Alloc LO_PRIO_QUEUES x LO_PRIO_EVENTS
+                                        events */
+ODP_STATIC_ASSERT(HI_PRIO_QUEUES <= MAX_QUEUES, "Too many HI priority queues");
+ODP_STATIC_ASSERT(LO_PRIO_QUEUES <= MAX_QUEUES, "Too many LO priority queues");
+
+#define CACHE_ALIGN_ROUNDUP(x)\
+       ((ODP_CACHE_LINE_SIZE) * \
+        (((x) + ODP_CACHE_LINE_SIZE - 1) / (ODP_CACHE_LINE_SIZE)))
+
+/* Test priorities */
+#define NUM_PRIOS 2 /**< Number of tested priorities */
+#define HI_PRIO          0
+#define LO_PRIO          1
+
+/** Test event types */
+typedef enum {
+       WARM_UP, /**< Warm up event */
+       TRAFFIC, /**< Event used only as traffic load */
+       SAMPLE   /**< Event used to measure latency */
+} event_type_t;
+
+/** Test event */
+typedef struct {
+       uint64_t ts;            /**< Send timestamp */
+       event_type_t type;      /**< Message type */
+       int src_idx[NUM_PRIOS]; /**< Source ODP queue */
+       int prio;               /**< Source queue priority */
+} test_event_t;
+
+/** Test arguments */
+typedef struct {
+       int cpu_count;                  /**< CPU count */
+       odp_schedule_sync_t sync_type;  /**< Scheduler sync type */
+       struct {
+               int queues;     /**< Number of scheduling queues */
+               int events;     /**< Number of events */
+               odp_bool_t events_per_queue; /**< Allocate 'queues' x 'events'
+                                                 test events */
+       } prio[NUM_PRIOS];
+       odp_bool_t sample_per_prio; /**< Allocate a separate sample event for
+                                        each priority */
+} test_args_t;
+
+/** Latency measurements statistics */
+typedef struct {
+       uint64_t events;   /**< Total number of received events */
+       uint64_t sample_events;  /**< Number of received sample events */
+       uint64_t tot;      /**< Total event latency. Sum of all events. */
+       uint64_t min;      /**< Minimum event latency */
+       uint64_t max;      /**< Maximum event latency */
+} test_stat_t;
+
+/** Performance test statistics (per core) */
+typedef union {
+       test_stat_t prio[NUM_PRIOS]; /**< Test statistics per priority */
+
+       uint8_t pad[CACHE_ALIGN_ROUNDUP(NUM_PRIOS * sizeof(test_stat_t))];
+} core_stat_t ODP_ALIGNED_CACHE;
+
+/** Test global variables */
+typedef struct {
+       core_stat_t      core_stat[MAX_WORKERS]; /**< Core specific stats */
+       odp_barrier_t    barrier; /**< Barrier for thread synchronization */
+       odp_pool_t       pool;    /**< Pool for allocating test events */
+       test_args_t      args;    /**< Parsed command line arguments */
+       odp_queue_t      queue[NUM_PRIOS][MAX_QUEUES]; /**< Scheduled queues */
+} test_globals_t;
+
+/**
+ * Clear all scheduled queues.
+ *
+ * Retry to be sure that all buffers have been scheduled.
+ */
+static void clear_sched_queues(void)
+{
+       odp_event_t ev;
+
+       while (1) {
+               ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
+
+               if (ev == ODP_EVENT_INVALID)
+                       break;
+
+               odp_event_free(ev);
+       }
+}
+
+/**
+ * Enqueue events into queues
+ *
+ * @param prio        Queue priority (HI_PRIO/LO_PRIO)
+ * @param num_queues  Number of queues
+ * @param num_events  Number of 'TRAFFIC' events
+ * @param num_samples Number of 'SAMPLE' events
+ * @param div_events  If true, divide 'num_events' between 'num_queues'. if
+ *                   false, enqueue 'num_events' to each queue.
+ * @param globals     Test shared data
+ *
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+static int enqueue_events(int prio, int num_queues, int num_events,
+                         int num_samples, odp_bool_t div_events,
+                         test_globals_t *globals)
+{
+       odp_buffer_t buf[num_events + num_samples];
+       odp_event_t ev[num_events + num_samples];
+       odp_queue_t queue;
+       test_event_t *event;
+       int i, j, ret;
+       int enq_events;
+       int events_per_queue;
+       int tot_events;
+       int rdy_events = 0;
+
+       tot_events = num_events + num_samples;
+
+       if (!num_queues || !tot_events)
+               return 0;
+
+       events_per_queue = tot_events;
+       if (div_events)
+               events_per_queue = (tot_events + num_queues - 1) / num_queues;
+
+       for (i = 0; i < num_queues; i++) {
+               queue = globals->queue[prio][i];
+
+               ret = odp_buffer_alloc_multi(globals->pool, buf,
+                                            events_per_queue);
+               if (ret != events_per_queue) {
+                       LOG_ERR("Buffer alloc failed. Try increasing 
EVENT_POOL_SIZE.\n");
+                       ret = ret < 0 ? 0 : ret;
+                       odp_buffer_free_multi(buf, ret);
+                       return -1;
+               }
+               for (j = 0; j < events_per_queue; j++) {
+                       if (!odp_buffer_is_valid(buf[j])) {
+                               LOG_ERR("Buffer alloc failed\n");
+                               odp_buffer_free_multi(buf, events_per_queue);
+                               return -1;
+                       }
+
+                       event = odp_buffer_addr(buf[j]);
+                       memset(event, 0, sizeof(test_event_t));
+
+                       /* Latency isn't measured from the first processing
+                        * round. */
+                       if (num_samples > 0) {
+                               event->type = WARM_UP;
+                               num_samples--;
+                       } else {
+                               event->type = TRAFFIC;
+                       }
+                       event->src_idx[prio] = i;
+                       event->prio = prio;
+                       ev[j] = odp_buffer_to_event(buf[j]);
+               }
+
+               enq_events = 0;
+               do {
+                       ret = odp_queue_enq_multi(queue, &ev[enq_events],
+                                                 events_per_queue -
+                                                 enq_events);
+                       if (ret < 0) {
+                               LOG_ERR("Queue enqueue failed.\n");
+                               return -1;
+                       }
+                       enq_events += ret;
+               } while (enq_events < events_per_queue);
+
+               rdy_events += events_per_queue;
+               if (div_events && rdy_events >= tot_events)
+                       return 0;
+       }
+       return 0;
+}
+
+/**
+ * Print latency measurement results
+ *
+ * @param globals  Test shared data
+ */
+static void print_results(test_globals_t *globals)
+{
+       test_stat_t *lat;
+       odp_schedule_sync_t stype;
+       test_stat_t total;
+       test_args_t *args;
+       uint64_t avg;
+       int i, j;
+
+       args = &globals->args;
+       stype = globals->args.sync_type;
+
+       printf("\n%s queue scheduling latency\n",
+              (stype == ODP_SCHED_SYNC_ATOMIC) ? "ATOMIC" :
+              ((stype == ODP_SCHED_SYNC_ORDERED) ? "ORDERED" : "PARALLEL"));
+
+       printf("  LO_PRIO queues: %i\n", args->prio[LO_PRIO].queues);
+       if (args->prio[LO_PRIO].events_per_queue)
+               printf("  LO_PRIO event per queue: %i\n",
+                      args->prio[LO_PRIO].events);
+       else
+               printf("  LO_PRIO events: %i\n", args->prio[LO_PRIO].events);
+
+       printf("  HI_PRIO queues: %i\n", args->prio[HI_PRIO].queues);
+       if (args->prio[HI_PRIO].events_per_queue)
+               printf("  HI_PRIO event per queue: %i\n\n",
+                      args->prio[HI_PRIO].events);
+       else
+               printf("  HI_PRIO events: %i\n\n", args->prio[HI_PRIO].events);
+
+       for (i = 0; i < NUM_PRIOS; i++) {
+               memset(&total, 0, sizeof(test_stat_t));
+               total.min = UINT64_MAX;
+
+               printf("%s priority\n"
+                      "Thread   Avg[ns]    Min[ns]    Max[ns]    Samples    
Total\n"
+                      
"---------------------------------------------------------------\n",
+                      i == HI_PRIO ? "HIGH" : "LOW");
+               for (j = 1; j <= args->cpu_count; j++) {
+                       lat = &globals->core_stat[j].prio[i];
+
+                       if (lat->sample_events == 0) {
+                               printf("%-8d N/A\n", j);
+                               continue;
+                       }
+
+                       if (lat->max > total.max)
+                               total.max = lat->max;
+                       if (lat->min < total.min)
+                               total.min = lat->min;
+                       total.tot += lat->tot;
+                       total.sample_events += lat->sample_events;
+                       total.events += lat->events;
+
+                       avg = lat->events ? lat->tot / lat->sample_events : 0;
+                       printf("%-8d %-10" PRIu64 " %-10" PRIu64 " "
+                              "%-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 "\n",
+                              j, avg, lat->min, lat->max, lat->sample_events,
+                              lat->events);
+               }
+               
printf("---------------------------------------------------------------\n");
+               if (total.sample_events == 0) {
+                       printf("Total    N/A\n\n");
+                       continue;
+               }
+               avg = total.events ? total.tot / total.sample_events : 0;
+               printf("Total    %-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 " "
+                      "%-10" PRIu64 " %-10" PRIu64 "\n\n", avg, total.min,
+                      total.max, total.sample_events, total.events);
+       }
+}
+
+/**
+ * Measure latency of scheduled ODP events
+ *
+ * Schedule and enqueue events until 'TEST_ROUNDS' events have been processed.
+ * Scheduling latency is measured only from type 'SAMPLE' events. Other events
+ * are simply enqueued back to the scheduling queues.
+ *
+ * For 'TRAFFIC' type events the destination queue is selected from the same
+ * priority class as source queue. 'SAMPLE' type event may change priority
+ * depending on the command line arguments.
+ *
+ * @param thr      Thread ID
+ * @param globals  Test shared data
+ *
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+static int test_schedule(int thr, test_globals_t *globals)
+{
+       odp_event_t ev;
+       odp_buffer_t buf;
+       odp_queue_t src_queue;
+       odp_queue_t dst_queue;
+       uint64_t latency;
+       uint32_t i;
+       test_event_t *event;
+       test_stat_t *stats;
+       int dst_idx;
+
+       memset(&globals->core_stat[thr], 0, sizeof(core_stat_t));
+       globals->core_stat[thr].prio[HI_PRIO].min = UINT64_MAX;
+       globals->core_stat[thr].prio[LO_PRIO].min = UINT64_MAX;
+
+       for (i = 0; i < TEST_ROUNDS; i++) {
+               ev = odp_schedule(&src_queue, ODP_SCHED_WAIT);
+
+               buf = odp_buffer_from_event(ev);
+               event = odp_buffer_addr(buf);
+
+               stats = &globals->core_stat[thr].prio[event->prio];
+
+               if (event->type == SAMPLE) {
+                       latency = odp_time_to_ns(odp_time_global()) - event->ts;
+
+                       if (latency > stats->max)
+                               stats->max = latency;
+                       if (latency < stats->min)
+                               stats->min = latency;
+                       stats->tot += latency;
+                       stats->sample_events++;
+
+                       /* Move sample event to a different priority */
+                       if (!globals->args.sample_per_prio &&
+                           globals->args.prio[!event->prio].queues)
+                               event->prio = !event->prio;
+               }
+
+               if (odp_unlikely(event->type == WARM_UP))
+                       event->type = SAMPLE;
+               else
+                       stats->events++;
+
+               /* Move event to next queue */
+               dst_idx = event->src_idx[event->prio] + 1;
+               if (dst_idx >= globals->args.prio[event->prio].queues)
+                       dst_idx = 0;
+               event->src_idx[event->prio] = dst_idx;
+               dst_queue = globals->queue[event->prio][dst_idx];
+
+               if (event->type == SAMPLE)
+                       event->ts = odp_time_to_ns(odp_time_global());
+
+               if (odp_queue_enq(dst_queue, ev)) {
+                       LOG_ERR("[%i] Queue enqueue failed.\n", thr);
+                       odp_event_free(ev);
+                       return -1;
+               }
+       }
+
+       /* Clear possible locally stored buffers */
+       odp_schedule_pause();
+
+       while (1) {
+               ev = odp_schedule(&src_queue, ODP_SCHED_NO_WAIT);
+
+               if (ev == ODP_EVENT_INVALID)
+                       break;
+
+               if (odp_queue_enq(src_queue, ev)) {
+                       LOG_ERR("[%i] Queue enqueue failed.\n", thr);
+                       odp_event_free(ev);
+                       return -1;
+               }
+       }
+
+       odp_schedule_resume();
+
+       odp_barrier_wait(&globals->barrier);
+
+       clear_sched_queues();
+
+       if (thr == MAIN_THREAD)
+               print_results(globals);
+
+       return 0;
+}
+
+/**
+ * Worker thread
+ *
+ * @param arg  Arguments
+ *
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+static int run_thread(void *arg ODP_UNUSED)
+{
+       odp_shm_t shm;
+       test_globals_t *globals;
+       test_args_t *args;
+       int thr;
+       int sample_events = 0;
+
+       thr = odp_thread_id();
+
+       shm     = odp_shm_lookup("test_globals");
+       globals = odp_shm_addr(shm);
+
+       if (globals == NULL) {
+               LOG_ERR("Shared mem lookup failed\n");
+               return -1;
+       }
+
+       if (thr == MAIN_THREAD) {
+               args = &globals->args;
+
+               if (enqueue_events(HI_PRIO, args->prio[HI_PRIO].queues,
+                                  args->prio[HI_PRIO].events, 1,
+                                  !args->prio[HI_PRIO].events_per_queue,
+                                  globals))
+                       return -1;
+
+               if (!args->prio[HI_PRIO].queues || args->sample_per_prio)
+                       sample_events = 1;
+
+               if (enqueue_events(LO_PRIO, args->prio[LO_PRIO].queues,
+                                  args->prio[LO_PRIO].events, sample_events,
+                                  !args->prio[LO_PRIO].events_per_queue,
+                                  globals))
+                       return -1;
+       }
+
+       odp_barrier_wait(&globals->barrier);
+
+       if (test_schedule(thr, globals))
+               return -1;
+
+       return 0;
+}
+
+/**
+ * Print usage information
+ */
+static void usage(void)
+{
+       printf("\n"
+              "OpenDataPlane scheduler latency benchmark application.\n"
+              "\n"
+              "Usage: ./odp_sched_latency [options]\n"
+              "Optional OPTIONS:\n"
+              "  -c, --count <number> CPU count\n"
+              "  -l, --lo-prio-queues <number> Number of low priority 
scheduled queues\n"
+              "  -t, --hi-prio-queues <number> Number of high priority 
scheduled queues\n"
+              "  -m, --lo-prio-events-per-queue <number> Number of events per 
low priority queue\n"
+              "  -n, --hi-prio-events-per-queue <number> Number of events per 
high priority queues\n"
+              "  -o, --lo-prio-events <number> Total number of low priority 
events (overrides the\n"
+              "                                number of events per queue)\n"
+              "  -p, --hi-prio-events <number> Total number of high priority 
events (overrides the\n"
+              "                                number of events per queue)\n"
+              "  -r  --sample-per-prio Allocate a separate sample event for 
each priority. By default\n"
+              "                        a single sample event is used and its 
priority is changed after\n"
+              "                        each processing round.\n"
+              "  -s, --sync  Scheduled queues' sync type\n"
+              "               0: ODP_SCHED_SYNC_PARALLEL (default)\n"
+              "               1: ODP_SCHED_SYNC_ATOMIC\n"
+              "               2: ODP_SCHED_SYNC_ORDERED\n"
+              "  -h, --help   Display help and exit.\n\n"
+              );
+}
+
+/**
+ * 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;
+       int i;
+
+       static const struct option longopts[] = {
+               {"count", required_argument, NULL, 'c'},
+               {"lo-prio-queues", required_argument, NULL, 'l'},
+               {"hi-prio-queues", required_argument, NULL, 't'},
+               {"lo-prio-events-per-queue", required_argument, NULL, 'm'},
+               {"hi-prio-events-per-queue", required_argument, NULL, 'n'},
+               {"lo-prio-events", required_argument, NULL, 'o'},
+               {"hi-prio-events", required_argument, NULL, 'p'},
+               {"sample-per-prio", no_argument, NULL, 'r'},
+               {"sync", required_argument, NULL, 's'},
+               {"help", no_argument, NULL, 'h'},
+               {NULL, 0, NULL, 0}
+       };
+
+       static const char *shortopts = "+c:s:l:t:m:n:o:p:rh";
+
+       /* Let helper collect its own arguments (e.g. --odph_proc) */
+       odph_parse_options(argc, argv, shortopts, longopts);
+
+       args->sync_type = ODP_SCHED_SYNC_PARALLEL;
+       args->sample_per_prio = SAMPLE_EVENT_PER_PRIO;
+       args->prio[LO_PRIO].queues = LO_PRIO_QUEUES;
+       args->prio[HI_PRIO].queues = HI_PRIO_QUEUES;
+       args->prio[LO_PRIO].events = LO_PRIO_EVENTS;
+       args->prio[HI_PRIO].events = HI_PRIO_EVENTS;
+       args->prio[LO_PRIO].events_per_queue = EVENTS_PER_LO_PRIO_QUEUE;
+       args->prio[HI_PRIO].events_per_queue = EVENTS_PER_HI_PRIO_QUEUE;
+
+       opterr = 0; /* Do not issue errors on helper options */
+       while (1) {
+               opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+               if (opt == -1)
+                       break;  /* No more options */
+
+               switch (opt) {
+               case 'c':
+                       args->cpu_count = atoi(optarg);
+                       break;
+               case 'l':
+                       args->prio[LO_PRIO].queues = atoi(optarg);
+                       break;
+               case 't':
+                       args->prio[HI_PRIO].queues = atoi(optarg);
+                       break;
+               case 'm':
+                       args->prio[LO_PRIO].events = atoi(optarg);
+                       args->prio[LO_PRIO].events_per_queue = 1;
+                       break;
+               case 'n':
+                       args->prio[HI_PRIO].events = atoi(optarg);
+                       args->prio[HI_PRIO].events_per_queue = 1;
+                       break;
+               case 'o':
+                       args->prio[LO_PRIO].events = atoi(optarg);
+                       args->prio[LO_PRIO].events_per_queue = 0;
+                       break;
+               case 'p':
+                       args->prio[HI_PRIO].events = atoi(optarg);
+                       args->prio[HI_PRIO].events_per_queue = 0;
+                       break;
+               case 's':
+                       i = atoi(optarg);
+                       if (i == 1)
+                               args->sync_type = ODP_SCHED_SYNC_ATOMIC;
+                       else if (i == 2)
+                               args->sync_type = ODP_SCHED_SYNC_ORDERED;
+                       else
+                               args->sync_type = ODP_SCHED_SYNC_PARALLEL;
+                       break;
+               case 'r':
+                       args->sample_per_prio = 1;
+                       break;
+               case 'h':
+                       usage();
+                       exit(EXIT_SUCCESS);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       /* Make sure arguments are valid */
+       if (args->cpu_count > MAX_WORKERS)
+               args->cpu_count = MAX_WORKERS;
+       if (args->prio[LO_PRIO].queues > MAX_QUEUES)
+               args->prio[LO_PRIO].queues = MAX_QUEUES;
+       if (args->prio[HI_PRIO].queues > MAX_QUEUES)
+               args->prio[HI_PRIO].queues = MAX_QUEUES;
+       if (!args->prio[HI_PRIO].queues && !args->prio[LO_PRIO].queues) {
+               printf("No queues configured\n");
+               usage();
+               exit(EXIT_FAILURE);
+       }
+}
+
+/**
+ * Test main function
+ */
+int main(int argc, char *argv[])
+{
+       odph_odpthread_t *thread_tbl;
+       test_args_t args;
+       int num_workers;
+       odp_cpumask_t cpumask;
+       odp_pool_t pool;
+       int i, j;
+       odp_shm_t shm;
+       test_globals_t *globals;
+       char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+       odp_pool_param_t params;
+       int ret = 0;
+       odp_instance_t instance;
+       odph_odpthread_params_t thr_params;
+
+       printf("\nODP scheduling latency benchmark starts\n\n");
+
+       memset(&args, 0, sizeof(args));
+       parse_args(argc, argv, &args);
+
+       /* ODP global init */
+       if (odp_init_global(&instance, NULL, NULL)) {
+               LOG_ERR("ODP global init failed.\n");
+               return -1;
+       }
+
+       /*
+        * Init this thread. It makes also ODP calls when
+        * setting up resources for worker threads.
+        */
+       if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+               LOG_ERR("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("ODP impl name:    %s\n",        odp_version_impl_name());
+       printf("ODP impl details: %s\n",        odp_version_impl_str());
+       printf("CPU model:        %s\n",        odp_cpu_model_str());
+       printf("CPU freq (hz):    %" PRIu64 "\n", odp_cpu_hz_max());
+       printf("Cache line size:  %i\n",        odp_sys_cache_line_size());
+       printf("Max CPU count:    %i\n",        odp_cpu_count());
+
+       /* Get default worker cpumask */
+       num_workers = MAX_WORKERS;
+       if (args.cpu_count)
+               num_workers = args.cpu_count;
+
+       num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
+       args.cpu_count = num_workers;
+
+       (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+       printf("Worker threads:   %i\n", num_workers);
+       printf("First CPU:        %i\n", odp_cpumask_first(&cpumask));
+       printf("CPU mask:         %s\n\n", cpumaskstr);
+
+       thread_tbl = calloc(sizeof(odph_odpthread_t), num_workers);
+       if (!thread_tbl) {
+               LOG_ERR("no memory for thread_tbl\n");
+               return -1;
+       }
+
+       shm = odp_shm_reserve("test_globals",
+                             sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
+       if (shm == ODP_SHM_INVALID) {
+               LOG_ERR("Shared memory reserve failed.\n");
+               return -1;
+       }
+
+       globals = odp_shm_addr(shm);
+       memset(globals, 0, sizeof(test_globals_t));
+       memcpy(&globals->args, &args, sizeof(test_args_t));
+
+       /*
+        * Create event pool
+        */
+       odp_pool_param_init(&params);
+       params.buf.size  = sizeof(test_event_t);
+       params.buf.align = 0;
+       params.buf.num   = EVENT_POOL_SIZE;
+       params.type      = ODP_POOL_BUFFER;
+
+       pool = odp_pool_create("event_pool", &params);
+
+       if (pool == ODP_POOL_INVALID) {
+               LOG_ERR("Pool create failed.\n");
+               return -1;
+       }
+       globals->pool = pool;
+
+       /*
+        * Create queues for schedule test
+        */
+       for (i = 0; i < NUM_PRIOS; i++) {
+               char name[] = "sched_XX_YY";
+               odp_queue_t queue;
+               odp_queue_param_t param;
+               int prio;
+
+               if (i == HI_PRIO)
+                       prio = ODP_SCHED_PRIO_HIGHEST;
+               else
+                       prio = ODP_SCHED_PRIO_LOWEST;
+
+               name[6] = '0' + (prio / 10);
+               name[7] = '0' + prio - (10 * (prio / 10));
+
+               odp_queue_param_init(&param);
+               param.type        = ODP_QUEUE_TYPE_SCHED;
+               param.sched.prio  = prio;
+               param.sched.sync  = args.sync_type;
+               param.sched.group = ODP_SCHED_GROUP_ALL;
+
+               for (j = 0; j < args.prio[i].queues; j++) {
+                       name[9]  = '0' + j / 10;
+                       name[10] = '0' + j - 10 * (j / 10);
+
+                       queue = odp_queue_create(name, &param);
+
+                       if (queue == ODP_QUEUE_INVALID) {
+                               LOG_ERR("Scheduled queue create failed.\n");
+                               return -1;
+                       }
+
+                       globals->queue[i][j] = queue;
+               }
+       }
+
+       odp_barrier_init(&globals->barrier, num_workers);
+
+       /* Create and launch worker threads */
+       memset(&thr_params, 0, sizeof(thr_params));
+       thr_params.thr_type = ODP_THREAD_WORKER;
+       thr_params.instance = instance;
+       thr_params.start = run_thread;
+       thr_params.arg   = NULL;
+       odph_odpthreads_create(thread_tbl, &cpumask, &thr_params);
+
+       /* Wait for worker threads to terminate */
+       odph_odpthreads_join(thread_tbl);
+       free(thread_tbl);
+
+       printf("ODP scheduling latency test complete\n\n");
+
+       for (i = 0; i < NUM_PRIOS; i++) {
+               odp_queue_t queue;
+               int num_queues;
+
+               num_queues = args.prio[i].queues;
+
+               for (j = 0; j < num_queues; j++) {
+                       queue = globals->queue[i][j];
+                       ret += odp_queue_destroy(queue);
+               }
+       }
+
+       ret += odp_shm_free(shm);
+       ret += odp_pool_destroy(pool);
+       ret += odp_term_local();
+       ret += odp_term_global(instance);
+
+       return ret;
+}
-- 
2.7.4

Reply via email to