When a new task is created, the kernel copies all the states from the
parent. If the parent already has any dynamic user state in use, the new
task has to expand the XSAVE buffer to save them. Also, disable the
associated first-use fault.
No functional change until the kernel supports dynamic user states.
Signed-off-by: Chang S. Bae <[email protected]>
Reviewed-by: Len Brown <[email protected]>
Cc: [email protected]
Cc: [email protected]
---
arch/x86/kernel/fpu/core.c | 28 ++++++++++++++++++++--------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c
index ece6428ba85b..aefa219ba84f 100644
--- a/arch/x86/kernel/fpu/core.c
+++ b/arch/x86/kernel/fpu/core.c
@@ -225,6 +225,7 @@ int fpu__copy(struct task_struct *dst, struct task_struct *src)
{
struct fpu *dst_fpu = &dst->thread.fpu;
struct fpu *src_fpu = &src->thread.fpu;
+ unsigned int size;
dst_fpu->last_cpu = -1;
@@ -233,15 +234,26 @@ int fpu__copy(struct task_struct *dst, struct task_struct *src)
WARN_ON_FPU(src_fpu != ¤t->thread.fpu);
- /*
- * Don't let 'init optimized' areas of the XSAVE area
- * leak into the child task:
- */
- memset(&dst_fpu->state.xsave, 0, fpu_kernel_xstate_default_size);
-
- dst_fpu->state_mask = xfeatures_mask_all & ~xfeatures_mask_user_dynamic;
dst_fpu->state_ptr = NULL;
+ /* Inherit the dynamic area if the parent already has. */
+ if (src_fpu->state_ptr) {
+ int ret;
+
+ dst_fpu->state_mask = 0;
+ ret = alloc_xstate_area(dst_fpu, src_fpu->state_mask, &size);
+ if (ret)
+ return ret;
+ } else {
+ dst_fpu->state_mask = src_fpu->state_mask & ~xfeatures_mask_user_dynamic;
+ size = fpu_kernel_xstate_default_size;
+ /*
+ * Don't let 'init optimized' areas of the XSAVE area
+ * leak into the child task:
+ */
+ memset(&dst_fpu->state.xsave, 0, size);
+ }
+
/*
* If the FPU registers are not current just memcpy() the state.
* Otherwise save current FPU registers directly into the child's FPU
@@ -252,7 +264,7 @@ int fpu__copy(struct task_struct *dst, struct task_struct *src)
*/
fpregs_lock();
if (test_thread_flag(TIF_NEED_FPU_LOAD))
- memcpy(__xstate(dst_fpu), __xstate(src_fpu), fpu_kernel_xstate_default_size);
+ memcpy(__xstate(dst_fpu), __xstate(src_fpu), size);
else if (!copy_fpregs_to_fpstate(dst_fpu))
copy_kernel_to_fpregs(dst_fpu);
--
2.17.1
On Thu, Nov 19, 2020 at 3:37 PM Chang S. Bae <[email protected]> wrote:
>
> When a new task is created, the kernel copies all the states from the
> parent. If the parent already has any dynamic user state in use, the new
> task has to expand the XSAVE buffer to save them. Also, disable the
> associated first-use fault.
This seems like a mistake. If init uses AMX for some misguided
reason, ever task on the whole system will end up with AMX state
allocated.
On Fri, Nov 20, 2020 at 12:08 AM Andy Lutomirski <[email protected]> wrote:
>
> On Thu, Nov 19, 2020 at 3:37 PM Chang S. Bae <[email protected]> wrote:
> >
> > When a new task is created, the kernel copies all the states from the
> > parent. If the parent already has any dynamic user state in use, the new
> > task has to expand the XSAVE buffer to save them. Also, disable the
> > associated first-use fault.
>
> This seems like a mistake. If init uses AMX for some misguided
> reason, ever task on the whole system will end up with AMX state
> allocated.
Andy, you are right -- the child can (and should) start with the
un-expanded context switch buffer, and as a result XFD should be armed
to fire on the child's first access to TMM hardware.
TMM registers are scratchpad, they will never be used to pass globals,
say, from parent to child thread.
Further, they are volatile and caller saved. The callee can assume
they are empty -- so even by virtue of being in a fork system call,
that state is already gone.
thanks,
Len Brown, Intel Open Source Technology Center