diff --git a/include/litmus/debug_trace.h b/include/litmus/debug_trace.h
index 48d086d..99be3af 100644
--- a/include/litmus/debug_trace.h
+++ b/include/litmus/debug_trace.h
@@ -4,10 +4,18 @@
 #ifdef CONFIG_SCHED_DEBUG_TRACE
 void sched_trace_log_message(const char* fmt, ...);
 void dump_trace_buffer(int max);
+
 #else
 
 #define sched_trace_log_message(fmt, ...)
+#define dump_trace_buffer(max)
+
+#endif
 
+#ifdef CONFIG_SCHED_DEBUG_DUMP_ON_OOPS
+#define litmus_dump_trace() dump_trace_buffer(0)
+#else
+#define litmus_dump_trace()
 #endif
 
 extern atomic_t __log_seq_no;
@@ -28,8 +36,11 @@ extern atomic_t __log_seq_no;
 				TRACE_ARGS,  ## args)
 
 #define TRACE_TASK(t, fmt, args...)			\
-	TRACE("(%s/%d:%d) " fmt, (t)->comm, (t)->pid,	\
-	      (t)->rt_param.job_params.job_no,  ##args)
+	TRACE("(%s/%d:%d) " fmt,			 \
+	      t ? (t)->comm : "null",			 \
+	      t ? (t)->pid : 0,				 \
+	      t ? (t)->rt_param.job_params.job_no : 0,	 \
+	      ##args)
 
 #define TRACE_CUR(fmt, args...) \
 	TRACE_TASK(current, fmt, ## args)
diff --git a/include/litmus/fdso.h b/include/litmus/fdso.h
index f2115b8..181da1c 100644
--- a/include/litmus/fdso.h
+++ b/include/litmus/fdso.h
@@ -1,6 +1,6 @@
 /* fdso.h - file descriptor attached shared objects
  *
- * (c) 2007 B. Brandenburg, LITMUS^RT project
+ * (c) 2007-2013 B. Brandenburg, LITMUS^RT project
  */
 
 #ifndef _LINUX_FDSO_H_
@@ -26,7 +26,10 @@ typedef enum  {
 
 	PCP_SEM         = 5,
 
-	MAX_OBJ_TYPE	= 5
+	OMLP_SEM	= 6,
+	OMIP_SEM	= 7,
+
+	MAX_OBJ_TYPE	= 7
 } obj_type_t;
 
 struct inode_obj_id {
diff --git a/include/litmus/rt_domain.h b/include/litmus/rt_domain.h
index ac24929..c341258 100644
--- a/include/litmus/rt_domain.h
+++ b/include/litmus/rt_domain.h
@@ -60,8 +60,14 @@ struct release_heap {
 	/* used to delegate releases */
 	struct hrtimer_start_on_info	info;
 #endif
-	/* required for the timer callback */
-	rt_domain_t*			dom;
+
+	union {
+		/* required for the timer callback */
+		rt_domain_t*			dom;
+
+		/* To simplify per-task timers. */
+		struct task_struct*		task;
+	};
 };
 
 
diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h
index 4cd06dd..4ddc790 100644
--- a/include/litmus/rt_param.h
+++ b/include/litmus/rt_param.h
@@ -119,6 +119,8 @@ struct _rt_domain;
 struct bheap_node;
 struct release_heap;
 
+struct migratory_prio_inh;
+
 struct rt_job {
 	/* Time instant the the job was or will be released.  */
 	lt_t	release;
@@ -182,7 +184,14 @@ struct rt_param {
 	 * could point to self if PI does not result in
 	 * an increased task priority.
 	 */
-	 struct task_struct*	inh_task;
+	struct task_struct*	inh_task;
+
+	int			priodon_state;
+
+	struct migratory_prio_inh* mpi;
+
+	/* priority donation support */
+	int			donor_cpu;
 
 #ifdef CONFIG_NP_SECTION
 	/* For the FMLP under PSN-EDF, it is required to make the task
diff --git a/kernel/panic.c b/kernel/panic.c
index 6923167..d87528c 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -24,6 +24,8 @@
 #include <linux/nmi.h>
 #include <linux/dmi.h>
 
+#include <litmus/debug_trace.h>
+
 #define PANIC_TIMER_STEP 100
 #define PANIC_BLINK_SPD 18
 
@@ -77,6 +79,7 @@ NORET_TYPE void panic(const char * fmt, ...)
 	vsnprintf(buf, sizeof(buf), fmt, args);
 	va_end(args);
 	printk(KERN_EMERG "Kernel panic - not syncing: %s\n",buf);
+	litmus_dump_trace();
 #ifdef CONFIG_DEBUG_BUGVERBOSE
 	dump_stack();
 #endif
@@ -354,6 +357,7 @@ void oops_exit(void)
 {
 	do_oops_enter_exit();
 	print_oops_end_marker();
+	litmus_dump_trace();
 	kmsg_dump(KMSG_DUMP_OOPS);
 }
 
diff --git a/kernel/sched.c b/kernel/sched.c
index c4b6bd5..0d013b0 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -5317,6 +5317,7 @@ recheck:
 
 	p->sched_reset_on_fork = reset_on_fork;
 
+	preempt_disable();
 	if (p->policy == SCHED_LITMUS)
 		litmus_exit_task(p);
 
@@ -5330,6 +5331,8 @@ recheck:
 		litmus->task_new(p, on_rq, running);
 	}
 
+	preempt_enable();
+
 	if (running)
 		p->sched_class->set_curr_task(rq);
 	if (on_rq)
diff --git a/lib/bug.c b/lib/bug.c
index 1955209..eac01fe 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -43,6 +43,7 @@
 #include <linux/bug.h>
 #include <linux/sched.h>
 
+#include <litmus/debug_trace.h>
 extern const struct bug_entry __start___bug_table[], __stop___bug_table[];
 
 static inline unsigned long bug_addr(const struct bug_entry *bug)
@@ -179,5 +180,7 @@ enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs)
 		       "[verbose debug info unavailable]\n",
 		       (void *)bugaddr);
 
+	litmus_dump_trace();
+
 	return BUG_TRAP_TYPE_BUG;
 }
diff --git a/litmus/Kconfig b/litmus/Kconfig
index bd6635c..1e6cbfe 100644
--- a/litmus/Kconfig
+++ b/litmus/Kconfig
@@ -263,6 +263,17 @@ config SCHED_DEBUG_TRACE_CALLER
 
 	 If unsure, say No.
 
+config SCHED_DEBUG_DUMP_ON_OOPS
+       bool "Dump  TRACE() log in case of BUG/OOPS"
+       depends on SCHED_DEBUG_TRACE
+       default n
+       help
+         With this option enabled, panic() and friends try to dump the remaining
+	 contents of the TRACE() buffer to the console. This creates a fair bit
+	 of clutter, but can be helpful in getting the last messages out.
+
+	 If unsure, say No.
+
 config PREEMPT_STATE_TRACE
        bool "Trace preemption state machine transitions"
        depends on SCHED_DEBUG_TRACE && DEBUG_KERNEL
diff --git a/litmus/fdso.c b/litmus/fdso.c
index 250377d..2a88197 100644
--- a/litmus/fdso.c
+++ b/litmus/fdso.c
@@ -1,6 +1,6 @@
 /* fdso.c - file descriptor attached shared objects
  *
- * (c) 2007 B. Brandenburg, LITMUS^RT project
+ * (c) 2007-2013 B. Brandenburg, LITMUS^RT project
  *
  * Notes:
  *   - objects descriptor (OD) tables are not cloned during a fork.
@@ -27,10 +27,14 @@ static const struct fdso_ops* fdso_ops[] = {
 	&generic_lock_ops, /* MPCP_VS_SEM */
 	&generic_lock_ops, /* DPCP_SEM */
 	&generic_lock_ops, /* PCP_SEM */
+	&generic_lock_ops, /* OMLP_SEM */
+	&generic_lock_ops, /* OMIP_SEM */
 };
 
 static int fdso_create(void** obj_ref, obj_type_t type, void* __user config)
 {
+	BUILD_BUG_ON(ARRAY_SIZE(fdso_ops) != MAX_OBJ_TYPE + 1);
+
 	if (fdso_ops[type]->create)
 		return fdso_ops[type]->create(obj_ref, type, config);
 	else
diff --git a/litmus/litmus.c b/litmus/litmus.c
index 9c6b738..dc94be7 100644
--- a/litmus/litmus.c
+++ b/litmus/litmus.c
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/reboot.h>
+#include <linux/stop_machine.h>
 
 #include <litmus/litmus.h>
 #include <litmus/bheap.h>
@@ -24,9 +25,6 @@
 
 /* Number of RT tasks that exist in the system */
 atomic_t rt_task_count 		= ATOMIC_INIT(0);
-static DEFINE_RAW_SPINLOCK(task_transition_lock);
-/* synchronize plugin switching */
-atomic_t cannot_use_plugin	= ATOMIC_INIT(0);
 
 /* Give log messages sequential IDs. */
 atomic_t __log_seq_no = ATOMIC_INIT(0);
@@ -322,10 +320,12 @@ static void reinit_litmus_state(struct task_struct* p, int restore)
 long litmus_admit_task(struct task_struct* tsk)
 {
 	long retval = 0;
-	unsigned long flags;
 
 	BUG_ON(is_realtime(tsk));
 
+	tsk_rt(tsk)->heap_node = NULL;
+	tsk_rt(tsk)->rel_heap = NULL;
+
 	if (get_rt_relative_deadline(tsk) == 0 ||
 	    get_exec_cost(tsk) >
 			min(get_rt_relative_deadline(tsk), get_rt_period(tsk)) ) {
@@ -347,9 +347,6 @@ long litmus_admit_task(struct task_struct* tsk)
 
 	INIT_LIST_HEAD(&tsk_rt(tsk)->list);
 
-	/* avoid scheduler plugin changing underneath us */
-	raw_spin_lock_irqsave(&task_transition_lock, flags);
-
 	/* allocate heap node for this task */
 	tsk_rt(tsk)->heap_node = bheap_node_alloc(GFP_ATOMIC);
 	tsk_rt(tsk)->rel_heap = release_heap_alloc(GFP_ATOMIC);
@@ -357,15 +354,14 @@ long litmus_admit_task(struct task_struct* tsk)
 	if (!tsk_rt(tsk)->heap_node || !tsk_rt(tsk)->rel_heap) {
 		printk(KERN_WARNING "litmus: no more heap node memory!?\n");
 
-		bheap_node_free(tsk_rt(tsk)->heap_node);
-		release_heap_free(tsk_rt(tsk)->rel_heap);
-
 		retval = -ENOMEM;
-		goto out_unlock;
+		goto out;
 	} else {
 		bheap_node_init(&tsk_rt(tsk)->heap_node, tsk);
 	}
 
+	preempt_disable();
+
 	retval = litmus->admit_task(tsk);
 
 	if (!retval) {
@@ -374,9 +370,13 @@ long litmus_admit_task(struct task_struct* tsk)
 		atomic_inc(&rt_task_count);
 	}
 
-out_unlock:
-	raw_spin_unlock_irqrestore(&task_transition_lock, flags);
+	preempt_enable();
+
 out:
+	if (retval) {
+		bheap_node_free(tsk_rt(tsk)->heap_node);
+		release_heap_free(tsk_rt(tsk)->rel_heap);
+	}
 	return retval;
 }
 
@@ -396,37 +396,10 @@ void litmus_exit_task(struct task_struct* tsk)
 	}
 }
 
-/* IPI callback to synchronize plugin switching */
-static void synch_on_plugin_switch(void* info)
-{
-	atomic_inc(&cannot_use_plugin);
-	while (atomic_read(&cannot_use_plugin) > 0)
-		cpu_relax();
-}
-
-/* Switching a plugin in use is tricky.
- * We must watch out that no real-time tasks exists
- * (and that none is created in parallel) and that the plugin is not
- * currently in use on any processor (in theory).
- */
-int switch_sched_plugin(struct sched_plugin* plugin)
+static int do_plugin_switch(void *_plugin)
 {
-	unsigned long flags;
-	int ret = 0;
-
-	BUG_ON(!plugin);
-
-	/* forbid other cpus to use the plugin */
-	atomic_set(&cannot_use_plugin, 1);
-	/* send IPI to force other CPUs to synch with us */
-	smp_call_function(synch_on_plugin_switch, NULL, 0);
-
-	/* wait until all other CPUs have started synch */
-	while (atomic_read(&cannot_use_plugin) < num_online_cpus())
-		cpu_relax();
-
-	/* stop task transitions */
-	raw_spin_lock_irqsave(&task_transition_lock, flags);
+	int ret;
+	struct sched_plugin* plugin = _plugin;
 
 	/* don't switch if there are active real-time tasks */
 	if (atomic_read(&rt_task_count) == 0) {
@@ -444,11 +417,24 @@ int switch_sched_plugin(struct sched_plugin* plugin)
 	} else
 		ret = -EBUSY;
 out:
-	raw_spin_unlock_irqrestore(&task_transition_lock, flags);
-	atomic_set(&cannot_use_plugin, 0);
 	return ret;
 }
 
+/* Switching a plugin in use is tricky.
+ * We must watch out that no real-time tasks exists
+ * (and that none is created in parallel) and that the plugin is not
+ * currently in use on any processor (in theory).
+ */
+int switch_sched_plugin(struct sched_plugin* plugin)
+{
+	BUG_ON(!plugin);
+
+	if (atomic_read(&rt_task_count) == 0)
+		return stop_machine(do_plugin_switch, plugin, NULL);
+	else
+		return -EBUSY;
+}
+
 /* Called upon fork.
  * p is the newly forked task.
  */
diff --git a/litmus/sched_cedf.c b/litmus/sched_cedf.c
index b45b46f..6762d48 100644
--- a/litmus/sched_cedf.c
+++ b/litmus/sched_cedf.c
@@ -1,7 +1,7 @@
 /*
  * litmus/sched_cedf.c
  *
- * Implementation of the C-EDF scheduling algorithm.
+ * Implementation of the C-EDF scheduling algorithm with priority donation.
  *
  * This implementation is based on G-EDF:
  * - CPUs are clustered around L2 or L3 caches.
@@ -15,17 +15,15 @@
  *   supported clusters are: L1 (private cache: pedf), L2, L3, ALL (all
  *   online_cpus are placed in a single cluster).
  *
- *   For details on functions, take a look at sched_gsn_edf.c
- *
  * Currently, we do not support changes in the number of online cpus.
  * If the num_online_cpus() dynamically changes, the plugin is broken.
  *
  * This version uses the simple approach and serializes all scheduling
- * decisions by the use of a queue lock. This is probably not the
- * best way to do it, but it should suffice for now.
+ * decisions by the use of a queue lock.
  */
 
 #include <linux/spinlock.h>
+#include <linux/cpumask.h>
 #include <linux/percpu.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -39,6 +37,9 @@
 #include <litmus/sched_plugin.h>
 #include <litmus/edf_common.h>
 #include <litmus/sched_trace.h>
+#include <litmus/trace.h>
+#include <litmus/fdso.h>
+#include <litmus/wait.h>
 
 #include <litmus/clustered.h>
 
@@ -70,52 +71,228 @@ typedef struct  {
 	struct clusterdomain*	cluster;	/* owning cluster */
 	struct task_struct*	linked;		/* only RT tasks */
 	struct task_struct*	scheduled;	/* only RT tasks */
-	atomic_t		will_schedule;	/* prevent unneeded IPIs */
 	struct bheap_node*	hn;
+
+	struct task_struct*	pd_task;	/* priority donation */
 } cpu_entry_t;
 
 /* one cpu_entry_t per CPU */
 DEFINE_PER_CPU(cpu_entry_t, cedf_cpu_entries);
 
-#define set_will_schedule() \
-	(atomic_set(&__get_cpu_var(cedf_cpu_entries).will_schedule, 1))
-#define clear_will_schedule() \
-	(atomic_set(&__get_cpu_var(cedf_cpu_entries).will_schedule, 0))
-#define test_will_schedule(cpu) \
-	(atomic_read(&per_cpu(cedf_cpu_entries, cpu).will_schedule))
-
 /*
  * In C-EDF there is a cedf domain _per_ cluster
  * The number of clusters is dynamically determined accordingly to the
  * total cpu number and the cluster size
  */
 typedef struct clusterdomain {
-	/* rt_domain for this cluster */
-	rt_domain_t	domain;
+	/* lock for this cluster */
+	raw_spinlock_t		cluster_lock;
+	/* ready queue for this cluster */
+	struct list_head        job_queue;
 	/* cpus in this cluster */
-	cpu_entry_t*	*cpus;
+	cpu_entry_t*		*cpus;
+	/* which cluster is this? */
+	int			cluster;
+	/* how many CPUs are in this cluster? */
+	int			num_cpus;
 	/* map of this cluster cpus */
-	cpumask_var_t	cpu_map;
+	cpumask_var_t		cpu_map;
 	/* the cpus queue themselves according to priority in here */
-	struct bheap_node *heap_node;
-	struct bheap      cpu_heap;
-	/* lock for this cluster */
-#define cluster_lock domain.ready_lock
+	struct bheap_node	*heap_node;
+	struct bheap		cpu_heap;
 } cedf_domain_t;
 
 /* a cedf_domain per cluster; allocation is done at init/activation time */
 cedf_domain_t *cedf;
 
+#define remote_cpu(cpu)		(&per_cpu(cedf_cpu_entries, cpu))
 #define remote_cluster(cpu)	((cedf_domain_t *) per_cpu(cedf_cpu_entries, cpu).cluster)
 #define task_cpu_cluster(task)	remote_cluster(get_partition(task))
 
 /* Uncomment WANT_ALL_SCHED_EVENTS if you want to see all scheduling
  * decisions in the TRACE() log; uncomment VERBOSE_INIT for verbose
  * information during the initialization of the plugin (e.g., topology)
-#define WANT_ALL_SCHED_EVENTS
+
  */
+#define WANT_ALL_SCHED_EVENTS
 #define VERBOSE_INIT
 
+#define NOT_WAITING 0
+#define WAITING_TO_BE_LINKED 1
+#define WAITING_FOR_DONATION_END 2
+
+
+static int is_waiting_to_be_linked(struct task_struct *t)
+{
+	return t && t->rt_param.priodon_state == WAITING_TO_BE_LINKED;
+}
+
+static int is_waiting_for_donation_end(struct task_struct *t)
+{
+	return t && t->rt_param.priodon_state == WAITING_FOR_DONATION_END;
+}
+
+
+/* When the task is currently not subject to cross-cluster
+ * migration. */
+#define NOT_XCLUSTER_MIGRATORY (-3)
+#define ON_HOME_CLUSTER (-2)
+
+struct migratory_prio_inh {
+	raw_spinlock_t lock;
+
+	struct task_struct *owner;
+	struct cpumask scheduling_candidates;
+
+	int scheduled_on;
+};
+
+#define MPI_EXIT_CS ((struct migratory_prio_inh *) 0x123)
+
+void mpi_init(struct migratory_prio_inh *mpi)
+{
+	raw_spin_lock_init(&mpi->lock);
+	cpumask_clear(&mpi->scheduling_candidates);
+	mpi->scheduled_on = NO_CPU;
+	mpi->owner = NULL;
+}
+
+/* assumes IRQ off */
+struct task_struct* mpi_try_to_schedule(struct migratory_prio_inh *mpi)
+{
+	struct task_struct *t = NULL;
+	int need_notify = 1;
+
+	/* Check whether we raced with the end of a critical section that
+	 * was migrated to a remote core. The remote core will send an IPI
+	 * when it is safe to schedule this task. */
+	if (mpi == MPI_EXIT_CS)
+		return NULL;
+
+	raw_spin_lock(&mpi->lock);
+
+	BUG_ON(!mpi->owner);
+
+	TRACE("trying to schedule mpi-task mpi:%p owner:%s/%d scheduled_on=%d\n",
+	      mpi, mpi->owner->comm, mpi->owner->pid,
+	      mpi->scheduled_on);
+
+	if (mpi->scheduled_on == NO_CPU &&
+	    is_present(mpi->owner) &&
+	    is_running(mpi->owner)) {
+		/* it's not scheduled, so grab it */
+		mpi->scheduled_on = smp_processor_id();
+		t = mpi->owner;
+		cpumask_clear_cpu(smp_processor_id(),
+				  &mpi->scheduling_candidates);
+		need_notify = 0;
+
+	} else if (mpi->scheduled_on == smp_processor_id()) {
+		/* we already got it, nothing to change */
+		if (is_present(mpi->owner) && is_running(mpi->owner)) {
+			need_notify = 0;
+			t = mpi->owner;
+		} else {
+			/* we can't actually schedule it */
+			mpi->scheduled_on = NO_CPU;
+		}
+	}
+
+	if (need_notify) {
+		/* can't get to it, let's register our interest */
+		cpumask_set_cpu(smp_processor_id(),
+				&mpi->scheduling_candidates);
+	}
+
+	raw_spin_unlock(&mpi->lock);
+
+	return t;
+}
+
+/* preempt - force a CPU to reschedule
+ */
+static void preempt(cpu_entry_t *entry)
+{
+	preempt_if_preemptable(entry->scheduled, entry->cpu);
+}
+
+void __mpi_notify_cpus(struct migratory_prio_inh *mpi)
+{
+	int cpu;
+
+	for_each_cpu(cpu, &mpi->scheduling_candidates) {
+		TRACE("notifying P%d\n", cpu);
+		preempt(remote_cpu(cpu)); /* XXX use propr smp_call */
+	}
+
+	cpumask_clear(&mpi->scheduling_candidates);
+}
+
+void mpi_notify_cpus(struct migratory_prio_inh *mpi)
+{
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&mpi->lock, flags);
+	__mpi_notify_cpus(mpi);
+	raw_spin_unlock_irqrestore(&mpi->lock, flags);
+}
+
+
+void mpi_deschedule(struct migratory_prio_inh *mpi,
+		    struct task_struct *preempted)
+{
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&mpi->lock, flags);
+
+	if (preempted == mpi->owner) {
+		mpi->scheduled_on = NO_CPU;
+		/* XXX copy out mask and drop lock first? */
+		__mpi_notify_cpus(mpi);
+	}
+
+	raw_spin_unlock_irqrestore(&mpi->lock, flags);
+}
+
+/* assumes IRQ off */
+void mpi_update_owner(struct migratory_prio_inh *mpi,
+		      struct task_struct *new_owner,
+		      int running_on)
+{
+	raw_spin_lock(&mpi->lock);
+
+	mpi->owner = new_owner;
+	mpi->scheduled_on = running_on;
+	if (running_on == NO_CPU)
+		__mpi_notify_cpus(mpi);
+
+	raw_spin_unlock(&mpi->lock);
+}
+
+/* assumes preempt off */
+static void mpi_migrate_back(void)
+{
+	struct task_struct *t = current;
+	int cpu;
+
+	cpu = smp_processor_id();
+
+	if (task_cpu_cluster(t) != remote_cluster(cpu) ||
+	    remote_cpu(cpu)->linked != t) {
+
+		TRACE_CUR("not on the linked CPU (%d), migrating back (part:%d)\n",
+			  tsk_rt(t)->linked_on,
+			  get_partition(t));
+
+		preempt_enable_no_resched();
+
+		schedule();
+
+		preempt_disable();
+	} else
+		tsk_rt(t)->mpi = NULL;
+}
+
 static int cpu_lower_prio(struct bheap_node *_a, struct bheap_node *_b)
 {
 	cpu_entry_t *a, *b;
@@ -150,6 +327,49 @@ static cpu_entry_t* lowest_prio_cpu(cedf_domain_t *cluster)
 	return hn->value;
 }
 
+static struct task_struct* select_job(struct task_struct *candidate)
+{
+	/* Special case: delegate can be used to mark a task as
+	 * pseudo-suspended. It should not be scheduled in this case. */
+	if (is_waiting_to_be_linked(candidate) ||
+	    is_waiting_for_donation_end(candidate))
+		candidate = NULL;
+
+	if (candidate && !(is_present(candidate) && is_running(candidate))) {
+		TRACE_TASK(candidate, "should be scheduled, but is not running\n");
+		candidate = NULL;
+	}
+
+	return candidate;
+}
+
+static int cedf_is_linked(struct task_struct* task)
+{
+	return task->rt_param.linked_on != NO_CPU;
+}
+
+static cpu_entry_t *cedf_linked_cpu(struct task_struct *task)
+{
+	if (cedf_is_linked(task))
+		return &per_cpu(cedf_cpu_entries, task->rt_param.linked_on);
+	else
+		return NULL;
+}
+
+static int cedf_preempt_linked_cpu(struct task_struct* task)
+{
+	cpu_entry_t *cpu = cedf_linked_cpu(task);
+	struct task_struct *should_schedule = select_job(task);
+
+	if (cpu && should_schedule &&
+	    cpu->scheduled != should_schedule) {
+		preempt(cpu);
+		return 1;
+	} else
+		return 0;
+}
+
+static void job_completion(struct task_struct *t, int forced);
 
 /* link_task_to_cpu - Update the link of a CPU.
  *                    Handles the case where the to-be-linked task is already
@@ -159,22 +379,33 @@ static noinline void link_task_to_cpu(struct task_struct* linked,
 				      cpu_entry_t *entry)
 {
 	cpu_entry_t *sched;
-	struct task_struct* tmp;
+	struct task_struct *tmp, *unlinked;
 	int on_cpu;
 
 	BUG_ON(linked && !is_realtime(linked));
 
-	/* Currently linked task is set to be unlinked. */
-	if (entry->linked) {
-		entry->linked->rt_param.linked_on = NO_CPU;
+	unlinked = entry->linked;
+
+	/* Currently linked task is will be unlinked. */
+	if (unlinked) {
+		unlinked->rt_param.linked_on = NO_CPU;
 	}
 
 	/* Link new task to CPU. */
 	if (linked) {
-		tsk_rt(linked)->completed = 0;
-		/* handle task is already scheduled somewhere! */
+
+		/* Handle task is already scheduled somewhere!
+		 * Special case with m-PI: it might be scheduled outside
+		 * of our cluster.
+		 */
 		on_cpu = linked->rt_param.scheduled_on;
-		if (on_cpu != NO_CPU) {
+		if (on_cpu != NO_CPU &&
+		    remote_cluster(on_cpu) != task_cpu_cluster(linked))  {
+			TRACE_TASK(linked, "linked, but already scheduled "
+				   "outside of cluster on CPU=%d\n", on_cpu);
+		}
+		if (on_cpu != NO_CPU &&
+		    remote_cluster(on_cpu) == task_cpu_cluster(linked))  {
 			sched = &per_cpu(cedf_cpu_entries, on_cpu);
 			/* this should only happen if not linked already */
 			BUG_ON(sched->linked == linked);
@@ -188,17 +419,46 @@ static noinline void link_task_to_cpu(struct task_struct* linked,
 				TRACE_TASK(linked,
 					   "already scheduled on %d, updating link.\n",
 					   sched->cpu);
+
 				tmp = sched->linked;
+
+				/* need to get priority donation right */
+				if (tmp && sched->pd_task == sched->linked) {
+					/* currently in a CS */
+					BUG_ON(linked->rt_param.donor_cpu != NO_CPU);
+					/* Entry just had a completion, there cannot
+					 * be a CS in progress. */
+					BUG_ON(entry->pd_task != NULL);
+
+					sched->pd_task = NULL;
+					entry->pd_task = tmp;
+					tmp->rt_param.donor_cpu = sched->cpu;
+				} else if (is_waiting_for_donation_end(tmp)) {
+					preempt(entry);
+				}
+
 				linked->rt_param.linked_on = sched->cpu;
 				sched->linked = linked;
 				update_cpu_position(sched);
 				linked = tmp;
 			}
+		} else if (linked->rt_param.donor_cpu != NO_CPU &&
+			   entry->cpu != linked->rt_param.donor_cpu) {
+			/* It's not linked, it's not scheduled, but it's got a
+			 * donor. We need to patch up the priority donation
+			 * info. */
+			entry->pd_task = linked;
+			sched = remote_cpu(linked->rt_param.donor_cpu);
+			linked->rt_param.donor_cpu = entry->cpu;
+			sched->pd_task = NULL;
+			if (is_waiting_for_donation_end(sched->linked))
+				preempt(sched);
 		}
 		if (linked) /* might be NULL due to swap */
 			linked->rt_param.linked_on = entry->cpu;
 	}
 	entry->linked = linked;
+
 #ifdef WANT_ALL_SCHED_EVENTS
 	if (linked)
 		TRACE_TASK(linked, "linked to %d.\n", entry->cpu);
@@ -206,6 +466,14 @@ static noinline void link_task_to_cpu(struct task_struct* linked,
 		TRACE("NULL linked to %d.\n", entry->cpu);
 #endif
 	update_cpu_position(entry);
+
+
+	if (is_waiting_for_donation_end(unlinked) &&
+	    is_completed(unlinked)) {
+		/* delayed job completion */
+		TRACE_TASK(unlinked, "delayed job completion\n");
+		job_completion(unlinked, 0);
+	}
 }
 
 /* unlink - Make sure a task is not linked any longer to an entry
@@ -215,50 +483,178 @@ static noinline void unlink(struct task_struct* t)
 {
     	cpu_entry_t *entry;
 
+	TRACE_TASK(t, "unlink (linked_on=%d)\n", t->rt_param.linked_on, in_list(&t->rt_param.list));
+
+	BUG_ON(t->rt_param.linked_on != NO_CPU && in_list(&t->rt_param.list));
+
 	if (t->rt_param.linked_on != NO_CPU) {
 		/* unlink */
 		entry = &per_cpu(cedf_cpu_entries, t->rt_param.linked_on);
 		t->rt_param.linked_on = NO_CPU;
 		link_task_to_cpu(NULL, entry);
-	} else if (is_queued(t)) {
-		/* This is an interesting situation: t is scheduled,
-		 * but was just recently unlinked.  It cannot be
-		 * linked anywhere else (because then it would have
-		 * been relinked to this CPU), thus it must be in some
-		 * queue. We must remove it from the list in this
-		 * case.
-		 *
-		 * in C-EDF case is should be somewhere in the queue for
-		 * its domain, therefore and we can get the domain using
-		 * task_cpu_cluster
-		 */
-		remove(&(task_cpu_cluster(t))->domain, t);
+	} else if (in_list(&t->rt_param.list)) {
+		/* Remove from queue of pending jobs. */
+		list_del(&t->rt_param.list);
 	}
 }
 
 
-/* preempt - force a CPU to reschedule
- */
-static void preempt(cpu_entry_t *entry)
+static int edf_queue_job(struct list_head *queue, struct task_struct *t)
 {
-	preempt_if_preemptable(entry->scheduled, entry->cpu);
+	unsigned int passed = 0;
+	struct list_head *pos;
+	struct task_struct *other;
+
+	BUG_ON(in_list(&t->rt_param.list));
+
+	/* sorted enqueue */
+	list_for_each(pos, queue)
+	{
+		other = list_entry(pos, struct task_struct, rt_param.list);
+		if (edf_higher_prio(t, other)) {
+			/* found correct position: insert after prev and return */
+			list_add(&t->rt_param.list, pos->prev);
+			return passed;
+		} else
+			passed++;
+	}
+
+	/* Fell through: hit end of list. */
+
+	list_add_tail(&t->rt_param.list, queue);
+
+	return passed;
 }
 
-/* requeue - Put an unlinked task into gsn-edf domain.
+static struct task_struct* job_queue_head(struct list_head *queue)
+{
+	if (list_empty(queue))
+		return NULL;
+	else
+		return list_entry(queue->next, struct task_struct, rt_param.list);
+}
+
+static struct task_struct* job_queue_first_runnable(struct list_head *queue)
+{
+	struct list_head *pos;
+	struct task_struct *task;
+
+	/* sorted enqueue */
+	list_for_each(pos, queue)
+	{
+		task = list_entry(pos, struct task_struct, rt_param.list);
+		task = select_job(task);
+		if (task && tsk_rt(task)->mpi)
+			task = mpi_try_to_schedule(tsk_rt(task)->mpi);
+		if (task
+		    && tsk_rt(task)->scheduled_on == NO_CPU
+		    && tsk_rt(task)->donor_cpu == NO_CPU)
+			return task;
+	}
+
+	return NULL;
+}
+
+
+static struct task_struct* dequeue_head(struct list_head *queue)
+{
+	struct task_struct* head = job_queue_head(queue);
+
+	if (head)
+		list_del(&head->rt_param.list);
+
+	return head;
+}
+
+static int edf_need_to_preempt(struct list_head *queue, struct task_struct *scheduled)
+{
+	if (list_empty(queue))
+		return 0;
+	if (!scheduled || !is_realtime(scheduled))
+		return 1;
+
+	return edf_higher_prio(job_queue_head(queue), scheduled);
+}
+
+static void check_for_preemptions(cedf_domain_t *cluster);
+
+enum hrtimer_restart on_cedf_release_timer(struct hrtimer *timer)
+{
+	/* We now that the calling thread/context is not holding the ready
+	 * queue lock because the hrtimer subsystem pushes the arming of
+	 * already expired timers into a softirq (see
+	 * hrtimer_enqueue_reprogram). Thus, even when arming an already
+	 * expired timer, the call to the callback function does not happen
+	 * until after the ready queue lock has been dropped.
+	*/
+
+	struct task_struct *task;
+	cedf_domain_t* cluster;
+	unsigned long flags;
+
+	task = container_of(timer, struct release_heap, timer)->task;
+	cluster = task_cpu_cluster(task);
+
+	raw_spin_lock_irqsave(&cluster->cluster_lock, flags);
+
+	TRACE_TASK(task, "released! complete:%d\n", task->rt_param.completed);
+	tsk_rt(task)->completed = 0;
+
+	edf_queue_job(&cluster->job_queue, task);
+	check_for_preemptions(cluster);
+
+	raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags);
+
+	return  HRTIMER_NORESTART;
+}
+
+void arm_timer_for_job_release(struct task_struct *t)
+{
+	struct hrtimer *timer = &tsk_rt(t)->rel_heap->timer;
+
+	if (hrtimer_active(timer)) {
+		TRACE_TASK(t, "BUG BUG BUG timer still active!\n");
+		TRACE_TASK(t, "giving up\n");
+		printk(KERN_CRIT "BUG BUG BUG\n");
+		return;
+	}
+	BUG_ON(hrtimer_active(timer));
+
+	timer->function = on_cedf_release_timer;
+	tsk_rt(t)->rel_heap->task = t;
+
+	TRACE_TASK(t, "arming job releas timer\n");
+
+	/* todo: release master support is missing */
+	__hrtimer_start_range_ns(timer,
+				 ns_to_ktime(get_release(t)),
+				 0, HRTIMER_MODE_ABS_PINNED, 0);
+
+	sched_trace_task_release(t);
+}
+
+/* requeue - Put an unlinked task into appropriate queue (ready or release).
  *           Caller must hold cedf_lock.
  */
 static noinline void requeue(struct task_struct* task)
 {
 	cedf_domain_t *cluster = task_cpu_cluster(task);
-	BUG_ON(!task);
-	/* sanity check before insertion */
-	BUG_ON(is_queued(task));
+	lt_t now = litmus_clock();
 
-	if (is_released(task, litmus_clock()))
-		__add_ready(&cluster->domain, task);
+	TRACE_TASK(task, "requeue: rel=%d queued=%d linked=%d\n",
+		   is_released(task, now),
+		   in_list(&task->rt_param.list),
+		   cedf_is_linked(task));
+
+	BUG_ON(task->rt_param.linked_on != NO_CPU && !is_released(task, litmus_clock()));
+
+	if (is_released(task, now)
+		 && !in_list(&task->rt_param.list)
+		 && !cedf_is_linked(task))
+		edf_queue_job(&cluster->job_queue, task);
 	else {
-		/* it has got to wait */
-		add_release(&cluster->domain, task);
+		if (!is_released(task, now))
+			TRACE_TASK(task, "not requeueing not-yet-released job\n");
 	}
 }
 
@@ -288,16 +684,18 @@ static cpu_entry_t* cedf_get_nearest_available_cpu(
 /* check for any necessary preemptions */
 static void check_for_preemptions(cedf_domain_t *cluster)
 {
-	struct task_struct *task;
+	struct task_struct *task, *unlinked;
 	cpu_entry_t *last;
 
 	for(last = lowest_prio_cpu(cluster);
-	    edf_preemption_needed(&cluster->domain, last->linked);
+	    edf_need_to_preempt(&cluster->job_queue, last->linked);
 	    last = lowest_prio_cpu(cluster)) {
 		/* preemption necessary */
-		task = __take_ready(&cluster->domain);
+		task = dequeue_head(&cluster->job_queue);
 		TRACE("check_for_preemptions: attempting to link task %d to %d\n",
 		      task->pid, last->cpu);
+		unlinked = last->linked;
+		link_task_to_cpu(task, last);
 #ifdef CONFIG_SCHED_CPU_AFFINITY
 		{
 			cpu_entry_t *affinity =
@@ -309,10 +707,9 @@ static void check_for_preemptions(cedf_domain_t *cluster)
 				requeue(last->linked);
 		}
 #else
-		if (requeue_preempted_job(last->linked))
-			requeue(last->linked);
+		if (requeue_preempted_job(unlinked))
+			requeue(unlinked);
 #endif
-		link_task_to_cpu(task, last);
 		preempt(last);
 	}
 }
@@ -325,42 +722,38 @@ static noinline void cedf_job_arrival(struct task_struct* task)
 
 	requeue(task);
 	check_for_preemptions(cluster);
-}
-
-static void cedf_release_jobs(rt_domain_t* rt, struct bheap* tasks)
-{
-	cedf_domain_t* cluster = container_of(rt, cedf_domain_t, domain);
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&cluster->cluster_lock, flags);
-
-	__merge_ready(&cluster->domain, tasks);
-	check_for_preemptions(cluster);
-
-	raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags);
+	cedf_preempt_linked_cpu(task);
 }
 
 /* caller holds cedf_lock */
