Echa un vistazo a esta rama: arch / x86 / kernel / fpu (que maneja todas las cosas específicas de la FPU de x86):
Lea este comentario que puede proporcionar la respuesta:
enlace
/*
* FPU context switching strategies:
*
* Against popular belief, we don't do lazy FPU saves, due to the
* task migration complications it brings on SMP - we only do
* lazy FPU restores.
*
* 'lazy' is the traditional strategy, which is based on setting
* CR0::TS to 1 during context-switch (instead of doing a full
* restore of the FPU state), which causes the first FPU instruction
* after the context switch (whenever it is executed) to fault - at
* which point we lazily restore the FPU state into FPU registers.
*
* Tasks are of course under no obligation to execute FPU instructions,
* so it can easily happen that another context-switch occurs without
* a single FPU instruction being executed. If we eventually switch
* back to the original task (that still owns the FPU) then we have
* not only saved the restores along the way, but we also have the
* FPU ready to be used for the original task.
*
* 'lazy' is deprecated because it's almost never a performance win
* and it's much more complicated than 'eager'.
*
* 'eager' switching is by default on all CPUs, there we switch the FPU
* state during every context switch, regardless of whether the task
* has used FPU instructions in that time slice or not. This is done
* because modern FPU context saving instructions are able to optimize
* state saving and restoration in hardware: they can detect both
* unused and untouched FPU state and optimize accordingly.
Así que para repetir lo que se explica:
a. Modo LAZY: la FPU no se restaura / guarda todo el tiempo, pero solo cuando se usa, y el uso de la FPU también restablecerá un indicador en CR0: TS, por lo que no tenemos que detectar el uso del registro de la FPU todo el tiempo . Pero este modo no es el predeterminado, ya que el ahorro de tiempo / rendimiento mejorado no es significativo, y el algoritmo se vuelve muy complejo, lo que aumenta las sobrecargas de procesamiento.
b. Modo EAGER: Este es el modo por defecto. FPU siempre se guarda y se restaura para cada cambio de contexto. Pero nuevamente, hay una función de hardware que puede detectar si se utiliza la cadena larga de registros de FPU, y cualquiera que se use, solo ese registro se guardará / restaurará, y por lo tanto es muy eficiente en hardware.
Hacer esto no es nada fácil, ya que significó escribir 208 parches en 2015:
enlace
Las instrucciones para guardar todas las FPU: XMM, MMX, SSE, SSE2, etc. se llaman FXSAVE, FNSAVE, FSAVE:
enlace
y la sobrecarga en el kernel de Linux se compara con 87 ciclos.
enlace
Esta forma optimizada de guardar también se puede encontrar en los comentarios a continuación:
* When executing XSAVEOPT (or other optimized XSAVE instructions), if
* a processor implementation detects that an FPU state component is still
* (or is again) in its initialized state, it may clear the corresponding
* bit in the header.xfeatures field, and can skip the writeout of registers
* to the corresponding memory layout.
*
* This means that when the bit is zero, the state component might still contain
* some previous - non-initialized register state.
Para detectar que el kernel se activa con el uso de FPU, podemos establecer un punto de interrupción en fpstate_sanitize_xstate en KGDB, y el kernel stacktrace es el siguiente:
Thread 441 hit Breakpoint 1, fpstate_sanitize_xstate (fpu=0xffff8801e7a2ea80) at /build/linux-FvcHlK/linux-4.4.0/arch/x86/kernel/fpu/xstate.c:111
111 {
#0 fpstate_sanitize_xstate (fpu=0xffff8801e7a2ea80) at /build/linux-FvcHlK/linux-4.4.0/arch/x86/kernel/fpu/xstate.c:111
#1 0xffffffff8103b183 in copy_fpstate_to_sigframe (buf=0xffff8801e7a2ea80, buf_fx=0x7f73ad4fe3c0, size=<optimized out>) at /build/linux-FvcHlK/linux-4.4.0/arch/x86/kernel/fpu/signal.c:178
#2 0xffffffff8102e207 in get_sigframe (frame_size=440, fpstate=0xffff880034dcbe10, regs=<optimized out>, ka=<optimized out>) at /build/linux-FvcHlK/linux-4.4.0/arch/x86/kernel/signal.c:247
#3 0xffffffff8102e703 in __setup_rt_frame (regs=<optimized out>, set=<optimized out>, ksig=<optimized out>, sig=<optimized out>) at /build/linux-FvcHlK/linux-4.4.0/arch/x86/kernel/signal.c:413
#4 setup_rt_frame (regs=<optimized out>, ksig=<optimized out>) at /build/linux-FvcHlK/linux-4.4.0/arch/x86/kernel/signal.c:627
#5 handle_signal (regs=<optimized out>, ksig=<optimized out>) at /build/linux-FvcHlK/linux-4.4.0/arch/x86/kernel/signal.c:671
#6 do_signal (regs=0xffff880034dcbf58) at /build/linux-FvcHlK/linux-4.4.0/arch/x86/kernel/signal.c:714
#7 0xffffffff8100320c in exit_to_usermode_loop (regs=0xffff880034dcbf58, cached_flags=4) at /build/linux-FvcHlK/linux-4.4.0/arch/x86/entry/common.c:248
#8 0xffffffff81003c6e in prepare_exit_to_usermode (regs=<optimized out>) at /build/linux-FvcHlK/linux-4.4.0/arch/x86/entry/common.c:283
Utilizando "info thread 441" (ver más arriba) y descubrirá que "Xorg" es el creador del stacktrace anterior, pero de lo contrario, la mayoría de los procesos no usa FPU.
Desde el seguimiento de pila, "get_sigframe ()" es la primera función que parece analizar el uso de FPU:
if (fpu->fpstate_active) {
unsigned long fx_aligned, math_size;
sp = fpu__alloc_mathframe(sp, 1, &fx_aligned, &math_size);
*fpstate = (struct _fpstate_32 __user *) sp;
if (copy_fpstate_to_sigframe(*fpstate, (void __user *)fx_aligned,
math_size) < 0)
return (void __user *) -1L;
}
Básicamente, lo que está sucediendo aquí es copiar la información de la FPU al puntero de la pila del espacio de usuario (que es "sp").
En resumen, esta parte de la lógica de guardado / copia / restauración de FPU se activa solo con el uso de FPU.