From patchwork Thu Oct 11 04:59:32 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Honnappa Nagarahalli X-Patchwork-Id: 148604 Delivered-To: patch@linaro.org Received: by 2002:a2e:8595:0:0:0:0:0 with SMTP id b21-v6csp1682929lji; Wed, 10 Oct 2018 22:00:47 -0700 (PDT) X-Google-Smtp-Source: ACcGV62PbogJ5oSQgm3j1NQSC+jFgwYcgkEtujPJ7bXdWue5JPLjWSCsWTjRIf+D5/VW/bOo4baR X-Received: by 2002:a50:af61:: with SMTP id g88-v6mr527260edd.220.1539234047473; Wed, 10 Oct 2018 22:00:47 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1539234047; cv=none; d=google.com; s=arc-20160816; b=tKtqp8J/sUV0oiBD7QdfE7KljhA1CvceywBZbshFpeNur04RaThU6ZVIJvoZCzjzz8 7COoZL3M5FQFxq+qQHqROUv7jmSY+BQXeXKtx0ycl/s6y/i9Js/pOFIZOKv96ozq1Wte Y8qa/QwpXzphpS7+Yo3o0iCnRAy6IzXBJMDllzeLYsOEgvn8a19QrKhKk7LfMBAjL/Wq TfSOq0pFBWmESCFNkzQ2X6VUqFY1qwhgvHTx49H8BjI5ysL5Cn8o0o6NheBbsl2flP4E Jza234lIudsuOZQYOJ/G3iHI6+1hHCvnV90IGs7JFtS6qdSG0X6TwHijihMTYm9EwI2d FFpQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:subject:references:in-reply-to :message-id:date:cc:to:from; bh=4hOVkDFPFPUDmMDmraVWYQGwSduMFa8g0OjsbzxjyAc=; b=Lp/3abYmGRaNYQhxVtjc6ciE+Ia0BxlcFjy+04HSsXtej37oQIr3CsprHdOOYwQRA6 m9a8almZFoGnX0YCeVxhmPHoSoRWnNnxYdXgiKuatxbdwe7tHFRu3hGZrEOIrxPxiIVu pqcJgYCiBBfOql9+RgEQ8txN/Cjo729d887eZqSuzkkXDLQhhyuwafcNy9QE7dXg+cD6 Lby0ulhN5TcOjj66n45LbHkbIjXMQxagQtLcyCe/WyEEoGUozF3g5iRwAkE0exvIRNps djuM/CNVpQQ+RGUgPXYhb7SKuMQYsHvrj+YFgKlQxd1/Ire25o3a1EEQ5ogj4ap0EsOg RY/w== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of dev-bounces@dpdk.org designates 92.243.14.124 as permitted sender) smtp.mailfrom=dev-bounces@dpdk.org Return-Path: Received: from dpdk.org (dpdk.org. [92.243.14.124]) by mx.google.com with ESMTP id f11-v6si2369769edn.275.2018.10.10.22.00.47; Wed, 10 Oct 2018 22:00:47 -0700 (PDT) Received-SPF: pass (google.com: domain of dev-bounces@dpdk.org designates 92.243.14.124 as permitted sender) client-ip=92.243.14.124; Authentication-Results: mx.google.com; spf=pass (google.com: domain of dev-bounces@dpdk.org designates 92.243.14.124 as permitted sender) smtp.mailfrom=dev-bounces@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 477EE1B451; Thu, 11 Oct 2018 07:00:01 +0200 (CEST) Received: from foss.arm.com (usa-sjc-mx-foss1.foss.arm.com [217.140.101.70]) by dpdk.org (Postfix) with ESMTP id 214461B42B for ; Thu, 11 Oct 2018 06:59:50 +0200 (CEST) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 4755F1596; Wed, 10 Oct 2018 21:59:50 -0700 (PDT) Received: from 2p2660v4-1.austin.arm.com (2p2660v4-1.austin.arm.com [10.118.12.190]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C45143F5B3; Wed, 10 Oct 2018 21:59:49 -0700 (PDT) From: Honnappa Nagarahalli To: bruce.richardson@intel.com, pablo.de.lara.guarch@intel.com Cc: dev@dpdk.org, yipeng1.wang@intel.com, honnappa.nagarahalli@arm.com, Dharmik.Thakkar@arm.com, nd@arm.com, Dharmik Thakkar Date: Wed, 10 Oct 2018 23:59:32 -0500 Message-Id: <1539233972-49860-8-git-send-email-honnappa.nagarahalli@arm.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1539233972-49860-1-git-send-email-honnappa.nagarahalli@arm.com> References: <1539233972-49860-1-git-send-email-honnappa.nagarahalli@arm.com> Subject: [dpdk-dev] [PATCH v2 7/7] test/hash: read-write lock-free concurrency test X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 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" From: Dharmik Thakkar Unit tests to check for hash lookup perf with lock-free enabled and with lock-free disabled. Unit tests performed with readers running in parallel with writers. Tests include: - hash lookup on existing keys with: - hash add causing NO key-shifts of existing keys in the table - hash lookup on existing keys likely to be on shift-path with: - hash add causing key-shifts of existing keys in the table - hash lookup on existing keys NOT likely to be on shift-path with: - hash add causing key-shifts of existing keys in the table - hash lookup on non-existing keys with: - hash add causing NO key-shifts of existing keys in the table - hash add causing key-shifts of existing keys in the table - hash lookup on keys likely to be on shift-path with: - multiple writers causing key-shifts of existing keys in the table Signed-off-by: Dharmik Thakkar Reviewed-by: Honnappa Nagarahalli Reviewed-by: Gavin Hu --- test/test/Makefile | 1 + test/test/meson.build | 1 + test/test/test_hash_readwrite_lf.c | 1084 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1086 insertions(+) create mode 100644 test/test/test_hash_readwrite_lf.c -- 2.7.4 diff --git a/test/test/Makefile b/test/test/Makefile index e6967ba..068ed72 100644 --- a/test/test/Makefile +++ b/test/test/Makefile @@ -115,6 +115,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_functions.c SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_scaling.c SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_multiwriter.c SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite.c +SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_readwrite_lf.c SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm_perf.c diff --git a/test/test/meson.build b/test/test/meson.build index b1dd6ec..366d9a7 100644 --- a/test/test/meson.build +++ b/test/test/meson.build @@ -41,6 +41,7 @@ test_sources = files('commands.c', 'test_hash_functions.c', 'test_hash_multiwriter.c', 'test_hash_perf.c', + 'test_hash_readwrite_lf.c', 'test_hash_scaling.c', 'test_interrupts.c', 'test_kni.c', diff --git a/test/test/test_hash_readwrite_lf.c b/test/test/test_hash_readwrite_lf.c new file mode 100644 index 0000000..841e989 --- /dev/null +++ b/test/test/test_hash_readwrite_lf.c @@ -0,0 +1,1084 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Arm Limited + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" + +#ifndef RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF +#define RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF 0 +#endif + +#define RUN_WITH_HTM_DISABLED 0 + +#if (RUN_WITH_HTM_DISABLED) + +#define TOTAL_ENTRY (5*1024) +#define TOTAL_INSERT (5*1024) + +#else + +#define TOTAL_ENTRY (16*1024*1024) +#define TOTAL_INSERT (16*1024*1024) + +#endif + +#define READ_FAIL 0 +#define READ_PASS_NO_KEY_SHIFTS 1 +#define READ_PASS_SHIFT_PATH 2 +#define READ_PASS_NON_SHIFT_PATH 3 + +#define NUM_TEST 3 +unsigned int rwc_core_cnt[NUM_TEST] = {1, 2, 4}; + +struct rwc_perf { + uint32_t w_no_ks_r_pass[NUM_TEST]; + uint32_t w_no_ks_r_fail[NUM_TEST]; + uint32_t w_ks_r_pass_nsp[NUM_TEST]; + uint32_t w_ks_r_pass_sp[NUM_TEST]; + uint32_t w_ks_r_fail[NUM_TEST]; + uint32_t multi_rw[NUM_TEST - 1][NUM_TEST]; +}; + +static struct rwc_perf rwc_lf_results, rwc_non_lf_results; + +struct { + uint32_t *keys; + uint32_t *keys_no_ks; + uint32_t *keys_ks; + uint32_t *keys_absent; + uint32_t *keys_shift_path; + uint32_t *keys_non_shift_path; + uint32_t count_keys_no_ks; + uint32_t count_keys_ks; + uint32_t count_keys_absent; + uint32_t count_keys_shift_path; + uint32_t count_keys_non_shift_path; + uint32_t single_insert; + struct rte_hash *h; +} tbl_rwc_test_param; + +static rte_atomic64_t gread_cycles; +static rte_atomic64_t greads; + +static volatile uint8_t writer_done; +static volatile uint8_t multi_writer_done[4]; +uint8_t num_test; +uint8_t htm; + +uint16_t enabled_core_ids[RTE_MAX_LCORE]; + +uint8_t *scanned_bkts; + +static inline int +get_enabled_cores_list(void) +{ + uint32_t i = 0; + uint16_t core_id; + uint32_t max_cores = rte_lcore_count(); + for (core_id = 0; core_id < RTE_MAX_LCORE && i < max_cores; core_id++) { + if (rte_lcore_is_enabled(core_id)) { + enabled_core_ids[i] = core_id; + i++; + } + } + + if (i != rte_lcore_count()) { + printf("Number of enabled cores in list is different from " + "number given by rte_lcore_count()\n"); + return -1; + } + return 0; +} + +static inline int +check_bucket(uint32_t bkt_idx, uint32_t key) +{ + uint32_t iter; + uint32_t prev_iter; + uint32_t diff; + uint32_t count = 0; + const void *next_key; + void *next_data; + + /* Temporary bucket to hold the keys */ + uint32_t keys_in_bkt[8]; + + iter = bkt_idx * 8; + prev_iter = iter; + while (rte_hash_iterate(tbl_rwc_test_param.h, + &next_key, &next_data, &iter) >= 0) { + + /* Check for duplicate entries */ + if (*(const uint32_t *)next_key == key) + return 1; + + /* Identify if there is any free entry in the bucket */ + diff = iter - prev_iter; + if (diff > 1) + break; + + prev_iter = iter; + keys_in_bkt[count] = *(const uint32_t *)next_key; + count++; + + /* All entries in the bucket are occupied */ + if (count == 8) { + + /* + * Check if bucket was not scanned before, to avoid + * duplicate keys. + */ + if (scanned_bkts[bkt_idx] == 0) { + /* + * Since this bucket (pointed to by bkt_idx) is + * full, it is likely that key(s) in this + * bucket will be on the shift path, when + * collision occurs. Thus, add it to + * keys_shift_path. + */ + memcpy(tbl_rwc_test_param.keys_shift_path + + tbl_rwc_test_param.count_keys_shift_path + , keys_in_bkt, 32); + tbl_rwc_test_param.count_keys_shift_path += 8; + scanned_bkts[bkt_idx] = 1; + } + return -1; + } + } + return 0; +} + +static int +generate_keys(void) +{ + uint32_t *keys = NULL; + uint32_t *keys_no_ks = NULL; + uint32_t *keys_ks = NULL; + uint32_t *keys_absent = NULL; + uint32_t *keys_non_shift_path = NULL; + uint32_t *found = NULL; + uint32_t count_keys_no_ks = 0; + uint32_t count_keys_ks = 0; + uint32_t i; + + /* + * keys will consist of a) keys whose addition to the hash table + * will result in shifting of the existing keys to their alternate + * locations b) keys whose addition to the hash table will not result + * in shifting of the existing keys. + */ + keys = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0); + if (keys == NULL) { + printf("RTE_MALLOC failed\n"); + goto err; + } + + /* + * keys_no_ks (no key-shifts): Subset of 'keys' - consists of keys that + * will NOT result in shifting of the existing keys to their alternate + * locations. Roughly around 900K keys. + */ + keys_no_ks = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0); + if (keys_no_ks == NULL) { + printf("RTE_MALLOC failed\n"); + goto err; + } + + /* + * keys_ks (key-shifts): Subset of 'keys' - consists of keys that will + * result in shifting of the existing keys to their alternate locations. + * Roughly around 146K keys. There might be repeating keys. More code is + * required to filter out these keys which will complicate the test case + */ + keys_ks = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0); + if (keys_ks == NULL) { + printf("RTE_MALLOC failed\n"); + goto err; + } + + /* Used to identify keys not inserted in the hash table */ + found = rte_zmalloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0); + if (found == NULL) { + printf("RTE_MALLOC failed\n"); + goto err; + } + + /* + * This consist of keys not inserted to the hash table. + * Used to test perf of lookup on keys that do not exist in the table. + */ + keys_absent = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, 0); + if (keys_absent == NULL) { + printf("RTE_MALLOC failed\n"); + goto err; + } + + /* + * This consist of keys which are likely to be on the shift + * path (i.e. being moved to alternate location), when collision occurs + * on addition of a key to an already full primary bucket. + * Used to test perf of lookup on keys that are on the shift path. + */ + tbl_rwc_test_param.keys_shift_path = rte_malloc(NULL, sizeof(uint32_t) * + TOTAL_INSERT, 0); + if (tbl_rwc_test_param.keys_shift_path == NULL) { + printf("RTE_MALLOC failed\n"); + goto err; + } + + /* + * This consist of keys which are never on the shift + * path (i.e. being moved to alternate location), when collision occurs + * on addition of a key to an already full primary bucket. + * Used to test perf of lookup on keys that are not on the shift path. + */ + keys_non_shift_path = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_INSERT, + 0); + if (keys_non_shift_path == NULL) { + printf("RTE_MALLOC failed\n"); + goto err; + } + + /* + * Used to mark bkts in which at least one key was shifted to its + * alternate location + */ + scanned_bkts = rte_malloc(NULL, sizeof(uint8_t) * TOTAL_INSERT / 8, 0); + if (scanned_bkts == NULL) { + printf("RTE_MALLOC failed\n"); + goto err; + } + + tbl_rwc_test_param.keys = keys; + tbl_rwc_test_param.keys_no_ks = keys_no_ks; + tbl_rwc_test_param.keys_ks = keys_ks; + tbl_rwc_test_param.keys_absent = keys_absent; + tbl_rwc_test_param.keys_non_shift_path = keys_non_shift_path; + + hash_sig_t sig; + uint32_t prim_bucket_idx; + int ret; + uint32_t num_buckets; + uint32_t bucket_bitmask; + num_buckets = TOTAL_ENTRY/8; + bucket_bitmask = num_buckets - 1; + + /* Generate keys by adding previous two keys, neglect overflow */ + keys[0] = 0; + keys[1] = 1; + for (i = 2; i < TOTAL_INSERT; i++) + keys[i] = keys[i-1] + keys[i-2]; + + /* Segregate keys into keys_no_ks and keys_ks */ + for (i = 0; i < TOTAL_INSERT; i++) { + /* Check if primary bucket has space.*/ + sig = rte_hash_hash(tbl_rwc_test_param.h, + tbl_rwc_test_param.keys+i); + prim_bucket_idx = sig & bucket_bitmask; + ret = check_bucket(prim_bucket_idx, keys[i]); + if (ret < 0) { + /* + * Primary bucket is full, this key will result in + * shifting of the keys to their alternate locations. + */ + keys_ks[count_keys_ks] = keys[i]; + count_keys_ks++; + } else if (ret == 0) { + /* + * Primary bucket has space, this key will not result in + * shifting of the keys. Hence, add key to the table. + */ + ret = rte_hash_add_key_data(tbl_rwc_test_param.h, + keys+i, + (void *)((uintptr_t)i)); + if (ret < 0) { + printf("writer failed %"PRIu32"\n", i); + break; + } + keys_no_ks[count_keys_no_ks] = keys[i]; + count_keys_no_ks++; + } + } + + for (i = 0; i < count_keys_no_ks; i++) { + /* Identify keys in keys_no_ks with value less than 1M */ + if (keys_no_ks[i] < TOTAL_INSERT) + found[keys_no_ks[i]]++; + } + + for (i = 0; i < count_keys_ks; i++) { + /* Identify keys in keys_ks with value less than 1M */ + if (keys_ks[i] < TOTAL_INSERT) + found[keys_ks[i]]++; + } + + uint32_t count_keys_absent = 0; + for (i = 0; i < TOTAL_INSERT; i++) { + /* Identify missing keys between 0 and 1M */ + if (found[i] == 0) + keys_absent[count_keys_absent++] = i; + } + + /* Find keys that will not be on the shift path */ + uint32_t iter; + const void *next_key; + void *next_data; + uint32_t count = 0; + for (i = 0; i < TOTAL_INSERT / 8; i++) { + /* Check bucket for no keys shifted to alternate locations */ + if (scanned_bkts[i] == 0) { + iter = i * 8; + while (rte_hash_iterate(tbl_rwc_test_param.h, + &next_key, &next_data, &iter) >= 0) { + + /* Check if key belongs to the current bucket */ + if (i >= (iter-1)/8) + keys_non_shift_path[count++] + = *(const uint32_t *)next_key; + else + break; + } + } + } + + tbl_rwc_test_param.count_keys_no_ks = count_keys_no_ks; + tbl_rwc_test_param.count_keys_ks = count_keys_ks; + tbl_rwc_test_param.count_keys_absent = count_keys_absent; + tbl_rwc_test_param.count_keys_non_shift_path = count; + + printf("\nCount of keys NOT causing shifting of existing keys to " + "alternate location: %d\n", tbl_rwc_test_param.count_keys_no_ks); + printf("\nCount of keys causing shifting of existing keys to alternate " + "locations: %d\n\n", tbl_rwc_test_param.count_keys_ks); + printf("Count of absent keys that will never be added to the hash " + "table: %d\n\n", tbl_rwc_test_param.count_keys_absent); + printf("Count of keys likely to be on the shift path: %d\n\n", + tbl_rwc_test_param.count_keys_shift_path); + printf("Count of keys not likely to be on the shift path: %d\n\n", + tbl_rwc_test_param.count_keys_non_shift_path); + + rte_free(found); + rte_hash_free(tbl_rwc_test_param.h); + return 0; + +err: + rte_free(keys); + rte_free(keys_no_ks); + rte_free(keys_ks); + rte_free(keys_absent); + rte_free(found); + rte_free(tbl_rwc_test_param.keys_shift_path); + rte_free(scanned_bkts); + return -1; +} + +static int +init_params(int rwc_lf, int use_jhash) +{ + struct rte_hash *handle; + + struct rte_hash_parameters hash_params = { + .entries = TOTAL_ENTRY, + .key_len = sizeof(uint32_t), + .hash_func_init_val = 0, + .socket_id = rte_socket_id(), + }; + + if (use_jhash) + hash_params.hash_func = rte_jhash; + else + hash_params.hash_func = rte_hash_crc; + + if (rwc_lf) + hash_params.extra_flag = + RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF | + RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD; + else if (htm) + hash_params.extra_flag = + RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT | + RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY | + RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD; + else + hash_params.extra_flag = + RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY | + RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD; + + hash_params.name = "tests"; + + handle = rte_hash_create(&hash_params); + if (handle == NULL) { + printf("hash creation failed"); + return -1; + } + + tbl_rwc_test_param.h = handle; + return 0; +} + +static int +test_rwc_reader(__attribute__((unused)) void *arg) +{ + uint32_t i; + int ret; + uint64_t begin, cycles; + uint32_t loop_cnt = 0; + uint8_t read_type = (uint8_t)((uintptr_t)arg); + uint32_t read_cnt; + uint32_t *keys; + + if (read_type == READ_FAIL) { + keys = tbl_rwc_test_param.keys_absent; + read_cnt = tbl_rwc_test_param.count_keys_absent; + } else if (read_type == READ_PASS_NO_KEY_SHIFTS) { + keys = tbl_rwc_test_param.keys_no_ks; + read_cnt = tbl_rwc_test_param.count_keys_no_ks; + } else if (read_type == READ_PASS_SHIFT_PATH) { + keys = tbl_rwc_test_param.keys_shift_path; + read_cnt = tbl_rwc_test_param.count_keys_shift_path; + } else { + keys = tbl_rwc_test_param.keys_non_shift_path; + read_cnt = tbl_rwc_test_param.count_keys_non_shift_path; + } + + begin = rte_rdtsc_precise(); + do { + for (i = 0; i < read_cnt; i++) { + ret = rte_hash_lookup(tbl_rwc_test_param.h, keys + i); + if ((read_type == READ_FAIL && ret != -ENOENT) + || (read_type != READ_FAIL && ret == -ENOENT)) { + printf("lookup failed! %"PRIu32"\n", keys[i]); + return -1; + } + } + loop_cnt++; + } while (!writer_done); + + cycles = rte_rdtsc_precise() - begin; + rte_atomic64_add(&gread_cycles, cycles); + rte_atomic64_add(&greads, i*loop_cnt); + return 0; +} + +static int +write_keys(uint8_t key_shift) +{ + uint32_t i; + int ret; + uint32_t key_cnt; + uint32_t *keys; + if (key_shift) { + key_cnt = tbl_rwc_test_param.count_keys_ks; + keys = tbl_rwc_test_param.keys_ks; + } else { + key_cnt = tbl_rwc_test_param.count_keys_no_ks; + keys = tbl_rwc_test_param.keys_no_ks; + } + for (i = 0; i < key_cnt; i++) { + ret = rte_hash_add_key(tbl_rwc_test_param.h, keys + i); + if (!key_shift && ret < 0) { + printf("writer failed %"PRIu32"\n", i); + return -1; + } + } + return 0; +} + +static int +test_rwc_multi_writer(__attribute__((unused)) void *arg) +{ + uint32_t i, offset; + uint32_t pos_core = (uint32_t)((uintptr_t)arg); + offset = pos_core * tbl_rwc_test_param.single_insert; + for (i = offset; i < offset + tbl_rwc_test_param.single_insert; i++) + rte_hash_add_key(tbl_rwc_test_param.h, + tbl_rwc_test_param.keys_ks + i); + multi_writer_done[pos_core] = 1; + return 0; +} + +/* + * Test lookup perf: + * Reader(s) lookup keys present in the table. + */ +static int +test_hash_add_no_ks_lookup_pass(struct rwc_perf *rwc_perf_results, int rwc_lf) +{ + unsigned int n; + uint64_t i; + int use_jhash = 0; + uint8_t key_shift = 0; + uint8_t read_type = READ_PASS_NO_KEY_SHIFTS; + + rte_atomic64_init(&greads); + rte_atomic64_init(&gread_cycles); + + if (init_params(rwc_lf, use_jhash) != 0) + goto err; + printf("\nTest: Hash add - no key-shifts, read - pass\n"); + for (n = 0; n < num_test; n++) { + unsigned int tot_lcore = rte_lcore_count(); + if (tot_lcore < rwc_core_cnt[n] + 1) + goto finish; + + printf("\nNumber of readers: %u\n", rwc_core_cnt[n]); + + rte_atomic64_clear(&greads); + rte_atomic64_clear(&gread_cycles); + + rte_hash_reset(tbl_rwc_test_param.h); + writer_done = 0; + if (write_keys(key_shift) < 0) + goto err; + writer_done = 1; + for (i = 1; i <= rwc_core_cnt[n]; i++) + rte_eal_remote_launch(test_rwc_reader, + (void *)(uintptr_t)read_type, + enabled_core_ids[i]); + rte_eal_mp_wait_lcore(); + + for (i = 1; i <= rwc_core_cnt[n]; i++) + if (lcore_config[i].ret < 0) + goto err; + + unsigned long long cycles_per_lookup = + rte_atomic64_read(&gread_cycles) / + rte_atomic64_read(&greads); + rwc_perf_results->w_no_ks_r_pass[n] = cycles_per_lookup; + printf("Cycles per lookup: %llu\n", cycles_per_lookup); + } + +finish: + rte_hash_free(tbl_rwc_test_param.h); + return 0; + +err: + rte_hash_free(tbl_rwc_test_param.h); + return -1; +} + +/* + * Test lookup perf: + * Reader(s) lookup keys absent in the table while + * 'Main' thread adds with no key-shifts. + */ +static int +test_hash_add_no_ks_lookup_fail(struct rwc_perf *rwc_perf_results, int rwc_lf) +{ + unsigned int n; + uint64_t i; + int use_jhash = 0; + uint8_t key_shift = 0; + uint8_t read_type = READ_FAIL; + int ret; + + rte_atomic64_init(&greads); + rte_atomic64_init(&gread_cycles); + + if (init_params(rwc_lf, use_jhash) != 0) + goto err; + printf("\nTest: Hash add - no key-shifts, Hash lookup - fail\n"); + for (n = 0; n < num_test; n++) { + unsigned int tot_lcore = rte_lcore_count(); + if (tot_lcore < rwc_core_cnt[n] + 1) + goto finish; + + printf("\nNumber of readers: %u\n", rwc_core_cnt[n]); + + rte_atomic64_clear(&greads); + rte_atomic64_clear(&gread_cycles); + + rte_hash_reset(tbl_rwc_test_param.h); + writer_done = 0; + + for (i = 1; i <= rwc_core_cnt[n]; i++) + rte_eal_remote_launch(test_rwc_reader, + (void *)(uintptr_t)read_type, + enabled_core_ids[i]); + ret = write_keys(key_shift); + writer_done = 1; + rte_eal_mp_wait_lcore(); + + if (ret < 0) + goto err; + for (i = 1; i <= rwc_core_cnt[n]; i++) + if (lcore_config[i].ret < 0) + goto err; + + unsigned long long cycles_per_lookup = + rte_atomic64_read(&gread_cycles) / + rte_atomic64_read(&greads); + rwc_perf_results->w_no_ks_r_fail[n] = cycles_per_lookup; + printf("Cycles per lookup: %llu\n", cycles_per_lookup); + } + +finish: + rte_hash_free(tbl_rwc_test_param.h); + return 0; + +err: + rte_hash_free(tbl_rwc_test_param.h); + return -1; +} + +/* + * Test lookup perf: + * Reader(s) lookup keys present in the table and not likely to be on the + * shift path while 'Main' thread adds keys causing key-shifts. + */ +static int +test_hash_add_ks_lookup_pass_non_sp(struct rwc_perf *rwc_perf_results, + int rwc_lf) +{ + unsigned int n; + uint64_t i; + int use_jhash = 0; + int ret; + uint8_t key_shift; + uint8_t read_type = READ_PASS_NON_SHIFT_PATH; + + rte_atomic64_init(&greads); + rte_atomic64_init(&gread_cycles); + + if (init_params(rwc_lf, use_jhash) != 0) + goto err; + printf("\nTest: Hash add - key shift, Hash lookup - pass" + " (non-shift-path)\n"); + for (n = 0; n < num_test; n++) { + unsigned int tot_lcore = rte_lcore_count(); + if (tot_lcore < rwc_core_cnt[n] + 1) + goto finish; + + printf("\nNumber of readers: %u\n", rwc_core_cnt[n]); + + rte_atomic64_clear(&greads); + rte_atomic64_clear(&gread_cycles); + + rte_hash_reset(tbl_rwc_test_param.h); + writer_done = 0; + key_shift = 0; + if (write_keys(key_shift) < 0) + goto err; + for (i = 1; i <= rwc_core_cnt[n]; i++) + rte_eal_remote_launch(test_rwc_reader, + (void *)(uintptr_t)read_type, + enabled_core_ids[i]); + key_shift = 1; + ret = write_keys(key_shift); + writer_done = 1; + rte_eal_mp_wait_lcore(); + + if (ret < 0) + goto err; + for (i = 1; i <= rwc_core_cnt[n]; i++) + if (lcore_config[i].ret < 0) + goto err; + + unsigned long long cycles_per_lookup = + rte_atomic64_read(&gread_cycles) / + rte_atomic64_read(&greads); + rwc_perf_results->w_ks_r_pass_nsp[n] = cycles_per_lookup; + printf("Cycles per lookup: %llu\n", cycles_per_lookup); + } + +finish: + rte_hash_free(tbl_rwc_test_param.h); + return 0; + +err: + rte_hash_free(tbl_rwc_test_param.h); + return -1; +} + +/* + * Test lookup perf: + * Reader(s) lookup keys present in the table and likely on the shift-path while + * 'Main' thread adds keys causing key-shifts. + */ +static int +test_hash_add_ks_lookup_pass_sp(struct rwc_perf *rwc_perf_results, int rwc_lf) +{ + unsigned int n; + uint64_t i; + int use_jhash = 0; + int ret; + uint8_t key_shift; + uint8_t read_type = READ_PASS_SHIFT_PATH; + + rte_atomic64_init(&greads); + rte_atomic64_init(&gread_cycles); + + if (init_params(rwc_lf, use_jhash) != 0) + goto err; + printf("\nTest: Hash add - key shift, Hash lookup - pass (shift-path)" + "\n"); + + for (n = 0; n < num_test; n++) { + unsigned int tot_lcore = rte_lcore_count(); + if (tot_lcore < rwc_core_cnt[n]) + goto finish; + + printf("\nNumber of readers: %u\n", rwc_core_cnt[n]); + rte_atomic64_clear(&greads); + rte_atomic64_clear(&gread_cycles); + + rte_hash_reset(tbl_rwc_test_param.h); + writer_done = 0; + key_shift = 0; + if (write_keys(key_shift) < 0) + goto err; + for (i = 1; i <= rwc_core_cnt[n]; i++) + rte_eal_remote_launch(test_rwc_reader, + (void *)(uintptr_t)read_type, + enabled_core_ids[i]); + key_shift = 1; + ret = write_keys(key_shift); + writer_done = 1; + rte_eal_mp_wait_lcore(); + + if (ret < 0) + goto err; + for (i = 1; i <= rwc_core_cnt[n]; i++) + if (lcore_config[i].ret < 0) + goto err; + + unsigned long long cycles_per_lookup = + rte_atomic64_read(&gread_cycles) / + rte_atomic64_read(&greads); + rwc_perf_results->w_ks_r_pass_sp[n] = cycles_per_lookup; + printf("Cycles per lookup: %llu\n", cycles_per_lookup); + } + +finish: + rte_hash_free(tbl_rwc_test_param.h); + return 0; + +err: + rte_hash_free(tbl_rwc_test_param.h); + return -1; +} + +/* + * Test lookup perf: + * Reader(s) lookup keys absent in the table while + * 'Main' thread adds keys causing key-shifts. + */ +static int +test_hash_add_ks_lookup_fail(struct rwc_perf *rwc_perf_results, int rwc_lf) +{ + unsigned int n; + uint64_t i; + int use_jhash = 0; + int ret; + uint8_t key_shift; + uint8_t read_type = READ_FAIL; + + rte_atomic64_init(&greads); + rte_atomic64_init(&gread_cycles); + + if (init_params(rwc_lf, use_jhash) != 0) + goto err; + printf("\nTest: Hash add - key shift, Hash lookup - fail\n"); + for (n = 0; n < num_test; n++) { + unsigned int tot_lcore = rte_lcore_count(); + if (tot_lcore < rwc_core_cnt[n] + 1) + goto finish; + + printf("\nNumber of readers: %u\n", rwc_core_cnt[n]); + + rte_atomic64_clear(&greads); + rte_atomic64_clear(&gread_cycles); + + rte_hash_reset(tbl_rwc_test_param.h); + writer_done = 0; + key_shift = 0; + if (write_keys(key_shift) < 0) + goto err; + for (i = 1; i <= rwc_core_cnt[n]; i++) + rte_eal_remote_launch(test_rwc_reader, + (void *)(uintptr_t)read_type, + enabled_core_ids[i]); + key_shift = 1; + ret = write_keys(key_shift); + writer_done = 1; + rte_eal_mp_wait_lcore(); + + if (ret < 0) + goto err; + for (i = 1; i <= rwc_core_cnt[n]; i++) + if (lcore_config[i].ret < 0) + goto err; + + unsigned long long cycles_per_lookup = + rte_atomic64_read(&gread_cycles) / + rte_atomic64_read(&greads); + rwc_perf_results->w_ks_r_fail[n] = cycles_per_lookup; + printf("Cycles per lookup: %llu\n", cycles_per_lookup); + } + +finish: + rte_hash_free(tbl_rwc_test_param.h); + return 0; + +err: + rte_hash_free(tbl_rwc_test_param.h); + return -1; +} + +/* + * Test lookup perf: + * Reader(s) lookup keys present in the table and likely on the shift-path while + * Writers add keys causing key-shifts. + */ +static int +test_hash_multi_add_lookup(struct rwc_perf *rwc_perf_results, int rwc_lf) +{ + unsigned int n, m; + uint64_t i; + int use_jhash = 0; + uint8_t key_shift; + uint8_t read_type = READ_PASS_SHIFT_PATH; + + rte_atomic64_init(&greads); + rte_atomic64_init(&gread_cycles); + + if (init_params(rwc_lf, use_jhash) != 0) + goto err; + printf("\nTest: Multi-add-lookup\n"); + uint8_t pos_core; + for (m = 1; m < num_test; m++) { + /* Calculate keys added by each writer */ + tbl_rwc_test_param.single_insert = + tbl_rwc_test_param.count_keys_ks / rwc_core_cnt[m]; + + for (n = 0; n < num_test; n++) { + unsigned int tot_lcore = rte_lcore_count(); + if (tot_lcore < rwc_core_cnt[n] + rwc_core_cnt[m] + 1) + goto finish; + + printf("\nNumber of writers: %u", rwc_core_cnt[m]); + printf("\nNumber of readers: %u\n", rwc_core_cnt[n]); + + rte_atomic64_clear(&greads); + rte_atomic64_clear(&gread_cycles); + + rte_hash_reset(tbl_rwc_test_param.h); + writer_done = 0; + for (i = 0; i < 4; i++) + multi_writer_done[i] = 0; + key_shift = 0; + if (write_keys(key_shift) < 0) + goto err; + + /* Launch reader(s) */ + for (i = 1; i <= rwc_core_cnt[n]; i++) + rte_eal_remote_launch(test_rwc_reader, + (void *)(uintptr_t)read_type, + enabled_core_ids[i]); + key_shift = 1; + pos_core = 0; + + /* Launch writers */ + for (; i <= rwc_core_cnt[m] + rwc_core_cnt[n]; i++) { + rte_eal_remote_launch(test_rwc_multi_writer, + (void *)(uintptr_t)pos_core, + enabled_core_ids[i]); + pos_core++; + } + + /* Wait for writers to complete */ + for (i = 0; i < rwc_core_cnt[m]; i++) + while + (multi_writer_done[i] == 0); + writer_done = 1; + + rte_eal_mp_wait_lcore(); + + for (i = 1; i <= rwc_core_cnt[n]; i++) + if (lcore_config[i].ret < 0) + goto err; + + unsigned long long cycles_per_lookup = + rte_atomic64_read(&gread_cycles) / + rte_atomic64_read(&greads); + rwc_perf_results->multi_rw[m][n] = cycles_per_lookup; + printf("Cycles per lookup: %llu\n", cycles_per_lookup); + } + } + +finish: + rte_hash_free(tbl_rwc_test_param.h); + return 0; + +err: + rte_hash_free(tbl_rwc_test_param.h); + return -1; +} + +static int +test_hash_readwrite_lf_main(void) +{ + /* + * Variables used to choose different tests. + * rwc_lf indicates if read-write concurrency lock-free support is + * enabled. + * htm indicates if Hardware transactional memory support is enabled. + */ + int rwc_lf = 0; + int use_jhash = 0; + num_test = NUM_TEST; + if (rte_lcore_count() == 1) { + printf("More than one lcore is required " + "to do read write lock-free concurrency test\n"); + return -1; + } + + setlocale(LC_NUMERIC, ""); + + if (rte_tm_supported()) + htm = 1; + else + htm = 0; + + if (init_params(rwc_lf, use_jhash) != 0) + return -1; + if (generate_keys() != 0) + return -1; + if (get_enabled_cores_list() != 0) + return -1; + + if (RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF) { + rwc_lf = 1; + printf("Test lookup with read-write concurrency lock free support" + " enabled\n"); + if (test_hash_add_no_ks_lookup_pass(&rwc_lf_results, rwc_lf) + < 0) + return -1; + if (test_hash_add_no_ks_lookup_fail(&rwc_lf_results, rwc_lf) + < 0) + return -1; + if (test_hash_add_ks_lookup_pass_non_sp(&rwc_lf_results, rwc_lf) + < 0) + return -1; + if (test_hash_add_ks_lookup_pass_sp(&rwc_lf_results, rwc_lf) + < 0) + return -1; + if (test_hash_add_ks_lookup_fail(&rwc_lf_results, rwc_lf) < 0) + return -1; + if (test_hash_multi_add_lookup(&rwc_lf_results, rwc_lf) < 0) + return -1; + } + printf("\nTest lookup with read-write concurrency lock free support" + " disabled\n"); + rwc_lf = 0; + if (!htm) { + printf("With HTM Disabled\n"); + if (!RUN_WITH_HTM_DISABLED) { + printf("Enable RUN_WITH_HTM_DISABLED to test with" + " lock-free disabled"); + goto results; + } + } else + printf("With HTM Enabled\n"); + if (test_hash_add_no_ks_lookup_pass(&rwc_non_lf_results, rwc_lf) < 0) + return -1; + if (test_hash_add_no_ks_lookup_fail(&rwc_non_lf_results, rwc_lf) < 0) + return -1; + if (test_hash_add_ks_lookup_pass_non_sp(&rwc_non_lf_results, rwc_lf) + < 0) + return -1; + if (test_hash_add_ks_lookup_pass_sp(&rwc_non_lf_results, rwc_lf) < 0) + return -1; + if (test_hash_add_ks_lookup_fail(&rwc_non_lf_results, rwc_lf) < 0) + return -1; + if (test_hash_multi_add_lookup(&rwc_non_lf_results, rwc_lf) < 0) + return -1; +results: + printf("\n\t\t\t\t\t\t********** Results summary **********\n\n"); + printf("_______\t\t_______\t\t_________\t___\t\t_________\t\t\t\t\t\t" + "_________________\n"); + int i, j; + printf("Writers\t\tReaders\t\tLock-free\tHTM\t\tTest-case\t\t\t\t\t\t" + "Cycles per lookup\n"); + printf("_______\t\t_______\t\t_________\t___\t\t_________\t\t\t\t\t\t" + "_________________\n"); + for (i = 0; i < NUM_TEST; i++) { + printf("%u\t\t%u\t\t", 1, rwc_core_cnt[i]); + printf("Enabled\t\t"); + printf("N/A\t\t"); + printf("Hash add - no key-shifts, lookup - pass\t\t\t\t%u\n\t\t" + "\t\t\t\t\t\t", rwc_lf_results.w_no_ks_r_pass[i]); + printf("Hash add - no key-shifts, lookup - fail\t\t\t\t%u\n\t\t" + "\t\t\t\t\t\t", rwc_lf_results.w_no_ks_r_fail[i]); + printf("Hash add - key-shifts, lookup - pass (non-shift-path)\t" + "\t%u\n\t\t\t\t\t\t\t\t", + rwc_lf_results.w_ks_r_pass_nsp[i]); + printf("Hash add - key-shifts, lookup - pass (shift-path)\t\t%u" + "\n\t\t\t\t\t\t\t\t", rwc_lf_results.w_ks_r_pass_sp[i]); + printf("Hash add - key-shifts, Hash lookup fail\t\t\t\t%u\n\n" + "\t\t\t\t", rwc_lf_results.w_ks_r_fail[i]); + + printf("Disabled\t"); + if (htm) + printf("Enabled\t\t"); + else + printf("Disabled\t"); + printf("Hash add - no key-shifts, lookup - pass\t\t\t\t%u\n\t\t" + "\t\t\t\t\t\t", rwc_non_lf_results.w_no_ks_r_pass[i]); + printf("Hash add - no key-shifts, lookup - fail\t\t\t\t%u\n\t\t" + "\t\t\t\t\t\t", rwc_non_lf_results.w_no_ks_r_fail[i]); + printf("Hash add - key-shifts, lookup - pass (non-shift-path)\t" + "\t%u\n\t\t\t\t\t\t\t\t", + rwc_non_lf_results.w_ks_r_pass_nsp[i]); + printf("Hash add - key-shifts, lookup - pass (shift-path)\t\t%u" + "\n\t\t\t\t\t\t\t\t", + rwc_non_lf_results.w_ks_r_pass_sp[i]); + printf("Hash add - key-shifts, Hash lookup fail\t\t\t\t%u\n", + rwc_non_lf_results.w_ks_r_fail[i]); + + printf("_______\t\t_______\t\t_________\t___\t\t_________\t\t\t\t" + "\t\t_________________\n"); + } + + for (i = 1; i < NUM_TEST; i++) { + for (j = 0; j < NUM_TEST; j++) { + printf("%u", rwc_core_cnt[i]); + printf("\t\t%u\t\t", rwc_core_cnt[j]); + printf("Enabled\t\t"); + printf("N/A\t\t"); + printf("Multi-add-lookup\t\t\t\t\t\t%u\n\n\t\t\t\t", + rwc_lf_results.multi_rw[i][j]); + printf("Disabled\t"); + if (htm) + printf("Enabled\t\t"); + else + printf("Disabled\t"); + printf("Multi-add-lookup\t\t\t\t\t\t%u\n", + rwc_non_lf_results.multi_rw[i][j]); + + printf("_______\t\t_______\t\t_________\t___\t\t" + "_________\t\t\t\t\t\t_________________\n"); + } + } + + rte_free(tbl_rwc_test_param.keys); + rte_free(tbl_rwc_test_param.keys_no_ks); + rte_free(tbl_rwc_test_param.keys_ks); + rte_free(tbl_rwc_test_param.keys_absent); + rte_free(tbl_rwc_test_param.keys_shift_path); + rte_free(scanned_bkts); + return 0; +} + +REGISTER_TEST_COMMAND(hash_readwrite_lf_autotest, test_hash_readwrite_lf_main);