i387: don't ever touch TS_USEDFPU directly, use helper functions
[firefly-linux-kernel-4.4.55.git] / arch / x86 / include / asm / i387.h
index c9e09ea05644fdea45227388d87e04be3db0390f..730d7becc97ac819b60f3bb989761dce9633688a 100644 (file)
@@ -29,7 +29,7 @@ extern unsigned int sig_xstate_size;
 extern void fpu_init(void);
 extern void mxcsr_feature_mask_init(void);
 extern int init_fpu(struct task_struct *child);
-extern asmlinkage void math_state_restore(void);
+extern void math_state_restore(void);
 extern void __math_state_restore(void);
 extern int dump_fpu(struct pt_regs *, struct user_i387_struct *);
 
@@ -259,7 +259,6 @@ static inline void fpu_save_init(struct fpu *fpu)
 static inline void __save_init_fpu(struct task_struct *tsk)
 {
        fpu_save_init(&tsk->thread.fpu);
-       task_thread_info(tsk)->status &= ~TS_USEDFPU;
 }
 
 static inline int fpu_fxrstor_checking(struct fpu *fpu)
@@ -280,6 +279,47 @@ static inline int restore_fpu_checking(struct task_struct *tsk)
        return fpu_restore_checking(&tsk->thread.fpu);
 }
 
+/*
+ * Software FPU state helpers. Careful: these need to
+ * be preemption protection *and* they need to be
+ * properly paired with the CR0.TS changes!
+ */
+static inline int __thread_has_fpu(struct thread_info *ti)
+{
+       return ti->status & TS_USEDFPU;
+}
+
+/* Must be paired with an 'stts' after! */
+static inline void __thread_clear_has_fpu(struct thread_info *ti)
+{
+       ti->status &= ~TS_USEDFPU;
+}
+
+/* Must be paired with a 'clts' before! */
+static inline void __thread_set_has_fpu(struct thread_info *ti)
+{
+       ti->status |= TS_USEDFPU;
+}
+
+/*
+ * Encapsulate the CR0.TS handling together with the
+ * software flag.
+ *
+ * These generally need preemption protection to work,
+ * do try to avoid using these on their own.
+ */
+static inline void __thread_fpu_end(struct thread_info *ti)
+{
+       __thread_clear_has_fpu(ti);
+       stts();
+}
+
+static inline void __thread_fpu_begin(struct thread_info *ti)
+{
+       clts();
+       __thread_set_has_fpu(ti);
+}
+
 /*
  * Signal frame handlers...
  */
@@ -288,32 +328,78 @@ extern int restore_i387_xstate(void __user *buf);
 
 static inline void __unlazy_fpu(struct task_struct *tsk)
 {
-       if (task_thread_info(tsk)->status & TS_USEDFPU) {
+       if (__thread_has_fpu(task_thread_info(tsk))) {
                __save_init_fpu(tsk);
-               stts();
+               __thread_fpu_end(task_thread_info(tsk));
        } else
                tsk->fpu_counter = 0;
 }
 
 static inline void __clear_fpu(struct task_struct *tsk)
 {
-       if (task_thread_info(tsk)->status & TS_USEDFPU) {
+       if (__thread_has_fpu(task_thread_info(tsk))) {
                /* Ignore delayed exceptions from user space */
                asm volatile("1: fwait\n"
                             "2:\n"
                             _ASM_EXTABLE(1b, 2b));
-               task_thread_info(tsk)->status &= ~TS_USEDFPU;
-               stts();
+               __thread_fpu_end(task_thread_info(tsk));
        }
 }
 
+/*
+ * Were we in an interrupt that interrupted kernel mode?
+ *
+ * We can do a kernel_fpu_begin/end() pair *ONLY* if that
+ * pair does nothing at all: the thread must not have fpu (so
+ * that we don't try to save the FPU state), and TS must
+ * be set (so that the clts/stts pair does nothing that is
+ * visible in the interrupted kernel thread).
+ */
+static inline bool interrupted_kernel_fpu_idle(void)
+{
+       return !__thread_has_fpu(current_thread_info()) &&
+               (read_cr0() & X86_CR0_TS);
+}
+
+/*
+ * Were we in user mode (or vm86 mode) when we were
+ * interrupted?
+ *
+ * Doing kernel_fpu_begin/end() is ok if we are running
+ * in an interrupt context from user mode - we'll just
+ * save the FPU state as required.
+ */
+static inline bool interrupted_user_mode(void)
+{
+       struct pt_regs *regs = get_irq_regs();
+       return regs && user_mode_vm(regs);
+}
+
+/*
+ * Can we use the FPU in kernel mode with the
+ * whole "kernel_fpu_begin/end()" sequence?
+ *
+ * It's always ok in process context (ie "not interrupt")
+ * but it is sometimes ok even from an irq.
+ */
+static inline bool irq_fpu_usable(void)
+{
+       return !in_interrupt() ||
+               interrupted_user_mode() ||
+               interrupted_kernel_fpu_idle();
+}
+
 static inline void kernel_fpu_begin(void)
 {
        struct thread_info *me = current_thread_info();
+
+       WARN_ON_ONCE(!irq_fpu_usable());
        preempt_disable();
-       if (me->status & TS_USEDFPU)
+       if (__thread_has_fpu(me)) {
                __save_init_fpu(me->task);
-       else
+               __thread_clear_has_fpu(me);
+               /* We do 'stts()' in kernel_fpu_end() */
+       } else
                clts();
 }
 
@@ -323,14 +409,6 @@ static inline void kernel_fpu_end(void)
        preempt_enable();
 }
 
-static inline bool irq_fpu_usable(void)
-{
-       struct pt_regs *regs;
-
-       return !in_interrupt() || !(regs = get_irq_regs()) || \
-               user_mode(regs) || (read_cr0() & X86_CR0_TS);
-}
-
 /*
  * Some instructions like VIA's padlock instructions generate a spurious
  * DNA fault but don't modify SSE registers. And these instructions
@@ -362,14 +440,54 @@ static inline void irq_ts_restore(int TS_state)
                stts();
 }
 
+/*
+ * The question "does this thread have fpu access?"
+ * is slightly racy, since preemption could come in
+ * and revoke it immediately after the test.
+ *
+ * However, even in that very unlikely scenario,
+ * we can just assume we have FPU access - typically
+ * to save the FP state - we'll just take a #NM
+ * fault and get the FPU access back.
+ *
+ * The actual user_fpu_begin/end() functions
+ * need to be preemption-safe, though.
+ *
+ * NOTE! user_fpu_end() must be used only after you
+ * have saved the FP state, and user_fpu_begin() must
+ * be used only immediately before restoring it.
+ * These functions do not do any save/restore on
+ * their own.
+ */
+static inline int user_has_fpu(void)
+{
+       return __thread_has_fpu(current_thread_info());
+}
+
+static inline void user_fpu_end(void)
+{
+       preempt_disable();
+       __thread_fpu_end(current_thread_info());
+       preempt_enable();
+}
+
+static inline void user_fpu_begin(void)
+{
+       preempt_disable();
+       if (!user_has_fpu())
+               __thread_fpu_begin(current_thread_info());
+       preempt_enable();
+}
+
 /*
  * These disable preemption on their own and are safe
  */
 static inline void save_init_fpu(struct task_struct *tsk)
 {
+       WARN_ON_ONCE(!__thread_has_fpu(task_thread_info(tsk)));
        preempt_disable();
        __save_init_fpu(tsk);
-       stts();
+       __thread_fpu_end(task_thread_info(tsk));
        preempt_enable();
 }