Migrate robust_list test to the kselftest harness framework, removing mixed legacy ksft_* API usages and passing test metadata to cloned child functions via child_args struct.
Also, fix an out-of-bounds waitpid bug during multithreaded tests. Signed-off-by: Wake Liu <[email protected]> --- .../selftests/futex/functional/robust_list.c | 166 +++++++++--------- 1 file changed, 85 insertions(+), 81 deletions(-) diff --git a/tools/testing/selftests/futex/functional/robust_list.c b/tools/testing/selftests/futex/functional/robust_list.c index e7d1254e18ca..d0b461962586 100644 --- a/tools/testing/selftests/futex/functional/robust_list.c +++ b/tools/testing/selftests/futex/functional/robust_list.c @@ -25,7 +25,7 @@ #define _GNU_SOURCE #include "futextest.h" -#include "../../kselftest_harness.h" +#include "kselftest_harness.h" #include <errno.h> #include <pthread.h> @@ -35,11 +35,11 @@ #include <stddef.h> #include <sys/mman.h> #include <sys/wait.h> +#include <stdlib.h> +#include <string.h> #define STACK_SIZE (1024 * 1024) - #define FUTEX_TIMEOUT 3 - #define SLEEP_US 100 static pthread_barrier_t barrier, barrier2; @@ -58,30 +58,46 @@ static int get_robust_list(int pid, struct robust_list_head **head, size_t *len_ * Basic lock struct, contains just the futex word and the robust list element * Real implementations have also a *prev to easily walk in the list */ +typedef _Atomic(unsigned int) atomic_futex_t; + struct lock_struct { - _Atomic(unsigned int) futex; + atomic_futex_t futex; struct robust_list list; }; +struct child_args { + struct __test_metadata *_metadata; + void *arg; +}; + /* * Helper function to spawn a child thread. Returns -1 on error, pid on success */ -static int create_child(int (*fn)(void *arg), void *arg) +static int create_child(struct __test_metadata *_metadata, int (*fn)(void *arg), void *arg) { char *stack; pid_t pid; + struct child_args *cargs = malloc(sizeof(*cargs)); + + if (!cargs) + return -1; + cargs->_metadata = _metadata; + cargs->arg = arg; stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); - if (stack == MAP_FAILED) + if (stack == MAP_FAILED) { + free(cargs); return -1; + } stack += STACK_SIZE; - pid = clone(fn, stack, CLONE_VM | SIGCHLD, arg); - - if (pid == -1) + pid = clone(fn, stack, CLONE_VM | SIGCHLD, cargs); + if (pid == -1) { + free(cargs); return -1; + } return pid; } @@ -110,7 +126,7 @@ static int set_list(struct robust_list_head *head) */ static int mutex_lock(struct lock_struct *lock, struct robust_list_head *head, bool error_inject) { - _Atomic(unsigned int) *futex = &lock->futex; + atomic_futex_t *futex = &lock->futex; unsigned int zero = 0; pid_t tid = gettid(); int ret = -1; @@ -170,21 +186,19 @@ static int mutex_lock(struct lock_struct *lock, struct robust_list_head *head, b */ static int child_fn_lock(void *arg) { - struct lock_struct *lock = arg; + struct child_args *cargs = arg; + struct __test_metadata *_metadata = cargs->_metadata; + struct lock_struct *lock = cargs->arg; struct robust_list_head head; int ret; + free(cargs); + ret = set_list(&head); - if (ret) { - ksft_test_result_fail("set_robust_list error\n"); - return ret; - } + ASSERT_EQ(ret, 0) TH_LOG("set_robust_list error"); ret = mutex_lock(lock, &head, false); - if (ret) { - ksft_test_result_fail("mutex_lock error\n"); - return ret; - } + ASSERT_EQ(ret, 0) TH_LOG("mutex_lock error"); pthread_barrier_wait(&barrier); @@ -207,7 +221,7 @@ static int child_fn_lock(void *arg) TEST(test_robustness) { struct lock_struct lock = { .futex = 0 }; - _Atomic(unsigned int) *futex = &lock.futex; + atomic_futex_t *futex = &lock.futex; struct robust_list_head head; int ret, pid, wstatus; @@ -221,7 +235,7 @@ TEST(test_robustness) ret = pthread_barrier_init(&barrier, NULL, 2); ASSERT_EQ(ret, 0); - pid = create_child(&child_fn_lock, &lock); + pid = create_child(_metadata, &child_fn_lock, &lock); ASSERT_NE(pid, -1); pthread_barrier_wait(&barrier); @@ -238,9 +252,7 @@ TEST(test_robustness) wait(&wstatus); pthread_barrier_destroy(&barrier); - /* Pass only if the child hasn't return error */ - if (!WEXITSTATUS(wstatus)) - ksft_test_result_pass("%s\n", __func__); + EXPECT_EQ(WEXITSTATUS(wstatus), 0) TH_LOG("child failed"); } /* @@ -266,8 +278,6 @@ TEST(test_set_robust_list_invalid_size) ret = set_robust_list(&head, 0); ASSERT_EQ(ret, -1); ASSERT_EQ(errno, EINVAL); - - ksft_test_result_pass("%s\n", __func__); } /* @@ -294,20 +304,19 @@ TEST(test_get_robust_list_self) ASSERT_EQ(ret, 0); ASSERT_EQ(get_head, &head2); ASSERT_EQ(head_size, len_ptr); - - ksft_test_result_pass("%s\n", __func__); } static int child_list(void *arg) { - struct robust_list_head *head = arg; + struct child_args *cargs = arg; + struct __test_metadata *_metadata = cargs->_metadata; + struct robust_list_head *head = cargs->arg; int ret; + free(cargs); + ret = set_robust_list(head, sizeof(*head)); - if (ret) { - ksft_test_result_fail("set_robust_list error\n"); - return -1; - } + ASSERT_EQ(ret, 0) TH_LOG("set_robust_list error"); /* * After setting the list head, wait until the main thread can call @@ -337,7 +346,7 @@ TEST(test_get_robust_list_child) ret = pthread_barrier_init(&barrier2, NULL, 2); ASSERT_EQ(ret, 0); - tid = create_child(&child_list, &head); + tid = create_child(_metadata, &child_list, &head); ASSERT_NE(tid, -1); pthread_barrier_wait(&barrier); @@ -352,28 +361,24 @@ TEST(test_get_robust_list_child) pthread_barrier_destroy(&barrier); pthread_barrier_destroy(&barrier2); - /* Pass only if the child hasn't return error */ - if (!WEXITSTATUS(wstatus)) - ksft_test_result_pass("%s\n", __func__); + EXPECT_EQ(WEXITSTATUS(wstatus), 0) TH_LOG("child failed"); } static int child_fn_lock_with_error(void *arg) { - struct lock_struct *lock = arg; + struct child_args *cargs = arg; + struct __test_metadata *_metadata = cargs->_metadata; + struct lock_struct *lock = cargs->arg; struct robust_list_head head; int ret; + free(cargs); + ret = set_list(&head); - if (ret) { - ksft_test_result_fail("set_robust_list error\n"); - return -1; - } + ASSERT_EQ(ret, 0) TH_LOG("set_robust_list error"); ret = mutex_lock(lock, &head, true); - if (ret) { - ksft_test_result_fail("mutex_lock error\n"); - return -1; - } + ASSERT_EQ(ret, 0) TH_LOG("mutex_lock error"); pthread_barrier_wait(&barrier); @@ -391,7 +396,7 @@ static int child_fn_lock_with_error(void *arg) TEST(test_set_list_op_pending) { struct lock_struct lock = { .futex = 0 }; - _Atomic(unsigned int) *futex = &lock.futex; + atomic_futex_t *futex = &lock.futex; struct robust_list_head head; int ret, wstatus; @@ -401,7 +406,7 @@ TEST(test_set_list_op_pending) ret = pthread_barrier_init(&barrier, NULL, 2); ASSERT_EQ(ret, 0); - ret = create_child(&child_fn_lock_with_error, &lock); + ret = create_child(_metadata, &child_fn_lock_with_error, &lock); ASSERT_NE(ret, -1); pthread_barrier_wait(&barrier); @@ -414,21 +419,21 @@ TEST(test_set_list_op_pending) wait(&wstatus); pthread_barrier_destroy(&barrier); - /* Pass only if the child hasn't return error */ - if (!WEXITSTATUS(wstatus)) - ksft_test_result_pass("%s\n", __func__); - else - ksft_test_result_fail("%s\n", __func__); + EXPECT_EQ(WEXITSTATUS(wstatus), 0) TH_LOG("child failed"); } #define CHILD_NR 10 static int child_lock_holder(void *arg) { - struct lock_struct *locks = arg; + struct child_args *cargs = arg; + struct __test_metadata *_metadata = cargs->_metadata; + struct lock_struct *locks = cargs->arg; struct robust_list_head head; int i; + free(cargs); + set_list(&head); for (i = 0; i < CHILD_NR; i++) { @@ -447,22 +452,20 @@ static int child_lock_holder(void *arg) static int child_wait_lock(void *arg) { - struct lock_struct *lock = arg; + struct child_args *cargs = arg; + struct __test_metadata *_metadata = cargs->_metadata; + struct lock_struct *lock = cargs->arg; struct robust_list_head head; int ret; + free(cargs); + pthread_barrier_wait(&barrier2); ret = mutex_lock(lock, &head, false); + ASSERT_EQ(ret, 0) TH_LOG("mutex_lock error"); - if (ret) { - ksft_test_result_fail("mutex_lock error\n"); - return -1; - } - - if (!(lock->futex & FUTEX_OWNER_DIED)) { - ksft_test_result_fail("futex not marked with FUTEX_OWNER_DIED\n"); - return -1; - } + ASSERT_TRUE(lock->futex & FUTEX_OWNER_DIED) + TH_LOG("futex not marked with FUTEX_OWNER_DIED"); return 0; } @@ -482,18 +485,20 @@ TEST(test_robust_list_multiple_elements) ret = pthread_barrier_init(&barrier2, NULL, CHILD_NR + 1); ASSERT_EQ(ret, 0); - pids[0] = create_child(&child_lock_holder, &locks); + pids[0] = create_child(_metadata, &child_lock_holder, &locks); + ASSERT_NE(pids[0], -1); /* Wait until the locker thread takes the look */ pthread_barrier_wait(&barrier); - for (i = 0; i < CHILD_NR; i++) - pids[i+1] = create_child(&child_wait_lock, &locks[i]); + for (i = 0; i < CHILD_NR; i++) { + pids[i+1] = create_child(_metadata, &child_wait_lock, &locks[i]); + ASSERT_NE(pids[i+1], -1); + } - /* Wait for all children to return */ + /* Wait for all children to return (holder + all waiters) */ ret = 0; - - for (i = 0; i < CHILD_NR; i++) { + for (i = 0; i < CHILD_NR + 1; i++) { waitpid(pids[i], &wstatus, 0); if (WEXITSTATUS(wstatus)) ret = -1; @@ -502,22 +507,21 @@ TEST(test_robust_list_multiple_elements) pthread_barrier_destroy(&barrier); pthread_barrier_destroy(&barrier2); - /* Pass only if the child hasn't return error */ - if (!ret) - ksft_test_result_pass("%s\n", __func__); + EXPECT_EQ(ret, 0) TH_LOG("One or more children failed"); } static int child_circular_list(void *arg) { + struct child_args *cargs = arg; + struct __test_metadata *_metadata = cargs->_metadata; static struct robust_list_head head; struct lock_struct a, b, c; int ret; + free(cargs); + ret = set_list(&head); - if (ret) { - ksft_test_result_fail("set_list error\n"); - return -1; - } + ASSERT_EQ(ret, 0) TH_LOG("set_list error"); head.list.next = &a.list; @@ -539,14 +543,14 @@ static int child_circular_list(void *arg) TEST(test_circular_list) { int wstatus; + pid_t pid; - create_child(child_circular_list, NULL); + pid = create_child(_metadata, child_circular_list, NULL); + ASSERT_NE(pid, -1); wait(&wstatus); - /* Pass only if the child hasn't return error */ - if (!WEXITSTATUS(wstatus)) - ksft_test_result_pass("%s\n", __func__); + EXPECT_EQ(WEXITSTATUS(wstatus), 0) TH_LOG("child failed"); } TEST_HARNESS_MAIN -- 2.54.0.823.g6e5bcc1fc9-goog

