Kernel: retain FPU state upon save

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.
This commit is contained in:
David van Moolenbroek 2012-03-03 17:48:14 +01:00
parent 0a8a2ecfb5
commit a615a7d4d2
2 changed files with 22 additions and 12 deletions

View 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)

View 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 */
}