-static noinline void job_completion(struct task_struct *t, int forced)
+static void job_completion(struct task_struct *t, int forced)
 {
+	lt_t now = litmus_clock();
+
 	BUG_ON(!t);
 
 	sched_trace_task_completion(t, forced);
 
-	TRACE_TASK(t, "job_completion().\n");
+	TRACE_TASK(t, "job_completion(). lateness=%ld\n",
+		   (long) now - (long) get_deadline(t));
 
 	/* set flags */
 	tsk_rt(t)->completed = 1;
 	/* prepare for next period */
 	prepare_for_next_period(t);
-	if (is_released(t, litmus_clock()))
+	if (is_released(t, now))
 		sched_trace_task_release(t);
 	/* unlink */
 	unlink(t);
 	/* requeue
 	 * But don't requeue a blocking task. */
-	if (is_running(t))
-		cedf_job_arrival(t);
+	tsk_rt(t)->completed = 0;
+	if (is_present(t) && is_running(t)) {
+		if (!is_released(t, now))
+			arm_timer_for_job_release(t);
+		else
+			cedf_job_arrival(t);
+	}
 }
 
 /* cedf_tick - this function is called for every local timer
@@ -377,7 +770,6 @@ static void cedf_tick(struct task_struct* t)
 			 * preemptable again
 			 */
 			litmus_reschedule_local();
