Index: kernel/generic/src/cpu/cpu.c
===================================================================
--- kernel/generic/src/cpu/cpu.c	(revision fbaf6ac810438ece2c6b76f304cd40ab362397c7)
+++ kernel/generic/src/cpu/cpu.c	(revision d8581611fd026fe0e15b2f7f878a31fea99e2708)
@@ -84,5 +84,7 @@
 			cpus[i].id = i;
 
+#ifdef CONFIG_FPU_LAZY
 			irq_spinlock_initialize(&cpus[i].fpu_lock, "cpus[].fpu_lock");
+#endif
 			irq_spinlock_initialize(&cpus[i].tlb_lock, "cpus[].tlb_lock");
 
Index: kernel/generic/src/proc/scheduler.c
===================================================================
--- kernel/generic/src/proc/scheduler.c	(revision fbaf6ac810438ece2c6b76f304cd40ab362397c7)
+++ kernel/generic/src/proc/scheduler.c	(revision d8581611fd026fe0e15b2f7f878a31fea99e2708)
@@ -84,8 +84,12 @@
 
 #ifdef CONFIG_FPU_LAZY
+	irq_spinlock_lock(&CPU->fpu_lock, true);
+
 	if (THREAD == CPU->fpu_owner)
 		fpu_enable();
 	else
 		fpu_disable();
+
+	irq_spinlock_unlock(&CPU->fpu_lock, true);
 #elif defined CONFIG_FPU
 	fpu_enable();
@@ -133,14 +137,8 @@
 	/* Save old context */
 	if (CPU->fpu_owner != NULL) {
-		irq_spinlock_lock(&CPU->fpu_owner->lock, false);
 		fpu_context_save(&CPU->fpu_owner->fpu_context);
-
-		/* Don't prevent migration */
-		CPU->fpu_owner->fpu_context_engaged = false;
-		irq_spinlock_unlock(&CPU->fpu_owner->lock, false);
 		CPU->fpu_owner = NULL;
 	}
 
-	irq_spinlock_lock(&THREAD->lock, false);
 	if (THREAD->fpu_context_exists) {
 		fpu_context_restore(&THREAD->fpu_context);
@@ -151,6 +149,4 @@
 
 	CPU->fpu_owner = THREAD;
-	THREAD->fpu_context_engaged = true;
-	irq_spinlock_unlock(&THREAD->lock, false);
 
 	irq_spinlock_unlock(&CPU->fpu_lock, false);
@@ -503,4 +499,28 @@
 #ifdef CONFIG_SMP
 
+static inline void fpu_owner_lock(cpu_t *cpu)
+{
+#ifdef CONFIG_FPU_LAZY
+	irq_spinlock_lock(&cpu->fpu_lock, false);
+#endif
+}
+
+static inline void fpu_owner_unlock(cpu_t *cpu)
+{
+#ifdef CONFIG_FPU_LAZY
+	irq_spinlock_unlock(&cpu->fpu_lock, false);
+#endif
+}
+
+static inline thread_t *fpu_owner(cpu_t *cpu)
+{
+#ifdef CONFIG_FPU_LAZY
+	assert(irq_spinlock_locked(&cpu->fpu_lock));
+	return cpu->fpu_owner;
+#else
+	return NULL;
+#endif
+}
+
 static thread_t *steal_thread_from(cpu_t *old_cpu, int i)
 {
@@ -508,5 +528,8 @@
 	runq_t *new_rq = &CPU->rq[i];
 
-	irq_spinlock_lock(&old_rq->lock, true);
+	ipl_t ipl = interrupts_disable();
+
+	fpu_owner_lock(old_cpu);
+	irq_spinlock_lock(&old_rq->lock, false);
 
 	/* Search rq from the back */
@@ -521,8 +544,11 @@
 		 * FPU context is still in the CPU.
 		 */
-		if (thread->stolen || thread->nomigrate || thread->fpu_context_engaged) {
+		if (thread->stolen || thread->nomigrate ||
+		    thread == fpu_owner(old_cpu)) {
 			irq_spinlock_unlock(&thread->lock, false);
 			continue;
 		}
+
+		fpu_owner_unlock(old_cpu);
 
 		thread->stolen = true;
@@ -546,20 +572,21 @@
 		old_rq->n--;
 		list_remove(&thread->rq_link);
-
-		irq_spinlock_pass(&old_rq->lock, &new_rq->lock);
+		irq_spinlock_unlock(&old_rq->lock, false);
 
 		/* Append thread to local queue. */
+		irq_spinlock_lock(&new_rq->lock, false);
 		list_append(&thread->rq_link, &new_rq->rq);
 		new_rq->n++;
-
-		irq_spinlock_unlock(&new_rq->lock, true);
+		irq_spinlock_unlock(&new_rq->lock, false);
 
 		atomic_dec(&old_cpu->nrdy);
 		atomic_inc(&CPU->nrdy);
-
+		interrupts_restore(ipl);
 		return thread;
 	}
 
-	irq_spinlock_unlock(&old_rq->lock, true);
+	irq_spinlock_unlock(&old_rq->lock, false);
+	fpu_owner_unlock(old_cpu);
+	interrupts_restore(ipl);
 	return NULL;
 }
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision fbaf6ac810438ece2c6b76f304cd40ab362397c7)
+++ kernel/generic/src/proc/thread.c	(revision d8581611fd026fe0e15b2f7f878a31fea99e2708)
@@ -355,5 +355,4 @@
 
 	thread->fpu_context_exists = false;
-	thread->fpu_context_engaged = false;
 
 	odlink_initialize(&thread->lthreads);
