From patchwork Fri Feb 22 07:04:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Honnappa Nagarahalli X-Patchwork-Id: 158985 Delivered-To: patch@linaro.org Received: by 2002:a02:48:0:0:0:0:0 with SMTP id 69csp1395254jaa; Thu, 21 Feb 2019 23:05:15 -0800 (PST) X-Google-Smtp-Source: AHgI3IZmUoZngVPFlrhkrJlFsxu3Q/kjk330deyGdaT3of4z5GuJwTemkvCl8EP43Zi02OwgkWD6 X-Received: by 2002:a17:906:1945:: with SMTP id b5mr1794053eje.156.1550819115876; Thu, 21 Feb 2019 23:05:15 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1550819115; cv=none; d=google.com; s=arc-20160816; b=nKtU4SuCI+QRUotC0yhAjJoIx/7n0bJUopaWXQw7jIgt5321pyKkn1hC+Hy5cs8luJ nfjRdqXcl2FRvyLiIATFM5mAvOqkLQKL1o80R+Fc7P5dZfgulwOVwKYVyU0tWTGdSOEV 9+R6xM9iElQkp/hKTMYDtES0XMJPqWAW6/IISESEZQkRqvy/51IHq2WtsILnBUIdNZ+7 D2QbquQ/Hm5FCFOvPwICqsh+BVMRrMIvk6XN1NvqNIJuqXiO9WRTe/y+g3J6I7b2nWVJ sKiuXKBrHWRet8mfBSF8oUiPrdQkIRf/Dl0IXSYtgBYzRmafXO/s9XT7FPmoK7wsT+8d KLvQ== 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=R+TAH/otDOv57v7/YPeFnIgXjUgUyyTDfClJWX27O8o=; b=KijB17D7rCbutTTpeBv3IAJLRbvIxC/DP4HIHktebgat8x4931KFvk7GUrPZIkFrY4 OV7XoAM4DDnhg6LvGUEhAFP6k2s+CYjVlAU0KZ98o2GFn0Ro8SuieDto8Y4f9WUSL8uy O5l2wr0nPTiGwIoJmoxhNHxKaFPYRqswARBIl4MTU7eYR9mw6SBK7T/TGfx/W+n3PMew hDPESTJqjq1e2Dht2OrgW/xrYQCAGZ68QzK2o++AebTYhAX54+Xaly6Tcf9dZNEAxegM 9R0sJWUjd5PKit+RlknS+hyelCia5Bq1JAaOB7Bwojn+huSjUNRlpcEeb2JpxHeictax HQ8w== 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 39si310292edq.222.2019.02.21.23.05.15; Thu, 21 Feb 2019 23:05:15 -0800 (PST) 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 579172C38; Fri, 22 Feb 2019 08:05:09 +0100 (CET) Received: from foss.arm.com (foss.arm.com [217.140.101.70]) by dpdk.org (Postfix) with ESMTP id 6C11B2C2B for ; Fri, 22 Feb 2019 08:05:07 +0100 (CET) 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 D6D0480D; Thu, 21 Feb 2019 23:05:06 -0800 (PST) Received: from qc2400f-1.austin.arm.com (qc2400f-1.austin.arm.com [10.118.12.104]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 537E33F690; Thu, 21 Feb 2019 23:05:06 -0800 (PST) From: Honnappa Nagarahalli To: konstantin.ananyev@intel.com, stephen@networkplumber.org, paulmck@linux.ibm.com, dev@dpdk.org, honnappa.nagarahalli@arm.com Cc: gavin.hu@arm.com, dharmik.thakkar@arm.com, malvika.gupta@arm.com, nd@arm.com Date: Fri, 22 Feb 2019 01:04:24 -0600 Message-Id: <20190222070427.22866-3-honnappa.nagarahalli@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190222070427.22866-1-honnappa.nagarahalli@arm.com> References: <20181222021420.5114-1-honnappa.nagarahalli@arm.com> <20190222070427.22866-1-honnappa.nagarahalli@arm.com> Subject: [dpdk-dev] [RFC v3 2/5] test/rcu_qsbr: add API and functional tests 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 Add API positive/negative test cases, functional tests and performance tests. Signed-off-by: Malvika Gupta Signed-off-by: Dharmik Thakkar Signed-off-by: Honnappa Nagarahalli Reviewed-by: Gavin Hu --- test/test/Makefile | 2 + test/test/autotest_data.py | 12 + test/test/meson.build | 7 +- test/test/test_rcu_qsbr.c | 831 +++++++++++++++++++++++++++++++++ test/test/test_rcu_qsbr_perf.c | 272 +++++++++++ 5 files changed, 1123 insertions(+), 1 deletion(-) create mode 100644 test/test/test_rcu_qsbr.c create mode 100644 test/test/test_rcu_qsbr_perf.c -- 2.17.1 diff --git a/test/test/Makefile b/test/test/Makefile index 89949c2bb..6b6dfefc2 100644 --- a/test/test/Makefile +++ b/test/test/Makefile @@ -213,6 +213,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_KVARGS) += test_kvargs.c SRCS-$(CONFIG_RTE_LIBRTE_BPF) += test_bpf.c +SRCS-$(CONFIG_RTE_LIBRTE_RCU) += test_rcu_qsbr.c test_rcu_qsbr_perf.c + SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += test_ipsec.c ifeq ($(CONFIG_RTE_LIBRTE_IPSEC),y) LDLIBS += -lrte_ipsec diff --git a/test/test/autotest_data.py b/test/test/autotest_data.py index 5f87bb94d..c26ec889c 100644 --- a/test/test/autotest_data.py +++ b/test/test/autotest_data.py @@ -694,6 +694,18 @@ "Func": default_autotest, "Report": None, }, + { + "Name": "RCU QSBR autotest", + "Command": "rcu_qsbr_autotest", + "Func": default_autotest, + "Report": None, + }, + { + "Name": "RCU QSBR performance autotest", + "Command": "rcu_qsbr_perf_autotest", + "Func": default_autotest, + "Report": None, + }, # # Please always make sure that ring_perf is the last test! # diff --git a/test/test/meson.build b/test/test/meson.build index 05e5ddeb0..4df8e337b 100644 --- a/test/test/meson.build +++ b/test/test/meson.build @@ -107,6 +107,8 @@ test_sources = files('commands.c', 'test_timer.c', 'test_timer_perf.c', 'test_timer_racecond.c', + 'test_rcu_qsbr.c', + 'test_rcu_qsbr_perf.c', 'test_version.c', 'virtual_pmd.c' ) @@ -132,7 +134,8 @@ test_deps = ['acl', 'port', 'reorder', 'ring', - 'timer' + 'timer', + 'rcu' ] # All test cases in fast_parallel_test_names list are parallel @@ -171,6 +174,7 @@ fast_parallel_test_names = [ 'ring_autotest', 'ring_pmd_autotest', 'rwlock_autotest', + 'rcu_qsbr_autotest', 'sched_autotest', 'spinlock_autotest', 'string_autotest', @@ -236,6 +240,7 @@ perf_test_names = [ 'red_perf', 'distributor_perf_autotest', 'ring_pmd_perf_autotest', + 'rcu_qsbr_perf_autotest', 'pmd_perf_autotest', ] diff --git a/test/test/test_rcu_qsbr.c b/test/test/test_rcu_qsbr.c new file mode 100644 index 000000000..ae60614af --- /dev/null +++ b/test/test/test_rcu_qsbr.c @@ -0,0 +1,831 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2018 Arm Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" + +/* Check condition and return an error if true. */ +#define RCU_QSBR_RETURN_IF_ERROR(cond, str, ...) do { \ + if (cond) { \ + printf("ERROR file %s, line %d: " str "\n", __FILE__, \ + __LINE__, ##__VA_ARGS__); \ + return -1; \ + } \ +} while (0) + +#define RTE_RCU_MAX_LCORE 128 +uint16_t enabled_core_ids[RTE_RCU_MAX_LCORE]; +uint8_t num_cores; +uint16_t num_1qs = 1; /* Number of quiescent states = 1 */ +uint16_t num_2qs = 2; /* Number of quiescent states = 2 */ +uint16_t num_3qs = 3; /* Number of quiescent states = 3 */ + +static uint32_t *keys; +#define TOTAL_ENTRY (1024 * 8) +#define COUNTER_VALUE 4096 +uint32_t *hash_data[RTE_RCU_MAX_LCORE][TOTAL_ENTRY]; +uint8_t writer_done; + +struct rte_rcu_qsbr t[RTE_RCU_MAX_LCORE]; +struct rte_hash *h[RTE_RCU_MAX_LCORE]; +char hash_name[RTE_RCU_MAX_LCORE][8]; + +static inline int +get_enabled_cores_mask(void) +{ + uint16_t core_id; + uint32_t max_cores = rte_lcore_count(); + if (max_cores > RTE_RCU_MAX_LCORE) { + printf("Number of cores exceed %d\n", RTE_RCU_MAX_LCORE); + return -1; + } + + core_id = 0; + num_cores = 0; + RTE_LCORE_FOREACH_SLAVE(core_id) { + enabled_core_ids[num_cores] = core_id; + num_cores++; + } + + return 0; +} + +/* + * rte_rcu_qsbr_register_thread: Add a reader thread, to the list of threads + * reporting their quiescent state on a QS variable. + */ +static int +test_rcu_qsbr_register_thread(void) +{ + printf("\nTest rte_rcu_qsbr_register_thread()\n"); + + rte_rcu_qsbr_init(&t[0]); + + rte_rcu_qsbr_register_thread(&t[0], enabled_core_ids[0]); + return 0; +} + +/* + * rte_rcu_qsbr_unregister_thread: Remove a reader thread, from the list of + * threads reporting their quiescent state on a QS variable. + */ +static int +test_rcu_qsbr_unregister_thread(void) +{ + int i, j, ret; + uint64_t token; + uint8_t num_threads[3] = {1, RTE_RCU_MAX_THREADS, 1}; + + printf("\nTest rte_rcu_qsbr_unregister_thread()\n"); + + rte_rcu_qsbr_init(&t[0]); + + rte_rcu_qsbr_register_thread(&t[0], enabled_core_ids[0]); + + /* Find first disabled core */ + for (i = 0; i < RTE_RCU_MAX_LCORE; i++) { + if (enabled_core_ids[i] == 0) + break; + } + /* Test with disabled lcore */ + rte_rcu_qsbr_unregister_thread(&t[0], i); + + /* Test with enabled lcore */ + rte_rcu_qsbr_unregister_thread(&t[0], enabled_core_ids[0]); + + /* + * Test with different thread_ids: + * 1 - thread_id = 0 + * 2 - All possible thread_ids, from 0 to RTE_RCU_MAX_THREADS + * 3 - thread_id = RTE_RCU_MAX_THREADS - 1 + */ + for (j = 0; j < 3; j++) { + rte_rcu_qsbr_init(&t[0]); + + for (i = 0; i < num_threads[j]; i++) + rte_rcu_qsbr_register_thread(&t[0], + (j == 2) ? (RTE_RCU_MAX_THREADS - 1) : i); + + rte_rcu_qsbr_start(&t[0], 1, &token); + RCU_QSBR_RETURN_IF_ERROR((token != 1), "QSBR Start"); + /* Update quiescent state counter */ + for (i = 0; i < num_threads[j]; i++) { + /* Skip one update */ + if (i == (RTE_RCU_MAX_THREADS - 10)) + continue; + rte_rcu_qsbr_update(&t[0], + (j == 2) ? (RTE_RCU_MAX_THREADS - 1) : i); + } + + if (j == 1) { + /* Validate the updates */ + ret = rte_rcu_qsbr_check(&t[0], token, false); + RCU_QSBR_RETURN_IF_ERROR((ret == 1), "Non-blocking QSBR check"); + /* Update the previously skipped thread */ + rte_rcu_qsbr_update(&t[0], RTE_RCU_MAX_THREADS - 10); + } + + /* Validate the updates */ + ret = rte_rcu_qsbr_check(&t[0], token, false); + RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Non-blocking QSBR check"); + + for (i = 0; i < num_threads[j]; i++) + rte_rcu_qsbr_unregister_thread(&t[0], + (j == 2) ? (RTE_RCU_MAX_THREADS - 1) : i); + + /* Check with no thread registered */ + ret = rte_rcu_qsbr_check(&t[0], token, true); + RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Blocking QSBR check"); + } + return 0; +} + +/* + * rte_rcu_qsbr_start: Trigger the worker threads to report the quiescent state + * status. + */ +static int +test_rcu_qsbr_start(void) +{ + uint64_t token; + int i; + + printf("\nTest rte_rcu_qsbr_start()\n"); + + rte_rcu_qsbr_init(&t[0]); + + for (i = 0; i < 3; i++) + rte_rcu_qsbr_register_thread(&t[0], enabled_core_ids[i]); + + rte_rcu_qsbr_start(&t[0], 1, &token); + RCU_QSBR_RETURN_IF_ERROR((token != 1), "QSBR Start"); + return 0; +} + +static int +test_rcu_qsbr_check_reader(void *arg) +{ + struct rte_rcu_qsbr *temp; + uint8_t read_type = (uint8_t)((uintptr_t)arg); + temp = &t[read_type]; + + /* Update quiescent state counter */ + rte_rcu_qsbr_update(temp, enabled_core_ids[0]); + rte_rcu_qsbr_update(temp, enabled_core_ids[1]); + rte_rcu_qsbr_unregister_thread(temp, enabled_core_ids[2]); + rte_rcu_qsbr_update(temp, enabled_core_ids[3]); + return 0; +} +/* + * rte_rcu_qsbr_check: Checks if all the worker threads have entered the queis- + * cent state 'n' number of times. 'n' is provided in rte_rcu_qsbr_start API. + */ +static int +test_rcu_qsbr_check(void) +{ + int i, ret; + uint64_t token; + + printf("\nTest rte_rcu_qsbr_check()\n"); + + rte_rcu_qsbr_init(&t[0]); + + rte_rcu_qsbr_start(&t[0], 1, &token); + RCU_QSBR_RETURN_IF_ERROR((token != 1), "QSBR Start"); + + + ret = rte_rcu_qsbr_check(&t[0], 0, false); + RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Token = 0"); + + ret = rte_rcu_qsbr_check(&t[0], token, true); + RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Blocking QSBR check"); + + for (i = 0; i < 3; i++) + rte_rcu_qsbr_register_thread(&t[0], enabled_core_ids[i]); + + ret = rte_rcu_qsbr_check(&t[0], token, false); + RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Non-blocking QSBR check"); + + rte_rcu_qsbr_start(&t[0], 1, &token); + RCU_QSBR_RETURN_IF_ERROR((token != 2), "QSBR Start"); + + ret = rte_rcu_qsbr_check(&t[0], token, false); + RCU_QSBR_RETURN_IF_ERROR((ret == 1), "Non-blocking QSBR check"); + + for (i = 0; i < 3; i++) + rte_rcu_qsbr_unregister_thread(&t[0], enabled_core_ids[i]); + + ret = rte_rcu_qsbr_check(&t[0], token, true); + RCU_QSBR_RETURN_IF_ERROR((ret == 0), "Blocking QSBR check"); + + rte_rcu_qsbr_init(&t[0]); + + for (i = 0; i < 4; i++) + rte_rcu_qsbr_register_thread(&t[0], enabled_core_ids[i]); + + rte_rcu_qsbr_start(&t[0], 1, &token); + RCU_QSBR_RETURN_IF_ERROR((token != 1), "QSBR Start"); + + rte_eal_remote_launch(test_rcu_qsbr_check_reader, NULL, + enabled_core_ids[0]); + + rte_eal_mp_wait_lcore(); + ret = rte_rcu_qsbr_check(&t[0], token, true); + RCU_QSBR_RETURN_IF_ERROR((ret != 1), "Blocking QSBR check"); + + return 0; +} + +/* + * rte_rcu_qsbr_dump: Dump status of a single QS variable to a file + */ +static int +test_rcu_qsbr_dump(void) +{ + int i; + + printf("\nTest rte_rcu_qsbr_dump()\n"); + + rte_rcu_qsbr_init(&t[0]); + rte_rcu_qsbr_init(&t[1]); + + /* QS variable with 0 core mask */ + rte_rcu_qsbr_dump(stdout, &t[0]); + + rte_rcu_qsbr_register_thread(&t[0], enabled_core_ids[0]); + + for (i = 1; i < 3; i++) + rte_rcu_qsbr_register_thread(&t[1], enabled_core_ids[i]); + + rte_rcu_qsbr_dump(stdout, &t[0]); + rte_rcu_qsbr_dump(stdout, &t[1]); + printf("\n"); + return 0; +} + +static int +test_rcu_qsbr_reader(void *arg) +{ + struct rte_rcu_qsbr *temp; + struct rte_hash *hash = NULL; + int i; + uint32_t lcore_id = rte_lcore_id(); + uint8_t read_type = (uint8_t)((uintptr_t)arg); + uint32_t *pdata; + + temp = &t[read_type]; + hash = h[read_type]; + + do { + rte_rcu_qsbr_register_thread(temp, lcore_id); + for (i = 0; i < TOTAL_ENTRY; i++) { + if (rte_hash_lookup_data(hash, keys+i, + (void **)&pdata) != -ENOENT) { + *pdata = 0; + while (*pdata < COUNTER_VALUE) + ++*pdata; + } + } + /* Update quiescent state counter */ + rte_rcu_qsbr_update(temp, lcore_id); + rte_rcu_qsbr_unregister_thread(temp, lcore_id); + } while (!writer_done); + + return 0; +} + +static int +test_rcu_qsbr_writer(void *arg) +{ + uint64_t token; + int32_t pos; + struct rte_rcu_qsbr *temp; + struct rte_hash *hash = NULL; + uint8_t writer_type = (uint8_t)((uintptr_t)arg); + + temp = &t[(writer_type/2) % RTE_RCU_MAX_LCORE]; + hash = h[(writer_type/2) % RTE_RCU_MAX_LCORE]; + + /* Delete element from the shared data structure */ + pos = rte_hash_del_key(hash, keys + (writer_type % TOTAL_ENTRY)); + if (pos < 0) { + printf("Delete key failed #%d\n", + keys[writer_type % TOTAL_ENTRY]); + return -1; + } + /* + * Start the quiescent state query process + * Note: Expected Quiescent states kept greater than 1 for test only + */ + rte_rcu_qsbr_start(temp, writer_type + 1, &token); + /* Check the quiescent state status */ + rte_rcu_qsbr_check(temp, token, true); + if (*hash_data[(writer_type/2) % RTE_RCU_MAX_LCORE] + [writer_type % TOTAL_ENTRY] != COUNTER_VALUE && + *hash_data[(writer_type/2) % RTE_RCU_MAX_LCORE] + [writer_type % TOTAL_ENTRY] != 0) { + printf("Reader did not complete #%d = %d\t", writer_type, + *hash_data[(writer_type/2) % RTE_RCU_MAX_LCORE] + [writer_type % TOTAL_ENTRY]); + return -1; + } + + if (rte_hash_free_key_with_position(hash, pos) < 0) { + printf("Failed to free the key #%d\n", + keys[writer_type % TOTAL_ENTRY]); + return -1; + } + rte_free(hash_data[(writer_type/2) % RTE_RCU_MAX_LCORE] + [writer_type % TOTAL_ENTRY]); + hash_data[(writer_type/2) % RTE_RCU_MAX_LCORE] + [writer_type % TOTAL_ENTRY] = NULL; + + return 0; +} + +static struct rte_hash * +init_hash(int hash_id) +{ + int i; + struct rte_hash *h = NULL; + sprintf(hash_name[hash_id], "hash%d", hash_id); + struct rte_hash_parameters hash_params = { + .entries = TOTAL_ENTRY, + .key_len = sizeof(uint32_t), + .hash_func_init_val = 0, + .socket_id = rte_socket_id(), + .hash_func = rte_hash_crc, + .extra_flag = + RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF, + .name = hash_name[hash_id], + }; + + h = rte_hash_create(&hash_params); + if (h == NULL) { + printf("Hash create Failed\n"); + return NULL; + } + + for (i = 0; i < TOTAL_ENTRY; i++) { + hash_data[hash_id][i] = rte_zmalloc(NULL, sizeof(uint32_t), 0); + if (hash_data[hash_id][i] == NULL) { + printf("No memory\n"); + return NULL; + } + } + keys = rte_malloc(NULL, sizeof(uint32_t) * TOTAL_ENTRY, 0); + if (keys == NULL) { + printf("No memory\n"); + return NULL; + } + + for (i = 0; i < TOTAL_ENTRY; i++) + keys[i] = i; + + for (i = 0; i < TOTAL_ENTRY; i++) { + if (rte_hash_add_key_data(h, keys + i, + (void *)((uintptr_t)hash_data[hash_id][i])) + < 0) { + printf("Hash key add Failed #%d\n", i); + return NULL; + } + } + return h; +} + +/* + * Functional test: + * Single writer, Single QS variable Single QSBR query, Blocking rcu_qsbr_check + */ +static int +test_rcu_qsbr_sw_sv_1qs(void) +{ + uint64_t token; + int i; + int32_t pos; + writer_done = 0; + + printf("\nTest: 1 writer, 1 QSBR variable, 1 QSBR Query, " + "Blocking QSBR Check\n"); + + /* QS variable is initialized */ + rte_rcu_qsbr_init(&t[0]); + + /* Register worker threads on 4 cores */ + for (i = 0; i < 4; i++) + rte_rcu_qsbr_register_thread(&t[0], enabled_core_ids[i]); + + /* Shared data structure created */ + h[0] = init_hash(0); + if (h[0] == NULL) { + printf("Hash init failed\n"); + goto error; + } + + /* Reader threads are launched */ + for (i = 0; i < 4; i++) + rte_eal_remote_launch(test_rcu_qsbr_reader, NULL, + enabled_core_ids[i]); + + for (i = 0; i < TOTAL_ENTRY; i++) { + /* Delete elements from the shared data structure */ + pos = rte_hash_del_key(h[0], keys + i); + if (pos < 0) { + printf("Delete key failed #%d\n", keys[i]); + goto error; + } + /* Start the quiescent state query process */ + rte_rcu_qsbr_start(&t[0], num_1qs, &token); + + /* Check the quiescent state status */ + rte_rcu_qsbr_check(&t[0], token, true); + if (*hash_data[0][i] != COUNTER_VALUE && + *hash_data[0][i] != 0) { + printf("Reader did not complete #%d = %d\n", i, + *hash_data[0][i]); + goto error; + } + + if (rte_hash_free_key_with_position(h[0], pos) < 0) { + printf("Failed to free the key #%d\n", keys[i]); + goto error; + } + rte_free(hash_data[0][i]); + hash_data[0][i] = NULL; + } + writer_done = 1; + /* Wait until all readers have exited */ + rte_eal_mp_wait_lcore(); + /* Check return value from threads */ + for (i = 0; i < 4; i++) + if (lcore_config[enabled_core_ids[i]].ret < 0) + goto error; + rte_hash_free(h[0]); + rte_free(keys); + + return 0; + +error: + writer_done = 1; + /* Wait until all readers have exited */ + rte_eal_mp_wait_lcore(); + + rte_hash_free(h[0]); + rte_free(keys); + for (i = 0; i < TOTAL_ENTRY; i++) + rte_free(hash_data[0][i]); + + return -1; +} + +/* + * Functional test: + * Single writer, Single QS variable, Single QSBR query, + * Non-blocking rcu_qsbr_check + */ +static int +test_rcu_qsbr_sw_sv_1qs_non_blocking(void) +{ + uint64_t token; + int i, ret; + int32_t pos; + writer_done = 0; + + printf("Test: 1 writer, 1 QSBR variable, 1 QSBR Query, " + "Non-Blocking QSBR check\n"); + + rte_rcu_qsbr_init(&t[0]); + /* Register worker threads on 4 cores */ + for (i = 0; i < 4; i++) + rte_rcu_qsbr_register_thread(&t[0], enabled_core_ids[i]); + + /* Shared data structure created */ + h[0] = init_hash(0); + if (h[0] == NULL) { + printf("Hash init failed\n"); + goto error; + } + + /* Reader threads are launched */ + for (i = 0; i < 4; i++) + rte_eal_remote_launch(test_rcu_qsbr_reader, NULL, + enabled_core_ids[i]); + + for (i = 0; i < TOTAL_ENTRY; i++) { + /* Delete elements from the shared data structure */ + pos = rte_hash_del_key(h[0], keys + i); + if (pos < 0) { + printf("Delete key failed #%d\n", keys[i]); + goto error; + } + /* Start the quiescent state query process */ + rte_rcu_qsbr_start(&t[0], num_1qs, &token); + + /* Check the quiescent state status */ + do { + ret = rte_rcu_qsbr_check(&t[0], token, false); + } while (ret == 0); + if (*hash_data[0][i] != COUNTER_VALUE && + *hash_data[0][i] != 0) { + printf("Reader did not complete #%d = %d\n", i, + *hash_data[0][i]); + goto error; + } + + if (rte_hash_free_key_with_position(h[0], pos) < 0) { + printf("Failed to free the key #%d\n", keys[i]); + goto error; + } + rte_free(hash_data[0][i]); + hash_data[0][i] = NULL; + } + writer_done = 1; + /* Wait until all readers have exited */ + rte_eal_mp_wait_lcore(); + /* Check return value from threads */ + for (i = 0; i < 4; i++) + if (lcore_config[enabled_core_ids[i]].ret < 0) + goto error; + rte_hash_free(h[0]); + rte_free(keys); + + return 0; + +error: + writer_done = 1; + /* Wait until all readers have exited */ + rte_eal_mp_wait_lcore(); + + rte_hash_free(h[0]); + rte_free(keys); + for (i = 0; i < TOTAL_ENTRY; i++) + rte_free(hash_data[0][i]); + + return -1; +} + +/* + * Functional test: + * Single writer, Single QS variable, simultaneous QSBR Queries + */ +static int +test_rcu_qsbr_sw_sv_3qs(void) +{ + uint64_t token[3]; + int i; + int32_t pos[3]; + writer_done = 0; + + printf("Test: 1 writer, 1 QSBR variable, simultaneous QSBR queries\n"); + + rte_rcu_qsbr_init(&t[0]); + + /* Register worker threads on 4 cores */ + for (i = 0; i < 4; i++) + rte_rcu_qsbr_register_thread(&t[0], enabled_core_ids[i]); + + /* Shared data structure created */ + h[0] = init_hash(0); + if (h[0] == NULL) { + printf("Hash init failed\n"); + goto error; + } + + /* Reader threads are launched */ + for (i = 0; i < 4; i++) + rte_eal_remote_launch(test_rcu_qsbr_reader, NULL, + enabled_core_ids[i]); + + /* Delete element from the shared data structure */ + pos[0] = rte_hash_del_key(h[0], keys + 0); + if (pos[0] < 0) { + printf("Delete key failed #%d\n", keys[0]); + goto error; + } + /* Start the quiescent state query process */ + rte_rcu_qsbr_start(&t[0], num_1qs, &token[0]); + + /* Delete element from the shared data structure */ + pos[1] = rte_hash_del_key(h[0], keys + 3); + if (pos[1] < 0) { + printf("Delete key failed #%d\n", keys[3]); + goto error; + } + /* + * Start the quiescent state query process + * Note: num_2qs kept greater than 1 for test only + */ + rte_rcu_qsbr_start(&t[0], num_2qs, &token[1]); + + /* Delete element from the shared data structure */ + pos[2] = rte_hash_del_key(h[0], keys + 6); + if (pos[2] < 0) { + printf("Delete key failed #%d\n", keys[6]); + goto error; + } + /* + * Start the quiescent state query process + * Note: num_3qs kept greater than 1 for test only + */ + rte_rcu_qsbr_start(&t[0], num_3qs, &token[2]); + + /* Check the quiescent state status */ + rte_rcu_qsbr_check(&t[0], token[0], true); + if (*hash_data[0][0] != COUNTER_VALUE && *hash_data[0][0] != 0) { + printf("Reader did not complete #0 = %d\n", *hash_data[0][0]); + goto error; + } + + if (rte_hash_free_key_with_position(h[0], pos[0]) < 0) { + printf("Failed to free the key #%d\n", keys[0]); + goto error; + } + rte_free(hash_data[0][0]); + hash_data[0][0] = NULL; + + /* Check the quiescent state status */ + rte_rcu_qsbr_check(&t[0], token[1], true); + if (*hash_data[0][3] != COUNTER_VALUE && *hash_data[0][3] != 0) { + printf("Reader did not complete #3 = %d\n", *hash_data[0][3]); + goto error; + } + + if (rte_hash_free_key_with_position(h[0], pos[1]) < 0) { + printf("Failed to free the key #%d\n", keys[3]); + goto error; + } + rte_free(hash_data[0][3]); + hash_data[0][3] = NULL; + + /* Check the quiescent state status */ + rte_rcu_qsbr_check(&t[0], token[2], true); + if (*hash_data[0][6] != COUNTER_VALUE && *hash_data[0][6] != 0) { + printf("Reader did not complete #6 = %d\n", *hash_data[0][6]); + goto error; + } + + if (rte_hash_free_key_with_position(h[0], pos[2]) < 0) { + printf("Failed to free the key #%d\n", keys[6]); + goto error; + } + rte_free(hash_data[0][6]); + hash_data[0][6] = NULL; + + writer_done = 1; + /* Wait until all readers have exited */ + rte_eal_mp_wait_lcore(); + /* Check return value from threads */ + for (i = 0; i < 4; i++) + if (lcore_config[enabled_core_ids[i]].ret < 0) + goto error; + rte_hash_free(h[0]); + rte_free(keys); + + return 0; + +error: + writer_done = 1; + /* Wait until all readers have exited */ + rte_eal_mp_wait_lcore(); + + rte_hash_free(h[0]); + rte_free(keys); + for (i = 0; i < TOTAL_ENTRY; i++) + rte_free(hash_data[0][i]); + + return -1; +} + +/* + * Multi writer, Multiple QS variable, simultaneous QSBR queries + */ +static int +test_rcu_qsbr_mw_mv_mqs(void) +{ + int i, j; + writer_done = 0; + uint8_t test_cores; + test_cores = num_cores / 4; + test_cores = test_cores * 4; + + printf("Test: %d writers, %d QSBR variable, Simultaneous QSBR queries\n" + , test_cores / 2, test_cores / 4); + + for (i = 0; i < num_cores / 4; i++) { + rte_rcu_qsbr_init(&t[i]); + h[i] = init_hash(i); + if (h[i] == NULL) { + printf("Hash init failed\n"); + goto error; + } + } + + /* Register worker threads on 2 cores */ + for (i = 0; i < test_cores / 2; i += 2) { + rte_rcu_qsbr_register_thread(&t[i / 2], enabled_core_ids[i]); + rte_rcu_qsbr_register_thread(&t[i / 2], + enabled_core_ids[i + 1]); + } + + /* Reader threads are launched */ + for (i = 0; i < test_cores / 2; i++) + rte_eal_remote_launch(test_rcu_qsbr_reader, + (void *)(uintptr_t)(i / 2), + enabled_core_ids[i]); + + /* Writer threads are launched */ + for (; i < test_cores; i++) + rte_eal_remote_launch(test_rcu_qsbr_writer, + (void *)(uintptr_t)(i - (test_cores / 2)), + enabled_core_ids[i]); + /* Wait for writers to complete */ + for (i = test_cores / 2; i < test_cores; i++) + rte_eal_wait_lcore(enabled_core_ids[i]); + + writer_done = 1; + /* Wait for readers to complete */ + rte_eal_mp_wait_lcore(); + + /* Check return value from threads */ + for (i = 0; i < test_cores; i++) + if (lcore_config[enabled_core_ids[i]].ret < 0) + goto error; + + for (i = 0; i < num_cores / 4; i++) + rte_hash_free(h[i]); + + rte_free(keys); + + return 0; + +error: + writer_done = 1; + /* Wait until all readers have exited */ + rte_eal_mp_wait_lcore(); + + for (i = 0; i < num_cores / 4; i++) + rte_hash_free(h[i]); + rte_free(keys); + for (j = 0; j < RTE_RCU_MAX_LCORE; j++) + for (i = 0; i < TOTAL_ENTRY; i++) + rte_free(hash_data[j][i]); + + return -1; +} + +static int +test_rcu_qsbr_main(void) +{ + if (get_enabled_cores_mask() != 0) + return -1; + /* Error-checking test cases */ + if (test_rcu_qsbr_register_thread() < 0) + goto test_fail; + + if (test_rcu_qsbr_unregister_thread() < 0) + goto test_fail; + + if (test_rcu_qsbr_start() < 0) + goto test_fail; + + if (test_rcu_qsbr_check() < 0) + goto test_fail; + + if (test_rcu_qsbr_dump() < 0) + goto test_fail; + + /* Functional test cases */ + if (num_cores < 4) { + printf("Test failed! Need 4 or more cores\n"); + goto test_fail; + } + if (test_rcu_qsbr_sw_sv_1qs() < 0) + goto test_fail; + + if (test_rcu_qsbr_sw_sv_1qs_non_blocking() < 0) + goto test_fail; + + if (test_rcu_qsbr_sw_sv_3qs() < 0) + goto test_fail; + + if (test_rcu_qsbr_mw_mv_mqs() < 0) + goto test_fail; + + printf("\n"); + return 0; + +test_fail: + return -1; +} + +REGISTER_TEST_COMMAND(rcu_qsbr_autotest, test_rcu_qsbr_main); diff --git a/test/test/test_rcu_qsbr_perf.c b/test/test/test_rcu_qsbr_perf.c new file mode 100644 index 000000000..51af4961d --- /dev/null +++ b/test/test/test_rcu_qsbr_perf.c @@ -0,0 +1,272 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2018 Arm Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" + +/* Check condition and return an error if true. */ +#define TEST_RCU_MAX_LCORE 128 +uint16_t enabled_core_ids[TEST_RCU_MAX_LCORE]; +uint8_t num_cores; + +#define TOTAL_ENTRY (1024 * 8) +#define COUNTER_VALUE 4096 +uint32_t *hash_data[TEST_RCU_MAX_LCORE][TOTAL_ENTRY]; +uint8_t writer_done; + +static struct rte_rcu_qsbr t; +struct rte_hash *h[TEST_RCU_MAX_LCORE]; +char hash_name[TEST_RCU_MAX_LCORE][8]; +static rte_atomic64_t updates, checks; +static rte_atomic64_t update_cycles, check_cycles; + +/* Scale down results to 1000 operations to support lower + * granularity clocks. + */ +#define RCU_SCALE_DOWN 1000 + +extern int test_rcu_qsbr_get_memsize(void); + +static inline int +get_enabled_cores_mask(void) +{ + uint16_t core_id; + uint32_t max_cores = rte_lcore_count(); + if (max_cores > TEST_RCU_MAX_LCORE) { + printf("Number of cores exceed %d\n", TEST_RCU_MAX_LCORE); + return -1; + } + + core_id = 0; + num_cores = 0; + RTE_LCORE_FOREACH_SLAVE(core_id) { + enabled_core_ids[num_cores] = core_id; + num_cores++; + } + + return 0; +} + +static int +test_rcu_qsbr_reader_perf(void *arg) +{ + bool writer_present = (bool)arg; + uint32_t lcore_id = rte_lcore_id(); + uint64_t loop_cnt = 0; + uint64_t begin, cycles; + + begin = rte_rdtsc_precise(); + + if (writer_present) { + while (!writer_done) { + /* Update quiescent state counter */ + rte_rcu_qsbr_update(&t, lcore_id); + loop_cnt++; + } + } else { + while (loop_cnt < 100000000) { + /* Update quiescent state counter */ + rte_rcu_qsbr_update(&t, lcore_id); + loop_cnt++; + } + } + + cycles = rte_rdtsc_precise() - begin; + rte_atomic64_add(&update_cycles, cycles); + rte_atomic64_add(&updates, loop_cnt); + + /* Unregister before exiting to avoid writer from waiting */ + rte_rcu_qsbr_unregister_thread(&t, lcore_id); + + return 0; +} + +static int +test_rcu_qsbr_writer_perf(void *arg) +{ + bool wait = (bool)arg; + uint64_t token = 0; + uint64_t loop_cnt = 0; + uint64_t begin, cycles; + + begin = rte_rdtsc_precise(); + + do { + /* Start the quiescent state query process */ + if (wait) rte_rcu_qsbr_start(&t, 1, &token); + + /* Check quiescent state status */ + rte_rcu_qsbr_check(&t, token, wait); + loop_cnt++; + } while (loop_cnt < 20000000); + + cycles = rte_rdtsc_precise() - begin; + rte_atomic64_add(&check_cycles, cycles); + rte_atomic64_add(&checks, loop_cnt); + return 0; +} + +/* + * Perf test: Reader/writer + * Single writer, Multiple Readers, Single QS var, Non-Blocking rcu_qsbr_check + */ +static int +test_rcu_qsbr_perf(void) +{ + int i; + writer_done = 0; + + rte_atomic64_clear(&updates); + rte_atomic64_clear(&update_cycles); + rte_atomic64_clear(&checks); + rte_atomic64_clear(&check_cycles); + + printf("\nPerf Test: 6 Readers/1 Writer('wait' in qsbr_check == true)\n"); + + /* QS variable is initialized */ + rte_rcu_qsbr_init(&t); + + /* Register worker threads on 6 cores */ + for (i = 0; i < 6; i++) + rte_rcu_qsbr_register_thread(&t, enabled_core_ids[i]); + + /* Reader threads are launched */ + for (i = 0; i < 6; i++) + rte_eal_remote_launch(test_rcu_qsbr_reader_perf, (void *)1, + enabled_core_ids[i]); + + /* Writer thread is launched */ + rte_eal_remote_launch(test_rcu_qsbr_writer_perf, + (void *)1, enabled_core_ids[i]); + + /* Wait for the writer thread */ + rte_eal_wait_lcore(enabled_core_ids[i]); + writer_done = 1; + + /* Wait until all readers have exited */ + rte_eal_mp_wait_lcore(); + + printf("Total RCU updates = %ld\n", rte_atomic64_read(&updates)); + printf("Cycles per %d updates: %lu\n", RCU_SCALE_DOWN, + rte_atomic64_read(&update_cycles) / + (rte_atomic64_read(&updates) / RCU_SCALE_DOWN)); + printf("Total RCU checks = %ld\n", rte_atomic64_read(&checks)); + printf("Cycles per %d checks: %lu\n", RCU_SCALE_DOWN, + rte_atomic64_read(&check_cycles) / + (rte_atomic64_read(&checks) / RCU_SCALE_DOWN)); + + return 0; +} + +/* + * Perf test: Readers + * Single writer, Multiple readers, Single QS variable + */ +static int +test_rcu_qsbr_rperf(void) +{ + int i; + + rte_atomic64_clear(&updates); + rte_atomic64_clear(&update_cycles); + rte_atomic64_clear(&checks); + rte_atomic64_clear(&check_cycles); + + printf("\nPerf Test: 8 Readers\n"); + + /* QS variable is initialized */ + rte_rcu_qsbr_init(&t); + + /* Register worker threads on 8 cores */ + for (i = 0; i < 8; i++) + rte_rcu_qsbr_register_thread(&t, enabled_core_ids[i]); + + /* Reader threads are launched */ + for (i = 0; i < 8; i++) + rte_eal_remote_launch(test_rcu_qsbr_reader_perf, NULL, + enabled_core_ids[i]); + + /* Wait until all readers have exited */ + rte_eal_mp_wait_lcore(); + + printf("Total RCU updates = %ld\n", rte_atomic64_read(&updates)); + printf("Cycles per %d updates: %lu\n", RCU_SCALE_DOWN, + rte_atomic64_read(&update_cycles) / + (rte_atomic64_read(&updates) / RCU_SCALE_DOWN)); + + return 0; +} + +/* + * Perf test: + * Multiple writer, Single QS variable, Non-blocking rcu_qsbr_check + */ +static int +test_rcu_qsbr_wperf(void) +{ + int i; + + rte_atomic64_clear(&updates); + rte_atomic64_clear(&update_cycles); + rte_atomic64_clear(&checks); + rte_atomic64_clear(&check_cycles); + + printf("\nPerf test: 8 Writers ('wait' in qsbr_check == false)\n"); + + /* QS variable is initialized */ + rte_rcu_qsbr_init(&t); + + /* Writer threads are launched */ + for (i = 0; i < 8; i++) + rte_eal_remote_launch(test_rcu_qsbr_writer_perf, + (void *)0, enabled_core_ids[i]); + + /* Wait until all readers have exited */ + rte_eal_mp_wait_lcore(); + + printf("Total RCU checks = %ld\n", rte_atomic64_read(&checks)); + printf("Cycles per %d checks: %lu\n", RCU_SCALE_DOWN, + rte_atomic64_read(&check_cycles) / + (rte_atomic64_read(&checks) / RCU_SCALE_DOWN)); + + return 0; +} + +static int +test_rcu_qsbr_main(void) +{ + rte_atomic64_init(&updates); + rte_atomic64_init(&update_cycles); + rte_atomic64_init(&checks); + rte_atomic64_init(&check_cycles); + + if (get_enabled_cores_mask() != 0) + return -1; + + if (test_rcu_qsbr_perf() < 0) + goto test_fail; + + if (test_rcu_qsbr_rperf() < 0) + goto test_fail; + + if (test_rcu_qsbr_wperf() < 0) + goto test_fail; + + printf("\n"); + return 0; + +test_fail: + return -1; +} + +REGISTER_TEST_COMMAND(rcu_qsbr_perf_autotest, test_rcu_qsbr_main);