-			set_will_schedule();
 			TRACE("cedf_scheduler_tick: "
 			      "%d is preemptable "
 			      " => FORCE_RESCHED\n", t->pid);
@@ -415,8 +807,9 @@ static struct task_struct* cedf_schedule(struct task_struct * prev)
 {
 	cpu_entry_t* entry = &__get_cpu_var(cedf_cpu_entries);
 	cedf_domain_t *cluster = entry->cluster;
-	int out_of_time, sleep, preempt, np, exists, blocks;
+	int out_of_time, sleep, preempt, np, exists, blocks, dead, exiting;
 	struct task_struct* next = NULL;
+	struct task_struct* should_schedule = NULL;
 
 #ifdef CONFIG_RELEASE_MASTER
 	/* Bail out early if we are the release master.
@@ -429,10 +822,17 @@ static struct task_struct* cedf_schedule(struct task_struct * prev)
 #endif
 
 	raw_spin_lock(&cluster->cluster_lock);
-	clear_will_schedule();
 
 	/* sanity checking */
 	BUG_ON(entry->scheduled && entry->scheduled != prev);
+	if (entry->scheduled && !is_realtime(prev)) {
+		TRACE_TASK(prev, "BUG BUG BUG :: not real-time\n");
+		TRACE_TASK(entry->scheduled, "scheduled instead\n");
+		should_schedule = NULL;
+		next = NULL;
+		exists = blocks = np = 0;
+		goto bailout;
+	}
 	BUG_ON(entry->scheduled && !is_realtime(prev));
 	BUG_ON(is_realtime(prev) && !entry->scheduled);
 
@@ -444,7 +844,51 @@ static struct task_struct* cedf_schedule(struct task_struct * prev)
 				  budget_exhausted(entry->scheduled);
 	np 	    = exists && is_np(entry->scheduled);
 	sleep	    = exists && is_completed(entry->scheduled);
-	preempt     = entry->scheduled != entry->linked;
+	dead	    = exists && entry->scheduled->state == TASK_DEAD;
+	exiting	    = exists && entry->scheduled->flags & PF_EXITING;
+
+	/* check whether linked is available */
+	should_schedule = select_job(entry->linked);
+
+	BUG_ON(entry->pd_task && !entry->linked);
+
+	/* Check for priority donation */
+	if (!should_schedule && entry->linked) {
+
+		if (is_waiting_for_donation_end(entry->linked) &&
+		    !entry->pd_task) {
+			TRACE_TASK(entry->linked, "no longer waiting for donation end\n");
+			entry->linked->rt_param.priodon_state = NOT_WAITING;
+
+			if (is_completed(entry->linked)) {
+				TRACE_TASK(entry->linked, "delayed job completion processed\n");
+				job_completion(entry->linked, 0);
+				sleep = exists && is_completed(entry->scheduled);
+			}
+
+			should_schedule = select_job(entry->linked);
+
+		} else if (is_waiting_to_be_linked(entry->linked)) {
+			TRACE_TASK(entry->linked, "no longer waiting to be linked\n");
+			entry->linked->rt_param.priodon_state = NOT_WAITING;
+			should_schedule = select_job(entry->linked);
+
+			/* A task is only blocked once by priority donation */
+			BUG_ON(entry->pd_task);
+		}
+	}
+
+	if (entry->pd_task) {
+		should_schedule = select_job(entry->pd_task);
+
+		/* special case: donor completion needs to be delayed */
+		if (is_completed(entry->linked)) {
+			TRACE_TASK(entry->linked, "job completion delayed\n");
+			entry->linked->rt_param.priodon_state = WAITING_FOR_DONATION_END;
+		}
+	}
+
+	preempt     = entry->scheduled != should_schedule;
 
 #ifdef WANT_ALL_SCHED_EVENTS
 	TRACE_TASK(prev, "invoked cedf_schedule.\n");
@@ -453,17 +897,22 @@ static struct task_struct* cedf_schedule(struct task_struct * prev)
 	if (exists)
 		TRACE_TASK(prev,
 			   "blocks:%d out_of_time:%d np:%d sleep:%d preempt:%d "
-			   "state:%d sig:%d\n",
+			   "state:%d sig:%d dead:%d exit_state:%d exiting:%d\n",
 			   blocks, out_of_time, np, sleep, preempt,
-			   prev->state, signal_pending(prev));
-	if (entry->linked && preempt)
+			   prev->state, signal_pending(prev), dead, prev->exit_state, exiting);
+	if (should_schedule && preempt)
 		TRACE_TASK(prev, "will be preempted by %s/%d\n",
-			   entry->linked->comm, entry->linked->pid);
+			   should_schedule->comm, should_schedule->pid);
 
 
 	/* If a task blocks we have no choice but to reschedule.
 	 */
