From efbcdaa55b9305bb3f0e799696c75a621ed60264 Mon Sep 17 00:00:00 2001 From: Joyce Kong Date: Mon, 25 Mar 2019 19:11:09 +0800 Subject: [PATCH] test/ticketlock: add test cases Add test cases for ticket lock, recursive ticket lock, and ticket lock performance. Signed-off-by: Joyce Kong Reviewed-by: Gavin Hu Reviewed-by: Phil Yang Acked-by: Konstantin Ananyev --- MAINTAINERS | 1 + app/test/Makefile | 1 + app/test/autotest_data.py | 6 + app/test/meson.build | 1 + app/test/test_ticketlock.c | 319 +++++++++++++++++++++++++++++++++++++ 5 files changed, 328 insertions(+) create mode 100644 app/test/test_ticketlock.c diff --git a/MAINTAINERS b/MAINTAINERS index eaf0c3d158..e9ff2b4c22 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -219,6 +219,7 @@ F: app/test/test_bitmap.c Ticketlock M: Joyce Kong F: lib/librte_eal/common/include/generic/rte_ticketlock.h +F: app/test/test_ticketlock.c ARM v7 M: Jan Viktorin diff --git a/app/test/Makefile b/app/test/Makefile index 89949c2bb9..d6aa28bad9 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -65,6 +65,7 @@ SRCS-y += test_barrier.c SRCS-y += test_malloc.c SRCS-y += test_cycles.c SRCS-y += test_spinlock.c +SRCS-y += test_ticketlock.c SRCS-y += test_memory.c SRCS-y += test_memzone.c SRCS-y += test_bitmap.c diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py index 5f87bb94dd..db2527489e 100644 --- a/app/test/autotest_data.py +++ b/app/test/autotest_data.py @@ -170,6 +170,12 @@ parallel_test_list = [ "Func": spinlock_autotest, "Report": None, }, + { + "Name": "Ticketlock autotest", + "Command": "ticketlock_autotest", + "Func": ticketlock_autotest, + "Report": None, + } { "Name": "Byte order autotest", "Command": "byteorder_autotest", diff --git a/app/test/meson.build b/app/test/meson.build index 05e5ddeb09..ddb4d09ae0 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -107,6 +107,7 @@ test_sources = files('commands.c', 'test_timer.c', 'test_timer_perf.c', 'test_timer_racecond.c', + 'test_ticketlock.c', 'test_version.c', 'virtual_pmd.c' ) diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c new file mode 100644 index 0000000000..13bdadbcb1 --- /dev/null +++ b/app/test/test_ticketlock.c @@ -0,0 +1,319 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Arm Limited + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" + +/* + * Ticketlock test + * ============= + * + * - There is a global ticketlock and a table of ticketlocks (one per lcore). + * + * - The test function takes all of these locks and launches the + * ``test_ticketlock_per_core()`` function on each core (except the master). + * + * - The function takes the global lock, display something, then releases + * the global lock. + * - The function takes the per-lcore lock, display something, then releases + * the per-core lock. + * + * - The main function unlocks the per-lcore locks sequentially and + * waits between each lock. This triggers the display of a message + * for each core, in the correct order. The autotest script checks that + * this order is correct. + * + * - A load test is carried out, with all cores attempting to lock a single lock + * multiple times + */ + +static rte_ticketlock_t tl, tl_try; +static rte_ticketlock_t tl_tab[RTE_MAX_LCORE]; +static rte_ticketlock_recursive_t tlr; +static unsigned int count; + +static rte_atomic32_t synchro; + +static int +test_ticketlock_per_core(__attribute__((unused)) void *arg) +{ + rte_ticketlock_lock(&tl); + printf("Global lock taken on core %u\n", rte_lcore_id()); + rte_ticketlock_unlock(&tl); + + rte_ticketlock_lock(&tl_tab[rte_lcore_id()]); + printf("Hello from core %u !\n", rte_lcore_id()); + rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]); + + return 0; +} + +static int +test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg) +{ + unsigned int id = rte_lcore_id(); + + rte_ticketlock_recursive_lock(&tlr); + printf("Global recursive lock taken on core %u - count = %d\n", + id, tlr.count); + rte_ticketlock_recursive_lock(&tlr); + printf("Global recursive lock taken on core %u - count = %d\n", + id, tlr.count); + rte_ticketlock_recursive_lock(&tlr); + printf("Global recursive lock taken on core %u - count = %d\n", + id, tlr.count); + + printf("Hello from within recursive locks from core %u !\n", id); + + rte_ticketlock_recursive_unlock(&tlr); + printf("Global recursive lock released on core %u - count = %d\n", + id, tlr.count); + rte_ticketlock_recursive_unlock(&tlr); + printf("Global recursive lock released on core %u - count = %d\n", + id, tlr.count); + rte_ticketlock_recursive_unlock(&tlr); + printf("Global recursive lock released on core %u - count = %d\n", + id, tlr.count); + + return 0; +} + +static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER; +static uint64_t lcount __rte_cache_aligned; +static uint64_t lcore_count[RTE_MAX_LCORE] __rte_cache_aligned; +static uint64_t time_cost[RTE_MAX_LCORE]; + +#define MAX_LOOP 10000 + +static int +load_loop_fn(void *func_param) +{ + uint64_t time_diff = 0, begin; + uint64_t hz = rte_get_timer_hz(); + const int use_lock = *(int *)func_param; + const unsigned int lcore = rte_lcore_id(); + + /* wait synchro for slaves */ + if (lcore != rte_get_master_lcore()) + while (rte_atomic32_read(&synchro) == 0) + ; + + begin = rte_rdtsc_precise(); + while (lcore_count[lcore] < MAX_LOOP) { + if (use_lock) + rte_ticketlock_lock(&lk); + lcore_count[lcore]++; + lcount++; + if (use_lock) + rte_ticketlock_unlock(&lk); + } + time_diff = rte_rdtsc_precise() - begin; + time_cost[lcore] = time_diff * 1000000 / hz; + return 0; +} + +static int +test_ticketlock_perf(void) +{ + unsigned int i; + uint64_t tcount = 0; + uint64_t total_time = 0; + int lock = 0; + const unsigned int lcore = rte_lcore_id(); + + printf("\nTest with no lock on single core...\n"); + load_loop_fn(&lock); + printf("Core [%u] cost time = %"PRIu64" us\n", lcore, time_cost[lcore]); + memset(lcore_count, 0, sizeof(lcore_count)); + memset(time_cost, 0, sizeof(time_cost)); + + printf("\nTest with lock on single core...\n"); + lock = 1; + load_loop_fn(&lock); + printf("Core [%u] cost time = %"PRIu64" us\n", lcore, time_cost[lcore]); + memset(lcore_count, 0, sizeof(lcore_count)); + memset(time_cost, 0, sizeof(time_cost)); + + lcount = 0; + printf("\nTest with lock on %u cores...\n", rte_lcore_count()); + + /* Clear synchro and start slaves */ + rte_atomic32_set(&synchro, 0); + rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER); + + /* start synchro and launch test on master */ + rte_atomic32_set(&synchro, 1); + load_loop_fn(&lock); + + rte_eal_mp_wait_lcore(); + + RTE_LCORE_FOREACH(i) { + printf("Core [%u] cost time = %"PRIu64" us\n", i, time_cost[i]); + tcount += lcore_count[i]; + total_time += time_cost[i]; + } + + if (tcount != lcount) + return -1; + + printf("Total cost time = %"PRIu64" us\n", total_time); + + return 0; +} + +/* + * Use rte_ticketlock_trylock() to trylock a ticketlock object, + * If it could not lock the object successfully, it would + * return immediately and the variable of "count" would be + * increased by one per times. the value of "count" could be + * checked as the result later. + */ +static int +test_ticketlock_try(__attribute__((unused)) void *arg) +{ + if (rte_ticketlock_trylock(&tl_try) == 0) { + rte_ticketlock_lock(&tl); + count++; + rte_ticketlock_unlock(&tl); + } + + return 0; +} + + +/* + * Test rte_eal_get_lcore_state() in addition to ticketlocks + * as we have "waiting" then "running" lcores. + */ +static int +test_ticketlock(void) +{ + int ret = 0; + int i; + + /* slave cores should be waiting: print it */ + RTE_LCORE_FOREACH_SLAVE(i) { + printf("lcore %d state: %d\n", i, + (int) rte_eal_get_lcore_state(i)); + } + + rte_ticketlock_init(&tl); + rte_ticketlock_init(&tl_try); + rte_ticketlock_recursive_init(&tlr); + RTE_LCORE_FOREACH_SLAVE(i) { + rte_ticketlock_init(&tl_tab[i]); + } + + rte_ticketlock_lock(&tl); + + RTE_LCORE_FOREACH_SLAVE(i) { + rte_ticketlock_lock(&tl_tab[i]); + rte_eal_remote_launch(test_ticketlock_per_core, NULL, i); + } + + /* slave cores should be busy: print it */ + RTE_LCORE_FOREACH_SLAVE(i) { + printf("lcore %d state: %d\n", i, + (int) rte_eal_get_lcore_state(i)); + } + rte_ticketlock_unlock(&tl); + + RTE_LCORE_FOREACH_SLAVE(i) { + rte_ticketlock_unlock(&tl_tab[i]); + rte_delay_ms(10); + } + + rte_eal_mp_wait_lcore(); + + rte_ticketlock_recursive_lock(&tlr); + + /* + * Try to acquire a lock that we already own + */ + if (!rte_ticketlock_recursive_trylock(&tlr)) { + printf("rte_ticketlock_recursive_trylock failed on a lock that " + "we already own\n"); + ret = -1; + } else + rte_ticketlock_recursive_unlock(&tlr); + + RTE_LCORE_FOREACH_SLAVE(i) { + rte_eal_remote_launch(test_ticketlock_recursive_per_core, + NULL, i); + } + rte_ticketlock_recursive_unlock(&tlr); + rte_eal_mp_wait_lcore(); + + /* + * Test if it could return immediately from try-locking a locked object. + * Here it will lock the ticketlock object first, then launch all the + * slave lcores to trylock the same ticketlock object. + * All the slave lcores should give up try-locking a locked object and + * return immediately, and then increase the "count" initialized with + * zero by one per times. + * We can check if the "count" is finally equal to the number of all + * slave lcores to see if the behavior of try-locking a locked + * ticketlock object is correct. + */ + if (rte_ticketlock_trylock(&tl_try) == 0) + return -1; + + count = 0; + RTE_LCORE_FOREACH_SLAVE(i) { + rte_eal_remote_launch(test_ticketlock_try, NULL, i); + } + rte_eal_mp_wait_lcore(); + rte_ticketlock_unlock(&tl_try); + if (rte_ticketlock_is_locked(&tl)) { + printf("ticketlock is locked but it should not be\n"); + return -1; + } + rte_ticketlock_lock(&tl); + if (count != (rte_lcore_count() - 1)) + ret = -1; + + rte_ticketlock_unlock(&tl); + + /* + * Test if it can trylock recursively. + * Use rte_ticketlock_recursive_trylock() to check if it can lock + * a ticketlock object recursively. Here it will try to lock a + * ticketlock object twice. + */ + if (rte_ticketlock_recursive_trylock(&tlr) == 0) { + printf("It failed to do the first ticketlock_recursive_trylock " + "but it should able to do\n"); + return -1; + } + if (rte_ticketlock_recursive_trylock(&tlr) == 0) { + printf("It failed to do the second ticketlock_recursive_trylock " + "but it should able to do\n"); + return -1; + } + rte_ticketlock_recursive_unlock(&tlr); + rte_ticketlock_recursive_unlock(&tlr); + + if (test_ticketlock_perf() < 0) + return -1; + + return ret; +} + +REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock); -- 2.20.1