From: Thomas Gleixner <tglx@linutronix.de>
To: LKML <linux-kernel@vger.kernel.org>
Cc: x86@kernel.org, Filipe Manana <fdmanana@suse.com>
Subject: [patch 3/3] x86/fpu: Make FPU protection more robust
Date: Sun, 1 May 2022 21:31:47 +0200 (CEST) [thread overview]
Message-ID: <20220501193102.704267030@linutronix.de> (raw)
In-Reply-To: 20220501192740.203963477@linutronix.de
FPU state maintenance is protected by fpregs_lock(), which is a wrapper
around local_bh_disable() on non-RT kernels and preempt_disable() on RT
kernels. In-kernel FPU usage has it's own protection via a per CPU
variable.
This separation is pointless and error-prone as a recently discovered wrong
condition for granting in-kernel FPU usage has shown.
Make the whole FPU state protection simpler and more robust by using the
per CPU usage variable for all FPU operations so state is tracked
consistently.
Change related WARN_ON_FPU() instances to WARN_ON_ONCE() as the usage of
CONFIG_X86_DEBUG_FPU is optional and hides inconsistencies for a
potentially long time.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
arch/x86/include/asm/fpu/api.h | 17 +-------
arch/x86/kernel/fpu/core.c | 78 ++++++++++++++++++++++++++---------------
2 files changed, 52 insertions(+), 43 deletions(-)
--- a/arch/x86/include/asm/fpu/api.h
+++ b/arch/x86/include/asm/fpu/api.h
@@ -66,21 +66,8 @@ static inline void kernel_fpu_begin(void
*
* Disabling preemption also serializes against kernel_fpu_begin().
*/
-static inline void fpregs_lock(void)
-{
- if (!IS_ENABLED(CONFIG_PREEMPT_RT))
- local_bh_disable();
- else
- preempt_disable();
-}
-
-static inline void fpregs_unlock(void)
-{
- if (!IS_ENABLED(CONFIG_PREEMPT_RT))
- local_bh_enable();
- else
- preempt_enable();
-}
+extern void fpregs_lock(void);
+extern void fpregs_unlock(void);
#ifdef CONFIG_X86_DEBUG_FPU
extern void fpregs_assert_state_consistent(void);
--- a/arch/x86/kernel/fpu/core.c
+++ b/arch/x86/kernel/fpu/core.c
@@ -42,7 +42,7 @@ struct fpu_state_config fpu_user_cfg __r
struct fpstate init_fpstate __ro_after_init;
/* Track in-kernel FPU usage */
-static DEFINE_PER_CPU(bool, in_kernel_fpu);
+static DEFINE_PER_CPU(bool, fpu_in_use);
/*
* Track which context is using the FPU on the CPU:
@@ -50,6 +50,50 @@ static DEFINE_PER_CPU(bool, in_kernel_fp
DEFINE_PER_CPU(struct fpu *, fpu_fpregs_owner_ctx);
/**
+ * fpregs_lock - Lock FPU state for maintenance operations
+ *
+ * This protects against preemption, soft interrupts and in-kernel FPU
+ * usage on both !RT and RT enabled kernels.
+ *
+ * !RT kernels use local_bh_disable() to prevent soft interrupt processing
+ * and preemption.
+ *
+ * On RT kernels local_bh_disable() is not sufficient because it only
+ * serializes soft interrupt related sections via a local lock, but stays
+ * preemptible. Disabling preemption is the right choice here as bottom
+ * half processing is always in thread context on RT kernels so it
+ * implicitly prevents bottom half processing as well.
+ */
+void fpregs_lock(void)
+{
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT))
+ local_bh_disable();
+ else
+ preempt_disable();
+
+ WARN_ON_ONCE(this_cpu_read(fpu_in_use));
+ this_cpu_write(fpu_in_use, true);
+}
+EXPORT_SYMBOL_GPL(fpregs_lock);
+
+/**
+ * fpregs_unlock - Unlock FPU state after maintenance operations
+ *
+ * Counterpart to fpregs_lock().
+ */
+void fpregs_unlock(void)
+{
+ WARN_ON_ONCE(!this_cpu_read(fpu_in_use));
+ this_cpu_write(fpu_in_use, false);
+
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT))
+ local_bh_enable();
+ else
+ preempt_enable();
+}
+EXPORT_SYMBOL_GPL(fpregs_unlock);
+
+/**
* kernel_fpu_usable - Check whether kernel FPU usage is possible
*
* Has to be invoked before calling kernel_fpu_begin().
@@ -59,28 +103,7 @@ bool kernel_fpu_usable(void)
if (WARN_ON_ONCE(in_nmi()))
return false;
- /* In kernel FPU usage already active? */
- if (this_cpu_read(in_kernel_fpu))
- return false;
-
- /*
- * When not in NMI or hard interrupt context, FPU can be used:
- *
- * - Task context is safe except from within fpregs_lock()'ed
- * critical regions.
- *
- * - Soft interrupt processing context which cannot happen
- * while in a fpregs_lock()'ed critical region.
- */
- if (!in_hardirq())
- return true;
-
- /*
- * In hard interrupt context it's safe when soft interrupts
- * are enabled, which means the interrupt did not hit in
- * a fpregs_lock()'ed critical region.
- */
- return !softirq_count();
+ return !this_cpu_read(fpu_in_use);
}
EXPORT_SYMBOL(kernel_fpu_usable);
@@ -410,10 +433,9 @@ void kernel_fpu_begin_mask(unsigned int
{
preempt_disable();
- WARN_ON_FPU(!kernel_fpu_usable());
- WARN_ON_FPU(this_cpu_read(in_kernel_fpu));
+ WARN_ON_ONCE(!kernel_fpu_usable());
- this_cpu_write(in_kernel_fpu, true);
+ this_cpu_write(fpu_in_use, true);
if (!(current->flags & PF_KTHREAD) &&
!test_thread_flag(TIF_NEED_FPU_LOAD)) {
@@ -433,9 +455,9 @@ EXPORT_SYMBOL_GPL(kernel_fpu_begin_mask)
void kernel_fpu_end(void)
{
- WARN_ON_FPU(!this_cpu_read(in_kernel_fpu));
+ WARN_ON_ONCE(!this_cpu_read(fpu_in_use));
- this_cpu_write(in_kernel_fpu, false);
+ this_cpu_write(fpu_in_use, false);
preempt_enable();
}
EXPORT_SYMBOL_GPL(kernel_fpu_end);
next prev parent reply other threads:[~2022-05-01 19:32 UTC|newest]
Thread overview: 38+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-05-01 19:31 [patch 0/3] x86/fpu: Prevent FPU state corruption Thomas Gleixner
2022-05-01 19:31 ` [patch 1/3] " Thomas Gleixner
2022-05-02 13:16 ` Borislav Petkov
2022-05-05 0:42 ` [tip: x86/urgent] " tip-bot2 for Thomas Gleixner
2022-05-01 19:31 ` [patch 2/3] x86/fpu: Rename irq_fpu_usable() Thomas Gleixner
2022-05-02 13:57 ` Borislav Petkov
2022-05-01 19:31 ` Thomas Gleixner [this message]
2022-05-02 14:35 ` [patch 3/3] x86/fpu: Make FPU protection more robust Borislav Petkov
2022-05-02 15:58 ` Thomas Gleixner
2022-05-03 9:06 ` Peter Zijlstra
2022-05-04 15:36 ` Thomas Gleixner
2022-05-04 15:55 ` Jason A. Donenfeld
2022-05-04 16:45 ` Thomas Gleixner
2022-05-04 19:05 ` Jason A. Donenfeld
2022-05-04 21:04 ` Thomas Gleixner
2022-05-04 23:52 ` Jason A. Donenfeld
2022-05-05 0:55 ` Thomas Gleixner
2022-05-05 1:11 ` Jason A. Donenfeld
2022-05-05 1:21 ` Thomas Gleixner
2022-05-05 11:02 ` Jason A. Donenfeld
2022-05-05 11:34 ` David Laight
2022-05-05 11:35 ` Jason A. Donenfeld
2022-05-05 11:53 ` David Laight
2022-05-06 22:34 ` Jason A. Donenfeld
2022-05-07 13:50 ` David Laight
2022-05-05 13:48 ` Jason A. Donenfeld
2022-05-06 22:15 ` Jason A. Donenfeld
2022-05-03 9:03 ` Peter Zijlstra
2022-05-02 10:02 ` [patch 0/3] x86/fpu: Prevent FPU state corruption Filipe Manana
2022-05-02 12:22 ` Borislav Petkov
2022-05-04 15:40 ` Jason A. Donenfeld
2022-05-04 18:05 ` Thomas Gleixner
2022-05-18 1:02 ` Jason A. Donenfeld
2022-05-18 11:14 ` Jason A. Donenfeld
2022-05-18 11:18 ` Jason A. Donenfeld
2022-05-18 13:09 ` Thomas Gleixner
2022-05-18 14:08 ` Jason A. Donenfeld
2022-05-25 20:36 ` Jason A. Donenfeld
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220501193102.704267030@linutronix.de \
--to=tglx@linutronix.de \
--cc=fdmanana@suse.com \
--cc=linux-kernel@vger.kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox