* [PATCH 0/2] selftests/futex: Migrate functional tests to harness and fix validations
@ 2026-05-25 7:57 Wake Liu
2026-05-25 7:57 ` [PATCH 1/2] selftests/futex: Migrate functional tests to harness Wake Liu
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Wake Liu @ 2026-05-25 7:57 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Shuah Khan, linux-kselftest
Cc: Peter Zijlstra, Darren Hart, Davidlohr Bueso, André Almeida,
Carlos Llamas, Edward Liaw, linux-kernel, wakel
This series refactors futex functional tests to use the kselftest_harness.h
framework, fixing inconsistencies on older kernels where some syscalls
are missing, and corrects the validation logic in waitv negative tests.
Patch 1 migrates wait_timeout, waitv, and wait_wouldblock to the harness.
Patch 2 corrects the validation logic in waitv negative tests.
Wake Liu (2):
selftests/futex: Migrate functional tests to harness
selftests/futex: Correct validation logic in waitv
.../futex/functional/futex_wait_timeout.c | 101 ++++++-------
.../futex/functional/futex_wait_wouldblock.c | 40 ++---
.../selftests/futex/functional/futex_waitv.c | 142 +++++++++---------
3 files changed, 133 insertions(+), 150 deletions(-)
--
2.54.0.746.g67dd491aae-goog
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/2] selftests/futex: Migrate functional tests to harness
2026-05-25 7:57 [PATCH 0/2] selftests/futex: Migrate functional tests to harness and fix validations Wake Liu
@ 2026-05-25 7:57 ` Wake Liu
2026-05-25 18:37 ` André Almeida
2026-05-25 7:57 ` [PATCH 2/2] selftests/futex: Correct validation logic in waitv Wake Liu
2026-05-26 1:06 ` [PATCH v2 0/2] selftests/futex: Migrate functional tests to harness and fix validations Wake Liu
2 siblings, 1 reply; 7+ messages in thread
From: Wake Liu @ 2026-05-25 7:57 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Shuah Khan, linux-kselftest
Cc: Peter Zijlstra, Darren Hart, Davidlohr Bueso, André Almeida,
Carlos Llamas, Edward Liaw, linux-kernel, wakel
Currently, multiple futex functional tests (wait_timeout, waitv,
wait_wouldblock) mix low-level ksft_* logging and result APIs with
the kselftest_harness.h framework. On older kernels where system calls
like futex_waitv are missing (returning -ENOSYS), this mixed usage
triggers framework inconsistencies, causing the test to fail with:
"Illegal usage of low-level ksft APIs in harness test".
Address this by completely refactoring these tests to exclusively use
the high-level kselftest_harness.h framework (gtest-like API), mapping
all low-level calls to native harness macros:
- Non-fatal assertions: Replace ksft_test_result_fail() with EXPECT_EQ()
and EXPECT_NE().
- Fatal assertions: Replace ksft_exit_fail_msg() with ASSERT_EQ() and
ASSERT_TRUE() to immediately terminate execution upon setup failure.
- Test skipping: Replace ksft_exit_skip() with the graceful SKIP() macro
combined with early runtime availability checks for sys_futex_waitv.
- Debug logging: Replace ksft_print_dbg_msg() with TH_LOG() to inherit
automatic file/line context.
- Success reporting: Remove explicit ksft_test_result_pass() calls,
deferring to automated harness completion reporting.
Additionally:
- Fix a critical SIGSEGV crash in early syscall probing logic caused by
passing a NULL pointer to inline user-space timespec conversions.
- Introduce TEST_TIMEOUT() and GET_ABS_TIMEOUT() macros in wait_timeout
to encapsulate assertions while preserving source line attribution.
- Pass test metadata (_metadata) into secondary threads to allow native
harness assertions during concurrent execution.
Signed-off-by: Wake Liu <wakel@google.com>
---
.../futex/functional/futex_wait_timeout.c | 101 +++++++----------
.../futex/functional/futex_wait_wouldblock.c | 40 ++++---
.../selftests/futex/functional/futex_waitv.c | 107 +++++++++---------
3 files changed, 118 insertions(+), 130 deletions(-)
diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
index 674dd13af421..89d6a7daeda4 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
@@ -31,53 +31,43 @@ 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 +80,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 +100,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 +118,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 +135,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)
@@ -172,16 +157,14 @@ TEST(waitv)
int res;
/* 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_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
index 9ff936ecf164..4fd517404f1f 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
@@ -28,21 +28,26 @@
#define timeout_ns 100000
+static 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);
+}
+
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);
}
TEST(futex_waitv_wouldblock)
@@ -57,8 +62,10 @@ 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,15 +74,12 @@ 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);
}
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/futex/functional/futex_waitv.c b/tools/testing/selftests/futex/functional/futex_waitv.c
index b5ada9fdb26f..7bed76a1caed 100644
--- a/tools/testing/selftests/futex/functional/futex_waitv.c
+++ b/tools/testing/selftests/futex/functional/futex_waitv.c
@@ -25,24 +25,31 @@
static struct futex_waitv waitv[NR_FUTEXES];
u_int32_t futexes[NR_FUTEXES] = {0};
+static 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);
+}
+
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 +60,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 +71,15 @@ 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 +87,17 @@ 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 +109,15 @@ 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 +128,21 @@ 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",
+ EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
res ? errno : res,
res ? strerror(errno) : "");
- } else {
- ksft_test_result_pass("futex_waitv without FUTEX_32\n");
}
}
@@ -147,22 +151,22 @@ 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",
+ EXPECT_TRUE(0) TH_LOG("futex_wake private returned: %d %s",
res ? errno : res,
res ? strerror(errno) : "");
- } else {
- ksft_test_result_pass("futex_waitv with an unaligned address\n");
}
}
@@ -171,36 +175,33 @@ 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",
+ EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
res ? errno : res,
res ? strerror(errno) : "");
- } else {
- ksft_test_result_pass("futex_waitv NULL address in waitv.uaddr\n");
}
/* 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",
+ EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
res ? errno : res,
res ? strerror(errno) : "");
- } else {
- ksft_test_result_pass("futex_waitv NULL address in *waiters\n");
}
}
@@ -209,19 +210,19 @@ 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",
+ EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
res ? errno : res,
res ? strerror(errno) : "");
- } else {
- ksft_test_result_pass("futex_waitv invalid clockid\n");
}
}
--
2.54.0.746.g67dd491aae-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/2] selftests/futex: Correct validation logic in waitv
2026-05-25 7:57 [PATCH 0/2] selftests/futex: Migrate functional tests to harness and fix validations Wake Liu
2026-05-25 7:57 ` [PATCH 1/2] selftests/futex: Migrate functional tests to harness Wake Liu
@ 2026-05-25 7:57 ` Wake Liu
2026-05-26 1:06 ` [PATCH v2 0/2] selftests/futex: Migrate functional tests to harness and fix validations Wake Liu
2 siblings, 0 replies; 7+ messages in thread
From: Wake Liu @ 2026-05-25 7:57 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Shuah Khan, linux-kselftest
Cc: Peter Zijlstra, Darren Hart, Davidlohr Bueso, André Almeida,
Carlos Llamas, Edward Liaw, linux-kernel, wakel
In futex_waitv negative tests (invalid_flag, unaligned_address, etc.),
test results were previously evaluated as:
if (res == EINVAL)
Since sys_futex_waitv returns -1 on error and sets errno, direct positive
comparisons against res are always false, causing tests to silently pass
regardless of real errors.
Correct these validations to assert EXPECT_EQ(res, -1) and compare errno
directly against expected constants.
Signed-off-by: Wake Liu <wakel@google.com>
---
.../selftests/futex/functional/futex_waitv.c | 45 +++++++++----------
1 file changed, 20 insertions(+), 25 deletions(-)
diff --git a/tools/testing/selftests/futex/functional/futex_waitv.c b/tools/testing/selftests/futex/functional/futex_waitv.c
index 7bed76a1caed..9dc0f9c1f22a 100644
--- a/tools/testing/selftests/futex/functional/futex_waitv.c
+++ b/tools/testing/selftests/futex/functional/futex_waitv.c
@@ -139,11 +139,10 @@ TEST(invalid_flag)
to.tv_sec++;
res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
- if (res == EINVAL) {
- EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
- res ? errno : res,
- res ? strerror(errno) : "");
- }
+ 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);
}
TEST(unaligned_address)
@@ -163,11 +162,10 @@ TEST(unaligned_address)
to.tv_sec++;
res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
- if (res == EINVAL) {
- EXPECT_TRUE(0) TH_LOG("futex_wake private returned: %d %s",
- res ? errno : res,
- res ? strerror(errno) : "");
- }
+ 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);
}
TEST(null_address)
@@ -186,11 +184,10 @@ TEST(null_address)
to.tv_sec++;
res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
- if (res == EINVAL) {
- EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
- res ? errno : res,
- res ? strerror(errno) : "");
- }
+ 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 */
ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) TH_LOG("gettime64 failed");
@@ -198,11 +195,10 @@ TEST(null_address)
to.tv_sec++;
res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
- if (res == EINVAL) {
- EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
- res ? errno : res,
- res ? strerror(errno) : "");
- }
+ 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);
}
TEST(invalid_clockid)
@@ -219,11 +215,10 @@ TEST(invalid_clockid)
to.tv_sec++;
res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_TAI);
- if (res == EINVAL) {
- EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
- res ? errno : res,
- res ? strerror(errno) : "");
- }
+ 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);
}
TEST_HARNESS_MAIN
--
2.54.0.746.g67dd491aae-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] selftests/futex: Migrate functional tests to harness
2026-05-25 7:57 ` [PATCH 1/2] selftests/futex: Migrate functional tests to harness Wake Liu
@ 2026-05-25 18:37 ` André Almeida
0 siblings, 0 replies; 7+ messages in thread
From: André Almeida @ 2026-05-25 18:37 UTC (permalink / raw)
To: Wake Liu
Cc: Shuah Khan, Thomas Gleixner, Peter Zijlstra, Darren Hart,
Ingo Molnar, linux-kselftest, Davidlohr Bueso, Carlos Llamas,
Edward Liaw, linux-kernel
Hi Wake,
Thanks for the help fixing my mix of kselftest_harness and ksft_() :)
Em 25/05/2026 04:57, Wake Liu escreveu:
> Currently, multiple futex functional tests (wait_timeout, waitv,
> wait_wouldblock) mix low-level ksft_* logging and result APIs with
> the kselftest_harness.h framework. On older kernels where system calls
> like futex_waitv are missing (returning -ENOSYS), this mixed usage
> triggers framework inconsistencies, causing the test to fail with:
> "Illegal usage of low-level ksft APIs in harness test".
>
> Address this by completely refactoring these tests to exclusively use
> the high-level kselftest_harness.h framework (gtest-like API), mapping
> all low-level calls to native harness macros:
> - Non-fatal assertions: Replace ksft_test_result_fail() with EXPECT_EQ()
> and EXPECT_NE().
> - Fatal assertions: Replace ksft_exit_fail_msg() with ASSERT_EQ() and
> ASSERT_TRUE() to immediately terminate execution upon setup failure.
> - Test skipping: Replace ksft_exit_skip() with the graceful SKIP() macro
> combined with early runtime availability checks for sys_futex_waitv.
> - Debug logging: Replace ksft_print_dbg_msg() with TH_LOG() to inherit
> automatic file/line context.
> - Success reporting: Remove explicit ksft_test_result_pass() calls,
> deferring to automated harness completion reporting.
>
> Additionally:
> - Fix a critical SIGSEGV crash in early syscall probing logic caused by
> passing a NULL pointer to inline user-space timespec conversions.
> - Introduce TEST_TIMEOUT() and GET_ABS_TIMEOUT() macros in wait_timeout
> to encapsulate assertions while preserving source line attribution.
> - Pass test metadata (_metadata) into secondary threads to allow native
> harness assertions during concurrent execution.
>
> Signed-off-by: Wake Liu <wakel@google.com>
> ---
> .../futex/functional/futex_wait_timeout.c | 101 +++++++----------
> .../futex/functional/futex_wait_wouldblock.c | 40 ++++---
> .../selftests/futex/functional/futex_waitv.c | 107 +++++++++---------
> 3 files changed, 118 insertions(+), 130 deletions(-)
>
> diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
> index 674dd13af421..89d6a7daeda4 100644
> --- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c
> +++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
[...]
> -}
> +#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)
Perhaps we should always check for ETIMEDOUT and threat the only time we
check for ENOSYS here as a special case. But no strong preference.
[...]
>
> TEST(waitv)
> @@ -172,16 +157,14 @@ TEST(waitv)
> int res;
>
For the other futex_waitv tests you call is_futex_waitv_supported(), why
don't you call for this one as well?
> /* 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_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
> index 9ff936ecf164..4fd517404f1f 100644
> --- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
> +++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
> @@ -28,21 +28,26 @@
>
> #define timeout_ns 100000
>
> +static 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);
> +}
> +
[...]
> +static 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);
> +}
If you want to have this function in two tests, why don't you add it to
include/futex2test.h?
Also, even if not super common, we keep adding new futex features (like
syscall(__NR_futex_{wait, wake}), maybe there could be a more generic
function to skip tests if a given syscall, op or flag is not available?
André
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 0/2] selftests/futex: Migrate functional tests to harness and fix validations
2026-05-25 7:57 [PATCH 0/2] selftests/futex: Migrate functional tests to harness and fix validations Wake Liu
2026-05-25 7:57 ` [PATCH 1/2] selftests/futex: Migrate functional tests to harness Wake Liu
2026-05-25 7:57 ` [PATCH 2/2] selftests/futex: Correct validation logic in waitv Wake Liu
@ 2026-05-26 1:06 ` Wake Liu
2026-05-26 1:06 ` [PATCH v2 1/2] selftests/futex: Migrate functional tests to harness Wake Liu
2026-05-26 1:06 ` [PATCH v2 2/2] selftests/futex: Correct validation logic in waitv Wake Liu
2 siblings, 2 replies; 7+ messages in thread
From: Wake Liu @ 2026-05-26 1:06 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Shuah Khan, linux-kselftest
Cc: Peter Zijlstra, Darren Hart, Davidlohr Bueso, André Almeida,
Carlos Llamas, linux-kernel, wakel
This is v2 of the series to migrate futex functional tests to the harness.
Changes in v2:
- Move is_futex_waitv_supported() helper to a shared header (include/futex2test.h)
to avoid duplication, as suggested by André Almeida.
- Call is_futex_waitv_supported() check at the start of TEST(waitv) in
futex_wait_timeout.c for consistency.
Original description:
This series refactors futex functional tests to use the kselftest_harness.h
framework, fixing inconsistencies on older kernels where some syscalls
are missing, and corrects the validation logic in waitv negative tests.
Wake Liu (2):
selftests/futex: Migrate functional tests to harness
selftests/futex: Correct validation logic in waitv
.../futex/functional/futex_wait_timeout.c | 104 ++++++--------
.../futex/functional/futex_wait_wouldblock.c | 33 ++---
.../selftests/futex/functional/futex_waitv.c | 135 ++++++++----------
.../selftests/futex/include/futex2test.h | 10 ++
4 files changed, 132 insertions(+), 150 deletions(-)
--
2.54.0.746.g67dd491aae-goog
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/2] selftests/futex: Migrate functional tests to harness
2026-05-26 1:06 ` [PATCH v2 0/2] selftests/futex: Migrate functional tests to harness and fix validations Wake Liu
@ 2026-05-26 1:06 ` Wake Liu
2026-05-26 1:06 ` [PATCH v2 2/2] selftests/futex: Correct validation logic in waitv Wake Liu
1 sibling, 0 replies; 7+ messages in thread
From: Wake Liu @ 2026-05-26 1:06 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Shuah Khan, linux-kselftest
Cc: Peter Zijlstra, Darren Hart, Davidlohr Bueso, André Almeida,
Carlos Llamas, linux-kernel, wakel
Currently, multiple futex functional tests (wait_timeout, waitv,
wait_wouldblock) mix low-level ksft_* logging and result APIs with
the kselftest_harness.h framework. On older kernels where system calls
like futex_waitv are missing (returning -ENOSYS), this mixed usage
triggers framework inconsistencies, causing the test to fail with:
"Illegal usage of low-level ksft APIs in harness test".
Address this by completely refactoring these tests to exclusively use
the high-level kselftest_harness.h framework (gtest-like API), mapping
all low-level calls to native harness macros:
- Non-fatal assertions: Replace ksft_test_result_fail() with EXPECT_EQ()
and EXPECT_NE().
- Fatal assertions: Replace ksft_exit_fail_msg() with ASSERT_EQ() and
ASSERT_TRUE() to immediately terminate execution upon setup failure.
- Test skipping: Replace ksft_exit_skip() with the graceful SKIP() macro
combined with early runtime availability checks for sys_futex_waitv.
- Debug logging: Replace ksft_print_dbg_msg() with TH_LOG() to inherit
automatic file/line context.
- Success reporting: Remove explicit ksft_test_result_pass() calls,
deferring to automated harness completion reporting.
Additionally:
- Fix a critical SIGSEGV crash in early syscall probing logic caused by
passing a NULL pointer to inline user-space timespec conversions.
- Introduce TEST_TIMEOUT() and GET_ABS_TIMEOUT() macros in wait_timeout
to encapsulate assertions while preserving source line attribution.
- Pass test metadata (_metadata) into secondary threads to allow native
harness assertions during concurrent execution.
Signed-off-by: Wake Liu <wakel@google.com>
---
.../futex/functional/futex_wait_timeout.c | 104 ++++++++----------
.../futex/functional/futex_wait_wouldblock.c | 33 +++---
.../selftests/futex/functional/futex_waitv.c | 100 ++++++++---------
.../selftests/futex/include/futex2test.h | 10 ++
4 files changed, 117 insertions(+), 130 deletions(-)
diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
index 674dd13af421..a79ec51d0f34 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
@@ -31,53 +31,43 @@ 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 +80,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 +100,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 +118,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 +135,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 +156,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_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
index 9ff936ecf164..5683569e83d9 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
@@ -28,21 +28,19 @@
#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);
}
TEST(futex_waitv_wouldblock)
@@ -57,8 +55,10 @@ 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,15 +67,12 @@ 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);
}
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/futex/functional/futex_waitv.c b/tools/testing/selftests/futex/functional/futex_waitv.c
index b5ada9fdb26f..3053f69cf0c3 100644
--- a/tools/testing/selftests/futex/functional/futex_waitv.c
+++ b/tools/testing/selftests/futex/functional/futex_waitv.c
@@ -25,24 +25,24 @@
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 +53,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 +64,15 @@ 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 +80,17 @@ 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 +102,15 @@ 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 +121,21 @@ 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",
+ EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
res ? errno : res,
res ? strerror(errno) : "");
- } else {
- ksft_test_result_pass("futex_waitv without FUTEX_32\n");
}
}
@@ -147,22 +144,22 @@ 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",
+ EXPECT_TRUE(0) TH_LOG("futex_wake private returned: %d %s",
res ? errno : res,
res ? strerror(errno) : "");
- } else {
- ksft_test_result_pass("futex_waitv with an unaligned address\n");
}
}
@@ -171,36 +168,33 @@ 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",
+ EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
res ? errno : res,
res ? strerror(errno) : "");
- } else {
- ksft_test_result_pass("futex_waitv NULL address in waitv.uaddr\n");
}
/* 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",
+ EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
res ? errno : res,
res ? strerror(errno) : "");
- } else {
- ksft_test_result_pass("futex_waitv NULL address in *waiters\n");
}
}
@@ -209,19 +203,19 @@ 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",
+ EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
res ? errno : res,
res ? strerror(errno) : "");
- } else {
- ksft_test_result_pass("futex_waitv invalid clockid\n");
}
}
diff --git a/tools/testing/selftests/futex/include/futex2test.h b/tools/testing/selftests/futex/include/futex2test.h
index 1f625b39948a..d157ba45eabc 100644
--- a/tools/testing/selftests/futex/include/futex2test.h
+++ b/tools/testing/selftests/futex/include/futex2test.h
@@ -6,6 +6,8 @@
*/
#include <linux/time_types.h>
#include <stdint.h>
+#include <stdbool.h>
+#include <errno.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);
+}
--
2.54.0.746.g67dd491aae-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 2/2] selftests/futex: Correct validation logic in waitv
2026-05-26 1:06 ` [PATCH v2 0/2] selftests/futex: Migrate functional tests to harness and fix validations Wake Liu
2026-05-26 1:06 ` [PATCH v2 1/2] selftests/futex: Migrate functional tests to harness Wake Liu
@ 2026-05-26 1:06 ` Wake Liu
1 sibling, 0 replies; 7+ messages in thread
From: Wake Liu @ 2026-05-26 1:06 UTC (permalink / raw)
To: Thomas Gleixner, Ingo Molnar, Shuah Khan, linux-kselftest
Cc: Peter Zijlstra, Darren Hart, Davidlohr Bueso, André Almeida,
Carlos Llamas, linux-kernel, wakel
In futex_waitv negative tests (invalid_flag, unaligned_address, etc.),
test results were previously evaluated as:
if (res == EINVAL)
Since sys_futex_waitv returns -1 on error and sets errno, direct positive
comparisons against res are always false, causing tests to silently pass
regardless of real errors.
Correct these validations to assert EXPECT_EQ(res, -1) and compare errno
directly against expected constants.
Signed-off-by: Wake Liu <wakel@google.com>
---
.../selftests/futex/functional/futex_waitv.c | 45 +++++++++----------
1 file changed, 20 insertions(+), 25 deletions(-)
diff --git a/tools/testing/selftests/futex/functional/futex_waitv.c b/tools/testing/selftests/futex/functional/futex_waitv.c
index 3053f69cf0c3..6a6fc49708a2 100644
--- a/tools/testing/selftests/futex/functional/futex_waitv.c
+++ b/tools/testing/selftests/futex/functional/futex_waitv.c
@@ -132,11 +132,10 @@ TEST(invalid_flag)
to.tv_sec++;
res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
- if (res == EINVAL) {
- EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
- res ? errno : res,
- res ? strerror(errno) : "");
- }
+ 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);
}
TEST(unaligned_address)
@@ -156,11 +155,10 @@ TEST(unaligned_address)
to.tv_sec++;
res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
- if (res == EINVAL) {
- EXPECT_TRUE(0) TH_LOG("futex_wake private returned: %d %s",
- res ? errno : res,
- res ? strerror(errno) : "");
- }
+ 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);
}
TEST(null_address)
@@ -179,11 +177,10 @@ TEST(null_address)
to.tv_sec++;
res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
- if (res == EINVAL) {
- EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
- res ? errno : res,
- res ? strerror(errno) : "");
- }
+ 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 */
ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) TH_LOG("gettime64 failed");
@@ -191,11 +188,10 @@ TEST(null_address)
to.tv_sec++;
res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
- if (res == EINVAL) {
- EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
- res ? errno : res,
- res ? strerror(errno) : "");
- }
+ 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);
}
TEST(invalid_clockid)
@@ -212,11 +208,10 @@ TEST(invalid_clockid)
to.tv_sec++;
res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_TAI);
- if (res == EINVAL) {
- EXPECT_TRUE(0) TH_LOG("futex_waitv private returned: %d %s",
- res ? errno : res,
- res ? strerror(errno) : "");
- }
+ 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);
}
TEST_HARNESS_MAIN
--
2.54.0.746.g67dd491aae-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-05-26 1:06 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-25 7:57 [PATCH 0/2] selftests/futex: Migrate functional tests to harness and fix validations Wake Liu
2026-05-25 7:57 ` [PATCH 1/2] selftests/futex: Migrate functional tests to harness Wake Liu
2026-05-25 18:37 ` André Almeida
2026-05-25 7:57 ` [PATCH 2/2] selftests/futex: Correct validation logic in waitv Wake Liu
2026-05-26 1:06 ` [PATCH v2 0/2] selftests/futex: Migrate functional tests to harness and fix validations Wake Liu
2026-05-26 1:06 ` [PATCH v2 1/2] selftests/futex: Migrate functional tests to harness Wake Liu
2026-05-26 1:06 ` [PATCH v2 2/2] selftests/futex: Correct validation logic in waitv Wake Liu
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.