diff options
| author | Ingo Molnar <mingo@kernel.org> | 2026-07-26 10:42:11 +0200 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2026-07-26 10:42:12 +0200 |
| commit | 2906e2925afd666d9434d1506746a81606e736d0 (patch) | |
| tree | c1ccba89766ce125335c5cba5d76ddacbe7f9734 /tools/testing | |
| parent | 66958e251d167c6531bfecc46f460b7d8fb01d73 (diff) | |
| parent | d1c656df36d00aad9c810e563f918dbbaf41a7b9 (diff) | |
| download | linux-next-2906e2925afd666d9434d1506746a81606e736d0.tar.gz linux-next-2906e2925afd666d9434d1506746a81606e736d0.zip | |
Merge branch into tip/master: 'locking/futex'
# New commits in locking/futex:
d1c656df36d0 ("selftests/futex: Give circular-list nodes static storage")
23dd3f884926 ("selftests/futex: Use thread synchronization helpers instead of usleep()")
6c4a1b972643 ("selftests/futex: Provide thread creation and synchronization helpers")
50c121e5a57a ("selftests/futex: Dynamically skip unsupported tests")
90a0286c7d35 ("selftests/futex: Add FUTEX_LOCK_PI owner-exiting coverage")
553bd67a9032 ("selftests/futex: Migrate robust_list to harness")
4f22ba7eee3e ("selftests/futex: Migrate futex_priv_hash to harness")
9b19fbb3a6c5 ("selftests/futex: Migrate futex_numa_mpol to harness")
7fe733f11215 ("selftests/futex: Migrate futex_requeue_pi_signal_restart to harness")
4559deb73828 ("selftests/futex: Migrate futex_requeue_pi_mismatched_ops to harness")
b7d837c2d49e ("selftests/futex: Migrate futex_requeue_pi to harness")
d0f7df9bb778 ("selftests/futex: Migrate futex_requeue to harness")
78834d8b9c99 ("selftests/futex: Migrate futex_wait_uninitialized_heap to harness")
dccef66d850b ("selftests/futex: Migrate futex_wait_private_mapped_file to harness")
0d65d1abb5e4 ("selftests/futex: Migrate futex_wait to harness")
a894f6f40332 ("selftests/futex: Correct validation logic in waitv")
e531301dd8fa ("selftests/futex: Migrate functional tests to harness")
dfa2f2378fb1 ("selftests/futex: Remove static keyword from 'head'")
4903ab0c83f0 ("futex: Remove unnecessary NULL check before kvfree()")
d7b2769f8dba ("selftests/rseq: Replace glibc-specific __GNUC_PREREQ with portable check")
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/testing')
20 files changed, 1096 insertions, 606 deletions
diff --git a/tools/testing/selftests/futex/functional/.gitignore b/tools/testing/selftests/futex/functional/.gitignore index 23b9fea8d190..7c39d10b38e4 100644 --- a/tools/testing/selftests/futex/functional/.gitignore +++ b/tools/testing/selftests/futex/functional/.gitignore @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +futex_lock_pi_exiting futex_numa_mpol futex_priv_hash futex_requeue diff --git a/tools/testing/selftests/futex/functional/Makefile b/tools/testing/selftests/futex/functional/Makefile index 5c1c824f9740..a03bd5acba50 100644 --- a/tools/testing/selftests/futex/functional/Makefile +++ b/tools/testing/selftests/futex/functional/Makefile @@ -11,7 +11,8 @@ endif LOCAL_HDRS := \ ../include/futextest.h \ - ../include/atomic.h + ../include/atomic.h \ + ../include/futex_thread.h TEST_GEN_PROGS := \ futex_wait_timeout \ futex_wait_wouldblock \ @@ -26,7 +27,8 @@ TEST_GEN_PROGS := \ futex_numa_mpol \ futex_waitv \ futex_numa \ - robust_list + robust_list \ + futex_lock_pi_exiting TEST_PROGS := run.sh diff --git a/tools/testing/selftests/futex/functional/futex_lock_pi_exiting.c b/tools/testing/selftests/futex/functional/futex_lock_pi_exiting.c new file mode 100644 index 000000000000..623c5f3a1836 --- /dev/null +++ b/tools/testing/selftests/futex/functional/futex_lock_pi_exiting.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/****************************************************************************** + * + * futex_lock_pi_exiting.c + * + * Coverage for the FUTEX_LOCK_PI owner-exiting path. futex_wait_timeout.c + * already covers FUTEX_LOCK_PI timeout semantics and robust_list.c covers + * owner death via the robust list, but nothing exercises FUTEX_LOCK_PI when a + * non-robust PI owner exits while holding the lock, nor the basic ownership / + * EDEADLK / unlock word semantics. + * + * DESCRIPTION + * Three tests: + * + * 1. lock_unlock_basic - uncontended FUTEX_LOCK_PI semantics: the futex + * word carries the owner TID, a recursive lock by the owner returns + * EDEADLK, and FUTEX_UNLOCK_PI clears the word. + * + * 2. owner_dies_with_blocked_waiter - a thread acquires a PI futex and + * exits while holding it. do_exit() runs futex_cleanup_begin() (which + * flips the task's futex state to FUTEX_STATE_EXITING) and + * exit_pi_state_list() (which hands off / tears down the pi_state). A + * contending FUTEX_LOCK_PI waiter must end up in one of: + * + * 0 - ownership was transferred to / acquired by the waiter + * EOWNERDEAD - previous owner died holding the lock; the caller is + * now the owner and must acknowledge by unlocking + * ESRCH - the owner encoded in the futex word is already gone + * + * and on the first two it must actually own the lock afterwards. + * + * 3. stress_owner_exits - hammer that same exiting-owner path. This is + * where the following bug lived: the 'exiting' task pointer was not + * reset at the retry label, so after wait_for_owner_exiting() dropped + * its reference a subsequent retry that returned a non-EBUSY error fed + * the stale pointer back in and tripped WARN_ON_ONCE(exiting). That + * warning is invisible to user space, so this test cannot observe it + * through a syscall return value; it only becomes a visible failure + * (crash) on a kernel booted with panic_on_warn=1 (or built with + * CONFIG_BUG_ON_DATA_CORRUPTION). The loop drives the path so that + * such a kernel trips on it - the canonical way fuzz/CI catch these. + * + * Fix: 210d36d892de ("futex: Clear stale exiting pointer in + * futex_lock_pi() retry path") + * Fixes: 3ef240eaff36 ("futex: Prevent exit livelock") + * + * AUTHOR + * Based on futex test boilerplate by Darren Hart <dvhart@linux.intel.com> + * + *****************************************************************************/ + +#define _GNU_SOURCE + +#include <errno.h> +#include <pthread.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <sys/syscall.h> + +#include "futextest.h" +#include "kselftest_harness.h" + +/* + * Iterations for the stress variant. Enough to repeatedly land in the narrow + * EXITING window while keeping the test fast. + */ +#define STRESS_ITERS 1000 + +static futex_t pi_futex; +static pthread_barrier_t locked_barrier; +static pthread_barrier_t release_barrier; + +static pid_t sys_gettid(void) +{ + return syscall(SYS_gettid); +} + +/* + * Owner thread: acquire the PI futex and exit while still holding it. Two + * modes: + * park == 0: signal that we hold the lock, then exit immediately (racy; the + * waiter races against our exit path). + * park == 1: signal that we hold the lock and keep holding until released + * via release_barrier, so a waiter has time to contend as a real + * PI waiter before we die. + */ +static void *owner_thread(void *arg) +{ + long park = (long)arg; + + if (futex_lock_pi(&pi_futex, NULL, 0, FUTEX_PRIVATE_FLAG) != 0) + return (void *)(intptr_t)-errno; + + pthread_barrier_wait(&locked_barrier); + + if (park) + pthread_barrier_wait(&release_barrier); + + /* Die while still holding the lock. */ + pthread_exit((void *)0); +} + +/* + * Block on the PI futex as a waiter. Returns 0 on acquisition, otherwise the + * positive errno. + */ +static int waiter_lock_pi(void) +{ + int ret = futex_lock_pi(&pi_futex, NULL, 0, FUTEX_PRIVATE_FLAG); + + return ret == 0 ? 0 : errno; +} + +static int outcome_ok(int outcome) +{ + return outcome == 0 || outcome == EOWNERDEAD || outcome == ESRCH; +} + +/* Results published by waiter_thread() for the owning thread to assert on. */ +static int waiter_outcome; +static int waiter_owns; + +/* + * Waiter thread for the blocked-waiter test. Contends for the lock and, when + * it acquires, records whether the futex word actually carries its TID and + * releases the lock itself (FUTEX_UNLOCK_PI must run in the owning thread). + */ +static void *waiter_thread(void *arg) +{ + pid_t tid = sys_gettid(); + + waiter_outcome = waiter_lock_pi(); + if (waiter_outcome == 0 || waiter_outcome == EOWNERDEAD) { + waiter_owns = (pi_futex & FUTEX_TID_MASK) == (futex_t)tid; + futex_unlock_pi(&pi_futex, FUTEX_PRIVATE_FLAG); + } + return NULL; +} + +FIXTURE(lock_pi_exiting) { +}; + +FIXTURE_SETUP(lock_pi_exiting) { +} + +FIXTURE_TEARDOWN(lock_pi_exiting) { +} + +/* + * Uncontended FUTEX_LOCK_PI semantics, fully deterministic. + */ +TEST_F(lock_pi_exiting, lock_unlock_basic) +{ + pid_t tid = sys_gettid(); + int ret; + + pi_futex = FUTEX_INITIALIZER; + + /* Acquire: we become the owner, our TID lands in the futex word. */ + ret = futex_lock_pi(&pi_futex, NULL, 0, FUTEX_PRIVATE_FLAG); + ASSERT_EQ(ret, 0) + TH_LOG("lock failed: errno=%d (%s)", errno, strerror(errno)); + ASSERT_EQ(pi_futex & FUTEX_TID_MASK, (futex_t)tid) + TH_LOG("owner TID not in futex word: 0x%08x", pi_futex); + + /* A recursive lock by the owner must be refused, not deadlock. */ + errno = 0; + ret = futex_lock_pi(&pi_futex, NULL, 0, FUTEX_PRIVATE_FLAG); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EDEADLK) + TH_LOG("recursive lock: expected EDEADLK, got errno=%d", errno); + + /* Release: the futex word is handed back clean. */ + ret = futex_unlock_pi(&pi_futex, FUTEX_PRIVATE_FLAG); + ASSERT_EQ(ret, 0) + TH_LOG("unlock failed: errno=%d", errno); + ASSERT_EQ(pi_futex, (futex_t)0) + TH_LOG("futex word not cleared after unlock: 0x%08x", pi_futex); +} + +/* + * A PI waiter inherits the lock when the owner dies holding it. + * + * The owner parks while holding the lock, this thread contends for it, then + * the owner exits. The waiter must come out cleanly (no hang, no unexpected + * error) and, when it acquires, must actually own the lock. + */ +TEST_F(lock_pi_exiting, owner_dies_with_blocked_waiter) +{ + pthread_t owner, waiter; + + pthread_barrier_init(&locked_barrier, NULL, 2); + pthread_barrier_init(&release_barrier, NULL, 2); + pi_futex = FUTEX_INITIALIZER; + waiter_outcome = -1; + waiter_owns = 0; + + ASSERT_EQ(pthread_create(&owner, NULL, owner_thread, (void *)1), 0); + + /* Wait until the owner actually holds the lock. */ + pthread_barrier_wait(&locked_barrier); + + /* Start the waiter and give it time to block as a real PI waiter. */ + ASSERT_EQ(pthread_create(&waiter, NULL, waiter_thread, NULL), 0); + usleep(1000); + + /* Release the owner so it dies while the waiter is queued on it. */ + pthread_barrier_wait(&release_barrier); + + pthread_join(waiter, NULL); + pthread_join(owner, NULL); + + ASSERT_TRUE(outcome_ok(waiter_outcome)) { + TH_LOG("unexpected FUTEX_LOCK_PI outcome: %d (%s)", + waiter_outcome, strerror(waiter_outcome)); + } + if (waiter_outcome == 0 || waiter_outcome == EOWNERDEAD) { + ASSERT_TRUE(waiter_owns) + TH_LOG("waiter acquired but futex word lacks its TID"); + } + + pthread_barrier_destroy(&locked_barrier); + pthread_barrier_destroy(&release_barrier); +} + +/* + * Stress: repeatedly let an owner exit while a waiter contends for the lock. + * + * Each iteration drives the FUTEX_STATE_EXITING -> -EBUSY -> retry path that + * the stale-'exiting'-pointer bug lived on (210d36d892de). The warning it + * fixed is invisible to user space, so on a normally-configured kernel both + * the buggy and fixed kernels pass here; the point is to make a kernel booted + * with panic_on_warn=1 trip during one of these iterations. + */ +TEST_F(lock_pi_exiting, stress_owner_exits) +{ + for (int i = 0; i < STRESS_ITERS; i++) { + pthread_t owner; + int outcome; + + pthread_barrier_init(&locked_barrier, NULL, 2); + pi_futex = FUTEX_INITIALIZER; + + ASSERT_EQ(pthread_create(&owner, NULL, owner_thread, (void *)0), 0); + + /* Owner holds the lock; race FUTEX_LOCK_PI against its exit. */ + pthread_barrier_wait(&locked_barrier); + + outcome = waiter_lock_pi(); + ASSERT_TRUE(outcome_ok(outcome)) { + TH_LOG("iter %d: unexpected outcome %d (%s)", + i, outcome, strerror(outcome)); + } + if (outcome == 0 || outcome == EOWNERDEAD) + futex_unlock_pi(&pi_futex, FUTEX_PRIVATE_FLAG); + + pthread_join(owner, NULL); + pthread_barrier_destroy(&locked_barrier); + } +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/futex/functional/futex_numa_mpol.c b/tools/testing/selftests/futex/functional/futex_numa_mpol.c index 78c0f7a59e17..4ffcf41efe1f 100644 --- a/tools/testing/selftests/futex/functional/futex_numa_mpol.c +++ b/tools/testing/selftests/futex/functional/futex_numa_mpol.c @@ -9,6 +9,7 @@ #include <pthread.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #ifdef LIBNUMA_VER_SUFFICIENT #include <numa.h> @@ -28,9 +29,9 @@ static pthread_barrier_t barrier_main; static pthread_t threads[MAX_THREADS]; struct thread_args { - void *futex_ptr; - unsigned int flags; - int result; + void *futex_ptr; + unsigned int flags; + int result; }; static struct thread_args thread_args[MAX_THREADS]; @@ -54,7 +55,7 @@ static void *thread_lock_fn(void *arg) return NULL; } -static void create_max_threads(void *futex_ptr) +static void create_max_threads(struct __test_metadata *_metadata, void *futex_ptr) { int i, ret; @@ -63,28 +64,29 @@ static void create_max_threads(void *futex_ptr) thread_args[i].flags = FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA; thread_args[i].result = 0; ret = pthread_create(&threads[i], NULL, thread_lock_fn, &thread_args[i]); - if (ret) - ksft_exit_fail_msg("pthread_create failed\n"); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_create failed"); } } -static void join_max_threads(void) +static void join_max_threads(struct __test_metadata *_metadata) { int i, ret; for (i = 0; i < MAX_THREADS; i++) { ret = pthread_join(threads[i], NULL); - if (ret) - ksft_exit_fail_msg("pthread_join failed for thread %d\n", i); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_join failed for thread %d", i); } } -static void __test_futex(void *futex_ptr, int err_value, unsigned int futex_flags) +static void __test_futex(struct __test_metadata *_metadata, void *futex_ptr, int err_value, + unsigned int futex_flags) { - int to_wake, ret, i, need_exit = 0; + int to_wake, ret, i; pthread_barrier_init(&barrier_main, NULL, MAX_THREADS + 1); - create_max_threads(futex_ptr); + create_max_threads(_metadata, futex_ptr); pthread_barrier_wait(&barrier_main); to_wake = MAX_THREADS; @@ -92,45 +94,50 @@ static void __test_futex(void *futex_ptr, int err_value, unsigned int futex_flag ret = futex2_wake(futex_ptr, to_wake, futex_flags); if (err_value) { - if (ret >= 0) - ksft_exit_fail_msg("futex2_wake(%d, 0x%x) should fail, but didn't\n", - to_wake, futex_flags); + EXPECT_LT(ret, 0) { + TH_LOG("futex2_wake(%d, 0x%x) should fail, but didn't", + to_wake, futex_flags); + } - if (errno != err_value) - ksft_exit_fail_msg("futex2_wake(%d, 0x%x) expected error was %d, but returned %d (%s)\n", - to_wake, futex_flags, err_value, errno, strerror(errno)); + EXPECT_EQ(errno, err_value) { + TH_LOG("futex2_wake(%d, 0x%x) expected error was %d, but returned %d (%s)", + to_wake, futex_flags, err_value, errno, strerror(errno)); + } break; } if (ret < 0) { - ksft_exit_fail_msg("Failed futex2_wake(%d, 0x%x): %m\n", - to_wake, futex_flags); + if (errno == ENOSYS || (errno == EINVAL && (futex_flags & FUTEX2_NUMA))) + SKIP(return, "futex2 or FUTEX2_NUMA not supported by kernel"); + + ASSERT_GE(ret, 0) { + TH_LOG("Failed futex2_wake(%d, 0x%x): %s", + to_wake, futex_flags, strerror(errno)); + } } if (!ret) usleep(50); to_wake -= ret; } while (to_wake); - join_max_threads(); + join_max_threads(_metadata); for (i = 0; i < MAX_THREADS; i++) { - if (err_value && thread_args[i].result != -1) { - ksft_print_msg("Thread %d should fail but succeeded (%d)\n", + if (err_value) { + EXPECT_EQ(thread_args[i].result, -1) { + TH_LOG("Thread %d should fail but succeeded (%d)", i, thread_args[i].result); - need_exit = 1; - } - if (!err_value && thread_args[i].result != 0) { - ksft_print_msg("Thread %d failed (%d)\n", i, thread_args[i].result); - need_exit = 1; + } + } else { + EXPECT_EQ(thread_args[i].result, 0) + TH_LOG("Thread %d failed (%d)", i, thread_args[i].result); } } - if (need_exit) - ksft_exit_fail_msg("Aborting due to earlier errors.\n"); } -static void test_futex(void *futex_ptr, int err_value) +static void test_futex(struct __test_metadata *_metadata, void *futex_ptr, int err_value) { - __test_futex(futex_ptr, err_value, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA); + __test_futex(_metadata, futex_ptr, err_value, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA); } TEST(futex_numa_mpol) @@ -141,43 +148,41 @@ TEST(futex_numa_mpol) mem_size = sysconf(_SC_PAGE_SIZE); futex_ptr = mmap(NULL, mem_size * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); - if (futex_ptr == MAP_FAILED) - ksft_exit_fail_msg("mmap() for %d bytes failed\n", mem_size); + ASSERT_NE(futex_ptr, MAP_FAILED) + TH_LOG("mmap() for %d bytes failed: %s", mem_size, strerror(errno)); /* Create an invalid memory region for the "Memory out of range" test */ mprotect(futex_ptr + mem_size, mem_size, PROT_NONE); futex_numa = futex_ptr; - ksft_print_msg("Regular test\n"); + TH_LOG("Regular test"); futex_numa->futex = 0; futex_numa->numa = FUTEX_NO_NODE; - test_futex(futex_ptr, 0); + test_futex(_metadata, futex_ptr, 0); - if (futex_numa->numa == FUTEX_NO_NODE) - ksft_exit_fail_msg("NUMA node is left uninitialized\n"); + EXPECT_NE(futex_numa->numa, FUTEX_NO_NODE) + TH_LOG("NUMA node is left uninitialized"); /* FUTEX2_NUMA futex must be 8-byte aligned */ - ksft_print_msg("Mis-aligned futex\n"); - test_futex(futex_ptr + mem_size - 4, EINVAL); + TH_LOG("Mis-aligned futex"); + test_futex(_metadata, futex_ptr + mem_size - 4, EINVAL); - ksft_print_msg("Memory out of range\n"); - test_futex(futex_ptr + mem_size, EFAULT); + TH_LOG("Memory out of range"); + test_futex(_metadata, futex_ptr + mem_size, EFAULT); futex_numa->numa = FUTEX_NO_NODE; mprotect(futex_ptr, mem_size, PROT_READ); - ksft_print_msg("Memory, RO\n"); - test_futex(futex_ptr, EFAULT); + TH_LOG("Memory, RO"); + test_futex(_metadata, futex_ptr, EFAULT); mprotect(futex_ptr, mem_size, PROT_NONE); - ksft_print_msg("Memory, no access\n"); - test_futex(futex_ptr, EFAULT); + TH_LOG("Memory, no access"); + test_futex(_metadata, futex_ptr, EFAULT); mprotect(futex_ptr, mem_size, PROT_READ | PROT_WRITE); - ksft_print_msg("Memory back to RW\n"); - test_futex(futex_ptr, 0); - - ksft_test_result_pass("futex2 memory boundary tests passed\n"); + TH_LOG("Memory back to RW"); + test_futex(_metadata, futex_ptr, 0); /* MPOL test. Does not work as expected */ #ifdef LIBNUMA_VER_SUFFICIENT @@ -190,25 +195,23 @@ TEST(futex_numa_mpol) sizeof(nodemask) * 8, 0); if (ret == 0) { ret = numa_set_mempolicy_home_node(futex_ptr, mem_size, i, 0); - if (ret != 0) - ksft_exit_fail_msg("Failed to set home node: %m, %d\n", errno); + ASSERT_EQ(ret, 0) + TH_LOG("Failed to set home node: %s, %d", strerror(errno), errno); - ksft_print_msg("Node %d test\n", i); + TH_LOG("Node %d test", i); futex_numa->futex = 0; futex_numa->numa = FUTEX_NO_NODE; - ret = futex2_wake(futex_ptr, 0, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL); - if (ret < 0) - ksft_test_result_fail("Failed to wake 0 with MPOL: %m\n"); - if (futex_numa->numa != i) { - ksft_exit_fail_msg("Returned NUMA node is %d expected %d\n", - futex_numa->numa, i); - } + ret = futex2_wake(futex_ptr, 0, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | + FUTEX2_NUMA | FUTEX2_MPOL); + EXPECT_GE(ret, 0) + TH_LOG("Failed to wake 0 with MPOL: %s", strerror(errno)); + EXPECT_EQ(futex_numa->numa, i) + TH_LOG("Returned NUMA node is %d expected %d", futex_numa->numa, i); } } - ksft_test_result_pass("futex2 MPOL hints test passed\n"); #else - ksft_test_result_skip("futex2 MPOL hints test requires libnuma 2.0.18+\n"); + SKIP(return, "futex2 MPOL hints test requires libnuma 2.0.18+"); #endif munmap(futex_ptr, mem_size * 2); } diff --git a/tools/testing/selftests/futex/functional/futex_priv_hash.c b/tools/testing/selftests/futex/functional/futex_priv_hash.c index e8079d7c65e8..a8742e204540 100644 --- a/tools/testing/selftests/futex/functional/futex_priv_hash.c +++ b/tools/testing/selftests/futex/functional/futex_priv_hash.c @@ -9,6 +9,7 @@ #include <pthread.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <linux/prctl.h> @@ -39,31 +40,28 @@ static int futex_hash_slots_get(void) return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_SLOTS); } -static void futex_hash_slots_set_verify(int slots) +static void futex_hash_slots_set_verify(struct __test_metadata *_metadata, int slots) { int ret; ret = futex_hash_slots_set(slots); - if (ret != 0) { - ksft_test_result_fail("Failed to set slots to %d: %m\n", slots); - ksft_finished(); - } + ASSERT_EQ(ret, 0) + TH_LOG("Failed to set slots to %d: %s", slots, strerror(errno)); + ret = futex_hash_slots_get(); - if (ret != slots) { - ksft_test_result_fail("Set %d slots but PR_FUTEX_HASH_GET_SLOTS returns: %d, %m\n", - slots, ret); - ksft_finished(); + ASSERT_EQ(ret, slots) { + TH_LOG("Set %d slots but PR_FUTEX_HASH_GET_SLOTS returns: %d, %s", + slots, ret, strerror(errno)); } - ksft_test_result_pass("SET and GET slots %d passed\n", slots); } -static void futex_hash_slots_set_must_fail(int slots) +static void futex_hash_slots_set_must_fail(struct __test_metadata *_metadata, int slots) { int ret; ret = futex_hash_slots_set(slots); - ksft_test_result(ret < 0, "futex_hash_slots_set(%d)\n", - slots); + EXPECT_LT(ret, 0) + TH_LOG("futex_hash_slots_set(%d) should fail but succeeded", slots); } static void *thread_return_fn(void *arg) @@ -82,32 +80,32 @@ static void *thread_lock_fn(void *arg) return NULL; } -static void create_max_threads(void *(*thread_fn)(void *)) +static void create_max_threads(struct __test_metadata *_metadata, void *(*thread_fn)(void *)) { int i, ret; for (i = 0; i < MAX_THREADS; i++) { ret = pthread_create(&threads[i], NULL, thread_fn, NULL); - if (ret) - ksft_exit_fail_msg("pthread_create failed: %m\n"); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_create failed: %s", strerror(errno)); } } -static void join_max_threads(void) +static void join_max_threads(struct __test_metadata *_metadata) { int i, ret; for (i = 0; i < MAX_THREADS; i++) { ret = pthread_join(threads[i], NULL); - if (ret) - ksft_exit_fail_msg("pthread_join failed for thread %d\n", i); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_join failed for thread %d: %s", i, strerror(errno)); } } #define SEC_IN_NSEC 1000000000 #define MSEC_IN_NSEC 1000000 -static void futex_dummy_op(void) +static void futex_dummy_op(struct __test_metadata *_metadata) { pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; struct timespec timeout; @@ -121,11 +119,11 @@ static void futex_dummy_op(void) timeout.tv_sec++; } ret = pthread_mutex_timedlock(&lock, &timeout); - if (ret == 0) - ksft_exit_fail_msg("Successfully locked an already locked mutex.\n"); + ASSERT_NE(ret, 0) + TH_LOG("Successfully locked an already locked mutex"); - if (ret != ETIMEDOUT) - ksft_exit_fail_msg("pthread_mutex_timedlock() did not timeout: %d.\n", ret); + ASSERT_EQ(ret, ETIMEDOUT) + TH_LOG("pthread_mutex_timedlock() did not timeout: %d", ret); } static const char *test_msg_auto_create = "Automatic hash bucket init on thread creation.\n"; @@ -140,50 +138,48 @@ TEST(priv_hash) ret = pthread_mutexattr_init(&mutex_attr_pi); ret |= pthread_mutexattr_setprotocol(&mutex_attr_pi, PTHREAD_PRIO_INHERIT); ret |= pthread_mutex_init(&global_lock, &mutex_attr_pi); - if (ret != 0) { - ksft_exit_fail_msg("Failed to initialize pthread mutex.\n"); - } + ASSERT_EQ(ret, 0) + TH_LOG("Failed to initialize pthread mutex"); + /* First thread, expect to be 0, not yet initialized */ ret = futex_hash_slots_get(); - if (ret != 0) - ksft_exit_fail_msg("futex_hash_slots_get() failed: %d, %m\n", ret); + if (ret < 0 && errno == EINVAL) + SKIP(return, "PR_FUTEX_HASH not supported by kernel"); + + ASSERT_EQ(ret, 0) + TH_LOG("futex_hash_slots_get() failed: %d, %s", ret, strerror(errno)); - ksft_test_result_pass("Basic get slots and immutable status.\n"); ret = pthread_create(&threads[0], NULL, thread_return_fn, NULL); - if (ret != 0) - ksft_exit_fail_msg("pthread_create() failed: %d, %m\n", ret); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_create() failed: %d, %s", ret, strerror(errno)); ret = pthread_join(threads[0], NULL); - if (ret != 0) - ksft_exit_fail_msg("pthread_join() failed: %d, %m\n", ret); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_join() failed: %d, %s", ret, strerror(errno)); /* First thread, has to initialize private hash */ futex_slots1 = futex_hash_slots_get(); - if (futex_slots1 <= 0) { - ksft_print_msg("Current hash buckets: %d\n", futex_slots1); - ksft_exit_fail_msg("%s", test_msg_auto_create); - } - - ksft_test_result_pass("%s", test_msg_auto_create); + EXPECT_GT(futex_slots1, 0) + TH_LOG("Current hash buckets: %d. %s", futex_slots1, test_msg_auto_create); online_cpus = sysconf(_SC_NPROCESSORS_ONLN); ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS + 1); - if (ret != 0) - ksft_exit_fail_msg("pthread_barrier_init failed: %m.\n"); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_barrier_init failed: %s", strerror(errno)); ret = pthread_mutex_lock(&global_lock); - if (ret != 0) - ksft_exit_fail_msg("pthread_mutex_lock failed: %m.\n"); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_mutex_lock failed: %s", strerror(errno)); counter = 0; - create_max_threads(thread_lock_fn); + create_max_threads(_metadata, thread_lock_fn); pthread_barrier_wait(&barrier_main); /* * The current default size of hash buckets is 16. The auto increase * works only if more than 16 CPUs are available. */ - ksft_print_msg("Online CPUs: %d\n", online_cpus); + TH_LOG("Online CPUs: %d", online_cpus); if (online_cpus > 16) { retry_getslots: futex_slotsn = futex_hash_slots_get(); @@ -200,71 +196,76 @@ retry_getslots: * sleep for 100ms and issue a futex operation. */ if (retry > 0) { - futex_dummy_op(); + futex_dummy_op(_metadata); goto retry_getslots; } - ksft_print_msg("Expected increase of hash buckets but got: %d -> %d\n", - futex_slots1, futex_slotsn); - ksft_exit_fail_msg("%s", test_msg_auto_inc); + EXPECT_NE(futex_slots1, futex_slotsn) { + TH_LOG("Expected increase of hash buckets but got: %d -> %d. %s", + futex_slots1, futex_slotsn, test_msg_auto_inc); + } } - ksft_test_result_pass("%s", test_msg_auto_inc); } else { - ksft_test_result_skip("%s", test_msg_auto_inc); + SKIP(return, "Automatic increase with more than 16 CPUs (only %d online)", online_cpus); } ret = pthread_mutex_unlock(&global_lock); /* Once the user changes it, it has to be what is set */ - futex_hash_slots_set_verify(2); - futex_hash_slots_set_verify(4); - futex_hash_slots_set_verify(8); - futex_hash_slots_set_verify(32); - futex_hash_slots_set_verify(16); + futex_hash_slots_set_verify(_metadata, 2); + futex_hash_slots_set_verify(_metadata, 4); + futex_hash_slots_set_verify(_metadata, 8); + futex_hash_slots_set_verify(_metadata, 32); + futex_hash_slots_set_verify(_metadata, 16); ret = futex_hash_slots_set(15); - ksft_test_result(ret < 0, "Use 15 slots\n"); + EXPECT_LT(ret, 0) + TH_LOG("Use 15 slots should fail but succeeded"); + + futex_hash_slots_set_verify(_metadata, 2); + join_max_threads(_metadata); + + EXPECT_EQ(counter, MAX_THREADS) + TH_LOG("Created and waited for %d of %d threads", counter, MAX_THREADS); - futex_hash_slots_set_verify(2); - join_max_threads(); - ksft_test_result(counter == MAX_THREADS, "Created and waited for %d of %d threads\n", - counter, MAX_THREADS); counter = 0; /* Once the user set something, auto resize must be disabled */ ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_barrier_init failed: %s", strerror(errno)); - create_max_threads(thread_lock_fn); - join_max_threads(); + create_max_threads(_metadata, thread_lock_fn); + join_max_threads(_metadata); ret = futex_hash_slots_get(); - ksft_test_result(ret == 2, "No more auto-resize after manual setting, got %d\n", - ret); + EXPECT_EQ(ret, 2) + TH_LOG("No more auto-resize after manual setting, got %d", ret); - futex_hash_slots_set_must_fail(1 << 29); - futex_hash_slots_set_verify(4); + futex_hash_slots_set_must_fail(_metadata, 1 << 29); + futex_hash_slots_set_verify(_metadata, 4); /* * Once the global hash has been requested, then this requested can not * be undone. */ ret = futex_hash_slots_set(0); - ksft_test_result(ret == 0, "Global hash request\n"); - if (ret != 0) - return; + ASSERT_EQ(ret, 0) + TH_LOG("Global hash request failed: %s", strerror(errno)); - futex_hash_slots_set_must_fail(4); - futex_hash_slots_set_must_fail(8); - futex_hash_slots_set_must_fail(8); - futex_hash_slots_set_must_fail(0); - futex_hash_slots_set_must_fail(6); + futex_hash_slots_set_must_fail(_metadata, 4); + futex_hash_slots_set_must_fail(_metadata, 8); + futex_hash_slots_set_must_fail(_metadata, 8); + futex_hash_slots_set_must_fail(_metadata, 0); + futex_hash_slots_set_must_fail(_metadata, 6); ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS); - if (ret != 0) - ksft_exit_fail_msg("pthread_barrier_init failed: %m\n"); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_barrier_init failed: %s", strerror(errno)); - create_max_threads(thread_lock_fn); - join_max_threads(); + create_max_threads(_metadata, thread_lock_fn); + join_max_threads(_metadata); ret = futex_hash_slots_get(); - ksft_test_result(ret == 0, "Continue to use global hash\n"); + EXPECT_EQ(ret, 0) + TH_LOG("Continue to use global hash failed"); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/futex/functional/futex_requeue.c b/tools/testing/selftests/futex/functional/futex_requeue.c index dcf0d5f2f312..cc31f051765d 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue.c +++ b/tools/testing/selftests/futex/functional/futex_requeue.c @@ -5,55 +5,70 @@ * futex cmp requeue test by André Almeida <andrealmeid@collabora.com> */ -#include <pthread.h> #include <limits.h> +#include <pthread.h> +#include <string.h> #include "futextest.h" +#include "futex_thread.h" #include "kselftest_harness.h" -#define timeout_ns 30000000 -#define WAKE_WAIT_US 10000 +struct waiter_args { + struct __test_metadata *_metadata; + unsigned int n_threads; +}; volatile futex_t *f1; -void *waiterfn(void *arg) +static int waiterfn(void *arg) { - struct timespec to; + struct __test_metadata *_metadata; + struct waiter_args *wargs = arg; + struct timespec to = { }; + int res; - to.tv_sec = 0; - to.tv_nsec = timeout_ns; + _metadata = wargs->_metadata; + to.tv_sec = (wargs->n_threads + 1) * WAIT_FOR_THREAD_SECS; - if (futex_wait(f1, *f1, &to, 0)) - printf("waiter failed errno %d\n", errno); + res = futex_wait(f1, *f1, &to, 0); + if (res) { + EXPECT_EQ(res, 0) + TH_LOG("waiter failed errno %d: %s", errno, strerror(errno)); + } - return NULL; + return 0; } TEST(requeue_single) { + struct waiter_args wargs = { ._metadata = _metadata, .n_threads = 1 }; + struct futex_thread waiter; volatile futex_t _f1 = 0; volatile futex_t f2 = 0; - pthread_t waiter[10]; f1 = &_f1; /* * Requeue a waiter from f1 to f2, and wake f2. */ - ASSERT_EQ(0, pthread_create(&waiter[0], NULL, waiterfn, NULL)); + ASSERT_EQ(futex_thread_create(&waiter, waiterfn, &wargs), 0) + TH_LOG("pthread_create failed"); - usleep(WAKE_WAIT_US); + ASSERT_EQ(futex_wait_for_thread(&waiter, _metadata), 0) + TH_LOG("Wait for thread failed"); - EXPECT_EQ(1, futex_cmp_requeue(f1, 0, &f2, 0, 1, 0)); - EXPECT_EQ(1, futex_wake(&f2, 1, 0)); + EXPECT_EQ(futex_cmp_requeue(f1, 0, &f2, 0, 1, 0), 1); + EXPECT_EQ(futex_wake(&f2, 1, 0), 1); + + EXPECT_EQ(futex_thread_destroy(&waiter), 0); } TEST(requeue_multiple) { + struct waiter_args wargs = { ._metadata = _metadata, .n_threads = 10 }; + struct futex_thread waiter[10]; volatile futex_t _f1 = 0; volatile futex_t f2 = 0; - pthread_t waiter[10]; - int i; f1 = &_f1; @@ -61,13 +76,21 @@ TEST(requeue_multiple) * Create 10 waiters at f1. At futex_requeue, wake 3 and requeue 7. * At futex_wake, wake INT_MAX (should be exactly 7). */ - for (i = 0; i < 10; i++) - ASSERT_EQ(0, pthread_create(&waiter[i], NULL, waiterfn, NULL)); + for (int i = 0; i < 10; i++) { + ASSERT_EQ(futex_thread_create(&waiter[i], waiterfn, &wargs), 0) + TH_LOG("pthread_create failed for waiter %d", i); + } + + for (int i = 0; i < 10; i++) { + ASSERT_EQ(futex_wait_for_thread(&waiter[i], _metadata), 0) + TH_LOG("Wait for waiter thread %d failed", i); + } - usleep(WAKE_WAIT_US); + EXPECT_EQ(futex_cmp_requeue(f1, 0, &f2, 3, 7, 0), 10); + EXPECT_EQ(futex_wake(&f2, INT_MAX, 0), 7); - EXPECT_EQ(10, futex_cmp_requeue(f1, 0, &f2, 3, 7, 0)); - EXPECT_EQ(7, futex_wake(&f2, INT_MAX, 0)); + for (int i = 0; i < 10; i++) + EXPECT_EQ(futex_thread_destroy(&waiter[i]), 0); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi.c b/tools/testing/selftests/futex/functional/futex_requeue_pi.c index 46d2858e15a8..65326cbef3a4 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi.c @@ -43,12 +43,13 @@ futex_t f2 = FUTEX_INITIALIZER; futex_t wake_complete = FUTEX_INITIALIZER; struct thread_arg { - long id; - struct timespec *timeout; - int lock; - int ret; + struct __test_metadata *_metadata; + long id; + struct timespec *timeout; + int lock; + int ret; }; -#define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 } +#define THREAD_ARG_INITIALIZER { NULL, 0, NULL, 0, 0 } FIXTURE(args) { @@ -118,7 +119,7 @@ FIXTURE_VARIANT_ADD_TIMEOUT(5000); FIXTURE_VARIANT_ADD_TIMEOUT(500000); FIXTURE_VARIANT_ADD_TIMEOUT(2000000000); -int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg, +int create_rt_thread(struct __test_metadata *_metadata, pthread_t *pth, void*(*func)(void *), void *arg, int policy, int prio) { int ret; @@ -129,29 +130,22 @@ int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg, memset(&schedp, 0, sizeof(schedp)); ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); - if (ret) { - ksft_exit_fail_msg("pthread_attr_setinheritsched\n"); - return -1; - } + ASSERT_EQ(ret, 0) + TH_LOG("pthread_attr_setinheritsched failed"); ret = pthread_attr_setschedpolicy(&attr, policy); - if (ret) { - ksft_exit_fail_msg("pthread_attr_setschedpolicy\n"); - return -1; - } + ASSERT_EQ(ret, 0) + TH_LOG("pthread_attr_setschedpolicy failed"); schedp.sched_priority = prio; ret = pthread_attr_setschedparam(&attr, &schedp); - if (ret) { - ksft_exit_fail_msg("pthread_attr_setschedparam\n"); - return -1; - } + ASSERT_EQ(ret, 0) + TH_LOG("pthread_attr_setschedparam failed"); ret = pthread_create(pth, &attr, func, arg); - if (ret) { - ksft_exit_fail_msg("pthread_create\n"); - return -1; - } + ASSERT_EQ(ret, 0) + TH_LOG("pthread_create failed"); + return 0; } @@ -159,70 +153,76 @@ int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg, void *waiterfn(void *arg) { struct thread_arg *args = (struct thread_arg *)arg; + struct __test_metadata *_metadata = args->_metadata; futex_t old_val; - ksft_print_dbg_msg("Waiter %ld: running\n", args->id); + TH_LOG("Waiter %ld: running", args->id); /* Each thread sleeps for a different amount of time * This is to avoid races, because we don't lock the - * external mutex here */ + * external mutex here + */ usleep(1000 * (long)args->id); old_val = f1; atomic_inc(&waiters_blocked); - ksft_print_dbg_msg("Calling futex_wait_requeue_pi: %p (%u) -> %p\n", - &f1, f1, &f2); + TH_LOG("Calling futex_wait_requeue_pi: %p (%u) -> %p", &f1, f1, &f2); args->ret = futex_wait_requeue_pi(&f1, old_val, &f2, args->timeout, FUTEX_PRIVATE_FLAG); - ksft_print_dbg_msg("waiter %ld woke with %d %s\n", args->id, args->ret, - args->ret < 0 ? strerror(errno) : ""); + TH_LOG("waiter %ld woke with %d %s", args->id, args->ret, + args->ret < 0 ? strerror(errno) : ""); atomic_inc(&waiters_woken); if (args->ret < 0) { - if (args->timeout && errno == ETIMEDOUT) + if (args->timeout && errno == ETIMEDOUT) { args->ret = 0; - else { - ksft_exit_fail_msg("futex_wait_requeue_pi\n"); + } else { + ASSERT_EQ(args->ret, 0) + TH_LOG("futex_wait_requeue_pi failed: %s", strerror(errno)); } futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); } futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); - ksft_print_dbg_msg("Waiter %ld: exiting with %d\n", args->id, args->ret); + TH_LOG("Waiter %ld: exiting with %d", args->id, args->ret); pthread_exit((void *)&args->ret); } void *broadcast_wakerfn(void *arg) { struct thread_arg *args = (struct thread_arg *)arg; + struct __test_metadata *_metadata = args->_metadata; int nr_requeue = INT_MAX; int task_count = 0; futex_t old_val; int nr_wake = 1; int i = 0; - ksft_print_dbg_msg("Waker: waiting for waiters to block\n"); + TH_LOG("Waker: waiting for waiters to block"); while (waiters_blocked.val < THREAD_MAX) usleep(1000); usleep(1000); - ksft_print_dbg_msg("Waker: Calling broadcast\n"); + TH_LOG("Waker: Calling broadcast"); if (args->lock) { - ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2); + TH_LOG("Calling FUTEX_LOCK_PI on mutex=%x @ %p", f2, &f2); futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); } continue_requeue: old_val = f1; args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue, - FUTEX_PRIVATE_FLAG); + FUTEX_PRIVATE_FLAG); if (args->ret < 0) { - ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n"); + ASSERT_GE(args->ret, 0) + TH_LOG("FUTEX_CMP_REQUEUE_PI failed: %s", strerror(errno)); } else if (++i < MAX_WAKE_ITERS) { task_count += args->ret; if (task_count < THREAD_MAX - waiters_woken.val) goto continue_requeue; } else { - ksft_exit_fail_msg("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n", - MAX_WAKE_ITERS, task_count, THREAD_MAX); + ASSERT_TRUE(0) { + TH_LOG("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued", + MAX_WAKE_ITERS, task_count, THREAD_MAX); + } } futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); @@ -233,33 +233,33 @@ void *broadcast_wakerfn(void *arg) if (args->ret > 0) args->ret = task_count; - ksft_print_dbg_msg("Waker: exiting with %d\n", args->ret); + TH_LOG("Waker: exiting with %d", args->ret); pthread_exit((void *)&args->ret); } void *signal_wakerfn(void *arg) { struct thread_arg *args = (struct thread_arg *)arg; + struct __test_metadata *_metadata = args->_metadata; unsigned int old_val; int nr_requeue = 0; int task_count = 0; int nr_wake = 1; int i = 0; - ksft_print_dbg_msg("Waker: waiting for waiters to block\n"); + TH_LOG("Waker: waiting for waiters to block"); while (waiters_blocked.val < THREAD_MAX) usleep(1000); usleep(1000); while (task_count < THREAD_MAX && waiters_woken.val < THREAD_MAX) { - ksft_print_dbg_msg("task_count: %d, waiters_woken: %d\n", + TH_LOG("task_count: %d, waiters_woken: %d", task_count, waiters_woken.val); if (args->lock) { - ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", - f2, &f2); + TH_LOG("Calling FUTEX_LOCK_PI on mutex=%x @ %p", f2, &f2); futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); } - ksft_print_dbg_msg("Waker: Calling signal\n"); + TH_LOG("Waker: Calling signal"); /* cond_signal */ old_val = f1; args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, @@ -267,23 +267,27 @@ void *signal_wakerfn(void *arg) FUTEX_PRIVATE_FLAG); if (args->ret < 0) args->ret = -errno; - ksft_print_dbg_msg("futex: %x\n", f2); + TH_LOG("futex: %x", f2); if (args->lock) { - ksft_print_dbg_msg("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", + TH_LOG("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p", f2, &f2); futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); } - ksft_print_dbg_msg("futex: %x\n", f2); - if (args->ret < 0) - ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n"); + TH_LOG("futex: %x", f2); + if (args->ret < 0) { + ASSERT_GE(args->ret, 0) + TH_LOG("FUTEX_CMP_REQUEUE_PI failed: %s", strerror(-args->ret)); + } task_count += args->ret; usleep(SIGNAL_PERIOD_US); i++; /* we have to loop at least THREAD_MAX times */ if (i > MAX_WAKE_ITERS + THREAD_MAX) { - ksft_exit_fail_msg("max signaling iterations (%d) reached, giving up on pending waiters.\n", - MAX_WAKE_ITERS + THREAD_MAX); + ASSERT_TRUE(0) { + TH_LOG("max signaling iterations (%d) reached, giving up on pending waiters.", + MAX_WAKE_ITERS + THREAD_MAX); + } } } @@ -292,14 +296,15 @@ void *signal_wakerfn(void *arg) if (args->ret >= 0) args->ret = task_count; - ksft_print_dbg_msg("Waker: exiting with %d\n", args->ret); - ksft_print_dbg_msg("Waker: waiters_woken: %d\n", waiters_woken.val); + TH_LOG("Waker: exiting with %d", args->ret); + TH_LOG("Waker: waiters_woken: %d", waiters_woken.val); pthread_exit((void *)&args->ret); } void *third_party_blocker(void *arg) { struct thread_arg *args = (struct thread_arg *)arg; + struct __test_metadata *_metadata = args->_metadata; int ret2 = 0; args->ret = futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); @@ -310,8 +315,10 @@ void *third_party_blocker(void *arg) ret2 = futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); out: - if (args->ret || ret2) - ksft_exit_fail_msg("third_party_blocker() futex error"); + if (args->ret || ret2) { + ASSERT_TRUE(0) + TH_LOG("%s() futex error", __func__); + } pthread_exit((void *)&args->ret); } @@ -330,20 +337,19 @@ TEST_F(args, futex_requeue_pi) bool lock = variant->locked; int *waiter_ret, i, ret = 0; - ksft_print_msg( - "\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n", + TH_LOG("Arguments: broadcast=%d locked=%d owner=%d timeout=%ldns", broadcast, lock, third_party_owner, timeout_ns); if (timeout_ns) { time_t secs; - ksft_print_dbg_msg("timeout_ns = %ld\n", timeout_ns); + TH_LOG("timeout_ns = %ld", timeout_ns); ret = clock_gettime(CLOCK_MONOTONIC, &ts); secs = (ts.tv_nsec + timeout_ns) / 1000000000; ts.tv_nsec = ((int64_t)ts.tv_nsec + timeout_ns) % 1000000000; ts.tv_sec += secs; - ksft_print_dbg_msg("ts.tv_sec = %ld\n", ts.tv_sec); - ksft_print_dbg_msg("ts.tv_nsec = %ld\n", ts.tv_nsec); + TH_LOG("ts.tv_sec = %ld", ts.tv_sec); + TH_LOG("ts.tv_nsec = %ld", ts.tv_nsec); tsp = &ts; } @@ -351,34 +357,29 @@ TEST_F(args, futex_requeue_pi) wakerfn = broadcast_wakerfn; if (third_party_owner) { - if (create_rt_thread(&blocker, third_party_blocker, - (void *)&blocker_arg, SCHED_FIFO, 1)) { - ksft_exit_fail_msg("Creating third party blocker thread failed\n"); - } + blocker_arg._metadata = _metadata; + create_rt_thread(_metadata, &blocker, third_party_blocker, + (void *)&blocker_arg, SCHED_FIFO, 1); } atomic_set(&waiters_woken, 0); for (i = 0; i < THREAD_MAX; i++) { + args[i]._metadata = _metadata; args[i].id = i; args[i].timeout = tsp; - ksft_print_dbg_msg("Starting thread %d\n", i); - if (create_rt_thread(&waiter[i], waiterfn, (void *)&args[i], - SCHED_FIFO, 1)) { - ksft_exit_fail_msg("Creating waiting thread failed\n"); - } + TH_LOG("Starting thread %d", i); + create_rt_thread(_metadata, &waiter[i], waiterfn, (void *)&args[i], + SCHED_FIFO, 1); } + waker_arg._metadata = _metadata; waker_arg.lock = lock; - if (create_rt_thread(&waker, wakerfn, (void *)&waker_arg, - SCHED_FIFO, 1)) { - ksft_exit_fail_msg("Creating waker thread failed\n"); - } + create_rt_thread(_metadata, &waker, wakerfn, (void *)&waker_arg, SCHED_FIFO, 1); /* Wait for threads to finish */ /* Store the first error or failure encountered in waiter_ret */ waiter_ret = &args[0].ret; for (i = 0; i < THREAD_MAX; i++) - pthread_join(waiter[i], - *waiter_ret ? NULL : (void **)&waiter_ret); + pthread_join(waiter[i], *waiter_ret ? NULL : (void **)&waiter_ret); if (third_party_owner) pthread_join(blocker, NULL); @@ -393,8 +394,8 @@ TEST_F(args, futex_requeue_pi) ret = blocker_arg.ret; } - if (ret) - ksft_test_result_fail("fail"); + EXPECT_EQ(ret, 0) + TH_LOG("Test failed with error code: %d", ret); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c b/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c index f686e605359c..35bb8a807bb9 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c @@ -29,14 +29,17 @@ futex_t f1 = FUTEX_INITIALIZER; futex_t f2 = FUTEX_INITIALIZER; -int child_ret = 0; +int child_ret; void *blocking_child(void *arg) { + struct __test_metadata *_metadata = (struct __test_metadata *)arg; + child_ret = futex_wait(&f1, f1, NULL, FUTEX_PRIVATE_FLAG); if (child_ret < 0) { child_ret = -errno; - ksft_exit_fail_msg("futex_wait\n"); + ASSERT_EQ(child_ret, 0) + TH_LOG("futex_wait failed: %s", strerror(errno)); } return (void *)&child_ret; } @@ -46,8 +49,8 @@ TEST(requeue_pi_mismatched_ops) pthread_t child; int ret; - if (pthread_create(&child, NULL, blocking_child, NULL)) - ksft_exit_fail_msg("pthread_create\n"); + ASSERT_EQ(pthread_create(&child, NULL, blocking_child, _metadata), 0) + TH_LOG("pthread_create failed"); /* Allow the child to block in the kernel. */ sleep(1); @@ -67,27 +70,33 @@ TEST(requeue_pi_mismatched_ops) * FUTEX_WAKE. */ ret = futex_wake(&f1, 1, FUTEX_PRIVATE_FLAG); - if (ret == 1) + if (ret == 1) { ret = 0; - else if (ret < 0) - ksft_exit_fail_msg("futex_wake\n"); - else - ksft_exit_fail_msg("futex_wake did not wake the child\n"); + } else if (ret < 0) { + ASSERT_GE(ret, 0) + TH_LOG("futex_wake failed: %s", strerror(errno)); + } else { + ASSERT_TRUE(0) + TH_LOG("futex_wake did not wake the child"); + } } else { - ksft_exit_fail_msg("futex_cmp_requeue_pi\n"); + ASSERT_TRUE(0) + TH_LOG("futex_cmp_requeue_pi failed with unexpected errno: %s", strerror(errno)); } } else if (ret > 0) { - ksft_test_result_fail("futex_cmp_requeue_pi failed to detect the mismatch\n"); + EXPECT_EQ(ret, 0) + TH_LOG("futex_cmp_requeue_pi failed to detect the mismatch"); } else { - ksft_exit_fail_msg("futex_cmp_requeue_pi found no waiters\n"); + ASSERT_TRUE(0) + TH_LOG("futex_cmp_requeue_pi found no waiters"); } pthread_join(child, NULL); - if (!ret && !child_ret) - ksft_test_result_pass("futex_requeue_pi_mismatched_ops passed\n"); - else - ksft_test_result_pass("futex_requeue_pi_mismatched_ops failed\n"); + EXPECT_EQ(ret, 0) + TH_LOG("Test failed: ret=%d", ret); + EXPECT_EQ(child_ret, 0) + TH_LOG("Child failed: child_ret=%d", child_ret); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c b/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c index a18ccae73eb1..4933612a7b55 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c @@ -35,10 +35,10 @@ futex_t f1 = FUTEX_INITIALIZER; futex_t f2 = FUTEX_INITIALIZER; atomic_t requeued = ATOMIC_INITIALIZER; -int waiter_ret = 0; +int waiter_ret; -int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg, - int policy, int prio) +int create_rt_thread(struct __test_metadata *_metadata, pthread_t *pth, void*(*func)(void *), + void *arg, int policy, int prio) { struct sched_param schedp; pthread_attr_t attr; @@ -48,45 +48,47 @@ int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg, memset(&schedp, 0, sizeof(schedp)); ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); - if (ret) - ksft_exit_fail_msg("pthread_attr_setinheritsched\n"); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_attr_setinheritsched failed"); ret = pthread_attr_setschedpolicy(&attr, policy); - if (ret) - ksft_exit_fail_msg("pthread_attr_setschedpolicy\n"); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_attr_setschedpolicy failed"); schedp.sched_priority = prio; ret = pthread_attr_setschedparam(&attr, &schedp); - if (ret) - ksft_exit_fail_msg("pthread_attr_setschedparam\n"); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_attr_setschedparam failed"); ret = pthread_create(pth, &attr, func, arg); - if (ret) - ksft_exit_fail_msg("pthread_create\n"); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_create failed"); return 0; } void handle_signal(int signo) { - ksft_print_dbg_msg("signal received %s requeue\n", - requeued.val ? "after" : "prior to"); + printf("INFO: signal received %s requeue\n", requeued.val ? "after" : "prior to"); } void *waiterfn(void *arg) { + struct __test_metadata *_metadata = (struct __test_metadata *)arg; unsigned int old_val; int res; - ksft_print_dbg_msg("Waiter running\n"); - ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2); + TH_LOG("Waiter running"); + TH_LOG("Calling FUTEX_LOCK_PI on f2=%x @ %p", f2, &f2); old_val = f1; res = futex_wait_requeue_pi(&f1, old_val, &(f2), NULL, FUTEX_PRIVATE_FLAG); if (!requeued.val || errno != EWOULDBLOCK) { - ksft_test_result_fail("unexpected return from futex_wait_requeue_pi: %d (%s)\n", - res, strerror(errno)); - ksft_print_dbg_msg("w2:futex: %x\n", f2); + EXPECT_TRUE(0) { + TH_LOG("unexpected return from futex_wait_requeue_pi: %d (%s)", + res, strerror(errno)); + } + TH_LOG("w2:futex: %x", f2); if (!res) futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); } @@ -94,7 +96,6 @@ void *waiterfn(void *arg) pthread_exit(NULL); } - TEST(futex_requeue_pi_signal_restart) { unsigned int old_val; @@ -105,19 +106,17 @@ TEST(futex_requeue_pi_signal_restart) sa.sa_handler = handle_signal; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; - if (sigaction(SIGUSR1, &sa, NULL)) - ksft_exit_fail_msg("sigaction\n"); + ASSERT_EQ(sigaction(SIGUSR1, &sa, NULL), 0) + TH_LOG("sigaction failed"); - ksft_print_dbg_msg("m1:f2: %x\n", f2); - ksft_print_dbg_msg("Creating waiter\n"); - res = create_rt_thread(&waiter, waiterfn, NULL, SCHED_FIFO, 1); - if (res) - ksft_exit_fail_msg("Creating waiting thread failed"); + TH_LOG("m1:f2: %x", f2); + TH_LOG("Creating waiter"); + create_rt_thread(_metadata, &waiter, waiterfn, _metadata, SCHED_FIFO, 1); - ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2); - ksft_print_dbg_msg("m2:f2: %x\n", f2); + TH_LOG("Calling FUTEX_LOCK_PI on f2=%x @ %p", f2, &f2); + TH_LOG("m2:f2: %x", f2); futex_lock_pi(&f2, 0, 0, FUTEX_PRIVATE_FLAG); - ksft_print_dbg_msg("m3:f2: %x\n", f2); + TH_LOG("m3:f2: %x", f2); while (1) { /* @@ -125,11 +124,11 @@ TEST(futex_requeue_pi_signal_restart) * restart futex_wait_requeue_pi() in the kernel. Wait for the * waiter to block on f1 again. */ - ksft_print_dbg_msg("Issuing SIGUSR1 to waiter\n"); + TH_LOG("Issuing SIGUSR1 to waiter"); pthread_kill(waiter, SIGUSR1); usleep(DELAY_US); - ksft_print_dbg_msg("Requeueing waiter via FUTEX_CMP_REQUEUE_PI\n"); + TH_LOG("Requeueing waiter via FUTEX_CMP_REQUEUE_PI"); old_val = f1; res = futex_cmp_requeue_pi(&f1, old_val, &(f2), 1, 0, FUTEX_PRIVATE_FLAG); @@ -143,10 +142,11 @@ TEST(futex_requeue_pi_signal_restart) atomic_set(&requeued, 1); break; } else if (res < 0) { - ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n"); + ASSERT_GE(res, 0) + TH_LOG("FUTEX_CMP_REQUEUE_PI failed: %s", strerror(errno)); } } - ksft_print_dbg_msg("m4:f2: %x\n", f2); + TH_LOG("m4:f2: %x", f2); /* * Signal the waiter after requeue, waiter should return from @@ -154,14 +154,14 @@ TEST(futex_requeue_pi_signal_restart) * futex_unlock_pi() can't happen before the signal wakeup is detected * in the kernel. */ - ksft_print_dbg_msg("Issuing SIGUSR1 to waiter\n"); + TH_LOG("Issuing SIGUSR1 to waiter"); pthread_kill(waiter, SIGUSR1); - ksft_print_dbg_msg("Waiting for waiter to return\n"); + TH_LOG("Waiting for waiter to return"); pthread_join(waiter, NULL); - ksft_print_dbg_msg("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", f2, &f2); + TH_LOG("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p", f2, &f2); futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); - ksft_print_dbg_msg("m5:f2: %x\n", f2); + TH_LOG("m5:f2: %x", f2); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/futex/functional/futex_wait.c b/tools/testing/selftests/futex/functional/futex_wait.c index 7b8879409007..5292f5d2c3b5 100644 --- a/tools/testing/selftests/futex/functional/futex_wait.c +++ b/tools/testing/selftests/futex/functional/futex_wait.c @@ -5,10 +5,11 @@ * futex cmp requeue test by André Almeida <andrealmeid@collabora.com> */ +#include <fcntl.h> #include <pthread.h> +#include <stdlib.h> #include <sys/shm.h> #include <sys/mman.h> -#include <fcntl.h> #include "futextest.h" #include "kselftest_harness.h" @@ -19,125 +20,157 @@ void *futex; +struct waiter_args { + struct __test_metadata *_metadata; + unsigned int flags; +}; + static void *waiterfn(void *arg) { + struct waiter_args *args = (struct waiter_args *)arg; + struct __test_metadata *_metadata = args->_metadata; struct timespec to; - unsigned int flags = 0; - - if (arg) - flags = *((unsigned int *) arg); + int res; to.tv_sec = 0; to.tv_nsec = timeout_ns; - if (futex_wait(futex, 0, &to, flags)) - printf("waiter failed errno %d\n", errno); + res = futex_wait(futex, 0, &to, args->flags); + if (res) { + EXPECT_EQ(res, 0) + TH_LOG("waiter failed errno %d: %s", errno, strerror(errno)); + } + free(args); return NULL; } TEST(private_futex) { - unsigned int flags = FUTEX_PRIVATE_FLAG; + struct waiter_args *args = malloc(sizeof(*args)); u_int32_t f_private = 0; pthread_t waiter; int res; + args->_metadata = _metadata; + args->flags = FUTEX_PRIVATE_FLAG; futex = &f_private; /* Testing a private futex */ - ksft_print_dbg_msg("Calling private futex_wait on futex: %p\n", futex); - if (pthread_create(&waiter, NULL, waiterfn, (void *) &flags)) - ksft_exit_fail_msg("pthread_create failed\n"); + TH_LOG("Calling private futex_wait on futex: %p", futex); + ASSERT_EQ(pthread_create(&waiter, NULL, waiterfn, args), 0) + TH_LOG("pthread_create failed"); usleep(WAKE_WAIT_US); - ksft_print_dbg_msg("Calling private futex_wake on futex: %p\n", futex); + TH_LOG("Calling private futex_wake on futex: %p", futex); res = futex_wake(futex, 1, FUTEX_PRIVATE_FLAG); - if (res != 1) { - ksft_test_result_fail("futex_wake private returned: %d %s\n", - errno, strerror(errno)); - } else { - ksft_test_result_pass("futex_wake private succeeds\n"); - } + EXPECT_EQ(res, 1) + TH_LOG("futex_wake private returned: %d %s", res, res < 0 ? strerror(errno) : ""); + + pthread_join(waiter, NULL); } TEST(anon_page) { + struct waiter_args *args = malloc(sizeof(*args)); u_int32_t *shared_data; pthread_t waiter; int res, shm_id; + args->_metadata = _metadata; + args->flags = 0; + /* Testing an anon page shared memory */ shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666); if (shm_id < 0) { - if (errno == ENOSYS) - ksft_exit_skip("shmget syscall not supported\n"); - perror("shmget"); - exit(1); + if (errno == ENOSYS) { + free(args); + SKIP(return, "shmget syscall not supported"); + } + ASSERT_GE(shm_id, 0) + TH_LOG("shmget failed: %s", strerror(errno)); } shared_data = shmat(shm_id, NULL, 0); + if (shared_data == (void *)-1) { + free(args); + ASSERT_NE(shared_data, (void *)-1) + TH_LOG("shmat failed: %s", strerror(errno)); + } *shared_data = 0; futex = shared_data; - ksft_print_dbg_msg("Calling shared (page anon) futex_wait on futex: %p\n", futex); - if (pthread_create(&waiter, NULL, waiterfn, NULL)) - ksft_exit_fail_msg("pthread_create failed\n"); + TH_LOG("Calling shared (page anon) futex_wait on futex: %p", futex); + ASSERT_EQ(pthread_create(&waiter, NULL, waiterfn, args), 0) + TH_LOG("pthread_create failed"); usleep(WAKE_WAIT_US); - ksft_print_dbg_msg("Calling shared (page anon) futex_wake on futex: %p\n", futex); + TH_LOG("Calling shared (page anon) futex_wake on futex: %p", futex); res = futex_wake(futex, 1, 0); - if (res != 1) { - ksft_test_result_fail("futex_wake shared (page anon) returned: %d %s\n", - errno, strerror(errno)); - } else { - ksft_test_result_pass("futex_wake shared (page anon) succeeds\n"); + EXPECT_EQ(res, 1) { + TH_LOG("futex_wake shared (page anon) returned: %d %s", + res, res < 0 ? strerror(errno) : ""); } + pthread_join(waiter, NULL); shmdt(shared_data); } TEST(file_backed) { + struct waiter_args *args = malloc(sizeof(*args)); u_int32_t f_private = 0; pthread_t waiter; int res, fd; void *shm; + args->_metadata = _metadata; + args->flags = 0; + /* Testing a file backed shared memory */ - fd = open(SHM_PATH, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); - if (fd < 0) - ksft_exit_fail_msg("open\n"); + fd = open(SHM_PATH, O_RDWR | O_CREAT, 0600); + if (fd < 0) { + free(args); + ASSERT_GE(fd, 0) + TH_LOG("open failed: %s", strerror(errno)); + } - if (ftruncate(fd, sizeof(f_private))) - ksft_exit_fail_msg("ftruncate\n"); + if (ftruncate(fd, sizeof(f_private))) { + free(args); + close(fd); + ASSERT_TRUE(0) + TH_LOG("ftruncate failed: %s", strerror(errno)); + } shm = mmap(NULL, sizeof(f_private), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (shm == MAP_FAILED) - ksft_exit_fail_msg("mmap\n"); + if (shm == MAP_FAILED) { + free(args); + close(fd); + ASSERT_NE(shm, MAP_FAILED) + TH_LOG("mmap failed: %s", strerror(errno)); + } memcpy(shm, &f_private, sizeof(f_private)); futex = shm; - ksft_print_dbg_msg("Calling shared (file backed) futex_wait on futex: %p\n", futex); - if (pthread_create(&waiter, NULL, waiterfn, NULL)) - ksft_exit_fail_msg("pthread_create failed\n"); + TH_LOG("Calling shared (file backed) futex_wait on futex: %p", futex); + ASSERT_EQ(pthread_create(&waiter, NULL, waiterfn, args), 0) + TH_LOG("pthread_create failed"); usleep(WAKE_WAIT_US); - ksft_print_dbg_msg("Calling shared (file backed) futex_wake on futex: %p\n", futex); + TH_LOG("Calling shared (file backed) futex_wake on futex: %p", futex); res = futex_wake(shm, 1, 0); - if (res != 1) { - ksft_test_result_fail("futex_wake shared (file backed) returned: %d %s\n", - errno, strerror(errno)); - } else { - ksft_test_result_pass("futex_wake shared (file backed) succeeds\n"); + EXPECT_EQ(res, 1) { + TH_LOG("futex_wake shared (file backed) returned: %d %s", + res, res < 0 ? strerror(errno) : ""); } + pthread_join(waiter, NULL); munmap(shm, sizeof(f_private)); remove(SHM_PATH); close(fd); diff --git a/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c b/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c index 2a749f9b14eb..a9f7a02e3a0b 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c +++ b/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c @@ -26,6 +26,7 @@ #include <pthread.h> #include <libgen.h> #include <signal.h> +#include <string.h> #include "futextest.h" #include "kselftest_harness.h" @@ -41,17 +42,22 @@ struct timespec wait_timeout = { .tv_sec = 5, .tv_nsec = 0}; void *thr_futex_wait(void *arg) { + struct __test_metadata *_metadata = (struct __test_metadata *)arg; int ret; - ksft_print_dbg_msg("futex wait\n"); + TH_LOG("futex wait"); ret = futex_wait(&val, 1, &wait_timeout, 0); - if (ret && errno != EWOULDBLOCK && errno != ETIMEDOUT) - ksft_exit_fail_msg("futex error.\n"); + if (ret && errno != EWOULDBLOCK && errno != ETIMEDOUT) { + ASSERT_TRUE(0) + TH_LOG("futex error: %s", strerror(errno)); + } - if (ret && errno == ETIMEDOUT) - ksft_exit_fail_msg("waiter timedout\n"); + if (ret && errno == ETIMEDOUT) { + ASSERT_TRUE(0) + TH_LOG("waiter timedout"); + } - ksft_print_dbg_msg("futex_wait: ret = %d, errno = %d\n", ret, errno); + TH_LOG("futex_wait: ret = %d, errno = %d", ret, errno); return NULL; } @@ -61,22 +67,20 @@ TEST(wait_private_mapped_file) pthread_t thr; int res; - res = pthread_create(&thr, NULL, thr_futex_wait, NULL); - if (res < 0) - ksft_exit_fail_msg("pthread_create error\n"); + res = pthread_create(&thr, NULL, thr_futex_wait, _metadata); + ASSERT_EQ(res, 0) + TH_LOG("pthread_create error"); - ksft_print_dbg_msg("wait a while\n"); + TH_LOG("wait a while"); usleep(WAKE_WAIT_US); val = 2; res = futex_wake(&val, 1, 0); - ksft_print_dbg_msg("futex_wake %d\n", res); - if (res != 1) - ksft_exit_fail_msg("FUTEX_WAKE didn't find the waiting thread.\n"); + TH_LOG("futex_wake %d", res); + EXPECT_EQ(res, 1) + TH_LOG("FUTEX_WAKE didn't find the waiting thread"); - ksft_print_dbg_msg("join\n"); + TH_LOG("join"); pthread_join(thr, NULL); - - ksft_test_result_pass("wait_private_mapped_file"); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c index 674dd13af421..6e6e770fe96a 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c +++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c @@ -31,53 +31,47 @@ static pthread_barrier_t barrier; */ void *get_pi_lock(void *arg) { + struct __test_metadata *_metadata = (struct __test_metadata *)arg; int ret; volatile futex_t lock = 0; ret = futex_lock_pi(&futex_pi, NULL, 0, 0); - if (ret != 0) - ksft_exit_fail_msg("futex_lock_pi failed\n"); + ASSERT_EQ(ret, 0) + TH_LOG("futex_lock_pi failed"); pthread_barrier_wait(&barrier); /* Blocks forever */ ret = futex_wait(&lock, 0, NULL, 0); - ksft_exit_fail_msg("futex_wait failed\n"); + ASSERT_TRUE(0) + TH_LOG("futex_wait returned unexpectedly: %d", ret); return NULL; } -/* - * Check if the function returned the expected error - */ -static void test_timeout(int res, char *test_name, int err) -{ - if (!res || errno != err) { - ksft_test_result_fail("%s returned %d\n", test_name, - res < 0 ? errno : res); - } else { - ksft_test_result_pass("%s succeeds\n", test_name); - } -} - -/* - * Calculate absolute timeout and correct overflow - */ -static int futex_get_abs_timeout(clockid_t clockid, struct timespec *to, - long timeout_ns) -{ - if (clock_gettime(clockid, to)) - ksft_exit_fail_msg("clock_gettime failed\n"); - - to->tv_nsec += timeout_ns; - - if (to->tv_nsec >= 1000000000) { - to->tv_sec++; - to->tv_nsec -= 1000000000; - } - - return 0; -} +#define TEST_TIMEOUT(_res, _test_name, _err) do { \ + if ((_res) < 0 && errno == ENOSYS && (_err) != ENOSYS) { \ + SKIP(return, "%s is not supported (ENOSYS)", _test_name); \ + } \ + EXPECT_EQ((_res), -1) \ + TH_LOG("%s returned unexpected result: %d", _test_name, (_res));\ + if ((_res) == -1) { \ + EXPECT_EQ(errno, (_err)) { \ + TH_LOG("%s returned unexpected errno: %d (expected %d)",\ + _test_name, errno, (_err)); \ + } \ + } \ +} while (0) + +#define GET_ABS_TIMEOUT(_clockid, _to, _timeout_ns) do { \ + ASSERT_EQ(clock_gettime((_clockid), (_to)), 0) \ + TH_LOG("clock_gettime failed"); \ + (_to)->tv_nsec += (_timeout_ns); \ + if ((_to)->tv_nsec >= 1000000000) { \ + (_to)->tv_sec++; \ + (_to)->tv_nsec -= 1000000000; \ + } \ +} while (0) TEST(wait_bitset) { @@ -90,19 +84,17 @@ TEST(wait_bitset) to.tv_nsec = timeout_ns; res = futex_wait(&f1, f1, &to, 0); - test_timeout(res, "futex_wait relative", ETIMEDOUT); + TEST_TIMEOUT(res, "futex_wait relative", ETIMEDOUT); /* FUTEX_WAIT_BITSET with CLOCK_REALTIME */ - if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) - ksft_test_result_error("get_time error"); + GET_ABS_TIMEOUT(CLOCK_REALTIME, &to, timeout_ns); res = futex_wait_bitset(&f1, f1, &to, 1, FUTEX_CLOCK_REALTIME); - test_timeout(res, "futex_wait_bitset realtime", ETIMEDOUT); + TEST_TIMEOUT(res, "futex_wait_bitset realtime", ETIMEDOUT); /* FUTEX_WAIT_BITSET with CLOCK_MONOTONIC */ - if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns)) - ksft_test_result_error("get_time error"); + GET_ABS_TIMEOUT(CLOCK_MONOTONIC, &to, timeout_ns); res = futex_wait_bitset(&f1, f1, &to, 1, 0); - test_timeout(res, "futex_wait_bitset monotonic", ETIMEDOUT); + TEST_TIMEOUT(res, "futex_wait_bitset monotonic", ETIMEDOUT); } TEST(requeue_pi) @@ -112,17 +104,14 @@ TEST(requeue_pi) int res; /* FUTEX_WAIT_REQUEUE_PI with CLOCK_REALTIME */ - if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) - ksft_test_result_error("get_time error"); + GET_ABS_TIMEOUT(CLOCK_REALTIME, &to, timeout_ns); res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, FUTEX_CLOCK_REALTIME); - test_timeout(res, "futex_wait_requeue_pi realtime", ETIMEDOUT); + TEST_TIMEOUT(res, "futex_wait_requeue_pi realtime", ETIMEDOUT); /* FUTEX_WAIT_REQUEUE_PI with CLOCK_MONOTONIC */ - if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns)) - ksft_test_result_error("get_time error"); + GET_ABS_TIMEOUT(CLOCK_MONOTONIC, &to, timeout_ns); res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, 0); - test_timeout(res, "futex_wait_requeue_pi monotonic", ETIMEDOUT); - + TEST_TIMEOUT(res, "futex_wait_requeue_pi monotonic", ETIMEDOUT); } TEST(lock_pi) @@ -133,7 +122,8 @@ TEST(lock_pi) /* Create a thread that will lock forever so any waiter will timeout */ pthread_barrier_init(&barrier, NULL, 2); - pthread_create(&thread, NULL, get_pi_lock, NULL); + ASSERT_EQ(pthread_create(&thread, NULL, get_pi_lock, _metadata), 0) + TH_LOG("pthread_create failed"); /* Wait until the other thread calls futex_lock_pi() */ pthread_barrier_wait(&barrier); @@ -149,14 +139,13 @@ TEST(lock_pi) * time or your time machine) the monotonic clock value is always * smaller than realtime and the syscall will timeout immediately. */ - if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) - ksft_test_result_error("get_time error"); + GET_ABS_TIMEOUT(CLOCK_REALTIME, &to, timeout_ns); res = futex_lock_pi(&futex_pi, &to, 0, 0); - test_timeout(res, "futex_lock_pi realtime", ETIMEDOUT); + TEST_TIMEOUT(res, "futex_lock_pi realtime", ETIMEDOUT); /* Test operations that don't support FUTEX_CLOCK_REALTIME */ res = futex_lock_pi(&futex_pi, NULL, 0, FUTEX_CLOCK_REALTIME); - test_timeout(res, "futex_lock_pi invalid timeout flag", ENOSYS); + TEST_TIMEOUT(res, "futex_lock_pi invalid timeout flag", ENOSYS); } TEST(waitv) @@ -171,17 +160,18 @@ TEST(waitv) struct timespec to; int res; + if (!is_futex_waitv_supported()) + SKIP(return, "futex_waitv syscall not supported"); + /* futex_waitv with CLOCK_MONOTONIC */ - if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns)) - ksft_test_result_error("get_time error"); + GET_ABS_TIMEOUT(CLOCK_MONOTONIC, &to, timeout_ns); res = futex_waitv(&waitv, 1, 0, &to, CLOCK_MONOTONIC); - test_timeout(res, "futex_waitv monotonic", ETIMEDOUT); + TEST_TIMEOUT(res, "futex_waitv monotonic", ETIMEDOUT); /* futex_waitv with CLOCK_REALTIME */ - if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) - ksft_test_result_error("get_time error"); + GET_ABS_TIMEOUT(CLOCK_REALTIME, &to, timeout_ns); res = futex_waitv(&waitv, 1, 0, &to, CLOCK_REALTIME); - test_timeout(res, "futex_waitv realtime", ETIMEDOUT); + TEST_TIMEOUT(res, "futex_waitv realtime", ETIMEDOUT); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c b/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c index b07d68a67f31..bbffc23e0006 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c +++ b/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c @@ -17,17 +17,18 @@ * *****************************************************************************/ +#include <errno.h> +#include <libgen.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <linux/futex.h> #include <sys/mman.h> #include <syscall.h> #include <sys/types.h> #include <sys/stat.h> -#include <unistd.h> -#include <errno.h> -#include <linux/futex.h> -#include <libgen.h> #include "futextest.h" #include "kselftest_harness.h" @@ -40,6 +41,7 @@ void *buf; void *wait_thread(void *arg) { + struct __test_metadata *_metadata = (struct __test_metadata *)arg; int res; child_ret = true; @@ -47,7 +49,8 @@ void *wait_thread(void *arg) child_blocked = 0; if (res != 0 && errno != EWOULDBLOCK) { - ksft_exit_fail_msg("futex failure\n"); + EXPECT_EQ(res, 0) + TH_LOG("futex failure: %s", strerror(errno)); child_ret = false; } pthread_exit(NULL); @@ -63,21 +66,23 @@ TEST(futex_wait_uninitialized_heap) buf = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); - if (buf == (void *)-1) - ksft_exit_fail_msg("mmap\n"); + ASSERT_NE(buf, MAP_FAILED) + TH_LOG("mmap failed: %s", strerror(errno)); - ret = pthread_create(&thr, NULL, wait_thread, NULL); - if (ret) - ksft_exit_fail_msg("pthread_create\n"); + ret = pthread_create(&thr, NULL, wait_thread, _metadata); + ASSERT_EQ(ret, 0) + TH_LOG("pthread_create failed"); - ksft_print_dbg_msg("waiting %dus for child to return\n", WAIT_US); + TH_LOG("waiting %dus for child to return", WAIT_US); usleep(WAIT_US); - if (child_blocked) - ksft_test_result_fail("child blocked in kernel\n"); + EXPECT_EQ(child_blocked, 0) + TH_LOG("child blocked in kernel"); + EXPECT_TRUE(child_ret) + TH_LOG("child error"); - if (!child_ret) - ksft_test_result_fail("child error\n"); + pthread_join(thr, NULL); + munmap(buf, page_size); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c index 9ff936ecf164..ab039dda3e51 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c +++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c @@ -28,20 +28,20 @@ #define timeout_ns 100000 + TEST(futex_wait_wouldblock) { struct timespec to = {.tv_sec = 0, .tv_nsec = timeout_ns}; futex_t f1 = FUTEX_INITIALIZER; int res; - ksft_print_dbg_msg("Calling futex_wait on f1: %u @ %p with val=%u\n", f1, &f1, f1+1); + TH_LOG("Calling futex_wait on f1: %u @ %p with val=%u", f1, &f1, f1+1); res = futex_wait(&f1, f1+1, &to, FUTEX_PRIVATE_FLAG); - if (!res || errno != EWOULDBLOCK) { - ksft_test_result_fail("futex_wait returned: %d %s\n", - res ? errno : res, - res ? strerror(errno) : ""); - } else { - ksft_test_result_pass("futex_wait\n"); + EXPECT_EQ(res, -1) + TH_LOG("futex_wait returned unexpected result: %d", res); + if (res == -1) { + EXPECT_EQ(errno, EWOULDBLOCK) + TH_LOG("futex_wait returned unexpected errno: %d", errno); } } @@ -57,8 +57,11 @@ TEST(futex_waitv_wouldblock) }; int res; - if (clock_gettime(CLOCK_MONOTONIC, &to)) - ksft_exit_fail_msg("clock_gettime failed %d\n", errno); + if (!is_futex_waitv_supported()) + SKIP(return, "futex_waitv syscall not supported"); + + ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) + TH_LOG("clock_gettime failed"); to.tv_nsec += timeout_ns; @@ -67,14 +70,13 @@ TEST(futex_waitv_wouldblock) to.tv_nsec -= 1000000000; } - ksft_print_dbg_msg("Calling futex_waitv on f1: %u @ %p with val=%u\n", f1, &f1, f1+1); + TH_LOG("Calling futex_waitv on f1: %u @ %p with val=%u", f1, &f1, f1+1); res = futex_waitv(&waitv, 1, 0, &to, CLOCK_MONOTONIC); - if (!res || errno != EWOULDBLOCK) { - ksft_test_result_fail("futex_waitv returned: %d %s\n", - res ? errno : res, - res ? strerror(errno) : ""); - } else { - ksft_test_result_pass("futex_waitv\n"); + EXPECT_EQ(res, -1) + TH_LOG("futex_waitv returned unexpected result: %d", res); + if (res == -1) { + EXPECT_EQ(errno, EWOULDBLOCK) + TH_LOG("futex_waitv returned unexpected errno: %d", errno); } } diff --git a/tools/testing/selftests/futex/functional/futex_waitv.c b/tools/testing/selftests/futex/functional/futex_waitv.c index b5ada9fdb26f..4858d5faeecf 100644 --- a/tools/testing/selftests/futex/functional/futex_waitv.c +++ b/tools/testing/selftests/futex/functional/futex_waitv.c @@ -25,24 +25,26 @@ static struct futex_waitv waitv[NR_FUTEXES]; u_int32_t futexes[NR_FUTEXES] = {0}; + void *waiterfn(void *arg) { + struct __test_metadata *_metadata = (struct __test_metadata *)arg; struct timespec to; int res; /* setting absolute timeout for futex2 */ - if (clock_gettime(CLOCK_MONOTONIC, &to)) - ksft_exit_fail_msg("gettime64 failed\n"); + ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) + TH_LOG("gettime64 failed"); to.tv_sec++; res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); if (res < 0) { - ksft_test_result_fail("futex_waitv returned: %d %s\n", - errno, strerror(errno)); - } else if (res != NR_FUTEXES - 1) { - ksft_test_result_fail("futex_waitv returned: %d, expecting %d\n", - res, NR_FUTEXES - 1); + EXPECT_EQ(res, NR_FUTEXES - 1) + TH_LOG("futex_waitv failed: %s", strerror(errno)); + } else { + EXPECT_EQ(res, NR_FUTEXES - 1) + TH_LOG("futex_waitv returned %d, expected %d", res, NR_FUTEXES - 1); } return NULL; @@ -53,6 +55,9 @@ TEST(private_waitv) pthread_t waiter; int res, i; + if (!is_futex_waitv_supported()) + SKIP(return, "futex_waitv syscall not supported"); + for (i = 0; i < NR_FUTEXES; i++) { waitv[i].uaddr = (uintptr_t)&futexes[i]; waitv[i].flags = FUTEX_32 | FUTEX_PRIVATE_FLAG; @@ -61,19 +66,14 @@ TEST(private_waitv) } /* Private waitv */ - if (pthread_create(&waiter, NULL, waiterfn, NULL)) - ksft_exit_fail_msg("pthread_create failed\n"); + ASSERT_EQ(pthread_create(&waiter, NULL, waiterfn, _metadata), 0) + TH_LOG("pthread_create failed"); usleep(WAKE_WAIT_US); res = futex_wake(u64_to_ptr(waitv[NR_FUTEXES - 1].uaddr), 1, FUTEX_PRIVATE_FLAG); - if (res != 1) { - ksft_test_result_fail("futex_wake private returned: %d %s\n", - res ? errno : res, - res ? strerror(errno) : ""); - } else { - ksft_test_result_pass("futex_waitv private\n"); - } + EXPECT_EQ(res, 1) + TH_LOG("futex_wake private returned: %d %s", res, res < 0 ? strerror(errno) : ""); } TEST(shared_waitv) @@ -81,15 +81,18 @@ TEST(shared_waitv) pthread_t waiter; int res, i; + if (!is_futex_waitv_supported()) + SKIP(return, "futex_waitv syscall not supported"); + /* Shared waitv */ for (i = 0; i < NR_FUTEXES; i++) { int shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666); if (shm_id < 0) { if (errno == ENOSYS) - ksft_exit_skip("shmget syscall not supported\n"); - perror("shmget"); - exit(1); + SKIP(return, "shmget syscall not supported"); + ASSERT_GE(shm_id, 0) + TH_LOG("shmget failed"); } unsigned int *shared_data = shmat(shm_id, NULL, 0); @@ -101,19 +104,14 @@ TEST(shared_waitv) waitv[i].__reserved = 0; } - if (pthread_create(&waiter, NULL, waiterfn, NULL)) - ksft_exit_fail_msg("pthread_create failed\n"); + ASSERT_EQ(pthread_create(&waiter, NULL, waiterfn, _metadata), 0) + TH_LOG("pthread_create failed"); usleep(WAKE_WAIT_US); res = futex_wake(u64_to_ptr(waitv[NR_FUTEXES - 1].uaddr), 1, 0); - if (res != 1) { - ksft_test_result_fail("futex_wake shared returned: %d %s\n", - res ? errno : res, - res ? strerror(errno) : ""); - } else { - ksft_test_result_pass("futex_waitv shared\n"); - } + EXPECT_EQ(res, 1) + TH_LOG("futex_wake shared returned: %d %s", res, res < 0 ? strerror(errno) : ""); for (i = 0; i < NR_FUTEXES; i++) shmdt(u64_to_ptr(waitv[i].uaddr)); @@ -124,21 +122,24 @@ TEST(invalid_flag) struct timespec to; int res; + if (!is_futex_waitv_supported()) + SKIP(return, "futex_waitv syscall not supported"); + /* Testing a waiter without FUTEX_32 flag */ waitv[0].flags = FUTEX_PRIVATE_FLAG; - if (clock_gettime(CLOCK_MONOTONIC, &to)) - ksft_exit_fail_msg("gettime64 failed\n"); + ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) + TH_LOG("gettime64 failed"); to.tv_sec++; res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); - if (res == EINVAL) { - ksft_test_result_fail("futex_waitv private returned: %d %s\n", - res ? errno : res, - res ? strerror(errno) : ""); - } else { - ksft_test_result_pass("futex_waitv without FUTEX_32\n"); + + EXPECT_EQ(res, -1) + TH_LOG("futex_waitv returned unexpected result: %d", res); + if (res == -1) { + EXPECT_EQ(errno, EINVAL) + TH_LOG("futex_waitv returned unexpected errno: %d", errno); } } @@ -147,22 +148,25 @@ TEST(unaligned_address) struct timespec to; int res; + if (!is_futex_waitv_supported()) + SKIP(return, "futex_waitv syscall not supported"); + /* Testing a waiter with an unaligned address */ waitv[0].flags = FUTEX_PRIVATE_FLAG | FUTEX_32; waitv[0].uaddr = 1; - if (clock_gettime(CLOCK_MONOTONIC, &to)) - ksft_exit_fail_msg("gettime64 failed\n"); + ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) + TH_LOG("gettime64 failed"); to.tv_sec++; res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); - if (res == EINVAL) { - ksft_test_result_fail("futex_wake private returned: %d %s\n", - res ? errno : res, - res ? strerror(errno) : ""); - } else { - ksft_test_result_pass("futex_waitv with an unaligned address\n"); + + EXPECT_EQ(res, -1) + TH_LOG("futex_waitv returned unexpected result: %d", res); + if (res == -1) { + EXPECT_EQ(errno, EINVAL) + TH_LOG("futex_waitv returned unexpected errno: %d", errno); } } @@ -171,36 +175,39 @@ TEST(null_address) struct timespec to; int res; + if (!is_futex_waitv_supported()) + SKIP(return, "futex_waitv syscall not supported"); + /* Testing a NULL address for waiters.uaddr */ waitv[0].uaddr = 0x00000000; - if (clock_gettime(CLOCK_MONOTONIC, &to)) - ksft_exit_fail_msg("gettime64 failed\n"); + ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) + TH_LOG("gettime64 failed"); to.tv_sec++; res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); - if (res == EINVAL) { - ksft_test_result_fail("futex_waitv private returned: %d %s\n", - res ? errno : res, - res ? strerror(errno) : ""); - } else { - ksft_test_result_pass("futex_waitv NULL address in waitv.uaddr\n"); + + EXPECT_EQ(res, -1) + TH_LOG("futex_waitv returned unexpected result: %d", res); + if (res == -1) { + EXPECT_EQ(errno, EINVAL) + TH_LOG("futex_waitv returned unexpected errno: %d", errno); } /* Testing a NULL address for *waiters */ - if (clock_gettime(CLOCK_MONOTONIC, &to)) - ksft_exit_fail_msg("gettime64 failed\n"); + ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) + TH_LOG("gettime64 failed"); to.tv_sec++; res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); - if (res == EINVAL) { - ksft_test_result_fail("futex_waitv private returned: %d %s\n", - res ? errno : res, - res ? strerror(errno) : ""); - } else { - ksft_test_result_pass("futex_waitv NULL address in *waiters\n"); + + EXPECT_EQ(res, -1) + TH_LOG("futex_waitv returned unexpected result: %d", res); + if (res == -1) { + EXPECT_EQ(errno, EINVAL) + TH_LOG("futex_waitv returned unexpected errno: %d", errno); } } @@ -209,19 +216,22 @@ TEST(invalid_clockid) struct timespec to; int res; + if (!is_futex_waitv_supported()) + SKIP(return, "futex_waitv syscall not supported"); + /* Testing an invalid clockid */ - if (clock_gettime(CLOCK_MONOTONIC, &to)) - ksft_exit_fail_msg("gettime64 failed\n"); + ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) + TH_LOG("gettime64 failed"); to.tv_sec++; res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_TAI); - if (res == EINVAL) { - ksft_test_result_fail("futex_waitv private returned: %d %s\n", - res ? errno : res, - res ? strerror(errno) : ""); - } else { - ksft_test_result_pass("futex_waitv invalid clockid\n"); + + EXPECT_EQ(res, -1) + TH_LOG("futex_waitv returned unexpected result: %d", res); + if (res == -1) { + EXPECT_EQ(errno, EINVAL) + TH_LOG("futex_waitv returned unexpected errno: %d", errno); } } diff --git a/tools/testing/selftests/futex/functional/robust_list.c b/tools/testing/selftests/futex/functional/robust_list.c index b3fab60181d5..87217c549361 100644 --- a/tools/testing/selftests/futex/functional/robust_list.c +++ b/tools/testing/selftests/futex/functional/robust_list.c @@ -25,24 +25,24 @@ #define _GNU_SOURCE #include "futextest.h" -#include "../../kselftest_harness.h" +#include "kselftest_harness.h" #include <dlfcn.h> #include <errno.h> #include <pthread.h> #include <signal.h> -#include <stdint.h> #include <stdatomic.h> #include <stdbool.h> #include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> #include <sys/auxv.h> #include <sys/mman.h> #include <sys/wait.h> #define STACK_SIZE (1024 * 1024) - #define FUTEX_TIMEOUT 3 - #define SLEEP_US 100 #if __SIZEOF_LONG__ == 8 @@ -71,30 +71,46 @@ static int sys_futex_robust_unlock(_Atomic(uint32_t) *uaddr, unsigned int op, in * 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) { + struct child_args *cargs = malloc(sizeof(*cargs)); char *stack; pid_t pid; + 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; } @@ -123,7 +139,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; @@ -183,21 +199,21 @@ 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); @@ -220,7 +236,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; @@ -234,7 +250,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); @@ -251,9 +267,8 @@ 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"); } /* @@ -279,8 +294,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__); } /* @@ -307,20 +320,20 @@ 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 @@ -350,7 +363,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); @@ -365,28 +378,27 @@ 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); @@ -404,7 +416,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; @@ -414,7 +426,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); @@ -427,21 +439,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 lock_struct *locks = cargs->arg; struct robust_list_head head; int i; + free(cargs); + set_list(&head); for (i = 0; i < CHILD_NR; i++) { @@ -460,22 +472,21 @@ 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; } @@ -495,18 +506,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; @@ -515,22 +528,23 @@ 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) { - static struct robust_list_head head; - struct lock_struct a, b, c; + struct child_args *cargs = arg; + struct __test_metadata *_metadata = cargs->_metadata; + static struct lock_struct a, b, c; + struct robust_list_head head; 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; @@ -552,14 +566,15 @@ 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"); } /* diff --git a/tools/testing/selftests/futex/functional/run.sh b/tools/testing/selftests/futex/functional/run.sh index e88545c06d57..d1a681b798bd 100755 --- a/tools/testing/selftests/futex/functional/run.sh +++ b/tools/testing/selftests/futex/functional/run.sh @@ -51,3 +51,6 @@ echo echo ./futex_numa_mpol + +echo +./futex_lock_pi_exiting diff --git a/tools/testing/selftests/futex/include/futex2test.h b/tools/testing/selftests/futex/include/futex2test.h index 1f625b39948a..53e88b60ac6d 100644 --- a/tools/testing/selftests/futex/include/futex2test.h +++ b/tools/testing/selftests/futex/include/futex2test.h @@ -5,7 +5,9 @@ * Copyright 2021 Collabora Ltd. */ #include <linux/time_types.h> +#include <errno.h> #include <stdint.h> +#include <stdbool.h> #define u64_to_ptr(x) ((void *)(uintptr_t)(x)) @@ -96,3 +98,11 @@ static inline int futex2_wake(void *uaddr, int nr, unsigned int flags) { return syscall(__NR_futex_wake, uaddr, ~0U, nr, flags); } + +static inline bool is_futex_waitv_supported(void) +{ + struct timespec ts = {0, 0}; + int res = futex_waitv(NULL, 0, 0, &ts, CLOCK_MONOTONIC); + + return !(res < 0 && errno == ENOSYS); +} diff --git a/tools/testing/selftests/futex/include/futex_thread.h b/tools/testing/selftests/futex/include/futex_thread.h new file mode 100644 index 000000000000..a90882960264 --- /dev/null +++ b/tools/testing/selftests/futex/include/futex_thread.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _FUTEX_THREAD_H +#define _FUTEX_THREAD_H +#include <errno.h> +#include <pthread.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "kselftest_harness.h" + +#define USEC_PER_SEC 1000000L +#define WAIT_FOR_THREAD_SECS 1 +#define WAIT_FOR_THREAD_USECS (WAIT_FOR_THREAD_SECS * USEC_PER_SEC) +#define WAIT_THREAD_RETRIES 100 + +struct futex_thread { + pthread_t thread; + pthread_barrier_t barrier; + pid_t tid; + int (*threadfn)(void *arg); + void *arg; + int retval; +}; + +static inline int __wait_for_thread(FILE *fp, struct __test_metadata *_metadata) +{ + unsigned int sleep_time_us = WAIT_FOR_THREAD_USECS / WAIT_THREAD_RETRIES; + char buf[80] = ""; + + for (int i = 0; i < WAIT_THREAD_RETRIES; i++) { + if (!fgets(buf, sizeof(buf), fp)) + return EIO; + if (!strncmp(buf, "futex", 5)) + return 0; + usleep(sleep_time_us); + rewind(fp); + } + + TH_LOG("/proc/$PID/wchan contains \"%s\". Trying to continue.", buf); + return 0; +} + +static void *__futex_thread_fn(void *arg) +{ + struct futex_thread *t = arg; + + t->tid = gettid(); + pthread_barrier_wait(&t->barrier); + t->retval = t->threadfn(t->arg); + return NULL; +} + +/** + * futex_wait_for_thread - Wait for the child thread to sleep in the futex context + * @t: Thread handle. + * @_metadata: Test metadata for TH_LOG() context + */ +static inline int futex_wait_for_thread(struct futex_thread *t, struct __test_metadata *_metadata) +{ + char fname[80]; + FILE *fp; + int res; + + snprintf(fname, sizeof(fname), "/proc/%d/wchan", t->tid); + fp = fopen(fname, "r"); + if (!fp) { + /* If /proc/... is not available, sleep */ + if (errno != ENOENT) + return errno; + TH_LOG("/proc/$PID/wchan not accessible, continue with sleep()"); + sleep(WAIT_FOR_THREAD_SECS); + return 0; + } + + res = __wait_for_thread(fp, _metadata); + fclose(fp); + return res; +} + +/** + * futex_thread_create - Create a new thread for testing. + * @t: The handle of the newly created thread. + * @threadfn: The new thread starts execution by invoking threadfn + * @arg: The parameters passed to threadfn. + */ +static inline int futex_thread_create(struct futex_thread *t, int (*threadfn)(void *), void *arg) +{ + pthread_barrier_init(&t->barrier, NULL, 2); + + t->tid = 0; + t->threadfn = threadfn; + t->arg = arg; + + if (pthread_create(&t->thread, NULL, __futex_thread_fn, t) < 0) { + int ret = errno; + pthread_barrier_destroy(&t->barrier); + return ret; + } + + pthread_barrier_wait(&t->barrier); + return 0; +} + +/** + * futex_thread_destroy - Wait for and reclaim the resources of the thread. + * @t: Thread handle. + */ +static inline int futex_thread_destroy(struct futex_thread *t) +{ + pthread_join(t->thread, NULL); + pthread_barrier_destroy(&t->barrier); + return t->retval; +} + +#endif diff --git a/tools/testing/selftests/rseq/rseq-x86-thread-pointer.h b/tools/testing/selftests/rseq/rseq-x86-thread-pointer.h index d3133587d996..5a29d6bec51f 100644 --- a/tools/testing/selftests/rseq/rseq-x86-thread-pointer.h +++ b/tools/testing/selftests/rseq/rseq-x86-thread-pointer.h @@ -8,13 +8,11 @@ #ifndef _RSEQ_X86_THREAD_POINTER #define _RSEQ_X86_THREAD_POINTER -#include <features.h> - #ifdef __cplusplus extern "C" { #endif -#if __GNUC_PREREQ (11, 1) +#if __GNUC__ > 11 || (__GNUC__ == 11 && __GNUC_MINOR__ >= 1) static inline void *rseq_thread_pointer(void) { return __builtin_thread_pointer(); |
