Signed-off-by: Mike Holmes <[email protected]>
---
helper/test/.gitignore | 1 +
helper/test/Makefile.am | 4 +-
helper/test/odp_ring.c | 556 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 560 insertions(+), 1 deletion(-)
create mode 100644 helper/test/odp_ring.c
diff --git a/helper/test/.gitignore b/helper/test/.gitignore
index fe65f30..c4fd3d1 100644
--- a/helper/test/.gitignore
+++ b/helper/test/.gitignore
@@ -2,3 +2,4 @@
*.log
odp_process
odp_thread
+odp_ring
diff --git a/helper/test/Makefile.am b/helper/test/Makefile.am
index f330533..a511f67 100644
--- a/helper/test/Makefile.am
+++ b/helper/test/Makefile.am
@@ -6,7 +6,8 @@ AM_LDFLAGS += -static
TESTS_ENVIRONMENT = ODP_PLATFORM=${with_platform} TEST_DIR=${builddir}
EXECUTABLES = odp_thread \
- odp_process
+ odp_process \
+ odp_ring
COMPILE_ONLY =
@@ -23,3 +24,4 @@ bin_PROGRAMS = $(EXECUTABLES) $(COMPILE_ONLY)
dist_odp_thread_SOURCES = odp_thread.c
dist_odp_process_SOURCES = odp_process.c
+dist_odp_ring_SOURCES = odp_ring.c
diff --git a/helper/test/odp_ring.c b/helper/test/odp_ring.c
new file mode 100644
index 0000000..5c4f229
--- /dev/null
+++ b/helper/test/odp_ring.c
@@ -0,0 +1,556 @@
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *
+ * ODP test ring
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <odp.h>
+#include <odp/helper/ring.h>
+#include <odp/helper/linux.h>
+#include <test_debug.h>
+
+#define RING_SIZE 4096
+#define MAX_BULK 32
+
+#define RING_TEST_BASIC
+#define MAX_WORKERS 32 /**< Maximum number of work threads */
+
+/* Globals */
+static odph_linux_pthread_t thread_tbl[MAX_WORKERS]; /**< worker threads
table*/
+static int num_workers; /**< number of
workers */
+
+typedef enum {
+ ODP_ATOMIC_TEST = 0,
+ ODP_SHM_TEST,
+ ODP_RING_TEST_BASIC,
+ ODP_RING_TEST_STRESS,
+ ODP_TIMER_PING_TEST,
+ ODP_MAX_TEST
+} odp_test_case_e;
+
+/**
+ * Thread argument
+ */
+typedef struct {
+ int testcase; /**< specifies which set of API's to exercise */
+ int numthrds; /**< no of pthreads to create */
+} pthrd_arg;
+
+/**
+ * Print system information
+ */
+static void odp_print_system_info(void)
+{
+ odp_cpumask_t cpumask;
+ char str[ODP_CPUMASK_STR_SIZE];
+
+ memset(str, 1, sizeof(str));
+
+ odp_cpumask_zero(&cpumask);
+
+ odp_cpumask_from_str(&cpumask, "0x1");
+ (void)odp_cpumask_to_str(&cpumask, str, sizeof(str));
+
+ 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("CPU count: %i\n", odp_cpu_count());
+ printf("CPU mask: %s\n", str);
+
+ printf("\n");
+}
+
+/** test init globals and call odp_init_global() */
+static int odp_test_global_init(void)
+{
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+
+ if (odp_init_global(NULL, NULL)) {
+ LOG_ERR("ODP global init failed.\n");
+ return -1;
+ }
+
+ num_workers = odp_cpu_count();
+ /* force to max CPU count */
+ if (num_workers > MAX_WORKERS)
+ num_workers = MAX_WORKERS;
+
+ return 0;
+}
+
+/** create test thread */
+static int odp_test_thread_create(void *func_ptr(void *), pthrd_arg *arg)
+{
+ odp_cpumask_t cpumask;
+
+ /* Create and init additional threads */
+ odph_linux_cpumask_default(&cpumask, arg->numthrds);
+ odph_linux_pthread_create(thread_tbl, &cpumask, func_ptr,
+ (void *)arg);
+
+ return 0;
+}
+
+/** exit from test thread */
+static int odp_test_thread_exit(pthrd_arg *arg)
+{
+ /* Wait for other threads to exit */
+ odph_linux_pthread_join(thread_tbl, arg->numthrds);
+
+ return 0;
+}
+
+static int test_ring_basic(odph_ring_t *r)
+{
+ void **src = NULL, **cur_src = NULL, **dst = NULL, **cur_dst = NULL;
+ int ret;
+ unsigned i, num_elems;
+
+ /* alloc dummy object pointers */
+ src = malloc(RING_SIZE * 2 * sizeof(void *));
+ if (!src) {
+ LOG_ERR("failed to allocate test ring src memory\n");
+ goto fail;
+ }
+ for (i = 0; i < RING_SIZE * 2; i++)
+ src[i] = (void *)(unsigned long)i;
+
+ cur_src = src;
+
+ /* alloc some room for copied objects */
+ dst = malloc(RING_SIZE * 2 * sizeof(void *));
+ if (!dst) {
+ LOG_ERR("failed to allocate test ring dst memory\n");
+ goto fail;
+ }
+
+ memset(dst, 0, RING_SIZE * 2 * sizeof(void *));
+ cur_dst = dst;
+
+ printf("Test SP & SC basic functions\n");
+ printf("enqueue 1 obj\n");
+ ret = odph_ring_sp_enqueue_burst(r, cur_src, 1);
+ cur_src += 1;
+ if ((ret & ODPH_RING_SZ_MASK) != 1) {
+ LOG_ERR("sp_enq for 1 obj failed\n");
+ goto fail;
+ }
+
+ printf("enqueue 2 objs\n");
+ ret = odph_ring_sp_enqueue_burst(r, cur_src, 2);
+ cur_src += 2;
+ if ((ret & ODPH_RING_SZ_MASK) != 2) {
+ LOG_ERR("sp_enq for 2 obj failed\n");
+ goto fail;
+ }
+
+ printf("enqueue MAX_BULK objs\n");
+ ret = odph_ring_sp_enqueue_burst(r, cur_src, MAX_BULK);
+ if ((ret & ODPH_RING_SZ_MASK) != MAX_BULK) {
+ LOG_ERR("sp_enq for %d obj failed\n", MAX_BULK);
+ goto fail;
+ }
+
+ printf("dequeue 1 obj\n");
+ ret = odph_ring_sc_dequeue_burst(r, cur_dst, 1);
+ cur_dst += 1;
+ if ((ret & ODPH_RING_SZ_MASK) != 1) {
+ LOG_ERR("sc_deq for 1 obj failed\n");
+ goto fail;
+ }
+
+ printf("dequeue 2 objs\n");
+ ret = odph_ring_sc_dequeue_burst(r, cur_dst, 2);
+ cur_dst += 2;
+ if ((ret & ODPH_RING_SZ_MASK) != 2) {
+ LOG_ERR("sc_deq for 2 obj failed\n");
+ goto fail;
+ }
+
+ printf("dequeue MAX_BULK objs\n");
+ ret = odph_ring_sc_dequeue_burst(r, cur_dst, MAX_BULK);
+ cur_dst += MAX_BULK;
+ if ((ret & ODPH_RING_SZ_MASK) != MAX_BULK) {
+ LOG_ERR("sc_deq for %d obj failed\n", MAX_BULK);
+ goto fail;
+ }
+
+ /* check data */
+ if (memcmp(src, dst, cur_dst - dst)) {
+ LOG_ERR("data after dequeue is not the same\n");
+ goto fail;
+ }
+
+ cur_src = src;
+ cur_dst = dst;
+
+ printf("Test MP & MC basic functions\n");
+
+ printf("enqueue 1 obj\n");
+ ret = odph_ring_mp_enqueue_bulk(r, cur_src, 1);
+ cur_src += 1;
+ if (ret != 0) {
+ LOG_ERR("mp_enq for 1 obj failed\n");
+ goto fail;
+ }
+ printf("enqueue 2 objs\n");
+ ret = odph_ring_mp_enqueue_bulk(r, cur_src, 2);
+ cur_src += 2;
+ if (ret != 0) {
+ LOG_ERR("mp_enq for 2 obj failed\n");
+ goto fail;
+ }
+ printf("enqueue MAX_BULK objs\n");
+ ret = odph_ring_mp_enqueue_bulk(r, cur_src, MAX_BULK);
+ if (ret != 0) {
+ LOG_ERR("mp_enq for %d obj failed\n", MAX_BULK);
+ goto fail;
+ }
+ printf("dequeue 1 obj\n");
+ ret = odph_ring_mc_dequeue_bulk(r, cur_dst, 1);
+ cur_dst += 1;
+ if (ret != 0) {
+ LOG_ERR("mc_deq for 1 obj failed\n");
+ goto fail;
+ }
+ printf("dequeue 2 objs\n");
+ ret = odph_ring_mc_dequeue_bulk(r, cur_dst, 2);
+ cur_dst += 2;
+ if (ret != 0) {
+ LOG_ERR("mc_deq for 2 obj failed\n");
+ goto fail;
+ }
+ printf("dequeue MAX_BULK objs\n");
+ ret = odph_ring_mc_dequeue_bulk(r, cur_dst, MAX_BULK);
+ cur_dst += MAX_BULK;
+ if (ret != 0) {
+ LOG_ERR("mc_deq for %d obj failed\n", MAX_BULK);
+ goto fail;
+ }
+ /* check data */
+ if (memcmp(src, dst, cur_dst - dst)) {
+ LOG_ERR("data after dequeue is not the same\n");
+ goto fail;
+ }
+
+ printf("test watermark and default bulk enqueue / dequeue\n");
+ odph_ring_set_water_mark(r, 20);
+ num_elems = 16;
+
+ cur_src = src;
+ cur_dst = dst;
+
+ ret = odph_ring_mp_enqueue_bulk(r, cur_src, num_elems);
+ cur_src += num_elems;
+ if (ret != 0) {
+ LOG_ERR("Cannot enqueue\n");
+ goto fail;
+ }
+ ret = odph_ring_mp_enqueue_bulk(r, cur_src, num_elems);
+ if (ret != -EDQUOT) {
+ LOG_ERR("Watermark not exceeded\n");
+ goto fail;
+ }
+ ret = odph_ring_mc_dequeue_bulk(r, cur_dst, num_elems);
+ cur_dst += num_elems;
+ if (ret != 0) {
+ LOG_ERR("Cannot dequeue\n");
+ goto fail;
+ }
+ ret = odph_ring_mc_dequeue_bulk(r, cur_dst, num_elems);
+ cur_dst += num_elems;
+ if (ret != 0) {
+ LOG_ERR("Cannot dequeue2\n");
+ goto fail;
+ }
+
+ /* check data */
+ if (memcmp(src, dst, cur_dst - dst)) {
+ LOG_ERR("data after dequeue is not the same\n");
+ goto fail;
+ }
+
+ printf("basic enqueu, dequeue test for ring <%s>@%p passed\n",
+ r->name, r);
+
+ free(src);
+ free(dst);
+ return 0;
+
+fail:
+ free(src);
+ free(dst);
+ return -1;
+}
+
+/* global shared ring used for stress testing */
+static odph_ring_t *r_stress;
+
+/* Stress func for Multi producer only */
+static int producer_fn(void)
+{
+ unsigned i;
+
+ void **src = NULL;
+
+ /* alloc dummy object pointers */
+ src = malloc(MAX_BULK * 2 * sizeof(void *));
+ if (!src) {
+ LOG_ERR("failed to allocate producer memory.\n");
+ return -1;
+ }
+ for (i = 0; i < MAX_BULK; i++)
+ src[i] = (void *)(unsigned long)i;
+
+ do {
+ i = odph_ring_mp_enqueue_bulk(r_stress, src, MAX_BULK);
+ if (i == 0) {
+ free(src);
+ return 0;
+ }
+ } while (1);
+}
+
+/* Stress func for Multi consumer only */
+static int consumer_fn(void)
+{
+ unsigned i;
+ void **src = NULL;
+
+ /* alloc dummy object pointers */
+ src = malloc(MAX_BULK * 2 * sizeof(void *));
+ if (!src) {
+ LOG_ERR("failed to allocate consumer memory.\n");
+ return -1;
+ }
+
+ do {
+ i = odph_ring_mc_dequeue_bulk(r_stress, src, MAX_BULK);
+ if (i == 0) {
+ for (i = 0; i < MAX_BULK; i++) {
+ if (src[i] != (void *)(unsigned long)i) {
+ free(src);
+ printf("data mismatch.. lockless ops
fail\n");
+ return -1;
+ }
+ }
+ free(src);
+ printf("\n Test OK !\n");
+ return 0;
+ }
+ } while (1);
+}
+
+/*
+ * Note : make sure that both enqueue and dequeue
+ * operation starts at same time so to avoid data corruption
+ * Its because atomic lock will protect only indexes, but if order of
+ * read or write operation incorrect then data mismatch will happen
+ * So its resposibility of application develop to take care of order of
+ * data read or write.
+*/
+typedef enum {
+ one_enq_one_deq, /* One thread to enqueue one to
+ dequeu at same time */
+ one_enq_rest_deq, /* one thread to enq rest to
+ dequeue at same time */
+ one_deq_rest_enq, /* one to deq and rest enq at very same time */
+ multi_enq_multi_deq /* multiple enq,deq */
+} stress_type_t;
+
+static void test_ring_stress(stress_type_t type)
+{
+ int thr;
+
+ thr = odp_thread_id();
+
+ switch (type) {
+ case one_enq_one_deq:
+
+ if (thr == 1)
+ producer_fn();
+ if (thr == 2)
+ consumer_fn();
+ break;
+
+ case multi_enq_multi_deq:
+ if (thr % 2 == 0)
+ producer_fn();
+ else
+ consumer_fn();
+ break;
+
+ case one_deq_rest_enq:
+ case one_enq_rest_deq:/*TBD*/
+ default:
+ LOG_ERR("Invalid stress type or test case yet not supported\n");
+ }
+}
+
+/* local struct for ring_thread argument */
+typedef struct {
+ pthrd_arg thrdarg;
+ int stress_type;
+} ring_arg_t;
+
+static void *test_ring(void *arg)
+{
+ ring_arg_t *parg = (ring_arg_t *)arg;
+ int thr;
+ char ring_name[ODPH_RING_NAMESIZE];
+ odph_ring_t *r;
+ int result = 0;
+
+ thr = odp_thread_id();
+
+ printf("Thread %i starts\n", thr);
+
+ switch (parg->thrdarg.testcase) {
+ case ODP_RING_TEST_BASIC:
+ snprintf(ring_name, sizeof(ring_name), "test_ring_%i", thr);
+
+ r = odph_ring_create(ring_name, RING_SIZE,
+ 0 /* not used, alignement
+ taken care inside func : todo */);
+ if (!r) {
+ LOG_ERR("ring create failed\n");
+ result = -1;
+ break;
+ }
+ /* lookup ring from its name */
+ if (odph_ring_lookup(ring_name) != r) {
+ LOG_ERR("ring lookup failed\n");
+ result = -1;
+ break;
+ }
+
+ /* basic operations */
+ if (test_ring_basic(r) < 0) {
+ LOG_ERR("ring basic enqueue/dequeu ops failed\n");
+ result = -1;
+ }
+
+ /* dump ring stats */
+ odph_ring_list_dump();
+
+ break;
+
+ case ODP_RING_TEST_STRESS:
+ test_ring_stress(parg->stress_type);
+
+ /* dump ring stats */
+ odph_ring_list_dump();
+ break;
+
+ default:
+ LOG_ERR("Invalid test case [%d]\n", parg->thrdarg.testcase);
+ result = -1;
+ break;
+ }
+
+ LOG_DBG("result = %d\n", result);
+ if (result == 0)
+ printf("test_ring Result:pass\n");
+ else
+ printf("test_ring Result:fail\n");
+
+ fflush(stdout);
+
+ return parg;
+}
+
+int main(int argc __attribute__((__unused__)),
+ char *argv[] __attribute__((__unused__)))
+{
+ ring_arg_t rarg;
+
+ if (odp_test_global_init() != 0)
+ return -1;
+
+ odp_print_system_info();
+
+ odph_ring_tailq_init();
+
+ rarg.thrdarg.numthrds = odp_cpu_count();
+
+#ifdef RING_TEST_BASIC
+ rarg.thrdarg.testcase = ODP_RING_TEST_BASIC;
+#else
+ rarg.thrdarg.testcase = ODP_RING_TEST_STRESS;
+ rarg.stress_type = one_enq_one_deq;
+/* rarg.stress_type = multi_enq_multi_deq;*/
+ char ring_name[ODPH_RING_NAMESIZE];
+
+ printf("starting stess test type : %d..\n", rarg.stress_type);
+ /* create a ring */
+ snprintf(ring_name, sizeof(ring_name), "test_ring_stress");
+
+ r_stress = odph_ring_create(ring_name, RING_SIZE,
+ 0 /* not used, alignement
+ taken care inside func : todo */);
+ if (!r_stress) {
+ LOG_ERR("ring create failed\n");
+ goto fail;
+ }
+ /* lookup ring from its name */
+ if (odph_ring_lookup(ring_name) != r_stress) {
+ LOG_ERR("ring lookup failed\n");
+ goto fail;
+ }
+#endif
+ odp_test_thread_create(test_ring, (pthrd_arg *)&rarg);
+
+#ifndef RING_TEST_BASIC
+fail:
+#endif
+
+ odp_test_thread_exit(&rarg.thrdarg);
+
+ return 0;
+}
--
2.1.4
_______________________________________________
lng-odp mailing list
[email protected]
https://lists.linaro.org/mailman/listinfo/lng-odp