mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-05-24 10:39:52 +00:00
x86/fpu: Prepare fpu_clone() for dynamically enabled features
The default portion of the parent's FPU state is saved in a child task. With dynamic features enabled, the non-default portion is not saved in a child's fpstate because these register states are defined to be caller-saved. The new task's fpstate is therefore the default buffer. Fork inherits the permission of the parent. Also, do not use memcpy() when TIF_NEED_FPU_LOAD is set because it is invalid when the parent has dynamic features. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com> Signed-off-by: Borislav Petkov <bp@suse.de> Link: https://lkml.kernel.org/r/20211021225527.10184-11-chang.seok.bae@intel.com
This commit is contained in:
parent
53599b4d54
commit
9e798e9aa1
3 changed files with 27 additions and 12 deletions
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
extern void save_fpregs_to_fpstate(struct fpu *fpu);
|
extern void save_fpregs_to_fpstate(struct fpu *fpu);
|
||||||
extern void fpu__drop(struct fpu *fpu);
|
extern void fpu__drop(struct fpu *fpu);
|
||||||
extern int fpu_clone(struct task_struct *dst);
|
extern int fpu_clone(struct task_struct *dst, unsigned long clone_flags);
|
||||||
extern void fpu_flush_thread(void);
|
extern void fpu_flush_thread(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -423,8 +423,20 @@ void fpstate_reset(struct fpu *fpu)
|
||||||
fpu->perm.__user_state_size = fpu_user_cfg.default_size;
|
fpu->perm.__user_state_size = fpu_user_cfg.default_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void fpu_inherit_perms(struct fpu *dst_fpu)
|
||||||
|
{
|
||||||
|
if (fpu_state_size_dynamic()) {
|
||||||
|
struct fpu *src_fpu = ¤t->group_leader->thread.fpu;
|
||||||
|
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
/* Fork also inherits the permissions of the parent */
|
||||||
|
dst_fpu->perm = src_fpu->perm;
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Clone current's FPU state on fork */
|
/* Clone current's FPU state on fork */
|
||||||
int fpu_clone(struct task_struct *dst)
|
int fpu_clone(struct task_struct *dst, unsigned long clone_flags)
|
||||||
{
|
{
|
||||||
struct fpu *src_fpu = ¤t->thread.fpu;
|
struct fpu *src_fpu = ¤t->thread.fpu;
|
||||||
struct fpu *dst_fpu = &dst->thread.fpu;
|
struct fpu *dst_fpu = &dst->thread.fpu;
|
||||||
|
@ -455,17 +467,20 @@ int fpu_clone(struct task_struct *dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the FPU registers are not owned by current just memcpy() the
|
* Save the default portion of the current FPU state into the
|
||||||
* state. Otherwise save the FPU registers directly into the
|
* clone. Assume all dynamic features to be defined as caller-
|
||||||
* child's FPU context, without any memory-to-memory copying.
|
* saved, which enables skipping both the expansion of fpstate
|
||||||
|
* and the copying of any dynamic state.
|
||||||
|
*
|
||||||
|
* Do not use memcpy() when TIF_NEED_FPU_LOAD is set because
|
||||||
|
* copying is not valid when current uses non-default states.
|
||||||
*/
|
*/
|
||||||
fpregs_lock();
|
fpregs_lock();
|
||||||
if (test_thread_flag(TIF_NEED_FPU_LOAD)) {
|
if (test_thread_flag(TIF_NEED_FPU_LOAD))
|
||||||
memcpy(&dst_fpu->fpstate->regs, &src_fpu->fpstate->regs,
|
fpregs_restore_userregs();
|
||||||
dst_fpu->fpstate->size);
|
save_fpregs_to_fpstate(dst_fpu);
|
||||||
} else {
|
if (!(clone_flags & CLONE_THREAD))
|
||||||
save_fpregs_to_fpstate(dst_fpu);
|
fpu_inherit_perms(dst_fpu);
|
||||||
}
|
|
||||||
fpregs_unlock();
|
fpregs_unlock();
|
||||||
|
|
||||||
trace_x86_fpu_copy_src(src_fpu);
|
trace_x86_fpu_copy_src(src_fpu);
|
||||||
|
|
|
@ -157,7 +157,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, unsigned long arg,
|
||||||
frame->flags = X86_EFLAGS_FIXED;
|
frame->flags = X86_EFLAGS_FIXED;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
fpu_clone(p);
|
fpu_clone(p, clone_flags);
|
||||||
|
|
||||||
/* Kernel thread ? */
|
/* Kernel thread ? */
|
||||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue