aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarren Hart <dvhltc@us.ibm.com>2009-11-11 00:26:48 -0800
committerDarren Hart <dvhltc@us.ibm.com>2009-11-11 00:26:48 -0800
commit2462c22b4e23574735ca98819f704c0d558112e4 (patch)
tree9a743b01a30991222adf2280c5668c4441c90802
downloadfutextest-2462c22b4e23574735ca98819f704c0d558112e4.tar.gz
Initial public commit
Signed-off-by: Darren Hart <dvhltc@us.ibm.com>
-rw-r--r--.gitignore5
-rw-r--r--Makefile8
-rw-r--r--README234
-rw-r--r--functional/Makefile18
-rw-r--r--functional/futex_requeue_pi.c429
-rw-r--r--functional/futex_requeue_pi_mismatched_ops.c146
-rw-r--r--functional/futex_requeue_pi_signal_restart.c209
-rw-r--r--functional/futex_wait_timeout.c92
-rwxr-xr-xfunctional/run.sh69
-rw-r--r--include/futextest.h220
-rwxr-xr-xperformance/run.sh35
-rwxr-xr-xrun.sh38
-rwxr-xr-xstress/run.sh35
13 files changed, 1538 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c9b062c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+functional/futex_requeue_pi
+functional/futex_requeue_pi_signal_restart
+functional/futex_wait_timeout
+
+functional/futex_requeue_pi_mismatched_ops
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b3c4111
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+SUBDIRS = functional performance stress
+
+.PHONY: all clean
+all:
+ for DIR in $(SUBDIRS); do (cd $$DIR; ${MAKE} all); done
+
+clean:
+ for DIR in $(SUBDIRS); do (cd $$DIR; ${MAKE} clean); done
diff --git a/README b/README
new file mode 100644
index 0000000..41ed174
--- /dev/null
+++ b/README
@@ -0,0 +1,234 @@
+Futex Test
+==========
+Futex Test is intended to thoroughly test the Linux kernel futex system call
+API. To the extent possible, each test is implemented using raw system calls
+as well as the glibc pthread library.
+
+Tests shall fall under one of three categories:
+o Functional
+ Functional tests shall test the documented behavior of the futex operation
+ code under test. This includes checking for proper behavior under normal use,
+ odd corner cases, regression tests, and abject abuse and misuse.
+
+o Stress
+ Stress tests shall impose a heavy load on the futex infrastructure. Tests
+ should stress the bottlenecks of the futex implementation, such as the
+ hashbucket locks, the mmap_sem (for shared futexes), and scheduler wakeups.
+
+o Performance
+ Performance tests shall measure quantifiable attributes of futex usage, such
+ as timeout latency, operations per second, scheduler wake-ups, etc.
+
+Quick Start
+-----------
+# make
+# ./run.sh
+
+Design and Implementation Goals
+-------------------------------
+o Tests should be as self contained as is practical so as to facilitate sharing
+ the individual tests on mailing list discussions and bug reports.
+o The build system shall remain as simple as possible, avoiding any archive or
+ shared object building and linking.
+o Where possible, any helper functions or other package-wide code shall be
+ implemented in header files, avoiding the need to compile intermediate object
+ files.
+o External dependendencies shall remain as minimal as possible. Currently gcc
+ and glibc are the only dependencies.
+o Tests return 0 for success and < 0 for failure.
+
+Output Formatting
+-----------------
+Test output shall be easily parsable by both human and machine. Title and
+results are printed to stdout, while intermediate ERROR or FAIL messages are
+sent to stderr. Tests shall support the -c option to print PASS, FAIL, and
+ERROR strings in color for easy visual parsing. Output shall conform to the
+following format:
+
+test_name: Description of the test
+ Arguments: arg1=val1 #units specified for clarity where appropriate
+ ERROR: Description of unexpected error
+ FAIL: Reason for test failure
+ # FIXME: Perhaps an " INFO: informational message" option would be
+ # useful here. Using -v to toggle it them on and off, as with -c.
+ # there may be multiple ERROR or FAIL messages
+Result: (PASS|FAIL|ERROR) # functional tests
+Result: (measurement (units)|ERROR) # performance tests
+Result: (COMPLETED|ERROR) # stress tests
+
+Naming
+------
+o FIXME: decide on a sane test naming scheme. Currently the tests are named
+ based on the primary futex operation they test. Eventually this will become a
+ problem as we intend to write multiple tests which collide in this namespace.
+ Perhaps something like "wait-wake-1" "wait-wake-2" is adequate, leaving the
+ detailed description in the test source and the output. Opinions welcome!
+
+Coding Style
+------------
+o The Futex Test project adheres to the coding standards set forth by Linux
+ kernel as defined in the Linux source Documentation/CodingStyle.
+
+
+--------------------------------------------------------------------------------
+
+
+Darren's Notes
+==============
+TODO
+----
+o Incorporate robust futexes
+o execve testing
+ - http://git.kernel.org/?p=linux/kernel/git/tip/linux-2.6-tip.git;a=commit;h=322a2c100a8998158445599ea437fb556aa95b11
+ - http://git.kernel.org/?p=linux/kernel/git/tip/linux-2.6-tip.git;a=commit;h=fc6b177dee33365ccb29fe6d2092223cf8d679f9
+o http://git.kernel.org/?p=linux/kernel/git/tip/linux-2.6-tip.git;a=commit;h=eaaea8036d0261d87d7072c5bc88c7ea730c18ac
+
+Futex Op Codes
+--------------
+FUTEX_WAIT
+FUTEX_WAKE
+FUTEX_FD
+FUTEX_REQUEUE
+FUTEX_CMP_REQUEUE
+FUTEX_WAKE_OP
+FUTEX_LOCK_PI
+FUTEX_UNLOCK_PI
+FUTEX_TRYLOCK_PI
+FUTEX_WAIT_BITSET
+FUTEX_WAKE_BITSET
+FUTEX_WAIT_REQUEUE_PI
+FUTEX_CMP_REQUEUE_PI
+
+Syscalls to Test
+----------------
+futex_wake
+futex_wake_op
+futex_cmp_requeue
+futex_wait
+futex_lock_pi
+futex_unlock_pi
+futex_wait_requeue_pi
+futex_cmp_requeue_pi
+
+Functional Tests
+----------------
+requeue_pi/*
+ Exercise the FUTEX_WAIT_REQUEUE_PI and FUTEX_CMP_REQUEUE_PI op codes,
+ under every possible combination of the following scenarios:
+
+ o shared and private futexes
+ o CLOCK_MONOTONIC and CLOCK_REALTIME timeouts (and none)
+ o Signal handling prior to and post requeue
+ - http://bugzilla.kernel.org/show_bug.cgi?id=14289
+ o correct and incorrect settings for val
+ o target futex owned by waker, owned by third party, unowned
+ o OWNERDIED reclaim of mutex
+ o ensure priority ordered wakeup of waiters
+
+ Error and Misuse Cases
+ ----------------------
+ o mixed shared and private futexes (should fail)
+ o pi source futex
+ o non-pi target futex
+ o unmapped shared futex fault handling
+ o bogus uaddrs
+ o invalid nr_wake and nr_requeue values
+ o mismatched wait_requeue and futex_requeue target futexes
+ o incorrect pairing of futex_wait_requeue_pi with futex_wake
+ - and the futex_wait with futex_requeue_pi
+ -http://git.kernel.org/?p=linux/kernel/git/tip/linux-2.6-tip.git;a=commit;h=2bc872036e1c5948b5b02942810bbdd8dbdb9812
+ o http://git.kernel.org/?p=linux/kernel/git/tip/linux-2.6-tip.git;a=commit;h=0729e196147692d84d4c099fcff056eba2ed61d8
+
+ Syscalls Exercised
+ ------------------
+ futex_wait_requeue_pi
+ futex_requeue
+ futex_lock_pi
+ futex_unlock_pi
+
+pi_lock/*
+ Exercise the FUTEX_LOCK_PI and FUTEX_UNLOCK_PI op codes, under every
+ possible combination of the following scenarious:
+
+ o shared and private futexes
+ o CLOCK_MONOTONIC and CLOCK_REALTIME timeouts (and none)
+ o Signal handling
+ o correct and incorrect settings for val
+ o bogus uaddrs
+ o contended and uncontended cases
+ o OWNERDIED reclaim of mutex
+
+ Error and Misuse Cases
+ ----------------------
+ o pi_unlock of a non-pi-locked futex
+ o pi_lock of an owned non-pi futex
+ o unmapped shared futex fault handling
+ o mismatched futex_lock_pi and futex_wake(_op)? calls
+ o mismatched futex_wait and futex_unlock_pi calls
+
+ Syscalls Exercised
+ ------------------
+ futex_lock_pi
+ futex_unlock_pi
+ futex_wait
+ futex_wake
+ futex_wake_op
+
+requeue/*
+ Exercise the FUTEX_WAIT and the FUTEX_CMP_REQUEUE op codes. Perform
+ basic testing for FUTEX_REQUEUE, purposefully avoiding its known
+ flaws.
+
+ Error and Misuse Cases
+ ----------------------
+
+ Syscalls Exercised
+ ------------------
+ futex_wait
+ futex_requeue
+
+wait/*
+ Exercise the FUTEX_WAIT and FUTEX_WAKE op codes.
+
+ Error and Misuse Cases
+ ----------------------
+ o spurious wakeup, see ERESTARTSYS lkml thread
+ - http://lkml.org/lkml/2009/10/10/36
+ - http://git.kernel.org/?p=linux/kernel/git/tip/linux-2.6-tip.git;a=commit;h=d58e6576b0deec6f0b9ff8450fe282da18c50883
+
+ Syscalls Exercised
+ ------------------
+ futex_wait
+ futex_wake
+ futex_wake_op
+
+
+
+Performance Tests
+-----------------
+ o attempt to expose lock contention issues, such as those exposed by
+ calling futex_wait on an unowned futex
+ o rapid lock and unlock of an uncontended futex
+ o rapid lock and unlock of a heavily conteded futex
+ o attempt to expose bottlenecks imposed by the shared hash-bucket
+ implementation
+ o attempt to expose real-time scheduling overhead
+
+Stress Tests
+------------
+ o thousands of threads/processes contending on a single futex
+ o thousands of threads/processes on thousands of futexes
+
+Other Thoughts
+--------------
+kernel-side futex fault injection
+ There are a lot of places in futex.c that have to handle faults. I
+ think some kind of a fault injection system is needed. This could be
+ enabled via a sysctl or perhaps just configured in to a debug kernel.
+ Running this test suite in a loop would allow us to achieve some
+ statistical confidence in these numerous fault paths.
+
+FUTEX_REQUEUE
+ This op code is deprecated in favor of FUTEX_CMP_REQUEUE. Do to the
+ unreliable nature of the op code, only very limited testing can be
+ performed.
diff --git a/functional/Makefile b/functional/Makefile
new file mode 100644
index 0000000..4aeac3c
--- /dev/null
+++ b/functional/Makefile
@@ -0,0 +1,18 @@
+INCLUDES = -I../include
+CFLAGS = -g -O2 -Wall -D_GNU_SOURCE $(INCLUDES)
+LDFLAGS = -lpthread -lrt
+
+HEADERS = ../include/futextest.h
+TARGETS = \
+ futex_wait_timeout \
+ futex_requeue_pi \
+ futex_requeue_pi_signal_restart \
+ futex_requeue_pi_mismatched_ops
+
+.PHONY: all clean
+all: $(TARGETS)
+
+$(TARGETS): $(HEADERS)
+
+clean:
+ rm -f $(TARGETS)
diff --git a/functional/futex_requeue_pi.c b/functional/futex_requeue_pi.c
new file mode 100644
index 0000000..a20a56d
--- /dev/null
+++ b/functional/futex_requeue_pi.c
@@ -0,0 +1,429 @@
+/******************************************************************************
+ *
+ * Copyright © International Business Machines Corp., 2006-2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * NAME
+ * futex_requeue_pi.c
+ *
+ * DESCRIPTION
+ * This test excercises the futex syscall op codes needed for requeuing
+ * priority inheritance aware POSIX condition variables and mutexes.
+ *
+ * AUTHORS
+ * Sripathi Kodi <sripathik@in.ibm.com>
+ * Darren Hart <dvhltc@us.ibm.com>
+ *
+ * HISTORY
+ * 2008-Jan-13: Initial version by Sripathi Kodi <sripathik@in.ibm.com>
+ * 2009-Nov-6: futex test adaptation by Darren Hart <dvhltc@us.ibm.com>
+ *
+ *****************************************************************************/
+
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include "futextest.h"
+
+#define PRIVATE 1
+#ifdef PRIVATE
+#define FUTEX_PRIVATE_FLAG 128
+#define PSHARED PTHREAD_PROCESS_PRIVATE
+#else
+#define FUTEX_PRIVATE_FLAG 0
+#define PSHARED PTHREAD_PROCESS_SHARED
+#endif
+
+#define THREAD_MAX 10
+#define SIGNAL_PERIOD_US 100
+
+pthread_mutex_t mutex;
+pthread_barrier_t wake_barrier;
+pthread_barrier_t waiter_barrier;
+int waiters_woken;
+futex_t wait_q = FUTEX_INITIALIZER;
+
+/* Test option defaults */
+static long timeout_ns = 0;
+static int broadcast = 0;
+static int owner = 0;
+static int locked = 0;
+
+typedef struct struct_waiter_arg {
+ long id;
+ struct timespec *timeout;
+} waiter_arg_t;
+
+void usage(char *prog)
+{
+ printf("Usage: %s\n", prog);
+ printf(" -b Broadcast wakeup (all waiters)\n");
+ printf(" -c Use color\n");
+ printf(" -h Display this help message\n");
+ printf(" -l Lock the pi futex across requeue\n");
+ printf(" -o Use a third party pi futex owner during requeue\n");
+ printf(" -t N Timeout in nanoseconds (default: 100,000)\n");
+}
+
+int create_pi_mutex(pthread_mutex_t *mutex)
+{
+ int ret;
+ pthread_mutexattr_t mutexattr;
+
+ if ((ret = pthread_mutexattr_init(&mutexattr)) != 0) {
+ fprintf(stderr, "\t%s: pthread_mutexattr_init: %s\n",
+ ERROR, strerror(ret));
+ return -1;
+ }
+ if ((ret = pthread_mutexattr_setprotocol(&mutexattr, PTHREAD_PRIO_INHERIT)) != 0) {
+ fprintf(stderr, "\t%s: pthread_mutexattr_setprotocol: %s\n",
+ ERROR, strerror(ret));
+ pthread_mutexattr_destroy(&mutexattr);
+ return -1;
+ }
+ if ((ret = pthread_mutexattr_setpshared(&mutexattr, PSHARED)) != 0) {
+ fprintf(stderr, "\t%s: pthread_mutexattr_setpshared(%d): %s\n",
+ ERROR, PSHARED, strerror(ret));
+ pthread_mutexattr_destroy(&mutexattr);
+ return -1;
+ }
+
+ int pshared;
+ pthread_mutexattr_getpshared(&mutexattr, &pshared);
+ fprintf(stderr, "\tpshared set to %d\n", pshared);
+
+ if ((ret = pthread_mutex_init(mutex, &mutexattr)) != 0) {
+ printf("pthread_mutex_init: %s\n", strerror(ret));
+ pthread_mutexattr_destroy(&mutexattr);
+ return -1;
+ }
+
+ fprintf(stderr, "\tmutex.__data.__kind: %x\n", mutex->__data.__kind);
+
+ return 0;
+}
+
+int create_rt_thread(pthread_t *pth, void*(*func)(void*), void *arg, int policy, int prio)
+{
+ int ret;
+ struct sched_param schedp;
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ memset(&schedp, 0, sizeof(schedp));
+
+ if ((ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) != 0) {
+ fprintf(stderr, "\t%s: pthread_attr_setinheritsched: %s\n",
+ ERROR, strerror(ret));
+ return -1;
+ }
+
+ if ((ret = pthread_attr_setschedpolicy(&attr, policy)) != 0) {
+ fprintf(stderr, "\t%s: pthread_attr_setschedpolicy: %s\n",
+ ERROR, strerror(ret));
+ return -1;
+ }
+
+ schedp.sched_priority = prio;
+ if ((ret = pthread_attr_setschedparam(&attr, &schedp)) != 0) {
+ fprintf(stderr, "\t%s: pthread_attr_setschedparam: %s\n",
+ ERROR, strerror(ret));
+ return -1;
+ }
+
+ if ((ret = pthread_create(pth, &attr, func, arg)) != 0) {
+ fprintf(stderr, "\t%s: pthread_create: %s\n",
+ ERROR, strerror(ret));
+ return -1;
+ }
+ return 0;
+}
+
+
+void *waiterfn(void *arg)
+{
+ waiter_arg_t *args = (waiter_arg_t *)arg;
+ unsigned int old_val;
+ int ret;
+
+ fprintf(stderr, "\tWaiter %ld: running\n", args->id); fflush(stderr);
+ /* Each thread sleeps for a different amount of time
+ * This is to avoid races, because we don't lock the
+ * external mutex here */
+ usleep(1000 * (long)args->id);
+
+ /* FIXME: need to hold the mutex prior to waiting right?... sort of... */
+
+ /* cond_wait */
+ old_val = wait_q;
+ pthread_barrier_wait(&waiter_barrier);
+ ret = futex_wait_requeue_pi(&wait_q, old_val, &(mutex.__data.__lock),
+ args->timeout, FUTEX_PRIVATE_FLAG);
+ fprintf(stderr, "\twaiter %ld woke\n", args->id); fflush(stderr);
+ if (ret < 0) {
+ ret = -errno;
+ fprintf(stderr, "\t%s: waiterfn: %s\n", ERROR, strerror(errno));
+ pthread_mutex_lock(&mutex);
+ }
+ waiters_woken++;
+ pthread_mutex_unlock(&mutex);
+
+ fprintf(stderr, "\tWaiter %ld: exiting with %d\n", args->id, ret);
+ return (void*)(long)ret;
+}
+
+void *broadcast_wakerfn(void *arg)
+{
+ unsigned int old_val;
+ int nr_wake = 1;
+ int nr_requeue = INT_MAX;
+ long lock = (long)arg;
+ int ret = 0;
+ pthread_barrier_wait(&waiter_barrier);
+ usleep(100000); /*icky*/
+ fprintf(stderr, "\tWaker: Calling broadcast\n");
+
+ if (lock) {
+ fprintf(stderr, "\tCalling FUTEX_LOCK_PI on mutex=%x @ %p\n",
+ mutex.__data.__lock, &mutex.__data.__lock);
+ pthread_mutex_lock(&mutex);
+ }
+ /* cond_broadcast */
+ old_val = wait_q;
+ ret = futex_cmp_requeue_pi(&wait_q, old_val, &(mutex.__data.__lock), nr_wake,
+ nr_requeue, FUTEX_PRIVATE_FLAG);
+ if (ret < 0) {
+ ret = -errno;
+ fprintf(stderr, "\t%s: FUTEX_CMP_REQUEUE_PI failed: %s\n",
+ ERROR, strerror(errno));
+ }
+
+ if (pthread_barrier_wait(&wake_barrier) == -EINVAL)
+ fprintf(stderr, "\t%s: broadcast_wakerfn: %s\n",
+ ERROR, strerror(errno));
+
+ if (lock)
+ pthread_mutex_unlock(&mutex);
+
+ fprintf(stderr, "\tWaker: exiting with %d\n", ret);
+ return (void *)(long)ret;;
+}
+
+void *signal_wakerfn(void *arg)
+{
+ long lock = (long)arg;
+ unsigned int old_val;
+ int nr_requeue = 0;
+ int task_count = 0;
+ int nr_wake = 1;
+ int ret = 0;
+ int i = 0;
+
+ pthread_barrier_wait(&waiter_barrier);
+ while (task_count < THREAD_MAX && waiters_woken < THREAD_MAX) {
+ fprintf(stderr, "\ttask_count: %d, waiters_woken: %d\n",
+ task_count, waiters_woken);
+ if (lock) {
+ fprintf(stderr, "\tCalling FUTEX_LOCK_PI on mutex=%x @ %p\n",
+ mutex.__data.__lock, &mutex.__data.__lock);
+ pthread_mutex_lock(&mutex);
+ }
+ fprintf(stderr, "\tWaker: Calling signal\n");
+ /* cond_signal */
+ old_val = wait_q;
+ ret = futex_cmp_requeue_pi(&wait_q, old_val, &(mutex.__data.__lock),
+ nr_wake, nr_requeue, FUTEX_PRIVATE_FLAG);
+ if (ret < 0)
+ ret = -errno;
+ fprintf(stderr, "\tfutex: %x\n", mutex.__data.__lock);
+ if (lock) {
+ fprintf(stderr, "\tCalling FUTEX_UNLOCK_PI on mutex=%x @ %p\n",
+ mutex.__data.__lock, &mutex.__data.__lock);
+ pthread_mutex_unlock(&mutex);
+ }
+ fprintf(stderr, "\tfutex: %x\n", mutex.__data.__lock);
+ if (ret < 0) {
+ fprintf(stderr, "\t%s: FUTEX_CMP_REQUEUE_PI failed: %s\n",
+ ERROR, strerror(errno));
+ break;
+ }
+
+ if (!i) {
+ fprintf(stderr, "\twaker waiting on wake_barrier\n");
+ if (pthread_barrier_wait(&wake_barrier) == -EINVAL)
+ fprintf(stderr, "\t%s: signal_wakerfn: %s",
+ ERROR, strerror(errno));
+ }
+
+ task_count += ret;
+ usleep(SIGNAL_PERIOD_US);
+ i++;
+ if (i > 1000) {
+ fprintf(stderr, "\ti>1000, giving up on pending waiters...\n");
+ break;
+ }
+ }
+ if (ret >= 0)
+ ret = task_count;
+
+ fprintf(stderr, "\tWaker: exiting with %d\n", ret);
+ fprintf(stderr, "\tWaker: waiters_woken: %d\n", waiters_woken);
+ return (void *)(long)ret;
+}
+
+void *third_party_blocker(void *arg)
+{
+ pthread_mutex_lock(&mutex);
+ if (pthread_barrier_wait(&wake_barrier) == -EINVAL)
+ fprintf(stderr, "\t%s: third_party_blocker: %s",
+ ERROR, strerror(errno));
+ pthread_mutex_unlock(&mutex);
+ return NULL;
+}
+
+int unit_test(int broadcast, long lock, int third_party_owner, long timeout_ns)
+{
+ void *(*wakerfn)(void *) = signal_wakerfn;
+ pthread_t waiter[THREAD_MAX], waker, blocker;
+ waiter_arg_t args[THREAD_MAX];
+ struct timespec ts, *tsp = NULL;
+ int ret;
+ long i;
+
+ if (timeout_ns) {
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ time_t secs = (ts.tv_nsec + timeout_ns) / 1000000000;
+ ts.tv_nsec = (ts.tv_nsec + timeout_ns) % 1000000000;
+ ts.tv_sec += secs;
+ tsp = &ts;
+ }
+
+ if ((ret = pthread_barrier_init(&wake_barrier, NULL,
+ 1+third_party_owner))) {
+ fprintf(stderr, "\t%s: pthread_barrier_init(wake_barrier) failed: %s\n",
+ ERROR, strerror(errno));
+ return ret;
+ }
+ if ((ret = pthread_barrier_init(&waiter_barrier, NULL,
+ 1+THREAD_MAX))) {
+ fprintf(stderr, "\t%s: pthread_barrier_init(waiter_barrier) failed: %s\n",
+ ERROR, strerror(errno));
+ return ret;
+ }
+
+ if (broadcast)
+ wakerfn = broadcast_wakerfn;
+
+ if (third_party_owner) {
+ if ((ret = create_rt_thread(&blocker, third_party_blocker, NULL,
+ SCHED_FIFO, 1))) {
+ fprintf(stderr, "\t%s: Creating third party blocker thread failed: %s\n",
+ ERROR, strerror(errno));
+ goto out;
+ }
+ }
+
+ waiters_woken = 0;
+ for (i = 0; i < THREAD_MAX; i++) {
+ args[i].id = i;
+ args[i].timeout = tsp;
+ fprintf(stderr, "\tStarting thread %ld\n", i); fflush(stderr);
+ if ((ret = create_rt_thread(&waiter[i], waiterfn, (void *)&args[i],
+ SCHED_FIFO, 1))) {
+ fprintf(stderr, "\t%s: Creating waiting thread failed: %s\n",
+ ERROR, strerror(errno));
+ goto out;
+ }
+ }
+ if ((ret = create_rt_thread(&waker, wakerfn, (void *)lock,
+ SCHED_FIFO, 1))) {
+ fprintf(stderr, "\t%s: Creating waker thread failed: %s\n",
+ ERROR, strerror(errno));
+ goto out;
+ }
+
+ /* Wait for threads to finish */
+ for (i=0; i<THREAD_MAX; i++) {
+ pthread_join(waiter[i], NULL);
+ }
+ if (third_party_owner)
+ pthread_join(blocker, NULL);
+ pthread_join(waker, NULL);
+
+out:
+ if ((ret = pthread_barrier_destroy(&wake_barrier)))
+ fprintf(stderr, "\t%s: pthread_barrier_destroy(wake_barrier) failed: %s\n",
+ ERROR, strerror(errno));
+ if ((ret = pthread_barrier_destroy(&waiter_barrier)))
+ fprintf(stderr, "\t%s: pthread_barrier_destroy(waiter_barrier) failed: %s\n",
+ ERROR, strerror(errno));
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int c, ret;
+
+ while ((c = getopt(argc, argv, "bchlot:")) != -1) {
+ switch(c) {
+ case 'b':
+ broadcast = 1;
+ break;
+ case 'c':
+ futextest_use_color(1);
+ break;
+ case 'h':
+ usage(basename(argv[0]));
+ exit(0);
+ case 'l':
+ locked = 1;
+ break;
+ case 'o':
+ owner = 1;
+ break;
+ case 't':
+ timeout_ns = atoi(optarg);
+ break;
+ default:
+ usage(basename(argv[0]));
+ exit(1);
+ }
+ }
+
+ printf("%s: Test requeue functionality\n", basename(argv[0]));
+ printf("\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n",
+ broadcast, locked, owner, timeout_ns);
+
+ if ((ret = create_pi_mutex(&mutex)) != 0) {
+ printf("Creating pi mutex failed\n");
+ exit(1);
+ }
+
+ /*
+ * 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);
+
+ /* FIXME: need to distinguish between FAIL and ERROR */
+ printf("Result: %s\n", ret ? ERROR : PASS);
+ return ret;
+}
diff --git a/functional/futex_requeue_pi_mismatched_ops.c b/functional/futex_requeue_pi_mismatched_ops.c
new file mode 100644
index 0000000..14707f2
--- /dev/null
+++ b/functional/futex_requeue_pi_mismatched_ops.c
@@ -0,0 +1,146 @@
+/******************************************************************************
+ *
+ * Copyright © International Business Machines Corp., 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * NAME
+ * futex_requeue_pi_mismatched_ops.c
+ *
+ * DESCRIPTION
+ * 1. Block a thread using FUTEX_WAIT
+ * 2. Attempt to use FUTEX_CMP_REQUEUE_PI on the futex from 1.
+ * 3. The kernel must detect the mismatch and return -EINVAL.
+ *
+ * AUTHOR
+ * Darren Hart <dvhltc@us.ibm.com>
+ *
+ * HISTORY
+ * 2009-Nov-9: Initial version by Darren Hart <dvhltc@us.ibm.com>
+ *
+ *****************************************************************************/
+
+#include <errno.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "futextest.h"
+
+futex_t f1 = FUTEX_INITIALIZER;
+futex_t f2 = FUTEX_INITIALIZER;
+int child_ret = 0;
+
+void usage(char *prog)
+{
+ printf("Usage: %s\n", prog);
+ printf(" -c Use color\n");
+ printf(" -h Display this help message\n");
+}
+
+void *blocking_child(void *arg)
+{
+ child_ret = futex_wait(&f1, f1, NULL, FUTEX_PRIVATE_FLAG);
+ if (child_ret < 0) {
+ child_ret = -errno;
+ fprintf(stderr, "\t%s: futex_wait: %s\n", ERROR, strerror(errno));
+ }
+ return (void *)&child_ret;
+}
+
+int main(int argc, char *argv[])
+{
+ pthread_t child;
+ int ret = 0;
+ int c;
+
+ while ((c = getopt(argc, argv, "ch")) != -1) {
+ switch(c) {
+ case 'c':
+ futextest_use_color(1);
+ break;
+ case 'h':
+ usage(basename(argv[0]));
+ exit(0);
+ default:
+ usage(basename(argv[0]));
+ exit(1);
+ }
+ }
+
+ printf("%s: Detect mismatched requeue_pi operations\n",
+ basename(argv[0]));
+
+ ret = pthread_create(&child, NULL, blocking_child, NULL);
+ if (ret) {
+ fprintf(stderr, "\t%s: pthread_create: %s\n",
+ ERROR, strerror(errno));
+ goto out;
+ }
+ /* Allow the child to block in the kernel. */
+ sleep(1);
+
+ /*
+ * The kernel should detect the waiter did not setup the
+ * q->requeue_pi_key and return -EINVAL. If it does not,
+ * it likely gave the lock to the child, which is now hung
+ * in the kernel.
+ */
+ ret = futex_cmp_requeue_pi(&f1, f1, &f2, 1, 0, FUTEX_PRIVATE_FLAG);
+ if (ret < 0) {
+ ret = -errno;
+ if (ret == -EINVAL) {
+ /*
+ * The kernel correctly detected the mismatched
+ * requeue_pi target and aborted. Wake the child with
+ * FUTEX_WAKE.
+ */
+ ret = futex_wake(&f1, f1, 1, FUTEX_PRIVATE_FLAG);
+ if (ret == 1)
+ ret = 0;
+ else if (ret < 0)
+ fprintf(stderr, "\t%s: futex_wake: %s\n",
+ ERROR, strerror(errno));
+ else {
+ fprintf(stderr, "\t%s: futex_wake did not wake"
+ "the child\n", ERROR);
+ ret = -1;
+ }
+ } else {
+ fprintf(stderr, "\t%s: futex_cmp_requeue_pi: %s\n",
+ ERROR, strerror(errno));
+ }
+ } else if (ret > 0) {
+ fprintf(stderr, "\t%s: futex_cmp_requeue_pi failed to detect "
+ "the mismatch\n", FAIL);
+ } else {
+ fprintf(stderr, "\t%s: futex_cmp_requeue_pi found no waiters\n",
+ ERROR);
+ ret = -1;
+ }
+
+ pthread_join(child, NULL);
+
+ if (!ret)
+ ret = child_ret;
+
+
+ out:
+ /* If the kernel crashes, we shouldn't return at all. */
+ printf("Result: %s\n", ret == 0 ? PASS : FAIL);
+ return ret;
+}
diff --git a/functional/futex_requeue_pi_signal_restart.c b/functional/futex_requeue_pi_signal_restart.c
new file mode 100644
index 0000000..dbaf23e
--- /dev/null
+++ b/functional/futex_requeue_pi_signal_restart.c
@@ -0,0 +1,209 @@
+/******************************************************************************
+ *
+ * Copyright © International Business Machines Corp., 2006-2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * NAME
+ * requeue_pi_sig_restart.c
+ *
+ * DESCRIPTION
+ * This test exercises the futex_wait_requeue_pi signal restart after a
+ * deliberate wake-up.
+ *
+ * AUTHORS
+ * Darren Hart <dvhltc@us.ibm.com>
+ *
+ * HISTORY
+ * 2008-May-5: Initial version by Darren Hart <dvhltc@us.ibm.com>
+ *
+ *****************************************************************************/
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "futextest.h"
+
+futex_t f1 = FUTEX_INITIALIZER;
+futex_t f2 = FUTEX_INITIALIZER;
+
+typedef struct struct_waiter_arg {
+ long id;
+ struct timespec *timeout;
+} waiter_arg_t;
+
+void usage(char *prog)
+{
+ printf("Usage: %s\n", prog);
+ printf(" -c Use color\n");
+ printf(" -h Display this help message\n");
+}
+
+int create_rt_thread(pthread_t *pth, void*(*func)(void*), void *arg, int policy, int prio)
+{
+ int ret;
+ struct sched_param schedp;
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ memset(&schedp, 0, sizeof(schedp));
+
+ if ((ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) != 0) {
+ fprintf(stderr, "\t%s: pthread_attr_setinheritsched: %s\n",
+ ERROR, strerror(ret));
+ return -1;
+ }
+
+ if ((ret = pthread_attr_setschedpolicy(&attr, policy)) != 0) {
+ fprintf(stderr, "\t%s: pthread_attr_setschedpolicy: %s\n",
+ ERROR, strerror(ret));
+ return -1;
+ }
+
+ schedp.sched_priority = prio;
+ if ((ret = pthread_attr_setschedparam(&attr, &schedp)) != 0) {
+ fprintf(stderr, "\t%s: pthread_attr_setschedparam: %s\n",
+ ERROR, strerror(ret));
+ return -1;
+ }
+
+ if ((ret = pthread_create(pth, &attr, func, arg)) != 0) {
+ fprintf(stderr, "\t%s: pthread_create: %s\n",
+ ERROR, strerror(ret));
+ return -1;
+ }
+ return 0;
+}
+
+void handle_signal(int signo)
+{
+ fprintf(stderr, "\thandled signal: %d\n", signo);
+}
+
+void *waiterfn(void *arg)
+{
+ unsigned int old_val;
+ int ret;
+
+ fprintf(stderr, "\tWaiter running\n");
+
+ fprintf(stderr, "\tCalling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
+ /* cond_wait */
+ old_val = f1;
+ ret = futex_wait_requeue_pi(&f1, old_val, &(f2), NULL, FUTEX_PRIVATE_FLAG);
+ if (ret < 0) {
+ ret = -errno;
+ fprintf(stderr, "\t%s: waiterfn: %s\n", ERROR, strerror(errno));
+ }
+ fprintf(stderr, "\tFUTEX_WAIT_REQUEUE_PI returned: %d\n", ret); fflush(stdout);
+ fprintf(stderr, "\tw1:futex: %x\n", f2); fflush(stdout);
+ if (ret)
+ futex_lock_pi(&f2, 0, 0, FUTEX_PRIVATE_FLAG);
+ futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
+
+ fprintf(stderr, "\tWaiter exiting with %d\n", ret); fflush(stdout);
+ fprintf(stderr, "\tw2:futex: %x\n", f2); fflush(stdout);
+ return (void*)(long)ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+ unsigned int old_val;
+ struct sigaction sa;
+ pthread_t waiter;
+ int c, ret = 0;
+
+ while ((c = getopt(argc, argv, "ch")) != -1) {
+ switch(c) {
+ case 'c':
+ futextest_use_color(1);
+ break;
+ case 'h':
+ usage(basename(argv[0]));
+ exit(0);
+ default:
+ usage(basename(argv[0]));
+ exit(1);
+ }
+ }
+
+ printf("%s: Test signal handling during requeue_pi\n", basename(argv[0]));
+ printf("\tArguments: <none>\n");
+
+ sa.sa_handler = handle_signal;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ if (sigaction(SIGUSR1, &sa, NULL)) {
+ fprintf(stderr, "\t%s: sigaction: %s\n", ERROR, strerror(errno));
+ exit(1);
+ }
+
+ fprintf(stderr, "\tm1:futex: %x\n", f2);
+ fprintf(stderr, "\tCreating waiter\n");
+ if ((ret = create_rt_thread(&waiter, waiterfn, NULL, SCHED_FIFO, 1))) {
+ perror("Creating waiting thread failed");
+ exit(1);
+ }
+ fprintf(stderr, "\tm2:futex: %x\n", f2);
+
+ fprintf(stderr, "\tCalling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2);
+
+ futex_lock_pi(&f2, 0, 0, FUTEX_PRIVATE_FLAG);
+ fprintf(stderr, "\tm3:futex: %x\n", f2);
+
+ fprintf(stderr, "\tWaking waiter via FUTEX_CMP_REQUEUE_PI\n");fflush(stdout);
+ /* cond_signal */
+ old_val = f1;
+ ret = futex_cmp_requeue_pi(&f1, old_val, &(f2),
+ 1, 0, FUTEX_PRIVATE_FLAG);
+ if (ret < 0) {
+ ret = -errno;
+ fprintf(stderr, "\t%s: FUTEX_CMP_REQUEUE_PI failed: %s\n",
+ ERROR, strerror(errno));
+ /* FIXME - do something sane.... */
+ }
+ fprintf(stderr, "\tm4:futex: %x\n", f2);
+
+ /* give the waiter time to wake and block on the lock */
+ sleep(2);
+ fprintf(stderr, "\tm5:futex: %x\n", f2);
+
+ /*
+ * signal the waiter to force a syscall restart to
+ * futex_lock_pi_restart()
+ */
+ fprintf(stderr, "\tIssuing SIGUSR1 to waiter\n");
+ pthread_kill(waiter, SIGUSR1);
+
+ /* give the signal time to get to the waiter */
+ sleep(2);
+ fprintf(stderr, "\tm6:futex: %x\n", f2);
+ fprintf(stderr, "\tCalling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", f2, &f2);
+ futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
+
+ /* Wait for waiter to finish */
+ fprintf(stderr, "\tWaiting for waiter to return\n");
+ pthread_join(waiter, NULL);
+ fprintf(stderr, "\tm7:futex: %x\n", f2);
+
+ printf("Result: %s\n", ret ? ERROR : PASS);
+ return ret;
+}
diff --git a/functional/futex_wait_timeout.c b/functional/futex_wait_timeout.c
new file mode 100644
index 0000000..d419733
--- /dev/null
+++ b/functional/futex_wait_timeout.c
@@ -0,0 +1,92 @@
+/******************************************************************************
+ *
+ * Copyright © International Business Machines Corp., 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * NAME
+ * futex_wait.c
+ *
+ * DESCRIPTION
+ * Block on a futex and wait for timeout.
+ *
+ * AUTHOR
+ * Darren Hart <dvhltc@us.ibm.com>
+ *
+ * HISTORY
+ * 2009-Nov-6: Initial version by Darren Hart <dvhltc@us.ibm.com>
+ *
+ *****************************************************************************/
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "futextest.h"
+
+static long timeout_ns = 100000; /* 100us default timeout */
+
+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");
+}
+
+int main(int argc, char *argv[])
+{
+ futex_t f1 = FUTEX_INITIALIZER;
+ struct timespec to;
+ int ret = 0;
+ int c;
+
+ while ((c = getopt(argc, argv, "cht:")) != -1) {
+ switch(c) {
+ case 'c':
+ futextest_use_color(1);
+ break;
+ case 'h':
+ usage(basename(argv[0]));
+ exit(0);
+ case 't':
+ timeout_ns = atoi(optarg);
+ break;
+ default:
+ usage(basename(argv[0]));
+ exit(1);
+ }
+ }
+
+ printf("%s: Block on a futex and wait for timeout\n", basename(argv[0]));
+ printf("\tArguments: timeout=%ldns\n", timeout_ns);
+
+ /* 100us relative timeout */
+ to.tv_sec = 0;
+ to.tv_nsec = timeout_ns;
+ ret = futex_wait(&f1, f1, &to, FUTEX_PRIVATE_FLAG);
+ if (ret < 0) {
+ ret = -errno;
+ if (ret == -ETIMEDOUT)
+ ret = 0;
+ else
+ perror("ERROR: futex_wait");
+ }
+
+ printf("Result: %s\n", ret ? ERROR : PASS);
+ return ret;
+}
diff --git a/functional/run.sh b/functional/run.sh
new file mode 100755
index 0000000..d32d6fd
--- /dev/null
+++ b/functional/run.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+###############################################################################
+#
+# Copyright © International Business Machines Corp., 2009
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+# the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# NAME
+# run.sh
+#
+# DESCRIPTION
+# Run tests in the current directory.
+#
+# AUTHOR
+# Darren Hart <dvhltc@us.ibm.com>
+#
+# HISTORY
+# 2009-Nov-9: Initial version by Darren Hart <dvhltc@us.ibm.com>
+#
+###############################################################################
+
+echo
+# requeue pi testing
+# without timeouts
+./futex_requeue_pi
+./futex_requeue_pi -c -b
+./futex_requeue_pi -c -b -l
+./futex_requeue_pi -c -b -o
+./futex_requeue_pi -c -l
+./futex_requeue_pi -c -o
+# with timeouts
+./futex_requeue_pi -c -b -l -t 5000
+./futex_requeue_pi -c -l -t 5000
+./futex_requeue_pi -c -b -l -t 500000
+./futex_requeue_pi -c -l -t 500000
+./futex_requeue_pi -c -b -t 5000
+./futex_requeue_pi -c -t 5000
+./futex_requeue_pi -c -b -t 500000
+./futex_requeue_pi -c -t 500000
+./futex_requeue_pi -c -b -o -t 5000
+./futex_requeue_pi -c -l -t 5000
+./futex_requeue_pi -c -b -o -t 500000
+./futex_requeue_pi -c -l -t 500000
+# with long timeout
+./futex_requeue_pi -c -b -l -t 2000000000
+./futex_requeue_pi -c -l -t 2000000000
+
+
+echo
+./futex_requeue_pi_mismatched_ops -c
+
+echo
+./futex_requeue_pi_signal_restart -c
+
+echo
+./futex_wait_timeout -c
diff --git a/include/futextest.h b/include/futextest.h
new file mode 100644
index 0000000..9d1c90f
--- /dev/null
+++ b/include/futextest.h
@@ -0,0 +1,220 @@
+/******************************************************************************
+ *
+ * Copyright © International Business Machines Corp., 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * NAME
+ * futex.h
+ *
+ * DESCRIPTION
+ * Glibc independent futex library for testing kernel functionality.
+ *
+ * AUTHOR
+ * Darren Hart <dvhltc@us.ibm.com>
+ *
+ * HISTORY
+ * 2009-Nov-6: Initial version by Darren Hart <dvhltc@us.ibm.com>
+ *
+ *****************************************************************************/
+
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <linux/futex.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'
+char PASS_COLOR[] = {BRIGHT_GREEN, ' ', 'P', 'A', 'S', 'S', RESET_COLOR, 0};
+char ERROR_COLOR[] = {BRIGHT_YELLOW, 'E', 'R', 'R', 'O', 'R', RESET_COLOR, 0};
+char FAIL_COLOR[] = {BRIGHT_RED, ' ', 'F', 'A', 'I', 'L', RESET_COLOR, 0};
+char PASS_NORMAL[] = " PASS";
+char ERROR_NORMAL[] = "ERROR";
+char FAIL_NORMAL[] = " FAIL";
+char *PASS = PASS_NORMAL;
+char *ERROR = ERROR_NORMAL;
+char *FAIL = FAIL_NORMAL;
+
+typedef volatile __uint32_t futex_t;
+#define FUTEX_INITIALIZER 0
+
+/* Define the newer op codes if the system header file is not up to date. */
+#ifndef FUTEX_WAIT_REQUEUE_PI
+#define FUTEX_WAIT_REQUEUE_PI 11
+#endif
+#ifndef FUTEX_CMP_REQUEUE_PI
+#define FUTEX_CMP_REQUEUE_PI 12
+#endif
+#ifndef FUTEX_WAIT_REQUEUE_PI_PRIVATE
+#define FUTEX_WAIT_REQUEUE_PI_PRIVATE (FUTEX_WAIT_REQUEUE_PI | \
+ FUTEX_PRIVATE_FLAG)
+#endif
+#ifndef FUTEX_REQUEUE_PI_PRIVATE
+#define FUTEX_CMP_REQUEUE_PI_PRIVATE (FUTEX_CMP_REQUEUE_PI | \
+ FUTEX_PRIVATE_FLAG)
+#endif
+
+/**
+ * futex() - SYS_futex syscall wrapper
+ * @uaddr: address of first futex
+ * @op: futex op code
+ * @val: typically expected value of uaddr, but varies by op
+ * @timeout: typically an absolute struct timespec (except where noted
+ * otherwise). Overloaded by some ops
+ * @uaddr2: address of second futex for some ops\
+ * @val3: varies by op
+ * @opflags: flags to be bitwise OR'd with op, such as FUTEX_PRIVATE_FLAG
+ *
+ * futex() is used by all the following futex op wrappers. It can also be
+ * used for misuse and abuse testing. Generally, the specific op wrappers
+ * should be used instead.
+ *
+ * These argument descriptions are the defaults for all
+ * like-named arguments in the following wrappers except where noted below.
+ */
+#define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
+ syscall(SYS_futex, uaddr, op | opflags, val, timeout, uaddr2, val3);
+
+/**
+ * futex_wait() - block on uaddr with optional timeout
+ * @timeout: relative timeout
+ */
+#define futex_wait(uaddr, val, timeout, opflags) \
+ futex(uaddr, FUTEX_WAIT, val, timeout, NULL, 0, opflags)
+
+/**
+ * futex_wake() - wake one or more tasks blocked on uaddr
+ * @nr_wake: wake up to this many tasks
+ */
+#define futex_wake(uaddr, val, nr_wake, opflags) \
+ futex(uaddr, FUTEX_WAKE, val, NULL, NULL, nr_wake, opflags)
+
+/**
+ * futex_wait_bitset() - block on uaddr with bitset
+ * @bitset: bitset to be used with futex_wake_bitset
+ */
+#define futex_wait_bitset(uaddr, val, timeout, bitset, opflags) \
+ futex(uaddr, FUTEX_WAIT_BITSET, val, timeout, NULL, bitset, opflags)
+
+/**
+ * futex_wake_bitset() - wake one or more tasks blocked on uaddr with bitset
+ * @bitset: bitset to compare with that used in futex_wait_bitset
+ */
+#define futex_wake_bitset(uaddr, val, nr_wake, bitset, opflags) \
+ futex(uaddr, FUTEX_WAKE_BITSET, val, NULL, NULL, bitset, opflags)
+
+/**
+ * futex_lock_pi() - block on uaddr as a PI mutex
+ * @detect: whether (1) or not (0) to perform deadlock detection
+ */
+#define futex_lock_pi(uaddr, timeout, detect, opflags) \
+ futex(uaddr, FUTEX_LOCK_PI, detect, timeout, NULL, 0, opflags)
+
+/**
+ * futex_unlock_pi() - release uaddr as a PI mutex, waking the top waiter
+ */
+#define futex_unlock_pi(uaddr, opflags) \
+ futex(uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0, opflags)
+
+/**
+ * futex_wake_op() - FIXME: COME UP WITH A GOOD ONE LINE DESCRIPTION
+ */
+#define futex_wake_op(uaddr, uaddr2, nr_wake, nr_wake2, wake_op, opflags) \
+ futex(uaddr, FUTEX_WAKE_OP, nr_wake, nr_wake2, uaddr2, wake_op, opflags)
+
+/**
+ * futex_requeue() - requeue without expected value comparison, deprecated
+ * @nr_wake: wake up to this many tasks
+ * @nr_requeue: requeue up to this many tasks
+ *
+ * Due to its inherently racy implementation, futex_requeue() is deprecated in
+ * favor of futex_cmp_requeue().
+ */
+#define futex_requeue(uaddr, uaddr2, nr_wake, nr_requeue, opflags) \
+ futex(uaddr, FUTEX_REQUEUE, nr_wake, nr_requeue, uaddr2, 0, opflags)
+
+/**
+ * futex_cmp_requeue() - requeue tasks from uaddr to uaddr2
+ * @nr_wake: wake up to this many tasks
+ * @nr_requeue: requeue up to this many tasks
+ */
+#define futex_cmp_requeue(uaddr, val, uaddr2, nr_wake, nr_requeue, opflags) \
+ futex(uaddr, FUTEX_CMP_REQUEUE, nr_wake, nr_requeue, uaddr2, val, \
+ opflags)
+
+/**
+ * futex_wait_requeue_pi() - block on uaddr and prepare to requeue to uaddr2
+ * @uaddr: non-PI futex source
+ * @uaddr2: PI futex target
+ *
+ * This is the first half of the requeue_pi mechanism. It shall always be
+ * paired with futex_cmp_requeue_pi().
+ */
+#define futex_wait_requeue_pi(uaddr, val, uaddr2, timeout, opflags) \
+ futex(uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0, opflags)
+
+/**
+ * futex_cmp_requeue_pi() - requeue tasks from uaddr to uaddr2 (PI aware)
+ * @uaddr: non-PI futex source
+ * @uaddr2: PI futex target
+ * @nr_wake: wake up to this many tasks
+ * @nr_requeue: requeue up to this many tasks
+ */
+#define futex_cmp_requeue_pi(uaddr, val, uaddr2, nr_wake, nr_requeue, opflags) \
+ futex(uaddr, FUTEX_CMP_REQUEUE_PI, nr_wake, nr_requeue, uaddr2, val, \
+ opflags)
+
+/**
+ * futex_cmpxchg() - Atomic compare and exchange
+ * @uaddr: The address of the futex to be modified
+ * @oldval: The expected value of the futex
+ * @newval: The new value to try and assign the futex
+ *
+ * Implement cmpxchg using gcc atomic builtins.
+ * http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html
+ */
+int futex_cmpxchg(futex_t *uaddr, u_int32_t oldval, u_int32_t newval)
+{
+ return __sync_val_compare_and_swap(uaddr, oldval, newval);
+}
+
+/**
+ * futextest_use_color() - Use colored output for PASS, ERROR, and FAIL strings
+ * @use_color: use color (1) or not (0)
+ */
+void futextest_use_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;
+ }
+}
diff --git a/performance/run.sh b/performance/run.sh
new file mode 100755
index 0000000..655fc39
--- /dev/null
+++ b/performance/run.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+###############################################################################
+#
+# Copyright © International Business Machines Corp., 2009
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+# the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# NAME
+# run.sh
+#
+# DESCRIPTION
+# Run tests in the current directory.
+#
+# AUTHOR
+# Darren Hart <dvhltc@us.ibm.com>
+#
+# HISTORY
+# 2009-Nov-9: Initial version by Darren Hart <dvhltc@us.ibm.com>
+#
+###############################################################################
+
+exit 0
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000..0c54a74
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+###############################################################################
+#
+# Copyright © International Business Machines Corp., 2009
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+# the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# NAME
+# run.sh
+#
+# DESCRIPTION
+# Run all tests under the functional, performance, and stress directories.
+# Format and summarize the results.
+#
+# AUTHOR
+# Darren Hart <dvhltc@us.ibm.com>
+#
+# HISTORY
+# 2009-Nov-9: Initial version by Darren Hart <dvhltc@us.ibm.com>
+#
+###############################################################################
+
+(cd functional; ./run.sh)
+(cd performance; ./run.sh)
+(cd stress; ./run.sh)
diff --git a/stress/run.sh b/stress/run.sh
new file mode 100755
index 0000000..655fc39
--- /dev/null
+++ b/stress/run.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+###############################################################################
+#
+# Copyright © International Business Machines Corp., 2009
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+# the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# NAME
+# run.sh
+#
+# DESCRIPTION
+# Run tests in the current directory.
+#
+# AUTHOR
+# Darren Hart <dvhltc@us.ibm.com>
+#
+# HISTORY
+# 2009-Nov-9: Initial version by Darren Hart <dvhltc@us.ibm.com>
+#
+###############################################################################
+
+exit 0