diff mbox

[1/2] test: helper: add ring test

Message ID 1432748352-13524-1-git-send-email-mike.holmes@linaro.org
State New
Headers show

Commit Message

Mike Holmes May 27, 2015, 5:39 p.m. UTC
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

Comments

Christophe Milard May 28, 2015, 6:43 a.m. UTC | #1
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
>
>
Mike Holmes May 28, 2015, 9:18 a.m. UTC | #2
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 mbox

Patch

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;
+}