-	if (blocks)
+//	if (blocks)
+//		unlink(entry->scheduled);
+
+
+	/* Exiting tasks do not count as having pending jobs. */
+	if (unlikely(blocks && exiting))
 		unlink(entry->scheduled);
 
 	/* Request a sys_exit_np() call if we would like to preempt but cannot.
@@ -472,7 +921,7 @@ static struct task_struct* cedf_schedule(struct task_struct * prev)
 	 * hurt.
 	 */
 	if (np && (out_of_time || preempt || sleep)) {
-		unlink(entry->scheduled);
+//		unlink(entry->scheduled);
 		request_exit_np(entry->scheduled);
 	}
 
@@ -480,24 +929,65 @@ static struct task_struct* cedf_schedule(struct task_struct * prev)
 	 * budget or wants to sleep completes. We may have to reschedule after
 	 * this. Don't do a job completion if we block (can't have timers running
 	 * for blocked jobs).
+	 *
+	 * Priority donation: priority donors may not complete.o
 	 */
-	if (!np && (out_of_time || sleep) && !blocks)
+	if (!np && (out_of_time || sleep) && !blocks
+	    && !(entry->linked == entry->scheduled
+		 && is_waiting_for_donation_end(entry->linked)))
 		job_completion(entry->scheduled, !sleep);
 
 	/* Link pending task if we became unlinked.
 	 */
