From 8cc1caa8190c0dcb95d690d43f73d3fea867d377 Mon Sep 17 00:00:00 2001
From: Glenn Elliott <gelliott@cs.unc.edu>
Date: Sun, 19 May 2013 23:35:28 -0400
Subject: [PATCH] squash

---
 Makefile            |  127 ++-
 bin/base_mt_task.c  |   38 +-
 bin/base_task.c     |   38 +-
 bin/null_call.c     |    4 +-
 bin/release_ts.c    |   37 +-
 bin/rt_launch.c     |   62 +-
 bin/rtspin.c        |  132 ++-
 bin/uncache.c       |  381 ++++++++
 gpu/aux_threads.c   |  313 ++++++
 gpu/budget.cpp      |  379 ++++++++
 gpu/dgl.c           |  282 ++++++
 gpu/gpuspin.cu      | 2705 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gpu/ikglptest.c     |  653 +++++++++++++
 gpu/locktest.c      |  206 ++++
 gpu/nested.c        |  262 +++++
 gpu/normal_task.c   |   90 ++
 include/common.h    |    7 +
 include/litmus.h    |  322 +++++-
 include/migration.h |   24 +
 include/tests.h     |    7 +-
 src/kernel_iface.c  |   17 +-
 src/litmus.c        |  270 ++++-
 src/migration.c     |  217 +++++
 src/signal.c        |  109 +++
 src/syscalls.c      |   82 +-
 src/task.c          |   24 +-
 tests/core_api.c    |    9 +-
 tests/fdso.c        |   10 +-
 tests/locks.c       |   12 +-
 tests/nesting.c     |  468 +++++++++
 tests/pcp.c         |  224 ++++-
 tests/sched.c       |   15 +-
 32 files changed, 7264 insertions(+), 262 deletions(-)
 create mode 100644 bin/uncache.c
 create mode 100644 gpu/aux_threads.c
 create mode 100644 gpu/budget.cpp
 create mode 100644 gpu/dgl.c
 create mode 100644 gpu/gpuspin.cu
 create mode 100644 gpu/ikglptest.c
 create mode 100644 gpu/locktest.c
 create mode 100644 gpu/nested.c
 create mode 100644 gpu/normal_task.c
 create mode 100644 include/migration.h
 create mode 100644 src/migration.c
 create mode 100644 src/signal.c
 create mode 100644 tests/nesting.c

diff --git a/Makefile b/Makefile
index 8195752..e877ca4 100644
--- a/Makefile
+++ b/Makefile
@@ -14,13 +14,29 @@ ARCH ?= ${host-arch}
 # LITMUS_KERNEL -- where to find the litmus kernel?
 LITMUS_KERNEL ?= ../litmus-rt
 
+# NUMA Support. Comment out to disable. Requires libnuma dev files.
+#
+# Enabling this option will ensure all memory resides on NUMA nodes
+# that overlap clusters/partitions specified by a call to be_migrate*().
+NUMA_SUPPORT = dummyval
 
 # ##############################################################################
 # Internal configuration.
 
 # compiler flags
-flags-debug    = -Wall -Werror -g -Wdeclaration-after-statement
+flags-debug    = -O2 -Wall -Werror -g -Wdeclaration-after-statement
+#flags-debug    = -Wall -Werror -g -Wdeclaration-after-statement
+flags-debug-cpp    = -O2 -Wall -Werror -g
+#flags-debug-cpp    = -Wall -Werror -g
 flags-api      = -D_XOPEN_SOURCE=600 -D_GNU_SOURCE
+flags-misc     = -fasynchronous-unwind-tables -fnon-call-exceptions
+
+flags-cu-debug = -g -G -Xcompiler -Wall -Xcompiler -Werror
+flags-cu-optim = -O2 -Xcompiler -march=native
+#flags-cu-optim = -Xcompiler -march=native
+flags-cu-nvcc = --use_fast_math -gencode arch=compute_20,code=sm_20 -gencode arch=compute_30,code=sm_30
+flags-cu-misc  = -Xcompiler -fasynchronous-unwind-tables -Xcompiler -fnon-call-exceptions -Xcompiler -malign-double -Xcompiler -pthread
+flags-cu-x86_64 = -m64
 
 # architecture-specific flags
 flags-i386     = -m32
@@ -48,12 +64,27 @@ LIBLITMUS ?= .
 headers = -I${LIBLITMUS}/include -I${LIBLITMUS}/arch/${include-${ARCH}}/include
 
 # combine options
-CPPFLAGS = ${flags-api} ${flags-${ARCH}} -DARCH=${ARCH} ${headers}
-CFLAGS   = ${flags-debug}
+CPPFLAGS = ${flags-api} ${flags-debug-cpp} ${flags-misc} ${flags-${ARCH}} -DARCH=${ARCH} ${headers}
+CUFLAGS  = ${flags-api} ${flags-cu-debug} ${flags-cu-optim} ${flags-cu-nvcc} ${flags-cu-misc} -DARCH=${ARCH} ${headers}
+CFLAGS   = ${flags-debug} ${flags-misc}
 LDFLAGS  = ${flags-${ARCH}}
 
+ifdef NUMA_SUPPORT
+CFLAGS += -DLITMUS_NUMA_SUPPORT
+CPPFLAGS += -DLITMUS_NUMA_SUPPORT
+CUFLAGS += -DLITMUS_NUMA_SUPPORT
+endif
+
 # how to link against liblitmus
 liblitmus-flags = -L${LIBLITMUS} -llitmus
+ifdef NUMA_SUPPORT
+liblitmus-flags += -lnuma
+endif
+
+# how to link cuda
+cuda-flags-i386 = -L/usr/local/cuda/lib
+cuda-flags-x86_64 = -L/usr/local/cuda/lib64
+cuda-flags = ${cuda-flags-${ARCH}} -lcudart -lcuda
 
 # Force gcc instead of cc, but let the user specify a more specific version if
 # desired.
@@ -61,17 +92,28 @@ ifeq (${CC},cc)
 CC = gcc
 endif
 
+#ifeq (${CPP},cpp)
+CPP = g++
+#endif
+
+CU = nvcc
+
 # incorporate cross-compiler (if any)
 CC  := ${CROSS_COMPILE}${CC}
+CPP  := ${CROSS_COMPILE}${CPP}
 LD  := ${CROSS_COMPILE}${LD}
 AR  := ${CROSS_COMPILE}${AR}
+CU  := ${CROSS_COMPILE}${CU}
 
 # ##############################################################################
 # Targets
 
-all     = lib ${rt-apps}
+all     = lib ${rt-apps} ${rt-cppapps} ${rt-cuapps}
 rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \
-	  base_mt_task runtests
+	  base_mt_task uncache runtests \
+	  nested locktest ikglptest dgl aux_threads normal_task
+rt-cppapps = budget
+rt-cuapps = gpuspin
 
 .PHONY: all lib clean dump-config TAGS tags cscope help
 
@@ -86,10 +128,14 @@ inc/config.makefile: Makefile
 	@printf "%-15s= %-20s\n" \
 		ARCH ${ARCH} \
 		CFLAGS '${CFLAGS}' \
+		CPPFLAGS '${CPPFLAGS}' \
+		CUFLAGS '${CUFLAGS}' \
 		LDFLAGS '${LDFLAGS}' \
 		LDLIBS '${liblitmus-flags}' \
 		CPPFLAGS '${CPPFLAGS}' \
 		CC '${shell which ${CC}}' \
+		CPP '${shell which ${CPP}}' \
+		CU '${shell which ${CU}}' \
 		LD '${shell which ${LD}}' \
 		AR '${shell which ${AR}}' \
 	> $@
@@ -103,10 +149,12 @@ dump-config:
 		headers "${headers}" \
 		"kernel headers" "${imported-headers}" \
 		CFLAGS "${CFLAGS}" \
-		LDFLAGS "${LDFLAGS}" \
 		CPPFLAGS "${CPPFLAGS}" \
+		CUFLAGS "${CUFLAGS}" \
+		LDFLAGS "${LDFLAGS}" \
 		CC "${CC}" \
 		CPP "${CPP}" \
+		CU "${CU}" \
 		LD "${LD}" \
 		AR "${AR}" \
 		obj-all "${obj-all}"
@@ -115,7 +163,7 @@ help:
 	@cat INSTALL
 
 clean:
-	rm -f ${rt-apps}
+	rm -f ${rt-apps} ${rt-cppapps} ${rt-cuapps}
 	rm -f *.o *.d *.a test_catalog.inc
 	rm -f ${imported-headers}
 	rm -f inc/config.makefile
@@ -156,6 +204,8 @@ arch/${include-${ARCH}}/include/asm/%.h: \
 litmus-headers = \
 	include/litmus/rt_param.h \
 	include/litmus/fpmath.h \
+	include/litmus/binheap.h \
+	include/litmus/signal.h \
 	include/litmus/unistd_32.h \
 	include/litmus/unistd_64.h
 
@@ -201,7 +251,7 @@ tests/runner.c: test_catalog.inc
 # Tools that link with liblitmus
 
 # these source files are found in bin/
-vpath %.c bin/
+vpath %.c bin/ gpu/
 
 obj-cycles = cycles.o
 
@@ -210,16 +260,49 @@ obj-base_task = base_task.o
 obj-base_mt_task = base_mt_task.o
 ldf-base_mt_task = -pthread
 
+obj-aux_threads = aux_threads.o
+ldf-aux_threads = -pthread
+
 obj-rt_launch = rt_launch.o common.o
 
 obj-rtspin = rtspin.o common.o
 lib-rtspin = -lrt
 
+obj-nested = nested.o common.o
+lib-nested = -lrt -pthread
+
+obj-locktest = locktest.o common.o
+lib-locktest = -lrt -pthread
+
+obj-ikglptest = ikglptest.o common.o
+lib-ikglptest = -lrt -pthread -lm
+
+obj-normal_task = normal_task.o common.o
+lib-normal_task = -lrt -pthread -lm
+
+obj-dgl = dgl.o common.o
+lib-dgl = -lrt -pthread
+
+obj-uncache = uncache.o
+lib-uncache = -lrt
+
 obj-release_ts = release_ts.o
 
 obj-measure_syscall = null_call.o
 lib-measure_syscall = -lm
 
+
+vpath %.cpp gpu/
+
+objcpp-budget = budget.o common.o
+lib-budget = -lrt -lm -pthread
+
+
+vpath %.cu gpu/
+
+objcu-gpuspin = gpuspin.o common.o
+lib-gpuspin = -lblitz -lrt -lm -lpthread -lboost_filesystem -lboost_system
+
 # ##############################################################################
 # Build everything that depends on liblitmus.
 
@@ -227,12 +310,22 @@ lib-measure_syscall = -lm
 ${rt-apps}: $${obj-$$@} liblitmus.a
 	$(CC) -o $@ $(LDFLAGS) ${ldf-$@} $(filter-out liblitmus.a,$+) $(LOADLIBS) $(LDLIBS) ${liblitmus-flags} ${lib-$@}
 
+${rt-cppapps}: $${objcpp-$$@} liblitmus.a
+	$(CPP) -o $@ $(LDFLAGS) ${ldf-$@} $(filter-out liblitmus.a,$+) $(LOADLIBS) $(LDLIBS) ${liblitmus-flags} ${lib-$@}
+
+${rt-cuapps}: $${objcu-$$@} liblitmus.a
+	$(CPP) -o $@ $(LDFLAGS) ${ldf-$@} $(filter-out liblitmus.a,$+) $(LOADLIBS) $(LDLIBS) ${liblitmus-flags} ${cuda-flags} ${lib-$@}
+
 # ##############################################################################
 # Dependency resolution.
 
-vpath %.c bin/ src/ tests/
+vpath %.c bin/ src/ gpu/ tests/
+vpath %.cpp gpu/
+vpath %.cu gpu/
 
 obj-all = ${sort ${foreach target,${all},${obj-${target}}}}
+obj-all += ${sort ${foreach target,${all},${objcpp-${target}}}}
+obj-all += ${sort ${foreach target,${all},${objcu-${target}}}}
 
 # rule to generate dependency files
 %.d: %.c ${imported-headers}
@@ -241,6 +334,22 @@ obj-all = ${sort ${foreach target,${all},${obj-${target}}}}
 		sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
 		rm -f $@.$$$$
 
+%.d: %.cpp ${imported-headers}
+	@set -e; rm -f $@; \
+		$(CPP) -MM $(CPPFLAGS) $< > $@.$$$$; \
+		sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+		rm -f $@.$$$$
+
+%.d: %.cu ${imported-headers}
+	@set -e; rm -f $@; \
+		$(CU) --generate-dependencies $(CUFLAGS) $< > $@.$$$$; \
+		sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+		rm -f $@.$$$$
+
+# teach make how to compile .cu files
+%.o: %.cu
+	$(CU) --compile $(CUFLAGS) $(OUTPUT_OPTION) $<
+
 ifeq ($(MAKECMDGOALS),)
 MAKECMDGOALS += all
 endif
diff --git a/bin/base_mt_task.c b/bin/base_mt_task.c
index 8090cc3..1406b20 100644
--- a/bin/base_mt_task.c
+++ b/bin/base_mt_task.c
@@ -1,4 +1,4 @@
-/* based_mt_task.c -- A basic multi-threaded real-time task skeleton. 
+/* based_mt_task.c -- A basic multi-threaded real-time task skeleton.
  *
  * This (by itself useless) task demos how to setup a multi-threaded LITMUS^RT
  * real-time task. Familiarity with the single threaded example (base_task.c)
@@ -26,12 +26,10 @@
 #define RELATIVE_DEADLINE 100
 #define EXEC_COST         10
 
-#define NS_PER_MS         1e6
-
 /* Let's create 10 threads in the example, 
  * for a total utilization of 1.
  */
-#define NUM_THREADS      10 
+#define NUM_THREADS      10
 
 /* The information passed to each thread. Could be anything. */
 struct thread_context {
@@ -43,7 +41,7 @@ struct thread_context {
  */
 void* rt_thread(void *tcontext);
 
-/* Declare the periodically invoked job. 
+/* Declare the periodically invoked job.
  * Returns 1 -> task should exit.
  *         0 -> task should continue.
  */
@@ -62,7 +60,7 @@ int job(void);
 	} while (0)
 
 
-/* Basic setup is the same as in the single-threaded example. However, 
+/* Basic setup is the same as in the single-threaded example. However,
  * we do some thread initiliazation first before invoking the job.
  */
 int main(int argc, char** argv)
@@ -71,7 +69,7 @@ int main(int argc, char** argv)
 	struct thread_context ctx[NUM_THREADS];
 	pthread_t             task[NUM_THREADS];
 
-	/* The task is in background mode upon startup. */		
+	/* The task is in background mode upon startup. */
 
 
 	/*****
@@ -79,7 +77,7 @@ int main(int argc, char** argv)
 	 */
 
 
-       
+
 	/*****
 	 * 2) Work environment (e.g., global data structures, file data, etc.) would
 	 *    be setup here.
@@ -94,7 +92,7 @@ int main(int argc, char** argv)
 	init_litmus();
 
 
-	/***** 
+	/*****
 	 * 4) Launch threads.
 	 */
 	for (i = 0; i < NUM_THREADS; i++) {
@@ -102,15 +100,15 @@ int main(int argc, char** argv)
 		pthread_create(task + i, NULL, rt_thread, (void *) (ctx + i));
 	}
 
-	
+
 	/*****
 	 * 5) Wait for RT threads to terminate.
 	 */
 	for (i = 0; i < NUM_THREADS; i++)
 		pthread_join(task[i], NULL);
-	
 
-	/***** 
+
+	/*****
 	 * 6) Clean up, maybe print results and stats, and exit.
 	 */
 	return 0;
@@ -129,10 +127,10 @@ void* rt_thread(void *tcontext)
 	struct rt_task param;
 
 	/* Set up task parameters */
-	memset(&param, 0, sizeof(param));
-	param.exec_cost = EXEC_COST * NS_PER_MS;
-	param.period = PERIOD * NS_PER_MS;
-	param.relative_deadline = RELATIVE_DEADLINE * NS_PER_MS;
+	init_rt_task_param(&param);
+	param.exec_cost = ms2ns(EXEC_COST);
+	param.period = ms2ns(PERIOD);
+	param.relative_deadline = ms2ns(RELATIVE_DEADLINE);
 
 	/* What to do in the case of budget overruns? */
 	param.budget_policy = NO_ENFORCEMENT;
@@ -166,7 +164,7 @@ void* rt_thread(void *tcontext)
 	 */
 	CALL( task_mode(LITMUS_RT_TASK) );
 
-	/* The task is now executing as a real-time task if the call didn't fail. 
+	/* The task is now executing as a real-time task if the call didn't fail.
 	 */
 
 
@@ -178,11 +176,11 @@ void* rt_thread(void *tcontext)
 		/* Wait until the next job is released. */
 		sleep_next_period();
 		/* Invoke job. */
-		do_exit = job();		
+		do_exit = job();
 	} while (!do_exit);
 
 
-	
+
 	/*****
 	 * 4) Transition to background mode.
 	 */
@@ -194,7 +192,7 @@ void* rt_thread(void *tcontext)
 
 
 
-int job(void) 
+int job(void)
 {
 	/* Do real-time calculation. */
 
diff --git a/bin/base_task.c b/bin/base_task.c
index df0c5a2..0274c89 100644
--- a/bin/base_task.c
+++ b/bin/base_task.c
@@ -1,6 +1,6 @@
-/* based_task.c -- A basic real-time task skeleton. 
+/* based_task.c -- A basic real-time task skeleton.
  *
- * This (by itself useless) task demos how to setup a 
+ * This (by itself useless) task demos how to setup a
  * single-threaded LITMUS^RT real-time task.
  */
 
@@ -20,7 +20,7 @@
  */
 #include "litmus.h"
 
-/* Next, we define period and execution cost to be constant. 
+/* Next, we define period and execution cost to be constant.
  * These are only constants for convenience in this example, they can be
  * determined at run time, e.g., from command line parameters.
  *
@@ -30,8 +30,6 @@
 #define RELATIVE_DEADLINE 100
 #define EXEC_COST         10
 
-#define NS_PER_MS         1e6
-
 /* Catch errors.
  */
 #define CALL( exp ) do { \
@@ -44,13 +42,13 @@
 	} while (0)
 
 
-/* Declare the periodically invoked job. 
+/* Declare the periodically invoked job.
  * Returns 1 -> task should exit.
  *         0 -> task should continue.
  */
 int job(void);
 
-/* typically, main() does a couple of things: 
+/* typically, main() does a couple of things:
  * 	1) parse command line parameters, etc.
  *	2) Setup work environment.
  *	3) Setup real-time parameters.
@@ -60,7 +58,7 @@ int job(void);
  *	7) Clean up and exit.
  *
  * The following main() function provides the basic skeleton of a single-threaded
- * LITMUS^RT real-time task. In a real program, all the return values should be 
+ * LITMUS^RT real-time task. In a real program, all the return values should be
  * checked for errors.
  */
 int main(int argc, char** argv)
@@ -69,10 +67,10 @@ int main(int argc, char** argv)
 	struct rt_task param;
 
 	/* Setup task parameters */
-	memset(&param, 0, sizeof(param));
-	param.exec_cost = EXEC_COST * NS_PER_MS;
-	param.period = PERIOD * NS_PER_MS;
-	param.relative_deadline = RELATIVE_DEADLINE * NS_PER_MS;
+	init_rt_task_param(&param);
+	param.exec_cost = ms2ns(EXEC_COST);
+	param.period = ms2ns(PERIOD);
+	param.relative_deadline = ms2ns(RELATIVE_DEADLINE);
 
 	/* What to do in the case of budget overruns? */
 	param.budget_policy = NO_ENFORCEMENT;
@@ -100,9 +98,9 @@ int main(int argc, char** argv)
 
 
 	/*****
-	 * 3) Setup real-time parameters. 
-	 *    In this example, we create a sporadic task that does not specify a 
-	 *    target partition (and thus is intended to run under global scheduling). 
+	 * 3) Setup real-time parameters.
+	 *    In this example, we create a sporadic task that does not specify a
+	 *    target partition (and thus is intended to run under global scheduling).
 	 *    If this were to execute under a partitioned scheduler, it would be assigned
 	 *    to the first partition (since partitioning is performed offline).
 	 */
@@ -124,7 +122,7 @@ int main(int argc, char** argv)
 	 */
 	CALL( task_mode(LITMUS_RT_TASK) );
 
-	/* The task is now executing as a real-time task if the call didn't fail. 
+	/* The task is now executing as a real-time task if the call didn't fail.
 	 */
 
 
@@ -136,11 +134,11 @@ int main(int argc, char** argv)
 		/* Wait until the next job is released. */
 		sleep_next_period();
 		/* Invoke job. */
-		do_exit = job();		
+		do_exit = job();
 	} while (!do_exit);
 
 
-	
+
 	/*****
 	 * 6) Transition to background mode.
 	 */
@@ -148,14 +146,14 @@ int main(int argc, char** argv)
 
 
 
-	/***** 
+	/*****
 	 * 7) Clean up, maybe print results and stats, and exit.
 	 */
 	return 0;
 }
 
 
