summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAdrian Moreno <amorenoz@redhat.com>2023-01-10 09:32:01 +0100
committerIlya Maximets <i.maximets@ovn.org>2023-01-11 17:44:55 +0100
commitbd14aa31e39b55d12a366c2c7a627358112f3fca (patch)
treef5c7b38c1c1b415ea3e0c64083f452b1f8163a28 /tests
parente5d92c1a54852e9b5912aa53417d1f64bfee9af2 (diff)
downloadopenvswitch-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.mk1
-rw-r--r--tests/library.at5
-rw-r--r--tests/test-rculist.c235
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);