diff options
Diffstat (limited to 'test/common_plat')
152 files changed, 25988 insertions, 0 deletions
diff --git a/test/common_plat/Makefile.am b/test/common_plat/Makefile.am new file mode 100644 index 000000000..af78bb653 --- /dev/null +++ b/test/common_plat/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = + +if cunit_support +SUBDIRS += common +endif + +SUBDIRS += performance miscellaneous validation diff --git a/test/common_plat/common/Makefile.am b/test/common_plat/common/Makefile.am new file mode 100644 index 000000000..fd41fb428 --- /dev/null +++ b/test/common_plat/common/Makefile.am @@ -0,0 +1,13 @@ +AUTOMAKE_OPTIONS = foreign +include $(top_srcdir)/test/Makefile.inc + +noinst_LTLIBRARIES = libcunit_common.la libcpumask_common.la libthrmask_common.la + +libcunit_common_la_SOURCES = odp_cunit_common.c + +libcpumask_common_la_SOURCES = mask_common.c + +libthrmask_common_la_SOURCES = mask_common.c +libthrmask_common_la_CFLAGS = $(AM_CFLAGS) -DTEST_THRMASK + +EXTRA_DIST = mask_common.h odp_cunit_common.h diff --git a/test/common_plat/common/mask_common.c b/test/common_plat/common/mask_common.c new file mode 100644 index 000000000..b31534c64 --- /dev/null +++ b/test/common_plat/common/mask_common.c @@ -0,0 +1,475 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> + +#include "odp_cunit_common.h" +#include "mask_common.h" + +#include <stdlib.h> + +/* + * The following strings are used to build masks with odp_*mask_from_str(). + * Both 0x prefixed and non prefixed hex values are supported. + */ +#define TEST_MASK_NONE "0x0" +#define TEST_MASK_0 "0x1" +#define TEST_MASK_1 "0x2" +#define TEST_MASK_2 "0x4" +#define TEST_MASK_0_2 "0x5" +#define TEST_MASK_0_3 "0x9" +#define TEST_MASK_1_2 "0x6" +#define TEST_MASK_1_3 "0xA" +#define TEST_MASK_0_1_2 "0x7" +#define TEST_MASK_0_2_4_6 "0x55" +#define TEST_MASK_1_2_4_6 "0x56" + +#define TEST_MASK_0_NO_PREFIX "1" + +/* padding pattern used to check buffer overflow: */ +#define FILLING_PATTERN 0x55 + +/* + * returns the length of a string, excluding terminating NULL. + * As its C lib strlen equivalent. Just rewritten here to avoid C lib + * dependency in ODP tests (for platform independent / bare metal testing) + */ +static unsigned int stringlen(const char *str) +{ + unsigned int i = 0; + + while (str[i] != 0) + i++; + return i; +} + +/* + * builds a string containing a 0x prefixed hex number where a single bit + * (corresponding to a cpu or thread) is set. + * The string is null terminated. + * bit_set_str(0) returns "0x1". + * bit_set_str(10) returns "0x400". + * The buffer should be at least ceil(offs/4)+3 bytes long, + * to accommodate with 4 bits per nibble + "0x" prefix + null. + */ +#define BITS_PER_NIBBLE 4 +static void bit_set_str(char *buff, int offs) +{ + const char *hex_nibble = "1248"; + int i = 0; + + buff[i++] = '0'; + buff[i++] = 'x'; + buff[i++] = hex_nibble[offs % BITS_PER_NIBBLE]; + while (offs > 3) { + buff[i++] = '0'; + offs -= BITS_PER_NIBBLE; + } + buff[i++] = 0; /* null */ +} + +/* + * Returns the maximum number of CPUs that a mask can contain. + */ +unsigned mask_capacity(void) +{ + _odp_mask_t mask; + + _odp_mask_setall(&mask); + + return _odp_mask_count(&mask); +} + +MASK_TESTFUNC(to_from_str) +{ + _odp_mask_t mask; + int32_t str_sz; + unsigned int buf_sz; /* buf size for the 2 following bufs */ + char *buf_in; + char *buf_out; + unsigned int cpu; + unsigned int i; + + /* makes sure the mask has room for at least 1 CPU...: */ + CU_ASSERT_FATAL(mask_capacity() > 0); + + /* allocate memory for the buffers containing the mask strings: + 1 char per nibble, i.e. 1 char per 4 cpus +extra for "0x" and null:*/ + buf_sz = (mask_capacity() >> 2) + 20; + buf_in = malloc(buf_sz); + buf_out = malloc(buf_sz); + CU_ASSERT_FATAL(buf_in && buf_out); + + /* test 1 CPU at a time for all possible cpu positions in the mask */ + for (cpu = 0; cpu < mask_capacity(); cpu++) { + /* init buffer for overwrite check: */ + for (i = 0; i < buf_sz; i++) + buf_out[i] = FILLING_PATTERN; + + /* generate a hex string with that cpu set: */ + bit_set_str(buf_in, cpu); + + /* generate mask: */ + _odp_mask_from_str(&mask, buf_in); + + /* reverse cpu mask computation to get string back: */ + str_sz = _odp_mask_to_str(&mask, buf_out, + stringlen(buf_in) + 1); + + /* check that returned size matches original (with NULL): */ + CU_ASSERT(str_sz == (int32_t)stringlen(buf_in) + 1); + + /* check that returned string matches original (with NULL): */ + CU_ASSERT_NSTRING_EQUAL(buf_out, buf_in, stringlen(buf_in) + 1); + + /* check that no extra buffer writes occurred: */ + CU_ASSERT(buf_out[stringlen(buf_in) + 2] == FILLING_PATTERN); + } + + /* re-init buffer for overwrite check: */ + for (i = 0; i < buf_sz; i++) + buf_out[i] = FILLING_PATTERN; + + /* check for buffer overflow when too small buffer given: */ + _odp_mask_from_str(&mask, TEST_MASK_0); + str_sz = _odp_mask_to_str(&mask, buf_out, stringlen(TEST_MASK_0)); + + CU_ASSERT(str_sz == -1); + + for (i = 0; i < buf_sz; i++) + CU_ASSERT(buf_out[i] == FILLING_PATTERN); + + /* check for handling of missing "0x" prefix: */ + _odp_mask_from_str(&mask, TEST_MASK_0_NO_PREFIX); + + str_sz = _odp_mask_to_str(&mask, buf_out, + stringlen(TEST_MASK_0) + 1); + CU_ASSERT(str_sz == (int32_t)stringlen(TEST_MASK_0) + 1); + + CU_ASSERT_NSTRING_EQUAL(buf_out, TEST_MASK_0, + stringlen(TEST_MASK_0) + 1); + + free(buf_out); + free(buf_in); +} + +MASK_TESTFUNC(equal) +{ + _odp_mask_t mask1; + _odp_mask_t mask2; + _odp_mask_t mask3; + + _odp_mask_from_str(&mask1, TEST_MASK_0); + _odp_mask_from_str(&mask2, TEST_MASK_0); + _odp_mask_from_str(&mask3, TEST_MASK_NONE); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); + CU_ASSERT_FALSE(_odp_mask_equal(&mask1, &mask3)); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_0_2); + _odp_mask_from_str(&mask2, TEST_MASK_0_2); + _odp_mask_from_str(&mask3, TEST_MASK_1_2); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); + CU_ASSERT_FALSE(_odp_mask_equal(&mask1, &mask3)); + + if (mask_capacity() < 8) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_0_2_4_6); + _odp_mask_from_str(&mask2, TEST_MASK_0_2_4_6); + _odp_mask_from_str(&mask3, TEST_MASK_1_2_4_6); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); + CU_ASSERT_FALSE(_odp_mask_equal(&mask1, &mask3)); +} + +MASK_TESTFUNC(zero) +{ + _odp_mask_t mask1; + _odp_mask_t mask2; + + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + _odp_mask_from_str(&mask2, TEST_MASK_0); + _odp_mask_zero(&mask2); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); +} + +MASK_TESTFUNC(set) +{ + _odp_mask_t mask1; + _odp_mask_t mask2; + + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + _odp_mask_from_str(&mask2, TEST_MASK_0); + _odp_mask_set(&mask1, 0); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask2, TEST_MASK_0_3); + _odp_mask_set(&mask1, 3); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); + + /* make sure that re-asserting a cpu has no impact: */ + _odp_mask_set(&mask1, 3); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); +} + +MASK_TESTFUNC(clr) +{ + _odp_mask_t mask1; + _odp_mask_t mask2; + + _odp_mask_from_str(&mask1, TEST_MASK_0); + _odp_mask_from_str(&mask2, TEST_MASK_NONE); + _odp_mask_clr(&mask1, 0); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_0_2); + _odp_mask_from_str(&mask2, TEST_MASK_0); + _odp_mask_clr(&mask1, 2); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); + + _odp_mask_from_str(&mask2, TEST_MASK_NONE); + _odp_mask_clr(&mask1, 0); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); + + /* make sure that re-clearing a cpu has no impact: */ + _odp_mask_clr(&mask1, 0); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); +} + +MASK_TESTFUNC(isset) +{ + _odp_mask_t mask1; + + _odp_mask_from_str(&mask1, TEST_MASK_0); + CU_ASSERT(_odp_mask_isset(&mask1, 0)); + + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + CU_ASSERT_FALSE(_odp_mask_isset(&mask1, 0)); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_0_2); + CU_ASSERT(_odp_mask_isset(&mask1, 0)); + CU_ASSERT_FALSE(_odp_mask_isset(&mask1, 1)); + CU_ASSERT(_odp_mask_isset(&mask1, 2)); + CU_ASSERT_FALSE(_odp_mask_isset(&mask1, 3)); +} + +MASK_TESTFUNC(count) +{ + _odp_mask_t mask1; + + _odp_mask_from_str(&mask1, TEST_MASK_0); + CU_ASSERT(_odp_mask_count(&mask1) == 1); + + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + CU_ASSERT(_odp_mask_count(&mask1) == 0); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_0_2); + CU_ASSERT(_odp_mask_count(&mask1) == 2); +} + +MASK_TESTFUNC(and) +{ + _odp_mask_t mask1; + _odp_mask_t mask2; + _odp_mask_t mask3; + _odp_mask_t mask4; + + _odp_mask_from_str(&mask1, TEST_MASK_0); + _odp_mask_from_str(&mask2, TEST_MASK_0); + _odp_mask_from_str(&mask4, TEST_MASK_0); + _odp_mask_and(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); + + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + _odp_mask_from_str(&mask2, TEST_MASK_0); + _odp_mask_from_str(&mask4, TEST_MASK_NONE); + _odp_mask_and(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); + + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + _odp_mask_from_str(&mask2, TEST_MASK_NONE); + _odp_mask_from_str(&mask4, TEST_MASK_NONE); + _odp_mask_and(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_0_2); + _odp_mask_from_str(&mask2, TEST_MASK_1_2); + _odp_mask_from_str(&mask4, TEST_MASK_2); + _odp_mask_and(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); +} + +MASK_TESTFUNC(or) +{ + _odp_mask_t mask1; + _odp_mask_t mask2; + _odp_mask_t mask3; + _odp_mask_t mask4; + + _odp_mask_from_str(&mask1, TEST_MASK_0); + _odp_mask_from_str(&mask2, TEST_MASK_0); + _odp_mask_from_str(&mask4, TEST_MASK_0); + _odp_mask_or(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); + + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + _odp_mask_from_str(&mask2, TEST_MASK_0); + _odp_mask_from_str(&mask4, TEST_MASK_0); + _odp_mask_or(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); + + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + _odp_mask_from_str(&mask2, TEST_MASK_NONE); + _odp_mask_from_str(&mask4, TEST_MASK_NONE); + _odp_mask_or(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_0_2); + _odp_mask_from_str(&mask2, TEST_MASK_1); + _odp_mask_from_str(&mask4, TEST_MASK_0_1_2); + _odp_mask_or(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); +} + +MASK_TESTFUNC(xor) +{ + _odp_mask_t mask1; + _odp_mask_t mask2; + _odp_mask_t mask3; + _odp_mask_t mask4; + + _odp_mask_from_str(&mask1, TEST_MASK_0); + _odp_mask_from_str(&mask2, TEST_MASK_0); + _odp_mask_from_str(&mask4, TEST_MASK_NONE); + _odp_mask_xor(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); + + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + _odp_mask_from_str(&mask2, TEST_MASK_0); + _odp_mask_from_str(&mask4, TEST_MASK_0); + _odp_mask_xor(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); + + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + _odp_mask_from_str(&mask2, TEST_MASK_NONE); + _odp_mask_from_str(&mask4, TEST_MASK_NONE); + _odp_mask_xor(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_2); + _odp_mask_from_str(&mask2, TEST_MASK_1_2); + _odp_mask_from_str(&mask4, TEST_MASK_1); + _odp_mask_xor(&mask3, &mask1, &mask2); + CU_ASSERT(_odp_mask_equal(&mask3, &mask4)); +} + +MASK_TESTFUNC(copy) +{ + _odp_mask_t mask1; + _odp_mask_t mask2; + + _odp_mask_from_str(&mask1, TEST_MASK_0); + _odp_mask_copy(&mask2, &mask1); + CU_ASSERT(_odp_mask_equal(&mask1, &mask2)); +} + +MASK_TESTFUNC(first) +{ + _odp_mask_t mask1; + + /* check when there is no first */ + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + CU_ASSERT(_odp_mask_first(&mask1) == -1); + + /* single CPU case: */ + _odp_mask_from_str(&mask1, TEST_MASK_0); + CU_ASSERT(_odp_mask_first(&mask1) == 0); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_1_3); + CU_ASSERT(_odp_mask_first(&mask1) == 1); +} + +MASK_TESTFUNC(last) +{ + _odp_mask_t mask1; + + /* check when there is no last: */ + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + CU_ASSERT(_odp_mask_last(&mask1) == -1); + + /* single CPU case: */ + _odp_mask_from_str(&mask1, TEST_MASK_0); + CU_ASSERT(_odp_mask_last(&mask1) == 0); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_1_3); + CU_ASSERT(_odp_mask_last(&mask1) == 3); +} + +MASK_TESTFUNC(next) +{ + unsigned int i; + int expected[] = {1, 3, 3, -1}; + _odp_mask_t mask1; + + /* case when the mask does not contain any CPU: */ + _odp_mask_from_str(&mask1, TEST_MASK_NONE); + CU_ASSERT(_odp_mask_next(&mask1, -1) == -1); + + /* case when the mask just contain CPU 0: */ + _odp_mask_from_str(&mask1, TEST_MASK_0); + CU_ASSERT(_odp_mask_next(&mask1, -1) == 0); + CU_ASSERT(_odp_mask_next(&mask1, 0) == -1); + + if (mask_capacity() < 4) + return; + + _odp_mask_from_str(&mask1, TEST_MASK_1_3); + + for (i = 0; i < sizeof(expected) / sizeof(int); i++) + CU_ASSERT(_odp_mask_next(&mask1, i) == expected[i]); +} + +MASK_TESTFUNC(setall) +{ + int num; + int max = mask_capacity(); + _odp_mask_t mask; + + _odp_mask_setall(&mask); + num = _odp_mask_count(&mask); + + CU_ASSERT(num > 0); + CU_ASSERT(num <= max); +} diff --git a/test/common_plat/common/mask_common.h b/test/common_plat/common/mask_common.h new file mode 100644 index 000000000..e7a38a7c7 --- /dev/null +++ b/test/common_plat/common/mask_common.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ODP_MASK_COMMON_H_ +#define ODP_MASK_COMMON_H_ + +/* + * The same set of tests are used for testing both the odp_thrmask_ and + * odp_cpumask_ APIs. + * + * To build the thrmask tests TEST_THRMASK must be defined. + */ +#ifdef TEST_THRMASK +typedef odp_thrmask_t _odp_mask_t; +#define MASK_API_PREFIX(n) odp_thrmask_##n +#define MASK_TESTFUNC(n) void thread_test_odp_thrmask_##n(void) +#else +typedef odp_cpumask_t _odp_mask_t; +#define MASK_API_PREFIX(n) odp_cpumask_##n +#define MASK_TESTFUNC(n) void cpumask_test_odp_cpumask_##n(void) +#endif + +#define _odp_mask_from_str MASK_API_PREFIX(from_str) +#define _odp_mask_to_str MASK_API_PREFIX(to_str) +#define _odp_mask_equal MASK_API_PREFIX(equal) +#define _odp_mask_zero MASK_API_PREFIX(zero) +#define _odp_mask_set MASK_API_PREFIX(set) +#define _odp_mask_clr MASK_API_PREFIX(clr) +#define _odp_mask_isset MASK_API_PREFIX(isset) +#define _odp_mask_count MASK_API_PREFIX(count) +#define _odp_mask_and MASK_API_PREFIX(and) +#define _odp_mask_or MASK_API_PREFIX(or) +#define _odp_mask_xor MASK_API_PREFIX(xor) +#define _odp_mask_copy MASK_API_PREFIX(copy) +#define _odp_mask_first MASK_API_PREFIX(first) +#define _odp_mask_next MASK_API_PREFIX(next) +#define _odp_mask_last MASK_API_PREFIX(last) +#define _odp_mask_setall MASK_API_PREFIX(setall) + +unsigned mask_capacity(void); + +MASK_TESTFUNC(to_from_str); +MASK_TESTFUNC(equal); +MASK_TESTFUNC(zero); +MASK_TESTFUNC(set); +MASK_TESTFUNC(clr); +MASK_TESTFUNC(isset); +MASK_TESTFUNC(count); +MASK_TESTFUNC(and); +MASK_TESTFUNC(or); +MASK_TESTFUNC(xor); +MASK_TESTFUNC(copy); +MASK_TESTFUNC(first); +MASK_TESTFUNC(last); +MASK_TESTFUNC(next); +MASK_TESTFUNC(setall); + +#endif diff --git a/test/common_plat/common/odp_cunit_common.c b/test/common_plat/common/odp_cunit_common.c new file mode 100644 index 000000000..2337c92b0 --- /dev/null +++ b/test/common_plat/common/odp_cunit_common.c @@ -0,0 +1,373 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <string.h> +#include <odp_api.h> +#include <odp_cunit_common.h> +#include <odp/helper/linux.h> +/* Globals */ +static odph_odpthread_t thread_tbl[MAX_WORKERS]; +static odp_instance_t instance; + +/* + * global init/term functions which may be registered + * defaults to functions performing odp init/term. + */ +static int tests_global_init(odp_instance_t *inst); +static int tests_global_term(odp_instance_t inst); +static struct { + int (*global_init_ptr)(odp_instance_t *inst); + int (*global_term_ptr)(odp_instance_t inst); +} global_init_term = {tests_global_init, tests_global_term}; + +static odp_suiteinfo_t *global_testsuites; + +/** create test thread */ +int odp_cunit_thread_create(int func_ptr(void *), pthrd_arg *arg) +{ + odp_cpumask_t cpumask; + odph_odpthread_params_t thr_params; + + memset(&thr_params, 0, sizeof(thr_params)); + thr_params.start = func_ptr; + thr_params.arg = arg; + thr_params.thr_type = ODP_THREAD_WORKER; + thr_params.instance = instance; + + /* Create and init additional threads */ + odp_cpumask_default_worker(&cpumask, arg->numthrds); + + return odph_odpthreads_create(thread_tbl, &cpumask, &thr_params); +} + +/** exit from test thread */ +int odp_cunit_thread_exit(pthrd_arg *arg) +{ + /* Wait for other threads to exit */ + if (odph_odpthreads_join(thread_tbl) != arg->numthrds) { + fprintf(stderr, + "error: odph_odpthreads_join() failed.\n"); + return -1; + } + + return 0; +} + +static int tests_global_init(odp_instance_t *inst) +{ + if (0 != odp_init_global(inst, NULL, NULL)) { + fprintf(stderr, "error: odp_init_global() failed.\n"); + return -1; + } + if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) { + fprintf(stderr, "error: odp_init_local() failed.\n"); + return -1; + } + + return 0; +} + +static int tests_global_term(odp_instance_t inst) +{ + if (0 != odp_term_local()) { + fprintf(stderr, "error: odp_term_local() failed.\n"); + return -1; + } + + if (0 != odp_term_global(inst)) { + fprintf(stderr, "error: odp_term_global() failed.\n"); + return -1; + } + + return 0; +} + +/* + * register tests_global_init and tests_global_term functions. + * If some of these functions are not registered, the defaults functions + * (tests_global_init() and tests_global_term()) defined above are used. + * One should use these register functions when defining these hooks. + * Note that passing NULL as function pointer is valid and will simply + * prevent the default (odp init/term) to be done. + */ +void odp_cunit_register_global_init(int (*func_init_ptr)(odp_instance_t *inst)) +{ + global_init_term.global_init_ptr = func_init_ptr; +} + +void odp_cunit_register_global_term(int (*func_term_ptr)(odp_instance_t inst)) +{ + global_init_term.global_term_ptr = func_term_ptr; +} + +static odp_suiteinfo_t *cunit_get_suite_info(const char *suite_name) +{ + odp_suiteinfo_t *sinfo; + + for (sinfo = global_testsuites; sinfo->pName; sinfo++) + if (strcmp(sinfo->pName, suite_name) == 0) + return sinfo; + + return NULL; +} + +static odp_testinfo_t *cunit_get_test_info(odp_suiteinfo_t *sinfo, + const char *test_name) +{ + odp_testinfo_t *tinfo; + + for (tinfo = sinfo->pTests; tinfo->pName; tinfo++) + if (strcmp(tinfo->pName, test_name) == 0) + return tinfo; + + return NULL; +} + +/* A wrapper for the suite's init function. This is done to allow for a + * potential runtime check to determine whether each test in the suite + * is active (enabled by using ODP_TEST_INFO_CONDITIONAL()). If present, + * the conditional check is run after the suite's init function. + */ +static int _cunit_suite_init(void) +{ + int ret = 0; + CU_pSuite cur_suite = CU_get_current_suite(); + odp_suiteinfo_t *sinfo; + odp_testinfo_t *tinfo; + + /* find the suite currently being run */ + cur_suite = CU_get_current_suite(); + if (!cur_suite) + return -1; + + sinfo = cunit_get_suite_info(cur_suite->pName); + if (!sinfo) + return -1; + + /* execute its init function */ + if (sinfo->pInitFunc) { + ret = sinfo->pInitFunc(); + if (ret) + return ret; + } + + /* run any configured conditional checks and mark inactive tests */ + for (tinfo = sinfo->pTests; tinfo->pName; tinfo++) { + CU_pTest ptest; + CU_ErrorCode err; + + if (!tinfo->check_active || tinfo->check_active()) + continue; + + /* test is inactive, mark it as such */ + ptest = CU_get_test_by_name(tinfo->pName, cur_suite); + if (ptest) + err = CU_set_test_active(ptest, CU_FALSE); + else + err = CUE_NOTEST; + + if (err != CUE_SUCCESS) { + fprintf(stderr, "%s: failed to set test %s inactive\n", + __func__, tinfo->pName); + return -1; + } + } + + return ret; +} + +/* + * Register suites and tests with CUnit. + * + * Similar to CU_register_suites() but using locally defined wrapper + * types. + */ +static int cunit_register_suites(odp_suiteinfo_t testsuites[]) +{ + odp_suiteinfo_t *sinfo; + odp_testinfo_t *tinfo; + CU_pSuite suite; + CU_pTest test; + + for (sinfo = testsuites; sinfo->pName; sinfo++) { + suite = CU_add_suite(sinfo->pName, + _cunit_suite_init, sinfo->pCleanupFunc); + if (!suite) + return CU_get_error(); + + for (tinfo = sinfo->pTests; tinfo->pName; tinfo++) { + test = CU_add_test(suite, tinfo->pName, + tinfo->pTestFunc); + if (!test) + return CU_get_error(); + } + } + + return 0; +} + +static int cunit_update_test(CU_pSuite suite, + odp_suiteinfo_t *sinfo, + odp_testinfo_t *updated_tinfo) +{ + CU_pTest test = NULL; + CU_ErrorCode err; + odp_testinfo_t *tinfo; + const char *test_name = updated_tinfo->pName; + + tinfo = cunit_get_test_info(sinfo, test_name); + if (tinfo) + test = CU_get_test(suite, test_name); + + if (!tinfo || !test) { + fprintf(stderr, "%s: unable to find existing test named %s\n", + __func__, test_name); + return -1; + } + + err = CU_set_test_func(test, updated_tinfo->pTestFunc); + if (err != CUE_SUCCESS) { + fprintf(stderr, "%s: failed to update test func for %s\n", + __func__, test_name); + return -1; + } + + tinfo->check_active = updated_tinfo->check_active; + + return 0; +} + +static int cunit_update_suite(odp_suiteinfo_t *updated_sinfo) +{ + CU_pSuite suite = NULL; + CU_ErrorCode err; + odp_suiteinfo_t *sinfo; + odp_testinfo_t *tinfo; + + /* find previously registered suite with matching name */ + sinfo = cunit_get_suite_info(updated_sinfo->pName); + + if (sinfo) { + /* lookup the associated CUnit suite */ + suite = CU_get_suite_by_name(updated_sinfo->pName, + CU_get_registry()); + } + + if (!sinfo || !suite) { + fprintf(stderr, "%s: unable to find existing suite named %s\n", + __func__, updated_sinfo->pName); + return -1; + } + + sinfo->pInitFunc = updated_sinfo->pInitFunc; + sinfo->pCleanupFunc = updated_sinfo->pCleanupFunc; + + err = CU_set_suite_cleanupfunc(suite, updated_sinfo->pCleanupFunc); + if (err != CUE_SUCCESS) { + fprintf(stderr, "%s: failed to update cleanup func for %s\n", + __func__, updated_sinfo->pName); + return -1; + } + + for (tinfo = updated_sinfo->pTests; tinfo->pName; tinfo++) { + int ret; + + ret = cunit_update_test(suite, sinfo, tinfo); + if (ret != 0) + return ret; + } + + return 0; +} + +/* + * Run tests previously registered via odp_cunit_register() + */ +int odp_cunit_run(void) +{ + int ret; + + printf("\tODP API version: %s\n", odp_version_api_str()); + printf("\tODP implementation name: %s\n", odp_version_impl_name()); + printf("\tODP implementation version: %s\n", odp_version_impl_str()); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + + ret = CU_get_number_of_failure_records(); + + CU_cleanup_registry(); + + /* call test executable terminason hook, if any */ + if (global_init_term.global_term_ptr && + ((*global_init_term.global_term_ptr)(instance) != 0)) + return -1; + + return (ret) ? -1 : 0; +} + +/* + * Update suites/tests previously registered via odp_cunit_register(). + * + * Note that this is intended for modifying the properties of already + * registered suites/tests. New suites/tests can only be registered via + * odp_cunit_register(). + */ +int odp_cunit_update(odp_suiteinfo_t testsuites[]) +{ + int ret = 0; + odp_suiteinfo_t *sinfo; + + for (sinfo = testsuites; sinfo->pName && ret == 0; sinfo++) + ret = cunit_update_suite(sinfo); + + return ret; +} + +/* + * Register test suites to be run via odp_cunit_run() + */ +int odp_cunit_register(odp_suiteinfo_t testsuites[]) +{ + /* call test executable init hook, if any */ + if (global_init_term.global_init_ptr) { + if ((*global_init_term.global_init_ptr)(&instance) == 0) { + /* After ODP initialization, set main thread's + * CPU affinity to the 1st available control CPU core + */ + int cpu = 0; + odp_cpumask_t cpuset; + + odp_cpumask_zero(&cpuset); + if (odp_cpumask_default_control(&cpuset, 1) == 1) { + cpu = odp_cpumask_first(&cpuset); + odph_odpthread_setaffinity(cpu); + } + } else { + /* ODP initialization failed */ + return -1; + } + } + + CU_set_error_action(CUEA_ABORT); + + CU_initialize_registry(); + global_testsuites = testsuites; + cunit_register_suites(testsuites); + CU_set_fail_on_inactive(CU_FALSE); + + return 0; +} + +/* + * Parse command line options to extract options affectiong cunit_common. + * (hence also helpers options as cunit_common uses the helpers) + * Options private to the test calling cunit_common are not parsed here. + */ +int odp_cunit_parse_options(int argc, char *argv[]) +{ + return odph_parse_options(argc, argv, NULL, NULL); +} diff --git a/test/common_plat/common/odp_cunit_common.h b/test/common_plat/common/odp_cunit_common.h new file mode 100644 index 000000000..486a5ec51 --- /dev/null +++ b/test/common_plat/common/odp_cunit_common.h @@ -0,0 +1,106 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * ODP test application common headers + */ + +#ifndef ODP_CUNICT_COMMON_H +#define ODP_CUNICT_COMMON_H + +#include <stdint.h> +#include <inttypes.h> +#include "CUnit/Basic.h" +#include "CUnit/TestDB.h" +#include <odp_api.h> + +#define MAX_WORKERS 32 /**< Maximum number of work threads */ + +typedef int (*cunit_test_check_active)(void); + +typedef struct { + const char *pName; + CU_TestFunc pTestFunc; + cunit_test_check_active check_active; +} odp_testinfo_t; + +typedef struct { + const char *pName; + CU_InitializeFunc pInitFunc; + CU_CleanupFunc pCleanupFunc; + odp_testinfo_t *pTests; +} odp_suiteinfo_t; + +static inline int odp_cunit_test_inactive(void) { return 0; } +static inline void odp_cunit_test_missing(void) { } + +/* An active test case, with the test name matching the test function name */ +#define ODP_TEST_INFO(test_func) \ + {#test_func, test_func, NULL} + +/* A test case that is unconditionally inactive. Its name will be registered + * with CUnit but it won't be executed and will be reported as inactive in + * the result summary. */ +#define ODP_TEST_INFO_INACTIVE(test_func, args...) \ + {#test_func, odp_cunit_test_missing, odp_cunit_test_inactive} + +#define ODP_TEST_INACTIVE 0 +#define ODP_TEST_ACTIVE 1 + +/* A test case that may be marked as inactive at runtime based on the + * return value of the cond_func function. A return value of ODP_TEST_INACTIVE + * means inactive, ODP_TEST_ACTIVE means active. */ +#define ODP_TEST_INFO_CONDITIONAL(test_func, cond_func) \ + {#test_func, test_func, cond_func} + +#define ODP_TEST_INFO_NULL {NULL, NULL, NULL} +#define ODP_SUITE_INFO_NULL {NULL, NULL, NULL, NULL} + +typedef struct { + uint32_t foo; + uint32_t bar; +} test_shared_data_t; + +/** + * Thread argument + */ +typedef struct { + int testcase; /**< specifies which set of API's to exercise */ + int numthrds; /**< no of pthreads to create */ +} pthrd_arg; + +/* parse parameters that affect the behaviour of odp_cunit_common */ +int odp_cunit_parse_options(int argc, char *argv[]); +/* register suites to be run via odp_cunit_run() */ +int odp_cunit_register(odp_suiteinfo_t testsuites[]); +/* update tests previously registered via odp_cunit_register() */ +int odp_cunit_update(odp_suiteinfo_t testsuites[]); +/* the function, called by module main(), to run the testsuites: */ +int odp_cunit_run(void); + +/** create thread for start_routine function (which returns 0 on success) */ +int odp_cunit_thread_create(int func_ptr(void *), pthrd_arg *arg); +int odp_cunit_thread_exit(pthrd_arg *); + +/** + * Global tests initialization/termination. + * + * Initialize global resources needed by the test executable. Default + * definition does ODP init / term (both global and local). + * Test executables can override it by calling one of the register function + * below. + * The functions are called at the very beginning and very end of the test + * execution. Passing NULL to odp_cunit_register_global_init() and/or + * odp_cunit_register_global_term() is legal and will simply prevent the + * default (ODP init/term) to be done. + */ +void odp_cunit_register_global_init(int (*func_init_ptr)(odp_instance_t *inst)); + +void odp_cunit_register_global_term(int (*func_term_ptr)(odp_instance_t inst)); + +#endif /* ODP_CUNICT_COMMON_H */ diff --git a/test/common_plat/m4/configure.m4 b/test/common_plat/m4/configure.m4 new file mode 100644 index 000000000..be878bd7d --- /dev/null +++ b/test/common_plat/m4/configure.m4 @@ -0,0 +1,33 @@ +m4_include([test/common_plat/m4/miscellaneous.m4]) +m4_include([test/common_plat/m4/performance.m4]) +m4_include([test/common_plat/m4/validation.m4]) + +AC_CONFIG_FILES([test/common_plat/Makefile + test/common_plat/common/Makefile + test/common_plat/miscellaneous/Makefile + test/common_plat/performance/Makefile + test/common_plat/validation/Makefile + test/common_plat/validation/api/atomic/Makefile + test/common_plat/validation/api/barrier/Makefile + test/common_plat/validation/api/buffer/Makefile + test/common_plat/validation/api/classification/Makefile + test/common_plat/validation/api/cpumask/Makefile + test/common_plat/validation/api/crypto/Makefile + test/common_plat/validation/api/errno/Makefile + test/common_plat/validation/api/hash/Makefile + test/common_plat/validation/api/init/Makefile + test/common_plat/validation/api/lock/Makefile + test/common_plat/validation/api/Makefile + test/common_plat/validation/api/packet/Makefile + test/common_plat/validation/api/pktio/Makefile + test/common_plat/validation/api/pool/Makefile + test/common_plat/validation/api/queue/Makefile + test/common_plat/validation/api/random/Makefile + test/common_plat/validation/api/scheduler/Makefile + test/common_plat/validation/api/shmem/Makefile + test/common_plat/validation/api/std_clib/Makefile + test/common_plat/validation/api/system/Makefile + test/common_plat/validation/api/thread/Makefile + test/common_plat/validation/api/time/Makefile + test/common_plat/validation/api/timer/Makefile + test/common_plat/validation/api/traffic_mngr/Makefile]) diff --git a/test/common_plat/m4/miscellaneous.m4 b/test/common_plat/m4/miscellaneous.m4 new file mode 100644 index 000000000..cc881edb7 --- /dev/null +++ b/test/common_plat/m4/miscellaneous.m4 @@ -0,0 +1,9 @@ +########################################################################## +# Enable/disable test-cpp +########################################################################## +test_cpp=no +AC_ARG_ENABLE([test-cpp], + [ --enable-test-cpp run basic test aginast cpp], + [if test "x$enableval" = "xyes"; then + test_cpp=yes + fi]) diff --git a/test/common_plat/m4/performance.m4 b/test/common_plat/m4/performance.m4 new file mode 100644 index 000000000..1e2000d97 --- /dev/null +++ b/test/common_plat/m4/performance.m4 @@ -0,0 +1,9 @@ +########################################################################## +# Enable/disable test-perf +########################################################################## +test_perf=no +AC_ARG_ENABLE([test-perf], + [ --enable-test-perf run test in test/performance], + [if test "x$enableval" = "xyes"; then + test_perf=yes + fi]) diff --git a/test/common_plat/m4/validation.m4 b/test/common_plat/m4/validation.m4 new file mode 100644 index 000000000..d32f675ae --- /dev/null +++ b/test/common_plat/m4/validation.m4 @@ -0,0 +1,58 @@ +########################################################################## +# Enable/disable Unit tests +########################################################################## +cunit_support=no +test_vald=no +AC_ARG_ENABLE([test_vald], + [ --enable-test-vald run test in test/validation], + [if test x$enableval = xyes; then + test_vald=yes + cunit_support=yes + fi]) + +########################################################################## +# Enable/disable Unit tests +########################################################################## +AC_ARG_ENABLE([cunit_support], + [ --enable-cunit-support include cunit infrastructure], + [if test x$enableval = xyes; then + cunit_support=yes + fi]) + +########################################################################## +# Set optional CUnit path +########################################################################## +AC_ARG_WITH([cunit-path], +AC_HELP_STRING([--with-cunit-path=DIR path to CUnit libs and headers], + [(or in the default path if not specified).]), + [CUNIT_PATH=$withval + AM_CPPFLAGS="$AM_CPPFLAGS -I$CUNIT_PATH/include" + AM_LDFLAGS="$AM_LDFLAGS -L$CUNIT_PATH/lib" + cunit_support=yes],[]) + +########################################################################## +# Save and set temporary compilation flags +########################################################################## +OLD_LDFLAGS=$LDFLAGS +OLD_CPPFLAGS=$CPPFLAGS +LDFLAGS="$AM_LDFLAGS $LDFLAGS" +CPPFLAGS="$AM_CPPFLAGS $CPPFLAGS" + +########################################################################## +# Check for CUnit availability +########################################################################## +if test x$cunit_support = xyes +then + AC_CHECK_LIB([cunit],[CU_get_error], [], + [AC_MSG_ERROR([CUnit libraries required])]) + AC_CHECK_HEADERS([CUnit/Basic.h], [], + [AC_MSG_FAILURE(["can't find cunit headers"])]) +else + cunit_support=no +fi + +########################################################################## +# Restore old saved variables +########################################################################## +LDFLAGS=$OLD_LDFLAGS +CPPFLAGS=$OLD_CPPFLAGS diff --git a/test/common_plat/miscellaneous/.gitignore b/test/common_plat/miscellaneous/.gitignore new file mode 100644 index 000000000..6e555c58e --- /dev/null +++ b/test/common_plat/miscellaneous/.gitignore @@ -0,0 +1,3 @@ +odp_api_from_cpp +*.trs +*.log diff --git a/test/common_plat/miscellaneous/Makefile.am b/test/common_plat/miscellaneous/Makefile.am new file mode 100644 index 000000000..7d8cf3531 --- /dev/null +++ b/test/common_plat/miscellaneous/Makefile.am @@ -0,0 +1,12 @@ +include $(top_srcdir)/test/Makefile.inc + +if test_cpp +bin_PROGRAMS = odp_api_from_cpp$(EXEEXT) +TESTS = odp_api_from_cpp$(EXEEXT) +endif + +odp_api_from_cpp_CXXFLAGS = $(AM_CXXFLAGS) + +odp_api_from_cpp_LDFLAGS = $(AM_LDFLAGS) -static + +dist_odp_api_from_cpp_SOURCES = odp_api_from_cpp.cpp diff --git a/test/common_plat/miscellaneous/odp_api_from_cpp.cpp b/test/common_plat/miscellaneous/odp_api_from_cpp.cpp new file mode 100644 index 000000000..be74c275c --- /dev/null +++ b/test/common_plat/miscellaneous/odp_api_from_cpp.cpp @@ -0,0 +1,12 @@ +#include <cstdio> +#include <odp_api.h> +#include <odp/helper/linux.h> + +int main(int argc ODP_UNUSED, const char *argv[] ODP_UNUSED) +{ + + printf("\tODP API version: %s\n", odp_version_api_str()); + printf("\tODP implementation version: %s\n", odp_version_impl_str()); + + return 0; +} diff --git a/test/common_plat/performance/.gitignore b/test/common_plat/performance/.gitignore new file mode 100644 index 000000000..edcc83292 --- /dev/null +++ b/test/common_plat/performance/.gitignore @@ -0,0 +1,7 @@ +*.log +*.trs +odp_atomic +odp_crypto +odp_l2fwd +odp_pktio_perf +odp_scheduling diff --git a/test/common_plat/performance/Makefile.am b/test/common_plat/performance/Makefile.am new file mode 100644 index 000000000..d23bb3e51 --- /dev/null +++ b/test/common_plat/performance/Makefile.am @@ -0,0 +1,33 @@ +include $(top_srcdir)/test/Makefile.inc + +TESTS_ENVIRONMENT += TEST_DIR=${builddir} + +EXECUTABLES = odp_crypto$(EXEEXT) odp_pktio_perf$(EXEEXT) + +COMPILE_ONLY = odp_l2fwd$(EXEEXT) \ + odp_scheduling$(EXEEXT) + +TESTSCRIPTS = odp_l2fwd_run.sh \ + odp_scheduling_run.sh + +TEST_EXTENSIONS = .sh + +if test_perf +TESTS = $(EXECUTABLES) $(TESTSCRIPTS) +endif + +bin_PROGRAMS = $(EXECUTABLES) $(COMPILE_ONLY) + +odp_crypto_LDFLAGS = $(AM_LDFLAGS) -static +odp_crypto_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test +odp_scheduling_LDFLAGS = $(AM_LDFLAGS) -static +odp_scheduling_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test + +noinst_HEADERS = \ + $(top_srcdir)/test/test_debug.h + +dist_odp_crypto_SOURCES = odp_crypto.c +dist_odp_scheduling_SOURCES = odp_scheduling.c +dist_odp_pktio_perf_SOURCES = odp_pktio_perf.c + +EXTRA_DIST = $(TESTSCRIPTS) diff --git a/test/common_plat/performance/odp_crypto.c b/test/common_plat/performance/odp_crypto.c new file mode 100644 index 000000000..49a9f4b6f --- /dev/null +++ b/test/common_plat/performance/odp_crypto.c @@ -0,0 +1,972 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif /* _GNU_SOURCE */ + +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <odp_api.h> +#include <odp/helper/linux.h> + +#define app_err(fmt, ...) \ + fprintf(stderr, "%s:%d:%s(): Error: " fmt, __FILE__, \ + __LINE__, __func__, ##__VA_ARGS__) + +/** @def SHM_PKT_POOL_SIZE + * @brief Size of the shared memory block + */ +#define SHM_PKT_POOL_SIZE (512 * 2048 * 2) + +/** @def SHM_PKT_POOL_BUF_SIZE + * @brief Buffer size of the packet pool buffer + */ +#define SHM_PKT_POOL_BUF_SIZE (1024 * 32) + +static uint8_t test_iv[8] = "01234567"; + +static uint8_t test_key16[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, +}; + +static uint8_t test_key24[24] = { 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18 +}; + +/** + * Structure that holds template for session create call + * for different algorithms supported by test + */ +typedef struct { + const char *name; /**< Algorithm name */ + odp_crypto_session_params_t session; /**< Prefilled crypto session params */ + unsigned int hash_adjust; /**< Size of hash */ +} crypto_alg_config_t; + +/** + * Parsed command line crypto arguments. Describes test configuration. + */ +typedef struct { + /** + * If non zero prints content of packets. Enabled by -d or + * --debug option. + */ + int debug_packets; + + /** + * If non zero Try to run crypto operation in place. Note some + * implementation may not support such mode. Enabled by -n or + * --inplace option. + */ + int in_place; + + /** + * If non zeor output of previous operation taken as input for + * next encrypt operations. Enabled by -r or --reuse option. + */ + int reuse_packet; + + /** + * Maximum number of outstanding encryption requests. Note code + * poll for results over queue and if nothing is available it can + * submit more encryption requests up to maximum number specified by + * this option. Specified through -f or --flight option. + */ + int in_flight; + + /** + * Number of iteration to repeat crypto operation to get good + * average number. Specified through -i or --terations option. + * Default is 10000. + */ + int iteration_count; + + /** + * Maximum sessions. Currently is not used. + */ + int max_sessions; + + /** + * Payload size to test. If 0 set of predefined payload sizes + * is tested. Specified through -p or --payload option. + */ + int payload_length; + + /** + * Pointer to selected algorithm to test. If NULL all available + * alogorthims are tested. Name of algorithm is passed through + * -a or --algorithm option. + */ + crypto_alg_config_t *alg_config; + + /** + * Use scheduler to get completion events from crypto operation. + * Specified through -s argument. + * */ + int schedule; + + /* + * Poll completion queue for crypto completion events. + * Specified through -p argument. + */ + int poll; +} crypto_args_t; + +/* + * Helper structure that holds averages for test of one algorithm + * for given payload size. + */ +typedef struct { + /** + * Elapsed time for one crypto operation. + */ + double elapsed; + + /** + * CPU time spent pre one crypto operation by whole process + * i.e include current and all other threads in process. + * It is filled with 'getrusage(RUSAGE_SELF, ...)' call. + */ + double rusage_self; + + /** + * CPU time spent per one crypto operation by current thread + * only. It is filled with 'getrusage(RUSAGE_THREAD, ...)' + * call. + */ + double rusage_thread; +} crypto_run_result_t; + +/** + * Structure holds one snap to misc times of current process. + */ +typedef struct { + struct timeval tv; /**< Elapsed time */ + struct rusage ru_self; /**< Rusage value for whole process */ + struct rusage ru_thread; /**< Rusage value for current thread */ +} time_record_t; + +static void parse_args(int argc, char *argv[], crypto_args_t *cargs); +static void usage(char *progname); + +/** + * Set of predefined payloads. Make sure that maximum payload + * size is not bigger than SHM_PKT_POOL_BUF_SIZE. May relax when + * implementation start support segmented buffers/packets. + */ +static unsigned int payloads[] = { + 16, + 64, + 256, + 1024, + 8192, + 16384 +}; + +/** + * Set of known algorithms to test + */ +static crypto_alg_config_t algs_config[] = { + { + .name = "3des-cbc-null", + .session = { + .cipher_alg = ODP_CIPHER_ALG_3DES_CBC, + .cipher_key = { + .data = test_key24, + .length = sizeof(test_key24) + }, + .iv = { + .data = test_iv, + .length = 8, + }, + .auth_alg = ODP_AUTH_ALG_NULL + }, + }, + { + .name = "3des-cbc-hmac-md5-96", + .session = { + .cipher_alg = ODP_CIPHER_ALG_3DES_CBC, + .cipher_key = { + .data = test_key24, + .length = sizeof(test_key24) + }, + .iv = { + .data = test_iv, + .length = 8, + }, + .auth_alg = ODP_AUTH_ALG_MD5_96, + .auth_key = { + .data = test_key16, + .length = sizeof(test_key16) + } + }, + .hash_adjust = 12 + }, + { + .name = "null-hmac-md5-96", + .session = { + .cipher_alg = ODP_CIPHER_ALG_NULL, + .auth_alg = ODP_AUTH_ALG_MD5_96, + .auth_key = { + .data = test_key16, + .length = sizeof(test_key16) + } + }, + .hash_adjust = 12 + }, +}; + +/** + * Find corresponding config for given name. Returns NULL + * if config for given name is not found. + */ +static crypto_alg_config_t * +find_config_by_name(const char *name) { + unsigned int i; + crypto_alg_config_t *ret = NULL; + + for (i = 0; i < (sizeof(algs_config) / sizeof(crypto_alg_config_t)); + i++) { + if (strcmp(algs_config[i].name, name) == 0) { + ret = algs_config + i; + break; + } + } + return ret; +} + +/** + * Helper function that prints list of algorithms that this + * test understands. + */ +static void +print_config_names(const char *prefix) { + unsigned int i; + + for (i = 0; i < (sizeof(algs_config) / sizeof(crypto_alg_config_t)); + i++) { + printf("%s %s\n", prefix, algs_config[i].name); + } +} + +/** + * Snap current time values and put them into 'rec'. + */ +static void +fill_time_record(time_record_t *rec) +{ + gettimeofday(&rec->tv, NULL); + getrusage(RUSAGE_SELF, &rec->ru_self); + getrusage(RUSAGE_THREAD, &rec->ru_thread); +} + +/** + * Calculated CPU time difference for given two rusage structures. + * Note it adds user space and system time together. + */ +static unsigned long long +get_rusage_diff(struct rusage *start, struct rusage *end) +{ + unsigned long long rusage_diff; + unsigned long long rusage_start; + unsigned long long rusage_end; + + rusage_start = (start->ru_utime.tv_sec * 1000000) + + (start->ru_utime.tv_usec); + rusage_start += (start->ru_stime.tv_sec * 1000000) + + (start->ru_stime.tv_usec); + + rusage_end = (end->ru_utime.tv_sec * 1000000) + + (end->ru_utime.tv_usec); + rusage_end += (end->ru_stime.tv_sec * 1000000) + + (end->ru_stime.tv_usec); + + rusage_diff = rusage_end - rusage_start; + + return rusage_diff; +} + +/** + * Get diff for RUSAGE_SELF (whole process) between two time snap + * records. + */ +static unsigned long long +get_rusage_self_diff(time_record_t *start, time_record_t *end) +{ + return get_rusage_diff(&start->ru_self, &end->ru_self); +} + +/** + * Get diff for RUSAGE_THREAD (current thread only) between two + * time snap records. + */ +static unsigned long long +get_rusage_thread_diff(time_record_t *start, time_record_t *end) +{ + return get_rusage_diff(&start->ru_thread, &end->ru_thread); +} + +/** + * Get diff of elapsed time between two time snap records + */ +static unsigned long long +get_elapsed_usec(time_record_t *start, time_record_t *end) +{ + unsigned long long s; + unsigned long long e; + + s = (start->tv.tv_sec * 1000000) + + (start->tv.tv_usec); + e = (end->tv.tv_sec * 1000000) + + (end->tv.tv_usec); + + return e - s; +} + +#define REPORT_HEADER "\n%30.30s %15s %15s %15s %15s %15s %15s\n" +#define REPORT_LINE "%30.30s %15d %15d %15.3f %15.3f %15.3f %15d\n" + +/** + * Print header line for our report. + */ +static void +print_result_header(void) +{ + printf(REPORT_HEADER, + "algorithm", "avg over #", "payload (bytes)", "elapsed (us)", + "rusg self (us)", "rusg thrd (us)", "throughput (Kb)"); +} + +/** + * Print one line of our report. + */ +static void +print_result(crypto_args_t *cargs, + unsigned int payload_length, + crypto_alg_config_t *config, + crypto_run_result_t *result) +{ + unsigned int throughput; + + throughput = (1000000.0 / result->elapsed) * payload_length / 1024; + printf(REPORT_LINE, + config->name, cargs->iteration_count, payload_length, + result->elapsed, result->rusage_self, result->rusage_thread, + throughput); +} + +/** + * Print piece of memory with given size. + */ +static void +print_mem(const char *msg, + const unsigned char *ptr, + unsigned int len) +{ + unsigned i, j; + char c; + char line[81]; + char *p; + + if (msg) + printf("\n%s (bytes size = %d)", msg, len); + + for (i = 0; i < len; i += 16) { + p = line; + sprintf(p, "\n%04x ", i); p += 8; + + for (j = 0; j < 16; j++) { + if (i + j == len) + break; + + sprintf(p, " %02x", (ptr)[i + j]); p += 3; + } + + for (; j < 16; j++) { + sprintf(p, " "); p += 3; + } + + sprintf(p, " "); p += 3; + + for (j = 0; j < 16; j++) { + if (i + j == len) + break; + c = (ptr)[i + j]; + *p++ = (' ' <= c && c <= '~') ? c : '.'; + } + + *p = '\0'; + printf("%s", line); + } + printf("\n"); +} + +/** + * Create ODP crypto session for given config. + */ +static int +create_session_from_config(odp_crypto_session_t *session, + crypto_alg_config_t *config, + crypto_args_t *cargs) +{ + odp_crypto_session_params_t params; + odp_crypto_ses_create_err_t ses_create_rc; + odp_pool_t pkt_pool; + odp_queue_t out_queue; + + memcpy(¶ms, &config->session, sizeof(odp_crypto_session_params_t)); + params.op = ODP_CRYPTO_OP_ENCODE; + params.pref_mode = ODP_CRYPTO_SYNC; + + /* Lookup the packet pool */ + pkt_pool = odp_pool_lookup("packet_pool"); + if (pkt_pool == ODP_POOL_INVALID) { + app_err("packet_pool pool not found\n"); + return -1; + } + params.output_pool = pkt_pool; + + if (cargs->schedule || cargs->poll) { + out_queue = odp_queue_lookup("crypto-out"); + if (out_queue == ODP_QUEUE_INVALID) { + app_err("crypto-out queue not found\n"); + return -1; + } + params.compl_queue = out_queue; + + } else { + params.compl_queue = ODP_QUEUE_INVALID; + } + if (odp_crypto_session_create(¶ms, session, + &ses_create_rc)) { + app_err("crypto session create failed.\n"); + return -1; + } + + return 0; +} + +/** + * Run measurement iterations for given config and payload size. + * Result of run returned in 'result' out parameter. + */ +static int +run_measure_one(crypto_args_t *cargs, + crypto_alg_config_t *config, + odp_crypto_session_t *session, + unsigned int payload_length, + crypto_run_result_t *result) +{ + odp_crypto_op_params_t params; + + odp_pool_t pkt_pool; + odp_queue_t out_queue; + odp_packet_t pkt; + int rc = 0; + + odp_bool_t posted = 0; + + pkt_pool = odp_pool_lookup("packet_pool"); + if (pkt_pool == ODP_POOL_INVALID) { + app_err("pkt_pool not found\n"); + return -1; + } + + out_queue = odp_queue_lookup("crypto-out"); + if (cargs->schedule || cargs->poll) { + if (out_queue == ODP_QUEUE_INVALID) { + app_err("crypto-out queue not found\n"); + return -1; + } + } + + pkt = odp_packet_alloc(pkt_pool, payload_length); + if (pkt == ODP_PACKET_INVALID) { + app_err("failed to allocate buffer\n"); + return -1; + } + + void *mem = odp_packet_data(pkt); + + memset(mem, 1, payload_length); + + time_record_t start, end; + int packets_sent = 0; + int packets_received = 0; + + /* Initialize parameters block */ + memset(¶ms, 0, sizeof(params)); + params.session = *session; + + params.cipher_range.offset = 0; + params.cipher_range.length = payload_length; + + params.auth_range.offset = 0; + params.auth_range.length = payload_length; + params.hash_result_offset = payload_length; + + if (cargs->reuse_packet) { + params.pkt = pkt; + params.out_pkt = cargs->in_place ? pkt : + ODP_PACKET_INVALID; + } + + fill_time_record(&start); + + while ((packets_sent < cargs->iteration_count) || + (packets_received < cargs->iteration_count)) { + void *mem; + odp_crypto_op_result_t result; + + if ((packets_sent < cargs->iteration_count) && + (packets_sent - packets_received < + cargs->in_flight)) { + if (!cargs->reuse_packet) { + /* + * For in place test we use just one + * statically allocated buffer. + * For now in place test we have to + * allocate and initialize packet + * every time. + * Note we leaked one packet here. + */ + odp_packet_t newpkt; + + newpkt = odp_packet_alloc(pkt_pool, + payload_length); + if (newpkt == ODP_PACKET_INVALID) { + app_err("failed to allocate buffer\n"); + return -1; + } + mem = odp_packet_data(newpkt); + memset(mem, 1, payload_length); + params.pkt = newpkt; + params.out_pkt = cargs->in_place ? newpkt : + ODP_PACKET_INVALID; + } + + if (cargs->debug_packets) { + mem = odp_packet_data(params.pkt); + print_mem("Packet before encryption:", + mem, payload_length); + } + + rc = odp_crypto_operation(¶ms, &posted, + &result); + if (rc) + app_err("failed odp_crypto_operation: rc = %d\n", + rc); + else + packets_sent++; + } + + if (!posted) { + packets_received++; + if (cargs->debug_packets) { + mem = odp_packet_data(params.out_pkt); + print_mem("Immediately encrypted packet", mem, + payload_length + + config->hash_adjust); + } + if (!cargs->in_place) { + if (cargs->reuse_packet) { + params.pkt = params.out_pkt; + params.out_pkt = ODP_PACKET_INVALID; + } else { + odp_packet_free(params.out_pkt); + } + } + } else { + odp_event_t ev; + odp_crypto_compl_t compl; + odp_crypto_op_result_t result; + odp_packet_t out_pkt; + + if (cargs->schedule) + ev = odp_schedule(NULL, + ODP_SCHED_NO_WAIT); + else + ev = odp_queue_deq(out_queue); + + while (ev != ODP_EVENT_INVALID) { + compl = odp_crypto_compl_from_event(ev); + odp_crypto_compl_result(compl, &result); + odp_crypto_compl_free(compl); + out_pkt = result.pkt; + + if (cargs->debug_packets) { + mem = odp_packet_data(out_pkt); + print_mem("Receieved encrypted packet", + mem, + payload_length + + config->hash_adjust); + } + if (cargs->reuse_packet) { + params.pkt = out_pkt; + params.out_pkt = ODP_PACKET_INVALID; + } else { + odp_packet_free(out_pkt); + } + packets_received++; + if (cargs->schedule) + ev = odp_schedule(NULL, + ODP_SCHED_NO_WAIT); + else + ev = odp_queue_deq(out_queue); + }; + } + } + + fill_time_record(&end); + + { + double count; + + count = get_elapsed_usec(&start, &end); + result->elapsed = count / + cargs->iteration_count; + + count = get_rusage_self_diff(&start, &end); + result->rusage_self = count / + cargs->iteration_count; + + count = get_rusage_thread_diff(&start, &end); + result->rusage_thread = count / + cargs->iteration_count; + } + + odp_packet_free(pkt); + + return rc; +} + +/** + * Process one algorithm. Note if paload size is specicified it is + * only one run. Or iterate over set of predefined payloads. + */ +static int +run_measure_one_config(crypto_args_t *cargs, + crypto_alg_config_t *config) +{ + crypto_run_result_t result; + odp_crypto_session_t session; + int rc = 0; + + if (create_session_from_config(&session, config, cargs)) + rc = -1; + + if (!rc) { + if (cargs->payload_length) { + rc = run_measure_one(cargs, config, &session, + cargs->payload_length, &result); + if (!rc) { + print_result_header(); + print_result(cargs, cargs->payload_length, + config, &result); + } + } else { + unsigned int i; + + print_result_header(); + for (i = 0; + i < (sizeof(payloads) / sizeof(unsigned int)); + i++) { + rc = run_measure_one(cargs, config, &session, + payloads[i], &result); + if (rc) + break; + print_result(cargs, payloads[i], + config, &result); + } + } + } + + if (session != ODP_CRYPTO_SESSION_INVALID) + odp_crypto_session_destroy(session); + return rc; +} + +typedef struct thr_arg { + crypto_args_t crypto_args; + crypto_alg_config_t *crypto_alg_config; +} thr_arg_t; + +static int run_thr_func(void *arg) +{ + thr_arg_t *thr_args = (thr_arg_t *)arg; + + run_measure_one_config(&thr_args->crypto_args, + thr_args->crypto_alg_config); + return 0; +} + +int main(int argc, char *argv[]) +{ + crypto_args_t cargs; + odp_pool_t pool; + odp_queue_param_t qparam; + odp_pool_param_t params; + odp_queue_t out_queue = ODP_QUEUE_INVALID; + thr_arg_t thr_arg; + odp_cpumask_t cpumask; + char cpumaskstr[ODP_CPUMASK_STR_SIZE]; + int num_workers = 1; + odph_odpthread_t thr[num_workers]; + odp_instance_t instance; + + memset(&cargs, 0, sizeof(cargs)); + + /* Parse and store the application arguments */ + parse_args(argc, argv, &cargs); + + /* Init ODP before calling anything else */ + if (odp_init_global(&instance, NULL, NULL)) { + app_err("ODP global init failed.\n"); + exit(EXIT_FAILURE); + } + + /* Init this thread */ + odp_init_local(instance, ODP_THREAD_WORKER); + + /* Create packet pool */ + odp_pool_param_init(¶ms); + params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE; + params.pkt.len = SHM_PKT_POOL_BUF_SIZE; + params.pkt.num = SHM_PKT_POOL_SIZE / SHM_PKT_POOL_BUF_SIZE; + params.type = ODP_POOL_PACKET; + pool = odp_pool_create("packet_pool", ¶ms); + + if (pool == ODP_POOL_INVALID) { + app_err("packet pool create failed.\n"); + exit(EXIT_FAILURE); + } + odp_pool_print(pool); + + odp_queue_param_init(&qparam); + if (cargs.schedule) { + qparam.type = ODP_QUEUE_TYPE_SCHED; + qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT; + qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qparam.sched.group = ODP_SCHED_GROUP_ALL; + out_queue = odp_queue_create("crypto-out", &qparam); + } else if (cargs.poll) { + qparam.type = ODP_QUEUE_TYPE_PLAIN; + out_queue = odp_queue_create("crypto-out", &qparam); + } + if (cargs.schedule || cargs.poll) { + if (out_queue == ODP_QUEUE_INVALID) { + app_err("crypto-out queue create failed.\n"); + exit(EXIT_FAILURE); + } + } + + if (cargs.schedule) { + printf("Run in async scheduled mode\n"); + + thr_arg.crypto_args = cargs; + thr_arg.crypto_alg_config = cargs.alg_config; + num_workers = odp_cpumask_default_worker(&cpumask, + num_workers); + (void)odp_cpumask_to_str(&cpumask, cpumaskstr, + sizeof(cpumaskstr)); + printf("num worker threads: %i\n", + num_workers); + printf("first CPU: %i\n", + odp_cpumask_first(&cpumask)); + printf("cpu mask: %s\n", + cpumaskstr); + } else if (cargs.poll) { + printf("Run in async poll mode\n"); + } else { + printf("Run in sync mode\n"); + } + + memset(thr, 0, sizeof(thr)); + + if (cargs.alg_config) { + odph_odpthread_params_t thr_params; + + memset(&thr_params, 0, sizeof(thr_params)); + thr_params.start = run_thr_func; + thr_params.arg = &thr_arg; + thr_params.thr_type = ODP_THREAD_WORKER; + thr_params.instance = instance; + + if (cargs.schedule) { + odph_odpthreads_create(&thr[0], &cpumask, &thr_params); + odph_odpthreads_join(&thr[0]); + } else { + run_measure_one_config(&cargs, cargs.alg_config); + } + } else { + unsigned int i; + + for (i = 0; + i < (sizeof(algs_config) / sizeof(crypto_alg_config_t)); + i++) { + run_measure_one_config(&cargs, algs_config + i); + } + } + + if (odp_pool_destroy(pool)) { + app_err("Error: pool destroy\n"); + exit(EXIT_FAILURE); + } + + if (odp_term_local()) { + app_err("Error: term local\n"); + exit(EXIT_FAILURE); + } + + if (odp_term_global(instance)) { + app_err("Error: term global\n"); + exit(EXIT_FAILURE); + } + + return 0; +} + +static void parse_args(int argc, char *argv[], crypto_args_t *cargs) +{ + int opt; + int long_index; + static const struct option longopts[] = { + {"algorithm", optional_argument, NULL, 'a'}, + {"debug", no_argument, NULL, 'd'}, + {"flight", optional_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"iterations", optional_argument, NULL, 'i'}, + {"inplace", no_argument, NULL, 'n'}, + {"payload", optional_argument, NULL, 'l'}, + {"sessions", optional_argument, NULL, 'm'}, + {"reuse", no_argument, NULL, 'r'}, + {"poll", no_argument, NULL, 'p'}, + {"schedule", no_argument, NULL, 's'}, + {NULL, 0, NULL, 0} + }; + + static const char *shortopts = "+a:c:df:hi:m:nl:spr"; + + /* let helper collect its own arguments (e.g. --odph_proc) */ + odph_parse_options(argc, argv, shortopts, longopts); + + cargs->in_place = 0; + cargs->in_flight = 1; + cargs->debug_packets = 0; + cargs->iteration_count = 10000; + cargs->payload_length = 0; + cargs->alg_config = NULL; + cargs->reuse_packet = 0; + cargs->schedule = 0; + + opterr = 0; /* do not issue errors on helper options */ + + while (1) { + opt = getopt_long(argc, argv, shortopts, longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'a': + cargs->alg_config = find_config_by_name(optarg); + if (!cargs->alg_config) { + printf("cannot test crypto '%s' configuration\n", + optarg); + usage(argv[0]); + exit(-1); + } + break; + case 'd': + cargs->debug_packets = 1; + break; + case 'i': + cargs->iteration_count = atoi(optarg); + break; + case 'f': + cargs->in_flight = atoi(optarg); + break; + case 'h': + usage(argv[0]); + exit(EXIT_SUCCESS); + break; + case 'm': + cargs->max_sessions = atoi(optarg); + break; + case 'n': + cargs->in_place = 1; + break; + case 'l': + cargs->payload_length = atoi(optarg); + break; + case 'r': + cargs->reuse_packet = 1; + break; + case 's': + cargs->schedule = 1; + break; + case 'p': + cargs->poll = 1; + break; + default: + break; + } + } + + optind = 1; /* reset 'extern optind' from the getopt lib */ + + if ((cargs->in_flight > 1) && cargs->reuse_packet) { + printf("-f (in flight > 1) and -r (reuse packet) options are not compatible\n"); + usage(argv[0]); + exit(-1); + } + if (cargs->schedule && cargs->poll) { + printf("-s (schedule) and -p (poll) options are not compatible\n"); + usage(argv[0]); + exit(-1); + } +} + +/** + * Prinf usage information + */ +static void usage(char *progname) +{ + printf("\n" + "Usage: %s OPTIONS\n" + " E.g. %s -i 100000\n" + "\n" + "OpenDataPlane crypto speed measure.\n" + "Optional OPTIONS\n" + " -a, --algorithm <name> Specify algorithm name (default all)\n" + " Supported values are:\n", + progname, progname); + + print_config_names(" "); + printf(" -d, --debug Enable dump of processed packets.\n" + " -f, --flight <number> Max number of packet processed in parallel (default 1)\n" + " -i, --iterations <number> Number of iterations.\n" + " -n, --inplace Encrypt on place.\n" + " -l, --payload Payload length.\n" + " -r, --reuse Output encrypted packet is passed as input\n" + " to next encrypt iteration.\n" + " -s, --schedule Use scheduler for completion events.\n" + " -p, --poll Poll completion queue for completion events.\n" + " -h, --help Display help and exit.\n" + "\n"); +} diff --git a/test/common_plat/performance/odp_l2fwd.c b/test/common_plat/performance/odp_l2fwd.c new file mode 100644 index 000000000..418382dc1 --- /dev/null +++ b/test/common_plat/performance/odp_l2fwd.c @@ -0,0 +1,1501 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * @example odp_l2fwd.c ODP basic forwarding application + */ + +/** enable strtok */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdlib.h> +#include <getopt.h> +#include <unistd.h> +#include <errno.h> +#include <inttypes.h> + +#include <test_debug.h> + +#include <odp_api.h> +#include <odp/helper/linux.h> +#include <odp/helper/eth.h> +#include <odp/helper/ip.h> + +/** @def MAX_WORKERS + * @brief Maximum number of worker threads + */ +#define MAX_WORKERS 32 + +/** @def SHM_PKT_POOL_SIZE + * @brief Size of the shared memory block + */ +#define SHM_PKT_POOL_SIZE 8192 + +/** @def SHM_PKT_POOL_BUF_SIZE + * @brief Buffer size of the packet pool buffer + */ +#define SHM_PKT_POOL_BUF_SIZE 1856 + +/** @def MAX_PKT_BURST + * @brief Maximum number of packet in a burst + */ +#define MAX_PKT_BURST 32 + +/** Maximum number of pktio queues per interface */ +#define MAX_QUEUES 32 + +/** Maximum number of pktio interfaces */ +#define MAX_PKTIOS 8 + +/** + * Packet input mode + */ +typedef enum pktin_mode_t { + DIRECT_RECV, + PLAIN_QUEUE, + SCHED_PARALLEL, + SCHED_ATOMIC, + SCHED_ORDERED, +} pktin_mode_t; + +/** + * Packet output modes + */ +typedef enum pktout_mode_t { + PKTOUT_DIRECT, + PKTOUT_QUEUE +} pktout_mode_t; + +static inline int sched_mode(pktin_mode_t in_mode) +{ + return (in_mode == SCHED_PARALLEL) || + (in_mode == SCHED_ATOMIC) || + (in_mode == SCHED_ORDERED); +} + +/** Get rid of path in filename - only for unix-type paths using '/' */ +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \ + strrchr((file_name), '/') + 1 : (file_name)) +/** + * Parsed command line application arguments + */ +typedef struct { + int cpu_count; + int if_count; /**< Number of interfaces to be used */ + int addr_count; /**< Number of dst addresses to be used */ + int num_workers; /**< Number of worker threads */ + char **if_names; /**< Array of pointers to interface names */ + odph_ethaddr_t addrs[MAX_PKTIOS]; /**< Array of dst addresses */ + pktin_mode_t in_mode; /**< Packet input mode */ + pktout_mode_t out_mode; /**< Packet output mode */ + int time; /**< Time in seconds to run. */ + int accuracy; /**< Number of seconds to get and print statistics */ + char *if_str; /**< Storage for interface names */ + int dst_change; /**< Change destination eth addresses */ + int src_change; /**< Change source eth addresses */ + int error_check; /**< Check packet errors */ +} appl_args_t; + +static int exit_threads; /**< Break workers loop if set to 1 */ + +/** + * Statistics + */ +typedef union { + struct { + /** Number of forwarded packets */ + uint64_t packets; + /** Packets dropped due to receive error */ + uint64_t rx_drops; + /** Packets dropped due to transmit error */ + uint64_t tx_drops; + } s; + + uint8_t padding[ODP_CACHE_LINE_SIZE]; +} stats_t ODP_ALIGNED_CACHE; + +/** + * Thread specific arguments + */ +typedef struct thread_args_t { + int thr_idx; + int num_pktio; + + struct { + odp_pktio_t rx_pktio; + odp_pktio_t tx_pktio; + odp_pktin_queue_t pktin; + odp_pktout_queue_t pktout; + odp_queue_t rx_queue; + odp_queue_t tx_queue; + int rx_idx; + int tx_idx; + int rx_queue_idx; + int tx_queue_idx; + } pktio[MAX_PKTIOS]; + + stats_t *stats; /**< Pointer to per thread stats */ +} thread_args_t; + +/** + * Grouping of all global data + */ +typedef struct { + /** Per thread packet stats */ + stats_t stats[MAX_WORKERS]; + /** Application (parsed) arguments */ + appl_args_t appl; + /** Thread specific arguments */ + thread_args_t thread[MAX_WORKERS]; + /** Table of port ethernet addresses */ + odph_ethaddr_t port_eth_addr[MAX_PKTIOS]; + /** Table of dst ethernet addresses */ + odph_ethaddr_t dst_eth_addr[MAX_PKTIOS]; + /** Table of dst ports */ + int dst_port[MAX_PKTIOS]; + /** Table of pktio handles */ + struct { + odp_pktio_t pktio; + odp_pktin_queue_t pktin[MAX_QUEUES]; + odp_pktout_queue_t pktout[MAX_QUEUES]; + odp_queue_t rx_q[MAX_QUEUES]; + odp_queue_t tx_q[MAX_QUEUES]; + int num_rx_thr; + int num_tx_thr; + int num_rx_queue; + int num_tx_queue; + int next_rx_queue; + int next_tx_queue; + } pktios[MAX_PKTIOS]; +} args_t; + +/** Global pointer to args */ +static args_t *gbl_args; +/** Global barrier to synchronize main and workers */ +static odp_barrier_t barrier; + +/** + * Lookup the destination port for a given packet + * + * @param pkt ODP packet handle + */ +static inline int lookup_dest_port(odp_packet_t pkt) +{ + int i, src_idx; + odp_pktio_t pktio_src; + + pktio_src = odp_packet_input(pkt); + + for (src_idx = -1, i = 0; gbl_args->pktios[i].pktio + != ODP_PKTIO_INVALID; ++i) + if (gbl_args->pktios[i].pktio == pktio_src) + src_idx = i; + + if (src_idx == -1) + LOG_ABORT("Failed to determine pktio input\n"); + + return gbl_args->dst_port[src_idx]; +} + +/** + * Drop packets which input parsing marked as containing errors. + * + * Frees packets with error and modifies pkt_tbl[] to only contain packets with + * no detected errors. + * + * @param pkt_tbl Array of packets + * @param num Number of packets in pkt_tbl[] + * + * @return Number of packets dropped + */ +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num) +{ + odp_packet_t pkt; + unsigned dropped = 0; + unsigned i, j; + + for (i = 0, j = 0; i < num; ++i) { + pkt = pkt_tbl[i]; + + if (odp_unlikely(odp_packet_has_error(pkt))) { + odp_packet_free(pkt); /* Drop */ + dropped++; + } else if (odp_unlikely(i != j++)) { + pkt_tbl[j - 1] = pkt; + } + } + + return dropped; +} + +/** + * Fill packets' eth addresses according to the destination port + * + * @param pkt_tbl Array of packets + * @param num Number of packets in the array + * @param dst_port Destination port + */ +static inline void fill_eth_addrs(odp_packet_t pkt_tbl[], + unsigned num, int dst_port) +{ + odp_packet_t pkt; + odph_ethhdr_t *eth; + unsigned i; + + if (!gbl_args->appl.dst_change && !gbl_args->appl.src_change) + return; + + for (i = 0; i < num; ++i) { + pkt = pkt_tbl[i]; + + odp_packet_prefetch(pkt, 0, ODPH_ETHHDR_LEN); + + if (odp_packet_has_eth(pkt)) { + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + + if (gbl_args->appl.src_change) + eth->src = gbl_args->port_eth_addr[dst_port]; + + if (gbl_args->appl.dst_change) + eth->dst = gbl_args->dst_eth_addr[dst_port]; + } + } +} + +static inline int event_queue_send(odp_queue_t queue, odp_packet_t *pkt_tbl, + unsigned pkts) +{ + int ret; + unsigned i; + unsigned sent = 0; + odp_event_t ev_tbl[pkts]; + + for (i = 0; i < pkts; i++) + ev_tbl[i] = odp_packet_to_event(pkt_tbl[i]); + + while (sent < pkts) { + ret = odp_queue_enq_multi(queue, &ev_tbl[sent], pkts - sent); + + if (ret < 0) { + LOG_ERR("Failed to send packet as events\n"); + break; + } + + sent += ret; + } + + return sent; +} + +/** + * Packet IO worker thread using scheduled queues + * + * @param arg thread arguments of type 'thread_args_t *' + */ +static int run_worker_sched_mode(void *arg) +{ + odp_event_t ev_tbl[MAX_PKT_BURST]; + odp_packet_t pkt_tbl[MAX_PKT_BURST]; + int pkts; + int thr; + uint64_t wait; + int dst_idx; + int thr_idx; + int i; + odp_pktout_queue_t pktout[MAX_PKTIOS]; + odp_queue_t tx_queue[MAX_PKTIOS]; + thread_args_t *thr_args = arg; + stats_t *stats = thr_args->stats; + int use_event_queue = gbl_args->appl.out_mode; + pktin_mode_t in_mode = gbl_args->appl.in_mode; + + thr = odp_thread_id(); + thr_idx = thr_args->thr_idx; + + memset(pktout, 0, sizeof(pktout)); + + for (i = 0; i < MAX_PKTIOS; i++) + tx_queue[i] = ODP_QUEUE_INVALID; + + for (i = 0; i < gbl_args->appl.if_count; i++) { + if (gbl_args->pktios[i].num_tx_queue == + gbl_args->appl.num_workers) { + pktout[i] = gbl_args->pktios[i].pktout[thr_idx]; + tx_queue[i] = gbl_args->pktios[i].tx_q[thr_idx]; + } else if (gbl_args->pktios[i].num_tx_queue == 1) { + pktout[i] = gbl_args->pktios[i].pktout[0]; + tx_queue[i] = gbl_args->pktios[i].tx_q[0]; + } else { + LOG_ABORT("Bad number of output queues %i\n", i); + } + } + + printf("[%02i] PKTIN_SCHED_%s, %s\n", thr, + (in_mode == SCHED_PARALLEL) ? "PARALLEL" : + ((in_mode == SCHED_ATOMIC) ? "ATOMIC" : "ORDERED"), + (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT"); + + odp_barrier_wait(&barrier); + + wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS * 100); + + /* Loop packets */ + while (!exit_threads) { + int sent; + unsigned tx_drops; + + pkts = odp_schedule_multi(NULL, wait, ev_tbl, MAX_PKT_BURST); + + if (pkts <= 0) + continue; + + for (i = 0; i < pkts; i++) + pkt_tbl[i] = odp_packet_from_event(ev_tbl[i]); + + if (gbl_args->appl.error_check) { + int rx_drops; + + /* Drop packets with errors */ + rx_drops = drop_err_pkts(pkt_tbl, pkts); + + if (odp_unlikely(rx_drops)) { + stats->s.rx_drops += rx_drops; + if (pkts == rx_drops) + continue; + + pkts -= rx_drops; + } + } + + /* packets from the same queue are from the same interface */ + dst_idx = lookup_dest_port(pkt_tbl[0]); + fill_eth_addrs(pkt_tbl, pkts, dst_idx); + + if (odp_unlikely(use_event_queue)) + sent = event_queue_send(tx_queue[dst_idx], pkt_tbl, + pkts); + else + sent = odp_pktout_send(pktout[dst_idx], pkt_tbl, pkts); + + sent = odp_unlikely(sent < 0) ? 0 : sent; + tx_drops = pkts - sent; + + if (odp_unlikely(tx_drops)) { + stats->s.tx_drops += tx_drops; + + /* Drop rejected packets */ + for (i = sent; i < pkts; i++) + odp_packet_free(pkt_tbl[i]); + } + + stats->s.packets += pkts; + } + + /* Make sure that latest stat writes are visible to other threads */ + odp_mb_full(); + + return 0; +} + +/** + * Packet IO worker thread using plain queues + * + * @param arg thread arguments of type 'thread_args_t *' + */ +static int run_worker_plain_queue_mode(void *arg) +{ + int thr; + int pkts; + odp_packet_t pkt_tbl[MAX_PKT_BURST]; + int dst_idx, num_pktio; + odp_queue_t queue; + odp_pktout_queue_t pktout; + odp_queue_t tx_queue; + int pktio = 0; + thread_args_t *thr_args = arg; + stats_t *stats = thr_args->stats; + int use_event_queue = gbl_args->appl.out_mode; + + thr = odp_thread_id(); + + num_pktio = thr_args->num_pktio; + dst_idx = thr_args->pktio[pktio].tx_idx; + queue = thr_args->pktio[pktio].rx_queue; + pktout = thr_args->pktio[pktio].pktout; + tx_queue = thr_args->pktio[pktio].tx_queue; + + printf("[%02i] num pktios %i, PKTIN_QUEUE, %s\n", thr, num_pktio, + (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT"); + + odp_barrier_wait(&barrier); + + /* Loop packets */ + while (!exit_threads) { + int sent; + unsigned tx_drops; + odp_event_t event[MAX_PKT_BURST]; + int i; + + if (num_pktio > 1) { + dst_idx = thr_args->pktio[pktio].tx_idx; + queue = thr_args->pktio[pktio].rx_queue; + pktout = thr_args->pktio[pktio].pktout; + pktio++; + if (pktio == num_pktio) + pktio = 0; + } + + pkts = odp_queue_deq_multi(queue, event, MAX_PKT_BURST); + if (odp_unlikely(pkts <= 0)) + continue; + + for (i = 0; i < pkts; i++) + pkt_tbl[i] = odp_packet_from_event(event[i]); + + if (gbl_args->appl.error_check) { + int rx_drops; + + /* Drop packets with errors */ + rx_drops = drop_err_pkts(pkt_tbl, pkts); + + if (odp_unlikely(rx_drops)) { + stats->s.rx_drops += rx_drops; + if (pkts == rx_drops) + continue; + + pkts -= rx_drops; + } + } + + fill_eth_addrs(pkt_tbl, pkts, dst_idx); + + if (odp_unlikely(use_event_queue)) + sent = event_queue_send(tx_queue, pkt_tbl, pkts); + else + sent = odp_pktout_send(pktout, pkt_tbl, pkts); + + sent = odp_unlikely(sent < 0) ? 0 : sent; + tx_drops = pkts - sent; + + if (odp_unlikely(tx_drops)) { + int i; + + stats->s.tx_drops += tx_drops; + + /* Drop rejected packets */ + for (i = sent; i < pkts; i++) + odp_packet_free(pkt_tbl[i]); + } + + stats->s.packets += pkts; + } + + /* Make sure that latest stat writes are visible to other threads */ + odp_mb_full(); + + return 0; +} + +/** + * Packet IO worker thread accessing IO resources directly + * + * @param arg thread arguments of type 'thread_args_t *' + */ +static int run_worker_direct_mode(void *arg) +{ + int thr; + int pkts; + odp_packet_t pkt_tbl[MAX_PKT_BURST]; + int dst_idx, num_pktio; + odp_pktin_queue_t pktin; + odp_pktout_queue_t pktout; + odp_queue_t tx_queue; + int pktio = 0; + thread_args_t *thr_args = arg; + stats_t *stats = thr_args->stats; + int use_event_queue = gbl_args->appl.out_mode; + + thr = odp_thread_id(); + + num_pktio = thr_args->num_pktio; + dst_idx = thr_args->pktio[pktio].tx_idx; + pktin = thr_args->pktio[pktio].pktin; + pktout = thr_args->pktio[pktio].pktout; + tx_queue = thr_args->pktio[pktio].tx_queue; + + printf("[%02i] num pktios %i, PKTIN_DIRECT, %s\n", thr, num_pktio, + (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT"); + + odp_barrier_wait(&barrier); + + /* Loop packets */ + while (!exit_threads) { + int sent; + unsigned tx_drops; + + if (num_pktio > 1) { + dst_idx = thr_args->pktio[pktio].tx_idx; + pktin = thr_args->pktio[pktio].pktin; + pktout = thr_args->pktio[pktio].pktout; + pktio++; + if (pktio == num_pktio) + pktio = 0; + } + + pkts = odp_pktin_recv(pktin, pkt_tbl, MAX_PKT_BURST); + if (odp_unlikely(pkts <= 0)) + continue; + + if (gbl_args->appl.error_check) { + int rx_drops; + + /* Drop packets with errors */ + rx_drops = drop_err_pkts(pkt_tbl, pkts); + + if (odp_unlikely(rx_drops)) { + stats->s.rx_drops += rx_drops; + if (pkts == rx_drops) + continue; + + pkts -= rx_drops; + } + } + + fill_eth_addrs(pkt_tbl, pkts, dst_idx); + + if (odp_unlikely(use_event_queue)) + sent = event_queue_send(tx_queue, pkt_tbl, pkts); + else + sent = odp_pktout_send(pktout, pkt_tbl, pkts); + + sent = odp_unlikely(sent < 0) ? 0 : sent; + tx_drops = pkts - sent; + + if (odp_unlikely(tx_drops)) { + int i; + + stats->s.tx_drops += tx_drops; + + /* Drop rejected packets */ + for (i = sent; i < pkts; i++) + odp_packet_free(pkt_tbl[i]); + } + + stats->s.packets += pkts; + } + + /* Make sure that latest stat writes are visible to other threads */ + odp_mb_full(); + + return 0; +} + +/** + * Create a pktio handle, optionally associating a default input queue. + * + * @param dev Name of device to open + * @param index Pktio index + * @param pool Pool to associate with device for packet RX/TX + * + * @retval 0 on success + * @retval -1 on failure + */ +static int create_pktio(const char *dev, int idx, int num_rx, int num_tx, + odp_pool_t pool) +{ + odp_pktio_t pktio; + odp_pktio_param_t pktio_param; + odp_schedule_sync_t sync_mode; + odp_pktio_capability_t capa; + odp_pktin_queue_param_t pktin_param; + odp_pktout_queue_param_t pktout_param; + odp_pktio_op_mode_t mode_rx; + odp_pktio_op_mode_t mode_tx; + pktin_mode_t in_mode = gbl_args->appl.in_mode; + int num_tx_shared; + + odp_pktio_param_init(&pktio_param); + + if (gbl_args->appl.in_mode == PLAIN_QUEUE) + pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE; + else if (gbl_args->appl.in_mode != DIRECT_RECV) /* pktin_mode SCHED_* */ + pktio_param.in_mode = ODP_PKTIN_MODE_SCHED; + + if (gbl_args->appl.out_mode != PKTOUT_DIRECT) + pktio_param.out_mode = ODP_PKTOUT_MODE_QUEUE; + + pktio = odp_pktio_open(dev, pool, &pktio_param); + if (pktio == ODP_PKTIO_INVALID) { + LOG_ERR("Error: failed to open %s\n", dev); + return -1; + } + + printf("created pktio %" PRIu64 " (%s)\n", + odp_pktio_to_u64(pktio), dev); + + if (odp_pktio_capability(pktio, &capa)) { + LOG_ERR("Error: capability query failed %s\n", dev); + return -1; + } + + odp_pktin_queue_param_init(&pktin_param); + odp_pktout_queue_param_init(&pktout_param); + + if (sched_mode(in_mode)) { + num_tx_shared = 1; + mode_tx = ODP_PKTIO_OP_MT; + mode_rx = ODP_PKTIO_OP_MT; + + if (gbl_args->appl.in_mode == SCHED_ATOMIC) + sync_mode = ODP_SCHED_SYNC_ATOMIC; + else if (gbl_args->appl.in_mode == SCHED_ORDERED) + sync_mode = ODP_SCHED_SYNC_ORDERED; + else + sync_mode = ODP_SCHED_SYNC_PARALLEL; + + pktin_param.queue_param.sched.prio = ODP_SCHED_PRIO_DEFAULT; + pktin_param.queue_param.sched.sync = sync_mode; + pktin_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL; + } else { + num_tx_shared = capa.max_output_queues; + mode_tx = ODP_PKTIO_OP_MT_UNSAFE; + mode_rx = ODP_PKTIO_OP_MT_UNSAFE; + } + + if (num_rx > (int)capa.max_input_queues) { + printf("Sharing %i input queues between %i workers\n", + capa.max_input_queues, num_rx); + num_rx = capa.max_input_queues; + mode_rx = ODP_PKTIO_OP_MT; + } + + if (num_tx > (int)capa.max_output_queues) { + printf("Sharing %i output queues between %i workers\n", + num_tx_shared, num_tx); + num_tx = num_tx_shared; + mode_tx = ODP_PKTIO_OP_MT; + } + + pktin_param.hash_enable = 1; + pktin_param.hash_proto.proto.ipv4_udp = 1; + pktin_param.num_queues = num_rx; + pktin_param.op_mode = mode_rx; + + pktout_param.op_mode = mode_tx; + pktout_param.num_queues = num_tx; + + if (odp_pktin_queue_config(pktio, &pktin_param)) { + LOG_ERR("Error: input queue config failed %s\n", dev); + return -1; + } + + if (odp_pktout_queue_config(pktio, &pktout_param)) { + LOG_ERR("Error: output queue config failed %s\n", dev); + return -1; + } + + if (gbl_args->appl.in_mode == DIRECT_RECV) { + if (odp_pktin_queue(pktio, gbl_args->pktios[idx].pktin, + num_rx) != num_rx) { + LOG_ERR("Error: pktin queue query failed %s\n", + dev); + return -1; + } + } else { + if (odp_pktin_event_queue(pktio, + gbl_args->pktios[idx].rx_q, + num_rx) != num_rx) { + LOG_ERR("Error: pktin event queue query failed %s\n", + dev); + return -1; + } + } + + if (gbl_args->appl.out_mode == PKTOUT_DIRECT) { + if (odp_pktout_queue(pktio, + gbl_args->pktios[idx].pktout, + num_tx) != num_tx) { + LOG_ERR("Error: pktout queue query failed %s\n", dev); + return -1; + } + } else { + if (odp_pktout_event_queue(pktio, + gbl_args->pktios[idx].tx_q, + num_tx) != num_tx) { + LOG_ERR("Error: event queue query failed %s\n", dev); + return -1; + } + } + + printf("created %i input and %i output queues on (%s)\n", + num_rx, num_tx, dev); + + gbl_args->pktios[idx].num_rx_queue = num_rx; + gbl_args->pktios[idx].num_tx_queue = num_tx; + gbl_args->pktios[idx].pktio = pktio; + + return 0; +} + +/** + * Print statistics + * + * @param num_workers Number of worker threads + * @param thr_stats Pointer to stats storage + * @param duration Number of seconds to loop in + * @param timeout Number of seconds for stats calculation + * + */ +static int print_speed_stats(int num_workers, stats_t *thr_stats, + int duration, int timeout) +{ + uint64_t pkts = 0; + uint64_t pkts_prev = 0; + uint64_t pps; + uint64_t rx_drops, tx_drops; + uint64_t maximum_pps = 0; + int i; + int elapsed = 0; + int stats_enabled = 1; + int loop_forever = (duration == 0); + + if (timeout <= 0) { + stats_enabled = 0; + timeout = 1; + } + /* Wait for all threads to be ready*/ + odp_barrier_wait(&barrier); + + do { + pkts = 0; + rx_drops = 0; + tx_drops = 0; + + sleep(timeout); + + for (i = 0; i < num_workers; i++) { + pkts += thr_stats[i].s.packets; + rx_drops += thr_stats[i].s.rx_drops; + tx_drops += thr_stats[i].s.tx_drops; + } + if (stats_enabled) { + pps = (pkts - pkts_prev) / timeout; + if (pps > maximum_pps) + maximum_pps = pps; + printf("%" PRIu64 " pps, %" PRIu64 " max pps, ", pps, + maximum_pps); + + printf(" %" PRIu64 " rx drops, %" PRIu64 " tx drops\n", + rx_drops, tx_drops); + + pkts_prev = pkts; + } + elapsed += timeout; + } while (loop_forever || (elapsed < duration)); + + if (stats_enabled) + printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n", + maximum_pps); + + return pkts > 100 ? 0 : -1; +} + +static void print_port_mapping(void) +{ + int if_count, num_workers; + int thr, pktio; + + if_count = gbl_args->appl.if_count; + num_workers = gbl_args->appl.num_workers; + + printf("\nWorker mapping table (port[queue])\n--------------------\n"); + + for (thr = 0; thr < num_workers; thr++) { + int rx_idx, tx_idx; + int rx_queue_idx, tx_queue_idx; + thread_args_t *thr_args = &gbl_args->thread[thr]; + int num = thr_args->num_pktio; + + printf("Worker %i\n", thr); + + for (pktio = 0; pktio < num; pktio++) { + rx_idx = thr_args->pktio[pktio].rx_idx; + tx_idx = thr_args->pktio[pktio].tx_idx; + rx_queue_idx = thr_args->pktio[pktio].rx_queue_idx; + tx_queue_idx = thr_args->pktio[pktio].tx_queue_idx; + printf(" %i[%i] -> %i[%i]\n", + rx_idx, rx_queue_idx, tx_idx, tx_queue_idx); + } + } + + printf("\nPort config\n--------------------\n"); + + for (pktio = 0; pktio < if_count; pktio++) { + const char *dev = gbl_args->appl.if_names[pktio]; + + printf("Port %i (%s)\n", pktio, dev); + printf(" rx workers %i\n", + gbl_args->pktios[pktio].num_rx_thr); + printf(" tx workers %i\n", + gbl_args->pktios[pktio].num_tx_thr); + printf(" rx queues %i\n", + gbl_args->pktios[pktio].num_rx_queue); + printf(" tx queues %i\n", + gbl_args->pktios[pktio].num_tx_queue); + } + + printf("\n"); +} + +/** + * Find the destination port for a given input port + * + * @param port Input port index + */ +static int find_dest_port(int port) +{ + /* Even number of ports */ + if (gbl_args->appl.if_count % 2 == 0) + return (port % 2 == 0) ? port + 1 : port - 1; + + /* Odd number of ports */ + if (port == gbl_args->appl.if_count - 1) + return 0; + else + return port + 1; +} + +/* + * Bind worker threads to interfaces and calculate number of queues needed + * + * less workers (N) than interfaces (M) + * - assign each worker to process every Nth interface + * - workers process inequal number of interfaces, when M is not divisible by N + * - needs only single queue per interface + * otherwise + * - assign an interface to every Mth worker + * - interfaces are processed by inequal number of workers, when N is not + * divisible by M + * - tries to configure a queue per worker per interface + * - shares queues, if interface capability does not allows a queue per worker + */ +static void bind_workers(void) +{ + int if_count, num_workers; + int rx_idx, tx_idx, thr, pktio; + thread_args_t *thr_args; + + if_count = gbl_args->appl.if_count; + num_workers = gbl_args->appl.num_workers; + + /* initialize port forwarding table */ + for (rx_idx = 0; rx_idx < if_count; rx_idx++) + gbl_args->dst_port[rx_idx] = find_dest_port(rx_idx); + + if (if_count > num_workers) { + thr = 0; + + for (rx_idx = 0; rx_idx < if_count; rx_idx++) { + thr_args = &gbl_args->thread[thr]; + pktio = thr_args->num_pktio; + tx_idx = gbl_args->dst_port[rx_idx]; + thr_args->pktio[pktio].rx_idx = rx_idx; + thr_args->pktio[pktio].tx_idx = tx_idx; + thr_args->num_pktio++; + + gbl_args->pktios[rx_idx].num_rx_thr++; + gbl_args->pktios[tx_idx].num_tx_thr++; + + thr++; + if (thr >= num_workers) + thr = 0; + } + } else { + rx_idx = 0; + + for (thr = 0; thr < num_workers; thr++) { + thr_args = &gbl_args->thread[thr]; + pktio = thr_args->num_pktio; + tx_idx = gbl_args->dst_port[rx_idx]; + thr_args->pktio[pktio].rx_idx = rx_idx; + thr_args->pktio[pktio].tx_idx = tx_idx; + thr_args->num_pktio++; + + gbl_args->pktios[rx_idx].num_rx_thr++; + gbl_args->pktios[tx_idx].num_tx_thr++; + + rx_idx++; + if (rx_idx >= if_count) + rx_idx = 0; + } + } +} + +/* + * Bind queues to threads and fill in missing thread arguments (handles) + */ +static void bind_queues(void) +{ + int num_workers; + int thr, pktio; + + num_workers = gbl_args->appl.num_workers; + + for (thr = 0; thr < num_workers; thr++) { + int rx_idx, tx_idx; + thread_args_t *thr_args = &gbl_args->thread[thr]; + int num = thr_args->num_pktio; + + for (pktio = 0; pktio < num; pktio++) { + int rx_queue, tx_queue; + + rx_idx = thr_args->pktio[pktio].rx_idx; + tx_idx = thr_args->pktio[pktio].tx_idx; + rx_queue = gbl_args->pktios[rx_idx].next_rx_queue; + tx_queue = gbl_args->pktios[tx_idx].next_tx_queue; + + thr_args->pktio[pktio].rx_queue_idx = rx_queue; + thr_args->pktio[pktio].tx_queue_idx = tx_queue; + thr_args->pktio[pktio].pktin = + gbl_args->pktios[rx_idx].pktin[rx_queue]; + thr_args->pktio[pktio].pktout = + gbl_args->pktios[tx_idx].pktout[tx_queue]; + thr_args->pktio[pktio].rx_queue = + gbl_args->pktios[rx_idx].rx_q[rx_queue]; + thr_args->pktio[pktio].tx_queue = + gbl_args->pktios[tx_idx].tx_q[tx_queue]; + thr_args->pktio[pktio].rx_pktio = + gbl_args->pktios[rx_idx].pktio; + thr_args->pktio[pktio].tx_pktio = + gbl_args->pktios[tx_idx].pktio; + + rx_queue++; + tx_queue++; + + if (rx_queue >= gbl_args->pktios[rx_idx].num_rx_queue) + rx_queue = 0; + if (tx_queue >= gbl_args->pktios[tx_idx].num_tx_queue) + tx_queue = 0; + + gbl_args->pktios[rx_idx].next_rx_queue = rx_queue; + gbl_args->pktios[tx_idx].next_tx_queue = tx_queue; + } + } +} + +/** + * Prinf usage information + */ +static void usage(char *progname) +{ + printf("\n" + "OpenDataPlane L2 forwarding application.\n" + "\n" + "Usage: %s OPTIONS\n" + " E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n" + " In the above example,\n" + " eth0 will send pkts to eth1 and vice versa\n" + " eth2 will send pkts to eth3 and vice versa\n" + "\n" + "Mandatory OPTIONS:\n" + " -i, --interface Eth interfaces (comma-separated, no spaces)\n" + " Interface count min 1, max %i\n" + "\n" + "Optional OPTIONS:\n" + " -m, --mode Packet input mode\n" + " 0: Direct mode: PKTIN_MODE_DIRECT (default)\n" + " 1: Scheduler mode with parallel queues: PKTIN_MODE_SCHED + SCHED_SYNC_PARALLEL\n" + " 2: Scheduler mode with atomic queues: PKTIN_MODE_SCHED + SCHED_SYNC_ATOMIC\n" + " 3: Scheduler mode with ordered queues: PKTIN_MODE_SCHED + SCHED_SYNC_ORDERED\n" + " 4: Plain queue mode: ODP_PKTIN_MODE_QUEUE\n" + " -o, --out_mode Packet output mode\n" + " 0: Direct mode: PKTOUT_MODE_DIRECT (default)\n" + " 1: Queue mode: PKTOUT_MODE_QUEUE\n" + " -c, --count <number> CPU count.\n" + " -t, --time <number> Time in seconds to run.\n" + " -a, --accuracy <number> Time in seconds get print statistics\n" + " (default is 1 second).\n" + " -d, --dst_change 0: Don't change packets' dst eth addresses\n" + " 1: Change packets' dst eth addresses (default)\n" + " -s, --src_change 0: Don't change packets' src eth addresses\n" + " 1: Change packets' src eth addresses (default)\n" + " -r, --dst_addr Destination addresses (comma-separated, no spaces)\n" + " Requires also the -d flag to be set\n" + " -e, --error_check 0: Don't check packet errors (default)\n" + " 1: Check packet errors\n" + " -h, --help Display help and exit.\n\n" + "\n", NO_PATH(progname), NO_PATH(progname), MAX_PKTIOS + ); +} + +/** + * Parse and store the command line arguments + * + * @param argc argument count + * @param argv[] argument vector + * @param appl_args Store application arguments here + */ +static void parse_args(int argc, char *argv[], appl_args_t *appl_args) +{ + int opt; + int long_index; + char *token; + char *addr_str; + size_t len; + int i; + static const struct option longopts[] = { + {"count", required_argument, NULL, 'c'}, + {"time", required_argument, NULL, 't'}, + {"accuracy", required_argument, NULL, 'a'}, + {"interface", required_argument, NULL, 'i'}, + {"mode", required_argument, NULL, 'm'}, + {"out_mode", required_argument, NULL, 'o'}, + {"dst_addr", required_argument, NULL, 'r'}, + {"dst_change", required_argument, NULL, 'd'}, + {"src_change", required_argument, NULL, 's'}, + {"error_check", required_argument, NULL, 'e'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + static const char *shortopts = "+c:+t:+a:i:m:o:r:d:s:e:h"; + + /* let helper collect its own arguments (e.g. --odph_proc) */ + odph_parse_options(argc, argv, shortopts, longopts); + + appl_args->time = 0; /* loop forever if time to run is 0 */ + appl_args->accuracy = 1; /* get and print pps stats second */ + appl_args->dst_change = 1; /* change eth dst address by default */ + appl_args->src_change = 1; /* change eth src address by default */ + appl_args->error_check = 0; /* don't check packet errors by default */ + + opterr = 0; /* do not issue errors on helper options */ + + while (1) { + opt = getopt_long(argc, argv, shortopts, longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'c': + appl_args->cpu_count = atoi(optarg); + break; + case 't': + appl_args->time = atoi(optarg); + break; + case 'a': + appl_args->accuracy = atoi(optarg); + break; + /* parse packet-io interface names */ + case 'r': + len = strlen(optarg); + if (len == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + len += 1; /* add room for '\0' */ + + addr_str = malloc(len); + if (addr_str == NULL) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* store the mac addresses names */ + strcpy(addr_str, optarg); + for (token = strtok(addr_str, ","), i = 0; + token != NULL; token = strtok(NULL, ","), i++) { + if (i >= MAX_PKTIOS) { + printf("too many MAC addresses\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + if (odph_eth_addr_parse(&appl_args->addrs[i], + token) != 0) { + printf("invalid MAC address\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + } + appl_args->addr_count = i; + if (appl_args->addr_count < 1) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + free(addr_str); + break; + case 'i': + len = strlen(optarg); + if (len == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + len += 1; /* add room for '\0' */ + + appl_args->if_str = malloc(len); + if (appl_args->if_str == NULL) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* count the number of tokens separated by ',' */ + strcpy(appl_args->if_str, optarg); + for (token = strtok(appl_args->if_str, ","), i = 0; + token != NULL; + token = strtok(NULL, ","), i++) + ; + + appl_args->if_count = i; + + if (appl_args->if_count < 1 || + appl_args->if_count > MAX_PKTIOS) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* allocate storage for the if names */ + appl_args->if_names = + calloc(appl_args->if_count, sizeof(char *)); + + /* store the if names (reset names string) */ + strcpy(appl_args->if_str, optarg); + for (token = strtok(appl_args->if_str, ","), i = 0; + token != NULL; token = strtok(NULL, ","), i++) { + appl_args->if_names[i] = token; + } + break; + case 'm': + i = atoi(optarg); + if (i == 1) + appl_args->in_mode = SCHED_PARALLEL; + else if (i == 2) + appl_args->in_mode = SCHED_ATOMIC; + else if (i == 3) + appl_args->in_mode = SCHED_ORDERED; + else if (i == 4) + appl_args->in_mode = PLAIN_QUEUE; + else + appl_args->in_mode = DIRECT_RECV; + break; + case 'o': + i = atoi(optarg); + if (i != 0) + appl_args->out_mode = PKTOUT_QUEUE; + break; + case 'd': + appl_args->dst_change = atoi(optarg); + break; + case 's': + appl_args->src_change = atoi(optarg); + break; + case 'e': + appl_args->error_check = atoi(optarg); + break; + case 'h': + usage(argv[0]); + exit(EXIT_SUCCESS); + break; + default: + break; + } + } + + if (appl_args->if_count == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + if (appl_args->addr_count != 0 && + appl_args->addr_count != appl_args->if_count) { + printf("Number of destination addresses differs from number" + " of interfaces\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + optind = 1; /* reset 'extern optind' from the getopt lib */ +} + +/** + * Print system and application info + */ +static void print_info(char *progname, appl_args_t *appl_args) +{ + int i; + + printf("\n" + "ODP system info\n" + "---------------\n" + "ODP API version: %s\n" + "ODP impl name: %s\n" + "CPU model: %s\n" + "CPU freq (hz): %" PRIu64 "\n" + "Cache line size: %i\n" + "CPU count: %i\n" + "\n", + odp_version_api_str(), odp_version_impl_name(), + odp_cpu_model_str(), odp_cpu_hz_max(), + odp_sys_cache_line_size(), odp_cpu_count()); + + printf("Running ODP appl: \"%s\"\n" + "-----------------\n" + "IF-count: %i\n" + "Using IFs: ", + progname, appl_args->if_count); + for (i = 0; i < appl_args->if_count; ++i) + printf(" %s", appl_args->if_names[i]); + printf("\n" + "Mode: "); + if (appl_args->in_mode == DIRECT_RECV) + printf("PKTIN_DIRECT, "); + else if (appl_args->in_mode == PLAIN_QUEUE) + printf("PKTIN_QUEUE, "); + else if (appl_args->in_mode == SCHED_PARALLEL) + printf("PKTIN_SCHED_PARALLEL, "); + else if (appl_args->in_mode == SCHED_ATOMIC) + printf("PKTIN_SCHED_ATOMIC, "); + else if (appl_args->in_mode == SCHED_ORDERED) + printf("PKTIN_SCHED_ORDERED, "); + + if (appl_args->out_mode) + printf("PKTOUT_QUEUE"); + else + printf("PKTOUT_DIRECT"); + + printf("\n\n"); + fflush(NULL); +} + +static void gbl_args_init(args_t *args) +{ + int pktio, queue; + + memset(args, 0, sizeof(args_t)); + + for (pktio = 0; pktio < MAX_PKTIOS; pktio++) { + args->pktios[pktio].pktio = ODP_PKTIO_INVALID; + + for (queue = 0; queue < MAX_QUEUES; queue++) + args->pktios[pktio].rx_q[queue] = ODP_QUEUE_INVALID; + } +} + +/** + * ODP L2 forwarding main function + */ +int main(int argc, char *argv[]) +{ + odph_odpthread_t thread_tbl[MAX_WORKERS]; + odp_pool_t pool; + int i; + int cpu; + int num_workers; + odp_shm_t shm; + odp_cpumask_t cpumask; + char cpumaskstr[ODP_CPUMASK_STR_SIZE]; + odph_ethaddr_t new_addr; + odp_pool_param_t params; + int ret; + stats_t *stats; + int if_count; + int (*thr_run_func)(void *); + odp_instance_t instance; + + /* Init ODP before calling anything else */ + if (odp_init_global(&instance, NULL, NULL)) { + LOG_ERR("Error: ODP global init failed.\n"); + exit(EXIT_FAILURE); + } + + /* Init this thread */ + if (odp_init_local(instance, ODP_THREAD_CONTROL)) { + LOG_ERR("Error: ODP local init failed.\n"); + exit(EXIT_FAILURE); + } + + /* Reserve memory for args from shared mem */ + shm = odp_shm_reserve("shm_args", sizeof(args_t), + ODP_CACHE_LINE_SIZE, 0); + gbl_args = odp_shm_addr(shm); + + if (gbl_args == NULL) { + LOG_ERR("Error: shared mem alloc failed.\n"); + exit(EXIT_FAILURE); + } + gbl_args_init(gbl_args); + + /* Parse and store the application arguments */ + parse_args(argc, argv, &gbl_args->appl); + + /* Print both system and application information */ + print_info(NO_PATH(argv[0]), &gbl_args->appl); + + /* Default to system CPU count unless user specified */ + num_workers = MAX_WORKERS; + if (gbl_args->appl.cpu_count) + num_workers = gbl_args->appl.cpu_count; + + /* Get default worker cpumask */ + num_workers = odp_cpumask_default_worker(&cpumask, num_workers); + (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr)); + + gbl_args->appl.num_workers = num_workers; + + for (i = 0; i < num_workers; i++) + gbl_args->thread[i].thr_idx = i; + + if_count = gbl_args->appl.if_count; + + printf("num worker threads: %i\n", num_workers); + printf("first CPU: %i\n", odp_cpumask_first(&cpumask)); + printf("cpu mask: %s\n", cpumaskstr); + + /* Create packet pool */ + odp_pool_param_init(¶ms); + params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE; + params.pkt.len = SHM_PKT_POOL_BUF_SIZE; + params.pkt.num = SHM_PKT_POOL_SIZE; + params.type = ODP_POOL_PACKET; + + pool = odp_pool_create("packet pool", ¶ms); + + if (pool == ODP_POOL_INVALID) { + LOG_ERR("Error: packet pool create failed.\n"); + exit(EXIT_FAILURE); + } + odp_pool_print(pool); + + bind_workers(); + + for (i = 0; i < if_count; ++i) { + const char *dev = gbl_args->appl.if_names[i]; + int num_rx, num_tx; + + /* A queue per worker in scheduled mode */ + num_rx = num_workers; + num_tx = num_workers; + + if (gbl_args->appl.in_mode == DIRECT_RECV || + gbl_args->appl.in_mode == PLAIN_QUEUE) { + /* A queue per assigned worker */ + num_rx = gbl_args->pktios[i].num_rx_thr; + num_tx = gbl_args->pktios[i].num_tx_thr; + } + + if (create_pktio(dev, i, num_rx, num_tx, pool)) + exit(EXIT_FAILURE); + + /* Save interface ethernet address */ + if (odp_pktio_mac_addr(gbl_args->pktios[i].pktio, + gbl_args->port_eth_addr[i].addr, + ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) { + LOG_ERR("Error: interface ethernet address unknown\n"); + exit(EXIT_FAILURE); + } + + /* Save destination eth address */ + if (gbl_args->appl.dst_change) { + /* 02:00:00:00:00:XX */ + memset(&new_addr, 0, sizeof(odph_ethaddr_t)); + if (gbl_args->appl.addr_count) { + memcpy(&new_addr, &gbl_args->appl.addrs[i], + sizeof(odph_ethaddr_t)); + } else { + new_addr.addr[0] = 0x02; + new_addr.addr[5] = i; + } + gbl_args->dst_eth_addr[i] = new_addr; + } + } + + gbl_args->pktios[i].pktio = ODP_PKTIO_INVALID; + + bind_queues(); + + if (gbl_args->appl.in_mode == DIRECT_RECV || + gbl_args->appl.in_mode == PLAIN_QUEUE) + print_port_mapping(); + + memset(thread_tbl, 0, sizeof(thread_tbl)); + + stats = gbl_args->stats; + + odp_barrier_init(&barrier, num_workers + 1); + + if (gbl_args->appl.in_mode == DIRECT_RECV) + thr_run_func = run_worker_direct_mode; + else if (gbl_args->appl.in_mode == PLAIN_QUEUE) + thr_run_func = run_worker_plain_queue_mode; + else /* SCHED_PARALLEL / SCHED_ATOMIC / SCHED_ORDERED */ + thr_run_func = run_worker_sched_mode; + + /* Create worker threads */ + cpu = odp_cpumask_first(&cpumask); + for (i = 0; i < num_workers; ++i) { + odp_cpumask_t thd_mask; + odph_odpthread_params_t thr_params; + + memset(&thr_params, 0, sizeof(thr_params)); + thr_params.start = thr_run_func; + thr_params.arg = &gbl_args->thread[i]; + thr_params.thr_type = ODP_THREAD_WORKER; + thr_params.instance = instance; + + gbl_args->thread[i].stats = &stats[i]; + + odp_cpumask_zero(&thd_mask); + odp_cpumask_set(&thd_mask, cpu); + odph_odpthreads_create(&thread_tbl[i], &thd_mask, + &thr_params); + cpu = odp_cpumask_next(&cpumask, cpu); + } + + /* Start packet receive and transmit */ + for (i = 0; i < if_count; ++i) { + odp_pktio_t pktio; + + pktio = gbl_args->pktios[i].pktio; + ret = odp_pktio_start(pktio); + if (ret) { + LOG_ERR("Error: unable to start %s\n", + gbl_args->appl.if_names[i]); + exit(EXIT_FAILURE); + } + } + + ret = print_speed_stats(num_workers, stats, gbl_args->appl.time, + gbl_args->appl.accuracy); + exit_threads = 1; + + /* Master thread waits for other threads to exit */ + for (i = 0; i < num_workers; ++i) + odph_odpthreads_join(&thread_tbl[i]); + + free(gbl_args->appl.if_names); + free(gbl_args->appl.if_str); + + if (odp_pool_destroy(pool)) { + LOG_ERR("Error: pool destroy\n"); + exit(EXIT_FAILURE); + } + + if (odp_term_local()) { + LOG_ERR("Error: term local\n"); + exit(EXIT_FAILURE); + } + + if (odp_term_global(instance)) { + LOG_ERR("Error: term global\n"); + exit(EXIT_FAILURE); + } + + printf("Exit %d\n\n", ret); + return ret; +} diff --git a/test/common_plat/performance/odp_l2fwd_run.sh b/test/common_plat/performance/odp_l2fwd_run.sh new file mode 100755 index 000000000..68e4498a6 --- /dev/null +++ b/test/common_plat/performance/odp_l2fwd_run.sh @@ -0,0 +1,108 @@ +#!/bin/sh +# +# Copyright (c) 2015, Linaro Limited +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# TEST_DIR is set by Makefile, when we add a rule to Makefile for odp_l2fwd_run +# we can use TEST_DIR the same way odp_pktio_run uses it now. +# If TEST_DIR is not set it means we are not running with make, and in this case +# there are two situations: +# 1. user build ODP in the same dir as the source (most likely) +# here the user can simply call odp_l2fwd_run +# 2. user may have built ODP in a separate build dir (like bitbake usually does) +# here the user has to do something like $ODP/test/performance/odp_l2fwd_run +# +# In both situations the script assumes that the user is in the directory where +# odp_l2fwd exists. If that's not true, then the user has to specify the path +# to it and run: +# TEST_DIR=$builddir $ODP/test/performance/odp_l2fwd_run + +# directory where test binaries have been built +TEST_DIR="${TEST_DIR:-$PWD}" +# directory where test sources are, including scripts +TEST_SRC_DIR=$(dirname $0) + +PATH=$TEST_DIR:$TEST_DIR/../../../example/generator:$PATH + +# exit codes expected by automake for skipped tests +TEST_SKIPPED=77 + +# Use installed pktio env or for make check take it from platform directory +if [ -f "./pktio_env" ]; then + . ./pktio_env +elif [ "$ODP_PLATFORM" = "" ]; then + echo "$0: error: ODP_PLATFORM must be defined" + # not skipped as this should never happen via "make check" + exit 1 +elif [ -f ${TEST_SRC_DIR}/../../platform/$ODP_PLATFORM/pktio/pktio_env ]; then + . ${TEST_SRC_DIR}/../../platform/$ODP_PLATFORM/pktio/pktio_env +else + echo "BUG: unable to find pktio_env!" + echo "pktio_env has to be in current directory or in platform/\$ODP_PLATFORM/test." + echo "ODP_PLATFORM=\"$ODP_PLATFORM\"" + exit 1 +fi + +run_l2fwd() +{ + setup_pktio_env clean # install trap to call cleanup_pktio_env + + if [ $? -ne 0 ]; then + echo "setup_pktio_env error $?" + exit $TEST_SKIPPED + fi + + type odp_generator > /dev/null + if [ $? -ne 0 ]; then + echo "odp_generator not installed. Aborting." + cleanup_pktio_env + exit 1 + fi + + #@todo: limit odp_generator to cores + #https://bugs.linaro.org/show_bug.cgi?id=1398 + (odp_generator${EXEEXT} -I $IF0 \ + --srcip 192.168.0.1 --dstip 192.168.0.2 \ + -m u 2>&1 > /dev/null) \ + 2>&1 > /dev/null & + GEN_PID=$! + + # this just turns off output buffering so that you still get periodic + # output while piping to tee, as long as stdbuf is available. + if [ "$(which stdbuf)" != "" ]; then + STDBUF="stdbuf -o 0" + else + STDBUF= + fi + LOG=odp_l2fwd_tmp.log + $STDBUF odp_l2fwd${EXEEXT} -i $IF1,$IF2 -m 0 -t 30 -c 2 | tee $LOG + ret=$? + + kill ${GEN_PID} + + if [ ! -f $LOG ]; then + echo "FAIL: $LOG not found" + ret=1 + elif [ $ret -eq 0 ]; then + PASS_PPS=5000 + MAX_PPS=$(awk '/TEST RESULT/ {print $3}' $LOG) + if [ "$MAX_PPS" -lt "$PASS_PPS" ]; then + echo "FAIL: pps below threshold $MAX_PPS < $PASS_PPS" + ret=1 + fi + fi + + rm -f $LOG + cleanup_pktio_env + + exit $ret +} + +case "$1" in + setup) setup_pktio_env ;; + cleanup) cleanup_pktio_env ;; + *) run_l2fwd ;; +esac diff --git a/test/common_plat/performance/odp_pktio_perf.c b/test/common_plat/performance/odp_pktio_perf.c new file mode 100644 index 000000000..f041b1325 --- /dev/null +++ b/test/common_plat/performance/odp_pktio_perf.c @@ -0,0 +1,1075 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * ODP Packet IO basic performance test application. + * + * Runs a number of transmit and receive workers on separate cores, the + * transmitters generate packets at a defined rate and the receivers consume + * them. Generated packets are UDP and each packet is marked with a magic + * number in the UDP payload allowing receiver to distinguish them from other + * traffic. + * + * Each test iteration runs for a fixed period, at the end of the iteration + * it is verified that the number of packets transmitted was as expected and + * that all transmitted packets were received. + * + * The default mode is to run multiple test iterations at different rates to + * determine the maximum rate at which no packet loss occurs. Alternatively + * a single packet rate can be specified on the command line. + * + */ +#include <odp_api.h> + +#include <odp/helper/eth.h> +#include <odp/helper/ip.h> +#include <odp/helper/udp.h> +#include <odp/helper/linux.h> + +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <test_debug.h> + +#define PKT_BUF_NUM 8192 +#define MAX_NUM_IFACES 2 +#define TEST_HDR_MAGIC 0x92749451 +#define MAX_WORKERS 32 +#define BATCH_LEN_MAX 32 + +/* Packet rate at which to start when using binary search */ +#define RATE_SEARCH_INITIAL_PPS 1000000 + +/* When using the binary search method to determine the maximum + * achievable packet rate, this value specifies how close the pass + * and fail measurements must be before the test is terminated. */ +#define RATE_SEARCH_ACCURACY_PPS 100000 + +/* Amount of time to wait, in nanoseconds, between the transmitter(s) + * completing and the receiver(s) being shutdown. Any packets not + * received by this time will be assumed to have been lost. */ +#define SHUTDOWN_DELAY_NS (ODP_TIME_MSEC_IN_NS * 100) + +#define VPRINT(fmt, ...) \ + do { \ + if (gbl_args->args.verbose) \ + printf(fmt, ##__VA_ARGS__); \ + } while (0) + +#define CACHE_ALIGN_ROUNDUP(x)\ + ((ODP_CACHE_LINE_SIZE) * \ + (((x) + ODP_CACHE_LINE_SIZE - 1) / (ODP_CACHE_LINE_SIZE))) + +#define PKT_HDR_LEN (sizeof(pkt_head_t) + ODPH_UDPHDR_LEN + \ + ODPH_IPV4HDR_LEN + ODPH_ETHHDR_LEN) + +/** Parsed command line application arguments */ +typedef struct { + int cpu_count; /* CPU count */ + int num_tx_workers;/* Number of CPUs to use for transmit */ + int duration; /* Number of seconds to run each iteration + of the test for */ + uint32_t tx_batch_len; /* Number of packets to send in a single + batch */ + int schedule; /* 1: receive packets via scheduler + 0: receive packets via direct deq */ + uint32_t rx_batch_len; /* Number of packets to receive in a single + batch */ + uint64_t pps; /* Attempted packet rate */ + int verbose; /* Print verbose information, such as per + thread statistics */ + unsigned pkt_len; /* Packet payload length in bytes (not + including headers) */ + int search; /* Set implicitly when pps is not configured. + Perform a search at different packet rates + to determine the maximum rate at which no + packet loss occurs. */ + + char *if_str; + const char *ifaces[MAX_NUM_IFACES]; + int num_ifaces; +} test_args_t; + +struct rx_stats_s { + uint64_t rx_cnt; /* Valid packets received */ + uint64_t rx_ignore; /* Ignored packets */ +}; + +typedef union rx_stats_u { + struct rx_stats_s s; + uint8_t pad[CACHE_ALIGN_ROUNDUP(sizeof(struct rx_stats_s))]; +} pkt_rx_stats_t; + +struct tx_stats_s { + uint64_t tx_cnt; /* Packets transmitted */ + uint64_t alloc_failures;/* Packet allocation failures */ + uint64_t enq_failures; /* Enqueue failures */ + odp_time_t idle_ticks; /* Idle ticks count in TX loop */ +}; + +typedef union tx_stats_u { + struct tx_stats_s s; + uint8_t pad[CACHE_ALIGN_ROUNDUP(sizeof(struct tx_stats_s))]; +} pkt_tx_stats_t; + +/* Test global variables */ +typedef struct { + odp_instance_t instance; + test_args_t args; + odp_barrier_t rx_barrier; + odp_barrier_t tx_barrier; + odp_pktio_t pktio_tx; + odp_pktio_t pktio_rx; + pkt_rx_stats_t *rx_stats; + pkt_tx_stats_t *tx_stats; + uint8_t src_mac[ODPH_ETHADDR_LEN]; + uint8_t dst_mac[ODPH_ETHADDR_LEN]; + uint32_t rx_stats_size; + uint32_t tx_stats_size; +} test_globals_t; + +/* Status of max rate search */ +typedef struct { + uint64_t pps_curr; /* Current attempted PPS */ + uint64_t pps_pass; /* Highest passing PPS */ + uint64_t pps_fail; /* Lowest failing PPS */ + int warmup; /* Warmup stage - ignore results */ +} test_status_t; + +/* Thread specific arguments */ +typedef struct { + int batch_len; /* Number of packets per transmit batch */ + int duration; /* Run duration in seconds */ + uint64_t pps; /* Packets per second for this thread */ +} thread_args_t; + +typedef struct { + odp_u32be_t magic; /* Packet header magic number */ +} pkt_head_t; + +/* Pool from which transmitted packets are allocated */ +static odp_pool_t transmit_pkt_pool = ODP_POOL_INVALID; + +/* Sequence number of IP packets */ +static odp_atomic_u32_t ip_seq; + +/* Indicate to the receivers to shutdown */ +static odp_atomic_u32_t shutdown; + +/* Application global data */ +static test_globals_t *gbl_args; + +/* + * Generate a single test packet for transmission. + */ +static odp_packet_t pktio_create_packet(void) +{ + odp_packet_t pkt; + odph_ethhdr_t *eth; + odph_ipv4hdr_t *ip; + odph_udphdr_t *udp; + char *buf; + uint16_t seq; + uint32_t offset; + pkt_head_t pkt_hdr; + size_t payload_len; + + payload_len = sizeof(pkt_hdr) + gbl_args->args.pkt_len; + + pkt = odp_packet_alloc(transmit_pkt_pool, + payload_len + ODPH_UDPHDR_LEN + + ODPH_IPV4HDR_LEN + ODPH_ETHHDR_LEN); + + if (pkt == ODP_PACKET_INVALID) + return ODP_PACKET_INVALID; + + buf = odp_packet_data(pkt); + + /* Ethernet */ + offset = 0; + odp_packet_l2_offset_set(pkt, offset); + eth = (odph_ethhdr_t *)buf; + memcpy(eth->src.addr, gbl_args->src_mac, ODPH_ETHADDR_LEN); + memcpy(eth->dst.addr, gbl_args->dst_mac, ODPH_ETHADDR_LEN); + eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4); + + /* IP */ + offset += ODPH_ETHHDR_LEN; + odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN); + ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN); + ip->dst_addr = odp_cpu_to_be_32(0); + ip->src_addr = odp_cpu_to_be_32(0); + ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN; + ip->tot_len = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN + + ODPH_IPV4HDR_LEN); + ip->ttl = 128; + ip->proto = ODPH_IPPROTO_UDP; + seq = odp_atomic_fetch_inc_u32(&ip_seq); + ip->id = odp_cpu_to_be_16(seq); + ip->chksum = 0; + odph_ipv4_csum_update(pkt); + + /* UDP */ + offset += ODPH_IPV4HDR_LEN; + odp_packet_l4_offset_set(pkt, offset); + udp = (odph_udphdr_t *)(buf + offset); + udp->src_port = odp_cpu_to_be_16(0); + udp->dst_port = odp_cpu_to_be_16(0); + udp->length = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN); + udp->chksum = 0; + + /* payload */ + offset += ODPH_UDPHDR_LEN; + pkt_hdr.magic = TEST_HDR_MAGIC; + if (odp_packet_copy_from_mem(pkt, offset, sizeof(pkt_hdr), + &pkt_hdr) != 0) + LOG_ABORT("Failed to generate test packet.\n"); + + return pkt; +} + +/* + * Check if a packet payload contains test payload magic number. + */ +static int pktio_pkt_has_magic(odp_packet_t pkt) +{ + size_t l4_off; + pkt_head_t pkt_hdr; + + l4_off = odp_packet_l4_offset(pkt); + if (l4_off) { + int ret = odp_packet_copy_to_mem(pkt, + l4_off + ODPH_UDPHDR_LEN, + sizeof(pkt_hdr), &pkt_hdr); + + if (ret != 0) + return 0; + + if (pkt_hdr.magic == TEST_HDR_MAGIC) + return 1; + } + + return 0; +} + +/* + * Allocate packets for transmission. + */ +static int alloc_packets(odp_packet_t *pkt_tbl, int num_pkts) +{ + int n; + + for (n = 0; n < num_pkts; ++n) { + pkt_tbl[n] = pktio_create_packet(); + if (pkt_tbl[n] == ODP_PACKET_INVALID) + break; + } + + return n; +} + +static int send_packets(odp_pktout_queue_t pktout, + odp_packet_t *pkt_tbl, unsigned pkts) +{ + unsigned tx_drops; + unsigned sent = 0; + + if (pkts == 0) + return 0; + + while (sent < pkts) { + int ret; + + ret = odp_pktout_send(pktout, &pkt_tbl[sent], pkts - sent); + + if (odp_likely(ret > 0)) + sent += ret; + else + break; + } + + tx_drops = pkts - sent; + + if (odp_unlikely(tx_drops)) + odp_packet_free_multi(&pkt_tbl[sent], tx_drops); + + return sent; +} + +/* + * Main packet transmit routine. Transmit packets at a fixed rate for + * specified length of time. + */ +static int run_thread_tx(void *arg) +{ + test_globals_t *globals; + int thr_id; + odp_pktout_queue_t pktout; + pkt_tx_stats_t *stats; + odp_time_t cur_time, send_time_end, send_duration; + odp_time_t burst_gap_end, burst_gap; + uint32_t batch_len; + int unsent_pkts = 0; + odp_packet_t tx_packet[BATCH_LEN_MAX]; + odp_time_t idle_start = ODP_TIME_NULL; + + thread_args_t *targs = arg; + + batch_len = targs->batch_len; + + if (batch_len > BATCH_LEN_MAX) + batch_len = BATCH_LEN_MAX; + + thr_id = odp_thread_id(); + + globals = odp_shm_addr(odp_shm_lookup("test_globals")); + stats = &globals->tx_stats[thr_id]; + + if (odp_pktout_queue(globals->pktio_tx, &pktout, 1) != 1) + LOG_ABORT("Failed to get output queue for thread %d\n", thr_id); + + burst_gap = odp_time_local_from_ns( + ODP_TIME_SEC_IN_NS / (targs->pps / targs->batch_len)); + send_duration = + odp_time_local_from_ns(targs->duration * ODP_TIME_SEC_IN_NS); + + odp_barrier_wait(&globals->tx_barrier); + + cur_time = odp_time_local(); + send_time_end = odp_time_sum(cur_time, send_duration); + burst_gap_end = cur_time; + while (odp_time_cmp(send_time_end, cur_time) > 0) { + unsigned alloc_cnt = 0, tx_cnt; + + if (odp_time_cmp(burst_gap_end, cur_time) > 0) { + cur_time = odp_time_local(); + if (!odp_time_cmp(idle_start, ODP_TIME_NULL)) + idle_start = cur_time; + continue; + } + + if (odp_time_cmp(idle_start, ODP_TIME_NULL) > 0) { + odp_time_t diff = odp_time_diff(cur_time, idle_start); + + stats->s.idle_ticks = + odp_time_sum(diff, stats->s.idle_ticks); + + idle_start = ODP_TIME_NULL; + } + + burst_gap_end = odp_time_sum(burst_gap_end, burst_gap); + + alloc_cnt = alloc_packets(tx_packet, batch_len - unsent_pkts); + if (alloc_cnt != batch_len) + stats->s.alloc_failures++; + + tx_cnt = send_packets(pktout, tx_packet, alloc_cnt); + unsent_pkts = alloc_cnt - tx_cnt; + stats->s.enq_failures += unsent_pkts; + stats->s.tx_cnt += tx_cnt; + + cur_time = odp_time_local(); + } + + VPRINT(" %02d: TxPkts %-8" PRIu64 " EnqFail %-6" PRIu64 + " AllocFail %-6" PRIu64 " Idle %" PRIu64 "ms\n", + thr_id, stats->s.tx_cnt, + stats->s.enq_failures, stats->s.alloc_failures, + odp_time_to_ns(stats->s.idle_ticks) / + (uint64_t)ODP_TIME_MSEC_IN_NS); + + return 0; +} + +static int receive_packets(odp_queue_t queue, + odp_event_t *event_tbl, unsigned num_pkts) +{ + int n_ev = 0; + + if (num_pkts == 0) + return 0; + + if (queue != ODP_QUEUE_INVALID) { + if (num_pkts == 1) { + event_tbl[0] = odp_queue_deq(queue); + n_ev = event_tbl[0] != ODP_EVENT_INVALID; + } else { + n_ev = odp_queue_deq_multi(queue, event_tbl, num_pkts); + } + } else { + if (num_pkts == 1) { + event_tbl[0] = odp_schedule(NULL, ODP_SCHED_NO_WAIT); + n_ev = event_tbl[0] != ODP_EVENT_INVALID; + } else { + n_ev = odp_schedule_multi(NULL, ODP_SCHED_NO_WAIT, + event_tbl, num_pkts); + } + } + return n_ev; +} + +static int run_thread_rx(void *arg) +{ + test_globals_t *globals; + int thr_id, batch_len; + odp_queue_t queue = ODP_QUEUE_INVALID; + odp_packet_t pkt; + + thread_args_t *targs = arg; + + batch_len = targs->batch_len; + + if (batch_len > BATCH_LEN_MAX) + batch_len = BATCH_LEN_MAX; + + thr_id = odp_thread_id(); + + globals = odp_shm_addr(odp_shm_lookup("test_globals")); + + pkt_rx_stats_t *stats = &globals->rx_stats[thr_id]; + + if (gbl_args->args.schedule == 0) { + if (odp_pktin_event_queue(globals->pktio_rx, &queue, 1) != 1) + LOG_ABORT("No input queue.\n"); + } + + odp_barrier_wait(&globals->rx_barrier); + while (1) { + odp_event_t ev[BATCH_LEN_MAX]; + int i, n_ev; + + n_ev = receive_packets(queue, ev, batch_len); + + for (i = 0; i < n_ev; ++i) { + if (odp_event_type(ev[i]) == ODP_EVENT_PACKET) { + pkt = odp_packet_from_event(ev[i]); + if (pktio_pkt_has_magic(pkt)) + stats->s.rx_cnt++; + else + stats->s.rx_ignore++; + } + odp_event_free(ev[i]); + } + if (n_ev == 0 && odp_atomic_load_u32(&shutdown)) + break; + } + + return 0; +} + +/* + * Process the results from a single fixed rate test run to determine whether + * it passed or failed. Pass criteria are that the requested transmit packet + * rate was achieved and that all of the transmitted packets were received. + */ +static int process_results(uint64_t expected_tx_cnt, + test_status_t *status) +{ + int fail = 0; + uint64_t drops = 0; + uint64_t rx_pkts = 0; + uint64_t tx_pkts = 0; + uint64_t attempted_pps; + int i; + char str[512]; + int len = 0; + + for (i = 0; i < odp_thread_count_max(); ++i) { + rx_pkts += gbl_args->rx_stats[i].s.rx_cnt; + tx_pkts += gbl_args->tx_stats[i].s.tx_cnt; + } + + if (rx_pkts == 0) { + LOG_ERR("no packets received\n"); + return -1; + } + + if (tx_pkts < (expected_tx_cnt - (expected_tx_cnt / 100))) { + /* failed to transmit packets at (99% of) requested rate */ + fail = 1; + } else if (tx_pkts > rx_pkts) { + /* failed to receive all of the transmitted packets */ + fail = 1; + drops = tx_pkts - rx_pkts; + } + + attempted_pps = status->pps_curr; + + len += snprintf(&str[len], sizeof(str) - 1 - len, + "PPS: %-8" PRIu64 " ", attempted_pps); + len += snprintf(&str[len], sizeof(str) - 1 - len, + "Succeeded: %-4s ", fail ? "No" : "Yes"); + len += snprintf(&str[len], sizeof(str) - 1 - len, + "TxPkts: %-8" PRIu64 " ", tx_pkts); + len += snprintf(&str[len], sizeof(str) - 1 - len, + "RxPkts: %-8" PRIu64 " ", rx_pkts); + len += snprintf(&str[len], sizeof(str) - 1 - len, + "DropPkts: %-8" PRIu64 " ", drops); + printf("%s\n", str); + + if (gbl_args->args.search == 0) { + printf("Result: %s\n", fail ? "FAILED" : "PASSED"); + return fail ? -1 : 0; + } + + if (fail && (status->pps_fail == 0 || + attempted_pps < status->pps_fail)) { + status->pps_fail = attempted_pps; + } else if (attempted_pps > status->pps_pass) { + status->pps_pass = attempted_pps; + } + + if (status->pps_fail == 0) { + /* ramping up, double the previously attempted pps */ + status->pps_curr *= 2; + } else { + /* set the new target to half way between the upper and lower + * limits */ + status->pps_curr = status->pps_pass + + ((status->pps_fail - status->pps_pass) / 2); + } + + /* stop once the pass and fail measurements are within range */ + if ((status->pps_fail - status->pps_pass) < RATE_SEARCH_ACCURACY_PPS) { + unsigned pkt_len = gbl_args->args.pkt_len + PKT_HDR_LEN; + int mbps = (pkt_len * status->pps_pass * 8) / 1024 / 1024; + + printf("Maximum packet rate: %" PRIu64 " PPS (%d Mbps)\n", + status->pps_pass, mbps); + + return 0; + } + + return 1; +} + +static int setup_txrx_masks(odp_cpumask_t *thd_mask_tx, + odp_cpumask_t *thd_mask_rx) +{ + odp_cpumask_t cpumask; + int num_workers, num_tx_workers, num_rx_workers; + int i, cpu; + + num_workers = + odp_cpumask_default_worker(&cpumask, + gbl_args->args.cpu_count); + if (num_workers < 2) { + LOG_ERR("Need at least two cores\n"); + return -1; + } + + if (gbl_args->args.num_tx_workers) { + if (gbl_args->args.num_tx_workers > (num_workers - 1)) { + LOG_ERR("Invalid TX worker count\n"); + return -1; + } + num_tx_workers = gbl_args->args.num_tx_workers; + } else { + /* default is to split the available cores evenly into TX and + * RX workers, favour TX for odd core count */ + num_tx_workers = (num_workers + 1) / 2; + } + + odp_cpumask_zero(thd_mask_tx); + odp_cpumask_zero(thd_mask_rx); + + cpu = odp_cpumask_first(&cpumask); + for (i = 0; i < num_workers; ++i) { + if (i < num_tx_workers) + odp_cpumask_set(thd_mask_tx, cpu); + else + odp_cpumask_set(thd_mask_rx, cpu); + cpu = odp_cpumask_next(&cpumask, cpu); + } + + num_rx_workers = odp_cpumask_count(thd_mask_rx); + + odp_barrier_init(&gbl_args->rx_barrier, num_rx_workers + 1); + odp_barrier_init(&gbl_args->tx_barrier, num_tx_workers + 1); + + return 0; +} + +/* + * Run a single instance of the throughput test. When attempting to determine + * the maximum packet rate this will be invoked multiple times with the only + * difference between runs being the target PPS rate. + */ +static int run_test_single(odp_cpumask_t *thd_mask_tx, + odp_cpumask_t *thd_mask_rx, + test_status_t *status) +{ + odph_odpthread_t thd_tbl[MAX_WORKERS]; + thread_args_t args_tx, args_rx; + uint64_t expected_tx_cnt; + int num_tx_workers, num_rx_workers; + odph_odpthread_params_t thr_params; + + memset(&thr_params, 0, sizeof(thr_params)); + thr_params.thr_type = ODP_THREAD_WORKER; + thr_params.instance = gbl_args->instance; + + odp_atomic_store_u32(&shutdown, 0); + + memset(thd_tbl, 0, sizeof(thd_tbl)); + memset(gbl_args->rx_stats, 0, gbl_args->rx_stats_size); + memset(gbl_args->tx_stats, 0, gbl_args->tx_stats_size); + + expected_tx_cnt = status->pps_curr * gbl_args->args.duration; + + /* start receiver threads first */ + thr_params.start = run_thread_rx; + thr_params.arg = &args_rx; + args_rx.batch_len = gbl_args->args.rx_batch_len; + odph_odpthreads_create(&thd_tbl[0], thd_mask_rx, &thr_params); + odp_barrier_wait(&gbl_args->rx_barrier); + num_rx_workers = odp_cpumask_count(thd_mask_rx); + + /* then start transmitters */ + thr_params.start = run_thread_tx; + thr_params.arg = &args_tx; + num_tx_workers = odp_cpumask_count(thd_mask_tx); + args_tx.pps = status->pps_curr / num_tx_workers; + args_tx.duration = gbl_args->args.duration; + args_tx.batch_len = gbl_args->args.tx_batch_len; + odph_odpthreads_create(&thd_tbl[num_rx_workers], thd_mask_tx, + &thr_params); + odp_barrier_wait(&gbl_args->tx_barrier); + + /* wait for transmitter threads to terminate */ + odph_odpthreads_join(&thd_tbl[num_rx_workers]); + + /* delay to allow transmitted packets to reach the receivers */ + odp_time_wait_ns(SHUTDOWN_DELAY_NS); + + /* indicate to the receivers to exit */ + odp_atomic_store_u32(&shutdown, 1); + + /* wait for receivers */ + odph_odpthreads_join(&thd_tbl[0]); + + if (!status->warmup) + return process_results(expected_tx_cnt, status); + + return 1; +} + +static int run_test(void) +{ + int ret = 1; + int i; + odp_cpumask_t txmask, rxmask; + test_status_t status = { + .pps_curr = gbl_args->args.pps, + .pps_pass = 0, + .pps_fail = 0, + .warmup = 1, + }; + + if (setup_txrx_masks(&txmask, &rxmask) != 0) + return -1; + + printf("Starting test with params:\n"); + printf("\tTransmit workers: \t%d\n", odp_cpumask_count(&txmask)); + printf("\tReceive workers: \t%d\n", odp_cpumask_count(&rxmask)); + printf("\tDuration (seconds): \t%d\n", gbl_args->args.duration); + printf("\tTransmit batch length:\t%" PRIu32 "\n", + gbl_args->args.tx_batch_len); + printf("\tReceive batch length: \t%" PRIu32 "\n", + gbl_args->args.rx_batch_len); + printf("\tPacket receive method:\t%s\n", + gbl_args->args.schedule ? "schedule" : "plain"); + printf("\tInterface(s): \t"); + for (i = 0; i < gbl_args->args.num_ifaces; ++i) + printf("%s ", gbl_args->args.ifaces[i]); + printf("\n"); + + /* first time just run the test but throw away the results */ + run_test_single(&txmask, &rxmask, &status); + status.warmup = 0; + + while (ret > 0) + ret = run_test_single(&txmask, &rxmask, &status); + + return ret; +} + +static odp_pktio_t create_pktio(const char *iface, int schedule) +{ + odp_pool_t pool; + odp_pktio_t pktio; + char pool_name[ODP_POOL_NAME_LEN]; + odp_pool_param_t params; + odp_pktio_param_t pktio_param; + + odp_pool_param_init(¶ms); + params.pkt.len = PKT_HDR_LEN + gbl_args->args.pkt_len; + params.pkt.seg_len = params.pkt.len; + params.pkt.num = PKT_BUF_NUM; + params.type = ODP_POOL_PACKET; + + snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s", iface); + pool = odp_pool_create(pool_name, ¶ms); + if (pool == ODP_POOL_INVALID) + return ODP_PKTIO_INVALID; + + odp_pktio_param_init(&pktio_param); + + if (schedule) + pktio_param.in_mode = ODP_PKTIN_MODE_SCHED; + else + pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE; + + pktio = odp_pktio_open(iface, pool, &pktio_param); + + return pktio; +} + +static int test_init(void) +{ + odp_pool_param_t params; + const char *iface; + int schedule; + + odp_pool_param_init(¶ms); + params.pkt.len = PKT_HDR_LEN + gbl_args->args.pkt_len; + params.pkt.seg_len = params.pkt.len; + params.pkt.num = PKT_BUF_NUM; + params.type = ODP_POOL_PACKET; + + transmit_pkt_pool = odp_pool_create("pkt_pool_transmit", ¶ms); + if (transmit_pkt_pool == ODP_POOL_INVALID) + LOG_ABORT("Failed to create transmit pool\n"); + + odp_atomic_init_u32(&ip_seq, 0); + odp_atomic_init_u32(&shutdown, 0); + + iface = gbl_args->args.ifaces[0]; + schedule = gbl_args->args.schedule; + + /* create pktios and associate input/output queues */ + gbl_args->pktio_tx = create_pktio(iface, schedule); + if (gbl_args->args.num_ifaces > 1) { + iface = gbl_args->args.ifaces[1]; + gbl_args->pktio_rx = create_pktio(iface, schedule); + } else { + gbl_args->pktio_rx = gbl_args->pktio_tx; + } + + odp_pktio_mac_addr(gbl_args->pktio_tx, gbl_args->src_mac, + ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(gbl_args->pktio_rx, gbl_args->dst_mac, + ODPH_ETHADDR_LEN); + + if (gbl_args->pktio_rx == ODP_PKTIO_INVALID || + gbl_args->pktio_tx == ODP_PKTIO_INVALID) { + LOG_ERR("failed to open pktio\n"); + return -1; + } + + /* Create single queue with default parameters */ + if (odp_pktout_queue_config(gbl_args->pktio_tx, NULL)) { + LOG_ERR("failed to configure pktio_tx queue\n"); + return -1; + } + + /* Configure also input side (with defaults) */ + if (odp_pktin_queue_config(gbl_args->pktio_tx, NULL)) { + LOG_ERR("failed to configure pktio_tx queue\n"); + return -1; + } + + if (gbl_args->args.num_ifaces > 1) { + if (odp_pktout_queue_config(gbl_args->pktio_rx, NULL)) { + LOG_ERR("failed to configure pktio_rx queue\n"); + return -1; + } + + if (odp_pktin_queue_config(gbl_args->pktio_rx, NULL)) { + LOG_ERR("failed to configure pktio_rx queue\n"); + return -1; + } + } + + if (odp_pktio_start(gbl_args->pktio_tx) != 0) + return -1; + if (gbl_args->args.num_ifaces > 1 && + odp_pktio_start(gbl_args->pktio_rx)) + return -1; + + return 0; +} + +static int empty_inq(odp_pktio_t pktio) +{ + odp_queue_t queue; + odp_event_t ev; + odp_queue_type_t q_type; + + if (odp_pktin_event_queue(pktio, &queue, 1) != 1) + return -1; + + q_type = odp_queue_type(queue); + + /* flush any pending events */ + while (1) { + if (q_type == ODP_QUEUE_TYPE_PLAIN) + ev = odp_queue_deq(queue); + else + ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT); + + if (ev != ODP_EVENT_INVALID) + odp_event_free(ev); + else + break; + } + + return 0; +} + +static int test_term(void) +{ + char pool_name[ODP_POOL_NAME_LEN]; + odp_pool_t pool; + int i; + int ret = 0; + + if (gbl_args->pktio_tx != gbl_args->pktio_rx) { + if (odp_pktio_stop(gbl_args->pktio_tx)) { + LOG_ERR("Failed to stop pktio_tx\n"); + return -1; + } + + if (odp_pktio_close(gbl_args->pktio_tx)) { + LOG_ERR("Failed to close pktio_tx\n"); + ret = -1; + } + } + + empty_inq(gbl_args->pktio_rx); + + if (odp_pktio_stop(gbl_args->pktio_rx)) { + LOG_ERR("Failed to stop pktio_rx\n"); + return -1; + } + + if (odp_pktio_close(gbl_args->pktio_rx) != 0) { + LOG_ERR("Failed to close pktio_rx\n"); + ret = -1; + } + + for (i = 0; i < gbl_args->args.num_ifaces; ++i) { + snprintf(pool_name, sizeof(pool_name), + "pkt_pool_%s", gbl_args->args.ifaces[i]); + pool = odp_pool_lookup(pool_name); + if (pool == ODP_POOL_INVALID) + continue; + + if (odp_pool_destroy(pool) != 0) { + LOG_ERR("Failed to destroy pool %s\n", pool_name); + ret = -1; + } + } + + if (odp_pool_destroy(transmit_pkt_pool) != 0) { + LOG_ERR("Failed to destroy transmit pool\n"); + ret = -1; + } + + free(gbl_args->args.if_str); + + if (odp_shm_free(odp_shm_lookup("test_globals")) != 0) { + LOG_ERR("Failed to free test_globals\n"); + ret = -1; + } + + return ret; +} + +static void usage(void) +{ + printf("\nUsage: odp_pktio_perf [options]\n\n"); + printf(" -c, --count <number> CPU count\n"); + printf(" default: all available\n"); + printf(" -t, --txcount <number> Number of CPUs to use for TX\n"); + printf(" default: cpu_count+1/2\n"); + printf(" -b, --txbatch <length> Number of packets per TX batch\n"); + printf(" default: %d\n", BATCH_LEN_MAX); + printf(" -p, --plain Plain input queue for packet RX\n"); + printf(" default: disabled (use scheduler)\n"); + printf(" -R, --rxbatch <length> Number of packets per RX batch\n"); + printf(" default: %d\n", BATCH_LEN_MAX); + printf(" -l, --length <length> Additional payload length in bytes\n"); + printf(" default: 0\n"); + printf(" -r, --rate <number> Attempted packet rate in PPS\n"); + printf(" -i, --interface <list> List of interface names to use\n"); + printf(" -d, --duration <secs> Duration of each test iteration\n"); + printf(" -v, --verbose Print verbose information\n"); + printf(" -h, --help This help\n"); + printf("\n"); +} + +static void parse_args(int argc, char *argv[], test_args_t *args) +{ + int opt; + int long_index; + + static const struct option longopts[] = { + {"count", required_argument, NULL, 'c'}, + {"txcount", required_argument, NULL, 't'}, + {"txbatch", required_argument, NULL, 'b'}, + {"plain", no_argument, NULL, 'p'}, + {"rxbatch", required_argument, NULL, 'R'}, + {"length", required_argument, NULL, 'l'}, + {"rate", required_argument, NULL, 'r'}, + {"interface", required_argument, NULL, 'i'}, + {"duration", required_argument, NULL, 'd'}, + {"verbose", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + static const char *shortopts = "+c:t:b:pR:l:r:i:d:vh"; + + /* let helper collect its own arguments (e.g. --odph_proc) */ + odph_parse_options(argc, argv, shortopts, longopts); + + args->cpu_count = 0; /* all CPUs */ + args->num_tx_workers = 0; /* defaults to cpu_count+1/2 */ + args->tx_batch_len = BATCH_LEN_MAX; + args->rx_batch_len = BATCH_LEN_MAX; + args->duration = 1; + args->pps = RATE_SEARCH_INITIAL_PPS; + args->search = 1; + args->schedule = 1; + args->verbose = 0; + + opterr = 0; /* do not issue errors on helper options */ + + while (1) { + opt = getopt_long(argc, argv, shortopts, + longopts, &long_index); + + if (opt == -1) + break; + + switch (opt) { + case 'h': + usage(); + exit(EXIT_SUCCESS); + case 'c': + args->cpu_count = atoi(optarg); + break; + case 't': + args->num_tx_workers = atoi(optarg); + break; + case 'd': + args->duration = atoi(optarg); + break; + case 'r': + args->pps = atoi(optarg); + args->search = 0; + args->verbose = 1; + break; + case 'i': + { + char *token; + + args->if_str = malloc(strlen(optarg) + 1); + + if (!args->if_str) + LOG_ABORT("Failed to alloc iface storage\n"); + + strcpy(args->if_str, optarg); + for (token = strtok(args->if_str, ","); + token != NULL && args->num_ifaces < MAX_NUM_IFACES; + token = strtok(NULL, ",")) + args->ifaces[args->num_ifaces++] = token; + } + break; + case 'p': + args->schedule = 0; + break; + case 'b': + args->tx_batch_len = atoi(optarg); + break; + case 'R': + args->rx_batch_len = atoi(optarg); + break; + case 'v': + args->verbose = 1; + break; + case 'l': + args->pkt_len = atoi(optarg); + break; + } + } + + if (args->num_ifaces == 0) { + args->ifaces[0] = "loop"; + args->num_ifaces = 1; + } +} + +int main(int argc, char **argv) +{ + int ret; + odp_shm_t shm; + int max_thrs; + odp_instance_t instance; + + if (odp_init_global(&instance, NULL, NULL) != 0) + LOG_ABORT("Failed global init.\n"); + + if (odp_init_local(instance, ODP_THREAD_CONTROL) != 0) + LOG_ABORT("Failed local init.\n"); + + shm = odp_shm_reserve("test_globals", + sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0); + gbl_args = odp_shm_addr(shm); + if (gbl_args == NULL) + LOG_ABORT("Shared memory reserve failed.\n"); + memset(gbl_args, 0, sizeof(test_globals_t)); + + max_thrs = odp_thread_count_max(); + + gbl_args->instance = instance; + gbl_args->rx_stats_size = max_thrs * sizeof(pkt_rx_stats_t); + gbl_args->tx_stats_size = max_thrs * sizeof(pkt_tx_stats_t); + + shm = odp_shm_reserve("test_globals.rx_stats", + gbl_args->rx_stats_size, + ODP_CACHE_LINE_SIZE, 0); + + gbl_args->rx_stats = odp_shm_addr(shm); + + if (gbl_args->rx_stats == NULL) + LOG_ABORT("Shared memory reserve failed.\n"); + + memset(gbl_args->rx_stats, 0, gbl_args->rx_stats_size); + + shm = odp_shm_reserve("test_globals.tx_stats", + gbl_args->tx_stats_size, + ODP_CACHE_LINE_SIZE, 0); + + gbl_args->tx_stats = odp_shm_addr(shm); + + if (gbl_args->tx_stats == NULL) + LOG_ABORT("Shared memory reserve failed.\n"); + + memset(gbl_args->tx_stats, 0, gbl_args->tx_stats_size); + + parse_args(argc, argv, &gbl_args->args); + + ret = test_init(); + + if (ret == 0) { + ret = run_test(); + test_term(); + } + + return ret; +} diff --git a/test/common_plat/performance/odp_scheduling.c b/test/common_plat/performance/odp_scheduling.c new file mode 100644 index 000000000..5a2997fd1 --- /dev/null +++ b/test/common_plat/performance/odp_scheduling.c @@ -0,0 +1,963 @@ +/* Copyright (c) 2013, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * @example odp_example.c ODP example application + */ + +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> + +#include <test_debug.h> + +/* ODP main header */ +#include <odp_api.h> + +/* ODP helper for Linux apps */ +#include <odp/helper/linux.h> + +/* Needs librt*/ +#include <time.h> + +/* GNU lib C */ +#include <getopt.h> + +#define MSG_POOL_SIZE (4 * 1024 * 1024) /**< Message pool size */ +#define MAX_ALLOCS 35 /**< Alloc burst size */ +#define QUEUES_PER_PRIO 64 /**< Queue per priority */ +#define NUM_PRIOS 2 /**< Number of tested priorities */ +#define QUEUE_ROUNDS (512 * 1024) /**< Queue test rounds */ +#define ALLOC_ROUNDS (1024 * 1024) /**< Alloc test rounds */ +#define MULTI_BUFS_MAX 4 /**< Buffer burst size */ +#define TEST_SEC 2 /**< Time test duration in sec */ +#define STATS_PER_LINE 8 /**< Stats per printed line */ + +/** Dummy message */ +typedef struct { + int msg_id; /**< Message ID */ + int seq; /**< Sequence number */ +} test_message_t; + +#define MSG_HELLO 1 /**< Hello */ +#define MSG_ACK 2 /**< Ack */ + +/** Test arguments */ +typedef struct { + int cpu_count; /**< CPU count */ + int fairness; /**< Check fairness */ +} test_args_t; + +typedef struct { + uint64_t num_ev; + + /* Round up the struct size to cache line size */ + uint8_t pad[ODP_CACHE_LINE_SIZE - sizeof(uint64_t)]; +} queue_context_t ODP_ALIGNED_CACHE; + +/** Test global variables */ +typedef struct { + odp_barrier_t barrier; + odp_spinlock_t lock; + odp_pool_t pool; + int first_thr; + test_args_t args; + odp_queue_t queue[NUM_PRIOS][QUEUES_PER_PRIO]; + queue_context_t queue_ctx[NUM_PRIOS][QUEUES_PER_PRIO]; +} test_globals_t; + +/* Prints and initializes queue statistics */ +static void print_stats(int prio, test_globals_t *globals) +{ + int i, j, k; + + if (prio == ODP_SCHED_PRIO_HIGHEST) + i = 0; + else + i = 1; + + printf("\nQueue fairness\n-----+--------\n"); + + for (j = 0; j < QUEUES_PER_PRIO;) { + printf(" %2i | ", j); + + for (k = 0; k < STATS_PER_LINE - 1; k++) { + printf(" %8" PRIu64, + globals->queue_ctx[i][j].num_ev); + globals->queue_ctx[i][j++].num_ev = 0; + } + + printf(" %8" PRIu64 "\n", globals->queue_ctx[i][j].num_ev); + globals->queue_ctx[i][j++].num_ev = 0; + } + + printf("\n"); +} + +/** + * @internal Clear all scheduled queues. Retry to be sure that all + * buffers have been scheduled. + */ +static void clear_sched_queues(void) +{ + odp_event_t ev; + + while (1) { + ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT); + + if (ev == ODP_EVENT_INVALID) + break; + + odp_event_free(ev); + } +} + +/** + * @internal Enqueue events into queues + * + * @param thr Thread + * @param prio Queue priority + * @param num_queues Number of queues + * @param num_events Number of events + * @param globals Test shared data + * + * @return 0 if successful + */ +static int enqueue_events(int thr, int prio, int num_queues, int num_events, + test_globals_t *globals) +{ + odp_buffer_t buf; + odp_queue_t queue; + int i, j, k; + + if (prio == ODP_SCHED_PRIO_HIGHEST) + i = 0; + else + i = 1; + + /* Alloc and enqueue a buffer per queue */ + for (j = 0; j < num_queues; j++) { + queue = globals->queue[i][j]; + + for (k = 0; k < num_events; k++) { + buf = odp_buffer_alloc(globals->pool); + + if (!odp_buffer_is_valid(buf)) { + LOG_ERR(" [%i] buffer alloc failed\n", thr); + return -1; + } + + if (odp_queue_enq(queue, odp_buffer_to_event(buf))) { + LOG_ERR(" [%i] Queue enqueue failed.\n", thr); + odp_buffer_free(buf); + return -1; + } + } + } + + return 0; +} + +/** + * @internal Test single buffer alloc and free + * + * @param thr Thread + * @param globals Test shared data + * + * @return 0 if successful + */ +static int test_alloc_single(int thr, test_globals_t *globals) +{ + int i; + odp_buffer_t temp_buf; + uint64_t c1, c2, cycles; + + c1 = odp_cpu_cycles(); + + for (i = 0; i < ALLOC_ROUNDS; i++) { + temp_buf = odp_buffer_alloc(globals->pool); + + if (!odp_buffer_is_valid(temp_buf)) { + LOG_ERR(" [%i] alloc_single failed\n", thr); + return -1; + } + + odp_buffer_free(temp_buf); + } + + c2 = odp_cpu_cycles(); + cycles = odp_cpu_cycles_diff(c2, c1); + cycles = cycles / ALLOC_ROUNDS; + + printf(" [%i] alloc_sng alloc+free %6" PRIu64 " CPU cycles\n", + thr, cycles); + + return 0; +} + +/** + * @internal Test multiple buffers alloc and free + * + * @param thr Thread + * @param globals Test shared data + * + * @return 0 if successful + */ +static int test_alloc_multi(int thr, test_globals_t *globals) +{ + int i, j; + odp_buffer_t temp_buf[MAX_ALLOCS]; + uint64_t c1, c2, cycles; + + c1 = odp_cpu_cycles(); + + for (i = 0; i < ALLOC_ROUNDS; i++) { + for (j = 0; j < MAX_ALLOCS; j++) { + temp_buf[j] = odp_buffer_alloc(globals->pool); + + if (!odp_buffer_is_valid(temp_buf[j])) { + LOG_ERR(" [%i] alloc_multi failed\n", thr); + return -1; + } + } + + for (; j > 0; j--) + odp_buffer_free(temp_buf[j - 1]); + } + + c2 = odp_cpu_cycles(); + cycles = odp_cpu_cycles_diff(c2, c1); + cycles = cycles / (ALLOC_ROUNDS * MAX_ALLOCS); + + printf(" [%i] alloc_multi alloc+free %6" PRIu64 " CPU cycles\n", + thr, cycles); + + return 0; +} + +/** + * @internal Test plain queues + * + * Enqueue to and dequeue to/from a single shared queue. + * + * @param thr Thread + * @param globals Test shared data + * + * @return 0 if successful + */ +static int test_plain_queue(int thr, test_globals_t *globals) +{ + odp_event_t ev; + odp_buffer_t buf; + test_message_t *t_msg; + odp_queue_t queue; + uint64_t c1, c2, cycles; + int i; + + /* Alloc test message */ + buf = odp_buffer_alloc(globals->pool); + + if (!odp_buffer_is_valid(buf)) { + LOG_ERR(" [%i] buffer alloc failed\n", thr); + return -1; + } + + /* odp_buffer_print(buf); */ + + t_msg = odp_buffer_addr(buf); + t_msg->msg_id = MSG_HELLO; + t_msg->seq = 0; + + queue = odp_queue_lookup("plain_queue"); + + if (queue == ODP_QUEUE_INVALID) { + printf(" [%i] Queue lookup failed.\n", thr); + return -1; + } + + c1 = odp_cpu_cycles(); + + for (i = 0; i < QUEUE_ROUNDS; i++) { + ev = odp_buffer_to_event(buf); + + if (odp_queue_enq(queue, ev)) { + LOG_ERR(" [%i] Queue enqueue failed.\n", thr); + odp_buffer_free(buf); + return -1; + } + + ev = odp_queue_deq(queue); + + buf = odp_buffer_from_event(ev); + + if (!odp_buffer_is_valid(buf)) { + LOG_ERR(" [%i] Queue empty.\n", thr); + return -1; + } + } + + c2 = odp_cpu_cycles(); + cycles = odp_cpu_cycles_diff(c2, c1); + cycles = cycles / QUEUE_ROUNDS; + + printf(" [%i] plain_queue enq+deq %6" PRIu64 " CPU cycles\n", + thr, cycles); + + odp_buffer_free(buf); + return 0; +} + +/** + * @internal Test scheduling of a single queue - with odp_schedule() + * + * Enqueue a buffer to the shared queue. Schedule and enqueue the received + * buffer back into the queue. + * + * @param str Test case name string + * @param thr Thread + * @param prio Priority + * @param globals Test shared data + * + * @return 0 if successful + */ +static int test_schedule_single(const char *str, int thr, + int prio, test_globals_t *globals) +{ + odp_event_t ev; + odp_queue_t queue; + uint64_t c1, c2, cycles; + uint32_t i; + uint32_t tot; + + if (enqueue_events(thr, prio, 1, 1, globals)) + return -1; + + c1 = odp_cpu_cycles(); + + for (i = 0; i < QUEUE_ROUNDS; i++) { + ev = odp_schedule(&queue, ODP_SCHED_WAIT); + + if (odp_queue_enq(queue, ev)) { + LOG_ERR(" [%i] Queue enqueue failed.\n", thr); + odp_event_free(ev); + return -1; + } + } + + /* Clear possible locally stored buffers */ + odp_schedule_pause(); + + tot = i; + + while (1) { + ev = odp_schedule(&queue, ODP_SCHED_NO_WAIT); + + if (ev == ODP_EVENT_INVALID) + break; + + tot++; + + if (odp_queue_enq(queue, ev)) { + LOG_ERR(" [%i] Queue enqueue failed.\n", thr); + odp_event_free(ev); + return -1; + } + } + + odp_schedule_resume(); + + c2 = odp_cpu_cycles(); + cycles = odp_cpu_cycles_diff(c2, c1); + + odp_barrier_wait(&globals->barrier); + clear_sched_queues(); + + cycles = cycles / tot; + + printf(" [%i] %s enq+deq %6" PRIu64 " CPU cycles\n", thr, str, cycles); + + return 0; +} + +/** + * @internal Test scheduling of multiple queues - with odp_schedule() + * + * Enqueue a buffer to each queue. Schedule and enqueue the received + * buffer back into the queue it came from. + * + * @param str Test case name string + * @param thr Thread + * @param prio Priority + * @param globals Test shared data + * + * @return 0 if successful + */ +static int test_schedule_many(const char *str, int thr, + int prio, test_globals_t *globals) +{ + odp_event_t ev; + odp_queue_t queue; + uint64_t c1, c2, cycles; + uint32_t i; + uint32_t tot; + + if (enqueue_events(thr, prio, QUEUES_PER_PRIO, 1, globals)) + return -1; + + /* Start sched-enq loop */ + c1 = odp_cpu_cycles(); + + for (i = 0; i < QUEUE_ROUNDS; i++) { + ev = odp_schedule(&queue, ODP_SCHED_WAIT); + + if (odp_queue_enq(queue, ev)) { + LOG_ERR(" [%i] Queue enqueue failed.\n", thr); + odp_event_free(ev); + return -1; + } + } + + /* Clear possible locally stored buffers */ + odp_schedule_pause(); + + tot = i; + + while (1) { + ev = odp_schedule(&queue, ODP_SCHED_NO_WAIT); + + if (ev == ODP_EVENT_INVALID) + break; + + tot++; + + if (odp_queue_enq(queue, ev)) { + LOG_ERR(" [%i] Queue enqueue failed.\n", thr); + odp_event_free(ev); + return -1; + } + } + + odp_schedule_resume(); + + c2 = odp_cpu_cycles(); + cycles = odp_cpu_cycles_diff(c2, c1); + + odp_barrier_wait(&globals->barrier); + clear_sched_queues(); + + cycles = cycles / tot; + + printf(" [%i] %s enq+deq %6" PRIu64 " CPU cycles\n", thr, str, cycles); + + return 0; +} + +/** + * @internal Test scheduling of multiple queues with multi_sched and multi_enq + * + * @param str Test case name string + * @param thr Thread + * @param prio Priority + * @param globals Test shared data + * + * @return 0 if successful + */ +static int test_schedule_multi(const char *str, int thr, + int prio, test_globals_t *globals) +{ + odp_event_t ev[MULTI_BUFS_MAX]; + odp_queue_t queue; + uint64_t c1, c2, cycles; + int i; + int num; + uint32_t tot = 0; + + if (enqueue_events(thr, prio, QUEUES_PER_PRIO, MULTI_BUFS_MAX, globals)) + return -1; + + /* Start sched-enq loop */ + c1 = odp_cpu_cycles(); + + for (i = 0; i < QUEUE_ROUNDS; i++) { + num = odp_schedule_multi(&queue, ODP_SCHED_WAIT, ev, + MULTI_BUFS_MAX); + + tot += num; + + if (globals->args.fairness) { + queue_context_t *queue_ctx; + + queue_ctx = odp_queue_context(queue); + queue_ctx->num_ev += num; + } + + /* Assume we can enqueue all events */ + if (odp_queue_enq_multi(queue, ev, num) != num) { + LOG_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + /* Clear possible locally stored events */ + odp_schedule_pause(); + + while (1) { + num = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT, ev, + MULTI_BUFS_MAX); + + if (num == 0) + break; + + tot += num; + + if (globals->args.fairness) { + queue_context_t *queue_ctx; + + queue_ctx = odp_queue_context(queue); + queue_ctx->num_ev += num; + } + + /* Assume we can enqueue all events */ + if (odp_queue_enq_multi(queue, ev, num) != num) { + LOG_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + odp_schedule_resume(); + + c2 = odp_cpu_cycles(); + cycles = odp_cpu_cycles_diff(c2, c1); + + odp_barrier_wait(&globals->barrier); + clear_sched_queues(); + + if (tot) + cycles = cycles / tot; + else + cycles = 0; + + printf(" [%i] %s enq+deq %6" PRIu64 " CPU cycles\n", thr, str, cycles); + + odp_barrier_wait(&globals->barrier); + + if (globals->args.fairness && globals->first_thr == thr) + print_stats(prio, globals); + + return 0; +} + +/** + * @internal Worker thread + * + * @param arg Arguments + * + * @return non zero on failure + */ +static int run_thread(void *arg ODP_UNUSED) +{ + int thr; + odp_shm_t shm; + test_globals_t *globals; + odp_barrier_t *barrier; + + thr = odp_thread_id(); + + printf("Thread %i starts on CPU %i\n", thr, odp_cpu_id()); + + shm = odp_shm_lookup("test_globals"); + globals = odp_shm_addr(shm); + + if (globals == NULL) { + LOG_ERR("Shared mem lookup failed\n"); + return -1; + } + + barrier = &globals->barrier; + + /* + * Test barriers back-to-back + */ + odp_barrier_wait(barrier); + odp_barrier_wait(barrier); + odp_barrier_wait(barrier); + odp_barrier_wait(barrier); + odp_barrier_wait(barrier); + + /* Select which thread is the first_thr */ + while (globals->first_thr < 0) { + if (odp_spinlock_trylock(&globals->lock)) { + globals->first_thr = thr; + odp_spinlock_unlock(&globals->lock); + } + } + + odp_barrier_wait(barrier); + + if (test_alloc_single(thr, globals)) + return -1; + + odp_barrier_wait(barrier); + + if (test_alloc_multi(thr, globals)) + return -1; + + odp_barrier_wait(barrier); + + if (test_plain_queue(thr, globals)) + return -1; + + /* Low prio */ + + odp_barrier_wait(barrier); + + if (test_schedule_single("sched_____s_lo", thr, + ODP_SCHED_PRIO_LOWEST, globals)) + return -1; + + odp_barrier_wait(barrier); + + if (test_schedule_many("sched_____m_lo", thr, + ODP_SCHED_PRIO_LOWEST, globals)) + return -1; + + odp_barrier_wait(barrier); + + if (test_schedule_multi("sched_multi_lo", thr, + ODP_SCHED_PRIO_LOWEST, globals)) + return -1; + + /* High prio */ + + odp_barrier_wait(barrier); + + if (test_schedule_single("sched_____s_hi", thr, + ODP_SCHED_PRIO_HIGHEST, globals)) + return -1; + + odp_barrier_wait(barrier); + + if (test_schedule_many("sched_____m_hi", thr, + ODP_SCHED_PRIO_HIGHEST, globals)) + return -1; + + odp_barrier_wait(barrier); + + if (test_schedule_multi("sched_multi_hi", thr, + ODP_SCHED_PRIO_HIGHEST, globals)) + return -1; + + printf("Thread %i exits\n", thr); + fflush(NULL); + return 0; +} + +/** + * @internal Test cycle counter frequency + */ +static void test_cpu_freq(void) +{ + odp_time_t cur_time, test_time, start_time, end_time; + uint64_t c1, c2, cycles; + uint64_t nsec; + double diff_max_hz, max_cycles; + + printf("\nCPU cycle count frequency test (runs about %i sec)\n", + TEST_SEC); + + test_time = odp_time_local_from_ns(TEST_SEC * ODP_TIME_SEC_IN_NS); + start_time = odp_time_local(); + end_time = odp_time_sum(start_time, test_time); + + /* Start the measurement */ + c1 = odp_cpu_cycles(); + + do { + cur_time = odp_time_local(); + } while (odp_time_cmp(end_time, cur_time) > 0); + + c2 = odp_cpu_cycles(); + + test_time = odp_time_diff(cur_time, start_time); + nsec = odp_time_to_ns(test_time); + + cycles = odp_cpu_cycles_diff(c2, c1); + max_cycles = (nsec * odp_cpu_hz_max()) / 1000000000.0; + + /* Compare measured CPU cycles to maximum theoretical CPU cycle count */ + diff_max_hz = ((double)(cycles) - max_cycles) / max_cycles; + + printf("odp_time %" PRIu64 " ns\n", nsec); + printf("odp_cpu_cycles %" PRIu64 " CPU cycles\n", cycles); + printf("odp_sys_cpu_hz %" PRIu64 " hz\n", odp_cpu_hz_max()); + printf("Diff from max CPU freq %f%%\n", diff_max_hz * 100.0); + + printf("\n"); +} + +/** + * @internal Print help + */ +static void print_usage(void) +{ + printf("\n\nUsage: ./odp_example [options]\n"); + printf("Options:\n"); + printf(" -c, --count <number> CPU count\n"); + printf(" -h, --help this help\n"); + printf(" -f, --fair collect fairness statistics\n"); + printf("\n\n"); +} + +/** + * @internal Parse arguments + * + * @param argc Argument count + * @param argv Argument vector + * @param args Test arguments + */ +static void parse_args(int argc, char *argv[], test_args_t *args) +{ + int opt; + int long_index; + + static const struct option longopts[] = { + {"count", required_argument, NULL, 'c'}, + {"fair", no_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + static const char *shortopts = "+c:fh"; + + /* let helper collect its own arguments (e.g. --odph_proc) */ + odph_parse_options(argc, argv, shortopts, longopts); + + opterr = 0; /* do not issue errors on helper options */ + while (1) { + opt = getopt_long(argc, argv, shortopts, longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'f': + args->fairness = 1; + break; + + case 'c': + args->cpu_count = atoi(optarg); + break; + + case 'h': + print_usage(); + exit(EXIT_SUCCESS); + break; + + default: + break; + } + } +} + +/** + * Test main function + */ +int main(int argc, char *argv[]) +{ + odph_odpthread_t *thread_tbl; + test_args_t args; + int num_workers; + odp_cpumask_t cpumask; + odp_pool_t pool; + odp_queue_t plain_queue; + int i, j; + odp_shm_t shm; + test_globals_t *globals; + char cpumaskstr[ODP_CPUMASK_STR_SIZE]; + odp_pool_param_t params; + int ret = 0; + odp_instance_t instance; + odph_odpthread_params_t thr_params; + + printf("\nODP example starts\n\n"); + + memset(&args, 0, sizeof(args)); + parse_args(argc, argv, &args); + + /* ODP global init */ + if (odp_init_global(&instance, NULL, NULL)) { + LOG_ERR("ODP global init failed.\n"); + return -1; + } + + /* + * Init this thread. It makes also ODP calls when + * setting up resources for worker threads. + */ + if (odp_init_local(instance, ODP_THREAD_CONTROL)) { + LOG_ERR("ODP global init failed.\n"); + return -1; + } + + printf("\n"); + printf("ODP system info\n"); + printf("---------------\n"); + printf("ODP API version: %s\n", odp_version_api_str()); + printf("ODP impl name: %s\n", odp_version_impl_name()); + printf("ODP impl details: %s\n", odp_version_impl_str()); + printf("CPU model: %s\n", odp_cpu_model_str()); + printf("CPU freq (hz): %" PRIu64 "\n", odp_cpu_hz_max()); + printf("Cache line size: %i\n", odp_sys_cache_line_size()); + printf("Max CPU count: %i\n", odp_cpu_count()); + + printf("\n"); + + /* Get default worker cpumask */ + num_workers = odp_cpumask_default_worker(&cpumask, args.cpu_count); + (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr)); + + printf("num worker threads: %i\n", num_workers); + printf("first CPU: %i\n", odp_cpumask_first(&cpumask)); + printf("cpu mask: %s\n", cpumaskstr); + + thread_tbl = calloc(sizeof(odph_odpthread_t), num_workers); + if (!thread_tbl) { + LOG_ERR("no memory for thread_tbl\n"); + return -1; + } + + /* Test cycle count frequency */ + test_cpu_freq(); + + shm = odp_shm_reserve("test_globals", + sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0); + if (shm == ODP_SHM_INVALID) { + LOG_ERR("Shared memory reserve failed.\n"); + return -1; + } + + globals = odp_shm_addr(shm); + memset(globals, 0, sizeof(test_globals_t)); + memcpy(&globals->args, &args, sizeof(test_args_t)); + + /* + * Create message pool + */ + + odp_pool_param_init(¶ms); + params.buf.size = sizeof(test_message_t); + params.buf.align = 0; + params.buf.num = MSG_POOL_SIZE / sizeof(test_message_t); + params.type = ODP_POOL_BUFFER; + + pool = odp_pool_create("msg_pool", ¶ms); + + if (pool == ODP_POOL_INVALID) { + LOG_ERR("Pool create failed.\n"); + return -1; + } + + globals->pool = pool; + + /* odp_pool_print(pool); */ + + /* + * Create a queue for plain queue test + */ + plain_queue = odp_queue_create("plain_queue", NULL); + + if (plain_queue == ODP_QUEUE_INVALID) { + LOG_ERR("Plain queue create failed.\n"); + return -1; + } + + /* + * Create queues for schedule test. QUEUES_PER_PRIO per priority. + */ + for (i = 0; i < NUM_PRIOS; i++) { + char name[] = "sched_XX_YY"; + odp_queue_t queue; + odp_queue_param_t param; + int prio; + + if (i == 0) + prio = ODP_SCHED_PRIO_HIGHEST; + else + prio = ODP_SCHED_PRIO_LOWEST; + + name[6] = '0' + (prio / 10); + name[7] = '0' + prio - (10 * (prio / 10)); + + odp_queue_param_init(¶m); + param.type = ODP_QUEUE_TYPE_SCHED; + param.sched.prio = prio; + param.sched.sync = ODP_SCHED_SYNC_ATOMIC; + param.sched.group = ODP_SCHED_GROUP_ALL; + + for (j = 0; j < QUEUES_PER_PRIO; j++) { + name[9] = '0' + j / 10; + name[10] = '0' + j - 10 * (j / 10); + + queue = odp_queue_create(name, ¶m); + + if (queue == ODP_QUEUE_INVALID) { + LOG_ERR("Schedule queue create failed.\n"); + return -1; + } + + globals->queue[i][j] = queue; + + if (odp_queue_context_set(queue, + &globals->queue_ctx[i][j], + sizeof(queue_context_t)) + < 0) { + LOG_ERR("Queue context set failed.\n"); + return -1; + } + } + } + + odp_shm_print_all(); + + /* Barrier to sync test case execution */ + odp_barrier_init(&globals->barrier, num_workers); + + odp_spinlock_init(&globals->lock); + globals->first_thr = -1; + + /* Create and launch worker threads */ + memset(&thr_params, 0, sizeof(thr_params)); + thr_params.thr_type = ODP_THREAD_WORKER; + thr_params.instance = instance; + thr_params.start = run_thread; + thr_params.arg = NULL; + odph_odpthreads_create(thread_tbl, &cpumask, &thr_params); + + /* Wait for worker threads to terminate */ + odph_odpthreads_join(thread_tbl); + free(thread_tbl); + + printf("ODP example complete\n\n"); + + for (i = 0; i < NUM_PRIOS; i++) { + odp_queue_t queue; + + for (j = 0; j < QUEUES_PER_PRIO; j++) { + queue = globals->queue[i][j]; + odp_queue_destroy(queue); + } + } + + odp_shm_free(shm); + odp_queue_destroy(plain_queue); + odp_pool_destroy(pool); + odp_term_local(); + odp_term_global(instance); + + return ret; +} diff --git a/test/common_plat/performance/odp_scheduling_run.sh b/test/common_plat/performance/odp_scheduling_run.sh new file mode 100755 index 000000000..755b0c1f7 --- /dev/null +++ b/test/common_plat/performance/odp_scheduling_run.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2015, Linaro Limited +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# Script that passes command line arguments to odp_scheduling test when +# launched by 'make check' + +TEST_DIR="${TEST_DIR:-$(dirname $0)}" +ret=0 + +run() +{ + echo odp_scheduling_run starts with $1 worker threads + echo =============================================== + + $TEST_DIR/odp_scheduling${EXEEXT} -c $1 || ret=1 +} + +run 1 +run 8 + +exit $ret diff --git a/test/common_plat/validation/Makefile.am b/test/common_plat/validation/Makefile.am new file mode 100644 index 000000000..5d525fba4 --- /dev/null +++ b/test/common_plat/validation/Makefile.am @@ -0,0 +1,3 @@ +if cunit_support + SUBDIRS = api +endif diff --git a/test/common_plat/validation/api/.gitignore b/test/common_plat/validation/api/.gitignore new file mode 100644 index 000000000..7e563b8b3 --- /dev/null +++ b/test/common_plat/validation/api/.gitignore @@ -0,0 +1,2 @@ +*.log +*.trs diff --git a/test/common_plat/validation/api/Makefile.am b/test/common_plat/validation/api/Makefile.am new file mode 100644 index 000000000..e2d30a673 --- /dev/null +++ b/test/common_plat/validation/api/Makefile.am @@ -0,0 +1,28 @@ +ODP_MODULES = atomic \ + barrier \ + buffer \ + classification \ + cpumask \ + crypto \ + errno \ + hash \ + init \ + lock \ + queue \ + packet \ + pktio \ + pool \ + random \ + scheduler \ + std_clib \ + thread \ + time \ + timer \ + traffic_mngr \ + shmem \ + system + +SUBDIRS = $(ODP_MODULES) + +#The tests will need to retain the deprecated test implementation +AM_CFLAGS += -Wno-deprecated-declarations diff --git a/test/common_plat/validation/api/Makefile.inc b/test/common_plat/validation/api/Makefile.inc new file mode 100644 index 000000000..ffba62013 --- /dev/null +++ b/test/common_plat/validation/api/Makefile.inc @@ -0,0 +1,16 @@ +include $(top_srcdir)/test/Makefile.inc + +COMMON_DIR = $(top_builddir)/test/common_plat/common + +#the following option ensure that option '-I.' is not passed to gcc, +#therefore distinguishing between '#include "X"' and '#include <X>'. +#It allows common filenames (such as 'errno.h') to be used locally. +AUTOMAKE_OPTIONS = nostdinc + +AM_CFLAGS += -I$(top_srcdir)/test/common_plat/common +AM_LDFLAGS += -static + +LIBCUNIT_COMMON = $(COMMON_DIR)/libcunit_common.la +LIBCPUMASK_COMMON = $(COMMON_DIR)/libcpumask_common.la +LIBTHRMASK_COMMON = $(COMMON_DIR)/libthrmask_common.la +LIBODP = $(LIB)/libodphelper-linux.la $(LIB)/libodp-linux.la diff --git a/test/common_plat/validation/api/README b/test/common_plat/validation/api/README new file mode 100644 index 000000000..1baebaafc --- /dev/null +++ b/test/common_plat/validation/api/README @@ -0,0 +1,35 @@ +Copyright (c) 2015, Linaro Limited +All rights reserved. + +SPDX-License-Identifier: BSD-3-Clause + + +To add tests in here, please observe the rules listed below. This list +is a brief overview, for a more detailed explanation of the test +framework refer to the ODP Implementers' Guide, which can built as +follows: + + ./configure --enable-user-guides + make + +Output will be in doc/output/. If this fails, check the documentation +section of the DEPENDENCIES file. + +Rules for all tests under this tree: + +1. Tests must be placed in the directory of the module they belong to. + +2. Tests must be platform agnostic, i.e. + + - should be written in plain C only. + - may only use C standard library functions, CUnit functions and of + course ODP functions + - should be expected to pass on all ODP implementations + + Tests that do not follow these rules should be placed in the platform + specific test area (currently platform/<platform>/test/). + +3. If a new ODP API module is created, please update the Makefile.am. + +4. Symbols exported from test libraries must respect the naming + convention detailed in the ODP Implementers' Guide. diff --git a/test/common_plat/validation/api/atomic/.gitignore b/test/common_plat/validation/api/atomic/.gitignore new file mode 100644 index 000000000..610ffeab0 --- /dev/null +++ b/test/common_plat/validation/api/atomic/.gitignore @@ -0,0 +1 @@ +atomic_main diff --git a/test/common_plat/validation/api/atomic/Makefile.am b/test/common_plat/validation/api/atomic/Makefile.am new file mode 100644 index 000000000..9b6bd6315 --- /dev/null +++ b/test/common_plat/validation/api/atomic/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestatomic.la +libtestatomic_la_SOURCES = atomic.c + +test_PROGRAMS = atomic_main$(EXEEXT) +dist_atomic_main_SOURCES = atomic_main.c +atomic_main_LDADD = libtestatomic.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = atomic.h diff --git a/test/common_plat/validation/api/atomic/atomic.c b/test/common_plat/validation/api/atomic/atomic.c new file mode 100644 index 000000000..c4e934525 --- /dev/null +++ b/test/common_plat/validation/api/atomic/atomic.c @@ -0,0 +1,885 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <malloc.h> +#include <odp_api.h> +#include <CUnit/Basic.h> +#include <odp_cunit_common.h> +#include <unistd.h> +#include "atomic.h" + +#define VERBOSE 0 +#define MAX_ITERATIONS 1000 + +#define ADD_SUB_CNT 5 + +#define CNT 10 +#define U32_INIT_VAL (1UL << 10) +#define U64_INIT_VAL (1ULL << 33) +#define U32_MAGIC 0xa23f65b2 +#define U64_MAGIC 0xf2e1c5430cb6a52e + +#define GLOBAL_SHM_NAME "GlobalLockTest" + +#define UNUSED __attribute__((__unused__)) + +#define CHECK_MAX_MIN (1 << 0) +#define CHECK_XCHG (1 << 2) + +static odp_atomic_u32_t a32u; +static odp_atomic_u64_t a64u; +static odp_atomic_u32_t a32u_min; +static odp_atomic_u32_t a32u_max; +static odp_atomic_u64_t a64u_min; +static odp_atomic_u64_t a64u_max; +static odp_atomic_u32_t a32u_xchg; +static odp_atomic_u64_t a64u_xchg; + +typedef __volatile uint32_t volatile_u32_t; +typedef __volatile uint64_t volatile_u64_t; + +typedef struct { + /* Global variables */ + uint32_t g_num_threads; + uint32_t g_iterations; + uint32_t g_verbose; + uint32_t g_max_num_cores; + + volatile_u32_t global_lock_owner; +} global_shared_mem_t; + +/* Per-thread memory */ +typedef struct { + global_shared_mem_t *global_mem; + + int thread_id; + int thread_core; + + volatile_u64_t delay_counter; +} per_thread_mem_t; + +static odp_shm_t global_shm; +static global_shared_mem_t *global_mem; + +/* Initialise per-thread memory */ +static per_thread_mem_t *thread_init(void) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + odp_shm_t global_shm; + uint32_t per_thread_mem_len; + + per_thread_mem_len = sizeof(per_thread_mem_t); + per_thread_mem = malloc(per_thread_mem_len); + memset(per_thread_mem, 0, per_thread_mem_len); + + per_thread_mem->delay_counter = 1; + + per_thread_mem->thread_id = odp_thread_id(); + per_thread_mem->thread_core = odp_cpu_id(); + + global_shm = odp_shm_lookup(GLOBAL_SHM_NAME); + global_mem = odp_shm_addr(global_shm); + CU_ASSERT_PTR_NOT_NULL(global_mem); + + per_thread_mem->global_mem = global_mem; + + return per_thread_mem; +} + +static void thread_finalize(per_thread_mem_t *per_thread_mem) +{ + free(per_thread_mem); +} + +static void test_atomic_inc_32(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_inc_u32(&a32u); +} + +static void test_atomic_inc_64(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_inc_u64(&a64u); +} + +static void test_atomic_dec_32(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_dec_u32(&a32u); +} + +static void test_atomic_dec_64(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_dec_u64(&a64u); +} + +static void test_atomic_fetch_inc_32(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_fetch_inc_u32(&a32u); +} + +static void test_atomic_fetch_inc_64(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_fetch_inc_u64(&a64u); +} + +static void test_atomic_fetch_dec_32(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_fetch_dec_u32(&a32u); +} + +static void test_atomic_fetch_dec_64(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_fetch_dec_u64(&a64u); +} + +static void test_atomic_add_32(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_add_u32(&a32u, ADD_SUB_CNT); +} + +static void test_atomic_add_64(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_add_u64(&a64u, ADD_SUB_CNT); +} + +static void test_atomic_sub_32(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_sub_u32(&a32u, ADD_SUB_CNT); +} + +static void test_atomic_sub_64(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_sub_u64(&a64u, ADD_SUB_CNT); +} + +static void test_atomic_fetch_add_32(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_fetch_add_u32(&a32u, ADD_SUB_CNT); +} + +static void test_atomic_fetch_add_64(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_fetch_add_u64(&a64u, ADD_SUB_CNT); +} + +static void test_atomic_fetch_sub_32(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_fetch_sub_u32(&a32u, ADD_SUB_CNT); +} + +static void test_atomic_fetch_sub_64(void) +{ + int i; + + for (i = 0; i < CNT; i++) + odp_atomic_fetch_sub_u64(&a64u, ADD_SUB_CNT); +} + +static void test_atomic_min_32(void) +{ + int i; + uint32_t tmp; + + for (i = 0; i < CNT; i++) { + tmp = odp_atomic_fetch_dec_u32(&a32u); + odp_atomic_min_u32(&a32u_min, tmp); + } +} + +static void test_atomic_min_64(void) +{ + int i; + uint64_t tmp; + + for (i = 0; i < CNT; i++) { + tmp = odp_atomic_fetch_dec_u64(&a64u); + odp_atomic_min_u64(&a64u_min, tmp); + } +} + +static void test_atomic_max_32(void) +{ + int i; + uint32_t tmp; + + for (i = 0; i < CNT; i++) { + tmp = odp_atomic_fetch_inc_u32(&a32u); + odp_atomic_max_u32(&a32u_max, tmp); + } +} + +static void test_atomic_max_64(void) +{ + int i; + uint64_t tmp; + + for (i = 0; i < CNT; i++) { + tmp = odp_atomic_fetch_inc_u64(&a64u); + odp_atomic_max_u64(&a64u_max, tmp); + } +} + +static void test_atomic_cas_inc_32(void) +{ + int i; + uint32_t old; + + for (i = 0; i < CNT; i++) { + old = odp_atomic_load_u32(&a32u); + + while (odp_atomic_cas_u32(&a32u, &old, old + 1) == 0) + ; + } +} + +static void test_atomic_cas_dec_32(void) +{ + int i; + uint32_t old; + + for (i = 0; i < CNT; i++) { + old = odp_atomic_load_u32(&a32u); + + while (odp_atomic_cas_u32(&a32u, &old, old - 1) == 0) + ; + } +} + +static void test_atomic_cas_inc_64(void) +{ + int i; + uint64_t old; + + for (i = 0; i < CNT; i++) { + old = odp_atomic_load_u64(&a64u); + + while (odp_atomic_cas_u64(&a64u, &old, old + 1) == 0) + ; + } +} + +static void test_atomic_cas_dec_64(void) +{ + int i; + uint64_t old; + + for (i = 0; i < CNT; i++) { + old = odp_atomic_load_u64(&a64u); + + while (odp_atomic_cas_u64(&a64u, &old, old - 1) == 0) + ; + } +} + +static void test_atomic_xchg_32(void) +{ + uint32_t old, new; + int i; + + for (i = 0; i < CNT; i++) { + new = odp_atomic_fetch_inc_u32(&a32u); + old = odp_atomic_xchg_u32(&a32u_xchg, new); + + if (old & 0x1) + odp_atomic_xchg_u32(&a32u_xchg, 0); + else + odp_atomic_xchg_u32(&a32u_xchg, 1); + } + + odp_atomic_sub_u32(&a32u, CNT); + odp_atomic_xchg_u32(&a32u_xchg, U32_MAGIC); +} + +static void test_atomic_xchg_64(void) +{ + uint64_t old, new; + int i; + + for (i = 0; i < CNT; i++) { + new = odp_atomic_fetch_inc_u64(&a64u); + old = odp_atomic_xchg_u64(&a64u_xchg, new); + + if (old & 0x1) + odp_atomic_xchg_u64(&a64u_xchg, 0); + else + odp_atomic_xchg_u64(&a64u_xchg, 1); + } + + odp_atomic_sub_u64(&a64u, CNT); + odp_atomic_xchg_u64(&a64u_xchg, U64_MAGIC); +} + +static void test_atomic_non_relaxed_32(void) +{ + int i; + uint32_t tmp; + + for (i = 0; i < CNT; i++) { + tmp = odp_atomic_load_acq_u32(&a32u); + odp_atomic_store_rel_u32(&a32u, tmp); + + tmp = odp_atomic_load_acq_u32(&a32u_max); + odp_atomic_add_rel_u32(&a32u_max, 1); + + tmp = odp_atomic_load_acq_u32(&a32u_min); + odp_atomic_sub_rel_u32(&a32u_min, 1); + + tmp = odp_atomic_load_u32(&a32u_xchg); + while (odp_atomic_cas_acq_u32(&a32u_xchg, &tmp, tmp + 1) == 0) + ; + + tmp = odp_atomic_load_u32(&a32u_xchg); + while (odp_atomic_cas_rel_u32(&a32u_xchg, &tmp, tmp + 1) == 0) + ; + + tmp = odp_atomic_load_u32(&a32u_xchg); + /* finally set value for validation */ + while (odp_atomic_cas_acq_rel_u32(&a32u_xchg, &tmp, U32_MAGIC) + == 0) + ; + } +} + +static void test_atomic_non_relaxed_64(void) +{ + int i; + uint64_t tmp; + + for (i = 0; i < CNT; i++) { + tmp = odp_atomic_load_acq_u64(&a64u); + odp_atomic_store_rel_u64(&a64u, tmp); + + tmp = odp_atomic_load_acq_u64(&a64u_max); + odp_atomic_add_rel_u64(&a64u_max, 1); + + tmp = odp_atomic_load_acq_u64(&a64u_min); + odp_atomic_sub_rel_u64(&a64u_min, 1); + + tmp = odp_atomic_load_u64(&a64u_xchg); + while (odp_atomic_cas_acq_u64(&a64u_xchg, &tmp, tmp + 1) == 0) + ; + + tmp = odp_atomic_load_u64(&a64u_xchg); + while (odp_atomic_cas_rel_u64(&a64u_xchg, &tmp, tmp + 1) == 0) + ; + + tmp = odp_atomic_load_u64(&a64u_xchg); + /* finally set value for validation */ + while (odp_atomic_cas_acq_rel_u64(&a64u_xchg, &tmp, U64_MAGIC) + == 0) + ; + } +} + +static void test_atomic_inc_dec_32(void) +{ + test_atomic_inc_32(); + test_atomic_dec_32(); +} + +static void test_atomic_inc_dec_64(void) +{ + test_atomic_inc_64(); + test_atomic_dec_64(); +} + +static void test_atomic_fetch_inc_dec_32(void) +{ + test_atomic_fetch_inc_32(); + test_atomic_fetch_dec_32(); +} + +static void test_atomic_fetch_inc_dec_64(void) +{ + test_atomic_fetch_inc_64(); + test_atomic_fetch_dec_64(); +} + +static void test_atomic_add_sub_32(void) +{ + test_atomic_add_32(); + test_atomic_sub_32(); +} + +static void test_atomic_add_sub_64(void) +{ + test_atomic_add_64(); + test_atomic_sub_64(); +} + +static void test_atomic_fetch_add_sub_32(void) +{ + test_atomic_fetch_add_32(); + test_atomic_fetch_sub_32(); +} + +static void test_atomic_fetch_add_sub_64(void) +{ + test_atomic_fetch_add_64(); + test_atomic_fetch_sub_64(); +} + +static void test_atomic_max_min_32(void) +{ + test_atomic_max_32(); + test_atomic_min_32(); +} + +static void test_atomic_max_min_64(void) +{ + test_atomic_max_64(); + test_atomic_min_64(); +} + +static void test_atomic_cas_inc_dec_32(void) +{ + test_atomic_cas_inc_32(); + test_atomic_cas_dec_32(); +} + +static void test_atomic_cas_inc_dec_64(void) +{ + test_atomic_cas_inc_64(); + test_atomic_cas_dec_64(); +} + +static void test_atomic_init(void) +{ + odp_atomic_init_u32(&a32u, 0); + odp_atomic_init_u64(&a64u, 0); + odp_atomic_init_u32(&a32u_min, 0); + odp_atomic_init_u32(&a32u_max, 0); + odp_atomic_init_u64(&a64u_min, 0); + odp_atomic_init_u64(&a64u_max, 0); + odp_atomic_init_u32(&a32u_xchg, 0); + odp_atomic_init_u64(&a64u_xchg, 0); +} + +static void test_atomic_store(void) +{ + odp_atomic_store_u32(&a32u, U32_INIT_VAL); + odp_atomic_store_u64(&a64u, U64_INIT_VAL); + odp_atomic_store_u32(&a32u_min, U32_INIT_VAL); + odp_atomic_store_u32(&a32u_max, U32_INIT_VAL); + odp_atomic_store_u64(&a64u_min, U64_INIT_VAL); + odp_atomic_store_u64(&a64u_max, U64_INIT_VAL); + odp_atomic_store_u32(&a32u_xchg, U32_INIT_VAL); + odp_atomic_store_u64(&a64u_xchg, U64_INIT_VAL); +} + +static void test_atomic_validate(int check) +{ + CU_ASSERT(U32_INIT_VAL == odp_atomic_load_u32(&a32u)); + CU_ASSERT(U64_INIT_VAL == odp_atomic_load_u64(&a64u)); + + if (check & CHECK_MAX_MIN) { + CU_ASSERT(odp_atomic_load_u32(&a32u_max) > + odp_atomic_load_u32(&a32u_min)); + + CU_ASSERT(odp_atomic_load_u64(&a64u_max) > + odp_atomic_load_u64(&a64u_min)); + } + + if (check & CHECK_XCHG) { + CU_ASSERT(odp_atomic_load_u32(&a32u_xchg) == U32_MAGIC); + CU_ASSERT(odp_atomic_load_u64(&a64u_xchg) == U64_MAGIC); + } +} + +int atomic_init(odp_instance_t *inst) +{ + uint32_t workers_count, max_threads; + int ret = 0; + odp_cpumask_t mask; + + if (0 != odp_init_global(inst, NULL, NULL)) { + fprintf(stderr, "error: odp_init_global() failed.\n"); + return -1; + } + if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) { + fprintf(stderr, "error: odp_init_local() failed.\n"); + return -1; + } + + global_shm = odp_shm_reserve(GLOBAL_SHM_NAME, + sizeof(global_shared_mem_t), 64, + ODP_SHM_SW_ONLY); + if (ODP_SHM_INVALID == global_shm) { + fprintf(stderr, "Unable reserve memory for global_shm\n"); + return -1; + } + + global_mem = odp_shm_addr(global_shm); + memset(global_mem, 0, sizeof(global_shared_mem_t)); + + global_mem->g_num_threads = MAX_WORKERS; + global_mem->g_iterations = MAX_ITERATIONS; + global_mem->g_verbose = VERBOSE; + + workers_count = odp_cpumask_default_worker(&mask, 0); + + max_threads = (workers_count >= MAX_WORKERS) ? + MAX_WORKERS : workers_count; + + if (max_threads < global_mem->g_num_threads) { + printf("Requested num of threads is too large\n"); + printf("reducing from %" PRIu32 " to %" PRIu32 "\n", + global_mem->g_num_threads, + max_threads); + global_mem->g_num_threads = max_threads; + } + + printf("Num of threads used = %" PRIu32 "\n", + global_mem->g_num_threads); + + return ret; +} + +/* Atomic tests */ +static int test_atomic_inc_dec_thread(void *arg UNUSED) +{ + per_thread_mem_t *per_thread_mem; + + per_thread_mem = thread_init(); + test_atomic_inc_dec_32(); + test_atomic_inc_dec_64(); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int test_atomic_add_sub_thread(void *arg UNUSED) +{ + per_thread_mem_t *per_thread_mem; + + per_thread_mem = thread_init(); + test_atomic_add_sub_32(); + test_atomic_add_sub_64(); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int test_atomic_fetch_inc_dec_thread(void *arg UNUSED) +{ + per_thread_mem_t *per_thread_mem; + + per_thread_mem = thread_init(); + test_atomic_fetch_inc_dec_32(); + test_atomic_fetch_inc_dec_64(); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int test_atomic_fetch_add_sub_thread(void *arg UNUSED) +{ + per_thread_mem_t *per_thread_mem; + + per_thread_mem = thread_init(); + test_atomic_fetch_add_sub_32(); + test_atomic_fetch_add_sub_64(); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int test_atomic_max_min_thread(void *arg UNUSED) +{ + per_thread_mem_t *per_thread_mem; + + per_thread_mem = thread_init(); + test_atomic_max_min_32(); + test_atomic_max_min_64(); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int test_atomic_cas_inc_dec_thread(void *arg UNUSED) +{ + per_thread_mem_t *per_thread_mem; + + per_thread_mem = thread_init(); + test_atomic_cas_inc_dec_32(); + test_atomic_cas_inc_dec_64(); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int test_atomic_xchg_thread(void *arg UNUSED) +{ + per_thread_mem_t *per_thread_mem; + + per_thread_mem = thread_init(); + test_atomic_xchg_32(); + test_atomic_xchg_64(); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int test_atomic_non_relaxed_thread(void *arg UNUSED) +{ + per_thread_mem_t *per_thread_mem; + + per_thread_mem = thread_init(); + test_atomic_non_relaxed_32(); + test_atomic_non_relaxed_64(); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static void test_atomic_functional(int func_ptr(void *), int check) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + test_atomic_init(); + test_atomic_store(); + odp_cunit_thread_create(func_ptr, &arg); + odp_cunit_thread_exit(&arg); + test_atomic_validate(check); +} + +void atomic_test_atomic_inc_dec(void) +{ + test_atomic_functional(test_atomic_inc_dec_thread, 0); +} + +void atomic_test_atomic_add_sub(void) +{ + test_atomic_functional(test_atomic_add_sub_thread, 0); +} + +void atomic_test_atomic_fetch_inc_dec(void) +{ + test_atomic_functional(test_atomic_fetch_inc_dec_thread, 0); +} + +void atomic_test_atomic_fetch_add_sub(void) +{ + test_atomic_functional(test_atomic_fetch_add_sub_thread, 0); +} + +void atomic_test_atomic_max_min(void) +{ + test_atomic_functional(test_atomic_max_min_thread, CHECK_MAX_MIN); +} + +void atomic_test_atomic_cas_inc_dec(void) +{ + test_atomic_functional(test_atomic_cas_inc_dec_thread, 0); +} + +void atomic_test_atomic_xchg(void) +{ + test_atomic_functional(test_atomic_xchg_thread, CHECK_XCHG); +} + +void atomic_test_atomic_non_relaxed(void) +{ + test_atomic_functional(test_atomic_non_relaxed_thread, + CHECK_MAX_MIN | CHECK_XCHG); +} + +void atomic_test_atomic_op_lock_free(void) +{ + odp_atomic_op_t atomic_op; + int ret_null, ret; + + memset(&atomic_op, 0xff, sizeof(odp_atomic_op_t)); + atomic_op.all_bits = 0; + + CU_ASSERT(atomic_op.all_bits == 0); + CU_ASSERT(atomic_op.op.init == 0); + CU_ASSERT(atomic_op.op.load == 0); + CU_ASSERT(atomic_op.op.store == 0); + CU_ASSERT(atomic_op.op.fetch_add == 0); + CU_ASSERT(atomic_op.op.add == 0); + CU_ASSERT(atomic_op.op.fetch_sub == 0); + CU_ASSERT(atomic_op.op.sub == 0); + CU_ASSERT(atomic_op.op.fetch_inc == 0); + CU_ASSERT(atomic_op.op.inc == 0); + CU_ASSERT(atomic_op.op.fetch_dec == 0); + CU_ASSERT(atomic_op.op.dec == 0); + CU_ASSERT(atomic_op.op.min == 0); + CU_ASSERT(atomic_op.op.max == 0); + CU_ASSERT(atomic_op.op.cas == 0); + CU_ASSERT(atomic_op.op.xchg == 0); + + /* Test setting first, last and couple of other bits */ + atomic_op.op.init = 1; + CU_ASSERT(atomic_op.op.init == 1); + CU_ASSERT(atomic_op.all_bits != 0); + atomic_op.op.init = 0; + CU_ASSERT(atomic_op.all_bits == 0); + + atomic_op.op.xchg = 1; + CU_ASSERT(atomic_op.op.xchg == 1); + CU_ASSERT(atomic_op.all_bits != 0); + atomic_op.op.xchg = 0; + CU_ASSERT(atomic_op.all_bits == 0); + + atomic_op.op.add = 1; + CU_ASSERT(atomic_op.op.add == 1); + CU_ASSERT(atomic_op.all_bits != 0); + atomic_op.op.add = 0; + CU_ASSERT(atomic_op.all_bits == 0); + + atomic_op.op.dec = 1; + CU_ASSERT(atomic_op.op.dec == 1); + CU_ASSERT(atomic_op.all_bits != 0); + atomic_op.op.dec = 0; + CU_ASSERT(atomic_op.all_bits == 0); + + memset(&atomic_op, 0xff, sizeof(odp_atomic_op_t)); + ret = odp_atomic_lock_free_u64(&atomic_op); + ret_null = odp_atomic_lock_free_u64(NULL); + + CU_ASSERT(ret == ret_null); + + /* Init operation is not atomic by the spec. Call to + * odp_atomic_lock_free_u64() zeros it but never sets it. */ + + if (ret == 0) { + /* none are lock free */ + CU_ASSERT(atomic_op.all_bits == 0); + CU_ASSERT(atomic_op.op.init == 0); + CU_ASSERT(atomic_op.op.load == 0); + CU_ASSERT(atomic_op.op.store == 0); + CU_ASSERT(atomic_op.op.fetch_add == 0); + CU_ASSERT(atomic_op.op.add == 0); + CU_ASSERT(atomic_op.op.fetch_sub == 0); + CU_ASSERT(atomic_op.op.sub == 0); + CU_ASSERT(atomic_op.op.fetch_inc == 0); + CU_ASSERT(atomic_op.op.inc == 0); + CU_ASSERT(atomic_op.op.fetch_dec == 0); + CU_ASSERT(atomic_op.op.dec == 0); + CU_ASSERT(atomic_op.op.min == 0); + CU_ASSERT(atomic_op.op.max == 0); + CU_ASSERT(atomic_op.op.cas == 0); + CU_ASSERT(atomic_op.op.xchg == 0); + } + + if (ret == 1) { + /* some are lock free */ + CU_ASSERT(atomic_op.all_bits != 0); + CU_ASSERT(atomic_op.op.init == 0); + } + + if (ret == 2) { + /* all are lock free */ + CU_ASSERT(atomic_op.all_bits != 0); + CU_ASSERT(atomic_op.op.init == 0); + CU_ASSERT(atomic_op.op.load == 1); + CU_ASSERT(atomic_op.op.store == 1); + CU_ASSERT(atomic_op.op.fetch_add == 1); + CU_ASSERT(atomic_op.op.add == 1); + CU_ASSERT(atomic_op.op.fetch_sub == 1); + CU_ASSERT(atomic_op.op.sub == 1); + CU_ASSERT(atomic_op.op.fetch_inc == 1); + CU_ASSERT(atomic_op.op.inc == 1); + CU_ASSERT(atomic_op.op.fetch_dec == 1); + CU_ASSERT(atomic_op.op.dec == 1); + CU_ASSERT(atomic_op.op.min == 1); + CU_ASSERT(atomic_op.op.max == 1); + CU_ASSERT(atomic_op.op.cas == 1); + CU_ASSERT(atomic_op.op.xchg == 1); + } +} + +odp_testinfo_t atomic_suite_atomic[] = { + ODP_TEST_INFO(atomic_test_atomic_inc_dec), + ODP_TEST_INFO(atomic_test_atomic_add_sub), + ODP_TEST_INFO(atomic_test_atomic_fetch_inc_dec), + ODP_TEST_INFO(atomic_test_atomic_fetch_add_sub), + ODP_TEST_INFO(atomic_test_atomic_max_min), + ODP_TEST_INFO(atomic_test_atomic_cas_inc_dec), + ODP_TEST_INFO(atomic_test_atomic_xchg), + ODP_TEST_INFO(atomic_test_atomic_non_relaxed), + ODP_TEST_INFO(atomic_test_atomic_op_lock_free), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t atomic_suites[] = { + {"atomic", NULL, NULL, + atomic_suite_atomic}, + ODP_SUITE_INFO_NULL +}; + +int atomic_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + odp_cunit_register_global_init(atomic_init); + + ret = odp_cunit_register(atomic_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/atomic/atomic.h b/test/common_plat/validation/api/atomic/atomic.h new file mode 100644 index 000000000..4ea837b7a --- /dev/null +++ b/test/common_plat/validation/api/atomic/atomic.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_ATOMIC_H_ +#define _ODP_TEST_ATOMIC_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void atomic_test_atomic_inc_dec(void); +void atomic_test_atomic_add_sub(void); +void atomic_test_atomic_fetch_inc_dec(void); +void atomic_test_atomic_fetch_add_sub(void); +void atomic_test_atomic_max_min(void); +void atomic_test_atomic_cas_inc_dec(void); +void atomic_test_atomic_xchg(void); +void atomic_test_atomic_non_relaxed(void); +void atomic_test_atomic_op_lock_free(void); + +/* test arrays: */ +extern odp_testinfo_t atomic_suite_atomic[]; + +/* test array init/term functions: */ +int atomic_suite_init(void); + +/* test registry: */ +extern odp_suiteinfo_t atomic_suites[]; + +/* executable init/term functions: */ +int atomic_init(odp_instance_t *inst); + +/* main test program: */ +int atomic_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/atomic/atomic_main.c b/test/common_plat/validation/api/atomic/atomic_main.c new file mode 100644 index 000000000..db035373e --- /dev/null +++ b/test/common_plat/validation/api/atomic/atomic_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "atomic.h" + +int main(int argc, char *argv[]) +{ + return atomic_main(argc, argv); +} diff --git a/test/common_plat/validation/api/barrier/.gitignore b/test/common_plat/validation/api/barrier/.gitignore new file mode 100644 index 000000000..2e0ee7ade --- /dev/null +++ b/test/common_plat/validation/api/barrier/.gitignore @@ -0,0 +1 @@ +barrier_main diff --git a/test/common_plat/validation/api/barrier/Makefile.am b/test/common_plat/validation/api/barrier/Makefile.am new file mode 100644 index 000000000..8fc632c27 --- /dev/null +++ b/test/common_plat/validation/api/barrier/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestbarrier.la +libtestbarrier_la_SOURCES = barrier.c + +test_PROGRAMS = barrier_main$(EXEEXT) +dist_barrier_main_SOURCES = barrier_main.c +barrier_main_LDADD = libtestbarrier.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = barrier.h diff --git a/test/common_plat/validation/api/barrier/barrier.c b/test/common_plat/validation/api/barrier/barrier.c new file mode 100644 index 000000000..d4583884a --- /dev/null +++ b/test/common_plat/validation/api/barrier/barrier.c @@ -0,0 +1,397 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <malloc.h> +#include <odp_api.h> +#include <CUnit/Basic.h> +#include <odp_cunit_common.h> +#include <unistd.h> +#include "barrier.h" + +#define VERBOSE 0 +#define MAX_ITERATIONS 1000 +#define BARRIER_ITERATIONS 64 + +#define SLOW_BARRIER_DELAY 400 +#define BASE_DELAY 6 + +#define NUM_TEST_BARRIERS BARRIER_ITERATIONS +#define NUM_RESYNC_BARRIERS 100 + +#define BARRIER_DELAY 10 + +#define GLOBAL_SHM_NAME "GlobalLockTest" + +#define UNUSED __attribute__((__unused__)) + +static volatile int temp_result; + +typedef __volatile uint32_t volatile_u32_t; +typedef __volatile uint64_t volatile_u64_t; + +typedef struct { + odp_atomic_u32_t wait_cnt; +} custom_barrier_t; + +typedef struct { + /* Global variables */ + uint32_t g_num_threads; + uint32_t g_iterations; + uint32_t g_verbose; + uint32_t g_max_num_cores; + + odp_barrier_t test_barriers[NUM_TEST_BARRIERS]; + custom_barrier_t custom_barrier1[NUM_TEST_BARRIERS]; + custom_barrier_t custom_barrier2[NUM_TEST_BARRIERS]; + volatile_u32_t slow_thread_num; + volatile_u32_t barrier_cnt1; + volatile_u32_t barrier_cnt2; + odp_barrier_t global_barrier; + +} global_shared_mem_t; + +/* Per-thread memory */ +typedef struct { + global_shared_mem_t *global_mem; + + int thread_id; + int thread_core; + + volatile_u64_t delay_counter; +} per_thread_mem_t; + +static odp_shm_t global_shm; +static global_shared_mem_t *global_mem; + +/* +* Delay a consistent amount of time. Ideally the amount of CPU time taken +* is linearly proportional to "iterations". The goal is to try to do some +* work that the compiler optimizer won't optimize away, and also to +* minimize loads and stores (at least to different memory addresses) +* so as to not affect or be affected by caching issues. This does NOT have to +* correlate to a specific number of cpu cycles or be consistent across +* CPU architectures. +*/ +static void thread_delay(per_thread_mem_t *per_thread_mem, uint32_t iterations) +{ + volatile_u64_t *counter_ptr; + uint32_t cnt; + + counter_ptr = &per_thread_mem->delay_counter; + + for (cnt = 1; cnt <= iterations; cnt++) + (*counter_ptr)++; +} + +/* Initialise per-thread memory */ +static per_thread_mem_t *thread_init(void) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + odp_shm_t global_shm; + uint32_t per_thread_mem_len; + + per_thread_mem_len = sizeof(per_thread_mem_t); + per_thread_mem = malloc(per_thread_mem_len); + memset(per_thread_mem, 0, per_thread_mem_len); + + per_thread_mem->delay_counter = 1; + + per_thread_mem->thread_id = odp_thread_id(); + per_thread_mem->thread_core = odp_cpu_id(); + + global_shm = odp_shm_lookup(GLOBAL_SHM_NAME); + global_mem = odp_shm_addr(global_shm); + CU_ASSERT_PTR_NOT_NULL(global_mem); + + per_thread_mem->global_mem = global_mem; + + return per_thread_mem; +} + +static void thread_finalize(per_thread_mem_t *per_thread_mem) +{ + free(per_thread_mem); +} + +static void custom_barrier_init(custom_barrier_t *custom_barrier, + uint32_t num_threads) +{ + odp_atomic_init_u32(&custom_barrier->wait_cnt, num_threads); +} + +static void custom_barrier_wait(custom_barrier_t *custom_barrier) +{ + volatile_u64_t counter = 1; + uint32_t delay_cnt, wait_cnt; + + odp_atomic_sub_u32(&custom_barrier->wait_cnt, 1); + + wait_cnt = 1; + while (wait_cnt != 0) { + for (delay_cnt = 1; delay_cnt <= BARRIER_DELAY; delay_cnt++) + counter++; + + wait_cnt = odp_atomic_load_u32(&custom_barrier->wait_cnt); + } +} + +static uint32_t barrier_test(per_thread_mem_t *per_thread_mem, + odp_bool_t no_barrier_test) +{ + global_shared_mem_t *global_mem; + uint32_t barrier_errs, iterations, cnt, i_am_slow_thread; + uint32_t thread_num, slow_thread_num, next_slow_thread, num_threads; + uint32_t lock_owner_delay, barrier_cnt1, barrier_cnt2; + + thread_num = odp_thread_id(); + global_mem = per_thread_mem->global_mem; + num_threads = global_mem->g_num_threads; + iterations = BARRIER_ITERATIONS; + + barrier_errs = 0; + lock_owner_delay = SLOW_BARRIER_DELAY; + + for (cnt = 1; cnt < iterations; cnt++) { + /* Wait here until all of the threads reach this point */ + custom_barrier_wait(&global_mem->custom_barrier1[cnt]); + + barrier_cnt1 = global_mem->barrier_cnt1; + barrier_cnt2 = global_mem->barrier_cnt2; + + if ((barrier_cnt1 != cnt) || (barrier_cnt2 != cnt)) { + printf("thread_num=%" PRIu32 " barrier_cnts of %" PRIu32 + " %" PRIu32 " cnt=%" PRIu32 "\n", + thread_num, barrier_cnt1, barrier_cnt2, cnt); + barrier_errs++; + } + + /* Wait here until all of the threads reach this point */ + custom_barrier_wait(&global_mem->custom_barrier2[cnt]); + + slow_thread_num = global_mem->slow_thread_num; + i_am_slow_thread = thread_num == slow_thread_num; + next_slow_thread = slow_thread_num + 1; + if (num_threads < next_slow_thread) + next_slow_thread = 1; + + /* + * Now run the test, which involves having all but one thread + * immediately calling odp_barrier_wait(), and one thread wait a + * moderate amount of time and then calling odp_barrier_wait(). + * The test fails if any of the first group of threads + * has not waited for the "slow" thread. The "slow" thread is + * responsible for re-initializing the barrier for next trial. + */ + if (i_am_slow_thread) { + thread_delay(per_thread_mem, lock_owner_delay); + lock_owner_delay += BASE_DELAY; + if ((global_mem->barrier_cnt1 != cnt) || + (global_mem->barrier_cnt2 != cnt) || + (global_mem->slow_thread_num + != slow_thread_num)) + barrier_errs++; + } + + if (no_barrier_test == 0) + odp_barrier_wait(&global_mem->test_barriers[cnt]); + + global_mem->barrier_cnt1 = cnt + 1; + odp_mb_full(); + + if (i_am_slow_thread) { + global_mem->slow_thread_num = next_slow_thread; + global_mem->barrier_cnt2 = cnt + 1; + odp_mb_full(); + } else { + while (global_mem->barrier_cnt2 != (cnt + 1)) + thread_delay(per_thread_mem, BASE_DELAY); + } + } + + if ((global_mem->g_verbose) && (barrier_errs != 0)) + printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32 + " barrier_errs in %" PRIu32 " iterations\n", thread_num, + per_thread_mem->thread_id, + per_thread_mem->thread_core, barrier_errs, iterations); + + return barrier_errs; +} + +static int no_barrier_functional_test(void *arg UNUSED) +{ + per_thread_mem_t *per_thread_mem; + uint32_t barrier_errs; + + per_thread_mem = thread_init(); + barrier_errs = barrier_test(per_thread_mem, 1); + + /* + * Note that the following CU_ASSERT MAY appear incorrect, but for the + * no_barrier test it should see barrier_errs or else there is something + * wrong with the test methodology or the ODP thread implementation. + * So this test PASSES only if it sees barrier_errs or a single + * worker was used. + */ + CU_ASSERT(barrier_errs != 0 || global_mem->g_num_threads == 1); + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int barrier_functional_test(void *arg UNUSED) +{ + per_thread_mem_t *per_thread_mem; + uint32_t barrier_errs; + + per_thread_mem = thread_init(); + barrier_errs = barrier_test(per_thread_mem, 0); + + CU_ASSERT(barrier_errs == 0); + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static void barrier_test_init(void) +{ + uint32_t num_threads, idx; + + num_threads = global_mem->g_num_threads; + + for (idx = 0; idx < NUM_TEST_BARRIERS; idx++) { + odp_barrier_init(&global_mem->test_barriers[idx], num_threads); + custom_barrier_init(&global_mem->custom_barrier1[idx], + num_threads); + custom_barrier_init(&global_mem->custom_barrier2[idx], + num_threads); + } + + global_mem->slow_thread_num = 1; + global_mem->barrier_cnt1 = 1; + global_mem->barrier_cnt2 = 1; +} + +/* Barrier tests */ +void barrier_test_memory_barrier(void) +{ + volatile int a = 0; + volatile int b = 0; + volatile int c = 0; + volatile int d = 0; + + /* Call all memory barriers to verify that those are implemented */ + a = 1; + odp_mb_release(); + b = 1; + odp_mb_acquire(); + c = 1; + odp_mb_full(); + d = 1; + + /* Avoid "variable set but not used" warning */ + temp_result = a + b + c + d; +} + +void barrier_test_no_barrier_functional(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + barrier_test_init(); + odp_cunit_thread_create(no_barrier_functional_test, &arg); + odp_cunit_thread_exit(&arg); +} + +void barrier_test_barrier_functional(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + barrier_test_init(); + odp_cunit_thread_create(barrier_functional_test, &arg); + odp_cunit_thread_exit(&arg); +} + +odp_testinfo_t barrier_suite_barrier[] = { + ODP_TEST_INFO(barrier_test_memory_barrier), + ODP_TEST_INFO(barrier_test_no_barrier_functional), + ODP_TEST_INFO(barrier_test_barrier_functional), + ODP_TEST_INFO_NULL +}; + +int barrier_init(odp_instance_t *inst) +{ + uint32_t workers_count, max_threads; + int ret = 0; + odp_cpumask_t mask; + + if (0 != odp_init_global(inst, NULL, NULL)) { + fprintf(stderr, "error: odp_init_global() failed.\n"); + return -1; + } + if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) { + fprintf(stderr, "error: odp_init_local() failed.\n"); + return -1; + } + + global_shm = odp_shm_reserve(GLOBAL_SHM_NAME, + sizeof(global_shared_mem_t), 64, + ODP_SHM_SW_ONLY); + if (ODP_SHM_INVALID == global_shm) { + fprintf(stderr, "Unable reserve memory for global_shm\n"); + return -1; + } + + global_mem = odp_shm_addr(global_shm); + memset(global_mem, 0, sizeof(global_shared_mem_t)); + + global_mem->g_num_threads = MAX_WORKERS; + global_mem->g_iterations = MAX_ITERATIONS; + global_mem->g_verbose = VERBOSE; + + workers_count = odp_cpumask_default_worker(&mask, 0); + + max_threads = (workers_count >= MAX_WORKERS) ? + MAX_WORKERS : workers_count; + + if (max_threads < global_mem->g_num_threads) { + printf("Requested num of threads is too large\n"); + printf("reducing from %" PRIu32 " to %" PRIu32 "\n", + global_mem->g_num_threads, + max_threads); + global_mem->g_num_threads = max_threads; + } + + printf("Num of threads used = %" PRIu32 "\n", + global_mem->g_num_threads); + + return ret; +} + +odp_suiteinfo_t barrier_suites[] = { + {"barrier", NULL, NULL, + barrier_suite_barrier}, + ODP_SUITE_INFO_NULL +}; + +int barrier_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + odp_cunit_register_global_init(barrier_init); + + ret = odp_cunit_register(barrier_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/barrier/barrier.h b/test/common_plat/validation/api/barrier/barrier.h new file mode 100644 index 000000000..e4890e0f4 --- /dev/null +++ b/test/common_plat/validation/api/barrier/barrier.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_BARRIER_H_ +#define _ODP_TEST_BARRIER_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void barrier_test_memory_barrier(void); +void barrier_test_no_barrier_functional(void); +void barrier_test_barrier_functional(void); + +/* test arrays: */ +extern odp_testinfo_t barrier_suite_barrier[]; + +/* test registry: */ +extern odp_suiteinfo_t barrier_suites[]; + +/* executable init/term functions: */ +int barrier_init(odp_instance_t *inst); + +/* main test program: */ +int barrier_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/barrier/barrier_main.c b/test/common_plat/validation/api/barrier/barrier_main.c new file mode 100644 index 000000000..064decf6c --- /dev/null +++ b/test/common_plat/validation/api/barrier/barrier_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "barrier.h" + +int main(int argc, char *argv[]) +{ + return barrier_main(argc, argv); +} diff --git a/test/common_plat/validation/api/buffer/.gitignore b/test/common_plat/validation/api/buffer/.gitignore new file mode 100644 index 000000000..0e8ac15c1 --- /dev/null +++ b/test/common_plat/validation/api/buffer/.gitignore @@ -0,0 +1 @@ +buffer_main diff --git a/test/common_plat/validation/api/buffer/Makefile.am b/test/common_plat/validation/api/buffer/Makefile.am new file mode 100644 index 000000000..add2a3419 --- /dev/null +++ b/test/common_plat/validation/api/buffer/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestbuffer.la +libtestbuffer_la_SOURCES = buffer.c + +test_PROGRAMS = buffer_main$(EXEEXT) +dist_buffer_main_SOURCES = buffer_main.c +buffer_main_LDADD = libtestbuffer.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = buffer.h diff --git a/test/common_plat/validation/api/buffer/buffer.c b/test/common_plat/validation/api/buffer/buffer.c new file mode 100644 index 000000000..d26d5e82e --- /dev/null +++ b/test/common_plat/validation/api/buffer/buffer.c @@ -0,0 +1,274 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include "odp_cunit_common.h" +#include "buffer.h" + +static odp_pool_t raw_pool; +static odp_buffer_t raw_buffer = ODP_BUFFER_INVALID; +static const size_t raw_buffer_size = 1500; + +int buffer_suite_init(void) +{ + odp_pool_param_t params = { + .buf = { + .size = raw_buffer_size, + .align = ODP_CACHE_LINE_SIZE, + .num = 100, + }, + .type = ODP_POOL_BUFFER, + }; + + raw_pool = odp_pool_create("raw_pool", ¶ms); + if (raw_pool == ODP_POOL_INVALID) + return -1; + raw_buffer = odp_buffer_alloc(raw_pool); + if (raw_buffer == ODP_BUFFER_INVALID) + return -1; + return 0; +} + +int buffer_suite_term(void) +{ + odp_buffer_free(raw_buffer); + if (odp_pool_destroy(raw_pool) != 0) + return -1; + return 0; +} + +void buffer_test_pool_alloc(void) +{ + odp_pool_t pool; + const int num = 3; + const size_t size = 1500; + odp_buffer_t buffer[num]; + odp_event_t ev; + int index; + char wrong_type = 0, wrong_size = 0; + odp_pool_param_t params = { + .buf = { + .size = size, + .align = ODP_CACHE_LINE_SIZE, + .num = num, + }, + .type = ODP_POOL_BUFFER, + }; + + pool = odp_pool_create("buffer_pool_alloc", ¶ms); + odp_pool_print(pool); + + /* Try to allocate num items from the pool */ + for (index = 0; index < num; index++) { + buffer[index] = odp_buffer_alloc(pool); + + if (buffer[index] == ODP_BUFFER_INVALID) + break; + + ev = odp_buffer_to_event(buffer[index]); + if (odp_event_type(ev) != ODP_EVENT_BUFFER) + wrong_type = 1; + if (odp_buffer_size(buffer[index]) < size) + wrong_size = 1; + if (wrong_type || wrong_size) + odp_buffer_print(buffer[index]); + } + + /* Check that the pool had at least num items */ + CU_ASSERT(index == num); + /* index points out of buffer[] or it point to an invalid buffer */ + index--; + + /* Check that the pool had correct buffers */ + CU_ASSERT(wrong_type == 0); + CU_ASSERT(wrong_size == 0); + + for (; index >= 0; index--) + odp_buffer_free(buffer[index]); + + CU_ASSERT(odp_pool_destroy(pool) == 0); +} + +/* Wrapper to call odp_buffer_alloc_multi multiple times until + * either no mure buffers are returned, or num buffers were alloced */ +static int buffer_alloc_multi(odp_pool_t pool, odp_buffer_t buffer[], int num) +{ + int ret, total = 0; + + do { + ret = odp_buffer_alloc_multi(pool, buffer + total, num - total); + CU_ASSERT(ret >= 0); + CU_ASSERT(ret <= num - total); + total += ret; + } while (total < num && ret); + + return total; +} + +void buffer_test_pool_alloc_multi(void) +{ + odp_pool_t pool; + const int num = 3; + const size_t size = 1500; + odp_buffer_t buffer[num + 1]; + odp_event_t ev; + int index; + char wrong_type = 0, wrong_size = 0; + odp_pool_param_t params = { + .buf = { + .size = size, + .align = ODP_CACHE_LINE_SIZE, + .num = num, + }, + .type = ODP_POOL_BUFFER, + }; + + pool = odp_pool_create("buffer_pool_alloc_multi", ¶ms); + odp_pool_print(pool); + + /* Try to allocate num + 1 items from the pool */ + CU_ASSERT_FATAL(buffer_alloc_multi(pool, buffer, num + 1) == num); + + for (index = 0; index < num; index++) { + if (buffer[index] == ODP_BUFFER_INVALID) + break; + + ev = odp_buffer_to_event(buffer[index]); + if (odp_event_type(ev) != ODP_EVENT_BUFFER) + wrong_type = 1; + if (odp_buffer_size(buffer[index]) < size) + wrong_size = 1; + if (wrong_type || wrong_size) + odp_buffer_print(buffer[index]); + } + + /* Check that the pool had at least num items */ + CU_ASSERT(index == num); + + /* Check that the pool had correct buffers */ + CU_ASSERT(wrong_type == 0); + CU_ASSERT(wrong_size == 0); + + odp_buffer_free_multi(buffer, num); + + CU_ASSERT(odp_pool_destroy(pool) == 0); +} + +void buffer_test_pool_free(void) +{ + odp_pool_t pool; + odp_buffer_t buffer; + odp_pool_param_t params = { + .buf = { + .size = 64, + .align = ODP_CACHE_LINE_SIZE, + .num = 1, + }, + .type = ODP_POOL_BUFFER, + }; + + pool = odp_pool_create("buffer_pool_free", ¶ms); + + /* Allocate the only buffer from the pool */ + buffer = odp_buffer_alloc(pool); + CU_ASSERT_FATAL(buffer != ODP_BUFFER_INVALID); + + /* Pool should have only one buffer */ + CU_ASSERT_FATAL(odp_buffer_alloc(pool) == ODP_BUFFER_INVALID) + + odp_buffer_free(buffer); + + /* Check that the buffer was returned back to the pool */ + buffer = odp_buffer_alloc(pool); + CU_ASSERT_FATAL(buffer != ODP_BUFFER_INVALID); + + odp_buffer_free(buffer); + CU_ASSERT(odp_pool_destroy(pool) == 0); +} + +void buffer_test_pool_free_multi(void) +{ + odp_pool_t pool[2]; + odp_buffer_t buffer[4]; + odp_buffer_t buf_inval[2]; + odp_pool_param_t params = { + .buf = { + .size = 64, + .align = ODP_CACHE_LINE_SIZE, + .num = 2, + }, + .type = ODP_POOL_BUFFER, + }; + + pool[0] = odp_pool_create("buffer_pool_free_multi_0", ¶ms); + pool[1] = odp_pool_create("buffer_pool_free_multi_1", ¶ms); + CU_ASSERT_FATAL(pool[0] != ODP_POOL_INVALID); + CU_ASSERT_FATAL(pool[1] != ODP_POOL_INVALID); + + /* Allocate all the buffers from the pools */ + CU_ASSERT_FATAL(buffer_alloc_multi(pool[0], &buffer[0], 2) == 2); + CU_ASSERT_FATAL(buffer_alloc_multi(pool[1], &buffer[2], 2) == 2); + + /* Pools should have no more buffer */ + CU_ASSERT(odp_buffer_alloc_multi(pool[0], buf_inval, 2) == 0); + CU_ASSERT(odp_buffer_alloc_multi(pool[1], buf_inval, 2) == 0); + + /* Try to free both buffers from both pools at once */ + odp_buffer_free_multi(buffer, 4); + + /* Check that all buffers were returned back to the pools */ + CU_ASSERT_FATAL(buffer_alloc_multi(pool[0], &buffer[0], 2) == 2); + CU_ASSERT_FATAL(buffer_alloc_multi(pool[1], &buffer[2], 2) == 2); + + odp_buffer_free_multi(buffer, 4); + CU_ASSERT(odp_pool_destroy(pool[0]) == 0); + CU_ASSERT(odp_pool_destroy(pool[1]) == 0); +} + +void buffer_test_management_basic(void) +{ + odp_event_t ev = odp_buffer_to_event(raw_buffer); + + CU_ASSERT(odp_buffer_is_valid(raw_buffer) == 1); + CU_ASSERT(odp_buffer_pool(raw_buffer) != ODP_POOL_INVALID); + CU_ASSERT(odp_event_type(ev) == ODP_EVENT_BUFFER); + CU_ASSERT(odp_buffer_size(raw_buffer) >= raw_buffer_size); + CU_ASSERT(odp_buffer_addr(raw_buffer) != NULL); + odp_buffer_print(raw_buffer); + CU_ASSERT(odp_buffer_to_u64(raw_buffer) != + odp_buffer_to_u64(ODP_BUFFER_INVALID)); + CU_ASSERT(odp_event_to_u64(ev) != odp_event_to_u64(ODP_EVENT_INVALID)); +} + +odp_testinfo_t buffer_suite[] = { + ODP_TEST_INFO(buffer_test_pool_alloc), + ODP_TEST_INFO(buffer_test_pool_free), + ODP_TEST_INFO(buffer_test_pool_alloc_multi), + ODP_TEST_INFO(buffer_test_pool_free_multi), + ODP_TEST_INFO(buffer_test_management_basic), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t buffer_suites[] = { + {"buffer tests", buffer_suite_init, buffer_suite_term, buffer_suite}, + ODP_SUITE_INFO_NULL, +}; + +int buffer_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(buffer_suites); + + if (ret == 0) + odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/buffer/buffer.h b/test/common_plat/validation/api/buffer/buffer.h new file mode 100644 index 000000000..48331e3f1 --- /dev/null +++ b/test/common_plat/validation/api/buffer/buffer.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_BUFFER_H_ +#define _ODP_TEST_BUFFER_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void buffer_test_pool_alloc(void); +void buffer_test_pool_free(void); +void buffer_test_pool_alloc_multi(void); +void buffer_test_pool_free_multi(void); +void buffer_test_management_basic(void); + +/* test arrays: */ +extern odp_testinfo_t buffer_suite[]; + +/* test array init/term functions: */ +int buffer_suite_init(void); +int buffer_suite_term(void); + +/* test registry: */ +extern odp_suiteinfo_t buffer_suites[]; + +/* main test program: */ +int buffer_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/buffer/buffer_main.c b/test/common_plat/validation/api/buffer/buffer_main.c new file mode 100644 index 000000000..47168f8b9 --- /dev/null +++ b/test/common_plat/validation/api/buffer/buffer_main.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "buffer.h" + +int main(int argc, char *argv[]) +{ + return buffer_main(argc, argv); +} diff --git a/test/common_plat/validation/api/classification/.gitignore b/test/common_plat/validation/api/classification/.gitignore new file mode 100644 index 000000000..e2cdfefe1 --- /dev/null +++ b/test/common_plat/validation/api/classification/.gitignore @@ -0,0 +1 @@ +classification_main diff --git a/test/common_plat/validation/api/classification/Makefile.am b/test/common_plat/validation/api/classification/Makefile.am new file mode 100644 index 000000000..df382c51f --- /dev/null +++ b/test/common_plat/validation/api/classification/Makefile.am @@ -0,0 +1,14 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestclassification.la +libtestclassification_la_SOURCES = odp_classification_basic.c \ + odp_classification_tests.c \ + odp_classification_test_pmr.c \ + odp_classification_common.c \ + classification.c + +test_PROGRAMS = classification_main$(EXEEXT) +dist_classification_main_SOURCES = classification_main.c +classification_main_LDADD = libtestclassification.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = classification.h odp_classification_testsuites.h diff --git a/test/common_plat/validation/api/classification/classification.c b/test/common_plat/validation/api/classification/classification.c new file mode 100644 index 000000000..1032e7f1f --- /dev/null +++ b/test/common_plat/validation/api/classification/classification.c @@ -0,0 +1,43 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include <odp_cunit_common.h> +#include "odp_classification_testsuites.h" +#include "classification.h" + +odp_suiteinfo_t classification_suites[] = { + { .pName = "classification basic", + .pTests = classification_suite_basic, + }, + { .pName = "classification pmr tests", + .pTests = classification_suite_pmr, + .pInitFunc = classification_suite_pmr_init, + .pCleanupFunc = classification_suite_pmr_term, + }, + { .pName = "classification tests", + .pTests = classification_suite, + .pInitFunc = classification_suite_init, + .pCleanupFunc = classification_suite_term, + }, + ODP_SUITE_INFO_NULL, +}; + +int classification_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(classification_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/classification/classification.h b/test/common_plat/validation/api/classification/classification.h new file mode 100644 index 000000000..d73c82161 --- /dev/null +++ b/test/common_plat/validation/api/classification/classification.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_CLASSIFICATION_H_ +#define _ODP_TEST_CLASSIFICATION_H_ + +#include <odp_cunit_common.h> + +#define SHM_PKT_NUM_BUFS 32 +#define SHM_PKT_BUF_SIZE 1024 + +/* Config values for Default CoS */ +#define TEST_DEFAULT 1 +#define CLS_DEFAULT 0 +#define CLS_DEFAULT_SADDR "10.0.0.1/32" +#define CLS_DEFAULT_DADDR "10.0.0.100/32" +#define CLS_DEFAULT_SPORT 1024 +#define CLS_DEFAULT_DPORT 2048 +#define CLS_DEFAULT_DMAC 0x010203040506 +#define CLS_DEFAULT_SMAC 0x060504030201 + +/* Config values for Error CoS */ +#define TEST_ERROR 1 +#define CLS_ERROR 1 + +/* Config values for PMR_CHAIN */ +#define TEST_PMR_CHAIN 1 +#define CLS_PMR_CHAIN_SRC 2 +#define CLS_PMR_CHAIN_DST 3 +#define CLS_PMR_CHAIN_SADDR "10.0.0.5/32" +#define CLS_PMR_CHAIN_PORT 3000 + +/* Config values for PMR */ +#define TEST_PMR 1 +#define CLS_PMR 4 +#define CLS_PMR_PORT 4000 + +/* Config values for PMR SET */ +#define TEST_PMR_SET 1 +#define CLS_PMR_SET 5 +#define CLS_PMR_SET_SADDR "10.0.0.6/32" +#define CLS_PMR_SET_PORT 5000 + +/* Config values for CoS L2 Priority */ +#define TEST_L2_QOS 1 +#define CLS_L2_QOS_0 6 +#define CLS_L2_QOS_MAX 5 + +#define CLS_ENTRIES (CLS_L2_QOS_0 + CLS_L2_QOS_MAX) + +/* Test Packet values */ +#define DATA_MAGIC 0x01020304 +#define TEST_SEQ_INVALID ((uint32_t)~0) + +/* test functions: */ +void classification_test_create_cos(void); +void classification_test_destroy_cos(void); +void classification_test_create_pmr_match(void); +void classification_test_cos_set_queue(void); +void classification_test_cos_set_pool(void); +void classification_test_cos_set_drop(void); +void classification_test_pmr_composite_create(void); +void classification_test_pmr_composite_destroy(void); + +void classification_test_pktio_set_skip(void); +void classification_test_pktio_set_headroom(void); +void classification_test_pktio_configure(void); +void classification_test_pktio_test(void); + +void classification_test_pmr_term_tcp_dport(void); +void classification_test_pmr_term_tcp_sport(void); +void classification_test_pmr_term_udp_dport(void); +void classification_test_pmr_term_udp_sport(void); +void classification_test_pmr_term_ipproto(void); +void classification_test_pmr_term_dmac(void); +void classification_test_pmr_term_packet_len(void); + +/* test arrays: */ +extern odp_testinfo_t classification_suite_basic[]; +extern odp_testinfo_t classification_suite[]; + +/* test array init/term functions: */ +int classification_suite_init(void); +int classification_suite_term(void); + +/* test registry: */ +extern odp_suiteinfo_t classification_suites[]; + +/* main test program: */ +int classification_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/classification/classification_main.c b/test/common_plat/validation/api/classification/classification_main.c new file mode 100644 index 000000000..8902463c2 --- /dev/null +++ b/test/common_plat/validation/api/classification/classification_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "classification.h" + +int main(int argc, char *argv[]) +{ + return classification_main(argc, argv); +} diff --git a/test/common_plat/validation/api/classification/odp_classification_basic.c b/test/common_plat/validation/api/classification/odp_classification_basic.c new file mode 100644 index 000000000..372377d85 --- /dev/null +++ b/test/common_plat/validation/api/classification/odp_classification_basic.c @@ -0,0 +1,332 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_cunit_common.h> +#include "odp_classification_testsuites.h" +#include "classification.h" + +#define PMR_SET_NUM 5 + +void classification_test_create_cos(void) +{ + odp_cos_t cos; + odp_cls_cos_param_t cls_param; + odp_pool_t pool; + odp_queue_t queue; + char cosname[ODP_COS_NAME_LEN]; + + pool = pool_create("cls_basic_pool"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + queue = queue_create("cls_basic_queue", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + sprintf(cosname, "ClassOfService"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT(odp_cos_to_u64(cos) != odp_cos_to_u64(ODP_COS_INVALID)); + odp_cos_destroy(cos); + odp_pool_destroy(pool); + odp_queue_destroy(queue); +} + +void classification_test_destroy_cos(void) +{ + odp_cos_t cos; + char name[ODP_COS_NAME_LEN]; + odp_pool_t pool; + odp_queue_t queue; + odp_cls_cos_param_t cls_param; + int retval; + + pool = pool_create("cls_basic_pool"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + queue = queue_create("cls_basic_queue", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + sprintf(name, "ClassOfService"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(name, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + retval = odp_cos_destroy(cos); + CU_ASSERT(retval == 0); + retval = odp_cos_destroy(ODP_COS_INVALID); + CU_ASSERT(retval < 0); + + odp_pool_destroy(pool); + odp_queue_destroy(queue); +} + +void classification_test_create_pmr_match(void) +{ + odp_pmr_t pmr; + uint16_t val; + uint16_t mask; + int retval; + odp_pmr_param_t pmr_param; + odp_cos_t default_cos; + odp_cos_t cos; + odp_queue_t default_queue; + odp_queue_t queue; + odp_pool_t default_pool; + odp_pool_t pool; + odp_pool_t pkt_pool; + odp_cls_cos_param_t cls_param; + odp_pktio_t pktio; + + pkt_pool = pool_create("pkt_pool"); + CU_ASSERT_FATAL(pkt_pool != ODP_POOL_INVALID); + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("pmr_match", true); + CU_ASSERT(queue != ODP_QUEUE_INVALID); + + pool = pool_create("pmr_match"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create("pmr_match", &cls_param); + CU_ASSERT(cos != ODP_COS_INVALID); + + val = 1024; + mask = 0xffff; + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = find_first_supported_l3_pmr(); + pmr_param.range_term = false; + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(val); + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT(pmr != ODP_PMR_INVAL); + CU_ASSERT(odp_pmr_to_u64(pmr) != odp_pmr_to_u64(ODP_PMR_INVAL)); + /* destroy the created PMR */ + retval = odp_cls_pmr_destroy(pmr); + CU_ASSERT(retval == 0); + + /* destroy an INVALID PMR */ + retval = odp_cls_pmr_destroy(ODP_PMR_INVAL); + CU_ASSERT(retval < 0); + + odp_queue_destroy(queue); + odp_pool_destroy(pool); + odp_pool_destroy(pkt_pool); + odp_cos_destroy(cos); + odp_queue_destroy(default_queue); + odp_pool_destroy(default_pool); + odp_cos_destroy(default_cos); + odp_pktio_close(pktio); +} + +void classification_test_cos_set_queue(void) +{ + int retval; + char cosname[ODP_COS_NAME_LEN]; + odp_cls_cos_param_t cls_param; + odp_pool_t pool; + odp_queue_t queue; + odp_queue_t queue_cos; + odp_cos_t cos_queue; + odp_queue_t recvqueue; + + pool = pool_create("cls_basic_pool"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + queue = queue_create("cls_basic_queue", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + sprintf(cosname, "CoSQueue"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + cos_queue = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos_queue != ODP_COS_INVALID); + + queue_cos = queue_create("QueueCoS", true); + CU_ASSERT_FATAL(queue_cos != ODP_QUEUE_INVALID); + + retval = odp_cos_queue_set(cos_queue, queue_cos); + CU_ASSERT(retval == 0); + recvqueue = odp_cos_queue(cos_queue); + CU_ASSERT(recvqueue == queue_cos); + + odp_cos_destroy(cos_queue); + odp_queue_destroy(queue_cos); + odp_queue_destroy(queue); + odp_pool_destroy(pool); +} + +void classification_test_cos_set_pool(void) +{ + int retval; + char cosname[ODP_COS_NAME_LEN]; + odp_cls_cos_param_t cls_param; + odp_pool_t pool; + odp_queue_t queue; + odp_pool_t cos_pool; + odp_cos_t cos; + odp_pool_t recvpool; + + pool = pool_create("cls_basic_pool"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + queue = queue_create("cls_basic_queue", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + sprintf(cosname, "CoSQueue"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + + cos_pool = pool_create("PoolCoS"); + CU_ASSERT_FATAL(cos_pool != ODP_POOL_INVALID); + + retval = odp_cls_cos_pool_set(cos, cos_pool); + CU_ASSERT(retval == 0); + recvpool = odp_cls_cos_pool(cos); + CU_ASSERT(recvpool == cos_pool); + + odp_cos_destroy(cos); + odp_queue_destroy(queue); + odp_pool_destroy(pool); + odp_pool_destroy(cos_pool); +} + +void classification_test_cos_set_drop(void) +{ + int retval; + char cosname[ODP_COS_NAME_LEN]; + odp_cos_t cos_drop; + odp_queue_t queue; + odp_pool_t pool; + odp_cls_cos_param_t cls_param; + + pool = pool_create("cls_basic_pool"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + queue = queue_create("cls_basic_queue", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + sprintf(cosname, "CoSDrop"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + cos_drop = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos_drop != ODP_COS_INVALID); + + retval = odp_cos_drop_set(cos_drop, ODP_COS_DROP_POOL); + CU_ASSERT(retval == 0); + CU_ASSERT(ODP_COS_DROP_POOL == odp_cos_drop(cos_drop)); + + retval = odp_cos_drop_set(cos_drop, ODP_COS_DROP_NEVER); + CU_ASSERT(retval == 0); + CU_ASSERT(ODP_COS_DROP_NEVER == odp_cos_drop(cos_drop)); + odp_cos_destroy(cos_drop); + odp_pool_destroy(pool); + odp_queue_destroy(queue); +} + +void classification_test_pmr_composite_create(void) +{ + odp_pmr_t pmr_composite; + int retval; + odp_pmr_param_t pmr_terms[PMR_SET_NUM]; + odp_cos_t default_cos; + odp_cos_t cos; + odp_queue_t default_queue; + odp_queue_t queue; + odp_pool_t default_pool; + odp_pool_t pool; + odp_pool_t pkt_pool; + odp_cls_cos_param_t cls_param; + odp_pktio_t pktio; + uint16_t val = 1024; + uint16_t mask = 0xffff; + int i; + + pkt_pool = pool_create("pkt_pool"); + CU_ASSERT_FATAL(pkt_pool != ODP_POOL_INVALID); + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("pmr_match", true); + CU_ASSERT(queue != ODP_QUEUE_INVALID); + + pool = pool_create("pmr_match"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create("pmr_match", &cls_param); + CU_ASSERT(cos != ODP_COS_INVALID); + + for (i = 0; i < PMR_SET_NUM; i++) { + odp_cls_pmr_param_init(&pmr_terms[i]); + pmr_terms[i].term = ODP_PMR_TCP_DPORT; + pmr_terms[i].match.value = &val; + pmr_terms[i].range_term = false; + pmr_terms[i].match.mask = &mask; + pmr_terms[i].val_sz = sizeof(val); + } + + pmr_composite = odp_cls_pmr_create(pmr_terms, PMR_SET_NUM, + default_cos, cos); + CU_ASSERT(odp_pmr_to_u64(pmr_composite) != + odp_pmr_to_u64(ODP_PMR_INVAL)); + + retval = odp_cls_pmr_destroy(pmr_composite); + CU_ASSERT(retval == 0); + + odp_queue_destroy(queue); + odp_pool_destroy(pool); + odp_pool_destroy(pkt_pool); + odp_cos_destroy(cos); + odp_queue_destroy(default_queue); + odp_pool_destroy(default_pool); + odp_cos_destroy(default_cos); + odp_pktio_close(pktio); +} + +odp_testinfo_t classification_suite_basic[] = { + ODP_TEST_INFO(classification_test_create_cos), + ODP_TEST_INFO(classification_test_destroy_cos), + ODP_TEST_INFO(classification_test_create_pmr_match), + ODP_TEST_INFO(classification_test_cos_set_queue), + ODP_TEST_INFO(classification_test_cos_set_drop), + ODP_TEST_INFO(classification_test_cos_set_pool), + ODP_TEST_INFO(classification_test_pmr_composite_create), + ODP_TEST_INFO_NULL, +}; diff --git a/test/common_plat/validation/api/classification/odp_classification_common.c b/test/common_plat/validation/api/classification/odp_classification_common.c new file mode 100644 index 000000000..7a42ac745 --- /dev/null +++ b/test/common_plat/validation/api/classification/odp_classification_common.c @@ -0,0 +1,388 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "odp_classification_testsuites.h" +#include "classification.h" +#include <odp_cunit_common.h> +#include <odp/helper/eth.h> +#include <odp/helper/ip.h> +#include <odp/helper/udp.h> +#include <odp/helper/tcp.h> + +typedef struct cls_test_packet { + odp_u32be_t magic; + odp_u32be_t seq; +} cls_test_packet_t; + +odp_pktio_t create_pktio(odp_queue_type_t q_type, odp_pool_t pool) +{ + odp_pktio_t pktio; + odp_pktio_param_t pktio_param; + odp_pktin_queue_param_t pktin_param; + int ret; + + if (pool == ODP_POOL_INVALID) + return ODP_PKTIO_INVALID; + + odp_pktio_param_init(&pktio_param); + if (q_type == ODP_QUEUE_TYPE_PLAIN) + pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE; + else + pktio_param.in_mode = ODP_PKTIN_MODE_SCHED; + + pktio = odp_pktio_open("loop", pool, &pktio_param); + if (pktio == ODP_PKTIO_INVALID) { + ret = odp_pool_destroy(pool); + if (ret) + fprintf(stderr, "unable to destroy pool.\n"); + return ODP_PKTIO_INVALID; + } + + odp_pktin_queue_param_init(&pktin_param); + pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC; + + if (odp_pktin_queue_config(pktio, &pktin_param)) { + fprintf(stderr, "pktin queue config failed.\n"); + return ODP_PKTIO_INVALID; + } + + if (odp_pktout_queue_config(pktio, NULL)) { + fprintf(stderr, "pktout queue config failed.\n"); + return ODP_PKTIO_INVALID; + } + + return pktio; +} + +int stop_pktio(odp_pktio_t pktio) +{ + odp_event_t ev; + + if (odp_pktio_stop(pktio)) { + fprintf(stderr, "pktio stop failed.\n"); + return -1; + } + + while (1) { + ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT); + + if (ev != ODP_EVENT_INVALID) + odp_event_free(ev); + else + break; + } + + return 0; +} + +int cls_pkt_set_seq(odp_packet_t pkt) +{ + static uint32_t seq; + cls_test_packet_t data; + uint32_t offset; + odph_ipv4hdr_t *ip; + odph_tcphdr_t *tcp; + int status; + + data.magic = DATA_MAGIC; + data.seq = ++seq; + + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + offset = odp_packet_l4_offset(pkt); + CU_ASSERT_FATAL(offset != ODP_PACKET_OFFSET_INVALID); + + if (ip->proto == ODPH_IPPROTO_UDP) + status = odp_packet_copy_from_mem(pkt, offset + ODPH_UDPHDR_LEN, + sizeof(data), &data); + else { + tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL); + status = odp_packet_copy_from_mem(pkt, offset + tcp->hl * 4, + sizeof(data), &data); + } + + return status; +} + +uint32_t cls_pkt_get_seq(odp_packet_t pkt) +{ + uint32_t offset; + cls_test_packet_t data; + odph_ipv4hdr_t *ip; + odph_tcphdr_t *tcp; + + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + offset = odp_packet_l4_offset(pkt); + + if (offset == ODP_PACKET_OFFSET_INVALID || ip == NULL) + return TEST_SEQ_INVALID; + + if (ip->proto == ODPH_IPPROTO_UDP) + odp_packet_copy_to_mem(pkt, offset + ODPH_UDPHDR_LEN, + sizeof(data), &data); + else { + tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL); + odp_packet_copy_to_mem(pkt, offset + tcp->hl * 4, + sizeof(data), &data); + } + + if (data.magic == DATA_MAGIC) + return data.seq; + + return TEST_SEQ_INVALID; +} + +int parse_ipv4_string(const char *ipaddress, uint32_t *addr, uint32_t *mask) +{ + int b[4]; + int qualifier = 32; + int converted; + + if (strchr(ipaddress, '/')) { + converted = sscanf(ipaddress, "%d.%d.%d.%d/%d", + &b[3], &b[2], &b[1], &b[0], + &qualifier); + if (5 != converted) + return -1; + } else { + converted = sscanf(ipaddress, "%d.%d.%d.%d", + &b[3], &b[2], &b[1], &b[0]); + if (4 != converted) + return -1; + } + + if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255)) + return -1; + if (!qualifier || (qualifier > 32)) + return -1; + + *addr = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24; + if (mask) + *mask = ~(0xFFFFFFFF & ((1ULL << (32 - qualifier)) - 1)); + + return 0; +} + +void enqueue_pktio_interface(odp_packet_t pkt, odp_pktio_t pktio) +{ + odp_pktout_queue_t pktout; + + CU_ASSERT_FATAL(odp_pktout_queue(pktio, &pktout, 1) == 1); + CU_ASSERT(odp_pktout_send(pktout, &pkt, 1) == 1); +} + +odp_packet_t receive_packet(odp_queue_t *queue, uint64_t ns) +{ + odp_event_t ev; + + ev = odp_schedule(queue, ns); + return odp_packet_from_event(ev); +} + +odp_queue_t queue_create(const char *queuename, bool sched) +{ + odp_queue_t queue; + odp_queue_param_t qparam; + + if (sched) { + odp_queue_param_init(&qparam); + qparam.type = ODP_QUEUE_TYPE_SCHED; + qparam.sched.prio = ODP_SCHED_PRIO_HIGHEST; + qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qparam.sched.group = ODP_SCHED_GROUP_ALL; + + queue = odp_queue_create(queuename, &qparam); + } else { + queue = odp_queue_create(queuename, NULL); + } + + return queue; +} + +odp_pool_t pool_create(const char *poolname) +{ + odp_pool_param_t param; + + odp_pool_param_init(¶m); + param.pkt.seg_len = SHM_PKT_BUF_SIZE; + param.pkt.len = SHM_PKT_BUF_SIZE; + param.pkt.num = SHM_PKT_NUM_BUFS; + param.type = ODP_POOL_PACKET; + + return odp_pool_create(poolname, ¶m); +} + +odp_packet_t create_packet(odp_pool_t pool, bool vlan, + odp_atomic_u32_t *seq, bool flag_udp) +{ + return create_packet_len(pool, vlan, seq, flag_udp, 0); +} + +odp_packet_t create_packet_len(odp_pool_t pool, bool vlan, + odp_atomic_u32_t *seq, bool flag_udp, + uint16_t len) +{ + uint32_t seqno; + odph_ethhdr_t *ethhdr; + odph_udphdr_t *udp; + odph_tcphdr_t *tcp; + odph_ipv4hdr_t *ip; + uint16_t payload_len; + uint64_t src_mac = CLS_DEFAULT_SMAC; + uint64_t dst_mac = CLS_DEFAULT_DMAC; + uint64_t dst_mac_be; + uint32_t addr = 0; + uint32_t mask; + int offset; + odp_packet_t pkt; + int packet_len = 0; + + /* 48 bit ethernet address needs to be left shifted for proper + value after changing to be*/ + dst_mac_be = odp_cpu_to_be_64(dst_mac); + if (dst_mac != dst_mac_be) + dst_mac_be = dst_mac_be >> (64 - 8 * ODPH_ETHADDR_LEN); + + payload_len = sizeof(cls_test_packet_t) + len; + packet_len += ODPH_ETHHDR_LEN; + packet_len += ODPH_IPV4HDR_LEN; + if (flag_udp) + packet_len += ODPH_UDPHDR_LEN; + else + packet_len += ODPH_TCPHDR_LEN; + packet_len += payload_len; + + if (vlan) + packet_len += ODPH_VLANHDR_LEN; + + pkt = odp_packet_alloc(pool, packet_len); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + + /* Ethernet Header */ + offset = 0; + odp_packet_l2_offset_set(pkt, offset); + ethhdr = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + memcpy(ethhdr->src.addr, &src_mac, ODPH_ETHADDR_LEN); + memcpy(ethhdr->dst.addr, &dst_mac_be, ODPH_ETHADDR_LEN); + offset += sizeof(odph_ethhdr_t); + if (vlan) { + /* Default vlan header */ + odph_vlanhdr_t *vlan_hdr; + + ethhdr->type = odp_cpu_to_be_16(ODPH_ETHTYPE_VLAN); + vlan_hdr = (odph_vlanhdr_t *)(ethhdr + 1); + vlan_hdr->tci = odp_cpu_to_be_16(0); + vlan_hdr->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4); + offset += sizeof(odph_vlanhdr_t); + } else { + ethhdr->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4); + } + + odp_packet_l3_offset_set(pkt, offset); + + /* ipv4 */ + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + + parse_ipv4_string(CLS_DEFAULT_DADDR, &addr, &mask); + ip->dst_addr = odp_cpu_to_be_32(addr); + + parse_ipv4_string(CLS_DEFAULT_SADDR, &addr, &mask); + ip->src_addr = odp_cpu_to_be_32(addr); + ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN; + if (flag_udp) + ip->tot_len = odp_cpu_to_be_16(ODPH_UDPHDR_LEN + payload_len + + ODPH_IPV4HDR_LEN); + else + ip->tot_len = odp_cpu_to_be_16(ODPH_TCPHDR_LEN + payload_len + + ODPH_IPV4HDR_LEN); + + ip->ttl = 128; + if (flag_udp) + ip->proto = ODPH_IPPROTO_UDP; + else + ip->proto = ODPH_IPPROTO_TCP; + + seqno = odp_atomic_fetch_inc_u32(seq); + ip->id = odp_cpu_to_be_16(seqno); + ip->chksum = 0; + ip->chksum = odph_ipv4_csum_update(pkt); + offset += ODPH_IPV4HDR_LEN; + + /* udp */ + if (flag_udp) { + odp_packet_l4_offset_set(pkt, offset); + udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); + udp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT); + udp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT); + udp->length = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN); + udp->chksum = 0; + } else { + odp_packet_l4_offset_set(pkt, offset); + tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL); + tcp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT); + tcp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT); + tcp->hl = ODPH_TCPHDR_LEN / 4; + /* TODO: checksum field has to be updated */ + tcp->cksm = 0; + } + + /* set pkt sequence number */ + cls_pkt_set_seq(pkt); + + return pkt; +} + +odp_cls_pmr_term_t find_first_supported_l3_pmr(void) +{ + odp_cls_pmr_term_t term = ODP_PMR_TCP_DPORT; + odp_cls_capability_t capability; + + odp_cls_capability(&capability); + + /* choose supported PMR */ + if (capability.supported_terms.bit.udp_sport) + term = ODP_PMR_UDP_SPORT; + else if (capability.supported_terms.bit.udp_dport) + term = ODP_PMR_UDP_DPORT; + else if (capability.supported_terms.bit.tcp_sport) + term = ODP_PMR_TCP_SPORT; + else if (capability.supported_terms.bit.tcp_dport) + term = ODP_PMR_TCP_DPORT; + else + CU_FAIL("Implementations doesn't support any TCP/UDP PMR"); + + return term; +} + +int set_first_supported_pmr_port(odp_packet_t pkt, uint16_t port) +{ + odph_udphdr_t *udp; + odph_tcphdr_t *tcp; + odp_cls_pmr_term_t term; + + udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); + tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL); + port = odp_cpu_to_be_16(port); + term = find_first_supported_l3_pmr(); + switch (term) { + case ODP_PMR_UDP_SPORT: + udp->src_port = port; + break; + case ODP_PMR_UDP_DPORT: + udp->dst_port = port; + break; + case ODP_PMR_TCP_DPORT: + tcp->dst_port = port; + break; + case ODP_PMR_TCP_SPORT: + tcp->src_port = port; + break; + default: + CU_FAIL("Unsupported L3 term"); + return -1; + } + + return 0; +} diff --git a/test/common_plat/validation/api/classification/odp_classification_test_pmr.c b/test/common_plat/validation/api/classification/odp_classification_test_pmr.c new file mode 100644 index 000000000..c8bbf50b5 --- /dev/null +++ b/test/common_plat/validation/api/classification/odp_classification_test_pmr.c @@ -0,0 +1,1162 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "odp_classification_testsuites.h" +#include "classification.h" +#include <odp_cunit_common.h> +#include <odp/helper/eth.h> +#include <odp/helper/ip.h> +#include <odp/helper/udp.h> +#include <odp/helper/tcp.h> + +static odp_pool_t pkt_pool; + +/** sequence number of IP packets */ +odp_atomic_u32_t seq; + +int classification_suite_pmr_init(void) +{ + pkt_pool = pool_create("classification_pmr_pool"); + if (ODP_POOL_INVALID == pkt_pool) { + fprintf(stderr, "Packet pool creation failed.\n"); + return -1; + } + + odp_atomic_init_u32(&seq, 0); + return 0; +} + +static int start_pktio(odp_pktio_t pktio) +{ + if (odp_pktio_start(pktio)) { + fprintf(stderr, "unable to start loop\n"); + return -1; + } + + return 0; +} + +void configure_default_cos(odp_pktio_t pktio, odp_cos_t *cos, + odp_queue_t *queue, odp_pool_t *pool) +{ + odp_cls_cos_param_t cls_param; + odp_pool_t default_pool; + odp_cos_t default_cos; + odp_queue_t default_queue; + int retval; + char cosname[ODP_COS_NAME_LEN]; + + default_pool = pool_create("DefaultPool"); + CU_ASSERT(default_pool != ODP_POOL_INVALID); + + default_queue = queue_create("DefaultQueue", true); + CU_ASSERT(default_queue != ODP_QUEUE_INVALID); + + sprintf(cosname, "DefaultCos"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = default_pool; + cls_param.queue = default_queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + default_cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT(default_cos != ODP_COS_INVALID); + + retval = odp_pktio_default_cos_set(pktio, default_cos); + CU_ASSERT(retval == 0); + + *cos = default_cos; + *queue = default_queue; + *pool = default_pool; +} + +int classification_suite_pmr_term(void) +{ + int retcode = 0; + + if (0 != odp_pool_destroy(pkt_pool)) { + fprintf(stderr, "pkt_pool destroy failed.\n"); + retcode = -1; + } + + return retcode; +} + +void classification_test_pmr_term_tcp_dport(void) +{ + odp_packet_t pkt; + odph_tcphdr_t *tcp; + uint32_t seqno; + uint16_t val; + uint16_t mask; + int retval; + odp_pktio_t pktio; + odp_queue_t queue; + odp_queue_t retqueue; + odp_queue_t default_queue; + odp_cos_t default_cos; + odp_pool_t default_pool; + odp_pool_t recvpool; + odp_pmr_t pmr; + odp_cos_t cos; + char cosname[ODP_COS_NAME_LEN]; + odp_cls_cos_param_t cls_param; + odp_pool_t pool; + odp_pool_t pool_recv; + odp_pmr_param_t pmr_param; + odph_ethhdr_t *eth; + + val = CLS_DEFAULT_DPORT; + mask = 0xffff; + seqno = 0; + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + retval = start_pktio(pktio); + CU_ASSERT(retval == 0); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("tcp_dport1", true); + CU_ASSERT(queue != ODP_QUEUE_INVALID); + + pool = pool_create("tcp_dport1"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + sprintf(cosname, "tcp_dport"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT(cos != ODP_COS_INVALID); + + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_TCP_DPORT; + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(val); + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT(pmr != ODP_PMR_INVAL); + + pkt = create_packet(pkt_pool, false, &seq, false); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL); + tcp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + pool_recv = odp_packet_pool(pkt); + CU_ASSERT(pool == pool_recv); + CU_ASSERT(retqueue == queue); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + + odp_packet_free(pkt); + + /* Other packets are delivered to default queue */ + pkt = create_packet(pkt_pool, false, &seq, false); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL); + tcp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT + 1); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + CU_ASSERT(retqueue == default_queue); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == default_pool); + + odp_packet_free(pkt); + odp_cos_destroy(cos); + odp_cos_destroy(default_cos); + odp_cls_pmr_destroy(pmr); + stop_pktio(pktio); + odp_queue_destroy(queue); + odp_queue_destroy(default_queue); + odp_pool_destroy(pool); + odp_pool_destroy(default_pool); + odp_pktio_close(pktio); +} + +void classification_test_pmr_term_tcp_sport(void) +{ + odp_packet_t pkt; + odph_tcphdr_t *tcp; + uint32_t seqno; + uint16_t val; + uint16_t mask; + int retval; + odp_pktio_t pktio; + odp_queue_t queue; + odp_queue_t retqueue; + odp_queue_t default_queue; + odp_cos_t default_cos; + odp_pool_t default_pool; + odp_pool_t pool; + odp_pool_t recvpool; + odp_pmr_t pmr; + odp_cos_t cos; + char cosname[ODP_COS_NAME_LEN]; + odp_cls_cos_param_t cls_param; + odp_pmr_param_t pmr_param; + odph_ethhdr_t *eth; + + val = CLS_DEFAULT_SPORT; + mask = 0xffff; + seqno = 0; + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + retval = start_pktio(pktio); + CU_ASSERT(retval == 0); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("tcp_sport", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + pool = pool_create("tcp_sport"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + sprintf(cosname, "tcp_sport"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_TCP_SPORT; + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(val); + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT(pmr != ODP_PMR_INVAL); + + pkt = create_packet(pkt_pool, false, &seq, false); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL); + tcp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + CU_ASSERT(retqueue == queue); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == pool); + odp_packet_free(pkt); + + pkt = create_packet(pkt_pool, false, &seq, false); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + tcp = (odph_tcphdr_t *)odp_packet_l4_ptr(pkt, NULL); + tcp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT + 1); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + CU_ASSERT(retqueue == default_queue); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == default_pool); + + odp_packet_free(pkt); + odp_cos_destroy(cos); + odp_cos_destroy(default_cos); + odp_cls_pmr_destroy(pmr); + stop_pktio(pktio); + odp_pool_destroy(default_pool); + odp_pool_destroy(pool); + odp_queue_destroy(queue); + odp_queue_destroy(default_queue); + odp_pktio_close(pktio); +} + +void classification_test_pmr_term_udp_dport(void) +{ + odp_packet_t pkt; + odph_udphdr_t *udp; + uint32_t seqno; + uint16_t val; + uint16_t mask; + int retval; + odp_pktio_t pktio; + odp_pool_t pool; + odp_pool_t recvpool; + odp_queue_t queue; + odp_queue_t retqueue; + odp_queue_t default_queue; + odp_cos_t default_cos; + odp_pool_t default_pool; + odp_pmr_t pmr; + odp_cos_t cos; + char cosname[ODP_COS_NAME_LEN]; + odp_pmr_param_t pmr_param; + odp_cls_cos_param_t cls_param; + odph_ethhdr_t *eth; + + val = CLS_DEFAULT_DPORT; + mask = 0xffff; + seqno = 0; + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + retval = start_pktio(pktio); + CU_ASSERT(retval == 0); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("udp_dport", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + pool = pool_create("udp_dport"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + sprintf(cosname, "udp_dport"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_UDP_DPORT; + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(val); + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT(pmr != ODP_PMR_INVAL); + + pkt = create_packet(pkt_pool, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); + udp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + CU_ASSERT(retqueue == queue); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == pool); + odp_packet_free(pkt); + + /* Other packets received in default queue */ + pkt = create_packet(pkt_pool, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); + udp->dst_port = odp_cpu_to_be_16(CLS_DEFAULT_DPORT + 1); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + CU_ASSERT(retqueue == default_queue); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == default_pool); + + odp_packet_free(pkt); + odp_cos_destroy(cos); + odp_cos_destroy(default_cos); + odp_cls_pmr_destroy(pmr); + stop_pktio(pktio); + odp_queue_destroy(queue); + odp_queue_destroy(default_queue); + odp_pool_destroy(default_pool); + odp_pool_destroy(pool); + odp_pktio_close(pktio); +} + +void classification_test_pmr_term_udp_sport(void) +{ + odp_packet_t pkt; + odph_udphdr_t *udp; + uint32_t seqno; + uint16_t val; + uint16_t mask; + int retval; + odp_pktio_t pktio; + odp_queue_t queue; + odp_queue_t retqueue; + odp_queue_t default_queue; + odp_cos_t default_cos; + odp_pool_t default_pool; + odp_pool_t pool; + odp_pool_t recvpool; + odp_pmr_t pmr; + odp_cos_t cos; + char cosname[ODP_COS_NAME_LEN]; + odp_pmr_param_t pmr_param; + odp_cls_cos_param_t cls_param; + odph_ethhdr_t *eth; + + val = CLS_DEFAULT_SPORT; + mask = 0xffff; + seqno = 0; + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + retval = start_pktio(pktio); + CU_ASSERT(retval == 0); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("udp_sport", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + pool = pool_create("udp_sport"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + sprintf(cosname, "udp_sport"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_UDP_SPORT; + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(val); + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT(pmr != ODP_PMR_INVAL); + + pkt = create_packet(pkt_pool, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); + udp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + CU_ASSERT(retqueue == queue); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == pool); + odp_packet_free(pkt); + + pkt = create_packet(pkt_pool, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); + udp->src_port = odp_cpu_to_be_16(CLS_DEFAULT_SPORT + 1); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + CU_ASSERT(retqueue == default_queue); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == default_pool); + odp_packet_free(pkt); + + odp_cos_destroy(cos); + odp_cos_destroy(default_cos); + odp_cls_pmr_destroy(pmr); + stop_pktio(pktio); + odp_pool_destroy(default_pool); + odp_pool_destroy(pool); + odp_queue_destroy(queue); + odp_queue_destroy(default_queue); + odp_pktio_close(pktio); +} + +void classification_test_pmr_term_ipproto(void) +{ + odp_packet_t pkt; + uint32_t seqno; + uint8_t val; + uint8_t mask; + int retval; + odp_pktio_t pktio; + odp_queue_t queue; + odp_queue_t retqueue; + odp_queue_t default_queue; + odp_cos_t default_cos; + odp_pool_t default_pool; + odp_pool_t pool; + odp_pool_t recvpool; + odp_pmr_t pmr; + odp_cos_t cos; + char cosname[ODP_COS_NAME_LEN]; + odp_cls_cos_param_t cls_param; + odp_pmr_param_t pmr_param; + odph_ethhdr_t *eth; + + val = ODPH_IPPROTO_UDP; + mask = 0xff; + seqno = 0; + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + retval = start_pktio(pktio); + CU_ASSERT(retval == 0); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("ipproto", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + pool = pool_create("ipproto"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + sprintf(cosname, "ipproto"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_IPPROTO; + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(val); + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT(pmr != ODP_PMR_INVAL); + + pkt = create_packet(pkt_pool, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == pool); + CU_ASSERT(retqueue == queue); + odp_packet_free(pkt); + + /* Other packets delivered to default queue */ + pkt = create_packet(pkt_pool, false, &seq, false); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == default_pool); + CU_ASSERT(retqueue == default_queue); + + odp_cos_destroy(cos); + odp_cos_destroy(default_cos); + odp_cls_pmr_destroy(pmr); + odp_packet_free(pkt); + stop_pktio(pktio); + odp_pool_destroy(default_pool); + odp_pool_destroy(pool); + odp_queue_destroy(queue); + odp_queue_destroy(default_queue); + odp_pktio_close(pktio); +} + +void classification_test_pmr_term_dmac(void) +{ + odp_packet_t pkt; + uint32_t seqno; + uint64_t val; + uint64_t mask; + int retval; + odp_pktio_t pktio; + odp_queue_t queue; + odp_queue_t retqueue; + odp_queue_t default_queue; + odp_cos_t default_cos; + odp_pool_t default_pool; + odp_pool_t pool; + odp_pool_t recvpool; + odp_pmr_t pmr; + odp_cos_t cos; + char cosname[ODP_COS_NAME_LEN]; + odp_cls_cos_param_t cls_param; + odp_pmr_param_t pmr_param; + odph_ethhdr_t *eth; + + val = CLS_DEFAULT_DMAC; /* 48 bit Ethernet Mac address */ + mask = 0xffffffffffff; + seqno = 0; + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + retval = start_pktio(pktio); + CU_ASSERT(retval == 0); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("dmac", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + pool = pool_create("dmac"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + sprintf(cosname, "dmac"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_DMAC; + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = ODPH_ETHADDR_LEN; + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT(pmr != ODP_PMR_INVAL); + + pkt = create_packet(pkt_pool, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == pool); + CU_ASSERT(retqueue == queue); + odp_packet_free(pkt); + + /* Other packets delivered to default queue */ + pkt = create_packet(pkt_pool, false, &seq, false); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + memset(eth->dst.addr, 0, ODPH_ETHADDR_LEN); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == default_pool); + CU_ASSERT(retqueue == default_queue); + + odp_cos_destroy(cos); + odp_cos_destroy(default_cos); + odp_cls_pmr_destroy(pmr); + odp_packet_free(pkt); + stop_pktio(pktio); + odp_pool_destroy(default_pool); + odp_pool_destroy(pool); + odp_queue_destroy(queue); + odp_queue_destroy(default_queue); + odp_pktio_close(pktio); +} + +void classification_test_pmr_term_packet_len(void) +{ + odp_packet_t pkt; + uint32_t seqno; + uint16_t val; + uint16_t mask; + int retval; + odp_pktio_t pktio; + odp_queue_t queue; + odp_queue_t retqueue; + odp_queue_t default_queue; + odp_cos_t default_cos; + odp_pool_t default_pool; + odp_pool_t pool; + odp_pool_t recvpool; + odp_pmr_t pmr; + odp_cos_t cos; + char cosname[ODP_COS_NAME_LEN]; + odp_cls_cos_param_t cls_param; + odp_pmr_param_t pmr_param; + odph_ethhdr_t *eth; + + val = 1024; + /*Mask value will match any packet of length 1000 - 1099*/ + mask = 0xff00; + seqno = 0; + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + retval = start_pktio(pktio); + CU_ASSERT(retval == 0); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("packet_len", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + pool = pool_create("packet_len"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + sprintf(cosname, "packet_len"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_LEN; + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(val); + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT(pmr != ODP_PMR_INVAL); + + /* create packet of payload length 1024 */ + pkt = create_packet_len(pkt_pool, false, &seq, true, 1024); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == pool); + CU_ASSERT(retqueue == queue); + odp_packet_free(pkt); + + /* Other packets delivered to default queue */ + pkt = create_packet(pkt_pool, false, &seq, false); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == default_pool); + CU_ASSERT(retqueue == default_queue); + + odp_cos_destroy(cos); + odp_cos_destroy(default_cos); + odp_cls_pmr_destroy(pmr); + odp_packet_free(pkt); + stop_pktio(pktio); + odp_pool_destroy(default_pool); + odp_pool_destroy(pool); + odp_queue_destroy(queue); + odp_queue_destroy(default_queue); + odp_pktio_close(pktio); +} + +static void classification_test_pmr_pool_set(void) +{ + odp_packet_t pkt; + uint32_t seqno; + uint8_t val; + uint8_t mask; + int retval; + odp_pktio_t pktio; + odp_queue_t queue; + odp_queue_t retqueue; + odp_queue_t default_queue; + odp_cos_t default_cos; + odp_pool_t default_pool; + odp_pool_t pool; + odp_pool_t pool_new; + odp_pool_t recvpool; + odp_pmr_t pmr; + odp_cos_t cos; + char cosname[ODP_COS_NAME_LEN]; + odp_cls_cos_param_t cls_param; + odp_pmr_param_t pmr_param; + odph_ethhdr_t *eth; + + val = ODPH_IPPROTO_UDP; + mask = 0xff; + seqno = 0; + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + retval = start_pktio(pktio); + CU_ASSERT(retval == 0); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("ipproto1", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + pool = pool_create("ipproto1"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + sprintf(cosname, "ipproto1"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + + pool_new = pool_create("ipproto2"); + CU_ASSERT_FATAL(pool_new != ODP_POOL_INVALID); + + /* new pool is set on CoS */ + retval = odp_cls_cos_pool_set(cos, pool_new); + CU_ASSERT(retval == 0); + + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_IPPROTO; + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(val); + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT(pmr != ODP_PMR_INVAL); + + pkt = create_packet(pkt_pool, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == pool_new); + CU_ASSERT(retqueue == queue); + odp_packet_free(pkt); + + odp_cos_destroy(cos); + odp_cos_destroy(default_cos); + odp_cls_pmr_destroy(pmr); + stop_pktio(pktio); + odp_pool_destroy(default_pool); + odp_pool_destroy(pool); + odp_pool_destroy(pool_new); + odp_queue_destroy(queue); + odp_queue_destroy(default_queue); + odp_pktio_close(pktio); +} + +static void classification_test_pmr_queue_set(void) +{ + odp_packet_t pkt; + uint32_t seqno; + uint8_t val; + uint8_t mask; + int retval; + odp_pktio_t pktio; + odp_queue_t queue; + odp_queue_t retqueue; + odp_queue_t default_queue; + odp_cos_t default_cos; + odp_pool_t default_pool; + odp_pool_t pool; + odp_queue_t queue_new; + odp_pool_t recvpool; + odp_pmr_t pmr; + odp_cos_t cos; + char cosname[ODP_COS_NAME_LEN]; + odp_cls_cos_param_t cls_param; + odp_pmr_param_t pmr_param; + odph_ethhdr_t *eth; + + val = ODPH_IPPROTO_UDP; + mask = 0xff; + seqno = 0; + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + retval = start_pktio(pktio); + CU_ASSERT(retval == 0); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("ipproto1", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + pool = pool_create("ipproto1"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + sprintf(cosname, "ipproto1"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + + queue_new = queue_create("ipproto2", true); + CU_ASSERT_FATAL(queue_new != ODP_QUEUE_INVALID); + + /* new queue is set on CoS */ + retval = odp_cos_queue_set(cos, queue_new); + CU_ASSERT(retval == 0); + + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_IPPROTO; + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(val); + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT(pmr != ODP_PMR_INVAL); + + pkt = create_packet(pkt_pool, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + recvpool = odp_packet_pool(pkt); + CU_ASSERT(recvpool == pool); + CU_ASSERT(retqueue == queue_new); + odp_packet_free(pkt); + + odp_cos_destroy(cos); + odp_cos_destroy(default_cos); + odp_cls_pmr_destroy(pmr); + stop_pktio(pktio); + odp_pool_destroy(default_pool); + odp_pool_destroy(pool); + odp_queue_destroy(queue_new); + odp_queue_destroy(queue); + odp_queue_destroy(default_queue); + odp_pktio_close(pktio); +} + +static void classification_test_pmr_term_daddr(void) +{ + odp_packet_t pkt; + uint32_t seqno; + int retval; + odp_pktio_t pktio; + odp_queue_t queue; + odp_queue_t retqueue; + odp_queue_t default_queue; + odp_pool_t pool; + odp_pool_t default_pool; + odp_pmr_t pmr; + odp_cos_t cos; + odp_cos_t default_cos; + uint32_t addr; + uint32_t mask; + char cosname[ODP_QUEUE_NAME_LEN]; + odp_pmr_param_t pmr_param; + odp_cls_cos_param_t cls_param; + odph_ipv4hdr_t *ip; + const char *dst_addr = "10.0.0.99/32"; + odph_ethhdr_t *eth; + + pktio = create_pktio(ODP_QUEUE_TYPE_SCHED, pkt_pool); + retval = start_pktio(pktio); + CU_ASSERT(retval == 0); + + configure_default_cos(pktio, &default_cos, + &default_queue, &default_pool); + + queue = queue_create("daddr", true); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + pool = pool_create("daddr"); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + sprintf(cosname, "daddr"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos != ODP_COS_INVALID); + + parse_ipv4_string(dst_addr, &addr, &mask); + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_DIP_ADDR; + pmr_param.match.value = &addr; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(addr); + + pmr = odp_cls_pmr_create(&pmr_param, 1, default_cos, cos); + CU_ASSERT_FATAL(pmr != ODP_PMR_INVAL); + + /* packet with dst ip address matching PMR rule to be + received in the CoS queue*/ + pkt = create_packet(pkt_pool, false, &seq, false); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + ip->dst_addr = odp_cpu_to_be_32(addr); + ip->chksum = odph_ipv4_csum_update(pkt); + + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + CU_ASSERT(retqueue == queue); + odp_packet_free(pkt); + + /* Other packets delivered to default queue */ + pkt = create_packet(pkt_pool, false, &seq, false); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + odp_pktio_mac_addr(pktio, eth->src.addr, ODPH_ETHADDR_LEN); + odp_pktio_mac_addr(pktio, eth->dst.addr, ODPH_ETHADDR_LEN); + + enqueue_pktio_interface(pkt, pktio); + + pkt = receive_packet(&retqueue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + CU_ASSERT(retqueue == default_queue); + + odp_cos_destroy(cos); + odp_cos_destroy(default_cos); + odp_cls_pmr_destroy(pmr); + odp_packet_free(pkt); + stop_pktio(pktio); + odp_pool_destroy(default_pool); + odp_pool_destroy(pool); + odp_queue_destroy(queue); + odp_queue_destroy(default_queue); + odp_pktio_close(pktio); +} + +odp_testinfo_t classification_suite_pmr[] = { + ODP_TEST_INFO(classification_test_pmr_term_tcp_dport), + ODP_TEST_INFO(classification_test_pmr_term_tcp_sport), + ODP_TEST_INFO(classification_test_pmr_term_udp_dport), + ODP_TEST_INFO(classification_test_pmr_term_udp_sport), + ODP_TEST_INFO(classification_test_pmr_term_ipproto), + ODP_TEST_INFO(classification_test_pmr_term_dmac), + ODP_TEST_INFO(classification_test_pmr_pool_set), + ODP_TEST_INFO(classification_test_pmr_queue_set), + ODP_TEST_INFO(classification_test_pmr_term_daddr), + ODP_TEST_INFO(classification_test_pmr_term_packet_len), + ODP_TEST_INFO_NULL, +}; diff --git a/test/common_plat/validation/api/classification/odp_classification_tests.c b/test/common_plat/validation/api/classification/odp_classification_tests.c new file mode 100644 index 000000000..ed45518be --- /dev/null +++ b/test/common_plat/validation/api/classification/odp_classification_tests.c @@ -0,0 +1,699 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "odp_classification_testsuites.h" +#include "classification.h" +#include <odp_cunit_common.h> +#include <odp/helper/eth.h> +#include <odp/helper/ip.h> +#include <odp/helper/udp.h> +#include <odp/helper/tcp.h> + +static odp_cos_t cos_list[CLS_ENTRIES]; +static odp_pmr_t pmr_list[CLS_ENTRIES]; +static odp_queue_t queue_list[CLS_ENTRIES]; +static odp_pool_t pool_list[CLS_ENTRIES]; + +static odp_pool_t pool_default; +static odp_pktio_t pktio_loop; + +/** sequence number of IP packets */ +odp_atomic_u32_t seq; + +int classification_suite_init(void) +{ + int i; + int ret; + odp_pktio_param_t pktio_param; + odp_pktin_queue_param_t pktin_param; + + pool_default = pool_create("classification_pool"); + if (ODP_POOL_INVALID == pool_default) { + fprintf(stderr, "Packet pool creation failed.\n"); + return -1; + } + + odp_pktio_param_init(&pktio_param); + pktio_param.in_mode = ODP_PKTIN_MODE_SCHED; + + pktio_loop = odp_pktio_open("loop", pool_default, &pktio_param); + if (pktio_loop == ODP_PKTIO_INVALID) { + ret = odp_pool_destroy(pool_default); + if (ret) + fprintf(stderr, "unable to destroy pool.\n"); + return -1; + } + + odp_pktin_queue_param_init(&pktin_param); + pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC; + + if (odp_pktin_queue_config(pktio_loop, &pktin_param)) { + fprintf(stderr, "pktin queue config failed.\n"); + return -1; + } + + if (odp_pktout_queue_config(pktio_loop, NULL)) { + fprintf(stderr, "pktout queue config failed.\n"); + return -1; + } + + for (i = 0; i < CLS_ENTRIES; i++) + cos_list[i] = ODP_COS_INVALID; + + for (i = 0; i < CLS_ENTRIES; i++) + pmr_list[i] = ODP_PMR_INVAL; + + for (i = 0; i < CLS_ENTRIES; i++) + queue_list[i] = ODP_QUEUE_INVALID; + + for (i = 0; i < CLS_ENTRIES; i++) + pool_list[i] = ODP_POOL_INVALID; + + odp_atomic_init_u32(&seq, 0); + + ret = odp_pktio_start(pktio_loop); + if (ret) { + fprintf(stderr, "unable to start loop\n"); + return -1; + } + + return 0; +} + +int classification_suite_term(void) +{ + int i; + int retcode = 0; + + if (0 > stop_pktio(pktio_loop)) { + fprintf(stderr, "stop pktio failed.\n"); + retcode = -1; + } + + if (0 > odp_pktio_close(pktio_loop)) { + fprintf(stderr, "pktio close failed.\n"); + retcode = -1; + } + + if (0 != odp_pool_destroy(pool_default)) { + fprintf(stderr, "pool_default destroy failed.\n"); + retcode = -1; + } + + for (i = 0; i < CLS_ENTRIES; i++) + odp_cos_destroy(cos_list[i]); + + for (i = 0; i < CLS_ENTRIES; i++) + odp_cls_pmr_destroy(pmr_list[i]); + + for (i = 0; i < CLS_ENTRIES; i++) + odp_queue_destroy(queue_list[i]); + + for (i = 0; i < CLS_ENTRIES; i++) + odp_pool_destroy(pool_list[i]); + + return retcode; +} + +void configure_cls_pmr_chain(void) +{ + /* PKTIO --> PMR_SRC(SRC IP ADDR) --> PMR_DST (TCP SPORT) */ + + /* Packet matching only the SRC IP ADDR should be delivered + in queue[CLS_PMR_CHAIN_SRC] and a packet matching both SRC IP ADDR and + TCP SPORT should be delivered to queue[CLS_PMR_CHAIN_DST] */ + + uint16_t val; + uint16_t maskport; + char cosname[ODP_QUEUE_NAME_LEN]; + odp_queue_param_t qparam; + odp_cls_cos_param_t cls_param; + char queuename[ODP_QUEUE_NAME_LEN]; + char poolname[ODP_POOL_NAME_LEN]; + uint32_t addr; + uint32_t mask; + odp_pmr_param_t pmr_param; + odp_queue_capability_t queue_capa; + + CU_ASSERT_FATAL(odp_queue_capability(&queue_capa) == 0); + + odp_queue_param_init(&qparam); + qparam.type = ODP_QUEUE_TYPE_SCHED; + qparam.sched.prio = ODP_SCHED_PRIO_NORMAL; + qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qparam.sched.group = ODP_SCHED_GROUP_ALL; + qparam.sched.lock_count = queue_capa.max_ordered_locks; + sprintf(queuename, "%s", "SrcQueue"); + + queue_list[CLS_PMR_CHAIN_SRC] = odp_queue_create(queuename, &qparam); + + CU_ASSERT_FATAL(queue_list[CLS_PMR_CHAIN_SRC] != ODP_QUEUE_INVALID); + + sprintf(poolname, "%s", "SrcPool"); + pool_list[CLS_PMR_CHAIN_SRC] = pool_create(poolname); + CU_ASSERT_FATAL(pool_list[CLS_PMR_CHAIN_SRC] != ODP_POOL_INVALID); + + sprintf(cosname, "SrcCos"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool_list[CLS_PMR_CHAIN_SRC]; + cls_param.queue = queue_list[CLS_PMR_CHAIN_SRC]; + cls_param.drop_policy = ODP_COS_DROP_POOL; + + cos_list[CLS_PMR_CHAIN_SRC] = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos_list[CLS_PMR_CHAIN_SRC] != ODP_COS_INVALID); + + odp_queue_param_init(&qparam); + qparam.type = ODP_QUEUE_TYPE_SCHED; + qparam.sched.prio = ODP_SCHED_PRIO_NORMAL; + qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qparam.sched.group = ODP_SCHED_GROUP_ALL; + sprintf(queuename, "%s", "DstQueue"); + + queue_list[CLS_PMR_CHAIN_DST] = odp_queue_create(queuename, &qparam); + CU_ASSERT_FATAL(queue_list[CLS_PMR_CHAIN_DST] != ODP_QUEUE_INVALID); + + sprintf(poolname, "%s", "DstPool"); + pool_list[CLS_PMR_CHAIN_DST] = pool_create(poolname); + CU_ASSERT_FATAL(pool_list[CLS_PMR_CHAIN_DST] != ODP_POOL_INVALID); + + sprintf(cosname, "DstCos"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool_list[CLS_PMR_CHAIN_DST]; + cls_param.queue = queue_list[CLS_PMR_CHAIN_DST]; + cls_param.drop_policy = ODP_COS_DROP_POOL; + cos_list[CLS_PMR_CHAIN_DST] = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos_list[CLS_PMR_CHAIN_DST] != ODP_COS_INVALID); + + parse_ipv4_string(CLS_PMR_CHAIN_SADDR, &addr, &mask); + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = ODP_PMR_SIP_ADDR; + pmr_param.match.value = &addr; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(addr); + pmr_list[CLS_PMR_CHAIN_SRC] = + odp_cls_pmr_create(&pmr_param, 1, cos_list[CLS_DEFAULT], + cos_list[CLS_PMR_CHAIN_SRC]); + CU_ASSERT_FATAL(pmr_list[CLS_PMR_CHAIN_SRC] != ODP_PMR_INVAL); + + val = CLS_PMR_CHAIN_PORT; + maskport = 0xffff; + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = find_first_supported_l3_pmr(); + pmr_param.match.value = &val; + pmr_param.match.mask = &maskport; + pmr_param.val_sz = sizeof(val); + pmr_list[CLS_PMR_CHAIN_DST] = + odp_cls_pmr_create(&pmr_param, 1, cos_list[CLS_PMR_CHAIN_SRC], + cos_list[CLS_PMR_CHAIN_DST]); + CU_ASSERT_FATAL(pmr_list[CLS_PMR_CHAIN_DST] != ODP_PMR_INVAL); +} + +void test_cls_pmr_chain(void) +{ + odp_packet_t pkt; + odph_ipv4hdr_t *ip; + odp_queue_t queue; + odp_pool_t pool; + uint32_t addr = 0; + uint32_t mask; + uint32_t seqno = 0; + + pkt = create_packet(pool_default, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + parse_ipv4_string(CLS_PMR_CHAIN_SADDR, &addr, &mask); + ip->src_addr = odp_cpu_to_be_32(addr); + ip->chksum = 0; + ip->chksum = odph_ipv4_csum_update(pkt); + + set_first_supported_pmr_port(pkt, CLS_PMR_CHAIN_PORT); + + enqueue_pktio_interface(pkt, pktio_loop); + + pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(queue == queue_list[CLS_PMR_CHAIN_DST]); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + pool = odp_packet_pool(pkt); + CU_ASSERT(pool == pool_list[CLS_PMR_CHAIN_DST]); + odp_packet_free(pkt); + + pkt = create_packet(pool_default, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + parse_ipv4_string(CLS_PMR_CHAIN_SADDR, &addr, &mask); + ip->src_addr = odp_cpu_to_be_32(addr); + ip->chksum = 0; + ip->chksum = odph_ipv4_csum_update(pkt); + + enqueue_pktio_interface(pkt, pktio_loop); + pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(queue == queue_list[CLS_PMR_CHAIN_SRC]); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + pool = odp_packet_pool(pkt); + CU_ASSERT(pool == pool_list[CLS_PMR_CHAIN_SRC]); + odp_packet_free(pkt); +} + +void configure_pktio_default_cos(void) +{ + int retval; + odp_queue_param_t qparam; + odp_cls_cos_param_t cls_param; + char cosname[ODP_COS_NAME_LEN]; + char queuename[ODP_QUEUE_NAME_LEN]; + char poolname[ODP_POOL_NAME_LEN]; + + odp_queue_param_init(&qparam); + qparam.type = ODP_QUEUE_TYPE_SCHED; + qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT; + qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qparam.sched.group = ODP_SCHED_GROUP_ALL; + sprintf(queuename, "%s", "DefaultQueue"); + queue_list[CLS_DEFAULT] = odp_queue_create(queuename, &qparam); + CU_ASSERT_FATAL(queue_list[CLS_DEFAULT] != ODP_QUEUE_INVALID); + + sprintf(poolname, "DefaultPool"); + pool_list[CLS_DEFAULT] = pool_create(poolname); + CU_ASSERT_FATAL(pool_list[CLS_DEFAULT] != ODP_POOL_INVALID); + + sprintf(cosname, "DefaultCoS"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool_list[CLS_DEFAULT]; + cls_param.queue = queue_list[CLS_DEFAULT]; + cls_param.drop_policy = ODP_COS_DROP_POOL; + cos_list[CLS_DEFAULT] = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos_list[CLS_DEFAULT] != ODP_COS_INVALID); + + retval = odp_pktio_default_cos_set(pktio_loop, cos_list[CLS_DEFAULT]); + CU_ASSERT(retval == 0); +} + +void test_pktio_default_cos(void) +{ + odp_packet_t pkt; + odp_queue_t queue; + uint32_t seqno = 0; + odp_pool_t pool; + /* create a default packet */ + pkt = create_packet(pool_default, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + + enqueue_pktio_interface(pkt, pktio_loop); + + pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + /* Default packet should be received in default queue */ + CU_ASSERT(queue == queue_list[CLS_DEFAULT]); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + pool = odp_packet_pool(pkt); + CU_ASSERT(pool == pool_list[CLS_DEFAULT]); + + odp_packet_free(pkt); +} + +void configure_pktio_error_cos(void) +{ + int retval; + odp_queue_param_t qparam; + odp_cls_cos_param_t cls_param; + char queuename[ODP_QUEUE_NAME_LEN]; + char cosname[ODP_COS_NAME_LEN]; + char poolname[ODP_POOL_NAME_LEN]; + + odp_queue_param_init(&qparam); + qparam.type = ODP_QUEUE_TYPE_SCHED; + qparam.sched.prio = ODP_SCHED_PRIO_LOWEST; + qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qparam.sched.group = ODP_SCHED_GROUP_ALL; + sprintf(queuename, "%s", "ErrorCos"); + + queue_list[CLS_ERROR] = odp_queue_create(queuename, &qparam); + CU_ASSERT_FATAL(queue_list[CLS_ERROR] != ODP_QUEUE_INVALID); + + sprintf(poolname, "ErrorPool"); + pool_list[CLS_ERROR] = pool_create(poolname); + CU_ASSERT_FATAL(pool_list[CLS_ERROR] != ODP_POOL_INVALID); + + sprintf(cosname, "%s", "ErrorCos"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool_list[CLS_ERROR]; + cls_param.queue = queue_list[CLS_ERROR]; + cls_param.drop_policy = ODP_COS_DROP_POOL; + cos_list[CLS_ERROR] = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos_list[CLS_ERROR] != ODP_COS_INVALID); + + retval = odp_pktio_error_cos_set(pktio_loop, cos_list[CLS_ERROR]); + CU_ASSERT(retval == 0); +} + +void test_pktio_error_cos(void) +{ + odp_queue_t queue; + odp_packet_t pkt; + odp_pool_t pool; + + /*Create an error packet */ + pkt = create_packet(pool_default, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + + /* Incorrect IpV4 version */ + ip->ver_ihl = 8 << 4 | ODPH_IPV4HDR_IHL_MIN; + ip->chksum = 0; + enqueue_pktio_interface(pkt, pktio_loop); + + pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + /* Error packet should be received in error queue */ + CU_ASSERT(queue == queue_list[CLS_ERROR]); + pool = odp_packet_pool(pkt); + CU_ASSERT(pool == pool_list[CLS_ERROR]); + odp_packet_free(pkt); +} + +void classification_test_pktio_set_skip(void) +{ + int retval; + size_t offset = 5; + + retval = odp_pktio_skip_set(pktio_loop, offset); + CU_ASSERT(retval == 0); + + retval = odp_pktio_skip_set(ODP_PKTIO_INVALID, offset); + CU_ASSERT(retval < 0); + + /* reset skip value to zero as validation suite expects + offset to be zero*/ + + retval = odp_pktio_skip_set(pktio_loop, 0); + CU_ASSERT(retval == 0); +} + +void classification_test_pktio_set_headroom(void) +{ + size_t headroom; + int retval; + + headroom = 5; + retval = odp_pktio_headroom_set(pktio_loop, headroom); + CU_ASSERT(retval == 0); + + retval = odp_pktio_headroom_set(ODP_PKTIO_INVALID, headroom); + CU_ASSERT(retval < 0); +} + +void configure_cos_with_l2_priority(void) +{ + uint8_t num_qos = CLS_L2_QOS_MAX; + odp_cos_t cos_tbl[CLS_L2_QOS_MAX]; + odp_queue_t queue_tbl[CLS_L2_QOS_MAX]; + odp_pool_t pool; + uint8_t qos_tbl[CLS_L2_QOS_MAX]; + char cosname[ODP_COS_NAME_LEN]; + char queuename[ODP_QUEUE_NAME_LEN]; + char poolname[ODP_POOL_NAME_LEN]; + int retval; + int i; + odp_queue_param_t qparam; + odp_cls_cos_param_t cls_param; + + /** Initialize scalar variable qos_tbl **/ + for (i = 0; i < CLS_L2_QOS_MAX; i++) + qos_tbl[i] = 0; + + odp_queue_param_init(&qparam); + qparam.type = ODP_QUEUE_TYPE_SCHED; + qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qparam.sched.group = ODP_SCHED_GROUP_ALL; + for (i = 0; i < num_qos; i++) { + qparam.sched.prio = ODP_SCHED_PRIO_LOWEST - i; + sprintf(queuename, "%s_%d", "L2_Queue", i); + queue_tbl[i] = odp_queue_create(queuename, &qparam); + CU_ASSERT_FATAL(queue_tbl[i] != ODP_QUEUE_INVALID); + queue_list[CLS_L2_QOS_0 + i] = queue_tbl[i]; + + sprintf(poolname, "%s_%d", "L2_Pool", i); + pool = pool_create(poolname); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + pool_list[CLS_L2_QOS_0 + i] = pool; + + sprintf(cosname, "%s_%d", "L2_Cos", i); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool; + cls_param.queue = queue_tbl[i]; + cls_param.drop_policy = ODP_COS_DROP_POOL; + cos_tbl[i] = odp_cls_cos_create(cosname, &cls_param); + if (cos_tbl[i] == ODP_COS_INVALID) + break; + + cos_list[CLS_L2_QOS_0 + i] = cos_tbl[i]; + qos_tbl[i] = i; + } + /* count 'i' is passed instead of num_qos to handle the rare scenario + if the odp_cls_cos_create() failed in the middle*/ + retval = odp_cos_with_l2_priority(pktio_loop, i, qos_tbl, cos_tbl); + CU_ASSERT(retval == 0); +} + +void test_cos_with_l2_priority(void) +{ + odp_packet_t pkt; + odph_ethhdr_t *ethhdr; + odph_vlanhdr_t *vlan; + odp_queue_t queue; + odp_pool_t pool; + uint32_t seqno = 0; + uint8_t i; + + for (i = 0; i < CLS_L2_QOS_MAX; i++) { + pkt = create_packet(pool_default, true, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + ethhdr = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + vlan = (odph_vlanhdr_t *)(ethhdr + 1); + vlan->tci = odp_cpu_to_be_16(i << 13); + enqueue_pktio_interface(pkt, pktio_loop); + pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(queue == queue_list[CLS_L2_QOS_0 + i]); + pool = odp_packet_pool(pkt); + CU_ASSERT(pool == pool_list[CLS_L2_QOS_0 + i]); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + odp_packet_free(pkt); + } +} + +void configure_pmr_cos(void) +{ + uint16_t val; + uint16_t mask; + odp_pmr_param_t pmr_param; + odp_queue_param_t qparam; + odp_cls_cos_param_t cls_param; + char cosname[ODP_COS_NAME_LEN]; + char queuename[ODP_QUEUE_NAME_LEN]; + char poolname[ODP_POOL_NAME_LEN]; + + odp_queue_param_init(&qparam); + qparam.type = ODP_QUEUE_TYPE_SCHED; + qparam.sched.prio = ODP_SCHED_PRIO_HIGHEST; + qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qparam.sched.group = ODP_SCHED_GROUP_ALL; + sprintf(queuename, "%s", "PMR_CoS"); + + queue_list[CLS_PMR] = odp_queue_create(queuename, &qparam); + CU_ASSERT_FATAL(queue_list[CLS_PMR] != ODP_QUEUE_INVALID); + + sprintf(poolname, "PMR_Pool"); + pool_list[CLS_PMR] = pool_create(poolname); + CU_ASSERT_FATAL(pool_list[CLS_PMR] != ODP_POOL_INVALID); + + sprintf(cosname, "PMR_CoS"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool_list[CLS_PMR]; + cls_param.queue = queue_list[CLS_PMR]; + cls_param.drop_policy = ODP_COS_DROP_POOL; + cos_list[CLS_PMR] = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos_list[CLS_PMR] != ODP_COS_INVALID); + + val = CLS_PMR_PORT; + mask = 0xffff; + odp_cls_pmr_param_init(&pmr_param); + pmr_param.term = find_first_supported_l3_pmr(); + pmr_param.match.value = &val; + pmr_param.match.mask = &mask; + pmr_param.val_sz = sizeof(val); + + pmr_list[CLS_PMR] = odp_cls_pmr_create(&pmr_param, 1, + cos_list[CLS_DEFAULT], + cos_list[CLS_PMR]); + CU_ASSERT_FATAL(pmr_list[CLS_PMR] != ODP_PMR_INVAL); +} + +void test_pmr_cos(void) +{ + odp_packet_t pkt; + odp_queue_t queue; + odp_pool_t pool; + uint32_t seqno = 0; + + pkt = create_packet(pool_default, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + set_first_supported_pmr_port(pkt, CLS_PMR_PORT); + enqueue_pktio_interface(pkt, pktio_loop); + pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(queue == queue_list[CLS_PMR]); + pool = odp_packet_pool(pkt); + CU_ASSERT(pool == pool_list[CLS_PMR]); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + odp_packet_free(pkt); +} + +void configure_pktio_pmr_composite(void) +{ + odp_pmr_param_t pmr_params[2]; + uint16_t val; + uint16_t maskport; + int num_terms = 2; /* one pmr for each L3 and L4 */ + odp_queue_param_t qparam; + odp_cls_cos_param_t cls_param; + char cosname[ODP_COS_NAME_LEN]; + char queuename[ODP_QUEUE_NAME_LEN]; + char poolname[ODP_POOL_NAME_LEN]; + uint32_t addr = 0; + uint32_t mask; + + odp_queue_param_init(&qparam); + qparam.type = ODP_QUEUE_TYPE_SCHED; + qparam.sched.prio = ODP_SCHED_PRIO_HIGHEST; + qparam.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qparam.sched.group = ODP_SCHED_GROUP_ALL; + sprintf(queuename, "%s", "cos_pmr_composite_queue"); + + queue_list[CLS_PMR_SET] = odp_queue_create(queuename, &qparam); + CU_ASSERT_FATAL(queue_list[CLS_PMR_SET] != ODP_QUEUE_INVALID); + + sprintf(poolname, "cos_pmr_composite_pool"); + pool_list[CLS_PMR_SET] = pool_create(poolname); + CU_ASSERT_FATAL(pool_list[CLS_PMR_SET] != ODP_POOL_INVALID); + + sprintf(cosname, "cos_pmr_composite"); + odp_cls_cos_param_init(&cls_param); + cls_param.pool = pool_list[CLS_PMR_SET]; + cls_param.queue = queue_list[CLS_PMR_SET]; + cls_param.drop_policy = ODP_COS_DROP_POOL; + cos_list[CLS_PMR_SET] = odp_cls_cos_create(cosname, &cls_param); + CU_ASSERT_FATAL(cos_list[CLS_PMR_SET] != ODP_COS_INVALID); + + parse_ipv4_string(CLS_PMR_SET_SADDR, &addr, &mask); + odp_cls_pmr_param_init(&pmr_params[0]); + pmr_params[0].term = ODP_PMR_SIP_ADDR; + pmr_params[0].match.value = &addr; + pmr_params[0].match.mask = &mask; + pmr_params[0].val_sz = sizeof(addr); + + val = CLS_PMR_SET_PORT; + maskport = 0xffff; + odp_cls_pmr_param_init(&pmr_params[1]); + pmr_params[1].term = find_first_supported_l3_pmr(); + pmr_params[1].match.value = &val; + pmr_params[1].match.mask = &maskport; + pmr_params[1].range_term = false; + pmr_params[1].val_sz = sizeof(val); + + pmr_list[CLS_PMR_SET] = odp_cls_pmr_create(pmr_params, num_terms, + cos_list[CLS_DEFAULT], + cos_list[CLS_PMR_SET]); + CU_ASSERT_FATAL(pmr_list[CLS_PMR_SET] != ODP_PMR_INVAL); +} + +void test_pktio_pmr_composite_cos(void) +{ + uint32_t addr = 0; + uint32_t mask; + odph_ipv4hdr_t *ip; + odp_packet_t pkt; + odp_pool_t pool; + odp_queue_t queue; + uint32_t seqno = 0; + + pkt = create_packet(pool_default, false, &seq, true); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + seqno = cls_pkt_get_seq(pkt); + CU_ASSERT(seqno != TEST_SEQ_INVALID); + + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + parse_ipv4_string(CLS_PMR_SET_SADDR, &addr, &mask); + ip->src_addr = odp_cpu_to_be_32(addr); + ip->chksum = 0; + ip->chksum = odph_ipv4_csum_update(pkt); + + set_first_supported_pmr_port(pkt, CLS_PMR_SET_PORT); + enqueue_pktio_interface(pkt, pktio_loop); + pkt = receive_packet(&queue, ODP_TIME_SEC_IN_NS); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(queue == queue_list[CLS_PMR_SET]); + pool = odp_packet_pool(pkt); + CU_ASSERT(pool == pool_list[CLS_PMR_SET]); + CU_ASSERT(seqno == cls_pkt_get_seq(pkt)); + odp_packet_free(pkt); +} + +void classification_test_pktio_configure(void) +{ + /* Configure the Different CoS for the pktio interface */ + if (TEST_DEFAULT) + configure_pktio_default_cos(); + if (TEST_ERROR) + configure_pktio_error_cos(); + if (TEST_PMR_CHAIN) + configure_cls_pmr_chain(); + if (TEST_L2_QOS) + configure_cos_with_l2_priority(); + if (TEST_PMR) + configure_pmr_cos(); + if (TEST_PMR_SET) + configure_pktio_pmr_composite(); +} + +void classification_test_pktio_test(void) +{ + /* Test Different CoS on the pktio interface */ + if (TEST_DEFAULT) + test_pktio_default_cos(); + if (TEST_ERROR) + test_pktio_error_cos(); + if (TEST_PMR_CHAIN) + test_cls_pmr_chain(); + if (TEST_L2_QOS) + test_cos_with_l2_priority(); + if (TEST_PMR) + test_pmr_cos(); + if (TEST_PMR_SET) + test_pktio_pmr_composite_cos(); +} + +odp_testinfo_t classification_suite[] = { + ODP_TEST_INFO(classification_test_pktio_set_skip), + ODP_TEST_INFO(classification_test_pktio_set_headroom), + ODP_TEST_INFO(classification_test_pktio_configure), + ODP_TEST_INFO(classification_test_pktio_test), + ODP_TEST_INFO_NULL, +}; diff --git a/test/common_plat/validation/api/classification/odp_classification_testsuites.h b/test/common_plat/validation/api/classification/odp_classification_testsuites.h new file mode 100644 index 000000000..aea3de1b1 --- /dev/null +++ b/test/common_plat/validation/api/classification/odp_classification_testsuites.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ODP_CLASSIFICATION_TESTSUITES_H_ +#define ODP_CLASSIFICATION_TESTSUITES_H_ + +#include <odp_api.h> +#include <odp_cunit_common.h> +#include <stdbool.h> + +extern odp_testinfo_t classification_suite[]; +extern odp_testinfo_t classification_suite_basic[]; +extern odp_testinfo_t classification_suite_pmr[]; + +int classification_suite_init(void); +int classification_suite_term(void); + +int classification_suite_pmr_term(void); +int classification_suite_pmr_init(void); + +odp_packet_t create_packet(odp_pool_t pool, bool vlan, + odp_atomic_u32_t *seq, bool udp); +odp_packet_t create_packet_len(odp_pool_t pool, bool vlan, + odp_atomic_u32_t *seq, bool flag_udp, + uint16_t len); +int cls_pkt_set_seq(odp_packet_t pkt); +uint32_t cls_pkt_get_seq(odp_packet_t pkt); +odp_pktio_t create_pktio(odp_queue_type_t q_type, odp_pool_t pool); +void configure_default_cos(odp_pktio_t pktio, odp_cos_t *cos, + odp_queue_t *queue, odp_pool_t *pool); +int parse_ipv4_string(const char *ipaddress, uint32_t *addr, uint32_t *mask); +void enqueue_pktio_interface(odp_packet_t pkt, odp_pktio_t pktio); +odp_packet_t receive_packet(odp_queue_t *queue, uint64_t ns); +odp_pool_t pool_create(const char *poolname); +odp_queue_t queue_create(const char *queuename, bool sched); +void configure_pktio_default_cos(void); +void test_pktio_default_cos(void); +void configure_pktio_error_cos(void); +void test_pktio_error_cos(void); +void configure_cls_pmr_chain(void); +void test_cls_pmr_chain(void); +void configure_cos_with_l2_priority(void); +void test_cos_with_l2_priority(void); +void configure_pmr_cos(void); +void test_pmr_cos(void); +void configure_pktio_pmr_composite(void); +void test_pktio_pmr_composite_cos(void); +int stop_pktio(odp_pktio_t pktio); +odp_cls_pmr_term_t find_first_supported_l3_pmr(void); +int set_first_supported_pmr_port(odp_packet_t pkt, uint16_t port); + +#endif /* ODP_BUFFER_TESTSUITES_H_ */ diff --git a/test/common_plat/validation/api/cpumask/.gitignore b/test/common_plat/validation/api/cpumask/.gitignore new file mode 100644 index 000000000..655a1640f --- /dev/null +++ b/test/common_plat/validation/api/cpumask/.gitignore @@ -0,0 +1 @@ +cpumask_main diff --git a/test/common_plat/validation/api/cpumask/Makefile.am b/test/common_plat/validation/api/cpumask/Makefile.am new file mode 100644 index 000000000..ec5fce338 --- /dev/null +++ b/test/common_plat/validation/api/cpumask/Makefile.am @@ -0,0 +1,11 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestcpumask.la +libtestcpumask_la_SOURCES = cpumask.c +libtestcpumask_la_LIBADD = $(LIBCPUMASK_COMMON) + +test_PROGRAMS = cpumask_main$(EXEEXT) +dist_cpumask_main_SOURCES = cpumask_main.c +cpumask_main_LDADD = libtestcpumask.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = cpumask.h diff --git a/test/common_plat/validation/api/cpumask/cpumask.c b/test/common_plat/validation/api/cpumask/cpumask.c new file mode 100644 index 000000000..a0cb559fb --- /dev/null +++ b/test/common_plat/validation/api/cpumask/cpumask.c @@ -0,0 +1,116 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> + +#include "odp_cunit_common.h" +#include "cpumask.h" +#include "mask_common.h" + +/* default worker parameter to get all that may be available */ +#define ALL_AVAILABLE 0 + +void cpumask_test_odp_cpumask_def_control(void) +{ + unsigned num; + unsigned mask_count; + unsigned max_cpus = mask_capacity(); + odp_cpumask_t mask; + + num = odp_cpumask_default_control(&mask, ALL_AVAILABLE); + mask_count = odp_cpumask_count(&mask); + + CU_ASSERT(mask_count == num); + CU_ASSERT(num > 0); + CU_ASSERT(num <= max_cpus); +} + +void cpumask_test_odp_cpumask_def_worker(void) +{ + unsigned num; + unsigned mask_count; + unsigned max_cpus = mask_capacity(); + odp_cpumask_t mask; + + num = odp_cpumask_default_worker(&mask, ALL_AVAILABLE); + mask_count = odp_cpumask_count(&mask); + + CU_ASSERT(mask_count == num); + CU_ASSERT(num > 0); + CU_ASSERT(num <= max_cpus); +} + +void cpumask_test_odp_cpumask_def(void) +{ + unsigned mask_count; + unsigned num_worker; + unsigned num_control; + unsigned max_cpus = mask_capacity(); + unsigned available_cpus = odp_cpu_count(); + unsigned requested_cpus; + odp_cpumask_t mask; + + CU_ASSERT(available_cpus <= max_cpus); + + if (available_cpus > 1) + requested_cpus = available_cpus - 1; + else + requested_cpus = available_cpus; + num_worker = odp_cpumask_default_worker(&mask, requested_cpus); + mask_count = odp_cpumask_count(&mask); + CU_ASSERT(mask_count == num_worker); + + num_control = odp_cpumask_default_control(&mask, 1); + mask_count = odp_cpumask_count(&mask); + CU_ASSERT(mask_count == num_control); + + CU_ASSERT(num_control >= 1); + CU_ASSERT(num_worker <= available_cpus); + CU_ASSERT(num_worker > 0); +} + +odp_testinfo_t cpumask_suite[] = { + ODP_TEST_INFO(cpumask_test_odp_cpumask_to_from_str), + ODP_TEST_INFO(cpumask_test_odp_cpumask_equal), + ODP_TEST_INFO(cpumask_test_odp_cpumask_zero), + ODP_TEST_INFO(cpumask_test_odp_cpumask_set), + ODP_TEST_INFO(cpumask_test_odp_cpumask_clr), + ODP_TEST_INFO(cpumask_test_odp_cpumask_isset), + ODP_TEST_INFO(cpumask_test_odp_cpumask_count), + ODP_TEST_INFO(cpumask_test_odp_cpumask_and), + ODP_TEST_INFO(cpumask_test_odp_cpumask_or), + ODP_TEST_INFO(cpumask_test_odp_cpumask_xor), + ODP_TEST_INFO(cpumask_test_odp_cpumask_copy), + ODP_TEST_INFO(cpumask_test_odp_cpumask_first), + ODP_TEST_INFO(cpumask_test_odp_cpumask_last), + ODP_TEST_INFO(cpumask_test_odp_cpumask_next), + ODP_TEST_INFO(cpumask_test_odp_cpumask_setall), + ODP_TEST_INFO(cpumask_test_odp_cpumask_def_control), + ODP_TEST_INFO(cpumask_test_odp_cpumask_def_worker), + ODP_TEST_INFO(cpumask_test_odp_cpumask_def), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t cpumask_suites[] = { + {"Cpumask", NULL, NULL, cpumask_suite}, + ODP_SUITE_INFO_NULL, +}; + +int cpumask_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(cpumask_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/cpumask/cpumask.h b/test/common_plat/validation/api/cpumask/cpumask.h new file mode 100644 index 000000000..87a4512bf --- /dev/null +++ b/test/common_plat/validation/api/cpumask/cpumask.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_CPUMASK_H_ +#define _ODP_TEST_CPUMASK_H_ + +#include <odp_api.h> +#include <odp_cunit_common.h> + +/* test functions: */ +#include "mask_common.h" +void cpumask_test_odp_cpumask_def_control(void); +void cpumask_test_odp_cpumask_def_worker(void); +void cpumask_test_odp_cpumask_def(void); + +/* test arrays: */ +extern odp_testinfo_t cpumask_suite[]; + +/* test registry: */ +extern odp_suiteinfo_t cpumask_suites[]; + +/* main test program: */ +int cpumask_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/cpumask/cpumask_main.c b/test/common_plat/validation/api/cpumask/cpumask_main.c new file mode 100644 index 000000000..39e3171ca --- /dev/null +++ b/test/common_plat/validation/api/cpumask/cpumask_main.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "cpumask.h" + +int main(int argc, char *argv[]) +{ + return cpumask_main(argc, argv); +} diff --git a/test/common_plat/validation/api/crypto/.gitignore b/test/common_plat/validation/api/crypto/.gitignore new file mode 100644 index 000000000..0ac55e35e --- /dev/null +++ b/test/common_plat/validation/api/crypto/.gitignore @@ -0,0 +1 @@ +crypto_main diff --git a/test/common_plat/validation/api/crypto/Makefile.am b/test/common_plat/validation/api/crypto/Makefile.am new file mode 100644 index 000000000..3ea41b41f --- /dev/null +++ b/test/common_plat/validation/api/crypto/Makefile.am @@ -0,0 +1,11 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestcrypto.la +libtestcrypto_la_SOURCES = crypto.c \ + odp_crypto_test_inp.c + +test_PROGRAMS = crypto_main$(EXEEXT) +dist_crypto_main_SOURCES = crypto_main.c +crypto_main_LDADD = libtestcrypto.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = crypto.h odp_crypto_test_inp.h test_vectors.h test_vectors_len.h diff --git a/test/common_plat/validation/api/crypto/crypto.c b/test/common_plat/validation/api/crypto/crypto.c new file mode 100644 index 000000000..8946cde62 --- /dev/null +++ b/test/common_plat/validation/api/crypto/crypto.c @@ -0,0 +1,121 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include <odp_cunit_common.h> +#include "odp_crypto_test_inp.h" +#include "crypto.h" + +#define SHM_PKT_POOL_SIZE (512 * 2048 * 2) +#define SHM_PKT_POOL_BUF_SIZE (1024 * 32) + +#define SHM_COMPL_POOL_SIZE (128 * 1024) +#define SHM_COMPL_POOL_BUF_SIZE 128 + +odp_suiteinfo_t crypto_suites[] = { + {ODP_CRYPTO_SYNC_INP, crypto_suite_sync_init, NULL, crypto_suite}, + {ODP_CRYPTO_ASYNC_INP, crypto_suite_async_init, NULL, crypto_suite}, + ODP_SUITE_INFO_NULL, +}; + +int crypto_init(odp_instance_t *inst) +{ + odp_pool_param_t params; + odp_pool_t pool; + odp_queue_t out_queue; + odp_pool_capability_t pool_capa; + + if (0 != odp_init_global(inst, NULL, NULL)) { + fprintf(stderr, "error: odp_init_global() failed.\n"); + return -1; + } + + if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) { + fprintf(stderr, "error: odp_init_local() failed.\n"); + return -1; + } + + if (odp_pool_capability(&pool_capa) < 0) { + fprintf(stderr, "error: odp_pool_capability() failed.\n"); + return -1; + } + + memset(¶ms, 0, sizeof(params)); + params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE; + params.pkt.len = SHM_PKT_POOL_BUF_SIZE; + params.pkt.num = SHM_PKT_POOL_SIZE / SHM_PKT_POOL_BUF_SIZE; + params.type = ODP_POOL_PACKET; + + if (SHM_PKT_POOL_BUF_SIZE > pool_capa.pkt.max_len) + params.pkt.len = pool_capa.pkt.max_len; + + pool = odp_pool_create("packet_pool", ¶ms); + + if (ODP_POOL_INVALID == pool) { + fprintf(stderr, "Packet pool creation failed.\n"); + return -1; + } + out_queue = odp_queue_create("crypto-out", NULL); + if (ODP_QUEUE_INVALID == out_queue) { + fprintf(stderr, "Crypto outq creation failed.\n"); + return -1; + } + + return 0; +} + +int crypto_term(odp_instance_t inst) +{ + odp_pool_t pool; + odp_queue_t out_queue; + + out_queue = odp_queue_lookup("crypto-out"); + if (ODP_QUEUE_INVALID != out_queue) { + if (odp_queue_destroy(out_queue)) + fprintf(stderr, "Crypto outq destroy failed.\n"); + } else { + fprintf(stderr, "Crypto outq not found.\n"); + } + + pool = odp_pool_lookup("packet_pool"); + if (ODP_POOL_INVALID != pool) { + if (odp_pool_destroy(pool)) + fprintf(stderr, "Packet pool destroy failed.\n"); + } else { + fprintf(stderr, "Packet pool not found.\n"); + } + + if (0 != odp_term_local()) { + fprintf(stderr, "error: odp_term_local() failed.\n"); + return -1; + } + + if (0 != odp_term_global(inst)) { + fprintf(stderr, "error: odp_term_global() failed.\n"); + return -1; + } + + return 0; +} + +int crypto_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + odp_cunit_register_global_init(crypto_init); + odp_cunit_register_global_term(crypto_term); + + ret = odp_cunit_register(crypto_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/crypto/crypto.h b/test/common_plat/validation/api/crypto/crypto.h new file mode 100644 index 000000000..9b909aa04 --- /dev/null +++ b/test/common_plat/validation/api/crypto/crypto.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_CRYPTO_H_ +#define _ODP_TEST_CRYPTO_H_ + +#include "odp_cunit_common.h" + +/* test functions: */ +void crypto_test_enc_alg_3des_cbc(void); +void crypto_test_enc_alg_3des_cbc_ovr_iv(void); +void crypto_test_dec_alg_3des_cbc(void); +void crypto_test_dec_alg_3des_cbc_ovr_iv(void); +void crypto_test_enc_alg_aes128_cbc(void); +void crypto_test_enc_alg_aes128_cbc_ovr_iv(void); +void crypto_test_dec_alg_aes128_cbc(void); +void crypto_test_dec_alg_aes128_cbc_ovr_iv(void); +void crypto_test_enc_alg_aes128_gcm(void); +void crypto_test_enc_alg_aes128_gcm_ovr_iv(void); +void crypto_test_dec_alg_aes128_gcm(void); +void crypto_test_dec_alg_aes128_gcm_ovr_iv(void); +void crypto_test_alg_hmac_md5(void); +void crypto_test_alg_hmac_sha256(void); + +/* test arrays: */ +extern odp_testinfo_t crypto_suite[]; + +/* test array init/term functions: */ +int crypto_suite_sync_init(void); +int crypto_suite_async_init(void); + +/* test registry: */ +extern odp_suiteinfo_t crypto_suites[]; + +/* executable init/term functions: */ +int crypto_init(odp_instance_t *inst); +int crypto_term(odp_instance_t inst); + +/* main test program: */ +int crypto_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/crypto/crypto_main.c b/test/common_plat/validation/api/crypto/crypto_main.c new file mode 100644 index 000000000..d8c26fa25 --- /dev/null +++ b/test/common_plat/validation/api/crypto/crypto_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "crypto.h" + +int main(int argc, char *argv[]) +{ + return crypto_main(argc, argv); +} diff --git a/test/common_plat/validation/api/crypto/odp_crypto_test_inp.c b/test/common_plat/validation/api/crypto/odp_crypto_test_inp.c new file mode 100644 index 000000000..4ac4a0700 --- /dev/null +++ b/test/common_plat/validation/api/crypto/odp_crypto_test_inp.c @@ -0,0 +1,726 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include <CUnit/Basic.h> +#include <odp_cunit_common.h> +#include "test_vectors.h" +#include "odp_crypto_test_inp.h" +#include "crypto.h" + +struct suite_context_s { + odp_crypto_op_mode_t pref_mode; + odp_pool_t pool; + odp_queue_t queue; +}; + +static struct suite_context_s suite_context; + +/* Basic algorithm run function for async inplace mode. + * Creates a session from input parameters and runs one operation + * on input_vec. Checks the output of the crypto operation against + * output_vec. Operation completion event is dequeued polling the + * session output queue. Completion context pointer is retrieved + * and checked against the one set before the operation. + * Completion event can be a separate buffer or the input packet + * buffer can be used. + * */ +static void alg_test(odp_crypto_op_t op, + odp_cipher_alg_t cipher_alg, + odp_crypto_iv_t ses_iv, + uint8_t *op_iv_ptr, + odp_crypto_key_t cipher_key, + odp_auth_alg_t auth_alg, + odp_crypto_key_t auth_key, + odp_crypto_data_range_t *cipher_range, + odp_crypto_data_range_t *auth_range, + const uint8_t *plaintext, + unsigned int plaintext_len, + const uint8_t *ciphertext, + unsigned int ciphertext_len, + const uint8_t *digest, + unsigned int digest_len + ) +{ + odp_crypto_session_t session; + odp_crypto_capability_t capability; + int rc; + odp_crypto_ses_create_err_t status; + odp_bool_t posted; + odp_event_t event; + odp_crypto_compl_t compl_event; + odp_crypto_op_result_t result; + odp_crypto_session_params_t ses_params; + odp_crypto_op_params_t op_params; + uint8_t *data_addr; + int data_off; + + rc = odp_crypto_capability(&capability); + CU_ASSERT(!rc); + + if (capability.hw_ciphers.all_bits) { + if (cipher_alg == ODP_CIPHER_ALG_3DES_CBC && + !(capability.hw_ciphers.bit.trides_cbc)) + rc = -1; + if (cipher_alg == ODP_CIPHER_ALG_AES128_CBC && + !(capability.hw_ciphers.bit.aes128_cbc)) + rc = -1; + if (cipher_alg == ODP_CIPHER_ALG_AES128_GCM && + !(capability.hw_ciphers.bit.aes128_gcm)) + rc = -1; + } else { + if (cipher_alg == ODP_CIPHER_ALG_3DES_CBC && + !(capability.ciphers.bit.trides_cbc)) + rc = -1; + if (cipher_alg == ODP_CIPHER_ALG_AES128_CBC && + !(capability.ciphers.bit.aes128_cbc)) + rc = -1; + if (cipher_alg == ODP_CIPHER_ALG_AES128_GCM && + !(capability.ciphers.bit.aes128_gcm)) + rc = -1; + } + + CU_ASSERT(!rc); + + if (capability.hw_auths.all_bits) { + if (auth_alg == ODP_AUTH_ALG_AES128_GCM && + !(capability.hw_auths.bit.aes128_gcm)) + rc = -1; + if (auth_alg == ODP_AUTH_ALG_NULL && + !(capability.hw_auths.bit.null)) + rc = -1; + } else { + if (auth_alg == ODP_AUTH_ALG_AES128_GCM && + !(capability.auths.bit.aes128_gcm)) + rc = -1; + if (auth_alg == ODP_AUTH_ALG_NULL && + !(capability.auths.bit.null)) + rc = -1; + } + + CU_ASSERT(!rc); + + /* Create a crypto session */ + memset(&ses_params, 0, sizeof(ses_params)); + ses_params.op = op; + ses_params.auth_cipher_text = false; + ses_params.pref_mode = suite_context.pref_mode; + ses_params.cipher_alg = cipher_alg; + ses_params.auth_alg = auth_alg; + ses_params.compl_queue = suite_context.queue; + ses_params.output_pool = suite_context.pool; + ses_params.cipher_key = cipher_key; + ses_params.iv = ses_iv; + ses_params.auth_key = auth_key; + + rc = odp_crypto_session_create(&ses_params, &session, &status); + CU_ASSERT_FATAL(!rc); + CU_ASSERT(status == ODP_CRYPTO_SES_CREATE_ERR_NONE); + CU_ASSERT(odp_crypto_session_to_u64(session) != + odp_crypto_session_to_u64(ODP_CRYPTO_SESSION_INVALID)); + + /* Prepare input data */ + odp_packet_t pkt = odp_packet_alloc(suite_context.pool, + plaintext_len + digest_len); + CU_ASSERT(pkt != ODP_PACKET_INVALID); + data_addr = odp_packet_data(pkt); + memcpy(data_addr, plaintext, plaintext_len); + data_off = 0; + + /* Prepare input/output params */ + memset(&op_params, 0, sizeof(op_params)); + op_params.session = session; + op_params.pkt = pkt; + op_params.out_pkt = pkt; + op_params.ctx = (void *)0xdeadbeef; + + if (cipher_range) { + op_params.cipher_range = *cipher_range; + data_off = cipher_range->offset; + } else { + op_params.cipher_range.offset = data_off; + op_params.cipher_range.length = plaintext_len; + } + if (auth_range) { + op_params.auth_range = *auth_range; + } else { + op_params.auth_range.offset = data_off; + op_params.auth_range.length = plaintext_len; + } + if (op_iv_ptr) + op_params.override_iv_ptr = op_iv_ptr; + + op_params.hash_result_offset = plaintext_len; + + rc = odp_crypto_operation(&op_params, &posted, &result); + if (rc < 0) { + CU_FAIL("Failed odp_crypto_operation()"); + goto cleanup; + } + + if (posted) { + /* Poll completion queue for results */ + do { + event = odp_queue_deq(suite_context.queue); + } while (event == ODP_EVENT_INVALID); + + compl_event = odp_crypto_compl_from_event(event); + CU_ASSERT(odp_crypto_compl_to_u64(compl_event) == + odp_crypto_compl_to_u64(odp_crypto_compl_from_event(event))); + odp_crypto_compl_result(compl_event, &result); + odp_crypto_compl_free(compl_event); + } + + CU_ASSERT(result.ok); + CU_ASSERT(result.pkt == pkt); + + if (cipher_alg != ODP_CIPHER_ALG_NULL) + CU_ASSERT(!memcmp(data_addr, ciphertext, ciphertext_len)); + + if (op == ODP_CRYPTO_OP_ENCODE && auth_alg != ODP_AUTH_ALG_NULL) + CU_ASSERT(!memcmp(data_addr + op_params.hash_result_offset, + digest, digest_len)); + + CU_ASSERT(result.ctx == (void *)0xdeadbeef); +cleanup: + rc = odp_crypto_session_destroy(session); + CU_ASSERT(!rc); + + odp_packet_free(pkt); +} + +/* This test verifies the correctness of encode (plaintext -> ciphertext) + * operation for 3DES_CBC algorithm. IV for the operation is the session IV. + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer.*/ +void crypto_test_enc_alg_3des_cbc(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv; + unsigned int test_vec_num = (sizeof(tdes_cbc_reference_length) / + sizeof(tdes_cbc_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = tdes_cbc_reference_key[i]; + cipher_key.length = sizeof(tdes_cbc_reference_key[i]); + iv.data = tdes_cbc_reference_iv[i]; + iv.length = sizeof(tdes_cbc_reference_iv[i]); + + alg_test(ODP_CRYPTO_OP_ENCODE, + ODP_CIPHER_ALG_3DES_CBC, + iv, + NULL, + cipher_key, + ODP_AUTH_ALG_NULL, + auth_key, + NULL, NULL, + tdes_cbc_reference_plaintext[i], + tdes_cbc_reference_length[i], + tdes_cbc_reference_ciphertext[i], + tdes_cbc_reference_length[i], NULL, 0); + } +} + +/* This test verifies the correctness of encode (plaintext -> ciphertext) + * operation for 3DES_CBC algorithm. IV for the operation is the operation IV. + * */ +void crypto_test_enc_alg_3des_cbc_ovr_iv(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = TDES_CBC_IV_LEN }; + unsigned int test_vec_num = (sizeof(tdes_cbc_reference_length) / + sizeof(tdes_cbc_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = tdes_cbc_reference_key[i]; + cipher_key.length = sizeof(tdes_cbc_reference_key[i]); + + alg_test(ODP_CRYPTO_OP_ENCODE, + ODP_CIPHER_ALG_3DES_CBC, + iv, + tdes_cbc_reference_iv[i], + cipher_key, + ODP_AUTH_ALG_NULL, + auth_key, + NULL, NULL, + tdes_cbc_reference_plaintext[i], + tdes_cbc_reference_length[i], + tdes_cbc_reference_ciphertext[i], + tdes_cbc_reference_length[i], NULL, 0); + } +} + +/* This test verifies the correctness of decode (ciphertext -> plaintext) + * operation for 3DES_CBC algorithm. IV for the operation is the session IV + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer. + * */ +void crypto_test_dec_alg_3des_cbc(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = 0 }; + unsigned int test_vec_num = (sizeof(tdes_cbc_reference_length) / + sizeof(tdes_cbc_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = tdes_cbc_reference_key[i]; + cipher_key.length = sizeof(tdes_cbc_reference_key[i]); + iv.data = tdes_cbc_reference_iv[i]; + iv.length = sizeof(tdes_cbc_reference_iv[i]); + + alg_test(ODP_CRYPTO_OP_DECODE, + ODP_CIPHER_ALG_3DES_CBC, + iv, + NULL, + cipher_key, + ODP_AUTH_ALG_NULL, + auth_key, + NULL, NULL, + tdes_cbc_reference_ciphertext[i], + tdes_cbc_reference_length[i], + tdes_cbc_reference_plaintext[i], + tdes_cbc_reference_length[i], NULL, 0); + } +} + +/* This test verifies the correctness of decode (ciphertext -> plaintext) + * operation for 3DES_CBC algorithm. IV for the operation is the session IV + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer. + * */ +void crypto_test_dec_alg_3des_cbc_ovr_iv(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = TDES_CBC_IV_LEN }; + unsigned int test_vec_num = (sizeof(tdes_cbc_reference_length) / + sizeof(tdes_cbc_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = tdes_cbc_reference_key[i]; + cipher_key.length = sizeof(tdes_cbc_reference_key[i]); + + alg_test(ODP_CRYPTO_OP_DECODE, + ODP_CIPHER_ALG_3DES_CBC, + iv, + tdes_cbc_reference_iv[i], + cipher_key, + ODP_AUTH_ALG_NULL, + auth_key, + NULL, NULL, + tdes_cbc_reference_ciphertext[i], + tdes_cbc_reference_length[i], + tdes_cbc_reference_plaintext[i], + tdes_cbc_reference_length[i], NULL, 0); + } +} + +/* This test verifies the correctness of encode (plaintext -> ciphertext) + * operation for AES128_GCM algorithm. IV for the operation is the session IV. + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer.*/ +void crypto_test_enc_alg_aes128_gcm(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = AES128_GCM_IV_LEN }; + unsigned int test_vec_num = (sizeof(aes128_gcm_reference_length) / + sizeof(aes128_gcm_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = aes128_gcm_reference_key[i]; + cipher_key.length = sizeof(aes128_gcm_reference_key[i]); + iv.data = aes128_gcm_reference_iv[i]; + iv.length = sizeof(aes128_gcm_reference_iv[i]); + + alg_test(ODP_CRYPTO_OP_ENCODE, + ODP_CIPHER_ALG_AES128_GCM, + iv, + NULL, + cipher_key, + ODP_AUTH_ALG_AES128_GCM, + auth_key, + &aes128_gcm_cipher_range[i], + &aes128_gcm_auth_range[i], + aes128_gcm_reference_plaintext[i], + aes128_gcm_reference_length[i], + aes128_gcm_reference_ciphertext[i], + aes128_gcm_reference_length[i], + aes128_gcm_reference_ciphertext[i] + + aes128_gcm_reference_length[i], + AES128_GCM_CHECK_LEN); + } +} + +/* This test verifies the correctness of encode (plaintext -> ciphertext) + * operation for AES128_GCM algorithm. IV for the operation is the session IV. + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer.*/ +void crypto_test_enc_alg_aes128_gcm_ovr_iv(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = AES128_GCM_IV_LEN }; + unsigned int test_vec_num = (sizeof(aes128_gcm_reference_length) / + sizeof(aes128_gcm_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = aes128_gcm_reference_key[i]; + cipher_key.length = sizeof(aes128_gcm_reference_key[i]); + + alg_test(ODP_CRYPTO_OP_ENCODE, + ODP_CIPHER_ALG_AES128_GCM, + iv, + aes128_gcm_reference_iv[i], + cipher_key, + ODP_AUTH_ALG_AES128_GCM, + auth_key, + &aes128_gcm_cipher_range[i], + &aes128_gcm_auth_range[i], + aes128_gcm_reference_plaintext[i], + aes128_gcm_reference_length[i], + aes128_gcm_reference_ciphertext[i], + aes128_gcm_reference_length[i], + aes128_gcm_reference_ciphertext[i] + + aes128_gcm_reference_length[i], + AES128_GCM_CHECK_LEN); + } +} + +/* This test verifies the correctness of decode (ciphertext -> plaintext) + * operation for 3DES_CBC algorithm. IV for the operation is the session IV + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer. + * */ +void crypto_test_dec_alg_aes128_gcm(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = AES128_GCM_IV_LEN }; + unsigned int test_vec_num = (sizeof(aes128_gcm_reference_length) / + sizeof(aes128_gcm_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = aes128_gcm_reference_key[i]; + cipher_key.length = sizeof(aes128_gcm_reference_key[i]); + iv.data = aes128_gcm_reference_iv[i]; + iv.length = sizeof(aes128_gcm_reference_iv[i]); + + alg_test(ODP_CRYPTO_OP_DECODE, + ODP_CIPHER_ALG_AES128_GCM, + iv, + NULL, + cipher_key, + ODP_AUTH_ALG_AES128_GCM, + auth_key, + &aes128_gcm_cipher_range[i], + &aes128_gcm_auth_range[i], + aes128_gcm_reference_ciphertext[i], + aes128_gcm_reference_length[i] + AES128_GCM_CHECK_LEN, + aes128_gcm_reference_plaintext[i], + aes128_gcm_reference_length[i], + aes128_gcm_reference_ciphertext[i] + + aes128_gcm_reference_length[i], + AES128_GCM_CHECK_LEN); + } +} + +/* This test verifies the correctness of decode (ciphertext -> plaintext) + * operation for 3DES_CBC algorithm. IV for the operation is the session IV + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer. + * */ +void crypto_test_dec_alg_aes128_gcm_ovr_iv(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = AES128_GCM_IV_LEN }; + unsigned int test_vec_num = (sizeof(aes128_gcm_reference_length) / + sizeof(aes128_gcm_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = aes128_gcm_reference_key[i]; + cipher_key.length = sizeof(aes128_gcm_reference_key[i]); + + alg_test(ODP_CRYPTO_OP_DECODE, + ODP_CIPHER_ALG_AES128_GCM, + iv, + aes128_gcm_reference_iv[i], + cipher_key, + ODP_AUTH_ALG_AES128_GCM, + auth_key, + &aes128_gcm_cipher_range[i], + &aes128_gcm_auth_range[i], + aes128_gcm_reference_ciphertext[i], + aes128_gcm_reference_length[i] + AES128_GCM_CHECK_LEN, + aes128_gcm_reference_plaintext[i], + aes128_gcm_reference_length[i], + aes128_gcm_reference_ciphertext[i] + + aes128_gcm_reference_length[i], + AES128_GCM_CHECK_LEN); + } +} + +/* This test verifies the correctness of encode (plaintext -> ciphertext) + * operation for AES128_CBC algorithm. IV for the operation is the session IV. + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer.*/ +void crypto_test_enc_alg_aes128_cbc(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv; + unsigned int test_vec_num = (sizeof(aes128_cbc_reference_length) / + sizeof(aes128_cbc_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = aes128_cbc_reference_key[i]; + cipher_key.length = sizeof(aes128_cbc_reference_key[i]); + iv.data = aes128_cbc_reference_iv[i]; + iv.length = sizeof(aes128_cbc_reference_iv[i]); + + alg_test(ODP_CRYPTO_OP_ENCODE, + ODP_CIPHER_ALG_AES128_CBC, + iv, + NULL, + cipher_key, + ODP_AUTH_ALG_NULL, + auth_key, + NULL, NULL, + aes128_cbc_reference_plaintext[i], + aes128_cbc_reference_length[i], + aes128_cbc_reference_ciphertext[i], + aes128_cbc_reference_length[i], NULL, 0); + } +} + +/* This test verifies the correctness of encode (plaintext -> ciphertext) + * operation for AES128_CBC algorithm. IV for the operation is the operation IV. + * */ +void crypto_test_enc_alg_aes128_cbc_ovr_iv(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = AES128_CBC_IV_LEN }; + unsigned int test_vec_num = (sizeof(aes128_cbc_reference_length) / + sizeof(aes128_cbc_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = aes128_cbc_reference_key[i]; + cipher_key.length = sizeof(aes128_cbc_reference_key[i]); + + alg_test(ODP_CRYPTO_OP_ENCODE, + ODP_CIPHER_ALG_AES128_CBC, + iv, + aes128_cbc_reference_iv[i], + cipher_key, + ODP_AUTH_ALG_NULL, + auth_key, + NULL, NULL, + aes128_cbc_reference_plaintext[i], + aes128_cbc_reference_length[i], + aes128_cbc_reference_ciphertext[i], + aes128_cbc_reference_length[i], NULL, 0); + } +} + +/* This test verifies the correctness of decode (ciphertext -> plaintext) + * operation for AES128_CBC algorithm. IV for the operation is the session IV + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer. + * */ +void crypto_test_dec_alg_aes128_cbc(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = 0 }; + unsigned int test_vec_num = (sizeof(aes128_cbc_reference_length) / + sizeof(aes128_cbc_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = aes128_cbc_reference_key[i]; + cipher_key.length = sizeof(aes128_cbc_reference_key[i]); + iv.data = aes128_cbc_reference_iv[i]; + iv.length = sizeof(aes128_cbc_reference_iv[i]); + + alg_test(ODP_CRYPTO_OP_DECODE, + ODP_CIPHER_ALG_AES128_CBC, + iv, + NULL, + cipher_key, + ODP_AUTH_ALG_NULL, + auth_key, + NULL, NULL, + aes128_cbc_reference_ciphertext[i], + aes128_cbc_reference_length[i], + aes128_cbc_reference_plaintext[i], + aes128_cbc_reference_length[i], NULL, 0); + } +} + +/* This test verifies the correctness of decode (ciphertext -> plaintext) + * operation for AES128_CBC algorithm. IV for the operation is the session IV + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer. + * */ +void crypto_test_dec_alg_aes128_cbc_ovr_iv(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = AES128_CBC_IV_LEN }; + unsigned int test_vec_num = (sizeof(aes128_cbc_reference_length) / + sizeof(aes128_cbc_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + cipher_key.data = aes128_cbc_reference_key[i]; + cipher_key.length = sizeof(aes128_cbc_reference_key[i]); + + alg_test(ODP_CRYPTO_OP_DECODE, + ODP_CIPHER_ALG_AES128_CBC, + iv, + aes128_cbc_reference_iv[i], + cipher_key, + ODP_AUTH_ALG_NULL, + auth_key, + NULL, NULL, + aes128_cbc_reference_ciphertext[i], + aes128_cbc_reference_length[i], + aes128_cbc_reference_plaintext[i], + aes128_cbc_reference_length[i], NULL, 0); + } +} + +/* This test verifies the correctness of HMAC_MD5 digest operation. + * The output check length is truncated to 12 bytes (96 bits) as + * returned by the crypto operation API call. + * Note that hash digest is a one-way operation. + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer. + * */ +void crypto_test_alg_hmac_md5(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = 0 }; + + unsigned int test_vec_num = (sizeof(hmac_md5_reference_length) / + sizeof(hmac_md5_reference_length[0])); + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + auth_key.data = hmac_md5_reference_key[i]; + auth_key.length = sizeof(hmac_md5_reference_key[i]); + + alg_test(ODP_CRYPTO_OP_ENCODE, + ODP_CIPHER_ALG_NULL, + iv, + iv.data, + cipher_key, + ODP_AUTH_ALG_MD5_96, + auth_key, + NULL, NULL, + hmac_md5_reference_plaintext[i], + hmac_md5_reference_length[i], + NULL, 0, + hmac_md5_reference_digest[i], + HMAC_MD5_96_CHECK_LEN); + } +} + +/* This test verifies the correctness of HMAC_MD5 digest operation. + * The output check length is truncated to 12 bytes (96 bits) as + * returned by the crypto operation API call. + * Note that hash digest is a one-way operation. + * In addition the test verifies if the implementation can use the + * packet buffer as completion event buffer. + * */ +void crypto_test_alg_hmac_sha256(void) +{ + odp_crypto_key_t cipher_key = { .data = NULL, .length = 0 }, + auth_key = { .data = NULL, .length = 0 }; + odp_crypto_iv_t iv = { .data = NULL, .length = 0 }; + + unsigned int test_vec_num = (sizeof(hmac_sha256_reference_length) / + sizeof(hmac_sha256_reference_length[0])); + + unsigned int i; + + for (i = 0; i < test_vec_num; i++) { + auth_key.data = hmac_sha256_reference_key[i]; + auth_key.length = sizeof(hmac_sha256_reference_key[i]); + + alg_test(ODP_CRYPTO_OP_ENCODE, + ODP_CIPHER_ALG_NULL, + iv, + iv.data, + cipher_key, + ODP_AUTH_ALG_SHA256_128, + auth_key, + NULL, NULL, + hmac_sha256_reference_plaintext[i], + hmac_sha256_reference_length[i], + NULL, 0, + hmac_sha256_reference_digest[i], + HMAC_SHA256_128_CHECK_LEN); + } +} + +int crypto_suite_sync_init(void) +{ + suite_context.pool = odp_pool_lookup("packet_pool"); + if (suite_context.pool == ODP_POOL_INVALID) + return -1; + + suite_context.queue = ODP_QUEUE_INVALID; + suite_context.pref_mode = ODP_CRYPTO_SYNC; + return 0; +} + +int crypto_suite_async_init(void) +{ + suite_context.pool = odp_pool_lookup("packet_pool"); + if (suite_context.pool == ODP_POOL_INVALID) + return -1; + suite_context.queue = odp_queue_lookup("crypto-out"); + if (suite_context.queue == ODP_QUEUE_INVALID) + return -1; + + suite_context.pref_mode = ODP_CRYPTO_ASYNC; + return 0; +} + +odp_testinfo_t crypto_suite[] = { + ODP_TEST_INFO(crypto_test_enc_alg_3des_cbc), + ODP_TEST_INFO(crypto_test_dec_alg_3des_cbc), + ODP_TEST_INFO(crypto_test_enc_alg_3des_cbc_ovr_iv), + ODP_TEST_INFO(crypto_test_dec_alg_3des_cbc_ovr_iv), + ODP_TEST_INFO(crypto_test_enc_alg_aes128_cbc), + ODP_TEST_INFO(crypto_test_dec_alg_aes128_cbc), + ODP_TEST_INFO(crypto_test_enc_alg_aes128_cbc_ovr_iv), + ODP_TEST_INFO(crypto_test_dec_alg_aes128_cbc_ovr_iv), + ODP_TEST_INFO(crypto_test_enc_alg_aes128_gcm), + ODP_TEST_INFO(crypto_test_enc_alg_aes128_gcm_ovr_iv), + ODP_TEST_INFO(crypto_test_dec_alg_aes128_gcm), + ODP_TEST_INFO(crypto_test_dec_alg_aes128_gcm_ovr_iv), + ODP_TEST_INFO(crypto_test_alg_hmac_md5), + ODP_TEST_INFO(crypto_test_alg_hmac_sha256), + ODP_TEST_INFO_NULL, +}; diff --git a/test/common_plat/validation/api/crypto/odp_crypto_test_inp.h b/test/common_plat/validation/api/crypto/odp_crypto_test_inp.h new file mode 100644 index 000000000..8bda34472 --- /dev/null +++ b/test/common_plat/validation/api/crypto/odp_crypto_test_inp.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef ODP_CRYPTO_TEST_ASYNC_INP_ +#define ODP_CRYPTO_TEST_ASYNC_INP_ + +#include <odp_cunit_common.h> + +/* Suite names */ +#define ODP_CRYPTO_ASYNC_INP "odp_crypto_async_inp" +#define ODP_CRYPTO_SYNC_INP "odp_crypto_sync_inp" + +/* Suite test array */ +extern odp_testinfo_t crypto_suite[]; + +int crypto_suite_sync_init(void); +int crypto_suite_async_init(void); + +#endif diff --git a/test/common_plat/validation/api/crypto/test_vectors.h b/test/common_plat/validation/api/crypto/test_vectors.h new file mode 100644 index 000000000..da4610f33 --- /dev/null +++ b/test/common_plat/validation/api/crypto/test_vectors.h @@ -0,0 +1,353 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_CRYPTO_VECTORS_H_ +#define _ODP_TEST_CRYPTO_VECTORS_H_ + +#include "test_vectors_len.h" +/* TDES-CBC reference vectors, according to + * "http://csrc.nist.gov/groups/STM/cavp/documents/des/DESMMT.pdf" + */ +static uint8_t tdes_cbc_reference_key[][TDES_CBC_KEY_LEN] = { + {0x62, 0x7f, 0x46, 0x0e, 0x08, 0x10, 0x4a, 0x10, 0x43, 0xcd, 0x26, 0x5d, + 0x58, 0x40, 0xea, 0xf1, 0x31, 0x3e, 0xdf, 0x97, 0xdf, 0x2a, 0x8a, 0x8c, + }, + + {0x37, 0xae, 0x5e, 0xbf, 0x46, 0xdf, 0xf2, 0xdc, 0x07, 0x54, 0xb9, 0x4f, + 0x31, 0xcb, 0xb3, 0x85, 0x5e, 0x7f, 0xd3, 0x6d, 0xc8, 0x70, 0xbf, 0xae} +}; + +static uint8_t tdes_cbc_reference_iv[][TDES_CBC_IV_LEN] = { + {0x8e, 0x29, 0xf7, 0x5e, 0xa7, 0x7e, 0x54, 0x75}, + + {0x3d, 0x1d, 0xe3, 0xcc, 0x13, 0x2e, 0x3b, 0x65} +}; + +/** length in bytes */ +static uint32_t tdes_cbc_reference_length[] = { 8, 16 }; + +static uint8_t +tdes_cbc_reference_plaintext[][TDES_CBC_MAX_DATA_LEN] = { + {0x32, 0x6a, 0x49, 0x4c, 0xd3, 0x3f, 0xe7, 0x56}, + + {0x84, 0x40, 0x1f, 0x78, 0xfe, 0x6c, 0x10, 0x87, 0x6d, 0x8e, 0xa2, 0x30, + 0x94, 0xea, 0x53, 0x09} +}; + +static uint8_t +tdes_cbc_reference_ciphertext[][TDES_CBC_MAX_DATA_LEN] = { + {0xb2, 0x2b, 0x8d, 0x66, 0xde, 0x97, 0x06, 0x92}, + + {0x7b, 0x1f, 0x7c, 0x7e, 0x3b, 0x1c, 0x94, 0x8e, 0xbd, 0x04, 0xa7, 0x5f, + 0xfb, 0xa7, 0xd2, 0xf5} +}; + +static uint8_t aes128_cbc_reference_key[][AES128_CBC_KEY_LEN] = { + {0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, + 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 }, + {0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0, + 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a }, + {0x6c, 0x3e, 0xa0, 0x47, 0x76, 0x30, 0xce, 0x21, + 0xa2, 0xce, 0x33, 0x4a, 0xa7, 0x46, 0xc2, 0xcd }, + {0x56, 0xe4, 0x7a, 0x38, 0xc5, 0x59, 0x89, 0x74, + 0xbc, 0x46, 0x90, 0x3d, 0xba, 0x29, 0x03, 0x49 } +}; + +static uint8_t aes128_cbc_reference_iv[][AES128_CBC_IV_LEN] = { + { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, + 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 }, + { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, + 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 }, + { 0xc7, 0x82, 0xdc, 0x4c, 0x09, 0x8c, 0x66, 0xcb, + 0xd9, 0xcd, 0x27, 0xd8, 0x25, 0x68, 0x2c, 0x81 }, + { 0x8c, 0xe8, 0x2e, 0xef, 0xbe, 0xa0, 0xda, 0x3c, + 0x44, 0x69, 0x9e, 0xd7, 0xdb, 0x51, 0xb7, 0xd9 } +}; + +/** length in bytes */ +static uint32_t aes128_cbc_reference_length[] = { 16, 32, 48, 64 }; + +static uint8_t +aes128_cbc_reference_plaintext[][AES128_CBC_MAX_DATA_LEN] = { + "Single block msg", + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }, + "This is a 48-byte message (exactly 3 AES blocks)", + { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf } +}; + +static uint8_t +aes128_cbc_reference_ciphertext[][AES128_CBC_MAX_DATA_LEN] = { + { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }, + { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a, + 0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a, + 0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9, + 0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 }, + { 0xd0, 0xa0, 0x2b, 0x38, 0x36, 0x45, 0x17, 0x53, + 0xd4, 0x93, 0x66, 0x5d, 0x33, 0xf0, 0xe8, 0x86, + 0x2d, 0xea, 0x54, 0xcd, 0xb2, 0x93, 0xab, 0xc7, + 0x50, 0x69, 0x39, 0x27, 0x67, 0x72, 0xf8, 0xd5, + 0x02, 0x1c, 0x19, 0x21, 0x6b, 0xad, 0x52, 0x5c, + 0x85, 0x79, 0x69, 0x5d, 0x83, 0xba, 0x26, 0x84 }, + { 0xc3, 0x0e, 0x32, 0xff, 0xed, 0xc0, 0x77, 0x4e, + 0x6a, 0xff, 0x6a, 0xf0, 0x86, 0x9f, 0x71, 0xaa, + 0x0f, 0x3a, 0xf0, 0x7a, 0x9a, 0x31, 0xa9, 0xc6, + 0x84, 0xdb, 0x20, 0x7e, 0xb0, 0xef, 0x8e, 0x4e, + 0x35, 0x90, 0x7a, 0xa6, 0x32, 0xc3, 0xff, 0xdf, + 0x86, 0x8b, 0xb7, 0xb2, 0x9d, 0x3d, 0x46, 0xad, + 0x83, 0xce, 0x9f, 0x9a, 0x10, 0x2e, 0xe9, 0x9d, + 0x49, 0xa5, 0x3e, 0x87, 0xf4, 0xc3, 0xda, 0x55 } +}; + +/* AES-GCM test vectors extracted from + * https://tools.ietf.org/html/draft-mcgrew-gcm-test-01#section-2 + */ +static uint8_t aes128_gcm_reference_key[][AES128_GCM_KEY_LEN] = { + { 0x4c, 0x80, 0xcd, 0xef, 0xbb, 0x5d, 0x10, 0xda, + 0x90, 0x6a, 0xc7, 0x3c, 0x36, 0x13, 0xa6, 0x34 }, + { 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x3d, 0xe0, 0x98, 0x74, 0xb3, 0x88, 0xe6, 0x49, + 0x19, 0x88, 0xd0, 0xc3, 0x60, 0x7e, 0xae, 0x1f } +}; + +static uint8_t aes128_gcm_reference_iv[][AES128_GCM_IV_LEN] = { + { 0x2e, 0x44, 0x3b, 0x68, 0x49, 0x56, 0xed, 0x7e, + 0x3b, 0x24, 0x4c, 0xfe }, + { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, + 0xde, 0xca, 0xf8, 0x88 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }, + { 0x57, 0x69, 0x0e, 0x43, 0x4e, 0x28, 0x00, 0x00, + 0xa2, 0xfc, 0xa1, 0xa3 } +}; + +static uint32_t aes128_gcm_reference_length[] = { 84, 72, 72, 40}; + +static odp_crypto_data_range_t aes128_gcm_cipher_range[] = { + { .offset = 12, .length = 72 }, + { .offset = 8, .length = 64 }, + { .offset = 8, .length = 64 }, + { .offset = 12, .length = 28 }, +}; + +static odp_crypto_data_range_t aes128_gcm_auth_range[] = { + { .offset = 0, .length = 84 }, + { .offset = 0, .length = 72 }, + { .offset = 0, .length = 72 }, + { .offset = 0, .length = 40 }, +}; + +static uint8_t +aes128_gcm_reference_plaintext[][AES128_GCM_MAX_DATA_LEN] = { + { /* Aad */ + 0x00, 0x00, 0x43, 0x21, 0x87, 0x65, 0x43, 0x21, + 0x00, 0x00, 0x00, 0x00, + /* Plain */ + 0x45, 0x00, 0x00, 0x48, 0x69, 0x9a, 0x00, 0x00, + 0x80, 0x11, 0x4d, 0xb7, 0xc0, 0xa8, 0x01, 0x02, + 0xc0, 0xa8, 0x01, 0x01, 0x0a, 0x9b, 0xf1, 0x56, + 0x38, 0xd3, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x5f, 0x73, 0x69, + 0x70, 0x04, 0x5f, 0x75, 0x64, 0x70, 0x03, 0x73, + 0x69, 0x70, 0x09, 0x63, 0x79, 0x62, 0x65, 0x72, + 0x63, 0x69, 0x74, 0x79, 0x02, 0x64, 0x6b, 0x00, + 0x00, 0x21, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01 }, + + { /* Aad */ + 0x00, 0x00, 0xa5, 0xf8, 0x00, 0x00, 0x00, 0x0a, + /* Plain */ + 0x45, 0x00, 0x00, 0x3e, 0x69, 0x8f, 0x00, 0x00, + 0x80, 0x11, 0x4d, 0xcc, 0xc0, 0xa8, 0x01, 0x02, + 0xc0, 0xa8, 0x01, 0x01, 0x0a, 0x98, 0x00, 0x35, + 0x00, 0x2a, 0x23, 0x43, 0xb2, 0xd0, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x73, 0x69, 0x70, 0x09, 0x63, 0x79, 0x62, + 0x65, 0x72, 0x63, 0x69, 0x74, 0x79, 0x02, 0x64, + 0x6b, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 }, + + { /* Aad */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* Plain */ + 0x45, 0x00, 0x00, 0x3c, 0x99, 0xc5, 0x00, 0x00, + 0x80, 0x01, 0xcb, 0x7a, 0x40, 0x67, 0x93, 0x18, + 0x01, 0x01, 0x01, 0x01, 0x08, 0x00, 0x07, 0x5c, + 0x02, 0x00, 0x44, 0x00, 0x61, 0x62, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, + 0x66, 0x67, 0x68, 0x69, 0x01, 0x02, 0x02, 0x01 }, + + { /* Aad */ + 0x42, 0xf6, 0x7e, 0x3f, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, + /* Plain */ + 0x45, 0x00, 0x00, 0x1c, 0x42, 0xa2, 0x00, 0x00, + 0x80, 0x01, 0x44, 0x1f, 0x40, 0x67, 0x93, 0xb6, + 0xe0, 0x00, 0x00, 0x02, 0x0a, 0x00, 0xf5, 0xff, + 0x01, 0x02, 0x02, 0x01 } +}; + +static uint8_t +aes128_gcm_reference_ciphertext[][AES128_GCM_MAX_DATA_LEN] = { + { /* Aad */ + 0x00, 0x00, 0x43, 0x21, 0x87, 0x65, 0x43, 0x21, + 0x00, 0x00, 0x00, 0x00, + /* Plain */ + 0xfe, 0xcf, 0x53, 0x7e, 0x72, 0x9d, 0x5b, 0x07, + 0xdc, 0x30, 0xdf, 0x52, 0x8d, 0xd2, 0x2b, 0x76, + 0x8d, 0x1b, 0x98, 0x73, 0x66, 0x96, 0xa6, 0xfd, + 0x34, 0x85, 0x09, 0xfa, 0x13, 0xce, 0xac, 0x34, + 0xcf, 0xa2, 0x43, 0x6f, 0x14, 0xa3, 0xf3, 0xcf, + 0x65, 0x92, 0x5b, 0xf1, 0xf4, 0xa1, 0x3c, 0x5d, + 0x15, 0xb2, 0x1e, 0x18, 0x84, 0xf5, 0xff, 0x62, + 0x47, 0xae, 0xab, 0xb7, 0x86, 0xb9, 0x3b, 0xce, + 0x61, 0xbc, 0x17, 0xd7, 0x68, 0xfd, 0x97, 0x32, + /* Digest */ + 0x45, 0x90, 0x18, 0x14, 0x8f, 0x6c, 0xbe, 0x72, + 0x2f, 0xd0, 0x47, 0x96, 0x56, 0x2d, 0xfd, 0xb4 }, + + { /* Aad */ + 0x00, 0x00, 0xa5, 0xf8, 0x00, 0x00, 0x00, 0x0a, + /* Plain */ + 0xde, 0xb2, 0x2c, 0xd9, 0xb0, 0x7c, 0x72, 0xc1, + 0x6e, 0x3a, 0x65, 0xbe, 0xeb, 0x8d, 0xf3, 0x04, + 0xa5, 0xa5, 0x89, 0x7d, 0x33, 0xae, 0x53, 0x0f, + 0x1b, 0xa7, 0x6d, 0x5d, 0x11, 0x4d, 0x2a, 0x5c, + 0x3d, 0xe8, 0x18, 0x27, 0xc1, 0x0e, 0x9a, 0x4f, + 0x51, 0x33, 0x0d, 0x0e, 0xec, 0x41, 0x66, 0x42, + 0xcf, 0xbb, 0x85, 0xa5, 0xb4, 0x7e, 0x48, 0xa4, + 0xec, 0x3b, 0x9b, 0xa9, 0x5d, 0x91, 0x8b, 0xd1, + /* Digest */ + 0x83, 0xb7, 0x0d, 0x3a, 0xa8, 0xbc, 0x6e, 0xe4, + 0xc3, 0x09, 0xe9, 0xd8, 0x5a, 0x41, 0xad, 0x4a }, + { /* Aad */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* Plain */ + 0x46, 0x88, 0xda, 0xf2, 0xf9, 0x73, 0xa3, 0x92, + 0x73, 0x29, 0x09, 0xc3, 0x31, 0xd5, 0x6d, 0x60, + 0xf6, 0x94, 0xab, 0xaa, 0x41, 0x4b, 0x5e, 0x7f, + 0xf5, 0xfd, 0xcd, 0xff, 0xf5, 0xe9, 0xa2, 0x84, + 0x45, 0x64, 0x76, 0x49, 0x27, 0x19, 0xff, 0xb6, + 0x4d, 0xe7, 0xd9, 0xdc, 0xa1, 0xe1, 0xd8, 0x94, + 0xbc, 0x3b, 0xd5, 0x78, 0x73, 0xed, 0x4d, 0x18, + 0x1d, 0x19, 0xd4, 0xd5, 0xc8, 0xc1, 0x8a, 0xf3, + /* Digest */ + 0xf8, 0x21, 0xd4, 0x96, 0xee, 0xb0, 0x96, 0xe9, + 0x8a, 0xd2, 0xb6, 0x9e, 0x47, 0x99, 0xc7, 0x1d }, + + { /* Aad */ + 0x42, 0xf6, 0x7e, 0x3f, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, + /* Plain */ + 0xfb, 0xa2, 0xca, 0x84, 0x5e, 0x5d, 0xf9, 0xf0, + 0xf2, 0x2c, 0x3e, 0x6e, 0x86, 0xdd, 0x83, 0x1e, + 0x1f, 0xc6, 0x57, 0x92, 0xcd, 0x1a, 0xf9, 0x13, + 0x0e, 0x13, 0x79, 0xed, + /* Digest */ + 0x36, 0x9f, 0x07, 0x1f, 0x35, 0xe0, 0x34, 0xbe, + 0x95, 0xf1, 0x12, 0xe4, 0xe7, 0xd0, 0x5d, 0x35 } +}; + +static uint8_t hmac_md5_reference_key[][HMAC_MD5_KEY_LEN] = { + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }, + + /* "Jefe" */ + { 0x4a, 0x65, 0x66, 0x65 }, + + { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa } +}; + +static uint32_t hmac_md5_reference_length[] = { 8, 28, 50 }; + +static uint8_t +hmac_md5_reference_plaintext[][HMAC_MD5_MAX_DATA_LEN] = { + /* "Hi There" */ + { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65}, + + /* what do ya want for nothing?*/ + { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, + 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, + 0x69, 0x6e, 0x67, 0x3f }, + + { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd } +}; + +static uint8_t hmac_md5_reference_digest[][HMAC_MD5_DIGEST_LEN] = { + { 0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c, + 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d }, + + { 0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03, + 0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38 }, + + { 0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88, + 0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6 } +}; + +static uint8_t hmac_sha256_reference_key[][HMAC_SHA256_KEY_LEN] = { + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b }, + + /* "Jefe" */ + { 0x4a, 0x65, 0x66, 0x65 }, + + { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa } +}; + +static uint32_t hmac_sha256_reference_length[] = { 8, 28, 50 }; + +static uint8_t +hmac_sha256_reference_plaintext[][HMAC_SHA256_MAX_DATA_LEN] = { + /* "Hi There" */ + { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65}, + + /* what do ya want for nothing?*/ + { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, + 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, + 0x69, 0x6e, 0x67, 0x3f }, + + { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd } +}; + +static uint8_t hmac_sha256_reference_digest[][HMAC_SHA256_DIGEST_LEN] = { + { 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, + 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b }, + + { 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, + 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7 }, + + { 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, + 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7 } +}; + +#endif diff --git a/test/common_plat/validation/api/crypto/test_vectors_len.h b/test/common_plat/validation/api/crypto/test_vectors_len.h new file mode 100644 index 000000000..4fbb5cd70 --- /dev/null +++ b/test/common_plat/validation/api/crypto/test_vectors_len.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef TEST_VECTORS_LEN_ +#define TEST_VECTORS_LEN_ + +/* TDES-CBC */ +#define TDES_CBC_KEY_LEN 24 +#define TDES_CBC_IV_LEN 8 +#define TDES_CBC_MAX_DATA_LEN 16 + +/* AES128-CBC */ +#define AES128_CBC_KEY_LEN 16 +#define AES128_CBC_IV_LEN 16 +#define AES128_CBC_MAX_DATA_LEN 64 + +/* AES128-CBC */ +#define AES128_GCM_KEY_LEN 16 +#define AES128_GCM_IV_LEN 12 +#define AES128_GCM_MAX_DATA_LEN 106 +#define AES128_GCM_DIGEST_LEN 16 +#define AES128_GCM_CHECK_LEN 16 + +/* HMAC-MD5 */ +#define HMAC_MD5_KEY_LEN 16 +#define HMAC_MD5_MAX_DATA_LEN 128 +#define HMAC_MD5_DIGEST_LEN 16 +#define HMAC_MD5_96_CHECK_LEN 12 + +/* HMAC-SHA256 */ +#define HMAC_SHA256_KEY_LEN 32 +#define HMAC_SHA256_MAX_DATA_LEN 128 +#define HMAC_SHA256_DIGEST_LEN 32 +#define HMAC_SHA256_128_CHECK_LEN 16 + +#endif diff --git a/test/common_plat/validation/api/errno/.gitignore b/test/common_plat/validation/api/errno/.gitignore new file mode 100644 index 000000000..12256e38c --- /dev/null +++ b/test/common_plat/validation/api/errno/.gitignore @@ -0,0 +1 @@ +errno_main diff --git a/test/common_plat/validation/api/errno/Makefile.am b/test/common_plat/validation/api/errno/Makefile.am new file mode 100644 index 000000000..a24275d6e --- /dev/null +++ b/test/common_plat/validation/api/errno/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtesterrno.la +libtesterrno_la_SOURCES = errno.c + +test_PROGRAMS = errno_main$(EXEEXT) +dist_errno_main_SOURCES = errno_main.c +errno_main_LDADD = libtesterrno.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = errno.h diff --git a/test/common_plat/validation/api/errno/errno.c b/test/common_plat/validation/api/errno/errno.c new file mode 100644 index 000000000..e3b6ced54 --- /dev/null +++ b/test/common_plat/validation/api/errno/errno.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include "odp_cunit_common.h" +#include "errno.h" + +void errno_test_odp_errno_sunny_day(void) +{ + int my_errno; + + odp_errno_zero(); + my_errno = odp_errno(); + CU_ASSERT_TRUE(my_errno == 0); + odp_errno_print("odp_errno"); + CU_ASSERT_PTR_NOT_NULL(odp_errno_str(my_errno)); +} + +odp_testinfo_t errno_suite[] = { + ODP_TEST_INFO(errno_test_odp_errno_sunny_day), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t errno_suites[] = { + {"Errno", NULL, NULL, errno_suite}, + ODP_SUITE_INFO_NULL, +}; + +int errno_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(errno_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/errno/errno.h b/test/common_plat/validation/api/errno/errno.h new file mode 100644 index 000000000..720385196 --- /dev/null +++ b/test/common_plat/validation/api/errno/errno.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_ERRNO_H_ +#define _ODP_TEST_ERRNO_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void errno_test_odp_errno_sunny_day(void); + +/* test arrays: */ +extern odp_testinfo_t errno_suite[]; + +/* test registry: */ +extern odp_suiteinfo_t errno_suites[]; + +/* main test program: */ +int errno_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/errno/errno_main.c b/test/common_plat/validation/api/errno/errno_main.c new file mode 100644 index 000000000..0138279ef --- /dev/null +++ b/test/common_plat/validation/api/errno/errno_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "errno.h" + +int main(int argc, char *argv[]) +{ + return errno_main(argc, argv); +} diff --git a/test/common_plat/validation/api/hash/.gitignore b/test/common_plat/validation/api/hash/.gitignore new file mode 100644 index 000000000..6d0bc9314 --- /dev/null +++ b/test/common_plat/validation/api/hash/.gitignore @@ -0,0 +1 @@ +hash_main diff --git a/test/common_plat/validation/api/hash/Makefile.am b/test/common_plat/validation/api/hash/Makefile.am new file mode 100644 index 000000000..b899b8bd3 --- /dev/null +++ b/test/common_plat/validation/api/hash/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtesthash.la +libtesthash_la_SOURCES = hash.c + +test_PROGRAMS = hash_main$(EXEEXT) +dist_hash_main_SOURCES = hash_main.c +hash_main_LDADD = libtesthash.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = hash.h diff --git a/test/common_plat/validation/api/hash/hash.c b/test/common_plat/validation/api/hash/hash.c new file mode 100644 index 000000000..b353fcecd --- /dev/null +++ b/test/common_plat/validation/api/hash/hash.c @@ -0,0 +1,54 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include <odp_cunit_common.h> +#include "hash.h" + +void hash_test_crc32c(void) +{ + uint32_t test_value = 0x12345678; + uint32_t ret = odp_hash_crc32c(&test_value, 4, 0); + + CU_ASSERT(ret == 0xfa745634); + + test_value = 0x87654321; + ret = odp_hash_crc32c(&test_value, 4, 0); + + CU_ASSERT(ret == 0xaca37da7); + + uint32_t test_values[] = {0x12345678, 0x87654321}; + + ret = odp_hash_crc32c(test_values, 8, 0); + + CU_ASSERT(ret == 0xe6e910b0); +} + +odp_testinfo_t hash_suite[] = { + ODP_TEST_INFO(hash_test_crc32c), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t hash_suites[] = { + {"Hash", NULL, NULL, hash_suite}, + ODP_SUITE_INFO_NULL +}; + +int hash_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(hash_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/hash/hash.h b/test/common_plat/validation/api/hash/hash.h new file mode 100644 index 000000000..936571e6a --- /dev/null +++ b/test/common_plat/validation/api/hash/hash.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_HASH_H_ +#define _ODP_TEST_HASH_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void hash_test_crc32c(void); + +/* test arrays: */ +extern odp_testinfo_t hash_suite[]; + +/* test registry: */ +extern odp_suiteinfo_t hash_suites[]; + +/* main test program: */ +int hash_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/hash/hash_main.c b/test/common_plat/validation/api/hash/hash_main.c new file mode 100644 index 000000000..f9818b7bb --- /dev/null +++ b/test/common_plat/validation/api/hash/hash_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "hash.h" + +int main(int argc, char *argv[]) +{ + return hash_main(argc, argv); +} diff --git a/test/common_plat/validation/api/init/.gitignore b/test/common_plat/validation/api/init/.gitignore new file mode 100644 index 000000000..f433708b0 --- /dev/null +++ b/test/common_plat/validation/api/init/.gitignore @@ -0,0 +1,3 @@ +init_main_abort +init_main_log +init_main_ok diff --git a/test/common_plat/validation/api/init/Makefile.am b/test/common_plat/validation/api/init/Makefile.am new file mode 100644 index 000000000..0793e6423 --- /dev/null +++ b/test/common_plat/validation/api/init/Makefile.am @@ -0,0 +1,16 @@ +include ../Makefile.inc +noinst_LTLIBRARIES = libtestinit.la +libtestinit_la_SOURCES = init.c + +# most platforms are expected not to support multiple ODP inits +# following each other: therefore 3 separate binaries are +# created, each containing its ODP init test. +test_PROGRAMS = init_main_abort$(EXEEXT) init_main_log$(EXEEXT) init_main_ok$(EXEEXT) +dist_init_main_abort_SOURCES = init_main_abort.c +dist_init_main_log_SOURCES = init_main_log.c +dist_init_main_ok_SOURCES = init_main_ok.c +init_main_abort_LDADD = libtestinit.la $(LIBCUNIT_COMMON) $(LIBODP) +init_main_log_LDADD = libtestinit.la $(LIBCUNIT_COMMON) $(LIBODP) +init_main_ok_LDADD = libtestinit.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = init.h diff --git a/test/common_plat/validation/api/init/init.c b/test/common_plat/validation/api/init/init.c new file mode 100644 index 000000000..61055fad5 --- /dev/null +++ b/test/common_plat/validation/api/init/init.c @@ -0,0 +1,188 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <odp_api.h> +#include <CUnit/Basic.h> +#include "init.h" + +/* flag set when the replacement logging function is used */ +int replacement_logging_used; + +/* replacement abort function: */ +static void odp_init_abort(void) ODP_NORETURN; + +/* replacement log function: */ +ODP_PRINTF_FORMAT(2, 3) +static int odp_init_log(odp_log_level_t level, const char *fmt, ...); + +/* test ODP global init, with alternate abort function */ +void init_test_odp_init_global_replace_abort(void) +{ + int status; + struct odp_init_t init_data; + odp_instance_t instance; + + memset(&init_data, 0, sizeof(init_data)); + init_data.abort_fn = &odp_init_abort; + + status = odp_init_global(&instance, &init_data, NULL); + CU_ASSERT_FATAL(status == 0); + + status = odp_term_global(instance); + CU_ASSERT(status == 0); +} + +odp_testinfo_t init_suite_abort[] = { + ODP_TEST_INFO(init_test_odp_init_global_replace_abort), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t init_suites_abort[] = { + {"Init", NULL, NULL, init_suite_abort}, + ODP_SUITE_INFO_NULL, +}; + +static void odp_init_abort(void) +{ + abort(); +} + +int init_main_abort(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + /* prevent default ODP init: */ + odp_cunit_register_global_init(NULL); + odp_cunit_register_global_term(NULL); + + /* run the tests: */ + ret = odp_cunit_register(init_suites_abort); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} + +/* test ODP global init, with alternate log function */ +void init_test_odp_init_global_replace_log(void) +{ + int status; + struct odp_init_t init_data; + odp_instance_t instance; + + memset(&init_data, 0, sizeof(init_data)); + init_data.log_fn = &odp_init_log; + + replacement_logging_used = 0; + + status = odp_init_global(&instance, &init_data, NULL); + CU_ASSERT_FATAL(status == 0); + + CU_ASSERT_TRUE(replacement_logging_used || ODP_DEBUG_PRINT == 0); + + status = odp_term_global(instance); + CU_ASSERT(status == 0); +} + +odp_testinfo_t init_suite_log[] = { + ODP_TEST_INFO(init_test_odp_init_global_replace_log), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t init_suites_log[] = { + {"Init", NULL, NULL, init_suite_log}, + ODP_SUITE_INFO_NULL, +}; + +static int odp_init_log(odp_log_level_t level __attribute__((unused)), + const char *fmt, ...) +{ + va_list args; + int r; + + /* just set a flag to be sure the replacement fn was used */ + replacement_logging_used = 1; + + va_start(args, fmt); + r = vfprintf(stderr, fmt, args); + va_end(args); + + return r; +} + +int init_main_log(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + /* prevent default ODP init: */ + odp_cunit_register_global_init(NULL); + odp_cunit_register_global_term(NULL); + + /* register the tests: */ + ret = odp_cunit_register(init_suites_log); + + /* run the tests: */ + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} + +/* test normal ODP global init */ +void init_test_odp_init_global(void) +{ + int status; + odp_instance_t instance; + + status = odp_init_global(&instance, NULL, NULL); + CU_ASSERT_FATAL(status == 0); + + status = odp_term_global(instance); + CU_ASSERT(status == 0); +} + +odp_testinfo_t init_suite_ok[] = { + ODP_TEST_INFO(init_test_odp_init_global), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t init_suites_ok[] = { + {"Init", NULL, NULL, init_suite_ok}, + ODP_SUITE_INFO_NULL, +}; + +int init_main_ok(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + /* prevent default ODP init: */ + odp_cunit_register_global_init(NULL); + odp_cunit_register_global_term(NULL); + + /* register the tests: */ + ret = odp_cunit_register(init_suites_ok); + + /* run the tests: */ + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/init/init.h b/test/common_plat/validation/api/init/init.h new file mode 100644 index 000000000..cad9cf988 --- /dev/null +++ b/test/common_plat/validation/api/init/init.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_INIT_H_ +#define _ODP_TEST_INIT_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void init_test_odp_init_global_replace_abort(void); +void init_test_odp_init_global_replace_log(void); +void init_test_odp_init_global(void); + +/* test arrays: */ +extern odp_testinfo_t init_suite_abort[]; +extern odp_testinfo_t init_suite_log[]; +extern odp_testinfo_t init_suite_ok[]; + +/* test registry: */ +extern odp_suiteinfo_t init_suites_abort[]; +extern odp_suiteinfo_t init_suites_log[]; +extern odp_suiteinfo_t init_suites_ok[]; + +/* main test program: */ +int init_main_abort(int argc, char *argv[]); +int init_main_log(int argc, char *argv[]); +int init_main_ok(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/init/init_main_abort.c b/test/common_plat/validation/api/init/init_main_abort.c new file mode 100644 index 000000000..2e0faafb8 --- /dev/null +++ b/test/common_plat/validation/api/init/init_main_abort.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "init.h" + +int main(int argc, char *argv[]) +{ + return init_main_abort(argc, argv); +} diff --git a/test/common_plat/validation/api/init/init_main_log.c b/test/common_plat/validation/api/init/init_main_log.c new file mode 100644 index 000000000..41dd00d72 --- /dev/null +++ b/test/common_plat/validation/api/init/init_main_log.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "init.h" + +int main(int argc, char *argv[]) +{ + return init_main_log(argc, argv); +} diff --git a/test/common_plat/validation/api/init/init_main_ok.c b/test/common_plat/validation/api/init/init_main_ok.c new file mode 100644 index 000000000..6053ec188 --- /dev/null +++ b/test/common_plat/validation/api/init/init_main_ok.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "init.h" + +int main(int argc, char *argv[]) +{ + return init_main_ok(argc, argv); +} diff --git a/test/common_plat/validation/api/lock/.gitignore b/test/common_plat/validation/api/lock/.gitignore new file mode 100644 index 000000000..ff16646f4 --- /dev/null +++ b/test/common_plat/validation/api/lock/.gitignore @@ -0,0 +1 @@ +lock_main diff --git a/test/common_plat/validation/api/lock/Makefile.am b/test/common_plat/validation/api/lock/Makefile.am new file mode 100644 index 000000000..29993df44 --- /dev/null +++ b/test/common_plat/validation/api/lock/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestlock.la +libtestlock_la_SOURCES = lock.c + +test_PROGRAMS = lock_main$(EXEEXT) +dist_lock_main_SOURCES = lock_main.c +lock_main_LDADD = libtestlock.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = lock.h diff --git a/test/common_plat/validation/api/lock/lock.c b/test/common_plat/validation/api/lock/lock.c new file mode 100644 index 000000000..a668a3157 --- /dev/null +++ b/test/common_plat/validation/api/lock/lock.c @@ -0,0 +1,1224 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <malloc.h> +#include <odp_api.h> +#include <CUnit/Basic.h> +#include <odp_cunit_common.h> +#include <unistd.h> +#include "lock.h" + +#define VERBOSE 0 + +#define MIN_ITERATIONS 1000 +#define MAX_ITERATIONS 30000 +#define ITER_MPLY_FACTOR 3 + +#define SLOW_BARRIER_DELAY 400 +#define BASE_DELAY 6 +#define MIN_DELAY 1 + +#define NUM_RESYNC_BARRIERS 100 + +#define GLOBAL_SHM_NAME "GlobalLockTest" + +#define UNUSED __attribute__((__unused__)) + +typedef __volatile uint32_t volatile_u32_t; +typedef __volatile uint64_t volatile_u64_t; + +typedef struct { + odp_atomic_u32_t wait_cnt; +} custom_barrier_t; + +typedef struct { + /* Global variables */ + uint32_t g_num_threads; + uint32_t g_iterations; + uint32_t g_verbose; + uint32_t g_max_num_cores; + + volatile_u32_t slow_thread_num; + volatile_u32_t barrier_cnt1; + volatile_u32_t barrier_cnt2; + odp_barrier_t global_barrier; + + /* Used to periodically resync within the lock functional tests */ + odp_barrier_t barrier_array[NUM_RESYNC_BARRIERS]; + + /* Locks */ + odp_spinlock_t global_spinlock; + odp_spinlock_recursive_t global_recursive_spinlock; + odp_ticketlock_t global_ticketlock; + odp_rwlock_t global_rwlock; + odp_rwlock_recursive_t global_recursive_rwlock; + + volatile_u32_t global_lock_owner; +} global_shared_mem_t; + +/* Per-thread memory */ +typedef struct { + global_shared_mem_t *global_mem; + + int thread_id; + int thread_core; + + odp_spinlock_t per_thread_spinlock; + odp_spinlock_recursive_t per_thread_recursive_spinlock; + odp_ticketlock_t per_thread_ticketlock; + odp_rwlock_t per_thread_rwlock; + odp_rwlock_recursive_t per_thread_recursive_rwlock; + + volatile_u64_t delay_counter; +} per_thread_mem_t; + +static odp_shm_t global_shm; +static global_shared_mem_t *global_mem; + +/* +* Delay a consistent amount of time. Ideally the amount of CPU time taken +* is linearly proportional to "iterations". The goal is to try to do some +* work that the compiler optimizer won't optimize away, and also to +* minimize loads and stores (at least to different memory addresses) +* so as to not affect or be affected by caching issues. This does NOT have to +* correlate to a specific number of cpu cycles or be consistent across +* CPU architectures. +*/ +static void thread_delay(per_thread_mem_t *per_thread_mem, uint32_t iterations) +{ + volatile_u64_t *counter_ptr; + uint32_t cnt; + + counter_ptr = &per_thread_mem->delay_counter; + + for (cnt = 1; cnt <= iterations; cnt++) + (*counter_ptr)++; +} + +/* Initialise per-thread memory */ +static per_thread_mem_t *thread_init(void) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + odp_shm_t global_shm; + uint32_t per_thread_mem_len; + + per_thread_mem_len = sizeof(per_thread_mem_t); + per_thread_mem = malloc(per_thread_mem_len); + memset(per_thread_mem, 0, per_thread_mem_len); + + per_thread_mem->delay_counter = 1; + + per_thread_mem->thread_id = odp_thread_id(); + per_thread_mem->thread_core = odp_cpu_id(); + + global_shm = odp_shm_lookup(GLOBAL_SHM_NAME); + global_mem = odp_shm_addr(global_shm); + CU_ASSERT_PTR_NOT_NULL(global_mem); + + per_thread_mem->global_mem = global_mem; + + return per_thread_mem; +} + +static void thread_finalize(per_thread_mem_t *per_thread_mem) +{ + free(per_thread_mem); +} + +static void spinlock_api_test(odp_spinlock_t *spinlock) +{ + odp_spinlock_init(spinlock); + CU_ASSERT(odp_spinlock_is_locked(spinlock) == 0); + + odp_spinlock_lock(spinlock); + CU_ASSERT(odp_spinlock_is_locked(spinlock) == 1); + + odp_spinlock_unlock(spinlock); + CU_ASSERT(odp_spinlock_is_locked(spinlock) == 0); + + CU_ASSERT(odp_spinlock_trylock(spinlock) == 1); + + CU_ASSERT(odp_spinlock_is_locked(spinlock) == 1); + + odp_spinlock_unlock(spinlock); + CU_ASSERT(odp_spinlock_is_locked(spinlock) == 0); +} + +static int spinlock_api_tests(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + odp_spinlock_t local_spin_lock; + + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + + odp_barrier_wait(&global_mem->global_barrier); + + spinlock_api_test(&local_spin_lock); + spinlock_api_test(&per_thread_mem->per_thread_spinlock); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static void spinlock_recursive_api_test(odp_spinlock_recursive_t *spinlock) +{ + odp_spinlock_recursive_init(spinlock); + CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 0); + + odp_spinlock_recursive_lock(spinlock); + CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1); + + odp_spinlock_recursive_lock(spinlock); + CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1); + + odp_spinlock_recursive_unlock(spinlock); + CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1); + + odp_spinlock_recursive_unlock(spinlock); + CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 0); + + CU_ASSERT(odp_spinlock_recursive_trylock(spinlock) == 1); + CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1); + + CU_ASSERT(odp_spinlock_recursive_trylock(spinlock) == 1); + CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1); + + odp_spinlock_recursive_unlock(spinlock); + CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 1); + + odp_spinlock_recursive_unlock(spinlock); + CU_ASSERT(odp_spinlock_recursive_is_locked(spinlock) == 0); +} + +static int spinlock_recursive_api_tests(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + odp_spinlock_recursive_t local_recursive_spin_lock; + + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + + odp_barrier_wait(&global_mem->global_barrier); + + spinlock_recursive_api_test(&local_recursive_spin_lock); + spinlock_recursive_api_test( + &per_thread_mem->per_thread_recursive_spinlock); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static void ticketlock_api_test(odp_ticketlock_t *ticketlock) +{ + odp_ticketlock_init(ticketlock); + CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 0); + + odp_ticketlock_lock(ticketlock); + CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 1); + + odp_ticketlock_unlock(ticketlock); + CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 0); + + CU_ASSERT(odp_ticketlock_trylock(ticketlock) == 1); + CU_ASSERT(odp_ticketlock_trylock(ticketlock) == 0); + CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 1); + + odp_ticketlock_unlock(ticketlock); + CU_ASSERT(odp_ticketlock_is_locked(ticketlock) == 0); +} + +static int ticketlock_api_tests(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + odp_ticketlock_t local_ticket_lock; + + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + + odp_barrier_wait(&global_mem->global_barrier); + + ticketlock_api_test(&local_ticket_lock); + ticketlock_api_test(&per_thread_mem->per_thread_ticketlock); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static void rwlock_api_test(odp_rwlock_t *rw_lock) +{ + int rc; + + odp_rwlock_init(rw_lock); + /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */ + + odp_rwlock_read_lock(rw_lock); + + rc = odp_rwlock_read_trylock(rw_lock); + CU_ASSERT(rc == 0); + rc = odp_rwlock_write_trylock(rw_lock); + CU_ASSERT(rc == 0); + + odp_rwlock_read_unlock(rw_lock); + + rc = odp_rwlock_read_trylock(rw_lock); + if (rc == 1) + odp_rwlock_read_unlock(rw_lock); + + odp_rwlock_write_lock(rw_lock); + /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 1); */ + + odp_rwlock_write_unlock(rw_lock); + /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */ + + rc = odp_rwlock_write_trylock(rw_lock); + if (rc == 1) + odp_rwlock_write_unlock(rw_lock); +} + +static int rwlock_api_tests(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + odp_rwlock_t local_rwlock; + + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + + odp_barrier_wait(&global_mem->global_barrier); + + rwlock_api_test(&local_rwlock); + rwlock_api_test(&per_thread_mem->per_thread_rwlock); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static void rwlock_recursive_api_test(odp_rwlock_recursive_t *rw_lock) +{ + int rc; + + odp_rwlock_recursive_init(rw_lock); + /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */ + + odp_rwlock_recursive_read_lock(rw_lock); + odp_rwlock_recursive_read_lock(rw_lock); + rc = odp_rwlock_recursive_read_trylock(rw_lock); + CU_ASSERT(rc == 1); + rc = odp_rwlock_recursive_write_trylock(rw_lock); + CU_ASSERT(rc == 0); + + odp_rwlock_recursive_read_unlock(rw_lock); + odp_rwlock_recursive_read_unlock(rw_lock); + odp_rwlock_recursive_read_unlock(rw_lock); + + odp_rwlock_recursive_write_lock(rw_lock); + odp_rwlock_recursive_write_lock(rw_lock); + /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 1); */ + rc = odp_rwlock_recursive_read_trylock(rw_lock); + CU_ASSERT(rc == 0); + rc = odp_rwlock_recursive_write_trylock(rw_lock); + CU_ASSERT(rc == 1); + + odp_rwlock_recursive_write_unlock(rw_lock); + odp_rwlock_recursive_write_unlock(rw_lock); + odp_rwlock_recursive_write_unlock(rw_lock); + /* CU_ASSERT(odp_rwlock_is_locked(rw_lock) == 0); */ +} + +static int rwlock_recursive_api_tests(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + odp_rwlock_recursive_t local_recursive_rwlock; + + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + + odp_barrier_wait(&global_mem->global_barrier); + + rwlock_recursive_api_test(&local_recursive_rwlock); + rwlock_recursive_api_test(&per_thread_mem->per_thread_recursive_rwlock); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +/* + * Tests that we do have contention between threads when running. + * Also adjust the number of iterations to be done (by other tests) + * so we have a fair chance to see that the tested synchronizer + * does avoid the race condition. + */ +static int no_lock_functional_test(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt; + uint32_t sync_failures, current_errs, lock_owner_delay; + + thread_num = odp_cpu_id() + 1; + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + iterations = 0; + + odp_barrier_wait(&global_mem->global_barrier); + + sync_failures = 0; + current_errs = 0; + rs_idx = 0; + resync_cnt = MAX_ITERATIONS / NUM_RESYNC_BARRIERS; + lock_owner_delay = BASE_DELAY; + + /* + * Tunning the iteration number: + * Here, we search for an iteration number that guarantees to show + * race conditions between the odp threads. + * Iterations is set to ITER_MPLY_FACTOR * cnt where cnt is when + * the threads start to see "errors" (i.e. effect of other threads + * running concurrentely without any synchronisation mechanism). + * In other words, "iterations" is set to ITER_MPLY_FACTOR times the + * minimum loop count necessary to see a need for synchronisation + * mechanism. + * If, later, these "errors" disappear when running other tests up to + * "iterations" with synchro, the effect of the tested synchro mechanism + * is likely proven. + * If we reach "MAX_ITERATIONS", and "iteration" remains zero, + * it means that we cannot see any race condition between the different + * running theads (e.g. the OS is not preemptive) and all other tests + * being passed won't tell much about the functionality of the + * tested synchro mechanism. + */ + for (cnt = 1; cnt <= MAX_ITERATIONS; cnt++) { + global_mem->global_lock_owner = thread_num; + odp_mb_full(); + thread_delay(per_thread_mem, lock_owner_delay); + + if (global_mem->global_lock_owner != thread_num) { + current_errs++; + sync_failures++; + if (!iterations) + iterations = cnt; + } + + global_mem->global_lock_owner = 0; + odp_mb_full(); + thread_delay(per_thread_mem, MIN_DELAY); + + if (global_mem->global_lock_owner == thread_num) { + current_errs++; + sync_failures++; + if (!iterations) + iterations = cnt; + } + + if (current_errs == 0) + lock_owner_delay++; + + /* Wait a small amount of time and rerun the test */ + thread_delay(per_thread_mem, BASE_DELAY); + + /* Try to resync all of the threads to increase contention */ + if ((rs_idx < NUM_RESYNC_BARRIERS) && + ((cnt % resync_cnt) == (resync_cnt - 1))) + odp_barrier_wait(&global_mem->barrier_array[rs_idx++]); + } + + if (global_mem->g_verbose) + printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32 + " sync_failures in %" PRIu32 " iterations\n", + thread_num, + per_thread_mem->thread_id, + per_thread_mem->thread_core, + sync_failures, iterations); + + /* Note that the following CU_ASSERT MAY appear incorrect, but for the + * no_lock test it should see sync_failures or else there is something + * wrong with the test methodology or the ODP thread implementation. + * So this test PASSES only if it sees sync_failures or a single + * worker was used. + */ + CU_ASSERT(sync_failures != 0 || global_mem->g_num_threads == 1); + + /* + * set the iterration for the future tests to be far above the + * contention level + */ + iterations *= ITER_MPLY_FACTOR; + + if (iterations > MAX_ITERATIONS) + iterations = MAX_ITERATIONS; + if (iterations < MIN_ITERATIONS) + iterations = MIN_ITERATIONS; + + /* + * Note that the following statement has race conditions: + * global_mem->g_iterations should really be an atomic and a TAS + * function be used. But this would mean that we would be testing + * synchronisers assuming synchronisers works... + * If we do not use atomic TAS, we may not get the grand max for + * all threads, but we are guaranteed to have passed the error + * threshold, for at least some threads, which is good enough + */ + if (iterations > global_mem->g_iterations) + global_mem->g_iterations = iterations; + + odp_mb_full(); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int spinlock_functional_test(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt; + uint32_t sync_failures, is_locked_errs, current_errs; + uint32_t lock_owner_delay; + + thread_num = odp_cpu_id() + 1; + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + iterations = global_mem->g_iterations; + + odp_barrier_wait(&global_mem->global_barrier); + + sync_failures = 0; + is_locked_errs = 0; + current_errs = 0; + rs_idx = 0; + resync_cnt = iterations / NUM_RESYNC_BARRIERS; + lock_owner_delay = BASE_DELAY; + + for (cnt = 1; cnt <= iterations; cnt++) { + /* Acquire the shared global lock */ + odp_spinlock_lock(&global_mem->global_spinlock); + + /* Make sure we have the lock AND didn't previously own it */ + if (odp_spinlock_is_locked(&global_mem->global_spinlock) != 1) + is_locked_errs++; + + if (global_mem->global_lock_owner != 0) { + current_errs++; + sync_failures++; + } + + /* Now set the global_lock_owner to be us, wait a while, and + * then we see if anyone else has snuck in and changed the + * global_lock_owner to be themselves + */ + global_mem->global_lock_owner = thread_num; + odp_mb_full(); + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != thread_num) { + current_errs++; + sync_failures++; + } + + /* Release shared lock, and make sure we no longer have it */ + global_mem->global_lock_owner = 0; + odp_mb_full(); + odp_spinlock_unlock(&global_mem->global_spinlock); + if (global_mem->global_lock_owner == thread_num) { + current_errs++; + sync_failures++; + } + + if (current_errs == 0) + lock_owner_delay++; + + /* Wait a small amount of time and rerun the test */ + thread_delay(per_thread_mem, BASE_DELAY); + + /* Try to resync all of the threads to increase contention */ + if ((rs_idx < NUM_RESYNC_BARRIERS) && + ((cnt % resync_cnt) == (resync_cnt - 1))) + odp_barrier_wait(&global_mem->barrier_array[rs_idx++]); + } + + if ((global_mem->g_verbose) && + ((sync_failures != 0) || (is_locked_errs != 0))) + printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32 + " sync_failures and %" PRIu32 + " is_locked_errs in %" PRIu32 + " iterations\n", thread_num, + per_thread_mem->thread_id, per_thread_mem->thread_core, + sync_failures, is_locked_errs, iterations); + + CU_ASSERT(sync_failures == 0); + CU_ASSERT(is_locked_errs == 0); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int spinlock_recursive_functional_test(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt; + uint32_t sync_failures, recursive_errs, is_locked_errs, current_errs; + uint32_t lock_owner_delay; + + thread_num = odp_cpu_id() + 1; + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + iterations = global_mem->g_iterations; + + odp_barrier_wait(&global_mem->global_barrier); + + sync_failures = 0; + recursive_errs = 0; + is_locked_errs = 0; + current_errs = 0; + rs_idx = 0; + resync_cnt = iterations / NUM_RESYNC_BARRIERS; + lock_owner_delay = BASE_DELAY; + + for (cnt = 1; cnt <= iterations; cnt++) { + /* Acquire the shared global lock */ + odp_spinlock_recursive_lock( + &global_mem->global_recursive_spinlock); + + /* Make sure we have the lock AND didn't previously own it */ + if (odp_spinlock_recursive_is_locked( + &global_mem->global_recursive_spinlock) != 1) + is_locked_errs++; + + if (global_mem->global_lock_owner != 0) { + current_errs++; + sync_failures++; + } + + /* Now set the global_lock_owner to be us, wait a while, and + * then we see if anyone else has snuck in and changed the + * global_lock_owner to be themselves + */ + global_mem->global_lock_owner = thread_num; + odp_mb_full(); + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != thread_num) { + current_errs++; + sync_failures++; + } + + /* Verify that we can acquire the lock recursively */ + odp_spinlock_recursive_lock( + &global_mem->global_recursive_spinlock); + if (global_mem->global_lock_owner != thread_num) { + current_errs++; + recursive_errs++; + } + + /* Release the lock and verify that we still have it*/ + odp_spinlock_recursive_unlock( + &global_mem->global_recursive_spinlock); + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != thread_num) { + current_errs++; + recursive_errs++; + } + + /* Release shared lock, and make sure we no longer have it */ + global_mem->global_lock_owner = 0; + odp_mb_full(); + odp_spinlock_recursive_unlock( + &global_mem->global_recursive_spinlock); + if (global_mem->global_lock_owner == thread_num) { + current_errs++; + sync_failures++; + } + + if (current_errs == 0) + lock_owner_delay++; + + /* Wait a small amount of time and rerun the test */ + thread_delay(per_thread_mem, BASE_DELAY); + + /* Try to resync all of the threads to increase contention */ + if ((rs_idx < NUM_RESYNC_BARRIERS) && + ((cnt % resync_cnt) == (resync_cnt - 1))) + odp_barrier_wait(&global_mem->barrier_array[rs_idx++]); + } + + if ((global_mem->g_verbose) && + (sync_failures != 0 || recursive_errs != 0 || is_locked_errs != 0)) + printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32 + " sync_failures and %" PRIu32 + " recursive_errs and %" PRIu32 + " is_locked_errs in %" PRIu32 + " iterations\n", thread_num, + per_thread_mem->thread_id, per_thread_mem->thread_core, + sync_failures, recursive_errs, is_locked_errs, + iterations); + + CU_ASSERT(sync_failures == 0); + CU_ASSERT(recursive_errs == 0); + CU_ASSERT(is_locked_errs == 0); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int ticketlock_functional_test(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt; + uint32_t sync_failures, is_locked_errs, current_errs; + uint32_t lock_owner_delay; + + thread_num = odp_cpu_id() + 1; + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + iterations = global_mem->g_iterations; + + /* Wait here until all of the threads have also reached this point */ + odp_barrier_wait(&global_mem->global_barrier); + + sync_failures = 0; + is_locked_errs = 0; + current_errs = 0; + rs_idx = 0; + resync_cnt = iterations / NUM_RESYNC_BARRIERS; + lock_owner_delay = BASE_DELAY; + + for (cnt = 1; cnt <= iterations; cnt++) { + /* Acquire the shared global lock */ + odp_ticketlock_lock(&global_mem->global_ticketlock); + + /* Make sure we have the lock AND didn't previously own it */ + if (odp_ticketlock_is_locked(&global_mem->global_ticketlock) + != 1) + is_locked_errs++; + + if (global_mem->global_lock_owner != 0) { + current_errs++; + sync_failures++; + } + + /* Now set the global_lock_owner to be us, wait a while, and + * then we see if anyone else has snuck in and changed the + * global_lock_owner to be themselves + */ + global_mem->global_lock_owner = thread_num; + odp_mb_full(); + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != thread_num) { + current_errs++; + sync_failures++; + } + + /* Release shared lock, and make sure we no longer have it */ + global_mem->global_lock_owner = 0; + odp_mb_full(); + odp_ticketlock_unlock(&global_mem->global_ticketlock); + if (global_mem->global_lock_owner == thread_num) { + current_errs++; + sync_failures++; + } + + if (current_errs == 0) + lock_owner_delay++; + + /* Wait a small amount of time and then rerun the test */ + thread_delay(per_thread_mem, BASE_DELAY); + + /* Try to resync all of the threads to increase contention */ + if ((rs_idx < NUM_RESYNC_BARRIERS) && + ((cnt % resync_cnt) == (resync_cnt - 1))) + odp_barrier_wait(&global_mem->barrier_array[rs_idx++]); + } + + if ((global_mem->g_verbose) && + ((sync_failures != 0) || (is_locked_errs != 0))) + printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32 + " sync_failures and %" PRIu32 + " is_locked_errs in %" PRIu32 " iterations\n", + thread_num, + per_thread_mem->thread_id, per_thread_mem->thread_core, + sync_failures, is_locked_errs, iterations); + + CU_ASSERT(sync_failures == 0); + CU_ASSERT(is_locked_errs == 0); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int rwlock_functional_test(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt; + uint32_t sync_failures, current_errs, lock_owner_delay; + + thread_num = odp_cpu_id() + 1; + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + iterations = global_mem->g_iterations; + + /* Wait here until all of the threads have also reached this point */ + odp_barrier_wait(&global_mem->global_barrier); + + sync_failures = 0; + current_errs = 0; + rs_idx = 0; + resync_cnt = iterations / NUM_RESYNC_BARRIERS; + lock_owner_delay = BASE_DELAY; + + for (cnt = 1; cnt <= iterations; cnt++) { + /* Verify that we can obtain a read lock */ + odp_rwlock_read_lock(&global_mem->global_rwlock); + + /* Verify lock is unowned (no writer holds it) */ + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != 0) { + current_errs++; + sync_failures++; + } + + /* Release the read lock */ + odp_rwlock_read_unlock(&global_mem->global_rwlock); + + /* Acquire the shared global lock */ + odp_rwlock_write_lock(&global_mem->global_rwlock); + + /* Make sure we have lock now AND didn't previously own it */ + if (global_mem->global_lock_owner != 0) { + current_errs++; + sync_failures++; + } + + /* Now set the global_lock_owner to be us, wait a while, and + * then we see if anyone else has snuck in and changed the + * global_lock_owner to be themselves + */ + global_mem->global_lock_owner = thread_num; + odp_mb_full(); + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != thread_num) { + current_errs++; + sync_failures++; + } + + /* Release shared lock, and make sure we no longer have it */ + global_mem->global_lock_owner = 0; + odp_mb_full(); + odp_rwlock_write_unlock(&global_mem->global_rwlock); + if (global_mem->global_lock_owner == thread_num) { + current_errs++; + sync_failures++; + } + + if (current_errs == 0) + lock_owner_delay++; + + /* Wait a small amount of time and then rerun the test */ + thread_delay(per_thread_mem, BASE_DELAY); + + /* Try to resync all of the threads to increase contention */ + if ((rs_idx < NUM_RESYNC_BARRIERS) && + ((cnt % resync_cnt) == (resync_cnt - 1))) + odp_barrier_wait(&global_mem->barrier_array[rs_idx++]); + } + + if ((global_mem->g_verbose) && (sync_failures != 0)) + printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32 + " sync_failures in %" PRIu32 " iterations\n", thread_num, + per_thread_mem->thread_id, + per_thread_mem->thread_core, + sync_failures, iterations); + + CU_ASSERT(sync_failures == 0); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +static int rwlock_recursive_functional_test(void *arg UNUSED) +{ + global_shared_mem_t *global_mem; + per_thread_mem_t *per_thread_mem; + uint32_t thread_num, resync_cnt, rs_idx, iterations, cnt; + uint32_t sync_failures, recursive_errs, current_errs, lock_owner_delay; + + thread_num = odp_cpu_id() + 1; + per_thread_mem = thread_init(); + global_mem = per_thread_mem->global_mem; + iterations = global_mem->g_iterations; + + /* Wait here until all of the threads have also reached this point */ + odp_barrier_wait(&global_mem->global_barrier); + + sync_failures = 0; + recursive_errs = 0; + current_errs = 0; + rs_idx = 0; + resync_cnt = iterations / NUM_RESYNC_BARRIERS; + lock_owner_delay = BASE_DELAY; + + for (cnt = 1; cnt <= iterations; cnt++) { + /* Verify that we can obtain a read lock */ + odp_rwlock_recursive_read_lock( + &global_mem->global_recursive_rwlock); + + /* Verify lock is unowned (no writer holds it) */ + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != 0) { + current_errs++; + sync_failures++; + } + + /* Verify we can get read lock recursively */ + odp_rwlock_recursive_read_lock( + &global_mem->global_recursive_rwlock); + + /* Verify lock is unowned (no writer holds it) */ + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != 0) { + current_errs++; + sync_failures++; + } + + /* Release the read lock */ + odp_rwlock_recursive_read_unlock( + &global_mem->global_recursive_rwlock); + odp_rwlock_recursive_read_unlock( + &global_mem->global_recursive_rwlock); + + /* Acquire the shared global lock */ + odp_rwlock_recursive_write_lock( + &global_mem->global_recursive_rwlock); + + /* Make sure we have lock now AND didn't previously own it */ + if (global_mem->global_lock_owner != 0) { + current_errs++; + sync_failures++; + } + + /* Now set the global_lock_owner to be us, wait a while, and + * then we see if anyone else has snuck in and changed the + * global_lock_owner to be themselves + */ + global_mem->global_lock_owner = thread_num; + odp_mb_full(); + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != thread_num) { + current_errs++; + sync_failures++; + } + + /* Acquire it again and verify we still own it */ + odp_rwlock_recursive_write_lock( + &global_mem->global_recursive_rwlock); + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != thread_num) { + current_errs++; + recursive_errs++; + } + + /* Release the recursive lock and make sure we still own it */ + odp_rwlock_recursive_write_unlock( + &global_mem->global_recursive_rwlock); + thread_delay(per_thread_mem, lock_owner_delay); + if (global_mem->global_lock_owner != thread_num) { + current_errs++; + recursive_errs++; + } + + /* Release shared lock, and make sure we no longer have it */ + global_mem->global_lock_owner = 0; + odp_mb_full(); + odp_rwlock_recursive_write_unlock( + &global_mem->global_recursive_rwlock); + if (global_mem->global_lock_owner == thread_num) { + current_errs++; + sync_failures++; + } + + if (current_errs == 0) + lock_owner_delay++; + + /* Wait a small amount of time and then rerun the test */ + thread_delay(per_thread_mem, BASE_DELAY); + + /* Try to resync all of the threads to increase contention */ + if ((rs_idx < NUM_RESYNC_BARRIERS) && + ((cnt % resync_cnt) == (resync_cnt - 1))) + odp_barrier_wait(&global_mem->barrier_array[rs_idx++]); + } + + if ((global_mem->g_verbose) && (sync_failures != 0)) + printf("\nThread %" PRIu32 " (id=%d core=%d) had %" PRIu32 + " sync_failures and %" PRIu32 + " recursive_errs in %" PRIu32 + " iterations\n", thread_num, + per_thread_mem->thread_id, + per_thread_mem->thread_core, + sync_failures, recursive_errs, iterations); + + CU_ASSERT(sync_failures == 0); + CU_ASSERT(recursive_errs == 0); + + thread_finalize(per_thread_mem); + + return CU_get_number_of_failures(); +} + +/* Thread-unsafe tests */ +void lock_test_no_lock_functional(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_cunit_thread_create(no_lock_functional_test, &arg); + odp_cunit_thread_exit(&arg); +} + +odp_testinfo_t lock_suite_no_locking[] = { + ODP_TEST_INFO(lock_test_no_lock_functional), /* must be first */ + ODP_TEST_INFO_NULL +}; + +/* Spin lock tests */ +void lock_test_spinlock_api(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_cunit_thread_create(spinlock_api_tests, &arg); + odp_cunit_thread_exit(&arg); +} + +void lock_test_spinlock_functional(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_spinlock_init(&global_mem->global_spinlock); + odp_cunit_thread_create(spinlock_functional_test, &arg); + odp_cunit_thread_exit(&arg); +} + +void lock_test_spinlock_recursive_api(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_cunit_thread_create(spinlock_recursive_api_tests, &arg); + odp_cunit_thread_exit(&arg); +} + +void lock_test_spinlock_recursive_functional(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_spinlock_recursive_init(&global_mem->global_recursive_spinlock); + odp_cunit_thread_create(spinlock_recursive_functional_test, &arg); + odp_cunit_thread_exit(&arg); +} + +odp_testinfo_t lock_suite_spinlock[] = { + ODP_TEST_INFO(lock_test_spinlock_api), + ODP_TEST_INFO(lock_test_spinlock_functional), + ODP_TEST_INFO_NULL +}; + +odp_testinfo_t lock_suite_spinlock_recursive[] = { + ODP_TEST_INFO(lock_test_spinlock_recursive_api), + ODP_TEST_INFO(lock_test_spinlock_recursive_functional), + ODP_TEST_INFO_NULL +}; + +/* Ticket lock tests */ +void lock_test_ticketlock_api(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_cunit_thread_create(ticketlock_api_tests, &arg); + odp_cunit_thread_exit(&arg); +} + +void lock_test_ticketlock_functional(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_ticketlock_init(&global_mem->global_ticketlock); + + odp_cunit_thread_create(ticketlock_functional_test, &arg); + odp_cunit_thread_exit(&arg); +} + +odp_testinfo_t lock_suite_ticketlock[] = { + ODP_TEST_INFO(lock_test_ticketlock_api), + ODP_TEST_INFO(lock_test_ticketlock_functional), + ODP_TEST_INFO_NULL +}; + +/* RW lock tests */ +void lock_test_rwlock_api(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_cunit_thread_create(rwlock_api_tests, &arg); + odp_cunit_thread_exit(&arg); +} + +void lock_test_rwlock_functional(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_rwlock_init(&global_mem->global_rwlock); + odp_cunit_thread_create(rwlock_functional_test, &arg); + odp_cunit_thread_exit(&arg); +} + +odp_testinfo_t lock_suite_rwlock[] = { + ODP_TEST_INFO(lock_test_rwlock_api), + ODP_TEST_INFO(lock_test_rwlock_functional), + ODP_TEST_INFO_NULL +}; + +void lock_test_rwlock_recursive_api(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_cunit_thread_create(rwlock_recursive_api_tests, &arg); + odp_cunit_thread_exit(&arg); +} + +void lock_test_rwlock_recursive_functional(void) +{ + pthrd_arg arg; + + arg.numthrds = global_mem->g_num_threads; + odp_rwlock_recursive_init(&global_mem->global_recursive_rwlock); + odp_cunit_thread_create(rwlock_recursive_functional_test, &arg); + odp_cunit_thread_exit(&arg); +} + +odp_testinfo_t lock_suite_rwlock_recursive[] = { + ODP_TEST_INFO(lock_test_rwlock_recursive_api), + ODP_TEST_INFO(lock_test_rwlock_recursive_functional), + ODP_TEST_INFO_NULL +}; + +int lock_suite_init(void) +{ + uint32_t num_threads, idx; + + num_threads = global_mem->g_num_threads; + odp_barrier_init(&global_mem->global_barrier, num_threads); + for (idx = 0; idx < NUM_RESYNC_BARRIERS; idx++) + odp_barrier_init(&global_mem->barrier_array[idx], num_threads); + + return 0; +} + +int lock_init(odp_instance_t *inst) +{ + uint32_t workers_count, max_threads; + int ret = 0; + odp_cpumask_t mask; + + if (0 != odp_init_global(inst, NULL, NULL)) { + fprintf(stderr, "error: odp_init_global() failed.\n"); + return -1; + } + if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) { + fprintf(stderr, "error: odp_init_local() failed.\n"); + return -1; + } + + global_shm = odp_shm_reserve(GLOBAL_SHM_NAME, + sizeof(global_shared_mem_t), 64, + ODP_SHM_SW_ONLY); + if (ODP_SHM_INVALID == global_shm) { + fprintf(stderr, "Unable reserve memory for global_shm\n"); + return -1; + } + + global_mem = odp_shm_addr(global_shm); + memset(global_mem, 0, sizeof(global_shared_mem_t)); + + global_mem->g_num_threads = MAX_WORKERS; + global_mem->g_iterations = 0; /* tuned by first test */ + global_mem->g_verbose = VERBOSE; + + workers_count = odp_cpumask_default_worker(&mask, 0); + + max_threads = (workers_count >= MAX_WORKERS) ? + MAX_WORKERS : workers_count; + + if (max_threads < global_mem->g_num_threads) { + printf("Requested num of threads is too large\n"); + printf("reducing from %" PRIu32 " to %" PRIu32 "\n", + global_mem->g_num_threads, + max_threads); + global_mem->g_num_threads = max_threads; + } + + printf("Num of threads used = %" PRIu32 "\n", + global_mem->g_num_threads); + + return ret; +} + +odp_suiteinfo_t lock_suites[] = { + {"nolocking", lock_suite_init, NULL, + lock_suite_no_locking}, /* must be first */ + {"spinlock", lock_suite_init, NULL, + lock_suite_spinlock}, + {"spinlock_recursive", lock_suite_init, NULL, + lock_suite_spinlock_recursive}, + {"ticketlock", lock_suite_init, NULL, + lock_suite_ticketlock}, + {"rwlock", lock_suite_init, NULL, + lock_suite_rwlock}, + {"rwlock_recursive", lock_suite_init, NULL, + lock_suite_rwlock_recursive}, + ODP_SUITE_INFO_NULL +}; + +int lock_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + odp_cunit_register_global_init(lock_init); + + ret = odp_cunit_register(lock_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/lock/lock.h b/test/common_plat/validation/api/lock/lock.h new file mode 100644 index 000000000..5adc63352 --- /dev/null +++ b/test/common_plat/validation/api/lock/lock.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_LOCK_H_ +#define _ODP_TEST_LOCK_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void lock_test_no_lock_functional(void); +void lock_test_spinlock_api(void); +void lock_test_spinlock_functional(void); +void lock_test_spinlock_recursive_api(void); +void lock_test_spinlock_recursive_functional(void); +void lock_test_ticketlock_api(void); +void lock_test_ticketlock_functional(void); +void lock_test_rwlock_api(void); +void lock_test_rwlock_functional(void); +void lock_test_rwlock_recursive_api(void); +void lock_test_rwlock_recursive_functional(void); + +/* test arrays: */ +extern odp_testinfo_t lock_suite_no_locking[]; +extern odp_testinfo_t lock_suite_spinlock[]; +extern odp_testinfo_t lock_suite_spinlock_recursive[]; +extern odp_testinfo_t lock_suite_ticketlock[]; +extern odp_testinfo_t lock_suite_rwlock[]; +extern odp_testinfo_t lock_suite_rwlock_recursive[]; + +/* test array init/term functions: */ +int lock_suite_init(void); + +/* test registry: */ +extern odp_suiteinfo_t lock_suites[]; + +/* executable init/term functions: */ +int lock_init(odp_instance_t *inst); + +/* main test program: */ +int lock_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/lock/lock_main.c b/test/common_plat/validation/api/lock/lock_main.c new file mode 100644 index 000000000..5a30f02b4 --- /dev/null +++ b/test/common_plat/validation/api/lock/lock_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "lock.h" + +int main(int argc, char *argv[]) +{ + return lock_main(argc, argv); +} diff --git a/test/common_plat/validation/api/packet/.gitignore b/test/common_plat/validation/api/packet/.gitignore new file mode 100644 index 000000000..c05530d2d --- /dev/null +++ b/test/common_plat/validation/api/packet/.gitignore @@ -0,0 +1 @@ +packet_main diff --git a/test/common_plat/validation/api/packet/Makefile.am b/test/common_plat/validation/api/packet/Makefile.am new file mode 100644 index 000000000..d8ebc1a23 --- /dev/null +++ b/test/common_plat/validation/api/packet/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestpacket.la +libtestpacket_la_SOURCES = packet.c + +test_PROGRAMS = packet_main$(EXEEXT) +dist_packet_main_SOURCES = packet_main.c +packet_main_LDADD = libtestpacket.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = packet.h diff --git a/test/common_plat/validation/api/packet/packet.c b/test/common_plat/validation/api/packet/packet.c new file mode 100644 index 000000000..a4426e22f --- /dev/null +++ b/test/common_plat/validation/api/packet/packet.c @@ -0,0 +1,1369 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdlib.h> + +#include <odp_api.h> +#include <odp_cunit_common.h> +#include "packet.h" + +#define PACKET_BUF_LEN ODP_CONFIG_PACKET_SEG_LEN_MIN +/* Reserve some tailroom for tests */ +#define PACKET_TAILROOM_RESERVE 4 + +static odp_pool_t packet_pool, packet_pool_no_uarea, packet_pool_double_uarea; +static uint32_t packet_len; + +static uint32_t segmented_packet_len; +static odp_bool_t segmentation_supported = true; + +odp_packet_t test_packet, segmented_test_packet; + +static struct udata_struct { + uint64_t u64; + uint32_t u32; + char str[10]; +} test_packet_udata = { + 123456, + 789912, + "abcdefg", +}; + +int packet_suite_init(void) +{ + odp_pool_param_t params; + odp_pool_capability_t capa; + struct udata_struct *udat; + uint32_t udat_size; + uint8_t data = 0; + uint32_t i; + + if (odp_pool_capability(&capa) < 0) + return -1; + + packet_len = capa.pkt.min_seg_len - PACKET_TAILROOM_RESERVE; + + if (capa.pkt.max_len) { + segmented_packet_len = capa.pkt.max_len; + } else { + segmented_packet_len = capa.pkt.min_seg_len * + capa.pkt.max_segs_per_pkt; + } + + odp_pool_param_init(¶ms); + + params.type = ODP_POOL_PACKET; + params.pkt.seg_len = capa.pkt.min_seg_len; + params.pkt.len = capa.pkt.min_seg_len; + params.pkt.num = 100; + params.pkt.uarea_size = sizeof(struct udata_struct); + + packet_pool = odp_pool_create("packet_pool", ¶ms); + if (packet_pool == ODP_POOL_INVALID) + return -1; + + params.pkt.uarea_size = 0; + packet_pool_no_uarea = odp_pool_create("packet_pool_no_uarea", + ¶ms); + if (packet_pool_no_uarea == ODP_POOL_INVALID) { + odp_pool_destroy(packet_pool); + return -1; + } + + params.pkt.uarea_size = 2 * sizeof(struct udata_struct); + packet_pool_double_uarea = odp_pool_create("packet_pool_double_uarea", + ¶ms); + + if (packet_pool_double_uarea == ODP_POOL_INVALID) { + odp_pool_destroy(packet_pool_no_uarea); + odp_pool_destroy(packet_pool); + return -1; + } + + test_packet = odp_packet_alloc(packet_pool, packet_len); + + for (i = 0; i < packet_len; i++) { + odp_packet_copy_from_mem(test_packet, i, 1, &data); + data++; + } + + /* Try to allocate the largest possible packet to see + * if segmentation is supported */ + do { + segmented_test_packet = odp_packet_alloc(packet_pool, + segmented_packet_len); + if (segmented_test_packet == ODP_PACKET_INVALID) + segmented_packet_len -= capa.pkt.min_seg_len; + } while (segmented_test_packet == ODP_PACKET_INVALID); + + if (odp_packet_is_valid(test_packet) == 0 || + odp_packet_is_valid(segmented_test_packet) == 0) + return -1; + + segmentation_supported = odp_packet_is_segmented(segmented_test_packet); + + data = 0; + for (i = 0; i < segmented_packet_len; i++) { + odp_packet_copy_from_mem(segmented_test_packet, i, 1, &data); + data++; + } + + udat = odp_packet_user_area(test_packet); + udat_size = odp_packet_user_area_size(test_packet); + if (!udat || udat_size != sizeof(struct udata_struct)) + return -1; + odp_pool_print(packet_pool); + memcpy(udat, &test_packet_udata, sizeof(struct udata_struct)); + + udat = odp_packet_user_area(segmented_test_packet); + udat_size = odp_packet_user_area_size(segmented_test_packet); + if (udat == NULL || udat_size != sizeof(struct udata_struct)) + return -1; + memcpy(udat, &test_packet_udata, sizeof(struct udata_struct)); + + return 0; +} + +int packet_suite_term(void) +{ + odp_packet_free(test_packet); + odp_packet_free(segmented_test_packet); + + if (odp_pool_destroy(packet_pool_double_uarea) != 0 || + odp_pool_destroy(packet_pool_no_uarea) != 0 || + odp_pool_destroy(packet_pool) != 0) + return -1; + + return 0; +} + +void packet_test_alloc_free(void) +{ + odp_pool_t pool; + odp_packet_t packet; + odp_pool_param_t params; + odp_pool_capability_t capa; + + CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0); + + odp_pool_param_init(¶ms); + + params.type = ODP_POOL_PACKET; + params.pkt.seg_len = capa.pkt.min_seg_len; + params.pkt.len = capa.pkt.min_seg_len; + params.pkt.num = 1; + + pool = odp_pool_create("packet_pool_alloc", ¶ms); + + /* Allocate the only buffer from the pool */ + packet = odp_packet_alloc(pool, packet_len); + CU_ASSERT_FATAL(packet != ODP_PACKET_INVALID); + CU_ASSERT(odp_packet_len(packet) == packet_len); + CU_ASSERT(odp_event_type(odp_packet_to_event(packet)) == + ODP_EVENT_PACKET); + CU_ASSERT(odp_packet_to_u64(packet) != + odp_packet_to_u64(ODP_PACKET_INVALID)); + + /* Pool should have only one packet */ + CU_ASSERT_FATAL(odp_packet_alloc(pool, packet_len) + == ODP_PACKET_INVALID); + + odp_packet_free(packet); + + /* Check that the buffer was returned back to the pool */ + packet = odp_packet_alloc(pool, packet_len); + CU_ASSERT_FATAL(packet != ODP_PACKET_INVALID); + CU_ASSERT(odp_packet_len(packet) == packet_len); + + odp_packet_free(packet); + CU_ASSERT(odp_pool_destroy(pool) == 0); +} + +/* Wrapper to call odp_packet_alloc_multi multiple times until + * either no mure buffers are returned, or num buffers were alloced */ +static int packet_alloc_multi(odp_pool_t pool, uint32_t pkt_len, + odp_packet_t pkt[], int num) +{ + int ret, total = 0; + + do { + ret = odp_packet_alloc_multi(pool, pkt_len, pkt + total, + num - total); + CU_ASSERT(ret >= 0); + CU_ASSERT(ret <= num - total); + total += ret; + } while (total < num && ret); + + return total; +} + +void packet_test_alloc_free_multi(void) +{ + const int num_pkt = 2; + odp_pool_t pool[2]; + int i, ret; + odp_packet_t packet[2 * num_pkt + 1]; + odp_packet_t inval_pkt[num_pkt]; + odp_pool_param_t params; + odp_pool_capability_t capa; + + CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0); + + odp_pool_param_init(¶ms); + + params.type = ODP_POOL_PACKET; + params.pkt.seg_len = capa.pkt.min_seg_len; + params.pkt.len = capa.pkt.min_seg_len; + params.pkt.num = num_pkt; + + pool[0] = odp_pool_create("packet_pool_alloc_multi_0", ¶ms); + pool[1] = odp_pool_create("packet_pool_alloc_multi_1", ¶ms); + CU_ASSERT_FATAL(pool[0] != ODP_POOL_INVALID); + CU_ASSERT_FATAL(pool[1] != ODP_POOL_INVALID); + + /* Allocate all the packets from the pools */ + + ret = packet_alloc_multi(pool[0], packet_len, &packet[0], num_pkt + 1); + CU_ASSERT_FATAL(ret == num_pkt); + ret = packet_alloc_multi(pool[1], packet_len, + &packet[num_pkt], num_pkt + 1); + CU_ASSERT_FATAL(ret == num_pkt); + + for (i = 0; i < 2 * num_pkt; ++i) { + CU_ASSERT(odp_packet_len(packet[i]) == packet_len); + CU_ASSERT(odp_event_type(odp_packet_to_event(packet[i])) == + ODP_EVENT_PACKET); + CU_ASSERT(odp_packet_to_u64(packet[i]) != + odp_packet_to_u64(ODP_PACKET_INVALID)); + } + + /* Pools should have no more packets */ + ret = odp_packet_alloc_multi(pool[0], packet_len, inval_pkt, num_pkt); + CU_ASSERT(ret == 0); + ret = odp_packet_alloc_multi(pool[1], packet_len, inval_pkt, num_pkt); + CU_ASSERT(ret == 0); + + /* Free all packets from all pools at once */ + odp_packet_free_multi(packet, 2 * num_pkt); + + /* Check that all the packets were returned back to their pools */ + ret = packet_alloc_multi(pool[0], packet_len, &packet[0], num_pkt); + CU_ASSERT(ret); + ret = packet_alloc_multi(pool[1], packet_len, + &packet[num_pkt], num_pkt); + CU_ASSERT(ret); + + for (i = 0; i < 2 * num_pkt; ++i) { + CU_ASSERT_FATAL(packet[i] != ODP_PACKET_INVALID); + CU_ASSERT(odp_packet_len(packet[i]) == packet_len); + } + odp_packet_free_multi(packet, 2 * num_pkt); + CU_ASSERT(odp_pool_destroy(pool[0]) == 0); + CU_ASSERT(odp_pool_destroy(pool[1]) == 0); +} + +void packet_test_alloc_segmented(void) +{ + odp_packet_t pkt; + uint32_t len; + odp_pool_capability_t capa; + + CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0); + + if (capa.pkt.max_len) + len = capa.pkt.max_len; + else + len = capa.pkt.min_seg_len * capa.pkt.max_segs_per_pkt; + + pkt = odp_packet_alloc(packet_pool, len); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + CU_ASSERT(odp_packet_len(pkt) == len); + if (segmentation_supported) + CU_ASSERT(odp_packet_is_segmented(pkt) == 1); + odp_packet_free(pkt); +} + +void packet_test_event_conversion(void) +{ + odp_packet_t pkt = test_packet; + odp_packet_t tmp_pkt; + odp_event_t ev; + + ev = odp_packet_to_event(pkt); + CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID); + CU_ASSERT(odp_event_type(ev) == ODP_EVENT_PACKET); + + tmp_pkt = odp_packet_from_event(ev); + CU_ASSERT_FATAL(tmp_pkt != ODP_PACKET_INVALID); + /** @todo: Need an API to compare packets */ +} + +void packet_test_basic_metadata(void) +{ + odp_packet_t pkt = test_packet; + odp_time_t ts; + + CU_ASSERT_PTR_NOT_NULL(odp_packet_head(pkt)); + CU_ASSERT_PTR_NOT_NULL(odp_packet_data(pkt)); + + CU_ASSERT(odp_packet_pool(pkt) != ODP_POOL_INVALID); + /* Packet was allocated by application so shouldn't have valid pktio. */ + CU_ASSERT(odp_packet_input(pkt) == ODP_PKTIO_INVALID); + CU_ASSERT(odp_packet_input_index(pkt) < 0); + + odp_packet_flow_hash_set(pkt, UINT32_MAX); + CU_ASSERT(odp_packet_has_flow_hash(pkt)); + CU_ASSERT(odp_packet_flow_hash(pkt) == UINT32_MAX); + odp_packet_has_flow_hash_clr(pkt); + CU_ASSERT(!odp_packet_has_flow_hash(pkt)); + + ts = odp_time_global(); + odp_packet_ts_set(pkt, ts); + CU_ASSERT_FATAL(odp_packet_has_ts(pkt)); + CU_ASSERT(!odp_time_cmp(ts, odp_packet_ts(pkt))); + odp_packet_has_ts_clr(pkt); + CU_ASSERT(!odp_packet_has_ts(pkt)); +} + +void packet_test_length(void) +{ + odp_packet_t pkt = test_packet; + uint32_t buf_len, headroom, tailroom; + odp_pool_capability_t capa; + + CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0); + + buf_len = odp_packet_buf_len(pkt); + headroom = odp_packet_headroom(pkt); + tailroom = odp_packet_tailroom(pkt); + + CU_ASSERT(odp_packet_len(pkt) == packet_len); + CU_ASSERT(headroom >= capa.pkt.min_headroom); + CU_ASSERT(tailroom >= capa.pkt.min_tailroom); + + CU_ASSERT(buf_len >= packet_len + headroom + tailroom); +} + +void packet_test_prefetch(void) +{ + odp_packet_prefetch(test_packet, 0, odp_packet_len(test_packet)); + CU_PASS(); +} + +void packet_test_debug(void) +{ + CU_ASSERT(odp_packet_is_valid(test_packet) == 1); + odp_packet_print(test_packet); +} + +void packet_test_context(void) +{ + odp_packet_t pkt = test_packet; + char ptr_test_value = 2; + void *prev_ptr; + struct udata_struct *udat; + + prev_ptr = odp_packet_user_ptr(pkt); + odp_packet_user_ptr_set(pkt, &ptr_test_value); + CU_ASSERT(odp_packet_user_ptr(pkt) == &ptr_test_value); + odp_packet_user_ptr_set(pkt, prev_ptr); + + udat = odp_packet_user_area(pkt); + CU_ASSERT_PTR_NOT_NULL(udat); + CU_ASSERT(odp_packet_user_area_size(pkt) == + sizeof(struct udata_struct)); + CU_ASSERT(memcmp(udat, &test_packet_udata, sizeof(struct udata_struct)) + == 0); + + odp_packet_reset(pkt, packet_len); +} + +void packet_test_layer_offsets(void) +{ + odp_packet_t pkt = test_packet; + uint8_t *l2_addr, *l3_addr, *l4_addr; + uint32_t seg_len; + const uint32_t l2_off = 2; + const uint32_t l3_off = l2_off + 14; + const uint32_t l4_off = l3_off + 14; + int ret; + + /* Set offsets to the same value */ + ret = odp_packet_l2_offset_set(pkt, l2_off); + CU_ASSERT(ret == 0); + ret = odp_packet_l3_offset_set(pkt, l2_off); + CU_ASSERT(ret == 0); + ret = odp_packet_l4_offset_set(pkt, l2_off); + CU_ASSERT(ret == 0); + + /* Addresses should be the same */ + l2_addr = odp_packet_l2_ptr(pkt, &seg_len); + CU_ASSERT(seg_len != 0); + l3_addr = odp_packet_l3_ptr(pkt, &seg_len); + CU_ASSERT(seg_len != 0); + l4_addr = odp_packet_l4_ptr(pkt, &seg_len); + CU_ASSERT(seg_len != 0); + CU_ASSERT_PTR_NOT_NULL(l2_addr); + CU_ASSERT(l2_addr == l3_addr); + CU_ASSERT(l2_addr == l4_addr); + + /* Set offsets to the different values */ + odp_packet_l2_offset_set(pkt, l2_off); + CU_ASSERT(odp_packet_l2_offset(pkt) == l2_off); + odp_packet_l3_offset_set(pkt, l3_off); + CU_ASSERT(odp_packet_l3_offset(pkt) == l3_off); + odp_packet_l4_offset_set(pkt, l4_off); + CU_ASSERT(odp_packet_l4_offset(pkt) == l4_off); + + /* Addresses should not be the same */ + l2_addr = odp_packet_l2_ptr(pkt, NULL); + CU_ASSERT_PTR_NOT_NULL(l2_addr); + l3_addr = odp_packet_l3_ptr(pkt, NULL); + CU_ASSERT_PTR_NOT_NULL(l3_addr); + l4_addr = odp_packet_l4_ptr(pkt, NULL); + CU_ASSERT_PTR_NOT_NULL(l4_addr); + + CU_ASSERT(l2_addr != l3_addr); + CU_ASSERT(l2_addr != l4_addr); + CU_ASSERT(l3_addr != l4_addr); +} + +static void _verify_headroom_shift(odp_packet_t *pkt, + int shift) +{ + uint32_t room = odp_packet_headroom(*pkt); + uint32_t seg_data_len = odp_packet_seg_len(*pkt); + uint32_t pkt_data_len = odp_packet_len(*pkt); + void *data; + char *data_orig = odp_packet_data(*pkt); + char *head_orig = odp_packet_head(*pkt); + uint32_t seg_len; + int extended, rc; + + if (shift >= 0) { + if ((uint32_t)abs(shift) <= room) { + data = odp_packet_push_head(*pkt, shift); + extended = 0; + } else { + rc = odp_packet_extend_head(pkt, shift, + &data, &seg_len); + extended = 1; + } + } else { + if ((uint32_t)abs(shift) <= seg_data_len) { + data = odp_packet_pull_head(*pkt, -shift); + extended = 0; + } else { + rc = odp_packet_trunc_head(pkt, -shift, + &data, &seg_len); + extended = 1; + } + } + + CU_ASSERT_PTR_NOT_NULL(data); + if (extended) { + CU_ASSERT(rc >= 0); + if (shift >= 0) { + CU_ASSERT(odp_packet_seg_len(*pkt) == shift - room); + } else { + CU_ASSERT(odp_packet_headroom(*pkt) >= + (uint32_t)abs(shift) - seg_data_len); + } + CU_ASSERT(odp_packet_head(*pkt) != head_orig); + } else { + CU_ASSERT(odp_packet_headroom(*pkt) == room - shift); + CU_ASSERT(odp_packet_seg_len(*pkt) == seg_data_len + shift); + CU_ASSERT(data == data_orig - shift); + CU_ASSERT(odp_packet_head(*pkt) == head_orig); + } + + CU_ASSERT(odp_packet_len(*pkt) == pkt_data_len + shift); + CU_ASSERT(odp_packet_data(*pkt) == data); +} + +void packet_test_headroom(void) +{ + odp_packet_t pkt = odp_packet_copy(test_packet, + odp_packet_pool(test_packet)); + uint32_t room; + uint32_t seg_data_len; + uint32_t push_val, pull_val; + odp_pool_capability_t capa; + + CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0); + + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + room = odp_packet_headroom(pkt); + + CU_ASSERT(room >= capa.pkt.min_headroom); + + seg_data_len = odp_packet_seg_len(pkt); + CU_ASSERT(seg_data_len >= 1); + /** @todo: should be len - 1 */ + pull_val = seg_data_len / 2; + push_val = room; + + _verify_headroom_shift(&pkt, -pull_val); + _verify_headroom_shift(&pkt, push_val + pull_val); + _verify_headroom_shift(&pkt, -push_val); + _verify_headroom_shift(&pkt, 0); + + if (segmentation_supported) { + push_val = room * 2; + _verify_headroom_shift(&pkt, push_val); + _verify_headroom_shift(&pkt, 0); + _verify_headroom_shift(&pkt, -push_val); + } + + odp_packet_free(pkt); +} + +static void _verify_tailroom_shift(odp_packet_t *pkt, + int shift) +{ + odp_packet_seg_t seg; + uint32_t room; + uint32_t seg_data_len, pkt_data_len, seg_len; + void *tail; + char *tail_orig; + int extended, rc; + + room = odp_packet_tailroom(*pkt); + pkt_data_len = odp_packet_len(*pkt); + tail_orig = odp_packet_tail(*pkt); + + seg = odp_packet_last_seg(*pkt); + CU_ASSERT(seg != ODP_PACKET_SEG_INVALID); + seg_data_len = odp_packet_seg_data_len(*pkt, seg); + + if (shift >= 0) { + uint32_t l2_off, l3_off, l4_off; + + l2_off = odp_packet_l2_offset(*pkt); + l3_off = odp_packet_l3_offset(*pkt); + l4_off = odp_packet_l4_offset(*pkt); + + if ((uint32_t)abs(shift) <= room) { + tail = odp_packet_push_tail(*pkt, shift); + extended = 0; + } else { + rc = odp_packet_extend_tail(pkt, shift, + &tail, &seg_len); + extended = 1; + } + + CU_ASSERT(l2_off == odp_packet_l2_offset(*pkt)); + CU_ASSERT(l3_off == odp_packet_l3_offset(*pkt)); + CU_ASSERT(l4_off == odp_packet_l4_offset(*pkt)); + } else { + if ((uint32_t)abs(shift) <= seg_data_len) { + tail = odp_packet_pull_tail(*pkt, -shift); + extended = 0; + } else { + rc = odp_packet_trunc_tail(pkt, -shift, + &tail, &seg_len); + extended = 1; + } + } + + CU_ASSERT_PTR_NOT_NULL(tail); + if (extended) { + CU_ASSERT(rc >= 0); + CU_ASSERT(odp_packet_last_seg(*pkt) != seg); + seg = odp_packet_last_seg(*pkt); + if (shift > 0) { + CU_ASSERT(odp_packet_seg_data_len(*pkt, seg) == + shift - room); + } else { + CU_ASSERT(odp_packet_tailroom(*pkt) >= + (uint32_t)abs(shift) - seg_data_len); + CU_ASSERT(seg_len == odp_packet_tailroom(*pkt)); + } + } else { + CU_ASSERT(odp_packet_seg_data_len(*pkt, seg) == + seg_data_len + shift); + CU_ASSERT(odp_packet_tailroom(*pkt) == room - shift); + if (room == 0 || (room - shift) == 0) + return; + if (shift >= 0) { + CU_ASSERT(odp_packet_tail(*pkt) == + tail_orig + shift); + } else { + CU_ASSERT(tail == tail_orig + shift); + } + } + + CU_ASSERT(odp_packet_len(*pkt) == pkt_data_len + shift); + if (shift >= 0) { + CU_ASSERT(tail == tail_orig); + } else { + CU_ASSERT(odp_packet_tail(*pkt) == tail); + } +} + +void packet_test_tailroom(void) +{ + odp_packet_t pkt = odp_packet_copy(test_packet, + odp_packet_pool(test_packet)); + odp_packet_seg_t segment; + uint32_t room; + uint32_t seg_data_len; + uint32_t push_val, pull_val; + odp_pool_capability_t capa; + + CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0); + + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + + segment = odp_packet_last_seg(pkt); + CU_ASSERT(segment != ODP_PACKET_SEG_INVALID); + room = odp_packet_tailroom(pkt); + CU_ASSERT(room >= capa.pkt.min_tailroom); + + seg_data_len = odp_packet_seg_data_len(pkt, segment); + CU_ASSERT(seg_data_len >= 1); + /** @todo: should be len - 1 */ + pull_val = seg_data_len / 2; + /* Leave one byte in a tailroom for odp_packet_tail() to succeed */ + push_val = (room > 0) ? room - 1 : room; + + _verify_tailroom_shift(&pkt, -pull_val); + _verify_tailroom_shift(&pkt, push_val + pull_val); + _verify_tailroom_shift(&pkt, -push_val); + _verify_tailroom_shift(&pkt, 0); + + if (segmentation_supported) { + _verify_tailroom_shift(&pkt, pull_val); + _verify_tailroom_shift(&pkt, 0); + _verify_tailroom_shift(&pkt, -pull_val); + } + + odp_packet_free(pkt); +} + +void packet_test_segments(void) +{ + int num_segs, seg_index; + uint32_t data_len; + odp_packet_seg_t seg; + odp_packet_t pkt = test_packet; + odp_packet_t seg_pkt = segmented_test_packet; + + CU_ASSERT(odp_packet_is_valid(pkt) == 1); + + num_segs = odp_packet_num_segs(pkt); + CU_ASSERT(num_segs != 0); + + if (odp_packet_is_segmented(pkt)) { + CU_ASSERT(num_segs > 1); + } else { + CU_ASSERT(num_segs == 1); + } + + CU_ASSERT(odp_packet_is_segmented(pkt) == 0); + if (segmentation_supported) + CU_ASSERT(odp_packet_is_segmented(seg_pkt) == 1); + + seg = odp_packet_first_seg(pkt); + data_len = 0; + seg_index = 0; + while (seg_index < num_segs && seg != ODP_PACKET_SEG_INVALID) { + uint32_t seg_data_len; + void *seg_data; + + seg_data_len = odp_packet_seg_data_len(pkt, seg); + seg_data = odp_packet_seg_data(pkt, seg); + + CU_ASSERT(seg_data_len > 0); + CU_ASSERT_PTR_NOT_NULL(seg_data); + CU_ASSERT(odp_packet_seg_to_u64(seg) != + odp_packet_seg_to_u64(ODP_PACKET_SEG_INVALID)); + + data_len += seg_data_len; + + /** @todo: touch memory in a segment */ + seg_index++; + seg = odp_packet_next_seg(pkt, seg); + } + + CU_ASSERT(seg_index == num_segs); + CU_ASSERT(data_len <= odp_packet_buf_len(pkt)); + CU_ASSERT(data_len == odp_packet_len(pkt)); + + if (seg_index == num_segs) + CU_ASSERT(seg == ODP_PACKET_SEG_INVALID); + + seg = odp_packet_first_seg(seg_pkt); + num_segs = odp_packet_num_segs(seg_pkt); + + data_len = 0; + seg_index = 0; + + while (seg_index < num_segs && seg != ODP_PACKET_SEG_INVALID) { + uint32_t seg_data_len; + void *seg_data; + + seg_data_len = odp_packet_seg_data_len(seg_pkt, seg); + seg_data = odp_packet_seg_data(seg_pkt, seg); + + CU_ASSERT(seg_data_len > 0); + CU_ASSERT(seg_data != NULL); + CU_ASSERT(odp_packet_seg_to_u64(seg) != + odp_packet_seg_to_u64(ODP_PACKET_SEG_INVALID)); + + data_len += seg_data_len; + + /** @todo: touch memory in a segment */ + seg_index++; + seg = odp_packet_next_seg(seg_pkt, seg); + } + + CU_ASSERT(seg_index == num_segs); + CU_ASSERT(data_len <= odp_packet_buf_len(seg_pkt)); + CU_ASSERT(data_len == odp_packet_len(seg_pkt)); + + if (seg_index == num_segs) + CU_ASSERT(seg == ODP_PACKET_SEG_INVALID); +} + +void packet_test_segment_last(void) +{ + odp_packet_t pkt = test_packet; + odp_packet_seg_t seg; + + seg = odp_packet_last_seg(pkt); + CU_ASSERT_FATAL(seg != ODP_PACKET_SEG_INVALID); + + seg = odp_packet_next_seg(pkt, seg); + CU_ASSERT(seg == ODP_PACKET_SEG_INVALID); +} + +#define TEST_INFLAG(packet, flag) \ +do { \ + odp_packet_has_##flag##_set(packet, 0); \ + CU_ASSERT(odp_packet_has_##flag(packet) == 0); \ + odp_packet_has_##flag##_set(packet, 1); \ + CU_ASSERT(odp_packet_has_##flag(packet) != 0); \ +} while (0) + +void packet_test_in_flags(void) +{ + odp_packet_t pkt = test_packet; + + TEST_INFLAG(pkt, l2); + TEST_INFLAG(pkt, l3); + TEST_INFLAG(pkt, l4); + TEST_INFLAG(pkt, eth); + TEST_INFLAG(pkt, eth_bcast); + TEST_INFLAG(pkt, eth_mcast); + TEST_INFLAG(pkt, jumbo); + TEST_INFLAG(pkt, vlan); + TEST_INFLAG(pkt, vlan_qinq); + TEST_INFLAG(pkt, arp); + TEST_INFLAG(pkt, ipv4); + TEST_INFLAG(pkt, ipv6); + TEST_INFLAG(pkt, ip_bcast); + TEST_INFLAG(pkt, ip_mcast); + TEST_INFLAG(pkt, ipfrag); + TEST_INFLAG(pkt, ipopt); + TEST_INFLAG(pkt, ipsec); + TEST_INFLAG(pkt, udp); + TEST_INFLAG(pkt, tcp); + TEST_INFLAG(pkt, sctp); + TEST_INFLAG(pkt, icmp); +} + +void packet_test_error_flags(void) +{ + odp_packet_t pkt = test_packet; + int err; + + /** + * The packet have not been classified so it doesn't have error flags + * properly set. Just check that functions return one of allowed values. + * @todo: try with known good and bad packets. + */ + err = odp_packet_has_error(pkt); + CU_ASSERT(err == 0 || err == 1); + + err = odp_packet_has_l2_error(pkt); + CU_ASSERT(err == 0 || err == 1); + + err = odp_packet_has_l3_error(pkt); + CU_ASSERT(err == 0 || err == 1); + + err = odp_packet_has_l4_error(pkt); + CU_ASSERT(err == 0 || err == 1); +} + +struct packet_metadata { + uint32_t l2_off; + uint32_t l3_off; + uint32_t l4_off; + void *usr_ptr; + uint64_t usr_u64; +}; + +void packet_test_add_rem_data(void) +{ + odp_packet_t pkt, new_pkt; + uint32_t pkt_len, offset, add_len; + void *usr_ptr; + struct udata_struct *udat, *new_udat; + int ret; + odp_pool_capability_t capa; + uint32_t min_seg_len; + + CU_ASSERT_FATAL(odp_pool_capability(&capa) == 0); + + min_seg_len = capa.pkt.min_seg_len; + + pkt = odp_packet_alloc(packet_pool, packet_len); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + + pkt_len = odp_packet_len(pkt); + usr_ptr = odp_packet_user_ptr(pkt); + udat = odp_packet_user_area(pkt); + CU_ASSERT(odp_packet_user_area_size(pkt) == + sizeof(struct udata_struct)); + memcpy(udat, &test_packet_udata, sizeof(struct udata_struct)); + + offset = pkt_len / 2; + + if (segmentation_supported) { + /* Insert one more packet length in the middle of a packet */ + add_len = min_seg_len; + } else { + /* Add diff between largest and smaller packets + * which is at least tailroom */ + add_len = segmented_packet_len - packet_len; + } + + new_pkt = pkt; + ret = odp_packet_add_data(&new_pkt, offset, add_len); + CU_ASSERT(ret >= 0); + if (ret < 0) + goto free_packet; + CU_ASSERT(odp_packet_len(new_pkt) == pkt_len + add_len); + /* Verify that user metadata is preserved */ + CU_ASSERT(odp_packet_user_ptr(new_pkt) == usr_ptr); + + /* Verify that user metadata has been preserved */ + new_udat = odp_packet_user_area(new_pkt); + CU_ASSERT_PTR_NOT_NULL(new_udat); + CU_ASSERT(odp_packet_user_area_size(new_pkt) == + sizeof(struct udata_struct)); + CU_ASSERT(memcmp(new_udat, &test_packet_udata, + sizeof(struct udata_struct)) == 0); + + pkt = new_pkt; + + pkt_len = odp_packet_len(pkt); + usr_ptr = odp_packet_user_ptr(pkt); + + ret = odp_packet_rem_data(&new_pkt, offset, add_len); + CU_ASSERT(ret >= 0); + if (ret < 0) + goto free_packet; + CU_ASSERT(odp_packet_len(new_pkt) == pkt_len - add_len); + CU_ASSERT(odp_packet_user_ptr(new_pkt) == usr_ptr); + + /* Verify that user metadata has been preserved */ + new_udat = odp_packet_user_area(new_pkt); + CU_ASSERT_PTR_NOT_NULL(new_udat); + CU_ASSERT(odp_packet_user_area_size(new_pkt) == + sizeof(struct udata_struct)); + CU_ASSERT(memcmp(new_udat, &test_packet_udata, + sizeof(struct udata_struct)) == 0); + + pkt = new_pkt; + +free_packet: + odp_packet_free(pkt); +} + +#define COMPARE_HAS_INFLAG(p1, p2, flag) \ + CU_ASSERT(odp_packet_has_##flag(p1) == odp_packet_has_##flag(p2)) + +#define COMPARE_INFLAG(p1, p2, flag) \ + CU_ASSERT(odp_packet_##flag(p1) == odp_packet_##flag(p2)) + +static void _packet_compare_inflags(odp_packet_t pkt1, odp_packet_t pkt2) +{ + COMPARE_HAS_INFLAG(pkt1, pkt2, l2); + COMPARE_HAS_INFLAG(pkt1, pkt2, l3); + COMPARE_HAS_INFLAG(pkt1, pkt2, l4); + COMPARE_HAS_INFLAG(pkt1, pkt2, eth); + COMPARE_HAS_INFLAG(pkt1, pkt2, eth_bcast); + COMPARE_HAS_INFLAG(pkt1, pkt2, eth_mcast); + COMPARE_HAS_INFLAG(pkt1, pkt2, jumbo); + COMPARE_HAS_INFLAG(pkt1, pkt2, vlan); + COMPARE_HAS_INFLAG(pkt1, pkt2, vlan_qinq); + COMPARE_HAS_INFLAG(pkt1, pkt2, arp); + COMPARE_HAS_INFLAG(pkt1, pkt2, ipv4); + COMPARE_HAS_INFLAG(pkt1, pkt2, ipv6); + COMPARE_HAS_INFLAG(pkt1, pkt2, ip_bcast); + COMPARE_HAS_INFLAG(pkt1, pkt2, ip_mcast); + COMPARE_HAS_INFLAG(pkt1, pkt2, ipfrag); + COMPARE_HAS_INFLAG(pkt1, pkt2, ipopt); + COMPARE_HAS_INFLAG(pkt1, pkt2, ipsec); + COMPARE_HAS_INFLAG(pkt1, pkt2, udp); + COMPARE_HAS_INFLAG(pkt1, pkt2, tcp); + COMPARE_HAS_INFLAG(pkt1, pkt2, sctp); + COMPARE_HAS_INFLAG(pkt1, pkt2, icmp); + COMPARE_HAS_INFLAG(pkt1, pkt2, flow_hash); + COMPARE_HAS_INFLAG(pkt1, pkt2, ts); + + COMPARE_INFLAG(pkt1, pkt2, color); + COMPARE_INFLAG(pkt1, pkt2, drop_eligible); + COMPARE_INFLAG(pkt1, pkt2, shaper_len_adjust); +} + +static void _packet_compare_data(odp_packet_t pkt1, odp_packet_t pkt2) +{ + uint32_t len = odp_packet_len(pkt1); + uint32_t offset = 0; + uint32_t seglen1, seglen2, cmplen; + + CU_ASSERT_FATAL(len == odp_packet_len(pkt2)); + + while (len > 0) { + void *pkt1map = odp_packet_offset(pkt1, offset, &seglen1, NULL); + void *pkt2map = odp_packet_offset(pkt2, offset, &seglen2, NULL); + + CU_ASSERT_PTR_NOT_NULL_FATAL(pkt1map); + CU_ASSERT_PTR_NOT_NULL_FATAL(pkt2map); + cmplen = seglen1 < seglen2 ? seglen1 : seglen2; + CU_ASSERT(!memcmp(pkt1map, pkt2map, cmplen)); + + offset += cmplen; + len -= cmplen; + } +} + +static void _packet_compare_udata(odp_packet_t pkt1, odp_packet_t pkt2) +{ + uint32_t usize1 = odp_packet_user_area_size(pkt1); + uint32_t usize2 = odp_packet_user_area_size(pkt2); + + void *uaddr1 = odp_packet_user_area(pkt1); + void *uaddr2 = odp_packet_user_area(pkt2); + + uint32_t cmplen = usize1 <= usize2 ? usize1 : usize2; + + if (cmplen) + CU_ASSERT(!memcmp(uaddr1, uaddr2, cmplen)); +} + +static void _packet_compare_offset(odp_packet_t pkt1, uint32_t off1, + odp_packet_t pkt2, uint32_t off2, + uint32_t len) +{ + uint32_t seglen1, seglen2, cmplen; + + if (off1 + len > odp_packet_len(pkt1) || + off2 + len > odp_packet_len(pkt2)) + return; + + while (len > 0) { + void *pkt1map = odp_packet_offset(pkt1, off1, &seglen1, NULL); + void *pkt2map = odp_packet_offset(pkt2, off2, &seglen2, NULL); + + CU_ASSERT_PTR_NOT_NULL_FATAL(pkt1map); + CU_ASSERT_PTR_NOT_NULL_FATAL(pkt2map); + cmplen = seglen1 < seglen2 ? seglen1 : seglen2; + if (len < cmplen) + cmplen = len; + CU_ASSERT(!memcmp(pkt1map, pkt2map, cmplen)); + + off1 += cmplen; + off2 += cmplen; + len -= cmplen; + } +} + +void packet_test_copy(void) +{ + odp_packet_t pkt; + odp_packet_t pkt_copy, pkt_part; + odp_pool_t pool; + uint32_t i, plen, seg_len, src_offset, dst_offset; + void *pkt_data; + + pkt = odp_packet_copy(test_packet, packet_pool_no_uarea); + CU_ASSERT(pkt == ODP_PACKET_INVALID); + if (pkt != ODP_PACKET_INVALID) + odp_packet_free(pkt); + + pkt = odp_packet_copy(test_packet, odp_packet_pool(test_packet)); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + _packet_compare_data(pkt, test_packet); + pool = odp_packet_pool(pkt); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + pkt_copy = odp_packet_copy(pkt, pool); + CU_ASSERT_FATAL(pkt_copy != ODP_PACKET_INVALID); + + CU_ASSERT(pkt != pkt_copy); + CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(pkt_copy)); + CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(pkt_copy)); + + _packet_compare_inflags(pkt, pkt_copy); + _packet_compare_data(pkt, pkt_copy); + CU_ASSERT(odp_packet_user_area_size(pkt) == + odp_packet_user_area_size(test_packet)); + _packet_compare_udata(pkt, pkt_copy); + odp_packet_free(pkt_copy); + odp_packet_free(pkt); + + pkt = odp_packet_copy(test_packet, packet_pool_double_uarea); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + _packet_compare_data(pkt, test_packet); + pool = odp_packet_pool(pkt); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + pkt_copy = odp_packet_copy(pkt, pool); + CU_ASSERT_FATAL(pkt_copy != ODP_PACKET_INVALID); + + CU_ASSERT(pkt != pkt_copy); + CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(pkt_copy)); + CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(pkt_copy)); + + _packet_compare_inflags(pkt, pkt_copy); + _packet_compare_data(pkt, pkt_copy); + CU_ASSERT(odp_packet_user_area_size(pkt) == + 2 * odp_packet_user_area_size(test_packet)); + _packet_compare_udata(pkt, pkt_copy); + _packet_compare_udata(pkt, test_packet); + odp_packet_free(pkt_copy); + + /* Now test copy_part */ + pkt_part = odp_packet_copy_part(pkt, 0, odp_packet_len(pkt) + 1, pool); + CU_ASSERT(pkt_part == ODP_PACKET_INVALID); + pkt_part = odp_packet_copy_part(pkt, odp_packet_len(pkt), 1, pool); + CU_ASSERT(pkt_part == ODP_PACKET_INVALID); + + pkt_part = odp_packet_copy_part(pkt, 0, odp_packet_len(pkt), pool); + CU_ASSERT_FATAL(pkt_part != ODP_PACKET_INVALID); + CU_ASSERT(pkt != pkt_part); + CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(pkt_part)); + CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(pkt_part)); + + _packet_compare_data(pkt, pkt_part); + odp_packet_free(pkt_part); + + plen = odp_packet_len(pkt); + for (i = 0; i < plen / 2; i += 5) { + pkt_part = odp_packet_copy_part(pkt, i, plen / 4, pool); + CU_ASSERT_FATAL(pkt_part != ODP_PACKET_INVALID); + CU_ASSERT(odp_packet_len(pkt_part) == plen / 4); + _packet_compare_offset(pkt_part, 0, pkt, i, plen / 4); + odp_packet_free(pkt_part); + } + + /* Test copy and move apis */ + CU_ASSERT(odp_packet_copy_data(pkt, 0, plen - plen / 8, plen / 8) == 0); + _packet_compare_offset(pkt, 0, pkt, plen - plen / 8, plen / 8); + _packet_compare_offset(pkt, 0, test_packet, plen - plen / 8, plen / 8); + + /* Test segment crossing if we support segments */ + pkt_data = odp_packet_offset(pkt, 0, &seg_len, NULL); + CU_ASSERT(pkt_data != NULL); + + if (seg_len < plen) { + src_offset = seg_len - 15; + dst_offset = seg_len - 5; + } else { + src_offset = seg_len - 40; + dst_offset = seg_len - 25; + } + + pkt_part = odp_packet_copy_part(pkt, src_offset, 20, pool); + CU_ASSERT(odp_packet_move_data(pkt, dst_offset, src_offset, 20) == 0); + _packet_compare_offset(pkt, dst_offset, pkt_part, 0, 20); + + odp_packet_free(pkt_part); + odp_packet_free(pkt); +} + +void packet_test_copydata(void) +{ + odp_packet_t pkt = test_packet; + uint32_t pkt_len = odp_packet_len(pkt); + uint8_t *data_buf; + uint32_t i; + int correct_memory; + + CU_ASSERT_FATAL(pkt_len > 0); + + data_buf = malloc(pkt_len); + CU_ASSERT_PTR_NOT_NULL_FATAL(data_buf); + + for (i = 0; i < pkt_len; i++) + data_buf[i] = (uint8_t)i; + + CU_ASSERT(!odp_packet_copy_from_mem(pkt, 0, pkt_len, data_buf)); + memset(data_buf, 0, pkt_len); + CU_ASSERT(!odp_packet_copy_to_mem(pkt, 0, pkt_len, data_buf)); + + correct_memory = 1; + for (i = 0; i < pkt_len; i++) + if (data_buf[i] != (uint8_t)i) { + correct_memory = 0; + break; + } + CU_ASSERT(correct_memory); + + free(data_buf); + + pkt = odp_packet_alloc(odp_packet_pool(test_packet), pkt_len / 2); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + + CU_ASSERT(odp_packet_copy_from_pkt(pkt, 0, test_packet, 0, + pkt_len) < 0); + CU_ASSERT(odp_packet_copy_from_pkt(pkt, pkt_len, test_packet, 0, + 1) < 0); + + for (i = 0; i < pkt_len / 2; i++) { + CU_ASSERT(odp_packet_copy_from_pkt(pkt, i, test_packet, i, + 1) == 0); + } + + _packet_compare_offset(pkt, 0, test_packet, 0, pkt_len / 2); + odp_packet_free(pkt); + + pkt = odp_packet_alloc(odp_packet_pool(segmented_test_packet), + odp_packet_len(segmented_test_packet) / 2); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + + CU_ASSERT(odp_packet_copy_from_pkt(pkt, 0, segmented_test_packet, + odp_packet_len(pkt) / 4, + odp_packet_len(pkt)) == 0); + _packet_compare_offset(pkt, 0, segmented_test_packet, + odp_packet_len(pkt) / 4, + odp_packet_len(pkt)); + odp_packet_free(pkt); +} + +void packet_test_concatsplit(void) +{ + odp_packet_t pkt, pkt2; + uint32_t pkt_len; + odp_packet_t splits[4]; + + pkt = odp_packet_copy(test_packet, odp_packet_pool(test_packet)); + pkt_len = odp_packet_len(test_packet); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + + CU_ASSERT(odp_packet_concat(&pkt, pkt) == 0); + CU_ASSERT(odp_packet_len(pkt) == pkt_len * 2); + _packet_compare_offset(pkt, 0, pkt, pkt_len, pkt_len); + + CU_ASSERT(odp_packet_split(&pkt, pkt_len, &pkt2) == 0); + CU_ASSERT(pkt != pkt2); + CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(pkt2)); + CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(pkt2)); + _packet_compare_data(pkt, pkt2); + _packet_compare_data(pkt, test_packet); + + odp_packet_free(pkt); + odp_packet_free(pkt2); + + pkt = odp_packet_copy(segmented_test_packet, + odp_packet_pool(segmented_test_packet)); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + pkt_len = odp_packet_len(pkt); + + _packet_compare_data(pkt, segmented_test_packet); + CU_ASSERT(odp_packet_split(&pkt, pkt_len / 2, &splits[0]) == 0); + CU_ASSERT(pkt != splits[0]); + CU_ASSERT(odp_packet_data(pkt) != odp_packet_data(splits[0])); + CU_ASSERT(odp_packet_len(pkt) == pkt_len / 2); + CU_ASSERT(odp_packet_len(pkt) + odp_packet_len(splits[0]) == pkt_len); + + _packet_compare_offset(pkt, 0, segmented_test_packet, 0, pkt_len / 2); + _packet_compare_offset(splits[0], 0, segmented_test_packet, + pkt_len / 2, odp_packet_len(splits[0])); + + CU_ASSERT(odp_packet_concat(&pkt, splits[0]) == 0); + _packet_compare_offset(pkt, 0, segmented_test_packet, 0, pkt_len / 2); + _packet_compare_offset(pkt, pkt_len / 2, segmented_test_packet, + pkt_len / 2, pkt_len / 2); + _packet_compare_offset(pkt, 0, segmented_test_packet, 0, + pkt_len); + + CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(segmented_test_packet)); + _packet_compare_data(pkt, segmented_test_packet); + + CU_ASSERT(odp_packet_split(&pkt, pkt_len / 2, &splits[0]) == 0); + CU_ASSERT(odp_packet_split(&pkt, pkt_len / 4, &splits[1]) == 0); + CU_ASSERT(odp_packet_split(&pkt, pkt_len / 8, &splits[2]) == 0); + + CU_ASSERT(odp_packet_len(splits[0]) + odp_packet_len(splits[1]) + + odp_packet_len(splits[2]) + odp_packet_len(pkt) == pkt_len); + + CU_ASSERT(odp_packet_concat(&pkt, splits[2]) == 0); + CU_ASSERT(odp_packet_concat(&pkt, splits[1]) == 0); + CU_ASSERT(odp_packet_concat(&pkt, splits[0]) == 0); + + CU_ASSERT(odp_packet_len(pkt) == odp_packet_len(segmented_test_packet)); + _packet_compare_data(pkt, segmented_test_packet); + + odp_packet_free(pkt); +} + +void packet_test_align(void) +{ + odp_packet_t pkt; + uint32_t pkt_len, seg_len, offset, aligned_seglen; + void *pkt_data, *aligned_data; + const uint32_t max_align = 32; + + pkt = odp_packet_copy_part(segmented_test_packet, 0, + odp_packet_len(segmented_test_packet) / 2, + odp_packet_pool(segmented_test_packet)); + CU_ASSERT_FATAL(pkt != ODP_PACKET_INVALID); + + pkt_len = odp_packet_len(pkt); + seg_len = odp_packet_seg_len(pkt); + + if (odp_packet_is_segmented(pkt)) { + /* Can't address across segment boundaries */ + CU_ASSERT(odp_packet_align(&pkt, 0, pkt_len, 0) < 0); + + offset = seg_len - 5; + (void)odp_packet_offset(pkt, offset, &seg_len, NULL); + + /* Realign for addressability */ + CU_ASSERT(odp_packet_align(&pkt, offset, + seg_len + 2, 0) >= 0); + + /* Alignment doesn't change packet length or contents */ + CU_ASSERT(odp_packet_len(pkt) == pkt_len); + (void)odp_packet_offset(pkt, offset, &aligned_seglen, NULL); + _packet_compare_offset(pkt, offset, + segmented_test_packet, offset, + aligned_seglen); + + /* Verify requested contiguous addressabilty */ + CU_ASSERT(aligned_seglen >= seg_len + 2); + } + + /* Get a misaligned address */ + pkt_data = odp_packet_offset(pkt, 0, &seg_len, NULL); + offset = seg_len - 5; + pkt_data = odp_packet_offset(pkt, offset, &seg_len, NULL); + if ((uintptr_t)pkt_data % max_align == 0) { + offset--; + pkt_data = odp_packet_offset(pkt, offset, &seg_len, NULL); + } + + /* Realign for alignment */ + CU_ASSERT(odp_packet_align(&pkt, offset, 1, max_align) >= 0); + aligned_data = odp_packet_offset(pkt, offset, &aligned_seglen, NULL); + + CU_ASSERT(odp_packet_len(pkt) == pkt_len); + _packet_compare_offset(pkt, offset, segmented_test_packet, offset, + aligned_seglen); + CU_ASSERT((uintptr_t)aligned_data % max_align == 0); + + odp_packet_free(pkt); +} + +void packet_test_offset(void) +{ + odp_packet_t pkt = test_packet; + uint32_t seg_len, full_seg_len; + odp_packet_seg_t seg; + uint8_t *ptr, *start_ptr; + uint32_t offset; + + ptr = odp_packet_offset(pkt, 0, &seg_len, &seg); + CU_ASSERT(seg != ODP_PACKET_SEG_INVALID); + CU_ASSERT(seg_len > 1); + CU_ASSERT(seg_len == odp_packet_seg_len(pkt)); + CU_ASSERT(seg_len == odp_packet_seg_data_len(pkt, seg)); + CU_ASSERT_PTR_NOT_NULL(ptr); + CU_ASSERT(ptr == odp_packet_data(pkt)); + CU_ASSERT(ptr == odp_packet_seg_data(pkt, seg)); + + /* Query a second byte */ + start_ptr = ptr; + full_seg_len = seg_len; + offset = 1; + + ptr = odp_packet_offset(pkt, offset, &seg_len, NULL); + CU_ASSERT_PTR_NOT_NULL(ptr); + CU_ASSERT(ptr == start_ptr + offset); + CU_ASSERT(seg_len == full_seg_len - offset); + + /* Query the last byte in a segment */ + offset = full_seg_len - 1; + + ptr = odp_packet_offset(pkt, offset, &seg_len, NULL); + CU_ASSERT_PTR_NOT_NULL(ptr); + CU_ASSERT(ptr == start_ptr + offset); + CU_ASSERT(seg_len == full_seg_len - offset); + + /* Query the last byte in a packet */ + offset = odp_packet_len(pkt) - 1; + ptr = odp_packet_offset(pkt, offset, &seg_len, NULL); + CU_ASSERT_PTR_NOT_NULL(ptr); + CU_ASSERT(seg_len == 1); + + /* Pass NULL to [out] arguments */ + ptr = odp_packet_offset(pkt, 0, NULL, NULL); + CU_ASSERT_PTR_NOT_NULL(ptr); +} + +odp_testinfo_t packet_suite[] = { + ODP_TEST_INFO(packet_test_alloc_free), + ODP_TEST_INFO(packet_test_alloc_free_multi), + ODP_TEST_INFO(packet_test_alloc_segmented), + ODP_TEST_INFO(packet_test_basic_metadata), + ODP_TEST_INFO(packet_test_debug), + ODP_TEST_INFO(packet_test_segments), + ODP_TEST_INFO(packet_test_length), + ODP_TEST_INFO(packet_test_prefetch), + ODP_TEST_INFO(packet_test_headroom), + ODP_TEST_INFO(packet_test_tailroom), + ODP_TEST_INFO(packet_test_context), + ODP_TEST_INFO(packet_test_event_conversion), + ODP_TEST_INFO(packet_test_layer_offsets), + ODP_TEST_INFO(packet_test_segment_last), + ODP_TEST_INFO(packet_test_in_flags), + ODP_TEST_INFO(packet_test_error_flags), + ODP_TEST_INFO(packet_test_add_rem_data), + ODP_TEST_INFO(packet_test_copy), + ODP_TEST_INFO(packet_test_copydata), + ODP_TEST_INFO(packet_test_concatsplit), + ODP_TEST_INFO(packet_test_align), + ODP_TEST_INFO(packet_test_offset), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t packet_suites[] = { + { .pName = "packet tests", + .pTests = packet_suite, + .pInitFunc = packet_suite_init, + .pCleanupFunc = packet_suite_term, + }, + ODP_SUITE_INFO_NULL, +}; + +int packet_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(packet_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/packet/packet.h b/test/common_plat/validation/api/packet/packet.h new file mode 100644 index 000000000..10a377cf0 --- /dev/null +++ b/test/common_plat/validation/api/packet/packet.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_PACKET_H_ +#define _ODP_TEST_PACKET_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void packet_test_alloc_free(void); +void packet_test_alloc_free_multi(void); +void packet_test_alloc_segmented(void); +void packet_test_event_conversion(void); +void packet_test_basic_metadata(void); +void packet_test_length(void); +void packet_test_prefetch(void); +void packet_test_debug(void); +void packet_test_context(void); +void packet_test_layer_offsets(void); +void packet_test_headroom(void); +void packet_test_tailroom(void); +void packet_test_segments(void); +void packet_test_segment_last(void); +void packet_test_in_flags(void); +void packet_test_error_flags(void); +void packet_test_add_rem_data(void); +void packet_test_copy(void); +void packet_test_copydata(void); +void packet_test_concatsplit(void); +void packet_test_align(void); +void packet_test_offset(void); + +/* test arrays: */ +extern odp_testinfo_t packet_suite[]; + +/* test array init/term functions: */ +int packet_suite_init(void); +int packet_suite_term(void); + +/* test registry: */ +extern odp_suiteinfo_t packet_suites[]; + +/* main test program: */ +int packet_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/packet/packet_main.c b/test/common_plat/validation/api/packet/packet_main.c new file mode 100644 index 000000000..511bb104b --- /dev/null +++ b/test/common_plat/validation/api/packet/packet_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "packet.h" + +int main(int argc, char *argv[]) +{ + return packet_main(argc, argv); +} diff --git a/test/common_plat/validation/api/pktio/.gitignore b/test/common_plat/validation/api/pktio/.gitignore new file mode 100644 index 000000000..1a5dd46e4 --- /dev/null +++ b/test/common_plat/validation/api/pktio/.gitignore @@ -0,0 +1 @@ +pktio_main diff --git a/test/common_plat/validation/api/pktio/Makefile.am b/test/common_plat/validation/api/pktio/Makefile.am new file mode 100644 index 000000000..466d690dc --- /dev/null +++ b/test/common_plat/validation/api/pktio/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestpktio.la +libtestpktio_la_SOURCES = pktio.c + +test_PROGRAMS = pktio_main$(EXEEXT) +dist_pktio_main_SOURCES = pktio_main.c +pktio_main_LDADD = libtestpktio.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = pktio.h diff --git a/test/common_plat/validation/api/pktio/pktio.c b/test/common_plat/validation/api/pktio/pktio.c new file mode 100644 index 000000000..a6a18c352 --- /dev/null +++ b/test/common_plat/validation/api/pktio/pktio.c @@ -0,0 +1,2170 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <odp_api.h> +#include <odp_cunit_common.h> + +#include <odp/helper/eth.h> +#include <odp/helper/ip.h> +#include <odp/helper/udp.h> + +#include <stdlib.h> +#include "pktio.h" + +#define PKT_BUF_NUM 32 +#define PKT_BUF_SIZE (9 * 1024) +#define PKT_LEN_NORMAL 64 +#define PKT_LEN_MAX (PKT_BUF_SIZE - ODPH_ETHHDR_LEN - \ + ODPH_IPV4HDR_LEN - ODPH_UDPHDR_LEN) + +#define USE_MTU 0 +#define MAX_NUM_IFACES 2 +#define TEST_SEQ_INVALID ((uint32_t)~0) +#define TEST_SEQ_MAGIC 0x92749451 +#define TX_BATCH_LEN 4 +#define MAX_QUEUES 128 + +#define PKTIN_TS_INTERVAL (50 * ODP_TIME_MSEC_IN_NS) +#define PKTIN_TS_MIN_RES 1000 +#define PKTIN_TS_MAX_RES 10000000000 +#define PKTIN_TS_CMP_RES 1 + +#undef DEBUG_STATS + +/** interface names used for testing */ +static const char *iface_name[MAX_NUM_IFACES]; + +/** number of interfaces being used (1=loopback, 2=pair) */ +static int num_ifaces; + +/** while testing real-world interfaces additional time may be + needed for external network to enable link to pktio + interface that just become up.*/ +static bool wait_for_network; + +/** local container for pktio attributes */ +typedef struct { + const char *name; + odp_pktio_t id; + odp_pktout_queue_t pktout; + odp_queue_t queue_out; + odp_queue_t inq; + odp_pktin_mode_t in_mode; +} pktio_info_t; + +/** magic number and sequence at start of UDP payload */ +typedef struct ODP_PACKED { + odp_u32be_t magic; + odp_u32be_t seq; +} pkt_head_t; + +/** magic number at end of UDP payload */ +typedef struct ODP_PACKED { + odp_u32be_t magic; +} pkt_tail_t; + +/** Run mode */ +typedef enum { + PKT_POOL_UNSEGMENTED, + PKT_POOL_SEGMENTED, +} pkt_segmented_e; + +typedef enum { + TXRX_MODE_SINGLE, + TXRX_MODE_MULTI, + TXRX_MODE_MULTI_EVENT +} txrx_mode_e; + +typedef enum { + RECV_TMO, + RECV_MQ_TMO, + RECV_MQ_TMO_NO_IDX, +} recv_tmo_mode_e; + +/** size of transmitted packets */ +static uint32_t packet_len = PKT_LEN_NORMAL; + +/** default packet pool */ +odp_pool_t default_pkt_pool = ODP_POOL_INVALID; + +/** sequence number of IP packets */ +odp_atomic_u32_t ip_seq; + +/** Type of pool segmentation */ +pkt_segmented_e pool_segmentation = PKT_POOL_UNSEGMENTED; + +odp_pool_t pool[MAX_NUM_IFACES] = {ODP_POOL_INVALID, ODP_POOL_INVALID}; + +static inline void _pktio_wait_linkup(odp_pktio_t pktio) +{ + /* wait 1 second for link up */ + uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS); + int wait_num = 100; + int i; + int ret = -1; + + for (i = 0; i < wait_num; i++) { + ret = odp_pktio_link_status(pktio); + if (ret < 0 || ret == 1) + break; + /* link is down, call status again after delay */ + odp_time_wait_ns(wait_ns); + } + + if (ret != -1) { + /* assert only if link state supported and + * it's down. */ + CU_ASSERT_FATAL(ret == 1); + } +} + +static void set_pool_len(odp_pool_param_t *params) +{ + switch (pool_segmentation) { + case PKT_POOL_SEGMENTED: + /* Force segment to minimum size */ + params->pkt.seg_len = 0; + params->pkt.len = PKT_BUF_SIZE; + break; + case PKT_POOL_UNSEGMENTED: + default: + params->pkt.seg_len = PKT_BUF_SIZE; + params->pkt.len = PKT_BUF_SIZE; + break; + } +} + +static void pktio_pkt_set_macs(odp_packet_t pkt, + odp_pktio_t src, odp_pktio_t dst) +{ + uint32_t len; + odph_ethhdr_t *eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, &len); + int ret; + + ret = odp_pktio_mac_addr(src, ð->src, ODP_PKTIO_MACADDR_MAXSIZE); + CU_ASSERT(ret == ODPH_ETHADDR_LEN); + CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE); + + ret = odp_pktio_mac_addr(dst, ð->dst, ODP_PKTIO_MACADDR_MAXSIZE); + CU_ASSERT(ret == ODPH_ETHADDR_LEN); + CU_ASSERT(ret <= ODP_PKTIO_MACADDR_MAXSIZE); +} + +static uint32_t pktio_pkt_set_seq(odp_packet_t pkt) +{ + static uint32_t tstseq; + size_t off; + pkt_head_t head; + pkt_tail_t tail; + + off = odp_packet_l4_offset(pkt); + if (off == ODP_PACKET_OFFSET_INVALID) { + CU_FAIL("packet L4 offset not set"); + return TEST_SEQ_INVALID; + } + + head.magic = TEST_SEQ_MAGIC; + head.seq = tstseq; + + off += ODPH_UDPHDR_LEN; + if (odp_packet_copy_from_mem(pkt, off, sizeof(head), &head) != 0) + return TEST_SEQ_INVALID; + + tail.magic = TEST_SEQ_MAGIC; + off = odp_packet_len(pkt) - sizeof(pkt_tail_t); + if (odp_packet_copy_from_mem(pkt, off, sizeof(tail), &tail) != 0) + return TEST_SEQ_INVALID; + + tstseq++; + + return head.seq; +} + +static uint32_t pktio_pkt_seq(odp_packet_t pkt) +{ + size_t off; + uint32_t seq = TEST_SEQ_INVALID; + pkt_head_t head; + pkt_tail_t tail; + + if (pkt == ODP_PACKET_INVALID) { + fprintf(stderr, "error: pkt invalid\n"); + return TEST_SEQ_INVALID; + } + + off = odp_packet_l4_offset(pkt); + if (off == ODP_PACKET_OFFSET_INVALID) { + fprintf(stderr, "error: offset invalid\n"); + return TEST_SEQ_INVALID; + } + + off += ODPH_UDPHDR_LEN; + if (odp_packet_copy_to_mem(pkt, off, sizeof(head), &head) != 0) { + fprintf(stderr, "error: header copy failed\n"); + return TEST_SEQ_INVALID; + } + + if (head.magic != TEST_SEQ_MAGIC) { + fprintf(stderr, "error: header magic invalid %u\n", head.magic); + return TEST_SEQ_INVALID; + } + + if (odp_packet_len(pkt) == packet_len) { + off = packet_len - sizeof(tail); + if (odp_packet_copy_to_mem(pkt, off, sizeof(tail), + &tail) != 0) { + fprintf(stderr, "error: header copy failed\n"); + return TEST_SEQ_INVALID; + } + + if (tail.magic == TEST_SEQ_MAGIC) { + seq = head.seq; + CU_ASSERT(seq != TEST_SEQ_INVALID); + } else { + fprintf(stderr, "error: tail magic invalid %u\n", + tail.magic); + } + } else { + fprintf(stderr, "error: packet length invalid: %u (%u)\n", + odp_packet_len(pkt), packet_len); + } + + return seq; +} + +static uint32_t pktio_init_packet(odp_packet_t pkt) +{ + odph_ethhdr_t *eth; + odph_ipv4hdr_t *ip; + odph_udphdr_t *udp; + char *buf; + uint16_t seq; + uint8_t mac[ODP_PKTIO_MACADDR_MAXSIZE] = {0}; + int pkt_len = odp_packet_len(pkt); + + buf = odp_packet_data(pkt); + + /* Ethernet */ + odp_packet_l2_offset_set(pkt, 0); + eth = (odph_ethhdr_t *)buf; + memcpy(eth->src.addr, mac, ODPH_ETHADDR_LEN); + memcpy(eth->dst.addr, mac, ODPH_ETHADDR_LEN); + eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4); + + /* IP */ + odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN); + ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN); + ip->dst_addr = odp_cpu_to_be_32(0x0a000064); + ip->src_addr = odp_cpu_to_be_32(0x0a000001); + ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN; + ip->tot_len = odp_cpu_to_be_16(pkt_len - ODPH_ETHHDR_LEN); + ip->ttl = 128; + ip->proto = ODPH_IPPROTO_UDP; + seq = odp_atomic_fetch_inc_u32(&ip_seq); + ip->id = odp_cpu_to_be_16(seq); + ip->chksum = 0; + odph_ipv4_csum_update(pkt); + + /* UDP */ + odp_packet_l4_offset_set(pkt, ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN); + udp = (odph_udphdr_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN); + udp->src_port = odp_cpu_to_be_16(12049); + udp->dst_port = odp_cpu_to_be_16(12050); + udp->length = odp_cpu_to_be_16(pkt_len - + ODPH_ETHHDR_LEN - ODPH_IPV4HDR_LEN); + udp->chksum = 0; + + return pktio_pkt_set_seq(pkt); +} + +static int pktio_fixup_checksums(odp_packet_t pkt) +{ + odph_ipv4hdr_t *ip; + odph_udphdr_t *udp; + uint32_t len; + + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, &len); + + if (ip->proto != ODPH_IPPROTO_UDP) { + CU_FAIL("unexpected L4 protocol"); + return -1; + } + + udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, &len); + + ip->chksum = 0; + odph_ipv4_csum_update(pkt); + udp->chksum = 0; + udp->chksum = odph_ipv4_udp_chksum(pkt); + + return 0; +} + +static int default_pool_create(void) +{ + odp_pool_param_t params; + char pool_name[ODP_POOL_NAME_LEN]; + + if (default_pkt_pool != ODP_POOL_INVALID) + return -1; + + memset(¶ms, 0, sizeof(params)); + set_pool_len(¶ms); + params.pkt.num = PKT_BUF_NUM; + params.type = ODP_POOL_PACKET; + + snprintf(pool_name, sizeof(pool_name), + "pkt_pool_default_%d", pool_segmentation); + default_pkt_pool = odp_pool_create(pool_name, ¶ms); + if (default_pkt_pool == ODP_POOL_INVALID) + return -1; + + return 0; +} + +static odp_pktio_t create_pktio(int iface_idx, odp_pktin_mode_t imode, + odp_pktout_mode_t omode) +{ + odp_pktio_t pktio; + odp_pktio_param_t pktio_param; + odp_pktin_queue_param_t pktin_param; + const char *iface = iface_name[iface_idx]; + + odp_pktio_param_init(&pktio_param); + + pktio_param.in_mode = imode; + pktio_param.out_mode = omode; + + pktio = odp_pktio_open(iface, pool[iface_idx], &pktio_param); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + CU_ASSERT(odp_pktio_to_u64(pktio) != + odp_pktio_to_u64(ODP_PKTIO_INVALID)); + + odp_pktin_queue_param_init(&pktin_param); + + /* Atomic queue when in scheduled mode */ + pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC; + + /* By default, single input and output queue in all modes. Config can + * be overridden before starting the interface. */ + CU_ASSERT(odp_pktin_queue_config(pktio, &pktin_param) == 0); + CU_ASSERT(odp_pktout_queue_config(pktio, NULL) == 0); + + if (wait_for_network) + odp_time_wait_ns(ODP_TIME_SEC_IN_NS / 4); + + return pktio; +} + +static int flush_input_queue(odp_pktio_t pktio, odp_pktin_mode_t imode) +{ + odp_event_t ev; + odp_queue_t queue = ODP_QUEUE_INVALID; + + if (imode == ODP_PKTIN_MODE_QUEUE) { + /* Assert breaks else-if without brackets */ + CU_ASSERT_FATAL(odp_pktin_event_queue(pktio, &queue, 1) == 1); + } else if (imode == ODP_PKTIN_MODE_DIRECT) { + return 0; + } + + /* flush any pending events */ + while (1) { + if (queue != ODP_QUEUE_INVALID) + ev = odp_queue_deq(queue); + else + ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT); + + if (ev != ODP_EVENT_INVALID) + odp_event_free(ev); + else + break; + } + + return 0; +} + +static int create_packets(odp_packet_t pkt_tbl[], uint32_t pkt_seq[], int num, + odp_pktio_t pktio_src, odp_pktio_t pktio_dst) +{ + int i; + + for (i = 0; i < num; i++) { + pkt_tbl[i] = odp_packet_alloc(default_pkt_pool, packet_len); + if (pkt_tbl[i] == ODP_PACKET_INVALID) + break; + + pkt_seq[i] = pktio_init_packet(pkt_tbl[i]); + if (pkt_seq[i] == TEST_SEQ_INVALID) { + odp_packet_free(pkt_tbl[i]); + break; + } + + pktio_pkt_set_macs(pkt_tbl[i], pktio_src, pktio_dst); + + if (pktio_fixup_checksums(pkt_tbl[i]) != 0) { + odp_packet_free(pkt_tbl[i]); + break; + } + } + + return i; +} + +static int get_packets(pktio_info_t *pktio_rx, odp_packet_t pkt_tbl[], + int num, txrx_mode_e mode) +{ + odp_event_t evt_tbl[num]; + int num_evts = 0; + int num_pkts = 0; + int i; + + if (pktio_rx->in_mode == ODP_PKTIN_MODE_DIRECT) { + odp_pktin_queue_t pktin; + + CU_ASSERT_FATAL(odp_pktin_queue(pktio_rx->id, &pktin, 1) == 1); + return odp_pktin_recv(pktin, pkt_tbl, num); + } + + if (mode == TXRX_MODE_MULTI) { + if (pktio_rx->in_mode == ODP_PKTIN_MODE_QUEUE) + num_evts = odp_queue_deq_multi(pktio_rx->inq, evt_tbl, + num); + else + num_evts = odp_schedule_multi(NULL, ODP_SCHED_NO_WAIT, + evt_tbl, num); + } else { + odp_event_t evt_tmp; + + if (pktio_rx->in_mode == ODP_PKTIN_MODE_QUEUE) + evt_tmp = odp_queue_deq(pktio_rx->inq); + else + evt_tmp = odp_schedule(NULL, ODP_SCHED_NO_WAIT); + + if (evt_tmp != ODP_EVENT_INVALID) + evt_tbl[num_evts++] = evt_tmp; + } + + /* convert events to packets, discarding any non-packet events */ + for (i = 0; i < num_evts; ++i) { + if (odp_event_type(evt_tbl[i]) == ODP_EVENT_PACKET) + pkt_tbl[num_pkts++] = odp_packet_from_event(evt_tbl[i]); + else + odp_event_free(evt_tbl[i]); + } + + return num_pkts; +} + +static int wait_for_packets(pktio_info_t *pktio_rx, odp_packet_t pkt_tbl[], + uint32_t seq_tbl[], int num, txrx_mode_e mode, + uint64_t ns) +{ + odp_time_t wait_time, end; + int num_rx = 0; + int i; + odp_packet_t pkt_tmp[num]; + + wait_time = odp_time_local_from_ns(ns); + end = odp_time_sum(odp_time_local(), wait_time); + + do { + int n = get_packets(pktio_rx, pkt_tmp, num - num_rx, mode); + + if (n < 0) + break; + + for (i = 0; i < n; ++i) { + if (pktio_pkt_seq(pkt_tmp[i]) == seq_tbl[num_rx]) + pkt_tbl[num_rx++] = pkt_tmp[i]; + else + odp_packet_free(pkt_tmp[i]); + } + } while (num_rx < num && odp_time_cmp(end, odp_time_local()) > 0); + + return num_rx; +} + +static int recv_packets_tmo(odp_pktio_t pktio, odp_packet_t pkt_tbl[], + uint32_t seq_tbl[], int num, recv_tmo_mode_e mode, + uint64_t tmo, uint64_t ns) +{ + odp_packet_t pkt_tmp[num]; + odp_pktin_queue_t pktin[MAX_QUEUES]; + odp_time_t ts1, ts2; + int num_rx = 0; + int num_q; + int i; + int n; + unsigned from_val; + unsigned *from = NULL; + + if (mode == RECV_MQ_TMO) + from = &from_val; + + num_q = odp_pktin_queue(pktio, pktin, MAX_QUEUES); + CU_ASSERT_FATAL(num_q > 0); + + /** Multiple odp_pktin_recv_tmo()/odp_pktin_recv_mq_tmo() calls may be + * required to discard possible non-test packets. */ + do { + ts1 = odp_time_global(); + if (mode == RECV_TMO) + n = odp_pktin_recv_tmo(pktin[0], pkt_tmp, num - num_rx, + tmo); + else + n = odp_pktin_recv_mq_tmo(pktin, (unsigned)num_q, + from, pkt_tmp, + num - num_rx, tmo); + ts2 = odp_time_global(); + + if (n <= 0) + break; + for (i = 0; i < n; i++) { + if (pktio_pkt_seq(pkt_tmp[i]) == seq_tbl[num_rx]) + pkt_tbl[num_rx++] = pkt_tmp[i]; + else + odp_packet_free(pkt_tmp[i]); + } + if (mode == RECV_MQ_TMO) + CU_ASSERT(from_val < (unsigned)num_q); + } while (num_rx < num); + + if (tmo == ODP_PKTIN_WAIT) + CU_ASSERT(num_rx == num); + if (num_rx < num) + CU_ASSERT(odp_time_to_ns(odp_time_diff(ts2, ts1)) >= ns); + + return num_rx; +} + +static int send_packets(odp_pktout_queue_t pktout, + odp_packet_t *pkt_tbl, unsigned pkts) +{ + int ret; + unsigned sent = 0; + + while (sent < pkts) { + ret = odp_pktout_send(pktout, &pkt_tbl[sent], pkts - sent); + + if (ret < 0) { + CU_FAIL_FATAL("failed to send test packet"); + return -1; + } + + sent += ret; + } + + return 0; +} + +static int send_packet_events(odp_queue_t queue, + odp_packet_t *pkt_tbl, unsigned pkts) +{ + int ret; + unsigned i; + unsigned sent = 0; + odp_event_t ev_tbl[pkts]; + + for (i = 0; i < pkts; i++) + ev_tbl[i] = odp_packet_to_event(pkt_tbl[i]); + + while (sent < pkts) { + ret = odp_queue_enq_multi(queue, &ev_tbl[sent], pkts - sent); + + if (ret < 0) { + CU_FAIL_FATAL("failed to send test packet as events"); + return -1; + } + + sent += ret; + } + + return 0; +} + +static void pktio_txrx_multi(pktio_info_t *pktio_a, pktio_info_t *pktio_b, + int num_pkts, txrx_mode_e mode) +{ + odp_packet_t tx_pkt[num_pkts]; + odp_packet_t rx_pkt[num_pkts]; + uint32_t tx_seq[num_pkts]; + int i, ret, num_rx; + + if (packet_len == USE_MTU) { + uint32_t mtu; + + mtu = odp_pktio_mtu(pktio_a->id); + if (odp_pktio_mtu(pktio_b->id) < mtu) + mtu = odp_pktio_mtu(pktio_b->id); + CU_ASSERT_FATAL(mtu > 0); + packet_len = mtu; + if (packet_len > PKT_LEN_MAX) + packet_len = PKT_LEN_MAX; + } + + /* generate test packets to send */ + ret = create_packets(tx_pkt, tx_seq, num_pkts, pktio_a->id, + pktio_b->id); + if (ret != num_pkts) { + CU_FAIL("failed to generate test packets"); + return; + } + + /* send packet(s) out */ + if (mode == TXRX_MODE_SINGLE) { + for (i = 0; i < num_pkts; ++i) { + ret = odp_pktout_send(pktio_a->pktout, &tx_pkt[i], 1); + if (ret != 1) { + CU_FAIL_FATAL("failed to send test packet"); + odp_packet_free(tx_pkt[i]); + return; + } + } + } else if (mode == TXRX_MODE_MULTI) { + send_packets(pktio_a->pktout, tx_pkt, num_pkts); + } else { + send_packet_events(pktio_a->queue_out, tx_pkt, num_pkts); + } + + /* and wait for them to arrive back */ + num_rx = wait_for_packets(pktio_b, rx_pkt, tx_seq, + num_pkts, mode, ODP_TIME_SEC_IN_NS); + CU_ASSERT(num_rx == num_pkts); + + for (i = 0; i < num_rx; ++i) { + CU_ASSERT_FATAL(rx_pkt[i] != ODP_PACKET_INVALID); + CU_ASSERT(odp_packet_input(rx_pkt[i]) == pktio_b->id); + CU_ASSERT(odp_packet_has_error(rx_pkt[i]) == 0); + odp_packet_free(rx_pkt[i]); + } +} + +static void test_txrx(odp_pktin_mode_t in_mode, int num_pkts, + txrx_mode_e mode) +{ + int ret, i, if_b; + pktio_info_t pktios[MAX_NUM_IFACES]; + pktio_info_t *io; + + /* create pktios and associate input/output queues */ + for (i = 0; i < num_ifaces; ++i) { + odp_pktout_queue_t pktout; + odp_queue_t queue; + odp_pktout_mode_t out_mode = ODP_PKTOUT_MODE_DIRECT; + + if (mode == TXRX_MODE_MULTI_EVENT) + out_mode = ODP_PKTOUT_MODE_QUEUE; + + io = &pktios[i]; + + io->name = iface_name[i]; + io->id = create_pktio(i, in_mode, out_mode); + if (io->id == ODP_PKTIO_INVALID) { + CU_FAIL("failed to open iface"); + return; + } + + if (mode == TXRX_MODE_MULTI_EVENT) { + CU_ASSERT_FATAL(odp_pktout_event_queue(io->id, + &queue, 1) == 1); + } else { + CU_ASSERT_FATAL(odp_pktout_queue(io->id, + &pktout, 1) == 1); + io->pktout = pktout; + queue = ODP_QUEUE_INVALID; + } + + io->queue_out = queue; + io->in_mode = in_mode; + + if (in_mode == ODP_PKTIN_MODE_QUEUE) { + CU_ASSERT_FATAL(odp_pktin_event_queue(io->id, &queue, 1) + == 1); + io->inq = queue; + } else { + io->inq = ODP_QUEUE_INVALID; + } + + ret = odp_pktio_start(io->id); + CU_ASSERT(ret == 0); + + _pktio_wait_linkup(io->id); + } + + /* if we have two interfaces then send through one and receive on + * another but if there's only one assume it's a loopback */ + if_b = (num_ifaces == 1) ? 0 : 1; + pktio_txrx_multi(&pktios[0], &pktios[if_b], num_pkts, mode); + + for (i = 0; i < num_ifaces; ++i) { + ret = odp_pktio_stop(pktios[i].id); + CU_ASSERT_FATAL(ret == 0); + flush_input_queue(pktios[i].id, in_mode); + ret = odp_pktio_close(pktios[i].id); + CU_ASSERT(ret == 0); + } +} + +void pktio_test_plain_queue(void) +{ + test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_SINGLE); + test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_SINGLE); +} + +void pktio_test_plain_multi(void) +{ + test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_MULTI); + test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_MULTI); +} + +void pktio_test_plain_multi_event(void) +{ + test_txrx(ODP_PKTIN_MODE_QUEUE, 1, TXRX_MODE_MULTI_EVENT); + test_txrx(ODP_PKTIN_MODE_QUEUE, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT); +} + +void pktio_test_sched_queue(void) +{ + test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_SINGLE); + test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_SINGLE); +} + +void pktio_test_sched_multi(void) +{ + test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_MULTI); + test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_MULTI); +} + +void pktio_test_sched_multi_event(void) +{ + test_txrx(ODP_PKTIN_MODE_SCHED, 1, TXRX_MODE_MULTI_EVENT); + test_txrx(ODP_PKTIN_MODE_SCHED, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT); +} + +void pktio_test_recv(void) +{ + test_txrx(ODP_PKTIN_MODE_DIRECT, 1, TXRX_MODE_SINGLE); +} + +void pktio_test_recv_multi(void) +{ + test_txrx(ODP_PKTIN_MODE_DIRECT, TX_BATCH_LEN, TXRX_MODE_MULTI); +} + +void pktio_test_recv_multi_event(void) +{ + test_txrx(ODP_PKTIN_MODE_DIRECT, 1, TXRX_MODE_MULTI_EVENT); + test_txrx(ODP_PKTIN_MODE_DIRECT, TX_BATCH_LEN, TXRX_MODE_MULTI_EVENT); +} + +void pktio_test_recv_queue(void) +{ + odp_pktio_t pktio_tx, pktio_rx; + odp_pktio_t pktio[MAX_NUM_IFACES]; + odp_pktio_capability_t capa; + odp_pktin_queue_param_t in_queue_param; + odp_pktout_queue_param_t out_queue_param; + odp_pktout_queue_t pktout_queue[MAX_QUEUES]; + odp_pktin_queue_t pktin_queue[MAX_QUEUES]; + odp_packet_t pkt_tbl[TX_BATCH_LEN]; + odp_packet_t tmp_pkt[TX_BATCH_LEN]; + uint32_t pkt_seq[TX_BATCH_LEN]; + odp_time_t wait_time, end; + int num_rx = 0; + int num_queues; + int ret; + int i; + + CU_ASSERT_FATAL(num_ifaces >= 1); + + /* Open and configure interfaces */ + for (i = 0; i < num_ifaces; ++i) { + pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT, + ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID); + + CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0); + + odp_pktin_queue_param_init(&in_queue_param); + num_queues = capa.max_input_queues; + in_queue_param.num_queues = num_queues; + in_queue_param.hash_enable = (num_queues > 1) ? 1 : 0; + in_queue_param.hash_proto.proto.ipv4_udp = 1; + + ret = odp_pktin_queue_config(pktio[i], &in_queue_param); + CU_ASSERT_FATAL(ret == 0); + + odp_pktout_queue_param_init(&out_queue_param); + out_queue_param.num_queues = capa.max_output_queues; + + ret = odp_pktout_queue_config(pktio[i], &out_queue_param); + CU_ASSERT_FATAL(ret == 0); + + CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0); + } + + for (i = 0; i < num_ifaces; ++i) + _pktio_wait_linkup(pktio[i]); + + pktio_tx = pktio[0]; + if (num_ifaces > 1) + pktio_rx = pktio[1]; + else + pktio_rx = pktio_tx; + + /* Allocate and initialize test packets */ + ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx, + pktio_rx); + if (ret != TX_BATCH_LEN) { + CU_FAIL("Failed to generate test packets"); + return; + } + + /* Send packets */ + num_queues = odp_pktout_queue(pktio_tx, pktout_queue, MAX_QUEUES); + CU_ASSERT_FATAL(num_queues > 0); + if (num_queues > MAX_QUEUES) + num_queues = MAX_QUEUES; + + ret = odp_pktout_send(pktout_queue[num_queues - 1], pkt_tbl, + TX_BATCH_LEN); + CU_ASSERT_FATAL(ret == TX_BATCH_LEN); + + /* Receive packets */ + num_queues = odp_pktin_queue(pktio_rx, pktin_queue, MAX_QUEUES); + CU_ASSERT_FATAL(num_queues > 0); + if (num_queues > MAX_QUEUES) + num_queues = MAX_QUEUES; + + wait_time = odp_time_local_from_ns(ODP_TIME_SEC_IN_NS); + end = odp_time_sum(odp_time_local(), wait_time); + do { + int n = 0; + + for (i = 0; i < num_queues; i++) { + n = odp_pktin_recv(pktin_queue[i], tmp_pkt, + TX_BATCH_LEN); + if (n != 0) + break; + } + if (n < 0) + break; + for (i = 0; i < n; i++) { + if (pktio_pkt_seq(tmp_pkt[i]) == pkt_seq[num_rx]) + pkt_tbl[num_rx++] = tmp_pkt[i]; + else + odp_packet_free(tmp_pkt[i]); + } + } while (num_rx < TX_BATCH_LEN && + odp_time_cmp(end, odp_time_local()) > 0); + + CU_ASSERT(num_rx == TX_BATCH_LEN); + + for (i = 0; i < num_rx; i++) + odp_packet_free(pkt_tbl[i]); + + for (i = 0; i < num_ifaces; i++) { + CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0); + CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0); + } +} + +static void test_recv_tmo(recv_tmo_mode_e mode) +{ + odp_pktio_t pktio_tx, pktio_rx; + odp_pktio_t pktio[MAX_NUM_IFACES]; + odp_pktio_capability_t capa; + odp_pktin_queue_param_t in_queue_param; + odp_pktout_queue_t pktout_queue; + int test_pkt_count = 6; + odp_packet_t pkt_tbl[test_pkt_count]; + uint32_t pkt_seq[test_pkt_count]; + uint64_t ns; + unsigned num_q; + int ret; + int i; + + CU_ASSERT_FATAL(num_ifaces >= 1); + + /* Open and configure interfaces */ + for (i = 0; i < num_ifaces; ++i) { + pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT, + ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID); + + CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0); + + odp_pktin_queue_param_init(&in_queue_param); + if (mode == RECV_TMO) + num_q = 1; + else + num_q = (capa.max_input_queues < MAX_QUEUES) ? + capa.max_input_queues : MAX_QUEUES; + in_queue_param.num_queues = num_q; + in_queue_param.hash_enable = (num_q > 1) ? 1 : 0; + in_queue_param.hash_proto.proto.ipv4_udp = 1; + + ret = odp_pktin_queue_config(pktio[i], &in_queue_param); + CU_ASSERT_FATAL(ret == 0); + + CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0); + } + + for (i = 0; i < num_ifaces; i++) + _pktio_wait_linkup(pktio[i]); + + pktio_tx = pktio[0]; + pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx; + + ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1); + CU_ASSERT_FATAL(ret > 0); + + memset(pkt_seq, 0, sizeof(pkt_seq)); + + /* No packets sent yet, so should wait */ + ns = 100 * ODP_TIME_MSEC_IN_NS; + ret = recv_packets_tmo(pktio_rx, &pkt_tbl[0], &pkt_seq[0], 1, mode, + odp_pktin_wait_time(ns), ns); + CU_ASSERT(ret == 0); + + ret = create_packets(pkt_tbl, pkt_seq, test_pkt_count, pktio_tx, + pktio_rx); + CU_ASSERT_FATAL(ret == test_pkt_count); + + ret = odp_pktout_send(pktout_queue, pkt_tbl, test_pkt_count); + CU_ASSERT_FATAL(ret == test_pkt_count); + + ret = recv_packets_tmo(pktio_rx, &pkt_tbl[0], &pkt_seq[0], 1, mode, + ODP_PKTIN_WAIT, 0); + CU_ASSERT_FATAL(ret == 1); + + ret = recv_packets_tmo(pktio_rx, &pkt_tbl[1], &pkt_seq[1], 1, mode, + ODP_PKTIN_NO_WAIT, 0); + CU_ASSERT_FATAL(ret == 1); + + ret = recv_packets_tmo(pktio_rx, &pkt_tbl[2], &pkt_seq[2], 1, mode, + odp_pktin_wait_time(0), 0); + CU_ASSERT_FATAL(ret == 1); + + ret = recv_packets_tmo(pktio_rx, &pkt_tbl[3], &pkt_seq[3], 3, mode, + odp_pktin_wait_time(ns), ns); + CU_ASSERT_FATAL(ret == 3); + + for (i = 0; i < test_pkt_count; i++) + odp_packet_free(pkt_tbl[i]); + + for (i = 0; i < num_ifaces; i++) { + CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0); + CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0); + } +} + +void pktio_test_recv_tmo(void) +{ + test_recv_tmo(RECV_TMO); +} + +void pktio_test_recv_mq_tmo(void) +{ + test_recv_tmo(RECV_MQ_TMO); + test_recv_tmo(RECV_MQ_TMO_NO_IDX); +} + +void pktio_test_recv_mtu(void) +{ + packet_len = USE_MTU; + pktio_test_sched_multi(); + packet_len = PKT_LEN_NORMAL; +} + +void pktio_test_mtu(void) +{ + int ret; + uint32_t mtu; + + odp_pktio_t pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, + ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + mtu = odp_pktio_mtu(pktio); + CU_ASSERT(mtu > 0); + + printf(" %" PRIu32 " ", mtu); + + ret = odp_pktio_close(pktio); + CU_ASSERT(ret == 0); +} + +void pktio_test_promisc(void) +{ + int ret; + odp_pktio_capability_t capa; + + odp_pktio_t pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, + ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + ret = odp_pktio_promisc_mode(pktio); + CU_ASSERT(ret >= 0); + + CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0); + if (!capa.set_op.op.promisc_mode) { + printf("promiscuous mode not supported\n"); + ret = odp_pktio_close(pktio); + CU_ASSERT(ret == 0); + return; + } + + ret = odp_pktio_promisc_mode_set(pktio, 1); + CU_ASSERT(0 == ret); + + /* Verify that promisc mode set */ + ret = odp_pktio_promisc_mode(pktio); + CU_ASSERT(1 == ret); + + ret = odp_pktio_promisc_mode_set(pktio, 0); + CU_ASSERT(0 == ret); + + /* Verify that promisc mode is not set */ + ret = odp_pktio_promisc_mode(pktio); + CU_ASSERT(0 == ret); + + ret = odp_pktio_close(pktio); + CU_ASSERT(ret == 0); +} + +void pktio_test_mac(void) +{ + unsigned char mac_addr[ODP_PKTIO_MACADDR_MAXSIZE]; + int mac_len; + int ret; + odp_pktio_t pktio; + + pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, + ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + printf("testing mac for %s\n", iface_name[0]); + + mac_len = odp_pktio_mac_addr(pktio, mac_addr, + ODP_PKTIO_MACADDR_MAXSIZE); + CU_ASSERT(ODPH_ETHADDR_LEN == mac_len); + CU_ASSERT(ODP_PKTIO_MACADDR_MAXSIZE >= mac_len); + + printf(" %X:%X:%X:%X:%X:%X ", + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + + /* Fail case: wrong addr_size. Expected <0. */ + mac_len = odp_pktio_mac_addr(pktio, mac_addr, 2); + CU_ASSERT(mac_len < 0); + + ret = odp_pktio_close(pktio); + CU_ASSERT(0 == ret); +} + +void pktio_test_open(void) +{ + odp_pktio_t pktio; + odp_pktio_param_t pktio_param; + int i; + + /* test the sequence open->close->open->close() */ + for (i = 0; i < 2; ++i) { + pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, + ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + CU_ASSERT(odp_pktio_close(pktio) == 0); + } + + odp_pktio_param_init(&pktio_param); + pktio_param.in_mode = ODP_PKTIN_MODE_SCHED; + + pktio = odp_pktio_open("nothere", default_pkt_pool, &pktio_param); + CU_ASSERT(pktio == ODP_PKTIO_INVALID); +} + +void pktio_test_lookup(void) +{ + odp_pktio_t pktio, pktio_inval; + odp_pktio_param_t pktio_param; + + odp_pktio_param_init(&pktio_param); + pktio_param.in_mode = ODP_PKTIN_MODE_SCHED; + + pktio = odp_pktio_open(iface_name[0], default_pkt_pool, &pktio_param); + CU_ASSERT(pktio != ODP_PKTIO_INVALID); + + CU_ASSERT(odp_pktio_lookup(iface_name[0]) == pktio); + + pktio_inval = odp_pktio_open(iface_name[0], default_pkt_pool, + &pktio_param); + CU_ASSERT(odp_errno() != 0); + CU_ASSERT(pktio_inval == ODP_PKTIO_INVALID); + + CU_ASSERT(odp_pktio_close(pktio) == 0); + + CU_ASSERT(odp_pktio_lookup(iface_name[0]) == ODP_PKTIO_INVALID); +} + +void pktio_test_index(void) +{ + odp_pktio_t pktio, pktio_inval = ODP_PKTIO_INVALID; + odp_pktio_param_t pktio_param; + int ndx; + + odp_pktio_param_init(&pktio_param); + pktio_param.in_mode = ODP_PKTIN_MODE_SCHED; + + pktio = odp_pktio_open(iface_name[0], default_pkt_pool, &pktio_param); + CU_ASSERT(pktio != ODP_PKTIO_INVALID); + + ndx = odp_pktio_index(pktio); + CU_ASSERT(ndx >= 0); + CU_ASSERT(odp_pktio_index(pktio_inval) < 0); + + CU_ASSERT(odp_pktio_close(pktio) == 0); + CU_ASSERT(odp_pktio_index(pktio) < 0); +} + +static void pktio_test_print(void) +{ + odp_pktio_t pktio; + int i; + + for (i = 0; i < num_ifaces; ++i) { + pktio = create_pktio(i, ODP_PKTIN_MODE_QUEUE, + ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + /* Print pktio debug info and test that the + * odp_pktio_print() function is implemented. */ + odp_pktio_print(pktio); + + CU_ASSERT(odp_pktio_close(pktio) == 0); + } +} + +void pktio_test_pktio_config(void) +{ + odp_pktio_t pktio; + odp_pktio_capability_t capa; + odp_pktio_config_t config; + + pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + odp_pktio_config_init(&config); + + CU_ASSERT(odp_pktio_config(pktio, NULL) == 0); + + CU_ASSERT(odp_pktio_config(pktio, &config) == 0); + + CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0); + + config = capa.config; + CU_ASSERT(odp_pktio_config(pktio, &config) == 0); + + CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0); +} + +void pktio_test_info(void) +{ + odp_pktio_t pktio; + odp_pktio_info_t pktio_info; + int i; + + for (i = 0; i < num_ifaces; i++) { + pktio = create_pktio(i, ODP_PKTIN_MODE_QUEUE, + ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + CU_ASSERT_FATAL(odp_pktio_info(pktio, &pktio_info) == 0); + + printf("pktio %d\n name %s\n driver %s\n", i, + pktio_info.name, pktio_info.drv_name); + + CU_ASSERT(strcmp(pktio_info.name, iface_name[i]) == 0); + CU_ASSERT(pktio_info.pool == pool[i]); + CU_ASSERT(pktio_info.param.in_mode == ODP_PKTIN_MODE_QUEUE); + CU_ASSERT(pktio_info.param.out_mode == ODP_PKTOUT_MODE_DIRECT); + + CU_ASSERT(odp_pktio_info(ODP_PKTIO_INVALID, &pktio_info) < 0); + + CU_ASSERT(odp_pktio_close(pktio) == 0); + } +} + +void pktio_test_pktin_queue_config_direct(void) +{ + odp_pktio_t pktio; + odp_pktio_capability_t capa; + odp_pktin_queue_param_t queue_param; + odp_pktin_queue_t pktin_queues[MAX_QUEUES]; + odp_queue_t in_queues[MAX_QUEUES]; + int num_queues; + + pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + CU_ASSERT(odp_pktio_capability(ODP_PKTIO_INVALID, &capa) < 0); + + CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 && + capa.max_input_queues > 0); + num_queues = capa.max_input_queues; + + odp_pktin_queue_param_init(&queue_param); + + queue_param.hash_enable = (num_queues > 1) ? 1 : 0; + queue_param.hash_proto.proto.ipv4_udp = 1; + queue_param.num_queues = num_queues; + CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0); + + CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES) + == num_queues); + CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES) < 0); + + queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE; + queue_param.num_queues = 1; + CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0); + + CU_ASSERT(odp_pktin_queue_config(ODP_PKTIO_INVALID, &queue_param) < 0); + + queue_param.num_queues = capa.max_input_queues + 1; + CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0); + + CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0); +} + +void pktio_test_pktin_queue_config_sched(void) +{ + odp_pktio_t pktio; + odp_pktio_capability_t capa; + odp_pktin_queue_param_t queue_param; + odp_pktin_queue_t pktin_queues[MAX_QUEUES]; + odp_queue_t in_queues[MAX_QUEUES]; + int num_queues; + + pktio = create_pktio(0, ODP_PKTIN_MODE_SCHED, ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 && + capa.max_input_queues > 0); + num_queues = capa.max_input_queues; + + odp_pktin_queue_param_init(&queue_param); + + queue_param.hash_enable = (num_queues > 1) ? 1 : 0; + queue_param.hash_proto.proto.ipv4_udp = 1; + queue_param.num_queues = num_queues; + queue_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL; + queue_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC; + CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0); + + CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES) + == num_queues); + CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES) < 0); + + queue_param.num_queues = 1; + CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0); + + queue_param.num_queues = capa.max_input_queues + 1; + CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0); + + CU_ASSERT_FATAL(odp_pktio_close(pktio) == 0); +} + +void pktio_test_pktin_queue_config_queue(void) +{ + odp_pktio_t pktio; + odp_pktio_capability_t capa; + odp_pktin_queue_param_t queue_param; + odp_pktin_queue_t pktin_queues[MAX_QUEUES]; + odp_queue_t in_queues[MAX_QUEUES]; + int num_queues; + + pktio = create_pktio(0, ODP_PKTIN_MODE_QUEUE, ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 && + capa.max_input_queues > 0); + num_queues = capa.max_input_queues; + + odp_pktin_queue_param_init(&queue_param); + + queue_param.hash_enable = (num_queues > 1) ? 1 : 0; + queue_param.hash_proto.proto.ipv4_udp = 1; + queue_param.num_queues = num_queues; + CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0); + + CU_ASSERT(odp_pktin_event_queue(pktio, in_queues, MAX_QUEUES) + == num_queues); + CU_ASSERT(odp_pktin_queue(pktio, pktin_queues, MAX_QUEUES) < 0); + + queue_param.num_queues = 1; + CU_ASSERT_FATAL(odp_pktin_queue_config(pktio, &queue_param) == 0); + + queue_param.num_queues = capa.max_input_queues + 1; + CU_ASSERT(odp_pktin_queue_config(pktio, &queue_param) < 0); + + CU_ASSERT(odp_pktio_close(pktio) == 0); +} + +void pktio_test_pktout_queue_config(void) +{ + odp_pktio_t pktio; + odp_pktio_capability_t capa; + odp_pktout_queue_param_t queue_param; + odp_pktout_queue_t pktout_queues[MAX_QUEUES]; + int num_queues; + + pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio != ODP_PKTIO_INVALID); + + CU_ASSERT_FATAL(odp_pktio_capability(pktio, &capa) == 0 && + capa.max_output_queues > 0); + num_queues = capa.max_output_queues; + + odp_pktout_queue_param_init(&queue_param); + + queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE; + queue_param.num_queues = num_queues; + CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) == 0); + + CU_ASSERT(odp_pktout_queue(pktio, pktout_queues, MAX_QUEUES) + == num_queues); + + queue_param.op_mode = ODP_PKTIO_OP_MT; + queue_param.num_queues = 1; + CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) == 0); + + CU_ASSERT(odp_pktout_queue_config(ODP_PKTIO_INVALID, &queue_param) < 0); + + queue_param.num_queues = capa.max_output_queues + 1; + CU_ASSERT(odp_pktout_queue_config(pktio, &queue_param) < 0); + + CU_ASSERT(odp_pktio_close(pktio) == 0); +} + +#ifdef DEBUG_STATS +static void _print_pktio_stats(odp_pktio_stats_t *s, const char *name) +{ + fprintf(stderr, "\n%s:\n" + " in_octets %" PRIu64 "\n" + " in_ucast_pkts %" PRIu64 "\n" + " in_discards %" PRIu64 "\n" + " in_errors %" PRIu64 "\n" + " in_unknown_protos %" PRIu64 "\n" + " out_octets %" PRIu64 "\n" + " out_ucast_pkts %" PRIu64 "\n" + " out_discards %" PRIu64 "\n" + " out_errors %" PRIu64 "\n", + name, + s->in_octets, + s->in_ucast_pkts, + s->in_discards, + s->in_errors, + s->in_unknown_protos, + s->out_octets, + s->out_ucast_pkts, + s->out_discards, + s->out_errors); +} +#endif + +/* some pktio like netmap support various methods to + * get statistics counters. ethtool strings are not standardised + * and sysfs may not be supported. skip pktio_stats test until + * we will solve that.*/ +int pktio_check_statistics_counters(void) +{ + odp_pktio_t pktio; + odp_pktio_stats_t stats; + int ret; + odp_pktio_param_t pktio_param; + const char *iface = iface_name[0]; + + odp_pktio_param_init(&pktio_param); + pktio_param.in_mode = ODP_PKTIN_MODE_SCHED; + + pktio = odp_pktio_open(iface, pool[0], &pktio_param); + if (pktio == ODP_PKTIO_INVALID) + return ODP_TEST_INACTIVE; + + ret = odp_pktio_stats(pktio, &stats); + (void)odp_pktio_close(pktio); + + if (ret == 0) + return ODP_TEST_ACTIVE; + + return ODP_TEST_INACTIVE; +} + +void pktio_test_statistics_counters(void) +{ + odp_pktio_t pktio_rx, pktio_tx; + odp_pktio_t pktio[MAX_NUM_IFACES]; + odp_packet_t pkt; + odp_packet_t tx_pkt[1000]; + uint32_t pkt_seq[1000]; + odp_event_t ev; + int i, pkts, tx_pkts, ret, alloc = 0; + odp_pktout_queue_t pktout; + uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS); + odp_pktio_stats_t stats[2]; + + for (i = 0; i < num_ifaces; i++) { + pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED, + ODP_PKTOUT_MODE_DIRECT); + + CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID); + } + pktio_tx = pktio[0]; + pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx; + + CU_ASSERT(odp_pktout_queue(pktio_tx, &pktout, 1) == 1); + + ret = odp_pktio_start(pktio_tx); + CU_ASSERT(ret == 0); + if (num_ifaces > 1) { + ret = odp_pktio_start(pktio_rx); + CU_ASSERT(ret == 0); + } + + /* flush packets with magic number in pipes */ + for (i = 0; i < 1000; i++) { + ev = odp_schedule(NULL, wait); + if (ev != ODP_EVENT_INVALID) + odp_event_free(ev); + } + + alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio_tx, pktio_rx); + + ret = odp_pktio_stats_reset(pktio_tx); + CU_ASSERT(ret == 0); + if (num_ifaces > 1) { + ret = odp_pktio_stats_reset(pktio_rx); + CU_ASSERT(ret == 0); + } + + /* send */ + for (pkts = 0; pkts != alloc; ) { + ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts); + if (ret < 0) { + CU_FAIL("unable to send packet\n"); + break; + } + pkts += ret; + } + tx_pkts = pkts; + + /* get */ + for (i = 0, pkts = 0; i < 1000 && pkts != tx_pkts; i++) { + ev = odp_schedule(NULL, wait); + if (ev != ODP_EVENT_INVALID) { + if (odp_event_type(ev) == ODP_EVENT_PACKET) { + pkt = odp_packet_from_event(ev); + if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID) + pkts++; + } + odp_event_free(ev); + } + } + + CU_ASSERT(pkts == tx_pkts); + + ret = odp_pktio_stats(pktio_tx, &stats[0]); + CU_ASSERT(ret == 0); + + if (num_ifaces > 1) { + ret = odp_pktio_stats(pktio_rx, &stats[1]); + CU_ASSERT(ret == 0); + CU_ASSERT((stats[1].in_ucast_pkts == 0) || + (stats[1].in_ucast_pkts >= (uint64_t)pkts)); + CU_ASSERT((stats[0].out_octets == 0) || + (stats[0].out_octets >= + (PKT_LEN_NORMAL * (uint64_t)pkts))); + } else { + CU_ASSERT((stats[0].in_ucast_pkts == 0) || + (stats[0].in_ucast_pkts == (uint64_t)pkts)); + CU_ASSERT((stats[0].in_octets == 0) || + (stats[0].in_octets == + (PKT_LEN_NORMAL * (uint64_t)pkts))); + } + + CU_ASSERT(0 == stats[0].in_discards); + CU_ASSERT(0 == stats[0].in_errors); + CU_ASSERT(0 == stats[0].in_unknown_protos); + CU_ASSERT(0 == stats[0].out_discards); + CU_ASSERT(0 == stats[0].out_errors); + + for (i = 0; i < num_ifaces; i++) { + CU_ASSERT(odp_pktio_stop(pktio[i]) == 0); +#ifdef DEBUG_STATS + _print_pktio_stats(&stats[i], iface_name[i]); +#endif + flush_input_queue(pktio[i], ODP_PKTIN_MODE_SCHED); + CU_ASSERT(odp_pktio_close(pktio[i]) == 0); + } +} + +void pktio_test_start_stop(void) +{ + odp_pktio_t pktio[MAX_NUM_IFACES]; + odp_pktio_t pktio_in; + odp_packet_t pkt; + odp_packet_t tx_pkt[1000]; + uint32_t pkt_seq[1000]; + odp_event_t ev; + int i, pkts, ret, alloc = 0; + odp_pktout_queue_t pktout; + uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS); + + for (i = 0; i < num_ifaces; i++) { + pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED, + ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID); + } + + CU_ASSERT(odp_pktout_queue(pktio[0], &pktout, 1) == 1); + + /* Interfaces are stopped by default, + * Check that stop when stopped generates an error */ + ret = odp_pktio_stop(pktio[0]); + CU_ASSERT(ret < 0); + + /* start first */ + ret = odp_pktio_start(pktio[0]); + CU_ASSERT(ret == 0); + /* Check that start when started generates an error */ + ret = odp_pktio_start(pktio[0]); + CU_ASSERT(ret < 0); + + _pktio_wait_linkup(pktio[0]); + + /* Test Rx on a stopped interface. Only works if there are 2 */ + if (num_ifaces > 1) { + alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio[0], + pktio[1]); + + for (pkts = 0; pkts != alloc; ) { + ret = odp_pktout_send(pktout, &tx_pkt[pkts], + alloc - pkts); + if (ret < 0) { + CU_FAIL("unable to enqueue packet\n"); + break; + } + pkts += ret; + } + /* check that packets did not arrive */ + for (i = 0, pkts = 0; i < 1000; i++) { + ev = odp_schedule(NULL, wait); + if (ev == ODP_EVENT_INVALID) + continue; + + if (odp_event_type(ev) == ODP_EVENT_PACKET) { + pkt = odp_packet_from_event(ev); + if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID) + pkts++; + } + odp_event_free(ev); + } + if (pkts) + CU_FAIL("pktio stopped, received unexpected events"); + + /* start both, send and get packets */ + /* 0 already started */ + ret = odp_pktio_start(pktio[1]); + CU_ASSERT(ret == 0); + + _pktio_wait_linkup(pktio[1]); + + /* flush packets with magic number in pipes */ + for (i = 0; i < 1000; i++) { + ev = odp_schedule(NULL, wait); + if (ev != ODP_EVENT_INVALID) + odp_event_free(ev); + } + } + + if (num_ifaces > 1) + pktio_in = pktio[1]; + else + pktio_in = pktio[0]; + + alloc = create_packets(tx_pkt, pkt_seq, 1000, pktio[0], pktio_in); + + /* send */ + for (pkts = 0; pkts != alloc; ) { + ret = odp_pktout_send(pktout, &tx_pkt[pkts], alloc - pkts); + if (ret < 0) { + CU_FAIL("unable to enqueue packet\n"); + break; + } + pkts += ret; + } + + /* get */ + for (i = 0, pkts = 0; i < 1000; i++) { + ev = odp_schedule(NULL, wait); + if (ev != ODP_EVENT_INVALID) { + if (odp_event_type(ev) == ODP_EVENT_PACKET) { + pkt = odp_packet_from_event(ev); + if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID) + pkts++; + } + odp_event_free(ev); + } + } + CU_ASSERT(pkts == alloc); + + for (i = 0; i < num_ifaces; i++) { + CU_ASSERT(odp_pktio_stop(pktio[i]) == 0); + CU_ASSERT(odp_pktio_close(pktio[i]) == 0); + } + + /* Verify that a schedule call after stop and close does not generate + errors. */ + ev = odp_schedule(NULL, wait); + CU_ASSERT(ev == ODP_EVENT_INVALID); + if (ev != ODP_EVENT_INVALID) + odp_event_free(ev); +} + +/* + * This is a pre-condition check that the pktio_test_send_failure() + * test case can be run. If the TX interface MTU is larger that the + * biggest packet we can allocate then the test won't be able to + * attempt to send packets larger than the MTU, so skip the test. + */ +int pktio_check_send_failure(void) +{ + odp_pktio_t pktio_tx; + uint32_t mtu; + odp_pktio_param_t pktio_param; + int iface_idx = 0; + const char *iface = iface_name[iface_idx]; + odp_pool_capability_t pool_capa; + + if (odp_pool_capability(&pool_capa) < 0) { + fprintf(stderr, "%s: pool capability failed\n", __func__); + return ODP_TEST_INACTIVE; + }; + + memset(&pktio_param, 0, sizeof(pktio_param)); + + pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT; + + pktio_tx = odp_pktio_open(iface, pool[iface_idx], &pktio_param); + if (pktio_tx == ODP_PKTIO_INVALID) { + fprintf(stderr, "%s: failed to open pktio\n", __func__); + return ODP_TEST_INACTIVE; + } + + /* read the MTU from the transmit interface */ + mtu = odp_pktio_mtu(pktio_tx); + + odp_pktio_close(pktio_tx); + + if (mtu <= pool_capa.pkt.max_len - 32) + return ODP_TEST_ACTIVE; + + return ODP_TEST_INACTIVE; +} + +void pktio_test_send_failure(void) +{ + odp_pktio_t pktio_tx, pktio_rx; + odp_packet_t pkt_tbl[TX_BATCH_LEN]; + uint32_t pkt_seq[TX_BATCH_LEN]; + int ret, i, alloc_pkts; + uint32_t mtu; + odp_pool_param_t pool_params; + odp_pool_t pkt_pool; + int long_pkt_idx = TX_BATCH_LEN / 2; + pktio_info_t info_rx; + odp_pktout_queue_t pktout; + + pktio_tx = create_pktio(0, ODP_PKTIN_MODE_DIRECT, + ODP_PKTOUT_MODE_DIRECT); + if (pktio_tx == ODP_PKTIO_INVALID) { + CU_FAIL("failed to open pktio"); + return; + } + + CU_ASSERT_FATAL(odp_pktout_queue(pktio_tx, &pktout, 1) == 1); + + /* read the MTU from the transmit interface */ + mtu = odp_pktio_mtu(pktio_tx); + + ret = odp_pktio_start(pktio_tx); + CU_ASSERT_FATAL(ret == 0); + + _pktio_wait_linkup(pktio_tx); + + /* configure the pool so that we can generate test packets larger + * than the interface MTU */ + memset(&pool_params, 0, sizeof(pool_params)); + pool_params.pkt.len = mtu + 32; + pool_params.pkt.seg_len = pool_params.pkt.len; + pool_params.pkt.num = TX_BATCH_LEN + 1; + pool_params.type = ODP_POOL_PACKET; + pkt_pool = odp_pool_create("pkt_pool_oversize", &pool_params); + CU_ASSERT_FATAL(pkt_pool != ODP_POOL_INVALID); + + if (num_ifaces > 1) { + pktio_rx = create_pktio(1, ODP_PKTIN_MODE_DIRECT, + ODP_PKTOUT_MODE_DIRECT); + ret = odp_pktio_start(pktio_rx); + CU_ASSERT_FATAL(ret == 0); + + _pktio_wait_linkup(pktio_rx); + } else { + pktio_rx = pktio_tx; + } + + /* generate a batch of packets with a single overly long packet + * in the middle */ + for (i = 0; i < TX_BATCH_LEN; ++i) { + uint32_t pkt_len; + + if (i == long_pkt_idx) + pkt_len = pool_params.pkt.len; + else + pkt_len = PKT_LEN_NORMAL; + + pkt_tbl[i] = odp_packet_alloc(pkt_pool, pkt_len); + if (pkt_tbl[i] == ODP_PACKET_INVALID) + break; + + pkt_seq[i] = pktio_init_packet(pkt_tbl[i]); + + pktio_pkt_set_macs(pkt_tbl[i], pktio_tx, pktio_rx); + if (pktio_fixup_checksums(pkt_tbl[i]) != 0) { + odp_packet_free(pkt_tbl[i]); + break; + } + + if (pkt_seq[i] == TEST_SEQ_INVALID) { + odp_packet_free(pkt_tbl[i]); + break; + } + } + alloc_pkts = i; + + if (alloc_pkts == TX_BATCH_LEN) { + /* try to send the batch with the long packet in the middle, + * the initial short packets should be sent successfully */ + odp_errno_zero(); + ret = odp_pktout_send(pktout, pkt_tbl, TX_BATCH_LEN); + CU_ASSERT_FATAL(ret == long_pkt_idx); + CU_ASSERT(odp_errno() == 0); + + info_rx.id = pktio_rx; + info_rx.inq = ODP_QUEUE_INVALID; + info_rx.in_mode = ODP_PKTIN_MODE_DIRECT; + + i = wait_for_packets(&info_rx, pkt_tbl, pkt_seq, ret, + TXRX_MODE_MULTI, ODP_TIME_SEC_IN_NS); + + if (i == ret) { + /* now try to send starting with the too-long packet + * and verify it fails */ + odp_errno_zero(); + ret = odp_pktout_send(pktout, + &pkt_tbl[long_pkt_idx], + TX_BATCH_LEN - long_pkt_idx); + CU_ASSERT(ret == -1); + CU_ASSERT(odp_errno() != 0); + } else { + CU_FAIL("failed to receive transmitted packets\n"); + } + + /* now reduce the size of the long packet and attempt to send + * again - should work this time */ + i = long_pkt_idx; + odp_packet_pull_tail(pkt_tbl[i], + odp_packet_len(pkt_tbl[i]) - + PKT_LEN_NORMAL); + pkt_seq[i] = pktio_init_packet(pkt_tbl[i]); + + pktio_pkt_set_macs(pkt_tbl[i], pktio_tx, pktio_rx); + ret = pktio_fixup_checksums(pkt_tbl[i]); + CU_ASSERT_FATAL(ret == 0); + + CU_ASSERT_FATAL(pkt_seq[i] != TEST_SEQ_INVALID); + ret = odp_pktout_send(pktout, &pkt_tbl[i], TX_BATCH_LEN - i); + CU_ASSERT_FATAL(ret == (TX_BATCH_LEN - i)); + + i = wait_for_packets(&info_rx, &pkt_tbl[i], &pkt_seq[i], ret, + TXRX_MODE_MULTI, ODP_TIME_SEC_IN_NS); + CU_ASSERT(i == ret); + } else { + CU_FAIL("failed to generate test packets\n"); + } + + for (i = 0; i < alloc_pkts; ++i) { + if (pkt_tbl[i] != ODP_PACKET_INVALID) + odp_packet_free(pkt_tbl[i]); + } + + if (pktio_rx != pktio_tx) { + CU_ASSERT(odp_pktio_stop(pktio_rx) == 0); + CU_ASSERT(odp_pktio_close(pktio_rx) == 0); + } + CU_ASSERT(odp_pktio_stop(pktio_tx) == 0); + CU_ASSERT(odp_pktio_close(pktio_tx) == 0); + CU_ASSERT(odp_pool_destroy(pkt_pool) == 0); +} + +void pktio_test_recv_on_wonly(void) +{ + odp_pktio_t pktio; + int ret; + odp_pktin_queue_t pktin; + + pktio = create_pktio(0, ODP_PKTIN_MODE_DISABLED, + ODP_PKTOUT_MODE_DIRECT); + + if (pktio == ODP_PKTIO_INVALID) { + CU_FAIL("failed to open pktio"); + return; + } + + CU_ASSERT(odp_pktin_queue(pktio, &pktin, 1) == 0); + + ret = odp_pktio_start(pktio); + CU_ASSERT_FATAL(ret == 0); + + _pktio_wait_linkup(pktio); + + ret = odp_pktio_stop(pktio); + CU_ASSERT_FATAL(ret == 0); + + ret = odp_pktio_close(pktio); + CU_ASSERT_FATAL(ret == 0); +} + +void pktio_test_send_on_ronly(void) +{ + odp_pktio_t pktio; + int ret; + odp_pktout_queue_t pktout; + + pktio = create_pktio(0, ODP_PKTIN_MODE_DIRECT, + ODP_PKTOUT_MODE_DISABLED); + + if (pktio == ODP_PKTIO_INVALID) { + CU_FAIL("failed to open pktio"); + return; + } + + CU_ASSERT(odp_pktout_queue(pktio, &pktout, 1) == 0); + + ret = odp_pktio_start(pktio); + CU_ASSERT_FATAL(ret == 0); + + _pktio_wait_linkup(pktio); + + ret = odp_pktio_stop(pktio); + CU_ASSERT_FATAL(ret == 0); + + ret = odp_pktio_close(pktio); + CU_ASSERT_FATAL(ret == 0); +} + +int pktio_check_pktin_ts(void) +{ + odp_pktio_t pktio; + odp_pktio_capability_t capa; + odp_pktio_param_t pktio_param; + int ret; + + odp_pktio_param_init(&pktio_param); + pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT; + + pktio = odp_pktio_open(iface_name[0], pool[0], &pktio_param); + if (pktio == ODP_PKTIO_INVALID) + return ODP_TEST_INACTIVE; + + ret = odp_pktio_capability(pktio, &capa); + (void)odp_pktio_close(pktio); + + if (ret < 0 || !capa.config.pktin.bit.ts_all) + return ODP_TEST_INACTIVE; + + return ODP_TEST_ACTIVE; +} + +void pktio_test_pktin_ts(void) +{ + odp_pktio_t pktio_tx, pktio_rx; + odp_pktio_t pktio[MAX_NUM_IFACES]; + pktio_info_t pktio_rx_info; + odp_pktio_capability_t capa; + odp_pktio_config_t config; + odp_pktout_queue_t pktout_queue; + odp_packet_t pkt_tbl[TX_BATCH_LEN]; + uint32_t pkt_seq[TX_BATCH_LEN]; + uint64_t ns1, ns2; + uint64_t res; + odp_time_t ts_prev; + odp_time_t ts; + int num_rx = 0; + int ret; + int i; + + CU_ASSERT_FATAL(num_ifaces >= 1); + + /* Open and configure interfaces */ + for (i = 0; i < num_ifaces; ++i) { + pktio[i] = create_pktio(i, ODP_PKTIN_MODE_DIRECT, + ODP_PKTOUT_MODE_DIRECT); + CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID); + + CU_ASSERT_FATAL(odp_pktio_capability(pktio[i], &capa) == 0); + CU_ASSERT_FATAL(capa.config.pktin.bit.ts_all); + + odp_pktio_config_init(&config); + config.pktin.bit.ts_all = 1; + CU_ASSERT_FATAL(odp_pktio_config(pktio[i], &config) == 0); + + CU_ASSERT_FATAL(odp_pktio_start(pktio[i]) == 0); + } + + for (i = 0; i < num_ifaces; i++) + _pktio_wait_linkup(pktio[i]); + + pktio_tx = pktio[0]; + pktio_rx = (num_ifaces > 1) ? pktio[1] : pktio_tx; + pktio_rx_info.id = pktio_rx; + pktio_rx_info.inq = ODP_QUEUE_INVALID; + pktio_rx_info.in_mode = ODP_PKTIN_MODE_DIRECT; + + /* Test odp_pktin_ts_res() and odp_pktin_ts_from_ns() */ + res = odp_pktin_ts_res(pktio_tx); + CU_ASSERT(res > PKTIN_TS_MIN_RES); + CU_ASSERT(res < PKTIN_TS_MAX_RES); + ns1 = 100; + ts = odp_pktin_ts_from_ns(pktio_tx, ns1); + ns2 = odp_time_to_ns(ts); + /* Allow some arithmetic tolerance */ + CU_ASSERT((ns2 <= (ns1 + PKTIN_TS_CMP_RES)) && + (ns2 >= (ns1 - PKTIN_TS_CMP_RES))); + + ret = create_packets(pkt_tbl, pkt_seq, TX_BATCH_LEN, pktio_tx, + pktio_rx); + CU_ASSERT_FATAL(ret == TX_BATCH_LEN); + + ret = odp_pktout_queue(pktio_tx, &pktout_queue, 1); + CU_ASSERT_FATAL(ret > 0); + + /* Send packets one at a time and add delay between the packets */ + for (i = 0; i < TX_BATCH_LEN; i++) { + CU_ASSERT_FATAL(odp_pktout_send(pktout_queue, + &pkt_tbl[i], 1) == 1); + ret = wait_for_packets(&pktio_rx_info, &pkt_tbl[i], &pkt_seq[i], + 1, TXRX_MODE_SINGLE, ODP_TIME_SEC_IN_NS); + if (ret != 1) + break; + odp_time_wait_ns(PKTIN_TS_INTERVAL); + } + num_rx = i; + CU_ASSERT(num_rx == TX_BATCH_LEN); + + ts_prev = ODP_TIME_NULL; + for (i = 0; i < num_rx; i++) { + ts = odp_packet_ts(pkt_tbl[i]); + + CU_ASSERT(odp_time_cmp(ts, ts_prev) > 0); + + ts_prev = ts; + odp_packet_free(pkt_tbl[i]); + } + + for (i = 0; i < num_ifaces; i++) { + CU_ASSERT_FATAL(odp_pktio_stop(pktio[i]) == 0); + CU_ASSERT_FATAL(odp_pktio_close(pktio[i]) == 0); + } +} + +static int create_pool(const char *iface, int num) +{ + char pool_name[ODP_POOL_NAME_LEN]; + odp_pool_param_t params; + + memset(¶ms, 0, sizeof(params)); + set_pool_len(¶ms); + params.pkt.num = PKT_BUF_NUM; + params.type = ODP_POOL_PACKET; + + snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s_%d", + iface, pool_segmentation); + + pool[num] = odp_pool_create(pool_name, ¶ms); + if (ODP_POOL_INVALID == pool[num]) { + fprintf(stderr, "%s: failed to create pool: %d", + __func__, odp_errno()); + return -1; + } + + return 0; +} + +static int pktio_suite_init(void) +{ + int i; + + odp_atomic_init_u32(&ip_seq, 0); + + if (getenv("ODP_WAIT_FOR_NETWORK")) + wait_for_network = true; + + iface_name[0] = getenv("ODP_PKTIO_IF0"); + iface_name[1] = getenv("ODP_PKTIO_IF1"); + num_ifaces = 1; + + if (!iface_name[0]) { + printf("No interfaces specified, using default \"loop\".\n"); + iface_name[0] = "loop"; + } else if (!iface_name[1]) { + printf("Using loopback interface: %s\n", iface_name[0]); + } else { + num_ifaces = 2; + printf("Using paired interfaces: %s %s\n", + iface_name[0], iface_name[1]); + } + + for (i = 0; i < num_ifaces; i++) { + if (create_pool(iface_name[i], i) != 0) + return -1; + } + + if (default_pool_create() != 0) { + fprintf(stderr, "error: failed to create default pool\n"); + return -1; + } + + return 0; +} + +int pktio_suite_init_unsegmented(void) +{ + pool_segmentation = PKT_POOL_UNSEGMENTED; + return pktio_suite_init(); +} + +int pktio_suite_init_segmented(void) +{ + pool_segmentation = PKT_POOL_SEGMENTED; + return pktio_suite_init(); +} + +int pktio_suite_term(void) +{ + char pool_name[ODP_POOL_NAME_LEN]; + odp_pool_t pool; + int i; + int ret = 0; + + for (i = 0; i < num_ifaces; ++i) { + snprintf(pool_name, sizeof(pool_name), + "pkt_pool_%s_%d", iface_name[i], pool_segmentation); + pool = odp_pool_lookup(pool_name); + if (pool == ODP_POOL_INVALID) + continue; + + if (odp_pool_destroy(pool) != 0) { + fprintf(stderr, "error: failed to destroy pool %s\n", + pool_name); + ret = -1; + } + } + + if (odp_pool_destroy(default_pkt_pool) != 0) { + fprintf(stderr, "error: failed to destroy default pool\n"); + ret = -1; + } + default_pkt_pool = ODP_POOL_INVALID; + + return ret; +} + +odp_testinfo_t pktio_suite_unsegmented[] = { + ODP_TEST_INFO(pktio_test_open), + ODP_TEST_INFO(pktio_test_lookup), + ODP_TEST_INFO(pktio_test_index), + ODP_TEST_INFO(pktio_test_print), + ODP_TEST_INFO(pktio_test_pktio_config), + ODP_TEST_INFO(pktio_test_info), + ODP_TEST_INFO(pktio_test_pktin_queue_config_direct), + ODP_TEST_INFO(pktio_test_pktin_queue_config_sched), + ODP_TEST_INFO(pktio_test_pktin_queue_config_queue), + ODP_TEST_INFO(pktio_test_pktout_queue_config), + ODP_TEST_INFO(pktio_test_plain_queue), + ODP_TEST_INFO(pktio_test_plain_multi), + ODP_TEST_INFO(pktio_test_sched_queue), + ODP_TEST_INFO(pktio_test_sched_multi), + ODP_TEST_INFO(pktio_test_recv), + ODP_TEST_INFO(pktio_test_recv_multi), + ODP_TEST_INFO(pktio_test_recv_queue), + ODP_TEST_INFO(pktio_test_recv_tmo), + ODP_TEST_INFO(pktio_test_recv_mq_tmo), + ODP_TEST_INFO(pktio_test_recv_mtu), + ODP_TEST_INFO_CONDITIONAL(pktio_test_send_failure, + pktio_check_send_failure), + ODP_TEST_INFO(pktio_test_mtu), + ODP_TEST_INFO(pktio_test_promisc), + ODP_TEST_INFO(pktio_test_mac), + ODP_TEST_INFO(pktio_test_start_stop), + ODP_TEST_INFO(pktio_test_recv_on_wonly), + ODP_TEST_INFO(pktio_test_send_on_ronly), + ODP_TEST_INFO(pktio_test_plain_multi_event), + ODP_TEST_INFO(pktio_test_sched_multi_event), + ODP_TEST_INFO(pktio_test_recv_multi_event), + ODP_TEST_INFO_CONDITIONAL(pktio_test_statistics_counters, + pktio_check_statistics_counters), + ODP_TEST_INFO_CONDITIONAL(pktio_test_pktin_ts, + pktio_check_pktin_ts), + ODP_TEST_INFO_NULL +}; + +odp_testinfo_t pktio_suite_segmented[] = { + ODP_TEST_INFO(pktio_test_plain_queue), + ODP_TEST_INFO(pktio_test_plain_multi), + ODP_TEST_INFO(pktio_test_sched_queue), + ODP_TEST_INFO(pktio_test_sched_multi), + ODP_TEST_INFO(pktio_test_recv), + ODP_TEST_INFO(pktio_test_recv_multi), + ODP_TEST_INFO(pktio_test_recv_mtu), + ODP_TEST_INFO_CONDITIONAL(pktio_test_send_failure, + pktio_check_send_failure), + ODP_TEST_INFO_NULL +}; + +odp_suiteinfo_t pktio_suites[] = { + {"Packet I/O Unsegmented", pktio_suite_init_unsegmented, + pktio_suite_term, pktio_suite_unsegmented}, + {"Packet I/O Segmented", pktio_suite_init_segmented, + pktio_suite_term, pktio_suite_segmented}, + ODP_SUITE_INFO_NULL +}; + +int pktio_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(pktio_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/pktio/pktio.h b/test/common_plat/validation/api/pktio/pktio.h new file mode 100644 index 000000000..8131d05fe --- /dev/null +++ b/test/common_plat/validation/api/pktio/pktio.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_PKTIO_H_ +#define _ODP_TEST_PKTIO_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void pktio_test_plain_queue(void); +void pktio_test_plain_multi(void); +void pktio_test_sched_queue(void); +void pktio_test_sched_multi(void); +void pktio_test_recv(void); +void pktio_test_recv_multi(void); +void pktio_test_recv_queue(void); +void pktio_test_recv_tmo(void); +void pktio_test_recv_mq_tmo(void); +void pktio_test_recv_mtu(void); +void pktio_test_mtu(void); +void pktio_test_promisc(void); +void pktio_test_mac(void); +void pktio_test_inq_remdef(void); +void pktio_test_open(void); +void pktio_test_lookup(void); +void pktio_test_index(void); +void pktio_test_info(void); +void pktio_test_inq(void); +void pktio_test_pktio_config(void); +void pktio_test_pktin_queue_config_direct(void); +void pktio_test_pktin_queue_config_sched(void); +void pktio_test_pktin_queue_config_queue(void); +void pktio_test_pktout_queue_config(void); +void pktio_test_start_stop(void); +int pktio_check_send_failure(void); +void pktio_test_send_failure(void); +void pktio_test_recv_on_wonly(void); +void pktio_test_send_on_ronly(void); +void pktio_test_plain_multi_event(void); +void pktio_test_sched_multi_event(void); +void pktio_test_recv_multi_event(void); +int pktio_check_statistics_counters(void); +void pktio_test_statistics_counters(void); +int pktio_check_pktin_ts(void); +void pktio_test_pktin_ts(void); + +/* test arrays: */ +extern odp_testinfo_t pktio_suite[]; + +/* test array init/term functions: */ +int pktio_suite_term(void); +int pktio_suite_init_segmented(void); +int pktio_suite_init_unsegmented(void); + +/* test registry: */ +extern odp_suiteinfo_t pktio_suites[]; + +/* main test program: */ +int pktio_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/pktio/pktio_main.c b/test/common_plat/validation/api/pktio/pktio_main.c new file mode 100644 index 000000000..2928e1b8a --- /dev/null +++ b/test/common_plat/validation/api/pktio/pktio_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pktio.h" + +int main(int argc, char *argv[]) +{ + return pktio_main(argc, argv); +} diff --git a/test/common_plat/validation/api/pool/.gitignore b/test/common_plat/validation/api/pool/.gitignore new file mode 100644 index 000000000..fc91b28d6 --- /dev/null +++ b/test/common_plat/validation/api/pool/.gitignore @@ -0,0 +1 @@ +pool_main diff --git a/test/common_plat/validation/api/pool/Makefile.am b/test/common_plat/validation/api/pool/Makefile.am new file mode 100644 index 000000000..1eb8d714b --- /dev/null +++ b/test/common_plat/validation/api/pool/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestpool.la +libtestpool_la_SOURCES = pool.c + +test_PROGRAMS = pool_main$(EXEEXT) +dist_pool_main_SOURCES = pool_main.c +pool_main_LDADD = libtestpool.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = pool.h diff --git a/test/common_plat/validation/api/pool/pool.c b/test/common_plat/validation/api/pool/pool.c new file mode 100644 index 000000000..d48ac2a34 --- /dev/null +++ b/test/common_plat/validation/api/pool/pool.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include "odp_cunit_common.h" +#include "pool.h" + +static int pool_name_number = 1; +static const int default_buffer_size = 1500; +static const int default_buffer_num = 1000; + +static void pool_create_destroy(odp_pool_param_t *params) +{ + odp_pool_t pool; + char pool_name[ODP_POOL_NAME_LEN]; + + snprintf(pool_name, sizeof(pool_name), + "test_pool-%d", pool_name_number++); + + pool = odp_pool_create(pool_name, params); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + CU_ASSERT(odp_pool_to_u64(pool) != + odp_pool_to_u64(ODP_POOL_INVALID)); + CU_ASSERT(odp_pool_destroy(pool) == 0); +} + +void pool_test_create_destroy_buffer(void) +{ + odp_pool_param_t params = { + .buf = { + .size = default_buffer_size, + .align = ODP_CACHE_LINE_SIZE, + .num = default_buffer_num, + }, + .type = ODP_POOL_BUFFER, + }; + + pool_create_destroy(¶ms); +} + +void pool_test_create_destroy_packet(void) +{ + odp_pool_param_t params = { + .pkt = { + .seg_len = 0, + .len = default_buffer_size, + .num = default_buffer_num, + }, + .type = ODP_POOL_PACKET, + }; + + pool_create_destroy(¶ms); +} + +void pool_test_create_destroy_timeout(void) +{ + odp_pool_param_t params = { + .tmo = { + .num = default_buffer_num, + }, + .type = ODP_POOL_TIMEOUT, + }; + + pool_create_destroy(¶ms); +} + +void pool_test_lookup_info_print(void) +{ + odp_pool_t pool; + const char pool_name[] = "pool_for_lookup_test"; + odp_pool_info_t info; + odp_pool_param_t params = { + .buf = { + .size = default_buffer_size, + .align = ODP_CACHE_LINE_SIZE, + .num = default_buffer_num, + }, + .type = ODP_POOL_BUFFER, + }; + + pool = odp_pool_create(pool_name, ¶ms); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + pool = odp_pool_lookup(pool_name); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + CU_ASSERT_FATAL(odp_pool_info(pool, &info) == 0); + CU_ASSERT(strncmp(pool_name, info.name, sizeof(pool_name)) == 0); + CU_ASSERT(params.buf.size <= info.params.buf.size); + CU_ASSERT(params.buf.align <= info.params.buf.align); + CU_ASSERT(params.buf.num <= info.params.buf.num); + CU_ASSERT(params.type == info.params.type); + + odp_pool_print(pool); + + CU_ASSERT(odp_pool_destroy(pool) == 0); +} + +odp_testinfo_t pool_suite[] = { + ODP_TEST_INFO(pool_test_create_destroy_buffer), + ODP_TEST_INFO(pool_test_create_destroy_packet), + ODP_TEST_INFO(pool_test_create_destroy_timeout), + ODP_TEST_INFO(pool_test_lookup_info_print), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t pool_suites[] = { + { .pName = "Pool tests", + .pTests = pool_suite, + }, + ODP_SUITE_INFO_NULL, +}; + +int pool_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(pool_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/pool/pool.h b/test/common_plat/validation/api/pool/pool.h new file mode 100644 index 000000000..29e517633 --- /dev/null +++ b/test/common_plat/validation/api/pool/pool.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_POOL_H_ +#define _ODP_TEST_POOL_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void pool_test_create_destroy_buffer(void); +void pool_test_create_destroy_packet(void); +void pool_test_create_destroy_timeout(void); +void pool_test_create_destroy_buffer_shm(void); +void pool_test_lookup_info_print(void); + +/* test arrays: */ +extern odp_testinfo_t pool_suite[]; + +/* test registry: */ +extern odp_suiteinfo_t pool_suites[]; + +/* main test program: */ +int pool_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/pool/pool_main.c b/test/common_plat/validation/api/pool/pool_main.c new file mode 100644 index 000000000..bf06585b5 --- /dev/null +++ b/test/common_plat/validation/api/pool/pool_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pool.h" + +int main(int argc, char *argv[]) +{ + return pool_main(argc, argv); +} diff --git a/test/common_plat/validation/api/queue/.gitignore b/test/common_plat/validation/api/queue/.gitignore new file mode 100644 index 000000000..469506a13 --- /dev/null +++ b/test/common_plat/validation/api/queue/.gitignore @@ -0,0 +1 @@ +queue_main diff --git a/test/common_plat/validation/api/queue/Makefile.am b/test/common_plat/validation/api/queue/Makefile.am new file mode 100644 index 000000000..a477e3c56 --- /dev/null +++ b/test/common_plat/validation/api/queue/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestqueue.la +libtestqueue_la_SOURCES = queue.c + +test_PROGRAMS = queue_main$(EXEEXT) +dist_queue_main_SOURCES = queue_main.c +queue_main_LDADD = libtestqueue.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = queue.h diff --git a/test/common_plat/validation/api/queue/queue.c b/test/common_plat/validation/api/queue/queue.c new file mode 100644 index 000000000..dc3a977cb --- /dev/null +++ b/test/common_plat/validation/api/queue/queue.c @@ -0,0 +1,321 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include <odp_cunit_common.h> +#include "queue.h" + +#define MAX_BUFFER_QUEUE (8) +#define MSG_POOL_SIZE (4 * 1024 * 1024) +#define CONFIG_MAX_ITERATION (100) +#define MAX_QUEUES (64 * 1024) + +static int queue_context = 0xff; +static odp_pool_t pool; + +static void generate_name(char *name, uint32_t index) +{ + /* Uniqueue name for up to 300M queues */ + name[0] = 'A' + ((index / (26 * 26 * 26 * 26 * 26)) % 26); + name[1] = 'A' + ((index / (26 * 26 * 26 * 26)) % 26); + name[2] = 'A' + ((index / (26 * 26 * 26)) % 26); + name[3] = 'A' + ((index / (26 * 26)) % 26); + name[4] = 'A' + ((index / 26) % 26); + name[5] = 'A' + (index % 26); +} + +int queue_suite_init(void) +{ + odp_pool_param_t params; + + params.buf.size = 0; + params.buf.align = ODP_CACHE_LINE_SIZE; + params.buf.num = 1024 * 10; + params.type = ODP_POOL_BUFFER; + + pool = odp_pool_create("msg_pool", ¶ms); + + if (ODP_POOL_INVALID == pool) { + printf("Pool create failed.\n"); + return -1; + } + return 0; +} + +int queue_suite_term(void) +{ + return odp_pool_destroy(pool); +} + +void queue_test_capa(void) +{ + odp_queue_capability_t capa; + odp_queue_param_t qparams; + char name[ODP_QUEUE_NAME_LEN]; + odp_queue_t queue[MAX_QUEUES]; + uint32_t num_queues, i; + + memset(&capa, 0, sizeof(odp_queue_capability_t)); + CU_ASSERT(odp_queue_capability(&capa) == 0); + + CU_ASSERT(capa.max_queues != 0); + CU_ASSERT(capa.max_ordered_locks != 0); + CU_ASSERT(capa.max_sched_groups != 0); + CU_ASSERT(capa.sched_prios != 0); + + for (i = 0; i < ODP_QUEUE_NAME_LEN; i++) + name[i] = 'A' + (i % 26); + + name[ODP_QUEUE_NAME_LEN - 1] = 0; + + if (capa.max_queues > MAX_QUEUES) + num_queues = MAX_QUEUES; + else + num_queues = capa.max_queues; + + odp_queue_param_init(&qparams); + + for (i = 0; i < num_queues; i++) { + generate_name(name, i); + queue[i] = odp_queue_create(name, &qparams); + + if (queue[i] == ODP_QUEUE_INVALID) { + CU_FAIL("Queue create failed"); + num_queues = i; + break; + } + + CU_ASSERT(odp_queue_lookup(name) != ODP_QUEUE_INVALID); + } + + for (i = 0; i < num_queues; i++) + CU_ASSERT(odp_queue_destroy(queue[i]) == 0); +} + +void queue_test_mode(void) +{ + odp_queue_param_t qparams; + odp_queue_t queue; + int i, j; + odp_queue_op_mode_t mode[3] = { ODP_QUEUE_OP_MT, + ODP_QUEUE_OP_MT_UNSAFE, + ODP_QUEUE_OP_DISABLED }; + + odp_queue_param_init(&qparams); + + /* Plain queue modes */ + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + /* Should not disable both enq and deq */ + if (i == 2 && j == 2) + break; + + qparams.enq_mode = mode[i]; + qparams.deq_mode = mode[j]; + queue = odp_queue_create("test_queue", &qparams); + CU_ASSERT(queue != ODP_QUEUE_INVALID); + if (queue != ODP_QUEUE_INVALID) + CU_ASSERT(odp_queue_destroy(queue) == 0); + } + } + + odp_queue_param_init(&qparams); + qparams.type = ODP_QUEUE_TYPE_SCHED; + + /* Scheduled queue modes. Dequeue mode is fixed. */ + for (i = 0; i < 3; i++) { + qparams.enq_mode = mode[i]; + queue = odp_queue_create("test_queue", &qparams); + CU_ASSERT(queue != ODP_QUEUE_INVALID); + if (queue != ODP_QUEUE_INVALID) + CU_ASSERT(odp_queue_destroy(queue) == 0); + } +} + +void queue_test_param(void) +{ + odp_queue_t queue; + odp_event_t enev[MAX_BUFFER_QUEUE]; + odp_event_t deev[MAX_BUFFER_QUEUE]; + odp_buffer_t buf; + odp_event_t ev; + odp_pool_t msg_pool; + odp_event_t *pev_tmp; + int i, deq_ret, ret; + int nr_deq_entries = 0; + int max_iteration = CONFIG_MAX_ITERATION; + odp_queue_param_t qparams; + odp_buffer_t enbuf; + + /* Schedule type queue */ + odp_queue_param_init(&qparams); + qparams.type = ODP_QUEUE_TYPE_SCHED; + qparams.sched.prio = ODP_SCHED_PRIO_LOWEST; + qparams.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qparams.sched.group = ODP_SCHED_GROUP_WORKER; + + queue = odp_queue_create("test_queue", &qparams); + CU_ASSERT(ODP_QUEUE_INVALID != queue); + CU_ASSERT(odp_queue_to_u64(queue) != + odp_queue_to_u64(ODP_QUEUE_INVALID)); + CU_ASSERT(queue == odp_queue_lookup("test_queue")); + CU_ASSERT(ODP_QUEUE_TYPE_SCHED == odp_queue_type(queue)); + CU_ASSERT(ODP_SCHED_PRIO_LOWEST == odp_queue_sched_prio(queue)); + CU_ASSERT(ODP_SCHED_SYNC_PARALLEL == odp_queue_sched_type(queue)); + CU_ASSERT(ODP_SCHED_GROUP_WORKER == odp_queue_sched_group(queue)); + + CU_ASSERT(0 == odp_queue_context_set(queue, &queue_context, + sizeof(queue_context))); + + CU_ASSERT(&queue_context == odp_queue_context(queue)); + CU_ASSERT(odp_queue_destroy(queue) == 0); + + /* Plain type queue */ + odp_queue_param_init(&qparams); + qparams.type = ODP_QUEUE_TYPE_PLAIN; + qparams.context = &queue_context; + qparams.context_len = sizeof(queue_context); + + queue = odp_queue_create("test_queue", &qparams); + CU_ASSERT(ODP_QUEUE_INVALID != queue); + CU_ASSERT(queue == odp_queue_lookup("test_queue")); + CU_ASSERT(ODP_QUEUE_TYPE_PLAIN == odp_queue_type(queue)); + CU_ASSERT(&queue_context == odp_queue_context(queue)); + + msg_pool = odp_pool_lookup("msg_pool"); + buf = odp_buffer_alloc(msg_pool); + CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID); + ev = odp_buffer_to_event(buf); + + if (!(CU_ASSERT(odp_queue_enq(queue, ev) == 0))) { + odp_buffer_free(buf); + } else { + CU_ASSERT(ev == odp_queue_deq(queue)); + odp_buffer_free(buf); + } + + for (i = 0; i < MAX_BUFFER_QUEUE; i++) { + buf = odp_buffer_alloc(msg_pool); + enev[i] = odp_buffer_to_event(buf); + } + + /* + * odp_queue_enq_multi may return 0..n buffers due to the resource + * constraints in the implementation at that given point of time. + * But here we assume that we succeed in enqueuing all buffers. + */ + ret = odp_queue_enq_multi(queue, enev, MAX_BUFFER_QUEUE); + CU_ASSERT(MAX_BUFFER_QUEUE == ret); + i = ret < 0 ? 0 : ret; + for ( ; i < MAX_BUFFER_QUEUE; i++) + odp_event_free(enev[i]); + + pev_tmp = deev; + do { + deq_ret = odp_queue_deq_multi(queue, pev_tmp, + MAX_BUFFER_QUEUE); + nr_deq_entries += deq_ret; + max_iteration--; + pev_tmp += deq_ret; + CU_ASSERT(max_iteration >= 0); + } while (nr_deq_entries < MAX_BUFFER_QUEUE); + + for (i = 0; i < MAX_BUFFER_QUEUE; i++) { + enbuf = odp_buffer_from_event(enev[i]); + CU_ASSERT(enev[i] == deev[i]); + odp_buffer_free(enbuf); + } + + CU_ASSERT(odp_queue_destroy(queue) == 0); +} + +void queue_test_info(void) +{ + odp_queue_t q_plain, q_order; + const char *const nq_plain = "test_q_plain"; + const char *const nq_order = "test_q_order"; + odp_queue_info_t info; + odp_queue_param_t param; + char q_plain_ctx[] = "test_q_plain context data"; + char q_order_ctx[] = "test_q_order context data"; + unsigned lock_count; + char *ctx; + int ret; + + /* Create a plain queue and set context */ + q_plain = odp_queue_create(nq_plain, NULL); + CU_ASSERT(ODP_QUEUE_INVALID != q_plain); + CU_ASSERT(odp_queue_context_set(q_plain, q_plain_ctx, + sizeof(q_plain_ctx)) == 0); + + /* Create a scheduled ordered queue with explicitly set params */ + odp_queue_param_init(¶m); + param.type = ODP_QUEUE_TYPE_SCHED; + param.sched.prio = ODP_SCHED_PRIO_NORMAL; + param.sched.sync = ODP_SCHED_SYNC_ORDERED; + param.sched.group = ODP_SCHED_GROUP_ALL; + param.sched.lock_count = 1; + param.context = q_order_ctx; + q_order = odp_queue_create(nq_order, ¶m); + CU_ASSERT(ODP_QUEUE_INVALID != q_order); + + /* Check info for the plain queue */ + CU_ASSERT(odp_queue_info(q_plain, &info) == 0); + CU_ASSERT(strcmp(nq_plain, info.name) == 0); + CU_ASSERT(info.param.type == ODP_QUEUE_TYPE_PLAIN); + CU_ASSERT(info.param.type == odp_queue_type(q_plain)); + ctx = info.param.context; /* 'char' context ptr */ + CU_ASSERT(ctx == q_plain_ctx); + CU_ASSERT(info.param.context == odp_queue_context(q_plain)); + + /* Check info for the scheduled ordered queue */ + CU_ASSERT(odp_queue_info(q_order, &info) == 0); + CU_ASSERT(strcmp(nq_order, info.name) == 0); + CU_ASSERT(info.param.type == ODP_QUEUE_TYPE_SCHED); + CU_ASSERT(info.param.type == odp_queue_type(q_order)); + ctx = info.param.context; /* 'char' context ptr */ + CU_ASSERT(ctx == q_order_ctx); + CU_ASSERT(info.param.context == odp_queue_context(q_order)); + CU_ASSERT(info.param.sched.prio == odp_queue_sched_prio(q_order)); + CU_ASSERT(info.param.sched.sync == odp_queue_sched_type(q_order)); + CU_ASSERT(info.param.sched.group == odp_queue_sched_group(q_order)); + ret = odp_queue_lock_count(q_order); + CU_ASSERT(ret >= 0); + lock_count = (unsigned)ret; + CU_ASSERT(info.param.sched.lock_count == lock_count); + + CU_ASSERT(odp_queue_destroy(q_plain) == 0); + CU_ASSERT(odp_queue_destroy(q_order) == 0); +} + +odp_testinfo_t queue_suite[] = { + ODP_TEST_INFO(queue_test_capa), + ODP_TEST_INFO(queue_test_mode), + ODP_TEST_INFO(queue_test_param), + ODP_TEST_INFO(queue_test_info), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t queue_suites[] = { + {"Queue", queue_suite_init, queue_suite_term, queue_suite}, + ODP_SUITE_INFO_NULL, +}; + +int queue_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(queue_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/queue/queue.h b/test/common_plat/validation/api/queue/queue.h new file mode 100644 index 000000000..6b787b1d6 --- /dev/null +++ b/test/common_plat/validation/api/queue/queue.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_QUEUE_H_ +#define _ODP_TEST_QUEUE_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void queue_test_capa(void); +void queue_test_mode(void); +void queue_test_param(void); +void queue_test_info(void); + +/* test arrays: */ +extern odp_testinfo_t queue_suite[]; + +/* test array init/term functions: */ +int queue_suite_init(void); +int queue_suite_term(void); + +/* test registry: */ +extern odp_suiteinfo_t queue_suites[]; + +/* main test program: */ +int queue_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/queue/queue_main.c b/test/common_plat/validation/api/queue/queue_main.c new file mode 100644 index 000000000..b461b860a --- /dev/null +++ b/test/common_plat/validation/api/queue/queue_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "queue.h" + +int main(int argc, char *argv[]) +{ + return queue_main(argc, argv); +} diff --git a/test/common_plat/validation/api/random/.gitignore b/test/common_plat/validation/api/random/.gitignore new file mode 100644 index 000000000..2c88ec0b8 --- /dev/null +++ b/test/common_plat/validation/api/random/.gitignore @@ -0,0 +1 @@ +random_main diff --git a/test/common_plat/validation/api/random/Makefile.am b/test/common_plat/validation/api/random/Makefile.am new file mode 100644 index 000000000..69259a4db --- /dev/null +++ b/test/common_plat/validation/api/random/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestrandom.la +libtestrandom_la_SOURCES = random.c + +test_PROGRAMS = random_main$(EXEEXT) +dist_random_main_SOURCES = random_main.c +random_main_LDADD = libtestrandom.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = random.h diff --git a/test/common_plat/validation/api/random/random.c b/test/common_plat/validation/api/random/random.c new file mode 100644 index 000000000..7572366c2 --- /dev/null +++ b/test/common_plat/validation/api/random/random.c @@ -0,0 +1,44 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include <odp_cunit_common.h> +#include "random.h" + +void random_test_get_size(void) +{ + int32_t ret; + uint8_t buf[32]; + + ret = odp_random_data(buf, sizeof(buf), false); + CU_ASSERT(ret == sizeof(buf)); +} + +odp_testinfo_t random_suite[] = { + ODP_TEST_INFO(random_test_get_size), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t random_suites[] = { + {"Random", NULL, NULL, random_suite}, + ODP_SUITE_INFO_NULL, +}; + +int random_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(random_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/random/random.h b/test/common_plat/validation/api/random/random.h new file mode 100644 index 000000000..26202cc37 --- /dev/null +++ b/test/common_plat/validation/api/random/random.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_RANDOM_H_ +#define _ODP_TEST_RANDOM_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void random_test_get_size(void); + +/* test arrays: */ +extern odp_testinfo_t random_suite[]; + +/* test registry: */ +extern odp_suiteinfo_t random_suites[]; + +/* main test program: */ +int random_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/random/random_main.c b/test/common_plat/validation/api/random/random_main.c new file mode 100644 index 000000000..8f38a84c6 --- /dev/null +++ b/test/common_plat/validation/api/random/random_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "random.h" + +int main(int argc, char *argv[]) +{ + return random_main(argc, argv); +} diff --git a/test/common_plat/validation/api/scheduler/.gitignore b/test/common_plat/validation/api/scheduler/.gitignore new file mode 100644 index 000000000..b4eb30091 --- /dev/null +++ b/test/common_plat/validation/api/scheduler/.gitignore @@ -0,0 +1 @@ +scheduler_main diff --git a/test/common_plat/validation/api/scheduler/Makefile.am b/test/common_plat/validation/api/scheduler/Makefile.am new file mode 100644 index 000000000..2555cab81 --- /dev/null +++ b/test/common_plat/validation/api/scheduler/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestscheduler.la +libtestscheduler_la_SOURCES = scheduler.c + +test_PROGRAMS = scheduler_main$(EXEEXT) +dist_scheduler_main_SOURCES = scheduler_main.c +scheduler_main_LDADD = libtestscheduler.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = scheduler.h diff --git a/test/common_plat/validation/api/scheduler/scheduler.c b/test/common_plat/validation/api/scheduler/scheduler.c new file mode 100644 index 000000000..919cfb6ce --- /dev/null +++ b/test/common_plat/validation/api/scheduler/scheduler.c @@ -0,0 +1,1653 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include "odp_cunit_common.h" +#include "scheduler.h" + +#define MAX_WORKERS_THREADS 32 +#define MAX_ORDERED_LOCKS 2 +#define MSG_POOL_SIZE (64 * 1024) +#define QUEUES_PER_PRIO 16 +#define BUF_SIZE 64 +#define BUFS_PER_QUEUE 100 +#define BUFS_PER_QUEUE_EXCL 10000 +#define BURST_BUF_SIZE 4 +#define NUM_BUFS_PAUSE 1000 +#define NUM_BUFS_BEFORE_PAUSE 10 +#define NUM_GROUPS 2 + +#define GLOBALS_SHM_NAME "test_globals" +#define MSG_POOL_NAME "msg_pool" +#define QUEUE_CTX_POOL_NAME "queue_ctx_pool" +#define SHM_THR_ARGS_NAME "shm_thr_args" + +#define ONE_Q 1 +#define MANY_QS QUEUES_PER_PRIO + +#define ONE_PRIO 1 + +#define SCHD_ONE 0 +#define SCHD_MULTI 1 + +#define DISABLE_EXCL_ATOMIC 0 +#define ENABLE_EXCL_ATOMIC 1 + +#define MAGIC 0xdeadbeef +#define MAGIC1 0xdeadbeef +#define MAGIC2 0xcafef00d + +#define CHAOS_NUM_QUEUES 6 +#define CHAOS_NUM_BUFS_PER_QUEUE 6 +#define CHAOS_NUM_ROUNDS 1000 +#define CHAOS_NUM_EVENTS (CHAOS_NUM_QUEUES * CHAOS_NUM_BUFS_PER_QUEUE) +#define CHAOS_DEBUG (CHAOS_NUM_ROUNDS < 1000) +#define CHAOS_PTR_TO_NDX(p) ((uint64_t)(uint32_t)(uintptr_t)p) +#define CHAOS_NDX_TO_PTR(n) ((void *)(uintptr_t)n) + +#define ODP_WAIT_TOLERANCE (60 * ODP_TIME_MSEC_IN_NS) + +/* Test global variables */ +typedef struct { + int num_workers; + odp_barrier_t barrier; + int buf_count; + int buf_count_cpy; + odp_ticketlock_t lock; + odp_spinlock_t atomic_lock; + struct { + odp_queue_t handle; + char name[ODP_QUEUE_NAME_LEN]; + } chaos_q[CHAOS_NUM_QUEUES]; +} test_globals_t; + +typedef struct { + pthrd_arg cu_thr; + test_globals_t *globals; + odp_schedule_sync_t sync; + int num_queues; + int num_prio; + int num_bufs; + int num_workers; + int enable_schd_multi; + int enable_excl_atomic; +} thread_args_t; + +typedef struct { + uint64_t sequence; + uint64_t lock_sequence[MAX_ORDERED_LOCKS]; + uint64_t output_sequence; +} buf_contents; + +typedef struct { + odp_buffer_t ctx_handle; + odp_queue_t pq_handle; + uint64_t sequence; + uint64_t lock_sequence[MAX_ORDERED_LOCKS]; +} queue_context; + +typedef struct { + uint64_t evno; + uint64_t seqno; +} chaos_buf; + +odp_pool_t pool; +odp_pool_t queue_ctx_pool; + +static int drain_queues(void) +{ + odp_event_t ev; + uint64_t wait = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS); + int ret = 0; + + while ((ev = odp_schedule(NULL, wait)) != ODP_EVENT_INVALID) { + odp_event_free(ev); + ret++; + } + + return ret; +} + +static int exit_schedule_loop(void) +{ + odp_event_t ev; + int ret = 0; + + odp_schedule_pause(); + + while ((ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT)) + != ODP_EVENT_INVALID) { + odp_event_free(ev); + ret++; + } + + odp_schedule_resume(); + + return ret; +} + +void scheduler_test_wait_time(void) +{ + int i; + odp_queue_t queue; + uint64_t wait_time; + odp_queue_param_t qp; + odp_time_t lower_limit, upper_limit; + odp_time_t start_time, end_time, diff; + + /* check on read */ + wait_time = odp_schedule_wait_time(0); + wait_time = odp_schedule_wait_time(1); + + /* check ODP_SCHED_NO_WAIT */ + odp_queue_param_init(&qp); + qp.type = ODP_QUEUE_TYPE_SCHED; + qp.sched.sync = ODP_SCHED_SYNC_PARALLEL; + qp.sched.prio = ODP_SCHED_PRIO_NORMAL; + qp.sched.group = ODP_SCHED_GROUP_ALL; + queue = odp_queue_create("dummy_queue", &qp); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + wait_time = odp_schedule_wait_time(ODP_TIME_SEC_IN_NS); + start_time = odp_time_local(); + odp_schedule(&queue, ODP_SCHED_NO_WAIT); + end_time = odp_time_local(); + + diff = odp_time_diff(end_time, start_time); + lower_limit = ODP_TIME_NULL; + upper_limit = odp_time_local_from_ns(ODP_WAIT_TOLERANCE); + + CU_ASSERT(odp_time_cmp(diff, lower_limit) >= 0); + CU_ASSERT(odp_time_cmp(diff, upper_limit) <= 0); + + /* check time correctness */ + start_time = odp_time_local(); + for (i = 1; i < 6; i++) { + odp_schedule(&queue, wait_time); + printf("%d..", i); + } + end_time = odp_time_local(); + + diff = odp_time_diff(end_time, start_time); + lower_limit = odp_time_local_from_ns(5 * ODP_TIME_SEC_IN_NS - + ODP_WAIT_TOLERANCE); + upper_limit = odp_time_local_from_ns(5 * ODP_TIME_SEC_IN_NS + + ODP_WAIT_TOLERANCE); + + CU_ASSERT(odp_time_cmp(diff, lower_limit) >= 0); + CU_ASSERT(odp_time_cmp(diff, upper_limit) <= 0); + + CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0); +} + +void scheduler_test_num_prio(void) +{ + int prio; + + prio = odp_schedule_num_prio(); + + CU_ASSERT(prio > 0); + CU_ASSERT(prio == odp_schedule_num_prio()); +} + +void scheduler_test_queue_destroy(void) +{ + odp_pool_t p; + odp_pool_param_t params; + odp_queue_param_t qp; + odp_queue_t queue, from; + odp_buffer_t buf; + odp_event_t ev; + uint32_t *u32; + int i; + odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL, + ODP_SCHED_SYNC_ATOMIC, + ODP_SCHED_SYNC_ORDERED}; + + odp_queue_param_init(&qp); + odp_pool_param_init(¶ms); + params.buf.size = 100; + params.buf.align = 0; + params.buf.num = 1; + params.type = ODP_POOL_BUFFER; + + p = odp_pool_create("sched_destroy_pool", ¶ms); + + CU_ASSERT_FATAL(p != ODP_POOL_INVALID); + + for (i = 0; i < 3; i++) { + qp.type = ODP_QUEUE_TYPE_SCHED; + qp.sched.prio = ODP_SCHED_PRIO_DEFAULT; + qp.sched.sync = sync[i]; + qp.sched.group = ODP_SCHED_GROUP_ALL; + + queue = odp_queue_create("sched_destroy_queue", &qp); + + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + buf = odp_buffer_alloc(p); + + CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID); + + u32 = odp_buffer_addr(buf); + u32[0] = MAGIC; + + ev = odp_buffer_to_event(buf); + if (!(CU_ASSERT(odp_queue_enq(queue, ev) == 0))) + odp_buffer_free(buf); + + ev = odp_schedule(&from, ODP_SCHED_WAIT); + + CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID); + + CU_ASSERT_FATAL(from == queue); + + buf = odp_buffer_from_event(ev); + u32 = odp_buffer_addr(buf); + + CU_ASSERT_FATAL(u32[0] == MAGIC); + + odp_buffer_free(buf); + odp_schedule_release_ordered(); + + CU_ASSERT_FATAL(odp_queue_destroy(queue) == 0); + } + + CU_ASSERT_FATAL(odp_pool_destroy(p) == 0); +} + +void scheduler_test_groups(void) +{ + odp_pool_t p; + odp_pool_param_t params; + odp_queue_t queue_grp1, queue_grp2; + odp_buffer_t buf; + odp_event_t ev; + uint32_t *u32; + int i, j, rc; + odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL, + ODP_SCHED_SYNC_ATOMIC, + ODP_SCHED_SYNC_ORDERED}; + int thr_id = odp_thread_id(); + odp_thrmask_t zeromask, mymask, testmask; + odp_schedule_group_t mygrp1, mygrp2, lookup; + odp_schedule_group_info_t info; + + odp_thrmask_zero(&zeromask); + odp_thrmask_zero(&mymask); + odp_thrmask_set(&mymask, thr_id); + + /* Can't find a group before we create it */ + lookup = odp_schedule_group_lookup("Test Group 1"); + CU_ASSERT(lookup == ODP_SCHED_GROUP_INVALID); + + /* Now create the group */ + mygrp1 = odp_schedule_group_create("Test Group 1", &zeromask); + CU_ASSERT_FATAL(mygrp1 != ODP_SCHED_GROUP_INVALID); + + /* Verify we can now find it */ + lookup = odp_schedule_group_lookup("Test Group 1"); + CU_ASSERT(lookup == mygrp1); + + /* Threadmask should be retrievable and be what we expect */ + rc = odp_schedule_group_thrmask(mygrp1, &testmask); + CU_ASSERT(rc == 0); + CU_ASSERT(!odp_thrmask_isset(&testmask, thr_id)); + + /* Now join the group and verify we're part of it */ + rc = odp_schedule_group_join(mygrp1, &mymask); + CU_ASSERT(rc == 0); + + rc = odp_schedule_group_thrmask(mygrp1, &testmask); + CU_ASSERT(rc == 0); + CU_ASSERT(odp_thrmask_isset(&testmask, thr_id)); + + /* Info struct */ + memset(&info, 0, sizeof(odp_schedule_group_info_t)); + rc = odp_schedule_group_info(mygrp1, &info); + CU_ASSERT(rc == 0); + CU_ASSERT(odp_thrmask_equal(&info.thrmask, &mymask) != 0); + CU_ASSERT(strcmp(info.name, "Test Group 1") == 0); + + /* We can't join or leave an unknown group */ + rc = odp_schedule_group_join(ODP_SCHED_GROUP_INVALID, &mymask); + CU_ASSERT(rc != 0); + + rc = odp_schedule_group_leave(ODP_SCHED_GROUP_INVALID, &mymask); + CU_ASSERT(rc != 0); + + /* But we can leave our group */ + rc = odp_schedule_group_leave(mygrp1, &mymask); + CU_ASSERT(rc == 0); + + rc = odp_schedule_group_thrmask(mygrp1, &testmask); + CU_ASSERT(rc == 0); + CU_ASSERT(!odp_thrmask_isset(&testmask, thr_id)); + + /* We shouldn't be able to find our second group before creating it */ + lookup = odp_schedule_group_lookup("Test Group 2"); + CU_ASSERT(lookup == ODP_SCHED_GROUP_INVALID); + + /* Now create it and verify we can find it */ + mygrp2 = odp_schedule_group_create("Test Group 2", &zeromask); + CU_ASSERT_FATAL(mygrp2 != ODP_SCHED_GROUP_INVALID); + + lookup = odp_schedule_group_lookup("Test Group 2"); + CU_ASSERT(lookup == mygrp2); + + /* Verify we're not part of it */ + rc = odp_schedule_group_thrmask(mygrp2, &testmask); + CU_ASSERT(rc == 0); + CU_ASSERT(!odp_thrmask_isset(&testmask, thr_id)); + + /* Now join the group and verify we're part of it */ + rc = odp_schedule_group_join(mygrp2, &mymask); + CU_ASSERT(rc == 0); + + rc = odp_schedule_group_thrmask(mygrp2, &testmask); + CU_ASSERT(rc == 0); + CU_ASSERT(odp_thrmask_isset(&testmask, thr_id)); + + /* Now verify scheduler adherence to groups */ + odp_pool_param_init(¶ms); + params.buf.size = 100; + params.buf.align = 0; + params.buf.num = 2; + params.type = ODP_POOL_BUFFER; + + p = odp_pool_create("sched_group_pool", ¶ms); + + CU_ASSERT_FATAL(p != ODP_POOL_INVALID); + + for (i = 0; i < 3; i++) { + odp_queue_param_t qp; + odp_queue_t queue, from; + odp_schedule_group_t mygrp[NUM_GROUPS]; + odp_queue_t queue_grp[NUM_GROUPS]; + int num = NUM_GROUPS; + + odp_queue_param_init(&qp); + qp.type = ODP_QUEUE_TYPE_SCHED; + qp.sched.prio = ODP_SCHED_PRIO_DEFAULT; + qp.sched.sync = sync[i]; + qp.sched.group = mygrp1; + + /* Create and populate a group in group 1 */ + queue_grp1 = odp_queue_create("sched_group_test_queue_1", &qp); + CU_ASSERT_FATAL(queue_grp1 != ODP_QUEUE_INVALID); + CU_ASSERT_FATAL(odp_queue_sched_group(queue_grp1) == mygrp1); + + buf = odp_buffer_alloc(p); + + CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID); + + u32 = odp_buffer_addr(buf); + u32[0] = MAGIC1; + + ev = odp_buffer_to_event(buf); + rc = odp_queue_enq(queue_grp1, ev); + CU_ASSERT(rc == 0); + if (rc) + odp_buffer_free(buf); + + /* Now create and populate a queue in group 2 */ + qp.sched.group = mygrp2; + queue_grp2 = odp_queue_create("sched_group_test_queue_2", &qp); + CU_ASSERT_FATAL(queue_grp2 != ODP_QUEUE_INVALID); + CU_ASSERT_FATAL(odp_queue_sched_group(queue_grp2) == mygrp2); + + buf = odp_buffer_alloc(p); + CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID); + + u32 = odp_buffer_addr(buf); + u32[0] = MAGIC2; + + ev = odp_buffer_to_event(buf); + rc = odp_queue_enq(queue_grp2, ev); + CU_ASSERT(rc == 0); + if (rc) + odp_buffer_free(buf); + + /* Swap between two groups. Application should serve both + * groups to avoid potential head of line blocking in + * scheduler. */ + mygrp[0] = mygrp1; + mygrp[1] = mygrp2; + queue_grp[0] = queue_grp1; + queue_grp[1] = queue_grp2; + j = 0; + + /* Ensure that each test run starts from mygrp1 */ + odp_schedule_group_leave(mygrp1, &mymask); + odp_schedule_group_leave(mygrp2, &mymask); + odp_schedule_group_join(mygrp1, &mymask); + + while (num) { + queue = queue_grp[j]; + ev = odp_schedule(&from, ODP_SCHED_NO_WAIT); + + if (ev == ODP_EVENT_INVALID) { + /* change group */ + rc = odp_schedule_group_leave(mygrp[j], + &mymask); + CU_ASSERT_FATAL(rc == 0); + + j = (j + 1) % NUM_GROUPS; + rc = odp_schedule_group_join(mygrp[j], + &mymask); + CU_ASSERT_FATAL(rc == 0); + continue; + } + + CU_ASSERT_FATAL(from == queue); + + buf = odp_buffer_from_event(ev); + u32 = odp_buffer_addr(buf); + + if (from == queue_grp1) { + /* CU_ASSERT_FATAL needs these brackets */ + CU_ASSERT_FATAL(u32[0] == MAGIC1); + } else { + CU_ASSERT_FATAL(u32[0] == MAGIC2); + } + + odp_buffer_free(buf); + + /* Tell scheduler we're about to request an event. + * Not needed, but a convenient place to test this API. + */ + odp_schedule_prefetch(1); + + num--; + } + + /* Release schduler context and leave groups */ + odp_schedule_group_join(mygrp1, &mymask); + odp_schedule_group_join(mygrp2, &mymask); + CU_ASSERT(exit_schedule_loop() == 0); + odp_schedule_group_leave(mygrp1, &mymask); + odp_schedule_group_leave(mygrp2, &mymask); + + /* Done with queues for this round */ + CU_ASSERT_FATAL(odp_queue_destroy(queue_grp1) == 0); + CU_ASSERT_FATAL(odp_queue_destroy(queue_grp2) == 0); + + /* Verify we can no longer find our queues */ + CU_ASSERT_FATAL(odp_queue_lookup("sched_group_test_queue_1") == + ODP_QUEUE_INVALID); + CU_ASSERT_FATAL(odp_queue_lookup("sched_group_test_queue_2") == + ODP_QUEUE_INVALID); + } + + CU_ASSERT_FATAL(odp_schedule_group_destroy(mygrp1) == 0); + CU_ASSERT_FATAL(odp_schedule_group_destroy(mygrp2) == 0); + CU_ASSERT_FATAL(odp_pool_destroy(p) == 0); +} + +static int chaos_thread(void *arg) +{ + uint64_t i, wait; + int rc; + chaos_buf *cbuf; + odp_event_t ev; + odp_queue_t from; + thread_args_t *args = (thread_args_t *)arg; + test_globals_t *globals = args->globals; + int me = odp_thread_id(); + odp_time_t start_time, end_time, diff; + + if (CHAOS_DEBUG) + printf("Chaos thread %d starting...\n", me); + + /* Wait for all threads to start */ + odp_barrier_wait(&globals->barrier); + start_time = odp_time_local(); + + /* Run the test */ + wait = odp_schedule_wait_time(5 * ODP_TIME_MSEC_IN_NS); + for (i = 0; i < CHAOS_NUM_ROUNDS; i++) { + ev = odp_schedule(&from, wait); + if (ev == ODP_EVENT_INVALID) + continue; + + cbuf = odp_buffer_addr(odp_buffer_from_event(ev)); + CU_ASSERT_FATAL(cbuf != NULL); + if (CHAOS_DEBUG) + printf("Thread %d received event %" PRIu64 + " seq %" PRIu64 + " from Q %s, sending to Q %s\n", + me, cbuf->evno, cbuf->seqno, + globals-> + chaos_q + [CHAOS_PTR_TO_NDX(odp_queue_context(from))].name, + globals-> + chaos_q[cbuf->seqno % CHAOS_NUM_QUEUES].name); + + rc = odp_queue_enq( + globals-> + chaos_q[cbuf->seqno++ % CHAOS_NUM_QUEUES].handle, + ev); + CU_ASSERT_FATAL(rc == 0); + } + + if (CHAOS_DEBUG) + printf("Thread %d completed %d rounds...terminating\n", + odp_thread_id(), CHAOS_NUM_EVENTS); + + exit_schedule_loop(); + + end_time = odp_time_local(); + diff = odp_time_diff(end_time, start_time); + + printf("Thread %d ends, elapsed time = %" PRIu64 "us\n", + odp_thread_id(), odp_time_to_ns(diff) / 1000); + + return 0; +} + +static void chaos_run(unsigned int qtype) +{ + odp_pool_t pool; + odp_pool_param_t params; + odp_queue_param_t qp; + odp_buffer_t buf; + chaos_buf *cbuf; + test_globals_t *globals; + thread_args_t *args; + odp_shm_t shm; + int i, rc; + odp_schedule_sync_t sync[] = {ODP_SCHED_SYNC_PARALLEL, + ODP_SCHED_SYNC_ATOMIC, + ODP_SCHED_SYNC_ORDERED}; + const unsigned num_sync = (sizeof(sync) / sizeof(odp_schedule_sync_t)); + const char *const qtypes[] = {"parallel", "atomic", "ordered"}; + + /* Set up the scheduling environment */ + shm = odp_shm_lookup(GLOBALS_SHM_NAME); + CU_ASSERT_FATAL(shm != ODP_SHM_INVALID); + globals = odp_shm_addr(shm); + CU_ASSERT_PTR_NOT_NULL_FATAL(globals); + + shm = odp_shm_lookup(SHM_THR_ARGS_NAME); + CU_ASSERT_FATAL(shm != ODP_SHM_INVALID); + args = odp_shm_addr(shm); + CU_ASSERT_PTR_NOT_NULL_FATAL(args); + + args->globals = globals; + args->cu_thr.numthrds = globals->num_workers; + + odp_queue_param_init(&qp); + odp_pool_param_init(¶ms); + params.buf.size = sizeof(chaos_buf); + params.buf.align = 0; + params.buf.num = CHAOS_NUM_EVENTS; + params.type = ODP_POOL_BUFFER; + + pool = odp_pool_create("sched_chaos_pool", ¶ms); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + qp.type = ODP_QUEUE_TYPE_SCHED; + qp.sched.prio = ODP_SCHED_PRIO_DEFAULT; + qp.sched.group = ODP_SCHED_GROUP_ALL; + + for (i = 0; i < CHAOS_NUM_QUEUES; i++) { + uint32_t ndx = (qtype == num_sync ? i % num_sync : qtype); + + qp.sched.sync = sync[ndx]; + snprintf(globals->chaos_q[i].name, + sizeof(globals->chaos_q[i].name), + "chaos queue %d - %s", i, + qtypes[ndx]); + + globals->chaos_q[i].handle = + odp_queue_create(globals->chaos_q[i].name, &qp); + CU_ASSERT_FATAL(globals->chaos_q[i].handle != + ODP_QUEUE_INVALID); + rc = odp_queue_context_set(globals->chaos_q[i].handle, + CHAOS_NDX_TO_PTR(i), 0); + CU_ASSERT_FATAL(rc == 0); + } + + /* Now populate the queues with the initial seed elements */ + for (i = 0; i < CHAOS_NUM_EVENTS; i++) { + buf = odp_buffer_alloc(pool); + CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID); + cbuf = odp_buffer_addr(buf); + cbuf->evno = i; + cbuf->seqno = 0; + rc = odp_queue_enq( + globals->chaos_q[i % CHAOS_NUM_QUEUES].handle, + odp_buffer_to_event(buf)); + CU_ASSERT_FATAL(rc == 0); + } + + /* Run the test */ + odp_cunit_thread_create(chaos_thread, &args->cu_thr); + odp_cunit_thread_exit(&args->cu_thr); + + if (CHAOS_DEBUG) + printf("Thread %d returning from chaos threads..cleaning up\n", + odp_thread_id()); + + drain_queues(); + exit_schedule_loop(); + + for (i = 0; i < CHAOS_NUM_QUEUES; i++) { + if (CHAOS_DEBUG) + printf("Destroying queue %s\n", + globals->chaos_q[i].name); + rc = odp_queue_destroy(globals->chaos_q[i].handle); + CU_ASSERT(rc == 0); + } + + rc = odp_pool_destroy(pool); + CU_ASSERT(rc == 0); +} + +void scheduler_test_parallel(void) +{ + chaos_run(0); +} + +void scheduler_test_atomic(void) +{ + chaos_run(1); +} + +void scheduler_test_ordered(void) +{ + chaos_run(2); +} + +void scheduler_test_chaos(void) +{ + chaos_run(3); +} + +static int schedule_common_(void *arg) +{ + thread_args_t *args = (thread_args_t *)arg; + odp_schedule_sync_t sync; + test_globals_t *globals; + queue_context *qctx; + buf_contents *bctx, *bctx_cpy; + odp_pool_t pool; + int locked; + int num; + odp_event_t ev; + odp_buffer_t buf, buf_cpy; + odp_queue_t from; + + globals = args->globals; + sync = args->sync; + + pool = odp_pool_lookup(MSG_POOL_NAME); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + if (args->num_workers > 1) + odp_barrier_wait(&globals->barrier); + + while (1) { + from = ODP_QUEUE_INVALID; + num = 0; + + odp_ticketlock_lock(&globals->lock); + if (globals->buf_count == 0) { + odp_ticketlock_unlock(&globals->lock); + break; + } + odp_ticketlock_unlock(&globals->lock); + + if (args->enable_schd_multi) { + odp_event_t events[BURST_BUF_SIZE], + ev_cpy[BURST_BUF_SIZE]; + odp_buffer_t buf_cpy[BURST_BUF_SIZE]; + int j; + + num = odp_schedule_multi(&from, ODP_SCHED_NO_WAIT, + events, BURST_BUF_SIZE); + CU_ASSERT(num >= 0); + CU_ASSERT(num <= BURST_BUF_SIZE); + if (num == 0) + continue; + + if (sync == ODP_SCHED_SYNC_ORDERED) { + int ndx; + int ndx_max; + int rc; + + ndx_max = odp_queue_lock_count(from); + CU_ASSERT_FATAL(ndx_max >= 0); + + qctx = odp_queue_context(from); + + for (j = 0; j < num; j++) { + bctx = odp_buffer_addr( + odp_buffer_from_event + (events[j])); + + buf_cpy[j] = odp_buffer_alloc(pool); + CU_ASSERT_FATAL(buf_cpy[j] != + ODP_BUFFER_INVALID); + bctx_cpy = odp_buffer_addr(buf_cpy[j]); + memcpy(bctx_cpy, bctx, + sizeof(buf_contents)); + bctx_cpy->output_sequence = + bctx_cpy->sequence; + ev_cpy[j] = + odp_buffer_to_event(buf_cpy[j]); + } + + rc = odp_queue_enq_multi(qctx->pq_handle, + ev_cpy, num); + CU_ASSERT(rc == num); + + bctx = odp_buffer_addr( + odp_buffer_from_event(events[0])); + for (ndx = 0; ndx < ndx_max; ndx++) { + odp_schedule_order_lock(ndx); + CU_ASSERT(bctx->sequence == + qctx->lock_sequence[ndx]); + qctx->lock_sequence[ndx] += num; + odp_schedule_order_unlock(ndx); + } + } + + for (j = 0; j < num; j++) + odp_event_free(events[j]); + } else { + ev = odp_schedule(&from, ODP_SCHED_NO_WAIT); + if (ev == ODP_EVENT_INVALID) + continue; + + buf = odp_buffer_from_event(ev); + num = 1; + if (sync == ODP_SCHED_SYNC_ORDERED) { + int ndx; + int ndx_max; + int rc; + + ndx_max = odp_queue_lock_count(from); + CU_ASSERT_FATAL(ndx_max >= 0); + + qctx = odp_queue_context(from); + bctx = odp_buffer_addr(buf); + buf_cpy = odp_buffer_alloc(pool); + CU_ASSERT_FATAL(buf_cpy != ODP_BUFFER_INVALID); + bctx_cpy = odp_buffer_addr(buf_cpy); + memcpy(bctx_cpy, bctx, sizeof(buf_contents)); + bctx_cpy->output_sequence = bctx_cpy->sequence; + + rc = odp_queue_enq(qctx->pq_handle, + odp_buffer_to_event + (buf_cpy)); + CU_ASSERT(rc == 0); + + for (ndx = 0; ndx < ndx_max; ndx++) { + odp_schedule_order_lock(ndx); + CU_ASSERT(bctx->sequence == + qctx->lock_sequence[ndx]); + qctx->lock_sequence[ndx] += num; + odp_schedule_order_unlock(ndx); + } + } + + odp_buffer_free(buf); + } + + if (args->enable_excl_atomic) { + locked = odp_spinlock_trylock(&globals->atomic_lock); + CU_ASSERT(locked != 0); + CU_ASSERT(from != ODP_QUEUE_INVALID); + if (locked) { + int cnt; + odp_time_t time = ODP_TIME_NULL; + /* Do some work here to keep the thread busy */ + for (cnt = 0; cnt < 1000; cnt++) + time = odp_time_sum(time, + odp_time_local()); + + odp_spinlock_unlock(&globals->atomic_lock); + } + } + + if (sync == ODP_SCHED_SYNC_ATOMIC) + odp_schedule_release_atomic(); + + if (sync == ODP_SCHED_SYNC_ORDERED) + odp_schedule_release_ordered(); + + odp_ticketlock_lock(&globals->lock); + + globals->buf_count -= num; + + if (globals->buf_count < 0) { + odp_ticketlock_unlock(&globals->lock); + CU_FAIL_FATAL("Buffer counting failed"); + } + + odp_ticketlock_unlock(&globals->lock); + } + + if (args->num_workers > 1) + odp_barrier_wait(&globals->barrier); + + if (sync == ODP_SCHED_SYNC_ORDERED) + locked = odp_ticketlock_trylock(&globals->lock); + else + locked = 0; + + if (locked && globals->buf_count_cpy > 0) { + odp_event_t ev; + odp_queue_t pq; + uint64_t seq; + uint64_t bcount = 0; + int i, j; + char name[32]; + uint64_t num_bufs = args->num_bufs; + uint64_t buf_count = globals->buf_count_cpy; + + for (i = 0; i < args->num_prio; i++) { + for (j = 0; j < args->num_queues; j++) { + snprintf(name, sizeof(name), + "plain_%d_%d_o", i, j); + pq = odp_queue_lookup(name); + CU_ASSERT_FATAL(pq != ODP_QUEUE_INVALID); + + seq = 0; + while (1) { + ev = odp_queue_deq(pq); + + if (ev == ODP_EVENT_INVALID) { + CU_ASSERT(seq == num_bufs); + break; + } + + bctx = odp_buffer_addr( + odp_buffer_from_event(ev)); + + CU_ASSERT(bctx->sequence == seq); + seq++; + bcount++; + odp_event_free(ev); + } + } + } + CU_ASSERT(bcount == buf_count); + globals->buf_count_cpy = 0; + } + + if (locked) + odp_ticketlock_unlock(&globals->lock); + + /* Clear scheduler atomic / ordered context between tests */ + num = exit_schedule_loop(); + + CU_ASSERT(num == 0); + + if (num) + printf("\nDROPPED %i events\n\n", num); + + return 0; +} + +static void fill_queues(thread_args_t *args) +{ + odp_schedule_sync_t sync; + int num_queues, num_prio; + odp_pool_t pool; + int i, j, k; + int buf_count = 0; + test_globals_t *globals; + char name[32]; + int ret; + odp_buffer_t buf; + odp_event_t ev; + + globals = args->globals; + sync = args->sync; + num_queues = args->num_queues; + num_prio = args->num_prio; + + pool = odp_pool_lookup(MSG_POOL_NAME); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + for (i = 0; i < num_prio; i++) { + for (j = 0; j < num_queues; j++) { + odp_queue_t queue; + + switch (sync) { + case ODP_SCHED_SYNC_PARALLEL: + snprintf(name, sizeof(name), + "sched_%d_%d_n", i, j); + break; + case ODP_SCHED_SYNC_ATOMIC: + snprintf(name, sizeof(name), + "sched_%d_%d_a", i, j); + break; + case ODP_SCHED_SYNC_ORDERED: + snprintf(name, sizeof(name), + "sched_%d_%d_o", i, j); + break; + default: + CU_ASSERT_FATAL(0); + break; + } + + queue = odp_queue_lookup(name); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + for (k = 0; k < args->num_bufs; k++) { + buf = odp_buffer_alloc(pool); + CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID); + ev = odp_buffer_to_event(buf); + if (sync == ODP_SCHED_SYNC_ORDERED) { + queue_context *qctx = + odp_queue_context(queue); + buf_contents *bctx = + odp_buffer_addr(buf); + bctx->sequence = qctx->sequence++; + } + + ret = odp_queue_enq(queue, ev); + CU_ASSERT_FATAL(ret == 0); + + if (ret) + odp_buffer_free(buf); + else + buf_count++; + } + } + } + + globals->buf_count = buf_count; + globals->buf_count_cpy = buf_count; +} + +static void reset_queues(thread_args_t *args) +{ + int i, j, k; + int num_prio = args->num_prio; + int num_queues = args->num_queues; + char name[32]; + + for (i = 0; i < num_prio; i++) { + for (j = 0; j < num_queues; j++) { + odp_queue_t queue; + + snprintf(name, sizeof(name), + "sched_%d_%d_o", i, j); + queue = odp_queue_lookup(name); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + for (k = 0; k < args->num_bufs; k++) { + queue_context *qctx = + odp_queue_context(queue); + int ndx; + int ndx_max; + + ndx_max = odp_queue_lock_count(queue); + CU_ASSERT_FATAL(ndx_max >= 0); + qctx->sequence = 0; + for (ndx = 0; ndx < ndx_max; ndx++) + qctx->lock_sequence[ndx] = 0; + } + } + } +} + +static void schedule_common(odp_schedule_sync_t sync, int num_queues, + int num_prio, int enable_schd_multi) +{ + thread_args_t args; + odp_shm_t shm; + test_globals_t *globals; + + shm = odp_shm_lookup(GLOBALS_SHM_NAME); + CU_ASSERT_FATAL(shm != ODP_SHM_INVALID); + globals = odp_shm_addr(shm); + CU_ASSERT_PTR_NOT_NULL_FATAL(globals); + + memset(&args, 0, sizeof(thread_args_t)); + args.globals = globals; + args.sync = sync; + args.num_queues = num_queues; + args.num_prio = num_prio; + args.num_bufs = BUFS_PER_QUEUE; + args.num_workers = 1; + args.enable_schd_multi = enable_schd_multi; + args.enable_excl_atomic = 0; /* Not needed with a single CPU */ + + fill_queues(&args); + + schedule_common_(&args); + if (sync == ODP_SCHED_SYNC_ORDERED) + reset_queues(&args); +} + +static void parallel_execute(odp_schedule_sync_t sync, int num_queues, + int num_prio, int enable_schd_multi, + int enable_excl_atomic) +{ + odp_shm_t shm; + test_globals_t *globals; + thread_args_t *args; + + shm = odp_shm_lookup(GLOBALS_SHM_NAME); + CU_ASSERT_FATAL(shm != ODP_SHM_INVALID); + globals = odp_shm_addr(shm); + CU_ASSERT_PTR_NOT_NULL_FATAL(globals); + + shm = odp_shm_lookup(SHM_THR_ARGS_NAME); + CU_ASSERT_FATAL(shm != ODP_SHM_INVALID); + args = odp_shm_addr(shm); + CU_ASSERT_PTR_NOT_NULL_FATAL(args); + + args->globals = globals; + args->sync = sync; + args->num_queues = num_queues; + args->num_prio = num_prio; + if (enable_excl_atomic) + args->num_bufs = BUFS_PER_QUEUE_EXCL; + else + args->num_bufs = BUFS_PER_QUEUE; + args->num_workers = globals->num_workers; + args->enable_schd_multi = enable_schd_multi; + args->enable_excl_atomic = enable_excl_atomic; + + fill_queues(args); + + /* Create and launch worker threads */ + args->cu_thr.numthrds = globals->num_workers; + odp_cunit_thread_create(schedule_common_, &args->cu_thr); + + /* Wait for worker threads to terminate */ + odp_cunit_thread_exit(&args->cu_thr); + + /* Cleanup ordered queues for next pass */ + if (sync == ODP_SCHED_SYNC_ORDERED) + reset_queues(args); +} + +/* 1 queue 1 thread ODP_SCHED_SYNC_PARALLEL */ +void scheduler_test_1q_1t_n(void) +{ + schedule_common(ODP_SCHED_SYNC_PARALLEL, ONE_Q, ONE_PRIO, SCHD_ONE); +} + +/* 1 queue 1 thread ODP_SCHED_SYNC_ATOMIC */ +void scheduler_test_1q_1t_a(void) +{ + schedule_common(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_ONE); +} + +/* 1 queue 1 thread ODP_SCHED_SYNC_ORDERED */ +void scheduler_test_1q_1t_o(void) +{ + schedule_common(ODP_SCHED_SYNC_ORDERED, ONE_Q, ONE_PRIO, SCHD_ONE); +} + +/* Many queues 1 thread ODP_SCHED_SYNC_PARALLEL */ +void scheduler_test_mq_1t_n(void) +{ + /* Only one priority involved in these tests, but use + the same number of queues the more general case uses */ + schedule_common(ODP_SCHED_SYNC_PARALLEL, MANY_QS, ONE_PRIO, SCHD_ONE); +} + +/* Many queues 1 thread ODP_SCHED_SYNC_ATOMIC */ +void scheduler_test_mq_1t_a(void) +{ + schedule_common(ODP_SCHED_SYNC_ATOMIC, MANY_QS, ONE_PRIO, SCHD_ONE); +} + +/* Many queues 1 thread ODP_SCHED_SYNC_ORDERED */ +void scheduler_test_mq_1t_o(void) +{ + schedule_common(ODP_SCHED_SYNC_ORDERED, MANY_QS, ONE_PRIO, SCHD_ONE); +} + +/* Many queues 1 thread check priority ODP_SCHED_SYNC_PARALLEL */ +void scheduler_test_mq_1t_prio_n(void) +{ + int prio = odp_schedule_num_prio(); + + schedule_common(ODP_SCHED_SYNC_PARALLEL, MANY_QS, prio, SCHD_ONE); +} + +/* Many queues 1 thread check priority ODP_SCHED_SYNC_ATOMIC */ +void scheduler_test_mq_1t_prio_a(void) +{ + int prio = odp_schedule_num_prio(); + + schedule_common(ODP_SCHED_SYNC_ATOMIC, MANY_QS, prio, SCHD_ONE); +} + +/* Many queues 1 thread check priority ODP_SCHED_SYNC_ORDERED */ +void scheduler_test_mq_1t_prio_o(void) +{ + int prio = odp_schedule_num_prio(); + + schedule_common(ODP_SCHED_SYNC_ORDERED, MANY_QS, prio, SCHD_ONE); +} + +/* Many queues many threads check priority ODP_SCHED_SYNC_PARALLEL */ +void scheduler_test_mq_mt_prio_n(void) +{ + int prio = odp_schedule_num_prio(); + + parallel_execute(ODP_SCHED_SYNC_PARALLEL, MANY_QS, prio, SCHD_ONE, + DISABLE_EXCL_ATOMIC); +} + +/* Many queues many threads check priority ODP_SCHED_SYNC_ATOMIC */ +void scheduler_test_mq_mt_prio_a(void) +{ + int prio = odp_schedule_num_prio(); + + parallel_execute(ODP_SCHED_SYNC_ATOMIC, MANY_QS, prio, SCHD_ONE, + DISABLE_EXCL_ATOMIC); +} + +/* Many queues many threads check priority ODP_SCHED_SYNC_ORDERED */ +void scheduler_test_mq_mt_prio_o(void) +{ + int prio = odp_schedule_num_prio(); + + parallel_execute(ODP_SCHED_SYNC_ORDERED, MANY_QS, prio, SCHD_ONE, + DISABLE_EXCL_ATOMIC); +} + +/* 1 queue many threads check exclusive access on ATOMIC queues */ +void scheduler_test_1q_mt_a_excl(void) +{ + parallel_execute(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_ONE, + ENABLE_EXCL_ATOMIC); +} + +/* 1 queue 1 thread ODP_SCHED_SYNC_PARALLEL multi */ +void scheduler_test_multi_1q_1t_n(void) +{ + schedule_common(ODP_SCHED_SYNC_PARALLEL, ONE_Q, ONE_PRIO, SCHD_MULTI); +} + +/* 1 queue 1 thread ODP_SCHED_SYNC_ATOMIC multi */ +void scheduler_test_multi_1q_1t_a(void) +{ + schedule_common(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_MULTI); +} + +/* 1 queue 1 thread ODP_SCHED_SYNC_ORDERED multi */ +void scheduler_test_multi_1q_1t_o(void) +{ + schedule_common(ODP_SCHED_SYNC_ORDERED, ONE_Q, ONE_PRIO, SCHD_MULTI); +} + +/* Many queues 1 thread ODP_SCHED_SYNC_PARALLEL multi */ +void scheduler_test_multi_mq_1t_n(void) +{ + /* Only one priority involved in these tests, but use + the same number of queues the more general case uses */ + schedule_common(ODP_SCHED_SYNC_PARALLEL, MANY_QS, ONE_PRIO, SCHD_MULTI); +} + +/* Many queues 1 thread ODP_SCHED_SYNC_ATOMIC multi */ +void scheduler_test_multi_mq_1t_a(void) +{ + schedule_common(ODP_SCHED_SYNC_ATOMIC, MANY_QS, ONE_PRIO, SCHD_MULTI); +} + +/* Many queues 1 thread ODP_SCHED_SYNC_ORDERED multi */ +void scheduler_test_multi_mq_1t_o(void) +{ + schedule_common(ODP_SCHED_SYNC_ORDERED, MANY_QS, ONE_PRIO, SCHD_MULTI); +} + +/* Many queues 1 thread check priority ODP_SCHED_SYNC_PARALLEL multi */ +void scheduler_test_multi_mq_1t_prio_n(void) +{ + int prio = odp_schedule_num_prio(); + + schedule_common(ODP_SCHED_SYNC_PARALLEL, MANY_QS, prio, SCHD_MULTI); +} + +/* Many queues 1 thread check priority ODP_SCHED_SYNC_ATOMIC multi */ +void scheduler_test_multi_mq_1t_prio_a(void) +{ + int prio = odp_schedule_num_prio(); + + schedule_common(ODP_SCHED_SYNC_ATOMIC, MANY_QS, prio, SCHD_MULTI); +} + +/* Many queues 1 thread check priority ODP_SCHED_SYNC_ORDERED multi */ +void scheduler_test_multi_mq_1t_prio_o(void) +{ + int prio = odp_schedule_num_prio(); + + schedule_common(ODP_SCHED_SYNC_ORDERED, MANY_QS, prio, SCHD_MULTI); +} + +/* Many queues many threads check priority ODP_SCHED_SYNC_PARALLEL multi */ +void scheduler_test_multi_mq_mt_prio_n(void) +{ + int prio = odp_schedule_num_prio(); + + parallel_execute(ODP_SCHED_SYNC_PARALLEL, MANY_QS, prio, SCHD_MULTI, 0); +} + +/* Many queues many threads check priority ODP_SCHED_SYNC_ATOMIC multi */ +void scheduler_test_multi_mq_mt_prio_a(void) +{ + int prio = odp_schedule_num_prio(); + + parallel_execute(ODP_SCHED_SYNC_ATOMIC, MANY_QS, prio, SCHD_MULTI, 0); +} + +/* Many queues many threads check priority ODP_SCHED_SYNC_ORDERED multi */ +void scheduler_test_multi_mq_mt_prio_o(void) +{ + int prio = odp_schedule_num_prio(); + + parallel_execute(ODP_SCHED_SYNC_ORDERED, MANY_QS, prio, SCHD_MULTI, 0); +} + +/* 1 queue many threads check exclusive access on ATOMIC queues multi */ +void scheduler_test_multi_1q_mt_a_excl(void) +{ + parallel_execute(ODP_SCHED_SYNC_ATOMIC, ONE_Q, ONE_PRIO, SCHD_MULTI, + ENABLE_EXCL_ATOMIC); +} + +void scheduler_test_pause_resume(void) +{ + odp_queue_t queue; + odp_buffer_t buf; + odp_event_t ev; + odp_queue_t from; + int i; + int local_bufs = 0; + int ret; + + queue = odp_queue_lookup("sched_0_0_n"); + CU_ASSERT_FATAL(queue != ODP_QUEUE_INVALID); + + pool = odp_pool_lookup(MSG_POOL_NAME); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + for (i = 0; i < NUM_BUFS_PAUSE; i++) { + buf = odp_buffer_alloc(pool); + CU_ASSERT_FATAL(buf != ODP_BUFFER_INVALID); + ev = odp_buffer_to_event(buf); + ret = odp_queue_enq(queue, ev); + CU_ASSERT(ret == 0); + + if (ret) + odp_buffer_free(buf); + } + + for (i = 0; i < NUM_BUFS_BEFORE_PAUSE; i++) { + from = ODP_QUEUE_INVALID; + ev = odp_schedule(&from, ODP_SCHED_WAIT); + CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID); + CU_ASSERT(from == queue); + buf = odp_buffer_from_event(ev); + odp_buffer_free(buf); + } + + odp_schedule_pause(); + + while (1) { + ev = odp_schedule(&from, ODP_SCHED_NO_WAIT); + if (ev == ODP_EVENT_INVALID) + break; + + CU_ASSERT(from == queue); + buf = odp_buffer_from_event(ev); + odp_buffer_free(buf); + local_bufs++; + } + + CU_ASSERT(local_bufs < NUM_BUFS_PAUSE - NUM_BUFS_BEFORE_PAUSE); + + odp_schedule_resume(); + + for (i = local_bufs + NUM_BUFS_BEFORE_PAUSE; i < NUM_BUFS_PAUSE; i++) { + ev = odp_schedule(&from, ODP_SCHED_WAIT); + CU_ASSERT(from == queue); + buf = odp_buffer_from_event(ev); + odp_buffer_free(buf); + } + + ret = exit_schedule_loop(); + + CU_ASSERT(ret == 0); +} + +static int create_queues(void) +{ + int i, j, prios, rc; + odp_queue_capability_t capa; + odp_pool_param_t params; + odp_buffer_t queue_ctx_buf; + queue_context *qctx, *pqctx; + uint32_t ndx; + odp_queue_param_t p; + + if (odp_queue_capability(&capa) < 0) { + printf("Queue capability query failed\n"); + return -1; + } + + /* Limit to test maximum */ + if (capa.max_ordered_locks > MAX_ORDERED_LOCKS) { + capa.max_ordered_locks = MAX_ORDERED_LOCKS; + printf("Testing only %u ordered locks\n", + capa.max_ordered_locks); + } + + prios = odp_schedule_num_prio(); + odp_pool_param_init(¶ms); + params.buf.size = sizeof(queue_context); + params.buf.num = prios * QUEUES_PER_PRIO * 2; + params.type = ODP_POOL_BUFFER; + + queue_ctx_pool = odp_pool_create(QUEUE_CTX_POOL_NAME, ¶ms); + + if (queue_ctx_pool == ODP_POOL_INVALID) { + printf("Pool creation failed (queue ctx).\n"); + return -1; + } + + for (i = 0; i < prios; i++) { + odp_queue_param_init(&p); + p.type = ODP_QUEUE_TYPE_SCHED; + p.sched.prio = i; + + for (j = 0; j < QUEUES_PER_PRIO; j++) { + /* Per sched sync type */ + char name[32]; + odp_queue_t q, pq; + + snprintf(name, sizeof(name), "sched_%d_%d_n", i, j); + p.sched.sync = ODP_SCHED_SYNC_PARALLEL; + q = odp_queue_create(name, &p); + + if (q == ODP_QUEUE_INVALID) { + printf("Schedule queue create failed.\n"); + return -1; + } + + snprintf(name, sizeof(name), "sched_%d_%d_a", i, j); + p.sched.sync = ODP_SCHED_SYNC_ATOMIC; + q = odp_queue_create(name, &p); + + if (q == ODP_QUEUE_INVALID) { + printf("Schedule queue create failed.\n"); + return -1; + } + + snprintf(name, sizeof(name), "plain_%d_%d_o", i, j); + pq = odp_queue_create(name, NULL); + if (pq == ODP_QUEUE_INVALID) { + printf("Plain queue create failed.\n"); + return -1; + } + + queue_ctx_buf = odp_buffer_alloc(queue_ctx_pool); + + if (queue_ctx_buf == ODP_BUFFER_INVALID) { + printf("Cannot allocate plain queue ctx buf\n"); + return -1; + } + + pqctx = odp_buffer_addr(queue_ctx_buf); + pqctx->ctx_handle = queue_ctx_buf; + pqctx->sequence = 0; + + rc = odp_queue_context_set(pq, pqctx, 0); + + if (rc != 0) { + printf("Cannot set plain queue context\n"); + return -1; + } + + snprintf(name, sizeof(name), "sched_%d_%d_o", i, j); + p.sched.sync = ODP_SCHED_SYNC_ORDERED; + p.sched.lock_count = capa.max_ordered_locks; + q = odp_queue_create(name, &p); + + if (q == ODP_QUEUE_INVALID) { + printf("Schedule queue create failed.\n"); + return -1; + } + if (odp_queue_lock_count(q) != + (int)capa.max_ordered_locks) { + printf("Queue %" PRIu64 " created with " + "%d locks instead of expected %d\n", + odp_queue_to_u64(q), + odp_queue_lock_count(q), + capa.max_ordered_locks); + return -1; + } + + queue_ctx_buf = odp_buffer_alloc(queue_ctx_pool); + + if (queue_ctx_buf == ODP_BUFFER_INVALID) { + printf("Cannot allocate queue ctx buf\n"); + return -1; + } + + qctx = odp_buffer_addr(queue_ctx_buf); + qctx->ctx_handle = queue_ctx_buf; + qctx->pq_handle = pq; + qctx->sequence = 0; + + for (ndx = 0; + ndx < capa.max_ordered_locks; + ndx++) { + qctx->lock_sequence[ndx] = 0; + } + + rc = odp_queue_context_set(q, qctx, 0); + + if (rc != 0) { + printf("Cannot set queue context\n"); + return -1; + } + } + } + + return 0; +} + +int scheduler_suite_init(void) +{ + odp_cpumask_t mask; + odp_shm_t shm; + odp_pool_t pool; + test_globals_t *globals; + thread_args_t *args; + odp_pool_param_t params; + + odp_pool_param_init(¶ms); + params.buf.size = BUF_SIZE; + params.buf.align = 0; + params.buf.num = MSG_POOL_SIZE; + params.type = ODP_POOL_BUFFER; + + pool = odp_pool_create(MSG_POOL_NAME, ¶ms); + + if (pool == ODP_POOL_INVALID) { + printf("Pool creation failed (msg).\n"); + return -1; + } + + shm = odp_shm_reserve(GLOBALS_SHM_NAME, + sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0); + + globals = odp_shm_addr(shm); + + if (!globals) { + printf("Shared memory reserve failed (globals).\n"); + return -1; + } + + memset(globals, 0, sizeof(test_globals_t)); + + globals->num_workers = odp_cpumask_default_worker(&mask, 0); + if (globals->num_workers > MAX_WORKERS) + globals->num_workers = MAX_WORKERS; + + shm = odp_shm_reserve(SHM_THR_ARGS_NAME, sizeof(thread_args_t), + ODP_CACHE_LINE_SIZE, 0); + args = odp_shm_addr(shm); + + if (!args) { + printf("Shared memory reserve failed (args).\n"); + return -1; + } + + memset(args, 0, sizeof(thread_args_t)); + + /* Barrier to sync test case execution */ + odp_barrier_init(&globals->barrier, globals->num_workers); + odp_ticketlock_init(&globals->lock); + odp_spinlock_init(&globals->atomic_lock); + + if (create_queues() != 0) + return -1; + + return 0; +} + +static int destroy_queue(const char *name) +{ + odp_queue_t q; + queue_context *qctx; + + q = odp_queue_lookup(name); + + if (q == ODP_QUEUE_INVALID) + return -1; + qctx = odp_queue_context(q); + if (qctx) + odp_buffer_free(qctx->ctx_handle); + + return odp_queue_destroy(q); +} + +static int destroy_queues(void) +{ + int i, j, prios; + + prios = odp_schedule_num_prio(); + + for (i = 0; i < prios; i++) { + for (j = 0; j < QUEUES_PER_PRIO; j++) { + char name[32]; + + snprintf(name, sizeof(name), "sched_%d_%d_n", i, j); + if (destroy_queue(name) != 0) + return -1; + + snprintf(name, sizeof(name), "sched_%d_%d_a", i, j); + if (destroy_queue(name) != 0) + return -1; + + snprintf(name, sizeof(name), "sched_%d_%d_o", i, j); + if (destroy_queue(name) != 0) + return -1; + + snprintf(name, sizeof(name), "plain_%d_%d_o", i, j); + if (destroy_queue(name) != 0) + return -1; + } + } + + if (odp_pool_destroy(queue_ctx_pool) != 0) { + fprintf(stderr, "error: failed to destroy queue ctx pool\n"); + return -1; + } + + return 0; +} + +int scheduler_suite_term(void) +{ + odp_pool_t pool; + + if (destroy_queues() != 0) { + fprintf(stderr, "error: failed to destroy queues\n"); + return -1; + } + + pool = odp_pool_lookup(MSG_POOL_NAME); + if (odp_pool_destroy(pool) != 0) + fprintf(stderr, "error: failed to destroy pool\n"); + + return 0; +} + +odp_testinfo_t scheduler_suite[] = { + ODP_TEST_INFO(scheduler_test_wait_time), + ODP_TEST_INFO(scheduler_test_num_prio), + ODP_TEST_INFO(scheduler_test_queue_destroy), + ODP_TEST_INFO(scheduler_test_groups), + ODP_TEST_INFO(scheduler_test_pause_resume), + ODP_TEST_INFO(scheduler_test_parallel), + ODP_TEST_INFO(scheduler_test_atomic), + ODP_TEST_INFO(scheduler_test_ordered), + ODP_TEST_INFO(scheduler_test_chaos), + ODP_TEST_INFO(scheduler_test_1q_1t_n), + ODP_TEST_INFO(scheduler_test_1q_1t_a), + ODP_TEST_INFO(scheduler_test_1q_1t_o), + ODP_TEST_INFO(scheduler_test_mq_1t_n), + ODP_TEST_INFO(scheduler_test_mq_1t_a), + ODP_TEST_INFO(scheduler_test_mq_1t_o), + ODP_TEST_INFO(scheduler_test_mq_1t_prio_n), + ODP_TEST_INFO(scheduler_test_mq_1t_prio_a), + ODP_TEST_INFO(scheduler_test_mq_1t_prio_o), + ODP_TEST_INFO(scheduler_test_mq_mt_prio_n), + ODP_TEST_INFO(scheduler_test_mq_mt_prio_a), + ODP_TEST_INFO(scheduler_test_mq_mt_prio_o), + ODP_TEST_INFO(scheduler_test_1q_mt_a_excl), + ODP_TEST_INFO(scheduler_test_multi_1q_1t_n), + ODP_TEST_INFO(scheduler_test_multi_1q_1t_a), + ODP_TEST_INFO(scheduler_test_multi_1q_1t_o), + ODP_TEST_INFO(scheduler_test_multi_mq_1t_n), + ODP_TEST_INFO(scheduler_test_multi_mq_1t_a), + ODP_TEST_INFO(scheduler_test_multi_mq_1t_o), + ODP_TEST_INFO(scheduler_test_multi_mq_1t_prio_n), + ODP_TEST_INFO(scheduler_test_multi_mq_1t_prio_a), + ODP_TEST_INFO(scheduler_test_multi_mq_1t_prio_o), + ODP_TEST_INFO(scheduler_test_multi_mq_mt_prio_n), + ODP_TEST_INFO(scheduler_test_multi_mq_mt_prio_a), + ODP_TEST_INFO(scheduler_test_multi_mq_mt_prio_o), + ODP_TEST_INFO(scheduler_test_multi_1q_mt_a_excl), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t scheduler_suites[] = { + {"Scheduler", + scheduler_suite_init, scheduler_suite_term, scheduler_suite + }, + ODP_SUITE_INFO_NULL, +}; + +int scheduler_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(scheduler_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/scheduler/scheduler.h b/test/common_plat/validation/api/scheduler/scheduler.h new file mode 100644 index 000000000..a619d89b2 --- /dev/null +++ b/test/common_plat/validation/api/scheduler/scheduler.h @@ -0,0 +1,62 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_SCHEDULER_H_ +#define _ODP_TEST_SCHEDULER_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void scheduler_test_wait_time(void); +void scheduler_test_num_prio(void); +void scheduler_test_queue_destroy(void); +void scheduler_test_groups(void); +void scheduler_test_chaos(void); +void scheduler_test_parallel(void); +void scheduler_test_atomic(void); +void scheduler_test_ordered(void); +void scheduler_test_1q_1t_n(void); +void scheduler_test_1q_1t_a(void); +void scheduler_test_1q_1t_o(void); +void scheduler_test_mq_1t_n(void); +void scheduler_test_mq_1t_a(void); +void scheduler_test_mq_1t_o(void); +void scheduler_test_mq_1t_prio_n(void); +void scheduler_test_mq_1t_prio_a(void); +void scheduler_test_mq_1t_prio_o(void); +void scheduler_test_mq_mt_prio_n(void); +void scheduler_test_mq_mt_prio_a(void); +void scheduler_test_mq_mt_prio_o(void); +void scheduler_test_1q_mt_a_excl(void); +void scheduler_test_multi_1q_1t_n(void); +void scheduler_test_multi_1q_1t_a(void); +void scheduler_test_multi_1q_1t_o(void); +void scheduler_test_multi_mq_1t_n(void); +void scheduler_test_multi_mq_1t_a(void); +void scheduler_test_multi_mq_1t_o(void); +void scheduler_test_multi_mq_1t_prio_n(void); +void scheduler_test_multi_mq_1t_prio_a(void); +void scheduler_test_multi_mq_1t_prio_o(void); +void scheduler_test_multi_mq_mt_prio_n(void); +void scheduler_test_multi_mq_mt_prio_a(void); +void scheduler_test_multi_mq_mt_prio_o(void); +void scheduler_test_multi_1q_mt_a_excl(void); +void scheduler_test_pause_resume(void); + +/* test arrays: */ +extern odp_testinfo_t scheduler_suite[]; + +/* test array init/term functions: */ +int scheduler_suite_init(void); +int scheduler_suite_term(void); + +/* test registry: */ +extern odp_suiteinfo_t scheduler_suites[]; + +/* main test program: */ +int scheduler_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/scheduler/scheduler_main.c b/test/common_plat/validation/api/scheduler/scheduler_main.c new file mode 100644 index 000000000..57cfa5fc5 --- /dev/null +++ b/test/common_plat/validation/api/scheduler/scheduler_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "scheduler.h" + +int main(int argc, char *argv[]) +{ + return scheduler_main(argc, argv); +} diff --git a/test/common_plat/validation/api/shmem/.gitignore b/test/common_plat/validation/api/shmem/.gitignore new file mode 100644 index 000000000..4d82fd53a --- /dev/null +++ b/test/common_plat/validation/api/shmem/.gitignore @@ -0,0 +1 @@ +shmem_main diff --git a/test/common_plat/validation/api/shmem/Makefile.am b/test/common_plat/validation/api/shmem/Makefile.am new file mode 100644 index 000000000..da88af662 --- /dev/null +++ b/test/common_plat/validation/api/shmem/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestshmem.la +libtestshmem_la_SOURCES = shmem.c + +test_PROGRAMS = shmem_main$(EXEEXT) +dist_shmem_main_SOURCES = shmem_main.c +shmem_main_LDADD = libtestshmem.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = shmem.h diff --git a/test/common_plat/validation/api/shmem/shmem.c b/test/common_plat/validation/api/shmem/shmem.c new file mode 100644 index 000000000..cbff6738c --- /dev/null +++ b/test/common_plat/validation/api/shmem/shmem.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include <odp_cunit_common.h> +#include "shmem.h" + +#define ALIGE_SIZE (128) +#define TESTNAME "cunit_test_shared_data" +#define TEST_SHARE_FOO (0xf0f0f0f0) +#define TEST_SHARE_BAR (0xf0f0f0f) + +static odp_barrier_t test_barrier; + +static int run_shm_thread(void *arg ODP_UNUSED) +{ + odp_shm_info_t info; + odp_shm_t shm; + test_shared_data_t *test_shared_data; + int thr; + + odp_barrier_wait(&test_barrier); + thr = odp_thread_id(); + printf("Thread %i starts\n", thr); + + shm = odp_shm_lookup(TESTNAME); + CU_ASSERT(ODP_SHM_INVALID != shm); + test_shared_data = odp_shm_addr(shm); + CU_ASSERT(TEST_SHARE_FOO == test_shared_data->foo); + CU_ASSERT(TEST_SHARE_BAR == test_shared_data->bar); + CU_ASSERT(0 == odp_shm_info(shm, &info)); + CU_ASSERT(0 == strcmp(TESTNAME, info.name)); + CU_ASSERT(0 == info.flags); + CU_ASSERT(test_shared_data == info.addr); + CU_ASSERT(sizeof(test_shared_data_t) <= info.size); +#ifdef MAP_HUGETLB + CU_ASSERT(odp_sys_huge_page_size() == info.page_size); +#else + CU_ASSERT(odp_sys_page_size() == info.page_size); +#endif + odp_shm_print_all(); + + fflush(stdout); + return CU_get_number_of_failures(); +} + +void shmem_test_odp_shm_sunnyday(void) +{ + pthrd_arg thrdarg; + odp_shm_t shm; + test_shared_data_t *test_shared_data; + odp_cpumask_t unused; + + shm = odp_shm_reserve(TESTNAME, + sizeof(test_shared_data_t), ALIGE_SIZE, 0); + CU_ASSERT(ODP_SHM_INVALID != shm); + CU_ASSERT(odp_shm_to_u64(shm) != odp_shm_to_u64(ODP_SHM_INVALID)); + + CU_ASSERT(0 == odp_shm_free(shm)); + CU_ASSERT(ODP_SHM_INVALID == odp_shm_lookup(TESTNAME)); + + shm = odp_shm_reserve(TESTNAME, + sizeof(test_shared_data_t), ALIGE_SIZE, 0); + CU_ASSERT(ODP_SHM_INVALID != shm); + + test_shared_data = odp_shm_addr(shm); + CU_ASSERT_FATAL(NULL != test_shared_data); + test_shared_data->foo = TEST_SHARE_FOO; + test_shared_data->bar = TEST_SHARE_BAR; + + thrdarg.numthrds = odp_cpumask_default_worker(&unused, 0); + + if (thrdarg.numthrds > MAX_WORKERS) + thrdarg.numthrds = MAX_WORKERS; + + odp_barrier_init(&test_barrier, thrdarg.numthrds); + odp_cunit_thread_create(run_shm_thread, &thrdarg); + CU_ASSERT(odp_cunit_thread_exit(&thrdarg) >= 0); +} + +odp_testinfo_t shmem_suite[] = { + ODP_TEST_INFO(shmem_test_odp_shm_sunnyday), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t shmem_suites[] = { + {"Shared Memory", NULL, NULL, shmem_suite}, + ODP_SUITE_INFO_NULL, +}; + +int shmem_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(shmem_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/shmem/shmem.h b/test/common_plat/validation/api/shmem/shmem.h new file mode 100644 index 000000000..a5893d931 --- /dev/null +++ b/test/common_plat/validation/api/shmem/shmem.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_SHMEM_H_ +#define _ODP_TEST_SHMEM_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void shmem_test_odp_shm_sunnyday(void); + +/* test arrays: */ +extern odp_testinfo_t shmem_suite[]; + +/* test registry: */ +extern odp_suiteinfo_t shmem_suites[]; + +/* main test program: */ +int shmem_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/shmem/shmem_main.c b/test/common_plat/validation/api/shmem/shmem_main.c new file mode 100644 index 000000000..4c6913051 --- /dev/null +++ b/test/common_plat/validation/api/shmem/shmem_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "shmem.h" + +int main(int argc, char *argv[]) +{ + return shmem_main(argc, argv); +} diff --git a/test/common_plat/validation/api/std_clib/.gitignore b/test/common_plat/validation/api/std_clib/.gitignore new file mode 100644 index 000000000..37828330a --- /dev/null +++ b/test/common_plat/validation/api/std_clib/.gitignore @@ -0,0 +1 @@ +std_clib_main diff --git a/test/common_plat/validation/api/std_clib/Makefile.am b/test/common_plat/validation/api/std_clib/Makefile.am new file mode 100644 index 000000000..e2fc0ccf3 --- /dev/null +++ b/test/common_plat/validation/api/std_clib/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libteststd_clib.la +libteststd_clib_la_SOURCES = std_clib.c + +test_PROGRAMS = std_clib_main$(EXEEXT) +dist_std_clib_main_SOURCES = std_clib_main.c +std_clib_main_LDADD = libteststd_clib.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = std_clib.h diff --git a/test/common_plat/validation/api/std_clib/std_clib.c b/test/common_plat/validation/api/std_clib/std_clib.c new file mode 100644 index 000000000..7f089eabb --- /dev/null +++ b/test/common_plat/validation/api/std_clib/std_clib.c @@ -0,0 +1,110 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include <odp_cunit_common.h> +#include "std_clib.h" + +#include <string.h> + +#define PATTERN 0x5e + +static void std_clib_test_memcpy(void) +{ + uint8_t src[] = {0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15}; + uint8_t dst[16]; + int ret; + + memset(dst, 0, sizeof(dst)); + + odp_memcpy(dst, src, sizeof(dst)); + + ret = memcmp(dst, src, sizeof(dst)); + + CU_ASSERT(ret == 0); +} + +static void std_clib_test_memset(void) +{ + uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15}; + uint8_t ref[16]; + int ret; + + odp_memset(data, PATTERN, sizeof(data)); + + memset(ref, PATTERN, sizeof(ref)); + + ret = memcmp(data, ref, sizeof(data)); + + CU_ASSERT(ret == 0); +} + +static void std_clib_test_memcmp(void) +{ + uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + uint8_t equal[] = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + uint8_t greater_11[] = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 99, 12, 13, 14, 15, 16}; + uint8_t less_6[] = {1, 2, 3, 4, 5, 2, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + size_t i; + + CU_ASSERT(odp_memcmp(data, equal, 0) == 0); + CU_ASSERT(odp_memcmp(data, equal, sizeof(data)) == 0); + CU_ASSERT(odp_memcmp(data, equal, sizeof(data) - 3) == 0); + + CU_ASSERT(odp_memcmp(greater_11, data, sizeof(data)) > 0); + CU_ASSERT(odp_memcmp(greater_11, data, 11) > 0); + CU_ASSERT(odp_memcmp(greater_11, data, 10) == 0); + + CU_ASSERT(odp_memcmp(less_6, data, sizeof(data)) < 0); + CU_ASSERT(odp_memcmp(less_6, data, 6) < 0); + CU_ASSERT(odp_memcmp(less_6, data, 5) == 0); + + for (i = 0; i < sizeof(data); i++) { + uint8_t tmp; + + CU_ASSERT(odp_memcmp(data, equal, i + 1) == 0); + tmp = equal[i]; + equal[i] = 88; + CU_ASSERT(odp_memcmp(data, equal, i + 1) < 0); + equal[i] = 0; + CU_ASSERT(odp_memcmp(data, equal, i + 1) > 0); + equal[i] = tmp; + } +} + +odp_testinfo_t std_clib_suite[] = { + ODP_TEST_INFO(std_clib_test_memcpy), + ODP_TEST_INFO(std_clib_test_memset), + ODP_TEST_INFO(std_clib_test_memcmp), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t std_clib_suites[] = { + {"Std C library", NULL, NULL, std_clib_suite}, + ODP_SUITE_INFO_NULL +}; + +int std_clib_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(std_clib_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/std_clib/std_clib.h b/test/common_plat/validation/api/std_clib/std_clib.h new file mode 100644 index 000000000..2804f27e2 --- /dev/null +++ b/test/common_plat/validation/api/std_clib/std_clib.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_STD_CLIB_H_ +#define _ODP_TEST_STD_CLIB_H_ + +#include <odp_cunit_common.h> + +/* test arrays: */ +extern odp_testinfo_t std_clib_suite[]; + +/* test registry: */ +extern odp_suiteinfo_t std_clib_suites[]; + +/* main test program: */ +int std_clib_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/std_clib/std_clib_main.c b/test/common_plat/validation/api/std_clib/std_clib_main.c new file mode 100644 index 000000000..ef6f2736f --- /dev/null +++ b/test/common_plat/validation/api/std_clib/std_clib_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "std_clib.h" + +int main(int argc, char *argv[]) +{ + return std_clib_main(argc, argv); +} diff --git a/test/common_plat/validation/api/system/.gitignore b/test/common_plat/validation/api/system/.gitignore new file mode 100644 index 000000000..347b1ee21 --- /dev/null +++ b/test/common_plat/validation/api/system/.gitignore @@ -0,0 +1 @@ +system_main diff --git a/test/common_plat/validation/api/system/Makefile.am b/test/common_plat/validation/api/system/Makefile.am new file mode 100644 index 000000000..3789c36c2 --- /dev/null +++ b/test/common_plat/validation/api/system/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestsystem.la +libtestsystem_la_SOURCES = system.c + +test_PROGRAMS = system_main$(EXEEXT) +dist_system_main_SOURCES = system_main.c +system_main_LDADD = libtestsystem.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = system.h diff --git a/test/common_plat/validation/api/system/system.c b/test/common_plat/validation/api/system/system.c new file mode 100644 index 000000000..57ff34eb9 --- /dev/null +++ b/test/common_plat/validation/api/system/system.c @@ -0,0 +1,344 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <ctype.h> +#include <odp_api.h> +#include <odp/api/cpumask.h> +#include "odp_cunit_common.h" +#include "test_debug.h" +#include "system.h" + +#define DIFF_TRY_NUM 160 +#define RES_TRY_NUM 10 + +void system_test_odp_version_numbers(void) +{ + int char_ok = 0; + char version_string[128]; + char *s = version_string; + + strncpy(version_string, odp_version_api_str(), + sizeof(version_string) - 1); + + while (*s) { + if (isdigit((int)*s) || (strncmp(s, ".", 1) == 0)) { + char_ok = 1; + s++; + } else { + char_ok = 0; + LOG_DBG("\nBAD VERSION=%s\n", version_string); + break; + } + } + CU_ASSERT(char_ok); +} + +void system_test_odp_cpu_count(void) +{ + int cpus; + + cpus = odp_cpu_count(); + CU_ASSERT(0 < cpus); +} + +void system_test_odp_cpu_cycles(void) +{ + uint64_t c2, c1; + + c1 = odp_cpu_cycles(); + odp_time_wait_ns(100); + c2 = odp_cpu_cycles(); + + CU_ASSERT(c2 != c1); +} + +void system_test_odp_cpu_cycles_max(void) +{ + uint64_t c2, c1; + uint64_t max1, max2; + + max1 = odp_cpu_cycles_max(); + odp_time_wait_ns(100); + max2 = odp_cpu_cycles_max(); + + CU_ASSERT(max1 >= UINT32_MAX / 2); + CU_ASSERT(max1 == max2); + + c1 = odp_cpu_cycles(); + odp_time_wait_ns(1000); + c2 = odp_cpu_cycles(); + + CU_ASSERT(c1 <= max1 && c2 <= max1); +} + +void system_test_odp_cpu_cycles_resolution(void) +{ + int i; + uint64_t res; + uint64_t c2, c1, max; + + max = odp_cpu_cycles_max(); + + res = odp_cpu_cycles_resolution(); + CU_ASSERT(res != 0); + CU_ASSERT(res < max / 1024); + + for (i = 0; i < RES_TRY_NUM; i++) { + c1 = odp_cpu_cycles(); + odp_time_wait_ns(100 * ODP_TIME_MSEC_IN_NS + i); + c2 = odp_cpu_cycles(); + + CU_ASSERT(c1 % res == 0); + CU_ASSERT(c2 % res == 0); + } +} + +void system_test_odp_cpu_cycles_diff(void) +{ + int i; + uint64_t c2, c1, c3, max; + uint64_t tmp, diff, res; + + res = odp_cpu_cycles_resolution(); + max = odp_cpu_cycles_max(); + + /* check resolution for wrap */ + c1 = max - 2 * res; + do + c2 = odp_cpu_cycles(); + while (c1 < c2); + + diff = odp_cpu_cycles_diff(c1, c1); + CU_ASSERT(diff == 0); + + /* wrap */ + tmp = c2 + (max - c1) + res; + diff = odp_cpu_cycles_diff(c2, c1); + CU_ASSERT(diff == tmp); + CU_ASSERT(diff % res == 0); + + /* no wrap, revert args */ + tmp = c1 - c2; + diff = odp_cpu_cycles_diff(c1, c2); + CU_ASSERT(diff == tmp); + CU_ASSERT(diff % res == 0); + + c3 = odp_cpu_cycles(); + for (i = 0; i < DIFF_TRY_NUM; i++) { + c1 = odp_cpu_cycles(); + odp_time_wait_ns(100 * ODP_TIME_MSEC_IN_NS + i); + c2 = odp_cpu_cycles(); + + CU_ASSERT(c2 != c1); + CU_ASSERT(c1 % res == 0); + CU_ASSERT(c2 % res == 0); + CU_ASSERT(c1 <= max && c2 <= max); + + if (c2 > c1) + tmp = c2 - c1; + else + tmp = c2 + (max - c1) + res; + + diff = odp_cpu_cycles_diff(c2, c1); + CU_ASSERT(diff == tmp); + CU_ASSERT(diff % res == 0); + + /* wrap is detected and verified */ + if (c2 < c1) + break; + } + + /* wrap was detected, no need to continue */ + if (i < DIFF_TRY_NUM) + return; + + /* wrap has to be detected if possible */ + CU_ASSERT(max > UINT32_MAX); + CU_ASSERT((max - c3) > UINT32_MAX); + + printf("wrap was not detected..."); +} + +void system_test_odp_sys_cache_line_size(void) +{ + uint64_t cache_size; + + cache_size = odp_sys_cache_line_size(); + CU_ASSERT(0 < cache_size); + CU_ASSERT(ODP_CACHE_LINE_SIZE == cache_size); +} + +void system_test_odp_cpu_model_str(void) +{ + char model[128]; + + snprintf(model, 128, "%s", odp_cpu_model_str()); + CU_ASSERT(strlen(model) > 0); + CU_ASSERT(strlen(model) < 127); +} + +void system_test_odp_cpu_model_str_id(void) +{ + char model[128]; + odp_cpumask_t mask; + int i, num, cpu; + + num = odp_cpumask_all_available(&mask); + cpu = odp_cpumask_first(&mask); + + for (i = 0; i < num; i++) { + snprintf(model, 128, "%s", odp_cpu_model_str_id(cpu)); + CU_ASSERT(strlen(model) > 0); + CU_ASSERT(strlen(model) < 127); + cpu = odp_cpumask_next(&mask, cpu); + } +} + +void system_test_odp_sys_page_size(void) +{ + uint64_t page; + + page = odp_sys_page_size(); + CU_ASSERT(0 < page); + CU_ASSERT(ODP_PAGE_SIZE == page); +} + +void system_test_odp_sys_huge_page_size(void) +{ + uint64_t page; + + page = odp_sys_huge_page_size(); + CU_ASSERT(0 < page); +} + +int system_check_odp_cpu_hz(void) +{ + if (odp_cpu_hz() == 0) { + fprintf(stderr, "odp_cpu_hz is not supported, skipping\n"); + return ODP_TEST_INACTIVE; + } + + return ODP_TEST_ACTIVE; +} + +void system_test_odp_cpu_hz(void) +{ + uint64_t hz = odp_cpu_hz(); + + /* Test value sanity: less than 10GHz */ + CU_ASSERT(hz < 10 * GIGA_HZ); + + /* larger than 1kHz */ + CU_ASSERT(hz > 1 * KILO_HZ); +} + +int system_check_odp_cpu_hz_id(void) +{ + uint64_t hz; + odp_cpumask_t mask; + int i, num, cpu; + + num = odp_cpumask_all_available(&mask); + cpu = odp_cpumask_first(&mask); + + for (i = 0; i < num; i++) { + hz = odp_cpu_hz_id(cpu); + if (hz == 0) { + fprintf(stderr, "cpu %d does not support" + " odp_cpu_hz_id()," + "skip that test\n", cpu); + return ODP_TEST_INACTIVE; + } + cpu = odp_cpumask_next(&mask, cpu); + } + + return ODP_TEST_ACTIVE; +} + +void system_test_odp_cpu_hz_id(void) +{ + uint64_t hz; + odp_cpumask_t mask; + int i, num, cpu; + + num = odp_cpumask_all_available(&mask); + cpu = odp_cpumask_first(&mask); + + for (i = 0; i < num; i++) { + hz = odp_cpu_hz_id(cpu); + /* Test value sanity: less than 10GHz */ + CU_ASSERT(hz < 10 * GIGA_HZ); + /* larger than 1kHz */ + CU_ASSERT(hz > 1 * KILO_HZ); + cpu = odp_cpumask_next(&mask, cpu); + } +} + +void system_test_odp_cpu_hz_max(void) +{ + uint64_t hz; + + hz = odp_cpu_hz_max(); + CU_ASSERT(0 < hz); +} + +void system_test_odp_cpu_hz_max_id(void) +{ + uint64_t hz; + odp_cpumask_t mask; + int i, num, cpu; + + num = odp_cpumask_all_available(&mask); + cpu = odp_cpumask_first(&mask); + + for (i = 0; i < num; i++) { + hz = odp_cpu_hz_max_id(cpu); + CU_ASSERT(0 < hz); + cpu = odp_cpumask_next(&mask, cpu); + } +} + +odp_testinfo_t system_suite[] = { + ODP_TEST_INFO(system_test_odp_version_numbers), + ODP_TEST_INFO(system_test_odp_cpu_count), + ODP_TEST_INFO(system_test_odp_sys_cache_line_size), + ODP_TEST_INFO(system_test_odp_cpu_model_str), + ODP_TEST_INFO(system_test_odp_cpu_model_str_id), + ODP_TEST_INFO(system_test_odp_sys_page_size), + ODP_TEST_INFO(system_test_odp_sys_huge_page_size), + ODP_TEST_INFO_CONDITIONAL(system_test_odp_cpu_hz, + system_check_odp_cpu_hz), + ODP_TEST_INFO_CONDITIONAL(system_test_odp_cpu_hz_id, + system_check_odp_cpu_hz_id), + ODP_TEST_INFO(system_test_odp_cpu_hz_max), + ODP_TEST_INFO(system_test_odp_cpu_hz_max_id), + ODP_TEST_INFO(system_test_odp_cpu_cycles), + ODP_TEST_INFO(system_test_odp_cpu_cycles_max), + ODP_TEST_INFO(system_test_odp_cpu_cycles_resolution), + ODP_TEST_INFO(system_test_odp_cpu_cycles_diff), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t system_suites[] = { + {"System Info", NULL, NULL, system_suite}, + ODP_SUITE_INFO_NULL, +}; + +int system_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(system_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/system/system.h b/test/common_plat/validation/api/system/system.h new file mode 100644 index 000000000..cbb994eb0 --- /dev/null +++ b/test/common_plat/validation/api/system/system.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_SYSTEM_H_ +#define _ODP_TEST_SYSTEM_H_ + +#include <odp_cunit_common.h> + +#define GIGA_HZ 1000000000ULL +#define KILO_HZ 1000ULL + +/* test functions: */ +void system_test_odp_version_numbers(void); +void system_test_odp_cpu_count(void); +void system_test_odp_sys_cache_line_size(void); +void system_test_odp_cpu_model_str(void); +void system_test_odp_cpu_model_str_id(void); +void system_test_odp_sys_page_size(void); +void system_test_odp_sys_huge_page_size(void); +int system_check_odp_cpu_hz(void); +void system_test_odp_cpu_hz(void); +int system_check_odp_cpu_hz_id(void); +void system_test_odp_cpu_hz_id(void); +void system_test_odp_cpu_hz_max(void); +void system_test_odp_cpu_hz_max_id(void); +void system_test_odp_cpu_cycles_max(void); +void system_test_odp_cpu_cycles(void); +void system_test_odp_cpu_cycles_diff(void); +void system_test_odp_cpu_cycles_resolution(void); + +/* test arrays: */ +extern odp_testinfo_t system_suite[]; + +/* test registry: */ +extern odp_suiteinfo_t system_suites[]; + +/* main test program: */ +int system_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/system/system_main.c b/test/common_plat/validation/api/system/system_main.c new file mode 100644 index 000000000..50d202a84 --- /dev/null +++ b/test/common_plat/validation/api/system/system_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "system.h" + +int main(int argc, char *argv[]) +{ + return system_main(argc, argv); +} diff --git a/test/common_plat/validation/api/thread/.gitignore b/test/common_plat/validation/api/thread/.gitignore new file mode 100644 index 000000000..ab1787d97 --- /dev/null +++ b/test/common_plat/validation/api/thread/.gitignore @@ -0,0 +1 @@ +thread_main diff --git a/test/common_plat/validation/api/thread/Makefile.am b/test/common_plat/validation/api/thread/Makefile.am new file mode 100644 index 000000000..eaf680cf5 --- /dev/null +++ b/test/common_plat/validation/api/thread/Makefile.am @@ -0,0 +1,12 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtestthread.la +libtestthread_la_SOURCES = thread.c +libtestthread_la_CFLAGS = $(AM_CFLAGS) -DTEST_THRMASK +libtestthread_la_LIBADD = $(LIBTHRMASK_COMMON) + +test_PROGRAMS = thread_main$(EXEEXT) +dist_thread_main_SOURCES = thread_main.c +thread_main_LDADD = libtestthread.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = thread.h diff --git a/test/common_plat/validation/api/thread/thread.c b/test/common_plat/validation/api/thread/thread.c new file mode 100644 index 000000000..24f1c4580 --- /dev/null +++ b/test/common_plat/validation/api/thread/thread.c @@ -0,0 +1,140 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include <odp_cunit_common.h> +#include <mask_common.h> +#include <test_debug.h> +#include "thread.h" + +/* Test thread entry and exit synchronization barriers */ +odp_barrier_t bar_entry; +odp_barrier_t bar_exit; + +void thread_test_odp_cpu_id(void) +{ + (void)odp_cpu_id(); + CU_PASS(); +} + +void thread_test_odp_thread_id(void) +{ + (void)odp_thread_id(); + CU_PASS(); +} + +void thread_test_odp_thread_count(void) +{ + (void)odp_thread_count(); + CU_PASS(); +} + +static int thread_func(void *arg TEST_UNUSED) +{ + /* indicate that thread has started */ + odp_barrier_wait(&bar_entry); + + CU_ASSERT(odp_thread_type() == ODP_THREAD_WORKER); + + /* wait for indication that we can exit */ + odp_barrier_wait(&bar_exit); + + return CU_get_number_of_failures(); +} + +void thread_test_odp_thrmask_worker(void) +{ + odp_thrmask_t mask; + int ret; + pthrd_arg args = { .testcase = 0, .numthrds = 1 }; + + CU_ASSERT_FATAL(odp_thread_type() == ODP_THREAD_CONTROL); + + odp_barrier_init(&bar_entry, args.numthrds + 1); + odp_barrier_init(&bar_exit, args.numthrds + 1); + + /* should start out with 0 worker threads */ + ret = odp_thrmask_worker(&mask); + CU_ASSERT(ret == odp_thrmask_count(&mask)); + CU_ASSERT(ret == 0); + + /* start the test thread(s) */ + ret = odp_cunit_thread_create(thread_func, &args); + CU_ASSERT(ret == args.numthrds); + + if (ret != args.numthrds) + return; + + /* wait for thread(s) to start */ + odp_barrier_wait(&bar_entry); + + ret = odp_thrmask_worker(&mask); + CU_ASSERT(ret == odp_thrmask_count(&mask)); + CU_ASSERT(ret == args.numthrds); + CU_ASSERT(ret <= odp_thread_count_max()); + + /* allow thread(s) to exit */ + odp_barrier_wait(&bar_exit); + + odp_cunit_thread_exit(&args); +} + +void thread_test_odp_thrmask_control(void) +{ + odp_thrmask_t mask; + int ret; + + CU_ASSERT(odp_thread_type() == ODP_THREAD_CONTROL); + + /* should start out with 1 worker thread */ + ret = odp_thrmask_control(&mask); + CU_ASSERT(ret == odp_thrmask_count(&mask)); + CU_ASSERT(ret == 1); +} + +odp_testinfo_t thread_suite[] = { + ODP_TEST_INFO(thread_test_odp_cpu_id), + ODP_TEST_INFO(thread_test_odp_thread_id), + ODP_TEST_INFO(thread_test_odp_thread_count), + ODP_TEST_INFO(thread_test_odp_thrmask_to_from_str), + ODP_TEST_INFO(thread_test_odp_thrmask_equal), + ODP_TEST_INFO(thread_test_odp_thrmask_zero), + ODP_TEST_INFO(thread_test_odp_thrmask_set), + ODP_TEST_INFO(thread_test_odp_thrmask_clr), + ODP_TEST_INFO(thread_test_odp_thrmask_isset), + ODP_TEST_INFO(thread_test_odp_thrmask_count), + ODP_TEST_INFO(thread_test_odp_thrmask_and), + ODP_TEST_INFO(thread_test_odp_thrmask_or), + ODP_TEST_INFO(thread_test_odp_thrmask_xor), + ODP_TEST_INFO(thread_test_odp_thrmask_copy), + ODP_TEST_INFO(thread_test_odp_thrmask_first), + ODP_TEST_INFO(thread_test_odp_thrmask_last), + ODP_TEST_INFO(thread_test_odp_thrmask_next), + ODP_TEST_INFO(thread_test_odp_thrmask_worker), + ODP_TEST_INFO(thread_test_odp_thrmask_control), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t thread_suites[] = { + {"thread", NULL, NULL, thread_suite}, + ODP_SUITE_INFO_NULL, +}; + +int thread_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(thread_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/thread/thread.h b/test/common_plat/validation/api/thread/thread.h new file mode 100644 index 000000000..d511c9259 --- /dev/null +++ b/test/common_plat/validation/api/thread/thread.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_THREAD_H_ +#define _ODP_TEST_THREAD_H_ + +#include <odp_api.h> +#include <odp_cunit_common.h> + +/* test functions: */ +#ifndef TEST_THRMASK +#define TEST_THRMASK +#endif +#include "mask_common.h" +void thread_test_odp_cpu_id(void); +void thread_test_odp_thread_id(void); +void thread_test_odp_thread_count(void); +void thread_test_odp_thrmask_control(void); +void thread_test_odp_thrmask_worker(void); + +/* test arrays: */ +extern odp_testinfo_t thread_suite[]; + +/* test registry: */ +extern odp_suiteinfo_t thread_suites[]; + +/* main test program: */ +int thread_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/thread/thread_main.c b/test/common_plat/validation/api/thread/thread_main.c new file mode 100644 index 000000000..53c756551 --- /dev/null +++ b/test/common_plat/validation/api/thread/thread_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "thread.h" + +int main(int argc, char *argv[]) +{ + return thread_main(argc, argv); +} diff --git a/test/common_plat/validation/api/time/.gitignore b/test/common_plat/validation/api/time/.gitignore new file mode 100644 index 000000000..0ef3e6162 --- /dev/null +++ b/test/common_plat/validation/api/time/.gitignore @@ -0,0 +1 @@ +time_main diff --git a/test/common_plat/validation/api/time/Makefile.am b/test/common_plat/validation/api/time/Makefile.am new file mode 100644 index 000000000..bf2d0268c --- /dev/null +++ b/test/common_plat/validation/api/time/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtesttime.la +libtesttime_la_SOURCES = time.c + +test_PROGRAMS = time_main$(EXEEXT) +dist_time_main_SOURCES = time_main.c +time_main_LDADD = libtesttime.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = time.h diff --git a/test/common_plat/validation/api/time/time.c b/test/common_plat/validation/api/time/time.c new file mode 100644 index 000000000..530d5c07a --- /dev/null +++ b/test/common_plat/validation/api/time/time.c @@ -0,0 +1,476 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_api.h> +#include "odp_cunit_common.h" +#include "time.h" + +#define BUSY_LOOP_CNT 30000000 /* used for t > min resolution */ +#define BUSY_LOOP_CNT_LONG 6000000000 /* used for t > 4 sec */ +#define MIN_TIME_RATE 32000 +#define MAX_TIME_RATE 15000000000 +#define DELAY_TOLERANCE 20000000 /* deviation for delay */ +#define WAIT_SECONDS 3 + +static uint64_t local_res; +static uint64_t global_res; + +typedef odp_time_t time_cb(void); +typedef uint64_t time_res_cb(void); +typedef odp_time_t time_from_ns_cb(uint64_t ns); + +void time_test_constants(void) +{ + uint64_t ns; + + ns = ODP_TIME_SEC_IN_NS / 1000; + CU_ASSERT(ns == ODP_TIME_MSEC_IN_NS); + ns /= 1000; + CU_ASSERT(ns == ODP_TIME_USEC_IN_NS); +} + +static void time_test_res(time_res_cb time_res, uint64_t *res) +{ + uint64_t rate; + + rate = time_res(); + CU_ASSERT(rate > MIN_TIME_RATE); + CU_ASSERT(rate < MAX_TIME_RATE); + + *res = ODP_TIME_SEC_IN_NS / rate; + if (ODP_TIME_SEC_IN_NS % rate) + (*res)++; +} + +void time_test_local_res(void) +{ + time_test_res(odp_time_local_res, &local_res); +} + +void time_test_global_res(void) +{ + time_test_res(odp_time_global_res, &global_res); +} + +/* check that related conversions come back to the same value */ +static void time_test_conversion(time_from_ns_cb time_from_ns, uint64_t res) +{ + uint64_t ns1, ns2; + odp_time_t time; + uint64_t upper_limit, lower_limit; + + ns1 = 100; + time = time_from_ns(ns1); + + ns2 = odp_time_to_ns(time); + + /* need to check within arithmetic tolerance that the same + * value in ns is returned after conversions */ + upper_limit = ns1 + res; + lower_limit = ns1 - res; + CU_ASSERT((ns2 <= upper_limit) && (ns2 >= lower_limit)); + + ns1 = 60 * 11 * ODP_TIME_SEC_IN_NS; + time = time_from_ns(ns1); + + ns2 = odp_time_to_ns(time); + + /* need to check within arithmetic tolerance that the same + * value in ns is returned after conversions */ + upper_limit = ns1 + res; + lower_limit = ns1 - res; + CU_ASSERT((ns2 <= upper_limit) && (ns2 >= lower_limit)); + + /* test on 0 */ + ns1 = odp_time_to_ns(ODP_TIME_NULL); + CU_ASSERT(ns1 == 0); +} + +void time_test_local_conversion(void) +{ + time_test_conversion(odp_time_local_from_ns, local_res); +} + +void time_test_global_conversion(void) +{ + time_test_conversion(odp_time_global_from_ns, global_res); +} + +void time_test_monotony(void) +{ + volatile uint64_t count = 0; + odp_time_t l_t1, l_t2, l_t3; + odp_time_t g_t1, g_t2, g_t3; + uint64_t ns1, ns2, ns3; + + l_t1 = odp_time_local(); + g_t1 = odp_time_global(); + + while (count < BUSY_LOOP_CNT) { + count++; + }; + + l_t2 = odp_time_local(); + g_t2 = odp_time_global(); + + while (count < BUSY_LOOP_CNT_LONG) { + count++; + }; + + l_t3 = odp_time_local(); + g_t3 = odp_time_global(); + + ns1 = odp_time_to_ns(l_t1); + ns2 = odp_time_to_ns(l_t2); + ns3 = odp_time_to_ns(l_t3); + + /* Local time assertions */ + CU_ASSERT(ns2 > ns1); + CU_ASSERT(ns3 > ns2); + + ns1 = odp_time_to_ns(g_t1); + ns2 = odp_time_to_ns(g_t2); + ns3 = odp_time_to_ns(g_t3); + + /* Global time assertions */ + CU_ASSERT(ns2 > ns1); + CU_ASSERT(ns3 > ns2); +} + +static void time_test_cmp(time_cb time, time_from_ns_cb time_from_ns) +{ + /* volatile to stop optimization of busy loop */ + volatile int count = 0; + odp_time_t t1, t2, t3; + + t1 = time(); + + while (count < BUSY_LOOP_CNT) { + count++; + }; + + t2 = time(); + + while (count < BUSY_LOOP_CNT * 2) { + count++; + }; + + t3 = time(); + + CU_ASSERT(odp_time_cmp(t2, t1) > 0); + CU_ASSERT(odp_time_cmp(t3, t2) > 0); + CU_ASSERT(odp_time_cmp(t3, t1) > 0); + CU_ASSERT(odp_time_cmp(t1, t2) < 0); + CU_ASSERT(odp_time_cmp(t2, t3) < 0); + CU_ASSERT(odp_time_cmp(t1, t3) < 0); + CU_ASSERT(odp_time_cmp(t1, t1) == 0); + CU_ASSERT(odp_time_cmp(t2, t2) == 0); + CU_ASSERT(odp_time_cmp(t3, t3) == 0); + + t2 = time_from_ns(60 * 10 * ODP_TIME_SEC_IN_NS); + t1 = time_from_ns(3); + + CU_ASSERT(odp_time_cmp(t2, t1) > 0); + CU_ASSERT(odp_time_cmp(t1, t2) < 0); + + t1 = time_from_ns(0); + CU_ASSERT(odp_time_cmp(t1, ODP_TIME_NULL) == 0); +} + +void time_test_local_cmp(void) +{ + time_test_cmp(odp_time_local, odp_time_local_from_ns); +} + +void time_test_global_cmp(void) +{ + time_test_cmp(odp_time_global, odp_time_global_from_ns); +} + +/* check that a time difference gives a reasonable result */ +static void time_test_diff(time_cb time, + time_from_ns_cb time_from_ns, + uint64_t res) +{ + /* volatile to stop optimization of busy loop */ + volatile int count = 0; + odp_time_t diff, t1, t2; + uint64_t nsdiff, ns1, ns2, ns; + uint64_t upper_limit, lower_limit; + + /* test timestamp diff */ + t1 = time(); + + while (count < BUSY_LOOP_CNT) { + count++; + }; + + t2 = time(); + CU_ASSERT(odp_time_cmp(t2, t1) > 0); + + diff = odp_time_diff(t2, t1); + CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0); + + ns1 = odp_time_to_ns(t1); + ns2 = odp_time_to_ns(t2); + ns = ns2 - ns1; + nsdiff = odp_time_to_ns(diff); + + upper_limit = ns + 2 * res; + lower_limit = ns - 2 * res; + CU_ASSERT((nsdiff <= upper_limit) && (nsdiff >= lower_limit)); + + /* test timestamp and interval diff */ + ns1 = 54; + t1 = time_from_ns(ns1); + ns = ns2 - ns1; + + diff = odp_time_diff(t2, t1); + CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0); + nsdiff = odp_time_to_ns(diff); + + upper_limit = ns + 2 * res; + lower_limit = ns - 2 * res; + CU_ASSERT((nsdiff <= upper_limit) && (nsdiff >= lower_limit)); + + /* test interval diff */ + ns2 = 60 * 10 * ODP_TIME_SEC_IN_NS; + ns = ns2 - ns1; + + t2 = time_from_ns(ns2); + diff = odp_time_diff(t2, t1); + CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0); + nsdiff = odp_time_to_ns(diff); + + upper_limit = ns + 2 * res; + lower_limit = ns - 2 * res; + CU_ASSERT((nsdiff <= upper_limit) && (nsdiff >= lower_limit)); + + /* same time has to diff to 0 */ + diff = odp_time_diff(t2, t2); + CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) == 0); + + diff = odp_time_diff(t2, ODP_TIME_NULL); + CU_ASSERT(odp_time_cmp(t2, diff) == 0); +} + +void time_test_local_diff(void) +{ + time_test_diff(odp_time_local, odp_time_local_from_ns, local_res); +} + +void time_test_global_diff(void) +{ + time_test_diff(odp_time_global, odp_time_global_from_ns, global_res); +} + +/* check that a time sum gives a reasonable result */ +static void time_test_sum(time_cb time, + time_from_ns_cb time_from_ns, + uint64_t res) +{ + odp_time_t sum, t1, t2; + uint64_t nssum, ns1, ns2, ns; + uint64_t upper_limit, lower_limit; + + /* sum timestamp and interval */ + t1 = time(); + ns2 = 103; + t2 = time_from_ns(ns2); + ns1 = odp_time_to_ns(t1); + ns = ns1 + ns2; + + sum = odp_time_sum(t2, t1); + CU_ASSERT(odp_time_cmp(sum, ODP_TIME_NULL) > 0); + nssum = odp_time_to_ns(sum); + + upper_limit = ns + 2 * res; + lower_limit = ns - 2 * res; + CU_ASSERT((nssum <= upper_limit) && (nssum >= lower_limit)); + + /* sum intervals */ + ns1 = 60 * 13 * ODP_TIME_SEC_IN_NS; + t1 = time_from_ns(ns1); + ns = ns1 + ns2; + + sum = odp_time_sum(t2, t1); + CU_ASSERT(odp_time_cmp(sum, ODP_TIME_NULL) > 0); + nssum = odp_time_to_ns(sum); + + upper_limit = ns + 2 * res; + lower_limit = ns - 2 * res; + CU_ASSERT((nssum <= upper_limit) && (nssum >= lower_limit)); + + /* test on 0 */ + sum = odp_time_sum(t2, ODP_TIME_NULL); + CU_ASSERT(odp_time_cmp(t2, sum) == 0); +} + +void time_test_local_sum(void) +{ + time_test_sum(odp_time_local, odp_time_local_from_ns, local_res); +} + +void time_test_global_sum(void) +{ + time_test_sum(odp_time_global, odp_time_global_from_ns, global_res); +} + +static void time_test_wait_until(time_cb time, time_from_ns_cb time_from_ns) +{ + int i; + odp_time_t lower_limit, upper_limit; + odp_time_t start_time, end_time, wait; + odp_time_t second = time_from_ns(ODP_TIME_SEC_IN_NS); + + start_time = time(); + wait = start_time; + for (i = 0; i < WAIT_SECONDS; i++) { + wait = odp_time_sum(wait, second); + odp_time_wait_until(wait); + } + end_time = time(); + + wait = odp_time_diff(end_time, start_time); + lower_limit = time_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS - + DELAY_TOLERANCE); + upper_limit = time_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS + + DELAY_TOLERANCE); + + if (odp_time_cmp(wait, lower_limit) < 0) { + fprintf(stderr, "Exceed lower limit: " + "wait is %" PRIu64 ", lower_limit %" PRIu64 "\n", + odp_time_to_ns(wait), odp_time_to_ns(lower_limit)); + CU_FAIL("Exceed lower limit\n"); + } + + if (odp_time_cmp(wait, upper_limit) > 0) { + fprintf(stderr, "Exceed upper limit: " + "wait is %" PRIu64 ", upper_limit %" PRIu64 "\n", + odp_time_to_ns(wait), odp_time_to_ns(lower_limit)); + CU_FAIL("Exceed upper limit\n"); + } +} + +void time_test_local_wait_until(void) +{ + time_test_wait_until(odp_time_local, odp_time_local_from_ns); +} + +void time_test_global_wait_until(void) +{ + time_test_wait_until(odp_time_global, odp_time_global_from_ns); +} + +void time_test_wait_ns(void) +{ + int i; + odp_time_t lower_limit, upper_limit; + odp_time_t start_time, end_time, diff; + + start_time = odp_time_local(); + for (i = 0; i < WAIT_SECONDS; i++) + odp_time_wait_ns(ODP_TIME_SEC_IN_NS); + end_time = odp_time_local(); + + diff = odp_time_diff(end_time, start_time); + + lower_limit = odp_time_local_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS - + DELAY_TOLERANCE); + upper_limit = odp_time_local_from_ns(WAIT_SECONDS * ODP_TIME_SEC_IN_NS + + DELAY_TOLERANCE); + + if (odp_time_cmp(diff, lower_limit) < 0) { + fprintf(stderr, "Exceed lower limit: " + "diff is %" PRIu64 ", lower_limit %" PRIu64 "\n", + odp_time_to_ns(diff), odp_time_to_ns(lower_limit)); + CU_FAIL("Exceed lower limit\n"); + } + + if (odp_time_cmp(diff, upper_limit) > 0) { + fprintf(stderr, "Exceed upper limit: " + "diff is %" PRIu64 ", upper_limit %" PRIu64 "\n", + odp_time_to_ns(diff), odp_time_to_ns(lower_limit)); + CU_FAIL("Exceed upper limit\n"); + } +} + +static void time_test_to_u64(time_cb time) +{ + volatile int count = 0; + uint64_t val1, val2; + odp_time_t t1, t2; + + t1 = time(); + + val1 = odp_time_to_u64(t1); + CU_ASSERT(val1 > 0); + + while (count < BUSY_LOOP_CNT) { + count++; + }; + + t2 = time(); + val2 = odp_time_to_u64(t2); + CU_ASSERT(val2 > 0); + + CU_ASSERT(val2 > val1); + + val1 = odp_time_to_u64(ODP_TIME_NULL); + CU_ASSERT(val1 == 0); +} + +void time_test_local_to_u64(void) +{ + time_test_to_u64(odp_time_local); +} + +void time_test_global_to_u64(void) +{ + time_test_to_u64(odp_time_global); +} + +odp_testinfo_t time_suite_time[] = { + ODP_TEST_INFO(time_test_constants), + ODP_TEST_INFO(time_test_local_res), + ODP_TEST_INFO(time_test_local_conversion), + ODP_TEST_INFO(time_test_monotony), + ODP_TEST_INFO(time_test_local_cmp), + ODP_TEST_INFO(time_test_local_diff), + ODP_TEST_INFO(time_test_local_sum), + ODP_TEST_INFO(time_test_local_wait_until), + ODP_TEST_INFO(time_test_wait_ns), + ODP_TEST_INFO(time_test_local_to_u64), + ODP_TEST_INFO(time_test_global_res), + ODP_TEST_INFO(time_test_global_conversion), + ODP_TEST_INFO(time_test_global_cmp), + ODP_TEST_INFO(time_test_global_diff), + ODP_TEST_INFO(time_test_global_sum), + ODP_TEST_INFO(time_test_global_wait_until), + ODP_TEST_INFO(time_test_global_to_u64), + ODP_TEST_INFO_NULL +}; + +odp_suiteinfo_t time_suites[] = { + {"Time", NULL, NULL, time_suite_time}, + ODP_SUITE_INFO_NULL +}; + +int time_main(int argc, char *argv[]) +{ + int ret; + + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + ret = odp_cunit_register(time_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/time/time.h b/test/common_plat/validation/api/time/time.h new file mode 100644 index 000000000..e5132a494 --- /dev/null +++ b/test/common_plat/validation/api/time/time.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_TIME_H_ +#define _ODP_TEST_TIME_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void time_test_constants(void); +void time_test_local_res(void); +void time_test_global_res(void); +void time_test_local_conversion(void); +void time_test_global_conversion(void); +void time_test_local_cmp(void); +void time_test_global_cmp(void); +void time_test_local_diff(void); +void time_test_global_diff(void); +void time_test_local_sum(void); +void time_test_global_sum(void); +void time_test_local_wait_until(void); +void time_test_global_wait_until(void); +void time_test_wait_ns(void); +void time_test_local_to_u64(void); +void time_test_global_to_u64(void); +void time_test_monotony(void); + +/* test arrays: */ +extern odp_testinfo_t time_suite_time[]; + +/* test registry: */ +extern odp_suiteinfo_t time_suites[]; + +/* main test program: */ +int time_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/time/time_main.c b/test/common_plat/validation/api/time/time_main.c new file mode 100644 index 000000000..f86d638a5 --- /dev/null +++ b/test/common_plat/validation/api/time/time_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "time.h" + +int main(int argc, char *argv[]) +{ + return time_main(argc, argv); +} diff --git a/test/common_plat/validation/api/timer/.gitignore b/test/common_plat/validation/api/timer/.gitignore new file mode 100644 index 000000000..74e8fa992 --- /dev/null +++ b/test/common_plat/validation/api/timer/.gitignore @@ -0,0 +1 @@ +timer_main diff --git a/test/common_plat/validation/api/timer/Makefile.am b/test/common_plat/validation/api/timer/Makefile.am new file mode 100644 index 000000000..fe6872f41 --- /dev/null +++ b/test/common_plat/validation/api/timer/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtesttimer.la +libtesttimer_la_SOURCES = timer.c + +test_PROGRAMS = timer_main$(EXEEXT) +dist_timer_main_SOURCES = timer_main.c +timer_main_LDADD = libtesttimer.la $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = timer.h diff --git a/test/common_plat/validation/api/timer/timer.c b/test/common_plat/validation/api/timer/timer.c new file mode 100644 index 000000000..0007639cc --- /dev/null +++ b/test/common_plat/validation/api/timer/timer.c @@ -0,0 +1,605 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + */ + +/* For rand_r and nanosleep */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <time.h> +#include <odp.h> +#include <odp/helper/linux.h> +#include "odp_cunit_common.h" +#include "test_debug.h" +#include "timer.h" + +/** @private Timeout range in milliseconds (ms) */ +#define RANGE_MS 2000 + +/** @private Number of timers per thread */ +#define NTIMERS 2000 + +/** @private Barrier for thread synchronisation */ +static odp_barrier_t test_barrier; + +/** @private Timeout pool handle used by all threads */ +static odp_pool_t tbp; + +/** @private Timer pool handle used by all threads */ +static odp_timer_pool_t tp; + +/** @private Count of timeouts delivered too late */ +static odp_atomic_u32_t ndelivtoolate; + +/** @private Sum of all allocated timers from all threads. Thread-local + * caches may make this number lower than the capacity of the pool */ +static odp_atomic_u32_t timers_allocated; + +/* @private Timer helper structure */ +struct test_timer { + odp_timer_t tim; /* Timer handle */ + odp_event_t ev; /* Timeout event */ + odp_event_t ev2; /* Copy of event handle */ + uint64_t tick; /* Expiration tick or TICK_INVALID */ +}; + +#define TICK_INVALID (~(uint64_t)0) + +void timer_test_timeout_pool_alloc(void) +{ + odp_pool_t pool; + const int num = 3; + odp_timeout_t tmo[num]; + odp_event_t ev; + int index; + char wrong_type = 0; + odp_pool_param_t params; + + odp_pool_param_init(¶ms); + params.type = ODP_POOL_TIMEOUT; + params.tmo.num = num; + + pool = odp_pool_create("timeout_pool_alloc", ¶ms); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + + odp_pool_print(pool); + + /* Try to allocate num items from the pool */ + for (index = 0; index < num; index++) { + tmo[index] = odp_timeout_alloc(pool); + + if (tmo[index] == ODP_TIMEOUT_INVALID) + break; + + ev = odp_timeout_to_event(tmo[index]); + if (odp_event_type(ev) != ODP_EVENT_TIMEOUT) + wrong_type = 1; + } + + /* Check that the pool had at least num items */ + CU_ASSERT(index == num); + /* index points out of buffer[] or it point to an invalid buffer */ + index--; + + /* Check that the pool had correct buffers */ + CU_ASSERT(wrong_type == 0); + + for (; index >= 0; index--) + odp_timeout_free(tmo[index]); + + CU_ASSERT(odp_pool_destroy(pool) == 0); +} + +void timer_test_timeout_pool_free(void) +{ + odp_pool_t pool; + odp_timeout_t tmo; + odp_pool_param_t params; + + odp_pool_param_init(¶ms); + params.type = ODP_POOL_TIMEOUT; + params.tmo.num = 1; + + pool = odp_pool_create("timeout_pool_free", ¶ms); + CU_ASSERT_FATAL(pool != ODP_POOL_INVALID); + odp_pool_print(pool); + + /* Allocate the only timeout from the pool */ + tmo = odp_timeout_alloc(pool); + CU_ASSERT_FATAL(tmo != ODP_TIMEOUT_INVALID); + + /* Pool should have only one timeout */ + CU_ASSERT_FATAL(odp_timeout_alloc(pool) == ODP_TIMEOUT_INVALID) + + odp_timeout_free(tmo); + + /* Check that the timeout was returned back to the pool */ + tmo = odp_timeout_alloc(pool); + CU_ASSERT_FATAL(tmo != ODP_TIMEOUT_INVALID); + + odp_timeout_free(tmo); + CU_ASSERT(odp_pool_destroy(pool) == 0); +} + +void timer_test_odp_timer_cancel(void) +{ + odp_pool_t pool; + odp_pool_param_t params; + odp_timer_pool_param_t tparam; + odp_timer_pool_t tp; + odp_queue_t queue; + odp_timer_t tim; + odp_event_t ev; + odp_timeout_t tmo; + odp_timer_set_t rc; + uint64_t tick; + + odp_pool_param_init(¶ms); + params.type = ODP_POOL_TIMEOUT; + params.tmo.num = 1; + + pool = odp_pool_create("tmo_pool_for_cancel", ¶ms); + + if (pool == ODP_POOL_INVALID) + CU_FAIL_FATAL("Timeout pool create failed"); + + tparam.res_ns = 100 * ODP_TIME_MSEC_IN_NS; + tparam.min_tmo = 1 * ODP_TIME_SEC_IN_NS; + tparam.max_tmo = 10 * ODP_TIME_SEC_IN_NS; + tparam.num_timers = 1; + tparam.priv = 0; + tparam.clk_src = ODP_CLOCK_CPU; + tp = odp_timer_pool_create("timer_pool0", &tparam); + if (tp == ODP_TIMER_POOL_INVALID) + CU_FAIL_FATAL("Timer pool create failed"); + + /* Start all created timer pools */ + odp_timer_pool_start(); + + queue = odp_queue_create("timer_queue", NULL); + if (queue == ODP_QUEUE_INVALID) + CU_FAIL_FATAL("Queue create failed"); + + #define USER_PTR ((void *)0xdead) + tim = odp_timer_alloc(tp, queue, USER_PTR); + if (tim == ODP_TIMER_INVALID) + CU_FAIL_FATAL("Failed to allocate timer"); + LOG_DBG("Timer handle: %" PRIu64 "\n", odp_timer_to_u64(tim)); + + ev = odp_timeout_to_event(odp_timeout_alloc(pool)); + if (ev == ODP_EVENT_INVALID) + CU_FAIL_FATAL("Failed to allocate timeout"); + + tick = odp_timer_ns_to_tick(tp, 2 * ODP_TIME_SEC_IN_NS); + + rc = odp_timer_set_rel(tim, tick, &ev); + if (rc != ODP_TIMER_SUCCESS) + CU_FAIL_FATAL("Failed to set timer (relative time)"); + + ev = ODP_EVENT_INVALID; + if (odp_timer_cancel(tim, &ev) != 0) + CU_FAIL_FATAL("Failed to cancel timer (relative time)"); + + if (ev == ODP_EVENT_INVALID) + CU_FAIL_FATAL("Cancel did not return event"); + + tmo = odp_timeout_from_event(ev); + if (tmo == ODP_TIMEOUT_INVALID) + CU_FAIL_FATAL("Cancel did not return timeout"); + LOG_DBG("Timeout handle: %" PRIu64 "\n", odp_timeout_to_u64(tmo)); + + if (odp_timeout_timer(tmo) != tim) + CU_FAIL("Cancel invalid tmo.timer"); + + if (odp_timeout_user_ptr(tmo) != USER_PTR) + CU_FAIL("Cancel invalid tmo.user_ptr"); + + odp_timeout_free(tmo); + + ev = odp_timer_free(tim); + if (ev != ODP_EVENT_INVALID) + CU_FAIL_FATAL("Free returned event"); + + odp_timer_pool_destroy(tp); + + if (odp_queue_destroy(queue) != 0) + CU_FAIL_FATAL("Failed to destroy queue"); + + if (odp_pool_destroy(pool) != 0) + CU_FAIL_FATAL("Failed to destroy pool"); +} + +/* @private Handle a received (timeout) event */ +static void handle_tmo(odp_event_t ev, bool stale, uint64_t prev_tick) +{ + CU_ASSERT_FATAL(ev != ODP_EVENT_INVALID); /* Internal error */ + if (odp_event_type(ev) != ODP_EVENT_TIMEOUT) { + /* Not a timeout event */ + CU_FAIL("Unexpected event type received"); + return; + } + /* Read the metadata from the timeout */ + odp_timeout_t tmo = odp_timeout_from_event(ev); + odp_timer_t tim = odp_timeout_timer(tmo); + uint64_t tick = odp_timeout_tick(tmo); + struct test_timer *ttp = odp_timeout_user_ptr(tmo); + + if (tim == ODP_TIMER_INVALID) + CU_FAIL("odp_timeout_timer() invalid timer"); + if (!ttp) + CU_FAIL("odp_timeout_user_ptr() null user ptr"); + + if (ttp && ttp->ev2 != ev) + CU_FAIL("odp_timeout_user_ptr() wrong user ptr"); + if (ttp && ttp->tim != tim) + CU_FAIL("odp_timeout_timer() wrong timer"); + if (stale) { + if (odp_timeout_fresh(tmo)) + CU_FAIL("Wrong status (fresh) for stale timeout"); + /* Stale timeout => local timer must have invalid tick */ + if (ttp && ttp->tick != TICK_INVALID) + CU_FAIL("Stale timeout for active timer"); + } else { + if (!odp_timeout_fresh(tmo)) + CU_FAIL("Wrong status (stale) for fresh timeout"); + /* Fresh timeout => local timer must have matching tick */ + if (ttp && ttp->tick != tick) { + LOG_DBG("Wrong tick: expected %" PRIu64 + " actual %" PRIu64 "\n", + ttp->tick, tick); + CU_FAIL("odp_timeout_tick() wrong tick"); + } + /* Check that timeout was delivered 'timely' */ + if (tick > odp_timer_current_tick(tp)) + CU_FAIL("Timeout delivered early"); + if (tick < prev_tick) { + LOG_DBG("Too late tick: %" PRIu64 + " prev_tick %" PRIu64"\n", + tick, prev_tick); + /* We don't report late timeouts using CU_FAIL */ + odp_atomic_inc_u32(&ndelivtoolate); + } + } + + if (ttp) { + /* Internal error */ + CU_ASSERT_FATAL(ttp->ev == ODP_EVENT_INVALID); + ttp->ev = ev; + } +} + +/* @private Worker thread entrypoint which performs timer alloc/set/cancel/free + * tests */ +static int worker_entrypoint(void *arg TEST_UNUSED) +{ + int thr = odp_thread_id(); + uint32_t i, allocated; + unsigned seed = thr; + int rc; + odp_queue_t queue; + struct test_timer *tt; + uint32_t nset; + uint64_t tck; + uint32_t nrcv; + uint32_t nreset; + uint32_t ncancel; + uint32_t ntoolate; + uint32_t ms; + uint64_t prev_tick; + odp_event_t ev; + struct timespec ts; + uint32_t nstale; + odp_timer_set_t timer_rc; + + queue = odp_queue_create("timer_queue", NULL); + if (queue == ODP_QUEUE_INVALID) + CU_FAIL_FATAL("Queue create failed"); + + tt = malloc(sizeof(struct test_timer) * NTIMERS); + if (!tt) + CU_FAIL_FATAL("malloc failed"); + + /* Prepare all timers */ + for (i = 0; i < NTIMERS; i++) { + tt[i].ev = odp_timeout_to_event(odp_timeout_alloc(tbp)); + if (tt[i].ev == ODP_EVENT_INVALID) { + LOG_DBG("Failed to allocate timeout (%" PRIu32 "/%d)\n", + i, NTIMERS); + break; + } + tt[i].tim = odp_timer_alloc(tp, queue, &tt[i]); + if (tt[i].tim == ODP_TIMER_INVALID) { + LOG_DBG("Failed to allocate timer (%" PRIu32 "/%d)\n", + i, NTIMERS); + odp_event_free(tt[i].ev); + break; + } + tt[i].ev2 = tt[i].ev; + tt[i].tick = TICK_INVALID; + } + allocated = i; + if (allocated == 0) + CU_FAIL_FATAL("unable to alloc a timer"); + odp_atomic_fetch_add_u32(&timers_allocated, allocated); + + odp_barrier_wait(&test_barrier); + + /* Initial set all timers with a random expiration time */ + nset = 0; + for (i = 0; i < allocated; i++) { + tck = odp_timer_current_tick(tp) + 1 + + odp_timer_ns_to_tick(tp, (rand_r(&seed) % RANGE_MS) + * 1000000ULL); + timer_rc = odp_timer_set_abs(tt[i].tim, tck, &tt[i].ev); + if (timer_rc != ODP_TIMER_SUCCESS) { + CU_FAIL("Failed to set timer"); + } else { + tt[i].tick = tck; + nset++; + } + } + + /* Step through wall time, 1ms at a time and check for expired timers */ + nrcv = 0; + nreset = 0; + ncancel = 0; + ntoolate = 0; + prev_tick = odp_timer_current_tick(tp); + + for (ms = 0; ms < 7 * RANGE_MS / 10 && allocated > 0; ms++) { + while ((ev = odp_queue_deq(queue)) != ODP_EVENT_INVALID) { + /* Subtract one from prev_tick to allow for timeouts + * to be delivered a tick late */ + handle_tmo(ev, false, prev_tick - 1); + nrcv++; + } + prev_tick = odp_timer_current_tick(tp); + i = rand_r(&seed) % allocated; + if (tt[i].ev == ODP_EVENT_INVALID && + (rand_r(&seed) % 2 == 0)) { + /* Timer active, cancel it */ + rc = odp_timer_cancel(tt[i].tim, &tt[i].ev); + if (rc != 0) + /* Cancel failed, timer already expired */ + ntoolate++; + tt[i].tick = TICK_INVALID; + ncancel++; + } else { + if (tt[i].ev != ODP_EVENT_INVALID) + /* Timer inactive => set */ + nset++; + else + /* Timer active => reset */ + nreset++; + uint64_t tck = 1 + odp_timer_ns_to_tick(tp, + (rand_r(&seed) % RANGE_MS) * 1000000ULL); + odp_timer_set_t rc; + uint64_t cur_tick; + /* Loop until we manage to read cur_tick and set a + * relative timer in the same tick */ + do { + cur_tick = odp_timer_current_tick(tp); + rc = odp_timer_set_rel(tt[i].tim, + tck, &tt[i].ev); + } while (cur_tick != odp_timer_current_tick(tp)); + if (rc == ODP_TIMER_TOOEARLY || + rc == ODP_TIMER_TOOLATE) { + CU_FAIL("Failed to set timer (tooearly/toolate)"); + } else if (rc != ODP_TIMER_SUCCESS) { + /* Set/reset failed, timer already expired */ + ntoolate++; + } else if (rc == ODP_TIMER_SUCCESS) { + /* Save expected expiration tick on success */ + tt[i].tick = cur_tick + tck; + } + } + ts.tv_sec = 0; + ts.tv_nsec = 1000000; /* 1ms */ + if (nanosleep(&ts, NULL) < 0) + CU_FAIL_FATAL("nanosleep failed"); + } + + /* Cancel and free all timers */ + nstale = 0; + for (i = 0; i < allocated; i++) { + (void)odp_timer_cancel(tt[i].tim, &tt[i].ev); + tt[i].tick = TICK_INVALID; + if (tt[i].ev == ODP_EVENT_INVALID) + /* Cancel too late, timer already expired and + * timeout enqueued */ + nstale++; + } + + LOG_DBG("Thread %u: %" PRIu32 " timers set\n", thr, nset); + LOG_DBG("Thread %u: %" PRIu32 " timers reset\n", thr, nreset); + LOG_DBG("Thread %u: %" PRIu32 " timers cancelled\n", thr, ncancel); + LOG_DBG("Thread %u: %" PRIu32 " timers reset/cancelled too late\n", + thr, ntoolate); + LOG_DBG("Thread %u: %" PRIu32 " timeouts received\n", thr, nrcv); + LOG_DBG("Thread %u: %" PRIu32 + " stale timeout(s) after odp_timer_free()\n", + thr, nstale); + + /* Delay some more to ensure timeouts for expired timers can be + * received. Can not use busy loop here to make background timer + * thread finish their work. */ + ts.tv_sec = 0; + ts.tv_nsec = (3 * RANGE_MS / 10 + 50) * ODP_TIME_MSEC_IN_NS; + if (nanosleep(&ts, NULL) < 0) + CU_FAIL_FATAL("nanosleep failed"); + + while (nstale != 0) { + ev = odp_queue_deq(queue); + if (ev != ODP_EVENT_INVALID) { + handle_tmo(ev, true, 0/*Don't care for stale tmo's*/); + nstale--; + } else { + CU_FAIL("Failed to receive stale timeout"); + break; + } + } + + for (i = 0; i < allocated; i++) { + if (odp_timer_free(tt[i].tim) != ODP_EVENT_INVALID) + CU_FAIL("odp_timer_free"); + } + + /* Check if there any more (unexpected) events */ + ev = odp_queue_deq(queue); + if (ev != ODP_EVENT_INVALID) + CU_FAIL("Unexpected event received"); + + rc = odp_queue_destroy(queue); + CU_ASSERT(rc == 0); + for (i = 0; i < allocated; i++) { + if (tt[i].ev != ODP_EVENT_INVALID) + odp_event_free(tt[i].ev); + } + + free(tt); + LOG_DBG("Thread %u: exiting\n", thr); + return CU_get_number_of_failures(); +} + +/* @private Timer test case entrypoint */ +void timer_test_odp_timer_all(void) +{ + int rc; + odp_pool_param_t params; + odp_timer_pool_param_t tparam; + odp_cpumask_t unused; + odp_timer_pool_info_t tpinfo; + uint64_t tick; + uint64_t ns; + uint64_t t2; + pthrd_arg thrdarg; + + /* Reserve at least one core for running other processes so the timer + * test hopefully can run undisturbed and thus get better timing + * results. */ + int num_workers = odp_cpumask_default_worker(&unused, 0); + + /* force to max CPU count */ + if (num_workers > MAX_WORKERS) + num_workers = MAX_WORKERS; + + /* On a single-CPU machine run at least one thread */ + if (num_workers < 1) + num_workers = 1; + + /* Create timeout pools */ + odp_pool_param_init(¶ms); + params.type = ODP_POOL_TIMEOUT; + params.tmo.num = (NTIMERS + 1) * num_workers; + + tbp = odp_pool_create("tmo_pool", ¶ms); + if (tbp == ODP_POOL_INVALID) + CU_FAIL_FATAL("Timeout pool create failed"); + +#define NAME "timer_pool" +#define RES (10 * ODP_TIME_MSEC_IN_NS / 3) +#define MIN (10 * ODP_TIME_MSEC_IN_NS / 3) +#define MAX (1000000 * ODP_TIME_MSEC_IN_NS) + /* Create a timer pool */ + tparam.res_ns = RES; + tparam.min_tmo = MIN; + tparam.max_tmo = MAX; + tparam.num_timers = num_workers * NTIMERS; + tparam.priv = 0; + tparam.clk_src = ODP_CLOCK_CPU; + tp = odp_timer_pool_create(NAME, &tparam); + if (tp == ODP_TIMER_POOL_INVALID) + CU_FAIL_FATAL("Timer pool create failed"); + + /* Start all created timer pools */ + odp_timer_pool_start(); + + if (odp_timer_pool_info(tp, &tpinfo) != 0) + CU_FAIL("odp_timer_pool_info"); + CU_ASSERT(strcmp(tpinfo.name, NAME) == 0); + CU_ASSERT(tpinfo.param.res_ns == RES); + CU_ASSERT(tpinfo.param.min_tmo == MIN); + CU_ASSERT(tpinfo.param.max_tmo == MAX); + CU_ASSERT(strcmp(tpinfo.name, NAME) == 0); + + LOG_DBG("Timer pool handle: %" PRIu64 "\n", odp_timer_pool_to_u64(tp)); + LOG_DBG("#timers..: %u\n", NTIMERS); + LOG_DBG("Tmo range: %u ms (%" PRIu64 " ticks)\n", RANGE_MS, + odp_timer_ns_to_tick(tp, 1000000ULL * RANGE_MS)); + + for (tick = 0; tick < 1000000000000ULL; tick += 1000000ULL) { + ns = odp_timer_tick_to_ns(tp, tick); + t2 = odp_timer_ns_to_tick(tp, ns); + if (tick != t2) + CU_FAIL("Invalid conversion tick->ns->tick"); + } + + /* Initialize barrier used by worker threads for synchronization */ + odp_barrier_init(&test_barrier, num_workers); + + /* Initialize the shared timeout counter */ + odp_atomic_init_u32(&ndelivtoolate, 0); + + /* Initialize the number of finally allocated elements */ + odp_atomic_init_u32(&timers_allocated, 0); + + /* Create and start worker threads */ + thrdarg.testcase = 0; + thrdarg.numthrds = num_workers; + odp_cunit_thread_create(worker_entrypoint, &thrdarg); + + /* Wait for worker threads to exit */ + odp_cunit_thread_exit(&thrdarg); + LOG_DBG("Number of timeouts delivered/received too late: %" PRIu32 "\n", + odp_atomic_load_u32(&ndelivtoolate)); + + /* Check some statistics after the test */ + if (odp_timer_pool_info(tp, &tpinfo) != 0) + CU_FAIL("odp_timer_pool_info"); + CU_ASSERT(tpinfo.param.num_timers == (unsigned)num_workers * NTIMERS); + CU_ASSERT(tpinfo.cur_timers == 0); + CU_ASSERT(tpinfo.hwm_timers == odp_atomic_load_u32(&timers_allocated)); + + /* Destroy timer pool, all timers must have been freed */ + odp_timer_pool_destroy(tp); + + /* Destroy timeout pool, all timeouts must have been freed */ + rc = odp_pool_destroy(tbp); + CU_ASSERT(rc == 0); + + CU_PASS("ODP timer test"); +} + +odp_testinfo_t timer_suite[] = { + ODP_TEST_INFO(timer_test_timeout_pool_alloc), + ODP_TEST_INFO(timer_test_timeout_pool_free), + ODP_TEST_INFO(timer_test_odp_timer_cancel), + ODP_TEST_INFO(timer_test_odp_timer_all), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t timer_suites[] = { + {"Timer", NULL, NULL, timer_suite}, + ODP_SUITE_INFO_NULL, +}; + +int timer_main(int argc, char *argv[]) +{ + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + int ret = odp_cunit_register(timer_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/timer/timer.h b/test/common_plat/validation/api/timer/timer.h new file mode 100644 index 000000000..bd304fffd --- /dev/null +++ b/test/common_plat/validation/api/timer/timer.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_TIMER_H_ +#define _ODP_TEST_TIMER_H_ + +#include <odp_cunit_common.h> + +/* test functions: */ +void timer_test_timeout_pool_alloc(void); +void timer_test_timeout_pool_free(void); +void timer_test_odp_timer_cancel(void); +void timer_test_odp_timer_all(void); + +/* test arrays: */ +extern odp_testinfo_t timer_suite[]; + +/* test registry: */ +extern odp_suiteinfo_t timer_suites[]; + +/* main test program: */ +int timer_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/timer/timer_main.c b/test/common_plat/validation/api/timer/timer_main.c new file mode 100644 index 000000000..c318763fa --- /dev/null +++ b/test/common_plat/validation/api/timer/timer_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "timer.h" + +int main(int argc, char *argv[]) +{ + return timer_main(argc, argv); +} diff --git a/test/common_plat/validation/api/traffic_mngr/.gitignore b/test/common_plat/validation/api/traffic_mngr/.gitignore new file mode 100644 index 000000000..efd07a27d --- /dev/null +++ b/test/common_plat/validation/api/traffic_mngr/.gitignore @@ -0,0 +1 @@ +traffic_mngr_main diff --git a/test/common_plat/validation/api/traffic_mngr/Makefile.am b/test/common_plat/validation/api/traffic_mngr/Makefile.am new file mode 100644 index 000000000..35e689a02 --- /dev/null +++ b/test/common_plat/validation/api/traffic_mngr/Makefile.am @@ -0,0 +1,10 @@ +include ../Makefile.inc + +noinst_LTLIBRARIES = libtesttraffic_mngr.la +libtesttraffic_mngr_la_SOURCES = traffic_mngr.c + +test_PROGRAMS = traffic_mngr_main$(EXEEXT) +dist_traffic_mngr_main_SOURCES = traffic_mngr_main.c +traffic_mngr_main_LDADD = libtesttraffic_mngr.la -lm $(LIBCUNIT_COMMON) $(LIBODP) + +EXTRA_DIST = traffic_mngr.h diff --git a/test/common_plat/validation/api/traffic_mngr/traffic_mngr.c b/test/common_plat/validation/api/traffic_mngr/traffic_mngr.c new file mode 100644 index 000000000..1c4e90bf3 --- /dev/null +++ b/test/common_plat/validation/api/traffic_mngr/traffic_mngr.c @@ -0,0 +1,4009 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> +#include <math.h> +#include <odp.h> +#include <odp/helper/eth.h> +#include <odp/helper/ip.h> +#include <odp/helper/udp.h> +#include <odp/helper/tcp.h> +#include <odp/helper/chksum.h> +#include <test_debug.h> +#include "odp_cunit_common.h" +#include "traffic_mngr.h" + +#define TM_DEBUG 0 + +#define MAX_CAPABILITIES 16 +#define MAX_NUM_IFACES 2 +#define MAX_TM_SYSTEMS 3 +#define NUM_LEVELS 3 +#define NUM_PRIORITIES 4 +#define NUM_QUEUES_PER_NODE NUM_PRIORITIES +#define FANIN_RATIO 8 +#define NUM_LEVEL0_TM_NODES 1 +#define NUM_LEVEL1_TM_NODES FANIN_RATIO +#define NUM_LEVEL2_TM_NODES (FANIN_RATIO * FANIN_RATIO) +#define NUM_TM_QUEUES (NUM_LEVEL2_TM_NODES * NUM_QUEUES_PER_NODE) +#define NUM_SHAPER_PROFILES 64 +#define NUM_SCHED_PROFILES 64 +#define NUM_THRESHOLD_PROFILES 64 +#define NUM_WRED_PROFILES 64 +#define NUM_SHAPER_TEST_PROFILES 8 +#define NUM_SCHED_TEST_PROFILES 8 +#define NUM_THRESH_TEST_PROFILES 8 +#define NUM_WRED_TEST_PROFILES 8 + +#define ODP_NUM_PKT_COLORS ODP_NUM_PACKET_COLORS +#define PKT_GREEN ODP_PACKET_GREEN +#define PKT_YELLOW ODP_PACKET_YELLOW +#define PKT_RED ODP_PACKET_RED + +#define MIN_COMMIT_BW (64 * 1024) +#define MIN_COMMIT_BURST 8000 +#define MIN_PEAK_BW 2000000 +#define MIN_PEAK_BURST 16000 + +#define INITIAL_RCV_GAP_DROP 10 /* This is a percent of rcvd pkts */ +#define ENDING_RCV_GAP_DROP 20 /* This is a percent of rcvd pkts */ + +#define MIN_SHAPER_BW_RCV_GAP 80 /* Percent of expected_rcv_gap */ +#define MAX_SHAPER_BW_RCV_GAP 125 /* Percent of expected_rcv_gap */ + +#define MIN_PKT_THRESHOLD 10 +#define MIN_BYTE_THRESHOLD 2048 + +#define MIN_WRED_THRESH 5 +#define MED_WRED_THRESH 10 +#define MED_DROP_PROB 4 +#define MAX_DROP_PROB 8 + +#define MAX_PKTS 1000 +#define PKT_BUF_SIZE 1460 +#define MAX_PAYLOAD 1400 +#define USE_IPV4 false +#define USE_IPV6 true +#define USE_UDP false +#define USE_TCP true +#define LOW_DROP_PRECEDENCE 0x02 +#define MEDIUM_DROP_PRECEDENCE 0x04 +#define HIGH_DROP_PRECEDENCE 0x06 +#define DROP_PRECEDENCE_MASK 0x06 +#define DSCP_CLASS1 0x08 +#define DSCP_CLASS2 0x10 +#define DSCP_CLASS3 0x18 +#define DSCP_CLASS4 0x20 +#define DEFAULT_DSCP (DSCP_CLASS2 | LOW_DROP_PRECEDENCE) +#define DEFAULT_ECN ODPH_IP_ECN_ECT0 +#define DEFAULT_TOS ((DEFAULT_DSCP << ODPH_IP_TOS_DSCP_SHIFT) | \ + DEFAULT_ECN) +#define DEFAULT_TTL 128 +#define DEFAULT_UDP_SRC_PORT 12049 +#define DEFAULT_UDP_DST_PORT 12050 +#define DEFAULT_TCP_SRC_PORT 0xDEAD +#define DEFAULT_TCP_DST_PORT 0xBABE +#define DEFAULT_TCP_SEQ_NUM 0x12345678 +#define DEFAULT_TCP_ACK_NUM 0x12340000 +#define DEFAULT_TCP_WINDOW 0x4000 +#define VLAN_PRIORITY_BK 1 /* Background - lowest priority */ +#define VLAN_PRIORITY_BE 0 /* Best Effort */ +#define VLAN_PRIORITY_EE 2 /* Excellent Effort */ +#define VLAN_PRIORITY_NC 7 /* Network Control - highest priority */ +#define VLAN_DEFAULT_VID 12 +#define VLAN_NO_DEI ((VLAN_PRIORITY_EE << 13) | VLAN_DEFAULT_VID) +#define ETHERNET_IFG 12 /* Ethernet Interframe Gap */ +#define ETHERNET_PREAMBLE 8 +#define ETHERNET_OVHD_LEN (ETHERNET_IFG + ETHERNET_PREAMBLE) +#define CRC_LEN 4 +#define SHAPER_LEN_ADJ ETHERNET_OVHD_LEN +#define TM_NAME_LEN 32 +#define BILLION 1000000000ULL +#define MS 1000000 /* Millisecond in units of NS */ +#define MBPS 1000000 +#define GBPS 1000000000 + +#define MIN(a, b) (((a) <= (b)) ? (a) : (b)) +#define MAX(a, b) (((a) <= (b)) ? (b) : (a)) + +#define TM_PERCENT(percent) ((uint32_t)(100 * percent)) + +typedef enum { + SHAPER_PROFILE, SCHED_PROFILE, THRESHOLD_PROFILE, WRED_PROFILE +} profile_kind_t; + +typedef struct { + uint32_t num_queues; + odp_tm_queue_t tm_queues[0]; +} tm_queue_desc_t; + +typedef struct tm_node_desc_s tm_node_desc_t; + +struct tm_node_desc_s { + uint32_t level; + uint32_t node_idx; + uint32_t num_children; + char *node_name; + odp_tm_node_t node; + odp_tm_node_t parent_node; + tm_queue_desc_t *queue_desc; + tm_node_desc_t *children[0]; +}; + +typedef struct { + uint32_t num_samples; + uint32_t min_rcv_gap; + uint32_t max_rcv_gap; + uint32_t total_rcv_gap; + uint64_t total_rcv_gap_squared; + uint32_t avg_rcv_gap; + uint32_t std_dev_gap; +} rcv_stats_t; + +typedef struct { + odp_time_t xmt_time; + odp_time_t rcv_time; + uint64_t delta_ns; + odp_tm_queue_t tm_queue; + uint16_t pkt_len; + uint16_t xmt_unique_id; + uint16_t xmt_idx; + uint8_t pkt_class; + uint8_t was_rcvd; +} xmt_pkt_desc_t; + +typedef struct { + odp_time_t rcv_time; + xmt_pkt_desc_t *xmt_pkt_desc; + uint16_t rcv_unique_id; + uint16_t xmt_idx; + uint8_t errors; + uint8_t matched; + uint8_t pkt_class; + uint8_t is_ipv4_pkt; +} rcv_pkt_desc_t; + +typedef struct { + odp_tm_percent_t confidence_percent; + odp_tm_percent_t drop_percent; + uint32_t min_cnt; + uint32_t max_cnt; +} wred_pkt_cnts_t; + +typedef struct { + uint32_t num_queues; + uint32_t priority; + odp_tm_queue_t tm_queues[NUM_LEVEL2_TM_NODES]; +} queue_array_t; + +typedef struct { + queue_array_t queue_array[NUM_PRIORITIES]; +} queues_set_t; + +typedef struct { + uint16_t vlan_tci; + uint8_t pkt_class; + uint8_t ip_tos; /* TOS for IPv4 and TC for IPv6 */ + odp_packet_color_t pkt_color; + odp_bool_t drop_eligible; + odp_bool_t use_vlan; /* Else no VLAN header */ + odp_bool_t use_ipv6; /* Else use IPv4 */ + odp_bool_t use_tcp; /* Else use UDP */ +} pkt_info_t; + +static const char ALPHABET[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/* The following constant table determines the minimum and maximum number of + * pkts that will be received when sending 100 pkts through a system with a + * drop probability of p% (using a uniform probability distribution), with a + * confidence of 99.9% 99.99% and 99.999%. The confidence is interepreted as + * follows: a 99.99% confidence says that receiving LESS pkts than the given + * minimum or receiving MORE pkts than the given maximum (assuming a uniform + * drop percent of p) will happen less than 1 time in 10,000 trials. + * Mathematically the minimum pkt cnt is the largest value of cnt + * that satisfies the following equation: + * "(1 - cf/100)/2 <= Sum(binomial(100,k) * (1-p)^k * p^(100-k), k=0..cnt)", + * where cf is the confidence, caret (^) represents exponentiation, + * binomial(n,k) is the binomial coefficient defined as n! / (k! * (n-k)!). + * and p is the drop probability. Similarly the maximum pkt cnt is the + * smallest value of cnt that satisfies the equation: + * "(1 - cf/100)/2 <= Sum(binomial(100,k) * (1-p)^k * p^(100-k), k=cnt..100)". + * As a consequence of this, it should be the case that: + * cf/100 <= Sum(binomial(100,k) * (1-p)^k * p^(100-k), k=min..max)". + */ +static wred_pkt_cnts_t EXPECTED_PKT_RCVD[] = { + { TM_PERCENT(99.0), TM_PERCENT(10.0), 82, 97 }, + { TM_PERCENT(99.0), TM_PERCENT(20.0), 69, 90 }, + { TM_PERCENT(99.0), TM_PERCENT(30.0), 58, 81 }, + { TM_PERCENT(99.0), TM_PERCENT(40.0), 47, 72 }, + { TM_PERCENT(99.0), TM_PERCENT(50.0), 37, 63 }, + { TM_PERCENT(99.0), TM_PERCENT(60.0), 28, 53 }, + { TM_PERCENT(99.0), TM_PERCENT(70.0), 19, 42 }, + { TM_PERCENT(99.0), TM_PERCENT(80.0), 10, 31 }, + { TM_PERCENT(99.0), TM_PERCENT(90.0), 3, 18 }, + + { TM_PERCENT(99.9), TM_PERCENT(10.0), 79, 98 }, + { TM_PERCENT(99.9), TM_PERCENT(20.0), 66, 92 }, + { TM_PERCENT(99.9), TM_PERCENT(30.0), 54, 84 }, + { TM_PERCENT(99.9), TM_PERCENT(40.0), 44, 76 }, + { TM_PERCENT(99.9), TM_PERCENT(50.0), 34, 66 }, + { TM_PERCENT(99.9), TM_PERCENT(60.0), 24, 56 }, + { TM_PERCENT(99.9), TM_PERCENT(70.0), 16, 46 }, + { TM_PERCENT(99.9), TM_PERCENT(80.0), 8, 34 }, + { TM_PERCENT(99.9), TM_PERCENT(90.0), 2, 21 }, + + { TM_PERCENT(99.99), TM_PERCENT(10.0), 77, 99 }, + { TM_PERCENT(99.99), TM_PERCENT(20.0), 63, 94 }, + { TM_PERCENT(99.99), TM_PERCENT(30.0), 51, 87 }, + { TM_PERCENT(99.99), TM_PERCENT(40.0), 41, 78 }, + { TM_PERCENT(99.99), TM_PERCENT(50.0), 31, 69 }, + { TM_PERCENT(99.99), TM_PERCENT(60.0), 22, 59 }, + { TM_PERCENT(99.99), TM_PERCENT(70.0), 13, 49 }, + { TM_PERCENT(99.99), TM_PERCENT(80.0), 6, 37 }, + { TM_PERCENT(99.99), TM_PERCENT(90.0), 1, 23 }, +}; + +static uint8_t EQUAL_WEIGHTS[FANIN_RATIO] = { + 16, 16, 16, 16, 16, 16, 16, 16 +}; + +static uint8_t INCREASING_WEIGHTS[FANIN_RATIO] = { + 8, 12, 16, 24, 32, 48, 64, 96 +}; + +static uint8_t IPV4_SRC_ADDR[ODPH_IPV4ADDR_LEN] = { + 10, 0, 0, 1 /* I.e. 10.0.0.1 */ +}; + +static uint8_t IPV4_DST_ADDR[ODPH_IPV4ADDR_LEN] = { + 10, 0, 0, 100 /* I.e. 10.0.0.100 */ +}; + +static uint8_t IPV6_SRC_ADDR[ODPH_IPV6ADDR_LEN] = { + /* I.e. ::ffff:10.0.0.1 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 0, 0, 1 +}; + +static uint8_t IPV6_DST_ADDR[ODPH_IPV6ADDR_LEN] = { + /* I.e. ::ffff:10.0.0.100 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 10, 0, 0, 100 +}; + +static odp_tm_t odp_tm_systems[MAX_TM_SYSTEMS]; +static tm_node_desc_t *root_node_descs[MAX_TM_SYSTEMS]; +static uint32_t num_odp_tm_systems; + +static odp_tm_capabilities_t tm_capabilities; + +static odp_tm_shaper_t shaper_profiles[NUM_SHAPER_PROFILES]; +static odp_tm_sched_t sched_profiles[NUM_SCHED_PROFILES]; +static odp_tm_threshold_t threshold_profiles[NUM_THRESHOLD_PROFILES]; +static odp_tm_wred_t wred_profiles[NUM_WRED_PROFILES][ODP_NUM_PKT_COLORS]; + +static uint32_t num_shaper_profiles; +static uint32_t num_sched_profiles; +static uint32_t num_threshold_profiles; +static uint32_t num_wred_profiles; + +static uint8_t payload_data[MAX_PAYLOAD]; + +static odp_packet_t xmt_pkts[MAX_PKTS]; +static xmt_pkt_desc_t xmt_pkt_descs[MAX_PKTS]; +static uint32_t num_pkts_made; +static uint32_t num_pkts_sent; + +static odp_packet_t rcv_pkts[MAX_PKTS]; +static rcv_pkt_desc_t rcv_pkt_descs[MAX_PKTS]; +static uint32_t num_rcv_pkts; + +static uint32_t rcv_gaps[MAX_PKTS]; +static uint32_t rcv_gap_cnt; + +static queues_set_t queues_set; +static uint32_t unique_id_list[MAX_PKTS]; + +/* interface names used for testing */ +static const char *iface_name[MAX_NUM_IFACES]; + +/** number of interfaces being used (1=loopback, 2=pair) */ +static uint32_t num_ifaces; + +static odp_pool_t pools[MAX_NUM_IFACES] = {ODP_POOL_INVALID, ODP_POOL_INVALID}; + +static odp_pktio_t pktios[MAX_NUM_IFACES]; +static odp_pktin_queue_t pktins[MAX_NUM_IFACES]; +static odp_pktout_queue_t pktouts[MAX_NUM_IFACES]; +static odp_pktin_queue_t rcv_pktin; +static odp_pktout_queue_t xmt_pktout; + +static odph_ethaddr_t src_mac; +static odph_ethaddr_t dst_mac; + +static uint32_t cpu_unique_id; +static uint32_t cpu_tcp_seq_num; + +static void busy_wait(uint64_t nanoseconds) +{ + odp_time_t start_time, end_time; + + start_time = odp_time_local(); + end_time = odp_time_sum(start_time, + odp_time_local_from_ns(nanoseconds)); + + while (odp_time_cmp(odp_time_local(), end_time) < 0) + odp_cpu_pause(); +} + +static odp_bool_t approx_eq32(uint32_t val, uint32_t correct) +{ + uint64_t low_bound, val_times_100, high_bound; + + if (val == correct) + return true; + + low_bound = 98 * (uint64_t)correct; + val_times_100 = 100 * (uint64_t)val; + high_bound = 102 * (uint64_t)correct; + + if ((low_bound <= val_times_100) && (val_times_100 <= high_bound)) + return true; + else + return false; +} + +static odp_bool_t approx_eq64(uint64_t val, uint64_t correct) +{ + uint64_t low_bound, val_times_100, high_bound; + + if (val == correct) + return true; + + low_bound = 98 * correct; + val_times_100 = 100 * val; + high_bound = 102 * correct; + + if ((low_bound <= val_times_100) && (val_times_100 <= high_bound)) + return true; + else + return false; +} + +static int test_overall_capabilities(void) +{ + odp_tm_level_capabilities_t *per_level; + odp_tm_capabilities_t capabilities_array[MAX_CAPABILITIES]; + odp_tm_capabilities_t *cap_ptr; + uint32_t num_records, idx, num_levels, level; + int rc; + + rc = odp_tm_capabilities(capabilities_array, MAX_CAPABILITIES); + if (rc < 0) { + CU_ASSERT(rc < 0); + return -1; + } + + /* Now test the return code (which did not indicate a failure code) + * to make sure that there is at least ONE capabilities record + * returned */ + if (rc == 0) { + CU_ASSERT(rc != 0); + return -1; + } + + /* Now test the return code to see if there were more capabilities + * records than the call above allowed for. This is not an error, + * just an interesting fact. + */ + num_records = MAX_CAPABILITIES; + if (MAX_CAPABILITIES < rc) + LOG_DBG("There were more than %u capabilities (%u)\n", + MAX_CAPABILITIES, rc); + else + num_records = rc; + + /* Loop through the returned capabilities (there MUST be at least one) + * and do some basic checks to prove that it isn't just an empty + * record. */ + for (idx = 0; idx < num_records; idx++) { + cap_ptr = &capabilities_array[idx]; + if (cap_ptr->max_tm_queues == 0) { + CU_ASSERT(cap_ptr->max_tm_queues != 0); + return -1; + } + + if (cap_ptr->max_levels == 0) { + CU_ASSERT(cap_ptr->max_levels != 0); + return -1; + } + + num_levels = cap_ptr->max_levels; + for (level = 0; level < num_levels; level++) { + per_level = &cap_ptr->per_level[level]; + + if (per_level->max_num_tm_nodes == 0) { + CU_ASSERT(per_level->max_num_tm_nodes != 0); + return -1; + } + + if (per_level->max_fanin_per_node == 0) { + CU_ASSERT(per_level->max_fanin_per_node != 0); + return -1; + } + + if (per_level->max_priority == 0) { + CU_ASSERT(per_level->max_priority != 0); + return -1; + } + } + } + + return 0; +} + +static int wait_linkup(odp_pktio_t pktio) +{ + /* wait 1 second for link up */ + uint64_t wait_ns = (10 * ODP_TIME_MSEC_IN_NS); + int wait_num = 100; + int i; + int ret = -1; + + for (i = 0; i < wait_num; i++) { + ret = odp_pktio_link_status(pktio); + if (ret < 0 || ret == 1) + break; + /* link is down, call status again after delay */ + odp_time_wait_ns(wait_ns); + } + + return ret; +} + +static int open_pktios(void) +{ + odp_pktio_param_t pktio_param; + odp_pool_param_t pool_param; + odp_pktio_t pktio; + odp_pool_t pkt_pool; + uint32_t iface; + char pool_name[ODP_POOL_NAME_LEN]; + int rc, ret; + + odp_pool_param_init(&pool_param); + pool_param.pkt.num = 10 * MAX_PKTS; + pool_param.type = ODP_POOL_PACKET; + + odp_pktio_param_init(&pktio_param); + pktio_param.in_mode = ODP_PKTIN_MODE_DIRECT; + pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT; + + for (iface = 0; iface < num_ifaces; iface++) { + snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s", + iface_name[iface]); + + pkt_pool = odp_pool_create(pool_name, &pool_param); + if (pkt_pool == ODP_POOL_INVALID) { + CU_FAIL("unable to create pool"); + return -1; + } + + pools[iface] = pkt_pool; + pktio = odp_pktio_open(iface_name[iface], pkt_pool, + &pktio_param); + if (pktio == ODP_PKTIO_INVALID) + pktio = odp_pktio_lookup(iface_name[iface]); + if (pktio == ODP_PKTIO_INVALID) { + LOG_ERR("odp_pktio_open() failed\n"); + return -1; + } + + /* Set defaults for PktIn and PktOut queues */ + (void)odp_pktin_queue_config(pktio, NULL); + (void)odp_pktout_queue_config(pktio, NULL); + rc = odp_pktio_promisc_mode_set(pktio, true); + if (rc != 0) + printf("****** promisc_mode_set failed ******\n"); + + pktios[iface] = pktio; + + if (odp_pktin_queue(pktio, &pktins[iface], 1) != 1) { + odp_pktio_close(pktio); + LOG_ERR("odp_pktio_open() failed: no pktin queue\n"); + return -1; + } + + if (odp_pktout_queue(pktio, &pktouts[iface], 1) != 1) { + odp_pktio_close(pktio); + LOG_ERR("odp_pktio_open() failed: no pktout queue\n"); + return -1; + } + + rc = -1; + if (iface == 0) + rc = odp_pktio_mac_addr(pktio, &src_mac, + ODPH_ETHADDR_LEN); + + if ((iface == 1) || (num_ifaces == 1)) + rc = odp_pktio_mac_addr(pktio, &dst_mac, + ODPH_ETHADDR_LEN); + + if (rc != ODPH_ETHADDR_LEN) { + LOG_ERR("odp_pktio_mac_addr() failed\n"); + return -1; + } + } + + if (2 <= num_ifaces) { + xmt_pktout = pktouts[0]; + rcv_pktin = pktins[1]; + ret = odp_pktio_start(pktios[1]); + if (ret != 0) { + LOG_ERR("odp_pktio_start() failed\n"); + return -1; + } + } else { + xmt_pktout = pktouts[0]; + rcv_pktin = pktins[0]; + } + + ret = odp_pktio_start(pktios[0]); + if (ret != 0) { + LOG_ERR("odp_pktio_start() failed\n"); + return -1; + } + + /* Now wait until the link or links are up. */ + rc = wait_linkup(pktios[0]); + if (rc != 1) { + LOG_ERR("link %" PRIX64 " not up\n", + odp_pktio_to_u64(pktios[0])); + return -1; + } + + if (num_ifaces < 2) + return 0; + + /* Wait for 2nd link to be up */ + rc = wait_linkup(pktios[1]); + if (rc != 1) { + LOG_ERR("link %" PRIX64 " not up\n", + odp_pktio_to_u64(pktios[0])); + return -1; + } + + return 0; +} + +static int get_unique_id(odp_packet_t odp_pkt, + uint16_t *unique_id_ptr, + uint8_t *is_ipv4_pkt_ptr) +{ + odp_u32be_t be_ver_tc_flow; + odp_u16be_t be_ip_ident; + odp_bool_t is_ipv4; + uint32_t l3_offset, ident_offset, flow_offset, ver_tc_flow; + uint16_t unique_id; + + l3_offset = odp_packet_l3_offset(odp_pkt); + + if (odp_packet_has_ipv4(odp_pkt)) { + /* For IPv4 pkts use the ident field to store the unique_id. */ + ident_offset = l3_offset + offsetof(odph_ipv4hdr_t, id); + + odp_packet_copy_to_mem(odp_pkt, ident_offset, 2, &be_ip_ident); + unique_id = odp_be_to_cpu_16(be_ip_ident); + is_ipv4 = true; + } else if (odp_packet_has_ipv6(odp_pkt)) { + /* For IPv6 pkts use the flow field to store the unique_id. */ + flow_offset = l3_offset + offsetof(odph_ipv6hdr_t, ver_tc_flow); + + odp_packet_copy_to_mem(odp_pkt, flow_offset, 4, + &be_ver_tc_flow); + ver_tc_flow = odp_be_to_cpu_32(be_ver_tc_flow); + unique_id = ver_tc_flow & ODPH_IPV6HDR_FLOW_LABEL_MASK; + is_ipv4 = false; + } else { + return -1; + } + + if (unique_id_ptr != NULL) + *unique_id_ptr = unique_id; + + if (is_ipv4_pkt_ptr != NULL) + *is_ipv4_pkt_ptr = is_ipv4; + + return 0; +} + +static int get_vlan_tci(odp_packet_t odp_pkt, uint16_t *vlan_tci_ptr) +{ + odph_vlanhdr_t *vlan_hdr; + odph_ethhdr_t *ether_hdr; + uint32_t hdr_len; + uint16_t vlan_tci; + + if (!odp_packet_has_vlan(odp_pkt)) + return -1; + + /* *TBD* check value of hdr_len? */ + ether_hdr = odp_packet_l2_ptr(odp_pkt, &hdr_len); + vlan_hdr = (odph_vlanhdr_t *)(ether_hdr + 1); + vlan_tci = odp_be_to_cpu_16(vlan_hdr->tci); + if (vlan_tci_ptr != NULL) + *vlan_tci_ptr = vlan_tci; + + return 0; +} + +/* Returns either the TOS field for IPv4 pkts or the TC field for IPv6 pkts. */ +static int get_ip_tos(odp_packet_t odp_pkt, uint8_t *tos_ptr) +{ + odph_ipv4hdr_t *ipv4_hdr; + odph_ipv6hdr_t *ipv6_hdr; + uint32_t hdr_len, ver_tc_flow; + uint8_t tos, tc; + + if (odp_packet_has_ipv4(odp_pkt)) { + ipv4_hdr = odp_packet_l3_ptr(odp_pkt, &hdr_len); + if (hdr_len < 12) + return -1; + + tos = ipv4_hdr->tos; + } else if (odp_packet_has_ipv6(odp_pkt)) { + ipv6_hdr = odp_packet_l3_ptr(odp_pkt, &hdr_len); + if (hdr_len < 4) + return -1; + + ver_tc_flow = odp_be_to_cpu_32(ipv6_hdr->ver_tc_flow); + tc = (ver_tc_flow & ODPH_IPV6HDR_TC_MASK) + >> ODPH_IPV6HDR_TC_SHIFT; + tos = tc; + } else { + return -1; + } + + if (tos_ptr != NULL) + *tos_ptr = tos; + + return 0; +} + +static odp_packet_t make_pkt(odp_pool_t pkt_pool, + uint32_t payload_len, + uint16_t unique_id, + pkt_info_t *pkt_info) +{ + odph_vlanhdr_t *vlan_hdr; + odph_ipv4hdr_t *ipv4_hdr; + odph_ipv6hdr_t *ipv6_hdr; + odph_ethhdr_t *eth_hdr; + odph_udphdr_t *udp_hdr; + odph_tcphdr_t *tcp_hdr; + odp_packet_t odp_pkt; + uint32_t l4_hdr_len, l3_hdr_len, vlan_hdr_len, l2_hdr_len; + uint32_t l4_len, l3_len, l2_len, pkt_len, l3_offset, l4_offset; + uint32_t version, tc, flow, ver_tc_flow, app_offset; + uint16_t final_ether_type; + uint8_t *buf, *pkt_class_ptr, next_hdr; + int rc; + + l4_hdr_len = pkt_info->use_tcp ? ODPH_TCPHDR_LEN : ODPH_UDPHDR_LEN; + l3_hdr_len = pkt_info->use_ipv6 ? ODPH_IPV6HDR_LEN : ODPH_IPV4HDR_LEN; + vlan_hdr_len = pkt_info->use_vlan ? ODPH_VLANHDR_LEN : 0; + l2_hdr_len = ODPH_ETHHDR_LEN + vlan_hdr_len; + l4_len = l4_hdr_len + payload_len; + l3_len = l3_hdr_len + l4_len; + l2_len = l2_hdr_len + l3_len; + pkt_len = l2_len; + if (unique_id == 0) { + LOG_ERR("make_pkt called with invalid unique_id of 0\n"); + return ODP_PACKET_INVALID; + } + + odp_pkt = odp_packet_alloc(pkt_pool, pkt_len); + if (odp_pkt == ODP_PACKET_INVALID) + return ODP_PACKET_INVALID; + + buf = odp_packet_data(odp_pkt); + + /* Ethernet Header */ + odp_packet_l2_offset_set(odp_pkt, 0); + eth_hdr = (odph_ethhdr_t *)buf; + final_ether_type = pkt_info->use_ipv6 ? ODPH_ETHTYPE_IPV6 + : ODPH_ETHTYPE_IPV4; + memcpy(eth_hdr->src.addr, &src_mac, ODPH_ETHADDR_LEN); + memcpy(eth_hdr->dst.addr, &dst_mac, ODPH_ETHADDR_LEN); + + /* Vlan Header */ + if (pkt_info->use_vlan) { + odp_packet_has_vlan_set(odp_pkt, 1); + eth_hdr->type = odp_cpu_to_be_16(ODPH_ETHTYPE_VLAN); + vlan_hdr = (odph_vlanhdr_t *)(eth_hdr + 1); + vlan_hdr->tci = odp_cpu_to_be_16(pkt_info->vlan_tci); + vlan_hdr->type = odp_cpu_to_be_16(final_ether_type); + } else { + eth_hdr->type = odp_cpu_to_be_16(final_ether_type); + } + + l3_offset = l2_hdr_len; + next_hdr = pkt_info->use_tcp ? ODPH_IPPROTO_TCP : ODPH_IPPROTO_UDP; + odp_packet_l3_offset_set(odp_pkt, l3_offset); + if (pkt_info->use_ipv6) { + /* IPv6 Header */ + odp_packet_has_ipv6_set(odp_pkt, 1); + version = ODPH_IPV6 << ODPH_IPV6HDR_VERSION_SHIFT; + tc = pkt_info->ip_tos << ODPH_IPV6HDR_TC_SHIFT; + flow = unique_id << ODPH_IPV6HDR_FLOW_LABEL_SHIFT; + ver_tc_flow = version | tc | flow; + + ipv6_hdr = (odph_ipv6hdr_t *)(buf + l3_offset); + ipv6_hdr->ver_tc_flow = odp_cpu_to_be_32(ver_tc_flow); + ipv6_hdr->payload_len = odp_cpu_to_be_16(l4_len); + ipv6_hdr->next_hdr = next_hdr; + ipv6_hdr->hop_limit = DEFAULT_TTL; + memcpy(ipv6_hdr->src_addr, IPV6_SRC_ADDR, ODPH_IPV6ADDR_LEN); + memcpy(ipv6_hdr->dst_addr, IPV6_DST_ADDR, ODPH_IPV6ADDR_LEN); + } else { + /* IPv4 Header */ + odp_packet_has_ipv4_set(odp_pkt, 1); + ipv4_hdr = (odph_ipv4hdr_t *)(buf + l3_offset); + ipv4_hdr->ver_ihl = (ODPH_IPV4 << 4) | ODPH_IPV4HDR_IHL_MIN; + ipv4_hdr->tos = pkt_info->ip_tos; + ipv4_hdr->tot_len = odp_cpu_to_be_16(l3_len); + ipv4_hdr->id = odp_cpu_to_be_16(unique_id); + ipv4_hdr->frag_offset = 0; + ipv4_hdr->ttl = DEFAULT_TTL; + ipv4_hdr->proto = next_hdr; + ipv4_hdr->chksum = 0; + memcpy(&ipv4_hdr->src_addr, IPV4_SRC_ADDR, ODPH_IPV4ADDR_LEN); + memcpy(&ipv4_hdr->dst_addr, IPV4_DST_ADDR, ODPH_IPV4ADDR_LEN); + } + + l4_offset = l3_offset + l3_hdr_len; + odp_packet_l4_offset_set(odp_pkt, l4_offset); + tcp_hdr = (odph_tcphdr_t *)(buf + l4_offset); + udp_hdr = (odph_udphdr_t *)(buf + l4_offset); + + if (pkt_info->use_tcp) { + /* TCP Header */ + odp_packet_has_tcp_set(odp_pkt, 1); + tcp_hdr->src_port = odp_cpu_to_be_16(DEFAULT_TCP_SRC_PORT); + tcp_hdr->dst_port = odp_cpu_to_be_16(DEFAULT_TCP_DST_PORT); + tcp_hdr->seq_no = odp_cpu_to_be_32(cpu_tcp_seq_num); + tcp_hdr->ack_no = odp_cpu_to_be_32(DEFAULT_TCP_ACK_NUM); + tcp_hdr->window = odp_cpu_to_be_16(DEFAULT_TCP_WINDOW); + tcp_hdr->cksm = 0; + tcp_hdr->urgptr = 0; + + tcp_hdr->doffset_flags = 0; + tcp_hdr->hl = 5; + tcp_hdr->ack = 1; + cpu_tcp_seq_num += payload_len; + } else { + /* UDP Header */ + odp_packet_has_udp_set(odp_pkt, 1); + udp_hdr->src_port = odp_cpu_to_be_16(DEFAULT_UDP_SRC_PORT); + udp_hdr->dst_port = odp_cpu_to_be_16(DEFAULT_UDP_DST_PORT); + udp_hdr->length = odp_cpu_to_be_16(l4_len); + udp_hdr->chksum = 0; + } + + app_offset = l4_offset + l4_hdr_len; + rc = odp_packet_copy_from_mem(odp_pkt, app_offset, payload_len, + payload_data); + CU_ASSERT_FATAL(rc == 0); + + pkt_class_ptr = odp_packet_offset(odp_pkt, app_offset, NULL, NULL); + CU_ASSERT_FATAL(pkt_class_ptr != NULL); + *pkt_class_ptr = pkt_info->pkt_class; + + /* Calculate and insert checksums. First the IPv4 header checksum. */ + if (!pkt_info->use_ipv6) + odph_ipv4_csum_update(odp_pkt); + + /* Next the UDP/TCP checksum. */ + if (odph_udp_tcp_chksum(odp_pkt, ODPH_CHKSUM_GENERATE, NULL) != 0) + LOG_ERR("odph_udp_tcp_chksum failed\n"); + + return odp_pkt; +} + +static xmt_pkt_desc_t *find_matching_xmt_pkt_desc(uint16_t unique_id) +{ + xmt_pkt_desc_t *xmt_pkt_desc; + uint32_t xmt_pkt_idx; + + if (unique_id == 0) + return NULL; + + for (xmt_pkt_idx = 0; xmt_pkt_idx < num_pkts_sent; xmt_pkt_idx++) { + xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx]; + if (xmt_pkt_desc->xmt_unique_id == unique_id) + return xmt_pkt_desc; + } + + return NULL; +} + +static int receive_pkts(odp_tm_t odp_tm, + odp_pktin_queue_t pktin, + uint32_t num_pkts, + uint64_t rate_bps) +{ + xmt_pkt_desc_t *xmt_pkt_desc; + rcv_pkt_desc_t *rcv_pkt_desc; + odp_packet_t rcv_pkt; + odp_time_t start_time, current_time, duration, xmt_time; + odp_time_t rcv_time, delta_time; + uint64_t temp1, timeout_ns, duration_ns, delta_ns; + uint32_t pkts_rcvd, rcv_idx, l4_offset, l4_hdr_len, app_offset; + uint16_t unique_id; + uint8_t *pkt_class_ptr, pkt_class, is_ipv4_pkt; + int rc; + + temp1 = (1000000ULL * 10000ULL * (uint64_t)num_pkts) / rate_bps; + timeout_ns = 1000ULL * ((4ULL * temp1) + 10000ULL); + + pkts_rcvd = 0; + start_time = odp_time_local(); + duration_ns = 0; + + while ((pkts_rcvd < num_pkts) || (!odp_tm_is_idle(odp_tm))) { + rc = odp_pktin_recv(pktin, &rcv_pkts[pkts_rcvd], 1); + if (rc < 0) + return rc; + + current_time = odp_time_local(); + duration = odp_time_diff(current_time, start_time); + duration_ns = odp_time_to_ns(duration); + if (rc == 1) + rcv_pkt_descs[pkts_rcvd++].rcv_time = current_time; + else if (timeout_ns < duration_ns) + break; + } + + /* Now go through matching the rcv pkts to the xmt pkts, determining + * which xmt_pkts were lost and for the ones that did arrive, how + * long did they take. We don't do this work while receiving the pkts + * in the loop above because we want to try to get as accurate a + * rcv timestamp as possible. */ + for (rcv_idx = 0; rcv_idx < pkts_rcvd; rcv_idx++) { + rcv_pkt = rcv_pkts[rcv_idx]; + rcv_pkt_desc = &rcv_pkt_descs[rcv_idx]; + + if (odp_packet_has_error(rcv_pkt)) { + rcv_pkt_desc->errors = 0x01 | + (odp_packet_has_l2_error(rcv_pkt) << 1) | + (odp_packet_has_l3_error(rcv_pkt) << 2) | + (odp_packet_has_l4_error(rcv_pkt) << 3); + + LOG_ERR("received a pkt with the following errors\n"); + LOG_ERR(" l2_err=%u l3_err=%u l4_err=%u. Skipping\n", + (rcv_pkt_desc->errors >> 1) & 0x1, + (rcv_pkt_desc->errors >> 2) & 0x1, + (rcv_pkt_desc->errors >> 3) & 0x1); + } + + unique_id = 0; + rc = get_unique_id(rcv_pkt, &unique_id, &is_ipv4_pkt); + if (rc != 0) { + LOG_ERR("received a non IPv4/IPv6 pkt\n"); + return -1; + } + + rcv_pkt_desc->rcv_unique_id = unique_id; + rcv_pkt_desc->is_ipv4_pkt = is_ipv4_pkt; + if (odp_packet_has_udp(rcv_pkt)) + l4_hdr_len = ODPH_UDPHDR_LEN; + else if (odp_packet_has_tcp(rcv_pkt)) + l4_hdr_len = ODPH_TCPHDR_LEN; + else + l4_hdr_len = 0; + + l4_offset = odp_packet_l4_offset(rcv_pkt); + app_offset = l4_offset + l4_hdr_len; + pkt_class_ptr = odp_packet_offset(rcv_pkt, app_offset, + NULL, NULL); + if (pkt_class_ptr != NULL) + rcv_pkt_desc->pkt_class = *pkt_class_ptr; + + xmt_pkt_desc = find_matching_xmt_pkt_desc(unique_id); + if (xmt_pkt_desc != NULL) { + rcv_pkt_desc->xmt_pkt_desc = xmt_pkt_desc; + rcv_pkt_desc->matched = true; + + xmt_time = xmt_pkt_desc->xmt_time; + rcv_time = rcv_pkt_desc->rcv_time; + pkt_class = rcv_pkt_desc->pkt_class; + delta_time = odp_time_diff(rcv_time, xmt_time); + delta_ns = odp_time_to_ns(delta_time); + + rcv_pkt_desc->xmt_idx = xmt_pkt_desc->xmt_idx; + xmt_pkt_desc->rcv_time = rcv_time; + xmt_pkt_desc->delta_ns = delta_ns; + xmt_pkt_desc->pkt_class = pkt_class; + xmt_pkt_desc->was_rcvd = 1; + } + } + + return pkts_rcvd; +} + +static void dump_rcvd_pkts(uint32_t first_rcv_idx, uint32_t last_rcv_idx) +{ + rcv_pkt_desc_t *rcv_pkt_desc; + odp_packet_t rcv_pkt; + uint32_t rcv_idx; + int32_t xmt_idx; + uint16_t unique_id; + uint8_t is_ipv4; + int rc; + + for (rcv_idx = first_rcv_idx; rcv_idx <= last_rcv_idx; rcv_idx++) { + rcv_pkt = rcv_pkts[rcv_idx]; + rcv_pkt_desc = &rcv_pkt_descs[rcv_idx]; + rc = get_unique_id(rcv_pkt, &unique_id, &is_ipv4); + xmt_idx = -1; + if (rcv_pkt_desc->matched) + xmt_idx = rcv_pkt_desc->xmt_pkt_desc->xmt_idx; + + printf("rcv_idx=%u odp_pkt=0x%" PRIX64 " xmt_idx=%d " + "pkt_class=%u is_ipv4=%u unique_id=0x%X (rc=%d)\n", + rcv_idx, odp_packet_to_u64(rcv_pkt), xmt_idx, + rcv_pkt_desc->pkt_class, is_ipv4, unique_id, rc); + } +} + +static void free_rcvd_pkts(void) +{ + odp_packet_t rcv_pkt; + uint32_t rcv_idx; + + /* Go through all of the received pkts and free them. */ + for (rcv_idx = 0; rcv_idx < num_rcv_pkts; rcv_idx++) { + rcv_pkt = rcv_pkts[rcv_idx]; + if (rcv_pkt != ODP_PACKET_INVALID) { + odp_packet_free(rcv_pkt); + rcv_pkts[rcv_idx] = ODP_PACKET_INVALID; + } + } +} + +static void flush_leftover_pkts(odp_tm_t odp_tm, odp_pktin_queue_t pktin) +{ + odp_packet_t rcv_pkt; + odp_time_t start_time, current_time, duration; + uint64_t min_timeout_ns, max_timeout_ns, duration_ns; + int rc; + + /* Set the timeout to be at least 10 milliseconds and at most 100 + * milliseconds */ + min_timeout_ns = 10 * ODP_TIME_MSEC_IN_NS; + max_timeout_ns = 100 * ODP_TIME_MSEC_IN_NS; + start_time = odp_time_local(); + + while (true) { + rc = odp_pktin_recv(pktin, &rcv_pkt, 1); + if (rc == 1) + odp_packet_free(rcv_pkt); + + current_time = odp_time_local(); + duration = odp_time_diff(current_time, start_time); + duration_ns = odp_time_to_ns(duration); + + if (max_timeout_ns <= duration_ns) + break; + else if (duration_ns < min_timeout_ns) + ; + else if ((odp_tm_is_idle(odp_tm)) && (rc == 0)) + break; + + /* Busy wait here a little bit to prevent overwhelming the + * odp_pktin_recv logic. */ + busy_wait(10000); + } +} + +static void init_xmt_pkts(pkt_info_t *pkt_info) +{ + memset(xmt_pkts, 0, sizeof(xmt_pkts)); + memset(xmt_pkt_descs, 0, sizeof(xmt_pkt_descs)); + num_pkts_made = 0; + num_pkts_sent = 0; + + free_rcvd_pkts(); + memset(rcv_pkts, 0, sizeof(rcv_pkts)); + memset(rcv_pkt_descs, 0, sizeof(rcv_pkt_descs)); + num_rcv_pkts = 0; + + memset(rcv_gaps, 0, sizeof(rcv_gaps)); + rcv_gap_cnt = 0; + memset(pkt_info, 0, sizeof(pkt_info_t)); + pkt_info->ip_tos = DEFAULT_TOS; +} + +static int make_pkts(uint32_t num_pkts, + uint32_t pkt_len, + pkt_info_t *pkt_info) +{ + xmt_pkt_desc_t *xmt_pkt_desc; + odp_packet_t odp_pkt; + uint32_t l4_hdr_len, l3_hdr_len, vlan_hdr_len, l2_hdr_len; + uint32_t hdrs_len, payload_len, idx, unique_id, xmt_pkt_idx; + + l4_hdr_len = pkt_info->use_tcp ? ODPH_TCPHDR_LEN : ODPH_UDPHDR_LEN; + l3_hdr_len = pkt_info->use_ipv6 ? ODPH_IPV6HDR_LEN : ODPH_IPV4HDR_LEN; + vlan_hdr_len = pkt_info->use_vlan ? ODPH_VLANHDR_LEN : 0; + l2_hdr_len = ODPH_ETHHDR_LEN + vlan_hdr_len; + + hdrs_len = l2_hdr_len + l3_hdr_len + l4_hdr_len; + payload_len = pkt_len - hdrs_len; + + for (idx = 0; idx < num_pkts; idx++) { + unique_id = cpu_unique_id++; + xmt_pkt_idx = num_pkts_made++; + xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx]; + xmt_pkt_desc->pkt_len = pkt_len; + xmt_pkt_desc->xmt_unique_id = unique_id; + xmt_pkt_desc->pkt_class = pkt_info->pkt_class; + + odp_pkt = make_pkt(pools[0], payload_len, unique_id, pkt_info); + if (odp_pkt == ODP_PACKET_INVALID) + return -1; + + odp_packet_color_set(odp_pkt, pkt_info->pkt_color); + odp_packet_drop_eligible_set(odp_pkt, pkt_info->drop_eligible); + odp_packet_shaper_len_adjust_set(odp_pkt, SHAPER_LEN_ADJ); + + xmt_pkts[xmt_pkt_idx] = odp_pkt; + } + + return 0; +} + +static uint32_t send_pkts(odp_tm_queue_t tm_queue, uint32_t num_pkts) +{ + xmt_pkt_desc_t *xmt_pkt_desc; + odp_packet_t odp_pkt; + uint32_t idx, xmt_pkt_idx, pkts_sent; + int rc; + + /* Now send the pkts as fast as we can. */ + pkts_sent = 0; + for (idx = 0; idx < num_pkts; idx++) { + xmt_pkt_idx = num_pkts_sent; + odp_pkt = xmt_pkts[xmt_pkt_idx]; + xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx]; + + /* Alternate calling with odp_tm_enq and odp_tm_enq_with_cnt */ + if ((idx & 1) == 0) + rc = odp_tm_enq(tm_queue, odp_pkt); + else + rc = odp_tm_enq_with_cnt(tm_queue, odp_pkt); + + xmt_pkt_desc->xmt_idx = xmt_pkt_idx; + if (0 <= rc) { + xmt_pkt_desc->xmt_time = odp_time_local(); + xmt_pkt_desc->tm_queue = tm_queue; + pkts_sent++; + } else { + odp_packet_free(odp_pkt); + xmt_pkts[xmt_pkt_idx] = ODP_PACKET_INVALID; + } + + num_pkts_sent++; + } + + return pkts_sent; +} + +static uint32_t pkts_rcvd_in_send_order(void) +{ + xmt_pkt_desc_t *xmt_pkt_desc; + odp_time_t last_rcv_time, rcv_time; + uint32_t xmt_pkt_idx, pkts_rcvd; + + pkts_rcvd = 0; + last_rcv_time = ODP_TIME_NULL; + for (xmt_pkt_idx = 0; xmt_pkt_idx < num_pkts_sent; xmt_pkt_idx++) { + xmt_pkt_desc = &xmt_pkt_descs[xmt_pkt_idx]; + rcv_time = xmt_pkt_desc->rcv_time; + if (xmt_pkt_desc->was_rcvd != 0) { + if ((pkts_rcvd != 0) && + (odp_time_cmp(rcv_time, last_rcv_time) < 0)) + return 0; + + pkts_rcvd++; + last_rcv_time = xmt_pkt_desc->rcv_time; + } + } + + return pkts_rcvd; +} + +static int unique_id_list_idx(uint32_t unique_id, + uint32_t unique_id_list[], + uint32_t unique_id_list_len) +{ + uint32_t idx; + + for (idx = 0; idx < unique_id_list_len; idx++) + if (unique_id_list[idx] == unique_id) + return idx; + + return -1; +} + +static uint32_t pkts_rcvd_in_given_order(uint32_t unique_id_list[], + uint32_t unique_id_list_len, + uint8_t pkt_class, + odp_bool_t match_pkt_class, + odp_bool_t ignore_pkt_class) +{ + rcv_pkt_desc_t *rcv_pkt_desc; + odp_bool_t is_match; + uint32_t rcv_pkt_idx, pkts_in_order, pkts_out_of_order; + uint32_t rcv_unique_id; + int last_pkt_idx, pkt_idx; + + pkts_in_order = 1; + pkts_out_of_order = 0; + last_pkt_idx = -1; + pkt_idx = -1; + + for (rcv_pkt_idx = 0; rcv_pkt_idx < num_rcv_pkts; rcv_pkt_idx++) { + rcv_pkt_desc = &rcv_pkt_descs[rcv_pkt_idx]; + + if (ignore_pkt_class) + is_match = true; + else if (match_pkt_class) + is_match = rcv_pkt_desc->pkt_class == pkt_class; + else + is_match = rcv_pkt_desc->pkt_class != pkt_class; + + if (is_match) { + rcv_unique_id = rcv_pkt_desc->rcv_unique_id; + pkt_idx = unique_id_list_idx(rcv_unique_id, + unique_id_list, + unique_id_list_len); + if (0 <= pkt_idx) { + if (0 <= last_pkt_idx) { + if (last_pkt_idx < pkt_idx) + pkts_in_order++; + else + pkts_out_of_order++; + } + + last_pkt_idx = pkt_idx; + } + } + } + + return pkts_in_order; +} + +static inline void record_rcv_gap(odp_time_t rcv_time, odp_time_t last_rcv_time) +{ + odp_time_t delta_time; + uint64_t delta_ns; + uint32_t rcv_gap; + + rcv_gap = 0; + if (odp_time_cmp(last_rcv_time, rcv_time) <= 0) { + delta_time = odp_time_diff(rcv_time, last_rcv_time); + delta_ns = odp_time_to_ns(delta_time); + rcv_gap = delta_ns / 1000; + } + + /* Note that rcv_gap is in units of microseconds. */ + rcv_gaps[rcv_gap_cnt++] = rcv_gap; +} + +static int rcv_gap_cmp(const void *left_ptr, const void *right_ptr) +{ + uint32_t left_value, right_value; + + left_value = * (const uint32_t *)left_ptr; + right_value = * (const uint32_t *)right_ptr; + + if (left_value < right_value) + return -1; + else if (left_value == right_value) + return 0; + else + return 1; +} + +static inline void calc_rcv_stats(rcv_stats_t *rcv_stats, + uint32_t initial_drop_percent, + uint32_t ending_drop_percent) +{ + uint32_t first_rcv_gap_idx, last_rcv_gap_idx, idx, rcv_gap; + + /* Sort the rcv_gaps, and then drop the outlying x values before doing + * doing the rcv stats on the remaining */ + qsort(&rcv_gaps[0], rcv_gap_cnt, sizeof(uint32_t), rcv_gap_cmp); + + /* Next we drop the outlying values before doing doing the rcv stats + * on the remaining rcv_gap values. The number of initial (very low) + * rcv_gaps dropped and the number of ending (very high) rcv_gaps + * drops is based on the percentages passed in. */ + first_rcv_gap_idx = (rcv_gap_cnt * initial_drop_percent) / 100; + last_rcv_gap_idx = (rcv_gap_cnt * (100 - ending_drop_percent)) / 100; + for (idx = first_rcv_gap_idx; idx <= last_rcv_gap_idx; idx++) { + rcv_gap = rcv_gaps[idx]; + rcv_stats->min_rcv_gap = MIN(rcv_stats->min_rcv_gap, rcv_gap); + rcv_stats->max_rcv_gap = MAX(rcv_stats->max_rcv_gap, rcv_gap); + rcv_stats->total_rcv_gap += rcv_gap; + rcv_stats->total_rcv_gap_squared += rcv_gap * rcv_gap; + rcv_stats->num_samples++; + } +} + +static int rcv_rate_stats(rcv_stats_t *rcv_stats, uint8_t pkt_class) +{ + xmt_pkt_desc_t *xmt_pkt_desc; + odp_time_t last_rcv_time, rcv_time; + uint32_t pkt_idx, pkts_rcvd, num; + uint32_t avg, variance, std_dev; + + pkts_rcvd = 0; + last_rcv_time = ODP_TIME_NULL; + memset(rcv_stats, 0, sizeof(rcv_stats_t)); + rcv_stats->min_rcv_gap = 1000000000; + + for (pkt_idx = 0; pkt_idx < num_pkts_sent; pkt_idx++) { + xmt_pkt_desc = &xmt_pkt_descs[pkt_idx]; + if ((xmt_pkt_desc->was_rcvd != 0) && + (xmt_pkt_desc->pkt_class == pkt_class)) { + rcv_time = xmt_pkt_desc->rcv_time; + if (pkts_rcvd != 0) + record_rcv_gap(rcv_time, last_rcv_time); + pkts_rcvd++; + last_rcv_time = rcv_time; + } + } + + if (pkts_rcvd == 0) + return -1; + + calc_rcv_stats(rcv_stats, INITIAL_RCV_GAP_DROP, ENDING_RCV_GAP_DROP); + num = rcv_stats->num_samples; + if (num == 0) + return -1; + + avg = rcv_stats->total_rcv_gap / num; + variance = (rcv_stats->total_rcv_gap_squared / num) - avg * avg; + std_dev = (uint32_t)sqrt((double)variance); + + rcv_stats->avg_rcv_gap = avg; + rcv_stats->std_dev_gap = std_dev; + return 0; +} + +static int create_tm_queue(odp_tm_t odp_tm, + odp_tm_node_t tm_node, + uint32_t node_idx, + tm_queue_desc_t *queue_desc, + uint32_t priority) +{ + odp_tm_queue_params_t queue_params; + odp_tm_queue_t tm_queue; + odp_tm_wred_t green_profile, yellow_profile, red_profile; + int rc; + + odp_tm_queue_params_init(&queue_params); + queue_params.priority = priority; + if (priority == 0) { + green_profile = wred_profiles[node_idx][PKT_GREEN]; + yellow_profile = wred_profiles[node_idx][PKT_YELLOW]; + red_profile = wred_profiles[node_idx][PKT_RED]; + + queue_params.shaper_profile = shaper_profiles[0]; + queue_params.threshold_profile = threshold_profiles[0]; + queue_params.wred_profile[PKT_GREEN] = green_profile; + queue_params.wred_profile[PKT_YELLOW] = yellow_profile; + queue_params.wred_profile[PKT_RED] = red_profile; + } + + tm_queue = odp_tm_queue_create(odp_tm, &queue_params); + if (tm_queue == ODP_TM_INVALID) { + LOG_ERR("odp_tm_queue_create() failed\n"); + return -1; + } + + queue_desc->tm_queues[priority] = tm_queue; + rc = odp_tm_queue_connect(tm_queue, tm_node); + if (rc != 0) { + LOG_ERR("odp_tm_queue_connect() failed\n"); + odp_tm_queue_destroy(tm_queue); + return -1; + } + + return 0; +} + +static int destroy_tm_queue(odp_tm_queue_t tm_queue) +{ + odp_tm_queue_disconnect(tm_queue); + return odp_tm_queue_destroy(tm_queue); +} + +static tm_node_desc_t *create_tm_node(odp_tm_t odp_tm, + uint32_t level, + uint32_t num_levels, + uint32_t node_idx, + tm_node_desc_t *parent_node_desc) +{ + odp_tm_node_params_t node_params; + tm_queue_desc_t *queue_desc; + tm_node_desc_t *node_desc; + odp_tm_wred_t green_profile, yellow_profile, red_profile; + odp_tm_node_t tm_node, parent_node; + uint32_t node_desc_size, queue_desc_size, priority; + char node_name[TM_NAME_LEN]; + int rc; + + odp_tm_node_params_init(&node_params); + node_params.shaper_profile = ODP_TM_INVALID; + node_params.threshold_profile = ODP_TM_INVALID; + node_params.wred_profile[PKT_GREEN] = ODP_TM_INVALID; + node_params.wred_profile[PKT_YELLOW] = ODP_TM_INVALID; + node_params.wred_profile[PKT_RED] = ODP_TM_INVALID; + if (node_idx == 0) { + node_params.shaper_profile = shaper_profiles[0]; + node_params.threshold_profile = threshold_profiles[0]; + if (level == num_levels) { + green_profile = wred_profiles[node_idx][PKT_GREEN]; + yellow_profile = wred_profiles[node_idx][PKT_YELLOW]; + red_profile = wred_profiles[node_idx][PKT_RED]; + + node_params.wred_profile[PKT_GREEN] = green_profile; + node_params.wred_profile[PKT_YELLOW] = yellow_profile; + node_params.wred_profile[PKT_RED] = red_profile; + } + } + + node_params.max_fanin = FANIN_RATIO; + node_params.level = level; + if (parent_node_desc == NULL) + snprintf(node_name, sizeof(node_name), "node_%u", + node_idx + 1); + else + snprintf(node_name, sizeof(node_name), "%s_%u", + parent_node_desc->node_name, node_idx + 1); + + tm_node = odp_tm_node_create(odp_tm, node_name, &node_params); + if (tm_node == ODP_TM_INVALID) { + LOG_ERR("odp_tm_node_create() failed @ level=%u\n", + level); + return NULL; + } + + /* Now connect this node to the lower level "parent" node. */ + if (level == 0 || !parent_node_desc) + parent_node = ODP_TM_ROOT; + else + parent_node = parent_node_desc->node; + + rc = odp_tm_node_connect(tm_node, parent_node); + if (rc != 0) { + LOG_ERR("odp_tm_node_connect() failed @ level=%u\n", + level); + odp_tm_node_destroy(tm_node); + return NULL; + } + + node_desc_size = sizeof(tm_node_desc_t) + + sizeof(odp_tm_node_t) * FANIN_RATIO; + node_desc = malloc(node_desc_size); + memset(node_desc, 0, node_desc_size); + node_desc->level = level; + node_desc->node_idx = node_idx; + node_desc->num_children = FANIN_RATIO; + node_desc->node = tm_node; + node_desc->parent_node = parent_node; + node_desc->node_name = strdup(node_name); + + /* Finally if the level is the highest then make fanin_ratio tm_queues + * feeding this node. */ + if (level < (num_levels - 1)) + return node_desc; + + node_desc->num_children = 0; + queue_desc_size = sizeof(tm_queue_desc_t) + + sizeof(odp_tm_queue_t) * NUM_QUEUES_PER_NODE; + queue_desc = malloc(queue_desc_size); + memset(queue_desc, 0, queue_desc_size); + queue_desc->num_queues = NUM_QUEUES_PER_NODE; + node_desc->queue_desc = queue_desc; + + for (priority = 0; priority < NUM_QUEUES_PER_NODE; priority++) { + rc = create_tm_queue(odp_tm, tm_node, node_idx, queue_desc, + priority); + if (rc != 0) { + LOG_ERR("create_tm_queue() failed @ level=%u\n", + level); + while (priority > 0) + (void)destroy_tm_queue + (queue_desc->tm_queues[--priority]); + free(queue_desc); + free(node_desc); + return NULL; + } + } + + return node_desc; +} + +static tm_node_desc_t *create_tm_subtree(odp_tm_t odp_tm, + uint32_t level, + uint32_t num_levels, + uint32_t node_idx, + tm_node_desc_t *parent_node) +{ + tm_node_desc_t *node_desc, *child_desc; + uint32_t child_idx; + + node_desc = create_tm_node(odp_tm, level, num_levels, + node_idx, parent_node); + if (node_desc == NULL) { + LOG_ERR("create_tm_node() failed @ level=%u\n", level); + return NULL; + } + + if (level < (num_levels - 1)) { + for (child_idx = 0; child_idx < FANIN_RATIO; child_idx++) { + child_desc = create_tm_subtree(odp_tm, level + 1, + num_levels, child_idx, + node_desc); + if (child_desc == NULL) { + LOG_ERR("create_tm_subtree failed level=%u\n", + level); + + return NULL; + } + + node_desc->children[child_idx] = child_desc; + } + } + + return node_desc; +} + +static odp_tm_node_t find_tm_node(uint8_t tm_system_idx, const char *node_name) +{ + return odp_tm_node_lookup(odp_tm_systems[tm_system_idx], node_name); +} + +static tm_node_desc_t *find_node_desc(uint8_t tm_system_idx, + const char *node_name) +{ + tm_node_desc_t *node_desc; + uint32_t child_num; + char *name_ptr; + + /* Assume node_name is "node_" followed by a sequence of integers + * separated by underscores, where each integer is the child number to + * get to the next level node. */ + node_desc = root_node_descs[tm_system_idx]; + name_ptr = strchr(node_name, '_'); + if (name_ptr == NULL) + return NULL; + + /* Skip over the first integer */ + name_ptr++; + name_ptr = strchr(name_ptr, '_'); + if (name_ptr != NULL) + name_ptr++; + + while (node_desc != NULL) { + if (strcmp(node_desc->node_name, node_name) == 0) + return node_desc; + + if (name_ptr == NULL) + return NULL; + + child_num = atoi(name_ptr); + if (node_desc->num_children < child_num) + return NULL; + + node_desc = node_desc->children[child_num - 1]; + name_ptr = strchr(name_ptr, '_'); + if (name_ptr != NULL) + name_ptr++; + } + + return NULL; +} + +static odp_tm_queue_t find_tm_queue(uint8_t tm_system_idx, + const char *node_name, + uint8_t priority) +{ + tm_queue_desc_t *queue_desc; + tm_node_desc_t *node_desc; + + node_desc = find_node_desc(tm_system_idx, node_name); + if (node_desc == NULL) + return ODP_TM_INVALID; + + queue_desc = node_desc->queue_desc; + if (queue_desc == NULL) + return ODP_TM_INVALID; + + return queue_desc->tm_queues[priority]; +} + +static uint32_t find_child_queues(uint8_t tm_system_idx, + tm_node_desc_t *node_desc, + uint8_t priority, + odp_tm_queue_t tm_queues[], + uint32_t max_queues) +{ + tm_queue_desc_t *queue_desc; + tm_node_desc_t *child_node_desc; + uint32_t num_children, num_queues, child_idx, rem_queues; + + if (max_queues == 0) + return 0; + + queue_desc = node_desc->queue_desc; + if (queue_desc != NULL) { + tm_queues[0] = queue_desc->tm_queues[priority]; + return 1; + } + + num_children = node_desc->num_children; + num_queues = 0; + + for (child_idx = 0; child_idx < num_children; child_idx++) { + child_node_desc = node_desc->children[child_idx]; + rem_queues = max_queues - num_queues; + num_queues += find_child_queues(tm_system_idx, child_node_desc, + priority, + &tm_queues[num_queues], + rem_queues); + if (num_queues == max_queues) + break; + } + + return num_queues; +} + +static int create_tm_system(void) +{ + odp_tm_level_requirements_t *per_level; + odp_tm_requirements_t requirements; + odp_tm_egress_t egress; + odp_packet_color_t color; + tm_node_desc_t *root_node_desc; + uint32_t level, max_nodes[ODP_TM_MAX_LEVELS]; + odp_tm_t odp_tm, found_odp_tm; + char tm_name[TM_NAME_LEN]; + int rc; + + odp_tm_requirements_init(&requirements); + odp_tm_egress_init(&egress); + + requirements.max_tm_queues = NUM_TM_QUEUES + 1; + requirements.num_levels = NUM_LEVELS; + requirements.tm_queue_shaper_needed = true; + requirements.tm_queue_wred_needed = true; + requirements.tm_queue_dual_slope_needed = true; + requirements.vlan_marking_needed = false; + requirements.ecn_marking_needed = true; + requirements.drop_prec_marking_needed = true; + for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) + requirements.marking_colors_needed[color] = true; + + /* Set the max_num_tm_nodes to be double the expected number of nodes + * at that level */ + memset(max_nodes, 0, sizeof(max_nodes)); + max_nodes[0] = 2 * NUM_LEVEL0_TM_NODES; + max_nodes[1] = 2 * NUM_LEVEL1_TM_NODES; + max_nodes[2] = 2 * NUM_LEVEL2_TM_NODES; + max_nodes[3] = 2 * NUM_LEVEL2_TM_NODES * FANIN_RATIO; + + for (level = 0; level < NUM_LEVELS; level++) { + per_level = &requirements.per_level[level]; + per_level->max_priority = NUM_PRIORITIES - 1; + per_level->max_num_tm_nodes = max_nodes[level]; + per_level->max_fanin_per_node = FANIN_RATIO; + per_level->tm_node_shaper_needed = true; + per_level->tm_node_wred_needed = false; + per_level->tm_node_dual_slope_needed = false; + per_level->fair_queuing_needed = true; + per_level->weights_needed = true; + } + + egress.egress_kind = ODP_TM_EGRESS_PKT_IO; + egress.pktout = xmt_pktout; + + snprintf(tm_name, sizeof(tm_name), "TM_system_%u", num_odp_tm_systems); + odp_tm = odp_tm_create(tm_name, &requirements, &egress); + if (odp_tm == ODP_TM_INVALID) { + LOG_ERR("odp_tm_create() failed\n"); + return -1; + } + + odp_tm_systems[num_odp_tm_systems] = odp_tm; + + root_node_desc = create_tm_subtree(odp_tm, 0, NUM_LEVELS, 0, NULL); + root_node_descs[num_odp_tm_systems] = root_node_desc; + if (root_node_desc == NULL) { + LOG_ERR("create_tm_subtree() failed\n"); + return -1; + } + + num_odp_tm_systems++; + + /* Test odp_tm_capability and odp_tm_find. */ + rc = odp_tm_capability(odp_tm, &tm_capabilities); + if (rc != 0) { + LOG_ERR("odp_tm_capability() failed\n"); + return -1; + } + + found_odp_tm = odp_tm_find(tm_name, &requirements, &egress); + if ((found_odp_tm == ODP_TM_INVALID) || (found_odp_tm != odp_tm)) { + LOG_ERR("odp_tm_find() failed\n"); + return -1; + } + + return 0; +} + +static void dump_tm_subtree(tm_node_desc_t *node_desc) +{ + odp_tm_node_info_t node_info; + uint32_t idx, num_queues, child_idx; + int rc; + + for (idx = 0; idx < node_desc->level; idx++) + printf(" "); + + rc = odp_tm_node_info(node_desc->node, &node_info); + if (rc != 0) { + LOG_ERR("odp_tm_node_info failed for tm_node=0x%" PRIX64 "\n", + node_desc->node); + } + + num_queues = 0; + if (node_desc->queue_desc != NULL) + num_queues = node_desc->queue_desc->num_queues; + + printf("node_desc=%p name='%s' tm_node=0x%" PRIX64 " idx=%u level=%u " + "parent=0x%" PRIX64 " children=%u queues=%u queue_fanin=%u " + "node_fanin=%u\n", + node_desc, node_desc->node_name, node_desc->node, + node_desc->node_idx, node_desc->level, node_desc->parent_node, + node_desc->num_children, num_queues, node_info.tm_queue_fanin, + node_info.tm_node_fanin); + + for (child_idx = 0; child_idx < node_desc->num_children; child_idx++) + dump_tm_subtree(node_desc->children[child_idx]); +} + +static void dump_tm_tree(uint32_t tm_idx) +{ + tm_node_desc_t *root_node_desc; + + if (!TM_DEBUG) + return; + + root_node_desc = root_node_descs[tm_idx]; + dump_tm_subtree(root_node_desc); +} + +static int unconfig_tm_queue_profiles(odp_tm_queue_t tm_queue) +{ + odp_tm_queue_info_t queue_info; + odp_tm_wred_t wred_profile; + uint32_t color; + int rc; + + rc = odp_tm_queue_info(tm_queue, &queue_info); + if (rc != 0) { + LOG_ERR("odp_tm_queue_info failed code=%d\n", rc); + return rc; + } + + if (queue_info.shaper_profile != ODP_TM_INVALID) { + rc = odp_tm_queue_shaper_config(tm_queue, ODP_TM_INVALID); + if (rc != 0) { + LOG_ERR("odp_tm_queue_shaper_config failed code=%d\n", + rc); + return rc; + } + } + + if (queue_info.threshold_profile != ODP_TM_INVALID) { + rc = odp_tm_queue_threshold_config(tm_queue, ODP_TM_INVALID); + if (rc != 0) { + LOG_ERR("odp_tm_queue_threshold_config failed " + "code=%d\n", rc); + return rc; + } + } + + for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) { + wred_profile = queue_info.wred_profile[color]; + if (wred_profile != ODP_TM_INVALID) { + rc = odp_tm_queue_wred_config(tm_queue, color, + ODP_TM_INVALID); + if (rc != 0) { + LOG_ERR("odp_tm_queue_wred_config failed " + "color=%u code=%d\n", color, rc); + return rc; + } + } + } + + return 0; +} + +static int destroy_tm_queues(tm_queue_desc_t *queue_desc) +{ + odp_tm_queue_t tm_queue; + uint32_t num_queues, queue_idx; + int rc; + + num_queues = queue_desc->num_queues; + for (queue_idx = 0; queue_idx < num_queues; queue_idx++) { + tm_queue = queue_desc->tm_queues[queue_idx]; + if (tm_queue != ODP_TM_INVALID) { + rc = odp_tm_queue_disconnect(tm_queue); + if (rc != 0) { + LOG_ERR("odp_tm_queue_disconnect failed " + "idx=%u code=%d\n", queue_idx, rc); + return rc; + } + + rc = unconfig_tm_queue_profiles(tm_queue); + if (rc != 0) { + LOG_ERR("unconfig_tm_queue_profiles failed " + "idx=%u code=%d\n", queue_idx, rc); + return rc; + } + + rc = odp_tm_queue_destroy(tm_queue); + if (rc != 0) { + LOG_ERR("odp_tm_queue_destroy failed " + "idx=%u code=%d\n", queue_idx, rc); + return rc; + } + } + } + + free(queue_desc); + return 0; +} + +static int unconfig_tm_node_profiles(odp_tm_node_t tm_node) +{ + odp_tm_node_info_t node_info; + odp_tm_wred_t wred_profile; + uint32_t color; + int rc; + + rc = odp_tm_node_info(tm_node, &node_info); + if (rc != 0) { + LOG_ERR("odp_tm_node_info failed code=%d\n", rc); + return rc; + } + + if (node_info.shaper_profile != ODP_TM_INVALID) { + rc = odp_tm_node_shaper_config(tm_node, ODP_TM_INVALID); + if (rc != 0) { + LOG_ERR("odp_tm_node_shaper_config failed code=%d\n", + rc); + return rc; + } + } + + if (node_info.threshold_profile != ODP_TM_INVALID) { + rc = odp_tm_node_threshold_config(tm_node, ODP_TM_INVALID); + if (rc != 0) { + LOG_ERR("odp_tm_node_threshold_config failed " + "code=%d\n", rc); + return rc; + } + } + + for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) { + wred_profile = node_info.wred_profile[color]; + if (wred_profile != ODP_TM_INVALID) { + rc = odp_tm_node_wred_config(tm_node, color, + ODP_TM_INVALID); + if (rc != 0) { + LOG_ERR("odp_tm_node_wred_config failed " + "color=%u code=%d\n", color, rc); + return rc; + } + } + } + + return 0; +} + +static int destroy_tm_subtree(tm_node_desc_t *node_desc) +{ + tm_queue_desc_t *queue_desc; + tm_node_desc_t *child_desc; + odp_tm_node_t tm_node; + uint32_t num_children, child_num; + int rc; + + num_children = node_desc->num_children; + for (child_num = 0; child_num < num_children; child_num++) { + child_desc = node_desc->children[child_num]; + if (child_desc != NULL) { + rc = destroy_tm_subtree(child_desc); + if (rc != 0) { + LOG_ERR("destroy_tm_subtree failed " + "child_num=%u code=%d\n", + child_num, rc); + return rc; + } + } + } + + queue_desc = node_desc->queue_desc; + if (queue_desc != NULL) { + rc = destroy_tm_queues(queue_desc); + if (rc != 0) { + LOG_ERR("destroy_tm_queues failed code=%d\n", rc); + return rc; + } + } + + tm_node = node_desc->node; + rc = odp_tm_node_disconnect(tm_node); + if (rc != 0) { + LOG_ERR("odp_tm_node_disconnect failed code=%d\n", rc); + return rc; + } + + rc = unconfig_tm_node_profiles(tm_node); + if (rc != 0) { + LOG_ERR("unconfig_tm_node_profiles failed code=%d\n", rc); + return rc; + } + + rc = odp_tm_node_destroy(tm_node); + if (rc != 0) { + LOG_ERR("odp_tm_node_destroy failed code=%d\n", rc); + return rc; + } + + if (node_desc->node_name) + free(node_desc->node_name); + + free(node_desc); + return 0; +} + +static int destroy_all_shaper_profiles(void) +{ + odp_tm_shaper_t shaper_profile; + uint32_t idx; + int rc; + + for (idx = 0; idx < NUM_SHAPER_PROFILES; idx++) { + shaper_profile = shaper_profiles[idx]; + if (shaper_profile != ODP_TM_INVALID) { + rc = odp_tm_shaper_destroy(shaper_profile); + if (rc != 0) { + LOG_ERR("odp_tm_sched_destroy failed " + "idx=%u code=%d\n", idx, rc); + return rc; + } + shaper_profiles[idx] = ODP_TM_INVALID; + } + } + + return 0; +} + +static int destroy_all_sched_profiles(void) +{ + odp_tm_sched_t sched_profile; + uint32_t idx; + int rc; + + for (idx = 0; idx < NUM_SCHED_PROFILES; idx++) { + sched_profile = sched_profiles[idx]; + if (sched_profile != ODP_TM_INVALID) { + rc = odp_tm_sched_destroy(sched_profile); + if (rc != 0) { + LOG_ERR("odp_tm_sched_destroy failed " + "idx=%u code=%d\n", idx, rc); + return rc; + } + sched_profiles[idx] = ODP_TM_INVALID; + } + } + + return 0; +} + +static int destroy_all_threshold_profiles(void) +{ + odp_tm_threshold_t threshold_profile; + uint32_t idx; + int rc; + + for (idx = 0; idx < NUM_THRESHOLD_PROFILES; idx++) { + threshold_profile = threshold_profiles[idx]; + if (threshold_profile != ODP_TM_INVALID) { + rc = odp_tm_threshold_destroy(threshold_profile); + if (rc != 0) { + LOG_ERR("odp_tm_threshold_destroy failed " + "idx=%u code=%d\n", idx, rc); + return rc; + } + threshold_profiles[idx] = ODP_TM_INVALID; + } + } + + return 0; +} + +static int destroy_all_wred_profiles(void) +{ + odp_tm_wred_t wred_profile; + uint32_t idx, color; + int rc; + + for (idx = 0; idx < NUM_WRED_PROFILES; idx++) { + for (color = 0; color < ODP_NUM_PKT_COLORS; color++) { + wred_profile = wred_profiles[idx][color]; + if (wred_profile != ODP_TM_INVALID) { + rc = odp_tm_wred_destroy(wred_profile); + if (rc != 0) { + LOG_ERR("odp_tm_wred_destroy failed " + "idx=%u color=%u code=%d\n", + idx, color, rc); + return rc; + } + wred_profiles[idx][color] = ODP_TM_INVALID; + } + } + } + + return 0; +} + +static int destroy_all_profiles(void) +{ + int rc; + + rc = destroy_all_shaper_profiles(); + if (rc != 0) { + LOG_ERR("destroy_all_shaper_profiles failed code=%d\n", rc); + return rc; + } + + rc = destroy_all_sched_profiles(); + if (rc != 0) { + LOG_ERR("destroy_all_sched_profiles failed code=%d\n", rc); + return rc; + } + + rc = destroy_all_threshold_profiles(); + if (rc != 0) { + LOG_ERR("destroy_all_threshold_profiles failed code=%d\n", rc); + return rc; + } + + rc = destroy_all_wred_profiles(); + if (rc != 0) { + LOG_ERR("destroy_all_wred_profiles failed code=%d\n", rc); + return rc; + } + + return 0; +} + +static int destroy_tm_systems(void) +{ + uint32_t idx; + + /* Close/free the TM systems. */ + for (idx = 0; idx < num_odp_tm_systems; idx++) { + if (destroy_tm_subtree(root_node_descs[idx]) != 0) + return -1; + + if (odp_tm_destroy(odp_tm_systems[idx]) != 0) + return -1; + } + + /* Close/free the TM profiles. */ + if (destroy_all_profiles() != 0) + return -1; + + return 0; +} + +int traffic_mngr_suite_init(void) +{ + uint32_t payload_len, copy_len; + + /* Initialize some global variables. */ + num_pkts_made = 0; + num_pkts_sent = 0; + num_rcv_pkts = 0; + cpu_unique_id = 1; + cpu_tcp_seq_num = DEFAULT_TCP_SEQ_NUM; + memset(xmt_pkts, 0, sizeof(xmt_pkts)); + memset(rcv_pkts, 0, sizeof(rcv_pkts)); + + payload_len = 0; + while (payload_len < MAX_PAYLOAD) { + copy_len = MIN(MAX_PAYLOAD - payload_len, sizeof(ALPHABET)); + memcpy(&payload_data[payload_len], ALPHABET, copy_len); + payload_len += copy_len; + } + + /* Next open a single or pair of interfaces. This should be the same + * logic as in the pktio_suite_init() function in the + * test/validation/pktio.c file. */ + iface_name[0] = getenv("ODP_PKTIO_IF0"); + iface_name[1] = getenv("ODP_PKTIO_IF1"); + num_ifaces = 1; + + if (!iface_name[0]) { + printf("No interfaces specified, using default \"loop\".\n"); + iface_name[0] = "loop"; + } else if (!iface_name[1]) { + printf("Using loopback interface: %s\n", iface_name[0]); + } else { + num_ifaces = 2; + printf("Using paired interfaces: %s %s\n", + iface_name[0], iface_name[1]); + } + + if (open_pktios() != 0) + return -1; + + return 0; +} + +int traffic_mngr_suite_term(void) +{ + uint32_t iface; + + /* Close the pktios and associated packet pools. */ + free_rcvd_pkts(); + for (iface = 0; iface < num_ifaces; iface++) { + if (odp_pktio_stop(pktios[iface]) != 0) + return -1; + + if (odp_pktio_close(pktios[iface]) != 0) + return -1; + + if (odp_pool_destroy(pools[iface]) != 0) + return -1; + } + + return 0; +} + +static void check_shaper_profile(char *shaper_name, uint32_t shaper_idx) +{ + odp_tm_shaper_params_t shaper_params; + odp_tm_shaper_t profile; + + profile = odp_tm_shaper_lookup(shaper_name); + CU_ASSERT(profile != ODP_TM_INVALID); + CU_ASSERT(profile == shaper_profiles[shaper_idx - 1]); + if (profile != shaper_profiles[shaper_idx - 1]) + return; + + odp_tm_shaper_params_read(profile, &shaper_params); + CU_ASSERT(approx_eq64(shaper_params.commit_bps, + shaper_idx * MIN_COMMIT_BW)); + CU_ASSERT(approx_eq64(shaper_params.peak_bps, + shaper_idx * MIN_PEAK_BW)); + CU_ASSERT(approx_eq32(shaper_params.commit_burst, + shaper_idx * MIN_COMMIT_BURST)); + CU_ASSERT(approx_eq32(shaper_params.peak_burst, + shaper_idx * MIN_PEAK_BURST)); + + CU_ASSERT(shaper_params.shaper_len_adjust == SHAPER_LEN_ADJ); + CU_ASSERT(shaper_params.dual_rate == 0); +} + +void traffic_mngr_test_shaper_profile(void) +{ + odp_tm_shaper_params_t shaper_params; + odp_tm_shaper_t profile; + uint32_t idx, shaper_idx, i; + char shaper_name[TM_NAME_LEN]; + + odp_tm_shaper_params_init(&shaper_params); + shaper_params.shaper_len_adjust = SHAPER_LEN_ADJ; + shaper_params.dual_rate = 0; + + for (idx = 1; idx <= NUM_SHAPER_TEST_PROFILES; idx++) { + snprintf(shaper_name, sizeof(shaper_name), + "shaper_profile_%u", idx); + shaper_params.commit_bps = idx * MIN_COMMIT_BW; + shaper_params.peak_bps = idx * MIN_PEAK_BW; + shaper_params.commit_burst = idx * MIN_COMMIT_BURST; + shaper_params.peak_burst = idx * MIN_PEAK_BURST; + + profile = odp_tm_shaper_create(shaper_name, &shaper_params); + CU_ASSERT_FATAL(profile != ODP_TM_INVALID); + + /* Make sure profile handle is unique */ + for (i = 1; i < idx - 1; i++) + CU_ASSERT(profile != shaper_profiles[i - 1]); + + shaper_profiles[idx - 1] = profile; + num_shaper_profiles++; + } + + /* Now test odp_tm_shaper_lookup */ + for (idx = 1; idx <= NUM_SHAPER_TEST_PROFILES; idx++) { + /* The following equation is designed is somewhat randomize + * the lookup of the profiles to catch any implementations + *taking shortcuts. */ + shaper_idx = ((3 + 7 * idx) % NUM_SHAPER_TEST_PROFILES) + 1; + snprintf(shaper_name, sizeof(shaper_name), + "shaper_profile_%u", shaper_idx); + + check_shaper_profile(shaper_name, shaper_idx); + } +} + +static void check_sched_profile(char *sched_name, uint32_t sched_idx) +{ + odp_tm_sched_params_t sched_params; + odp_tm_sched_t profile; + uint32_t priority; + + profile = odp_tm_sched_lookup(sched_name); + CU_ASSERT(profile != ODP_TM_INVALID); + CU_ASSERT(profile == sched_profiles[sched_idx - 1]); + if (profile != sched_profiles[sched_idx - 1]) + return; + + odp_tm_sched_params_read(profile, &sched_params); + for (priority = 0; priority < NUM_PRIORITIES; priority++) { + CU_ASSERT(sched_params.sched_modes[priority] == + ODP_TM_BYTE_BASED_WEIGHTS); + CU_ASSERT(approx_eq32(sched_params.sched_weights[priority], + 8 + sched_idx + priority)); + } +} + +void traffic_mngr_test_sched_profile(void) +{ + odp_tm_sched_params_t sched_params; + odp_tm_sched_t profile; + uint32_t idx, priority, sched_idx, i; + char sched_name[TM_NAME_LEN]; + + odp_tm_sched_params_init(&sched_params); + + for (idx = 1; idx <= NUM_SCHED_TEST_PROFILES; idx++) { + snprintf(sched_name, sizeof(sched_name), + "sched_profile_%u", idx); + for (priority = 0; priority < 16; priority++) { + sched_params.sched_modes[priority] = + ODP_TM_BYTE_BASED_WEIGHTS; + sched_params.sched_weights[priority] = 8 + idx + + priority; + } + + profile = odp_tm_sched_create(sched_name, &sched_params); + CU_ASSERT_FATAL(profile != ODP_TM_INVALID); + + /* Make sure profile handle is unique */ + for (i = 1; i < idx - 1; i++) + CU_ASSERT(profile != sched_profiles[i - 1]); + + sched_profiles[idx - 1] = profile; + num_sched_profiles++; + } + + /* Now test odp_tm_sched_lookup */ + for (idx = 1; idx <= NUM_SCHED_TEST_PROFILES; idx++) { + /* The following equation is designed is somewhat randomize + * the lookup of the profiles to catch any implementations + * taking shortcuts. */ + sched_idx = ((3 + 7 * idx) % NUM_SCHED_TEST_PROFILES) + 1; + snprintf(sched_name, sizeof(sched_name), "sched_profile_%u", + sched_idx); + check_sched_profile(sched_name, sched_idx); + } +} + +static void check_threshold_profile(char *threshold_name, + uint32_t threshold_idx) +{ + odp_tm_threshold_params_t threshold_params; + odp_tm_threshold_t profile; + + profile = odp_tm_thresholds_lookup(threshold_name); + CU_ASSERT(profile != ODP_TM_INVALID); + CU_ASSERT(profile == threshold_profiles[threshold_idx - 1]); + + if (profile == threshold_profiles[threshold_idx - 1]) + return; + + odp_tm_thresholds_params_read(profile, &threshold_params); + CU_ASSERT(threshold_params.max_pkts == + threshold_idx * MIN_PKT_THRESHOLD); + CU_ASSERT(threshold_params.max_bytes == + threshold_idx * MIN_BYTE_THRESHOLD); + CU_ASSERT(threshold_params.enable_max_pkts == 1); + CU_ASSERT(threshold_params.enable_max_bytes == 1); +} + +void traffic_mngr_test_threshold_profile(void) +{ + odp_tm_threshold_params_t threshold_params; + odp_tm_threshold_t profile; + uint32_t idx, threshold_idx, i; + char threshold_name[TM_NAME_LEN]; + + odp_tm_threshold_params_init(&threshold_params); + threshold_params.enable_max_pkts = 1; + threshold_params.enable_max_bytes = 1; + + for (idx = 1; idx <= NUM_THRESH_TEST_PROFILES; idx++) { + snprintf(threshold_name, sizeof(threshold_name), + "threshold_profile_%u", idx); + threshold_params.max_pkts = idx * MIN_PKT_THRESHOLD; + threshold_params.max_bytes = idx * MIN_BYTE_THRESHOLD; + + profile = odp_tm_threshold_create(threshold_name, + &threshold_params); + CU_ASSERT_FATAL(profile != ODP_TM_INVALID); + + /* Make sure profile handle is unique */ + for (i = 1; i < idx - 1; i++) + CU_ASSERT(profile != threshold_profiles[i - 1]); + + threshold_profiles[idx - 1] = profile; + num_threshold_profiles++; + } + + /* Now test odp_tm_threshold_lookup */ + for (idx = 1; idx <= NUM_THRESH_TEST_PROFILES; idx++) { + /* The following equation is designed is somewhat randomize + * the lookup of the profiles to catch any implementations + * taking shortcuts. */ + threshold_idx = ((3 + 7 * idx) % NUM_THRESH_TEST_PROFILES) + 1; + snprintf(threshold_name, sizeof(threshold_name), + "threshold_profile_%u", threshold_idx); + check_threshold_profile(threshold_name, threshold_idx); + } +} + +static void check_wred_profile(char *wred_name, + uint32_t wred_idx, + uint32_t color) +{ + odp_tm_wred_params_t wred_params; + odp_tm_wred_t profile; + + profile = odp_tm_wred_lookup(wred_name); + CU_ASSERT(profile != ODP_TM_INVALID); + CU_ASSERT(profile == wred_profiles[wred_idx - 1][color]); + if (profile != wred_profiles[wred_idx - 1][color]) + return; + + odp_tm_wred_params_read(profile, &wred_params); + CU_ASSERT(wred_params.min_threshold == wred_idx * MIN_WRED_THRESH); + CU_ASSERT(wred_params.med_threshold == wred_idx * MED_WRED_THRESH); + CU_ASSERT(wred_params.med_drop_prob == wred_idx * MED_DROP_PROB); + CU_ASSERT(wred_params.max_drop_prob == wred_idx * MAX_DROP_PROB); + + CU_ASSERT(wred_params.enable_wred == 1); + CU_ASSERT(wred_params.use_byte_fullness == 0); +} + +void traffic_mngr_test_wred_profile(void) +{ + odp_tm_wred_params_t wred_params; + odp_tm_wred_t profile; + uint32_t idx, color, wred_idx, i, c; + char wred_name[TM_NAME_LEN]; + + odp_tm_wred_params_init(&wred_params); + wred_params.enable_wred = 1; + wred_params.use_byte_fullness = 0; + + for (idx = 1; idx <= NUM_WRED_TEST_PROFILES; idx++) { + for (color = 0; color < ODP_NUM_PKT_COLORS; color++) { + snprintf(wred_name, sizeof(wred_name), + "wred_profile_%u_%u", idx, color); + wred_params.min_threshold = idx * MIN_WRED_THRESH; + wred_params.med_threshold = idx * MED_WRED_THRESH; + wred_params.med_drop_prob = idx * MED_DROP_PROB; + wred_params.max_drop_prob = idx * MAX_DROP_PROB; + + profile = odp_tm_wred_create(wred_name, &wred_params); + CU_ASSERT_FATAL(profile != ODP_TM_INVALID); + + /* Make sure profile handle is unique */ + for (i = 1; i < idx - 1; i++) + for (c = 0; c < ODP_NUM_PKT_COLORS; c++) + CU_ASSERT(profile != + wred_profiles[i - 1][c]); + + wred_profiles[idx - 1][color] = profile; + } + + num_wred_profiles++; + } + + /* Now test odp_tm_wred_lookup */ + for (idx = 1; idx <= NUM_WRED_TEST_PROFILES; idx++) { + /* The following equation is designed is somewhat randomize + * the lookup of the profiles to catch any implementations + * taking shortcuts. */ + wred_idx = ((3 + 7 * idx) % NUM_WRED_TEST_PROFILES) + 1; + + for (color = 0; color < ODP_NUM_PKT_COLORS; color++) { + snprintf(wred_name, sizeof(wred_name), + "wred_profile_%u_%u", wred_idx, color); + check_wred_profile(wred_name, wred_idx, color); + } + } +} + +static int set_shaper(const char *node_name, + const char *shaper_name, + const uint64_t commit_bps, + const uint64_t commit_burst_in_bits) +{ + odp_tm_shaper_params_t shaper_params; + odp_tm_shaper_t shaper_profile; + odp_tm_node_t tm_node; + + tm_node = find_tm_node(0, node_name); + if (tm_node == ODP_TM_INVALID) { + LOG_ERR("find_tm_node(%s) failed\n", node_name); + CU_ASSERT_FATAL(tm_node != ODP_TM_INVALID); + return -1; + } + + odp_tm_shaper_params_init(&shaper_params); + shaper_params.commit_bps = commit_bps; + shaper_params.peak_bps = 0; + shaper_params.commit_burst = commit_burst_in_bits; + shaper_params.peak_burst = 0; + shaper_params.shaper_len_adjust = 0; + shaper_params.dual_rate = 0; + + /* First see if a shaper profile already exists with this name, in + * which case we use that profile, else create a new one. */ + shaper_profile = odp_tm_shaper_lookup(shaper_name); + if (shaper_profile != ODP_TM_INVALID) { + odp_tm_shaper_params_update(shaper_profile, &shaper_params); + } else { + shaper_profile = odp_tm_shaper_create(shaper_name, + &shaper_params); + shaper_profiles[num_shaper_profiles] = shaper_profile; + num_shaper_profiles++; + } + + return odp_tm_node_shaper_config(tm_node, shaper_profile); +} + +int traffic_mngr_check_shaper(void) +{ + odp_cpumask_t cpumask; + int cpucount = odp_cpumask_all_available(&cpumask); + + if (cpucount < 2) { + LOG_DBG("\nSkipping shaper test because cpucount = %d " + "is less then min number 2 required\n", cpucount); + LOG_DBG("Rerun with more cpu resources\n"); + return ODP_TEST_INACTIVE; + } + + return ODP_TEST_ACTIVE; +} + +int traffic_mngr_check_scheduler(void) +{ + odp_cpumask_t cpumask; + int cpucount = odp_cpumask_all_available(&cpumask); + + if (cpucount < 2) { + LOG_DBG("\nSkipping scheduler test because cpucount = %d " + "is less then min number 2 required\n", cpucount); + LOG_DBG("Rerun with more cpu resources\n"); + return ODP_TEST_INACTIVE; + } + + return ODP_TEST_ACTIVE; +} + +static int test_shaper_bw(const char *shaper_name, + const char *node_name, + uint8_t priority, + uint64_t commit_bps) +{ + odp_tm_queue_t tm_queue; + rcv_stats_t rcv_stats; + pkt_info_t pkt_info; + uint64_t expected_rcv_gap_us; + uint32_t num_pkts, pkt_len, pkts_rcvd_in_order, avg_rcv_gap; + uint32_t min_rcv_gap, max_rcv_gap, pkts_sent; + int rc, ret_code; + + /* This test can support a commit_bps from 64K to 2 Gbps and possibly + * up to a max of 10 Gbps, but no higher. */ + CU_ASSERT_FATAL(commit_bps <= (10ULL * 1000000000ULL)); + + /* Pick a tm_queue and set the parent node's shaper BW to be commit_bps + * with a small burst tolerance. Then send the traffic with a pkt_len + * such that the pkt start time to next pkt start time is 10,000 bit + * times and then measure the average inter-arrival receive "gap" in + * microseconds. */ + tm_queue = find_tm_queue(0, node_name, priority); + if (set_shaper(node_name, shaper_name, commit_bps, 10000) != 0) + return -1; + + init_xmt_pkts(&pkt_info); + num_pkts = 50; + pkt_len = (10000 / 8) - (ETHERNET_OVHD_LEN + CRC_LEN); + pkt_info.pkt_class = 1; + if (make_pkts(num_pkts, pkt_len, &pkt_info) != 0) + return -1; + + pkts_sent = send_pkts(tm_queue, num_pkts); + + /* The expected inter arrival receive gap in seconds is equal to + * "10,000 bits / commit_bps". To get the gap time in microseconds + * we multiply this by one million. The timeout we use is 50 times + * this gap time (since we send 50 pkts) multiplied by 4 to be + * conservative, plus a constant time of 1 millisecond to account for + * testing delays. This then needs to be expressed in nanoseconds by + * multiplying by 1000. */ + expected_rcv_gap_us = (1000000ULL * 10000ULL) / commit_bps; + num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent, + commit_bps); + pkts_rcvd_in_order = pkts_rcvd_in_send_order(); + ret_code = -1; + + /* First verify that MOST of the pkts were received in any order. */ + if (num_rcv_pkts <= (pkts_sent / 2)) { + /* This is fairly major failure in that most of the pkts didn't + * even get received, regardless of rate or order. Log the error + * to assist with debugging */ + LOG_ERR("Sent %u pkts but only %u came back\n", + pkts_sent, num_rcv_pkts); + CU_ASSERT(num_rcv_pkts <= (pkts_sent / 2)); + } else if (pkts_rcvd_in_order <= 32) { + LOG_ERR("Sent %u pkts but only %u came back (%u in order)\n", + pkts_sent, num_rcv_pkts, pkts_rcvd_in_order); + CU_ASSERT(pkts_rcvd_in_order <= 32); + } else { + if (pkts_rcvd_in_order < pkts_sent) + LOG_DBG("Info: of %u pkts sent %u came back (%u " + "in order)\n", pkts_sent, + num_rcv_pkts, pkts_rcvd_in_order); + + /* Next determine the inter arrival receive pkt statistics. */ + rc = rcv_rate_stats(&rcv_stats, pkt_info.pkt_class); + CU_ASSERT(rc == 0); + + /* Next verify that the rcvd pkts have an average inter-receive + * gap of "expected_rcv_gap_us" microseconds, +/- 25%. */ + avg_rcv_gap = rcv_stats.avg_rcv_gap; + min_rcv_gap = ((MIN_SHAPER_BW_RCV_GAP * expected_rcv_gap_us) / + 100) - 2; + max_rcv_gap = ((MAX_SHAPER_BW_RCV_GAP * expected_rcv_gap_us) / + 100) + 2; + if ((avg_rcv_gap < min_rcv_gap) || + (max_rcv_gap < avg_rcv_gap)) { + LOG_ERR("min=%u avg_rcv_gap=%u max=%u " + "std_dev_gap=%u\n", + rcv_stats.min_rcv_gap, avg_rcv_gap, + rcv_stats.max_rcv_gap, rcv_stats.std_dev_gap); + LOG_ERR(" expected_rcv_gap=%" PRIu64 " acceptable " + "rcv_gap range=%u..%u\n", + expected_rcv_gap_us, min_rcv_gap, max_rcv_gap); + } else if (expected_rcv_gap_us < rcv_stats.std_dev_gap) { + LOG_ERR("min=%u avg_rcv_gap=%u max=%u " + "std_dev_gap=%u\n", + rcv_stats.min_rcv_gap, avg_rcv_gap, + rcv_stats.max_rcv_gap, rcv_stats.std_dev_gap); + LOG_ERR(" expected_rcv_gap=%" PRIu64 " acceptable " + "rcv_gap range=%u..%u\n", + expected_rcv_gap_us, min_rcv_gap, max_rcv_gap); + ret_code = 0; + } else { + ret_code = 0; + } + + CU_ASSERT((min_rcv_gap <= avg_rcv_gap) && + (avg_rcv_gap <= max_rcv_gap)); + CU_ASSERT(rcv_stats.std_dev_gap <= expected_rcv_gap_us); + } + + /* Disable the shaper, so as to get the pkts out quicker. */ + set_shaper(node_name, shaper_name, 0, 0); + flush_leftover_pkts(odp_tm_systems[0], rcv_pktin); + CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0])); + return ret_code; +} + +static int set_sched_fanin(const char *node_name, + const char *sched_base_name, + odp_tm_sched_mode_t sched_mode, + uint8_t sched_weights[FANIN_RATIO]) +{ + odp_tm_sched_params_t sched_params; + odp_tm_sched_t sched_profile; + tm_node_desc_t *node_desc, *child_desc; + odp_tm_node_t tm_node, fanin_node; + uint32_t fanin_cnt, fanin, priority; + uint8_t sched_weight; + char sched_name[TM_NAME_LEN]; + int rc; + + node_desc = find_node_desc(0, node_name); + if (node_desc == NULL) + return -1; + + fanin_cnt = MIN(node_desc->num_children, FANIN_RATIO); + for (fanin = 0; fanin < fanin_cnt; fanin++) { + odp_tm_sched_params_init(&sched_params); + sched_weight = sched_weights[fanin]; + + /* Set the weights and mode the same for all priorities */ + for (priority = 0; priority < NUM_PRIORITIES; priority++) { + sched_params.sched_modes[priority] = sched_mode; + sched_params.sched_weights[priority] = sched_weight; + } + + /* Create the scheduler profile name using the sched_base_name + * and the fanin index */ + snprintf(sched_name, sizeof(sched_name), "%s_%u", + sched_base_name, fanin); + + /* First see if a sched profile already exists with this name, + * in which case we use that profile, else create a new one. */ + sched_profile = odp_tm_sched_lookup(sched_name); + if (sched_profile != ODP_TM_INVALID) { + odp_tm_sched_params_update(sched_profile, + &sched_params); + } else { + sched_profile = odp_tm_sched_create(sched_name, + &sched_params); + sched_profiles[num_sched_profiles] = sched_profile; + num_sched_profiles++; + } + + /* Apply the weights to the nodes fan-in. */ + child_desc = node_desc->children[fanin]; + tm_node = node_desc->node; + fanin_node = child_desc->node; + rc = odp_tm_node_sched_config(tm_node, fanin_node, + sched_profile); + if (rc != 0) + return -1; + } + + return 0; +} + +static int test_sched_queue_priority(const char *shaper_name, + const char *node_name, + uint32_t num_pkts) +{ + odp_tm_queue_t tm_queues[NUM_PRIORITIES]; + pkt_info_t pkt_info; + uint32_t pkt_cnt, pkts_in_order, base_idx; + uint32_t idx, unique_id, pkt_len, base_pkt_len, pkts_sent; + int priority; + + memset(unique_id_list, 0, sizeof(unique_id_list)); + for (priority = 0; priority < NUM_PRIORITIES; priority++) + tm_queues[priority] = find_tm_queue(0, node_name, priority); + + /* Enable the shaper to be low bandwidth. */ + pkt_len = 1400; + set_shaper(node_name, shaper_name, 64 * 1000, 4 * pkt_len); + + /* Make a couple of low priority dummy pkts first. */ + init_xmt_pkts(&pkt_info); + if (make_pkts(4, pkt_len, &pkt_info) != 0) + return -1; + + /* Now make "num_pkts" first at the lowest priority, then "num_pkts" + * at the second lowest priority, etc until "num_pkts" are made last + * at the highest priority (which is always priority 0). */ + pkt_cnt = NUM_PRIORITIES * num_pkts; + base_pkt_len = 256; + for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--) { + unique_id = cpu_unique_id; + pkt_info.pkt_class = priority + 1; + pkt_len = base_pkt_len + 64 * priority; + if (make_pkts(num_pkts, pkt_len, &pkt_info) != 0) + return -1; + + base_idx = priority * num_pkts; + for (idx = 0; idx < num_pkts; idx++) + unique_id_list[base_idx + idx] = unique_id++; + } + + /* Send the low priority dummy pkts first. The arrival order of + * these pkts will be ignored. */ + pkts_sent = send_pkts(tm_queues[NUM_PRIORITIES - 1], 4); + + /* Now send "num_pkts" first at the lowest priority, then "num_pkts" + * at the second lowest priority, etc until "num_pkts" are sent last + * at the highest priority. */ + for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--) + pkts_sent += send_pkts(tm_queues[priority], num_pkts); + + busy_wait(1000000); /* wait 1 millisecond */ + + /* Disable the shaper, so as to get the pkts out quicker. */ + set_shaper(node_name, shaper_name, 0, 0); + + num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, + pkt_cnt + 4, 64 * 1000); + + /* Check rcvd packet arrivals to make sure that pkts arrived in + * priority order, except for perhaps the first few lowest priority + * dummy pkts. */ + pkts_in_order = pkts_rcvd_in_given_order(unique_id_list, pkt_cnt, 0, + false, false); + if (pkts_in_order != pkt_cnt) { + LOG_ERR("pkts_sent=%u pkt_cnt=%u num_rcv_pkts=%u" + " rcvd_in_order=%u\n", pkts_sent, pkt_cnt, num_rcv_pkts, + pkts_in_order); + } + + CU_ASSERT(pkts_in_order == pkt_cnt); + + flush_leftover_pkts(odp_tm_systems[0], rcv_pktin); + CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0])); + return 0; +} + +static int test_sched_node_priority(const char *shaper_name, + const char *node_name, + uint32_t num_pkts) +{ + odp_tm_queue_t *tm_queues, tm_queue; + tm_node_desc_t *node_desc; + queue_array_t *queue_array; + pkt_info_t pkt_info; + uint32_t total_num_queues, max_queues, num_queues, pkt_cnt; + uint32_t pkts_in_order, base_idx, queue_idx, idx, unique_id; + uint32_t pkt_len, base_pkt_len, total_pkt_cnt, pkts_sent; + int priority; + + memset(unique_id_list, 0, sizeof(unique_id_list)); + node_desc = find_node_desc(0, node_name); + if (node_desc == NULL) + return -1; + + total_num_queues = 0; + for (priority = 0; priority < NUM_PRIORITIES; priority++) { + max_queues = NUM_LEVEL2_TM_NODES; + queue_array = &queues_set.queue_array[priority]; + tm_queues = queue_array->tm_queues; + num_queues = find_child_queues(0, node_desc, priority, + tm_queues, max_queues); + queue_array->num_queues = num_queues; + queue_array->priority = priority; + total_num_queues += num_queues; + } + + /* Enable the shaper to be low bandwidth. */ + pkt_len = 1400; + set_shaper(node_name, shaper_name, 64 * 1000, 4 * pkt_len); + + /* Make a couple of low priority large dummy pkts first. */ + init_xmt_pkts(&pkt_info); + if (make_pkts(4, pkt_len, &pkt_info) != 0) + return -1; + + /* Now make "num_pkts" for each tm_queue at the lowest priority, then + * "num_pkts" for each tm_queue at the second lowest priority, etc. + * until "num_pkts" for each tm_queue at the highest priority are made + * last. Note that the highest priority is always priority 0. */ + total_pkt_cnt = total_num_queues * num_pkts; + base_pkt_len = 256; + base_idx = 0; + for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--) { + unique_id = cpu_unique_id; + queue_array = &queues_set.queue_array[priority]; + num_queues = queue_array->num_queues; + pkt_cnt = num_queues * num_pkts; + pkt_info.pkt_class = priority + 1; + pkt_len = base_pkt_len + 64 * priority; + if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0) + return -1; + + base_idx = priority * num_pkts; + for (idx = 0; idx < pkt_cnt; idx++) + unique_id_list[base_idx + idx] = unique_id++; + } + + /* Send the low priority dummy pkts first. The arrival order of + * these pkts will be ignored. */ + queue_array = &queues_set.queue_array[NUM_PRIORITIES - 1]; + tm_queue = queue_array->tm_queues[0]; + pkts_sent = send_pkts(tm_queue, 4); + + /* Now send "num_pkts" for each tm_queue at the lowest priority, then + * "num_pkts" for each tm_queue at the second lowest priority, etc. + * until "num_pkts" for each tm_queue at the highest priority are sent + * last. */ + for (priority = NUM_PRIORITIES - 1; 0 <= priority; priority--) { + queue_array = &queues_set.queue_array[priority]; + num_queues = queue_array->num_queues; + for (queue_idx = 0; queue_idx < num_queues; queue_idx++) { + tm_queue = queue_array->tm_queues[queue_idx]; + pkts_sent += send_pkts(tm_queue, num_pkts); + } + } + + busy_wait(1000000); /* wait 1 millisecond */ + + /* Disable the shaper, so as to get the pkts out quicker. */ + set_shaper(node_name, shaper_name, 0, 0); + + num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, + pkts_sent, 64 * 1000); + + /* Check rcvd packet arrivals to make sure that pkts arrived in + * priority order, except for perhaps the first few lowest priority + * dummy pkts. */ + pkts_in_order = pkts_rcvd_in_given_order(unique_id_list, total_pkt_cnt, + 0, false, false); + CU_ASSERT(pkts_in_order == total_pkt_cnt); + + flush_leftover_pkts(odp_tm_systems[0], rcv_pktin); + CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0])); + return 0; +} + +static int test_sched_wfq(const char *sched_base_name, + const char *shaper_name, + const char *node_name, + odp_tm_sched_mode_t sched_mode, + uint8_t sched_weights[FANIN_RATIO]) +{ + odp_tm_queue_t tm_queues[FANIN_RATIO], tm_queue; + tm_node_desc_t *node_desc, *child_desc; + rcv_stats_t rcv_stats[FANIN_RATIO]; + pkt_info_t pkt_info; + uint32_t fanin_cnt, fanin, num_queues, pkt_cnt; + uint32_t pkt_len, pkts_sent, pkt_idx; + uint8_t pkt_class; + int priority, rc; + + memset(tm_queues, 0, sizeof(tm_queues)); + node_desc = find_node_desc(0, node_name); + if (node_desc == NULL) + return -1; + + rc = set_sched_fanin(node_name, sched_base_name, sched_mode, + sched_weights); + if (rc != 0) + return -1; + + /* Now determine at least one tm_queue that feeds into each fanin/ + * child node. */ + priority = 0; + fanin_cnt = MIN(node_desc->num_children, FANIN_RATIO); + for (fanin = 0; fanin < fanin_cnt; fanin++) { + child_desc = node_desc->children[fanin]; + num_queues = find_child_queues(0, child_desc, priority, + &tm_queues[fanin], 1); + if (num_queues != 1) + return -1; + } + + /* Enable the shaper to be low bandwidth. */ + pkt_len = 1400; + set_shaper(node_name, shaper_name, 64 * 1000, 8 * pkt_len); + + /* Make a couple of low priority dummy pkts first. */ + init_xmt_pkts(&pkt_info); + if (make_pkts(4, pkt_len, &pkt_info) != 0) + return -1; + + /* Make 100 pkts for each fanin of this node, alternating amongst + * the inputs. */ + pkt_cnt = FANIN_RATIO * 100; + fanin = 0; + for (pkt_idx = 0; pkt_idx < pkt_cnt; pkt_idx++) { + pkt_len = 128 + 128 * fanin; + pkt_info.pkt_class = 1 + fanin++; + if (make_pkts(1, pkt_len, &pkt_info) != 0) + return -1; + + if (FANIN_RATIO <= fanin) + fanin = 0; + } + + /* Send the low priority dummy pkts first. The arrival order of + * these pkts will be ignored. */ + pkts_sent = send_pkts(tm_queues[NUM_PRIORITIES - 1], 4); + + /* Now send the test pkts, alternating amongst the input queues. */ + fanin = 0; + for (pkt_idx = 0; pkt_idx < pkt_cnt; pkt_idx++) { + tm_queue = tm_queues[fanin++]; + pkts_sent += send_pkts(tm_queue, 1); + if (FANIN_RATIO <= fanin) + fanin = 0; + } + + busy_wait(1000000); /* wait 1 millisecond */ + + /* Disable the shaper, so as to get the pkts out quicker. */ + set_shaper(node_name, shaper_name, 0, 0); + + num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, + pkt_cnt + 4, 64 * 1000); + + /* Check rcvd packet arrivals to make sure that pkts arrived in + * an order commensurate with their weights, sched mode and pkt_len. */ + for (fanin = 0; fanin < fanin_cnt; fanin++) { + pkt_class = 1 + fanin; + CU_ASSERT(rcv_rate_stats(&rcv_stats[fanin], pkt_class) == 0); + } + + flush_leftover_pkts(odp_tm_systems[0], rcv_pktin); + CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0])); + return 0; +} + +static int set_queue_thresholds(odp_tm_queue_t tm_queue, + const char *threshold_name, + odp_tm_threshold_params_t *threshold_params) +{ + odp_tm_threshold_t threshold_profile; + + /* First see if a threshold profile already exists with this name, in + * which case we use that profile, else create a new one. */ + threshold_profile = odp_tm_thresholds_lookup(threshold_name); + if (threshold_profile != ODP_TM_INVALID) { + odp_tm_thresholds_params_update(threshold_profile, + threshold_params); + } else { + threshold_profile = odp_tm_threshold_create(threshold_name, + threshold_params); + threshold_profiles[num_threshold_profiles] = threshold_profile; + num_threshold_profiles++; + } + + return odp_tm_queue_threshold_config(tm_queue, threshold_profile); +} + +static int test_threshold(const char *threshold_name, + const char *shaper_name, + const char *node_name, + uint8_t priority, + uint32_t max_pkts, + uint32_t max_bytes) +{ + odp_tm_threshold_params_t threshold_params; + odp_tm_queue_t tm_queue; + pkt_info_t pkt_info; + uint32_t num_pkts, pkt_len, pkts_sent; + + odp_tm_threshold_params_init(&threshold_params); + if (max_pkts != 0) { + max_pkts = MIN(max_pkts, MAX_PKTS / 3); + threshold_params.max_pkts = max_pkts; + threshold_params.enable_max_pkts = true; + num_pkts = 2 * max_pkts; + pkt_len = 256; + } else if (max_bytes != 0) { + max_bytes = MIN(max_bytes, MAX_PKTS * MAX_PAYLOAD / 3); + threshold_params.max_bytes = max_bytes; + threshold_params.enable_max_bytes = true; + num_pkts = 2 * max_bytes / MAX_PAYLOAD; + pkt_len = MAX_PAYLOAD; + } else { + return -1; + } + + /* Pick a tm_queue and set the tm_queue's threshold profile and then + * send in twice the amount of traffic as suggested by the thresholds + * and make sure at least SOME pkts get dropped. */ + tm_queue = find_tm_queue(0, node_name, priority); + if (set_queue_thresholds(tm_queue, threshold_name, + &threshold_params) != 0) { + LOG_ERR("set_queue_thresholds failed\n"); + return -1; + } + + /* Enable the shaper to be very low bandwidth. */ + set_shaper(node_name, shaper_name, 256 * 1000, 8 * pkt_len); + + init_xmt_pkts(&pkt_info); + pkt_info.drop_eligible = true; + pkt_info.pkt_class = 1; + if (make_pkts(num_pkts, pkt_len, &pkt_info) != 0) { + LOG_ERR("make_pkts failed\n"); + return -1; + } + + pkts_sent = send_pkts(tm_queue, num_pkts); + + num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent, + 1 * GBPS); + + /* Disable the shaper, so as to get the pkts out quicker. */ + set_shaper(node_name, shaper_name, 0, 0); + flush_leftover_pkts(odp_tm_systems[0], rcv_pktin); + CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0])); + + if (num_rcv_pkts < num_pkts) + return 0; + + CU_ASSERT(num_rcv_pkts < pkts_sent); + return 0; +} + +static wred_pkt_cnts_t *search_expected_pkt_rcv_tbl(odp_tm_percent_t confidence, + odp_tm_percent_t drop_perc) +{ + wred_pkt_cnts_t *wred_pkt_cnts; + uint32_t idx, table_size; + + /* Search the EXPECTED_PKT_RCVD table to find a matching entry */ + table_size = sizeof(EXPECTED_PKT_RCVD) / sizeof(wred_pkt_cnts_t); + for (idx = 0; idx < table_size; idx++) { + wred_pkt_cnts = &EXPECTED_PKT_RCVD[idx]; + if ((wred_pkt_cnts->confidence_percent == confidence) && + (wred_pkt_cnts->drop_percent == drop_perc)) + return wred_pkt_cnts; + } + + return NULL; +} + +static int set_queue_wred(odp_tm_queue_t tm_queue, + const char *wred_name, + uint8_t pkt_color, + odp_tm_percent_t drop_percent, + odp_bool_t use_byte_fullness, + odp_bool_t use_dual_slope) +{ + odp_tm_wred_params_t wred_params; + odp_tm_wred_t wred_profile; + + odp_tm_wred_params_init(&wred_params); + if (use_dual_slope) { + wred_params.min_threshold = TM_PERCENT(20); + wred_params.med_threshold = TM_PERCENT(40); + wred_params.med_drop_prob = drop_percent; + wred_params.max_drop_prob = drop_percent; + } else { + wred_params.min_threshold = 0; + wred_params.med_threshold = TM_PERCENT(20); + wred_params.med_drop_prob = 0; + wred_params.max_drop_prob = 2 * drop_percent; + } + + wred_params.enable_wred = true; + wred_params.use_byte_fullness = use_byte_fullness; + + /* First see if a wred profile already exists with this name, in + * which case we use that profile, else create a new one. */ + wred_profile = odp_tm_wred_lookup(wred_name); + if (wred_profile != ODP_TM_INVALID) { + odp_tm_wred_params_update(wred_profile, &wred_params); + } else { + wred_profile = odp_tm_wred_create(wred_name, &wred_params); + if (wred_profiles[num_wred_profiles - 1][pkt_color] == + ODP_TM_INVALID) { + wred_profiles[num_wred_profiles - 1][pkt_color] = + wred_profile; + } else { + wred_profiles[num_wred_profiles][pkt_color] = + wred_profile; + num_wred_profiles++; + } + } + + return odp_tm_queue_wred_config(tm_queue, pkt_color, wred_profile); +} + +static int test_byte_wred(const char *wred_name, + const char *shaper_name, + const char *threshold_name, + const char *node_name, + uint8_t priority, + uint8_t pkt_color, + odp_tm_percent_t drop_percent, + odp_bool_t use_dual_slope) +{ + odp_tm_threshold_params_t threshold_params; + wred_pkt_cnts_t *wred_pkt_cnts; + odp_tm_queue_t tm_queue; + pkt_info_t pkt_info; + uint32_t num_fill_pkts, num_test_pkts, pkts_sent; + + /* Pick the tm_queue and set the tm_queue's wred profile to drop the + * given percentage of traffic, then send 100 pkts and see how many + * pkts are received. */ + tm_queue = find_tm_queue(0, node_name, priority); + set_queue_wred(tm_queue, wred_name, pkt_color, drop_percent, + true, use_dual_slope); + + /* Enable the shaper to be very low bandwidth. */ + set_shaper(node_name, shaper_name, 64 * 1000, 8 * PKT_BUF_SIZE); + + /* Set the threshold to be byte based and to handle 200 pkts of + * size PKT_BUF_SIZE. This way the byte-fullness for the wred test + * pkts will be around 60%. */ + odp_tm_threshold_params_init(&threshold_params); + threshold_params.max_bytes = 200 * PKT_BUF_SIZE; + threshold_params.enable_max_bytes = true; + if (set_queue_thresholds(tm_queue, threshold_name, + &threshold_params) != 0) { + LOG_ERR("set_queue_thresholds failed\n"); + return -1; + } + + /* Make and send the first batch of pkts whose job is to set the + * queue byte fullness to around 60% for the subsequent test packets. + * These packets MUST have drop_eligible false. */ + init_xmt_pkts(&pkt_info); + num_fill_pkts = 120; + pkt_info.pkt_color = pkt_color; + pkt_info.pkt_class = 0; + pkt_info.drop_eligible = false; + if (make_pkts(num_fill_pkts, PKT_BUF_SIZE, &pkt_info) != 0) + return -1; + + send_pkts(tm_queue, num_fill_pkts); + + /* Now send the real test pkts, which are all small so as to try to + * keep the byte fullness still close to the 60% point. These pkts + * MUST have drop_eligible true. */ + num_test_pkts = 100; + pkt_info.pkt_class = 1; + pkt_info.drop_eligible = true; + if (make_pkts(num_test_pkts, 128, &pkt_info) != 0) + return -1; + + pkts_sent = send_pkts(tm_queue, num_test_pkts); + + /* Disable the shaper, so as to get the pkts out quicker. */ + set_shaper(node_name, shaper_name, 0, 0); + num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, + num_fill_pkts + pkts_sent, 64 * 1000); + + /* Search the EXPECTED_PKT_RCVD table to find a matching entry */ + wred_pkt_cnts = search_expected_pkt_rcv_tbl(TM_PERCENT(99.9), + drop_percent); + if (wred_pkt_cnts == NULL) + return -1; + + flush_leftover_pkts(odp_tm_systems[0], rcv_pktin); + CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0])); + + if ((wred_pkt_cnts->min_cnt <= pkts_sent) && + (pkts_sent <= wred_pkt_cnts->max_cnt)) + return 0; + + CU_ASSERT((wred_pkt_cnts->min_cnt <= pkts_sent) && + (pkts_sent <= wred_pkt_cnts->max_cnt)); + return 0; +} + +static int test_pkt_wred(const char *wred_name, + const char *shaper_name, + const char *threshold_name, + const char *node_name, + uint8_t priority, + uint8_t pkt_color, + odp_tm_percent_t drop_percent, + odp_bool_t use_dual_slope) +{ + odp_tm_threshold_params_t threshold_params; + wred_pkt_cnts_t *wred_pkt_cnts; + odp_tm_queue_t tm_queue; + pkt_info_t pkt_info; + uint32_t num_fill_pkts, num_test_pkts, pkts_sent; + + /* Pick the tm_queue and set the tm_queue's wred profile to drop the + * given percentage of traffic, then send 100 pkts and see how many + * pkts are received. */ + tm_queue = find_tm_queue(0, node_name, priority); + set_queue_wred(tm_queue, wred_name, pkt_color, drop_percent, + false, use_dual_slope); + + /* Enable the shaper to be very low bandwidth. */ + set_shaper(node_name, shaper_name, 64 * 1000, 1000); + + /* Set the threshold to be pkt based and to handle 1000 pkts. This + * way the pkt-fullness for the wred test pkts will be around 60%. */ + odp_tm_threshold_params_init(&threshold_params); + threshold_params.max_pkts = 1000; + threshold_params.enable_max_pkts = true; + if (set_queue_thresholds(tm_queue, threshold_name, + &threshold_params) != 0) { + LOG_ERR("set_queue_thresholds failed\n"); + return -1; + } + + /* Make and send the first batch of pkts whose job is to set the + * queue pkt fullness to around 60% for the subsequent test packets. + * These packets MUST have drop_eligible false. */ + init_xmt_pkts(&pkt_info); + num_fill_pkts = 600; + pkt_info.pkt_color = pkt_color; + pkt_info.pkt_class = 0; + pkt_info.drop_eligible = false; + if (make_pkts(num_fill_pkts, 80, &pkt_info) != 0) + return -1; + + send_pkts(tm_queue, num_fill_pkts); + + /* Now send the real test pkts. These pkts MUST have drop_eligible + * true. */ + num_test_pkts = 100; + pkt_info.pkt_class = 1; + pkt_info.drop_eligible = true; + if (make_pkts(num_test_pkts, 80, &pkt_info) != 0) + return -1; + + pkts_sent = send_pkts(tm_queue, num_test_pkts); + + /* Disable the shaper, so as to get the pkts out quicker. */ + set_shaper(node_name, shaper_name, 0, 0); + num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, + num_fill_pkts + pkts_sent, 64 * 1000); + + /* Search the EXPECTED_PKT_RCVD table to find a matching entry */ + wred_pkt_cnts = search_expected_pkt_rcv_tbl(TM_PERCENT(99.9), + drop_percent); + if (wred_pkt_cnts == NULL) + return -1; + + flush_leftover_pkts(odp_tm_systems[0], rcv_pktin); + CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0])); + + if ((wred_pkt_cnts->min_cnt <= pkts_sent) && + (pkts_sent <= wred_pkt_cnts->max_cnt)) + return 0; + + CU_ASSERT((wred_pkt_cnts->min_cnt <= pkts_sent) && + (pkts_sent <= wred_pkt_cnts->max_cnt)); + return 0; +} + +static int test_query_functions(const char *shaper_name, + const char *node_name, + uint8_t priority, + uint32_t num_pkts) +{ + odp_tm_query_info_t query_info; + odp_tm_queue_t tm_queue; + pkt_info_t pkt_info; + uint64_t commit_bps, expected_pkt_cnt, expected_byte_cnt; + int rc; + + /* Pick a tm_queue and set the egress node's shaper BW to be 64K bps + * with a small burst tolerance. Then send the traffic. */ + tm_queue = find_tm_queue(0, node_name, priority); + commit_bps = 64 * 1000; + if (set_shaper(node_name, shaper_name, commit_bps, 1000) != 0) + return -1; + + init_xmt_pkts(&pkt_info); + pkt_info.pkt_class = 1; + if (make_pkts(num_pkts, PKT_BUF_SIZE, &pkt_info) != 0) + return -1; + + send_pkts(tm_queue, num_pkts); + + /* Assume all but 2 of the pkts are still in the queue.*/ + expected_pkt_cnt = num_pkts - 2; + expected_byte_cnt = expected_pkt_cnt * PKT_BUF_SIZE; + + rc = odp_tm_queue_query(tm_queue, + ODP_TM_QUERY_PKT_CNT | ODP_TM_QUERY_BYTE_CNT, + &query_info); + CU_ASSERT(rc == 0); + CU_ASSERT(query_info.total_pkt_cnt_valid); + CU_ASSERT(expected_pkt_cnt < query_info.total_pkt_cnt); + CU_ASSERT(query_info.total_byte_cnt_valid); + CU_ASSERT(expected_byte_cnt < query_info.total_byte_cnt); + + rc = odp_tm_priority_query(odp_tm_systems[0], priority, + ODP_TM_QUERY_PKT_CNT | ODP_TM_QUERY_BYTE_CNT, + &query_info); + CU_ASSERT(rc == 0); + CU_ASSERT(query_info.total_pkt_cnt_valid); + CU_ASSERT(expected_pkt_cnt < query_info.total_pkt_cnt); + CU_ASSERT(query_info.total_byte_cnt_valid); + CU_ASSERT(expected_byte_cnt < query_info.total_byte_cnt); + + rc = odp_tm_total_query(odp_tm_systems[0], + ODP_TM_QUERY_PKT_CNT | ODP_TM_QUERY_BYTE_CNT, + &query_info); + CU_ASSERT(rc == 0); + CU_ASSERT(query_info.total_pkt_cnt_valid); + CU_ASSERT(expected_pkt_cnt < query_info.total_pkt_cnt); + CU_ASSERT(query_info.total_byte_cnt_valid); + CU_ASSERT(expected_byte_cnt < query_info.total_byte_cnt); + + /* Disable the shaper, so as to get the pkts out quicker. */ + set_shaper(node_name, shaper_name, 0, 0); + num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, num_pkts, + commit_bps); + + flush_leftover_pkts(odp_tm_systems[0], rcv_pktin); + CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0])); + return 0; +} + +static int check_vlan_marking_pkts(void) +{ + odp_packet_t rcv_pkt; + uint32_t rcv_pkt_idx, err_cnt; + uint16_t tci; + uint8_t pkt_class, dei, expected_dei; + + /* Check rcvd packets to make sure that pkt_class 1 pkts continue to + * not have a VLAN header, pkt class 2 pkts have a VLAN header with the + * drop precedence not set and pkt class 3 pkts have a VLAN header with + * the DEI bit set. */ + err_cnt = 0; + for (rcv_pkt_idx = 0; rcv_pkt_idx < num_rcv_pkts; rcv_pkt_idx++) { + rcv_pkt = rcv_pkts[rcv_pkt_idx]; + pkt_class = rcv_pkt_descs[rcv_pkt_idx].pkt_class; + + switch (pkt_class) { + case 1: + /* Make sure no VLAN header. */ + if (odp_packet_has_vlan(rcv_pkt)) { + err_cnt++; + LOG_ERR("VLAN incorrectly added\n"); + CU_ASSERT(odp_packet_has_vlan(rcv_pkt)); + } + break; + + case 2: + case 3: + /* Make sure it does have a VLAN header */ + if (!odp_packet_has_vlan(rcv_pkt)) { + err_cnt++; + LOG_ERR("VLAN header missing\n"); + CU_ASSERT(!odp_packet_has_vlan(rcv_pkt)); + break; + } + + /* Make sure DEI bit is 0 if pkt_class == 2, and 1 if + * pkt_class == 3. */ + if (get_vlan_tci(rcv_pkt, &tci) != 0) { + err_cnt++; + LOG_ERR("VLAN header missing\n"); + CU_ASSERT(!odp_packet_has_vlan(rcv_pkt)); + break; + } + + dei = (tci >> ODPH_VLANHDR_DEI_SHIFT) & 1; + expected_dei = (pkt_class == 2) ? 0 : 1; + if (dei != expected_dei) { + LOG_ERR("expected_dei=%u rcvd dei=%u\n", + expected_dei, dei); + err_cnt++; + CU_ASSERT(dei == expected_dei); + } + break; + + default: + /* Log error but otherwise ignore, since it is + * probably a stray pkt from a previous test. */ + LOG_ERR("Pkt rcvd with invalid pkt class\n"); + } + } + + return (err_cnt == 0) ? 0 : -1; +} + +static int test_vlan_marking(const char *node_name, + odp_packet_color_t pkt_color) +{ + odp_packet_color_t color; + odp_tm_queue_t tm_queue; + pkt_info_t pkt_info; + odp_tm_t odp_tm; + uint32_t pkt_cnt, num_pkts, pkt_len, pkts_sent; + int rc; + + /* First disable vlan marking for all colors. These "disable" calls + * should NEVER fail. */ + odp_tm = odp_tm_systems[0]; + for (color = 0; color < ODP_NUM_PKT_COLORS; color++) { + rc = odp_tm_vlan_marking(odp_tm, color, false); + if (rc != 0) { + LOG_ERR("disabling odp_tm_vlan_marking() failed\n"); + return -1; + } + } + + /* Next enable vlan marking for just the given color parameter */ + rc = odp_tm_vlan_marking(odp_tm, pkt_color, true); + + tm_queue = find_tm_queue(0, node_name, 0); + if (tm_queue == ODP_TM_INVALID) { + LOG_ERR("No tm_queue found for node_name='%s'\n", node_name); + return -1; + } + + /* Next make 2*X pkts of each color, half with vlan headers - + * half without. */ + init_xmt_pkts(&pkt_info); + + pkt_cnt = 5; + num_pkts = 0; + pkt_len = 600; + pkt_info.pkt_class = 1; + for (color = 0; color < ODP_NUM_PKT_COLORS; color++) { + num_pkts += pkt_cnt; + pkt_info.pkt_color = color; + if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0) + return -1; + } + + for (color = 0; color < ODP_NUM_PKT_COLORS; color++) { + num_pkts += pkt_cnt; + pkt_info.pkt_color = color; + pkt_info.pkt_class = (color == pkt_color) ? 3 : 2; + pkt_info.use_vlan = true; + pkt_info.vlan_tci = VLAN_NO_DEI; + if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0) + return -1; + } + + pkts_sent = send_pkts(tm_queue, num_pkts); + num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent, + 1000 * 1000); + if (num_rcv_pkts == 0) { + LOG_ERR("No pkts received\n"); + rc = -1; + } else if (num_rcv_pkts != pkts_sent) { + LOG_ERR("pkts_sent=%u but num_rcv_pkts=%u\n", + pkts_sent, num_rcv_pkts); + dump_rcvd_pkts(0, num_rcv_pkts - 1); + CU_ASSERT(num_rcv_pkts == pkts_sent); + } else { + rc = check_vlan_marking_pkts(); + } + + flush_leftover_pkts(odp_tm_systems[0], rcv_pktin); + CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0])); + return rc; +} + +static int check_tos_marking_pkts(odp_bool_t use_ipv6, + odp_bool_t use_tcp, + odp_bool_t test_ecn, + odp_bool_t test_drop_prec, + uint8_t unmarked_tos, + uint8_t new_dscp, + uint8_t dscp_mask) +{ + odp_packet_t rcv_pkt; + uint32_t rcv_pkt_idx; + uint8_t unmarked_ecn, unmarked_dscp, shifted_dscp, pkt_class; + uint8_t tos, expected_tos; + int rc; + + /* Turn off test_ecn for UDP pkts, since ECN marking should + * only happen for TCP pkts. */ + if (!use_tcp) + test_ecn = false; + + /* The expected_tos value is only the expected TOS/TC field for pkts + * that have been enabled for modification, as indicated by the + * pkt_class associated with this pkt. */ + unmarked_ecn = (unmarked_tos & ODPH_IP_TOS_ECN_MASK) + >> ODPH_IP_TOS_ECN_SHIFT; + unmarked_dscp = (unmarked_tos & ODPH_IP_TOS_DSCP_MASK) + >> ODPH_IP_TOS_DSCP_SHIFT; + new_dscp = (new_dscp & dscp_mask) | (unmarked_dscp & ~dscp_mask); + shifted_dscp = new_dscp << ODPH_IP_TOS_DSCP_SHIFT; + + if (test_ecn && test_drop_prec) + expected_tos = shifted_dscp | ODPH_IP_ECN_CE; + else if (test_ecn) + expected_tos = unmarked_tos | ODPH_IP_ECN_CE; + else if (test_drop_prec) + expected_tos = shifted_dscp | unmarked_ecn; + else + expected_tos = unmarked_tos; + + for (rcv_pkt_idx = 0; rcv_pkt_idx < num_rcv_pkts; rcv_pkt_idx++) { + rcv_pkt = rcv_pkts[rcv_pkt_idx]; + pkt_class = rcv_pkt_descs[rcv_pkt_idx].pkt_class; + + /* Check that the pkts match the use_ipv6 setting */ + if (use_ipv6) + rc = odp_packet_has_ipv6(rcv_pkt); + else + rc = odp_packet_has_ipv4(rcv_pkt); + + if (rc != 1) { + if (use_ipv6) + LOG_ERR("Expected IPv6 pkt but got IPv4"); + else + LOG_ERR("Expected IPv4 pkt but got IPv6"); + + return -1; + } + + /* Check that the pkts match the use_tcp setting */ + if (use_tcp) + rc = odp_packet_has_tcp(rcv_pkt); + else + rc = odp_packet_has_udp(rcv_pkt); + + if (rc != 1) { + if (use_tcp) + LOG_ERR("Expected TCP pkt but got UDP"); + else + LOG_ERR("Expected UDP pkt but got TCP"); + + return -1; + } + + /* Now get the tos field to see if it was changed */ + rc = get_ip_tos(rcv_pkt, &tos); + if (rc != 0) { + LOG_ERR("get_ip_tos failed\n"); + return -1; + } + + switch (pkt_class) { + case 2: + /* Tos field must be unchanged. */ + if (unmarked_tos != tos) { + LOG_ERR("Tos was changed from 0x%X to 0x%X\n", + unmarked_tos, tos); + return -1; + } + break; + + case 3: + /* Tos field must be changed. */ + if (tos != expected_tos) { + LOG_ERR("tos=0x%X instead of expected 0x%X\n", + tos, expected_tos); + CU_ASSERT(tos == expected_tos); + } + break; + + default: + /* Log error but otherwise ignore, since it is + * probably a stray pkt from a previous test. */ + LOG_ERR("Pkt rcvd with invalid pkt class=%u\n", + pkt_class); + } + } + + return 0; +} + +static int test_ip_marking(const char *node_name, + odp_packet_color_t pkt_color, + odp_bool_t use_ipv6, + odp_bool_t use_tcp, + odp_bool_t test_ecn, + odp_bool_t test_drop_prec, + uint8_t new_dscp, + uint8_t dscp_mask) +{ + odp_packet_color_t color; + odp_tm_queue_t tm_queue; + pkt_info_t pkt_info; + odp_tm_t odp_tm; + uint32_t pkt_cnt, num_pkts, pkt_len, pkts_sent; + int rc, ret_code; + + /* First disable IP TOS marking for all colors. These "disable" calls + * should NEVER fail. */ + odp_tm = odp_tm_systems[0]; + for (color = 0; color < ODP_NUM_PKT_COLORS; color++) { + rc = odp_tm_ecn_marking(odp_tm, color, false); + if (rc != 0) { + LOG_ERR("disabling odp_tm_ecn_marking() failed\n"); + return -1; + } + + rc = odp_tm_drop_prec_marking(odp_tm, color, false); + if (rc != 0) { + LOG_ERR("disabling odp_tm_drop_prec_marking failed\n"); + return -1; + } + } + + /* Next enable IP TOS marking for just the given color parameter */ + if ((!test_ecn) && (!test_drop_prec)) + return 0; + + if (test_ecn) { + rc = odp_tm_ecn_marking(odp_tm, pkt_color, true); + if (rc != 0) { + LOG_ERR("odp_tm_ecn_marking() call failed\n"); + return -1; + } + } + + if (test_drop_prec) { + rc = odp_tm_drop_prec_marking(odp_tm, pkt_color, true); + if (rc != 0) { + LOG_ERR("odp_tm_drop_prec_marking() call failed\n"); + return -1; + } + } + + tm_queue = find_tm_queue(0, node_name, 0); + if (tm_queue == ODP_TM_INVALID) { + LOG_ERR("No tm_queue found for node_name='%s'\n", node_name); + return -1; + } + + init_xmt_pkts(&pkt_info); + pkt_info.use_ipv6 = use_ipv6; + pkt_info.use_tcp = use_tcp; + pkt_info.ip_tos = DEFAULT_TOS; + + pkt_cnt = 5; + num_pkts = 0; + pkt_len = 1340; + for (color = 0; color < ODP_NUM_PKT_COLORS; color++) { + num_pkts += pkt_cnt; + pkt_info.pkt_color = color; + if (test_drop_prec || (test_ecn && use_tcp)) + pkt_info.pkt_class = (color == pkt_color) ? 3 : 2; + else + pkt_info.pkt_class = 2; + + if (make_pkts(pkt_cnt, pkt_len, &pkt_info) != 0) { + LOG_ERR("make_pkts failed\n"); + return -1; + } + } + + pkts_sent = send_pkts(tm_queue, num_pkts); + num_rcv_pkts = receive_pkts(odp_tm_systems[0], rcv_pktin, pkts_sent, + 1000 * 1000); + ret_code = -1; + + if (num_rcv_pkts == 0) { + LOG_ERR("No pkts received\n"); + CU_ASSERT(num_rcv_pkts != 0); + ret_code = -1; + } else if (num_rcv_pkts != pkts_sent) { + LOG_ERR("pkts_sent=%u but num_rcv_pkts=%u\n", + pkts_sent, num_rcv_pkts); + dump_rcvd_pkts(0, num_rcv_pkts - 1); + CU_ASSERT(num_rcv_pkts == pkts_sent); + ret_code = -1; + } else { + rc = check_tos_marking_pkts(use_ipv6, use_tcp, test_ecn, + test_drop_prec, DEFAULT_TOS, + new_dscp, dscp_mask); + CU_ASSERT(rc == 0); + ret_code = (rc == 0) ? 0 : -1; + } + + flush_leftover_pkts(odp_tm_systems[0], rcv_pktin); + CU_ASSERT(odp_tm_is_idle(odp_tm_systems[0])); + return ret_code; +} + +static int test_protocol_marking(const char *node_name, + odp_packet_color_t pkt_color, + odp_bool_t test_ecn, + odp_bool_t test_drop_prec, + uint8_t new_dscp, + uint8_t dscp_mask) +{ + uint32_t errs = 0; + int rc; + + /* Now call test_ip_marking once for all combinations of IPv4 or IPv6 + * pkts AND for UDP or TCP. */ + rc = test_ip_marking(node_name, pkt_color, USE_IPV4, USE_UDP, + test_ecn, test_drop_prec, new_dscp, dscp_mask); + CU_ASSERT(rc == 0); + if (rc != 0) { + LOG_ERR("test_ip_marking failed using IPV4/UDP pkts color=%u " + "test_ecn=%u test_drop_prec=%u\n", + pkt_color, test_ecn, test_drop_prec); + errs++; + } + + rc = test_ip_marking(node_name, pkt_color, USE_IPV6, USE_UDP, + test_ecn, test_drop_prec, new_dscp, dscp_mask); + CU_ASSERT(rc == 0); + if (rc != 0) { + LOG_ERR("test_ip_marking failed using IPV6/UDP pkts color=%u " + "test_ecn=%u test_drop_prec=%u\n", + pkt_color, test_ecn, test_drop_prec); + errs++; + } + + rc = test_ip_marking(node_name, pkt_color, USE_IPV4, USE_TCP, + test_ecn, test_drop_prec, new_dscp, dscp_mask); + CU_ASSERT(rc == 0); + if (rc != 0) { + LOG_ERR("test_ip_marking failed using IPV4/TCP pkts color=%u " + "test_ecn=%u test_drop_prec=%u\n", + pkt_color, test_ecn, test_drop_prec); + errs++; + } + + rc = test_ip_marking(node_name, pkt_color, USE_IPV6, USE_TCP, + test_ecn, test_drop_prec, new_dscp, dscp_mask); + CU_ASSERT(rc == 0); + if (rc != 0) { + LOG_ERR("test_ip_marking failed using IPV6/TCP pkts color=%u " + "test_ecn=%u test_drop_prec=%u\n", + pkt_color, test_ecn, test_drop_prec); + errs++; + } + + return (errs == 0) ? 0 : -1; +} + +static int ip_marking_tests(const char *node_name, + odp_bool_t test_ecn, + odp_bool_t test_drop_prec) +{ + odp_packet_color_t color; + uint32_t errs = 0; + uint8_t new_dscp, dscp_mask; + int rc; + + dscp_mask = DROP_PRECEDENCE_MASK; + for (color = 0; color < ODP_NUM_PKT_COLORS; color++) { + if (tm_capabilities.marking_colors_supported[color]) { + if (color == PKT_YELLOW) + new_dscp = MEDIUM_DROP_PRECEDENCE; + else if (color == PKT_RED) + new_dscp = HIGH_DROP_PRECEDENCE; + else + new_dscp = LOW_DROP_PRECEDENCE; + + rc = test_protocol_marking(node_name, color, test_ecn, + test_drop_prec, new_dscp, + dscp_mask); + CU_ASSERT(rc == 0); + if (rc != 0) + errs++; + } + } + + return (errs == 0) ? 0 : -1; +} + +static int walk_tree_backwards(odp_tm_node_t tm_node) +{ + odp_tm_node_fanin_info_t fanin_info; + odp_tm_node_info_t node_info; + odp_tm_queue_t first_tm_queue; + odp_tm_node_t first_tm_node; + uint32_t tm_queue_fanin, tm_node_fanin; + int rc; + + /* Start from the given tm_node and try to go backwards until a valid + * and active tm_queue is reached. */ + rc = odp_tm_node_info(tm_node, &node_info); + if (rc != 0) { + LOG_ERR("odp_tm_node_info failed for tm_node=0x%" PRIX64 "\n", + tm_node); + return rc; + } + + if ((node_info.tm_queue_fanin == 0) && + (node_info.tm_node_fanin == 0)) { + LOG_ERR("odp_tm_node_info showed no fanin for this node\n"); + return -1; + } + + fanin_info.tm_queue = ODP_TM_INVALID; + fanin_info.tm_node = ODP_TM_INVALID; + fanin_info.is_last = false; + + /* TBD* Loop over the entire fanin list verifying the fanin counts. + * Also remember the first tm_queue and tm_node seen. */ + tm_queue_fanin = 0; + tm_node_fanin = 0; + first_tm_queue = ODP_TM_INVALID; + first_tm_node = ODP_TM_INVALID; + + while (!fanin_info.is_last) { + rc = odp_tm_node_fanin_info(tm_node, &fanin_info); + if (rc != 0) + return rc; + + if ((fanin_info.tm_queue != ODP_TM_INVALID) && + (fanin_info.tm_node != ODP_TM_INVALID)) { + LOG_ERR("Both tm_queue and tm_node are set\n"); + return -1; + } else if (fanin_info.tm_queue != ODP_TM_INVALID) { + tm_queue_fanin++; + if (first_tm_queue == ODP_TM_INVALID) + first_tm_queue = fanin_info.tm_queue; + } else if (fanin_info.tm_node != ODP_TM_INVALID) { + tm_node_fanin++; + if (first_tm_node == ODP_TM_INVALID) + first_tm_node = fanin_info.tm_node; + } else { + LOG_ERR("both tm_queue and tm_node are INVALID\n"); + return -1; + } + } + + if (tm_queue_fanin != node_info.tm_queue_fanin) + LOG_ERR("tm_queue_fanin count error\n"); + else if (tm_node_fanin != node_info.tm_node_fanin) + LOG_ERR("tm_node_fanin count error\n"); + + /* If we have found a tm_queue then we are successfully done. */ + if (first_tm_queue != ODP_TM_INVALID) + return 0; + + /* Now recurse up a level */ + return walk_tree_backwards(first_tm_node); +} + +static int test_fanin_info(const char *node_name) +{ + tm_node_desc_t *node_desc; + odp_tm_node_t tm_node; + + node_desc = find_node_desc(0, node_name); + if (node_desc == NULL) { + LOG_ERR("node_name %s not found\n", node_name); + return -1; + } + + tm_node = node_desc->node; + if (tm_node == ODP_TM_INVALID) { + LOG_ERR("tm_node is ODP_TM_INVALID\n"); + return -1; + } + + return walk_tree_backwards(node_desc->node); +} + +void traffic_mngr_test_capabilities(void) +{ + CU_ASSERT(test_overall_capabilities() == 0); +} + +void traffic_mngr_test_tm_create(void) +{ + /* Create the first/primary TM system. */ + CU_ASSERT_FATAL(create_tm_system() == 0); + dump_tm_tree(0); +} + +void traffic_mngr_test_shaper(void) +{ + CU_ASSERT(test_shaper_bw("bw1", "node_1_1_1", 0, 1 * MBPS) == 0); + CU_ASSERT(test_shaper_bw("bw4", "node_1_1_1", 1, 4 * MBPS) == 0); + CU_ASSERT(test_shaper_bw("bw10", "node_1_1_1", 2, 10 * MBPS) == 0); + CU_ASSERT(test_shaper_bw("bw40", "node_1_1_1", 3, 40 * MBPS) == 0); + CU_ASSERT(test_shaper_bw("bw100", "node_1_1_2", 0, 100 * MBPS) == 0); +} + +void traffic_mngr_test_scheduler(void) +{ + CU_ASSERT(test_sched_queue_priority("que_prio", "node_1_1_3", 10) == 0); + return; + + /* The following tests are not quite ready for production use. */ + CU_ASSERT(test_sched_node_priority("node_prio", "node_1_3", 4) == 0); + + CU_ASSERT(test_sched_wfq("sched_rr", "shaper_rr", "node_1_3", + ODP_TM_FRAME_BASED_WEIGHTS, + EQUAL_WEIGHTS) == 0); + CU_ASSERT(test_sched_wfq("sched_wrr", "shaper_wrr", "node_1_3", + ODP_TM_FRAME_BASED_WEIGHTS, + INCREASING_WEIGHTS) == 0); + CU_ASSERT(test_sched_wfq("sched_wfq", "shaper_wfq", "node_1_3", + ODP_TM_BYTE_BASED_WEIGHTS, + INCREASING_WEIGHTS) == 0); +} + +void traffic_mngr_test_thresholds(void) +{ + CU_ASSERT(test_threshold("thresh_A", "shaper_A", "node_1_2_1", 0, + 16, 0) == 0); + CU_ASSERT(test_threshold("thresh_B", "shaper_B", "node_1_2_1", 1, + 0, 6400) == 0); +} + +void traffic_mngr_test_byte_wred(void) +{ + if (!tm_capabilities.tm_queue_wred_supported) { + LOG_DBG("\nwas not run because tm_capabilities indicates" + " no WRED support\n"); + return; + } + + CU_ASSERT(test_byte_wred("byte_wred_30G", "byte_bw_30G", + "byte_thresh_30G", "node_1_3_1", 1, + ODP_PACKET_GREEN, TM_PERCENT(30), true) == 0); + CU_ASSERT(test_byte_wred("byte_wred_50Y", "byte_bw_50Y", + "byte_thresh_50Y", "node_1_3_1", 2, + ODP_PACKET_YELLOW, TM_PERCENT(50), true) == 0); + CU_ASSERT(test_byte_wred("byte_wred_70R", "byte_bw_70R", + "byte_thresh_70R", "node_1_3_1", 3, + ODP_PACKET_RED, TM_PERCENT(70), true) == 0); + + CU_ASSERT(test_byte_wred("byte_wred_40G", "byte_bw_40G", + "byte_thresh_40G", "node_1_3_1", 1, + ODP_PACKET_GREEN, TM_PERCENT(30), false) == 0); +} + +void traffic_mngr_test_pkt_wred(void) +{ + int rc; + + if (!tm_capabilities.tm_queue_wred_supported) { + LOG_DBG("\ntest_pkt_wred was not run because tm_capabilities " + "indicates no WRED support\n"); + return; + } + + CU_ASSERT(test_pkt_wred("pkt_wred_40G", "pkt_bw_40G", + "pkt_thresh_40G", "node_1_3_2", 1, + ODP_PACKET_GREEN, TM_PERCENT(30), false) == 0); + + if (!tm_capabilities.tm_queue_dual_slope_supported) { + LOG_DBG("since tm_capabilities indicates no dual slope " + "WRED support these tests are skipped.\n"); + return; + } + + rc = test_pkt_wred("pkt_wred_30G", "pkt_bw_30G", + "pkt_thresh_30G", "node_1_3_2", 1, + ODP_PACKET_GREEN, TM_PERCENT(30), true); + CU_ASSERT(rc == 0); + + CU_ASSERT(test_pkt_wred("pkt_wred_50Y", "pkt_bw_50Y", + "pkt_thresh_50Y", "node_1_3_2", 2, + ODP_PACKET_YELLOW, TM_PERCENT(50), true) == 0); + CU_ASSERT(test_pkt_wred("pkt_wred_70R", "pkt_bw_70R", + "pkt_thresh_70R", "node_1_3_2", 3, + ODP_PACKET_RED, TM_PERCENT(70), true) == 0); +} + +void traffic_mngr_test_query(void) +{ + CU_ASSERT(test_query_functions("query_shaper", "node_1_3_3", 3, 10) + == 0); +} + +void traffic_mngr_test_marking(void) +{ + odp_packet_color_t color; + odp_bool_t test_ecn, test_drop_prec; + int rc; + + if (tm_capabilities.vlan_marking_supported) { + for (color = 0; color < ODP_NUM_PKT_COLORS; color++) { + rc = test_vlan_marking("node_1_3_1", color); + CU_ASSERT(rc == 0); + } + } else { + LOG_DBG("\ntest_vlan_marking was not run because " + "tm_capabilities indicates no vlan marking support\n"); + } + + if (tm_capabilities.ecn_marking_supported) { + test_ecn = true; + test_drop_prec = false; + + rc = ip_marking_tests("node_1_3_2", test_ecn, test_drop_prec); + CU_ASSERT(rc == 0); + } else { + LOG_DBG("\necn_marking tests were not run because " + "tm_capabilities indicates no ecn marking support\n"); + } + + if (tm_capabilities.drop_prec_marking_supported) { + test_ecn = false; + test_drop_prec = true; + + rc = ip_marking_tests("node_1_4_2", test_ecn, test_drop_prec); + CU_ASSERT(rc == 0); + } else { + LOG_DBG("\ndrop_prec marking tests were not run because " + "tm_capabilities indicates no drop precedence " + "marking support\n"); + } + + if (tm_capabilities.ecn_marking_supported && + tm_capabilities.drop_prec_marking_supported) { + test_ecn = true; + test_drop_prec = true; + + rc = ip_marking_tests("node_1_4_2", test_ecn, test_drop_prec); + CU_ASSERT(rc == 0); + } +} + +void traffic_mngr_test_fanin_info(void) +{ + CU_ASSERT(test_fanin_info("node_1") == 0); + CU_ASSERT(test_fanin_info("node_1_2") == 0); + CU_ASSERT(test_fanin_info("node_1_3_7") == 0); +} + +void traffic_mngr_test_destroy(void) +{ + CU_ASSERT(destroy_tm_systems() == 0); +} + +odp_testinfo_t traffic_mngr_suite[] = { + ODP_TEST_INFO(traffic_mngr_test_capabilities), + ODP_TEST_INFO(traffic_mngr_test_tm_create), + ODP_TEST_INFO(traffic_mngr_test_shaper_profile), + ODP_TEST_INFO(traffic_mngr_test_sched_profile), + ODP_TEST_INFO(traffic_mngr_test_threshold_profile), + ODP_TEST_INFO(traffic_mngr_test_wred_profile), + ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_shaper, + traffic_mngr_check_shaper), + ODP_TEST_INFO_CONDITIONAL(traffic_mngr_test_scheduler, + traffic_mngr_check_scheduler), + ODP_TEST_INFO(traffic_mngr_test_thresholds), + ODP_TEST_INFO(traffic_mngr_test_byte_wred), + ODP_TEST_INFO(traffic_mngr_test_pkt_wred), + ODP_TEST_INFO(traffic_mngr_test_query), + ODP_TEST_INFO(traffic_mngr_test_marking), + ODP_TEST_INFO(traffic_mngr_test_fanin_info), + ODP_TEST_INFO(traffic_mngr_test_destroy), + ODP_TEST_INFO_NULL, +}; + +odp_suiteinfo_t traffic_mngr_suites[] = { + { "traffic_mngr tests", traffic_mngr_suite_init, + traffic_mngr_suite_term, traffic_mngr_suite }, + ODP_SUITE_INFO_NULL +}; + +int traffic_mngr_main(int argc, char *argv[]) +{ + /* parse common options: */ + if (odp_cunit_parse_options(argc, argv)) + return -1; + + int ret = odp_cunit_register(traffic_mngr_suites); + + if (ret == 0) + ret = odp_cunit_run(); + + return ret; +} diff --git a/test/common_plat/validation/api/traffic_mngr/traffic_mngr.h b/test/common_plat/validation/api/traffic_mngr/traffic_mngr.h new file mode 100644 index 000000000..af115fef7 --- /dev/null +++ b/test/common_plat/validation/api/traffic_mngr/traffic_mngr.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ODP_TEST_TRAFFIC_MNGR_H_ +#define _ODP_TEST_TRAFFIC_MNGR_H_ + +#include <odp_cunit_common.h> + +int traffic_mngr_check_shaper(void); +int traffic_mngr_check_scheduler(void); + +/* test functions: */ +void traffic_mngr_test_capabilities(void); +void traffic_mngr_test_tm_create(void); +void traffic_mngr_test_shaper_profile(void); +void traffic_mngr_test_sched_profile(void); +void traffic_mngr_test_threshold_profile(void); +void traffic_mngr_test_wred_profile(void); +void traffic_mngr_test_shaper(void); +void traffic_mngr_test_scheduler(void); +void traffic_mngr_test_thresholds(void); +void traffic_mngr_test_byte_wred(void); +void traffic_mngr_test_pkt_wred(void); +void traffic_mngr_test_query(void); +void traffic_mngr_test_marking(void); +void traffic_mngr_test_fanin_info(void); +void traffic_mngr_test_destroy(void); + +/* test arrays: */ +extern odp_testinfo_t traffic_mngr_suite[]; + +/* test suite init/term functions: */ +int traffic_mngr_suite_init(void); +int traffic_mngr_suite_term(void); + +/* test registry: */ +extern odp_suiteinfo_t traffic_mngr_suites[]; + +/* main test program: */ +int traffic_mngr_main(int argc, char *argv[]); + +#endif diff --git a/test/common_plat/validation/api/traffic_mngr/traffic_mngr_main.c b/test/common_plat/validation/api/traffic_mngr/traffic_mngr_main.c new file mode 100644 index 000000000..1fc1f78d7 --- /dev/null +++ b/test/common_plat/validation/api/traffic_mngr/traffic_mngr_main.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "traffic_mngr.h" + +int main(int argc, char *argv[]) +{ + return traffic_mngr_main(argc, argv); +} |