patch-2.3.99-pre6 linux/arch/sh/kernel/fpu.c

Next file: linux/arch/sh/kernel/head.S
Previous file: linux/arch/sh/kernel/entry.S
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre5/linux/arch/sh/kernel/fpu.c linux/arch/sh/kernel/fpu.c
@@ -23,6 +23,7 @@
 {
 	asm volatile("sts.l	$fpul, @-%0\n\t"
 		     "sts.l	$fpscr, @-%0\n\t"
+		     "lds	%1, $fpscr\n\t"
 		     "frchg\n\t"
 		     "fmov.s	$fr15, @-%0\n\t"
 		     "fmov.s	$fr14, @-%0\n\t"
@@ -58,7 +59,8 @@
 		     "fmov.s	$fr1, @-%0\n\t"
 		     "fmov.s	$fr0, @-%0"
 		     : /* no output */
-		     : "r" ((char *)(&tsk->thread.fpu.hard.status))
+		     : "r" ((char *)(&tsk->thread.fpu.hard.status)),
+		       "r" (FPSCR_INIT)
 		     : "memory");
 
 	tsk->flags &= ~PF_USEDFPU;
@@ -68,7 +70,8 @@
 static void
 restore_fpu(struct task_struct *tsk)
 {
-	asm volatile("fmov.s	@%0+, $fr0\n\t"
+	asm volatile("lds	%1, $fpscr\n\t"
+		     "fmov.s	@%0+, $fr0\n\t"
 		     "fmov.s	@%0+, $fr1\n\t"
 		     "fmov.s	@%0+, $fr2\n\t"
 		     "fmov.s	@%0+, $fr3\n\t"
@@ -105,7 +108,7 @@
 		     "lds.l	@%0+, $fpscr\n\t"
 		     "lds.l	@%0+, $fpul\n\t"
 		     : /* no output */
-		     : "r" (&tsk->thread.fpu)
+		     : "r" (&tsk->thread.fpu), "r" (FPSCR_INIT)
 		     : "memory");
 }
 
@@ -163,7 +166,6 @@
 {
 	struct task_struct *tsk = current;
 
-	regs.syscall_nr = -1;
 	regs.pc += 2;
 
 	grab_fpu();
@@ -179,15 +181,34 @@
 {
 	struct task_struct *tsk = current;
 
-	regs.syscall_nr = -1;
-
 	if (!user_mode(&regs)) {
 		if (tsk != &init_task) {
 			unlazy_fpu(tsk);
 		}
 		tsk = &init_task;
-		if (tsk->flags & PF_USEDFPU)
-			BUG();
+		if (tsk->flags & PF_USEDFPU) {
+			/*
+			 * This weird situation can be occurred.
+			 *
+			 * There's race condition in __cli:
+			 *
+			 *   (1) $SR --> register
+			 *   (2) Set IMASK of register
+			 *   (3) $SR <-- register
+			 *
+			 * Between (1) and (2), or (2) and (3) getting
+			 * interrupt, and interrupt handler (or
+			 * softirq) may use FPU.
+			 *
+			 * Then, SR.FD is overwritten by (3).
+			 *
+			 * This results init_task.PF_USEDFPU is on,
+			 * with SR.FD == 1.
+			 *
+			 */
+			release_fpu();
+			return;
+		}
 	}
 
 	grab_fpu();
@@ -216,8 +237,8 @@
 			grab_fpu();
 		else {
 			if (!(sr & SR_FD)) {
-				release_fpu();
 				BUG();
+				release_fpu();
 			}
 		}
 		return;
@@ -228,16 +249,20 @@
 			grab_fpu();
 		else {
 			if (init_task.flags & PF_USEDFPU) {
-				init_task.flags &= ~PF_USEDFPU;
-				BUG();
+				/*
+				 * This weird situation can be occurred.
+				 * See the comment in do_fpu_state_restore.
+				 */
+				grab_fpu();
+				save_fpu(&init_task);
 			}
 		}
 	} else {
 		if (init_task.flags & PF_USEDFPU)
 			save_fpu(&init_task);
 		else {
-			release_fpu();
 			BUG();
+			release_fpu();
 		}
 	}
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)