X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=app%2Ftest%2Ftest_thash.c;h=d8981fbb7ab0b5e8782def34da568a0789e6b282;hb=12b253ee876f3e687524a12fd670ffc65f0aed4d;hp=5a6912cda4dff8884dd6cd87f3ab26a066c8f014;hpb=0c9da7555da8c8373dfd69f798f832723ae6de71;p=dpdk.git diff --git a/app/test/test_thash.c b/app/test/test_thash.c index 5a6912cda4..d8981fbb7a 100644 --- a/app/test/test_thash.c +++ b/app/test/test_thash.c @@ -1,44 +1,19 @@ -/*- - * BSD LICENSE - * - * Copyright(c) 2015 Vladimir Medvedkin - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2015-2019 Vladimir Medvedkin */ #include #include #include +#include #include "test.h" #include +#define HASH_MSK(reta_sz) ((1 << reta_sz) - 1) +#define TUPLE_SZ (RTE_THASH_V4_L4_LEN * 4) + struct test_thash_v4 { uint32_t dst_ip; uint32_t src_ip; @@ -104,7 +79,7 @@ uint8_t default_rss_key[] = { }; static int -test_thash(void) +test_toeplitz_hash_calc(void) { uint32_t i, j; union rte_thash_tuple tuple; @@ -129,7 +104,7 @@ test_thash(void) RTE_THASH_V4_L4_LEN, default_rss_key); if ((rss_l3 != v4_tbl[i].hash_l3) || (rss_l3l4 != v4_tbl[i].hash_l3l4)) - return -1; + return -TEST_FAILED; /*Calculate hash with converted key*/ rss_l3 = rte_softrss_be((uint32_t *)&tuple, RTE_THASH_V4_L3_LEN, rss_key_be); @@ -137,7 +112,7 @@ test_thash(void) RTE_THASH_V4_L4_LEN, rss_key_be); if ((rss_l3 != v4_tbl[i].hash_l3) || (rss_l3l4 != v4_tbl[i].hash_l3l4)) - return -1; + return -TEST_FAILED; } for (i = 0; i < RTE_DIM(v6_tbl); i++) { /*Fill ipv6 hdr*/ @@ -156,7 +131,7 @@ test_thash(void) RTE_THASH_V6_L4_LEN, default_rss_key); if ((rss_l3 != v6_tbl[i].hash_l3) || (rss_l3l4 != v6_tbl[i].hash_l3l4)) - return -1; + return -TEST_FAILED; /*Calculate hash with converted key*/ rss_l3 = rte_softrss_be((uint32_t *)&tuple, RTE_THASH_V6_L3_LEN, rss_key_be); @@ -164,9 +139,462 @@ test_thash(void) RTE_THASH_V6_L4_LEN, rss_key_be); if ((rss_l3 != v6_tbl[i].hash_l3) || (rss_l3l4 != v6_tbl[i].hash_l3l4)) - return -1; + return -TEST_FAILED; + } + return TEST_SUCCESS; +} + +static int +test_create_invalid(void) +{ + struct rte_thash_ctx *ctx; + int key_len = 40; + int reta_sz = 7; + + ctx = rte_thash_init_ctx(NULL, key_len, reta_sz, NULL, 0); + RTE_TEST_ASSERT(ctx == NULL, + "Call succeeded with invalid parameters\n"); + + ctx = rte_thash_init_ctx("test", 0, reta_sz, NULL, 0); + RTE_TEST_ASSERT(ctx == NULL, + "Call succeeded with invalid parameters\n"); + + ctx = rte_thash_init_ctx(NULL, key_len, 1, NULL, 0); + RTE_TEST_ASSERT(ctx == NULL, + "Call succeeded with invalid parameters\n"); + + ctx = rte_thash_init_ctx(NULL, key_len, 17, NULL, 0); + RTE_TEST_ASSERT(ctx == NULL, + "Call succeeded with invalid parameters\n"); + + return TEST_SUCCESS; +} + +static int +test_multiple_create(void) +{ + struct rte_thash_ctx *ctx; + int key_len = 40; + int reta_sz = 7; + int i; + + for (i = 0; i < 100; i++) { + ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0); + RTE_TEST_ASSERT(ctx != NULL, "Can not create CTX\n"); + + rte_thash_free_ctx(ctx); + } + + return TEST_SUCCESS; +} + +static int +test_free_null(void) +{ + struct rte_thash_ctx *ctx; + + ctx = rte_thash_init_ctx("test", 40, 7, NULL, 0); + RTE_TEST_ASSERT(ctx != NULL, "Can not create CTX\n"); + + rte_thash_free_ctx(ctx); + rte_thash_free_ctx(NULL); + + return TEST_SUCCESS; +} + +static int +test_add_invalid_helper(void) +{ + struct rte_thash_ctx *ctx; + const int key_len = 40; + int reta_sz = 7; + int ret; + + ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0); + RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n"); + + ret = rte_thash_add_helper(NULL, "test", reta_sz, 0); + RTE_TEST_ASSERT(ret == -EINVAL, + "Call succeeded with invalid parameters\n"); + + ret = rte_thash_add_helper(ctx, NULL, reta_sz, 0); + RTE_TEST_ASSERT(ret == -EINVAL, + "Call succeeded with invalid parameters\n"); + + ret = rte_thash_add_helper(ctx, "test", reta_sz - 1, 0); + RTE_TEST_ASSERT(ret == -EINVAL, + "Call succeeded with invalid parameters\n"); + + ret = rte_thash_add_helper(ctx, "test", reta_sz, key_len * 8); + RTE_TEST_ASSERT(ret == -EINVAL, + "Call succeeded with invalid parameters\n"); + + ret = rte_thash_add_helper(ctx, "first_range", reta_sz, 0); + RTE_TEST_ASSERT(ret == 0, "Can not create helper\n"); + + ret = rte_thash_add_helper(ctx, "first_range", reta_sz, 0); + RTE_TEST_ASSERT(ret == -EEXIST, + "Call succeeded with duplicated name\n"); + + /* + * Create second helper with offset 3 * reta_sz. + * Note first_range helper created range in key: + * [0, 32 + length{= reta_sz} - 1), i.e [0, 37). + * second range is [44, 81) + */ + ret = rte_thash_add_helper(ctx, "second_range", reta_sz, + 32 + 2 * reta_sz); + RTE_TEST_ASSERT(ret == 0, "Can not create helper\n"); + + /* + * Try to create overlapping with first_ and second_ ranges, + * i.e. [6, 49) + */ + ret = rte_thash_add_helper(ctx, "third_range", 2 * reta_sz, reta_sz); + RTE_TEST_ASSERT(ret == -EEXIST, + "Call succeeded with overlapping ranges\n"); + + rte_thash_free_ctx(ctx); + + return TEST_SUCCESS; +} + +static int +test_find_existing(void) +{ + struct rte_thash_ctx *ctx, *ret_ctx; + + ctx = rte_thash_init_ctx("test", 40, 7, NULL, 0); + RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n"); + + ret_ctx = rte_thash_find_existing("test"); + RTE_TEST_ASSERT(ret_ctx != NULL, "can not find existing ctx\n"); + + rte_thash_free_ctx(ctx); + + return TEST_SUCCESS; +} + +static int +test_get_helper(void) +{ + struct rte_thash_ctx *ctx; + struct rte_thash_subtuple_helper *h; + int ret; + + ctx = rte_thash_init_ctx("test", 40, 7, NULL, 0); + RTE_TEST_ASSERT(ctx != NULL, "Can not create thash ctx\n"); + + h = rte_thash_get_helper(NULL, "first_range"); + RTE_TEST_ASSERT(h == NULL, "Call succeeded with invalid parameters\n"); + + h = rte_thash_get_helper(ctx, NULL); + RTE_TEST_ASSERT(h == NULL, "Call succeeded with invalid parameters\n"); + + ret = rte_thash_add_helper(ctx, "first_range", 8, 0); + RTE_TEST_ASSERT(ret == 0, "Can not create helper\n"); + + h = rte_thash_get_helper(ctx, "first_range"); + RTE_TEST_ASSERT(h != NULL, "Can not find helper\n"); + + rte_thash_free_ctx(ctx); + + return TEST_SUCCESS; +} + +static int +test_period_overflow(void) +{ + struct rte_thash_ctx *ctx; + int reta_sz = 7; /* reflects polynomial degree */ + int ret; + + /* first create without RTE_THASH_IGNORE_PERIOD_OVERFLOW flag */ + ctx = rte_thash_init_ctx("test", 40, reta_sz, NULL, 0); + RTE_TEST_ASSERT(ctx != NULL, "Can not create thash ctx\n"); + + /* requested range > (2^reta_sz) - 1 */ + ret = rte_thash_add_helper(ctx, "test", (1 << reta_sz), 0); + RTE_TEST_ASSERT(ret == -ENOSPC, + "Call succeeded with invalid parameters\n"); + + /* requested range == len + 32 - 1, smaller than (2^reta_sz) - 1 */ + ret = rte_thash_add_helper(ctx, "test", (1 << reta_sz) - 32, 0); + RTE_TEST_ASSERT(ret == 0, "Can not create helper\n"); + + rte_thash_free_ctx(ctx); + + /* create with RTE_THASH_IGNORE_PERIOD_OVERFLOW flag */ + ctx = rte_thash_init_ctx("test", 40, reta_sz, NULL, + RTE_THASH_IGNORE_PERIOD_OVERFLOW); + RTE_TEST_ASSERT(ctx != NULL, "Can not create thash ctx\n"); + + /* requested range > (2^reta_sz - 1) */ + ret = rte_thash_add_helper(ctx, "test", (1 << reta_sz) + 10, 0); + RTE_TEST_ASSERT(ret == 0, "Can not create helper\n"); + + rte_thash_free_ctx(ctx); + + return TEST_SUCCESS; +} + +static int +test_predictable_rss_min_seq(void) +{ + struct rte_thash_ctx *ctx; + struct rte_thash_subtuple_helper *h; + const int key_len = 40; + int reta_sz = 6; + uint8_t initial_key[key_len]; + const uint8_t *new_key; + int ret; + union rte_thash_tuple tuple; + uint32_t orig_hash, adj_hash, adj; + unsigned int desired_value = 27 & HASH_MSK(reta_sz); + uint16_t port_value = 22; + + memset(initial_key, 0, key_len); + + ctx = rte_thash_init_ctx("test", key_len, reta_sz, initial_key, + RTE_THASH_MINIMAL_SEQ); + RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n"); + + ret = rte_thash_add_helper(ctx, "snat", sizeof(uint16_t) * 8, + offsetof(union rte_thash_tuple, v4.sport) * 8); + RTE_TEST_ASSERT(ret == 0, "can not add helper, ret %d\n", ret); + + h = rte_thash_get_helper(ctx, "snat"); + RTE_TEST_ASSERT(h != NULL, "can not find helper\n"); + + new_key = rte_thash_get_key(ctx); + tuple.v4.src_addr = RTE_IPV4(0, 0, 0, 0); + tuple.v4.dst_addr = RTE_IPV4(0, 0, 0, 0); + tuple.v4.sport = 0; + tuple.v4.sport = rte_cpu_to_be_16(port_value); + tuple.v4.dport = 0; + tuple.v4.sctp_tag = rte_be_to_cpu_32(tuple.v4.sctp_tag); + + orig_hash = rte_softrss((uint32_t *)&tuple, + RTE_THASH_V4_L4_LEN, new_key); + adj = rte_thash_get_complement(h, orig_hash, desired_value); + + tuple.v4.sctp_tag = rte_cpu_to_be_32(tuple.v4.sctp_tag); + tuple.v4.sport ^= rte_cpu_to_be_16(adj); + tuple.v4.sctp_tag = rte_be_to_cpu_32(tuple.v4.sctp_tag); + + adj_hash = rte_softrss((uint32_t *)&tuple, + RTE_THASH_V4_L4_LEN, new_key); + RTE_TEST_ASSERT((adj_hash & HASH_MSK(reta_sz)) == + desired_value, "bad desired value\n"); + + rte_thash_free_ctx(ctx); + + return TEST_SUCCESS; +} + +/* + * This test creates 7 subranges in the following order: + * range_one = [56, 95), len = 8, offset = 56 + * range_two = [64, 103), len = 8, offset = 64 + * range_three = [120, 159), len = 8, offset = 120 + * range_four = [48, 87), len = 8, offset = 48 + * range_five = [57, 95), len = 7, offset = 57 + * range_six = [40, 111), len = 40, offset = 40 + * range_seven = [0, 39), len = 8, offset = 0 + */ +struct range { + const char *name; + int len; + int offset; + int byte_idx; +}; + +struct range rng_arr[] = { + {"one", 8, 56, 7}, + {"two", 8, 64, 8}, + {"three", 8, 120, 15}, + {"four", 8, 48, 6}, + {"six", 40, 40, 9}, + {"five", 7, 57, 7}, + {"seven", 8, 0, 0} +}; + +static int +test_predictable_rss_multirange(void) +{ + struct rte_thash_ctx *ctx; + struct rte_thash_subtuple_helper *h[RTE_DIM(rng_arr)]; + const uint8_t *new_key; + const int key_len = 40; + int reta_sz = 7; + unsigned int i, j, k; + int ret; + uint32_t desired_value = rte_rand() & HASH_MSK(reta_sz); + uint8_t tuples[RTE_DIM(rng_arr)][16] = { {0} }; + uint32_t *ptr; + uint32_t hashes[RTE_DIM(rng_arr)]; + uint32_t adj_hashes[RTE_DIM(rng_arr)]; + uint32_t adj; + + ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0); + RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n"); + + for (i = 0; i < RTE_DIM(rng_arr); i++) { + ret = rte_thash_add_helper(ctx, rng_arr[i].name, + rng_arr[i].len, rng_arr[i].offset); + RTE_TEST_ASSERT(ret == 0, "can not add helper\n"); + + h[i] = rte_thash_get_helper(ctx, rng_arr[i].name); + RTE_TEST_ASSERT(h[i] != NULL, "can not find helper\n"); + } + new_key = rte_thash_get_key(ctx); + + /* + * calculate hashes, complements, then adjust keys with + * complements and recalsulate hashes + */ + for (i = 0; i < RTE_DIM(rng_arr); i++) { + for (k = 0; k < 100; k++) { + /* init with random keys */ + ptr = (uint32_t *)&tuples[i][0]; + for (j = 0; j < 4; j++) + ptr[j] = rte_rand(); + /* convert keys from BE to CPU byte order */ + for (j = 0; j < 4; j++) + ptr[j] = rte_be_to_cpu_32(ptr[j]); + + hashes[i] = rte_softrss(ptr, 4, new_key); + adj = rte_thash_get_complement(h[i], hashes[i], + desired_value); + /* convert back to BE to adjust the value */ + for (j = 0; j < 4; j++) + ptr[j] = rte_cpu_to_be_32(ptr[j]); + + tuples[i][rng_arr[i].byte_idx] ^= adj; + + for (j = 0; j < 4; j++) + ptr[j] = rte_be_to_cpu_32(ptr[j]); + + adj_hashes[i] = rte_softrss(ptr, 4, new_key); + RTE_TEST_ASSERT((adj_hashes[i] & HASH_MSK(reta_sz)) == + desired_value, + "bad desired value for %d tuple\n", i); + } } - return 0; + + rte_thash_free_ctx(ctx); + + return TEST_SUCCESS; +} + +static int +cmp_tuple_eq(void *userdata, uint8_t *tuple) +{ + return memcmp(userdata, tuple, TUPLE_SZ); +} + +static int +test_adjust_tuple(void) +{ + struct rte_thash_ctx *ctx; + struct rte_thash_subtuple_helper *h; + const int key_len = 40; + const uint8_t *new_key; + uint8_t tuple[TUPLE_SZ]; + uint32_t tmp_tuple[TUPLE_SZ / sizeof(uint32_t)]; + uint32_t tuple_copy[TUPLE_SZ / sizeof(uint32_t)]; + uint32_t hash; + int reta_sz = CHAR_BIT; + int ret; + unsigned int i, desired_value = rte_rand() & HASH_MSK(reta_sz); + + memset(tuple, 0xab, TUPLE_SZ); + + ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0); + RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n"); + + /* + * set offset to be in the middle of a byte + * set size of the subtuple to be 2 * rets_sz + * to have the room for random bits + */ + ret = rte_thash_add_helper(ctx, "test", reta_sz * 2, + (5 * CHAR_BIT) + 4); + RTE_TEST_ASSERT(ret == 0, "can not add helper, ret %d\n", ret); + + new_key = rte_thash_get_key(ctx); + + h = rte_thash_get_helper(ctx, "test"); + RTE_TEST_ASSERT(h != NULL, "can not find helper\n"); + + ret = rte_thash_adjust_tuple(ctx, h, tuple, TUPLE_SZ, desired_value, + 1, NULL, NULL); + RTE_TEST_ASSERT(ret == 0, "can not adjust tuple, ret %d\n", ret); + + for (i = 0; i < (TUPLE_SZ / 4); i++) + tmp_tuple[i] = + rte_be_to_cpu_32(*(uint32_t *)&tuple[i * 4]); + + hash = rte_softrss(tmp_tuple, TUPLE_SZ / 4, new_key); + RTE_TEST_ASSERT((hash & HASH_MSK(reta_sz)) == + desired_value, "bad desired value\n"); + + + /* Pass previously calculated tuple to callback function */ + memcpy(tuple_copy, tuple, TUPLE_SZ); + + memset(tuple, 0xab, TUPLE_SZ); + ret = rte_thash_adjust_tuple(ctx, h, tuple, TUPLE_SZ, desired_value, + 1, cmp_tuple_eq, tuple_copy); + RTE_TEST_ASSERT(ret == -EEXIST, + "adjust tuple didn't indicate collision\n"); + + /* + * Make the function to generate random bits into subtuple + * after first adjustment attempt. + */ + memset(tuple, 0xab, TUPLE_SZ); + ret = rte_thash_adjust_tuple(ctx, h, tuple, TUPLE_SZ, desired_value, + 2, cmp_tuple_eq, tuple_copy); + RTE_TEST_ASSERT(ret == 0, "can not adjust tuple, ret %d\n", ret); + + for (i = 0; i < (TUPLE_SZ / 4); i++) + tmp_tuple[i] = + rte_be_to_cpu_32(*(uint32_t *)&tuple[i * 4]); + + hash = rte_softrss(tmp_tuple, TUPLE_SZ / 4, new_key); + RTE_TEST_ASSERT((hash & HASH_MSK(reta_sz)) == + desired_value, "bad desired value\n"); + + rte_thash_free_ctx(ctx); + + return TEST_SUCCESS; +} + +static struct unit_test_suite thash_tests = { + .suite_name = "thash autotest", + .setup = NULL, + .teardown = NULL, + .unit_test_cases = { + TEST_CASE(test_toeplitz_hash_calc), + TEST_CASE(test_create_invalid), + TEST_CASE(test_multiple_create), + TEST_CASE(test_free_null), + TEST_CASE(test_add_invalid_helper), + TEST_CASE(test_find_existing), + TEST_CASE(test_get_helper), + TEST_CASE(test_period_overflow), + TEST_CASE(test_predictable_rss_min_seq), + TEST_CASE(test_predictable_rss_multirange), + TEST_CASE(test_adjust_tuple), + TEST_CASES_END() + } +}; + +static int +test_thash(void) +{ + return unit_test_suite_runner(&thash_tests); } REGISTER_TEST_COMMAND(thash_autotest, test_thash);