On Mon, Dec 5, 2022 at 3:41 AM Adrian Moreno <[email protected]> wrote:
>
> Low test coverage on this area caused some errors to remain unnoticed.
> Add basic functional test of rculist.
>
> Signed-off-by: Adrian Moreno <[email protected]>

Looks good! Triggers a warning if the preceding patch hasn't been
applied as intended.

Acked-by: Mike Pattrick <[email protected]>

> ---
>  tests/automake.mk    |   1 +
>  tests/library.at     |   5 ++
>  tests/test-rculist.c | 205 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 211 insertions(+)
>  create mode 100644 tests/test-rculist.c
>
> diff --git a/tests/automake.mk b/tests/automake.mk
> index d509cf935..88f97b8b7 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -474,6 +474,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..49fe434ff
> --- /dev/null
> +++ b/tests/test-rculist.c
> @@ -0,0 +1,205 @@
> +#include <config.h>
> +#undef NDEBUG
> +#include <unistd.h>
> +
> +#include "ovstest.h"
> +#include "rculist.h"
> +#include "openvswitch/list.h"
> +#include "ovs-thread.h"
> +#include "random.h"
> +#include "util.h"
> +
> +enum { MAX_ELEMS = 10, MAX_CHECKS = 200 };
> +
> +/* Sample list element. */
> +struct element {
> +    int value;
> +    struct rculist node;
> +};
> +
> +/* Continuously check the integrity of the list until it's empty. */
> +static void *
> +checker_main(void *aux)
> +{
> +    struct element *elem;
> +    struct rculist *list = (struct rculist *) aux;
> +    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;
> +            }
> +            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. */
> +        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. */
> +            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. */
> +            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. */
> +        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. */
> +        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. */
> +        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. */
> +        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. */
> +        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. */
> +        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);
> --
> 2.38.1
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to