From patchwork Tue Apr 13 13:19:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Medvedkin X-Patchwork-Id: 91248 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 351EBA0524; Tue, 13 Apr 2021 15:20:01 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id D697B160F80; Tue, 13 Apr 2021 15:19:47 +0200 (CEST) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by mails.dpdk.org (Postfix) with ESMTP id A2940160F72 for ; Tue, 13 Apr 2021 15:19:44 +0200 (CEST) IronPort-SDR: dYiAkN3Ga4j3ePE89LnNO87rf9ZZIpTi2y8Xt/WRNEnY2+dZ46g1ezVShr4o2grZ3pOvmVX50d AUypHnVu324g== X-IronPort-AV: E=McAfee;i="6200,9189,9952"; a="194522556" X-IronPort-AV: E=Sophos;i="5.82,219,1613462400"; d="scan'208";a="194522556" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Apr 2021 06:19:44 -0700 IronPort-SDR: 6WDKKJQS7EuCPieRWmi2Qu+nHHTrxGf0IBP7MtinYVI3wVUuxlr1oSq1EBufqkSKD5I/0Mb+jj aQDnQB6Qcc5A== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.82,219,1613462400"; d="scan'208";a="381966256" Received: from silpixa00400072.ir.intel.com ([10.237.222.213]) by orsmga003.jf.intel.com with ESMTP; 13 Apr 2021 06:19:42 -0700 From: Vladimir Medvedkin To: dev@dpdk.org Cc: konstantin.ananyev@intel.com, andrey.chilikin@intel.com, ray.kinsella@intel.com, yipeng1.wang@intel.com, sameh.gobriel@intel.com, bruce.richardson@intel.com Date: Tue, 13 Apr 2021 14:19:33 +0100 Message-Id: <1618319973-391016-4-git-send-email-vladimir.medvedkin@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1618319973-391016-1-git-send-email-vladimir.medvedkin@intel.com> References: <1618319973-391016-1-git-send-email-vladimir.medvedkin@intel.com> In-Reply-To: <1618168266-338017-1-git-send-email-vladimir.medvedkin@intel.com> References: <1618168266-338017-1-git-send-email-vladimir.medvedkin@intel.com> Subject: [dpdk-dev] [PATCH v4 3/3] test/hash: add additional thash tests X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" This patch adds tests for predictable RSS feature. Signed-off-by: Vladimir Medvedkin Acked-by: Yipeng Wang --- app/test/test_thash.c | 469 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 463 insertions(+), 6 deletions(-) diff --git a/app/test/test_thash.c b/app/test/test_thash.c index a6aadd1..d8981fb 100644 --- a/app/test/test_thash.c +++ b/app/test/test_thash.c @@ -5,11 +5,15 @@ #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; @@ -75,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; @@ -100,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); @@ -108,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*/ @@ -127,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); @@ -135,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 0; + 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); + } + } + + 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);