Message ID | 1432748352-13524-1-git-send-email-mike.holmes@linaro.org |
---|---|
State | New |
Headers | show |
Hi Mike, Got: PASS: odp_thread PASS: odp_process ../../../test-driver: line 107: 7705 Segmentation fault (core dumped) "$@" > $log_file 2>&1 FAIL: odp_ring when running PATCH_DIR=/home/erachmi/incoming ./apply-and-build.sh (check-odp) On 27 May 2015 at 19:39, Mike Holmes <mike.holmes@linaro.org> wrote: > Signed-off-by: Mike Holmes <mike.holmes@linaro.org> > --- > 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 > >
On 28 May 2015 at 02:43, Christophe Milard <christophe.milard@linaro.org> wrote: > Hi Mike, > > Got: > PASS: odp_thread > PASS: odp_process > ../../../test-driver: line 107: 7705 Segmentation fault (core > dumped) "$@" > $log_file 2>&1 > FAIL: odp_ring > when running > PATCH_DIR=/home/erachmi/incoming ./apply-and-build.sh > (check-odp) > > Thanks, will take a look > > On 27 May 2015 at 19:39, Mike Holmes <mike.holmes@linaro.org> wrote: > >> Signed-off-by: Mike Holmes <mike.holmes@linaro.org> >> --- >> 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 >> >> >
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; +}
Signed-off-by: Mike Holmes <mike.holmes@linaro.org> --- 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