Hello Yi,
some more clean up is needed here.
perl ./scripts/checkpatch.pl
0001-linux-generic-test-ring-convert-to-cunit-and-improve.patch
CHECK: Alignment should match open parenthesis
#164: FILE: platform/linux-generic/test/ring/ring_basic.c:84:
+ st_ring = _ring_create(st_ring_name, RING_SIZE,
+ _RING_F_SP_ENQ | _RING_F_SC_DEQ);
CHECK: Alignment should match open parenthesis
#171: FILE: platform/linux-generic/test/ring/ring_basic.c:91:
+ mt_ring = _ring_create(mt_ring_name, RING_SIZE,
+ 0 /* not used, alignement
and few comments bellow:
On 05/23/16 10:35, Yi He wrote:
Fixes: https://bugs.linaro.org/show_bug.cgi?id=2228
Convert ring test program into cunit framework
Improve LCOV coverage for linux-generic/pktio/ring.c
Signed-off-by: Yi He <yi...@linaro.org>
---
platform/linux-generic/test/Makefile.am | 2 +-
platform/linux-generic/test/ring/Makefile.am | 20 +-
platform/linux-generic/test/ring/ring_basic.c | 359 ++++++++++++++++++
platform/linux-generic/test/ring/ring_main.c | 12 +
platform/linux-generic/test/ring/ring_stress.c | 237 ++++++++++++
platform/linux-generic/test/ring/ring_suites.c | 74 ++++
platform/linux-generic/test/ring/ring_suites.h | 33 ++
platform/linux-generic/test/ring/ringtest.c | 495 -------------------------
8 files changed, 725 insertions(+), 507 deletions(-)
create mode 100644 platform/linux-generic/test/ring/ring_basic.c
create mode 100644 platform/linux-generic/test/ring/ring_main.c
create mode 100644 platform/linux-generic/test/ring/ring_stress.c
create mode 100644 platform/linux-generic/test/ring/ring_suites.c
create mode 100644 platform/linux-generic/test/ring/ring_suites.h
delete mode 100644 platform/linux-generic/test/ring/ringtest.c
diff --git a/platform/linux-generic/test/Makefile.am
b/platform/linux-generic/test/Makefile.am
index f74185d..2d58c57 100644
--- a/platform/linux-generic/test/Makefile.am
+++ b/platform/linux-generic/test/Makefile.am
@@ -8,7 +8,7 @@ ODP_MODULES = pktio \
if test_vald
TESTS = pktio/pktio_run.sh \
pktio/pktio_run_tap.sh \
- ring/ringtest$(EXEEXT) \
+ ring/ring_main$(EXEEXT) \
shmem/shmem_linux \
${top_builddir}/test/validation/atomic/atomic_main$(EXEEXT) \
${top_builddir}/test/validation/barrier/barrier_main$(EXEEXT) \
diff --git a/platform/linux-generic/test/ring/Makefile.am
b/platform/linux-generic/test/ring/Makefile.am
index 5a949d0..c086584 100644
--- a/platform/linux-generic/test/ring/Makefile.am
+++ b/platform/linux-generic/test/ring/Makefile.am
@@ -1,16 +1,14 @@
-include $(top_srcdir)/test/validation/Makefile.inc
+include ../Makefile.inc
-AM_CFLAGS += -I$(srcdir)/common
-AM_CFLAGS += -I$(top_srcdir)/test/validation/common
-AM_LDFLAGS += -static
+noinst_LTLIBRARIES = libtestring.la
+libtestring_la_SOURCES = ring_suites.c ring_basic.c ring_stress.c
+libtestring_la_CFLAGS = $(AM_CFLAGS) $(INCCUNIT_COMMON) $(INCODP)
-COMPILE_ONLY =
+test_PROGRAMS = ring_main$(EXEEXT)
+dist_ring_main_SOURCES = ring_main.c
-TESTSCRIPTS =
+ring_main_LDFLAGS = $(AM_LDFLAGS)
+ring_main_LDADD = libtestring.la $(LIBCUNIT_COMMON) $(LIBODP)
-EXECUTABLES = ringtest$(EXEEXT)
+noinst_HEADERS = ring_suites.h
-test_PROGRAMS = $(EXECUTABLES) $(COMPILE_ONLY)
-
-ringtest_SOURCES = ringtest.c
-ringtest_LDADD = $(LIBCUNIT_COMMON) $(LIBODP)
diff --git a/platform/linux-generic/test/ring/ring_basic.c
b/platform/linux-generic/test/ring/ring_basic.c
new file mode 100644
index 0000000..8daaa9f
--- /dev/null
+++ b/platform/linux-generic/test/ring/ring_basic.c
@@ -0,0 +1,359 @@
+/* Copyright (c) 2016, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * ODP ring basic test
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <test_debug.h>
+#include <odp_cunit_common.h>
+#include <odp_packet_io_ring_internal.h>
+
+#include "ring_suites.h"
+
+/* labor functions declaration */
+static void __do_basic_burst(_ring_t *r);
+static void __do_basic_bulk(_ring_t *r);
+static void __do_basic_watermark(_ring_t *r);
+
+/* dummy object pointers for enqueue and dequeue testing */
+static void **test_enq_data;
+static void **test_deq_data;
+
+/* create two rings: one for single thread usage scenario
+ * and another for multiple thread usage scenario.
+ * st - single thread usage scenario
+ * mt - multiple thread usage scenario
+ */
+static const char *st_ring_name = "ST basic ring";
+static const char *mt_ring_name = "MT basic ring";
+static _ring_t *st_ring, *mt_ring;
+
+int ring_test_basic_start(void)
+{
+ int i = 0;
+
+ /* alloc dummy object pointers for enqueue testing */
+ test_enq_data = malloc(RING_SIZE * 2 * sizeof(void *));
+ if (NULL == test_enq_data) {
+ LOG_ERR("failed to allocate basic test enqeue data\n");
+ return -1;
+ }
+
+ for (i = 0; i < RING_SIZE * 2; i++)
+ test_enq_data[i] = (void *)(unsigned long)i;
+
+ /* alloc dummy object pointers for dequeue testing */
+ test_deq_data = malloc(RING_SIZE * 2 * sizeof(void *));
+ if (NULL == test_deq_data) {
+ LOG_ERR("failed to allocate basic test dequeue data\n");
+ free(test_enq_data); test_enq_data = NULL;
+ return -1;
+ }
+
+ memset(test_deq_data, 0, RING_SIZE * 2 * sizeof(void *));
+ return 0;
+}
+
+int ring_test_basic_end(void)
+{
+ free(test_enq_data);
+ free(test_deq_data);
+ return 0;
+}
+
+/* basic test cases */
+void ring_test_basic_create(void)
+{
+ /* prove illegal size shall fail */
+ st_ring = _ring_create(st_ring_name, ILLEGAL_SIZE, 0);
+ CU_ASSERT(NULL == st_ring);
+ CU_ASSERT(EINVAL == __odp_errno);
+
+ /* create ring for single thread usage scenario */
+ st_ring = _ring_create(st_ring_name, RING_SIZE,
+ _RING_F_SP_ENQ | _RING_F_SC_DEQ);
+
+ CU_ASSERT(NULL != st_ring);
+ CU_ASSERT(_ring_lookup(st_ring_name) == st_ring);
+
+ /* create ring for multiple thread usage scenario */
+ mt_ring = _ring_create(mt_ring_name, RING_SIZE,
+ 0 /* not used, alignement
+ taken care inside function: todo */);
+
+ CU_ASSERT(NULL != mt_ring);
+ CU_ASSERT(_ring_lookup(mt_ring_name) == mt_ring);
+}
+
+void ring_test_basic_burst(void)
+{
+ /* two rounds to cover both single
+ * thread and multiple thread APIs
+ */
+ __do_basic_burst(st_ring);
+ __do_basic_burst(mt_ring);
+}
+
+void ring_test_basic_bulk(void)
+{
+ __do_basic_bulk(st_ring);
+ __do_basic_bulk(mt_ring);
+}
+
+void ring_test_basic_watermark(void)
+{
+ __do_basic_watermark(st_ring);
+ __do_basic_watermark(mt_ring);
+}
+
+/* labor functions definition */
+static void __do_basic_burst(_ring_t *r)
+{
+ int result = 0;
+ unsigned int count = 0;
+ void * const *source = test_enq_data;
+ void * const *dest = test_deq_data;
+ void **enq = NULL, **deq = NULL;
+
+ enq = test_enq_data; deq = test_deq_data;
+
+ /* ring is empty */
+ CU_ASSERT(1 == _ring_empty(r));
+
+ /* enqueue 1 object */
+ result = _ring_enqueue_burst(r, enq, 1);
+ enq += 1;
+ CU_ASSERT(1 == (result & _RING_SZ_MASK));
+
+ /* enqueue 2 objects */
+ result = _ring_enqueue_burst(r, enq, 2);
+ enq += 2;
+ CU_ASSERT(2 == (result & _RING_SZ_MASK));
+
+ /* enqueue HALF_BULK objects */
+ result = _ring_enqueue_burst(r, enq, HALF_BULK);
+ enq += HALF_BULK;
+ CU_ASSERT(HALF_BULK == (result & _RING_SZ_MASK));
+
+ /* ring is neither empty nor full */
+ CU_ASSERT(0 == _ring_full(r));
+ CU_ASSERT(0 == _ring_empty(r));
+
+ /* _ring_count() equals enqueued */
+ count = (1 + 2 + HALF_BULK);
+ CU_ASSERT(count == _ring_count(r));
+ /* _ring_free_count() equals rooms left */
+ count = (RING_SIZE - 1) - count;
+ CU_ASSERT(count == _ring_free_count(r));
+
+ /* exceed the size, enquene as many as possible */
+ result = _ring_enqueue_burst(r, enq, HALF_BULK);
+ enq += count;
+ CU_ASSERT(count == (result & _RING_SZ_MASK));
+ CU_ASSERT(1 == _ring_full(r));
+
+ /* dequeue 1 object */
+ result = _ring_dequeue_burst(r, deq, 1);
+ deq += 1;
+ CU_ASSERT(1 == (result & _RING_SZ_MASK));
+
+ /* dequeue 2 objects */
+ result = _ring_dequeue_burst(r, deq, 2);
+ deq += 2;
+ CU_ASSERT(2 == (result & _RING_SZ_MASK));
+
+ /* dequeue HALF_BULK objects */
+ result = _ring_dequeue_burst(r, deq, HALF_BULK);
+ deq += HALF_BULK;
+ CU_ASSERT(HALF_BULK == (result & _RING_SZ_MASK));
+
+ /* _ring_free_count() equals dequeued */
+ count = (1 + 2 + HALF_BULK);
+ CU_ASSERT(count == _ring_free_count(r));
+ /* _ring_count() equals remained left */
+ count = (RING_SIZE - 1) - count;
+ CU_ASSERT(count == _ring_count(r));
+
+ /* underrun the size, dequeue as many as possible */
+ result = _ring_dequeue_burst(r, deq, HALF_BULK);
+ deq += count;
+ CU_ASSERT(count == (result & _RING_SZ_MASK));
+ CU_ASSERT(1 == _ring_empty(r));
+
+ /* check data */
+ CU_ASSERT(0 == memcmp(source, dest, deq - dest));
+
+ /* reset dequeue data */
+ memset(test_deq_data, 0, RING_SIZE * 2 * sizeof(void *));
+}
+
+/* incomplete ring API set: strange!
+ * complement _ring_enqueue/dequeue_bulk to improve coverage
+ */
+static inline int __ring_enqueue_bulk(
+ _ring_t *r, void * const *objects, unsigned bulk)
+{
+ if (r->prod.sp_enqueue)
+ return _ring_sp_enqueue_bulk(r, objects, bulk);
+ else
+ return _ring_mp_enqueue_bulk(r, objects, bulk);
+}
+
+static inline int __ring_dequeue_bulk(
+ _ring_t *r, void **objects, unsigned bulk)
+{
+ if (r->cons.sc_dequeue)
+ return _ring_sc_dequeue_bulk(r, objects, bulk);
+ else
+ return _ring_mc_dequeue_bulk(r, objects, bulk);
+}
+
+static void __do_basic_bulk(_ring_t *r)
+{
+ int result = 0;
+ unsigned int count = 0;
+ void * const *source = test_enq_data;
+ void * const *dest = test_deq_data;
+ void **enq = NULL, **deq = NULL;
+
+ enq = test_enq_data; deq = test_deq_data;
+
+ /* ring is empty */
+ CU_ASSERT(1 == _ring_empty(r));
+
+ /* enqueue 1 object */
+ result = __ring_enqueue_bulk(r, enq, 1);
+ enq += 1;
+ CU_ASSERT(0 == result);
+
+ /* enqueue 2 objects */
+ result = __ring_enqueue_bulk(r, enq, 2);
+ enq += 2;
+ CU_ASSERT(0 == result);
+
+ /* enqueue HALF_BULK objects */
+ result = __ring_enqueue_bulk(r, enq, HALF_BULK);
+ enq += HALF_BULK;
+ CU_ASSERT(0 == result);
+
+ /* ring is neither empty nor full */
+ CU_ASSERT(0 == _ring_full(r));
+ CU_ASSERT(0 == _ring_empty(r));
+
+ /* _ring_count() equals enqueued */
+ count = (1 + 2 + HALF_BULK);
+ CU_ASSERT(count == _ring_count(r));
+ /* _ring_free_count() equals rooms left */
+ count = (RING_SIZE - 1) - count;
+ CU_ASSERT(count == _ring_free_count(r));
+
+ /* exceed the size, enquene shall fail with -ENOBUFS */
+ result = __ring_enqueue_bulk(r, enq, HALF_BULK);
+ CU_ASSERT(-ENOBUFS == result);
+
+ /* fullful the ring */
+ result = __ring_enqueue_bulk(r, enq, count);
+ enq += count;
+ CU_ASSERT(0 == result);
+ CU_ASSERT(1 == _ring_full(r));
+
+ /* dequeue 1 object */
+ result = __ring_dequeue_bulk(r, deq, 1);
+ deq += 1;
+ CU_ASSERT(0 == result);
+
+ /* dequeue 2 objects */
+ result = __ring_dequeue_bulk(r, deq, 2);
+ deq += 2;
+ CU_ASSERT(0 == result);
+
+ /* dequeue HALF_BULK objects */
+ result = __ring_dequeue_bulk(r, deq, HALF_BULK);
+ deq += HALF_BULK;
+ CU_ASSERT(0 == result);
+
+ /* _ring_free_count() equals dequeued */
+ count = (1 + 2 + HALF_BULK);
+ CU_ASSERT(count == _ring_free_count(r));
+ /* _ring_count() equals remained left */
+ count = (RING_SIZE - 1) - count;
+ CU_ASSERT(count == _ring_count(r));
+
+ /* underrun the size, dequeue shall fail with -ENOENT */
+ result = __ring_dequeue_bulk(r, deq, HALF_BULK);
+ CU_ASSERT(-ENOENT == result);
+
+ /* empty the queue */
+ result = __ring_dequeue_bulk(r, deq, count);
+ deq += count;
+ CU_ASSERT(0 == result);
+ CU_ASSERT(1 == _ring_empty(r));
+
+ /* check data */
+ CU_ASSERT(0 == memcmp(source, dest, deq - dest));
+
+ /* reset dequeue data */
+ memset(test_deq_data, 0, RING_SIZE * 2 * sizeof(void *));
+}
+
+void __do_basic_watermark(_ring_t *r)
+{
+ int result = 0;
+ void * const *source = test_enq_data;
+ void * const *dest = test_deq_data;
+ void **enq = NULL, **deq = NULL;
+
+ enq = test_enq_data; deq = test_deq_data;
+
+ /* bulk = 3/4 watermark to trigger alarm on 2nd enqueue */
+ const unsigned watermark = PIECE_BULK;
+ const unsigned bulk = (watermark / 4) * 3;
+
+ /* watermark cannot exceed ring size */
+ result = _ring_set_water_mark(r, ILLEGAL_SIZE);
+ CU_ASSERT(-EINVAL == result);
+
+ /* set watermark */
+ result = _ring_set_water_mark(r, watermark);
+ CU_ASSERT(0 == result);
+
+ /* 1st enqueue shall succeed */
+ result = __ring_enqueue_bulk(r, enq, bulk);
+ enq += bulk;
+ CU_ASSERT(0 == result);
+
+ /* 2nd enqueue shall succeed but return -EDQUOT */
+ result = __ring_enqueue_bulk(r, enq, bulk);
+ enq += bulk;
+ CU_ASSERT(-EDQUOT == result);
+
+ /* dequeue 1st bulk */
+ result = __ring_dequeue_bulk(r, deq, bulk);
+ deq += bulk;
+ CU_ASSERT(0 == result);
+
+ /* dequeue 2nd bulk */
+ result = __ring_dequeue_bulk(r, deq, bulk);
+ deq += bulk;
+ CU_ASSERT(0 == result);
+
+ /* check data */
+ CU_ASSERT(0 == memcmp(source, dest, deq - dest));
+
+ /* reset watermark */
+ result = _ring_set_water_mark(r, 0);
+ CU_ASSERT(0 == result);
+
+ /* reset dequeue data */
+ memset(test_deq_data, 0, RING_SIZE * 2 * sizeof(void *));
+}
diff --git a/platform/linux-generic/test/ring/ring_main.c
b/platform/linux-generic/test/ring/ring_main.c
new file mode 100644
index 0000000..ad710f5
--- /dev/null
+++ b/platform/linux-generic/test/ring/ring_main.c
@@ -0,0 +1,12 @@
+/* Copyright (c) 2015, Linaro Limited
2016
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "ring_suites.h"
+
+int main(int argc, char *argv[])
+{
+ return ring_suites_main(argc, argv);
+}
diff --git a/platform/linux-generic/test/ring/ring_stress.c
b/platform/linux-generic/test/ring/ring_stress.c
new file mode 100644
index 0000000..110506f
--- /dev/null
+++ b/platform/linux-generic/test/ring/ring_stress.c
@@ -0,0 +1,237 @@
+/* Copyright (c) 2016, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * ODP ring stress test
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <odp_api.h>
+#include <odp/helper/linux.h>
+#include <odp_packet_io_ring_internal.h>
+#include <test_debug.h>
+#include <odp_cunit_common.h>
+
+#include "ring_suites.h"
+
+/*
+ * Since cunit framework cannot work with multi-threading, ask workers
+ * to save their results for delayed assertion after thread collection.
+ */
+static int worker_results[MAX_WORKERS];
+
+/*
+ * 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 {
+ STRESS_1_1_PRODUCER_CONSUMER,
+ STRESS_1_N_PRODUCER_CONSUMER,
+ STRESS_N_1_PRODUCER_CONSUMER,
+ STRESS_N_M_PRODUCER_CONSUMER
+} stress_case_t;
+
+/* worker function declarations */
+static int stress_worker(void *_data);
+
+/* global name for later look up in workers' context */
+static const char *ring_name = "stress ring";
+
+int ring_test_stress_start(void)
+{
+ /* multiple thread usage scenario only */
+ _ring_t *r_stress = _ring_create(ring_name, RING_SIZE,
+ 0 /* not used, alignement
+ taken care inside function: todo */);
+ if (r_stress == NULL) {
+ LOG_ERR("create ring failed for stress.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void ring_test_stress_1_1_producer_consumer(void)
+{
+ int i = 0;
+ odp_cpumask_t cpus;
+ pthrd_arg worker_param;
+
+ /* reset results for delayed assertion */
+ memset(worker_results, 0, sizeof(worker_results));
+
+ /* request 2 threads to run 1:1 stress */
+ worker_param.numthrds = odp_cpumask_default_worker(&cpus, 2);
+ worker_param.testcase = STRESS_1_1_PRODUCER_CONSUMER;
+
+ /* not failure, insufficient resource */
+ if (worker_param.numthrds < 2) {
+ LOG_ERR("insufficient cpu for 1:1 "
+ "producer/consumer stress.\n");
+ return;
+ }
+
+ /* kick the workers */
+ odp_cunit_thread_create(stress_worker, &worker_param);
+
+ /* collect the results */
+ odp_cunit_thread_exit(&worker_param);
+
+ /* delayed assertion due to cunit limitation */
+ for (i = 0; i < worker_param.numthrds; i++)
+ CU_ASSERT(0 == worker_results[i]);
+}
+
+void ring_test_stress_N_M_producer_consumer(void)
+{
+ int i = 0;
+ odp_cpumask_t cpus;
+ pthrd_arg worker_param;
+
+ /* reset results for delayed assertion */
+ memset(worker_results, 0, sizeof(worker_results));
+
+ /* request MAX_WORKERS threads to run N:M stress */
+ worker_param.numthrds =
+ odp_cpumask_default_worker(&cpus, MAX_WORKERS);
+ worker_param.testcase = STRESS_N_M_PRODUCER_CONSUMER;
+
+ /* not failure, insufficient resource */
+ if (worker_param.numthrds < 3) {
+ LOG_ERR("insufficient cpu for N:M "
+ "producer/consumer stress.\n");
+ return;
+ }
+
+ /* kick the workers */
+ odp_cunit_thread_create(stress_worker, &worker_param);
+
+ /* collect the results */
+ odp_cunit_thread_exit(&worker_param);
+
+ /* delayed assertion due to cunit limitation */
+ for (i = 0; i < worker_param.numthrds; i++)
+ CU_ASSERT(0 == worker_results[i]);
+}
+
+void ring_test_stress_1_N_producer_consumer(void)
+{
+}
+
+void ring_test_stress_N_1_producer_consumer(void)
+{
+}
+
+void ring_test_stress_ring_list_dump(void)
+{
+ /* improve code coverage */
+ _ring_list_dump();
+}
+
+/* worker function for multiple producer instances */
+static int do_producer(_ring_t *r)
+{
+ int i, result = 0;
+ void **enq = NULL;
+
+ /* allocate dummy object pointers for enqueue */
+ enq = malloc(PIECE_BULK * 2 * sizeof(void *));
+ if (NULL == enq) {
+ LOG_ERR("insufficient memory for producer enqueue.\n");
+ return 0; /* not failure, skip for insufficient memory */
+ }
+
+ /* data pattern to be evaluated later in consumer */
+ for (i = 0; i < PIECE_BULK; i++)
+ enq[i] = (void *)(unsigned long)i;
+
+ do {
+ result = _ring_mp_enqueue_bulk(r, enq, PIECE_BULK);
+ if (0 == result) {
+ free(enq);
+ return 0;
+ }
+ } while (1);
It's better to remove this while(1) in the test. I think we should avoid
any places where
code can hang forever. I think if it's needed you can add some delay
before enqueue.
And do one call without loop.
+}
+
+/* worker function for multiple consumer instances */
+static int do_consumer(_ring_t *r)
+{
+ int i, result = 0;
+ void **deq = NULL;
+ const char *message = "test OK!";
+ const char *mismatch = "data mismatch..lockless enq/deq failed.";
+
+ /* allocate dummy object pointers for dequeue */
+ deq = malloc(PIECE_BULK * 2 * sizeof(void *));
+ if (NULL == deq) {
+ LOG_ERR("insufficient memory for consumer dequeue.\n");
+ return 0; /* not failure, skip for insufficient memory */
+ }
+
+ do {
+ result = _ring_mc_dequeue_bulk(r, deq, PIECE_BULK);
+ if (0 == result) {
+ /* evaluate the data pattern */
+ for (i = 0; i < PIECE_BULK; i++) {
+ if (deq[i] != (void *)(unsigned long)i) {
+ result = -1;
+ message = mismatch;
+ break;
+ }
+ }
+
+ free(deq);
+ LOG_ERR("%s\n", message);
+ return result;
+ }
+ } while (1);
this while it's better to avoid also.
+}
+
+static int stress_worker(void *_data)
+{
+ pthrd_arg *worker_param = (pthrd_arg *)_data;
+ _ring_t *r_stress = NULL;
+
empty line is not needed.
+ int worker_id = odp_thread_id();
+ /* save the worker result for delayed assertion */
+ int *result = &worker_results[(worker_id % worker_param->numthrds)];
+
+ /* verify ring lookup in worker context */
+ r_stress = _ring_lookup(ring_name);
+ if (NULL == r_stress) {
+ LOG_ERR("ring lookup %s not found\n", ring_name);
+ *result = -1;
+ return -1;
+ }
+
+ switch (worker_param->testcase) {
+ case STRESS_1_1_PRODUCER_CONSUMER:
+ case STRESS_N_M_PRODUCER_CONSUMER:
+ /* interleaved producer/consumer */
+ if (0 == (worker_id % 2))
+ *result = do_producer(r_stress);
+ else if (1 == (worker_id % 2))
+ *result = do_consumer(r_stress);
+ break;
+ case STRESS_1_N_PRODUCER_CONSUMER:
+ case STRESS_N_1_PRODUCER_CONSUMER:
+ default:
+ LOG_ERR("invalid or not-implemented stress type (%d)\n",
+ worker_param->testcase);
+ break;
+ }
+ return 0;
+}
diff --git a/platform/linux-generic/test/ring/ring_suites.c
b/platform/linux-generic/test/ring/ring_suites.c
new file mode 100644
index 0000000..3f655a6
--- /dev/null
+++ b/platform/linux-generic/test/ring/ring_suites.c
@@ -0,0 +1,74 @@
+/* Copyright (c) 2015, Linaro Limited
2016?
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <odp_api.h>
+#include <test_debug.h>
+#include <odp_cunit_common.h>
+#include <odp_packet_io_ring_internal.h>
+
+#include "ring_suites.h"
+
+static int ring_suites_init(odp_instance_t *inst)
+{
+ if (0 != odp_init_global(inst, NULL, NULL)) {
+ LOG_ERR("error: odp_init_global() failed.\n");
+ return -1;
+ }
+ if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) {
+ LOG_ERR("error: odp_init_local() failed.\n");
odp_destroy_global(inst);
+ return -1;
+ }
+
+ _ring_tailq_init();
+ return 0;
+}
+
+static odp_testinfo_t ring_suite_basic[] = {
+ ODP_TEST_INFO(ring_test_basic_create),
+ ODP_TEST_INFO(ring_test_basic_burst),
+ ODP_TEST_INFO(ring_test_basic_bulk),
+ ODP_TEST_INFO(ring_test_basic_watermark),
+ ODP_TEST_INFO_NULL,
+};
+
+static odp_testinfo_t ring_suite_stress[] = {
+ ODP_TEST_INFO(ring_test_stress_1_1_producer_consumer),
+ ODP_TEST_INFO(ring_test_stress_1_N_producer_consumer),
+ ODP_TEST_INFO(ring_test_stress_N_1_producer_consumer),
+ ODP_TEST_INFO(ring_test_stress_N_M_producer_consumer),
+ ODP_TEST_INFO(ring_test_stress_ring_list_dump),
+ ODP_TEST_INFO_NULL,
+};
+
+static odp_suiteinfo_t ring_suites[] = {
+ {"ring basic", ring_test_basic_start,
+ ring_test_basic_end, ring_suite_basic},
+ {"ring stress", ring_test_stress_start,
+ NULL, ring_suite_stress},
+ ODP_SUITE_INFO_NULL
+};
+
+int ring_suites_main(int argc, char *argv[])
+{
+ int ret;
+
+ /* let helper collect its own arguments (e.g. --odph_proc) */
+ if (odp_cunit_parse_options(argc, argv))
+ return -1;
+
+ odp_cunit_register_global_init(ring_suites_init);
+
+ ret = odp_cunit_register(ring_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}
diff --git a/platform/linux-generic/test/ring/ring_suites.h
b/platform/linux-generic/test/ring/ring_suites.h
new file mode 100644
index 0000000..ba63bfc
--- /dev/null
+++ b/platform/linux-generic/test/ring/ring_suites.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2015, Linaro Limited
2016
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#define RING_SIZE 4096
+#define PIECE_BULK 32
+
+#define HALF_BULK (RING_SIZE >> 1)
+#define ILLEGAL_SIZE (RING_SIZE | 0x3)
+
+/* test suite start and stop */
+int ring_test_basic_start(void);
+int ring_test_basic_end(void);
+
+/* basic test cases */
+void ring_test_basic_create(void);
+void ring_test_basic_burst(void);
+void ring_test_basic_bulk(void);
+void ring_test_basic_watermark(void);
+
+/* test suite start and stop */
+int ring_test_stress_start(void);
+
+/* stress test cases */
+void ring_test_stress_1_1_producer_consumer(void);
+void ring_test_stress_1_N_producer_consumer(void);
+void ring_test_stress_N_1_producer_consumer(void);
+void ring_test_stress_N_M_producer_consumer(void);
+void ring_test_stress_ring_list_dump(void);
+
+int ring_suites_main(int argc, char *argv[]);
diff --git a/platform/linux-generic/test/ring/ringtest.c
b/platform/linux-generic/test/ring/ringtest.c
deleted file mode 100644
index 2ebef8a..0000000
--- a/platform/linux-generic/test/ring/ringtest.c
+++ /dev/null
@@ -1,495 +0,0 @@
-/* Copyright (c) 2014, 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_api.h>
-#include <odp/helper/linux.h>
-#include <odp_packet_io_ring_internal.h>
-#include <test_debug.h>
-#include <odp_cunit_common.h>
-
-#define RING_SIZE 4096
-#define MAX_BULK 32
-
-enum {
- ODP_RING_TEST_BASIC,
- ODP_RING_TEST_STRESS,
-};
-
-/* local struct for ring_thread argument */
-typedef struct {
- pthrd_arg thrdarg;
- int stress_type;
-} ring_arg_t;
-
-static int test_ring_basic(_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 == NULL) {
- 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 == NULL) {
- 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 = _ring_sp_enqueue_burst(r, cur_src, 1);
- cur_src += 1;
- if ((ret & _RING_SZ_MASK) != 1) {
- LOG_ERR("sp_enq for 1 obj failed\n");
- goto fail;
- }
-
- printf("enqueue 2 objs\n");
- ret = _ring_sp_enqueue_burst(r, cur_src, 2);
- cur_src += 2;
- if ((ret & _RING_SZ_MASK) != 2) {
- LOG_ERR("sp_enq for 2 obj failed\n");
- goto fail;
- }
-
- printf("enqueue MAX_BULK objs\n");
- ret = _ring_sp_enqueue_burst(r, cur_src, MAX_BULK);
- if ((ret & _RING_SZ_MASK) != MAX_BULK) {
- LOG_ERR("sp_enq for %d obj failed\n", MAX_BULK);
- goto fail;
- }
-
- printf("dequeue 1 obj\n");
- ret = _ring_sc_dequeue_burst(r, cur_dst, 1);
- cur_dst += 1;
- if ((ret & _RING_SZ_MASK) != 1) {
- LOG_ERR("sc_deq for 1 obj failed\n");
- goto fail;
- }
-
- printf("dequeue 2 objs\n");
- ret = _ring_sc_dequeue_burst(r, cur_dst, 2);
- cur_dst += 2;
- if ((ret & _RING_SZ_MASK) != 2) {
- LOG_ERR("sc_deq for 2 obj failed\n");
- goto fail;
- }
-
- printf("dequeue MAX_BULK objs\n");
- ret = _ring_sc_dequeue_burst(r, cur_dst, MAX_BULK);
- cur_dst += MAX_BULK;
- if ((ret & _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 = _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 = _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 = _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 = _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 = _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 = _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");
- _ring_set_water_mark(r, 20);
- num_elems = 16;
-
- cur_src = src;
- cur_dst = dst;
-
- ret = _ring_mp_enqueue_bulk(r, cur_src, num_elems);
- cur_src += num_elems;
- if (ret != 0) {
- LOG_ERR("Cannot enqueue\n");
- goto fail;
- }
- ret = _ring_mp_enqueue_bulk(r, cur_src, num_elems);
- if (ret != -EDQUOT) {
- LOG_ERR("Watermark not exceeded\n");
- goto fail;
- }
- ret = _ring_mc_dequeue_bulk(r, cur_dst, num_elems);
- cur_dst += num_elems;
- if (ret != 0) {
- LOG_ERR("Cannot dequeue\n");
- goto fail;
- }
- ret = _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 _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 == NULL) {
- 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 = _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 == NULL) {
- LOG_ERR("failed to allocate consumer memory.\n");
- return -1;
- }
-
- do {
- i = _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");
- }
-}
-
-static int test_ring(void *arg)
-{
- ring_arg_t *parg = (ring_arg_t *)arg;
- int thr;
- char ring_name[_RING_NAMESIZE];
- _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 = _ring_create(ring_name, RING_SIZE,
- 0 /* not used, alignement
- taken care inside func : todo */);
- if (r == NULL) {
- LOG_ERR("ring create failed\n");
- result = -1;
- break;
- }
- /* lookup ring from its name */
- if (_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;
- }
-
- if (result)
- _ring_list_dump();
-
- break;
-
- case ODP_RING_TEST_STRESS:
- test_ring_stress(parg->stress_type);
-
- if (result)
- _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 0;
-}
-
-int main(int argc, char *argv[])
-{
- ring_arg_t rarg;
- odph_odpthread_t thread_tbl[MAX_WORKERS];
- odp_cpumask_t cpu_mask;
- char ring_name[_RING_NAMESIZE];
- odp_instance_t instance;
- odph_odpthread_params_t thr_params;
-
- if (odp_init_global(&instance, NULL, NULL)) {
- LOG_ERR("Error: ODP global init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
- LOG_ERR("Error: ODP local init failed.\n");
- exit(EXIT_FAILURE);
- }
-
- /* let helper collect its own arguments (e.g. --odph_proc) */
- odph_parse_options(argc, argv, NULL, NULL);
-
- _ring_tailq_init();
-
- odp_cpumask_default_worker(&cpu_mask, MAX_WORKERS);
- rarg.thrdarg.numthrds = rarg.thrdarg.numthrds;
-
- rarg.thrdarg.testcase = ODP_RING_TEST_BASIC;
-
- memset(&thr_params, 0, sizeof(thr_params));
- thr_params.start = test_ring;
- thr_params.arg = &rarg;
- thr_params.thr_type = ODP_THREAD_WORKER;
- thr_params.instance = instance;
-
- printf("starting stess test type : %d..\n", rarg.stress_type);
- odph_odpthreads_create(&thread_tbl[0], &cpu_mask, &thr_params);
- odph_odpthreads_join(thread_tbl);
-
- rarg.thrdarg.testcase = ODP_RING_TEST_STRESS;
- rarg.stress_type = one_enq_one_deq;
-
- printf("starting stess test type : %d..\n", rarg.stress_type);
- snprintf(ring_name, sizeof(ring_name), "test_ring_stress");
- r_stress = _ring_create(ring_name, RING_SIZE,
- 0/* not used, alignement
- taken care inside func : todo */);
- if (r_stress == NULL) {
- LOG_ERR("ring create failed\n");
- goto fail;
- }
- /* lookup ring from its name */
- if (_ring_lookup(ring_name) != r_stress) {
- LOG_ERR("ring lookup failed\n");
- goto fail;
- }
-
- thr_params.start = test_ring;
- thr_params.arg = &rarg;
-
- odph_odpthreads_create(&thread_tbl[0], &cpu_mask, &thr_params);
- odph_odpthreads_join(thread_tbl);
-
-fail:
- if (odp_term_local()) {
- LOG_ERR("Error: ODP local term failed.\n");
- exit(EXIT_FAILURE);
- }
-
- if (odp_term_global(instance)) {
- LOG_ERR("Error: ODP global term failed.\n");
- exit(EXIT_FAILURE);
- }
-
- return 0;
-}
_______________________________________________
lng-odp mailing list
lng-odp@lists.linaro.org
https://lists.linaro.org/mailman/listinfo/lng-odp