-	if (!entry->linked)
-		link_task_to_cpu(__take_ready(&cluster->domain), entry);
+	if (!entry->linked) {
+		link_task_to_cpu(dequeue_head(&cluster->job_queue), entry);
+		if (is_waiting_to_be_linked(entry->linked)) {
+			TRACE_TASK(entry->linked, "no longer waiting to be linked\n");
+			entry->linked->rt_param.priodon_state = NOT_WAITING;
+
+			/* A task is only blocked once by priority donation */
+			BUG_ON(entry->pd_task);
+		}
+	}
+
+	TRACE_TASK(should_schedule, "PRE PD\n");
+
+	should_schedule = select_job(entry->pd_task);
 
+	TRACE_TASK(should_schedule, "POST PD\n");
+	if (!should_schedule) {
+		/* check for migratory priority inheritance */
+		if (entry->linked && entry->linked->rt_param.mpi)
+			should_schedule = mpi_try_to_schedule(entry->linked->rt_param.mpi);
+		else
+			should_schedule = select_job(entry->linked);
+	}
+
+	TRACE_TASK(should_schedule, "should_schedule\n");
+
+	if (entry->linked && entry->linked != should_schedule
+	    && should_schedule)
+		TRACE_TASK(entry->linked, "linked, but delegates to %s/%d\n",
+			   should_schedule->comm, should_schedule->pid);
+
+	if (!should_schedule) {
+		/* Neither linked nor delegate are available. */
+		/* Pick someone else. */
+		should_schedule = job_queue_first_runnable(&cluster->job_queue);
+	}
+
+
+bailout:
 	/* The final scheduling decision. Do we need to switch for some reason?
 	 * If linked is different from scheduled, then select linked as next.
 	 */
 	if ((!np || blocks) &&
-	    entry->linked != entry->scheduled) {
+	    should_schedule != entry->scheduled) {
 		/* Schedule a linked job? */
-		if (entry->linked) {
-			entry->linked->rt_param.scheduled_on = entry->cpu;
-			next = entry->linked;
+		if (should_schedule) {
+			should_schedule->rt_param.scheduled_on = entry->cpu;
+			next = should_schedule;
+			TRACE_TASK(should_schedule, "should_schedule!\n");
 		}
 		if (entry->scheduled) {
 			/* not gonna be scheduled soon */
@@ -528,13 +1018,39 @@ static struct task_struct* cedf_schedule(struct task_struct * prev)
 }
 
 
+// XXX When a task wakes up, we also need to check whether it
+// has any notifiers attached.
+
+// XXX What if prev is already scheduled elsewhere?
+// XXX Need to handle completion while migrated away!
+
 /* _finish_switch - we just finished the switch away from prev
  */
 static void cedf_finish_switch(struct task_struct *prev)
 {
 	cpu_entry_t* 	entry = &__get_cpu_var(cedf_cpu_entries);
+	cedf_domain_t *cluster  = task_cpu_cluster(prev);
+	unsigned long flags;
+
+	/* check for preemption notifiers */
 
 	entry->scheduled = is_realtime(current) ? current : NULL;
+	if (is_realtime(prev) && prev->rt_param.mpi) {
+		if (prev->rt_param.mpi == MPI_EXIT_CS) {
+			/* Signal to remote cores that is now safe
+			 * to consider this task again. */
+			prev->rt_param.mpi = NULL;
+		} else
+			mpi_deschedule(prev->rt_param.mpi, prev);
+	}
+
+	if (cluster != entry->cluster) {
+		/* needs to migrate back to its own cluster */
+		raw_spin_lock_irqsave(&cluster->cluster_lock, flags);
+		cedf_preempt_linked_cpu(prev);
+		raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags);
+	}
+
 #ifdef WANT_ALL_SCHED_EVENTS
 	TRACE_TASK(prev, "switched away from\n");
 #endif
@@ -549,13 +1065,15 @@ static void cedf_task_new(struct task_struct * t, int on_rq, int running)
 	cpu_entry_t* 		entry;
 	cedf_domain_t*		cluster;
 
-	TRACE("gsn edf: task new %d\n", t->pid);
+	TRACE("cedf: task new %d\n", t->pid);
 
 	/* the cluster doesn't change even if t is running */
 	cluster = task_cpu_cluster(t);
 
 	raw_spin_lock_irqsave(&cluster->cluster_lock, flags);
 
+	t->rt_param.donor_cpu = NO_CPU;
+
 	/* setup job params */
 	release_at(t, litmus_clock());
 
@@ -595,36 +1113,44 @@ static void cedf_task_wake_up(struct task_struct *task)
 	cluster = task_cpu_cluster(task);
 
 	raw_spin_lock_irqsave(&cluster->cluster_lock, flags);
-	now = litmus_clock();
-	if (is_tardy(task, now)) {
-		/* new sporadic release */
-		release_at(task, now);
-		sched_trace_task_release(task);
-	}
-	else {
-		if (task->rt.time_slice) {
-			/* came back in time before deadline
-			*/
-			tsk_rt(task)->completed = 0;
+
+	if (task->rt_param.donor_cpu != NO_CPU) {
+		/* preempt the CPU where the donor is located at */
+		preempt(&per_cpu(cedf_cpu_entries, task->rt_param.donor_cpu));
+	} else if (task->rt_param.mpi) {
+		mpi_notify_cpus(task->rt_param.mpi);
+	} else {
+		now = litmus_clock();
+		if (is_tardy(task, now)) {
+			/* new sporadic release */
+			release_at(task, now);
+			sched_trace_task_release(task);
 		}
+		else {
+			if (task->rt.time_slice) {
+				/* came back in time before deadline
+				 */
+				tsk_rt(task)->completed = 0;
+			}
+		}
+		cedf_job_arrival(task);
 	}
-	cedf_job_arrival(task);
 	raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags);
 }
 
 static void cedf_task_block(struct task_struct *t)
 {
-	unsigned long flags;
+//	unsigned long flags;
 	cedf_domain_t *cluster;
 
 	TRACE_TASK(t, "block at %llu\n", litmus_clock());
 
 	cluster = task_cpu_cluster(t);
 
-	/* unlink if necessary */
-	raw_spin_lock_irqsave(&cluster->cluster_lock, flags);
-	unlink(t);
-	raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags);
+//	/* unlink if necessary */
+//	raw_spin_lock_irqsave(&cluster->cluster_lock, flags);
+//	unlink(t);
+//	raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags);
 
 	BUG_ON(!is_realtime(t));
 }
@@ -634,16 +1160,25 @@ static void cedf_task_exit(struct task_struct * t)
 {
 	unsigned long flags;
 	cedf_domain_t *cluster = task_cpu_cluster(t);
+	cpu_entry_t *cpu;
 
 	/* unlink if necessary */
 	raw_spin_lock_irqsave(&cluster->cluster_lock, flags);
+
+	cpu = &__get_cpu_var(cedf_cpu_entries);
+	TRACE_TASK(t, "exiting linked_on=%d scheduled_on=%d\n",
+		   tsk_rt(t)->linked_on,
+		   tsk_rt(t)->scheduled_on);
+	TRACE_TASK(cpu->linked, "locally linked\n");
+	TRACE_TASK(cpu->scheduled, "locally scheduled\n");
+
 	unlink(t);
 	if (tsk_rt(t)->scheduled_on != NO_CPU) {
-		cpu_entry_t *cpu;
 		cpu = &per_cpu(cedf_cpu_entries, tsk_rt(t)->scheduled_on);
 		cpu->scheduled = NULL;
 		tsk_rt(t)->scheduled_on = NO_CPU;
 	}
+	check_for_preemptions(cluster);
 	raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags);
 
 	BUG_ON(!is_realtime(t));
@@ -652,6 +1187,10 @@ static void cedf_task_exit(struct task_struct * t)
 
 static long cedf_admit_task(struct task_struct* tsk)
 {
+	struct hrtimer *timer = &tsk_rt(tsk)->rel_heap->timer;
+
+	hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+
 	return task_cpu(tsk) == tsk->rt_param.task_params.cpu ? 0 : -EINVAL;
 }
 
@@ -739,17 +1278,19 @@ static long cedf_activate_plugin(void)
 	/* initialize clusters */
 	cedf = kmalloc(num_clusters * sizeof(cedf_domain_t), GFP_ATOMIC);
 	for (i = 0; i < num_clusters; i++) {
-
+		cedf[i].cluster = i;
 		cedf[i].cpus = kmalloc(cluster_size * sizeof(cpu_entry_t),
 				GFP_ATOMIC);
 		cedf[i].heap_node = kmalloc(
 				cluster_size * sizeof(struct bheap_node),
 				GFP_ATOMIC);
 		bheap_init(&(cedf[i].cpu_heap));
-		edf_domain_init(&(cedf[i].domain), NULL, cedf_release_jobs);
+		INIT_LIST_HEAD(&cedf[i].job_queue);
+		raw_spin_lock_init(&cedf[i].cluster_lock);
 
 		if(!zalloc_cpumask_var(&cedf[i].cpu_map, GFP_ATOMIC))
 			return -ENOMEM;
+		cedf[i].num_cpus = 0;
 #ifdef CONFIG_RELEASE_MASTER
 		cedf[i].domain.release_master = atomic_read(&release_master_cpu);
 #endif
@@ -785,7 +1326,6 @@ static long cedf_activate_plugin(void)
 
 				entry = &per_cpu(cedf_cpu_entries, ccpu);
 				cedf[i].cpus[cpu_count] = entry;
-				atomic_set(&entry->will_schedule, 0);
 				entry->cpu = ccpu;
 				entry->cluster = &cedf[i];
 				entry->hn = &(cedf[i].heap_node[cpu_count]);
@@ -795,12 +1335,14 @@ static long cedf_activate_plugin(void)
 
 				entry->linked = NULL;
 				entry->scheduled = NULL;
+				entry->pd_task = NULL;
 #ifdef CONFIG_RELEASE_MASTER
 				/* only add CPUs that should schedule jobs */
 				if (entry->cpu != entry->cluster->domain.release_master)
 #endif
 					update_cpu_position(entry);
 			}
+			cedf[i].num_cpus = cpu_count;
 			/* done with this cluster */
 			break;
 		}
