[GIT pull] locking/futex for v7.3-rc1

Thomas Gleixner posted 1 patch 1 month, 1 week ago
kernel/futex/core.c                                |   3 +-
.../testing/selftests/futex/functional/.gitignore  |   1 +
tools/testing/selftests/futex/functional/Makefile  |   6 +-
.../futex/functional/futex_lock_pi_exiting.c       | 263 +++++++++++++++++++++
.../selftests/futex/functional/futex_numa_mpol.c   | 127 +++++-----
.../selftests/futex/functional/futex_priv_hash.c   | 163 ++++++-------
.../selftests/futex/functional/futex_requeue.c     |  67 ++++--
.../selftests/futex/functional/futex_requeue_pi.c  | 157 ++++++------
.../functional/futex_requeue_pi_mismatched_ops.c   |  41 ++--
.../functional/futex_requeue_pi_signal_restart.c   |  74 +++---
.../selftests/futex/functional/futex_wait.c        | 127 ++++++----
.../functional/futex_wait_private_mapped_file.c    |  36 +--
.../futex/functional/futex_wait_timeout.c          | 108 ++++-----
.../functional/futex_wait_uninitialized_heap.c     |  35 +--
.../futex/functional/futex_wait_wouldblock.c       |  34 +--
.../selftests/futex/functional/futex_waitv.c       | 146 ++++++------
.../selftests/futex/functional/robust_list.c       | 183 +++++++-------
tools/testing/selftests/futex/functional/run.sh    |   3 +
tools/testing/selftests/futex/include/futex2test.h |  10 +
.../testing/selftests/futex/include/futex_thread.h | 117 +++++++++
.../selftests/rseq/rseq-x86-thread-pointer.h       |   4 +-
21 files changed, 1097 insertions(+), 608 deletions(-)
create mode 100644 tools/testing/selftests/futex/functional/futex_lock_pi_exiting.c
create mode 100644 tools/testing/selftests/futex/include/futex_thread.h
[GIT pull] locking/futex for v7.3-rc1
Posted by Thomas Gleixner 1 month, 1 week ago
Linus,

please pull the latest locking/futex branch from:

   git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking-futex-2026-08-17

up to:  d1c656df36d0: selftests/futex: Give circular-list nodes static storage

Futex updates:

  - A set of improvements to various futex self tests

    - Conversion to the selftest harness

    - Provide and use thread creation and synchronization helpers to reduce
      the dependency on delays, which tend to fail on loaded test systems

    - New tests for validating owner exit scenarios for robust and PI futexes

    - Runtime detect supported features and skip the tests if the kernel
      has no support

    - A few minor fixes

Thanks,

	tglx

------------------>
Ankit Khushwaha (1):
      selftests/futex: Remove static keyword from 'head'

Hisam Mehboob (1):
      selftests/rseq: Replace glibc-specific __GNUC_PREREQ with portable check

Miles Krause (1):
      selftests/futex: Give circular-list nodes static storage

Valery Borovsky (1):
      selftests/futex: Add FUTEX_LOCK_PI owner-exiting coverage

Vasileios Almpanis (1):
      futex: Remove unnecessary NULL check before kvfree()

Wake Liu (13):
      selftests/futex: Migrate functional tests to harness
      selftests/futex: Correct validation logic in waitv
      selftests/futex: Migrate futex_wait to harness
      selftests/futex: Migrate futex_wait_private_mapped_file to harness
      selftests/futex: Migrate futex_wait_uninitialized_heap to harness
      selftests/futex: Migrate futex_requeue to harness
      selftests/futex: Migrate futex_requeue_pi to harness
      selftests/futex: Migrate futex_requeue_pi_mismatched_ops to harness
      selftests/futex: Migrate futex_requeue_pi_signal_restart to harness
      selftests/futex: Migrate futex_numa_mpol to harness
      selftests/futex: Migrate futex_priv_hash to harness
      selftests/futex: Migrate robust_list to harness
      selftests/futex: Dynamically skip unsupported tests

Yuwen Chen (2):
      selftests/futex: Provide thread creation and synchronization helpers
      selftests/futex: Use thread synchronization helpers instead of usleep()


 kernel/futex/core.c                                |   3 +-
 .../testing/selftests/futex/functional/.gitignore  |   1 +
 tools/testing/selftests/futex/functional/Makefile  |   6 +-
 .../futex/functional/futex_lock_pi_exiting.c       | 263 +++++++++++++++++++++
 .../selftests/futex/functional/futex_numa_mpol.c   | 127 +++++-----
 .../selftests/futex/functional/futex_priv_hash.c   | 163 ++++++-------
 .../selftests/futex/functional/futex_requeue.c     |  67 ++++--
 .../selftests/futex/functional/futex_requeue_pi.c  | 157 ++++++------
 .../functional/futex_requeue_pi_mismatched_ops.c   |  41 ++--
 .../functional/futex_requeue_pi_signal_restart.c   |  74 +++---
 .../selftests/futex/functional/futex_wait.c        | 127 ++++++----
 .../functional/futex_wait_private_mapped_file.c    |  36 +--
 .../futex/functional/futex_wait_timeout.c          | 108 ++++-----
 .../functional/futex_wait_uninitialized_heap.c     |  35 +--
 .../futex/functional/futex_wait_wouldblock.c       |  34 +--
 .../selftests/futex/functional/futex_waitv.c       | 146 ++++++------
 .../selftests/futex/functional/robust_list.c       | 183 +++++++-------
 tools/testing/selftests/futex/functional/run.sh    |   3 +
 tools/testing/selftests/futex/include/futex2test.h |  10 +
 .../testing/selftests/futex/include/futex_thread.h | 117 +++++++++
 .../selftests/rseq/rseq-x86-thread-pointer.h       |   4 +-
 21 files changed, 1097 insertions(+), 608 deletions(-)
 create mode 100644 tools/testing/selftests/futex/functional/futex_lock_pi_exiting.c
 create mode 100644 tools/testing/selftests/futex/include/futex_thread.h

diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index 179b26e9c934..233b926f5624 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -1746,8 +1746,7 @@ void futex_hash_free(struct mm_struct *mm)
 	free_percpu(mm->futex.phash.ref);
 	kvfree(mm->futex.phash.hash_new);
 	fph = rcu_dereference_raw(mm->futex.phash.hash);
-	if (fph)
-		kvfree(fph);
+	kvfree(fph);
 }
 
 static bool futex_pivot_pending(struct mm_struct *mm)
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 @@ TEST(priv_hash)
 			 * 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();
Re: [GIT pull] locking/futex for v7.3-rc1
Posted by pr-tracker-bot@kernel.org 1 month, 1 week ago
The pull request you sent on Mon, 17 Aug 2026 13:23:40 +0200:

> git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking-futex-2026-08-17

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/b844715e8aca2929c0a97a32b3a5650496b5872f

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html