>>>>> On Sat, 09 Oct 2004 23:38:10 +0900 (JST), Atsushi Nemoto
>>>>> <anemo@mba.ocn.ne.jp> said:
anemo> And here is a revised patch. It fixes:
anemo> * FPU ownership lost in math-emu.
anemo> (revised fix of "Wed, 06 Oct 2004 10:19:20 +0900 (JST)" patch)
anemo> * ieee754_csr corruption by re-entrance of math-emu.
anemo> (from "Wed, 06 Oct 2004 18:40:14 +0900 (JST)" patch)
anemo> * FPU ownership lost in setup/restore sigcontext.
anemo> (from "Thu, 07 Oct 2004 15:20:17 +0900 (JST)" patch)
anemo> * preemption during middle of FPU manipulation. (for preemptable kernel)
anemo> (derived from 030304-b.preempt-mips.patch)
Again, here is a revised patch (against CVS which include some
preemption fixes now)
diff -ur linux-mips-cvs/arch/mips/kernel/ptrace.c
linux-mips/arch/mips/kernel/ptrace.c
--- linux-mips-cvs/arch/mips/kernel/ptrace.c Sun May 9 22:31:30 2004
+++ linux-mips/arch/mips/kernel/ptrace.c Sat Oct 9 22:20:00 2004
@@ -167,10 +167,12 @@
if (!cpu_has_fpu)
break;
+ preempt_disable();
flags = read_c0_status();
__enable_fpu();
__asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
write_c0_status(flags);
+ preempt_enable();
break;
}
default:
diff -ur linux-mips-cvs/arch/mips/kernel/ptrace32.c
linux-mips/arch/mips/kernel/ptrace32.c
--- linux-mips-cvs/arch/mips/kernel/ptrace32.c Mon Nov 24 20:21:44 2003
+++ linux-mips/arch/mips/kernel/ptrace32.c Sat Oct 9 22:20:25 2004
@@ -155,10 +155,12 @@
if (!cpu_has_fpu)
break;
+ preempt_disable();
flags = read_c0_status();
__enable_fpu();
__asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
write_c0_status(flags);
+ preempt_enable();
break;
}
default:
diff -ur linux-mips-cvs/arch/mips/kernel/signal.c
linux-mips/arch/mips/kernel/signal.c
--- linux-mips-cvs/arch/mips/kernel/signal.c Sun Oct 24 23:36:08 2004
+++ linux-mips/arch/mips/kernel/signal.c Sun Oct 24 23:43:36 2004
@@ -181,14 +181,19 @@
err |= __get_user(current->used_math, &sc->sc_used_math);
- preempt_disable();
-
if (current->used_math) {
+ /* make sure restore_fp_context not sleep */
+ struct sigcontext tmpsc;
+ err |= __copy_from_user(&tmpsc.sc_fpregs, &sc->sc_fpregs,
sizeof(tmpsc.sc_fpregs));
+ err |= __get_user(tmpsc.sc_fpc_csr, &sc->sc_fpc_csr);
+ err |= __get_user(tmpsc.sc_fpc_eir, &sc->sc_fpc_eir);
+ preempt_disable();
/* restore fpu context if we have used it before */
own_fpu();
- err |= restore_fp_context(sc);
+ err |= restore_fp_context(&tmpsc);
} else {
/* signal handler may have used FPU. Give it up. */
+ preempt_disable();
lose_fpu();
}
@@ -295,6 +300,7 @@
inline int setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
{
int err = 0;
+ struct sigcontext tmpsc;
err |= __put_user(regs->cp0_epc, &sc->sc_pc);
err |= __put_user(regs->cp0_status, &sc->sc_status);
@@ -333,9 +339,15 @@
own_fpu();
restore_fp(current);
}
- err |= save_fp_context(sc);
+ /* make sure save_fp_context not sleep */
+ err |= save_fp_context(&tmpsc);
preempt_enable();
+
+ err |= __copy_to_user(&sc->sc_fpregs, &tmpsc.sc_fpregs,
+ sizeof(tmpsc.sc_fpregs));
+ err |= __put_user(tmpsc.sc_fpc_csr, &sc->sc_fpc_csr);
+ err |= __put_user(tmpsc.sc_fpc_eir, &sc->sc_fpc_eir);
out:
return err;
diff -ur linux-mips-cvs/arch/mips/kernel/signal32.c
linux-mips/arch/mips/kernel/signal32.c
--- linux-mips-cvs/arch/mips/kernel/signal32.c Sun Oct 24 23:36:09 2004
+++ linux-mips/arch/mips/kernel/signal32.c Sun Oct 24 23:43:30 2004
@@ -364,14 +364,19 @@
err |= __get_user(current->used_math, &sc->sc_used_math);
- preempt_disable();
-
if (current->used_math) {
+ struct sigcontext32 tmpsc;
+ /* make sure restore_fp_context32 not sleep */
+ err |= __copy_from_user(&tmpsc.sc_fpregs, &sc->sc_fpregs,
sizeof(tmpsc.sc_fpregs));
+ err |= __get_user(tmpsc.sc_fpc_csr, &sc->sc_fpc_csr);
+ err |= __get_user(tmpsc.sc_fpc_eir, &sc->sc_fpc_eir);
+ preempt_disable();
/* restore fpu context if we have used it before */
own_fpu();
- err |= restore_fp_context32(sc);
+ err |= restore_fp_context32(&tmpsc);
} else {
/* signal handler may have used FPU. Give it up. */
+ preempt_disable();
lose_fpu();
}
@@ -530,6 +535,7 @@
struct sigcontext32 *sc)
{
int err = 0;
+ struct sigcontext32 tmpsc;
err |= __put_user(regs->cp0_epc, &sc->sc_pc);
err |= __put_user(regs->cp0_status, &sc->sc_status);
@@ -568,9 +574,15 @@
own_fpu();
restore_fp(current);
}
- err |= save_fp_context32(sc);
+ /* make sure save_fp_context32 not sleep */
+ err |= save_fp_context32(&tmpsc);
preempt_enable();
+
+ err |= __copy_to_user(&sc->sc_fpregs, &tmpsc.sc_fpregs,
+ sizeof(tmpsc.sc_fpregs));
+ err |= __put_user(tmpsc.sc_fpc_csr, &sc->sc_fpc_csr);
+ err |= __put_user(tmpsc.sc_fpc_eir, &sc->sc_fpc_eir);
out:
return err;
diff -ur linux-mips-cvs/arch/mips/kernel/traps.c
linux-mips/arch/mips/kernel/traps.c
--- linux-mips-cvs/arch/mips/kernel/traps.c Sun Oct 24 23:36:09 2004
+++ linux-mips/arch/mips/kernel/traps.c Sun Oct 24 23:47:04 2004
@@ -506,6 +506,14 @@
preempt_disable();
+#ifdef CONFIG_PREEMPT
+ if (!is_fpu_owner()) {
+ /* We might lose fpu before disabling preempt... */
+ own_fpu();
+ BUG_ON(!current->used_math);
+ restore_fp(current);
+ }
+#endif
/*
* Unimplemented operation exception. If we've got the full
* software emulator on-board, let's use it...
@@ -517,10 +525,13 @@
* a bit extreme for what should be an infrequent event.
*/
save_fp(current);
+ /* Ensure 'resume' not overwrite saved fp context again. */
+ lose_fpu();
/* Run the emulator */
sig = fpu_emulator_cop1Handler (0, regs,
¤t->thread.fpu.soft);
+ own_fpu(); /* Using the FPU again. */
/*
* We can't allow the emulated instruction to leave any of
diff -ur linux-mips-cvs/arch/mips/math-emu/cp1emu.c
linux-mips/arch/mips/math-emu/cp1emu.c
--- linux-mips-cvs/arch/mips/math-emu/cp1emu.c Sat Jul 31 21:27:26 2004
+++ linux-mips/arch/mips/math-emu/cp1emu.c Sat Oct 9 22:23:23 2004
@@ -51,6 +51,28 @@
#include "ieee754.h"
#include "dsemul.h"
+#define math_put_user(x, ptr) \
+({ \
+ long math_pu_err; \
+ struct ieee754_csr pu_csr_save; \
+ pu_csr_save = ieee754_csr; \
+ preempt_enable(); \
+ math_pu_err = put_user(x, ptr); \
+ preempt_disable(); \
+ ieee754_csr = pu_csr_save; \
+ math_pu_err; \
+})
+#define math_get_user(x, ptr) \
+({ \
+ long math_gu_err; \
+ struct ieee754_csr gu_csr_save; \
+ gu_csr_save = ieee754_csr; \
+ preempt_enable(); \
+ math_gu_err = get_user(x, ptr); \
+ preempt_disable(); \
+ ieee754_csr = gu_csr_save; \
+ math_gu_err; \
+})
/* Strap kernel emulator for full MIPS IV emulation */
#ifdef __mips
@@ -199,7 +221,7 @@
vaddr_t emulpc, contpc;
unsigned int cond;
- if (get_user(ir, (mips_instruction *) xcp->cp0_epc)) {
+ if (math_get_user(ir, (mips_instruction *) xcp->cp0_epc)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -230,7 +252,7 @@
#endif
return SIGILL;
}
- if (get_user(ir, (mips_instruction *) emulpc)) {
+ if (math_get_user(ir, (mips_instruction *) emulpc)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -254,7 +276,7 @@
u64 val;
fpuemuprivate.stats.loads++;
- if (get_user(val, va)) {
+ if (math_get_user(val, va)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -269,7 +291,7 @@
fpuemuprivate.stats.stores++;
DIFROMREG(val, MIPSInst_RT(ir));
- if (put_user(val, va)) {
+ if (math_put_user(val, va)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -283,7 +305,7 @@
u32 val;
fpuemuprivate.stats.loads++;
- if (get_user(val, va)) {
+ if (math_get_user(val, va)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -310,7 +332,7 @@
}
#endif
SIFROMREG(val, MIPSInst_RT(ir));
- if (put_user(val, va)) {
+ if (math_put_user(val, va)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -365,7 +387,11 @@
u32 value;
if (ir == CP1UNDEF) {
- return do_dsemulret(xcp);
+ int ret;
+ preempt_enable();
+ ret = do_dsemulret(xcp);
+ preempt_disable();
+ return ret;
}
if (MIPSInst_RD(ir) == FPCREG_CSR) {
value = ctx->fcr31;
@@ -449,7 +475,7 @@
(xcp->cp0_epc +
(MIPSInst_SIMM(ir) << 2));
- if (get_user(ir, (mips_instruction *)
+ if (math_get_user(ir, (mips_instruction *)
REG_TO_VA xcp->cp0_epc)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
@@ -480,7 +506,13 @@
* Single step the non-cp1
* instruction in the dslot
*/
- return mips_dsemul(xcp, ir, VA_TO_REG contpc);
+ {
+ int ret;
+ preempt_enable();
+ ret = mips_dsemul(xcp, ir, VA_TO_REG
contpc);
+ preempt_disable();
+ return ret;
+ }
}
else {
/* branch not taken */
@@ -632,7 +664,7 @@
xcp->regs[MIPSInst_FT(ir)]);
fpuemuprivate.stats.loads++;
- if (get_user(val, va)) {
+ if (math_get_user(val, va)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -662,7 +694,7 @@
#endif
SIFROMREG(val, MIPSInst_FS(ir));
- if (put_user(val, va)) {
+ if (math_put_user(val, va)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -728,7 +760,7 @@
xcp->regs[MIPSInst_FT(ir)]);
fpuemuprivate.stats.loads++;
- if (get_user(val, va)) {
+ if (math_get_user(val, va)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -741,7 +773,7 @@
fpuemuprivate.stats.stores++;
DIFROMREG(val, MIPSInst_FS(ir));
- if (put_user(val, va)) {
+ if (math_put_user(val, va)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -1290,7 +1322,7 @@
do {
prevepc = xcp->cp0_epc;
- if (get_user(insn, (mips_instruction *) xcp->cp0_epc)) {
+ if (math_get_user(insn, (mips_instruction *) xcp->cp0_epc)) {
fpuemuprivate.stats.errors++;
return SIGBUS;
}
@@ -1310,7 +1342,9 @@
if (sig)
break;
+ preempt_enable();
cond_resched();
+ preempt_disable();
} while (xcp->cp0_epc > prevepc);
/* SIGILL indicates a non-fpu instruction */
diff -ur linux-mips-cvs/include/asm-mips/fpu.h linux-mips/include/asm-mips/fpu.h
--- linux-mips-cvs/include/asm-mips/fpu.h Fri Dec 19 23:54:16 2003
+++ linux-mips/include/asm-mips/fpu.h Sat Oct 9 22:18:39 2004
@@ -127,8 +127,10 @@
static inline fpureg_t *get_fpu_regs(struct task_struct *tsk)
{
if (cpu_has_fpu) {
+ preempt_disable();
if ((tsk == current) && is_fpu_owner())
_save_fp(current);
+ preempt_enable();
return tsk->thread.fpu.hard.fpr;
}
---
Atsushi Nemoto
|