@@ -811,6 +1353,494 @@ static long cedf_activate_plugin(void)
 	return 0;
 }
 
+
+#ifdef CONFIG_LITMUS_LOCKING
+
+static void priodon_become_eligible(void)
+{
+	struct task_struct* t = current;
+	cpu_entry_t* cpu;
+	int started = 0;
+
+	while (!started) {
+
+		preempt_disable();
+
+		cpu = &__get_cpu_var(cedf_cpu_entries);
+
+		BUG_ON(cpu->pd_task == t);
+
+		if (cpu->linked == t) {
+			/* We observed that we are linked.
+			 * => We are among the $m$ highest-priority pending jobs.
+			 * => Start the critical section. preempt_disable() ensures
+			 *    that this does not race with other critical
+			 *    sections starting on the same CPU.
+			 */
+
+
+			if (cpu->pd_task) {
+				/* Uh oh, we are currently a priority donor,
+				 * so we are not allowed to start our own
+				 * critical section yet.
+				 */
+				TRACE_TASK(t, "must wait for donation to end\n");
+
+				t->rt_param.priodon_state = WAITING_FOR_DONATION_END;
+				preempt_enable_no_resched();
+
+				schedule();
+			} else {
+				started = 1;
+				cpu->pd_task = t;
+				t->rt_param.donor_cpu = cpu->cpu;
+				preempt_enable();
+			}
+		} else {
+			/* We are currently not linked. Must wait. */
+			t->rt_param.priodon_state = WAITING_TO_BE_LINKED;
+			TRACE_TASK(t, "must wait to be linked\n");
+			preempt_enable_no_resched();
+			schedule();
+		}
+	}
+}
+
+static void priodon_complete_request(void)
+{
+	struct task_struct* t = current;
+	cpu_entry_t* cpu;
+
+	preempt_disable();
+
+	cpu = &__get_cpu_var(cedf_cpu_entries);
+
+	BUG_ON(cpu->pd_task != t);
+
+	cpu->pd_task = NULL;
+	t->rt_param.donor_cpu = NO_CPU;
+
+	/* Reschedule if we became unlinked during
+	 * the critical section.
+	 */
+	if (cpu->linked != t)
+		preempt(cpu);
+
+	preempt_enable();
+}
+
+/* struct for semaphore with priority inheritance */
+struct omlp_semaphore {
+	struct litmus_lock litmus_lock;
+
+	/* current resource holder */
+	struct task_struct *owner;
+
+	/* FIFO queue of waiting tasks */
+	wait_queue_head_t fifo_wait;
+};
+
+static inline struct omlp_semaphore* omlp_from_lock(struct litmus_lock* lock)
+{
+	return container_of(lock, struct omlp_semaphore, litmus_lock);
+}
+
+static int cedf_omlp_lock(struct litmus_lock* l)
+{
+	struct task_struct* t = current;
+	struct omlp_semaphore *sem = omlp_from_lock(l);
+	wait_queue_t wait;
+	unsigned long flags;
+
+	if (!is_realtime(t))
+		return -EPERM;
+
+	priodon_become_eligible();
+
+	spin_lock_irqsave(&sem->fifo_wait.lock, flags);
+
+	if (sem->owner) {
+		/* resource is not free => must suspend and wait */
+
+		init_waitqueue_entry(&wait, t);
+
+		set_task_state(t, TASK_UNINTERRUPTIBLE);
+
+		__add_wait_queue_tail_exclusive(&sem->fifo_wait, &wait);
+
+		TS_LOCK_SUSPEND;
+
+		spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
+
+		schedule();
+
+		TS_LOCK_RESUME;
+
+		BUG_ON(sem->owner != t);
+	} else {
+		/* it's ours now */
+		sem->owner = t;
+
+		spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
+	}
+
+	return 0;
+}
+
+static int cedf_omlp_unlock(struct litmus_lock* l)
+{
+	struct task_struct *t = current, *next;
+	struct omlp_semaphore *sem = omlp_from_lock(l);
+	unsigned long flags;
+	int err = 0;
+
+	spin_lock_irqsave(&sem->fifo_wait.lock, flags);
+
+	if (sem->owner != t) {
+		err = -EINVAL;
+		spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
+		goto out;
+	}
+
+	/* check if there are jobs waiting for this resource */
+	next = __waitqueue_remove_first(&sem->fifo_wait);
+	if (next) {
+		/* next becomes the resouce holder */
+		sem->owner = next;
+		TRACE_CUR("lock ownership passed to %s/%d\n", next->comm, next->pid);
+
+		/* wake up next */
+		wake_up_process(next);
+	} else
+		/* becomes available */
+		sem->owner = NULL;
+
+	spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
+
+	priodon_complete_request();
+
+out:
+	return err;
+}
+
+static int cedf_omlp_close(struct litmus_lock* l)
+{
+	struct task_struct *t = current;
+	struct omlp_semaphore *sem = omlp_from_lock(l);
+	unsigned long flags;
+
+	int owner;
+
+	spin_lock_irqsave(&sem->fifo_wait.lock, flags);
+
+	owner = sem->owner == t;
+
+	spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
+
+	if (owner)
+		cedf_omlp_unlock(l);
+
+	return 0;
+}
+
+static void cedf_omlp_free(struct litmus_lock* lock)
+{
+	kfree(omlp_from_lock(lock));
+}
+
+static struct litmus_lock_ops cedf_omlp_lock_ops = {
+	.close  = cedf_omlp_close,
+	.lock   = cedf_omlp_lock,
+	.unlock = cedf_omlp_unlock,
+	.deallocate = cedf_omlp_free,
+};
+
+static struct litmus_lock* cedf_new_omlp(void)
+{
+	struct omlp_semaphore* sem;
+
+	sem = kmalloc(sizeof(*sem), GFP_KERNEL);
+	if (!sem)
+		return NULL;
+
+	sem->owner   = NULL;
+	init_waitqueue_head(&sem->fifo_wait);
+	sem->litmus_lock.ops = &cedf_omlp_lock_ops;
+
+	return &sem->litmus_lock;
+}
+
+// xxxx
+
+struct omip_per_cluster_info {
+	/* per-cluster FIFO queue of waiting tasks */
+	wait_queue_head_t fifo_wait;
+	unsigned int queued;
+
+	/* Priority queue of waiting tasks, on each cluster. */
+	wait_queue_head_t prio_wait;
+};
+
+static void omip_per_cluster_init(struct omip_per_cluster_info *cluster)
+{
+	init_waitqueue_head(&cluster->fifo_wait);
+	init_waitqueue_head(&cluster->prio_wait);
+	cluster->queued = 0;
+}
+
+
+/* struct for semaphore with priority inheritance */
+struct omip_semaphore {
+	struct litmus_lock litmus_lock;
+
+	/* information for migratory priority inheritance */
+	struct migratory_prio_inh mpi;
+
+	struct task_struct *owner;
+
+	/* FIFO queue of waiting tasks */
+	wait_queue_head_t fifo_wait;
+
+	/* per-cluster queues */
+	struct omip_per_cluster_info cluster[NR_CPUS];
+};
+
+static inline struct omip_semaphore* omip_from_lock(struct litmus_lock* lock)
+{
+	return container_of(lock, struct omip_semaphore, litmus_lock);
+}
+
+static void omip_enqueue(prio_wait_queue_t *wait,
+			 struct omip_semaphore *sem,
+			 struct task_struct *t)
+{
+	int c = get_partition(t);
+	cedf_domain_t *cluster = remote_cluster(c);
+
+	if (sem->cluster[c].queued >= cluster->num_cpus) {
+		/* must go into priority queue */
+		TRACE_TASK(t, "sem=%p added to prio queue in cluster C%\n", sem, c);
+		__add_wait_queue_prio_exclusive(&sem->cluster[c].prio_wait, wait);
+	} else if (sem->cluster[c].queued) {
+		/* must go into FIFO queue */
+		TRACE_TASK(t, "sem=%p added to FIFO queue in cluster C%\n", sem, c);
+		__add_wait_queue_tail_exclusive(&sem->cluster[c].fifo_wait,
+						&wait->wq);
+	} else {
+		/* empty, t immediately progresses to the global queue */
+		TRACE_TASK(t, "sem=%p added to global queue in cluster C%\n", sem, c);
+		__add_wait_queue_tail_exclusive(&sem->fifo_wait,
+						&wait->wq);
+	}
+
+	sem->cluster[c].queued++;
+}
+
+static void omip_dequeue(struct omip_semaphore *sem, struct task_struct *t)
+{
+	int c = get_partition(t);
+	struct list_head* first;
+
+	BUG_ON(!sem->cluster[c].queued);
+
+	sem->cluster[c].queued--;
+
+	/* move from prio to FIFO queue within cluster */
+	if (waitqueue_active(&sem->cluster[c].prio_wait)) {
+		TRACE_TASK(t, "sem=%p moves head of PRIO queue to FIFO queue C%d\n", sem, c);
+		first = sem->cluster[c].prio_wait.task_list.next;
+		list_move_tail(first, &sem->cluster[c].fifo_wait.task_list);
+	}
+
+	/* move new head from local to global queue */
+	if (waitqueue_active(&sem->cluster[c].fifo_wait)) {
+		TRACE_TASK(t, "sem=%p moves head of FIFO queue to global queue C%d\n", sem, c);
+		first = sem->cluster[c].fifo_wait.task_list.next;
+		list_move_tail(first, &sem->fifo_wait.task_list);
+	}
+}
+
+static int cedf_omip_lock(struct litmus_lock* l)
+{
+	struct task_struct* t = current;
+	struct omip_semaphore *sem = omip_from_lock(l);
+	prio_wait_queue_t wait;
+	unsigned long flags;
+
+	if (!is_realtime(t))
+		return -EPERM;
+
+	spin_lock_irqsave(&sem->fifo_wait.lock, flags);
+
+	t->rt_param.mpi = &sem->mpi;
+
+	if (sem->owner) {
+		/* resource is not free => must suspend and wait */
+
+		init_prio_waitqueue_entry(&wait, t, get_deadline(t));
+
+		set_task_state(t, TASK_UNINTERRUPTIBLE);
+
+		omip_enqueue(&wait, sem, t);
+
+		TS_LOCK_SUSPEND;
+
+		spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
+
+		schedule();
+
+		TS_LOCK_RESUME;
+
+		BUG_ON(sem->owner != t);
+	} else {
+		/* it's ours now */
+		sem->owner = t;
+		sem->cluster[get_partition(t)].queued++;
+
+		mpi_update_owner(&sem->mpi, t, smp_processor_id());
+
+		spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
+	}
+
+	return 0;
+}
+
+static int cedf_omip_unlock(struct litmus_lock* l)
+{
+	struct task_struct *t = current, *next;
+	struct omip_semaphore *sem = omip_from_lock(l);
+	unsigned long flags;
+	int err = 0;
+
+	preempt_disable();
+
+	spin_lock_irqsave(&sem->fifo_wait.lock, flags);
+
+	if (sem->owner != t) {
+		err = -EINVAL;
+		spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
+		goto out;
+	}
+
+	omip_dequeue(sem, t);
+
+	/* check if there are jobs waiting for this resource */
+	next = __waitqueue_remove_first(&sem->fifo_wait);
+	if (next) {
+		/* next becomes the resouce holder */
+		sem->owner = next;
+		TRACE_CUR("lock ownership passed to %s/%d\n", next->comm, next->pid);
+
+		/* wake up next */
+		wake_up_process(next);
+	} else
+		/* becomes available */
+		sem->owner = NULL;
+
+	/* Mark us that we are no longer holding a resource. This
+	 * is required to prevent scheduler races when we have to
+	 * migrate back to our core at the end of a CS. */
+	tsk_rt(t)->mpi = MPI_EXIT_CS;
+	/* Tell everyone else who the new owner is and notify
+	 * other CPUs if required. */
+	mpi_update_owner(&sem->mpi, sem->owner, NO_CPU);
+
+	spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
+
+	mpi_migrate_back();
+
+out:
+	preempt_enable();
+	return err;
+}
+
+static int cedf_omip_close(struct litmus_lock* l)
+{
+	struct task_struct *t = current;
+	struct omip_semaphore *sem = omip_from_lock(l);
+	unsigned long flags;
+
+	int owner;
+
+	spin_lock_irqsave(&sem->fifo_wait.lock, flags);
+
+	owner = sem->owner == t;
+
+	spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
+
+	if (owner)
+		cedf_omip_unlock(l);
+
+	return 0;
+}
+
+static void cedf_omip_free(struct litmus_lock* lock)
+{
+	kfree(omip_from_lock(lock));
+}
+
+static struct litmus_lock_ops cedf_omip_lock_ops = {
+	.close  = cedf_omip_close,
+	.lock   = cedf_omip_lock,
+	.unlock = cedf_omip_unlock,
+	.deallocate = cedf_omip_free,
+};
+
+static struct litmus_lock* cedf_new_omip(void)
+{
+	struct omip_semaphore* sem;
+	int i;
+
+	sem = kmalloc(sizeof(*sem), GFP_KERNEL);
+	if (!sem)
+		return NULL;
+
+	sem->owner   = NULL;
+	init_waitqueue_head(&sem->fifo_wait);
+	mpi_init(&sem->mpi);
+	sem->litmus_lock.ops = &cedf_omip_lock_ops;
+	for (i = 0; i < num_clusters; i++)
+		omip_per_cluster_init(sem->cluster + i);
+
+	return &sem->litmus_lock;
+}
+
+
+static long cedf_allocate_lock(struct litmus_lock **lock, int type,
+			       void* __user unused)
+{
+	int err = -ENXIO;
+
+	switch (type) {
+
+	case OMLP_SEM:
+		/* O(m) Multiprocessor Locking Protocol */
+		*lock  = cedf_new_omlp();
+		if (*lock)
+			err = 0;
+		else
+			err = -ENOMEM;
+		break;
+
+	case OMIP_SEM:
+		/* O(m) Multiprocessor Independence-Preserving Locking Protocol */
+		*lock  = cedf_new_omip();
+		if (*lock)
+			err = 0;
+		else
+			err = -ENOMEM;
+		break;
+
+	};
+
+
+	return err;
+}
+
+
+#endif
+
+
 /*	Plugin object	*/
 static struct sched_plugin cedf_plugin __cacheline_aligned_in_smp = {
 	.plugin_name		= "C-EDF",
@@ -824,6 +1854,9 @@ static struct sched_plugin cedf_plugin __cacheline_aligned_in_smp = {
 	.task_block		= cedf_task_block,
 	.admit_task		= cedf_admit_task,
 	.activate_plugin	= cedf_activate_plugin,
+#ifdef CONFIG_LITMUS_LOCKING
+	.allocate_lock		= cedf_allocate_lock,
+#endif
 };
 
 static struct proc_dir_entry *cluster_file = NULL, *cedf_dir = NULL;
diff --git a/litmus/sched_pfp.c b/litmus/sched_pfp.c
index 91e5239..0e875a3 100644
--- a/litmus/sched_pfp.c
+++ b/litmus/sched_pfp.c
@@ -182,7 +182,7 @@ static struct task_struct* pfp_schedule(struct task_struct * prev)
 	np 	    = exists && is_np(pfp->scheduled);
 	sleep	    = exists && is_completed(pfp->scheduled);
 	migrate     = exists && get_partition(pfp->scheduled) != pfp->cpu;
-	preempt     = migrate || fp_preemption_needed(&pfp->ready_queue, prev);
+	preempt     = !blocks && (migrate || fp_preemption_needed(&pfp->ready_queue, prev));
 
 	/* If we need to preempt do so.
 	 * The following checks set resched to 1 in case of special
@@ -1089,8 +1089,10 @@ static void pcp_priority_inheritance(void)
 		fp_set_prio_inh(pfp, blocker, blocked);
 	}
 
-	/* check if anything changed */
-	if (fp_higher_prio(fp_prio_peek(&pfp->ready_queue), pfp->scheduled))
+	/* Check if anything changed. If the blocked job is current, then it is
+	 * just blocking and hence is going to call the scheduler anyway. */
+	if (blocked != current &&
+	    fp_higher_prio(fp_prio_peek(&pfp->ready_queue), pfp->scheduled))
 		preempt(pfp);
 
 	raw_spin_unlock_irqrestore(&pfp->slock, flags);
@@ -1201,10 +1203,10 @@ static void pcp_lower_ceiling(struct pcp_semaphore* sem)
 
 	TRACE_CUR("PCP released sem %p\n", sem);
 
+	pcp_priority_inheritance();
+
 	/* Wake up all ceiling-blocked jobs that now pass the ceiling. */
 	pcp_resume_unblocked();
-
-	pcp_priority_inheritance();
 }
 
 static void pcp_update_prio_ceiling(struct pcp_semaphore* sem,
diff --git a/litmus/sched_trace.c b/litmus/sched_trace.c
index f4171fd..536bc67 100644
--- a/litmus/sched_trace.c
+++ b/litmus/sched_trace.c
@@ -75,7 +75,6 @@ void sched_trace_log_message(const char* fmt, ...)
 	va_end(args);
 }
 
