diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-02-21 13:25:45 -0500 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2012-02-21 17:12:46 -0500 |
commit | 8546c008924d5fd1724fa698eaa92b414bafd50d (patch) | |
tree | fe2d3f50b350c884201c57ca6c331dd867c5d1e8 /arch/x86/kernel/i387.c | |
parent | 27e74da9800289e69ba907777df1e2085231eff7 (diff) |
i387: Uninline the generic FP helpers that we expose to kernel modules
Instead of exporting the very low-level internals of the FPU state
save/restore code (ie things like 'fpu_owner_task'), we should export
the higher-level interfaces.
Inlining these things is pointless anyway: sure, sometimes the end
result is small, but while 'stts()' can result in just three x86
instructions, those are not cheap instructions (writing %cr0 is a
serializing instruction and a very slow one at that).
So the overhead of a function call is not noticeable, and we really
don't want random modules mucking about with our internal state save
logic anyway.
So this unexports 'fpu_owner_task', and instead uninlines and exports
the actual functions that modules can use: fpu_kernel_begin/end() and
unlazy_fpu().
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Link: http://lkml.kernel.org/r/alpine.LFD.2.02.1202211339590.5354@i5.linux-foundation.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86/kernel/i387.c')
-rw-r--r-- | arch/x86/kernel/i387.c | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index 739d8598f78..17b7549c413 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c | |||
@@ -32,6 +32,86 @@ | |||
32 | # define user32_fxsr_struct user_fxsr_struct | 32 | # define user32_fxsr_struct user_fxsr_struct |
33 | #endif | 33 | #endif |
34 | 34 | ||
35 | /* | ||
36 | * Were we in an interrupt that interrupted kernel mode? | ||
37 | * | ||
38 | * We can do a kernel_fpu_begin/end() pair *ONLY* if that | ||
39 | * pair does nothing at all: the thread must not have fpu (so | ||
40 | * that we don't try to save the FPU state), and TS must | ||
41 | * be set (so that the clts/stts pair does nothing that is | ||
42 | * visible in the interrupted kernel thread). | ||
43 | */ | ||
44 | static inline bool interrupted_kernel_fpu_idle(void) | ||
45 | { | ||
46 | return !__thread_has_fpu(current) && | ||
47 | (read_cr0() & X86_CR0_TS); | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * Were we in user mode (or vm86 mode) when we were | ||
52 | * interrupted? | ||
53 | * | ||
54 | * Doing kernel_fpu_begin/end() is ok if we are running | ||
55 | * in an interrupt context from user mode - we'll just | ||
56 | * save the FPU state as required. | ||
57 | */ | ||
58 | static inline bool interrupted_user_mode(void) | ||
59 | { | ||
60 | struct pt_regs *regs = get_irq_regs(); | ||
61 | return regs && user_mode_vm(regs); | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * Can we use the FPU in kernel mode with the | ||
66 | * whole "kernel_fpu_begin/end()" sequence? | ||
67 | * | ||
68 | * It's always ok in process context (ie "not interrupt") | ||
69 | * but it is sometimes ok even from an irq. | ||
70 | */ | ||
71 | bool irq_fpu_usable(void) | ||
72 | { | ||
73 | return !in_interrupt() || | ||
74 | interrupted_user_mode() || | ||
75 | interrupted_kernel_fpu_idle(); | ||
76 | } | ||
77 | EXPORT_SYMBOL(irq_fpu_usable); | ||
78 | |||
79 | void kernel_fpu_begin(void) | ||
80 | { | ||
81 | struct task_struct *me = current; | ||
82 | |||
83 | WARN_ON_ONCE(!irq_fpu_usable()); | ||
84 | preempt_disable(); | ||
85 | if (__thread_has_fpu(me)) { | ||
86 | __save_init_fpu(me); | ||
87 | __thread_clear_has_fpu(me); | ||
88 | /* We do 'stts()' in kernel_fpu_end() */ | ||
89 | } else { | ||
90 | percpu_write(fpu_owner_task, NULL); | ||
91 | clts(); | ||
92 | } | ||
93 | } | ||
94 | EXPORT_SYMBOL(kernel_fpu_begin); | ||
95 | |||
96 | void kernel_fpu_end(void) | ||
97 | { | ||
98 | stts(); | ||
99 | preempt_enable(); | ||
100 | } | ||
101 | EXPORT_SYMBOL(kernel_fpu_end); | ||
102 | |||
103 | void unlazy_fpu(struct task_struct *tsk) | ||
104 | { | ||
105 | preempt_disable(); | ||
106 | if (__thread_has_fpu(tsk)) { | ||
107 | __save_init_fpu(tsk); | ||
108 | __thread_fpu_end(tsk); | ||
109 | } else | ||
110 | tsk->fpu_counter = 0; | ||
111 | preempt_enable(); | ||
112 | } | ||
113 | EXPORT_SYMBOL(unlazy_fpu); | ||
114 | |||
35 | #ifdef CONFIG_MATH_EMULATION | 115 | #ifdef CONFIG_MATH_EMULATION |
36 | # define HAVE_HWFP (boot_cpu_data.hard_math) | 116 | # define HAVE_HWFP (boot_cpu_data.hard_math) |
37 | #else | 117 | #else |