From 7aeccb83e76316b365e4b44a1dd982ee22a7d8b2 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 19 Jan 2015 19:51:32 +0100 Subject: [PATCH 1/3] x86/fpu: __kernel_fpu_begin() should clear fpu_owner_task even if use_eager_fpu() __kernel_fpu_begin() does nothing if !__thread_has_fpu() && use_eager_fpu(), perhaps it assumes that this case is simply impossible. This is certainly not possible if in_interrupt() == T; interrupted_user_mode() should have FPU, and interrupted_kernel_fpu_idle() should fail if !__thread_has_fpu(). However, even if use_eager_fpu() == T a task can do drop_fpu(), then switch to another thread which becomes fpu_owner_task, then resume and call some function which does kernel_fpu_begin(). Say, an exiting task does a lot of things after exit_thread(), it is not safe to assume that it can't use FPU in these paths. Signed-off-by: Oleg Nesterov Reviewed-by: Rik van Riel Cc: Linus Torvalds Cc: Suresh Siddha Cc: Andy Lutomirski Cc: Pekka Riikonen Link: http://lkml.kernel.org/r/20150119185132.GB16427@redhat.com Signed-off-by: Borislav Petkov --- arch/x86/kernel/i387.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index f59d80622e60..ad3a2a23f248 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c @@ -93,9 +93,10 @@ void __kernel_fpu_begin(void) if (__thread_has_fpu(me)) { __save_init_fpu(me); - } else if (!use_eager_fpu()) { + } else { this_cpu_write(fpu_owner_task, NULL); - clts(); + if (!use_eager_fpu()) + clts(); } } EXPORT_SYMBOL(__kernel_fpu_begin); From 4b2e762e2e53c721458a83d547b222178bb72a34 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 19 Jan 2015 19:51:51 +0100 Subject: [PATCH 2/3] x86/fpu: Always allow FPU in interrupt if use_eager_fpu() The __thread_has_fpu() check in interrupted_kernel_fpu_idle() was needed to prevent the nested kernel_fpu_begin(). Now that we have in_kernel_fpu and !__thread_has_fpu() case in __kernel_fpu_begin() does not depend on use_eager_fpu() (except clts) we can remove it. __thread_has_fpu() can be false even if use_eager_fpu(), but this case does not differ from !use_eager_fpu() case except we should not worry about X86_CR0_TS, __kernel_fpu_begin()/end() will not touch this bit. Note: I think we can kill all irq_fpu_usable() checks except in_kernel_fpu, just we need to record the state of X86_CR0_TS in __kernel_fpu_begin() and conditionalize stts() in __kernel_fpu_end(), but this needs another patch. Signed-off-by: Oleg Nesterov Reviewed-by: Rik van Riel Acked-by: Andy Lutomirski Cc: Linus Torvalds Cc: Suresh Siddha Link: http://lkml.kernel.org/r/20150119185151.GC16427@redhat.com Signed-off-by: Borislav Petkov --- arch/x86/kernel/i387.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index ad3a2a23f248..8416b5f85806 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c @@ -41,8 +41,8 @@ void kernel_fpu_enable(void) * be set (so that the clts/stts pair does nothing that is * visible in the interrupted kernel thread). * - * Except for the eagerfpu case when we return 1 unless we've already - * been eager and saved the state in kernel_fpu_begin(). + * Except for the eagerfpu case when we return true; in the likely case + * the thread has FPU but we are not going to set/clear TS. */ static inline bool interrupted_kernel_fpu_idle(void) { @@ -50,7 +50,7 @@ static inline bool interrupted_kernel_fpu_idle(void) return false; if (use_eager_fpu()) - return __thread_has_fpu(current); + return true; return !__thread_has_fpu(current) && (read_cr0() & X86_CR0_TS); From 110d7f7513bbb916b8654da9e2973ac5bed929a9 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 19 Jan 2015 19:52:12 +0100 Subject: [PATCH 3/3] x86/fpu: Don't abuse FPU in kernel threads if use_eager_fpu() AFAICS, there is no reason why kernel threads should have FPU context even if use_eager_fpu() == T. Now that interrupted_kernel_fpu_idle() does not check __thread_has_fpu() in the use_eager_fpu() case, we can remove the init_fpu() code from eager_fpu_init() and change flush_thread() called by do_execve() to initialize FPU. Note: of course, the change in flush_thread() is horrible and must be cleanuped. We need the new helper, and flush_thread() should return the error if init_fpu() fails. Signed-off-by: Oleg Nesterov Reviewed-by: Rik van Riel Cc: Linus Torvalds Cc: Suresh Siddha Cc: Andy Lutomirski Link: http://lkml.kernel.org/r/20150119185212.GD16427@redhat.com Signed-off-by: Borislav Petkov --- arch/x86/kernel/process.c | 7 +++++++ arch/x86/kernel/xsave.c | 13 +------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index ce8b10351e28..83480373a642 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -130,6 +130,7 @@ void flush_thread(void) flush_ptrace_hw_breakpoint(tsk); memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array)); + drop_init_fpu(tsk); /* * Free the FPU state for non xsave platforms. They get reallocated @@ -137,6 +138,12 @@ void flush_thread(void) */ if (!use_eager_fpu()) free_thread_xstate(tsk); + else if (!used_math()) { + /* kthread execs. TODO: cleanup this horror. */ + if (WARN_ON(init_fpu(current))) + force_sig(SIGKILL, current); + math_state_restore(); + } } static void hard_disable_TSC(void) diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index 0de1fae2bdf0..de9dcf89a302 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c @@ -688,7 +688,7 @@ void eager_fpu_init(void) { static __refdata void (*boot_func)(void) = eager_fpu_init_bp; - clear_used_math(); + WARN_ON(used_math()); current_thread_info()->status = 0; if (eagerfpu == ENABLE) @@ -703,17 +703,6 @@ void eager_fpu_init(void) boot_func(); boot_func = NULL; } - - /* - * This is same as math_state_restore(). But use_xsave() is - * not yet patched to use math_state_restore(). - */ - init_fpu(current); - __thread_fpu_begin(current); - if (cpu_has_xsave) - xrstor_state(init_xstate_buf, -1); - else - fxrstor_checking(&init_xstate_buf->i387); } /*