-
 /*
  * log_read - Read the trace buffer
  *
@@ -199,23 +198,27 @@ static struct miscdevice litmus_log_dev = {
 	.fops    = &log_fops,
 };
 
-#ifdef CONFIG_MAGIC_SYSRQ
+
 void dump_trace_buffer(int max)
 {
-	char line[80];
+	char line[255];
 	int len;
 	int count = 0;
 
 	/* potential, but very unlikely, race... */
 	trace_recurse = 1;
+	printk(KERN_CRIT "<<< LITMUS^RT TRACE() dump start >>>");
 	while ((max == 0 || count++ < max) &&
-	       (len = kfifo_out(&debug_buffer, line, sizeof(line - 1))) > 0) {
+	       (len = kfifo_out(&debug_buffer, line, sizeof(line) -1)) > 0) {
 		line[len] = '\0';
-		printk("%s", line);
+		printk(KERN_CRIT "%s", line);
 	}
+	printk(KERN_CRIT "<<< LITMUS^RT TRACE() dump end >>>");
 	trace_recurse = 0;
 }
 
+#ifdef CONFIG_MAGIC_SYSRQ
+
 static void sysrq_dump_trace_buffer(int key)
 {
 	dump_trace_buffer(100);
diff --git a/litmus/sync.c b/litmus/sync.c
index 873b3ff..3e79e0a 100644
--- a/litmus/sync.c
+++ b/litmus/sync.c
@@ -89,7 +89,7 @@ static long do_release_ts(lt_t start)
 {
 	long  task_count = 0;
 
-	struct list_head	*pos;
+	struct list_head	*pos, *safe;
 	struct ts_release_wait	*wait;
 
 	if (mutex_lock_interruptible(&task_release_lock)) {
@@ -101,7 +101,7 @@ static long do_release_ts(lt_t start)
 	sched_trace_sys_release(&start);
 
 	task_count = 0;
-	list_for_each(pos, &task_release_list) {
+	list_for_each_safe(pos, safe, &task_release_list) {
 		wait = (struct ts_release_wait*)
 			list_entry(pos, struct ts_release_wait, list);
 
