A set of updates for futexes and related selftests:

- Plug the ptrace_may_access() race against a concurrent exec() which
     allows to pass the check before the target's process transition in
     exec() by taking a read lock on signal->ext_update_lock.
 
   - A large set of cleanups and enhancement to the futex selftests. The
     bulk of changes is the conversion to the kselftest harness.
 -----BEGIN PGP SIGNATURE-----
 
 iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmjaS8QTHHRnbHhAbGlu
 dXRyb25peC5kZQAKCRCmGPVMDXSYof44EAC7amBzdJlaluVjLTpCV07G8K4bDkzz
 iN5vOm+vrxJlg7kq9L4CffuGKS8yFj7sgNkSexOv/+JUFp/E9WpV3CT+kqJNm71x
 bDPDAFR3reZQ7u27dzpciOTgtCEnq+hqeAovx2c1v2GuESvnTfqWTFSQBLfzqGW7
 leCiaR6VJ87+RB10ZrlxJDdrFnVbKBa48k1dZjy97ivkw+SUS5xSEadSBG2vw7Rd
 C+SQDa8Hhh7xAMJvySWkES6rfzZvC/ubkpq5DmjRZvHu/wS4wDNaKj06wEtbLxEA
 5eaemJVBeEbjC6GNBgba6cUmtrT45PQG7wNFoPmgJsO0nlnfwfmm713CAa9vK+dT
 MTUnGWKSpf0Vj1yHkzLeHer226jqfOe1lA4anRzD6FoZy2XaJiNCymQysfIrIWTG
 BCcA8za3W4PTiUHxDe5JGsbA4f57xDV+bL0SmjaXv5wSo/FX21iJBisBI2FFl531
 9XlgU3ssvUUfQfH6bPIdgcZrWD2Uc9W5aj2dZv+zN6LT8EhvLaQwcM2bxWbuJfWu
 wmU07LMZS9zVrC8B6T3KvrYwnIuETWRZiPhenDy+Shtn8UM1N/MmlNNmAEvdOYue
 V3o1BTm9wDWfSMJAq7q7erjl15QOgUyOpte1gTuzDbqtcWj132q31HDegtxlGIyu
 NLOx8otYdLEOqQ==
 =vUpN
 -----END PGP SIGNATURE-----

Merge tag 'locking-futex-2025-09-29' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull futex updates from Thomas Gleixner:
 "A set of updates for futexes and related selftests:

   - Plug the ptrace_may_access() race against a concurrent exec() which
     allows to pass the check before the target's process transition in
     exec() by taking a read lock on signal->ext_update_lock.

   - A large set of cleanups and enhancement to the futex selftests. The
     bulk of changes is the conversion to the kselftest harness"

* tag 'locking-futex-2025-09-29' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (25 commits)
  selftest/futex: Fix spelling mistake "boundarie" -> "boundary"
  selftests/futex: Remove logging.h file
  selftests/futex: Drop logging.h include from futex_numa
  selftests/futex: Refactor futex_numa_mpol with kselftest_harness.h
  selftests/futex: Refactor futex_priv_hash with kselftest_harness.h
  selftests/futex: Refactor futex_waitv with kselftest_harness.h
  selftests/futex: Refactor futex_requeue with kselftest_harness.h
  selftests/futex: Refactor futex_wait with kselftest_harness.h
  selftests/futex: Refactor futex_wait_private_mapped_file with kselftest_harness.h
  selftests/futex: Refactor futex_wait_unitialized_heap with kselftest_harness.h
  selftests/futex: Refactor futex_wait_wouldblock with kselftest_harness.h
  selftests/futex: Refactor futex_wait_timeout with kselftest_harness.h
  selftests/futex: Refactor futex_requeue_pi_signal_restart with kselftest_harness.h
  selftests/futex: Refactor futex_requeue_pi_mismatched_ops with kselftest_harness.h
  selftests/futex: Refactor futex_requeue_pi with kselftest_harness.h
  selftests: kselftest: Create ksft_print_dbg_msg()
  futex: Don't leak robust_list pointer on exec race
  selftest/futex: Compile also with libnuma < 2.0.16
  selftest/futex: Reintroduce "Memory out of range" numa_mpol's subtest
  selftest/futex: Make the error check more precise for futex_numa_mpol
  ...
This commit is contained in:
Linus Torvalds 2025-09-30 16:07:10 -07:00
commit c574fb2ed7
20 changed files with 576 additions and 1091 deletions

View File

@ -39,6 +39,56 @@ SYSCALL_DEFINE2(set_robust_list, struct robust_list_head __user *, head,
return 0;
}
static inline void __user *futex_task_robust_list(struct task_struct *p, bool compat)
{
#ifdef CONFIG_COMPAT
if (compat)
return p->compat_robust_list;
#endif
return p->robust_list;
}
static void __user *futex_get_robust_list_common(int pid, bool compat)
{
struct task_struct *p = current;
void __user *head;
int ret;
scoped_guard(rcu) {
if (pid) {
p = find_task_by_vpid(pid);
if (!p)
return (void __user *)ERR_PTR(-ESRCH);
}
get_task_struct(p);
}
/*
* Hold exec_update_lock to serialize with concurrent exec()
* so ptrace_may_access() is checked against stable credentials
*/
ret = down_read_killable(&p->signal->exec_update_lock);
if (ret)
goto err_put;
ret = -EPERM;
if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS))
goto err_unlock;
head = futex_task_robust_list(p, compat);
up_read(&p->signal->exec_update_lock);
put_task_struct(p);
return head;
err_unlock:
up_read(&p->signal->exec_update_lock);
err_put:
put_task_struct(p);
return (void __user *)ERR_PTR(ret);
}
/**
* sys_get_robust_list() - Get the robust-futex list head of a task
* @pid: pid of the process [zero for current task]
@ -49,36 +99,14 @@ SYSCALL_DEFINE3(get_robust_list, int, pid,
struct robust_list_head __user * __user *, head_ptr,
size_t __user *, len_ptr)
{
struct robust_list_head __user *head;
unsigned long ret;
struct task_struct *p;
struct robust_list_head __user *head = futex_get_robust_list_common(pid, false);
rcu_read_lock();
ret = -ESRCH;
if (!pid)
p = current;
else {
p = find_task_by_vpid(pid);
if (!p)
goto err_unlock;
}
ret = -EPERM;
if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS))
goto err_unlock;
head = p->robust_list;
rcu_read_unlock();
if (IS_ERR(head))
return PTR_ERR(head);
if (put_user(sizeof(*head), len_ptr))
return -EFAULT;
return put_user(head, head_ptr);
err_unlock:
rcu_read_unlock();
return ret;
}
long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
@ -455,36 +483,14 @@ COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid,
compat_uptr_t __user *, head_ptr,
compat_size_t __user *, len_ptr)
{
struct compat_robust_list_head __user *head;
unsigned long ret;
struct task_struct *p;
struct compat_robust_list_head __user *head = futex_get_robust_list_common(pid, true);
rcu_read_lock();
ret = -ESRCH;
if (!pid)
p = current;
else {
p = find_task_by_vpid(pid);
if (!p)
goto err_unlock;
}
ret = -EPERM;
if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS))
goto err_unlock;
head = p->compat_robust_list;
rcu_read_unlock();
if (IS_ERR(head))
return PTR_ERR(head);
if (put_user(sizeof(*head), len_ptr))
return -EFAULT;
return put_user(ptr_to_compat(head), head_ptr);
err_unlock:
rcu_read_unlock();
return ret;
}
#endif /* CONFIG_COMPAT */

View File

@ -1,12 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
PKG_CONFIG ?= pkg-config
LIBNUMA_TEST = $(shell sh -c "$(PKG_CONFIG) numa --atleast-version 2.0.16 > /dev/null 2>&1 && echo SUFFICIENT || echo NO")
INCLUDES := -I../include -I../../ $(KHDR_INCLUDES)
CFLAGS := $(CFLAGS) -g -O2 -Wall -pthread $(INCLUDES) $(KHDR_INCLUDES)
CFLAGS := $(CFLAGS) -g -O2 -Wall -pthread -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 $(INCLUDES) $(KHDR_INCLUDES) -DLIBNUMA_VER_$(LIBNUMA_TEST)=1
LDLIBS := -lpthread -lrt -lnuma
LOCAL_HDRS := \
../include/futextest.h \
../include/atomic.h \
../include/logging.h
../include/atomic.h
TEST_GEN_PROGS := \
futex_wait_timeout \
futex_wait_wouldblock \

View File

@ -5,9 +5,10 @@
#include <sys/mman.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include "logging.h"
#include "futextest.h"
#include "futex2test.h"

View File

