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;
|
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
|
* sys_get_robust_list() - Get the robust-futex list head of a task
|
||||||
* @pid: pid of the process [zero for current 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,
|
struct robust_list_head __user * __user *, head_ptr,
|
||||||
size_t __user *, len_ptr)
|
size_t __user *, len_ptr)
|
||||||
{
|
{
|
||||||
struct robust_list_head __user *head;
|
struct robust_list_head __user *head = futex_get_robust_list_common(pid, false);
|
||||||
unsigned long ret;
|
|
||||||
struct task_struct *p;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
if (IS_ERR(head))
|
||||||
|
return PTR_ERR(head);
|
||||||
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 (put_user(sizeof(*head), len_ptr))
|
if (put_user(sizeof(*head), len_ptr))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return put_user(head, head_ptr);
|
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,
|
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_uptr_t __user *, head_ptr,
|
||||||
compat_size_t __user *, len_ptr)
|
compat_size_t __user *, len_ptr)
|
||||||
{
|
{
|
||||||
struct compat_robust_list_head __user *head;
|
struct compat_robust_list_head __user *head = futex_get_robust_list_common(pid, true);
|
||||||
unsigned long ret;
|
|
||||||
struct task_struct *p;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
if (IS_ERR(head))
|
||||||
|
return PTR_ERR(head);
|
||||||
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 (put_user(sizeof(*head), len_ptr))
|
if (put_user(sizeof(*head), len_ptr))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return put_user(ptr_to_compat(head), head_ptr);
|
return put_user(ptr_to_compat(head), head_ptr);
|
||||||
|
|
||||||
err_unlock:
|
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_COMPAT */
|
#endif /* CONFIG_COMPAT */
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# 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)
|
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
|
LDLIBS := -lpthread -lrt -lnuma
|
||||||
|
|
||||||
LOCAL_HDRS := \
|
LOCAL_HDRS := \
|
||||||
../include/futextest.h \
|
../include/futextest.h \
|
||||||
../include/atomic.h \
|
../include/atomic.h
|
||||||
../include/logging.h
|
|
||||||
TEST_GEN_PROGS := \
|
TEST_GEN_PROGS := \
|
||||||
futex_wait_timeout \
|
futex_wait_timeout \
|
||||||
futex_wait_wouldblock \
|
futex_wait_wouldblock \
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "logging.h"
|
|
||||||
#include "futextest.h"
|
#include "futextest.h"
|
||||||
#include "futex2test.h"
|
#include "futex2test.h"
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
#include <linux/futex.h>
|
#include <linux/futex.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include "logging.h"
|
|
||||||
#include "futextest.h"
|
#include "futextest.h"
|
||||||
#include "futex2test.h"
|
#include "futex2test.h"
|
||||||
|
#include "../../kselftest_harness.h"
|
||||||
|
|
||||||
#define MAX_THREADS 64
|
#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;
|
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 {
|
do {
|
||||||
ret = futex2_wake(futex_ptr, to_wake, futex_flags);
|
ret = futex2_wake(futex_ptr, to_wake, futex_flags);
|
||||||
if (must_fail) {
|
|
||||||
if (ret < 0)
|
if (err_value) {
|
||||||
break;
|
if (ret >= 0)
|
||||||
ksft_exit_fail_msg("futex2_wake(%d, 0x%x) should fail, but didn't\n",
|
ksft_exit_fail_msg("futex2_wake(%d, 0x%x) should fail, but didn't\n",
|
||||||
to_wake, futex_flags);
|
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) {
|
if (ret < 0) {
|
||||||
ksft_exit_fail_msg("Failed futex2_wake(%d, 0x%x): %m\n",
|
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();
|
join_max_threads();
|
||||||
|
|
||||||
for (i = 0; i < MAX_THREADS; i++) {
|
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",
|
ksft_print_msg("Thread %d should fail but succeeded (%d)\n",
|
||||||
i, thread_args[i].result);
|
i, thread_args[i].result);
|
||||||
need_exit = 1;
|
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);
|
ksft_print_msg("Thread %d failed (%d)\n", i, thread_args[i].result);
|
||||||
need_exit = 1;
|
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");
|
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)
|
TEST(futex_numa_mpol)
|
||||||
{
|
|
||||||
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[])
|
|
||||||
{
|
{
|
||||||
struct futex32_numa *futex_numa;
|
struct futex32_numa *futex_numa;
|
||||||
int mem_size, i;
|
|
||||||
void *futex_ptr;
|
void *futex_ptr;
|
||||||
int c;
|
int mem_size;
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
mem_size = sysconf(_SC_PAGE_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)
|
if (futex_ptr == MAP_FAILED)
|
||||||
ksft_exit_fail_msg("mmap() for %d bytes failed\n", mem_size);
|
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;
|
futex_numa = futex_ptr;
|
||||||
|
|
||||||
ksft_print_msg("Regular test\n");
|
ksft_print_msg("Regular test\n");
|
||||||
|
@ -182,27 +160,31 @@ int main(int argc, char *argv[])
|
||||||
if (futex_numa->numa == FUTEX_NO_NODE)
|
if (futex_numa->numa == FUTEX_NO_NODE)
|
||||||
ksft_exit_fail_msg("NUMA node is left uninitialized\n");
|
ksft_exit_fail_msg("NUMA node is left uninitialized\n");
|
||||||
|
|
||||||
ksft_print_msg("Memory too small\n");
|
/* FUTEX2_NUMA futex must be 8-byte aligned */
|
||||||
test_futex(futex_ptr + mem_size - 4, 1);
|
ksft_print_msg("Mis-aligned futex\n");
|
||||||
|
test_futex(futex_ptr + mem_size - 4, EINVAL);
|
||||||
|
|
||||||
ksft_print_msg("Memory out of range\n");
|
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;
|
futex_numa->numa = FUTEX_NO_NODE;
|
||||||
mprotect(futex_ptr, mem_size, PROT_READ);
|
mprotect(futex_ptr, mem_size, PROT_READ);
|
||||||
ksft_print_msg("Memory, RO\n");
|
ksft_print_msg("Memory, RO\n");
|
||||||
test_futex(futex_ptr, 1);
|
test_futex(futex_ptr, EFAULT);
|
||||||
|
|
||||||
mprotect(futex_ptr, mem_size, PROT_NONE);
|
mprotect(futex_ptr, mem_size, PROT_NONE);
|
||||||
ksft_print_msg("Memory, no access\n");
|
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);
|
mprotect(futex_ptr, mem_size, PROT_READ | PROT_WRITE);
|
||||||
ksft_print_msg("Memory back to RW\n");
|
ksft_print_msg("Memory back to RW\n");
|
||||||
test_futex(futex_ptr, 0);
|
test_futex(futex_ptr, 0);
|
||||||
|
|
||||||
|
ksft_test_result_pass("futex2 memory boundary tests passed\n");
|
||||||
|
|
||||||
/* MPOL test. Does not work as expected */
|
/* 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;
|
unsigned long nodemask;
|
||||||
int ret;
|
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);
|
ret = futex2_wake(futex_ptr, 0, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
ksft_test_result_fail("Failed to wake 0 with MPOL: %m\n");
|
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) {
|
if (futex_numa->numa != i) {
|
||||||
ksft_exit_fail_msg("Returned NUMA node is %d expected %d\n",
|
ksft_exit_fail_msg("Returned NUMA node is %d expected %d\n",
|
||||||
futex_numa->numa, i);
|
futex_numa->numa, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ksft_test_result_pass("NUMA MPOL tests passed\n");
|
ksft_test_result_pass("futex2 MPOL hints test passed\n");
|
||||||
ksft_finished();
|
#else
|
||||||
return 0;
|
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 <linux/prctl.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
|
|
||||||
#include "logging.h"
|
#include "../../kselftest_harness.h"
|
||||||
|
|
||||||
#define MAX_THREADS 64
|
#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);
|
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_create = "Automatic hash bucket init on thread creation.\n";
|
||||||
static const char *test_msg_auto_inc = "Automatic increase with more than 16 CPUs\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;
|
int futex_slots1, futex_slotsn, online_cpus;
|
||||||
pthread_mutexattr_t mutex_attr_pi;
|
pthread_mutexattr_t mutex_attr_pi;
|
||||||
int ret, retry = 20;
|
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_init(&mutex_attr_pi);
|
||||||
ret |= pthread_mutexattr_setprotocol(&mutex_attr_pi, PTHREAD_PRIO_INHERIT);
|
ret |= pthread_mutexattr_setprotocol(&mutex_attr_pi, PTHREAD_PRIO_INHERIT);
|
||||||
|
@ -189,14 +157,14 @@ int main(int argc, char *argv[])
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
ksft_exit_fail_msg("pthread_join() failed: %d, %m\n", ret);
|
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();
|
futex_slots1 = futex_hash_slots_get();
|
||||||
if (futex_slots1 <= 0) {
|
if (futex_slots1 <= 0) {
|
||||||
ksft_print_msg("Current hash buckets: %d\n", futex_slots1);
|
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);
|
online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS + 1);
|
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",
|
ksft_print_msg("Expected increase of hash buckets but got: %d -> %d\n",
|
||||||
futex_slots1, futex_slotsn);
|
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 {
|
} else {
|
||||||
ksft_test_result_skip(test_msg_auto_inc);
|
ksft_test_result_skip("%s", test_msg_auto_inc);
|
||||||
}
|
}
|
||||||
ret = pthread_mutex_unlock(&global_lock);
|
ret = pthread_mutex_unlock(&global_lock);
|
||||||
|
|
||||||
|
@ -257,17 +225,17 @@ retry_getslots:
|
||||||
|
|
||||||
futex_hash_slots_set_verify(2);
|
futex_hash_slots_set_verify(2);
|
||||||
join_max_threads();
|
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, MAX_THREADS);
|
||||||
counter = 0;
|
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);
|
ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS);
|
||||||
|
|
||||||
create_max_threads(thread_lock_fn);
|
create_max_threads(thread_lock_fn);
|
||||||
join_max_threads();
|
join_max_threads();
|
||||||
|
|
||||||
ret = futex_hash_slots_get();
|
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);
|
ret);
|
||||||
|
|
||||||
futex_hash_slots_set_must_fail(1 << 29);
|
futex_hash_slots_set_must_fail(1 << 29);
|
||||||
|
@ -280,7 +248,7 @@ retry_getslots:
|
||||||
ret = futex_hash_slots_set(0);
|
ret = futex_hash_slots_set(0);
|
||||||
ksft_test_result(ret == 0, "Global hash request\n");
|
ksft_test_result(ret == 0, "Global hash request\n");
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
goto out;
|
return;
|
||||||
|
|
||||||
futex_hash_slots_set_must_fail(4);
|
futex_hash_slots_set_must_fail(4);
|
||||||
futex_hash_slots_set_must_fail(8);
|
futex_hash_slots_set_must_fail(8);
|
||||||
|
@ -289,17 +257,14 @@ retry_getslots:
|
||||||
futex_hash_slots_set_must_fail(6);
|
futex_hash_slots_set_must_fail(6);
|
||||||
|
|
||||||
ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS);
|
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");
|
ksft_exit_fail_msg("pthread_barrier_init failed: %m\n");
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
create_max_threads(thread_lock_fn);
|
create_max_threads(thread_lock_fn);
|
||||||
join_max_threads();
|
join_max_threads();
|
||||||
|
|
||||||
ret = futex_hash_slots_get();
|
ret = futex_hash_slots_get();
|
||||||
ksft_test_result(ret == 0, "Continue to use global hash\n");
|
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 <pthread.h>
|
||||||
#include <limits.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 timeout_ns 30000000
|
||||||
#define WAKE_WAIT_US 10000
|
#define WAKE_WAIT_US 10000
|
||||||
|
|
||||||
volatile futex_t *f1;
|
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)
|
void *waiterfn(void *arg)
|
||||||
{
|
{
|
||||||
struct timespec to;
|
struct timespec to;
|
||||||
|
@ -38,67 +29,49 @@ void *waiterfn(void *arg)
|
||||||
return NULL;
|
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 _f1 = 0;
|
||||||
volatile futex_t f2 = 0;
|
volatile futex_t f2 = 0;
|
||||||
|
pthread_t waiter[10];
|
||||||
|
int res;
|
||||||
|
|
||||||
f1 = &_f1;
|
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.
|
* Requeue a waiter from f1 to f2, and wake f2.
|
||||||
*/
|
*/
|
||||||
if (pthread_create(&waiter[0], NULL, waiterfn, NULL))
|
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);
|
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);
|
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",
|
ksft_test_result_fail("futex_requeue simple returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ksft_print_dbg_msg("Waking 1 futex at f2\n");
|
||||||
info("Waking 1 futex at f2\n");
|
|
||||||
res = futex_wake(&f2, 1, 0);
|
res = futex_wake(&f2, 1, 0);
|
||||||
if (res != 1) {
|
if (res != 1) {
|
||||||
ksft_test_result_fail("futex_requeue simple returned: %d %s\n",
|
ksft_test_result_fail("futex_requeue simple returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_requeue simple succeeds\n");
|
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.
|
* 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++) {
|
for (i = 0; i < 10; i++) {
|
||||||
if (pthread_create(&waiter[i], NULL, waiterfn, NULL))
|
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);
|
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);
|
res = futex_cmp_requeue(f1, 0, &f2, 3, 7, 0);
|
||||||
if (res != 10) {
|
if (res != 10) {
|
||||||
ksft_test_result_fail("futex_requeue many returned: %d %s\n",
|
ksft_test_result_fail("futex_requeue many returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
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);
|
res = futex_wake(&f2, INT_MAX, 0);
|
||||||
if (res != 7) {
|
if (res != 7) {
|
||||||
ksft_test_result_fail("futex_requeue many returned: %d %s\n",
|
ksft_test_result_fail("futex_requeue many returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_requeue many succeeds\n");
|
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 <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
#include "futextest.h"
|
#include "futextest.h"
|
||||||
#include "logging.h"
|
#include "../../kselftest_harness.h"
|
||||||
|
|
||||||
#define TEST_NAME "futex-requeue-pi"
|
|
||||||
#define MAX_WAKE_ITERS 1000
|
#define MAX_WAKE_ITERS 1000
|
||||||
#define THREAD_MAX 10
|
#define THREAD_MAX 10
|
||||||
#define SIGNAL_PERIOD_US 100
|
#define SIGNAL_PERIOD_US 100
|
||||||
|
@ -42,12 +42,6 @@ futex_t f1 = FUTEX_INITIALIZER;
|
||||||
futex_t f2 = FUTEX_INITIALIZER;
|
futex_t f2 = FUTEX_INITIALIZER;
|
||||||
futex_t wake_complete = 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 {
|
struct thread_arg {
|
||||||
long id;
|
long id;
|
||||||
struct timespec *timeout;
|
struct timespec *timeout;
|
||||||
|
@ -56,18 +50,73 @@ struct thread_arg {
|
||||||
};
|
};
|
||||||
#define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 }
|
#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");
|
FIXTURE_SETUP(args)
|
||||||
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");
|
FIXTURE_TEARDOWN(args)
|
||||||
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
|
{
|
||||||
VQUIET, VCRITICAL, VINFO);
|
};
|
||||||
}
|
|
||||||
|
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 create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg,
|
||||||
int policy, int prio)
|
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);
|
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error("pthread_attr_setinheritsched\n", ret);
|
ksft_exit_fail_msg("pthread_attr_setinheritsched\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pthread_attr_setschedpolicy(&attr, policy);
|
ret = pthread_attr_setschedpolicy(&attr, policy);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error("pthread_attr_setschedpolicy\n", ret);
|
ksft_exit_fail_msg("pthread_attr_setschedpolicy\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
schedp.sched_priority = prio;
|
schedp.sched_priority = prio;
|
||||||
ret = pthread_attr_setschedparam(&attr, &schedp);
|
ret = pthread_attr_setschedparam(&attr, &schedp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error("pthread_attr_setschedparam\n", ret);
|
ksft_exit_fail_msg("pthread_attr_setschedparam\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pthread_create(pth, &attr, func, arg);
|
ret = pthread_create(pth, &attr, func, arg);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error("pthread_create\n", ret);
|
ksft_exit_fail_msg("pthread_create\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -112,7 +161,7 @@ void *waiterfn(void *arg)
|
||||||
struct thread_arg *args = (struct thread_arg *)arg;
|
struct thread_arg *args = (struct thread_arg *)arg;
|
||||||
futex_t old_val;
|
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
|
/* Each thread sleeps for a different amount of time
|
||||||
* This is to avoid races, because we don't lock the
|
* This is to avoid races, because we don't lock the
|
||||||
* external mutex here */
|
* external mutex here */
|
||||||
|
@ -120,26 +169,25 @@ void *waiterfn(void *arg)
|
||||||
|
|
||||||
old_val = f1;
|
old_val = f1;
|
||||||
atomic_inc(&waiters_blocked);
|
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);
|
&f1, f1, &f2);
|
||||||
args->ret = futex_wait_requeue_pi(&f1, old_val, &f2, args->timeout,
|
args->ret = futex_wait_requeue_pi(&f1, old_val, &f2, args->timeout,
|
||||||
FUTEX_PRIVATE_FLAG);
|
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) : "");
|
args->ret < 0 ? strerror(errno) : "");
|
||||||
atomic_inc(&waiters_woken);
|
atomic_inc(&waiters_woken);
|
||||||
if (args->ret < 0) {
|
if (args->ret < 0) {
|
||||||
if (args->timeout && errno == ETIMEDOUT)
|
if (args->timeout && errno == ETIMEDOUT)
|
||||||
args->ret = 0;
|
args->ret = 0;
|
||||||
else {
|
else {
|
||||||
args->ret = RET_ERROR;
|
ksft_exit_fail_msg("futex_wait_requeue_pi\n");
|
||||||
error("futex_wait_requeue_pi\n", errno);
|
|
||||||
}
|
}
|
||||||
futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
|
futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
|
||||||
}
|
}
|
||||||
futex_unlock_pi(&f2, 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);
|
pthread_exit((void *)&args->ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,14 +200,14 @@ void *broadcast_wakerfn(void *arg)
|
||||||
int nr_wake = 1;
|
int nr_wake = 1;
|
||||||
int i = 0;
|
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)
|
while (waiters_blocked.val < THREAD_MAX)
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
|
|
||||||
info("Waker: Calling broadcast\n");
|
ksft_print_dbg_msg("Waker: Calling broadcast\n");
|
||||||
if (args->lock) {
|
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);
|
futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
|
||||||
}
|
}
|
||||||
continue_requeue:
|
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,
|
args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue,
|
||||||
FUTEX_PRIVATE_FLAG);
|
FUTEX_PRIVATE_FLAG);
|
||||||
if (args->ret < 0) {
|
if (args->ret < 0) {
|
||||||
args->ret = RET_ERROR;
|
ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n");
|
||||||
error("FUTEX_CMP_REQUEUE_PI failed\n", errno);
|
|
||||||
} else if (++i < MAX_WAKE_ITERS) {
|
} else if (++i < MAX_WAKE_ITERS) {
|
||||||
task_count += args->ret;
|
task_count += args->ret;
|
||||||
if (task_count < THREAD_MAX - waiters_woken.val)
|
if (task_count < THREAD_MAX - waiters_woken.val)
|
||||||
goto continue_requeue;
|
goto continue_requeue;
|
||||||
} else {
|
} else {
|
||||||
error("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n",
|
ksft_exit_fail_msg("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n",
|
||||||
0, MAX_WAKE_ITERS, task_count, THREAD_MAX);
|
MAX_WAKE_ITERS, task_count, THREAD_MAX);
|
||||||
args->ret = RET_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG);
|
futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG);
|
||||||
|
@ -187,7 +233,7 @@ void *broadcast_wakerfn(void *arg)
|
||||||
if (args->ret > 0)
|
if (args->ret > 0)
|
||||||
args->ret = task_count;
|
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);
|
pthread_exit((void *)&args->ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,20 +246,20 @@ void *signal_wakerfn(void *arg)
|
||||||
int nr_wake = 1;
|
int nr_wake = 1;
|
||||||
int i = 0;
|
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)
|
while (waiters_blocked.val < THREAD_MAX)
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
|
|
||||||
while (task_count < THREAD_MAX && waiters_woken.val < THREAD_MAX) {
|
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);
|
task_count, waiters_woken.val);
|
||||||
if (args->lock) {
|
if (args->lock) {
|
||||||
info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n",
|
ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n",
|
||||||
f2, &f2);
|
f2, &f2);
|
||||||
futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
|
futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
|
||||||
}
|
}
|
||||||
info("Waker: Calling signal\n");
|
ksft_print_dbg_msg("Waker: Calling signal\n");
|
||||||
/* cond_signal */
|
/* cond_signal */
|
||||||
old_val = f1;
|
old_val = f1;
|
||||||
args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2,
|
args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2,
|
||||||
|
@ -221,28 +267,23 @@ void *signal_wakerfn(void *arg)
|
||||||
FUTEX_PRIVATE_FLAG);
|
FUTEX_PRIVATE_FLAG);
|
||||||
if (args->ret < 0)
|
if (args->ret < 0)
|
||||||
args->ret = -errno;
|
args->ret = -errno;
|
||||||
info("futex: %x\n", f2);
|
ksft_print_dbg_msg("futex: %x\n", f2);
|
||||||
if (args->lock) {
|
if (args->lock) {
|
||||||
info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n",
|
ksft_print_dbg_msg("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n",
|
||||||
f2, &f2);
|
f2, &f2);
|
||||||
futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
|
futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
|
||||||
}
|
}
|
||||||
info("futex: %x\n", f2);
|
ksft_print_dbg_msg("futex: %x\n", f2);
|
||||||
if (args->ret < 0) {
|
if (args->ret < 0)
|
||||||
error("FUTEX_CMP_REQUEUE_PI failed\n", errno);
|
ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n");
|
||||||
args->ret = RET_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
task_count += args->ret;
|
task_count += args->ret;
|
||||||
usleep(SIGNAL_PERIOD_US);
|
usleep(SIGNAL_PERIOD_US);
|
||||||
i++;
|
i++;
|
||||||
/* we have to loop at least THREAD_MAX times */
|
/* we have to loop at least THREAD_MAX times */
|
||||||
if (i > MAX_WAKE_ITERS + THREAD_MAX) {
|
if (i > MAX_WAKE_ITERS + THREAD_MAX) {
|
||||||
error("max signaling iterations (%d) reached, giving up on pending waiters.\n",
|
ksft_exit_fail_msg("max signaling iterations (%d) reached, giving up on pending waiters.\n",
|
||||||
0, MAX_WAKE_ITERS + THREAD_MAX);
|
MAX_WAKE_ITERS + THREAD_MAX);
|
||||||
args->ret = RET_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,8 +292,8 @@ void *signal_wakerfn(void *arg)
|
||||||
if (args->ret >= 0)
|
if (args->ret >= 0)
|
||||||
args->ret = task_count;
|
args->ret = task_count;
|
||||||
|
|
||||||
info("Waker: exiting with %d\n", args->ret);
|
ksft_print_dbg_msg("Waker: exiting with %d\n", args->ret);
|
||||||
info("Waker: waiters_woken: %d\n", waiters_woken.val);
|
ksft_print_dbg_msg("Waker: waiters_woken: %d\n", waiters_woken.val);
|
||||||
pthread_exit((void *)&args->ret);
|
pthread_exit((void *)&args->ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,35 +310,40 @@ void *third_party_blocker(void *arg)
|
||||||
ret2 = futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
|
ret2 = futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (args->ret || ret2) {
|
if (args->ret || ret2)
|
||||||
error("third_party_blocker() futex error", 0);
|
ksft_exit_fail_msg("third_party_blocker() futex error");
|
||||||
args->ret = RET_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_exit((void *)&args->ret);
|
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 blocker_arg = THREAD_ARG_INITIALIZER;
|
||||||
struct thread_arg waker_arg = THREAD_ARG_INITIALIZER;
|
struct thread_arg waker_arg = THREAD_ARG_INITIALIZER;
|
||||||
pthread_t waiter[THREAD_MAX], waker, blocker;
|
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];
|
struct thread_arg args[THREAD_MAX];
|
||||||
int *waiter_ret;
|
struct timespec ts, *tsp = NULL;
|
||||||
int i, ret = RET_PASS;
|
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) {
|
if (timeout_ns) {
|
||||||
time_t secs;
|
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);
|
ret = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
secs = (ts.tv_nsec + timeout_ns) / 1000000000;
|
secs = (ts.tv_nsec + timeout_ns) / 1000000000;
|
||||||
ts.tv_nsec = ((int64_t)ts.tv_nsec + timeout_ns) % 1000000000;
|
ts.tv_nsec = ((int64_t)ts.tv_nsec + timeout_ns) % 1000000000;
|
||||||
ts.tv_sec += secs;
|
ts.tv_sec += secs;
|
||||||
info("ts.tv_sec = %ld\n", ts.tv_sec);
|
ksft_print_dbg_msg("ts.tv_sec = %ld\n", ts.tv_sec);
|
||||||
info("ts.tv_nsec = %ld\n", ts.tv_nsec);
|
ksft_print_dbg_msg("ts.tv_nsec = %ld\n", ts.tv_nsec);
|
||||||
tsp = &ts;
|
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 (third_party_owner) {
|
||||||
if (create_rt_thread(&blocker, third_party_blocker,
|
if (create_rt_thread(&blocker, third_party_blocker,
|
||||||
(void *)&blocker_arg, SCHED_FIFO, 1)) {
|
(void *)&blocker_arg, SCHED_FIFO, 1)) {
|
||||||
error("Creating third party blocker thread failed\n",
|
ksft_exit_fail_msg("Creating third party blocker thread failed\n");
|
||||||
errno);
|
|
||||||
ret = RET_ERROR;
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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++) {
|
for (i = 0; i < THREAD_MAX; i++) {
|
||||||
args[i].id = i;
|
args[i].id = i;
|
||||||
args[i].timeout = tsp;
|
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],
|
if (create_rt_thread(&waiter[i], waiterfn, (void *)&args[i],
|
||||||
SCHED_FIFO, 1)) {
|
SCHED_FIFO, 1)) {
|
||||||
error("Creating waiting thread failed\n", errno);
|
ksft_exit_fail_msg("Creating waiting thread failed\n");
|
||||||
ret = RET_ERROR;
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
waker_arg.lock = lock;
|
waker_arg.lock = lock;
|
||||||
if (create_rt_thread(&waker, wakerfn, (void *)&waker_arg,
|
if (create_rt_thread(&waker, wakerfn, (void *)&waker_arg,
|
||||||
SCHED_FIFO, 1)) {
|
SCHED_FIFO, 1)) {
|
||||||
error("Creating waker thread failed\n", errno);
|
ksft_exit_fail_msg("Creating waker thread failed\n");
|
||||||
ret = RET_ERROR;
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for threads to finish */
|
/* 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(blocker, NULL);
|
||||||
pthread_join(waker, NULL);
|
pthread_join(waker, NULL);
|
||||||
|
|
||||||
out:
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
if (*waiter_ret)
|
if (*waiter_ret)
|
||||||
ret = *waiter_ret;
|
ret = *waiter_ret;
|
||||||
|
@ -355,66 +393,8 @@ out:
|
||||||
ret = blocker_arg.ret;
|
ret = blocker_arg.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
if (ret)
|
||||||
|
ksft_test_result_fail("fail");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
TEST_HARNESS_MAIN
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,67 +23,32 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.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 f1 = FUTEX_INITIALIZER;
|
||||||
futex_t f2 = FUTEX_INITIALIZER;
|
futex_t f2 = FUTEX_INITIALIZER;
|
||||||
int child_ret = 0;
|
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)
|
void *blocking_child(void *arg)
|
||||||
{
|
{
|
||||||
child_ret = futex_wait(&f1, f1, NULL, FUTEX_PRIVATE_FLAG);
|
child_ret = futex_wait(&f1, f1, NULL, FUTEX_PRIVATE_FLAG);
|
||||||
if (child_ret < 0) {
|
if (child_ret < 0) {
|
||||||
child_ret = -errno;
|
child_ret = -errno;
|
||||||
error("futex_wait\n", errno);
|
ksft_exit_fail_msg("futex_wait\n");
|
||||||
}
|
}
|
||||||
return (void *)&child_ret;
|
return (void *)&child_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
TEST(requeue_pi_mismatched_ops)
|
||||||
{
|
{
|
||||||
int ret = RET_PASS;
|
|
||||||
pthread_t child;
|
pthread_t child;
|
||||||
int c;
|
int ret;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "chv:")) != -1) {
|
if (pthread_create(&child, NULL, blocking_child, NULL))
|
||||||
switch (c) {
|
ksft_exit_fail_msg("pthread_create\n");
|
||||||
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: 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. */
|
/* Allow the child to block in the kernel. */
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
|
@ -102,34 +67,27 @@ int main(int argc, char *argv[])
|
||||||
* FUTEX_WAKE.
|
* FUTEX_WAKE.
|
||||||
*/
|
*/
|
||||||
ret = futex_wake(&f1, 1, FUTEX_PRIVATE_FLAG);
|
ret = futex_wake(&f1, 1, FUTEX_PRIVATE_FLAG);
|
||||||
if (ret == 1) {
|
if (ret == 1)
|
||||||
ret = RET_PASS;
|
ret = 0;
|
||||||
} else if (ret < 0) {
|
else if (ret < 0)
|
||||||
error("futex_wake\n", errno);
|
ksft_exit_fail_msg("futex_wake\n");
|
||||||
ret = RET_ERROR;
|
else
|
||||||
} else {
|
ksft_exit_fail_msg("futex_wake did not wake the child\n");
|
||||||
error("futex_wake did not wake the child\n", 0);
|
|
||||||
ret = RET_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
error("futex_cmp_requeue_pi\n", errno);
|
ksft_exit_fail_msg("futex_cmp_requeue_pi\n");
|
||||||
ret = RET_ERROR;
|
|
||||||
}
|
}
|
||||||
} else if (ret > 0) {
|
} else if (ret > 0) {
|
||||||
fail("futex_cmp_requeue_pi failed to detect the mismatch\n");
|
ksft_test_result_fail("futex_cmp_requeue_pi failed to detect the mismatch\n");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
error("futex_cmp_requeue_pi found no waiters\n", 0);
|
ksft_exit_fail_msg("futex_cmp_requeue_pi found no waiters\n");
|
||||||
ret = RET_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_join(child, NULL);
|
pthread_join(child, NULL);
|
||||||
|
|
||||||
if (!ret)
|
if (!ret && !child_ret)
|
||||||
ret = child_ret;
|
ksft_test_result_pass("futex_requeue_pi_mismatched_ops passed\n");
|
||||||
|
else
|
||||||
out:
|
ksft_test_result_pass("futex_requeue_pi_mismatched_ops failed\n");
|
||||||
/* If the kernel crashes, we shouldn't return at all. */
|
|
||||||
print_result(TEST_NAME, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_HARNESS_MAIN
|
||||||
|
|
|
@ -24,11 +24,11 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
#include "futextest.h"
|
#include "futextest.h"
|
||||||
#include "logging.h"
|
#include "../../kselftest_harness.h"
|
||||||
|
|
||||||
#define TEST_NAME "futex-requeue-pi-signal-restart"
|
|
||||||
#define DELAY_US 100
|
#define DELAY_US 100
|
||||||
|
|
||||||
futex_t f1 = FUTEX_INITIALIZER;
|
futex_t f1 = FUTEX_INITIALIZER;
|
||||||
|
@ -37,15 +37,6 @@ atomic_t requeued = ATOMIC_INITIALIZER;
|
||||||
|
|
||||||
int waiter_ret = 0;
|
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 create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg,
|
||||||
int policy, int prio)
|
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));
|
memset(&schedp, 0, sizeof(schedp));
|
||||||
|
|
||||||
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
||||||
if (ret) {
|
if (ret)
|
||||||
error("pthread_attr_setinheritsched\n", ret);
|
ksft_exit_fail_msg("pthread_attr_setinheritsched\n");
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = pthread_attr_setschedpolicy(&attr, policy);
|
ret = pthread_attr_setschedpolicy(&attr, policy);
|
||||||
if (ret) {
|
if (ret)
|
||||||
error("pthread_attr_setschedpolicy\n", ret);
|
ksft_exit_fail_msg("pthread_attr_setschedpolicy\n");
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
schedp.sched_priority = prio;
|
schedp.sched_priority = prio;
|
||||||
ret = pthread_attr_setschedparam(&attr, &schedp);
|
ret = pthread_attr_setschedparam(&attr, &schedp);
|
||||||
if (ret) {
|
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);
|
ret = pthread_create(pth, &attr, func, arg);
|
||||||
if (ret) {
|
if (ret)
|
||||||
error("pthread_create\n", ret);
|
ksft_exit_fail_msg("pthread_create\n");
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_signal(int signo)
|
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");
|
requeued.val ? "after" : "prior to");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,78 +78,46 @@ void *waiterfn(void *arg)
|
||||||
unsigned int old_val;
|
unsigned int old_val;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
waiter_ret = RET_PASS;
|
ksft_print_dbg_msg("Waiter running\n");
|
||||||
|
ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
|
||||||
info("Waiter running\n");
|
|
||||||
info("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
|
|
||||||
old_val = f1;
|
old_val = f1;
|
||||||
res = futex_wait_requeue_pi(&f1, old_val, &(f2), NULL,
|
res = futex_wait_requeue_pi(&f1, old_val, &(f2), NULL,
|
||||||
FUTEX_PRIVATE_FLAG);
|
FUTEX_PRIVATE_FLAG);
|
||||||
if (!requeued.val || errno != EWOULDBLOCK) {
|
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));
|
res, strerror(errno));
|
||||||
info("w2:futex: %x\n", f2);
|
ksft_print_dbg_msg("w2:futex: %x\n", f2);
|
||||||
if (!res)
|
if (!res)
|
||||||
futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
|
futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
|
||||||
waiter_ret = RET_FAIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info("Waiter exiting with %d\n", waiter_ret);
|
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
TEST(futex_requeue_pi_signal_restart)
|
||||||
{
|
{
|
||||||
unsigned int old_val;
|
unsigned int old_val;
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
pthread_t waiter;
|
pthread_t waiter;
|
||||||
int c, res, ret = RET_PASS;
|
int res;
|
||||||
|
|
||||||
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");
|
|
||||||
|
|
||||||
sa.sa_handler = handle_signal;
|
sa.sa_handler = handle_signal;
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = 0;
|
sa.sa_flags = 0;
|
||||||
if (sigaction(SIGUSR1, &sa, NULL)) {
|
if (sigaction(SIGUSR1, &sa, NULL))
|
||||||
error("sigaction\n", errno);
|
ksft_exit_fail_msg("sigaction\n");
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
info("m1:f2: %x\n", f2);
|
ksft_print_dbg_msg("m1:f2: %x\n", f2);
|
||||||
info("Creating waiter\n");
|
ksft_print_dbg_msg("Creating waiter\n");
|
||||||
res = create_rt_thread(&waiter, waiterfn, NULL, SCHED_FIFO, 1);
|
res = create_rt_thread(&waiter, waiterfn, NULL, SCHED_FIFO, 1);
|
||||||
if (res) {
|
if (res)
|
||||||
error("Creating waiting thread failed", res);
|
ksft_exit_fail_msg("Creating waiting thread failed");
|
||||||
ret = RET_ERROR;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
info("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
|
ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
|
||||||
info("m2:f2: %x\n", f2);
|
ksft_print_dbg_msg("m2:f2: %x\n", f2);
|
||||||
futex_lock_pi(&f2, 0, 0, FUTEX_PRIVATE_FLAG);
|
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) {
|
while (1) {
|
||||||
/*
|
/*
|
||||||
|
@ -173,11 +125,11 @@ int main(int argc, char *argv[])
|
||||||
* restart futex_wait_requeue_pi() in the kernel. Wait for the
|
* restart futex_wait_requeue_pi() in the kernel. Wait for the
|
||||||
* waiter to block on f1 again.
|
* 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);
|
pthread_kill(waiter, SIGUSR1);
|
||||||
usleep(DELAY_US);
|
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;
|
old_val = f1;
|
||||||
res = futex_cmp_requeue_pi(&f1, old_val, &(f2), 1, 0,
|
res = futex_cmp_requeue_pi(&f1, old_val, &(f2), 1, 0,
|
||||||
FUTEX_PRIVATE_FLAG);
|
FUTEX_PRIVATE_FLAG);
|
||||||
|
@ -191,12 +143,10 @@ int main(int argc, char *argv[])
|
||||||
atomic_set(&requeued, 1);
|
atomic_set(&requeued, 1);
|
||||||
break;
|
break;
|
||||||
} else if (res < 0) {
|
} else if (res < 0) {
|
||||||
error("FUTEX_CMP_REQUEUE_PI failed\n", errno);
|
ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n");
|
||||||
ret = RET_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info("m4:f2: %x\n", f2);
|
ksft_print_dbg_msg("m4:f2: %x\n", f2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Signal the waiter after requeue, waiter should return from
|
* 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
|
* futex_unlock_pi() can't happen before the signal wakeup is detected
|
||||||
* in the kernel.
|
* in the kernel.
|
||||||
*/
|
*/
|
||||||
info("Issuing SIGUSR1 to waiter\n");
|
ksft_print_dbg_msg("Issuing SIGUSR1 to waiter\n");
|
||||||
pthread_kill(waiter, SIGUSR1);
|
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);
|
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);
|
futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
|
||||||
info("m5:f2: %x\n", f2);
|
ksft_print_dbg_msg("m5:f2: %x\n", f2);
|
||||||
|
|
||||||
out:
|
|
||||||
if (ret == RET_PASS && waiter_ret)
|
|
||||||
ret = waiter_ret;
|
|
||||||
|
|
||||||
print_result(TEST_NAME, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_HARNESS_MAIN
|
||||||
|
|
|
@ -9,25 +9,16 @@
|
||||||
#include <sys/shm.h>
|
#include <sys/shm.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <fcntl.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 timeout_ns 30000000
|
||||||
#define WAKE_WAIT_US 10000
|
#define WAKE_WAIT_US 10000
|
||||||
#define SHM_PATH "futex_shm_file"
|
#define SHM_PATH "futex_shm_file"
|
||||||
|
|
||||||
void *futex;
|
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)
|
static void *waiterfn(void *arg)
|
||||||
{
|
{
|
||||||
struct timespec to;
|
struct timespec to;
|
||||||
|
@ -45,53 +36,37 @@ static void *waiterfn(void *arg)
|
||||||
return NULL;
|
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;
|
unsigned int flags = FUTEX_PRIVATE_FLAG;
|
||||||
|
u_int32_t f_private = 0;
|
||||||
pthread_t waiter;
|
pthread_t waiter;
|
||||||
void *shm;
|
int res;
|
||||||
|
|
||||||
futex = &f_private;
|
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 */
|
/* 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))
|
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);
|
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);
|
res = futex_wake(futex, 1, FUTEX_PRIVATE_FLAG);
|
||||||
if (res != 1) {
|
if (res != 1) {
|
||||||
ksft_test_result_fail("futex_wake private returned: %d %s\n",
|
ksft_test_result_fail("futex_wake private returned: %d %s\n",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_wake private succeeds\n");
|
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 */
|
/* Testing an anon page shared memory */
|
||||||
shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);
|
shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);
|
||||||
|
@ -105,67 +80,65 @@ int main(int argc, char *argv[])
|
||||||
*shared_data = 0;
|
*shared_data = 0;
|
||||||
futex = shared_data;
|
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))
|
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);
|
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);
|
res = futex_wake(futex, 1, 0);
|
||||||
if (res != 1) {
|
if (res != 1) {
|
||||||
ksft_test_result_fail("futex_wake shared (page anon) returned: %d %s\n",
|
ksft_test_result_fail("futex_wake shared (page anon) returned: %d %s\n",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_wake shared (page anon) succeeds\n");
|
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 */
|
/* Testing a file backed shared memory */
|
||||||
fd = open(SHM_PATH, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
fd = open(SHM_PATH, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
||||||
if (fd < 0) {
|
if (fd < 0)
|
||||||
perror("open");
|
ksft_exit_fail_msg("open");
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ftruncate(fd, sizeof(f_private))) {
|
if (ftruncate(fd, sizeof(f_private)))
|
||||||
perror("ftruncate");
|
ksft_exit_fail_msg("ftruncate");
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
shm = mmap(NULL, sizeof(f_private), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
shm = mmap(NULL, sizeof(f_private), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
if (shm == MAP_FAILED) {
|
if (shm == MAP_FAILED)
|
||||||
perror("mmap");
|
ksft_exit_fail_msg("mmap");
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(shm, &f_private, sizeof(f_private));
|
memcpy(shm, &f_private, sizeof(f_private));
|
||||||
|
|
||||||
futex = shm;
|
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))
|
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);
|
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);
|
res = futex_wake(shm, 1, 0);
|
||||||
if (res != 1) {
|
if (res != 1) {
|
||||||
ksft_test_result_fail("futex_wake shared (file backed) returned: %d %s\n",
|
ksft_test_result_fail("futex_wake shared (file backed) returned: %d %s\n",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_wake shared (file backed) succeeds\n");
|
ksft_test_result_pass("futex_wake shared (file backed) succeeds\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Freeing resources */
|
|
||||||
shmdt(shared_data);
|
|
||||||
munmap(shm, sizeof(f_private));
|
munmap(shm, sizeof(f_private));
|
||||||
remove(SHM_PATH);
|
remove(SHM_PATH);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
ksft_print_cnts();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_HARNESS_MAIN
|
||||||
|
|
|
@ -27,10 +27,9 @@
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#include "logging.h"
|
|
||||||
#include "futextest.h"
|
#include "futextest.h"
|
||||||
|
#include "../../kselftest_harness.h"
|
||||||
|
|
||||||
#define TEST_NAME "futex-wait-private-mapped-file"
|
|
||||||
#define PAGE_SZ 4096
|
#define PAGE_SZ 4096
|
||||||
|
|
||||||
char pad[PAGE_SZ] = {1};
|
char pad[PAGE_SZ] = {1};
|
||||||
|
@ -40,86 +39,44 @@ char pad2[PAGE_SZ] = {1};
|
||||||
#define WAKE_WAIT_US 3000000
|
#define WAKE_WAIT_US 3000000
|
||||||
struct timespec wait_timeout = { .tv_sec = 5, .tv_nsec = 0};
|
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)
|
void *thr_futex_wait(void *arg)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
info("futex wait\n");
|
ksft_print_dbg_msg("futex wait\n");
|
||||||
ret = futex_wait(&val, 1, &wait_timeout, 0);
|
ret = futex_wait(&val, 1, &wait_timeout, 0);
|
||||||
if (ret && errno != EWOULDBLOCK && errno != ETIMEDOUT) {
|
if (ret && errno != EWOULDBLOCK && errno != ETIMEDOUT)
|
||||||
error("futex error.\n", errno);
|
ksft_exit_fail_msg("futex error.\n");
|
||||||
print_result(TEST_NAME, RET_ERROR);
|
|
||||||
exit(RET_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret && errno == ETIMEDOUT)
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
TEST(wait_private_mapped_file)
|
||||||
{
|
{
|
||||||
pthread_t thr;
|
pthread_t thr;
|
||||||
int ret = RET_PASS;
|
|
||||||
int res;
|
int res;
|
||||||
int c;
|
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "chv:")) != -1) {
|
res = pthread_create(&thr, NULL, thr_futex_wait, NULL);
|
||||||
switch (c) {
|
if (res < 0)
|
||||||
case 'c':
|
ksft_exit_fail_msg("pthread_create error\n");
|
||||||
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_print_dbg_msg("wait a while\n");
|
||||||
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");
|
|
||||||
usleep(WAKE_WAIT_US);
|
usleep(WAKE_WAIT_US);
|
||||||
val = 2;
|
val = 2;
|
||||||
res = futex_wake(&val, 1, 0);
|
res = futex_wake(&val, 1, 0);
|
||||||
info("futex_wake %d\n", res);
|
ksft_print_dbg_msg("futex_wake %d\n", res);
|
||||||
if (res != 1) {
|
if (res != 1)
|
||||||
fail("FUTEX_WAKE didn't find the waiting thread.\n");
|
ksft_exit_fail_msg("FUTEX_WAKE didn't find the waiting thread.\n");
|
||||||
ret = RET_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
info("join\n");
|
ksft_print_dbg_msg("join\n");
|
||||||
pthread_join(thr, NULL);
|
pthread_join(thr, NULL);
|
||||||
|
|
||||||
out:
|
ksft_test_result_pass("wait_private_mapped_file");
|
||||||
print_result(TEST_NAME, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_HARNESS_MAIN
|
||||||
|
|
|
@ -16,26 +16,15 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
#include "futextest.h"
|
#include "futextest.h"
|
||||||
#include "futex2test.h"
|
#include "futex2test.h"
|
||||||
#include "logging.h"
|
#include "../../kselftest_harness.h"
|
||||||
|
|
||||||
#define TEST_NAME "futex-wait-timeout"
|
|
||||||
|
|
||||||
static long timeout_ns = 100000; /* 100us default timeout */
|
static long timeout_ns = 100000; /* 100us default timeout */
|
||||||
static futex_t futex_pi;
|
static futex_t futex_pi;
|
||||||
static pthread_barrier_t barrier;
|
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
|
* Get a PI lock and hold it forever, so the main thread lock_pi will block
|
||||||
* and we can test the timeout
|
* 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);
|
ret = futex_lock_pi(&futex_pi, NULL, 0, 0);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
error("futex_lock_pi failed\n", ret);
|
ksft_exit_fail_msg("futex_lock_pi failed\n");
|
||||||
|
|
||||||
pthread_barrier_wait(&barrier);
|
pthread_barrier_wait(&barrier);
|
||||||
|
|
||||||
/* Blocks forever */
|
/* Blocks forever */
|
||||||
ret = futex_wait(&lock, 0, NULL, 0);
|
ret = futex_wait(&lock, 0, NULL, 0);
|
||||||
error("futex_wait failed\n", ret);
|
ksft_exit_fail_msg("futex_wait failed\n");
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -61,12 +50,11 @@ void *get_pi_lock(void *arg)
|
||||||
/*
|
/*
|
||||||
* Check if the function returned the expected error
|
* 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) {
|
if (!res || errno != err) {
|
||||||
ksft_test_result_fail("%s returned %d\n", test_name,
|
ksft_test_result_fail("%s returned %d\n", test_name,
|
||||||
res < 0 ? errno : res);
|
res < 0 ? errno : res);
|
||||||
*ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("%s succeeds\n", test_name);
|
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,
|
static int futex_get_abs_timeout(clockid_t clockid, struct timespec *to,
|
||||||
long timeout_ns)
|
long timeout_ns)
|
||||||
{
|
{
|
||||||
if (clock_gettime(clockid, to)) {
|
if (clock_gettime(clockid, to))
|
||||||
error("clock_gettime failed\n", errno);
|
ksft_exit_fail_msg("clock_gettime failed\n");
|
||||||
return errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
to->tv_nsec += timeout_ns;
|
to->tv_nsec += timeout_ns;
|
||||||
|
|
||||||
|
@ -93,83 +79,66 @@ static int futex_get_abs_timeout(clockid_t clockid, struct timespec *to,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
TEST(wait_bitset)
|
||||||
{
|
{
|
||||||
futex_t f1 = FUTEX_INITIALIZER;
|
futex_t f1 = FUTEX_INITIALIZER;
|
||||||
int res, ret = RET_PASS;
|
|
||||||
struct timespec to;
|
struct timespec to;
|
||||||
pthread_t thread;
|
int res;
|
||||||
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);
|
|
||||||
|
|
||||||
/* initialize relative timeout */
|
/* initialize relative timeout */
|
||||||
to.tv_sec = 0;
|
to.tv_sec = 0;
|
||||||
to.tv_nsec = timeout_ns;
|
to.tv_nsec = timeout_ns;
|
||||||
|
|
||||||
res = futex_wait(&f1, f1, &to, 0);
|
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 */
|
/* FUTEX_WAIT_BITSET with CLOCK_REALTIME */
|
||||||
if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
|
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);
|
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 */
|
/* FUTEX_WAIT_BITSET with CLOCK_MONOTONIC */
|
||||||
if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns))
|
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);
|
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 */
|
/* FUTEX_WAIT_REQUEUE_PI with CLOCK_REALTIME */
|
||||||
if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
|
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);
|
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 */
|
/* FUTEX_WAIT_REQUEUE_PI with CLOCK_MONOTONIC */
|
||||||
if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns))
|
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);
|
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() */
|
/* Wait until the other thread calls futex_lock_pi() */
|
||||||
pthread_barrier_wait(&barrier);
|
pthread_barrier_wait(&barrier);
|
||||||
pthread_barrier_destroy(&barrier);
|
pthread_barrier_destroy(&barrier);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FUTEX_LOCK_PI with CLOCK_REALTIME
|
* FUTEX_LOCK_PI with CLOCK_REALTIME
|
||||||
* Due to historical reasons, FUTEX_LOCK_PI supports only 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.
|
* smaller than realtime and the syscall will timeout immediately.
|
||||||
*/
|
*/
|
||||||
if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
|
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);
|
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 */
|
/* Test operations that don't support FUTEX_CLOCK_REALTIME */
|
||||||
res = futex_lock_pi(&futex_pi, NULL, 0, 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 */
|
/* futex_waitv with CLOCK_MONOTONIC */
|
||||||
if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns))
|
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);
|
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 */
|
/* futex_waitv with CLOCK_REALTIME */
|
||||||
if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
|
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);
|
res = futex_waitv(&waitv, 1, 0, &to, CLOCK_REALTIME);
|
||||||
test_timeout(res, &ret, "futex_waitv realtime", ETIMEDOUT);
|
test_timeout(res, "futex_waitv realtime", ETIMEDOUT);
|
||||||
|
|
||||||
ksft_print_cnts();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_HARNESS_MAIN
|
||||||
|
|
|
@ -29,95 +29,55 @@
|
||||||
#include <linux/futex.h>
|
#include <linux/futex.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
|
||||||
#include "logging.h"
|
|
||||||
#include "futextest.h"
|
#include "futextest.h"
|
||||||
|
#include "../../kselftest_harness.h"
|
||||||
|
|
||||||
#define TEST_NAME "futex-wait-uninitialized-heap"
|
|
||||||
#define WAIT_US 5000000
|
#define WAIT_US 5000000
|
||||||
|
|
||||||
static int child_blocked = 1;
|
static int child_blocked = 1;
|
||||||
static int child_ret;
|
static bool child_ret;
|
||||||
void *buf;
|
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)
|
void *wait_thread(void *arg)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
child_ret = RET_PASS;
|
child_ret = true;
|
||||||
res = futex_wait(buf, 1, NULL, 0);
|
res = futex_wait(buf, 1, NULL, 0);
|
||||||
child_blocked = 0;
|
child_blocked = 0;
|
||||||
|
|
||||||
if (res != 0 && errno != EWOULDBLOCK) {
|
if (res != 0 && errno != EWOULDBLOCK) {
|
||||||
error("futex failure\n", errno);
|
ksft_exit_fail_msg("futex failure\n");
|
||||||
child_ret = RET_ERROR;
|
child_ret = false;
|
||||||
}
|
}
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
TEST(futex_wait_uninitialized_heap)
|
||||||
{
|
{
|
||||||
int c, ret = RET_PASS;
|
|
||||||
long page_size;
|
long page_size;
|
||||||
pthread_t thr;
|
pthread_t thr;
|
||||||
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
page_size = sysconf(_SC_PAGESIZE);
|
page_size = sysconf(_SC_PAGESIZE);
|
||||||
|
|
||||||
buf = mmap(NULL, page_size, PROT_READ|PROT_WRITE,
|
buf = mmap(NULL, page_size, PROT_READ|PROT_WRITE,
|
||||||
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
|
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
|
||||||
if (buf == (void *)-1) {
|
if (buf == (void *)-1)
|
||||||
error("mmap\n", errno);
|
ksft_exit_fail_msg("mmap\n");
|
||||||
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]));
|
|
||||||
|
|
||||||
|
|
||||||
ret = pthread_create(&thr, NULL, wait_thread, NULL);
|
ret = pthread_create(&thr, NULL, wait_thread, NULL);
|
||||||
if (ret) {
|
if (ret)
|
||||||
error("pthread_create\n", errno);
|
ksft_exit_fail_msg("pthread_create\n");
|
||||||
ret = RET_ERROR;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
usleep(WAIT_US);
|
||||||
|
|
||||||
ret = child_ret;
|
if (child_blocked)
|
||||||
if (child_blocked) {
|
ksft_test_result_fail("child blocked in kernel\n");
|
||||||
fail("child blocked in kernel\n");
|
|
||||||
ret = RET_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
if (!child_ret)
|
||||||
print_result(TEST_NAME, ret);
|
ksft_test_result_fail("child error\n");
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_HARNESS_MAIN
|
||||||
|
|
|
@ -21,72 +21,44 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "futextest.h"
|
#include "futextest.h"
|
||||||
#include "futex2test.h"
|
#include "futex2test.h"
|
||||||
#include "logging.h"
|
#include "../../kselftest_harness.h"
|
||||||
|
|
||||||
#define TEST_NAME "futex-wait-wouldblock"
|
|
||||||
#define timeout_ns 100000
|
#define timeout_ns 100000
|
||||||
|
|
||||||
void usage(char *prog)
|
TEST(futex_wait_wouldblock)
|
||||||
{
|
|
||||||
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[])
|
|
||||||
{
|
{
|
||||||
struct timespec to = {.tv_sec = 0, .tv_nsec = timeout_ns};
|
struct timespec to = {.tv_sec = 0, .tv_nsec = timeout_ns};
|
||||||
futex_t f1 = FUTEX_INITIALIZER;
|
futex_t f1 = FUTEX_INITIALIZER;
|
||||||
int res, ret = RET_PASS;
|
int res;
|
||||||
int c;
|
|
||||||
struct futex_waitv waitv = {
|
|
||||||
.uaddr = (uintptr_t)&f1,
|
|
||||||
.val = f1+1,
|
|
||||||
.flags = FUTEX_32,
|
|
||||||
.__reserved = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "cht:v:")) != -1) {
|
ksft_print_dbg_msg("Calling futex_wait on f1: %u @ %p with val=%u\n", f1, &f1, f1+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);
|
|
||||||
res = futex_wait(&f1, f1+1, &to, FUTEX_PRIVATE_FLAG);
|
res = futex_wait(&f1, f1+1, &to, FUTEX_PRIVATE_FLAG);
|
||||||
if (!res || errno != EWOULDBLOCK) {
|
if (!res || errno != EWOULDBLOCK) {
|
||||||
ksft_test_result_fail("futex_wait returned: %d %s\n",
|
ksft_test_result_fail("futex_wait returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_wait\n");
|
ksft_test_result_pass("futex_wait\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (clock_gettime(CLOCK_MONOTONIC, &to)) {
|
TEST(futex_waitv_wouldblock)
|
||||||
error("clock_gettime failed\n", errno);
|
{
|
||||||
return errno;
|
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;
|
to.tv_nsec += timeout_ns;
|
||||||
|
|
||||||
|
@ -95,17 +67,15 @@ int main(int argc, char *argv[])
|
||||||
to.tv_nsec -= 1000000000;
|
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);
|
res = futex_waitv(&waitv, 1, 0, &to, CLOCK_MONOTONIC);
|
||||||
if (!res || errno != EWOULDBLOCK) {
|
if (!res || errno != EWOULDBLOCK) {
|
||||||
ksft_test_result_fail("futex_waitv returned: %d %s\n",
|
ksft_test_result_fail("futex_waitv returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_waitv\n");
|
ksft_test_result_pass("futex_waitv\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
ksft_print_cnts();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_HARNESS_MAIN
|
||||||
|
|
|
@ -15,25 +15,16 @@
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/shm.h>
|
#include <sys/shm.h>
|
||||||
|
|
||||||
#include "futextest.h"
|
#include "futextest.h"
|
||||||
#include "futex2test.h"
|
#include "futex2test.h"
|
||||||
#include "logging.h"
|
#include "../../kselftest_harness.h"
|
||||||
|
|
||||||
#define TEST_NAME "futex-wait"
|
|
||||||
#define WAKE_WAIT_US 10000
|
#define WAKE_WAIT_US 10000
|
||||||
#define NR_FUTEXES 30
|
#define NR_FUTEXES 30
|
||||||
static struct futex_waitv waitv[NR_FUTEXES];
|
static struct futex_waitv waitv[NR_FUTEXES];
|
||||||
u_int32_t futexes[NR_FUTEXES] = {0};
|
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)
|
void *waiterfn(void *arg)
|
||||||
{
|
{
|
||||||
struct timespec to;
|
struct timespec to;
|
||||||
|
@ -41,7 +32,7 @@ void *waiterfn(void *arg)
|
||||||
|
|
||||||
/* setting absolute timeout for futex2 */
|
/* setting absolute timeout for futex2 */
|
||||||
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
||||||
error("gettime64 failed\n", errno);
|
ksft_exit_fail_msg("gettime64 failed\n");
|
||||||
|
|
||||||
to.tv_sec++;
|
to.tv_sec++;
|
||||||
|
|
||||||
|
@ -57,34 +48,10 @@ void *waiterfn(void *arg)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
TEST(private_waitv)
|
||||||
{
|
{
|
||||||
pthread_t waiter;
|
pthread_t waiter;
|
||||||
int res, ret = RET_PASS;
|
int res, i;
|
||||||
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]));
|
|
||||||
|
|
||||||
for (i = 0; i < NR_FUTEXES; i++) {
|
for (i = 0; i < NR_FUTEXES; i++) {
|
||||||
waitv[i].uaddr = (uintptr_t)&futexes[i];
|
waitv[i].uaddr = (uintptr_t)&futexes[i];
|
||||||
|
@ -95,7 +62,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
/* Private waitv */
|
/* Private waitv */
|
||||||
if (pthread_create(&waiter, NULL, waiterfn, NULL))
|
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);
|
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",
|
ksft_test_result_fail("futex_wake private returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_waitv private\n");
|
ksft_test_result_pass("futex_waitv private\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(shared_waitv)
|
||||||
|
{
|
||||||
|
pthread_t waiter;
|
||||||
|
int res, i;
|
||||||
|
|
||||||
/* Shared waitv */
|
/* Shared waitv */
|
||||||
for (i = 0; i < NR_FUTEXES; i++) {
|
for (i = 0; i < NR_FUTEXES; i++) {
|
||||||
|
@ -128,7 +100,7 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pthread_create(&waiter, NULL, waiterfn, NULL))
|
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);
|
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",
|
ksft_test_result_fail("futex_wake shared returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_waitv shared\n");
|
ksft_test_result_pass("futex_waitv shared\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < NR_FUTEXES; i++)
|
for (i = 0; i < NR_FUTEXES; i++)
|
||||||
shmdt(u64_to_ptr(waitv[i].uaddr));
|
shmdt(u64_to_ptr(waitv[i].uaddr));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(invalid_flag)
|
||||||
|
{
|
||||||
|
struct timespec to;
|
||||||
|
int res;
|
||||||
|
|
||||||
/* Testing a waiter without FUTEX_32 flag */
|
/* Testing a waiter without FUTEX_32 flag */
|
||||||
waitv[0].flags = FUTEX_PRIVATE_FLAG;
|
waitv[0].flags = FUTEX_PRIVATE_FLAG;
|
||||||
|
|
||||||
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
||||||
error("gettime64 failed\n", errno);
|
ksft_exit_fail_msg("gettime64 failed\n");
|
||||||
|
|
||||||
to.tv_sec++;
|
to.tv_sec++;
|
||||||
|
|
||||||
|
@ -158,17 +135,22 @@ int main(int argc, char *argv[])
|
||||||
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
|
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_waitv without FUTEX_32\n");
|
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 */
|
/* Testing a waiter with an unaligned address */
|
||||||
waitv[0].flags = FUTEX_PRIVATE_FLAG | FUTEX_32;
|
waitv[0].flags = FUTEX_PRIVATE_FLAG | FUTEX_32;
|
||||||
waitv[0].uaddr = 1;
|
waitv[0].uaddr = 1;
|
||||||
|
|
||||||
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
||||||
error("gettime64 failed\n", errno);
|
ksft_exit_fail_msg("gettime64 failed\n");
|
||||||
|
|
||||||
to.tv_sec++;
|
to.tv_sec++;
|
||||||
|
|
||||||
|
@ -177,16 +159,21 @@ int main(int argc, char *argv[])
|
||||||
ksft_test_result_fail("futex_wake private returned: %d %s\n",
|
ksft_test_result_fail("futex_wake private returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_waitv with an unaligned address\n");
|
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 */
|
/* Testing a NULL address for waiters.uaddr */
|
||||||
waitv[0].uaddr = 0x00000000;
|
waitv[0].uaddr = 0x00000000;
|
||||||
|
|
||||||
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
||||||
error("gettime64 failed\n", errno);
|
ksft_exit_fail_msg("gettime64 failed\n");
|
||||||
|
|
||||||
to.tv_sec++;
|
to.tv_sec++;
|
||||||
|
|
||||||
|
@ -195,14 +182,13 @@ int main(int argc, char *argv[])
|
||||||
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
|
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_waitv NULL address in waitv.uaddr\n");
|
ksft_test_result_pass("futex_waitv NULL address in waitv.uaddr\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Testing a NULL address for *waiters */
|
/* Testing a NULL address for *waiters */
|
||||||
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
||||||
error("gettime64 failed\n", errno);
|
ksft_exit_fail_msg("gettime64 failed\n");
|
||||||
|
|
||||||
to.tv_sec++;
|
to.tv_sec++;
|
||||||
|
|
||||||
|
@ -211,14 +197,19 @@ int main(int argc, char *argv[])
|
||||||
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
|
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_waitv NULL address in *waiters\n");
|
ksft_test_result_pass("futex_waitv NULL address in *waiters\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(invalid_clockid)
|
||||||
|
{
|
||||||
|
struct timespec to;
|
||||||
|
int res;
|
||||||
|
|
||||||
/* Testing an invalid clockid */
|
/* Testing an invalid clockid */
|
||||||
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
if (clock_gettime(CLOCK_MONOTONIC, &to))
|
||||||
error("gettime64 failed\n", errno);
|
ksft_exit_fail_msg("gettime64 failed\n");
|
||||||
|
|
||||||
to.tv_sec++;
|
to.tv_sec++;
|
||||||
|
|
||||||
|
@ -227,11 +218,9 @@ int main(int argc, char *argv[])
|
||||||
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
|
ksft_test_result_fail("futex_waitv private returned: %d %s\n",
|
||||||
res ? errno : res,
|
res ? errno : res,
|
||||||
res ? strerror(errno) : "");
|
res ? strerror(errno) : "");
|
||||||
ret = RET_FAIL;
|
|
||||||
} else {
|
} else {
|
||||||
ksft_test_result_pass("futex_waitv invalid clockid\n");
|
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
|
echo
|
||||||
if [ -z "$USE_COLOR" ]; then
|
./futex_requeue_pi
|
||||||
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
|
echo
|
||||||
# requeue pi testing
|
./futex_requeue_pi_mismatched_ops
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
./futex_requeue_pi_mismatched_ops $COLOR
|
./futex_requeue_pi_signal_restart
|
||||||
|
|
||||||
echo
|
echo
|
||||||
./futex_requeue_pi_signal_restart $COLOR
|
./futex_wait_timeout
|
||||||
|
|
||||||
echo
|
echo
|
||||||
./futex_wait_timeout $COLOR
|
./futex_wait_wouldblock
|
||||||
|
|
||||||
echo
|
echo
|
||||||
./futex_wait_wouldblock $COLOR
|
./futex_wait_uninitialized_heap
|
||||||
|
./futex_wait_private_mapped_file
|
||||||
|
|
||||||
echo
|
echo
|
||||||
./futex_wait_uninitialized_heap $COLOR
|
./futex_wait
|
||||||
./futex_wait_private_mapped_file $COLOR
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
./futex_wait $COLOR
|
./futex_requeue
|
||||||
|
|
||||||
echo
|
echo
|
||||||
./futex_requeue $COLOR
|
./futex_waitv
|
||||||
|
|
||||||
echo
|
echo
|
||||||
./futex_waitv $COLOR
|
./futex_priv_hash
|
||||||
|
|
||||||
echo
|
echo
|
||||||
./futex_priv_hash $COLOR
|
./futex_numa_mpol
|
||||||
./futex_priv_hash -g $COLOR
|
|
||||||
|
|
||||||
echo
|
|
||||||
./futex_numa_mpol $COLOR
|
|
||||||
|
|
|
@ -58,6 +58,17 @@ typedef volatile u_int32_t futex_t;
|
||||||
#define SYS_futex SYS_futex_time64
|
#define SYS_futex SYS_futex_time64
|
||||||
#endif
|
#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
|
* futex() - SYS_futex syscall wrapper
|
||||||
* @uaddr: address of first futex
|
* @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 <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
@ -104,6 +105,7 @@ struct ksft_count {
|
||||||
|
|
||||||
static struct ksft_count ksft_cnt;
|
static struct ksft_count ksft_cnt;
|
||||||
static unsigned int ksft_plan;
|
static unsigned int ksft_plan;
|
||||||
|
static bool ksft_debug_enabled;
|
||||||
|
|
||||||
static inline unsigned int ksft_test_num(void)
|
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);
|
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)
|
static inline void ksft_perror(const char *msg)
|
||||||
{
|
{
|
||||||
ksft_print_msg("%s: %s (%d)\n", msg, strerror(errno), errno);
|
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;
|
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) {
|
switch (opt) {
|
||||||
case 'f':
|
case 'f':
|
||||||
case 'F':
|
case 'F':
|
||||||
|
@ -1104,12 +1104,16 @@ static int test_harness_argv_check(int argc, char **argv)
|
||||||
case 'l':
|
case 'l':
|
||||||
test_harness_list_tests();
|
test_harness_list_tests();
|
||||||
return KSFT_SKIP;
|
return KSFT_SKIP;
|
||||||
|
case 'd':
|
||||||
|
ksft_debug_enabled = true;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
default:
|
default:
|
||||||
fprintf(stderr,
|
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-h print help\n"
|
||||||
"\t-l list all tests\n"
|
"\t-l list all tests\n"
|
||||||
|
"\t-d enable debug prints\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\t-t name include test\n"
|
"\t-t name include test\n"
|
||||||
"\t-T name exclude test\n"
|
"\t-T name exclude test\n"
|
||||||
|
@ -1142,8 +1146,9 @@ static bool test_enabled(int argc, char **argv,
|
||||||
int opt;
|
int opt;
|
||||||
|
|
||||||
optind = 1;
|
optind = 1;
|
||||||
while ((opt = getopt(argc, argv, "F:f:V:v:t:T:r:")) != -1) {
|
while ((opt = getopt(argc, argv, "dF:f:V:v:t:T:r:")) != -1) {
|
||||||
has_positive |= islower(opt);
|
if (opt != 'd')
|
||||||
|
has_positive |= islower(opt);
|
||||||
|
|
||||||
switch (tolower(opt)) {
|
switch (tolower(opt)) {
|
||||||
case 't':
|
case 't':
|
||||||
|
|
Loading…
Reference in New Issue