diff options
author | Atsushi Nemoto <anemo@mba.ocn.ne.jp> | 2007-04-16 10:19:44 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2007-04-20 09:58:37 -0400 |
commit | faea62346444ce5b1dba8fb5291d95b676522c42 (patch) | |
tree | d00e53763ca9b145348e5754aaf0cd4dcbb12123 | |
parent | 5323180db75d562a287cb2020b07c9422df13df6 (diff) |
[MIPS] Retry {save,restore}_fp_context if failed in atomic context.
The save_fp_context()/restore_fp_context() might sleep on accessing
user stack and therefore might lose FPU ownership in middle of them.
If these function failed due to "in_atomic" test in do_page_fault,
touch the sigcontext area in non-atomic context and retry these
save/restore operation.
This is a replacement of a (broken) fix which was titled "Allow CpU
exception in kernel partially".
Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r-- | arch/mips/kernel/signal-common.h | 9 | ||||
-rw-r--r-- | arch/mips/kernel/signal.c | 52 | ||||
-rw-r--r-- | arch/mips/kernel/signal32.c | 52 | ||||
-rw-r--r-- | include/asm-mips/fpu.h | 9 |
4 files changed, 102 insertions, 20 deletions
diff --git a/arch/mips/kernel/signal-common.h b/arch/mips/kernel/signal-common.h index 297dfcb97524..c0faabd52010 100644 --- a/arch/mips/kernel/signal-common.h +++ b/arch/mips/kernel/signal-common.h | |||
@@ -34,4 +34,13 @@ extern int install_sigtramp(unsigned int __user *tramp, unsigned int syscall); | |||
34 | /* Check and clear pending FPU exceptions in saved CSR */ | 34 | /* Check and clear pending FPU exceptions in saved CSR */ |
35 | extern int fpcsr_pending(unsigned int __user *fpcsr); | 35 | extern int fpcsr_pending(unsigned int __user *fpcsr); |
36 | 36 | ||
37 | /* Make sure we will not lose FPU ownership */ | ||
38 | #ifdef CONFIG_PREEMPT | ||
39 | #define lock_fpu_owner() preempt_disable() | ||
40 | #define unlock_fpu_owner() preempt_enable() | ||
41 | #else | ||
42 | #define lock_fpu_owner() pagefault_disable() | ||
43 | #define unlock_fpu_owner() pagefault_enable() | ||
44 | #endif | ||
45 | |||
37 | #endif /* __SIGNAL_COMMON_H */ | 46 | #endif /* __SIGNAL_COMMON_H */ |
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index fa581192de21..07d67309451a 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/ptrace.h> | 20 | #include <linux/ptrace.h> |
21 | #include <linux/unistd.h> | 21 | #include <linux/unistd.h> |
22 | #include <linux/compiler.h> | 22 | #include <linux/compiler.h> |
23 | #include <linux/uaccess.h> | ||
23 | 24 | ||
24 | #include <asm/abi.h> | 25 | #include <asm/abi.h> |
25 | #include <asm/asm.h> | 26 | #include <asm/asm.h> |
@@ -27,7 +28,6 @@ | |||
27 | #include <asm/cacheflush.h> | 28 | #include <asm/cacheflush.h> |
28 | #include <asm/fpu.h> | 29 | #include <asm/fpu.h> |
29 | #include <asm/sim.h> | 30 | #include <asm/sim.h> |
30 | #include <asm/uaccess.h> | ||
31 | #include <asm/ucontext.h> | 31 | #include <asm/ucontext.h> |
32 | #include <asm/cpu-features.h> | 32 | #include <asm/cpu-features.h> |
33 | #include <asm/war.h> | 33 | #include <asm/war.h> |
@@ -78,6 +78,46 @@ struct rt_sigframe { | |||
78 | /* | 78 | /* |
79 | * Helper routines | 79 | * Helper routines |
80 | */ | 80 | */ |
81 | static int protected_save_fp_context(struct sigcontext __user *sc) | ||
82 | { | ||
83 | int err; | ||
84 | while (1) { | ||
85 | lock_fpu_owner(); | ||
86 | own_fpu_inatomic(1); | ||
87 | err = save_fp_context(sc); /* this might fail */ | ||
88 | unlock_fpu_owner(); | ||
89 | if (likely(!err)) | ||
90 | break; | ||
91 | /* touch the sigcontext and try again */ | ||
92 | err = __put_user(0, &sc->sc_fpregs[0]) | | ||
93 | __put_user(0, &sc->sc_fpregs[31]) | | ||
94 | __put_user(0, &sc->sc_fpc_csr); | ||
95 | if (err) | ||
96 | break; /* really bad sigcontext */ | ||
97 | } | ||
98 | return err; | ||
99 | } | ||
100 | |||
101 | static int protected_restore_fp_context(struct sigcontext __user *sc) | ||
102 | { | ||
103 | int err, tmp; | ||
104 | while (1) { | ||
105 | lock_fpu_owner(); | ||
106 | own_fpu_inatomic(0); | ||
107 | err = restore_fp_context(sc); /* this might fail */ | ||
108 | unlock_fpu_owner(); | ||
109 | if (likely(!err)) | ||
110 | break; | ||
111 | /* touch the sigcontext and try again */ | ||
112 | err = __get_user(tmp, &sc->sc_fpregs[0]) | | ||
113 | __get_user(tmp, &sc->sc_fpregs[31]) | | ||
114 | __get_user(tmp, &sc->sc_fpc_csr); | ||
115 | if (err) | ||
116 | break; /* really bad sigcontext */ | ||
117 | } | ||
118 | return err; | ||
119 | } | ||
120 | |||
81 | int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | 121 | int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) |
82 | { | 122 | { |
83 | int err = 0; | 123 | int err = 0; |
@@ -113,10 +153,7 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
113 | * Save FPU state to signal context. Signal handler | 153 | * Save FPU state to signal context. Signal handler |
114 | * will "inherit" current FPU state. | 154 | * will "inherit" current FPU state. |
115 | */ | 155 | */ |
116 | preempt_disable(); | 156 | err |= protected_save_fp_context(sc); |
117 | own_fpu(1); | ||
118 | err |= save_fp_context(sc); | ||
119 | preempt_enable(); | ||
120 | } | 157 | } |
121 | return err; | 158 | return err; |
122 | } | 159 | } |
@@ -148,10 +185,7 @@ check_and_restore_fp_context(struct sigcontext __user *sc) | |||
148 | err = sig = fpcsr_pending(&sc->sc_fpc_csr); | 185 | err = sig = fpcsr_pending(&sc->sc_fpc_csr); |
149 | if (err > 0) | 186 | if (err > 0) |
150 | err = 0; | 187 | err = 0; |
151 | preempt_disable(); | 188 | err |= protected_restore_fp_context(sc); |
152 | own_fpu(0); | ||
153 | err |= restore_fp_context(sc); | ||
154 | preempt_enable(); | ||
155 | return err ?: sig; | 189 | return err ?: sig; |
156 | } | 190 | } |
157 | 191 | ||
diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c index 53a337cfeb66..b9a014411f83 100644 --- a/arch/mips/kernel/signal32.c +++ b/arch/mips/kernel/signal32.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/compat.h> | 22 | #include <linux/compat.h> |
23 | #include <linux/suspend.h> | 23 | #include <linux/suspend.h> |
24 | #include <linux/compiler.h> | 24 | #include <linux/compiler.h> |
25 | #include <linux/uaccess.h> | ||
25 | 26 | ||
26 | #include <asm/abi.h> | 27 | #include <asm/abi.h> |
27 | #include <asm/asm.h> | 28 | #include <asm/asm.h> |
@@ -29,7 +30,6 @@ | |||
29 | #include <linux/bitops.h> | 30 | #include <linux/bitops.h> |
30 | #include <asm/cacheflush.h> | 31 | #include <asm/cacheflush.h> |
31 | #include <asm/sim.h> | 32 | #include <asm/sim.h> |
32 | #include <asm/uaccess.h> | ||
33 | #include <asm/ucontext.h> | 33 | #include <asm/ucontext.h> |
34 | #include <asm/system.h> | 34 | #include <asm/system.h> |
35 | #include <asm/fpu.h> | 35 | #include <asm/fpu.h> |
@@ -176,6 +176,46 @@ struct rt_sigframe32 { | |||
176 | /* | 176 | /* |
177 | * sigcontext handlers | 177 | * sigcontext handlers |
178 | */ | 178 | */ |
179 | static int protected_save_fp_context32(struct sigcontext32 __user *sc) | ||
180 | { | ||
181 | int err; | ||
182 | while (1) { | ||
183 | lock_fpu_owner(); | ||
184 | own_fpu_inatomic(1); | ||
185 | err = save_fp_context32(sc); /* this might fail */ | ||
186 | unlock_fpu_owner(); | ||
187 | if (likely(!err)) | ||
188 | break; | ||
189 | /* touch the sigcontext and try again */ | ||
190 | err = __put_user(0, &sc->sc_fpregs[0]) | | ||
191 | __put_user(0, &sc->sc_fpregs[31]) | | ||
192 | __put_user(0, &sc->sc_fpc_csr); | ||
193 | if (err) | ||
194 | break; /* really bad sigcontext */ | ||
195 | } | ||
196 | return err; | ||
197 | } | ||
198 | |||
199 | static int protected_restore_fp_context32(struct sigcontext32 __user *sc) | ||
200 | { | ||
201 | int err, tmp; | ||
202 | while (1) { | ||
203 | lock_fpu_owner(); | ||
204 | own_fpu_inatomic(0); | ||
205 | err = restore_fp_context32(sc); /* this might fail */ | ||
206 | unlock_fpu_owner(); | ||
207 | if (likely(!err)) | ||
208 | break; | ||
209 | /* touch the sigcontext and try again */ | ||
210 | err = __get_user(tmp, &sc->sc_fpregs[0]) | | ||
211 | __get_user(tmp, &sc->sc_fpregs[31]) | | ||
212 | __get_user(tmp, &sc->sc_fpc_csr); | ||
213 | if (err) | ||
214 | break; /* really bad sigcontext */ | ||
215 | } | ||
216 | return err; | ||
217 | } | ||
218 | |||
179 | static int setup_sigcontext32(struct pt_regs *regs, | 219 | static int setup_sigcontext32(struct pt_regs *regs, |
180 | struct sigcontext32 __user *sc) | 220 | struct sigcontext32 __user *sc) |
181 | { | 221 | { |
@@ -209,10 +249,7 @@ static int setup_sigcontext32(struct pt_regs *regs, | |||
209 | * Save FPU state to signal context. Signal handler | 249 | * Save FPU state to signal context. Signal handler |
210 | * will "inherit" current FPU state. | 250 | * will "inherit" current FPU state. |
211 | */ | 251 | */ |
212 | preempt_disable(); | 252 | err |= protected_save_fp_context32(sc); |
213 | own_fpu(1); | ||
214 | err |= save_fp_context32(sc); | ||
215 | preempt_enable(); | ||
216 | } | 253 | } |
217 | return err; | 254 | return err; |
218 | } | 255 | } |
@@ -225,10 +262,7 @@ check_and_restore_fp_context32(struct sigcontext32 __user *sc) | |||
225 | err = sig = fpcsr_pending(&sc->sc_fpc_csr); | 262 | err = sig = fpcsr_pending(&sc->sc_fpc_csr); |
226 | if (err > 0) | 263 | if (err > 0) |
227 | err = 0; | 264 | err = 0; |
228 | preempt_disable(); | 265 | err |= protected_restore_fp_context32(sc); |
229 | own_fpu(0); | ||
230 | err |= restore_fp_context32(sc); | ||
231 | preempt_enable(); | ||
232 | return err ?: sig; | 266 | return err ?: sig; |
233 | } | 267 | } |
234 | 268 | ||
diff --git a/include/asm-mips/fpu.h b/include/asm-mips/fpu.h index 71436f90203f..b414a7d9db43 100644 --- a/include/asm-mips/fpu.h +++ b/include/asm-mips/fpu.h | |||
@@ -100,14 +100,19 @@ static inline void __own_fpu(void) | |||
100 | set_thread_flag(TIF_USEDFPU); | 100 | set_thread_flag(TIF_USEDFPU); |
101 | } | 101 | } |
102 | 102 | ||
103 | static inline void own_fpu(int restore) | 103 | static inline void own_fpu_inatomic(int restore) |
104 | { | 104 | { |
105 | preempt_disable(); | ||
106 | if (cpu_has_fpu && !__is_fpu_owner()) { | 105 | if (cpu_has_fpu && !__is_fpu_owner()) { |
107 | __own_fpu(); | 106 | __own_fpu(); |
108 | if (restore) | 107 | if (restore) |
109 | _restore_fp(current); | 108 | _restore_fp(current); |
110 | } | 109 | } |
110 | } | ||
111 | |||
112 | static inline void own_fpu(int restore) | ||
113 | { | ||
114 | preempt_disable(); | ||
115 | own_fpu_inatomic(restore); | ||
111 | preempt_enable(); | 116 | preempt_enable(); |
112 | } | 117 | } |
113 | 118 | ||