@ -16,9 +16,9 @@
#include <linux/futex.h>
#include <sys/mman.h>
#include "logging.h"
#include "futextest.h"
#include "futex2test.h"
#include "../../kselftest_harness.h"
#define MAX_THREADS 64
@ -77,7 +77,7 @@ static void join_max_threads(void)
}
}
static void __test_futex(void *futex_ptr, int must_fail, unsigned int futex_flags)
static void __test_futex(void *futex_ptr, int err_value, unsigned int futex_flags)
{
int to_wake, ret, i, need_exit = 0;
@ -88,11 +88,17 @@ static void __test_futex(void *futex_ptr, int must_fail, unsigned int futex_flag
do {
ret = futex2_wake(futex_ptr, to_wake, futex_flags);
if (must_fail) {
if (ret < 0)
break;
ksft_exit_fail_msg("futex2_wake(%d, 0x%x) should fail, but didn't\n",
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);
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));
break;
}
if (ret < 0) {
ksft_exit_fail_msg("Failed futex2_wake(%d, 0x%x): %m\n",
@ -106,12 +112,12 @@ static void __test_futex(void *futex_ptr, int must_fail, unsigned int futex_flag
join_max_threads();
for (i = 0; i < MAX_THREADS; i++) {
if (must_fail && thread_args[i].result != -1) {
if (err_value && thread_args[i].result != -1) {
ksft_print_msg("Thread %d should fail but succeeded (%d)\n",
i, thread_args[i].result);
need_exit = 1;
}
if (!must_fail && thread_args[i].result != 0) {
if (!err_value && thread_args[i].result != 0) {
ksft_print_msg("Thread %d failed (%d)\n", i, thread_args[i].result);
need_exit = 1;
}
@ -120,58 +126,30 @@ static void __test_futex(void *futex_ptr, int must_fail, unsigned int futex_flag
ksft_exit_fail_msg("Aborting due to earlier errors.\n");
}
static void test_futex(void *futex_ptr, int must_fail)
static void test_futex(void *futex_ptr, int err_value)
{
__test_futex(futex_ptr, must_fail, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA);
__test_futex(futex_ptr, err_value, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA);
}
static void test_futex_mpol(void *futex_ptr, int must_fail)
static void test_futex_mpol(void *futex_ptr, int err_value)
{
__test_futex(futex_ptr, must_fail, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
__test_futex(futex_ptr, err_value, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
}
static void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
int main(int argc, char *argv[])
TEST(futex_numa_mpol)
{
struct futex32_numa *futex_numa;
int mem_size, i;
void *futex_ptr;
int c;
while ((c = getopt(argc, argv, "chv:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
break;
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
ksft_print_header();
ksft_set_plan(1);
int mem_size;
mem_size = sysconf(_SC_PAGE_SIZE);
futex_ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
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);
/* 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");
@ -182,27 +160,31 @@ int main(int argc, char *argv[])
if (futex_numa->numa == FUTEX_NO_NODE)
ksft_exit_fail_msg("NUMA node is left uninitialized\n");
ksft_print_msg("Memory too small\n");
test_futex(futex_ptr + mem_size - 4, 1);
/* FUTEX2_NUMA futex must be 8-byte aligned */
ksft_print_msg("Mis-aligned futex\n");
test_futex(futex_ptr + mem_size - 4, EINVAL);
ksft_print_msg("Memory out of range\n");
test_futex(futex_ptr + mem_size, 1);
test_futex(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, 1);
test_futex(futex_ptr, EFAULT);
mprotect(futex_ptr, mem_size, PROT_NONE);
ksft_print_msg("Memory, no access\n");
test_futex(futex_ptr, 1);
test_futex(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");
/* MPOL test. Does not work as expected */
for (i = 0; i < 4; i++) {
#ifdef LIBNUMA_VER_SUFFICIENT
for (int i = 0; i < 4; i++) {
unsigned long nodemask;
int ret;
@ -221,15 +203,17 @@ int main(int argc, char *argv[])
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 (0)
test_futex_mpol(futex_numa, 0);
if (futex_numa->numa != i) {
ksft_exit_fail_msg("Returned NUMA node is %d expected %d\n",
futex_numa->numa, i);
}
}
}
ksft_test_result_pass("NUMA MPOL tests passed\n");
ksft_finished();
return 0;
ksft_test_result_pass("futex2 MPOL hints test passed\n");
#else
ksft_test_result_skip("futex2 MPOL hints test requires libnuma 2.0.16+\n");
#endif
munmap(futex_ptr, mem_size * 2);
}
TEST_HARNESS_MAIN

View File

@ -14,7 +14,7 @@
#include <linux/prctl.h>
#include <sys/prctl.h>
#include "logging.h"
#include "../../kselftest_harness.h"
#define MAX_THREADS 64
@ -128,46 +128,14 @@ static void futex_dummy_op(void)
ksft_exit_fail_msg("pthread_mutex_timedlock() did not timeout: %d.\n", ret);
}
static void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -g Test global hash instead intead local immutable \n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
static const char *test_msg_auto_create = "Automatic hash bucket init on thread creation.\n";
static const char *test_msg_auto_inc = "Automatic increase with more than 16 CPUs\n";
int main(int argc, char *argv[])
TEST(priv_hash)
{
int futex_slots1, futex_slotsn, online_cpus;
pthread_mutexattr_t mutex_attr_pi;
int ret, retry = 20;
int c;
while ((c = getopt(argc, argv, "chv:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
break;
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
ksft_print_header();
ksft_set_plan(21);
ret = pthread_mutexattr_init(&mutex_attr_pi);
ret |= pthread_mutexattr_setprotocol(&mutex_attr_pi, PTHREAD_PRIO_INHERIT);
@ -189,14 +157,14 @@ int main(int argc, char *argv[])
if (ret != 0)
ksft_exit_fail_msg("pthread_join() failed: %d, %m\n", ret);
/* First thread, has to initialiaze private hash */
/* 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(test_msg_auto_create);
ksft_exit_fail_msg("%s", test_msg_auto_create);
}
ksft_test_result_pass(test_msg_auto_create);
ksft_test_result_pass("%s", test_msg_auto_create);
online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS + 1);
@ -237,11 +205,11 @@ retry_getslots:
}
ksft_print_msg("Expected increase of hash buckets but got: %d -> %d\n",
futex_slots1, futex_slotsn);
ksft_exit_fail_msg(test_msg_auto_inc);
ksft_exit_fail_msg("%s", test_msg_auto_inc);
}
ksft_test_result_pass(test_msg_auto_inc);
ksft_test_result_pass("%s", test_msg_auto_inc);
} else {
ksft_test_result_skip(test_msg_auto_inc);
ksft_test_result_skip("%s", test_msg_auto_inc);
}
ret = pthread_mutex_unlock(&global_lock);
@ -257,17 +225,17 @@ retry_getslots:
futex_hash_slots_set_verify(2);
join_max_threads();
ksft_test_result(counter == MAX_THREADS, "Created of waited for %d of %d threads\n",
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 reisze must be disabled */
/* Once the user set something, auto resize must be disabled */
ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS);
create_max_threads(thread_lock_fn);
join_max_threads();
ret = futex_hash_slots_get();
ksft_test_result(ret == 2, "No more auto-resize after manaul setting, got %d\n",
ksft_test_result(ret == 2, "No more auto-resize after manual setting, got %d\n",
ret);
futex_hash_slots_set_must_fail(1 << 29);
@ -280,7 +248,7 @@ retry_getslots:
ret = futex_hash_slots_set(0);
ksft_test_result(ret == 0, "Global hash request\n");
if (ret != 0)
goto out;
return;
futex_hash_slots_set_must_fail(4);
futex_hash_slots_set_must_fail(8);
@ -289,17 +257,14 @@ retry_getslots:
futex_hash_slots_set_must_fail(6);
ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS);
if (ret != 0) {
if (ret != 0)
ksft_exit_fail_msg("pthread_barrier_init failed: %m\n");
return 1;
}
create_max_threads(thread_lock_fn);
join_max_threads();
ret = futex_hash_slots_get();
ksft_test_result(ret == 0, "Continue to use global hash\n");
out:
ksft_finished();
return 0;
}
TEST_HARNESS_MAIN

View File

@ -7,24 +7,15 @@
#include <pthread.h>
#include <limits.h>
#include "logging.h"
#include "futextest.h"
#define TEST_NAME "futex-requeue"
#include "futextest.h"
#include "../../kselftest_harness.h"
#define timeout_ns 30000000
#define WAKE_WAIT_US 10000
volatile futex_t *f1;
void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
void *waiterfn(void *arg)
{
struct timespec to;
@ -38,67 +29,49 @@ void *waiterfn(void *arg)
return NULL;
}
int main(int argc, char *argv[])
TEST(requeue_single)
{
pthread_t waiter[10];
int res, ret = RET_PASS;
int c, i;
volatile futex_t _f1 = 0;
volatile futex_t f2 = 0;
pthread_t waiter[10];
int res;
f1 = &_f1;
while ((c = getopt(argc, argv, "cht:v:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
ksft_print_header();
ksft_set_plan(2);
ksft_print_msg("%s: Test futex_requeue\n",
basename(argv[0]));
/*
* Requeue a waiter from f1 to f2, and wake f2.
*/
if (pthread_create(&waiter[0], NULL, waiterfn, NULL))
error("pthread_create failed\n", errno);
ksft_exit_fail_msg("pthread_create failed\n");
usleep(WAKE_WAIT_US);
info("Requeuing 1 futex from f1 to f2\n");
ksft_print_dbg_msg("Requeuing 1 futex from f1 to f2\n");
res = futex_cmp_requeue(f1, 0, &f2, 0, 1, 0);
if (res != 1) {
if (res != 1)
ksft_test_result_fail("futex_requeue simple returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
}
info("Waking 1 futex at f2\n");
ksft_print_dbg_msg("Waking 1 futex at f2\n");
res = futex_wake(&f2, 1, 0);
if (res != 1) {
ksft_test_result_fail("futex_requeue simple returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_requeue simple succeeds\n");
}
}
TEST(requeue_multiple)
{
volatile futex_t _f1 = 0;
volatile futex_t f2 = 0;
pthread_t waiter[10];
int res, i;
f1 = &_f1;
/*
* Create 10 waiters at f1. At futex_requeue, wake 3 and requeue 7.
@ -106,31 +79,28 @@ int main(int argc, char *argv[])
*/
for (i = 0; i < 10; i++) {
if (pthread_create(&waiter[i], NULL, waiterfn, NULL))
error("pthread_create failed\n", errno);
ksft_exit_fail_msg("pthread_create failed\n");
}
usleep(WAKE_WAIT_US);
info("Waking 3 futexes at f1 and requeuing 7 futexes from f1 to f2\n");
ksft_print_dbg_msg("Waking 3 futexes at f1 and requeuing 7 futexes from f1 to f2\n");
res = futex_cmp_requeue(f1, 0, &f2, 3, 7, 0);
if (res != 10) {
ksft_test_result_fail("futex_requeue many returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
}
info("Waking INT_MAX futexes at f2\n");
ksft_print_dbg_msg("Waking INT_MAX futexes at f2\n");
res = futex_wake(&f2, INT_MAX, 0);
if (res != 7) {
ksft_test_result_fail("futex_requeue many returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_requeue many succeeds\n");
}
ksft_print_cnts();
return ret;
}
TEST_HARNESS_MAIN

View File

@ -26,11 +26,11 @@
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include "atomic.h"
#include "futextest.h"
#include "logging.h"
#include "../../kselftest_harness.h"
#define TEST_NAME "futex-requeue-pi"
#define MAX_WAKE_ITERS 1000
#define THREAD_MAX 10
#define SIGNAL_PERIOD_US 100
@ -42,12 +42,6 @@ futex_t f1 = FUTEX_INITIALIZER;
futex_t f2 = FUTEX_INITIALIZER;
futex_t wake_complete = FUTEX_INITIALIZER;
/* Test option defaults */
static long timeout_ns;
static int broadcast;
static int owner;
static int locked;
struct thread_arg {
long id;
struct timespec *timeout;
@ -56,18 +50,73 @@ struct thread_arg {
};
#define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 }
void usage(char *prog)
FIXTURE(args)
{
printf("Usage: %s\n", prog);
printf(" -b Broadcast wakeup (all waiters)\n");
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -l Lock the pi futex across requeue\n");
printf(" -o Use a third party pi futex owner during requeue (cancels -l)\n");
printf(" -t N Timeout in nanoseconds (default: 0)\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
};
FIXTURE_SETUP(args)
{
};
FIXTURE_TEARDOWN(args)
{
};
FIXTURE_VARIANT(args)
{
long timeout_ns;
bool broadcast;
bool owner;
bool locked;
};
/*
* For a given timeout value, this macro creates a test input with all the
* possible combinations of valid arguments
*/
#define FIXTURE_VARIANT_ADD_TIMEOUT(timeout) \
\
FIXTURE_VARIANT_ADD(args, t_##timeout) \
{ \
.timeout_ns = timeout, \
}; \
\
FIXTURE_VARIANT_ADD(args, t_##timeout##_broadcast) \
{ \
.timeout_ns = timeout, \
.broadcast = true, \
}; \
\
FIXTURE_VARIANT_ADD(args, t_##timeout##_broadcast_locked) \
{ \
.timeout_ns = timeout, \
.broadcast = true, \
.locked = true, \
}; \
\
FIXTURE_VARIANT_ADD(args, t_##timeout##_broadcast_owner) \
{ \
.timeout_ns = timeout, \
.broadcast = true, \
.owner = true, \
}; \
\
FIXTURE_VARIANT_ADD(args, t_##timeout##_locked) \
{ \
.timeout_ns = timeout, \
.locked = true, \
}; \
\
FIXTURE_VARIANT_ADD(args, t_##timeout##_owner) \
{ \
.timeout_ns = timeout, \
.owner = true, \
}; \
FIXTURE_VARIANT_ADD_TIMEOUT(0);
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 policy, int prio)
@ -81,26 +130,26 @@ int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg,
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (ret) {
error("pthread_attr_setinheritsched\n", ret);
ksft_exit_fail_msg("pthread_attr_setinheritsched\n");
return -1;
}
ret = pthread_attr_setschedpolicy(&attr, policy);
if (ret) {
error("pthread_attr_setschedpolicy\n", ret);
ksft_exit_fail_msg("pthread_attr_setschedpolicy\n");
return -1;
}
schedp.sched_priority = prio;
ret = pthread_attr_setschedparam(&attr, &schedp);
if (ret) {
error("pthread_attr_setschedparam\n", ret);
ksft_exit_fail_msg("pthread_attr_setschedparam\n");
return -1;
}
ret = pthread_create(pth, &attr, func, arg);
if (ret) {
error("pthread_create\n", ret);
ksft_exit_fail_msg("pthread_create\n");
return -1;
}
return 0;
@ -112,7 +161,7 @@ void *waiterfn(void *arg)
struct thread_arg *args = (struct thread_arg *)arg;
futex_t old_val;
info("Waiter %ld: running\n", args->id);
ksft_print_dbg_msg("Waiter %ld: running\n", 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 */
@ -120,26 +169,25 @@ void *waiterfn(void *arg)
old_val = f1;
atomic_inc(&waiters_blocked);
info("Calling futex_wait_requeue_pi: %p (%u) -> %p\n",
ksft_print_dbg_msg("Calling futex_wait_requeue_pi: %p (%u) -> %p\n",
&f1, f1, &f2);
args->ret = futex_wait_requeue_pi(&f1, old_val, &f2, args->timeout,
FUTEX_PRIVATE_FLAG);
info("waiter %ld woke with %d %s\n", args->id, args->ret,
ksft_print_dbg_msg("waiter %ld woke with %d %s\n", args->id, args->ret,
args->ret < 0 ? strerror(errno) : "");
atomic_inc(&waiters_woken);
if (args->ret < 0) {
if (args->timeout && errno == ETIMEDOUT)
args->ret = 0;
else {
args->ret = RET_ERROR;
error("futex_wait_requeue_pi\n", errno);
ksft_exit_fail_msg("futex_wait_requeue_pi\n");
}
futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
}
futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
info("Waiter %ld: exiting with %d\n", args->id, args->ret);
ksft_print_dbg_msg("Waiter %ld: exiting with %d\n", args->id, args->ret);
pthread_exit((void *)&args->ret);
}
@ -152,14 +200,14 @@ void *broadcast_wakerfn(void *arg)
int nr_wake = 1;
int i = 0;
info("Waker: waiting for waiters to block\n");
ksft_print_dbg_msg("Waker: waiting for waiters to block\n");
while (waiters_blocked.val < THREAD_MAX)
usleep(1000);
usleep(1000);
info("Waker: Calling broadcast\n");
ksft_print_dbg_msg("Waker: Calling broadcast\n");
if (args->lock) {
info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2);
ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2);
futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
}
continue_requeue:
@ -167,16 +215,14 @@ void *broadcast_wakerfn(void *arg)
args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue,
FUTEX_PRIVATE_FLAG);
if (args->ret < 0) {
args->ret = RET_ERROR;
error("FUTEX_CMP_REQUEUE_PI failed\n", errno);
ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n");
} else if (++i < MAX_WAKE_ITERS) {
task_count += args->ret;
if (task_count < THREAD_MAX - waiters_woken.val)
goto continue_requeue;
} else {
error("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n",
0, MAX_WAKE_ITERS, task_count, THREAD_MAX);
args->ret = RET_ERROR;
ksft_exit_fail_msg("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n",
MAX_WAKE_ITERS, task_count, THREAD_MAX);
}
futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG);
@ -187,7 +233,7 @@ void *broadcast_wakerfn(void *arg)
if (args->ret > 0)
args->ret = task_count;
info("Waker: exiting with %d\n", args->ret);
ksft_print_dbg_msg("Waker: exiting with %d\n", args->ret);
pthread_exit((void *)&args->ret);
}
@ -200,20 +246,20 @@ void *signal_wakerfn(void *arg)
int nr_wake = 1;
int i = 0;
info("Waker: waiting for waiters to block\n");
ksft_print_dbg_msg("Waker: waiting for waiters to block\n");
while (waiters_blocked.val < THREAD_MAX)
usleep(1000);
usleep(1000);
while (task_count < THREAD_MAX && waiters_woken.val < THREAD_MAX) {
info("task_count: %d, waiters_woken: %d\n",
ksft_print_dbg_msg("task_count: %d, waiters_woken: %d\n",
task_count, waiters_woken.val);
if (args->lock) {
info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n",
f2, &f2);
ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n",
f2, &f2);
futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
}
info("Waker: Calling signal\n");
ksft_print_dbg_msg("Waker: Calling signal\n");
/* cond_signal */
old_val = f1;
args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2,
@ -221,28 +267,23 @@ void *signal_wakerfn(void *arg)
FUTEX_PRIVATE_FLAG);
if (args->ret < 0)
args->ret = -errno;
info("futex: %x\n", f2);
ksft_print_dbg_msg("futex: %x\n", f2);
if (args->lock) {
info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n",
f2, &f2);
ksft_print_dbg_msg("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n",
f2, &f2);
futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
}
info("futex: %x\n", f2);
if (args->ret < 0) {
error("FUTEX_CMP_REQUEUE_PI failed\n", errno);
args->ret = RET_ERROR;
break;
}
ksft_print_dbg_msg("futex: %x\n", f2);
if (args->ret < 0)
ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n");
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) {
error("max signaling iterations (%d) reached, giving up on pending waiters.\n",
0, MAX_WAKE_ITERS + THREAD_MAX);
args->ret = RET_ERROR;
break;
ksft_exit_fail_msg("max signaling iterations (%d) reached, giving up on pending waiters.\n",
MAX_WAKE_ITERS + THREAD_MAX);
}
}
@ -251,8 +292,8 @@ void *signal_wakerfn(void *arg)
if (args->ret >= 0)
args->ret = task_count;
info("Waker: exiting with %d\n", args->ret);
info("Waker: waiters_woken: %d\n", waiters_woken.val);
ksft_print_dbg_msg("Waker: exiting with %d\n", args->ret);
ksft_print_dbg_msg("Waker: waiters_woken: %d\n", waiters_woken.val);
pthread_exit((void *)&args->ret);
}
@ -269,35 +310,40 @@ void *third_party_blocker(void *arg)
ret2 = futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
out:
if (args->ret || ret2) {
error("third_party_blocker() futex error", 0);
args->ret = RET_ERROR;
}
if (args->ret || ret2)
ksft_exit_fail_msg("third_party_blocker() futex error");
pthread_exit((void *)&args->ret);
}
int unit_test(int broadcast, long lock, int third_party_owner, long timeout_ns)
TEST_F(args, futex_requeue_pi)
{
void *(*wakerfn)(void *) = signal_wakerfn;
struct thread_arg blocker_arg = THREAD_ARG_INITIALIZER;
struct thread_arg waker_arg = THREAD_ARG_INITIALIZER;
pthread_t waiter[THREAD_MAX], waker, blocker;
struct timespec ts, *tsp = NULL;
void *(*wakerfn)(void *) = signal_wakerfn;
bool third_party_owner = variant->owner;
long timeout_ns = variant->timeout_ns;
bool broadcast = variant->broadcast;
struct thread_arg args[THREAD_MAX];
int *waiter_ret;
int i, ret = RET_PASS;
struct timespec ts, *tsp = NULL;
bool lock = variant->locked;
int *waiter_ret, i, ret = 0;
ksft_print_msg(
"\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n",
broadcast, lock, third_party_owner, timeout_ns);
if (timeout_ns) {
time_t secs;
info("timeout_ns = %ld\n", timeout_ns);
ksft_print_dbg_msg("timeout_ns = %ld\n", 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;
info("ts.tv_sec = %ld\n", ts.tv_sec);
info("ts.tv_nsec = %ld\n", ts.tv_nsec);
ksft_print_dbg_msg("ts.tv_sec = %ld\n", ts.tv_sec);
ksft_print_dbg_msg("ts.tv_nsec = %ld\n", ts.tv_nsec);
tsp = &ts;
}
@ -307,10 +353,7 @@ int unit_test(int broadcast, long lock, int third_party_owner, long timeout_ns)
if (third_party_owner) {
if (create_rt_thread(&blocker, third_party_blocker,
(void *)&blocker_arg, SCHED_FIFO, 1)) {
error("Creating third party blocker thread failed\n",
errno);
ret = RET_ERROR;
goto out;
ksft_exit_fail_msg("Creating third party blocker thread failed\n");
}
}
@ -318,20 +361,16 @@ int unit_test(int broadcast, long lock, int third_party_owner, long timeout_ns)
for (i = 0; i < THREAD_MAX; i++) {
args[i].id = i;
args[i].timeout = tsp;
info("Starting thread %d\n", i);
ksft_print_dbg_msg("Starting thread %d\n", i);
if (create_rt_thread(&waiter[i], waiterfn, (void *)&args[i],
SCHED_FIFO, 1)) {
error("Creating waiting thread failed\n", errno);
ret = RET_ERROR;
goto out;
ksft_exit_fail_msg("Creating waiting thread failed\n");
}
}
waker_arg.lock = lock;
if (create_rt_thread(&waker, wakerfn, (void *)&waker_arg,
SCHED_FIFO, 1)) {
error("Creating waker thread failed\n", errno);
ret = RET_ERROR;
goto out;
ksft_exit_fail_msg("Creating waker thread failed\n");
}
/* Wait for threads to finish */
@ -345,7 +384,6 @@ int unit_test(int broadcast, long lock, int third_party_owner, long timeout_ns)
pthread_join(blocker, NULL);
pthread_join(waker, NULL);
out:
if (!ret) {
if (*waiter_ret)
ret = *waiter_ret;
@ -355,66 +393,8 @@ out:
ret = blocker_arg.ret;
}
return ret;
if (ret)
ksft_test_result_fail("fail");
}
int main(int argc, char *argv[])
{
char *test_name;
int c, ret;
while ((c = getopt(argc, argv, "bchlot:v:")) != -1) {
switch (c) {
case 'b':
broadcast = 1;
break;
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 'l':
locked = 1;
break;
case 'o':
owner = 1;
locked = 0;
break;
case 't':
timeout_ns = atoi(optarg);
break;
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
ksft_print_header();
ksft_set_plan(1);
ksft_print_msg("%s: Test requeue functionality\n", basename(argv[0]));
ksft_print_msg(
"\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n",
broadcast, locked, owner, timeout_ns);
ret = asprintf(&test_name,
"%s broadcast=%d locked=%d owner=%d timeout=%ldns",
TEST_NAME, broadcast, locked, owner, timeout_ns);
if (ret < 0) {
ksft_print_msg("Failed to generate test name\n");
test_name = TEST_NAME;
}
/*
* FIXME: unit_test is obsolete now that we parse options and the
* various style of runs are done by run.sh - simplify the code and move
* unit_test into main()
*/
ret = unit_test(broadcast, locked, owner, timeout_ns);
print_result(test_name, ret);
return ret;
}
TEST_HARNESS_MAIN

View File

@ -23,67 +23,32 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "futextest.h"
#include "logging.h"
#define TEST_NAME "futex-requeue-pi-mismatched-ops"
#include "futextest.h"
#include "../../kselftest_harness.h"
futex_t f1 = FUTEX_INITIALIZER;
futex_t f2 = FUTEX_INITIALIZER;
int child_ret = 0;
void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
void *blocking_child(void *arg)
{
child_ret = futex_wait(&f1, f1, NULL, FUTEX_PRIVATE_FLAG);
if (child_ret < 0) {
child_ret = -errno;
error("futex_wait\n", errno);
ksft_exit_fail_msg("futex_wait\n");
}
return (void *)&child_ret;
}
int main(int argc, char *argv[])
TEST(requeue_pi_mismatched_ops)
{
int ret = RET_PASS;
pthread_t child;
int c;
int ret;
while ((c = getopt(argc, argv, "chv:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
if (pthread_create(&child, NULL, blocking_child, NULL))
ksft_exit_fail_msg("pthread_create\n");
ksft_print_header();
ksft_set_plan(1);
ksft_print_msg("%s: Detect mismatched requeue_pi operations\n",
basename(argv[0]));
if (pthread_create(&child, NULL, blocking_child, NULL)) {
error("pthread_create\n", errno);
ret = RET_ERROR;
goto out;
}
/* Allow the child to block in the kernel. */
sleep(1);
@ -102,34 +67,27 @@ int main(int argc, char *argv[])
* FUTEX_WAKE.
*/
ret = futex_wake(&f1, 1, FUTEX_PRIVATE_FLAG);
if (ret == 1) {
ret = RET_PASS;
} else if (ret < 0) {
error("futex_wake\n", errno);
ret = RET_ERROR;
} else {
error("futex_wake did not wake the child\n", 0);
ret = RET_ERROR;
}
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 {
error("futex_cmp_requeue_pi\n", errno);
ret = RET_ERROR;
ksft_exit_fail_msg("futex_cmp_requeue_pi\n");
}
} else if (ret > 0) {
fail("futex_cmp_requeue_pi failed to detect the mismatch\n");
ret = RET_FAIL;
ksft_test_result_fail("futex_cmp_requeue_pi failed to detect the mismatch\n");
} else {
error("futex_cmp_requeue_pi found no waiters\n", 0);
ret = RET_ERROR;
ksft_exit_fail_msg("futex_cmp_requeue_pi found no waiters\n");
}
pthread_join(child, NULL);
if (!ret)
ret = child_ret;
out:
/* If the kernel crashes, we shouldn't return at all. */
print_result(TEST_NAME, ret);
return ret;
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");
}
TEST_HARNESS_MAIN

View File

@ -24,11 +24,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "atomic.h"
#include "futextest.h"
#include "logging.h"
#include "../../kselftest_harness.h"
#define TEST_NAME "futex-requeue-pi-signal-restart"
#define DELAY_US 100
futex_t f1 = FUTEX_INITIALIZER;
@ -37,15 +37,6 @@ atomic_t requeued = ATOMIC_INITIALIZER;
int waiter_ret = 0;
void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg,
int policy, int prio)
{
@ -57,35 +48,28 @@ 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) {
error("pthread_attr_setinheritsched\n", ret);
return -1;
}
if (ret)
ksft_exit_fail_msg("pthread_attr_setinheritsched\n");
ret = pthread_attr_setschedpolicy(&attr, policy);
if (ret) {
error("pthread_attr_setschedpolicy\n", ret);
return -1;
}
if (ret)
ksft_exit_fail_msg("pthread_attr_setschedpolicy\n");
schedp.sched_priority = prio;
ret = pthread_attr_setschedparam(&attr, &schedp);
if (ret) {
error("pthread_attr_setschedparam\n", ret);
return -1;
}
if (ret)
ksft_exit_fail_msg("pthread_attr_setschedparam\n");
ret = pthread_create(pth, &attr, func, arg);
if (ret) {
error("pthread_create\n", ret);
return -1;
}
if (ret)
ksft_exit_fail_msg("pthread_create\n");
return 0;
}
void handle_signal(int signo)
{
info("signal received %s requeue\n",
ksft_print_dbg_msg("signal received %s requeue\n",
requeued.val ? "after" : "prior to");
}
@ -94,78 +78,46 @@ void *waiterfn(void *arg)
unsigned int old_val;
int res;
waiter_ret = RET_PASS;
info("Waiter running\n");
info("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
ksft_print_dbg_msg("Waiter running\n");
ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
old_val = f1;
res = futex_wait_requeue_pi(&f1, old_val, &(f2), NULL,
FUTEX_PRIVATE_FLAG);
if (!requeued.val || errno != EWOULDBLOCK) {
fail("unexpected return from futex_wait_requeue_pi: %d (%s)\n",
ksft_test_result_fail("unexpected return from futex_wait_requeue_pi: %d (%s)\n",
res, strerror(errno));
info("w2:futex: %x\n", f2);
ksft_print_dbg_msg("w2:futex: %x\n", f2);
if (!res)
futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
waiter_ret = RET_FAIL;
}
info("Waiter exiting with %d\n", waiter_ret);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
TEST(futex_requeue_pi_signal_restart)
{
unsigned int old_val;
struct sigaction sa;
pthread_t waiter;
int c, res, ret = RET_PASS;
while ((c = getopt(argc, argv, "chv:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
ksft_print_header();
ksft_set_plan(1);
ksft_print_msg("%s: Test signal handling during requeue_pi\n",
basename(argv[0]));
ksft_print_msg("\tArguments: <none>\n");
int res;
sa.sa_handler = handle_signal;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGUSR1, &sa, NULL)) {
error("sigaction\n", errno);
exit(1);
}
if (sigaction(SIGUSR1, &sa, NULL))
ksft_exit_fail_msg("sigaction\n");
info("m1:f2: %x\n", f2);
info("Creating waiter\n");
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) {
error("Creating waiting thread failed", res);
ret = RET_ERROR;
goto out;
}
if (res)
ksft_exit_fail_msg("Creating waiting thread failed");
info("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
info("m2:f2: %x\n", f2);
ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
ksft_print_dbg_msg("m2:f2: %x\n", f2);
futex_lock_pi(&f2, 0, 0, FUTEX_PRIVATE_FLAG);
info("m3:f2: %x\n", f2);
ksft_print_dbg_msg("m3:f2: %x\n", f2);
while (1) {
/*
@ -173,11 +125,11 @@ int main(int argc, char *argv[])
* restart futex_wait_requeue_pi() in the kernel. Wait for the
* waiter to block on f1 again.
*/
info("Issuing SIGUSR1 to waiter\n");
ksft_print_dbg_msg("Issuing SIGUSR1 to waiter\n");
pthread_kill(waiter, SIGUSR1);
usleep(DELAY_US);
info("Requeueing waiter via FUTEX_CMP_REQUEUE_PI\n");
ksft_print_dbg_msg("Requeueing waiter via FUTEX_CMP_REQUEUE_PI\n");
old_val = f1;
res = futex_cmp_requeue_pi(&f1, old_val, &(f2), 1, 0,
FUTEX_PRIVATE_FLAG);
@ -191,12 +143,10 @@ int main(int argc, char *argv[])
atomic_set(&requeued, 1);
break;
} else if (res < 0) {
error("FUTEX_CMP_REQUEUE_PI failed\n", errno);
ret = RET_ERROR;
break;
ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n");
}
}
info("m4:f2: %x\n", f2);
ksft_print_dbg_msg("m4:f2: %x\n", f2);
/*
* Signal the waiter after requeue, waiter should return from
@ -204,19 +154,14 @@ int main(int argc, char *argv[])
* futex_unlock_pi() can't happen before the signal wakeup is detected
* in the kernel.
*/
info("Issuing SIGUSR1 to waiter\n");
ksft_print_dbg_msg("Issuing SIGUSR1 to waiter\n");
pthread_kill(waiter, SIGUSR1);
info("Waiting for waiter to return\n");
ksft_print_dbg_msg("Waiting for waiter to return\n");
pthread_join(waiter, NULL);
info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", f2, &f2);
ksft_print_dbg_msg("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", f2, &f2);
futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
info("m5:f2: %x\n", f2);
out:
if (ret == RET_PASS && waiter_ret)
ret = waiter_ret;
print_result(TEST_NAME, ret);
return ret;
ksft_print_dbg_msg("m5:f2: %x\n", f2);
}
TEST_HARNESS_MAIN

View File

@ -9,25 +9,16 @@
#include <sys/shm.h>
#include <sys/mman.h>
#include <fcntl.h>
#include "logging.h"
#include "futextest.h"
#define TEST_NAME "futex-wait"
#include "futextest.h"
#include "../../kselftest_harness.h"
#define timeout_ns 30000000
#define WAKE_WAIT_US 10000
#define SHM_PATH "futex_shm_file"
void *futex;
void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
static void *waiterfn(void *arg)
{
struct timespec to;
@ -45,53 +36,37 @@ static void *waiterfn(void *arg)
return NULL;
}
int main(int argc, char *argv[])
TEST(private_futex)
{
int res, ret = RET_PASS, fd, c, shm_id;
u_int32_t f_private = 0, *shared_data;
unsigned int flags = FUTEX_PRIVATE_FLAG;
u_int32_t f_private = 0;
pthread_t waiter;
void *shm;
int res;
futex = &f_private;
while ((c = getopt(argc, argv, "cht:v:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
ksft_print_header();
ksft_set_plan(3);
ksft_print_msg("%s: Test futex_wait\n", basename(argv[0]));
/* Testing a private futex */
info("Calling private futex_wait on futex: %p\n", futex);
ksft_print_dbg_msg("Calling private futex_wait on futex: %p\n", futex);
if (pthread_create(&waiter, NULL, waiterfn, (void *) &flags))
error("pthread_create failed\n", errno);
ksft_exit_fail_msg("pthread_create failed\n");
usleep(WAKE_WAIT_US);
info("Calling private futex_wake on futex: %p\n", futex);
ksft_print_dbg_msg("Calling private futex_wake on futex: %p\n", 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));
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_wake private succeeds\n");
}
}
TEST(anon_page)
{
u_int32_t *shared_data;
pthread_t waiter;
int res, shm_id;
/* Testing an anon page shared memory */
shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);
@ -105,67 +80,65 @@ int main(int argc, char *argv[])
*shared_data = 0;
futex = shared_data;
info("Calling shared (page anon) futex_wait on futex: %p\n", futex);
ksft_print_dbg_msg("Calling shared (page anon) futex_wait on futex: %p\n", futex);
if (pthread_create(&waiter, NULL, waiterfn, NULL))
error("pthread_create failed\n", errno);
ksft_exit_fail_msg("pthread_create failed\n");
usleep(WAKE_WAIT_US);
info("Calling shared (page anon) futex_wake on futex: %p\n", futex);
ksft_print_dbg_msg("Calling shared (page anon) futex_wake on futex: %p\n", 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));
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_wake shared (page anon) succeeds\n");
}
shmdt(shared_data);
}
TEST(file_backed)
{
u_int32_t f_private = 0;
pthread_t waiter;
int res, fd;
void *shm;
/* Testing a file backed shared memory */
fd = open(SHM_PATH, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0) {
perror("open");
exit(1);
}
if (fd < 0)
ksft_exit_fail_msg("open");
if (ftruncate(fd, sizeof(f_private))) {
perror("ftruncate");
exit(1);
}
if (ftruncate(fd, sizeof(f_private)))
ksft_exit_fail_msg("ftruncate");
shm = mmap(NULL, sizeof(f_private), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shm == MAP_FAILED) {
perror("mmap");
exit(1);
}
if (shm == MAP_FAILED)
ksft_exit_fail_msg("mmap");
memcpy(shm, &f_private, sizeof(f_private));
futex = shm;
info("Calling shared (file backed) futex_wait on futex: %p\n", futex);
ksft_print_dbg_msg("Calling shared (file backed) futex_wait on futex: %p\n", futex);
if (pthread_create(&waiter, NULL, waiterfn, NULL))
error("pthread_create failed\n", errno);
ksft_exit_fail_msg("pthread_create failed\n");
usleep(WAKE_WAIT_US);
info("Calling shared (file backed) futex_wake on futex: %p\n", futex);
ksft_print_dbg_msg("Calling shared (file backed) futex_wake on futex: %p\n", 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));
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_wake shared (file backed) succeeds\n");
}
/* Freeing resources */
shmdt(shared_data);
munmap(shm, sizeof(f_private));
remove(SHM_PATH);
close(fd);
ksft_print_cnts();
return ret;
}
TEST_HARNESS_MAIN

View File

@ -27,10 +27,9 @@
#include <libgen.h>
#include <signal.h>
#include "logging.h"
#include "futextest.h"
#include "../../kselftest_harness.h"
#define TEST_NAME "futex-wait-private-mapped-file"
#define PAGE_SZ 4096
char pad[PAGE_SZ] = {1};
@ -40,86 +39,44 @@ char pad2[PAGE_SZ] = {1};
#define WAKE_WAIT_US 3000000
struct timespec wait_timeout = { .tv_sec = 5, .tv_nsec = 0};
void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
void *thr_futex_wait(void *arg)
{
int ret;
info("futex wait\n");
ksft_print_dbg_msg("futex wait\n");
ret = futex_wait(&val, 1, &wait_timeout, 0);
if (ret && errno != EWOULDBLOCK && errno != ETIMEDOUT) {
error("futex error.\n", errno);
print_result(TEST_NAME, RET_ERROR);
exit(RET_ERROR);
}
if (ret && errno != EWOULDBLOCK && errno != ETIMEDOUT)
ksft_exit_fail_msg("futex error.\n");
if (ret && errno == ETIMEDOUT)
fail("waiter timedout\n");
ksft_exit_fail_msg("waiter timedout\n");
info("futex_wait: ret = %d, errno = %d\n", ret, errno);
ksft_print_dbg_msg("futex_wait: ret = %d, errno = %d\n", ret, errno);
return NULL;
}
int main(int argc, char **argv)
TEST(wait_private_mapped_file)
{
pthread_t thr;
int ret = RET_PASS;
int res;
int c;
while ((c = getopt(argc, argv, "chv:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
res = pthread_create(&thr, NULL, thr_futex_wait, NULL);
if (res < 0)
ksft_exit_fail_msg("pthread_create error\n");
ksft_print_header();
ksft_set_plan(1);
ksft_print_msg(
"%s: Test the futex value of private file mappings in FUTEX_WAIT\n",
basename(argv[0]));
ret = pthread_create(&thr, NULL, thr_futex_wait, NULL);
if (ret < 0) {
fprintf(stderr, "pthread_create error\n");
ret = RET_ERROR;
goto out;
}
info("wait a while\n");
ksft_print_dbg_msg("wait a while\n");
usleep(WAKE_WAIT_US);
val = 2;
res = futex_wake(&val, 1, 0);
info("futex_wake %d\n", res);
if (res != 1) {
fail("FUTEX_WAKE didn't find the waiting thread.\n");
ret = RET_FAIL;
}
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");
info("join\n");
ksft_print_dbg_msg("join\n");
pthread_join(thr, NULL);
out:
print_result(TEST_NAME, ret);
return ret;
ksft_test_result_pass("wait_private_mapped_file");
}
TEST_HARNESS_MAIN

View File

@ -16,26 +16,15 @@
*****************************************************************************/
#include <pthread.h>
#include "futextest.h"
#include "futex2test.h"
#include "logging.h"
#define TEST_NAME "futex-wait-timeout"
#include "../../kselftest_harness.h"
static long timeout_ns = 100000; /* 100us default timeout */
static futex_t futex_pi;
static pthread_barrier_t barrier;
void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -t N Timeout in nanoseconds (default: 100,000)\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
/*
* Get a PI lock and hold it forever, so the main thread lock_pi will block
* and we can test the timeout
@ -47,13 +36,13 @@ void *get_pi_lock(void *arg)
ret = futex_lock_pi(&futex_pi, NULL, 0, 0);
if (ret != 0)
error("futex_lock_pi failed\n", ret);
ksft_exit_fail_msg("futex_lock_pi failed\n");
pthread_barrier_wait(&barrier);
/* Blocks forever */
ret = futex_wait(&lock, 0, NULL, 0);
error("futex_wait failed\n", ret);
ksft_exit_fail_msg("futex_wait failed\n");
return NULL;
}
@ -61,12 +50,11 @@ void *get_pi_lock(void *arg)
/*
* Check if the function returned the expected error
*/
static void test_timeout(int res, int *ret, char *test_name, int err)
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);
*ret = RET_FAIL;
} else {
ksft_test_result_pass("%s succeeds\n", test_name);
}
@ -78,10 +66,8 @@ static void test_timeout(int res, int *ret, char *test_name, int err)
static int futex_get_abs_timeout(clockid_t clockid, struct timespec *to,
long timeout_ns)
{
if (clock_gettime(clockid, to)) {
error("clock_gettime failed\n", errno);
return errno;
}
if (clock_gettime(clockid, to))
ksft_exit_fail_msg("clock_gettime failed\n");
to->tv_nsec += timeout_ns;
@ -93,83 +79,66 @@ static int futex_get_abs_timeout(clockid_t clockid, struct timespec *to,
return 0;
}
int main(int argc, char *argv[])
TEST(wait_bitset)
{
futex_t f1 = FUTEX_INITIALIZER;
int res, ret = RET_PASS;
struct timespec to;
pthread_t thread;
int c;
struct futex_waitv waitv = {
.uaddr = (uintptr_t)&f1,
.val = f1,
.flags = FUTEX_32,
.__reserved = 0
};
while ((c = getopt(argc, argv, "cht:v:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 't':
timeout_ns = atoi(optarg);
break;
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
ksft_print_header();
ksft_set_plan(9);
ksft_print_msg("%s: Block on a futex and wait for timeout\n",
basename(argv[0]));
ksft_print_msg("\tArguments: timeout=%ldns\n", timeout_ns);
pthread_barrier_init(&barrier, NULL, 2);
pthread_create(&thread, NULL, get_pi_lock, NULL);
int res;
/* initialize relative timeout */
to.tv_sec = 0;
to.tv_nsec = timeout_ns;
res = futex_wait(&f1, f1, &to, 0);
test_timeout(res, &ret, "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))
return RET_FAIL;
ksft_test_result_error("get_time error");
res = futex_wait_bitset(&f1, f1, &to, 1, FUTEX_CLOCK_REALTIME);
test_timeout(res, &ret, "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))
return RET_FAIL;
ksft_test_result_error("get_time error");
res = futex_wait_bitset(&f1, f1, &to, 1, 0);
test_timeout(res, &ret, "futex_wait_bitset monotonic", ETIMEDOUT);
test_timeout(res, "futex_wait_bitset monotonic", ETIMEDOUT);
}
TEST(requeue_pi)
{
futex_t f1 = FUTEX_INITIALIZER;
struct timespec to;
int res;
/* FUTEX_WAIT_REQUEUE_PI with CLOCK_REALTIME */
if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
return RET_FAIL;
ksft_test_result_error("get_time error");
res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, FUTEX_CLOCK_REALTIME);
test_timeout(res, &ret, "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))
return RET_FAIL;
ksft_test_result_error("get_time error");
res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, 0);
test_timeout(res, &ret, "futex_wait_requeue_pi monotonic", ETIMEDOUT);
test_timeout(res, "futex_wait_requeue_pi monotonic", ETIMEDOUT);
}
TEST(lock_pi)
{
struct timespec to;
pthread_t thread;
int res;
/* 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);
/* Wait until the other thread calls futex_lock_pi() */
pthread_barrier_wait(&barrier);
pthread_barrier_destroy(&barrier);
/*
* FUTEX_LOCK_PI with CLOCK_REALTIME
* Due to historical reasons, FUTEX_LOCK_PI supports only realtime
@ -181,26 +150,38 @@ int main(int argc, char *argv[])
* smaller than realtime and the syscall will timeout immediately.
*/
if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
return RET_FAIL;
ksft_test_result_error("get_time error");
res = futex_lock_pi(&futex_pi, &to, 0, 0);
test_timeout(res, &ret, "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, &ret, "futex_lock_pi invalid timeout flag", ENOSYS);
test_timeout(res, "futex_lock_pi invalid timeout flag", ENOSYS);
}
TEST(waitv)
{
futex_t f1 = FUTEX_INITIALIZER;
struct futex_waitv waitv = {
.uaddr = (uintptr_t)&f1,
.val = f1,
.flags = FUTEX_32,
.__reserved = 0,
};
struct timespec to;
int res;
/* futex_waitv with CLOCK_MONOTONIC */
if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns))
return RET_FAIL;
ksft_test_result_error("get_time error");
res = futex_waitv(&waitv, 1, 0, &to, CLOCK_MONOTONIC);
test_timeout(res, &ret, "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))
return RET_FAIL;
ksft_test_result_error("get_time error");
res = futex_waitv(&waitv, 1, 0, &to, CLOCK_REALTIME);
test_timeout(res, &ret, "futex_waitv realtime", ETIMEDOUT);
ksft_print_cnts();
return ret;
test_timeout(res, "futex_waitv realtime", ETIMEDOUT);
}
TEST_HARNESS_MAIN

View File

@ -29,95 +29,55 @@
#include <linux/futex.h>
#include <libgen.h>
#include "logging.h"
#include "futextest.h"
#include "../../kselftest_harness.h"
#define TEST_NAME "futex-wait-uninitialized-heap"
#define WAIT_US 5000000
static int child_blocked = 1;
static int child_ret;
static bool child_ret;
void *buf;
void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
void *wait_thread(void *arg)
{
int res;
child_ret = RET_PASS;
child_ret = true;
res = futex_wait(buf, 1, NULL, 0);
child_blocked = 0;
if (res != 0 && errno != EWOULDBLOCK) {
error("futex failure\n", errno);
child_ret = RET_ERROR;
ksft_exit_fail_msg("futex failure\n");
child_ret = false;
}
pthread_exit(NULL);
}
int main(int argc, char **argv)
TEST(futex_wait_uninitialized_heap)
{
int c, ret = RET_PASS;
long page_size;
pthread_t thr;
while ((c = getopt(argc, argv, "chv:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
int ret;
page_size = sysconf(_SC_PAGESIZE);
buf = mmap(NULL, page_size, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
if (buf == (void *)-1) {
error("mmap\n", errno);
exit(1);
}
ksft_print_header();
ksft_set_plan(1);
ksft_print_msg("%s: Test the uninitialized futex value in FUTEX_WAIT\n",
basename(argv[0]));
if (buf == (void *)-1)
ksft_exit_fail_msg("mmap\n");
ret = pthread_create(&thr, NULL, wait_thread, NULL);
if (ret) {
error("pthread_create\n", errno);
ret = RET_ERROR;
goto out;
}
if (ret)
ksft_exit_fail_msg("pthread_create\n");
info("waiting %dus for child to return\n", WAIT_US);
ksft_print_dbg_msg("waiting %dus for child to return\n", WAIT_US);
usleep(WAIT_US);
ret = child_ret;
if (child_blocked) {
fail("child blocked in kernel\n");
ret = RET_FAIL;
}
if (child_blocked)
ksft_test_result_fail("child blocked in kernel\n");
out:
print_result(TEST_NAME, ret);
return ret;
if (!child_ret)
ksft_test_result_fail("child error\n");
}
TEST_HARNESS_MAIN

View File

@ -21,72 +21,44 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "futextest.h"
#include "futex2test.h"
#include "logging.h"
#include "../../kselftest_harness.h"
#define TEST_NAME "futex-wait-wouldblock"
#define timeout_ns 100000
void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
int main(int argc, char *argv[])
TEST(futex_wait_wouldblock)
{
struct timespec to = {.tv_sec = 0, .tv_nsec = timeout_ns};
futex_t f1 = FUTEX_INITIALIZER;
int res, ret = RET_PASS;
int c;
struct futex_waitv waitv = {
.uaddr = (uintptr_t)&f1,
.val = f1+1,
.flags = FUTEX_32,
.__reserved = 0
};
int res;
while ((c = getopt(argc, argv, "cht:v:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
ksft_print_header();
ksft_set_plan(2);
ksft_print_msg("%s: Test the unexpected futex value in FUTEX_WAIT\n",
basename(argv[0]));
info("Calling futex_wait on f1: %u @ %p with val=%u\n", f1, &f1, f1+1);
ksft_print_dbg_msg("Calling futex_wait on f1: %u @ %p with val=%u\n", 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) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_wait\n");
}
}
if (clock_gettime(CLOCK_MONOTONIC, &to)) {
error("clock_gettime failed\n", errno);
return errno;
}
TEST(futex_waitv_wouldblock)
{
struct timespec to = {.tv_sec = 0, .tv_nsec = timeout_ns};
futex_t f1 = FUTEX_INITIALIZER;
struct futex_waitv waitv = {
.uaddr = (uintptr_t)&f1,
.val = f1 + 1,
.flags = FUTEX_32,
.__reserved = 0,
};
int res;
if (clock_gettime(CLOCK_MONOTONIC, &to))
ksft_exit_fail_msg("clock_gettime failed %d\n", errno);
to.tv_nsec += timeout_ns;
@ -95,17 +67,15 @@ int main(int argc, char *argv[])
to.tv_nsec -= 1000000000;
}
info("Calling futex_waitv on f1: %u @ %p with val=%u\n", f1, &f1, f1+1);
ksft_print_dbg_msg("Calling futex_waitv on f1: %u @ %p with val=%u\n", 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) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_waitv\n");
}
ksft_print_cnts();
return ret;
}
TEST_HARNESS_MAIN

View File

@ -15,25 +15,16 @@
#include <pthread.h>
#include <stdint.h>
#include <sys/shm.h>
#include "futextest.h"
#include "futex2test.h"
#include "logging.h"
#include "../../kselftest_harness.h"
#define TEST_NAME "futex-wait"
#define WAKE_WAIT_US 10000
#define NR_FUTEXES 30
static struct futex_waitv waitv[NR_FUTEXES];
u_int32_t futexes[NR_FUTEXES] = {0};
void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}
void *waiterfn(void *arg)
{
struct timespec to;
@ -41,7 +32,7 @@ void *waiterfn(void *arg)
/* setting absolute timeout for futex2 */
if (clock_gettime(CLOCK_MONOTONIC, &to))
error("gettime64 failed\n", errno);
ksft_exit_fail_msg("gettime64 failed\n");
to.tv_sec++;
@ -57,34 +48,10 @@ void *waiterfn(void *arg)
return NULL;
}
int main(int argc, char *argv[])
TEST(private_waitv)
{
pthread_t waiter;
int res, ret = RET_PASS;
struct timespec to;
int c, i;
while ((c = getopt(argc, argv, "cht:v:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
ksft_print_header();
ksft_set_plan(7);
ksft_print_msg("%s: Test FUTEX_WAITV\n",
basename(argv[0]));
int res, i;
for (i = 0; i < NR_FUTEXES; i++) {
waitv[i].uaddr = (uintptr_t)&futexes[i];
@ -95,7 +62,7 @@ int main(int argc, char *argv[])
/* Private waitv */
if (pthread_create(&waiter, NULL, waiterfn, NULL))
error("pthread_create failed\n", errno);
ksft_exit_fail_msg("pthread_create failed\n");
usleep(WAKE_WAIT_US);
@ -104,10 +71,15 @@ int main(int argc, char *argv[])
ksft_test_result_fail("futex_wake private returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_waitv private\n");
}
}
TEST(shared_waitv)
{
pthread_t waiter;
int res, i;
/* Shared waitv */
for (i = 0; i < NR_FUTEXES; i++) {
@ -128,7 +100,7 @@ int main(int argc, char *argv[])
}
if (pthread_create(&waiter, NULL, waiterfn, NULL))
error("pthread_create failed\n", errno);
ksft_exit_fail_msg("pthread_create failed\n");
usleep(WAKE_WAIT_US);
@ -137,19 +109,24 @@ int main(int argc, char *argv[])
ksft_test_result_fail("futex_wake shared returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_waitv shared\n");
}
for (i = 0; i < NR_FUTEXES; i++)
shmdt(u64_to_ptr(waitv[i].uaddr));
}
TEST(invalid_flag)
{
struct timespec to;
int res;
/* Testing a waiter without FUTEX_32 flag */
waitv[0].flags = FUTEX_PRIVATE_FLAG;
if (clock_gettime(CLOCK_MONOTONIC, &to))
error("gettime64 failed\n", errno);
ksft_exit_fail_msg("gettime64 failed\n");
to.tv_sec++;
@ -158,17 +135,22 @@ int main(int argc, char *argv[])
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_waitv without FUTEX_32\n");
}
}
TEST(unaligned_address)
{
struct timespec to;
int res;
/* 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))
error("gettime64 failed\n", errno);
ksft_exit_fail_msg("gettime64 failed\n");
to.tv_sec++;
@ -177,16 +159,21 @@ int main(int argc, char *argv[])
ksft_test_result_fail("futex_wake private returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_waitv with an unaligned address\n");
}
}
TEST(null_address)
{
struct timespec to;
int res;
/* Testing a NULL address for waiters.uaddr */
waitv[0].uaddr = 0x00000000;
if (clock_gettime(CLOCK_MONOTONIC, &to))
error("gettime64 failed\n", errno);
ksft_exit_fail_msg("gettime64 failed\n");
to.tv_sec++;
@ -195,14 +182,13 @@ int main(int argc, char *argv[])
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} 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))
error("gettime64 failed\n", errno);
ksft_exit_fail_msg("gettime64 failed\n");
to.tv_sec++;
@ -211,14 +197,19 @@ int main(int argc, char *argv[])
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_waitv NULL address in *waiters\n");
}
}
TEST(invalid_clockid)
{
struct timespec to;
int res;
/* Testing an invalid clockid */
if (clock_gettime(CLOCK_MONOTONIC, &to))
error("gettime64 failed\n", errno);
ksft_exit_fail_msg("gettime64 failed\n");
to.tv_sec++;
@ -227,11 +218,9 @@ int main(int argc, char *argv[])
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex_waitv invalid clockid\n");
}
ksft_print_cnts();
return ret;
}
TEST_HARNESS_MAIN

View File

@ -18,74 +18,36 @@
#
###############################################################################
# Test for a color capable console
if [ -z "$USE_COLOR" ]; then
tput setf 7 || tput setaf 7
if [ $? -eq 0 ]; then
USE_COLOR=1
tput sgr0
fi
fi
if [ "$USE_COLOR" -eq 1 ]; then
COLOR="-c"
fi
echo
./futex_requeue_pi
echo
# requeue pi testing
# without timeouts
./futex_requeue_pi $COLOR
./futex_requeue_pi $COLOR -b
./futex_requeue_pi $COLOR -b -l
./futex_requeue_pi $COLOR -b -o
./futex_requeue_pi $COLOR -l
./futex_requeue_pi $COLOR -o
# with timeouts
./futex_requeue_pi $COLOR -b -l -t 5000
./futex_requeue_pi $COLOR -l -t 5000
./futex_requeue_pi $COLOR -b -l -t 500000
./futex_requeue_pi $COLOR -l -t 500000
./futex_requeue_pi $COLOR -b -t 5000
./futex_requeue_pi $COLOR -t 5000
./futex_requeue_pi $COLOR -b -t 500000
./futex_requeue_pi $COLOR -t 500000
./futex_requeue_pi $COLOR -b -o -t 5000
./futex_requeue_pi $COLOR -l -t 5000
./futex_requeue_pi $COLOR -b -o -t 500000
./futex_requeue_pi $COLOR -l -t 500000
# with long timeout
./futex_requeue_pi $COLOR -b -l -t 2000000000
./futex_requeue_pi $COLOR -l -t 2000000000
./futex_requeue_pi_mismatched_ops
echo
./futex_requeue_pi_mismatched_ops $COLOR
./futex_requeue_pi_signal_restart
echo
./futex_requeue_pi_signal_restart $COLOR
./futex_wait_timeout
echo
./futex_wait_timeout $COLOR
./futex_wait_wouldblock
echo
./futex_wait_wouldblock $COLOR
./futex_wait_uninitialized_heap
./futex_wait_private_mapped_file
echo
./futex_wait_uninitialized_heap $COLOR
./futex_wait_private_mapped_file $COLOR
./futex_wait
echo
./futex_wait $COLOR
./futex_requeue
echo
./futex_requeue $COLOR
./futex_waitv
echo
./futex_waitv $COLOR
./futex_priv_hash
echo
./futex_priv_hash $COLOR
./futex_priv_hash -g $COLOR
echo
./futex_numa_mpol $COLOR
./futex_numa_mpol

View File

@ -58,6 +58,17 @@ typedef volatile u_int32_t futex_t;
#define SYS_futex SYS_futex_time64
#endif
/*
* On 32bit systems if we use "-D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64" or if
* we are using a newer compiler then the size of the timestamps will be 64bit,
* however, the SYS_futex will still point to the 32bit futex system call.
*/
#if __SIZEOF_POINTER__ == 4 && defined(SYS_futex_time64) && \
defined(_TIME_BITS) && _TIME_BITS == 64
# undef SYS_futex
# define SYS_futex SYS_futex_time64
#endif
/**
* futex() - SYS_futex syscall wrapper
* @uaddr: address of first futex

View File

@ -1,148 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/******************************************************************************
*
* Copyright © International Business Machines Corp., 2009
*
* DESCRIPTION
* Glibc independent futex library for testing kernel functionality.
*
* AUTHOR
* Darren Hart <dvhart@linux.intel.com>
*
* HISTORY
* 2009-Nov-6: Initial version by Darren Hart <dvhart@linux.intel.com>
*
*****************************************************************************/
#ifndef _LOGGING_H
#define _LOGGING_H
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <linux/futex.h>
#include "kselftest.h"
/*
* Define PASS, ERROR, and FAIL strings with and without color escape
* sequences, default to no color.
*/
#define ESC 0x1B, '['
#define BRIGHT '1'
#define GREEN '3', '2'
#define YELLOW '3', '3'
#define RED '3', '1'
#define ESCEND 'm'
#define BRIGHT_GREEN ESC, BRIGHT, ';', GREEN, ESCEND
#define BRIGHT_YELLOW ESC, BRIGHT, ';', YELLOW, ESCEND
#define BRIGHT_RED ESC, BRIGHT, ';', RED, ESCEND
#define RESET_COLOR ESC, '0', 'm'
static const char PASS_COLOR[] = {BRIGHT_GREEN, ' ', 'P', 'A', 'S', 'S',
RESET_COLOR, 0};
static const char ERROR_COLOR[] = {BRIGHT_YELLOW, 'E', 'R', 'R', 'O', 'R',
RESET_COLOR, 0};
static const char FAIL_COLOR[] = {BRIGHT_RED, ' ', 'F', 'A', 'I', 'L',
RESET_COLOR, 0};
static const char INFO_NORMAL[] = " INFO";
static const char PASS_NORMAL[] = " PASS";
static const char ERROR_NORMAL[] = "ERROR";
static const char FAIL_NORMAL[] = " FAIL";
const char *INFO = INFO_NORMAL;
const char *PASS = PASS_NORMAL;
const char *ERROR = ERROR_NORMAL;
const char *FAIL = FAIL_NORMAL;
/* Verbosity setting for INFO messages */
#define VQUIET 0
#define VCRITICAL 1
#define VINFO 2
#define VMAX VINFO
int _verbose = VCRITICAL;
/* Functional test return codes */
#define RET_PASS 0
#define RET_ERROR -1
#define RET_FAIL -2
/**
* log_color() - Use colored output for PASS, ERROR, and FAIL strings
* @use_color: use color (1) or not (0)
*/
void log_color(int use_color)
{
if (use_color) {
PASS = PASS_COLOR;
ERROR = ERROR_COLOR;
FAIL = FAIL_COLOR;
} else {
PASS = PASS_NORMAL;
ERROR = ERROR_NORMAL;
FAIL = FAIL_NORMAL;
}
}
/**
* log_verbosity() - Set verbosity of test output
* @verbose: Enable (1) verbose output or not (0)
*
* Currently setting verbose=1 will enable INFO messages and 0 will disable
* them. FAIL and ERROR messages are always displayed.
*/
void log_verbosity(int level)
{
if (level > VMAX)
level = VMAX;
else if (level < 0)
level = 0;
_verbose = level;
}
/**
* print_result() - Print standard PASS | ERROR | FAIL results
* @ret: the return value to be considered: 0 | RET_ERROR | RET_FAIL
*
* print_result() is primarily intended for functional tests.
*/
void print_result(const char *test_name, int ret)
{
switch (ret) {
case RET_PASS:
ksft_test_result_pass("%s\n", test_name);
ksft_print_cnts();
return;
case RET_ERROR:
ksft_test_result_error("%s\n", test_name);
ksft_print_cnts();
return;
case RET_FAIL:
ksft_test_result_fail("%s\n", test_name);
ksft_print_cnts();
return;
}
}
/* log level macros */
#define info(message, vargs...) \
do { \
if (_verbose >= VINFO) \
fprintf(stderr, "\t%s: "message, INFO, ##vargs); \
} while (0)
#define error(message, err, args...) \
do { \
if (_verbose >= VCRITICAL) {\
if (err) \
fprintf(stderr, "\t%s: %s: "message, \
ERROR, strerror(err), ##args); \
else \
fprintf(stderr, "\t%s: "message, ERROR, ##args); \
} \
} while (0)
#define fail(message, args...) \
do { \
if (_verbose >= VCRITICAL) \
fprintf(stderr, "\t%s: "message, FAIL, ##args); \
} while (0)
#endif

View File

@ -54,6 +54,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <sys/utsname.h>
@ -104,6 +105,7 @@ struct ksft_count {
static struct ksft_count ksft_cnt;
static unsigned int ksft_plan;
static bool ksft_debug_enabled;
static inline unsigned int ksft_test_num(void)
{
@ -175,6 +177,18 @@ static inline __printf(1, 2) void ksft_print_msg(const char *msg, ...)
va_end(args);
}
static inline void ksft_print_dbg_msg(const char *msg, ...)
{
va_list args;
if (!ksft_debug_enabled)
return;
va_start(args, msg);
ksft_print_msg(msg, args);
va_end(args);
}
static inline void ksft_perror(const char *msg)
{
ksft_print_msg("%s: %s (%d)\n", msg, strerror(errno), errno);

View File

@ -1091,7 +1091,7 @@ static int test_harness_argv_check(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "hlF:f:V:v:t:T:r:")) != -1) {
while ((opt = getopt(argc, argv, "dhlF:f:V:v:t:T:r:")) != -1) {
switch (opt) {
case 'f':
case 'F':
@ -1104,12 +1104,16 @@ static int test_harness_argv_check(int argc, char **argv)
case 'l':
test_harness_list_tests();
return KSFT_SKIP;
case 'd':
ksft_debug_enabled = true;
break;
case 'h':
default:
fprintf(stderr,
"Usage: %s [-h|-l] [-t|-T|-v|-V|-f|-F|-r name]\n"
"Usage: %s [-h|-l|-d] [-t|-T|-v|-V|-f|-F|-r name]\n"
"\t-h print help\n"
"\t-l list all tests\n"
"\t-d enable debug prints\n"
"\n"
"\t-t name include test\n"
"\t-T name exclude test\n"
@ -1142,8 +1146,9 @@ static bool test_enabled(int argc, char **argv,
int opt;
optind = 1;
while ((opt = getopt(argc, argv, "F:f:V:v:t:T:r:")) != -1) {
has_positive |= islower(opt);
while ((opt = getopt(argc, argv, "dF:f:V:v:t:T:r:")) != -1) {
if (opt != 'd')
has_positive |= islower(opt);
switch (tolower(opt)) {
case 't':