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 | |
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')
-rw-r--r-- | arch/x86/include/asm/i387.h | 78 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/common.c | 2 | ||||
-rw-r--r-- | arch/x86/kernel/i387.c | 80 |
3 files changed, 84 insertions, 76 deletions
diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index 247904945d3f..0c1031d354f2 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h | |||
@@ -419,70 +419,9 @@ static inline void __clear_fpu(struct task_struct *tsk) | |||
419 | } | 419 | } |
420 | } | 420 | } |
421 | 421 | ||
422 | /* | 422 | extern bool irq_fpu_usable(void); |
423 | * Were we in an interrupt that interrupted kernel mode? | 423 | extern void kernel_fpu_begin(void); |
424 | * | 424 | extern void kernel_fpu_end(void); |
425 | * We can do a kernel_fpu_begin/end() pair *ONLY* if that | ||
426 | * pair does nothing at all: the thread must not have fpu (so | ||
427 | * that we don't try to save the FPU state), and TS must | ||
428 | * be set (so that the clts/stts pair does nothing that is | ||
429 | * visible in the interrupted kernel thread). | ||
430 | */ | ||
431 | static inline bool interrupted_kernel_fpu_idle(void) | ||
432 | { | ||
433 | return !__thread_has_fpu(current) && | ||
434 | (read_cr0() & X86_CR0_TS); | ||
435 | } | ||
436 | |||
437 | /* | ||
438 | * Were we in user mode (or vm86 mode) when we were | ||
439 | * interrupted? | ||
440 | * | ||
441 | * Doing kernel_fpu_begin/end() is ok if we are running | ||
442 | * in an interrupt context from user mode - we'll just | ||
443 | * save the FPU state as required. | ||
444 | */ | ||
445 | static inline bool interrupted_user_mode(void) | ||
446 | { | ||
447 | struct pt_regs *regs = get_irq_regs(); | ||
448 | return regs && user_mode_vm(regs); | ||
449 | } | ||
450 | |||
451 | /* | ||
452 | * Can we use the FPU in kernel mode with the | ||
453 | * whole "kernel_fpu_begin/end()" sequence? | ||
454 | * | ||
455 | * It's always ok in process context (ie "not interrupt") | ||
456 | * but it is sometimes ok even from an irq. | ||
457 | */ | ||
458 | static inline bool irq_fpu_usable(void) | ||
459 | { | ||
460 | return !in_interrupt() || | ||
461 | interrupted_user_mode() || | ||
462 | interrupted_kernel_fpu_idle(); | ||
463 | } | ||
464 | |||
465 | static inline void kernel_fpu_begin(void) | ||
466 | { | ||
467 | struct task_struct *me = current; | ||
468 | |||
469 | WARN_ON_ONCE(!irq_fpu_usable()); | ||
470 | preempt_disable(); | ||
471 | if (__thread_has_fpu(me)) { | ||
472 | __save_init_fpu(me); | ||
473 | __thread_clear_has_fpu(me); | ||
474 | /* We do 'stts()' in kernel_fpu_end() */ | ||
475 | } else { | ||
476 | percpu_write(fpu_owner_task, NULL); | ||
477 | clts(); | ||
478 | } | ||
479 | } | ||
480 | |||
481 | static inline void kernel_fpu_end(void) | ||
482 | { | ||
483 | stts(); | ||
484 | preempt_enable(); | ||
485 | } | ||
486 | 425 | ||
487 | /* | 426 | /* |
488 | * Some instructions like VIA's padlock instructions generate a spurious | 427 | * Some instructions like VIA's padlock instructions generate a spurious |
@@ -566,16 +505,7 @@ static inline void save_init_fpu(struct task_struct *tsk) | |||
566 | preempt_enable(); | 505 | preempt_enable(); |
567 | } | 506 | } |
568 | 507 | ||
569 | static inline void unlazy_fpu(struct task_struct *tsk) | 508 | extern void unlazy_fpu(struct task_struct *tsk); |
570 | { | ||
571 | preempt_disable(); | ||
572 | if (__thread_has_fpu(tsk)) { | ||
573 | __save_init_fpu(tsk); | ||
574 | __thread_fpu_end(tsk); | ||
575 | } else | ||
576 | tsk->fpu_counter = 0; | ||
577 | preempt_enable(); | ||
578 | } | ||
579 | 509 | ||
580 | static inline void clear_fpu(struct task_struct *tsk) | 510 | static inline void clear_fpu(struct task_struct *tsk) |
581 | { | 511 | { |
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index c0f7d68d318f..cb71b01ab66e 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c | |||
@@ -1045,7 +1045,6 @@ DEFINE_PER_CPU(char *, irq_stack_ptr) = | |||
1045 | DEFINE_PER_CPU(unsigned int, irq_count) = -1; | 1045 | DEFINE_PER_CPU(unsigned int, irq_count) = -1; |
1046 | 1046 | ||
1047 | DEFINE_PER_CPU(struct task_struct *, fpu_owner_task); | 1047 | DEFINE_PER_CPU(struct task_struct *, fpu_owner_task); |
1048 | EXPORT_PER_CPU_SYMBOL(fpu_owner_task); | ||
1049 | 1048 | ||
1050 | /* | 1049 | /* |
1051 | * Special IST stacks which the CPU switches to when it calls | 1050 | * Special IST stacks which the CPU switches to when it calls |
@@ -1115,7 +1114,6 @@ void debug_stack_reset(void) | |||
1115 | DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task; | 1114 | DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task; |
1116 | EXPORT_PER_CPU_SYMBOL(current_task); | 1115 | EXPORT_PER_CPU_SYMBOL(current_task); |
1117 | DEFINE_PER_CPU(struct task_struct *, fpu_owner_task); | 1116 | DEFINE_PER_CPU(struct task_struct *, fpu_owner_task); |
1118 | EXPORT_PER_CPU_SYMBOL(fpu_owner_task); | ||
1119 | 1117 | ||
1120 | #ifdef CONFIG_CC_STACKPROTECTOR | 1118 | #ifdef CONFIG_CC_STACKPROTECTOR |
1121 | DEFINE_PER_CPU_ALIGNED(struct stack_canary, stack_canary); | 1119 | DEFINE_PER_CPU_ALIGNED(struct stack_canary, stack_canary); |
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index 739d8598f789..17b7549c4134 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 |