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:
commit
c574fb2ed7
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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':
|
||||
|
|
Loading…
Reference in New Issue