Kernel: only reset/reload FPU state when necessary

This commit is contained in:
David van Moolenbroek 2012-03-03 20:33:02 +01:00
parent a615a7d4d2
commit c8c9565a03
4 changed files with 15 additions and 18 deletions

View File

@ -274,24 +274,27 @@ PUBLIC void fpu_init(void)
} }
} }
PUBLIC void save_local_fpu(struct proc *pr) PUBLIC void save_local_fpu(struct proc *pr, int retain)
{ {
/* Save process FPU context. If the 'retain' flag is set, keep the FPU
* state as is. If the flag is not set, the state is undefined upon
* return, and the caller is responsible for reloading a proper state.
*/
if(!is_fpu()) if(!is_fpu())
return; return;
/* Save changed FPU context. */
if(osfxsr_feature) { if(osfxsr_feature) {
fxsave(pr->p_fpu_state.fpu_save_area_p); fxsave(pr->p_fpu_state.fpu_save_area_p);
fninit();
} else { } else {
fnsave(pr->p_fpu_state.fpu_save_area_p); fnsave(pr->p_fpu_state.fpu_save_area_p);
if (retain)
(void) frstor(pr->p_fpu_state.fpu_save_area_p);
} }
} }
PUBLIC void save_fpu(struct proc *pr) PUBLIC void save_fpu(struct proc *pr)
{ {
int r;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
if (cpuid != pr->p_cpu) { if (cpuid != pr->p_cpu) {
int stopped; int stopped;
@ -316,13 +319,7 @@ PUBLIC void save_fpu(struct proc *pr)
if (get_cpulocal_var(fpu_owner) == pr) { if (get_cpulocal_var(fpu_owner) == pr) {
disable_fpu_exception(); disable_fpu_exception();
save_local_fpu(pr); save_local_fpu(pr, TRUE /*retain*/);
/* 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);
} }
} }

View File

@ -1878,7 +1878,7 @@ PUBLIC void copr_not_available_handler(void)
local_fpu_owner = get_cpulocal_var_ptr(fpu_owner); local_fpu_owner = get_cpulocal_var_ptr(fpu_owner);
if (*local_fpu_owner != NULL) { if (*local_fpu_owner != NULL) {
assert(*local_fpu_owner != p); assert(*local_fpu_owner != p);
save_local_fpu(*local_fpu_owner); save_local_fpu(*local_fpu_owner, FALSE /*retain*/);
} }
/* /*
@ -1889,7 +1889,7 @@ PUBLIC void copr_not_available_handler(void)
/* Restoring FPU state failed. This is always the process's own /* Restoring FPU state failed. This is always the process's own
* fault. Send a signal, and schedule another process instead. * fault. Send a signal, and schedule another process instead.
*/ */
*local_fpu_owner = NULL; *local_fpu_owner = NULL; /* release FPU */
cause_sig(proc_nr(p), SIGFPE); cause_sig(proc_nr(p), SIGFPE);
return; return;
} }

View File

@ -34,7 +34,7 @@ _PROTOTYPE( void context_stop, (struct proc * p) );
_PROTOTYPE( void context_stop_idle, (void) ); _PROTOTYPE( void context_stop_idle, (void) );
_PROTOTYPE( int restore_fpu, (struct proc *) ); _PROTOTYPE( int restore_fpu, (struct proc *) );
_PROTOTYPE( void save_fpu, (struct proc *) ); _PROTOTYPE( void save_fpu, (struct proc *) );
_PROTOTYPE( void save_local_fpu, (struct proc *) ); _PROTOTYPE( void save_local_fpu, (struct proc *, int retain) );
_PROTOTYPE( void fpu_sigcontext, (struct proc *, struct sigframe *fr, struct sigcontext *sc) ); _PROTOTYPE( void fpu_sigcontext, (struct proc *, struct sigframe *fr, struct sigcontext *sc) );
/* main.c */ /* main.c */

View File

@ -168,12 +168,12 @@ PUBLIC void smp_sched_handler(void)
RTS_SET(p, RTS_PROC_STOP); RTS_SET(p, RTS_PROC_STOP);
} }
if (flgs & SCHED_IPI_SAVE_CTX) { if (flgs & SCHED_IPI_SAVE_CTX) {
/* all context have been save already, FPU remains */ /* all context has been saved already, FPU remains */
if (proc_used_fpu(p) && if (proc_used_fpu(p) &&
get_cpulocal_var(fpu_owner) == p) { get_cpulocal_var(fpu_owner) == p) {
disable_fpu_exception(); disable_fpu_exception();
save_local_fpu(p); save_local_fpu(p, FALSE /*retain*/);
/* we re preparing to migrate somewhere else */ /* we're preparing to migrate somewhere else */
release_fpu(p); release_fpu(p);
} }
} }