diff options
Diffstat (limited to 'arch/x86/kernel/i387.c')
-rw-r--r-- | arch/x86/kernel/i387.c | 83 |
1 files changed, 82 insertions, 1 deletions
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index 739d8598f789..7734bcbb5a3a 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <asm/uaccess.h> | 16 | #include <asm/uaccess.h> |
17 | #include <asm/ptrace.h> | 17 | #include <asm/ptrace.h> |
18 | #include <asm/i387.h> | 18 | #include <asm/i387.h> |
19 | #include <asm/fpu-internal.h> | ||
19 | #include <asm/user.h> | 20 | #include <asm/user.h> |
20 | 21 | ||
21 | #ifdef CONFIG_X86_64 | 22 | #ifdef CONFIG_X86_64 |
@@ -32,6 +33,86 @@ | |||
32 | # define user32_fxsr_struct user_fxsr_struct | 33 | # define user32_fxsr_struct user_fxsr_struct |
33 | #endif | 34 | #endif |
34 | 35 | ||
36 | /* | ||
37 | * Were we in an interrupt that interrupted kernel mode? | ||
38 | * | ||
39 | * We can do a kernel_fpu_begin/end() pair *ONLY* if that | ||
40 | * pair does nothing at all: the thread must not have fpu (so | ||
41 | * that we don't try to save the FPU state), and TS must | ||
42 | * be set (so that the clts/stts pair does nothing that is | ||
43 | * visible in the interrupted kernel thread). | ||
44 | */ | ||
45 | static inline bool interrupted_kernel_fpu_idle(void) | ||
46 | { | ||
47 | return !__thread_has_fpu(current) && | ||
48 | (read_cr0() & X86_CR0_TS); | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * Were we in user mode (or vm86 mode) when we were | ||
53 | * interrupted? | ||
54 | * | ||
55 | * Doing kernel_fpu_begin/end() is ok if we are running | ||
56 | * in an interrupt context from user mode - we'll just | ||
57 | * save the FPU state as required. | ||
58 | */ | ||
59 | static inline bool interrupted_user_mode(void) | ||
60 | { | ||
61 | struct pt_regs *regs = get_irq_regs(); | ||
62 | return regs && user_mode_vm(regs); | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * Can we use the FPU in kernel mode with the | ||
67 | * whole "kernel_fpu_begin/end()" sequence? | ||
68 | * | ||
69 | * It's always ok in process context (ie "not interrupt") | ||
70 | * but it is sometimes ok even from an irq. | ||
71 | */ | ||
72 | bool irq_fpu_usable(void) | ||
73 | { | ||
74 | return !in_interrupt() || | ||
75 | interrupted_user_mode() || | ||
76 | interrupted_kernel_fpu_idle(); | ||
77 | } | ||
78 | EXPORT_SYMBOL(irq_fpu_usable); | ||
79 | |||
80 | void kernel_fpu_begin(void) | ||
81 | { | ||
82 | struct task_struct *me = current; | ||
83 | |||
84 | WARN_ON_ONCE(!irq_fpu_usable()); | ||
85 | preempt_disable(); | ||
86 | if (__thread_has_fpu(me)) { | ||
87 | __save_init_fpu(me); | ||
88 | __thread_clear_has_fpu(me); | ||
89 | /* We do 'stts()' in kernel_fpu_end() */ | ||
90 | } else { | ||
91 | percpu_write(fpu_owner_task, NULL); | ||
92 | clts(); | ||
93 | } | ||
94 | } | ||
95 | EXPORT_SYMBOL(kernel_fpu_begin); | ||
96 | |||
97 | void kernel_fpu_end(void) | ||
98 | { | ||
99 | stts(); | ||
100 | preempt_enable(); | ||
101 | } | ||
102 | EXPORT_SYMBOL(kernel_fpu_end); | ||
103 | |||
104 | void unlazy_fpu(struct task_struct *tsk) | ||
105 | { | ||
106 | preempt_disable(); | ||
107 | if (__thread_has_fpu(tsk)) { | ||
108 | __save_init_fpu(tsk); | ||
109 | __thread_fpu_end(tsk); | ||
110 | } else | ||
111 | tsk->fpu_counter = 0; | ||
112 | preempt_enable(); | ||
113 | } | ||
114 | EXPORT_SYMBOL(unlazy_fpu); | ||
115 | |||
35 | #ifdef CONFIG_MATH_EMULATION | 116 | #ifdef CONFIG_MATH_EMULATION |
36 | # define HAVE_HWFP (boot_cpu_data.hard_math) | 117 | # define HAVE_HWFP (boot_cpu_data.hard_math) |
37 | #else | 118 | #else |
@@ -44,7 +125,7 @@ EXPORT_SYMBOL_GPL(xstate_size); | |||
44 | unsigned int sig_xstate_ia32_size = sizeof(struct _fpstate_ia32); | 125 | unsigned int sig_xstate_ia32_size = sizeof(struct _fpstate_ia32); |
45 | static struct i387_fxsave_struct fx_scratch __cpuinitdata; | 126 | static struct i387_fxsave_struct fx_scratch __cpuinitdata; |
46 | 127 | ||
47 | void __cpuinit mxcsr_feature_mask_init(void) | 128 | static void __cpuinit mxcsr_feature_mask_init(void) |
48 | { | 129 | { |
49 | unsigned long mask = 0; | 130 | unsigned long mask = 0; |
50 | 131 | ||