]> Zhao Yanbai Git Server - minix.git/commitdiff
Kernel: retain FPU state upon save
authorDavid van Moolenbroek <david@minix3.org>
Sat, 3 Mar 2012 16:48:14 +0000 (17:48 +0100)
committerDavid van Moolenbroek <david@minix3.org>
Mon, 5 Mar 2012 21:32:14 +0000 (22:32 +0100)
On the x86, saving FPU state has the side effect of resetting this
state. In some cases (fork, getcontext), this would cause the state
to be lost. This patch restores the FPU state right after saving it,
except when different state is loaded immediately after.

kernel/arch/i386/arch_system.c
test/test51.c

index 505b50f53cbc4f8c9ebb4809eef63045456bcdaa..be8503a579629ee4981dbe7f3241bf39d71b1404 100644 (file)
@@ -290,20 +290,16 @@ PUBLIC void save_local_fpu(struct proc *pr)
 
 PUBLIC void save_fpu(struct proc *pr)
 {
+       int r;
+
 #ifdef CONFIG_SMP
-       if (cpuid == pr->p_cpu) {
-               if (get_cpulocal_var(fpu_owner) == pr) {
-                       disable_fpu_exception();
-                       save_local_fpu(pr);
-               }
-       }
-       else {
+       if (cpuid != pr->p_cpu) {
                int stopped;
 
                /* remember if the process was already stopped */
                stopped = RTS_ISSET(pr, RTS_PROC_STOP);
 
-               /* stop the remote process and force it's context to be saved */
+               /* stop the remote process and force its context to be saved */
                smp_schedule_stop_proc_save_ctx(pr);
 
                /*
@@ -313,13 +309,21 @@ PUBLIC void save_fpu(struct proc *pr)
                 */
                if (!stopped)
                        RTS_UNSET(pr, RTS_PROC_STOP);
+
+               return;
        }
-#else
+#endif
+
        if (get_cpulocal_var(fpu_owner) == pr) {
                disable_fpu_exception();
                save_local_fpu(pr);
+
+               /* The state may now be reset, and the caller does not expect
+                * this. Immediately restore the saved state.
+                */
+               r = restore_fpu(pr);
+               assert(r == OK);
        }
-#endif
 }
 
 PUBLIC int restore_fpu(struct proc *pr)
index e896c6da2bb6b6d59863ebf2a006f054acda7b95..0a8d83140ddd983afc45deb8b9bea2234215f1c0 100644 (file)
@@ -106,6 +106,7 @@ void do_child(void)
 
 void do_parent(void)
 {
+  ucontext_t dummy;
   int s;
   s = 1;
 
@@ -116,11 +117,16 @@ void do_parent(void)
      between context swaps. */
   if (fesetround(FE_UPWARD) != 0) err(10, 2);
 
+  /* Quick check to make sure that getcontext does not reset the FPU state. */
+  getcontext(&dummy);
+
+  if (fegetround() != FE_UPWARD) err(10, 3);
+
   while(s < SWAPS) {
        do_calcs();
-       if (fegetround() != FE_UPWARD) err(10, 3);
+       if (fegetround() != FE_UPWARD) err(10, 4);
        s++;
-       if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 4);
+       if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 5);
   }
   /* Returning to main thread through uc_link */
 }