-int job(void) 
+int job(void)
 {
 	/* Do real-time calculation. */
 
diff --git a/bin/null_call.c b/bin/null_call.c
index d714e77..bab8e73 100644
--- a/bin/null_call.c
+++ b/bin/null_call.c
@@ -16,7 +16,7 @@ static void time_null_call(void)
 	t2 = get_cycles();
 	if (ret != 0)
 		perror("null_call");
-	printf("%10" CYCLES_FMT ", " 
+	printf("%10" CYCLES_FMT ", "
 	       "%10" CYCLES_FMT ", "
 	       "%10" CYCLES_FMT ", "
 	       "%10" CYCLES_FMT ", "
@@ -38,7 +38,7 @@ int main(int argc, char **argv)
 {
 	double delay;
 	struct timespec sleep_time;
-	
+
 	if (argc == 2) {
 		delay = atof(argv[1]);
 		sleep_time = sec2timespec(delay);
diff --git a/bin/release_ts.c b/bin/release_ts.c
index 7752097..6a74710 100644
--- a/bin/release_ts.c
+++ b/bin/release_ts.c
@@ -10,7 +10,6 @@
 #include "internal.h"
 
 #define OPTSTR "d:wf:"
-#define NS_PER_MS 1000000
 
 #define LITMUS_STATS_FILE "/proc/litmus/stats"
 
@@ -31,54 +30,34 @@ void usage(char *error) {
 void wait_until_ready(int expected)
 {
 	int ready = 0, all = 0;
-	char buf[100];
 	int loops = 0;
-	ssize_t len;
-	
 
 	do {
 		if (loops++ > 0)
 			sleep(1);
-		len = read_file(LITMUS_STATS_FILE, buf, sizeof(buf) - 1);
-		if (len < 0) {
-			fprintf(stderr,
-				"(EE) Error while reading '%s': %m.\n"
-				"(EE) Ignoring -w option.\n",
-				LITMUS_STATS_FILE);
-			break;
-		} else {
-			len = sscanf(buf,
-				     "real-time tasks   = %d\n"
-				     "ready for release = %d\n",
-				     &all, &ready);
-			if (len != 2) {
-				fprintf(stderr, 
-					"(EE) Could not parse '%s'.\n"
-					"(EE) Ignoring -w option.\n",
-					LITMUS_STATS_FILE);
-				break;
-			}
-		}
-	} while (expected > ready || ready < all);
+		if (!read_litmus_stats(&ready, &all))
+			perror("read_litmus_stats");
+	} while (expected > ready || (!expected && ready < all));
 }
 
 int main(int argc, char** argv)
 {
 	int released;
-	lt_t delay = ms2lt(1000);
+	lt_t delay = ms2ns(1000);
 	int wait = 0;
 	int expected = 0;
 	int opt;
-      
+
 	while ((opt = getopt(argc, argv, OPTSTR)) != -1) {
 		switch (opt) {
 		case 'd':
-			delay = ms2lt(atoi(optarg));
+			delay = ms2ns(atoi(optarg));
 			break;
 		case 'w':
 			wait = 1;
 			break;
 		case 'f':
+			wait = 1;
 			expected = atoi(optarg);
 			break;
 		case ':':
@@ -99,7 +78,7 @@ int main(int argc, char** argv)
 		perror("release task system");
 		exit(1);
 	}
-	
+
 	printf("Released %d real-time tasks.\n", released);
 
 	return 0;
diff --git a/bin/rt_launch.c b/bin/rt_launch.c
index 3863031..805e20b 100644
--- a/bin/rt_launch.c
+++ b/bin/rt_launch.c
@@ -29,10 +29,11 @@ int launch(void *task_info_p) {
 }
 
 void usage(char *error) {
-	fprintf(stderr, "%s\nUsage: rt_launch [-w][-v][-p cpu][-c hrt | srt | be] wcet period program [arg1 arg2 ...]\n"
+	fprintf(stderr, "%s\nUsage: rt_launch [-w][-v][-p partition/cluster [-z cluster size]][-q prio][-c hrt | srt | be] wcet period program [arg1 arg2 ...]\n"
 			"\t-w\tSynchronous release\n"
 			"\t-v\tVerbose\n"
-			"\t-p\tcpu (or initial cpu)\n"
+			"\t-p\tpartition or cluster\n"
+			"\t-z\tsize of cluster (default = 1 for partitioned)\n"
 			"\t-c\tClass\n"
 			"\twcet, period in ms\n"
 			"\tprogram to be launched\n",
@@ -41,20 +42,24 @@ void usage(char *error) {
 }
 
 
-#define OPTSTR "p:c:vw"
+#define OPTSTR "p:z:c:vwq:t"
 
-int main(int argc, char** argv) 
+int main(int argc, char** argv)
 {
 	int ret;
 	lt_t wcet;
 	lt_t period;
 	int migrate = 0;
-	int cpu = 0;
+	int cluster = 0;
+	int cluster_size = 1;
 	int opt;
 	int verbose = 0;
 	int wait = 0;
 	startup_info_t info;
-	task_class_t class = RT_CLASS_HARD;
+	task_class_t cls = RT_CLASS_HARD;
+	unsigned int priority = LITMUS_LOWEST_PRIORITY;
+	budget_policy_t budget_pol = QUANTUM_ENFORCEMENT;
+	struct rt_task param;
 
 	while ((opt = getopt(argc, argv, OPTSTR)) != -1) {
 		switch (opt) {
@@ -65,15 +70,26 @@ int main(int argc, char** argv)
 			verbose = 1;
 			break;
 		case 'p':
-			cpu = atoi(optarg);
+			cluster = atoi(optarg);
 			migrate = 1;
 			break;
+		case 'z':
+			cluster_size = atoi(optarg);
+			break;
+		case 'q':
+			priority = atoi(optarg);
+			if (!litmus_is_valid_fixed_prio(priority))
+				usage("Invalid priority.");
+			break;
 		case 'c':
-			class = str2class(optarg);
-			if (class == -1)
+			cls = str2class(optarg);
+			if (cls == -1)
 				usage("Unknown task class.");
 			break;
-
+		case 't':
+			/* use an hrtimer for budget enforcement */
+			budget_pol = PRECISE_ENFORCEMENT;
+			break;
 		case ':':
 			usage("Argument missing.");
 			break;
@@ -87,9 +103,9 @@ int main(int argc, char** argv)
 	signal(SIGUSR1, SIG_IGN);
 
 	if (argc - optind < 3)
-		usage("Arguments missing.");       
-	wcet   = ms2lt(atoi(argv[optind + 0]));
-	period = ms2lt(atoi(argv[optind + 1]));
+		usage("Arguments missing.");
+	wcet   = ms2ns(atoi(argv[optind + 0]));
+	period = ms2ns(atoi(argv[optind + 1]));
 	if (wcet <= 0)
 	usage("The worst-case execution time must be a "
 	      "positive number.");
@@ -103,17 +119,27 @@ int main(int argc, char** argv)
 	info.argv      = argv + optind + 2;
 	info.wait      = wait;
 	if (migrate) {
-		ret = be_migrate_to(cpu);
+		ret = be_migrate_to_cluster(cluster, cluster_size);
 		if (ret < 0)
-			bail_out("could not migrate to target partition");
+			bail_out("could not migrate to target partition or cluster");
 	}
-	ret = __create_rt_task(launch, &info, cpu, wcet, period, class);
 
-	
+	init_rt_task_param(&param);
+	param.exec_cost = wcet;
+	param.period = period;
+	param.priority = priority;
+	param.cls = cls;
+	param.budget_policy = budget_pol;
+
+	if (migrate)
+		param.cpu = cluster_to_first_cpu(cluster, cluster_size);
+
+	ret = create_rt_task(launch, &info, &param);
+
 	if (ret < 0)
 		bail_out("could not create rt child process");
 	else if (verbose)
 		printf("%d\n", ret);
 
-	return 0;	
+	return 0;
 }
diff --git a/bin/rtspin.c b/bin/rtspin.c
index f0a477d..4a1d994 100644
--- a/bin/rtspin.c
+++ b/bin/rtspin.c
@@ -4,6 +4,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <time.h>
+#include <string.h>
 #include <assert.h>
 
 
@@ -20,9 +21,12 @@ static void usage(char *error) {
 		"	rt_spin [COMMON-OPTS] -f FILE [-o COLUMN] WCET PERIOD\n"
 		"	rt_spin -l\n"
 		"\n"
-		"COMMON-OPTS = [-w] [-p PARTITION] [-c CLASS] [-s SCALE]\n"
+		"COMMON-OPTS = [-w] [-s SCALE]\n"
+		"              [-p PARTITION/CLUSTER [-z CLUSTER SIZE]] [-c CLASS]\n"
+		"              [-X LOCKING-PROTOCOL] [-L CRITICAL SECTION LENGTH] [-Q RESOURCE-ID]"
 		"\n"
-		"WCET and PERIOD are milliseconds, DURATION is seconds.\n");
+		"WCET and PERIOD are milliseconds, DURATION is seconds.\n"
+		"CRITICAL SECTION LENGTH is in milliseconds.\n");
 	exit(EXIT_FAILURE);
 }
 
@@ -67,7 +71,7 @@ static void get_exec_times(const char *file, const int column,
 		bail_out("rewinding file failed");
 
 	/* allocate space for exec times */
-	*exec_times = calloc(*num_jobs, sizeof(*exec_times));
+	*exec_times = (double*)calloc(*num_jobs, sizeof(*exec_times));
 	if (!*exec_times)
 		bail_out("couldn't allocate memory");
 
@@ -77,7 +81,7 @@ static void get_exec_times(const char *file, const int column,
 
 		for (cur_col = 1; cur_col < column; ++cur_col) {
 			/* discard input until we get to the column we want */
-			fscanf(fstream, "%*s,");
+			int unused __attribute__ ((unused)) = fscanf(fstream, "%*s,");
 		}
 
 		/* get the desired exec. time */
@@ -150,19 +154,37 @@ static void debug_delay_loop(void)
 	}
 }
 
-static int job(double exec_time, double program_end)
+static int job(double exec_time, double program_end, int lock_od, double cs_length)
 {
+	double chunk1, chunk2;
+
 	if (wctime() > program_end)
 		return 0;
 	else {
-		loop_for(exec_time, program_end + 1);
+		if (lock_od >= 0) {
+			/* simulate critical section somewhere in the middle */
+			chunk1 = drand48() * (exec_time - cs_length);
+			chunk2 = exec_time - cs_length - chunk1;
+
+			/* non-critical section */
+			loop_for(chunk1, program_end + 1);
+
+			/* critical section */
+			litmus_lock(lock_od);
+			loop_for(cs_length, program_end + 1);
+			litmus_unlock(lock_od);
+
+			/* non-critical section */
+			loop_for(chunk2, program_end + 2);
+		} else {
+			loop_for(exec_time, program_end + 1);
+		}
 		sleep_next_period();
 		return 1;
 	}
 }
 
-#define OPTSTR "p:c:wlveo:f:s:q:"
-
+#define OPTSTR "p:z:c:wlveio:f:s:q:X:L:Q:"
 int main(int argc, char** argv)
 {
 	int ret;
@@ -171,18 +193,28 @@ int main(int argc, char** argv)
 	double wcet_ms, period_ms;
 	unsigned int priority = LITMUS_LOWEST_PRIORITY;
 	int migrate = 0;
-	int cpu = 0;
+	int cluster = 0;
+	int cluster_size = 1;
 	int opt;
 	int wait = 0;
 	int test_loop = 0;
 	int column = 1;
 	const char *file = NULL;
 	int want_enforcement = 0;
-	double duration = 0, start;
+	int want_signals = 0;
+	double duration = 0, start = 0;
 	double *exec_times = NULL;
 	double scale = 1.0;
-	task_class_t class = RT_CLASS_HARD;
-	int cur_job, num_jobs;
+	task_class_t cls = RT_CLASS_HARD;
+	int cur_job = 0, num_jobs = 0;
+	struct rt_task param;
+
+	/* locking */
+	int lock_od = -1;
+	int resource_id = 0;
+	const char *lock_namespace = "./rtspin-locks";
+	int protocol = -1;
+	double cs_length = 1; /* millisecond */
 
 	progname = argv[0];
 
@@ -192,22 +224,28 @@ int main(int argc, char** argv)
 			wait = 1;
 			break;
 		case 'p':
-			cpu = atoi(optarg);
+			cluster = atoi(optarg);
 			migrate = 1;
 			break;
+		case 'z':
+			cluster_size = atoi(optarg);
+			break;
 		case 'q':
 			priority = atoi(optarg);
 			if (!litmus_is_valid_fixed_prio(priority))
 				usage("Invalid priority.");
 			break;
 		case 'c':
-			class = str2class(optarg);
-			if (class == -1)
+			cls = str2class(optarg);
+			if (cls == -1)
 				usage("Unknown task class.");
 			break;
 		case 'e':
 			want_enforcement = 1;
 			break;
+		case 'i':
+			want_signals = 1;
+			break;
 		case 'l':
 			test_loop = 1;
 			break;
@@ -220,6 +258,21 @@ int main(int argc, char** argv)
 		case 's':
 			scale = atof(optarg);
 			break;
+		case 'X':
+			protocol = lock_protocol_for_name(optarg);
+			if (protocol < 0)
+				usage("Unknown locking protocol specified.");
+			break;
+		case 'L':
+			cs_length = atof(optarg);
+			if (cs_length <= 0)
+				usage("Invalid critical section length.");
+			break;
+		case 'Q':
+			resource_id = atoi(optarg);
+			if (resource_id <= 0 && strcmp(optarg, "0"))
+				usage("Invalid resource ID.");
+			break;
 		case ':':
 			usage("Argument missing.");
 			break;
@@ -235,6 +288,8 @@ int main(int argc, char** argv)
 		return 0;
 	}
 
+	srand(getpid());
+
 	if (file) {
 		get_exec_times(file, column, &num_jobs, &exec_times);
 
@@ -257,8 +312,8 @@ int main(int argc, char** argv)
 	wcet_ms   = atof(argv[optind + 0]);
 	period_ms = atof(argv[optind + 1]);
 
-	wcet   = wcet_ms * __NS_PER_MS;
-	period = period_ms * __NS_PER_MS;
+	wcet   = ms2ns(wcet_ms);
+	period = ms2ns(period_ms);
 	if (wcet <= 0)
 		usage("The worst-case execution time must be a "
 				"positive number.");
@@ -275,24 +330,47 @@ int main(int argc, char** argv)
 		duration += period_ms * 0.001 * (num_jobs - 1);
 
 	if (migrate) {
-		ret = be_migrate_to(cpu);
+		ret = be_migrate_to_cluster(cluster, cluster_size);
 		if (ret < 0)
-			bail_out("could not migrate to target partition");
+			bail_out("could not migrate to target partition or cluster.");
 	}
 
-	ret = sporadic_task_ns(wcet, period, 0, cpu, priority, class,
-			       want_enforcement ? PRECISE_ENFORCEMENT
-			                        : NO_ENFORCEMENT,
-			       migrate);
+	init_rt_task_param(&param);
+	param.exec_cost = wcet;
+	param.period = period;
+	param.priority = priority;
+	param.cls = cls;
+	param.budget_policy = (want_enforcement) ?
+			PRECISE_ENFORCEMENT : NO_ENFORCEMENT;
+	param.budget_signal_policy = (want_enforcement && want_signals) ?
+			PRECISE_SIGNALS : NO_SIGNALS;
+				
+	if (migrate)
+		param.cpu = cluster_to_first_cpu(cluster, cluster_size);
+	ret = set_rt_task_param(gettid(), &param);
 	if (ret < 0)
 		bail_out("could not setup rt task params");
 
 	init_litmus();
 
+	if (want_signals) {
+		/* bind default longjmp signal handler to SIG_BUDGET. */
+		activate_litmus_signals(SIG_BUDGET_MASK, longjmp_on_litmus_signal);
+	}
+
 	ret = task_mode(LITMUS_RT_TASK);
 	if (ret != 0)
 		bail_out("could not become RT task");
 
+	if (protocol >= 0) {
+		/* open reference to semaphore */
+		lock_od = litmus_open_lock(protocol, resource_id, lock_namespace, &cluster);
+		if (lock_od < 0) {
+			perror("litmus_open_lock");
+			usage("Could not open lock.");
+		}
+	}
+
 	if (wait) {
 		ret = wait_for_ts_release();
 		if (ret != 0)
@@ -306,11 +384,13 @@ int main(int argc, char** argv)
 		for (cur_job = 0; cur_job < num_jobs; ++cur_job) {
 			/* convert job's length to seconds */
 			job(exec_times[cur_job] * 0.001 * scale,
-					start + duration);
+			    start + duration,
+			    lock_od, cs_length * 0.001);
 		}
 	} else {
-		/* conver to seconds and scale */
-		while (job(wcet_ms * 0.001 * scale, start + duration));
+		/* convert to seconds and scale */
+		while (job(wcet_ms * 0.001 * scale, start + duration,
+			   lock_od, cs_length * 0.001));
 	}
 
 	ret = task_mode(BACKGROUND_TASK);
diff --git a/bin/uncache.c b/bin/uncache.c
new file mode 100644
index 0000000..b6f6913
--- /dev/null
+++ b/bin/uncache.c
@@ -0,0 +1,381 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sched.h>
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+
+/* Test tool for validating Litmus's uncache device.     */
+/* Tool also capable basic cache vs. sysmem statistics.  */
+/* Compile with '-O2' for significaintly greater margins */
+/* in performance between cache and sysmem:              */
+/* (Intel Xeon X5650)                                    */
+/*    -g -> uncache is 30x slower                        */
+/*    -O2 -> uncache is >100x slower                     */
+
+int PAGE_SIZE;
+#define NR_PAGES 16
+
+#define UNCACHE_DEV "/dev/litmus/uncache"
+
+/* volatile forces a read from memory (or cache) on every reference. Note
+   that volatile does not keep data out of the cache! */
+typedef volatile char* pbuf_t;
+
+/* hit the first byte in each page.
+   addr must be page aligned. */
+inline int linear_write(pbuf_t addr, int size, char val)
+{
+	pbuf_t end = addr + size;
+	pbuf_t step;
+	int nr_pages = (unsigned long)(end - addr)/PAGE_SIZE;
+	int times = nr_pages * PAGE_SIZE;
+	int i;
+
+	for (i = 0; i < times; ++i)
+		for(step = addr; step < end; step += PAGE_SIZE)
+			*step = val;
+	return 0;
+}
+inline int linear_read(pbuf_t addr, int size, char val)
+{
+	pbuf_t end = addr + size;
+	pbuf_t step;
+	int nr_pages = (unsigned long)(end - addr)/PAGE_SIZE;
+	int times = nr_pages * PAGE_SIZE;
+	int i;
+
+	for (i = 0; i < times; ++i)
+		for(step = addr; step < end; step += PAGE_SIZE) {
+			if (*step != val)
+				return -1;
+		}
+	return 0;
+}
+
+/* write to *data nr times. */
+inline int hammer_write(pbuf_t data, char val, int nr)
+{
+	int i;
+	for (i = 0; i < nr; ++i)
+		*data = val;
+	return 0;
+}
+
+/* read from *data nr times. */
+inline int hammer_read(pbuf_t data, char val, int nr)
+{
+	int i;
+	for (i = 0; i < nr; ++i) {
+		if (*data != val)
+			return -1;
+	}
+	return 0;
+}
+
+inline int test(pbuf_t data, int size, int trials)
+{
+	int HAMMER_TIME = 10000;  /* can't cache this! */
+	char VAL = 0x55;
+	int t;
+	for(t = 0; t < trials; ++t) {
+
+#if 0
+		if (linear_write(data, size, VAL) != 0) {
+			printf("failed linear_write()\n");
+			return -1;
+		}
+		if (linear_read(data, size, VAL) != 0) {
+			printf("failed linear_read()\n");
+			return -1;
+		}
+#endif
+
+		/* hammer at the first byte in the array */
+		if (hammer_write(data, VAL, HAMMER_TIME) != 0) {
+			printf("failed hammer_write()\n");
+			return -1;
+		}
+		if (hammer_read(data, VAL, HAMMER_TIME) != 0) {
+			printf("failed hammer_read()\n");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+inline void timespec_normalize(struct timespec* ts, time_t sec, int64_t nsec)
+{
+	while(nsec > 1000000000LL) {
+		asm("" : "+rm"(nsec));
+		nsec -= 1000000000LL;
+		++sec;
+	}
+	while(nsec < 0) {
+		asm("" : "+rm"(nsec));
+		nsec += 1000000000LL;
+		--sec;
+	}
+
+	ts->tv_sec = sec;
+	ts->tv_nsec = nsec;
+}
+
+inline struct timespec timespec_sub(struct timespec lhs, struct timespec rhs)
+{
+	struct timespec delta;
+	timespec_normalize(&delta, lhs.tv_sec - rhs.tv_sec, lhs.tv_nsec - rhs.tv_nsec);
+	return delta;
+}
+
+inline struct timespec timespec_add(struct timespec lhs, struct timespec rhs)
+{
+	struct timespec delta;
+	timespec_normalize(&delta, lhs.tv_sec + rhs.tv_sec, lhs.tv_nsec + rhs.tv_nsec);
+	return delta;
+}
+
+inline int64_t timespec_to_us(struct timespec ts)
+{
+	int64_t t;
+	t = ts.tv_sec * 1000000LL;
+	t += ts.tv_nsec / 1000LL;
+	return t;
+}
+
+/* hammers away at the first byte in each mmaped page and
+   times how long it took. */
+int do_data(int do_uncache, int64_t* time)
+{
+	int size;
+	int prot = PROT_READ | PROT_WRITE;
+	int flags = MAP_PRIVATE;
+
+	pbuf_t data;
+
+	struct sched_param fifo_params;
+
+	struct timespec start, end;
+	int64_t elapsed;
+	int trials = 1000;
+
+	printf("Running data access test.\n");
+
+	mlockall(MCL_CURRENT | MCL_FUTURE);
+
+	memset(&fifo_params, 0, sizeof(fifo_params));
+	fifo_params.sched_priority = sched_get_priority_max(SCHED_FIFO);
+
+	size = PAGE_SIZE*NR_PAGES;
+
+	printf("Allocating %d %s pages.\n", NR_PAGES, (do_uncache) ?
+					"uncacheable" : "cacheable");
+	if (do_uncache) {
+		int fd = open(UNCACHE_DEV, O_RDWR);
+		data = mmap(NULL, size, prot, flags, fd, 0);
+		close(fd);
+	}
+	else {
+		/* Accessed data will probably fit in L1, so this will go VERY fast.
+		   Code should also have little-to-no pipeline stalls. */
+		flags |= MAP_ANONYMOUS;
+		data = mmap(NULL, size, prot, flags, -1, 0);
+	}
+	if (data == MAP_FAILED) {
+		printf("Failed to alloc data! "
+			   "Are you running Litmus? "
+			   "Is Litmus broken?\n");
+		return -1;
+	}
+	else {
+		printf("Data allocated at %p.\n", data);
+	}
+
+	printf("Beginning tests...\n");
+	if (sched_setscheduler(getpid(), SCHED_FIFO, &fifo_params)) {
+		printf("(Could not become SCHED_FIFO task.) Are you running as root?\n");
+	}
+
+	/* observations suggest that no warmup phase is needed. */
+	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start);
+	if (test(data, size, trials) != 0) {
+		printf("Test failed!\n");
+		munmap((char*)data, size);
+		return -1;
+	}
+	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
+	elapsed = timespec_to_us(timespec_sub(end, start));
+	printf("%s Time: %ldus\n", (do_uncache) ?
+					"Uncache" : "Cache", elapsed);
+
+	munmap((char*)data, size);
+
+	if(time)
+		*time = elapsed;
+
+	return 0;
+}
+
+/* compares runtime of cached vs. uncached */
+int do_data_compare()
+{
+	const double thresh = 1.3;
+	int ret = 0;
+	double ratio;
+	int64_t cache_time = 0, uncache_time = 0;
+
+	printf("Timing cached pages...\n");
+	ret = do_data(0, &cache_time);
+	if (ret != 0)
+		goto out;
+
+	printf("Timing uncached pages...\n");
+	ret = do_data(1, &uncache_time);
+	if (ret != 0)
+		goto out;
+
+	ratio = (double)uncache_time/(double)cache_time;
+	printf("Uncached/Cached Ratio: %f\n", ratio);
+
+	if (ratio < thresh) {
+		printf("Ratio is unexpectedly small (< %f)! "
+				" Uncache broken? Are you on kvm?\n", thresh);
+		ret = -1;
+	}
+
+out:
+	return ret;
+}
+
+/* tries to max out uncache allocations.
+   under normal conditions (non-mlock),
+   pages should spill into swap. uncache
+   pages are not locked in memory. */
+int do_max_alloc(void)
+{
+	int fd;
+	int good = 1;
+	int count = 0;
+	uint64_t mmap_size = PAGE_SIZE; /* start at one page per mmap */
+
+	/* half of default limit on ubuntu. (see /proc/sys/vm/max_map_count) */
+	int max_mmaps = 32765;
+	volatile char** maps = calloc(max_mmaps, sizeof(pbuf_t));
+
+	if (!maps) {
+		printf("failed to alloc pointers for pages\n");
+		return -1;
+	}
+
+	printf("Testing max amount of uncache data. System may get wonkie (OOM Killer)!\n");
+
+	fd = open(UNCACHE_DEV, O_RDWR);
+	do {
+		int i;
+		int nr_pages = mmap_size/PAGE_SIZE;
+		printf("Testing mmaps of %d pages.\n", nr_pages);
+
+		count = 0;
+		for (i = 0; (i < max_mmaps) && good; ++i) {
+			pbuf_t data = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_POPULATE, fd, 0);
+
+			if (data != MAP_FAILED) {
+				maps[i] = data;
+				++count;
+			}
+			else {
+				perror(NULL);
+				good = 0;
+			}
+		}
+		for (i = 0; i < count; ++i) {
+			if (maps[i])
+				munmap((char*)(maps[i]), mmap_size);
+		}
+		memset(maps, 0, sizeof(maps[0])*max_mmaps);
+
+		mmap_size *= 2; /* let's do it again with bigger allocations */
+	}while(good);
+
+	free(maps);
+	close(fd);
+
+	printf("Maxed out allocs with %d mmaps of %lu pages in size.\n",
+		count, mmap_size/PAGE_SIZE);
+
+	return 0;
+}
+
+typedef enum
+{
+	UNCACHE,
+	CACHE,
+	COMPARE,
+	MAX_ALLOC
+} test_t;
+
+#define OPTSTR "ucxa"
+int main(int argc, char** argv)
+{
+	int ret;
+	test_t test = UNCACHE;
+	int opt;
+	PAGE_SIZE = sysconf(_SC_PAGE_SIZE);
+
+	while((opt = getopt(argc, argv, OPTSTR)) != -1) {
+		switch(opt) {
+			case 'c':
+				test = CACHE;
+				break;
+			case 'u':
+				test = UNCACHE;
+				break;
+			case 'x':
+				test = COMPARE;
+				break;
+			case 'a':
+				test = MAX_ALLOC;
+				break;
+			case ':':
+				printf("missing option\n");
+				exit(-1);
+			case '?':
+			default:
+				printf("bad argument\n");
+				exit(-1);
+		}
+	}
+
+
+	printf("Page Size: %d\n", PAGE_SIZE);
+
+	switch(test)
+	{
+	case CACHE:
+		ret = do_data(0, NULL);
+		break;
+	case UNCACHE:
+		ret = do_data(1, NULL);
+		break;
+	case COMPARE:
+		ret = do_data_compare();
+		break;
+	case MAX_ALLOC:
+		ret = do_max_alloc();
+		break;
+	default:
+		printf("invalid test\n");
+		ret = -1;
+		break;
+	}
+
+	if (ret != 0) {
+		printf("Test failed.\n");
+	}
+
+	return ret;
+}
diff --git a/gpu/aux_threads.c b/gpu/aux_threads.c
new file mode 100644
index 0000000..1711c40
--- /dev/null
+++ b/gpu/aux_threads.c
@@ -0,0 +1,313 @@
+/* based_mt_task.c -- A basic multi-threaded real-time task skeleton.
+ *
+ * This (by itself useless) task demos how to setup a multi-threaded LITMUS^RT
+ * real-time task. Familiarity with the single threaded example (base_task.c)
+ * is assumed.
+ *
+ * Currently, liblitmus still lacks automated support for real-time
+ * tasks, but internaly it is thread-safe, and thus can be used together
+ * with pthreads.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+/* Include gettid() */
+#include <sys/types.h>
+
+/* Include threading support. */
+#include <pthread.h>
+
+/* Include the LITMUS^RT API.*/
+#include "litmus.h"
+
+//#define PERIOD		500
+#define PERIOD		 10
+//#define EXEC_COST	 10
+#define EXEC_COST 1
+
+int NUM_AUX_THREADS = 2;
+
+#define LITMUS_STATS_FILE "/proc/litmus/stats"
+
+/* The information passed to each thread. Could be anything. */
+struct thread_context {
+	int id;
+	struct timeval total_time;
+};
+
+/* The real-time thread program. Doesn't have to be the same for
+ * all threads. Here, we only have one that will invoke job().
+ */
+void* rt_thread(void *tcontext);
+void* aux_thread(void *tcontext);
+
+/* Declare the periodically invoked job.
+ * Returns 1 -> task should exit.
+ *         0 -> task should continue.
+ */
+int job(void);
+
+
+/* Catch errors.
+ */
+#define CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "%s failed: %m\n", #exp);\
+		else \
+			fprintf(stderr, "%s ok.\n", #exp); \
+	} while (0)
+
+int gRun = 1;
+
+pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_barrier_t gBar;
+
+#define OPTSTR "t:fcb"
+
+int main(int argc, char** argv)
+{
+	int i;
+	struct thread_context *ctx;
+	pthread_t *task;
+
+	int opt;
+	int before = 0;
+	int aux_flags = 0;
+	int do_future = 0;
+
+	while ((opt = getopt(argc, argv, OPTSTR)) != -1) {
+		switch(opt)
+		{
+		case 't':
+			NUM_AUX_THREADS = atoi(optarg);
+			printf("%d aux threads\n", NUM_AUX_THREADS);
+			break;
+		case 'f':
+			aux_flags |= AUX_FUTURE;
+			do_future = 1;
+			break;
+		case 'c':
+			aux_flags |= AUX_CURRENT;
+			break;
+		case 'b':
+			before = 1;
+			printf("Will become real-time before spawning aux threads.\n");
+			break;
+		}
+	}
+
+	if (aux_flags == 0) {
+		printf("Must specify -c (AUX_CURRENT) and/or -f (AUX_FUTURE) for aux tasks.\n");
+		return -1;
+	}
+
+	ctx = calloc(NUM_AUX_THREADS, sizeof(struct thread_context));
+	task = calloc(NUM_AUX_THREADS, sizeof(pthread_t));
+
+	//lt_t delay = ms2lt(1000);
+
+	/*****
+	 * 3) Initialize LITMUS^RT.
+	 *    Task parameters will be specified per thread.
+	 */
+	init_litmus();
+
+	{
+		pthread_barrierattr_t battr;
+		pthread_barrierattr_init(&battr);
+		pthread_barrier_init(&gBar, &battr, (NUM_AUX_THREADS)+1);
+	}
+
+	if(before)
+	{
+		CALL( init_rt_thread() );
+		CALL( sporadic_partitioned(EXEC_COST, PERIOD, 0) );
+		CALL( task_mode(LITMUS_RT_TASK) );
+	}
+
+
+	if(do_future && before)
+	{
+		CALL( enable_aux_rt_tasks(aux_flags) );
+	}
+
+//	printf("Red Leader is now real-time!\n");
+
+	for (i = 0; i < NUM_AUX_THREADS; i++) {
+		ctx[i].id = i;
+		pthread_create(task + i, NULL, aux_thread, (void *) (ctx + i));
+	}
+
+//	pthread_barrier_wait(&gBar);
+
+//	sleep(1);
+
+	if(!before)
+	{
+		CALL( init_rt_thread() );
+		CALL( sporadic_global(EXEC_COST, PERIOD) );
+		CALL( task_mode(LITMUS_RT_TASK) );
+	}
+
+	// secondary call *should* be harmless
+	CALL( enable_aux_rt_tasks(aux_flags) );
+
+	{
+	int last = time(0);
+//	struct timespec sleeptime = {0, 1000}; // 1 microsecond
+//	for(i = 0; i < 24000; ++i) {
+	for(i = 0; i < 2000; ++i) {
+		sleep_next_period();
+//		printf("RED LEADER!\n");
+
+//		nanosleep(&sleeptime, NULL);
+
+		pthread_mutex_lock(&gMutex);
+
+		if((i%(10000/PERIOD)) == 0) {
+			int now = time(0);
+			printf("hearbeat %d: %d\n", i, now - last);
+			last = now;
+		}
+
+		pthread_mutex_unlock(&gMutex);
+	}
+	}
+
+	CALL( disable_aux_rt_tasks(aux_flags) );
+	gRun = 0;
+
+	CALL( task_mode(BACKGROUND_TASK) );
+
+	/*****
+	 * 5) Wait for RT threads to terminate.
+	 */
+	for (i = 0; i < NUM_AUX_THREADS; i++) {
+		if (task[i] != 0) {
+			float time;
+			pthread_join(task[i], NULL);
+			time = ctx[i].total_time.tv_sec + ctx[i].total_time.tv_usec / (float)(1e6);
+			printf("child %d: %fs\n", i, time);
+		}
+	}
+
+
+	/*****
+	 * 6) Clean up, maybe print results and stats, and exit.
+	 */
+	return 0;
+}
+
+
+
+/* A real-time thread is very similar to the main function of a single-threaded
+ * real-time app. Notice, that init_rt_thread() is called to initialized per-thread
+ * data structures of the LITMUS^RT user space libary.
+ */
+void* aux_thread(void *tcontext)
+{
+	struct thread_context *ctx = (struct thread_context *) tcontext;
+	int count = 0;
+
+//	pthread_barrier_wait(&gBar);
+
+	while(gRun)
+	{
+		if(count++ % 100000 == 0) {
+			pthread_mutex_lock(&gMutex);
+			pthread_mutex_unlock(&gMutex);
+		}
+	}
+
+	{
+	struct rusage use;
+	long int sec;
+
+	getrusage(RUSAGE_THREAD, &use);
+
+	ctx->total_time.tv_usec = use.ru_utime.tv_usec + use.ru_stime.tv_usec;
+	sec = ctx->total_time.tv_usec / (long int)(1e6);
+	ctx->total_time.tv_usec = ctx->total_time.tv_usec % (long int)(1e6);
+	ctx->total_time.tv_sec = use.ru_utime.tv_sec + use.ru_stime.tv_sec + sec;
+	}
+
+	return ctx;
+}
+
+
+/* A real-time thread is very similar to the main function of a single-threaded
+ * real-time app. Notice, that init_rt_thread() is called to initialized per-thread
+ * data structures of the LITMUS^RT user space libary.
+ */
+void* rt_thread(void *tcontext)
+{
+	struct thread_context *ctx = (struct thread_context *) tcontext;
+
+	/* Make presence visible. */
+	printf("RT Thread %d active.\n", ctx->id);
+
+	/*****
+	 * 1) Initialize real-time settings.
+	 */
+	CALL( init_rt_thread() );
+	CALL( sporadic_global(EXEC_COST, PERIOD + ctx->id * 10) );
+
+
+	/*****
+	 * 2) Transition to real-time mode.
+	 */
+	CALL( task_mode(LITMUS_RT_TASK) );
+
+
+
+	wait_for_ts_release();
+
+	/* The task is now executing as a real-time task if the call didn't fail.
+	 */
+
+
+
+	/*****
+	 * 3) Invoke real-time jobs.
+	 */
+	while(gRun) {
+		/* Wait until the next job is released. */
+		sleep_next_period();
+		printf("%d: task.\n", ctx->id);
+	}
+
+	/*****
+	 * 4) Transition to background mode.
+	 */
+	CALL( task_mode(BACKGROUND_TASK) );
+
+	{
+	struct rusage use;
+	long int sec;
+
+	getrusage(RUSAGE_THREAD, &use);
+	ctx->total_time.tv_usec = use.ru_utime.tv_usec + use.ru_stime.tv_usec;
+	sec = ctx->total_time.tv_usec / (long int)(1e6);
+	ctx->total_time.tv_usec = ctx->total_time.tv_usec % (long int)(1e6);
+	ctx->total_time.tv_sec = use.ru_utime.tv_sec + use.ru_stime.tv_sec + sec;
+	}
+
+	return ctx;
+}
+
+int job(void)
+{
+	/* Do real-time calculation. */
+
+	/* Don't exit. */
+	return 0;
+}
diff --git a/gpu/budget.cpp b/gpu/budget.cpp
new file mode 100644
index 0000000..e08daf7
--- /dev/null
+++ b/gpu/budget.cpp
@@ -0,0 +1,379 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* Include gettid() */
+#include <sys/types.h>
+
+/* Include threading support. */
+#include <pthread.h>
+
+/* Include the LITMUS^RT API.*/
+#include "litmus.h"
+
+#define NUMS 4096
+static int nums[NUMS];
+
+inline static lt_t cputime_ns(void)
+{
+	struct timespec ts;
+	lt_t time;
+	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+
+	// safe, as long as sizeof(ls_t) >= 8
+	time = s2ns(ts.tv_sec) + ts.tv_nsec;
+
+	return time;
+}
+
+inline static lt_t wtime_ns(void)
+{
+	struct timespec ts;
+	lt_t time;
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+
+	// safe, as long as sizeof(ls_t) >= 8
+	time = s2ns(ts.tv_sec) + ts.tv_nsec;
+
+	return time;
+}
+
+static int loop_once(void)
+{
+	int i, j = 0;
+	for (i = 0; i < NUMS; ++i)
+		j += nums[i]++;
+	return j;
+}
+
+int loop_for(lt_t time)
+{
+	lt_t end, now;
+	lt_t last_loop = 0, loop_start;
+	int dummy = 0;
+
+	last_loop = 0;
+
+	now = cputime_ns();
+	end = now + time;
+
+	/* '+ last_loop' attempts to avoid overrun */
+	while (now + last_loop < end) {
+		loop_start = now;
+		dummy += loop_once();
+		now = cputime_ns();
+		last_loop = now - loop_start;
+	}
+
+	return dummy;
+}
+
+int OVERRUN = 0;
+int SIGNALS = 0;
+int BLOCK_SIGNALS_ON_SLEEP = 0;
+int OVERRUN_RATE = 1; /* default: every job overruns */
+
+int CXS_OVERRUN = 0;
+int NUM_LOCKS = 1;
+int NUM_REPLICAS = 1;
+int NAMESPACE = 0;
+int *LOCKS = NULL;
+int IKGLP_LOCK = 0;
+int USE_DGLS = 0;
+int NEST_IN_IKGLP = 0;
+
+int WAIT = 0;
+
+enum eLockType
+{
+	FIFO,
+	PRIOQ,
+	IKGLP
+};
+
+eLockType LOCK_TYPE = FIFO;
+
+int OVERRUN_BY_SLEEP = 0;
+
+int NUM_JOBS = 0;
+int NUM_COMPLETED_JOBS = 0;
+int NUM_OVERRUNS = 0;
+
+lt_t overrun_extra = 0;
+
+int job(lt_t exec_ns, lt_t budget_ns)
+{
+	++NUM_JOBS;
+
+	try{
+		lt_t approx_remaining = budget_ns;
+		lt_t now = cputime_ns();
+		loop_for(lt_t(exec_ns * 0.9)); /* fudge it a bit to account for overheads */
+
+		if (OVERRUN) {
+			// do we want to overrun this job?
+			if ((NUM_JOBS % OVERRUN_RATE) == 0) {
+				approx_remaining -= (cputime_ns() - now);
+
+				if (SIGNALS && BLOCK_SIGNALS_ON_SLEEP)
+					block_litmus_signals(SIG_BUDGET);
+
+				if(CXS_OVERRUN) {
+					if (NEST_IN_IKGLP)
+						litmus_lock(IKGLP_LOCK);
+					if (USE_DGLS)
+						litmus_dgl_lock(LOCKS, NUM_LOCKS);
+					else
+						for(int i = 0; i < NUM_LOCKS; ++i)
+							litmus_lock(LOCKS[i]);
+				}
+
+				// intentionally overrun via suspension
+				if (OVERRUN_BY_SLEEP)
+					lt_sleep(approx_remaining + overrun_extra);
+				else
+					loop_for((approx_remaining + overrun_extra) * 0.9);
+
+				if(CXS_OVERRUN) {
+					if (USE_DGLS)
+						litmus_dgl_unlock(LOCKS, NUM_LOCKS);
+					else
+						for(int i = NUM_LOCKS-1; i >= 0; --i)
+							litmus_unlock(LOCKS[i]);
+					if (NEST_IN_IKGLP)
+						litmus_unlock(IKGLP_LOCK);
+				}
+
+				if (SIGNALS && BLOCK_SIGNALS_ON_SLEEP)
+					unblock_litmus_signals(SIG_BUDGET);
+			}
+		}
+		++NUM_COMPLETED_JOBS;
+	}
+	catch (const litmus::sigbudget& e) {
+		++NUM_OVERRUNS;
+	}
+
+	sleep_next_period();
+	return 1;
+}
+
+#define OPTSTR "SbosOvzalwqixdn:r:p:"
+
+int main(int argc, char** argv)
+{
+	int ret;
+
+	srand(getpid());
+
+	lt_t e_ns = ms2ns(2);
+	lt_t p_ns = ms2ns(50) + rand()%200;
+	lt_t budget_ns = p_ns/2;
+	lt_t duration = s2ns(60);
+	lt_t terminate_time;
+	unsigned int first_job, last_job;
+	int opt;
+	struct rt_task param;
+	budget_drain_policy_t drain_policy = DRAIN_SIMPLE;
+	int compute_overrun_rate = 0;
+	int once = 1;
+
+	bool migrate = false;
+	int partition = 0;
+	int partition_sz = 1;
+
+	while ((opt = getopt(argc, argv, OPTSTR)) != -1) {
+		switch(opt) {
+		case 'p':
+			migrate = true;
+			partition = atoi(optarg);
+			break;
+		case 'S':
+			SIGNALS = 1;
+			break;
+		case 'b':
+			BLOCK_SIGNALS_ON_SLEEP = 1;
+			break;
+		case 's':
+			OVERRUN_BY_SLEEP = 1;
+			break;
+		case 'o':
+			OVERRUN = 1;
+			overrun_extra = budget_ns/2;
+			break;
+		case 'O':
+			OVERRUN = 1;
+			overrun_extra = 4*p_ns;
+			break;
+		case 'a':
+			/* select an overrun rate such that a task should be caught
+			 * up from a backlog caused by an overrun before the next
+			 * overrun occurs.
+			 */
+			compute_overrun_rate = 1;
+			break;
+		case 'v':
+			drain_policy = DRAIN_SOBLIV;
+			break;
+		case 'z':
+			drain_policy = DRAIN_SIMPLE_IO;
+			break;
+		case 'l':
+			CXS_OVERRUN = 1;
+			NAMESPACE = open("semaphores", O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
+			break;
+		case 'q':
+			LOCK_TYPE = PRIOQ;
+			break;
+		case 'i':
+			LOCK_TYPE = IKGLP;
+			break;
+		case 'x':
+			NEST_IN_IKGLP = 1;
+			break;
+		case 'w':
+			WAIT = 1;
+			break;
+		case 'd':
+			USE_DGLS = 1;
+			break;
+		case 'n':
+			NUM_LOCKS = atoi(optarg);
+			break;
+		case 'r':
+			NUM_REPLICAS = atoi(optarg);
+			break;
+		case ':':
+			printf("missing argument\n");
+			assert(false);
+			break;
+		default:
+			printf("unknown option\n");
+			assert(false);
+			break;
+		}
+	}
+
+	assert(!BLOCK_SIGNALS_ON_SLEEP || (BLOCK_SIGNALS_ON_SLEEP && SIGNALS));
+	assert(!CXS_OVERRUN || (CXS_OVERRUN && WAIT));
+	assert(LOCK_TYPE != IKGLP || NUM_LOCKS == 1);
+	assert(LOCK_TYPE != IKGLP || (LOCK_TYPE == IKGLP && !NEST_IN_IKGLP));
+	assert(NUM_LOCKS > 0);
+	if (LOCK_TYPE == IKGLP || NEST_IN_IKGLP)
+		assert(NUM_REPLICAS >= 1);
+
+	LOCKS = new int[NUM_LOCKS];
+
+	if (compute_overrun_rate) {
+		int backlog = (int)ceil((overrun_extra + budget_ns)/(double)budget_ns);
+		if (!CXS_OVERRUN)
+			OVERRUN_RATE = backlog + 2; /* some padding */
+		else
+			OVERRUN_RATE = 2*backlog + 2; /* overrun less frequently for testing */
+	}
+
+	init_rt_task_param(&param);
+	param.exec_cost = budget_ns;
+	param.period = p_ns;
+	param.release_policy = PERIODIC;
+	param.drain_policy = drain_policy;
+	if (!SIGNALS)
+		param.budget_policy = PRECISE_ENFORCEMENT;
+	else
+		param.budget_signal_policy = PRECISE_SIGNALS;
+	if (migrate)
+		param.cpu = cluster_to_first_cpu(partition, partition_sz);
+
+	// set up affinity and init litmus
+	if (migrate) {
+		ret = be_migrate_to_cluster(partition, partition_sz);
+		assert(!ret);
+	}
+	init_litmus();
+
+	ret = set_rt_task_param(gettid(), &param);
+	assert(ret == 0);
+
+	if (CXS_OVERRUN) {
+		int i;
+		for(i = 0; i < NUM_LOCKS; ++i) {
+			int lock = -1;
+			switch(LOCK_TYPE)
+			{
+				case FIFO:
+					lock = open_fifo_sem(NAMESPACE, i);
+					break;
+				case PRIOQ:
+					lock = open_prioq_sem(NAMESPACE, i);
+					break;
+				case IKGLP:
+					lock = open_ikglp_sem(NAMESPACE, i, NUM_REPLICAS);
+					break;
+			}
+			if (lock < 0) {
+				perror("open_sem");
+				exit(-1);
+			}
+			LOCKS[i] = lock;
+		}
+
+		if (NEST_IN_IKGLP) {
+			IKGLP_LOCK = open_ikglp_sem(NAMESPACE, i, NUM_REPLICAS);
+			if (IKGLP_LOCK < 0) {
+				perror("open_sem");
+				exit(-1);
+			}
+		}
+	}
+
+	if (WAIT) {
+		ret = wait_for_ts_release();
+		if (ret < 0)
+			perror("wait_for_ts_release");
+	}
+
+	ret = task_mode(LITMUS_RT_TASK);
+	assert(ret == 0);
+
+	sleep_next_period();
+
+	ret = get_job_no(&first_job);
+	assert(ret == 0);
+
+	terminate_time = duration + wtime_ns();
+
+	while (wtime_ns() < terminate_time) {
+		try{
+			if(once) {
+				activate_litmus_signals(SIG_BUDGET, litmus::throw_on_litmus_signal);
+				once = 0;
+			}
+			job(e_ns, budget_ns);
+		}
+		catch(const litmus::sigbudget &e) {
+			/* drop silently */
+		}
+	}
+
+	ret = get_job_no(&last_job);
+	assert(ret == 0);
+
+	ret = task_mode(BACKGROUND_TASK);
+	assert(ret == 0);
+
+	printf("# Kernel Jobs: %d\n", last_job - first_job + 1);
+	printf("# User Started Jobs: %d\n", NUM_JOBS);
+	printf("# User Jobs Completed: %d\n", NUM_COMPLETED_JOBS);
+	printf("# Overruns: %d\n", NUM_OVERRUNS);
+
+	delete[] LOCKS;
+
+	return 0;
+}
diff --git a/gpu/dgl.c b/gpu/dgl.c
new file mode 100644
index 0000000..c40fec6
--- /dev/null
+++ b/gpu/dgl.c
@@ -0,0 +1,282 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* Include gettid() */
+#include <sys/types.h>
+
+/* Include threading support. */
+#include <pthread.h>
+
+/* Include the LITMUS^RT API.*/
+#include "litmus.h"
+
+#define xfprintf( ... ) do { \
+if(!SILENT) { fprintf( __VA_ARGS__ ) ; } \
+} while (0)
+
+
+/* Catch errors.
+ */
+#define CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			xfprintf(stderr, "%s failed: %m\n", #exp);\
+		else \
+			xfprintf(stderr, "%s ok.\n", #exp); \
+	} while (0)
+
+#define TH_CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			xfprintf(stderr, "[%d] %s failed: %m\n", ctx->id, #exp); \
+		else \
+			xfprintf(stderr, "[%d] %s ok.\n", ctx->id, #exp); \
+	} while (0)
+
+#define TH_SAFE_CALL( exp ) do { \
+		int ret; \
+		xfprintf(stderr, "[%d] calling %s...\n", ctx->id, #exp); \
+		ret = exp; \
+		if (ret != 0) \
+			xfprintf(stderr, "\t...[%d] %s failed: %m\n", ctx->id, #exp); \
+		else \
+			xfprintf(stderr, "\t...[%d] %s ok.\n", ctx->id, #exp); \
+	} while (0)
+
+
+
+
+
+/* these are only default values */
+int NUM_THREADS=3;
+int NUM_SEMS=1;
+unsigned int NUM_REPLICAS=0;
+int NEST_DEPTH=1;
+
+int SILENT = 0;
+
+int SLEEP_BETWEEN_JOBS = 1;
+int USE_PRIOQ = 0;
+
+#define MAX_SEMS 1000
+#define MAX_NEST_DEPTH 10
+
+
+// 1000 = 1us
+#define EXEC_COST 	 1000*1
+#define PERIOD		1000*10
+
+/* The information passed to each thread. Could be anything. */
+struct thread_context {
+	int id;
+	int fd;
+	int ikglp;
+	int od[MAX_SEMS];
+	int count;
+	unsigned int rand;
+};
+
+void* rt_thread(void* _ctx);
+int nested_job(struct thread_context* ctx, int *count, int *next);
+int job(struct thread_context*);
+
+#define OPTSTR "t:k:s:d:fqX"
+
+int main(int argc, char** argv)
+{
+	int i;
+	struct thread_context* ctx;
+	pthread_t*	     task;
+	int fd;
+
+	int opt;
+	while((opt = getopt(argc, argv, OPTSTR)) != -1) {
+		switch(opt) {
+			case 't':
+				NUM_THREADS = atoi(optarg);
+				break;
+			case 'k':
+				NUM_REPLICAS = atoi(optarg);
+				assert(NUM_REPLICAS > 0);
+				break;
+			case 's':
+				NUM_SEMS = atoi(optarg);
+				assert(NUM_SEMS >= 0 && NUM_SEMS <= MAX_SEMS);
+				break;
+			case 'd':
+				NEST_DEPTH = atoi(optarg);
+				assert(NEST_DEPTH >= 1 && NEST_DEPTH <= MAX_NEST_DEPTH);
+				break;
+			case 'f':
+				SLEEP_BETWEEN_JOBS = 0;
+				break;
+			case 'q':
+				USE_PRIOQ = 1;
+				break;
+			case 'X':
+				SILENT = 1;
+				break;
+			default:
+				fprintf(stderr, "Unknown option: %c\n", opt);
+				exit(-1);
+				break;
+		}
+	}
+
+	ctx = (struct thread_context*) calloc(NUM_THREADS, sizeof(struct thread_context));
+	task = (pthread_t*) calloc(NUM_THREADS, sizeof(pthread_t));
+
+	srand(0); /* something repeatable for now */
+
+	fd = open("semaphores", O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
+
+	CALL( init_litmus() );
+
+	for (i = 0; i < NUM_THREADS; i++) {
+		ctx[i].id = i;
+		ctx[i].fd = fd;
+		ctx[i].rand = rand();
+		CALL( pthread_create(task + i, NULL, rt_thread, ctx + i) );
+	}
+
+
+	for (i = 0; i < NUM_THREADS; i++)
+		pthread_join(task[i], NULL);
+
+
+	return 0;
+}
+
+void* rt_thread(void* _ctx)
+{
+	int i;
+	int do_exit = 0;
+	struct rt_task param;
+
+	struct thread_context *ctx = (struct thread_context*)_ctx;
+
+	init_rt_task_param(&param);
+	param.exec_cost = EXEC_COST;
+	param.period = PERIOD + 10*ctx->id; /* Vary period a little bit. */
+	param.cls = RT_CLASS_SOFT;
+
+	TH_CALL( init_rt_thread() );
+	TH_CALL( set_rt_task_param(gettid(), &param) );
+
+	if (NUM_REPLICAS) {
+		ctx->ikglp = open_ikglp_sem(ctx->fd, 0, NUM_REPLICAS);
+		if(ctx->ikglp < 0)
+			perror("open_ikglp_sem");
+		else
+			xfprintf(stdout, "ikglp od = %d\n", ctx->ikglp);
+	}
+
+
+	for (i = 0; i < NUM_SEMS; i++) {
+		if(!USE_PRIOQ) {
+			ctx->od[i] = open_fifo_sem(ctx->fd, i+1);
+			if(ctx->od[i] < 0)
+				perror("open_fifo_sem");
+			else
+				xfprintf(stdout, "fifo[%d] od = %d\n", i, ctx->od[i]);
+		}
+		else {
+			ctx->od[i] = open_prioq_sem(ctx->fd, i+1);
+			if(ctx->od[i] < 0)
+				perror("open_prioq_sem");
+			else
+				xfprintf(stdout, "prioq[%d] od = %d\n", i, ctx->od[i]);
+		}
+	}
+
+	TH_CALL( task_mode(LITMUS_RT_TASK) );
+
+
+	xfprintf(stdout, "[%d] Waiting for TS release.\n ", ctx->id);
+	wait_for_ts_release();
+	ctx->count = 0;
+
+	do {
+		int replica = -1;
+		int first = (int)(NUM_SEMS * (rand_r(&(ctx->rand)) / (RAND_MAX + 1.0)));
+		int last = (first + NEST_DEPTH - 1 >= NUM_SEMS) ? NUM_SEMS - 1 : first + NEST_DEPTH - 1;
+		int dgl_size = last - first + 1;
+		int dgl[dgl_size];
+
+		// construct the DGL
+		for(i = first; i <= last; ++i) {
+			dgl[i-first] = ctx->od[i];
+		}
+
+
+		if(NUM_REPLICAS) {
+			replica = litmus_lock(ctx->ikglp);
+			xfprintf(stdout, "[%d] got ikglp replica %d.\n", ctx->id, replica);
+		}
+
+
+		litmus_dgl_lock(dgl, dgl_size);
+		xfprintf(stdout, "[%d] acquired dgl.\n", ctx->id);
+
+		do_exit = job(ctx);
+
+		fprintf(stdout, "[%d] should yield dgl: %d.\n", ctx->id, litmus_dgl_should_yield_lock(dgl, dgl_size));
+
+		xfprintf(stdout, "[%d] unlocking dgl.\n", ctx->id);
+		litmus_dgl_unlock(dgl, dgl_size);
+
+		if(NUM_REPLICAS) {
+			xfprintf(stdout, "[%d]: freeing ikglp replica %d.\n", ctx->id, replica);
+			litmus_unlock(ctx->ikglp);
+		}
+
+		if(SLEEP_BETWEEN_JOBS && !do_exit) {
+			sleep_next_period();
+		}
+	} while(!do_exit);
+
+	/*****
+	 * 4) Transition to background mode.
+	 */
+	TH_CALL( task_mode(BACKGROUND_TASK) );
+
+
+	return NULL;
+}
+
+void dirty_kb(int kb)
+{
+	int32_t one_kb[256];
+	int32_t sum = 0;
+	int32_t i;
+
+	for (i = 0; i < 256; i++)
+		sum += one_kb[i];
+	kb--;
+	/* prevent tail recursion */
+	if (kb)
+		dirty_kb(kb);
+	for (i = 0; i < 256; i++)
+		sum += one_kb[i];
+}
+
+int job(struct thread_context* ctx)
+{
+	/* Do real-time calculation. */
+	dirty_kb(8);
+
+	/* Don't exit. */
+	//return ctx->count++ > 100;
+	//return ctx->count++ > 12000;
+	//return ctx->count++ > 120000;
+	return ctx->count++ >   50000;  // controls number of jobs per task
+}
diff --git a/gpu/gpuspin.cu b/gpu/gpuspin.cu
new file mode 100644
index 0000000..c42dea9
--- /dev/null
+++ b/gpu/gpuspin.cu
@@ -0,0 +1,2705 @@
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <execinfo.h>
+
+#include <exception>
+
+#include <boost/interprocess/managed_shared_memory.hpp>
+#include <boost/interprocess/sync/interprocess_mutex.hpp>
+#include <boost/filesystem.hpp>
+
+#include <random/normal.h>
+
+#include <cuda.h>
+#include <cuda_runtime.h>
+
+#include "litmus.h"
+#include "common.h"
+
+using namespace std;
+using namespace boost::interprocess;
+using namespace ranlib;
+
+#define ms2s(ms)  ((ms)*0.001)
+
+const unsigned int TOKEN_START = 100;
+const unsigned int TOKEN_END = 101;
+
+const unsigned int EE_START = 200;
+const unsigned int EE_END = 201;
+
+const unsigned int CE_SEND_START = 300;
+const unsigned int CE_SEND_END = 301;
+
+const unsigned int CE_RECV_START = 400;
+const unsigned int CE_RECV_END = 401;
+
+bool SILENT = true;
+//bool SILENT = false;
+inline int xprintf(const char *format, ...)
+{
+	int ret = 0;
+	if (!SILENT) {
+		va_list args;
+		va_start(args, format);
+		ret = vprintf(format, args);
+		va_end(args);
+	}
+	return ret;
+}
+
+const char *lock_namespace = "./.gpuspin-locks";
+const size_t PAGE_SIZE = sysconf(_SC_PAGESIZE);
+
+const int NR_GPUS = 8;
+
+bool WANT_SIGNALS = false;
+inline void gpuspin_block_litmus_signals(unsigned long mask)
+{
+	if (WANT_SIGNALS)
+		block_litmus_signals(mask);
+}
+
+inline void gpuspin_unblock_litmus_signals(unsigned long mask)
+{
+	if (WANT_SIGNALS)
+		unblock_litmus_signals(mask);
+}
+
+bool GPU_USING = false;
+bool ENABLE_AFFINITY = false;
+bool RELAX_FIFO_MAX_LEN = false;
+bool ENABLE_CHUNKING = false;
+bool MIGRATE_VIA_SYSMEM = false;
+
+bool YIELD_LOCKS = false;
+
+enum eEngineLockTypes
+{
+	FIFO,
+	PRIOQ
+};
+
+eEngineLockTypes ENGINE_LOCK_TYPE = FIFO;
+
+int GPU_PARTITION = 0;
+int GPU_PARTITION_SIZE = 0;
+int CPU_PARTITION_SIZE = 0;
+
+int RHO = 2;
+
+int NUM_COPY_ENGINES = 2;
+
+
+__attribute__((unused)) static size_t kbToB(size_t kb) { return kb * 1024; }
+__attribute__((unused)) static size_t mbToB(size_t mb) { return kbToB(mb * 1024); }
+
+/* in bytes */
+size_t SEND_SIZE = 0;
+size_t RECV_SIZE = 0;
+size_t STATE_SIZE = 0;
+size_t CHUNK_SIZE = 0;
+
+int TOKEN_LOCK = -1;
+
+bool USE_ENGINE_LOCKS = false;
+bool USE_DYNAMIC_GROUP_LOCKS = false;
+int EE_LOCKS[NR_GPUS];
+int CE_SEND_LOCKS[NR_GPUS];
+int CE_RECV_LOCKS[NR_GPUS];
+int CE_MIGR_SEND_LOCKS[NR_GPUS];
+int CE_MIGR_RECV_LOCKS[NR_GPUS];
+bool RESERVED_MIGR_COPY_ENGINE = false;  // only checked if NUM_COPY_ENGINES == 2
+
+//bool ENABLE_RT_AUX_THREADS = false;
+bool ENABLE_RT_AUX_THREADS = true;
+
+enum eGpuSyncMode
+{
+	IKGLP_MODE,
+	IKGLP_WC_MODE, /* work-conserving IKGLP. no GPU is left idle, but breaks optimality */
+	KFMLP_MODE,
+	RGEM_MODE,
+};
+
+eGpuSyncMode GPU_SYNC_MODE = IKGLP_MODE;
+
+enum eCudaSyncMode
+{
+	BLOCKING,
+	SPIN
+};
+
+eCudaSyncMode CUDA_SYNC_MODE = BLOCKING;
+
+
+int CUR_DEVICE = -1;
+int LAST_DEVICE = -1;
+
+cudaStream_t STREAMS[NR_GPUS];
+cudaEvent_t EVENTS[NR_GPUS];
+int GPU_HZ[NR_GPUS];
+int NUM_SM[NR_GPUS];
+int WARP_SIZE[NR_GPUS];
+int ELEM_PER_THREAD[NR_GPUS];
+
+enum eScheduler
+{
+	LITMUS,
+	LINUX,
+	RT_LINUX
+};
+
+struct Args
+{
+	bool wait;
+	bool migrate;
+	int cluster;
+	int cluster_size;
+	bool gpu_using;
+	int gpu_partition;
+	int gpu_partition_size;
+	int rho;
+	int num_ce;
+	bool reserve_migr_ce;
+	bool use_engine_locks;
+	eEngineLockTypes engine_lock_type;
+	bool yield_locks;
+	bool use_dgls;
+	eGpuSyncMode gpusync_mode;
+	bool enable_affinity;
+	int relax_fifo_len;
+	eCudaSyncMode sync_mode;
+	size_t send_size;
+	size_t recv_size;
+	size_t state_size;
+	bool enable_chunking;
+	size_t chunk_size;
+	bool use_sysmem_migration;
+	int num_kernels;
+
+	double wcet_ms;
+	double gpu_wcet_ms;
+	double period_ms;
+
+	double budget_ms;
+
+	double stddev;
+
+	eScheduler scheduler;
+
+	unsigned int priority;
+
+	task_class_t cls;
+
+	bool want_enforcement;
+	bool want_signals;
+	budget_drain_policy_t drain_policy;
+
+	int column;
+
+	int num_gpu_tasks;
+	int num_tasks;
+
+	double scale;
+
+	double duration;
+
+	bool is_aberrant;
+	double aberrant_prob;
+	double aberrant_factor;
+};
+
+
+
+#define DEFINE_PER_GPU(type, var) type var[NR_GPUS]
+#define per_gpu(var, idx) (var[(idx)])
+#define this_gpu(var) (var[(CUR_DEVICE)])
+#define cur_stream() (this_gpu(STREAMS))
+#define cur_event() (this_gpu(EVENTS))
+#define cur_gpu() (CUR_DEVICE)
+#define last_gpu() (LAST_DEVICE)
+#define cur_ee() (EE_LOCKS[CUR_DEVICE])
+#define cur_send() (CE_SEND_LOCKS[CUR_DEVICE])
+#define cur_recv() (CE_RECV_LOCKS[CUR_DEVICE])
+#define cur_migr_send() (CE_MIGR_SEND_LOCKS[CUR_DEVICE])
+#define cur_migr_recv() (CE_MIGR_RECV_LOCKS[CUR_DEVICE])
+#define cur_hz() (GPU_HZ[CUR_DEVICE])
+#define cur_sms() (NUM_SM[CUR_DEVICE])
+#define cur_warp_size() (WARP_SIZE[CUR_DEVICE])
+#define cur_elem_per_thread() (ELEM_PER_THREAD[CUR_DEVICE])
+#define num_online_gpus() (NUM_GPUS)
+
+static bool useEngineLocks()
+{
+	return(USE_ENGINE_LOCKS);
+}
+
+//#define VANILLA_LINUX
+
+bool TRACE_MIGRATIONS = false;
+#ifndef VANILLA_LINUX
+#define trace_migration(to, from)					do { inject_gpu_migration((to), (from)); } while(0)
+#define trace_release(arrival, deadline, jobno)		do { inject_release((arrival), (deadline), (jobno)); } while(0)
+#define trace_completion(jobno)						do { inject_completion((jobno)); } while(0)
+#define trace_name()								do { inject_name(); } while(0)
+#define trace_param()								do { inject_param(); } while(0)
+#else
+#define set_rt_task_param(x, y)						(0)
+#define trace_migration(to, from)
+#define trace_release(arrival, deadline, jobno)
+#define trace_completion(jobno)
+#define trace_name()
+#define trace_param()
+#endif
+
+struct ce_lock_state
+{
+	int locks[2];
+	size_t num_locks;
+	size_t budget_remaining;
+	bool locked;
+
+	ce_lock_state(int device_a, enum cudaMemcpyKind kind, size_t size, int device_b = -1, bool migration = false) {
+		num_locks = (device_a != -1) + (device_b != -1);
+
+		if(device_a != -1) {
+			if (!migration)
+				locks[0] = (kind == cudaMemcpyHostToDevice || (kind == cudaMemcpyDeviceToDevice && device_b == -1)) ?
+				CE_SEND_LOCKS[device_a] : CE_RECV_LOCKS[device_a];
+			else
+				locks[0] = (kind == cudaMemcpyHostToDevice || (kind == cudaMemcpyDeviceToDevice && device_b == -1)) ?
+				CE_MIGR_SEND_LOCKS[device_a] : CE_MIGR_RECV_LOCKS[device_a];
+		}
+
+		if(device_b != -1) {
+			assert(kind == cudaMemcpyDeviceToDevice);
+
+			if (!migration)
+				locks[1] = CE_RECV_LOCKS[device_b];
+			else
+				locks[1] = CE_MIGR_RECV_LOCKS[device_b];
+
+			if(locks[1] < locks[0]) {
+				// enforce total order on locking
+				int temp = locks[1];
+				locks[1] = locks[0];
+				locks[0] = temp;
+			}
+		}
+		else {
+			locks[1] = -1;
+		}
+
+		if(!ENABLE_CHUNKING)
+			budget_remaining = size;
+		else
+			budget_remaining = CHUNK_SIZE;
+	}
+
+	void crash(void) {
+		void *array[50];
+		int size, i;
+		char **messages;
+
+		size = backtrace(array, 50);
+		messages = backtrace_symbols(array, size);
+
+		fprintf(stderr, "%d: TRIED TO GRAB SAME LOCK TWICE! Lock = %d\n", getpid(), locks[0]);
+		for (i = 1; i < size && messages != NULL; ++i)
+		{
+			fprintf(stderr, "%d: [bt]: (%d) %s\n", getpid(), i, messages[i]);
+		}
+		free(messages);
+
+		assert(false);
+	}
+
+
+	void lock() {
+		if(locks[0] == locks[1]) crash();
+
+		if (num_locks == 1) {
+			gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+			litmus_lock(locks[0]);
+			gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		}
+		else if(USE_DYNAMIC_GROUP_LOCKS) {
+			gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+			litmus_dgl_lock(locks, num_locks);
+			gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		}
+		else
+		{
+			gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+			for(int l = 0; l < num_locks; ++l)
+			{
+				litmus_lock(locks[l]);
+			}
+			gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		}
+		locked = true;
+	}
+
+	void unlock() {
+		if(locks[0] == locks[1]) crash();
+
+		if (num_locks == 1) {
+			gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+			litmus_unlock(locks[0]);
+			gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		}
+		else if(USE_DYNAMIC_GROUP_LOCKS) {
+			gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+			litmus_dgl_unlock(locks, num_locks);
+			gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		}
+		else
+		{
+			gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+			// reverse order
+			for(int l = num_locks - 1; l >= 0; --l)
+			{
+				litmus_unlock(locks[l]);
+			}
+			gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		}
+		locked = false;
+	}
+
+	bool should_yield() {
+		int yield = 1; // assume we should yield
+		if (YIELD_LOCKS) {
+			if(locks[0] == locks[1]) crash();
+			if (num_locks == 1)
+				yield = litmus_should_yield_lock(locks[0]);
+			else if(USE_DYNAMIC_GROUP_LOCKS)
+				yield = litmus_dgl_should_yield_lock(locks, num_locks);
+			else
+				for(int l = num_locks - 1; l >= 0; --l)  // reverse order
+					yield |= litmus_should_yield_lock(locks[l]);
+		}
+		return (yield);
+	}
+
+	void refresh() {
+		budget_remaining = CHUNK_SIZE;
+	}
+
+	bool budgetIsAvailable(size_t tosend) {
+		return(tosend >= budget_remaining);
+	}
+
+	void decreaseBudget(size_t spent) {
+		budget_remaining -= spent;
+	}
+};
+
+// precondition: if do_locking == true, locks in state are held.
+static cudaError_t __chunkMemcpy(void* a_dst, const void* a_src, size_t count,
+								 enum cudaMemcpyKind kind,
+								 ce_lock_state* state)
+{
+    cudaError_t ret = cudaSuccess;
+    int remaining = count;
+
+    char* dst = (char*)a_dst;
+    const char* src = (const char*)a_src;
+
+	// disable chunking, if needed, by setting chunk_size equal to the
+	// amount of data to be copied.
+	int chunk_size = (ENABLE_CHUNKING) ? CHUNK_SIZE : count;
+	int i = 0;
+
+    while(remaining != 0)
+    {
+        int bytesToCopy = std::min(remaining, chunk_size);
+
+		if (state && state->locked) {
+			// we have to unlock/re-lock the copy engine to refresh our budget unless
+			// we still have budget available.
+			if (!state->budgetIsAvailable(bytesToCopy)) {
+				// optimization - don't unlock if no one else needs the engine
+				if (state->should_yield()) {
+					gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS); 
+					cudaEventSynchronize(cur_event());
+					ret = cudaGetLastError();
+					if (kind == cudaMemcpyDeviceToHost || kind == cudaMemcpyDeviceToDevice)
+						inject_action(CE_RECV_END);
+					if (kind == cudaMemcpyHostToDevice)
+						inject_action(CE_SEND_END);
+					gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+
+					state->unlock();
+					if(ret != cudaSuccess)
+						break;
+				}
+				// we can only run out of
+				// budget if chunking is enabled.
+				// we presume that init budget would
+				// be set to cover entire memcpy
+				// if chunking were disabled.
+				state->refresh();
+			}
+		}
+
+		if(state && !state->locked) {
+			state->lock();
+			if (kind == cudaMemcpyDeviceToHost || kind == cudaMemcpyDeviceToDevice)
+				inject_action(CE_RECV_START);
+			if (kind == cudaMemcpyHostToDevice)
+				inject_action(CE_SEND_START);
+		}
+
+        //ret = cudaMemcpy(dst+i*chunk_size, src+i*chunk_size, bytesToCopy, kind);
+		gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		cudaMemcpyAsync(dst+i*chunk_size, src+i*chunk_size, bytesToCopy, kind, cur_stream());
+		cudaEventRecord(cur_event(), cur_stream());
+		gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+
+		if(state)
+			state->decreaseBudget(bytesToCopy);
+
+        ++i;
+        remaining -= bytesToCopy;
+    }
+    return ret;
+}
+
+static cudaError_t chunkMemcpy(void* a_dst, const void* a_src, size_t count,
+							   enum cudaMemcpyKind kind,
+							   int device_a = -1,  // device_a == -1 disables locking
+							   bool do_locking = true,
+							   int device_b = -1,
+							   bool migration = false)
+{
+	cudaError_t ret;
+	if(!do_locking || device_a == -1) {
+		ret = __chunkMemcpy(a_dst, a_src, count, kind, NULL);
+		gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		cudaEventSynchronize(cur_event());
+		if(ret == cudaSuccess)
+			ret = cudaGetLastError();
+		gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+	}
+	else {
+		ce_lock_state state(device_a, kind, count, device_b, migration);
+		state.lock();
+
+		if (kind == cudaMemcpyDeviceToHost || kind == cudaMemcpyDeviceToDevice)
+			inject_action(CE_RECV_START);
+		if (kind == cudaMemcpyHostToDevice)
+			inject_action(CE_SEND_START);
+
+		ret = __chunkMemcpy(a_dst, a_src, count, kind, &state);
+		gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		cudaEventSynchronize(cur_event());
+		//		cudaStreamSynchronize(cur_stream());
+		if(ret == cudaSuccess)
+			ret = cudaGetLastError();
+
+		if (kind == cudaMemcpyDeviceToHost || kind == cudaMemcpyDeviceToDevice)
+			inject_action(CE_RECV_END);
+		if (kind == cudaMemcpyHostToDevice)
+			inject_action(CE_SEND_END);
+		gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+
+		state.unlock();
+	}
+	return ret;
+}
+
+int LITMUS_LOCK_FD = 0;
+
+int EXP_OFFSET = 0;
+
+void allocate_locks_litmus(void)
+{
+	stringstream ss;
+	ss<<lock_namespace<<"-"<<EXP_OFFSET;
+
+	// allocate k-FMLP lock
+	//LITMUS_LOCK_FD = open(lock_namespace, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
+	LITMUS_LOCK_FD = open(ss.str().c_str(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
+	int *fd = &LITMUS_LOCK_FD;
+
+	int base_name = GPU_PARTITION * 100 + EXP_OFFSET * 200;
+	++EXP_OFFSET;
+
+	if (GPU_SYNC_MODE == IKGLP_MODE) {
+		/* Standard (optimal) IKGLP */
+		TOKEN_LOCK = open_gpusync_token_lock(*fd,
+						base_name,  /* name */
+						GPU_PARTITION_SIZE,
+						GPU_PARTITION*GPU_PARTITION_SIZE,
+						RHO,
+						IKGLP_M_IN_FIFOS,
+						(!RELAX_FIFO_MAX_LEN) ?
+						IKGLP_OPTIMAL_FIFO_LEN :
+						IKGLP_UNLIMITED_FIFO_LEN,
+						ENABLE_AFFINITY);
+	}
+	else if (GPU_SYNC_MODE == KFMLP_MODE) {
+		/* KFMLP. FIFO queues only for tokens. */
+		TOKEN_LOCK = open_gpusync_token_lock(*fd,
+						base_name,  /* name */
+						GPU_PARTITION_SIZE,
+						GPU_PARTITION*GPU_PARTITION_SIZE,
+						RHO,
+						IKGLP_UNLIMITED_IN_FIFOS,
+						IKGLP_UNLIMITED_FIFO_LEN,
+						ENABLE_AFFINITY);
+	}
+	else if (GPU_SYNC_MODE == RGEM_MODE) {
+		/* RGEM-like token allocation. Shared priority queue for all tokens. */
+		TOKEN_LOCK = open_gpusync_token_lock(*fd,
+						base_name,  /* name */
+						GPU_PARTITION_SIZE,
+						GPU_PARTITION*GPU_PARTITION_SIZE,
+						RHO,
+						RHO*GPU_PARTITION_SIZE,
+						1,
+						ENABLE_AFFINITY);
+	}
+	else if (GPU_SYNC_MODE == IKGLP_WC_MODE) {
+		/* Non-optimal IKGLP that never lets a replica idle if there are pending
+		 * token requests. */
+		int max_simult_run = std::max(CPU_PARTITION_SIZE, RHO*GPU_PARTITION_SIZE);
+		int max_fifo_len = (int)ceil((float)max_simult_run / (RHO*GPU_PARTITION_SIZE));
+		TOKEN_LOCK = open_gpusync_token_lock(*fd,
+						base_name,  /* name */
+						GPU_PARTITION_SIZE,
+						GPU_PARTITION*GPU_PARTITION_SIZE,
+						RHO,
+						max_simult_run,
+						(!RELAX_FIFO_MAX_LEN) ?
+							max_fifo_len :
+							IKGLP_UNLIMITED_FIFO_LEN,
+						ENABLE_AFFINITY);
+	}
+	else {
+		perror("Invalid GPUSync mode specified\n");
+		TOKEN_LOCK = -1;
+	}
+
+	if(TOKEN_LOCK < 0)
+		perror("open_token_sem");
+
+	if(USE_ENGINE_LOCKS)
+	{
+		assert(NUM_COPY_ENGINES == 1 || NUM_COPY_ENGINES == 2);
+		assert((NUM_COPY_ENGINES == 1 && !RESERVED_MIGR_COPY_ENGINE) || NUM_COPY_ENGINES == 2);
+
+		// allocate the engine locks.
+		for (int i = 0; i < GPU_PARTITION_SIZE; ++i)
+		{
+			int idx = GPU_PARTITION*GPU_PARTITION_SIZE + i;
+			int ee_name = (i+1)*10 + base_name;
+			int ce_0_name = (i+1)*10 + base_name + 1;
+			int ce_1_name = (i+1)*10 + base_name + 2;
+			int ee_lock = -1, ce_0_lock = -1, ce_1_lock = -1;
+
+			open_sem_t openEngineLock = (ENGINE_LOCK_TYPE == FIFO) ?
+				open_fifo_sem : open_prioq_sem;
+
+			ee_lock = openEngineLock(*fd, ee_name);
+			if (ee_lock < 0)
+				perror("open_*_sem (engine lock)");
+
+			ce_0_lock = openEngineLock(*fd, ce_0_name);
+			if (ce_0_lock < 0)
+				perror("open_*_sem (engine lock)");
+
+			if (NUM_COPY_ENGINES == 2)
+			{
+				ce_1_lock = openEngineLock(*fd, ce_1_name);
+				if (ce_1_lock < 0)
+					perror("open_*_sem (engine lock)");
+			}
+
+			EE_LOCKS[idx] = ee_lock;
+
+			if (NUM_COPY_ENGINES == 1)
+			{
+				// share locks
+				CE_SEND_LOCKS[idx] = ce_0_lock;
+				CE_RECV_LOCKS[idx] = ce_0_lock;
+				CE_MIGR_SEND_LOCKS[idx] = ce_0_lock;
+				CE_MIGR_RECV_LOCKS[idx] = ce_0_lock;
+			}
+			else
+			{
+				assert(NUM_COPY_ENGINES == 2);
+
+				if (RESERVED_MIGR_COPY_ENGINE) {
+					// copy engine deadicated to migration operations
+					CE_SEND_LOCKS[idx] = ce_0_lock;
+					CE_RECV_LOCKS[idx] = ce_0_lock;
+					CE_MIGR_SEND_LOCKS[idx] = ce_1_lock;
+					CE_MIGR_RECV_LOCKS[idx] = ce_1_lock;
+				}
+				else {
+					// migration transmissions treated as regular data
+					CE_SEND_LOCKS[idx] = ce_0_lock;
+					CE_RECV_LOCKS[idx] = ce_1_lock;
+					CE_MIGR_SEND_LOCKS[idx] = ce_0_lock;
+					CE_MIGR_RECV_LOCKS[idx] = ce_1_lock;
+				}
+			}
+		}
+	}
+}
+
+void deallocate_locks_litmus(void)
+{
+	for (int i = 0; i < GPU_PARTITION_SIZE; ++i)
+	{
+		int idx = GPU_PARTITION*GPU_PARTITION_SIZE + i;
+
+		od_close(EE_LOCKS[idx]);
+		if (NUM_COPY_ENGINES == 1)
+		{
+			od_close(CE_SEND_LOCKS[idx]);
+		}
+		else
+		{
+			if (RESERVED_MIGR_COPY_ENGINE) {
+				od_close(CE_SEND_LOCKS[idx]);
+				od_close(CE_MIGR_SEND_LOCKS[idx]);
+			}
+			else {
+				od_close(CE_SEND_LOCKS[idx]);
+				od_close(CE_RECV_LOCKS[idx]);
+			}
+		}
+	}
+
+	od_close(TOKEN_LOCK);
+
+	close(LITMUS_LOCK_FD);
+
+	memset(&CE_SEND_LOCKS[0], 0, sizeof(CE_SEND_LOCKS));
+	memset(&CE_RECV_LOCKS[0], 0, sizeof(CE_RECV_LOCKS));
+	memset(&CE_MIGR_SEND_LOCKS[0], 0, sizeof(CE_MIGR_SEND_LOCKS));
+	memset(&CE_MIGR_RECV_LOCKS[0], 0, sizeof(CE_MIGR_RECV_LOCKS));
+	TOKEN_LOCK = -1;
+	LITMUS_LOCK_FD = 0;
+}
+
+
+class gpu_pool
+{
+public:
+    gpu_pool(int pSz): poolSize(pSz)
+    {
+		memset(&pool[0], 0, sizeof(pool[0])*poolSize);
+    }
+
+    int get(pthread_mutex_t* tex, int preference = -1)
+    {
+        int which = -1;
+		int last = (ENABLE_AFFINITY) ?
+				((preference >= 0) ? preference : 0) :
+				(rand()%poolSize);
+		int minIdx = last;
+
+		pthread_mutex_lock(tex);
+
+		int min = pool[last];
+		for(int i = (minIdx+1)%poolSize; i != last; i = (i+1)%poolSize)
+		{
+			if(min > pool[i])
+				minIdx = i;
+		}
+		++pool[minIdx];
+
+		pthread_mutex_unlock(tex);
+
+		which = minIdx;
+
+        return which;
+    }
+
+    void put(pthread_mutex_t* tex, int which)
+    {
+		pthread_mutex_lock(tex);
+		--pool[which];
+		pthread_mutex_unlock(tex);
+    }
+
+private:
+	int poolSize;
+    int pool[NR_GPUS]; // >= gpu_part_size
+};
+
+
+static managed_shared_memory *linux_lock_segment_ptr = NULL;
+static gpu_pool* GPU_LINUX_SEM_POOL = NULL;
+static pthread_mutex_t* GPU_LINUX_MUTEX_POOL = NULL;
+
+static void allocate_locks_linux(const int num_gpu_users)
+{
+	int numGpuPartitions = NR_GPUS/GPU_PARTITION_SIZE;
+
+	if(num_gpu_users > 0)
+	{
+		xprintf("%d: creating linux locks\n", getpid());
+		shared_memory_object::remove("linux_lock_memory");
+
+		linux_lock_segment_ptr = new managed_shared_memory(create_only, "linux_lock_memory", 30*PAGE_SIZE);
+		GPU_LINUX_MUTEX_POOL = linux_lock_segment_ptr->construct<pthread_mutex_t>("pthread_mutex_t linux_m")[numGpuPartitions]();
+		for(int i = 0; i < numGpuPartitions; ++i)
+		{
+			pthread_mutexattr_t attr;
+			pthread_mutexattr_init(&attr);
+			pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+			pthread_mutex_init(&(GPU_LINUX_MUTEX_POOL[i]), &attr);
+			pthread_mutexattr_destroy(&attr);
+		}
+		GPU_LINUX_SEM_POOL = linux_lock_segment_ptr->construct<gpu_pool>("gpu_pool linux_p")[numGpuPartitions](GPU_PARTITION_SIZE);
+	}
+	else
+	{
+		sleep(5);
+		do
+		{
+			try
+			{
+				if (!linux_lock_segment_ptr)
+					linux_lock_segment_ptr = new managed_shared_memory(open_only, "linux_lock_memory");
+			}
+			catch(...)
+			{
+				sleep(1);
+			}
+		}while(linux_lock_segment_ptr == NULL);
+
+		GPU_LINUX_MUTEX_POOL = linux_lock_segment_ptr->find<pthread_mutex_t>("pthread_mutex_t linux_m").first;
+		GPU_LINUX_SEM_POOL = linux_lock_segment_ptr->find<gpu_pool>("gpu_pool linux_p").first;
+	}
+}
+
+static void deallocate_locks_linux(const int num_gpu_users)
+{
+	GPU_LINUX_MUTEX_POOL = NULL;
+	GPU_LINUX_SEM_POOL = NULL;
+
+	delete linux_lock_segment_ptr;
+	linux_lock_segment_ptr = NULL;
+
+	if(num_gpu_users > 0)
+		shared_memory_object::remove("linux_lock_memory");
+}
+
+
+
+
+static void allocate_locks(const int num_gpu_users, bool linux_mode)
+{
+	if(!linux_mode)
+		allocate_locks_litmus();
+	else
+		allocate_locks_linux(num_gpu_users);
+}
+
+static void deallocate_locks(const int num_gpu_users, bool linux_mode)
+{
+	if(!linux_mode)
+		deallocate_locks_litmus();
+	else
+		deallocate_locks_linux(num_gpu_users);
+}
+
+static void set_cur_gpu(int gpu)
+{
+	if (TRACE_MIGRATIONS) {
+		trace_migration(gpu, CUR_DEVICE);
+	}
+	if(gpu != CUR_DEVICE) {
+		cudaSetDevice(gpu);
+		CUR_DEVICE = gpu;
+	}
+}
+
+
+//static pthread_barrier_t *gpu_barrier = NULL;
+static interprocess_mutex *gpu_mgmt_mutexes = NULL;
+static managed_shared_memory *gpu_mutex_segment_ptr = NULL;
+
+void coordinate_gpu_tasks(const int num_gpu_users)
+{
+	if(num_gpu_users > 0)
+	{
+		xprintf("%d creating shared memory\n", getpid());
+		shared_memory_object::remove("gpu_mutex_memory");
+		gpu_mutex_segment_ptr = new managed_shared_memory(create_only, "gpu_mutex_memory", PAGE_SIZE);
+
+//		printf("%d creating a barrier for %d users\n", getpid(), num_gpu_users);
+//		gpu_barrier = segment_ptr->construct<pthread_barrier_t>("pthread_barrier_t gpu_barrier")();
+//		pthread_barrierattr_t battr;
+//		pthread_barrierattr_init(&battr);
+//		pthread_barrierattr_setpshared(&battr, PTHREAD_PROCESS_SHARED);
+//		pthread_barrier_init(gpu_barrier, &battr, num_gpu_users);
+//		pthread_barrierattr_destroy(&battr);
+//		printf("%d creating gpu mgmt mutexes for %d devices\n", getpid(), NR_GPUS);
+		gpu_mgmt_mutexes = gpu_mutex_segment_ptr->construct<interprocess_mutex>("interprocess_mutex m")[NR_GPUS]();
+	}
+	else
+	{
+		sleep(5);
+		do
+		{
+			try
+			{
+				gpu_mutex_segment_ptr = new managed_shared_memory(open_only, "gpu_mutex_memory");
+			}
+			catch(...)
+			{
+				sleep(1);
+			}
+		}while(gpu_mutex_segment_ptr == NULL);
+
+//		gpu_barrier = segment_ptr->find<pthread_barrier_t>("pthread_barrier_t gpu_barrier").first;
+		gpu_mgmt_mutexes = gpu_mutex_segment_ptr->find<interprocess_mutex>("interprocess_mutex m").first;
+	}
+}
+
+const size_t SEND_ALLOC_SIZE = 12*1024;
+const size_t RECV_ALLOC_SIZE = 12*1024;
+const size_t STATE_ALLOC_SIZE = 16*1024;
+
+typedef float spindata_t;
+
+char *d_send_data[NR_GPUS] = {0};
+char *d_recv_data[NR_GPUS] = {0};
+char *d_state_data[NR_GPUS] = {0};
+spindata_t *d_spin_data[NR_GPUS] = {0};
+//unsigned int *d_iteration_count[NR_GPUS] = {0};
+
+
+bool p2pMigration[NR_GPUS][NR_GPUS] = {0};
+
+char *h_send_data = 0;
+char *h_recv_data = 0;
+char *h_state_data = 0;
+
+static void destroy_events()
+{
+	for(int i = 0; i < GPU_PARTITION_SIZE; ++i)
+	{
+		int which = GPU_PARTITION*GPU_PARTITION_SIZE + i;
+		gpu_mgmt_mutexes[which].lock();
+		set_cur_gpu(which);
+		cudaEventDestroy(EVENTS[which]);
+		gpu_mgmt_mutexes[which].unlock();
+	}
+}
+
+static void init_events()
+{
+	xprintf("creating %s events\n", (CUDA_SYNC_MODE == BLOCKING) ? "blocking" : "spinning");
+	for(int i = 0; i < GPU_PARTITION_SIZE; ++i)
+	{
+		int which = GPU_PARTITION*GPU_PARTITION_SIZE + i;
+		gpu_mgmt_mutexes[which].lock();
+		set_cur_gpu(which);
+		if (CUDA_SYNC_MODE == BLOCKING)
+			cudaEventCreateWithFlags(&EVENTS[which], cudaEventBlockingSync | cudaEventDisableTiming);
+		else
+			cudaEventCreateWithFlags(&EVENTS[which], cudaEventDefault | cudaEventDisableTiming);
+		gpu_mgmt_mutexes[which].unlock();
+	}
+}
+
+static void init_cuda(const int num_gpu_users)
+{
+	size_t send_alloc_bytes = SEND_ALLOC_SIZE + (SEND_ALLOC_SIZE%PAGE_SIZE != 0)*PAGE_SIZE;
+	size_t recv_alloc_bytes = RECV_ALLOC_SIZE + (RECV_ALLOC_SIZE%PAGE_SIZE != 0)*PAGE_SIZE;
+	size_t state_alloc_bytes = STATE_ALLOC_SIZE + (STATE_ALLOC_SIZE%PAGE_SIZE != 0)*PAGE_SIZE;
+
+	static bool first_time = true;
+
+	if (first_time) {
+		coordinate_gpu_tasks(num_gpu_users);
+		first_time = false;
+	}
+
+#if 0
+	switch (CUDA_SYNC_MODE)
+	{
+		case BLOCKING:
+			cudaSetDeviceFlags(cudaDeviceScheduleBlockingSync);
+			break;
+		case SPIN:
+			cudaSetDeviceFlags(cudaDeviceScheduleSpin);
+			break;
+	}
+#endif
+
+	for(int i = 0; i < GPU_PARTITION_SIZE; ++i)
+	{
+		cudaDeviceProp prop;
+		int which = GPU_PARTITION*GPU_PARTITION_SIZE + i;
+
+		gpu_mgmt_mutexes[which].lock();
+		try
+		{
+			set_cur_gpu(which);
+
+			xprintf("setting up GPU %d\n", which);
+
+			cudaDeviceSetLimit(cudaLimitPrintfFifoSize, 0);
+			cudaDeviceSetLimit(cudaLimitMallocHeapSize, 0);
+
+			cudaGetDeviceProperties(&prop, which);
+			GPU_HZ[which] = prop.clockRate * 1000; /* khz -> hz */
+			NUM_SM[which] = prop.multiProcessorCount;
+			WARP_SIZE[which] = prop.warpSize;
+
+			// enough to fill the L2 cache exactly.
+			ELEM_PER_THREAD[which] = (prop.l2CacheSize/(NUM_SM[which]*WARP_SIZE[which]*sizeof(spindata_t)));
+
+//			if (!MIGRATE_VIA_SYSMEM && prop.unifiedAddressing)
+			if (prop.unifiedAddressing)
+			{
+				for(int j = 0; j < GPU_PARTITION_SIZE; ++j)
+				{
+					if (i != j)
+					{
+						int other = GPU_PARTITION*GPU_PARTITION_SIZE + j;
+						int canAccess = 0;
+						cudaDeviceCanAccessPeer(&canAccess, which, other);
+						if(canAccess)
+						{
+							cudaDeviceEnablePeerAccess(other, 0);
+							p2pMigration[which][other] = true;
+						}
+					}
+				}
+			}
+
+			cudaStreamCreate(&STREAMS[which]);
+
+			// gpu working set
+			cudaMalloc(&d_spin_data[which], prop.l2CacheSize);
+			cudaMemset(&d_spin_data[which], 0, prop.l2CacheSize);
+
+			// send data
+			cudaMalloc(&d_send_data[which], send_alloc_bytes);
+			cudaHostAlloc(&h_send_data, send_alloc_bytes, cudaHostAllocPortable | cudaHostAllocMapped);
+
+			// recv data
+			cudaMalloc(&d_recv_data[which], recv_alloc_bytes);
+			cudaHostAlloc(&h_recv_data, recv_alloc_bytes, cudaHostAllocPortable | cudaHostAllocMapped);
+
+			// state data
+			cudaMalloc(&d_state_data[which], state_alloc_bytes);
+			cudaHostAlloc(&h_state_data, state_alloc_bytes, cudaHostAllocPortable | cudaHostAllocMapped | cudaHostAllocWriteCombined);
+		}
+		catch(std::exception &e)
+		{
+			fprintf(stderr, "caught an exception during initializiation!: %s\n", e.what());
+		}
+		catch(...)
+		{
+			fprintf(stderr, "caught unknown exception.\n");
+		}
+
+		gpu_mgmt_mutexes[which].unlock();
+	}
+
+	// roll back to first GPU
+	set_cur_gpu(GPU_PARTITION*GPU_PARTITION_SIZE);
+}
+
+
+
+static bool MigrateToGPU_P2P(int from, int to)
+{
+	bool success = true;
+	set_cur_gpu(to);
+	chunkMemcpy(this_gpu(d_state_data), per_gpu(d_state_data, from),
+				STATE_SIZE, cudaMemcpyDeviceToDevice, to,
+				useEngineLocks(), from, true);
+	return success;
+}
+
+
+static bool PullState(void)
+{
+	bool success = true;
+	chunkMemcpy(h_state_data, this_gpu(d_state_data),
+				STATE_SIZE, cudaMemcpyDeviceToHost,
+				cur_gpu(), useEngineLocks(), -1, true);
+	return success;
+}
+
+static bool PushState(void)
+{
+	bool success = true;
+	chunkMemcpy(this_gpu(d_state_data), h_state_data,
+				STATE_SIZE, cudaMemcpyHostToDevice,
+				cur_gpu(), useEngineLocks(), -1, true);
+	return success;
+}
+
+static bool MigrateToGPU_SysMem(int from, int to)
+{
+	// THIS IS ON-DEMAND SYS_MEM MIGRATION.  GPUSync says
+	// you should be using speculative migrations.
+	// Use PushState() and PullState().
+	fprintf(stderr, "Tried to sysmem migrate from %d to %d\n",
+					from, to);
+	assert(false); // for now
+
+	bool success = true;
+
+	set_cur_gpu(from);
+	chunkMemcpy(h_state_data, this_gpu(d_state_data),
+				STATE_SIZE, cudaMemcpyDeviceToHost,
+				from, useEngineLocks(), -1, true);
+
+	set_cur_gpu(to);
+	chunkMemcpy(this_gpu(d_state_data), h_state_data,
+				STATE_SIZE, cudaMemcpyHostToDevice,
+				to, useEngineLocks(), -1, true);
+
+	return success;
+}
+
+static bool MigrateToGPU(int from, int to)
+{
+	bool success = false;
+
+	if (from != to)
+	{
+		if(!MIGRATE_VIA_SYSMEM && p2pMigration[to][from])
+			success = MigrateToGPU_P2P(from, to);
+		else
+			success = MigrateToGPU_SysMem(from, to);
+	}
+	else
+	{
+		set_cur_gpu(to);
+		success = true;
+	}
+
+	return success;
+}
+
+static bool MigrateToGPU_Implicit(int to)
+{
+	return( MigrateToGPU(cur_gpu(), to) );
+}
+
+static void MigrateIfNeeded(int next_gpu)
+{
+	if(next_gpu != cur_gpu() && cur_gpu() != -1)
+	{
+		if (!MIGRATE_VIA_SYSMEM)
+			MigrateToGPU_Implicit(next_gpu);
+		else {
+			set_cur_gpu(next_gpu);
+			PushState();
+		}
+	}
+	else if(cur_gpu() == -1) {
+		set_cur_gpu(next_gpu);
+	}
+}
+
+static void exit_cuda()
+{
+#if 0
+	for(int i = 0; i < GPU_PARTITION_SIZE; ++i)
+	{
+		int which = GPU_PARTITION*GPU_PARTITION_SIZE + i;
+		gpu_mgmt_mutexes[which].lock();
+		set_cur_gpu(which);
+		cudaFree(d_send_data[which]);
+		cudaFree(d_recv_data[which]);
+		cudaFree(d_state_data[which]);
+		cudaFree(d_spin_data[which]);
+		gpu_mgmt_mutexes[which].unlock();
+	}
+#endif
+
+	cudaFreeHost(h_send_data);
+	cudaFreeHost(h_recv_data);
+	cudaFreeHost(h_state_data);
+
+	for(int i = 0; i < GPU_PARTITION_SIZE; ++i)
+	{
+		int which = GPU_PARTITION*GPU_PARTITION_SIZE + i;
+		gpu_mgmt_mutexes[which].lock();
+		set_cur_gpu(which);
+		cudaDeviceReset();
+		gpu_mgmt_mutexes[which].unlock();
+	}
+
+	memset(d_send_data, 0, sizeof(d_send_data));
+	memset(d_recv_data, 0, sizeof(d_recv_data));
+	memset(d_state_data, 0, sizeof(d_state_data));
+	memset(d_spin_data, 0, sizeof(d_spin_data));
+	h_send_data = NULL;
+	h_recv_data = NULL;
+	h_state_data = NULL;
+}
+
+bool safetynet = false;
+
+static void catch_exit(int catch_exit)
+{
+	if(GPU_USING && USE_ENGINE_LOCKS && safetynet)
+	{
+		safetynet = false;
+		for(int i = 0; i < GPU_PARTITION_SIZE; ++i)
+		{
+			int which = GPU_PARTITION*GPU_PARTITION_SIZE + i;
+			set_cur_gpu(which);
+
+//			cudaDeviceReset();
+
+			// try to unlock everything.  litmus will prevent bogus calls.
+			if(USE_ENGINE_LOCKS)
+			{
+				litmus_unlock(EE_LOCKS[which]);
+				litmus_unlock(CE_SEND_LOCKS[which]);
+				if (NUM_COPY_ENGINES == 2)
+				{
+					if (RESERVED_MIGR_COPY_ENGINE)
+						litmus_unlock(CE_MIGR_SEND_LOCKS[which]);
+					else
+						litmus_unlock(CE_MIGR_RECV_LOCKS[which]);
+				}
+			}
+		}
+		litmus_unlock(TOKEN_LOCK);
+	}
+}
+
+
+__global__ void docudaspin(float* data, /*unsigned int* iterations,*/ unsigned int num_elem, unsigned int cycles)
+{
+	long long int now = clock64();
+	long long unsigned int elapsed = 0;
+	long long int last;
+
+//	unsigned int iter = 0;
+	unsigned int i = blockDim.x * blockIdx.x + threadIdx.x;
+	unsigned int j = 0;
+	bool toggle = true;
+
+//	iterations[i] = 0;
+	do
+	{
+		data[i*num_elem+j] += (toggle) ? M_PI : -M_PI;
+		j = (j + 1 != num_elem) ? j + 1 : 0;
+		toggle = !toggle;
+//		iter++;
+
+		last = now;
+		now = clock64();
+
+//		// exact calculation takes more cycles than a second
+//		// loop iteration when code is compiled optimized
+//		long long int diff = now - last;
+//		elapsed += (diff > 0) ?
+//			diff :
+//			now + ((~((long long int)0)<<1)>>1) - last;
+
+		// don't count iterations with clock roll-over
+		elapsed += max(0ll, now - last);
+	}while(elapsed < cycles);
+
+//	iterations[i] = iter;
+
+	return;
+}
+
+
+int next_gpu = -1;
+static bool ee_locked = false;
+static bool early_exit = false;
+static bool have_token = false;
+
+static void gpu_loop_for(double gpu_sec_time, unsigned int num_kernels, double emergency_exit)
+{
+//	int next_gpu;
+	next_gpu = -1;
+	ee_locked = false;
+	early_exit = false;
+	have_token = false;
+
+	if (gpu_sec_time <= 0.0)
+		goto out;
+	if (emergency_exit && wctime() > emergency_exit)
+		goto out;
+
+	LITMUS_TRY
+	{
+		gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		next_gpu = litmus_lock(TOKEN_LOCK);
+		inject_action(TOKEN_START);
+		have_token = true;
+		__sync_synchronize();
+		MigrateIfNeeded(next_gpu);
+		gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+
+		unsigned int numcycles = ((unsigned int)(cur_hz() * gpu_sec_time))/num_kernels;
+
+		if(SEND_SIZE > 0) {
+			chunkMemcpy(this_gpu(d_state_data), h_send_data, SEND_SIZE,
+						cudaMemcpyHostToDevice, cur_gpu(), useEngineLocks());
+		}
+
+		for(unsigned int i = 0; i < num_kernels; ++i)
+		{
+			gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+
+			if(useEngineLocks() && !ee_locked) {
+				litmus_lock(cur_ee());
+				inject_action(EE_START);
+				ee_locked = true;
+				__sync_synchronize();
+			}
+			/* one block per sm, one warp per block */
+			docudaspin <<<cur_sms(), cur_warp_size(), 0, cur_stream()>>> (d_spin_data[cur_gpu()], cur_elem_per_thread(), numcycles);
+			if(useEngineLocks() &&
+				(i == num_kernels - 1 || /* last kernel */
+				 !YIELD_LOCKS         || /* always yeild */
+				 (YIELD_LOCKS && litmus_should_yield_lock(cur_ee())) /* we should yield */
+				)
+			   ) {
+				cudaEventRecord(cur_event(), cur_stream());
+				cudaEventSynchronize(cur_event());
+				inject_action(EE_END);
+				litmus_unlock(cur_ee());
+				ee_locked = false;
+				__sync_synchronize();
+			}
+
+			gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		}
+
+		if(RECV_SIZE > 0) {
+			chunkMemcpy(h_recv_data, this_gpu(d_state_data), RECV_SIZE,
+						cudaMemcpyDeviceToHost, cur_gpu(), useEngineLocks());
+		}
+
+		if (MIGRATE_VIA_SYSMEM) {
+			gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+			PullState();
+			gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		}
+
+		gpuspin_block_litmus_signals(ALL_LITMUS_SIG_MASKS);
+		inject_action(TOKEN_END);
+		litmus_unlock(TOKEN_LOCK);
+		last_gpu() = cur_gpu();
+		have_token = false;
+		__sync_synchronize();
+		gpuspin_unblock_litmus_signals(ALL_LITMUS_SIG_MASKS);
+	}
+	LITMUS_CATCH(SIG_BUDGET)
+	{
+		if (have_token)
+		{
+			cudaEventRecord(cur_event(), cur_stream());
+			cudaEventSynchronize(cur_event());
+
+			if (useEngineLocks()) {
+				if (ee_locked) {
+					litmus_unlock(cur_ee());
+				}
+
+				/* we don't know which CEs might be locked... unlock them all. */
+				if (NUM_COPY_ENGINES == 1) {
+					litmus_unlock(cur_send());
+				}
+				else if (RESERVED_MIGR_COPY_ENGINE) {
+					litmus_unlock(cur_send());
+					litmus_unlock(cur_migr_send());
+				}
+				else {
+					litmus_unlock(cur_send());
+					litmus_unlock(cur_recv());
+				}
+			}
+
+			litmus_unlock(TOKEN_LOCK);
+			last_gpu() = cur_gpu();
+		}
+
+		early_exit = true;
+	}
+	END_LITMUS_TRY
+
+	if (early_exit)
+		throw std::exception();
+
+out:
+	return;
+}
+
+static void gpu_loop_for_linux(double gpu_sec_time, unsigned int num_kernels, double emergency_exit)
+{
+	int GPU_OFFSET = GPU_PARTITION * GPU_PARTITION_SIZE;
+	gpu_pool *pool = &GPU_LINUX_SEM_POOL[GPU_PARTITION];
+	pthread_mutex_t *mutex = &GPU_LINUX_MUTEX_POOL[GPU_PARTITION];
+
+	int next_gpu;
+
+	if (gpu_sec_time <= 0.0)
+		goto out;
+	if (emergency_exit && wctime() > emergency_exit)
+		goto out;
+
+	next_gpu = pool->get(mutex, ((cur_gpu() != -1) ?
+					 	cur_gpu() - GPU_OFFSET :
+						-1))
+				+ GPU_OFFSET;
+	{
+		MigrateIfNeeded(next_gpu);
+
+		unsigned int numcycles = ((unsigned int)(cur_hz() * gpu_sec_time))/num_kernels;
+
+		if(SEND_SIZE > 0)
+			chunkMemcpy(this_gpu(d_state_data), h_send_data, SEND_SIZE,
+						cudaMemcpyHostToDevice, cur_gpu(), useEngineLocks());
+
+		for(unsigned int i = 0; i < num_kernels; ++i)
+		{
+			/* one block per sm, one warp per block */
+			docudaspin <<<cur_sms(),cur_warp_size(), 0, cur_stream()>>> (d_spin_data[cur_gpu()], cur_elem_per_thread(), numcycles);
+			cudaEventRecord(cur_event(), cur_stream());
+			cudaEventSynchronize(cur_event());
+		}
+
+		if(RECV_SIZE > 0)
+			chunkMemcpy(h_recv_data, this_gpu(d_state_data), RECV_SIZE,
+						cudaMemcpyDeviceToHost, cur_gpu(), useEngineLocks());
+
+		if (MIGRATE_VIA_SYSMEM)
+			PullState();
+	}
+	pool->put(mutex, cur_gpu() - GPU_OFFSET);
+
+	last_gpu() = cur_gpu();
+
+out:
+	return;
+}
+
+
+
+
+static void usage(char *error) {
+	fprintf(stderr, "Error: %s\n", error);
+	fprintf(stderr,
+		"Usage:\n"
+		"	rt_spin [COMMON-OPTS] WCET PERIOD DURATION\n"
+		"	rt_spin [COMMON-OPTS] -f FILE [-o COLUMN] WCET PERIOD\n"
+		"	rt_spin -l\n"
+		"\n"
+		"COMMON-OPTS = [-w] [-s SCALE]\n"
+		"              [-p PARTITION/CLUSTER [-z CLUSTER SIZE]] [-c CLASS]\n"
+		"              [-X LOCKING-PROTOCOL] [-L CRITICAL SECTION LENGTH] [-Q RESOURCE-ID]"
+		"\n"
+		"WCET and PERIOD are milliseconds, DURATION is seconds.\n"
+		"CRITICAL SECTION LENGTH is in milliseconds.\n");
+	exit(EXIT_FAILURE);
+}
+
+///*
+// * returns the character that made processing stop, newline or EOF
+// */
+//static int skip_to_next_line(FILE *fstream)
+//{
+//	int ch;
+//	for (ch = fgetc(fstream); ch != EOF && ch != '\n'; ch = fgetc(fstream));
+//	return ch;
+//}
+//
+//static void skip_comments(FILE *fstream)
+//{
+//	int ch;
+//	for (ch = fgetc(fstream); ch == '#'; ch = fgetc(fstream))
+//		skip_to_next_line(fstream);
+//	ungetc(ch, fstream);
+//}
+//
+//static void get_exec_times(const char *file, const int column,
+//			   int *num_jobs,    double **exec_times)
+//{
+//	FILE *fstream;
+//	int  cur_job, cur_col, ch;
+//	*num_jobs = 0;
+//
+//	fstream = fopen(file, "r");
+//	if (!fstream)
+//		bail_out("could not open execution time file");
+//
+//	/* figure out the number of jobs */
+//	do {
+//		skip_comments(fstream);
+//		ch = skip_to_next_line(fstream);
+//		if (ch != EOF)
+//			++(*num_jobs);
+//	} while (ch != EOF);
+//
+//	if (-1 == fseek(fstream, 0L, SEEK_SET))
+//		bail_out("rewinding file failed");
+//
+//	/* allocate space for exec times */
+//	*exec_times = (double*)calloc(*num_jobs, sizeof(*exec_times));
+//	if (!*exec_times)
+//		bail_out("couldn't allocate memory");
+//
+//	for (cur_job = 0; cur_job < *num_jobs && !feof(fstream); ++cur_job) {
+//
+//		skip_comments(fstream);
+//
+//		for (cur_col = 1; cur_col < column; ++cur_col) {
+//			/* discard input until we get to the column we want */
+//			int unused __attribute__ ((unused)) = fscanf(fstream, "%*s,");
+//		}
+//
+//		/* get the desired exec. time */
+//		if (1 != fscanf(fstream, "%lf", (*exec_times)+cur_job)) {
+//			fprintf(stderr, "invalid execution time near line %d\n",
+//					cur_job);
+//			exit(EXIT_FAILURE);
+//		}
+//
+//		skip_to_next_line(fstream);
+//	}
+//
+//	assert(cur_job == *num_jobs);
+//	fclose(fstream);
+//}
+
+#define NUMS 4096
+static int num[NUMS];
+__attribute__((unused)) static char* progname;
+
+static int loop_once(void)
+{
+	int i, j = 0;
+	for (i = 0; i < NUMS; i++)
+		j += num[i]++;
+	return j;
+}
+
+static int loop_for(double exec_time, double emergency_exit)
+{
+	int tmp = 0;
+	double last_loop, loop_start;
+	double start, now;
+
+	if (exec_time <= 0.0)
+		goto out;
+
+	start = cputime();
+	now = cputime();
+
+	if (emergency_exit && wctime() > emergency_exit)
+		goto out;
+
+	last_loop = 0;
+	while (now + last_loop < start + exec_time) {
+		loop_start = now;
+		tmp += loop_once();
+		now = cputime();
+		last_loop = now - loop_start;
+		if (emergency_exit && wctime() > emergency_exit) {
+			/* Oops --- this should only be possible if the execution time tracking
+			 * is broken in the LITMUS^RT kernel. */
+			fprintf(stderr, "!!! gpuspin/%d emergency exit!\n", getpid());
+			fprintf(stderr, "Something is seriously wrong! Do not ignore this.\n");
+			break;
+		}
+	}
+
+out:
+	return tmp;
+}
+
+
+//static void debug_delay_loop(void)
+//{
+//	double start, end, delay;
+//
+//	while (1) {
+//		for (delay = 0.5; delay > 0.01; delay -= 0.01) {
+//			start = wctime();
+//			loop_for(delay, 0);
+//			end = wctime();
+//			printf("%6.4fs: looped for %10.8fs, delta=%11.8fs, error=%7.4f%%\n",
+//			       delay,
+//			       end - start,
+//			       end - start - delay,
+//			       100 * (end - start - delay) / delay);
+//		}
+//	}
+//}
+
+typedef bool (*gpu_job_t)(double exec_time, double gpu_exec_time, unsigned int num_kernels, double program_end);
+typedef bool (*cpu_job_t)(double exec_time, double program_end);
+
+static bool gpu_job(double exec_time, double gpu_exec_time, unsigned int num_kernels, double program_end)
+{
+	double chunk1, chunk2;
+
+	if (wctime() > program_end) {
+		return false;
+	}
+	else {
+		chunk1 = exec_time * drand48();
+		chunk2 = exec_time - chunk1;
+
+		LITMUS_TRY
+		{
+			try
+			{
+				loop_for(chunk1, program_end + 1);
+				gpu_loop_for(gpu_exec_time, num_kernels, program_end + 1);
+				loop_for(chunk2, program_end + 1);
+			}
+			catch(std::exception& e)
+			{
+				xprintf("%d: ran out of time while using GPU\n", gettid());
+			}
+		}
+		LITMUS_CATCH(SIG_BUDGET)
+		{
+			xprintf("%d: ran out of time\n", gettid());
+		}
+		END_LITMUS_TRY
+
+		sleep_next_period();
+	}
+	return true;
+}
+
+static bool job(double exec_time, double program_end)
+{
+	if (wctime() > program_end) {
+		return false;
+	}
+	else {
+		LITMUS_TRY
+		{
+			loop_for(exec_time, program_end + 1);
+		}
+		LITMUS_CATCH(SIG_BUDGET)
+		{
+			xprintf("%d: ran out of time\n", gettid());
+		}
+		END_LITMUS_TRY
+		sleep_next_period();
+	}
+	return true;
+}
+
+/*****************************/
+/* only used for linux modes */
+
+static struct timespec periodTime;
+static struct timespec releaseTime;
+static unsigned int job_no = 0;
+
+static lt_t period_ns;
+
+static void log_release()
+{
+	__attribute__ ((unused)) lt_t rel = releaseTime.tv_sec * s2ns(1) + releaseTime.tv_nsec;
+	__attribute__ ((unused)) lt_t dead = rel + period_ns;
+	trace_release(rel, dead, job_no);
+}
+
+static void log_completion()
+{
+	trace_completion(job_no);
+	++job_no;
+}
+
+static void setup_next_period_linux(struct timespec* spec, struct timespec* period)
+{
+	spec->tv_sec += period->tv_sec;
+	spec->tv_nsec += period->tv_nsec;
+	if (spec->tv_nsec >= s2ns(1)) {
+		++(spec->tv_sec);
+		spec->tv_nsec -= s2ns(1);
+	}
+}
+
+static void sleep_next_period_linux()
+{
+	log_completion();
+	setup_next_period_linux(&releaseTime, &periodTime);
+	clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &releaseTime, NULL);
+	log_release();
+}
+
+static void init_linux()
+{
+	mlockall(MCL_CURRENT | MCL_FUTURE);
+}
+
+static int enable_aux_rt_tasks_linux(pid_t tid)
+{
+	/* pre: caller must already be real time */
+	int ret = 0;
+	struct sched_param param;
+	stringstream pidstr;
+	boost::filesystem::directory_iterator theEnd;
+	boost::filesystem::path proc_dir;
+
+	int policy = sched_getscheduler(tid);
+	if (policy == -1 || policy != SCHED_FIFO) {
+		ret = -1;
+		goto out;
+	}
+
+	ret = sched_getparam(tid, &param);
+	if (ret < 0)
+		goto out;
+
+
+	pidstr<<getpid();
+	proc_dir = boost::filesystem::path("/proc");
+	proc_dir /= pidstr.str();
+	proc_dir /= "task";
+
+	for(boost::filesystem::directory_iterator iter(proc_dir); iter != theEnd; ++iter)
+	{
+		stringstream taskstr(iter->path().leaf().c_str());
+		int child = 0;
+		taskstr>>child;
+		if (child != tid && child != 0)
+		{
+			/* mirror tid's params to others */
+			ret = sched_setscheduler(child, policy, &param);
+			if (ret != 0)
+				goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static int disable_aux_rt_tasks_linux(pid_t tid)
+{
+	int ret = 0;
+	struct sched_param param;
+	stringstream pidstr;
+	boost::filesystem::directory_iterator theEnd;
+	boost::filesystem::path proc_dir;
+
+	memset(&param, 0, sizeof(param));
+
+	pidstr<<getpid();
+	proc_dir = boost::filesystem::path("/proc");
+	proc_dir /= pidstr.str();
+	proc_dir /= "task";
+
+	for(boost::filesystem::directory_iterator iter(proc_dir); iter != theEnd; ++iter)
+	{
+		stringstream taskstr(iter->path().leaf().c_str());
+		int child = 0;
+		taskstr>>child;
+		if (child != tid && child != 0)
+		{
+			/* make all other threads sched_normal */
+			ret = sched_setscheduler(child, SCHED_OTHER, &param);
+			if (ret != 0)
+				goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static int be_migrate_all_to_cluster(int cluster, int cluster_size)
+{
+	int ret = 0;
+	stringstream pidstr;
+
+	pidstr<<getpid();
+	boost::filesystem::path proc_dir("/proc");
+	proc_dir /= pidstr.str();
+	proc_dir /= "task";
+	boost::filesystem::directory_iterator theEnd;
+	for(boost::filesystem::directory_iterator iter(proc_dir); iter != theEnd; ++iter)
+	{
+		stringstream taskstr(iter->path().leaf().c_str());
+		int task = 0;
+		taskstr>>task;
+		if (task != 0) {
+			ret = be_migrate_to_cluster(cluster, cluster_size);
+			if (ret != 0)
+				goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static bool gpu_job_linux(double exec_time, double gpu_exec_time, unsigned int num_kernels, double program_end)
+{
+	double chunk1, chunk2;
+
+	if (wctime() > program_end) {
+		return false;
+	}
+	else {
+		chunk1 = exec_time * drand48();
+		chunk2 = exec_time - chunk1;
+
+		loop_for(chunk1, program_end + 1);
+		gpu_loop_for_linux(gpu_exec_time, num_kernels, program_end + 1);
+		loop_for(chunk2, program_end + 1);
+
+		sleep_next_period_linux();
+	}
+	return true;
+}
+
+static bool job_linux(double exec_time, double program_end)
+{
+	if (wctime() > program_end) {
+		return false;
+	}
+	else {
+		loop_for(exec_time, program_end + 1);
+		sleep_next_period_linux();
+	}
+	return true;
+}
+
+/*****************************/
+
+
+
+
+
+enum eRunMode
+{
+	NORMAL,
+	PROXY,
+	DAEMON,
+};
+
+void set_defaults(struct Args* args)
+{
+	memset(args, 0, sizeof(*args));
+	args->wcet_ms = -1.0;
+	args->gpu_wcet_ms = 0.0;
+	args->period_ms = -1.0;
+	args->budget_ms = -1.0;
+	args->gpusync_mode = IKGLP_MODE;
+	args->sync_mode = BLOCKING;
+	args->gpu_using = false;
+	args->enable_affinity = false;
+	args->enable_chunking = false;
+	args->relax_fifo_len = false;
+	args->use_sysmem_migration = false;
+	args->rho = 2;
+	args->num_ce = 2;
+	args->reserve_migr_ce = false;
+	args->num_kernels = 1;
+	args->engine_lock_type = FIFO;
+	args->yield_locks = false;
+	args->drain_policy = DRAIN_SIMPLE;
+	args->want_enforcement = false;
+	args->want_signals = false;
+	args->priority = LITMUS_LOWEST_PRIORITY;
+	args->cls = RT_CLASS_SOFT;
+	args->scheduler = LITMUS;
+	args->migrate = false;
+	args->cluster = 0;
+	args->cluster_size = 1;
+	args->stddev = 0.0;
+	args->wait = false;
+	args->scale = 1.0;
+	args->duration = 0.0;
+}
+
+void apply_args(struct Args* args)
+{
+	// set all the globals
+	CPU_PARTITION_SIZE = args->cluster_size;
+	GPU_USING = args->gpu_using;
+	GPU_PARTITION = args->gpu_partition;
+	GPU_PARTITION_SIZE = args->gpu_partition_size;
+	RHO = args->rho;
+	NUM_COPY_ENGINES = args->num_ce;
+	RESERVED_MIGR_COPY_ENGINE = args->reserve_migr_ce;
+	USE_ENGINE_LOCKS = args->use_engine_locks;
+	ENGINE_LOCK_TYPE = args->engine_lock_type;
+	YIELD_LOCKS = args->yield_locks;
+	USE_DYNAMIC_GROUP_LOCKS = args->use_dgls;
+	GPU_SYNC_MODE = args->gpusync_mode;
+	ENABLE_AFFINITY = args->enable_affinity;
+	RELAX_FIFO_MAX_LEN = args->relax_fifo_len;
+	CUDA_SYNC_MODE = args->sync_mode;
+	SEND_SIZE = args->send_size;
+	RECV_SIZE = args->recv_size;
+	STATE_SIZE = args->state_size;
+	ENABLE_CHUNKING = args->enable_chunking;
+	CHUNK_SIZE = args->chunk_size;
+	MIGRATE_VIA_SYSMEM = args->use_sysmem_migration;
+
+	if (args->scheduler == LITMUS && !ENABLE_AFFINITY)
+		TRACE_MIGRATIONS = true;
+	else if (args->scheduler == LITMUS)
+		TRACE_MIGRATIONS = false;
+	else if (args->scheduler != LITMUS)
+		TRACE_MIGRATIONS = true;
+
+	WANT_SIGNALS = args->want_signals;
+
+	// roll back other globals to an initial state
+	CUR_DEVICE = -1;
+	LAST_DEVICE = -1;
+}
+
+int __do_normal(struct Args* args)
+{
+	int ret = 0;
+	struct rt_task param;
+
+	lt_t wcet;
+	lt_t period;
+	lt_t budget;
+
+	Normal<double> *wcet_dist_ms = NULL;
+
+	cpu_job_t cjobfn = NULL;
+	gpu_job_t gjobfn = NULL;
+
+	double start = 0;
+
+	if (MIGRATE_VIA_SYSMEM && GPU_PARTITION_SIZE == 1)
+		return -1;
+
+	// turn off some features to be safe
+	if (args->scheduler != LITMUS)
+	{
+		RHO = 0;
+		USE_ENGINE_LOCKS = false;
+		USE_DYNAMIC_GROUP_LOCKS = false;
+		RELAX_FIFO_MAX_LEN = false;
+		ENABLE_RT_AUX_THREADS = false;
+		args->want_enforcement = false;
+		args->want_signals = false;
+
+		cjobfn = job_linux;
+		gjobfn = gpu_job_linux;
+	}
+	else
+	{
+		cjobfn = job;
+		gjobfn = gpu_job;
+	}
+
+	wcet   = ms2ns(args->wcet_ms);
+	period = ms2ns(args->period_ms);
+
+	if (wcet <= 0) {
+		fprintf(stderr, "The worst-case execution time must be a positive number.\n");
+		ret = -1;
+		goto out;
+	}
+	if (period <= 0) {
+		fprintf(stderr, "The period must be a positive number.\n");
+		ret = -1;
+		goto out;
+	}
+	if (wcet > period) {
+		fprintf(stderr, "The worst-case execution time must not exceed the period.\n");
+		ret = -1;
+		goto out;
+	}
+	if (args->gpu_using && args->gpu_wcet_ms <= 0) {
+		fprintf(stderr, "The worst-case gpu execution time must be a positive number.\n");
+		ret = -1;
+		goto out;
+	}
+
+	if (args->budget_ms > 0.0)
+		budget = ms2ns(args->budget_ms);
+	else
+		budget = wcet;
+
+	// randomize execution time according to a normal distribution
+	// centered around the desired execution time.
+	// standard deviation is a percentage of this average
+	wcet_dist_ms = new Normal<double>(args->wcet_ms + args->gpu_wcet_ms, (args->wcet_ms + args->gpu_wcet_ms) * args->stddev);
+	wcet_dist_ms->seed((unsigned int)time(0));
+
+	ret = be_migrate_all_to_cluster(args->cluster, args->cluster_size);
+	if (ret < 0) {
+		fprintf(stderr, "could not migrate to target partition or cluster.\n");
+		goto out;
+	}
+
+	if (args->scheduler != LITMUS)
+	{
+		// set some variables needed by linux modes
+		if (args->gpu_using)
+			TRACE_MIGRATIONS = true;
+		periodTime.tv_sec = period / s2ns(1);
+		periodTime.tv_nsec = period - periodTime.tv_sec * s2ns(1);
+		period_ns = period;
+		job_no = 0;
+	}
+
+
+	ignore_litmus_signals(SIG_BUDGET_MASK);
+
+	init_rt_task_param(&param);
+	param.exec_cost = budget;
+	param.period = period;
+	param.priority = args->priority;
+	param.cls = args->cls;
+	param.budget_policy = (args->want_enforcement) ?
+		PRECISE_ENFORCEMENT : NO_ENFORCEMENT;
+	param.budget_signal_policy = (args->want_signals) ?
+		PRECISE_SIGNALS : NO_SIGNALS;
+	param.drain_policy = args->drain_policy;
+	param.drain_policy = args->drain_policy;
+	param.release_policy = PERIODIC;
+	param.cpu = cluster_to_first_cpu(args->cluster, args->cluster_size);
+
+	ret = set_rt_task_param(gettid(), &param);
+	if (ret < 0) {
+		bail_out("could not setup rt task params\n");
+		goto out;
+	}
+
+	if (args->gpu_using)
+		allocate_locks(args->num_gpu_tasks, args->scheduler != LITMUS);
+
+	if (args->scheduler == LITMUS)
+	{
+		ret = task_mode(LITMUS_RT_TASK);
+		if (ret < 0) {
+			fprintf(stderr, "could not become RT task\n");
+			goto out;
+		}
+	}
+	else
+	{
+		if (args->scheduler == RT_LINUX)
+		{
+			struct sched_param fifoparams;
+			memset(&fifoparams, 0, sizeof(fifoparams));
+			fifoparams.sched_priority = args->priority;
+			ret = sched_setscheduler(getpid(), SCHED_FIFO, &fifoparams);
+			if (ret < 0) {
+				fprintf(stderr, "could not become sched_fifo task\n");
+				goto out;
+			}
+		}
+		trace_name();
+		trace_param();
+	}
+
+	if (args->wait) {
+		xprintf("%d: waiting for release.\n", getpid());
+		ret = wait_for_ts_release2(&releaseTime);
+		if (ret != 0) {
+			printf("wait_for_ts_release2()\n");
+			goto out;
+		}
+
+		if (args->scheduler != LITMUS)
+			log_release();
+	}
+	else if (args->scheduler != LITMUS)
+	{
+		clock_gettime(CLOCK_MONOTONIC, &releaseTime);
+		sleep_next_period_linux();
+	}
+
+	if (args->gpu_using && ENABLE_RT_AUX_THREADS) {
+		if (args->scheduler == LITMUS) {
+			ret = enable_aux_rt_tasks(AUX_CURRENT | AUX_FUTURE);
+			if (ret != 0) {
+				fprintf(stderr, "enable_aux_rt_tasks() failed\n");
+				goto out;
+			}
+		}
+		else if (args->scheduler == RT_LINUX) {
+			ret = enable_aux_rt_tasks_linux(gettid());
+			if (ret != 0) {
+				fprintf(stderr, "enable_aux_rt_tasks_linux() failed\n");
+				goto out;
+			}
+		}
+	}
+
+	start = wctime();
+
+	if (args->want_signals) {
+		ignore_litmus_signals(SIG_BUDGET_MASK); /* flush signals? */
+		activate_litmus_signals(SIG_BUDGET_MASK, longjmp_on_litmus_signal);
+	}
+
+	if (!args->gpu_using) {
+		bool keepgoing;
+		do
+		{
+			double job_ms = wcet_dist_ms->random();
+			if (args->is_aberrant) {
+				double roll = drand48();
+				if (roll <= args->aberrant_prob)
+					job_ms *= args->aberrant_factor;
+			}
+
+			if (job_ms < 0.0)
+				job_ms = 0.0;
+			keepgoing = cjobfn(ms2s(job_ms * args->scale), start + args->duration);
+		}while(keepgoing);
+	}
+	else {
+		bool keepgoing;
+		do
+		{
+			double job_ms = wcet_dist_ms->random();
+
+			if (args->is_aberrant) {
+				double roll = drand48();
+				if (roll <= args->aberrant_prob)
+					job_ms *= args->aberrant_factor;
+			}
+
+			if (job_ms < 0.0)
+				job_ms = 0.0;
+
+			double cpu_job_ms = (job_ms/(args->wcet_ms + args->gpu_wcet_ms))*args->wcet_ms;
+			double gpu_job_ms = (job_ms/(args->wcet_ms + args->gpu_wcet_ms))*args->gpu_wcet_ms;
+			keepgoing = gjobfn(
+							   ms2s(cpu_job_ms * args->scale),
+							   ms2s(gpu_job_ms * args->scale),
+							   args->num_kernels,
+							   start + args->duration);
+		}while(keepgoing);
+	}
+
+	ignore_litmus_signals(SIG_BUDGET_MASK);
+
+	if (args->gpu_using && ENABLE_RT_AUX_THREADS) {
+		if (args->scheduler == LITMUS) {
+			ret = disable_aux_rt_tasks(AUX_CURRENT | AUX_FUTURE);
+			if (ret != 0) {
+				fprintf(stderr, "disable_aux_rt_tasks() failed\n");
+				goto out;
+			}
+		}
+		else if(args->scheduler == RT_LINUX) {
+			ret = disable_aux_rt_tasks_linux(gettid());
+			if (ret != 0) {
+				fprintf(stderr, "disable_aux_rt_tasks_linux() failed\n");
+				goto out;
+			}
+		}
+	}
+
+	if (args->gpu_using)
+		deallocate_locks(args->num_gpu_tasks, args->scheduler != LITMUS);
+
+	if (args->scheduler == LITMUS)
+	{
+		ret = task_mode(BACKGROUND_TASK);
+		if (ret != 0) {
+			fprintf(stderr, "could not become regular task (huh?)\n");
+			goto out;
+		}
+	}
+
+	{
+		// become a normal task just in case.
+		struct sched_param normalparams;
+		memset(&normalparams, 0, sizeof(normalparams));
+		ret = sched_setscheduler(getpid(), SCHED_OTHER, &normalparams);
+		if (ret < 0) {
+			fprintf(stderr, "could not become sched_normal task\n");
+			goto out;
+		}
+	}
+
+out:
+	if (wcet_dist_ms)
+		delete wcet_dist_ms;
+
+	return ret;
+}
+
+int do_normal(struct Args* args)
+{
+	int ret = 0;
+
+	apply_args(args);
+
+	if (args->scheduler == LITMUS)
+		init_litmus();
+	else
+		init_linux();
+
+	if (args->gpu_using) {
+#if 0
+		signal(SIGABRT, catch_exit);
+		signal(SIGTERM, catch_exit);
+		signal(SIGQUIT, catch_exit);
+		signal(SIGSEGV, catch_exit);
+#endif
+
+		cudaSetDeviceFlags(cudaDeviceScheduleSpin);
+		init_cuda(args->num_gpu_tasks);
+		init_events();
+		safetynet = true;
+	}
+
+	ret = __do_normal(args);
+
+	if (args->gpu_using) {
+		safetynet = false;
+		exit_cuda();
+	}
+
+	return ret;
+}
+
+typedef struct run_entry
+{
+	struct Args args;
+	int used;
+	int ret;
+} run_entry_t;
+
+
+
+static int *num_run_entries = NULL;
+static run_entry_t *run_entries = NULL;
+static pthread_barrier_t *daemon_barrier = NULL;
+static pthread_mutex_t *daemon_mutex = NULL;
+
+static run_entry_t *my_run_entry = NULL;
+static managed_shared_memory *daemon_segment_ptr = NULL;
+
+int init_daemon(struct Args* args, int num_total_users, bool is_daemon)
+{
+	if (num_total_users)
+	{
+		shared_memory_object::remove("gpuspin_daemon_memory");
+
+		daemon_segment_ptr = new managed_shared_memory(create_only, "gpuspin_daemon_memory", 30*PAGE_SIZE);
+		num_run_entries = daemon_segment_ptr->construct<int>("int num_run_entries")();
+		*num_run_entries = num_total_users;
+
+		run_entries = daemon_segment_ptr->construct<struct run_entry>("run_entry_t run_entries")[num_total_users]();
+		memset(run_entries, 0, sizeof(run_entry_t)*num_total_users);
+
+		daemon_mutex = daemon_segment_ptr->construct<pthread_mutex_t>("pthread_mutex_t daemon_mutex")();
+		pthread_mutexattr_t attr;
+		pthread_mutexattr_init(&attr);
+		pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+		pthread_mutex_init(daemon_mutex, &attr);
+		pthread_mutexattr_destroy(&attr);
+
+		daemon_barrier = daemon_segment_ptr->construct<pthread_barrier_t>("pthread_barrier_t daemon_barrier")();
+		pthread_barrierattr_t battr;
+		pthread_barrierattr_init(&battr);
+		pthread_barrierattr_setpshared(&battr, PTHREAD_PROCESS_SHARED);
+		pthread_barrier_init(daemon_barrier, &battr, args->num_tasks*2);
+		pthread_barrierattr_destroy(&battr);
+	}
+	else
+	{
+		do
+		{
+			try
+			{
+				if (!daemon_segment_ptr) daemon_segment_ptr = new managed_shared_memory(open_only, "gpuspin_daemon_memory");
+			}
+			catch(...)
+			{
+				sleep(1);
+			}
+		}while(daemon_segment_ptr == NULL);
+
+		num_run_entries = daemon_segment_ptr->find<int>("int num_run_entries").first;
+		run_entries = daemon_segment_ptr->find<struct run_entry>("run_entry_t run_entries").first;
+		daemon_mutex = daemon_segment_ptr->find<pthread_mutex_t>("pthread_mutex_t daemon_mutex").first;
+		daemon_barrier = daemon_segment_ptr->find<pthread_barrier_t>("pthread_barrier_t daemon_barrier").first;
+	}
+
+	if (is_daemon)
+	{
+		// find and claim an entry
+		pthread_mutex_lock(daemon_mutex);
+		for(int i = 0; i < *num_run_entries; ++i)
+		{
+			if(!run_entries[i].used)
+			{
+				my_run_entry = &run_entries[i];
+				my_run_entry->used = 1;
+				break;
+			}
+		}
+		pthread_mutex_unlock(daemon_mutex);
+
+		assert(my_run_entry);
+		my_run_entry->args = *args;
+		my_run_entry->ret = 0;
+	}
+	else
+	{
+		// find my entry
+		pthread_mutex_lock(daemon_mutex);
+		for(int i = 0; i < *num_run_entries; ++i)
+		{
+			if (run_entries[i].args.wcet_ms == args->wcet_ms &&
+				run_entries[i].args.gpu_wcet_ms == args->gpu_wcet_ms &&
+				run_entries[i].args.period_ms == args->period_ms)
+			{
+				my_run_entry = &run_entries[i];
+				break;
+			}
+		}
+		pthread_mutex_unlock(daemon_mutex);
+	}
+
+	if (!my_run_entry) {
+		fprintf(stderr, "Could not find task <wcet, gpu_wcet, period>: <%f %f %f>\n", args->wcet_ms, args->gpu_wcet_ms, args->period_ms);
+		return -1;
+	}
+	return 0;
+}
+
+int put_next_run(struct Args* args)
+{
+	assert(my_run_entry);
+
+	pthread_mutex_lock(daemon_mutex);
+	my_run_entry->args = *args;
+	pthread_mutex_unlock(daemon_mutex);
+
+	pthread_barrier_wait(daemon_barrier);
+
+	return 0;
+}
+
+int get_next_run(struct Args* args)
+{
+	assert(my_run_entry);
+
+	pthread_barrier_wait(daemon_barrier);
+
+	pthread_mutex_lock(daemon_mutex);
+	*args = my_run_entry->args;
+	my_run_entry->ret = 0;
+	pthread_mutex_unlock(daemon_mutex);
+
+	return 0;
+}
+
+int complete_run(int ret)
+{
+	assert(my_run_entry);
+
+	pthread_mutex_lock(daemon_mutex);
+	my_run_entry->ret = ret;
+	pthread_mutex_unlock(daemon_mutex);
+
+	pthread_barrier_wait(daemon_barrier);
+
+	return 0;
+}
+
+int wait_completion()
+{
+	int ret = 0;
+
+	assert(my_run_entry);
+
+	pthread_barrier_wait(daemon_barrier);
+
+	pthread_mutex_lock(daemon_mutex);
+	ret = my_run_entry->ret;
+	pthread_mutex_unlock(daemon_mutex);
+
+	return ret;
+}
+
+
+
+
+int do_proxy(struct Args* args)
+{
+	int ret = 0;
+	ret = init_daemon(args, 0, false);
+	if (ret < 0)
+		goto out;
+	put_next_run(args);
+	ret = wait_completion();
+
+out:
+	return ret;
+}
+
+static bool is_daemon = false;
+static bool running = false;
+static void catch_exit2(int signal)
+{
+	if (is_daemon && running)
+		complete_run(-signal);
+	catch_exit(signal);
+}
+
+int do_daemon(struct Args* args)
+{
+	is_daemon = true;
+
+	int ret = 0;
+	struct Args nextargs;
+
+	signal(SIGFPE, catch_exit2);
+	signal(SIGABRT, catch_exit2);
+	signal(SIGTERM, catch_exit2);
+	signal(SIGQUIT, catch_exit2);
+	signal(SIGSEGV, catch_exit2);
+
+	init_daemon(args, args->num_tasks, true);
+
+	apply_args(args);
+	init_litmus(); /* does everything init_linux() does, plus litmus stuff */
+
+	if (args->gpu_using) {
+		cudaSetDeviceFlags(cudaDeviceScheduleSpin);
+		init_cuda(args->num_gpu_tasks);
+		init_events();
+		safetynet = true;
+	}
+
+	do {
+		bool sync_change = false;
+		bool gpu_part_change = false;
+		bool gpu_part_size_change = false;
+
+		xprintf("%d: waiting for work\n", getpid());
+
+		get_next_run(&nextargs);
+
+		if (nextargs.gpu_using) {
+			xprintf("%d: gpu using! gpu partition = %d, gwcet = %f, send = %lu\n",
+							getpid(),
+							nextargs.gpu_partition,
+							nextargs.gpu_wcet_ms,
+							nextargs.send_size);
+		}
+
+		running = true;
+		sync_change = args->gpu_using && (CUDA_SYNC_MODE != nextargs.sync_mode);
+		gpu_part_change = args->gpu_using && (GPU_PARTITION != nextargs.gpu_partition);
+		gpu_part_size_change = args->gpu_using && (GPU_PARTITION_SIZE != nextargs.gpu_partition_size);
+
+		if (sync_change || gpu_part_change || gpu_part_size_change) {
+			destroy_events();
+			if (gpu_part_change || gpu_part_size_change)
+				exit_cuda();
+		}
+		apply_args(&nextargs);
+		if (sync_change || gpu_part_change || gpu_part_size_change) {
+			if (gpu_part_change || gpu_part_size_change) {
+				xprintf("%d: changing device configuration\n", getpid());
+				init_cuda(nextargs.num_gpu_tasks);
+				CUR_DEVICE = -1;
+				LAST_DEVICE = -1;
+			}
+			init_events();
+		}
+
+		xprintf("%d: starting run\n", getpid());
+
+		ret = __do_normal(&nextargs);
+		complete_run(ret);
+		running = false;
+	}while(ret == 0);
+
+	if (args->gpu_using) {
+		safetynet = false;
+		exit_cuda();
+	}
+
+	if (args->num_gpu_tasks)
+		shared_memory_object::remove("gpu_mutex_memory");
+
+	if (args->num_tasks)
+		shared_memory_object::remove("gpuspin_daemon_memory");
+
+	return ret;
+}
+
+#define CPU_OPTIONS "p:z:c:wlveio:f:s:q:X:L:Q:d:"
+#define GPU_OPTIONS "g:y:r:C:E:DG:xS:R:T:Z:aFm:b:MNIk:VW:uU:O:"
+#define PROXY_OPTIONS "B:PA"
+
+// concat the option strings
+#define OPTSTR CPU_OPTIONS GPU_OPTIONS PROXY_OPTIONS
+
+int main(int argc, char** argv)
+{
+	struct Args myArgs;
+	set_defaults(&myArgs);
+
+	eRunMode run_mode = NORMAL;
+
+	int opt;
+	progname = argv[0];
+
+	while ((opt = getopt(argc, argv, OPTSTR)) != -1) {
+		switch (opt) {
+		case 'B':
+			myArgs.num_tasks = atoi(optarg);
+			break;
+		case 'P':
+			run_mode = PROXY;
+			break;
+		case 'A':
+			run_mode = DAEMON;
+			break;
+		case 'U':
+			myArgs.is_aberrant = true;
+			myArgs.aberrant_prob = (double)atoi(optarg);
+			break;
+		case 'O':
+			myArgs.is_aberrant = true;
+			myArgs.aberrant_factor = atof(optarg);
+			break;
+
+
+		case 'w':
+			myArgs.wait = true;
+			break;
+		case 'p':
+			myArgs.cluster = atoi(optarg);
+			myArgs.migrate = true;
+			break;
+		case 'z':
+//			CPU_PARTITION_SIZE = cluster_size;
+			myArgs.cluster_size = atoi(optarg);
+			break;
+		case 'g':
+//			GPU_USING = true;
+//			GPU_PARTITION = atoi(optarg);
+			myArgs.gpu_using = true;
+			myArgs.gpu_partition = atoi(optarg);
+//			assert(GPU_PARTITION >= 0 && GPU_PARTITION < NR_GPUS);
+			break;
+		case 'y':
+//			GPU_PARTITION_SIZE = atoi(optarg);
+			myArgs.gpu_partition_size = atoi(optarg);
+//			assert(GPU_PARTITION_SIZE > 0);
+			break;
+		case 'r':
+//			RHO = atoi(optarg);
+			myArgs.rho = atoi(optarg);
+//			assert(RHO > 0);
+			break;
+		case 'C':
+//			NUM_COPY_ENGINES = atoi(optarg);
+			myArgs.num_ce = atoi(optarg);
+//			assert(NUM_COPY_ENGINES == 1 || NUM_COPY_ENGINES == 2);
+			break;
+		case 'V':
+//			RESERVED_MIGR_COPY_ENGINE = true;
+			myArgs.reserve_migr_ce = true;
+			break;
+		case 'E':
+//			USE_ENGINE_LOCKS = true;
+//			ENGINE_LOCK_TYPE = (eEngineLockTypes)atoi(optarg);
+			myArgs.use_engine_locks = true;
+			myArgs.engine_lock_type = (eEngineLockTypes)atoi(optarg);
+//			assert(ENGINE_LOCK_TYPE == FIFO || ENGINE_LOCK_TYPE == PRIOQ);
+			break;
+		case 'u':
+			myArgs.yield_locks = true;
+			break;
+		case 'D':
+//			USE_DYNAMIC_GROUP_LOCKS = true;
+			myArgs.use_dgls = true;
+			break;
+		case 'G':
+//			GPU_SYNC_MODE = (eGpuSyncMode)atoi(optarg);
+			myArgs.gpusync_mode = (eGpuSyncMode)atoi(optarg);
+//			assert(GPU_SYNC_MODE >= IKGLP_MODE && GPU_SYNC_MODE <= RGEM_MODE);
+			break;
+		case 'a':
+//			ENABLE_AFFINITY = true;
+			myArgs.enable_affinity = true;
+			break;
+		case 'F':
+//			RELAX_FIFO_MAX_LEN = true;
+			myArgs.relax_fifo_len = true;
+			break;
+		case 'x':
+//			CUDA_SYNC_MODE = SPIN;
+			myArgs.sync_mode = SPIN;
+			break;
+		case 'S':
+//			SEND_SIZE = kbToB((size_t)atoi(optarg));
+			myArgs.send_size = kbToB((size_t)atoi(optarg));
+			break;
+		case 'R':
+//			RECV_SIZE = kbToB((size_t)atoi(optarg));
+			myArgs.recv_size = kbToB((size_t)atoi(optarg));
+			break;
+		case 'T':
+//			STATE_SIZE = kbToB((size_t)atoi(optarg));
+			myArgs.state_size = kbToB((size_t)atoi(optarg));
+			break;
+		case 'Z':
+//			ENABLE_CHUNKING = true;
+//			CHUNK_SIZE = kbToB((size_t)atoi(optarg));
+			myArgs.enable_chunking = true;
+			myArgs.chunk_size = kbToB((size_t)atoi(optarg));
+			break;
+		case 'M':
+//			MIGRATE_VIA_SYSMEM = true;
+			myArgs.use_sysmem_migration = true;
+			break;
+		case 'm':
+//			num_gpu_users = (int)atoi(optarg);
+			myArgs.num_gpu_tasks = (int)atoi(optarg);
+//			assert(num_gpu_users > 0);
+			break;
+		case 'k':
+//			num_kernels = (unsigned int)atoi(optarg);
+			myArgs.num_kernels = (unsigned int)atoi(optarg);
+			break;
+		case 'b':
+//			budget_ms = atoi(optarg);
+			myArgs.budget_ms = atoi(optarg);
+			break;
+		case 'W':
+//			stdpct = (double)atof(optarg);
+			myArgs.stddev = (double)atof(optarg);
+			break;
+		case 'N':
+//			scheduler = LINUX;
+			myArgs.scheduler = LINUX;
+			break;
+		case 'I':
+//			scheduler = RT_LINUX;
+			myArgs.scheduler = RT_LINUX;
+			break;
+		case 'q':
+//			priority = atoi(optarg);
+			myArgs.priority = atoi(optarg);
+			break;
+		case 'c':
+//			cls = str2class(optarg);
+			myArgs.cls = str2class(optarg);
+			break;
+		case 'e':
+//			want_enforcement = true;
+			myArgs.want_enforcement = true;
+			break;
+		case 'i':
+//			want_signals = true;
+			myArgs.want_signals = true;
+			break;
+		case 'd':
+//			drain = (budget_drain_policy_t)atoi(optarg);
+			myArgs.drain_policy = (budget_drain_policy_t)atoi(optarg);
+//			assert(drain >= DRAIN_SIMPLE && drain <= DRAIN_SOBLIV);
+//			assert(drain != DRAIN_SAWARE); // unsupported
+			break;
+//		case 'l':
+//			test_loop = 1;
+//			break;
+//		case 'o':
+////			column = atoi(optarg);
+//			myArgs.column = atoi(optarg);
+//			break;
+//		case 'f':
+//			file = optarg;
+//			break;
+		case 's':
+//			scale = (double)atof(optarg);
+			myArgs.scale = (double)atof(optarg);
+			break;
+//		case 'X':
+//			protocol = lock_protocol_for_name(optarg);
+//			if (protocol < 0)
+//				usage("Unknown locking protocol specified.");
+//			break;
+//		case 'L':
+//			cs_length = atof(optarg);
+//			if (cs_length <= 0)
+//				usage("Invalid critical section length.");
+//			break;
+//		case 'Q':
+//			resource_id = atoi(optarg);
+//			if (resource_id <= 0 && strcmp(optarg, "0"))
+//				usage("Invalid resource ID.");
+//			break;
+		case ':':
+			usage("Argument missing.");
+			break;
+		case '?':
+		default:
+			usage("Bad argument.");
+			break;
+		}
+	}
+
+
+	srand(time(0));
+
+	if (argc - optind == 3) {
+		myArgs.wcet_ms   = atof(argv[optind + 0]);
+		myArgs.period_ms = atof(argv[optind + 1]);
+		myArgs.duration  = atof(argv[optind + 2]);
+	}
+	else if (argc - optind == 4) {
+		myArgs.wcet_ms   = atof(argv[optind + 0]);
+		myArgs.gpu_wcet_ms = atof(argv[optind + 1]);
+		myArgs.period_ms = atof(argv[optind + 2]);
+		myArgs.duration  = atof(argv[optind + 3]);
+	}
+
+	double rate = (1000.0/myArgs.period_ms)*myArgs.aberrant_prob;
+	myArgs.aberrant_prob = 1.0 / rate;
+
+	if (myArgs.num_tasks == 0 || myArgs.num_gpu_tasks == 0) {
+		// safety w.r.t. shared mem.
+		sleep(2);
+	}
+
+	/* make sure children don't take sigmasks */
+	ignore_litmus_signals(ALL_LITMUS_SIG_MASKS);
+
+	if (run_mode == NORMAL) {
+		return do_normal(&myArgs);
+	}
+	else if (run_mode == PROXY) {
+		return do_proxy(&myArgs);
+	}
+	else if (run_mode == DAEMON) {
+		return do_daemon(&myArgs);
+	}
+}
diff --git a/gpu/ikglptest.c b/gpu/ikglptest.c
new file mode 100644
index 0000000..e5fa6fc
--- /dev/null
+++ b/gpu/ikglptest.c
@@ -0,0 +1,653 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <math.h>
+
+/* Include gettid() */
+#include <sys/types.h>
+
+/* Include threading support. */
+#include <pthread.h>
+
+/* Include the LITMUS^RT API.*/
+#include "litmus.h"
+
+/* Catch errors.
+ */
+#if 1
+#define CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "%s failed: %m\n", #exp);\
+		else \
+			fprintf(stderr, "%s ok.\n", #exp); \
+	} while (0)
+
+#define TH_CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "[%d] %s failed: %m\n", ctx->id, #exp); \
+		else \
+			fprintf(stderr, "[%d] %s ok.\n", ctx->id, #exp); \
+	} while (0)
+
+#define TH_SAFE_CALL( exp ) do { \
+		int ret; \
+		fprintf(stderr, "[%d] calling %s...\n", ctx->id, #exp); \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "\t...[%d] %s failed: %m\n", ctx->id, #exp); \
+		else \
+			fprintf(stderr, "\t...[%d] %s ok.\n", ctx->id, #exp); \
+	} while (0)
+#else
+#define CALL( exp )
+#define TH_CALL( exp )
+#define TH_SAFE_CALL( exp )
+#endif
+
+/* these are only default values */
+int NUM_THREADS=3;
+int NUM_AUX_THREADS=0;
+int NUM_SEMS=1;
+int NUM_GPUS=1;
+int GPU_OFFSET=0;
+int NUM_SIMULT_USERS = 1;
+int ENABLE_AFFINITY = 0;
+int NEST_DEPTH=1;
+int USE_KFMLP = 0;
+int RELAX_FIFO_MAX_LEN = 0;
+int USE_DYNAMIC_GROUP_LOCKS = 0;
+
+int SLEEP_BETWEEN_JOBS = 1;
+int USE_PRIOQ = 0;
+
+int gAuxRun = 1;
+pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
+
+#define MAX_SEMS 1000
+
+// 1000 = 1us
+#define EXEC_COST 	1000*1
+#define PERIOD		2*1000*100
+
+/* The information passed to each thread. Could be anything. */
+struct thread_context {
+	int id;
+	int fd;
+	int kexclu;
+	int od[MAX_SEMS];
+	int count;
+	unsigned int rand;
+	int mig_count[5];
+};
+
+void* rt_thread(void* _ctx);
+void* aux_thread(void* _ctx);
+int nested_job(struct thread_context* ctx, int *count, int *next, int runfactor);
+int job(struct thread_context* ctx, int runfactor);
+
+
+struct avg_info
+{
+	float avg;
+	float stdev;
+};
+
+struct avg_info feedback(int _a, int _b)
+{
+	fp_t a = _frac(_a, 10000);
+	fp_t b = _frac(_b, 10000);
+	int i;
+
+	fp_t actual_fp;
+
+	fp_t _est, _err;
+
+	int base = 1000000;
+	//int range = 40;
+
+	fp_t est = _integer_to_fp(base);
+	fp_t err = _fp(base/2);
+
+#define NUM_SAMPLES 10000
+
+	float samples[NUM_SAMPLES] = {0.0};
+	float accu_abs, accu;
+	float avg;
+	float devsum;
+	float stdev;
+	struct avg_info ret;
+
+	for(i = 0; i < NUM_SAMPLES; ++i) {
+		int num = ((rand()%40)*(rand()%2 ? -1 : 1)/100.0)*base + base;
+		float rel_err;
+
+		actual_fp = _integer_to_fp(num);
+
+//	printf("Before: est = %d\terr = %d\n", (int)_fp_to_integer(est), (int)_fp_to_integer(err));
+
+		_err = _sub(actual_fp, est);
+		_est = _add(_mul(a, _err), _mul(b, err));
+
+		rel_err = _fp_to_integer(_mul(_div(_err, est), _integer_to_fp(10000)))/10000.0;
+		rel_err *= 100.0;
+		//printf("%6.2f\n", rel_err);
+		samples[i] = rel_err;
+
+		est = _est;
+		err = _add(err, _err);
+
+		if((int)_fp_to_integer(est) <= 0) {
+			est = actual_fp;
+			err = _div(actual_fp, _integer_to_fp(2));
+		}
+
+	//printf("After: est = %d\terr = %d\n", (int)_fp_to_integer(est), (int)_fp_to_integer(err));
+	}
+
+	accu_abs = 0.0;
+	accu = 0.0;
+	for(i = 0; i < NUM_SAMPLES; ++i) {
+		accu += samples[i];
+		accu_abs += abs(samples[i]);
+	}
+
+	avg = accu_abs/NUM_SAMPLES;
+	devsum = 0;
+	for(i = 0; i < NUM_SAMPLES; ++i) {
+		float dev = samples[i] - avg;
+		dev *= dev;
+		devsum += dev;
+	}
+
+	stdev = sqrtf(devsum/(NUM_SAMPLES-1));
+
+	ret.avg = avg;
+	ret.stdev = stdev;
+
+	//printf("AVG: %6.2f\tw/ neg: %6.2f\n", accu_abs/NUM_SAMPLES, accu/NUM_SAMPLES);
+
+	//return (accu_abs/NUM_SAMPLES);
+	return(ret);
+}
+
+
+
+#define OPTSTR "t:k:o:z:s:d:lfaryA:q"
+
+int main(int argc, char** argv)
+{
+	int i;
+	struct thread_context* ctx = NULL;
+	struct thread_context* aux_ctx = NULL;
+	pthread_t*	     task = NULL;
+	pthread_t*	     aux_task = NULL;
+	struct rt_task	param;
+	int fd;
+
+	int opt;
+	while((opt = getopt(argc, argv, OPTSTR)) != -1) {
+		switch(opt) {
+			case 't':
+				NUM_THREADS = atoi(optarg);
+				break;
+			case 'A':
+				NUM_AUX_THREADS = atoi(optarg);
+				break;
+			case 'k':
+				NUM_GPUS = atoi(optarg);
+				assert(NUM_GPUS > 0);
+				break;
+			case 'z':
+				NUM_SIMULT_USERS = atoi(optarg);
+				assert(NUM_SIMULT_USERS > 0);
+				break;
+			case 'o':
+				GPU_OFFSET = atoi(optarg);
+				assert(GPU_OFFSET >= 0);
+				break;
+			case 's':
+				NUM_SEMS = atoi(optarg);
+				assert(NUM_SEMS >= 0 && NUM_SEMS < MAX_SEMS);
+				break;
+			case 'd':
+				NEST_DEPTH = atoi(optarg);
+				assert(NEST_DEPTH >= 0);
+				break;
+			case 'f':
+				SLEEP_BETWEEN_JOBS = 0;
+				break;
+			case 'a':
+				ENABLE_AFFINITY = 1;
+				break;
+			case 'l':
+				USE_KFMLP = 1;
+				break;
+			case 'y':
+				USE_DYNAMIC_GROUP_LOCKS = 1;
+				break;
+			case 'r':
+				RELAX_FIFO_MAX_LEN = 1;
+				break;
+			case 'q':
+				USE_PRIOQ = 1;
+				break;
+			default:
+				fprintf(stderr, "Unknown option: %c\n", opt);
+				exit(-1);
+				break;
+		}
+	}
+
+#if 0
+	int best_a = 0, best_b = 0;
+	int first = 1;
+	int TRIALS = 15;
+
+	int a, b, t;
+
+	struct avg_info best = {0.0,0.0}, second_best;
+
+	int second_best_a, second_best_b;
+
+	srand(time(0));
+
+	int step = 50;
+
+	for(b = 2000; b < 5000; b += step) {
+		for(a = 1500; a < b; a += (step/4)) {
+			float std_accum = 0;
+			float avg_accum = 0;
+			for(t = 0; t < TRIALS; ++t) {
+				struct avg_info temp;
+				temp = feedback(a, b);
+				std_accum += temp.stdev;
+				avg_accum += temp.avg;
+			}
+
+			float avg_std = std_accum / TRIALS;
+
+			if(first || avg_std < best.stdev) {
+				second_best_a = best_a;
+				second_best_b = best_b;
+				second_best = best;
+
+				best.stdev = avg_std;
+				best.avg = avg_accum / TRIALS;
+				best_a = a;
+				best_b = b;
+
+				first = 0;
+			}
+		}
+	}
+
+	printf("Best:\ta = %d\tb = %d\t(b-a) = %d\tavg = %6.2f\tstdev = %6.2f\n", best_a, best_b, best_b - best_a, best.avg, best.stdev);
+	printf("2nd:\ta = %d\tb = %d\t(b-a) = %d\tavg = %6.2f\tstdev = %6.2f\n", second_best_a, second_best_b, second_best_b - second_best_a, second_best.avg, second_best.stdev);
+
+
+			a = 14008;
+			b = 16024;
+			float std_accum = 0;
+			float avg_accum = 0;
+			for(t = 0; t < TRIALS; ++t) {
+				struct avg_info temp;
+				temp = feedback(a, b);
+				std_accum += temp.stdev;
+				avg_accum += temp.avg;
+			}
+
+	printf("Aaron:\tavg = %6.2f\tstd = %6.2f\n", avg_accum/TRIALS, std_accum/TRIALS);
+
+
+
+
+	return 0;
+#endif
+
+
+
+
+	ctx = (struct thread_context*) calloc(NUM_THREADS, sizeof(struct thread_context));
+	task = (pthread_t*) calloc(NUM_THREADS, sizeof(pthread_t));
+
+	if (NUM_AUX_THREADS) {
+		aux_ctx = (struct thread_context*) calloc(NUM_AUX_THREADS, sizeof(struct thread_context));
+		aux_task = (pthread_t*) calloc(NUM_AUX_THREADS, sizeof(pthread_t));
+	}
+
+	srand(0); /* something repeatable for now */
+
+	fd = open("semaphores", O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
+
+	CALL( init_litmus() );
+
+	for (i = 0; i < NUM_AUX_THREADS; i++) {
+		aux_ctx[i].id = i;
+		CALL( pthread_create(aux_task + i, NULL, aux_thread, ctx + i) );
+	}
+
+	for (i = 0; i < NUM_THREADS; i++) {
+		ctx[i].id = i;
+		ctx[i].fd = fd;
+		ctx[i].rand = rand();
+		memset(&ctx[i].mig_count, 0, sizeof(ctx[i].mig_count));
+		CALL( pthread_create(task + i, NULL, rt_thread, ctx + i) );
+	}
+
+	if (NUM_AUX_THREADS) {
+		init_rt_task_param(&param);
+		param.exec_cost = EXEC_COST;
+		param.period = PERIOD + 10*NUM_THREADS+1;
+		param.cls = RT_CLASS_SOFT;
+
+		TH_CALL( init_rt_thread() );
+		TH_CALL( set_rt_task_param(gettid(), &param) );
+		TH_CALL( task_mode(LITMUS_RT_TASK) );
+
+		printf("[MASTER] Waiting for TS release.\n ");
+		wait_for_ts_release();
+
+		CALL( enable_aux_rt_tasks(AUX_CURRENT) );
+
+		for(i = 0; i < 25000; ++i) {
+			sleep_next_period();
+			pthread_mutex_lock(&gMutex);
+			pthread_mutex_unlock(&gMutex);
+		}
+
+		CALL( disable_aux_rt_tasks(AUX_CURRENT) );
+		__sync_synchronize();
+		gAuxRun = 0;
+		__sync_synchronize();
+
+		for (i = 0; i < NUM_AUX_THREADS; i++)
+			pthread_join(aux_task[i], NULL);
+
+		TH_CALL( task_mode(BACKGROUND_TASK) );
+	}
+
+	for (i = 0; i < NUM_THREADS; i++)
+		pthread_join(task[i], NULL);
+
+	return 0;
+}
+
+int affinity_cost[] = {1, 4, 8, 16};
+
+int affinity_distance(struct thread_context* ctx, int a, int b)
+{
+	int i;
+	int dist;
+
+	if(a >= 0 && b >= 0) {
+		for(i = 0; i <= 3; ++i) {
+			if(a>>i == b>>i) {
+				dist = i;
+				goto out;
+			}
+		}
+		dist = 0; // hopefully never reached.
+	}
+	else {
+		dist = 0;
+	}
+
+out:
+	//printf("[%d]: distance: %d -> %d = %d\n", ctx->id, a, b, dist);
+
+	++(ctx->mig_count[dist]);
+
+	return dist;
+
+//	int groups[] = {2, 4, 8};
+//	int i;
+//
+//	if(a < 0 || b < 0)
+//		return (sizeof(groups)/sizeof(groups[0]));  // worst affinity
+//
+//	// no migration
+//	if(a == b)
+//		return 0;
+//
+//	for(i = 0; i < sizeof(groups)/sizeof(groups[0]); ++i) {
+//		if(a/groups[i] == b/groups[i])
+//			return (i+1);
+//	}
+//	assert(0);
+//	return -1;
+}
+
+
+void* aux_thread(void* _ctx)
+{
+	struct thread_context *ctx = (struct thread_context*)_ctx;
+
+	while (gAuxRun) {
+		pthread_mutex_lock(&gMutex);
+		pthread_mutex_unlock(&gMutex);
+	}
+
+	return ctx;
+}
+
+void* rt_thread(void* _ctx)
+{
+	int i;
+	int do_exit = 0;
+	int last_replica = -1;
+	struct rt_task param;
+
+	struct thread_context *ctx = (struct thread_context*)_ctx;
+
+	init_rt_task_param(&param);
+	param.exec_cost = EXEC_COST;
+	param.period = PERIOD + 10*ctx->id; /* Vary period a little bit. */
+	param.cls = RT_CLASS_SOFT;
+
+	TH_CALL( init_rt_thread() );
+	TH_CALL( set_rt_task_param(gettid(), &param) );
+
+	if(USE_KFMLP) {
+		ctx->kexclu = open_kfmlp_gpu_sem(ctx->fd,
+										 0,  /* name */
+										 NUM_GPUS,
+										 GPU_OFFSET,
+										 NUM_SIMULT_USERS,
+										 ENABLE_AFFINITY
+										 );
+	}
+	else {
+//		ctx->kexclu = open_ikglp_sem(ctx->fd, 0, &NUM_GPUS);
+		ctx->kexclu = open_gpusync_token_lock(ctx->fd,
+								0,  /* name */
+								NUM_GPUS,
+								GPU_OFFSET,
+								NUM_SIMULT_USERS,
+								IKGLP_M_IN_FIFOS,
+								(!RELAX_FIFO_MAX_LEN) ?
+									  IKGLP_OPTIMAL_FIFO_LEN :
+									  IKGLP_UNLIMITED_FIFO_LEN,
+								ENABLE_AFFINITY
+								);
+	}
+	if(ctx->kexclu < 0)
+		perror("open_kexclu_sem");
+	else
+		printf("kexclu od = %d\n", ctx->kexclu);
+
+	for (i = 0; i < NUM_SEMS; ++i) {
+		if(!USE_PRIOQ) {
+			ctx->od[i] = open_fifo_sem(ctx->fd, i + ctx->kexclu + 2);
+			if(ctx->od[i] < 0)
+				perror("open_fifo_sem");
+			else
+				printf("fifo[%d] od = %d\n", i, ctx->od[i]);
+		}
+		else {
+			ctx->od[i] = open_prioq_sem(ctx->fd, i + ctx->kexclu + 2);
+			if(ctx->od[i] < 0)
+				perror("open_prioq_sem");
+			else
+				printf("prioq[%d] od = %d\n", i, ctx->od[i]);
+		}
+	}
+
+	TH_CALL( task_mode(LITMUS_RT_TASK) );
+
+	printf("[%d] Waiting for TS release.\n ", ctx->id);
+	wait_for_ts_release();
+	ctx->count = 0;
+
+	do {
+		int first = (int)(NUM_SEMS * (rand_r(&(ctx->rand)) / (RAND_MAX + 1.0)));
+		int last = (first + NEST_DEPTH - 1 >= NUM_SEMS) ? NUM_SEMS - 1 : first + NEST_DEPTH - 1;
+		int dgl_size = last - first + 1;
+		int replica = -1;
+		int distance;
+
+		int dgl[dgl_size];
+
+		// construct the DGL
+		for(i = first; i <= last; ++i) {
+			dgl[i-first] = ctx->od[i];
+		}
+
+		replica = litmus_lock(ctx->kexclu);
+
+		//printf("[%d] got kexclu replica %d.\n", ctx->id, replica);
+		//fflush(stdout);
+
+		distance = affinity_distance(ctx, replica, last_replica);
+
+		if(USE_DYNAMIC_GROUP_LOCKS) {
+			litmus_dgl_lock(dgl, dgl_size);
+		}
+		else {
+			for(i = 0; i < dgl_size; ++i) {
+				litmus_lock(dgl[i]);
+			}
+		}
+
+		//do_exit = nested_job(ctx, &count, &first, affinity_cost[distance]);
+		do_exit = job(ctx, affinity_cost[distance]);
+
+		if(USE_DYNAMIC_GROUP_LOCKS) {
+			litmus_dgl_unlock(dgl, dgl_size);
+		}
+		else {
+			for(i = dgl_size - 1; i >= 0; --i) {
+				litmus_unlock(dgl[i]);
+			}
+		}
+
+		//printf("[%d]: freeing kexclu replica %d.\n", ctx->id, replica);
+		//fflush(stdout);
+
+		litmus_unlock(ctx->kexclu);
+
+		last_replica = replica;
+
+		if(SLEEP_BETWEEN_JOBS && !do_exit) {
+			sleep_next_period();
+		}
+	} while(!do_exit);
+
+//	if (ctx->id == 0 && NUM_AUX_THREADS) {
+//		gAuxRun = 0;
+//		__sync_synchronize();
+//		CALL( disable_aux_rt_tasks() );
+//	}
+
+	/*****
+	 * 4) Transition to background mode.
+	 */
+	TH_CALL( task_mode(BACKGROUND_TASK) );
+
+	for(i = 0; i < sizeof(ctx->mig_count)/sizeof(ctx->mig_count[0]); ++i)
+	{
+		printf("[%d]: mig_count[%d] = %d\n", ctx->id, i, ctx->mig_count[i]);
+	}
+
+	return NULL;
+}
+
+//int nested_job(struct thread_context* ctx, int *count, int *next, int runfactor)
+//{
+//	int ret;
+//
+//	if(*count == 0 || *next == NUM_SEMS)
+//	{
+//		ret = job(ctx, runfactor);
+//	}
+//	else
+//	{
+//		int which_sem = *next;
+//		int rsm_od = ctx->od[which_sem];
+//
+//		++(*next);
+//		--(*count);
+//
+//		//printf("[%d]: trying to get semaphore %d.\n", ctx->id, which_sem);
+//		//fflush(stdout);
+//		litmus_lock(rsm_od);
+//
+//		//printf("[%d] got semaphore %d.\n", ctx->id, which_sem);
+//		//fflush(stdout);
+//		ret = nested_job(ctx, count, next, runfactor);
+//
+//		//printf("[%d]: freeing semaphore %d.\n", ctx->id, which_sem);
+//		//fflush(stdout);
+//		litmus_unlock(rsm_od);
+//	}
+//
+//return(ret);
+//}
+
+
+void dirty_kb(int kb)
+{
+	int32_t one_kb[256];
+	int32_t sum = 0;
+	int32_t i;
+
+	if(!kb)
+		return;
+
+	for (i = 0; i < 256; i++)
+		sum += one_kb[i];
+	kb--;
+	/* prevent tail recursion */
+	if (kb)
+		dirty_kb(kb);
+	for (i = 0; i < 256; i++)
+		sum += one_kb[i];
+}
+
+int job(struct thread_context* ctx, int runfactor)
+{
+	//struct timespec tosleep = {0, 100000}; // 0.1 ms
+
+	//printf("[%d]: runfactor = %d\n", ctx->id, runfactor);
+
+	//dirty_kb(8 * runfactor);
+	dirty_kb(1 * runfactor);
+	//nanosleep(&tosleep, NULL);
+
+	/* Don't exit. */
+	//return ctx->count++ > 100;
+	//return ctx->count++ > 12000;
+	//return ctx->count++ > 120000;
+	return ctx->count++ >   25000;  // controls number of jobs per task
+}
diff --git a/gpu/locktest.c b/gpu/locktest.c
new file mode 100644
index 0000000..6a1219a
--- /dev/null
+++ b/gpu/locktest.c
@@ -0,0 +1,206 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* Include gettid() */
+#include <sys/types.h>
+
+/* Include threading support. */
+#include <pthread.h>
+
+/* Include the LITMUS^RT API.*/
+#include "litmus.h"
+
+/* Catch errors.
+ */
+#define CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "%s failed: %m\n", #exp);\
+		else \
+			fprintf(stderr, "%s ok.\n", #exp); \
+	} while (0)
+
+#define TH_CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "[%d] %s failed: %m\n", ctx->id, #exp); \
+		else \
+			fprintf(stderr, "[%d] %s ok.\n", ctx->id, #exp); \
+	} while (0)
+
+#define TH_SAFE_CALL( exp ) do { \
+		int ret; \
+		fprintf(stderr, "[%d] calling %s...\n", ctx->id, #exp); \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "\t...[%d] %s failed: %m\n", ctx->id, #exp); \
+		else \
+			fprintf(stderr, "\t...[%d] %s ok.\n", ctx->id, #exp); \
+	} while (0)
+
+
+/* these are only default values */
+int NUM_THREADS=3;
+int NUM_SEMS=10;
+
+#define MAX_SEMS 1000
+
+#define EXEC_COST 	 10
+#define PERIOD		100
+
+/* The information passed to each thread. Could be anything. */
+struct thread_context {
+	int id;
+	int fd;
+	int od[MAX_SEMS];
+	int count;
+	unsigned int rand;
+};
+
+void* rt_thread(void* _ctx);
+int nested_job(struct thread_context* ctx, int *count, int *next);
+int job(struct thread_context*);
+
+#define OPTSTR "t:s:"
+
+int main(int argc, char** argv)
+{
+	int i;
+	struct thread_context* ctx;
+	pthread_t*	     task;
+	int fd;
+
+	int opt;
+	while((opt = getopt(argc, argv, OPTSTR)) != -1) {
+		switch(opt) {
+			case 't':
+				NUM_THREADS = atoi(optarg);
+				break;
+			case 's':
+				NUM_SEMS = atoi(optarg);
+				assert(NUM_SEMS <= MAX_SEMS);
+				break;
+			default:
+				fprintf(stderr, "Unknown option: %c\n", opt);
+				exit(-1);
+				break;
+		}
+	}
+
+	ctx = (struct thread_context*) calloc(NUM_THREADS, sizeof(struct thread_context));
+	task = (pthread_t*) calloc(NUM_THREADS, sizeof(pthread_t));
+
+	srand(0); /* something repeatable for now */
+
+	fd = open("semaphores", O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
+
+	CALL( init_litmus() );
+
+	for (i = 0; i < NUM_THREADS; i++) {
+		ctx[i].id = i;
+		ctx[i].fd = fd;
+		ctx[i].rand = rand();
+		CALL( pthread_create(task + i, NULL, rt_thread, ctx + i) );
+	}
+
+
+	for (i = 0; i < NUM_THREADS; i++)
+		pthread_join(task[i], NULL);
+
+
+	return 0;
+}
+
+void* rt_thread(void* _ctx)
+{
+	int i;
+	int do_exit = 0;
+
+	struct thread_context *ctx = (struct thread_context*)_ctx;
+
+	TH_CALL( init_rt_thread() );
+
+	/* Vary period a little bit. */
+	TH_CALL( sporadic_global(EXEC_COST, PERIOD + 10*ctx->id) );
+
+	for (i = 0; i < NUM_SEMS; i++) {
+		ctx->od[i] = open_fmlp_sem(ctx->fd, i);
+		if(ctx->od[i] < 0)
+			perror("open_fmlp_sem");
+	}
+
+	TH_CALL( task_mode(LITMUS_RT_TASK) );
+
+
+	printf("[%d] Waiting for TS release.\n ", ctx->id);
+	wait_for_ts_release();
+	ctx->count = 0;
+
+	do {
+		int which_sem = (int)(NUM_SEMS * (rand_r(&(ctx->rand)) / (RAND_MAX + 1.0)));
+
+		printf("[%d]: trying to get semaphore %d.\n", ctx->id, which_sem);
+		fflush(stdout);
+
+		TH_SAFE_CALL ( litmus_lock(which_sem) );
+
+		printf("[%d] got semaphore %d.\n", ctx->id, which_sem);
+		fflush(stdout);
+
+		do_exit = job(ctx);
+
+		printf("[%d]: freeing semaphore %d.\n", ctx->id, which_sem);
+		fflush(stdout);
+
+		TH_SAFE_CALL ( litmus_unlock(which_sem) );
+
+		if(!do_exit) {
+			sleep_next_period();
+		}
+	} while(!do_exit);
+
+	/*****
+	 * 4) Transition to background mode.
+	 */
+	TH_CALL( task_mode(BACKGROUND_TASK) );
+
+
+	return NULL;
+}
+
+void dirty_kb(int kb)
+{
+	int32_t one_kb[256];
+	int32_t sum = 0;
+	int32_t i;
+
+	for (i = 0; i < 256; i++)
+		sum += one_kb[i];
+	kb--;
+	/* prevent tail recursion */
+	if (kb)
+		dirty_kb(kb);
+	for (i = 0; i < 256; i++)
+		sum += one_kb[i];
+}
+
+int job(struct thread_context* ctx)
+{
+	/* Do real-time calculation. */
+	dirty_kb(8);
+
+	/* Don't exit. */
+	//return ctx->count++ > 100;
+	//return ctx->count++ > 12000;
+	//return ctx->count++ > 120000;
+	return ctx->count++ > 30000;  // controls number of jobs per task
+}
diff --git a/gpu/nested.c b/gpu/nested.c
new file mode 100644
index 0000000..334de10
--- /dev/null
+++ b/gpu/nested.c
@@ -0,0 +1,262 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* Include gettid() */
+#include <sys/types.h>
+
+/* Include threading support. */
+#include <pthread.h>
+
+/* Include the LITMUS^RT API.*/
+#include "litmus.h"
+
+/* Catch errors.
+ */
+#define CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "%s failed: %m\n", #exp);\
+		else \
+			fprintf(stderr, "%s ok.\n", #exp); \
+	} while (0)
+
+#define TH_CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "[%d] %s failed: %m\n", ctx->id, #exp); \
+		else \
+			fprintf(stderr, "[%d] %s ok.\n", ctx->id, #exp); \
+	} while (0)
+
+#define TH_SAFE_CALL( exp ) do { \
+		int ret; \
+		fprintf(stderr, "[%d] calling %s...\n", ctx->id, #exp); \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "\t...[%d] %s failed: %m\n", ctx->id, #exp); \
+		else \
+			fprintf(stderr, "\t...[%d] %s ok.\n", ctx->id, #exp); \
+	} while (0)
+
+
+#define NUM_CPUS	4
+
+//#define NUM_THREADS	3
+int NUM_THREADS=3;
+
+/* NEST_DEPTH may not be greater than NUM_SEMS. */
+//#define NUM_SEMS	10
+int NUM_SEMS=10;
+
+int SLEEP_BETWEEN_JOBS = 1;
+
+int USE_PRIOQ = 0;
+
+#define MAX_SEMS 1000
+
+//#define NEST_DEPTH	 5
+int NEST_DEPTH=5;
+
+#define EXEC_COST 	 1000*1
+#define PERIOD		1000*10
+
+/* The information passed to each thread. Could be anything. */
+struct thread_context {
+	int id;
+	int fd;
+	int od[MAX_SEMS];
+	int count;
+	unsigned int rand;
+};
+
+void* rt_thread(void* _ctx);
+int nested_job(struct thread_context* ctx, int *count, int *next);
+int job(struct thread_context*);
+
+#define OPTSTR "t:s:d:fq"
+
+int main(int argc, char** argv)
+{
+	int i;
+	struct thread_context* ctx; //[NUM_THREADS];
+	pthread_t*	     task;  //[NUM_THREADS];
+	int fd;
+
+	int opt;
+	while((opt = getopt(argc, argv, OPTSTR)) != -1) {
+		switch(opt) {
+			case 't':
+				NUM_THREADS = atoi(optarg);
+				break;
+			case 's':
+				NUM_SEMS = atoi(optarg);
+				assert(NUM_SEMS <= MAX_SEMS);
+				break;
+			case 'd':
+				NEST_DEPTH = atoi(optarg);
+				break;
+			case 'f':
+				SLEEP_BETWEEN_JOBS = 0;
+				break;
+			case 'q':
+				USE_PRIOQ = 1;
+				break;
+			default:
+				fprintf(stderr, "Unknown option: %c\n", opt);
+				exit(-1);
+				break;
+		}
+	}
+
+	ctx = (struct thread_context*) calloc(NUM_THREADS, sizeof(struct thread_context));
+	task = (pthread_t*) calloc(NUM_THREADS, sizeof(pthread_t));
+
+	srand(0); /* something repeatable for now */
+
+	fd = open("semaphores", O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
+
+	CALL( init_litmus() );
+
+	for (i = 0; i < NUM_THREADS; i++) {
+		ctx[i].id = i;
+		ctx[i].fd = fd;
+		ctx[i].rand = rand();
+		CALL( pthread_create(task + i, NULL, rt_thread, ctx + i) );
+	}
+
+
+	for (i = 0; i < NUM_THREADS; i++)
+		pthread_join(task[i], NULL);
+
+
+	return 0;
+}
+
+void* rt_thread(void* _ctx)
+{
+	int i;
+	int do_exit = 0;
+	struct rt_task param;
+
+	struct thread_context *ctx = (struct thread_context*)_ctx;
+
+	init_rt_task_param(&param);
+	param.exec_cost = EXEC_COST;
+	param.period = PERIOD + 10*ctx->id;
+	param.cls = RT_CLASS_SOFT;
+
+	/* Make presence visible. */
+	//printf("RT Thread %d active.\n", ctx->id);
+
+	TH_CALL( init_rt_thread() );
+	TH_CALL( set_rt_task_param(gettid(), &param) );
+
+	for (i = 0; i < NUM_SEMS; i++) {
+		if (!USE_PRIOQ) {
+			ctx->od[i] = open_fifo_sem(ctx->fd, i);
+			if(ctx->od[i] < 0)
+				perror("open_fifo_sem");
+		}
+		else {
+			ctx->od[i] = open_prioq_sem(ctx->fd, i);
+			if(ctx->od[i] < 0)
+				perror("open_prioq_sem");
+		}
+		//printf("[%d] ctx->od[%d]: %d\n", ctx->id, i, ctx->od[i]);
+	}
+
+	TH_CALL( task_mode(LITMUS_RT_TASK) );
+
+
+	printf("[%d] Waiting for TS release.\n ", ctx->id);
+	wait_for_ts_release();
+	ctx->count = 0;
+
+	do {
+		int first = (int)(NUM_SEMS * (rand_r(&(ctx->rand)) / (RAND_MAX + 1.0)));
+		int count = NEST_DEPTH;
+		do_exit = nested_job(ctx, &count, &first);
+
+		if(SLEEP_BETWEEN_JOBS && !do_exit) {
+			sleep_next_period();
+		}
+	} while(!do_exit);
+
+	/*****
+	 * 4) Transition to background mode.
+	 */
+	TH_CALL( task_mode(BACKGROUND_TASK) );
+
+
+	return NULL;
+}
+
+
+int nested_job(struct thread_context* ctx, int *count, int *next)
+{
+	int ret;
+
+	if(*count == 0 || *next == NUM_SEMS)  /* base case */
+	{
+		ret = job(ctx);
+	}
+	else
+	{
+		int which_sem = ctx->od[*next];
+
+		++(*next);
+		--(*count);
+
+		printf("[%d]: trying to get semaphore %d.\n", ctx->id, which_sem);
+		fflush(stdout);
+		TH_SAFE_CALL ( litmus_lock(which_sem) );
+		printf("[%d] got semaphore %d.\n", ctx->id, which_sem);
+		fflush(stdout);
+		ret = nested_job(ctx, count, next);
+		TH_SAFE_CALL ( litmus_unlock(which_sem) );
+		fflush(stdout);
+	}
+
+	return(ret);
+}
+
+
+
+void dirty_kb(int kb)
+{
+	int32_t one_kb[256];
+	int32_t sum = 0;
+	int32_t i;
+
+	for (i = 0; i < 256; i++)
+		sum += one_kb[i];
+	kb--;
+	/* prevent tail recursion */
+	if (kb)
+		dirty_kb(kb);
+	for (i = 0; i < 256; i++)
+		sum += one_kb[i];
+}
+
+
+
+int job(struct thread_context* ctx)
+{
+	/* Do real-time calculation. */
+	dirty_kb(8);
+
+	/* Don't exit. */
+	//return ctx->count++ > 100;
+	//return ctx->count++ > 12000;
+	//return ctx->count++ > 120000;
+	return ctx->count++ > 30000;
+}
diff --git a/gpu/normal_task.c b/gpu/normal_task.c
new file mode 100644
index 0000000..ccc265c
--- /dev/null
+++ b/gpu/normal_task.c
@@ -0,0 +1,90 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <math.h>
+
+/* Include gettid() */
+#include <sys/types.h>
+
+/* Include threading support. */
+#include <pthread.h>
+
+/* Include the LITMUS^RT API.*/
+#include "litmus.h"
+
+/* Catch errors.
+ */
+#if 1
+#define CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "%s failed: %m\n", #exp);\
+		else \
+			fprintf(stderr, "%s ok.\n", #exp); \
+	} while (0)
+
+#define TH_CALL( exp ) do { \
+		int ret; \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "[%d] %s failed: %m\n", ctx->id, #exp); \
+		else \
+			fprintf(stderr, "[%d] %s ok.\n", ctx->id, #exp); \
+	} while (0)
+
+#define TH_SAFE_CALL( exp ) do { \
+		int ret; \
+		fprintf(stderr, "[%d] calling %s...\n", ctx->id, #exp); \
+		ret = exp; \
+		if (ret != 0) \
+			fprintf(stderr, "\t...[%d] %s failed: %m\n", ctx->id, #exp); \
+		else \
+			fprintf(stderr, "\t...[%d] %s ok.\n", ctx->id, #exp); \
+	} while (0)
+#else
+#define CALL( exp )
+#define TH_CALL( exp )
+#define TH_SAFE_CALL( exp )
+#endif
+
+/* these are only default values */
+// 1000 = 1us
+#define EXEC_COST 	1000*1
+#define PERIOD		2*1000*100
+
+
+int main(int argc, char** argv)
+{
+	struct rt_task param;
+
+	init_rt_task_param(&param);
+	param.exec_cost = EXEC_COST;
+	param.period = PERIOD;
+	param.cls = RT_CLASS_SOFT;
+
+	CALL( init_litmus() );
+
+	CALL( init_rt_thread() );
+	CALL( set_rt_task_param(gettid(), &param) );
+	//CALL( task_mode(LITMUS_RT_TASK) );
+
+	fprintf(stdout, "Waiting for TS release.\n ");
+	wait_for_ts_release();
+
+	fprintf(stdout, "Released!\n");
+
+	//sleep_next_period();
+	//CALL( task_mode(BACKGROUND_TASK) );
+
+	return 0;
+}
+
diff --git a/include/common.h b/include/common.h
index d1234ba..faf2c07 100644
--- a/include/common.h
+++ b/include/common.h
@@ -1,7 +1,14 @@
 #ifndef COMMON_H
 #define COMMON_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 void bail_out(const char* msg);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/include/litmus.h b/include/litmus.h
index 677f9a9..e785f92 100644
--- a/include/litmus.h
+++ b/include/litmus.h
@@ -7,57 +7,45 @@ extern "C" {
 
 #include <sys/types.h>
 #include <stdint.h>
+#include <setjmp.h>
 
 /* Include kernel header.
  * This is required for the rt_param
  * and control_page structures.
  */
 #include "litmus/rt_param.h"
+#include "litmus/signal.h"
 
 #include "asm/cycles.h" /* for null_call() */
 
-typedef int pid_t;	 /* PID of a task */
-
-/* obtain the PID of a thread */
-pid_t gettid(void);
-
-/* migrate to partition */
-int be_migrate_to(int target_cpu);
+#include "migration.h"
 
+void init_rt_task_param(struct rt_task* param);
 int set_rt_task_param(pid_t pid, struct rt_task* param);
 int get_rt_task_param(pid_t pid, struct rt_task* param);
 
-/* setup helper */
-
-/* Times are given in ms. The 'priority' parameter
- * is only relevant under fixed-priority scheduling (and
- * ignored by other plugins). The task_class_t parameter
- * is ignored by most plugins.
- */
-int sporadic_task(
-		lt_t e, lt_t p, lt_t phase,
-		int partition, unsigned int priority,
-		task_class_t cls,
-		budget_policy_t budget_policy, int set_cpu_set);
-
-/* Times are given in ns. The 'priority' parameter
- * is only relevant under fixed-priority scheduling (and
- * ignored by other plugins). The task_class_t parameter
- * is ignored by most plugins.
+/* Release-master-aware functions for getting the first
+ * CPU in a particular cluster or partition. Use these
+ * to set rt_task::cpu for cluster/partitioned scheduling.
  */
-int sporadic_task_ns(
-		lt_t e, lt_t p, lt_t phase,
-		int cpu, unsigned int priority,
-		task_class_t cls,
-		budget_policy_t budget_policy, int set_cpu_set);
-
-/* Convenience macros. Budget enforcement off by default in these macros. */
-#define sporadic_global(e, p) \
-	sporadic_task(e, p, 0, 0, LITMUS_LOWEST_PRIORITY, \
-		RT_CLASS_SOFT, NO_ENFORCEMENT, 0)
-#define sporadic_partitioned(e, p, cpu) \
-	sporadic_task(e, p, 0, cpu, LITMUS_LOWEST_PRIORITY, \
-		RT_CLASS_SOFT, NO_ENFORCEMENT, 1)
+int partition_to_cpu(int partition);
+int cluster_to_first_cpu(int cluster, int cluster_size);
+
+/* Convenience functions for setting up real-time tasks.
+ * Default behaviors set by init_rt_task_params() used.
+ * Also sets affinity masks for clustered/partitions
+ * functions. Time units in nanoseconds. */
+int sporadic_global(lt_t e_ns, lt_t p_ns);
+int sporadic_partitioned(lt_t e_ns, lt_t p_ns, int partition);
+int sporadic_clustered(lt_t e_ns, lt_t p_ns, int cluster, int cluster_size);
+
+/* simple time unit conversion macros */
+#define s2ns(s)   ((s)*1000000000LL)
+#define s2us(s)   ((s)*1000000LL)
+#define s2ms(s)   ((s)*1000LL)
+#define ms2ns(ms) ((ms)*1000000LL)
+#define ms2us(ms) ((ms)*1000LL)
+#define us2ns(us) ((us)*1000LL)
 
 /* file descriptor attached shared objects support */
 typedef enum  {
@@ -66,7 +54,18 @@ typedef enum  {
 	MPCP_SEM	= 2,
 	MPCP_VS_SEM	= 3,
 	DPCP_SEM	= 4,
-	PCP_SEM         = 5,
+	PCP_SEM		= 5,
+
+	FIFO_MUTEX	= 6,
+	IKGLP_SEM	= 7,
+	KFMLP_SEM	= 8,
+
+	IKGLP_SIMPLE_GPU_AFF_OBS = 9,
+	IKGLP_GPU_AFF_OBS = 10,
+	KFMLP_SIMPLE_GPU_AFF_OBS = 11,
+	KFMLP_GPU_AFF_OBS = 12,
+
+	PRIOQ_MUTEX = 13,
 } obj_type_t;
 
 int lock_protocol_for_name(const char* name);
@@ -80,9 +79,32 @@ static inline int od_open(int fd, obj_type_t type, int obj_id)
 	return od_openx(fd, type, obj_id, 0);
 }
 
+int litmus_open_lock(
+	obj_type_t protocol,	/* which locking protocol to use, e.g., FMLP_SEM */
+	int lock_id,		/* numerical id of the lock, user-specified */
+	const char* ns,	/* path to a shared file */
+	void *config_param);	/* any extra info needed by the protocol (such
+				 * as CPU under SRP and PCP), may be NULL */
+
 /* real-time locking protocol support */
 int litmus_lock(int od);
 int litmus_unlock(int od);
+int litmus_should_yield_lock(int od);
+
+/* Dynamic group lock support.  ods arrays MUST BE PARTIALLY ORDERED!!!!!!
+ * Use the same ordering for lock and unlock.
+ *
+ * Ex:
+ *   litmus_dgl_lock({A, B, C, D}, 4);
+ *   litmus_dgl_unlock({A, B, C, D}, 4);
+ */
+int litmus_dgl_lock(int* ods, int dgl_size);
+int litmus_dgl_unlock(int* ods, int dgl_size);
+int litmus_dgl_should_yield_lock(int* ods, int dgl_size);
+
+/* nvidia graphics cards */
+int register_nv_device(int nv_device_id);
+int unregister_nv_device(int nv_device_id);
 
 /* job control*/
 int get_job_no(unsigned int* job_no);
@@ -97,10 +119,8 @@ void exit_litmus(void);
 /* A real-time program. */
 typedef int (*rt_fn_t)(void*);
 
-/* These two functions configure the RT task to use enforced exe budgets */
-int create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet, int period);
-int __create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet,
-		     int period, task_class_t cls);
+/* exec another program as a real-time task. */
+int create_rt_task(rt_fn_t rt_prog, void *arg, struct rt_task* param);
 
 /*	per-task modes */
 enum rt_task_mode_t {
@@ -118,16 +138,14 @@ void exit_np(void);
 int  requested_to_preempt(void);
 
 /* task system support */
-int wait_for_ts_release(void);
+int wait_for_ts_release();
+int wait_for_ts_release2(struct timespec *release);
 int release_ts(lt_t *delay);
 int get_nr_ts_release_waiters(void);
+int read_litmus_stats(int *ready, int *total);
 
-#define __NS_PER_MS 1000000
-
-static inline lt_t ms2lt(unsigned long milliseconds)
-{
-	return __NS_PER_MS * milliseconds;
-}
+int enable_aux_rt_tasks(int flags);
+int disable_aux_rt_tasks(int flags);
 
 /* sleep for some number of nanoseconds */
 int lt_sleep(lt_t timeout);
@@ -140,11 +158,20 @@ double wctime(void);
 
 /* semaphore allocation */
 
+typedef int (*open_sem_t)(int fd, int name);
+
 static inline int open_fmlp_sem(int fd, int name)
 {
 	return od_open(fd, FMLP_SEM, name);
 }
 
+static inline int open_kfmlp_sem(int fd, int name, unsigned int nr_replicas)
+{
+	if (!nr_replicas)
+		return -1;
+	return od_openx(fd, KFMLP_SEM, name, &nr_replicas);
+}
+
 static inline int open_srp_sem(int fd, int name)
 {
 	return od_open(fd, SRP_SEM, name);
@@ -165,6 +192,64 @@ static inline int open_dpcp_sem(int fd, int name, int cpu)
 	return od_openx(fd, DPCP_SEM, name, &cpu);
 }
 
+static inline int open_fifo_sem(int fd, int name)
+{
+	return od_open(fd, FIFO_MUTEX, name);
+}
+
+static inline int open_prioq_sem(int fd, int name)
+{
+	return od_open(fd, PRIOQ_MUTEX, name);
+}
+
+int open_ikglp_sem(int fd, int name, unsigned int nr_replicas);
+
+/* KFMLP-based Token Lock for GPUs
+ * Legacy; mostly untested.
+ */
+int open_kfmlp_gpu_sem(int fd, int name,
+	unsigned int num_gpus, unsigned int gpu_offset, unsigned int rho,
+	int affinity_aware /* bool */);
+
+/* -- Example Configurations --
+ *
+ * Optimal IKGLP Configuration:
+ *   max_in_fifos = IKGLP_M_IN_FIFOS
+ *   max_fifo_len = IKGLP_OPTIMAL_FIFO_LEN
+ *
+ * IKGLP with Relaxed FIFO Length Constraints:
+ *   max_in_fifos = IKGLP_M_IN_FIFOS
+ *   max_fifo_len = IKGLP_UNLIMITED_FIFO_LEN
+ * NOTE: max_in_fifos still limits total number of requests in FIFOs.
+ *
+ * KFMLP Configuration (FIFO queues only):
+ *   max_in_fifos = IKGLP_UNLIMITED_IN_FIFOS
+ *   max_fifo_len = IKGLP_UNLIMITED_FIFO_LEN
+ * NOTE: Uses a non-optimal IKGLP configuration, not an actual KFMLP_SEM.
+ *
+ * RGEM-like Configuration (priority queues only):
+ *   max_in_fifos = 1..(rho*num_gpus)
+ *   max_fifo_len = 1
+ *
+ * For exclusive GPU allocation, use rho = 1
+ * For trivial token lock, use rho = # of tasks in task set
+ *
+ * A simple load-balancing heuristic will still be used if
+ * enable_affinity_heuristics = 0.
+ *
+ * Other constraints:
+ *  - max_in_fifos <= max_fifo_len * rho
+ *        (unless max_in_fifos = IKGLP_UNLIMITED_IN_FIFOS and
+ *         max_fifo_len = IKGLP_UNLIMITED_FIFO_LEN
+ *  - rho > 0
+ *  - num_gpus > 0
+ */
+// takes names 'name' and 'name+1'
+int open_gpusync_token_lock(int fd, int name,
+		unsigned int num_gpus, unsigned int gpu_offset,
+		unsigned int rho, unsigned int max_in_fifos,
+		unsigned int max_fifo_len,
+		int enable_affinity_heuristics /* bool */);
 
 /* syscall overhead measuring */
 int null_call(cycles_t *timestamp);
@@ -176,7 +261,146 @@ int null_call(cycles_t *timestamp);
  */
 struct control_page* get_ctrl_page(void);
 
+
+/* sched_trace injection */
+int inject_name(void);
+int inject_param(void); /* sporadic_task_ns*() must have already been called */
+int inject_release(lt_t release, lt_t deadline, unsigned int job_no);
+int inject_completion(unsigned int job_no);
+int inject_gpu_migration(unsigned int to, unsigned int from);
+int __inject_action(unsigned int action);
+
+#if 1
+#define inject_action(COUNT) \
+do { \
+__inject_action(COUNT); \
+}while(0);
+#else
+#define inject_action(COUNT) \
+do { \
+}while(0);
+#endif
+
+/* Litmus signal handling */
+
+typedef struct litmus_sigjmp
+{
+	sigjmp_buf env;
+	struct litmus_sigjmp *prev;
+} litmus_sigjmp_t;
+
+void push_sigjmp(litmus_sigjmp_t* buf);
+litmus_sigjmp_t* pop_sigjmp(void);
+
+typedef void (*litmus_sig_handler_t)(int);
+typedef void (*litmus_sig_actions_t)(int, siginfo_t *, void *);
+
+/* ignore specified signals. all signals raised while ignored are dropped */
+void ignore_litmus_signals(unsigned long litmus_sig_mask);
+
+/* register a handler for the given set of litmus signals */
+void activate_litmus_signals(unsigned long litmus_sig_mask,
+				litmus_sig_handler_t handler);
+
+/* register an action signal handler for a given set of signals */
+void activate_litmus_signal_actions(unsigned long litmus_sig_mask,
+				litmus_sig_actions_t handler);
+
+/* Block a given set of litmus signals. Any signals raised while blocked
+ * are queued and delivered after unblocking. Call ignore_litmus_signals()
+ * before unblocking if you wish to discard these. Blocking may be
+ * useful to protect COTS code in Litmus that may not be able to deal
+ * with exception-raising signals.
+ */
+void block_litmus_signals(unsigned long litmus_sig_mask);
+
+/* Unblock a given set of litmus signals. */
+void unblock_litmus_signals(unsigned long litmus_sig_mask);
+
+#define SIG_BUDGET_MASK			0x00000001
+/* more ... */
+
+#define ALL_LITMUS_SIG_MASKS	(SIG_BUDGET_MASK)
+
+/* Try/Catch structures useful for implementing abortable jobs.
+ * Should only be used in legitimate cases. ;)
+ */
+#define LITMUS_TRY \
+do { \
+	int sigsetjmp_ret_##__FUNCTION__##__LINE__; \
+	litmus_sigjmp_t lit_env_##__FUNCTION__##__LINE__; \
+	push_sigjmp(&lit_env_##__FUNCTION__##__LINE__); \
+	sigsetjmp_ret_##__FUNCTION__##__LINE__ = \
+		sigsetjmp(lit_env_##__FUNCTION__##__LINE__.env, 1); \
+	if (sigsetjmp_ret_##__FUNCTION__##__LINE__ == 0) {
+
+#define LITMUS_CATCH(x) \
+	} else if (sigsetjmp_ret_##__FUNCTION__##__LINE__ == (x)) {
+
+#define END_LITMUS_TRY \
+	} /* end if-else-if chain */ \
+} while(0); /* end do from 'LITMUS_TRY' */
+
+/* Calls siglongjmp(signum). Use with TRY/CATCH.
+ * Example:
+ *  activate_litmus_signals(SIG_BUDGET_MASK, longjmp_on_litmus_signal);
+ */
+void longjmp_on_litmus_signal(int signum);
+
 #ifdef __cplusplus
 }
 #endif
+
+
+
+
+#ifdef __cplusplus
+/* Expose litmus exceptions if C++.
+ *
+ * KLUDGE: We define everything in the header since liblitmus is a C-only
+ * library, but this header could be included in C++ code.
+ */
+
+#include <exception>
+
+namespace litmus
+{
+	class litmus_exception: public std::exception
+	{
+	public:
+		litmus_exception() throw() {}
+		virtual ~litmus_exception() throw() {}
+		virtual const char* what() const throw() { return "litmus_exception";}
+	};
+
+	class sigbudget: public litmus_exception
+	{
+	public:
+		sigbudget() throw() {}
+		virtual ~sigbudget() throw() {}
+		virtual const char* what() const throw() { return "sigbudget"; }
+	};
+
+	/* Must compile your program with "non-call-exception". */
+	static __attribute__((used))
+	void throw_on_litmus_signal(int signum)
+	{
+		/* We have to unblock the received signal to get more in the future
+		 * because we are not calling siglongjmp(), which normally restores
+		 * the mask for us.
+		 */
+		if (SIG_BUDGET == signum) {
+			unblock_litmus_signals(SIG_BUDGET_MASK);
+			throw sigbudget();
+		}
+		/* else if (...) */
+		else {
+			/* silently ignore */
+		}
+	}
+
+}; /* end namespace 'litmus' */
+
+#endif /* end __cplusplus */
+
 #endif
diff --git a/include/migration.h b/include/migration.h
new file mode 100644
index 0000000..2413e7c
--- /dev/null
+++ b/include/migration.h
@@ -0,0 +1,24 @@
+
+typedef int pid_t;
+
+/* obtain the PID of a thread */
+pid_t gettid();
+
+/* Assign a task to a cpu/partition/cluster.
+ * PRECOND: tid is not yet in real-time mode (it's a best effort task).
+ * Set tid == 0 to migrate the caller */
+int be_migrate_thread_to_cpu(pid_t tid, int target_cpu);
+int be_migrate_thread_to_partition(pid_t tid, int partition);
+/* If using release master, set cluster_sz to size of largest cluster. tid
+ * will not be scheduled on release master. */
+int be_migrate_thread_to_cluster(pid_t tid, int cluster, int cluster_sz);
+
+/* set ignore_rm == 1 to include release master in tid's cpu affinity */
+int __be_migrate_thread_to_cluster(pid_t tid, int cluster, int cluster_sz, int ignore_rm);
+
+int be_migrate_to_cpu(int target_cpu);
+int be_migrate_to_partition(int partition);
+int be_migrate_to_cluster(int cluster, int cluster_sz);
+
+int num_online_cpus();
+int release_master();
diff --git a/include/tests.h b/include/tests.h
index ed2b409..4ca21f8 100644
--- a/include/tests.h
+++ b/include/tests.h
@@ -7,8 +7,11 @@
 
 #define fail(fmt, args...)						\
 	do {								\
-		fprintf(stderr, "\n!! TEST FAILURE " fmt "\n   at %s:%d (%s)\n", \
-			## args, __FILE__, __LINE__, __FUNCTION__);	\
+		fprintf(stderr, "\n!! TEST FAILURE " fmt		\
+			"\n   at %s:%d (%s)"				\
+			"\n   in task PID=%d\n",			\
+			## args, __FILE__, __LINE__, __FUNCTION__,	\
+			getpid());					\
 		fflush(stderr);						\
 		exit(200);						\
 	} while (0)
diff --git a/src/kernel_iface.c b/src/kernel_iface.c
index 4cc1af5..73d398f 100644
--- a/src/kernel_iface.c
+++ b/src/kernel_iface.c
@@ -56,9 +56,8 @@ ssize_t read_file(const char* fname, void* buf, size_t maxlen)
 		return got;
 }
 
-int get_nr_ts_release_waiters(void)
+int read_litmus_stats(int *ready, int *all)
 {
-	int ready = 0, all = 0;
 	char buf[100];
 	ssize_t len;
 
@@ -67,15 +66,21 @@ int get_nr_ts_release_waiters(void)
 		len = sscanf(buf,
 			     "real-time tasks   = %d\n"
 			     "ready for release = %d\n",
-			     &all, &ready);
-	if (len == 2)
+			     all, ready);
+	return len == 2;
+}
+
+int get_nr_ts_release_waiters(void)
+{
+	int ready, all;
+	if (read_litmus_stats(&ready, &all))
 		return ready;
 	else
-		return len;
+		return -1;
 }
 
 /* thread-local pointer to control page */
-static __thread struct control_page *ctrl_page;
+static __thread struct control_page *ctrl_page = NULL;
 
 int init_kernel_iface(void)
 {
diff --git a/src/litmus.c b/src/litmus.c
index b32254b..70f7fb6 100644
--- a/src/litmus.c
+++ b/src/litmus.c
@@ -3,7 +3,10 @@
 #include <stdio.h>
 #include <string.h>
 #include <signal.h>
+#include <fcntl.h>
 #include <sys/mman.h>
+#include <sys/types.h>
+
 
 #include <sched.h> /* for cpu sets */
 
@@ -23,6 +26,17 @@ static struct {
 	{MPCP_VS_SEM, "MPCP-VS"},
 	LP(DPCP),
 	LP(PCP),
+
+	{FIFO_MUTEX, "FIFO"},
+	LP(IKGLP),
+	LP(KFMLP),
+
+	{IKGLP_SIMPLE_GPU_AFF_OBS, "IKGLP-GPU-SIMPLE"},
+	{IKGLP_GPU_AFF_OBS, "IKGLP-GPU"},
+	{KFMLP_SIMPLE_GPU_AFF_OBS, "KFMLP-GPU-SIMPLE"},
+	{KFMLP_GPU_AFF_OBS, "KFMLP-GPU"},
+
+	{PRIOQ_MUTEX, "PRIOQ"},
 };
 
 #define NUM_PROTOS (sizeof(protocol)/sizeof(protocol[0]))
@@ -49,6 +63,23 @@ const char* name_for_lock_protocol(int id)
 	return "<UNKNOWN>";
 }
 
+int litmus_open_lock(
+	obj_type_t protocol,
+	int lock_id,
+	const char* namespace,
+	void *config_param)
+{
+	int fd, od;
+
+	fd = open(namespace, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+	if (fd < 0)
+		return -1;
+	od = od_openx(fd, protocol, lock_id, config_param);
+	close(fd);
+	return od;
+}
+
+
 
 void show_rt_param(struct rt_task* tp)
 {
@@ -57,6 +88,36 @@ void show_rt_param(struct rt_task* tp)
 	       tp->exec_cost, tp->period, tp->cpu);
 }
 
+void init_rt_task_param(struct rt_task* tp)
+{
+	/* Defaults:
+	 *  - implicit deadline (t->relative_deadline == 0)
+	 *  - phase = 0
+	 *  - class = RT_CLASS_SOFT
+	 *  - budget policy = NO_ENFORCEMENT
+	 *  - fixed priority = LITMUS_LOWEST_PRIORITY
+	 *  - release policy = SPORADIC
+	 *  - cpu assignment = 0
+	 *
+	 * User must still set the following fields to non-zero values:
+	 *  - tp->exec_cost
+	 *  - tp->period
+	 *
+	 * User must set tp->cpu to the appropriate value for non-global
+	 * schedulers. For clusters, set tp->cpu to the first CPU in the
+	 * assigned cluster.
+	 */
+
+	memset(tp, 0, sizeof(*tp));
+
+	tp->cls = RT_CLASS_SOFT;
+	tp->priority = LITMUS_LOWEST_PRIORITY;
+	tp->budget_policy = NO_ENFORCEMENT;
+	tp->drain_policy = DRAIN_SIMPLE;
+	tp->budget_signal_policy = NO_SIGNALS;
+	tp->release_policy = SPORADIC;
+}
+
 task_class_t str2class(const char* str)
 {
 	if      (!strcmp(str, "hrt"))
@@ -66,56 +127,55 @@ task_class_t str2class(const char* str)
 	else if (!strcmp(str, "be"))
 		return RT_CLASS_BEST_EFFORT;
 	else
-		return -1;
+		return (task_class_t)(-1);
 }
 
 #define NS_PER_MS 1000000
 
-/* only for best-effort execution: migrate to target_cpu */
-int be_migrate_to(int target_cpu)
+int sporadic_global(lt_t e_ns, lt_t p_ns)
 {
-	cpu_set_t cpu_set;
+	struct rt_task param;
 
-	CPU_ZERO(&cpu_set);
-	CPU_SET(target_cpu, &cpu_set);
-	return sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set);
+	init_rt_task_param(&param);
+	param.exec_cost = e_ns;
+	param.period = p_ns;
+
+	return set_rt_task_param(gettid(), &param);
 }
 
-int sporadic_task(lt_t e, lt_t p, lt_t phase,
-		  int cpu, unsigned int priority,
-		  task_class_t cls,
-		  budget_policy_t budget_policy, int set_cpu_set)
+int sporadic_partitioned(lt_t e_ns, lt_t p_ns, int partition)
 {
-	return sporadic_task_ns(e * NS_PER_MS, p * NS_PER_MS, phase * NS_PER_MS,
-				cpu, priority, cls, budget_policy, set_cpu_set);
+	int ret;
+	struct rt_task param;
+
+	ret = be_migrate_to_partition(partition);
+	check("be_migrate_to_partition()");
+	if (ret != 0)
+		return ret;
+
+	init_rt_task_param(&param);
+	param.exec_cost = e_ns;
+	param.period = p_ns;
+	param.cpu = partition_to_cpu(partition);
+
+	return set_rt_task_param(gettid(), &param);
 }
 
-int sporadic_task_ns(lt_t e, lt_t p, lt_t phase,
-		     int cpu, unsigned int priority,
-		     task_class_t cls,
-		     budget_policy_t budget_policy, int set_cpu_set)
+int sporadic_clustered(lt_t e_ns, lt_t p_ns, int cluster, int cluster_size)
 {
-	struct rt_task param;
 	int ret;
+	struct rt_task param;
+
+	ret = be_migrate_to_cluster(cluster, cluster_size);
+	check("be_migrate_to_cluster()");
+	if (ret != 0)
+		return ret;
+
+	init_rt_task_param(&param);
+	param.exec_cost = e_ns;
+	param.period = p_ns;
+	param.cpu = cluster_to_first_cpu(cluster, cluster_size);
 
-	/* Zero out first --- this is helpful when we add plugin-specific
-	 * parameters during development.
-	 */
-	memset(&param, 0, sizeof(param));
-
-	param.exec_cost = e;
-	param.period    = p;
-	param.relative_deadline = p; /* implicit deadline */
-	param.cpu       = cpu;
-	param.cls       = cls;
-	param.phase	= phase;
-	param.budget_policy = budget_policy;
-	param.priority  = priority;
-
-	if (set_cpu_set) {
-		ret = be_migrate_to(cpu);
-		check("migrate to cpu");
-	}
 	return set_rt_task_param(gettid(), &param);
 }
 
@@ -144,3 +204,141 @@ void exit_litmus(void)
 {
 	/* nothing to do in current version */
 }
+
+int open_kfmlp_gpu_sem(int fd, int name,
+	unsigned int num_gpus, unsigned int gpu_offset, unsigned int rho,
+	int affinity_aware)
+{
+	int lock_od;
+	int affinity_od;
+	unsigned int num_replicas;
+	struct gpu_affinity_observer_args aff_args;
+	int aff_type;
+
+	// number of GPU tokens
+	num_replicas = num_gpus * rho;
+
+	// create the GPU token lock
+	lock_od = open_kfmlp_sem(fd, name, num_replicas);
+	if(lock_od < 0) {
+		perror("open_kfmlp_sem");
+		return -1;
+	}
+
+	// create the affinity method to use.
+	// "no affinity" -> KFMLP_SIMPLE_GPU_AFF_OBS
+	aff_args.obs.lock_od = lock_od;
+	aff_args.replica_to_gpu_offset = gpu_offset;
+	aff_args.rho = rho;
+
+	aff_type = (affinity_aware) ? KFMLP_GPU_AFF_OBS : KFMLP_SIMPLE_GPU_AFF_OBS;
+	affinity_od = od_openx(fd, aff_type, name+1, &aff_args);
+	if(affinity_od < 0) {
+		perror("open_kfmlp_aff");
+		return -1;
+	}
+
+	return lock_od;
+}
+
+
+//int open_ikglp_gpu_sem(int fd, int name, int num_gpus, int gpu_offset, int rho, int affinity_aware, int relax_max_fifo_len)
+//{
+//	int lock_od;
+//	int affinity_od;
+//	int num_replicas;
+//	struct gpu_affinity_observer_args aff_args;
+//	int aff_type;
+//
+//	// number of GPU tokens
+//	num_replicas = num_gpus * num_simult_users;
+//
+//	// create the GPU token lock
+//	lock_od = open_ikglp_sem(fd, name, (void*)&num_replicas);
+//	if(lock_od < 0) {
+//		perror("open_ikglp_sem");
+//		return -1;
+//	}
+//
+//	// create the affinity method to use.
+//	// "no affinity" -> KFMLP_SIMPLE_GPU_AFF_OBS
+//	aff_args.obs.lock_od = lock_od;
+//	aff_args.replica_to_gpu_offset = gpu_offset;
+//	aff_args.nr_simult_users = num_simult_users;
+//	aff_args.relaxed_rules = (relax_max_fifo_len) ? 1 : 0;
+//
+//	aff_type = (affinity_aware) ? IKGLP_GPU_AFF_OBS : IKGLP_SIMPLE_GPU_AFF_OBS;
+//	affinity_od = od_openx(fd, aff_type, name+1, &aff_args);
+//	if(affinity_od < 0) {
+//		perror("open_ikglp_aff");
+//		return -1;
+//	}
+//
+//	return lock_od;
+//}
+
+
+
+
+int open_ikglp_sem(int fd, int name, unsigned int nr_replicas)
+{
+	struct ikglp_args args = {
+		.nr_replicas = nr_replicas,
+		.max_in_fifos = IKGLP_M_IN_FIFOS,
+		.max_fifo_len = IKGLP_OPTIMAL_FIFO_LEN};
+
+	return od_openx(fd, IKGLP_SEM, name, &args);
+}
+
+
+
+int open_gpusync_token_lock(int fd, int name,
+							unsigned int num_gpus, unsigned int gpu_offset,
+							unsigned int rho, unsigned int max_in_fifos,
+							unsigned int max_fifo_len,
+							int enable_affinity_heuristics)
+{
+	int lock_od;
+	int affinity_od;
+
+	struct ikglp_args args = {
+		.nr_replicas = num_gpus*rho,
+		.max_in_fifos = max_in_fifos,
+		.max_fifo_len = max_fifo_len,
+	};
+	struct gpu_affinity_observer_args aff_args;
+	int aff_type;
+
+	if (!num_gpus || !rho) {
+		perror("open_gpusync_sem");
+		return -1;
+	}
+
+	if ((max_in_fifos != IKGLP_UNLIMITED_IN_FIFOS) &&
+		(max_fifo_len != IKGLP_UNLIMITED_FIFO_LEN) &&
+		(max_in_fifos > args.nr_replicas * max_fifo_len)) {
+		perror("open_gpusync_sem");
+		return(-1);
+	}
+
+	lock_od = od_openx(fd, IKGLP_SEM, name, &args);
+	if(lock_od < 0) {
+		perror("open_gpusync_sem");
+		return -1;
+	}
+
+	// create the affinity method to use.
+	aff_args.obs.lock_od = lock_od;
+	aff_args.replica_to_gpu_offset = gpu_offset;
+	aff_args.rho = rho;
+	aff_args.relaxed_rules = (max_fifo_len == IKGLP_UNLIMITED_FIFO_LEN) ? 1 : 0;
+
+	aff_type = (enable_affinity_heuristics) ? IKGLP_GPU_AFF_OBS : IKGLP_SIMPLE_GPU_AFF_OBS;
+	affinity_od = od_openx(fd, aff_type, name+1, &aff_args);
+	if(affinity_od < 0) {
+		perror("open_gpusync_affinity");
+		return -1;
+	}
+
+	return lock_od;
+}
diff --git a/src/migration.c b/src/migration.c
new file mode 100644
index 0000000..084b68c
--- /dev/null
+++ b/src/migration.c
@@ -0,0 +1,217 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h> /* for cpu sets */
+#include <unistd.h>
+
+#ifdef LITMUS_NUMA_SUPPORT
+#include <numa.h>
+#endif
+
+#include "migration.h"
+
+
+extern ssize_t read_file(const char* fname, void* buf, size_t maxlen);
+
+int release_master()
+{
+	static const char NO_CPU[] = "NO_CPU";
+	char buf[5] = {0}; /* up to 9999 CPUs */
+	int master = -1;
+
+	int ret = read_file("/proc/litmus/release_master", &buf, sizeof(buf)-1);
+
+	if ((ret > 0) && (strncmp(buf, NO_CPU, sizeof(NO_CPU)-1) != 0))
+		master = atoi(buf);
+
+	return master;
+}
+
+int num_online_cpus()
+{
+	return sysconf(_SC_NPROCESSORS_ONLN);
+}
+
+int partition_to_cpu(int partition)
+{
+	int cpu = partition;
+	int master = release_master();
+	if (master != -1 && master <= cpu) {
+		++cpu; /* skip over the release master */
+	}
+	return cpu;
+}
+
+int cluster_to_first_cpu(int cluster, int cluster_sz)
+{
+	int first_cpu;
+	int master;
+
+	if (cluster_sz == 1)
+		return partition_to_cpu(cluster);
+
+	master = release_master();
+	first_cpu = cluster * cluster_sz;
+
+	if (master == first_cpu)
+		++first_cpu;
+
+	return first_cpu;
+}
+
+#ifdef LITMUS_NUMA_SUPPORT
+/* Restrict the task to the numa nodes in the cpu mask. */
+/* Call this before setting up CPU affinity masks since that mask may be
+ * a subset of the numa nodes. */
+static int setup_numa(pid_t tid, int sz, const cpu_set_t *cpus)
+{
+	int nr_nodes;
+	int nr_cpus = num_online_cpus();
+	struct bitmask* new_nodes;
+	struct bitmask* old_nodes;
+	int i;
+	int ret = 0;
+
+	if (numa_available() != 0)
+		goto out;
+
+	nr_nodes = numa_max_node()+1;
+	new_nodes = numa_bitmask_alloc(nr_nodes);
+	old_nodes = numa_bitmask_alloc(nr_nodes);
+	/* map the cpu mask to a numa mask */
+	for (i = 0; i < nr_cpus; ++i) {
+		if(CPU_ISSET_S(i, sz, cpus)) {
+			numa_bitmask_setbit(new_nodes, numa_node_of_cpu(i));
+		}
+	}
+	/* compute the complement numa mask */
+	for (i = 0; i < nr_nodes; ++i) {
+		if (!numa_bitmask_isbitset(new_nodes, i)) {
+			numa_bitmask_setbit(old_nodes, i);
+		}
+	}
+
+	numa_set_strict(1);
+	numa_bind(new_nodes); /* sets CPU and memory policy */
+	ret = numa_migrate_pages(tid, old_nodes, new_nodes); /* move over prio alloc'ed pages */
+	numa_bitmask_free(new_nodes);
+	numa_bitmask_free(old_nodes);
+
+out:
+	return ret;
+}
+#else
+#define setup_numa(x, y, z) 0
+#endif
+
+int be_migrate_thread_to_cpu(pid_t tid, int target_cpu)
+{
+	cpu_set_t *cpu_set;
+	size_t sz;
+	int num_cpus;
+	int ret;
+
+	/* TODO: Error check to make sure that tid is not a real-time task. */
+
+	if (target_cpu < 0)
+		return -1;
+
+	num_cpus = num_online_cpus();
+	if (num_cpus == -1)
+		return -1;
+
+	if (target_cpu >= num_cpus)
+		return -1;
+
+	cpu_set = CPU_ALLOC(num_cpus);
+	sz = CPU_ALLOC_SIZE(num_cpus);
+
+	CPU_ZERO_S(sz, cpu_set);
+	CPU_SET_S(target_cpu, sz, cpu_set);
+
+	/* apply to caller */
+	if (tid == 0)
+		tid = gettid();
+
+	ret = (setup_numa(tid, sz, cpu_set) >= 0) ? 0 : -1;
+	if (!ret)
+		ret = sched_setaffinity(tid, sz, cpu_set);
+
+	CPU_FREE(cpu_set);
+
+	return ret;
+}
+
+int be_migrate_thread_to_cluster(pid_t tid, int cluster, int cluster_sz)
+{
+	return __be_migrate_thread_to_cluster(tid, cluster, cluster_sz, 0);
+}
+
+int __be_migrate_thread_to_cluster(pid_t tid, int cluster, int cluster_sz,
+						 int ignore_rm)
+{
+	int first_cpu = cluster * cluster_sz; /* first CPU in cluster */
+	int last_cpu = first_cpu + cluster_sz - 1;
+	int master;
+	int num_cpus;
+	cpu_set_t *cpu_set;
+	size_t sz;
+	int i;
+	int ret;
+
+	/* TODO: Error check to make sure that tid is not a real-time task. */
+
+	if (cluster_sz == 1) {
+		/* we're partitioned */
+		return be_migrate_thread_to_partition(tid, cluster);
+	}
+
+	master = (ignore_rm) ? -1 : release_master();
+		num_cpus = num_online_cpus();
+
+	if (num_cpus == -1 || last_cpu >= num_cpus || first_cpu < 0)
+		return -1;
+
+	cpu_set = CPU_ALLOC(num_cpus);
+	sz = CPU_ALLOC_SIZE(num_cpus);
+	CPU_ZERO_S(sz, cpu_set);
+
+	for (i = first_cpu; i <= last_cpu; ++i) {
+		if (i != master) {
+			CPU_SET_S(i, sz, cpu_set);
+		}
+	}
+
+	/* apply to caller */
+	if (tid == 0)
+		tid = gettid();
+
+	ret = (setup_numa(tid, sz, cpu_set) >= 0) ? 0 : -1;
+	if (!ret)
+		ret = sched_setaffinity(tid, sz, cpu_set);
+
+	CPU_FREE(cpu_set);
+
+	return ret;
+}
+
+int be_migrate_thread_to_partition(pid_t tid, int partition)
+{
+	return be_migrate_thread_to_cpu(tid, partition_to_cpu(partition));
+}
+
+
+int be_migrate_to_cpu(int target_cpu)
+{
+	return be_migrate_thread_to_cpu(0, target_cpu);
+}
+
+int be_migrate_to_cluster(int cluster, int cluster_sz)
+{
+	return be_migrate_thread_to_cluster(0, cluster, cluster_sz);
+}
+
+int be_migrate_to_partition(int partition)
+{
+	return be_migrate_thread_to_partition(0, partition);
+}
diff --git a/src/signal.c b/src/signal.c
new file mode 100644
index 0000000..1bd0f62
--- /dev/null
+++ b/src/signal.c
@@ -0,0 +1,109 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "litmus.h"
+#include "internal.h"
+
+/* setjmp calls are stored on a singlely link list,
+ * one stack per thread.
+ */
+static __thread litmus_sigjmp_t *g_sigjmp_tail = 0;
+
+void push_sigjmp(litmus_sigjmp_t *buf)
+{
+	buf->prev = g_sigjmp_tail;
+	g_sigjmp_tail = buf;
+}
+
+litmus_sigjmp_t* pop_sigjmp(void)
+{
+	litmus_sigjmp_t* ret;
+	ret = g_sigjmp_tail;
+	g_sigjmp_tail = (ret) ? ret->prev : NULL;
+	return ret;
+}
+
+static void reg_litmus_signals(unsigned long litmus_sig_mask,
+		struct sigaction *pAction)
+{
+	int ret;
+
+	if (litmus_sig_mask | SIG_BUDGET_MASK) {
+		ret = sigaction(SIG_BUDGET, pAction, NULL);
+		check("SIG_BUDGET");
+	}
+	/* more signals ... */
+}
+
+void ignore_litmus_signals(unsigned long litmus_sig_mask)
+{
+	activate_litmus_signals(litmus_sig_mask, SIG_IGN);
+}
+
+void activate_litmus_signals(unsigned long litmus_sig_mask,
+	litmus_sig_handler_t handle)
+{
+	struct sigaction action;
+	memset(&action, 0, sizeof(action));
+	action.sa_handler = handle;
+
+	reg_litmus_signals(litmus_sig_mask, &action);
+}
+
+void activate_litmus_signal_actions(unsigned long litmus_sig_mask,
+		litmus_sig_actions_t handle)
+{
+	struct sigaction action;
+	memset(&action, 0, sizeof(action));
+	action.sa_sigaction = handle;
+	action.sa_flags = SA_SIGINFO;
+
+	reg_litmus_signals(litmus_sig_mask, &action);
+}
+
+void block_litmus_signals(unsigned long litmus_sig_mask)
+{
+	int ret;
+	sigset_t sigs;
+	sigemptyset(&sigs);
+
+	if (litmus_sig_mask | SIG_BUDGET_MASK) {
+		sigaddset(&sigs, SIG_BUDGET);
+	}
+	/* more signals ... */
+
+	ret = sigprocmask(SIG_BLOCK, &sigs, NULL);
+	check("SIG_BLOCK litmus signals");
+}
+
+void unblock_litmus_signals(unsigned long litmus_sig_mask)
+{
+	int ret;
+	sigset_t sigs;
+	sigemptyset(&sigs);
+
+	if (litmus_sig_mask | SIG_BUDGET_MASK) {
+		sigaddset(&sigs, SIG_BUDGET);
+	}
+	/* more ... */
+
+	ret = sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+	check("SIG_UNBLOCK litmus signals");
+}
+
+
+void longjmp_on_litmus_signal(int signum)
+{
+	/* We get signal!  Main screen turn on! */
+	litmus_sigjmp_t *lit_env;
+	lit_env = pop_sigjmp();
+	if (lit_env) {
+		/* What you say?! */
+		//printf("%d: we get signal = %d!\n", gettid(), signum);
+		siglongjmp(lit_env->env, signum); /* restores signal mask */
+	}
+	else {
+		/* silently ignore the signal */
+		//printf("%d: silently ignoring signal.\n", gettid());
+	}
+}
diff --git a/src/syscalls.c b/src/syscalls.c
index c68f15b..ff02b7d 100644
--- a/src/syscalls.c
+++ b/src/syscalls.c
@@ -19,6 +19,12 @@ pid_t gettid(void)
 
 int set_rt_task_param(pid_t pid, struct rt_task *param)
 {
+	if (param->budget_signal_policy != NO_SIGNALS) {
+		/* drop all signals until they're explicitly activated by
+		 * user code. */
+		ignore_litmus_signals(SIG_BUDGET);
+	}
+
 	return syscall(__NR_set_rt_task_param, pid, param);
 }
 
@@ -52,6 +58,26 @@ int litmus_unlock(int od)
 	return syscall(__NR_litmus_unlock, od);
 }
 
+int litmus_should_yield_lock(int od)
+{
+	return syscall(__NR_litmus_should_yield_lock, od);
+}
+
+int litmus_dgl_lock(int *ods, int dgl_size)
+{
+	return syscall(__NR_litmus_dgl_lock, ods, dgl_size);
+}
+
+int litmus_dgl_unlock(int *ods, int dgl_size)
+{
+	return syscall(__NR_litmus_dgl_unlock, ods, dgl_size);
+}
+
+int litmus_dgl_should_yield_lock(int *ods, int dgl_size)
+{
+	return syscall(__NR_litmus_dgl_should_yield_lock, ods, dgl_size);
+}
+
 int get_job_no(unsigned int *job_no)
 {
 	return syscall(__NR_query_job_no, job_no);
@@ -72,9 +98,19 @@ int sched_getscheduler(pid_t pid)
 	return syscall(__NR_sched_getscheduler, pid);
 }
 
+static int __wait_for_ts_release(struct timespec *release)
+{
+	return syscall(__NR_wait_for_ts_release, release);
+}
+
 int wait_for_ts_release(void)
 {
-	return syscall(__NR_wait_for_ts_release);
+	return __wait_for_ts_release(NULL);
+}
+
+int wait_for_ts_release2(struct timespec *release)
+{
+	return __wait_for_ts_release(release);
 }
 
 int release_ts(lt_t *delay)
@@ -86,3 +122,47 @@ int null_call(cycles_t *timestamp)
 {
 	return syscall(__NR_null_call, timestamp);
 }
+
+int enable_aux_rt_tasks(int flags)
+{
+	return syscall(__NR_set_aux_tasks, flags | AUX_ENABLE);
+}
+
+int disable_aux_rt_tasks(int flags)
+{
+	return syscall(__NR_set_aux_tasks, flags & ~AUX_ENABLE);
+}
+
+int inject_name(void)
+{
+	return syscall(__NR_sched_trace_event, ST_INJECT_NAME, NULL);
+}
+
+int inject_param(void)
+{
+	return syscall(__NR_sched_trace_event, ST_INJECT_PARAM, NULL);
+}
+
+int inject_release(lt_t release, lt_t deadline, unsigned int job_no)
+{
+	struct st_inject_args args = {.release = release, .deadline = deadline, .job_no = job_no};
+	return syscall(__NR_sched_trace_event, ST_INJECT_RELEASE, &args);
+}
+
+int inject_completion(unsigned int job_no)
+{
+	struct st_inject_args args = {.release = 0, .deadline = 0, .job_no = job_no};
+	return syscall(__NR_sched_trace_event, ST_INJECT_COMPLETION, &args);
+}
+
+int inject_gpu_migration(unsigned int to, unsigned int from)
+{
+	struct st_inject_args args = {.to = to, .from = from};
+	return syscall(__NR_sched_trace_event, ST_INJECT_MIGRATION, &args);
+}
+
+int __inject_action(unsigned int action)
+{
+	struct st_inject_args args = {.action = action};
+	return syscall(__NR_sched_trace_event, ST_INJECT_ACTION, &args);
+}
diff --git a/src/task.c b/src/task.c
index 4d237bd..c3a9109 100644
--- a/src/task.c
+++ b/src/task.c
@@ -40,24 +40,16 @@ int __launch_rt_task(rt_fn_t rt_prog, void *rt_arg, rt_setup_fn_t setup,
 	return rt_task;
 }
 
-int __create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet, int period,
-		     task_class_t class)
+int create_rt_task(rt_fn_t rt_prog, void *arg, struct rt_task* param)
 {
-	struct rt_task params;
-	params.cpu       = cpu;
-	params.period    = period;
-	params.exec_cost = wcet;
-	params.cls       = class;
-	params.phase     = 0;
-	/* enforce budget for tasks that might not use sleep_next_period() */
-	params.budget_policy = QUANTUM_ENFORCEMENT;
-
-	return __launch_rt_task(rt_prog, arg,
-				(rt_setup_fn_t) set_rt_task_param, &params);
-}
+	if (param->budget_policy == NO_ENFORCEMENT) {
+		/* This is only safe if the task to be launched does not peg the CPU.
+		 That is, it must block frequently for I/O or call sleep_next_period()
+		 at the end of each job. Otherwise, the task may peg the CPU. */
+		//printf("Warning: running budget enforcement used.\n");
+	}
 
-int create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet, int period) {
-	return __create_rt_task(rt_prog, arg, cpu, wcet, period, RT_CLASS_HARD);
+	return __launch_rt_task(rt_prog, arg, (rt_setup_fn_t) set_rt_task_param, param);
 }
 
 
diff --git a/tests/core_api.c b/tests/core_api.c
index c0b291e..fc4deb9 100644
--- a/tests/core_api.c
+++ b/tests/core_api.c
@@ -18,6 +18,7 @@ TESTCASE(set_rt_task_param_invalid_params, ALL,
 	 "reject invalid rt_task values")
 {
 	struct rt_task params;
+	init_rt_task_param(&params);
 	params.cpu        = 0;
 	params.period     = 100;
 	params.relative_deadline = params.period;
@@ -53,6 +54,7 @@ TESTCASE(reject_bad_priorities, P_FP,
 	 "reject invalid priorities")
 {
 	struct rt_task params;
+	init_rt_task_param(&params);
 	params.cpu        = 0;
 	params.exec_cost  =  10;
 	params.period     = 100;
@@ -61,7 +63,7 @@ TESTCASE(reject_bad_priorities, P_FP,
 	params.cls        = RT_CLASS_HARD;
 	params.budget_policy = NO_ENFORCEMENT;
 
-	SYSCALL( be_migrate_to(params.cpu) );
+	SYSCALL( be_migrate_to_cpu(params.cpu) );
 
 	/* too high */
 	params.priority	  = 0;
@@ -79,6 +81,7 @@ TESTCASE(accept_valid_priorities, P_FP,
 	 "accept lowest and highest valid priorities")
 {
 	struct rt_task params;
+	init_rt_task_param(&params);
 	params.cpu        = 0;
 	params.exec_cost  =  10;
 	params.period     = 100;
@@ -87,7 +90,7 @@ TESTCASE(accept_valid_priorities, P_FP,
 	params.cls        = RT_CLASS_HARD;
 	params.budget_policy = NO_ENFORCEMENT;
 
-	SYSCALL( be_migrate_to(params.cpu) );
+	SYSCALL( be_migrate_to_cpu(params.cpu) );
 
 	/* acceptable */
 	params.priority   = LITMUS_LOWEST_PRIORITY;
@@ -120,7 +123,7 @@ TESTCASE(rt_fork_non_rt, LITMUS,
 	unsigned int pid, job_no;
 	int status;
 
-	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( sporadic_partitioned(ms2ns(10), ms2ns(100), 0) );
 	SYSCALL( task_mode(LITMUS_RT_TASK) );
 
 	pid = fork();
diff --git a/tests/fdso.c b/tests/fdso.c
index 8a2a0d0..b216cb5 100644
--- a/tests/fdso.c
+++ b/tests/fdso.c
@@ -16,7 +16,7 @@ TESTCASE(fmlp_not_active, C_EDF | PFAIR | LINUX,
 {
 	int fd;
 
-	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT) );
+	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
 
 	ASSERT(fd != -1);
 
@@ -48,8 +48,8 @@ TESTCASE(invalid_od, ALL,
 TESTCASE(invalid_obj_type, ALL,
 	 "reject invalid object types")
 {
-	SYSCALL_FAILS( EINVAL, od_open(0, -1, 0) );
-	SYSCALL_FAILS( EINVAL, od_open(0, 10, 0) );
+	SYSCALL_FAILS( EINVAL, od_open(0, (obj_type_t)-1, 0) );
+	SYSCALL_FAILS( EINVAL, od_open(0, (obj_type_t)10, 0) );
 }
 
 TESTCASE(not_inherit_od, GSN_EDF | PSN_EDF,
@@ -57,7 +57,7 @@ TESTCASE(not_inherit_od, GSN_EDF | PSN_EDF,
 {
 	int fd, od, pid, status;
 
-	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT) );
+	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
 
 	SYSCALL( od = open_fmlp_sem(fd, 0) );
 
@@ -66,7 +66,7 @@ TESTCASE(not_inherit_od, GSN_EDF | PSN_EDF,
 	ASSERT( pid != -1 );
 
 	/* must be an RT task to lock at all */
-	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( sporadic_partitioned(ms2ns(10), ms2ns(100), 0) );
 	SYSCALL( task_mode(LITMUS_RT_TASK) );
 
 	if (pid == 0) {
diff --git a/tests/locks.c b/tests/locks.c
index d7ebfe2..c3eba4e 100644
--- a/tests/locks.c
+++ b/tests/locks.c
@@ -11,7 +11,7 @@ TESTCASE(not_lock_fmlp_be, GSN_EDF | PSN_EDF | P_FP,
 {
 	int fd, od;
 
-	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT) );
+	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
 
 	SYSCALL( od = open_fmlp_sem(fd, 0) );
 
@@ -34,7 +34,7 @@ TESTCASE(not_lock_srp_be, PSN_EDF | P_FP,
 {
 	int fd, od;
 
-	SYSCALL( fd = open(".srp_locks", O_RDONLY | O_CREAT) );
+	SYSCALL( fd = open(".srp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
 
 	/* BE tasks may not open SRP semaphores */
 
@@ -51,9 +51,9 @@ TESTCASE(lock_srp, PSN_EDF | P_FP,
 {
 	int fd, od;
 
-	SYSCALL( fd = open(".srp_locks", O_RDONLY | O_CREAT) );
+	SYSCALL( fd = open(".srp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
 
-	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( sporadic_partitioned(ms2ns(10), ms2ns(100), 0) );
 	SYSCALL( task_mode(LITMUS_RT_TASK) );
 
 	SYSCALL( od = open_srp_sem(fd, 0) );
@@ -83,9 +83,9 @@ TESTCASE(lock_fmlp, PSN_EDF | GSN_EDF | P_FP,
 {
 	int fd, od;
 
-	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT) );
+	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
 
-	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( sporadic_partitioned(ms2ns(10), ms2ns(100), 0) );
 	SYSCALL( task_mode(LITMUS_RT_TASK) );
 
 	SYSCALL( od = open_fmlp_sem(fd, 0) );
diff --git a/tests/nesting.c b/tests/nesting.c
new file mode 100644
index 0000000..b294334
--- /dev/null
+++ b/tests/nesting.c
@@ -0,0 +1,468 @@
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "tests.h"
+#include "litmus.h"
+
+TESTCASE(lock_fmlp_nesting, PSN_EDF | GSN_EDF | P_FP,
+	 "FMLP no nesting allowed")
+{
+	int fd, od, od2;
+
+	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od = open_fmlp_sem(fd, 0) );
+	SYSCALL( od2 = open_fmlp_sem(fd, 1) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( close(fd) );
+
+	SYSCALL( remove(".fmlp_locks") );
+}
+
+TESTCASE(lock_fmlp_srp_nesting, PSN_EDF | P_FP,
+	 "FMLP no nesting with SRP resources allowed")
+{
+	int fd, od, od2;
+
+	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od = open_fmlp_sem(fd, 0) );
+	SYSCALL( od2 = open_srp_sem(fd, 1) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( close(fd) );
+
+	SYSCALL( remove(".fmlp_locks") );
+}
+
+TESTCASE(lock_srp_nesting, PSN_EDF | P_FP,
+	 "SRP nesting allowed")
+{
+	int fd, od, od2;
+
+	SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od = open_srp_sem(fd, 0) );
+	SYSCALL( od2 = open_srp_sem(fd, 1) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( close(fd) );
+
+	SYSCALL( remove(".fmlp_locks") );
+}
+
+TESTCASE(lock_pcp_nesting, P_FP,
+	 "PCP nesting allowed")
+{
+	int od, od2;
+	const char* namespace = ".pcp_locks";
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(PCP_SEM, 0, namespace, NULL) );
+	SYSCALL( od2 = litmus_open_lock(PCP_SEM, 1, namespace, NULL) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(namespace) );
+}
+
+TESTCASE(lock_mpcp_pcp_no_nesting, P_FP,
+	 "PCP and MPCP nesting not allowed")
+{
+	int od, od2;
+	const char* namespace = ".pcp_locks";
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(PCP_SEM, 0, namespace, NULL) );
+	SYSCALL( od2 = litmus_open_lock(MPCP_SEM, 1, namespace, NULL) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(namespace) );
+}
+
+TESTCASE(lock_fmlp_pcp_no_nesting, P_FP,
+	 "PCP and FMLP nesting not allowed")
+{
+	int od, od2;
+	const char* namespace = ".pcp_locks";
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(PCP_SEM, 0, namespace, NULL) );
+	SYSCALL( od2 = litmus_open_lock(FMLP_SEM, 1, namespace, NULL) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(namespace) );
+}
+
+TESTCASE(lock_dpcp_pcp_no_nesting, P_FP,
+	 "PCP and DPCP nesting not allowed")
+{
+	int od, od2;
+	int cpu = 0;
+	const char* namespace = ".pcp_locks";
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(PCP_SEM, 0, namespace, NULL) );
+	SYSCALL( od2 = litmus_open_lock(DPCP_SEM, 1, namespace, &cpu) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(namespace) );
+}
+
+TESTCASE(lock_mpcp_srp_no_nesting, P_FP,
+	 "SRP and MPCP nesting not allowed")
+{
+	int od, od2;
+	const char* namespace = ".pcp_locks";
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(SRP_SEM, 0, namespace, NULL) );
+	SYSCALL( od2 = litmus_open_lock(MPCP_SEM, 1, namespace, NULL) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(namespace) );
+}
+
+TESTCASE(lock_dpcp_srp_no_nesting, P_FP,
+	 "SRP and DPCP nesting not allowed")
+{
+	int od, od2;
+	int cpu = 0;
+	const char* namespace = ".pcp_locks";
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(SRP_SEM, 0, namespace, NULL) );
+	SYSCALL( od2 = litmus_open_lock(DPCP_SEM, 1, namespace, &cpu) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(namespace) );
+}
+
+TESTCASE(lock_fmlp_mpcp_no_nesting, P_FP,
+	 "MPCP and FMLP nesting not allowed")
+{
+	int od, od2;
+	const char* namespace = ".pcp_locks";
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(MPCP_SEM, 0, namespace, NULL) );
+	SYSCALL( od2 = litmus_open_lock(FMLP_SEM, 1, namespace, NULL) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(namespace) );
+}
+
+TESTCASE(lock_fmlp_dpcp_no_nesting, P_FP,
+	 "DPCP and FMLP nesting not allowed")
+{
+	int od, od2;
+	const char* namespace = ".pcp_locks";
+	int cpu = 0;
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(DPCP_SEM, 0, namespace, &cpu) );
+	SYSCALL( od2 = litmus_open_lock(FMLP_SEM, 1, namespace, NULL) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(namespace) );
+}
+
+TESTCASE(mpcp_nesting, P_FP,
+	 "MPCP no nesting allowed")
+{
+	int od, od2;
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(MPCP_SEM, 0, ".mpcp_locks", NULL) );
+	SYSCALL( od2 = litmus_open_lock(MPCP_SEM, 1, ".mpcp_locks", NULL) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(".mpcp_locks") );
+}
+
+TESTCASE(mpcpvs_nesting, P_FP,
+	 "MPCP-VS no nesting allowed")
+{
+	int od, od2;
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(MPCP_VS_SEM, 0, ".mpcp_locks", NULL) );
+	SYSCALL( od2 = litmus_open_lock(MPCP_VS_SEM, 1, ".mpcp_locks", NULL) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(".mpcp_locks") );
+}
+
+TESTCASE(dpcp_nesting, P_FP,
+	 "DPCP no nesting allowed")
+{
+	int od, od2;
+	int cpu = 0;
+
+	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+	SYSCALL( od  = litmus_open_lock(DPCP_SEM, 0, ".dpcp_locks", &cpu) );
+	SYSCALL( od2 = litmus_open_lock(DPCP_SEM, 1, ".dpcp_locks", &cpu) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( litmus_lock(od) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od2));
+	SYSCALL( litmus_unlock(od) );
+
+	SYSCALL( litmus_lock(od2) );
+	SYSCALL_FAILS(EBUSY, litmus_lock(od));
+	SYSCALL( litmus_unlock(od2) );
+
+	SYSCALL( od_close(od) );
+	SYSCALL( od_close(od2) );
+
+	SYSCALL( remove(".dpcp_locks") );
+}
diff --git a/tests/pcp.c b/tests/pcp.c
index 88d1be3..19009a3 100644
--- a/tests/pcp.c
+++ b/tests/pcp.c
@@ -1,6 +1,8 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <stdio.h>
+#include <sys/wait.h> /* for waitpid() */
+
 
 #include "tests.h"
 #include "litmus.h"
@@ -11,9 +13,9 @@ TESTCASE(lock_pcp, P_FP,
 {
 	int fd, od, cpu = 0;
 
-	SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT) );
+	SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
 
-	SYSCALL( sporadic_partitioned(10, 100, cpu) );
+	SYSCALL( sporadic_partitioned(ms2ns(10), ms2ns(100), cpu) );
 	SYSCALL( task_mode(LITMUS_RT_TASK) );
 
 	SYSCALL( od = open_pcp_sem(fd, 0, cpu) );
@@ -37,14 +39,222 @@ TESTCASE(lock_pcp, P_FP,
 	SYSCALL( remove(".pcp_locks") );
 }
 
+TESTCASE(pcp_inheritance, P_FP,
+	 "PCP priority inheritance")
+{
+	int fd, od, cpu = 0;
+
+	int child_hi, child_lo, child_middle, status, waiters;
+	lt_t delay = ms2ns(100);
+	double start, stop;
+
+	struct rt_task params;
+	init_rt_task_param(&params);
+	params.cpu        = 0;
+	params.exec_cost  =  ms2ns(10000);
+	params.period     = ms2ns(100000);
+	params.relative_deadline = params.period;
+	params.phase      = 0;
+	params.cls        = RT_CLASS_HARD;
+	params.budget_policy = NO_ENFORCEMENT;
+
+	SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
+
+
+	child_lo = FORK_TASK(
+		params.priority = LITMUS_LOWEST_PRIORITY;
+		params.phase    = 0;
+		SYSCALL( set_rt_task_param(gettid(), &params) );
+		SYSCALL( be_migrate_to_cpu(params.cpu) );
+		SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+		SYSCALL( od = open_pcp_sem(fd, 0, cpu) );
+
+		SYSCALL( wait_for_ts_release() );
+
+		SYSCALL( litmus_lock(od) );
+		start = cputime();
+		while (cputime() - start < 0.25)
+			;
+		SYSCALL( litmus_unlock(od) );
+
+		SYSCALL(sleep_next_period() );
+		);
+
+	child_middle = FORK_TASK(
+		params.priority	= LITMUS_HIGHEST_PRIORITY + 1;
+		params.phase    = ms2ns(100);
+
+		SYSCALL( set_rt_task_param(gettid(), &params) );
+		SYSCALL( be_migrate_to_cpu(params.cpu) );
+		SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+
+		SYSCALL( wait_for_ts_release() );
+
+		start = cputime();
+		while (cputime() - start < 5)
+			;
+		SYSCALL( sleep_next_period() );
+		);
+
+	child_hi = FORK_TASK(
+		params.priority	= LITMUS_HIGHEST_PRIORITY;
+		params.phase    = ms2ns(50);
+
+		SYSCALL( set_rt_task_param(gettid(), &params) );
+		SYSCALL( be_migrate_to_cpu(params.cpu) );
+		SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+		SYSCALL( od = open_pcp_sem(fd, 0, cpu) );
+
+		SYSCALL( wait_for_ts_release() );
+
+		start = wctime();
+		/* block on semaphore */
+		SYSCALL( litmus_lock(od) );
+		SYSCALL( litmus_unlock(od) );
+		stop  = wctime();
+
+		/* Assert we had some blocking. */
+		ASSERT( stop - start > 0.1);
+
+		/* Assert we woke up 'soonish' after the sleep. */
+		ASSERT( stop - start < 1 );
+
+		SYSCALL( kill(child_middle, SIGUSR2) );
+		SYSCALL( kill(child_lo, SIGUSR2) );
+		);
+
+	do {
+		waiters = get_nr_ts_release_waiters();
+		ASSERT( waiters >= 0 );
+	} while (waiters != 3);
+
+	SYSCALL( be_migrate_to_cpu(1) );
+
+	waiters = release_ts(&delay);
+
+	SYSCALL( waitpid(child_hi, &status, 0) );
+	ASSERT( status == 0 );
+
+	SYSCALL( waitpid(child_lo, &status, 0) );
+	ASSERT( status ==  SIGUSR2);
+
+	SYSCALL( waitpid(child_middle, &status, 0) );
+	ASSERT( status ==  SIGUSR2);
+}
+
+TESTCASE(srp_ceiling_blocking, P_FP | PSN_EDF,
+	 "SRP ceiling blocking")
+{
+	int fd, od;
+
+	int child_hi, child_lo, child_middle, status, waiters;
+	lt_t delay = ms2ns(100);
+	double start, stop;
+
+	struct rt_task params;
+	init_rt_task_param(&params);
+	params.cpu        = 0;
+	params.exec_cost  =  ms2ns(10000);
+	params.period     = ms2ns(100000);
+	params.relative_deadline = params.period;
+	params.phase      = 0;
+	params.cls        = RT_CLASS_HARD;
+	params.budget_policy = NO_ENFORCEMENT;
+
+	SYSCALL( fd = open(".srp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
+
+
+	child_lo = FORK_TASK(
+		params.priority = LITMUS_LOWEST_PRIORITY;
+		params.phase    = 0;
+		SYSCALL( set_rt_task_param(gettid(), &params) );
+		SYSCALL( be_migrate_to_cpu(params.cpu) );
+		SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+		SYSCALL( od = open_srp_sem(fd, 0) );
+
+		SYSCALL( wait_for_ts_release() );
+
+		SYSCALL( litmus_lock(od) );
+		start = cputime();
+		while (cputime() - start < 0.25)
+			;
+		SYSCALL( litmus_unlock(od) );
+		);
+
+	child_middle = FORK_TASK(
+		params.priority	= LITMUS_HIGHEST_PRIORITY + 1;
+		params.phase    = ms2ns(100);
+		params.relative_deadline -= ms2ns(110);
+
+		SYSCALL( set_rt_task_param(gettid(), &params) );
+		SYSCALL( be_migrate_to_cpu(params.cpu) );
+		SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+
+		SYSCALL( wait_for_ts_release() );
+
+		start = cputime();
+		while (cputime() - start < 5)
+			;
+		);
+
+	child_hi = FORK_TASK(
+		params.priority	= LITMUS_HIGHEST_PRIORITY;
+		params.phase    = ms2ns(50);
+		params.relative_deadline -= ms2ns(200);
+
+		SYSCALL( set_rt_task_param(gettid(), &params) );
+		SYSCALL( be_migrate_to_cpu(params.cpu) );
+		SYSCALL( task_mode(LITMUS_RT_TASK) );
+
+		SYSCALL( od = open_srp_sem(fd, 0) );
+
+		SYSCALL( wait_for_ts_release() );
+
+		start = wctime();
+		/* block on semaphore */
+		SYSCALL( litmus_lock(od) );
+		SYSCALL( litmus_unlock(od) );
+		stop  = wctime();
+
+		/* Assert we had "no" blocking (modulo qemu overheads). */
+		ASSERT( stop - start < 0.01);
+
+		SYSCALL( kill(child_middle, SIGUSR2) );
+		SYSCALL( kill(child_lo, SIGUSR2) );
+		);
+
+	do {
+		waiters = get_nr_ts_release_waiters();
+		ASSERT( waiters >= 0 );
+	} while (waiters != 3);
+
+	SYSCALL( be_migrate_to_cpu(1) );
+
+	waiters = release_ts(&delay);
+
+	SYSCALL( waitpid(child_hi, &status, 0) );
+	ASSERT( status == 0 );
+
+	SYSCALL( waitpid(child_lo, &status, 0) );
+	ASSERT( status ==  SIGUSR2);
+
+	SYSCALL( waitpid(child_middle, &status, 0) );
+	ASSERT( status ==  SIGUSR2);
+}
+
 TESTCASE(lock_dpcp, P_FP,
 	 "DPCP acquisition and release")
 {
 	int fd, od, cpu = 1;
 
-	SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT) );
+	SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
 
-	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( sporadic_partitioned(ms2ns(10), ms2ns(100), 0) );
 	SYSCALL( task_mode(LITMUS_RT_TASK) );
 
 	SYSCALL( od = open_dpcp_sem(fd, 0, cpu) );
@@ -73,7 +283,7 @@ TESTCASE(not_lock_pcp_be, P_FP,
 {
 	int fd, od;
 
-	SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT) );
+	SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
 
 	/* BE tasks are not even allowed to open a PCP semaphore */
 	SYSCALL_FAILS(EPERM, od = open_pcp_sem(fd, 0, 1) );
@@ -95,9 +305,9 @@ TESTCASE(lock_mpcp, P_FP,
 {
 	int fd, od;
 
-	SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT) );
+	SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
 
-	SYSCALL( sporadic_partitioned(10, 100, 0) );
+	SYSCALL( sporadic_partitioned(ms2ns(10), ms2ns(100), 0) );
 	SYSCALL( task_mode(LITMUS_RT_TASK) );
 
 	SYSCALL( od = open_mpcp_sem(fd, 0) );
diff --git a/tests/sched.c b/tests/sched.c
index ab47a91..6726b46 100644
--- a/tests/sched.c
+++ b/tests/sched.c
@@ -9,13 +9,14 @@ TESTCASE(preempt_on_resume, P_FP | PSN_EDF,
 	 "preempt lower-priority task when a higher-priority task resumes")
 {
 	int child_hi, child_lo, status, waiters;
-	lt_t delay = ms2lt(100);
+	lt_t delay = ms2ns(100);
 	double start, stop;
 
 	struct rt_task params;
+	init_rt_task_param(&params);
 	params.cpu        = 0;
-	params.exec_cost  =  ms2lt(10000);
-	params.period     = ms2lt(100000);
+	params.exec_cost  = ms2ns(10000);
+	params.period     = ms2ns(100000);
 	params.relative_deadline = params.period;
 	params.phase      = 0;
 	params.cls        = RT_CLASS_HARD;
@@ -24,7 +25,7 @@ TESTCASE(preempt_on_resume, P_FP | PSN_EDF,
 	child_lo = FORK_TASK(
 		params.priority = LITMUS_LOWEST_PRIORITY;
 		SYSCALL( set_rt_task_param(gettid(), &params) );
-		SYSCALL( be_migrate_to(params.cpu) );
+		SYSCALL( be_migrate_to_cpu(params.cpu) );
 		SYSCALL( task_mode(LITMUS_RT_TASK) );
 
 		SYSCALL( wait_for_ts_release() );
@@ -40,7 +41,7 @@ TESTCASE(preempt_on_resume, P_FP | PSN_EDF,
 		params.priority	= LITMUS_HIGHEST_PRIORITY;
 		params.relative_deadline -= 1000000;
 		SYSCALL( set_rt_task_param(gettid(), &params) );
-		SYSCALL( be_migrate_to(params.cpu) );
+		SYSCALL( be_migrate_to_cpu(params.cpu) );
 		SYSCALL( task_mode(LITMUS_RT_TASK) );
 
 		SYSCALL( wait_for_ts_release() );
@@ -51,14 +52,14 @@ TESTCASE(preempt_on_resume, P_FP | PSN_EDF,
 			;
 
 		start = wctime();
-		SYSCALL( lt_sleep(ms2lt(100)) );
+		SYSCALL( lt_sleep(ms2ns(100)) );
 		stop = wctime();
 
 		SYSCALL( kill(child_lo, SIGUSR2) );
 
 		if (stop - start >= 0.2)
 			fprintf(stderr, "\nHi-prio delay = %fsec\n",
-				stop - start - (ms2lt(100) / 1E9));
+				stop - start - (ms2ns(100) / (float)s2ns(1)));
 
 		/* Assert we woke up 'soonish' after the sleep. */
 		ASSERT( stop - start < 0.2 );
-- 
1.7.10.4

