[PATCH] x86-64: always use physical delivery mode on > 8 CPUs
[sfrench/cifs-2.6.git] / include / asm-x86_64 / i387.h
index 57f7e14338492df5c8c1b37f54658d6f61894afd..0217b74cc9fc0cd30456d40929e6196229680178 100644 (file)
@@ -24,14 +24,17 @@ extern unsigned int mxcsr_feature_mask;
 extern void mxcsr_feature_mask_init(void);
 extern void init_fpu(struct task_struct *child);
 extern int save_i387(struct _fpstate __user *buf);
+extern asmlinkage void math_state_restore(void);
 
 /*
  * FPU lazy state save handling...
  */
 
 #define unlazy_fpu(tsk) do { \
-       if ((tsk)->thread_info->status & TS_USEDFPU) \
-               save_init_fpu(tsk); \
+       if (task_thread_info(tsk)->status & TS_USEDFPU) \
+               save_init_fpu(tsk);                     \
+       else                                            \
+               tsk->fpu_counter = 0;                   \
 } while (0)
 
 /* Ignore delayed exceptions from user space */
@@ -46,9 +49,9 @@ static inline void tolerant_fwait(void)
 }
 
 #define clear_fpu(tsk) do { \
-       if ((tsk)->thread_info->status & TS_USEDFPU) {          \
+       if (task_thread_info(tsk)->status & TS_USEDFPU) {       \
                tolerant_fwait();                               \
-               (tsk)->thread_info->status &= ~TS_USEDFPU;      \
+               task_thread_info(tsk)->status &= ~TS_USEDFPU;   \
                stts();                                         \
        }                                                       \
 } while (0)
@@ -72,6 +75,23 @@ extern int set_fpregs(struct task_struct *tsk,
 #define set_fpu_swd(t,val) ((t)->thread.i387.fxsave.swd = (val))
 #define set_fpu_fxsr_twd(t,val) ((t)->thread.i387.fxsave.twd = (val))
 
+#define X87_FSW_ES (1 << 7)    /* Exception Summary */
+
+/* AMD CPUs don't save/restore FDP/FIP/FOP unless an exception
+   is pending. Clear the x87 state here by setting it to fixed
+   values. The kernel data segment can be sometimes 0 and sometimes
+   new user value. Both should be ok.
+   Use the PDA as safe address because it should be already in L1. */
+static inline void clear_fpu_state(struct i387_fxsave_struct *fx)
+{
+       if (unlikely(fx->swd & X87_FSW_ES))
+                asm volatile("fnclex");
+       alternative_input(ASM_NOP8 ASM_NOP2,
+                    "    emms\n"               /* clear stack tags */
+                    "    fildl %%gs:0",        /* load to clear state */
+                    X86_FEATURE_FXSAVE_LEAK);
+}
+
 static inline int restore_fpu_checking(struct i387_fxsave_struct *fx) 
 { 
        int err;
@@ -117,8 +137,9 @@ static inline int save_i387_checking(struct i387_fxsave_struct __user *fx)
 #else
                     : [fx] "cdaSDb" (fx), "0" (0));
 #endif
-       if (unlikely(err))
-               __clear_user(fx, sizeof(struct i387_fxsave_struct));
+       if (unlikely(err) && __clear_user(fx, sizeof(struct i387_fxsave_struct)))
+               err = -EFAULT;
+       /* No need to clear here because the caller clears USED_MATH */
        return err;
 } 
 
@@ -149,7 +170,7 @@ static inline void __fxsave_clear(struct task_struct *tsk)
                                "i" (offsetof(__typeof__(*tsk),
                                              thread.i387.fxsave)));
 #endif
-       __asm__ __volatile__("fnclex");
+       clear_fpu_state(&tsk->thread.i387.fxsave);
 }
 
 static inline void kernel_fpu_begin(void)
@@ -170,10 +191,10 @@ static inline void kernel_fpu_end(void)
        preempt_enable();
 }
 
-static inline void save_init_fpu( struct task_struct *tsk )
+static inline void save_init_fpu(struct task_struct *tsk)
 {
        __fxsave_clear(tsk);
-       tsk->thread_info->status &= ~TS_USEDFPU;
+       task_thread_info(tsk)->status &= ~TS_USEDFPU;
        stts();
 }