diff options
author | Adrian Moreno <amorenoz@redhat.com> | 2023-01-10 09:32:01 +0100 |
---|---|---|
committer | Ilya Maximets <i.maximets@ovn.org> | 2023-01-11 17:44:55 +0100 |
commit | bd14aa31e39b55d12a366c2c7a627358112f3fca (patch) | |
tree | f5c7b38c1c1b415ea3e0c64083f452b1f8163a28 /tests | |
parent | e5d92c1a54852e9b5912aa53417d1f64bfee9af2 (diff) | |
download | openvswitch-bd14aa31e39b55d12a366c2c7a627358112f3fca.tar.gz |
tests: Add unit tests to rculist.
Low test coverage on this area caused some errors to remain unnoticed.
Add basic functional test of rculist.
Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/automake.mk | 1 | ||||
-rw-r--r-- | tests/library.at | 5 | ||||
-rw-r--r-- | tests/test-rculist.c | 235 |
3 files changed, 241 insertions, 0 deletions
diff --git a/tests/automake.mk b/tests/automake.mk index 4091a2796..c8de3fe28 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -476,6 +476,7 @@ tests_ovstest_SOURCES = \ tests/test-packets.c \ tests/test-random.c \ tests/test-rcu.c \ + tests/test-rculist.c \ tests/test-reconnect.c \ tests/test-rstp.c \ tests/test-sflow.c \ diff --git a/tests/library.at b/tests/library.at index bafb28277..164ae789d 100644 --- a/tests/library.at +++ b/tests/library.at @@ -27,6 +27,11 @@ AT_CHECK([ovstest test-hindex], [0], [..................... ]) AT_CLEANUP +AT_SETUP([test rcu linked lists]) +AT_CHECK([ovstest test-rculist], [0], [..... +]) +AT_CLEANUP + AT_SETUP([cuckoo hash]) AT_KEYWORDS([cmap]) AT_CHECK([ovstest test-cmap check 1], [0], [... diff --git a/tests/test-rculist.c b/tests/test-rculist.c new file mode 100644 index 000000000..07a6338b8 --- /dev/null +++ b/tests/test-rculist.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2023 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#undef NDEBUG +#include <unistd.h> + +#include "openvswitch/list.h" +#include "ovstest.h" +#include "ovs-thread.h" +#include "random.h" +#include "rculist.h" +#include "util.h" + +enum { MAX_ELEMS = 10, MAX_CHECKS = 200 }; + +/* Sample list element. */ +struct element { + int value; + struct rculist node; +}; + +static void +do_usleep(unsigned int usecs) +{ +#ifdef _WIN32 + Sleep(MAX(usecs / 1000, 1)); +#else + usleep(usecs); +#endif +} + +/* Continuously check the integrity of the list until it's empty. */ +static void * +checker_main(void *aux) +{ + struct rculist *list = (struct rculist *) aux; + struct element *elem; + bool checked = false; + + for (int i = 0; i < MAX_CHECKS; i++) { + int value = -1; + + RCULIST_FOR_EACH (elem, node, list) { + ovs_assert(value <= elem->value); + ovs_assert(elem->value < MAX_ELEMS); + value = elem->value; + if (!checked) { + checked = true; + } + do_usleep(10); + } + + ovsrcu_quiesce(); + + if (checked && rculist_is_empty(list)) { + break; + } + } + return NULL; +} + +/* Run test while a thread checks the integrity of the list. + * Tests must end up emptying the list. */ +static void +run_test_while_checking(void (*function)(struct rculist *list)) +{ + struct rculist list; + pthread_t checker; + + rculist_init(&list); + + checker = ovs_thread_create("checker", checker_main, &list); + function(&list); + + ovs_assert(rculist_is_empty(&list)); + ovsrcu_quiesce(); + xpthread_join(checker, NULL); + printf("."); +} + +static void +test_rculist_insert_delete__(struct rculist *list, bool long_version) +{ + struct element *elem; + int value; + + for (int i = 1; i < MAX_ELEMS; i++) { + elem = xmalloc(sizeof *elem); + elem->value = i; + rculist_insert(list, &elem->node); + /* Leave some time for checkers to iterate through. */ + do_usleep(random_range(1000)); + } + + ovsrcu_quiesce(); + + value = MAX_ELEMS; + RCULIST_FOR_EACH_REVERSE_PROTECTED (elem, node, list) { + ovs_assert (elem->value <= value); + value = elem->value; + } + + if (long_version) { + struct element *next; + RCULIST_FOR_EACH_SAFE_PROTECTED (elem, next, node, list) { + rculist_remove(&elem->node); + ovsrcu_postpone(free, elem); + /* Leave some time for checkers to iterate through. */ + do_usleep(random_range(1000)); + } + } else { + RCULIST_FOR_EACH_SAFE_PROTECTED (elem, node, list) { + rculist_remove(&elem->node); + ovsrcu_postpone(free, elem); + /* Leave some time for checkers to iterate through. */ + do_usleep(random_range(1000)); + } + } +} + +static void +test_rculist_insert_delete(struct rculist *list) +{ + test_rculist_insert_delete__(list, false); +} + +static void +test_rculist_insert_delete_long(struct rculist *list) +{ + test_rculist_insert_delete__(list, true); +} + +static void +test_rculist_push_front_pop_back(struct rculist *list) +{ + struct element *elem; + + for (int i = MAX_ELEMS - 1; i > 0; i--) { + elem = xmalloc(sizeof *elem); + elem->value = i; + rculist_push_front(list, &elem->node); + /* Leave some time for checkers to iterate through. */ + do_usleep(random_range(1000)); + } + + ovsrcu_quiesce(); + + while (!rculist_is_empty(list)) { + elem = CONTAINER_OF(rculist_pop_back(list), struct element, node); + ovsrcu_postpone(free, elem); + /* Leave some time for checkers to iterate through. */ + do_usleep(random_range(1000)); + } +} + +static void +test_rculist_push_back_pop_front(struct rculist *list) +{ + struct element *elem; + + for (int i = 0; i < MAX_ELEMS; i++) { + elem = xmalloc(sizeof *elem); + elem->value = i; + rculist_push_back(list, &elem->node); + /* Leave some time for checkers to iterate through. */ + do_usleep(random_range(1000)); + } + + ovsrcu_quiesce(); + + while (!rculist_is_empty(list)) { + elem = CONTAINER_OF(rculist_pop_front(list), struct element, node); + ovsrcu_postpone(free, elem); + /* Leave some time for checkers to iterate through. */ + do_usleep(random_range(1000)); + } +} + +static void +test_rculist_splice(struct rculist *list) +{ + struct element *elem; + struct rculist other; + + rculist_init(&other); + + /* Insert elements in list by splicing an intermediate rculist. */ + for (int i = 0; i < MAX_ELEMS; i++) { + elem = xmalloc(sizeof *elem); + elem->value = i; + rculist_insert(&other, &elem->node); + rculist_splice_hidden(list, rculist_next_protected(&other), &other); + rculist_init(&other); + /* Leave some time for checkers to iterate through. */ + do_usleep(random_range(1000)); + } + + ovsrcu_quiesce(); + + ovs_assert(rculist_size(list) == MAX_ELEMS); + ovs_assert(rculist_is_empty(&other)); + while (!rculist_is_empty(list)) { + elem = CONTAINER_OF(rculist_pop_front(list), struct element, node); + ovsrcu_postpone(free, elem); + /* Leave some time for checkers to iterate through. */ + do_usleep(random_range(1000)); + } +} + +static void +test_rculist_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + run_test_while_checking(test_rculist_insert_delete); + run_test_while_checking(test_rculist_insert_delete_long); + run_test_while_checking(test_rculist_push_back_pop_front); + run_test_while_checking(test_rculist_push_front_pop_back); + run_test_while_checking(test_rculist_splice); + printf("\n"); +} + +OVSTEST_REGISTER("test-rculist", test_rculist_main); |