If TIF_NEED_FPU_LOAD is cleared then the CPU’s FPU registers hold current thread’s FPU registers.
If TIF_NEED_FPU_LOAD is set then CPU’s FPU registers may not hold current()’s FPU registers. It is required to load the registers before returning to userland or using the content otherwise.
Idea:Defer loading of FPU state until return to userspace. This gives the kernel the potential to skip loading FPU state for tasks that stay in kernel mode.
save the FPU register into xsave area for the previous task when task switch occurs
// https://elixir.bootlin.com/linux/v5.15-rc6/source/arch/x86/include/asm/fpu/internal.h#L534 /* * Delay loading of the complete FPU state until the return to userland. * PKRU is handled separately. */ staticinlinevoidswitch_fpu_finish(struct fpu *new_fpu) { if (cpu_feature_enabled(X86_FEATURE_FPU)) set_thread_flag(TIF_NEED_FPU_LOAD); }
// https://elixir.bootlin.com/linux/v5.15-rc6/source/arch/x86/include/asm/fpu/internal.h#L508 /* * FPU state switching for scheduling. * * This is a two-stage process: * * - switch_fpu_prepare() saves the old state. * This is done within the context of the old process. * * - switch_fpu_finish() sets TIF_NEED_FPU_LOAD; the floating point state * will get loaded on return to userspace, or when the kernel needs it. * * If TIF_NEED_FPU_LOAD is cleared then the CPU's FPU registers * are saved in the current thread's FPU register state. * * If TIF_NEED_FPU_LOAD is set then CPU's FPU registers may not * hold current()'s FPU registers. It is required to load the * registers before returning to userland or using the content * otherwise. * * The FPU context is only stored/restored for a user task and * PF_KTHREAD is used to distinguish between kernel and user threads. */ staticinlinevoidswitch_fpu_prepare(struct fpu *old_fpu, int cpu) { if (static_cpu_has(X86_FEATURE_FPU) && !(current->flags & PF_KTHREAD)) { save_fpregs_to_fpstate(old_fpu); /* * The save operation preserved register state, so the * fpu_fpregs_owner_ctx is still @old_fpu. Store the * current CPU number in @old_fpu, so the next return * to user space can avoid the FPU register restore * when is returns on the same CPU and still owns the * context. */ old_fpu->last_cpu = cpu;
// https://elixir.bootlin.com/linux/v5.15-rc6/source/arch/x86/include/asm/fpu/internal.h#L456 staticinlinevoidfpregs_restore_userregs(void) { structfpu *fpu = ¤t->thread.fpu; int cpu = smp_processor_id();
if (WARN_ON_ONCE(current->flags & PF_KTHREAD)) return;
if (!fpregs_state_valid(fpu, cpu)) { u64 mask;
/* * This restores _all_ xstate which has not been * established yet. * * If PKRU is enabled, then the PKRU value is already * correct because it was either set in switch_to() or in * flush_thread(). So it is excluded because it might be * not up to date in current->thread.fpu.xsave state. */ mask = xfeatures_mask_restore_user() | xfeatures_mask_supervisor(); __restore_fpregs_from_fpstate(&fpu->state, mask);
/* * Highest level per task FPU state data structure that * contains the FPU register state plus various FPU * state fields: */ structfpu { /* * @last_cpu: * * Records the last CPU on which this context was loaded into * FPU registers. (In the lazy-restore case we might be * able to reuse FPU registers across multiple context switches * this way, if no intermediate task used the FPU.) * * A value of -1 is used to indicate that the FPU state in context * memory is newer than the FPU state in registers, and that the * FPU state should be reloaded next time the task is run. */ unsignedint last_cpu;
/* * @avx512_timestamp: * * Records the timestamp of AVX512 use during last context switch. */ unsignedlong avx512_timestamp;
/* * @state: * * In-memory copy of all FPU registers that we save/restore * over context switches. If the task is using the FPU then * the registers in the FPU are more recent than this state * copy. If the task context-switches away then they get * saved here and represent the FPU state. */ union fpregs_state state; /* * WARNING: 'state' is dynamically-sized. Do not put * anything after it here. */ };