summaryrefslogtreecommitdiff
path: root/tools/testing
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2026-07-26 10:42:11 +0200
committerIngo Molnar <mingo@kernel.org>2026-07-26 10:42:12 +0200
commit2906e2925afd666d9434d1506746a81606e736d0 (patch)
treec1ccba89766ce125335c5cba5d76ddacbe7f9734 /tools/testing
parent66958e251d167c6531bfecc46f460b7d8fb01d73 (diff)
parentd1c656df36d00aad9c810e563f918dbbaf41a7b9 (diff)
downloadlinux-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')
-rw-r--r--tools/testing/selftests/futex/functional/.gitignore1
-rw-r--r--tools/testing/selftests/futex/functional/Makefile6
-rw-r--r--tools/testing/selftests/futex/functional/futex_lock_pi_exiting.c263
-rw-r--r--tools/testing/selftests/futex/functional/futex_numa_mpol.c127
-rw-r--r--tools/testing/selftests/futex/functional/futex_priv_hash.c163
-rw-r--r--tools/testing/selftests/futex/functional/futex_requeue.c67
-rw-r--r--tools/testing/selftests/futex/functional/futex_requeue_pi.c157
-rw-r--r--tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c41
-rw-r--r--tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c74
-rw-r--r--tools/testing/selftests/futex/functional/futex_wait.c127
-rw-r--r--tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c36
-rw-r--r--tools/testing/selftests/futex/functional/futex_wait_timeout.c108
-rw-r--r--tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c35
-rw-r--r--tools/testing/selftests/futex/functional/futex_wait_wouldblock.c34
-rw-r--r--tools/testing/selftests/futex/functional/futex_waitv.c146
-rw-r--r--tools/testing/selftests/futex/functional/robust_list.c183
-rwxr-xr-xtools/testing/selftests/futex/functional/run.sh3
-rw-r--r--tools/testing/selftests/futex/include/futex2test.h10
-rw-r--r--tools/testing/selftests/futex/include/futex_thread.h117
-rw-r--r--tools/testing/selftests/rseq/rseq-x86-thread-pointer.h4
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();